From aed6e134981e7f4f20691f3f4ead95d68a5f0ea3 Mon Sep 17 00:00:00 2001 From: james-prysm <90280386+james-prysm@users.noreply.github.com> Date: Mon, 31 Jan 2022 10:44:17 -0600 Subject: [PATCH] Web3Signer: CLI implementation (#10056) * initial commit for cli integration of web3signer * resolving conflicts and execution * remove aggregation slot from proto * rem aggregation slot * define a sync message block root struct * fix sync message name * sync message block root struct * amend where sync committee block root is used * altered switch statement to return correct json request by type * fixing fork data import, types, and unit tests * reverting unwanted changes * reverting more unwanted changes * fixing deepsource issues * fixing formatting * more fixes for deepsource and code clean up * only want to fetch once for fetch validating public keys * adding more comments * new unit tests for requests and fixing a mapper issue * Update validator/client/validator.go Co-authored-by: Raul Jordan * Update validator/accounts/wallet/wallet.go Co-authored-by: Raul Jordan * adjusting comment * adjusting comment * fixing import organization * including more unit tests * adding new cli edit * adding in checks for wallet initialize * adding web3signer flags to main.go * some how resolved files did not save correctly * adding in check to make sure web flag only works with types imported and derived * Update validator/client/sync_committee.go Co-authored-by: Raul Jordan * Update validator/client/aggregate.go Co-authored-by: Raul Jordan * Update validator/accounts/wallet/wallet.go Co-authored-by: Raul Jordan * Update cmd/validator/wallet/wallet.go Co-authored-by: Raul Jordan * Update cmd/validator/wallet/wallet.go Co-authored-by: Raul Jordan * Update cmd/validator/main.go Co-authored-by: Raul Jordan * Update cmd/validator/flags/flags.go Co-authored-by: Raul Jordan * Update cmd/validator/flags/flags.go Co-authored-by: Raul Jordan * Update cmd/validator/wallet/wallet.go Co-authored-by: Raul Jordan * Update cmd/validator/wallet/wallet.go Co-authored-by: Raul Jordan * reverting changes that accidently got checked in * reverting * reverting * continuing to revert unintenteded changes * reverting * removing more unneeded changes * addressing review comment * initial refactor * adding in more clarifying comments * fixing mock * resolving desource issues * addressing gosec scan for helper go file * addressing gosec * trying to fix bazel build * removal of interface to fix build * fixing maligned struct * addressing deepsource * fixing deepsource * addressing efficiency of type checking * fixing bazel test failure * fixing go linter errors * gaz * web changes * add w3signer * new kind * proper use * align * adding prysm validator flags to help flags list * addressing root comment * ci lint * fixing standardapi tests * fixing accounts_test after removal of keymanager from rpc server * fixing more unit tests * Update cmd/validator/flags/flags.go Co-authored-by: Raul Jordan * Update cmd/validator/flags/flags.go Co-authored-by: Raul Jordan * Update validator/client/service.go Co-authored-by: Raul Jordan * Update validator/client/service.go Co-authored-by: Raul Jordan * addressing missed err checks * fixing mock tests * fixing gofmt * unskipping minimal e2e test and removing related TODOs * Update testing/endtoend/components/validator.go Co-authored-by: Preston Van Loon * Update testing/endtoend/components/validator.go Co-authored-by: Preston Van Loon * adding some error wrapers to clarify failure point * fixing bazel build with new error checks * taking preston's advice to make test fail faster to understand what's going on with the test * checking if genesis validators root is not zero hash * adding check for genesis validators root giving zero hash * fixing missing dependency * adding check for wallet * log all * fixing errors for http responses * switching marshal to pretty print * adding pretty sign request test * fixing base url setting * adding in check for web3signer and temporary wallet instead of having to open the wallet * refactoring web3signer to not require wallet * bazel build fix * fixing gazelle build * adding content type of request * fixing more bazel * removing unused code * removing unused comments * adding skip test back in * addressing a validation and error message * fix parse * body * fixing logic for datadir * improving error handling * show resp * fix * sign resp as str * point of pointer remove * sign resp * unmarshal sig resp * read body as str * adding more verbose logging * removing unused result * fixing unit test * reconfiguring files to properly nest code and mocks * fix build issue * using context when using client function calls * fixing based on suggestion * addressing comments * gaz * removing defined max timeout * reverting json print pretty * Update validator/accounts/wallet_edit.go Co-authored-by: Preston Van Loon * removing unneeded code restrictions * should not introduce new code that may impact existing key manager types * adjusting comments * adding in json validation * running go mod tidy * some logging * more logs * fixing typo * remove logs * testing without byte trim * fixing order or properties * gaz * tidy * reverting some logs * removing the confusing comments * Update validator/client/aggregate.go Co-authored-by: Raul Jordan * Update validator/client/aggregate.go Co-authored-by: Raul Jordan * addressing pr comments * editing bytes test * Run gazelle update-repos * run gazelle * improving unit test coverage * fixing text * fixing a potential escaped error Co-authored-by: Raul Jordan Co-authored-by: Preston Van Loon --- .../rpc/prysm/v1alpha1/beacon/BUILD.bazel | 1 + .../rpc/prysm/v1alpha1/beacon/blocks.go | 7 +- cmd/validator/flags/flags.go | 19 ++ cmd/validator/main.go | 8 +- cmd/validator/usage.go | 2 + deps.bzl | 107 +++++----- encoding/bytesutil/BUILD.bazel | 2 + encoding/bytesutil/bytes.go | 6 + encoding/bytesutil/bytes_test.go | 11 ++ go.mod | 3 +- go.sum | 19 +- .../v1alpha1/validator-client/web_api.pb.go | 20 +- .../v1alpha1/validator-client/web_api.proto | 1 + testing/endtoend/components/validator.go | 6 +- .../endtoend/components/web3remotesigner.go | 1 + testing/endtoend/evaluators/operations.go | 5 +- testing/endtoend/helpers/helpers.go | 4 + testing/endtoend/minimal_e2e_test.go | 2 +- validator/accounts/BUILD.bazel | 1 + validator/accounts/accounts_backup.go | 7 +- validator/accounts/accounts_delete.go | 6 + validator/accounts/accounts_exit.go | 7 +- validator/accounts/accounts_list.go | 47 +++++ validator/accounts/iface/BUILD.bazel | 5 +- validator/accounts/iface/wallet.go | 2 + validator/accounts/testing/BUILD.bazel | 2 + validator/accounts/testing/mock.go | 103 ++++++++++ validator/accounts/wallet/BUILD.bazel | 6 + validator/accounts/wallet/wallet.go | 36 +++- validator/accounts/wallet/wallet_test.go | 35 ++++ validator/accounts/wallet_create.go | 19 +- validator/client/BUILD.bazel | 4 + validator/client/iface/validator.go | 4 +- validator/client/runner.go | 22 ++- validator/client/runner_test.go | 29 +-- validator/client/service.go | 103 +++------- validator/client/testutil/mock_validator.go | 10 +- validator/client/validator.go | 149 +++++++++++--- validator/client/validator_test.go | 32 +++ validator/client/wait_for_activation.go | 6 +- validator/keymanager/imported/keymanager.go | 8 + .../keymanager/remote-web3signer/BUILD.bazel | 17 +- .../keymanager/remote-web3signer/client.go | 159 --------------- .../remote-web3signer/client_test.go | 88 --------- .../remote-web3signer/internal/BUILD.bazel | 28 +++ .../remote-web3signer/internal/client.go | 175 +++++++++++++++++ .../remote-web3signer/internal/client_test.go | 155 +++++++++++++++ .../remote-web3signer/internal/log.go | 7 + .../remote-web3signer/keymanager.go | 82 ++++---- .../remote-web3signer/keymanager_test.go | 49 ++--- validator/keymanager/remote-web3signer/log.go | 7 - .../remote-web3signer/v1/BUILD.bazel | 6 +- .../remote-web3signer/v1/custom_mappers.go | 6 +- .../v1/custom_mappers_test.go | 68 +++---- .../remote-web3signer/v1/mock/BUILD.bazel | 17 ++ .../remote-web3signer/v1/{ => mock}/mocks.go | 185 ++++++++++-------- .../remote-web3signer/v1/requests_test.go | 84 ++++---- .../remote-web3signer/v1/web3signer_types.go | 71 ++++--- validator/keymanager/types.go | 6 + validator/node/BUILD.bazel | 5 +- validator/node/node.go | 140 ++++++++----- validator/rpc/BUILD.bazel | 2 +- validator/rpc/accounts.go | 66 ++++--- validator/rpc/accounts_test.go | 79 ++++++-- validator/rpc/server.go | 4 - validator/rpc/standard_api.go | 27 ++- validator/rpc/standard_api_test.go | 35 +++- validator/rpc/wallet.go | 37 ++-- validator/rpc/wallet_test.go | 73 ++++++- 69 files changed, 1647 insertions(+), 898 deletions(-) delete mode 100644 validator/keymanager/remote-web3signer/client.go delete mode 100644 validator/keymanager/remote-web3signer/client_test.go create mode 100644 validator/keymanager/remote-web3signer/internal/BUILD.bazel create mode 100644 validator/keymanager/remote-web3signer/internal/client.go create mode 100644 validator/keymanager/remote-web3signer/internal/client_test.go create mode 100644 validator/keymanager/remote-web3signer/internal/log.go delete mode 100644 validator/keymanager/remote-web3signer/log.go create mode 100644 validator/keymanager/remote-web3signer/v1/mock/BUILD.bazel rename validator/keymanager/remote-web3signer/v1/{ => mock}/mocks.go (83%) diff --git a/beacon-chain/rpc/prysm/v1alpha1/beacon/BUILD.bazel b/beacon-chain/rpc/prysm/v1alpha1/beacon/BUILD.bazel index 435458f2bc..258279cfb5 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/beacon/BUILD.bazel +++ b/beacon-chain/rpc/prysm/v1alpha1/beacon/BUILD.bazel @@ -54,6 +54,7 @@ go_library( "//runtime/version:go_default_library", "//time/slots:go_default_library", "@com_github_patrickmn_go_cache//:go_default_library", + "@com_github_pkg_errors//:go_default_library", "@com_github_prometheus_client_golang//prometheus:go_default_library", "@com_github_prometheus_client_golang//prometheus/promauto:go_default_library", "@com_github_prysmaticlabs_eth2_types//:go_default_library", diff --git a/beacon-chain/rpc/prysm/v1alpha1/beacon/blocks.go b/beacon-chain/rpc/prysm/v1alpha1/beacon/blocks.go index d097fa0d95..d00a63fd5f 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/beacon/blocks.go +++ b/beacon-chain/rpc/prysm/v1alpha1/beacon/blocks.go @@ -4,6 +4,7 @@ import ( "context" "strconv" + "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/api/pagination" "github.com/prysmaticlabs/prysm/async/event" "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" @@ -436,15 +437,15 @@ func (bs *Server) chainHeadRetrieval(ctx context.Context) (*ethpb.ChainHead, err fSlot, err := slots.EpochStart(finalizedCheckpoint.Epoch) if err != nil { - return nil, err + return nil, errors.Wrap(err, "could not get epoch start slot from finalized checkpoint epoch") } jSlot, err := slots.EpochStart(justifiedCheckpoint.Epoch) if err != nil { - return nil, err + return nil, errors.Wrap(err, "could not get epoch start slot from justified checkpoint epoch") } pjSlot, err := slots.EpochStart(prevJustifiedCheckpoint.Epoch) if err != nil { - return nil, err + return nil, errors.Wrap(err, "could not get epoch start slot from prev justified checkpoint epoch") } return ðpb.ChainHead{ HeadSlot: headBlock.Block().Slot(), diff --git a/cmd/validator/flags/flags.go b/cmd/validator/flags/flags.go index bee8cc8bd8..ba6d930b6b 100644 --- a/cmd/validator/flags/flags.go +++ b/cmd/validator/flags/flags.go @@ -267,6 +267,25 @@ var ( Usage: "/path/to/ca.crt for establishing a secure, TLS gRPC connection to a remote signer server", Value: "", } + // Web3SignerURLFlag defines the URL for a web3signer to connect to. + // example:--validators-external-signer-url=http://localhost:9000 + // web3signer documentation can be found in Consensys' web3signer project docs + Web3SignerURLFlag = &cli.StringFlag{ + Name: "validators-external-signer-url", + Usage: "URL for consensys' web3signer software to use with the Prysm validator client", + Value: "", + } + + // Web3SignerPublicValidatorKeysFlag defines a comma-separated list of hex string public keys or external url for web3signer to use for validator signing. + // example with external url: --validators-external-signer-public-keys= https://web3signer.com/api/v1/eth2/publicKeys + // example with public key: --validators-external-signer-public-keys=0xa99a...e44c,0xb89b...4a0b + // web3signer documentation can be found in Consensys' web3signer project docs``` + Web3SignerPublicValidatorKeysFlag = &cli.StringFlag{ + Name: "validators-external-signer-public-keys", + Usage: "comma separated list of public keys OR an external url endpoint for the validator to retrieve public keys from for usage with web3signer", + Value: "", + } + // KeymanagerKindFlag defines the kind of keymanager desired by a user during wallet creation. KeymanagerKindFlag = &cli.StringFlag{ Name: "keymanager-kind", diff --git a/cmd/validator/main.go b/cmd/validator/main.go index 264ef9ac8e..b06438e4db 100644 --- a/cmd/validator/main.go +++ b/cmd/validator/main.go @@ -64,9 +64,7 @@ var appFlags = []cli.Flag{ flags.GrpcHeadersFlag, flags.GPRCGatewayCorsDomain, flags.DisableAccountMetricsFlag, - cmd.MonitoringHostFlag, flags.MonitoringPortFlag, - cmd.DisableMonitoringFlag, flags.SlasherRPCProviderFlag, flags.SlasherCertFlag, flags.WalletPasswordFileFlag, @@ -74,6 +72,12 @@ var appFlags = []cli.Flag{ flags.EnableWebFlag, flags.GraffitiFileFlag, flags.EnableDutyCountDown, + // Consensys' Web3Signer flags + flags.Web3SignerURLFlag, + flags.Web3SignerPublicValidatorKeysFlag, + //////////////////// + cmd.DisableMonitoringFlag, + cmd.MonitoringHostFlag, cmd.BackupWebhookOutputDir, cmd.EnableBackupWebhookFlag, cmd.MinimalConfigFlag, diff --git a/cmd/validator/usage.go b/cmd/validator/usage.go index 0deefeba2d..f1ca834d57 100644 --- a/cmd/validator/usage.go +++ b/cmd/validator/usage.go @@ -106,6 +106,8 @@ var appHelpFlagGroups = []flagGroup{ flags.WalletPasswordFileFlag, flags.GraffitiFileFlag, flags.EnableDutyCountDown, + flags.Web3SignerURLFlag, + flags.Web3SignerPublicValidatorKeysFlag, }, }, { diff --git a/deps.bzl b/deps.bzl index f8e65ac1ff..56de3c28ba 100644 --- a/deps.bzl +++ b/deps.bzl @@ -959,12 +959,7 @@ def prysm_deps(): sum = "h1:gclg6gY70GLy3PbkQ1AERPfmLMMagS60DKF78eWwLn8=", version = "v0.0.0-20190410193231-58a59202ab31", ) - go_repository( - name = "com_github_go_check_check", - importpath = "github.com/go-check/check", - sum = "h1:0gkP6mzaMqkmpcJYCFOLkIBwI7xFExG03bbkOkCvUPI=", - version = "v0.0.0-20180628173108-788fd7840127", - ) + go_repository( name = "com_github_go_chi_chi_v5", importpath = "github.com/go-chi/chi/v5", @@ -1046,6 +1041,31 @@ def prysm_deps(): sum = "h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=", version = "v0.19.5", ) + go_repository( + name = "com_github_go_playground_assert_v2", + importpath = "github.com/go-playground/assert/v2", + sum = "h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=", + version = "v2.0.1", + ) + go_repository( + name = "com_github_go_playground_locales", + importpath = "github.com/go-playground/locales", + sum = "h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=", + version = "v0.14.0", + ) + go_repository( + name = "com_github_go_playground_universal_translator", + importpath = "github.com/go-playground/universal-translator", + sum = "h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=", + version = "v0.18.0", + ) + go_repository( + name = "com_github_go_playground_validator_v10", + importpath = "github.com/go-playground/validator/v10", + sum = "h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=", + version = "v10.10.0", + ) + go_repository( name = "com_github_go_sourcemap_sourcemap", importpath = "github.com/go-sourcemap/sourcemap", @@ -1682,13 +1702,6 @@ def prysm_deps(): version = "v2.4.0", ) - go_repository( - name = "com_github_jackpal_gateway", - importpath = "github.com/jackpal/gateway", - sum = "h1:qzXWUJfuMdlLMtt0a3Dgt+xkWQiA5itDEITVJtuSwMc=", - version = "v1.0.5", - ) - go_repository( name = "com_github_jackpal_go_nat_pmp", importpath = "github.com/jackpal/go-nat-pmp", @@ -1949,8 +1962,8 @@ def prysm_deps(): go_repository( name = "com_github_kr_pretty", importpath = "github.com/kr/pretty", - sum = "h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=", - version = "v0.2.1", + sum = "h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=", + version = "v0.3.0", ) go_repository( name = "com_github_kr_pty", @@ -1964,12 +1977,7 @@ def prysm_deps(): sum = "h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=", version = "v0.2.0", ) - go_repository( - name = "com_github_kubuxu_go_os_helper", - importpath = "github.com/Kubuxu/go-os-helper", - sum = "h1:EJiD2VUQyh5A9hWJLmc6iWg6yIcJ7jpBcwC8GMGXfDk=", - version = "v0.0.1", - ) + go_repository( name = "com_github_kylelemons_godebug", importpath = "github.com/kylelemons/godebug", @@ -1995,6 +2003,13 @@ def prysm_deps(): sum = "h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=", version = "v0.2.9", ) + go_repository( + name = "com_github_leodido_go_urn", + importpath = "github.com/leodido/go-urn", + sum = "h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=", + version = "v1.2.1", + ) + go_repository( name = "com_github_lib_pq", importpath = "github.com/lib/pq", @@ -2085,12 +2100,6 @@ def prysm_deps(): sum = "h1:IFG/s8dN6JN2OTrXX9eq2wNU/Zlz2KLdwZUp5FplgXI=", version = "v0.13.0", ) - go_repository( - name = "com_github_libp2p_go_libp2p_crypto", - importpath = "github.com/libp2p/go-libp2p-crypto", - sum = "h1:k9MFy+o2zGDNGsaoZl0MA3iZ75qXxr9OOoAZF+sD5OQ=", - version = "v0.1.0", - ) go_repository( name = "com_github_libp2p_go_libp2p_discovery", @@ -2132,12 +2141,7 @@ def prysm_deps(): sum = "h1:NCVH7evhVt9njbTQshzT7N1S3Q6fjj9M11FCgfH5+cA=", version = "v0.3.0", ) - go_repository( - name = "com_github_libp2p_go_libp2p_peer", - importpath = "github.com/libp2p/go-libp2p-peer", - sum = "h1:EQ8kMjaCUwt/Y5uLgjT8iY2qg0mGUT0N1zUjer50DsY=", - version = "v0.2.0", - ) + go_repository( name = "com_github_libp2p_go_libp2p_peerstore", importpath = "github.com/libp2p/go-libp2p-peerstore", @@ -2165,13 +2169,6 @@ def prysm_deps(): version = "v0.15.2", ) - go_repository( - name = "com_github_libp2p_go_libp2p_secio", - build_file_proto_mode = "disable_global", - importpath = "github.com/libp2p/go-libp2p-secio", - sum = "h1:rLLPvShPQAcY6eNurKNZq3eZjPWfU9kXF2eI9jIYdrg=", - version = "v0.2.2", - ) go_repository( name = "com_github_libp2p_go_libp2p_swarm", build_file_proto_mode = "disable_global", @@ -2259,12 +2256,6 @@ def prysm_deps(): sum = "h1:yD80l2ZOdGksnOyHrhxDdTDFrf7Oy+v3FMVArIRgZxQ=", version = "v0.1.1", ) - go_repository( - name = "com_github_libp2p_go_stream_muxer", - importpath = "github.com/libp2p/go-stream-muxer", - sum = "h1:Ce6e2Pyu+b5MC1k3eeFtAax0pW4gc6MosYSLV05UeLw=", - version = "v0.0.1", - ) go_repository( name = "com_github_libp2p_go_stream_muxer_multistream", @@ -2372,8 +2363,8 @@ def prysm_deps(): go_repository( name = "com_github_marten_seemann_qtls_go1_15", importpath = "github.com/marten-seemann/qtls-go1-15", - sum = "h1:Ci4EIUN6Rlb+D6GmLdej/bCQ4nPYNtVXQB+xjiXE1nk=", - version = "v0.1.5", + sum = "h1:RehYMOyRW8hPVEja1KBVsFVNSm35Jj9Mvs5yNoZZ28A=", + version = "v0.1.4", ) go_repository( name = "com_github_marten_seemann_qtls_go1_16", @@ -2904,6 +2895,12 @@ def prysm_deps(): sum = "h1:mFe7ttWaflA46Mhqh+jUfjp2qTbPYxLB2/OyBppH9dg=", version = "v2.4.1+incompatible", ) + go_repository( + name = "com_github_pkg_diff", + importpath = "github.com/pkg/diff", + sum = "h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=", + version = "v0.0.0-20210226163009-20ebb0f2a09e", + ) go_repository( name = "com_github_pkg_errors", @@ -3047,8 +3044,8 @@ def prysm_deps(): go_repository( name = "com_github_rogpeppe_go_internal", importpath = "github.com/rogpeppe/go-internal", - sum = "h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=", - version = "v1.3.0", + sum = "h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=", + version = "v1.8.0", ) go_repository( @@ -3635,16 +3632,10 @@ def prysm_deps(): go_repository( name = "com_github_whyrusleeping_go_logging", importpath = "github.com/whyrusleeping/go-logging", - sum = "h1:fwpzlmT0kRC/Fmd0MdmGgJG/CXIZ6gFq46FQZjprUcc=", - version = "v0.0.1", + sum = "h1:9lDbC6Rz4bwmou+oE6Dt4Cb2BGMur5eR/GYptkKUVHo=", + version = "v0.0.0-20170515211332-0457bb6b88fc", ) - go_repository( - name = "com_github_whyrusleeping_mafmt", - importpath = "github.com/whyrusleeping/mafmt", - sum = "h1:TCghSl5kkwEE0j+sU/gudyhVMRlpBin8fMBBHg59EbA=", - version = "v1.2.8", - ) go_repository( name = "com_github_whyrusleeping_mdns", importpath = "github.com/whyrusleeping/mdns", diff --git a/encoding/bytesutil/BUILD.bazel b/encoding/bytesutil/BUILD.bazel index 81da860d7b..00fb3be2e1 100644 --- a/encoding/bytesutil/BUILD.bazel +++ b/encoding/bytesutil/BUILD.bazel @@ -6,6 +6,7 @@ go_library( importpath = "github.com/prysmaticlabs/prysm/encoding/bytesutil", visibility = ["//visibility:public"], deps = [ + "//config/fieldparams:go_default_library", "@com_github_pkg_errors//:go_default_library", "@com_github_prysmaticlabs_eth2_types//:go_default_library", ], @@ -17,6 +18,7 @@ go_test( srcs = ["bytes_test.go"], deps = [ ":go_default_library", + "//config/fieldparams:go_default_library", "//testing/assert:go_default_library", "//testing/require:go_default_library", ], diff --git a/encoding/bytesutil/bytes.go b/encoding/bytesutil/bytes.go index efdd78aa75..8a5a65b871 100644 --- a/encoding/bytesutil/bytes.go +++ b/encoding/bytesutil/bytes.go @@ -9,6 +9,7 @@ import ( "github.com/pkg/errors" types "github.com/prysmaticlabs/eth2-types" + fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams" ) var hexRegex = regexp.MustCompile("^0x[0-9a-fA-F]+$") @@ -404,3 +405,8 @@ func ReverseByteOrder(input []byte) []byte { } return b } + +// NonZeroRoot returns whether or not a root is of proper length and non-zero hash. +func NonZeroRoot(root []byte) bool { + return len(root) == fieldparams.RootLength && string(make([]byte, fieldparams.RootLength)) != string(root) +} diff --git a/encoding/bytesutil/bytes_test.go b/encoding/bytesutil/bytes_test.go index 35e8185328..e758f4e623 100644 --- a/encoding/bytesutil/bytes_test.go +++ b/encoding/bytesutil/bytes_test.go @@ -5,6 +5,7 @@ import ( "reflect" "testing" + fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams" "github.com/prysmaticlabs/prysm/encoding/bytesutil" "github.com/prysmaticlabs/prysm/testing/assert" "github.com/prysmaticlabs/prysm/testing/require" @@ -516,3 +517,13 @@ func TestSafeCopy2d32Bytes(t *testing.T) { assert.Equal(t, false, &input == &output, "No copy was made") assert.DeepEqual(t, input, output) } + +func TestNonZeroRoot(t *testing.T) { + input := make([]byte, fieldparams.RootLength) + output := bytesutil.NonZeroRoot(input) + assert.Equal(t, false, output) + copy(input[2:], "a") + copy(input[3:], "b") + output = bytesutil.NonZeroRoot(input) + assert.Equal(t, true, output) +} diff --git a/go.mod b/go.mod index 03b4733bbd..9b4e13a8d2 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ require ( github.com/json-iterator/go v1.1.11 github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 github.com/kevinms/leakybucket-go v0.0.0-20200115003610-082473db97ca - github.com/kr/pretty v0.2.1 + github.com/kr/pretty v0.3.0 github.com/libp2p/go-libp2p v0.17.0 github.com/libp2p/go-libp2p-blankhost v0.3.0 github.com/libp2p/go-libp2p-core v0.13.0 @@ -103,6 +103,7 @@ require ( github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect github.com/go-logr/logr v0.2.1 // indirect github.com/go-ole/go-ole v1.2.5 // indirect + github.com/go-playground/validator/v10 v10.10.0 github.com/peterh/liner v1.2.0 // indirect github.com/prometheus/tsdb v0.10.0 // indirect golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 // indirect diff --git a/go.sum b/go.sum index c269168850..1dab825f48 100644 --- a/go.sum +++ b/go.sum @@ -326,6 +326,14 @@ github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1 github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= +github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= @@ -644,8 +652,9 @@ github.com/koron/go-ssdp v0.0.2/go.mod h1:XoLfkAiA2KeZsYh4DbHxD7h3nR2AZNqVQOa+LJ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -655,6 +664,8 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E= github.com/libp2p/go-addr-util v0.1.0 h1:acKsntI33w2bTU7tC9a0SaPimJGfSI0bFKC18ChxeVI= @@ -1042,6 +1053,7 @@ github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.4.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -1127,6 +1139,9 @@ github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRr github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= @@ -1370,6 +1385,7 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI= golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -1592,6 +1608,7 @@ golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc= diff --git a/proto/prysm/v1alpha1/validator-client/web_api.pb.go b/proto/prysm/v1alpha1/validator-client/web_api.pb.go index 8ab548f56a..a13d2b3a78 100755 --- a/proto/prysm/v1alpha1/validator-client/web_api.pb.go +++ b/proto/prysm/v1alpha1/validator-client/web_api.pb.go @@ -36,9 +36,10 @@ const _ = proto.ProtoPackageIsVersion4 type KeymanagerKind int32 const ( - KeymanagerKind_DERIVED KeymanagerKind = 0 - KeymanagerKind_IMPORTED KeymanagerKind = 1 - KeymanagerKind_REMOTE KeymanagerKind = 2 + KeymanagerKind_DERIVED KeymanagerKind = 0 + KeymanagerKind_IMPORTED KeymanagerKind = 1 + KeymanagerKind_REMOTE KeymanagerKind = 2 + KeymanagerKind_WEB3SIGNER KeymanagerKind = 3 ) // Enum value maps for KeymanagerKind. @@ -47,11 +48,13 @@ var ( 0: "DERIVED", 1: "IMPORTED", 2: "REMOTE", + 3: "WEB3SIGNER", } KeymanagerKind_value = map[string]int32{ - "DERIVED": 0, - "IMPORTED": 1, - "REMOTE": 2, + "DERIVED": 0, + "IMPORTED": 1, + "REMOTE": 2, + "WEB3SIGNER": 3, } ) @@ -1940,11 +1943,12 @@ var file_proto_prysm_v1alpha1_validator_client_web_api_proto_rawDesc = []byte{ 0x38, 0x0a, 0x18, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x74, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x73, 0x6f, 0x6e, 0x2a, 0x37, 0x0a, 0x0e, 0x4b, 0x65, 0x79, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x73, 0x6f, 0x6e, 0x2a, 0x47, 0x0a, 0x0e, 0x4b, 0x65, 0x79, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x52, 0x49, 0x56, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x49, 0x4d, 0x50, 0x4f, 0x52, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, - 0x10, 0x02, 0x32, 0x99, 0x06, 0x0a, 0x06, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x12, 0xa1, 0x01, + 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x57, 0x45, 0x42, 0x33, 0x53, 0x49, 0x47, 0x4e, 0x45, 0x52, + 0x10, 0x03, 0x32, 0x99, 0x06, 0x0a, 0x06, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x12, 0xa1, 0x01, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x12, 0x33, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, diff --git a/proto/prysm/v1alpha1/validator-client/web_api.proto b/proto/prysm/v1alpha1/validator-client/web_api.proto index dbebd117d5..d3db06c6f5 100644 --- a/proto/prysm/v1alpha1/validator-client/web_api.proto +++ b/proto/prysm/v1alpha1/validator-client/web_api.proto @@ -173,6 +173,7 @@ enum KeymanagerKind { DERIVED = 0; IMPORTED = 1; REMOTE = 2; + WEB3SIGNER = 3; } message CreateWalletRequest { diff --git a/testing/endtoend/components/validator.go b/testing/endtoend/components/validator.go index 431163db4f..f2b6f10268 100644 --- a/testing/endtoend/components/validator.go +++ b/testing/endtoend/components/validator.go @@ -149,8 +149,7 @@ func (v *ValidatorNode) Start(ctx context.Context) error { args = append(args, features.E2EValidatorFlags...) } if v.config.UseWeb3RemoteSigner { - // TODO(9994): Replace "validators-external-signer-url" with flags.Web3RemoteSignerURLFlag.Name - args = append(args, fmt.Sprintf("--%s=localhost:%d", "validators-external-signer-url", Web3RemoteSignerPort)) + args = append(args, fmt.Sprintf("--%s=localhost:%d", flags.Web3SignerURLFlag.Name, Web3RemoteSignerPort)) // Write the pubkeys as comma seperated hex strings with 0x prefix. // See: https://docs.teku.consensys.net/en/latest/HowTo/External-Signer/Use-External-Signer/ _, pubs, err := interop.DeterministicallyGenerateKeys(uint64(offset), uint64(validatorNum)) @@ -161,8 +160,7 @@ func (v *ValidatorNode) Start(ctx context.Context) error { for _, pub := range pubs { hexPubs = append(hexPubs, "0x"+hex.EncodeToString(pub.Marshal())) } - // TODO(9994): Replace "validators-external-signer-public-keys" with flags.Web3RemoteSignerPubkeysFlag.Name - args = append(args, fmt.Sprintf("--validators-external-signer-public-keys=%s", strings.Join(hexPubs, ","))) + args = append(args, fmt.Sprintf("--%s=%s", flags.Web3SignerPublicValidatorKeysFlag.Name, strings.Join(hexPubs, ","))) } else { // When not using remote key signer, use interop keys. args = append(args, diff --git a/testing/endtoend/components/web3remotesigner.go b/testing/endtoend/components/web3remotesigner.go index d3ca6ea80d..27c88126a8 100644 --- a/testing/endtoend/components/web3remotesigner.go +++ b/testing/endtoend/components/web3remotesigner.go @@ -71,6 +71,7 @@ func (w *Web3RemoteSigner) Start(ctx context.Context) error { fmt.Sprintf("--key-store-path=%s", keystorePath), fmt.Sprintf("--data-path=%s", websignerDataDir), fmt.Sprintf("--http-listen-port=%d", Web3RemoteSignerPort), + "--logging=ALL", // Command "eth2", // Command flags diff --git a/testing/endtoend/evaluators/operations.go b/testing/endtoend/evaluators/operations.go index 55bb6f8a61..4c21babc68 100644 --- a/testing/endtoend/evaluators/operations.go +++ b/testing/endtoend/evaluators/operations.go @@ -145,8 +145,7 @@ func verifyGraffitiInBlocks(conns ...*grpc.ClientConn) error { if err != nil { return errors.Wrap(err, "failed to get chain head") } - - req := ðpb.ListBlocksRequest{QueryFilter: ðpb.ListBlocksRequest_Epoch{Epoch: chainHead.HeadEpoch - 1}} + req := ðpb.ListBlocksRequest{QueryFilter: ðpb.ListBlocksRequest_Epoch{Epoch: chainHead.HeadEpoch.Sub(1)}} blks, err := client.ListBeaconBlocks(context.Background(), req) if err != nil { return errors.Wrap(err, "failed to get blocks from beacon-chain") @@ -355,7 +354,7 @@ func validatorsVoteWithTheMajority(conns ...*grpc.ClientConn) error { return errors.Wrap(err, "failed to get chain head") } - req := ðpb.ListBlocksRequest{QueryFilter: ðpb.ListBlocksRequest_Epoch{Epoch: chainHead.HeadEpoch - 1}} + req := ðpb.ListBlocksRequest{QueryFilter: ðpb.ListBlocksRequest_Epoch{Epoch: chainHead.HeadEpoch.Sub(1)}} blks, err := client.ListBeaconBlocks(context.Background(), req) if err != nil { return errors.Wrap(err, "failed to get blocks from beacon-chain") diff --git a/testing/endtoend/helpers/helpers.go b/testing/endtoend/helpers/helpers.go index 89d9b3b817..9ac0c07f6d 100644 --- a/testing/endtoend/helpers/helpers.go +++ b/testing/endtoend/helpers/helpers.go @@ -48,7 +48,9 @@ func DeleteAndCreateFile(tmpPath, fileName string) (*os.File, error) { return nil, err } } + newFile, err := os.Create(filepath.Clean(path.Join(tmpPath, fileName))) + if err != nil { return nil, err } @@ -176,7 +178,9 @@ func writeURLRespAtPath(url, fp string) error { if err != nil { return err } + file, err := os.Create(filepath.Clean(fp)) + if err != nil { return err } diff --git a/testing/endtoend/minimal_e2e_test.go b/testing/endtoend/minimal_e2e_test.go index ccc0ff9d5a..6327a133a2 100644 --- a/testing/endtoend/minimal_e2e_test.go +++ b/testing/endtoend/minimal_e2e_test.go @@ -27,7 +27,7 @@ func TestEndToEnd_MinimalConfig(t *testing.T) { } func TestEndToEnd_MinimalConfig_Web3Signer(t *testing.T) { - t.Skip("TODO(9994): Complete web3signer client implementation") + t.Skip("TODO(9994): Complete web3signer client implementation, currently blocked by https://github.com/ConsenSys/web3signer/issues/494") e2eMinimal(t, &testArgs{ usePrysmSh: false, useWeb3RemoteSigner: true, diff --git a/validator/accounts/BUILD.bazel b/validator/accounts/BUILD.bazel index 1a707c684d..e35db12822 100644 --- a/validator/accounts/BUILD.bazel +++ b/validator/accounts/BUILD.bazel @@ -44,6 +44,7 @@ go_library( "//validator/keymanager/derived:go_default_library", "//validator/keymanager/imported:go_default_library", "//validator/keymanager/remote:go_default_library", + "//validator/keymanager/remote-web3signer:go_default_library", "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", "@com_github_google_uuid//:go_default_library", "@com_github_logrusorgru_aurora//:go_default_library", diff --git a/validator/accounts/accounts_backup.go b/validator/accounts/accounts_backup.go index e642ebacda..0b47d9dd50 100644 --- a/validator/accounts/accounts_backup.go +++ b/validator/accounts/accounts_backup.go @@ -48,9 +48,10 @@ func BackupAccountsCli(cliCtx *cli.Context) error { if err != nil { return errors.Wrap(err, "could not initialize wallet") } - if w.KeymanagerKind() == keymanager.Remote { + // TODO(#9883) - Remove this when we have a better way to handle this. + if w.KeymanagerKind() == keymanager.Remote || w.KeymanagerKind() == keymanager.Web3Signer { return errors.New( - "remote wallets cannot backup accounts", + "remote and web3signer wallets cannot backup accounts", ) } km, err := w.InitializeKeymanager(cliCtx.Context, iface.InitKeymanagerConfig{ListenForChanges: false}) @@ -115,6 +116,8 @@ func BackupAccountsCli(cliCtx *cli.Context) error { } case keymanager.Remote: return errors.New("backing up keys is not supported for a remote keymanager") + case keymanager.Web3Signer: + return errors.New("backing up keys is not supported for a web3signer keymanager") default: return fmt.Errorf(errKeymanagerNotSupported, w.KeymanagerKind()) } diff --git a/validator/accounts/accounts_delete.go b/validator/accounts/accounts_delete.go index 936f2b6fb1..72d9fd9ffb 100644 --- a/validator/accounts/accounts_delete.go +++ b/validator/accounts/accounts_delete.go @@ -27,6 +27,12 @@ func DeleteAccountCli(cliCtx *cli.Context) error { if err != nil { return errors.Wrap(err, "could not open wallet") } + // TODO(#9883) - Remove this when we have a better way to handle this. + if w.KeymanagerKind() == keymanager.Remote || w.KeymanagerKind() == keymanager.Web3Signer { + return errors.New( + "remote and web3signer wallets cannot delete accounts locally. please delete the account on the remote signer node", + ) + } kManager, err := w.InitializeKeymanager(cliCtx.Context, iface.InitKeymanagerConfig{ListenForChanges: false}) if err != nil { return errors.Wrap(err, ErrCouldNotInitializeKeymanager) diff --git a/validator/accounts/accounts_exit.go b/validator/accounts/accounts_exit.go index 833f22a181..ccaf94ed2e 100644 --- a/validator/accounts/accounts_exit.go +++ b/validator/accounts/accounts_exit.go @@ -135,7 +135,12 @@ func prepareWallet(cliCtx *cli.Context) (validatingPublicKeys [][fieldparams.BLS if err != nil { return nil, nil, errors.Wrap(err, "could not open wallet") } - + // TODO(#9883) - Remove this when we have a better way to handle this. + if w.KeymanagerKind() == keymanager.Web3Signer { + return nil, nil, errors.New( + "web3signer wallets cannot exit accounts through cli command yet. please perform this on the remote signer node", + ) + } km, err = w.InitializeKeymanager(cliCtx.Context, iface.InitKeymanagerConfig{ListenForChanges: false}) if err != nil { return nil, nil, errors.Wrap(err, ErrCouldNotInitializeKeymanager) diff --git a/validator/accounts/accounts_list.go b/validator/accounts/accounts_list.go index d0288f70e9..60b9f3d6cc 100644 --- a/validator/accounts/accounts_list.go +++ b/validator/accounts/accounts_list.go @@ -18,6 +18,7 @@ import ( "github.com/prysmaticlabs/prysm/validator/keymanager/derived" "github.com/prysmaticlabs/prysm/validator/keymanager/imported" "github.com/prysmaticlabs/prysm/validator/keymanager/remote" + remote_web3signer "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer" "github.com/urfave/cli/v2" ) @@ -29,6 +30,8 @@ func ListAccountsCli(cliCtx *cli.Context) error { if err != nil { return errors.Wrap(err, "could not open wallet") } + // TODO(#9883) - Remove this when we have a better way to handle this. this is fine. + // genesis root is not set here which is used for sign function, but fetch keys should be fine. km, err := w.InitializeKeymanager(cliCtx.Context, iface.InitKeymanagerConfig{ListenForChanges: false}) if err != nil && strings.Contains(err.Error(), keymanager.IncorrectPasswordErrMsg) { return errors.New("wrong wallet password entered") @@ -73,6 +76,15 @@ func ListAccountsCli(cliCtx *cli.Context) error { if err := listRemoteKeymanagerAccounts(cliCtx.Context, w, km, km.KeymanagerOpts()); err != nil { return errors.Wrap(err, "could not list validator accounts with remote keymanager") } + case keymanager.Web3Signer: + km, ok := km.(*remote_web3signer.Keymanager) + if !ok { + return errors.New("could not assert keymanager interface to concrete type") + } + if err := listWeb3SignerKeymanagerAccounts(cliCtx.Context, w, km); err != nil { + return errors.Wrap(err, "could not list validator accounts with web3signer keymanager") + } + default: return fmt.Errorf(errKeymanagerNotSupported, w.KeymanagerKind().String()) } @@ -212,6 +224,11 @@ func listRemoteKeymanagerAccounts( } else { fmt.Printf("Showing %d validator accounts\n", len(validatingPubKeys)) } + displayRemotePublicKeys(validatingPubKeys) + return nil +} + +func displayRemotePublicKeys(validatingPubKeys [][48]byte) { for i := 0; i < len(validatingPubKeys); i++ { fmt.Println("") fmt.Printf( @@ -221,6 +238,36 @@ func listRemoteKeymanagerAccounts( fmt.Printf("%s %#x\n", au.BrightCyan("[validating public key]").Bold(), validatingPubKeys[i]) fmt.Println(" ") } +} + +func listWeb3SignerKeymanagerAccounts( + ctx context.Context, + w *wallet.Wallet, + keymanager *remote_web3signer.Keymanager, +) error { + au := aurora.NewAurora(true) + fmt.Printf("(keymanager kind) %s\n", au.BrightGreen("web3signer").Bold()) + fmt.Printf( + "(configuration file path) %s\n", + au.BrightGreen(filepath.Join(w.AccountsDir(), wallet.KeymanagerConfigFileName)).Bold(), + ) + fmt.Println(" ") + fmt.Printf("%s\n", au.BrightGreen("Setup Configuration").Bold()) + fmt.Println(" ") + //TODO: add config options, may require refactor again + validatingPubKeys, err := keymanager.FetchValidatingPublicKeys(ctx) + if err != nil { + return errors.Wrap(err, "could not fetch validating public keys") + } + if len(validatingPubKeys) == 1 { + fmt.Print("Showing 1 validator account\n") + } else if len(validatingPubKeys) == 0 { + fmt.Print("No accounts found\n") + return nil + } else { + fmt.Printf("Showing %d validator accounts\n", len(validatingPubKeys)) + } + displayRemotePublicKeys(validatingPubKeys) return nil } diff --git a/validator/accounts/iface/BUILD.bazel b/validator/accounts/iface/BUILD.bazel index 8e11a840c1..d3bc921b88 100644 --- a/validator/accounts/iface/BUILD.bazel +++ b/validator/accounts/iface/BUILD.bazel @@ -8,5 +8,8 @@ go_library( "//validator:__pkg__", "//validator:__subpackages__", ], - deps = ["//validator/keymanager:go_default_library"], + deps = [ + "//validator/keymanager:go_default_library", + "//validator/keymanager/remote-web3signer:go_default_library", + ], ) diff --git a/validator/accounts/iface/wallet.go b/validator/accounts/iface/wallet.go index a148aae322..21fb02f937 100644 --- a/validator/accounts/iface/wallet.go +++ b/validator/accounts/iface/wallet.go @@ -4,11 +4,13 @@ import ( "context" "github.com/prysmaticlabs/prysm/validator/keymanager" + remote_web3signer "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer" ) // InitKeymanagerConfig defines configuration options for initializing a keymanager. type InitKeymanagerConfig struct { ListenForChanges bool + Web3SignerConfig *remote_web3signer.SetupConfig } // Wallet defines a struct which has capabilities and knowledge of how diff --git a/validator/accounts/testing/BUILD.bazel b/validator/accounts/testing/BUILD.bazel index 0318b51a9b..972c1f7377 100644 --- a/validator/accounts/testing/BUILD.bazel +++ b/validator/accounts/testing/BUILD.bazel @@ -11,6 +11,8 @@ go_library( ], deps = [ "//validator/accounts/iface:go_default_library", + "//validator/client/iface:go_default_library", "//validator/keymanager:go_default_library", + "@com_github_prysmaticlabs_eth2_types//:go_default_library", ], ) diff --git a/validator/accounts/testing/mock.go b/validator/accounts/testing/mock.go index 82e43ee3e5..4f4a7b8bc6 100644 --- a/validator/accounts/testing/mock.go +++ b/validator/accounts/testing/mock.go @@ -5,8 +5,11 @@ import ( "errors" "strings" "sync" + "time" + types "github.com/prysmaticlabs/eth2-types" "github.com/prysmaticlabs/prysm/validator/accounts/iface" + iface2 "github.com/prysmaticlabs/prysm/validator/client/iface" "github.com/prysmaticlabs/prysm/validator/keymanager" ) @@ -75,3 +78,103 @@ func (w *Wallet) ReadFileAtPath(_ context.Context, pathName, fileName string) ([ func (_ *Wallet) InitializeKeymanager(_ context.Context, _ iface.InitKeymanagerConfig) (keymanager.IKeymanager, error) { return nil, nil } + +type MockValidator struct { + Km keymanager.IKeymanager +} + +func (_ MockValidator) Done() { + panic("implement me") +} + +func (_ MockValidator) WaitForChainStart(_ context.Context) error { + panic("implement me") +} + +func (_ MockValidator) WaitForSync(_ context.Context) error { + panic("implement me") +} + +func (_ MockValidator) WaitForActivation(_ context.Context, _ chan [][48]byte) error { + panic("implement me") +} + +func (_ MockValidator) CanonicalHeadSlot(_ context.Context) (types.Slot, error) { + panic("implement me") +} + +func (_ MockValidator) NextSlot() <-chan types.Slot { + panic("implement me") +} + +func (_ MockValidator) SlotDeadline(_ types.Slot) time.Time { + panic("implement me") +} + +func (_ MockValidator) LogValidatorGainsAndLosses(_ context.Context, _ types.Slot) error { + panic("implement me") +} + +func (_ MockValidator) UpdateDuties(_ context.Context, _ types.Slot) error { + panic("implement me") +} + +func (_ MockValidator) RolesAt(_ context.Context, _ types.Slot) (map[[48]byte][]iface2.ValidatorRole, error) { + panic("implement me") +} + +func (_ MockValidator) SubmitAttestation(_ context.Context, _ types.Slot, _ [48]byte) { + panic("implement me") +} + +func (_ MockValidator) ProposeBlock(_ context.Context, _ types.Slot, _ [48]byte) { + panic("implement me") +} + +func (_ MockValidator) SubmitAggregateAndProof(_ context.Context, _ types.Slot, _ [48]byte) { + panic("implement me") +} + +func (_ MockValidator) SubmitSyncCommitteeMessage(_ context.Context, _ types.Slot, _ [48]byte) { + panic("implement me") +} + +func (_ MockValidator) SubmitSignedContributionAndProof(_ context.Context, _ types.Slot, _ [48]byte) { + panic("implement me") +} + +func (_ MockValidator) LogAttestationsSubmitted() { + panic("implement me") +} + +func (_ MockValidator) LogNextDutyTimeLeft(_ types.Slot) error { + panic("implement me") +} + +func (_ MockValidator) UpdateDomainDataCaches(_ context.Context, _ types.Slot) { + panic("implement me") +} + +func (_ MockValidator) WaitForKeymanagerInitialization(_ context.Context) error { + panic("implement me") +} + +func (_ MockValidator) AllValidatorsAreExited(_ context.Context) (bool, error) { + panic("implement me") +} + +func (m MockValidator) Keymanager() (keymanager.IKeymanager, error) { + return m.Km, nil +} + +func (_ MockValidator) ReceiveBlocks(_ context.Context, _ chan<- error) { + panic("implement me") +} + +func (_ MockValidator) HandleKeyReload(_ context.Context, _ [][48]byte) (bool, error) { + panic("implement me") +} + +func (_ MockValidator) CheckDoppelGanger(_ context.Context) error { + panic("implement me") +} diff --git a/validator/accounts/wallet/BUILD.bazel b/validator/accounts/wallet/BUILD.bazel index a6314cf5a3..6bc4b2c043 100644 --- a/validator/accounts/wallet/BUILD.bazel +++ b/validator/accounts/wallet/BUILD.bazel @@ -13,6 +13,7 @@ go_library( ], deps = [ "//cmd/validator/flags:go_default_library", + "//encoding/bytesutil:go_default_library", "//io/file:go_default_library", "//io/prompt:go_default_library", "//validator/accounts/iface:go_default_library", @@ -21,6 +22,7 @@ go_library( "//validator/keymanager/derived:go_default_library", "//validator/keymanager/imported:go_default_library", "//validator/keymanager/remote:go_default_library", + "//validator/keymanager/remote-web3signer:go_default_library", "@com_github_pkg_errors//:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", "@com_github_urfave_cli_v2//:go_default_library", @@ -33,7 +35,11 @@ go_test( deps = [ ":go_default_library", "//config/params:go_default_library", + "//testing/assert:go_default_library", "//testing/require:go_default_library", + "//validator/accounts/iface:go_default_library", + "//validator/keymanager/remote-web3signer:go_default_library", + "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", ], ) diff --git a/validator/accounts/wallet/wallet.go b/validator/accounts/wallet/wallet.go index 4e34398ea6..7b0ebb7bd4 100644 --- a/validator/accounts/wallet/wallet.go +++ b/validator/accounts/wallet/wallet.go @@ -11,6 +11,7 @@ import ( "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/cmd/validator/flags" + "github.com/prysmaticlabs/prysm/encoding/bytesutil" "github.com/prysmaticlabs/prysm/io/file" "github.com/prysmaticlabs/prysm/io/prompt" "github.com/prysmaticlabs/prysm/validator/accounts/iface" @@ -19,12 +20,13 @@ import ( "github.com/prysmaticlabs/prysm/validator/keymanager/derived" "github.com/prysmaticlabs/prysm/validator/keymanager/imported" "github.com/prysmaticlabs/prysm/validator/keymanager/remote" + remote_web3signer "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer" "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" ) const ( - // KeymanagerConfigFileName for the keymanager used by the wallet: imported, derived, or remote. + // KeymanagerConfigFileName for the keymanager used by the wallet: imported, derived, remote, or web3signer. KeymanagerConfigFileName = "keymanageropts.json" // NewWalletPasswordPromptText for wallet creation. NewWalletPasswordPromptText = "New wallet password" @@ -52,9 +54,10 @@ var ( ) // KeymanagerKindSelections as friendly text. KeymanagerKindSelections = map[keymanager.Kind]string{ - keymanager.Imported: "Imported Wallet (Recommended)", - keymanager.Derived: "HD Wallet", - keymanager.Remote: "Remote Signing Wallet (Advanced)", + keymanager.Imported: "Imported Wallet (Recommended)", + keymanager.Derived: "HD Wallet", + keymanager.Remote: "Remote Signing Wallet (Advanced)", + keymanager.Web3Signer: "Consensys Web3Signer (Advanced)", } // ValidateExistingPass checks that an input cannot be empty. ValidateExistingPass = func(input string) error { @@ -193,6 +196,17 @@ func OpenWalletOrElseCli(cliCtx *cli.Context, otherwise func(cliCtx *cli.Context }) } +// NewWalletForWeb3Signer returns a new wallet for web3 signer which is temporary and not stored locally. +func NewWalletForWeb3Signer() *Wallet { + // wallet is just a temporary wallet for web3 signer used to call intialize keymanager. + return &Wallet{ + walletDir: "", + accountsPath: "", + keymanagerKind: keymanager.Web3Signer, + walletPassword: "", + } +} + // OpenWallet instantiates a wallet from a specified path. It checks the // type of keymanager associated with the wallet by reading files in the wallet // path, if applicable. If a wallet does not exist, returns an appropriate error. @@ -290,6 +304,20 @@ func (w *Wallet) InitializeKeymanager(ctx context.Context, cfg iface.InitKeymana if err != nil { return nil, errors.Wrap(err, "could not initialize remote keymanager") } + case keymanager.Web3Signer: + config := cfg.Web3SignerConfig + if config == nil { + return nil, errors.New("web3signer config is nil") + } + // TODO(9883): future work needs to address how initialize keymanager is called for web3signer. + // an error may be thrown for genesis validators root for some InitializeKeymanager calls. + if !bytesutil.NonZeroRoot(config.GenesisValidatorsRoot) { + return nil, errors.New("web3signer requires a genesis validators root value") + } + km, err = remote_web3signer.NewKeymanager(ctx, config) + if err != nil { + return nil, errors.Wrap(err, "could not initialize web3signer keymanager") + } default: return nil, fmt.Errorf("keymanager kind not supported: %s", w.keymanagerKind) } diff --git a/validator/accounts/wallet/wallet_test.go b/validator/accounts/wallet/wallet_test.go index 425f89286f..40c27ba13b 100644 --- a/validator/accounts/wallet/wallet_test.go +++ b/validator/accounts/wallet/wallet_test.go @@ -1,14 +1,19 @@ package wallet_test import ( + "context" "io/ioutil" "os" "path/filepath" "testing" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/prysmaticlabs/prysm/config/params" + "github.com/prysmaticlabs/prysm/testing/assert" "github.com/prysmaticlabs/prysm/testing/require" + "github.com/prysmaticlabs/prysm/validator/accounts/iface" "github.com/prysmaticlabs/prysm/validator/accounts/wallet" + remote_web3signer "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer" "github.com/sirupsen/logrus" ) @@ -49,3 +54,33 @@ func Test_IsValid_RandomFiles(t *testing.T) { require.NoError(t, err) require.Equal(t, true, valid) } + +func TestWallet_InitializeKeymanager_web3Signer_HappyPath(t *testing.T) { + w := wallet.NewWalletForWeb3Signer() + ctx := context.Background() + root, err := hexutil.Decode("0x270d43e74ce340de4bca2b1936beca0f4f5408d9e78aec4850920baf659d5b69") + require.NoError(t, err) + config := iface.InitKeymanagerConfig{ + ListenForChanges: false, + Web3SignerConfig: &remote_web3signer.SetupConfig{ + BaseEndpoint: "http://localhost:8545", + GenesisValidatorsRoot: root, + PublicKeysURL: "http://localhost:8545/public_keys", + }, + } + km, err := w.InitializeKeymanager(ctx, config) + require.NoError(t, err) + assert.NotNil(t, km) +} + +func TestWallet_InitializeKeymanager_web3Signer_nilConfig(t *testing.T) { + w := wallet.NewWalletForWeb3Signer() + ctx := context.Background() + config := iface.InitKeymanagerConfig{ + ListenForChanges: false, + Web3SignerConfig: nil, + } + km, err := w.InitializeKeymanager(ctx, config) + assert.NotNil(t, err) + assert.Equal(t, nil, km) +} diff --git a/validator/accounts/wallet_create.go b/validator/accounts/wallet_create.go index ae93a2cf45..91f97ec383 100644 --- a/validator/accounts/wallet_create.go +++ b/validator/accounts/wallet_create.go @@ -18,16 +18,18 @@ import ( "github.com/prysmaticlabs/prysm/validator/keymanager/derived" "github.com/prysmaticlabs/prysm/validator/keymanager/imported" "github.com/prysmaticlabs/prysm/validator/keymanager/remote" + remote_web3signer "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer" "github.com/urfave/cli/v2" ) // CreateWalletConfig defines the parameters needed to call the create wallet functions. type CreateWalletConfig struct { - SkipMnemonicConfirm bool - NumAccounts int - RemoteKeymanagerOpts *remote.KeymanagerOpts - WalletCfg *wallet.Config - Mnemonic25thWord string + SkipMnemonicConfirm bool + NumAccounts int + RemoteKeymanagerOpts *remote.KeymanagerOpts + Web3SignerSetupConfig *remote_web3signer.SetupConfig + WalletCfg *wallet.Config + Mnemonic25thWord string } // CreateAndSaveWalletCli from user input with a desired keymanager. If a @@ -73,6 +75,7 @@ func CreateWalletWithKeymanager(ctx context.Context, cfg *CreateWalletConfig) (* if err = createImportedKeymanagerWallet(ctx, w); err != nil { return nil, errors.Wrap(err, "could not initialize wallet") } + // TODO(#9883) - Remove this when we have a better way to handle this. should be safe to use for now. km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false}) if err != nil { return nil, errors.Wrap(err, ErrCouldNotInitializeKeymanager) @@ -116,6 +119,8 @@ func CreateWalletWithKeymanager(ctx context.Context, cfg *CreateWalletConfig) (* log.WithField("--wallet-dir", cfg.WalletCfg.WalletDir).Info( "Successfully created wallet with remote keymanager configuration", ) + case keymanager.Web3Signer: + return nil, errors.New("web3signer keymanager does not require persistent wallets.") default: return nil, errors.Wrapf(err, errKeymanagerNotSupported, w.KeymanagerKind()) } @@ -193,6 +198,9 @@ func extractWalletCreationConfigFromCli(cliCtx *cli.Context, keymanagerKind keym } createWalletConfig.RemoteKeymanagerOpts = opts } + if keymanagerKind == keymanager.Web3Signer { + return nil, errors.New("web3signer keymanager does not require persistent wallets.") + } return createWalletConfig, nil } @@ -260,6 +268,7 @@ func inputKeymanagerKind(cliCtx *cli.Context) (keymanager.Kind, error) { wallet.KeymanagerKindSelections[keymanager.Imported], wallet.KeymanagerKindSelections[keymanager.Derived], wallet.KeymanagerKindSelections[keymanager.Remote], + wallet.KeymanagerKindSelections[keymanager.Web3Signer], }, } selection, _, err := promptSelect.Run() diff --git a/validator/client/BUILD.bazel b/validator/client/BUILD.bazel index 526395bcdf..a1093a35f5 100644 --- a/validator/client/BUILD.bazel +++ b/validator/client/BUILD.bazel @@ -53,6 +53,7 @@ go_library( "//validator/keymanager:go_default_library", "//validator/keymanager/imported:go_default_library", "//validator/keymanager/remote:go_default_library", + "//validator/keymanager/remote-web3signer:go_default_library", "@com_github_dgraph_io_ristretto//:go_default_library", "@com_github_grpc_ecosystem_go_grpc_middleware//:go_default_library", "@com_github_grpc_ecosystem_go_grpc_middleware//retry:go_default_library", @@ -124,14 +125,17 @@ go_test( "//time/slots:go_default_library", "//time/slots/testing:go_default_library", "//validator/accounts/testing:go_default_library", + "//validator/accounts/wallet:go_default_library", "//validator/client/iface:go_default_library", "//validator/client/testutil:go_default_library", "//validator/db/testing:go_default_library", "//validator/graffiti:go_default_library", "//validator/keymanager/derived:go_default_library", + "//validator/keymanager/remote-web3signer:go_default_library", "//validator/keymanager/remote/mock:go_default_library", "//validator/slashing-protection-history:go_default_library", "//validator/testing:go_default_library", + "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", "@com_github_golang_mock//gomock:go_default_library", "@com_github_pkg_errors//:go_default_library", "@com_github_prysmaticlabs_eth2_types//:go_default_library", diff --git a/validator/client/iface/validator.go b/validator/client/iface/validator.go index 3463c54f49..f5d62438eb 100644 --- a/validator/client/iface/validator.go +++ b/validator/client/iface/validator.go @@ -51,9 +51,9 @@ type Validator interface { LogAttestationsSubmitted() LogNextDutyTimeLeft(slot types.Slot) error UpdateDomainDataCaches(ctx context.Context, slot types.Slot) - WaitForWalletInitialization(ctx context.Context) error + WaitForKeymanagerInitialization(ctx context.Context) error AllValidatorsAreExited(ctx context.Context) (bool, error) - GetKeymanager() keymanager.IKeymanager + Keymanager() (keymanager.IKeymanager, error) ReceiveBlocks(ctx context.Context, connectionErrorChannel chan<- error) HandleKeyReload(ctx context.Context, newKeys [][fieldparams.BLSPubkeyLength]byte) (bool, error) CheckDoppelGanger(ctx context.Context) error diff --git a/validator/client/runner.go b/validator/client/runner.go index 04cbfac8dc..ab2a152a7e 100644 --- a/validator/client/runner.go +++ b/validator/client/runner.go @@ -35,11 +35,7 @@ var backOffPeriod = 10 * time.Second func run(ctx context.Context, v iface.Validator) { cleanup := v.Done defer cleanup() - if err := v.WaitForWalletInitialization(ctx); err != nil { - // log.Fatalf will prevent defer from being called - cleanup() - log.Fatalf("Wallet is not ready: %v", err) - } + ticker := time.NewTicker(backOffPeriod) defer ticker.Stop() @@ -63,6 +59,14 @@ func run(ctx context.Context, v iface.Validator) { if err != nil { log.Fatalf("Could not determine if beacon chain started: %v", err) } + + err = v.WaitForKeymanagerInitialization(ctx) + if err != nil { + // log.Fatalf will prevent defer from being called + cleanup() + log.Fatalf("Wallet is not ready: %v", err) + } + err = v.WaitForSync(ctx) if isConnectionError(err) { log.Warnf("Could not determine if beacon chain started: %v", err) @@ -105,7 +109,11 @@ func run(ctx context.Context, v iface.Validator) { } accountsChangedChan := make(chan [][fieldparams.BLSPubkeyLength]byte, 1) - sub := v.GetKeymanager().SubscribeAccountChanges(accountsChangedChan) + km, err := v.Keymanager() + if err != nil { + log.Fatalf("Could not get keymanager: %v", err) + } + sub := km.SubscribeAccountChanges(accountsChangedChan) for { slotCtx, cancel := context.WithCancel(ctx) ctx, span := trace.StartSpan(ctx, "validator.processSlot") @@ -139,7 +147,7 @@ func run(ctx context.Context, v iface.Validator) { case slot := <-v.NextSlot(): span.AddAttributes(trace.Int64Attribute("slot", int64(slot))) - remoteKm, ok := v.GetKeymanager().(remote.RemoteKeymanager) + remoteKm, ok := km.(remote.RemoteKeymanager) if ok { _, err := remoteKm.ReloadPublicKeys(ctx) if err != nil { diff --git a/validator/client/runner_test.go b/validator/client/runner_test.go index e26b6d904d..68e6025399 100644 --- a/validator/client/runner_test.go +++ b/validator/client/runner_test.go @@ -24,13 +24,13 @@ func cancelledContext() context.Context { } func TestCancelledContext_CleansUpValidator(t *testing.T) { - v := &testutil.FakeValidator{Keymanager: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} + v := &testutil.FakeValidator{Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} run(cancelledContext(), v) assert.Equal(t, true, v.DoneCalled, "Expected Done() to be called") } func TestCancelledContext_WaitsForChainStart(t *testing.T) { - v := &testutil.FakeValidator{Keymanager: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} + v := &testutil.FakeValidator{Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} run(cancelledContext(), v) assert.Equal(t, 1, v.WaitForChainStartCalled, "Expected WaitForChainStart() to be called") } @@ -38,7 +38,7 @@ func TestCancelledContext_WaitsForChainStart(t *testing.T) { func TestRetry_On_ConnectionError(t *testing.T) { retry := 10 v := &testutil.FakeValidator{ - Keymanager: &mockKeymanager{accountsChangedFeed: &event.Feed{}}, + Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}, RetryTillSuccess: retry, } backOffPeriod = 10 * time.Millisecond @@ -57,13 +57,13 @@ func TestRetry_On_ConnectionError(t *testing.T) { } func TestCancelledContext_WaitsForActivation(t *testing.T) { - v := &testutil.FakeValidator{Keymanager: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} + v := &testutil.FakeValidator{Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} run(cancelledContext(), v) assert.Equal(t, 1, v.WaitForActivationCalled, "Expected WaitForActivation() to be called") } func TestUpdateDuties_NextSlot(t *testing.T) { - v := &testutil.FakeValidator{Keymanager: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} + v := &testutil.FakeValidator{Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} ctx, cancel := context.WithCancel(context.Background()) slot := types.Slot(55) @@ -83,7 +83,7 @@ func TestUpdateDuties_NextSlot(t *testing.T) { func TestUpdateDuties_HandlesError(t *testing.T) { hook := logTest.NewGlobal() - v := &testutil.FakeValidator{Keymanager: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} + v := &testutil.FakeValidator{Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} ctx, cancel := context.WithCancel(context.Background()) slot := types.Slot(55) @@ -102,7 +102,7 @@ func TestUpdateDuties_HandlesError(t *testing.T) { } func TestRoleAt_NextSlot(t *testing.T) { - v := &testutil.FakeValidator{Keymanager: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} + v := &testutil.FakeValidator{Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} ctx, cancel := context.WithCancel(context.Background()) slot := types.Slot(55) @@ -121,7 +121,7 @@ func TestRoleAt_NextSlot(t *testing.T) { } func TestAttests_NextSlot(t *testing.T) { - v := &testutil.FakeValidator{Keymanager: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} + v := &testutil.FakeValidator{Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} ctx, cancel := context.WithCancel(context.Background()) slot := types.Slot(55) @@ -141,7 +141,7 @@ func TestAttests_NextSlot(t *testing.T) { } func TestProposes_NextSlot(t *testing.T) { - v := &testutil.FakeValidator{Keymanager: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} + v := &testutil.FakeValidator{Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} ctx, cancel := context.WithCancel(context.Background()) slot := types.Slot(55) @@ -161,7 +161,7 @@ func TestProposes_NextSlot(t *testing.T) { } func TestBothProposesAndAttests_NextSlot(t *testing.T) { - v := &testutil.FakeValidator{Keymanager: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} + v := &testutil.FakeValidator{Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} ctx, cancel := context.WithCancel(context.Background()) slot := types.Slot(55) @@ -183,7 +183,7 @@ func TestBothProposesAndAttests_NextSlot(t *testing.T) { } func TestAllValidatorsAreExited_NextSlot(t *testing.T) { - v := &testutil.FakeValidator{Keymanager: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} + v := &testutil.FakeValidator{Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} ctx, cancel := context.WithCancel(context.WithValue(context.Background(), testutil.AllValidatorsAreExitedCtxKey, true)) hook := logTest.NewGlobal() @@ -202,7 +202,7 @@ func TestAllValidatorsAreExited_NextSlot(t *testing.T) { func TestKeyReload_ActiveKey(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) km := &mockKeymanager{} - v := &testutil.FakeValidator{Keymanager: km} + v := &testutil.FakeValidator{Km: km} go func() { km.SimulateAccountChanges([][fieldparams.BLSPubkeyLength]byte{testutil.ActiveKey}) @@ -218,7 +218,7 @@ func TestKeyReload_ActiveKey(t *testing.T) { func TestKeyReload_NoActiveKey(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) km := &mockKeymanager{} - v := &testutil.FakeValidator{Keymanager: km} + v := &testutil.FakeValidator{Km: km} go func() { km.SimulateAccountChanges(make([][fieldparams.BLSPubkeyLength]byte, 0)) @@ -231,8 +231,9 @@ func TestKeyReload_NoActiveKey(t *testing.T) { func TestKeyReload_RemoteKeymanager(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) + km := mock.NewMock() - v := &testutil.FakeValidator{Keymanager: &km} + v := &testutil.FakeValidator{Km: &km} ticker := make(chan types.Slot) v.NextSlotRet = ticker diff --git a/validator/client/service.go b/validator/client/service.go index 0bb41a61b7..c493cda116 100644 --- a/validator/client/service.go +++ b/validator/client/service.go @@ -2,7 +2,6 @@ package client import ( "context" - "fmt" "strings" "time" @@ -18,16 +17,15 @@ import ( lruwrpr "github.com/prysmaticlabs/prysm/cache/lru" fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams" "github.com/prysmaticlabs/prysm/config/params" - "github.com/prysmaticlabs/prysm/encoding/bytesutil" ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block" - accountsiface "github.com/prysmaticlabs/prysm/validator/accounts/iface" "github.com/prysmaticlabs/prysm/validator/accounts/wallet" "github.com/prysmaticlabs/prysm/validator/client/iface" "github.com/prysmaticlabs/prysm/validator/db" "github.com/prysmaticlabs/prysm/validator/graffiti" "github.com/prysmaticlabs/prysm/validator/keymanager" "github.com/prysmaticlabs/prysm/validator/keymanager/imported" + remote_web3signer "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer" "go.opencensus.io/plugin/ocgrpc" "google.golang.org/grpc" "google.golang.org/grpc/credentials" @@ -53,22 +51,24 @@ type ValidatorService struct { emitAccountMetrics bool logValidatorBalances bool logDutyCountDown bool + interopKeysConfig *imported.InteropKeymanagerConfig conn *grpc.ClientConn grpcRetryDelay time.Duration grpcRetries uint maxCallRecvMsgSize int - walletInitializedFeed *event.Feed cancel context.CancelFunc - db db.Database + walletInitializedFeed *event.Feed + wallet *wallet.Wallet + graffitiStruct *graffiti.Graffiti dataDir string withCert string endpoint string - validator iface.Validator ctx context.Context - keyManager keymanager.IKeymanager + validator iface.Validator + db db.Database grpcHeaders []string graffiti []byte - graffitiStruct *graffiti.Graffiti + web3SignerConfig *remote_web3signer.SetupConfig } // Config for the validator service. @@ -77,19 +77,21 @@ type Config struct { LogValidatorBalances bool EmitAccountMetrics bool LogDutyCountDown bool + InteropKeysConfig *imported.InteropKeymanagerConfig + Wallet *wallet.Wallet WalletInitializedFeed *event.Feed GrpcRetriesFlag uint - GrpcRetryDelay time.Duration GrpcMaxCallRecvMsgSizeFlag int - Endpoint string + GrpcRetryDelay time.Duration + GraffitiStruct *graffiti.Graffiti Validator iface.Validator ValDB db.Database - KeyManager keymanager.IKeymanager - GraffitiFlag string CertFlag string DataDir string GrpcHeadersFlag string - GraffitiStruct *graffiti.Graffiti + GraffitiFlag string + Endpoint string + Web3SignerConfig *remote_web3signer.SetupConfig } // NewValidatorService creates a new validator service for the service @@ -103,7 +105,6 @@ func NewValidatorService(ctx context.Context, cfg *Config) (*ValidatorService, e withCert: cfg.CertFlag, dataDir: cfg.DataDir, graffiti: []byte(cfg.GraffitiFlag), - keyManager: cfg.KeyManager, logValidatorBalances: cfg.LogValidatorBalances, emitAccountMetrics: cfg.EmitAccountMetrics, maxCallRecvMsgSize: cfg.GrpcMaxCallRecvMsgSizeFlag, @@ -112,10 +113,13 @@ func NewValidatorService(ctx context.Context, cfg *Config) (*ValidatorService, e grpcHeaders: strings.Split(cfg.GrpcHeadersFlag, ","), validator: cfg.Validator, db: cfg.ValDB, + wallet: cfg.Wallet, walletInitializedFeed: cfg.WalletInitializedFeed, useWeb: cfg.UseWeb, + interopKeysConfig: cfg.InteropKeysConfig, graffitiStruct: cfg.GraffitiStruct, logDutyCountDown: cfg.LogDutyCountDown, + web3SignerConfig: cfg.Web3SignerConfig, }, nil } @@ -177,7 +181,6 @@ func (v *ValidatorService) Start() { beaconClient: ethpb.NewBeaconChainClient(v.conn), slashingProtectionClient: ethpb.NewSlasherClient(v.conn), node: ethpb.NewNodeClient(v.conn), - keyManager: v.keyManager, graffiti: v.graffiti, logValidatorBalances: v.logValidatorBalances, emitAccountMetrics: v.emitAccountMetrics, @@ -188,12 +191,15 @@ func (v *ValidatorService) Start() { aggregatedSlotCommitteeIDCache: aggregatedSlotCommitteeIDCache, voteStats: voteStats{startEpoch: types.Epoch(^uint64(0))}, useWeb: v.useWeb, + interopKeysConfig: v.interopKeysConfig, + wallet: v.wallet, walletInitializedFeed: v.walletInitializedFeed, blockFeed: new(event.Feed), graffitiStruct: v.graffitiStruct, graffitiOrderedIndex: graffitiOrderedIndex, eipImportBlacklistedPublicKeys: slashablePublicKeys, logDutyCountDown: v.logDutyCountDown, + Web3SignerConfig: v.web3SignerConfig, } // To resolve a race condition at startup due to the interface // nature of the abstracted block type. We initialize @@ -207,7 +213,6 @@ func (v *ValidatorService) Start() { v.validator = valStruct go run(v.ctx, v.validator) - go v.recheckKeys(v.ctx) } // Stop the validator service. @@ -228,36 +233,13 @@ func (v *ValidatorService) Status() error { return nil } -func (v *ValidatorService) recheckKeys(ctx context.Context) { - var validatingKeys [][fieldparams.BLSPubkeyLength]byte - var err error - if v.useWeb { - initializedChan := make(chan *wallet.Wallet) - sub := v.walletInitializedFeed.Subscribe(initializedChan) - cleanup := sub.Unsubscribe - defer cleanup() - w := <-initializedChan - keyManager, err := w.InitializeKeymanager(ctx, accountsiface.InitKeymanagerConfig{ListenForChanges: true}) - if err != nil { - // log.Fatalf will prevent defer from being called - cleanup() - log.Fatalf("Could not read keymanager for wallet: %v", err) - } - v.keyManager = keyManager - } - validatingKeys, err = v.keyManager.FetchValidatingPublicKeys(ctx) - if err != nil { - log.WithError(err).Debug("Could not fetch validating keys") - } - if err := v.db.UpdatePublicKeysBuckets(validatingKeys); err != nil { - log.WithError(err).Debug("Could not update public keys buckets") - } - go recheckValidatingKeysBucket(ctx, v.db, v.keyManager) - for _, key := range validatingKeys { - log.WithField( - "publicKey", fmt.Sprintf("%#x", bytesutil.Trunc(key[:])), - ).Info("Validating for public key") - } +// UseInteropKeys returns the useInteropKeys flag. +func (v *ValidatorService) InteropKeysConfig() *imported.InteropKeymanagerConfig { + return v.interopKeysConfig +} + +func (v *ValidatorService) Keymanager() (keymanager.IKeymanager, error) { + return v.validator.Keymanager() } // ConstructDialOptions constructs a list of grpc dial options @@ -330,32 +312,3 @@ func (v *ValidatorService) GenesisInfo(ctx context.Context) (*ethpb.Genesis, err nc := ethpb.NewNodeClient(v.conn) return nc.GetGenesis(ctx, &emptypb.Empty{}) } - -// to accounts changes in the keymanager, then updates those keys' -// buckets in bolt DB if a bucket for a key does not exist. -func recheckValidatingKeysBucket(ctx context.Context, valDB db.Database, km keymanager.IKeymanager) { - importedKeymanager, ok := km.(*imported.Keymanager) - if !ok { - return - } - validatingPubKeysChan := make(chan [][fieldparams.BLSPubkeyLength]byte, 1) - sub := importedKeymanager.SubscribeAccountChanges(validatingPubKeysChan) - defer func() { - sub.Unsubscribe() - close(validatingPubKeysChan) - }() - for { - select { - case keys := <-validatingPubKeysChan: - if err := valDB.UpdatePublicKeysBuckets(keys); err != nil { - log.WithError(err).Debug("Could not update public keys buckets") - continue - } - case <-ctx.Done(): - return - case <-sub.Err(): - log.Error("Subscriber closed, exiting goroutine") - return - } - } -} diff --git a/validator/client/testutil/mock_validator.go b/validator/client/testutil/mock_validator.go index 25a1a33db0..5ee496fb97 100644 --- a/validator/client/testutil/mock_validator.go +++ b/validator/client/testutil/mock_validator.go @@ -49,7 +49,7 @@ type FakeValidator struct { IndexToPubkeyMap map[uint64][fieldparams.BLSPubkeyLength]byte PubkeyToIndexMap map[[fieldparams.BLSPubkeyLength]byte]uint64 PubkeysToStatusesMap map[[fieldparams.BLSPubkeyLength]byte]ethpb.ValidatorStatus - Keymanager keymanager.IKeymanager + Km keymanager.IKeymanager } type ctxKey string @@ -63,7 +63,7 @@ func (fv *FakeValidator) Done() { } // WaitForWalletInitialization for mocking. -func (fv *FakeValidator) WaitForWalletInitialization(_ context.Context) error { +func (fv *FakeValidator) WaitForKeymanagerInitialization(_ context.Context) error { fv.WaitForWalletInitializationCalled = true return nil } @@ -214,9 +214,9 @@ func (_ *FakeValidator) AllValidatorsAreExited(ctx context.Context) (bool, error return ctx.Value(AllValidatorsAreExitedCtxKey).(bool), nil } -// GetKeymanager for mocking -func (fv *FakeValidator) GetKeymanager() keymanager.IKeymanager { - return fv.Keymanager +// Keymanager for mocking +func (fv *FakeValidator) Keymanager() (keymanager.IKeymanager, error) { + return fv.Km, nil } // CheckDoppelGanger for mocking diff --git a/validator/client/validator.go b/validator/client/validator.go index 1be294d517..b73101fafa 100644 --- a/validator/client/validator.go +++ b/validator/client/validator.go @@ -36,6 +36,8 @@ import ( "github.com/prysmaticlabs/prysm/validator/db/kv" "github.com/prysmaticlabs/prysm/validator/graffiti" "github.com/prysmaticlabs/prysm/validator/keymanager" + "github.com/prysmaticlabs/prysm/validator/keymanager/imported" + remote_web3signer "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer" "github.com/sirupsen/logrus" "go.opencensus.io/trace" "google.golang.org/protobuf/proto" @@ -63,28 +65,31 @@ type validator struct { aggregatedSlotCommitteeIDCacheLock sync.Mutex prevBalanceLock sync.RWMutex slashableKeysLock sync.RWMutex + eipImportBlacklistedPublicKeys map[[fieldparams.BLSPubkeyLength]byte]bool walletInitializedFeed *event.Feed - blockFeed *event.Feed - genesisTime uint64 - highestValidSlot types.Slot - domainDataCache *ristretto.Cache - aggregatedSlotCommitteeIDCache *lru.Cache - ticker slots.Ticker - prevBalance map[[fieldparams.BLSPubkeyLength]byte]uint64 - duties *ethpb.DutiesResponse - startBalances map[[fieldparams.BLSPubkeyLength]byte]uint64 attLogs map[[32]byte]*attSubmitted + startBalances map[[fieldparams.BLSPubkeyLength]byte]uint64 + duties *ethpb.DutiesResponse + prevBalance map[[fieldparams.BLSPubkeyLength]byte]uint64 + graffitiOrderedIndex uint64 + aggregatedSlotCommitteeIDCache *lru.Cache + domainDataCache *ristretto.Cache + highestValidSlot types.Slot + genesisTime uint64 + blockFeed *event.Feed + interopKeysConfig *imported.InteropKeymanagerConfig + wallet *wallet.Wallet + graffitiStruct *graffiti.Graffiti node ethpb.NodeClient - keyManager keymanager.IKeymanager - beaconClient ethpb.BeaconChainClient - validatorClient ethpb.BeaconNodeValidatorClient slashingProtectionClient ethpb.SlasherClient db vdb.Database + beaconClient ethpb.BeaconChainClient + keyManager keymanager.IKeymanager + ticker slots.Ticker + validatorClient ethpb.BeaconNodeValidatorClient graffiti []byte voteStats voteStats - graffitiStruct *graffiti.Graffiti - graffitiOrderedIndex uint64 - eipImportBlacklistedPublicKeys map[[fieldparams.BLSPubkeyLength]byte]bool + Web3SignerConfig *remote_web3signer.SetupConfig } type validatorStatus struct { @@ -98,33 +103,110 @@ func (v *validator) Done() { v.ticker.Done() } -// WaitForWalletInitialization checks if the validator needs to wait for -func (v *validator) WaitForWalletInitialization(ctx context.Context) error { - // This function should only run if we are using managing the - // validator client using the Prysm web UI. - if !v.useWeb { - return nil +// WaitForKeymanagerInitialization checks if the validator needs to wait for +func (v *validator) WaitForKeymanagerInitialization(ctx context.Context) error { + genesisRoot, err := v.db.GenesisValidatorsRoot(ctx) + if err != nil { + return errors.Wrap(err, "unable to retrieve valid genesis validators root while initializing key manager") } - if v.keyManager != nil { - return nil + if v.useWeb && v.wallet == nil { + // if wallet is not set, wait for it to be set through the UI + km, err := waitForWebWalletInitialization(ctx, v.walletInitializedFeed) + if err != nil { + return err + } + v.keyManager = km + } else { + if v.interopKeysConfig != nil { + keyManager, err := imported.NewInteropKeymanager(ctx, v.interopKeysConfig.Offset, v.interopKeysConfig.NumValidatorKeys) + if err != nil { + return errors.Wrap(err, "could not generate interop keys for key manager") + } + v.keyManager = keyManager + } else if v.wallet == nil { + return errors.New("wallet not set") + } else { + if v.Web3SignerConfig != nil { + v.Web3SignerConfig.GenesisValidatorsRoot = genesisRoot + } + keyManager, err := v.wallet.InitializeKeymanager(ctx, accountsiface.InitKeymanagerConfig{ListenForChanges: true, Web3SignerConfig: v.Web3SignerConfig}) + if err != nil { + return errors.Wrap(err, "could not initialize key manager") + } + v.keyManager = keyManager + } } + recheckKeys(ctx, v.db, v.keyManager) + return nil +} + +// subscribe to channel for when the wallet is initialized +func waitForWebWalletInitialization(ctx context.Context, walletInitializedEvent *event.Feed) (keymanager.IKeymanager, error) { walletChan := make(chan *wallet.Wallet) - sub := v.walletInitializedFeed.Subscribe(walletChan) + sub := walletInitializedEvent.Subscribe(walletChan) defer sub.Unsubscribe() for { select { case w := <-walletChan: keyManager, err := w.InitializeKeymanager(ctx, accountsiface.InitKeymanagerConfig{ListenForChanges: true}) if err != nil { - return errors.Wrap(err, "could not read keymanager") + return nil, errors.Wrap(err, "could not read keymanager") } - v.keyManager = keyManager - return nil + return keyManager, nil case <-ctx.Done(): - return errors.New("context canceled") + return nil, errors.New("context canceled") case <-sub.Err(): log.Error("Subscriber closed, exiting goroutine") - return nil + return nil, nil + } + } +} + +// recheckKeys checks if the validator has any keys that need to be rechecked. +// the keymanager implements a subscription to push these updates to the validator. +func recheckKeys(ctx context.Context, valDB vdb.Database, keyManager keymanager.IKeymanager) { + var validatingKeys [][fieldparams.BLSPubkeyLength]byte + var err error + validatingKeys, err = keyManager.FetchValidatingPublicKeys(ctx) + if err != nil { + log.WithError(err).Debug("Could not fetch validating keys") + } + if err := valDB.UpdatePublicKeysBuckets(validatingKeys); err != nil { + log.WithError(err).Debug("Could not update public keys buckets") + } + go recheckValidatingKeysBucket(ctx, valDB, keyManager) + for _, key := range validatingKeys { + log.WithField( + "publicKey", fmt.Sprintf("%#x", bytesutil.Trunc(key[:])), + ).Info("Validating for public key") + } +} + +// to accounts changes in the keymanager, then updates those keys' +// buckets in bolt DB if a bucket for a key does not exist. +func recheckValidatingKeysBucket(ctx context.Context, valDB vdb.Database, km keymanager.IKeymanager) { + importedKeymanager, ok := km.(*imported.Keymanager) + if !ok { + return + } + validatingPubKeysChan := make(chan [][fieldparams.BLSPubkeyLength]byte, 1) + sub := importedKeymanager.SubscribeAccountChanges(validatingPubKeysChan) + defer func() { + sub.Unsubscribe() + close(validatingPubKeysChan) + }() + for { + select { + case keys := <-validatingPubKeysChan: + if err := valDB.UpdatePublicKeysBuckets(keys); err != nil { + log.WithError(err).Debug("Could not update public keys buckets") + continue + } + case <-ctx.Done(): + return + case <-sub.Err(): + log.Error("Subscriber closed, exiting goroutine") + return } } } @@ -647,9 +729,12 @@ func (v *validator) RolesAt(ctx context.Context, slot types.Slot) (map[[fieldpar return rolesAt, nil } -// GetKeymanager returns the underlying validator's keymanager. -func (v *validator) GetKeymanager() keymanager.IKeymanager { - return v.keyManager +// Keymanager returns the underlying validator's keymanager. +func (v *validator) Keymanager() (keymanager.IKeymanager, error) { + if v.keyManager == nil { + return nil, errors.New("keymanager is not initialized") + } + return v.keyManager, nil } // isAggregator checks if a validator is an aggregator of a given slot and committee, diff --git a/validator/client/validator_test.go b/validator/client/validator_test.go index ad2990eb4e..734a22bafa 100644 --- a/validator/client/validator_test.go +++ b/validator/client/validator_test.go @@ -10,6 +10,7 @@ import ( "testing" "time" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/golang/mock/gomock" types "github.com/prysmaticlabs/eth2-types" "github.com/prysmaticlabs/prysm/async/event" @@ -24,8 +25,10 @@ import ( mock2 "github.com/prysmaticlabs/prysm/testing/mock" "github.com/prysmaticlabs/prysm/testing/require" "github.com/prysmaticlabs/prysm/testing/util" + "github.com/prysmaticlabs/prysm/validator/accounts/wallet" "github.com/prysmaticlabs/prysm/validator/client/iface" dbTest "github.com/prysmaticlabs/prysm/validator/db/testing" + remote_web3signer "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer" "github.com/sirupsen/logrus" logTest "github.com/sirupsen/logrus/hooks/test" "google.golang.org/grpc" @@ -1327,3 +1330,32 @@ func TestIsSyncCommitteeAggregator_OK(t *testing.T) { require.NoError(t, err) require.Equal(t, true, aggregator) } + +func TestValidator_WaitForKeymanagerInitialization_web3Signer(t *testing.T) { + ctx := context.Background() + db := dbTest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{}) + root := make([]byte, 32) + copy(root[2:], "a") + err := db.SaveGenesisValidatorsRoot(ctx, root) + require.NoError(t, err) + w := wallet.NewWalletForWeb3Signer() + decodedKey, err := hexutil.Decode("0xa2b5aaad9c6efefe7bb9b1243a043404f3362937cfb6b31833929833173f476630ea2cfeb0d9ddf15f97ca8685948820") + require.NoError(t, err) + keys := [][48]byte{ + bytesutil.ToBytes48(decodedKey), + } + v := validator{ + db: db, + useWeb: false, + wallet: w, + Web3SignerConfig: &remote_web3signer.SetupConfig{ + BaseEndpoint: "http://localhost:8545", + ProvidedPublicKeys: keys, + }, + } + err = v.WaitForKeymanagerInitialization(context.Background()) + require.NoError(t, err) + km, err := v.Keymanager() + require.NoError(t, err) + require.NotNil(t, km) +} diff --git a/validator/client/wait_for_activation.go b/validator/client/wait_for_activation.go index 961ff4925f..3a78136670 100644 --- a/validator/client/wait_for_activation.go +++ b/validator/client/wait_for_activation.go @@ -27,7 +27,11 @@ func (v *validator) WaitForActivation(ctx context.Context, accountsChangedChan c // Monitor the key manager for updates. if accountsChangedChan == nil { accountsChangedChan = make(chan [][fieldparams.BLSPubkeyLength]byte, 1) - sub := v.GetKeymanager().SubscribeAccountChanges(accountsChangedChan) + km, err := v.Keymanager() + if err != nil { + return err + } + sub := km.SubscribeAccountChanges(accountsChangedChan) defer func() { sub.Unsubscribe() close(accountsChangedChan) diff --git a/validator/keymanager/imported/keymanager.go b/validator/keymanager/imported/keymanager.go index 6f206f5d54..cbc1df3549 100644 --- a/validator/keymanager/imported/keymanager.go +++ b/validator/keymanager/imported/keymanager.go @@ -95,7 +95,15 @@ func NewKeymanager(ctx context.Context, cfg *SetupConfig) (*Keymanager, error) { return k, nil } +// InteropKeymanagerConfig is used on validator launch to initialize the keymanager. +// InteropKeys are used for testing purposes. +type InteropKeymanagerConfig struct { + Offset uint64 + NumValidatorKeys uint64 +} + // NewInteropKeymanager instantiates a new imported keymanager with the deterministically generated interop keys. +// InteropKeys are used for testing purposes. func NewInteropKeymanager(_ context.Context, offset, numValidatorKeys uint64) (*Keymanager, error) { k := &Keymanager{ accountsChangedFeed: new(event.Feed), diff --git a/validator/keymanager/remote-web3signer/BUILD.bazel b/validator/keymanager/remote-web3signer/BUILD.bazel index 780b4c714b..049f1fd9b4 100644 --- a/validator/keymanager/remote-web3signer/BUILD.bazel +++ b/validator/keymanager/remote-web3signer/BUILD.bazel @@ -2,11 +2,7 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", - srcs = [ - "client.go", - "keymanager.go", - "log.go", - ], + srcs = ["keymanager.go"], importpath = "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer", visibility = [ "//cmd/validator:__subpackages__", @@ -18,26 +14,25 @@ go_library( "//crypto/bls:go_default_library", "//encoding/bytesutil:go_default_library", "//proto/prysm/v1alpha1/validator-client:go_default_library", + "//validator/keymanager/remote-web3signer/internal:go_default_library", "//validator/keymanager/remote-web3signer/v1:go_default_library", "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", + "@com_github_go_playground_validator_v10//:go_default_library", "@com_github_pkg_errors//:go_default_library", - "@com_github_sirupsen_logrus//:go_default_library", ], ) go_test( name = "go_default_test", - srcs = [ - "client_test.go", - "keymanager_test.go", - ], + srcs = ["keymanager_test.go"], embed = [":go_default_library"], deps = [ "//crypto/bls:go_default_library", "//encoding/bytesutil:go_default_library", "//proto/prysm/v1alpha1/validator-client:go_default_library", "//testing/require:go_default_library", - "//validator/keymanager/remote-web3signer/v1:go_default_library", + "//validator/keymanager/remote-web3signer/internal:go_default_library", + "//validator/keymanager/remote-web3signer/v1/mock: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/client.go b/validator/keymanager/remote-web3signer/client.go deleted file mode 100644 index ddfb4560d8..0000000000 --- a/validator/keymanager/remote-web3signer/client.go +++ /dev/null @@ -1,159 +0,0 @@ -package remote_web3signer - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "net/http" - "net/url" - "time" - - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/pkg/errors" - fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams" - "github.com/prysmaticlabs/prysm/crypto/bls" - "github.com/prysmaticlabs/prysm/encoding/bytesutil" - v1 "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer/v1" -) - -const ( - ethApiNamespace = "/api/v1/eth2/sign/" - maxTimeout = 3 * time.Second -) - -type SignRequestJson []byte - -// httpSignerClient defines the interface for interacting with a remote web3signer. -type httpSignerClient interface { - Sign(ctx context.Context, pubKey string, request SignRequestJson) (bls.Signature, error) - GetPublicKeys(ctx context.Context, url string) ([][48]byte, error) -} - -// apiClient a wrapper object around web3signer APIs. API docs found here https://consensys.github.io/web3signer/web3signer-eth2.html. -type apiClient struct { - BasePath string - restClient *http.Client -} - -// newApiClient method instantiates a new apiClient object. -func newApiClient(baseEndpoint string) (*apiClient, error) { - u, err := url.Parse(baseEndpoint) - if err != nil { - return nil, errors.Wrap(err, "invalid format, unable to parse url") - } - return &apiClient{ - BasePath: u.Host, - restClient: &http.Client{ - Timeout: maxTimeout, - }, - }, nil -} - -// Sign is a wrapper method around the web3signer sign api. -func (client *apiClient) Sign(_ context.Context, pubKey string, request SignRequestJson) (bls.Signature, error) { - requestPath := ethApiNamespace + pubKey - resp, err := client.doRequest(http.MethodPost, client.BasePath+requestPath, bytes.NewBuffer(request)) - if err != nil { - return nil, err - } - if resp.StatusCode == http.StatusNotFound { - return nil, errors.Wrap(err, "public key not found") - } - if resp.StatusCode == http.StatusPreconditionFailed { - return nil, errors.Wrap(err, "signing operation failed due to slashing protection rules") - } - signResp := &v1.SignResponse{} - if err := client.unmarshalResponse(resp.Body, &signResp); err != nil { - return nil, err - } - decoded, err := hexutil.Decode(signResp.Signature) - if err != nil { - return nil, errors.Wrap(err, "failed to decode signature") - } - return bls.SignatureFromBytes(decoded) -} - -// GetPublicKeys is a wrapper method around the web3signer publickeys api (this may be removed in the future or moved to another location due to its usage). -func (client *apiClient) GetPublicKeys(_ context.Context, url string) ([][fieldparams.BLSPubkeyLength]byte, error) { - resp, err := client.doRequest(http.MethodGet, url, nil /* no body needed on get request */) - if err != nil { - return nil, err - } - var publicKeys []string - if err := client.unmarshalResponse(resp.Body, &publicKeys); err != nil { - return nil, err - } - decodedKeys := make([][fieldparams.BLSPubkeyLength]byte, len(publicKeys)) - var errorKeyPositions string - for i, value := range publicKeys { - decodedKey, err := hexutil.Decode(value) - if err != nil { - errorKeyPositions += fmt.Sprintf("%v, ", i) - continue - } - decodedKeys[i] = bytesutil.ToBytes48(decodedKey) - } - if errorKeyPositions != "" { - return nil, errors.New("failed to decode from Hex from the following public key index locations: " + errorKeyPositions) - } - return decodedKeys, nil -} - -// ReloadSignerKeys is a wrapper method around the web3signer reload api. -func (client *apiClient) ReloadSignerKeys(_ context.Context) error { - const requestPath = "/reload" - if _, err := client.doRequest(http.MethodPost, client.BasePath+requestPath, nil); err != nil { - return err - } - return nil -} - -// GetServerStatus is a wrapper method around the web3signer upcheck api -func (client *apiClient) GetServerStatus(_ context.Context) (string, error) { - const requestPath = "/upcheck" - resp, err := client.doRequest(http.MethodGet, client.BasePath+requestPath, nil /* no body needed on get request */) - if err != nil { - return "", err - } - var status string - if err := client.unmarshalResponse(resp.Body, &status); err != nil { - return "", err - } - return status, nil -} - -// doRequest is a utility method for requests. -func (client *apiClient) doRequest(httpMethod, fullPath string, body io.Reader) (*http.Response, error) { - req, err := http.NewRequest(httpMethod, fullPath, body) - if err != nil { - return nil, errors.Wrap(err, "invalid format, failed to create new Post Request Object") - } - resp, err := client.restClient.Do(req) - if err != nil { - return resp, errors.Wrap(err, "failed to execute json request") - } - if resp.StatusCode == http.StatusInternalServerError { - return nil, errors.Wrap(err, "internal Web3Signer server error") - } else if resp.StatusCode == http.StatusBadRequest { - return nil, errors.Wrap(err, "bad request format") - } - return resp, err -} - -// unmarshalResponse is a utility method for unmarshalling responses. -func (*apiClient) unmarshalResponse(responseBody io.ReadCloser, unmarshalledResponseObject interface{}) error { - defer closeBody(responseBody) - if err := json.NewDecoder(responseBody).Decode(&unmarshalledResponseObject); err != nil { - return errors.Wrap(err, "invalid format, unable to read response body as array of strings") - } - return nil -} - -// closeBody a utility method to wrap an error for closing -func closeBody(body io.Closer) { - if err := body.Close(); err != nil { - log.Errorf("could not close response body: %v", err) - } -} diff --git a/validator/keymanager/remote-web3signer/client_test.go b/validator/keymanager/remote-web3signer/client_test.go deleted file mode 100644 index 5f8ac26c72..0000000000 --- a/validator/keymanager/remote-web3signer/client_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package remote_web3signer - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "testing" - - v1 "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer/v1" - "github.com/stretchr/testify/assert" -) - -// mockTransport is the mock Transport object -type mockTransport struct { - mockResponse *http.Response -} - -// RoundTrip is mocking my own implementation of the RoundTripper interface -func (m *mockTransport) RoundTrip(*http.Request) (*http.Response, error) { - return m.mockResponse, nil -} - -func TestClient_Sign_HappyPath(t *testing.T) { - jsonSig := `{ - "signature": "0xb3baa751d0a9132cfe93e4e3d5ff9075111100e3789dca219ade5a24d27e19d16b3353149da1833e9b691bb38634e8dc04469be7032132906c927d7e1a49b414730612877bc6b2810c8f202daf793d1ab0d6b5cb21d52f9e52e883859887a5d9" - }` - // create a new reader with that JSON - r := ioutil.NopCloser(bytes.NewReader([]byte(jsonSig))) - mock := &mockTransport{mockResponse: &http.Response{ - StatusCode: 200, - Body: r, - }} - cl := apiClient{BasePath: "example.com", restClient: &http.Client{Transport: mock}} - request := v1.MockAggregationSlotSignRequest() // could be any request - jsonRequest, err := json.Marshal(request) - if err != nil { - fmt.Printf("error: %v", err) - } - resp, err := cl.Sign(context.Background(), "a2b5aaad9c6efefe7bb9b1243a043404f3362937cfb6b31833929833173f476630ea2cfeb0d9ddf15f97ca8685948820", jsonRequest) - assert.NotNil(t, resp) - assert.Nil(t, err) - assert.EqualValues(t, "0xb3baa751d0a9132cfe93e4e3d5ff9075111100e3789dca219ade5a24d27e19d16b3353149da1833e9b691bb38634e8dc04469be7032132906c927d7e1a49b414730612877bc6b2810c8f202daf793d1ab0d6b5cb21d52f9e52e883859887a5d9", fmt.Sprintf("%#x", resp.Marshal())) -} - -func TestClient_GetPublicKeys_HappyPath(t *testing.T) { - // public keys are returned hex encoded with 0x - json := `["0xa2b5aaad9c6efefe7bb9b1243a043404f3362937cfb6b31833929833173f476630ea2cfeb0d9ddf15f97ca8685948820"]` - // create a new reader with that JSON - r := ioutil.NopCloser(bytes.NewReader([]byte(json))) - mock := &mockTransport{mockResponse: &http.Response{ - StatusCode: 200, - Body: r, - }} - cl := apiClient{BasePath: "example.com", restClient: &http.Client{Transport: mock}} - resp, err := cl.GetPublicKeys(context.Background(), "example.com/api/publickeys") - assert.NotNil(t, resp) - assert.Nil(t, err) - // we would like them as 48byte base64 without 0x - assert.EqualValues(t, "[162 181 170 173 156 110 254 254 123 185 177 36 58 4 52 4 243 54 41 55 207 182 179 24 51 146 152 51 23 63 71 102 48 234 44 254 176 217 221 241 95 151 202 134 133 148 136 32]", fmt.Sprintf("%v", resp[0][:])) -} - -// TODO: not really in use, should be revisited -func TestClient_ReloadSignerKeys_HappyPath(t *testing.T) { - mock := &mockTransport{mockResponse: &http.Response{ - StatusCode: 200, - Body: ioutil.NopCloser(bytes.NewReader(nil)), - }} - cl := apiClient{BasePath: "example.com", restClient: &http.Client{Transport: mock}} - err := cl.ReloadSignerKeys(context.Background()) - assert.Nil(t, err) -} - -// TODO: not really in use, should be revisited -func TestClient_GetServerStatus_HappyPath(t *testing.T) { - json := `"some server status, not sure what it looks like, need to find some sample data"` - r := ioutil.NopCloser(bytes.NewReader([]byte(json))) - mock := &mockTransport{mockResponse: &http.Response{ - StatusCode: 200, - Body: r, - }} - cl := apiClient{BasePath: "example.com", restClient: &http.Client{Transport: mock}} - resp, err := cl.GetServerStatus(context.Background()) - assert.NotNil(t, resp) - assert.Nil(t, err) -} diff --git a/validator/keymanager/remote-web3signer/internal/BUILD.bazel b/validator/keymanager/remote-web3signer/internal/BUILD.bazel new file mode 100644 index 0000000000..13acaead16 --- /dev/null +++ b/validator/keymanager/remote-web3signer/internal/BUILD.bazel @@ -0,0 +1,28 @@ +load("@prysm//tools/go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "client.go", + "log.go", + ], + importpath = "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer/internal", + visibility = ["//validator/keymanager/remote-web3signer:__subpackages__"], + deps = [ + "//config/fieldparams:go_default_library", + "//crypto/bls:go_default_library", + "//encoding/bytesutil: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", + ], +) + +go_test( + name = "go_default_test", + srcs = ["client_test.go"], + deps = [ + ":go_default_library", + "@com_github_stretchr_testify//assert:go_default_library", + ], +) diff --git a/validator/keymanager/remote-web3signer/internal/client.go b/validator/keymanager/remote-web3signer/internal/client.go new file mode 100644 index 0000000000..8e0eae967f --- /dev/null +++ b/validator/keymanager/remote-web3signer/internal/client.go @@ -0,0 +1,175 @@ +package internal + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/pkg/errors" + fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams" + "github.com/prysmaticlabs/prysm/crypto/bls" + "github.com/prysmaticlabs/prysm/encoding/bytesutil" +) + +const ( + ethApiNamespace = "/api/v1/eth2/sign/" +) + +type SignRequestJson []byte + +// HttpSignerClient defines the interface for interacting with a remote web3signer. +type HttpSignerClient interface { + Sign(ctx context.Context, pubKey string, request SignRequestJson) (bls.Signature, error) + GetPublicKeys(ctx context.Context, url string) ([][48]byte, error) +} + +// ApiClient a wrapper object around web3signer APIs. Please refer to the docs from Consensys' web3signer project. +type ApiClient struct { + BaseURL *url.URL + RestClient *http.Client +} + +// NewApiClient method instantiates a new ApiClient object. +func NewApiClient(baseEndpoint string) (*ApiClient, error) { + u, err := url.Parse(baseEndpoint) + if err != nil { + return nil, errors.Wrap(err, "invalid format, unable to parse url") + } + return &ApiClient{ + BaseURL: u, + RestClient: &http.Client{}, + }, nil +} + +// Sign is a wrapper method around the web3signer sign api. +func (client *ApiClient) Sign(ctx context.Context, pubKey string, request SignRequestJson) (bls.Signature, error) { + requestPath := ethApiNamespace + pubKey + resp, err := client.doRequest(ctx, http.MethodPost, client.BaseURL.String()+requestPath, bytes.NewBuffer(request)) + if err != nil { + return nil, err + } + if resp.StatusCode == http.StatusNotFound { + return nil, fmt.Errorf("public key not found") + } + if resp.StatusCode == http.StatusPreconditionFailed { + return nil, fmt.Errorf("signing operation failed due to slashing protection rules, Signing Request URL: %v, Signing Request Body: %v, Full Response: %v", client.BaseURL.String()+requestPath, string(request), resp) + } + + return unmarshalSignatureResponse(resp.Body) + +} + +// GetPublicKeys is a wrapper method around the web3signer publickeys api (this may be removed in the future or moved to another location due to its usage). +func (client *ApiClient) GetPublicKeys(ctx context.Context, url string) ([][fieldparams.BLSPubkeyLength]byte, error) { + resp, err := client.doRequest(ctx, http.MethodGet, url, nil /* no body needed on get request */) + if err != nil { + return nil, err + } + var publicKeys []string + if err := unmarshalResponse(resp.Body, &publicKeys); err != nil { + return nil, err + } + decodedKeys := make([][fieldparams.BLSPubkeyLength]byte, len(publicKeys)) + var errorKeyPositions string + for i, value := range publicKeys { + decodedKey, err := hexutil.Decode(value) + if err != nil { + errorKeyPositions += fmt.Sprintf("%v, ", i) + continue + } + decodedKeys[i] = bytesutil.ToBytes48(decodedKey) + } + if errorKeyPositions != "" { + return nil, errors.New("failed to decode from Hex from the following public key index locations: " + errorKeyPositions) + } + return decodedKeys, nil +} + +// ReloadSignerKeys is a wrapper method around the web3signer reload api. +func (client *ApiClient) ReloadSignerKeys(ctx context.Context) error { + const requestPath = "/reload" + if _, err := client.doRequest(ctx, http.MethodPost, client.BaseURL.String()+requestPath, nil); err != nil { + return err + } + return nil +} + +// GetServerStatus is a wrapper method around the web3signer upcheck api +func (client *ApiClient) GetServerStatus(ctx context.Context) (string, error) { + const requestPath = "/upcheck" + resp, err := client.doRequest(ctx, http.MethodGet, client.BaseURL.String()+requestPath, nil /* no body needed on get request */) + if err != nil { + return "", err + } + var status string + if err := unmarshalResponse(resp.Body, &status); err != nil { + return "", err + } + return status, nil +} + +// doRequest is a utility method for requests. +func (client *ApiClient) doRequest(ctx context.Context, httpMethod, fullPath string, body io.Reader) (*http.Response, error) { + req, err := http.NewRequestWithContext(ctx, httpMethod, fullPath, body) + if err != nil { + return nil, errors.Wrap(err, "invalid format, failed to create new Post Request Object") + } + req.Header.Set("Content-Type", "application/json") + resp, err := client.RestClient.Do(req) + if err != nil { + return resp, errors.Wrap(err, "failed to execute json request") + } + if resp.StatusCode == http.StatusInternalServerError { + b, err := io.ReadAll(body) + if err != nil { + return nil, errors.Wrap(err, "failed to read body") + } + return nil, fmt.Errorf("internal Web3Signer server error, Signing Request URL: %v, Signing Request Body: %s, Full Response: %v", fullPath, string(b), resp) + } else if resp.StatusCode == http.StatusBadRequest { + b, err := io.ReadAll(body) + if err != nil { + return nil, errors.Wrap(err, "failed to read body") + } + return nil, fmt.Errorf("bad request format, Signing Request URL: %v, Signing Request Body: %v, Full Response: %v", fullPath, string(b), resp) + } + return resp, err +} + +// unmarshalResponse is a utility method for unmarshalling responses. +func unmarshalResponse(responseBody io.ReadCloser, unmarshalledResponseObject interface{}) error { + defer closeBody(responseBody) + if err := json.NewDecoder(responseBody).Decode(&unmarshalledResponseObject); err != nil { + body, err := ioutil.ReadAll(responseBody) + if err != nil { + return errors.Wrap(err, "failed to read response body") + } + return errors.Wrap(err, fmt.Sprintf("invalid format, unable to read response body: %v", string(body))) + } + return nil +} + +func unmarshalSignatureResponse(responseBody io.ReadCloser) (bls.Signature, error) { + defer closeBody(responseBody) + body, err := ioutil.ReadAll(responseBody) + if err != nil { + return nil, err + } + sigBytes, err := hexutil.Decode(string(body)) + if err != nil { + return nil, err + } + return bls.SignatureFromBytes(sigBytes) +} + +// closeBody a utility method to wrap an error for closing +func closeBody(body io.Closer) { + if err := body.Close(); err != nil { + log.Errorf("could not close response body: %v", err) + } +} diff --git a/validator/keymanager/remote-web3signer/internal/client_test.go b/validator/keymanager/remote-web3signer/internal/client_test.go new file mode 100644 index 0000000000..1b0a33d5c1 --- /dev/null +++ b/validator/keymanager/remote-web3signer/internal/client_test.go @@ -0,0 +1,155 @@ +package internal_test + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "testing" + + "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer/internal" + "github.com/stretchr/testify/assert" +) + +// mockTransport is the mock Transport object +type mockTransport struct { + mockResponse *http.Response +} + +// RoundTrip is mocking my own implementation of the RoundTripper interface +func (m *mockTransport) RoundTrip(*http.Request) (*http.Response, error) { + return m.mockResponse, nil +} + +func TestNewApiClient(t *testing.T) { + apiClient, err := internal.NewApiClient("http://localhost:8545") + assert.NoError(t, err) + assert.NotNil(t, apiClient) +} + +func TestClient_Sign_HappyPath(t *testing.T) { + jsonSig := `0xb3baa751d0a9132cfe93e4e3d5ff9075111100e3789dca219ade5a24d27e19d16b3353149da1833e9b691bb38634e8dc04469be7032132906c927d7e1a49b414730612877bc6b2810c8f202daf793d1ab0d6b5cb21d52f9e52e883859887a5d9` + // create a new reader with that JSON + r := ioutil.NopCloser(bytes.NewReader([]byte(jsonSig))) + mock := &mockTransport{mockResponse: &http.Response{ + StatusCode: 200, + Body: r, + }} + u, err := url.Parse("example.com") + assert.NoError(t, err) + cl := internal.ApiClient{BaseURL: u, RestClient: &http.Client{Transport: mock}} + jsonRequest, err := json.Marshal(`{message: "hello"}`) + assert.NoError(t, err) + resp, err := cl.Sign(context.Background(), "a2b5aaad9c6efefe7bb9b1243a043404f3362937cfb6b31833929833173f476630ea2cfeb0d9ddf15f97ca8685948820", jsonRequest) + assert.NotNil(t, resp) + assert.Nil(t, err) + assert.EqualValues(t, "0xb3baa751d0a9132cfe93e4e3d5ff9075111100e3789dca219ade5a24d27e19d16b3353149da1833e9b691bb38634e8dc04469be7032132906c927d7e1a49b414730612877bc6b2810c8f202daf793d1ab0d6b5cb21d52f9e52e883859887a5d9", fmt.Sprintf("%#x", resp.Marshal())) +} + +func TestClient_Sign_500(t *testing.T) { + jsonSig := `0xb3baa751d0a9132cfe93e4e3d5ff9075111100e3789dca219ade5a24d27e19d16b3353149da1833e9b691bb38634e8dc04469be7032132906c927d7e1a49b414730612877bc6b2810c8f202daf793d1ab0d6b5cb21d52f9e52e883859887a5d9` + // create a new reader with that JSON + r := ioutil.NopCloser(bytes.NewReader([]byte(jsonSig))) + mock := &mockTransport{mockResponse: &http.Response{ + StatusCode: 500, + Body: r, + }} + u, err := url.Parse("example.com") + assert.NoError(t, err) + cl := internal.ApiClient{BaseURL: u, RestClient: &http.Client{Transport: mock}} + jsonRequest, err := json.Marshal(`{message: "hello"}`) + assert.NoError(t, err) + resp, err := cl.Sign(context.Background(), "a2b5aaad9c6efefe7bb9b1243a043404f3362937cfb6b31833929833173f476630ea2cfeb0d9ddf15f97ca8685948820", jsonRequest) + assert.NotNil(t, err) + assert.Nil(t, resp) + +} + +func TestClient_Sign_412(t *testing.T) { + jsonSig := `0xb3baa751d0a9132cfe93e4e3d5ff9075111100e3789dca219ade5a24d27e19d16b3353149da1833e9b691bb38634e8dc04469be7032132906c927d7e1a49b414730612877bc6b2810c8f202daf793d1ab0d6b5cb21d52f9e52e883859887a5d9` + // create a new reader with that JSON + r := ioutil.NopCloser(bytes.NewReader([]byte(jsonSig))) + mock := &mockTransport{mockResponse: &http.Response{ + StatusCode: 412, + Body: r, + }} + u, err := url.Parse("example.com") + assert.NoError(t, err) + cl := internal.ApiClient{BaseURL: u, RestClient: &http.Client{Transport: mock}} + jsonRequest, err := json.Marshal(`{message: "hello"}`) + assert.NoError(t, err) + resp, err := cl.Sign(context.Background(), "a2b5aaad9c6efefe7bb9b1243a043404f3362937cfb6b31833929833173f476630ea2cfeb0d9ddf15f97ca8685948820", jsonRequest) + assert.NotNil(t, err) + assert.Nil(t, resp) + +} + +func TestClient_Sign_400(t *testing.T) { + jsonSig := `0xb3baa751d0a9132cfe93e4e3d5ff9075111100e3789dca219ade5a24d27e19d16b3353149da1833e9b691bb38634e8dc04469be7032132906c927d7e1a49b414730612877bc6b2810c8f202daf793d1ab0d6b5cb21d52f9e52e883859887a5d9` + // create a new reader with that JSON + r := ioutil.NopCloser(bytes.NewReader([]byte(jsonSig))) + mock := &mockTransport{mockResponse: &http.Response{ + StatusCode: 400, + Body: r, + }} + u, err := url.Parse("example.com") + assert.NoError(t, err) + cl := internal.ApiClient{BaseURL: u, RestClient: &http.Client{Transport: mock}} + jsonRequest, err := json.Marshal(`{message: "hello"}`) + assert.NoError(t, err) + resp, err := cl.Sign(context.Background(), "a2b5aaad9c6efefe7bb9b1243a043404f3362937cfb6b31833929833173f476630ea2cfeb0d9ddf15f97ca8685948820", jsonRequest) + assert.NotNil(t, err) + assert.Nil(t, resp) + +} + +func TestClient_GetPublicKeys_HappyPath(t *testing.T) { + // public keys are returned hex encoded with 0x + json := `["0xa2b5aaad9c6efefe7bb9b1243a043404f3362937cfb6b31833929833173f476630ea2cfeb0d9ddf15f97ca8685948820"]` + // create a new reader with that JSON + r := ioutil.NopCloser(bytes.NewReader([]byte(json))) + mock := &mockTransport{mockResponse: &http.Response{ + StatusCode: 200, + Body: r, + }} + u, err := url.Parse("example.com") + assert.NoError(t, err) + cl := internal.ApiClient{BaseURL: u, RestClient: &http.Client{Transport: mock}} + resp, err := cl.GetPublicKeys(context.Background(), "example.com/api/publickeys") + assert.NotNil(t, resp) + assert.Nil(t, err) + // we would like them as 48byte base64 without 0x + assert.EqualValues(t, "[162 181 170 173 156 110 254 254 123 185 177 36 58 4 52 4 243 54 41 55 207 182 179 24 51 146 152 51 23 63 71 102 48 234 44 254 176 217 221 241 95 151 202 134 133 148 136 32]", fmt.Sprintf("%v", resp[0][:])) +} + +// TODO: not really in use, should be revisited +func TestClient_ReloadSignerKeys_HappyPath(t *testing.T) { + mock := &mockTransport{mockResponse: &http.Response{ + StatusCode: 200, + Body: ioutil.NopCloser(bytes.NewReader(nil)), + }} + u, err := url.Parse("example.com") + assert.NoError(t, err) + cl := internal.ApiClient{BaseURL: u, RestClient: &http.Client{Transport: mock}} + err = cl.ReloadSignerKeys(context.Background()) + assert.Nil(t, err) +} + +// TODO: not really in use, should be revisited +func TestClient_GetServerStatus_HappyPath(t *testing.T) { + json := `"some server status, not sure what it looks like, need to find some sample data"` + r := ioutil.NopCloser(bytes.NewReader([]byte(json))) + mock := &mockTransport{mockResponse: &http.Response{ + StatusCode: 200, + Body: r, + }} + u, err := url.Parse("example.com") + assert.NoError(t, err) + cl := internal.ApiClient{BaseURL: u, RestClient: &http.Client{Transport: mock}} + resp, err := cl.GetServerStatus(context.Background()) + assert.NotNil(t, resp) + assert.Nil(t, err) +} diff --git a/validator/keymanager/remote-web3signer/internal/log.go b/validator/keymanager/remote-web3signer/internal/log.go new file mode 100644 index 0000000000..13c1025c8d --- /dev/null +++ b/validator/keymanager/remote-web3signer/internal/log.go @@ -0,0 +1,7 @@ +package internal + +import ( + "github.com/sirupsen/logrus" +) + +var log = logrus.WithField("prefix", "remote_web3signer_internal") diff --git a/validator/keymanager/remote-web3signer/keymanager.go b/validator/keymanager/remote-web3signer/keymanager.go index 42c6e840b2..223d61c9de 100644 --- a/validator/keymanager/remote-web3signer/keymanager.go +++ b/validator/keymanager/remote-web3signer/keymanager.go @@ -4,15 +4,16 @@ import ( "context" "encoding/json" "fmt" - "io" - "io/ioutil" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/go-playground/validator/v10" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/async/event" fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams" "github.com/prysmaticlabs/prysm/crypto/bls" + "github.com/prysmaticlabs/prysm/encoding/bytesutil" validatorpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client" + "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer/internal" v1 "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer/v1" ) @@ -36,17 +37,18 @@ type SetupConfig struct { // Keymanager defines the web3signer keymanager. type Keymanager struct { - client httpSignerClient + client internal.HttpSignerClient genesisValidatorsRoot []byte publicKeysURL string providedPublicKeys [][48]byte accountsChangedFeed *event.Feed + validator *validator.Validate } // NewKeymanager instantiates a new web3signer key manager. func NewKeymanager(_ context.Context, cfg *SetupConfig) (*Keymanager, error) { - if cfg.BaseEndpoint == "" || len(cfg.GenesisValidatorsRoot) == 0 { - return nil, fmt.Errorf("invalid setup config, one or more configs are empty: BaseEndpoint: %v, GenesisValidatorsRoot: %v", cfg.BaseEndpoint, cfg.GenesisValidatorsRoot) + if cfg.BaseEndpoint == "" || !bytesutil.NonZeroRoot(cfg.GenesisValidatorsRoot) { + return nil, fmt.Errorf("invalid setup config, one or more configs are empty: BaseEndpoint: %v, GenesisValidatorsRoot: %#x", cfg.BaseEndpoint, cfg.GenesisValidatorsRoot) } if cfg.PublicKeysURL != "" && len(cfg.ProvidedPublicKeys) != 0 { return nil, errors.New("Either a provided list of public keys or a URL to a list of public keys must be provided, but not both") @@ -54,16 +56,17 @@ func NewKeymanager(_ context.Context, cfg *SetupConfig) (*Keymanager, error) { if cfg.PublicKeysURL == "" && len(cfg.ProvidedPublicKeys) == 0 { return nil, errors.New("no valid public key options provided") } - client, err := newApiClient(cfg.BaseEndpoint) + client, err := internal.NewApiClient(cfg.BaseEndpoint) if err != nil { return nil, errors.Wrap(err, "could not create apiClient") } return &Keymanager{ - client: httpSignerClient(client), + client: internal.HttpSignerClient(client), genesisValidatorsRoot: cfg.GenesisValidatorsRoot, accountsChangedFeed: new(event.Feed), publicKeysURL: cfg.PublicKeysURL, providedPublicKeys: cfg.ProvidedPublicKeys, + validator: validator.New(), }, nil } @@ -83,21 +86,19 @@ func (km *Keymanager) FetchValidatingPublicKeys(ctx context.Context) ([][fieldpa // Sign signs the message by using a remote web3signer server. func (km *Keymanager) Sign(ctx context.Context, request *validatorpb.SignRequest) (bls.Signature, error) { - signRequest, err := getSignRequestJson(request, km.genesisValidatorsRoot) + signRequest, err := getSignRequestJson(ctx, km.validator, request, km.genesisValidatorsRoot) if err != nil { return nil, err } - return km.client.Sign(ctx, hexutil.Encode(request.PublicKey), signRequest) - } // getSignRequestJson returns a json request based on the SignRequest type. -func getSignRequestJson(request *validatorpb.SignRequest, genesisValidatorsRoot []byte) (SignRequestJson, error) { +func getSignRequestJson(ctx context.Context, validator *validator.Validate, request *validatorpb.SignRequest, genesisValidatorsRoot []byte) (internal.SignRequestJson, error) { if request == nil { return nil, errors.New("nil sign request provided") } - if len(genesisValidatorsRoot) != fieldparams.RootLength { + if !bytesutil.NonZeroRoot(genesisValidatorsRoot) { return nil, fmt.Errorf("invalid genesis validators root length, genesis root: %v", genesisValidatorsRoot) } switch request.Object.(type) { @@ -106,32 +107,48 @@ func getSignRequestJson(request *validatorpb.SignRequest, genesisValidatorsRoot if err != nil { return nil, err } + if err = validator.StructCtx(ctx, bockSignRequest); err != nil { + return nil, err + } return json.Marshal(bockSignRequest) case *validatorpb.SignRequest_AttestationData: attestationSignRequest, err := v1.GetAttestationSignRequest(request, genesisValidatorsRoot) if err != nil { return nil, err } + if err = validator.StructCtx(ctx, attestationSignRequest); err != nil { + return nil, err + } return json.Marshal(attestationSignRequest) case *validatorpb.SignRequest_AggregateAttestationAndProof: aggregateAndProofSignRequest, err := v1.GetAggregateAndProofSignRequest(request, genesisValidatorsRoot) if err != nil { return nil, err } + if err = validator.StructCtx(ctx, aggregateAndProofSignRequest); err != nil { + return nil, err + } return json.Marshal(aggregateAndProofSignRequest) case *validatorpb.SignRequest_Slot: aggregationSlotSignRequest, err := v1.GetAggregationSlotSignRequest(request, genesisValidatorsRoot) if err != nil { return nil, err } + if err = validator.StructCtx(ctx, aggregationSlotSignRequest); err != nil { + return nil, err + } return json.Marshal(aggregationSlotSignRequest) case *validatorpb.SignRequest_BlockV2: blocv2AltairSignRequest, err := v1.GetBlockV2AltairSignRequest(request, genesisValidatorsRoot) if err != nil { return nil, err } + if err = validator.StructCtx(ctx, blocv2AltairSignRequest); err != nil { + return nil, err + } return json.Marshal(blocv2AltairSignRequest) - // TODO(#10053): Need to add support for Bellatrix blocks. + // TODO(#10053): Need to add support for merge blocks. + /* case *validatorpb.SignRequest_BlockV3: return "BLOCK_V3", nil @@ -148,30 +165,45 @@ func getSignRequestJson(request *validatorpb.SignRequest, genesisValidatorsRoot if err != nil { return nil, err } + if err = validator.StructCtx(ctx, randaoRevealSignRequest); err != nil { + return nil, err + } return json.Marshal(randaoRevealSignRequest) case *validatorpb.SignRequest_Exit: voluntaryExitRequest, err := v1.GetVoluntaryExitSignRequest(request, genesisValidatorsRoot) if err != nil { return nil, err } + if err = validator.StructCtx(ctx, voluntaryExitRequest); err != nil { + return nil, err + } return json.Marshal(voluntaryExitRequest) case *validatorpb.SignRequest_SyncMessageBlockRoot: syncCommitteeMessageRequest, err := v1.GetSyncCommitteeMessageSignRequest(request, genesisValidatorsRoot) if err != nil { return nil, err } + if err = validator.StructCtx(ctx, syncCommitteeMessageRequest); err != nil { + return nil, err + } return json.Marshal(syncCommitteeMessageRequest) case *validatorpb.SignRequest_SyncAggregatorSelectionData: syncCommitteeSelectionProofRequest, err := v1.GetSyncCommitteeSelectionProofSignRequest(request, genesisValidatorsRoot) if err != nil { return nil, err } + if err = validator.StructCtx(ctx, syncCommitteeSelectionProofRequest); err != nil { + return nil, err + } return json.Marshal(syncCommitteeSelectionProofRequest) case *validatorpb.SignRequest_ContributionAndProof: contributionAndProofRequest, err := v1.GetSyncCommitteeContributionAndProofSignRequest(request, genesisValidatorsRoot) if err != nil { return nil, err } + if err = validator.StructCtx(ctx, contributionAndProofRequest); err != nil { + return nil, err + } return json.Marshal(contributionAndProofRequest) default: return nil, fmt.Errorf("web3signer sign request type %T not supported", request.Object) @@ -187,27 +219,3 @@ func (*Keymanager) SubscribeAccountChanges(_ chan [][48]byte) event.Subscription return nil }) } - -// UnmarshalConfigFile attempts to JSON unmarshal a keymanager -// config file into a SetupConfig struct. -func UnmarshalConfigFile(r io.ReadCloser) (*SetupConfig, error) { - enc, err := ioutil.ReadAll(r) - if err != nil { - return nil, errors.Wrap(err, "could not read config") - } - defer func() { - if err := r.Close(); err != nil { - log.Errorf("Could not close keymanager config file: %v", err) - } - }() - config := &SetupConfig{} - if err := json.Unmarshal(enc, config); err != nil { - return nil, errors.Wrap(err, "could not JSON unmarshal") - } - return config, nil -} - -// MarshalConfigFile for the keymanager. -func MarshalConfigFile(_ context.Context, config *SetupConfig) ([]byte, error) { - return json.MarshalIndent(config, "", "\t") -} diff --git a/validator/keymanager/remote-web3signer/keymanager_test.go b/validator/keymanager/remote-web3signer/keymanager_test.go index e3211f3a10..f396a1dc97 100644 --- a/validator/keymanager/remote-web3signer/keymanager_test.go +++ b/validator/keymanager/remote-web3signer/keymanager_test.go @@ -1,12 +1,9 @@ package remote_web3signer import ( - "bytes" "context" "encoding/hex" - "encoding/json" "fmt" - "io/ioutil" "strings" "testing" @@ -15,7 +12,8 @@ import ( "github.com/prysmaticlabs/prysm/encoding/bytesutil" validatorpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client" "github.com/prysmaticlabs/prysm/testing/require" - v1 "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer/v1" + "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer/internal" + "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer/v1/mock" "github.com/stretchr/testify/assert" ) @@ -25,7 +23,7 @@ type MockClient struct { isThrowingError bool } -func (mc *MockClient) Sign(_ context.Context, _ string, _ SignRequestJson) (bls.Signature, error) { +func (mc *MockClient) Sign(_ context.Context, _ string, _ internal.SignRequestJson) (bls.Signature, error) { decoded, err := hexutil.Decode(mc.Signature) if err != nil { return nil, err @@ -86,7 +84,7 @@ func TestKeymanager_Sign(t *testing.T) { { name: "AGGREGATION_SLOT", args: args{ - request: v1.GetMockSignRequest("AGGREGATION_SLOT"), + request: mock.GetMockSignRequest("AGGREGATION_SLOT"), }, want: desiredSig, wantErr: false, @@ -94,7 +92,7 @@ func TestKeymanager_Sign(t *testing.T) { { name: "AGGREGATE_AND_PROOF", args: args{ - request: v1.GetMockSignRequest("AGGREGATE_AND_PROOF"), + request: mock.GetMockSignRequest("AGGREGATE_AND_PROOF"), }, want: desiredSig, wantErr: false, @@ -102,7 +100,7 @@ func TestKeymanager_Sign(t *testing.T) { { name: "ATTESTATION", args: args{ - request: v1.GetMockSignRequest("ATTESTATION"), + request: mock.GetMockSignRequest("ATTESTATION"), }, want: desiredSig, wantErr: false, @@ -110,7 +108,7 @@ func TestKeymanager_Sign(t *testing.T) { { name: "BLOCK", args: args{ - request: v1.GetMockSignRequest("BLOCK"), + request: mock.GetMockSignRequest("BLOCK"), }, want: desiredSig, wantErr: false, @@ -118,7 +116,7 @@ func TestKeymanager_Sign(t *testing.T) { { name: "BLOCK_V2", args: args{ - request: v1.GetMockSignRequest("BLOCK_V2"), + request: mock.GetMockSignRequest("BLOCK_V2"), }, want: desiredSig, wantErr: false, @@ -126,7 +124,7 @@ func TestKeymanager_Sign(t *testing.T) { { name: "RANDAO_REVEAL", args: args{ - request: v1.GetMockSignRequest("RANDAO_REVEAL"), + request: mock.GetMockSignRequest("RANDAO_REVEAL"), }, want: desiredSig, wantErr: false, @@ -134,7 +132,7 @@ func TestKeymanager_Sign(t *testing.T) { { name: "SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF", args: args{ - request: v1.GetMockSignRequest("SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF"), + request: mock.GetMockSignRequest("SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF"), }, want: desiredSig, wantErr: false, @@ -142,7 +140,7 @@ func TestKeymanager_Sign(t *testing.T) { { name: "SYNC_COMMITTEE_MESSAGE", args: args{ - request: v1.GetMockSignRequest("SYNC_COMMITTEE_MESSAGE"), + request: mock.GetMockSignRequest("SYNC_COMMITTEE_MESSAGE"), }, want: desiredSig, wantErr: false, @@ -150,7 +148,7 @@ func TestKeymanager_Sign(t *testing.T) { { name: "SYNC_COMMITTEE_SELECTION_PROOF", args: args{ - request: v1.GetMockSignRequest("SYNC_COMMITTEE_SELECTION_PROOF"), + request: mock.GetMockSignRequest("SYNC_COMMITTEE_SELECTION_PROOF"), }, want: desiredSig, wantErr: false, @@ -158,7 +156,7 @@ func TestKeymanager_Sign(t *testing.T) { { name: "VOLUNTARY_EXIT", args: args{ - request: v1.GetMockSignRequest("VOLUNTARY_EXIT"), + request: mock.GetMockSignRequest("VOLUNTARY_EXIT"), }, want: desiredSig, wantErr: false, @@ -268,24 +266,3 @@ func TestKeymanager_FetchValidatingPublicKeys_WithExternalURL_ThrowsError(t *tes assert.Nil(t, resp) assert.Equal(t, fmt.Errorf("mock error"), err) } - -func TestUnmarshalConfigFile_HappyPath(t *testing.T) { - fakeConfig := struct { - BaseEndpoint string - GenesisValidatorsRoot []byte - PublicKeysURL string - ProvidedPublicKeys [][48]byte - }{} - fakeConfig.BaseEndpoint = "example.com" - fmt.Printf("%v", fakeConfig) - var buffer bytes.Buffer - b, err := json.Marshal(fakeConfig) - require.NoError(t, err) - _, err = buffer.Write(b) - require.NoError(t, err) - r := ioutil.NopCloser(&buffer) - - config, err := UnmarshalConfigFile(r) - assert.NoError(t, err) - assert.Equal(t, fakeConfig.BaseEndpoint, config.BaseEndpoint) -} diff --git a/validator/keymanager/remote-web3signer/log.go b/validator/keymanager/remote-web3signer/log.go deleted file mode 100644 index 81c8912080..0000000000 --- a/validator/keymanager/remote-web3signer/log.go +++ /dev/null @@ -1,7 +0,0 @@ -package remote_web3signer - -import ( - "github.com/sirupsen/logrus" -) - -var log = logrus.WithField("prefix", "remote_web3signer") diff --git a/validator/keymanager/remote-web3signer/v1/BUILD.bazel b/validator/keymanager/remote-web3signer/v1/BUILD.bazel index 86647ecafa..173f1133ee 100644 --- a/validator/keymanager/remote-web3signer/v1/BUILD.bazel +++ b/validator/keymanager/remote-web3signer/v1/BUILD.bazel @@ -4,14 +4,12 @@ go_library( name = "go_default_library", srcs = [ "custom_mappers.go", - "mocks.go", "requests.go", "web3signer_types.go", ], importpath = "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer/v1", visibility = ["//visibility:public"], deps = [ - "//config/fieldparams:go_default_library", "//network/forks:go_default_library", "//proto/prysm/v1alpha1:go_default_library", "//proto/prysm/v1alpha1/validator-client:go_default_library", @@ -19,7 +17,6 @@ go_library( "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", "@com_github_pkg_errors//:go_default_library", "@com_github_prysmaticlabs_eth2_types//:go_default_library", - "@com_github_prysmaticlabs_go_bitfield//:go_default_library", ], ) @@ -29,11 +26,12 @@ go_test( "custom_mappers_test.go", "requests_test.go", ], - embed = [":go_default_library"], deps = [ + ":go_default_library", "//config/fieldparams:go_default_library", "//proto/prysm/v1alpha1:go_default_library", "//proto/prysm/v1alpha1/validator-client:go_default_library", + "//validator/keymanager/remote-web3signer/v1/mock:go_default_library", "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", "@com_github_prysmaticlabs_eth2_types//: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 index deed533ac2..dcd17082c5 100644 --- a/validator/keymanager/remote-web3signer/v1/custom_mappers.go +++ b/validator/keymanager/remote-web3signer/v1/custom_mappers.go @@ -57,8 +57,8 @@ func MapAttestation(attestation *ethpb.Attestation) (*Attestation, error) { return nil, err } return &Attestation{ + AggregationBits: hexutil.Encode(attestation.AggregationBits), Data: data, - AggregationBits: hexutil.Encode(attestation.AggregationBits.Bytes()), Signature: hexutil.Encode(attestation.Signature), }, nil } @@ -335,7 +335,7 @@ func MapBeaconBlockBodyAltair(body *ethpb.BeaconBlockBodyAltair) (*BeaconBlockBo Deposits: make([]*Deposit, len(body.Deposits)), VoluntaryExits: make([]*SignedVoluntaryExit, len(body.VoluntaryExits)), SyncAggregate: &SyncAggregate{ - SyncCommitteeBits: hexutil.Encode(body.SyncAggregate.SyncCommitteeBits.Bytes()), + SyncCommitteeBits: hexutil.Encode(body.SyncAggregate.SyncCommitteeBits), SyncCommitteeSignature: hexutil.Encode(body.SyncAggregate.SyncCommitteeSignature), }, } @@ -407,7 +407,7 @@ func MapContributionAndProof(contribution *ethpb.ContributionAndProof) (*Contrib 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()), + AggregationBits: hexutil.Encode(contribution.Contribution.AggregationBits), 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 index af83bbca28..44ad85dff4 100644 --- a/validator/keymanager/remote-web3signer/v1/custom_mappers_test.go +++ b/validator/keymanager/remote-web3signer/v1/custom_mappers_test.go @@ -1,4 +1,4 @@ -package v1 +package v1_test import ( "reflect" @@ -9,6 +9,8 @@ import ( "github.com/prysmaticlabs/go-bitfield" fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams" ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + v1 "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer/v1" + "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer/v1/mock" ) func TestMapAggregateAndProof(t *testing.T) { @@ -18,7 +20,7 @@ func TestMapAggregateAndProof(t *testing.T) { tests := []struct { name string args args - want *AggregateAndProof + want *v1.AggregateAndProof wantErr bool }{ { @@ -42,9 +44,9 @@ func TestMapAggregateAndProof(t *testing.T) { SelectionProof: make([]byte, fieldparams.BLSSignatureLength), }, }, - want: &AggregateAndProof{ + want: &v1.AggregateAndProof{ AggregatorIndex: "0", - Aggregate: MockAttestation(), + Aggregate: mock.MockAttestation(), SelectionProof: hexutil.Encode(make([]byte, fieldparams.BLSSignatureLength)), }, wantErr: false, @@ -52,7 +54,7 @@ func TestMapAggregateAndProof(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := MapAggregateAndProof(tt.args.from) + got, err := v1.MapAggregateAndProof(tt.args.from) if (err != nil) != tt.wantErr { t.Errorf("MapAggregateAndProof() error = %v, wantErr %v", err, tt.wantErr) return @@ -71,7 +73,7 @@ func TestMapAttestation(t *testing.T) { tests := []struct { name string args args - want *Attestation + want *v1.Attestation wantErr bool }{ { @@ -91,13 +93,13 @@ func TestMapAttestation(t *testing.T) { Signature: make([]byte, 96), }, }, - want: MockAttestation(), + want: mock.MockAttestation(), wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := MapAttestation(tt.args.attestation) + got, err := v1.MapAttestation(tt.args.attestation) if (err != nil) != tt.wantErr { t.Errorf("MapAttestation() error = %v, wantErr %v", err, tt.wantErr) return @@ -116,7 +118,7 @@ func TestMapAttestationData(t *testing.T) { tests := []struct { name string args args - want *AttestationData + want *v1.AttestationData wantErr bool }{ { @@ -132,13 +134,13 @@ func TestMapAttestationData(t *testing.T) { }, }, }, - want: MockAttestation().Data, + want: mock.MockAttestation().Data, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := MapAttestationData(tt.args.data) + got, err := v1.MapAttestationData(tt.args.data) if (err != nil) != tt.wantErr { t.Errorf("MapAttestationData() error = %v, wantErr %v", err, tt.wantErr) return @@ -157,7 +159,7 @@ func TestMapAttesterSlashing(t *testing.T) { tests := []struct { name string args args - want *AttesterSlashing + want *v1.AttesterSlashing wantErr bool }{ { @@ -192,16 +194,16 @@ func TestMapAttesterSlashing(t *testing.T) { }, }, }, - want: &AttesterSlashing{ - Attestation_1: MockIndexedAttestation(), - Attestation_2: MockIndexedAttestation(), + want: &v1.AttesterSlashing{ + Attestation_1: mock.MockIndexedAttestation(), + Attestation_2: mock.MockIndexedAttestation(), }, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := MapAttesterSlashing(tt.args.slashing) + got, err := v1.MapAttesterSlashing(tt.args.slashing) if (err != nil) != tt.wantErr { t.Errorf("MapAttesterSlashing() error = %v, wantErr %v", err, tt.wantErr) return @@ -220,7 +222,7 @@ func TestMapBeaconBlockAltair(t *testing.T) { tests := []struct { name string args args - want *BeaconBlockAltair + want *v1.BeaconBlockAltair wantErr bool }{ { @@ -330,18 +332,18 @@ func TestMapBeaconBlockAltair(t *testing.T) { }, SyncAggregate: ðpb.SyncAggregate{ SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength), - SyncCommitteeBits: bitfield.NewBitvector512(), + SyncCommitteeBits: mock.MockSyncComitteeBits(), }, }, }, }, - want: MockBeaconBlockAltair(), + want: mock.MockBeaconBlockAltair(), wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := MapBeaconBlockAltair(tt.args.block) + got, err := v1.MapBeaconBlockAltair(tt.args.block) if (err != nil) != tt.wantErr { t.Errorf("MapBeaconBlockAltair() error = %v, wantErr %v", err, tt.wantErr) return @@ -360,7 +362,7 @@ func TestMapBeaconBlockBody(t *testing.T) { tests := []struct { name string args args - want *BeaconBlockBody + want *v1.BeaconBlockBody wantErr bool }{ { @@ -465,13 +467,13 @@ func TestMapBeaconBlockBody(t *testing.T) { }, }, }, - want: MockBeaconBlockBody(), + want: mock.MockBeaconBlockBody(), wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := MapBeaconBlockBody(tt.args.body) + got, err := v1.MapBeaconBlockBody(tt.args.body) if (err != nil) != tt.wantErr { t.Errorf("MapBeaconBlockBody() error = %v, wantErr %v", err, tt.wantErr) return @@ -490,7 +492,7 @@ func TestMapContributionAndProof(t *testing.T) { tests := []struct { name string args args - want *ContributionAndProof + want *v1.ContributionAndProof wantErr bool }{ { @@ -502,18 +504,18 @@ func TestMapContributionAndProof(t *testing.T) { Slot: 0, BlockRoot: make([]byte, fieldparams.RootLength), SubcommitteeIndex: 0, - AggregationBits: bitfield.NewBitvector128(), + AggregationBits: mock.MockAggregationBits(), Signature: make([]byte, fieldparams.BLSSignatureLength), }, SelectionProof: make([]byte, fieldparams.BLSSignatureLength), }, }, - want: MockContributionAndProof(), + want: mock.MockContributionAndProof(), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := MapContributionAndProof(tt.args.contribution) + got, err := v1.MapContributionAndProof(tt.args.contribution) if (err != nil) != tt.wantErr { t.Errorf("MapContributionAndProof() error = %v, wantErr %v", err, tt.wantErr) return @@ -534,7 +536,7 @@ func TestMapForkInfo(t *testing.T) { tests := []struct { name string args args - want *ForkInfo + want *v1.ForkInfo wantErr bool }{ { @@ -543,13 +545,13 @@ func TestMapForkInfo(t *testing.T) { slot: 0, genesisValidatorsRoot: make([]byte, fieldparams.RootLength), }, - want: MockForkInfo(), + want: mock.MockForkInfo(), wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := MapForkInfo(tt.args.slot, tt.args.genesisValidatorsRoot) + got, err := v1.MapForkInfo(tt.args.slot, tt.args.genesisValidatorsRoot) if (err != nil) != tt.wantErr { t.Errorf("MapForkInfo() error = %v, wantErr %v", err, tt.wantErr) return @@ -568,7 +570,7 @@ func TestMapSyncAggregatorSelectionData(t *testing.T) { tests := []struct { name string args args - want *SyncAggregatorSelectionData + want *v1.SyncAggregatorSelectionData wantErr bool }{ { @@ -579,7 +581,7 @@ func TestMapSyncAggregatorSelectionData(t *testing.T) { SubcommitteeIndex: 0, }, }, - want: &SyncAggregatorSelectionData{ + want: &v1.SyncAggregatorSelectionData{ Slot: "0", SubcommitteeIndex: "0", }, @@ -588,7 +590,7 @@ func TestMapSyncAggregatorSelectionData(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := MapSyncAggregatorSelectionData(tt.args.data) + got, err := v1.MapSyncAggregatorSelectionData(tt.args.data) if (err != nil) != tt.wantErr { t.Errorf("MapSyncAggregatorSelectionData() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/validator/keymanager/remote-web3signer/v1/mock/BUILD.bazel b/validator/keymanager/remote-web3signer/v1/mock/BUILD.bazel new file mode 100644 index 0000000000..e227bdb8a1 --- /dev/null +++ b/validator/keymanager/remote-web3signer/v1/mock/BUILD.bazel @@ -0,0 +1,17 @@ +load("@prysm//tools/go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + testonly = True, + srcs = ["mocks.go"], + importpath = "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer/v1/mock", + visibility = ["//visibility:public"], + deps = [ + "//config/fieldparams: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_prysmaticlabs_go_bitfield//:go_default_library", + ], +) diff --git a/validator/keymanager/remote-web3signer/v1/mocks.go b/validator/keymanager/remote-web3signer/v1/mock/mocks.go similarity index 83% rename from validator/keymanager/remote-web3signer/v1/mocks.go rename to validator/keymanager/remote-web3signer/v1/mock/mocks.go index 5b05bb62b4..64b4fed958 100644 --- a/validator/keymanager/remote-web3signer/v1/mocks.go +++ b/validator/keymanager/remote-web3signer/v1/mock/mocks.go @@ -1,4 +1,4 @@ -package v1 +package mock import ( "fmt" @@ -8,12 +8,37 @@ import ( fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams" eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" validatorpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client" + v1 "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer/v1" ) ///////////////////////////////////////////////////////////////////////////////////////////////// //////////////// Mock Requests ////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////// +func MockSyncComitteeBits() []byte { + currSize := new(eth.SyncAggregate).SyncCommitteeBits.Len() + switch currSize { + case 512: + return bitfield.NewBitvector512() + case 32: + return bitfield.NewBitvector32() + default: + return nil + } +} + +func MockAggregationBits() []byte { + currSize := new(eth.SyncCommitteeContribution).AggregationBits.Len() + switch currSize { + case 128: + return bitfield.NewBitvector128() + case 8: + return bitfield.NewBitvector8() + default: + return nil + } +} + // GetMockSignRequest returns a mock SignRequest by type. func GetMockSignRequest(t string) *validatorpb.SignRequest { switch t { @@ -294,7 +319,7 @@ func GetMockSignRequest(t string) *validatorpb.SignRequest { }, SyncAggregate: ð.SyncAggregate{ SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength), - SyncCommitteeBits: bitfield.NewBitvector512(), + SyncCommitteeBits: MockSyncComitteeBits(), }, }, }, @@ -323,7 +348,7 @@ func GetMockSignRequest(t string) *validatorpb.SignRequest { Slot: 0, BlockRoot: make([]byte, fieldparams.RootLength), SubcommitteeIndex: 0, - AggregationBits: bitfield.NewBitvector128(), + AggregationBits: MockAggregationBits(), Signature: make([]byte, fieldparams.BLSSignatureLength), }, SelectionProof: make([]byte, fieldparams.BLSSignatureLength), @@ -374,22 +399,22 @@ func GetMockSignRequest(t string) *validatorpb.SignRequest { } // MockAggregationSlotSignRequest is a mock implementation of the AggregationSlotSignRequest. -func MockAggregationSlotSignRequest() *AggregationSlotSignRequest { - return &AggregationSlotSignRequest{ +func MockAggregationSlotSignRequest() *v1.AggregationSlotSignRequest { + return &v1.AggregationSlotSignRequest{ Type: "AGGREGATION_SLOT", ForkInfo: MockForkInfo(), SigningRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), - AggregationSlot: &AggregationSlot{Slot: "0"}, + AggregationSlot: &v1.AggregationSlot{Slot: "0"}, } } // MockAggregateAndProofSignRequest is a mock implementation of the AggregateAndProofSignRequest. -func MockAggregateAndProofSignRequest() *AggregateAndProofSignRequest { - return &AggregateAndProofSignRequest{ +func MockAggregateAndProofSignRequest() *v1.AggregateAndProofSignRequest { + return &v1.AggregateAndProofSignRequest{ Type: "AGGREGATE_AND_PROOF", ForkInfo: MockForkInfo(), SigningRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), - AggregateAndProof: &AggregateAndProof{ + AggregateAndProof: &v1.AggregateAndProof{ AggregatorIndex: "0", Aggregate: MockAttestation(), SelectionProof: hexutil.Encode(make([]byte, fieldparams.BLSSignatureLength)), @@ -398,8 +423,8 @@ func MockAggregateAndProofSignRequest() *AggregateAndProofSignRequest { } // MockAttestationSignRequest is a mock implementation of the AttestationSignRequest. -func MockAttestationSignRequest() *AttestationSignRequest { - return &AttestationSignRequest{ +func MockAttestationSignRequest() *v1.AttestationSignRequest { + return &v1.AttestationSignRequest{ Type: "ATTESTATION", ForkInfo: MockForkInfo(), SigningRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), @@ -408,12 +433,12 @@ func MockAttestationSignRequest() *AttestationSignRequest { } // MockBlockSignRequest is a mock implementation of the BlockSignRequest. -func MockBlockSignRequest() *BlockSignRequest { - return &BlockSignRequest{ +func MockBlockSignRequest() *v1.BlockSignRequest { + return &v1.BlockSignRequest{ Type: "BLOCK", ForkInfo: MockForkInfo(), SigningRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), - Block: &BeaconBlock{ + Block: &v1.BeaconBlock{ Slot: "0", ProposerIndex: "0", ParentRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), @@ -424,12 +449,12 @@ func MockBlockSignRequest() *BlockSignRequest { } // MockBlockV2AltairSignRequest is a mock implementation of the BlockV2AltairSignRequest. -func MockBlockV2AltairSignRequest() *BlockV2AltairSignRequest { - return &BlockV2AltairSignRequest{ +func MockBlockV2AltairSignRequest() *v1.BlockV2AltairSignRequest { + return &v1.BlockV2AltairSignRequest{ Type: "BLOCK_V2", ForkInfo: MockForkInfo(), SigningRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), - BeaconBlock: &BeaconBlockAltairBlockV2{ + BeaconBlock: &v1.BeaconBlockAltairBlockV2{ Version: "ALTAIR", Block: MockBeaconBlockAltair(), }, @@ -437,20 +462,20 @@ func MockBlockV2AltairSignRequest() *BlockV2AltairSignRequest { } // MockRandaoRevealSignRequest is a mock implementation of the RandaoRevealSignRequest. -func MockRandaoRevealSignRequest() *RandaoRevealSignRequest { - return &RandaoRevealSignRequest{ +func MockRandaoRevealSignRequest() *v1.RandaoRevealSignRequest { + return &v1.RandaoRevealSignRequest{ Type: "RANDAO_REVEAL", ForkInfo: MockForkInfo(), SigningRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), - RandaoReveal: &RandaoReveal{ + RandaoReveal: &v1.RandaoReveal{ Epoch: "0", }, } } // MockSyncCommitteeContributionAndProofSignRequest is a mock implementation of the SyncCommitteeContributionAndProofSignRequest. -func MockSyncCommitteeContributionAndProofSignRequest() *SyncCommitteeContributionAndProofSignRequest { - return &SyncCommitteeContributionAndProofSignRequest{ +func MockSyncCommitteeContributionAndProofSignRequest() *v1.SyncCommitteeContributionAndProofSignRequest { + return &v1.SyncCommitteeContributionAndProofSignRequest{ Type: "SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF", ForkInfo: MockForkInfo(), SigningRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), @@ -459,12 +484,12 @@ func MockSyncCommitteeContributionAndProofSignRequest() *SyncCommitteeContributi } // MockSyncCommitteeMessageSignRequest is a mock implementation of the SyncCommitteeMessageSignRequest. -func MockSyncCommitteeMessageSignRequest() *SyncCommitteeMessageSignRequest { - return &SyncCommitteeMessageSignRequest{ +func MockSyncCommitteeMessageSignRequest() *v1.SyncCommitteeMessageSignRequest { + return &v1.SyncCommitteeMessageSignRequest{ Type: "SYNC_COMMITTEE_MESSAGE", ForkInfo: MockForkInfo(), SigningRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), - SyncCommitteeMessage: &SyncCommitteeMessage{ + SyncCommitteeMessage: &v1.SyncCommitteeMessage{ BeaconBlockRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), Slot: "0", }, @@ -472,12 +497,12 @@ func MockSyncCommitteeMessageSignRequest() *SyncCommitteeMessageSignRequest { } // MockSyncCommitteeSelectionProofSignRequest is a mock implementation of the SyncCommitteeSelectionProofSignRequest. -func MockSyncCommitteeSelectionProofSignRequest() *SyncCommitteeSelectionProofSignRequest { - return &SyncCommitteeSelectionProofSignRequest{ +func MockSyncCommitteeSelectionProofSignRequest() *v1.SyncCommitteeSelectionProofSignRequest { + return &v1.SyncCommitteeSelectionProofSignRequest{ Type: "SYNC_COMMITTEE_SELECTION_PROOF", ForkInfo: MockForkInfo(), SigningRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), - SyncAggregatorSelectionData: &SyncAggregatorSelectionData{ + SyncAggregatorSelectionData: &v1.SyncAggregatorSelectionData{ Slot: "0", SubcommitteeIndex: "0", }, @@ -485,12 +510,12 @@ func MockSyncCommitteeSelectionProofSignRequest() *SyncCommitteeSelectionProofSi } // MockVoluntaryExitSignRequest is a mock implementation of the VoluntaryExitSignRequest. -func MockVoluntaryExitSignRequest() *VoluntaryExitSignRequest { - return &VoluntaryExitSignRequest{ +func MockVoluntaryExitSignRequest() *v1.VoluntaryExitSignRequest { + return &v1.VoluntaryExitSignRequest{ Type: "VOLUNTARY_EXIT", ForkInfo: MockForkInfo(), SigningRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), - VoluntaryExit: &VoluntaryExit{ + VoluntaryExit: &v1.VoluntaryExit{ Epoch: "0", ValidatorIndex: "0", }, @@ -502,9 +527,9 @@ func MockVoluntaryExitSignRequest() *VoluntaryExitSignRequest { ///////////////////////////////////////////////////////////////////////////////////////////////// // MockForkInfo is a mock implementation of the ForkInfo. -func MockForkInfo() *ForkInfo { - return &ForkInfo{ - Fork: &Fork{ +func MockForkInfo() *v1.ForkInfo { + return &v1.ForkInfo{ + Fork: &v1.Fork{ PreviousVersion: hexutil.Encode(make([]byte, 4)), CurrentVersion: hexutil.Encode(make([]byte, 4)), Epoch: "0", @@ -515,18 +540,18 @@ func MockForkInfo() *ForkInfo { } // MockAttestation is a mock implementation of the Attestation. -func MockAttestation() *Attestation { - return &Attestation{ - AggregationBits: hexutil.Encode(bitfield.Bitlist{0b1101}.Bytes()), - Data: &AttestationData{ +func MockAttestation() *v1.Attestation { + return &v1.Attestation{ + AggregationBits: hexutil.Encode(bitfield.Bitlist{0b1101}), + Data: &v1.AttestationData{ Slot: "0", Index: "0", BeaconBlockRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), - Source: &Checkpoint{ + Source: &v1.Checkpoint{ Epoch: "0", Root: hexutil.Encode(make([]byte, fieldparams.RootLength)), }, - Target: &Checkpoint{ + Target: &v1.Checkpoint{ Epoch: "0", Root: hexutil.Encode(make([]byte, fieldparams.RootLength)), }, @@ -535,18 +560,18 @@ func MockAttestation() *Attestation { } } -func MockIndexedAttestation() *IndexedAttestation { - return &IndexedAttestation{ +func MockIndexedAttestation() *v1.IndexedAttestation { + return &v1.IndexedAttestation{ AttestingIndices: []string{"0", "1", "2"}, - Data: &AttestationData{ + Data: &v1.AttestationData{ Slot: "0", Index: "0", BeaconBlockRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), - Source: &Checkpoint{ + Source: &v1.Checkpoint{ Epoch: "0", Root: hexutil.Encode(make([]byte, fieldparams.RootLength)), }, - Target: &Checkpoint{ + Target: &v1.Checkpoint{ Epoch: "0", Root: hexutil.Encode(make([]byte, fieldparams.RootLength)), }, @@ -555,24 +580,24 @@ func MockIndexedAttestation() *IndexedAttestation { } } -func MockBeaconBlockAltair() *BeaconBlockAltair { - return &BeaconBlockAltair{ +func MockBeaconBlockAltair() *v1.BeaconBlockAltair { + return &v1.BeaconBlockAltair{ Slot: "0", ProposerIndex: "0", ParentRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), StateRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), - Body: &BeaconBlockBodyAltair{ + Body: &v1.BeaconBlockBodyAltair{ RandaoReveal: hexutil.Encode(make([]byte, 32)), - Eth1Data: &Eth1Data{ + Eth1Data: &v1.Eth1Data{ DepositRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), DepositCount: "0", BlockHash: hexutil.Encode(make([]byte, 32)), }, Graffiti: hexutil.Encode(make([]byte, 32)), - ProposerSlashings: []*ProposerSlashing{ + ProposerSlashings: []*v1.ProposerSlashing{ { - SignedHeader_1: &SignedBeaconBlockHeader{ - Message: &BeaconBlockHeader{ + SignedHeader_1: &v1.SignedBeaconBlockHeader{ + Message: &v1.BeaconBlockHeader{ Slot: "0", ProposerIndex: "0", ParentRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), @@ -581,8 +606,8 @@ func MockBeaconBlockAltair() *BeaconBlockAltair { }, Signature: hexutil.Encode(make([]byte, fieldparams.BLSSignatureLength)), }, - SignedHeader_2: &SignedBeaconBlockHeader{ - Message: &BeaconBlockHeader{ + SignedHeader_2: &v1.SignedBeaconBlockHeader{ + Message: &v1.BeaconBlockHeader{ Slot: "0", ProposerIndex: "0", ParentRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), @@ -593,19 +618,19 @@ func MockBeaconBlockAltair() *BeaconBlockAltair { }, }, }, - AttesterSlashings: []*AttesterSlashing{ + AttesterSlashings: []*v1.AttesterSlashing{ { Attestation_1: MockIndexedAttestation(), Attestation_2: MockIndexedAttestation(), }, }, - Attestations: []*Attestation{ + Attestations: []*v1.Attestation{ MockAttestation(), }, - Deposits: []*Deposit{ + Deposits: []*v1.Deposit{ { Proof: []string{"0x41"}, - Data: &DepositData{ + Data: &v1.DepositData{ PublicKey: hexutil.Encode(make([]byte, fieldparams.BLSPubkeyLength)), WithdrawalCredentials: hexutil.Encode(make([]byte, 32)), Amount: "0", @@ -613,36 +638,36 @@ func MockBeaconBlockAltair() *BeaconBlockAltair { }, }, }, - VoluntaryExits: []*SignedVoluntaryExit{ + VoluntaryExits: []*v1.SignedVoluntaryExit{ { - Message: &VoluntaryExit{ + Message: &v1.VoluntaryExit{ Epoch: "0", ValidatorIndex: "0", }, Signature: hexutil.Encode(make([]byte, fieldparams.BLSSignatureLength)), }, }, - SyncAggregate: &SyncAggregate{ + SyncAggregate: &v1.SyncAggregate{ SyncCommitteeSignature: hexutil.Encode(make([]byte, fieldparams.BLSSignatureLength)), - SyncCommitteeBits: hexutil.Encode(bitfield.NewBitvector512().Bytes()), + SyncCommitteeBits: hexutil.Encode(MockSyncComitteeBits()), }, }, } } -func MockBeaconBlockBody() *BeaconBlockBody { - return &BeaconBlockBody{ +func MockBeaconBlockBody() *v1.BeaconBlockBody { + return &v1.BeaconBlockBody{ RandaoReveal: hexutil.Encode(make([]byte, 32)), - Eth1Data: &Eth1Data{ + Eth1Data: &v1.Eth1Data{ DepositRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), DepositCount: "0", BlockHash: hexutil.Encode(make([]byte, 32)), }, Graffiti: hexutil.Encode(make([]byte, 32)), - ProposerSlashings: []*ProposerSlashing{ + ProposerSlashings: []*v1.ProposerSlashing{ { - SignedHeader_1: &SignedBeaconBlockHeader{ - Message: &BeaconBlockHeader{ + SignedHeader_1: &v1.SignedBeaconBlockHeader{ + Message: &v1.BeaconBlockHeader{ Slot: "0", ProposerIndex: "0", ParentRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), @@ -651,8 +676,8 @@ func MockBeaconBlockBody() *BeaconBlockBody { }, Signature: hexutil.Encode(make([]byte, fieldparams.BLSSignatureLength)), }, - SignedHeader_2: &SignedBeaconBlockHeader{ - Message: &BeaconBlockHeader{ + SignedHeader_2: &v1.SignedBeaconBlockHeader{ + Message: &v1.BeaconBlockHeader{ Slot: "0", ProposerIndex: "0", ParentRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), @@ -663,19 +688,19 @@ func MockBeaconBlockBody() *BeaconBlockBody { }, }, }, - AttesterSlashings: []*AttesterSlashing{ + AttesterSlashings: []*v1.AttesterSlashing{ { Attestation_1: MockIndexedAttestation(), Attestation_2: MockIndexedAttestation(), }, }, - Attestations: []*Attestation{ + Attestations: []*v1.Attestation{ MockAttestation(), }, - Deposits: []*Deposit{ + Deposits: []*v1.Deposit{ { Proof: []string{"0x41"}, - Data: &DepositData{ + Data: &v1.DepositData{ PublicKey: hexutil.Encode(make([]byte, fieldparams.BLSPubkeyLength)), WithdrawalCredentials: hexutil.Encode(make([]byte, 32)), Amount: "0", @@ -683,9 +708,9 @@ func MockBeaconBlockBody() *BeaconBlockBody { }, }, }, - VoluntaryExits: []*SignedVoluntaryExit{ + VoluntaryExits: []*v1.SignedVoluntaryExit{ { - Message: &VoluntaryExit{ + Message: &v1.VoluntaryExit{ Epoch: "0", ValidatorIndex: "0", }, @@ -695,14 +720,14 @@ func MockBeaconBlockBody() *BeaconBlockBody { } } -func MockContributionAndProof() *ContributionAndProof { - return &ContributionAndProof{ +func MockContributionAndProof() *v1.ContributionAndProof { + return &v1.ContributionAndProof{ AggregatorIndex: "0", - Contribution: &SyncCommitteeContribution{ + Contribution: &v1.SyncCommitteeContribution{ Slot: "0", BeaconBlockRoot: hexutil.Encode(make([]byte, fieldparams.RootLength)), SubcommitteeIndex: "0", - AggregationBits: hexutil.Encode(bitfield.NewBitvector128().Bytes()), + AggregationBits: hexutil.Encode(MockAggregationBits()), Signature: hexutil.Encode(make([]byte, fieldparams.BLSSignatureLength)), }, SelectionProof: hexutil.Encode(make([]byte, fieldparams.BLSSignatureLength)), diff --git a/validator/keymanager/remote-web3signer/v1/requests_test.go b/validator/keymanager/remote-web3signer/v1/requests_test.go index 3edd7e1bea..4e91cf5cf8 100644 --- a/validator/keymanager/remote-web3signer/v1/requests_test.go +++ b/validator/keymanager/remote-web3signer/v1/requests_test.go @@ -1,4 +1,4 @@ -package v1 +package v1_test import ( "reflect" @@ -6,6 +6,8 @@ import ( fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams" validatorpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client" + v1 "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer/v1" + mock "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer/v1/mock" ) func TestGetAggregateAndProofSignRequest(t *testing.T) { @@ -16,22 +18,22 @@ func TestGetAggregateAndProofSignRequest(t *testing.T) { tests := []struct { name string args args - want *AggregateAndProofSignRequest + want *v1.AggregateAndProofSignRequest wantErr bool }{ { name: "Happy Path Test", args: args{ - request: GetMockSignRequest("AGGREGATE_AND_PROOF"), + request: mock.GetMockSignRequest("AGGREGATE_AND_PROOF"), genesisValidatorsRoot: make([]byte, fieldparams.RootLength), }, - want: MockAggregateAndProofSignRequest(), + want: mock.MockAggregateAndProofSignRequest(), wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := GetAggregateAndProofSignRequest(tt.args.request, tt.args.genesisValidatorsRoot) + got, err := v1.GetAggregateAndProofSignRequest(tt.args.request, tt.args.genesisValidatorsRoot) if (err != nil) != tt.wantErr { t.Errorf("GetAggregateAndProofSignRequest() error = %v, wantErr %v", err, tt.wantErr) return @@ -51,22 +53,22 @@ func TestGetAggregationSlotSignRequest(t *testing.T) { tests := []struct { name string args args - want *AggregationSlotSignRequest + want *v1.AggregationSlotSignRequest wantErr bool }{ { name: "Happy Path Test", args: args{ - request: GetMockSignRequest("AGGREGATION_SLOT"), + request: mock.GetMockSignRequest("AGGREGATION_SLOT"), genesisValidatorsRoot: make([]byte, fieldparams.RootLength), }, - want: MockAggregationSlotSignRequest(), + want: mock.MockAggregationSlotSignRequest(), wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := GetAggregationSlotSignRequest(tt.args.request, tt.args.genesisValidatorsRoot) + got, err := v1.GetAggregationSlotSignRequest(tt.args.request, tt.args.genesisValidatorsRoot) if (err != nil) != tt.wantErr { t.Errorf("GetAggregationSlotSignRequest() error = %v, wantErr %v", err, tt.wantErr) return @@ -86,21 +88,21 @@ func TestGetAttestationSignRequest(t *testing.T) { tests := []struct { name string args args - want *AttestationSignRequest + want *v1.AttestationSignRequest wantErr bool }{ { name: "Happy Path Test", args: args{ - request: GetMockSignRequest("ATTESTATION"), + request: mock.GetMockSignRequest("ATTESTATION"), genesisValidatorsRoot: make([]byte, fieldparams.RootLength), }, - want: MockAttestationSignRequest(), + want: mock.MockAttestationSignRequest(), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := GetAttestationSignRequest(tt.args.request, tt.args.genesisValidatorsRoot) + got, err := v1.GetAttestationSignRequest(tt.args.request, tt.args.genesisValidatorsRoot) if (err != nil) != tt.wantErr { t.Errorf("GetAttestationSignRequest() error = %v, wantErr %v", err, tt.wantErr) return @@ -120,22 +122,22 @@ func TestGetBlockSignRequest(t *testing.T) { tests := []struct { name string args args - want *BlockSignRequest + want *v1.BlockSignRequest wantErr bool }{ { name: "Happy Path Test", args: args{ - request: GetMockSignRequest("BLOCK"), + request: mock.GetMockSignRequest("BLOCK"), genesisValidatorsRoot: make([]byte, fieldparams.RootLength), }, - want: MockBlockSignRequest(), + want: mock.MockBlockSignRequest(), wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := GetBlockSignRequest(tt.args.request, tt.args.genesisValidatorsRoot) + got, err := v1.GetBlockSignRequest(tt.args.request, tt.args.genesisValidatorsRoot) if (err != nil) != tt.wantErr { t.Errorf("GetBlockSignRequest() error = %v, wantErr %v", err, tt.wantErr) return @@ -155,22 +157,22 @@ func TestGetBlockV2AltairSignRequest(t *testing.T) { tests := []struct { name string args args - want *BlockV2AltairSignRequest + want *v1.BlockV2AltairSignRequest wantErr bool }{ { name: "Happy Path Test", args: args{ - request: GetMockSignRequest("BLOCK_V2"), + request: mock.GetMockSignRequest("BLOCK_V2"), genesisValidatorsRoot: make([]byte, fieldparams.RootLength), }, - want: MockBlockV2AltairSignRequest(), + want: mock.MockBlockV2AltairSignRequest(), wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := GetBlockV2AltairSignRequest(tt.args.request, tt.args.genesisValidatorsRoot) + got, err := v1.GetBlockV2AltairSignRequest(tt.args.request, tt.args.genesisValidatorsRoot) if (err != nil) != tt.wantErr { t.Errorf("GetBlockV2AltairSignRequest() error = %v, wantErr %v", err, tt.wantErr) return @@ -190,22 +192,22 @@ func TestGetRandaoRevealSignRequest(t *testing.T) { tests := []struct { name string args args - want *RandaoRevealSignRequest + want *v1.RandaoRevealSignRequest wantErr bool }{ { name: "Happy Path Test", args: args{ - request: GetMockSignRequest("RANDAO_REVEAL"), + request: mock.GetMockSignRequest("RANDAO_REVEAL"), genesisValidatorsRoot: make([]byte, fieldparams.RootLength), }, - want: MockRandaoRevealSignRequest(), + want: mock.MockRandaoRevealSignRequest(), wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := GetRandaoRevealSignRequest(tt.args.request, tt.args.genesisValidatorsRoot) + got, err := v1.GetRandaoRevealSignRequest(tt.args.request, tt.args.genesisValidatorsRoot) if (err != nil) != tt.wantErr { t.Errorf("GetRandaoRevealSignRequest() error = %v, wantErr %v", err, tt.wantErr) return @@ -225,22 +227,22 @@ func TestGetSyncCommitteeContributionAndProofSignRequest(t *testing.T) { tests := []struct { name string args args - want *SyncCommitteeContributionAndProofSignRequest + want *v1.SyncCommitteeContributionAndProofSignRequest wantErr bool }{ { name: "Happy Path Test", args: args{ - request: GetMockSignRequest("SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF"), + request: mock.GetMockSignRequest("SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF"), genesisValidatorsRoot: make([]byte, fieldparams.RootLength), }, - want: MockSyncCommitteeContributionAndProofSignRequest(), + want: mock.MockSyncCommitteeContributionAndProofSignRequest(), wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := GetSyncCommitteeContributionAndProofSignRequest(tt.args.request, tt.args.genesisValidatorsRoot) + got, err := v1.GetSyncCommitteeContributionAndProofSignRequest(tt.args.request, tt.args.genesisValidatorsRoot) if (err != nil) != tt.wantErr { t.Errorf("GetSyncCommitteeContributionAndProofSignRequest() error = %v, wantErr %v", err, tt.wantErr) return @@ -260,22 +262,22 @@ func TestGetSyncCommitteeMessageSignRequest(t *testing.T) { tests := []struct { name string args args - want *SyncCommitteeMessageSignRequest + want *v1.SyncCommitteeMessageSignRequest wantErr bool }{ { name: "Happy Path Test", args: args{ - request: GetMockSignRequest("SYNC_COMMITTEE_MESSAGE"), + request: mock.GetMockSignRequest("SYNC_COMMITTEE_MESSAGE"), genesisValidatorsRoot: make([]byte, fieldparams.RootLength), }, - want: MockSyncCommitteeMessageSignRequest(), + want: mock.MockSyncCommitteeMessageSignRequest(), wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := GetSyncCommitteeMessageSignRequest(tt.args.request, tt.args.genesisValidatorsRoot) + got, err := v1.GetSyncCommitteeMessageSignRequest(tt.args.request, tt.args.genesisValidatorsRoot) if (err != nil) != tt.wantErr { t.Errorf("GetSyncCommitteeMessageSignRequest() error = %v, wantErr %v", err, tt.wantErr) return @@ -295,22 +297,22 @@ func TestGetSyncCommitteeSelectionProofSignRequest(t *testing.T) { tests := []struct { name string args args - want *SyncCommitteeSelectionProofSignRequest + want *v1.SyncCommitteeSelectionProofSignRequest wantErr bool }{ { name: "Happy Path Test", args: args{ - request: GetMockSignRequest("SYNC_COMMITTEE_SELECTION_PROOF"), + request: mock.GetMockSignRequest("SYNC_COMMITTEE_SELECTION_PROOF"), genesisValidatorsRoot: make([]byte, fieldparams.RootLength), }, - want: MockSyncCommitteeSelectionProofSignRequest(), + want: mock.MockSyncCommitteeSelectionProofSignRequest(), wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := GetSyncCommitteeSelectionProofSignRequest(tt.args.request, tt.args.genesisValidatorsRoot) + got, err := v1.GetSyncCommitteeSelectionProofSignRequest(tt.args.request, tt.args.genesisValidatorsRoot) if (err != nil) != tt.wantErr { t.Errorf("GetSyncCommitteeSelectionProofSignRequest() error = %v, wantErr %v", err, tt.wantErr) return @@ -330,22 +332,22 @@ func TestGetVoluntaryExitSignRequest(t *testing.T) { tests := []struct { name string args args - want *VoluntaryExitSignRequest + want *v1.VoluntaryExitSignRequest wantErr bool }{ { name: "Happy Path Test", args: args{ - request: GetMockSignRequest("VOLUNTARY_EXIT"), + request: mock.GetMockSignRequest("VOLUNTARY_EXIT"), genesisValidatorsRoot: make([]byte, fieldparams.RootLength), }, - want: MockVoluntaryExitSignRequest(), + want: mock.MockVoluntaryExitSignRequest(), wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := GetVoluntaryExitSignRequest(tt.args.request, tt.args.genesisValidatorsRoot) + got, err := v1.GetVoluntaryExitSignRequest(tt.args.request, tt.args.genesisValidatorsRoot) if (err != nil) != tt.wantErr { t.Errorf("GetVoluntaryExitSignRequest() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/validator/keymanager/remote-web3signer/v1/web3signer_types.go b/validator/keymanager/remote-web3signer/v1/web3signer_types.go index e3725b04cc..cfe51b289d 100644 --- a/validator/keymanager/remote-web3signer/v1/web3signer_types.go +++ b/validator/keymanager/remote-web3signer/v1/web3signer_types.go @@ -4,50 +4,50 @@ package v1 // AggregationSlotSignRequest is a request object for web3signer sign api. type AggregationSlotSignRequest struct { - Type string `json:"type"` - ForkInfo *ForkInfo `json:"fork_info"` + Type string `json:"type" validate:"required"` + ForkInfo *ForkInfo `json:"fork_info" validate:"required"` SigningRoot string `json:"signingRoot"` - AggregationSlot *AggregationSlot `json:"aggregation_slot"` + AggregationSlot *AggregationSlot `json:"aggregation_slot" validate:"required"` } // AggregationSlotSignRequest is a request object for web3signer sign api. type AggregateAndProofSignRequest struct { - Type string `json:"type"` - ForkInfo *ForkInfo `json:"fork_info"` + Type string `json:"type" validate:"required"` + ForkInfo *ForkInfo `json:"fork_info" validate:"required"` SigningRoot string `json:"signingRoot"` - AggregateAndProof *AggregateAndProof `json:"aggregation_and_proof"` + AggregateAndProof *AggregateAndProof `json:"aggregate_and_proof" validate:"required"` } // AttestationSignRequest is a request object for web3signer sign api. type AttestationSignRequest struct { - Type string `json:"type"` - ForkInfo *ForkInfo `json:"fork_info"` + Type string `json:"type" validate:"required"` + ForkInfo *ForkInfo `json:"fork_info" validate:"required"` SigningRoot string `json:"signingRoot"` - Attestation *AttestationData `json:"attestation"` + Attestation *AttestationData `json:"attestation" validate:"required"` } // BlockSignRequest is a request object for web3signer sign api. type BlockSignRequest struct { - Type string `json:"type"` - ForkInfo *ForkInfo `json:"fork_info"` + Type string `json:"type" validate:"required"` + ForkInfo *ForkInfo `json:"fork_info" validate:"required"` SigningRoot string `json:"signingRoot"` - Block *BeaconBlock `json:"block"` + Block *BeaconBlock `json:"block" validate:"required"` } // BlockV2AltairSignRequest is a request object for web3signer sign api. type BlockV2AltairSignRequest struct { - Type string `json:"type"` - ForkInfo *ForkInfo `json:"fork_info"` + Type string `json:"type" validate:"required"` + ForkInfo *ForkInfo `json:"fork_info" validate:"required"` SigningRoot string `json:"signingRoot"` - BeaconBlock *BeaconBlockAltairBlockV2 `json:"beacon_block"` + BeaconBlock *BeaconBlockAltairBlockV2 `json:"beacon_block" validate:"required"` } // BlockV2SignRequest is a request object for web3signer sign api. type BlockV2SignRequest struct { - Type string `json:"type"` - ForkInfo *ForkInfo `json:"fork_info"` + Type string `json:"type" validate:"required"` + ForkInfo *ForkInfo `json:"fork_info" validate:"required"` SigningRoot string `json:"signingRoot"` - BeaconBlock *BeaconBlockBlockV2 `json:"beacon_block"` + BeaconBlock *BeaconBlockBlockV2 `json:"beacon_block" validate:"required"` } // DepositSignRequest Not currently supported by Prysm. @@ -55,42 +55,42 @@ type BlockV2SignRequest struct { // RandaoRevealSignRequest is a request object for web3signer sign api. type RandaoRevealSignRequest struct { - Type string `json:"type"` - ForkInfo *ForkInfo `json:"fork_info"` + Type string `json:"type" validate:"required"` + ForkInfo *ForkInfo `json:"fork_info" validate:"required"` SigningRoot string `json:"signingRoot"` - RandaoReveal *RandaoReveal `json:"randao_reveal"` + RandaoReveal *RandaoReveal `json:"randao_reveal" validate:"required"` } // VoluntaryExitSignRequest is a request object for web3signer sign api. type VoluntaryExitSignRequest struct { - Type string `json:"type"` + Type string `json:"type" validate:"required"` ForkInfo *ForkInfo `json:"fork_info"` - SigningRoot string `json:"signingRoot"` - VoluntaryExit *VoluntaryExit `json:"voluntary_exit"` + SigningRoot string `json:"signingRoot" validate:"required"` + VoluntaryExit *VoluntaryExit `json:"voluntary_exit" validate:"required"` } // SyncCommitteeMessageSignRequest is a request object for web3signer sign api. type SyncCommitteeMessageSignRequest struct { - Type string `json:"type"` - ForkInfo *ForkInfo `json:"fork_info"` + Type string `json:"type" validate:"required"` + ForkInfo *ForkInfo `json:"fork_info" validate:"required"` SigningRoot string `json:"signingRoot"` - SyncCommitteeMessage *SyncCommitteeMessage `json:"sync_committee_message"` + SyncCommitteeMessage *SyncCommitteeMessage `json:"sync_committee_message" validate:"required"` } // SyncCommitteeSelectionProofSignRequest is a request object for web3signer sign api. type SyncCommitteeSelectionProofSignRequest struct { - Type string `json:"type"` - ForkInfo *ForkInfo `json:"fork_info"` + Type string `json:"type" validate:"required"` + ForkInfo *ForkInfo `json:"fork_info" validate:"required"` SigningRoot string `json:"signingRoot"` - SyncAggregatorSelectionData *SyncAggregatorSelectionData `json:"sync_aggregator_selection_data"` + SyncAggregatorSelectionData *SyncAggregatorSelectionData `json:"sync_aggregator_selection_data" validate:"required"` } // SyncCommitteeContributionAndProofSignRequest is a request object for web3signer sign api. type SyncCommitteeContributionAndProofSignRequest struct { - Type string `json:"type"` - ForkInfo *ForkInfo `json:"fork_info"` + Type string `json:"type" validate:"required"` + ForkInfo *ForkInfo `json:"fork_info" validate:"required"` SigningRoot string `json:"signingRoot"` - ContributionAndProof *ContributionAndProof `json:"contribution_and_proof"` + ContributionAndProof *ContributionAndProof `json:"contribution_and_proof" validate:"required"` } //////////////////////////////////////////////////////////////////////////////// @@ -313,8 +313,3 @@ type SyncCommitteeContribution struct { //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// - -// SignResponse the response object of the web3signer sign api. -type SignResponse struct { - Signature string `json:"signature"` -} diff --git a/validator/keymanager/types.go b/validator/keymanager/types.go index 8c42275206..bc1f0e4eae 100644 --- a/validator/keymanager/types.go +++ b/validator/keymanager/types.go @@ -71,6 +71,8 @@ const ( Derived // Remote keymanager capable of remote-signing data. Remote + // Web3Signer keymanager capable of signing data using a remote signer called Web3Signer. + Web3Signer ) // IncorrectPasswordErrMsg defines a common error string representing an EIP-2335 @@ -86,6 +88,8 @@ func (k Kind) String() string { return "direct" case Remote: return "remote" + case Web3Signer: + return "web3signer" default: return fmt.Sprintf("%d", int(k)) } @@ -100,6 +104,8 @@ func ParseKind(k string) (Kind, error) { return Imported, nil case "remote": return Remote, nil + case "web3signer": + return Web3Signer, nil default: return 0, fmt.Errorf("%s is not an allowed keymanager", k) } diff --git a/validator/node/BUILD.bazel b/validator/node/BUILD.bazel index fb065531da..895352abd0 100644 --- a/validator/node/BUILD.bazel +++ b/validator/node/BUILD.bazel @@ -35,6 +35,7 @@ go_library( "//cmd/validator/flags:go_default_library", "//config/features:go_default_library", "//config/params:go_default_library", + "//encoding/bytesutil:go_default_library", "//io/file:go_default_library", "//monitoring/backup:go_default_library", "//monitoring/prometheus:go_default_library", @@ -46,16 +47,16 @@ go_library( "//runtime/debug:go_default_library", "//runtime/prereqs:go_default_library", "//runtime/version:go_default_library", - "//validator/accounts/iface:go_default_library", "//validator/accounts/wallet:go_default_library", "//validator/client:go_default_library", "//validator/db/kv:go_default_library", "//validator/graffiti:go_default_library", - "//validator/keymanager:go_default_library", "//validator/keymanager/imported:go_default_library", + "//validator/keymanager/remote-web3signer:go_default_library", "//validator/rpc:go_default_library", "//validator/rpc/apimiddleware:go_default_library", "//validator/web:go_default_library", + "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", "@com_github_grpc_ecosystem_grpc_gateway_v2//runtime:go_default_library", "@com_github_pkg_errors//:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", diff --git a/validator/node/node.go b/validator/node/node.go index 25304dbe37..1be6a25762 100644 --- a/validator/node/node.go +++ b/validator/node/node.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "net/http" + "net/url" "os" "os/signal" "path/filepath" @@ -14,6 +15,7 @@ import ( "sync" "syscall" + "github.com/ethereum/go-ethereum/common/hexutil" gwruntime "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/api/gateway" @@ -23,6 +25,7 @@ import ( "github.com/prysmaticlabs/prysm/cmd/validator/flags" "github.com/prysmaticlabs/prysm/config/features" "github.com/prysmaticlabs/prysm/config/params" + "github.com/prysmaticlabs/prysm/encoding/bytesutil" "github.com/prysmaticlabs/prysm/io/file" "github.com/prysmaticlabs/prysm/monitoring/backup" "github.com/prysmaticlabs/prysm/monitoring/prometheus" @@ -34,13 +37,12 @@ import ( "github.com/prysmaticlabs/prysm/runtime/debug" "github.com/prysmaticlabs/prysm/runtime/prereqs" "github.com/prysmaticlabs/prysm/runtime/version" - accountsiface "github.com/prysmaticlabs/prysm/validator/accounts/iface" "github.com/prysmaticlabs/prysm/validator/accounts/wallet" "github.com/prysmaticlabs/prysm/validator/client" "github.com/prysmaticlabs/prysm/validator/db/kv" g "github.com/prysmaticlabs/prysm/validator/graffiti" - "github.com/prysmaticlabs/prysm/validator/keymanager" "github.com/prysmaticlabs/prysm/validator/keymanager/imported" + remote_web3signer "github.com/prysmaticlabs/prysm/validator/keymanager/remote-web3signer" "github.com/prysmaticlabs/prysm/validator/rpc" validatorMiddleware "github.com/prysmaticlabs/prysm/validator/rpc/apimiddleware" "github.com/prysmaticlabs/prysm/validator/web" @@ -65,6 +67,7 @@ type ValidatorClient struct { // NewValidatorClient creates a new instance of the Prysm validator client. func NewValidatorClient(cliCtx *cli.Context) (*ValidatorClient, error) { + // TODO(#9883) - Maybe we can pass in a new validator client config instead of the cliCTX to abstract away the use of flags here . if err := tracing2.Setup( "validator", // service name cliCtx.String(cmd.TracingProcessNameFlag.Name), @@ -107,6 +110,9 @@ func NewValidatorClient(cliCtx *cli.Context) (*ValidatorClient, error) { // If the --web flag is enabled to administer the validator // client via a web portal, we start the validator client in a different way. if cliCtx.IsSet(flags.EnableWebFlag.Name) { + if cliCtx.IsSet(flags.Web3SignerURLFlag.Name) || cliCtx.IsSet(flags.Web3SignerPublicValidatorKeysFlag.Name) { + return nil, errors.New("web3signer cannot be used with --web") + } log.Info("Enabling web portal to manage the validator client") if err := validatorClient.initializeForWeb(cliCtx); err != nil { return nil, err @@ -175,36 +181,31 @@ func (c *ValidatorClient) Close() { } func (c *ValidatorClient) initializeFromCLI(cliCtx *cli.Context) error { - var keyManager keymanager.IKeymanager var err error - if cliCtx.IsSet(flags.InteropNumValidators.Name) { - numValidatorKeys := cliCtx.Uint64(flags.InteropNumValidators.Name) - offset := cliCtx.Uint64(flags.InteropStartIndex.Name) - keyManager, err = imported.NewInteropKeymanager(cliCtx.Context, offset, numValidatorKeys) - if err != nil { - return errors.Wrap(err, "could not generate interop keys") - } - } else { - // Read the wallet from the specified path. - w, err := wallet.OpenWalletOrElseCli(cliCtx, func(cliCtx *cli.Context) (*wallet.Wallet, error) { - return nil, wallet.ErrNoWalletFound - }) - if err != nil { - return errors.Wrap(err, "could not open wallet") - } - c.wallet = w - log.WithFields(logrus.Fields{ - "wallet": w.AccountsDir(), - "keymanager-kind": w.KeymanagerKind().String(), - }).Info("Opened validator wallet") - keyManager, err = w.InitializeKeymanager(cliCtx.Context, accountsiface.InitKeymanagerConfig{ListenForChanges: true}) - if err != nil { - return errors.Wrap(err, "could not read keymanager for wallet") - } - } dataDir := cliCtx.String(flags.WalletDirFlag.Name) - if c.wallet != nil { - dataDir = c.wallet.AccountsDir() + if !cliCtx.IsSet(flags.InteropNumValidators.Name) { + // Custom Check For Web3Signer + if cliCtx.IsSet(flags.Web3SignerURLFlag.Name) || cliCtx.IsSet(flags.Web3SignerPublicValidatorKeysFlag.Name) { + if cliCtx.IsSet(flags.Web3SignerURLFlag.Name) && cliCtx.IsSet(flags.Web3SignerPublicValidatorKeysFlag.Name) { + c.wallet = wallet.NewWalletForWeb3Signer() + } else { + return errors.New("--validators-external-signer-url and --validators-external-signer-public-keys must be used together") + } + } else { + w, err := wallet.OpenWalletOrElseCli(cliCtx, func(cliCtx *cli.Context) (*wallet.Wallet, error) { + return nil, wallet.ErrNoWalletFound + }) + if err != nil { + return errors.Wrap(err, "could not open wallet") + } + c.wallet = w + // TODO(#9883) - Remove this when we have a better way to handle this. + log.WithFields(logrus.Fields{ + "wallet": w.AccountsDir(), + "keymanager-kind": w.KeymanagerKind().String(), + }).Info("Opened validator wallet") + dataDir = c.wallet.AccountsDir() + } } if cliCtx.String(cmd.DataDirFlag.Name) != cmd.DefaultDataDir() { dataDir = cliCtx.String(cmd.DataDirFlag.Name) @@ -252,11 +253,11 @@ func (c *ValidatorClient) initializeFromCLI(cliCtx *cli.Context) error { return err } } - if err := c.registerValidatorService(keyManager); err != nil { + if err := c.registerValidatorService(cliCtx); err != nil { return err } if cliCtx.Bool(flags.EnableRPCFlag.Name) { - if err := c.registerRPCService(cliCtx, keyManager); err != nil { + if err := c.registerRPCService(cliCtx); err != nil { return err } if err := c.registerRPCGatewayService(cliCtx); err != nil { @@ -267,7 +268,6 @@ func (c *ValidatorClient) initializeFromCLI(cliCtx *cli.Context) error { } func (c *ValidatorClient) initializeForWeb(cliCtx *cli.Context) error { - var keyManager keymanager.IKeymanager var err error // Read the wallet password file from the cli context. @@ -282,17 +282,7 @@ func (c *ValidatorClient) initializeForWeb(cliCtx *cli.Context) error { if err != nil { return errors.Wrap(err, "could not open wallet") } - if w != nil { - c.wallet = w - log.WithFields(logrus.Fields{ - "wallet": w.AccountsDir(), - "keymanager-kind": w.KeymanagerKind().String(), - }).Info("Opened validator wallet") - keyManager, err = w.InitializeKeymanager(cliCtx.Context, accountsiface.InitKeymanagerConfig{ListenForChanges: true}) - if err != nil { - return errors.Wrap(err, "could not read keymanager for wallet") - } - } + c.wallet = w dataDir := cliCtx.String(flags.WalletDirFlag.Name) if c.wallet != nil { dataDir = c.wallet.AccountsDir() @@ -336,10 +326,10 @@ func (c *ValidatorClient) initializeForWeb(cliCtx *cli.Context) error { return err } } - if err := c.registerValidatorService(keyManager); err != nil { + if err := c.registerValidatorService(cliCtx); err != nil { return err } - if err := c.registerRPCService(cliCtx, keyManager); err != nil { + if err := c.registerRPCService(cliCtx); err != nil { return err } if err := c.registerRPCGatewayService(cliCtx); err != nil { @@ -374,9 +364,7 @@ func (c *ValidatorClient) registerPrometheusService(cliCtx *cli.Context) error { return c.services.RegisterService(service) } -func (c *ValidatorClient) registerValidatorService( - keyManager keymanager.IKeymanager, -) error { +func (c *ValidatorClient) registerValidatorService(cliCtx *cli.Context) error { endpoint := c.cliCtx.String(flags.BeaconRPCProviderFlag.Name) dataDir := c.cliCtx.String(cmd.DataDirFlag.Name) logValidatorBalances := !c.cliCtx.Bool(flags.DisablePenaltyRewardLogFlag.Name) @@ -386,6 +374,14 @@ func (c *ValidatorClient) registerValidatorService( maxCallRecvMsgSize := c.cliCtx.Int(cmd.GrpcMaxCallRecvMsgSizeFlag.Name) grpcRetries := c.cliCtx.Uint(flags.GrpcRetriesFlag.Name) grpcRetryDelay := c.cliCtx.Duration(flags.GrpcRetryDelayFlag.Name) + var interopKeysConfig *imported.InteropKeymanagerConfig + if c.cliCtx.IsSet(flags.InteropNumValidators.Name) { + interopKeysConfig = &imported.InteropKeymanagerConfig{ + Offset: cliCtx.Uint64(flags.InteropStartIndex.Name), + NumValidatorKeys: cliCtx.Uint64(flags.InteropNumValidators.Name), + } + } + gStruct := &g.Graffiti{} var err error if c.cliCtx.IsSet(flags.GraffitiFileFlag.Name) { @@ -396,10 +392,14 @@ func (c *ValidatorClient) registerValidatorService( } } + wsc, err := web3SignerConfig(c.cliCtx) + if err != nil { + return err + } + v, err := client.NewValidatorService(c.cliCtx.Context, &client.Config{ Endpoint: endpoint, DataDir: dataDir, - KeyManager: keyManager, LogValidatorBalances: logValidatorBalances, EmitAccountMetrics: emitAccountMetrics, CertFlag: cert, @@ -410,9 +410,12 @@ func (c *ValidatorClient) registerValidatorService( GrpcHeadersFlag: c.cliCtx.String(flags.GrpcHeadersFlag.Name), ValDB: c.db, UseWeb: c.cliCtx.Bool(flags.EnableWebFlag.Name), + InteropKeysConfig: interopKeysConfig, + Wallet: c.wallet, WalletInitializedFeed: c.walletInitialized, GraffitiStruct: gStruct, LogDutyCountDown: c.cliCtx.Bool(flags.EnableDutyCountDown.Name), + Web3SignerConfig: wsc, }) if err != nil { return errors.Wrap(err, "could not initialize validator service") @@ -421,7 +424,41 @@ func (c *ValidatorClient) registerValidatorService( return c.services.RegisterService(v) } -func (c *ValidatorClient) registerRPCService(cliCtx *cli.Context, km keymanager.IKeymanager) error { +func web3SignerConfig(cliCtx *cli.Context) (*remote_web3signer.SetupConfig, error) { + var web3signerConfig *remote_web3signer.SetupConfig + if cliCtx.IsSet(flags.Web3SignerURLFlag.Name) && cliCtx.IsSet(flags.Web3SignerPublicValidatorKeysFlag.Name) { + urlStr := cliCtx.String(flags.Web3SignerURLFlag.Name) + publicKeysStr := cliCtx.String(flags.Web3SignerPublicValidatorKeysFlag.Name) + u, err := url.Parse(urlStr) + if err != nil { + return nil, errors.Wrapf(err, "web3signer url %s is invalid", urlStr) + } + web3signerConfig = &remote_web3signer.SetupConfig{ + BaseEndpoint: u.String(), + GenesisValidatorsRoot: nil, + } + u, err = url.Parse(publicKeysStr) + if err != nil { + return nil, errors.Wrapf(err, "could not parse %s as a URL for web3signer remote public keys", publicKeysStr) + } + if u != nil { + web3signerConfig.PublicKeysURL = publicKeysStr + } else { + var validatorKeys [][48]byte + for _, key := range strings.Split(publicKeysStr, ",") { + decodedKey, err := hexutil.Decode(key) + if err != nil { + return nil, errors.Wrapf(err, "could not decode public key for web3signer: %s", key) + } + validatorKeys = append(validatorKeys, bytesutil.ToBytes48(decodedKey)) + } + web3signerConfig.ProvidedPublicKeys = validatorKeys + } + } + return web3signerConfig, nil +} + +func (c *ValidatorClient) registerRPCService(cliCtx *cli.Context) error { var vs *client.ValidatorService if err := c.services.FetchService(&vs); err != nil { return err @@ -451,7 +488,6 @@ func (c *ValidatorClient) registerRPCService(cliCtx *cli.Context, km keymanager. NodeGatewayEndpoint: nodeGatewayEndpoint, WalletDir: walletDir, Wallet: c.wallet, - Keymanager: km, ValidatorGatewayHost: validatorGatewayHost, ValidatorGatewayPort: validatorGatewayPort, ValidatorMonitoringHost: validatorMonitoringHost, diff --git a/validator/rpc/BUILD.bazel b/validator/rpc/BUILD.bazel index d7b0b8abaa..05e040fc4c 100644 --- a/validator/rpc/BUILD.bazel +++ b/validator/rpc/BUILD.bazel @@ -38,7 +38,6 @@ go_library( "//proto/prysm/v1alpha1/validator-client:go_default_library", "//runtime/version:go_default_library", "//validator/accounts:go_default_library", - "//validator/accounts/iface:go_default_library", "//validator/accounts/petnames:go_default_library", "//validator/accounts/wallet:go_default_library", "//validator/client:go_default_library", @@ -103,6 +102,7 @@ go_test( "//testing/require:go_default_library", "//validator/accounts:go_default_library", "//validator/accounts/iface:go_default_library", + "//validator/accounts/testing:go_default_library", "//validator/accounts/wallet:go_default_library", "//validator/client:go_default_library", "//validator/db/kv:go_default_library", diff --git a/validator/rpc/accounts.go b/validator/rpc/accounts.go index 13bfcd653f..9eb8f9db57 100644 --- a/validator/rpc/accounts.go +++ b/validator/rpc/accounts.go @@ -23,6 +23,9 @@ import ( // ListAccounts allows retrieval of validating keys and their petnames // for a user's wallet via RPC. func (s *Server) ListAccounts(ctx context.Context, req *pb.ListAccountsRequest) (*pb.ListAccountsResponse, error) { + if s.validatorService == nil { + return nil, status.Error(codes.FailedPrecondition, "Validator service not yet initialized") + } if !s.walletInitialized { return nil, status.Error(codes.FailedPrecondition, "Wallet not yet initialized") } @@ -30,7 +33,11 @@ func (s *Server) ListAccounts(ctx context.Context, req *pb.ListAccountsRequest) return nil, status.Errorf(codes.InvalidArgument, "Requested page size %d can not be greater than max size %d", req.PageSize, cmd.Get().MaxRPCPageSize) } - keys, err := s.keymanager.FetchValidatingPublicKeys(ctx) + km, err := s.validatorService.Keymanager() + if err != nil { + return nil, err + } + keys, err := km.FetchValidatingPublicKeys(ctx) if err != nil { return nil, err } @@ -71,17 +78,23 @@ func (s *Server) ListAccounts(ctx context.Context, req *pb.ListAccountsRequest) func (s *Server) BackupAccounts( ctx context.Context, req *pb.BackupAccountsRequest, ) (*pb.BackupAccountsResponse, error) { + if s.validatorService == nil { + return nil, status.Error(codes.FailedPrecondition, "Validator service not yet initialized") + } if req.PublicKeys == nil || len(req.PublicKeys) < 1 { return nil, status.Error(codes.InvalidArgument, "No public keys specified to backup") } if req.BackupPassword == "" { return nil, status.Error(codes.InvalidArgument, "Backup password cannot be empty") } - if s.wallet == nil || s.keymanager == nil { - return nil, status.Error(codes.FailedPrecondition, "No wallet nor keymanager found") + + if s.wallet == nil { + return nil, status.Error(codes.FailedPrecondition, "No wallet found") } - if s.wallet.KeymanagerKind() != keymanager.Imported && s.wallet.KeymanagerKind() != keymanager.Derived { - return nil, status.Error(codes.FailedPrecondition, "Only HD or imported wallets can backup accounts") + var err error + km, err := s.validatorService.Keymanager() + if err != nil { + return nil, err } pubKeys := make([]bls.PublicKey, len(req.PublicKeys)) for i, key := range req.PublicKeys { @@ -92,27 +105,20 @@ func (s *Server) BackupAccounts( pubKeys[i] = pubKey } - var err error var keystoresToBackup []*keymanager.Keystore - switch s.wallet.KeymanagerKind() { - case keymanager.Imported: - km, ok := s.keymanager.(*imported.Keymanager) - if !ok { - return nil, status.Error(codes.FailedPrecondition, "Could not assert keymanager interface to concrete type") - } + switch km := km.(type) { + case *imported.Keymanager: keystoresToBackup, err = km.ExtractKeystores(ctx, pubKeys, req.BackupPassword) if err != nil { return nil, status.Errorf(codes.Internal, "Could not backup accounts for imported keymanager: %v", err) } - case keymanager.Derived: - km, ok := s.keymanager.(*derived.Keymanager) - if !ok { - return nil, status.Error(codes.FailedPrecondition, "Could not assert keymanager interface to concrete type") - } + case *derived.Keymanager: keystoresToBackup, err = km.ExtractKeystores(ctx, pubKeys, req.BackupPassword) if err != nil { return nil, status.Errorf(codes.Internal, "Could not backup accounts for derived keymanager: %v", err) } + default: + return nil, status.Error(codes.FailedPrecondition, "Only HD or imported wallets can backup accounts") } if len(keystoresToBackup) == 0 { return nil, status.Error(codes.InvalidArgument, "No keystores to backup") @@ -154,18 +160,22 @@ func (s *Server) BackupAccounts( func (s *Server) DeleteAccounts( ctx context.Context, req *pb.DeleteAccountsRequest, ) (*pb.DeleteAccountsResponse, error) { + if s.validatorService == nil { + return nil, status.Error(codes.FailedPrecondition, "Validator service not yet initialized") + } if req.PublicKeysToDelete == nil || len(req.PublicKeysToDelete) < 1 { return nil, status.Error(codes.InvalidArgument, "No public keys specified to delete") } - if s.wallet == nil || s.keymanager == nil { + if s.wallet == nil { return nil, status.Error(codes.FailedPrecondition, "No wallet found") } - if s.wallet.KeymanagerKind() != keymanager.Imported && s.wallet.KeymanagerKind() != keymanager.Derived { - return nil, status.Error(codes.FailedPrecondition, "Only Imported or Derived wallets can delete accounts") + km, err := s.validatorService.Keymanager() + if err != nil { + return nil, err } if err := accounts.DeleteAccount(ctx, &accounts.Config{ Wallet: s.wallet, - Keymanager: s.keymanager, + Keymanager: km, DeletePublicKeys: req.PublicKeysToDelete, }); err != nil { return nil, status.Errorf(codes.Internal, "Could not delete public keys: %v", err) @@ -179,16 +189,18 @@ func (s *Server) DeleteAccounts( func (s *Server) VoluntaryExit( ctx context.Context, req *pb.VoluntaryExitRequest, ) (*pb.VoluntaryExitResponse, error) { + if s.validatorService == nil { + return nil, status.Error(codes.FailedPrecondition, "Validator service not yet initialized") + } if len(req.PublicKeys) == 0 { return nil, status.Error(codes.InvalidArgument, "No public keys specified to delete") } - if s.wallet == nil || s.keymanager == nil { + if s.wallet == nil { return nil, status.Error(codes.FailedPrecondition, "No wallet found") } - if s.wallet.KeymanagerKind() != keymanager.Imported && s.wallet.KeymanagerKind() != keymanager.Derived { - return nil, status.Error( - codes.FailedPrecondition, "Only Imported or Derived wallets can submit voluntary exits", - ) + km, err := s.validatorService.Keymanager() + if err != nil { + return nil, err } formattedKeys := make([]string, len(req.PublicKeys)) for i, key := range req.PublicKeys { @@ -197,7 +209,7 @@ func (s *Server) VoluntaryExit( cfg := accounts.PerformExitCfg{ ValidatorClient: s.beaconNodeValidatorClient, NodeClient: s.beaconNodeClient, - Keymanager: s.keymanager, + Keymanager: km, RawPubKeys: req.PublicKeys, FormattedPubKeys: formattedKeys, } diff --git a/validator/rpc/accounts_test.go b/validator/rpc/accounts_test.go index 422e03132c..2ee06c4d08 100644 --- a/validator/rpc/accounts_test.go +++ b/validator/rpc/accounts_test.go @@ -21,7 +21,9 @@ import ( "github.com/prysmaticlabs/prysm/testing/require" "github.com/prysmaticlabs/prysm/validator/accounts" "github.com/prysmaticlabs/prysm/validator/accounts/iface" + mock "github.com/prysmaticlabs/prysm/validator/accounts/testing" "github.com/prysmaticlabs/prysm/validator/accounts/wallet" + "github.com/prysmaticlabs/prysm/validator/client" "github.com/prysmaticlabs/prysm/validator/keymanager" "github.com/prysmaticlabs/prysm/validator/keymanager/derived" constant "github.com/prysmaticlabs/prysm/validator/testing" @@ -48,10 +50,17 @@ func TestServer_ListAccounts(t *testing.T) { require.NoError(t, err) km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false}) require.NoError(t, err) + vs, err := client.NewValidatorService(ctx, &client.Config{ + Wallet: w, + Validator: &mock.MockValidator{ + Km: km, + }, + }) + require.NoError(t, err) s := &Server{ - keymanager: km, walletInitialized: true, wallet: w, + validatorService: vs, } numAccounts := 50 dr, ok := km.(*derived.Keymanager) @@ -113,10 +122,17 @@ func TestServer_BackupAccounts(t *testing.T) { require.NoError(t, err) km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false}) require.NoError(t, err) + vs, err := client.NewValidatorService(ctx, &client.Config{ + Wallet: w, + Validator: &mock.MockValidator{ + Km: km, + }, + }) + require.NoError(t, err) s := &Server{ - keymanager: km, walletInitialized: true, wallet: w, + validatorService: vs, } numAccounts := 50 dr, ok := km.(*derived.Keymanager) @@ -183,10 +199,17 @@ func TestServer_DeleteAccounts_FailedPreconditions_DerivedWallet(t *testing.T) { require.NoError(t, err) km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false}) require.NoError(t, err) + vs, err := client.NewValidatorService(ctx, &client.Config{ + Wallet: w, + Validator: &mock.MockValidator{ + Km: km, + }, + }) + require.NoError(t, err) s := &Server{ - keymanager: km, walletInitialized: true, wallet: w, + validatorService: vs, } numAccounts := 5 dr, ok := km.(*derived.Keymanager) @@ -198,8 +221,9 @@ func TestServer_DeleteAccounts_FailedPreconditions_DerivedWallet(t *testing.T) { PublicKeysToDelete: nil, }) assert.ErrorContains(t, "No public keys specified to delete", err) - - keys, err := s.keymanager.FetchValidatingPublicKeys(ctx) + ikm, err := s.validatorService.Keymanager() + require.NoError(t, err) + keys, err := ikm.FetchValidatingPublicKeys(ctx) require.NoError(t, err) _, err = s.DeleteAccounts(ctx, &pb.DeleteAccountsRequest{ PublicKeysToDelete: bytesutil.FromBytes48Array(keys), @@ -208,9 +232,31 @@ func TestServer_DeleteAccounts_FailedPreconditions_DerivedWallet(t *testing.T) { } func TestServer_DeleteAccounts_FailedPreconditions_NoWallet(t *testing.T) { - s := &Server{} ctx := context.Background() - _, err := s.DeleteAccounts(ctx, &pb.DeleteAccountsRequest{}) + localWalletDir := setupWalletDir(t) + defaultWalletPath = localWalletDir + w, err := accounts.CreateWalletWithKeymanager(ctx, &accounts.CreateWalletConfig{ + WalletCfg: &wallet.Config{ + WalletDir: defaultWalletPath, + KeymanagerKind: keymanager.Derived, + WalletPassword: strongPass, + }, + SkipMnemonicConfirm: true, + }) + require.NoError(t, err) + km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false}) + require.NoError(t, err) + vs, err := client.NewValidatorService(ctx, &client.Config{ + Wallet: w, + Validator: &mock.MockValidator{ + Km: km, + }, + }) + require.NoError(t, err) + s := &Server{ + validatorService: vs, + } + _, err = s.DeleteAccounts(ctx, &pb.DeleteAccountsRequest{}) assert.ErrorContains(t, "No public keys specified to delete", err) _, err = s.DeleteAccounts(ctx, &pb.DeleteAccountsRequest{ PublicKeysToDelete: make([][]byte, 1), @@ -221,7 +267,9 @@ func TestServer_DeleteAccounts_FailedPreconditions_NoWallet(t *testing.T) { func TestServer_DeleteAccounts_OK_ImportedWallet(t *testing.T) { s, pubKeys := createImportedWalletWithAccounts(t, 3) ctx := context.Background() - keys, err := s.keymanager.FetchValidatingPublicKeys(ctx) + ikm, err := s.validatorService.Keymanager() + require.NoError(t, err) + keys, err := ikm.FetchValidatingPublicKeys(ctx) require.NoError(t, err) require.Equal(t, len(pubKeys), len(keys)) @@ -230,11 +278,10 @@ func TestServer_DeleteAccounts_OK_ImportedWallet(t *testing.T) { PublicKeysToDelete: pubKeys[:1], // Delete the 0th public key }) require.NoError(t, err) - s.keymanager, err = s.wallet.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false}) + km, err := s.wallet.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false}) require.NoError(t, err) - // We expect one of the keys to have been deleted. - keys, err = s.keymanager.FetchValidatingPublicKeys(ctx) + keys, err = km.FetchValidatingPublicKeys(ctx) require.NoError(t, err) assert.Equal(t, len(pubKeys)-1, len(keys)) } @@ -288,12 +335,20 @@ func TestServer_VoluntaryExit(t *testing.T) { require.NoError(t, err) km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false}) require.NoError(t, err) + require.NoError(t, err) + vs, err := client.NewValidatorService(ctx, &client.Config{ + Wallet: w, + Validator: &mock.MockValidator{ + Km: km, + }, + }) + require.NoError(t, err) s := &Server{ - keymanager: km, walletInitialized: true, wallet: w, beaconNodeClient: mockNodeClient, beaconNodeValidatorClient: mockValidatorClient, + validatorService: vs, } numAccounts := 2 dr, ok := km.(*derived.Keymanager) diff --git a/validator/rpc/server.go b/validator/rpc/server.go index 2d14a4e9f0..a0a75ffb49 100644 --- a/validator/rpc/server.go +++ b/validator/rpc/server.go @@ -20,7 +20,6 @@ import ( "github.com/prysmaticlabs/prysm/validator/accounts/wallet" "github.com/prysmaticlabs/prysm/validator/client" "github.com/prysmaticlabs/prysm/validator/db" - "github.com/prysmaticlabs/prysm/validator/keymanager" "github.com/sirupsen/logrus" "go.opencensus.io/plugin/ocgrpc" "google.golang.org/grpc" @@ -52,7 +51,6 @@ type Config struct { WalletInitializedFeed *event.Feed NodeGatewayEndpoint string Wallet *wallet.Wallet - Keymanager keymanager.IKeymanager } // Server defining a gRPC server for the remote signer API. @@ -75,7 +73,6 @@ type Server struct { host string port string listener net.Listener - keymanager keymanager.IKeymanager withCert string withKey string credentialError error @@ -121,7 +118,6 @@ func NewServer(ctx context.Context, cfg *Config) *Server { walletInitializedFeed: cfg.WalletInitializedFeed, walletInitialized: cfg.Wallet != nil, wallet: cfg.Wallet, - keymanager: cfg.Keymanager, nodeGatewayEndpoint: cfg.NodeGatewayEndpoint, validatorMonitoringHost: cfg.ValidatorMonitoringHost, validatorMonitoringPort: cfg.ValidatorMonitoringPort, diff --git a/validator/rpc/standard_api.go b/validator/rpc/standard_api.go index ca9d25a64b..8a8011bbc1 100644 --- a/validator/rpc/standard_api.go +++ b/validator/rpc/standard_api.go @@ -25,7 +25,14 @@ func (s *Server) ListKeystores( if !s.walletInitialized { return nil, status.Error(codes.Internal, "Wallet not ready") } - pubKeys, err := s.keymanager.FetchValidatingPublicKeys(ctx) + if s.validatorService == nil { + return nil, status.Error(codes.Internal, "Validator service not ready") + } + km, err := s.validatorService.Keymanager() + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not get keymanager: %v", err) + } + pubKeys, err := km.FetchValidatingPublicKeys(ctx) if err != nil { return nil, status.Errorf(codes.Internal, "Could not list keystores: %v", err) } @@ -50,7 +57,14 @@ func (s *Server) ImportKeystores( if !s.walletInitialized { return nil, status.Error(codes.Internal, "Wallet not ready") } - importer, ok := s.keymanager.(keymanager.Importer) + if s.validatorService == nil { + return nil, status.Error(codes.Internal, "Validator service not ready") + } + km, err := s.validatorService.Keymanager() + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not get keymanager: %v", err) + } + importer, ok := km.(keymanager.Importer) if !ok { return nil, status.Error(codes.Internal, "Keymanager kind cannot import keys") } @@ -101,7 +115,14 @@ func (s *Server) DeleteKeystores( if !s.walletInitialized { return nil, status.Error(codes.Internal, "Wallet not ready") } - deleter, ok := s.keymanager.(keymanager.Deleter) + if s.validatorService == nil { + return nil, status.Error(codes.Internal, "Validator service not ready") + } + km, err := s.validatorService.Keymanager() + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not get keymanager: %v", err) + } + deleter, ok := km.(keymanager.Deleter) if !ok { return nil, status.Error(codes.Internal, "Keymanager kind cannot delete keys") } diff --git a/validator/rpc/standard_api_test.go b/validator/rpc/standard_api_test.go index e6f3230540..b9b92b9c67 100644 --- a/validator/rpc/standard_api_test.go +++ b/validator/rpc/standard_api_test.go @@ -17,7 +17,9 @@ import ( "github.com/prysmaticlabs/prysm/testing/require" "github.com/prysmaticlabs/prysm/validator/accounts" "github.com/prysmaticlabs/prysm/validator/accounts/iface" + mock "github.com/prysmaticlabs/prysm/validator/accounts/testing" "github.com/prysmaticlabs/prysm/validator/accounts/wallet" + "github.com/prysmaticlabs/prysm/validator/client" "github.com/prysmaticlabs/prysm/validator/db/kv" "github.com/prysmaticlabs/prysm/validator/keymanager" "github.com/prysmaticlabs/prysm/validator/keymanager/derived" @@ -47,11 +49,17 @@ func TestServer_ListKeystores(t *testing.T) { require.NoError(t, err) km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false}) require.NoError(t, err) - + vs, err := client.NewValidatorService(ctx, &client.Config{ + Wallet: w, + Validator: &mock.MockValidator{ + Km: km, + }, + }) + require.NoError(t, err) s := &Server{ - keymanager: km, walletInitialized: true, wallet: w, + validatorService: vs, } t.Run("no keystores found", func(t *testing.T) { @@ -104,11 +112,17 @@ func TestServer_ImportKeystores(t *testing.T) { require.NoError(t, err) km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false}) require.NoError(t, err) - + vs, err := client.NewValidatorService(ctx, &client.Config{ + Wallet: w, + Validator: &mock.MockValidator{ + Km: km, + }, + }) + require.NoError(t, err) s := &Server{ - keymanager: km, walletInitialized: true, wallet: w, + validatorService: vs, } t.Run("prevents importing if faulty keystore in request", func(t *testing.T) { _, err := s.ImportKeystores(context.Background(), ðpbservice.ImportKeystoresRequest{ @@ -216,7 +230,9 @@ func TestServer_DeleteKeystores(t *testing.T) { // We recover 3 accounts from a test mnemonic. numAccounts := 3 - dr, ok := srv.keymanager.(*derived.Keymanager) + km, er := srv.validatorService.Keymanager() + require.NoError(t, er) + dr, ok := km.(*derived.Keymanager) require.Equal(t, true, ok) err := dr.RecoverAccountsFromMnemonic(ctx, mocks.TestMnemonic, "", numAccounts) require.NoError(t, err) @@ -359,11 +375,18 @@ func setupServerWithWallet(t testing.TB) *Server { require.NoError(t, err) km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false}) require.NoError(t, err) + vs, err := client.NewValidatorService(ctx, &client.Config{ + Wallet: w, + Validator: &mock.MockValidator{ + Km: km, + }, + }) + require.NoError(t, err) return &Server{ - keymanager: km, walletInitialized: true, wallet: w, + validatorService: vs, } } diff --git a/validator/rpc/wallet.go b/validator/rpc/wallet.go index 8c119d11e2..1b7f3aa214 100644 --- a/validator/rpc/wallet.go +++ b/validator/rpc/wallet.go @@ -15,7 +15,6 @@ import ( "github.com/prysmaticlabs/prysm/io/prompt" pb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client" "github.com/prysmaticlabs/prysm/validator/accounts" - "github.com/prysmaticlabs/prysm/validator/accounts/iface" "github.com/prysmaticlabs/prysm/validator/accounts/wallet" "github.com/prysmaticlabs/prysm/validator/keymanager" "github.com/tyler-smith/go-bip39" @@ -53,6 +52,8 @@ func (s *Server) CreateWallet(ctx context.Context, req *pb.CreateWalletRequest) keymanagerKind = pb.KeymanagerKind_DERIVED case keymanager.Remote: keymanagerKind = pb.KeymanagerKind_REMOTE + case keymanager.Web3Signer: + keymanagerKind = pb.KeymanagerKind_WEB3SIGNER } return &pb.CreateWalletResponse{ Wallet: &pb.WalletResponse{ @@ -117,7 +118,7 @@ func (s *Server) WalletConfig(_ context.Context, _ *empty.Empty) (*pb.WalletResp return nil, status.Errorf(codes.FailedPrecondition, invalidWalletMsg) } - if s.wallet == nil || s.keymanager == nil { + if s.wallet == nil || s.validatorService == nil { // If no wallet is found, we simply return an empty response. return &pb.WalletResponse{}, nil } @@ -129,7 +130,10 @@ func (s *Server) WalletConfig(_ context.Context, _ *empty.Empty) (*pb.WalletResp keymanagerKind = pb.KeymanagerKind_IMPORTED case keymanager.Remote: keymanagerKind = pb.KeymanagerKind_REMOTE + case keymanager.Web3Signer: + keymanagerKind = pb.KeymanagerKind_WEB3SIGNER } + return &pb.WalletResponse{ WalletPath: s.walletDir, KeymanagerKind: keymanagerKind, @@ -258,7 +262,14 @@ func (s *Server) ImportAccounts( if s.wallet == nil { return nil, status.Error(codes.FailedPrecondition, "No wallet initialized") } - km, ok := s.keymanager.(keymanager.Importer) + if s.validatorService == nil { + return nil, status.Error(codes.FailedPrecondition, "No validator service initialized") + } + ikm, err := s.validatorService.Keymanager() + if err != nil { + return nil, status.Error(codes.FailedPrecondition, "No keymanager initialized") + } + km, ok := ikm.(keymanager.Importer) if !ok { return nil, status.Error(codes.FailedPrecondition, "Only imported wallets can import keystores") } @@ -285,7 +296,7 @@ func (s *Server) ImportAccounts( importedPubKeys[i] = pubKey } // Import the uploaded accounts. - _, err := accounts.ImportAccounts(ctx, &accounts.ImportAccountsConfig{ + statuses, err := accounts.ImportAccounts(ctx, &accounts.ImportAccountsConfig{ Importer: km, Keystores: keystores, AccountPassword: req.KeystoresPassword, @@ -293,6 +304,9 @@ func (s *Server) ImportAccounts( if err != nil { return nil, err } + if len(statuses) == 0 { + return nil, status.Error(codes.Internal, "No statuses returned from import") + } s.walletInitializedFeed.Send(s.wallet) return &pb.ImportAccountsResponse{ ImportedPublicKeys: importedPubKeys, @@ -331,22 +345,11 @@ func (s *Server) initializeWallet(ctx context.Context, cfg *wallet.Config) error } s.walletInitialized = true - km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: true}) - if err != nil { - return errors.Wrap(err, accounts.ErrCouldNotInitializeKeymanager) - } - s.keymanager = km s.wallet = w s.walletDir = cfg.WalletDir - // Only send over feed if we have validating keys. - validatingPublicKeys, err := km.FetchValidatingPublicKeys(ctx) - if err != nil { - return errors.Wrap(err, "could not check for validating public keys") - } - if len(validatingPublicKeys) > 0 { - s.walletInitializedFeed.Send(w) - } + s.walletInitializedFeed.Send(w) + return nil } diff --git a/validator/rpc/wallet_test.go b/validator/rpc/wallet_test.go index 51c28c9d5d..ece7dbd4f8 100644 --- a/validator/rpc/wallet_test.go +++ b/validator/rpc/wallet_test.go @@ -20,7 +20,9 @@ import ( "github.com/prysmaticlabs/prysm/testing/require" "github.com/prysmaticlabs/prysm/validator/accounts" "github.com/prysmaticlabs/prysm/validator/accounts/iface" + mock "github.com/prysmaticlabs/prysm/validator/accounts/testing" "github.com/prysmaticlabs/prysm/validator/accounts/wallet" + "github.com/prysmaticlabs/prysm/validator/client" "github.com/prysmaticlabs/prysm/validator/keymanager" "github.com/prysmaticlabs/prysm/validator/keymanager/imported" "github.com/tyler-smith/go-bip39" @@ -30,12 +32,31 @@ import ( const strongPass = "29384283xasjasd32%%&*@*#*" func TestServer_CreateWallet_Imported(t *testing.T) { + ctx := context.Background() localWalletDir := setupWalletDir(t) defaultWalletPath = localWalletDir - ctx := context.Background() + w, err := accounts.CreateWalletWithKeymanager(ctx, &accounts.CreateWalletConfig{ + WalletCfg: &wallet.Config{ + WalletDir: defaultWalletPath, + KeymanagerKind: keymanager.Derived, + WalletPassword: strongPass, + }, + SkipMnemonicConfirm: true, + }) + require.NoError(t, err) + km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false}) + require.NoError(t, err) + vs, err := client.NewValidatorService(ctx, &client.Config{ + Wallet: w, + Validator: &mock.MockValidator{ + Km: km, + }, + }) + require.NoError(t, err) s := &Server{ walletInitializedFeed: new(event.Feed), walletDir: defaultWalletPath, + validatorService: vs, } req := &pb.CreateWalletRequest{ Keymanager: pb.KeymanagerKind_IMPORTED, @@ -44,7 +65,7 @@ func TestServer_CreateWallet_Imported(t *testing.T) { // We delete the directory at defaultWalletPath as CreateWallet will return an error if it tries to create a wallet // where a directory already exists require.NoError(t, os.RemoveAll(defaultWalletPath)) - _, err := s.CreateWallet(ctx, req) + _, err = s.CreateWallet(ctx, req) require.NoError(t, err) importReq := &pb.ImportAccountsRequest{ @@ -301,7 +322,14 @@ func TestServer_WalletConfig(t *testing.T) { km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false}) require.NoError(t, err) s.wallet = w - s.keymanager = km + vs, err := client.NewValidatorService(ctx, &client.Config{ + Wallet: w, + Validator: &mock.MockValidator{ + Km: km, + }, + }) + require.NoError(t, err) + s.validatorService = vs resp, err := s.WalletConfig(ctx, &empty.Empty{}) require.NoError(t, err) @@ -326,8 +354,15 @@ func TestServer_ImportAccounts_FailedPreconditions(t *testing.T) { require.NoError(t, err) km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false}) require.NoError(t, err) + vs, err := client.NewValidatorService(ctx, &client.Config{ + Wallet: w, + Validator: &mock.MockValidator{ + Km: km, + }, + }) + require.NoError(t, err) ss := &Server{ - keymanager: km, + validatorService: vs, } _, err = ss.ImportAccounts(ctx, &pb.ImportAccountsRequest{}) assert.ErrorContains(t, "No wallet initialized", err) @@ -361,10 +396,17 @@ func TestServer_ImportAccounts_OK(t *testing.T) { require.NoError(t, err) km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false}) require.NoError(t, err) + vs, err := client.NewValidatorService(ctx, &client.Config{ + Wallet: w, + Validator: &mock.MockValidator{ + Km: km, + }, + }) + require.NoError(t, err) ss := &Server{ - keymanager: km, wallet: w, walletInitializedFeed: new(event.Feed), + validatorService: vs, } // Create 3 keystores. @@ -457,11 +499,19 @@ func createImportedWalletWithAccounts(t testing.TB, numAccounts int) (*Server, [ km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false}) require.NoError(t, err) + + vs, err := client.NewValidatorService(ctx, &client.Config{ + Wallet: w, + Validator: &mock.MockValidator{ + Km: km, + }, + }) + require.NoError(t, err) s := &Server{ - keymanager: km, wallet: w, walletDir: defaultWalletPath, walletInitializedFeed: new(event.Feed), + validatorService: vs, } // First we import accounts into the wallet. encryptor := keystorev4.New() @@ -492,7 +542,16 @@ func createImportedWalletWithAccounts(t testing.TB, numAccounts int) (*Server, [ KeystoresPassword: strongPass, }) require.NoError(t, err) - s.keymanager, err = s.wallet.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false}) + ikm, err := s.wallet.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false}) + require.NoError(t, err) + newVS, err := client.NewValidatorService(ctx, &client.Config{ + Wallet: s.wallet, + Validator: &mock.MockValidator{ + Km: ikm, + }, + }) + require.NoError(t, err) + s.validatorService = newVS require.NoError(t, err) return s, pubKeys }