mirror of
https://github.com/wealdtech/ethdo.git
synced 2026-01-10 14:37:57 -05:00
Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
7d4ead9640 | ||
|
|
945f2ca47c | ||
|
|
b61eaefb0d | ||
|
|
6786742004 | ||
|
|
6cb8886672 | ||
|
|
ab5c6317ca | ||
|
|
5896579496 | ||
|
|
8df1b74a79 | ||
|
|
47cdd8402a | ||
|
|
432967c14c | ||
|
|
36033d66d2 | ||
|
|
0b354c3ec5 | ||
|
|
b3e6178149 | ||
|
|
12bd0e303f | ||
|
|
502d9a9bd0 | ||
|
|
6f863bce9a | ||
|
|
d1d54989c2 | ||
|
|
eb813475c0 | ||
|
|
93e828f256 |
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"]
|
||||
225
README.md
225
README.md
@@ -10,6 +10,7 @@ A command-line tool for managing common tasks in Ethereum 2.
|
||||
## 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
|
||||
go get github.com/wealdtech/ethdo
|
||||
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,174 +118,13 @@ 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
|
||||
# Commands
|
||||
|
||||
#### `accounts`
|
||||
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).
|
||||
|
||||
`ethdo wallet accouts` lists the accounts within a wallet.
|
||||
# HOWTO
|
||||
|
||||
```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 (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
|
||||
|
||||
```sh
|
||||
$ ethdo wallet create --wallet="Personal wallet" --type="hd" --walletpassphrase="my wallet secret"
|
||||
```
|
||||
|
||||
#### `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
|
||||
```
|
||||
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
|
||||
|
||||
@@ -251,5 +136,5 @@ Contributions welcome. Please check out [the issues](https://github.com/wealdtec
|
||||
|
||||
## License
|
||||
|
||||
[Apache-2.0](LICENSE) © 2019 Weald Technology Trading Ltd
|
||||
[Apache-2.0](LICENSE) © 2019, 2020 Weald Technology Trading Ltd
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ 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")
|
||||
|
||||
@@ -51,7 +52,7 @@ In quiet mode this will return 0 if the account is created successfully, otherwi
|
||||
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,7 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/wealdtech/go-bytesutil"
|
||||
types "github.com/wealdtech/go-eth2-wallet-types"
|
||||
types "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
var accountImportKey string
|
||||
@@ -33,6 +33,7 @@ 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")
|
||||
assert(accountImportKey != "", "--key is required")
|
||||
@@ -59,7 +60,7 @@ In quiet mode this will return 0 if the account is imported successfully, otherw
|
||||
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,12 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
pb "github.com/wealdtech/eth2-signer-api/pb/v1"
|
||||
)
|
||||
|
||||
var accountInfoCmd = &cobra.Command{
|
||||
@@ -31,13 +33,28 @@ 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")
|
||||
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: %#048x\n", resp.Accounts[0].PublicKey)
|
||||
|
||||
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)
|
||||
} 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: %#048x", account.PublicKey().Marshal()))
|
||||
outputIf(verbose && account.Path() != "", fmt.Sprintf("Path: %s", account.Path()))
|
||||
}
|
||||
|
||||
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,7 +31,9 @@ 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")
|
||||
|
||||
@@ -47,7 +49,7 @@ In quiet mode this will return 0 if the key can be obtained, otherwise 1.`,
|
||||
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)
|
||||
unlockReq := &pb.UnlockAccountRequest{
|
||||
Account: rootAccount,
|
||||
Passphrase: []byte(rootAccountPassphrase),
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||
defer cancel()
|
||||
resp, err := client.Unlock(ctx, unlockReq)
|
||||
errCheck(err, "Failed in attempt to unlock account")
|
||||
switch resp.State {
|
||||
case pb.ResponseState_DENIED:
|
||||
die("Unlock request denied")
|
||||
case pb.ResponseState_FAILED:
|
||||
die("Unlock request failed")
|
||||
case pb.ResponseState_SUCCEEDED:
|
||||
outputIf(!quiet, "Unlock request succeeded")
|
||||
os.Exit(_exitSuccess)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
accountCmd.AddCommand(accountUnlockCmd)
|
||||
accountFlags(accountUnlockCmd)
|
||||
}
|
||||
32
cmd/block.go
Normal file
32
cmd/block.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright © 2020 Weald Technology Trading
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// blockCmd represents the block command
|
||||
var blockCmd = &cobra.Command{
|
||||
Use: "block",
|
||||
Short: "Obtain information about an Ethereum 2 block",
|
||||
Long: "Obtain information about an Ethereum 2 block",
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(blockCmd)
|
||||
}
|
||||
|
||||
func blockFlags(cmd *cobra.Command) {
|
||||
}
|
||||
224
cmd/blockinfo.go
Normal file
224
cmd/blockinfo.go
Normal file
@@ -0,0 +1,224 @@
|
||||
// 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 (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"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 blockInfoCmd = &cobra.Command{
|
||||
Use: "info",
|
||||
Short: "Obtain information about a block",
|
||||
Long: `Obtain information about a block. For example:
|
||||
|
||||
ethdo block info --slot=12345
|
||||
|
||||
In quiet mode this will return 0 if the block information is present and not skipped, otherwise 1.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := connect()
|
||||
errCheck(err, "Failed to obtain connection to Ethereum 2 beacon chain block")
|
||||
|
||||
assert(blockInfoSlot != 0, "--slot is required")
|
||||
|
||||
var slot uint64
|
||||
if blockInfoSlot < 0 {
|
||||
// TODO 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(_exitFailure)
|
||||
}
|
||||
block := signedBlock.Block
|
||||
body := block.Body
|
||||
|
||||
// General info.
|
||||
bodyRoot, err := ssz.HashTreeRoot(block)
|
||||
errCheck(err, "Failed to calculate block body root")
|
||||
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
|
||||
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)
|
||||
|
||||
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\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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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\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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
os.Exit(_exitSuccess)
|
||||
},
|
||||
}
|
||||
|
||||
// intersection returns a list of items common between the two sets.
|
||||
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)
|
||||
|
||||
set1Pos := 0
|
||||
set2Pos := 0
|
||||
for set1Pos < len(set1) && set2Pos < len(set2) {
|
||||
switch {
|
||||
case set1[set1Pos] < set2[set2Pos]:
|
||||
set1Pos++
|
||||
case set2[set2Pos] < set1[set1Pos]:
|
||||
set2Pos++
|
||||
default:
|
||||
res = append(res, set1[set1Pos])
|
||||
set1Pos++
|
||||
set2Pos++
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
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 strings.TrimSpace(res)
|
||||
}
|
||||
|
||||
func init() {
|
||||
blockCmd.AddCommand(blockInfoCmd)
|
||||
blockFlags(blockInfoCmd)
|
||||
blockInfoCmd.Flags().Int64Var(&blockInfoSlot, "slot", -1, "the default slot")
|
||||
}
|
||||
32
cmd/chain.go
Normal file
32
cmd/chain.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright © 2020 Weald Technology Trading
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// chainCmd represents the chain command
|
||||
var chainCmd = &cobra.Command{
|
||||
Use: "chain",
|
||||
Short: "Obtain information about an Ethereum 2 chain",
|
||||
Long: "Obtain information about an Ethereum 2 chain",
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(chainCmd)
|
||||
}
|
||||
|
||||
func chainFlags(cmd *cobra.Command) {
|
||||
}
|
||||
66
cmd/chaininfo.go
Normal file
66
cmd/chaininfo.go
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright © 2020 Weald Technology Trading
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/wealdtech/ethdo/grpc"
|
||||
)
|
||||
|
||||
var chainInfoCmd = &cobra.Command{
|
||||
Use: "info",
|
||||
Short: "Obtain information about a chain",
|
||||
Long: `Obtain information about a chain. For example:
|
||||
|
||||
ethdo chain info
|
||||
|
||||
In quiet mode this will return 0 if the chain information can be obtained, otherwise 1.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := connect()
|
||||
errCheck(err, "Failed to obtain connection to Ethereum 2 beacon chain node")
|
||||
config, err := grpc.FetchChainConfig(eth2GRPCConn)
|
||||
errCheck(err, "Failed to obtain beacon chain configuration")
|
||||
|
||||
genesisTime, err := grpc.FetchGenesisTime(eth2GRPCConn)
|
||||
errCheck(err, "Failed to obtain genesis time")
|
||||
|
||||
if quiet {
|
||||
os.Exit(_exitSuccess)
|
||||
}
|
||||
|
||||
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(_exitSuccess)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
chainCmd.AddCommand(chainInfoCmd)
|
||||
chainFlags(chainInfoCmd)
|
||||
}
|
||||
|
||||
func timestampToSlot(genesis int64, timestamp int64, secondsPerSlot uint64) uint64 {
|
||||
if timestamp < genesis {
|
||||
return 0
|
||||
}
|
||||
return uint64(timestamp-genesis) / secondsPerSlot
|
||||
}
|
||||
99
cmd/chainstatus.go
Normal file
99
cmd/chainstatus.go
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright © 2020 Weald Technology Trading
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/wealdtech/ethdo/grpc"
|
||||
)
|
||||
|
||||
var chainStatusSlot bool
|
||||
|
||||
var chainStatusCmd = &cobra.Command{
|
||||
Use: "status",
|
||||
Short: "Obtain status about a chain",
|
||||
Long: `Obtain status about a chain. For example:
|
||||
|
||||
ethdo chain status
|
||||
|
||||
In quiet mode this will return 0 if the chain status can be obtained, otherwise 1.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := connect()
|
||||
errCheck(err, "Failed to obtain connection to Ethereum 2 beacon chain node")
|
||||
config, err := grpc.FetchChainConfig(eth2GRPCConn)
|
||||
errCheck(err, "Failed to obtain beacon chain configuration")
|
||||
|
||||
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(_exitSuccess)
|
||||
}
|
||||
|
||||
slot := timestampToSlot(genesisTime.Unix(), time.Now().Unix(), config["SecondsPerSlot"].(uint64))
|
||||
if chainStatusSlot {
|
||||
fmt.Printf("Current slot: %d\n", slot)
|
||||
fmt.Printf("Justified slot: %d\n", info.GetJustifiedSlot())
|
||||
if verbose {
|
||||
distance := slot - info.GetJustifiedSlot()
|
||||
fmt.Printf("Justified slot distance: %d\n", distance)
|
||||
}
|
||||
fmt.Printf("Finalized slot: %d\n", info.GetFinalizedSlot())
|
||||
if verbose {
|
||||
distance := slot - info.GetFinalizedSlot()
|
||||
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)
|
||||
}
|
||||
} else {
|
||||
slotsPerEpoch := config["SlotsPerEpoch"].(uint64)
|
||||
epoch := slot / slotsPerEpoch
|
||||
fmt.Printf("Current epoch: %d\n", epoch)
|
||||
fmt.Printf("Justified epoch: %d\n", info.GetJustifiedSlot()/slotsPerEpoch)
|
||||
if verbose {
|
||||
distance := (slot - info.GetJustifiedSlot()) / slotsPerEpoch
|
||||
fmt.Printf("Justified epoch distance %d\n", distance)
|
||||
}
|
||||
fmt.Printf("Finalized epoch: %d\n", info.GetFinalizedSlot()/slotsPerEpoch)
|
||||
if verbose {
|
||||
distance := (slot - info.GetFinalizedSlot()) / slotsPerEpoch
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
os.Exit(_exitSuccess)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
chainCmd.AddCommand(chainStatusCmd)
|
||||
chainFlags(chainStatusCmd)
|
||||
chainStatusCmd.Flags().BoolVar(&chainStatusSlot, "slot", false, "Print slot-based values")
|
||||
|
||||
}
|
||||
@@ -14,6 +14,6 @@
|
||||
package cmd
|
||||
|
||||
const (
|
||||
_exit_success = 0
|
||||
_exit_failure = 1
|
||||
_exitSuccess = 0
|
||||
_exitFailure = 1
|
||||
)
|
||||
|
||||
32
cmd/node.go
Normal file
32
cmd/node.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright © 2020 Weald Technology Trading
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// nodeCmd represents the node command
|
||||
var nodeCmd = &cobra.Command{
|
||||
Use: "node",
|
||||
Short: "Obtain information about an Ethereum 2 node",
|
||||
Long: "Obtain information about an Ethereum 2 node",
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(nodeCmd)
|
||||
}
|
||||
|
||||
func nodeFlags(cmd *cobra.Command) {
|
||||
}
|
||||
70
cmd/nodeinfo.go
Normal file
70
cmd/nodeinfo.go
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright © 2020 Weald Technology Trading
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/wealdtech/ethdo/grpc"
|
||||
)
|
||||
|
||||
var nodeInfoCmd = &cobra.Command{
|
||||
Use: "info",
|
||||
Short: "Obtain information about a node",
|
||||
Long: `Obtain information about a node. For example:
|
||||
|
||||
ethdo node info
|
||||
|
||||
In quiet mode this will return 0 if the node information can be obtained, otherwise 1.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := connect()
|
||||
errCheck(err, "Failed to obtain connection to Ethereum 2 beacon chain node")
|
||||
config, err := grpc.FetchChainConfig(eth2GRPCConn)
|
||||
errCheck(err, "Failed to obtain beacon chain configuration")
|
||||
|
||||
genesisTime, err := grpc.FetchGenesisTime(eth2GRPCConn)
|
||||
errCheck(err, "Failed to obtain genesis time")
|
||||
|
||||
if quiet {
|
||||
os.Exit(_exitSuccess)
|
||||
}
|
||||
|
||||
if verbose {
|
||||
version, metadata, err := grpc.FetchVersion(eth2GRPCConn)
|
||||
errCheck(err, "Failed to obtain version")
|
||||
fmt.Printf("Version: %s\n", version)
|
||||
if metadata != "" {
|
||||
fmt.Printf("Metadata: %s\n", metadata)
|
||||
}
|
||||
}
|
||||
syncing, err := grpc.FetchSyncing(eth2GRPCConn)
|
||||
errCheck(err, "Failed to obtain syncing state")
|
||||
fmt.Printf("Syncing: %v\n", syncing)
|
||||
|
||||
slot := timestampToSlot(genesisTime.Unix(), time.Now().Unix(), config["SecondsPerSlot"].(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(_exitSuccess)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
nodeCmd.AddCommand(nodeInfoCmd)
|
||||
nodeFlags(nodeInfoCmd)
|
||||
}
|
||||
285
cmd/root.go
285
cmd/root.go
@@ -14,18 +14,30 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"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"
|
||||
filesystem "github.com/wealdtech/go-eth2-wallet-store-filesystem"
|
||||
s3 "github.com/wealdtech/go-eth2-wallet-store-s3"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
|
||||
wallet "github.com/wealdtech/go-eth2-wallet"
|
||||
wtypes "github.com/wealdtech/go-eth2-wallet-types"
|
||||
wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
var cfgFile string
|
||||
@@ -33,13 +45,32 @@ var quiet bool
|
||||
var verbose bool
|
||||
var debug bool
|
||||
|
||||
// Root variables, present for all commands
|
||||
// For transaction commands.
|
||||
var wait bool
|
||||
var generate bool
|
||||
|
||||
// Root variables, present for all commands.
|
||||
var rootStore string
|
||||
var rootAccount string
|
||||
var rootBaseDir string
|
||||
var rootStorePassphrase string
|
||||
var rootWalletPassphrase string
|
||||
var rootAccountPassphrase 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
|
||||
var RootCmd = &cobra.Command{
|
||||
Use: "ethdo",
|
||||
@@ -65,20 +96,61 @@ func persistentPreRun(cmd *cobra.Command, args []string) {
|
||||
debug = viper.GetBool("debug")
|
||||
rootStore = viper.GetString("store")
|
||||
rootAccount = viper.GetString("account")
|
||||
rootBaseDir = viper.GetString("basedir")
|
||||
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")
|
||||
|
||||
if quiet && verbose {
|
||||
die("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")
|
||||
}
|
||||
|
||||
// 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(rootStorePassphrase)))
|
||||
errCheck(err, "Failed to access Amazon S3 wallet store")
|
||||
case "filesystem":
|
||||
opts := make([]filesystem.Option, 0)
|
||||
if rootStorePassphrase != "" {
|
||||
opts = append(opts, filesystem.WithPassphrase([]byte(rootStorePassphrase)))
|
||||
}
|
||||
if rootBaseDir != "" {
|
||||
opts = append(opts, filesystem.WithLocation(rootBaseDir))
|
||||
}
|
||||
store = filesystem.New(opts...)
|
||||
default:
|
||||
die(fmt.Sprintf("Unsupported wallet store %s", rootStore))
|
||||
}
|
||||
err := wallet.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.
|
||||
@@ -86,11 +158,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)")
|
||||
@@ -106,6 +184,10 @@ 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("basedir", 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 {
|
||||
panic(err)
|
||||
@@ -130,6 +212,30 @@ func init() {
|
||||
if err := viper.BindPFlag("debug", RootCmd.PersistentFlags().Lookup("debug")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
RootCmd.PersistentFlags().String("connection", "localhost:4000", "connection to Ethereum 2 node via GRPC")
|
||||
if err := viper.BindPFlag("connection", RootCmd.PersistentFlags().Lookup("connection")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
RootCmd.PersistentFlags().Duration("timeout", 10*time.Second, "the time after which a network request will be considered failed. Increase this if you are running on an error-prone, high-latency or low-bandwidth connection")
|
||||
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.
|
||||
@@ -140,10 +246,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)
|
||||
@@ -156,10 +259,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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,7 +320,8 @@ func accountFromPath(path string) (wtypes.Account, error) {
|
||||
return nil, errors.New("no account name")
|
||||
}
|
||||
|
||||
if wallet.Type() == "hierarchical deterministic" && strings.HasPrefix(accountName, "m/") && rootWalletPassphrase != "" {
|
||||
if wallet.Type() == "hierarchical deterministic" && strings.HasPrefix(accountName, "m/") {
|
||||
assert(rootWalletPassphrase != "", "--walletpassphrase is required for direct path derivations")
|
||||
err = wallet.Unlock([]byte(rootWalletPassphrase))
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid wallet passphrase")
|
||||
@@ -230,17 +331,157 @@ func accountFromPath(path string) (wtypes.Account, error) {
|
||||
return wallet.AccountByName(accountName)
|
||||
}
|
||||
|
||||
func sign(path string, data []byte, domain uint64) (types.Signature, error) {
|
||||
assert(rootAccountPassphrase != "", "--passphrase is required")
|
||||
// accountsFromPath obtains 0 or more accounts given a path specification.
|
||||
func accountsFromPath(path string) ([]wtypes.Account, error) {
|
||||
accounts := make([]wtypes.Account, 0)
|
||||
|
||||
// Quick check to see if it's a single account
|
||||
account, err := accountFromPath(path)
|
||||
if err == nil && account != nil {
|
||||
accounts = append(accounts, account)
|
||||
return accounts, nil
|
||||
}
|
||||
|
||||
wallet, err := walletFromPath(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = account.Unlock([]byte(rootAccountPassphrase))
|
||||
_, accountSpec, err := walletAndAccountNamesFromPath(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer account.Lock()
|
||||
return account.Sign(data, domain)
|
||||
|
||||
if accountSpec == "" {
|
||||
accountSpec = "^.*$"
|
||||
} else {
|
||||
accountSpec = fmt.Sprintf("^%s$", accountSpec)
|
||||
}
|
||||
re := regexp.MustCompile(accountSpec)
|
||||
|
||||
for account := range wallet.Accounts() {
|
||||
if re.Match([]byte(account.Name())) {
|
||||
accounts = append(accounts, account)
|
||||
}
|
||||
}
|
||||
|
||||
// Tidy up accounts by name.
|
||||
sort.Slice(accounts, func(i, j int) bool {
|
||||
return accounts[i].Name() < accounts[j].Name()
|
||||
})
|
||||
|
||||
return accounts, nil
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
|
||||
outputIf(debug, fmt.Sprintf("Signing %x (%d)", data, len(data)))
|
||||
return account.Sign(data)
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
|
||||
// connect connects to an Ethereum 2 endpoint.
|
||||
func connect() error {
|
||||
connection := ""
|
||||
if viper.GetString("connection") != "" {
|
||||
connection = viper.GetString("connection")
|
||||
}
|
||||
|
||||
if connection == "" {
|
||||
return errors.New("no connection")
|
||||
}
|
||||
outputIf(debug, fmt.Sprintf("Connecting to %s", connection))
|
||||
|
||||
opts := []grpc.DialOption{grpc.WithInsecure()}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||
defer cancel()
|
||||
var err 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(rootAccountPassphrase))
|
||||
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,89 +14,174 @@
|
||||
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",
|
||||
Short: "Generate deposit data for a validator",
|
||||
Long: `Generate data for a deposit to the Ethereum 1 validator contract. For example:
|
||||
Short: "Generate deposit data for one or more validators",
|
||||
Long: `Generate data for deposits to the Ethereum 1 validator contract. For example:
|
||||
|
||||
ethdo validator depositdata --validatoraccount=primary/validator --withdrawalaccount=primary/current --value="32 Ether"
|
||||
|
||||
In quiet mode this will return 0 if the the data can be generated correctly, otherwise 1.
|
||||
If validatoraccount is provided with an account path it will generate deposit data for all matching accounts.
|
||||
|
||||
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(validatorDepositDataValidatorAccount != "", "--validatoraccount is required")
|
||||
validatorAccount, err := accountFromPath(validatorDepositDataValidatorAccount)
|
||||
validatorWallet, err := walletFromPath(validatorDepositDataValidatorAccount)
|
||||
errCheck(err, "Failed to obtain validator wallet")
|
||||
validatorAccounts, err := accountsFromPath(validatorDepositDataValidatorAccount)
|
||||
errCheck(err, "Failed to obtain validator account")
|
||||
outputIf(debug, fmt.Sprintf("Validator public key is %048x", validatorAccount.PublicKey().Marshal()))
|
||||
assert(len(validatorAccounts) > 0, "Failed to obtain validator account")
|
||||
if len(validatorAccounts) == 1 {
|
||||
outputIf(debug, fmt.Sprintf("Validator public key is %048x", validatorAccounts[0].PublicKey().Marshal()))
|
||||
} else {
|
||||
for _, validatorAccount := range validatorAccounts {
|
||||
outputIf(verbose, fmt.Sprintf("Creating deposit for %s/%s", validatorWallet.Name(), validatorAccount.Name()))
|
||||
outputIf(debug, fmt.Sprintf("Validator public key is %048x", validatorAccount.PublicKey().Marshal()))
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
depositData := struct {
|
||||
PubKey []byte `ssz-size:"48"`
|
||||
WithdrawalCredentials []byte `ssz-size:"32"`
|
||||
Value uint64
|
||||
}{
|
||||
PubKey: validatorAccount.PublicKey().Marshal(),
|
||||
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})
|
||||
signature, err := sign(validatorDepositDataValidatorAccount, signingRoot[:], domain)
|
||||
errCheck(err, "Failed to sign deposit data signing root")
|
||||
// For each key, generate deposit data
|
||||
outputs := make([]string, 0)
|
||||
for _, validatorAccount := range validatorAccounts {
|
||||
depositData := struct {
|
||||
PubKey []byte `ssz-size:"48"`
|
||||
WithdrawalCredentials []byte `ssz-size:"32"`
|
||||
Value uint64
|
||||
}{
|
||||
PubKey: validatorAccount.PublicKey().Marshal(),
|
||||
WithdrawalCredentials: withdrawalCredentials,
|
||||
Value: val,
|
||||
}
|
||||
outputIf(debug, fmt.Sprintf("Deposit data:\n\tPublic key: %x\n\tWithdrawal credentials: %x\n\tValue: %d", depositData.PubKey, depositData.WithdrawalCredentials, depositData.Value))
|
||||
|
||||
signedDepositData := struct {
|
||||
PubKey []byte `ssz-size:"48"`
|
||||
WithdrawalCredentials []byte `ssz-size:"32"`
|
||||
Value uint64
|
||||
Signature []byte `ssz-size:"96"`
|
||||
}{
|
||||
PubKey: validatorAccount.PublicKey().Marshal(),
|
||||
WithdrawalCredentials: withdrawalCredentials,
|
||||
Value: val,
|
||||
Signature: signature.Marshal(),
|
||||
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)
|
||||
errCheck(err, "Failed to obtain chain configuration")
|
||||
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))
|
||||
err = validatorAccount.Unlock([]byte(rootAccountPassphrase))
|
||||
errCheck(err, "Failed to unlock validator account")
|
||||
signature, err := signStruct(validatorAccount, depositData, domain)
|
||||
validatorAccount.Lock()
|
||||
errCheck(err, "Failed to generate deposit data signature")
|
||||
|
||||
signedDepositData := struct {
|
||||
PubKey []byte `ssz-size:"48"`
|
||||
WithdrawalCredentials []byte `ssz-size:"32"`
|
||||
Value uint64
|
||||
Signature []byte `ssz-size:"96"`
|
||||
}{
|
||||
PubKey: validatorAccount.PublicKey().Marshal(),
|
||||
WithdrawalCredentials: withdrawalCredentials,
|
||||
Value: val,
|
||||
Signature: signature.Marshal(),
|
||||
}
|
||||
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")
|
||||
outputIf(debug, fmt.Sprintf("Deposit data root is %x", depositDataRoot))
|
||||
|
||||
if validatorDepositDataRaw {
|
||||
// Build a raw transaction by hand
|
||||
txData := []byte{0x22, 0x89, 0x51, 0x18}
|
||||
// Pointer to validator public key
|
||||
txData = append(txData, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80}...)
|
||||
// Pointer to withdrawal credentials
|
||||
txData = append(txData, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0}...)
|
||||
// Pointer to validator signature
|
||||
txData = append(txData, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20}...)
|
||||
// Deposit data root
|
||||
txData = append(txData, depositDataRoot[:]...)
|
||||
// Validator public key (pad to 32-byte boundary)
|
||||
txData = append(txData, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30}...)
|
||||
txData = append(txData, validatorAccount.PublicKey().Marshal()...)
|
||||
txData = append(txData, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}...)
|
||||
// Withdrawal credentials
|
||||
txData = append(txData, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20}...)
|
||||
txData = append(txData, withdrawalCredentials...)
|
||||
// Deposit signature
|
||||
txData = append(txData, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60}...)
|
||||
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","version":1}`, fmt.Sprintf("%s/%s", validatorWallet.Name(), validatorAccount.Name()), signedDepositData.PubKey, signedDepositData.WithdrawalCredentials, signedDepositData.Signature, val, depositDataRoot))
|
||||
}
|
||||
}
|
||||
|
||||
outputIf(debug, fmt.Sprintf("Deposit data signature is %x", signedDepositData.Signature))
|
||||
if quiet {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
depositDataRoot, err := ssz.HashTreeRoot(signedDepositData)
|
||||
errCheck(err, "Failed to generate deposit data root")
|
||||
outputIf(debug, fmt.Sprintf("Deposit data root is %x", depositDataRoot))
|
||||
|
||||
outputIf(!quiet, fmt.Sprintf(`{"pubkey":"%048x","withdrawal_credentials":"%032x","signature":"%096x","value":%d,"deposit_data_root":"%032x"}`, signedDepositData.PubKey, signedDepositData.WithdrawalCredentials, signedDepositData.Signature, val, depositDataRoot))
|
||||
os.Exit(0)
|
||||
if len(outputs) == 1 {
|
||||
fmt.Printf("%s\n", outputs[0])
|
||||
} else {
|
||||
fmt.Printf("[")
|
||||
fmt.Print(strings.Join(outputs, ","))
|
||||
fmt.Println("]")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -105,5 +190,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)")
|
||||
}
|
||||
|
||||
237
cmd/validatorexit.go
Normal file
237
cmd/validatorexit.go
Normal file
@@ -0,0 +1,237 @@
|
||||
// Copyright © 2020 Weald Technology Trading
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/wealdtech/ethdo/grpc"
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
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",
|
||||
Short: "Send an exit request for a validator",
|
||||
Long: `Send an exit request for a validator. For example:
|
||||
|
||||
ethdo validator exit --account=primary/validator --passphrase=secret
|
||||
|
||||
In quiet mode this will return 0 if the transaction has been generated, otherwise 1.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := connect()
|
||||
errCheck(err, "Failed to obtain connect to Ethereum 2 beacon chain node")
|
||||
|
||||
exit, signature := validatorExitHandleInput()
|
||||
validatorExitHandleExit(exit, signature)
|
||||
os.Exit(_exitSuccess)
|
||||
},
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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")
|
||||
secondsPerEpoch := config["SecondsPerSlot"].(uint64) * config["SlotsPerEpoch"].(uint64)
|
||||
|
||||
// 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)
|
||||
|
||||
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); 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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
err = account.Unlock([]byte(rootAccountPassphrase))
|
||||
errCheck(err, "Failed to unlock account; please confirm passphrase is correct")
|
||||
signature, err := signStruct(account, exit, 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(),
|
||||
}
|
||||
|
||||
validatorClient := ethpb.NewBeaconNodeValidatorClient(eth2GRPCConn)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||
defer cancel()
|
||||
_, err := validatorClient.ProposeExit(ctx, proposal)
|
||||
errCheck(err, "Failed to propose exit")
|
||||
outputIf(!quiet, "Validator exit transaction sent")
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
validatorCmd.AddCommand(validatorExitCmd)
|
||||
validatorFlags(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")
|
||||
addTransactionFlags(validatorExitCmd)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
113
cmd/validatorinfo.go
Normal file
113
cmd/validatorinfo.go
Normal file
@@ -0,0 +1,113 @@
|
||||
// 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 (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/wealdtech/ethdo/grpc"
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
types "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
string2eth "github.com/wealdtech/go-string2eth"
|
||||
)
|
||||
|
||||
var validatorInfoPubKey string
|
||||
|
||||
var validatorInfoCmd = &cobra.Command{
|
||||
Use: "info",
|
||||
Short: "Obtain information about a validator",
|
||||
Long: `Obtain information about validator. For example:
|
||||
|
||||
ethdo validator info --account=primary/validator
|
||||
|
||||
In quiet mode this will return 0 if the validator information can be obtained, otherwise 1.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// Sanity checking and setup.
|
||||
assert(rootAccount != "" || validatorInfoPubKey != "", "--account or --pubkey is required")
|
||||
|
||||
err := connect()
|
||||
errCheck(err, "Failed to obtain connection to Ethereum 2 beacon chain node")
|
||||
|
||||
var account types.Account
|
||||
if rootAccount != "" {
|
||||
account, err = accountFromPath(rootAccount)
|
||||
errCheck(err, "Failed to access account")
|
||||
} else {
|
||||
pubKeyBytes, err := hex.DecodeString(strings.TrimPrefix(validatorInfoPubKey, "0x"))
|
||||
errCheck(err, fmt.Sprintf("Failed to decode public key %s", validatorInfoPubKey))
|
||||
account, err = util.NewScratchAccount(nil, pubKeyBytes)
|
||||
errCheck(err, fmt.Sprintf("Invalid public key %s", validatorInfoPubKey))
|
||||
}
|
||||
|
||||
validatorInfo, err := grpc.FetchValidatorInfo(eth2GRPCConn, account)
|
||||
errCheck(err, "Failed to obtain validator information")
|
||||
validatorDef, err := grpc.FetchValidator(eth2GRPCConn, account)
|
||||
if validatorInfo.Status != ethpb.ValidatorStatus_DEPOSITED &&
|
||||
validatorInfo.Status != ethpb.ValidatorStatus_UNKNOWN_STATUS {
|
||||
errCheck(err, "Failed to obtain validator definition")
|
||||
}
|
||||
|
||||
assert(validatorInfo.Status != ethpb.ValidatorStatus_UNKNOWN_STATUS, "Not known as a validator")
|
||||
|
||||
if quiet {
|
||||
os.Exit(_exitSuccess)
|
||||
}
|
||||
|
||||
outputIf(verbose, fmt.Sprintf("Epoch of data:\t\t%v", validatorInfo.Epoch))
|
||||
outputIf(verbose && validatorInfo.Status != ethpb.ValidatorStatus_DEPOSITED, fmt.Sprintf("Index:\t\t\t%v", validatorInfo.Index))
|
||||
outputIf(verbose, fmt.Sprintf("Public key:\t\t%#x", validatorInfo.PublicKey))
|
||||
fmt.Printf("Status:\t\t\t%s\n", strings.Title(strings.ToLower(validatorInfo.Status.String())))
|
||||
fmt.Printf("Balance:\t\t%s\n", string2eth.GWeiToString(validatorInfo.Balance, true))
|
||||
if validatorInfo.Status == ethpb.ValidatorStatus_ACTIVE ||
|
||||
validatorInfo.Status == ethpb.ValidatorStatus_EXITING ||
|
||||
validatorInfo.Status == ethpb.ValidatorStatus_SLASHING {
|
||||
fmt.Printf("Effective balance:\t%s\n", string2eth.GWeiToString(validatorInfo.EffectiveBalance, true))
|
||||
}
|
||||
if validatorDef != nil {
|
||||
outputIf(verbose, fmt.Sprintf("Withdrawal credentials:\t%#x", validatorDef.WithdrawalCredentials))
|
||||
}
|
||||
transition := time.Unix(int64(validatorInfo.TransitionTimestamp), 0)
|
||||
transitionPassed := int64(validatorInfo.TransitionTimestamp) <= time.Now().Unix()
|
||||
switch validatorInfo.Status {
|
||||
case ethpb.ValidatorStatus_DEPOSITED:
|
||||
if validatorInfo.TransitionTimestamp != 0 {
|
||||
fmt.Printf("Inclusion in chain:\t%s\n", transition)
|
||||
}
|
||||
case ethpb.ValidatorStatus_PENDING:
|
||||
fmt.Printf("Activation:\t\t%s\n", transition)
|
||||
case ethpb.ValidatorStatus_EXITING, ethpb.ValidatorStatus_SLASHING:
|
||||
fmt.Printf("Attesting finishes:\t%s\n", transition)
|
||||
case ethpb.ValidatorStatus_EXITED:
|
||||
if transitionPassed {
|
||||
fmt.Printf("Funds withdrawable:\tNow\n")
|
||||
} else {
|
||||
fmt.Printf("Funds withdrawable:\t%s\n", transition)
|
||||
}
|
||||
}
|
||||
|
||||
os.Exit(_exitSuccess)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
validatorCmd.AddCommand(validatorInfoCmd)
|
||||
validatorInfoCmd.Flags().StringVar(&validatorInfoPubKey, "pubkey", "", "Public key for which to obtain status")
|
||||
validatorFlags(validatorInfoCmd)
|
||||
}
|
||||
@@ -30,7 +30,7 @@ var versionCmd = &cobra.Command{
|
||||
|
||||
ethdo version.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("1.1.0")
|
||||
fmt.Println("1.4.13")
|
||||
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,13 +14,13 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/spf13/cobra"
|
||||
types "github.com/wealdtech/go-eth2-types"
|
||||
"github.com/spf13/viper"
|
||||
pb "github.com/wealdtech/eth2-signer-api/pb/v1"
|
||||
)
|
||||
|
||||
var walletAccountsCmd = &cobra.Command{
|
||||
@@ -32,33 +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")
|
||||
assert(walletWallet != "", "--wallet is required")
|
||||
|
||||
wallet, err := walletFromPath(walletWallet)
|
||||
errCheck(err, "Failed to access wallet")
|
||||
hasAccounts := false
|
||||
|
||||
// List the accounts. They come to us in random order and we want them in name order, so store them in an array and sort
|
||||
output := make([]addressListResult, 0)
|
||||
for account := range wallet.Accounts() {
|
||||
output = append(output, addressListResult{id: account.ID(), name: account.Name(), pubkey: account.PublicKey()})
|
||||
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 len(output) == 0 {
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
sort.Slice(output, func(i, j int) bool {
|
||||
return output[i].name < output[j].name
|
||||
})
|
||||
for _, out := range output {
|
||||
if verbose {
|
||||
fmt.Printf("%s\n\tUUID:\t\t%s\n\tPublic key:\t0x%048x\n", out.name, out.id, out.pubkey.Marshal())
|
||||
} else if !quiet {
|
||||
fmt.Printf("%s\n", out.name)
|
||||
if hasAccounts {
|
||||
os.Exit(_exitSuccess)
|
||||
}
|
||||
os.Exit(_exitFailure)
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -67,9 +85,3 @@ func init() {
|
||||
walletCmd.AddCommand(walletAccountsCmd)
|
||||
walletFlags(walletAccountsCmd)
|
||||
}
|
||||
|
||||
type addressListResult struct {
|
||||
id uuid.UUID
|
||||
name string
|
||||
pubkey types.PublicKey
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
bip39 "github.com/tyler-smith/go-bip39"
|
||||
wallet "github.com/wealdtech/go-eth2-wallet"
|
||||
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
|
||||
hd "github.com/wealdtech/go-eth2-wallet-hd/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)))
|
||||
err = walletCreateHD(walletWallet, rootWalletPassphrase, 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 := wallet.CreateWallet(name, wallet.WithType("nd"))
|
||||
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")
|
||||
}
|
||||
|
||||
@@ -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,7 +14,10 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/wealdtech/go-bytesutil"
|
||||
@@ -33,22 +36,32 @@ 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))
|
||||
// Assume this is a path
|
||||
fileData, err := ioutil.ReadFile(walletImportData)
|
||||
errCheck(err, "Failed to read wallet import data")
|
||||
walletImportData = strings.TrimSpace(string(fileData))
|
||||
}
|
||||
outputIf(debug, fmt.Sprintf("Wallet import data is of length %d", len(walletImportData)))
|
||||
importData, err := bytesutil.FromHexString(walletImportData)
|
||||
errCheck(err, "Failed to decode wallet data")
|
||||
|
||||
_, err = wallet.ImportWallet(importData, []byte(walletImportPassphrase))
|
||||
errCheck(err, "Failed to import wallet")
|
||||
|
||||
os.Exit(_exit_success)
|
||||
os.Exit(_exitSuccess)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
walletCmd.AddCommand(walletImportCmd)
|
||||
walletFlags(walletImportCmd)
|
||||
walletImportCmd.Flags().StringVar(&walletImportData, "importdata", "", "The data to import")
|
||||
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")
|
||||
}
|
||||
|
||||
@@ -16,8 +16,10 @@ package cmd
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
var walletInfoCmd = &cobra.Command{
|
||||
@@ -29,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")
|
||||
@@ -40,6 +43,15 @@ In quiet mode this will return 0 if the wallet exists, otherwise 1.`,
|
||||
|
||||
outputIf(verbose, fmt.Sprintf("UUID: %v", wallet.ID()))
|
||||
fmt.Printf("Type: %s\n", wallet.Type())
|
||||
if verbose {
|
||||
if storeProvider, ok := wallet.(wtypes.StoreProvider); ok {
|
||||
store := storeProvider.Store()
|
||||
fmt.Printf("Store: %s\n", store.Name())
|
||||
if storeLocationProvider, ok := store.(wtypes.StoreLocationProvider); ok {
|
||||
fmt.Printf("Location: %s\n", filepath.Join(storeLocationProvider.Location(), wallet.ID().String()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Count the accounts.
|
||||
accounts := 0
|
||||
|
||||
@@ -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 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 !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")
|
||||
|
||||
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))
|
||||
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.
|
||||
15
docs/troubleshooting.md
Normal file
15
docs/troubleshooting.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# 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.
|
||||
|
||||
412
docs/usage.md
Normal file
412
docs/usage.md
Normal file
@@ -0,0 +1,412 @@
|
||||
# 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 (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
|
||||
- `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"
|
||||
```
|
||||
|
||||
#### `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"
|
||||
```
|
||||
#### `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
|
||||
|
||||
53
go.mod
53
go.mod
@@ -3,32 +3,45 @@ module github.com/wealdtech/ethdo
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/FactomProject/go-bip39 v0.3.5
|
||||
github.com/dgraph-io/ristretto v0.0.1 // indirect
|
||||
github.com/OneOfOne/xxhash v1.2.5 // indirect
|
||||
github.com/aws/aws-sdk-go v1.30.25 // indirect
|
||||
github.com/ferranbt/fastssz v0.0.0-20200415074633-b062b680417b // indirect
|
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||
github.com/gogo/protobuf v1.3.1
|
||||
github.com/golang/protobuf v1.4.1 // indirect
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // 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.5 // indirect
|
||||
github.com/herumi/bls-eth-go-binary v0.0.0-20200508110836-07e8f416e478 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/pelletier/go-toml v1.6.0 // indirect
|
||||
github.com/protolambda/zssz v0.1.4 // indirect
|
||||
github.com/prysmaticlabs/go-bitfield v0.0.0-20191017011753-53b773adde52 // indirect
|
||||
github.com/mitchellh/mapstructure v1.3.0 // indirect
|
||||
github.com/pelletier/go-toml v1.7.0 // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prysmaticlabs/ethereumapis v0.0.0-20200511212457-3444ffb75440
|
||||
github.com/prysmaticlabs/go-bitfield v0.0.0-20200322041314-62c2aee71669
|
||||
github.com/prysmaticlabs/go-ssz v0.0.0-20200101200214-e24db4d9e963
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/spf13/afero v1.2.2 // 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.1
|
||||
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.8.0
|
||||
github.com/wealdtech/go-eth2-wallet-types v1.8.0
|
||||
github.com/spf13/viper v1.7.0
|
||||
github.com/tyler-smith/go-bip39 v1.0.2
|
||||
github.com/wealdtech/eth2-signer-api v1.3.0
|
||||
github.com/wealdtech/go-bytesutil v1.1.1
|
||||
github.com/wealdtech/go-eth2-types/v2 v2.3.1
|
||||
github.com/wealdtech/go-eth2-util v1.1.5
|
||||
github.com/wealdtech/go-eth2-wallet v1.9.4
|
||||
github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.0.0
|
||||
github.com/wealdtech/go-eth2-wallet-hd/v2 v2.0.4
|
||||
github.com/wealdtech/go-eth2-wallet-store-filesystem v1.7.3
|
||||
github.com/wealdtech/go-eth2-wallet-store-s3 v1.6.3
|
||||
github.com/wealdtech/go-eth2-wallet-types/v2 v2.0.2
|
||||
github.com/wealdtech/go-string2eth v1.1.0
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7 // indirect
|
||||
golang.org/x/text v0.3.2 // indirect
|
||||
gopkg.in/ini.v1 v1.51.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.7 // indirect
|
||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 // indirect
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f // indirect
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25 // indirect
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380 // indirect
|
||||
google.golang.org/grpc v1.29.1
|
||||
gopkg.in/ini.v1 v1.56.0 // indirect
|
||||
)
|
||||
|
||||
444
go.sum
444
go.sum
@@ -1,88 +1,176 @@
|
||||
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=
|
||||
github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
|
||||
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/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.25.25 h1:j3HLOqcDWjNox1DyvJRs+kVQF42Ghtv6oL6cVBfXS3U=
|
||||
github.com/aws/aws-sdk-go v1.25.25/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.25.33 h1:8muvpP+Bq5e0CDkM9PDZ6tN74fVUq5v3zSCRaZ93ykM=
|
||||
github.com/aws/aws-sdk-go v1.25.33/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.25.50 h1:fTCp6qKnf1WLZGZtL0hh5PykCUaLZQBxlkTNG6fOK4I=
|
||||
github.com/aws/aws-sdk-go v1.25.50/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.26.2 h1:MzYLmCeny4bMQcAbYcucIduVZKp0sEf1eRLvHpKI5Is=
|
||||
github.com/aws/aws-sdk-go v1.26.2/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
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/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.30.15 h1:Sd8QDVzzE8Sl+xNccmdj0HwMrFowv6uVUx9tGsCE1ZE=
|
||||
github.com/aws/aws-sdk-go v1.30.15/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/aws/aws-sdk-go v1.30.25 h1:89NXJkfpjnMEnsxkP8MVX+LDsoiLCSqevraLb5y4Mjk=
|
||||
github.com/aws/aws-sdk-go v1.30.25/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-20200415074633-b062b680417b h1:CxaMtGnKgr9Ar2xLMVddPhnMwYLDsY56w/LxQ/wnsKA=
|
||||
github.com/ferranbt/fastssz v0.0.0-20200415074633-b062b680417b/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=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
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 h1:+EOh4OY6tjM6ZueeUKinl1f0U2820HzQOuf1iqMnsks=
|
||||
github.com/golang/protobuf v1.4.0-rc.4/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
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/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/pprof v0.0.0-20190309163659-77426154d546/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
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/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/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.14.5 h1:aiLxiiVzAXb7wb3lAmubA69IokWOoUNe+E7TdGKh8yw=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.14.5/go.mod h1:UJ0EZAp832vCd54Wev9N1BMKEyvcZ5+IM0AwDrnlkEc=
|
||||
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-20200428020417-6dd0e5634b87 h1:23l9wMlu3iMRg5PwI4wuA7sbR77GSF+rnwI0Z/Y4IPc=
|
||||
github.com/herumi/bls-eth-go-binary v0.0.0-20200428020417-6dd0e5634b87/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U=
|
||||
github.com/herumi/bls-eth-go-binary v0.0.0-20200508110836-07e8f416e478 h1:UMIHMlIgtPmh96SmXdDiEDRbQ2NSroxBQzpEknueoG4=
|
||||
github.com/herumi/bls-eth-go-binary v0.0.0-20200508110836-07e8f416e478/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/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
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=
|
||||
@@ -92,62 +180,72 @@ 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.0 h1:iDwIio/3gk2QtLLEsqU5lInaMzos0hDTz8a6lazSFVw=
|
||||
github.com/mitchellh/mapstructure v1.3.0/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-20191016230924-b2e57acce2ed h1:pX150rn565RorbtQWv2pR0SGd5rr9iHSGCSR26dg5Wk=
|
||||
github.com/phoreproject/bls v0.0.0-20191016230924-b2e57acce2ed/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.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI=
|
||||
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
|
||||
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.0 h1:J8lpUdobwIeCI7OiSxHqEwJUKvJwicL5+3v1oe2Yb4k=
|
||||
github.com/pkg/errors v0.9.0/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/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=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
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/go-bitfield v0.0.0-20191017011753-53b773adde52 h1:kxZ+xSWX0qbxoiDXQBLztKeEmEQg6TgCYWAOa7gSGGU=
|
||||
github.com/prysmaticlabs/ethereumapis v0.0.0-20200511212457-3444ffb75440 h1:plalLbsEdAKKURjR7u+JUsgD2tyxSa/yitdMyqaWY4E=
|
||||
github.com/prysmaticlabs/ethereumapis v0.0.0-20200511212457-3444ffb75440/go.mod h1:5OkRN6UmvgtP+kIewitcEKC7S5KOzLOGtya/Tz+HBns=
|
||||
github.com/prysmaticlabs/go-bitfield v0.0.0-20191017011753-53b773adde52/go.mod h1:hCwmef+4qXWjv0jLDbQdWnL0Ol7cS7/lCSS26WR+u6s=
|
||||
github.com/prysmaticlabs/go-ssz v0.0.0-20191204195639-142dfef39d12 h1:bfPIvWvA7QSmIYARnHM9tgGx1LDeSKa2bYW5QGGArxQ=
|
||||
github.com/prysmaticlabs/go-ssz v0.0.0-20191204195639-142dfef39d12/go.mod h1:VecIJZrewdAuhVckySLFt2wAAHRME934bSDurP8ftkc=
|
||||
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-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/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
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/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
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=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
@@ -155,6 +253,8 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
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=
|
||||
@@ -162,8 +262,8 @@ 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=
|
||||
@@ -171,75 +271,52 @@ 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.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk=
|
||||
github.com/spf13/viper v1.6.1/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.3.0 h1:Fs0GfrdhboBKW7zaMvIvUHJaOB1ibpAmRG3lkB53in4=
|
||||
github.com/wealdtech/eth2-signer-api v1.3.0/go.mod h1:H8OpAoTBl6CaBvZEnhxWDjjWXNc3kwVFKWMAZd6sHlk=
|
||||
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-ecodec v1.0.0 h1:QQj1t/yOGLsx8WUJaCTN+RrivgdWlP+r5ixzGzhlXVU=
|
||||
github.com/wealdtech/go-ecodec v1.0.0/go.mod h1:PSdBFEB6cltdT7V4E1jbboufMZTZXcQOKG/2PeEjKK4=
|
||||
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.0.0 h1:sKnZ84xLzj1PntxaWeiGmA+1LZ6vOeIXqCHDkVvzRGU=
|
||||
github.com/wealdtech/go-eth2-util v1.0.0/go.mod h1:ZodZI58Seya6uhbMLe3cLydsV0AhneyWCHzmkJCKlIM=
|
||||
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.5.0 h1:6QFNfXyX009C8WIxnflyEctogx1QzQ8cBb7Vftyp5VE=
|
||||
github.com/wealdtech/go-eth2-wallet v1.5.0/go.mod h1:jpYP8snFvI3/rTS+zdfyyUsyf6EXoSHl5ByWmFYq72Q=
|
||||
github.com/wealdtech/go-eth2-wallet v1.8.0 h1:QO8HzHDXNgc8AEBulz0IFdk+U9SDc0+UX3dNVstB8wY=
|
||||
github.com/wealdtech/go-eth2-wallet v1.8.0/go.mod h1:PWvCjr8NXZbEIIpFYAc7tgjc50X9PCo7K4LzXn3//bY=
|
||||
github.com/wealdtech/go-eth2-types/v2 v2.3.1 h1:2KSUzducArOynCL2prRf4vWU5GjwaPSnSN9oqNgf+dQ=
|
||||
github.com/wealdtech/go-eth2-types/v2 v2.3.1/go.mod h1:FubkGSavaa+rvmHDMTUVoPdFh00wKg0k5QPW6G52mhw=
|
||||
github.com/wealdtech/go-eth2-util v1.1.5 h1:4OPbf2yaEQmqDmOIU6UKBfhKTPNZ7skU4lPhueBLx8o=
|
||||
github.com/wealdtech/go-eth2-util v1.1.5/go.mod h1:wYYmtc9KpQQAaAzWjXSPLgtsJMkoDAmTNN0h6uj3RCA=
|
||||
github.com/wealdtech/go-eth2-wallet v1.9.4 h1:9XFM1Y7dsyrgNFFCnE3Gd00PAsrpob70SAQqHSPmsBU=
|
||||
github.com/wealdtech/go-eth2-wallet v1.9.4/go.mod h1:UGd1bAPDEtP+UrFjj3HCbip7jggFGDIQoeGw8/XHMvo=
|
||||
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.6.0 h1:01G1IzKmpO33+jUEmdAU5My/PmoKjSoxsli9LpXQl9w=
|
||||
github.com/wealdtech/go-eth2-wallet-hd v1.6.0/go.mod h1:Fmr23uSPnsPp9kDV7A3W816qzxU15QS0cq4qm2Cbc+I=
|
||||
github.com/wealdtech/go-eth2-wallet-hd v1.7.0 h1:q4YyUOAkbRPK84uBFPVg+jVuLCo1VVh7KE8ZgIW6aUU=
|
||||
github.com/wealdtech/go-eth2-wallet-hd v1.7.0/go.mod h1:sDiIKeBg8zz1lBVJYdks8my4Qj/+Q5j4mZ8OcyA1mCM=
|
||||
github.com/wealdtech/go-eth2-wallet-hd v1.8.0 h1:3oYkM6H94vA5y5RUtiBErrZSOxZnwgqRC2Yg6ZnABVg=
|
||||
github.com/wealdtech/go-eth2-wallet-hd v1.8.0/go.mod h1:EgleYFBLF2AFAPkoNiEsVJkfRMjOj76zKtKEx38bjuk=
|
||||
github.com/wealdtech/go-eth2-wallet-hd v1.9.0 h1:AtBkFvE4Usrzz+jkLgThfSW5kiXKKbQLK8rHS9OXu5I=
|
||||
github.com/wealdtech/go-eth2-wallet-hd v1.9.0/go.mod h1:QGIkXF1AtI9cLByFlnnRXDoK01/SuunRfGLzhWDMnCU=
|
||||
github.com/wealdtech/go-eth2-wallet-nd v1.5.0 h1:0nOOD0TpvGTSbRF7NGxAzgFPoEdM0Awnr9Mx3mroOm4=
|
||||
github.com/wealdtech/go-eth2-wallet-nd v1.5.0/go.mod h1:KiORm3eoIq+XR043MPekEPebU8I12918bE7ldrqk9iw=
|
||||
github.com/wealdtech/go-eth2-wallet-nd v1.6.0 h1:uMX6K/LdJdo56vaR8pIDrwGDX8w+e/MxA88B19Ys/gw=
|
||||
github.com/wealdtech/go-eth2-wallet-nd v1.6.0/go.mod h1:oVsPJTBSHnBphYush38VQYCuLwYi/HViqvZByh9vqJ0=
|
||||
github.com/wealdtech/go-eth2-wallet-nd v1.7.0 h1:hdQ1XTbz/d1ySY5/kDNQokVP7nM3i6pe/tLgVJqhlxk=
|
||||
github.com/wealdtech/go-eth2-wallet-nd v1.7.0/go.mod h1:3vaCTMI2yPndhgjXqM6ynv3yrU0VvqksQ4MaQLCgZIo=
|
||||
github.com/wealdtech/go-eth2-wallet-store-filesystem v1.4.0 h1:IH9ncOXxd10A4C6EAptnycuRcoenRmf60oKq5yirfpc=
|
||||
github.com/wealdtech/go-eth2-wallet-store-filesystem v1.4.0/go.mod h1:FCzZpqn1KqU1ezKKjjxh6l9NjmCAiTwZksgE+bnTD68=
|
||||
github.com/wealdtech/go-eth2-wallet-store-filesystem v1.5.0 h1:bVOahxWdk0qkZ6xseQBw8fNmCDIbmIM/4KHY+cUSWj8=
|
||||
github.com/wealdtech/go-eth2-wallet-store-filesystem v1.5.0/go.mod h1:8zwb4tAWxGF4PZCNNYdkjlGQYbTEadkKwCkSNyCOfm4=
|
||||
github.com/wealdtech/go-eth2-wallet-store-s3 v1.3.0 h1:Cl/XoJUwUPPO1pq8PpFg+UWSO4XyJC5h+kKYBpgQtMM=
|
||||
github.com/wealdtech/go-eth2-wallet-store-s3 v1.3.0/go.mod h1:bW+mIEvCitsc3+ZYIIzaawCjrCtI+CpGUAIrevesOSw=
|
||||
github.com/wealdtech/go-eth2-wallet-store-s3 v1.4.0 h1:7M5pJeMUhTTAjKFNd7//clwV5c8IDbbmBQJiMOPmFu8=
|
||||
github.com/wealdtech/go-eth2-wallet-store-s3 v1.4.0/go.mod h1:XDUlPyp6q2X53yIktwP4gRXk6JLbegr4dp67GBgO5T0=
|
||||
github.com/wealdtech/go-eth2-wallet-store-s3 v1.5.0 h1:ZbG99FcnBJrZhVWIfcWS/qe4C8dcog0Amjj6v+RraSE=
|
||||
github.com/wealdtech/go-eth2-wallet-store-s3 v1.5.0/go.mod h1:NTchwZDLDZhrzF3ENkC890m4SKt5SI8mHdn5XJryVL0=
|
||||
github.com/wealdtech/go-eth2-wallet-store-scratch v1.2.0 h1:GzwNLhfHlR25PXSNWPFBQwMA4nQS80WLTp9DWKhUDXU=
|
||||
github.com/wealdtech/go-eth2-wallet-store-scratch v1.2.0/go.mod h1:Rmi0S69tuts1YbVXcimw/4yvYUSiLNIUE9gIT3i1h4E=
|
||||
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.5.0 h1:9uRdSws4Wg42lQ63AEg4X/1Mt/si5MIZOBaVpuCyrLU=
|
||||
github.com/wealdtech/go-eth2-wallet-types v1.5.0/go.mod h1:OQqqr/hWOwNHe9NyXI0cYGQI6CBBmAlcusqWnJCf7T8=
|
||||
github.com/wealdtech/go-eth2-wallet-types v1.7.0 h1:wzIrTpD31rFWJIU2rWGOowoaDBpl7nTSHL8W0tjv2oo=
|
||||
github.com/wealdtech/go-eth2-wallet-types v1.7.0/go.mod h1:5A83MUBhmLgxpg9X5eqvnDIOYfn329caf5DOcv7pcY0=
|
||||
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-hd/v2 v2.0.3/go.mod h1:nNNZNXCP1BrJCu1hLlFUCAQBvCInD4zwE7eslf2U5XQ=
|
||||
github.com/wealdtech/go-eth2-wallet-hd/v2 v2.0.4 h1:/nBbza+ZPY1dn455AnZRRpIEjeUmTRQCS5QdKf6nhrg=
|
||||
github.com/wealdtech/go-eth2-wallet-hd/v2 v2.0.4/go.mod h1:nNNZNXCP1BrJCu1hLlFUCAQBvCInD4zwE7eslf2U5XQ=
|
||||
github.com/wealdtech/go-eth2-wallet-nd/v2 v2.0.3 h1:NfeWHtyjtZt3hmVA7kysNf2w+cB9Y82w6Cv4zWbFRSk=
|
||||
github.com/wealdtech/go-eth2-wallet-nd/v2 v2.0.3/go.mod h1:RDXAfay+PL+apCVLnVtWBUb183m4uug93FBZ0c2cVaw=
|
||||
github.com/wealdtech/go-eth2-wallet-store-filesystem v1.7.3 h1:2nMDDRULzSSa6LCk3044d5J4rXi2HX61nRLyGLXGI3M=
|
||||
github.com/wealdtech/go-eth2-wallet-store-filesystem v1.7.3/go.mod h1:HE1JcXUhWUtJ7F/APzSI/ZTfXjdxbQ15CDe6L0mjtaQ=
|
||||
github.com/wealdtech/go-eth2-wallet-store-s3 v1.6.3 h1:SD5tsdj9pRdsfWbhpL09X6gDGO9rJvlI6lz2cxpdfA4=
|
||||
github.com/wealdtech/go-eth2-wallet-store-s3 v1.6.3/go.mod h1:wS8sqBuIcn9QuLR1VlV7JaSKrFiNOMSPDDBYtWWe98c=
|
||||
github.com/wealdtech/go-eth2-wallet-store-scratch v1.3.3 h1:0cKttlJ5QONJ2ZndVLUVv3RhbEaSU0TKvOI2BIB9j60=
|
||||
github.com/wealdtech/go-eth2-wallet-store-scratch v1.3.3/go.mod h1:/tvALCsQ07lvqlU+IKKAdwYFYyjIO628bu/Ssv0JRv4=
|
||||
github.com/wealdtech/go-eth2-wallet-types/v2 v2.0.2 h1:Lhwne1gRUp961fD+eoWrgDbZF5rHwosI2LS5pIdX4Yc=
|
||||
github.com/wealdtech/go-eth2-wallet-types/v2 v2.0.2/go.mod h1:d7WZ9WvtL3vGSHtSh/jnVh4YO93verLL1dRW2NK5sN4=
|
||||
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=
|
||||
@@ -247,95 +324,190 @@ 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-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-20191111213947-16651526fdb4 h1:AGVXd+IAyeAb3FuQvYDYQ9+WR2JHm0+C0oYJaU1C4rs=
|
||||
golang.org/x/crypto v0.0.0-20191111213947-16651526fdb4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708 h1:pXVtWnwHkrWD9ru3sDxY/qFK/bfc0egRovX91EjWjf4=
|
||||
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/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-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g=
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17 h1:nVJ3guKA9qdkEQ3TUdXI9QSINo2CUPM/cySEvw2w8I0=
|
||||
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200427165652-729f1e841bcc h1:ZGI/fILM2+ueot/UixBSoj9188jCAxVHEZEGhqq67I4=
|
||||
golang.org/x/crypto v0.0.0-20200427165652-729f1e841bcc/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
|
||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/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-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
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-20200506145744-7e3656a0809f h1:QBjCr1Fz5kw158VqdE9JfI9cJnl/ymnJWAdMuinqL7Y=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/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/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-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4 h1:Hynbrlo6LbYI3H1IqXpkVDOcX/3HiPdhVEuyj5a59RM=
|
||||
golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea h1:Mz1TMnfJDRJLk8S8OPCoJYgrsp/Se/2TBre2+vwX128=
|
||||
golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/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-20191210023423-ac6580df4449 h1:gSbV7h1NRL2G1xTg/owz62CST1oJBmxy4QpMMregXVQ=
|
||||
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1 h1:gZpLHxUX5BdYLA08Lj4YCJNN/jk7KtquiArPoeX0WvA=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/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-20200427175716-29b57079015a h1:08u6b1caTT9MQY4wSbmsd4Ulm6DmgNYnbImBuZjGJow=
|
||||
golang.org/x/sys v0.0.0-20200427175716-29b57079015a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25 h1:OKbAoGs4fGM5cPLlVQLZGYkFC8OnOfgo6tt0Smf9XhM=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/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/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-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-20200511104702-f5ebc3bea380 h1:xriR1EgvKfkKxIoU2uUvrMVl+H26359loFFUleSMXFo=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
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/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
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.20.1 h1:ESRXHgpUBG5D2I5mmsQIyYxB/tQIZfSZ8wLyFDf/N/U=
|
||||
google.golang.org/protobuf v1.20.1/go.mod h1:KqelGeouBkcbcuB3HCk4/YH2tmNLk6YSWA5LIWeI/lY=
|
||||
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=
|
||||
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.51.1 h1:GyboHr4UqMiLUybYjd22ZjQIKEJEpgtLXtuGbR21Oho=
|
||||
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.56.0 h1:DPMeDvGTM54DXbPkVIZsp19fp/I2K7zwA/itHYHKo8Y=
|
||||
gopkg.in/ini.v1 v1.56.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=
|
||||
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.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||
gopkg.in/yaml.v2 v2.2.7/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=
|
||||
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=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
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=
|
||||
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=
|
||||
|
||||
169
grpc/beaconchain.go
Normal file
169
grpc/beaconchain.go
Normal file
@@ -0,0 +1,169 @@
|
||||
// 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 grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gogo/protobuf/types"
|
||||
"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/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, &types.Empty{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results := make(map[string]interface{})
|
||||
for k, v := range config.Config {
|
||||
// Handle integers
|
||||
if v == "0" {
|
||||
results[k] = uint64(0)
|
||||
continue
|
||||
}
|
||||
intVal, err := strconv.ParseUint(v, 10, 64)
|
||||
if err == nil && intVal != 0 {
|
||||
results[k] = intVal
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle byte arrays
|
||||
if strings.HasPrefix(v, "[") {
|
||||
vals := strings.Split(v[1:len(v)-1], " ")
|
||||
res := make([]byte, len(vals))
|
||||
for i, val := range vals {
|
||||
intVal, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to convert value %q for %s", v, k)
|
||||
}
|
||||
res[i] = byte(intVal)
|
||||
}
|
||||
results[k] = res
|
||||
continue
|
||||
}
|
||||
|
||||
// String (or unhandled format)
|
||||
results[k] = v
|
||||
}
|
||||
return results, 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()
|
||||
|
||||
req := ðpb.GetValidatorRequest{
|
||||
QueryFilter: ðpb.GetValidatorRequest_PublicKey{
|
||||
PublicKey: account.PublicKey().Marshal(),
|
||||
},
|
||||
}
|
||||
return beaconClient.GetValidator(ctx, req)
|
||||
}
|
||||
|
||||
// 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()
|
||||
|
||||
req := ðpb.GetValidatorRequest{
|
||||
QueryFilter: ðpb.GetValidatorRequest_Index{
|
||||
Index: index,
|
||||
},
|
||||
}
|
||||
return beaconClient.GetValidator(ctx, req)
|
||||
}
|
||||
|
||||
// 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()
|
||||
|
||||
stream, err := beaconClient.StreamValidatorsInfo(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to contact beacon node")
|
||||
}
|
||||
|
||||
changeSet := ðpb.ValidatorChangeSet{
|
||||
Action: ethpb.SetAction_SET_VALIDATOR_KEYS,
|
||||
PublicKeys: [][]byte{account.PublicKey().Marshal()},
|
||||
}
|
||||
err = stream.Send(changeSet)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to send validator public key")
|
||||
}
|
||||
return stream.Recv()
|
||||
}
|
||||
|
||||
// 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, &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()
|
||||
|
||||
req := ðpb.ListBlocksRequest{
|
||||
QueryFilter: ðpb.ListBlocksRequest_Slot{Slot: slot},
|
||||
}
|
||||
resp, err := beaconClient.ListBlocks(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(resp.BlockContainers) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return resp.BlockContainers[0].Block, nil
|
||||
}
|
||||
67
grpc/beaconnode.go
Normal file
67
grpc/beaconnode.go
Normal file
@@ -0,0 +1,67 @@
|
||||
// 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 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/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()
|
||||
|
||||
// Fetch the account.
|
||||
req := ðpb.ValidatorIndexRequest{
|
||||
PublicKey: account.PublicKey().Marshal(),
|
||||
}
|
||||
resp, err := validatorClient.ValidatorIndex(ctx, req)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return resp.Index, nil
|
||||
}
|
||||
|
||||
// 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()
|
||||
|
||||
// Fetch the account.
|
||||
req := ðpb.ValidatorStatusRequest{
|
||||
PublicKey: account.PublicKey().Marshal(),
|
||||
}
|
||||
resp, err := validatorClient.ValidatorStatus(ctx, req)
|
||||
if err != nil {
|
||||
return ethpb.ValidatorStatus_UNKNOWN_STATUS, err
|
||||
}
|
||||
|
||||
return resp.Status, nil
|
||||
}
|
||||
86
grpc/node.go
Normal file
86
grpc/node.go
Normal file
@@ -0,0 +1,86 @@
|
||||
// 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 grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/gogo/protobuf/types"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/viper"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
||||
)
|
||||
|
||||
// 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, &types.Empty{})
|
||||
if err != nil {
|
||||
return time.Now(), err
|
||||
}
|
||||
return time.Unix(res.GetGenesisTime().Seconds, 0), nil
|
||||
}
|
||||
|
||||
// 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()
|
||||
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
|
||||
}
|
||||
return version.Version, version.Metadata, nil
|
||||
}
|
||||
|
||||
// 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, &types.Empty{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return syncStatus.Syncing, nil
|
||||
}
|
||||
100
util/scratchaccount.go
Normal file
100
util/scratchaccount.go
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright © 2020 Weald Technology Trading
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/google/uuid"
|
||||
types "github.com/wealdtech/go-eth2-types/v2"
|
||||
)
|
||||
|
||||
// ScratchAccount is an account that exists temporarily.
|
||||
type ScratchAccount struct {
|
||||
id uuid.UUID
|
||||
privKey types.PrivateKey
|
||||
pubKey types.PublicKey
|
||||
unlocked bool
|
||||
}
|
||||
|
||||
// NewScratchAccount creates a new local account.
|
||||
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,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *ScratchAccount) ID() uuid.UUID {
|
||||
return a.id
|
||||
}
|
||||
|
||||
func (a *ScratchAccount) Name() string {
|
||||
return "scratch"
|
||||
}
|
||||
|
||||
func (a *ScratchAccount) PublicKey() types.PublicKey {
|
||||
return a.pubKey
|
||||
}
|
||||
|
||||
func (a *ScratchAccount) Path() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (a *ScratchAccount) Lock() {
|
||||
a.unlocked = false
|
||||
}
|
||||
|
||||
func (a *ScratchAccount) Unlock([]byte) error {
|
||||
a.unlocked = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *ScratchAccount) IsUnlocked() bool {
|
||||
return a.unlocked
|
||||
}
|
||||
|
||||
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