From 6412a799983ef2c67ca1cf0268d9ed4ded2e4ea2 Mon Sep 17 00:00:00 2001 From: moshe-blox <89339422+moshe-blox@users.noreply.github.com> Date: Thu, 28 Mar 2024 16:27:18 +0200 Subject: [PATCH 1/5] fix: timings in attestation submission log (#1354) * fix: timings in attestation submission log * approve diffs * Add FieldSubmissionTime to logging/fields/fields.go and use it in attester.go * short numbers * 5 digits --- logging/fields/fields.go | 7 ++++++- protocol/v2/ssv/runner/attester.go | 5 ++++- scripts/spec-alignment/differ.config.yaml | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/logging/fields/fields.go b/logging/fields/fields.go index 94ca995621..bde9d90618 100644 --- a/logging/fields/fields.go +++ b/logging/fields/fields.go @@ -43,6 +43,7 @@ const ( FieldConfig = "config" FieldConnectionID = "connection_id" FieldConsensusTime = "consensus_time" + FieldSubmissionTime = "submission_time" FieldCount = "count" FieldTook = "took" FieldCurrentSlot = "current_slot" @@ -269,7 +270,11 @@ func Topic(val string) zap.Field { } func ConsensusTime(val time.Duration) zap.Field { - return zap.Float64(FieldConsensusTime, val.Seconds()) + return zap.String(FieldConsensusTime, strconv.FormatFloat(val.Seconds(), 'f', 5, 64)) +} + +func SubmissionTime(val time.Duration) zap.Field { + return zap.String(FieldSubmissionTime, strconv.FormatFloat(val.Seconds(), 'f', 5, 64)) } func DutyID(val string) zap.Field { diff --git a/protocol/v2/ssv/runner/attester.go b/protocol/v2/ssv/runner/attester.go index 0c0e4970af..9062db6300 100644 --- a/protocol/v2/ssv/runner/attester.go +++ b/protocol/v2/ssv/runner/attester.go @@ -167,8 +167,10 @@ func (r *AttesterRunner) ProcessPostConsensus(logger *zap.Logger, signedMsg *spe } attestationSubmissionEnd := r.metrics.StartBeaconSubmission() + consensusDuration := time.Since(r.started) // Submit it to the BN. + start := time.Now() if err := r.beacon.SubmitAttestation(signedAtt); err != nil { r.metrics.RoleSubmissionFailed() logger.Error("❌ failed to submit attestation", zap.Error(err)) @@ -181,7 +183,8 @@ func (r *AttesterRunner) ProcessPostConsensus(logger *zap.Logger, signedMsg *spe logger.Info("✅ successfully submitted attestation", zap.String("block_root", hex.EncodeToString(signedAtt.Data.BeaconBlockRoot[:])), - fields.ConsensusTime(time.Since(r.started)), + fields.ConsensusTime(consensusDuration), + fields.SubmissionTime(time.Since(start)), fields.Height(r.BaseRunner.QBFTController.Height), fields.Round(r.GetState().RunningInstance.State.Round)) } diff --git a/scripts/spec-alignment/differ.config.yaml b/scripts/spec-alignment/differ.config.yaml index e2d87d8ef6..b324d9e29d 100644 --- a/scripts/spec-alignment/differ.config.yaml +++ b/scripts/spec-alignment/differ.config.yaml @@ -13,7 +13,7 @@ ApprovedChanges: ["256a3dc0f1eb7abf","22b66e9a63ba145b","12c1c3a1622fb7cc","1c44 "b1c8e0148a4a755","2c25abb7c776bd54","a1754e08473bd1fa","4dbab14670fa155d","2a3667a499a23b16","930379d323dd95e8","65efe31656e8814f", "1270cef2e573f846","aeafb38ca9114f12","2a83e3384b45f2d7","91fbb874b3ce2570","74ad51ca63526e1e","defd8406641d53a5","efa21b9890ec787b", "6bd22f1a688bbca8","6b29645373ffac35","cfd236570c82c478","e2b0e9c6454c1c08","d5ddc708de23543a","11f8f0ea5709e42e","172a32c59d6f082e", - "63bbaffe74361d14"] + "63bbaffe74361d14","e783fe0d6b6643a"] IgnoredIdentifiers: - logger From a3db05a8423f3655a0b6050c8c603339f5a7287b Mon Sep 17 00:00:00 2001 From: Nikita Kryuchkov Date: Sun, 31 Mar 2024 17:38:47 +0400 Subject: [PATCH 2/5] Operator keys encapsulation (#1274) * Operator keys encapsulation * Fix log * Different hashes for storage and ekm * Refactoring * Code review request * Cleanup tests * Refactor * Further refactoring * Simplify field name * Extract reading file from keys package * Add tests * Move keystore enc/dec to separate package * deploy to stage nodes * Revert "deploy to stage nodes" This reverts commit 6f4778daf0c427a6177cb279d1545cd8ed445228. * Revert "Revert "deploy to stage nodes"" This reverts commit cd29980cbfc32425436730a05f5639d6da58e37f. * Debug logs * deploy * deploy log * backwards compatibility * revert deployments * Remove debug logs * fix: legacy hash calculation from keystores --------- Co-authored-by: olegshmuelov <45327364+olegshmuelov@users.noreply.github.com> Co-authored-by: y0sher Co-authored-by: moshe-blox --- cli/generate_operator_keys.go | 67 +++------ cli/operator/node.go | 125 +++++++++------ ekm/signer_key_manager_test.go | 22 +-- eth/ethtest/operator_added_test.go | 6 +- eth/ethtest/utils_test.go | 56 ++++--- eth/eventhandler/event_handler.go | 46 +++--- eth/eventhandler/event_handler_test.go | 95 ++++++------ eth/eventhandler/handlers.go | 11 +- eth/eventhandler/task_executor_test.go | 1 - eth/eventsyncer/event_syncer_test.go | 31 ++-- identity/store.go | 10 +- message/validation/errors.go | 2 +- message/validation/rsa.go | 29 +--- message/validation/validation.go | 8 +- message/validation/validation_test.go | 57 +++---- migrations/migration_2_encrypt_shares.go | 2 +- network/commons/keys.go | 6 +- network/p2p/config.go | 6 +- network/p2p/p2p.go | 6 +- network/p2p/p2p_pubsub.go | 7 +- network/p2p/p2p_setup.go | 2 +- network/p2p/test_utils.go | 15 +- network/peers/connections/filters.go | 16 +- network/peers/connections/handshaker.go | 40 ++--- network/peers/connections/handshaker_test.go | 20 +-- network/peers/connections/helpers_test.go | 56 +++---- .../connections/mock/mock_node_info_idx.go | 6 +- .../peers/connections/mock/mock_storage.go | 13 +- network/peers/index.go | 4 +- network/peers/peers_index.go | 13 +- network/records/handshake_data_test.go | 29 ---- network/records/handshake_signature.go | 5 +- network/records/signed_node_info_test.go | 34 +---- network/testing/keys.go | 16 +- operator/keys/keys.go | 142 ++++++++++++++++++ operator/keys/keys_test.go | 115 ++++++++++++++ operator/keystore/file.go | 51 +++++++ operator/keystore/file_test.go | 45 ++++++ operator/storage/storage.go | 112 ++------------ operator/storage/storage_test.go | 123 ++------------- operator/validator/controller.go | 33 ++-- operator/validator/controller_test.go | 29 ++-- utils/rsaencryption/rsa_encryption.go | 50 +----- utils/rsaencryption/rsa_encryption_test.go | 79 +++------- 44 files changed, 820 insertions(+), 821 deletions(-) delete mode 100644 network/records/handshake_data_test.go create mode 100644 operator/keys/keys.go create mode 100644 operator/keys/keys_test.go create mode 100644 operator/keystore/file.go create mode 100644 operator/keystore/file_test.go diff --git a/cli/generate_operator_keys.go b/cli/generate_operator_keys.go index 1a4c85fd16..d1bccb8b15 100644 --- a/cli/generate_operator_keys.go +++ b/cli/generate_operator_keys.go @@ -1,17 +1,16 @@ package cli import ( - "encoding/base64" - "encoding/json" + "github.com/bloxapp/ssv/operator/keystore" "log" "os" "path/filepath" - "github.com/bloxapp/ssv/logging" - "github.com/bloxapp/ssv/utils/rsaencryption" "github.com/spf13/cobra" - "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4" "go.uber.org/zap" + + "github.com/bloxapp/ssv/logging" + "github.com/bloxapp/ssv/operator/keys" ) var generateOperatorKeysCmd = &cobra.Command{ @@ -21,11 +20,12 @@ var generateOperatorKeysCmd = &cobra.Command{ if err := logging.SetGlobalLogger("debug", "capital", "console", nil); err != nil { log.Fatal(err) } + logger := zap.L().Named(logging.NameExportKeys) passwordFilePath, _ := cmd.Flags().GetString("password-file") privateKeyFilePath, _ := cmd.Flags().GetString("operator-key-file") - pk, sk, err := rsaencryption.GenerateKeys() + privKey, err := keys.GeneratePrivateKey() if err != nil { logger.Fatal("Failed to generate keys", zap.Error(err)) } @@ -35,21 +35,29 @@ var generateOperatorKeysCmd = &cobra.Command{ if err != nil { logger.Fatal("Failed to read private key from file", zap.Error(err)) } - sk, pk, err = parsePrivateKey(keyBytes) + + privKey, err = keys.PrivateKeyFromString(string(keyBytes)) if err != nil { - logger.Fatal("Failed to read private key from file", zap.Error(err)) + logger.Fatal("Failed to parse private key", zap.Error(err)) } } + pubKeyBase64, err := privKey.Public().Base64() + if err != nil { + logger.Fatal("Failed to get public key PEM", zap.Error(err)) + } + if passwordFilePath != "" { passwordBytes, err := readFile(passwordFilePath) if err != nil { logger.Fatal("Failed to read password file", zap.Error(err)) } - encryptedJSON, encryptedJSONErr := encryptPrivateKey(sk, pk, passwordBytes) + + encryptedJSON, encryptedJSONErr := keystore.EncryptKeystore(privKey.Bytes(), string(pubKeyBase64), string(passwordBytes)) if encryptedJSONErr != nil { logger.Fatal("Failed to encrypt private key", zap.Error(err)) } + err = writeFile("encrypted_private_key.json", encryptedJSON) if err != nil { logger.Fatal("Failed to save private key", zap.Error(err)) @@ -57,49 +65,12 @@ var generateOperatorKeysCmd = &cobra.Command{ logger.Info("private key encrypted and stored in encrypted_private_key.json") } } else { - logger.Info("generated public key (base64)", zap.String("pk", base64.StdEncoding.EncodeToString(pk))) - logger.Info("generated private key (base64)", zap.String("sk", base64.StdEncoding.EncodeToString(sk))) + logger.Info("generated public key (base64)", zap.String("pk", string(pubKeyBase64))) + logger.Info("generated private key (base64)", zap.String("sk", string(privKey.Base64()))) } }, } -func parsePrivateKey(keyBytes []byte) ([]byte, []byte, error) { - decodedBytes, err := base64.StdEncoding.DecodeString(string(keyBytes)) - if err != nil { - return nil, nil, err - } - rsaKey, err := rsaencryption.ConvertPemToPrivateKey(string(decodedBytes)) - if err != nil { - return nil, nil, err - } - - skPem := rsaencryption.PrivateKeyToByte(rsaKey) - - operatorPublicKey, err := rsaencryption.ExtractPublicKey(rsaKey) - if err != nil { - return nil, nil, err - } - pk, err := base64.StdEncoding.DecodeString(operatorPublicKey) - if err != nil { - return nil, nil, err - } - return skPem, pk, nil -} - -func encryptPrivateKey(sk []byte, pk []byte, passwordBytes []byte) ([]byte, error) { - encryptionPassword := string(passwordBytes) - encryptedData, err := keystorev4.New().Encrypt(sk, encryptionPassword) - if err != nil { - return nil, err - } - encryptedData["publicKey"] = base64.StdEncoding.EncodeToString(pk) - encryptedJSON, err := json.Marshal(encryptedData) - if err != nil { - return nil, err - } - return encryptedJSON, nil -} - func writeFile(fileName string, data []byte) error { return os.WriteFile(fileName, data, 0600) } diff --git a/cli/operator/node.go b/cli/operator/node.go index 93b4f0c7b1..762f431fdd 100644 --- a/cli/operator/node.go +++ b/cli/operator/node.go @@ -2,8 +2,6 @@ package operator import ( "context" - "crypto/rsa" - "crypto/x509" "encoding/base64" "fmt" "log" @@ -12,6 +10,8 @@ import ( "os" "time" + "github.com/bloxapp/ssv/operator/keystore" + "github.com/bloxapp/ssv/network" spectypes "github.com/bloxapp/ssv-spec/types" @@ -47,6 +47,7 @@ import ( "github.com/bloxapp/ssv/nodeprobe" "github.com/bloxapp/ssv/operator" "github.com/bloxapp/ssv/operator/duties/dutystore" + "github.com/bloxapp/ssv/operator/keys" "github.com/bloxapp/ssv/operator/slotticker" operatorstorage "github.com/bloxapp/ssv/operator/storage" "github.com/bloxapp/ssv/operator/validator" @@ -120,16 +121,52 @@ var StartNodeCmd = &cobra.Command{ logger.Fatal("could not setup db", zap.Error(err)) } - nodeStorage, operatorData := setupOperatorStorage(logger, db) + var operatorPrivKey keys.OperatorPrivateKey + var operatorPrivKeyText string + if cfg.KeyStore.PrivateKeyFile != "" { + // nolint: gosec + encryptedJSON, err := os.ReadFile(cfg.KeyStore.PrivateKeyFile) + if err != nil { + logger.Fatal("could not read PEM file", zap.Error(err)) + } + + // nolint: gosec + keyStorePassword, err := os.ReadFile(cfg.KeyStore.PasswordFile) + if err != nil { + logger.Fatal("could not read password file", zap.Error(err)) + } + + decryptedKeystore, err := keystore.DecryptKeystore(encryptedJSON, string(keyStorePassword)) + if err != nil { + logger.Fatal("could not decrypt operator private key keystore", zap.Error(err)) + } + operatorPrivKey, err = keys.PrivateKeyFromBytes(decryptedKeystore) + if err != nil { + logger.Fatal("could not extract operator private key from file", zap.Error(err)) + } + + operatorPrivKeyText = base64.StdEncoding.EncodeToString(decryptedKeystore) + } else { + operatorPrivKey, err = keys.PrivateKeyFromString(cfg.OperatorPrivateKey) + if err != nil { + logger.Fatal("could not decode operator private key", zap.Error(err)) + } + operatorPrivKeyText = cfg.OperatorPrivateKey + } + cfg.P2pNetworkConfig.OperatorSigner = operatorPrivKey + + nodeStorage, operatorData := setupOperatorStorage(logger, db, operatorPrivKey, operatorPrivKeyText) usingLocalEvents := len(cfg.LocalEventsPath) != 0 verifyConfig(logger, nodeStorage, networkConfig.Name, usingLocalEvents) - operatorKey, _, _ := nodeStorage.GetPrivateKey() - keyBytes := x509.MarshalPKCS1PrivateKey(operatorKey) - hashedKey, _ := rsaencryption.HashRsaKey(keyBytes) - keyManager, err := ekm.NewETHKeyManagerSigner(logger, db, networkConfig, cfg.SSVOptions.ValidatorOptions.BuilderProposals, hashedKey) + ekmHashedKey, err := operatorPrivKey.EKMHash() + if err != nil { + logger.Fatal("could not get operator private key hash", zap.Error(err)) + } + + keyManager, err := ekm.NewETHKeyManagerSigner(logger, db, networkConfig, cfg.SSVOptions.ValidatorOptions.BuilderProposals, ekmHashedKey) if err != nil { logger.Fatal("could not create new eth-key-manager signer", zap.Error(err)) } @@ -216,7 +253,6 @@ var StartNodeCmd = &cobra.Command{ cfg.SSVOptions.ValidatorOptions.KeyManager = keyManager cfg.SSVOptions.ValidatorOptions.ValidatorsMap = validatorsMap - cfg.SSVOptions.ValidatorOptions.ShareEncryptionKeyProvider = nodeStorage.GetPrivateKey cfg.SSVOptions.ValidatorOptions.OperatorData = operatorData cfg.SSVOptions.ValidatorOptions.RegistryStorage = nodeStorage cfg.SSVOptions.ValidatorOptions.RecipientsStorage = nodeStorage @@ -289,6 +325,7 @@ var StartNodeCmd = &cobra.Command{ metricsReporter, networkConfig, nodeStorage, + operatorPrivKey, ) nodeProber.AddNode("event syncer", eventSyncer) @@ -440,68 +477,61 @@ func setupDB(logger *zap.Logger, eth2Network beaconprotocol.Network) (*kv.Badger return db, nil } -func setupOperatorStorage(logger *zap.Logger, db basedb.Database) (operatorstorage.Storage, *registrystorage.OperatorData) { +func setupOperatorStorage(logger *zap.Logger, db basedb.Database, configPrivKey keys.OperatorPrivateKey, configPrivKeyText string) (operatorstorage.Storage, *registrystorage.OperatorData) { nodeStorage, err := operatorstorage.NewNodeStorage(logger, db) if err != nil { logger.Fatal("failed to create node storage", zap.Error(err)) } - if cfg.KeyStore.PrivateKeyFile != "" { - encryptedJSON, err := os.ReadFile(cfg.KeyStore.PrivateKeyFile) - if err != nil { - log.Fatal("Error reading PEM file", zap.Error(err)) - } - keyStorePassword, err := os.ReadFile(cfg.KeyStore.PasswordFile) - if err != nil { - log.Fatal("Error reading Password file", zap.Error(err)) - } - - privateKey, err := rsaencryption.ConvertEncryptedPemToPrivateKey(encryptedJSON, string(keyStorePassword)) - if err != nil { - logger.Fatal("could not decrypt operator private key", zap.Error(err)) - } - cfg.OperatorPrivateKey = rsaencryption.ExtractPrivateKey(privateKey) - } - cfg.P2pNetworkConfig.OperatorPrivateKey, err = decodePrivateKey(cfg.OperatorPrivateKey) + storedPrivKeyHash, found, err := nodeStorage.GetPrivateKeyHash() if err != nil { - logger.Fatal("could not decode operator private key", zap.Error(err)) + logger.Fatal("could not get hashed private key", zap.Error(err)) } - operatorPubKey, err := nodeStorage.SetupPrivateKey(cfg.OperatorPrivateKey) + configStoragePrivKeyHash, err := configPrivKey.StorageHash() if err != nil { - logger.Fatal("could not setup operator private key", zap.Error(err)) + logger.Fatal("could not hash private key", zap.Error(err)) } - _, found, err := nodeStorage.GetPrivateKey() - if err != nil || !found { - logger.Fatal("failed to get operator private key", zap.Error(err)) + // Backwards compatibility for the old hashing method, + // which was hashing the text from the configuration directly, + // whereas StorageHash re-encodes with PEM format. + cliPrivKeyDecoded, err := base64.StdEncoding.DecodeString(configPrivKeyText) + if err != nil { + logger.Fatal("could not decode private key", zap.Error(err)) } - var operatorData *registrystorage.OperatorData - operatorData, found, err = nodeStorage.GetOperatorDataByPubKey(nil, operatorPubKey) + configStoragePrivKeyLegacyHash, err := rsaencryption.HashRsaKey(cliPrivKeyDecoded) if err != nil { - logger.Fatal("could not get operator data by public key", zap.Error(err)) + logger.Fatal("could not hash private key", zap.Error(err)) } + if !found { - operatorData = ®istrystorage.OperatorData{ - PublicKey: operatorPubKey, + if err := nodeStorage.SavePrivateKeyHash(configStoragePrivKeyHash); err != nil { + logger.Fatal("could not save hashed private key", zap.Error(err)) } + } else if configStoragePrivKeyHash != storedPrivKeyHash && + configStoragePrivKeyLegacyHash != storedPrivKeyHash { + logger.Fatal("operator private key is not matching the one encrypted the storage") } - return nodeStorage, operatorData -} - -func decodePrivateKey(key string) (*rsa.PrivateKey, error) { - operatorKeyByte, err := base64.StdEncoding.DecodeString(key) + encodedPubKey, err := configPrivKey.Public().Base64() if err != nil { - return nil, err + logger.Fatal("could not encode public key", zap.Error(err)) } - sk, err := rsaencryption.ConvertPemToPrivateKey(string(operatorKeyByte)) + logger.Info("successfully loaded operator keys", zap.String("pubkey", string(encodedPubKey))) + + operatorData, found, err := nodeStorage.GetOperatorDataByPubKey(nil, encodedPubKey) if err != nil { - return nil, err + logger.Fatal("could not get operator data by public key", zap.Error(err)) + } + if !found { + operatorData = ®istrystorage.OperatorData{ + PublicKey: encodedPubKey, + } } - return sk, err + return nodeStorage, operatorData } func setupSSVNetwork(logger *zap.Logger) (networkconfig.NetworkConfig, error) { @@ -568,6 +598,7 @@ func setupEventHandling( metricsReporter metricsreporter.MetricsReporter, networkConfig networkconfig.NetworkConfig, nodeStorage operatorstorage.Storage, + operatorDecrypter keys.OperatorDecrypter, ) *eventsyncer.EventSyncer { eventFilterer, err := executionClient.Filterer() if err != nil { @@ -582,7 +613,7 @@ func setupEventHandling( validatorCtrl, networkConfig, validatorCtrl, - cfg.SSVOptions.ValidatorOptions.ShareEncryptionKeyProvider, + operatorDecrypter, cfg.SSVOptions.ValidatorOptions.KeyManager, cfg.SSVOptions.ValidatorOptions.Beacon, storageMap, diff --git a/ekm/signer_key_manager_test.go b/ekm/signer_key_manager_test.go index 34312c124a..5089b912bd 100644 --- a/ekm/signer_key_manager_test.go +++ b/ekm/signer_key_manager_test.go @@ -1,9 +1,6 @@ package ekm import ( - "crypto/rand" - "crypto/rsa" - "crypto/x509" "encoding/hex" "testing" @@ -23,9 +20,9 @@ import ( "github.com/bloxapp/ssv/logging" "github.com/bloxapp/ssv/networkconfig" + "github.com/bloxapp/ssv/operator/keys" "github.com/bloxapp/ssv/storage/basedb" "github.com/bloxapp/ssv/utils" - "github.com/bloxapp/ssv/utils/rsaencryption" "github.com/bloxapp/ssv/utils/threshold" ) @@ -68,10 +65,10 @@ func testKeyManager(t *testing.T, network *networkconfig.NetworkConfig) spectype func TestEncryptedKeyManager(t *testing.T) { // Generate key 1. - privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + privateKey, err := keys.GeneratePrivateKey() require.NoError(t, err) - keyBytes := x509.MarshalPKCS1PrivateKey(privateKey) - encryptionKey, err := rsaencryption.HashRsaKey(keyBytes) + + encryptionKey, err := privateKey.EKMHash() require.NoError(t, err) // Create account with key 1. @@ -82,31 +79,36 @@ func TestEncryptedKeyManager(t *testing.T) { logger := logging.TestLogger(t) db, err := getBaseStorage(logger) require.NoError(t, err) + signerStorage := NewSignerStorage(db, networkconfig.TestNetwork.Beacon.GetNetwork(), logger) err = signerStorage.SetEncryptionKey(encryptionKey) require.NoError(t, err) + defer func(db basedb.Database, logger *zap.Logger) { err := db.Close() if err != nil { t.Fatal(err) } }(db, logging.TestLogger(t)) + hdwallet := hd.NewWallet(&core.WalletContext{Storage: signerStorage}) require.NoError(t, signerStorage.SaveWallet(hdwallet)) + a, err := hdwallet.CreateValidatorAccountFromPrivateKey(sk.Serialize(), &index) require.NoError(t, err) // Load account with key 1 (should succeed). wallet, err := signerStorage.OpenWallet() require.NoError(t, err) + _, err = wallet.AccountByPublicKey(hex.EncodeToString(a.ValidatorPublicKey())) require.NoError(t, err) // Generate key 2. - privateKey2, err := rsa.GenerateKey(rand.Reader, 2048) + privateKey2, err := keys.GeneratePrivateKey() require.NoError(t, err) - keyBytes2 := x509.MarshalPKCS1PrivateKey(privateKey2) - encryptionKey2, err := rsaencryption.HashRsaKey(keyBytes2) + + encryptionKey2, err := privateKey2.EKMHash() require.NoError(t, err) // Load account with key 2 (should fail). diff --git a/eth/ethtest/operator_added_test.go b/eth/ethtest/operator_added_test.go index 9a173a5064..6b0df8533f 100644 --- a/eth/ethtest/operator_added_test.go +++ b/eth/ethtest/operator_added_test.go @@ -71,8 +71,12 @@ func (input *ProduceOperatorAddedEventsInput) produce() { for _, event := range input.events { op := event.op - packedOperatorPubKey, err := eventparser.PackOperatorPublicKey(op.rsaPub) + encodedPubKey, err := op.privateKey.Public().Base64() require.NoError(input.t, err) + + packedOperatorPubKey, err := eventparser.PackOperatorPublicKey(encodedPubKey) + require.NoError(input.t, err) + _, err = input.boundContract.SimcontractTransactor.RegisterOperator(event.auth, packedOperatorPubKey, big.NewInt(100_000_000)) require.NoError(input.t, err) diff --git a/eth/ethtest/utils_test.go b/eth/ethtest/utils_test.go index 7ec852c173..84978780b2 100644 --- a/eth/ethtest/utils_test.go +++ b/eth/ethtest/utils_test.go @@ -2,9 +2,6 @@ package ethtest import ( "context" - "crypto/rand" - "crypto/rsa" - "encoding/base64" "errors" "fmt" "math/big" @@ -24,6 +21,7 @@ import ( "github.com/bloxapp/ssv/eth/simulator" ibftstorage "github.com/bloxapp/ssv/ibft/storage" "github.com/bloxapp/ssv/networkconfig" + "github.com/bloxapp/ssv/operator/keys" operatorstorage "github.com/bloxapp/ssv/operator/storage" "github.com/bloxapp/ssv/operator/validator" "github.com/bloxapp/ssv/operator/validator/mocks" @@ -32,7 +30,6 @@ import ( "github.com/bloxapp/ssv/storage/basedb" "github.com/bloxapp/ssv/storage/kv" "github.com/bloxapp/ssv/utils/blskeygen" - "github.com/bloxapp/ssv/utils/rsaencryption" "github.com/bloxapp/ssv/utils/threshold" ) @@ -44,9 +41,8 @@ type testValidatorData struct { } type testOperator struct { - id uint64 - rsaPub []byte - rsaPriv []byte + id uint64 + privateKey keys.OperatorPrivateKey } type testShare struct { @@ -90,14 +86,14 @@ func createOperators(num uint64, idOffset uint64) ([]*testOperator, error) { testOps := make([]*testOperator, num) for i := uint64(1); i <= num; i++ { - pb, sk, err := rsaencryption.GenerateKeys() + privateKey, err := keys.GeneratePrivateKey() if err != nil { return nil, err } + testOps[i-1] = &testOperator{ - id: idOffset + i, - rsaPub: pb, - rsaPriv: sk, + id: idOffset + i, + privateKey: privateKey, } } @@ -109,25 +105,16 @@ func generateSharesData(validatorData *testValidatorData, operators []*testOpera var encryptedShares []byte for i, op := range operators { - rsaKey, err := rsaencryption.ConvertPemToPublicKey(op.rsaPub) - if err != nil { - return nil, fmt.Errorf("can't convert public key: %w", err) - } - rawShare := validatorData.operatorsShares[i].sec.SerializeToHexStr() - cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, rsaKey, []byte(rawShare)) - if err != nil { - return nil, fmt.Errorf("can't encrypt share: %w", err) - } - rsaPriv, err := rsaencryption.ConvertPemToPrivateKey(string(op.rsaPriv)) + cipherText, err := op.privateKey.Public().Encrypt([]byte(rawShare)) if err != nil { - return nil, fmt.Errorf("can't convert secret key to a private key share: %w", err) + return nil, fmt.Errorf("can't encrypt share: %w", err) } // check that we encrypt right shareSecret := &bls.SecretKey{} - decryptedSharePrivateKey, err := rsaencryption.DecodeKey(rsaPriv, cipherText) + decryptedSharePrivateKey, err := op.privateKey.Decrypt(cipherText) if err != nil { return nil, err } @@ -198,7 +185,7 @@ func setupEventHandler( validatorCtrl, testNetworkConfig, validatorCtrl, - nodeStorage.GetPrivateKey, + operator.privateKey, keyManager, bc, storageMap, @@ -212,6 +199,7 @@ func setupEventHandler( validatorCtrl.EXPECT().GetOperatorData().Return(operatorData).AnyTimes() validatorCtrl.EXPECT().GetOperatorID().Return(operatorData.ID).AnyTimes() + validatorCtrl.EXPECT().SetOperatorData(gomock.Any()).AnyTimes() return eh, validatorCtrl, ctrl, nodeStorage, nil } @@ -233,7 +221,7 @@ func setupEventHandler( validatorCtrl, testNetworkConfig, validatorCtrl, - nodeStorage.GetPrivateKey, + operator.privateKey, keyManager, bc, storageMap, @@ -262,24 +250,32 @@ func setupOperatorStorage( logger.Fatal("failed to create node storage", zap.Error(err)) } - operatorPubKey, err := nodeStorage.SetupPrivateKey(base64.StdEncoding.EncodeToString(operator.rsaPriv)) + encodedPubKey, err := operator.privateKey.Public().Base64() + if err != nil { + logger.Fatal("failed to encode operator public key", zap.Error(err)) + } + + privKeyHash, err := operator.privateKey.StorageHash() if err != nil { + logger.Fatal("failed to encode operator private key", zap.Error(err)) + } + + if err := nodeStorage.SavePrivateKeyHash(privKeyHash); err != nil { logger.Fatal("couldn't setup operator private key", zap.Error(err)) } - _, found, err := nodeStorage.GetPrivateKey() + _, found, err := nodeStorage.GetPrivateKeyHash() if err != nil || !found { logger.Fatal("failed to get operator private key", zap.Error(err)) } - var operatorData *registrystorage.OperatorData - operatorData, found, err = nodeStorage.GetOperatorDataByPubKey(nil, operatorPubKey) + operatorData, found, err := nodeStorage.GetOperatorDataByPubKey(nil, encodedPubKey) if err != nil { logger.Fatal("couldn't get operator data by public key", zap.Error(err)) } if !found { operatorData = ®istrystorage.OperatorData{ - PublicKey: operatorPubKey, + PublicKey: encodedPubKey, ID: operator.id, OwnerAddress: *ownerAddress, } diff --git a/eth/eventhandler/event_handler.go b/eth/eventhandler/event_handler.go index 438893073d..a6804fd02c 100644 --- a/eth/eventhandler/event_handler.go +++ b/eth/eventhandler/event_handler.go @@ -3,7 +3,6 @@ package eventhandler import ( - "crypto/rsa" "errors" "fmt" "math/big" @@ -22,6 +21,7 @@ import ( qbftstorage "github.com/bloxapp/ssv/ibft/storage" "github.com/bloxapp/ssv/logging/fields" "github.com/bloxapp/ssv/networkconfig" + "github.com/bloxapp/ssv/operator/keys" nodestorage "github.com/bloxapp/ssv/operator/storage" beaconprotocol "github.com/bloxapp/ssv/protocol/v2/blockchain/beacon" ssvtypes "github.com/bloxapp/ssv/protocol/v2/types" @@ -56,8 +56,6 @@ type taskExecutor interface { ExitValidator(pubKey phase0.BLSPubKey, blockNumber uint64, validatorIndex phase0.ValidatorIndex) error } -type ShareEncryptionKeyProvider = func() (*rsa.PrivateKey, bool, error) - type OperatorData interface { GetOperatorData() *storage.OperatorData SetOperatorData(*storage.OperatorData) @@ -65,15 +63,15 @@ type OperatorData interface { } type EventHandler struct { - nodeStorage nodestorage.Storage - taskExecutor taskExecutor - eventParser eventparser.Parser - networkConfig networkconfig.NetworkConfig - operatorData OperatorData - shareEncryptionKeyProvider ShareEncryptionKeyProvider - keyManager spectypes.KeyManager - beacon beaconprotocol.BeaconNode - storageMap *qbftstorage.QBFTStores + nodeStorage nodestorage.Storage + taskExecutor taskExecutor + eventParser eventparser.Parser + networkConfig networkconfig.NetworkConfig + operatorData OperatorData + operatorDecrypter keys.OperatorDecrypter + keyManager spectypes.KeyManager + beacon beaconprotocol.BeaconNode + storageMap *qbftstorage.QBFTStores fullNode bool logger *zap.Logger @@ -86,24 +84,24 @@ func New( taskExecutor taskExecutor, networkConfig networkconfig.NetworkConfig, operatorData OperatorData, - shareEncryptionKeyProvider ShareEncryptionKeyProvider, + operatorDecrypter keys.OperatorDecrypter, keyManager spectypes.KeyManager, beacon beaconprotocol.BeaconNode, storageMap *qbftstorage.QBFTStores, opts ...Option, ) (*EventHandler, error) { eh := &EventHandler{ - nodeStorage: nodeStorage, - taskExecutor: taskExecutor, - eventParser: eventParser, - networkConfig: networkConfig, - operatorData: operatorData, - shareEncryptionKeyProvider: shareEncryptionKeyProvider, - keyManager: keyManager, - beacon: beacon, - storageMap: storageMap, - logger: zap.NewNop(), - metrics: nopMetrics{}, + nodeStorage: nodeStorage, + taskExecutor: taskExecutor, + eventParser: eventParser, + networkConfig: networkConfig, + operatorData: operatorData, + operatorDecrypter: operatorDecrypter, + keyManager: keyManager, + beacon: beacon, + storageMap: storageMap, + logger: zap.NewNop(), + metrics: nopMetrics{}, } for _, opt := range opts { diff --git a/eth/eventhandler/event_handler_test.go b/eth/eventhandler/event_handler_test.go index 6b27e33846..90f06a6f7f 100644 --- a/eth/eventhandler/event_handler_test.go +++ b/eth/eventhandler/event_handler_test.go @@ -3,9 +3,6 @@ package eventhandler import ( "bytes" "context" - "crypto/rand" - "crypto/rsa" - "encoding/base64" "encoding/json" "fmt" "math/big" @@ -37,6 +34,7 @@ import ( "github.com/bloxapp/ssv/eth/simulator/simcontract" ibftstorage "github.com/bloxapp/ssv/ibft/storage" "github.com/bloxapp/ssv/networkconfig" + "github.com/bloxapp/ssv/operator/keys" operatorstorage "github.com/bloxapp/ssv/operator/storage" "github.com/bloxapp/ssv/operator/validator" "github.com/bloxapp/ssv/operator/validator/mocks" @@ -47,7 +45,6 @@ import ( "github.com/bloxapp/ssv/storage/kv" "github.com/bloxapp/ssv/utils" "github.com/bloxapp/ssv/utils/blskeygen" - "github.com/bloxapp/ssv/utils/rsaencryption" "github.com/bloxapp/ssv/utils/threshold" ) @@ -149,10 +146,12 @@ func TestHandleBlockEventsStream(t *testing.T) { currentSlot.SetSlot(100) t.Run("test OperatorAdded event handle", func(t *testing.T) { - for _, op := range ops { + encodedPubKey, err := op.privateKey.Public().Base64() + require.NoError(t, err) + // Call the contract method - packedOperatorPubKey, err := eventparser.PackOperatorPublicKey(op.rsaPub) + packedOperatorPubKey, err := eventparser.PackOperatorPublicKey(encodedPubKey) require.NoError(t, err) _, err = boundContract.SimcontractTransactor.RegisterOperator(auth, packedOperatorPubKey, big.NewInt(100_000_000)) require.NoError(t, err) @@ -190,11 +189,16 @@ func TestHandleBlockEventsStream(t *testing.T) { for i, log := range block.Logs { operatorAddedEvent, err := contractFilterer.ParseOperatorAdded(log) require.NoError(t, err) + data, _, err := eh.nodeStorage.GetOperatorData(nil, operatorAddedEvent.OperatorId) require.NoError(t, err) require.Equal(t, operatorAddedEvent.OperatorId, data.ID) require.Equal(t, operatorAddedEvent.Owner, data.OwnerAddress) - require.Equal(t, ops[i].rsaPub, data.PublicKey) + + encodedPubKey, err := ops[i].privateKey.Public().Base64() + require.NoError(t, err) + + require.Equal(t, encodedPubKey, data.PublicKey) } }) @@ -242,8 +246,11 @@ func TestHandleBlockEventsStream(t *testing.T) { require.NoError(t, err) operatorsCount++ + encodedPubKey, err := op[0].privateKey.Public().Base64() + require.NoError(t, err) + // Call the contract method - packedOperatorPubKey, err := eventparser.PackOperatorPublicKey(op[0].rsaPub) + packedOperatorPubKey, err := eventparser.PackOperatorPublicKey(encodedPubKey) require.NoError(t, err) _, err = boundContract.SimcontractTransactor.RegisterOperator(auth, packedOperatorPubKey, big.NewInt(100_000_000)) require.NoError(t, err) @@ -1088,8 +1095,11 @@ func TestHandleBlockEventsStream(t *testing.T) { operatorsCount++ op := tmpOps[0] + encodedPubKey, err := op.privateKey.Public().Base64() + require.NoError(t, err) + // Call the RegisterOperator contract method - packedOperatorPubKey, err := eventparser.PackOperatorPublicKey(op.rsaPub) + packedOperatorPubKey, err := eventparser.PackOperatorPublicKey(encodedPubKey) require.NoError(t, err) _, err = boundContract.SimcontractTransactor.RegisterOperator(auth, packedOperatorPubKey, big.NewInt(100_000_000)) require.NoError(t, err) @@ -1301,7 +1311,7 @@ func setupEventHandler(t *testing.T, ctx context.Context, logger *zap.Logger, ne validatorCtrl, *network, validatorCtrl, - nodeStorage.GetPrivateKey, + operator.privateKey, keyManager, bc, storageMap, @@ -1341,7 +1351,7 @@ func setupEventHandler(t *testing.T, ctx context.Context, logger *zap.Logger, ne validatorCtrl, *network, validatorCtrl, - nodeStorage.GetPrivateKey, + operator.privateKey, keyManager, bc, storageMap, @@ -1363,24 +1373,36 @@ func setupOperatorStorage(logger *zap.Logger, db basedb.Database, operator *test logger.Fatal("failed to create node storage", zap.Error(err)) } - operatorPubKey, err := nodeStorage.SetupPrivateKey(base64.StdEncoding.EncodeToString(operator.rsaPriv)) + encodedPrivKey, err := operator.privateKey.StorageHash() + if err != nil { + logger.Fatal("failed to encode operator private key", zap.Error(err)) + } + + encodedPubKey, err := operator.privateKey.Public().Base64() if err != nil { + logger.Fatal("failed to encode operator public key", zap.Error(err)) + } + + if err := nodeStorage.SavePrivateKeyHash(encodedPrivKey); err != nil { logger.Fatal("couldn't setup operator private key", zap.Error(err)) } - _, found, err := nodeStorage.GetPrivateKey() + _, found, err := nodeStorage.GetPrivateKeyHash() if err != nil || !found { logger.Fatal("failed to get operator private key", zap.Error(err)) } - var operatorData *registrystorage.OperatorData - operatorData, found, err = nodeStorage.GetOperatorDataByPubKey(nil, operatorPubKey) + + operatorData, found, err := nodeStorage.GetOperatorDataByPubKey(nil, encodedPubKey) + if err != nil { + logger.Fatal("couldn't get operator data by public key", zap.Error(err)) + } if err != nil { logger.Fatal("couldn't get operator data by public key", zap.Error(err)) } if !found { operatorData = ®istrystorage.OperatorData{ - PublicKey: operatorPubKey, + PublicKey: encodedPubKey, ID: operator.id, OwnerAddress: testAddr, } @@ -1419,10 +1441,11 @@ func TestCreatingSharesData(t *testing.T) { validatorData, err := createNewValidator(ops) require.NoError(t, err) + // TODO: maybe we can merge createNewValidator and generateSharesData sharesData, err := generateSharesData(validatorData, ops, owner, nonce) - require.NoError(t, err) + operatorCount := len(ops) signatureOffset := phase0.SignatureLength pubKeysOffset := phase0.PublicKeyLength*operatorCount + signatureOffset @@ -1438,11 +1461,10 @@ func TestCreatingSharesData(t *testing.T) { sharePublicKeys := splitBytes(sharesData[signatureOffset:pubKeysOffset], phase0.PublicKeyLength) encryptedKeys := splitBytes(sharesData[pubKeysOffset:], len(sharesData[pubKeysOffset:])/operatorCount) - for i, enck := range encryptedKeys { - priv, err := rsaencryption.ConvertPemToPrivateKey(string(ops[i].rsaPriv)) - require.NoError(t, err) - decryptedSharePrivateKey, err := rsaencryption.DecodeKey(priv, enck) + for i, encryptedKey := range encryptedKeys { + decryptedSharePrivateKey, err := ops[i].privateKey.Decrypt(encryptedKey) require.NoError(t, err) + share := &bls.SecretKey{} require.NoError(t, share.SetHexString(string(decryptedSharePrivateKey))) @@ -1460,9 +1482,8 @@ type testValidatorData struct { } type testOperator struct { - id uint64 - rsaPub []byte - rsaPriv []byte + id uint64 + privateKey keys.OperatorPrivateKey } type testShare struct { @@ -1515,14 +1536,14 @@ func createOperators(num uint64, idOffset uint64) ([]*testOperator, error) { testOps := make([]*testOperator, num) for i := uint64(1); i <= num; i++ { - pb, sk, err := rsaencryption.GenerateKeys() + privateKey, err := keys.GeneratePrivateKey() if err != nil { return nil, err } + testOps[i-1] = &testOperator{ - id: idOffset + i, - rsaPub: pb, - rsaPriv: sk, + id: idOffset + i, + privateKey: privateKey, } } @@ -1534,35 +1555,25 @@ func generateSharesData(validatorData *testValidatorData, operators []*testOpera var encryptedShares []byte for i, op := range operators { - rsaKey, err := rsaencryption.ConvertPemToPublicKey(op.rsaPub) - if err != nil { - return nil, fmt.Errorf("can't convert public key: %w", err) - } - rawShare := validatorData.operatorsShares[i].sec.SerializeToHexStr() - cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, rsaKey, []byte(rawShare)) + cipherText, err := op.privateKey.Public().Encrypt([]byte(rawShare)) if err != nil { return nil, fmt.Errorf("can't encrypt share: %w", err) } - rsaPriv, err := rsaencryption.ConvertPemToPrivateKey(string(op.rsaPriv)) - if err != nil { - return nil, fmt.Errorf("can't convert secret key to a private key share: %w", err) - } - // check that we encrypt right - shareSecret := &bls.SecretKey{} - decryptedSharePrivateKey, err := rsaencryption.DecodeKey(rsaPriv, cipherText) + decryptedSharePrivateKey, err := op.privateKey.Decrypt(cipherText) if err != nil { return nil, err } + + shareSecret := &bls.SecretKey{} if err = shareSecret.SetHexString(string(decryptedSharePrivateKey)); err != nil { return nil, err } pubKeys = append(pubKeys, validatorData.operatorsShares[i].pub.Serialize()...) encryptedShares = append(encryptedShares, cipherText...) - } toSign := fmt.Sprintf("%s:%d", owner.String(), nonce) diff --git a/eth/eventhandler/handlers.go b/eth/eventhandler/handlers.go index 34e3214a4a..1edf4ba03f 100644 --- a/eth/eventhandler/handlers.go +++ b/eth/eventhandler/handlers.go @@ -20,7 +20,6 @@ import ( ssvtypes "github.com/bloxapp/ssv/protocol/v2/types" registrystorage "github.com/bloxapp/ssv/registry/storage" "github.com/bloxapp/ssv/storage/basedb" - "github.com/bloxapp/ssv/utils/rsaencryption" ) // b64 encrypted key length is 256 @@ -288,16 +287,8 @@ func (eh *EventHandler) validatorAddedEventToShare( validatorShare.OperatorID = operatorID validatorShare.SharePubKey = sharePublicKeys[i] - operatorPrivateKey, found, err := eh.shareEncryptionKeyProvider() - if err != nil { - return nil, nil, fmt.Errorf("could not get operator private key: %w", err) - } - if !found { - return nil, nil, errors.New("could not find operator private key") - } - shareSecret = &bls.SecretKey{} - decryptedSharePrivateKey, err := rsaencryption.DecodeKey(operatorPrivateKey, encryptedKeys[i]) + decryptedSharePrivateKey, err := eh.operatorDecrypter.Decrypt(encryptedKeys[i]) if err != nil { return nil, nil, &MalformedEventError{ Err: fmt.Errorf("could not decrypt share private key: %w", err), diff --git a/eth/eventhandler/task_executor_test.go b/eth/eventhandler/task_executor_test.go index a735c53dc9..098c33e3d7 100644 --- a/eth/eventhandler/task_executor_test.go +++ b/eth/eventhandler/task_executor_test.go @@ -189,7 +189,6 @@ func TestHandleBlockEventsStreamWithExecution(t *testing.T) { observedLogsFlow = append(observedLogsFlow, entry.Message) } happyFlow := []string{ - "successfully setup operator keys", "setting up validator controller", "malformed event: failed to verify signature", "processed events from block", diff --git a/eth/eventsyncer/event_syncer_test.go b/eth/eventsyncer/event_syncer_test.go index 76b9e4c1fd..f0c29043de 100644 --- a/eth/eventsyncer/event_syncer_test.go +++ b/eth/eventsyncer/event_syncer_test.go @@ -11,6 +11,7 @@ import ( "github.com/bloxapp/ssv/eth/contract" "github.com/bloxapp/ssv/eth/simulator" + "github.com/bloxapp/ssv/operator/keys" "github.com/bloxapp/ssv/operator/validatorsmap" "github.com/bloxapp/ssv/utils/rsaencryption" @@ -136,7 +137,13 @@ func setupEventHandler(t *testing.T, ctx context.Context, logger *zap.Logger) *e require.NoError(t, err) storageMap := ibftstorage.NewStores() - nodeStorage, operatorData := setupOperatorStorage(logger, db) + + privateKey, err := keys.GeneratePrivateKey() + if err != nil { + logger.Fatal("failed generating operator key %v", zap.Error(err)) + } + + nodeStorage, operatorData := setupOperatorStorage(logger, db, privateKey) testNetworkConfig := networkconfig.TestNetwork keyManager, err := ekm.NewETHKeyManagerSigner(logger, db, testNetworkConfig, true, "") @@ -167,7 +174,7 @@ func setupEventHandler(t *testing.T, ctx context.Context, logger *zap.Logger) *e validatorCtrl, testNetworkConfig, validatorCtrl, - nodeStorage.GetPrivateKey, + privateKey, keyManager, bc, storageMap, @@ -188,32 +195,38 @@ func simTestBackend(testAddr ethcommon.Address) *simulator.SimulatedBackend { ) } -func setupOperatorStorage(logger *zap.Logger, db basedb.Database) (operatorstorage.Storage, *registrystorage.OperatorData) { +func setupOperatorStorage(logger *zap.Logger, db basedb.Database, privKey keys.OperatorPrivateKey) (operatorstorage.Storage, *registrystorage.OperatorData) { nodeStorage, err := operatorstorage.NewNodeStorage(logger, db) if err != nil { logger.Fatal("failed to create node storage", zap.Error(err)) } - _, pv, err := rsaencryption.GenerateKeys() + + privKeyHash, err := privKey.StorageHash() if err != nil { - logger.Fatal("failed generating operator key %v", zap.Error(err)) + logger.Fatal("failed to hash operator private key", zap.Error(err)) } - operatorPubKey, err := nodeStorage.SetupPrivateKey(base64.StdEncoding.EncodeToString(pv)) + + encodedPubKey, err := privKey.Public().Base64() if err != nil { + logger.Fatal("failed to encode operator public key", zap.Error(err)) + } + + if err := nodeStorage.SavePrivateKeyHash(privKeyHash); err != nil { logger.Fatal("could not setup operator private key", zap.Error(err)) } - _, found, err := nodeStorage.GetPrivateKey() + _, found, err := nodeStorage.GetPrivateKeyHash() if err != nil || !found { logger.Fatal("failed to get operator private key", zap.Error(err)) } var operatorData *registrystorage.OperatorData - operatorData, found, err = nodeStorage.GetOperatorDataByPubKey(nil, operatorPubKey) + operatorData, found, err = nodeStorage.GetOperatorDataByPubKey(nil, encodedPubKey) if err != nil { logger.Fatal("could not get operator data by public key", zap.Error(err)) } if !found { operatorData = ®istrystorage.OperatorData{ - PublicKey: operatorPubKey, + PublicKey: encodedPubKey, } } diff --git a/identity/store.go b/identity/store.go index c401ca5e32..3209d34a69 100644 --- a/identity/store.go +++ b/identity/store.go @@ -3,12 +3,13 @@ package p2p import ( "crypto/ecdsa" - "github.com/bloxapp/ssv/logging" - "github.com/bloxapp/ssv/storage/basedb" - "github.com/bloxapp/ssv/utils" gcrypto "github.com/ethereum/go-ethereum/crypto" "github.com/pkg/errors" "go.uber.org/zap" + + "github.com/bloxapp/ssv/logging" + "github.com/bloxapp/ssv/storage/basedb" + "github.com/bloxapp/ssv/utils" ) var ( @@ -24,12 +25,9 @@ var ( ) // Store represents the interface for accessing the node's keys (operator and network keys) -// TODO: add operator key type Store interface { GetNetworkKey() (*ecdsa.PrivateKey, bool, error) SetupNetworkKey(logger *zap.Logger, skEncoded string) (*ecdsa.PrivateKey, error) - // GetOperatorKey() (*rsa.PrivateKey, bool, error) - // SetupOperatorkKey(skEncoded string) (*rsa.PrivateKey, error) } type identityStore struct { diff --git a/message/validation/errors.go b/message/validation/errors.go index a71f1d8bee..6553fbf443 100644 --- a/message/validation/errors.go +++ b/message/validation/errors.go @@ -56,7 +56,7 @@ var ( ErrEarlyMessage = Error{text: "early message"} ErrLateMessage = Error{text: "late message"} ErrTooManySameTypeMessagesPerRound = Error{text: "too many messages of same type per round"} - ErrRSADecryption = Error{text: "rsa decryption", reject: true} + ErrSignatureVerification = Error{text: "signature verification", reject: true} ErrOperatorNotFound = Error{text: "operator not found", reject: true} ErrPubSubMessageHasNoData = Error{text: "pub-sub message has no data", reject: true} ErrPubSubDataTooBig = Error{text: "pub-sub message data too big", reject: true} diff --git a/message/validation/rsa.go b/message/validation/rsa.go index 94071ae0f6..a065adb523 100644 --- a/message/validation/rsa.go +++ b/message/validation/rsa.go @@ -1,19 +1,15 @@ package validation import ( - "crypto" - "crypto/rsa" - "crypto/sha256" - "encoding/base64" "fmt" spectypes "github.com/bloxapp/ssv-spec/types" - "github.com/bloxapp/ssv/utils/rsaencryption" + "github.com/bloxapp/ssv/operator/keys" ) -func (mv *messageValidator) verifyRSASignature(messageData []byte, operatorID spectypes.OperatorID, signature []byte) error { - rsaPubKey, ok := mv.operatorIDToPubkeyCache.Get(operatorID) +func (mv *messageValidator) verifySignature(messageData []byte, operatorID spectypes.OperatorID, signature []byte) error { + operatorPubKey, ok := mv.operatorIDToPubkeyCache.Get(operatorID) if !ok { operator, found, err := mv.nodeStorage.GetOperatorData(nil, operatorID) if err != nil { @@ -28,27 +24,18 @@ func (mv *messageValidator) verifyRSASignature(messageData []byte, operatorID sp return e } - operatorPubKey, err := base64.StdEncoding.DecodeString(string(operator.PublicKey)) + operatorPubKey, err = keys.PublicKeyFromString(string(operator.PublicKey)) if err != nil { - e := ErrRSADecryption + e := ErrSignatureVerification e.innerErr = fmt.Errorf("decode public key: %w", err) return e } - rsaPubKey, err = rsaencryption.ConvertPemToPublicKey(operatorPubKey) - if err != nil { - e := ErrRSADecryption - e.innerErr = fmt.Errorf("convert PEM: %w", err) - return e - } - - mv.operatorIDToPubkeyCache.Set(operatorID, rsaPubKey) + mv.operatorIDToPubkeyCache.Set(operatorID, operatorPubKey) } - messageHash := sha256.Sum256(messageData) - - if err := rsa.VerifyPKCS1v15(rsaPubKey, crypto.SHA256, messageHash[:], signature); err != nil { - e := ErrRSADecryption + if err := operatorPubKey.Verify(messageData, signature); err != nil { + e := ErrSignatureVerification e.innerErr = fmt.Errorf("verify opid: %v signature: %w", operatorID, err) return e } diff --git a/message/validation/validation.go b/message/validation/validation.go index e388da6dab..8efcb2271c 100644 --- a/message/validation/validation.go +++ b/message/validation/validation.go @@ -6,7 +6,6 @@ package validation import ( "bytes" "context" - "crypto/rsa" "encoding/hex" "fmt" "strings" @@ -29,6 +28,7 @@ import ( "github.com/bloxapp/ssv/network/commons" "github.com/bloxapp/ssv/networkconfig" "github.com/bloxapp/ssv/operator/duties/dutystore" + "github.com/bloxapp/ssv/operator/keys" operatorstorage "github.com/bloxapp/ssv/operator/storage" ssvmessage "github.com/bloxapp/ssv/protocol/v2/message" "github.com/bloxapp/ssv/protocol/v2/ssv/queue" @@ -80,7 +80,7 @@ type messageValidator struct { nodeStorage operatorstorage.Storage dutyStore *dutystore.Store getOwnOperatorID OperatorIDGetter - operatorIDToPubkeyCache *hashmap.Map[spectypes.OperatorID, *rsa.PublicKey] + operatorIDToPubkeyCache *hashmap.Map[spectypes.OperatorID, keys.OperatorPublicKey] // validationLocks is a map of lock per SSV message ID to // prevent concurrent access to the same state. @@ -97,7 +97,7 @@ func NewMessageValidator(netCfg networkconfig.NetworkConfig, opts ...Option) Mes logger: zap.NewNop(), metrics: metricsreporter.NewNop(), netCfg: netCfg, - operatorIDToPubkeyCache: hashmap.New[spectypes.OperatorID, *rsa.PublicKey](), + operatorIDToPubkeyCache: hashmap.New[spectypes.OperatorID, keys.OperatorPublicKey](), getOwnOperatorID: func() spectypes.OperatorID { return 0 }, validationLocks: make(map[spectypes.MessageID]*sync.Mutex), } @@ -322,7 +322,7 @@ func (mv *messageValidator) validateP2PMessage(pMsg *pubsub.Message, receivedAt signatureVerifier = func() error { mv.metrics.MessageValidationRSAVerifications() - return mv.verifyRSASignature(messageData, operatorID, signature) + return mv.verifySignature(messageData, operatorID, signature) } } diff --git a/message/validation/validation_test.go b/message/validation/validation_test.go index de5352c081..4f52cf7884 100644 --- a/message/validation/validation_test.go +++ b/message/validation/validation_test.go @@ -2,10 +2,6 @@ package validation import ( "bytes" - "crypto" - crand "crypto/rand" - "crypto/rsa" - "crypto/sha256" "encoding/hex" "math" "testing" @@ -27,6 +23,7 @@ import ( "github.com/bloxapp/ssv/network/commons" "github.com/bloxapp/ssv/networkconfig" "github.com/bloxapp/ssv/operator/duties/dutystore" + "github.com/bloxapp/ssv/operator/keys" "github.com/bloxapp/ssv/operator/storage" beaconprotocol "github.com/bloxapp/ssv/protocol/v2/blockchain/beacon" ssvmessage "github.com/bloxapp/ssv/protocol/v2/message" @@ -34,7 +31,6 @@ import ( registrystorage "github.com/bloxapp/ssv/registry/storage" "github.com/bloxapp/ssv/storage/basedb" "github.com/bloxapp/ssv/storage/kv" - "github.com/bloxapp/ssv/utils/rsaencryption" ) func Test_ValidateSSVMessage(t *testing.T) { @@ -1927,18 +1923,17 @@ func Test_ValidateSSVMessage(t *testing.T) { encodedMsg, err := commons.EncodeNetworkMsg(message) require.NoError(t, err) - hash := sha256.Sum256(encodedMsg) - privKey, err := rsa.GenerateKey(crand.Reader, 2048) + privKey, err := keys.GeneratePrivateKey() require.NoError(t, err) - const operatorID = spectypes.OperatorID(1) - - pubKey, err := rsaencryption.ExtractPublicKey(privKey) + pubKey, err := privKey.Public().Base64() require.NoError(t, err) + const operatorID = spectypes.OperatorID(1) + od := ®istrystorage.OperatorData{ ID: operatorID, - PublicKey: []byte(pubKey), + PublicKey: pubKey, OwnerAddress: common.Address{}, } @@ -1946,7 +1941,7 @@ func Test_ValidateSSVMessage(t *testing.T) { require.NoError(t, err) require.False(t, found) - signature, err := rsa.SignPKCS1v15(crand.Reader, privKey, crypto.SHA256, hash[:]) + signature, err := privKey.Sign(encodedMsg) require.NoError(t, err) encodedMsg = commons.EncodeSignedSSVMessage(encodedMsg, operatorID, signature) @@ -1985,18 +1980,17 @@ func Test_ValidateSSVMessage(t *testing.T) { encodedMsg, err := commons.EncodeNetworkMsg(message) require.NoError(t, err) - hash := sha256.Sum256(encodedMsg) - privKey, err := rsa.GenerateKey(crand.Reader, 2048) + privKey, err := keys.GeneratePrivateKey() require.NoError(t, err) - const operatorID = spectypes.OperatorID(1) - - pubKey, err := rsaencryption.ExtractPublicKey(privKey) + pubKey, err := privKey.Public().Base64() require.NoError(t, err) + const operatorID = spectypes.OperatorID(1) + od := ®istrystorage.OperatorData{ ID: operatorID, - PublicKey: []byte(pubKey), + PublicKey: pubKey, OwnerAddress: common.Address{}, } @@ -2004,7 +1998,7 @@ func Test_ValidateSSVMessage(t *testing.T) { require.NoError(t, err) require.False(t, found) - signature, err := rsa.SignPKCS1v15(crand.Reader, privKey, crypto.SHA256, hash[:]) + signature, err := privKey.Sign(encodedMsg) require.NoError(t, err) encodedMsg = commons.EncodeSignedSSVMessage(encodedMsg, operatorID, signature) @@ -2043,18 +2037,17 @@ func Test_ValidateSSVMessage(t *testing.T) { encodedMsg, err := commons.EncodeNetworkMsg(message) require.NoError(t, err) - hash := sha256.Sum256(encodedMsg) - privKey, err := rsa.GenerateKey(crand.Reader, 2048) + privKey, err := keys.GeneratePrivateKey() require.NoError(t, err) - const operatorID = spectypes.OperatorID(1) - - pubKey, err := rsaencryption.ExtractPublicKey(privKey) + pubKey, err := privKey.Public().Base64() require.NoError(t, err) + const operatorID = spectypes.OperatorID(1) + od := ®istrystorage.OperatorData{ ID: operatorID, - PublicKey: []byte(pubKey), + PublicKey: pubKey, OwnerAddress: common.Address{}, } @@ -2062,7 +2055,7 @@ func Test_ValidateSSVMessage(t *testing.T) { require.NoError(t, err) require.False(t, found) - signature, err := rsa.SignPKCS1v15(crand.Reader, privKey, crypto.SHA256, hash[:]) + signature, err := privKey.Sign(encodedMsg) require.NoError(t, err) const unexpectedOperatorID = 2 @@ -2102,17 +2095,17 @@ func Test_ValidateSSVMessage(t *testing.T) { encodedMsg, err := commons.EncodeNetworkMsg(message) require.NoError(t, err) - privKey, err := rsa.GenerateKey(crand.Reader, 2048) + privKey, err := keys.GeneratePrivateKey() require.NoError(t, err) - const operatorID = spectypes.OperatorID(1) - - pubKey, err := rsaencryption.ExtractPublicKey(privKey) + pubKey, err := privKey.Public().Base64() require.NoError(t, err) + const operatorID = spectypes.OperatorID(1) + od := ®istrystorage.OperatorData{ ID: operatorID, - PublicKey: []byte(pubKey), + PublicKey: pubKey, OwnerAddress: common.Address{}, } @@ -2132,7 +2125,7 @@ func Test_ValidateSSVMessage(t *testing.T) { receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(validator.waitAfterSlotStart(roleAttester)) _, _, err = validator.validateP2PMessage(pMsg, receivedAt) - require.ErrorContains(t, err, ErrRSADecryption.Error()) + require.ErrorContains(t, err, ErrSignatureVerification.Error()) require.NoError(t, ns.DeleteOperatorData(nil, operatorID)) }) diff --git a/migrations/migration_2_encrypt_shares.go b/migrations/migration_2_encrypt_shares.go index 03c40a301d..99ec863275 100644 --- a/migrations/migration_2_encrypt_shares.go +++ b/migrations/migration_2_encrypt_shares.go @@ -28,7 +28,7 @@ var migration_2_encrypt_shares = Migration{ if !found { return completed(txn) } - operatorKey, err := rsaencryption.ConvertPemToPrivateKey(string(obj.Value)) + operatorKey, err := rsaencryption.PemToPrivateKey(obj.Value) if err != nil { return fmt.Errorf("failed to get private key: %w", err) } diff --git a/network/commons/keys.go b/network/commons/keys.go index 4525de49fe..61536c90d6 100644 --- a/network/commons/keys.go +++ b/network/commons/keys.go @@ -16,11 +16,12 @@ import ( // ECDSAPrivFromInterface converts crypto.PrivKey back to ecdsa.PrivateKey func ECDSAPrivFromInterface(privkey crypto.PrivKey) (*ecdsa.PrivateKey, error) { - secpKey := (privkey.(*crypto.Secp256k1PrivateKey)) + secpKey := privkey.(*crypto.Secp256k1PrivateKey) rawKey, err := secpKey.Raw() if err != nil { return nil, errors.Wrap(err, "could mot convert ecdsa.PrivateKey") } + privKey := new(ecdsa.PrivateKey) k := new(big.Int).SetBytes(rawKey) privKey.D = k @@ -56,6 +57,7 @@ func ECDSAPubToInterface(pubkey *ecdsa.PublicKey) (crypto.PubKey, error) { if yVal.SetByteSlice(pubkey.Y.Bytes()) { return nil, errors.Errorf("Y value overflows") } + newKey := crypto.PubKey((*crypto.Secp256k1PublicKey)(btcec.NewPublicKey(xVal, yVal))) // Zero out temporary values. xVal.Zero() @@ -63,7 +65,7 @@ func ECDSAPubToInterface(pubkey *ecdsa.PublicKey) (crypto.PubKey, error) { return newKey, nil } -// RSAPrivToInterface converts ecdsa.PrivateKey to crypto.PrivKey +// RSAPrivToInterface converts rsa.PrivateKey to crypto.PrivKey func RSAPrivToInterface(privkey *rsa.PrivateKey) (crypto.PrivKey, error) { rsaPrivDER := x509.MarshalPKCS1PrivateKey(privkey) return crypto.UnmarshalRsaPrivateKey(rsaPrivDER) diff --git a/network/p2p/config.go b/network/p2p/config.go index a0ffe0a66b..60276fcbdb 100644 --- a/network/p2p/config.go +++ b/network/p2p/config.go @@ -3,7 +3,6 @@ package p2pv1 import ( "context" "crypto/ecdsa" - "crypto/rsa" "fmt" "strings" "time" @@ -22,6 +21,7 @@ import ( "github.com/bloxapp/ssv/network" "github.com/bloxapp/ssv/network/commons" "github.com/bloxapp/ssv/networkconfig" + "github.com/bloxapp/ssv/operator/keys" "github.com/bloxapp/ssv/operator/storage" uc "github.com/bloxapp/ssv/utils/commons" ) @@ -57,8 +57,8 @@ type Config struct { DiscoveryTrace bool `yaml:"DiscoveryTrace" env:"DISCOVERY_TRACE" env-description:"Flag to turn on/off discovery tracing in logs"` // NetworkPrivateKey is used for network identity, MUST be injected NetworkPrivateKey *ecdsa.PrivateKey - // OperatorPrivateKey is used for operator identity, MUST be injected - OperatorPrivateKey *rsa.PrivateKey + // OperatorSigner is used for signing with operator private key, MUST be injected + OperatorSigner keys.OperatorSigner // OperatorPubKeyHash is hash of operator public key, used for identity, optional OperatorPubKeyHash string // GetOperatorID contains numeric operator ID getter diff --git a/network/p2p/p2p.go b/network/p2p/p2p.go index d29b3836e9..116175b116 100644 --- a/network/p2p/p2p.go +++ b/network/p2p/p2p.go @@ -2,7 +2,6 @@ package p2pv1 import ( "context" - "crypto/rsa" "sync/atomic" "time" @@ -26,6 +25,7 @@ import ( "github.com/bloxapp/ssv/network/records" "github.com/bloxapp/ssv/network/streams" "github.com/bloxapp/ssv/network/topics" + "github.com/bloxapp/ssv/operator/keys" operatorstorage "github.com/bloxapp/ssv/operator/storage" "github.com/bloxapp/ssv/utils/async" "github.com/bloxapp/ssv/utils/tasks" @@ -78,7 +78,7 @@ type p2pNetwork struct { nodeStorage operatorstorage.Storage operatorPKHashToPKCache *hashmap.Map[string, []byte] // used for metrics - operatorPrivateKey *rsa.PrivateKey + operatorSigner keys.OperatorSigner getOperatorID func() spectypes.OperatorID } @@ -100,7 +100,7 @@ func New(logger *zap.Logger, cfg *Config, mr Metrics) network.P2PNetwork { activeValidators: hashmap.New[string, validatorStatus](), nodeStorage: cfg.NodeStorage, operatorPKHashToPKCache: hashmap.New[string, []byte](), - operatorPrivateKey: cfg.OperatorPrivateKey, + operatorSigner: cfg.OperatorSigner, getOperatorID: cfg.GetOperatorID, metrics: mr, } diff --git a/network/p2p/p2p_pubsub.go b/network/p2p/p2p_pubsub.go index 2a282cef8c..7f165b9ec5 100644 --- a/network/p2p/p2p_pubsub.go +++ b/network/p2p/p2p_pubsub.go @@ -2,9 +2,6 @@ package p2pv1 import ( "context" - "crypto" - "crypto/rsa" - "crypto/sha256" "encoding/hex" "fmt" "math/rand" @@ -65,9 +62,7 @@ func (n *p2pNetwork) Broadcast(msg *spectypes.SSVMessage) error { } if n.cfg.Network.Beacon.EstimatedCurrentEpoch() > n.cfg.Network.PermissionlessActivationEpoch { - hash := sha256.Sum256(encodedMsg) - - signature, err := rsa.SignPKCS1v15(nil, n.operatorPrivateKey, crypto.SHA256, hash[:]) + signature, err := n.operatorSigner.Sign(encodedMsg) if err != nil { return err } diff --git a/network/p2p/p2p_setup.go b/network/p2p/p2p_setup.go index 408c342fb8..2e42c838ab 100644 --- a/network/p2p/p2p_setup.go +++ b/network/p2p/p2p_setup.go @@ -221,7 +221,7 @@ func (n *p2pNetwork) setupPeerServices(logger *zap.Logger) error { IDService: ids, Network: n.host.Network(), SubnetsProvider: subnetsProvider, - NodeStorage: n.nodeStorage, + OperatorSigner: n.operatorSigner, Permissioned: n.cfg.Permissioned, }, filters) diff --git a/network/p2p/test_utils.go b/network/p2p/test_utils.go index 12899cd4e7..98241846a6 100644 --- a/network/p2p/test_utils.go +++ b/network/p2p/test_utils.go @@ -23,7 +23,6 @@ import ( "github.com/bloxapp/ssv/network/testing" "github.com/bloxapp/ssv/networkconfig" "github.com/bloxapp/ssv/utils/format" - "github.com/bloxapp/ssv/utils/rsaencryption" ) // PeersIndexProvider holds peers index instance @@ -129,15 +128,21 @@ func CreateAndStartLocalNet(pCtx context.Context, logger *zap.Logger, options Lo // NewTestP2pNetwork creates a new network.P2PNetwork instance func (ln *LocalNet) NewTestP2pNetwork(ctx context.Context, nodeIndex int, keys testing.NodeKeys, logger *zap.Logger, options LocalNetOptions) (network.P2PNetwork, error) { - operatorPubkey, err := rsaencryption.ExtractPublicKey(keys.OperatorKey) + operatorPubkey, err := keys.OperatorKey.Public().Base64() if err != nil { return nil, err } - cfg := NewNetConfig(keys, format.OperatorID([]byte(operatorPubkey)), ln.Bootnode, testing.RandomTCPPort(12001, 12999), ln.udpRand.Next(13001, 13999), options.Nodes) + + hash, err := keys.OperatorKey.StorageHash() + if err != nil { + panic(err) + } + + cfg := NewNetConfig(keys, format.OperatorID(operatorPubkey), ln.Bootnode, testing.RandomTCPPort(12001, 12999), ln.udpRand.Next(13001, 13999), options.Nodes) cfg.Ctx = ctx cfg.Subnets = "00000000000000000000020000000000" //PAY ATTENTION for future test scenarios which use more than one eth-validator we need to make this field dynamically changing cfg.NodeStorage = mock.NodeStorage{ - MockGetPrivateKey: keys.OperatorKey, + MockPrivateKeyHash: hash, RegisteredOperatorPublicKeyPEMs: []string{}, } cfg.Metrics = nil @@ -238,7 +243,7 @@ func NewNetConfig(keys testing.NodeKeys, operatorPubKeyHash string, bn *discover PubSubTrace: false, PubSubScoring: true, NetworkPrivateKey: keys.NetKey, - OperatorPrivateKey: keys.OperatorKey, + OperatorSigner: keys.OperatorKey, OperatorPubKeyHash: operatorPubKeyHash, UserAgent: ua, Discovery: discT, diff --git a/network/peers/connections/filters.go b/network/peers/connections/filters.go index 3dd3b42979..4bad54abcb 100644 --- a/network/peers/connections/filters.go +++ b/network/peers/connections/filters.go @@ -1,9 +1,6 @@ package connections import ( - "crypto" - "crypto/rsa" - "encoding/base64" "fmt" "time" @@ -11,8 +8,8 @@ import ( "github.com/pkg/errors" "github.com/bloxapp/ssv/network/records" + "github.com/bloxapp/ssv/operator/keys" "github.com/bloxapp/ssv/operator/storage" - "github.com/bloxapp/ssv/utils/rsaencryption" ) var AllowedDifference = 30 * time.Second @@ -52,18 +49,13 @@ func SignatureCheckFilter() HandshakeFilter { if !ok { return fmt.Errorf("wrong format nodeinfo sent") } - decodedPublicKey, err := base64.StdEncoding.DecodeString(string(sni.HandshakeData.SenderPublicKey)) - if err != nil { - return errors.Wrap(err, "failed to decode sender public key from signed node info") - } - publicKey, err := rsaencryption.ConvertPemToPublicKey(decodedPublicKey) + publicKey, err := keys.PublicKeyFromString(string(sni.HandshakeData.SenderPublicKey)) if err != nil { - return err + return errors.Wrap(err, "failed to decode sender public key from signed node info") } - hashed := sni.HandshakeData.Hash() - if err := rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, hashed[:], sni.Signature); err != nil { + if err := publicKey.Verify(sni.HandshakeData.Encode(), sni.Signature); err != nil { return err } diff --git a/network/peers/connections/handshaker.go b/network/peers/connections/handshaker.go index 1573c23c7a..d9e0200c6d 100644 --- a/network/peers/connections/handshaker.go +++ b/network/peers/connections/handshaker.go @@ -2,7 +2,6 @@ package connections import ( "context" - "fmt" "time" libp2pnetwork "github.com/libp2p/go-libp2p/core/network" @@ -15,7 +14,7 @@ import ( "github.com/bloxapp/ssv/network/peers" "github.com/bloxapp/ssv/network/records" "github.com/bloxapp/ssv/network/streams" - "github.com/bloxapp/ssv/operator/storage" + "github.com/bloxapp/ssv/operator/keys" ) // errPeerWasFiltered is thrown when a peer is filtered during handshake @@ -47,14 +46,14 @@ type handshaker struct { Permissioned func() bool filters func() []HandshakeFilter - streams streams.StreamController - nodeInfos peers.NodeInfoIndex - peerInfos peers.PeerInfoIndex - connIdx peers.ConnectionIndex - subnetsIdx peers.SubnetsIndex - ids identify.IDService - net libp2pnetwork.Network - nodeStorage storage.Storage + streams streams.StreamController + nodeInfos peers.NodeInfoIndex + peerInfos peers.PeerInfoIndex + connIdx peers.ConnectionIndex + subnetsIdx peers.SubnetsIndex + ids identify.IDService + net libp2pnetwork.Network + operatorSigner keys.OperatorSigner subnetsProvider SubnetsProvider } @@ -68,7 +67,7 @@ type HandshakerCfg struct { ConnIdx peers.ConnectionIndex SubnetsIdx peers.SubnetsIndex IDService identify.IDService - NodeStorage storage.Storage + OperatorSigner keys.OperatorSigner SubnetsProvider SubnetsProvider Permissioned func() bool } @@ -86,7 +85,7 @@ func NewHandshaker(ctx context.Context, cfg *HandshakerCfg, filters func() []Han peerInfos: cfg.PeerInfos, subnetsProvider: cfg.SubnetsProvider, net: cfg.Network, - nodeStorage: cfg.NodeStorage, + operatorSigner: cfg.OperatorSigner, Permissioned: cfg.Permissioned, } return h @@ -117,15 +116,11 @@ func (h *handshaker) Handler(logger *zap.Logger) libp2pnetwork.StreamHandler { return errors.Wrap(err, "could not consume node info request") } - // Respond with our own NodeInfo. - privateKey, found, err := h.nodeStorage.GetPrivateKey() - if !found { - return errors.Wrap(err, "could not get private key") - } - self, err := h.nodeInfos.SelfSealed(h.net.LocalPeer(), pid, permissioned, privateKey) + self, err := h.nodeInfos.SelfSealed(h.net.LocalPeer(), pid, permissioned, h.operatorSigner) if err != nil { return errors.Wrap(err, "could not seal self node info") } + if err := respond(self); err != nil { return errors.Wrap(err, "could not send self node info") } @@ -224,14 +219,7 @@ func (h *handshaker) updateNodeSubnets(logger *zap.Logger, pid peer.ID, ni *reco func (h *handshaker) requestNodeInfo(logger *zap.Logger, conn libp2pnetwork.Conn) (records.AnyNodeInfo, error) { permissioned := h.Permissioned() - privateKey, found, err := h.nodeStorage.GetPrivateKey() - if err != nil { - return nil, fmt.Errorf("could not get private key: %w", err) - } - if !found { - return nil, errors.New("could not get private key") - } - data, err := h.nodeInfos.SelfSealed(h.net.LocalPeer(), conn.RemotePeer(), permissioned, privateKey) + data, err := h.nodeInfos.SelfSealed(h.net.LocalPeer(), conn.RemotePeer(), permissioned, h.operatorSigner) if err != nil { return nil, err } diff --git a/network/peers/connections/handshaker_test.go b/network/peers/connections/handshaker_test.go index b4498ca979..f0ab56fd29 100644 --- a/network/peers/connections/handshaker_test.go +++ b/network/peers/connections/handshaker_test.go @@ -5,13 +5,13 @@ import ( "testing" "time" - "github.com/bloxapp/ssv/utils/rsaencryption" "github.com/libp2p/go-libp2p/core/peer" + "github.com/stretchr/testify/require" + "github.com/bloxapp/ssv/logging" "github.com/bloxapp/ssv/network/peers/connections/mock" "github.com/bloxapp/ssv/network/records" - "github.com/stretchr/testify/require" ) // TestHandshakeTestData is a test for testing data and mocks @@ -41,12 +41,6 @@ func TestHandshakeTestData(t *testing.T) { require.ErrorContains(t, pi.LastHandshakeError, "failed requesting node info") }) - t.Run("wrong NodeStorage", func(t *testing.T) { - td := getTestingData(t) - td.Handshaker.nodeStorage = mock.NodeStorage{} - require.Error(t, td.Handshaker.Handshake(logging.TestLogger(t), td.Conn)) - }) - t.Run("wrong StreamController", func(t *testing.T) { td := getTestingData(t) td.Handshaker.streams = mock.StreamController{} @@ -159,9 +153,9 @@ func TestHandshakeProcessIncomingNodeInfoFlow(t *testing.T) { require.NoError(t, err) td.Handshaker.streams = mock.StreamController{MockRequest: sealedIncomingMessage} - senderPublicKey, err := rsaencryption.ExtractPublicKey(td.SenderPrivateKey) + senderPublicKey, err := td.SenderPrivateKey.Public().Base64() require.NoError(t, err) - storgmock := mock.NodeStorage{RegisteredOperatorPublicKeyPEMs: []string{senderPublicKey}} + storgmock := mock.NodeStorage{RegisteredOperatorPublicKeyPEMs: []string{string(senderPublicKey)}} td.Handshaker.filters = func() []HandshakeFilter { return []HandshakeFilter{RegisteredOperatorsFilter(storgmock, []string{})} } @@ -174,14 +168,16 @@ func TestHandshakeProcessIncomingNodeInfoFlow(t *testing.T) { td.Handshaker.Permissioned = func() bool { return true } sealedIncomingMessage, err := td.SignedNodeInfo.Seal(td.NetworkPrivateKey) require.NoError(t, err) + td.Handshaker.streams = mock.StreamController{MockRequest: sealedIncomingMessage} - senderPublicKey, err := rsaencryption.ExtractPublicKey(td.SenderPrivateKey) + + senderPublicKey, err := td.SenderPrivateKey.Public().Base64() require.NoError(t, err) storgmock := mock.NodeStorage{RegisteredOperatorPublicKeyPEMs: []string{}} td.Handshaker.filters = func() []HandshakeFilter { - return []HandshakeFilter{RegisteredOperatorsFilter(storgmock, []string{senderPublicKey})} + return []HandshakeFilter{RegisteredOperatorsFilter(storgmock, []string{string(senderPublicKey)})} } require.ErrorIs(t, td.Handshaker.Handshake(logging.TestLogger(t), td.Conn), nil) diff --git a/network/peers/connections/helpers_test.go b/network/peers/connections/helpers_test.go index 13ab3e8f26..64a381b6b3 100644 --- a/network/peers/connections/helpers_test.go +++ b/network/peers/connections/helpers_test.go @@ -2,8 +2,6 @@ package connections import ( "context" - "crypto" - "crypto/rsa" "testing" "time" @@ -14,21 +12,19 @@ import ( "github.com/bloxapp/ssv/network/peers" "github.com/bloxapp/ssv/network/peers/connections/mock" "github.com/bloxapp/ssv/network/records" - "github.com/bloxapp/ssv/utils/rsaencryption" + "github.com/bloxapp/ssv/operator/keys" ) type TestData struct { NetworkPrivateKey libp2pcrypto.PrivKey - SenderPrivateKey *rsa.PrivateKey + SenderPrivateKey keys.OperatorPrivateKey - HandshakeData records.HandshakeData - HashedHandshakeData []byte - Signature []byte + HandshakeData records.HandshakeData + Signature []byte SenderPeerID peer.ID RecipientPeerID peer.ID - PrivateKeyPEM []byte SenderBase64PublicKeyPEM string Handshaker handshaker @@ -42,13 +38,10 @@ func getTestingData(t *testing.T) TestData { peerID1 := peer.ID("1.1.1.1") peerID2 := peer.ID("2.2.2.2") - _, privateKeyPem, err := rsaencryption.GenerateKeys() + privateKey, err := keys.GeneratePrivateKey() require.NoError(t, err) - senderPrivateKey, err := rsaencryption.ConvertPemToPrivateKey(string(privateKeyPem)) - require.NoError(t, err) - - senderPublicKey, err := rsaencryption.ExtractPublicKey(senderPrivateKey) + senderPublicKey, err := privateKey.Public().Base64() require.NoError(t, err) nodeInfo := &records.NodeInfo{ @@ -65,13 +58,10 @@ func getTestingData(t *testing.T) TestData { SenderPeerID: peerID2, RecipientPeerID: peerID1, Timestamp: time.Now(), - SenderPublicKey: []byte(senderPublicKey), + SenderPublicKey: senderPublicKey, } - hashed := handshakeData.Hash() - - hashedHandshakeData := hashed[:] - signature, err := rsa.SignPKCS1v15(nil, senderPrivateKey, crypto.SHA256, hashedHandshakeData) + signature, err := privateKey.Sign(handshakeData.Encode()) require.NoError(t, err) sni := &records.SignedNodeInfo{ @@ -97,12 +87,6 @@ func getTestingData(t *testing.T) TestData { net := mock.Net{ MockPeerstore: ps, } - nst := mock.NodeStorage{ - MockGetPrivateKey: senderPrivateKey, - RegisteredOperatorPublicKeyPEMs: []string{ - senderPublicKey, - }, - } networkPrivateKey, _, err := libp2pcrypto.GenerateKeyPair(libp2pcrypto.ECDSA, 0) require.NoError(t, err) @@ -115,15 +99,15 @@ func getTestingData(t *testing.T) TestData { } mockHandshaker := handshaker{ - ctx: context.Background(), - nodeInfos: nii, - peerInfos: ns, - ids: ids, - net: net, - nodeStorage: nst, - streams: sc, - filters: func() []HandshakeFilter { return []HandshakeFilter{} }, - Permissioned: func() bool { return false }, + ctx: context.Background(), + nodeInfos: nii, + peerInfos: ns, + ids: ids, + net: net, + operatorSigner: privateKey, + streams: sc, + filters: func() []HandshakeFilter { return []HandshakeFilter{} }, + Permissioned: func() bool { return false }, } mockConn := mock.Conn{ @@ -131,14 +115,12 @@ func getTestingData(t *testing.T) TestData { } return TestData{ - SenderPrivateKey: senderPrivateKey, + SenderPrivateKey: privateKey, HandshakeData: handshakeData, - HashedHandshakeData: hashedHandshakeData, Signature: signature, SenderPeerID: peerID2, RecipientPeerID: peerID1, - PrivateKeyPEM: privateKeyPem, - SenderBase64PublicKeyPEM: senderPublicKey, + SenderBase64PublicKeyPEM: string(senderPublicKey), Handshaker: mockHandshaker, Conn: mockConn, NetworkPrivateKey: networkPrivateKey, diff --git a/network/peers/connections/mock/mock_node_info_idx.go b/network/peers/connections/mock/mock_node_info_idx.go index 1fb7159a84..c57bfd8fcb 100644 --- a/network/peers/connections/mock/mock_node_info_idx.go +++ b/network/peers/connections/mock/mock_node_info_idx.go @@ -1,10 +1,10 @@ package mock import ( - "crypto/rsa" - "github.com/bloxapp/ssv/network/peers" "github.com/bloxapp/ssv/network/records" + "github.com/bloxapp/ssv/operator/keys" + "github.com/libp2p/go-libp2p/core/peer" "github.com/pkg/errors" ) @@ -16,7 +16,7 @@ type NodeInfoIndex struct { MockSelfSealed []byte } -func (m NodeInfoIndex) SelfSealed(sender, recipient peer.ID, permissioned bool, operatorPrivateKey *rsa.PrivateKey) ([]byte, error) { +func (m NodeInfoIndex) SelfSealed(sender, recipient peer.ID, permissioned bool, operatorSigner keys.OperatorSigner) ([]byte, error) { if len(m.MockSelfSealed) != 0 { return m.MockSelfSealed, nil } else { diff --git a/network/peers/connections/mock/mock_storage.go b/network/peers/connections/mock/mock_storage.go index a6944eb4b9..f2d3de655c 100644 --- a/network/peers/connections/mock/mock_storage.go +++ b/network/peers/connections/mock/mock_storage.go @@ -2,7 +2,6 @@ package mock import ( "bytes" - "crypto/rsa" "math/big" "github.com/attestantio/go-eth2-client/spec/bellatrix" @@ -18,7 +17,7 @@ import ( var _ storage.Storage = NodeStorage{} type NodeStorage struct { - MockGetPrivateKey *rsa.PrivateKey + MockPrivateKeyHash string RegisteredOperatorPublicKeyPEMs []string } @@ -142,15 +141,15 @@ func (m NodeStorage) DropShares() error { panic("implement me") } -func (m NodeStorage) GetPrivateKey() (*rsa.PrivateKey, bool, error) { - if m.MockGetPrivateKey != nil { - return m.MockGetPrivateKey, true, nil +func (m NodeStorage) GetPrivateKeyHash() (string, bool, error) { + if m.MockPrivateKeyHash != "" { + return m.MockPrivateKeyHash, true, nil } else { - return nil, false, errors.New("error") + return "", false, errors.New("error") } } -func (m NodeStorage) SetupPrivateKey(operatorKeyBase64 string) ([]byte, error) { +func (m NodeStorage) SavePrivateKeyHash(privKeyHash string) error { //TODO implement me panic("implement me") } diff --git a/network/peers/index.go b/network/peers/index.go index ecc4832cb2..11efe5a39f 100644 --- a/network/peers/index.go +++ b/network/peers/index.go @@ -1,7 +1,6 @@ package peers import ( - "crypto/rsa" "io" "github.com/libp2p/go-libp2p/core/network" @@ -12,6 +11,7 @@ import ( "go.uber.org/zap" "github.com/bloxapp/ssv/network/records" + "github.com/bloxapp/ssv/operator/keys" ) const ( @@ -57,7 +57,7 @@ type ScoreIndex interface { // NodeInfoIndex is an interface for managing records.NodeInfo of network peers type NodeInfoIndex interface { // SelfSealed returns a sealed, encoded of self node info - SelfSealed(sender, recipient peer.ID, permissioned bool, operatorPrivateKey *rsa.PrivateKey) ([]byte, error) + SelfSealed(sender, recipient peer.ID, permissioned bool, operatorSigner keys.OperatorSigner) ([]byte, error) // Self returns the current node info Self() *records.NodeInfo diff --git a/network/peers/peers_index.go b/network/peers/peers_index.go index bc50ed5948..23cf5d975a 100644 --- a/network/peers/peers_index.go +++ b/network/peers/peers_index.go @@ -1,8 +1,6 @@ package peers import ( - "crypto" - "crypto/rsa" "strconv" "sync" "time" @@ -14,7 +12,7 @@ import ( "go.uber.org/zap" "github.com/bloxapp/ssv/network/records" - "github.com/bloxapp/ssv/utils/rsaencryption" + "github.com/bloxapp/ssv/operator/keys" ) // MaxPeersProvider returns the max peers for the given topic. @@ -108,12 +106,12 @@ func (pi *peersIndex) Self() *records.NodeInfo { return pi.self } -func (pi *peersIndex) SelfSealed(sender, recipient peer.ID, permissioned bool, operatorPrivateKey *rsa.PrivateKey) ([]byte, error) { +func (pi *peersIndex) SelfSealed(sender, recipient peer.ID, permissioned bool, operatorSigner keys.OperatorSigner) ([]byte, error) { pi.selfLock.Lock() defer pi.selfLock.Unlock() if permissioned { - publicKey, err := rsaencryption.ExtractPublicKey(operatorPrivateKey) + publicKey, err := operatorSigner.Public().Base64() if err != nil { return nil, err } @@ -122,11 +120,10 @@ func (pi *peersIndex) SelfSealed(sender, recipient peer.ID, permissioned bool, o SenderPeerID: sender, RecipientPeerID: recipient, Timestamp: time.Now(), - SenderPublicKey: []byte(publicKey), + SenderPublicKey: publicKey, } - hash := handshakeData.Hash() - signature, err := rsa.SignPKCS1v15(nil, operatorPrivateKey, crypto.SHA256, hash[:]) + signature, err := operatorSigner.Sign(handshakeData.Encode()) if err != nil { return nil, err } diff --git a/network/records/handshake_data_test.go b/network/records/handshake_data_test.go deleted file mode 100644 index f80c619981..0000000000 --- a/network/records/handshake_data_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package records - -import ( - "bytes" - "encoding/base64" - "testing" - "time" - - "github.com/libp2p/go-libp2p/core/peer" - "github.com/stretchr/testify/require" -) - -func TestHandshakeData_Hash(t *testing.T) { - const expectedHashBase64 = "nAkWkk1gxFU/DeMLs6YfsyEvx1czhlY35gbwUzzvrbk=" - - expectedHash, err := base64.StdEncoding.DecodeString(expectedHashBase64) - require.NoError(t, err) - - handshakeData := HandshakeData{ - SenderPeerID: peer.ID("1.1.1.1"), - RecipientPeerID: peer.ID("2.2.2.2"), - Timestamp: time.Unix(1684228246, 0), - SenderPublicKey: []byte("some key"), - } - - actualHash := handshakeData.Hash() - - require.True(t, bytes.Equal(expectedHash, actualHash[:])) -} diff --git a/network/records/handshake_signature.go b/network/records/handshake_signature.go index 0e043f0784..f50f22e470 100644 --- a/network/records/handshake_signature.go +++ b/network/records/handshake_signature.go @@ -1,7 +1,6 @@ package records import ( - "crypto/sha256" "strconv" "strings" "time" @@ -16,7 +15,7 @@ type HandshakeData struct { SenderPublicKey []byte } -func (h *HandshakeData) Hash() [32]byte { +func (h *HandshakeData) Encode() []byte { sb := strings.Builder{} sb.WriteString(h.SenderPeerID.String()) @@ -24,5 +23,5 @@ func (h *HandshakeData) Hash() [32]byte { sb.WriteString(strconv.FormatInt(h.Timestamp.Unix(), 10)) sb.Write(h.SenderPublicKey) - return sha256.Sum256([]byte(sb.String())) + return []byte(sb.String()) } diff --git a/network/records/signed_node_info_test.go b/network/records/signed_node_info_test.go index 63779efb60..82bce418bf 100644 --- a/network/records/signed_node_info_test.go +++ b/network/records/signed_node_info_test.go @@ -1,9 +1,7 @@ package records import ( - "crypto" "crypto/rand" - "crypto/rsa" "reflect" "testing" "time" @@ -12,7 +10,7 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/stretchr/testify/require" - "github.com/bloxapp/ssv/utils/rsaencryption" + "github.com/bloxapp/ssv/operator/keys" ) func TestSignedNodeInfo_Seal_Consume(t *testing.T) { @@ -26,26 +24,17 @@ func TestSignedNodeInfo_Seal_Consume(t *testing.T) { }, } - _, senderPrivateKeyPem, err := rsaencryption.GenerateKeys() - require.NoError(t, err) - - senderPrivateKey, err := rsaencryption.ConvertPemToPrivateKey(string(senderPrivateKeyPem)) - require.NoError(t, err) - - senderBase64PublicKeyPem, err := rsaencryption.ExtractPublicKey(senderPrivateKey) + senderPrivateKey, err := keys.GeneratePrivateKey() require.NoError(t, err) handshakeData := HandshakeData{ SenderPeerID: peer.ID("1.1.1.1"), RecipientPeerID: peer.ID("2.2.2.2"), Timestamp: time.Now().Round(time.Second), - SenderPublicKey: []byte(senderBase64PublicKeyPem), + SenderPublicKey: senderPrivateKey.Base64(), } - hashed := handshakeData.Hash() - - hashedHandshakeData := hashed[:] - signature, err := rsa.SignPKCS1v15(nil, senderPrivateKey, crypto.SHA256, hashedHandshakeData) + signature, err := senderPrivateKey.Sign(handshakeData.Encode()) require.NoError(t, err) sni := &SignedNodeInfo{ @@ -77,26 +66,17 @@ func TestSignedNodeInfo_Marshal_Unmarshal(t *testing.T) { }, } - _, senderPrivateKeyPem, err := rsaencryption.GenerateKeys() - require.NoError(t, err) - - senderPrivateKey, err := rsaencryption.ConvertPemToPrivateKey(string(senderPrivateKeyPem)) - require.NoError(t, err) - - senderBase64PublicKeyPem, err := rsaencryption.ExtractPublicKey(senderPrivateKey) + senderPrivateKey, err := keys.GeneratePrivateKey() require.NoError(t, err) handshakeData := HandshakeData{ SenderPeerID: peer.ID("1.1.1.1"), RecipientPeerID: peer.ID("2.2.2.2"), Timestamp: time.Now().Round(time.Second), - SenderPublicKey: []byte(senderBase64PublicKeyPem), + SenderPublicKey: senderPrivateKey.Base64(), } - hashed := handshakeData.Hash() - - hashedHandshakeData := hashed[:] - signature, err := rsa.SignPKCS1v15(nil, senderPrivateKey, crypto.SHA256, hashedHandshakeData) + signature, err := senderPrivateKey.Sign(handshakeData.Encode()) require.NoError(t, err) sni := &SignedNodeInfo{ diff --git a/network/testing/keys.go b/network/testing/keys.go index a619fb5145..8cb7441aae 100644 --- a/network/testing/keys.go +++ b/network/testing/keys.go @@ -2,22 +2,20 @@ package testing import ( "crypto/ecdsa" - crand "crypto/rand" - "crypto/rsa" + + "github.com/herumi/bls-eth-go-binary/bls" "github.com/bloxapp/ssv/network/commons" + "github.com/bloxapp/ssv/operator/keys" "github.com/bloxapp/ssv/utils/threshold" - "github.com/herumi/bls-eth-go-binary/bls" ) // NodeKeys holds node's keys type NodeKeys struct { NetKey *ecdsa.PrivateKey - OperatorKey *rsa.PrivateKey + OperatorKey keys.OperatorPrivateKey } -var rsaKeySize = 2048 - // CreateKeys creates random node keys func CreateKeys(n int) ([]NodeKeys, error) { identities := make([]NodeKeys, n) @@ -26,13 +24,15 @@ func CreateKeys(n int) ([]NodeKeys, error) { if err != nil { return nil, err } - opKey, err := rsa.GenerateKey(crand.Reader, rsaKeySize) + + opPrivKey, err := keys.GeneratePrivateKey() if err != nil { return nil, err } + identities[i] = NodeKeys{ NetKey: netKey, - OperatorKey: opKey, + OperatorKey: opPrivKey, } } return identities, nil diff --git a/operator/keys/keys.go b/operator/keys/keys.go new file mode 100644 index 0000000000..4b2c398874 --- /dev/null +++ b/operator/keys/keys.go @@ -0,0 +1,142 @@ +package keys + +import ( + "crypto" + "crypto/rand" + crand "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "fmt" + + "github.com/bloxapp/ssv/utils/rsaencryption" +) + +type OperatorPublicKey interface { + Encrypt(data []byte) ([]byte, error) + Verify(data []byte, signature []byte) error + Base64() ([]byte, error) +} + +type OperatorPrivateKey interface { + OperatorSigner + OperatorDecrypter + StorageHash() (string, error) + EKMHash() (string, error) + Bytes() []byte + Base64() []byte +} + +type OperatorSigner interface { + Sign(data []byte) ([]byte, error) + Public() OperatorPublicKey +} + +type OperatorDecrypter interface { + Decrypt(data []byte) ([]byte, error) +} + +func PrivateKeyFromString(privKeyString string) (OperatorPrivateKey, error) { + operatorKeyByte, err := base64.StdEncoding.DecodeString(privKeyString) + if err != nil { + return nil, err + } + + privKey, err := rsaencryption.PemToPrivateKey(operatorKeyByte) + if err != nil { + return nil, err + } + + return &privateKey{privKey: privKey}, nil +} + +func PrivateKeyFromBytes(pemData []byte) (OperatorPrivateKey, error) { + privKey, err := rsaencryption.PemToPrivateKey(pemData) + if err != nil { + return nil, fmt.Errorf("can't decode operator private key: %w", err) + } + return &privateKey{privKey: privKey}, nil +} + +func GeneratePrivateKey() (OperatorPrivateKey, error) { + const keySize = 2048 + + privKey, err := rsa.GenerateKey(crand.Reader, keySize) + if err != nil { + return nil, err + } + + return &privateKey{privKey: privKey}, nil +} + +type privateKey struct { + privKey *rsa.PrivateKey +} + +func (p *privateKey) Public() OperatorPublicKey { + pubKey := p.privKey.PublicKey + return &publicKey{pubKey: &pubKey} +} + +func (p *privateKey) Sign(data []byte) ([]byte, error) { + hash := sha256.Sum256(data) + return rsa.SignPKCS1v15(nil, p.privKey, crypto.SHA256, hash[:]) +} + +func (p *privateKey) Decrypt(data []byte) ([]byte, error) { + return rsaencryption.DecodeKey(p.privKey, data) +} + +func (p *privateKey) Bytes() []byte { + return rsaencryption.PrivateKeyToByte(p.privKey) +} + +func (p *privateKey) Base64() []byte { + return []byte(rsaencryption.ExtractPrivateKey(p.privKey)) +} + +func (p *privateKey) StorageHash() (string, error) { + return rsaencryption.HashRsaKey(rsaencryption.PrivateKeyToByte(p.privKey)) +} + +func (p *privateKey) EKMHash() (string, error) { + return rsaencryption.HashRsaKey(x509.MarshalPKCS1PrivateKey(p.privKey)) +} + +type publicKey struct { + pubKey *rsa.PublicKey +} + +func PublicKeyFromString(pubKeyString string) (OperatorPublicKey, error) { + pubPem, err := base64.StdEncoding.DecodeString(pubKeyString) + if err != nil { + return nil, err + } + + pubKey, err := rsaencryption.ConvertPemToPublicKey(pubPem) + if err != nil { + return nil, err + } + + return &publicKey{ + pubKey: pubKey, + }, nil +} + +func (p *publicKey) Encrypt(data []byte) ([]byte, error) { + return rsa.EncryptPKCS1v15(rand.Reader, p.pubKey, data) +} + +func (p *publicKey) Verify(data []byte, signature []byte) error { + messageHash := sha256.Sum256(data) + return rsa.VerifyPKCS1v15(p.pubKey, crypto.SHA256, messageHash[:], signature) +} + +func (p *publicKey) Base64() ([]byte, error) { + b, err := rsaencryption.ExtractPublicKey(p.pubKey) + if err != nil { + return nil, err + } + return []byte(b), err +} diff --git a/operator/keys/keys_test.go b/operator/keys/keys_test.go new file mode 100644 index 0000000000..2690dbb99d --- /dev/null +++ b/operator/keys/keys_test.go @@ -0,0 +1,115 @@ +package keys_test + +import ( + "encoding/base64" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/bloxapp/ssv/operator/keys" + rsatesting "github.com/bloxapp/ssv/utils/rsaencryption/testingspace" +) + +const sampleRSAPublicKey = ` +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArVzXJ1Xm3YIY8QYs2MFL +O/FY8M5BZ5GtCgFVdAkhDY2S3n6Q0X8gY9K+9YiQ6ZrLGfrbhUQ9D8q2JY9KZpQ1 +X3sMfJ3TYIjdbq6KUZ0C8fLIft8E0qPMIYlGjjbYKjLC3MBq3Md0K9V7jW7NAjIe +A5CjGHlTlI5n8YUZBQhp2zKDHOFThq4Mh8BiWC5LdiJF1F4fW2JzruBHZMGxK4EX +E3y7OUL8IkYI3RFm7L4yx1M2FAhkQdqBP5LjCObTbk27R8nW5g4pvlrf9GPpDaV9 +UH3pIsH5oiLqSi6q5Y4yAgL1MVzF3eeZ5kPVwLzopY6B4KjP2Lvb9Kbw5tz4gjx2 +QwIDAQAB +-----END PUBLIC KEY----- +` + +func TestGeneratePrivateKey(t *testing.T) { + privKey1, err := keys.GeneratePrivateKey() + require.NoError(t, err, "Failed to generate private key 1") + require.NotNil(t, privKey1, "Generated private key 1 is nil") + + privKey2, err := keys.GeneratePrivateKey() + require.NoError(t, err, "Failed to generate private key 2") + require.NotNil(t, privKey2, "Generated private key 2 is nil") + + require.NotEqual(t, privKey1, privKey2, "Generated private keys 1 and 2 are same") +} + +func TestEncryptDecrypt(t *testing.T) { + privKey, err := keys.GeneratePrivateKey() + require.NoError(t, err, "Failed to generate private key") + + pubKey := privKey.Public() + + originalText := []byte("test") + encryptedText, err := pubKey.Encrypt(originalText) + require.NoError(t, err, "Failed to encrypt text") + + decryptedText, err := privKey.Decrypt(encryptedText) + require.NoError(t, err, "Failed to decrypt text") + require.Equal(t, originalText, decryptedText, "Original and decrypted text do not match") +} + +func TestSignVerify(t *testing.T) { + privKey, err := keys.GeneratePrivateKey() + require.NoError(t, err, "Failed to generate private key") + + pubKey := privKey.Public() + + dataToSign := []byte("test1") + signature, err := privKey.Sign(dataToSign) + require.NoError(t, err, "Failed to sign data") + + err = pubKey.Verify(dataToSign, signature) + require.NoError(t, err, "Failed to verify signature") + + alteredData := []byte("test2") + err = pubKey.Verify(alteredData, signature) + require.Error(t, err, "Verification should fail for altered data") +} + +func TestBase64Encoding(t *testing.T) { + privKey, err := keys.PrivateKeyFromString(base64.StdEncoding.EncodeToString([]byte(rsatesting.SkPem))) + require.NoError(t, err, "Failed to parse private key") + + encodedPrivKey := privKey.Base64() + require.NotEmpty(t, encodedPrivKey, "Encoded private key should not be empty") + + pubKey := privKey.Public() + encodedPubKey, err := pubKey.Base64() + require.NoError(t, err, "Failed to encode public key") + require.NotEmpty(t, encodedPubKey, "Encoded public key should not be empty") + + decodedPrivKeyBytes, err := base64.StdEncoding.DecodeString(string(encodedPrivKey)) + require.NoError(t, err, "Encoded private key should be a valid Base64 string") + + _, err = base64.StdEncoding.DecodeString(string(encodedPubKey)) + require.NoError(t, err, "Encoded public key should be a valid Base64 string") + + require.Equal(t, privKey.Bytes(), decodedPrivKeyBytes, "Decoded private key bytes do not match original private key") +} + +func TestStorageHash(t *testing.T) { + privKey, err := keys.PrivateKeyFromString(base64.StdEncoding.EncodeToString([]byte(rsatesting.SkPem))) + require.NoError(t, err, "Failed to parse private key") + + hash, err := privKey.StorageHash() + require.NoError(t, err, "Failed to compute storage hash") + require.NotEmpty(t, hash, "Storage hash should not be empty") + require.Equal(t, "eaba9e5320c1ef8023d74103e6b6e9828afce89442f2755cde217c06ccacf74a", hash, "Storage hash does not match expected value") +} + +func TestEKMHash(t *testing.T) { + privKey, err := keys.PrivateKeyFromString(base64.StdEncoding.EncodeToString([]byte(rsatesting.SkPem))) + require.NoError(t, err, "Failed to parse private key") + + hash, err := privKey.EKMHash() + require.NoError(t, err, "Failed to compute EKM hash") + require.NotEmpty(t, hash, "EKM hash should not be empty") + require.Equal(t, "6db24021c74d4f5784a0c1a6a519f9ffcb3996be5c0a3d9d4a6d8a567f9cc38a", hash, "EKM hash does not match expected value") +} + +func TestPublicKeyFromString(t *testing.T) { + pubKey, err := keys.PublicKeyFromString(base64.StdEncoding.EncodeToString([]byte(sampleRSAPublicKey))) + require.NoError(t, err, "Failed to parse public key") + require.NotNil(t, pubKey, "Parsed public key is nil") +} diff --git a/operator/keystore/file.go b/operator/keystore/file.go new file mode 100644 index 0000000000..44cddaa2cc --- /dev/null +++ b/operator/keystore/file.go @@ -0,0 +1,51 @@ +package keystore + +import ( + "encoding/json" + "fmt" + keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4" + "strings" +) + +// DecryptKeystore decrypts a keystore JSON file using the provided password. +func DecryptKeystore(encryptedJSONData []byte, password string) ([]byte, error) { + if strings.TrimSpace(password) == "" { + return nil, fmt.Errorf("Password required for decrypting keystore") + } + + // Unmarshal the JSON-encoded data + var data map[string]interface{} + if err := json.Unmarshal(encryptedJSONData, &data); err != nil { + return nil, fmt.Errorf("parse JSON data: %w", err) + } + + // Decrypt the private key using keystorev4 + decryptedBytes, err := keystorev4.New().Decrypt(data, password) + if err != nil { + return nil, fmt.Errorf("decrypt private key: %w", err) + } + + return decryptedBytes, nil +} + +// EncryptKeystore encrypts a private key using the provided password, adds in the public key and returns the encrypted keystore JSON data. +func EncryptKeystore(privkey []byte, pubKeyBase64, password string) ([]byte, error) { + if strings.TrimSpace(password) == "" { + return nil, fmt.Errorf("Password required for encrypting keystore") + } + + // Encrypt the private key using keystorev4 + encryptedKeystoreJSON, err := keystorev4.New().Encrypt(privkey, password) + if err != nil { + return nil, fmt.Errorf("encrypt private key: %w", err) + } + + encryptedKeystoreJSON["pubKey"] = pubKeyBase64 + + encryptedData, err := json.Marshal(encryptedKeystoreJSON) + if err != nil { + return nil, fmt.Errorf("marshal encrypted keystore: %w", err) + } + + return encryptedData, nil +} diff --git a/operator/keystore/file_test.go b/operator/keystore/file_test.go new file mode 100644 index 0000000000..8a359a740e --- /dev/null +++ b/operator/keystore/file_test.go @@ -0,0 +1,45 @@ +package keystore + +import ( + "encoding/json" + "github.com/stretchr/testify/require" + "testing" +) + +func TestDecryptKeystoreWithInvalidData(t *testing.T) { + password := "password" + encryptedJSONData := []byte(`{"version":4,"pubKey":"base64EncodedPublicKey","crypto":{"kdf":"scrypt","checksum":{"function":"sha256","params":{"dklen":32,"salt":"base64EncodedSalt"},"message":"base64EncodedMessage"},"cipher":{"function":"aes-128-ctr","params":{"iv":"base64EncodedIV"},"message":"base64EncodedEncryptedMessage"},"kdfparams":{"n":262144,"r":8,"p":1,"salt":"base64EncodedSalt"}}}`) + _, err := DecryptKeystore(encryptedJSONData, password) + require.Error(t, err) +} + +func TestDecryptKeystoreWithEmptyPassword(t *testing.T) { + password := "" + encryptedJSONData := []byte(`{"valid":"data"}`) + _, err := DecryptKeystore(encryptedJSONData, password) + require.NotNil(t, err) +} + +func TestEncryptKeystoreWithValidData(t *testing.T) { + password := "password" + privkey := []byte("privateKey") + pubKeyBase64 := "base64EncodedPublicKey" + data, err := EncryptKeystore(privkey, pubKeyBase64, password) + require.Nil(t, err) + var jsonData map[string]interface{} + err = json.Unmarshal(data, &jsonData) + require.Nil(t, err) + require.Equal(t, pubKeyBase64, jsonData["pubKey"]) + + decrtypted, err := DecryptKeystore(data, password) + require.Nil(t, err) + require.Equal(t, privkey, decrtypted) +} + +func TestEncryptKeystoreWithEmptyPassword(t *testing.T) { + password := "" + privkey := []byte("privateKey") + pubKeyBase64 := "base64EncodedPublicKey" + _, err := EncryptKeystore(privkey, pubKeyBase64, password) + require.NotNil(t, err) +} diff --git a/operator/storage/storage.go b/operator/storage/storage.go index 12e8c1a7f6..0762621be4 100644 --- a/operator/storage/storage.go +++ b/operator/storage/storage.go @@ -1,8 +1,6 @@ package storage import ( - "crypto/rsa" - "encoding/base64" "encoding/json" "fmt" "math/big" @@ -17,7 +15,6 @@ import ( registry "github.com/bloxapp/ssv/protocol/v2/blockchain/eth1" registrystorage "github.com/bloxapp/ssv/registry/storage" "github.com/bloxapp/ssv/storage/basedb" - "github.com/bloxapp/ssv/utils/rsaencryption" ) var HashedPrivateKey = "hashed-private-key" @@ -48,18 +45,17 @@ type Storage interface { registrystorage.Recipients Shares() registrystorage.Shares - GetPrivateKey() (*rsa.PrivateKey, bool, error) - SetupPrivateKey(operatorKeyBase64 string) ([]byte, error) + GetPrivateKeyHash() (string, bool, error) + SavePrivateKeyHash(privKeyHash string) error } type storage struct { logger *zap.Logger db basedb.Database - operatorPrivateKey []byte - operatorStore registrystorage.Operators - recipientStore registrystorage.Recipients - shareStore registrystorage.Shares + operatorStore registrystorage.Operators + recipientStore registrystorage.Recipients + shareStore registrystorage.Shares } // NewNodeStorage creates a new instance of Storage @@ -202,103 +198,21 @@ func (s *storage) GetLastProcessedBlock(r basedb.Reader) (*big.Int, bool, error) return offset, found, nil } -// GetHashedPrivateKey return sha256 hashed private key -func (s *storage) GetHashedPrivateKey() ([]byte, bool, error) { +// GetPrivateKeyHash return sha256 hashed private key +func (s *storage) GetPrivateKeyHash() (string, bool, error) { obj, found, err := s.db.Get(storagePrefix, []byte(HashedPrivateKey)) if !found { - return nil, found, nil - } - if err != nil { - return nil, found, err - } - return obj.Value, found, nil -} - -// GetPrivateKey return rsa private key -func (s *storage) GetPrivateKey() (*rsa.PrivateKey, bool, error) { - privateKey := s.operatorPrivateKey - if privateKey == nil { - return nil, false, nil - } - sk, err := rsaencryption.ConvertPemToPrivateKey(string(privateKey)) - if err != nil { - return nil, false, err - } - return sk, true, nil -} - -// SetupPrivateKey setup operator private key at the init of the node and set OperatorPublicKey config -func (s *storage) SetupPrivateKey(operatorKeyBase64 string) ([]byte, error) { - operatorKeyByte, err := base64.StdEncoding.DecodeString(operatorKeyBase64) - if err != nil { - return nil, errors.Wrap(err, "Failed to decode base64") - } - var operatorKey = string(operatorKeyByte) - - if err := s.validateKey(operatorKey); err != nil { - return nil, err - } - - sk, found, err := s.GetPrivateKey() - if err != nil { - return nil, errors.Wrap(err, "failed to get operator private key") - } - if !found { - return nil, errors.New("failed to find operator private key") + return "", found, nil } - - operatorPublicKey, err := rsaencryption.ExtractPublicKey(sk) if err != nil { - return nil, errors.Wrap(err, "failed to extract operator public key") + return "", found, err } - - //TODO change the log to generated/loaded private key to indicate better on the action - s.logger.Info("successfully setup operator keys", zap.String("pubkey", operatorPublicKey)) - return []byte(operatorPublicKey), nil + return string(obj.Value), found, nil } -// validateKey validate provided and exist key. save if needed. -func (s *storage) validateKey(operatorKey string) error { - // check if passed new key. if so, save new key (force to always save key when provided) - storedPrivateKey, privateKeyExist, err := s.GetHashedPrivateKey() - if err != nil { - return errors.New("Can't Get Operator private key from storage") - } - hashedKey, err := rsaencryption.HashRsaKey([]byte(operatorKey)) - if err != nil { - return errors.New("Cannot hash Operator private key") - } - if privateKeyExist && hashedKey != string(storedPrivateKey) { - return errors.New("Operator private key is not matching the one encrypted the storage") - } - if operatorKey != "" { - return s.savePrivateKey(operatorKey) - } - // new key not provided, check if key exist - _, found, err := s.GetPrivateKey() - if err != nil { - return err - } - // if no, check if you need to generate. if no, return error - if !found { - return errors.New("key not exist or provided") - } - - // key exist in storage. - return nil -} - -// SavePrivateKey save operator private key -func (s *storage) savePrivateKey(operatorKey string) error { - hashedKey, err := rsaencryption.HashRsaKey([]byte(operatorKey)) - if err != nil { - return err - } - if err := s.db.Set(storagePrefix, []byte(HashedPrivateKey), []byte(hashedKey)); err != nil { - return err - } - s.operatorPrivateKey = []byte(operatorKey) - return nil +// SavePrivateKeyHash saves operator private key hash +func (s *storage) SavePrivateKeyHash(hashedKey string) error { + return s.db.Set(storagePrefix, []byte(HashedPrivateKey), []byte(hashedKey)) } func (s *storage) UpdateValidatorMetadata(pk string, metadata *beacon.ValidatorMetadata) error { diff --git a/operator/storage/storage_test.go b/operator/storage/storage_test.go index f2d333ff08..711afecb11 100644 --- a/operator/storage/storage_test.go +++ b/operator/storage/storage_test.go @@ -1,31 +1,29 @@ package storage import ( - "encoding/base64" "testing" "github.com/attestantio/go-eth2-client/spec/bellatrix" spectypes "github.com/bloxapp/ssv-spec/types" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" - "go.uber.org/zap/zaptest" "github.com/bloxapp/ssv/logging" "github.com/bloxapp/ssv/networkconfig" + "github.com/bloxapp/ssv/operator/keys" "github.com/bloxapp/ssv/protocol/v2/types" registrystorage "github.com/bloxapp/ssv/registry/storage" "github.com/bloxapp/ssv/storage/basedb" "github.com/bloxapp/ssv/storage/kv" - "github.com/bloxapp/ssv/utils/rsaencryption" ) var ( - pkPem = "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBb3dFN09FYnd5TGt2clowVFU0amoKb295SUZ4TnZnclk4RmorV3NseVpUbHlqOFVEZkZyWWg1VW4ydTRZTWRBZStjUGYxWEsrQS9QOVhYN09CNG5mMQpPb0dWQjZ3ckMvamhMYnZPSDY1MHJ5VVlvcGVZaGxTWHhHbkQ0dmN2VHZjcUxMQit1ZTIvaXlTeFFMcFpSLzZWCnNUM2ZGckVvbnpGVHFuRkN3Q0YyOGlQbkpWQmpYNlQvSGNUSjU1SURrYnRvdGFyVTZjd3dOT0huSGt6V3J2N2kKdHlQa1I0R2UxMWhtVkc5UWpST3Q1NmVoWGZGc0ZvNU1xU3ZxcFlwbFhrSS96VU5tOGovbHFFZFUwUlhVcjQxTAoyaHlLWS9wVmpzZ21lVHNONy9acUFDa0h5ZTlGYmtWOVYvVmJUaDdoV1ZMVHFHU2g3QlkvRDdnd093ZnVLaXEyClR3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K" - skPem = "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBb3dFN09FYnd5TGt2clowVFU0ampvb3lJRnhOdmdyWThGaitXc2x5WlRseWo4VURmCkZyWWg1VW4ydTRZTWRBZStjUGYxWEsrQS9QOVhYN09CNG5mMU9vR1ZCNndyQy9qaExidk9INjUwcnlVWW9wZVkKaGxTWHhHbkQ0dmN2VHZjcUxMQit1ZTIvaXlTeFFMcFpSLzZWc1QzZkZyRW9uekZUcW5GQ3dDRjI4aVBuSlZCagpYNlQvSGNUSjU1SURrYnRvdGFyVTZjd3dOT0huSGt6V3J2N2l0eVBrUjRHZTExaG1WRzlRalJPdDU2ZWhYZkZzCkZvNU1xU3ZxcFlwbFhrSS96VU5tOGovbHFFZFUwUlhVcjQxTDJoeUtZL3BWanNnbWVUc043L1pxQUNrSHllOUYKYmtWOVYvVmJUaDdoV1ZMVHFHU2g3QlkvRDdnd093ZnVLaXEyVHdJREFRQUJBb0lCQURqTzNReW43SktIdDQ0UwpDQUk4MnRoemtabzVNOHVpSng2NTJwTWVvbThrNmgzU05lMThYQ1BFdXpCdmJ6ZWcyMFlUcEhkQTB2dFpJZUpBCmRTdXdFczdwQ2o4NlNXWkt2bTlwM0ZRK1FId3B1WVF3d1A5UHkvU3Z4NHo2Q0lyRXFQWWFMSkF2dzJtQ3lDTisKems3QTh2cHFUYTFpNEgxYWU0WVRJdWhDd1dseGUxdHRENnJWVVlmQzJyVmFGSitiOEpsekZScTRibkFSOHltZQpyRTRpQWxmZ1RPajl6TDgxNHFSbFlRZWVaaE12QThUMHFXVW9oYnIxaW1vNVh6SUpaYXlMb2N2cWhaRWJrMGRqCnE5cUtXZElwQUFUUmpXdmIrN1Bram1sd05qTE9oSjFwaHRDa2MvUzRqMmN2bzlnY1M3V2FmeGFxQ2wvaXg0WXQKNUt2UEo4RUNnWUVBMEVtNG5NTUVGWGJ1U00vbDVVQ3p2M2tUNkgvVFlPN0ZWaDA3MUc3UUFGb2xveEpCWkRGVgo3ZkhzYyt1Q2ltbEcyWHQzQ3JHbzl0c09uRi9aZ0RLTm10RHZ2anhtbFBuQWI1ZzR1aFhnWU5Nc0tRU2hwZVJXCi9heThDbVdic1JxWFphTG9JNWJyMmtDVEx3c1Z6MmhwYWJBekJPcjJZVjN2TVJCNWk3Q09ZU01DZ1lFQXlGZ0wKM0RrS3dzVFR5VnlwbGVub0FaYVMvbzBtS3habmZmUm5ITlA1UWdSZlQ0cFFrdW9naytNWUFlQnVHc2M0Y1RpNwpyVHR5dFVNQkFCWEVLR0lKa0FiTm9BU0hRTVVjTzF2dmN3aEJXN0F5K294dWMwSlNsbmFYam93UzBDMG8vNHFyClEvcnBVbmVpcitWdS9OOCs2ZWRFVFJrTmorNXVubWVQRWU5TkJ1VUNnWUVBZ3RVcjMxd29Ib3Q4RmNSeE5kVzAKa3BzdFJDZTIwUFpxZ2pNT3Q5dDdVQjFQOHVTdXFvN0syUkhUWXVVV05IYjRoL2VqeU5YYnVtUFRBNnE1Wm10YQp3MXBtbldvM1RYQ3J6ZTBpQk5GbEJhemYya3dNZGJXK1pzMnZ1Q0FtOGRJd015bG5BNlB6Tmo3RnRSRVRmQnFyCnpEVmZkc0ZZVGNUQlVHSjIxcVhxYVYwQ2dZRUFtdU1QRUV2OVdNVG80M1ZER3NhQ2VxL1pwdmlpK0k3U3Boc00KbU1uOG02QmJ1MWU0b1V4bXNVN1JvYW5NRmVITmJpTXBYVzFuYW1HSjVYSHVmRFlISkpWTjVaZDZwWVYrSlJvWApqanhrb3lrZTBIcy9iTlpxbVM3SVR3bFdCaUhUMzNScW9oemF3OG9BT2JMTVVxMlpxeVlEdFFOWWE5MHZJa0gzCjV5cTF4MDBDZ1lFQXM0enRRaEdSYmVVbHFuVzZaNnlmUko2WFhZcWRNUGh4dUJ4dk5uL2R4SjEwVDRXMkRVdUMKalNkcEdYclkrRUNZeVhVd2xYQnFiYUt4MUs1QVFEN25tdTlKM2wwb01rWDZ0U0JqMU9FNU1hYkFUcnNXNnd2VApoa1RQSlpNeVBVWWhvQmtpdlBVS3lRWHN3clFWL25VUUFzQWNMZUpTaFRXNGdTczBNNndlUUFjPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=" - skPem2 = "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBejJQUkdwNmhUbm5ESXRLTnhMQUNVMkpEOHhLcHpXdUtFK01BcUMwTU9DK3IwdmRYCkdhMkdTcGJXbXhMc1EvcTRWajRFcmpaWFliSGRKZWNkTVJJM1kzVjl1bU9sRGJBZjNxMWs3M2FEbE04U2tBbjMKektBOWtzQkpVejczNkw5UVgxU3FpWUN5S1o5V2V0d1NOclN0blczVUE3WHd1T2VmeWVFUkg0aERYclFIeVZuQgpReDFxdlZibkl6SE15Y09oaWsxbzVocUo0QzJEZjhHWUV5Nlk0czNhTnBkVHgwWFNWS2hWb3dOeDQrcTYyeG1ICmV3YUUycEVOcW9sSk5qVlBBOVRwMWhhQzJvY1l3STZqT1dlVGhaSWUyVDh3K0wzVytncjlMWXhFRnpvNFMrSmEKTWxPT01jUndEeis5bGpYOHpURXdnNHZETU05bUxYTE5WS3gxUXdJREFRQUJBb0lCQUVVMC9CeXovd1JmSWIxSApJa1FXc0UvL0pNbkMycU5RVmIyWkxTanlEM2ZZZ0xCZ0ZkTGQwMGlrMld6YWZibVp1MVljVUJlS3p0SXROcTFsCldKcDloN3BMQk8va1BMbzZvZ2YvT1FXb09QUzV2V29QeVgraG9hcU5QR3JwUW5XTEVsa2R1ZU0wN1Q5eWlydHAKSVRMY1RHdVNzUU9qL1hiVzVMM0x1NWtZTWRNeUN0SHlrUEVMeEpUN2crWkM3UmJuVDdweVN6SWpVRXNlMmtTVAozaFR4cytkekhJc3RqZHNzZWtQSlVBWDBIWi94UFpXek1TMWFvL1V6Sk5jMHpLbDFqRDJXV0k3Q0VodWpjL3QvCjQ0eExYQ0VBQUZTVUxMa29wLytkTDZldjc2SklIVlIxd3VvTHpMT0dVbDJ6UEdzUU9WLzVndTJmQ0xJOG11TlYKMXVtQ213RUNnWUVBM0xrQVZtZ0lteERPRzJNb291UGJVTkZ4NWNUcWZsYjRZK2dUSmhMbGw2T3ZsVno2Y0xnSwp3SWlIcnMwTWZ4YTYva01aMFZveDNxK0lsMk5LaDR2TXg3ZVY5STNnMGJ3RGhMUkxRQ3J1M29XRkpBZkZoa3FxCi9wUk41NElLU0orNTZhWEJwSkpKbU12a013RW1KL0RheklhMkF2cUlrRE9wWWdINkY3NXJYMEVDZ1lFQThJbE0KUy9zcmFFSXRQNUU2TjZwd1RZU0NrK2tLdndZRm15bXIxc2E0U29oNkJGbGpxSC81ZW5FV1BHcnRlTTVHMDFhYQpTTkRLVlRicHVRcmxSK3pTczRJUHBJWTk0Q2ZLTnNSZWpnOTlzRXBWSGZtU2o1UG9SVHQzditldDBOVXIvREE5Cmd5MHpBMG5qUW9KMUpOTVNEQnBYUnpReDFJNmlEdkNKai9rVDk0TUNnWUFJZ1FRM1VBak0yS2ZvUERqTGxkWFUKVmsxNkdjMGpFdnk4OUtzUU0zZ3ZFSHBxV2N1NFhnN2ovaDZrS0hoTHlUZHBKbkt2TXpkcXFmNnNQb0lYbU5aSgo5NVBLZVZEcEk4Sks4WnRZbkk3WmVmRjRRdWhrVlNvalp0bGRpeEFVWGpzT2VubHNlc3BsSGEzc0hTWTRNYnBzCldPQllXd2k1N1pPZ0dBMW5yc2w2UVFLQmdRQ0xEUFA4WUtEQlRyQlZ0U0RRbVVqK3B3SE5lOFRvbFJTY2xFUncKanNSdTRlS1hyUTA5bFcybGFNYVArc2g1TTlZaHlraTZtMmk4UmxocXptK3BXckNiY1M2Vno3enBYbGM1dmQ5agpoSFVHZXBJbUYrYXY5Yk1xZ3F4QlZpOVhNRVNUTDFnQUF4c2daWkJwSEgyWDRpVG10anVLUUJRbWFxWW91TWp0ClgvSTQvUUtCZ0RONFk1TDZDR1JwV3A5V3hCR3AwbVdwcndIYnFJbEV1bWEwSGJPaWJ5TldHOGtKUk5ubGM4aXYKamY4a3U3SDhGbjQxRTlNZkl5SXBEM20wczdDZ3d4Nzg2dnluRkZhS0pxRzQwQjZHcVBUdDZUSFd1Y3hiOEhBZQpHdlcydTQyT25jUXVYdlFEV0EzQ3J3SVlMQ3l4YlJyS040eGdleGFOakcwRERsV0RrM2NCCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==" + pkPem = "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBb3dFN09FYnd5TGt2clowVFU0amoKb295SUZ4TnZnclk4RmorV3NseVpUbHlqOFVEZkZyWWg1VW4ydTRZTWRBZStjUGYxWEsrQS9QOVhYN09CNG5mMQpPb0dWQjZ3ckMvamhMYnZPSDY1MHJ5VVlvcGVZaGxTWHhHbkQ0dmN2VHZjcUxMQit1ZTIvaXlTeFFMcFpSLzZWCnNUM2ZGckVvbnpGVHFuRkN3Q0YyOGlQbkpWQmpYNlQvSGNUSjU1SURrYnRvdGFyVTZjd3dOT0huSGt6V3J2N2kKdHlQa1I0R2UxMWhtVkc5UWpST3Q1NmVoWGZGc0ZvNU1xU3ZxcFlwbFhrSS96VU5tOGovbHFFZFUwUlhVcjQxTAoyaHlLWS9wVmpzZ21lVHNONy9acUFDa0h5ZTlGYmtWOVYvVmJUaDdoV1ZMVHFHU2g3QlkvRDdnd093ZnVLaXEyClR3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K" + skPem = "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBb3dFN09FYnd5TGt2clowVFU0ampvb3lJRnhOdmdyWThGaitXc2x5WlRseWo4VURmCkZyWWg1VW4ydTRZTWRBZStjUGYxWEsrQS9QOVhYN09CNG5mMU9vR1ZCNndyQy9qaExidk9INjUwcnlVWW9wZVkKaGxTWHhHbkQ0dmN2VHZjcUxMQit1ZTIvaXlTeFFMcFpSLzZWc1QzZkZyRW9uekZUcW5GQ3dDRjI4aVBuSlZCagpYNlQvSGNUSjU1SURrYnRvdGFyVTZjd3dOT0huSGt6V3J2N2l0eVBrUjRHZTExaG1WRzlRalJPdDU2ZWhYZkZzCkZvNU1xU3ZxcFlwbFhrSS96VU5tOGovbHFFZFUwUlhVcjQxTDJoeUtZL3BWanNnbWVUc043L1pxQUNrSHllOUYKYmtWOVYvVmJUaDdoV1ZMVHFHU2g3QlkvRDdnd093ZnVLaXEyVHdJREFRQUJBb0lCQURqTzNReW43SktIdDQ0UwpDQUk4MnRoemtabzVNOHVpSng2NTJwTWVvbThrNmgzU05lMThYQ1BFdXpCdmJ6ZWcyMFlUcEhkQTB2dFpJZUpBCmRTdXdFczdwQ2o4NlNXWkt2bTlwM0ZRK1FId3B1WVF3d1A5UHkvU3Z4NHo2Q0lyRXFQWWFMSkF2dzJtQ3lDTisKems3QTh2cHFUYTFpNEgxYWU0WVRJdWhDd1dseGUxdHRENnJWVVlmQzJyVmFGSitiOEpsekZScTRibkFSOHltZQpyRTRpQWxmZ1RPajl6TDgxNHFSbFlRZWVaaE12QThUMHFXVW9oYnIxaW1vNVh6SUpaYXlMb2N2cWhaRWJrMGRqCnE5cUtXZElwQUFUUmpXdmIrN1Bram1sd05qTE9oSjFwaHRDa2MvUzRqMmN2bzlnY1M3V2FmeGFxQ2wvaXg0WXQKNUt2UEo4RUNnWUVBMEVtNG5NTUVGWGJ1U00vbDVVQ3p2M2tUNkgvVFlPN0ZWaDA3MUc3UUFGb2xveEpCWkRGVgo3ZkhzYyt1Q2ltbEcyWHQzQ3JHbzl0c09uRi9aZ0RLTm10RHZ2anhtbFBuQWI1ZzR1aFhnWU5Nc0tRU2hwZVJXCi9heThDbVdic1JxWFphTG9JNWJyMmtDVEx3c1Z6MmhwYWJBekJPcjJZVjN2TVJCNWk3Q09ZU01DZ1lFQXlGZ0wKM0RrS3dzVFR5VnlwbGVub0FaYVMvbzBtS3habmZmUm5ITlA1UWdSZlQ0cFFrdW9naytNWUFlQnVHc2M0Y1RpNwpyVHR5dFVNQkFCWEVLR0lKa0FiTm9BU0hRTVVjTzF2dmN3aEJXN0F5K294dWMwSlNsbmFYam93UzBDMG8vNHFyClEvcnBVbmVpcitWdS9OOCs2ZWRFVFJrTmorNXVubWVQRWU5TkJ1VUNnWUVBZ3RVcjMxd29Ib3Q4RmNSeE5kVzAKa3BzdFJDZTIwUFpxZ2pNT3Q5dDdVQjFQOHVTdXFvN0syUkhUWXVVV05IYjRoL2VqeU5YYnVtUFRBNnE1Wm10YQp3MXBtbldvM1RYQ3J6ZTBpQk5GbEJhemYya3dNZGJXK1pzMnZ1Q0FtOGRJd015bG5BNlB6Tmo3RnRSRVRmQnFyCnpEVmZkc0ZZVGNUQlVHSjIxcVhxYVYwQ2dZRUFtdU1QRUV2OVdNVG80M1ZER3NhQ2VxL1pwdmlpK0k3U3Boc00KbU1uOG02QmJ1MWU0b1V4bXNVN1JvYW5NRmVITmJpTXBYVzFuYW1HSjVYSHVmRFlISkpWTjVaZDZwWVYrSlJvWApqanhrb3lrZTBIcy9iTlpxbVM3SVR3bFdCaUhUMzNScW9oemF3OG9BT2JMTVVxMlpxeVlEdFFOWWE5MHZJa0gzCjV5cTF4MDBDZ1lFQXM0enRRaEdSYmVVbHFuVzZaNnlmUko2WFhZcWRNUGh4dUJ4dk5uL2R4SjEwVDRXMkRVdUMKalNkcEdYclkrRUNZeVhVd2xYQnFiYUt4MUs1QVFEN25tdTlKM2wwb01rWDZ0U0JqMU9FNU1hYkFUcnNXNnd2VApoa1RQSlpNeVBVWWhvQmtpdlBVS3lRWHN3clFWL25VUUFzQWNMZUpTaFRXNGdTczBNNndlUUFjPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=" + //skPem2 = "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBejJQUkdwNmhUbm5ESXRLTnhMQUNVMkpEOHhLcHpXdUtFK01BcUMwTU9DK3IwdmRYCkdhMkdTcGJXbXhMc1EvcTRWajRFcmpaWFliSGRKZWNkTVJJM1kzVjl1bU9sRGJBZjNxMWs3M2FEbE04U2tBbjMKektBOWtzQkpVejczNkw5UVgxU3FpWUN5S1o5V2V0d1NOclN0blczVUE3WHd1T2VmeWVFUkg0aERYclFIeVZuQgpReDFxdlZibkl6SE15Y09oaWsxbzVocUo0QzJEZjhHWUV5Nlk0czNhTnBkVHgwWFNWS2hWb3dOeDQrcTYyeG1ICmV3YUUycEVOcW9sSk5qVlBBOVRwMWhhQzJvY1l3STZqT1dlVGhaSWUyVDh3K0wzVytncjlMWXhFRnpvNFMrSmEKTWxPT01jUndEeis5bGpYOHpURXdnNHZETU05bUxYTE5WS3gxUXdJREFRQUJBb0lCQUVVMC9CeXovd1JmSWIxSApJa1FXc0UvL0pNbkMycU5RVmIyWkxTanlEM2ZZZ0xCZ0ZkTGQwMGlrMld6YWZibVp1MVljVUJlS3p0SXROcTFsCldKcDloN3BMQk8va1BMbzZvZ2YvT1FXb09QUzV2V29QeVgraG9hcU5QR3JwUW5XTEVsa2R1ZU0wN1Q5eWlydHAKSVRMY1RHdVNzUU9qL1hiVzVMM0x1NWtZTWRNeUN0SHlrUEVMeEpUN2crWkM3UmJuVDdweVN6SWpVRXNlMmtTVAozaFR4cytkekhJc3RqZHNzZWtQSlVBWDBIWi94UFpXek1TMWFvL1V6Sk5jMHpLbDFqRDJXV0k3Q0VodWpjL3QvCjQ0eExYQ0VBQUZTVUxMa29wLytkTDZldjc2SklIVlIxd3VvTHpMT0dVbDJ6UEdzUU9WLzVndTJmQ0xJOG11TlYKMXVtQ213RUNnWUVBM0xrQVZtZ0lteERPRzJNb291UGJVTkZ4NWNUcWZsYjRZK2dUSmhMbGw2T3ZsVno2Y0xnSwp3SWlIcnMwTWZ4YTYva01aMFZveDNxK0lsMk5LaDR2TXg3ZVY5STNnMGJ3RGhMUkxRQ3J1M29XRkpBZkZoa3FxCi9wUk41NElLU0orNTZhWEJwSkpKbU12a013RW1KL0RheklhMkF2cUlrRE9wWWdINkY3NXJYMEVDZ1lFQThJbE0KUy9zcmFFSXRQNUU2TjZwd1RZU0NrK2tLdndZRm15bXIxc2E0U29oNkJGbGpxSC81ZW5FV1BHcnRlTTVHMDFhYQpTTkRLVlRicHVRcmxSK3pTczRJUHBJWTk0Q2ZLTnNSZWpnOTlzRXBWSGZtU2o1UG9SVHQzditldDBOVXIvREE5Cmd5MHpBMG5qUW9KMUpOTVNEQnBYUnpReDFJNmlEdkNKai9rVDk0TUNnWUFJZ1FRM1VBak0yS2ZvUERqTGxkWFUKVmsxNkdjMGpFdnk4OUtzUU0zZ3ZFSHBxV2N1NFhnN2ovaDZrS0hoTHlUZHBKbkt2TXpkcXFmNnNQb0lYbU5aSgo5NVBLZVZEcEk4Sks4WnRZbkk3WmVmRjRRdWhrVlNvalp0bGRpeEFVWGpzT2VubHNlc3BsSGEzc0hTWTRNYnBzCldPQllXd2k1N1pPZ0dBMW5yc2w2UVFLQmdRQ0xEUFA4WUtEQlRyQlZ0U0RRbVVqK3B3SE5lOFRvbFJTY2xFUncKanNSdTRlS1hyUTA5bFcybGFNYVArc2g1TTlZaHlraTZtMmk4UmxocXptK3BXckNiY1M2Vno3enBYbGM1dmQ5agpoSFVHZXBJbUYrYXY5Yk1xZ3F4QlZpOVhNRVNUTDFnQUF4c2daWkJwSEgyWDRpVG10anVLUUJRbWFxWW91TWp0ClgvSTQvUUtCZ0RONFk1TDZDR1JwV3A5V3hCR3AwbVdwcndIYnFJbEV1bWEwSGJPaWJ5TldHOGtKUk5ubGM4aXYKamY4a3U3SDhGbjQxRTlNZkl5SXBEM20wczdDZ3d4Nzg2dnluRkZhS0pxRzQwQjZHcVBUdDZUSFd1Y3hiOEhBZQpHdlcydTQyT25jUXVYdlFEV0EzQ3J3SVlMQ3l4YlJyS040eGdleGFOakcwRERsV0RrM2NCCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==" ) -func TestSaveAndGetPrivateKey(t *testing.T) { +func TestSaveAndGetPrivateKeyHash(t *testing.T) { logger := logging.TestLogger(t) db, err := kv.NewInMemory(logger, basedb.Options{}) require.NoError(t, err) @@ -35,111 +33,22 @@ func TestSaveAndGetPrivateKey(t *testing.T) { db: db, } - KeyByte, err := base64.StdEncoding.DecodeString(skPem) // passing keys format should be in base64 + parsedPrivKey, err := keys.PrivateKeyFromString(skPem) require.NoError(t, err) - require.NoError(t, operatorStorage.savePrivateKey(string(KeyByte))) - sk, found, err := operatorStorage.GetPrivateKey() - require.True(t, true, found) - require.NoError(t, err) - operatorPublicKey, err := rsaencryption.ExtractPublicKey(sk) - require.NoError(t, err) - require.Equal(t, pkPem, operatorPublicKey) -} - -func TestSetupPrivateKey(t *testing.T) { - tests := []struct { - name string - existKey string - passedKey string - expectedError string - }{ - { - name: "key not exist, passing nothing", // expected - raise an error - existKey: "", - passedKey: "", - expectedError: "key not exist or provided", - }, - { - name: "key not exist, passing key in env", // expected - set the passed key - existKey: "", - passedKey: skPem2, - expectedError: "", - }, - { - name: "key exist, passing key in env", // expected - throw ERROR of data encrypted with different key - existKey: skPem, - passedKey: skPem2, - expectedError: "Operator private key is not matching the one encrypted the storage", - }, - { - name: "key exist, passing nothing", // expected - throw ERROR of data encrypted with different key - existKey: skPem, - passedKey: "", - expectedError: "Operator private key is not matching the one encrypted the storage", - }, - { - name: "error raised", // expected - throw an error - existKey: "", - passedKey: "xxx", - expectedError: "Failed to decode base64: illegal base64 data at input byte 0", - }, - } - - for _, test := range tests { - test := test - t.Run(test.name, func(t *testing.T) { - logger := logging.TestLogger(t) - db, err := kv.NewInMemory(logger, basedb.Options{}) - require.NoError(t, err) - defer db.Close() - operatorStorage := storage{ - logger: zaptest.NewLogger(t), - db: db, - } + parsedPrivKeyHash, err := parsedPrivKey.StorageHash() + require.NoError(t, err) - if test.existKey != "" { // mock exist key - existKeyByte, err := base64.StdEncoding.DecodeString(test.existKey) // passing keys format should be in base64 - require.NoError(t, err) - require.NoError(t, operatorStorage.savePrivateKey(string(existKeyByte))) - sk, found, err := operatorStorage.GetPrivateKey() - require.NoError(t, err) - require.True(t, found) - require.NotNil(t, sk) + encodedPubKey, err := parsedPrivKey.Public().Base64() + require.NoError(t, err) + require.Equal(t, pkPem, string(encodedPubKey)) - existKeyByte, err = base64.StdEncoding.DecodeString(test.existKey) - require.NoError(t, err) - require.Equal(t, string(existKeyByte), string(rsaencryption.PrivateKeyToByte(sk))) - } + require.NoError(t, operatorStorage.SavePrivateKeyHash(parsedPrivKeyHash)) + extractedHash, found, err := operatorStorage.GetPrivateKeyHash() + require.True(t, true, found) + require.NoError(t, err) - pk, err := operatorStorage.SetupPrivateKey(test.passedKey) - if test.expectedError != "" { - require.NotNil(t, err) - require.Equal(t, test.expectedError, err.Error()) - return - } - require.NoError(t, err) - sk, found, err := operatorStorage.GetPrivateKey() - require.NoError(t, err) - require.True(t, found) - require.NotNil(t, sk) - if test.existKey == "" && test.passedKey == "" { // new key generated - require.NoError(t, err) - require.GreaterOrEqual(t, len(pk), 600) - return - } - if test.existKey != "" && test.passedKey == "" { // exist and not passed in env - existKeyByte, err := base64.StdEncoding.DecodeString(test.existKey) - require.NoError(t, err) - require.Equal(t, string(existKeyByte), string(rsaencryption.PrivateKeyToByte(sk))) - return - } - // not exist && passed and exist && passed - passedKeyByte, err := base64.StdEncoding.DecodeString(test.passedKey) - require.NoError(t, err) - require.Equal(t, string(passedKeyByte), string(rsaencryption.PrivateKeyToByte(sk))) - }) - } + require.Equal(t, parsedPrivKeyHash, extractedHash) } func TestDropRegistryData(t *testing.T) { diff --git a/operator/validator/controller.go b/operator/validator/controller.go index 6558663109..5f88af387a 100644 --- a/operator/validator/controller.go +++ b/operator/validator/controller.go @@ -2,7 +2,6 @@ package validator import ( "context" - "crypto/rsa" "encoding/hex" "encoding/json" "fmt" @@ -50,9 +49,6 @@ const ( networkRouterConcurrency = 2048 ) -// ShareEncryptionKeyProvider is a function that returns the operator private key -type ShareEncryptionKeyProvider = func() (*rsa.PrivateKey, bool, error) - type GetRecipientDataFunc func(r basedb.Reader, owner common.Address) (*registrystorage.RecipientData, bool, error) // ShareEventHandlerFunc is a function that handles event in an extended mode @@ -69,7 +65,6 @@ type ControllerOptions struct { BeaconNetwork beaconprotocol.Network Network P2PNetwork Beacon beaconprotocol.BeaconNode - ShareEncryptionKeyProvider ShareEncryptionKeyProvider FullNode bool `yaml:"FullNode" env:"FULLNODE" env-default:"false" env-description:"Save decided history rather than just highest messages"` Exporter bool `yaml:"Exporter" env:"EXPORTER" env-default:"false" env-description:""` BuilderProposals bool `yaml:"BuilderProposals" env:"BUILDER_PROPOSALS" env-default:"false" env-description:"Use external builders to produce blocks"` @@ -160,9 +155,8 @@ type controller struct { beacon beaconprotocol.BeaconNode keyManager spectypes.KeyManager - shareEncryptionKeyProvider ShareEncryptionKeyProvider - operatorData *registrystorage.OperatorData - operatorDataMutex sync.RWMutex + operatorData *registrystorage.OperatorData + operatorDataMutex sync.RWMutex validatorOptions validator.Options validatorsMap *validatorsmap.ValidatorsMap @@ -234,18 +228,17 @@ func NewController(logger *zap.Logger, options ControllerOptions) Controller { } ctrl := controller{ - logger: logger.Named(logging.NameController), - metrics: metrics, - sharesStorage: options.RegistryStorage.Shares(), - operatorsStorage: options.RegistryStorage, - recipientsStorage: options.RegistryStorage, - ibftStorageMap: options.StorageMap, - context: options.Context, - beacon: options.Beacon, - shareEncryptionKeyProvider: options.ShareEncryptionKeyProvider, - operatorData: options.OperatorData, - keyManager: options.KeyManager, - network: options.Network, + logger: logger.Named(logging.NameController), + metrics: metrics, + sharesStorage: options.RegistryStorage.Shares(), + operatorsStorage: options.RegistryStorage, + recipientsStorage: options.RegistryStorage, + ibftStorageMap: options.StorageMap, + context: options.Context, + beacon: options.Beacon, + operatorData: options.OperatorData, + keyManager: options.KeyManager, + network: options.Network, validatorsMap: options.ValidatorsMap, validatorOptions: validatorOptions, diff --git a/operator/validator/controller_test.go b/operator/validator/controller_test.go index cb3bff5b80..f4e65ca712 100644 --- a/operator/validator/controller_test.go +++ b/operator/validator/controller_test.go @@ -1028,21 +1028,20 @@ func TestGetIndices(t *testing.T) { func setupController(logger *zap.Logger, opts MockControllerOptions) controller { return controller{ - metadataUpdateInterval: 0, - shareEncryptionKeyProvider: nil, - logger: logger, - beacon: opts.beacon, - network: opts.network, - metrics: opts.metrics, - keyManager: opts.keyManager, - ibftStorageMap: opts.StorageMap, - operatorData: opts.operatorData, - sharesStorage: opts.sharesStorage, - validatorsMap: opts.validatorsMap, - context: context.Background(), - validatorOptions: opts.validatorOptions, - recipientsStorage: opts.recipientsStorage, - messageRouter: newMessageRouter(logger), + metadataUpdateInterval: 0, + logger: logger, + beacon: opts.beacon, + network: opts.network, + metrics: opts.metrics, + keyManager: opts.keyManager, + ibftStorageMap: opts.StorageMap, + operatorData: opts.operatorData, + sharesStorage: opts.sharesStorage, + validatorsMap: opts.validatorsMap, + context: context.Background(), + validatorOptions: opts.validatorOptions, + recipientsStorage: opts.recipientsStorage, + messageRouter: newMessageRouter(logger), messageWorker: worker.NewWorker(logger, &worker.Config{ Ctx: context.Background(), WorkersCount: 1, diff --git a/utils/rsaencryption/rsa_encryption.go b/utils/rsaencryption/rsa_encryption.go index 01e21e7058..7fda24a1b0 100644 --- a/utils/rsaencryption/rsa_encryption.go +++ b/utils/rsaencryption/rsa_encryption.go @@ -11,13 +11,9 @@ import ( "crypto/sha256" "crypto/x509" "encoding/base64" - "encoding/json" "encoding/pem" "fmt" - "strings" - "github.com/pkg/errors" - "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4" ) var keySize = 2048 @@ -61,16 +57,6 @@ func DecodeKey(sk *rsa.PrivateKey, hash []byte) ([]byte, error) { return decryptedKey, nil } -// ConvertPemToPrivateKey return rsa private key from secret key -func ConvertPemToPrivateKey(skPem string) (*rsa.PrivateKey, error) { - block, _ := pem.Decode([]byte(skPem)) - if block == nil { - return nil, errors.New("decode PEM block") - } - b := block.Bytes - return parsePrivateKey(b) -} - // HashRsaKey return sha256 hash of rsa private key func HashRsaKey(keyBytes []byte) (string, error) { hash := sha256.Sum256(keyBytes) @@ -78,37 +64,17 @@ func HashRsaKey(keyBytes []byte) (string, error) { return keyString, nil } -// ConvertEncryptedPemToPrivateKey return rsa private key from secret key -func ConvertEncryptedPemToPrivateKey(pemData []byte, password string) (*rsa.PrivateKey, error) { - if strings.TrimSpace(password) == "" { - return nil, errors.New("Password required for encrypted PEM block") - } - - // Unmarshal the JSON-encoded data - var data map[string]interface{} - if err := json.Unmarshal(pemData, &data); err != nil { - return nil, fmt.Errorf("parse JSON data: %w", err) - } - - // Decrypt the private key using keystorev4 - decryptedBytes, err := keystorev4.New().Decrypt(data, password) - if err != nil { - return nil, fmt.Errorf("decrypt private key: %w", err) - } - - // Parse the decrypted PEM data - block, _ := pem.Decode(decryptedBytes) +func PemToPrivateKey(pemData []byte) (*rsa.PrivateKey, error) { + block, _ := pem.Decode(pemData) if block == nil { - return nil, errors.New("parse PEM block") + return nil, errors.New("failed to parse PEM block containing the private key") } - // Parse the RSA private key - rsaKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) - if err != nil { - return nil, fmt.Errorf("parse RSA private key: %w", err) + if block.Type != "RSA PRIVATE KEY" { + return nil, errors.New("invalid PEM block type") } - return rsaKey, nil + return parsePrivateKey(block.Bytes) } func parsePrivateKey(derBytes []byte) (*rsa.PrivateKey, error) { @@ -149,8 +115,8 @@ func PrivateKeyToByte(sk *rsa.PrivateKey) []byte { } // ExtractPublicKey get public key from private key and return base64 encoded public key -func ExtractPublicKey(sk *rsa.PrivateKey) (string, error) { - pkBytes, err := x509.MarshalPKIXPublicKey(&sk.PublicKey) +func ExtractPublicKey(pk *rsa.PublicKey) (string, error) { + pkBytes, err := x509.MarshalPKIXPublicKey(pk) if err != nil { return "", errors.Wrap(err, "Failed to marshal private key") } diff --git a/utils/rsaencryption/rsa_encryption_test.go b/utils/rsaencryption/rsa_encryption_test.go index 52d32f95ad..a078a1bcd5 100644 --- a/utils/rsaencryption/rsa_encryption_test.go +++ b/utils/rsaencryption/rsa_encryption_test.go @@ -1,101 +1,66 @@ package rsaencryption import ( - "crypto/rand" - "crypto/rsa" - "crypto/x509" "encoding/base64" - "encoding/json" - "encoding/pem" - "os" "testing" testingspace "github.com/bloxapp/ssv/utils/rsaencryption/testingspace" "github.com/stretchr/testify/require" - "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4" ) func TestGenerateKeys(t *testing.T) { _, skByte, err := GenerateKeys() require.NoError(t, err) - sk, err := ConvertPemToPrivateKey(string(skByte)) + + sk, err := PemToPrivateKey(skByte) require.NoError(t, err) require.Equal(t, 2048, sk.N.BitLen()) require.NoError(t, sk.Validate()) } func TestDecodeKey(t *testing.T) { - sk, err := ConvertPemToPrivateKey(testingspace.SkPem) + sk, err := PemToPrivateKey([]byte(testingspace.SkPem)) require.NoError(t, err) + hash, err := base64.StdEncoding.DecodeString(testingspace.EncryptedKeyBase64) require.NoError(t, err) + key, err := DecodeKey(sk, hash) require.NoError(t, err) require.Equal(t, "626d6a13ae5b1458c310700941764f3841f279f9c8de5f4ba94abd01dc082517", string(key)) } -func TestExtractPublicKey(t *testing.T) { +func TestExtractPrivateKey(t *testing.T) { _, skByte, err := GenerateKeys() require.NoError(t, err) - sk, err := ConvertPemToPrivateKey(string(skByte)) - require.NoError(t, err) - pk, err := ExtractPublicKey(sk) - require.NoError(t, err) - require.NotNil(t, pk) -} - -func TestPrivateKeyToByte(t *testing.T) { - _, skByte, err := GenerateKeys() - require.NoError(t, err) - sk, err := ConvertPemToPrivateKey(string(skByte)) - require.NoError(t, err) - b := PrivateKeyToByte(sk) - require.NotNil(t, b) - require.Greater(t, len(b), 1024) -} -func TestConvertEncryptedPemToPrivateKey(t *testing.T) { - fileName := os.TempDir() + "/encrypted_private_key.json" - defer func() { - if err := os.Remove(fileName); err != nil { - t.Logf("Could not delete encrypted private key file: %v", err) - } - }() - password := "123123123" - privateKey, err := rsa.GenerateKey(rand.Reader, keySize) + sk, err := PemToPrivateKey(skByte) require.NoError(t, err) - privDER := x509.MarshalPKCS1PrivateKey(privateKey) - - privateBlock := pem.Block{ - Type: "RSA PRIVATE KEY", - Bytes: privDER, - } - - pemBytes := pem.EncodeToMemory(&privateBlock) + privKey := ExtractPrivateKey(sk) + require.NotNil(t, privKey) +} - encryptedData, err := keystorev4.New().Encrypt(pemBytes, password) +func TestExtractPublicKey(t *testing.T) { + _, skByte, err := GenerateKeys() require.NoError(t, err) - encryptedJSON, err := json.Marshal(encryptedData) + sk, err := PemToPrivateKey(skByte) require.NoError(t, err) - err = os.WriteFile(fileName, encryptedJSON, 0600) + pk, err := ExtractPublicKey(&sk.PublicKey) require.NoError(t, err) + require.NotNil(t, pk) +} - // Read and decrypt the private key - encryptedJSON, err = os.ReadFile(fileName) +func TestPrivateKeyToByte(t *testing.T) { + _, skByte, err := GenerateKeys() require.NoError(t, err) - key, err := ConvertEncryptedPemToPrivateKey(encryptedJSON, password) + sk, err := PemToPrivateKey(skByte) require.NoError(t, err) - require.Equal(t, privateKey, key) - - // Convert encrypted PEM to private key. - _, err = ConvertEncryptedPemToPrivateKey(encryptedJSON, password+"1") - require.Error(t, err) - // Test with incorrect password. - _, err = ConvertEncryptedPemToPrivateKey(pemBytes, "") - require.Error(t, err) + b := PrivateKeyToByte(sk) + require.NotNil(t, b) + require.Greater(t, len(b), 1024) } From 1c58d5a5f5128f035c439eac8b5acb4e63259676 Mon Sep 17 00:00:00 2001 From: Nikita Kryuchkov Date: Sun, 31 Mar 2024 18:48:27 +0400 Subject: [PATCH 3/5] OperatorData store (#1356) * OperatorData storage * Change formatting * Fix SetOperatorData * Attempt to fix unit tests * Remove tests workaround in goclient * Attempt to fix p2p tests * Deploy to all stage nodes * Revert "Deploy to all stage nodes" This reverts commit db28d4726323a02b49ab8aedfa51ddf4035b1c83. * early nil check for operatorData * Deploy to all stage nodes * Revert "Deploy to all stage nodes" This reverts commit db28d4726323a02b49ab8aedfa51ddf4035b1c83. --------- Co-authored-by: moshe-blox Co-authored-by: y0sher --- beacon/goclient/goclient.go | 10 +- beacon/goclient/goclient_test.go | 4 +- beacon/goclient/proposer.go | 9 +- cli/operator/node.go | 36 +++---- eth/ethtest/utils_test.go | 22 ++--- eth/eventhandler/event_handler.go | 14 +-- eth/eventhandler/event_handler_test.go | 28 +++--- eth/eventhandler/handlers.go | 24 +++-- eth/eventsyncer/event_syncer_test.go | 14 +-- message/validation/consensus_validation.go | 10 +- message/validation/partial_validation.go | 10 +- message/validation/validation.go | 14 ++- network/p2p/config.go | 5 +- network/p2p/p2p.go | 6 +- network/p2p/p2p_pubsub.go | 6 +- network/p2p/p2p_validation_test.go | 12 +-- network/p2p/test_utils.go | 5 + operator/datastore/data_store.go | 94 +++++++++++++++++++ operator/datastore/data_store_test.go | 104 +++++++++++++++++++++ operator/fee_recipient/controller.go | 14 ++- operator/fee_recipient/controller_test.go | 15 +-- operator/node.go | 3 +- operator/validator/controller.go | 39 +++----- operator/validator/controller_test.go | 71 ++++++-------- operator/validator/mocks/controller.go | 40 -------- operator/validator/task_executor_test.go | 22 +++-- 26 files changed, 385 insertions(+), 246 deletions(-) create mode 100644 operator/datastore/data_store.go create mode 100644 operator/datastore/data_store_test.go diff --git a/beacon/goclient/goclient.go b/beacon/goclient/goclient.go index 8d8e6eec65..65163b089b 100644 --- a/beacon/goclient/goclient.go +++ b/beacon/goclient/goclient.go @@ -20,7 +20,7 @@ import ( "go.uber.org/zap" "github.com/bloxapp/ssv/logging/fields" - "github.com/bloxapp/ssv/message/validation" + operatordatastore "github.com/bloxapp/ssv/operator/datastore" "github.com/bloxapp/ssv/operator/slotticker" beaconprotocol "github.com/bloxapp/ssv/protocol/v2/blockchain/beacon" ) @@ -150,7 +150,7 @@ type goClient struct { nodeClient NodeClient graffiti []byte gasLimit uint64 - getOperatorID validation.OperatorIDGetter + operatorDataStore operatordatastore.OperatorDataStore registrationMu sync.Mutex registrationLastSlot phase0.Slot registrationCache map[phase0.BLSPubKey]*api.VersionedSignedValidatorRegistration @@ -162,7 +162,7 @@ type goClient struct { func New( logger *zap.Logger, opt beaconprotocol.Options, - getOperatorID validation.OperatorIDGetter, + operatorDataStore operatordatastore.OperatorDataStore, slotTickerProvider slotticker.Provider, ) (beaconprotocol.BeaconNode, error) { logger.Info("consensus client: connecting", fields.Address(opt.BeaconNodeAddr), fields.Network(string(opt.Network.BeaconNetwork))) @@ -194,13 +194,12 @@ func New( client: httpClient.(*eth2clienthttp.Service), graffiti: opt.Graffiti, gasLimit: opt.GasLimit, - getOperatorID: getOperatorID, + operatorDataStore: operatorDataStore, registrationCache: map[phase0.BLSPubKey]*api.VersionedSignedValidatorRegistration{}, commonTimeout: commonTimeout, longTimeout: longTimeout, } - // Get the node's version and client. nodeVersionResp, err := client.client.NodeVersion(opt.Context, &api.NodeVersionOpts{}) if err != nil { return nil, fmt.Errorf("failed to get node version: %w", err) @@ -218,7 +217,6 @@ func New( zap.String("version", client.nodeVersion), ) - // Start registration submitter. go client.registrationSubmitter(slotTickerProvider) return client, nil diff --git a/beacon/goclient/goclient_test.go b/beacon/goclient/goclient_test.go index 55833b815f..416725fb03 100644 --- a/beacon/goclient/goclient_test.go +++ b/beacon/goclient/goclient_test.go @@ -14,8 +14,10 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap" + operatordatastore "github.com/bloxapp/ssv/operator/datastore" "github.com/bloxapp/ssv/operator/slotticker" "github.com/bloxapp/ssv/protocol/v2/blockchain/beacon" + registrystorage "github.com/bloxapp/ssv/registry/storage" ) func TestTimeouts(t *testing.T) { @@ -94,7 +96,7 @@ func mockClient(t *testing.T, ctx context.Context, serverURL string, commonTimeo CommonTimeout: commonTimeout, LongTimeout: longTimeout, }, - func() types.OperatorID { return 0 }, + operatordatastore.New(®istrystorage.OperatorData{ID: 1}), func() slotticker.SlotTicker { return slotticker.New(zap.NewNop(), slotticker.Config{ SlotDuration: 12 * time.Second, diff --git a/beacon/goclient/proposer.go b/beacon/goclient/proposer.go index 82e262a5c8..3be45e37f7 100644 --- a/beacon/goclient/proposer.go +++ b/beacon/goclient/proposer.go @@ -14,6 +14,7 @@ import ( "github.com/attestantio/go-eth2-client/spec/capella" "github.com/attestantio/go-eth2-client/spec/deneb" "github.com/attestantio/go-eth2-client/spec/phase0" + spectypes "github.com/bloxapp/ssv-spec/types" ssz "github.com/ferranbt/fastssz" "go.uber.org/zap" @@ -378,18 +379,20 @@ func (gc *goClient) createValidatorRegistration(pubkey []byte, feeRecipient bell } func (gc *goClient) registrationSubmitter(slotTickerProvider slotticker.Provider) { + operatorID := gc.operatorDataStore.AwaitOperatorID() + ticker := slotTickerProvider() for { select { case <-gc.ctx.Done(): return case <-ticker.Next(): - gc.submitRegistrationsFromCache(ticker.Slot()) + gc.submitRegistrationsFromCache(ticker.Slot(), operatorID) } } } -func (gc *goClient) submitRegistrationsFromCache(currentSlot phase0.Slot) { +func (gc *goClient) submitRegistrationsFromCache(currentSlot phase0.Slot, operatorID spectypes.OperatorID) { slotsPerEpoch := gc.network.SlotsPerEpoch() // Lock: @@ -398,7 +401,7 @@ func (gc *goClient) submitRegistrationsFromCache(currentSlot phase0.Slot) { gc.registrationMu.Lock() slotsSinceLastRegistration := currentSlot - gc.registrationLastSlot - operatorSubmissionSlotModulo := gc.getOperatorID() % slotsPerEpoch + operatorSubmissionSlotModulo := operatorID % slotsPerEpoch hasRegistrations := len(gc.registrationCache) != 0 operatorSubmissionSlot := uint64(currentSlot)%slotsPerEpoch == operatorSubmissionSlotModulo diff --git a/cli/operator/node.go b/cli/operator/node.go index 762f431fdd..0b877ea83b 100644 --- a/cli/operator/node.go +++ b/cli/operator/node.go @@ -21,8 +21,6 @@ import ( "github.com/spf13/cobra" "go.uber.org/zap" - p2pv1 "github.com/bloxapp/ssv/network/p2p" - "github.com/bloxapp/ssv/api/handlers" apiserver "github.com/bloxapp/ssv/api/server" "github.com/bloxapp/ssv/beacon/goclient" @@ -43,9 +41,11 @@ import ( "github.com/bloxapp/ssv/migrations" "github.com/bloxapp/ssv/monitoring/metrics" "github.com/bloxapp/ssv/monitoring/metricsreporter" + p2pv1 "github.com/bloxapp/ssv/network/p2p" "github.com/bloxapp/ssv/networkconfig" "github.com/bloxapp/ssv/nodeprobe" "github.com/bloxapp/ssv/operator" + operatordatastore "github.com/bloxapp/ssv/operator/datastore" "github.com/bloxapp/ssv/operator/duties/dutystore" "github.com/bloxapp/ssv/operator/keys" "github.com/bloxapp/ssv/operator/slotticker" @@ -156,6 +156,7 @@ var StartNodeCmd = &cobra.Command{ cfg.P2pNetworkConfig.OperatorSigner = operatorPrivKey nodeStorage, operatorData := setupOperatorStorage(logger, db, operatorPrivKey, operatorPrivKeyText) + operatorDataStore := operatordatastore.New(operatorData) usingLocalEvents := len(cfg.LocalEventsPath) != 0 @@ -190,12 +191,7 @@ var StartNodeCmd = &cobra.Command{ cfg.ConsensusClient.GasLimit = spectypes.DefaultGasLimit cfg.ConsensusClient.Network = networkConfig.Beacon.GetNetwork() - var validatorCtrl validator.Controller - getOperatorID := func() spectypes.OperatorID { - return validatorCtrl.GetOperatorID() - } - - consensusClient := setupConsensusClient(logger, getOperatorID, slotTickerProvider) + consensusClient := setupConsensusClient(logger, operatorDataStore, slotTickerProvider) executionClient, err := executionclient.New( cmd.Context(), @@ -215,7 +211,7 @@ var StartNodeCmd = &cobra.Command{ cfg.P2pNetworkConfig.Permissioned = permissioned cfg.P2pNetworkConfig.NodeStorage = nodeStorage cfg.P2pNetworkConfig.OperatorPubKeyHash = format.OperatorID(operatorData.PublicKey) - cfg.P2pNetworkConfig.GetOperatorID = getOperatorID + cfg.P2pNetworkConfig.OperatorDataStore = operatorDataStore cfg.P2pNetworkConfig.FullNode = cfg.SSVOptions.ValidatorOptions.FullNode cfg.P2pNetworkConfig.Network = networkConfig @@ -230,7 +226,7 @@ var StartNodeCmd = &cobra.Command{ validation.WithLogger(logger), validation.WithMetrics(metricsReporter), validation.WithDutyStore(dutyStore), - validation.WithOwnOperatorID(getOperatorID), + validation.WithOwnOperatorID(operatorDataStore), ) cfg.P2pNetworkConfig.Metrics = metricsReporter @@ -253,7 +249,7 @@ var StartNodeCmd = &cobra.Command{ cfg.SSVOptions.ValidatorOptions.KeyManager = keyManager cfg.SSVOptions.ValidatorOptions.ValidatorsMap = validatorsMap - cfg.SSVOptions.ValidatorOptions.OperatorData = operatorData + cfg.SSVOptions.ValidatorOptions.OperatorDataStore = operatorDataStore cfg.SSVOptions.ValidatorOptions.RegistryStorage = nodeStorage cfg.SSVOptions.ValidatorOptions.RecipientsStorage = nodeStorage cfg.SSVOptions.ValidatorOptions.GasLimit = cfg.ConsensusClient.GasLimit @@ -286,7 +282,7 @@ var StartNodeCmd = &cobra.Command{ cfg.SSVOptions.ValidatorOptions.Metrics = metricsReporter cfg.SSVOptions.Metrics = metricsReporter - validatorCtrl = validator.NewController(logger, cfg.SSVOptions.ValidatorOptions) + validatorCtrl := validator.NewController(logger, cfg.SSVOptions.ValidatorOptions) cfg.SSVOptions.ValidatorController = validatorCtrl operatorNode = operator.New(logger, cfg.SSVOptions, slotTickerProvider) @@ -325,6 +321,7 @@ var StartNodeCmd = &cobra.Command{ metricsReporter, networkConfig, nodeStorage, + operatorDataStore, operatorPrivKey, ) nodeProber.AddNode("event syncer", eventSyncer) @@ -530,6 +527,9 @@ func setupOperatorStorage(logger *zap.Logger, db basedb.Database, configPrivKey PublicKey: encodedPubKey, } } + if operatorData == nil { + logger.Fatal("invalid operator data in database: nil") + } return nodeStorage, operatorData } @@ -577,10 +577,10 @@ func setupP2P(logger *zap.Logger, db basedb.Database, mr metricsreporter.Metrics func setupConsensusClient( logger *zap.Logger, - getOperatorID validation.OperatorIDGetter, + operatorDataStore operatordatastore.OperatorDataStore, slotTickerProvider slotticker.Provider, ) beaconprotocol.BeaconNode { - cl, err := goclient.New(logger, cfg.ConsensusClient, getOperatorID, slotTickerProvider) + cl, err := goclient.New(logger, cfg.ConsensusClient, operatorDataStore, slotTickerProvider) if err != nil { logger.Fatal("failed to create beacon go-client", zap.Error(err), fields.Address(cfg.ConsensusClient.BeaconNodeAddr)) @@ -598,6 +598,7 @@ func setupEventHandling( metricsReporter metricsreporter.MetricsReporter, networkConfig networkconfig.NetworkConfig, nodeStorage operatorstorage.Storage, + operatorDataStore operatordatastore.OperatorDataStore, operatorDecrypter keys.OperatorDecrypter, ) *eventsyncer.EventSyncer { eventFilterer, err := executionClient.Filterer() @@ -612,7 +613,7 @@ func setupEventHandling( eventParser, validatorCtrl, networkConfig, - validatorCtrl, + operatorDataStore, operatorDecrypter, cfg.SSVOptions.ValidatorOptions.KeyManager, cfg.SSVOptions.ValidatorOptions.Beacon, @@ -676,10 +677,11 @@ func setupEventHandling( if err != nil { logger.Error("failed to get operators", zap.Error(err)) } - operatorID := validatorCtrl.GetOperatorID() + operatorValidators := 0 liquidatedValidators := 0 - if operatorID != 0 { + operatorID := operatorDataStore.GetOperatorID() + if operatorDataStore.OperatorIDReady() { for _, share := range shares { if share.BelongsToOperator(operatorID) { operatorValidators++ diff --git a/eth/ethtest/utils_test.go b/eth/ethtest/utils_test.go index 84978780b2..452930c287 100644 --- a/eth/ethtest/utils_test.go +++ b/eth/ethtest/utils_test.go @@ -21,6 +21,7 @@ import ( "github.com/bloxapp/ssv/eth/simulator" ibftstorage "github.com/bloxapp/ssv/ibft/storage" "github.com/bloxapp/ssv/networkconfig" + operatordatastore "github.com/bloxapp/ssv/operator/datastore" "github.com/bloxapp/ssv/operator/keys" operatorstorage "github.com/bloxapp/ssv/operator/storage" "github.com/bloxapp/ssv/operator/validator" @@ -159,6 +160,7 @@ func setupEventHandler( storageMap := ibftstorage.NewStores() nodeStorage, operatorData := setupOperatorStorage(logger, db, operator, ownerAddress) + operatorDataStore := operatordatastore.New(operatorData) testNetworkConfig := networkconfig.TestNetwork keyManager, err := ekm.NewETHKeyManagerSigner(logger, db, testNetworkConfig, true, "") @@ -184,7 +186,7 @@ func setupEventHandler( parser, validatorCtrl, testNetworkConfig, - validatorCtrl, + operatorDataStore, operator.privateKey, keyManager, bc, @@ -197,20 +199,16 @@ func setupEventHandler( return nil, nil, nil, nil, err } - validatorCtrl.EXPECT().GetOperatorData().Return(operatorData).AnyTimes() - validatorCtrl.EXPECT().GetOperatorID().Return(operatorData.ID).AnyTimes() - validatorCtrl.EXPECT().SetOperatorData(gomock.Any()).AnyTimes() - return eh, validatorCtrl, ctrl, nodeStorage, nil } validatorCtrl := validator.NewController(logger, validator.ControllerOptions{ - Context: ctx, - DB: db, - RegistryStorage: nodeStorage, - KeyManager: keyManager, - StorageMap: storageMap, - OperatorData: operatorData, + Context: ctx, + DB: db, + RegistryStorage: nodeStorage, + KeyManager: keyManager, + StorageMap: storageMap, + OperatorDataStore: operatorDataStore, }) parser := eventparser.New(contractFilterer) @@ -220,7 +218,7 @@ func setupEventHandler( parser, validatorCtrl, testNetworkConfig, - validatorCtrl, + operatorDataStore, operator.privateKey, keyManager, bc, diff --git a/eth/eventhandler/event_handler.go b/eth/eventhandler/event_handler.go index a6804fd02c..4c64b13268 100644 --- a/eth/eventhandler/event_handler.go +++ b/eth/eventhandler/event_handler.go @@ -21,11 +21,11 @@ import ( qbftstorage "github.com/bloxapp/ssv/ibft/storage" "github.com/bloxapp/ssv/logging/fields" "github.com/bloxapp/ssv/networkconfig" + operatordatastore "github.com/bloxapp/ssv/operator/datastore" "github.com/bloxapp/ssv/operator/keys" nodestorage "github.com/bloxapp/ssv/operator/storage" beaconprotocol "github.com/bloxapp/ssv/protocol/v2/blockchain/beacon" ssvtypes "github.com/bloxapp/ssv/protocol/v2/types" - "github.com/bloxapp/ssv/registry/storage" "github.com/bloxapp/ssv/storage/basedb" ) @@ -56,18 +56,12 @@ type taskExecutor interface { ExitValidator(pubKey phase0.BLSPubKey, blockNumber uint64, validatorIndex phase0.ValidatorIndex) error } -type OperatorData interface { - GetOperatorData() *storage.OperatorData - SetOperatorData(*storage.OperatorData) - GetOperatorID() spectypes.OperatorID -} - type EventHandler struct { nodeStorage nodestorage.Storage taskExecutor taskExecutor eventParser eventparser.Parser networkConfig networkconfig.NetworkConfig - operatorData OperatorData + operatorDataStore operatordatastore.OperatorDataStore operatorDecrypter keys.OperatorDecrypter keyManager spectypes.KeyManager beacon beaconprotocol.BeaconNode @@ -83,7 +77,7 @@ func New( eventParser eventparser.Parser, taskExecutor taskExecutor, networkConfig networkconfig.NetworkConfig, - operatorData OperatorData, + operatorDataStore operatordatastore.OperatorDataStore, operatorDecrypter keys.OperatorDecrypter, keyManager spectypes.KeyManager, beacon beaconprotocol.BeaconNode, @@ -95,7 +89,7 @@ func New( taskExecutor: taskExecutor, eventParser: eventParser, networkConfig: networkConfig, - operatorData: operatorData, + operatorDataStore: operatorDataStore, operatorDecrypter: operatorDecrypter, keyManager: keyManager, beacon: beacon, diff --git a/eth/eventhandler/event_handler_test.go b/eth/eventhandler/event_handler_test.go index 90f06a6f7f..f487afd140 100644 --- a/eth/eventhandler/event_handler_test.go +++ b/eth/eventhandler/event_handler_test.go @@ -12,7 +12,6 @@ import ( "github.com/attestantio/go-eth2-client/spec/phase0" ekmcore "github.com/bloxapp/eth2-key-manager/core" - spectypes "github.com/bloxapp/ssv-spec/types" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" ethcommon "github.com/ethereum/go-ethereum/common" @@ -34,6 +33,7 @@ import ( "github.com/bloxapp/ssv/eth/simulator/simcontract" ibftstorage "github.com/bloxapp/ssv/ibft/storage" "github.com/bloxapp/ssv/networkconfig" + operatordatastore "github.com/bloxapp/ssv/operator/datastore" "github.com/bloxapp/ssv/operator/keys" operatorstorage "github.com/bloxapp/ssv/operator/storage" "github.com/bloxapp/ssv/operator/validator" @@ -1282,6 +1282,8 @@ func setupEventHandler(t *testing.T, ctx context.Context, logger *zap.Logger, ne storageMap := ibftstorage.NewStores() nodeStorage, operatorData := setupOperatorStorage(logger, db, operator) + operatorDataStore := operatordatastore.New(operatorData) + if network == nil { network = &networkconfig.NetworkConfig{ Beacon: utils.SetupMockBeaconNetwork(t, &utils.SlotValue{}), @@ -1310,7 +1312,7 @@ func setupEventHandler(t *testing.T, ctx context.Context, logger *zap.Logger, ne parser, validatorCtrl, *network, - validatorCtrl, + operatorDataStore, operator.privateKey, keyManager, bc, @@ -1321,9 +1323,6 @@ func setupEventHandler(t *testing.T, ctx context.Context, logger *zap.Logger, ne return nil, nil, err } - validatorCtrl.EXPECT().GetOperatorData().Return(®istrystorage.OperatorData{}).AnyTimes() - validatorCtrl.EXPECT().GetOperatorID().Return(spectypes.OperatorID(0)).AnyTimes() - return eh, validatorCtrl, nil } ctrl := gomock.NewController(t) @@ -1331,13 +1330,13 @@ func setupEventHandler(t *testing.T, ctx context.Context, logger *zap.Logger, ne bc := beacon.NewMockBeaconNode(ctrl) validatorCtrl := validator.NewController(logger, validator.ControllerOptions{ - Context: ctx, - DB: db, - RegistryStorage: nodeStorage, - KeyManager: keyManager, - StorageMap: storageMap, - OperatorData: operatorData, - ValidatorsMap: validatorsmap.New(ctx), + Context: ctx, + DB: db, + RegistryStorage: nodeStorage, + KeyManager: keyManager, + StorageMap: storageMap, + OperatorDataStore: operatorDataStore, + ValidatorsMap: validatorsmap.New(ctx), }) contractFilterer, err := contract.NewContractFilterer(ethcommon.Address{}, nil) @@ -1350,7 +1349,7 @@ func setupEventHandler(t *testing.T, ctx context.Context, logger *zap.Logger, ne parser, validatorCtrl, *network, - validatorCtrl, + operatorDataStore, operator.privateKey, keyManager, bc, @@ -1397,9 +1396,6 @@ func setupOperatorStorage(logger *zap.Logger, db basedb.Database, operator *test logger.Fatal("couldn't get operator data by public key", zap.Error(err)) } - if err != nil { - logger.Fatal("couldn't get operator data by public key", zap.Error(err)) - } if !found { operatorData = ®istrystorage.OperatorData{ PublicKey: encodedPubKey, diff --git a/eth/eventhandler/handlers.go b/eth/eventhandler/handlers.go index 1edf4ba03f..b2cc588e89 100644 --- a/eth/eventhandler/handlers.go +++ b/eth/eventhandler/handlers.go @@ -54,7 +54,7 @@ func (eh *EventHandler) handleOperatorAdded(txn basedb.Txn, event *contract.Cont } // throw an error if there is an existing operator with the same public key and different operator id - operatorData := eh.operatorData.GetOperatorData() + operatorData := eh.operatorDataStore.GetOperatorData() if operatorData.ID != 0 && bytes.Equal(operatorData.PublicKey, event.PublicKey) && operatorData.ID != event.OperatorId { logger.Warn("malformed event: operator registered with the same operator public key", zap.Uint64("expected_operator_id", operatorData.ID)) @@ -71,10 +71,9 @@ func (eh *EventHandler) handleOperatorAdded(txn basedb.Txn, event *contract.Cont return nil } - ownOperator := bytes.Equal(event.PublicKey, operatorData.PublicKey) - if ownOperator { - eh.operatorData.SetOperatorData(od) - logger = logger.With(zap.Bool("own_operator", ownOperator)) + if bytes.Equal(event.PublicKey, operatorData.PublicKey) { + eh.operatorDataStore.SetOperatorData(od) + logger = logger.With(zap.Bool("own_operator", true)) } eh.metrics.OperatorPublicKey(od.ID, od.PublicKey) @@ -207,11 +206,10 @@ func (eh *EventHandler) handleValidatorAdded(txn basedb.Txn, event *contract.Con return nil, &MalformedEventError{Err: ErrShareBelongsToDifferentOwner} } - isOperatorShare := validatorShare.BelongsToOperator(eh.operatorData.GetOperatorID()) - if isOperatorShare { + if validatorShare.BelongsToOperator(eh.operatorDataStore.GetOperatorID()) { eh.metrics.ValidatorInactive(event.PublicKey) ownShare = validatorShare - logger = logger.With(zap.Bool("own_validator", isOperatorShare)) + logger = logger.With(zap.Bool("own_validator", true)) } logger.Debug("processed event") @@ -234,7 +232,7 @@ func (eh *EventHandler) handleShareCreation( return nil, fmt.Errorf("could not extract validator share from event: %w", err) } - if share.BelongsToOperator(eh.operatorData.GetOperatorID()) { + if share.BelongsToOperator(eh.operatorDataStore.GetOperatorID()) { if shareSecret == nil { return nil, errors.New("could not decode shareSecret") } @@ -269,7 +267,7 @@ func (eh *EventHandler) validatorAddedEventToShare( validatorShare.ValidatorPubKey = publicKey.Serialize() validatorShare.OwnerAddress = event.Owner - selfOperatorID := eh.operatorData.GetOperatorID() + selfOperatorID := eh.operatorDataStore.GetOperatorID() var shareSecret *bls.SecretKey committee := make([]*spectypes.Operator, 0) @@ -357,7 +355,7 @@ func (eh *EventHandler) handleValidatorRemoved(txn basedb.Txn, event *contract.C return nil, fmt.Errorf("could not remove validator share: %w", err) } - isOperatorShare := share.BelongsToOperator(eh.operatorData.GetOperatorID()) + isOperatorShare := share.BelongsToOperator(eh.operatorDataStore.GetOperatorID()) if isOperatorShare || eh.fullNode { logger = logger.With(zap.String("validator_pubkey", hex.EncodeToString(share.ValidatorPubKey))) } @@ -482,7 +480,7 @@ func (eh *EventHandler) handleValidatorExited(txn basedb.Txn, event *contract.Co return nil, &MalformedEventError{Err: ErrShareBelongsToDifferentOwner} } - if !share.BelongsToOperator(eh.operatorData.GetOperatorID()) { + if !share.BelongsToOperator(eh.operatorDataStore.GetOperatorID()) { return nil, nil } @@ -528,7 +526,7 @@ func (eh *EventHandler) processClusterEvent( updatedPubKeys := make([]string, 0) for _, share := range shares { - isOperatorShare := share.BelongsToOperator(eh.operatorData.GetOperatorID()) + isOperatorShare := share.BelongsToOperator(eh.operatorDataStore.GetOperatorID()) if isOperatorShare || eh.fullNode { updatedPubKeys = append(updatedPubKeys, hex.EncodeToString(share.ValidatorPubKey)) } diff --git a/eth/eventsyncer/event_syncer_test.go b/eth/eventsyncer/event_syncer_test.go index f0c29043de..81c99c6359 100644 --- a/eth/eventsyncer/event_syncer_test.go +++ b/eth/eventsyncer/event_syncer_test.go @@ -11,6 +11,7 @@ import ( "github.com/bloxapp/ssv/eth/contract" "github.com/bloxapp/ssv/eth/simulator" + operatordatastore "github.com/bloxapp/ssv/operator/datastore" "github.com/bloxapp/ssv/operator/keys" "github.com/bloxapp/ssv/operator/validatorsmap" "github.com/bloxapp/ssv/utils/rsaencryption" @@ -144,6 +145,7 @@ func setupEventHandler(t *testing.T, ctx context.Context, logger *zap.Logger) *e } nodeStorage, operatorData := setupOperatorStorage(logger, db, privateKey) + operatorDataStore := operatordatastore.New(operatorData) testNetworkConfig := networkconfig.TestNetwork keyManager, err := ekm.NewETHKeyManagerSigner(logger, db, testNetworkConfig, true, "") @@ -156,11 +158,11 @@ func setupEventHandler(t *testing.T, ctx context.Context, logger *zap.Logger) *e bc := beacon.NewMockBeaconNode(ctrl) validatorCtrl := validator.NewController(logger, validator.ControllerOptions{ - Context: ctx, - DB: db, - RegistryStorage: nodeStorage, - OperatorData: operatorData, - ValidatorsMap: validatorsmap.New(ctx), + Context: ctx, + DB: db, + RegistryStorage: nodeStorage, + OperatorDataStore: operatorDataStore, + ValidatorsMap: validatorsmap.New(ctx), }) contractFilterer, err := contract.NewContractFilterer(ethcommon.Address{}, nil) @@ -173,7 +175,7 @@ func setupEventHandler(t *testing.T, ctx context.Context, logger *zap.Logger) *e parser, validatorCtrl, testNetworkConfig, - validatorCtrl, + operatorDataStore, privateKey, keyManager, bc, diff --git a/message/validation/consensus_validation.go b/message/validation/consensus_validation.go index 467c4cd919..30d8c46f0d 100644 --- a/message/validation/consensus_validation.go +++ b/message/validation/consensus_validation.go @@ -26,10 +26,12 @@ func (mv *messageValidator) validateConsensusMessage( ) (ConsensusDescriptor, phase0.Slot, error) { var consensusDescriptor ConsensusDescriptor - if mv.inCommittee(share) { - mv.metrics.InCommitteeMessage(spectypes.SSVConsensusMsgType, mv.isDecidedMessage(signedMsg)) - } else { - mv.metrics.NonCommitteeMessage(spectypes.SSVConsensusMsgType, mv.isDecidedMessage(signedMsg)) + if mv.operatorDataStore != nil && mv.operatorDataStore.OperatorIDReady() { + if mv.inCommittee(share) { + mv.metrics.InCommitteeMessage(spectypes.SSVConsensusMsgType, mv.isDecidedMessage(signedMsg)) + } else { + mv.metrics.NonCommitteeMessage(spectypes.SSVConsensusMsgType, mv.isDecidedMessage(signedMsg)) + } } msgSlot := phase0.Slot(signedMsg.Message.Height) diff --git a/message/validation/partial_validation.go b/message/validation/partial_validation.go index c6a4655cfe..5b2115456e 100644 --- a/message/validation/partial_validation.go +++ b/message/validation/partial_validation.go @@ -16,10 +16,12 @@ func (mv *messageValidator) validatePartialSignatureMessage( msgID spectypes.MessageID, signatureVerifier func() error, ) (phase0.Slot, error) { - if mv.inCommittee(share) { - mv.metrics.InCommitteeMessage(spectypes.SSVPartialSignatureMsgType, false) - } else { - mv.metrics.NonCommitteeMessage(spectypes.SSVPartialSignatureMsgType, false) + if mv.operatorDataStore != nil && mv.operatorDataStore.OperatorIDReady() { + if mv.inCommittee(share) { + mv.metrics.InCommitteeMessage(spectypes.SSVPartialSignatureMsgType, false) + } else { + mv.metrics.NonCommitteeMessage(spectypes.SSVPartialSignatureMsgType, false) + } } msgSlot := signedMsg.Message.Slot diff --git a/message/validation/validation.go b/message/validation/validation.go index 8efcb2271c..4c42312a28 100644 --- a/message/validation/validation.go +++ b/message/validation/validation.go @@ -27,6 +27,7 @@ import ( "github.com/bloxapp/ssv/monitoring/metricsreporter" "github.com/bloxapp/ssv/network/commons" "github.com/bloxapp/ssv/networkconfig" + operatordatastore "github.com/bloxapp/ssv/operator/datastore" "github.com/bloxapp/ssv/operator/duties/dutystore" "github.com/bloxapp/ssv/operator/keys" operatorstorage "github.com/bloxapp/ssv/operator/storage" @@ -69,9 +70,6 @@ type MessageValidator interface { SSVMessageValidator } -// OperatorIDGetter defines a function that returns operator ID. -type OperatorIDGetter func() spectypes.OperatorID - type messageValidator struct { logger *zap.Logger metrics metricsreporter.MetricsReporter @@ -79,7 +77,7 @@ type messageValidator struct { index sync.Map nodeStorage operatorstorage.Storage dutyStore *dutystore.Store - getOwnOperatorID OperatorIDGetter + operatorDataStore operatordatastore.OperatorDataStore operatorIDToPubkeyCache *hashmap.Map[spectypes.OperatorID, keys.OperatorPublicKey] // validationLocks is a map of lock per SSV message ID to @@ -98,7 +96,6 @@ func NewMessageValidator(netCfg networkconfig.NetworkConfig, opts ...Option) Mes metrics: metricsreporter.NewNop(), netCfg: netCfg, operatorIDToPubkeyCache: hashmap.New[spectypes.OperatorID, keys.OperatorPublicKey](), - getOwnOperatorID: func() spectypes.OperatorID { return 0 }, validationLocks: make(map[spectypes.MessageID]*sync.Mutex), } @@ -134,9 +131,9 @@ func WithDutyStore(dutyStore *dutystore.Store) Option { } // WithOwnOperatorID sets the operator ID getter for the messageValidator. -func WithOwnOperatorID(f OperatorIDGetter) Option { +func WithOwnOperatorID(ods operatordatastore.OperatorDataStore) Option { return func(mv *messageValidator) { - mv.getOwnOperatorID = f + mv.operatorDataStore = ods } } @@ -589,8 +586,9 @@ func (mv *messageValidator) consensusState(messageID spectypes.MessageID) *Conse return cs.(*ConsensusState) } +// inCommittee should be called only when WithOwnOperatorID is set func (mv *messageValidator) inCommittee(share *ssvtypes.SSVShare) bool { return slices.ContainsFunc(share.Committee, func(operator *spectypes.Operator) bool { - return operator.OperatorID == mv.getOwnOperatorID() + return operator.OperatorID == mv.operatorDataStore.GetOperatorID() }) } diff --git a/network/p2p/config.go b/network/p2p/config.go index 60276fcbdb..f9355f87fe 100644 --- a/network/p2p/config.go +++ b/network/p2p/config.go @@ -21,6 +21,7 @@ import ( "github.com/bloxapp/ssv/network" "github.com/bloxapp/ssv/network/commons" "github.com/bloxapp/ssv/networkconfig" + operatordatastore "github.com/bloxapp/ssv/operator/datastore" "github.com/bloxapp/ssv/operator/keys" "github.com/bloxapp/ssv/operator/storage" uc "github.com/bloxapp/ssv/utils/commons" @@ -61,8 +62,8 @@ type Config struct { OperatorSigner keys.OperatorSigner // OperatorPubKeyHash is hash of operator public key, used for identity, optional OperatorPubKeyHash string - // GetOperatorID contains numeric operator ID getter - GetOperatorID validation.OperatorIDGetter + // OperatorDataStore contains own operator data including its ID + OperatorDataStore operatordatastore.OperatorDataStore // Router propagate incoming network messages to the responsive components Router network.MessageRouter // UserAgent to use by libp2p identify protocol diff --git a/network/p2p/p2p.go b/network/p2p/p2p.go index 116175b116..683ebf7e40 100644 --- a/network/p2p/p2p.go +++ b/network/p2p/p2p.go @@ -5,7 +5,6 @@ import ( "sync/atomic" "time" - spectypes "github.com/bloxapp/ssv-spec/types" "github.com/cornelk/hashmap" "github.com/libp2p/go-libp2p/core/connmgr" connmgrcore "github.com/libp2p/go-libp2p/core/connmgr" @@ -25,6 +24,7 @@ import ( "github.com/bloxapp/ssv/network/records" "github.com/bloxapp/ssv/network/streams" "github.com/bloxapp/ssv/network/topics" + operatordatastore "github.com/bloxapp/ssv/operator/datastore" "github.com/bloxapp/ssv/operator/keys" operatorstorage "github.com/bloxapp/ssv/operator/storage" "github.com/bloxapp/ssv/utils/async" @@ -79,7 +79,7 @@ type p2pNetwork struct { nodeStorage operatorstorage.Storage operatorPKHashToPKCache *hashmap.Map[string, []byte] // used for metrics operatorSigner keys.OperatorSigner - getOperatorID func() spectypes.OperatorID + operatorDataStore operatordatastore.OperatorDataStore } // New creates a new p2p network @@ -101,7 +101,7 @@ func New(logger *zap.Logger, cfg *Config, mr Metrics) network.P2PNetwork { nodeStorage: cfg.NodeStorage, operatorPKHashToPKCache: hashmap.New[string, []byte](), operatorSigner: cfg.OperatorSigner, - getOperatorID: cfg.GetOperatorID, + operatorDataStore: cfg.OperatorDataStore, metrics: mr, } } diff --git a/network/p2p/p2p_pubsub.go b/network/p2p/p2p_pubsub.go index 7f165b9ec5..f6f102d96d 100644 --- a/network/p2p/p2p_pubsub.go +++ b/network/p2p/p2p_pubsub.go @@ -56,6 +56,10 @@ func (n *p2pNetwork) Broadcast(msg *spectypes.SSVMessage) error { return p2pprotocol.ErrNetworkIsNotReady } + if !n.operatorDataStore.OperatorIDReady() { + return fmt.Errorf("operator ID is not ready") + } + encodedMsg, err := commons.EncodeNetworkMsg(msg) if err != nil { return errors.Wrap(err, "could not decode msg") @@ -67,7 +71,7 @@ func (n *p2pNetwork) Broadcast(msg *spectypes.SSVMessage) error { return err } - encodedMsg = commons.EncodeSignedSSVMessage(encodedMsg, n.getOperatorID(), signature) + encodedMsg = commons.EncodeSignedSSVMessage(encodedMsg, n.operatorDataStore.GetOperatorID(), signature) } vpk := msg.GetID().GetPubKey() diff --git a/network/p2p/p2p_validation_test.go b/network/p2p/p2p_validation_test.go index 2d8292a9ed..9593d157cc 100644 --- a/network/p2p/p2p_validation_test.go +++ b/network/p2p/p2p_validation_test.go @@ -5,7 +5,7 @@ import ( cryptorand "crypto/rand" "encoding/hex" "fmt" - "github.com/cornelk/hashmap" + "math/rand" "os" "sort" "sync" @@ -13,17 +13,17 @@ import ( "testing" "time" - "math/rand" - "github.com/aquasecurity/table" spectypes "github.com/bloxapp/ssv-spec/types" - "github.com/bloxapp/ssv/message/validation" - "github.com/bloxapp/ssv/network/commons" - "github.com/bloxapp/ssv/protocol/v2/ssv/queue" + "github.com/cornelk/hashmap" pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/peer" "github.com/sourcegraph/conc/pool" "github.com/stretchr/testify/require" + + "github.com/bloxapp/ssv/message/validation" + "github.com/bloxapp/ssv/network/commons" + "github.com/bloxapp/ssv/protocol/v2/ssv/queue" ) // TestP2pNetwork_MessageValidation tests p2pNetwork would score peers according diff --git a/network/p2p/test_utils.go b/network/p2p/test_utils.go index 98241846a6..31e763a378 100644 --- a/network/p2p/test_utils.go +++ b/network/p2p/test_utils.go @@ -6,6 +6,7 @@ import ( "fmt" "time" + spectypes "github.com/bloxapp/ssv-spec/types" pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" @@ -22,6 +23,8 @@ import ( "github.com/bloxapp/ssv/network/peers/connections/mock" "github.com/bloxapp/ssv/network/testing" "github.com/bloxapp/ssv/networkconfig" + operatordatastore "github.com/bloxapp/ssv/operator/datastore" + registrystorage "github.com/bloxapp/ssv/registry/storage" "github.com/bloxapp/ssv/utils/format" ) @@ -176,6 +179,8 @@ func (ln *LocalNet) NewTestP2pNetwork(ctx context.Context, nodeIndex int, keys t cfg.PeerScoreInspectorInterval = options.PeerScoreInspectorInterval } + cfg.OperatorDataStore = operatordatastore.New(®istrystorage.OperatorData{ID: spectypes.OperatorID(nodeIndex + 1)}) + mr := metricsreporter.New() p := New(logger, cfg, mr) err = p.Setup(logger) diff --git a/operator/datastore/data_store.go b/operator/datastore/data_store.go new file mode 100644 index 0000000000..160316c004 --- /dev/null +++ b/operator/datastore/data_store.go @@ -0,0 +1,94 @@ +package datastore + +import ( + "sync" + + spectypes "github.com/bloxapp/ssv-spec/types" + + registrystorage "github.com/bloxapp/ssv/registry/storage" +) + +// OperatorDataStore defines an interface for operations related to operator data. +type OperatorDataStore interface { + GetOperatorData() *registrystorage.OperatorData + GetOperatorID() spectypes.OperatorID + OperatorIDReady() bool + AwaitOperatorID() spectypes.OperatorID + SetOperatorData(data *registrystorage.OperatorData) +} + +// operatorDataStore provides a thread-safe implementation of OperatorDataStore. +type operatorDataStore struct { + operatorData *registrystorage.OperatorData + operatorDataMu sync.RWMutex + operatorIDReady bool + readyCond *sync.Cond +} + +// New creates and initializes a new instance of OperatorDataStore with optional initial data. +func New(od *registrystorage.OperatorData) OperatorDataStore { + ods := &operatorDataStore{ + operatorData: od, + } + ods.readyCond = sync.NewCond(&ods.operatorDataMu) + if od != nil && od.ID != 0 { + ods.setOperatorIDReady() + } + return ods +} + +// GetOperatorData returns the current operator data in a thread-safe manner. +func (ods *operatorDataStore) GetOperatorData() *registrystorage.OperatorData { + ods.operatorDataMu.RLock() + defer ods.operatorDataMu.RUnlock() + + return ods.operatorData +} + +// GetOperatorID returns the operator ID in a thread-safe manner. It must be called after OperatorIDReady returns true +func (ods *operatorDataStore) GetOperatorID() spectypes.OperatorID { + ods.operatorDataMu.RLock() + defer ods.operatorDataMu.RUnlock() + + if ods.operatorData == nil { + return 0 + } + + return ods.operatorData.ID +} + +// OperatorIDReady checks if the operator ID is ready. +func (ods *operatorDataStore) OperatorIDReady() bool { + ods.operatorDataMu.RLock() + defer ods.operatorDataMu.RUnlock() + + return ods.operatorIDReady +} + +// AwaitOperatorID blocks until the operator ID is ready and returns it. +func (ods *operatorDataStore) AwaitOperatorID() spectypes.OperatorID { + ods.operatorDataMu.Lock() + for !ods.operatorIDReady { + ods.readyCond.Wait() + } + id := ods.operatorData.ID + ods.operatorDataMu.Unlock() + return id +} + +// SetOperatorData sets the operator data in a thread-safe manner and marks the operator ID as ready if valid. +func (ods *operatorDataStore) SetOperatorData(od *registrystorage.OperatorData) { + ods.operatorDataMu.Lock() + defer ods.operatorDataMu.Unlock() + + ods.operatorData = od + if od != nil && od.ID != 0 { + ods.setOperatorIDReady() + } +} + +// setOperatorIDReady marks the operator ID as ready and notifies waiting goroutines. +func (ods *operatorDataStore) setOperatorIDReady() { + ods.operatorIDReady = true + ods.readyCond.Broadcast() +} diff --git a/operator/datastore/data_store_test.go b/operator/datastore/data_store_test.go new file mode 100644 index 0000000000..0691ae46f6 --- /dev/null +++ b/operator/datastore/data_store_test.go @@ -0,0 +1,104 @@ +package datastore + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + spectypes "github.com/bloxapp/ssv-spec/types" + + registrystorage "github.com/bloxapp/ssv/registry/storage" +) + +func TestNewOperatorDataStore(t *testing.T) { + t.Run("initializes with nil data", func(t *testing.T) { + store := New(nil).(*operatorDataStore) + + assert.NotNil(t, store) + assert.Nil(t, store.operatorData) + assert.False(t, store.operatorIDReady) + }) + + t.Run("initializes with valid data", func(t *testing.T) { + data := ®istrystorage.OperatorData{ID: 1} + store := New(data).(*operatorDataStore) + + assert.NotNil(t, store) + require.NotNil(t, store.operatorData) + assert.Equal(t, spectypes.OperatorID(1), store.operatorData.ID) + assert.True(t, store.operatorIDReady) + }) +} + +func TestSetAndGetOperatorData(t *testing.T) { + store := New(nil).(*operatorDataStore) + + data := ®istrystorage.OperatorData{ID: 123} + store.SetOperatorData(data) + + returnedData := store.GetOperatorData() + assert.Equal(t, data, returnedData) +} + +func TestSetOperatorDataConditions(t *testing.T) { + t.Run("valid data sets operator ID ready", func(t *testing.T) { + store := New(nil).(*operatorDataStore) + data := ®istrystorage.OperatorData{ID: 123} + + store.SetOperatorData(data) + + assert.True(t, store.OperatorIDReady(), "Operator ID should be marked as ready") + assert.Equal(t, spectypes.OperatorID(123), store.operatorData.ID, "Operator ID should match the input data") + }) + + t.Run("nil data does not set operator ID ready", func(t *testing.T) { + store := New(nil).(*operatorDataStore) + + store.SetOperatorData(nil) + + assert.False(t, store.OperatorIDReady(), "Operator ID should not be marked as ready with nil data") + assert.Nil(t, store.operatorData, "Operator data should remain nil") + }) + + t.Run("data with zero ID does not set operator ID ready", func(t *testing.T) { + store := New(nil).(*operatorDataStore) + data := ®istrystorage.OperatorData{ID: 0} + + store.SetOperatorData(data) + + assert.False(t, store.OperatorIDReady(), "Operator ID should not be marked as ready with zero ID") + assert.Equal(t, spectypes.OperatorID(0), store.operatorData.ID, "Operator ID should be zero") + }) +} + +func TestGetOperatorID(t *testing.T) { + data := ®istrystorage.OperatorData{ID: 123} + store := New(data).(*operatorDataStore) + + operatorID := store.GetOperatorID() + assert.Equal(t, spectypes.OperatorID(123), operatorID) +} + +func TestOperatorIDReady(t *testing.T) { + store := New(nil).(*operatorDataStore) + assert.False(t, store.OperatorIDReady()) + + data := ®istrystorage.OperatorData{ID: 123} + store.SetOperatorData(data) + assert.True(t, store.OperatorIDReady()) +} + +func TestAwaitOperatorID(t *testing.T) { + store := New(nil).(*operatorDataStore) + + go func() { + time.Sleep(1 * time.Millisecond) // Simulate some operation delay + data := ®istrystorage.OperatorData{ID: 456} + store.SetOperatorData(data) + }() + + receivedID := store.AwaitOperatorID() + assert.Equal(t, spectypes.OperatorID(456), receivedID) +} diff --git a/operator/fee_recipient/controller.go b/operator/fee_recipient/controller.go index 5f8e018150..717d0323ed 100644 --- a/operator/fee_recipient/controller.go +++ b/operator/fee_recipient/controller.go @@ -5,12 +5,12 @@ import ( "github.com/attestantio/go-eth2-client/spec/bellatrix" "github.com/attestantio/go-eth2-client/spec/phase0" - spectypes "github.com/bloxapp/ssv-spec/types" "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" "go.uber.org/zap" "github.com/bloxapp/ssv/networkconfig" + operatordatastore "github.com/bloxapp/ssv/operator/datastore" "github.com/bloxapp/ssv/operator/slotticker" beaconprotocol "github.com/bloxapp/ssv/protocol/v2/blockchain/beacon" "github.com/bloxapp/ssv/protocol/v2/types" @@ -32,7 +32,7 @@ type ControllerOptions struct { ShareStorage storage.Shares RecipientStorage storage.Recipients SlotTickerProvider slotticker.Provider - GetOperatorID func() spectypes.OperatorID + OperatorDataStore operatordatastore.OperatorDataStore } // recipientController implementation of RecipientController @@ -43,7 +43,7 @@ type recipientController struct { shareStorage storage.Shares recipientStorage storage.Recipients slotTickerProvider slotticker.Provider - getOperatorID func() spectypes.OperatorID + operatorDataStore operatordatastore.OperatorDataStore } func NewController(opts *ControllerOptions) *recipientController { @@ -54,7 +54,7 @@ func NewController(opts *ControllerOptions) *recipientController { shareStorage: opts.ShareStorage, recipientStorage: opts.RecipientStorage, slotTickerProvider: opts.SlotTickerProvider, - getOperatorID: opts.GetOperatorID, + operatorDataStore: opts.OperatorDataStore, } } @@ -87,7 +87,11 @@ func (rc *recipientController) listenToTicker(logger *zap.Logger) { } func (rc *recipientController) prepareAndSubmit(logger *zap.Logger, slot phase0.Slot) error { - shares := rc.shareStorage.List(nil, storage.ByOperatorID(rc.getOperatorID()), storage.ByActiveValidator()) + shares := rc.shareStorage.List( + nil, + storage.ByOperatorID(rc.operatorDataStore.GetOperatorID()), + storage.ByActiveValidator(), + ) const batchSize = 500 var submitted int diff --git a/operator/fee_recipient/controller_test.go b/operator/fee_recipient/controller_test.go index f517d0fdce..146a1ac458 100644 --- a/operator/fee_recipient/controller_test.go +++ b/operator/fee_recipient/controller_test.go @@ -18,6 +18,7 @@ import ( "github.com/bloxapp/ssv/logging" "github.com/bloxapp/ssv/networkconfig" + operatordatastore "github.com/bloxapp/ssv/operator/datastore" "github.com/bloxapp/ssv/operator/slotticker" "github.com/bloxapp/ssv/operator/slotticker/mocks" "github.com/bloxapp/ssv/protocol/v2/blockchain/beacon" @@ -36,19 +37,19 @@ func TestSubmitProposal(t *testing.T) { ID: 123456789, } + operatorDataStore := operatordatastore.New(operatorData) + db, shareStorage, recipientStorage := createStorage(t) defer db.Close() network := networkconfig.TestNetwork populateStorage(t, logger, shareStorage, operatorData) frCtrl := NewController(&ControllerOptions{ - Ctx: context.TODO(), - Network: network, - ShareStorage: shareStorage, - RecipientStorage: recipientStorage, - GetOperatorID: func() spectypes.OperatorID { - return operatorData.ID - }, + Ctx: context.TODO(), + Network: network, + ShareStorage: shareStorage, + RecipientStorage: recipientStorage, + OperatorDataStore: operatorDataStore, }) t.Run("submit first time or halfway through epoch", func(t *testing.T) { diff --git a/operator/node.go b/operator/node.go index f800c758f9..89e7b9a758 100644 --- a/operator/node.go +++ b/operator/node.go @@ -3,6 +3,7 @@ package operator import ( "context" "fmt" + "github.com/bloxapp/ssv/network" spectypes "github.com/bloxapp/ssv-spec/types" @@ -113,7 +114,7 @@ func New(logger *zap.Logger, opts Options, slotTickerProvider slotticker.Provide Network: opts.Network, ShareStorage: opts.ValidatorOptions.RegistryStorage.Shares(), RecipientStorage: opts.ValidatorOptions.RegistryStorage, - GetOperatorID: opts.ValidatorController.GetOperatorID, + OperatorDataStore: opts.ValidatorOptions.OperatorDataStore, SlotTickerProvider: slotTickerProvider, }), diff --git a/operator/validator/controller.go b/operator/validator/controller.go index 5f88af387a..ed5f469707 100644 --- a/operator/validator/controller.go +++ b/operator/validator/controller.go @@ -24,6 +24,7 @@ import ( "github.com/bloxapp/ssv/logging/fields" "github.com/bloxapp/ssv/message/validation" "github.com/bloxapp/ssv/network" + operatordatastore "github.com/bloxapp/ssv/operator/datastore" "github.com/bloxapp/ssv/operator/duties" nodestorage "github.com/bloxapp/ssv/operator/storage" "github.com/bloxapp/ssv/operator/validatorsmap" @@ -69,7 +70,7 @@ type ControllerOptions struct { Exporter bool `yaml:"Exporter" env:"EXPORTER" env-default:"false" env-description:""` BuilderProposals bool `yaml:"BuilderProposals" env:"BUILDER_PROPOSALS" env-default:"false" env-description:"Use external builders to produce blocks"` KeyManager spectypes.KeyManager - OperatorData *registrystorage.OperatorData + OperatorDataStore operatordatastore.OperatorDataStore RegistryStorage nodestorage.Storage RecipientsStorage Recipients NewDecidedHandler qbftcontroller.NewDecidedHandler @@ -101,9 +102,6 @@ type Controller interface { // - the amount of active validators (i.e. not slashed or existed) // - the amount of validators assigned to this operator GetValidatorStats() (uint64, uint64, uint64, error) - GetOperatorData() *registrystorage.OperatorData - SetOperatorData(data *registrystorage.OperatorData) - GetOperatorID() spectypes.OperatorID IndicesChangeChan() chan struct{} ValidatorExitChan() <-chan duties.ExitDescriptor @@ -155,8 +153,7 @@ type controller struct { beacon beaconprotocol.BeaconNode keyManager spectypes.KeyManager - operatorData *registrystorage.OperatorData - operatorDataMutex sync.RWMutex + operatorDataStore operatordatastore.OperatorDataStore validatorOptions validator.Options validatorsMap *validatorsmap.ValidatorsMap @@ -236,7 +233,7 @@ func NewController(logger *zap.Logger, options ControllerOptions) Controller { ibftStorageMap: options.StorageMap, context: options.Context, beacon: options.Beacon, - operatorData: options.OperatorData, + operatorDataStore: options.OperatorDataStore, keyManager: options.KeyManager, network: options.Network, @@ -281,23 +278,11 @@ func (c *controller) setupNetworkHandlers() error { } func (c *controller) GetOperatorShares() []*ssvtypes.SSVShare { - return c.sharesStorage.List(nil, registrystorage.ByOperatorID(c.GetOperatorID()), registrystorage.ByActiveValidator()) -} - -func (c *controller) GetOperatorData() *registrystorage.OperatorData { - c.operatorDataMutex.RLock() - defer c.operatorDataMutex.RUnlock() - return c.operatorData -} - -func (c *controller) SetOperatorData(data *registrystorage.OperatorData) { - c.operatorDataMutex.Lock() - defer c.operatorDataMutex.Unlock() - c.operatorData = data -} - -func (c *controller) GetOperatorID() spectypes.OperatorID { - return c.GetOperatorData().ID + return c.sharesStorage.List( + nil, + registrystorage.ByOperatorID(c.operatorDataStore.GetOperatorID()), + registrystorage.ByActiveValidator(), + ) } func (c *controller) IndicesChangeChan() chan struct{} { @@ -313,7 +298,7 @@ func (c *controller) GetValidatorStats() (uint64, uint64, uint64, error) { operatorShares := uint64(0) active := uint64(0) for _, s := range allShares { - if ok := s.BelongsToOperator(c.GetOperatorID()); ok { + if ok := s.BelongsToOperator(c.operatorDataStore.GetOperatorID()); ok { operatorShares++ } if s.IsAttesting(c.beacon.GetBeaconNetwork().EstimatedCurrentEpoch()) { @@ -429,7 +414,7 @@ func (c *controller) StartValidators() { var ownShares []*ssvtypes.SSVShare var allPubKeys = make([][]byte, 0, len(shares)) for _, share := range shares { - if share.BelongsToOperator(c.GetOperatorID()) { + if share.BelongsToOperator(c.operatorDataStore.GetOperatorID()) { ownShares = append(ownShares, share) } allPubKeys = append(allPubKeys, share.ValidatorPubKey) @@ -573,7 +558,7 @@ func (c *controller) UpdateValidatorMetadata(pk string, metadata *beaconprotocol if share == nil { return errors.New("share was not found") } - if !share.BelongsToOperator(c.GetOperatorID()) || share.Liquidated { + if !share.BelongsToOperator(c.operatorDataStore.GetOperatorID()) || share.Liquidated { return nil } diff --git a/operator/validator/controller_test.go b/operator/validator/controller_test.go index f4e65ca712..f57bc930f1 100644 --- a/operator/validator/controller_test.go +++ b/operator/validator/controller_test.go @@ -18,6 +18,7 @@ import ( "github.com/bloxapp/ssv/ekm" ibftstorage "github.com/bloxapp/ssv/ibft/storage" + operatordatastore "github.com/bloxapp/ssv/operator/datastore" "github.com/bloxapp/ssv/operator/storage" "github.com/bloxapp/ssv/operator/validator/mocks" "github.com/bloxapp/ssv/protocol/v2/ssv/runner" @@ -62,11 +63,11 @@ type MockControllerOptions struct { metadataLastUpdated map[string]time.Time StorageMap *ibftstorage.QBFTStores validatorsMap *validatorsmap.ValidatorsMap - operatorData *registrystorage.OperatorData + operatorDataStore operatordatastore.OperatorDataStore } func TestNewController(t *testing.T) { - operatorData := buildOperatorData(1, "67Ce5c69260bd819B4e0AD13f4b873074D479811") + operatorDataStore := operatordatastore.New(buildOperatorData(1, "67Ce5c69260bd819B4e0AD13f4b873074D479811")) _, logger, _, network, _, recipientStorage, bc := setupCommonTestComponents(t) db, err := getBaseStorage(logger) require.NoError(t, err) @@ -77,7 +78,7 @@ func TestNewController(t *testing.T) { Metrics: nil, FullNode: true, Network: network, - OperatorData: operatorData, + OperatorDataStore: operatorDataStore, RegistryStorage: registryStorage, RecipientsStorage: recipientStorage, Context: context.Background(), @@ -90,7 +91,7 @@ func TestSetupNonCommitteeValidators(t *testing.T) { passedEpoch := phase0.Epoch(1) operators := buildOperators(t) - operatorData := buildOperatorData(1, "67Ce5c69260bd819B4e0AD13f4b873074D479811") + operatorDataStore := operatordatastore.New(buildOperatorData(1, "67Ce5c69260bd819B4e0AD13f4b873074D479811")) recipientData := buildFeeRecipient("67Ce5c69260bd819B4e0AD13f4b873074D479811", "45E668aba4b7fc8761331EC3CE77584B7A99A51A") secretKey := &bls.SecretKey{} @@ -192,7 +193,7 @@ func TestSetupNonCommitteeValidators(t *testing.T) { controllerOptions := MockControllerOptions{ beacon: bc, network: network, - operatorData: operatorData, + operatorDataStore: operatorDataStore, sharesStorage: sharesStorage, recipientsStorage: recipientStorage, validatorsMap: mockValidatorsMap, @@ -341,7 +342,7 @@ func TestUpdateValidatorMetadata(t *testing.T) { // Initialize common setup ctrl, logger, sharesStorage, network, km, recipientStorage, _ := setupCommonTestComponents(t) defer ctrl.Finish() - operatorData := buildOperatorData(tc.operatorDataId, "67Ce5c69260bd819B4e0AD13f4b873074D479811") + operatorDataStore := operatordatastore.New(buildOperatorData(tc.operatorDataId, "67Ce5c69260bd819B4e0AD13f4b873074D479811")) recipientData := buildFeeRecipient("67Ce5c69260bd819B4e0AD13f4b873074D479811", "45E668aba4b7fc8761331EC3CE77584B7A99A51A") firstValidatorPublicKey := secretKey.GetPublicKey().SerializeToHexStr() @@ -354,7 +355,7 @@ func TestUpdateValidatorMetadata(t *testing.T) { controllerOptions := MockControllerOptions{ keyManager: km, network: network, - operatorData: operatorData, + operatorDataStore: operatorDataStore, sharesStorage: sharesStorage, recipientsStorage: recipientStorage, validatorsMap: mockValidatorsMap, @@ -445,7 +446,7 @@ func TestSetupValidators(t *testing.T) { }, } - operatorData := buildOperatorData(1, "67Ce5c69260bd819B4e0AD13f4b873074D479811") + operatorDataStore := operatordatastore.New(buildOperatorData(1, "67Ce5c69260bd819B4e0AD13f4b873074D479811")) recipientData := buildFeeRecipient("67Ce5c69260bd819B4e0AD13f4b873074D479811", "45E668aba4b7fc8761331EC3CE77584B7A99A51A") ownerAddressBytes := decodeHex(t, "67Ce5c69260bd819B4e0AD13f4b873074D479811", "Failed to decode owner address") feeRecipientBytes := decodeHex(t, "45E668aba4b7fc8761331EC3CE77584B7A99A51A", "Failed to decode second fee recipient address") @@ -614,7 +615,7 @@ func TestSetupValidators(t *testing.T) { beacon: bc, network: network, sharesStorage: sharesStorage, - operatorData: operatorData, + operatorDataStore: operatorDataStore, recipientsStorage: recipientStorage, validatorsMap: mockValidatorsMap, validatorOptions: validator.Options{ @@ -638,24 +639,6 @@ func TestSetupValidators(t *testing.T) { } } -func TestAssertionOperatorData(t *testing.T) { - // Setup logger and mock controller - logger := logging.TestLogger(t) - operatorData := buildOperatorData(1, "67Ce5c69260bd819B4e0AD13f4b873074D479811") - newOperatorData := buildOperatorData(2, "64Ce5c69260bd819B4e0AD13f4b873074D479811") - - // Set up the controller with mock data - controllerOptions := MockControllerOptions{ - operatorData: operatorData, - validatorsMap: validatorsmap.New(context.TODO()), - } - ctr := setupController(logger, controllerOptions) - ctr.SetOperatorData(newOperatorData) - - // Compare the objects - require.Equal(t, newOperatorData, ctr.GetOperatorData(), "The operator data did not match the expected value") -} - func TestGetValidator(t *testing.T) { // Setup logger and mock controller logger := logging.TestLogger(t) @@ -737,10 +720,10 @@ func TestGetValidatorStats(t *testing.T) { // Set up the controller with mock data for this subtest controllerOptions := MockControllerOptions{ - sharesStorage: sharesStorage, - validatorsMap: validatorsmap.New(context.TODO()), - operatorData: buildOperatorData(1, "67Ce5c69260bd819B4e0AD13f4b873074D479811"), - beacon: bc, + sharesStorage: sharesStorage, + validatorsMap: validatorsmap.New(context.TODO()), + operatorDataStore: operatordatastore.New(buildOperatorData(1, "67Ce5c69260bd819B4e0AD13f4b873074D479811")), + beacon: bc, } ctr := setupController(logger, controllerOptions) @@ -787,10 +770,10 @@ func TestGetValidatorStats(t *testing.T) { // Set up the controller with mock data for this subtest controllerOptions := MockControllerOptions{ - sharesStorage: sharesStorage, - validatorsMap: validatorsmap.New(context.TODO()), - operatorData: buildOperatorData(1, "67Ce5c69260bd819B4e0AD13f4b873074D479811"), - beacon: bc, + sharesStorage: sharesStorage, + validatorsMap: validatorsmap.New(context.TODO()), + operatorDataStore: operatordatastore.New(buildOperatorData(1, "67Ce5c69260bd819B4e0AD13f4b873074D479811")), + beacon: bc, } ctr := setupController(logger, controllerOptions) @@ -825,10 +808,10 @@ func TestGetValidatorStats(t *testing.T) { // Set up the controller with mock data for this subtest controllerOptions := MockControllerOptions{ - sharesStorage: sharesStorage, - validatorsMap: validatorsmap.New(context.TODO()), - operatorData: buildOperatorData(1, "67Ce5c69260bd819B4e0AD13f4b873074D479811"), - beacon: bc, + sharesStorage: sharesStorage, + validatorsMap: validatorsmap.New(context.TODO()), + operatorDataStore: operatordatastore.New(buildOperatorData(1, "67Ce5c69260bd819B4e0AD13f4b873074D479811")), + beacon: bc, } ctr := setupController(logger, controllerOptions) @@ -885,10 +868,10 @@ func TestGetValidatorStats(t *testing.T) { // Set up the controller with mock data for this subtest controllerOptions := MockControllerOptions{ - sharesStorage: sharesStorage, - validatorsMap: validatorsmap.New(context.TODO()), - operatorData: buildOperatorData(1, "67Ce5c69260bd819B4e0AD13f4b873074D479811"), - beacon: bc, + sharesStorage: sharesStorage, + validatorsMap: validatorsmap.New(context.TODO()), + operatorDataStore: operatordatastore.New(buildOperatorData(1, "67Ce5c69260bd819B4e0AD13f4b873074D479811")), + beacon: bc, } ctr := setupController(logger, controllerOptions) @@ -1035,7 +1018,7 @@ func setupController(logger *zap.Logger, opts MockControllerOptions) controller metrics: opts.metrics, keyManager: opts.keyManager, ibftStorageMap: opts.StorageMap, - operatorData: opts.operatorData, + operatorDataStore: opts.operatorDataStore, sharesStorage: opts.sharesStorage, validatorsMap: opts.validatorsMap, context: context.Background(), diff --git a/operator/validator/mocks/controller.go b/operator/validator/mocks/controller.go index 7ef1c3c6fc..9cdcd0bf0f 100644 --- a/operator/validator/mocks/controller.go +++ b/operator/validator/mocks/controller.go @@ -100,34 +100,6 @@ func (mr *MockControllerMockRecorder) ExitValidator(pubKey, blockNumber, validat return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExitValidator", reflect.TypeOf((*MockController)(nil).ExitValidator), pubKey, blockNumber, validatorIndex) } -// GetOperatorData mocks base method. -func (m *MockController) GetOperatorData() *storage.OperatorData { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetOperatorData") - ret0, _ := ret[0].(*storage.OperatorData) - return ret0 -} - -// GetOperatorData indicates an expected call of GetOperatorData. -func (mr *MockControllerMockRecorder) GetOperatorData() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOperatorData", reflect.TypeOf((*MockController)(nil).GetOperatorData)) -} - -// GetOperatorID mocks base method. -func (m *MockController) GetOperatorID() types.OperatorID { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetOperatorID") - ret0, _ := ret[0].(types.OperatorID) - return ret0 -} - -// GetOperatorID indicates an expected call of GetOperatorID. -func (mr *MockControllerMockRecorder) GetOperatorID() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOperatorID", reflect.TypeOf((*MockController)(nil).GetOperatorID)) -} - // GetOperatorShares mocks base method. func (m *MockController) GetOperatorShares() []*types0.SSVShare { m.ctrl.T.Helper() @@ -216,18 +188,6 @@ func (mr *MockControllerMockRecorder) ReactivateCluster(owner, operatorIDs, toRe return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReactivateCluster", reflect.TypeOf((*MockController)(nil).ReactivateCluster), owner, operatorIDs, toReactivate) } -// SetOperatorData mocks base method. -func (m *MockController) SetOperatorData(data *storage.OperatorData) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "SetOperatorData", data) -} - -// SetOperatorData indicates an expected call of SetOperatorData. -func (mr *MockControllerMockRecorder) SetOperatorData(data interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetOperatorData", reflect.TypeOf((*MockController)(nil).SetOperatorData), data) -} - // StartNetworkHandlers mocks base method. func (m *MockController) StartNetworkHandlers() { m.ctrl.T.Helper() diff --git a/operator/validator/task_executor_test.go b/operator/validator/task_executor_test.go index 5472d188e0..2776d07dde 100644 --- a/operator/validator/task_executor_test.go +++ b/operator/validator/task_executor_test.go @@ -8,21 +8,23 @@ import ( v1 "github.com/attestantio/go-eth2-client/api/v1" spectypes "github.com/bloxapp/ssv-spec/types" + "github.com/ethereum/go-ethereum/common" + "github.com/golang/mock/gomock" + "github.com/herumi/bls-eth-go-binary/bls" + "github.com/stretchr/testify/require" + ibftstorage "github.com/bloxapp/ssv/ibft/storage" "github.com/bloxapp/ssv/networkconfig" + operatordatastore "github.com/bloxapp/ssv/operator/datastore" "github.com/bloxapp/ssv/operator/validatorsmap" "github.com/bloxapp/ssv/protocol/v2/blockchain/beacon" "github.com/bloxapp/ssv/protocol/v2/ssv/runner" "github.com/bloxapp/ssv/protocol/v2/ssv/validator" "github.com/bloxapp/ssv/protocol/v2/types" - "github.com/ethereum/go-ethereum/common" - "github.com/golang/mock/gomock" - "github.com/herumi/bls-eth-go-binary/bls" - "github.com/stretchr/testify/require" ) func TestController_LiquidateCluster(t *testing.T) { - operatorData := buildOperatorData(1, "67Ce5c69260bd819B4e0AD13f4b873074D479811") + operatorDataStore := operatordatastore.New(buildOperatorData(1, "67Ce5c69260bd819B4e0AD13f4b873074D479811")) secretKey := &bls.SecretKey{} secretKey2 := &bls.SecretKey{} require.NoError(t, secretKey.SetHexString(sk1Str)) @@ -51,7 +53,7 @@ func TestController_LiquidateCluster(t *testing.T) { controllerOptions := MockControllerOptions{ beacon: bc, network: network, - operatorData: operatorData, + operatorDataStore: operatorDataStore, sharesStorage: sharesStorage, recipientsStorage: recipientStorage, validatorsMap: mockValidatorsMap, @@ -84,7 +86,7 @@ func (signable) GetRoot() ([32]byte, error) { } func TestController_StopValidator(t *testing.T) { - operatorData := buildOperatorData(1, "67Ce5c69260bd819B4e0AD13f4b873074D479811") + operatorDataStore := operatordatastore.New(buildOperatorData(1, "67Ce5c69260bd819B4e0AD13f4b873074D479811")) secretKey := &bls.SecretKey{} secretKey2 := &bls.SecretKey{} require.NoError(t, secretKey.SetHexString(sk1Str)) @@ -114,7 +116,7 @@ func TestController_StopValidator(t *testing.T) { controllerOptions := MockControllerOptions{ beacon: bc, network: network, - operatorData: operatorData, + operatorDataStore: operatorDataStore, sharesStorage: sharesStorage, recipientsStorage: recipientStorage, validatorsMap: mockValidatorsMap, @@ -145,7 +147,7 @@ func TestController_StopValidator(t *testing.T) { func TestController_ReactivateCluster(t *testing.T) { storageMap := ibftstorage.NewStores() - operatorData := buildOperatorData(1, "67Ce5c69260bd819B4e0AD13f4b873074D479811") + operatorDataStore := operatordatastore.New(buildOperatorData(1, "67Ce5c69260bd819B4e0AD13f4b873074D479811")) secretKey := &bls.SecretKey{} secretKey2 := &bls.SecretKey{} require.NoError(t, secretKey.SetHexString(sk1Str)) @@ -160,7 +162,7 @@ func TestController_ReactivateCluster(t *testing.T) { controllerOptions := MockControllerOptions{ beacon: bc, network: network, - operatorData: operatorData, + operatorDataStore: operatorDataStore, sharesStorage: sharesStorage, recipientsStorage: recipientStorage, validatorsMap: mockValidatorsMap, From 3d0f302bb53c468145cccae4fe9c897495a2d7b0 Mon Sep 17 00:00:00 2001 From: Nikita Kryuchkov Date: Sun, 31 Mar 2024 20:30:17 +0400 Subject: [PATCH 4/5] Partial Signature Verification Aggregation (#1351) * Reduce signatures in partial signatures steps * Port commit 5e592ba3c8b4331c629212954f4dc6c195e440fb * Revert accidental metric removal * Deploy to 1-56 * Revert "Deploy to 1-56" This reverts commit 0bcfec43a158cacae75335764324e5f60b63d024. * Use spec's tag * fix: missing alignment for `BaseRunner.didDecideCorrectly` * approve spec diffs * missing test alignment --------- Co-authored-by: moshe-blox --- go.mod | 9 +- go.sum | 236 +----------------- protocol/v2/qbft/spectest/controller_type.go | 4 + protocol/v2/ssv/runner/aggregator.go | 10 +- protocol/v2/ssv/runner/attester.go | 6 +- .../ssv/runner/pre_consensus_justification.go | 22 +- protocol/v2/ssv/runner/proposer.go | 10 +- protocol/v2/ssv/runner/runner.go | 30 ++- protocol/v2/ssv/runner/runner_signatures.go | 47 +++- protocol/v2/ssv/runner/runner_validations.go | 13 + protocol/v2/ssv/runner/sync_committee.go | 6 +- .../ssv/runner/sync_committee_aggregator.go | 12 +- .../v2/ssv/runner/validator_registration.go | 4 +- protocol/v2/ssv/runner/voluntary_exit.go | 4 +- protocol/v2/ssv/spectest/ssv_mapping_test.go | 13 + scripts/spec-alignment/differ.config.yaml | 3 +- 16 files changed, 148 insertions(+), 281 deletions(-) diff --git a/go.mod b/go.mod index d72ff484da..d3a5926a07 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/aquasecurity/table v1.8.0 github.com/attestantio/go-eth2-client v0.19.11-0.20240129201044-9d799aaab2bd github.com/bloxapp/eth2-key-manager v1.4.0 - github.com/bloxapp/ssv-spec v0.3.6 + github.com/bloxapp/ssv-spec v0.3.7 github.com/btcsuite/btcd/btcec/v2 v2.3.2 github.com/cespare/xxhash/v2 v2.2.0 github.com/cornelk/hashmap v1.0.8 @@ -33,7 +33,6 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.16.0 - github.com/prysmaticlabs/fastssz v0.0.0-20220628121656-93dfe28febab github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 github.com/prysmaticlabs/prysm/v4 v4.0.8 github.com/rs/zerolog v1.29.1 @@ -107,7 +106,6 @@ require ( github.com/google/go-cmp v0.5.9 // indirect github.com/google/gopacket v1.1.19 // indirect github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.0.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-bexpr v0.1.10 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -158,7 +156,6 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/pointerstructure v1.2.0 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect - github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/mr-tron/base58 v1.2.0 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect @@ -178,7 +175,6 @@ require ( github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect - github.com/prysmaticlabs/gohashtree v0.0.3-alpha // indirect github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qtls-go1-19 v0.3.3 // indirect github.com/quic-go/qtls-go1-20 v0.2.3 // indirect @@ -197,7 +193,6 @@ require ( github.com/status-im/keycard-go v0.2.0 // indirect github.com/supranational/blst v0.3.11 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect - github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect @@ -221,8 +216,6 @@ require ( golang.org/x/tools v0.13.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect gonum.org/v1/gonum v0.11.0 // indirect - google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect - google.golang.org/grpc v1.53.0 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index c6e7352b9a..18ddf1c6a4 100644 --- a/go.sum +++ b/go.sum @@ -3,39 +3,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= contrib.go.opencensus.io/exporter/jaeger v0.2.1 h1:yGBYzYMewVL0yO9qqJv3Z5+IRhPdU7e9o/2oKpX4YvI= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= @@ -45,7 +14,6 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= @@ -67,7 +35,6 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/aquasecurity/table v1.8.0 h1:9ntpSwrUfjrM6/YviArlx/ZBGd6ix8W+MtojQcM7tv0= github.com/aquasecurity/table v1.8.0/go.mod h1:eqOmvjjB7AhXFgFqpJUEE/ietg7RrMSJZXyTN8E/wZw= github.com/aristanetworks/fsnotify v1.4.2/go.mod h1:D/rtu7LpjYM8tRJphJ0hUBYpjai8SfX+aSNsWDTq/Ks= @@ -90,8 +57,8 @@ github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHl github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bloxapp/eth2-key-manager v1.4.0 h1:fwAW886z2mSUb/u4Kq/ln+wvSkIxLqEgxvE+x8gZiz8= github.com/bloxapp/eth2-key-manager v1.4.0/go.mod h1:m2DsvNCyLAAcgkN2JuroXUGpM61OeKSclmWuaRNW4Ss= -github.com/bloxapp/ssv-spec v0.3.6 h1:kTG9AUEdwA4qgy9wWVxWDu83YymQnH+E6yAQWcSvFD8= -github.com/bloxapp/ssv-spec v0.3.6/go.mod h1:f9UvuHTb8N3PQpvA6fKYW/l7fzkfyX0Oj9qS34cSOVw= +github.com/bloxapp/ssv-spec v0.3.7 h1:lz8WgbopVY02lomNzn0P0iFMXyGEZ+ujqH9B/LYVt5I= +github.com/bloxapp/ssv-spec v0.3.7/go.mod h1:f9UvuHTb8N3PQpvA6fKYW/l7fzkfyX0Oj9qS34cSOVw= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= @@ -230,9 +197,6 @@ github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg= github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -283,8 +247,6 @@ github.com/golang/gddo v0.0.0-20200528160355-8d077c1d8f4c/go.mod h1:sam69Hju0uq+ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -292,19 +254,12 @@ github.com/golang/lint v0.0.0-20170918230701-e5d664eb928e/go.mod h1:tluoj9z5200j github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -324,7 +279,6 @@ github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXi github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/flatbuffers v1.11.0 h1:O7CEyB8Cb3/DmtxODGtLHcEvpr81Jm5qLg/hsHnxA2A= github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.1.1-0.20171103154506-982329095285/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -332,9 +286,7 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -347,14 +299,7 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 h1:hR7/MlvK23p6+lIw9SN1TigNLn9ZnF3W4SYRKq2gAHs= github.com/google/pprof v0.0.0-20230602150820-91b7bce49751/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= @@ -366,8 +311,6 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -375,9 +318,9 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20170920190843-316c5e0ff04e/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/grpc-gateway v1.5.0 h1:WcmKMm43DR7RdtlkEXQJyo5ws8iTp98CyhCCbOHMvNI= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.0.1 h1:X2vfSnm1WC8HEo0MBHZg2TcuDUHJj6kd1TmEAQncnSA= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.0.1/go.mod h1:oVMjMN64nzEcepv1kdZKgx1qNYt4Ro0Gqefiq2JWdis= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -387,8 +330,6 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/v2 v2.0.2 h1:Dwmkdr5Nc/oBiXgJS3CDHNhJtIHkuZ3DZF5twqnfBdU= @@ -413,7 +354,6 @@ github.com/huandu/go-clone/generic v1.6.0 h1:Wgmt/fUZ28r16F2Y3APotFD59sHk1p78K0X github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ilyakaznacheev/cleanenv v1.4.2 h1:nRqiriLMAC7tz7GzjzUTBHfzdzw6SQ7XvTagkFqe/zU= github.com/ilyakaznacheev/cleanenv v1.4.2/go.mod h1:i0owW+HDxeGKE0/JPREJOdSCPIyOnmh6C0xhWAkF/xA= @@ -462,7 +402,6 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -607,7 +546,6 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= @@ -717,11 +655,9 @@ github.com/prometheus/procfs v0.0.10/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+G github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/prysmaticlabs/fastssz v0.0.0-20220628121656-93dfe28febab h1:Y3PcvUrnneMWLuypZpwPz8P70/DQsz6KgV9JveKpyZs= -github.com/prysmaticlabs/fastssz v0.0.0-20220628121656-93dfe28febab/go.mod h1:MA5zShstUwCQaE9faGHgCGvEWUbG87p4SAXINhmCkvg= github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 h1:0tVE4tdWQK9ZpYygoV7+vS6QkDvQVySboMVEIxBJmXw= github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7/go.mod h1:wmuf/mdK4VMD+jA9ThwcUKjg3a2XWM9cVfFYjDyY4j4= github.com/prysmaticlabs/gohashtree v0.0.3-alpha h1:1EVinCWdb3Lorq7xn8DYQHf48nCcdAM3Vb18KsFlRWY= -github.com/prysmaticlabs/gohashtree v0.0.3-alpha/go.mod h1:4pWaT30XoEx1j8KNJf3TV+E3mQkaufn7mf+jRNb/Fuk= github.com/prysmaticlabs/prysm/v4 v4.0.8 h1:F6Rt5gpaxbW50aP63jMmSXE16JW42HaEzUT55L9laaM= github.com/prysmaticlabs/prysm/v4 v4.0.8/go.mod h1:m01QCZ2qwuTpUQRfYj5gMkvEP+j6mPcMydG8mNcnYDY= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= @@ -742,7 +678,6 @@ github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqn github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -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.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= @@ -845,7 +780,6 @@ github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cb github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e h1:cR8/SYRgyQCt5cNCMniB/ZScMkhI9nk8U5C7SbISXjo= -github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e/go.mod h1:Tu4lItkATkonrYuvtVjG0/rhy15qrNGNTjPdaphtZ/8= github.com/tjfoc/gmsm v1.3.0/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= @@ -902,19 +836,12 @@ github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmv github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= @@ -950,7 +877,6 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -966,37 +892,17 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -1015,30 +921,17 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191116160921-f9c825593386/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -1056,10 +949,6 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20170517211232-f52d1811a629/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1069,8 +958,6 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= @@ -1084,42 +971,24 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1151,7 +1020,6 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -1164,7 +1032,6 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= @@ -1177,48 +1044,16 @@ golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -1239,31 +1074,12 @@ google.golang.org/api v0.0.0-20170921000349-586095a6e407/go.mod h1:4mhQ8q/RsB7i+ google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/api v0.34.0 h1:k40adF3uR+6x/+hO5Dh4ZFUqFp67vxvbpafFiJxl10A= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/genproto v0.0.0-20170918111702-1e559d0a00ee/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -1272,62 +1088,24 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200218151345-dad8c97a84f5/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/grpc v1.2.1-0.20170921194603-d4b75ebd4f9f/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1336,7 +1114,6 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= @@ -1371,7 +1148,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1387,11 +1163,8 @@ grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJd honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/apimachinery v0.20.0 h1:jjzbTJRXk0unNS71L7h3lxGDH/2HPxMPaQY+MjECKL8= k8s.io/client-go v0.20.0 h1:Xlax8PKbZsjX4gFvNtt4F5MoJ1V5prDvCuoq9B7iax0= k8s.io/klog/v2 v2.80.0 h1:lyJt0TWMPaGoODa8B8bUuxgHS3W/m/bNr2cca3brA/g= @@ -1400,9 +1173,6 @@ lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 h1:slmdOY3vp8a7KQbHkL+FLbvbkgMqmXojpFUO/jENuqQ= olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3/go.mod h1:oVgVk4OWVDi43qWBEyGhXgYxt7+ED4iYNpTngSLX2Iw= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= sigs.k8s.io/structured-merge-diff/v4 v4.0.2 h1:YHQV7Dajm86OuqnIR6zAelnDWBRjo+YhYV9PmGrh1s8= diff --git a/protocol/v2/qbft/spectest/controller_type.go b/protocol/v2/qbft/spectest/controller_type.go index a919cc104b..926b38a5d3 100644 --- a/protocol/v2/qbft/spectest/controller_type.go +++ b/protocol/v2/qbft/spectest/controller_type.go @@ -33,6 +33,10 @@ func RunControllerSpecTest(t *testing.T, test *spectests.ControllerSpecTest) { logger := logging.TestLogger(t) contr := generateController(logger) + if test.StartHeight != nil { + contr.Height = *test.StartHeight + } + var lastErr error for i, runData := range test.RunInstanceData { height := specqbft.Height(i) diff --git a/protocol/v2/ssv/runner/aggregator.go b/protocol/v2/ssv/runner/aggregator.go index 1850b8a75e..75a9e6c41a 100644 --- a/protocol/v2/ssv/runner/aggregator.go +++ b/protocol/v2/ssv/runner/aggregator.go @@ -81,7 +81,9 @@ func (r *AggregatorRunner) ProcessPreConsensus(logger *zap.Logger, signedMsg *sp // reconstruct selection proof sig fullSig, err := r.GetState().ReconstructBeaconSig(r.GetState().PreConsensusContainer, root, r.GetShare().ValidatorPubKey) if err != nil { - return errors.Wrap(err, "could not reconstruct selection proof sig") + // If the reconstructed signature verification failed, fall back to verifying each partial signature + r.BaseRunner.FallBackAndVerifyEachSignature(r.GetState().PreConsensusContainer, root) + return errors.Wrap(err, "got pre-consensus quorum but it has invalid signatures") } duty := r.GetState().StartingDuty @@ -186,7 +188,11 @@ func (r *AggregatorRunner) ProcessPostConsensus(logger *zap.Logger, signedMsg *s for _, root := range roots { sig, err := r.GetState().ReconstructBeaconSig(r.GetState().PostConsensusContainer, root, r.GetShare().ValidatorPubKey) if err != nil { - return errors.Wrap(err, "could not reconstruct post consensus signature") + // If the reconstructed signature verification failed, fall back to verifying each partial signature + for _, root := range roots { + r.BaseRunner.FallBackAndVerifyEachSignature(r.GetState().PostConsensusContainer, root) + } + return errors.Wrap(err, "got post-consensus quorum but it has invalid signatures") } specSig := phase0.BLSSignature{} copy(specSig[:], sig) diff --git a/protocol/v2/ssv/runner/attester.go b/protocol/v2/ssv/runner/attester.go index 9062db6300..bea76baaf2 100644 --- a/protocol/v2/ssv/runner/attester.go +++ b/protocol/v2/ssv/runner/attester.go @@ -150,7 +150,11 @@ func (r *AttesterRunner) ProcessPostConsensus(logger *zap.Logger, signedMsg *spe for _, root := range roots { sig, err := r.GetState().ReconstructBeaconSig(r.GetState().PostConsensusContainer, root, r.GetShare().ValidatorPubKey) if err != nil { - return errors.Wrap(err, "could not reconstruct post consensus signature") + // If the reconstructed signature verification failed, fall back to verifying each partial signature + for _, root := range roots { + r.BaseRunner.FallBackAndVerifyEachSignature(r.GetState().PostConsensusContainer, root) + } + return errors.Wrap(err, "got post-consensus quorum but it has invalid signatures") } specSig := phase0.BLSSignature{} copy(specSig[:], sig) diff --git a/protocol/v2/ssv/runner/pre_consensus_justification.go b/protocol/v2/ssv/runner/pre_consensus_justification.go index 237c790938..7a2171d6c0 100644 --- a/protocol/v2/ssv/runner/pre_consensus_justification.go +++ b/protocol/v2/ssv/runner/pre_consensus_justification.go @@ -3,6 +3,7 @@ package runner import ( "github.com/attestantio/go-eth2-client/spec/phase0" specqbft "github.com/bloxapp/ssv-spec/qbft" + "github.com/bloxapp/ssv-spec/ssv" spectypes "github.com/bloxapp/ssv-spec/types" "github.com/pkg/errors" "go.uber.org/zap" @@ -52,6 +53,7 @@ func (b *BaseRunner) validatePreConsensusJustifications(data *spectypes.Consensu signers := make(map[spectypes.OperatorID]bool) roots := make(map[[32]byte]bool) rootCount := 0 + partialSigContainer := ssv.NewPartialSigContainer(b.Share.Quorum) for i, msg := range data.PreConsensusJustifications { if err := msg.Validate(); err != nil { return err @@ -74,29 +76,39 @@ func (b *BaseRunner) validatePreConsensusJustifications(data *spectypes.Consensu } // validate roots - for _, msgRoot := range msg.Message.Messages { + for _, partialSigMessage := range msg.Message.Messages { // validate roots if i == 0 { // check signer did not sign duplicate root - if roots[msgRoot.SigningRoot] { + if roots[partialSigMessage.SigningRoot] { return errors.New("duplicate signed root") } // record roots - roots[msgRoot.SigningRoot] = true + roots[partialSigMessage.SigningRoot] = true } else { // compare roots - if !roots[msgRoot.SigningRoot] { + if !roots[partialSigMessage.SigningRoot] { return errors.New("inconsistent roots") } } + partialSigContainer.AddSignature(partialSigMessage) } - // verify sigs and duty.slot == msg.slot + // verify duty.slot == msg.slot if err := b.validatePartialSigMsgForSlot(msg, data.Duty.Slot); err != nil { return err } } + + // Verify the reconstructed signature for each root + for root := range roots { + _, err := b.State.ReconstructBeaconSig(partialSigContainer, root, b.Share.ValidatorPubKey) + if err != nil { + return errors.Wrap(err, "wrong pre-consensus partial signature") + } + } + return nil } diff --git a/protocol/v2/ssv/runner/proposer.go b/protocol/v2/ssv/runner/proposer.go index 4e5f0353ad..f98210d3dd 100644 --- a/protocol/v2/ssv/runner/proposer.go +++ b/protocol/v2/ssv/runner/proposer.go @@ -98,7 +98,9 @@ func (r *ProposerRunner) ProcessPreConsensus(logger *zap.Logger, signedMsg *spec // randao is relevant only for block proposals, no need to check type fullSig, err := r.GetState().ReconstructBeaconSig(r.GetState().PreConsensusContainer, root, r.GetShare().ValidatorPubKey) if err != nil { - return errors.Wrap(err, "could not reconstruct randao sig") + // If the reconstructed signature verification failed, fall back to verifying each partial signature + r.BaseRunner.FallBackAndVerifyEachSignature(r.GetState().PreConsensusContainer, root) + return errors.Wrap(err, "got pre-consensus quorum but it has invalid signatures") } logger.Debug("🧩 reconstructed partial RANDAO signatures", @@ -223,7 +225,11 @@ func (r *ProposerRunner) ProcessPostConsensus(logger *zap.Logger, signedMsg *spe for _, root := range roots { sig, err := r.GetState().ReconstructBeaconSig(r.GetState().PostConsensusContainer, root, r.GetShare().ValidatorPubKey) if err != nil { - return errors.Wrap(err, "could not reconstruct post consensus signature") + // If the reconstructed signature verification failed, fall back to verifying each partial signature + for _, root := range roots { + r.BaseRunner.FallBackAndVerifyEachSignature(r.GetState().PostConsensusContainer, root) + } + return errors.Wrap(err, "got post-consensus quorum but it has invalid signatures") } specSig := phase0.BLSSignature{} copy(specSig[:], sig) diff --git a/protocol/v2/ssv/runner/runner.go b/protocol/v2/ssv/runner/runner.go index bec575f242..dc03177258 100644 --- a/protocol/v2/ssv/runner/runner.go +++ b/protocol/v2/ssv/runner/runner.go @@ -196,7 +196,7 @@ func (b *BaseRunner) basePostConsensusMsgProcessing(logger *zap.Logger, runner R return hasQuorum, roots, errors.Wrap(err, "could not process post-consensus partial signature msg") } -// basePartialSigMsgProcessing adds an already validated partial msg to the container, checks for quorum and returns true (and roots) if quorum exists +// basePartialSigMsgProcessing adds a validated (without signature verification) validated partial msg to the container, checks for quorum and returns true (and roots) if quorum exists func (b *BaseRunner) basePartialSigMsgProcessing( signedMsg *spectypes.SignedPartialSignatureMessage, container *specssv.PartialSigContainer, @@ -206,14 +206,17 @@ func (b *BaseRunner) basePartialSigMsgProcessing( for _, msg := range signedMsg.Message.Messages { prevQuorum := container.HasQuorum(msg.SigningRoot) - container.AddSignature(msg) - - if prevQuorum { - continue + // Check if it has two signatures for the same signer + if container.HasSigner(msg.Signer, msg.SigningRoot) { + b.resolveDuplicateSignature(container, msg) + } else { + container.AddSignature(msg) } - quorum := container.HasQuorum(msg.SigningRoot) - if quorum { + hasQuorum := container.HasQuorum(msg.SigningRoot) + + if hasQuorum && !prevQuorum { + // Notify about first quorum only roots = append(roots, msg.SigningRoot) anyQuorum = true } @@ -224,15 +227,18 @@ func (b *BaseRunner) basePartialSigMsgProcessing( // didDecideCorrectly returns true if the expected consensus instance decided correctly func (b *BaseRunner) didDecideCorrectly(prevDecided bool, decidedMsg *specqbft.SignedMessage) (bool, error) { - decided := decidedMsg != nil - decidedRunningInstance := decided && b.State.RunningInstance != nil && decidedMsg.Message.Height == b.State.RunningInstance.GetHeight() - - if !decided { + if decidedMsg == nil { return false, nil } - if !decidedRunningInstance { + + if b.State.RunningInstance == nil { + return false, errors.New("decided wrong instance") + } + + if decidedMsg.Message.Height != b.State.RunningInstance.GetHeight() { return false, errors.New("decided wrong instance") } + // verify we decided running instance only, if not we do not proceed if prevDecided { return false, nil diff --git a/protocol/v2/ssv/runner/runner_signatures.go b/protocol/v2/ssv/runner/runner_signatures.go index d36cff7b56..19480b3c16 100644 --- a/protocol/v2/ssv/runner/runner_signatures.go +++ b/protocol/v2/ssv/runner/runner_signatures.go @@ -2,6 +2,7 @@ package runner import ( spec "github.com/attestantio/go-eth2-client/spec/phase0" + specssv "github.com/bloxapp/ssv-spec/ssv" spectypes "github.com/bloxapp/ssv-spec/types" ssz "github.com/ferranbt/fastssz" "github.com/herumi/bls-eth-go-binary/bls" @@ -46,6 +47,7 @@ func (b *BaseRunner) signPostConsensusMsg(runner Runner, msg *spectypes.PartialS }, nil } +// Validate message content without verifying signatures func (b *BaseRunner) validatePartialSigMsgForSlot( signedMsg *spectypes.SignedPartialSignatureMessage, slot spec.Slot, @@ -58,26 +60,24 @@ func (b *BaseRunner) validatePartialSigMsgForSlot( return errors.New("invalid partial sig slot") } - if err := types.VerifyByOperators(signedMsg.GetSignature(), signedMsg, b.Share.DomainType, spectypes.PartialSignatureType, b.Share.Committee); err != nil { - return errors.Wrap(err, "failed to verify PartialSignature") - } - - for _, msg := range signedMsg.Message.Messages { - if err := b.verifyBeaconPartialSignature(msg); err != nil { - return errors.Wrap(err, "could not verify Beacon partial Signature") + // Check if signer is in committee + signerInCommittee := false + for _, operator := range b.Share.Committee { + if operator.OperatorID == signedMsg.Signer { + signerInCommittee = true + break } } + if !signerInCommittee { + return errors.New("unknown signer") + } return nil } -func (b *BaseRunner) verifyBeaconPartialSignature(msg *spectypes.PartialSignatureMessage) error { +func (b *BaseRunner) verifyBeaconPartialSignature(signer uint64, signature spectypes.Signature, root [32]byte) error { types.MetricsSignaturesVerifications.WithLabelValues().Inc() - signer := msg.Signer - signature := msg.PartialSignature - root := msg.SigningRoot - for _, n := range b.Share.Committee { if n.GetID() == signer { pk, err := types.DeserializeBLSPublicKey(n.GetPublicKey()) @@ -98,3 +98,26 @@ func (b *BaseRunner) verifyBeaconPartialSignature(msg *spectypes.PartialSignatur } return errors.New("unknown signer") } + +// Stores the container's existing signature or the new one, depending on their validity. If both are invalid, remove the existing one +func (b *BaseRunner) resolveDuplicateSignature(container *specssv.PartialSigContainer, msg *spectypes.PartialSignatureMessage) { + + // Check previous signature validity + previousSignature, err := container.GetSignature(msg.Signer, msg.SigningRoot) + if err == nil { + err = b.verifyBeaconPartialSignature(msg.Signer, previousSignature, msg.SigningRoot) + if err == nil { + // Keep the previous sigature since it's correct + return + } + } + + // Previous signature is incorrect or doesn't exist + container.Remove(msg.Signer, msg.SigningRoot) + + // Hold the new signature, if correct + err = b.verifyBeaconPartialSignature(msg.Signer, msg.PartialSignature, msg.SigningRoot) + if err == nil { + container.AddSignature(msg) + } +} diff --git a/protocol/v2/ssv/runner/runner_validations.go b/protocol/v2/ssv/runner/runner_validations.go index 2694a69214..83b7502652 100644 --- a/protocol/v2/ssv/runner/runner_validations.go +++ b/protocol/v2/ssv/runner/runner_validations.go @@ -5,6 +5,7 @@ import ( "sort" spec "github.com/attestantio/go-eth2-client/spec/phase0" + specssv "github.com/bloxapp/ssv-spec/ssv" spectypes "github.com/bloxapp/ssv-spec/types" ssz "github.com/ferranbt/fastssz" "github.com/pkg/errors" @@ -27,6 +28,18 @@ func (b *BaseRunner) ValidatePreConsensusMsg(runner Runner, signedMsg *spectypes return b.verifyExpectedRoot(runner, signedMsg, roots, domain) } +// Verify each signature in container removing the invalid ones +func (b *BaseRunner) FallBackAndVerifyEachSignature(container *specssv.PartialSigContainer, root [32]byte) { + + signatures := container.GetSignatures(root) + + for operatorID, signature := range signatures { + if err := b.verifyBeaconPartialSignature(operatorID, signature, root); err != nil { + container.Remove(operatorID, root) + } + } +} + func (b *BaseRunner) ValidatePostConsensusMsg(runner Runner, signedMsg *spectypes.SignedPartialSignatureMessage) error { if !b.hasRunningDuty() { return errors.New("no running duty") diff --git a/protocol/v2/ssv/runner/sync_committee.go b/protocol/v2/ssv/runner/sync_committee.go index 2e801a4578..6f617bc26b 100644 --- a/protocol/v2/ssv/runner/sync_committee.go +++ b/protocol/v2/ssv/runner/sync_committee.go @@ -141,7 +141,11 @@ func (r *SyncCommitteeRunner) ProcessPostConsensus(logger *zap.Logger, signedMsg for _, root := range roots { sig, err := r.GetState().ReconstructBeaconSig(r.GetState().PostConsensusContainer, root, r.GetShare().ValidatorPubKey) if err != nil { - return errors.Wrap(err, "could not reconstruct post consensus signature") + // If the reconstructed signature verification failed, fall back to verifying each partial signature + for _, root := range roots { + r.BaseRunner.FallBackAndVerifyEachSignature(r.GetState().PostConsensusContainer, root) + } + return errors.Wrap(err, "got post-consensus quorum but it has invalid signatures") } specSig := phase0.BLSSignature{} copy(specSig[:], sig) diff --git a/protocol/v2/ssv/runner/sync_committee_aggregator.go b/protocol/v2/ssv/runner/sync_committee_aggregator.go index 9a90c68745..e6fc2c900a 100644 --- a/protocol/v2/ssv/runner/sync_committee_aggregator.go +++ b/protocol/v2/ssv/runner/sync_committee_aggregator.go @@ -87,7 +87,11 @@ func (r *SyncCommitteeAggregatorRunner) ProcessPreConsensus(logger *zap.Logger, // reconstruct selection proof sig sig, err := r.GetState().ReconstructBeaconSig(r.GetState().PreConsensusContainer, root, r.GetShare().ValidatorPubKey) if err != nil { - return errors.Wrap(err, "could not reconstruct sync committee index root") + // If the reconstructed signature verification failed, fall back to verifying each partial signature + for _, root := range roots { + r.BaseRunner.FallBackAndVerifyEachSignature(r.GetState().PreConsensusContainer, root) + } + return errors.Wrap(err, "got pre-consensus quorum but it has invalid signatures") } blsSigSelectionProof := phase0.BLSSignature{} copy(blsSigSelectionProof[:], sig) @@ -226,7 +230,11 @@ func (r *SyncCommitteeAggregatorRunner) ProcessPostConsensus(logger *zap.Logger, for _, root := range roots { sig, err := r.GetState().ReconstructBeaconSig(r.GetState().PostConsensusContainer, root, r.GetShare().ValidatorPubKey) if err != nil { - return errors.Wrap(err, "could not reconstruct post consensus signature") + // If the reconstructed signature verification failed, fall back to verifying each partial signature + for _, root := range roots { + r.BaseRunner.FallBackAndVerifyEachSignature(r.GetState().PostConsensusContainer, root) + } + return errors.Wrap(err, "got post-consensus quorum but it has invalid signatures") } specSig := phase0.BLSSignature{} copy(specSig[:], sig) diff --git a/protocol/v2/ssv/runner/validator_registration.go b/protocol/v2/ssv/runner/validator_registration.go index f12dae2f08..8b9dd39780 100644 --- a/protocol/v2/ssv/runner/validator_registration.go +++ b/protocol/v2/ssv/runner/validator_registration.go @@ -77,7 +77,9 @@ func (r *ValidatorRegistrationRunner) ProcessPreConsensus(logger *zap.Logger, si root := roots[0] fullSig, err := r.GetState().ReconstructBeaconSig(r.GetState().PreConsensusContainer, root, r.GetShare().ValidatorPubKey) if err != nil { - return errors.Wrap(err, "could not reconstruct validator registration sig") + // If the reconstructed signature verification failed, fall back to verifying each partial signature + r.BaseRunner.FallBackAndVerifyEachSignature(r.GetState().PreConsensusContainer, root) + return errors.Wrap(err, "got pre-consensus quorum but it has invalid signatures") } specSig := phase0.BLSSignature{} copy(specSig[:], fullSig) diff --git a/protocol/v2/ssv/runner/voluntary_exit.go b/protocol/v2/ssv/runner/voluntary_exit.go index b37bd2127f..ec58c1938c 100644 --- a/protocol/v2/ssv/runner/voluntary_exit.go +++ b/protocol/v2/ssv/runner/voluntary_exit.go @@ -78,7 +78,9 @@ func (r *VoluntaryExitRunner) ProcessPreConsensus(logger *zap.Logger, signedMsg root := roots[0] fullSig, err := r.GetState().ReconstructBeaconSig(r.GetState().PreConsensusContainer, root, r.GetShare().ValidatorPubKey) if err != nil { - return errors.Wrap(err, "could not reconstruct voluntary exit sig") + // If the reconstructed signature verification failed, fall back to verifying each partial signature + r.BaseRunner.FallBackAndVerifyEachSignature(r.GetState().PreConsensusContainer, root) + return errors.Wrap(err, "got pre-consensus quorum but it has invalid signatures") } specSig := phase0.BLSSignature{} copy(specSig[:], fullSig) diff --git a/protocol/v2/ssv/spectest/ssv_mapping_test.go b/protocol/v2/ssv/spectest/ssv_mapping_test.go index 822537a9b2..0f8cb876b6 100644 --- a/protocol/v2/ssv/spectest/ssv_mapping_test.go +++ b/protocol/v2/ssv/spectest/ssv_mapping_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/bloxapp/ssv-spec/ssv/spectest/tests" + "github.com/bloxapp/ssv-spec/ssv/spectest/tests/partialsigcontainer" "github.com/bloxapp/ssv-spec/ssv/spectest/tests/runner/duties/newduty" "github.com/bloxapp/ssv-spec/ssv/spectest/tests/runner/duties/synccommitteeaggregator" "github.com/bloxapp/ssv-spec/ssv/spectest/tests/valcheck" @@ -160,6 +161,18 @@ func prepareTest(t *testing.T, logger *zap.Logger, name string, test interface{} typedTest.Run(t, logger) }, } + case reflect.TypeOf(&partialsigcontainer.PartialSigContainerTest{}).String(): + byts, err := json.Marshal(test) + require.NoError(t, err) + typedTest := &partialsigcontainer.PartialSigContainerTest{} + require.NoError(t, json.Unmarshal(byts, &typedTest)) + + return &runnable{ + name: typedTest.TestName(), + test: func(t *testing.T) { + typedTest.Run(t) + }, + } default: t.Fatalf("unsupported test type %s [%s]", testType, testName) return nil diff --git a/scripts/spec-alignment/differ.config.yaml b/scripts/spec-alignment/differ.config.yaml index b324d9e29d..0655d670a2 100644 --- a/scripts/spec-alignment/differ.config.yaml +++ b/scripts/spec-alignment/differ.config.yaml @@ -13,7 +13,8 @@ ApprovedChanges: ["256a3dc0f1eb7abf","22b66e9a63ba145b","12c1c3a1622fb7cc","1c44 "b1c8e0148a4a755","2c25abb7c776bd54","a1754e08473bd1fa","4dbab14670fa155d","2a3667a499a23b16","930379d323dd95e8","65efe31656e8814f", "1270cef2e573f846","aeafb38ca9114f12","2a83e3384b45f2d7","91fbb874b3ce2570","74ad51ca63526e1e","defd8406641d53a5","efa21b9890ec787b", "6bd22f1a688bbca8","6b29645373ffac35","cfd236570c82c478","e2b0e9c6454c1c08","d5ddc708de23543a","11f8f0ea5709e42e","172a32c59d6f082e", - "63bbaffe74361d14","e783fe0d6b6643a"] + "63bbaffe74361d14","e783fe0d6b6643a","dcc4bcccb6a98797","68f1865003f0849c","f1d890dac1a7c433","bd2d3d706847daeb","b70a277af0882438", + "47fec869ebd67c1","c5328c01d31741fb","dfa274ccd46d9e9e","cc12f634cdeb7e22"] IgnoredIdentifiers: - logger From ef80171490eec7b884f442faa8d75a9493258b0b Mon Sep 17 00:00:00 2001 From: rehs0y Date: Sun, 31 Mar 2024 16:31:04 +0000 Subject: [PATCH 5/5] optimization: use miscrosoft/openssl for RSA sign/verify (#1358) * Use openssl lib for rsa on linux * use golang:1.20.7 image to have openssl present * don't use jemalloc and make sure its not present. * add test for the caches * added signature tests * add conversion error tests --------- Co-authored-by: moshe-blox --- Dockerfile | 20 +------ operator/keys/jemalloc_check.go | 8 +++ operator/keys/keys.go | 17 +----- operator/keys/rsa.go | 31 ++++++++++ operator/keys/rsa_linux.go | 103 ++++++++++++++++++++++++++++++++ operator/keys/rsa_linux_test.go | 101 +++++++++++++++++++++++++++++++ 6 files changed, 249 insertions(+), 31 deletions(-) create mode 100644 operator/keys/jemalloc_check.go create mode 100644 operator/keys/rsa.go create mode 100644 operator/keys/rsa_linux.go create mode 100644 operator/keys/rsa_linux_test.go diff --git a/Dockerfile b/Dockerfile index e79b48dc5d..d1dfc4ad30 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,14 +15,6 @@ RUN apt-get update && \ make=4.3-4.1 \ && rm -rf /var/lib/apt/lists/* -# install jemalloc -WORKDIR /tmp/jemalloc-temp -RUN curl -s -L "https://github.com/jemalloc/jemalloc/releases/download/5.2.1/jemalloc-5.2.1.tar.bz2" -o jemalloc.tar.bz2 \ - && tar xjf ./jemalloc.tar.bz2 -RUN cd jemalloc-5.2.1 \ - && ./configure --with-jemalloc-prefix='je_' --with-malloc-conf='background_thread:true,metadata_thp:auto' \ - && make && make install - RUN go version WORKDIR /go/src/github.com/bloxapp/ssv/ @@ -46,22 +38,16 @@ RUN --mount=type=cache,target=/root/.cache/go-build \ COMMIT=$(git rev-parse HEAD) && \ VERSION=$(git describe --tags $(git rev-list --tags --max-count=1) --always) && \ CGO_ENABLED=1 GOOS=linux go install \ - -tags="blst_enabled,jemalloc,allocator" \ + -tags="blst_enabled" \ -ldflags "-X main.Commit=$COMMIT -X main.Version=$VERSION -linkmode external -extldflags \"-static -lm\"" \ ./cmd/ssvnode # # STEP 3: Prepare image to run the binary # -FROM alpine:3.18.3 AS runner +FROM golang:1.20.7 AS runner -# Install ca-certificates, bash -RUN apk -v --update add \ - ca-certificates=20240226-r0 \ - bash=5.2.15-r5 \ - make=4.4.1-r1 \ - bind-tools=9.18.24-r0 && \ - rm /var/cache/apk/* +WORKDIR / COPY --from=builder /go/bin/ssvnode /go/bin/ssvnode COPY ./Makefile .env* ./ diff --git a/operator/keys/jemalloc_check.go b/operator/keys/jemalloc_check.go new file mode 100644 index 0000000000..0f8923108b --- /dev/null +++ b/operator/keys/jemalloc_check.go @@ -0,0 +1,8 @@ +//go:build jemalloc + +package keys + +func init() { + msg := "jemalloc is not supported because it clashes with openssl, please disable jemalloc by removing the build tags 'jemalloc, allocator' from the build command, e.g. 'go build -tags=\"allocator,jemalloc\"'" + panic(msg) +} diff --git a/operator/keys/keys.go b/operator/keys/keys.go index 4b2c398874..d17efc419f 100644 --- a/operator/keys/keys.go +++ b/operator/keys/keys.go @@ -1,8 +1,6 @@ package keys import ( - "crypto" - "crypto/rand" crand "crypto/rand" "crypto/rsa" "crypto/sha256" @@ -70,10 +68,6 @@ func GeneratePrivateKey() (OperatorPrivateKey, error) { return &privateKey{privKey: privKey}, nil } -type privateKey struct { - privKey *rsa.PrivateKey -} - func (p *privateKey) Public() OperatorPublicKey { pubKey := p.privKey.PublicKey return &publicKey{pubKey: &pubKey} @@ -81,7 +75,7 @@ func (p *privateKey) Public() OperatorPublicKey { func (p *privateKey) Sign(data []byte) ([]byte, error) { hash := sha256.Sum256(data) - return rsa.SignPKCS1v15(nil, p.privKey, crypto.SHA256, hash[:]) + return SignRSA(p, hash[:]) } func (p *privateKey) Decrypt(data []byte) ([]byte, error) { @@ -104,10 +98,6 @@ func (p *privateKey) EKMHash() (string, error) { return rsaencryption.HashRsaKey(x509.MarshalPKCS1PrivateKey(p.privKey)) } -type publicKey struct { - pubKey *rsa.PublicKey -} - func PublicKeyFromString(pubKeyString string) (OperatorPublicKey, error) { pubPem, err := base64.StdEncoding.DecodeString(pubKeyString) if err != nil { @@ -125,12 +115,11 @@ func PublicKeyFromString(pubKeyString string) (OperatorPublicKey, error) { } func (p *publicKey) Encrypt(data []byte) ([]byte, error) { - return rsa.EncryptPKCS1v15(rand.Reader, p.pubKey, data) + return EncryptRSA(p, data) } func (p *publicKey) Verify(data []byte, signature []byte) error { - messageHash := sha256.Sum256(data) - return rsa.VerifyPKCS1v15(p.pubKey, crypto.SHA256, messageHash[:], signature) + return VerifyRSA(p, data, signature) } func (p *publicKey) Base64() ([]byte, error) { diff --git a/operator/keys/rsa.go b/operator/keys/rsa.go new file mode 100644 index 0000000000..fed04abfca --- /dev/null +++ b/operator/keys/rsa.go @@ -0,0 +1,31 @@ +//go:build !linux + +package keys + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" +) + +type privateKey struct { + privKey *rsa.PrivateKey +} + +type publicKey struct { + pubKey *rsa.PublicKey +} + +func SignRSA(priv *privateKey, data []byte) ([]byte, error) { + return rsa.SignPKCS1v15(rand.Reader, priv.privKey, crypto.SHA256, data) +} + +func EncryptRSA(pub *publicKey, data []byte) ([]byte, error) { + return rsa.EncryptPKCS1v15(rand.Reader, pub.pubKey, data) +} + +func VerifyRSA(pub *publicKey, data, signature []byte) error { + hash := sha256.Sum256(data) + return rsa.VerifyPKCS1v15(pub.pubKey, crypto.SHA256, hash[:], signature) +} diff --git a/operator/keys/rsa_linux.go b/operator/keys/rsa_linux.go new file mode 100644 index 0000000000..2f736396d5 --- /dev/null +++ b/operator/keys/rsa_linux.go @@ -0,0 +1,103 @@ +//go:build linux + +package keys + +import ( + "crypto" + "crypto/rsa" + "crypto/sha256" + "math/big" + + "github.com/microsoft/go-crypto-openssl/openssl" + "github.com/microsoft/go-crypto-openssl/openssl/bbig/bridge" +) + +type privateKey struct { + privKey *rsa.PrivateKey + cachedPrivKey *openssl.PrivateKeyRSA +} + +type publicKey struct { + pubKey *rsa.PublicKey + cachedPubkey *openssl.PublicKeyRSA +} + +func init() { + // TODO: check multiple versions of openssl + // TODO: fallback to stdlib when openssl is not available + if err := openssl.Init(); err != nil { + panic(err) + } +} + +func rsaPrivateKeyToOpenSSL(priv *rsa.PrivateKey) (*openssl.PrivateKeyRSA, error) { + return bridge.NewPrivateKeyRSA( + priv.N, + big.NewInt(int64(priv.E)), + priv.D, + priv.Primes[0], + priv.Primes[1], + priv.Precomputed.Dp, + priv.Precomputed.Dq, + priv.Precomputed.Qinv, + ) +} + +func rsaPublicKeyToOpenSSL(pub *rsa.PublicKey) (*openssl.PublicKeyRSA, error) { + return bridge.NewPublicKeyRSA( + pub.N, + big.NewInt(int64(pub.E)), + ) +} + +func checkCachePrivkey(priv *privateKey) (*openssl.PrivateKeyRSA, error) { + if priv.cachedPrivKey != nil { + return priv.cachedPrivKey, nil + } + opriv, err := rsaPrivateKeyToOpenSSL(priv.privKey) + if err != nil { + return nil, err + } + priv.cachedPrivKey = opriv + + return opriv, nil +} + +func SignRSA(priv *privateKey, data []byte) ([]byte, error) { + opriv, err := checkCachePrivkey(priv) + if err != nil { + return nil, err + } + return openssl.SignRSAPKCS1v15(opriv, crypto.SHA256, data) +} + +func checkCachePubkey(pub *publicKey) (*openssl.PublicKeyRSA, error) { + if pub.cachedPubkey != nil { + return pub.cachedPubkey, nil + } + + opub, err := rsaPublicKeyToOpenSSL(pub.pubKey) + if err != nil { + return nil, err + } + pub.cachedPubkey = opub + + return opub, nil +} + +func EncryptRSA(pub *publicKey, data []byte) ([]byte, error) { + opub, err := checkCachePubkey(pub) + if err != nil { + return nil, err + } + return openssl.EncryptRSAPKCS1(opub, data) +} + +func VerifyRSA(pub *publicKey, data, signature []byte) error { + opub, err := checkCachePubkey(pub) + if err != nil { + return err + } + hashed := sha256.Sum256(data) + return openssl.VerifyRSAPKCS1v15(opub, crypto.SHA256, hashed[:], signature) +} diff --git a/operator/keys/rsa_linux_test.go b/operator/keys/rsa_linux_test.go new file mode 100644 index 0000000000..e53d875fdf --- /dev/null +++ b/operator/keys/rsa_linux_test.go @@ -0,0 +1,101 @@ +// go:build linux +package keys + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + + "github.com/stretchr/testify/require" + + "testing" +) + +func Test_VerifyRegularSigWithOpenSSL(t *testing.T) { + // Check valid signature. + key, err := rsa.GenerateKey(rand.Reader, 2048) + require.NoError(t, err) + msg := []byte("hello") + hashed := sha256.Sum256(msg) + sig, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, hashed[:]) + require.NoError(t, err) + + pk := &privateKey{key, nil} + pub := pk.Public().(*publicKey) + + require.NoError(t, VerifyRSA(pub, msg, sig)) + + // Check wrong signature. + key2, err := rsa.GenerateKey(rand.Reader, 2048) + require.NoError(t, err) + + hashed2 := sha256.Sum256(msg) + sig2, err := rsa.SignPKCS1v15(rand.Reader, key2, crypto.SHA256, hashed2[:]) + require.NoError(t, err) + + require.Error(t, VerifyRSA(pub, msg, sig2)) +} + +func Test_VerifyOpenSSLWithOpenSSL(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 2048) + require.NoError(t, err) + msg := []byte("hello") + priv := &privateKey{key, nil} + sig, err := priv.Sign(msg) + require.NoError(t, err) + + pub := priv.Public().(*publicKey) + + require.NoError(t, VerifyRSA(pub, msg, sig)) + + // Verify with Go RSA. + hash := sha256.Sum256(msg) + err = rsa.VerifyPKCS1v15(pub.pubKey, crypto.SHA256, hash[:], sig) + require.NoError(t, err) +} + +func Test_ConversionError(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 2048) + require.NoError(t, err) + + key.D = nil + msg := []byte("hello") + priv := &privateKey{key, nil} + _, err = priv.Sign(msg) + require.Error(t, err) + + key2, err := rsa.GenerateKey(rand.Reader, 2048) + require.NoError(t, err) + + priv2 := &privateKey{key2, nil} + sig, err := priv2.Sign(msg) + require.NoError(t, err) + pub := priv2.Public().(*publicKey) + + pub.pubKey.N = nil + require.Error(t, VerifyRSA(pub, msg, sig)) +} + +func Test_Caches(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 2048) + require.NoError(t, err) + msg := []byte("hello") + priv := &privateKey{key, nil} + sig, err := priv.Sign(msg) + require.NoError(t, err) + + pub := priv.Public().(*publicKey) + + require.NoError(t, VerifyRSA(pub, msg, sig)) + + // should sign using cache + require.NotNil(t, priv.cachedPrivKey) + + sig2, err := priv.Sign(msg) + require.NoError(t, err) + + require.NotNil(t, pub.cachedPubkey) + + require.NoError(t, VerifyRSA(pub, msg, sig2)) +}