diff --git a/validator/keymanager/remote-web3signer/BUILD.bazel b/validator/keymanager/remote-web3signer/BUILD.bazel index dd09380d7d71..dee432a03721 100644 --- a/validator/keymanager/remote-web3signer/BUILD.bazel +++ b/validator/keymanager/remote-web3signer/BUILD.bazel @@ -17,6 +17,7 @@ go_library( "//crypto/bls:go_default_library", "//encoding/bytesutil:go_default_library", "//proto/prysm/v1alpha1/validator-client:go_default_library", + "//validator/keymanager/remote-web3signer/v1:go_default_library", "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", "@com_github_pkg_errors//:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", @@ -35,6 +36,7 @@ go_test( "//encoding/bytesutil:go_default_library", "//proto/prysm/v1alpha1:go_default_library", "//proto/prysm/v1alpha1/validator-client:go_default_library", + "//validator/keymanager/remote-web3signer/v1:go_default_library", "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", "@com_github_stretchr_testify//assert:go_default_library", ], diff --git a/validator/keymanager/remote-web3signer/README.md b/validator/keymanager/remote-web3signer/README.md new file mode 100644 index 000000000000..a9086ab74c4a --- /dev/null +++ b/validator/keymanager/remote-web3signer/README.md @@ -0,0 +1,56 @@ +# Web3Signer + +Web3Signer is a popular remote signer tool by Consensys to allow users to store validation keys outside the validation +client and signed without the vc knowing the private keys. Web3Signer Specs are found by +searching `Consensys' Web3Signer API specification` + +issue: https://github.com/prysmaticlabs/prysm/issues/9994 + +## Support + +WIP + +## Features + +### CLI + +WIP + +### API + +- Get Public keys: returns all public keys currently stored with web3signer excluding newly added keys if reload keys + was not run. +- Sign: Signs a message with a given public key. There are several types of messages that can be signed ( web3signer + type to prysm type): + - BLOCK <- *validatorpb.SignRequest_Block + - ATTESTATION <- *validatorpb.SignRequest_AttestationData + - AGGREGATE_AND_PROOF <- *validatorpb.SignRequest_AggregateAttestationAndProof + - AGGREGATION_SLOT <- *validatorpb.SignRequest_Slot + - BLOCK_V2 <- *validatorpb.SignRequest_BlockV2 + - BLOCK_V3 <- *validatorpb.SignRequest_BlockV3 + - DEPOSIT <- not supported + - RANDAO_REVEAL <- *validatorpb.SignRequest_Epoch + - VOLUNTARY_EXIT <- *validatorpb.SignRequest_Exit + - SYNC_COMMITTEE_MESSAGE <- *validatorpb.SignRequest_SyncMessageBlockRoot + - SYNC_COMMITTEE_SELECTION_PROOF <- *validatorpb.SignRequest_SyncAggregatorSelectionData + - SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF <- *validatorpb.SignRequest_ContributionAndProof +- Reload Keys: reloads all public keys from the web3signer. +- Get Server Status: returns OK if the web3signer is ok. + +## Files Added and Files Changed + +- Files Added: + - validator/keymanager/remote-web3signer package + +- Files Modified: + - modified: cmd/validator/flags/flags.go + - modified: validator/accounts/accounts_backup.go + - modified: validator/accounts/accounts_list.go + - modified: validator/accounts/iface/wallet.go + - modified: validator/accounts/userprompt/prompt.go + - modified: validator/accounts/wallet/wallet.go + - modified: validator/accounts/wallet_create.go + - modified: validator/client/runner.go + - modified: validator/client/validator.go + - modified: validator/keymanager/remote-web3signer/keymanager.go + - modified: validator/keymanager/types.go \ No newline at end of file diff --git a/validator/keymanager/remote-web3signer/client.go b/validator/keymanager/remote-web3signer/client.go index 0c59a3f61ac8..1a1e589a0140 100644 --- a/validator/keymanager/remote-web3signer/client.go +++ b/validator/keymanager/remote-web3signer/client.go @@ -14,6 +14,7 @@ import ( "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/crypto/bls" "github.com/prysmaticlabs/prysm/encoding/bytesutil" + v1 "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer/v1" ) const ( @@ -47,35 +48,11 @@ func newApiClient(baseEndpoint string) (*apiClient, error) { }, nil } -// SignRequest is a request object for web3signer sign api. type SignRequest struct { - Type string `json:"type"` - ForkInfo *ForkInfo `json:"fork_info"` - SigningRoot string `json:"signingRoot"` - AggregationSlot *AggregationSlot `json:"aggregation_slot"` -} - -// ForkInfo a sub property object of the Sign request,in the future before bellatrix to remove the need to send the entire block body and just use the block_body_root. -type ForkInfo struct { - Fork *Fork `json:"fork"` - GenesisValidatorsRoot string `json:"genesis_validators_root"` -} - -// Fork a sub property of ForkInfo. -type Fork struct { - PreviousVersion string `json:"previous_version"` - CurrentVersion string `json:"current_version"` - Epoch string `json:"epoch"` -} - -// AggregationSlot a sub property of SignRequest. -type AggregationSlot struct { - Slot string `json:"slot"` -} - -// signResponse the response object of the web3signer sign api. -type signResponse struct { - Signature string `json:"signature"` + Type string `json:"type"` + ForkInfo *v1.ForkInfo `json:"fork_info"` + SigningRoot string `json:"signingRoot"` + AggregationSlot *v1.AggregationSlot `json:"aggregation_slot"` } // Sign is a wrapper method around the web3signer sign api. @@ -95,7 +72,7 @@ func (client *apiClient) Sign(_ context.Context, pubKey string, request *SignReq if resp.StatusCode == 412 { return nil, errors.Wrap(err, "signing operation failed due to slashing protection rules") } - signResp := &signResponse{} + signResp := &v1.SignResponse{} if err := client.unmarshalResponse(resp.Body, &signResp); err != nil { return nil, err } diff --git a/validator/keymanager/remote-web3signer/client_test.go b/validator/keymanager/remote-web3signer/client_test.go index 03e3c5cdd1c0..5712037fc033 100644 --- a/validator/keymanager/remote-web3signer/client_test.go +++ b/validator/keymanager/remote-web3signer/client_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + v1 "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer/v1" "github.com/stretchr/testify/assert" ) @@ -22,17 +23,17 @@ func (m *mockTransport) RoundTrip(*http.Request) (*http.Response, error) { } func getClientMockSignRequest() *SignRequest { - forkData := &Fork{ + forkData := &v1.Fork{ PreviousVersion: "", CurrentVersion: "", Epoch: "", } - forkInfoData := &ForkInfo{ + forkInfoData := &v1.ForkInfo{ Fork: forkData, GenesisValidatorsRoot: "", } - AggregationSlotData := &AggregationSlot{Slot: ""} + AggregationSlotData := &v1.AggregationSlot{Slot: ""} // remember to replace signing root with hex encoding remove 0x web3SignerRequest := SignRequest{ Type: "foo", diff --git a/validator/keymanager/remote-web3signer/keymanager.go b/validator/keymanager/remote-web3signer/keymanager.go index 5b806a3d2e1f..324771387a39 100644 --- a/validator/keymanager/remote-web3signer/keymanager.go +++ b/validator/keymanager/remote-web3signer/keymanager.go @@ -9,6 +9,7 @@ import ( "github.com/prysmaticlabs/prysm/async/event" "github.com/prysmaticlabs/prysm/crypto/bls" validatorpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client" + v1 "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer/v1" ) // SetupConfig includes configuration values for initializing. @@ -85,16 +86,16 @@ func (km *Keymanager) Sign(ctx context.Context, request *validatorpb.SignRequest return nil, err } - forkData := &Fork{ + forkData := &v1.Fork{ PreviousVersion: hexutil.Encode(request.Fork.PreviousVersion), CurrentVersion: hexutil.Encode(request.Fork.CurrentVersion), Epoch: fmt.Sprint(request.Fork.Epoch), } - forkInfoData := &ForkInfo{ + forkInfoData := &v1.ForkInfo{ Fork: forkData, GenesisValidatorsRoot: hexutil.Encode(km.genesisValidatorsRoot), } - aggregationSlotData := &AggregationSlot{Slot: fmt.Sprint(request.AggregationSlot)} + aggregationSlotData := &v1.AggregationSlot{Slot: fmt.Sprint(request.AggregationSlot)} web3SignerRequest := SignRequest{ Type: signRequestType, ForkInfo: forkInfoData, diff --git a/validator/keymanager/remote-web3signer/v1/BUILD.bazel b/validator/keymanager/remote-web3signer/v1/BUILD.bazel new file mode 100644 index 000000000000..623b77d40e74 --- /dev/null +++ b/validator/keymanager/remote-web3signer/v1/BUILD.bazel @@ -0,0 +1,32 @@ +load("@prysm//tools/go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "custom_mappers.go", + "mocks.go", + "web3signer_types.go", + ], + importpath = "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer/v1", + visibility = ["//visibility:public"], + deps = [ + "//config/fieldparams:go_default_library", + "//proto/prysm/v1alpha1:go_default_library", + "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", + "@com_github_pkg_errors//:go_default_library", + "@com_github_prysmaticlabs_go_bitfield//:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["custom_mappers_test.go"], + embed = [":go_default_library"], + deps = [ + "//config/fieldparams:go_default_library", + "//proto/prysm/v1alpha1:go_default_library", + "//testing/util:go_default_library", + "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", + "@com_github_prysmaticlabs_go_bitfield//:go_default_library", + ], +) diff --git a/validator/keymanager/remote-web3signer/v1/custom_mappers.go b/validator/keymanager/remote-web3signer/v1/custom_mappers.go new file mode 100644 index 000000000000..ae1c0a3b2e6c --- /dev/null +++ b/validator/keymanager/remote-web3signer/v1/custom_mappers.go @@ -0,0 +1,420 @@ +package v1 + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/pkg/errors" + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" +) + +// MapForkInfo maps the eth2.ForkInfo proto to the Web3Signer spec. +func MapForkInfo(from *ethpb.Fork, genesisValidatorsRoot []byte) (*ForkInfo, error) { + if from == nil { + return nil, fmt.Errorf("fork info is nil") + } + forkData := &Fork{ + PreviousVersion: hexutil.Encode(from.PreviousVersion), + CurrentVersion: hexutil.Encode(from.CurrentVersion), + Epoch: fmt.Sprint(from.Epoch), + } + return &ForkInfo{ + Fork: forkData, + GenesisValidatorsRoot: hexutil.Encode(genesisValidatorsRoot), + }, nil +} + +// MapAggregateAndProof maps the eth2.AggregateAndProof proto to the Web3Signer spec. +func MapAggregateAndProof(from *ethpb.AggregateAttestationAndProof) (*AggregateAndProof, error) { + if from == nil { + return nil, fmt.Errorf("AggregateAttestationAndProof is nil") + } + aggregate, err := MapAttestation(from.Aggregate) + if err != nil { + return nil, err + } + return &AggregateAndProof{ + AggregatorIndex: fmt.Sprint(from.AggregatorIndex), + Aggregate: aggregate, + SelectionProof: hexutil.Encode(from.SelectionProof), + }, nil +} + +// MapAttestation maps the eth2.Attestation proto to the Web3Signer spec. +func MapAttestation(attestation *ethpb.Attestation) (*Attestation, error) { + if attestation == nil { + return nil, fmt.Errorf("attestation is nil") + } + if attestation.AggregationBits == nil { + return nil, fmt.Errorf("aggregation bits in attestation is nil") + } + data, err := MapAttestationData(attestation.Data) + if err != nil { + return nil, err + } + return &Attestation{ + Data: data, + AggregationBits: hexutil.Encode(attestation.AggregationBits.Bytes()), + Signature: hexutil.Encode(attestation.Signature), + }, nil +} + +// MapAttestationData maps the eth2.AttestationData proto to the Web3Signer spec. +func MapAttestationData(data *ethpb.AttestationData) (*AttestationData, error) { + if data == nil { + return nil, fmt.Errorf("attestation data is nil") + } + source, err := MapCheckPoint(data.Source) + if err != nil { + return nil, errors.Wrap(err, "could not map source for attestation data") + } + target, err := MapCheckPoint(data.Target) + if err != nil { + return nil, errors.Wrap(err, "could not map target for attestation data") + } + return &AttestationData{ + Slot: fmt.Sprint(data.Slot), + Index: fmt.Sprint(data.CommitteeIndex), + BeaconBlockRoot: hexutil.Encode(data.BeaconBlockRoot), + Source: source, + Target: target, + }, nil +} + +// MapCheckPoint maps the eth2.Checkpoint proto to the Web3Signer spec. +func MapCheckPoint(checkpoint *ethpb.Checkpoint) (*Checkpoint, error) { + if checkpoint == nil { + return nil, fmt.Errorf("checkpoint is nil") + } + return &Checkpoint{ + Epoch: fmt.Sprint(checkpoint.Epoch), + Root: hexutil.Encode(checkpoint.Root), + }, nil +} + +// MapBeaconBlockBody maps the eth2.BeaconBlockBody proto to the Web3Signer spec. +func MapBeaconBlockBody(body *ethpb.BeaconBlockBody) (*BeaconBlockBody, error) { + if body == nil { + return nil, fmt.Errorf("beacon block body is nil") + } + if body.Eth1Data == nil { + return nil, fmt.Errorf("eth1 data in Beacon Block Body is nil") + } + block := &BeaconBlockBody{ + RandaoReveal: hexutil.Encode(body.RandaoReveal), + Eth1Data: &Eth1Data{ + DepositRoot: hexutil.Encode(body.Eth1Data.DepositRoot), + DepositCount: fmt.Sprint(0), + BlockHash: hexutil.Encode(body.Eth1Data.BlockHash), + }, + Graffiti: hexutil.Encode(body.Graffiti), + ProposerSlashings: make([]*ProposerSlashing, len(body.ProposerSlashings)), + AttesterSlashings: make([]*AttesterSlashing, len(body.AttesterSlashings)), + Attestations: make([]*Attestation, len(body.Attestations)), + Deposits: make([]*Deposit, len(body.Deposits)), + VoluntaryExits: make([]*SignedVoluntaryExit, len(body.VoluntaryExits)), + } + for i, slashing := range body.ProposerSlashings { + slashing, err := MapProposerSlashing(slashing) + if err != nil { + return nil, fmt.Errorf("could not map proposer slashing at index %v: %v", i, err) + } + block.ProposerSlashings[i] = slashing + } + for i, slashing := range body.AttesterSlashings { + slashing, err := MapAttesterSlashing(slashing) + if err != nil { + return nil, fmt.Errorf("could not map attester slashing at index %v: %v", i, err) + } + block.AttesterSlashings[i] = slashing + } + for i, attestation := range body.Attestations { + attestation, err := MapAttestation(attestation) + if err != nil { + return nil, fmt.Errorf("could not map attestation at index %v: %v", i, err) + } + block.Attestations[i] = attestation + } + for i, Deposit := range body.Deposits { + deposit, err := MapDeposit(Deposit) + if err != nil { + return nil, fmt.Errorf("could not map deposit at index %v: %v", i, err) + } + block.Deposits[i] = deposit + } + for i, signedVoluntaryExit := range body.VoluntaryExits { + signedVoluntaryExit, err := MapSignedVoluntaryExit(signedVoluntaryExit) + if err != nil { + return nil, fmt.Errorf("could not map signed voluntary exit at index %v: %v", i, err) + } + block.VoluntaryExits[i] = signedVoluntaryExit + } + return block, nil +} + +// MapProposerSlashing maps the eth2.ProposerSlashing proto to the Web3Signer spec. +func MapProposerSlashing(slashing *ethpb.ProposerSlashing) (*ProposerSlashing, error) { + if slashing == nil { + return nil, fmt.Errorf("proposer slashing is nil") + } + signedHeader1, err := MapSignedBeaconBlockHeader(slashing.Header_1) + if err != nil { + return nil, errors.Wrap(err, "could not map signed header 1") + } + signedHeader2, err := MapSignedBeaconBlockHeader(slashing.Header_2) + if err != nil { + return nil, errors.Wrap(err, "could not map signed header 2") + } + return &ProposerSlashing{ + SignedHeader_1: signedHeader1, + SignedHeader_2: signedHeader2, + }, nil +} + +// MapAttesterSlashing maps the eth2.AttesterSlashing proto to the Web3Signer spec. +func MapSignedBeaconBlockHeader(signedHeader *ethpb.SignedBeaconBlockHeader) (*SignedBeaconBlockHeader, error) { + if signedHeader == nil { + return nil, fmt.Errorf("signed beacon block header is nil") + } + if signedHeader.Header == nil { + return nil, fmt.Errorf("signed beacon block header message is nil") + } + return &SignedBeaconBlockHeader{ + Message: &BeaconBlockHeader{ + Slot: fmt.Sprint(signedHeader.Header.Slot), + ProposerIndex: fmt.Sprint(signedHeader.Header.ProposerIndex), + ParentRoot: hexutil.Encode( + signedHeader.Header.ParentRoot, + ), + StateRoot: hexutil.Encode( + signedHeader.Header.StateRoot, + ), + BodyRoot: hexutil.Encode( + signedHeader.Header.BodyRoot, + ), + }, + Signature: hexutil.Encode( + signedHeader.Signature, + ), + }, nil +} + +// MapAttesterSlashing maps the eth2.AttesterSlashing proto to the Web3Signer spec. +func MapAttesterSlashing(slashing *ethpb.AttesterSlashing) (*AttesterSlashing, error) { + if slashing == nil { + return nil, fmt.Errorf("attester slashing is nil") + } + attestation1, err := MapIndexedAttestation(slashing.Attestation_1) + if err != nil { + return nil, errors.Wrap(err, "could not map attestation 1") + } + attestation2, err := MapIndexedAttestation(slashing.Attestation_2) + if err != nil { + return nil, errors.Wrap(err, "could not map attestation 2") + } + return &AttesterSlashing{ + Attestation_1: attestation1, + Attestation_2: attestation2, + }, nil +} + +// MapIndexedAttestation maps the eth2.IndexedAttestation proto to the Web3Signer spec. +func MapIndexedAttestation(attestation *ethpb.IndexedAttestation) (*IndexedAttestation, error) { + if attestation == nil { + return nil, fmt.Errorf("indexed attestation is nil") + } + attestingIndices := make([]string, len(attestation.AttestingIndices)) + for i, indices := range attestation.AttestingIndices { + attestingIndices[i] = fmt.Sprint(indices) + } + attestationData, err := MapAttestationData(attestation.Data) + if err != nil { + return nil, errors.Wrap(err, "could not map attestation data to IndexedAttestation") + } + return &IndexedAttestation{ + AttestingIndices: attestingIndices, + Data: attestationData, + Signature: hexutil.Encode(attestation.Signature), + }, nil +} + +// MapDeposit maps the eth2.Deposit proto to the Web3Signer spec. +func MapDeposit(deposit *ethpb.Deposit) (*Deposit, error) { + if deposit == nil { + return nil, fmt.Errorf("deposit is nil") + } + proof := make([]string, len(deposit.Proof)) + for i, p := range deposit.Proof { + proof[i] = hexutil.Encode(p) + } + return &Deposit{ + Proof: proof, + Data: &DepositData{ + PublicKey: hexutil.Encode( + deposit.Data.PublicKey, + ), + WithdrawalCredentials: hexutil.Encode( + deposit.Data.WithdrawalCredentials, + ), + Amount: fmt.Sprint(deposit.Data.Amount), + Signature: hexutil.Encode( + deposit.Data.Signature, + ), + }, + }, nil +} + +// MapSignedVoluntaryExit maps the eth2.SignedVoluntaryExit proto to the Web3Signer spec. +func MapSignedVoluntaryExit(signedVoluntaryExit *ethpb.SignedVoluntaryExit) (*SignedVoluntaryExit, error) { + if signedVoluntaryExit == nil { + return nil, fmt.Errorf("signed voluntary exit is nil") + } + if signedVoluntaryExit.Exit == nil { + return nil, fmt.Errorf("exit in signed voluntary exit is nil") + } + return &SignedVoluntaryExit{ + Message: &VoluntaryExit{ + Epoch: fmt.Sprint(signedVoluntaryExit.Exit.Epoch), + ValidatorIndex: fmt.Sprint(signedVoluntaryExit.Exit.ValidatorIndex), + }, + Signature: hexutil.Encode( + signedVoluntaryExit.Signature, + ), + }, nil +} + +// MapBeaconBlockAltair maps the eth2.BeaconBlockAltair proto to the Web3Signer spec. +func MapBeaconBlockAltair(block *ethpb.BeaconBlockAltair) (*BeaconBlockAltair, error) { + if block == nil { + return nil, fmt.Errorf("beacon block altair is nil") + } + body, err := MapBeaconBlockBodyAltair(block.Body) + if err != nil { + return nil, errors.Wrap(err, "could not map beacon block body for altair") + } + return &BeaconBlockAltair{ + Slot: fmt.Sprint(block.Slot), + Body: body, + ParentRoot: hexutil.Encode( + block.ParentRoot, + ), + StateRoot: hexutil.Encode( + block.StateRoot, + ), + }, nil +} + +// MapBeaconBlockBodyAltair maps the eth2.BeaconBlockBodyAltair proto to the Web3Signer spec. +func MapBeaconBlockBodyAltair(body *ethpb.BeaconBlockBodyAltair) (*BeaconBlockBodyAltair, error) { + if body == nil { + return nil, fmt.Errorf("beacon block body altair is nil") + } + if body.SyncAggregate == nil { + return nil, fmt.Errorf("sync aggregate in beacon block body altair is nil") + } + if body.SyncAggregate.SyncCommitteeBits == nil { + return nil, fmt.Errorf("sync committee bits in sync aggregate in beacon block body altair is nil") + } + + block := &BeaconBlockBodyAltair{ + RandaoReveal: hexutil.Encode(body.RandaoReveal), + Eth1Data: &Eth1Data{ + DepositRoot: hexutil.Encode(body.Eth1Data.DepositRoot), + DepositCount: fmt.Sprint(body.Eth1Data.DepositCount), + BlockHash: hexutil.Encode(body.Eth1Data.BlockHash), + }, + Graffiti: hexutil.Encode(body.Graffiti), + ProposerSlashings: make([]*ProposerSlashing, len(body.ProposerSlashings)), + AttesterSlashings: make([]*AttesterSlashing, len(body.AttesterSlashings)), + Attestations: make([]*Attestation, len(body.Attestations)), + Deposits: make([]*Deposit, len(body.Deposits)), + VoluntaryExits: make([]*SignedVoluntaryExit, len(body.VoluntaryExits)), + SyncAggregate: &SyncAggregate{ + SyncCommitteeBits: hexutil.Encode(body.SyncAggregate.SyncCommitteeBits.Bytes()), + SyncCommitteeSignature: hexutil.Encode(body.SyncAggregate.SyncCommitteeSignature), + }, + } + for i, slashing := range body.ProposerSlashings { + proposer, err := MapProposerSlashing(slashing) + if err != nil { + return nil, fmt.Errorf("could not map proposer slashing at index %v: %v", i, err) + } + block.ProposerSlashings[i] = proposer + } + for i, slashing := range body.AttesterSlashings { + attesterSlashing, err := MapAttesterSlashing(slashing) + if err != nil { + return nil, fmt.Errorf("could not map attester slashing at index %v: %v", i, err) + } + block.AttesterSlashings[i] = attesterSlashing + } + for i, attestation := range body.Attestations { + attestation, err := MapAttestation(attestation) + if err != nil { + return nil, fmt.Errorf("could not map attestation at index %v: %v", i, err) + } + block.Attestations[i] = attestation + } + for i, deposit := range body.Deposits { + deposit, err := MapDeposit(deposit) + if err != nil { + return nil, fmt.Errorf("could not map deposit at index %v: %v", i, err) + } + block.Deposits[i] = deposit + } + for i, exit := range body.VoluntaryExits { + + exit, err := MapSignedVoluntaryExit(exit) + if err != nil { + return nil, fmt.Errorf("could not map signed voluntary exit at index %v: %v", i, err) + } + block.VoluntaryExits[i] = exit + } + return block, nil +} + +// MapSyncCommitteeMessage maps the eth2.SyncCommitteeMessage proto to the Web3Signer spec. +func MapSyncCommitteeMessage(message *ethpb.SyncCommitteeMessage) (*SyncCommitteeMessage, error) { + if message == nil { + return nil, fmt.Errorf("sync committee message is nil") + } + return &SyncCommitteeMessage{ + BeaconBlockRoot: hexutil.Encode(message.BlockRoot), + Slot: fmt.Sprint(message.Slot), + }, nil +} + +// MapSyncAggregatorSelectionData maps the eth2.SyncAggregatorSelectionData proto to the Web3Signer spec. +func MapSyncAggregatorSelectionData(data *ethpb.SyncAggregatorSelectionData) (*SyncAggregatorSelectionData, error) { + if data == nil { + return nil, fmt.Errorf("sync aggregator selection data is nil") + } + return &SyncAggregatorSelectionData{ + Slot: fmt.Sprint(data.Slot), + SubcommitteeIndex: fmt.Sprint(data.SubcommitteeIndex), + }, nil +} + +// MapContributionAndProof maps the eth2.ContributionAndProof proto to the Web3Signer spec. +func MapContributionAndProof(contribution *ethpb.ContributionAndProof) (*ContributionAndProof, error) { + if contribution == nil { + return nil, fmt.Errorf("contribution and proof is nil") + } + if contribution.Contribution == nil { + return nil, fmt.Errorf("contribution in ContributionAndProof is nil") + } + if contribution.Contribution.AggregationBits == nil { + return nil, fmt.Errorf("aggregation bits in ContributionAndProof is nil") + } + return &ContributionAndProof{ + AggregatorIndex: fmt.Sprint(contribution.AggregatorIndex), + SelectionProof: hexutil.Encode(contribution.SelectionProof), + Contribution: &SyncCommitteeContribution{ + Slot: fmt.Sprint(contribution.Contribution.Slot), + BeaconBlockRoot: hexutil.Encode(contribution.Contribution.BlockRoot), + SubcommitteeIndex: fmt.Sprint(contribution.Contribution.SubcommitteeIndex), + AggregationBits: hexutil.Encode(contribution.Contribution.AggregationBits.Bytes()), + Signature: hexutil.Encode(contribution.Contribution.Signature), + }, + }, nil +} diff --git a/validator/keymanager/remote-web3signer/v1/custom_mappers_test.go b/validator/keymanager/remote-web3signer/v1/custom_mappers_test.go new file mode 100644 index 000000000000..34a965e6f22d --- /dev/null +++ b/validator/keymanager/remote-web3signer/v1/custom_mappers_test.go @@ -0,0 +1,543 @@ +package v1 + +import ( + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/prysmaticlabs/go-bitfield" + fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams" + "github.com/prysmaticlabs/prysm/testing/util" + + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" +) + +func TestMapAggregateAndProof(t *testing.T) { + type args struct { + from *ethpb.AggregateAttestationAndProof + } + tests := []struct { + name string + args args + want *AggregateAndProof + wantErr bool + }{ + { + name: "HappyPathTest", + args: args{ + from: ðpb.AggregateAttestationAndProof{ + AggregatorIndex: 0, + Aggregate: util.NewAttestation(), + SelectionProof: make([]byte, fieldparams.BLSSignatureLength), + }, + }, + want: &AggregateAndProof{ + AggregatorIndex: "0", + Aggregate: MockAttestation(), + SelectionProof: hexutil.Encode(make([]byte, fieldparams.BLSSignatureLength)), + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := MapAggregateAndProof(tt.args.from) + if (err != nil) != tt.wantErr { + t.Errorf("MapAggregateAndProof() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got.Aggregate, tt.want.Aggregate) { + t.Errorf("MapAggregateAndProof() got = %v, want %v", got.Aggregate, tt.want.Aggregate) + } + }) + } +} + +func TestMapAttestation(t *testing.T) { + type args struct { + attestation *ethpb.Attestation + } + tests := []struct { + name string + args args + want *Attestation + wantErr bool + }{ + { + name: "HappyPathTest", + args: args{ + attestation: util.NewAttestation(), + }, + want: MockAttestation(), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := MapAttestation(tt.args.attestation) + if (err != nil) != tt.wantErr { + t.Errorf("MapAttestation() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("MapAttestation() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestMapAttestationData(t *testing.T) { + type args struct { + data *ethpb.AttestationData + } + tests := []struct { + name string + args args + want *AttestationData + wantErr bool + }{ + { + name: "HappyPathTest", + args: args{ + data: util.NewAttestation().Data, + }, + want: MockAttestation().Data, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := MapAttestationData(tt.args.data) + if (err != nil) != tt.wantErr { + t.Errorf("MapAttestationData() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("MapAttestationData() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestMapAttesterSlashing(t *testing.T) { + type args struct { + slashing *ethpb.AttesterSlashing + } + tests := []struct { + name string + args args + want *AttesterSlashing + wantErr bool + }{ + { + name: "HappyPathTest", + args: args{ + slashing: ðpb.AttesterSlashing{ + Attestation_1: ðpb.IndexedAttestation{ + AttestingIndices: []uint64{0, 1, 2}, + Data: util.NewAttestation().Data, + Signature: make([]byte, fieldparams.BLSSignatureLength), + }, + Attestation_2: ðpb.IndexedAttestation{ + AttestingIndices: []uint64{0, 1, 2}, + Data: util.NewAttestation().Data, + Signature: make([]byte, fieldparams.BLSSignatureLength), + }, + }, + }, + want: &AttesterSlashing{ + Attestation_1: MockIndexedAttestation(), + Attestation_2: MockIndexedAttestation(), + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := MapAttesterSlashing(tt.args.slashing) + if (err != nil) != tt.wantErr { + t.Errorf("MapAttesterSlashing() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got.Attestation_1, tt.want.Attestation_1) { + t.Errorf("MapAttesterSlashing() got = %v, want %v", got.Attestation_1, tt.want.Attestation_1) + } + }) + } +} + +func TestMapBeaconBlockAltair(t *testing.T) { + type args struct { + block *ethpb.BeaconBlockAltair + } + tests := []struct { + name string + args args + want *BeaconBlockAltair + wantErr bool + }{ + { + name: "Happy Path Test", + args: args{ + block: ðpb.BeaconBlockAltair{ + Slot: 0, + ProposerIndex: 0, + ParentRoot: make([]byte, fieldparams.RootLength), + StateRoot: make([]byte, fieldparams.RootLength), + Body: ðpb.BeaconBlockBodyAltair{ + RandaoReveal: make([]byte, 32), + Eth1Data: ðpb.Eth1Data{ + DepositRoot: make([]byte, fieldparams.RootLength), + DepositCount: 0, + BlockHash: make([]byte, 32), + }, + Graffiti: make([]byte, 32), + ProposerSlashings: []*ethpb.ProposerSlashing{ + { + Header_1: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 0, + ProposerIndex: 0, + ParentRoot: make([]byte, fieldparams.RootLength), + StateRoot: make([]byte, fieldparams.RootLength), + BodyRoot: make([]byte, fieldparams.RootLength), + }, + Signature: make([]byte, fieldparams.BLSSignatureLength), + }, + Header_2: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 0, + ProposerIndex: 0, + ParentRoot: make([]byte, fieldparams.RootLength), + StateRoot: make([]byte, fieldparams.RootLength), + BodyRoot: make([]byte, fieldparams.RootLength), + }, + Signature: make([]byte, fieldparams.BLSSignatureLength), + }, + }, + }, + AttesterSlashings: []*ethpb.AttesterSlashing{ + { + Attestation_1: ðpb.IndexedAttestation{ + AttestingIndices: []uint64{0, 1, 2}, + Data: util.NewAttestation().Data, + Signature: make([]byte, fieldparams.BLSSignatureLength), + }, + Attestation_2: ðpb.IndexedAttestation{ + AttestingIndices: []uint64{0, 1, 2}, + Data: util.NewAttestation().Data, + Signature: make([]byte, fieldparams.BLSSignatureLength), + }, + }, + }, + Attestations: []*ethpb.Attestation{ + util.NewAttestation(), + }, + Deposits: []*ethpb.Deposit{ + { + Proof: [][]byte{[]byte("A")}, + Data: ðpb.Deposit_Data{ + PublicKey: make([]byte, fieldparams.BLSPubkeyLength), + WithdrawalCredentials: make([]byte, 32), + Amount: 0, + Signature: make([]byte, fieldparams.BLSSignatureLength), + }, + }, + }, + VoluntaryExits: []*ethpb.SignedVoluntaryExit{ + { + Exit: ðpb.VoluntaryExit{ + Epoch: 0, + ValidatorIndex: 0, + }, + Signature: make([]byte, fieldparams.BLSSignatureLength), + }, + }, + SyncAggregate: ðpb.SyncAggregate{ + SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength), + SyncCommitteeBits: bitfield.NewBitvector512(), + }, + }, + }, + }, + want: MockBeaconBlockAltair(), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := MapBeaconBlockAltair(tt.args.block) + if (err != nil) != tt.wantErr { + t.Errorf("MapBeaconBlockAltair() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got.Body, tt.want.Body) { + t.Errorf("MapBeaconBlockAltair() got = %v, want %v", got.Body.SyncAggregate, tt.want.Body.SyncAggregate) + } + }) + } +} + +func TestMapBeaconBlockBody(t *testing.T) { + type args struct { + body *ethpb.BeaconBlockBody + } + tests := []struct { + name string + args args + want *BeaconBlockBody + wantErr bool + }{ + { + name: "Happy Path Test", + args: args{ + body: ðpb.BeaconBlockBody{ + RandaoReveal: make([]byte, 32), + Eth1Data: ðpb.Eth1Data{ + DepositRoot: make([]byte, fieldparams.RootLength), + DepositCount: 0, + BlockHash: make([]byte, 32), + }, + Graffiti: make([]byte, 32), + ProposerSlashings: []*ethpb.ProposerSlashing{ + { + Header_1: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 0, + ProposerIndex: 0, + ParentRoot: make([]byte, fieldparams.RootLength), + StateRoot: make([]byte, fieldparams.RootLength), + BodyRoot: make([]byte, fieldparams.RootLength), + }, + Signature: make([]byte, fieldparams.BLSSignatureLength), + }, + Header_2: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 0, + ProposerIndex: 0, + ParentRoot: make([]byte, fieldparams.RootLength), + StateRoot: make([]byte, fieldparams.RootLength), + BodyRoot: make([]byte, fieldparams.RootLength), + }, + Signature: make([]byte, fieldparams.BLSSignatureLength), + }, + }, + }, + AttesterSlashings: []*ethpb.AttesterSlashing{ + { + Attestation_1: ðpb.IndexedAttestation{ + AttestingIndices: []uint64{0, 1, 2}, + Data: util.NewAttestation().Data, + Signature: make([]byte, fieldparams.BLSSignatureLength), + }, + Attestation_2: ðpb.IndexedAttestation{ + AttestingIndices: []uint64{0, 1, 2}, + Data: util.NewAttestation().Data, + Signature: make([]byte, fieldparams.BLSSignatureLength), + }, + }, + }, + Attestations: []*ethpb.Attestation{ + util.NewAttestation(), + }, + Deposits: []*ethpb.Deposit{ + { + Proof: [][]byte{[]byte("A")}, + Data: ðpb.Deposit_Data{ + PublicKey: make([]byte, fieldparams.BLSPubkeyLength), + WithdrawalCredentials: make([]byte, 32), + Amount: 0, + Signature: make([]byte, fieldparams.BLSSignatureLength), + }, + }, + }, + VoluntaryExits: []*ethpb.SignedVoluntaryExit{ + { + Exit: ðpb.VoluntaryExit{ + Epoch: 0, + ValidatorIndex: 0, + }, + Signature: make([]byte, fieldparams.BLSSignatureLength), + }, + }, + }, + }, + want: MockBeaconBlockBody(), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := MapBeaconBlockBody(tt.args.body) + if (err != nil) != tt.wantErr { + t.Errorf("MapBeaconBlockBody() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("MapBeaconBlockBody() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestMapContributionAndProof(t *testing.T) { + type args struct { + contribution *ethpb.ContributionAndProof + } + tests := []struct { + name string + args args + want *ContributionAndProof + wantErr bool + }{ + { + name: "Happy Path Test", + args: args{ + contribution: ðpb.ContributionAndProof{ + AggregatorIndex: 0, + Contribution: ðpb.SyncCommitteeContribution{ + Slot: 0, + BlockRoot: make([]byte, fieldparams.RootLength), + SubcommitteeIndex: 0, + AggregationBits: bitfield.NewBitvector128(), + Signature: make([]byte, fieldparams.BLSSignatureLength), + }, + SelectionProof: make([]byte, fieldparams.BLSSignatureLength), + }, + }, + want: MockContributionAndProof(), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := MapContributionAndProof(tt.args.contribution) + if (err != nil) != tt.wantErr { + t.Errorf("MapContributionAndProof() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("MapContributionAndProof() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestMapForkInfo(t *testing.T) { + type args struct { + from *ethpb.Fork + genesisValidatorsRoot []byte + } + + tests := []struct { + name string + args args + want *ForkInfo + wantErr bool + }{ + { + name: "Happy Path Test", + args: args{ + from: ðpb.Fork{ + PreviousVersion: make([]byte, 4), + CurrentVersion: make([]byte, 4), + Epoch: 0, + }, + genesisValidatorsRoot: make([]byte, fieldparams.RootLength), + }, + want: MockForkInfo(), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := MapForkInfo(tt.args.from, tt.args.genesisValidatorsRoot) + if (err != nil) != tt.wantErr { + t.Errorf("MapForkInfo() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("MapForkInfo() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestMapSyncAggregatorSelectionData(t *testing.T) { + type args struct { + data *ethpb.SyncAggregatorSelectionData + } + tests := []struct { + name string + args args + want *SyncAggregatorSelectionData + wantErr bool + }{ + { + name: "Happy Path Test", + args: args{ + data: ðpb.SyncAggregatorSelectionData{ + Slot: 0, + SubcommitteeIndex: 0, + }, + }, + want: &SyncAggregatorSelectionData{ + Slot: "0", + SubcommitteeIndex: "0", + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := MapSyncAggregatorSelectionData(tt.args.data) + if (err != nil) != tt.wantErr { + t.Errorf("MapSyncAggregatorSelectionData() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("MapSyncAggregatorSelectionData() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestMapSyncCommitteeMessage(t *testing.T) { + type args struct { + message *ethpb.SyncCommitteeMessage + } + tests := []struct { + name string + args args + want *SyncCommitteeMessage + wantErr bool + }{ + { + name: "Happy Path Test", + args: args{ + message: ðpb.SyncCommitteeMessage{ + Slot: 0, + BlockRoot: make([]byte, fieldparams.RootLength), + ValidatorIndex: 0, + Signature: make([]byte, fieldparams.BLSSignatureLength), + }, + }, + want: &SyncCommitteeMessage{ + Slot: "0", + BeaconBlockRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := MapSyncCommitteeMessage(tt.args.message) + if (err != nil) != tt.wantErr { + t.Errorf("MapSyncCommitteeMessage() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("MapSyncCommitteeMessage() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/validator/keymanager/remote-web3signer/v1/mocks.go b/validator/keymanager/remote-web3signer/v1/mocks.go new file mode 100644 index 000000000000..2007bb0a5427 --- /dev/null +++ b/validator/keymanager/remote-web3signer/v1/mocks.go @@ -0,0 +1,215 @@ +package v1 + +import ( + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/prysmaticlabs/go-bitfield" + fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams" +) + +// MockForkInfo is a mock implementation of the ForkInfo. +func MockForkInfo() *ForkInfo { + return &ForkInfo{ + Fork: &Fork{ + PreviousVersion: hexutil.Encode(make([]byte, 4)), + CurrentVersion: hexutil.Encode(make([]byte, 4)), + Epoch: "0", + }, + GenesisValidatorsRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), + } + +} + +// MockAttestation is a mock implementation of the Attestation. +func MockAttestation() *Attestation { + return &Attestation{ + AggregationBits: hexutil.Encode(bitfield.Bitlist{0b1101}.Bytes()), + Data: &AttestationData{ + Slot: "0", + Index: "0", + BeaconBlockRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), + Source: &Checkpoint{ + Epoch: "0", + Root: hexutil.Encode(make([]byte, fieldparams.RootLength)), + }, + Target: &Checkpoint{ + Epoch: "0", + Root: hexutil.Encode(make([]byte, fieldparams.RootLength)), + }, + }, + Signature: hexutil.Encode(make([]byte, fieldparams.BLSSignatureLength)), + } +} + +func MockIndexedAttestation() *IndexedAttestation { + return &IndexedAttestation{ + AttestingIndices: []string{"0", "1", "2"}, + Data: &AttestationData{ + Slot: "0", + Index: "0", + BeaconBlockRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), + Source: &Checkpoint{ + Epoch: "0", + Root: hexutil.Encode(make([]byte, fieldparams.RootLength)), + }, + Target: &Checkpoint{ + Epoch: "0", + Root: hexutil.Encode(make([]byte, fieldparams.RootLength)), + }, + }, + Signature: hexutil.Encode(make([]byte, fieldparams.BLSSignatureLength)), + } +} + +func MockBeaconBlockAltair() *BeaconBlockAltair { + return &BeaconBlockAltair{ + Slot: "0", + ProposerIndex: "0", + ParentRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), + StateRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), + Body: &BeaconBlockBodyAltair{ + RandaoReveal: hexutil.Encode(make([]byte, 32)), + Eth1Data: &Eth1Data{ + DepositRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), + DepositCount: "0", + BlockHash: hexutil.Encode(make([]byte, 32)), + }, + Graffiti: hexutil.Encode(make([]byte, 32)), + ProposerSlashings: []*ProposerSlashing{ + { + SignedHeader_1: &SignedBeaconBlockHeader{ + Message: &BeaconBlockHeader{ + Slot: "0", + ProposerIndex: "0", + ParentRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), + StateRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), + BodyRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), + }, + Signature: hexutil.Encode(make([]byte, fieldparams.BLSSignatureLength)), + }, + SignedHeader_2: &SignedBeaconBlockHeader{ + Message: &BeaconBlockHeader{ + Slot: "0", + ProposerIndex: "0", + ParentRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), + StateRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), + BodyRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), + }, + Signature: hexutil.Encode(make([]byte, fieldparams.BLSSignatureLength)), + }, + }, + }, + AttesterSlashings: []*AttesterSlashing{ + { + Attestation_1: MockIndexedAttestation(), + Attestation_2: MockIndexedAttestation(), + }, + }, + Attestations: []*Attestation{ + MockAttestation(), + }, + Deposits: []*Deposit{ + { + Proof: []string{"0x41"}, + Data: &DepositData{ + PublicKey: hexutil.Encode(make([]byte, fieldparams.BLSPubkeyLength)), + WithdrawalCredentials: hexutil.Encode(make([]byte, 32)), + Amount: "0", + Signature: hexutil.Encode(make([]byte, fieldparams.BLSSignatureLength)), + }, + }, + }, + VoluntaryExits: []*SignedVoluntaryExit{ + { + Message: &VoluntaryExit{ + Epoch: "0", + ValidatorIndex: "0", + }, + Signature: hexutil.Encode(make([]byte, fieldparams.BLSSignatureLength)), + }, + }, + SyncAggregate: &SyncAggregate{ + SyncCommitteeSignature: hexutil.Encode(make([]byte, fieldparams.BLSSignatureLength)), + SyncCommitteeBits: hexutil.Encode(bitfield.NewBitvector512().Bytes()), + }, + }, + } +} + +func MockBeaconBlockBody() *BeaconBlockBody { + return &BeaconBlockBody{ + RandaoReveal: hexutil.Encode(make([]byte, 32)), + Eth1Data: &Eth1Data{ + DepositRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), + DepositCount: "0", + BlockHash: hexutil.Encode(make([]byte, 32)), + }, + Graffiti: hexutil.Encode(make([]byte, 32)), + ProposerSlashings: []*ProposerSlashing{ + { + SignedHeader_1: &SignedBeaconBlockHeader{ + Message: &BeaconBlockHeader{ + Slot: "0", + ProposerIndex: "0", + ParentRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), + StateRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), + BodyRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), + }, + Signature: hexutil.Encode(make([]byte, fieldparams.BLSSignatureLength)), + }, + SignedHeader_2: &SignedBeaconBlockHeader{ + Message: &BeaconBlockHeader{ + Slot: "0", + ProposerIndex: "0", + ParentRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), + StateRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), + BodyRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), + }, + Signature: hexutil.Encode(make([]byte, fieldparams.BLSSignatureLength)), + }, + }, + }, + AttesterSlashings: []*AttesterSlashing{ + { + Attestation_1: MockIndexedAttestation(), + Attestation_2: MockIndexedAttestation(), + }, + }, + Attestations: []*Attestation{ + MockAttestation(), + }, + Deposits: []*Deposit{ + { + Proof: []string{"0x41"}, + Data: &DepositData{ + PublicKey: hexutil.Encode(make([]byte, fieldparams.BLSPubkeyLength)), + WithdrawalCredentials: hexutil.Encode(make([]byte, 32)), + Amount: "0", + Signature: hexutil.Encode(make([]byte, fieldparams.BLSSignatureLength)), + }, + }, + }, + VoluntaryExits: []*SignedVoluntaryExit{ + { + Message: &VoluntaryExit{ + Epoch: "0", + ValidatorIndex: "0", + }, + Signature: hexutil.Encode(make([]byte, fieldparams.BLSSignatureLength)), + }, + }, + } +} + +func MockContributionAndProof() *ContributionAndProof { + return &ContributionAndProof{ + AggregatorIndex: "0", + Contribution: &SyncCommitteeContribution{ + Slot: "0", + BeaconBlockRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), + SubcommitteeIndex: "0", + AggregationBits: hexutil.Encode(bitfield.NewBitvector128().Bytes()), + Signature: hexutil.Encode(make([]byte, fieldparams.BLSSignatureLength)), + }, + SelectionProof: hexutil.Encode(make([]byte, fieldparams.BLSSignatureLength)), + } +} diff --git a/validator/keymanager/remote-web3signer/v1/web3signer_types.go b/validator/keymanager/remote-web3signer/v1/web3signer_types.go new file mode 100644 index 000000000000..15c492b48eb4 --- /dev/null +++ b/validator/keymanager/remote-web3signer/v1/web3signer_types.go @@ -0,0 +1,320 @@ +// Package v1 defines mappings of types as defined by the web3signer official specification for its v1 version i.e. /api/v1/eth2 +/* Web3Signer Specs are found by searching Consensys' Web3Signer API specification*/ +package v1 + +// AggregationSlotSignRequest is a request object for web3signer sign api. +type AggregationSlotSignRequest struct { + Type string `json:"type"` + ForkInfo *ForkInfo `json:"fork_info"` + SigningRoot string `json:"signingRoot"` + AggregationSlot *AggregationSlot `json:"aggregation_slot"` +} + +// AggregationSlotSignRequest is a request object for web3signer sign api. +type AggregateAndProofSignRequest struct { + Type string `json:"type"` + ForkInfo *ForkInfo `json:"fork_info"` + SigningRoot string `json:"signingRoot"` + AggregateAndProof *AggregateAndProof `json:"aggregation_and_proof"` +} + +// AttestationSignRequest is a request object for web3signer sign api. +type AttestationSignRequest struct { + Type string `json:"type"` + ForkInfo *ForkInfo `json:"fork_info"` + SigningRoot string `json:"signingRoot"` + Attestation *AttestationData `json:"attestation"` +} + +// BlockSignRequest is a request object for web3signer sign api. +type BlockSignRequest struct { + Type string `json:"type"` + ForkInfo *ForkInfo `json:"fork_info"` + SigningRoot string `json:"signingRoot"` + Block *BeaconBlock `json:"block"` +} + +// BlockV2AltairSignRequest is a request object for web3signer sign api. +type BlockV2AltairSignRequest struct { + Type string `json:"type"` + ForkInfo *ForkInfo `json:"fork_info"` + SigningRoot string `json:"signingRoot"` + BeaconBlock *BeaconBlockAltairBlockV2 `json:"beacon_block"` +} + +// BlockV2SignRequest is a request object for web3signer sign api. +type BlockV2SignRequest struct { + Type string `json:"type"` + ForkInfo *ForkInfo `json:"fork_info"` + SigningRoot string `json:"signingRoot"` + BeaconBlock *BeaconBlockBlockV2 `json:"beacon_block"` +} + +// DepositSignRequest Not currently supported by Prysm. +// DepositSignRequest is a request object for web3signer sign api. + +// RandaoRevealSignRequest is a request object for web3signer sign api. +type RandaoRevealSignRequest struct { + Type string `json:"type"` + ForkInfo *ForkInfo `json:"fork_info"` + SigningRoot string `json:"signingRoot"` + RandaoReveal *RandaoReveal `json:"randao_reveal"` +} + +// VoluntaryExitSignRequest is a request object for web3signer sign api. +type VoluntaryExitSignRequest struct { + Type string `json:"type"` + ForkInfo *ForkInfo `json:"fork_info"` + SigningRoot string `json:"signingRoot"` + VoluntaryExit *VoluntaryExit `json:"voluntary_exit"` +} + +// SyncCommitteeMessageSignRequest is a request object for web3signer sign api. +type SyncCommitteeMessageSignRequest struct { + Type string `json:"type"` + ForkInfo *ForkInfo `json:"fork_info"` + SigningRoot string `json:"signingRoot"` + SyncCommittee *SyncCommitteeMessage `json:"sync_committee_message"` +} + +// SyncCommitteeSelectionProofSignRequest is a request object for web3signer sign api. +type SyncCommitteeSelectionProofSignRequest struct { + Type string `json:"type"` + ForkInfo *ForkInfo `json:"fork_info"` + SigningRoot string `json:"signingRoot"` + SyncCommittee *SyncAggregatorSelectionData `json:"sync_committee_selection_proof"` +} + +// SyncCommitteeContributionAndProofSignRequest is a request object for web3signer sign api. +type SyncCommitteeContributionAndProofSignRequest struct { + Type string `json:"type"` + ForkInfo *ForkInfo `json:"fork_info"` + SigningRoot string `json:"signingRoot"` + SyncCommittee *ContributionAndProof `json:"sync_committee_contribution_and_proof"` +} + +//////////////////////////////////////////////////////////////////////////////// +// sub properties of Sign Requests ///////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +// ForkInfo a sub property object of the Sign request +type ForkInfo struct { + Fork *Fork `json:"fork"` + GenesisValidatorsRoot string `json:"genesis_validators_root"` +} + +// Fork a sub property of ForkInfo. +type Fork struct { + PreviousVersion string `json:"previous_version"` + CurrentVersion string `json:"current_version"` + Epoch string `json:"epoch"` +} + +// AggregationSlot a sub property of AggregationSlotSignRequest. +type AggregationSlot struct { + Slot string `json:"slot"` +} + +// AggregateAndProof a sub property of AggregateAndProofSignRequest. +type AggregateAndProof struct { + AggregatorIndex string `json:"aggregator_index"` /* uint64 */ + Aggregate *Attestation `json:"aggregate"` + SelectionProof string `json:"selection_proof"` /* 96 bytes */ +} + +// Attestation a sub property of AggregateAndProofSignRequest. +type Attestation struct { + AggregationBits string `json:"aggregation_bits"` + Data *AttestationData `json:"data"` + Signature string `json:"signature"` +} + +// AttestationData a sub property of Attestation. +type AttestationData struct { + Slot string `json:"slot"` /* uint64 */ + Index string `json:"index"` /* uint64 */ // Prysm uses CommitteeIndex but web3signer uses index. + BeaconBlockRoot string `json:"beacon_block_root"` + Source *Checkpoint `json:"source"` + Target *Checkpoint `json:"target"` +} + +// Checkpoint a sub property of AttestationData. +type Checkpoint struct { + Epoch string `json:"epoch"` + Root string `json:"root"` +} + +// BeaconBlock a sub property of BeaconBlockBlockV2. +type BeaconBlock struct { + Slot string `json:"slot"` /* uint64 */ + ProposerIndex string `json:"proposer_index"` /* uint64 */ + ParentRoot string `json:"parent_root"` + StateRoot string `json:"state_root"` + Body *BeaconBlockBody `json:"body"` +} + +// BeaconBlockBody a sub property of BeaconBlock. +type BeaconBlockBody struct { + RandaoReveal string `json:"randao_reveal"` + Eth1Data *Eth1Data `json:"eth1_data"` + Graffiti string `json:"graffiti"` // 32 bytes + ProposerSlashings []*ProposerSlashing `json:"proposer_slashings"` + AttesterSlashings []*AttesterSlashing `json:"attester_slashings"` + Attestations []*Attestation `json:"attestations"` + Deposits []*Deposit `json:"deposits"` + VoluntaryExits []*SignedVoluntaryExit `json:"voluntary_exits"` +} + +// Eth1Data a sub property of BeaconBlockBody. +type Eth1Data struct { + DepositRoot string `json:"deposit_root"` + DepositCount string `json:"deposit_count"` /* uint64 */ + BlockHash string `json:"block_hash"` +} + +// ProposerSlashing a sub property of BeaconBlockBody. +type ProposerSlashing struct { + // Prysm uses Header_1 but web3signer uses signed_header_1. + SignedHeader_1 *SignedBeaconBlockHeader `json:"signed_header_1"` + // Prysm uses Header_2 but web3signer uses signed_header_2. + SignedHeader_2 *SignedBeaconBlockHeader `json:"signed_header_2"` +} + +// SignedBeaconBlockHeader is a sub property of ProposerSlashing. +type SignedBeaconBlockHeader struct { + Message *BeaconBlockHeader `json:"message"` + Signature string `json:"signature"` +} + +// BeaconBlockHeader is a sub property of SignedBeaconBlockHeader. +type BeaconBlockHeader struct { + Slot string `json:"slot"` /* uint64 */ + ProposerIndex string `json:"proposer_index"` /* uint64 */ + ParentRoot string `json:"parent_root"` /* Hash32 */ + StateRoot string `json:"state_root"` /* Hash32 */ + BodyRoot string `json:"body_root"` /* Hash32 */ +} + +// AttesterSlashing a sub property of BeaconBlockBody. +type AttesterSlashing struct { + Attestation_1 *IndexedAttestation `json:"attestation_1"` + Attestation_2 *IndexedAttestation `json:"attestation_2"` +} + +// IndexedAttestation a sub property of AttesterSlashing. +type IndexedAttestation struct { + AttestingIndices []string `json:"attesting_indices"` /* uint64[] */ + Data *AttestationData `json:"data"` + Signature string `json:"signature"` +} + +// Deposit a sub property of DepositSignRequest. +type Deposit struct { + Proof []string `json:"proof"` + Data *DepositData `json:"data"` +} + +// DepositData a sub property of Deposit. +// DepositData :Prysm uses Deposit_data instead of DepositData which is inconsistent naming +type DepositData struct { + PublicKey string `json:"public_key"` + WithdrawalCredentials string `json:"withdrawal_credentials"` + Amount string `json:"amount"` /* uint64 */ + Signature string `json:"signature"` +} + +// SignedVoluntaryExit is a sub property of BeaconBlockBody. +type SignedVoluntaryExit struct { + // Prysm uses Exit instead of Message + Message *VoluntaryExit `json:"message"` + Signature string `json:"signature"` +} + +// VoluntaryExit a sub property of SignedVoluntaryExit. +type VoluntaryExit struct { + Epoch string `json:"epoch"` /* uint64 */ + ValidatorIndex string `json:"validator_index"` /* uint64 */ +} + +// BeaconBlockAltairBlockV2 a sub property of BlockV2AltairSignRequest. +type BeaconBlockAltairBlockV2 struct { + Version string `json:"version"` + Block *BeaconBlockAltair `json:"beacon_block"` +} + +// BeaconBlockAltair a sub property of BeaconBlockAltairBlockV2. +type BeaconBlockAltair struct { + Slot string `json:"slot"` + ProposerIndex string `json:"proposer_index"` + ParentRoot string `json:"parent_root"` + StateRoot string `json:"state_root"` + Body *BeaconBlockBodyAltair `json:"body"` +} + +// BeaconBlockBodyAltair a sub property of BeaconBlockAltair. +type BeaconBlockBodyAltair struct { + RandaoReveal string `json:"randao_reveal"` + Eth1Data *Eth1Data `json:"eth1_data"` + Graffiti string `json:"graffiti"` /* Hash32 */ + ProposerSlashings []*ProposerSlashing `json:"proposer_slashings"` + AttesterSlashings []*AttesterSlashing `json:"attester_slashings"` + Attestations []*Attestation `json:"attestations"` + Deposits []*Deposit `json:"deposits"` + VoluntaryExits []*SignedVoluntaryExit `json:"voluntary_exits"` + SyncAggregate *SyncAggregate `json:"sync_aggregate"` +} + +// SyncAggregate is a sub property of BeaconBlockBodyAltair. +type SyncAggregate struct { + SyncCommitteeBits string `json:"sync_committee_bits"` /* SSZ hexadecimal string */ + SyncCommitteeSignature string `json:"sync_committee_signature"` /* 96 byte hexadecimal string */ +} + +// BeaconBlockBlockV2 a sub property of BlockV2SignRequest. +type BeaconBlockBlockV2 struct { + Version string `json:"version"` + Block *BeaconBlock `json:"beacon_block"` +} + +// RandaoReveal a sub property of RandaoRevealSignRequest. +type RandaoReveal struct { + Epoch string `json:"epoch"` /* uint64 */ +} + +// SyncCommitteeMessage a sub property of SyncCommitteeSignRequest. +type SyncCommitteeMessage struct { + BeaconBlockRoot string `json:"beacon_block_root"` /* Hash32 */ + Slot string `json:"slot"` /* uint64 */ + // Prysm uses BlockRoot instead of BeaconBlockRoot and has the following extra properties : ValidatorIndex, Signature +} + +// SyncAggregatorSelectionData a sub property of SyncAggregatorSelectionSignRequest. +type SyncAggregatorSelectionData struct { + Slot string `json:"slot"` /* uint64 */ + SubcommitteeIndex string `json:"subcommittee_index"` /* uint64 */ +} + +// ContributionAndProof a sub property of AggregatorSelectionSignRequest. +type ContributionAndProof struct { + AggregatorIndex string `json:"aggregator_index"` /* uint64 */ + SelectionProof string `json:"selection_proof"` /* 96 byte hexadecimal */ + Contribution *SyncCommitteeContribution `json:"contribution"` +} + +// SyncCommitteeContribution a sub property of AggregatorSelectionSignRequest. +type SyncCommitteeContribution struct { + Slot string `json:"slot"` /* uint64 */ + BeaconBlockRoot string `json:"block_root"` /* Hash32 */ // Prysm uses BlockRoot instead of BeaconBlockRoot + SubcommitteeIndex string `json:"subcommittee_index"` /* uint64 */ + AggregationBits string `json:"aggregation_bits"` /* SSZ hexadecimal string */ + Signature string `json:"signature"` /* 96 byte hexadecimal string */ +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +// SignResponse the response object of the web3signer sign api. +type SignResponse struct { + Signature string `json:"signature"` +}