Compare commits

..

9 Commits

Author SHA1 Message Date
Jim McDonald
6118f9cab8 Release version 1.5.9 2020-09-21 09:34:32 +01:00
Jim McDonald
829dbd3bf2 fix issue where wallet mnemonics were not normalised to NFKD 2020-09-21 09:33:51 +01:00
Jim McDonald
f0ad10463e Tidy up verbose output 2020-09-07 11:03:35 +01:00
Jim McDonald
3d0dab0b95 block info supports fetching the genesis block 2020-09-07 09:37:03 +01:00
Jim McDonald
5abfabc355 Add attester inclusion command 2020-09-02 11:17:38 +01:00
Jim McDonald
e84b600d5d Add participants to account info; passphrase for account creation is optional for Dirk 2020-08-30 09:38:21 +01:00
Jim McDonald
e64a46f126 Update HOWTO for launchpad 2020-08-26 22:14:59 +01:00
Jim McDonald
0746fa3048 Release 1.5.8 2020-08-25 09:20:45 +01:00
Jim McDonald
94eb3fbca7 Add genesis validators root to chain info 2020-08-23 13:44:17 +01:00
20 changed files with 703 additions and 208 deletions

11
CHANGELOG.md Normal file
View File

@@ -0,0 +1,11 @@
1.5.9:
- fix issue where wallet mnemonics were not normalised to NFKD
- "block info" supports fetching the gensis block (--slot=0)
- "attester inclusion" command finds the inclusion slot for a validator's attestation
- "account info" with verbose option now displays participants for distributed accounts
- fix issue where distributed account generation without a passphrase was not allowed
1.5.8:
- allow raw deposit transactions to be supplied to "deposit verify"
- move functionality of "account withdrawalcredentials" to be part of "account info"
- add genesis validators root to "chain info"

View File

@@ -61,7 +61,7 @@ In quiet mode this will return 0 if the account is created successfully, otherwi
outputIf(debug, fmt.Sprintf("Distributed account has %d/%d threshold", viper.GetUint32("signing-threshold"), viper.GetUint32("participants")))
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
account, err = distributedCreator.CreateDistributedAccount(ctx, accountName, viper.GetUint32("participants"), viper.GetUint32("signing-threshold"), []byte(getPassphrase()))
account, err = distributedCreator.CreateDistributedAccount(ctx, accountName, viper.GetUint32("participants"), viper.GetUint32("signing-threshold"), []byte(getOptionalPassphrase()))
} else {
if viper.GetString("path") != "" {
// Want a pathed account
@@ -99,14 +99,17 @@ func init() {
accountCmd.AddCommand(accountCreateCmd)
accountFlags(accountCreateCmd)
accountCreateCmd.Flags().Uint32("participants", 0, "Number of participants (for distributed accounts)")
accountCreateCmd.Flags().Uint32("signing-threshold", 0, "Signing threshold (for distributed accounts)")
accountCreateCmd.Flags().String("path", "", "path of account (for hierarchical deterministic accounts)")
}
func accountCreateBindings() {
if err := viper.BindPFlag("participants", accountCreateCmd.Flags().Lookup("participants")); err != nil {
panic(err)
}
accountCreateCmd.Flags().Uint32("signing-threshold", 0, "Signing threshold (for distributed accounts)")
if err := viper.BindPFlag("signing-threshold", accountCreateCmd.Flags().Lookup("signing-threshold")); err != nil {
panic(err)
}
accountCreateCmd.Flags().String("path", "", "path of account (for hierarchical deterministic accounts)")
if err := viper.BindPFlag("path", accountCreateCmd.Flags().Lookup("path")); err != nil {
panic(err)
}

View File

@@ -58,6 +58,13 @@ In quiet mode this will return 0 if the account exists, otherwise 1.`,
if distributedAccount, ok := account.(e2wtypes.DistributedAccount); ok {
fmt.Printf("Composite public key: %#x\n", distributedAccount.CompositePublicKey().Marshal())
fmt.Printf("Signing threshold: %d/%d\n", distributedAccount.SigningThreshold(), len(distributedAccount.Participants()))
if verbose {
fmt.Printf("Participants:\n")
for k, v := range distributedAccount.Participants() {
fmt.Printf(" %d: %s\n", k, v)
}
}
withdrawalPubKey = distributedAccount.CompositePublicKey()
}
if verbose {

View File

@@ -1,73 +0,0 @@
// Copyright © 2019, 2020 Weald Technology Trading
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"context"
"encoding/hex"
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
"github.com/spf13/viper"
util "github.com/wealdtech/go-eth2-util"
)
var accountWithdrawalCredentialsCmd = &cobra.Command{
Use: "withdrawalcredentials",
Short: "Provide withdrawal credentials for an account",
Long: `Provide withdrawal credentials for an account. For example:
ethdo account withdrawalcredentials --account="Validators/1"
In quiet mode this will return 0 if the account exists, otherwise 1.`,
Run: func(cmd *cobra.Command, args []string) {
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
assert(viper.GetString("account") != "" || viper.GetString("pubkey") != "", "account or pubkey is required")
var pubKey []byte
if viper.GetString("pubkey") != "" {
var err error
pubKey, err = hex.DecodeString(strings.TrimPrefix(viper.GetString("pubkey"), "0x"))
errCheck(err, "Failed to decode supplied public key")
} else {
_, account, err := walletAndAccountFromInput(ctx)
errCheck(err, "Failed to obtain account")
key, err := bestPublicKey(account)
errCheck(err, "Account does not provide a public key")
pubKey = key.Marshal()
}
if quiet {
os.Exit(_exitSuccess)
}
withdrawalCredentials := util.SHA256(pubKey)
withdrawalCredentials[0] = byte(0) // BLS_WITHDRAWAL_PREFIX
fmt.Printf("%#x\n", withdrawalCredentials)
},
}
func init() {
accountCmd.AddCommand(accountWithdrawalCredentialsCmd)
accountFlags(accountWithdrawalCredentialsCmd)
accountWithdrawalCredentialsCmd.Flags().String("pubkey", "", "Public key (overrides account)")
if err := viper.BindPFlag("pubkey", accountCreateCmd.Flags().Lookup("pubkey")); err != nil {
panic(err)
}
}

32
cmd/attester.go Normal file
View File

@@ -0,0 +1,32 @@
// 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 (
"github.com/spf13/cobra"
)
// attesterCmd represents the attester command
var attesterCmd = &cobra.Command{
Use: "attester",
Short: "Obtain information about Ethereum 2 attesters",
Long: "Obtain information about Ethereum 2 attesters",
}
func init() {
RootCmd.AddCommand(attesterCmd)
}
func attesterFlags(cmd *cobra.Command) {
}

156
cmd/attesterinclusion.go Normal file
View File

@@ -0,0 +1,156 @@
// 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 (
"context"
"encoding/hex"
"fmt"
"os"
"strings"
"time"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/wealdtech/ethdo/grpc"
"github.com/wealdtech/ethdo/util"
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
)
var attesterInclusionCmd = &cobra.Command{
Use: "inclusion",
Short: "Obtain information about attester inclusion",
Long: `Obtain information about attester inclusion. For example:
ethdo attester inclusion --account=Validators/00001 --epoch=12345
In quiet mode this will return 0 if an attestation from the attester is found on the block fo the given epoch, otherwise 1.`,
Run: func(cmd *cobra.Command, args []string) {
err := connect()
errCheck(err, "Failed to obtain connection to Ethereum 2 beacon chain block")
// Obtain the epoch.
epoch := viper.GetInt64("epoch")
if epoch == -1 {
outputIf(debug, "No epoch supplied; fetching current epoch")
config, err := grpc.FetchChainConfig(eth2GRPCConn)
errCheck(err, "Failed to obtain beacon chain configuration")
slotsPerEpoch := config["SlotsPerEpoch"].(uint64)
secondsPerSlot := config["SecondsPerSlot"].(uint64)
genesisTime, err := grpc.FetchGenesisTime(eth2GRPCConn)
errCheck(err, "Failed to obtain beacon chain genesis")
epoch = int64(time.Since(genesisTime).Seconds()) / int64(secondsPerSlot*slotsPerEpoch)
}
outputIf(debug, fmt.Sprintf("Epoch is %d", epoch))
// Obtain the validator.
account, err := attesterInclusionAccount()
errCheck(err, "Failed to obtain account")
validatorIndex, err := grpc.FetchValidatorIndex(eth2GRPCConn, account)
errCheck(err, "Failed to obtain validator")
// Find the attesting slot for the given epoch.
committees, err := grpc.FetchValidatorCommittees(eth2GRPCConn, uint64(epoch))
errCheck(err, "Failed to obtain validator committees")
slot := uint64(0)
committeeIndex := uint64(0)
validatorPositionInCommittee := uint64(0)
found := false
for searchSlot, committee := range committees {
for searchCommitteeIndex, committeeValidatorIndices := range committee {
for position, committeeValidatorIndex := range committeeValidatorIndices {
if validatorIndex == committeeValidatorIndex {
outputIf(verbose, fmt.Sprintf("Validator %d scheduled to attest at slot %d for epoch %d: entry %d in committee %d", validatorIndex, searchSlot, epoch, position, searchCommitteeIndex))
slot = searchSlot
committeeIndex = uint64(searchCommitteeIndex)
validatorPositionInCommittee = uint64(position)
found = true
break
}
}
}
}
assert(found, "Failed to find attester duty for validator in the given epoch")
startSlot := slot + 1
endSlot := startSlot + 32
for curSlot := startSlot; curSlot < endSlot; curSlot++ {
signedBlock, err := grpc.FetchBlock(eth2GRPCConn, curSlot)
errCheck(err, "Failed to obtain block")
if signedBlock == nil {
outputIf(debug, fmt.Sprintf("No block at slot %d", curSlot))
continue
}
outputIf(debug, fmt.Sprintf("Fetched block %d", curSlot))
for i, attestation := range signedBlock.Block.Body.Attestations {
outputIf(debug, fmt.Sprintf("Attestation %d is for slot %d and committee %d", i, attestation.Data.Slot, attestation.Data.CommitteeIndex))
if attestation.Data.Slot == slot &&
attestation.Data.CommitteeIndex == committeeIndex &&
attestation.AggregationBits.BitAt(validatorPositionInCommittee) {
if verbose {
fmt.Printf("Attestation included in block %d, attestation %d (inclusion delay %d)\n", curSlot, i, curSlot-slot)
} else if !quiet {
fmt.Printf("Attestation included in block %d (inclusion delay %d)\n", curSlot, curSlot-slot)
}
os.Exit(_exitSuccess)
}
}
}
outputIf(verbose, "Attestation not included on the chain")
os.Exit(_exitFailure)
},
}
// attesterInclusionAccount obtains the account for the attester inclusion command.
func attesterInclusionAccount() (e2wtypes.Account, error) {
var account e2wtypes.Account
var err error
if viper.GetString("account") != "" {
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
_, account, err = walletAndAccountFromPath(ctx, viper.GetString("account"))
if err != nil {
return nil, errors.Wrap(err, "failed to obtain account")
}
} else {
pubKey := viper.GetString("pubkey")
pubKeyBytes, err := hex.DecodeString(strings.TrimPrefix(pubKey, "0x"))
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("failed to decode public key %s", pubKey))
}
account, err = util.NewScratchAccount(nil, pubKeyBytes)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("invalid public key %s", pubKey))
}
}
return account, nil
}
func init() {
attesterCmd.AddCommand(attesterInclusionCmd)
attesterFlags(attesterInclusionCmd)
attesterInclusionCmd.Flags().Int64("epoch", -1, "the current epoch")
attesterInclusionCmd.Flags().String("pubkey", "", "the public key of the attester")
}
func attesterInclusionBindings() {
if err := viper.BindPFlag("epoch", attesterInclusionCmd.Flags().Lookup("epoch")); err != nil {
panic(err)
}
if err := viper.BindPFlag("pubkey", attesterInclusionCmd.Flags().Lookup("pubkey")); err != nil {
panic(err)
}
}

View File

@@ -54,7 +54,6 @@ In quiet mode this will return 0 if the block information is present and not ski
genesisTime, err := grpc.FetchGenesisTime(eth2GRPCConn)
errCheck(err, "Failed to obtain beacon chain genesis")
assert(blockInfoStream || blockInfoSlot != 0, "--slot or --stream is required")
assert(!blockInfoStream || blockInfoSlot == -1, "--slot and --stream are not supported together")
var slot uint64
@@ -64,8 +63,6 @@ In quiet mode this will return 0 if the block information is present and not ski
} else {
slot = uint64(blockInfoSlot)
}
assert(slot > 0, "slot must be greater than 0")
signedBlock, err := grpc.FetchBlock(eth2GRPCConn, slot)
errCheck(err, "Failed to obtain block")
if signedBlock == nil {

View File

@@ -39,6 +39,9 @@ In quiet mode this will return 0 if the chain information can be obtained, other
genesisTime, err := grpc.FetchGenesisTime(eth2GRPCConn)
errCheck(err, "Failed to obtain genesis time")
genesisValidatorsRoot, err := grpc.FetchGenesisValidatorsRoot(eth2GRPCConn)
errCheck(err, "Failed to obtain genesis validators root")
if quiet {
os.Exit(_exitSuccess)
}
@@ -49,9 +52,10 @@ In quiet mode this will return 0 if the chain information can be obtained, other
fmt.Printf("Genesis time: %s\n", genesisTime.Format(time.UnixDate))
outputIf(verbose, fmt.Sprintf("Genesis timestamp: %v", genesisTime.Unix()))
}
outputIf(verbose, fmt.Sprintf("Genesis fork version: %0x", config["GenesisForkVersion"].([]byte)))
outputIf(verbose, fmt.Sprintf("Seconds per slot: %v", config["SecondsPerSlot"].(uint64)))
outputIf(verbose, fmt.Sprintf("Slots per epoch: %v", config["SlotsPerEpoch"].(uint64)))
fmt.Printf("Genesis validators root: %#x\n", genesisValidatorsRoot)
fmt.Printf("Genesis fork version: %#x\n", config["GenesisForkVersion"].([]byte))
fmt.Printf("Seconds per slot: %d\n", config["SecondsPerSlot"].(uint64))
fmt.Printf("Slots per epoch: %d\n", config["SlotsPerEpoch"].(uint64))
os.Exit(_exitSuccess)
},

View File

@@ -16,48 +16,60 @@ package cmd
import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/spf13/cobra"
"github.com/wealdtech/ethdo/util"
e2types "github.com/wealdtech/go-eth2-types/v2"
util "github.com/wealdtech/go-eth2-util"
eth2util "github.com/wealdtech/go-eth2-util"
string2eth "github.com/wealdtech/go-string2eth"
)
type depositData struct {
Name string `json:"name,omitempty"`
Account string `json:"account,omitempty"`
PublicKey string `json:"pubkey"`
WithdrawalCredentials string `json:"withdrawal_credentials"`
Signature string `json:"signature"`
DepositDataRoot string `json:"deposit_data_root"`
Value uint64 `json:"value"`
Version uint64 `json:"version"`
}
var depositVerifyData string
var depositVerifyWithdrawalPubKey string
var depositVerifyValidatorPubKey string
var depositVerifyDepositValue string
var depositVerifyDepositAmount string
var depositVerifyCmd = &cobra.Command{
Use: "verify",
Short: "Verify deposit data matches requirements",
Long: `Verify deposit data matches requirements. For example:
Short: "Verify deposit data matches the provided data",
Long: `Verify deposit data matches the provided input data. For example:
ethdo deposit verify --data=depositdata.json --withdrawalaccount=primary/current --value="32 Ether"
The information generated can be passed to ethereal to create a deposit from the Ethereum 1 chain.
The deposit data is compared to the supplied withdrawal account/public key, validator public key, and value to ensure they match.
In quiet mode this will return 0 if the the data can be generated correctly, otherwise 1.`,
In quiet mode this will return 0 if the the data is verified correctly, otherwise 1.`,
Run: func(cmd *cobra.Command, args []string) {
assert(depositVerifyData != "", "--data is required")
deposits, err := depositDataFromJSON(depositVerifyData)
var data []byte
var err error
// Input could be JSON or a path to JSON.
switch {
case strings.HasPrefix(depositVerifyData, "0x"):
// Looks like raw binary.
data = []byte(depositVerifyData)
case strings.HasPrefix(depositVerifyData, "{"):
// Looks like JSON.
data = []byte("[" + depositVerifyData + "]")
case strings.HasPrefix(depositVerifyData, "["):
// Looks like JSON array.
data = []byte(depositVerifyData)
default:
// Assume it's a path to JSON.
data, err = ioutil.ReadFile(depositVerifyData)
errCheck(err, "Failed to read deposit data file")
if data[0] == '{' {
data = []byte("[" + string(data) + "]")
}
}
deposits, err := util.DepositInfoFromJSON(data)
errCheck(err, "Failed to fetch deposit data")
var withdrawalCredentials []byte
@@ -67,17 +79,16 @@ In quiet mode this will return 0 if the the data can be generated correctly, oth
assert(len(withdrawalPubKeyBytes) == 48, "Public key should be 48 bytes")
withdrawalPubKey, err := e2types.BLSPublicKeyFromBytes(withdrawalPubKeyBytes)
errCheck(err, "Value supplied with --withdrawalpubkey is not a valid public key")
withdrawalCredentials = util.SHA256(withdrawalPubKey.Marshal())
withdrawalCredentials = eth2util.SHA256(withdrawalPubKey.Marshal())
withdrawalCredentials[0] = 0 // BLS_WITHDRAWAL_PREFIX
}
outputIf(debug, fmt.Sprintf("Withdrawal credentials are %#x", withdrawalCredentials))
depositValue := uint64(0)
if depositVerifyDepositValue != "" {
depositValue, err = string2eth.StringToGWei(depositVerifyDepositValue)
depositAmount := uint64(0)
if depositVerifyDepositAmount != "" {
depositAmount, err = string2eth.StringToGWei(depositVerifyDepositAmount)
errCheck(err, "Invalid value")
// This is hard-coded, to allow deposit data to be generated without a connection to the beacon node.
assert(depositValue >= 1000000000, "deposit value must be at least 1 Ether") // MIN_DEPOSIT_AMOUNT
assert(depositAmount >= 1000000000, "deposit amount must be at least 1 Ether") // MIN_DEPOSIT_AMOUNT
}
validatorPubKeys := make(map[[48]byte]bool)
@@ -88,31 +99,17 @@ In quiet mode this will return 0 if the the data can be generated correctly, oth
failures := false
for i, deposit := range deposits {
if withdrawalCredentials != nil {
depositWithdrawalCredentials, err := hex.DecodeString(strings.TrimPrefix(deposit.WithdrawalCredentials, "0x"))
errCheck(err, fmt.Sprintf("Invalid withdrawal public key for deposit %d", i))
if !bytes.Equal(depositWithdrawalCredentials, withdrawalCredentials) {
outputIf(!quiet, fmt.Sprintf("Invalid withdrawal credentials for deposit %d", i))
failures = true
}
if deposit.Amount == 0 {
deposit.Amount = depositAmount
}
if depositValue != 0 {
if deposit.Value != depositValue {
outputIf(!quiet, fmt.Sprintf("Invalid deposit value for deposit %d", i))
failures = true
}
verified, err := verifyDeposit(deposit, withdrawalCredentials, validatorPubKeys, depositAmount)
errCheck(err, fmt.Sprintf("Error attempting to verify deposit %d", i))
if !verified {
failures = true
outputIf(!quiet, fmt.Sprintf("Deposit %q failed verification", deposit.Name))
} else {
outputIf(quiet, fmt.Sprintf("Deposit %q verified", deposit.Name))
}
if len(validatorPubKeys) != 0 {
depositValidatorPubKey, err := hex.DecodeString(strings.TrimPrefix(deposit.PublicKey, "0x"))
errCheck(err, fmt.Sprintf("Invalid validator public key for deposit %d", i))
var key [48]byte
copy(key[:], depositValidatorPubKey)
if _, exists := validatorPubKeys[key]; !exists {
outputIf(!quiet, fmt.Sprintf("Unknown validator public key for deposit %d", i))
failures = true
}
}
outputIf(!quiet, fmt.Sprintf("Deposit %q verified", deposit.Name))
}
if failures {
@@ -177,61 +174,47 @@ func validatorPubKeysFromInput(input string) (map[[48]byte]bool, error) {
return pubKeys, nil
}
func depositDataFromJSON(input string) ([]*depositData, error) {
var err error
var data []byte
// Input could be JSON or a path to JSON
switch {
case strings.HasPrefix(input, "{"):
// Looks like JSON
data = []byte("[" + input + "]")
case strings.HasPrefix(input, "["):
// Looks like JSON array
data = []byte(input)
default:
// Assume it's a path to JSON
data, err = ioutil.ReadFile(input)
if err != nil {
return nil, errors.Wrap(err, "failed to find deposit data file")
}
if data[0] == '{' {
data = []byte("[" + string(data) + "]")
func verifyDeposit(deposit *util.DepositInfo, withdrawalCredentials []byte, validatorPubKeys map[[48]byte]bool, amount uint64) (bool, error) {
if withdrawalCredentials != nil {
if !bytes.Equal(deposit.WithdrawalCredentials, withdrawalCredentials) {
return false, errors.New("withdrawal credentials incorrect")
}
outputIf(verbose, "Withdrawal credentials verified")
}
var depositData []*depositData
err = json.Unmarshal(data, &depositData)
if amount != 0 {
if deposit.Amount != amount {
return false, errors.New("deposit value incorrect")
}
outputIf(verbose, "Amount verified")
}
if len(validatorPubKeys) != 0 {
var key [48]byte
copy(key[:], deposit.PublicKey)
if _, exists := validatorPubKeys[key]; !exists {
return false, errors.New("validator public key incorrect")
}
outputIf(verbose, "Validator public key verified")
}
depositData := &ethpb.Deposit_Data{
PublicKey: deposit.PublicKey,
WithdrawalCredentials: deposit.WithdrawalCredentials,
Amount: deposit.Amount,
Signature: deposit.Signature,
}
depositDataRoot, err := depositData.HashTreeRoot()
if err != nil {
return nil, errors.Wrap(err, "data is not valid JSON")
return false, errors.Wrap(err, "failed to generate deposit data root")
}
if len(depositData) == 0 {
return nil, errors.New("no deposits supplied")
if !bytes.Equal(deposit.DepositDataRoot, depositDataRoot[:]) {
return false, errors.New("deposit data root incorrect")
}
minVersion := depositData[0].Version
maxVersion := depositData[0].Version
for i := range depositData {
if depositData[i].PublicKey == "" {
return nil, fmt.Errorf("no public key for deposit %d", i)
}
if depositData[i].DepositDataRoot == "" {
return nil, fmt.Errorf("no data root for deposit %d", i)
}
if depositData[i].Signature == "" {
return nil, fmt.Errorf("no signature for deposit %d", i)
}
if depositData[i].WithdrawalCredentials == "" {
return nil, fmt.Errorf("no withdrawal credentials for deposit %d", i)
}
if depositData[i].Value < 1000000000 {
return nil, fmt.Errorf("Deposit amount too small for deposit %d", i)
}
if depositData[i].Version > maxVersion {
maxVersion = depositData[i].Version
}
if depositData[i].Version < minVersion {
minVersion = depositData[i].Version
}
}
return depositData, nil
outputIf(debug, "Deposit data root verified")
outputIf(verbose, "Deposit verified")
return true, nil
}
func init() {
@@ -239,6 +222,6 @@ 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(&depositVerifyDepositValue, "depositvalue", "", "Value of the amount to be deposited")
depositVerifyCmd.Flags().StringVar(&depositVerifyDepositAmount, "depositvalue", "", "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")
}

View File

@@ -36,14 +36,12 @@ var exitVerifyPubKey string
var exitVerifyCmd = &cobra.Command{
Use: "verify",
Short: "Verify deposit data matches requirements",
Long: `Verify deposit data matches requirements. For example:
Short: "Verify exit data is valid",
Long: `Verify that exit data generated by "ethdo validator exit" is correct for a given account. For example:
ethdo deposit verify --data=depositdata.json --withdrawalaccount=primary/current --value="32 Ether"
ethdo exit verify --data=exitdata.json --account=primary/current
The information generated can be passed to ethereal to create a deposit from the Ethereum 1 chain.
In quiet mode this will return 0 if the the data can be generated correctly, otherwise 1.`,
In quiet mode this will return 0 if the the exit is verified correctly, otherwise 1.`,
Run: func(cmd *cobra.Command, args []string) {
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
@@ -60,7 +58,7 @@ In quiet mode this will return 0 if the the data can be generated correctly, oth
err = connect()
errCheck(err, "Failed to obtain connection to Ethereum 2 beacon chain node")
genesisValidatorsRoot, err := grpc.FetchGenesisValidatorsRoot(eth2GRPCConn)
outputIf(debug, fmt.Sprintf("Genesis validators root is %x", genesisValidatorsRoot))
outputIf(debug, fmt.Sprintf("Genesis validators root is %#x", genesisValidatorsRoot))
errCheck(err, "Failed to obtain genesis validators root")
domain := e2types.Domain(e2types.DomainVoluntaryExit, data.ForkVersion, genesisValidatorsRoot)
exit := &ethpb.VoluntaryExit{
@@ -131,7 +129,10 @@ func init() {
exitFlags(exitVerifyCmd)
exitVerifyCmd.Flags().String("data", "", "JSON data, or path to JSON data")
exitVerifyCmd.Flags().StringVar(&exitVerifyPubKey, "pubkey", "", "Public key for which to verify exit")
if err := viper.BindPFlag("exit.data", exitVerifyCmd.Flags().Lookup("data")); err != nil {
}
func exitVerifyBindings() {
if err := viper.BindPFlag("data", exitVerifyCmd.Flags().Lookup("data")); err != nil {
panic(err)
}
}

View File

@@ -37,3 +37,13 @@ func getPassphrase() string {
assert(len(passphrases) == 1, "multiple passphrases supplied; cannot continue")
return passphrases[0]
}
// getOptionalPassphrase fetches the passphrase if supplied by the user.
func getOptionalPassphrase() string {
passphrases := getPassphrases()
if len(passphrases) == 0 {
return ""
}
assert(len(passphrases) == 1, "multiple passphrases supplied; cannot continue")
return passphrases[0]
}

View File

@@ -72,11 +72,22 @@ func persistentPreRun(cmd *cobra.Command, args []string) {
return
}
// We bind viper here so that we bind to the correct command
// We bind viper here so that we bind to the correct command.
quiet = viper.GetBool("quiet")
verbose = viper.GetBool("verbose")
debug = viper.GetBool("debug")
rootStore = viper.GetString("store")
// Command-specific bindings.
switch fmt.Sprintf("%s/%s", cmd.Parent().Name(), cmd.Name()) {
case "account/create":
accountCreateBindings()
case "attester/inclusion":
attesterInclusionBindings()
case "exit/verify":
exitVerifyBindings()
case "wallet/create":
walletCreateBindings()
}
if quiet && verbose {
fmt.Println("Cannot supply both quiet and verbose flags")

View File

@@ -22,7 +22,7 @@ import (
"github.com/spf13/viper"
)
var ReleaseVersion = "local build from v1.5.7"
var ReleaseVersion = "local build from v1.5.8"
// versionCmd represents the version command
var versionCmd = &cobra.Command{

View File

@@ -28,6 +28,7 @@ import (
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
hd "github.com/wealdtech/go-eth2-wallet-hd/v2"
nd "github.com/wealdtech/go-eth2-wallet-nd/v2"
"golang.org/x/text/unicode/norm"
)
var walletCreateCmd = &cobra.Command{
@@ -106,6 +107,9 @@ func walletCreateHD(ctx context.Context, name string, passphrase string, mnemoni
mnemonicPassphrase = strings.Join(mnemonicParts[24:], " ")
}
}
// Normalise the input.
mnemonic = string(norm.NFKD.Bytes([]byte(mnemonic)))
mnemonicPassphrase = string(norm.NFKD.Bytes([]byte(mnemonicPassphrase)))
// Ensure the mnemonic is valid
if !bip39.IsMnemonicValid(mnemonic) {
@@ -135,10 +139,13 @@ func init() {
walletCmd.AddCommand(walletCreateCmd)
walletFlags(walletCreateCmd)
walletCreateCmd.Flags().String("type", "non-deterministic", "Type of wallet to create (non-deterministic or hierarchical deterministic)")
walletCreateCmd.Flags().String("mnemonic", "", "The 24-word mnemonic for a hierarchical deterministic wallet")
}
func walletCreateBindings() {
if err := viper.BindPFlag("type", walletCreateCmd.Flags().Lookup("type")); err != nil {
panic(err)
}
walletCreateCmd.Flags().String("mnemonic", "", "The 24-word mnemonic for a hierarchical deterministic wallet")
if err := viper.BindPFlag("mnemonic", walletCreateCmd.Flags().Lookup("mnemonic")); err != nil {
panic(err)
}

View File

@@ -78,8 +78,8 @@ ethdo wallet create --wallet="Launchpad" --type=hd --walletpassphrase=walletsecr
Launchpad accounts are identified by their path. The path can be seen in the filename of the keystore, for example the filename `keystore-m_12381_3600_1_0_0-1596891358.json` relates to a path of `m/12381/3600/1/0/0`. It is also present directly in the keystore under the `path` key.
To create an account corresponding to this key you would use the command:
To create an account corresponding to this key with the account name "Account 1" you would use the command:
```sh
ethdo account create --wallet="Launchpad" --walletpassphrase=walletsecret --passphrase=secret --path=m/12381/3600/1/0/0
ethdo account create --account="Launchpad/Account 1" --walletpassphrase=walletsecret --passphrase=secret --path=m/12381/3600/1/0/0
```

View File

@@ -317,7 +317,7 @@ Deposit commands focus on information about deposit data information in a JSON f
#### `verify`
`ethdo deposit verify` verifies one or more deposit data information in a JSON file generated by the `ethdo validator depositdata` command. Options include:
- `data`: either a path to the JSON file or the JSON itself
- `data`: either a path to the JSON file, the JSON itself, or a hex string representing a deposit transaction
- `withdrawalpubkey`: the public key of the withdrawal for the deposit. If no value is supplied then withdrawal credentials for deposits will not be checked
- `validatorpubkey`: the public key of the validator for the deposit. If no value is supplied then validator public keys will not be checked
- `depositvalue`: the value of the Ether being deposited. If no value is supplied then deposit values will not be checked.
@@ -366,6 +366,7 @@ Current slot: 178
Current epoch: 5
Genesis timestamp: 1587020563
```
### `validator` commands
Validator commands focus on interaction with Ethereum 2 validators.
@@ -431,6 +432,22 @@ Balance: 3.201850307 Ether
Effective balance: 3.1 Ether
```
### `attester` commands
Attester commands focus on Ethereum 2 validators' actions as attesters.
#### `inclusion`
`ethdo attester inclusion` finds the block with wihch an attestation is included on the chain. Options include:
- `epoch` the epoch in which to obtain the inclusion information (defaults to current epoch)
- `account` the account for which to fetch the inclusion information
- `pubkey` the public key for which to fetch the inclusion information
```sh
$ ethdo attester inclusion --account=Validators/1 --epoch=6484
Attestation included in block 207492 (inclusion delay 1)
```
## Maintainers
Jim McDonald: [@mcdee](https://github.com/mcdee).

25
go.mod
View File

@@ -4,26 +4,27 @@ go 1.13
require (
github.com/OneOfOne/xxhash v1.2.5 // indirect
github.com/aws/aws-sdk-go v1.33.11 // indirect
github.com/aws/aws-sdk-go v1.34.9 // indirect
github.com/ferranbt/fastssz v0.0.0-20200818222714-826c7ef45b30 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/gogo/protobuf v1.3.1
github.com/google/uuid v1.1.1
github.com/grpc-ecosystem/grpc-gateway v1.14.6 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.14.7 // indirect
github.com/herumi/bls-eth-go-binary v0.0.0-20200722032157-41fc56eba7b4
github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/mapstructure v1.3.3 // indirect
github.com/pelletier/go-toml v1.8.0 // indirect
github.com/pkg/errors v0.9.1
github.com/prysmaticlabs/ethereumapis v0.0.0-20200709024211-e8095222f77b
github.com/prysmaticlabs/ethereumapis v0.0.0-20200812153649-a842fc47c2c3
github.com/prysmaticlabs/go-bitfield v0.0.0-20200618145306-2ae0807bef65
github.com/prysmaticlabs/go-ssz v0.0.0-20200612203617-6d5c9aa213ae
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/spf13/afero v1.3.2 // indirect
github.com/spf13/afero v1.3.4 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/cobra v1.0.0
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.7.0
github.com/spf13/viper v1.7.1
github.com/tyler-smith/go-bip39 v1.0.2
github.com/wealdtech/eth2-signer-api v1.5.2
github.com/wealdtech/go-bytesutil v1.1.1
@@ -31,7 +32,7 @@ require (
github.com/wealdtech/go-eth2-types/v2 v2.5.0
github.com/wealdtech/go-eth2-util v1.5.0
github.com/wealdtech/go-eth2-wallet v1.13.0
github.com/wealdtech/go-eth2-wallet-dirk v1.0.1
github.com/wealdtech/go-eth2-wallet-dirk v1.0.2
github.com/wealdtech/go-eth2-wallet-distributed v1.1.0
github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.1.0
github.com/wealdtech/go-eth2-wallet-hd/v2 v2.4.0
@@ -40,9 +41,11 @@ require (
github.com/wealdtech/go-eth2-wallet-store-s3 v1.8.0
github.com/wealdtech/go-eth2-wallet-types/v2 v2.7.0
github.com/wealdtech/go-string2eth v1.1.0
golang.org/x/sys v0.0.0-20200722175500-76b94024e4b6 // indirect
golang.org/x/text v0.3.3 // indirect
google.golang.org/genproto v0.0.0-20200722002428-88e341933a54 // indirect
google.golang.org/grpc v1.30.0
gopkg.in/ini.v1 v1.57.0 // indirect
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a // indirect
golang.org/x/net v0.0.0-20200822124328-c89045814202 // indirect
golang.org/x/sys v0.0.0-20200821140526-fda516888d29 // indirect
golang.org/x/text v0.3.3
google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70 // indirect
google.golang.org/grpc v1.31.0
gopkg.in/ini.v1 v1.60.1 // indirect
)

41
go.sum
View File

@@ -31,6 +31,10 @@ github.com/aws/aws-sdk-go v1.33.5 h1:p2fr1ryvNTU6avUWLI+/H7FGv0TBIjzVM5WDgXBBv4U
github.com/aws/aws-sdk-go v1.33.5/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.33.11 h1:A7b3mNKbh/0zrhnNN/KxWD0YZJw2RImnjFXWOquYKB4=
github.com/aws/aws-sdk-go v1.33.11/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.34.5 h1:FwubVVX9u+kW9qDCjVzyWOdsL+W5wPq683wMk2R2GXk=
github.com/aws/aws-sdk-go v1.34.5/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.34.9 h1:cUGBW9CVdi0mS7K1hDzxIqTpfeWhpoQiguq81M1tjK0=
github.com/aws/aws-sdk-go v1.34.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
@@ -67,6 +71,11 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/ferranbt/fastssz v0.0.0-20200514094935-99fccaf93472 h1:maoKvILdMk6CSWHanFcUdxXIZGKD9YpWIaVbUQ/4kfg=
github.com/ferranbt/fastssz v0.0.0-20200514094935-99fccaf93472/go.mod h1:LlFXPmgrgVYsuoFDwV8rDJ9tvt1pLQdjKvU1b5IRES0=
github.com/ferranbt/fastssz v0.0.0-20200728110133-0b6e349af87a/go.mod h1:DyEu2iuLBnb/T51BlsiO3yLYdJC6UbGMrIkqK1KmQxM=
github.com/ferranbt/fastssz v0.0.0-20200803113354-a18be873a4b6 h1:P440pnxSHIQI8gg7lQNNo+/F88EzUzS3Os0Xkhta7OQ=
github.com/ferranbt/fastssz v0.0.0-20200803113354-a18be873a4b6/go.mod h1:DyEu2iuLBnb/T51BlsiO3yLYdJC6UbGMrIkqK1KmQxM=
github.com/ferranbt/fastssz v0.0.0-20200818222714-826c7ef45b30 h1:Sbm5yhPObRc+RCZa9fAR/Px/j1qYwuqawF0X8dR2gAw=
github.com/ferranbt/fastssz v0.0.0-20200818222714-826c7ef45b30/go.mod h1:DyEu2iuLBnb/T51BlsiO3yLYdJC6UbGMrIkqK1KmQxM=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@@ -126,6 +135,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t
github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c=
github.com/grpc-ecosystem/grpc-gateway v1.14.6 h1:8ERzHx8aj1Sc47mu9n/AksaKCSWrMchFtkdrS4BIj5o=
github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw=
github.com/grpc-ecosystem/grpc-gateway v1.14.7 h1:Nk5kuHrnWUTf/0GL1a/vchH/om9Ap2/HnVna+jYZgTY=
github.com/grpc-ecosystem/grpc-gateway v1.14.7/go.mod h1:oYZKL012gGh6LMyg/xA7Q2yq6j8bu0wa+9w14EEthWU=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -243,6 +254,8 @@ github.com/prysmaticlabs/ethereumapis v0.0.0-20200619200018-174e3b90d786 h1:bJiO
github.com/prysmaticlabs/ethereumapis v0.0.0-20200619200018-174e3b90d786/go.mod h1:rs05kpTfWKl0KflsBWzBQFstoyPFMTWQTbxSAyGHe78=
github.com/prysmaticlabs/ethereumapis v0.0.0-20200709024211-e8095222f77b h1:GjYix8Y4VpQhlsjA2ickr3HxjIns4bI36zOmC+lwaNw=
github.com/prysmaticlabs/ethereumapis v0.0.0-20200709024211-e8095222f77b/go.mod h1:rs05kpTfWKl0KflsBWzBQFstoyPFMTWQTbxSAyGHe78=
github.com/prysmaticlabs/ethereumapis v0.0.0-20200812153649-a842fc47c2c3 h1:0f++UXRfp4/Mrmlfj3UaCnYj2lPr6El0gWWTBb9MD2Y=
github.com/prysmaticlabs/ethereumapis v0.0.0-20200812153649-a842fc47c2c3/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=
@@ -275,6 +288,8 @@ github.com/spf13/afero v1.3.0 h1:Ysnmjh1Di8EaWaBv40CYR4IdaIsBc5996Gh1oZzCBKk=
github.com/spf13/afero v1.3.0/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
github.com/spf13/afero v1.3.2 h1:GDarE4TJQI52kYSbSAmLiId1Elfj+xgSDqrUZxFhxlU=
github.com/spf13/afero v1.3.2/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
github.com/spf13/afero v1.3.4 h1:8q6vk3hthlpb2SouZcnBVKboxWQWMDNF38bwholZrJc=
github.com/spf13/afero v1.3.4/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
@@ -289,6 +304,8 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@@ -340,6 +357,8 @@ github.com/wealdtech/go-eth2-wallet-dirk v1.0.0 h1:1QUcWILF3h4OLCgTPpWklvRSuPu0f
github.com/wealdtech/go-eth2-wallet-dirk v1.0.0/go.mod h1:VTzjJ51dedvYPr4huI7g7KXZVTpGR6ZrCDQwBxJpLck=
github.com/wealdtech/go-eth2-wallet-dirk v1.0.1 h1:YUE1QlJPun8b+xbz0JM71/3t1i9zp9KjcZdJvtJQL+E=
github.com/wealdtech/go-eth2-wallet-dirk v1.0.1/go.mod h1:5jK/aEAjYAVRBKKjYAvJWSmOWxiECs4asYXHwloNI+w=
github.com/wealdtech/go-eth2-wallet-dirk v1.0.2 h1:ZxAdF6iTOzYHtQlWd1nzVevZ+HtXS/LLn580t+NXT3A=
github.com/wealdtech/go-eth2-wallet-dirk v1.0.2/go.mod h1:5jK/aEAjYAVRBKKjYAvJWSmOWxiECs4asYXHwloNI+w=
github.com/wealdtech/go-eth2-wallet-distributed v1.0.1 h1:3BxMII8T6t16g6lWcYWXjfdvaw8rXuwMQx9h0TG5wRg=
github.com/wealdtech/go-eth2-wallet-distributed v1.0.1/go.mod h1:Ha/8S+SCLEuSfXHdvhTLwnKaEF47o6gzQ+FURKwftvU=
github.com/wealdtech/go-eth2-wallet-distributed v1.1.0 h1:OZjjuxcIYo+EhAfph7lYP1z+VeNs9ruOI32kqtYe1Jg=
@@ -412,6 +431,10 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnk
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg=
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
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=
@@ -452,6 +475,10 @@ golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYc
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc h1:zK/HqS5bZxDptfPJNq8v7vJfXtkU7r9TLIoSr1bXaP4=
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
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=
@@ -484,6 +511,10 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORK
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200722175500-76b94024e4b6 h1:X9xIZ1YU8bLZA3l6gqDUHSFiD0GFI9S548h6C8nDtOY=
golang.org/x/sys v0.0.0-20200722175500-76b94024e4b6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed h1:J22ig1FUekjjkmZUM7pTKixYm8DvrYsvrBZdunYeIuQ=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200821140526-fda516888d29 h1:mNuhGagCf3lDDm5C0376C/sxh6V7fy9WbdEu/YDNA04=
golang.org/x/sys v0.0.0-20200821140526-fda516888d29/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@@ -546,6 +577,10 @@ google.golang.org/genproto v0.0.0-20200715011427-11fb19a81f2c h1:6DWnZZ6EY/59QRR
google.golang.org/genproto v0.0.0-20200715011427-11fb19a81f2c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200722002428-88e341933a54 h1:ASrBgpl9XvkNTP0m39/j18mid7aoF21npu2ioIBxYnY=
google.golang.org/genproto v0.0.0-20200722002428-88e341933a54/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200814021100-8c09557e8a18 h1:hjnc4GP1IeBnHi+u3sp8be4ELyIHmFSO5p8DUdJUVt4=
google.golang.org/genproto v0.0.0-20200814021100-8c09557e8a18/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70 h1:wboULUXGF3c5qdUnKp+6gLAccE6PRpa/czkYvQ4UXv8=
google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -559,6 +594,8 @@ google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0 h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0 h1:T7P4R73V3SSDPhH7WW7ATbfViLtmamH0DKrP3f9AuDI=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
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=
@@ -580,6 +617,10 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww=
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.58.0 h1:VdDvTzv/005R8vEFyQ56bpEnOKTNPbpJhL0VCohxlQw=
gopkg.in/ini.v1 v1.58.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.60.1 h1:P5y5shSkb0CFe44qEeMBgn8JLow09MP17jlJHanke5g=
gopkg.in/ini.v1 v1.60.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@@ -287,8 +287,11 @@ func FetchBlock(conn *grpc.ClientConn, slot uint64) (*ethpb.SignedBeaconBlock, e
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
req := &ethpb.ListBlocksRequest{
QueryFilter: &ethpb.ListBlocksRequest_Slot{Slot: slot},
req := &ethpb.ListBlocksRequest{}
if slot == 0 {
req.QueryFilter = &ethpb.ListBlocksRequest_Genesis{Genesis: true}
} else {
req.QueryFilter = &ethpb.ListBlocksRequest_Slot{Slot: slot}
}
resp, err := beaconClient.ListBlocks(ctx, req)
if err != nil {

282
util/depositinfo.go Normal file
View File

@@ -0,0 +1,282 @@
// 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 util
import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"strings"
"github.com/pkg/errors"
)
// DepositInfo is a generic deposit structure.
type DepositInfo struct {
Name string
Account string
PublicKey []byte
WithdrawalCredentials []byte
Signature []byte
DepositDataRoot []byte
DepositMessageRoot []byte
ForkVersion []byte
Amount uint64
Version uint64
}
// depositInfoV1 is an ethdo V1 deposit structure.
type depositInfoV1 struct {
Name string `json:"name,omitempty"`
Account string `json:"account,omitempty"`
PublicKey string `json:"pubkey"`
WithdrawalCredentials string `json:"withdrawal_credentials"`
Signature string `json:"signature"`
DepositDataRoot string `json:"deposit_data_root"`
Value uint64 `json:"value"`
Version uint64 `json:"version"`
}
// depositInfoV3 is an ethdo V3 deposit structure.
type depositInfoV3 struct {
Name string `json:"name,omitempty"`
Account string `json:"account,omitempty"`
PublicKey string `json:"pubkey"`
WithdrawalCredentials string `json:"withdrawal_credentials"`
Signature string `json:"signature"`
DepositDataRoot string `json:"deposit_data_root"`
DepositMessageRoot string `json:"deposit_message_root"`
ForkVersion string `json:"fork_version"`
Amount uint64 `json:"amount"`
Version uint64 `json:"version"`
}
// depositInfoCLI is a deposit structure from the eth2 deposit CLI.
type depositInfoCLI struct {
PublicKey string `json:"pubkey"`
WithdrawalCredentials string `json:"withdrawal_credentials"`
Signature string `json:"signature"`
DepositDataRoot string `json:"deposit_data_root"`
DepositMessageRoot string `json:"deposit_message_root"`
ForkVersion string `json:"fork_version"`
Amount uint64 `json:"amount"`
}
func DepositInfoFromJSON(input []byte) ([]*DepositInfo, error) {
// Work out the type of data that we're dealing with, and decode it appropriately.
depositInfo, err := tryRawTxData(input)
if err != nil {
depositInfo, err = tryV3DepositInfoFromJSON(input)
if err != nil {
depositInfo, err = tryV1DepositInfoFromJSON(input)
if err != nil {
depositInfo, err = tryCLIDepositInfoFromJSON(input)
if err != nil {
// Give up
return nil, errors.New("unknown deposit data format")
}
}
}
}
if len(depositInfo) == 0 {
return nil, errors.New("no deposits supplied")
}
for i := range depositInfo {
if len(depositInfo[i].PublicKey) == 0 {
return nil, fmt.Errorf("no public key for deposit %d", i)
}
if len(depositInfo[i].DepositDataRoot) == 0 {
return nil, fmt.Errorf("no data root for deposit %d", i)
}
if len(depositInfo[i].Signature) == 0 {
return nil, fmt.Errorf("no signature for deposit %d", i)
}
if len(depositInfo[i].WithdrawalCredentials) == 0 {
return nil, fmt.Errorf("no withdrawal credentials for deposit %d", i)
}
}
return depositInfo, nil
}
func tryV3DepositInfoFromJSON(data []byte) ([]*DepositInfo, error) {
var depositData []*depositInfoV3
err := json.Unmarshal(data, &depositData)
if err != nil {
return nil, err
}
depositInfos := make([]*DepositInfo, len(depositData))
for i, deposit := range depositData {
if deposit.Version != 3 {
return nil, errors.New("incorrect V3 deposit version")
}
publicKey, err := hex.DecodeString(strings.TrimPrefix(deposit.PublicKey, "0x"))
if err != nil {
return nil, errors.New("public key invalid")
}
withdrawalCredentials, err := hex.DecodeString(strings.TrimPrefix(deposit.WithdrawalCredentials, "0x"))
if err != nil {
return nil, errors.New("withdrawal credentials invalid")
}
signature, err := hex.DecodeString(strings.TrimPrefix(deposit.Signature, "0x"))
if err != nil {
return nil, errors.New("signature invalid")
}
depositDataRoot, err := hex.DecodeString(strings.TrimPrefix(deposit.DepositDataRoot, "0x"))
if err != nil {
return nil, errors.New("deposit data root invalid")
}
depositMessageRoot, err := hex.DecodeString(strings.TrimPrefix(deposit.DepositMessageRoot, "0x"))
if err != nil {
return nil, errors.New("deposit message root invalid")
}
forkVersion, err := hex.DecodeString(strings.TrimPrefix(deposit.ForkVersion, "0x"))
if err != nil {
return nil, errors.New("fork version invalid")
}
depositInfos[i] = &DepositInfo{
Name: deposit.Name,
Account: deposit.Account,
PublicKey: publicKey,
WithdrawalCredentials: withdrawalCredentials,
Signature: signature,
DepositDataRoot: depositDataRoot,
DepositMessageRoot: depositMessageRoot,
ForkVersion: forkVersion,
Amount: deposit.Amount,
Version: 3,
}
}
return depositInfos, nil
}
func tryCLIDepositInfoFromJSON(data []byte) ([]*DepositInfo, error) {
var depositData []*depositInfoCLI
err := json.Unmarshal(data, &depositData)
if err != nil {
return nil, err
}
depositInfos := make([]*DepositInfo, len(depositData))
for i, deposit := range depositData {
publicKey, err := hex.DecodeString(strings.TrimPrefix(deposit.PublicKey, "0x"))
if err != nil {
return nil, errors.New("public key invalid")
}
withdrawalCredentials, err := hex.DecodeString(strings.TrimPrefix(deposit.WithdrawalCredentials, "0x"))
if err != nil {
return nil, errors.New("withdrawal credentials invalid")
}
signature, err := hex.DecodeString(strings.TrimPrefix(deposit.Signature, "0x"))
if err != nil {
return nil, errors.New("signature invalid")
}
depositDataRoot, err := hex.DecodeString(strings.TrimPrefix(deposit.DepositDataRoot, "0x"))
if err != nil {
return nil, errors.New("deposit data root invalid")
}
depositMessageRoot, err := hex.DecodeString(strings.TrimPrefix(deposit.DepositMessageRoot, "0x"))
if err != nil {
return nil, errors.New("deposit message root invalid")
}
forkVersion, err := hex.DecodeString(strings.TrimPrefix(deposit.ForkVersion, "0x"))
if err != nil {
return nil, errors.New("fork version invalid")
}
depositInfos[i] = &DepositInfo{
PublicKey: publicKey,
WithdrawalCredentials: withdrawalCredentials,
Signature: signature,
DepositDataRoot: depositDataRoot,
DepositMessageRoot: depositMessageRoot,
ForkVersion: forkVersion,
Amount: deposit.Amount,
Version: 3,
}
}
return depositInfos, nil
}
func tryV1DepositInfoFromJSON(data []byte) ([]*DepositInfo, error) {
var depositData []*depositInfoV1
err := json.Unmarshal(data, &depositData)
if err != nil {
return nil, err
}
depositInfos := make([]*DepositInfo, len(depositData))
for i, deposit := range depositData {
if deposit.Version < 1 || deposit.Version > 2 {
return nil, errors.New("incorrect deposit version")
}
publicKey, err := hex.DecodeString(strings.TrimPrefix(deposit.PublicKey, "0x"))
if err != nil {
return nil, errors.New("public key invalid")
}
withdrawalCredentials, err := hex.DecodeString(strings.TrimPrefix(deposit.WithdrawalCredentials, "0x"))
if err != nil {
return nil, errors.New("withdrawal credentials invalid")
}
signature, err := hex.DecodeString(strings.TrimPrefix(deposit.Signature, "0x"))
if err != nil {
return nil, errors.New("signature invalid")
}
depositDataRoot, err := hex.DecodeString(strings.TrimPrefix(deposit.DepositDataRoot, "0x"))
if err != nil {
return nil, errors.New("deposit data root invalid")
}
depositInfos[i] = &DepositInfo{
Name: deposit.Name,
Account: deposit.Account,
PublicKey: publicKey,
WithdrawalCredentials: withdrawalCredentials,
Signature: signature,
DepositDataRoot: depositDataRoot,
Amount: deposit.Value,
Version: 3,
}
}
return depositInfos, nil
}
func tryRawTxData(data []byte) ([]*DepositInfo, error) {
txData, err := hex.DecodeString(strings.TrimPrefix(string(data), "0x"))
if err != nil {
return nil, errors.New("public key invalid")
}
depositInfos := make([]*DepositInfo, 1)
if len(txData) != 420 {
return nil, errors.New("invalid transaction length")
}
if !bytes.Equal(txData[0:4], []byte{0x22, 0x89, 0x51, 0x18}) {
return nil, errors.New("invalid function signature")
}
depositInfos[0] = &DepositInfo{
PublicKey: txData[164:212],
WithdrawalCredentials: txData[260:292],
Signature: txData[324:420],
DepositDataRoot: txData[100:132],
}
return depositInfos, nil
}