mirror of
https://github.com/wealdtech/ethdo.git
synced 2026-01-10 14:37:57 -05:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b76cdb01d1 | ||
|
|
ce5b250ef0 | ||
|
|
2c4ccf62af | ||
|
|
c7ad5194e6 | ||
|
|
ddb866131b | ||
|
|
49fb03aa3a | ||
|
|
1ed3a51117 | ||
|
|
4d5660ccbb |
11
CHANGELOG.md
11
CHANGELOG.md
@@ -1,3 +1,14 @@
|
||||
1.9.1
|
||||
- Avoid crash when required interfaces for chain status command are not supported
|
||||
- Avoid crash with latest version of herumi/go-bls
|
||||
|
||||
|
||||
1.9.0
|
||||
- allow use of Ethereum 1 address as withdrawal credentials
|
||||
|
||||
1.8.1
|
||||
- fix issue where 'attester duties' and 'attester inclusion' could crash
|
||||
|
||||
1.8.0
|
||||
- add "chain time"
|
||||
- add "validator keycheck"
|
||||
|
||||
@@ -66,7 +66,10 @@ func process(ctx context.Context, data *dataIn) (*dataOut, error) {
|
||||
if len(validators) == 0 {
|
||||
return nil, errors.New("validator is not known")
|
||||
}
|
||||
validator := validators[0]
|
||||
var validator *api.Validator
|
||||
for _, v := range validators {
|
||||
validator = v
|
||||
}
|
||||
|
||||
results := &dataOut{
|
||||
debug: data.debug,
|
||||
|
||||
@@ -66,7 +66,10 @@ func process(ctx context.Context, data *dataIn) (*dataOut, error) {
|
||||
if len(validators) == 0 {
|
||||
return nil, errors.New("validator is not known")
|
||||
}
|
||||
validator := validators[0]
|
||||
var validator *api.Validator
|
||||
for _, v := range validators {
|
||||
validator = v
|
||||
}
|
||||
|
||||
results := &dataOut{
|
||||
debug: data.debug,
|
||||
|
||||
@@ -40,13 +40,19 @@ In quiet mode this will return 0 if the chain status can be obtained, otherwise
|
||||
eth2Client, err := util.ConnectToBeaconNode(ctx, viper.GetString("connection"), viper.GetDuration("timeout"), viper.GetBool("allow-insecure-connections"))
|
||||
errCheck(err, "Failed to connect to Ethereum 2 beacon node")
|
||||
|
||||
config, err := eth2Client.(eth2client.SpecProvider).Spec(ctx)
|
||||
specProvider, isProvider := eth2Client.(eth2client.SpecProvider)
|
||||
assert(isProvider, "beacon node does not provide spec; cannot report on chain status")
|
||||
config, err := specProvider.Spec(ctx)
|
||||
errCheck(err, "Failed to obtain beacon chain specification")
|
||||
|
||||
finality, err := eth2Client.(eth2client.FinalityProvider).Finality(ctx, "head")
|
||||
finalityProvider, isProvider := eth2Client.(eth2client.FinalityProvider)
|
||||
assert(isProvider, "beacon node does not provide finality; cannot report on chain status")
|
||||
finality, err := finalityProvider.Finality(ctx, "head")
|
||||
errCheck(err, "Failed to obtain finality information")
|
||||
|
||||
genesis, err := eth2Client.(eth2client.GenesisProvider).Genesis(ctx)
|
||||
genesisProvider, isProvider := eth2Client.(eth2client.GenesisProvider)
|
||||
assert(isProvider, "beacon node does not provide genesis; cannot report on chain status")
|
||||
genesis, err := genesisProvider.Genesis(ctx)
|
||||
errCheck(err, "Failed to obtain genesis information")
|
||||
|
||||
slotDuration := config["SECONDS_PER_SLOT"].(time.Duration)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2019, 2020 Weald Technology Trading
|
||||
// Copyright © 2019-2021 Weald Technology Limited.
|
||||
// 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
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
|
||||
var depositVerifyData string
|
||||
var depositVerifyWithdrawalPubKey string
|
||||
var depositVerifyWithdrawalAddress string
|
||||
var depositVerifyValidatorPubKey string
|
||||
var depositVerifyDepositAmount string
|
||||
var depositVerifyForkVersion string
|
||||
@@ -81,7 +82,14 @@ In quiet mode this will return 0 if the the data is verified correctly, otherwis
|
||||
withdrawalPubKey, err := e2types.BLSPublicKeyFromBytes(withdrawalPubKeyBytes)
|
||||
errCheck(err, "Value supplied with --withdrawalpubkey is not a valid public key")
|
||||
withdrawalCredentials = eth2util.SHA256(withdrawalPubKey.Marshal())
|
||||
withdrawalCredentials[0] = 0 // BLS_WITHDRAWAL_PREFIX
|
||||
withdrawalCredentials[0] = 0x00 // BLS_WITHDRAWAL_PREFIX
|
||||
} else if depositVerifyWithdrawalAddress != "" {
|
||||
withdrawalAddressBytes, err := hex.DecodeString(strings.TrimPrefix(depositVerifyWithdrawalAddress, "0x"))
|
||||
errCheck(err, "Invalid withdrawal address")
|
||||
assert(len(withdrawalAddressBytes) == 20, "address should be 20 bytes")
|
||||
withdrawalCredentials = make([]byte, 32)
|
||||
withdrawalCredentials[0] = 0x01 // ETH1_ADDRESS_WITHDRAWAL_PREFIX
|
||||
copy(withdrawalCredentials[12:], withdrawalAddressBytes)
|
||||
}
|
||||
outputIf(debug, fmt.Sprintf("Withdrawal credentials are %#x", withdrawalCredentials))
|
||||
|
||||
@@ -181,10 +189,10 @@ func validatorPubKeysFromInput(input string) (map[[48]byte]bool, error) {
|
||||
|
||||
func verifyDeposit(deposit *util.DepositInfo, withdrawalCredentials []byte, validatorPubKeys map[[48]byte]bool, amount uint64) (bool, error) {
|
||||
if withdrawalCredentials == nil {
|
||||
outputIf(!quiet, "Withdrawal public key not supplied; withdrawal credentials NOT checked")
|
||||
outputIf(!quiet, "Withdrawal public key or address not supplied; withdrawal credentials NOT checked")
|
||||
} else {
|
||||
if !bytes.Equal(deposit.WithdrawalCredentials, withdrawalCredentials) {
|
||||
outputIf(!quiet, "Withdrawal public key incorrect")
|
||||
outputIf(!quiet, "Withdrawal credentials incorrect")
|
||||
return false, nil
|
||||
}
|
||||
outputIf(!quiet, "Withdrawal credentials verified")
|
||||
@@ -263,6 +271,7 @@ func init() {
|
||||
depositFlags(depositVerifyCmd)
|
||||
depositVerifyCmd.Flags().StringVar(&depositVerifyData, "data", "", "JSON data, or path to JSON data")
|
||||
depositVerifyCmd.Flags().StringVar(&depositVerifyWithdrawalPubKey, "withdrawalpubkey", "", "Public key of the account to which the validator funds will be withdrawn")
|
||||
depositVerifyCmd.Flags().StringVar(&depositVerifyWithdrawalAddress, "withdrawaladdress", "", "Ethereum 1 address of the account to which the validator funds will be withdrawn")
|
||||
depositVerifyCmd.Flags().StringVar(&depositVerifyDepositAmount, "depositvalue", "32 Ether", "Value of the amount to be deposited")
|
||||
depositVerifyCmd.Flags().StringVar(&depositVerifyValidatorPubKey, "validatorpubkey", "", "Public key(s) of the account(s) that will be carrying out validation")
|
||||
depositVerifyCmd.Flags().StringVar(&depositVerifyForkVersion, "forkversion", "0x00000000", "Fork version of the chain of the deposit")
|
||||
|
||||
@@ -70,7 +70,9 @@ In quiet mode this will return 0 if the the exit is verified correctly, otherwis
|
||||
}
|
||||
exitRoot, err := exit.HashTreeRoot()
|
||||
errCheck(err, "Failed to obtain exit hash tree root")
|
||||
sig, err := e2types.BLSSignatureFromBytes(data.Exit.Signature[:])
|
||||
signatureBytes := make([]byte, 96)
|
||||
copy(signatureBytes, data.Exit.Signature[:])
|
||||
sig, err := e2types.BLSSignatureFromBytes(signatureBytes)
|
||||
errCheck(err, "Invalid signature")
|
||||
verified, err := util.VerifyRoot(account, exitRoot, exitDomain, sig)
|
||||
errCheck(err, "Failed to verify voluntary exit")
|
||||
|
||||
@@ -17,25 +17,28 @@ import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
spec "github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/viper"
|
||||
ethdoutil "github.com/wealdtech/ethdo/util"
|
||||
e2types "github.com/wealdtech/go-eth2-types/v2"
|
||||
util "github.com/wealdtech/go-eth2-util"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
string2eth "github.com/wealdtech/go-string2eth"
|
||||
)
|
||||
|
||||
type dataIn struct {
|
||||
format string
|
||||
withdrawalCredentials []byte
|
||||
amount spec.Gwei
|
||||
validatorAccounts []e2wtypes.Account
|
||||
forkVersion *spec.Version
|
||||
domain *spec.Domain
|
||||
passphrases []string
|
||||
format string
|
||||
timeout time.Duration
|
||||
withdrawalAccount string
|
||||
withdrawalPubKey string
|
||||
withdrawalAddress string
|
||||
amount spec.Gwei
|
||||
validatorAccounts []e2wtypes.Account
|
||||
forkVersion *spec.Version
|
||||
domain *spec.Domain
|
||||
passphrases []string
|
||||
}
|
||||
|
||||
func input() (*dataIn, error) {
|
||||
@@ -49,6 +52,11 @@ func input() (*dataIn, error) {
|
||||
return nil, errors.New("validator account is required")
|
||||
}
|
||||
|
||||
if viper.GetDuration("timeout") == 0 {
|
||||
return nil, errors.New("timeout is required")
|
||||
}
|
||||
data.timeout = viper.GetDuration("timeout")
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||
defer cancel()
|
||||
_, data.validatorAccounts, err = ethdoutil.WalletAndAccountsFromPath(ctx, viper.GetString("validatoraccount"))
|
||||
@@ -70,37 +78,25 @@ func input() (*dataIn, error) {
|
||||
|
||||
data.passphrases = ethdoutil.GetPassphrases()
|
||||
|
||||
switch {
|
||||
case viper.GetString("withdrawalaccount") != "":
|
||||
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||
defer cancel()
|
||||
_, withdrawalAccount, err := ethdoutil.WalletAndAccountFromPath(ctx, viper.GetString("withdrawalaccount"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain withdrawal account")
|
||||
}
|
||||
pubKey, err := ethdoutil.BestPublicKey(withdrawalAccount)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain public key for withdrawal account")
|
||||
}
|
||||
data.withdrawalCredentials = util.SHA256(pubKey.Marshal())
|
||||
case viper.GetString("withdrawalpubkey") != "":
|
||||
withdrawalPubKeyBytes, err := hex.DecodeString(strings.TrimPrefix(viper.GetString("withdrawalpubkey"), "0x"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode withdrawal public key")
|
||||
}
|
||||
if len(withdrawalPubKeyBytes) != 48 {
|
||||
return nil, errors.New("withdrawal public key must be exactly 48 bytes in length")
|
||||
}
|
||||
withdrawalPubKey, err := e2types.BLSPublicKeyFromBytes(withdrawalPubKeyBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "withdrawal public key is not valid")
|
||||
}
|
||||
data.withdrawalCredentials = util.SHA256(withdrawalPubKey.Marshal())
|
||||
default:
|
||||
return nil, errors.New("withdrawalaccount or withdrawal public key is required")
|
||||
data.withdrawalAccount = viper.GetString("withdrawalaccount")
|
||||
data.withdrawalPubKey = viper.GetString("withdrawalpubkey")
|
||||
data.withdrawalAddress = viper.GetString("withdrawaladdress")
|
||||
withdrawalDetailsPresent := 0
|
||||
if data.withdrawalAccount != "" {
|
||||
withdrawalDetailsPresent++
|
||||
}
|
||||
if data.withdrawalPubKey != "" {
|
||||
withdrawalDetailsPresent++
|
||||
}
|
||||
if data.withdrawalAddress != "" {
|
||||
withdrawalDetailsPresent++
|
||||
}
|
||||
if withdrawalDetailsPresent == 0 {
|
||||
return nil, errors.New("withdrawal account, public key or address is required")
|
||||
}
|
||||
if withdrawalDetailsPresent > 1 {
|
||||
return nil, errors.New("only one of withdrawal account, public key or address is allowed")
|
||||
}
|
||||
// This is hard-coded, to allow deposit data to be generated without a connection to the beacon node.
|
||||
data.withdrawalCredentials[0] = byte(0) // BLS_WITHDRAWAL_PREFIX
|
||||
|
||||
if viper.GetString("depositvalue") == "" {
|
||||
return nil, errors.New("deposit value is required")
|
||||
@@ -135,7 +131,7 @@ func inputForkVersion(ctx context.Context) (*spec.Version, error) {
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode fork version")
|
||||
}
|
||||
if len(forkVersion) != 4 {
|
||||
if len(data) != 4 {
|
||||
return nil, errors.New("fork version must be exactly 4 bytes in length")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2019, 2020 Weald Technology Trading
|
||||
// Copyright © 2019-2021 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
|
||||
@@ -84,9 +84,20 @@ func TestInput(t *testing.T) {
|
||||
name: "Nil",
|
||||
err: "validator account is required",
|
||||
},
|
||||
{
|
||||
name: "TimeoutMissing",
|
||||
vars: map[string]interface{}{
|
||||
"validatoraccount": "Test/Interop 0",
|
||||
"withdrawalaccount": "Test/Interop 0",
|
||||
"depositvalue": "32 Ether",
|
||||
"forkversion": "0x01020304",
|
||||
},
|
||||
err: "timeout is required",
|
||||
},
|
||||
{
|
||||
name: "ValidatorAccountMissing",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "10s",
|
||||
"withdrawalaccount": "Test/Interop 0",
|
||||
"depositvalue": "32 Ether",
|
||||
"forkversion": "0x01020304",
|
||||
@@ -96,6 +107,7 @@ func TestInput(t *testing.T) {
|
||||
{
|
||||
name: "ValidatorAccountUnknown",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "10s",
|
||||
"validatoraccount": "Test/Unknown",
|
||||
"withdrawalaccount": "Test/Interop 0",
|
||||
"depositvalue": "32 Ether",
|
||||
@@ -104,59 +116,74 @@ func TestInput(t *testing.T) {
|
||||
err: "unknown validator account",
|
||||
},
|
||||
{
|
||||
name: "WithdrawalAccountMissing",
|
||||
name: "WithdrawalDetailsMissing",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "10s",
|
||||
"launchpad": true,
|
||||
"validatoraccount": "Test/Interop 0",
|
||||
"depositvalue": "32 Ether",
|
||||
"forkversion": "0x01020304",
|
||||
},
|
||||
err: "withdrawalaccount or withdrawal public key is required",
|
||||
err: "withdrawal account, public key or address is required",
|
||||
},
|
||||
{
|
||||
name: "WithdrawalAccountUnknown",
|
||||
name: "WithdrawalDetailsTooMany1",
|
||||
vars: map[string]interface{}{
|
||||
"raw": true,
|
||||
"timeout": "10s",
|
||||
"launchpad": true,
|
||||
"validatoraccount": "Test/Interop 0",
|
||||
"withdrawalaccount": "Test/Unknown",
|
||||
"withdrawalaccount": "Test/Interop 0",
|
||||
"withdrawalpubkey": "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c",
|
||||
"depositvalue": "32 Ether",
|
||||
"forkversion": "0x01020304",
|
||||
},
|
||||
err: "failed to obtain withdrawal account: failed to obtain account: no account with name \"Unknown\"",
|
||||
err: "only one of withdrawal account, public key or address is allowed",
|
||||
},
|
||||
{
|
||||
name: "WithdrawalPubKeyInvalid",
|
||||
name: "WithdrawalDetailsTooMany2",
|
||||
vars: map[string]interface{}{
|
||||
"validatoraccount": "Test/Interop 0",
|
||||
"withdrawalpubkey": "invalid",
|
||||
"depositvalue": "32 Ether",
|
||||
"forkversion": "0x01020304",
|
||||
"timeout": "10s",
|
||||
"launchpad": true,
|
||||
"validatoraccount": "Test/Interop 0",
|
||||
"withdrawalaccount": "Test/Interop 0",
|
||||
"withdrawalpubkey": "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c",
|
||||
"withdrawaladdress": "0x30C99930617B7b793beaB603ecEB08691005f2E5",
|
||||
"depositvalue": "32 Ether",
|
||||
"forkversion": "0x01020304",
|
||||
},
|
||||
err: "failed to decode withdrawal public key: encoding/hex: invalid byte: U+0069 'i'",
|
||||
err: "only one of withdrawal account, public key or address is allowed",
|
||||
},
|
||||
{
|
||||
name: "WithdrawalPubKeyWrongLength",
|
||||
name: "WithdrawalDetailsTooMany3",
|
||||
vars: map[string]interface{}{
|
||||
"validatoraccount": "Test/Interop 0",
|
||||
"withdrawalpubkey": "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0bff",
|
||||
"depositvalue": "32 Ether",
|
||||
"forkversion": "0x01020304",
|
||||
"timeout": "10s",
|
||||
"launchpad": true,
|
||||
"validatoraccount": "Test/Interop 0",
|
||||
"withdrawalpubkey": "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c",
|
||||
"withdrawaladdress": "0x30C99930617B7b793beaB603ecEB08691005f2E5",
|
||||
"depositvalue": "32 Ether",
|
||||
"forkversion": "0x01020304",
|
||||
},
|
||||
err: "withdrawal public key must be exactly 48 bytes in length",
|
||||
err: "only one of withdrawal account, public key or address is allowed",
|
||||
},
|
||||
{
|
||||
name: "WithdrawalPubKeyNotPubKey",
|
||||
name: "WithdrawalDetailsTooMany4",
|
||||
vars: map[string]interface{}{
|
||||
"validatoraccount": "Test/Interop 0",
|
||||
"withdrawalpubkey": "0x089bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b",
|
||||
"depositvalue": "32 Ether",
|
||||
"forkversion": "0x01020304",
|
||||
"timeout": "10s",
|
||||
"launchpad": true,
|
||||
"validatoraccount": "Test/Interop 0",
|
||||
"withdrawalaccount": "Test/Interop 0",
|
||||
"withdrawalpubkey": "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c",
|
||||
"withdrawaladdress": "0x30C99930617B7b793beaB603ecEB08691005f2E5",
|
||||
"depositvalue": "32 Ether",
|
||||
"forkversion": "0x01020304",
|
||||
},
|
||||
err: "withdrawal public key is not valid: failed to deserialize public key: err blsPublicKeyDeserialize 089bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b",
|
||||
err: "only one of withdrawal account, public key or address is allowed",
|
||||
},
|
||||
{
|
||||
name: "DepositValueMissing",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "10s",
|
||||
"validatoraccount": "Test/Interop 0",
|
||||
"withdrawalaccount": "Test/Interop 0",
|
||||
"forkversion": "0x01020304",
|
||||
@@ -166,6 +193,7 @@ func TestInput(t *testing.T) {
|
||||
{
|
||||
name: "DepositValueTooSmall",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "10s",
|
||||
"validatoraccount": "Test/Interop 0",
|
||||
"withdrawalaccount": "Test/Interop 0",
|
||||
"depositvalue": "1000 Wei",
|
||||
@@ -176,6 +204,7 @@ func TestInput(t *testing.T) {
|
||||
{
|
||||
name: "DepositValueInvalid",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "10s",
|
||||
"validatoraccount": "Test/Interop 0",
|
||||
"withdrawalaccount": "Test/Interop 0",
|
||||
"depositvalue": "1 groat",
|
||||
@@ -186,6 +215,7 @@ func TestInput(t *testing.T) {
|
||||
{
|
||||
name: "ForkVersionInvalid",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "10s",
|
||||
"validatoraccount": "Test/Interop 0",
|
||||
"withdrawalaccount": "Test/Interop 0",
|
||||
"depositvalue": "32 Ether",
|
||||
@@ -193,54 +223,68 @@ func TestInput(t *testing.T) {
|
||||
},
|
||||
err: "failed to obtain fork version: failed to decode fork version: encoding/hex: invalid byte: U+0069 'i'",
|
||||
},
|
||||
{
|
||||
name: "ForkVersionShort",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "10s",
|
||||
"validatoraccount": "Test/Interop 0",
|
||||
"withdrawalaccount": "Test/Interop 0",
|
||||
"depositvalue": "32 Ether",
|
||||
"forkversion": "0x01",
|
||||
},
|
||||
err: "failed to obtain fork version: fork version must be exactly 4 bytes in length",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "10s",
|
||||
"validatoraccount": "Test/Interop 0",
|
||||
"withdrawalaccount": "Test/Interop 0",
|
||||
"depositvalue": "32 Ether",
|
||||
},
|
||||
res: &dataIn{
|
||||
format: "json",
|
||||
withdrawalCredentials: testutil.HexToBytes("0x00fad2a6bfb0e7f1f0f45460944fbd8dfa7f37da06a4d13b3983cc90bb46963b"),
|
||||
amount: 32000000000,
|
||||
validatorAccounts: []e2wtypes.Account{interop0},
|
||||
forkVersion: mainnetForkVersion,
|
||||
domain: mainnetDomain,
|
||||
format: "json",
|
||||
withdrawalAccount: "Test/Interop 0",
|
||||
amount: 32000000000,
|
||||
validatorAccounts: []e2wtypes.Account{interop0},
|
||||
forkVersion: mainnetForkVersion,
|
||||
domain: mainnetDomain,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "GoodForkVersionOverride",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "10s",
|
||||
"validatoraccount": "Test/Interop 0",
|
||||
"withdrawalaccount": "Test/Interop 0",
|
||||
"depositvalue": "32 Ether",
|
||||
"forkversion": "0x01020304",
|
||||
},
|
||||
res: &dataIn{
|
||||
format: "json",
|
||||
withdrawalCredentials: testutil.HexToBytes("0x00fad2a6bfb0e7f1f0f45460944fbd8dfa7f37da06a4d13b3983cc90bb46963b"),
|
||||
amount: 32000000000,
|
||||
validatorAccounts: []e2wtypes.Account{interop0},
|
||||
forkVersion: forkVersion,
|
||||
domain: domain,
|
||||
format: "json",
|
||||
withdrawalAccount: "Test/Interop 0",
|
||||
amount: 32000000000,
|
||||
validatorAccounts: []e2wtypes.Account{interop0},
|
||||
forkVersion: forkVersion,
|
||||
domain: domain,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "GoodWithdrawalPubKey",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "10s",
|
||||
"validatoraccount": "Test/Interop 0",
|
||||
"withdrawalpubkey": "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c",
|
||||
"depositvalue": "32 Ether",
|
||||
"forkversion": "0x01020304",
|
||||
},
|
||||
res: &dataIn{
|
||||
format: "json",
|
||||
withdrawalCredentials: testutil.HexToBytes("0x00fad2a6bfb0e7f1f0f45460944fbd8dfa7f37da06a4d13b3983cc90bb46963b"),
|
||||
amount: 32000000000,
|
||||
validatorAccounts: []e2wtypes.Account{interop0},
|
||||
forkVersion: forkVersion,
|
||||
domain: domain,
|
||||
format: "json",
|
||||
withdrawalPubKey: "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c",
|
||||
amount: 32000000000,
|
||||
validatorAccounts: []e2wtypes.Account{interop0},
|
||||
forkVersion: forkVersion,
|
||||
domain: domain,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -258,7 +302,9 @@ func TestInput(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
// Cannot compare accounts directly, so need to check each element individually.
|
||||
require.Equal(t, test.res.format, res.format)
|
||||
require.Equal(t, test.res.withdrawalCredentials, res.withdrawalCredentials)
|
||||
require.Equal(t, test.res.withdrawalAccount, res.withdrawalAccount)
|
||||
require.Equal(t, test.res.withdrawalAddress, res.withdrawalAddress)
|
||||
require.Equal(t, test.res.withdrawalPubKey, res.withdrawalPubKey)
|
||||
require.Equal(t, test.res.amount, res.amount)
|
||||
require.Equal(t, test.res.forkVersion, res.forkVersion)
|
||||
require.Equal(t, test.res.domain, res.domain)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2019, 2020 Weald Technology Trading
|
||||
// Copyright © 2019-2021 Weald Technology Limited.
|
||||
// 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
|
||||
@@ -15,12 +15,16 @@ package depositdata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
spec "github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/wealdtech/ethdo/signing"
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
ethdoutil "github.com/wealdtech/ethdo/util"
|
||||
e2types "github.com/wealdtech/go-eth2-types/v2"
|
||||
util "github.com/wealdtech/go-eth2-util"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
@@ -31,8 +35,13 @@ func process(data *dataIn) ([]*dataOut, error) {
|
||||
|
||||
results := make([]*dataOut, 0)
|
||||
|
||||
withdrawalCredentials, err := createWithdrawalCredentials(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, validatorAccount := range data.validatorAccounts {
|
||||
validatorPubKey, err := util.BestPublicKey(validatorAccount)
|
||||
validatorPubKey, err := ethdoutil.BestPublicKey(validatorAccount)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "validator account does not provide a public key")
|
||||
}
|
||||
@@ -41,7 +50,7 @@ func process(data *dataIn) ([]*dataOut, error) {
|
||||
copy(pubKey[:], validatorPubKey.Marshal())
|
||||
depositMessage := &spec.DepositMessage{
|
||||
PublicKey: pubKey,
|
||||
WithdrawalCredentials: data.withdrawalCredentials,
|
||||
WithdrawalCredentials: withdrawalCredentials,
|
||||
Amount: data.amount,
|
||||
}
|
||||
root, err := depositMessage.HashTreeRoot()
|
||||
@@ -58,7 +67,7 @@ func process(data *dataIn) ([]*dataOut, error) {
|
||||
|
||||
depositData := &spec.DepositData{
|
||||
PublicKey: pubKey,
|
||||
WithdrawalCredentials: data.withdrawalCredentials,
|
||||
WithdrawalCredentials: withdrawalCredentials,
|
||||
Amount: data.amount,
|
||||
Signature: sig,
|
||||
}
|
||||
@@ -75,7 +84,7 @@ func process(data *dataIn) ([]*dataOut, error) {
|
||||
format: data.format,
|
||||
account: fmt.Sprintf("%s/%s", validatorWallet.Name(), validatorAccount.Name()),
|
||||
validatorPubKey: &pubKey,
|
||||
withdrawalCredentials: data.withdrawalCredentials,
|
||||
withdrawalCredentials: withdrawalCredentials,
|
||||
amount: data.amount,
|
||||
signature: &sig,
|
||||
forkVersion: data.forkVersion,
|
||||
@@ -85,3 +94,80 @@ func process(data *dataIn) ([]*dataOut, error) {
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// createWithdrawalCredentials creates withdrawal credentials given an account, public key or Ethereum 1 address.
|
||||
func createWithdrawalCredentials(data *dataIn) ([]byte, error) {
|
||||
var withdrawalCredentials []byte
|
||||
|
||||
switch {
|
||||
case data.withdrawalAccount != "":
|
||||
ctx, cancel := context.WithTimeout(context.Background(), data.timeout)
|
||||
defer cancel()
|
||||
_, withdrawalAccount, err := ethdoutil.WalletAndAccountFromPath(ctx, data.withdrawalAccount)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain withdrawal account")
|
||||
}
|
||||
pubKey, err := ethdoutil.BestPublicKey(withdrawalAccount)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain public key for withdrawal account")
|
||||
}
|
||||
withdrawalCredentials = util.SHA256(pubKey.Marshal())
|
||||
// This is hard-coded, to allow deposit data to be generated without a connection to the beacon node.
|
||||
withdrawalCredentials[0] = byte(0) // BLS_WITHDRAWAL_PREFIX
|
||||
case data.withdrawalPubKey != "":
|
||||
withdrawalPubKeyBytes, err := hex.DecodeString(strings.TrimPrefix(data.withdrawalPubKey, "0x"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode withdrawal public key")
|
||||
}
|
||||
if len(withdrawalPubKeyBytes) != 48 {
|
||||
return nil, errors.New("withdrawal public key must be exactly 48 bytes in length")
|
||||
}
|
||||
pubKey, err := e2types.BLSPublicKeyFromBytes(withdrawalPubKeyBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "withdrawal public key is not valid")
|
||||
}
|
||||
withdrawalCredentials = util.SHA256(pubKey.Marshal())
|
||||
// This is hard-coded, to allow deposit data to be generated without a connection to the beacon node.
|
||||
withdrawalCredentials[0] = byte(0) // BLS_WITHDRAWAL_PREFIX
|
||||
case data.withdrawalAddress != "":
|
||||
withdrawalAddressBytes, err := hex.DecodeString(strings.TrimPrefix(data.withdrawalAddress, "0x"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode withdrawal address")
|
||||
}
|
||||
if len(withdrawalAddressBytes) != 20 {
|
||||
return nil, errors.New("withdrawal address must be exactly 20 bytes in length")
|
||||
}
|
||||
// Ensure the address is properly checksummed.
|
||||
checksummedAddress := addressBytesToEIP55(withdrawalAddressBytes)
|
||||
if checksummedAddress != data.withdrawalAddress {
|
||||
return nil, fmt.Errorf("withdrawal address checksum does not match (expected %s)", checksummedAddress)
|
||||
}
|
||||
withdrawalCredentials = make([]byte, 32)
|
||||
copy(withdrawalCredentials[12:32], withdrawalAddressBytes)
|
||||
// This is hard-coded, to allow deposit data to be generated without a connection to the beacon node.
|
||||
withdrawalCredentials[0] = byte(1) // ETH1_ADDRESS_WITHDRAWAL_PREFIX
|
||||
default:
|
||||
return nil, errors.New("withdrawal account, public key or address is required")
|
||||
}
|
||||
|
||||
return withdrawalCredentials, 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 := util.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))
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2019, 2020 eald Technology Trading
|
||||
// Copyright © 2019-2021 Weald Technology Limited.
|
||||
// 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
|
||||
@@ -15,6 +15,8 @@ package depositdata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
spec "github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
@@ -49,6 +51,10 @@ func TestProcess(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
withdrawalAccount := "Test/Interop 0"
|
||||
withdrawalPubKey := "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c"
|
||||
withdrawalAddress := "0x30C99930617B7b793beaB603ecEB08691005f2E5"
|
||||
|
||||
var validatorPubKey *spec.BLSPubKey
|
||||
{
|
||||
tmp := testutil.HexToPubKey("0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c")
|
||||
@@ -101,6 +107,22 @@ func TestProcess(t *testing.T) {
|
||||
depositMessageRoot2 = &tmp
|
||||
}
|
||||
|
||||
var depositDataRoot3 *spec.Root
|
||||
{
|
||||
tmp := testutil.HexToRoot("0x489500535b03dd9deffa0f00cb38d82346111856fb58a9541fe1f01a1a97429c")
|
||||
depositDataRoot3 = &tmp
|
||||
}
|
||||
var depositMessageRoot3 *spec.Root
|
||||
{
|
||||
tmp := testutil.HexToRoot("0x7b8ee5694e4338cf2bfe5a4d2f46540f0ade85ebd30713673cf5783c4e925681")
|
||||
depositMessageRoot3 = &tmp
|
||||
}
|
||||
var signature3 *spec.BLSSignature
|
||||
{
|
||||
tmp := testutil.HexToSignature("0xba0019d5c421f205d845782f52a87ab95cd489fbef2911f8a1f9cf7c14b4ce59eefa82641e770a4cb405534b7776d0f801b0a8b178c1b71b718c104e89f4e633da10a398c7919a00c403d58f3f4b827af8adb263b192e7a45b0ed1926dff5f66")
|
||||
signature3 = &tmp
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
dataIn *dataIn
|
||||
@@ -111,16 +133,119 @@ func TestProcess(t *testing.T) {
|
||||
name: "Nil",
|
||||
err: "no data",
|
||||
},
|
||||
{
|
||||
name: "WithdrawalDetailsMissing",
|
||||
dataIn: &dataIn{
|
||||
format: "raw",
|
||||
passphrases: []string{"pass"},
|
||||
amount: 32000000000,
|
||||
validatorAccounts: []e2wtypes.Account{interop0},
|
||||
forkVersion: forkVersion,
|
||||
domain: domain,
|
||||
},
|
||||
err: "withdrawal account, public key or address is required",
|
||||
},
|
||||
{
|
||||
name: "WithdrawalAccountUnknown",
|
||||
dataIn: &dataIn{
|
||||
format: "raw",
|
||||
passphrases: []string{"pass"},
|
||||
withdrawalAccount: "Unknown",
|
||||
amount: 32000000000,
|
||||
validatorAccounts: []e2wtypes.Account{interop0},
|
||||
forkVersion: forkVersion,
|
||||
domain: domain,
|
||||
},
|
||||
err: "failed to obtain withdrawal account: failed to open wallet for account: wallet not found",
|
||||
},
|
||||
{
|
||||
name: "WithdrawalPubKeyInvalid",
|
||||
dataIn: &dataIn{
|
||||
format: "raw",
|
||||
passphrases: []string{"pass"},
|
||||
withdrawalPubKey: "invalid",
|
||||
amount: 32000000000,
|
||||
validatorAccounts: []e2wtypes.Account{interop0},
|
||||
forkVersion: forkVersion,
|
||||
domain: domain,
|
||||
},
|
||||
err: "failed to decode withdrawal public key: encoding/hex: invalid byte: U+0069 'i'",
|
||||
},
|
||||
{
|
||||
name: "WithdrawalPubKeyWrongLength",
|
||||
dataIn: &dataIn{
|
||||
format: "raw",
|
||||
passphrases: []string{"pass"},
|
||||
withdrawalPubKey: "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0bff",
|
||||
amount: 32000000000,
|
||||
validatorAccounts: []e2wtypes.Account{interop0},
|
||||
forkVersion: forkVersion,
|
||||
domain: domain,
|
||||
},
|
||||
err: "withdrawal public key must be exactly 48 bytes in length",
|
||||
},
|
||||
{
|
||||
name: "WithdrawalPubKeyNotPubKey",
|
||||
dataIn: &dataIn{
|
||||
format: "raw",
|
||||
passphrases: []string{"pass"},
|
||||
withdrawalPubKey: "0x089bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b",
|
||||
amount: 32000000000,
|
||||
validatorAccounts: []e2wtypes.Account{interop0},
|
||||
forkVersion: forkVersion,
|
||||
domain: domain,
|
||||
},
|
||||
err: "withdrawal public key is not valid: failed to deserialize public key: err blsPublicKeyDeserialize 089bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b",
|
||||
},
|
||||
{
|
||||
name: "WithdrawalAddressInvalid",
|
||||
dataIn: &dataIn{
|
||||
format: "raw",
|
||||
passphrases: []string{"pass"},
|
||||
withdrawalAddress: "invalid",
|
||||
amount: 32000000000,
|
||||
validatorAccounts: []e2wtypes.Account{interop0},
|
||||
forkVersion: forkVersion,
|
||||
domain: domain,
|
||||
},
|
||||
err: "failed to decode withdrawal address: encoding/hex: invalid byte: U+0069 'i'",
|
||||
},
|
||||
{
|
||||
name: "WithdrawalAddressWrongLength",
|
||||
dataIn: &dataIn{
|
||||
format: "raw",
|
||||
passphrases: []string{"pass"},
|
||||
withdrawalAddress: "0x30C99930617B7b793beaB603ecEB08691005f2",
|
||||
amount: 32000000000,
|
||||
validatorAccounts: []e2wtypes.Account{interop0},
|
||||
forkVersion: forkVersion,
|
||||
domain: domain,
|
||||
},
|
||||
err: "withdrawal address must be exactly 20 bytes in length",
|
||||
},
|
||||
{
|
||||
name: "WithdrawalAddressIncorrectChecksum",
|
||||
dataIn: &dataIn{
|
||||
format: "raw",
|
||||
passphrases: []string{"pass"},
|
||||
withdrawalAddress: "0x30c99930617b7b793beab603eceb08691005f2e5",
|
||||
amount: 32000000000,
|
||||
validatorAccounts: []e2wtypes.Account{interop0},
|
||||
forkVersion: forkVersion,
|
||||
domain: domain,
|
||||
},
|
||||
err: "withdrawal address checksum does not match (expected 0x30C99930617B7b793beaB603ecEB08691005f2E5)",
|
||||
},
|
||||
{
|
||||
name: "Single",
|
||||
dataIn: &dataIn{
|
||||
format: "raw",
|
||||
passphrases: []string{"pass"},
|
||||
withdrawalCredentials: testutil.HexToBytes("0x00fad2a6bfb0e7f1f0f45460944fbd8dfa7f37da06a4d13b3983cc90bb46963b"),
|
||||
amount: 32000000000,
|
||||
validatorAccounts: []e2wtypes.Account{interop0},
|
||||
forkVersion: forkVersion,
|
||||
domain: domain,
|
||||
format: "raw",
|
||||
passphrases: []string{"pass"},
|
||||
withdrawalAccount: withdrawalAccount,
|
||||
amount: 32000000000,
|
||||
validatorAccounts: []e2wtypes.Account{interop0},
|
||||
forkVersion: forkVersion,
|
||||
domain: domain,
|
||||
},
|
||||
res: []*dataOut{
|
||||
{
|
||||
@@ -139,13 +264,13 @@ func TestProcess(t *testing.T) {
|
||||
{
|
||||
name: "Double",
|
||||
dataIn: &dataIn{
|
||||
format: "raw",
|
||||
passphrases: []string{"pass"},
|
||||
withdrawalCredentials: testutil.HexToBytes("0x00fad2a6bfb0e7f1f0f45460944fbd8dfa7f37da06a4d13b3983cc90bb46963b"),
|
||||
amount: 32000000000,
|
||||
validatorAccounts: []e2wtypes.Account{interop0, interop1},
|
||||
forkVersion: forkVersion,
|
||||
domain: domain,
|
||||
format: "raw",
|
||||
passphrases: []string{"pass"},
|
||||
withdrawalPubKey: withdrawalPubKey,
|
||||
amount: 32000000000,
|
||||
validatorAccounts: []e2wtypes.Account{interop0, interop1},
|
||||
forkVersion: forkVersion,
|
||||
domain: domain,
|
||||
},
|
||||
res: []*dataOut{
|
||||
{
|
||||
@@ -172,6 +297,31 @@ func TestProcess(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "WithdrawalAddress",
|
||||
dataIn: &dataIn{
|
||||
format: "raw",
|
||||
passphrases: []string{"pass"},
|
||||
withdrawalAddress: withdrawalAddress,
|
||||
amount: 32000000000,
|
||||
validatorAccounts: []e2wtypes.Account{interop0},
|
||||
forkVersion: forkVersion,
|
||||
domain: domain,
|
||||
},
|
||||
res: []*dataOut{
|
||||
{
|
||||
format: "raw",
|
||||
account: "Test/Interop 0",
|
||||
validatorPubKey: validatorPubKey,
|
||||
amount: 32000000000,
|
||||
withdrawalCredentials: testutil.HexToBytes("0x01000000000000000000000030C99930617B7b793beaB603ecEB08691005f2E5"),
|
||||
signature: signature3,
|
||||
forkVersion: forkVersion,
|
||||
depositDataRoot: depositDataRoot3,
|
||||
depositMessageRoot: depositMessageRoot3,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -186,3 +336,18 @@ func TestProcess(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddressBytesToEIP55(t *testing.T) {
|
||||
tests := []string{
|
||||
"0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
|
||||
"0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
|
||||
"0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB",
|
||||
"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb",
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
bytes, err := hex.DecodeString(strings.TrimPrefix(test, "0x"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, addressBytesToEIP55(bytes), test)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,9 +49,10 @@ In quiet mode this will return 0 if the the data can be generated correctly, oth
|
||||
func init() {
|
||||
validatorCmd.AddCommand(validatorDepositDataCmd)
|
||||
validatorFlags(validatorDepositDataCmd)
|
||||
validatorDepositDataCmd.Flags().String("validatoraccount", "", "Account of the account carrying out the validation")
|
||||
validatorDepositDataCmd.Flags().String("withdrawalaccount", "", "Account of the account to which the validator funds will be withdrawn")
|
||||
validatorDepositDataCmd.Flags().String("validatoraccount", "", "Account carrying out the validation")
|
||||
validatorDepositDataCmd.Flags().String("withdrawalaccount", "", "Account to which the validator funds will be withdrawn")
|
||||
validatorDepositDataCmd.Flags().String("withdrawalpubkey", "", "Public key of the account to which the validator funds will be withdrawn")
|
||||
validatorDepositDataCmd.Flags().String("withdrawaladdress", "", "Ethereum 1 address of the account to which the validator funds will be withdrawn")
|
||||
validatorDepositDataCmd.Flags().String("depositvalue", "", "Value of the amount to be deposited")
|
||||
validatorDepositDataCmd.Flags().Bool("raw", false, "Print raw deposit data transaction data")
|
||||
validatorDepositDataCmd.Flags().String("forkversion", "", "Use a hard-coded fork version (default is to fetch it from the node)")
|
||||
@@ -68,6 +69,9 @@ func validatorDepositdataBindings() {
|
||||
if err := viper.BindPFlag("withdrawalpubkey", validatorDepositDataCmd.Flags().Lookup("withdrawalpubkey")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("withdrawaladdress", validatorDepositDataCmd.Flags().Lookup("withdrawaladdress")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("depositvalue", validatorDepositDataCmd.Flags().Lookup("depositvalue")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ import (
|
||||
)
|
||||
|
||||
// ReleaseVersion is the release version of the codebase.
|
||||
// Usually overrideen by tag names when building binaries.
|
||||
var ReleaseVersion = "local build (latest release 1.8.0)"
|
||||
// Usually overridden by tag names when building binaries.
|
||||
var ReleaseVersion = "local build (latest release 1.9.1)"
|
||||
|
||||
// versionCmd represents the version command
|
||||
var versionCmd = &cobra.Command{
|
||||
|
||||
1
go.mod
1
go.mod
@@ -54,6 +54,7 @@ require (
|
||||
github.com/wealdtech/go-eth2-wallet-store-scratch v1.6.2
|
||||
github.com/wealdtech/go-eth2-wallet-types/v2 v2.8.2
|
||||
github.com/wealdtech/go-string2eth v1.1.0
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect
|
||||
golang.org/x/text v0.3.5
|
||||
google.golang.org/genproto v0.0.0-20210201184850-646a494a81ea // indirect
|
||||
|
||||
Reference in New Issue
Block a user