Compare commits

...

76 Commits

Author SHA1 Message Date
Jim McDonald
f2bb5e0d51 Improve error message. 2020-06-22 11:46:53 +01:00
Jim McDonald
f5fc8b363d Merge branch 'master' of github.com:wealdtech/ethdo 2020-06-22 11:06:37 +01:00
Jim McDonald
a31509f7d6 Rework passphrase handling 2020-06-22 11:06:25 +01:00
Jim McDonald
4b451ec2fa Add explicit validator indices for attestations. 2020-06-22 08:08:32 +01:00
Jim McDonald
a8fee14b89 Tidy up use of functions when displaying chain status. 2020-06-22 08:07:19 +01:00
Jim McDonald
9cfb1b5637 Merge pull request #11 from dsystems-io/patch-1
Fixed output for "Justified epoch distance"
2020-06-19 22:24:46 +01:00
Distributed Systems
df1724f763 Fixed output for "Justified epoch distance"
Small inconsistency in output.
2020-06-19 21:38:30 +01:00
Jim McDonald
f1a586ca56 Add information about gopath 2020-06-18 13:33:08 +01:00
Jim McDonald
b9cb926662 Provide output for verified deposits 2020-06-18 10:15:17 +01:00
Jim McDonald
220cc21356 Bump deposit version 2020-06-11 20:38:47 +01:00
Jim McDonald
5bc7c1a26c Bump deposit version 2020-06-11 20:37:04 +01:00
Jim McDonald
4c3237cd0d Tidy up 2020-06-11 13:32:30 +01:00
Jim McDonald
7215e04a69 Provide withdrawal credentials as part of account info.
Supply clearer message when validator depositdata cannot reach a beacon
node.
2020-06-09 21:01:39 +01:00
Jim McDonald
71e9c0471b Update README 2020-06-08 16:24:23 +01:00
Jim McDonald
1a6f402fb8 Add ability to verify wallet exports 2020-06-08 16:13:35 +01:00
Jim McDonald
b9297c2506 Add wallet delete command 2020-06-08 15:45:02 +01:00
Jim McDonald
1032789706 Remove generic transaction options 2020-06-08 09:10:12 +01:00
Jim McDonald
51e289b72b Fix typo in error. 2020-06-02 14:14:41 +01:00
Jim McDonald
a75a350cef Update dependencies 2020-05-29 10:50:09 +01:00
Jim McDonald
aa34e61a80 Update GRPC for streaming 2020-05-29 10:49:56 +01:00
Jim McDonald
3f33f04be2 Default to latest block; add block info streaming 2020-05-29 10:46:47 +01:00
Jim McDonald
5733b5b638 Block info defaults to latest known block 2020-05-28 08:11:15 +01:00
Jim McDonald
e1ce81c81d Bump version 2020-05-28 07:59:14 +01:00
Jim McDonald
bd67ba0307 Do not use local copy of wallet store when creating HD wallet 2020-05-28 07:50:04 +01:00
Jim McDonald
12bb5a7ab8 Merge pull request #8 from olegshmuelov/patch-2
consistency
2020-05-26 06:50:54 +01:00
Jim McDonald
d57dbbf104 Merge pull request #9 from olegshmuelov/patch-1
consistency
2020-05-26 06:50:43 +01:00
olegshmuelov
6482b4add6 consistency
--wallet flag
2020-05-25 18:35:18 +03:00
olegshmuelov
b672e83470 consistency
--wallet flag
2020-05-25 18:34:06 +03:00
Jim McDonald
710e891844 Allow deposit data withdrawal address to be specified as a public key 2020-05-22 23:37:02 +01:00
Jim McDonald
9e3bf521a0 Merge branch 'master' of github.com:wealdtech/ethdo 2020-05-20 13:44:39 +01:00
Jim McDonald
3ca899b832 Fetch genesis fork version from chain for validator deposit data; allow override with flag 2020-05-20 13:43:04 +01:00
Jim McDonald
d221c0544c Add notes for docker 2020-05-20 13:21:02 +01:00
Jim McDonald
eed07a39a3 Add information about mapping default wallet location to docker 2020-05-20 10:50:25 +01:00
Jim McDonald
5a5edacd11 Merge pull request #5 from valo/master
Add Dockerfile
2020-05-18 08:48:37 +01:00
Jim McDonald
c7f4cb0ca5 Fix incorrect distance 2020-05-17 16:13:12 +01:00
Valentin Mihov
fbc2171053 Make the docker section a level 3 header
Fix the TOC to have the old structure
2020-05-16 14:30:05 +03:00
Valentin Mihov
57946f1552 Switched to a debian base image
The alpine base image is using a musl glibc implementation, which is
causing the binary to be not very stable. See
https://github.com/godror/godror/issues/30 for details
2020-05-16 12:41:24 +03:00
Valentin Mihov
b487bb042c Add docker instructions 2020-05-15 16:47:54 +03:00
Valentin Mihov
0c8029c950 Added a Dockerfile to build the tool in a docker container
Use an alpine base image. The image size is 40MB.
2020-05-15 16:41:16 +03:00
Jim McDonald
b3bbeea3fb Add block root to block info output 2020-05-14 15:24:10 +01:00
Jim McDonald
09105549b1 Tidy up block info output 2020-05-12 12:45:57 +01:00
Jim McDonald
364c764de2 Update README with basedir parameter 2020-05-12 12:32:43 +01:00
Jim McDonald
3f1fe30959 Add basedir option for the filesystem wallet store.
Fixes #4
2020-05-12 12:30:16 +01:00
Jim McDonald
aae360ccb8 Fetch genesis validators root from chain 2020-05-10 08:40:43 +01:00
Jim McDonald
32a3e4649d Update signing docs 2020-05-07 18:39:08 +01:00
Jim McDonald
dd3bd121fe Update wallet create docs 2020-05-07 18:33:15 +01:00
Jim McDonald
edf84f47ba Allow creation of HD wallets from seed phrase 2020-05-06 16:09:16 +01:00
Jim McDonald
320c5ee9ab Allow creation of HD wallets from seed phrase 2020-05-06 16:08:42 +01:00
Jim McDonald
36a1674549 Clarify that exit tx not sent when invalid 2020-05-03 12:58:42 +01:00
Jim McDonald
693b2a6961 Allow use of pubkey 2020-05-01 15:35:57 +01:00
Jim McDonald
e5481f9074 Bump version 2020-04-28 17:45:09 +01:00
Jim McDonald
c89712699c Update dependencies 2020-04-28 17:44:48 +01:00
Jim McDonald
7130855b73 Update dependencies 2020-04-28 17:15:11 +01:00
Jim McDonald
d505966a42 Clearer messages 2020-04-27 12:51:54 +01:00
Jim McDonald
732c07238a Merge branch 'master' of github.com:wealdtech/ethdo 2020-04-26 08:51:09 +01:00
Jim McDonald
b843d0077e Update documentation 2020-04-26 08:45:49 +01:00
Jim McDonald
f11cf0cbf5 Merge pull request #3 from superphiz/patch-1
extraneous letter
2020-04-25 23:00:22 +01:00
superphiz
ea43e71e60 extraneous letter 2020-04-25 11:51:29 -05:00
Jim McDonald
71b01ee2aa Merge branch 'master' of github.com:wealdtech/ethdo 2020-04-25 17:18:21 +01:00
Jim McDonald
de349f5691 Documentation tidy-ups 2020-04-25 17:18:06 +01:00
Jim McDonald
b1d2e94854 Merge pull request #2 from superphiz/patch-2
removed extraneous backtick
2020-04-25 07:26:38 +01:00
Jim McDonald
2293079861 Merge pull request #1 from superphiz/patch-1
typo in "accounts"
2020-04-25 07:26:14 +01:00
superphiz
69867ba21c removed extraneous backtick 2020-04-24 20:42:47 -05:00
superphiz
f72af8247a typo in "accounts" 2020-04-24 20:39:10 -05:00
Jim McDonald
9ca4406a80 Updates for validator exit 2020-04-23 09:56:04 +01:00
Jim McDonald
1ad82adf80 Tidy up chain info output 2020-04-21 14:48:31 +01:00
Jim McDonald
11eb440df2 Update README with wallet locations 2020-04-19 11:44:01 +01:00
Jim McDonald
e2192f6992 Version deposit data for additional security 2020-04-17 18:53:31 +01:00
Jim McDonald
864aa24484 Update docs 2020-04-16 08:43:51 +01:00
Jim McDonald
9f8d7c3f7b Update Prysm docs 2020-04-16 08:03:07 +01:00
Jim McDonald
4f643ad952 Add documentation for prysm 2020-04-16 07:55:02 +01:00
Jim McDonald
6551db8f9a Updates for v0.11 2020-04-15 14:27:42 +01:00
Jim McDonald
d0f944eacc Merge functionality for v0.11 2020-04-15 08:37:55 +01:00
Jim McDonald
be3fd8edd2 Use 0.11 style domains 2020-04-09 21:03:10 +01:00
Jim McDonald
afcbd177a3 Tidy-ups 2020-04-08 10:20:51 +01:00
Jim McDonald
a38ff57508 Add remote access 2020-03-31 11:31:54 +01:00
42 changed files with 2862 additions and 798 deletions

24
.dockerignore Normal file
View File

@@ -0,0 +1,24 @@
# Binaries for programs and plugins
*.exe
*.dll
*.so
*.dylib
ethdo
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
coverage.html
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
# Vim
*.sw?
# Local TODO
TODO.md
Dockerfile

19
Dockerfile Normal file
View File

@@ -0,0 +1,19 @@
FROM golang:1.14-buster as builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build
FROM debian:buster-slim
WORKDIR /app
COPY --from=builder /app/ethdo /app
ENTRYPOINT ["/app/ethdo"]

274
README.md
View File

@@ -5,11 +5,12 @@
A command-line tool for managing common tasks in Ethereum 2.
** Please note that this library uses standards that are not yet final, and as such may result in changes that alter public and private keys. Do not use this library for production use just yet **
**Please note that this tool and its underlying libraries have not yet undergone a security audit; use at your own risk.**
## Table of Contents
- [Install](#install)
- [Docker](#docker)
- [Usage](#usage)
- [Maintainers](#maintainers)
- [Contribute](#contribute)
@@ -17,24 +18,69 @@ A command-line tool for managing common tasks in Ethereum 2.
## Install
`ethdo` is a standard Go program which can be installed with:
```sh
GO111MODULE=on go get github.com/wealdtech/ethdo
```
Note that `ethdo` requires at least version 1.13 of go to operate. The version of go can be found with `go 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:
```sh
docker build -t ethdo .
```
You can run `ethdo` using docker after that. Example:
```sh
docker run -it ethdo --help
```
Note that that many `ethdo` commands connect to the beacon node to obtain information. If the beacon node is running directly on the server this requires the `--network=host` command, for example:
```sh
docker run --network=host ethdo chain status
```
Alternatively, if the beacon node is running in a separate docker container a shared network can be created with `docker network create eth2` and accessed by adding `--network=eth2` added to both the beacon node and `ethdo` containers.
## Usage
ethdo contains a large number of features that are useful for day-to-day interactions with the Ethereum 2 blockchain.
### Wallets and accounts
ethdo uses the [go-eth2-wallet](https://github.com/wealdtech/go-eth2-wallet) system to provide unified access to different wallet types.
ethdo uses the [go-eth2-wallet](https://github.com/wealdtech/go-eth2-wallet) system to provide unified access to different wallet types. When on the filesystem the locations of the created wallets and accounts are:
- for Linux: $HOME/.config/ethereum2/wallets
- for OSX: $HOME/Library/Application Support/ethereum2/wallets
- for Windows: %APPDATA%\ethereum2\wallets
If using the filesystem store, the additional parameter `basedir` can be supplied to change this location.
> If using docker as above you can make this directory accessible to docker to make wallets and accounts persistent. For example, for linux you could use the following command to list your wallets on Linux:
>
> ```
> docker run -v $HOME/.config/ethereum2/wallets:/data ethdo --basedir=/data wallet list
> ```
>
> This will allow you to use `ethdo` with or without docker, with the same location for wallets and accounts.
All ethdo comands take the following parameters:
- `store`: the name of the storage system for wallets. This can be one of "filesystem" or "s3", and defaults to "filesystem"
- `store`: the name of the storage system for wallets. This can be one of "filesystem" (for local storage of the wallet) or "s3" (for remote storage of the wallet on [Amazon's S3](https://aws.amazon.com/s3/) storage system), and defaults to "filesystem"
- `storepassphrase`: the passphrase for the store. If this is empty the store is unencrypted
- `walletpassphrase`: the passphrase for the wallet. This is required for some wallet-centric operations such as creating new accounts
- `accountpassphrase`: the passphrase for the account. This is required for some account-centric operations such as signing data
@@ -72,223 +118,25 @@ If set, the `--debug` argument will output additional information about the oper
Commands will have an exit status of 0 on success and 1 on failure. The specific definition of success is specified in the help for each command.
### `wallet` commands
## Rules for account passphrases
#### `accounts`
Account passphrases are used in various places in `ethdo`. Where they are used, the following rules apply:
`ethdo wallet accouts` lists the accounts within a wallet.
- commands that require passphrases to operate, for example unlocking an account, can be supplied with multiple passphrases. If they are, then each passphrase is tried until one succeeds or they all fail
- commands that require passphrases to create, for example creating an account, must be supplied with a single passphrase. If more than one passphrase is supplied the command will fail
```sh
$ ethdo wallet accounts --wallet="Personal wallet"
Auctions
Operations
Spending
```
In addition, the following rules apply to passphrases supplied on the command line:
With the `--verbose` flag this will provide the public key of the accounts.
- passphrases **must not** start with `0x`
- passphrases **must not** contain the comma (,) character
```sh
$ ethdo wallet accounts --wallet="Personal wallet" --verbose
Auctions: 0x812f340269c315c1d882ae7c13cdaddf862dbdbd482b1836798b2070160dd1e194088cc6f39347782028d1e56bd18674
Operations: 0x8e2f9e8cc29658ff37ecc30e95a0807579b224586c185d128cb7a7490784c1ad9b0ab93dbe604ab075b40079931e6670
Spending: 0x85dfc6dcee4c9da36f6473ec02fda283d6c920c641fc8e3a76113c5c227d4aeeb100efcfec977b12d20d571907d05650
```
#### `create`
# Commands
`ethdo wallet create` creates a new wallet with the given parameters. Options for creating a wallet include:
- `wallet`: the name of the wallet to create (defaults to "primary")
- `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
Command information, along with sample outputs and optional arguments, is available in [the usage section](https://github.com/wealdtech/ethdo/blob/master/docs/usage.md).
```sh
$ ethdo wallet create --wallet="Personal wallet" --type="hd" --walletpassphrase="my wallet secret"
```
# HOWTO
#### `export`
`ethdo wallet export` exports the wallet and all of its accounts. Options for exporting a wallet include:
- `wallet`: the name of the wallet to export (defaults to "primary")
- `exportpassphrase`: the passphrase with which to encrypt the wallet backup
```sh
$ ethdo wallet export --wallet="Personal wallet" --exportpassphrase="my export secret"
0x01c7a27ad40d45b4ae5be5f...
```
The encrypted wallet export is written to the console; it can be redirected to store it in a file.
```sh
$ ethdo wallet export --wallet="Personal wallet" --exportpassphrase="my export secret" >export.dat
```
#### `import`
`ethdo wallet import` imports a wallet and all of its accounts exported by `ethdo wallet export`. Options for importing a wallet include:
- `importdata`: the data exported by `ethdo wallet export`
- `importpassphrase`: the passphrase that was provided to `ethdo wallet export` to encrypt the data
```sh
$ ethdo wallet import --importdata="0x01c7a27ad40d45b4ae5be5f..." --importpassphrase="my export secret"
```
The encrypted wallet export can be read from a file. For example with Unix systems:
```sh
$ ethdo wallet import --importdata=`cat export.dat` --importpassphrase="my export secret"
```
#### `info`
`ethdo wallet info` provides information about a given wallet. Options include:
- `wallet`: the name of the wallet
```sh
$ ethdo wallet info --wallet="Personal wallet"
Type: hierarchical deterministic
Accounts: 3
```
#### `list`
`ethdo wallet list` lists all wallets in the store.
```sh
$ ethdo wallet list
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.
#### `create`
`ethdo account create` creates a new account with the given parameters. Options for creating an account include:
- `account`: the name of the account to create
- `passphrase`: the passphrase for the account
Note that for hierarchical deterministic wallets you will also need to supply `--walletpassphrase` to unlock the wallet seed.
```sh
$ ethdo account create --account="Personal wallet/Operations" --walletpassphrase="my wallet secret" --passphrase="my account secret"
```
#### `info`
`ethdo account info` provides information about the given account. Options include:
- `account`: the name of the account on which to obtain information
```sh
$ ethdo account info --account="Personal wallet/Operations"
Public key: 0x8e2f9e8cc29658ff37ecc30e95a0807579b224586c185d128cb7a7490784c1ad9b0ab93dbe604ab075b40079931e6670
```
### `signature` commands
Signature commands focus on generation and verification of data signatures.
#### `signature sign`
`ethdo signature sign` signs provided data. Options include:
- `data`: the data to sign, as a hex string
- `domain`: the domain in which to sign the data. This is an 8-byte hex string (default 0x0000000000000000)
- `account`: the account to sign the data
- `passphrase`: the passphrase for the account
```sh
$ ethdo signature sign --data="0x08140077a94642919041503caf5cc1795b23ecf2" --account="Personal wallet/Operations" --passphrase="my account secret"
0x89abe2e544ef3eafe397db036103b1d066ba86497f36ed4ab0264162eadc89c7744a2a08d43cec91df128660e70ecbbe11031b4c2e53682d2b91e67b886429bf8fac9bad8c7b63c5f231cc8d66b1377e06e27138b1ddc64b27c6e593e07ebb4b
```
#### `signature verify`
`ethdo signature verify` verifies signed data. Options include:
- `data`: the data whose signature to verify, as a hex string
- `signature`: the signature to verify, as a hex string
- `account`: the account which signed the data (if available as an account)
- `signer`: the public key of the account which signed the data (if not available as an account)
```sh
$ ethdo signature verify --data="0x08140077a94642919041503caf5cc1795b23ecf2" --signature="0x89abe2e544ef3eafe397db036103b1d066ba86497f36ed4ab0264162eadc89c7744a2a08d43cec91df128660e70ecbbe11031b4c2e53682d2b91e67b886429bf8fac9bad8c7b63c5f231cc8d66b1377e06e27138b1ddc64b27c6e593e07ebb4b" --account="Personal wallet/Operations"
Verified
$ ethdo signature verify --data="0x08140077a94642919041503caf5cc1795b23ecf2" --signature="0x89abe2e544ef3eafe397db036103b1d066ba86497f36ed4ab0264162eadc89c7744a2a08d43cec91df128660e70ecbbe11031b4c2e53682d2b91e67b886429bf8fac9bad8c7b63c5f231cc8d66b1377e06e27138b1ddc64b27c6e593e07ebb4b" --account="Personal wallet/Auctions"
Not verified
$ ethdo signature verify --data="0x08140077a94642919041503caf5cc1795b23ecf2" --signature="0x89abe2e544ef3eafe397db036103b1d066ba86497f36ed4ab0264162eadc89c7744a2a08d43cec91df128660e70ecbbe11031b4c2e53682d2b91e67b886429bf8fac9bad8c7b63c5f231cc8d66b1377e06e27138b1ddc64b27c6e593e07ebb4b" --signer="0x8e2f9e8cc29658ff37ecc30e95a0807579b224586c185d128cb7a7490784c1ad9b0ab93dbe604ab075b40079931e6670"
Verified
```
The same rules apply to `ethereal signature verify` as those in `ethereal signature sign` above.
### `version`
`ethdo version` provides the current version of ethdo. For example:
```sh
$ ethdo version
1.0.0
```
### `validator` commands
Validator commands focus on interaction with Ethereum 2 validators.
#### `depositdata`
`validator depositdata` generates the data required to deposit one or more Ethereum 2 validators.
#### `exit`
`validator exit` sends a transaction to the chain to tell an active validator to exit the validation queue.
```sh
$ ethdo validator exit --account=Validators/1 --passphrase="my validator secret"
```
#### `info`
`validator info` provides information for a given validator.
```sh
$ ethdo validator info --account=Validators/1
Status: Active
Balance: 3.203823585 Ether
Effective balance: 3.1 Ether
```
Additional information is supplied when using `--verbose`
```sh
$ ethdo validator info --account=Validators/1 --verbose
Epoch of data: 3398
Index: 26913
Public key: 0xb3bb6b7a8d809e59544472853d219499765bf01d14de1e0549bd6fc2a86627ac9033264c84cd503b6339e3334726562f
Status: Active
Balance: 3.204026813 Ether
Effective balance: 3.1 Ether
Withdrawal credentials: 0x0033ef3cb10b36d0771ffe8a02bc5bfc7e64ea2f398ce77e25bb78989edbee36
```
If the validator is not an account it can be queried directly with `--pubkey`.
```sh
$ ethdo validator info --pubkey=0x842dd66cfeaeff4397fc7c94f7350d2131ca0c4ad14ff727963be9a1edb4526604970df6010c3da6474a9820fa81642b
Status: Active
Balance: 3.201850307 Ether
Effective balance: 3.1 Ether
```
There is a [HOWTO](https://github.com/wealdtech/ethdo/blob/master/docs/howto.md) that covers details about how to carry out various common tasks.
## Maintainers

View File

@@ -18,6 +18,8 @@ import (
"os"
"github.com/spf13/cobra"
e2wallet "github.com/wealdtech/go-eth2-wallet"
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
)
var accountCreateCmd = &cobra.Command{
@@ -29,29 +31,32 @@ 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) {
assert(!remote, "account create not available with remote wallets")
assert(rootAccount != "", "--account is required")
assert(rootAccountPassphrase != "", "--passphrase is required")
w, err := walletFromPath(rootAccount)
wallet, err := walletFromPath(rootAccount)
errCheck(err, "Failed to access wallet")
if w.Type() == "hierarchical deterministic" {
assert(rootWalletPassphrase != "", "--walletpassphrase is required to create new accounts with hierarchical deterministic wallets")
if wallet.Type() == "hierarchical deterministic" {
assert(getWalletPassphrase() != "", "--walletpassphrase is required to create new accounts with hierarchical deterministic wallets")
}
_, err = accountFromPath(rootAccount)
assert(err != nil, "Account already exists")
err = w.Unlock([]byte(rootWalletPassphrase))
err = wallet.Unlock([]byte(getWalletPassphrase()))
errCheck(err, "Failed to unlock wallet")
_, accountName, err := walletAndAccountNamesFromPath(rootAccount)
errCheck(err, "Failed to obtain accout name")
_, accountName, err := e2wallet.WalletAndAccountNames(rootAccount)
errCheck(err, "Failed to obtain account name")
account, err := w.CreateAccount(accountName, []byte(rootAccountPassphrase))
walletAccountCreator, ok := wallet.(e2wtypes.WalletAccountCreator)
assert(ok, "wallet does not allow account creation")
account, err := walletAccountCreator.CreateAccount(accountName, []byte(getPassphrase()))
errCheck(err, "Failed to create account")
outputIf(verbose, fmt.Sprintf("0x%048x", account.PublicKey().Marshal()))
os.Exit(_exit_success)
os.Exit(_exitSuccess)
},
}

View File

@@ -19,7 +19,8 @@ import (
"github.com/spf13/cobra"
"github.com/wealdtech/go-bytesutil"
types "github.com/wealdtech/go-eth2-wallet-types"
e2wallet "github.com/wealdtech/go-eth2-wallet"
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
)
var accountImportKey string
@@ -33,8 +34,9 @@ var accountImportCmd = &cobra.Command{
In quiet mode this will return 0 if the account is imported successfully, otherwise 1.`,
Run: func(cmd *cobra.Command, args []string) {
assert(!remote, "account import not available with remote wallets")
assert(rootAccount != "", "--account is required")
assert(rootAccountPassphrase != "", "--passphrase is required")
passphrase := getPassphrase()
assert(accountImportKey != "", "--key is required")
key, err := bytesutil.FromHexString(accountImportKey)
@@ -43,23 +45,23 @@ In quiet mode this will return 0 if the account is imported successfully, otherw
w, err := walletFromPath(rootAccount)
errCheck(err, "Failed to access wallet")
_, ok := w.(types.WalletAccountImporter)
_, ok := w.(e2wtypes.WalletAccountImporter)
assert(ok, fmt.Sprintf("wallets of type %q do not allow importing accounts", w.Type()))
_, err = accountFromPath(rootAccount)
assert(err != nil, "Account already exists")
err = w.Unlock([]byte(rootWalletPassphrase))
err = w.Unlock([]byte(getWalletPassphrase()))
errCheck(err, "Failed to unlock wallet")
_, accountName, err := walletAndAccountNamesFromPath(rootAccount)
errCheck(err, "Failed to obtain accout name")
_, accountName, err := e2wallet.WalletAndAccountNames(rootAccount)
errCheck(err, "Failed to obtain account name")
account, err := w.(types.WalletAccountImporter).ImportAccount(accountName, key, []byte(rootAccountPassphrase))
account, err := w.(e2wtypes.WalletAccountImporter).ImportAccount(accountName, key, []byte(passphrase))
errCheck(err, "Failed to create account")
outputIf(verbose, fmt.Sprintf("0x%048x", account.PublicKey().Marshal()))
os.Exit(_exit_success)
os.Exit(_exitSuccess)
},
}

View File

@@ -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
@@ -14,10 +14,13 @@
package cmd
import (
"context"
"fmt"
"os"
"github.com/spf13/cobra"
pb "github.com/wealdtech/eth2-signer-api/pb/v1"
util "github.com/wealdtech/go-eth2-util"
)
var accountInfoCmd = &cobra.Command{
@@ -31,13 +34,34 @@ In quiet mode this will return 0 if the account exists, otherwise 1.`,
Run: func(cmd *cobra.Command, args []string) {
assert(rootAccount != "", "--account is required")
account, err := accountFromPath(rootAccount)
errCheck(err, "Failed to access wallet")
var withdrawalCredentials []byte
if remote {
listerClient := pb.NewListerClient(remoteGRPCConn)
listAccountsReq := &pb.ListAccountsRequest{
Paths: []string{
rootAccount,
},
}
resp, err := listerClient.ListAccounts(context.Background(), listAccountsReq)
errCheck(err, "Failed to access account")
assert(resp.State == pb.ResponseState_SUCCEEDED, "No such account")
assert(len(resp.Accounts) == 1, "No such account")
fmt.Printf("Public key: %#x\n", resp.Accounts[0].PublicKey)
withdrawalCredentials = util.SHA256(resp.Accounts[0].PublicKey)
withdrawalCredentials[0] = byte(0) // BLS_WITHDRAWAL_PREFIX
outputIf(verbose, fmt.Sprintf("Withdrawal credentials: %#x", withdrawalCredentials))
} else {
account, err := accountFromPath(rootAccount)
errCheck(err, "Failed to access wallet")
outputIf(verbose, fmt.Sprintf("UUID: %v", account.ID()))
outputIf(!quiet, fmt.Sprintf("Public key: %#x", account.PublicKey().Marshal()))
withdrawalCredentials = util.SHA256(account.PublicKey().Marshal())
withdrawalCredentials[0] = byte(0) // BLS_WITHDRAWAL_PREFIX
outputIf(verbose, fmt.Sprintf("Withdrawal credentials: %#x", withdrawalCredentials))
outputIf(verbose && account.Path() != "", fmt.Sprintf("Path: %s", account.Path()))
}
outputIf(verbose, fmt.Sprintf("UUID: %v", account.ID()))
outputIf(!quiet, fmt.Sprintf("Public key: %#048x", account.PublicKey().Marshal()))
outputIf(verbose && account.Path() != "", fmt.Sprintf("Path: %s", account.Path()))
os.Exit(_exit_success)
os.Exit(_exitSuccess)
},
}

View File

@@ -18,7 +18,7 @@ import (
"os"
"github.com/spf13/cobra"
types "github.com/wealdtech/go-eth2-wallet-types"
types "github.com/wealdtech/go-eth2-wallet-types/v2"
)
// accountKeyCmd represents the account key command
@@ -31,23 +31,31 @@ 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(rootAccount != "", "--account is required")
account, err := accountFromPath(rootAccount)
errCheck(err, "Failed to access account")
_, ok := account.(types.AccountPrivateKeyProvider)
assert(ok, fmt.Sprintf("account %q does not provide its private key", rootAccount))
assert(rootAccountPassphrase != "", "--passphrase is required")
err = account.Unlock([]byte(rootAccountPassphrase))
errCheck(err, "Failed to unlock account to obtain private key")
unlocked := false
for _, passphrase := range getPassphrases() {
err = account.Unlock([]byte(passphrase))
if err == nil {
unlocked = true
break
}
}
assert(unlocked, "Failed to unlock account to obtain private key")
defer account.Lock()
privateKey, err := account.(types.AccountPrivateKeyProvider).PrivateKey()
errCheck(err, "Failed to obtain private key")
account.Lock()
outputIf(!quiet, fmt.Sprintf("%#064x", privateKey.Marshal()))
os.Exit(_exit_success)
os.Exit(_exitSuccess)
},
}

60
cmd/accountlock.go Normal file
View File

@@ -0,0 +1,60 @@
// Copyright © 2019, 2020 Weald Technology Trading
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"context"
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
pb "github.com/wealdtech/eth2-signer-api/pb/v1"
)
var accountLockCmd = &cobra.Command{
Use: "lock",
Short: "Lock a remote account",
Long: `Lock a remote account. For example:
ethdo account lock --account="primary/my funds"
In quiet mode this will return 0 if the account is locked, otherwise 1.`,
Run: func(cmd *cobra.Command, args []string) {
assert(remote, "account lock only works with remote wallets")
assert(rootAccount != "", "--account is required")
client := pb.NewAccountManagerClient(remoteGRPCConn)
lockReq := &pb.LockAccountRequest{
Account: rootAccount,
}
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
resp, err := client.Lock(ctx, lockReq)
errCheck(err, "Failed in attempt to lock account")
switch resp.State {
case pb.ResponseState_DENIED:
die("Lock request denied")
case pb.ResponseState_FAILED:
die("Lock request failed")
case pb.ResponseState_SUCCEEDED:
outputIf(!quiet, "Lock request succeeded")
os.Exit(_exitSuccess)
}
},
}
func init() {
accountCmd.AddCommand(accountLockCmd)
accountFlags(accountLockCmd)
}

61
cmd/accountunlock.go Normal file
View File

@@ -0,0 +1,61 @@
// Copyright © 2019, 2020 Weald Technology Trading
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"context"
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
pb "github.com/wealdtech/eth2-signer-api/pb/v1"
)
var accountUnlockCmd = &cobra.Command{
Use: "unlock",
Short: "Unlock a remote account",
Long: `Unlock a remote account. For example:
ethdo account unlock --account="primary/my funds" --passphrase="secret"
In quiet mode this will return 0 if the account is unlocked, otherwise 1.`,
Run: func(cmd *cobra.Command, args []string) {
assert(remote, "account unlock only works with remote wallets")
assert(rootAccount != "", "--account is required")
client := pb.NewAccountManagerClient(remoteGRPCConn)
unlocked := false
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
for _, passphrase := range getPassphrases() {
unlockReq := &pb.UnlockAccountRequest{
Account: rootAccount,
Passphrase: []byte(passphrase),
}
resp, err := client.Unlock(ctx, unlockReq)
errCheck(err, "Failed in attempt to unlock account")
if resp.State == pb.ResponseState_SUCCEEDED {
unlocked = true
break
}
}
assert(unlocked, "Failed to unlock account")
os.Exit(_exitSuccess)
},
}
func init() {
accountCmd.AddCommand(accountUnlockCmd)
accountFlags(accountUnlockCmd)
}

View File

@@ -15,18 +15,23 @@ package cmd
import (
"bytes"
"encoding/hex"
"fmt"
"os"
"sort"
"strings"
"unicode/utf8"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/go-ssz"
"github.com/spf13/cobra"
"github.com/wealdtech/ethdo/grpc"
string2eth "github.com/wealdtech/go-string2eth"
)
var blockInfoSlot int64
var blockInfoStream bool
var blockInfoCmd = &cobra.Command{
Use: "info",
@@ -40,129 +45,175 @@ In quiet mode this will return 0 if the block information is present and not ski
err := connect()
errCheck(err, "Failed to obtain connection to Ethereum 2 beacon chain block")
assert(blockInfoSlot != 0, "--slot is required")
config, err := grpc.FetchChainConfig(eth2GRPCConn)
errCheck(err, "Failed to obtain beacon chain configuration")
slotsPerEpoch := config["SlotsPerEpoch"].(uint64)
if blockInfoStream {
stream, err := grpc.StreamBlocks(eth2GRPCConn)
errCheck(err, "Failed to obtain block stream")
for {
signedBlock, err := stream.Recv()
errCheck(err, "Failed to obtain block")
if signedBlock != nil {
fmt.Println("")
outputBlock(signedBlock, slotsPerEpoch)
}
}
var slot uint64
if blockInfoSlot < 0 {
// TODO latest block.
} else {
slot = uint64(blockInfoSlot)
}
assert(slot > 0, "slot must be greater than 0")
assert(blockInfoSlot != 0, "--slot is required")
var slot uint64
if blockInfoSlot < 0 {
slot, err = grpc.FetchLatestFilledSlot(eth2GRPCConn)
errCheck(err, "Failed to obtain slot of latest block")
} else {
slot = uint64(blockInfoSlot)
}
assert(slot > 0, "slot must be greater than 0")
signedBlock, err := grpc.FetchBlock(eth2GRPCConn, slot)
errCheck(err, "Failed to obtain block")
if signedBlock == nil {
outputIf(!quiet, "No block at that slot")
os.Exit(_exit_failure)
signedBlock, err := grpc.FetchBlock(eth2GRPCConn, slot)
errCheck(err, "Failed to obtain block")
if signedBlock == nil {
outputIf(!quiet, "No block at that slot")
os.Exit(_exitFailure)
}
outputBlock(signedBlock, slotsPerEpoch)
}
block := signedBlock.Block
body := block.Body
// General info.
outputIf(verbose, fmt.Sprintf("Parent root: %#x", block.ParentRoot))
outputIf(verbose, fmt.Sprintf("State root: %#x", block.StateRoot))
os.Exit(_exitSuccess)
},
}
func outputBlock(signedBlock *ethpb.SignedBeaconBlock, slotsPerEpoch uint64) {
block := signedBlock.Block
body := block.Body
// General info.
bodyRoot, err := ssz.HashTreeRoot(block)
errCheck(err, "Failed to calculate block body root")
fmt.Printf("Slot: %d\n", block.Slot)
fmt.Printf("Epoch: %d\n", block.Slot/slotsPerEpoch)
fmt.Printf("Block root: %#x\n", bodyRoot)
outputIf(verbose, fmt.Sprintf("Parent root: %#x", block.ParentRoot))
outputIf(verbose, fmt.Sprintf("State root: %#x", block.StateRoot))
if len(body.Graffiti) > 0 && hex.EncodeToString(body.Graffiti) != "0000000000000000000000000000000000000000000000000000000000000000" {
if utf8.Valid(body.Graffiti) {
fmt.Printf("Graffiti: %s\n", string(body.Graffiti))
} else {
fmt.Printf("Graffiti: %#x\n", body.Graffiti)
}
}
// Eth1 data.
eth1Data := body.Eth1Data
fmt.Printf("Ethereum 1 deposit count: %d\n", eth1Data.DepositCount)
outputIf(verbose, fmt.Sprintf("Ethereum 1 deposit root: %#x", eth1Data.DepositRoot))
outputIf(verbose, fmt.Sprintf("Ethereum 1 block hash: %#x", eth1Data.BlockHash))
// Eth1 data.
eth1Data := body.Eth1Data
outputIf(verbose, fmt.Sprintf("Ethereum 1 deposit count: %d", eth1Data.DepositCount))
outputIf(verbose, fmt.Sprintf("Ethereum 1 deposit root: %#x", eth1Data.DepositRoot))
outputIf(verbose, fmt.Sprintf("Ethereum 1 block hash: %#x", eth1Data.BlockHash))
// Attestations.
fmt.Printf("Attestations: %d\n", len(body.Attestations))
if verbose {
for i, att := range body.Attestations {
fmt.Printf("\t%d:\n", i)
validatorCommittees := make(map[uint64][][]uint64)
fmt.Printf("\t\tCommittee index: %d\n", att.Data.CommitteeIndex)
fmt.Printf("\t\tAttesters: %d\n", countSetBits(att.AggregationBits))
fmt.Printf("\t\tAggregation bits: %s\n", bitsToString(att.AggregationBits))
fmt.Printf("\t\tSlot: %d\n", att.Data.Slot)
fmt.Printf("\t\tBeacon block root: %#x\n", att.Data.BeaconBlockRoot)
fmt.Printf("\t\tSource epoch: %d\n", att.Data.Source.Epoch)
fmt.Printf("\t\tSource root: %#x\n", att.Data.Source.Root)
fmt.Printf("\t\tTarget epoch: %d\n", att.Data.Target.Epoch)
fmt.Printf("\t\tTarget root: %#x\n", att.Data.Target.Root)
}
}
// Attestations.
fmt.Printf("Attestations: %d\n", len(body.Attestations))
if verbose {
for i, att := range body.Attestations {
fmt.Printf("\t%d:\n", i)
// Attester slashings.
fmt.Printf("Attester slashings: %d\n", len(body.AttesterSlashings))
if verbose {
for i, slashing := range body.AttesterSlashings {
fmt.Printf("\t%d:\n", i)
// Say what was slashed.
att1 := slashing.Attestation_1
att2 := slashing.Attestation_2
slashedIndices := intersection(att1.AttestingIndices, att2.AttestingIndices)
if len(slashedIndices) == 0 {
continue
}
fmt.Println("\t\tSlashed validators:")
for _, slashedIndex := range slashedIndices {
validator, err := grpc.FetchValidatorByIndex(eth2GRPCConn, slashedIndex)
errCheck(err, "Failed to obtain validator information")
fmt.Printf("\t\t\t%#x (%d)\n", validator.PublicKey, slashedIndex)
}
// Say what caused the slashing.
if att1.Data.Target.Epoch == att2.Data.Target.Epoch {
fmt.Printf("\t\tDouble voted for same target epoch (%d):\n", att1.Data.Target.Epoch)
if !bytes.Equal(att1.Data.Target.Root, att2.Data.Target.Root) {
fmt.Printf("\t\t\tAttestation 1 target epoch root: %#x\n", att1.Data.Target.Root)
fmt.Printf("\t\t\tAttestation 2target epoch root: %#x\n", att2.Data.Target.Root)
}
if !bytes.Equal(att1.Data.BeaconBlockRoot, att2.Data.BeaconBlockRoot) {
fmt.Printf("\t\t\tAttestation 1 beacon block root: %#x\n", att1.Data.BeaconBlockRoot)
fmt.Printf("\t\t\tAttestation 2 beacon block root: %#x\n", att2.Data.BeaconBlockRoot)
}
} else {
if att1.Data.Source.Epoch < att2.Data.Source.Epoch &&
att1.Data.Target.Epoch > att2.Data.Target.Epoch {
fmt.Printf("\t\tSurround voted:\n")
fmt.Printf("\t\t\tAttestation 1 vote: %d->%d\n", att1.Data.Source.Epoch, att1.Data.Target.Epoch)
fmt.Printf("\t\t\tAttestation 2 vote: %d->%d\n", att2.Data.Source.Epoch, att2.Data.Target.Epoch)
}
// Fetch committees for this epoch if not already obtained.
committees, exists := validatorCommittees[att.Data.Slot]
if !exists {
attestationEpoch := att.Data.Slot / slotsPerEpoch
epochCommittees, err := grpc.FetchValidatorCommittees(eth2GRPCConn, attestationEpoch)
errCheck(err, "Failed to obtain committees")
for k, v := range epochCommittees {
validatorCommittees[k] = v
}
committees = validatorCommittees[att.Data.Slot]
}
fmt.Printf("\t\tCommittee index: %d\n", att.Data.CommitteeIndex)
fmt.Printf("\t\tAttesters: %d/%d\n", att.AggregationBits.Count(), att.AggregationBits.Len())
fmt.Printf("\t\tAggregation bits: %s\n", bitsToString(att.AggregationBits))
fmt.Printf("\t\tAttesting indices: %s\n", attestingIndices(att.AggregationBits, committees[att.Data.CommitteeIndex]))
fmt.Printf("\t\tSlot: %d\n", att.Data.Slot)
fmt.Printf("\t\tBeacon block root: %#x\n", att.Data.BeaconBlockRoot)
fmt.Printf("\t\tSource epoch: %d\n", att.Data.Source.Epoch)
fmt.Printf("\t\tSource root: %#x\n", att.Data.Source.Root)
fmt.Printf("\t\tTarget epoch: %d\n", att.Data.Target.Epoch)
fmt.Printf("\t\tTarget root: %#x\n", att.Data.Target.Root)
}
}
// TODO Proposer slashings once proposer slashings exist.
// Deposits.
fmt.Printf("Deposits: %d\n", len(body.Deposits))
if verbose {
for i, deposit := range body.Deposits {
data := deposit.Data
fmt.Printf("\t%d:\n", i)
fmt.Printf("\t\tPublic key: %#x\n", data.PublicKey)
fmt.Printf("\t\tAmount: %s\n", string2eth.GWeiToString(data.Amount, true))
fmt.Printf("\t\tWithdrawal credentials: %#x\n", data.WithdrawalCredentials)
fmt.Printf("\t\tSignature: %#x\n", data.Signature)
// Attester slashings.
fmt.Printf("Attester slashings: %d\n", len(body.AttesterSlashings))
if verbose {
for i, slashing := range body.AttesterSlashings {
// Say what was slashed.
att1 := slashing.Attestation_1
outputIf(debug, fmt.Sprintf("Attestation 1 attesting indices are %v", att1.AttestingIndices))
att2 := slashing.Attestation_2
outputIf(debug, fmt.Sprintf("Attestation 2 attesting indices are %v", att2.AttestingIndices))
slashedIndices := intersection(att1.AttestingIndices, att2.AttestingIndices)
if len(slashedIndices) == 0 {
continue
}
}
// Voluntary exits.
fmt.Printf("Voluntary exits: %d\n", len(body.VoluntaryExits))
if verbose {
for i, voluntaryExit := range body.VoluntaryExits {
fmt.Printf("\t%d:\n", i)
validator, err := grpc.FetchValidatorByIndex(eth2GRPCConn, voluntaryExit.Exit.ValidatorIndex)
fmt.Printf("\t%d:\n", i)
fmt.Println("\t\tSlashed validators:")
for _, slashedIndex := range slashedIndices {
validator, err := grpc.FetchValidatorByIndex(eth2GRPCConn, slashedIndex)
errCheck(err, "Failed to obtain validator information")
fmt.Printf("\t\tValidator: %#x (%d)\n", validator.PublicKey, voluntaryExit.Exit.ValidatorIndex)
fmt.Printf("\t\tEpoch: %d\n", voluntaryExit.Exit.Epoch)
fmt.Printf("\t\t\t%#x (%d)\n", validator.PublicKey, slashedIndex)
}
// Say what caused the slashing.
if att1.Data.Target.Epoch == att2.Data.Target.Epoch {
fmt.Printf("\t\tDouble voted for same target epoch (%d):\n", att1.Data.Target.Epoch)
if !bytes.Equal(att1.Data.Target.Root, att2.Data.Target.Root) {
fmt.Printf("\t\t\tAttestation 1 target epoch root: %#x\n", att1.Data.Target.Root)
fmt.Printf("\t\t\tAttestation 2target epoch root: %#x\n", att2.Data.Target.Root)
}
if !bytes.Equal(att1.Data.BeaconBlockRoot, att2.Data.BeaconBlockRoot) {
fmt.Printf("\t\t\tAttestation 1 beacon block root: %#x\n", att1.Data.BeaconBlockRoot)
fmt.Printf("\t\t\tAttestation 2 beacon block root: %#x\n", att2.Data.BeaconBlockRoot)
}
} else if att1.Data.Source.Epoch < att2.Data.Source.Epoch &&
att1.Data.Target.Epoch > att2.Data.Target.Epoch {
fmt.Printf("\t\tSurround voted:\n")
fmt.Printf("\t\t\tAttestation 1 vote: %d->%d\n", att1.Data.Source.Epoch, att1.Data.Target.Epoch)
fmt.Printf("\t\t\tAttestation 2 vote: %d->%d\n", att2.Data.Source.Epoch, att2.Data.Target.Epoch)
}
}
}
os.Exit(_exit_success)
},
fmt.Printf("Proposer slashings: %d\n", len(body.ProposerSlashings))
// TODO verbose proposer slashings.
// Deposits.
fmt.Printf("Deposits: %d\n", len(body.Deposits))
if verbose {
for i, deposit := range body.Deposits {
data := deposit.Data
fmt.Printf("\t%d:\n", i)
fmt.Printf("\t\tPublic key: %#x\n", data.PublicKey)
fmt.Printf("\t\tAmount: %s\n", string2eth.GWeiToString(data.Amount, true))
fmt.Printf("\t\tWithdrawal credentials: %#x\n", data.WithdrawalCredentials)
fmt.Printf("\t\tSignature: %#x\n", data.Signature)
}
}
// Voluntary exits.
fmt.Printf("Voluntary exits: %d\n", len(body.VoluntaryExits))
if verbose {
for i, voluntaryExit := range body.VoluntaryExits {
fmt.Printf("\t%d:\n", i)
validator, err := grpc.FetchValidatorByIndex(eth2GRPCConn, voluntaryExit.Exit.ValidatorIndex)
errCheck(err, "Failed to obtain validator information")
fmt.Printf("\t\tValidator: %#x (%d)\n", validator.PublicKey, voluntaryExit.Exit.ValidatorIndex)
fmt.Printf("\t\tEpoch: %d\n", voluntaryExit.Exit.Epoch)
}
}
}
// intersection returns a list of items common between the two sets.
@@ -170,24 +221,18 @@ func intersection(set1 []uint64, set2 []uint64) []uint64 {
sort.Slice(set1, func(i, j int) bool { return set1[i] < set1[j] })
sort.Slice(set2, func(i, j int) bool { return set2[i] < set2[j] })
res := make([]uint64, 0)
if len(set1) < len(set2) {
set1, set2 = set2, set1
}
set1Pos := 0
set2Pos := 0
set2LastIndex := len(set2) - 1
for set1Pos := range set1 {
for set1[set1Pos] == set2[set2Pos] {
res = append(res, set1[set1Pos])
if set2Pos == set2LastIndex {
break
}
for set1Pos < len(set1) && set2Pos < len(set2) {
switch {
case set1[set1Pos] < set2[set2Pos]:
set1Pos++
case set2[set2Pos] < set1[set1Pos]:
set2Pos++
}
for set1[set1Pos] > set2[set2Pos] {
if set2Pos == set2LastIndex {
break
}
default:
res = append(res, set1[set1Pos])
set1Pos++
set2Pos++
}
}
@@ -195,43 +240,37 @@ func intersection(set1 []uint64, set2 []uint64) []uint64 {
return res
}
// countSetBits counts the number of bits that are set in the given byte array.
func countSetBits(input []byte) int {
total := 0
for _, x := range input {
item := uint8(x)
for item > 0 {
if item&0x01 == 1 {
total++
}
item >>= 1
func bitsToString(input bitfield.Bitlist) string {
bits := int(input.Len())
res := ""
for i := 0; i < bits; i++ {
if input.BitAt(uint64(i)) {
res = fmt.Sprintf("%s✓", res)
} else {
res = fmt.Sprintf("%s✕", res)
}
if i%8 == 7 {
res = fmt.Sprintf("%s ", res)
}
}
return total
return strings.TrimSpace(res)
}
func bitsToString(input []byte) string {
elements := make([]string, len(input))
for i, x := range input {
item := uint8(x)
mask := uint8(0x80)
element := ""
for mask > 0 {
if item&mask != 0 {
element = fmt.Sprintf("%s✓", element)
} else {
element = fmt.Sprintf("%s✕", element)
}
mask >>= 1
func attestingIndices(input bitfield.Bitlist, indices []uint64) string {
bits := int(input.Len())
res := ""
for i := 0; i < bits; i++ {
if input.BitAt(uint64(i)) {
res = fmt.Sprintf("%s%d ", res, indices[i])
}
elements[i] = element
}
return strings.Join(elements, " ")
return strings.TrimSpace(res)
}
func init() {
blockCmd.AddCommand(blockInfoCmd)
blockFlags(blockInfoCmd)
blockInfoCmd.Flags().Int64Var(&blockInfoSlot, "slot", -1, "the default slot")
blockInfoCmd.Flags().Int64Var(&blockInfoSlot, "slot", -1, "the latest slot with a block")
blockInfoCmd.Flags().BoolVar(&blockInfoStream, "stream", false, "continually stream blocks as they arrive")
}

View File

@@ -36,23 +36,20 @@ In quiet mode this will return 0 if the chain information can be obtained, other
config, err := grpc.FetchChainConfig(eth2GRPCConn)
errCheck(err, "Failed to obtain beacon chain configuration")
genesisTime, err := grpc.FetchGenesis(eth2GRPCConn)
genesisTime, err := grpc.FetchGenesisTime(eth2GRPCConn)
errCheck(err, "Failed to obtain genesis time")
if quiet {
os.Exit(_exit_success)
os.Exit(_exitSuccess)
}
fmt.Printf("Genesis time:\t\t%s\n", genesisTime.Format(time.UnixDate))
slot := timestampToSlot(genesisTime.Unix(), time.Now().Unix(), config["SecondsPerSlot"].(uint64))
fmt.Printf("Current slot:\t\t%d\n", slot)
fmt.Printf("Current epoch:\t\t%d\n", slot/config["SlotsPerEpoch"].(uint64))
outputIf(verbose, fmt.Sprintf("Genesis fork version:\t%0x", config["GenesisForkVersion"].([]byte)))
outputIf(verbose, fmt.Sprintf("Genesis timestamp:\t%v", genesisTime.Unix()))
outputIf(verbose, fmt.Sprintf("Seconds per slot:\t%v", config["SecondsPerSlot"].(uint64)))
outputIf(verbose, fmt.Sprintf("Slots per epoch:\t%v", config["SlotsPerEpoch"].(uint64)))
fmt.Printf("Genesis time: %s\n", genesisTime.Format(time.UnixDate))
outputIf(verbose, fmt.Sprintf("Genesis timestamp: %v", genesisTime.Unix()))
outputIf(verbose, fmt.Sprintf("Genesis fork version: %0x", config["GenesisForkVersion"].([]byte)))
outputIf(verbose, fmt.Sprintf("Seconds per slot: %v", config["SecondsPerSlot"].(uint64)))
outputIf(verbose, fmt.Sprintf("Slots per epoch: %v", config["SlotsPerEpoch"].(uint64)))
os.Exit(_exit_success)
os.Exit(_exitSuccess)
},
}

View File

@@ -38,51 +38,56 @@ In quiet mode this will return 0 if the chain status can be obtained, otherwise
config, err := grpc.FetchChainConfig(eth2GRPCConn)
errCheck(err, "Failed to obtain beacon chain configuration")
genesisTime, err := grpc.FetchGenesis(eth2GRPCConn)
genesisTime, err := grpc.FetchGenesisTime(eth2GRPCConn)
errCheck(err, "Failed to obtain genesis time")
info, err := grpc.FetchChainInfo(eth2GRPCConn)
errCheck(err, "Failed to obtain chain info")
if quiet {
os.Exit(_exit_success)
os.Exit(_exitSuccess)
}
slot := timestampToSlot(genesisTime.Unix(), time.Now().Unix(), config["SecondsPerSlot"].(uint64))
if chainStatusSlot {
fmt.Printf("Current slot:\t\t%d\n", slot)
fmt.Printf("Justified slot:\t\t%d", info.GetJustifiedSlot())
fmt.Printf("Current slot: %d\n", slot)
fmt.Printf("Justified slot: %d\n", info.GetJustifiedSlot())
if verbose {
distance := slot - info.GetJustifiedSlot()
fmt.Printf(" (%d)", distance)
fmt.Printf("Justified slot distance: %d\n", distance)
}
fmt.Printf("\n")
fmt.Printf("Finalized slot:\t\t%d", info.GetFinalizedSlot())
fmt.Printf("Finalized slot: %d\n", info.GetFinalizedSlot())
if verbose {
distance := slot - info.GetFinalizedSlot()
fmt.Printf(" (%d)", distance)
fmt.Printf("Finalized slot distance: %d\n", distance)
}
if verbose {
fmt.Printf("Prior justified slot: %d\n", info.GetFinalizedSlot())
distance := slot - info.GetPreviousJustifiedSlot()
fmt.Printf("Prior justified slot distance: %d\n", distance)
}
fmt.Printf("\n")
outputIf(verbose, fmt.Sprintf("Prior justified slot:\t%v (%d)", info.GetPreviousJustifiedSlot(), slot-info.GetPreviousJustifiedSlot()))
} else {
slotsPerEpoch := config["SlotsPerEpoch"].(uint64)
fmt.Printf("Current epoch:\t\t%d\n", slot/slotsPerEpoch)
fmt.Printf("Justified epoch:\t%d", info.GetJustifiedSlot()/slotsPerEpoch)
epoch := slot / slotsPerEpoch
fmt.Printf("Current epoch: %d\n", epoch)
fmt.Printf("Justified epoch: %d\n", info.GetJustifiedEpoch())
if verbose {
distance := (slot - info.GetJustifiedSlot()) / slotsPerEpoch
fmt.Printf(" (%d)", distance)
fmt.Printf("Justified epoch distance: %d\n", distance)
}
fmt.Printf("\n")
fmt.Printf("Finalized epoch:\t%d", info.GetFinalizedSlot()/slotsPerEpoch)
fmt.Printf("Finalized epoch: %d\n", info.GetFinalizedEpoch())
if verbose {
distance := (slot - info.GetFinalizedSlot()) / slotsPerEpoch
fmt.Printf(" (%d)", distance)
fmt.Printf("Finalized epoch distance: %d\n", distance)
}
if verbose {
fmt.Printf("Prior justified epoch: %d\n", info.GetPreviousJustifiedEpoch())
distance := (slot - info.GetPreviousJustifiedSlot()) / slotsPerEpoch
fmt.Printf("Prior justified epoch distance: %d\n", distance)
}
fmt.Printf("\n")
outputIf(verbose, fmt.Sprintf("Prior justified epoch:\t%v (%d)", info.GetPreviousJustifiedSlot()/slotsPerEpoch, (slot-info.GetPreviousJustifiedSlot())/slotsPerEpoch))
}
os.Exit(_exit_success)
os.Exit(_exitSuccess)
},
}

View File

@@ -14,6 +14,6 @@
package cmd
const (
_exit_success = 0
_exit_failure = 1
_exitSuccess = 0
_exitFailure = 1
)

32
cmd/deposit.go Normal file
View 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"
)
// depositCmd represents the deposit command
var depositCmd = &cobra.Command{
Use: "deposit",
Short: "Manage Ethereum 2 deposits",
Long: `Manage Ethereum 2 deposits.`,
}
func init() {
RootCmd.AddCommand(depositCmd)
}
func depositFlags(cmd *cobra.Command) {
}

235
cmd/depositverify.go Normal file
View File

@@ -0,0 +1,235 @@
// 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"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/pkg/errors"
"github.com/spf13/cobra"
e2types "github.com/wealdtech/go-eth2-types/v2"
util "github.com/wealdtech/go-eth2-util"
string2eth "github.com/wealdtech/go-string2eth"
)
type depositData struct {
Name string `json:"name,omitempty"`
Account string `json:"account,omitempty"`
PublicKey string `json:"pubkey"`
WithdrawalCredentials string `json:"withdrawal_credentials"`
Signature string `json:"signature"`
DepositDataRoot string `json:"deposit_data_root"`
Value uint64 `json:"value"`
Version uint64 `json:"version"`
}
var depositVerifyData string
var depositVerifyWithdrawalPubKey string
var depositVerifyValidatorPubKey string
var depositVerifyDepositValue string
var depositVerifyCmd = &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) {
assert(depositVerifyData != "", "--data is required")
deposits, err := depositDataFromJSON(depositVerifyData)
errCheck(err, "Failed to fetch deposit data")
withdrawalCredentials := ""
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)
}
outputIf(debug, fmt.Sprintf("Withdrawal credentials are %s", withdrawalCredentials))
depositValue := uint64(0)
if depositVerifyDepositValue != "" {
depositValue, err = string2eth.StringToGWei(depositVerifyDepositValue)
errCheck(err, "Invalid value")
// This is hard-coded, to allow deposit data to be generated without a connection to the beacon node.
assert(depositValue >= 1000000000, "deposit value must be at least 1 Ether") // MIN_DEPOSIT_AMOUNT
}
validatorPubKeys := make(map[string]bool)
if depositVerifyValidatorPubKey != "" {
validatorPubKeys, err = validatorPubKeysFromInput(depositVerifyValidatorPubKey)
errCheck(err, "Failed to obtain validator public key(s))")
}
failures := false
for i, deposit := range deposits {
if withdrawalCredentials != "" {
if deposit.WithdrawalCredentials != withdrawalCredentials {
outputIf(!quiet, fmt.Sprintf("Invalid withdrawal credentials for deposit %d", i))
failures = true
}
}
if depositValue != 0 {
if deposit.Value != depositValue {
outputIf(!quiet, fmt.Sprintf("Invalid deposit value for deposit %d", i))
failures = true
}
}
if len(validatorPubKeys) != 0 {
if _, exists := validatorPubKeys[deposit.PublicKey]; !exists {
outputIf(!quiet, fmt.Sprintf("Unknown validator public key for deposit %d", i))
failures = true
}
}
outputIf(!quiet, fmt.Sprintf("Deposit for %q verified", deposit.Name))
}
if failures {
os.Exit(_exitFailure)
}
os.Exit(_exitSuccess)
},
}
func validatorPubKeysFromInput(input string) (map[string]bool, error) {
pubKeys := make(map[string]bool)
var err error
var data []byte
// Input could be a public key or a path to public keys.
if strings.HasPrefix(input, "0x") {
// Looks like a public key.
pubKeyBytes, err := hex.DecodeString(strings.TrimPrefix(input, "0x"))
if err != nil {
return nil, errors.Wrap(err, "public key is not a hex string")
}
if len(pubKeyBytes) != 48 {
return nil, errors.New("public key should be 48 bytes")
}
pubKey, err := e2types.BLSPublicKeyFromBytes(pubKeyBytes)
if err != nil {
return nil, errors.Wrap(err, "invalid public key")
}
pubKeys[fmt.Sprintf("%x", pubKey.Marshal())] = true
} else {
// Assume it's a path to a file of public keys.
data, err = ioutil.ReadFile(input)
if err != nil {
return nil, errors.Wrap(err, "failed to find public key file")
}
lines := bytes.Split(bytes.Replace(data, []byte("\r\n"), []byte("\n"), -1), []byte("\n"))
if len(lines) == 0 {
return nil, errors.New("file has no public keys")
}
for _, line := range lines {
if len(line) == 0 {
continue
}
pubKeyBytes, err := hex.DecodeString(strings.TrimPrefix(string(line), "0x"))
if err != nil {
return nil, errors.Wrap(err, "public key is not a hex string")
}
if len(pubKeyBytes) != 48 {
return nil, errors.New("public key should be 48 bytes")
}
pubKey, err := e2types.BLSPublicKeyFromBytes(pubKeyBytes)
if err != nil {
return nil, errors.Wrap(err, "invalid public key")
}
pubKeys[fmt.Sprintf("%x", pubKey.Marshal())] = true
}
}
return pubKeys, nil
}
func depositDataFromJSON(input string) ([]*depositData, error) {
var err error
var data []byte
// Input could be JSON or a path to JSON
switch {
case strings.HasPrefix(input, "{"):
// Looks like JSON
data = []byte("[" + input + "]")
case strings.HasPrefix(input, "["):
// Looks like JSON array
data = []byte(input)
default:
// Assume it's a path to JSON
data, err = ioutil.ReadFile(input)
if err != nil {
return nil, errors.Wrap(err, "failed to find deposit data file")
}
if data[0] == '{' {
data = []byte("[" + string(data) + "]")
}
}
var depositData []*depositData
err = json.Unmarshal(data, &depositData)
if err != nil {
return nil, errors.Wrap(err, "data is not valid JSON")
}
if len(depositData) == 0 {
return nil, errors.New("no deposits supplied")
}
minVersion := depositData[0].Version
maxVersion := depositData[0].Version
for i := range depositData {
if depositData[i].PublicKey == "" {
return nil, fmt.Errorf("no public key for deposit %d", i)
}
if depositData[i].DepositDataRoot == "" {
return nil, fmt.Errorf("no data root for deposit %d", i)
}
if depositData[i].Signature == "" {
return nil, fmt.Errorf("no signature for deposit %d", i)
}
if depositData[i].WithdrawalCredentials == "" {
return nil, fmt.Errorf("no withdrawal credentials for deposit %d", i)
}
if depositData[i].Value < 1000000000 {
return nil, fmt.Errorf("Deposit amount too small for deposit %d", i)
}
if depositData[i].Version > maxVersion {
maxVersion = depositData[i].Version
}
if depositData[i].Version < minVersion {
minVersion = depositData[i].Version
}
}
return depositData, nil
}
func init() {
depositCmd.AddCommand(depositVerifyCmd)
depositFlags(depositVerifyCmd)
depositVerifyCmd.Flags().StringVar(&depositVerifyData, "data", "", "JSON data, or path to JSON data")
depositVerifyCmd.Flags().StringVar(&depositVerifyWithdrawalPubKey, "withdrawalpubkey", "", "Public key of the account to which the validator funds will be withdrawn")
depositVerifyCmd.Flags().StringVar(&depositVerifyDepositValue, "depositvalue", "", "Value of the amount to be deposited")
depositVerifyCmd.Flags().StringVar(&depositVerifyValidatorPubKey, "validatorpubkey", "", "Public key(s) of the account(s) that will be carrying out validation")
}

View File

@@ -36,35 +36,31 @@ In quiet mode this will return 0 if the node information can be obtained, otherw
config, err := grpc.FetchChainConfig(eth2GRPCConn)
errCheck(err, "Failed to obtain beacon chain configuration")
genesisTime, err := grpc.FetchGenesis(eth2GRPCConn)
genesisTime, err := grpc.FetchGenesisTime(eth2GRPCConn)
errCheck(err, "Failed to obtain genesis time")
if quiet {
os.Exit(_exit_success)
os.Exit(_exitSuccess)
}
if verbose {
version, metadata, err := grpc.FetchVersion(eth2GRPCConn)
errCheck(err, "Failed to obtain version")
fmt.Printf("Version:\t\t%s\n", version)
fmt.Printf("Version: %s\n", version)
if metadata != "" {
fmt.Printf("Metadata:\t%s\n", metadata)
fmt.Printf("Metadata: %s\n", metadata)
}
}
syncing, err := grpc.FetchSyncing(eth2GRPCConn)
errCheck(err, "Failed to obtain syncing state")
fmt.Printf("Syncing:\t\t%v\n", syncing)
fmt.Printf("Syncing: %v\n", syncing)
fmt.Printf("Genesis time:\t\t%s\n", genesisTime.Format(time.UnixDate))
slot := timestampToSlot(genesisTime.Unix(), time.Now().Unix(), config["SecondsPerSlot"].(uint64))
fmt.Printf("Current slot:\t\t%d\n", slot)
fmt.Printf("Current epoch:\t\t%d\n", slot/config["SlotsPerEpoch"].(uint64))
outputIf(verbose, fmt.Sprintf("Genesis fork version:\t%0x", config["GenesisForkVersion"].([]byte)))
outputIf(verbose, fmt.Sprintf("Genesis timestamp:\t%v", genesisTime.Unix()))
outputIf(verbose, fmt.Sprintf("Seconds per slot:\t%v", config["SecondsPerSlot"].(uint64)))
outputIf(verbose, fmt.Sprintf("Slots per epoch:\t%v", config["SlotsPerEpoch"].(uint64)))
fmt.Printf("Current slot: %d\n", slot)
fmt.Printf("Current epoch: %d\n", slot/config["SlotsPerEpoch"].(uint64))
outputIf(verbose, fmt.Sprintf("Genesis timestamp: %v", genesisTime.Unix()))
os.Exit(_exit_success)
os.Exit(_exitSuccess)
},
}

39
cmd/passphrases.go Normal file
View File

@@ -0,0 +1,39 @@
// 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/viper"
// getStorePassphrases() fetches the store passphrase supplied by the user.
func getStorePassphrase() string {
return viper.GetString("store-passphrase")
}
// getWalletPassphrases() fetches the wallet passphrase supplied by the user.
func getWalletPassphrase() string {
return viper.GetString("wallet-passphrase")
}
// getPassphrases() fetches the passphrases supplied by the user.
func getPassphrases() []string {
return viper.GetStringSlice("passphrase")
}
// getPassphrase fetches the passphrase supplied by the user.
func getPassphrase() string {
passphrases := getPassphrases()
assert(len(passphrases) != 0, "passphrase is required")
assert(len(passphrases) == 1, "multiple passphrases supplied; cannot continue")
return passphrases[0]
}

View File

@@ -15,8 +15,10 @@ package cmd
import (
"context"
"errors"
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"os"
"regexp"
"sort"
@@ -24,13 +26,17 @@ import (
"time"
homedir "github.com/mitchellh/go-homedir"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-ssz"
"github.com/spf13/cobra"
"github.com/spf13/viper"
types "github.com/wealdtech/go-eth2-types"
e2types "github.com/wealdtech/go-eth2-types/v2"
e2wallet "github.com/wealdtech/go-eth2-wallet"
filesystem "github.com/wealdtech/go-eth2-wallet-store-filesystem"
s3 "github.com/wealdtech/go-eth2-wallet-store-s3"
wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
"google.golang.org/grpc"
wallet "github.com/wealdtech/go-eth2-wallet"
wtypes "github.com/wealdtech/go-eth2-wallet-types"
"google.golang.org/grpc/credentials"
)
var cfgFile string
@@ -38,17 +44,23 @@ var quiet bool
var verbose bool
var debug bool
// For transaction commands
var wait bool
var generate bool
// Root variables, present for all commands
// Root variables, present for all commands.
var rootStore string
var rootAccount string
var rootStorePassphrase string
var rootWalletPassphrase string
var rootAccountPassphrase string
var rootBaseDir string
// Store for wallet actions.
var store wtypes.Store
// Remote connection.
var remote bool
var remoteAddr string
var clientCert string
var clientKey string
var serverCACert string
var remoteGRPCConn *grpc.ClientConn
// Prysm connection.
var eth2GRPCConn *grpc.ClientConn
// RootCmd represents the base command when called without any subcommands
@@ -76,37 +88,41 @@ func persistentPreRun(cmd *cobra.Command, args []string) {
debug = viper.GetBool("debug")
rootStore = viper.GetString("store")
rootAccount = viper.GetString("account")
rootStorePassphrase = viper.GetString("storepassphrase")
rootWalletPassphrase = viper.GetString("walletpassphrase")
rootAccountPassphrase = viper.GetString("passphrase")
// ...lots of commands have transaction-related flags (e.g.) 'wait'
// as options but we want to bind them to this particular command and
// this is the first chance we get
if cmd.Flags().Lookup("wait") != nil {
err := viper.BindPFlag("wait", cmd.Flags().Lookup("wait"))
errCheck(err, "Failed to set wait option")
}
wait = viper.GetBool("wait")
if cmd.Flags().Lookup("generate") != nil {
err := viper.BindPFlag("generate", cmd.Flags().Lookup("generate"))
errCheck(err, "Failed to set generate option")
}
generate = viper.GetBool("generate")
rootBaseDir = viper.GetString("basedir")
if quiet && verbose {
die("Cannot supply both quiet and verbose flags")
fmt.Println("Cannot supply both quiet and verbose flags")
}
if quiet && debug {
die("Cannot supply both quiet and debug flags")
}
if generate && wait {
die("Cannot supply both generate and wait flags")
fmt.Println("Cannot supply both quiet and debug flags")
}
// Set up our wallet store
err := wallet.SetStore(rootStore, []byte(rootStorePassphrase))
errCheck(err, "Failed to set up wallet store")
if viper.GetString("remote") == "" {
// Set up our wallet store
switch rootStore {
case "s3":
assert(rootBaseDir == "", "--basedir does not apply for the s3 store")
var err error
store, err = s3.New(s3.WithPassphrase([]byte(getStorePassphrase())))
errCheck(err, "Failed to access Amazon S3 wallet store")
case "filesystem":
opts := make([]filesystem.Option, 0)
if getStorePassphrase() != "" {
opts = append(opts, filesystem.WithPassphrase([]byte(getStorePassphrase())))
}
if rootBaseDir != "" {
opts = append(opts, filesystem.WithLocation(rootBaseDir))
}
store = filesystem.New(opts...)
default:
die(fmt.Sprintf("Unsupported wallet store %s", rootStore))
}
err := e2wallet.UseStore(store)
errCheck(err, "Failed to use defined wallet store")
} else {
err := initRemote()
errCheck(err, "Failed to connect to remote wallet")
}
}
// Execute adds all child commands to the root command and sets flags appropriately.
@@ -114,11 +130,17 @@ func persistentPreRun(cmd *cobra.Command, args []string) {
func Execute() {
if err := RootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(_exit_failure)
os.Exit(_exitFailure)
}
}
func init() {
// Initialise our BLS library.
if err := e2types.InitBLS(); err != nil {
fmt.Println(err)
os.Exit(_exitFailure)
}
cobra.OnInitialize(initConfig)
RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.ethdo.yaml)")
@@ -134,15 +156,19 @@ func init() {
if err := viper.BindPFlag("account", RootCmd.PersistentFlags().Lookup("account")); err != nil {
panic(err)
}
RootCmd.PersistentFlags().String("basedir", "", "Base directory for filesystem wallets")
if err := viper.BindPFlag("base-dir", RootCmd.PersistentFlags().Lookup("basedir")); err != nil {
panic(err)
}
RootCmd.PersistentFlags().String("storepassphrase", "", "Passphrase for store (if applicable)")
if err := viper.BindPFlag("storepassphrase", RootCmd.PersistentFlags().Lookup("storepassphrase")); err != nil {
if err := viper.BindPFlag("store-passphrase", RootCmd.PersistentFlags().Lookup("storepassphrase")); err != nil {
panic(err)
}
RootCmd.PersistentFlags().String("walletpassphrase", "", "Passphrase for wallet (if applicable)")
if err := viper.BindPFlag("walletpassphrase", RootCmd.PersistentFlags().Lookup("walletpassphrase")); err != nil {
if err := viper.BindPFlag("wallet-passphrase", RootCmd.PersistentFlags().Lookup("walletpassphrase")); err != nil {
panic(err)
}
RootCmd.PersistentFlags().String("passphrase", "", "Passphrase for account (if applicable)")
RootCmd.PersistentFlags().StringSlice("passphrase", nil, "Passphrase for account (if applicable)")
if err := viper.BindPFlag("passphrase", RootCmd.PersistentFlags().Lookup("passphrase")); err != nil {
panic(err)
}
@@ -166,6 +192,22 @@ func init() {
if err := viper.BindPFlag("timeout", RootCmd.PersistentFlags().Lookup("timeout")); err != nil {
panic(err)
}
RootCmd.PersistentFlags().String("remote", "", "connection to a remote wallet daemon")
if err := viper.BindPFlag("remote", RootCmd.PersistentFlags().Lookup("remote")); err != nil {
panic(err)
}
RootCmd.PersistentFlags().String("client-cert", "", "location of a client certificate file when connecting to the remote wallet daemon")
if err := viper.BindPFlag("client-cert", RootCmd.PersistentFlags().Lookup("client-cert")); err != nil {
panic(err)
}
RootCmd.PersistentFlags().String("client-key", "", "location of a client key file when connecting to the remote wallet daemon")
if err := viper.BindPFlag("client-key", RootCmd.PersistentFlags().Lookup("client-key")); err != nil {
panic(err)
}
RootCmd.PersistentFlags().String("server-ca-cert", "", "location of the server certificate authority certificate when connecting to the remote wallet daemon")
if err := viper.BindPFlag("server-ca-cert", RootCmd.PersistentFlags().Lookup("server-ca-cert")); err != nil {
panic(err)
}
}
// initConfig reads in config file and ENV variables if set.
@@ -176,10 +218,7 @@ func initConfig() {
} else {
// Find home directory.
home, err := homedir.Dir()
if err != nil {
fmt.Println(err)
os.Exit(_exit_failure)
}
errCheck(err, "could not find home directory")
// Search config in home directory with name ".ethdo" (without extension).
viper.AddConfigPath(home)
@@ -192,10 +231,7 @@ func initConfig() {
// If a config file is found, read it in.
if err := viper.ReadInConfig(); err != nil {
// Don't report lack of config file...
if !strings.Contains(err.Error(), "Not Found") {
fmt.Println(err)
os.Exit(_exit_failure)
}
assert(strings.Contains(err.Error(), "Not Found"), "failed to read configuration")
}
}
@@ -209,37 +245,20 @@ func outputIf(condition bool, msg string) {
}
}
// walletAndAccountNamesFromPath breaks a path in to wallet and account names.
func walletAndAccountNamesFromPath(path string) (string, string, error) {
if len(path) == 0 {
return "", "", errors.New("invalid account format")
}
index := strings.Index(path, "/")
if index == -1 {
// Just the wallet
return path, "", nil
}
if index == len(path)-1 {
// Trailing /
return path[:index], "", nil
}
return path[:index], path[index+1:], nil
}
// walletFromPath obtains a wallet given a path specification.
func walletFromPath(path string) (wtypes.Wallet, error) {
walletName, _, err := walletAndAccountNamesFromPath(path)
walletName, _, err := e2wallet.WalletAndAccountNames(path)
if err != nil {
return nil, err
}
w, err := wallet.OpenWallet(walletName)
wallet, err := e2wallet.OpenWallet(walletName)
if err != nil {
if strings.Contains(err.Error(), "failed to decrypt wallet") {
return nil, errors.New("Incorrect store passphrase")
}
return nil, err
}
return w, nil
return wallet, nil
}
// accountFromPath obtains an account given a path specification.
@@ -248,7 +267,7 @@ func accountFromPath(path string) (wtypes.Account, error) {
if err != nil {
return nil, err
}
_, accountName, err := walletAndAccountNamesFromPath(path)
_, accountName, err := e2wallet.WalletAndAccountNames(path)
if err != nil {
return nil, err
}
@@ -256,8 +275,9 @@ func accountFromPath(path string) (wtypes.Account, error) {
return nil, errors.New("no account name")
}
if wallet.Type() == "hierarchical deterministic" && strings.HasPrefix(accountName, "m/") && rootWalletPassphrase != "" {
err = wallet.Unlock([]byte(rootWalletPassphrase))
if wallet.Type() == "hierarchical deterministic" && strings.HasPrefix(accountName, "m/") {
assert(getWalletPassphrase() != "", "--walletpassphrase is required for direct path derivations")
err = wallet.Unlock([]byte(viper.GetString("wallet-passphrase")))
if err != nil {
return nil, errors.New("invalid wallet passphrase")
}
@@ -281,7 +301,7 @@ func accountsFromPath(path string) ([]wtypes.Account, error) {
if err != nil {
return nil, err
}
_, accountSpec, err := walletAndAccountNamesFromPath(path)
_, accountSpec, err := e2wallet.WalletAndAccountNames(path)
if err != nil {
return nil, err
}
@@ -307,19 +327,47 @@ func accountsFromPath(path string) ([]wtypes.Account, error) {
return accounts, nil
}
// sign signs data in a domain.
func sign(account wtypes.Account, data []byte, domain uint64) (types.Signature, error) {
// signStruct signs an arbitrary structure.
func signStruct(account wtypes.Account, data interface{}, domain []byte) (e2types.Signature, error) {
objRoot, err := ssz.HashTreeRoot(data)
outputIf(debug, fmt.Sprintf("Object root is %x", objRoot))
if err != nil {
return nil, err
}
return signRoot(account, objRoot, domain)
}
// SigningContainer is the container for signing roots with a domain.
// Contains SSZ sizes to allow for correct calculation of root.
type SigningContainer struct {
Root []byte `ssz-size:"32"`
Domain []byte `ssz-size:"32"`
}
// signRoot signs a root.
func signRoot(account wtypes.Account, root [32]byte, domain []byte) (e2types.Signature, error) {
container := &SigningContainer{
Root: root[:],
Domain: domain,
}
outputIf(debug, fmt.Sprintf("Signing container:\n\troot: %x\n\tdomain: %x", container.Root, container.Domain))
signingRoot, err := ssz.HashTreeRoot(container)
if err != nil {
return nil, err
}
outputIf(debug, fmt.Sprintf("Signing root: %x", signingRoot))
return sign(account, signingRoot[:])
}
// sign signs arbitrary data.
func sign(account wtypes.Account, data []byte) (e2types.Signature, error) {
if !account.IsUnlocked() {
return nil, errors.New("account must be unlocked to sign")
}
return account.Sign(data, domain)
}
// addTransactionFlags adds flags used in all transactions.
func addTransactionFlags(cmd *cobra.Command) {
cmd.Flags().Bool("generate", false, "Do not send the transaction; generate and output as a hex string only")
cmd.Flags().Bool("wait", false, "wait for the transaction to be mined before returning")
outputIf(debug, fmt.Sprintf("Signing %x (%d)", data, len(data)))
return account.Sign(data)
}
// connect connects to an Ethereum 2 endpoint.
@@ -342,3 +390,47 @@ func connect() error {
eth2GRPCConn, err = grpc.DialContext(ctx, connection, opts...)
return err
}
func initRemote() error {
remote = true
remoteAddr = viper.GetString("remote")
clientCert = viper.GetString("client-cert")
assert(clientCert != "", "--remote requires --client-cert")
clientKey = viper.GetString("client-key")
assert(clientKey != "", "--remote requires --client-key")
serverCACert = viper.GetString("server-ca-cert")
assert(serverCACert != "", "--remote requires --server-ca-cert")
// Load the client certificates.
clientPair, err := tls.LoadX509KeyPair(clientCert, clientKey)
if err != nil {
return errors.Wrap(err, "failed to access client certificate/key")
}
tlsCfg := &tls.Config{
Certificates: []tls.Certificate{clientPair},
}
if serverCACert != "" {
// Load the CA for the server certificate.
serverCA, err := ioutil.ReadFile(serverCACert)
if err != nil {
return errors.Wrap(err, "failed to access CA certificate")
}
cp := x509.NewCertPool()
if !cp.AppendCertsFromPEM(serverCA) {
return errors.Wrap(err, "failed to add CA certificate")
}
tlsCfg.RootCAs = cp
}
clientCreds := credentials.NewTLS(tlsCfg)
opts := []grpc.DialOption{
// Require TLS.
grpc.WithTransportCredentials(clientCreds),
// Block until server responds.
grpc.WithBlock(),
}
remoteGRPCConn, err = grpc.Dial(remoteAddr, opts...)
return err
}

View File

@@ -1,4 +1,4 @@
// Copyright © 2017-2019 Weald Technology Trading
// Copyright © 2017-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
@@ -14,48 +14,77 @@
package cmd
import (
"context"
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
pb "github.com/wealdtech/eth2-signer-api/pb/v1"
"github.com/wealdtech/go-bytesutil"
types "github.com/wealdtech/go-eth2-types"
e2types "github.com/wealdtech/go-eth2-types/v2"
)
// signatureSignCmd represents the signature sign command
var signatureSignCmd = &cobra.Command{
Use: "sign",
Short: "Sign data",
Short: "Sign a 32-byte piece of data",
Long: `Sign presented data. For example:
ethereal signature sign --data="0x5FfC014343cd971B7eb70732021E26C35B744cc4" --account="Personal wallet/Operations" --passphrase="my account passphrase"
ethereal signature sign --data=0x5f24e819400c6a8ee2bfc014343cd971b7eb707320025a7bcd83e621e26c35b7 --account="Personal wallet/Operations" --passphrase="my account passphrase"
In quiet mode this will return 0 if the data can be signed, otherwise 1.`,
Run: func(cmd *cobra.Command, args []string) {
assert(signatureData != "", "--data is required")
data, err := bytesutil.FromHexString(signatureData)
errCheck(err, "Failed to parse data")
assert(len(data) == 32, "data to sign must be 32 bytes")
domain := types.Domain([]byte{0, 0, 0, 0}, []byte{0, 0, 0, 0})
domain := e2types.Domain(e2types.DomainType([4]byte{0, 0, 0, 0}), e2types.ZeroForkVersion, e2types.ZeroGenesisValidatorsRoot)
if signatureDomain != "" {
domainBytes, err := bytesutil.FromHexString(signatureDomain)
errCheck(err, "Failed to parse domain")
assert(len(domainBytes) == 8, "Domain data invalid")
assert(len(domainBytes) == 32, "Domain data invalid")
domain = domainBytes
}
assert(rootAccount != "", "--account is required")
account, err := accountFromPath(rootAccount)
errCheck(err, "Failed to access account for signing")
err = account.Unlock([]byte(rootAccountPassphrase))
errCheck(err, "Failed to unlock account for signing")
defer account.Lock()
signature, err := account.Sign(data, domain)
errCheck(err, "Failed to sign data")
var signature e2types.Signature
if remote {
signClient := pb.NewSignerClient(remoteGRPCConn)
signReq := &pb.SignRequest{
Id: &pb.SignRequest_Account{Account: rootAccount},
Data: data,
Domain: domain,
}
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
resp, err := signClient.Sign(ctx, signReq)
errCheck(err, "Failed to sign")
switch resp.State {
case pb.ResponseState_DENIED:
die("Signing request denied")
case pb.ResponseState_FAILED:
die("Signing request failed")
case pb.ResponseState_SUCCEEDED:
signature, err = e2types.BLSSignatureFromBytes(resp.Signature)
errCheck(err, "Invalid signature")
}
} else {
account, err := accountFromPath(rootAccount)
errCheck(err, "Failed to access account for signing")
err = account.Unlock([]byte(getPassphrase()))
errCheck(err, "Failed to unlock account for signing")
var fixedSizeData [32]byte
copy(fixedSizeData[:], data)
defer account.Lock()
signature, err = signRoot(account, fixedSizeData, domain)
errCheck(err, "Failed to sign data")
}
outputIf(!quiet, fmt.Sprintf("0x%096x", signature.Marshal()))
os.Exit(_exit_success)
os.Exit(_exitSuccess)
},
}

View File

@@ -1,4 +1,4 @@
// Copyright © 2017-2019 Weald Technology Trading
// Copyright © 2017-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
@@ -14,11 +14,14 @@
package cmd
import (
"context"
"os"
"github.com/prysmaticlabs/go-ssz"
"github.com/spf13/cobra"
pb "github.com/wealdtech/eth2-signer-api/pb/v1"
"github.com/wealdtech/go-bytesutil"
types "github.com/wealdtech/go-eth2-types"
e2types "github.com/wealdtech/go-eth2-types/v2"
)
var signatureVerifySignature string
@@ -30,46 +33,69 @@ var signatureVerifyCmd = &cobra.Command{
Short: "Verify signed data",
Long: `Verify signed data. For example:
ethereal signature verify --data="0x5FfC014343cd971B7eb70732021E26C35B744cc4" --signature="0x8888..." --account="Personal wallet/Operations"
ethereal signature verify --data=0x5f24e819400c6a8ee2bfc014343cd971b7eb707320025a7bcd83e621e26c35b7 --signature=0x8888... --account="Personal wallet/Operations"
In quiet mode this will return 0 if the data can be signed, otherwise 1.`,
Run: func(cmd *cobra.Command, args []string) {
assert(signatureData != "", "--data is required")
data, err := bytesutil.FromHexString(signatureData)
errCheck(err, "Failed to parse data")
assert(len(data) == 32, "data to verify must be 32 bytes")
assert(signatureVerifySignature != "", "--signature is required")
signatureBytes, err := bytesutil.FromHexString(signatureVerifySignature)
errCheck(err, "Failed to parse signature")
signature, err := types.BLSSignatureFromBytes(signatureBytes)
signature, err := e2types.BLSSignatureFromBytes(signatureBytes)
errCheck(err, "Invalid signature")
domain := types.Domain([]byte{0, 0, 0, 0}, []byte{0, 0, 0, 0})
domain := e2types.Domain(e2types.DomainType([4]byte{0, 0, 0, 0}), e2types.ZeroForkVersion, e2types.ZeroGenesisValidatorsRoot)
if signatureDomain != "" {
domainBytes, err := bytesutil.FromHexString(signatureDomain)
errCheck(err, "Failed to parse domain")
assert(len(domainBytes) == 8, "Domain data invalid")
assert(len(domainBytes) == 32, "Domain data invalid")
}
var pubKey types.PublicKey
var pubKey e2types.PublicKey
assert(signatureVerifyPubKey == "" || rootAccount == "", "Either --pubkey or --account should be supplied")
if rootAccount != "" {
account, err := accountFromPath(rootAccount)
errCheck(err, "Unknown account")
pubKey = account.PublicKey()
if remote {
listerClient := pb.NewListerClient(remoteGRPCConn)
listAccountsReq := &pb.ListAccountsRequest{
Paths: []string{
rootAccount,
},
}
resp, err := listerClient.ListAccounts(context.Background(), listAccountsReq)
errCheck(err, "Failed to access account")
assert(resp.State == pb.ResponseState_SUCCEEDED, "Failed to obtain account")
assert(len(resp.Accounts) == 1, "No such account")
pubKey, err = e2types.BLSPublicKeyFromBytes(resp.Accounts[0].PublicKey)
errCheck(err, "Invalid public key provided for account")
} else {
account, err := accountFromPath(rootAccount)
errCheck(err, "Unknown account")
pubKey = account.PublicKey()
}
} else {
pubKeyBytes, err := bytesutil.FromHexString(signatureVerifyPubKey)
errCheck(err, "Invalid public key")
pubKey, err = types.BLSPublicKeyFromBytes(pubKeyBytes)
pubKey, err = e2types.BLSPublicKeyFromBytes(pubKeyBytes)
errCheck(err, "Invalid public key")
}
verified := signature.Verify(data, pubKey, domain)
container := &SigningContainer{
Root: data,
Domain: domain,
}
root, err := ssz.HashTreeRoot(container)
errCheck(err, "Failed to create signing root")
verified := signature.Verify(root[:], pubKey)
if !verified {
outputIf(!quiet, "Not verified")
os.Exit(_exit_failure)
os.Exit(_exitFailure)
}
outputIf(!quiet, "Verified")
os.Exit(_exit_success)
os.Exit(_exitSuccess)
},
}

View File

@@ -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
@@ -14,21 +14,25 @@
package cmd
import (
"encoding/hex"
"fmt"
"os"
"strings"
"github.com/prysmaticlabs/go-ssz"
"github.com/spf13/cobra"
types "github.com/wealdtech/go-eth2-types"
"github.com/wealdtech/ethdo/grpc"
e2types "github.com/wealdtech/go-eth2-types/v2"
util "github.com/wealdtech/go-eth2-util"
string2eth "github.com/wealdtech/go-string2eth"
)
var validatorDepositDataValidatorAccount string
var validatorDepositDataWithdrawalAccount string
var validatorDepositDataWithdrawalPubKey string
var validatorDepositDataDepositValue string
var validatorDepositDataRaw bool
var validatorDepositDataForkVersion string
var validatorDepositDataCmd = &cobra.Command{
Use: "depositdata",
@@ -58,20 +62,32 @@ In quiet mode this will return 0 if the the data can be generated correctly, oth
}
}
assert(validatorDepositDataWithdrawalAccount != "", "--withdrawalaccount is required")
withdrawalAccount, err := accountFromPath(validatorDepositDataWithdrawalAccount)
errCheck(err, "Failed to obtain withdrawal account")
outputIf(debug, fmt.Sprintf("Withdrawal public key is %048x", withdrawalAccount.PublicKey().Marshal()))
withdrawalCredentials := util.SHA256(withdrawalAccount.PublicKey().Marshal())
errCheck(err, "Failed to hash withdrawal credentials")
withdrawalCredentials[0] = byte(0) // BLSWithdrawalPrefix
assert(validatorDepositDataWithdrawalAccount != "" || validatorDepositDataWithdrawalPubKey != "", "--withdrawalaccount or --withdrawalpubkey is required")
var withdrawalCredentials []byte
if validatorDepositDataWithdrawalAccount != "" {
withdrawalAccount, err := accountFromPath(validatorDepositDataWithdrawalAccount)
errCheck(err, "Failed to obtain withdrawal account")
outputIf(debug, fmt.Sprintf("Withdrawal public key is %048x", withdrawalAccount.PublicKey().Marshal()))
withdrawalCredentials = util.SHA256(withdrawalAccount.PublicKey().Marshal())
errCheck(err, "Failed to hash withdrawal credentials")
} else {
withdrawalPubKeyBytes, err := hex.DecodeString(strings.TrimPrefix(validatorDepositDataWithdrawalPubKey, "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")
withdrawalCredentials = util.SHA256(withdrawalPubKey.Marshal())
errCheck(err, "Failed to hash withdrawal credentials")
}
// This is hard-coded, to allow deposit data to be generated without a connection to the beacon node.
withdrawalCredentials[0] = byte(0) // BLS_WITHDRAWAL_PREFIX
outputIf(debug, fmt.Sprintf("Withdrawal credentials are %032x", withdrawalCredentials))
assert(validatorDepositDataDepositValue != "", "--depositvalue is required")
val, err := string2eth.StringToGWei(validatorDepositDataDepositValue)
errCheck(err, "Invalid value")
assert(val >= 1000000000, "deposit value must be at least 1 Ether")
// This is hard-coded, to allow deposit data to be generated without a connection to the beacon node.
assert(val >= 1000000000, "deposit value must be at least 1 Ether") // MIN_DEPOSIT_AMOUNT
// For each key, generate deposit data
outputs := make([]string, 0)
@@ -85,15 +101,41 @@ In quiet mode this will return 0 if the the data can be generated correctly, oth
WithdrawalCredentials: withdrawalCredentials,
Value: val,
}
signingRoot, err := ssz.HashTreeRoot(depositData)
errCheck(err, "Failed to generate deposit data signing root")
outputIf(debug, fmt.Sprintf("Signing root is %x", signingRoot))
domain := types.Domain(types.DomainDeposit, []byte{0, 0, 0, 0})
err = validatorAccount.Unlock([]byte(rootAccountPassphrase))
errCheck(err, "Failed to unlock validator account")
signature, err := validatorAccount.Sign(signingRoot[:], domain)
outputIf(debug, fmt.Sprintf("Deposit data:\n\tPublic key: %x\n\tWithdrawal credentials: %x\n\tValue: %d", depositData.PubKey, depositData.WithdrawalCredentials, depositData.Value))
var forkVersion []byte
if validatorDepositDataForkVersion != "" {
forkVersion, err = hex.DecodeString(strings.TrimPrefix(validatorDepositDataForkVersion, "0x"))
errCheck(err, fmt.Sprintf("Failed to decode fork version %s", validatorDepositDataForkVersion))
assert(len(forkVersion) == 4, "Fork version must be exactly four bytes")
} else {
err := connect()
errCheck(err, "Failed to connect to beacon node")
config, err := grpc.FetchChainConfig(eth2GRPCConn)
if err != nil {
outputIf(!quiet, "Could not connect to beacon node; supply a connection with --connection or provide a fork version with --forkversion to generate a deposit")
os.Exit(_exitFailure)
}
genesisForkVersion, exists := config["GenesisForkVersion"]
assert(exists, "Failed to obtain genesis fork version")
forkVersion = genesisForkVersion.([]byte)
}
outputIf(debug, fmt.Sprintf("Fork version is %x", forkVersion))
domain := e2types.Domain(e2types.DomainDeposit, forkVersion, e2types.ZeroGenesisValidatorsRoot)
outputIf(debug, fmt.Sprintf("Domain is %x", domain))
unlocked := false
for _, passphrase := range getPassphrases() {
err = validatorAccount.Unlock([]byte(passphrase))
if err == nil {
unlocked = true
break
}
}
assert(unlocked, "Failed to unlock validator account")
signature, err := signStruct(validatorAccount, depositData, domain)
validatorAccount.Lock()
errCheck(err, "Failed to sign deposit data signing root")
errCheck(err, "Failed to generate deposit data signature")
signedDepositData := struct {
PubKey []byte `ssz-size:"48"`
@@ -106,8 +148,7 @@ In quiet mode this will return 0 if the the data can be generated correctly, oth
Value: val,
Signature: signature.Marshal(),
}
outputIf(debug, fmt.Sprintf("Deposit data signature is %x", signedDepositData.Signature))
outputIf(debug, fmt.Sprintf("Signed deposit data:\n\tPublic key: %x\n\tWithdrawal credentials: %x\n\tValue: %d\n\tSignature: %x", signedDepositData.PubKey, signedDepositData.WithdrawalCredentials, signedDepositData.Value, signedDepositData.Signature))
depositDataRoot, err := ssz.HashTreeRoot(signedDepositData)
errCheck(err, "Failed to generate deposit data root")
@@ -136,7 +177,7 @@ In quiet mode this will return 0 if the the data can be generated correctly, oth
txData = append(txData, signedDepositData.Signature...)
outputs = append(outputs, fmt.Sprintf("%#x", txData))
} else {
outputs = append(outputs, fmt.Sprintf(`{"account":"%s","pubkey":"%048x","withdrawal_credentials":"%032x","signature":"%096x","value":%d,"deposit_data_root":"%032x"}`, fmt.Sprintf("%s/%s", validatorWallet.Name(), validatorAccount.Name()), signedDepositData.PubKey, signedDepositData.WithdrawalCredentials, signedDepositData.Signature, val, depositDataRoot))
outputs = append(outputs, fmt.Sprintf(`{"account":"%s","pubkey":"%048x","withdrawal_credentials":"%032x","signature":"%096x","value":%d,"deposit_data_root":"%032x","version":2}`, fmt.Sprintf("%s/%s", validatorWallet.Name(), validatorAccount.Name()), signedDepositData.PubKey, signedDepositData.WithdrawalCredentials, signedDepositData.Signature, val, depositDataRoot))
}
}
@@ -159,6 +200,8 @@ func init() {
validatorFlags(validatorDepositDataCmd)
validatorDepositDataCmd.Flags().StringVar(&validatorDepositDataValidatorAccount, "validatoraccount", "", "Account of the account carrying out the validation")
validatorDepositDataCmd.Flags().StringVar(&validatorDepositDataWithdrawalAccount, "withdrawalaccount", "", "Account of the account to which the validator funds will be withdrawn")
validatorDepositDataCmd.Flags().StringVar(&validatorDepositDataWithdrawalPubKey, "withdrawalpubkey", "", "Public key of the account to which the validator funds will be withdrawn")
validatorDepositDataCmd.Flags().StringVar(&validatorDepositDataDepositValue, "depositvalue", "", "Value of the amount to be deposited")
validatorDepositDataCmd.Flags().BoolVar(&validatorDepositDataRaw, "raw", false, "Print raw deposit data transaction data")
validatorDepositDataCmd.Flags().StringVar(&validatorDepositDataForkVersion, "forkversion", "", "Use a hard-coded fork version (default is to fetch it from the node)")
}

View File

@@ -15,19 +15,27 @@ package cmd
import (
"context"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"strings"
"time"
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/go-ssz"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/wealdtech/ethdo/grpc"
types "github.com/wealdtech/go-eth2-types"
"github.com/wealdtech/ethdo/util"
e2types "github.com/wealdtech/go-eth2-types/v2"
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
)
var validatorExitEpoch int64
var validatorExitKey string
var validatorExitJSON string
var validatorExitJSONOutput bool
var validatorExitCmd = &cobra.Command{
Use: "exit",
@@ -36,60 +44,130 @@ var validatorExitCmd = &cobra.Command{
ethdo validator exit --account=primary/validator --passphrase=secret
In quiet mode this will return 0 if the transaction has been sent, otherwise 1.`,
In quiet mode this will return 0 if the transaction has been generated, otherwise 1.`,
Run: func(cmd *cobra.Command, args []string) {
// Sanity checking and setup.
assert(rootAccount != "", "--account is required")
account, err := accountFromPath(rootAccount)
errCheck(err, "Failed to access account")
err = connect()
err := connect()
errCheck(err, "Failed to obtain connect to Ethereum 2 beacon chain node")
// Beacon chain config required for later work.
config, err := grpc.FetchChainConfig(eth2GRPCConn)
errCheck(err, "Failed to obtain beacon chain configuration")
exit, signature := validatorExitHandleInput()
validatorExitHandleExit(exit, signature)
os.Exit(_exitSuccess)
},
}
// Fetch the validator's index.
index, err := grpc.FetchValidatorIndex(eth2GRPCConn, account)
errCheck(err, "Failed to obtain validator index")
outputIf(debug, fmt.Sprintf("Validator index is %d", index))
func validatorExitHandleInput() (*ethpb.VoluntaryExit, e2types.Signature) {
if validatorExitJSON != "" {
return validatorExitHandleJSONInput(validatorExitJSON)
}
if rootAccount != "" {
account, err := accountFromPath(rootAccount)
errCheck(err, "Failed to access account")
return validatorExitHandleAccountInput(account)
}
if validatorExitKey != "" {
privKeyBytes, err := hex.DecodeString(strings.TrimPrefix(validatorExitKey, "0x"))
errCheck(err, fmt.Sprintf("Failed to decode key %s", validatorExitKey))
account, err := util.NewScratchAccount(privKeyBytes, nil)
errCheck(err, "Invalid private key")
return validatorExitHandleAccountInput(account)
}
die("one of --json, --account or --key is required")
return nil, nil
}
// Ensure the validator is active.
state, err := grpc.FetchValidatorState(eth2GRPCConn, account)
errCheck(err, "Failed to obtain validator state")
outputIf(debug, fmt.Sprintf("Validator state is %v", state))
assert(state == ethpb.ValidatorStatus_ACTIVE, "Validator must be active to exit")
func validatorExitHandleJSONInput(input string) (*ethpb.VoluntaryExit, e2types.Signature) {
data := &validatorExitData{}
err := json.Unmarshal([]byte(input), data)
errCheck(err, "Invalid JSON input")
exit := &ethpb.VoluntaryExit{
Epoch: data.Epoch,
ValidatorIndex: data.ValidatorIndex,
}
signature, err := e2types.BLSSignatureFromBytes(data.Signature)
errCheck(err, "Invalid signature")
return exit, signature
}
func validatorExitHandleAccountInput(account e2wtypes.Account) (*ethpb.VoluntaryExit, e2types.Signature) {
exit := &ethpb.VoluntaryExit{}
// Beacon chain config required for later work.
config, err := grpc.FetchChainConfig(eth2GRPCConn)
errCheck(err, "Failed to obtain beacon chain configuration")
secondsPerSlot, ok := config["SecondsPerSlot"].(uint64)
assert(ok, "Failed to obtain seconds per slot from chain")
slotsPerEpoch, ok := config["SlotsPerEpoch"].(uint64)
assert(ok, "Failed to obtain slots per epoch from chain")
secondsPerEpoch := secondsPerSlot * slotsPerEpoch
// Fetch the validator's index.
index, err := grpc.FetchValidatorIndex(eth2GRPCConn, account)
errCheck(err, "Failed to obtain validator index")
outputIf(debug, fmt.Sprintf("Validator index is %d", index))
exit.ValidatorIndex = index
// Ensure the validator is active.
state, err := grpc.FetchValidatorState(eth2GRPCConn, account)
errCheck(err, "Failed to obtain validator state")
outputIf(debug, fmt.Sprintf("Validator state is %v", state))
assert(state == ethpb.ValidatorStatus_ACTIVE, "Validator must be active to exit")
if validatorExitEpoch < 0 {
// Ensure the validator has been active long enough to exit.
validator, err := grpc.FetchValidator(eth2GRPCConn, account)
errCheck(err, "Failed to obtain validator information")
outputIf(debug, fmt.Sprintf("Activation epoch is %v", validator.ActivationEpoch))
earliestExitEpoch := validator.ActivationEpoch + config["PersistentCommitteePeriod"].(uint64)
shardCommitteePeriod, ok := config["ShardCommitteePeriod"].(uint64)
assert(ok, "Failed to obtain shard committee period from chain")
earliestExitEpoch := validator.ActivationEpoch + shardCommitteePeriod
secondsPerEpoch := config["SecondsPerSlot"].(uint64) * config["SlotsPerEpoch"].(uint64)
genesisTime, err := grpc.FetchGenesis(eth2GRPCConn)
genesisTime, err := grpc.FetchGenesisTime(eth2GRPCConn)
errCheck(err, "Failed to obtain genesis time")
currentEpoch := uint64(time.Since(genesisTime).Seconds()) / secondsPerEpoch
assert(currentEpoch >= earliestExitEpoch, fmt.Sprintf("Validator cannot exit until %s ( epoch %d)", genesisTime.Add(time.Duration(secondsPerEpoch*earliestExitEpoch)*time.Second).Format(time.UnixDate), earliestExitEpoch))
assert(currentEpoch >= earliestExitEpoch, fmt.Sprintf("Validator cannot exit until %s ( epoch %d); transaction not sent", genesisTime.Add(time.Duration(secondsPerEpoch*earliestExitEpoch)*time.Second).Format(time.UnixDate), earliestExitEpoch))
outputIf(verbose, "Validator confirmed to be in a suitable state")
exit.Epoch = currentEpoch
} else {
// User-specified epoch; no checks.
exit.Epoch = uint64(validatorExitEpoch)
}
// Set up the transaction.
exit := &ethpb.VoluntaryExit{
Epoch: currentEpoch,
ValidatorIndex: index,
// 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))
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)
unlocked := false
for _, passphrase := range getPassphrases() {
err = account.Unlock([]byte(passphrase))
if err == nil {
unlocked = true
break
}
root, err := ssz.HashTreeRoot(exit)
errCheck(err, "Failed to generate exit proposal root")
// TODO fetch current fork version from config (currently using genesis fork version)
currentForkVersion := config["GenesisForkVersion"].([]byte)
domain := types.Domain(types.DomainVoluntaryExit, currentForkVersion)
}
assert(unlocked, "Failed to unlock account; please confirm passphrase is correct")
signature, err := signStruct(account, exit, domain)
errCheck(err, "Failed to sign exit proposal")
err = account.Unlock([]byte(rootAccountPassphrase))
errCheck(err, "Failed to unlock account; please confirm passphrase is correct")
signature, err := sign(account, root[:], domain)
errCheck(err, "Failed to sign exit proposal")
return exit, signature
}
// validatorExitHandleExit handles the exit request.
func validatorExitHandleExit(exit *ethpb.VoluntaryExit, signature e2types.Signature) {
if validatorExitJSONOutput {
data := &validatorExitData{
Epoch: exit.Epoch,
ValidatorIndex: exit.ValidatorIndex,
Signature: signature.Marshal(),
}
res, err := json.Marshal(data)
errCheck(err, "Failed to generate JSON")
outputIf(!quiet, string(res))
} else {
proposal := &ethpb.SignedVoluntaryExit{
Exit: exit,
Signature: signature.Marshal(),
@@ -98,17 +176,74 @@ In quiet mode this will return 0 if the transaction has been sent, otherwise 1.`
validatorClient := ethpb.NewBeaconNodeValidatorClient(eth2GRPCConn)
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
_, err = validatorClient.ProposeExit(ctx, proposal)
_, err := validatorClient.ProposeExit(ctx, proposal)
errCheck(err, "Failed to propose exit")
outputIf(!quiet, "Validator exit transaction sent")
os.Exit(_exit_success)
},
}
}
func init() {
validatorCmd.AddCommand(validatorExitCmd)
validatorFlags(validatorExitCmd)
validatorExitCmd.Flags().Int64Var(&validatorExitEpoch, "epoch", -1, "Epoch at which to exit (defaults to now)")
addTransactionFlags(validatorExitCmd)
validatorExitCmd.Flags().Int64Var(&validatorExitEpoch, "epoch", -1, "Epoch at which to exit (defaults to current epoch)")
validatorExitCmd.Flags().StringVar(&validatorExitKey, "key", "", "Private key if account not known by ethdo")
validatorExitCmd.Flags().BoolVar(&validatorExitJSONOutput, "json-output", false, "Print JSON transaction; do not broadcast to network")
validatorExitCmd.Flags().StringVar(&validatorExitJSON, "json", "", "Use JSON as created by --json-output to exit")
}
type validatorExitData struct {
Epoch uint64 `json:"epoch"`
ValidatorIndex uint64 `json:"validator_index"`
Signature []byte `json:"signature"`
}
// 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
}
// UnmarshalJSON implements custom JSON unmarshaller.
func (d *validatorExitData) UnmarshalJSON(data []byte) error {
var v map[string]interface{}
if err := json.Unmarshal(data, &v); err != nil {
return err
}
if val, exists := v["epoch"]; exists {
var ok bool
epoch, ok := val.(float64)
if !ok {
return errors.New("epoch invalid")
}
d.Epoch = uint64(epoch)
} else {
return errors.New("epoch missing")
}
if val, exists := v["validator_index"]; exists {
var ok bool
validatorIndex, ok := val.(float64)
if !ok {
return errors.New("validator_index invalid")
}
d.ValidatorIndex = uint64(validatorIndex)
} else {
return errors.New("validator_index missing")
}
if val, exists := v["signature"]; exists {
signatureBytes, ok := val.(string)
if !ok {
return errors.New("signature invalid")
}
signature, err := hex.DecodeString(strings.TrimPrefix(signatureBytes, "0x"))
if err != nil {
return errors.Wrap(err, "signature invalid")
}
d.Signature = signature
} else {
return errors.New("signature missing")
}
return nil
}

View File

@@ -24,7 +24,7 @@ import (
"github.com/spf13/cobra"
"github.com/wealdtech/ethdo/grpc"
"github.com/wealdtech/ethdo/util"
types "github.com/wealdtech/go-eth2-wallet-types"
types "github.com/wealdtech/go-eth2-wallet-types/v2"
string2eth "github.com/wealdtech/go-string2eth"
)
@@ -52,7 +52,7 @@ In quiet mode this will return 0 if the validator information can be obtained, o
} else {
pubKeyBytes, err := hex.DecodeString(strings.TrimPrefix(validatorInfoPubKey, "0x"))
errCheck(err, fmt.Sprintf("Failed to decode public key %s", validatorInfoPubKey))
account, err = util.NewScratchAccount(pubKeyBytes)
account, err = util.NewScratchAccount(nil, pubKeyBytes)
errCheck(err, fmt.Sprintf("Invalid public key %s", validatorInfoPubKey))
}
@@ -67,7 +67,7 @@ In quiet mode this will return 0 if the validator information can be obtained, o
assert(validatorInfo.Status != ethpb.ValidatorStatus_UNKNOWN_STATUS, "Not known as a validator")
if quiet {
os.Exit(_exit_success)
os.Exit(_exitSuccess)
}
outputIf(verbose, fmt.Sprintf("Epoch of data:\t\t%v", validatorInfo.Epoch))
@@ -102,7 +102,7 @@ In quiet mode this will return 0 if the validator information can be obtained, o
}
}
os.Exit(_exit_success)
os.Exit(_exitSuccess)
},
}

View File

@@ -30,7 +30,7 @@ var versionCmd = &cobra.Command{
ethdo version.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("1.3.0")
fmt.Println("1.4.21")
if viper.GetBool("verbose") {
buildInfo, ok := dbg.ReadBuildInfo()
if ok {
@@ -44,7 +44,7 @@ var versionCmd = &cobra.Command{
}
}
}
os.Exit(_exit_success)
os.Exit(_exitSuccess)
},
}

View File

@@ -14,10 +14,13 @@
package cmd
import (
"context"
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
pb "github.com/wealdtech/eth2-signer-api/pb/v1"
)
var walletAccountsCmd = &cobra.Command{
@@ -29,26 +32,51 @@ var walletAccountsCmd = &cobra.Command{
In quiet mode this will return 0 if the wallet holds any addresses, otherwise 1.`,
Run: func(cmd *cobra.Command, args []string) {
assert(walletWallet != "", "wallet is required")
wallet, err := walletFromPath(walletWallet)
errCheck(err, "Failed to access wallet")
assert(walletWallet != "", "--wallet is required")
hasAccounts := false
for account := range wallet.Accounts() {
hasAccounts = true
if verbose {
fmt.Printf("%s\n\tUUID:\t\t%s\n\tPublic key:\t0x%048x\n", account.Name(), account.ID(), account.PublicKey().Marshal())
} else if !quiet {
fmt.Printf("%s\n", account.Name())
if remote {
listerClient := pb.NewListerClient(remoteGRPCConn)
listAccountsReq := &pb.ListAccountsRequest{
Paths: []string{
walletWallet,
},
}
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
accountsResp, err := listerClient.ListAccounts(ctx, listAccountsReq)
errCheck(err, "Failed to access wallet")
assert(accountsResp.State == pb.ResponseState_SUCCEEDED, "Request to list wallet accounts failed")
walletPrefixLen := len(walletWallet) + 1
for _, account := range accountsResp.Accounts {
hasAccounts = true
if verbose {
fmt.Printf("%s\n", account.Name[walletPrefixLen:])
fmt.Printf("\tPublic key: %#048x\n", account.PublicKey)
} else if !quiet {
fmt.Printf("%s\n", account.Name[walletPrefixLen:])
}
}
} else {
wallet, err := walletFromPath(walletWallet)
errCheck(err, "Failed to access wallet")
for account := range wallet.Accounts() {
hasAccounts = true
if verbose {
fmt.Printf("%s\n\tUUID:\t\t%s\n\tPublic key:\t0x%048x\n", account.Name(), account.ID(), account.PublicKey().Marshal())
} else if !quiet {
fmt.Printf("%s\n", account.Name())
}
}
}
if quiet {
if hasAccounts {
os.Exit(_exit_success)
os.Exit(_exitSuccess)
}
os.Exit(_exit_failure)
os.Exit(_exitFailure)
}
},
}

View File

@@ -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
@@ -17,10 +17,14 @@ import (
"strings"
"github.com/spf13/cobra"
wallet "github.com/wealdtech/go-eth2-wallet"
bip39 "github.com/tyler-smith/go-bip39"
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
hd "github.com/wealdtech/go-eth2-wallet-hd/v2"
nd "github.com/wealdtech/go-eth2-wallet-nd/v2"
)
var walletCreateType string
var walletCreateSeed string
var walletCreateCmd = &cobra.Command{
Use: "create",
@@ -31,16 +35,18 @@ var walletCreateCmd = &cobra.Command{
In quiet mode this will return 0 if the wallet is created successfully, otherwise 1.`,
Run: func(cmd *cobra.Command, args []string) {
assert(!remote, "wallet create not available with remote wallets")
assert(walletWallet != "", "--wallet is required")
assert(walletCreateType != "", "--type is required")
var err error
switch strings.ToLower(walletCreateType) {
case "non-deterministic", "nd":
_, err = wallet.CreateWallet(walletWallet, wallet.WithType("nd"))
assert(walletCreateSeed == "", "--seed is not allowed with non-deterministic wallets")
err = walletCreateND(walletWallet)
case "hierarchical deterministic", "hd":
assert(rootWalletPassphrase != "", "--walletpassphrase is required for hierarchical deterministic wallets")
_, err = wallet.CreateWallet(walletWallet, wallet.WithType("hd"), wallet.WithPassphrase([]byte(rootWalletPassphrase)))
assert(getWalletPassphrase() != "", "--walletpassphrase is required for hierarchical deterministic wallets")
err = walletCreateHD(walletWallet, getWalletPassphrase(), walletCreateSeed)
default:
die("unknown wallet type")
}
@@ -48,8 +54,34 @@ In quiet mode this will return 0 if the wallet is created successfully, otherwis
},
}
// walletCreateND creates a non-deterministic wallet.
func walletCreateND(name string) error {
_, err := nd.CreateWallet(name, store, keystorev4.New())
return err
}
// walletCreateND creates a hierarchical-deterministic wallet.
func walletCreateHD(name string, passphrase string, seedPhrase 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(name, []byte(passphrase), store, encryptor, seed)
return err
}
// Create wallet with a random seed.
_, err := hd.CreateWallet(name, []byte(passphrase), store, encryptor)
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")
}

55
cmd/walletdelete.go Normal file
View File

@@ -0,0 +1,55 @@
// 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 (
"os"
"path/filepath"
"github.com/spf13/cobra"
wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
)
var walletDeleteCmd = &cobra.Command{
Use: "delete",
Short: "Delete a wallet",
Long: `Delete a wallet. For example:
ethdo wallet delete --wallet=primary
In quiet mode this will return 0 if the wallet has been deleted, otherwise 1.`,
Run: func(cmd *cobra.Command, args []string) {
assert(!remote, "wallet delete not available with remote wallets")
assert(walletWallet != "", "--wallet is required")
wallet, err := walletFromPath(walletWallet)
errCheck(err, "Failed to access wallet")
storeProvider, ok := wallet.(wtypes.StoreProvider)
assert(ok, "Cannot obtain store for the wallet")
store := storeProvider.Store()
storeLocationProvider, ok := store.(wtypes.StoreLocationProvider)
assert(ok, "Cannot obtain store location for the wallet")
walletLocation := filepath.Join(storeLocationProvider.Location(), wallet.ID().String())
err = os.RemoveAll(walletLocation)
errCheck(err, "Failed to delete wallet")
os.Exit(_exitSuccess)
},
}
func init() {
walletCmd.AddCommand(walletDeleteCmd)
walletFlags(walletDeleteCmd)
}

View File

@@ -18,7 +18,7 @@ import (
"os"
"github.com/spf13/cobra"
types "github.com/wealdtech/go-eth2-wallet-types"
types "github.com/wealdtech/go-eth2-wallet-types/v2"
)
var walletExportPassphrase string
@@ -32,6 +32,7 @@ var walletExportCmd = &cobra.Command{
In quiet mode this will return 0 if the wallet is able to be exported, otherwise 1.`,
Run: func(cmd *cobra.Command, args []string) {
assert(!remote, "wallet export not available with remote wallets")
assert(walletWallet != "", "--wallet is required")
assert(walletExportPassphrase != "", "--exportpassphrase is required")
@@ -45,7 +46,7 @@ In quiet mode this will return 0 if the wallet is able to be exported, otherwise
errCheck(err, "Failed to export wallet")
outputIf(!quiet, fmt.Sprintf("0x%x", exportData))
os.Exit(_exit_success)
os.Exit(_exitSuccess)
},
}

View File

@@ -14,18 +14,22 @@
package cmd
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/google/uuid"
"github.com/spf13/cobra"
"github.com/wealdtech/go-bytesutil"
wallet "github.com/wealdtech/go-eth2-wallet"
"github.com/wealdtech/go-ecodec"
e2wallet "github.com/wealdtech/go-eth2-wallet"
)
var walletImportData string
var walletImportPassphrase string
var walletImportVerify bool
var walletImportCmd = &cobra.Command{
Use: "import",
@@ -36,8 +40,10 @@ var walletImportCmd = &cobra.Command{
In quiet mode this will return 0 if the wallet is imported successfully, otherwise 1.`,
Run: func(cmd *cobra.Command, args []string) {
assert(walletImportData != "", "--walletimportdata is required")
assert(!remote, "wallet import not available with remote wallets")
assert(walletImportData != "", "--importdata is required")
assert(walletImportPassphrase != "", "--importpassphrase is required")
assert(walletWallet == "", "--wallet is not allowed (the wallet will retain its name)")
if !strings.HasPrefix(walletImportData, "0x") {
outputIf(debug, fmt.Sprintf("Reading wallet import from file %s", walletImportData))
@@ -50,10 +56,44 @@ In quiet mode this will return 0 if the wallet is imported successfully, otherwi
importData, err := bytesutil.FromHexString(walletImportData)
errCheck(err, "Failed to decode wallet data")
_, err = wallet.ImportWallet(importData, []byte(walletImportPassphrase))
errCheck(err, "Failed to import wallet")
if walletImportVerify {
type accountInfo struct {
Name string `json:"name"`
}
type walletInfo struct {
ID uuid.UUID `json:"uuid"`
Name string `json:"name"`
Type string `json:"type"`
}
type export struct {
Wallet *walletInfo `json:"wallet"`
Accounts []*accountInfo `json:"accounts"`
}
os.Exit(_exit_success)
data, err := ecodec.Decrypt(importData, []byte(walletImportPassphrase))
errCheck(err, "Failed to decrypt wallet")
ext := &export{}
err = json.Unmarshal(data, ext)
errCheck(err, "Failed to read wallet")
outputIf(!quiet, fmt.Sprintf("Wallet name: %s", ext.Wallet.Name))
outputIf(!quiet, fmt.Sprintf("Wallet type: %s", ext.Wallet.Type))
outputIf(verbose, fmt.Sprintf("Wallet UUID: %s", ext.Wallet.ID))
if verbose {
fmt.Printf("Wallet accounts:\n")
for _, account := range ext.Accounts {
outputIf(verbose, fmt.Sprintf(" %s", account.Name))
}
} else {
outputIf(!quiet, fmt.Sprintf("Wallet accounts: %d", len(ext.Accounts)))
}
} else {
_, err = e2wallet.ImportWallet(importData, []byte(walletImportPassphrase))
errCheck(err, "Failed to import wallet")
}
os.Exit(_exitSuccess)
},
}
@@ -62,4 +102,5 @@ func init() {
walletFlags(walletImportCmd)
walletImportCmd.Flags().StringVar(&walletImportData, "importdata", "", "The data to import, or the name of a file to read")
walletImportCmd.Flags().StringVar(&walletImportPassphrase, "importpassphrase", "", "Passphrase protecting the data to import")
walletImportCmd.Flags().BoolVar(&walletImportVerify, "verify", false, "Verify the wallet can be imported, but do not import it")
}

View File

@@ -16,9 +16,10 @@ package cmd
import (
"fmt"
"os"
"path/filepath"
"github.com/spf13/cobra"
wtypes "github.com/wealdtech/go-eth2-wallet-types"
wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
)
var walletInfoCmd = &cobra.Command{
@@ -30,7 +31,8 @@ var walletInfoCmd = &cobra.Command{
In quiet mode this will return 0 if the wallet exists, otherwise 1.`,
Run: func(cmd *cobra.Command, args []string) {
assert(walletWallet != "", "Wallet is required")
assert(!remote, "wallet info not available with remote wallets")
assert(walletWallet != "", "--wallet is required")
wallet, err := walletFromPath(walletWallet)
errCheck(err, "unknown wallet")
@@ -46,7 +48,7 @@ In quiet mode this will return 0 if the wallet exists, otherwise 1.`,
store := storeProvider.Store()
fmt.Printf("Store: %s\n", store.Name())
if storeLocationProvider, ok := store.(wtypes.StoreLocationProvider); ok {
fmt.Printf("Location: %s\n", storeLocationProvider.Location())
fmt.Printf("Location: %s\n", filepath.Join(storeLocationProvider.Location(), wallet.ID().String()))
}
}
}

View File

@@ -18,7 +18,7 @@ import (
"os"
"github.com/spf13/cobra"
wallet "github.com/wealdtech/go-eth2-wallet"
e2wallet "github.com/wealdtech/go-eth2-wallet"
)
var walletListCmd = &cobra.Command{
@@ -30,17 +30,23 @@ var walletListCmd = &cobra.Command{
In quiet mode this will return 0 if any wallets are found, otherwise 1.`,
Run: func(cmd *cobra.Command, args []string) {
assert(!remote, "wallet list not available with remote wallets")
walletsFound := false
for w := range wallet.Wallets() {
walletsFound = true
outputIf(!quiet && !verbose, w.Name())
outputIf(verbose, fmt.Sprintf("%s\n\tUUID:\t\t%s", w.Name(), w.ID().String()))
if remote {
die("Remote wallets cannot be listed")
} else {
for w := range e2wallet.Wallets() {
walletsFound = true
outputIf(!quiet && !verbose, w.Name())
outputIf(verbose, fmt.Sprintf("%s\n\tUUID:\t\t%s", w.Name(), w.ID().String()))
}
}
if !walletsFound {
os.Exit(_exit_failure)
os.Exit(_exitFailure)
}
os.Exit(_exit_success)
os.Exit(_exitSuccess)
},
}

View File

@@ -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
@@ -14,12 +14,13 @@
package cmd
import (
"bytes"
"fmt"
"os"
bip39 "github.com/FactomProject/go-bip39"
"github.com/spf13/cobra"
types "github.com/wealdtech/go-eth2-wallet-types"
bip39 "github.com/tyler-smith/go-bip39"
types "github.com/wealdtech/go-eth2-wallet-types/v2"
)
var walletSeedCmd = &cobra.Command{
@@ -31,24 +32,33 @@ var walletSeedCmd = &cobra.Command{
In quiet mode this will return 0 if the wallet is a hierarchical deterministic wallet, otherwise 1.`,
Run: func(cmd *cobra.Command, args []string) {
assert(!remote, "wallet seed not available with remote wallets")
assert(walletWallet != "", "--wallet is required")
assert(rootWalletPassphrase != "", "--walletpassphrase is required")
assert(getWalletPassphrase() != "", "--walletpassphrase is required")
wallet, err := walletFromPath(walletWallet)
errCheck(err, "Failed to access wallet")
_, ok := wallet.(types.WalletKeyProvider)
assert(ok, fmt.Sprintf("wallets of type %q do not provide keys", wallet.Type()))
assert(ok, fmt.Sprintf("wallets of type %q do not have a seed", wallet.Type()))
err = wallet.Unlock([]byte(rootWalletPassphrase))
err = wallet.Unlock([]byte(getWalletPassphrase()))
errCheck(err, "Failed to unlock wallet")
seed, err := wallet.(types.WalletKeyProvider).Key()
errCheck(err, "Failed to obtain wallet key")
outputIf(debug, fmt.Sprintf("Seed is %#0x", seed))
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(_exit_success)
os.Exit(_exitSuccess)
},
}

67
docs/howto.md Normal file
View File

@@ -0,0 +1,67 @@
# How to achieve common tasks with ethdo
## Find out what ethdo can do
To find a list of topics that ethdo can carry out with the `ethdo help` command.
If you want more detailed information about the commands in a topic, they can be seen with the `ethdo help <topic>` command, for example:
```sh
ethdo help wallet
```
## List my wallets
The wallets you can currently access can be seen with the `ethdo wallet list` command.
## Create a new wallet
New wallets can be created with the `ethdo wallet create` command. Each wallet has to have a unique name, for example:
```sh
ethdo wallet create --wallet="My wallet"
```
Additional options are available to decide the type of wallet and encryption.
## Create an HD wallet from an existing seed
HD wallets can be created from an existing seed by adding the `--seed` parameter to `ethdo wallet create`, for example:
```sh
ethdo wallet create --wallet="Recreated wallet" --type=hd --seed="tooth moon mad fun romance athlete envelope next mix divert tip top symbol resemble stock family melody desk sheriff drift bargain need jaguar method"
```
The seed for an existing HD wallet can be obtained with `ethdo wallet seed`.
## Back up a wallet
A wallet can be backed up with the `ethdo wallet export` command. This creates an encrypted backup of the wallet, for example:
```sh
ethdo wallet export --wallet="My wallet" --exportpassphrase="export secret" >export.dat
```
Note that by default the wallet backup is printed to the console, hence the `>export.dat` to redirect it to a file.
## Restore a wallet
A backed up wallet can be restored with the `ethdo wallet import` command, for example:
```sh
ethdo wallet import --importdata=export.dat --importpassphrase="export secret"
```
In this example the wallet to be imported is being read from the `export.dat` file.
Note that if a wallet with the same name already exists it cannot be imported.
## Where is my wallet?
Details of the location of a wallet can be found with the `ethdo wallet info` command, for example:
```sh
ethdo wallet info --verbose --wallet="My wallet"
```
This will provide, amongst other information, a `Location` line giving the directory where the wallet information resides.

244
docs/prysm.md Normal file
View File

@@ -0,0 +1,244 @@
# Using ethdo with Prysm
## 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/).
## Typical validating setups
This section outlines the process of setting up a configuration with two validators and a single withdrawal account using ethdo.
### Generating a wallet
To create a non-deterministic wallet, where keys generated from random data, issue the command:
```sh
ethdo wallet create --wallet=Validators
```
If you prefer to have a hierarchical deterministic wallet, where keys are generated from a seed, issue the command:
```sh
ethdo wallet create --wallet=Validators --type=hd --walletpassphrase=walletsecret
```
This creates a wallet called "Validators" in the default wallet directory (see https://github.com/wealdtech/ethdo/#wallets-and-accounts for details) which contains the newly generated seed data.
> The `--walletpassphrase` flag and input is required to protect the seed. It is critical that you keep it private and secure.
Once the wallet is created, fetch its data to ensure it exists by issuing the following command:
```sh
ethdo wallet info --wallet=Validators
```
This command will produce output like so:
```sh
Type: non-deterministic
Accounts: 0
```
### Generating accounts
To create two separate accounts with different passphrases, issue the command:
```sh
ethdo account create --account=Validators/1 --passphrase=validator1secret
ethdo account create --account=Validators/2 --passphrase=validator2secret
```
> The two accounts are given different passphrases in the above example. This is not required; all accounts can have the same password if you prefer.
### Creating a withdrawal wallet and account
It is recommended to set up separate wallets for withdrawals and validator nodes. This allows users to have a validator wallet actively running on the node, while a second wallet can be kept securely offline in cold storage.
Creating a withdrawal wallet and account is very similar to the process above to generate the validator wallet. For example:
```sh
ethdo wallet create --wallet=Withdrawal
ethdo account create --account=Withdrawal/Primary --passphrase=withdrawalsecret
```
This creates a wallet called "Withdrawal" and within it an account called "Primary". It is also possible to apply additional protection to the Withdrawal wallet if desired; see the `ethdo` documentation for details.
### Depositing funds for a validator
The validator now requires deposited funds. If you do not have any Göerli Ether, the best approach is to follow the steps at https://prylabs.net/participate to use the faucet and make a deposit -- **however**, for step 3, do not run the commands provided. Instead, run the following command to generate the deposit data requested:
```sh
ethdo validator depositdata \
--validatoraccount=Validators/1 \
--withdrawalaccount=Withdrawal/Primary \
--depositvalue=32Ether \
--passphrase=validator1secret \
--raw
```
The raw data output of this command can be pasted in to the webpage above to generate the required transaction for validator 1 (and can be repeated for validator 2, or as many validators as you wish).
Alternatively, if you have your own Göerli ETH, you can send deposit transactions directly to the Göerli testnet. You can create JSON output containing the deposit data:
```sh
ethdo validator depositdata \
--validatoraccount=Validators/1 \
--withdrawalaccount=Withdrawal/Primary \
--depositvalue=32Ether \
--passphrase=validator1secret
{"account":"Validators/1","pubkey":"a9ca9cf7fa2d0ab1d5d52d2d8f79f68c50c5296bfce81546c254df68eaac0418717b2f9fc6655cbbddb145daeb282c00","withdrawal_credentials":"0059a28dc2db987d59bdfc4ab20b9ad4c83888bcd32456a629aece07de6895aa","signature":"9335b872253fdab328678bd3636115681d52b42fe826c6acb7f1cd1327c6bba48e3231d054e4f274cc7c1c184f28263b13083e01db8c08c17b59f22277dff341f7c96e7a0407a0a31c8563bcf479d31136c833712ae3bfd93ee9ea6abdfa52d4","value":3200000000,"deposit_data_root":"14278c9345eeeb7b2d5307a36ed1c72eea5ed09a30cf7c47525e34f39f564ef5"}
```
This can be passed to [ethereal](https://github.com/wealdtech/ethereal) to send the deposit (replacing `0x21A1A52aba41DB18F9F1D2625e1b19A251F3e0A9` below with your local Göerli account containing the funds and `eth1secret` with that account's passphrase; `ethereal --network=goerli account list --verbose` will provide details of your local accounts and their current funds).
on Linux/OSX:
```sh
DEPOSITDATA=`ethdo validator depositdata \
--validatoraccount=Validators/1 \
--withdrawalaccount=Withdrawal/Primary \
--depositvalue=32Ether \
--passphrase=validator1secret`
ethereal beacon deposit \
--network=goerli \
--data="${DEPOSITDATA}" \
--from=0x21A1A52aba41DB18F9F1D2625e1b19A251F3e0A9 \
--passphrase=eth1secret
```
or on Windows:
```sh
ethdo validator depositdata \
--validatoraccount=Validators/1 \
--withdrawalaccount=Withdrawal/Primary \
--depositvalue=32Ether \
--passphrase=validator1secret >depositdata.json
ethereal beacon deposit \
--network=goerli \
--data=depositdata.json \
--from=0x21A1A52aba41DB18F9F1D2625e1b19A251F3e0A9 \
--passphrase=eth1secret
erase depositdata.json
```
The `ethereal` command can either take a `passphrase`, if the `from` address is a local account (confirm with `ethereal --network=goerli account list`) or a `privatekey` if not.
### Validating
The next step is to start the validator using the validating keys that have been created.
#### Keymanager options
Although options for the wallet keymanager can be supplied directly on the command-line this is not considered best practice, as it exposes sensitive information such as passphrases, so it is better to create a file that contains this information and reference that file.
To create the relevant directory run the following for Linux/OSX:
```sh
mkdir -p ${HOME}/prysm/validator
```
or for Windows:
```sh
mkdir %APPDATA%\prysm\validator
```
and then use your favourite text editor to create a file in this directory called `wallet.json` with the following contents:
```json
{
"accounts": [
"Validators/1",
"Validators/2"
],
"passphrases": [
"validator1secret",
"validator2secret"
]
}
```
#### Starting the validator with Bazel
To start the validator you must supply the desired keymanager and the location of the keymanager options file. Run the following command for Linux/OSX:
```sh
bazel run //validator:validator -- --keymanager=wallet --keymanageropts=${HOME}/prysm/validator/wallet.json
```
or for Windows:
```sh
bazel run //validator:validator -- --keymanager=wallet --keymanageropts=%APPDATA%\prysm\validator\wallet.json
```
#### Starting the validator with Docker
Docker will not have direct access to the wallet created above, and requires the keymanager to be informed of the mapped location of the wallet. Edit the `wallet.json` file to include a location entry, as follows:
```json
{
"location": "/wallets",
"accounts": [
"Validators/1",
"Validators/2"
],
"passphrases": [
"validator1secret",
"validator2secret"
]
}
```
Then run the validator by issuing the following command on Linux:
```sh
docker run -v "${HOME}/prysm/validator:/data" \
-v "${HOME}/.config/ethereum2/wallets:/wallets" \
gcr.io/prysmaticlabs/prysm/validator:latest \
--keymanager=wallet \
--keymanageropts=/data/wallet.json
```
or for OSX:
```sh
docker run -v "${HOME}/prysm/validator:/data" \
-v "${HOME}/Library/Application Support/ethereum2/wallets:/wallets" \
gcr.io/prysmaticlabs/prysm/validator:latest \
--keymanager=wallet \
--keymanageropts=/data/wallet.json
```
or for Windows:
```sh
docker run -v %APPDATA%\prysm\validator:/data" \
-v %APPDATA%\ethereum2\wallets:/wallets" \
gcr.io/prysmaticlabs/prysm/validator:latest \
--keymanager=wallet \
--keymanageropts=/data/wallet.json
```
#### Confirming validation
When the validator is operational, you should see output similar to:
```text
[2020-02-07 10:00:59] INFO node: Validating for public key pubKey=0x85016bd4ca67e57e1438308fdb3d98b74b81428fb09e6d16d2dcbc72f240be090d5faebb63f84d6f35a950fdbb36f910
[2020-02-07 10:00:59] INFO node: Validating for public key pubKey=0x8de04b4cd3f0947f4e76fa2f86fa1cfd33cc2500688f2757e406448c36f0f1255758874b46d72002ad206ed560975d39
```
where each line states a public key that is being used for validating. Confirm that these values match your expectations.

33
docs/troubleshooting.md Normal file
View File

@@ -0,0 +1,33 @@
# Troubleshooting
## Compilation problems
### 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:
```sh
sudo apt install build-essential libstdc++6
```
and then try to install `ethdo` again.
## 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:
- Linux, Mac: `$HOME/go/bin`
- Windows: `%USERPROFILE%\go\bin`
You must add these paths to be able to access `ethdo`. To add the path on linux or OSX type:
```sh
export PATH=$PATH:$(go env GOPATH)/bin
```
and on Windows type:
```sh
setx /M path "%PATH%;%USERPROFILE%\go\bin"
```

423
docs/usage.md Normal file
View File

@@ -0,0 +1,423 @@
# ethdo commands
ethdo provides features to manage wallets and accounts, as well as interacting with Ethereum 2 nodes and remote signers. Below are a list of all available commands.
Note that the below provides a list of commands rather than a howto guide. Please follow the
### `wallet` commands
#### `accounts`
`ethdo wallet accounts` lists the accounts within a wallet.
```sh
$ ethdo wallet accounts --wallet="Personal wallet"
Auctions
Operations
Spending
```
With the `--verbose` flag this will provide the public key of the accounts.
```sh
$ ethdo wallet accounts --wallet="Personal wallet" --verbose
Auctions: 0x812f340269c315c1d882ae7c13cdaddf862dbdbd482b1836798b2070160dd1e194088cc6f39347782028d1e56bd18674
Operations: 0x8e2f9e8cc29658ff37ecc30e95a0807579b224586c185d128cb7a7490784c1ad9b0ab93dbe604ab075b40079931e6670
Spending: 0x85dfc6dcee4c9da36f6473ec02fda283d6c920c641fc8e3a76113c5c227d4aeeb100efcfec977b12d20d571907d05650
```
#### `create`
`ethdo wallet create` creates a new wallet with the given parameters. Options for creating a wallet include:
- `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
```sh
$ ethdo wallet create --wallet="Personal wallet" --type="hd" --walletpassphrase="my wallet secret"
```
#### `delete`
`ethdo wallet delete` deletes a wallet. Options for deleting a wallet include:
- `wallet`: the name of the wallet to delete
```sh
$ ethdo wallet delete --wallet="Old wallet"
```
**Warning** Deleting a wallet is permanent. Only use this command if you really don't want the wallet, or you have securely backed the wallet up using `wallet export`.
#### `export`
`ethdo wallet export` exports the wallet and all of its accounts. Options for exporting a wallet include:
- `wallet`: the name of the wallet to export (defaults to "primary")
- `exportpassphrase`: the passphrase with which to encrypt the wallet backup
```sh
$ ethdo wallet export --wallet="Personal wallet" --exportpassphrase="my export secret"
0x01c7a27ad40d45b4ae5be5f...
```
The encrypted wallet export is written to the console; it can be redirected to store it in a file.
```sh
$ ethdo wallet export --wallet="Personal wallet" --exportpassphrase="my export secret" >export.dat
```
#### `import`
`ethdo wallet import` imports a wallet and all of its accounts exported by `ethdo wallet export`. Options for importing a wallet include:
- `importdata`: the data exported by `ethdo wallet export`
- `importpassphrase`: the passphrase that was provided to `ethdo wallet export` to encrypt the data
- `verify`: confirm information about the wallet import without importing it
```sh
$ ethdo wallet import --importdata="0x01c7a27ad40d45b4ae5be5f..." --importpassphrase="my export secret"
```
The encrypted wallet export can be read from a file. For example with Unix systems:
```sh
$ ethdo wallet import --importdata=`cat export.dat` --importpassphrase="my export secret"
```
#### `info`
`ethdo wallet info` provides information about a given wallet. Options include:
- `wallet`: the name of the wallet
```sh
$ ethdo wallet info --wallet="Personal wallet"
Type: hierarchical deterministic
Accounts: 3
```
#### `list`
`ethdo wallet list` lists all wallets in the store.
```sh
$ ethdo wallet list
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.
#### `create`
`ethdo account create` creates a new account with the given parameters. Options for creating an account include:
- `account`: the name of the account to create
- `passphrase`: the passphrase for the account
Note that for hierarchical deterministic wallets you will also need to supply `--walletpassphrase` to unlock the wallet seed.
```sh
$ ethdo account create --account="Personal wallet/Operations" --walletpassphrase="my wallet secret" --passphrase="my account secret"
```
#### `import`
`ethdo account import` creates a new account by importing its private key. Options for creating the account include:
- `account`: the name of the account to create
- `passphrase`: the passphrase for the account
- `key`: the private key to import
```sh
$ ethdo account import --account=Validators/123 --key=6dd12d588d1c05ba40e80880ac7e894aa20babdbf16da52eae26b3f267d68032 --passphrase="my account secret"
```
#### `info`
`ethdo account info` provides information about the given account. Options include:
- `account`: the name of the account on which to obtain information
```sh
$ ethdo account info --account="Personal wallet/Operations"
Public key: 0x8e2f9e8cc29658ff37ecc30e95a0807579b224586c185d128cb7a7490784c1ad9b0ab93dbe604ab075b40079931e6670
```
#### `key`
`ethdo account key` provides the private key for an account. Options include:
- `account`: the name of the account on which to obtain information
- `passphrase`: the passphrase for the account
```sh
$ ethdo account key --account=interop/00001 --passphrase=secret
0x51d0b65185db6989ab0b560d6deed19c7ead0e24b9b6372cbecb1f26bdfad000
```
#### `lock`
`ethdo account lock` manually locks an account on a remote signer. Locked accounts cannot carry out signing requests. Options include:
- `account`: the name of the account to lock
Note that this command only works with remote signers; it has no effect on local accounts.
```sh
$ ethdo account lock --account=Validators/123
```
#### `unlock`
`ethdo account unlock` manually unlocks an account on a remote signer. Unlocked accounts cannot carry out signing requests. Options include:
- `account`: the name of the account to unlock
- `passphrase`: the passphrase for the account
Note that this command only works with remote signers; it has no effect on local accounts.
```sh
$ ethdo account unlock --account=Validators/123 --passphrase="my secret passphrase"
```
### `signature` commands
Signature commands focus on generation and verification of data signatures.
#### `signature sign`
`ethdo signature sign` signs provided data. Options include:
- `data`: the data to sign, as a hex string
- `domain`: the domain in which to sign the data. This is a 32-byte hex string
- `account`: the account to sign the data
- `passphrase`: the passphrase for the account
```sh
$ ethdo signature sign --data="0x08140077a94642919041503caf5cc1c89c7744a2a08d43cec91df1795b23ecf2" --account="Personal wallet/Operations" --passphrase="my account secret"
0x87c83b31081744667406a11170c5585a11195621d0d3f796bd9006ac4cb5f61c10bf8c5b3014cd4f792b143a644cae100cb3155e8b00a961287bd9e7a5e18cb3b80930708bc9074d11ff47f1e8b9dd0b633e71bcea725fc3e550fdc259c3d130
```
#### `signature verify`
`ethdo signature verify` verifies signed data. Options include:
- `data`: the data whose signature to verify, as a hex string
- `signature`: the signature to verify, as a hex string
- `account`: the account which signed the data (if available as an account)
- `signer`: the public key of the account which signed the data (if not available as an account)
```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"
Verified
```
The same rules apply to `ethereal signature verify` as those in `ethereal signature sign` above.
### `version`
`ethdo version` provides the current version of ethdo. For example:
```sh
$ ethdo version
1.4.0
```
### `block` commands
Block commands focus on providing information about Ethereum 2 blocks.
#### `info`
`ethdo block info` obtains information about a block in Ethereum 2. Options include:
- `slot`: the slot at which to attempt to fetch the block
```sh
$ ethdo block info --slot=80
Attestations: 1
Attester slashings: 0
Deposits: 0
Voluntary exits: 0
```
Additional information is supplied when using `--verbose`
```sh
$ ethdo block info --slot=80 --verbose
Parent root: 0x9a08aab7d5bbc816a9d2c20c79895519da2045e99ac6782ab3d05323a395fe51
State root: 0xc6a2626ba5cb37f984bdc4da4dc93a5012be5b69fdcebc50be70a1181a290265
Ethereum 1 deposit count: 512
Ethereum 1 deposit root: 0x05b88acdde2092e1ecf35714dca0ccf82fb7e73180643f51d3139553136d125f
Ethereum 1 block hash: 0x2b8d87e016376d83b2c04c1e626172a3f8bef3b4a37d7f2f3f76d0c62acdf573
Attestations: 1
0:
Committee index: 0
Attesters: 17
Aggregation bits: ✓✓✓✓✓✓✓✓ ✓✓✓✓✓✓✓✓ ✕✕✕✕✕✕✕✓
Slot: 79
Beacon block root: 0x9a08aab7d5bbc816a9d2c20c79895519da2045e99ac6782ab3d05323a395fe51
Source epoch: 0
Source root: 0x0000000000000000000000000000000000000000000000000000000000000000
Target epoch: 2
Target root: 0xb93273c516fc817e64fab53ff4093f295e5da463582e85e1ca60800e9464faf2
Attester slashings: 0
Deposits: 0
Voluntary exits: 0
```
### `chain` commands
Chain commands focus on providing information about Ethereum 2 chains.
#### `info`
`ethdo chain info` obtains information about an Ethereum 2 chain.
```sh
$ ethdo chain info
Genesis time: Thu Apr 16 08:02:43 BST 2020
```
Additional information is supplied when using `--verbose`
```sh
$ ethdo chain info --verbose
Genesis time: Thu Apr 16 08:02:43 BST 2020
Genesis fork version: 00000000
Seconds per slot: 12
Slots per epoch: 32
```
#### `status`
`ethdo chain status` obtains the status of an Ethereum 2 chain from the node's point of view. Options include:
- `slot` show output in terms of slots rather than epochs
```sh
$ ethdo chain status
Current epoch: 5
Justified epoch: 4
Finalized epoch: 3
```
Additional information is supplied when using `--verbose`
```sh
$ ethdo chain status --verbose
Current epoch: 5
Justified epoch: 4
Justified epoch distance 1
Finalized epoch: 3
Finalized epoch distance: 2
Prior justified epoch: 3
Prior justified epoch distance: 4
```
### `node` commands
Node commands focus on information from an Ethereum 2 node.
#### `info`
`ethdo node info` obtains the information about an Ethereum 2 node.
```sh
$ ethdo node info
Syncing: false
Current slot: 178
Current epoch: 5
```
Additional information is supplied when using `--verbose`
```sh
$ ethdo node info --verbose
Version: Prysm/Git commit: b0aa6e22455e4d9cb8720a259771fbbbd22dc3ec. Built at: 2020-04-16T08:02:43+01:00
Syncing: false
Current slot: 178
Current epoch: 5
Genesis timestamp: 1587020563
```
### `validator` commands
Validator commands focus on interaction with Ethereum 2 validators.
#### `depositdata`
`ethdo validator depositdata` generates the data required to deposit one or more Ethereum 2 validators. Options include:
- `withdrawalaccount` specify the account to be used for the withdrawal credentials (if withdrawalpubkey is not supplied)
- `withdrawalpubkey` specify the public key to be used for the withdrawal credentials (if withdrawalaccount is not supplied)
- `validatoraccount` specify the account to be used for the validator
- `depositvalue` specify the amount of the deposit
- `forkversion` specify the fork version for the deposit signature; this should not be included unless the deposit is being generated offline. Note that supplying an incorrect value could result in the loss of your deposit, so only supply this value if you are sure you know what you are doing
- `raw` generate raw hex output that can be supplied as the data to an Ethereum 1 deposit transaction
#### `exit`
`ethdo validator exit` sends a transaction to the chain to tell an active validator to exit the validation queue. Options include:
- `epoch` specify an epoch before which this exit is not valid
- `json-output` generate JSON output rather than sending a transaction immediately
- `json` use JSON input created by the `--json-output` option rather than generate data from scratch
- `forkversion` specify a specific fork version; default is to fetch it from the chain but this can be used when generating offline deposits
```sh
$ ethdo validator exit --account=Validators/1 --passphrase="my validator secret"
```
To send a transaction when the account is not accessible to ethdo accout you can use the validator's private key instead:
```sh
$ ethdo validator exit --key=0x01e748d098d3bcb477d636f19d510399ae18205fadf9814ee67052f88c1f88c0
```
#### `info`
`ethdo validator info` provides information for a given validator.
```sh
$ ethdo validator info --account=Validators/1
Status: Active
Balance: 3.203823585 Ether
Effective balance: 3.1 Ether
```
Additional information is supplied when using `--verbose`
```sh
$ ethdo validator info --account=Validators/1 --verbose
Epoch of data: 3398
Index: 26913
Public key: 0xb3bb6b7a8d809e59544472853d219499765bf01d14de1e0549bd6fc2a86627ac9033264c84cd503b6339e3334726562f
Status: Active
Balance: 3.204026813 Ether
Effective balance: 3.1 Ether
Withdrawal credentials: 0x0033ef3cb10b36d0771ffe8a02bc5bfc7e64ea2f398ce77e25bb78989edbee36
```
If the validator is not an account it can be queried directly with `--pubkey`.
```sh
$ ethdo validator info --pubkey=0x842dd66cfeaeff4397fc7c94f7350d2131ca0c4ad14ff727963be9a1edb4526604970df6010c3da6474a9820fa81642b
Status: Active
Balance: 3.201850307 Ether
Effective balance: 3.1 Ether
```
## Maintainers
Jim McDonald: [@mcdee](https://github.com/mcdee).
## Contribute
Contributions welcome. Please check out [the issues](https://github.com/wealdtech/ethdo/issues).
## License
[Apache-2.0](LICENSE) © 2019, 2020 Weald Technology Trading Ltd

57
go.mod
View File

@@ -3,41 +3,42 @@ module github.com/wealdtech/ethdo
go 1.13
require (
github.com/FactomProject/go-bip39 v0.3.5
github.com/OneOfOne/xxhash v1.2.5 // indirect
github.com/aws/aws-sdk-go v1.29.2 // indirect
github.com/dgraph-io/ristretto v0.0.1 // indirect
github.com/golang/protobuf v1.3.3
github.com/google/go-cmp v0.3.1 // 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.13.0 // indirect
github.com/minio/highwayhash v1.0.0 // indirect
github.com/minio/sha256-simd v0.1.1 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.14.6 // indirect
github.com/mitchellh/go-homedir v1.1.0
github.com/pelletier/go-toml v1.6.0 // indirect
github.com/mitchellh/mapstructure v1.3.2 // indirect
github.com/pelletier/go-toml v1.8.0 // indirect
github.com/pkg/errors v0.9.1
github.com/protolambda/zssz v0.1.4 // indirect
github.com/prysmaticlabs/ethereumapis v0.0.0-20200213224628-b7452dde4ca3
github.com/prysmaticlabs/go-bitfield v0.0.0-20191017011753-53b773adde52 // indirect
github.com/prysmaticlabs/go-ssz v0.0.0-20200101200214-e24db4d9e963
github.com/prysmaticlabs/ethereumapis v0.0.0-20200619200018-174e3b90d786
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.2.2 // indirect
github.com/spf13/afero v1.3.0 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/cobra v0.0.5
github.com/spf13/cobra v1.0.0
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.6.2
github.com/wealdtech/go-bytesutil v1.1.0
github.com/wealdtech/go-eth2-types v1.0.0
github.com/wealdtech/go-eth2-util v1.1.0
github.com/wealdtech/go-eth2-wallet v1.9.1
github.com/wealdtech/go-eth2-wallet-types v1.10.0
github.com/spf13/viper v1.7.0
github.com/tyler-smith/go-bip39 v1.0.2
github.com/wealdtech/eth2-signer-api v1.4.0
github.com/wealdtech/go-bytesutil v1.1.1
github.com/wealdtech/go-ecodec v1.1.0
github.com/wealdtech/go-eth2-types/v2 v2.4.2
github.com/wealdtech/go-eth2-util v1.2.2
github.com/wealdtech/go-eth2-wallet v1.10.2
github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.0.0
github.com/wealdtech/go-eth2-wallet-hd/v2 v2.1.3
github.com/wealdtech/go-eth2-wallet-nd/v2 v2.1.2
github.com/wealdtech/go-eth2-wallet-store-filesystem v1.15.2
github.com/wealdtech/go-eth2-wallet-store-s3 v1.7.2
github.com/wealdtech/go-eth2-wallet-types/v2 v2.2.0
github.com/wealdtech/go-string2eth v1.1.0
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 // indirect
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 // indirect
golang.org/x/text v0.3.2 // indirect
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce // indirect
google.golang.org/grpc v1.27.1
gopkg.in/ini.v1 v1.52.0 // indirect
gopkg.in/yaml.v2 v2.2.8 // indirect
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 // indirect
golang.org/x/text v0.3.3 // indirect
google.golang.org/genproto v0.0.0-20200620020550-bd6e04640131 // indirect
google.golang.org/grpc v1.29.1
gopkg.in/ini.v1 v1.57.0 // indirect
)

400
go.sum
View File

@@ -1,8 +1,19 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/FactomProject/go-bip39 v0.3.5 h1:l9g92TeqCkC5NZhm72igTpf5yaYDp3Sy4CvnPYknp6U=
github.com/FactomProject/go-bip39 v0.3.5/go.mod h1:ygPVOtW424QxnJMze9XYDeh4wT19V3iVDOqVUl/USkE=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OneOfOne/xxhash v1.2.5 h1:zl/OfRA6nftbBK9qTohYBJ5xvw6C/oNKizR7cZGl3cI=
@@ -10,42 +21,55 @@ github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdII
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aws/aws-sdk-go v1.28.0 h1:NkmnHFVEMTRYTleRLm5xUaL1mHKKkYQl4rCd+jzD58c=
github.com/aws/aws-sdk-go v1.28.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.28.13 h1:JyCQQ86yil3hg7MtWdNH8Pbcgx92qlUV2v22Km63Mf4=
github.com/aws/aws-sdk-go v1.28.13/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.29.2 h1:muUfu006FBFvEaDzt4Wq6Ng9E7ufedf8zrB4hmY65QA=
github.com/aws/aws-sdk-go v1.29.2/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aws/aws-sdk-go v1.31.7 h1:TCA+pXKvzDMA3vVqhK21cCy5GarC8pTQb/DrVOWI3iY=
github.com/aws/aws-sdk-go v1.31.7/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
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/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/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgraph-io/ristretto v0.0.1 h1:cJwdnj42uV8Jg4+KLrYovLiCgIfz9wtWm6E6KA+1tLs=
github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE=
github.com/dgraph-io/ristretto v0.0.2 h1:a5WaUrDa0qm0YrAAS1tUykT5El3kt62KNZZeMxQn3po=
github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dlespiau/covertool v0.0.0-20180314162135-b0c4c6d0583a/go.mod h1:/eQMcW3eA1bzKx23ZYI2H3tXPdJB5JWYTHzoUPBvQY4=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/ferranbt/fastssz v0.0.0-20200514094935-99fccaf93472 h1:maoKvILdMk6CSWHanFcUdxXIZGKD9YpWIaVbUQ/4kfg=
github.com/ferranbt/fastssz v0.0.0-20200514094935-99fccaf93472/go.mod h1:LlFXPmgrgVYsuoFDwV8rDJ9tvt1pLQdjKvU1b5IRES0=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
@@ -53,48 +77,102 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/pprof v0.0.0-20190309163659-77426154d546/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0 h1:bM6ZAFZmc/wPFaRDi0d5L7hGEZEx/2u+Tmr2evNHDiI=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.13.0 h1:sBDQoHXrOlfPobnKw69FIKa1wg9qsLLvvQ/Y19WtFgI=
github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c=
github.com/grpc-ecosystem/grpc-gateway v1.14.6 h1:8ERzHx8aj1Sc47mu9n/AksaKCSWrMchFtkdrS4BIj5o=
github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/herumi/bls-eth-go-binary v0.0.0-20200618063237-4d3c66ab099d h1:ZLYUT27mOTH+ogU6Sov6xs1zqYi9cRJ6ksBEELXC8R4=
github.com/herumi/bls-eth-go-binary v0.0.0-20200618063237-4d3c66ab099d/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U=
github.com/herumi/bls-eth-go-binary v0.0.0-20200621110855-298ffb6847bc h1:1ANh6XSZu8Quo4d03TDFhqiFjgUicMdavTtPm6B+RfE=
github.com/herumi/bls-eth-go-binary v0.0.0-20200621110855-298ffb6847bc/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/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -104,36 +182,43 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/minio/highwayhash v1.0.0 h1:iMSDhgUILCr0TNm8LWlSjF8N0ZIj2qbO8WHp6Q/J2BA=
github.com/minio/highwayhash v1.0.0/go.mod h1:xQboMTeM9nY9v/LlAOxFctujiv5+Aq2hR5dxBpaMbdc=
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mmcloughlin/avo v0.0.0-20190318053554-7a0eb66183da/go.mod h1:lf5GMZxA5kz8dnCweJuER5Rmbx6dDu6qvw0fO3uYKK8=
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/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=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4=
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
github.com/phoreproject/bls v0.0.0-20190821133044-da95d4798b09 h1:f0WZnMl5hMHNpfUPR+klp00ZaIL1dLPZigpJUWupreI=
github.com/phoreproject/bls v0.0.0-20190821133044-da95d4798b09/go.mod h1:7pK0Ldy91shCmI47LLTn3i3rfTQcHiJJvPqGqzvN5nE=
github.com/phoreproject/bls v0.0.0-20191113194321-fef763a1a842 h1:SFS72hX189sQC+kZzAcNv7ENTLJHddmokEM39RsLA24=
github.com/phoreproject/bls v0.0.0-20191113194321-fef763a1a842/go.mod h1:xHJKf2TLXUA39Dhv8k5QmQOxLsbrb1KeTS/3ERfLeqc=
github.com/phoreproject/bls v0.0.0-20191211001008-9d5f85bf4a9b h1:tE/F54uL3jp0ZGSKNMPGCTF003pSmtD/sQojpKADAxY=
github.com/phoreproject/bls v0.0.0-20191211001008-9d5f85bf4a9b/go.mod h1:xHJKf2TLXUA39Dhv8k5QmQOxLsbrb1KeTS/3ERfLeqc=
github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw=
github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@@ -144,19 +229,33 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/protolambda/zssz v0.1.3/go.mod h1:a4iwOX5FE7/JkKA+J/PH0Mjo9oXftN6P8NZyL28gpag=
github.com/protolambda/zssz v0.1.4 h1:4jkt8sqwhOVR8B1JebREU/gVX0Ply4GypsV8+RWrDuw=
github.com/protolambda/zssz v0.1.4/go.mod h1:a4iwOX5FE7/JkKA+J/PH0Mjo9oXftN6P8NZyL28gpag=
github.com/prysmaticlabs/ethereumapis v0.0.0-20200213224628-b7452dde4ca3 h1:vhqBgbKg17cwGZbiOjuUqLsvkN11T+eEli5sQBbpgz4=
github.com/prysmaticlabs/ethereumapis v0.0.0-20200213224628-b7452dde4ca3/go.mod h1:5OkRN6UmvgtP+kIewitcEKC7S5KOzLOGtya/Tz+HBns=
github.com/prysmaticlabs/go-bitfield v0.0.0-20191017011753-53b773adde52 h1:kxZ+xSWX0qbxoiDXQBLztKeEmEQg6TgCYWAOa7gSGGU=
github.com/protolambda/zssz v0.1.5 h1:7fjJjissZIIaa2QcvmhS/pZISMX21zVITt49sW1ouek=
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/go-bitfield v0.0.0-20191017011753-53b773adde52/go.mod h1:hCwmef+4qXWjv0jLDbQdWnL0Ol7cS7/lCSS26WR+u6s=
github.com/prysmaticlabs/go-bitfield v0.0.0-20200322041314-62c2aee71669 h1:cX6YRZnZ9sgMqM5U14llxUiXVNJ3u07Res1IIjTOgtI=
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-20200617184054-d57b8c55ea83 h1:HujNkiChDhXhDOlpYtwJu42VFn5Vp1yt+QAEJy03RjU=
github.com/prysmaticlabs/go-bitfield v0.0.0-20200617184054-d57b8c55ea83/go.mod h1:hCwmef+4qXWjv0jLDbQdWnL0Ol7cS7/lCSS26WR+u6s=
github.com/prysmaticlabs/go-bitfield v0.0.0-20200618145306-2ae0807bef65 h1:hJfAWrlxx7SKpn4S/h2JGl2HHwA1a2wSS3HAzzZ0F+U=
github.com/prysmaticlabs/go-bitfield v0.0.0-20200618145306-2ae0807bef65/go.mod h1:hCwmef+4qXWjv0jLDbQdWnL0Ol7cS7/lCSS26WR+u6s=
github.com/prysmaticlabs/go-ssz v0.0.0-20200101200214-e24db4d9e963 h1:Th5ufPIaL5s/7i3gXHTgiTwfsUhWDP/PwFRiI6qV6v0=
github.com/prysmaticlabs/go-ssz v0.0.0-20200101200214-e24db4d9e963/go.mod h1:VecIJZrewdAuhVckySLFt2wAAHRME934bSDurP8ftkc=
github.com/prysmaticlabs/go-ssz v0.0.0-20200612203617-6d5c9aa213ae h1:7qd0Af1ozWKBU3c93YW2RH+/09hJns9+ftqWUZyts9c=
github.com/prysmaticlabs/go-ssz v0.0.0-20200612203617-6d5c9aa213ae/go.mod h1:VecIJZrewdAuhVckySLFt2wAAHRME934bSDurP8ftkc=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 h1:Xuk8ma/ibJ1fOy4Ee11vHhUFHQNpHhrBneOCNHVXS5w=
github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0/go.mod h1:7AwjWCpdPhkSmNAgUv5C7EJ4AbmjEB3r047r3DXWu3Y=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
@@ -168,14 +267,14 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/afero v1.3.0 h1:Ysnmjh1Di8EaWaBv40CYR4IdaIsBc5996Gh1oZzCBKk=
github.com/spf13/afero v1.3.0/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
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=
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
@@ -183,50 +282,71 @@ github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.6.2 h1:7aKfF+e8/k68gda3LOjo5RxiUqddoFxVq4BKBPrxk5E=
github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tyler-smith/go-bip39 v1.0.2 h1:+t3w+KwLXO6154GNJY+qUtIxLTmFjfUmpguQT1OlOT8=
github.com/tyler-smith/go-bip39 v1.0.2/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/wealdtech/go-bytesutil v1.0.0/go.mod h1:jENeMqeTEU8FNZyDFRVc7KqBdRKSnJ9CCh26TcuNb9s=
github.com/wealdtech/eth2-signer-api v1.4.0 h1:y/Bg2SfTT5nGfwyIz7mILWE+BokeDOUE1eZ0jVqPBeA=
github.com/wealdtech/eth2-signer-api v1.4.0/go.mod h1:5wlLQ7NO7nbXo3znJOwIWHN8S4C3xHcZ0uOg9Ue4mvg=
github.com/wealdtech/go-bytesutil v1.0.1 h1:6xzMM+VEHf5WNh1PsIFcRwScgcno+CP8Rw1rGvT6Cew=
github.com/wealdtech/go-bytesutil v1.0.1/go.mod h1:jENeMqeTEU8FNZyDFRVc7KqBdRKSnJ9CCh26TcuNb9s=
github.com/wealdtech/go-bytesutil v1.1.0 h1:6XrN7OIQhhBjQy/PZ1HZ3ySE8v8UDyxzERkOgmsIc1g=
github.com/wealdtech/go-bytesutil v1.1.0/go.mod h1:jENeMqeTEU8FNZyDFRVc7KqBdRKSnJ9CCh26TcuNb9s=
github.com/wealdtech/go-bytesutil v1.1.1 h1:ocEg3Ke2GkZ4vQw5lp46rmO+pfqCCTgq35gqOy8JKVc=
github.com/wealdtech/go-bytesutil v1.1.1/go.mod h1:jENeMqeTEU8FNZyDFRVc7KqBdRKSnJ9CCh26TcuNb9s=
github.com/wealdtech/go-ecodec v1.1.0 h1:yggrTSckcPJRaxxOxQF7FPm21kgE8WA6+f5jdq5Kr8o=
github.com/wealdtech/go-ecodec v1.1.0/go.mod h1:PSdBFEB6cltdT7V4E1jbboufMZTZXcQOKG/2PeEjKK4=
github.com/wealdtech/go-eth2-types v1.0.0 h1:ggrbQ5HeFcxVm20zxVWr8Sc3uCditaetzWB/Ax/4g0w=
github.com/wealdtech/go-eth2-types v1.0.0/go.mod h1:fWUgtKQ7hiNVl6263bGeyjlydYuaxkxcUIPIopgz2CM=
github.com/wealdtech/go-eth2-util v1.1.0 h1:bDgNEtH5LoxI1JaBPUgHoPKmfPwtGuLw487U7AoMO6E=
github.com/wealdtech/go-eth2-util v1.1.0/go.mod h1:mX11133nOroRPHp3qXyAqT5iLmsZXQmpH2DZWWeFWP8=
github.com/wealdtech/go-eth2-wallet v1.9.1 h1:7UB3TaNz14oCb4Stwc+yAVxFzm/SXsz+d+SgsFWi1uU=
github.com/wealdtech/go-eth2-wallet v1.9.1/go.mod h1:VIuvk0JIQCl0JpbThUKy/ZR4ZXrcMEEimECAVs9n5nM=
github.com/wealdtech/go-eth2-types/v2 v2.4.1 h1:8Brj7MvJk3EUkYBQ8A5bWLleIc3/W1Jr1tApzTXtW1A=
github.com/wealdtech/go-eth2-types/v2 v2.4.1/go.mod h1:gqvLvmdw5CaDJUmClvmLRp653Ugf0VlGO9ZoWdi7mLY=
github.com/wealdtech/go-eth2-types/v2 v2.4.2 h1:EkOvP8Ma0Ru7WIh0haoST97rc0PYm2AJpuWG1HzgfCI=
github.com/wealdtech/go-eth2-types/v2 v2.4.2/go.mod h1:hhKa4ZFaNU2fwUjEh8GYr8wKg5D1W4QyxZ3xpsb/2hw=
github.com/wealdtech/go-eth2-util v1.2.1 h1:ReY7XWgTgXf1Fi9MoRLodD1H7rCVMFHMHJKHXoHYNQE=
github.com/wealdtech/go-eth2-util v1.2.1/go.mod h1:PPhVwXc9gv1omyf/Ik4h97jL77HdCEzlEUX4P5wDqQw=
github.com/wealdtech/go-eth2-util v1.2.2 h1:LALunpMSJFvu89RHS1zl6RjZ52805utRvd12RtquB54=
github.com/wealdtech/go-eth2-util v1.2.2/go.mod h1:R3VlTd69B2Jf58s62ChcyXt11ZK1/36CTplTuyR/6dE=
github.com/wealdtech/go-eth2-wallet v1.10.1 h1:Inp0Mk8noUoJIMSBc+W+Si+WhPvs04pcTJrcMuYrNzM=
github.com/wealdtech/go-eth2-wallet v1.10.1/go.mod h1:nB3aarecFrhJLXjKhl+N6jhnbtymb/yAQQ9n1AankR8=
github.com/wealdtech/go-eth2-wallet v1.10.2 h1:oUgi6Ih5fA9thhIipzXMSaLkiwDQXwT8q3bCOLpCr7s=
github.com/wealdtech/go-eth2-wallet v1.10.2/go.mod h1:8H9pgp5K7X1kU1cJMS/B3DrMZF74ZlwBThownrcRYgk=
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-hd v1.10.0 h1:rWpJRWXrnLGLadSOIhCqxAkSIOd93knxxUVxvScV/qw=
github.com/wealdtech/go-eth2-wallet-hd v1.10.0/go.mod h1:LiuHHIG7pU/u598yZBqsjWk0p5LmclxdPQeUSKJJwkg=
github.com/wealdtech/go-eth2-wallet-nd v1.8.0 h1:DD7QRS3mPS6GH0vNFn2DcJ3mHRgc+gtQ0Uq189pWZ00=
github.com/wealdtech/go-eth2-wallet-nd v1.8.0/go.mod h1:o0nR55vuaKZ4d+FuzzDhapu0LBQvk5CUER7ZCg1IYH8=
github.com/wealdtech/go-eth2-wallet-store-filesystem v1.7.0 h1:SJ1yx0K9bk/W7M7lSiOVIm2Trs0FTlQ3wkAfOIl82qk=
github.com/wealdtech/go-eth2-wallet-store-filesystem v1.7.0/go.mod h1:XM1bKpfV6dPdygA1HMO7vx1Vxlir8VMa649mcD2uudg=
github.com/wealdtech/go-eth2-wallet-store-s3 v1.6.0 h1:Xuq7JJ1DE8eGY6n3E/a+lgyySB7mvudAFaNbdEo+Srk=
github.com/wealdtech/go-eth2-wallet-store-s3 v1.6.0/go.mod h1:OjE3G9zCPgJs/W4EVpvIIg+d8X/I8uA1bR9ZVBwf22M=
github.com/wealdtech/go-eth2-wallet-store-scratch v1.3.0 h1:iC0pZQY9zfh1onYHwnkiQa6V998LVK54U5ShUu7QTf8=
github.com/wealdtech/go-eth2-wallet-store-scratch v1.3.0/go.mod h1:NaV/et5zTaqHhTpdpSveUYE2czCFAD8f07eABh/0pso=
github.com/wealdtech/go-eth2-wallet-types v1.8.0 h1:6K/u5CTcUavgzoABeeuBAZ0jp1qEOdK/9U8gaMJUlVE=
github.com/wealdtech/go-eth2-wallet-types v1.8.0/go.mod h1:5A83MUBhmLgxpg9X5eqvnDIOYfn329caf5DOcv7pcY0=
github.com/wealdtech/go-eth2-wallet-types v1.10.0 h1:iQx3MxMQQwoEfyPDHy5vtKYVtrUjY4nzEODbUPcz2bg=
github.com/wealdtech/go-eth2-wallet-types v1.10.0/go.mod h1:vwK05jlJM/ibZs/QCqJ4dOhsjjpNcIP4X7Vx6WYXLGI=
github.com/wealdtech/go-eth2-wallet-hd/v2 v2.1.1 h1:L4L445Uvd1Uw+Af4I9Bu29e8OCJOhYgN9lKmzjRhNwQ=
github.com/wealdtech/go-eth2-wallet-hd/v2 v2.1.1/go.mod h1:1IC4wOz0SIySMld5tRi4Vb0cpe7tOwqP0hS2LsrGJ34=
github.com/wealdtech/go-eth2-wallet-hd/v2 v2.1.2 h1:Rrpit3nuxKOUKE7poDcs2x9MZOaodd+mact+KL+/S98=
github.com/wealdtech/go-eth2-wallet-hd/v2 v2.1.2/go.mod h1:STigKib4ZSefVvJjx88V2QpUGaoyUE1TiupcpsHpvKE=
github.com/wealdtech/go-eth2-wallet-hd/v2 v2.1.3 h1:JJwCr0NfMnS0xI7gSE+GqENXBxNIfySoC3NtxK8dG1o=
github.com/wealdtech/go-eth2-wallet-hd/v2 v2.1.3/go.mod h1:STigKib4ZSefVvJjx88V2QpUGaoyUE1TiupcpsHpvKE=
github.com/wealdtech/go-eth2-wallet-nd/v2 v2.1.1 h1:ziEpwVutiAdP0+LsVsC+nlfhgc4kwQAaUNpPz/rJ+CM=
github.com/wealdtech/go-eth2-wallet-nd/v2 v2.1.1/go.mod h1:IEWuITdIQTImjHju0crCJkvGus05n1HmoMZmO+EDil8=
github.com/wealdtech/go-eth2-wallet-nd/v2 v2.1.2 h1:j3cKJ02Js83ygvZOmbb+GA2iSCAQDYhY5nNdm7anzjQ=
github.com/wealdtech/go-eth2-wallet-nd/v2 v2.1.2/go.mod h1:IssxoHII0ewO1VysMfCmdJP1D00tRhRhXIhhaEXIOVE=
github.com/wealdtech/go-eth2-wallet-store-filesystem v1.15.1 h1:HlJZp6xyJ14nd+IFPV8TvtJyumKe0RbHIG+PKEeqxTk=
github.com/wealdtech/go-eth2-wallet-store-filesystem v1.15.1/go.mod h1:C4m7Xv0gCgb7XUwCxxJp+45YX8Qfn1vrE0UOSHD/QfE=
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-s3 v1.7.1 h1:EFX2SfWQ9zi7lJ/Jy1WsVBHQuTdGp8Z/Q+uuxNFpBQA=
github.com/wealdtech/go-eth2-wallet-store-s3 v1.7.1/go.mod h1:LDmyvkgcaY4rGnGBQwf1uc02m0dMSebm9nRHx7feVHU=
github.com/wealdtech/go-eth2-wallet-store-s3 v1.7.2 h1:a7GWfFd139CODvvkuTbRIuRwAAjb55sFDGRh177KXGk=
github.com/wealdtech/go-eth2-wallet-store-s3 v1.7.2/go.mod h1:VWvXScZKUWHbhQpadLX8Yj+mc8U/i4zGthQJee+o3xg=
github.com/wealdtech/go-eth2-wallet-store-scratch v1.4.1 h1:uw2Kqqfcr7NMBHM0x/JpKkr1XpKr+zeASOXhOV6xobk=
github.com/wealdtech/go-eth2-wallet-store-scratch v1.4.1/go.mod h1:psrxFO0Df0R+2iGD8Wdvb5AAYBY7zx+4/qtULaJ57IY=
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-types/v2 v2.1.1 h1:RGvhM7C85ptEdVsCDQNt+fJrF0SrMnUb0gkgtU75ifI=
github.com/wealdtech/go-eth2-wallet-types/v2 v2.1.1/go.mod h1:608wNFKrwPzwjWeQVRE/rHnHkrRmmTCUhFg2/Bgkbvs=
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-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=
@@ -234,100 +354,187 @@ github.com/wealdtech/go-string2eth v1.1.0/go.mod h1:RUzsLjJtbZaJ/3UKn9kY19a/vCCU
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8=
golang.org/x/arch v0.0.0-20190312162104-788fe5ffcd8c/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 h1:0hQKqeLdqlt5iIwVOBErRisrHJAN57yOiPRQItI20fU=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191105034135-c7e5f84aec59 h1:PyXRxSVbvzDGuqYXjHndV7xDzJ7w2K8KD9Ef8GB7KOE=
golang.org/x/crypto v0.0.0-20191105034135-c7e5f84aec59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c h1:/nJuwDLoL/zrqY6gf57vxC+Pi+pZ8bfhpPkicO5H7W4=
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200208060501-ecb85df21340 h1:KOcEaR10tFr7gdJV2GCKw8Os5yED1u1aOqHjOAb6d2Y=
golang.org/x/crypto v0.0.0-20200208060501-ecb85df21340/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 h1:Sy5bstxEqwwbYs6n0/pBuxKENqOeZUgD45Gp3Q3pqLg=
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190326090315-15845e8f865b/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200528225125-3c3fba18258b h1:IYiJPiJfzktmDAO1HQiwjMjwjlYKHAL7KzeD544RJPs=
golang.org/x/net v0.0.0-20200528225125-3c3fba18258b/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200620081246-981b61492c35 h1:wb/9mP8eUAmHfkM8RmpeLq6nUA7c2i5+bQOtcDftjaE=
golang.org/x/sys v0.0.0-20200620081246-981b61492c35/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 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190106171756-3ef68632349c/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190325223049-1d95b17f1b04/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce h1:1mbrb1tUU+Zmt5C94IGKADBTJZjZXAd+BubWi7r9EiI=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200528191852-705c0b31589b h1:nl5tymnV+50ACFZUDAP+xFCe3Zh3SWdMDx+ernZSKNA=
google.golang.org/genproto v0.0.0-20200528191852-705c0b31589b/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200620020550-bd6e04640131 h1:IXNofpkLhv80L3TJQvj2YQLnMHZgAktycswvtXwQiRk=
google.golang.org/genproto v0.0.0-20200620020550-bd6e04640131/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
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 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0 h1:cJv5/xdbk1NnMPR1VP9+HU6gupuG9MLBoH1r6RHZ2MY=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.52.0 h1:j+Lt/M1oPPejkniCg1TkWE2J3Eh1oZTsHSXzMTzUXn4=
gopkg.in/ini.v1 v1.52.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww=
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -335,8 +542,13 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=

View File

@@ -18,23 +18,26 @@ import (
"strconv"
"strings"
"github.com/gogo/protobuf/types"
"github.com/pkg/errors"
"github.com/spf13/viper"
"google.golang.org/grpc"
"github.com/golang/protobuf/ptypes/empty"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
wtypes "github.com/wealdtech/go-eth2-wallet-types"
wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
)
// FetchChainConfig fetches the chain configuration from the beacon node.
// It tweaks the output to make it easier to work with by setting appropriate
// types.
func FetchChainConfig(conn *grpc.ClientConn) (map[string]interface{}, error) {
if conn == nil {
return nil, errors.New("no connection to beacon node")
}
beaconClient := ethpb.NewBeaconChainClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
config, err := beaconClient.GetBeaconConfig(ctx, &empty.Empty{})
config, err := beaconClient.GetBeaconConfig(ctx, &types.Empty{})
if err != nil {
return nil, err
}
@@ -72,8 +75,60 @@ func FetchChainConfig(conn *grpc.ClientConn) (map[string]interface{}, error) {
return results, nil
}
func FetchLatestFilledSlot(conn *grpc.ClientConn) (uint64, error) {
if conn == nil {
return 0, errors.New("no connection to beacon node")
}
beaconClient := ethpb.NewBeaconChainClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
chainHead, err := beaconClient.GetChainHead(ctx, &types.Empty{})
if err != nil {
return 0, errors.Wrap(err, "failed to obtain latest")
}
return chainHead.HeadSlot, nil
}
// FetchValidatorCommittees fetches the validator committees for a given epoch.
func FetchValidatorCommittees(conn *grpc.ClientConn, epoch uint64) (map[uint64][][]uint64, error) {
if conn == nil {
return nil, errors.New("no connection to beacon node")
}
beaconClient := ethpb.NewBeaconChainClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
req := &ethpb.ListCommitteesRequest{
QueryFilter: &ethpb.ListCommitteesRequest_Epoch{
Epoch: epoch,
},
}
resp, err := beaconClient.ListBeaconCommittees(ctx, req)
if err != nil {
return nil, errors.Wrap(err, "failed to obtain committees")
}
res := make(map[uint64][][]uint64)
for slot, committees := range resp.Committees {
res[slot] = make([][]uint64, len(resp.Committees))
for i, committee := range committees.Committees {
res[slot][uint64(i)] = make([]uint64, len(committee.ValidatorIndices))
indices := make([]uint64, len(committee.ValidatorIndices))
copy(indices, committee.ValidatorIndices)
res[slot][uint64(i)] = indices
}
}
return res, nil
}
// FetchValidator fetches the validator definition from the beacon node.
func FetchValidator(conn *grpc.ClientConn, account wtypes.Account) (*ethpb.Validator, error) {
if conn == nil {
return nil, errors.New("no connection to beacon node")
}
beaconClient := ethpb.NewBeaconChainClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
@@ -88,6 +143,9 @@ func FetchValidator(conn *grpc.ClientConn, account wtypes.Account) (*ethpb.Valid
// FetchValidatorByIndex fetches the validator definition from the beacon node.
func FetchValidatorByIndex(conn *grpc.ClientConn, index uint64) (*ethpb.Validator, error) {
if conn == nil {
return nil, errors.New("no connection to beacon node")
}
beaconClient := ethpb.NewBeaconChainClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
@@ -100,8 +158,59 @@ func FetchValidatorByIndex(conn *grpc.ClientConn, index uint64) (*ethpb.Validato
return beaconClient.GetValidator(ctx, req)
}
// FetchValidatorBalance fetches the validator balance from the beacon node.
func FetchValidatorBalance(conn *grpc.ClientConn, account wtypes.Account) (uint64, error) {
if conn == nil {
return 0, errors.New("no connection to beacon node")
}
beaconClient := ethpb.NewBeaconChainClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
res, err := beaconClient.ListValidatorBalances(ctx, &ethpb.ListValidatorBalancesRequest{
PublicKeys: [][]byte{account.PublicKey().Marshal()},
})
if err != nil {
return 0, err
}
if len(res.Balances) == 0 {
return 0, errors.New("unknown validator")
}
return res.Balances[0].Balance, nil
}
// FetchValidatorPerformance fetches the validator performance from the beacon node.
func FetchValidatorPerformance(conn *grpc.ClientConn, account wtypes.Account) (bool, bool, bool, uint64, int64, error) {
if conn == nil {
return false, false, false, 0, 0, errors.New("no connection to beacon node")
}
beaconClient := ethpb.NewBeaconChainClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
req := &ethpb.ValidatorPerformanceRequest{
PublicKeys: [][]byte{account.PublicKey().Marshal()},
}
res, err := beaconClient.GetValidatorPerformance(ctx, req)
if err != nil {
return false, false, false, 0, 0, err
}
if len(res.InclusionDistances) == 0 {
return false, false, false, 0, 0, errors.New("unknown validator")
}
return res.CorrectlyVotedHead[0],
res.CorrectlyVotedSource[0],
res.CorrectlyVotedTarget[0],
res.InclusionDistances[0],
int64(res.BalancesAfterEpochTransition[0]) - int64(res.BalancesBeforeEpochTransition[0]),
err
}
// FetchValidatorInfo fetches current validator info from the beacon node.
func FetchValidatorInfo(conn *grpc.ClientConn, account wtypes.Account) (*ethpb.ValidatorInfo, error) {
if conn == nil {
return nil, errors.New("no connection to beacon node")
}
beaconClient := ethpb.NewBeaconChainClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
@@ -124,15 +233,21 @@ func FetchValidatorInfo(conn *grpc.ClientConn, account wtypes.Account) (*ethpb.V
// FetchChainInfo fetches current chain info from the beacon node.
func FetchChainInfo(conn *grpc.ClientConn) (*ethpb.ChainHead, error) {
if conn == nil {
return nil, errors.New("no connection to beacon node")
}
beaconClient := ethpb.NewBeaconChainClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
return beaconClient.GetChainHead(ctx, &empty.Empty{})
return beaconClient.GetChainHead(ctx, &types.Empty{})
}
// FetchBlock fetches a block at a given slot from the beacon node.
func FetchBlock(conn *grpc.ClientConn, slot uint64) (*ethpb.SignedBeaconBlock, error) {
if conn == nil {
return nil, errors.New("no connection to beacon node")
}
beaconClient := ethpb.NewBeaconChainClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
@@ -149,3 +264,17 @@ func FetchBlock(conn *grpc.ClientConn, slot uint64) (*ethpb.SignedBeaconBlock, e
}
return resp.BlockContainers[0].Block, nil
}
func StreamBlocks(conn *grpc.ClientConn) (ethpb.BeaconChain_StreamBlocksClient, error) {
if conn == nil {
return nil, errors.New("no connection to beacon node")
}
beaconClient := ethpb.NewBeaconChainClient(conn)
stream, err := beaconClient.StreamBlocks(context.Background(), &types.Empty{})
if err != nil {
return nil, err
}
return stream, nil
}

View File

@@ -16,15 +16,19 @@ package grpc
import (
"context"
"github.com/pkg/errors"
"github.com/spf13/viper"
"google.golang.org/grpc"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
wtypes "github.com/wealdtech/go-eth2-wallet-types"
wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
)
// FetchValidatorIndex fetches the index of a validator.
func FetchValidatorIndex(conn *grpc.ClientConn, account wtypes.Account) (uint64, error) {
if conn == nil {
return 0, errors.New("no connection to beacon node")
}
validatorClient := ethpb.NewBeaconNodeValidatorClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
@@ -43,6 +47,9 @@ func FetchValidatorIndex(conn *grpc.ClientConn, account wtypes.Account) (uint64,
// FetchValidatorState fetches the state of a validator.
func FetchValidatorState(conn *grpc.ClientConn, account wtypes.Account) (ethpb.ValidatorStatus, error) {
if conn == nil {
return ethpb.ValidatorStatus_UNKNOWN_STATUS, errors.New("no connection to beacon node")
}
validatorClient := ethpb.NewBeaconNodeValidatorClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
@@ -53,7 +60,7 @@ func FetchValidatorState(conn *grpc.ClientConn, account wtypes.Account) (ethpb.V
}
resp, err := validatorClient.ValidatorStatus(ctx, req)
if err != nil {
return 0, err
return ethpb.ValidatorStatus_UNKNOWN_STATUS, err
}
return resp.Status, nil

View File

@@ -17,31 +17,53 @@ import (
"context"
"time"
"github.com/gogo/protobuf/types"
"github.com/pkg/errors"
"github.com/spf13/viper"
"google.golang.org/grpc"
"github.com/golang/protobuf/ptypes/empty"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
)
// FetchGenesis fetches the genesis time.
func FetchGenesis(conn *grpc.ClientConn) (time.Time, error) {
// FetchGenesisTime fetches the genesis time.
func FetchGenesisTime(conn *grpc.ClientConn) (time.Time, error) {
if conn == nil {
return time.Now(), 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, &empty.Empty{})
res, err := client.GetGenesis(ctx, &types.Empty{})
if err != nil {
return time.Now(), err
}
return time.Unix(res.GetGenesisTime().Seconds, 0), nil
}
// FetchVersion fetches the version and metadata from the server.
func FetchVersion(conn *grpc.ClientConn) (string, string, error) {
// FetchGenesisValidatorsRoot fetches the genesis validators root.
func FetchGenesisValidatorsRoot(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()
version, err := client.GetVersion(ctx, &empty.Empty{})
res, err := client.GetGenesis(ctx, &types.Empty{})
if err != nil {
return nil, err
}
return res.GetGenesisValidatorsRoot(), nil
}
// FetchVersion fetches the version and metadata from the server.
func FetchVersion(conn *grpc.ClientConn) (string, string, error) {
if conn == nil {
return "", "", errors.New("no connection to beacon node")
}
client := ethpb.NewNodeClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
version, err := client.GetVersion(ctx, &types.Empty{})
if err != nil {
return "", "", err
}
@@ -50,10 +72,13 @@ func FetchVersion(conn *grpc.ClientConn) (string, string, error) {
// FetchSyncing returns true if the node is syncing, otherwise false.
func FetchSyncing(conn *grpc.ClientConn) (bool, error) {
if conn == nil {
return false, errors.New("no connection to beacon node")
}
client := ethpb.NewNodeClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
syncStatus, err := client.GetSyncStatus(ctx, &empty.Empty{})
syncStatus, err := client.GetSyncStatus(ctx, &types.Empty{})
if err != nil {
return false, err
}

View File

@@ -17,22 +17,43 @@ import (
"errors"
"github.com/google/uuid"
types "github.com/wealdtech/go-eth2-types"
types "github.com/wealdtech/go-eth2-types/v2"
)
// ScratchAccount is an account that exists temporarily.
type ScratchAccount struct {
id uuid.UUID
pubKey types.PublicKey
id uuid.UUID
privKey types.PrivateKey
pubKey types.PublicKey
unlocked bool
}
// NewScratchAccount creates a new local account.
func NewScratchAccount(pubKey []byte) (*ScratchAccount, error) {
func NewScratchAccount(privKey []byte, pubKey []byte) (*ScratchAccount, error) {
if len(privKey) > 0 {
return newScratchAccountFromPrivKey(privKey)
} else {
return newScratchAccountFromPubKey(pubKey)
}
}
func newScratchAccountFromPrivKey(privKey []byte) (*ScratchAccount, error) {
key, err := types.BLSPrivateKeyFromBytes(privKey)
if err != nil {
return nil, err
}
return &ScratchAccount{
id: uuid.New(),
privKey: key,
pubKey: key.PublicKey(),
}, nil
}
func newScratchAccountFromPubKey(pubKey []byte) (*ScratchAccount, error) {
key, err := types.BLSPublicKeyFromBytes(pubKey)
if err != nil {
return nil, err
}
return &ScratchAccount{
id: uuid.New(),
pubKey: key,
@@ -56,16 +77,24 @@ func (a *ScratchAccount) Path() string {
}
func (a *ScratchAccount) Lock() {
a.unlocked = false
}
func (a *ScratchAccount) Unlock([]byte) error {
a.unlocked = true
return nil
}
func (a *ScratchAccount) IsUnlocked() bool {
return false
return a.unlocked
}
func (a *ScratchAccount) Sign(data []byte, domain uint64) (types.Signature, error) {
return nil, errors.New("Not implemented")
func (a *ScratchAccount) Sign(data []byte) (types.Signature, error) {
if !a.IsUnlocked() {
return nil, errors.New("locked")
}
if a.privKey == nil {
return nil, errors.New("no private key")
}
return a.privKey.Sign(data), nil
}