mirror of
https://github.com/wealdtech/ethdo.git
synced 2026-01-09 22:18:01 -05:00
Compare commits
22 Commits
v1.5.0-bet
...
v1.5.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85c7c7fc55 | ||
|
|
05406e8d81 | ||
|
|
70d451cea5 | ||
|
|
c270d7a2f7 | ||
|
|
115d037948 | ||
|
|
b34a633e53 | ||
|
|
d1b989a711 | ||
|
|
8078359eab | ||
|
|
7de8dc7424 | ||
|
|
987dbd26c6 | ||
|
|
1f0aeac9b4 | ||
|
|
37cab43242 | ||
|
|
6f94e599ac | ||
|
|
86bc3d5418 | ||
|
|
8cc45f0b4b | ||
|
|
77df1bba43 | ||
|
|
1d4e52cb49 | ||
|
|
c1e124fbd3 | ||
|
|
91bc6ec86d | ||
|
|
cd55abc9bf | ||
|
|
44478e147e | ||
|
|
61b73b0f61 |
@@ -1,8 +1,7 @@
|
||||
name: Go
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
# branches: [ master ]
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
@@ -29,12 +28,6 @@ jobs:
|
||||
dep ensure
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: go build -v .
|
||||
|
||||
- name: Test
|
||||
run: go test -v .
|
||||
|
||||
- name: Set env
|
||||
run: |
|
||||
echo '::set-env name=GO111MODULE::on'
|
||||
@@ -49,12 +42,18 @@ jobs:
|
||||
echo "::set-env name=RELEASE_VERSION::${RELEASE_VERSION}"
|
||||
echo "::set-output name=RELEASE_VERSION::${RELEASE_VERSION}"
|
||||
|
||||
- name: Build
|
||||
run: go build -v -ldflags="-X github.com/wealdtech/ethdo/cmd.ReleaseVersion=${RELEASE_VERSION}" .
|
||||
|
||||
- name: Test
|
||||
run: go test -v .
|
||||
|
||||
- name: Fetch xgo
|
||||
run: |
|
||||
go get github.com/suburbandad/xgo
|
||||
|
||||
- name: Cross-compile
|
||||
run: xgo -v -x --targets="linux/amd64,linux/arm64,windows/amd64" github.com/wealdtech/ethdo
|
||||
run: xgo -v -x -ldflags="-X github.com/wealdtech/ethdo/cmd.ReleaseVersion=${RELEASE_VERSION}" --targets="linux/amd64,linux/arm64,windows/amd64" github.com/wealdtech/ethdo
|
||||
|
||||
- name: Create windows zip file
|
||||
run: |
|
||||
@@ -79,8 +78,8 @@ jobs:
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release ${{ env.RELEASE_VERSION }}
|
||||
draft: true
|
||||
prerelease: true
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
- name: Upload windows zip file
|
||||
id: upload-release-asset-windows
|
||||
27
README.md
27
README.md
@@ -10,7 +10,9 @@ A command-line tool for managing common tasks in Ethereum 2.
|
||||
## Table of Contents
|
||||
|
||||
- [Install](#install)
|
||||
- [Binaries](#binaries)
|
||||
- [Docker](#docker)
|
||||
- [Source](#source)
|
||||
- [Usage](#usage)
|
||||
- [Maintainers](#maintainers)
|
||||
- [Contribute](#contribute)
|
||||
@@ -18,6 +20,19 @@ A command-line tool for managing common tasks in Ethereum 2.
|
||||
|
||||
## Install
|
||||
|
||||
### Binaries
|
||||
|
||||
Binaries for the latest version of `ethdo` can be obtained from [the releases page](https://github.com/wealdtech/ethdo/releases).
|
||||
|
||||
### Docker
|
||||
|
||||
You can obtain the latest version of `ethdo` using docker with:
|
||||
|
||||
```
|
||||
docker pull wealdtech/ethdo
|
||||
```
|
||||
|
||||
### Source
|
||||
`ethdo` is a standard Go program which can be installed with:
|
||||
|
||||
```sh
|
||||
@@ -28,15 +43,7 @@ Note that `ethdo` requires at least version 1.13 of go to operate. The version
|
||||
|
||||
If this does not work please see the [troubleshooting](https://github.com/wealdtech/ethdo/blob/master/docs/troubleshooting.md) page.
|
||||
|
||||
### Docker
|
||||
|
||||
You can obtain the latest version of `ethdo` using docker with:
|
||||
|
||||
```
|
||||
docker pull wealdtech/ethdo
|
||||
```
|
||||
|
||||
Or build `ethdo` using docker:
|
||||
The docker image can be build locally with:
|
||||
|
||||
```sh
|
||||
docker build -t ethdo .
|
||||
@@ -142,6 +149,8 @@ There is a [HOWTO](https://github.com/wealdtech/ethdo/blob/master/docs/howto.md)
|
||||
|
||||
Jim McDonald: [@mcdee](https://github.com/mcdee).
|
||||
|
||||
Special thanks to [@SuburbanDad](https://github.com/SuburbanDad) for updating xgo to allow for cross-compilation of `ethdo` releaes.
|
||||
|
||||
## Contribute
|
||||
|
||||
Contributions welcome. Please check out [the issues](https://github.com/wealdtech/ethdo/issues).
|
||||
|
||||
@@ -33,9 +33,12 @@ var accountCreateCmd = &cobra.Command{
|
||||
|
||||
In quiet mode this will return 0 if the account is created successfully, otherwise 1.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||
defer cancel()
|
||||
|
||||
assert(viper.GetString("account") != "", "--account is required")
|
||||
|
||||
wallet, err := openWallet()
|
||||
wallet, err := walletFromInput(ctx)
|
||||
errCheck(err, "Failed to access wallet")
|
||||
outputIf(debug, fmt.Sprintf("Opened wallet %q of type %s", wallet.Name(), wallet.Type()))
|
||||
if wallet.Type() == "hierarchical deterministic" {
|
||||
@@ -43,8 +46,6 @@ In quiet mode this will return 0 if the account is created successfully, otherwi
|
||||
}
|
||||
locker, isLocker := wallet.(e2wtypes.WalletLocker)
|
||||
if isLocker {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||
defer cancel()
|
||||
errCheck(locker.Unlock(ctx, []byte(getWalletPassphrase())), "Failed to unlock wallet")
|
||||
}
|
||||
|
||||
@@ -61,6 +62,7 @@ In quiet mode this will return 0 if the account is created successfully, otherwi
|
||||
defer cancel()
|
||||
account, err = distributedCreator.CreateDistributedAccount(ctx, accountName, viper.GetUint32("participants"), viper.GetUint32("signing-threshold"), []byte(getPassphrase()))
|
||||
} else {
|
||||
// Want a standard account.
|
||||
creator, isCreator := wallet.(e2wtypes.WalletAccountCreator)
|
||||
assert(isCreator, "Wallet does not support account creation")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||
|
||||
@@ -47,13 +47,13 @@ In quiet mode this will return 0 if the account is imported successfully, otherw
|
||||
key, err := bytesutil.FromHexString(accountImportKey)
|
||||
errCheck(err, "Invalid key")
|
||||
|
||||
w, err := walletFromPath(viper.GetString("account"))
|
||||
w, err := walletFromPath(ctx, viper.GetString("account"))
|
||||
errCheck(err, "Failed to access wallet")
|
||||
|
||||
_, ok := w.(e2wtypes.WalletAccountImporter)
|
||||
assert(ok, fmt.Sprintf("wallets of type %q do not allow importing accounts", w.Type()))
|
||||
|
||||
_, err = accountFromPath(ctx, viper.GetString("account"))
|
||||
_, _, err = walletAndAccountFromPath(ctx, viper.GetString("account"))
|
||||
assert(err != nil, "Account already exists")
|
||||
|
||||
locker, isLocker := w.(e2wtypes.WalletLocker)
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
e2types "github.com/wealdtech/go-eth2-types/v2"
|
||||
util "github.com/wealdtech/go-eth2-util"
|
||||
e2wallet "github.com/wealdtech/go-eth2-wallet"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
@@ -35,19 +34,11 @@ var accountInfoCmd = &cobra.Command{
|
||||
|
||||
In quiet mode this will return 0 if the account exists, otherwise 1.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
assert(viper.GetString("account") != "", "--account is required")
|
||||
|
||||
wallet, err := openWallet()
|
||||
errCheck(err, "Failed to access wallet")
|
||||
|
||||
_, accountName, err := e2wallet.WalletAndAccountNames(viper.GetString("account"))
|
||||
errCheck(err, "Failed to obtain account name")
|
||||
|
||||
accountByNameProvider, isAccountByNameProvider := wallet.(e2wtypes.WalletAccountByNameProvider)
|
||||
assert(isAccountByNameProvider, "wallet cannot obtain accounts by name")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||
defer cancel()
|
||||
account, err := accountByNameProvider.AccountByName(ctx, accountName)
|
||||
|
||||
assert(viper.GetString("account") != "", "--account is required")
|
||||
wallet, account, err := walletAndAccountFromInput(ctx)
|
||||
errCheck(err, "Failed to obtain account")
|
||||
|
||||
// Disallow wildcards (for now)
|
||||
|
||||
@@ -33,29 +33,32 @@ var accountKeyCmd = &cobra.Command{
|
||||
|
||||
In quiet mode this will return 0 if the key can be obtained, otherwise 1.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
assert(!remote, "account keys not available with remote wallets")
|
||||
assert(viper.GetString("account") != "", "--account is required")
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||
defer cancel()
|
||||
|
||||
account, err := accountFromPath(ctx, viper.GetString("account"))
|
||||
errCheck(err, "Failed to access account")
|
||||
assert(!remote, "account keys not available with remote wallets")
|
||||
assert(viper.GetString("account") != "", "--account is required")
|
||||
|
||||
_, account, err := walletAndAccountFromInput(ctx)
|
||||
errCheck(err, "Failed to obtain account")
|
||||
|
||||
privateKeyProvider, isPrivateKeyProvider := account.(e2wtypes.AccountPrivateKeyProvider)
|
||||
assert(isPrivateKeyProvider, fmt.Sprintf("account %q does not provide its private key", viper.GetString("account")))
|
||||
|
||||
if locker, isLocker := account.(e2wtypes.AccountLocker); isLocker {
|
||||
unlocked := false
|
||||
for _, passphrase := range getPassphrases() {
|
||||
err = locker.Unlock(ctx, []byte(passphrase))
|
||||
if err == nil {
|
||||
unlocked = true
|
||||
break
|
||||
unlocked, err := locker.IsUnlocked(ctx)
|
||||
errCheck(err, "Failed to find out if account is locked")
|
||||
if !unlocked {
|
||||
for _, passphrase := range getPassphrases() {
|
||||
err = locker.Unlock(ctx, []byte(passphrase))
|
||||
if err == nil {
|
||||
unlocked = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(unlocked, "Failed to unlock account to obtain private key")
|
||||
defer errCheck(locker.Lock(context.Background()), "failed to re-lock account")
|
||||
defer relockAccount(locker)
|
||||
}
|
||||
privateKey, err := privateKeyProvider.PrivateKey(ctx)
|
||||
errCheck(err, "Failed to obtain private key")
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
e2wallet "github.com/wealdtech/go-eth2-wallet"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
@@ -31,27 +30,18 @@ var accountLockCmd = &cobra.Command{
|
||||
|
||||
In quiet mode this will return 0 if the account is locked, otherwise 1.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
assert(viper.GetString("account") != "", "--account is required")
|
||||
|
||||
wallet, err := openWallet()
|
||||
errCheck(err, "Failed to access wallet")
|
||||
|
||||
_, accountName, err := e2wallet.WalletAndAccountNames(viper.GetString("account"))
|
||||
errCheck(err, "Failed to obtain account name")
|
||||
|
||||
accountByNameProvider, isAccountByNameProvider := wallet.(e2wtypes.WalletAccountByNameProvider)
|
||||
assert(isAccountByNameProvider, "wallet cannot obtain accounts by name")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||
defer cancel()
|
||||
account, err := accountByNameProvider.AccountByName(ctx, accountName)
|
||||
|
||||
assert(viper.GetString("account") != "", "--account is required")
|
||||
|
||||
_, account, err := walletAndAccountFromInput(ctx)
|
||||
errCheck(err, "Failed to obtain account")
|
||||
|
||||
locker, isLocker := account.(e2wtypes.AccountLocker)
|
||||
assert(isLocker, "Account does not support locking")
|
||||
|
||||
ctx, cancel = context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||
err = locker.Lock(ctx)
|
||||
cancel()
|
||||
errCheck(err, "Failed to lock account")
|
||||
},
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
e2wallet "github.com/wealdtech/go-eth2-wallet"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
@@ -32,19 +31,12 @@ var accountUnlockCmd = &cobra.Command{
|
||||
|
||||
In quiet mode this will return 0 if the account is unlocked, otherwise 1.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
assert(viper.GetString("account") != "", "--account is required")
|
||||
|
||||
wallet, err := openWallet()
|
||||
errCheck(err, "Failed to access wallet")
|
||||
|
||||
_, accountName, err := e2wallet.WalletAndAccountNames(viper.GetString("account"))
|
||||
errCheck(err, "Failed to obtain account name")
|
||||
|
||||
accountByNameProvider, isAccountByNameProvider := wallet.(e2wtypes.WalletAccountByNameProvider)
|
||||
assert(isAccountByNameProvider, "wallet cannot obtain accounts by name")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||
defer cancel()
|
||||
account, err := accountByNameProvider.AccountByName(ctx, accountName)
|
||||
|
||||
assert(viper.GetString("account") != "", "--account is required")
|
||||
|
||||
_, account, err := walletAndAccountFromInput(ctx)
|
||||
errCheck(err, "Failed to obtain account")
|
||||
|
||||
locker, isLocker := account.(e2wtypes.AccountLocker)
|
||||
|
||||
@@ -23,8 +23,6 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
util "github.com/wealdtech/go-eth2-util"
|
||||
e2wallet "github.com/wealdtech/go-eth2-wallet"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
var accountWithdrawalCredentialsCmd = &cobra.Command{
|
||||
@@ -47,15 +45,7 @@ In quiet mode this will return 0 if the account exists, otherwise 1.`,
|
||||
pubKey, err = hex.DecodeString(strings.TrimPrefix(viper.GetString("pubkey"), "0x"))
|
||||
errCheck(err, "Failed to decode supplied public key")
|
||||
} else {
|
||||
wallet, err := openWallet()
|
||||
errCheck(err, "Failed to access wallet")
|
||||
|
||||
_, accountName, err := e2wallet.WalletAndAccountNames(viper.GetString("account"))
|
||||
errCheck(err, "Failed to obtain account name")
|
||||
|
||||
accountByNameProvider, isAccountByNameProvider := wallet.(e2wtypes.WalletAccountByNameProvider)
|
||||
assert(isAccountByNameProvider, "wallet cannot obtain accounts by name")
|
||||
account, err := accountByNameProvider.AccountByName(ctx, accountName)
|
||||
_, account, err := walletAndAccountFromInput(ctx)
|
||||
errCheck(err, "Failed to obtain account")
|
||||
|
||||
key, err := bestPublicKey(account)
|
||||
|
||||
@@ -60,18 +60,17 @@ In quiet mode this will return 0 if the the data can be generated correctly, oth
|
||||
deposits, err := depositDataFromJSON(depositVerifyData)
|
||||
errCheck(err, "Failed to fetch deposit data")
|
||||
|
||||
withdrawalCredentials := ""
|
||||
var withdrawalCredentials []byte
|
||||
if depositVerifyWithdrawalPubKey != "" {
|
||||
withdrawalPubKeyBytes, err := hex.DecodeString(strings.TrimPrefix(depositVerifyWithdrawalPubKey, "0x"))
|
||||
errCheck(err, "Invalid withdrawal public key")
|
||||
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")
|
||||
withdrawalBytes := util.SHA256(withdrawalPubKey.Marshal())
|
||||
withdrawalBytes[0] = 0 // BLS_WITHDRAWAL_PREFIX
|
||||
withdrawalCredentials = fmt.Sprintf("%x", withdrawalBytes)
|
||||
withdrawalCredentials = util.SHA256(withdrawalPubKey.Marshal())
|
||||
withdrawalCredentials[0] = 0 // BLS_WITHDRAWAL_PREFIX
|
||||
}
|
||||
outputIf(debug, fmt.Sprintf("Withdrawal credentials are %s", withdrawalCredentials))
|
||||
outputIf(debug, fmt.Sprintf("Withdrawal credentials are %#x", withdrawalCredentials))
|
||||
|
||||
depositValue := uint64(0)
|
||||
if depositVerifyDepositValue != "" {
|
||||
@@ -81,7 +80,7 @@ In quiet mode this will return 0 if the the data can be generated correctly, oth
|
||||
assert(depositValue >= 1000000000, "deposit value must be at least 1 Ether") // MIN_DEPOSIT_AMOUNT
|
||||
}
|
||||
|
||||
validatorPubKeys := make(map[string]bool)
|
||||
validatorPubKeys := make(map[[48]byte]bool)
|
||||
if depositVerifyValidatorPubKey != "" {
|
||||
validatorPubKeys, err = validatorPubKeysFromInput(depositVerifyValidatorPubKey)
|
||||
errCheck(err, "Failed to obtain validator public key(s))")
|
||||
@@ -89,8 +88,10 @@ 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 != "" {
|
||||
if deposit.WithdrawalCredentials != withdrawalCredentials {
|
||||
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
|
||||
}
|
||||
@@ -102,7 +103,11 @@ In quiet mode this will return 0 if the the data can be generated correctly, oth
|
||||
}
|
||||
}
|
||||
if len(validatorPubKeys) != 0 {
|
||||
if _, exists := validatorPubKeys[deposit.PublicKey]; !exists {
|
||||
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
|
||||
}
|
||||
@@ -117,8 +122,8 @@ In quiet mode this will return 0 if the the data can be generated correctly, oth
|
||||
},
|
||||
}
|
||||
|
||||
func validatorPubKeysFromInput(input string) (map[string]bool, error) {
|
||||
pubKeys := make(map[string]bool)
|
||||
func validatorPubKeysFromInput(input string) (map[[48]byte]bool, error) {
|
||||
pubKeys := make(map[[48]byte]bool)
|
||||
var err error
|
||||
var data []byte
|
||||
// Input could be a public key or a path to public keys.
|
||||
@@ -135,7 +140,9 @@ func validatorPubKeysFromInput(input string) (map[string]bool, error) {
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "invalid public key")
|
||||
}
|
||||
pubKeys[fmt.Sprintf("%x", pubKey.Marshal())] = true
|
||||
var key [48]byte
|
||||
copy(key[:], pubKey.Marshal())
|
||||
pubKeys[key] = true
|
||||
} else {
|
||||
// Assume it's a path to a file of public keys.
|
||||
data, err = ioutil.ReadFile(input)
|
||||
@@ -161,7 +168,9 @@ func validatorPubKeysFromInput(input string) (map[string]bool, error) {
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "invalid public key")
|
||||
}
|
||||
pubKeys[fmt.Sprintf("%x", pubKey.Marshal())] = true
|
||||
var key [48]byte
|
||||
copy(key[:], pubKey.Marshal())
|
||||
pubKeys[key] = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,10 +57,10 @@ func assert(condition bool, msg string) {
|
||||
|
||||
// die prints an error and quits
|
||||
func die(msg string) {
|
||||
if !quiet {
|
||||
if msg != "" && !quiet {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", msg)
|
||||
}
|
||||
os.Exit(1)
|
||||
os.Exit(_exitFailure)
|
||||
}
|
||||
|
||||
// warnCheck checks for an error and warns if it is present
|
||||
|
||||
32
cmd/exit.go
Normal file
32
cmd/exit.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright © 2019 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"
|
||||
)
|
||||
|
||||
// exitCmd represents the exit command
|
||||
var exitCmd = &cobra.Command{
|
||||
Use: "exit",
|
||||
Short: "Manage Ethereum 2 voluntary exits",
|
||||
Long: `Manage Ethereum 2 voluntary exits.`,
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(exitCmd)
|
||||
}
|
||||
|
||||
func exitFlags(cmd *cobra.Command) {
|
||||
}
|
||||
137
cmd/exitverify.go
Normal file
137
cmd/exitverify.go
Normal file
@@ -0,0 +1,137 @@
|
||||
// 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"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/wealdtech/ethdo/grpc"
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
e2types "github.com/wealdtech/go-eth2-types/v2"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
var exitVerifyPubKey string
|
||||
|
||||
var exitVerifyCmd = &cobra.Command{
|
||||
Use: "verify",
|
||||
Short: "Verify deposit data matches requirements",
|
||||
Long: `Verify deposit data matches requirements. 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.
|
||||
|
||||
In quiet mode this will return 0 if the the data can be generated correctly, otherwise 1.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||
defer cancel()
|
||||
|
||||
assert(viper.GetString("account") != "" || exitVerifyPubKey != "", "account or public key is required")
|
||||
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"))
|
||||
errCheck(err, "Failed to obtain exit data")
|
||||
|
||||
// Confirm signature is good.
|
||||
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))
|
||||
errCheck(err, "Failed to obtain genesis validators root")
|
||||
domain := e2types.Domain(e2types.DomainVoluntaryExit, data.ForkVersion, genesisValidatorsRoot)
|
||||
exit := ðpb.VoluntaryExit{
|
||||
Epoch: data.Epoch,
|
||||
ValidatorIndex: data.ValidatorIndex,
|
||||
}
|
||||
sig, err := e2types.BLSSignatureFromBytes(data.Signature)
|
||||
errCheck(err, "Invalid signature")
|
||||
verified, err := verifyStruct(account, exit, domain, sig)
|
||||
errCheck(err, "Failed to verify voluntary exit")
|
||||
assert(verified, "Voluntary exit failed to verify")
|
||||
|
||||
// TODO confirm fork version is valid (once we have a way of obtaining the current fork version).
|
||||
|
||||
outputIf(verbose, "Verified")
|
||||
os.Exit(_exitSuccess)
|
||||
},
|
||||
}
|
||||
|
||||
// obtainExitData obtains exit data from an input, could be JSON itself or a path to JSON.
|
||||
func obtainExitData(input string) (*validatorExitData, error) {
|
||||
var err error
|
||||
var data []byte
|
||||
// Input could be JSON or a path to JSON
|
||||
if strings.HasPrefix(input, "{") {
|
||||
// Looks like JSON
|
||||
data = []byte(input)
|
||||
} else {
|
||||
// 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")
|
||||
}
|
||||
}
|
||||
exitData := &validatorExitData{}
|
||||
err = json.Unmarshal([]byte(data), exitData)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "data is not valid JSON")
|
||||
}
|
||||
|
||||
return exitData, nil
|
||||
}
|
||||
|
||||
// exitVerifyAccount obtains the account for the exitVerify command.
|
||||
func exitVerifyAccount(ctx context.Context) (e2wtypes.Account, error) {
|
||||
var account e2wtypes.Account
|
||||
var err error
|
||||
if viper.GetString("account") != "" {
|
||||
_, account, err = walletAndAccountFromPath(ctx, viper.GetString("account"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain account")
|
||||
}
|
||||
} else {
|
||||
pubKeyBytes, err := hex.DecodeString(strings.TrimPrefix(exitVerifyPubKey, "0x"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("failed to decode public key %s", exitVerifyPubKey))
|
||||
}
|
||||
account, err = util.NewScratchAccount(nil, pubKeyBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("invalid public key %s", exitVerifyPubKey))
|
||||
}
|
||||
}
|
||||
return account, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
exitCmd.AddCommand(exitVerifyCmd)
|
||||
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 {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
47
cmd/networks.go
Normal file
47
cmd/networks.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// 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/wealdtech/ethdo/grpc"
|
||||
)
|
||||
|
||||
// networks is a map of deposit contract addresses to networks.
|
||||
var networks = map[string]string{
|
||||
"16e82d77882a663454ef92806b7deca1d394810f": "Altona",
|
||||
"0f0f0fc0530007361933eab5db97d09acdd6c1c8": "Onyx",
|
||||
"07b39f4fde4a38bace212b546dac87c58dfe3fdc": "Medalla",
|
||||
}
|
||||
|
||||
// network returns the name of the network, if known.
|
||||
func network() string {
|
||||
if err := connect(); err != nil {
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
depositContractAddress, err := grpc.FetchDepositContractAddress(eth2GRPCConn)
|
||||
if err != nil {
|
||||
return "Unknown"
|
||||
}
|
||||
outputIf(debug, fmt.Sprintf("Deposit contract is %x", depositContractAddress))
|
||||
|
||||
depositContract := fmt.Sprintf("%x", depositContractAddress)
|
||||
if network, exists := networks[depositContract]; exists {
|
||||
return network
|
||||
} else {
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
117
cmd/root.go
117
cmd/root.go
@@ -233,12 +233,36 @@ func outputIf(condition bool, msg string) {
|
||||
}
|
||||
}
|
||||
|
||||
// walletFromInput obtains a wallet given the information in the viper variable
|
||||
// "account", or if not present the viper variable "wallet".
|
||||
func walletFromInput(ctx context.Context) (e2wtypes.Wallet, error) {
|
||||
if viper.GetString("account") != "" {
|
||||
return walletFromPath(ctx, viper.GetString("account"))
|
||||
}
|
||||
return walletFromPath(ctx, viper.GetString("wallet"))
|
||||
}
|
||||
|
||||
// walletFromPath obtains a wallet given a path specification.
|
||||
func walletFromPath(path string) (e2wtypes.Wallet, error) {
|
||||
func walletFromPath(ctx context.Context, path string) (e2wtypes.Wallet, error) {
|
||||
walletName, _, err := e2wallet.WalletAndAccountNames(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if viper.GetString("remote") != "" {
|
||||
assert(viper.GetString("client-cert") != "", "remote connections require client-cert")
|
||||
assert(viper.GetString("client-key") != "", "remote connections require client-key")
|
||||
credentials, err := dirk.ComposeCredentials(ctx, viper.GetString("client-cert"), viper.GetString("client-key"), viper.GetString("server-ca-cert"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to build dirk credentials")
|
||||
}
|
||||
|
||||
endpoints, err := remotesToEndpoints([]string{viper.GetString("remote")})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse remote servers")
|
||||
}
|
||||
|
||||
return dirk.OpenWallet(ctx, walletName, credentials, endpoints)
|
||||
}
|
||||
wallet, err := e2wallet.OpenWallet(walletName)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "failed to decrypt wallet") {
|
||||
@@ -249,18 +273,23 @@ func walletFromPath(path string) (e2wtypes.Wallet, error) {
|
||||
return wallet, nil
|
||||
}
|
||||
|
||||
// accountFromPath obtains an account given a path specification.
|
||||
func accountFromPath(ctx context.Context, path string) (e2wtypes.Account, error) {
|
||||
wallet, err := walletFromPath(path)
|
||||
// walletAndAccountFromInput obtains the wallet and account given the information in the viper variable "account".
|
||||
func walletAndAccountFromInput(ctx context.Context) (e2wtypes.Wallet, e2wtypes.Account, error) {
|
||||
return walletAndAccountFromPath(ctx, viper.GetString("account"))
|
||||
}
|
||||
|
||||
// walletAndAccountFromPath obtains the wallet and account given a path specification.
|
||||
func walletAndAccountFromPath(ctx context.Context, path string) (e2wtypes.Wallet, e2wtypes.Account, error) {
|
||||
wallet, err := walletFromPath(ctx, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, errors.Wrap(err, "faild to open wallet for account")
|
||||
}
|
||||
_, accountName, err := e2wallet.WalletAndAccountNames(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, errors.Wrap(err, "failed to obtain accout name")
|
||||
}
|
||||
if accountName == "" {
|
||||
return nil, errors.New("no account name")
|
||||
return nil, nil, errors.New("no account name")
|
||||
}
|
||||
|
||||
if wallet.Type() == "hierarchical deterministic" && strings.HasPrefix(accountName, "m/") {
|
||||
@@ -270,23 +299,34 @@ func accountFromPath(ctx context.Context, path string) (e2wtypes.Account, error)
|
||||
if isLocker {
|
||||
err = locker.Unlock(ctx, []byte(viper.GetString("wallet-passphrase")))
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid wallet passphrase")
|
||||
return nil, nil, errors.New("failed to unlock wallet")
|
||||
}
|
||||
defer errCheck(locker.Lock(context.Background()), "failed to re-lock account")
|
||||
defer relockAccount(locker)
|
||||
}
|
||||
}
|
||||
|
||||
accountByNameProvider, isAccountByNameProvider := wallet.(e2wtypes.WalletAccountByNameProvider)
|
||||
if !isAccountByNameProvider {
|
||||
return nil, errors.New("wallet cannot obtain accounts by name")
|
||||
return nil, nil, errors.New("wallet cannot obtain accounts by name")
|
||||
}
|
||||
return accountByNameProvider.AccountByName(ctx, accountName)
|
||||
account, err := accountByNameProvider.AccountByName(ctx, accountName)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "failed to obtain account")
|
||||
}
|
||||
return wallet, account, nil
|
||||
}
|
||||
|
||||
// accountsFromPath obtains 0 or more accounts given a path specification.
|
||||
func accountsFromPath(ctx context.Context, wallet e2wtypes.Wallet, accountSpec string) ([]e2wtypes.Account, error) {
|
||||
accounts := make([]e2wtypes.Account, 0)
|
||||
// walletAndAccountsFromPath obtains the wallet and matching accounts given a path specification.
|
||||
func walletAndAccountsFromPath(ctx context.Context, path string) (e2wtypes.Wallet, []e2wtypes.Account, error) {
|
||||
wallet, err := walletFromPath(ctx, path)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "faild to open wallet for account")
|
||||
}
|
||||
|
||||
_, accountSpec, err := e2wallet.WalletAndAccountNames(path)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "failed to obtain account specification")
|
||||
}
|
||||
if accountSpec == "" {
|
||||
accountSpec = "^.*$"
|
||||
} else {
|
||||
@@ -294,6 +334,7 @@ func accountsFromPath(ctx context.Context, wallet e2wtypes.Wallet, accountSpec s
|
||||
}
|
||||
re := regexp.MustCompile(accountSpec)
|
||||
|
||||
accounts := make([]e2wtypes.Account, 0)
|
||||
for account := range wallet.Accounts(ctx) {
|
||||
if re.Match([]byte(account.Name())) {
|
||||
accounts = append(accounts, account)
|
||||
@@ -305,11 +346,16 @@ func accountsFromPath(ctx context.Context, wallet e2wtypes.Wallet, accountSpec s
|
||||
return accounts[i].Name() < accounts[j].Name()
|
||||
})
|
||||
|
||||
return accounts, nil
|
||||
return wallet, accounts, nil
|
||||
}
|
||||
|
||||
// connect connects to an Ethereum 2 endpoint.
|
||||
func connect() error {
|
||||
if eth2GRPCConn != nil {
|
||||
// Already connected.
|
||||
return nil
|
||||
}
|
||||
|
||||
connection := ""
|
||||
if viper.GetString("connection") != "" {
|
||||
connection = viper.GetString("connection")
|
||||
@@ -364,42 +410,7 @@ func remotesToEndpoints(remotes []string) ([]*dirk.Endpoint, error) {
|
||||
return endpoints, nil
|
||||
}
|
||||
|
||||
// Oepn a wallet, local or remote.
|
||||
func openWallet() (e2wtypes.Wallet, error) {
|
||||
var err error
|
||||
// Obtain the name of the wallet.
|
||||
walletName := viper.GetString("wallet")
|
||||
if walletName == "" {
|
||||
walletName, _, err = e2wallet.WalletAndAccountNames(viper.GetString("account"))
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain wallet name")
|
||||
}
|
||||
if walletName == "" {
|
||||
return nil, errors.New("no wallet name provided")
|
||||
}
|
||||
|
||||
return openNamedWallet(walletName)
|
||||
}
|
||||
|
||||
// Open a named wallet, local or remote.
|
||||
func openNamedWallet(walletName string) (e2wtypes.Wallet, error) {
|
||||
if viper.GetString("remote") != "" {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||
defer cancel()
|
||||
assert(viper.GetString("client-cert") != "", "remote connections require client-cert")
|
||||
assert(viper.GetString("client-key") != "", "remote connections require client-key")
|
||||
credentials, err := dirk.ComposeCredentials(ctx, viper.GetString("client-cert"), viper.GetString("client-key"), viper.GetString("server-ca-cert"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to build dirk credentials")
|
||||
}
|
||||
|
||||
endpoints, err := remotesToEndpoints([]string{viper.GetString("remote")})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse remote servers")
|
||||
}
|
||||
|
||||
return dirk.OpenWallet(ctx, walletName, credentials, endpoints)
|
||||
}
|
||||
return walletFromPath(walletName)
|
||||
// relockAccount locks an account; generally called as a defer after an account is unlocked.
|
||||
func relockAccount(locker e2wtypes.AccountLocker) {
|
||||
errCheck(locker.Lock(context.Background()), "failed to re-lock account")
|
||||
}
|
||||
|
||||
@@ -22,8 +22,6 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
"github.com/wealdtech/go-bytesutil"
|
||||
e2types "github.com/wealdtech/go-eth2-types/v2"
|
||||
e2wallet "github.com/wealdtech/go-eth2-wallet"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
// signatureSignCmd represents the signature sign command
|
||||
@@ -53,17 +51,7 @@ In quiet mode this will return 0 if the data can be signed, otherwise 1.`,
|
||||
outputIf(debug, fmt.Sprintf("Domain is %#x", domain))
|
||||
|
||||
assert(viper.GetString("account") != "", "--account is required")
|
||||
|
||||
wallet, err := openWallet()
|
||||
errCheck(err, "Failed to access wallet")
|
||||
|
||||
_, accountName, err := e2wallet.WalletAndAccountNames(viper.GetString("account"))
|
||||
errCheck(err, "Failed to obtain account name")
|
||||
|
||||
accountByNameProvider, isAccountByNameProvider := wallet.(e2wtypes.WalletAccountByNameProvider)
|
||||
assert(isAccountByNameProvider, "wallet does not support obtaining accounts by name")
|
||||
|
||||
account, err := accountByNameProvider.AccountByName(ctx, accountName)
|
||||
_, account, err := walletAndAccountFromInput(ctx)
|
||||
errCheck(err, "Failed to obtain account")
|
||||
|
||||
var fixedSizeData [32]byte
|
||||
|
||||
@@ -15,15 +15,17 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/pkg/errors"
|
||||
"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"
|
||||
e2wallet "github.com/wealdtech/go-eth2-wallet"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
@@ -58,49 +60,44 @@ In quiet mode this will return 0 if the data can be signed, otherwise 1.`,
|
||||
assert(len(domain) == 32, "Domain data invalid")
|
||||
}
|
||||
|
||||
var pubKey e2types.PublicKey
|
||||
assert(signatureVerifySigner != "" || viper.GetString("account") != "", "Either --signer or --account should be supplied")
|
||||
if viper.GetString("account") != "" {
|
||||
wallet, err := openWallet()
|
||||
errCheck(err, "Failed to access wallet")
|
||||
_, accountName, err := e2wallet.WalletAndAccountNames(viper.GetString("account"))
|
||||
errCheck(err, "Failed to obtain account name")
|
||||
account, err := signatureVerifyAccount()
|
||||
errCheck(err, "Failed to obtain account")
|
||||
outputIf(debug, fmt.Sprintf("Public key is %#x", account.PublicKey().Marshal()))
|
||||
|
||||
accountByNameProvider, isAccountByNameProvider := wallet.(e2wtypes.WalletAccountByNameProvider)
|
||||
assert(isAccountByNameProvider, "wallet cannot obtain accounts by name")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||
defer cancel()
|
||||
account, err := accountByNameProvider.AccountByName(ctx, accountName)
|
||||
errCheck(err, "Failed to obtain account")
|
||||
pubKey, err = bestPublicKey(account)
|
||||
errCheck(err, "Failed to obtain account's public key")
|
||||
} else {
|
||||
pubKeyBytes, err := bytesutil.FromHexString(signatureVerifySigner)
|
||||
errCheck(err, "Invalid public key")
|
||||
pubKey, err = e2types.BLSPublicKeyFromBytes(pubKeyBytes)
|
||||
errCheck(err, "Invalid public key")
|
||||
}
|
||||
outputIf(debug, fmt.Sprintf("Public key is %#x", pubKey.Marshal()))
|
||||
container := &signingContainer{
|
||||
Root: data,
|
||||
Domain: domain,
|
||||
}
|
||||
outputIf(debug, fmt.Sprintf("Data root is %#x", data))
|
||||
outputIf(debug, fmt.Sprintf("Domain is %#x", domain))
|
||||
root, err := ssz.HashTreeRoot(container)
|
||||
errCheck(err, "Failed to create signing root")
|
||||
outputIf(debug, fmt.Sprintf("Signing root is %#x", root))
|
||||
var root [32]byte
|
||||
copy(root[:], data)
|
||||
verified, err := verifyRoot(account, root, domain, signature)
|
||||
errCheck(err, "Failed to verify data")
|
||||
assert(verified, "Failed to verify")
|
||||
|
||||
verified := signature.Verify(root[:], pubKey)
|
||||
if !verified {
|
||||
outputIf(!quiet, "Not verified")
|
||||
os.Exit(_exitFailure)
|
||||
}
|
||||
outputIf(!quiet, "Verified")
|
||||
outputIf(verbose, "Verified")
|
||||
os.Exit(_exitSuccess)
|
||||
},
|
||||
}
|
||||
|
||||
// signatureVerifyAccount obtains the account for the signature verify command.
|
||||
func signatureVerifyAccount() (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 {
|
||||
pubKeyBytes, err := hex.DecodeString(strings.TrimPrefix(signatureVerifySigner, "0x"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("failed to decode public key %s", signatureVerifySigner))
|
||||
}
|
||||
account, err = util.NewScratchAccount(nil, pubKeyBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("invalid public key %s", signatureVerifySigner))
|
||||
}
|
||||
}
|
||||
return account, nil
|
||||
}
|
||||
func init() {
|
||||
signatureCmd.AddCommand(signatureVerifyCmd)
|
||||
signatureFlags(signatureVerifyCmd)
|
||||
|
||||
@@ -36,6 +36,17 @@ func signStruct(account wtypes.Account, data interface{}, domain []byte) (e2type
|
||||
return signRoot(account, objRoot, domain)
|
||||
}
|
||||
|
||||
// verifyStruct verifies the signature of an arbitrary structure.
|
||||
func verifyStruct(account wtypes.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 {
|
||||
@@ -64,6 +75,21 @@ func signRoot(account wtypes.Account, root [32]byte, domain []byte) (e2types.Sig
|
||||
return sign(account, signingRoot[:])
|
||||
}
|
||||
|
||||
func verifyRoot(account wtypes.Account, root [32]byte, domain []byte, signature e2types.Signature) (bool, error) {
|
||||
// Build the signing data manually.
|
||||
container := &signingContainer{
|
||||
Root: root[:],
|
||||
Domain: domain,
|
||||
}
|
||||
outputIf(debug, fmt.Sprintf("Signing container:\n root: %#x\n domain: %#x", container.Root, 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)
|
||||
}
|
||||
|
||||
func signGeneric(account wtypes.Account, data []byte, domain []byte) (e2types.Signature, error) {
|
||||
alreadyUnlocked, err := unlock(account)
|
||||
if err != nil {
|
||||
@@ -113,6 +139,15 @@ func sign(account wtypes.Account, data []byte) (e2types.Signature, error) {
|
||||
return signature, err
|
||||
}
|
||||
|
||||
// verify the signature of arbitrary data.
|
||||
func verify(account wtypes.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)
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
"github.com/wealdtech/ethdo/grpc"
|
||||
e2types "github.com/wealdtech/go-eth2-types/v2"
|
||||
util "github.com/wealdtech/go-eth2-util"
|
||||
e2wallet "github.com/wealdtech/go-eth2-wallet"
|
||||
string2eth "github.com/wealdtech/go-string2eth"
|
||||
)
|
||||
|
||||
@@ -54,12 +53,8 @@ In quiet mode this will return 0 if the the data can be generated correctly, oth
|
||||
defer cancel()
|
||||
|
||||
assert(validatorDepositDataValidatorAccount != "", "--validatoraccount is required")
|
||||
validatorWalletName, validatorAccountSpec, err := e2wallet.WalletAndAccountNames(validatorDepositDataValidatorAccount)
|
||||
errCheck(err, "Failed to obtain wallet and account names")
|
||||
validatorWallet, err := openNamedWallet(validatorWalletName)
|
||||
errCheck(err, "Failed to obtain validator wallet")
|
||||
validatorAccounts, err := accountsFromPath(ctx, validatorWallet, validatorAccountSpec)
|
||||
errCheck(err, "Failed to obtain validator account")
|
||||
validatorWallet, validatorAccounts, err := walletAndAccountsFromPath(ctx, validatorDepositDataValidatorAccount)
|
||||
errCheck(err, "Failed to obtain validator accounts")
|
||||
assert(len(validatorAccounts) > 0, "Failed to obtain validator account")
|
||||
|
||||
for _, validatorAccount := range validatorAccounts {
|
||||
@@ -72,7 +67,7 @@ In quiet mode this will return 0 if the the data can be generated correctly, oth
|
||||
assert(validatorDepositDataWithdrawalAccount != "" || validatorDepositDataWithdrawalPubKey != "", "--withdrawalaccount or --withdrawalpubkey is required")
|
||||
var withdrawalCredentials []byte
|
||||
if validatorDepositDataWithdrawalAccount != "" {
|
||||
withdrawalAccount, err := accountFromPath(ctx, validatorDepositDataWithdrawalAccount)
|
||||
_, withdrawalAccount, err := walletAndAccountFromPath(ctx, validatorDepositDataWithdrawalAccount)
|
||||
errCheck(err, "Failed to obtain withdrawal account")
|
||||
pubKey, err := bestPublicKey(withdrawalAccount)
|
||||
errCheck(err, "Withdrawal account does not provide a public key")
|
||||
|
||||
@@ -52,19 +52,20 @@ In quiet mode this will return 0 if the transaction has been generated, otherwis
|
||||
err := connect()
|
||||
errCheck(err, "Failed to obtain connect to Ethereum 2 beacon chain node")
|
||||
|
||||
exit, signature := validatorExitHandleInput(ctx)
|
||||
validatorExitHandleExit(ctx, exit, signature)
|
||||
exit, signature, forkVersion := validatorExitHandleInput(ctx)
|
||||
validatorExitHandleExit(ctx, exit, signature, forkVersion)
|
||||
os.Exit(_exitSuccess)
|
||||
},
|
||||
}
|
||||
|
||||
func validatorExitHandleInput(ctx context.Context) (*ethpb.VoluntaryExit, e2types.Signature) {
|
||||
func validatorExitHandleInput(ctx context.Context) (*ethpb.VoluntaryExit, e2types.Signature, []byte) {
|
||||
if validatorExitJSON != "" {
|
||||
return validatorExitHandleJSONInput(validatorExitJSON)
|
||||
}
|
||||
if viper.GetString("account") != "" {
|
||||
account, err := accountFromPath(ctx, viper.GetString("account"))
|
||||
errCheck(err, "Failed to access account")
|
||||
_, account, err := walletAndAccountFromInput(ctx)
|
||||
errCheck(err, "Failed to obtain account")
|
||||
outputIf(debug, fmt.Sprintf("Account %s obtained", account.Name()))
|
||||
return validatorExitHandleAccountInput(ctx, account)
|
||||
}
|
||||
if validatorExitKey != "" {
|
||||
@@ -75,10 +76,10 @@ func validatorExitHandleInput(ctx context.Context) (*ethpb.VoluntaryExit, e2type
|
||||
return validatorExitHandleAccountInput(ctx, account)
|
||||
}
|
||||
die("one of --json, --account or --key is required")
|
||||
return nil, nil
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func validatorExitHandleJSONInput(input string) (*ethpb.VoluntaryExit, e2types.Signature) {
|
||||
func validatorExitHandleJSONInput(input string) (*ethpb.VoluntaryExit, e2types.Signature, []byte) {
|
||||
data := &validatorExitData{}
|
||||
err := json.Unmarshal([]byte(input), data)
|
||||
errCheck(err, "Invalid JSON input")
|
||||
@@ -88,10 +89,10 @@ func validatorExitHandleJSONInput(input string) (*ethpb.VoluntaryExit, e2types.S
|
||||
}
|
||||
signature, err := e2types.BLSSignatureFromBytes(data.Signature)
|
||||
errCheck(err, "Invalid signature")
|
||||
return exit, signature
|
||||
return exit, signature, data.ForkVersion
|
||||
}
|
||||
|
||||
func validatorExitHandleAccountInput(ctx context.Context, account e2wtypes.Account) (*ethpb.VoluntaryExit, e2types.Signature) {
|
||||
func validatorExitHandleAccountInput(ctx context.Context, account e2wtypes.Account) (*ethpb.VoluntaryExit, e2types.Signature, []byte) {
|
||||
exit := ðpb.VoluntaryExit{}
|
||||
|
||||
// Beacon chain config required for later work.
|
||||
@@ -137,12 +138,12 @@ func validatorExitHandleAccountInput(ctx context.Context, account e2wtypes.Accou
|
||||
}
|
||||
|
||||
// TODO fetch current fork version from config (currently using genesis fork version)
|
||||
currentForkVersion := config["GenesisForkVersion"].([]byte)
|
||||
outputIf(debug, fmt.Sprintf("Current fork version is %x", currentForkVersion))
|
||||
forkVersion := config["GenesisForkVersion"].([]byte)
|
||||
outputIf(debug, fmt.Sprintf("Current fork version is %x", forkVersion))
|
||||
genesisValidatorsRoot, err := grpc.FetchGenesisValidatorsRoot(eth2GRPCConn)
|
||||
outputIf(debug, fmt.Sprintf("Genesis validators root is %x", genesisValidatorsRoot))
|
||||
errCheck(err, "Failed to obtain genesis validators root")
|
||||
domain := e2types.Domain(e2types.DomainVoluntaryExit, currentForkVersion, genesisValidatorsRoot)
|
||||
domain := e2types.Domain(e2types.DomainVoluntaryExit, forkVersion, genesisValidatorsRoot)
|
||||
|
||||
alreadyUnlocked, err := unlock(account)
|
||||
errCheck(err, "Failed to unlock account; please confirm passphrase is correct")
|
||||
@@ -152,16 +153,17 @@ func validatorExitHandleAccountInput(ctx context.Context, account e2wtypes.Accou
|
||||
}
|
||||
errCheck(err, "Failed to sign exit proposal")
|
||||
|
||||
return exit, signature
|
||||
return exit, signature, forkVersion
|
||||
}
|
||||
|
||||
// validatorExitHandleExit handles the exit request.
|
||||
func validatorExitHandleExit(ctx context.Context, exit *ethpb.VoluntaryExit, signature e2types.Signature) {
|
||||
func validatorExitHandleExit(ctx context.Context, exit *ethpb.VoluntaryExit, signature e2types.Signature, forkVersion []byte) {
|
||||
if validatorExitJSONOutput {
|
||||
data := &validatorExitData{
|
||||
Epoch: exit.Epoch,
|
||||
ValidatorIndex: exit.ValidatorIndex,
|
||||
Signature: signature.Marshal(),
|
||||
ForkVersion: forkVersion,
|
||||
}
|
||||
res, err := json.Marshal(data)
|
||||
errCheck(err, "Failed to generate JSON")
|
||||
@@ -192,11 +194,12 @@ type validatorExitData struct {
|
||||
Epoch uint64 `json:"epoch"`
|
||||
ValidatorIndex uint64 `json:"validator_index"`
|
||||
Signature []byte `json:"signature"`
|
||||
ForkVersion []byte `json:"fork_version"`
|
||||
}
|
||||
|
||||
// MarshalJSON implements custom JSON marshaller.
|
||||
func (d *validatorExitData) MarshalJSON() ([]byte, error) {
|
||||
return []byte(fmt.Sprintf(`{"epoch":%d,"validator_index":%d,"signature":"%#x"}`, d.Epoch, d.ValidatorIndex, d.Signature)), nil
|
||||
return []byte(fmt.Sprintf(`{"epoch":%d,"validator_index":%d,"signature":"%#x","fork_version":"%#x"}`, d.Epoch, d.ValidatorIndex, d.Signature, d.ForkVersion)), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements custom JSON unmarshaller.
|
||||
@@ -242,5 +245,19 @@ func (d *validatorExitData) UnmarshalJSON(data []byte) error {
|
||||
return errors.New("signature missing")
|
||||
}
|
||||
|
||||
if val, exists := v["fork_version"]; exists {
|
||||
forkVersionBytes, ok := val.(string)
|
||||
if !ok {
|
||||
return errors.New("fork version invalid")
|
||||
}
|
||||
forkVersion, err := hex.DecodeString(strings.TrimPrefix(forkVersionBytes, "0x"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fork version invalid")
|
||||
}
|
||||
d.ForkVersion = forkVersion
|
||||
} else {
|
||||
return errors.New("fork version missing")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -14,19 +14,24 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/wealdtech/ethdo/grpc"
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
e2wallet "github.com/wealdtech/go-eth2-wallet"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
string2eth "github.com/wealdtech/go-string2eth"
|
||||
)
|
||||
@@ -42,74 +47,76 @@ var validatorInfoCmd = &cobra.Command{
|
||||
|
||||
In quiet mode this will return 0 if the validator information can be obtained, otherwise 1.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// Sanity checking and setup.
|
||||
assert(viper.GetString("account") != "" || validatorInfoPubKey != "", "--account or --pubkey is required")
|
||||
|
||||
err := connect()
|
||||
errCheck(err, "Failed to obtain connection to Ethereum 2 beacon chain node")
|
||||
|
||||
var account e2wtypes.Account
|
||||
if viper.GetString("account") != "" {
|
||||
wallet, err := openWallet()
|
||||
errCheck(err, "Failed to access wallet")
|
||||
_, accountName, err := e2wallet.WalletAndAccountNames(viper.GetString("account"))
|
||||
errCheck(err, "Failed to obtain account name")
|
||||
accountByNameProvider, isAccountByNameProvider := wallet.(e2wtypes.WalletAccountByNameProvider)
|
||||
assert(isAccountByNameProvider, "wallet cannot obtain accounts by name")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||
defer cancel()
|
||||
account, err = accountByNameProvider.AccountByName(ctx, accountName)
|
||||
errCheck(err, "Failed to obtain account")
|
||||
} else {
|
||||
pubKeyBytes, err := hex.DecodeString(strings.TrimPrefix(validatorInfoPubKey, "0x"))
|
||||
errCheck(err, fmt.Sprintf("Failed to decode public key %s", validatorInfoPubKey))
|
||||
account, err = util.NewScratchAccount(nil, pubKeyBytes)
|
||||
errCheck(err, fmt.Sprintf("Invalid public key %s", validatorInfoPubKey))
|
||||
account, err := validatorInfoAccount()
|
||||
errCheck(err, "Failed to obtain validator account")
|
||||
|
||||
if verbose {
|
||||
network := network()
|
||||
outputIf(debug, fmt.Sprintf("Network is %s", network))
|
||||
pubKey, err := bestPublicKey(account)
|
||||
if err == nil {
|
||||
deposits, totalDeposited, err := graphData(network, pubKey.Marshal())
|
||||
if err == nil {
|
||||
fmt.Printf("Number of deposits: %d\n", deposits)
|
||||
fmt.Printf("Total deposited: %s\n", string2eth.GWeiToString(totalDeposited, true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
validatorInfo, err := grpc.FetchValidatorInfo(eth2GRPCConn, account)
|
||||
errCheck(err, "Failed to obtain validator information")
|
||||
validatorDef, err := grpc.FetchValidator(eth2GRPCConn, account)
|
||||
validator, err := grpc.FetchValidator(eth2GRPCConn, account)
|
||||
if err != nil {
|
||||
// We can live with this.
|
||||
validator = nil
|
||||
}
|
||||
if validatorInfo.Status != ethpb.ValidatorStatus_DEPOSITED &&
|
||||
validatorInfo.Status != ethpb.ValidatorStatus_UNKNOWN_STATUS {
|
||||
errCheck(err, "Failed to obtain validator definition")
|
||||
}
|
||||
|
||||
assert(validatorInfo.Status != ethpb.ValidatorStatus_UNKNOWN_STATUS, "Not known as a validator")
|
||||
|
||||
if quiet {
|
||||
os.Exit(_exitSuccess)
|
||||
}
|
||||
|
||||
outputIf(verbose, fmt.Sprintf("Epoch of data:\t\t%v", validatorInfo.Epoch))
|
||||
outputIf(verbose && validatorInfo.Status != ethpb.ValidatorStatus_DEPOSITED, fmt.Sprintf("Index:\t\t\t%v", validatorInfo.Index))
|
||||
outputIf(verbose, fmt.Sprintf("Public key:\t\t%#x", validatorInfo.PublicKey))
|
||||
fmt.Printf("Status:\t\t\t%s\n", strings.Title(strings.ToLower(validatorInfo.Status.String())))
|
||||
fmt.Printf("Balance:\t\t%s\n", string2eth.GWeiToString(validatorInfo.Balance, true))
|
||||
outputIf(verbose, fmt.Sprintf("Epoch of data: %v", validatorInfo.Epoch))
|
||||
outputIf(verbose && validatorInfo.Status != ethpb.ValidatorStatus_DEPOSITED, fmt.Sprintf("Index: %v", validatorInfo.Index))
|
||||
outputIf(verbose, fmt.Sprintf("Public key: %#x", validatorInfo.PublicKey))
|
||||
fmt.Printf("Status: %s\n", strings.Title(strings.ToLower(validatorInfo.Status.String())))
|
||||
fmt.Printf("Balance: %s\n", string2eth.GWeiToString(validatorInfo.Balance, true))
|
||||
|
||||
if validatorInfo.Status == ethpb.ValidatorStatus_ACTIVE ||
|
||||
validatorInfo.Status == ethpb.ValidatorStatus_EXITING ||
|
||||
validatorInfo.Status == ethpb.ValidatorStatus_SLASHING {
|
||||
fmt.Printf("Effective balance:\t%s\n", string2eth.GWeiToString(validatorInfo.EffectiveBalance, true))
|
||||
fmt.Printf("Effective balance: %s\n", string2eth.GWeiToString(validatorInfo.EffectiveBalance, true))
|
||||
}
|
||||
if validatorDef != nil {
|
||||
outputIf(verbose, fmt.Sprintf("Withdrawal credentials:\t%#x", validatorDef.WithdrawalCredentials))
|
||||
|
||||
if validator != nil {
|
||||
outputIf(verbose, fmt.Sprintf("Withdrawal credentials: %#x", validator.WithdrawalCredentials))
|
||||
}
|
||||
|
||||
transition := time.Unix(int64(validatorInfo.TransitionTimestamp), 0)
|
||||
transitionPassed := int64(validatorInfo.TransitionTimestamp) <= time.Now().Unix()
|
||||
switch validatorInfo.Status {
|
||||
case ethpb.ValidatorStatus_DEPOSITED:
|
||||
if validatorInfo.TransitionTimestamp != 0 {
|
||||
fmt.Printf("Inclusion in chain:\t%s\n", transition)
|
||||
fmt.Printf("Inclusion in chain: %s\n", transition)
|
||||
}
|
||||
case ethpb.ValidatorStatus_PENDING:
|
||||
fmt.Printf("Activation:\t\t%s\n", transition)
|
||||
fmt.Printf("Activation: %s\n", transition)
|
||||
case ethpb.ValidatorStatus_EXITING, ethpb.ValidatorStatus_SLASHING:
|
||||
fmt.Printf("Attesting finishes:\t%s\n", transition)
|
||||
fmt.Printf("Attesting finishes: %s\n", transition)
|
||||
case ethpb.ValidatorStatus_EXITED:
|
||||
if transitionPassed {
|
||||
fmt.Printf("Funds withdrawable:\tNow\n")
|
||||
fmt.Printf("Funds withdrawable: Now\n")
|
||||
} else {
|
||||
fmt.Printf("Funds withdrawable:\t%s\n", transition)
|
||||
fmt.Printf("Funds withdrawable: %s\n", transition)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,6 +124,76 @@ In quiet mode this will return 0 if the validator information can be obtained, o
|
||||
},
|
||||
}
|
||||
|
||||
// validatorInfoAccount obtains the account for the validator info command.
|
||||
func validatorInfoAccount() (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 {
|
||||
pubKeyBytes, err := hex.DecodeString(strings.TrimPrefix(validatorInfoPubKey, "0x"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("failed to decode public key %s", validatorInfoPubKey))
|
||||
}
|
||||
account, err = util.NewScratchAccount(nil, pubKeyBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("invalid public key %s", validatorInfoPubKey))
|
||||
}
|
||||
}
|
||||
return account, nil
|
||||
}
|
||||
|
||||
// graphData returns data from the graph about number and amount of deposits
|
||||
func graphData(network string, validatorPubKey []byte) (uint64, uint64, error) {
|
||||
subgraph := fmt.Sprintf("attestantio/eth2deposits-%s", strings.ToLower(network))
|
||||
query := fmt.Sprintf(`{"query": "{deposits(where: {validatorPubKey:\"%#x\"}) { id amount withdrawalCredentials }}"}`, validatorPubKey)
|
||||
url := fmt.Sprintf("https://api.thegraph.com/subgraphs/name/%s", subgraph)
|
||||
graphResp, err := http.Post(url, "application/json", bytes.NewBufferString(query))
|
||||
if err != nil {
|
||||
return 0, 0, errors.Wrap(err, "failed to check if there is already a deposit for this validator")
|
||||
}
|
||||
defer graphResp.Body.Close()
|
||||
body, err := ioutil.ReadAll(graphResp.Body)
|
||||
if err != nil {
|
||||
return 0, 0, errors.Wrap(err, "bad information returned from existing deposit check")
|
||||
}
|
||||
|
||||
type graphDeposit struct {
|
||||
Index string `json:"index"`
|
||||
Amount string `json:"amount"`
|
||||
WithdrawalCredentials string `json:"withdrawalCredentials"`
|
||||
}
|
||||
type graphData struct {
|
||||
Deposits []*graphDeposit `json:"deposits,omitempty"`
|
||||
}
|
||||
type graphResponse struct {
|
||||
Data *graphData `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
var response graphResponse
|
||||
if err := json.Unmarshal(body, &response); err != nil {
|
||||
return 0, 0, errors.Wrap(err, "invalid data returned from existing deposit check")
|
||||
}
|
||||
deposits := uint64(0)
|
||||
totalDeposited := uint64(0)
|
||||
if response.Data != nil && len(response.Data.Deposits) > 0 {
|
||||
for _, deposit := range response.Data.Deposits {
|
||||
deposits++
|
||||
depositAmount, err := strconv.ParseUint(deposit.Amount, 10, 64)
|
||||
if err != nil {
|
||||
return 0, 0, errors.Wrap(err, fmt.Sprintf("invalid deposit amount from pre-existing deposit %s", deposit.Amount))
|
||||
}
|
||||
totalDeposited += depositAmount
|
||||
}
|
||||
}
|
||||
return deposits, totalDeposited, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
validatorCmd.AddCommand(validatorInfoCmd)
|
||||
validatorInfoCmd.Flags().StringVar(&validatorInfoPubKey, "pubkey", "", "Public key for which to obtain status")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2019 Weald Technology Trading
|
||||
// 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
|
||||
@@ -22,15 +22,17 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var ReleaseVersion = "local build from v1.5.6"
|
||||
|
||||
// versionCmd represents the version command
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Version of Ethdo",
|
||||
Long: `Obtain the version of Ethdo. For example:
|
||||
Short: "Version of ethdo",
|
||||
Long: `Obtain the version of ethdo. For example:
|
||||
|
||||
ethdo version.`,
|
||||
ethdo version`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("1.5.0-prerelease")
|
||||
fmt.Println(ReleaseVersion)
|
||||
if viper.GetBool("verbose") {
|
||||
buildInfo, ok := dbg.ReadBuildInfo()
|
||||
if ok {
|
||||
|
||||
@@ -17,6 +17,9 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
@@ -37,30 +40,69 @@ In quiet mode this will return 0 if the wallet holds any addresses, otherwise 1.
|
||||
|
||||
assert(viper.GetString("wallet") != "", "wallet is required")
|
||||
|
||||
wallet, err := openWallet()
|
||||
errCheck(err, "Failed to access wallet")
|
||||
wallet, err := walletFromInput(ctx)
|
||||
errCheck(err, "Failed to obtain wallet")
|
||||
|
||||
hasAccounts := false
|
||||
accounts := make([]e2wtypes.Account, 0, 128)
|
||||
for account := range wallet.Accounts(ctx) {
|
||||
hasAccounts = true
|
||||
accounts = append(accounts, account)
|
||||
}
|
||||
assert(len(accounts) > 0, "")
|
||||
|
||||
if _, isPathProvider := accounts[0].(e2wtypes.AccountPathProvider); isPathProvider {
|
||||
// Order accounts by their path components.
|
||||
sort.Slice(accounts, func(i int, j int) bool {
|
||||
iBits := strings.Split(accounts[i].(e2wtypes.AccountPathProvider).Path(), "/")
|
||||
jBits := strings.Split(accounts[j].(e2wtypes.AccountPathProvider).Path(), "/")
|
||||
for index := range iBits {
|
||||
if iBits[index] == "m" && jBits[index] == "m" {
|
||||
continue
|
||||
}
|
||||
if len(jBits) <= index {
|
||||
return false
|
||||
}
|
||||
iBit, err := strconv.ParseUint(iBits[index], 10, 64)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
jBit, err := strconv.ParseUint(jBits[index], 10, 64)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if iBit < jBit {
|
||||
return true
|
||||
}
|
||||
if iBit > jBit {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return len(jBits) > len(iBits)
|
||||
})
|
||||
} else {
|
||||
// Order accounts by their name.
|
||||
sort.Slice(accounts, func(i int, j int) bool {
|
||||
return strings.Compare(accounts[i].Name(), accounts[j].Name()) < 0
|
||||
})
|
||||
}
|
||||
|
||||
for _, account := range accounts {
|
||||
outputIf(!quiet, account.Name())
|
||||
if verbose {
|
||||
fmt.Printf(" UUID: %v\n", account.ID())
|
||||
pubKeyProvider, isProvider := account.(e2wtypes.AccountPublicKeyProvider)
|
||||
if isProvider {
|
||||
if pathProvider, isProvider := account.(e2wtypes.AccountPathProvider); isProvider {
|
||||
if pathProvider.Path() != "" {
|
||||
fmt.Printf("Path: %s\n", pathProvider.Path())
|
||||
}
|
||||
}
|
||||
if pubKeyProvider, isProvider := account.(e2wtypes.AccountPublicKeyProvider); isProvider {
|
||||
fmt.Printf(" Public key: %#x\n", pubKeyProvider.PublicKey().Marshal())
|
||||
}
|
||||
compositePubKeyProvider, isProvider := account.(e2wtypes.AccountCompositePublicKeyProvider)
|
||||
if isProvider {
|
||||
if compositePubKeyProvider, isProvider := account.(e2wtypes.AccountCompositePublicKeyProvider); isProvider {
|
||||
fmt.Printf(" Composite public key: %#x\n", compositePubKeyProvider.CompositePublicKey().Marshal())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if hasAccounts {
|
||||
os.Exit(_exitSuccess)
|
||||
}
|
||||
os.Exit(_exitFailure)
|
||||
os.Exit(_exitSuccess)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,12 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
bip39 "github.com/tyler-smith/go-bip39"
|
||||
@@ -26,9 +30,6 @@ import (
|
||||
nd "github.com/wealdtech/go-eth2-wallet-nd/v2"
|
||||
)
|
||||
|
||||
var walletCreateType string
|
||||
var walletCreateSeed string
|
||||
|
||||
var walletCreateCmd = &cobra.Command{
|
||||
Use: "create",
|
||||
Short: "Create a wallet",
|
||||
@@ -43,18 +44,22 @@ In quiet mode this will return 0 if the wallet is created successfully, otherwis
|
||||
|
||||
assert(viper.GetString("remote") == "", "wallet create not available with remote wallets")
|
||||
assert(viper.GetString("wallet") != "", "--wallet is required")
|
||||
assert(walletCreateType != "", "--type is required")
|
||||
assert(viper.GetString("type") != "", "--type is required")
|
||||
|
||||
var err error
|
||||
switch strings.ToLower(walletCreateType) {
|
||||
switch strings.ToLower(viper.GetString("type")) {
|
||||
case "non-deterministic", "nd":
|
||||
assert(walletCreateSeed == "", "--seed is not allowed with non-deterministic wallets")
|
||||
assert(viper.GetString("mnemonic") == "", "--mnemonic is not allowed with non-deterministic wallets")
|
||||
err = walletCreateND(ctx, viper.GetString("wallet"))
|
||||
case "hierarchical deterministic", "hd":
|
||||
if quiet {
|
||||
fmt.Printf("Creation of hierarchical deterministic wallets prints its mnemonic, so cannot be run with the --quiet flag")
|
||||
os.Exit(_exitFailure)
|
||||
}
|
||||
assert(getWalletPassphrase() != "", "--walletpassphrase is required for hierarchical deterministic wallets")
|
||||
err = walletCreateHD(ctx, viper.GetString("wallet"), getWalletPassphrase(), walletCreateSeed)
|
||||
err = walletCreateHD(ctx, viper.GetString("wallet"), getWalletPassphrase(), viper.GetString("mnemonic"))
|
||||
case "distributed":
|
||||
assert(walletCreateSeed == "", "--seed is not allowed with distributed wallets")
|
||||
assert(viper.GetString("mnemonic") == "", "--mnemonic is not allowed with distributed wallets")
|
||||
err = walletCreateDistributed(ctx, viper.GetString("wallet"))
|
||||
default:
|
||||
die("unknown wallet type")
|
||||
@@ -75,28 +80,66 @@ func walletCreateDistributed(ctx context.Context, name string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// walletCreateND creates a hierarchical-deterministic wallet.
|
||||
func walletCreateHD(ctx context.Context, name string, passphrase string, seedPhrase string) error {
|
||||
// walletCreateHD creates a hierarchical-deterministic wallet.
|
||||
func walletCreateHD(ctx context.Context, name string, passphrase string, mnemonic string) error {
|
||||
encryptor := keystorev4.New()
|
||||
if seedPhrase != "" {
|
||||
// Create wallet from a user-supplied seed.
|
||||
var seed []byte
|
||||
seed, err := bip39.MnemonicToByteArray(seedPhrase)
|
||||
errCheck(err, "Failed to decode seed")
|
||||
// Strip checksum; last byte.
|
||||
seed = seed[:len(seed)-1]
|
||||
assert(len(seed) == 32, "Seed must have 24 words")
|
||||
_, err = hd.CreateWalletFromSeed(ctx, name, []byte(passphrase), store, encryptor, seed)
|
||||
return err
|
||||
|
||||
printMnemonic := mnemonic == ""
|
||||
mnemonicPassphrase := ""
|
||||
|
||||
if mnemonic == "" {
|
||||
// Create a new random mnemonic.
|
||||
entropy := make([]byte, 32)
|
||||
_, err := rand.Read(entropy)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to generate entropy for wallet mnemonic")
|
||||
}
|
||||
mnemonic, err = bip39.NewMnemonic(entropy)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to generate wallet mnemonic")
|
||||
}
|
||||
} else {
|
||||
// We have an existing mnemonic. If there are more than 24 words we treat the additional characters as the passphrase.
|
||||
mnemonicParts := strings.Split(mnemonic, " ")
|
||||
if len(mnemonicParts) > 24 {
|
||||
mnemonic = strings.Join(mnemonicParts[:24], " ")
|
||||
mnemonicPassphrase = strings.Join(mnemonicParts[24:], " ")
|
||||
}
|
||||
}
|
||||
// Create wallet with a random seed.
|
||||
_, err := hd.CreateWallet(ctx, name, []byte(passphrase), store, encryptor)
|
||||
|
||||
// Ensure the mnemonic is valid
|
||||
if !bip39.IsMnemonicValid(mnemonic) {
|
||||
return errors.New("mnemonic is not valid")
|
||||
}
|
||||
|
||||
// Create seed from mnemonic and passphrase.
|
||||
seed := bip39.NewSeed(mnemonic, mnemonicPassphrase)
|
||||
|
||||
_, err := hd.CreateWallet(ctx, name, []byte(passphrase), store, encryptor, seed)
|
||||
|
||||
if printMnemonic {
|
||||
fmt.Printf(`The following phrase is your mnemonic for this wallet:
|
||||
|
||||
%s
|
||||
|
||||
Anyone with access to this mnemonic can recreate the accounts in this wallet, so please store this mnemonic safely. More information about mnemonics can be found at https://support.mycrypto.com/general-knowledge/cryptography/how-do-mnemonic-phrases-work
|
||||
|
||||
Please note this mnemonic is not stored within the wallet, so cannot be retrieved or displayed again. As such, this mnemonic should be written down or otherwise protected before proceeding.
|
||||
`, mnemonic)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func init() {
|
||||
walletCmd.AddCommand(walletCreateCmd)
|
||||
walletFlags(walletCreateCmd)
|
||||
walletCreateCmd.Flags().StringVar(&walletCreateType, "type", "non-deterministic", "Type of wallet to create (non-deterministic or hierarchical deterministic)")
|
||||
walletCreateCmd.Flags().StringVar(&walletCreateSeed, "seed", "", "The 24-word seed phrase for a hierarchical deterministic wallet")
|
||||
walletCreateCmd.Flags().String("type", "non-deterministic", "Type of wallet to create (non-deterministic or hierarchical deterministic)")
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -34,7 +35,10 @@ In quiet mode this will return 0 if the wallet has been deleted, otherwise 1.`,
|
||||
assert(viper.GetString("remote") == "", "wallet delete not available with remote wallets")
|
||||
assert(viper.GetString("wallet") != "", "--wallet is required")
|
||||
|
||||
wallet, err := walletFromPath(viper.GetString("wallet"))
|
||||
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||
defer cancel()
|
||||
|
||||
wallet, err := walletFromPath(ctx, viper.GetString("wallet"))
|
||||
errCheck(err, "Failed to access wallet")
|
||||
|
||||
storeProvider, ok := wallet.(wtypes.StoreProvider)
|
||||
|
||||
@@ -41,7 +41,7 @@ In quiet mode this will return 0 if the wallet is able to be exported, otherwise
|
||||
assert(viper.GetString("wallet") != "", "--wallet is required")
|
||||
assert(walletExportPassphrase != "", "--exportpassphrase is required")
|
||||
|
||||
wallet, err := walletFromPath(viper.GetString("wallet"))
|
||||
wallet, err := walletFromPath(ctx, viper.GetString("wallet"))
|
||||
errCheck(err, "Failed to access wallet")
|
||||
|
||||
_, ok := wallet.(types.WalletExporter)
|
||||
|
||||
@@ -39,7 +39,7 @@ In quiet mode this will return 0 if the wallet exists, otherwise 1.`,
|
||||
assert(viper.GetString("remote") == "", "wallet info not available with remote wallets")
|
||||
assert(viper.GetString("wallet") != "", "--wallet is required")
|
||||
|
||||
wallet, err := walletFromPath(viper.GetString("wallet"))
|
||||
wallet, err := walletFromPath(ctx, viper.GetString("wallet"))
|
||||
errCheck(err, "unknown wallet")
|
||||
|
||||
if quiet {
|
||||
|
||||
@@ -1,77 +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 (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
bip39 "github.com/tyler-smith/go-bip39"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
var walletSeedCmd = &cobra.Command{
|
||||
Use: "seed",
|
||||
Short: "Display the seed of a wallet",
|
||||
Long: `Display the seed for an hierarchical deterministic wallet. For example:
|
||||
|
||||
ethdo wallet seed --wallet=primary
|
||||
|
||||
In quiet mode this will return 0 if the wallet is a hierarchical deterministic wallet, otherwise 1.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||
defer cancel()
|
||||
|
||||
assert(viper.GetString("remote") == "", "wallet seed not available with remote wallets")
|
||||
assert(viper.GetString("wallet") != "", "--wallet is required")
|
||||
assert(getWalletPassphrase() != "", "--walletpassphrase is required")
|
||||
|
||||
wallet, err := walletFromPath(viper.GetString("wallet"))
|
||||
errCheck(err, "Failed to access wallet")
|
||||
_, ok := wallet.(e2wtypes.WalletKeyProvider)
|
||||
assert(ok, fmt.Sprintf("wallets of type %q do not have a seed", wallet.Type()))
|
||||
|
||||
locker, isLocker := wallet.(e2wtypes.WalletLocker)
|
||||
if isLocker {
|
||||
errCheck(locker.Unlock(ctx, []byte(getWalletPassphrase())), "Failed to unlock wallet")
|
||||
}
|
||||
keyProvider, isKeyProvider := wallet.(e2wtypes.WalletKeyProvider)
|
||||
assert(isKeyProvider, "Wallet does not provide key")
|
||||
seed, err := keyProvider.Key(ctx)
|
||||
errCheck(err, "Failed to obtain wallet key")
|
||||
outputIf(debug, fmt.Sprintf("Seed is %#x", seed))
|
||||
seedStr, err := bip39.NewMnemonic(seed)
|
||||
errCheck(err, "Failed to generate seed mnemonic")
|
||||
// Re-read mnemonimc to ensure correctness.
|
||||
recalcSeed, err := bip39.MnemonicToByteArray(seedStr)
|
||||
// Drop checksum (last byte).
|
||||
errCheck(err, "Failed to recalculate seed")
|
||||
recalcSeed = recalcSeed[:len(recalcSeed)-1]
|
||||
outputIf(debug, fmt.Sprintf("Recalc seed is %#x", recalcSeed))
|
||||
errCheck(err, "Failed to recalculate seed mnemonic")
|
||||
assert(bytes.Equal(recalcSeed, seed), "Generated invalid mnemonic")
|
||||
|
||||
outputIf(!quiet, seedStr)
|
||||
os.Exit(_exitSuccess)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
walletCmd.AddCommand(walletSeedCmd)
|
||||
walletFlags(walletSeedCmd)
|
||||
}
|
||||
@@ -2,19 +2,7 @@
|
||||
|
||||
## Installing ethdo
|
||||
|
||||
1. To install `ethdo`, issue the following command:
|
||||
|
||||
```sh
|
||||
GO111MODULE=on go get github.com/wealdtech/ethdo@latest
|
||||
```
|
||||
|
||||
2. Ensure `ethdo` is installed properly by issuing the command:
|
||||
|
||||
```sh
|
||||
ethdo version
|
||||
```
|
||||
|
||||
Ensure the output matches the most recent version listed on the repository's [release history](https://github.com/wealdtech/ethdo/releases/).
|
||||
1. To install `ethdo`, use the instructions on the [main page](https://github.com/wealdtech/ethdo).
|
||||
|
||||
## Typical validating setups
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# Troubleshooting
|
||||
|
||||
## Compilation problems
|
||||
## Compilation problems on Linux
|
||||
|
||||
### gcc not found
|
||||
### cannot find -lstdc++
|
||||
|
||||
This is usually an error on linux systems. If you receive errors of this type your computer is missing some files to allow `ethdo` to build. To resolve this run the following command:
|
||||
If you receive errors of this type your computer is missing some files to allow `ethdo` to build. To resolve this, run the following command:
|
||||
|
||||
```sh
|
||||
sudo apt install build-essential libstdc++6
|
||||
@@ -13,6 +13,12 @@ sudo apt install build-essential libstdc++6
|
||||
|
||||
and then try to install `ethdo` again.
|
||||
|
||||
## Compilation problems on Windows
|
||||
|
||||
### gcc not found
|
||||
|
||||
If you receive errors of this type your computer is missing some files to allow `ethdo` to build. To resolve this install gcc by following the instructions at http://mingw-w64.org/doku.php
|
||||
|
||||
## ethdo not found after installing
|
||||
|
||||
This is usually due to an incorrectly set path. Go installs its binaries (such as `ethdo`) in a particular location. The defaults are:
|
||||
|
||||
@@ -31,7 +31,7 @@ Spending: 0x85dfc6dcee4c9da36f6473ec02fda283d6c920c641fc8e3a76113c5c227d4aeeb100
|
||||
- `wallet`: the name of the wallet to create
|
||||
- `type`: the type of wallet to create. This can be either "nd" for a non-deterministic wallet, where private keys are generated randomly, or "hd" for a hierarchical deterministic wallet, where private keys are generated from a seed and path as per [ERC-2333](https://github.com/CarlBeek/EIPs/blob/bls_path/EIPS/eip-2334.md) (defaults to "nd")
|
||||
- `walletpassphrase`: the passphrase for of the wallet. This is required for hierarchical deterministic wallets, to protect the seed
|
||||
- `seed`: for hierarchical deterministic wallets only, use a pre-defined 24-word [BIP-39 seed phrase](https://en.bitcoin.it/wiki/Seed_phrase) to create the wallet. **Warning** The same seed can be imported in to multiple wallets, in which case they will generate the same keys. Please ensure that only a single wallet is active with any single seed phrase
|
||||
- `mnemonic`: for hierarchical deterministic wallets only, use a pre-defined 24-word [BIP-39 seed phrase](https://en.bitcoin.it/wiki/Seed_phrase) to create the wallet, along with an additional "seed extension" phrase if required. **Warning** The same mnemonic can be used to create multiple wallets, in which case they will generate the same keys.
|
||||
|
||||
```sh
|
||||
$ ethdo wallet create --wallet="Personal wallet" --type="hd" --walletpassphrase="my wallet secret"
|
||||
@@ -103,17 +103,6 @@ Personal wallet
|
||||
|
||||
**N.B.** encrypted wallets will not show up in this list unless the correct passphrase for the store is supplied.
|
||||
|
||||
#### `seed`
|
||||
|
||||
`ethdo wallet seed` provides the seed for hierarchical deterministic wallets. Options include:
|
||||
- `wallet`: the name of the wallet
|
||||
- `walletpassphrase`: the passphrase for the wallet
|
||||
|
||||
```sh
|
||||
$ ethdo wallet seed --wallet="Personal wallet" --walletpassphrase="my wallet secret"
|
||||
decorate false mail domain gain later motion chair tank muffin smoke involve witness bean shell urge team solve share truly shadow decorate jeans hen
|
||||
```
|
||||
|
||||
### `account` commands
|
||||
|
||||
Account commands focus on information about local accounts, generally those used by Geth and Parity but also those from hardware devices.
|
||||
@@ -211,11 +200,10 @@ $ ethdo signature sign --data="0x08140077a94642919041503caf5cc1c89c7744a2a08d43c
|
||||
|
||||
```sh
|
||||
$ ethdo signature verify --data="0x08140077a94642919041503caf5cc1c89c7744a2a08d43cec91df1795b23ecf2" --signature="0x87c83b31081744667406a11170c5585a11195621d0d3f796bd9006ac4cb5f61c10bf8c5b3014cd4f792b143a644cae100cb3155e8b00a961287bd9e7a5e18cb3b80930708bc9074d11ff47f1e8b9dd0b633e71bcea725fc3e550fdc259c3d130" --account="Personal wallet/Operations"
|
||||
Verified
|
||||
$ ethdo signature verify --data="0x08140077a94642919041503caf5cc1c89c7744a2a08d43cec91df1795b23ecf2" --signature="0x87c83b31081744667406a11170c5585a11195621d0d3f796bd9006ac4cb5f61c10bf8c5b3014cd4f792b143a644cae100cb3155e8b00a961287bd9e7a5e18cb3b80930708bc9074d11ff47f1e8b9dd0b633e71bcea725fc3e550fdc259c3d130" --account="Personal wallet/Auctions"
|
||||
Not verified
|
||||
$ ethdo signature verify --data="0x08140077a94642919041503caf5cc1c89c7744a2a08d43cec91df1795b23ecf2" --signature="0x89abe2e544ef3eafe397db036103b1d066ba86497f36ed4ab0264162eadc89c7744a2a08d43cec91df128660e70ecbbe11031b4c2e53682d2b91e67b886429bf8fac9bad8c7b63c5f231cc8d66b1377e06e27138b1ddc64b27c6e593e07ebb4b" --signer="0x8e2f9e8cc29658ff37ecc30e95a0807579b224586c185d128cb7a7490784c1ad9b0ab93dbe604ab075b40079931e6670"
|
||||
$ ethdo signature verify --data="0x08140077a94642919041503caf5cc1c89c7744a2a08d43cec91df1795b23ecf2" --signature="0x87c83b31081744667406a11170c5585a11195621d0d3f796bd9006ac4cb5f61c10bf8c5b3014cd4f792b143a644cae100cb3155e8b00a961287bd9e7a5e18cb3b80930708bc9074d11ff47f1e8b9dd0b633e71bcea725fc3e550fdc259c3d130" --signer="0xad1868210a0cff7aff22633c003c503d4c199c8dcca13bba5b3232fc784d39d3855936e94ce184c3ce27bf15d4347695"
|
||||
$ ethdo signature verify --data="0x08140077a94642919041503caf5cc1c89c7744a2a08d43cec91df1795b23ecf2" --signature="0x87c83b31081744667406a11170c5585a11195621d0d3f796bd9006ac4cb5f61c10bf8c5b3014cd4f792b143a644cae100cb3155e8b00a961287bd9e7a5e18cb3b80930708bc9074d11ff47f1e8b9dd0b633e71bcea725fc3e550fdc259c3d130" --signer="0xad1868210a0cff7aff22633c003c503d4c199c8dcca13bba5b3232fc784d39d3855936e94ce184c3ce27bf15d4347695" --verbose
|
||||
Verified
|
||||
```
|
||||
|
||||
@@ -319,6 +307,37 @@ Prior justified epoch: 3
|
||||
Prior justified epoch distance: 4
|
||||
```
|
||||
|
||||
### `deposit` comands
|
||||
|
||||
Deposit commands focus on information about deposit data information in a JSON file generated by the `ethdo validator depositdata` command.
|
||||
|
||||
#### `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
|
||||
- `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.
|
||||
|
||||
```sh
|
||||
$ ethdo deposit verify --data=${HOME}/depositdata.json --withdrawalpubkey=0xad1868210a0cff7aff22633c003c503d4c199c8dcca13bba5b3232fc784d39d3855936e94ce184c3ce27bf15d4347695 --validatorpubkey=0xa951530887ae2494a8cc4f11cf186963b0051ac4f7942375585b9cf98324db1e532a67e521d0fcaab510edad1352394c --depositvalue=32Ether
|
||||
```
|
||||
|
||||
### `exit` comands
|
||||
|
||||
Exit commands focus on information about validator exits generated by the `ethdo validator exit` command.
|
||||
|
||||
#### `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
|
||||
- `account`: the account that generated the exit transaction (if available as an account)
|
||||
- `pubkey`: the public key of the account that generated the exit transaction
|
||||
|
||||
```sh
|
||||
$ ethdo exit verify --data=${HOME}/exit.json --pubkey=0xa951530887ae2494a8cc4f11cf186963b0051ac4f7942375585b9cf98324db1e532a67e521d0fcaab510edad1352394c
|
||||
```
|
||||
|
||||
### `node` commands
|
||||
|
||||
Node commands focus on information from an Ethereum 2 node.
|
||||
|
||||
25
go.mod
25
go.mod
@@ -4,20 +4,21 @@ go 1.13
|
||||
|
||||
require (
|
||||
github.com/OneOfOne/xxhash v1.2.5 // indirect
|
||||
github.com/aws/aws-sdk-go v1.33.11 // 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/herumi/bls-eth-go-binary v0.0.0-20200719025738-3e30d132e8f6
|
||||
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.2 // indirect
|
||||
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-20200619200018-174e3b90d786
|
||||
github.com/prysmaticlabs/ethereumapis v0.0.0-20200709024211-e8095222f77b
|
||||
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.0 // indirect
|
||||
github.com/spf13/afero v1.3.2 // indirect
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/spf13/cobra v1.0.0
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
@@ -29,17 +30,19 @@ require (
|
||||
github.com/wealdtech/go-ecodec v1.1.0
|
||||
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.11.0
|
||||
github.com/wealdtech/go-eth2-wallet-dirk v1.0.0
|
||||
github.com/wealdtech/go-eth2-wallet-distributed v1.0.1
|
||||
github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.0.0
|
||||
github.com/wealdtech/go-eth2-wallet-hd/v2 v2.2.0
|
||||
github.com/wealdtech/go-eth2-wallet-nd/v2 v2.2.0
|
||||
github.com/wealdtech/go-eth2-wallet v1.12.0
|
||||
github.com/wealdtech/go-eth2-wallet-dirk v1.0.1
|
||||
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.3.0
|
||||
github.com/wealdtech/go-eth2-wallet-nd/v2 v2.3.0
|
||||
github.com/wealdtech/go-eth2-wallet-store-filesystem v1.16.1
|
||||
github.com/wealdtech/go-eth2-wallet-store-s3 v1.8.0
|
||||
github.com/wealdtech/go-eth2-wallet-types/v2 v2.5.0
|
||||
github.com/wealdtech/go-eth2-wallet-types/v2 v2.6.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
|
||||
)
|
||||
|
||||
31
go.sum
31
go.sum
@@ -29,6 +29,8 @@ github.com/aws/aws-sdk-go v1.32.6 h1:HoswAabUWgnrUF7X/9dr4WRgrr8DyscxXvTDm7Qw/5c
|
||||
github.com/aws/aws-sdk-go v1.32.6/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
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/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=
|
||||
@@ -155,6 +157,10 @@ github.com/herumi/bls-eth-go-binary v0.0.0-20200706085701-832d8c2c0f7d h1:P8yaFm
|
||||
github.com/herumi/bls-eth-go-binary v0.0.0-20200706085701-832d8c2c0f7d/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U=
|
||||
github.com/herumi/bls-eth-go-binary v0.0.0-20200719025738-3e30d132e8f6 h1:xOOHoKwCj0WXm60FqRxQ0u8cLr+kq5DJUlPspEPsu/s=
|
||||
github.com/herumi/bls-eth-go-binary v0.0.0-20200719025738-3e30d132e8f6/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U=
|
||||
github.com/herumi/bls-eth-go-binary v0.0.0-20200721081051-e31ced8c0204 h1:owpf19DI+vvpSOetnfE1NsLykSxZt8dSag7SDsJd8F0=
|
||||
github.com/herumi/bls-eth-go-binary v0.0.0-20200721081051-e31ced8c0204/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U=
|
||||
github.com/herumi/bls-eth-go-binary v0.0.0-20200722032157-41fc56eba7b4 h1:TfBVK1MJ9vhrMXWVHu5p/MlVHZTeCGgDAEu5RykVZeI=
|
||||
github.com/herumi/bls-eth-go-binary v0.0.0-20200722032157-41fc56eba7b4/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jackc/puddle v1.1.1 h1:PJAw7H/9hoWC4Kf3J8iNmL1SwA6E8vfsLqBiL+F6CtI=
|
||||
@@ -200,6 +206,8 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg=
|
||||
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/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
@@ -233,6 +241,8 @@ github.com/protolambda/zssz v0.1.5 h1:7fjJjissZIIaa2QcvmhS/pZISMX21zVITt49sW1oue
|
||||
github.com/protolambda/zssz v0.1.5/go.mod h1:a4iwOX5FE7/JkKA+J/PH0Mjo9oXftN6P8NZyL28gpag=
|
||||
github.com/prysmaticlabs/ethereumapis v0.0.0-20200619200018-174e3b90d786 h1:bJiOTV2sYykacsxViyRltztQY0DyjT/uFoVRZkEaxsY=
|
||||
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/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=
|
||||
@@ -263,6 +273,8 @@ github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
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/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=
|
||||
@@ -320,18 +332,30 @@ github.com/wealdtech/go-eth2-wallet v1.10.2 h1:oUgi6Ih5fA9thhIipzXMSaLkiwDQXwT8q
|
||||
github.com/wealdtech/go-eth2-wallet v1.10.2/go.mod h1:8H9pgp5K7X1kU1cJMS/B3DrMZF74ZlwBThownrcRYgk=
|
||||
github.com/wealdtech/go-eth2-wallet v1.11.0 h1:2KfrWDqF4sWGgk4N5+DaYmh0hOnqiCl0P4vCz5mx17U=
|
||||
github.com/wealdtech/go-eth2-wallet v1.11.0/go.mod h1:E9ZRNO4JNdi27ys7oc+xWWucXu4IGfV5q1vWC9X3oqg=
|
||||
github.com/wealdtech/go-eth2-wallet v1.12.0 h1:nrwI3jPhehUhJGlBtNv/UmIo/57llvuVZZavLnfdQHI=
|
||||
github.com/wealdtech/go-eth2-wallet v1.12.0/go.mod h1:ouV+YSMbzk2dyecmofm8jhaMKdSigdIPMSnSqmWEfW8=
|
||||
github.com/wealdtech/go-eth2-wallet-dirk v1.0.0 h1:1QUcWILF3h4OLCgTPpWklvRSuPu0fqrt15jwSm7CSC4=
|
||||
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-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=
|
||||
github.com/wealdtech/go-eth2-wallet-distributed v1.1.0/go.mod h1:8r06Vpg/315/7Hl9CXq0ShQP8/cgUrBGzKKo6ywA4yQ=
|
||||
github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.0.0 h1:IcpS4VpXhYz+TVupB5n6C6IQzaKwG+Rc8nvgCa/da4c=
|
||||
github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.0.0/go.mod h1:X8WRO5hEwbjx8ZOqoRmtS1ngyflKs25GkP7qGv7yOqE=
|
||||
github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.1.0 h1:CWb82xeNaZQt1Z829RyDALUy7UZbc6VOfTS+82jRdEQ=
|
||||
github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.1.0/go.mod h1:JelKMM10UzDJNXdIcojMj6SCIsHC8NYn4c1S2FFk7OQ=
|
||||
github.com/wealdtech/go-eth2-wallet-hd/v2 v2.1.3/go.mod h1:STigKib4ZSefVvJjx88V2QpUGaoyUE1TiupcpsHpvKE=
|
||||
github.com/wealdtech/go-eth2-wallet-hd/v2 v2.2.0 h1:L+yrAn8TC9DQUw+S7moOJxQTp2jrHCoAZLpI747Nx2g=
|
||||
github.com/wealdtech/go-eth2-wallet-hd/v2 v2.2.0/go.mod h1:lhSwtkIO/Pfg5kz8k50yrDgj7ZQaElCPsXnixlrQn/I=
|
||||
github.com/wealdtech/go-eth2-wallet-hd/v2 v2.3.0 h1:UORXUYRoUYgYF96Y+QiBq33OKQVtn/nEjnSoQbe1UOA=
|
||||
github.com/wealdtech/go-eth2-wallet-hd/v2 v2.3.0/go.mod h1:Kc/8WcqMTczfH2xy5mDfCRd0NI/ca/j2jXmqJ7gz8yk=
|
||||
github.com/wealdtech/go-eth2-wallet-nd/v2 v2.1.2/go.mod h1:IssxoHII0ewO1VysMfCmdJP1D00tRhRhXIhhaEXIOVE=
|
||||
github.com/wealdtech/go-eth2-wallet-nd/v2 v2.2.0 h1:h4eePfG0ANOJYMonmIYOvxJ9uLmBEX4APb2O8Vhtv6k=
|
||||
github.com/wealdtech/go-eth2-wallet-nd/v2 v2.2.0/go.mod h1:Un2EtseZWSObmTBjgkt7Qz2am54S/0115jrF83lto1U=
|
||||
github.com/wealdtech/go-eth2-wallet-nd/v2 v2.3.0 h1:L1aPK9nc+8Ctcw+8I05vM6408weFc4a5RtLQDUeS0eE=
|
||||
github.com/wealdtech/go-eth2-wallet-nd/v2 v2.3.0/go.mod h1:e2q2uuEdq5+B3GE7jk+Mi9oz9V5nPPKXcXRg1XYavsU=
|
||||
github.com/wealdtech/go-eth2-wallet-store-filesystem v1.15.2 h1:Z4Pw7/Mlp6jJLoJnhgov8M1011HP/Pb3YYqcdYGCy6Q=
|
||||
github.com/wealdtech/go-eth2-wallet-store-filesystem v1.15.2/go.mod h1:GSMbVCewjbxRrw32m6YCd9DOzCRjAXB3qUOQnr58JEs=
|
||||
github.com/wealdtech/go-eth2-wallet-store-filesystem v1.16.0 h1:sWuSrAKdWSphiQCVcThozaFgTrwemXNXDI5CnFcP02s=
|
||||
@@ -345,6 +369,7 @@ github.com/wealdtech/go-eth2-wallet-store-s3 v1.8.0/go.mod h1:OxYD+d79StAOHigNaI
|
||||
github.com/wealdtech/go-eth2-wallet-store-scratch v1.4.2 h1:GvG3ZuzxbqFjGUaGoa8Tz7XbPlDA33G6nHQbSZInC3g=
|
||||
github.com/wealdtech/go-eth2-wallet-store-scratch v1.4.2/go.mod h1:+TbqLmJuT98PWi/xW1bp5nwZbKz+SIJYVh/+NUkmnb4=
|
||||
github.com/wealdtech/go-eth2-wallet-store-scratch v1.5.0/go.mod h1:RMIIV5/N8TgukTVzyumQd7AplpC440ZXDSk8VffeEwQ=
|
||||
github.com/wealdtech/go-eth2-wallet-store-scratch v1.6.0/go.mod h1:XtXHbl4OV/XenQsvGmXbh+bVXaGS788oa30DB7kDInA=
|
||||
github.com/wealdtech/go-eth2-wallet-types/v2 v2.2.0 h1:SfoBlW2LYjW05uHhnTZaezX37gbRsp+VYtxWT6SeAME=
|
||||
github.com/wealdtech/go-eth2-wallet-types/v2 v2.2.0/go.mod h1:XEvrlKFnHLbg1tj4Dep76XKASeS13TBpvdeXmvLiH+k=
|
||||
github.com/wealdtech/go-eth2-wallet-types/v2 v2.3.0-beta4 h1:VmpgUSr+aUexFmC2AYlQ7zpeAy0w0mcK58ihpDeMCL8=
|
||||
@@ -353,6 +378,8 @@ github.com/wealdtech/go-eth2-wallet-types/v2 v2.3.0 h1:PsCvp/lw7+h8Q0V3jL0f+/w2V
|
||||
github.com/wealdtech/go-eth2-wallet-types/v2 v2.3.0/go.mod h1:SLST6Pw/2wOEfsMYvIQjWlxbWX+jaZu8jIEbZJc4K5Q=
|
||||
github.com/wealdtech/go-eth2-wallet-types/v2 v2.5.0 h1:J29mbkSCUMl2xdu8Lg6U+JptFGfmli6xl04DAHtq9aM=
|
||||
github.com/wealdtech/go-eth2-wallet-types/v2 v2.5.0/go.mod h1:X9kYUH/E5YMqFMZ4xL6MJanABUkJGaH/yPZRT2o+yYA=
|
||||
github.com/wealdtech/go-eth2-wallet-types/v2 v2.6.0 h1:vBrH5icPPSeb14cdShA7/P2PBZOgZscJ2IhBlTIaFrA=
|
||||
github.com/wealdtech/go-eth2-wallet-types/v2 v2.6.0/go.mod h1:X9kYUH/E5YMqFMZ4xL6MJanABUkJGaH/yPZRT2o+yYA=
|
||||
github.com/wealdtech/go-indexer v1.0.0 h1:/S4rfWQbSOnnYmwnvuTVatDibZ8o1s9bmTCHO16XINg=
|
||||
github.com/wealdtech/go-indexer v1.0.0/go.mod h1:u1cjsbsOXsm5jzJDyLmZY7GsrdX8KYXKBXkZcAmk3Zg=
|
||||
github.com/wealdtech/go-string2eth v1.1.0 h1:USJQmysUrBYYmZs7d45pMb90hRSyEwizP7lZaOZLDAw=
|
||||
@@ -449,6 +476,8 @@ golang.org/x/sys v0.0.0-20200620081246-981b61492c35 h1:wb/9mP8eUAmHfkM8RmpeLq6nU
|
||||
golang.org/x/sys v0.0.0-20200620081246-981b61492c35/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo=
|
||||
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/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=
|
||||
@@ -509,6 +538,8 @@ google.golang.org/genproto v0.0.0-20200711021454-869866162049 h1:YFTFpQhgvrLrmxt
|
||||
google.golang.org/genproto v0.0.0-20200711021454-869866162049/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200715011427-11fb19a81f2c h1:6DWnZZ6EY/59QRRQttZKiktVL23UuQYs7uy75MhhLRM=
|
||||
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/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=
|
||||
|
||||
15
grpc/node.go
15
grpc/node.go
@@ -55,6 +55,21 @@ func FetchGenesisValidatorsRoot(conn *grpc.ClientConn) ([]byte, error) {
|
||||
return res.GetGenesisValidatorsRoot(), nil
|
||||
}
|
||||
|
||||
// FetchDepositContractAddress fetches the address of the deposit contract.
|
||||
func FetchDepositContractAddress(conn *grpc.ClientConn) ([]byte, error) {
|
||||
if conn == nil {
|
||||
return nil, errors.New("no connection to beacon node")
|
||||
}
|
||||
client := ethpb.NewNodeClient(conn)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||
defer cancel()
|
||||
res, err := client.GetGenesis(ctx, &types.Empty{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res.DepositContractAddress, nil
|
||||
}
|
||||
|
||||
// FetchVersion fetches the version and metadata from the server.
|
||||
func FetchVersion(conn *grpc.ClientConn) (string, string, error) {
|
||||
if conn == nil {
|
||||
|
||||
Reference in New Issue
Block a user