Compare commits

...

6 Commits

Author SHA1 Message Date
Jim McDonald
b1647d2f3d Update version. 2020-12-10 00:10:46 +00:00
Jim McDonald
c7f3275dfa Add validtor duties; update validator exit 2020-12-09 20:38:21 +00:00
Jim McDonald
7aeba43338 Add validtor duties; update validator exit 2020-12-09 20:33:30 +00:00
Jim McDonald
688db9ef8c Update dependencies. 2020-12-07 15:21:37 +00:00
Jim McDonald
173883da3e Move core functions to util 2020-12-07 15:18:23 +00:00
Jim McDonald
6077e04619 Move core functions to util 2020-12-07 14:52:08 +00:00
39 changed files with 938 additions and 163 deletions

View File

@@ -1,3 +1,7 @@
1.7.3:
- fix issue where base directory was ignored for wallet creation
- new "validator duties" command to display known duties for a given validator
- update go-eth2-client to display correct validator status from prysm
1.7.2:
- new "account derive" command to derive keys directly from a mnemonic and derivation path
- add more output to "deposit verify" to explain operation

View File

@@ -19,7 +19,6 @@ import (
"github.com/pkg/errors"
"github.com/spf13/viper"
"github.com/wealdtech/ethdo/core"
"github.com/wealdtech/ethdo/util"
e2wallet "github.com/wealdtech/go-eth2-wallet"
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
@@ -63,7 +62,7 @@ func input(ctx context.Context) (*dataIn, error) {
// Wallet.
ctx, cancel := context.WithTimeout(ctx, data.timeout)
defer cancel()
data.wallet, err = core.WalletFromInput(ctx)
data.wallet, err = util.WalletFromInput(ctx)
cancel()
if err != nil {
return nil, errors.Wrap(err, "failed to obtain wallet")

View File

@@ -21,7 +21,6 @@ import (
"github.com/pkg/errors"
"github.com/spf13/viper"
"github.com/wealdtech/ethdo/core"
"github.com/wealdtech/ethdo/util"
e2wallet "github.com/wealdtech/go-eth2-wallet"
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
@@ -60,7 +59,7 @@ func input(ctx context.Context) (*dataIn, error) {
// Wallet.
ctx, cancel := context.WithTimeout(ctx, data.timeout)
defer cancel()
data.wallet, err = core.WalletFromInput(ctx)
data.wallet, err = util.WalletFromInput(ctx)
cancel()
if err != nil {
return nil, errors.Wrap(err, "failed to obtain wallet")

View File

@@ -19,7 +19,6 @@ import (
"github.com/pkg/errors"
"github.com/spf13/viper"
"github.com/wealdtech/ethdo/core"
"github.com/wealdtech/ethdo/util"
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
)
@@ -40,7 +39,7 @@ func input(ctx context.Context) (*dataIn, error) {
data.timeout = viper.GetDuration("timeout")
// Account.
_, data.account, err = core.WalletAndAccountFromInput(ctx)
_, data.account, err = util.WalletAndAccountFromInput(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to obtain acount")
}

View File

@@ -25,7 +25,6 @@ import (
spec "github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/pkg/errors"
"github.com/spf13/viper"
"github.com/wealdtech/ethdo/core"
"github.com/wealdtech/ethdo/util"
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
)
@@ -69,14 +68,15 @@ func input(ctx context.Context) (*dataIn, error) {
return nil, errors.Wrap(err, "failed to connect to Ethereum 2 beacon node")
}
// Epoch
config, err := data.eth2Client.(eth2client.SpecProvider).Spec(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to obtain beacon chain configuration")
}
data.slotsPerEpoch = config["SLOTS_PER_EPOCH"].(uint64)
// Epoch.
epoch := viper.GetInt64("epoch")
if epoch == -1 {
config, err := data.eth2Client.(eth2client.SpecProvider).Spec(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to obtain beacon chain configuration")
}
data.slotsPerEpoch = config["SLOTS_PER_EPOCH"].(uint64)
slotDuration := config["SECONDS_PER_SLOT"].(time.Duration)
genesis, err := data.eth2Client.(eth2client.GenesisProvider).Genesis(ctx)
if err != nil {
@@ -89,17 +89,24 @@ func input(ctx context.Context) (*dataIn, error) {
}
data.epoch = spec.Epoch(epoch)
// Validator.
stateID := "head"
if viper.GetInt64("epoch") != -1 {
stateID = fmt.Sprintf("%d", uint64(data.epoch)*data.slotsPerEpoch)
}
pubKeys := make([]spec.BLSPubKey, 1)
pubKey, err := core.BestPublicKey(data.account)
pubKey, err := util.BestPublicKey(data.account)
if err != nil {
return nil, errors.Wrap(err, "failed to obtain public key for account")
}
copy(pubKeys[0][:], pubKey.Marshal())
validators, err := data.eth2Client.(eth2client.ValidatorsProvider).ValidatorsByPubKey(ctx, fmt.Sprintf("%d", uint64(data.epoch)*data.slotsPerEpoch), pubKeys)
validators, err := data.eth2Client.(eth2client.ValidatorsProvider).ValidatorsByPubKey(ctx, stateID, pubKeys)
if err != nil {
return nil, errors.New("failed to obtain validator information")
}
data.validator = validators[0]
for _, validator := range validators {
data.validator = validator
}
return data, nil
}
@@ -111,7 +118,7 @@ func attesterInclusionAccount() (e2wtypes.Account, error) {
if viper.GetString("account") != "" {
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
_, account, err = core.WalletAndAccountFromPath(ctx, viper.GetString("account"))
_, account, err = util.WalletAndAccountFromPath(ctx, viper.GetString("account"))
if err != nil {
return nil, errors.Wrap(err, "failed to obtain account")
}

View File

@@ -67,7 +67,7 @@ func process(ctx context.Context, data *dataIn) (*dataOut, error) {
}
}
}
return nil, errors.New("not found")
return results, nil
}
func duty(ctx context.Context, eth2Client eth2client.Service, validator *api.Validator, epoch spec.Epoch, slotsPerEpoch uint64) (*api.AttesterDuty, error) {

View File

@@ -50,8 +50,8 @@ In quiet mode this will return 0 if the the exit is verified correctly, otherwis
account, err := exitVerifyAccount(ctx)
errCheck(err, "Failed to obtain account")
assert(viper.GetString("exit.data") != "", "exit data is required")
data, err := obtainExitData(viper.GetString("exit.Data"))
assert(viper.GetString("exit") != "", "exit is required")
data, err := obtainExitData(viper.GetString("exit"))
errCheck(err, "Failed to obtain exit data")
// Confirm signature is good.
@@ -62,13 +62,17 @@ In quiet mode this will return 0 if the the exit is verified correctly, otherwis
errCheck(err, "Failed to obtain beacon chain genesis")
domain := e2types.Domain(e2types.DomainVoluntaryExit, data.ForkVersion[:], genesis.GenesisValidatorsRoot[:])
var exitDomain spec.Domain
copy(exitDomain[:], domain)
exit := &spec.VoluntaryExit{
Epoch: data.Data.Message.Epoch,
ValidatorIndex: data.Data.Message.ValidatorIndex,
Epoch: data.Exit.Message.Epoch,
ValidatorIndex: data.Exit.Message.ValidatorIndex,
}
sig, err := e2types.BLSSignatureFromBytes(data.Data.Signature[:])
exitRoot, err := exit.HashTreeRoot()
errCheck(err, "Failed to obtain exit hash tree root")
sig, err := e2types.BLSSignatureFromBytes(data.Exit.Signature[:])
errCheck(err, "Invalid signature")
verified, err := verifyStruct(account, exit, domain, sig)
verified, err := util.VerifyRoot(account, exitRoot, exitDomain, sig)
errCheck(err, "Failed to verify voluntary exit")
assert(verified, "Voluntary exit failed to verify")
@@ -130,12 +134,12 @@ func exitVerifyAccount(ctx context.Context) (e2wtypes.Account, error) {
func init() {
exitCmd.AddCommand(exitVerifyCmd)
exitFlags(exitVerifyCmd)
exitVerifyCmd.Flags().String("data", "", "JSON data, or path to JSON data")
exitVerifyCmd.Flags().String("exit", "", "JSON data, or path to JSON data")
exitVerifyCmd.Flags().StringVar(&exitVerifyPubKey, "pubkey", "", "Public key for which to verify exit")
}
func exitVerifyBindings() {
if err := viper.BindPFlag("data", exitVerifyCmd.Flags().Lookup("data")); err != nil {
if err := viper.BindPFlag("exit", exitVerifyCmd.Flags().Lookup("exit")); err != nil {
panic(err)
}
}

View File

@@ -25,7 +25,6 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/wealdtech/ethdo/core"
"github.com/wealdtech/ethdo/util"
e2types "github.com/wealdtech/go-eth2-types/v2"
e2wallet "github.com/wealdtech/go-eth2-wallet"
@@ -77,6 +76,8 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error {
exitVerifyBindings()
case "validator/depositdata":
validatorDepositdataBindings()
case "validator/duties":
validatorDutiesBindings()
case "validator/exit":
validatorExitBindings()
case "validator/info":
@@ -94,7 +95,7 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error {
fmt.Println("Cannot supply both quiet and debug flags")
}
if err := core.SetupStore(); err != nil {
if err := util.SetupStore(); err != nil {
return err
}

View File

@@ -18,8 +18,10 @@ import (
"fmt"
"os"
spec "github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/wealdtech/ethdo/util"
"github.com/wealdtech/go-bytesutil"
e2types "github.com/wealdtech/go-eth2-types/v2"
)
@@ -54,9 +56,11 @@ In quiet mode this will return 0 if the data can be signed, otherwise 1.`,
_, account, err := walletAndAccountFromInput(ctx)
errCheck(err, "Failed to obtain account")
var specDomain spec.Domain
copy(specDomain[:], domain)
var fixedSizeData [32]byte
copy(fixedSizeData[:], data)
signature, err := signRoot(account, fixedSizeData, domain)
signature, err := util.SignRoot(account, fixedSizeData, specDomain)
errCheck(err, "Failed to sign")
outputIf(!quiet, fmt.Sprintf("%#x", signature.Marshal()))

View File

@@ -20,6 +20,7 @@ import (
"os"
"strings"
spec "github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
@@ -64,9 +65,11 @@ In quiet mode this will return 0 if the data can be signed, otherwise 1.`,
errCheck(err, "Failed to obtain account")
outputIf(debug, fmt.Sprintf("Public key is %#x", account.PublicKey().Marshal()))
var specDomain spec.Domain
copy(specDomain[:], domain)
var root [32]byte
copy(root[:], data)
verified, err := verifyRoot(account, root, domain, signature)
verified, err := util.VerifyRoot(account, root, specDomain, signature)
errCheck(err, "Failed to verify data")
assert(verified, "Failed to verify")

View File

@@ -21,7 +21,6 @@ import (
spec "github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/pkg/errors"
"github.com/spf13/viper"
"github.com/wealdtech/ethdo/core"
ethdoutil "github.com/wealdtech/ethdo/util"
e2types "github.com/wealdtech/go-eth2-types/v2"
util "github.com/wealdtech/go-eth2-util"
@@ -52,7 +51,7 @@ func input() (*dataIn, error) {
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
_, data.validatorAccounts, err = core.WalletAndAccountsFromPath(ctx, viper.GetString("validatoraccount"))
_, data.validatorAccounts, err = ethdoutil.WalletAndAccountsFromPath(ctx, viper.GetString("validatoraccount"))
if err != nil {
return nil, errors.New("failed to obtain validator account")
}
@@ -75,11 +74,11 @@ func input() (*dataIn, error) {
case viper.GetString("withdrawalaccount") != "":
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
_, withdrawalAccount, err := core.WalletAndAccountFromPath(ctx, viper.GetString("withdrawalaccount"))
_, withdrawalAccount, err := ethdoutil.WalletAndAccountFromPath(ctx, viper.GetString("withdrawalaccount"))
if err != nil {
return nil, errors.Wrap(err, "failed to obtain withdrawal account")
}
pubKey, err := core.BestPublicKey(withdrawalAccount)
pubKey, err := ethdoutil.BestPublicKey(withdrawalAccount)
if err != nil {
return nil, errors.Wrap(err, "failed to obtain public key for withdrawal account")
}

View File

@@ -105,6 +105,12 @@ func validatorDepositDataOutputRaw(datum *dataOut) (string, error) {
}
func validatorDepositDataOutputLaunchpad(datum *dataOut) (string, error) {
// Map of fork version to network name.
forkVersionMap := map[spec.Version]string{
[4]byte{0x00, 0x00, 0x00, 0x00}: "mainnet",
[4]byte{0x00, 0x00, 0x20, 0x09}: "pyrmont",
}
if datum.validatorPubKey == nil {
return "", errors.New("validator public key required")
}
@@ -124,7 +130,11 @@ func validatorDepositDataOutputLaunchpad(datum *dataOut) (string, error) {
return "", errors.New("deposit data root required")
}
output := fmt.Sprintf(`{"pubkey":"%x","withdrawal_credentials":"%x","amount":%d,"signature":"%x","deposit_message_root":"%x","deposit_data_root":"%x","fork_version":"%x"}`,
networkName := "unknown"
if network, exists := forkVersionMap[*datum.forkVersion]; exists {
networkName = network
}
output := fmt.Sprintf(`{"pubkey":"%x","withdrawal_credentials":"%x","amount":%d,"signature":"%x","deposit_message_root":"%x","deposit_data_root":"%x","fork_version":"%x","eth2_network_name":"%s","deposit_cli_version":"1.1.0"}`,
*datum.validatorPubKey,
datum.withdrawalCredentials,
datum.amount,
@@ -132,6 +142,7 @@ func validatorDepositDataOutputLaunchpad(datum *dataOut) (string, error) {
*datum.depositMessageRoot,
*datum.depositDataRoot,
*datum.forkVersion,
networkName,
)
return output, nil
}

View File

@@ -255,7 +255,7 @@ func TestOutputLaunchpad(t *testing.T) {
}
var forkVersion *spec.Version
{
tmp := testutil.HexToVersion("0x01020304")
tmp := testutil.HexToVersion("0x00002009")
forkVersion = &tmp
}
var depositDataRoot *spec.Root
@@ -418,7 +418,7 @@ func TestOutputLaunchpad(t *testing.T) {
depositMessageRoot: depositMessageRoot,
},
},
res: `[{"pubkey":"a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c","withdrawal_credentials":"00fad2a6bfb0e7f1f0f45460944fbd8dfa7f37da06a4d13b3983cc90bb46963b","amount":32000000000,"signature":"b7a757a4c506ac6ac5f2d23e065de7d00dc9f5a6a3f9610a8b60b65f166379139ae382c91ecbbf5c9fabc34b1cd2cf8f0211488d50d8754716d8e72e17c1a00b5d9b37cc73767946790ebe66cf9669abfc5c25c67e1e2d1c2e11429d149c25a2","deposit_message_root":"139b510ea7f2788ab82da1f427d6cbe1db147c15a053db738ad5500cd83754a6","deposit_data_root":"9e51b386f4271c18149dd0f73297a26a4a8c15c3622c44af79c92446f44a3554","fork_version":"01020304"}]`,
res: `[{"pubkey":"a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c","withdrawal_credentials":"00fad2a6bfb0e7f1f0f45460944fbd8dfa7f37da06a4d13b3983cc90bb46963b","amount":32000000000,"signature":"b7a757a4c506ac6ac5f2d23e065de7d00dc9f5a6a3f9610a8b60b65f166379139ae382c91ecbbf5c9fabc34b1cd2cf8f0211488d50d8754716d8e72e17c1a00b5d9b37cc73767946790ebe66cf9669abfc5c25c67e1e2d1c2e11429d149c25a2","deposit_message_root":"139b510ea7f2788ab82da1f427d6cbe1db147c15a053db738ad5500cd83754a6","deposit_data_root":"9e51b386f4271c18149dd0f73297a26a4a8c15c3622c44af79c92446f44a3554","fork_version":"00002009","eth2_network_name":"pyrmont","deposit_cli_version":"1.1.0"}]`,
},
{
name: "Double",
@@ -446,7 +446,7 @@ func TestOutputLaunchpad(t *testing.T) {
depositMessageRoot: depositMessageRoot2,
},
},
res: `[{"pubkey":"a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c","withdrawal_credentials":"00fad2a6bfb0e7f1f0f45460944fbd8dfa7f37da06a4d13b3983cc90bb46963b","amount":32000000000,"signature":"b7a757a4c506ac6ac5f2d23e065de7d00dc9f5a6a3f9610a8b60b65f166379139ae382c91ecbbf5c9fabc34b1cd2cf8f0211488d50d8754716d8e72e17c1a00b5d9b37cc73767946790ebe66cf9669abfc5c25c67e1e2d1c2e11429d149c25a2","deposit_message_root":"139b510ea7f2788ab82da1f427d6cbe1db147c15a053db738ad5500cd83754a6","deposit_data_root":"9e51b386f4271c18149dd0f73297a26a4a8c15c3622c44af79c92446f44a3554","fork_version":"01020304"},{"pubkey":"b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b","withdrawal_credentials":"00ec7ef7780c9d151597924036262dd28dc60e1228f4da6fecf9d402cb3f3594","amount":32000000000,"signature":"911fe0766e8b79d711dde46bc2142eb51e35be99e5f7da505af9eaad85707bbb8013f0dea35e30403b3e57bb13054c1d0d389aceeba1d4160a148026212c7e017044e3ea69cd96fbd23b6aa9fd1e6f7e82494fbd5f8fc75856711a6b8998926e","deposit_message_root":"bb4b6184b25873cdf430df3838c8d3e3d16cf3dc3b214e2f3ab7df9e6d5a9b52","deposit_data_root":"3b51670e9f266d44c879682a230d60f0d534c64ab25ee68700fe3adb17ddfcab","fork_version":"01020304"}]`,
res: `[{"pubkey":"a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c","withdrawal_credentials":"00fad2a6bfb0e7f1f0f45460944fbd8dfa7f37da06a4d13b3983cc90bb46963b","amount":32000000000,"signature":"b7a757a4c506ac6ac5f2d23e065de7d00dc9f5a6a3f9610a8b60b65f166379139ae382c91ecbbf5c9fabc34b1cd2cf8f0211488d50d8754716d8e72e17c1a00b5d9b37cc73767946790ebe66cf9669abfc5c25c67e1e2d1c2e11429d149c25a2","deposit_message_root":"139b510ea7f2788ab82da1f427d6cbe1db147c15a053db738ad5500cd83754a6","deposit_data_root":"9e51b386f4271c18149dd0f73297a26a4a8c15c3622c44af79c92446f44a3554","fork_version":"00002009","eth2_network_name":"pyrmont","deposit_cli_version":"1.1.0"},{"pubkey":"b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b","withdrawal_credentials":"00ec7ef7780c9d151597924036262dd28dc60e1228f4da6fecf9d402cb3f3594","amount":32000000000,"signature":"911fe0766e8b79d711dde46bc2142eb51e35be99e5f7da505af9eaad85707bbb8013f0dea35e30403b3e57bb13054c1d0d389aceeba1d4160a148026212c7e017044e3ea69cd96fbd23b6aa9fd1e6f7e82494fbd5f8fc75856711a6b8998926e","deposit_message_root":"bb4b6184b25873cdf430df3838c8d3e3d16cf3dc3b214e2f3ab7df9e6d5a9b52","deposit_data_root":"3b51670e9f266d44c879682a230d60f0d534c64ab25ee68700fe3adb17ddfcab","fork_version":"00002009","eth2_network_name":"pyrmont","deposit_cli_version":"1.1.0"}]`,
},
}

View File

@@ -19,8 +19,8 @@ import (
spec "github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/pkg/errors"
"github.com/wealdtech/ethdo/core"
"github.com/wealdtech/ethdo/signing"
"github.com/wealdtech/ethdo/util"
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
)
@@ -32,7 +32,7 @@ func process(data *dataIn) ([]*dataOut, error) {
results := make([]*dataOut, 0)
for _, validatorAccount := range data.validatorAccounts {
validatorPubKey, err := core.BestPublicKey(validatorAccount)
validatorPubKey, err := util.BestPublicKey(validatorAccount)
if err != nil {
return nil, errors.Wrap(err, "validator account does not provide a public key")
}

View File

@@ -0,0 +1,71 @@
// 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 validatorduties
import (
"context"
"time"
"github.com/pkg/errors"
"github.com/spf13/viper"
)
type dataIn struct {
// System.
timeout time.Duration
quiet bool
verbose bool
debug bool
// Ethereum 2 connection.
eth2Client string
allowInsecure bool
// Operation.
account string
pubKey string
index string
}
func input(ctx context.Context) (*dataIn, error) {
data := &dataIn{}
if viper.GetDuration("timeout") == 0 {
return nil, errors.New("timeout is required")
}
data.timeout = viper.GetDuration("timeout")
data.quiet = viper.GetBool("quiet")
data.verbose = viper.GetBool("verbose")
data.debug = viper.GetBool("debug")
// Ethereum 2 connection.
data.eth2Client = viper.GetString("connection")
if data.eth2Client == "" {
return nil, errors.New("connection is required")
}
data.allowInsecure = viper.GetBool("allow-insecure-connections")
// Account.
data.account = viper.GetString("account")
// PubKey.
data.pubKey = viper.GetString("pubkey")
// ID.
data.index = viper.GetString("index")
if data.account == "" && data.pubKey == "" && data.index == "" {
return nil, errors.New("account, pubkey or index required")
}
return data, nil
}

View File

@@ -0,0 +1,100 @@
// 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 validatorduties
import (
"context"
"os"
"testing"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
"github.com/wealdtech/ethdo/testutil"
e2types "github.com/wealdtech/go-eth2-types/v2"
e2wallet "github.com/wealdtech/go-eth2-wallet"
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
nd "github.com/wealdtech/go-eth2-wallet-nd/v2"
scratch "github.com/wealdtech/go-eth2-wallet-store-scratch"
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
)
func TestInput(t *testing.T) {
if os.Getenv("ETHDO_TEST_CONNECTION") == "" {
t.Skip("ETHDO_TEST_CONNECTION not configured; cannot run tests")
}
require.NoError(t, e2types.InitBLS())
store := scratch.New()
require.NoError(t, e2wallet.UseStore(store))
testWallet, err := nd.CreateWallet(context.Background(), "Test wallet", store, keystorev4.New())
require.NoError(t, err)
require.NoError(t, testWallet.(e2wtypes.WalletLocker).Unlock(context.Background(), nil))
viper.Set("passphrase", "pass")
_, err = testWallet.(e2wtypes.WalletAccountImporter).ImportAccount(context.Background(),
"Interop 0",
testutil.HexToBytes("0x25295f0d1d592a90b333e26e85149708208e9f8e8bc18f6c77bd62f8ad7a6866"),
[]byte("pass"),
)
require.NoError(t, err)
tests := []struct {
name string
vars map[string]interface{}
res *dataIn
err string
}{
{
name: "TimeoutMissing",
vars: map[string]interface{}{
"connection": "http://locahost:4000",
"pubkey": "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c",
},
err: "timeout is required",
},
{
name: "AccountMissing",
vars: map[string]interface{}{
"timeout": "5s",
"connection": "http://locahost:4000",
},
err: "account, pubkey or index required",
},
{
name: "ConnectionMissing",
vars: map[string]interface{}{
"timeout": "5s",
"pubkey": "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c",
},
err: "connection is required",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
viper.Reset()
for k, v := range test.vars {
viper.Set(k, v)
}
res, err := input(context.Background())
if test.err != "" {
require.EqualError(t, err, test.err)
} else {
require.NoError(t, err)
require.Equal(t, test.res.timeout, res.timeout)
}
})
}
}

View File

@@ -0,0 +1,112 @@
// 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 validatorduties
import (
"context"
"fmt"
"strings"
"time"
api "github.com/attestantio/go-eth2-client/api/v1"
"github.com/pkg/errors"
)
type dataOut struct {
debug bool
quiet bool
verbose bool
genesisTime time.Time
slotDuration time.Duration
slotsPerEpoch uint64
thisEpochAttesterDuty *api.AttesterDuty
thisEpochProposerDuties []*api.ProposerDuty
nextEpochAttesterDuty *api.AttesterDuty
}
func output(ctx context.Context, data *dataOut) (string, error) {
if data == nil {
return "", errors.New("no data")
}
if data.quiet {
return "", nil
}
builder := strings.Builder{}
now := time.Now()
builder.WriteString("Current time: ")
builder.WriteString(now.Format("15:04:05\n"))
if data.thisEpochAttesterDuty != nil {
thisEpochAttesterSlot := data.thisEpochAttesterDuty.Slot
thisSlotStart := data.genesisTime.Add(time.Duration(thisEpochAttesterSlot) * data.slotDuration)
thisSlotEnd := thisSlotStart.Add(data.slotDuration)
if thisSlotEnd.After(now) {
builder.WriteString("Upcoming attestation slot this epoch: ")
builder.WriteString(thisSlotStart.Format("15:04:05"))
builder.WriteString(" - ")
builder.WriteString(thisSlotEnd.Format("15:04:05 ("))
until := thisSlotStart.Sub(now)
if until > 0 {
builder.WriteString(fmt.Sprintf("%ds until start of slot)\n", int(until.Seconds())))
} else {
builder.WriteString("\n")
}
}
}
for _, proposerDuty := range data.thisEpochProposerDuties {
proposerSlot := proposerDuty.Slot
proposerSlotStart := data.genesisTime.Add(time.Duration(proposerSlot) * data.slotDuration)
proposerSlotEnd := proposerSlotStart.Add(data.slotDuration)
builder.WriteString("Upcoming proposer slot this epoch: ")
builder.WriteString(proposerSlotStart.Format("15:04:05"))
builder.WriteString(" - ")
builder.WriteString(proposerSlotEnd.Format("15:04:05 ("))
until := proposerSlotStart.Sub(now)
if until > 0 {
builder.WriteString(fmt.Sprintf("%ds until start of slot)\n", int(until.Seconds())))
} else {
builder.WriteString("\n")
}
}
if data.nextEpochAttesterDuty != nil {
nextEpochAttesterSlot := data.nextEpochAttesterDuty.Slot
nextSlotStart := data.genesisTime.Add(time.Duration(nextEpochAttesterSlot) * data.slotDuration)
nextSlotEnd := nextSlotStart.Add(data.slotDuration)
builder.WriteString("Upcoming attestation slot next epoch: ")
builder.WriteString(nextSlotStart.Format("15:04:05"))
builder.WriteString(" - ")
builder.WriteString(nextSlotEnd.Format("15:04:05 ("))
until := nextSlotStart.Sub(now)
builder.WriteString(fmt.Sprintf("%ds until start of slot)\n", int(until.Seconds())))
nextEpoch := uint64(data.nextEpochAttesterDuty.Slot) / data.slotsPerEpoch
nextEpochStart := data.genesisTime.Add(time.Duration(nextEpoch*data.slotsPerEpoch) * data.slotDuration)
builder.WriteString("Next epoch starts ")
builder.WriteString(nextEpochStart.Format("15:04:05 ("))
until = nextEpochStart.Sub(now)
if until > 0 {
builder.WriteString(fmt.Sprintf("%ds until start of epoch)\n", int(until.Seconds())))
} else {
builder.WriteString("\n")
}
}
return builder.String(), nil
}

View File

@@ -0,0 +1,83 @@
// 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 validatorduties
import (
"context"
"strings"
"testing"
"time"
api "github.com/attestantio/go-eth2-client/api/v1"
spec "github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/stretchr/testify/require"
)
func TestOutput(t *testing.T) {
tests := []struct {
name string
dataOut *dataOut
expected []string
err string
}{
{
name: "Nil",
err: "no data",
},
{
name: "Empty",
dataOut: &dataOut{},
expected: []string{"Current time"},
},
{
name: "Found",
dataOut: &dataOut{
genesisTime: time.Unix(16000000000, 0),
slotDuration: 12 * time.Second,
slotsPerEpoch: 32,
thisEpochAttesterDuty: &api.AttesterDuty{
Slot: spec.Slot(1),
},
thisEpochProposerDuties: []*api.ProposerDuty{
{
Slot: spec.Slot(2),
},
},
nextEpochAttesterDuty: &api.AttesterDuty{
Slot: spec.Slot(40),
},
},
expected: []string{
"Current time",
"Upcoming attestation slot this epoch",
"Upcoming proposer slot this epoch",
"Upcoming attestation slot next epoch",
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
res, err := output(context.Background(), test.dataOut)
if test.err != "" {
require.EqualError(t, err, test.err)
} else {
require.NoError(t, err)
for _, expected := range test.expected {
require.True(t, strings.Contains(res, expected))
}
}
})
}
}

View File

@@ -0,0 +1,182 @@
// 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 validatorduties
import (
"context"
"encoding/hex"
"fmt"
"strconv"
"strings"
"time"
eth2client "github.com/attestantio/go-eth2-client"
api "github.com/attestantio/go-eth2-client/api/v1"
spec "github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/pkg/errors"
"github.com/wealdtech/ethdo/util"
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
)
func process(ctx context.Context, data *dataIn) (*dataOut, error) {
if data == nil {
return nil, errors.New("no data")
}
// Ethereum 2 client.
eth2Client, err := util.ConnectToBeaconNode(ctx, data.eth2Client, data.timeout, data.allowInsecure)
if err != nil {
return nil, err
}
results := &dataOut{
debug: data.debug,
quiet: data.quiet,
verbose: data.verbose,
}
validatorIndex, err := validatorIndex(ctx, eth2Client, data)
if err != nil {
return nil, errors.Wrap(err, "failed to obtain validator index")
}
// Fetch duties for this and next epoch.
thisEpoch, err := currentEpoch(ctx, eth2Client)
if err != nil {
return nil, errors.Wrap(err, "failed to calculate current epoch")
}
thisEpochAttesterDuty, err := attesterDuty(ctx, eth2Client, validatorIndex, thisEpoch)
if err != nil {
return nil, errors.Wrap(err, "failed to obtain this epoch duty for validator")
}
results.thisEpochAttesterDuty = thisEpochAttesterDuty
thisEpochProposerDuties, err := proposerDuties(ctx, eth2Client, validatorIndex, thisEpoch)
results.thisEpochProposerDuties = thisEpochProposerDuties
nextEpoch := thisEpoch + 1
nextEpochAttesterDuty, err := attesterDuty(ctx, eth2Client, validatorIndex, nextEpoch)
if err != nil {
return nil, errors.Wrap(err, "failed to obtain next epoch duty for validator")
}
results.nextEpochAttesterDuty = nextEpochAttesterDuty
genesis, err := eth2Client.(eth2client.GenesisProvider).Genesis(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to obtain genesis data")
}
results.genesisTime = genesis.GenesisTime
config, err := eth2Client.(eth2client.SpecProvider).Spec(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to obtain beacon chain configuration")
}
results.slotsPerEpoch = config["SLOTS_PER_EPOCH"].(uint64)
results.slotDuration = config["SECONDS_PER_SLOT"].(time.Duration)
return results, nil
}
func attesterDuty(ctx context.Context, eth2Client eth2client.Service, validatorIndex spec.ValidatorIndex, epoch spec.Epoch) (*api.AttesterDuty, error) {
// Find the attesting slot for the given epoch.
duties, err := eth2Client.(eth2client.AttesterDutiesProvider).AttesterDuties(ctx, epoch, []spec.ValidatorIndex{validatorIndex})
if err != nil {
return nil, errors.Wrap(err, "failed to obtain attester duties")
}
if len(duties) == 0 {
return nil, errors.New("validator does not have duty for that epoch")
}
return duties[0], nil
}
func proposerDuties(ctx context.Context, eth2Client eth2client.Service, validatorIndex spec.ValidatorIndex, epoch spec.Epoch) ([]*api.ProposerDuty, error) {
// Fetch the proposer duties for this epoch.
proposerDuties, err := eth2Client.(eth2client.ProposerDutiesProvider).ProposerDuties(ctx, epoch, []spec.ValidatorIndex{validatorIndex})
if err != nil {
return nil, errors.Wrap(err, "failed to obtain proposer duties")
}
return proposerDuties, nil
}
func currentEpoch(ctx context.Context, eth2Client eth2client.Service) (spec.Epoch, error) {
config, err := eth2Client.(eth2client.SpecProvider).Spec(ctx)
if err != nil {
return 0, errors.Wrap(err, "failed to obtain beacon chain configuration")
}
slotsPerEpoch := config["SLOTS_PER_EPOCH"].(uint64)
slotDuration := config["SECONDS_PER_SLOT"].(time.Duration)
genesis, err := eth2Client.(eth2client.GenesisProvider).Genesis(ctx)
if err != nil {
return 0, errors.Wrap(err, "failed to obtain genesis data")
}
if genesis.GenesisTime.After(time.Now()) {
return spec.Epoch(0), nil
}
return spec.Epoch(uint64(time.Since(genesis.GenesisTime).Seconds()) / (uint64(slotDuration.Seconds()) * slotsPerEpoch)), nil
}
// validatorIndex obtains the index of a validator
func validatorIndex(ctx context.Context, eth2Client eth2client.Service, data *dataIn) (spec.ValidatorIndex, error) {
switch {
case data.account != "":
ctx, cancel := context.WithTimeout(context.Background(), data.timeout)
defer cancel()
_, account, err := util.WalletAndAccountFromPath(ctx, data.account)
if err != nil {
return 0, errors.Wrap(err, "failed to obtain account")
}
return accountToIndex(ctx, account, eth2Client)
case data.pubKey != "":
pubKeyBytes, err := hex.DecodeString(strings.TrimPrefix(data.pubKey, "0x"))
if err != nil {
return 0, errors.Wrap(err, fmt.Sprintf("failed to decode public key %s", data.pubKey))
}
account, err := util.NewScratchAccount(nil, pubKeyBytes)
if err != nil {
return 0, errors.Wrap(err, fmt.Sprintf("invalid public key %s", data.pubKey))
}
return accountToIndex(ctx, account, eth2Client)
case data.index != "":
val, err := strconv.ParseUint(data.index, 10, 64)
if err != nil {
return 0, err
}
return spec.ValidatorIndex(val), nil
default:
return 0, errors.New("no validator")
}
}
func accountToIndex(ctx context.Context, account e2wtypes.Account, eth2Client eth2client.Service) (spec.ValidatorIndex, error) {
pubKey, err := util.BestPublicKey(account)
if err != nil {
return 0, err
}
pubKeys := make([]spec.BLSPubKey, 1)
copy(pubKeys[0][:], pubKey.Marshal())
validators, err := eth2Client.(eth2client.ValidatorsProvider).ValidatorsByPubKey(ctx, "head", pubKeys)
if err != nil {
return 0, err
}
for index := range validators {
return index, nil
}
return 0, errors.New("validator not found")
}

View File

@@ -0,0 +1,60 @@
// 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 validatorduties
import (
"context"
"os"
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestProcess(t *testing.T) {
if os.Getenv("ETHDO_TEST_CONNECTION") == "" {
t.Skip("ETHDO_TEST_CONNECTION not configured; cannot run tests")
}
tests := []struct {
name string
dataIn *dataIn
err string
}{
{
name: "Nil",
err: "no data",
},
{
name: "Client",
dataIn: &dataIn{
timeout: 5 * time.Second,
eth2Client: os.Getenv("ETHDO_TEST_CONNECTION"),
allowInsecure: true,
index: "1",
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
_, err := process(context.Background(), test.dataIn)
if test.err != "" {
require.EqualError(t, err, test.err)
} else {
require.NoError(t, err)
}
})
}
}

View File

@@ -0,0 +1,50 @@
// 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 validatorduties
import (
"context"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
// Run runs the wallet create data command.
func Run(cmd *cobra.Command) (string, error) {
ctx := context.Background()
dataIn, err := input(ctx)
if err != nil {
return "", errors.Wrap(err, "failed to obtain input")
}
// Further errors do not need a usage report.
cmd.SilenceUsage = true
dataOut, err := process(ctx, dataIn)
if err != nil {
return "", errors.Wrap(err, "failed to process")
}
if viper.GetBool("quiet") {
return "", nil
}
results, err := output(ctx, dataOut)
if err != nil {
return "", errors.Wrap(err, "failed to obtain output")
}
return results, nil
}

View File

@@ -24,7 +24,6 @@ import (
spec "github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/pkg/errors"
"github.com/spf13/viper"
"github.com/wealdtech/ethdo/core"
"github.com/wealdtech/ethdo/util"
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
)
@@ -80,13 +79,13 @@ func inputJSON(ctx context.Context, data *dataIn) (*dataIn, error) {
if err != nil {
return nil, err
}
data.signedVoluntaryExit = validatorData.Data
data.signedVoluntaryExit = validatorData.Exit
return inputChainData(ctx, data)
}
func inputAccount(ctx context.Context, data *dataIn) (*dataIn, error) {
var err error
_, data.account, err = core.WalletAndAccountFromInput(ctx)
_, data.account, err = util.WalletAndAccountFromInput(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to obtain acount")
}

View File

@@ -91,9 +91,10 @@ func TestInput(t *testing.T) {
{
name: "KeyGood",
vars: map[string]interface{}{
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
"timeout": "5s",
"key": "0x25295f0d1d592a90b333e26e85149708208e9f8e8bc18f6c77bd62f8ad7a6866",
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
"allow-insecure-connections": true,
"timeout": "5s",
"key": "0x25295f0d1d592a90b333e26e85149708208e9f8e8bc18f6c77bd62f8ad7a6866",
},
res: &dataIn{
timeout: 5 * time.Second,
@@ -102,9 +103,10 @@ func TestInput(t *testing.T) {
{
name: "AccountUnknown",
vars: map[string]interface{}{
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
"timeout": "5s",
"account": "Test wallet/unknown",
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
"allow-insecure-connections": true,
"timeout": "5s",
"account": "Test wallet/unknown",
},
res: &dataIn{
timeout: 5 * time.Second,
@@ -114,9 +116,10 @@ func TestInput(t *testing.T) {
{
name: "AccountGood",
vars: map[string]interface{}{
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
"timeout": "5s",
"account": "Test wallet/Interop 0",
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
"allow-insecure-connections": true,
"timeout": "5s",
"account": "Test wallet/Interop 0",
},
res: &dataIn{
timeout: 5 * time.Second,
@@ -125,9 +128,10 @@ func TestInput(t *testing.T) {
{
name: "JSONInvalid",
vars: map[string]interface{}{
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
"timeout": "5s",
"exit": `invalid`,
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
"allow-insecure-connections": true,
"timeout": "5s",
"exit": `invalid`,
},
res: &dataIn{
timeout: 5 * time.Second,
@@ -137,9 +141,10 @@ func TestInput(t *testing.T) {
{
name: "JSONGood",
vars: map[string]interface{}{
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
"timeout": "5s",
"exit": `{"message":{"epoch":"123","validator_index":"456"},"signature":"0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f"}`,
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
"allow-insecure-connections": true,
"timeout": "5s",
"exit": `{"exit":{"message":{"epoch":"123","validator_index":"456"},"signature":"0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f"},"fork_version":"0x00002009"}`,
},
res: &dataIn{
timeout: 5 * time.Second,
@@ -148,19 +153,21 @@ func TestInput(t *testing.T) {
{
name: "ClientBad",
vars: map[string]interface{}{
"connection": "localhost:1",
"timeout": "5s",
"key": "0x25295f0d1d592a90b333e26e85149708208e9f8e8bc18f6c77bd62f8ad7a6866",
"connection": "localhost:1",
"allow-insecure-connections": true,
"timeout": "5s",
"key": "0x25295f0d1d592a90b333e26e85149708208e9f8e8bc18f6c77bd62f8ad7a6866",
},
err: "failed to connect to Ethereum 2 beacon node: failed to connect to beacon node: failed to connect to Ethereum 2 client with any known method",
},
{
name: "EpochProvided",
vars: map[string]interface{}{
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
"timeout": "5s",
"key": "0x25295f0d1d592a90b333e26e85149708208e9f8e8bc18f6c77bd62f8ad7a6866",
"epoch": "123",
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
"allow-insecure-connections": true,
"timeout": "5s",
"key": "0x25295f0d1d592a90b333e26e85149708208e9f8e8bc18f6c77bd62f8ad7a6866",
"epoch": "123",
},
res: &dataIn{
timeout: 5 * time.Second,

View File

@@ -46,7 +46,7 @@ func output(ctx context.Context, data *dataOut) (string, error) {
func outputJSON(ctx context.Context, data *dataOut) (string, error) {
validatorExitData := &util.ValidatorExitData{
Data: data.signedVoluntaryExit,
Exit: data.signedVoluntaryExit,
ForkVersion: data.forkVersion,
}
bytes, err := json.Marshal(validatorExitData)

View File

@@ -79,7 +79,7 @@ func TestOutput(t *testing.T) {
},
},
},
res: `{"data":{"message":{"epoch":"123","validator_index":"456"},"signature":"0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f"},"fork_version":"0x01020304"}`,
res: `{"exit":{"message":{"epoch":"123","validator_index":"456"},"signature":"0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f"},"fork_version":"0x01020304"}`,
},
}

View File

@@ -20,8 +20,8 @@ import (
api "github.com/attestantio/go-eth2-client/api/v1"
spec "github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/pkg/errors"
"github.com/wealdtech/ethdo/core"
"github.com/wealdtech/ethdo/signing"
"github.com/wealdtech/ethdo/util"
)
// maxFutureEpochs is the farthest in the future for which an exit will be created.
@@ -111,7 +111,7 @@ func fetchValidator(ctx context.Context, data *dataIn) (*api.Validator, error) {
var validator *api.Validator
validatorPubKeys := make([]spec.BLSPubKey, 1)
pubKey, err := core.BestPublicKey(data.account)
pubKey, err := util.BestPublicKey(data.account)
if err != nil {
return nil, errors.Wrap(err, "failed to obtain public key for account")
}

61
cmd/validatorduties.go Normal file
View File

@@ -0,0 +1,61 @@
// 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 cmd
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
validatorduties "github.com/wealdtech/ethdo/cmd/validator/duties"
)
var validatorDutiesCmd = &cobra.Command{
Use: "duties",
Short: "List known duties for a validator",
Long: `List known duties for a validator. For example:
ethdo validator duties --account=Validators/One
Attester duties are known for the current and next epoch. Proposer duties are known for the current epoch.
In quiet mode this will return 0 if the the duties have been obtained, otherwise 1.`,
RunE: func(cmd *cobra.Command, args []string) error {
res, err := validatorduties.Run(cmd)
if err != nil {
return err
}
if viper.GetBool("quiet") {
return nil
}
fmt.Printf(res)
return nil
},
}
func init() {
validatorCmd.AddCommand(validatorDutiesCmd)
validatorFlags(validatorDutiesCmd)
validatorDutiesCmd.Flags().String("pubkey", "", "validator public key for duties")
validatorDutiesCmd.Flags().String("index", "", "validator index for duties")
}
func validatorDutiesBindings() {
if err := viper.BindPFlag("pubkey", validatorDutiesCmd.Flags().Lookup("pubkey")); err != nil {
panic(err)
}
if err := viper.BindPFlag("index", validatorDutiesCmd.Flags().Lookup("index")); err != nil {
panic(err)
}
}

View File

@@ -31,7 +31,6 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/wealdtech/ethdo/core"
"github.com/wealdtech/ethdo/util"
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
string2eth "github.com/wealdtech/go-string2eth"
@@ -55,7 +54,7 @@ In quiet mode this will return 0 if the validator information can be obtained, o
errCheck(err, "Failed to obtain validator account")
pubKeys := make([]spec.BLSPubKey, 1)
pubKey, err := core.BestPublicKey(account)
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)

View File

@@ -24,7 +24,7 @@ import (
// ReleaseVersion is the release version of the codebase.
// Usually overrideen by tag names when building binaries.
var ReleaseVersion = "local build (latest release 1.7.2)"
var ReleaseVersion = "local build (latest release 1.7.3)"
// versionCmd represents the version command
var versionCmd = &cobra.Command{

View File

@@ -19,7 +19,7 @@ import (
"github.com/pkg/errors"
"github.com/spf13/viper"
"github.com/wealdtech/ethdo/core"
"github.com/wealdtech/ethdo/util"
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
)
@@ -49,7 +49,7 @@ func input(ctx context.Context) (*dataIn, error) {
data.debug = viper.GetBool("debug")
// Wallet.
wallet, err := core.WalletFromInput(ctx)
wallet, err := util.WalletFromInput(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to access wallet")
}

View File

@@ -19,7 +19,6 @@ import (
"github.com/pkg/errors"
"github.com/spf13/viper"
"github.com/wealdtech/ethdo/core"
"github.com/wealdtech/ethdo/util"
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
)
@@ -51,7 +50,7 @@ func input(ctx context.Context) (*dataIn, error) {
data.debug = viper.GetBool("debug")
// Wallet.
wallet, err := core.WalletFromInput(ctx)
wallet, err := util.WalletFromInput(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to access wallet")
}

View File

@@ -348,12 +348,12 @@ Exit commands focus on information about validator exits generated by the `ethdo
#### `verify`
`ethdo exit verify` verifies the validator exit information in a JSON file generated by the `ethdo validator exit` command. Options include:
- `data`: either a path to the JSON file or the JSON itself
- `exit`: either a path to the JSON file or the JSON itself
- `account`: the account that generated the exit transaction (if available as an account, in format "wallet/account")
- `pubkey`: the public key of the account that generated the exit transaction
```sh
$ ethdo exit verify --data=${HOME}/exit.json --pubkey=0xa951530887ae2494a8cc4f11cf186963b0051ac4f7942375585b9cf98324db1e532a67e521d0fcaab510edad1352394c
$ ethdo exit verify --exit=${HOME}/exit.json --pubkey=0xa951530887ae2494a8cc4f11cf186963b0051ac4f7942375585b9cf98324db1e532a67e521d0fcaab510edad1352394c
```
### `node` commands

20
go.mod
View File

@@ -5,23 +5,25 @@ go 1.13
require (
github.com/OneOfOne/xxhash v1.2.5 // indirect
github.com/attestantio/dirk v0.9.3
github.com/attestantio/go-eth2-client v0.6.10
github.com/ferranbt/fastssz v0.0.0-20201030134205-9b9624098321
github.com/attestantio/go-eth2-client v0.6.16
github.com/aws/aws-sdk-go v1.36.2 // indirect
github.com/ferranbt/fastssz v0.0.0-20201207112544-98a5de30d648
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/gofrs/uuid v3.3.0+incompatible
github.com/gogo/protobuf v1.3.1
github.com/google/uuid v1.1.2
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/herumi/bls-eth-go-binary v0.0.0-20201104034342-d782bdf735de
github.com/jackc/puddle v1.1.3 // indirect
github.com/magiconair/properties v1.8.4 // indirect
github.com/minio/highwayhash v1.0.1 // indirect
github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/mapstructure v1.3.3 // indirect
github.com/mitchellh/mapstructure v1.4.0 // indirect
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d
github.com/pelletier/go-toml v1.8.1 // indirect
github.com/pkg/errors v0.9.1
github.com/protolambda/zssz v0.1.5 // indirect
github.com/prysmaticlabs/ethereumapis v0.0.0-20201020182719-7f66dae2bbba
github.com/prysmaticlabs/ethereumapis v0.0.0-20201207010723-e69ac7fa952d
github.com/prysmaticlabs/go-bitfield v0.0.0-20200618145306-2ae0807bef65
github.com/prysmaticlabs/go-ssz v0.0.0-20200612203617-6d5c9aa213ae
github.com/rs/zerolog v1.20.0
@@ -52,10 +54,12 @@ require (
github.com/wealdtech/go-eth2-wallet-store-scratch v1.6.1
github.com/wealdtech/go-eth2-wallet-types/v2 v2.8.1
github.com/wealdtech/go-string2eth v1.1.0
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b // indirect
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd // indirect
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c // indirect
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb // indirect
golang.org/x/sys v0.0.0-20201204225414-ed752295db88 // indirect
golang.org/x/text v0.3.4
google.golang.org/genproto v0.0.0-20201111145450-ac7456db90a6 // indirect
google.golang.org/grpc v1.33.2
google.golang.org/genproto v0.0.0-20201204160425-06b3db808446 // indirect
google.golang.org/grpc v1.34.0
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

36
go.sum
View File

@@ -75,6 +75,10 @@ github.com/attestantio/go-eth2-client v0.6.9 h1:Hbf4tX9MvxCsLokED8Ic3tQxmEAb/pho
github.com/attestantio/go-eth2-client v0.6.9/go.mod h1:ODAZ4yS1YYYew/EsgGsVb/siNEoa505CrGsvlVFdkfo=
github.com/attestantio/go-eth2-client v0.6.10 h1:PMNBMLk6xfMEUqhaUnsI0/HZRrstZF18Gt6Dm5GelW4=
github.com/attestantio/go-eth2-client v0.6.10/go.mod h1:ODAZ4yS1YYYew/EsgGsVb/siNEoa505CrGsvlVFdkfo=
github.com/attestantio/go-eth2-client v0.6.15 h1:GNkiSF2Dqp6qahMXMW8r8Wy61WEvytnAM+rEyutdfv8=
github.com/attestantio/go-eth2-client v0.6.15/go.mod h1:Hya4fp1ZLWAFI64qMhNbQgfY4StWiHulW4CFwu+vP3s=
github.com/attestantio/go-eth2-client v0.6.16 h1:2Xn5RKqXUXfxLYVHn3D6l0FK7NUCjzl5v4oYIxcxc5k=
github.com/attestantio/go-eth2-client v0.6.16/go.mod h1:Hya4fp1ZLWAFI64qMhNbQgfY4StWiHulW4CFwu+vP3s=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.32.6 h1:HoswAabUWgnrUF7X/9dr4WRgrr8DyscxXvTDm7Qw/5c=
@@ -94,6 +98,8 @@ github.com/aws/aws-sdk-go v1.35.14 h1:nucVVXXjAr9UkmYCBWxQWRuYa5KOlaXjuJGg2ulW0K
github.com/aws/aws-sdk-go v1.35.14/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k=
github.com/aws/aws-sdk-go v1.35.26 h1:MawRvDpAp/Ai859dPC1xo1fdU/BIkijoHj0DwXLXXkI=
github.com/aws/aws-sdk-go v1.35.26/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k=
github.com/aws/aws-sdk-go v1.36.2 h1:UAeFPct+jHqWM+tgiqDrC9/sfbWj6wkcvpsJ+zdcsvA=
github.com/aws/aws-sdk-go v1.36.2/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
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=
@@ -112,6 +118,7 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
@@ -156,6 +163,7 @@ github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4s
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
@@ -172,6 +180,8 @@ github.com/ferranbt/fastssz v0.0.0-20200826142241-3a913c5a1313/go.mod h1:DyEu2iu
github.com/ferranbt/fastssz v0.0.0-20201020132831-68dc48984fd3/go.mod h1:DyEu2iuLBnb/T51BlsiO3yLYdJC6UbGMrIkqK1KmQxM=
github.com/ferranbt/fastssz v0.0.0-20201030134205-9b9624098321 h1:9Pkbf8HgETu3xKpz12Sj5clUrVFp2O+ymK7pBsTPYRM=
github.com/ferranbt/fastssz v0.0.0-20201030134205-9b9624098321/go.mod h1:DyEu2iuLBnb/T51BlsiO3yLYdJC6UbGMrIkqK1KmQxM=
github.com/ferranbt/fastssz v0.0.0-20201207112544-98a5de30d648 h1:TBgYVQ5wP1iSjg53BnlXibpYzmAZJLsZhOcGDtu0FlQ=
github.com/ferranbt/fastssz v0.0.0-20201207112544-98a5de30d648/go.mod h1:DyEu2iuLBnb/T51BlsiO3yLYdJC6UbGMrIkqK1KmQxM=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@@ -187,13 +197,17 @@ github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgO
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/goccy/go-yaml v1.8.3 h1:VGzw2KWSUyQX0yXai02S0nttBc+Oa4Kvh6RCFoxt8SE=
github.com/goccy/go-yaml v1.8.3/go.mod h1:wS4gNoLalDSJxo/SpngzPQ2BN4uuZVLCmbM4S3vd4+Y=
github.com/goccy/go-yaml v1.8.4 h1:AOEdR7aQgbgwHznGe3BLkDQVujxCPUpHOZZcQcp8Y3M=
github.com/goccy/go-yaml v1.8.4/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA=
github.com/gofrs/uuid v1.2.0 h1:coDhrjgyJaglxSjxuJdqQSSdUpG3w6p1OwN2od6frBU=
github.com/gofrs/uuid v3.3.0+incompatible h1:8K4tyRfvU1CYPgJsveYFQMhpFd/wXNM7iK6rR7UHz84=
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
@@ -347,6 +361,8 @@ github.com/jackc/puddle v1.1.1 h1:PJAw7H/9hoWC4Kf3J8iNmL1SwA6E8vfsLqBiL+F6CtI=
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.2 h1:mpQEXihFnWGDy6X98EOTh81JYuxn7txby8ilJ3iIPGM=
github.com/jackc/puddle v1.1.2/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3 h1:JnPg/5Q9xVJGfjsO5CPUOjnJps1JaRUm8I9FXVCFK94=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
@@ -419,6 +435,8 @@ github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYG
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8=
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.0 h1:7ks8ZkOP5/ujthUsT07rNv+nkLXCQWKNHuwzOAesEks=
github.com/mitchellh/mapstructure v1.4.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=
@@ -515,6 +533,8 @@ github.com/prysmaticlabs/ethereumapis v0.0.0-20201003171600-a72e5f77d233 h1:dGeu
github.com/prysmaticlabs/ethereumapis v0.0.0-20201003171600-a72e5f77d233/go.mod h1:k7b2dxy6RppCG6kmOJkNOXzRpEoTdsPygc2aQhsUsZk=
github.com/prysmaticlabs/ethereumapis v0.0.0-20201020182719-7f66dae2bbba h1:ItW6tq3B45Gws8dO0cIuU1Srlgf4qomZnWkc0sDCln0=
github.com/prysmaticlabs/ethereumapis v0.0.0-20201020182719-7f66dae2bbba/go.mod h1:k7b2dxy6RppCG6kmOJkNOXzRpEoTdsPygc2aQhsUsZk=
github.com/prysmaticlabs/ethereumapis v0.0.0-20201207010723-e69ac7fa952d h1:HdarpPepaIp6xIFfH4hG3IU6doct9WupCYQgT97G+4g=
github.com/prysmaticlabs/ethereumapis v0.0.0-20201207010723-e69ac7fa952d/go.mod h1:k7b2dxy6RppCG6kmOJkNOXzRpEoTdsPygc2aQhsUsZk=
github.com/prysmaticlabs/go-bitfield v0.0.0-20191017011753-53b773adde52/go.mod h1:hCwmef+4qXWjv0jLDbQdWnL0Ol7cS7/lCSS26WR+u6s=
github.com/prysmaticlabs/go-bitfield v0.0.0-20200322041314-62c2aee71669/go.mod h1:hCwmef+4qXWjv0jLDbQdWnL0Ol7cS7/lCSS26WR+u6s=
github.com/prysmaticlabs/go-bitfield v0.0.0-20200618145306-2ae0807bef65 h1:hJfAWrlxx7SKpn4S/h2JGl2HHwA1a2wSS3HAzzZ0F+U=
@@ -816,6 +836,8 @@ golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee h1:4yd7jl+vXjalO5ztz6Vc1V
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c h1:9HhBz5L/UjnK9XLtiZhYAdue5BVKep3PMmS2LuPDt8k=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -899,6 +921,8 @@ golang.org/x/net v0.0.0-20201024042810-be3efd7ff127 h1:pZPp9+iYUqwYKLjht0SDBbRCR
golang.org/x/net v0.0.0-20201024042810-be3efd7ff127/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
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=
@@ -939,6 +963,7 @@ golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -981,6 +1006,11 @@ golang.org/x/sys v0.0.0-20201101102859-da207088b7d1 h1:a/mKvvZr9Jcc8oKfcmgzyp7Ow
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY=
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88 h1:KmZPnMocC93w341XZp26yTJg8Za7lhb2KhkYmixoeso=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1136,6 +1166,8 @@ google.golang.org/genproto v0.0.0-20201022181438-0ff5f38871d5 h1:YejJbGvoWsTXHab
google.golang.org/genproto v0.0.0-20201022181438-0ff5f38871d5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201111145450-ac7456db90a6 h1:iRN4+t0lvZX/l9gH14ARF9i58tsVa5a97k6aH95rC3Y=
google.golang.org/genproto v0.0.0-20201111145450-ac7456db90a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201204160425-06b3db808446 h1:65ppmIPdaZE+BO34gntwqexoTYr30IRNGmS0OGOHu3A=
google.golang.org/genproto v0.0.0-20201204160425-06b3db808446/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
@@ -1165,6 +1197,8 @@ google.golang.org/grpc v1.33.1 h1:DGeFlSan2f+WEtCERJ4J9GJWk15TxUi8QGagfI87Xyc=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0 h1:raiipEjMOIC/TO2AvyTxP25XFdLxNIBwzDh3FM3XztI=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1212,6 +1246,8 @@ gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
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-20200605160147-a5ece683394c h1:grhR+C34yXImVGp7EzNk+DTIk+323eIUWOmEevy6bDo=
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -27,6 +27,10 @@ import (
// ConnectToBeaconNode connects to a beacon node at the given address.
func ConnectToBeaconNode(ctx context.Context, address string, timeout time.Duration, allowInsecure bool) (eth2client.Service, error) {
if timeout == 0 {
return nil, errors.New("no timeout specified")
}
if !allowInsecure {
// Ensure the connection is either secure or local.
connectionURL, err := url.Parse(address)

View File

@@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package core
package util
import (
"context"
@@ -23,7 +23,6 @@ import (
"github.com/pkg/errors"
"github.com/spf13/viper"
"github.com/wealdtech/ethdo/util"
e2types "github.com/wealdtech/go-eth2-types/v2"
e2wallet "github.com/wealdtech/go-eth2-wallet"
dirk "github.com/wealdtech/go-eth2-wallet-dirk"
@@ -44,20 +43,20 @@ func SetupStore() error {
// Set up our wallet store.
switch viper.GetString("store") {
case "s3":
if util.GetBaseDir() != "" {
if GetBaseDir() != "" {
return errors.New("basedir does not apply to the s3 store")
}
store, err = s3.New(s3.WithPassphrase([]byte(util.GetStorePassphrase())))
store, err = s3.New(s3.WithPassphrase([]byte(GetStorePassphrase())))
if err != nil {
return errors.Wrap(err, "failed to access Amazon S3 wallet store")
}
case "filesystem":
opts := make([]filesystem.Option, 0)
if util.GetStorePassphrase() != "" {
opts = append(opts, filesystem.WithPassphrase([]byte(util.GetStorePassphrase())))
if GetStorePassphrase() != "" {
opts = append(opts, filesystem.WithPassphrase([]byte(GetStorePassphrase())))
}
if util.GetBaseDir() != "" {
opts = append(opts, filesystem.WithLocation(viper.GetString("base-dir")))
if GetBaseDir() != "" {
opts = append(opts, filesystem.WithLocation(GetBaseDir()))
}
store = filesystem.New(opts...)
default:
@@ -139,13 +138,13 @@ func WalletAndAccountFromPath(ctx context.Context, path string) (e2wtypes.Wallet
}
if wallet.Type() == "hierarchical deterministic" && strings.HasPrefix(accountName, "m/") {
if util.GetWalletPassphrase() == "" {
if GetWalletPassphrase() == "" {
return nil, nil, errors.New("walletpassphrase is required for direct path derivations")
}
locker, isLocker := wallet.(e2wtypes.WalletLocker)
if isLocker {
err = locker.Unlock(ctx, []byte(util.GetWalletPassphrase()))
err = locker.Unlock(ctx, []byte(GetWalletPassphrase()))
if err != nil {
return nil, nil, errors.New("failed to unlock wallet")
}

View File

@@ -11,12 +11,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
package util
import (
"context"
"fmt"
spec "github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-ssz"
"github.com/spf13/viper"
@@ -24,66 +24,54 @@ import (
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
)
// verifyStruct verifies the signature of an arbitrary structure.
func verifyStruct(account e2wtypes.Account, data interface{}, domain []byte, signature e2types.Signature) (bool, error) {
objRoot, err := ssz.HashTreeRoot(data)
outputIf(debug, fmt.Sprintf("Object root is %#x", objRoot))
if err != nil {
return false, err
}
return verifyRoot(account, objRoot, domain, signature)
}
// SigningContainer is the container for signing roots with a domain.
// Contains SSZ sizes to allow for correct calculation of root.
type signingContainer struct {
Root []byte `ssz-size:"32"`
Domain []byte `ssz-size:"32"`
}
// signRoot signs a root.
func signRoot(account e2wtypes.Account, root [32]byte, domain []byte) (e2types.Signature, error) {
// SignRoot signs the hash tree root of a data structure
func SignRoot(account e2wtypes.Account, root spec.Root, domain spec.Domain) (e2types.Signature, error) {
if _, isProtectingSigner := account.(e2wtypes.AccountProtectingSigner); isProtectingSigner {
// Signer signs the data to sign itself.
return signGeneric(account, root[:], domain)
// Signer builds the signing data.
return signGeneric(account, root, domain)
}
// Build the signing data manually.
container := &signingContainer{
Root: root[:],
Domain: domain,
container := &spec.SigningData{
ObjectRoot: root,
Domain: domain,
}
outputIf(debug, fmt.Sprintf("Signing container:\n root: %#x\n domain: %#x", container.Root, container.Domain))
signingRoot, err := ssz.HashTreeRoot(container)
// outputIf(debug, fmt.Sprintf("Signing container:\n root: %#x\n domain: %#x", container.ObjectRoot, container.Domain))
signingRoot, err := container.HashTreeRoot()
if err != nil {
return nil, err
}
outputIf(debug, fmt.Sprintf("Signing root: %#x", signingRoot))
// outputIf(debug, fmt.Sprintf("Signing root: %#x", signingRoot))
return sign(account, signingRoot[:])
}
func verifyRoot(account e2wtypes.Account, root [32]byte, domain []byte, signature e2types.Signature) (bool, error) {
// VerifyRoot verifies the hash tree root of a data structure.
func VerifyRoot(account e2wtypes.Account, root spec.Root, domain spec.Domain, signature e2types.Signature) (bool, error) {
// Build the signing data manually.
container := &signingContainer{
Root: root[:],
Domain: domain,
container := &spec.SigningData{
ObjectRoot: root,
Domain: domain,
}
outputIf(debug, fmt.Sprintf("Signing container:\n root: %#x\n domain: %#x", container.Root, container.Domain))
// outputIf(debug, fmt.Sprintf("Signing container:\n root: %#x\n domain: %#x", container.ObjectRoot, container.Domain))
signingRoot, err := ssz.HashTreeRoot(container)
if err != nil {
return false, err
}
outputIf(debug, fmt.Sprintf("Signing root: %#x", signingRoot))
return verify(account, signingRoot[:], signature)
// outputIf(debug, fmt.Sprintf("Signing root: %#x", signingRoot))
pubKey, err := BestPublicKey(account)
if err != nil {
return false, errors.Wrap(err, "failed to obtain account public key")
}
return signature.Verify(signingRoot[:], pubKey), nil
}
func signGeneric(account e2wtypes.Account, data []byte, domain []byte) (e2types.Signature, error) {
// signGeneric signs generic data.
func signGeneric(account e2wtypes.Account, data spec.Root, domain spec.Domain) (e2types.Signature, error) {
alreadyUnlocked, err := unlock(account)
if err != nil {
return nil, err
}
outputIf(debug, fmt.Sprintf("Signing %x (%d)", data, len(data)))
// outputIf(debug, fmt.Sprintf("Signing %x (%d)", data, len(data)))
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
@@ -92,8 +80,8 @@ func signGeneric(account e2wtypes.Account, data []byte, domain []byte) (e2types.
return nil, errors.New("account does not provide generic signing")
}
signature, err := signer.SignGeneric(ctx, data, domain)
errCheck(err, "failed to sign")
signature, err := signer.SignGeneric(ctx, data[:], domain[:])
// errCheck(err, "failed to sign")
if !alreadyUnlocked {
if err := lock(account); err != nil {
return nil, errors.Wrap(err, "failed to lock account")
@@ -108,7 +96,7 @@ func sign(account e2wtypes.Account, data []byte) (e2types.Signature, error) {
if err != nil {
return nil, err
}
outputIf(debug, fmt.Sprintf("Signing %x (%d)", data, len(data)))
// outputIf(debug, fmt.Sprintf("Signing %x (%d)", data, len(data)))
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
@@ -118,7 +106,7 @@ func sign(account e2wtypes.Account, data []byte) (e2types.Signature, error) {
}
signature, err := signer.Sign(ctx, data)
errCheck(err, "failed to sign")
// errCheck(err, "failed to sign")
if !alreadyUnlocked {
if err := lock(account); err != nil {
return nil, errors.Wrap(err, "failed to lock account")
@@ -127,20 +115,11 @@ func sign(account e2wtypes.Account, data []byte) (e2types.Signature, error) {
return signature, err
}
// verify the signature of arbitrary data.
func verify(account e2wtypes.Account, data []byte, signature e2types.Signature) (bool, error) {
pubKey, err := bestPublicKey(account)
if err != nil {
return false, errors.Wrap(err, "failed to obtain account public key")
}
return signature.Verify(data, pubKey), nil
}
// unlock attempts to unlock an account. It returns true if the account was already unlocked.
func unlock(account e2wtypes.Account) (bool, error) {
locker, isAccountLocker := account.(e2wtypes.AccountLocker)
if !isAccountLocker {
outputIf(debug, "Account does not support unlocking")
// outputIf(debug, "Account does not support unlocking")
// This account doesn't support unlocking; return okay.
return true, nil
}
@@ -157,7 +136,7 @@ func unlock(account e2wtypes.Account) (bool, error) {
}
// Not already unlocked; attempt to unlock it.
for _, passphrase := range getPassphrases() {
for _, passphrase := range GetPassphrases() {
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
err = locker.Unlock(ctx, []byte(passphrase))
cancel()

View File

@@ -25,19 +25,19 @@ import (
// ValidatorExitData contains data for a validator exit.
type ValidatorExitData struct {
Data *spec.SignedVoluntaryExit
Exit *spec.SignedVoluntaryExit
ForkVersion spec.Version
}
type validatorExitJSON struct {
Data *spec.SignedVoluntaryExit `json:"data"`
Exit *spec.SignedVoluntaryExit `json:"exit"`
ForkVersion string `json:"fork_version"`
}
// MarshalJSON implements custom JSON marshaller.
func (d *ValidatorExitData) MarshalJSON() ([]byte, error) {
validatorExitJSON := &validatorExitJSON{
Data: d.Data,
Exit: d.Exit,
ForkVersion: fmt.Sprintf("%#x", d.ForkVersion),
}
return json.Marshal(validatorExitJSON)
@@ -51,10 +51,10 @@ func (d *ValidatorExitData) UnmarshalJSON(data []byte) error {
return errors.Wrap(err, "failed to unmarshal JSON")
}
if validatorExitJSON.Data == nil {
return errors.New("data missing")
if validatorExitJSON.Exit == nil {
return errors.New("exit missing")
}
d.Data = validatorExitJSON.Data
d.Exit = validatorExitJSON.Exit
if validatorExitJSON.ForkVersion == "" {
return errors.New("fork version missing")

View File

@@ -38,28 +38,28 @@ func TestUnmarshal(t *testing.T) {
err: "invalid character 'i' looking for beginning of value",
},
{
name: "DataMissing",
name: "ExitMissing",
in: []byte(`{"fork_version":"0x00000001"}`),
err: "data missing",
err: "exit missing",
},
{
name: "DataInvalid",
in: []byte(`{"data":{},"fork_version":"0x00000001"}`),
name: "ExitInvalid",
in: []byte(`{"exit":{},"fork_version":"0x00000001"}`),
err: "failed to unmarshal JSON: message missing",
},
{
name: "ForkVersionMissing",
in: []byte(`{"data":{"message":{"epoch":"0","validator_index":"0"},"signature":"0xb74eade64ebf1e02cc57e5d29517032c6ca99132fb8e7fb7e6d58c68713e581ef0ef88e2a6c599a007d997782abdd50b0f9763500a93a971c89cb2275583fe755d7c0e64f459ff22fcef5cab3f80848f0356e67c142b9cf3ee65613f56283d6e"}}`),
in: []byte(`{"exit":{"message":{"epoch":"0","validator_index":"0"},"signature":"0xb74eade64ebf1e02cc57e5d29517032c6ca99132fb8e7fb7e6d58c68713e581ef0ef88e2a6c599a007d997782abdd50b0f9763500a93a971c89cb2275583fe755d7c0e64f459ff22fcef5cab3f80848f0356e67c142b9cf3ee65613f56283d6e"}}`),
err: "fork version missing",
},
{
name: "ForkVersionInvalid",
in: []byte(`{"data":{"message":{"epoch":"0","validator_index":"0"},"signature":"0xb74eade64ebf1e02cc57e5d29517032c6ca99132fb8e7fb7e6d58c68713e581ef0ef88e2a6c599a007d997782abdd50b0f9763500a93a971c89cb2275583fe755d7c0e64f459ff22fcef5cab3f80848f0356e67c142b9cf3ee65613f56283d6e"},"fork_version":"invalid"}`),
in: []byte(`{"exit":{"message":{"epoch":"0","validator_index":"0"},"signature":"0xb74eade64ebf1e02cc57e5d29517032c6ca99132fb8e7fb7e6d58c68713e581ef0ef88e2a6c599a007d997782abdd50b0f9763500a93a971c89cb2275583fe755d7c0e64f459ff22fcef5cab3f80848f0356e67c142b9cf3ee65613f56283d6e"},"fork_version":"invalid"}`),
err: "fork version invalid: encoding/hex: invalid byte: U+0069 'i'",
},
{
name: "Good",
in: []byte(`{"data":{"message":{"epoch":"0","validator_index":"0"},"signature":"0xb74eade64ebf1e02cc57e5d29517032c6ca99132fb8e7fb7e6d58c68713e581ef0ef88e2a6c599a007d997782abdd50b0f9763500a93a971c89cb2275583fe755d7c0e64f459ff22fcef5cab3f80848f0356e67c142b9cf3ee65613f56283d6e"},"fork_version":"0x00000001"}`),
in: []byte(`{"exit":{"message":{"epoch":"0","validator_index":"0"},"signature":"0xb74eade64ebf1e02cc57e5d29517032c6ca99132fb8e7fb7e6d58c68713e581ef0ef88e2a6c599a007d997782abdd50b0f9763500a93a971c89cb2275583fe755d7c0e64f459ff22fcef5cab3f80848f0356e67c142b9cf3ee65613f56283d6e"},"fork_version":"0x00000001"}`),
},
}