diff --git a/.gitignore b/.gitignore index 70fa7ec..616fdea 100644 --- a/.gitignore +++ b/.gitignore @@ -18,5 +18,8 @@ coverage.html # Vim *.sw? +# Local JSON files +*.json + # Local TODO TODO.md diff --git a/cmd/account/create/process_internal_test.go b/cmd/account/create/process_internal_test.go deleted file mode 100644 index 82fd820..0000000 --- a/cmd/account/create/process_internal_test.go +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright © 2019, 2020 Weald Technology Trading -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package accountcreate - -import ( - "context" - "crypto/tls" - "crypto/x509" - "fmt" - "math/rand" - "os" - "testing" - "time" - - "github.com/attestantio/dirk/testing/daemon" - "github.com/attestantio/dirk/testing/resources" - "github.com/pkg/errors" - "github.com/stretchr/testify/require" - e2types "github.com/wealdtech/go-eth2-types/v2" - dirk "github.com/wealdtech/go-eth2-wallet-dirk" - keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4" - hd "github.com/wealdtech/go-eth2-wallet-hd/v2" - nd "github.com/wealdtech/go-eth2-wallet-nd/v2" - scratch "github.com/wealdtech/go-eth2-wallet-store-scratch" - "google.golang.org/grpc/credentials" -) - -func TestProcess(t *testing.T) { - require.NoError(t, e2types.InitBLS()) - - testNDWallet, err := nd.CreateWallet(context.Background(), - "Test", - scratch.New(), - keystorev4.New(), - ) - require.NoError(t, err) - testHDWallet, err := hd.CreateWallet(context.Background(), - "Test", - []byte("pass"), - scratch.New(), - keystorev4.New(), - []byte{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }, - ) - require.NoError(t, err) - - // #nosec G404 - port1 := uint32(12000 + rand.Intn(4000)) - // #nosec G404 - port2 := uint32(12000 + rand.Intn(4000)) - // #nosec G404 - port3 := uint32(12000 + rand.Intn(4000)) - peers := map[uint64]string{ - 1: fmt.Sprintf("signer-test01:%d", port1), - 2: fmt.Sprintf("signer-test02:%d", port2), - 3: fmt.Sprintf("signer-test03:%d", port3), - } - _, path, err := daemon.New(context.Background(), "", 1, port1, peers) - require.NoError(t, err) - defer os.RemoveAll(path) - _, path, err = daemon.New(context.Background(), "", 2, port2, peers) - require.NoError(t, err) - defer os.RemoveAll(path) - _, path, err = daemon.New(context.Background(), "", 3, port3, peers) - require.NoError(t, err) - defer os.RemoveAll(path) - endpoints := []*dirk.Endpoint{ - dirk.NewEndpoint("signer-test01", port1), - dirk.NewEndpoint("signer-test02", port2), - dirk.NewEndpoint("signer-test03", port3), - } - credentials, err := credentialsFromCerts(context.Background(), resources.ClientTest01Crt, resources.ClientTest01Key, resources.CACrt) - require.NoError(t, err) - testDistributedWallet, err := dirk.OpenWallet(context.Background(), "Wallet 3", credentials, endpoints) - require.NoError(t, err) - - tests := []struct { - name string - dataIn *dataIn - err string - }{ - { - name: "Nil", - err: "no data", - }, - { - name: "WalletPassphraseIncorrect", - dataIn: &dataIn{ - timeout: 5 * time.Second, - wallet: testHDWallet, - accountName: "Good", - passphrase: "ce%NohGhah4ye5ra", - walletPassphrase: "bad", - participants: 1, - signingThreshold: 1, - }, - err: "failed to unlock wallet: incorrect passphrase", - }, - { - name: "PassphraseMissing", - dataIn: &dataIn{ - timeout: 5 * time.Second, - wallet: testHDWallet, - accountName: "Good", - passphrase: "", - walletPassphrase: "pass", - participants: 1, - signingThreshold: 1, - }, - err: "passphrase is required", - }, - { - name: "PassphraseWeak", - dataIn: &dataIn{ - timeout: 5 * time.Second, - wallet: testHDWallet, - accountName: "Good", - passphrase: "poor", - walletPassphrase: "pass", - participants: 1, - signingThreshold: 1, - }, - err: "supplied passphrase is weak; use a stronger one or run with the --allow-weak-passphrases flag", - }, - { - name: "Good", - dataIn: &dataIn{ - timeout: 5 * time.Second, - wallet: testHDWallet, - accountName: "Good", - passphrase: "ce%NohGhah4ye5ra", - walletPassphrase: "pass", - participants: 1, - signingThreshold: 1, - }, - }, - { - name: "PathMalformed", - dataIn: &dataIn{ - timeout: 5 * time.Second, - wallet: testHDWallet, - accountName: "Pathed", - passphrase: "ce%NohGhah4ye5ra", - walletPassphrase: "pass", - participants: 1, - signingThreshold: 1, - path: "n/12381/3600/1/2/3", - }, - err: "path does not match expected format m/…", - }, - { - name: "PathPassphraseMissing", - dataIn: &dataIn{ - timeout: 5 * time.Second, - wallet: testHDWallet, - accountName: "Pathed", - passphrase: "", - walletPassphrase: "pass", - participants: 1, - signingThreshold: 1, - path: "m/12381/3600/1/2/3", - }, - err: "passphrase is required", - }, - { - name: "PathNotSupported", - dataIn: &dataIn{ - timeout: 5 * time.Second, - wallet: testNDWallet, - accountName: "Pathed", - passphrase: "ce%NohGhah4ye5ra", - walletPassphrase: "pass", - participants: 1, - signingThreshold: 1, - path: "m/12381/3600/1/2/3", - }, - err: "wallet does not support account creation with an explicit path", - }, - { - name: "GoodWithPath", - dataIn: &dataIn{ - timeout: 5 * time.Second, - wallet: testHDWallet, - accountName: "Pathed", - passphrase: "ce%NohGhah4ye5ra", - walletPassphrase: "pass", - participants: 1, - signingThreshold: 1, - path: "m/12381/3600/1/2/3", - }, - }, - { - name: "DistributedSigningThresholdZero", - dataIn: &dataIn{ - timeout: 5 * time.Second, - wallet: testDistributedWallet, - accountName: "Remote", - passphrase: "ce%NohGhah4ye5ra", - walletPassphrase: "pass", - participants: 3, - signingThreshold: 0, - }, - err: "signing threshold required", - }, - { - name: "DistributedSigningThresholdNotHalf", - dataIn: &dataIn{ - timeout: 5 * time.Second, - wallet: testDistributedWallet, - accountName: "Remote", - passphrase: "ce%NohGhah4ye5ra", - walletPassphrase: "pass", - participants: 3, - signingThreshold: 1, - }, - err: "signing threshold must be more than half the number of participants", - }, - { - name: "DistributedSigningThresholdTooHigh", - dataIn: &dataIn{ - timeout: 5 * time.Second, - wallet: testDistributedWallet, - accountName: "Remote", - passphrase: "ce%NohGhah4ye5ra", - walletPassphrase: "pass", - participants: 3, - signingThreshold: 4, - }, - err: "signing threshold cannot be higher than the number of participants", - }, - { - name: "DistributedNotSupported", - dataIn: &dataIn{ - timeout: 5 * time.Second, - wallet: testNDWallet, - accountName: "Remote", - passphrase: "ce%NohGhah4ye5ra", - walletPassphrase: "pass", - participants: 3, - signingThreshold: 2, - }, - err: "wallet does not support distributed account creation", - }, - { - name: "DistributedGood", - dataIn: &dataIn{ - timeout: 5 * time.Second, - wallet: testDistributedWallet, - accountName: "Remote", - passphrase: "ce%NohGhah4ye5ra", - walletPassphrase: "pass", - participants: 3, - signingThreshold: 2, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - res, err := process(context.Background(), test.dataIn) - if test.err != "" { - require.EqualError(t, err, test.err) - } else { - require.NoError(t, err) - require.Equal(t, test.dataIn.accountName, res.account.Name()) - } - }) - } -} - -func TestNilData(t *testing.T) { - _, err := processStandard(context.Background(), nil) - require.EqualError(t, err, "no data") - _, err = processPathed(context.Background(), nil) - require.EqualError(t, err, "no data") - _, err = processDistributed(context.Background(), nil) - require.EqualError(t, err, "no data") -} - -func credentialsFromCerts(ctx context.Context, clientCert []byte, clientKey []byte, caCert []byte) (credentials.TransportCredentials, error) { - clientPair, err := tls.X509KeyPair(clientCert, clientKey) - if err != nil { - return nil, errors.Wrap(err, "failed to load client keypair") - } - - tlsCfg := &tls.Config{ - Certificates: []tls.Certificate{clientPair}, - MinVersion: tls.VersionTLS13, - } - - if caCert != nil { - cp := x509.NewCertPool() - if !cp.AppendCertsFromPEM(caCert) { - return nil, errors.New("failed to add CA certificate") - } - tlsCfg.RootCAs = cp - } - - return credentials.NewTLS(tlsCfg), nil -} diff --git a/cmd/account/derive/output.go b/cmd/account/derive/output.go index eae128f..90c643c 100644 --- a/cmd/account/derive/output.go +++ b/cmd/account/derive/output.go @@ -42,11 +42,13 @@ func output(ctx context.Context, data *dataOut) (string, error) { if data.showPrivateKey { builder.WriteString(fmt.Sprintf("Private key: %#x\n", data.key.Marshal())) } - builder.WriteString(fmt.Sprintf("Public key: %#x", data.key.PublicKey().Marshal())) if data.showWithdrawalCredentials { withdrawalCredentials := util.SHA256(data.key.PublicKey().Marshal()) withdrawalCredentials[0] = byte(0) // BLS_WITHDRAWAL_PREFIX - builder.WriteString(fmt.Sprintf("\nWithdrawal credentials: %#x", withdrawalCredentials)) + builder.WriteString(fmt.Sprintf("Withdrawal credentials: %#x\n", withdrawalCredentials)) + } + if !(data.showPrivateKey || data.showWithdrawalCredentials) { + builder.WriteString(fmt.Sprintf("Public key: %#x\n", data.key.PublicKey().Marshal())) } return builder.String(), nil diff --git a/cmd/account/derive/output_internal_test.go b/cmd/account/derive/output_internal_test.go index 8bc8c82..aeb98f0 100644 --- a/cmd/account/derive/output_internal_test.go +++ b/cmd/account/derive/output_internal_test.go @@ -64,7 +64,7 @@ func TestOutput(t *testing.T) { key: blsPrivateKey("0x068dce0c90cb428ab37a74af0191eac49648035f1aaef077734b91e05985ec55"), showPrivateKey: true, }, - needs: []string{"Public key", "Private key"}, + needs: []string{"Private key"}, }, { name: "WithdrawalCredentials", @@ -72,7 +72,7 @@ func TestOutput(t *testing.T) { key: blsPrivateKey("0x068dce0c90cb428ab37a74af0191eac49648035f1aaef077734b91e05985ec55"), showWithdrawalCredentials: true, }, - needs: []string{"Public key", "Withdrawal credentials"}, + needs: []string{"Withdrawal credentials"}, }, { name: "All", @@ -81,7 +81,7 @@ func TestOutput(t *testing.T) { showPrivateKey: true, showWithdrawalCredentials: true, }, - needs: []string{"Public key", "Private key", "Withdrawal credentials"}, + needs: []string{"Private key", "Withdrawal credentials"}, }, } diff --git a/cmd/account/derive/process.go b/cmd/account/derive/process.go index 89ee75c..4135fa6 100644 --- a/cmd/account/derive/process.go +++ b/cmd/account/derive/process.go @@ -15,57 +15,32 @@ package accountderive import ( "context" - "regexp" - "strings" "github.com/pkg/errors" - "github.com/tyler-smith/go-bip39" - util "github.com/wealdtech/go-eth2-util" - "golang.org/x/text/unicode/norm" + "github.com/wealdtech/ethdo/util" + e2types "github.com/wealdtech/go-eth2-types/v2" + e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2" ) -// pathRegex is the regular expression that matches an HD path. -var pathRegex = regexp.MustCompile("^m/[0-9]+/[0-9]+(/[0-9+])+") - func process(ctx context.Context, data *dataIn) (*dataOut, error) { if data == nil { return nil, errors.New("no data") } - // If there are more than 24 words we treat the additional characters as the passphrase. - mnemonicParts := strings.Split(data.mnemonic, " ") - mnemonicPassphrase := "" - if len(mnemonicParts) > 24 { - data.mnemonic = strings.Join(mnemonicParts[:24], " ") - mnemonicPassphrase = strings.Join(mnemonicParts[24:], " ") - } - // Normalise the input. - data.mnemonic = string(norm.NFKD.Bytes([]byte(data.mnemonic))) - mnemonicPassphrase = string(norm.NFKD.Bytes([]byte(mnemonicPassphrase))) - - if !bip39.IsMnemonicValid(data.mnemonic) { - return nil, errors.New("mnemonic is invalid") - } - - // Create seed from mnemonic and passphrase. - seed := bip39.NewSeed(data.mnemonic, mnemonicPassphrase) - - // Ensure the path is valid. - match := pathRegex.Match([]byte(data.path)) - if !match { - return nil, errors.New("path does not match expected format m/…") + account, err := util.ParseAccount(ctx, data.mnemonic, []string{data.path}, true) + if err != nil { + return nil, errors.Wrap(err, "failed to derive account") } - // Derive private key from seed and path. - key, err := util.PrivateKeyFromSeedAndPath(seed, data.path) + key, err := account.(e2wtypes.AccountPrivateKeyProvider).PrivateKey(ctx) if err != nil { - return nil, errors.Wrap(err, "failed to generate key") + return nil, errors.Wrap(err, "failed to obtain account private key") } results := &dataOut{ - showPrivateKey: data.showPrivateKey, + showPrivateKey: data.showPrivateKey, showWithdrawalCredentials: data.showWithdrawalCredentials, - key: key, + key: key.(*e2types.BLSPrivateKey), } return results, nil diff --git a/cmd/account/derive/process_internal_test.go b/cmd/account/derive/process_internal_test.go index 1acd145..9bbd4f0 100644 --- a/cmd/account/derive/process_internal_test.go +++ b/cmd/account/derive/process_internal_test.go @@ -40,7 +40,7 @@ func TestProcess(t *testing.T) { dataIn: &dataIn{ path: "m/12381/3600/0/0", }, - err: "mnemonic is invalid", + err: "failed to derive account: no account specified", }, { name: "MnemonicInvalid", @@ -48,14 +48,14 @@ func TestProcess(t *testing.T) { mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art", path: "m/12381/3600/0/0", }, - err: "mnemonic is invalid", + err: "failed to derive account: mnemonic is invalid", }, { name: "PathMissing", dataIn: &dataIn{ mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art", }, - err: "path does not match expected format m/…", + err: "failed to derive account: path does not match expected format m/…", }, { name: "PathInvalid", @@ -63,7 +63,7 @@ func TestProcess(t *testing.T) { mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art", path: "n/12381/3600/0/0", }, - err: "path does not match expected format m/…", + err: "failed to derive account: path does not match expected format m/…", }, { name: "Good", diff --git a/cmd/account/derive/run.go b/cmd/account/derive/run.go index 2baaaa9..255de55 100644 --- a/cmd/account/derive/run.go +++ b/cmd/account/derive/run.go @@ -15,6 +15,7 @@ package accountderive import ( "context" + "strings" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -45,5 +46,5 @@ func Run(cmd *cobra.Command) (string, error) { return "", errors.Wrap(err, "failed to obtain output") } - return results, nil + return strings.TrimSuffix(results, "\n"), nil } diff --git a/cmd/accountcreate.go b/cmd/accountcreate.go index a05900a..b9009fb 100644 --- a/cmd/accountcreate.go +++ b/cmd/accountcreate.go @@ -49,7 +49,6 @@ func init() { accountFlags(accountCreateCmd) accountCreateCmd.Flags().Uint32("participants", 1, "Number of participants (1 for non-distributed accounts, >1 for distributed accounts)") accountCreateCmd.Flags().Uint32("signing-threshold", 1, "Signing threshold (1 for non-distributed accounts)") - accountCreateCmd.Flags().String("path", "", "path of account (for hierarchical deterministic accounts)") } func accountCreateBindings() { @@ -59,7 +58,4 @@ func accountCreateBindings() { if err := viper.BindPFlag("signing-threshold", accountCreateCmd.Flags().Lookup("signing-threshold")); err != nil { panic(err) } - if err := viper.BindPFlag("path", accountCreateCmd.Flags().Lookup("path")); err != nil { - panic(err) - } } diff --git a/cmd/accountderive.go b/cmd/accountderive.go index 245141d..270d654 100644 --- a/cmd/accountderive.go +++ b/cmd/accountderive.go @@ -47,19 +47,11 @@ In quiet mode this will return 0 if the inputs can derive an account account, ot func init() { accountCmd.AddCommand(accountDeriveCmd) accountFlags(accountDeriveCmd) - accountDeriveCmd.Flags().String("mnemonic", "", "mnemonic from which to derive the HD seed") - accountDeriveCmd.Flags().String("path", "", "path from which to derive the account") accountDeriveCmd.Flags().Bool("show-private-key", false, "show private key for derived account") accountDeriveCmd.Flags().Bool("show-withdrawal-credentials", false, "show withdrawal credentials for derived account") } func accountDeriveBindings() { - if err := viper.BindPFlag("mnemonic", accountDeriveCmd.Flags().Lookup("mnemonic")); err != nil { - panic(err) - } - if err := viper.BindPFlag("path", accountDeriveCmd.Flags().Lookup("path")); err != nil { - panic(err) - } if err := viper.BindPFlag("show-private-key", accountDeriveCmd.Flags().Lookup("show-private-key")); err != nil { panic(err) } diff --git a/cmd/block/info/output.go b/cmd/block/info/output.go index f550862..0c69ddd 100644 --- a/cmd/block/info/output.go +++ b/cmd/block/info/output.go @@ -296,7 +296,7 @@ func outputBellatrixBlockText(ctx context.Context, data *dataOut, signedBlock *b bodyRoot, signedBlock.Message.ParentRoot, signedBlock.Message.StateRoot, - signedBlock.Message.Body.Graffiti, + signedBlock.Message.Body.Graffiti[:], data.genesisTime, data.slotDuration, data.slotsPerEpoch) @@ -386,7 +386,7 @@ func outputAltairBlockText(ctx context.Context, data *dataOut, signedBlock *alta bodyRoot, signedBlock.Message.ParentRoot, signedBlock.Message.StateRoot, - signedBlock.Message.Body.Graffiti, + signedBlock.Message.Body.Graffiti[:], data.genesisTime, data.slotDuration, data.slotsPerEpoch) @@ -469,7 +469,7 @@ func outputPhase0BlockText(ctx context.Context, data *dataOut, signedBlock *phas bodyRoot, signedBlock.Message.ParentRoot, signedBlock.Message.StateRoot, - signedBlock.Message.Body.Graffiti, + signedBlock.Message.Body.Graffiti[:], data.genesisTime, data.slotDuration, data.slotsPerEpoch) diff --git a/cmd/chaininfo.go b/cmd/chaininfo.go index e2dbabd..b93d540 100644 --- a/cmd/chaininfo.go +++ b/cmd/chaininfo.go @@ -1,4 +1,4 @@ -// Copyright © 2020 Weald Technology Trading +// Copyright © 2020, 2022 Weald Technology Trading // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -53,6 +53,11 @@ In quiet mode this will return 0 if the chain information can be obtained, other os.Exit(_exitSuccess) } + if viper.GetBool("prepare-offline") { + fmt.Printf("Add the following to your command to run it offline:\n --offline --genesis-validators=root=%#x --fork-version=%#x\n", genesis.GenesisValidatorsRoot, fork.CurrentVersion) + os.Exit(_exitSuccess) + } + if genesis.GenesisTime.Unix() == 0 { fmt.Println("Genesis time: undefined") } else { @@ -84,4 +89,11 @@ In quiet mode this will return 0 if the chain information can be obtained, other func init() { chainCmd.AddCommand(chainInfoCmd) chainFlags(chainInfoCmd) + chainInfoCmd.Flags().Bool("prepare-offline", false, "Provide information useful for offline commands") +} + +func chainInfoBindings() { + if err := viper.BindPFlag("prepare-offline", chainInfoCmd.Flags().Lookup("prepare-offline")); err != nil { + panic(err) + } } diff --git a/cmd/root.go b/cmd/root.go index 0a95558..694a3f5 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -96,6 +96,8 @@ func includeCommandBindings(cmd *cobra.Command) { blockInfoBindings() case "chain/eth1votes": chainEth1VotesBindings() + case "chain/info": + chainInfoBindings() case "chain/queues": chainQueuesBindings() case "chain/time": @@ -118,6 +120,8 @@ func includeCommandBindings(cmd *cobra.Command) { synccommitteeMembersBindings() case "validator/credentials/get": validatorCredentialsGetBindings() + case "validator/credentials/set": + validatorCredentialsSetBindings() case "validator/depositdata": validatorDepositdataBindings() case "validator/duties": @@ -171,10 +175,26 @@ func init() { if err := viper.BindPFlag("store", RootCmd.PersistentFlags().Lookup("store")); err != nil { panic(err) } - RootCmd.PersistentFlags().String("account", "", "Account name (in format \"wallet/account\")") + RootCmd.PersistentFlags().String("account", "", `Account name (in format "/")`) if err := viper.BindPFlag("account", RootCmd.PersistentFlags().Lookup("account")); err != nil { panic(err) } + RootCmd.PersistentFlags().String("mnemonic", "", "Mnemonic to provide access to an account") + if err := viper.BindPFlag("mnemonic", RootCmd.PersistentFlags().Lookup("mnemonic")); err != nil { + panic(err) + } + RootCmd.PersistentFlags().String("path", "", "Hierarchical derivation path used with mnemonic to provide access to an account") + if err := viper.BindPFlag("path", RootCmd.PersistentFlags().Lookup("path")); err != nil { + panic(err) + } + RootCmd.PersistentFlags().String("private-key", "", "Private key to provide access to an account") + if err := viper.BindPFlag("private-key", RootCmd.PersistentFlags().Lookup("private-key")); err != nil { + panic(err) + } + RootCmd.PersistentFlags().String("public-key", "", "public key to provide access to an account") + if err := viper.BindPFlag("public-key", RootCmd.PersistentFlags().Lookup("public-key")); err != nil { + panic(err) + } RootCmd.PersistentFlags().String("basedir", "", "Base directory for filesystem wallets") if err := viper.BindPFlag("basedir", RootCmd.PersistentFlags().Lookup("basedir")); err != nil { panic(err) diff --git a/cmd/slot/time/output_internal_test.go b/cmd/slot/time/output_internal_test.go index 52a0d7f..a8ddb04 100644 --- a/cmd/slot/time/output_internal_test.go +++ b/cmd/slot/time/output_internal_test.go @@ -37,7 +37,7 @@ func TestOutput(t *testing.T) { dataOut: &dataOut{ startTime: time.Unix(1606824023, 0), }, - res: "2020-12-01 12:00:23 +0000 GMT", + res: "2020-12-01 12:00:23 +0000 UTC", }, { name: "Verbose", @@ -46,7 +46,7 @@ func TestOutput(t *testing.T) { endTime: time.Unix(1606824035, 0), verbose: true, }, - res: "2020-12-01 12:00:23 +0000 GMT - 2020-12-01 12:00:35 +0000 GMT", + res: "2020-12-01 12:00:23 +0000 UTC - 2020-12-01 12:00:35 +0000 UTC", }, } diff --git a/cmd/validator/credentials/get/command.go b/cmd/validator/credentials/get/command.go index 2c6784d..5ba50df 100644 --- a/cmd/validator/credentials/get/command.go +++ b/cmd/validator/credentials/get/command.go @@ -29,9 +29,7 @@ type command struct { debug bool // Input. - account string - index string - pubKey string + validator string // Beacon node connection. timeout time.Duration @@ -43,7 +41,7 @@ type command struct { validatorsProvider eth2client.ValidatorsProvider // Output. - validator *apiv1.Validator + validatorInfo *apiv1.Validator } func newCommand(ctx context.Context) (*command, error) { @@ -62,25 +60,10 @@ func newCommand(ctx context.Context) (*command, error) { c.connection = viper.GetString("connection") c.allowInsecureConnections = viper.GetBool("allow-insecure-connections") - c.account = viper.GetString("account") - c.index = viper.GetString("index") - c.pubKey = viper.GetString("pubkey") - nonNil := 0 - if c.account != "" { - nonNil++ - } - if c.index != "" { - nonNil++ - } - if c.pubKey != "" { - nonNil++ - } - if nonNil == 0 { - return nil, errors.New("one of account, index or pubkey required") - } - if nonNil > 1 { - return nil, errors.New("only one of account, index and pubkey allowed") + if viper.GetString("validator") == "" { + return nil, errors.New("validator is required") } + c.validator = viper.GetString("validator") return c, nil } diff --git a/cmd/validator/credentials/get/output.go b/cmd/validator/credentials/get/output.go index 9a69ea5..d1ac4bd 100644 --- a/cmd/validator/credentials/get/output.go +++ b/cmd/validator/credentials/get/output.go @@ -17,6 +17,8 @@ import ( "context" "fmt" "strings" + + ethutil "github.com/wealdtech/go-eth2-util" ) func (c *command) output(ctx context.Context) (string, error) { @@ -26,19 +28,38 @@ func (c *command) output(ctx context.Context) (string, error) { builder := strings.Builder{} - switch c.validator.Validator.WithdrawalCredentials[0] { + switch c.validatorInfo.Validator.WithdrawalCredentials[0] { case 0: builder.WriteString("BLS credentials: ") - builder.WriteString(fmt.Sprintf("%#x", c.validator.Validator.WithdrawalCredentials)) + builder.WriteString(fmt.Sprintf("%#x", c.validatorInfo.Validator.WithdrawalCredentials)) case 1: builder.WriteString("Ethereum execution address: ") - builder.WriteString(fmt.Sprintf("%#x", c.validator.Validator.WithdrawalCredentials[12:])) + builder.WriteString(addressBytesToEIP55(c.validatorInfo.Validator.WithdrawalCredentials[12:])) if c.verbose { builder.WriteString("\n") builder.WriteString("Withdrawal credentials: ") - builder.WriteString(fmt.Sprintf("%#x", c.validator.Validator.WithdrawalCredentials)) + builder.WriteString(fmt.Sprintf("%#x", c.validatorInfo.Validator.WithdrawalCredentials)) } } return builder.String(), nil } + +// addressBytesToEIP55 converts a byte array in to an EIP-55 string format. +func addressBytesToEIP55(address []byte) string { + bytes := []byte(fmt.Sprintf("%x", address)) + hash := ethutil.Keccak256(bytes) + for i := 0; i < len(bytes); i++ { + hashByte := hash[i/2] + if i%2 == 0 { + hashByte >>= 4 + } else { + hashByte &= 0xf + } + if bytes[i] > '9' && hashByte > 7 { + bytes[i] -= 32 + } + } + + return fmt.Sprintf("0x%s", string(bytes)) +} diff --git a/cmd/validator/credentials/get/process.go b/cmd/validator/credentials/get/process.go index 1c38527..4706484 100644 --- a/cmd/validator/credentials/get/process.go +++ b/cmd/validator/credentials/get/process.go @@ -65,17 +65,7 @@ func (c *command) setup(ctx context.Context) error { func (c *command) fetchValidator(ctx context.Context) error { var err error - switch { - case c.account != "": - c.validator, err = util.ParseValidator(ctx, c.validatorsProvider, c.account, "head") - case c.index != "": - c.validator, err = util.ParseValidator(ctx, c.validatorsProvider, c.index, "head") - case c.pubKey != "": - c.validator, err = util.ParseValidator(ctx, c.validatorsProvider, c.pubKey, "head") - default: - return errors.New("account, index or public key must be supplied") - } - + c.validatorInfo, err = util.ParseValidator(ctx, c.validatorsProvider, c.validator, "head") if err != nil { return errors.Wrap(err, "failed to obtain validator information") } diff --git a/cmd/validator/credentials/set/chaininfo.go b/cmd/validator/credentials/set/chaininfo.go new file mode 100644 index 0000000..8fa404a --- /dev/null +++ b/cmd/validator/credentials/set/chaininfo.go @@ -0,0 +1,127 @@ +// Copyright © 2022 Weald Technology Trading. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package validatorcredentialsset + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "strconv" + "strings" + + "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/pkg/errors" +) + +type chainInfo struct { + Version uint64 + Validators []*validatorInfo + GenesisValidatorsRoot phase0.Root + Epoch phase0.Epoch + ForkVersion phase0.Version + Domain phase0.Domain +} + +type chainInfoJSON struct { + Version string `json:"version"` + Validators []*validatorInfo `json:"validators"` + GenesisValidatorsRoot string `json:"genesis_validators_root"` + Epoch string `json:"epoch"` + ForkVersion string `json:"fork_version"` + Domain string `json:"domain"` +} + +// MarshalJSON implements json.Marshaler. +func (v *chainInfo) MarshalJSON() ([]byte, error) { + return json.Marshal(&chainInfoJSON{ + Version: fmt.Sprintf("%d", v.Version), + Validators: v.Validators, + GenesisValidatorsRoot: fmt.Sprintf("%#x", v.GenesisValidatorsRoot), + Epoch: fmt.Sprintf("%d", v.Epoch), + ForkVersion: fmt.Sprintf("%#x", v.ForkVersion), + Domain: fmt.Sprintf("%#x", v.Domain), + }) +} + +// UnmarshalJSON implements json.Unmarshaler. +func (v *chainInfo) UnmarshalJSON(input []byte) error { + var data chainInfoJSON + if err := json.Unmarshal(input, &data); err != nil { + return errors.Wrap(err, "invalid JSON") + } + + if data.Version == "" { + // Default to 1. + v.Version = 1 + } else { + version, err := strconv.ParseUint(data.Version, 10, 64) + if err != nil { + return errors.Wrap(err, "version invalid") + } + v.Version = version + } + + if len(data.Validators) == 0 { + return errors.New("validators missing") + } + v.Validators = data.Validators + + if data.GenesisValidatorsRoot == "" { + return errors.New("genesis validators root missing") + } + + genesisValidatorsRootBytes, err := hex.DecodeString(strings.TrimPrefix(data.GenesisValidatorsRoot, "0x")) + if err != nil { + return errors.Wrap(err, "genesis validators root invalid") + } + if len(genesisValidatorsRootBytes) != phase0.RootLength { + return errors.New("genesis validators root incorrect length") + } + copy(v.GenesisValidatorsRoot[:], genesisValidatorsRootBytes) + + if data.Epoch == "" { + return errors.New("epoch missing") + } + epoch, err := strconv.ParseUint(data.Epoch, 10, 64) + if err != nil { + return errors.Wrap(err, "epoch invalid") + } + v.Epoch = phase0.Epoch(epoch) + + if data.ForkVersion == "" { + return errors.New("fork version missing") + } + forkVersionBytes, err := hex.DecodeString(strings.TrimPrefix(data.ForkVersion, "0x")) + if err != nil { + return errors.Wrap(err, "fork version invalid") + } + if len(forkVersionBytes) != phase0.ForkVersionLength { + return errors.New("fork version incorrect length") + } + copy(v.ForkVersion[:], forkVersionBytes) + + if data.Domain == "" { + return errors.New("domain missing") + } + domainBytes, err := hex.DecodeString(strings.TrimPrefix(data.Domain, "0x")) + if err != nil { + return errors.Wrap(err, "domain invalid") + } + if len(domainBytes) != phase0.DomainLength { + return errors.New("domain incorrect length") + } + copy(v.Domain[:], domainBytes) + + return nil +} diff --git a/cmd/validator/credentials/set/command.go b/cmd/validator/credentials/set/command.go new file mode 100644 index 0000000..ef64f44 --- /dev/null +++ b/cmd/validator/credentials/set/command.go @@ -0,0 +1,104 @@ +// Copyright © 2022 Weald Technology Trading. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package validatorcredentialsset + +import ( + "context" + "time" + + consensusclient "github.com/attestantio/go-eth2-client" + "github.com/attestantio/go-eth2-client/spec/bellatrix" + capella "github.com/attestantio/go-eth2-client/spec/capella" + "github.com/pkg/errors" + "github.com/spf13/viper" + "github.com/wealdtech/ethdo/services/chaintime" + "github.com/wealdtech/ethdo/util" +) + +type command struct { + quiet bool + verbose bool + debug bool + offline bool + json bool + + // Input. + account string + passphrases []string + mnemonic string + path string + privateKey string + validator string + withdrawalAddressStr string + forkVersion string + genesisValidatorsRoot string + prepareOffline bool + + // Beacon node connection. + timeout time.Duration + connection string + allowInsecureConnections bool + + // Information required to generate the operations. + withdrawalAddress bellatrix.ExecutionAddress + chainInfo *chainInfo + + // Processing. + consensusClient consensusclient.Service + chainTime chaintime.Service + + // Output. + signedOperations []*capella.SignedBLSToExecutionChange +} + +func newCommand(ctx context.Context) (*command, error) { + c := &command{ + quiet: viper.GetBool("quiet"), + verbose: viper.GetBool("verbose"), + debug: viper.GetBool("debug"), + offline: viper.GetBool("offline"), + json: viper.GetBool("json"), + timeout: viper.GetDuration("timeout"), + connection: viper.GetString("connection"), + allowInsecureConnections: viper.GetBool("allow-insecure-connections"), + prepareOffline: viper.GetBool("prepare-offline"), + account: viper.GetString("account"), + passphrases: util.GetPassphrases(), + mnemonic: viper.GetString("mnemonic"), + path: viper.GetString("path"), + privateKey: viper.GetString("private-key"), + + validator: viper.GetString("validator"), + withdrawalAddressStr: viper.GetString("withdrawal-address"), + forkVersion: viper.GetString("fork-version"), + genesisValidatorsRoot: viper.GetString("genesis-validators-root"), + } + + // Timeout is required. + if c.timeout == 0 { + return nil, errors.New("timeout is required") + } + + // We are generating information for offline use, we don't need any information + // related to the accounts or signing. + if c.prepareOffline { + return c, nil + } + + if c.account != "" && len(c.passphrases) == 0 { + return nil, errors.New("passphrase required with account") + } + + return c, nil +} diff --git a/cmd/validator/credentials/set/command_internal_test.go b/cmd/validator/credentials/set/command_internal_test.go new file mode 100644 index 0000000..0beba2a --- /dev/null +++ b/cmd/validator/credentials/set/command_internal_test.go @@ -0,0 +1,83 @@ +// Copyright © 2022 Weald Technology Trading. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package validatorcredentialsset + +import ( + "context" + "os" + "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/require" +) + +func TestInput(t *testing.T) { + if os.Getenv("ETHDO_TEST_CONNECTION") == "" { + t.Skip("ETHDO_TEST_CONNECTION not configured; cannot run tests") + } + + tests := []struct { + name string + vars map[string]interface{} + err string + }{ + { + name: "TimeoutMissing", + vars: map[string]interface{}{}, + err: "timeout is required", + }, + { + name: "NoValidatorInfo", + vars: map[string]interface{}{ + "timeout": "5s", + "connection": os.Getenv("ETHDO_TEST_CONNECTION"), + }, + err: "one of account, index or pubkey required", + }, + { + name: "MultipleValidatorInfo", + vars: map[string]interface{}{ + "timeout": "5s", + "connection": os.Getenv("ETHDO_TEST_CONNECTION"), + "index": "1", + "pubkey": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + }, + err: "only one of account, index and pubkey allowed", + }, + { + name: "Good", + vars: map[string]interface{}{ + "timeout": "5s", + "connection": os.Getenv("ETHDO_TEST_CONNECTION"), + "index": "1", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + viper.Reset() + + for k, v := range test.vars { + viper.Set(k, v) + } + _, err := newCommand(context.Background()) + if test.err != "" { + require.EqualError(t, err, test.err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/cmd/validator/credentials/set/output.go b/cmd/validator/credentials/set/output.go new file mode 100644 index 0000000..2a94a55 --- /dev/null +++ b/cmd/validator/credentials/set/output.go @@ -0,0 +1,41 @@ +// Copyright © 2022 Weald Technology Trading. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package validatorcredentialsset + +import ( + "context" + "encoding/json" + "os" + + "github.com/pkg/errors" +) + +func (c *command) output(ctx context.Context) (string, error) { + if c.quiet { + return "", nil + } + + if c.json || c.offline { + data, err := json.Marshal(c.signedOperations) + if err != nil { + return "", errors.Wrap(err, "failed to marshal signed operations") + } + if err := os.WriteFile("credentials-operations.json", data, 0600); err != nil { + return "", errors.Wrap(err, "failed to write credentials-operations.json") + } + return "", nil + } + + return "", nil +} diff --git a/cmd/validator/credentials/set/process.go b/cmd/validator/credentials/set/process.go new file mode 100644 index 0000000..0c2fc89 --- /dev/null +++ b/cmd/validator/credentials/set/process.go @@ -0,0 +1,626 @@ +// Copyright © 2022 Weald Technology Trading. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package validatorcredentialsset + +import ( + "bytes" + "context" + "encoding/hex" + "encoding/json" + "fmt" + "os" + "regexp" + "strconv" + "strings" + + consensusclient "github.com/attestantio/go-eth2-client" + "github.com/attestantio/go-eth2-client/spec/bellatrix" + capella "github.com/attestantio/go-eth2-client/spec/capella" + "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/pkg/errors" + standardchaintime "github.com/wealdtech/ethdo/services/chaintime/standard" + "github.com/wealdtech/ethdo/signing" + "github.com/wealdtech/ethdo/util" + ethutil "github.com/wealdtech/go-eth2-util" + e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2" +) + +// validatorPath is the regular expression that matches a validator path. +var validatorPath = regexp.MustCompile("^m/12381/3600/[0-9]+/0/0$") + +var offlinePreparationFilename = "offline-preparation.json" +var changeOperationsFilename = "change-operations.json" + +func (c *command) process(ctx context.Context) error { + if err := c.setup(ctx); err != nil { + return err + } + + if err := c.obtainRequiredInformation(ctx); err != nil { + return err + } + + if c.prepareOffline { + return c.dumpRequiredInformation(ctx) + } + + if err := c.generateOperations(ctx); err != nil { + return err + } + + if validated, reason := c.validateOperations(ctx); !validated { + return fmt.Errorf("operation failed validation: %s", reason) + } + + if c.json || c.offline { + // Want JSON output, or cannot broadcast. + return nil + } + + return c.broadcastOperations(ctx) +} + +// obtainRequiredInformation obtains the information required to create a +// withdrawal credentials change operation. +func (c *command) obtainRequiredInformation(ctx context.Context) error { + c.chainInfo = &chainInfo{ + Validators: make([]*validatorInfo, 0), + } + + // Use the offline preparation file if present (and we haven't been asked to recreate it). + if !c.prepareOffline { + err := c.loadChainInfo(ctx) + if err == nil { + return nil + } + } + + if c.offline { + return fmt.Errorf("could not find the %s file; this is required to have been previously generated using --offline-preparation on an online mcahine and be readable in the directory in which this command is being run", offlinePreparationFilename) + } + + if err := c.populateChainInfo(ctx); err != nil { + return err + } + + return nil +} + +// populateChainInfo populates chain info structure from a beacon node. +func (c *command) populateChainInfo(ctx context.Context) error { + // Obtain validators. + validators, err := c.consensusClient.(consensusclient.ValidatorsProvider).Validators(ctx, "head", nil) + if err != nil { + return errors.Wrap(err, "failed to obtain validators") + } + + for _, validator := range validators { + c.chainInfo.Validators = append(c.chainInfo.Validators, &validatorInfo{ + Index: validator.Index, + Pubkey: validator.Validator.PublicKey, + WithdrawalCredentials: validator.Validator.WithdrawalCredentials, + }) + } + + // Obtain genesis validators root. + genesis, err := c.consensusClient.(consensusclient.GenesisProvider).Genesis(ctx) + if err != nil { + return errors.Wrap(err, "failed to obtain genesis information") + } + c.chainInfo.GenesisValidatorsRoot = genesis.GenesisValidatorsRoot + + // Obtain epoch. + c.chainInfo.Epoch = c.chainTime.CurrentEpoch() + + // Obtain fork version. + forkSchedule, err := c.consensusClient.(consensusclient.ForkScheduleProvider).ForkSchedule(ctx) + if err != nil { + return errors.Wrap(err, "failed to obtain fork schedule") + } + for i := range forkSchedule { + if forkSchedule[i].Epoch <= c.chainInfo.Epoch { + c.chainInfo.ForkVersion = forkSchedule[i].CurrentVersion + } + } + + // Calculate domain. + spec, err := c.consensusClient.(consensusclient.SpecProvider).Spec(ctx) + if err != nil { + return errors.Wrap(err, "failed to obtain spec") + } + domainType, exists := spec["DOMAIN_BLS_TO_EXECUTION_CHANGE"].(phase0.DomainType) + if !exists { + return errors.New("failed to obtain DOMAIN_BLS_TO_EXECUTION_CHANGE") + } + domainProvider, isProvider := c.consensusClient.(consensusclient.DomainProvider) + if !isProvider { + return errors.New("consensus node does not provide domain information") + } + c.chainInfo.Domain, err = domainProvider.Domain(ctx, domainType, c.chainInfo.Epoch) + if err != nil { + return errors.Wrap(err, "failed to obtain domain") + } + + return nil +} + +// dumpRequiredInformation prepares for an offline run of this command by dumping +// the chain information to a file. +func (c *command) dumpRequiredInformation(ctx context.Context) error { + data, err := json.Marshal(c.chainInfo) + if err != nil { + return err + } + if err := os.WriteFile(offlinePreparationFilename, data, 0600); err != nil { + return err + } + + return nil +} + +func (c *command) generateOperations(ctx context.Context) error { + // Ensure that we are beyond the capella hard fork epoch. + if c.chainTime.CurrentEpoch() < c.chainTime.CapellaInitialEpoch() { + return errors.New("chain not yet activated capella hard fork") + } + + if c.account == "" && c.mnemonic == "" && c.privateKey == "" { + // No input information; fetch the operations from a file. + if err := c.loadOperations(ctx); err == nil { + return nil + } + return fmt.Errorf("no account, mnemonic or private key specified and no %s file found; cannot proceed", changeOperationsFilename) + } + + if c.mnemonic != "" && c.path == "" { + // Have a mnemonic and no path; scan mnemonic. + return c.generateOperationsFromMnemonic(ctx) + } + + if c.mnemonic != "" && c.path != "" { + // Have a mnemonic and path. + return c.generateOperationsFromMnemonicAndPath(ctx) + } + + // Have a validator index or public key ; fetch the validator info. + validatorInfo, err := c.fetchValidatorInfo(ctx) + if err != nil { + return err + } + + // Fetch the individual account. + withdrawalAccount, err := c.fetchAccount(ctx) + if err != nil { + return err + } + + // Generate the operation. + if err := c.generateOperationFromAccount(ctx, validatorInfo, withdrawalAccount); err != nil { + return err + } + + return nil +} + +func (c *command) loadChainInfo(ctx context.Context) error { + _, err := os.Stat(offlinePreparationFilename) + if err != nil { + if c.debug { + fmt.Printf("Failed to read offline preparation file: %v\n", err) + } + } + + if c.debug { + fmt.Printf("%s found; loading chain state\n", offlinePreparationFilename) + } + data, err := os.ReadFile(offlinePreparationFilename) + if err != nil { + return errors.Wrap(err, "failed to read offline preparation file") + } + if err := json.Unmarshal(data, c.chainInfo); err != nil { + return errors.Wrap(err, "failed to parse offline preparation file") + } + + return nil +} + +func (c *command) loadOperations(ctx context.Context) error { + _, err := os.Stat(changeOperationsFilename) + if err != nil { + if c.debug { + fmt.Printf("Failed to read change operations file: %v\n", err) + } + return err + } + + if c.debug { + fmt.Printf("%s found; loading operations\n", changeOperationsFilename) + } + data, err := os.ReadFile(changeOperationsFilename) + if err != nil { + return errors.Wrap(err, "failed to read change operations file") + } + if err := json.Unmarshal(data, &c.signedOperations); err != nil { + return errors.Wrap(err, "failed to parse change operations file") + } + + return nil +} + +func (c *command) generateOperationsFromMnemonic(ctx context.Context) error { + seed, err := util.SeedFromMnemonic(c.mnemonic) + if err != nil { + return err + } + + // Turn the validators in to a map for easy lookup. + validators := make(map[string]*validatorInfo, 0) + for _, validator := range c.chainInfo.Validators { + validators[fmt.Sprintf("%#x", validator.Pubkey)] = validator + } + + maxDistance := 1024 + // Start scanning the validator keys. + lastFoundIndex := 0 + for i := 0; ; i++ { + if i-lastFoundIndex > maxDistance { + if c.debug { + fmt.Printf("Gone %d indices without finding a validator, not scanning any further\n", maxDistance) + } + break + } + validatorKeyPath := fmt.Sprintf("m/12381/3600/%d/0/0", i) + + found, err := c.generateOperationFromSeedAndPath(ctx, validators, seed, validatorKeyPath) + if err != nil { + return errors.Wrap(err, "failed to generate operation from seed and path") + } + if found { + lastFoundIndex = i + } + } + return nil +} + +func (c *command) generateOperationFromSeedAndPath(ctx context.Context, + validators map[string]*validatorInfo, + seed []byte, + path string, +) ( + bool, + error, +) { + validatorPrivkey, err := ethutil.PrivateKeyFromSeedAndPath(seed, path) + if err != nil { + return false, errors.Wrap(err, "failed to generate validator private key") + } + validatorPubkey := fmt.Sprintf("%#x", validatorPrivkey.PublicKey().Marshal()) + validator, exists := validators[validatorPubkey] + if !exists { + if c.debug { + fmt.Printf("No validator found with public key %s at path %s\n", validatorPubkey, path) + } + return false, nil + } + + if c.verbose { + fmt.Printf("Validator %d found with public key %s at path %s\n", validator.Index, validatorPubkey, path) + } + + if validator.WithdrawalCredentials[0] != byte(0) { + if c.debug { + fmt.Printf("Validator %s has non-BLS withdrawal credentials %#x\n", validatorPubkey, validator.WithdrawalCredentials) + } + return false, nil + } + + // Recreate the withdrawal credentials to ensure a match. + withdrawalKeyPath := strings.TrimSuffix(path, "/0") + withdrawalPrivkey, err := ethutil.PrivateKeyFromSeedAndPath(seed, withdrawalKeyPath) + if err != nil { + return false, errors.Wrap(err, "failed to generate withdrawal private key") + } + withdrawalPubkey := withdrawalPrivkey.PublicKey() + withdrawalCredentials := ethutil.SHA256(withdrawalPubkey.Marshal()) + withdrawalCredentials[0] = byte(0) // BLS_WITHDRAWAL_PREFIX + if !bytes.Equal(withdrawalCredentials, validator.WithdrawalCredentials) { + if c.verbose { + fmt.Printf("Validator %s withdrawal credentials %#x do not match expected credentials, cannot update\n", validatorPubkey, validator.WithdrawalCredentials) + } + return false, nil + } + + if c.debug { + fmt.Printf("Validator %s eligible for setting credentials\n", validatorPubkey) + } + + withdrawalAccount, err := util.ParseAccount(ctx, c.mnemonic, []string{withdrawalKeyPath}, true) + if err != nil { + return false, errors.Wrap(err, "failed to create withdrawal account") + } + + err = c.generateOperationFromAccount(ctx, validator, withdrawalAccount) + if err != nil { + return false, err + } + + return true, nil +} + +func (c *command) generateOperationFromAccount(ctx context.Context, + validator *validatorInfo, + withdrawalAccount e2wtypes.Account, +) error { + signedOperation, err := c.createSignedOperation(ctx, validator, withdrawalAccount) + if err != nil { + return err + } + c.signedOperations = append(c.signedOperations, signedOperation) + return nil +} + +func (c *command) createSignedOperation(ctx context.Context, + validator *validatorInfo, + withdrawalAccount e2wtypes.Account, +) ( + *capella.SignedBLSToExecutionChange, + error, +) { + pubkey, err := util.BestPublicKey(withdrawalAccount) + if err != nil { + return nil, err + } + blsPubkey := phase0.BLSPubKey{} + copy(blsPubkey[:], pubkey.Marshal()) + + if err := c.parseWithdrawalAddress(ctx); err != nil { + return nil, errors.Wrap(err, "invalid withdrawal address") + } + + operation := &capella.BLSToExecutionChange{ + ValidatorIndex: validator.Index, + FromBLSPubkey: blsPubkey, + ToExecutionAddress: c.withdrawalAddress, + } + root, err := operation.HashTreeRoot() + if err != nil { + return nil, errors.Wrap(err, "failed to generate root for credentials change operation") + } + + // Sign the operation. + signature, err := signing.SignRoot(ctx, withdrawalAccount, nil, root, c.chainInfo.Domain) + if err != nil { + return nil, errors.Wrap(err, "failed to sign credentials change operation") + } + + return &capella.SignedBLSToExecutionChange{ + Message: operation, + Signature: signature, + }, nil +} + +func (c *command) parseWithdrawalAddress(ctx context.Context) error { + withdrawalAddressBytes, err := hex.DecodeString(strings.TrimPrefix(c.withdrawalAddressStr, "0x")) + if err != nil { + return errors.Wrap(err, "failed to obtain execution address") + } + if len(withdrawalAddressBytes) != bellatrix.ExecutionAddressLength { + return errors.New("withdrawal address must be exactly 20 bytes in length") + } + // Ensure the address is properly checksummed. + checksummedAddress := addressBytesToEIP55(withdrawalAddressBytes) + if checksummedAddress != c.withdrawalAddressStr { + return fmt.Errorf("withdrawal address checksum does not match (expected %s)", checksummedAddress) + } + copy(c.withdrawalAddress[:], withdrawalAddressBytes) + + return nil +} + +func (c *command) validateOperations(ctx context.Context) (bool, string) { + // Turn the validators in to a map for easy lookup. + validators := make(map[phase0.ValidatorIndex]*validatorInfo, 0) + for _, validator := range c.chainInfo.Validators { + validators[validator.Index] = validator + } + + for _, signedOperation := range c.signedOperations { + if validated, reason := c.validateOperation(ctx, validators, signedOperation); !validated { + return validated, reason + } + } + return true, "" +} + +func (c *command) validateOperation(ctx context.Context, + validators map[phase0.ValidatorIndex]*validatorInfo, + signedOperation *capella.SignedBLSToExecutionChange, +) ( + bool, + string, +) { + validator, exists := validators[signedOperation.Message.ValidatorIndex] + if !exists { + return false, "validator not known on chain" + } + if c.debug { + fmt.Printf("Credentials change operation: %v", signedOperation) + fmt.Printf("On-chain validator info: %v\n", validator) + } + + if validator.WithdrawalCredentials[0] != byte(0) { + return false, "validator is not using BLS withdrawal credentials" + } + + withdrawalCredentials := ethutil.SHA256(signedOperation.Message.FromBLSPubkey[:]) + withdrawalCredentials[0] = byte(0) // BLS_WITHDRAWAL_PREFIX + if !bytes.Equal(withdrawalCredentials, validator.WithdrawalCredentials) { + if c.debug { + fmt.Printf("validator withdrawal credentials %#x do not match calculated operation withdrawal credentials %#x\n", validator.WithdrawalCredentials, withdrawalCredentials) + } + return false, "validator withdrawal credentials do not match those in the operation" + } + + return true, "" +} + +func (c *command) broadcastOperations(ctx context.Context) error { + // Broadcast the operations. + for _, signedOperation := range c.signedOperations { + if err := c.consensusClient.(consensusclient.BLSToExecutionChangeSubmitter).SubmitBLSToExecutionChange(ctx, signedOperation); err != nil { + return err + } + } + + return nil +} + +func (c *command) setup(ctx context.Context) error { + if c.offline { + return nil + } + + // Connect to the consensus node. + var err error + c.consensusClient, err = util.ConnectToBeaconNode(ctx, c.connection, c.timeout, c.allowInsecureConnections) + if err != nil { + return errors.Wrap(err, "failed to connect to consensus node") + } + + // Set up chaintime. + c.chainTime, err = standardchaintime.New(ctx, + standardchaintime.WithGenesisTimeProvider(c.consensusClient.(consensusclient.GenesisTimeProvider)), + standardchaintime.WithForkScheduleProvider(c.consensusClient.(consensusclient.ForkScheduleProvider)), + standardchaintime.WithSpecProvider(c.consensusClient.(consensusclient.SpecProvider)), + ) + if err != nil { + return errors.Wrap(err, "failed to create chaintime service") + } + + return nil +} + +func (c *command) fetchValidatorInfo(ctx context.Context) (*validatorInfo, error) { + var validatorInfo *validatorInfo + switch { + case c.validator == "": + return nil, errors.New("no validator specified") + case strings.HasPrefix(c.validator, "0x"): + // A public key + for _, validator := range c.chainInfo.Validators { + if strings.EqualFold(c.validator, fmt.Sprintf("%#x", validator.Pubkey)) { + validatorInfo = validator + break + } + } + case strings.Contains(c.validator, "/"): + // An account. + _, account, err := util.WalletAndAccountFromPath(ctx, c.validator) + if err != nil { + return nil, errors.Wrap(err, "unable to obtain account") + } + accPubKey, err := util.BestPublicKey(account) + if err != nil { + return nil, errors.Wrap(err, "unable to obtain public key for account") + } + pubkey := fmt.Sprintf("%#x", accPubKey.Marshal()) + for _, validator := range c.chainInfo.Validators { + if strings.EqualFold(pubkey, fmt.Sprintf("%#x", validator.Pubkey)) { + validatorInfo = validator + break + } + } + default: + // An index. + index, err := strconv.ParseUint(c.validator, 10, 64) + if err != nil { + return nil, errors.Wrap(err, "failed to parse validator index") + } + validatorIndex := phase0.ValidatorIndex(index) + for _, validator := range c.chainInfo.Validators { + if validator.Index == validatorIndex { + validatorInfo = validator + break + } + } + } + + if validatorInfo == nil { + return nil, errors.New("unknown validator") + } + + return validatorInfo, nil +} + +func (c *command) fetchAccount(ctx context.Context) (e2wtypes.Account, error) { + var account e2wtypes.Account + var err error + + switch { + case c.account != "": + account, err = util.ParseAccount(ctx, c.account, c.passphrases, true) + case c.mnemonic != "": + account, err = util.ParseAccount(ctx, c.mnemonic, []string{c.path}, true) + case c.privateKey != "": + account, err = util.ParseAccount(ctx, c.privateKey, nil, true) + default: + err = errors.New("account, mnemonic or private key must be supplied") + } + + return account, err +} + +// addressBytesToEIP55 converts a byte array in to an EIP-55 string format. +func addressBytesToEIP55(address []byte) string { + bytes := []byte(fmt.Sprintf("%x", address)) + hash := ethutil.Keccak256(bytes) + for i := 0; i < len(bytes); i++ { + hashByte := hash[i/2] + if i%2 == 0 { + hashByte >>= 4 + } else { + hashByte &= 0xf + } + if bytes[i] > '9' && hashByte > 7 { + bytes[i] -= 32 + } + } + + return fmt.Sprintf("0x%s", string(bytes)) +} + +func (c *command) generateOperationsFromMnemonicAndPath(ctx context.Context) error { + seed, err := util.SeedFromMnemonic(c.mnemonic) + if err != nil { + return err + } + + // Turn the validators in to a map for easy lookup. + validators := make(map[string]*validatorInfo, 0) + for _, validator := range c.chainInfo.Validators { + validators[fmt.Sprintf("%#x", validator.Pubkey)] = validator + } + + validatorKeyPath := c.path + match := validatorPath.Match([]byte(c.path)) + if !match { + return fmt.Errorf("path %s does not match EIP-2334 format", c.path) + } + + if _, err := c.generateOperationFromSeedAndPath(ctx, validators, seed, validatorKeyPath); err != nil { + return errors.Wrap(err, "failed to generate operation from seed and path") + } + + return nil +} diff --git a/cmd/validator/credentials/set/run.go b/cmd/validator/credentials/set/run.go new file mode 100644 index 0000000..33417d2 --- /dev/null +++ b/cmd/validator/credentials/set/run.go @@ -0,0 +1,50 @@ +// Copyright © 2022 Weald Technology Trading. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package validatorcredentialsset + +import ( + "context" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// Run runs the command. +func Run(cmd *cobra.Command) (string, error) { + ctx := context.Background() + + c, err := newCommand(ctx) + if err != nil { + return "", errors.Wrap(err, "failed to set up command") + } + + // Further errors do not need a usage report. + cmd.SilenceUsage = true + + if err := c.process(ctx); err != nil { + return "", errors.Wrap(err, "failed to process") + } + + if viper.GetBool("quiet") { + return "", nil + } + + results, err := c.output(ctx) + if err != nil { + return "", errors.Wrap(err, "failed to obtain output") + } + + return results, nil +} diff --git a/cmd/validator/credentials/set/validatorinfo.go b/cmd/validator/credentials/set/validatorinfo.go new file mode 100644 index 0000000..1709b7d --- /dev/null +++ b/cmd/validator/credentials/set/validatorinfo.go @@ -0,0 +1,97 @@ +// Copyright © 2022 Weald Technology Trading. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package validatorcredentialsset + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "strconv" + "strings" + + "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/pkg/errors" +) + +type validatorInfo struct { + Index phase0.ValidatorIndex + Pubkey phase0.BLSPubKey + WithdrawalCredentials []byte +} + +type validatorInfoJSON struct { + Index string `json:"index"` + Pubkey string `json:"pubkey"` + WithdrawalCredentials string `json:"withdrawal_credentials"` +} + +// MarshalJSON implements json.Marshaler. +func (v *validatorInfo) MarshalJSON() ([]byte, error) { + return json.Marshal(&validatorInfoJSON{ + Index: fmt.Sprintf("%d", v.Index), + Pubkey: fmt.Sprintf("%#x", v.Pubkey), + WithdrawalCredentials: fmt.Sprintf("%#x", v.WithdrawalCredentials), + }) +} + +// UnmarshalJSON implements json.Unmarshaler. +func (v *validatorInfo) UnmarshalJSON(input []byte) error { + var data validatorInfoJSON + if err := json.Unmarshal(input, &data); err != nil { + return errors.Wrap(err, "invalid JSON") + } + + if data.Index == "" { + return errors.New("index missing") + } + index, err := strconv.ParseUint(data.Index, 10, 64) + if err != nil { + return errors.Wrap(err, "invalid value for index") + } + v.Index = phase0.ValidatorIndex(index) + + if data.Pubkey == "" { + return errors.New("public key missing") + } + pubkey, err := hex.DecodeString(strings.TrimPrefix(data.Pubkey, "0x")) + if err != nil { + return errors.Wrap(err, "invalid value for public key") + } + if len(pubkey) != phase0.PublicKeyLength { + return fmt.Errorf("incorrect length %d for public key", len(pubkey)) + } + copy(v.Pubkey[:], pubkey) + + if data.WithdrawalCredentials == "" { + return errors.New("withdrawal credentials missing") + } + v.WithdrawalCredentials, err = hex.DecodeString(strings.TrimPrefix(data.WithdrawalCredentials, "0x")) + if err != nil { + return errors.Wrap(err, "invalid value for withdrawal credentials") + } + if len(v.WithdrawalCredentials) != phase0.HashLength { + return fmt.Errorf("incorrect length %d for withdrawal credentials", len(v.WithdrawalCredentials)) + } + + return nil +} + +// String implements the Stringer interface. +func (v *validatorInfo) String() string { + data, err := json.Marshal(v) + if err != nil { + return fmt.Sprintf("Err: %v\n", err) + } + return string(data) +} diff --git a/cmd/validatorcredentialsget.go b/cmd/validatorcredentialsget.go index 9556d2b..7cd0344 100644 --- a/cmd/validatorcredentialsget.go +++ b/cmd/validatorcredentialsget.go @@ -26,7 +26,7 @@ var validatorCredentialsGetCmd = &cobra.Command{ Short: "Obtain withdrawal credentials for an Ethereum consensus validator", Long: `Obtain withdrawal credentials for an Ethereum consensus validator. For example: - ethdo validator credentials get --account=primary/validator + ethdo validator credentials get --validator=primary/validator In quiet mode this will return 0 if the validator exists, otherwise 1.`, RunE: func(cmd *cobra.Command, args []string) error { @@ -47,19 +47,11 @@ In quiet mode this will return 0 if the validator exists, otherwise 1.`, func init() { validatorCredentialsCmd.AddCommand(validatorCredentialsGetCmd) validatorCredentialsFlags(validatorCredentialsGetCmd) - validatorCredentialsGetCmd.Flags().String("account", "", "Account for which to fetch validator credentials") - validatorCredentialsGetCmd.Flags().String("index", "", "Validator index for which to fetch validator credentials") - validatorCredentialsGetCmd.Flags().String("pubkey", "", "Validator public key for which to fetch validator credentials") + validatorCredentialsGetCmd.Flags().String("validator", "", "Validator for which to get validator credentials") } func validatorCredentialsGetBindings() { - if err := viper.BindPFlag("account", validatorCredentialsGetCmd.Flags().Lookup("account")); err != nil { - panic(err) - } - if err := viper.BindPFlag("index", validatorCredentialsGetCmd.Flags().Lookup("index")); err != nil { - panic(err) - } - if err := viper.BindPFlag("pubkey", validatorCredentialsGetCmd.Flags().Lookup("pubkey")); err != nil { + if err := viper.BindPFlag("validator", validatorCredentialsGetCmd.Flags().Lookup("validator")); err != nil { panic(err) } } diff --git a/cmd/validatorcredentialsset.go b/cmd/validatorcredentialsset.go new file mode 100644 index 0000000..9bdebc5 --- /dev/null +++ b/cmd/validatorcredentialsset.go @@ -0,0 +1,92 @@ +// Copyright © 2022 Weald Technology Trading +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + validatorcredentialsset "github.com/wealdtech/ethdo/cmd/validator/credentials/set" +) + +var validatorCredentialsSetCmd = &cobra.Command{ + Use: "set", + Short: "Set withdrawal credentials for an Ethereum consensus validator", + Long: `Set withdrawal credentials for an Ethereum consensus validator via a "change credentials" operation. For example: + + ethdo validator credentials set --validator=primary/validator --withdrawal-address=0x00...13 --private-key=0x00...1f + +The existing account can be specified in one of a number of ways: + + - mnemonic using --mnemonic; this will scan the mnemonic and generate all required operations + - mnemonic and path to the validator key using --mnemonic and --path; this will generate a single operation + - private key using --private-key; this will generate a single operation + - account and passphrase using --account and --passphrase; this will generate a single operation + +In quiet mode this will return 0 if the credentials operation has been generated (and successfully broadcast if online), otherwise 1.`, + RunE: func(cmd *cobra.Command, args []string) error { + res, err := validatorcredentialsset.Run(cmd) + if err != nil { + return err + } + if viper.GetBool("quiet") { + return nil + } + if res != "" { + fmt.Println(res) + } + return nil + }, +} + +func init() { + validatorCredentialsCmd.AddCommand(validatorCredentialsSetCmd) + validatorCredentialsFlags(validatorCredentialsSetCmd) + validatorCredentialsSetCmd.Flags().Bool("prepare-offline", false, "Create files for offline use") + validatorCredentialsSetCmd.Flags().String("validator", "", "Validator for which to set validator credentials") + validatorCredentialsSetCmd.Flags().String("withdrawal-address", "", "Execution address to which to direct withdrawals") + validatorCredentialsSetCmd.Flags().String("signed-operation", "", "Use pre-defined JSON signed operation as created by --json to transmit the credentials change operation") + validatorCredentialsSetCmd.Flags().Bool("json", false, "Generate JSON data containing a signed operation rather than broadcast it to the network (implied when offline)") + validatorCredentialsSetCmd.Flags().Bool("offline", false, "Do not attempt to connect to a beacon node to obtain information for the operation") + validatorCredentialsSetCmd.Flags().String("fork-version", "", "Fork version to use for signing (offline only)") + validatorCredentialsSetCmd.Flags().String("genesis-validators-root", "", "Genesis validators root to use for signing (offline only)") +} + +func validatorCredentialsSetBindings() { + if err := viper.BindPFlag("prepare-offline", validatorCredentialsSetCmd.Flags().Lookup("prepare-offline")); err != nil { + panic(err) + } + if err := viper.BindPFlag("validator", validatorCredentialsSetCmd.Flags().Lookup("validator")); err != nil { + panic(err) + } + if err := viper.BindPFlag("signed-operation", validatorCredentialsSetCmd.Flags().Lookup("signed-operation")); err != nil { + panic(err) + } + if err := viper.BindPFlag("withdrawal-address", validatorCredentialsSetCmd.Flags().Lookup("withdrawal-address")); err != nil { + panic(err) + } + if err := viper.BindPFlag("json", validatorCredentialsSetCmd.Flags().Lookup("json")); err != nil { + panic(err) + } + if err := viper.BindPFlag("offline", validatorCredentialsSetCmd.Flags().Lookup("offline")); err != nil { + panic(err) + } + if err := viper.BindPFlag("fork-version", validatorCredentialsSetCmd.Flags().Lookup("fork-version")); err != nil { + panic(err) + } + if err := viper.BindPFlag("genesis-validators-root", validatorCredentialsSetCmd.Flags().Lookup("genesis-validators-root")); err != nil { + panic(err) + } +} diff --git a/cmd/validatorinfo.go b/cmd/validatorinfo.go index d89b8ad..81f937b 100644 --- a/cmd/validatorinfo.go +++ b/cmd/validatorinfo.go @@ -1,4 +1,4 @@ -// Copyright © 2020, 2021 Weald Technology Trading +// Copyright © 2020 - 2022 Weald Technology Trading // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -16,7 +16,6 @@ package cmd import ( "bytes" "context" - "encoding/hex" "encoding/json" "fmt" "io/ioutil" @@ -32,7 +31,6 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/wealdtech/ethdo/util" - e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2" string2eth "github.com/wealdtech/go-string2eth" ) @@ -41,7 +39,7 @@ var validatorInfoCmd = &cobra.Command{ Short: "Obtain information about a validator", Long: `Obtain information about validator. For example: - ethdo validator info --account=primary/validator + ethdo validator info --validator=primary/validator In quiet mode this will return 0 if the validator information can be obtained, otherwise 1.`, Run: func(cmd *cobra.Command, args []string) { @@ -54,32 +52,21 @@ In quiet mode this will return 0 if the validator information can be obtained, o ) errCheck(err, "Failed to connect to Ethereum 2 beacon node") - account, err := validatorInfoAccount(ctx, eth2Client) - errCheck(err, "Failed to obtain validator account") - - pubKeys := make([]spec.BLSPubKey, 1) - pubKey, err := util.BestPublicKey(account) - errCheck(err, "Failed to obtain validator public key") - copy(pubKeys[0][:], pubKey.Marshal()) - validators, err := eth2Client.(eth2client.ValidatorsProvider).ValidatorsByPubKey(ctx, "head", pubKeys) - errCheck(err, "Failed to obtain validator information") - if len(validators) == 0 { - fmt.Println("Validator not known by beacon node") - os.Exit(_exitSuccess) + if viper.GetString("validator") == "" { + fmt.Println("validator is required") + os.Exit(_exitFailure) } - var validator *api.Validator - for _, v := range validators { - validator = v - } + validator, err := util.ParseValidator(ctx, eth2Client.(eth2client.ValidatorsProvider), viper.GetString("validator"), "head") + errCheck(err, "Failed to obtain validator") if verbose { network, err := util.Network(ctx, eth2Client) errCheck(err, "Failed to obtain network") outputIf(debug, fmt.Sprintf("Network is %s", network)) - pubKey, err := util.BestPublicKey(account) + pubKey, err := validator.PubKey(ctx) if err == nil { - deposits, totalDeposited, err := graphData(network, pubKey.Marshal()) + deposits, totalDeposited, err := graphData(network, pubKey[:]) if err == nil && deposits > 0 { fmt.Printf("Number of deposits: %d\n", deposits) fmt.Printf("Total deposited: %s\n", string2eth.GWeiToString(uint64(totalDeposited), true)) @@ -122,54 +109,6 @@ In quiet mode this will return 0 if the validator information can be obtained, o }, } -// validatorInfoAccount obtains the account for the validator info command. -func validatorInfoAccount(ctx context.Context, eth2Client eth2client.Service) (e2wtypes.Account, error) { - var account e2wtypes.Account - var err error - switch { - case viper.GetString("account") != "": - ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout")) - defer cancel() - _, account, err = walletAndAccountFromPath(ctx, viper.GetString("account")) - if err != nil { - return nil, errors.Wrap(err, "failed to obtain account") - } - case viper.GetString("pubkey") != "": - pubKeyBytes, err := hex.DecodeString(strings.TrimPrefix(viper.GetString("pubkey"), "0x")) - if err != nil { - return nil, errors.Wrap(err, fmt.Sprintf("failed to decode public key %s", viper.GetString("pubkey"))) - } - account, err = util.NewScratchAccount(nil, pubKeyBytes) - if err != nil { - return nil, errors.Wrap(err, fmt.Sprintf("invalid public key %s", viper.GetString("pubkey"))) - } - case viper.GetInt64("index") != -1: - validatorsProvider, isValidatorsProvider := eth2Client.(eth2client.ValidatorsProvider) - if !isValidatorsProvider { - return nil, errors.New("client does not provide validator information") - } - index := spec.ValidatorIndex(viper.GetInt64("index")) - validators, err := validatorsProvider.Validators(ctx, "head", []spec.ValidatorIndex{ - index, - }) - if err != nil { - return nil, errors.Wrap(err, "failed to obtain validator information") - } - if len(validators) == 0 { - return nil, errors.New("unknown validator index") - } - pubKeyBytes := make([]byte, 48) - copy(pubKeyBytes, validators[index].Validator.PublicKey[:]) - account, err = util.NewScratchAccount(nil, pubKeyBytes) - if err != nil { - return nil, errors.Wrap(err, fmt.Sprintf("invalid public key %s", viper.GetString("pubkey"))) - } - default: - return nil, errors.New("neither account nor public key supplied") - } - return account, nil -} - // graphData returns data from the graph about number and amount of deposits func graphData(network string, validatorPubKey []byte) (uint64, spec.Gwei, error) { subgraph := "" @@ -224,16 +163,12 @@ func graphData(network string, validatorPubKey []byte) (uint64, spec.Gwei, error func init() { validatorCmd.AddCommand(validatorInfoCmd) - validatorInfoCmd.Flags().String("pubkey", "", "Public key for which to obtain status") - validatorInfoCmd.Flags().Int64("index", -1, "Index for which to obtain status") + validatorInfoCmd.Flags().String("validator", "", "Public key for which to obtain status") validatorFlags(validatorInfoCmd) } func validatorInfoBindings() { - if err := viper.BindPFlag("pubkey", validatorInfoCmd.Flags().Lookup("pubkey")); err != nil { - panic(err) - } - if err := viper.BindPFlag("index", validatorInfoCmd.Flags().Lookup("index")); err != nil { + if err := viper.BindPFlag("validator", validatorInfoCmd.Flags().Lookup("validator")); err != nil { panic(err) } } diff --git a/cmd/validatorkeycheck.go b/cmd/validatorkeycheck.go index fccd4a2..928998f 100644 --- a/cmd/validatorkeycheck.go +++ b/cmd/validatorkeycheck.go @@ -50,7 +50,6 @@ func init() { validatorCmd.AddCommand(validatorKeycheckCmd) validatorFlags(validatorKeycheckCmd) validatorKeycheckCmd.Flags().String("withdrawal-credentials", "", "Withdrawal credentials to check (can run offline)") - validatorKeycheckCmd.Flags().String("mnemonic", "", "Mnemonic from which to generate withdrawal credentials") validatorKeycheckCmd.Flags().String("privkey", "", "Private key from which to generate withdrawal credentials") } @@ -58,9 +57,6 @@ func validatorKeycheckBindings() { if err := viper.BindPFlag("withdrawal-credentials", validatorKeycheckCmd.Flags().Lookup("withdrawal-credentials")); err != nil { panic(err) } - if err := viper.BindPFlag("mnemonic", validatorKeycheckCmd.Flags().Lookup("mnemonic")); err != nil { - panic(err) - } if err := viper.BindPFlag("privkey", validatorKeycheckCmd.Flags().Lookup("privkey")); err != nil { panic(err) } diff --git a/cmd/walletcreate.go b/cmd/walletcreate.go index cd1082e..8ed0835 100644 --- a/cmd/walletcreate.go +++ b/cmd/walletcreate.go @@ -45,14 +45,10 @@ func init() { walletCmd.AddCommand(walletCreateCmd) walletFlags(walletCreateCmd) walletCreateCmd.Flags().String("type", "non-deterministic", "Type of wallet to create (non-deterministic or hierarchical deterministic)") - walletCreateCmd.Flags().String("mnemonic", "", "The 24-word mnemonic for a hierarchical deterministic wallet") } func walletCreateBindings() { if err := viper.BindPFlag("type", walletCreateCmd.Flags().Lookup("type")); err != nil { panic(err) } - if err := viper.BindPFlag("mnemonic", walletCreateCmd.Flags().Lookup("mnemonic")); err != nil { - panic(err) - } } diff --git a/docs/changingwithdrawalcredentials.md b/docs/changingwithdrawalcredentials.md new file mode 100644 index 0000000..0b0e869 --- /dev/null +++ b/docs/changingwithdrawalcredentials.md @@ -0,0 +1,218 @@ +# Changing withdrawal credentials +When creating a validator it is possible to set its withdrawal credentials to those based upon a BLS private key (known as BLS withdrawal credentials, or "type 0" withdrawal credentials) or based upon an Ethereum execution address (known as execution withdrawal credentials, or "type 1" withdrawal credentials). With the advent of the Capella hard fork, it is possible for rewards accrued on the consensus chain (also known as the beacon chain) to be sent to the execution chain. However, for this to occur the validator's withdrawal credentials must be type 1. Capella also brings a mechanism to change existing type 0 withdrawal credentials to type 1 withdrawal credentials, and this document outlines the process to change withdrawal credentials from type 0 to type 1 so that consensus rewards can be accessed. + +**Once a validator has Ethereum execution credentials set they cannot be changed. Please be careful when following this or any similar process to ensure that you have access to the private key (either as a software file, a hardware key or a mnemonic) of the withdrawal address you use so that you have the ability to access your rewards.** + +## Concepts +The following concepts are useful when understanding the rest of this guide. + +### Validator +A validator is a logical entity that secures the Ethereum beacon chain (and hence the execution chain) by proposing blocks and attesting to blocks proposed by other validators. + +### Withdrawal credentials +Withdrawal credentials, held as part of a validator's on-chain definition, define where consensus rewards will be sent. + +### Private key +A private key is a hexadecimal string (_e.g._ 0x010203…a1a2a3) that can be used to generate a public key and (in the case of the execution chain) Ethereum address. + +### Mnemonic +A mnemonic is a 24-word phrase that can be used to generate multiple private keys with the use of _paths_. + +### Path +A path is a string starting with "m" and containing a number of components separated by "/", for example "m/12381/3600/0/0". The process to obtain a key from a mnemonic and path is known as "hierarchical derivation". + +### Withdrawal address +A withdrawal address is an Ethereum execution address that will receive consensus rewards periodically during the operation of the validator and, ultimately, to which the initial deposit will be returned when the validator is exited. It is important to understand that at time of writing this value cannot be changed, so it is critical that one of the following criteria are met: + +- the private keys for the Ethereum address are known +- the Ethereum address is secured by a hardware wallet +- the Ethereum address is that of a smart contract with the ability to withdraw funds + +The execution address must be supplied in [EIP-55](https://eips.ethereum.org/EIPS/eip-55) format, _i.e._ using mixed case for checksum. An example of a mixed-case Ethereum address is `0x8f0844Fd51E31ff6Bf5baBe21DCcf7328E19Fd9F` + +### Online and Offline +An _online_ computer is one that is is connected to the internet. It should be running a consensus node connected to the larger Ethereum network. An online computer is required to carry out the process, to obtain information from the consensus node and to broadcast your actions to the rest of the Ethereum network. + +An _offline_ computer is one that is not connected to the internet. As such, it will not be running a consensus node. It can optionally be used in conjunction with an online computer to provide higher levels of security for your mnemonic or private key, but is less convenient because it requires manual transfer of files from the online computer to the offline computer, and back. + +With only an online computer the flow of information is roughly as follows: + +![Online process](images/credentials-change-online.png) + +Here it can be seen that a copy of `ethdo` with access to private keys connects to a consensus node with access to the internet. Due to its connection to the internet it is possible that the computer on which `ethdo` and the consensus node runs has been compromised, and as such would expose the private keys to an attacker. + +With both an offline and an online computer the flow of information is roughly as follows: + +![Offline process](images/credentials-change-offline.png) + +Here the copy of `ethdo` with access to private keys is on an offline computer, which protects it from being compromised via the internet. Data is physically moved from the offline to the online computer via a USB storage key or similar, and none of the information on the online computer is sensitive. + +## Preparation +Regardless of the method selected, preparation must take place on the online computer to ensure that `ethdo` can access your consensus node. `ethdo` will attempt to find a local consensus node automatically, but if not then an explicit connection value will be required. To find out if `ethdo` has access to the consensus node run: + +``` +ethdo node info --verbose +``` + +The result should be something similar to the following: + +``` +Version: teku/v22.9.1/linux-x86_64/-privatebuild-openjdk64bitservervm-java-14 +Syncing: false +``` + +It is important to confirm that the "Syncing" value is "false". If this is "true" it means that the node is currently syncing, and you will need to wait for the process to finish before proceeding. + +If this command instead returns an error you will need to add an explicit connection string. For example, if your consensus node is serving its REST API on port 12345 then you should add `--connection=http://localhost:12345` to all `ethdo` commands in this process, for example: + +```sh +ethdo --connection=http://localhost:12345 node info --verbose +``` + +Note that some consensus nodes may require configuration to serve their REST API. Please refer to the documentation of your specific consensus node to enable this. + +Once the preparation is complete you should select either basic or advanced operation, depending on your requirements. + +## Basic operation +Given the above concepts, the purpose of this guide is to allow a change of validators' withdrawal credentials to be changed to a withdrawal address, allowing validator rewards to be accessed on the Ethereum execution chain. + +Basic operation is suitable in the majority of cases. If you: + +- generated your validators using a mnemonic (_e.g._ using the deposit CLI or launchpad) +- want to change all of your validators to have the same withdrawal address +- want to change all of your validators' withdrawal credentials at the same time + +then this method is for you. If any of the above does not apply then please go to the "Advanced operation" section. + +### Online process +The online process generates and broadcasts the operations to change withdrawal credentials for all of your validators tied to a mnemonic in a single action. + +Two pieces of information are required for carrying out this process online: the mnemonic and withdrawal address. + +On your _online_ computer run the following: + +``` +ethdo validator credentials set --mnemonic="abandon abandon abandon … art" --withdrawal-address=0x0123…cdef +``` + +Replacing the `mnemonic` and `withdrawal-address` values with your own values. This command will: + +1. obtain information from your consensus node about all currently-running validators and various additional information required to generate the operations +2. scan your mnemonic to find any validators that were generated by it, and create the operations to change their credentials +3. broadcast the credentials change operations to the Ethereum network + +### Online and Offline process +The online and offline process contains three steps. In the first, data is gathered on the online computer. In the second, the credentials change operations are generated on the offline computer. In the third, the operations are broadcast on the online computer. + +Two pieces of information are required for carrying out this process online: the mnemonic and withdrawal address. + +On your _online_ computer run the following: + +``` +ethdo validator credentials set --prepare-offline +``` + +This command will: + +1. obtain information from your consensus node about all currently-running validators and various additional information required to generate the operations +2. write this information to a file called `offline-preparation.json` + +The `offline-preparation.json` file must be copied to your _offline_ computer. Once this has been done, on your _offline_ computer run the following: + +``` +ethdo validator credentials set --offline --mnemonic="abandon abandon abandon … art" --withdrawal-address=0x0123…cdef +``` + +Replacing the `mnemonic` and `withdrawal-address` values with your own values. This command will: + +1. read the `offline-preparation.json` file to obtain information about all currently-running validators and various additional information required to generate the operations +2. scan your mnemonic to find any validators that were generated by it, and create the operations to change their credentials +3. write this information to a file called `change-operations.json` + +The `change-operations.json` file must be copied to your _online_ computer. Once this has been done, on your _online_ computer run the following: + +``` +ethdo validator credentials set +``` + +This command will: + +1. read the `change-operations.json` file to obtain the operations to change the validators' credentials +2. broadcast the credentials change operations to the Ethereum network + +## Advanced operation +Advanced operation is required when any of the following conditions are met: + +- your validators were created using something other than the deposit CLI or launchpad (_e.g._ `ethdo`) +- you want to set your validators to have different withdrawal addresses +- you want to change your validators' withdrawal credentials individually + +### Validator reference +There are three options to reference a validator: + +- the `ethdo` account of the validator (in format wallet/account) +- the validator's public key (in format 0x…) +- the validator's on-chain index (in format 123…) + +Any of these can be passed to the following commands with the `--validator` parameter. You need to ensure that you have this information before starting the process. + +**In the following examples we will use the validator with index 123. Please replace this with the reference to your validator in all commands.** + +### Withdrawal address +The withdrawal address is defined above in the concepts section. + +**In the following examples we will use a withdrawal address of 0x8f…9F. Please replace this with the your withdrawal address in all commands.** + +### Generating credentials change operations +Note that if you are carrying out this process offline then you still need to carry out the first and third steps outlined in the "Basic operation" section above. This is to ensure that the offline computer has the correct information to generate the operations, and that the operations are made available to the online computer for broadcasting to the network. + +If using the online and offline process run the commands below on the offline computer, and add the `--offline` flag to the commands below. You will need to copy the resultant `change-operations.json` file to the online computer to broadcast to the network. + +If using the online process run the commands below on the online computer. The operation will be broadcast to the network automatically. + +#### Using a mnemonic and path. +A mnemonic is a 24-word phrase from which withdrawal and validator keys are derived using a _path_. Commonly, keys will have been generated using two paths: + +- m/12381/3600/_i_/0 is the path to a withdrawal key, where _i_ starts at 0 for the first validator, 1 for the second validator, _etc._ +- m/12381/3600/_i_/0/0 is the path to a validator key, where _i_ starts at 0 for the first validator, 1 for the second validator, _etc._ + +however this is only a standard and not a restriction, and it is possible for users to have created validators using paths of their own choice. + +``` +ethdo validator credentials set --validator=123 --mnemonic="abandon abandon abandon … art" --path='m/12381/3600/0/0/0' --withdrawal-address=0x0123…cdef +``` + +replacing the path with the path to your _withdrawal_ key, and all other parameters with your own values. + +#### Using a private key +If you have the private key from which the current withdrawal credentials were derived this can be used to generate and broadcast the credentials change operation with the following command: + +``` +ethdo validator credentials set --validator=123 --withdrawal-address=0x8f…9F --private-key=0x3b…9c +``` + +replacing the parameters with your own values. + +#### Using an account +If you used `ethdo` to generate your validator deposit data you will likely have used a separate account to generate the withdrawal credentials. You can specify the account to generate and broadcast the credentials change operation with the following command: + +``` +ethdo validator credentials set --validator=123 --withdrawal-address=0x8f…9F --account=Wallet/Account --passphrase=secret +``` + +replacing the parameters with your own values. + +## Confirming the process has succeeded +The final step is confirming the operation has taken place. To do so, run the following command on an online server: + +```sh +ethdo validator credentials get --validator=123 +``` + +The result should start with the phrase "Ethereum execution address" and display the execution address you chose at the beginning of the process, for example: + +``` +Ethereum execution address: 0x8f0844Fd51E31ff6Bf5baBe21DCcf7328E19Fd9F +``` + +If the result starts with the phrase "BLS credentials" then it may be that the operation has yet to be incorporated on the chain, please wait a few minutes and check again. If this continues to be the case please obtain help to understand why the change operation failed to work. diff --git a/docs/images/credentials-change-offline.png b/docs/images/credentials-change-offline.png new file mode 100644 index 0000000..831a167 Binary files /dev/null and b/docs/images/credentials-change-offline.png differ diff --git a/docs/images/credentials-change-online.png b/docs/images/credentials-change-online.png new file mode 100644 index 0000000..15c6f72 Binary files /dev/null and b/docs/images/credentials-change-online.png differ diff --git a/docs/images/diagrams.svg b/docs/images/diagrams.svg new file mode 100644 index 0000000..a7000f2 --- /dev/null +++ b/docs/images/diagrams.svg @@ -0,0 +1,923 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Localconsensus node + Consensusnetwork + Consensusnetwork + User + + + + + + + + + + + + + + + + ethdo(with keys) + Online + + + diff --git a/docs/usage.md b/docs/usage.md index 4b99380..0517d3a 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -581,12 +581,18 @@ Validator commands focus on interaction with Ethereum 2 validators. #### `credentials get` `ethdo validator credentials get` provides information about the withdrawal credentials for the provided validator. Options include: - - `account` the account for which to obtain the withdrawal credentials (in format "wallet/account") - - `pubkey` the public key of the validator for which to obtain the withdrawal credentials - - `index` the index of the validator for which to obtain the withdrawal credentials + - `validator` the account, public key or index for which to obtain the withdrawal credentials ```sh -$ ethdo validator credentials get --account=Validators/1 +$ ethdo validator credentials get --validator=Validators/1 +``` + +#### `credentials set` + +`ethdo validator credentials set` updates withdrawal credentials from BLS "type 0" credentials to execution "type 1" credentials. Full information about using this command can be found in the [specific documentation](./changingwithdrawalcredentials.md). + +```sh +$ ethdo validator credentials set --validator=Validators/1 --execution-address=0x8f…9F --private-key=0x3b…9c ``` #### `depositdata` @@ -622,7 +628,7 @@ $ ethdo validator exit --key=0x01e748d098d3bcb477d636f19d510399ae18205fadf9814ee `ethdo validator info` provides information for a given validator. ```sh -$ ethdo validator info --account=Validators/1 +$ ethdo validator info --validator=Validators/1 Status: Active Balance: 3.203823585 Ether Effective balance: 3.1 Ether @@ -641,7 +647,7 @@ Effective balance: 3.1 Ether Withdrawal credentials: 0x0033ef3cb10b36d0771ffe8a02bc5bfc7e64ea2f398ce77e25bb78989edbee36 ``` -If the validator is not an account it can be queried directly with `--pubkey`. +If the validator is not an account then `--validator` option can be supplied with a validator index or public key. ```sh $ ethdo validator info --pubkey=0x842dd66cfeaeff4397fc7c94f7350d2131ca0c4ad14ff727963be9a1edb4526604970df6010c3da6474a9820fa81642b diff --git a/go.mod b/go.mod index 4bebb8d..4d18003 100644 --- a/go.mod +++ b/go.mod @@ -3,16 +3,18 @@ module github.com/wealdtech/ethdo go 1.16 require ( - github.com/OneOfOne/xxhash v1.2.5 // indirect - github.com/attestantio/dirk v1.1.0 - github.com/attestantio/go-eth2-client v0.11.0 - github.com/aws/aws-sdk-go v1.42.44 // indirect - github.com/ferranbt/fastssz v0.0.0-20220103083642-bc5fefefa28b + github.com/attestantio/go-eth2-client v0.14.0 + github.com/aws/aws-sdk-go v1.44.111 // indirect + github.com/ferranbt/fastssz v0.1.2 github.com/gofrs/uuid v4.2.0+incompatible + github.com/google/go-cmp v0.5.9 // indirect github.com/google/uuid v1.3.0 github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b - github.com/herumi/bls-eth-go-binary v0.0.0-20220103074059-01b0ca9e9ef7 - github.com/jackc/puddle v1.2.1 // indirect + github.com/herumi/bls-eth-go-binary v1.28.1 + github.com/jackc/puddle v1.3.0 // indirect + github.com/klauspost/compress v1.15.11 // indirect + github.com/klauspost/cpuid/v2 v2.1.1 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect github.com/minio/highwayhash v1.0.2 // indirect github.com/mitchellh/go-homedir v1.1.0 github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 @@ -20,22 +22,22 @@ require ( github.com/protolambda/zssz v0.1.5 // indirect github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 github.com/prysmaticlabs/go-ssz v0.0.0-20210121151755-f6208871c388 - github.com/rs/zerolog v1.26.1 + github.com/rs/zerolog v1.28.0 github.com/shopspring/decimal v1.3.1 - github.com/spf13/afero v1.8.0 // indirect + github.com/spf13/afero v1.9.2 // indirect github.com/spf13/cobra v1.3.0 github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.10.1 - github.com/stretchr/testify v1.7.0 + github.com/spf13/viper v1.13.0 + github.com/stretchr/testify v1.8.0 github.com/tyler-smith/go-bip39 v1.1.0 github.com/wealdtech/go-bytesutil v1.1.1 github.com/wealdtech/go-ecodec v1.1.2 - github.com/wealdtech/go-eth2-types/v2 v2.6.0 + github.com/wealdtech/go-eth2-types/v2 v2.7.0 github.com/wealdtech/go-eth2-util v1.7.0 github.com/wealdtech/go-eth2-wallet v1.15.0 github.com/wealdtech/go-eth2-wallet-dirk v1.2.0 github.com/wealdtech/go-eth2-wallet-distributed v1.1.4 - github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.2.0 + github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.3.0 github.com/wealdtech/go-eth2-wallet-hd/v2 v2.6.0 github.com/wealdtech/go-eth2-wallet-nd/v2 v2.4.0 github.com/wealdtech/go-eth2-wallet-store-filesystem v1.17.0 @@ -43,9 +45,11 @@ require ( github.com/wealdtech/go-eth2-wallet-store-scratch v1.7.0 github.com/wealdtech/go-eth2-wallet-types/v2 v2.9.0 github.com/wealdtech/go-string2eth v1.2.0 - golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed // indirect + golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b // indirect + golang.org/x/net v0.0.0-20221004154528-8021a29435af // indirect + golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect + golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect golang.org/x/text v0.3.7 - google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350 // indirect - google.golang.org/grpc v1.44.0 - gopkg.in/ini.v1 v1.66.3 // indirect + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect + google.golang.org/genproto v0.0.0-20220930163606-c98284e70a91 // indirect ) diff --git a/go.sum b/go.sum index f93344d..5734d4e 100644 --- a/go.sum +++ b/go.sum @@ -29,35 +29,156 @@ cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= +cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= +cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= +cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= +cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= +cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= +cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= +cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= +cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= +cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= +cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= +cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= +cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= +cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= +cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= +cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= +cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= 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/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= +cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= +cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= +cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= +cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= +cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= +cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= +cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= +cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= +cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= +cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= +cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= +cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= +cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= +cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= +cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= +cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= +cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= +cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= 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/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= +cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= +cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= +cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= +cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= +cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= +cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= +cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= +cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= cloud.google.com/go/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU= cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= +cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= +cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= +cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= +cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= +cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= +cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= +cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= +cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= +cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= +cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= +cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= +cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= +cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= +cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= +cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= +cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= +cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= +cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= +cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= +cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= +cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= +cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= +cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= +cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= +cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= +cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= +cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= +cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= +cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= +cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= +cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= 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/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= +cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= +cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= +cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= +cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= +cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= +cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= +cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= +cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= +cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= +cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= +cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= +cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= cloud.google.com/go/secretmanager v1.0.0/go.mod h1:+Qkm5qxIJ5mk74xxIXA+87fseaY1JLYBcFPQoc/GQxg= +cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= +cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= +cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= +cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= +cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= +cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= +cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= +cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= +cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= 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= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= +cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= +cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= +cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= +cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= +cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= +cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= +cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= +cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= +cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= +cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= +cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= +cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/HdrHistogram/hdrhistogram-go v1.1.1/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/OneOfOne/xxhash v1.2.5 h1:zl/OfRA6nftbBK9qTohYBJ5xvw6C/oNKizR7cZGl3cI= -github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -75,16 +196,15 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI github.com/attestantio/dirk v1.1.0 h1:hwMTYZkwj/Y0um3OD0LQxg2xSl4/5xqVWV2MRePE4ec= github.com/attestantio/dirk v1.1.0/go.mod h1:2jkOw/XHjvIDdhDcmj+Z3kuVPpxMcQ6zxzzjSSv71PY= github.com/attestantio/go-eth2-client v0.8.1/go.mod h1:kEK9iAAOBoADO5wEkd84FEOzjT1zXgVWveQsqn+uBGg= -github.com/attestantio/go-eth2-client v0.11.0 h1:8/Jn5AAfd+4tOggLi+FvOv9/ORaObECv42ab7vK2FJc= -github.com/attestantio/go-eth2-client v0.11.0/go.mod h1:zXL/BxC0cBBhxj+tP7QG7t9Ufoa8GwQLdlbvZRd9+dM= +github.com/attestantio/go-eth2-client v0.14.0 h1:usWgI0KIfGWQ8G7XUsMM2bTe+yuJEh4d3HEO1vPD5Go= +github.com/attestantio/go-eth2-client v0.14.0/go.mod h1:bcg5gfjVcm+MtcaZfzv/uSWNHU4i8hGamVG+9JCZnC0= github.com/aws/aws-sdk-go v1.33.17/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.40.41/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go v1.41.19/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= -github.com/aws/aws-sdk-go v1.42.44 h1:vPlF4cUsdN5ETfvb7ewZFbFZyB6Rsfndt3kS2XqLXKo= -github.com/aws/aws-sdk-go v1.42.44/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= +github.com/aws/aws-sdk-go v1.44.111 h1:AcWfOgeedSQ4gQVwcIe6aLxpQNJMloZQyqnr7Dzki+s= +github.com/aws/aws-sdk-go v1.44.111/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -116,6 +236,7 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -143,6 +264,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.0/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -154,12 +276,16 @@ github.com/ferranbt/fastssz v0.0.0-20200728110133-0b6e349af87a/go.mod h1:DyEu2iu github.com/ferranbt/fastssz v0.0.0-20210526181520-7df50c8568f8/go.mod h1:DyEu2iuLBnb/T51BlsiO3yLYdJC6UbGMrIkqK1KmQxM= github.com/ferranbt/fastssz v0.0.0-20210905181407-59cf6761a7d5/go.mod h1:S8yiDeAXy8f88W4Ul+0dBMPx49S05byYbmZD6Uv94K4= github.com/ferranbt/fastssz v0.0.0-20211031100431-9823ca9021f1/go.mod h1:S8yiDeAXy8f88W4Ul+0dBMPx49S05byYbmZD6Uv94K4= -github.com/ferranbt/fastssz v0.0.0-20220103083642-bc5fefefa28b h1:Jea4sHxe4sTegJgpfhWvxSjFF2nyq4/R/qWm6AziPiI= -github.com/ferranbt/fastssz v0.0.0-20220103083642-bc5fefefa28b/go.mod h1:S8yiDeAXy8f88W4Ul+0dBMPx49S05byYbmZD6Uv94K4= +github.com/ferranbt/fastssz v0.1.1/go.mod h1:U2ZsxlYyvGeQGmadhz8PlEqwkBzDIhHwd3xuKrg2JIs= +github.com/ferranbt/fastssz v0.1.2 h1:Dky6dXlngF6Qjc+EfDipAkE83N5I5DE68bY6O0VLNPk= +github.com/ferranbt/fastssz v0.1.2/go.mod h1:X5UPrE2u1UJjxHA8X54u04SBwdAQjG2sFtWs39YxyWs= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 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= @@ -188,7 +314,6 @@ github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZg github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -241,8 +366,11 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 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= @@ -269,13 +397,21 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= 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/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= +github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b h1:wDUNC2eKiL35DbLvsDhiblTUXHxcOPwQSCzi7xpQUN4= @@ -290,6 +426,7 @@ github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= @@ -313,20 +450,21 @@ github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOn github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= +github.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/herumi/bls-eth-go-binary v0.0.0-20210520070601-31246bfa8ac4/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U= github.com/herumi/bls-eth-go-binary v0.0.0-20210902234237-7763804ee078/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U= github.com/herumi/bls-eth-go-binary v0.0.0-20210917013441-d37c07cfda4e/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U= github.com/herumi/bls-eth-go-binary v0.0.0-20211117070716-2bdbdadbf8bb/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U= -github.com/herumi/bls-eth-go-binary v0.0.0-20220103074059-01b0ca9e9ef7 h1:gMN4oOdFLVR7ye4SCacHD4SIB64NPUNE+V9w8o7bR6A= -github.com/herumi/bls-eth-go-binary v0.0.0-20220103074059-01b0ca9e9ef7/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U= +github.com/herumi/bls-eth-go-binary v1.28.1 h1:fcIZ48y5EE9973k05XjE8+P3YiQgjZz4JI/YabAm8KA= +github.com/herumi/bls-eth-go-binary v1.28.1/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= 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/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jackc/puddle v1.1.4/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.2.1 h1:gI8os0wpRXFd4FiAY2dWiqRK037tjj3t7rKFeO4X5iw= -github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0= +github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -347,20 +485,23 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c= +github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.11 h1:i2lw1Pm7Yi/4O6XCSyJWqEHI2MDw2FzUK6o/D21xn2A= -github.com/klauspost/cpuid/v2 v2.0.11/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdydm0= +github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -369,24 +510,26 @@ github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= +github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= @@ -405,8 +548,9 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -424,8 +568,11 @@ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYr github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= +github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -441,25 +588,22 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/protolambda/zssz v0.1.5 h1:7fjJjissZIIaa2QcvmhS/pZISMX21zVITt49sW1ouek= github.com/protolambda/zssz v0.1.5/go.mod h1:a4iwOX5FE7/JkKA+J/PH0Mjo9oXftN6P8NZyL28gpag= @@ -470,24 +614,30 @@ github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 h1:0tVE4 github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7/go.mod h1:wmuf/mdK4VMD+jA9ThwcUKjg3a2XWM9cVfFYjDyY4j4= github.com/prysmaticlabs/go-ssz v0.0.0-20210121151755-f6208871c388 h1:4bD+ujqGfY4zoDUF3q9MhdmpPXzdp03DYUIlXeQ72kk= github.com/prysmaticlabs/go-ssz v0.0.0-20210121151755-f6208871c388/go.mod h1:VecIJZrewdAuhVckySLFt2wAAHRME934bSDurP8ftkc= +github.com/prysmaticlabs/gohashtree v0.0.1-alpha.0.20220714111606-acbb2962fb48 h1:cSo6/vk8YpvkLbk9v3FO97cakNmUoxwi2KMP8hd5WIw= +github.com/prysmaticlabs/gohashtree v0.0.1-alpha.0.20220714111606-acbb2962fb48/go.mod h1:4pWaT30XoEx1j8KNJf3TV+E3mQkaufn7mf+jRNb/Fuk= github.com/r3labs/sse/v2 v2.3.0/go.mod h1:hUrYMKfu9WquG9MyI0r6TKiNH+6Sw/QPKm2YbNbU5g8= github.com/r3labs/sse/v2 v2.7.4 h1:pvCMswPDlXd/ZUFx1dry0LbXJNHXwWPulLcUGYwClc0= github.com/r3labs/sse/v2 v2.7.4/go.mod h1:hUrYMKfu9WquG9MyI0r6TKiNH+6Sw/QPKm2YbNbU5g8= 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 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.19.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM= github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo= -github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc= github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc= +github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY= +github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= -github.com/sagikazarmark/crypt v0.4.0/go.mod h1:ALv2SRj7GxYV4HO9elxH9nS6M9gW+xDNxqmyJ6RfDFM= +github.com/sagikazarmark/crypt v0.6.0/go.mod h1:U8+INwJo3nBv1m6A/8OBXAq7Jnpspk5AxSgDyEQcea8= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 h1:Xuk8ma/ibJ1fOy4Ee11vHhUFHQNpHhrBneOCNHVXS5w= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0/go.mod h1:7AwjWCpdPhkSmNAgUv5C7EJ4AbmjEB3r047r3DXWu3Y= @@ -495,19 +645,19 @@ github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5g github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.8.0 h1:5MmtuhAgYeU6qpa7w7bP0dv6MBYuup0vekhSpSkoq60= -github.com/spf13/afero v1.8.0/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= +github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= +github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= +github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= +github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0= github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= @@ -520,21 +670,26 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= -github.com/spf13/viper v1.10.1 h1:nuJZuYpG7gTj/XqiUwg8bA0cp1+M2mC3J4g5luUYBKk= -github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU= +github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU= +github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= +github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= @@ -551,8 +706,9 @@ github.com/wealdtech/go-ecodec v1.1.2 h1:CcNBnFV6b8IoDtu8wj6cfrmuBRBw00VYqsvpT4+ github.com/wealdtech/go-ecodec v1.1.2/go.mod h1:0Rgn8mEr0GX4fDyuU3aUL+xp4n17Wviscasp6rWCh10= github.com/wealdtech/go-eth2-types/v2 v2.5.5/go.mod h1:QY9t7nwxc26C9/iZHh1wzc+hZbGGn7ipgjMYPhKnN/0= github.com/wealdtech/go-eth2-types/v2 v2.5.6/go.mod h1:SX7+QxnhXGxeW8tESzJ/BSIXvoflBHUi21chqRsZ9wE= -github.com/wealdtech/go-eth2-types/v2 v2.6.0 h1:djgMv1A40bstgkg6L1ZA7eowR/Gbmj1ZWnBdrK39lhY= github.com/wealdtech/go-eth2-types/v2 v2.6.0/go.mod h1:psOez/ZRBzZSDl5hiNDwRf5ZqQujNE6h5FxAz09Koxg= +github.com/wealdtech/go-eth2-types/v2 v2.7.0 h1:TV2jrNkane2nxTiVhyG3npVtg825WvRdCZZ4j+jTENA= +github.com/wealdtech/go-eth2-types/v2 v2.7.0/go.mod h1:aQ5dk+KNPE/A2r3lLhKpW+f5B24aJO68tRz6wO4Zo/g= github.com/wealdtech/go-eth2-util v1.7.0 h1:bgCnZ4H5K5VE+CiAwFTKhrICItq5rI+5VYaODFWC6is= github.com/wealdtech/go-eth2-util v1.7.0/go.mod h1:L0q3G7S1Dm1WJPye5m2wjuNweXC98o1CmLTKPmBTlpg= github.com/wealdtech/go-eth2-wallet v1.15.0 h1:yff2rWCvHyr5bOU41wwilsNemFRYOFinZoIPjhzG10M= @@ -562,8 +718,9 @@ github.com/wealdtech/go-eth2-wallet-dirk v1.2.0/go.mod h1:1lnHDSdcRxj9CFJh+jPtdg github.com/wealdtech/go-eth2-wallet-distributed v1.1.4 h1:EBk/FofiQWtbRYmwdCp0qM9TPDvlh1LN2yjlLr4W3ao= github.com/wealdtech/go-eth2-wallet-distributed v1.1.4/go.mod h1:Y31pDxcdyADwfQl65t8V6KhcmCes31EXkXZPDcT2SIk= github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.1.6/go.mod h1:hIeHsLJWVxFUUqQqaBm9nmUW3Y/eHneQclYyNayUeQg= -github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.2.0 h1:HgIK30YsXxgR6Ra6pvxW1KFUPbx7BpIIK1VGhOlmCm4= github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.2.0/go.mod h1:qqIU42c9sXcNYsiEjUQoOOWYZfZDL1zmyLtz3t+wN2s= +github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.3.0 h1:3Kx2QvKU/4PP0d7+e3+q9G+QiXQprNQPeAXsWOcfKRI= +github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.3.0/go.mod h1:qqIU42c9sXcNYsiEjUQoOOWYZfZDL1zmyLtz3t+wN2s= github.com/wealdtech/go-eth2-wallet-encryptor-unencrypted v1.0.1 h1:x6bq8cVgRgfhwtSQSYo/9AqJ8qEeaS6af28cW0cVj5U= github.com/wealdtech/go-eth2-wallet-encryptor-unencrypted v1.0.1/go.mod h1:49K88T/4LNQpB8ghVcjTKeRRi/bZHeYjN8Ef5S23yps= github.com/wealdtech/go-eth2-wallet-hd/v2 v2.6.0 h1:Oy5TsD6HqZzb3MwGOe+5WVCj4pCKKUYYc2OKyN42QCM= @@ -596,10 +753,14 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= +go.etcd.io/etcd/client/v2 v2.305.4/go.mod h1:Ud+VUwIi9/uQHOMA+4ekToJ12lTxlv0zB/+DHwTGEbU= +go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= 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= @@ -631,8 +792,9 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed h1:YoWVYYAfvQ4ddHv3OKmIvX7NCAhFGTj62VP2l2kfBbA= -golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b h1:huxqepDufQpLLIRXiVkTvnxrzJlpwmIWAObmcCcUFr0= +golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -725,9 +887,18 @@ golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211105192438-b53810dc28af/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211116231205-47ca1ff31462/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221004154528-8021a29435af h1:wv66FM3rLZGPdxpYL+ApnDe2HzHcTFta3z5nsc13wI4= +golang.org/x/net v0.0.0-20221004154528-8021a29435af/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/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= @@ -745,6 +916,13 @@ golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -755,8 +933,11 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ 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-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -841,8 +1022,23 @@ golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI= +golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -921,8 +1117,12 @@ golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= @@ -961,6 +1161,22 @@ google.golang.org/api v0.60.0/go.mod h1:d7rl65NZAkEQ90JFzqBjcRq1TVeG5ZoGV3sSpEnn google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.81.0/go.mod h1:FA6Mb/bZxj706H2j+j2d6mHEEaHBmbbWnkfvmorOCko= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= +google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= +google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= +google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 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= @@ -1013,6 +1229,7 @@ google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210406143921-e86de6bf7a46/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= @@ -1043,8 +1260,46 @@ google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350 h1:YxHp5zqIcAShDEvRr5/0rVESVS+njYF68PSdazrNLJo= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= +google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= +google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20220930163606-c98284e70a91 h1:Ezh2cpcnP5Rq60sLensUsFnxh7P6513NLvNtCm9iyJ4= +google.golang.org/genproto v0.0.0-20220930163606-c98284e70a91/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= 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= @@ -1074,9 +1329,14 @@ google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.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= @@ -1090,8 +1350,10 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj 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= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/cenkalti/backoff.v1 v1.1.0 h1:Arh75ttbsvlpVA7WtVpH4u9h6Zl46xuptxqLxPiSo4Y= gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UDAkHu8BrjI= @@ -1103,8 +1365,8 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.3 h1:jRskFVxYaMGAMUbN0UZ7niA9gzL9B49DOqE78vg0k3w= -gopkg.in/ini.v1 v1.66.3/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/jcmturner/gokrb5.v7 v7.5.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= 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= @@ -1116,8 +1378,9 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1131,3 +1394,4 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/services/chaintime/service.go b/services/chaintime/service.go index 911e3ac..6934f0d 100644 --- a/services/chaintime/service.go +++ b/services/chaintime/service.go @@ -54,4 +54,6 @@ type Service interface { AltairInitialEpoch() phase0.Epoch // AltairInitialSyncCommitteePeriod provides the sync committee period in which the Altair hard fork takes place. AltairInitialSyncCommitteePeriod() uint64 + // CapellaInitialEpoch provides the epoch at which the Capella hard fork takes place. + CapellaInitialEpoch() phase0.Epoch } diff --git a/services/chaintime/standard/service.go b/services/chaintime/standard/service.go index 40d4a62..2ab73a0 100644 --- a/services/chaintime/standard/service.go +++ b/services/chaintime/standard/service.go @@ -33,6 +33,7 @@ type Service struct { epochsPerSyncCommitteePeriod uint64 altairForkEpoch phase0.Epoch bellatrixForkEpoch phase0.Epoch + capellaForkEpoch phase0.Epoch } // module-wide log. @@ -100,6 +101,13 @@ func New(ctx context.Context, params ...Parameter) (*Service, error) { } log.Trace().Uint64("epoch", uint64(bellatrixForkEpoch)).Msg("Obtained Bellatrix fork epoch") + capellaForkEpoch, err := fetchCapellaForkEpoch(ctx, parameters.forkScheduleProvider) + if err != nil { + // Set to far future epoch. + capellaForkEpoch = 0xffffffffffffffff + } + log.Trace().Uint64("epoch", uint64(capellaForkEpoch)).Msg("Obtained Capella fork epoch") + s := &Service{ genesisTime: genesisTime, slotDuration: slotDuration, @@ -107,6 +115,7 @@ func New(ctx context.Context, params ...Parameter) (*Service, error) { epochsPerSyncCommitteePeriod: epochsPerSyncCommitteePeriod, altairForkEpoch: altairForkEpoch, bellatrixForkEpoch: bellatrixForkEpoch, + capellaForkEpoch: capellaForkEpoch, } return s, nil @@ -247,3 +256,28 @@ func fetchBellatrixForkEpoch(ctx context.Context, provider eth2client.ForkSchedu } return 0, errors.New("no bellatrix fork obtained") } + +// CapellaInitialEpoch provides the epoch at which the Capella hard fork takes place. +func (s *Service) CapellaInitialEpoch() phase0.Epoch { + return s.capellaForkEpoch +} + +func fetchCapellaForkEpoch(ctx context.Context, provider eth2client.ForkScheduleProvider) (phase0.Epoch, error) { + forkSchedule, err := provider.ForkSchedule(ctx) + if err != nil { + return 0, err + } + count := 0 + for i := range forkSchedule { + count++ + if bytes.Equal(forkSchedule[i].CurrentVersion[:], forkSchedule[i].PreviousVersion[:]) { + // This is the genesis fork; ignore it. + continue + } + if count == 2 { + return forkSchedule[i].Epoch, nil + } + count++ + } + return 0, errors.New("no capella fork obtained") +} diff --git a/signing/container_encoding.go b/signing/container_encoding.go index 426b98f..a77f7cb 100644 --- a/signing/container_encoding.go +++ b/signing/container_encoding.go @@ -1,4 +1,6 @@ // Code generated by fastssz. DO NOT EDIT. +// Hash: c953c4b72fdc3f250f4227b181e286fe53fd663c5aa4f5cc3de6cc9998c7fdb7 +// Version: 0.1.2 package signing import ( @@ -15,15 +17,15 @@ func (c *Container) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf // Field (0) 'Root' - if len(c.Root) != 32 { - err = ssz.ErrBytesLength + if size := len(c.Root); size != 32 { + err = ssz.ErrBytesLengthFn("Container.Root", size, 32) return } dst = append(dst, c.Root...) // Field (1) 'Domain' - if len(c.Domain) != 32 { - err = ssz.ErrBytesLength + if size := len(c.Domain); size != 32 { + err = ssz.ErrBytesLengthFn("Container.Domain", size, 32) return } dst = append(dst, c.Domain...) @@ -66,19 +68,19 @@ func (c *Container) HashTreeRoot() ([32]byte, error) { } // HashTreeRootWith ssz hashes the Container object with a hasher -func (c *Container) HashTreeRootWith(hh *ssz.Hasher) (err error) { +func (c *Container) HashTreeRootWith(hh ssz.HashWalker) (err error) { indx := hh.Index() // Field (0) 'Root' - if len(c.Root) != 32 { - err = ssz.ErrBytesLength + if size := len(c.Root); size != 32 { + err = ssz.ErrBytesLengthFn("Container.Root", size, 32) return } hh.PutBytes(c.Root) // Field (1) 'Domain' - if len(c.Domain) != 32 { - err = ssz.ErrBytesLength + if size := len(c.Domain); size != 32 { + err = ssz.ErrBytesLengthFn("Container.Domain", size, 32) return } hh.PutBytes(c.Domain) @@ -86,3 +88,8 @@ func (c *Container) HashTreeRootWith(hh *ssz.Hasher) (err error) { hh.Merkleize(indx) return } + +// GetTree ssz hashes the Container object +func (c *Container) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(c) +} diff --git a/util/account.go b/util/account.go index 40432e1..07a7c48 100644 --- a/util/account.go +++ b/util/account.go @@ -15,11 +15,126 @@ package util import ( "context" + "encoding/hex" + "fmt" + "strings" "github.com/pkg/errors" + util "github.com/wealdtech/go-eth2-util" e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2" ) +// ParseAccount parses input to obtain an account. +func ParseAccount(ctx context.Context, + accountStr string, + supplementary []string, + unlock bool, +) ( + e2wtypes.Account, + error, +) { + if accountStr == "" { + return nil, errors.New("no account specified") + } + + var account e2wtypes.Account + var err error + + switch { + case strings.HasPrefix(accountStr, "0x"): + // A key. Could be public or private. + data, err := hex.DecodeString(strings.TrimPrefix(accountStr, "0x")) + if err != nil { + return nil, errors.Wrap(err, "failed to parse account key") + } + switch len(data) { + case 48: + // Public key. + account, err = newScratchAccountFromPubKey(data) + if err != nil { + return nil, errors.Wrap(err, "failed to create account from public key") + } + if unlock { + return nil, errors.New("cannot unlock an account specified by its public key") + } + case 32: + // Private key. + account, err = newScratchAccountFromPrivKey(data) + if err != nil { + return nil, errors.Wrap(err, "failed to create account from public key") + } + if unlock { + _, err = UnlockAccount(ctx, account, nil) + if err != nil { + return nil, errors.Wrap(err, "failed to unlock account") + } + } + default: + return nil, fmt.Errorf("key of length %d neither public nor private key", len(data)) + } + case strings.Contains(accountStr, "/"): + // An account. + _, account, err = WalletAndAccountFromPath(ctx, accountStr) + if err != nil { + return nil, errors.Wrap(err, "unable to obtain account") + } + if unlock { + // Supplementary will be the unlock passphrase(s). + _, err = UnlockAccount(ctx, account, supplementary) + if err != nil { + return nil, errors.Wrap(err, "failed to unlock account") + } + } + case strings.Contains(accountStr, " "): + // A mnemonic. + // Supplementary will be the path. + if len(supplementary) == 0 { + return nil, errors.New("missing derivation path") + } + account, err = accountFromMnemonicAndPath(accountStr, supplementary[0]) + if err != nil { + return nil, err + } + if unlock { + err = account.(e2wtypes.AccountLocker).Unlock(ctx, nil) + if err != nil { + return nil, errors.Wrap(err, "failed to unlock account") + } + } + default: + return nil, fmt.Errorf("unknown account specifier %s", accountStr) + } + + return account, nil +} + +func accountFromMnemonicAndPath(mnemonic string, path string) (e2wtypes.Account, error) { + seed, err := SeedFromMnemonic(mnemonic) + if err != nil { + return nil, err + } + + // Ensure the path is valid. + match := hdPathRegex.Match([]byte(path)) + if !match { + return nil, errors.New("path does not match expected format m/…") + } + + // Derive private key from seed and path. + key, err := util.PrivateKeyFromSeedAndPath(seed, path) + if err != nil { + return nil, errors.Wrap(err, "failed to generate key") + } + + // Create a scratch account given the private key. + account, err := newScratchAccountFromPrivKey(key.Marshal()) + if err != nil { + return nil, errors.Wrap(err, "failed to generate scratch account") + } + + return account, nil +} + // UnlockAccount attempts to unlock an account. It returns true if the account was already unlocked. func UnlockAccount(ctx context.Context, account e2wtypes.Account, passphrases []string) (bool, error) { locker, isAccountLocker := account.(e2wtypes.AccountLocker) @@ -46,6 +161,13 @@ func UnlockAccount(ctx context.Context, account e2wtypes.Account, passphrases [] } } + // Also attempt to unlock without any passphrase. + err = locker.Unlock(ctx, nil) + if err == nil { + // Unlocked. + return false, nil + } + // Failed to unlock it. return false, errors.New("failed to unlock account") } diff --git a/util/account_test.go b/util/account_test.go new file mode 100644 index 0000000..bb3c721 --- /dev/null +++ b/util/account_test.go @@ -0,0 +1,103 @@ +// Copyright © 2020 Weald Technology Trading +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package util_test + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + "github.com/wealdtech/ethdo/util" + e2types "github.com/wealdtech/go-eth2-types/v2" + e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2" +) + +func TestParseAccount(t *testing.T) { + ctx := context.Background() + require.NoError(t, e2types.InitBLS()) + + tests := []struct { + name string + accountStr string + supplementary []string + unlock bool + err string + expectedPubkey string + expectedUnlocked bool + }{ + { + name: "Zero", + err: "no account specified", + }, + { + name: "Bad", + accountStr: "bad", + err: "unknown account specifier bad", + }, + { + name: "PublicKey", + accountStr: "0x99b1f1d84d76185466d86c34bde1101316afddae76217aa86cd066979b19858c2c9d9e56eebc1e067ac54277a61790db", + expectedPubkey: "0x99b1f1d84d76185466d86c34bde1101316afddae76217aa86cd066979b19858c2c9d9e56eebc1e067ac54277a61790db", + }, + { + name: "PublicKeyUnlocked", + accountStr: "0x99b1f1d84d76185466d86c34bde1101316afddae76217aa86cd066979b19858c2c9d9e56eebc1e067ac54277a61790db", + unlock: true, + err: "cannot unlock an account specified by its public key", + }, + { + name: "PrivateKey", + accountStr: "0x068dce0c90cb428ab37a74af0191eac49648035f1aaef077734b91e05985ec55", + expectedPubkey: "0x99b1f1d84d76185466d86c34bde1101316afddae76217aa86cd066979b19858c2c9d9e56eebc1e067ac54277a61790db", + }, + { + name: "PrivateKeyUnlocked", + accountStr: "0x068dce0c90cb428ab37a74af0191eac49648035f1aaef077734b91e05985ec55", + expectedPubkey: "0x99b1f1d84d76185466d86c34bde1101316afddae76217aa86cd066979b19858c2c9d9e56eebc1e067ac54277a61790db", + unlock: true, + expectedUnlocked: true, + }, + { + name: "Mnemonic", + accountStr: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art", + supplementary: []string{"m/12381/3600/0/0"}, + expectedPubkey: "0x99b1f1d84d76185466d86c34bde1101316afddae76217aa86cd066979b19858c2c9d9e56eebc1e067ac54277a61790db", + }, + { + name: "MnemonicUnlocked", + accountStr: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art", + supplementary: []string{"m/12381/3600/0/0"}, + expectedPubkey: "0x99b1f1d84d76185466d86c34bde1101316afddae76217aa86cd066979b19858c2c9d9e56eebc1e067ac54277a61790db", + unlock: true, + expectedUnlocked: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + account, err := util.ParseAccount(ctx, test.accountStr, test.supplementary, test.unlock) + if test.err == "" { + require.NoError(t, err) + require.NotNil(t, account) + unlocked, err := account.(e2wtypes.AccountLocker).IsUnlocked(ctx) + require.NoError(t, err) + require.Equal(t, test.expectedPubkey, fmt.Sprintf("%#x", account.PublicKey().Marshal())) + require.Equal(t, test.expectedUnlocked, unlocked) + } else { + require.EqualError(t, err, test.err) + } + }) + } +} diff --git a/util/mnemonic.go b/util/mnemonic.go new file mode 100644 index 0000000..2d8e98a --- /dev/null +++ b/util/mnemonic.go @@ -0,0 +1,47 @@ +// Copyright © 2020, 2022 Weald Technology Trading +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package util + +import ( + "regexp" + "strings" + + "github.com/pkg/errors" + "github.com/tyler-smith/go-bip39" + "golang.org/x/text/unicode/norm" +) + +// hdPathRegex is the regular expression that matches an HD path. +var hdPathRegex = regexp.MustCompile("^m/[0-9]+/[0-9]+(/[0-9+])+") + +// SeedFromMnemonic creates a seed from a mnemonic. +func SeedFromMnemonic(mnemonic string) ([]byte, error) { + // If there are more than 24 words we treat the additional characters as the passphrase. + mnemonicParts := strings.Split(mnemonic, " ") + mnemonicPassphrase := "" + if len(mnemonicParts) > 24 { + mnemonic = strings.Join(mnemonicParts[:24], " ") + mnemonicPassphrase = strings.Join(mnemonicParts[24:], " ") + } + // Normalise the input. + mnemonic = string(norm.NFKD.Bytes([]byte(mnemonic))) + mnemonicPassphrase = string(norm.NFKD.Bytes([]byte(mnemonicPassphrase))) + + if !bip39.IsMnemonicValid(mnemonic) { + return nil, errors.New("mnemonic is invalid") + } + + // Create seed from mnemonic and passphrase. + return bip39.NewSeed(mnemonic, mnemonicPassphrase), nil +} diff --git a/util/scratchaccount.go b/util/scratchaccount.go index 224f3f6..e7e0158 100644 --- a/util/scratchaccount.go +++ b/util/scratchaccount.go @@ -75,6 +75,14 @@ func (a *ScratchAccount) PublicKey() e2types.PublicKey { return a.pubKey } +// PrivateKey returns the account private key. +func (a *ScratchAccount) PrivateKey(ctx context.Context) (e2types.PrivateKey, error) { + if a.privKey == nil { + return nil, errors.New("no private key available") + } + return a.privKey, nil +} + // Path returns the account path. func (a *ScratchAccount) Path() string { return ""