mirror of
https://github.com/wealdtech/ethdo.git
synced 2026-01-10 14:37:57 -05:00
Compare commits
76 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2bb5e0d51 | ||
|
|
f5fc8b363d | ||
|
|
a31509f7d6 | ||
|
|
4b451ec2fa | ||
|
|
a8fee14b89 | ||
|
|
9cfb1b5637 | ||
|
|
df1724f763 | ||
|
|
f1a586ca56 | ||
|
|
b9cb926662 | ||
|
|
220cc21356 | ||
|
|
5bc7c1a26c | ||
|
|
4c3237cd0d | ||
|
|
7215e04a69 | ||
|
|
71e9c0471b | ||
|
|
1a6f402fb8 | ||
|
|
b9297c2506 | ||
|
|
1032789706 | ||
|
|
51e289b72b | ||
|
|
a75a350cef | ||
|
|
aa34e61a80 | ||
|
|
3f33f04be2 | ||
|
|
5733b5b638 | ||
|
|
e1ce81c81d | ||
|
|
bd67ba0307 | ||
|
|
12bb5a7ab8 | ||
|
|
d57dbbf104 | ||
|
|
6482b4add6 | ||
|
|
b672e83470 | ||
|
|
710e891844 | ||
|
|
9e3bf521a0 | ||
|
|
3ca899b832 | ||
|
|
d221c0544c | ||
|
|
eed07a39a3 | ||
|
|
5a5edacd11 | ||
|
|
c7f4cb0ca5 | ||
|
|
fbc2171053 | ||
|
|
57946f1552 | ||
|
|
b487bb042c | ||
|
|
0c8029c950 | ||
|
|
b3bbeea3fb | ||
|
|
09105549b1 | ||
|
|
364c764de2 | ||
|
|
3f1fe30959 | ||
|
|
aae360ccb8 | ||
|
|
32a3e4649d | ||
|
|
dd3bd121fe | ||
|
|
edf84f47ba | ||
|
|
320c5ee9ab | ||
|
|
36a1674549 | ||
|
|
693b2a6961 | ||
|
|
e5481f9074 | ||
|
|
c89712699c | ||
|
|
7130855b73 | ||
|
|
d505966a42 | ||
|
|
732c07238a | ||
|
|
b843d0077e | ||
|
|
f11cf0cbf5 | ||
|
|
ea43e71e60 | ||
|
|
71b01ee2aa | ||
|
|
de349f5691 | ||
|
|
b1d2e94854 | ||
|
|
2293079861 | ||
|
|
69867ba21c | ||
|
|
f72af8247a | ||
|
|
9ca4406a80 | ||
|
|
1ad82adf80 | ||
|
|
11eb440df2 | ||
|
|
e2192f6992 | ||
|
|
864aa24484 | ||
|
|
9f8d7c3f7b | ||
|
|
4f643ad952 | ||
|
|
6551db8f9a | ||
|
|
d0f944eacc | ||
|
|
be3fd8edd2 | ||
|
|
afcbd177a3 | ||
|
|
a38ff57508 |
24
.dockerignore
Normal file
24
.dockerignore
Normal 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
19
Dockerfile
Normal 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
274
README.md
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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
60
cmd/accountlock.go
Normal 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
61
cmd/accountunlock.go
Normal 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)
|
||||
}
|
||||
323
cmd/blockinfo.go
323
cmd/blockinfo.go
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
package cmd
|
||||
|
||||
const (
|
||||
_exit_success = 0
|
||||
_exit_failure = 1
|
||||
_exitSuccess = 0
|
||||
_exitFailure = 1
|
||||
)
|
||||
|
||||
32
cmd/deposit.go
Normal file
32
cmd/deposit.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright © 2019 Weald Technology Trading
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// 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
235
cmd/depositverify.go
Normal 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")
|
||||
}
|
||||
@@ -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
39
cmd/passphrases.go
Normal 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]
|
||||
}
|
||||
258
cmd/root.go
258
cmd/root.go
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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)")
|
||||
}
|
||||
|
||||
@@ -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 := ðpb.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 := ðpb.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 := ðpb.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 := ðpb.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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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
55
cmd/walletdelete.go
Normal 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)
|
||||
}
|
||||
@@ -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)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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
67
docs/howto.md
Normal 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
244
docs/prysm.md
Normal 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
33
docs/troubleshooting.md
Normal 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
423
docs/usage.md
Normal 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
57
go.mod
@@ -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
400
go.sum
@@ -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=
|
||||
|
||||
@@ -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 := ðpb.ListCommitteesRequest{
|
||||
QueryFilter: ðpb.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, ðpb.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 := ðpb.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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
41
grpc/node.go
41
grpc/node.go
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user