mirror of
https://github.com/wealdtech/ethdo.git
synced 2026-01-10 14:37:57 -05:00
Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76b9010bc8 | ||
|
|
15d58e20d4 | ||
|
|
ec9f5b8012 | ||
|
|
5c907bb8f8 | ||
|
|
5e2cf9697c | ||
|
|
7d3201826d | ||
|
|
5e0a341eb1 | ||
|
|
954a972a36 | ||
|
|
c0dd5dcfc6 | ||
|
|
fd394e3475 | ||
|
|
5dcdf9c11f | ||
|
|
14f559ab8b | ||
|
|
6bb79f821c | ||
|
|
6dcd3c9978 | ||
|
|
d5acd2f842 | ||
|
|
1395b7159f | ||
|
|
548442c33b | ||
|
|
f8f7eb26e8 | ||
|
|
ca10ba7411 | ||
|
|
d8ccf67be8 | ||
|
|
8fba19597e | ||
|
|
c034dfaf53 | ||
|
|
4576978347 | ||
|
|
97c409fde6 | ||
|
|
5b2e62c29e | ||
|
|
c7025d99dd | ||
|
|
111f5bf627 | ||
|
|
7de8ad8a59 | ||
|
|
31336dd5ce | ||
|
|
47104b31a4 | ||
|
|
59200e796a | ||
|
|
c91538644f | ||
|
|
3140fc5b8a | ||
|
|
a2afd37a97 | ||
|
|
d453ba9303 | ||
|
|
d0b278c0ec | ||
|
|
27a59c031b | ||
|
|
1bc139c591 | ||
|
|
7e8db1cd2e | ||
|
|
864bb30244 | ||
|
|
4209f725ba | ||
|
|
d01e789c8a | ||
|
|
85a0590d55 | ||
|
|
b9ba1ec1c2 | ||
|
|
29bffd0dbe | ||
|
|
e7a2c600f1 | ||
|
|
f2a5a93195 | ||
|
|
095c246efb | ||
|
|
581d22c7d7 | ||
|
|
1ca1e1f2d6 | ||
|
|
7c88b7c082 | ||
|
|
4d351e6d3d | ||
|
|
c46e9740d4 | ||
|
|
f9cb1054c0 | ||
|
|
4e2fa63f30 | ||
|
|
ca5eec9c8c | ||
|
|
582930d982 | ||
|
|
5a572abc6f | ||
|
|
a2d018c321 | ||
|
|
12f3154157 | ||
|
|
51689db315 | ||
|
|
1a8897ff0f | ||
|
|
5ee92bee78 | ||
|
|
eba7a0c88a | ||
|
|
f6a6224968 | ||
|
|
60e8878dc3 | ||
|
|
41d9160b63 | ||
|
|
b3d4c9af08 | ||
|
|
5a8a13d8f3 | ||
|
|
d94e1551a7 | ||
|
|
03e7f15d04 |
3
.github/workflows/golangci-lint.yml
vendored
3
.github/workflows/golangci-lint.yml
vendored
@@ -15,10 +15,9 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
go-version: '1.20'
|
||||
- uses: actions/checkout@v3
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: latest
|
||||
args: --timeout=60m
|
||||
|
||||
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
@@ -59,7 +59,7 @@ jobs:
|
||||
|
||||
- name: Compile
|
||||
run: |
|
||||
go build -tags osusergo,netgo -ldflags="-extldflags=-static" -v -ldflags="-X github.com/${{ github.repository }}/cmd.ReleaseVersion=${{ needs.env_vars.outputs.release_version }}"
|
||||
go build -tags osusergo,netgo -v -ldflags="-X github.com/${{ github.repository }}/cmd.ReleaseVersion=${{ needs.env_vars.outputs.release_version }} -extldflags -static"
|
||||
tar zcf ${{ needs.env_vars.outputs.binary }}-${{ needs.env_vars.outputs.release_version }}-linux-amd64.tar.gz ${{ needs.env_vars.outputs.binary }}
|
||||
sha256sum ${{ needs.env_vars.outputs.binary }}-${{ needs.env_vars.outputs.release_version }}-linux-amd64.tar.gz | sed -e 's/ .*//' >${{ needs.env_vars.outputs.binary }}-${{ needs.env_vars.outputs.release_version }}-linux-amd64.tar.gz.sha256
|
||||
- name: Upload binary
|
||||
@@ -84,8 +84,10 @@ jobs:
|
||||
|
||||
- name: Cross compile (ARM64)
|
||||
run: |
|
||||
apt install -y gcc-aarch64-linux-gnu libstdc++-12-pic-arm64-cross
|
||||
CGO_ENABLED=1 CC=aarch64-linux-gnu-gcc GOOS=linux GOARCH=arm64 go build -tags osusergo,netgo -ldflags="-extldflags=-static" -v -ldflags="-X github.com/${{ github.repository }}/cmd.ReleaseVersion=${{ needs.env_vars.outputs.release_version }}"
|
||||
sudo apt-get update
|
||||
sudo apt-get upgrade
|
||||
sudo apt install -y gcc-aarch64-linux-gnu libstdc++-11-pic-arm64-cross
|
||||
CGO_ENABLED=1 CC=aarch64-linux-gnu-gcc GOOS=linux GOARCH=arm64 go build -tags osusergo,netgo -v -ldflags="-X github.com/${{ github.repository }}/cmd.ReleaseVersion=${{ needs.env_vars.outputs.release_version }} -extldflags -static"
|
||||
tar zcf ${{ needs.env_vars.outputs.binary }}-${{ needs.env_vars.outputs.release_version }}-linux-arm64.tar.gz ${{ needs.env_vars.outputs.binary }}
|
||||
sha256sum ${{ needs.env_vars.outputs.binary }}-${{ needs.env_vars.outputs.release_version }}-linux-arm64.tar.gz | sed -e 's/ .*//' >${{ needs.env_vars.outputs.binary }}-${{ needs.env_vars.outputs.release_version }}-linux-arm64.tar.gz.sha256
|
||||
|
||||
@@ -130,7 +132,7 @@ jobs:
|
||||
|
||||
- name: Compile
|
||||
run: |
|
||||
go build -tags osusergo,netgo -ldflags="-extldflags=-static" -v -ldflags="-X github.com/${{ github.repository }}/cmd.ReleaseVersion=${{ needs.env_vars.outputs.release_version }}"
|
||||
go build -tags osusergo,netgo -v -ldflags="-X github.com/${{ github.repository }}/cmd.ReleaseVersion=${{ needs.env_vars.outputs.release_version }}"
|
||||
tar zcf ${{ needs.env_vars.outputs.binary }}-${{ needs.env_vars.outputs.release_version }}-darwin-amd64.tar.gz ${{ needs.env_vars.outputs.binary }}
|
||||
brew install coreutils
|
||||
sha256sum ${{ needs.env_vars.outputs.binary }}-${{ needs.env_vars.outputs.release_version }}-darwin-amd64.tar.gz | sed -e 's/ .*//' >${{ needs.env_vars.outputs.binary }}-${{ needs.env_vars.outputs.release_version }}-darwin-amd64.tar.gz.sha256
|
||||
@@ -175,7 +177,7 @@ jobs:
|
||||
|
||||
- name: Compile
|
||||
run: |
|
||||
go build -tags osusergo,netgo -ldflags="-extldflags=-static" -v -ldflags="-X github.com/${{ github.repository }}/cmd.ReleaseVersion=${{ needs.env_vars.outputs.release_version }}"
|
||||
go build -v -ldflags="-X github.com/${{ github.repository }}/cmd.ReleaseVersion=${{ needs.env_vars.outputs.release_version }} -extldflags -static"
|
||||
choco install zip
|
||||
zip --junk-paths ${{ needs.env_vars.outputs.binary }}-${{ needs.env_vars.outputs.release_version }}-windows-exe.zip ${{ needs.env_vars.outputs.binary }}.exe
|
||||
$FileHash=(certutil -hashfile ${{ needs.env_vars.outputs.binary }}-${{ needs.env_vars.outputs.release_version }}-windows-exe.zip SHA256 | findstr /v hash | findstr /v SHA).replace(" ", "")
|
||||
|
||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -10,6 +10,6 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
go-version: '1.20'
|
||||
- uses: actions/checkout@v3
|
||||
- uses: n8maninger/action-golang-test@v1
|
||||
|
||||
170
.golangci.yml
Normal file
170
.golangci.yml
Normal file
@@ -0,0 +1,170 @@
|
||||
# This file contains all available configuration options
|
||||
# with their default values (in comments).
|
||||
#
|
||||
# This file is not a configuration example,
|
||||
# it contains the exhaustive configuration with explanations of the options.
|
||||
|
||||
# Options for analysis running.
|
||||
run:
|
||||
# The default concurrency value is the number of available CPU.
|
||||
# concurrency: 4
|
||||
|
||||
# Timeout for analysis, e.g. 30s, 5m.
|
||||
# Default: 1m
|
||||
timeout: 10m
|
||||
|
||||
# Exit code when at least one issue was found.
|
||||
# Default: 1
|
||||
# issues-exit-code: 2
|
||||
|
||||
# Include test files or not.
|
||||
# Default: true
|
||||
tests: false
|
||||
|
||||
# List of build tags, all linters use it.
|
||||
# Default: [].
|
||||
# build-tags:
|
||||
# - mytag
|
||||
|
||||
# Which dirs to skip: issues from them won't be reported.
|
||||
# Can use regexp here: `generated.*`, regexp is applied on full path.
|
||||
# Default value is empty list,
|
||||
# but default dirs are skipped independently of this option's value (see skip-dirs-use-default).
|
||||
# "/" will be replaced by current OS file path separator to properly work on Windows.
|
||||
# skip-dirs:
|
||||
# - autogenerated_by_my_lib
|
||||
|
||||
# Enables skipping of directories:
|
||||
# - vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
|
||||
# Default: true
|
||||
# skip-dirs-use-default: false
|
||||
|
||||
# Which files to skip: they will be analyzed, but issues from them won't be reported.
|
||||
# Default value is empty list,
|
||||
# but there is no need to include all autogenerated files,
|
||||
# we confidently recognize autogenerated files.
|
||||
# If it's not please let us know.
|
||||
# "/" will be replaced by current OS file path separator to properly work on Windows.
|
||||
skip-files:
|
||||
- ".*_ssz\\.go$"
|
||||
|
||||
# If set we pass it to "go list -mod={option}". From "go help modules":
|
||||
# If invoked with -mod=readonly, the go command is disallowed from the implicit
|
||||
# automatic updating of go.mod described above. Instead, it fails when any changes
|
||||
# to go.mod are needed. This setting is most useful to check that go.mod does
|
||||
# not need updates, such as in a continuous integration and testing system.
|
||||
# If invoked with -mod=vendor, the go command assumes that the vendor
|
||||
# directory holds the correct copies of dependencies and ignores
|
||||
# the dependency descriptions in go.mod.
|
||||
#
|
||||
# Allowed values: readonly|vendor|mod
|
||||
# By default, it isn't set.
|
||||
modules-download-mode: readonly
|
||||
|
||||
# Allow multiple parallel golangci-lint instances running.
|
||||
# If false (default) - golangci-lint acquires file lock on start.
|
||||
allow-parallel-runners: true
|
||||
|
||||
# Define the Go version limit.
|
||||
# Mainly related to generics support since go1.18.
|
||||
# Default: use Go version from the go.mod file, fallback on the env var `GOVERSION`, fallback on 1.18
|
||||
go: '1.19'
|
||||
|
||||
|
||||
# output configuration options
|
||||
output:
|
||||
# Format: colored-line-number|line-number|json|tab|checkstyle|code-climate|junit-xml|github-actions
|
||||
#
|
||||
# Multiple can be specified by separating them by comma, output can be provided
|
||||
# for each of them by separating format name and path by colon symbol.
|
||||
# Output path can be either `stdout`, `stderr` or path to the file to write to.
|
||||
# Example: "checkstyle:report.json,colored-line-number"
|
||||
#
|
||||
# Default: colored-line-number
|
||||
# format: json
|
||||
|
||||
# Print lines of code with issue.
|
||||
# Default: true
|
||||
# print-issued-lines: false
|
||||
|
||||
# Print linter name in the end of issue text.
|
||||
# Default: true
|
||||
# print-linter-name: false
|
||||
|
||||
# Make issues output unique by line.
|
||||
# Default: true
|
||||
# uniq-by-line: false
|
||||
|
||||
# Add a prefix to the output file references.
|
||||
# Default is no prefix.
|
||||
# path-prefix: ""
|
||||
|
||||
# Sort results by: filepath, line and column.
|
||||
# sort-results: true
|
||||
|
||||
|
||||
# All available settings of specific linters.
|
||||
linters-settings:
|
||||
lll:
|
||||
line-length: 132
|
||||
|
||||
stylecheck:
|
||||
checks: [ "all", "-ST1000" ]
|
||||
|
||||
tagliatelle:
|
||||
case:
|
||||
# use-field-name: true
|
||||
rules:
|
||||
json: snake
|
||||
yaml: snake
|
||||
|
||||
linters:
|
||||
# Enable all available linters.
|
||||
# Default: false
|
||||
enable-all: true
|
||||
# Disable specific linter
|
||||
# https://golangci-lint.run/usage/linters/#disabled-by-default
|
||||
disable:
|
||||
- contextcheck
|
||||
- cyclop
|
||||
- deadcode
|
||||
- dupl
|
||||
- errorlint
|
||||
- exhaustive
|
||||
- exhaustivestruct
|
||||
- exhaustruct
|
||||
- forbidigo
|
||||
- forcetypeassert
|
||||
- funlen
|
||||
- gci
|
||||
- gochecknoglobals
|
||||
- gochecknoinits
|
||||
- gocognit
|
||||
- goconst
|
||||
- goerr113
|
||||
- goheader
|
||||
- golint
|
||||
- gomnd
|
||||
- ifshort
|
||||
- interfacer
|
||||
- ireturn
|
||||
- lll
|
||||
- maintidx
|
||||
- maligned
|
||||
- musttag
|
||||
- nestif
|
||||
- nilnil
|
||||
- nlreturn
|
||||
- nolintlint
|
||||
- nosnakecase
|
||||
- promlinter
|
||||
- rowserrcheck
|
||||
- scopelint
|
||||
- sqlclosecheck
|
||||
- structcheck
|
||||
- unparam
|
||||
- varcheck
|
||||
- varnamelen
|
||||
- wastedassign
|
||||
- wrapcheck
|
||||
- wsl
|
||||
19
CHANGELOG.md
19
CHANGELOG.md
@@ -1,3 +1,22 @@
|
||||
dev:
|
||||
- generate error message if "validator credentials set" process fails to generate any credentials
|
||||
- allow import of accounts with null name field in their keystore
|
||||
- show text of execution payload extra data if available
|
||||
|
||||
1.28.0:
|
||||
- support additional mnemonic word list languages
|
||||
- increase minimum timeout for commands that fetch all validators to 2 minutes
|
||||
- provide better error messages when offline preparation file cannot be read
|
||||
- allow creation of all credential change operations related to a private key (thanks to @joaocenoura)
|
||||
|
||||
1.27.1:
|
||||
- fix issue with voluntary exits using incorrect domain (thanks to @0xTylerHolmes)
|
||||
|
||||
1.27.0:
|
||||
- use new build system
|
||||
- support S3 credentials
|
||||
- update operation of validator exit to match validator credentials set
|
||||
|
||||
1.26.5:
|
||||
- provide validator information in "chain status" verbose output
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.17-bullseye as builder
|
||||
FROM golang:1.18-bullseye as builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
304
beacon/chaininfo.go
Normal file
304
beacon/chaininfo.go
Normal file
@@ -0,0 +1,304 @@
|
||||
// Copyright © 2023 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 beacon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
consensusclient "github.com/attestantio/go-eth2-client"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/wealdtech/ethdo/services/chaintime"
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
)
|
||||
|
||||
type ChainInfo struct {
|
||||
Version uint64
|
||||
Validators []*ValidatorInfo
|
||||
GenesisValidatorsRoot phase0.Root
|
||||
Epoch phase0.Epoch
|
||||
GenesisForkVersion phase0.Version
|
||||
CurrentForkVersion phase0.Version
|
||||
BLSToExecutionChangeDomainType phase0.DomainType
|
||||
VoluntaryExitDomainType phase0.DomainType
|
||||
}
|
||||
|
||||
type chainInfoJSON struct {
|
||||
Version string `json:"version"`
|
||||
Validators []*ValidatorInfo `json:"validators"`
|
||||
GenesisValidatorsRoot string `json:"genesis_validators_root"`
|
||||
Epoch string `json:"epoch"`
|
||||
GenesisForkVersion string `json:"genesis_fork_version"`
|
||||
CurrentForkVersion string `json:"current_fork_version"`
|
||||
BLSToExecutionChangeDomainType string `json:"bls_to_execution_change_domain_type"`
|
||||
VoluntaryExitDomainType string `json:"voluntary_exit_domain_type"`
|
||||
}
|
||||
|
||||
type chainInfoVersionJSON struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (c *ChainInfo) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&chainInfoJSON{
|
||||
Version: fmt.Sprintf("%d", c.Version),
|
||||
Validators: c.Validators,
|
||||
GenesisValidatorsRoot: fmt.Sprintf("%#x", c.GenesisValidatorsRoot),
|
||||
Epoch: fmt.Sprintf("%d", c.Epoch),
|
||||
GenesisForkVersion: fmt.Sprintf("%#x", c.GenesisForkVersion),
|
||||
CurrentForkVersion: fmt.Sprintf("%#x", c.CurrentForkVersion),
|
||||
BLSToExecutionChangeDomainType: fmt.Sprintf("%#x", c.BLSToExecutionChangeDomainType),
|
||||
VoluntaryExitDomainType: fmt.Sprintf("%#x", c.VoluntaryExitDomainType),
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (c *ChainInfo) UnmarshalJSON(input []byte) error {
|
||||
// See which version we are dealing with.
|
||||
var metadata chainInfoVersionJSON
|
||||
if err := json.Unmarshal(input, &metadata); err != nil {
|
||||
return errors.Wrap(err, "invalid JSON")
|
||||
}
|
||||
if metadata.Version == "" {
|
||||
return errors.New("version missing")
|
||||
}
|
||||
version, err := strconv.ParseUint(metadata.Version, 10, 64)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "version invalid")
|
||||
}
|
||||
if version < 2 {
|
||||
return errors.New("outdated version; please regenerate your offline data")
|
||||
}
|
||||
c.Version = version
|
||||
|
||||
var data chainInfoJSON
|
||||
if err := json.Unmarshal(input, &data); err != nil {
|
||||
return errors.Wrap(err, "invalid JSON")
|
||||
}
|
||||
|
||||
if len(data.Validators) == 0 {
|
||||
return errors.New("validators missing")
|
||||
}
|
||||
c.Validators = data.Validators
|
||||
|
||||
if data.GenesisValidatorsRoot == "" {
|
||||
return errors.New("genesis validators root missing")
|
||||
}
|
||||
genesisValidatorsRootBytes, err := hex.DecodeString(strings.TrimPrefix(data.GenesisValidatorsRoot, "0x"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "genesis validators root invalid")
|
||||
}
|
||||
if len(genesisValidatorsRootBytes) != phase0.RootLength {
|
||||
return errors.New("genesis validators root incorrect length")
|
||||
}
|
||||
copy(c.GenesisValidatorsRoot[:], genesisValidatorsRootBytes)
|
||||
|
||||
if data.Epoch == "" {
|
||||
return errors.New("epoch missing")
|
||||
}
|
||||
epoch, err := strconv.ParseUint(data.Epoch, 10, 64)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "epoch invalid")
|
||||
}
|
||||
c.Epoch = phase0.Epoch(epoch)
|
||||
|
||||
if data.GenesisForkVersion == "" {
|
||||
return errors.New("genesis fork version missing")
|
||||
}
|
||||
genesisForkVersionBytes, err := hex.DecodeString(strings.TrimPrefix(data.GenesisForkVersion, "0x"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "genesis fork version invalid")
|
||||
}
|
||||
if len(genesisForkVersionBytes) != phase0.ForkVersionLength {
|
||||
return errors.New("genesis fork version incorrect length")
|
||||
}
|
||||
copy(c.GenesisForkVersion[:], genesisForkVersionBytes)
|
||||
|
||||
if data.CurrentForkVersion == "" {
|
||||
return errors.New("current fork version missing")
|
||||
}
|
||||
currentForkVersionBytes, err := hex.DecodeString(strings.TrimPrefix(data.CurrentForkVersion, "0x"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "current fork version invalid")
|
||||
}
|
||||
if len(currentForkVersionBytes) != phase0.ForkVersionLength {
|
||||
return errors.New("current fork version incorrect length")
|
||||
}
|
||||
copy(c.CurrentForkVersion[:], currentForkVersionBytes)
|
||||
|
||||
if data.BLSToExecutionChangeDomainType == "" {
|
||||
return errors.New("bls to execution domain type missing")
|
||||
}
|
||||
blsToExecutionChangeDomainType, err := hex.DecodeString(strings.TrimPrefix(data.BLSToExecutionChangeDomainType, "0x"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "bls to execution domain type invalid")
|
||||
}
|
||||
if len(blsToExecutionChangeDomainType) != phase0.DomainTypeLength {
|
||||
return errors.New("bls to execution domain type incorrect length")
|
||||
}
|
||||
copy(c.BLSToExecutionChangeDomainType[:], blsToExecutionChangeDomainType)
|
||||
|
||||
if data.VoluntaryExitDomainType == "" {
|
||||
return errors.New("voluntary exit domain type missing")
|
||||
}
|
||||
voluntaryExitDomainType, err := hex.DecodeString(strings.TrimPrefix(data.VoluntaryExitDomainType, "0x"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "voluntary exit domain type invalid")
|
||||
}
|
||||
if len(voluntaryExitDomainType) != phase0.DomainTypeLength {
|
||||
return errors.New("voluntary exit domain type incorrect length")
|
||||
}
|
||||
copy(c.VoluntaryExitDomainType[:], voluntaryExitDomainType)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FetchValidatorInfo fetches validator info given a validator identifier.
|
||||
func (c *ChainInfo) FetchValidatorInfo(ctx context.Context, id string) (*ValidatorInfo, error) {
|
||||
var validatorInfo *ValidatorInfo
|
||||
switch {
|
||||
case id == "":
|
||||
return nil, errors.New("no validator specified")
|
||||
case strings.HasPrefix(id, "0x"):
|
||||
// ID is a public key.
|
||||
// Check that the key is the correct length.
|
||||
if len(id) != 98 {
|
||||
return nil, errors.New("invalid public key: incorrect length")
|
||||
}
|
||||
for _, validator := range c.Validators {
|
||||
if strings.EqualFold(id, fmt.Sprintf("%#x", validator.Pubkey)) {
|
||||
validatorInfo = validator
|
||||
break
|
||||
}
|
||||
}
|
||||
case strings.Contains(id, "/"):
|
||||
// An account.
|
||||
_, account, err := util.WalletAndAccountFromPath(ctx, id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to obtain account")
|
||||
}
|
||||
accPubKey, err := util.BestPublicKey(account)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to obtain public key for account")
|
||||
}
|
||||
pubkey := fmt.Sprintf("%#x", accPubKey.Marshal())
|
||||
for _, validator := range c.Validators {
|
||||
if strings.EqualFold(pubkey, fmt.Sprintf("%#x", validator.Pubkey)) {
|
||||
validatorInfo = validator
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
// An index.
|
||||
index, err := strconv.ParseUint(id, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse validator index")
|
||||
}
|
||||
validatorIndex := phase0.ValidatorIndex(index)
|
||||
for _, validator := range c.Validators {
|
||||
if validator.Index == validatorIndex {
|
||||
validatorInfo = validator
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if validatorInfo == nil {
|
||||
return nil, errors.New("unknown validator")
|
||||
}
|
||||
|
||||
return validatorInfo, nil
|
||||
}
|
||||
|
||||
// ObtainChainInfoFromNode obtains the chain information from a node.
|
||||
func ObtainChainInfoFromNode(ctx context.Context,
|
||||
consensusClient consensusclient.Service,
|
||||
chainTime chaintime.Service,
|
||||
) (
|
||||
*ChainInfo,
|
||||
error,
|
||||
) {
|
||||
res := &ChainInfo{
|
||||
Version: 2,
|
||||
Validators: make([]*ValidatorInfo, 0),
|
||||
Epoch: chainTime.CurrentEpoch(),
|
||||
}
|
||||
|
||||
// Obtain validators.
|
||||
validators, err := consensusClient.(consensusclient.ValidatorsProvider).Validators(ctx, "head", nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain validators")
|
||||
}
|
||||
|
||||
for _, validator := range validators {
|
||||
res.Validators = append(res.Validators, &ValidatorInfo{
|
||||
Index: validator.Index,
|
||||
Pubkey: validator.Validator.PublicKey,
|
||||
WithdrawalCredentials: validator.Validator.WithdrawalCredentials,
|
||||
State: validator.Status,
|
||||
})
|
||||
}
|
||||
|
||||
// Genesis validators root obtained from beacon node.
|
||||
genesis, err := consensusClient.(consensusclient.GenesisProvider).Genesis(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain genesis information")
|
||||
}
|
||||
res.GenesisValidatorsRoot = genesis.GenesisValidatorsRoot
|
||||
|
||||
// Fetch the genesis fork version from the specification.
|
||||
spec, err := consensusClient.(consensusclient.SpecProvider).Spec(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain spec")
|
||||
}
|
||||
tmp, exists := spec["GENESIS_FORK_VERSION"]
|
||||
if !exists {
|
||||
return nil, errors.New("capella fork version not known by chain")
|
||||
}
|
||||
var isForkVersion bool
|
||||
res.GenesisForkVersion, isForkVersion = tmp.(phase0.Version)
|
||||
if !isForkVersion {
|
||||
return nil, errors.New("could not obtain GENESIS_FORK_VERSION")
|
||||
}
|
||||
|
||||
// Fetch the current fork version from the fork schedule.
|
||||
forkSchedule, err := consensusClient.(consensusclient.ForkScheduleProvider).ForkSchedule(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain fork schedule")
|
||||
}
|
||||
for i := range forkSchedule {
|
||||
if forkSchedule[i].Epoch <= res.Epoch {
|
||||
res.CurrentForkVersion = forkSchedule[i].CurrentVersion
|
||||
}
|
||||
}
|
||||
|
||||
blsToExecutionChangeDomainType, exists := spec["DOMAIN_BLS_TO_EXECUTION_CHANGE"].(phase0.DomainType)
|
||||
if !exists {
|
||||
return nil, errors.New("failed to obtain DOMAIN_BLS_TO_EXECUTION_CHANGE")
|
||||
}
|
||||
copy(res.BLSToExecutionChangeDomainType[:], blsToExecutionChangeDomainType[:])
|
||||
|
||||
voluntaryExitDomainType, exists := spec["DOMAIN_VOLUNTARY_EXIT"].(phase0.DomainType)
|
||||
if !exists {
|
||||
return nil, errors.New("failed to obtain DOMAIN_VOLUNTARY_EXIT")
|
||||
}
|
||||
copy(res.VoluntaryExitDomainType[:], voluntaryExitDomainType[:])
|
||||
|
||||
return res, nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2022 Weald Technology Trading.
|
||||
// Copyright © 2023 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
|
||||
@@ -11,7 +11,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validatorcredentialsset
|
||||
package beacon
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
@@ -20,33 +20,37 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
apiv1 "github.com/attestantio/go-eth2-client/api/v1"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type validatorInfo struct {
|
||||
type ValidatorInfo struct {
|
||||
Index phase0.ValidatorIndex
|
||||
Pubkey phase0.BLSPubKey
|
||||
State apiv1.ValidatorState
|
||||
WithdrawalCredentials []byte
|
||||
}
|
||||
|
||||
type validatorInfoJSON struct {
|
||||
Index string `json:"index"`
|
||||
Pubkey string `json:"pubkey"`
|
||||
WithdrawalCredentials string `json:"withdrawal_credentials"`
|
||||
Index string `json:"index"`
|
||||
Pubkey string `json:"pubkey"`
|
||||
State apiv1.ValidatorState `json:"state"`
|
||||
WithdrawalCredentials string `json:"withdrawal_credentials"`
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (v *validatorInfo) MarshalJSON() ([]byte, error) {
|
||||
func (v *ValidatorInfo) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&validatorInfoJSON{
|
||||
Index: fmt.Sprintf("%d", v.Index),
|
||||
Pubkey: fmt.Sprintf("%#x", v.Pubkey),
|
||||
State: v.State,
|
||||
WithdrawalCredentials: fmt.Sprintf("%#x", v.WithdrawalCredentials),
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (v *validatorInfo) UnmarshalJSON(input []byte) error {
|
||||
func (v *ValidatorInfo) UnmarshalJSON(input []byte) error {
|
||||
var data validatorInfoJSON
|
||||
if err := json.Unmarshal(input, &data); err != nil {
|
||||
return errors.Wrap(err, "invalid JSON")
|
||||
@@ -73,6 +77,11 @@ func (v *validatorInfo) UnmarshalJSON(input []byte) error {
|
||||
}
|
||||
copy(v.Pubkey[:], pubkey)
|
||||
|
||||
if data.State == apiv1.ValidatorStateUnknown {
|
||||
return errors.New("state unknown")
|
||||
}
|
||||
v.State = data.State
|
||||
|
||||
if data.WithdrawalCredentials == "" {
|
||||
return errors.New("withdrawal credentials missing")
|
||||
}
|
||||
@@ -88,7 +97,7 @@ func (v *validatorInfo) UnmarshalJSON(input []byte) error {
|
||||
}
|
||||
|
||||
// String implements the Stringer interface.
|
||||
func (v *validatorInfo) String() string {
|
||||
func (v *ValidatorInfo) String() string {
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Err: %v\n", err)
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// accountCmd represents the account command
|
||||
// accountCmd represents the account command.
|
||||
var accountCmd = &cobra.Command{
|
||||
Use: "account",
|
||||
Short: "Manage account",
|
||||
|
||||
@@ -15,6 +15,7 @@ package accountimport
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -66,7 +67,7 @@ func processFromKey(ctx context.Context, data *dataIn) (*dataOut, error) {
|
||||
}
|
||||
account, err := importer.ImportAccount(ctx, data.accountName, data.key, []byte(data.passphrase))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to import account")
|
||||
return nil, errors.Wrap(err, "failed to import wallet")
|
||||
}
|
||||
results.account = account
|
||||
|
||||
@@ -79,15 +80,25 @@ func processFromKeystore(ctx context.Context, data *dataIn) (*dataOut, error) {
|
||||
encryptor := keystorev4.New()
|
||||
|
||||
// Need to add a couple of fields to the keystore to make it compliant.
|
||||
keystoreData := fmt.Sprintf(`{"name":"Import","encryptor":"keystore",%s`, string(data.keystore[1:]))
|
||||
walletData := fmt.Sprintf(`{"wallet":{"name":"ImportTest","type":"non-deterministic","uuid":"e1526407-1dc7-4f3f-9d05-ab696f40707c","version":1},"accounts":[%s]}`, keystoreData)
|
||||
var keystore map[string]any
|
||||
if err := json.Unmarshal(data.keystore, &keystore); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal keystore")
|
||||
}
|
||||
keystore["name"] = data.accountName
|
||||
keystore["encryptor"] = "keystore"
|
||||
keystoreData, err := json.Marshal(keystore)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshal keystore")
|
||||
}
|
||||
|
||||
walletData := fmt.Sprintf(`{"wallet":{"name":"Import","type":"non-deterministic","uuid":"e1526407-1dc7-4f3f-9d05-ab696f40707c","version":1},"accounts":[%s]}`, keystoreData)
|
||||
encryptedData, err := ecodec.Encrypt([]byte(walletData), data.keystorePassphrase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
wallet, err := nd.Import(ctx, encryptedData, data.keystorePassphrase, store, encryptor)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to import wallet")
|
||||
return nil, errors.Wrap(err, "failed to import account")
|
||||
}
|
||||
|
||||
account := <-wallet.Accounts(ctx)
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
accountkey "github.com/wealdtech/ethdo/cmd/account/key"
|
||||
)
|
||||
|
||||
// accountKeyCmd represents the account key command
|
||||
// accountKeyCmd represents the account key command.
|
||||
var accountKeyCmd = &cobra.Command{
|
||||
Use: "key",
|
||||
Short: "Obtain the private key of an account.",
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// attestationCmd represents the attestation command
|
||||
// attestationCmd represents the attestation command.
|
||||
var attestationCmd = &cobra.Command{
|
||||
Use: "attestation",
|
||||
Short: "Obtain information about an Ethereum 2 attestation",
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// attesterCmd represents the attester command
|
||||
// attesterCmd represents the attester command.
|
||||
var attesterCmd = &cobra.Command{
|
||||
Use: "attester",
|
||||
Short: "Obtain information about Ethereum 2 attesters",
|
||||
|
||||
@@ -35,7 +35,6 @@ func process(ctx context.Context, data *dataIn) (*dataOut, error) {
|
||||
|
||||
data.chainTime, err = standardchaintime.New(ctx,
|
||||
standardchaintime.WithSpecProvider(data.eth2Client.(eth2client.SpecProvider)),
|
||||
standardchaintime.WithForkScheduleProvider(data.eth2Client.(eth2client.ForkScheduleProvider)),
|
||||
standardchaintime.WithGenesisTimeProvider(data.eth2Client.(eth2client.GenesisTimeProvider)),
|
||||
)
|
||||
if err != nil {
|
||||
@@ -101,7 +100,6 @@ func process(ctx context.Context, data *dataIn) (*dataOut, error) {
|
||||
if attestation.Data.Slot == duty.Slot &&
|
||||
attestation.Data.Index == duty.CommitteeIndex &&
|
||||
attestation.AggregationBits.BitAt(duty.ValidatorCommitteeIndex) {
|
||||
|
||||
headCorrect := false
|
||||
targetCorrect := false
|
||||
if data.verbose {
|
||||
@@ -139,7 +137,7 @@ func calcHeadCorrect(ctx context.Context, data *dataIn, attestation *phase0.Atte
|
||||
for {
|
||||
header, err := data.eth2Client.(eth2client.BeaconBlockHeadersProvider).BeaconBlockHeader(ctx, fmt.Sprintf("%d", slot))
|
||||
if err != nil {
|
||||
return false, nil
|
||||
return false, err
|
||||
}
|
||||
if header == nil {
|
||||
// No block.
|
||||
@@ -161,7 +159,7 @@ func calcTargetCorrect(ctx context.Context, data *dataIn, attestation *phase0.At
|
||||
for {
|
||||
header, err := data.eth2Client.(eth2client.BeaconBlockHeadersProvider).BeaconBlockHeader(ctx, fmt.Sprintf("%d", slot))
|
||||
if err != nil {
|
||||
return false, nil
|
||||
return false, err
|
||||
}
|
||||
if header == nil {
|
||||
// No block.
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// blockCmd represents the block command
|
||||
// blockCmd represents the block command.
|
||||
var blockCmd = &cobra.Command{
|
||||
Use: "block",
|
||||
Short: "Obtain information about an Ethereum 2 block",
|
||||
|
||||
@@ -266,7 +266,6 @@ func (c *command) setup(ctx context.Context) error {
|
||||
|
||||
c.chainTime, err = standardchaintime.New(ctx,
|
||||
standardchaintime.WithSpecProvider(c.eth2Client.(eth2client.SpecProvider)),
|
||||
standardchaintime.WithForkScheduleProvider(c.eth2Client.(eth2client.ForkScheduleProvider)),
|
||||
standardchaintime.WithGenesisTimeProvider(c.eth2Client.(eth2client.GenesisTimeProvider)),
|
||||
)
|
||||
if err != nil {
|
||||
@@ -364,7 +363,7 @@ func (c *command) calcHeadCorrect(ctx context.Context, attestation *phase0.Attes
|
||||
for {
|
||||
header, err := c.blockHeadersProvider.BeaconBlockHeader(ctx, fmt.Sprintf("%d", slot))
|
||||
if err != nil {
|
||||
return false, nil
|
||||
return false, err
|
||||
}
|
||||
if header == nil {
|
||||
// No block.
|
||||
@@ -393,7 +392,7 @@ func (c *command) calcTargetCorrect(ctx context.Context, attestation *phase0.Att
|
||||
for {
|
||||
header, err := c.blockHeadersProvider.BeaconBlockHeader(ctx, fmt.Sprintf("%d", slot))
|
||||
if err != nil {
|
||||
return false, nil
|
||||
return false, err
|
||||
}
|
||||
if header == nil {
|
||||
// No block.
|
||||
|
||||
@@ -690,7 +690,11 @@ func outputCapellaBlockExecutionPayload(ctx context.Context,
|
||||
res.WriteString(" State root: ")
|
||||
res.WriteString(fmt.Sprintf("%#x\n", payload.StateRoot))
|
||||
res.WriteString(" Extra data: ")
|
||||
res.WriteString(fmt.Sprintf("%#x\n", payload.ExtraData))
|
||||
if utf8.Valid(payload.ExtraData) {
|
||||
res.WriteString(fmt.Sprintf("%s\n", string(payload.ExtraData)))
|
||||
} else {
|
||||
res.WriteString(fmt.Sprintf("%#x\n", payload.ExtraData))
|
||||
}
|
||||
res.WriteString(" Logs bloom: ")
|
||||
res.WriteString(fmt.Sprintf("%#x\n", payload.LogsBloom))
|
||||
res.WriteString(" Transactions: ")
|
||||
|
||||
@@ -29,9 +29,11 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var jsonOutput bool
|
||||
var sszOutput bool
|
||||
var results *dataOut
|
||||
var (
|
||||
jsonOutput bool
|
||||
sszOutput bool
|
||||
results *dataOut
|
||||
)
|
||||
|
||||
func process(ctx context.Context, data *dataIn) (*dataOut, error) {
|
||||
if data == nil {
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// chainCmd represents the chain command
|
||||
// chainCmd represents the chain command.
|
||||
var chainCmd = &cobra.Command{
|
||||
Use: "chain",
|
||||
Short: "Obtain information about an Ethereum 2 chain",
|
||||
|
||||
@@ -75,7 +75,7 @@ func (c *command) process(ctx context.Context) error {
|
||||
|
||||
switch state.Version {
|
||||
case spec.DataVersionPhase0:
|
||||
c.slot = phase0.Slot(state.Phase0.Slot)
|
||||
c.slot = state.Phase0.Slot
|
||||
c.incumbent = state.Phase0.ETH1Data
|
||||
c.eth1DataVotes = state.Phase0.ETH1DataVotes
|
||||
case spec.DataVersionAltair:
|
||||
@@ -121,7 +121,6 @@ func (c *command) setup(ctx context.Context) error {
|
||||
|
||||
c.chainTime, err = standardchaintime.New(ctx,
|
||||
standardchaintime.WithSpecProvider(c.eth2Client.(eth2client.SpecProvider)),
|
||||
standardchaintime.WithForkScheduleProvider(c.eth2Client.(eth2client.ForkScheduleProvider)),
|
||||
standardchaintime.WithGenesisTimeProvider(c.eth2Client.(eth2client.GenesisTimeProvider)),
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -65,7 +65,6 @@ func (c *command) setup(ctx context.Context) error {
|
||||
|
||||
c.chainTime, err = standardchaintime.New(ctx,
|
||||
standardchaintime.WithSpecProvider(c.eth2Client.(eth2client.SpecProvider)),
|
||||
standardchaintime.WithForkScheduleProvider(c.eth2Client.(eth2client.ForkScheduleProvider)),
|
||||
standardchaintime.WithGenesisTimeProvider(c.eth2Client.(eth2client.GenesisTimeProvider)),
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -38,6 +38,7 @@ func (c *command) process(ctx context.Context) error {
|
||||
err := json.Unmarshal([]byte(c.data), c.item)
|
||||
if err != nil {
|
||||
c.additionalInfo = err.Error()
|
||||
//nolint:nilerr
|
||||
return nil
|
||||
}
|
||||
c.itemStructureValid = true
|
||||
@@ -124,7 +125,7 @@ func (c *command) setup(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// isAggregator returns true if the given
|
||||
// isAggregator returns true if the given.
|
||||
func (c *command) isAggregator(ctx context.Context) (bool, error) {
|
||||
// Calculate the modulo.
|
||||
specProvider, isProvider := c.eth2Client.(eth2client.SpecProvider)
|
||||
@@ -204,6 +205,7 @@ func (c *command) confirmContributionSignature(ctx context.Context) error {
|
||||
_, err := e2types.BLSSignatureFromBytes(sigBytes)
|
||||
if err != nil {
|
||||
c.additionalInfo = err.Error()
|
||||
//nolint:nilerr
|
||||
return nil
|
||||
}
|
||||
c.contributionSignatureValidFormat = true
|
||||
@@ -256,6 +258,7 @@ func (c *command) confirmContributionAndProofSignature(ctx context.Context) erro
|
||||
sig, err := e2types.BLSSignatureFromBytes(sigBytes)
|
||||
if err != nil {
|
||||
c.additionalInfo = err.Error()
|
||||
//nolint:nilerr
|
||||
return nil
|
||||
}
|
||||
c.contributionAndProofSignatureValidFormat = true
|
||||
|
||||
@@ -46,7 +46,6 @@ In quiet mode this will return 0 if the chain status can be obtained, otherwise
|
||||
|
||||
chainTime, err := standardchaintime.New(ctx,
|
||||
standardchaintime.WithGenesisTimeProvider(eth2Client.(eth2client.GenesisTimeProvider)),
|
||||
standardchaintime.WithForkScheduleProvider(eth2Client.(eth2client.ForkScheduleProvider)),
|
||||
standardchaintime.WithSpecProvider(eth2Client.(eth2client.SpecProvider)),
|
||||
)
|
||||
errCheck(err, "Failed to configure chaintime service")
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// chainVerifyCmd represents the chain verify command
|
||||
// chainVerifyCmd represents the chain verify command.
|
||||
var chainVerifyCmd = &cobra.Command{
|
||||
Use: "verify",
|
||||
Short: "Verify a beacon chain signature",
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// depositCmd represents the deposit command
|
||||
// depositCmd represents the deposit command.
|
||||
var depositCmd = &cobra.Command{
|
||||
Use: "deposit",
|
||||
Short: "Manage Ethereum 2 deposits",
|
||||
|
||||
@@ -29,12 +29,14 @@ import (
|
||||
string2eth "github.com/wealdtech/go-string2eth"
|
||||
)
|
||||
|
||||
var depositVerifyData string
|
||||
var depositVerifyWithdrawalPubKey string
|
||||
var depositVerifyWithdrawalAddress string
|
||||
var depositVerifyValidatorPubKey string
|
||||
var depositVerifyDepositAmount string
|
||||
var depositVerifyForkVersion string
|
||||
var (
|
||||
depositVerifyData string
|
||||
depositVerifyWithdrawalPubKey string
|
||||
depositVerifyWithdrawalAddress string
|
||||
depositVerifyValidatorPubKey string
|
||||
depositVerifyDepositAmount string
|
||||
depositVerifyForkVersion string
|
||||
)
|
||||
|
||||
var depositVerifyCmd = &cobra.Command{
|
||||
Use: "verify",
|
||||
@@ -45,7 +47,7 @@ var depositVerifyCmd = &cobra.Command{
|
||||
|
||||
The deposit data is compared to the supplied withdrawal account/public key, validator public key, and value to ensure they match.
|
||||
|
||||
In quiet mode this will return 0 if the the data is verified correctly, otherwise 1.`,
|
||||
In quiet mode this will return 0 if the data is verified correctly, otherwise 1.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
assert(depositVerifyData != "", "--data is required")
|
||||
var data []byte
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// epochCmd represents the epoch command
|
||||
// epochCmd represents the epoch command.
|
||||
var epochCmd = &cobra.Command{
|
||||
Use: "epoch",
|
||||
Short: "Obtain information about Ethereum 2 epochs",
|
||||
|
||||
@@ -93,6 +93,7 @@ func (c *command) activeValidators(ctx context.Context) (map[phase0.ValidatorInd
|
||||
|
||||
return activeValidators, nil
|
||||
}
|
||||
|
||||
func (c *command) processAttesterDuties(ctx context.Context) error {
|
||||
activeValidators, err := c.activeValidators(ctx)
|
||||
if err != nil {
|
||||
@@ -202,7 +203,6 @@ func (c *command) processSlots(ctx context.Context,
|
||||
Slot: beaconCommittee.Slot,
|
||||
Committee: beaconCommittee.Index,
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
slotCommittees = allCommittees[attestation.Data.Slot]
|
||||
@@ -335,7 +335,6 @@ func (c *command) setup(ctx context.Context) error {
|
||||
|
||||
c.chainTime, err = standardchaintime.New(ctx,
|
||||
standardchaintime.WithSpecProvider(c.eth2Client.(eth2client.SpecProvider)),
|
||||
standardchaintime.WithForkScheduleProvider(c.eth2Client.(eth2client.ForkScheduleProvider)),
|
||||
standardchaintime.WithGenesisTimeProvider(c.eth2Client.(eth2client.GenesisTimeProvider)),
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// errCheck checks for an error and quits if it is present
|
||||
// errCheck checks for an error and quits if it is present.
|
||||
func errCheck(err error, msg string) {
|
||||
if err != nil {
|
||||
if !quiet {
|
||||
@@ -48,14 +48,14 @@ func errCheck(err error, msg string) {
|
||||
// }
|
||||
// }
|
||||
|
||||
// assert checks a condition and quits if it is false
|
||||
// assert checks a condition and quits if it is false.
|
||||
func assert(condition bool, msg string) {
|
||||
if !condition {
|
||||
die(msg)
|
||||
}
|
||||
}
|
||||
|
||||
// die prints an error and quits
|
||||
// die prints an error and quits.
|
||||
func die(msg string) {
|
||||
if msg != "" && !quiet {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", msg)
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// exitCmd represents the exit command
|
||||
// exitCmd represents the exit command.
|
||||
var exitCmd = &cobra.Command{
|
||||
Use: "exit",
|
||||
Short: "Manage Ethereum 2 voluntary exits",
|
||||
|
||||
@@ -41,7 +41,7 @@ var exitVerifyCmd = &cobra.Command{
|
||||
|
||||
ethdo exit verify --data=exitdata.json --account=primary/current
|
||||
|
||||
In quiet mode this will return 0 if the the exit is verified correctly, otherwise 1.`,
|
||||
In quiet mode this will return 0 if the exit is verified correctly, otherwise 1.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ctx := context.Background()
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// nodeCmd represents the node command
|
||||
// nodeCmd represents the node command.
|
||||
var nodeCmd = &cobra.Command{
|
||||
Use: "node",
|
||||
Short: "Obtain information about an Ethereum 2 node",
|
||||
|
||||
@@ -24,6 +24,9 @@ import (
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
)
|
||||
|
||||
// defaultBeaconNode is used if no other connection is supplied.
|
||||
var defaultBeaconNode = "http://mainnet-consensus.attestant.io/"
|
||||
|
||||
var nodeInfoCmd = &cobra.Command{
|
||||
Use: "info",
|
||||
Short: "Obtain information about a node",
|
||||
@@ -36,7 +39,26 @@ In quiet mode this will return 0 if the node information can be obtained, otherw
|
||||
ctx := context.Background()
|
||||
|
||||
eth2Client, err := util.ConnectToBeaconNode(ctx, viper.GetString("connection"), viper.GetDuration("timeout"), viper.GetBool("allow-insecure-connections"))
|
||||
errCheck(err, "Failed to connect to Ethereum 2 beacon node")
|
||||
if err != nil {
|
||||
if viper.GetString("connection") != "" {
|
||||
// The user provided a connection, so don't second-guess them by using a different node.
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// The user did not provide a connection, so attempt to use the default node.
|
||||
if viper.GetBool("debug") {
|
||||
fmt.Fprintf(os.Stderr, "No node connection, attempting to use %s\n", defaultBeaconNode)
|
||||
}
|
||||
eth2Client, err = util.ConnectToBeaconNode(ctx, defaultBeaconNode, viper.GetDuration("timeout"), viper.GetBool("allow-insecure-connections"))
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
return
|
||||
}
|
||||
if !viper.GetBool("quiet") {
|
||||
fmt.Fprintf(os.Stderr, "No connection supplied; using mainnet public access endpoint\n")
|
||||
}
|
||||
}
|
||||
|
||||
if quiet {
|
||||
os.Exit(_exitSuccess)
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// proposerCmd represents the proposer command
|
||||
// proposerCmd represents the proposer command.
|
||||
var proposerCmd = &cobra.Command{
|
||||
Use: "proposer",
|
||||
Short: "Obtain information about Ethereum 2 proposers",
|
||||
|
||||
@@ -53,7 +53,6 @@ func (c *command) setup(ctx context.Context) error {
|
||||
|
||||
c.chainTime, err = standardchaintime.New(ctx,
|
||||
standardchaintime.WithSpecProvider(c.eth2Client.(eth2client.SpecProvider)),
|
||||
standardchaintime.WithForkScheduleProvider(c.eth2Client.(eth2client.ForkScheduleProvider)),
|
||||
standardchaintime.WithGenesisTimeProvider(c.eth2Client.(eth2client.GenesisTimeProvider)),
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
21
cmd/root.go
21
cmd/root.go
@@ -33,12 +33,14 @@ import (
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
var cfgFile string
|
||||
var quiet bool
|
||||
var verbose bool
|
||||
var debug bool
|
||||
var (
|
||||
cfgFile string
|
||||
quiet bool
|
||||
verbose bool
|
||||
debug bool
|
||||
)
|
||||
|
||||
// RootCmd represents the base command when called without any subcommands
|
||||
// RootCmd represents the base command when called without any subcommands.
|
||||
var RootCmd = &cobra.Command{
|
||||
Use: "ethdo",
|
||||
Short: "Ethereum 2 CLI",
|
||||
@@ -244,7 +246,7 @@ func init() {
|
||||
if err := viper.BindPFlag("debug", RootCmd.PersistentFlags().Lookup("debug")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
RootCmd.PersistentFlags().String("connection", "", "URL to an Ethereum 2 node's RET API endpoint")
|
||||
RootCmd.PersistentFlags().String("connection", "", "URL to an Ethereum 2 node's REST API endpoint")
|
||||
if err := viper.BindPFlag("connection", RootCmd.PersistentFlags().Lookup("connection")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -342,7 +344,12 @@ func walletFromPath(ctx context.Context, path string) (e2wtypes.Wallet, error) {
|
||||
return nil, errors.Wrap(err, "failed to parse remote servers")
|
||||
}
|
||||
|
||||
return dirk.OpenWallet(ctx, walletName, credentials, endpoints)
|
||||
return dirk.Open(ctx,
|
||||
dirk.WithName(walletName),
|
||||
dirk.WithCredentials(credentials),
|
||||
dirk.WithEndpoints(endpoints),
|
||||
dirk.WithTimeout(viper.GetDuration("timeout")),
|
||||
)
|
||||
}
|
||||
wallet, err := e2wallet.OpenWallet(walletName)
|
||||
if err != nil {
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// signatureCmd represents the signature command
|
||||
// signatureCmd represents the signature command.
|
||||
var signatureCmd = &cobra.Command{
|
||||
Use: "signature",
|
||||
Aliases: []string{"sig"},
|
||||
@@ -31,8 +31,10 @@ func init() {
|
||||
RootCmd.AddCommand(signatureCmd)
|
||||
}
|
||||
|
||||
var dataFlag *pflag.Flag
|
||||
var domainFlag *pflag.Flag
|
||||
var (
|
||||
dataFlag *pflag.Flag
|
||||
domainFlag *pflag.Flag
|
||||
)
|
||||
|
||||
func signatureFlags(cmd *cobra.Command) {
|
||||
if dataFlag == nil {
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
|
||||
var signatureAggregateSignatures []string
|
||||
|
||||
// signatureAggregateCmd represents the signature aggregate command
|
||||
// signatureAggregateCmd represents the signature aggregate command.
|
||||
var signatureAggregateCmd = &cobra.Command{
|
||||
Use: "aggregate",
|
||||
Short: "Aggregate signatures",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2017-2020 Weald Technology Trading
|
||||
// Copyright © 2017-2023 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
|
||||
@@ -24,9 +24,10 @@ import (
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
"github.com/wealdtech/go-bytesutil"
|
||||
e2types "github.com/wealdtech/go-eth2-types/v2"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
// signatureSignCmd represents the signature sign command
|
||||
// signatureSignCmd represents the signature sign command.
|
||||
var signatureSignCmd = &cobra.Command{
|
||||
Use: "sign",
|
||||
Short: "Sign a 32-byte piece of data",
|
||||
@@ -52,14 +53,20 @@ In quiet mode this will return 0 if the data can be signed, otherwise 1.`,
|
||||
}
|
||||
outputIf(debug, fmt.Sprintf("Domain is %#x", domain))
|
||||
|
||||
assert(viper.GetString("account") != "", "--account is required")
|
||||
_, account, err := walletAndAccountFromInput(ctx)
|
||||
var account e2wtypes.Account
|
||||
switch {
|
||||
case viper.GetString("account") != "":
|
||||
account, err = util.ParseAccount(ctx, viper.GetString("account"), util.GetPassphrases(), true)
|
||||
case viper.GetString("private-key") != "":
|
||||
account, err = util.ParseAccount(ctx, viper.GetString("private-key"), nil, true)
|
||||
}
|
||||
errCheck(err, "Failed to obtain account")
|
||||
|
||||
var specDomain spec.Domain
|
||||
copy(specDomain[:], domain)
|
||||
var fixedSizeData [32]byte
|
||||
copy(fixedSizeData[:], data)
|
||||
fmt.Printf("Signing %#x with domain %#x by public key %#x\n", fixedSizeData, specDomain, account.PublicKey().Marshal())
|
||||
signature, err := util.SignRoot(account, fixedSizeData, specDomain)
|
||||
errCheck(err, "Failed to sign")
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2017-2020 Weald Technology Trading
|
||||
// Copyright © 2017-2023 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
|
||||
@@ -15,13 +15,10 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
spec "github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
@@ -30,10 +27,12 @@ import (
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
var signatureVerifySignature string
|
||||
var signatureVerifySigner string
|
||||
var (
|
||||
signatureVerifySignature string
|
||||
signatureVerifySigner string
|
||||
)
|
||||
|
||||
// signatureVerifyCmd represents the signature verify command
|
||||
// signatureVerifyCmd represents the signature verify command.
|
||||
var signatureVerifyCmd = &cobra.Command{
|
||||
Use: "verify",
|
||||
Short: "Verify signed data",
|
||||
@@ -43,6 +42,9 @@ var signatureVerifyCmd = &cobra.Command{
|
||||
|
||||
In quiet mode this will return 0 if the data can be signed, otherwise 1.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||
defer cancel()
|
||||
|
||||
assert(viper.GetString("signature-data") != "", "--data is required")
|
||||
data, err := bytesutil.FromHexString(viper.GetString("signature-data"))
|
||||
errCheck(err, "Failed to parse data")
|
||||
@@ -61,7 +63,15 @@ In quiet mode this will return 0 if the data can be signed, otherwise 1.`,
|
||||
assert(len(domain) == 32, "Domain data invalid")
|
||||
}
|
||||
|
||||
account, err := signatureVerifyAccount()
|
||||
var account e2wtypes.Account
|
||||
switch {
|
||||
case viper.GetString("account") != "":
|
||||
account, err = util.ParseAccount(ctx, viper.GetString("account"), nil, false)
|
||||
case viper.GetString("private-key") != "":
|
||||
account, err = util.ParseAccount(ctx, viper.GetString("private-key"), nil, false)
|
||||
case viper.GetString("public-key") != "":
|
||||
account, err = util.ParseAccount(ctx, viper.GetString("public-key"), nil, false)
|
||||
}
|
||||
errCheck(err, "Failed to obtain account")
|
||||
outputIf(debug, fmt.Sprintf("Public key is %#x", account.PublicKey().Marshal()))
|
||||
|
||||
@@ -78,29 +88,6 @@ In quiet mode this will return 0 if the data can be signed, otherwise 1.`,
|
||||
},
|
||||
}
|
||||
|
||||
// signatureVerifyAccount obtains the account for the signature verify command.
|
||||
func signatureVerifyAccount() (e2wtypes.Account, error) {
|
||||
var account e2wtypes.Account
|
||||
var err error
|
||||
if viper.GetString("account") != "" {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||
defer cancel()
|
||||
_, account, err = walletAndAccountFromPath(ctx, viper.GetString("account"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain account")
|
||||
}
|
||||
} else {
|
||||
pubKeyBytes, err := hex.DecodeString(strings.TrimPrefix(signatureVerifySigner, "0x"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("failed to decode public key %s", signatureVerifySigner))
|
||||
}
|
||||
account, err = util.NewScratchAccount(nil, pubKeyBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("invalid public key %s", signatureVerifySigner))
|
||||
}
|
||||
}
|
||||
return account, nil
|
||||
}
|
||||
func init() {
|
||||
signatureCmd.AddCommand(signatureVerifyCmd)
|
||||
signatureFlags(signatureVerifyCmd)
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// slotCmd represents the slot command
|
||||
// slotCmd represents the slot command.
|
||||
var slotCmd = &cobra.Command{
|
||||
Use: "slot",
|
||||
Short: "Obtain information about an Ethereum 2 slot",
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// synccommitteeCmd represents the synccommittee command
|
||||
// synccommitteeCmd represents the synccommittee command.
|
||||
var synccommitteeCmd = &cobra.Command{
|
||||
Use: "synccommittee",
|
||||
Short: "Obtain information about Ethereum 2 sync committees",
|
||||
|
||||
@@ -114,7 +114,6 @@ func (c *command) setup(ctx context.Context) error {
|
||||
|
||||
c.chainTime, err = standardchaintime.New(ctx,
|
||||
standardchaintime.WithSpecProvider(c.eth2Client.(eth2client.SpecProvider)),
|
||||
standardchaintime.WithForkScheduleProvider(c.eth2Client.(eth2client.ForkScheduleProvider)),
|
||||
standardchaintime.WithGenesisTimeProvider(c.eth2Client.(eth2client.GenesisTimeProvider)),
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -59,7 +59,6 @@ func input(ctx context.Context) (*dataIn, error) {
|
||||
// Chain time.
|
||||
data.chainTime, err = standardchaintime.New(ctx,
|
||||
standardchaintime.WithGenesisTimeProvider(data.eth2Client.(eth2client.GenesisTimeProvider)),
|
||||
standardchaintime.WithForkScheduleProvider(data.eth2Client.(eth2client.ForkScheduleProvider)),
|
||||
standardchaintime.WithSpecProvider(data.eth2Client.(eth2client.SpecProvider)),
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -37,7 +37,6 @@ func TestProcess(t *testing.T) {
|
||||
|
||||
chainTime, err := standardchaintime.New(context.Background(),
|
||||
standardchaintime.WithGenesisTimeProvider(eth2Client.(eth2client.GenesisTimeProvider)),
|
||||
standardchaintime.WithForkScheduleProvider(eth2Client.(eth2client.ForkScheduleProvider)),
|
||||
standardchaintime.WithSpecProvider(eth2Client.(eth2client.SpecProvider)),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// validatorCmd represents the validator command
|
||||
// validatorCmd represents the validator command.
|
||||
var validatorCmd = &cobra.Command{
|
||||
Use: "validator",
|
||||
Short: "Manage Ethereum 2 validators",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2022 Weald Technology Trading.
|
||||
// Copyright © 2022, 2023 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,114 +14,92 @@
|
||||
package validatorcredentialsset
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"os"
|
||||
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/wealdtech/ethdo/beacon"
|
||||
)
|
||||
|
||||
type chainInfo struct {
|
||||
Version uint64
|
||||
Validators []*validatorInfo
|
||||
GenesisValidatorsRoot phase0.Root
|
||||
Epoch phase0.Epoch
|
||||
ForkVersion phase0.Version
|
||||
Domain phase0.Domain
|
||||
}
|
||||
|
||||
type chainInfoJSON struct {
|
||||
Version string `json:"version"`
|
||||
Validators []*validatorInfo `json:"validators"`
|
||||
GenesisValidatorsRoot string `json:"genesis_validators_root"`
|
||||
Epoch string `json:"epoch"`
|
||||
ForkVersion string `json:"fork_version"`
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (v *chainInfo) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&chainInfoJSON{
|
||||
Version: fmt.Sprintf("%d", v.Version),
|
||||
Validators: v.Validators,
|
||||
GenesisValidatorsRoot: fmt.Sprintf("%#x", v.GenesisValidatorsRoot),
|
||||
Epoch: fmt.Sprintf("%d", v.Epoch),
|
||||
ForkVersion: fmt.Sprintf("%#x", v.ForkVersion),
|
||||
Domain: fmt.Sprintf("%#x", v.Domain),
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (v *chainInfo) UnmarshalJSON(input []byte) error {
|
||||
var data chainInfoJSON
|
||||
if err := json.Unmarshal(input, &data); err != nil {
|
||||
return errors.Wrap(err, "invalid JSON")
|
||||
}
|
||||
|
||||
if data.Version == "" {
|
||||
// Default to 1.
|
||||
v.Version = 1
|
||||
} else {
|
||||
version, err := strconv.ParseUint(data.Version, 10, 64)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "version invalid")
|
||||
// obtainChainInfo obtains the chain information required to create a withdrawal credentials change operation.
|
||||
func (c *command) obtainChainInfo(ctx context.Context) error {
|
||||
var err error
|
||||
// Use the offline preparation file if present (and we haven't been asked to recreate it).
|
||||
if !c.prepareOffline {
|
||||
if err = c.obtainChainInfoFromFile(ctx); err == nil {
|
||||
return nil
|
||||
}
|
||||
v.Version = version
|
||||
}
|
||||
|
||||
if len(data.Validators) == 0 {
|
||||
return errors.New("validators missing")
|
||||
}
|
||||
v.Validators = data.Validators
|
||||
|
||||
if data.GenesisValidatorsRoot == "" {
|
||||
return errors.New("genesis validators root missing")
|
||||
if c.offline {
|
||||
// If we are here it means that we are offline without chain information, and cannot continue.
|
||||
return fmt.Errorf("failed to obtain offline preparation file: %w", err)
|
||||
}
|
||||
|
||||
genesisValidatorsRootBytes, err := hex.DecodeString(strings.TrimPrefix(data.GenesisValidatorsRoot, "0x"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "genesis validators root invalid")
|
||||
if err := c.obtainChainInfoFromNode(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// obtainChainInfoFromFile obtains chain information from a pre-generated file.
|
||||
func (c *command) obtainChainInfoFromFile(_ context.Context) error {
|
||||
_, err := os.Stat(offlinePreparationFilename)
|
||||
if err != nil {
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "Failed to read offline preparation file: %v\n", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "%s found; loading chain state\n", offlinePreparationFilename)
|
||||
}
|
||||
data, err := os.ReadFile(offlinePreparationFilename)
|
||||
if err != nil {
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "failed to load offline preparation file: %v\n", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
c.chainInfo = &beacon.ChainInfo{}
|
||||
if err := json.Unmarshal(data, c.chainInfo); err != nil {
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "offline preparation file invalid: %v\n", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// obtainChainInfoFromNode obtains chain info from a beacon node.
|
||||
func (c *command) obtainChainInfoFromNode(ctx context.Context) error {
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "Populating chain info from beacon node\n")
|
||||
}
|
||||
|
||||
var err error
|
||||
c.chainInfo, err = beacon.ObtainChainInfoFromNode(ctx, c.consensusClient, c.chainTime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeChainInfoToFile prepares for an offline run of this command by dumping
|
||||
// the chain information to a file.
|
||||
func (c *command) writeChainInfoToFile(_ context.Context) error {
|
||||
data, err := json.Marshal(c.chainInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.WriteFile(offlinePreparationFilename, data, 0o600); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(genesisValidatorsRootBytes) != phase0.RootLength {
|
||||
return errors.New("genesis validators root incorrect length")
|
||||
}
|
||||
copy(v.GenesisValidatorsRoot[:], genesisValidatorsRootBytes)
|
||||
|
||||
if data.Epoch == "" {
|
||||
return errors.New("epoch missing")
|
||||
}
|
||||
epoch, err := strconv.ParseUint(data.Epoch, 10, 64)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "epoch invalid")
|
||||
}
|
||||
v.Epoch = phase0.Epoch(epoch)
|
||||
|
||||
if data.ForkVersion == "" {
|
||||
return errors.New("fork version missing")
|
||||
}
|
||||
forkVersionBytes, err := hex.DecodeString(strings.TrimPrefix(data.ForkVersion, "0x"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fork version invalid")
|
||||
}
|
||||
if len(forkVersionBytes) != phase0.ForkVersionLength {
|
||||
return errors.New("fork version incorrect length")
|
||||
}
|
||||
copy(v.ForkVersion[:], forkVersionBytes)
|
||||
|
||||
if data.Domain == "" {
|
||||
return errors.New("domain missing")
|
||||
}
|
||||
domainBytes, err := hex.DecodeString(strings.TrimPrefix(data.Domain, "0x"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "domain invalid")
|
||||
}
|
||||
if len(domainBytes) != phase0.DomainLength {
|
||||
return errors.New("domain incorrect length")
|
||||
}
|
||||
copy(v.Domain[:], domainBytes)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -20,8 +20,10 @@ import (
|
||||
consensusclient "github.com/attestantio/go-eth2-client"
|
||||
"github.com/attestantio/go-eth2-client/spec/bellatrix"
|
||||
capella "github.com/attestantio/go-eth2-client/spec/capella"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/wealdtech/ethdo/beacon"
|
||||
"github.com/wealdtech/ethdo/services/chaintime"
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
)
|
||||
@@ -35,6 +37,7 @@ type command struct {
|
||||
|
||||
// Input.
|
||||
account string
|
||||
withdrawalAccount string
|
||||
passphrases []string
|
||||
mnemonic string
|
||||
path string
|
||||
@@ -44,6 +47,7 @@ type command struct {
|
||||
forkVersion string
|
||||
genesisValidatorsRoot string
|
||||
prepareOffline bool
|
||||
signedOperationsInput string
|
||||
|
||||
// Beacon node connection.
|
||||
timeout time.Duration
|
||||
@@ -52,7 +56,8 @@ type command struct {
|
||||
|
||||
// Information required to generate the operations.
|
||||
withdrawalAddress bellatrix.ExecutionAddress
|
||||
chainInfo *chainInfo
|
||||
chainInfo *beacon.ChainInfo
|
||||
domain phase0.Domain
|
||||
|
||||
// Processing.
|
||||
consensusClient consensusclient.Service
|
||||
@@ -62,7 +67,7 @@ type command struct {
|
||||
signedOperations []*capella.SignedBLSToExecutionChange
|
||||
}
|
||||
|
||||
func newCommand(ctx context.Context) (*command, error) {
|
||||
func newCommand(_ context.Context) (*command, error) {
|
||||
c := &command{
|
||||
quiet: viper.GetBool("quiet"),
|
||||
verbose: viper.GetBool("verbose"),
|
||||
@@ -74,10 +79,12 @@ func newCommand(ctx context.Context) (*command, error) {
|
||||
allowInsecureConnections: viper.GetBool("allow-insecure-connections"),
|
||||
prepareOffline: viper.GetBool("prepare-offline"),
|
||||
account: viper.GetString("account"),
|
||||
withdrawalAccount: viper.GetString("withdrawal-account"),
|
||||
passphrases: util.GetPassphrases(),
|
||||
mnemonic: viper.GetString("mnemonic"),
|
||||
path: viper.GetString("path"),
|
||||
privateKey: viper.GetString("private-key"),
|
||||
signedOperationsInput: viper.GetString("signed-operations"),
|
||||
|
||||
validator: viper.GetString("validator"),
|
||||
withdrawalAddressStr: viper.GetString("withdrawal-address"),
|
||||
@@ -96,8 +103,8 @@ func newCommand(ctx context.Context) (*command, error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
if c.account != "" && len(c.passphrases) == 0 {
|
||||
return nil, errors.New("passphrase required with account")
|
||||
if c.withdrawalAccount != "" && len(c.passphrases) == 0 {
|
||||
return nil, errors.New("passphrase required with withdrawal-account")
|
||||
}
|
||||
|
||||
return c, nil
|
||||
|
||||
@@ -22,17 +22,25 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (c *command) output(ctx context.Context) (string, error) {
|
||||
//nolint:unparam
|
||||
func (c *command) output(_ context.Context) (string, error) {
|
||||
if c.quiet {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if c.prepareOffline {
|
||||
return fmt.Sprintf("%s generated", offlinePreparationFilename), nil
|
||||
}
|
||||
|
||||
if c.json || c.offline {
|
||||
data, err := json.Marshal(c.signedOperations)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to marshal signed operations")
|
||||
}
|
||||
if err := os.WriteFile(changeOperationsFilename, data, 0600); err != nil {
|
||||
if c.json {
|
||||
return string(data), nil
|
||||
}
|
||||
if err := os.WriteFile(changeOperationsFilename, data, 0o600); err != nil {
|
||||
return "", errors.Wrap(err, fmt.Sprintf("failed to write %s", changeOperationsFilename))
|
||||
}
|
||||
return "", nil
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
779
cmd/validator/credentials/set/process_internal_test.go
Normal file
779
cmd/validator/credentials/set/process_internal_test.go
Normal file
@@ -0,0 +1,779 @@
|
||||
// Copyright © 2022 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 validatorcredentialsset
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/attestantio/go-eth2-client/spec/bellatrix"
|
||||
capella "github.com/attestantio/go-eth2-client/spec/capella"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/wealdtech/ethdo/beacon"
|
||||
e2types "github.com/wealdtech/go-eth2-types/v2"
|
||||
)
|
||||
|
||||
func TestGenerateOperationsFromPrivateKey(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
require.NoError(t, e2types.InitBLS())
|
||||
|
||||
chainInfo := &beacon.ChainInfo{
|
||||
Version: 1,
|
||||
Validators: []*beacon.ValidatorInfo{
|
||||
{
|
||||
Index: 0,
|
||||
Pubkey: phase0.BLSPubKey{0xb3, 0x84, 0xf7, 0x67, 0xd9, 0x64, 0xe1, 0x00, 0xc8, 0xa9, 0xb2, 0x10, 0x18, 0xd0, 0x8c, 0x25, 0xff, 0xeb, 0xae, 0x26, 0x8b, 0x3a, 0xb6, 0xd6, 0x10, 0x35, 0x38, 0x97, 0x54, 0x19, 0x71, 0x72, 0x6d, 0xbf, 0xc3, 0xc7, 0x46, 0x38, 0x84, 0xc6, 0x8a, 0x53, 0x15, 0x15, 0xaa, 0xb9, 0x4c, 0x87},
|
||||
WithdrawalCredentials: []byte{0x00, 0x8b, 0xa1, 0xcc, 0x4b, 0x09, 0x1b, 0x91, 0xc1, 0x20, 0x2b, 0xba, 0x3f, 0x50, 0x80, 0x75, 0xd6, 0xff, 0x56, 0x5c, 0x77, 0xe5, 0x59, 0xf0, 0x80, 0x3c, 0x07, 0x92, 0xe0, 0x30, 0x2b, 0xf1},
|
||||
},
|
||||
{
|
||||
Index: 1,
|
||||
Pubkey: phase0.BLSPubKey{0xb3, 0xd8, 0x9e, 0x2f, 0x29, 0xc7, 0x12, 0xc6, 0xa9, 0xf8, 0xe5, 0xa2, 0x69, 0xb9, 0x76, 0x17, 0xc4, 0xa9, 0x4d, 0xd6, 0xf6, 0x66, 0x2a, 0xb3, 0xb0, 0x7c, 0xe9, 0xe5, 0x43, 0x45, 0x73, 0xf1, 0x5b, 0x5c, 0x98, 0x8c, 0xd1, 0x4b, 0xbd, 0x58, 0x04, 0xf7, 0x71, 0x56, 0xa8, 0xaf, 0x1c, 0xfa},
|
||||
WithdrawalCredentials: []byte{0x00, 0x78, 0x6c, 0xb0, 0x2e, 0xd2, 0x8e, 0x5f, 0xbb, 0x1f, 0x7f, 0x9e, 0x93, 0x1a, 0x2b, 0x72, 0x69, 0x29, 0x06, 0xe6, 0xb1, 0x2c, 0xe4, 0x64, 0x39, 0x75, 0xe3, 0x2b, 0x51, 0x76, 0x91, 0xf2},
|
||||
},
|
||||
{
|
||||
Index: 2,
|
||||
Pubkey: phase0.BLSPubKey{0xaf, 0x9c, 0xe4, 0x4f, 0x50, 0x14, 0x8d, 0xb4, 0x12, 0x19, 0x4a, 0xf0, 0xba, 0xf0, 0xba, 0xb3, 0x6b, 0xd5, 0xc3, 0xe0, 0xc4, 0x93, 0x89, 0x11, 0xa4, 0xe5, 0x02, 0xe3, 0x98, 0xb5, 0x9e, 0x5c, 0xca, 0x7c, 0x78, 0xe3, 0xfe, 0x03, 0x41, 0x95, 0x47, 0x88, 0x79, 0xee, 0xb2, 0x3d, 0xb0, 0xa6},
|
||||
WithdrawalCredentials: []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x93, 0x1a, 0x2b, 0x72, 0x69, 0x29, 0x06, 0xe6, 0xb1, 0x2c, 0xe4, 0x64, 0x39, 0x75, 0xe3, 0x2b, 0x51, 0x76, 0x91, 0xf2},
|
||||
},
|
||||
{
|
||||
Index: 3,
|
||||
Pubkey: phase0.BLSPubKey{0x86, 0xd3, 0x30, 0xaf, 0x51, 0xfa, 0x59, 0x3f, 0xa9, 0xf9, 0x3e, 0xdb, 0x9d, 0x16, 0x64, 0x01, 0x86, 0xbe, 0x2e, 0x93, 0xea, 0x94, 0xd2, 0x59, 0x78, 0x1e, 0x1e, 0xb3, 0x4d, 0xeb, 0x84, 0x4c, 0x39, 0x68, 0xd7, 0x5e, 0xa9, 0x1d, 0x19, 0xf1, 0x59, 0xdb, 0xd0, 0x52, 0x3c, 0x6c, 0x5b, 0xa5},
|
||||
WithdrawalCredentials: []byte{0x00, 0x81, 0x68, 0x45, 0x6b, 0x6d, 0x9a, 0x32, 0x83, 0x93, 0x1f, 0xea, 0x52, 0x10, 0xda, 0x12, 0x2d, 0x1e, 0x65, 0xe8, 0xed, 0x50, 0xb8, 0xe8, 0xf5, 0x91, 0x11, 0x83, 0xb0, 0x2f, 0xd1, 0x25},
|
||||
},
|
||||
},
|
||||
GenesisValidatorsRoot: phase0.Root{},
|
||||
Epoch: 1,
|
||||
CurrentForkVersion: phase0.Version{},
|
||||
}
|
||||
validators := make(map[string]*beacon.ValidatorInfo, len(chainInfo.Validators))
|
||||
for i := range chainInfo.Validators {
|
||||
validators[fmt.Sprintf("%#x", chainInfo.Validators[i].Pubkey)] = chainInfo.Validators[i]
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
command *command
|
||||
expected []*capella.SignedBLSToExecutionChange
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "WithdrawalAddressNotHexBadChar",
|
||||
command: &command{
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0xhc1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
privateKey: "0x67775f030068b4610d6e1bd04948f547305b2502423fcece4c1091d065b44638",
|
||||
},
|
||||
err: "invalid withdrawal address: failed to obtain execution address: encoding/hex: invalid byte: U+0068 'h'",
|
||||
},
|
||||
{
|
||||
name: "WithdrawalAddressNo0xPrefix",
|
||||
command: &command{
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
privateKey: "0x67775f030068b4610d6e1bd04948f547305b2502423fcece4c1091d065b44638",
|
||||
},
|
||||
err: "invalid withdrawal address: withdrawal address 8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15 does not contain a 0x prefix",
|
||||
},
|
||||
{
|
||||
name: "ValidatorDoesNotExistInChain",
|
||||
command: &command{
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
privateKey: "0x67775f030068b4610d6e1bd04948f547305b2502423fcece4c1091d065b44635",
|
||||
},
|
||||
err: "no validator found with withdrawal credentials 0x00afa1b7f669e09ba5a57ffdd6b140a4c30bc897202d6a8c14d694e361eeb5d3",
|
||||
},
|
||||
{
|
||||
name: "InvalidKey",
|
||||
command: &command{
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
privateKey: "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
},
|
||||
err: "failed to create account from private key: invalid private key: err blsSecretKeyDeserialize ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
},
|
||||
{
|
||||
name: "PrivateKeyAddressBadChar",
|
||||
command: &command{
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
privateKey: "0xh7775f030068b4610d6e1bd04948f547305b2502423fcece4c1091d065b44638",
|
||||
},
|
||||
err: "failed to parse account key: encoding/hex: invalid byte: U+0068 'h'",
|
||||
},
|
||||
{
|
||||
name: "PrivateKeyAddressNo0xPrefix",
|
||||
command: &command{
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
privateKey: "67775f030068b4610d6e1bd04948f547305b2502423fcece4c1091d065b44638",
|
||||
},
|
||||
err: "account key must be a hex string",
|
||||
},
|
||||
{
|
||||
name: "PrivateKeyAddressWrongLength",
|
||||
command: &command{
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
privateKey: "0x775f030068b4610d6e1bd04948f547305b2502423fcece4c1091d065b44638",
|
||||
},
|
||||
err: "account key must be 32 bytes",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
command: &command{
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
privateKey: "0x67775f030068b4610d6e1bd04948f547305b2502423fcece4c1091d065b44638",
|
||||
},
|
||||
expected: []*capella.SignedBLSToExecutionChange{
|
||||
{
|
||||
Message: &capella.BLSToExecutionChange{
|
||||
ValidatorIndex: 3,
|
||||
FromBLSPubkey: phase0.BLSPubKey{0x86, 0x71, 0x0a, 0xbb, 0x44, 0xb6, 0xcd, 0xa6, 0x66, 0x57, 0x7b, 0xbb, 0x25, 0x5e, 0x16, 0xd9, 0x8b, 0xf2, 0x52, 0x51, 0x76, 0x22, 0x3f, 0x35, 0x35, 0xc7, 0xdf, 0xf8, 0xe7, 0x0b, 0x3b, 0xc8, 0x92, 0xbb, 0x36, 0x11, 0x33, 0x95, 0x2b, 0x03, 0xd2, 0xb0, 0x78, 0xcd, 0x07, 0x18, 0xca, 0xf3},
|
||||
ToExecutionAddress: bellatrix.ExecutionAddress{0x8c, 0x1f, 0xf9, 0x78, 0x03, 0x6f, 0x2e, 0x9d, 0x7c, 0xc3, 0x82, 0xef, 0xf7, 0xb4, 0xc8, 0xc5, 0x3c, 0x22, 0xac, 0x15},
|
||||
},
|
||||
Signature: phase0.BLSSignature{0x8d, 0x92, 0xb9, 0x1c, 0x5d, 0xfd, 0x98, 0xc7, 0x98, 0xfc, 0x94, 0xe1, 0xe6, 0x69, 0xf3, 0xaa, 0xae, 0x72, 0xb2, 0x36, 0x47, 0xde, 0x88, 0x54, 0xea, 0x16, 0x74, 0x7f, 0xfe, 0xf0, 0x4d, 0x46, 0x5c, 0x07, 0x56, 0x34, 0x03, 0x30, 0x2f, 0xbc, 0x26, 0xa2, 0x6d, 0xec, 0x10, 0x20, 0xe7, 0x67, 0x10, 0xb0, 0x4a, 0x7e, 0x4e, 0x25, 0x89, 0x7e, 0x87, 0x88, 0xda, 0xaf, 0x2b, 0xb5, 0xb7, 0x73, 0x25, 0x64, 0x80, 0xc1, 0xba, 0xf3, 0x1d, 0x33, 0x8f, 0x17, 0xa5, 0x35, 0x74, 0x80, 0xf3, 0x37, 0x0e, 0xea, 0x19, 0x15, 0xd5, 0x69, 0x7e, 0xf6, 0x68, 0xaa, 0x9c, 0x3d, 0x47, 0x19, 0x75, 0xfc},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
err := test.command.generateOperationsFromPrivateKey(ctx)
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.expected, test.command.signedOperations)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateOperationsFromMnemonic(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
require.NoError(t, e2types.InitBLS())
|
||||
|
||||
chainInfo := &beacon.ChainInfo{
|
||||
Version: 1,
|
||||
Validators: []*beacon.ValidatorInfo{
|
||||
{
|
||||
Index: 0,
|
||||
Pubkey: phase0.BLSPubKey{0xb3, 0x84, 0xf7, 0x67, 0xd9, 0x64, 0xe1, 0x00, 0xc8, 0xa9, 0xb2, 0x10, 0x18, 0xd0, 0x8c, 0x25, 0xff, 0xeb, 0xae, 0x26, 0x8b, 0x3a, 0xb6, 0xd6, 0x10, 0x35, 0x38, 0x97, 0x54, 0x19, 0x71, 0x72, 0x6d, 0xbf, 0xc3, 0xc7, 0x46, 0x38, 0x84, 0xc6, 0x8a, 0x53, 0x15, 0x15, 0xaa, 0xb9, 0x4c, 0x87},
|
||||
WithdrawalCredentials: []byte{0x00, 0x8b, 0xa1, 0xcc, 0x4b, 0x09, 0x1b, 0x91, 0xc1, 0x20, 0x2b, 0xba, 0x3f, 0x50, 0x80, 0x75, 0xd6, 0xff, 0x56, 0x5c, 0x77, 0xe5, 0x59, 0xf0, 0x80, 0x3c, 0x07, 0x92, 0xe0, 0x30, 0x2b, 0xf1},
|
||||
},
|
||||
{
|
||||
Index: 1,
|
||||
Pubkey: phase0.BLSPubKey{0xb4, 0xd8, 0x9e, 0x2f, 0x29, 0xc7, 0x12, 0xc6, 0xa9, 0xf8, 0xe5, 0xa2, 0x69, 0xb9, 0x76, 0x17, 0xc4, 0xa9, 0x4d, 0xd6, 0xf6, 0x66, 0x2a, 0xb3, 0xb0, 0x7c, 0xe9, 0xe5, 0x43, 0x45, 0x73, 0xf1, 0x5b, 0x5c, 0x98, 0x8c, 0xd1, 0x4b, 0xbd, 0x58, 0x04, 0xf7, 0x71, 0x56, 0xa8, 0xaf, 0x1c, 0xfa},
|
||||
WithdrawalCredentials: []byte{0x00, 0x78, 0x6c, 0xb0, 0x2e, 0xd2, 0x8e, 0x5f, 0xbb, 0x1f, 0x7f, 0x9e, 0x93, 0x1a, 0x2b, 0x72, 0x69, 0x29, 0x06, 0xe6, 0xb1, 0x2c, 0xe4, 0x64, 0x39, 0x75, 0xe3, 0x2b, 0x51, 0x76, 0x91, 0xf2},
|
||||
},
|
||||
},
|
||||
GenesisValidatorsRoot: phase0.Root{},
|
||||
Epoch: 1,
|
||||
CurrentForkVersion: phase0.Version{},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
command *command
|
||||
expected []*capella.SignedBLSToExecutionChange
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "MnemonicInvalid",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
},
|
||||
err: "mnemonic is invalid",
|
||||
},
|
||||
{
|
||||
name: "NoWithdrawalAddressProvided",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
},
|
||||
err: "failed to generate operation from seed and path: invalid withdrawal address: no withdrawal address provided",
|
||||
},
|
||||
{
|
||||
name: "NoValidatorFound",
|
||||
command: &command{
|
||||
mnemonic: "struggle kangaroo horn sniff cradle soft ethics thunder cycle illegal flock unaware dynamic cinnamon play enforce card tennis inform parent surprise bring relax tail",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
},
|
||||
err: "failed to find validators using the provided mnemonic: searched 1024 indices without finding a validator",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
},
|
||||
expected: []*capella.SignedBLSToExecutionChange{
|
||||
{
|
||||
Message: &capella.BLSToExecutionChange{
|
||||
ValidatorIndex: 0,
|
||||
FromBLSPubkey: phase0.BLSPubKey{0x99, 0xb1, 0xf1, 0xd8, 0x4d, 0x76, 0x18, 0x54, 0x66, 0xd8, 0x6c, 0x34, 0xbd, 0xe1, 0x10, 0x13, 0x16, 0xaf, 0xdd, 0xae, 0x76, 0x21, 0x7a, 0xa8, 0x6c, 0xd0, 0x66, 0x97, 0x9b, 0x19, 0x85, 0x8c, 0x2c, 0x9d, 0x9e, 0x56, 0xee, 0xbc, 0x1e, 0x06, 0x7a, 0xc5, 0x42, 0x77, 0xa6, 0x17, 0x90, 0xdb},
|
||||
ToExecutionAddress: bellatrix.ExecutionAddress{0x8c, 0x1f, 0xf9, 0x78, 0x03, 0x6f, 0x2e, 0x9d, 0x7c, 0xc3, 0x82, 0xef, 0xf7, 0xb4, 0xc8, 0xc5, 0x3c, 0x22, 0xac, 0x15},
|
||||
},
|
||||
Signature: phase0.BLSSignature{0xb7, 0x8a, 0x05, 0xba, 0xd9, 0x27, 0xfc, 0x89, 0x6f, 0x14, 0x06, 0xb3, 0x2d, 0x64, 0x4a, 0xe1, 0x69, 0xce, 0xcd, 0x89, 0x86, 0xc1, 0xef, 0x8c, 0x0d, 0x03, 0x7d, 0x70, 0x86, 0xf8, 0x5f, 0x13, 0xe1, 0xe1, 0x88, 0xb4, 0x30, 0x96, 0x43, 0xa2, 0xc1, 0x3f, 0xfe, 0xfb, 0x0a, 0xe8, 0x05, 0x11, 0x09, 0x98, 0x53, 0xa0, 0x58, 0x1f, 0x4b, 0x2b, 0xd2, 0xe1, 0x45, 0x41, 0x04, 0x79, 0x01, 0xe2, 0x2a, 0x94, 0x0a, 0x9c, 0x7e, 0x3a, 0xc0, 0xa8, 0x82, 0xd1, 0xa8, 0xaf, 0x6b, 0xfa, 0xea, 0x81, 0x3a, 0x6a, 0x6b, 0xe7, 0x21, 0xf9, 0x26, 0x22, 0x04, 0xaa, 0x9d, 0xa4, 0xe4, 0x77, 0x27, 0xd0},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
err := test.command.generateOperationsFromMnemonic(ctx)
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.expected, test.command.signedOperations)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateOperationFromMnemonicAndPath(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
require.NoError(t, e2types.InitBLS())
|
||||
|
||||
chainInfo := &beacon.ChainInfo{
|
||||
Version: 1,
|
||||
Validators: []*beacon.ValidatorInfo{
|
||||
{
|
||||
Index: 0,
|
||||
Pubkey: phase0.BLSPubKey{0xb3, 0x84, 0xf7, 0x67, 0xd9, 0x64, 0xe1, 0x00, 0xc8, 0xa9, 0xb2, 0x10, 0x18, 0xd0, 0x8c, 0x25, 0xff, 0xeb, 0xae, 0x26, 0x8b, 0x3a, 0xb6, 0xd6, 0x10, 0x35, 0x38, 0x97, 0x54, 0x19, 0x71, 0x72, 0x6d, 0xbf, 0xc3, 0xc7, 0x46, 0x38, 0x84, 0xc6, 0x8a, 0x53, 0x15, 0x15, 0xaa, 0xb9, 0x4c, 0x87},
|
||||
WithdrawalCredentials: []byte{0x00, 0x8b, 0xa1, 0xcc, 0x4b, 0x09, 0x1b, 0x91, 0xc1, 0x20, 0x2b, 0xba, 0x3f, 0x50, 0x80, 0x75, 0xd6, 0xff, 0x56, 0x5c, 0x77, 0xe5, 0x59, 0xf0, 0x80, 0x3c, 0x07, 0x92, 0xe0, 0x30, 0x2b, 0xf1},
|
||||
},
|
||||
{
|
||||
Index: 1,
|
||||
Pubkey: phase0.BLSPubKey{0xb3, 0xd8, 0x9e, 0x2f, 0x29, 0xc7, 0x12, 0xc6, 0xa9, 0xf8, 0xe5, 0xa2, 0x69, 0xb9, 0x76, 0x17, 0xc4, 0xa9, 0x4d, 0xd6, 0xf6, 0x66, 0x2a, 0xb3, 0xb0, 0x7c, 0xe9, 0xe5, 0x43, 0x45, 0x73, 0xf1, 0x5b, 0x5c, 0x98, 0x8c, 0xd1, 0x4b, 0xbd, 0x58, 0x04, 0xf7, 0x71, 0x56, 0xa8, 0xaf, 0x1c, 0xfa},
|
||||
WithdrawalCredentials: []byte{0x00, 0x78, 0x6c, 0xb0, 0x2e, 0xd2, 0x8e, 0x5f, 0xbb, 0x1f, 0x7f, 0x9e, 0x93, 0x1a, 0x2b, 0x72, 0x69, 0x29, 0x06, 0xe6, 0xb1, 0x2c, 0xe4, 0x64, 0x39, 0x75, 0xe3, 0x2b, 0x51, 0x76, 0x91, 0xf2},
|
||||
},
|
||||
},
|
||||
GenesisValidatorsRoot: phase0.Root{},
|
||||
Epoch: 1,
|
||||
CurrentForkVersion: phase0.Version{},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
command *command
|
||||
expected []*capella.SignedBLSToExecutionChange
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "MnemonicInvalid",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon",
|
||||
path: "m/12381/3600/0/0/0",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
},
|
||||
err: "mnemonic is invalid",
|
||||
},
|
||||
{
|
||||
name: "PathInvalidNoIndex",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
path: "m/12381/3600/0/0",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
},
|
||||
err: "path m/12381/3600/0/0 does not match EIP-2334 format for a validator",
|
||||
},
|
||||
{
|
||||
name: "PathInvlaidIndexNot2334Format",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
path: "1",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
},
|
||||
err: "path 1 does not match EIP-2334 format for a validator",
|
||||
},
|
||||
{
|
||||
name: "WithdrawalAddressNo0xPrefix",
|
||||
command: &command{mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
path: "m/12381/3600/0/0/0",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
},
|
||||
err: "failed to generate operation from seed and path: invalid withdrawal address: withdrawal address 8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15 does not contain a 0x prefix",
|
||||
},
|
||||
{
|
||||
name: "WithdrawalAddressInvalidLength",
|
||||
command: &command{mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
path: "m/12381/3600/0/0/0",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac",
|
||||
},
|
||||
err: "failed to generate operation from seed and path: invalid withdrawal address: withdrawal address must be exactly 20 bytes in length",
|
||||
},
|
||||
{
|
||||
name: "WithdrawalAddressMissing",
|
||||
command: &command{mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
path: "m/12381/3600/0/0/0",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
},
|
||||
err: "failed to generate operation from seed and path: invalid withdrawal address: no withdrawal address provided",
|
||||
},
|
||||
{
|
||||
name: "InvalidWithdrawalAddressNotHexBarChar",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
path: "m/12381/3600/0/0/0",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0xrc1Ff978036F2e9d7CC382Eff7B4c8c53C22acaa",
|
||||
},
|
||||
err: "failed to generate operation from seed and path: invalid withdrawal address: failed to obtain execution address: encoding/hex: invalid byte: U+0072 'r'",
|
||||
},
|
||||
{
|
||||
name: "NoValidatorFoundAtGivenPath",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
path: "m/12381/3600/10/0/0",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0xrc1Ff978036F2e9d7CC382Eff7B4c8c53C22acaa",
|
||||
},
|
||||
err: "no validator found with the provided path and mnemonic, please run with --debug to see more information",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
path: "m/12381/3600/0/0/0",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
},
|
||||
expected: []*capella.SignedBLSToExecutionChange{
|
||||
{
|
||||
Message: &capella.BLSToExecutionChange{
|
||||
ValidatorIndex: 0,
|
||||
FromBLSPubkey: phase0.BLSPubKey{0x99, 0xb1, 0xf1, 0xd8, 0x4d, 0x76, 0x18, 0x54, 0x66, 0xd8, 0x6c, 0x34, 0xbd, 0xe1, 0x10, 0x13, 0x16, 0xaf, 0xdd, 0xae, 0x76, 0x21, 0x7a, 0xa8, 0x6c, 0xd0, 0x66, 0x97, 0x9b, 0x19, 0x85, 0x8c, 0x2c, 0x9d, 0x9e, 0x56, 0xee, 0xbc, 0x1e, 0x06, 0x7a, 0xc5, 0x42, 0x77, 0xa6, 0x17, 0x90, 0xdb},
|
||||
ToExecutionAddress: bellatrix.ExecutionAddress{0x8c, 0x1f, 0xf9, 0x78, 0x03, 0x6f, 0x2e, 0x9d, 0x7c, 0xc3, 0x82, 0xef, 0xf7, 0xb4, 0xc8, 0xc5, 0x3c, 0x22, 0xac, 0x15},
|
||||
},
|
||||
Signature: phase0.BLSSignature{0xb7, 0x8a, 0x05, 0xba, 0xd9, 0x27, 0xfc, 0x89, 0x6f, 0x14, 0x06, 0xb3, 0x2d, 0x64, 0x4a, 0xe1, 0x69, 0xce, 0xcd, 0x89, 0x86, 0xc1, 0xef, 0x8c, 0x0d, 0x03, 0x7d, 0x70, 0x86, 0xf8, 0x5f, 0x13, 0xe1, 0xe1, 0x88, 0xb4, 0x30, 0x96, 0x43, 0xa2, 0xc1, 0x3f, 0xfe, 0xfb, 0x0a, 0xe8, 0x05, 0x11, 0x09, 0x98, 0x53, 0xa0, 0x58, 0x1f, 0x4b, 0x2b, 0xd2, 0xe1, 0x45, 0x41, 0x04, 0x79, 0x01, 0xe2, 0x2a, 0x94, 0x0a, 0x9c, 0x7e, 0x3a, 0xc0, 0xa8, 0x82, 0xd1, 0xa8, 0xaf, 0x6b, 0xfa, 0xea, 0x81, 0x3a, 0x6a, 0x6b, 0xe7, 0x21, 0xf9, 0x26, 0x22, 0x04, 0xaa, 0x9d, 0xa4, 0xe4, 0x77, 0x27, 0xd0},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
err := test.command.generateOperationFromMnemonicAndPath(ctx)
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
// fmt.Printf("%v\n", test.command.signedOperations)
|
||||
require.Equal(t, test.expected, test.command.signedOperations)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateOperationFromMnemonicAndValidator(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
require.NoError(t, e2types.InitBLS())
|
||||
|
||||
chainInfo := &beacon.ChainInfo{
|
||||
Version: 1,
|
||||
Validators: []*beacon.ValidatorInfo{
|
||||
{
|
||||
Index: 0,
|
||||
Pubkey: phase0.BLSPubKey{0xb3, 0x84, 0xf7, 0x67, 0xd9, 0x64, 0xe1, 0x00, 0xc8, 0xa9, 0xb2, 0x10, 0x18, 0xd0, 0x8c, 0x25, 0xff, 0xeb, 0xae, 0x26, 0x8b, 0x3a, 0xb6, 0xd6, 0x10, 0x35, 0x38, 0x97, 0x54, 0x19, 0x71, 0x72, 0x6d, 0xbf, 0xc3, 0xc7, 0x46, 0x38, 0x84, 0xc6, 0x8a, 0x53, 0x15, 0x15, 0xaa, 0xb9, 0x4c, 0x87},
|
||||
WithdrawalCredentials: []byte{0x00, 0x8b, 0xa1, 0xcc, 0x4b, 0x09, 0x1b, 0x91, 0xc1, 0x20, 0x2b, 0xba, 0x3f, 0x50, 0x80, 0x75, 0xd6, 0xff, 0x56, 0x5c, 0x77, 0xe5, 0x59, 0xf0, 0x80, 0x3c, 0x07, 0x92, 0xe0, 0x30, 0x2b, 0xf1},
|
||||
},
|
||||
{
|
||||
Index: 1,
|
||||
Pubkey: phase0.BLSPubKey{0xb3, 0xd8, 0x9e, 0x2f, 0x29, 0xc7, 0x12, 0xc6, 0xa9, 0xf8, 0xe5, 0xa2, 0x69, 0xb9, 0x76, 0x17, 0xc4, 0xa9, 0x4d, 0xd6, 0xf6, 0x66, 0x2a, 0xb3, 0xb0, 0x7c, 0xe9, 0xe5, 0x43, 0x45, 0x73, 0xf1, 0x5b, 0x5c, 0x98, 0x8c, 0xd1, 0x4b, 0xbd, 0x58, 0x04, 0xf7, 0x71, 0x56, 0xa8, 0xaf, 0x1c, 0xfa},
|
||||
WithdrawalCredentials: []byte{0x00, 0x78, 0x6c, 0xb0, 0x2e, 0xd2, 0x8e, 0x5f, 0xbb, 0x1f, 0x7f, 0x9e, 0x93, 0x1a, 0x2b, 0x72, 0x69, 0x29, 0x06, 0xe6, 0xb1, 0x2c, 0xe4, 0x64, 0x39, 0x75, 0xe3, 0x2b, 0x51, 0x76, 0x91, 0xf2},
|
||||
},
|
||||
},
|
||||
GenesisValidatorsRoot: phase0.Root{},
|
||||
Epoch: 1,
|
||||
CurrentForkVersion: phase0.Version{},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
command *command
|
||||
expected []*capella.SignedBLSToExecutionChange
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "MnemonicInvalid",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon",
|
||||
validator: "0",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
},
|
||||
err: "mnemonic is invalid",
|
||||
},
|
||||
{
|
||||
name: "ValidatorMissing",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
},
|
||||
err: "no validator specified",
|
||||
},
|
||||
{
|
||||
name: "WithdrawalAddressMissing",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
validator: "0",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
},
|
||||
err: "invalid withdrawal address: no withdrawal address provided",
|
||||
},
|
||||
{
|
||||
name: "InvalidWithdrawalAddressLength",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
validator: "0",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac",
|
||||
},
|
||||
err: "invalid withdrawal address: withdrawal address must be exactly 20 bytes in length",
|
||||
},
|
||||
{
|
||||
name: "InvalidWithdrawalAddressNo0xPrefix",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
validator: "0",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "8c1Ff978036F2e9d7CC382Eff7B4c8c53C22acaa",
|
||||
},
|
||||
err: "invalid withdrawal address: withdrawal address 8c1Ff978036F2e9d7CC382Eff7B4c8c53C22acaa does not contain a 0x prefix",
|
||||
},
|
||||
{
|
||||
name: "InvalidWithdrawalAddressNotHexBadChar",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
validator: "0",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0xrc1Ff978036F2e9d7CC382Eff7B4c8c53C22acaa",
|
||||
},
|
||||
err: "invalid withdrawal address: failed to obtain execution address: encoding/hex: invalid byte: U+0072 'r'",
|
||||
},
|
||||
{
|
||||
name: "ValidatorBeyondMaxDistance",
|
||||
command: &command{
|
||||
mnemonic: "struggle kangaroo horn sniff cradle soft ethics thunder cycle illegal flock unaware dynamic cinnamon play enforce card tennis inform parent surprise bring relax tail",
|
||||
validator: "1",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
},
|
||||
err: "failed to find validator using the provided mnemonic, validator=1, pubkey=0xb3d89e2f29c712c6a9f8e5a269b97617c4a94dd6f6662ab3b07ce9e5434573f15b5c988cd14bbd5804f77156a8af1cfa",
|
||||
},
|
||||
{
|
||||
name: "UnknownValidatorPubKey",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
validator: "0xb384f767d964e100c8a9b21018d08c25ffebae268b3ab6d610353897541971726dbfc3c7463884c68a531515aab94c80",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
},
|
||||
|
||||
err: "unknown validator",
|
||||
},
|
||||
{
|
||||
name: "UnknownValidatorIndex",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
validator: "10",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
},
|
||||
|
||||
err: "unknown validator",
|
||||
},
|
||||
{
|
||||
name: "InvalidPubkeyLength",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
validator: "0xb384f767d964e100c8a9b21018d08c25ffebae268b3ab6d610353897541971726dbfc3c7463884c68a531515aab94c",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
},
|
||||
|
||||
err: "invalid public key: incorrect length",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
validator: "0",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
},
|
||||
expected: []*capella.SignedBLSToExecutionChange{
|
||||
{
|
||||
Message: &capella.BLSToExecutionChange{
|
||||
ValidatorIndex: 0,
|
||||
FromBLSPubkey: phase0.BLSPubKey{0x99, 0xb1, 0xf1, 0xd8, 0x4d, 0x76, 0x18, 0x54, 0x66, 0xd8, 0x6c, 0x34, 0xbd, 0xe1, 0x10, 0x13, 0x16, 0xaf, 0xdd, 0xae, 0x76, 0x21, 0x7a, 0xa8, 0x6c, 0xd0, 0x66, 0x97, 0x9b, 0x19, 0x85, 0x8c, 0x2c, 0x9d, 0x9e, 0x56, 0xee, 0xbc, 0x1e, 0x06, 0x7a, 0xc5, 0x42, 0x77, 0xa6, 0x17, 0x90, 0xdb},
|
||||
ToExecutionAddress: bellatrix.ExecutionAddress{0x8c, 0x1f, 0xf9, 0x78, 0x03, 0x6f, 0x2e, 0x9d, 0x7c, 0xc3, 0x82, 0xef, 0xf7, 0xb4, 0xc8, 0xc5, 0x3c, 0x22, 0xac, 0x15},
|
||||
},
|
||||
Signature: phase0.BLSSignature{0xb7, 0x8a, 0x05, 0xba, 0xd9, 0x27, 0xfc, 0x89, 0x6f, 0x14, 0x06, 0xb3, 0x2d, 0x64, 0x4a, 0xe1, 0x69, 0xce, 0xcd, 0x89, 0x86, 0xc1, 0xef, 0x8c, 0x0d, 0x03, 0x7d, 0x70, 0x86, 0xf8, 0x5f, 0x13, 0xe1, 0xe1, 0x88, 0xb4, 0x30, 0x96, 0x43, 0xa2, 0xc1, 0x3f, 0xfe, 0xfb, 0x0a, 0xe8, 0x05, 0x11, 0x09, 0x98, 0x53, 0xa0, 0x58, 0x1f, 0x4b, 0x2b, 0xd2, 0xe1, 0x45, 0x41, 0x04, 0x79, 0x01, 0xe2, 0x2a, 0x94, 0x0a, 0x9c, 0x7e, 0x3a, 0xc0, 0xa8, 0x82, 0xd1, 0xa8, 0xaf, 0x6b, 0xfa, 0xea, 0x81, 0x3a, 0x6a, 0x6b, 0xe7, 0x21, 0xf9, 0x26, 0x22, 0x04, 0xaa, 0x9d, 0xa4, 0xe4, 0x77, 0x27, 0xd0},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "GoodPubkey",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
validator: "0xb384f767d964e100c8a9b21018d08c25ffebae268b3ab6d610353897541971726dbfc3c7463884c68a531515aab94c87",
|
||||
chainInfo: chainInfo,
|
||||
signedOperations: make([]*capella.SignedBLSToExecutionChange, 0),
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
},
|
||||
expected: []*capella.SignedBLSToExecutionChange{
|
||||
{
|
||||
Message: &capella.BLSToExecutionChange{
|
||||
ValidatorIndex: 0,
|
||||
FromBLSPubkey: phase0.BLSPubKey{0x99, 0xb1, 0xf1, 0xd8, 0x4d, 0x76, 0x18, 0x54, 0x66, 0xd8, 0x6c, 0x34, 0xbd, 0xe1, 0x10, 0x13, 0x16, 0xaf, 0xdd, 0xae, 0x76, 0x21, 0x7a, 0xa8, 0x6c, 0xd0, 0x66, 0x97, 0x9b, 0x19, 0x85, 0x8c, 0x2c, 0x9d, 0x9e, 0x56, 0xee, 0xbc, 0x1e, 0x06, 0x7a, 0xc5, 0x42, 0x77, 0xa6, 0x17, 0x90, 0xdb},
|
||||
ToExecutionAddress: bellatrix.ExecutionAddress{0x8c, 0x1f, 0xf9, 0x78, 0x03, 0x6f, 0x2e, 0x9d, 0x7c, 0xc3, 0x82, 0xef, 0xf7, 0xb4, 0xc8, 0xc5, 0x3c, 0x22, 0xac, 0x15},
|
||||
},
|
||||
Signature: phase0.BLSSignature{0xb7, 0x8a, 0x05, 0xba, 0xd9, 0x27, 0xfc, 0x89, 0x6f, 0x14, 0x06, 0xb3, 0x2d, 0x64, 0x4a, 0xe1, 0x69, 0xce, 0xcd, 0x89, 0x86, 0xc1, 0xef, 0x8c, 0x0d, 0x03, 0x7d, 0x70, 0x86, 0xf8, 0x5f, 0x13, 0xe1, 0xe1, 0x88, 0xb4, 0x30, 0x96, 0x43, 0xa2, 0xc1, 0x3f, 0xfe, 0xfb, 0x0a, 0xe8, 0x05, 0x11, 0x09, 0x98, 0x53, 0xa0, 0x58, 0x1f, 0x4b, 0x2b, 0xd2, 0xe1, 0x45, 0x41, 0x04, 0x79, 0x01, 0xe2, 0x2a, 0x94, 0x0a, 0x9c, 0x7e, 0x3a, 0xc0, 0xa8, 0x82, 0xd1, 0xa8, 0xaf, 0x6b, 0xfa, 0xea, 0x81, 0x3a, 0x6a, 0x6b, 0xe7, 0x21, 0xf9, 0x26, 0x22, 0x04, 0xaa, 0x9d, 0xa4, 0xe4, 0x77, 0x27, 0xd0},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
err := test.command.generateOperationFromMnemonicAndValidator(ctx)
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.expected, test.command.signedOperations)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateOperationFromSeedAndPath(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
require.NoError(t, e2types.InitBLS())
|
||||
|
||||
chainInfo := &beacon.ChainInfo{
|
||||
Version: 1,
|
||||
Validators: []*beacon.ValidatorInfo{
|
||||
{
|
||||
Index: 0,
|
||||
Pubkey: phase0.BLSPubKey{0xb3, 0x84, 0xf7, 0x67, 0xd9, 0x64, 0xe1, 0x00, 0xc8, 0xa9, 0xb2, 0x10, 0x18, 0xd0, 0x8c, 0x25, 0xff, 0xeb, 0xae, 0x26, 0x8b, 0x3a, 0xb6, 0xd6, 0x10, 0x35, 0x38, 0x97, 0x54, 0x19, 0x71, 0x72, 0x6d, 0xbf, 0xc3, 0xc7, 0x46, 0x38, 0x84, 0xc6, 0x8a, 0x53, 0x15, 0x15, 0xaa, 0xb9, 0x4c, 0x87},
|
||||
WithdrawalCredentials: []byte{0x00, 0x8b, 0xa1, 0xcc, 0x4b, 0x09, 0x1b, 0x91, 0xc1, 0x20, 0x2b, 0xba, 0x3f, 0x50, 0x80, 0x75, 0xd6, 0xff, 0x56, 0x5c, 0x77, 0xe5, 0x59, 0xf0, 0x80, 0x3c, 0x07, 0x92, 0xe0, 0x30, 0x2b, 0xf1},
|
||||
},
|
||||
{
|
||||
Index: 1,
|
||||
Pubkey: phase0.BLSPubKey{0xb3, 0xd8, 0x9e, 0x2f, 0x29, 0xc7, 0x12, 0xc6, 0xa9, 0xf8, 0xe5, 0xa2, 0x69, 0xb9, 0x76, 0x17, 0xc4, 0xa9, 0x4d, 0xd6, 0xf6, 0x66, 0x2a, 0xb3, 0xb0, 0x7c, 0xe9, 0xe5, 0x43, 0x45, 0x73, 0xf1, 0x5b, 0x5c, 0x98, 0x8c, 0xd1, 0x4b, 0xbd, 0x58, 0x04, 0xf7, 0x71, 0x56, 0xa8, 0xaf, 0x1c, 0xfa},
|
||||
WithdrawalCredentials: []byte{0x00, 0x78, 0x6c, 0xb0, 0x2e, 0xd2, 0x8e, 0x5f, 0xbb, 0x1f, 0x7f, 0x9e, 0x93, 0x1a, 0x2b, 0x72, 0x69, 0x29, 0x06, 0xe6, 0xb1, 0x2c, 0xe4, 0x64, 0x39, 0x75, 0xe3, 0x2b, 0x51, 0x76, 0x91, 0xf2},
|
||||
},
|
||||
{
|
||||
Index: 2,
|
||||
Pubkey: phase0.BLSPubKey{0xaf, 0x9c, 0xe4, 0x4f, 0x50, 0x14, 0x8d, 0xb4, 0x12, 0x19, 0x4a, 0xf0, 0xba, 0xf0, 0xba, 0xb3, 0x6b, 0xd5, 0xc3, 0xe0, 0xc4, 0x93, 0x89, 0x11, 0xa4, 0xe5, 0x02, 0xe3, 0x98, 0xb5, 0x9e, 0x5c, 0xca, 0x7c, 0x78, 0xe3, 0xfe, 0x03, 0x41, 0x95, 0x47, 0x88, 0x79, 0xee, 0xb2, 0x3d, 0xb0, 0xa6},
|
||||
WithdrawalCredentials: []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x93, 0x1a, 0x2b, 0x72, 0x69, 0x29, 0x06, 0xe6, 0xb1, 0x2c, 0xe4, 0x64, 0x39, 0x75, 0xe3, 0x2b, 0x51, 0x76, 0x91, 0xf2},
|
||||
},
|
||||
{
|
||||
Index: 3,
|
||||
Pubkey: phase0.BLSPubKey{0x86, 0xd3, 0x30, 0xaf, 0x51, 0xfa, 0x59, 0x3f, 0xa9, 0xf9, 0x3e, 0xdb, 0x9d, 0x16, 0x64, 0x01, 0x86, 0xbe, 0x2e, 0x93, 0xea, 0x94, 0xd2, 0x59, 0x78, 0x1e, 0x1e, 0xb3, 0x4d, 0xeb, 0x84, 0x4c, 0x39, 0x68, 0xd7, 0x5e, 0xa9, 0x1d, 0x19, 0xf1, 0x59, 0xdb, 0xd0, 0x52, 0x3c, 0x6c, 0x5b, 0xa5},
|
||||
WithdrawalCredentials: []byte{0x00, 0x81, 0x68, 0x45, 0x6b, 0x6d, 0x9a, 0x32, 0x83, 0x93, 0x1f, 0xea, 0x52, 0x10, 0xda, 0x12, 0x2d, 0x1e, 0x65, 0xe8, 0xed, 0x50, 0xb8, 0xe8, 0xf5, 0x91, 0x11, 0x83, 0xb0, 0x2f, 0xd1, 0x25},
|
||||
},
|
||||
},
|
||||
GenesisValidatorsRoot: phase0.Root{},
|
||||
Epoch: 1,
|
||||
CurrentForkVersion: phase0.Version{},
|
||||
}
|
||||
validators := make(map[string]*beacon.ValidatorInfo, len(chainInfo.Validators))
|
||||
for i := range chainInfo.Validators {
|
||||
validators[fmt.Sprintf("%#x", chainInfo.Validators[i].Pubkey)] = chainInfo.Validators[i]
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
command *command
|
||||
seed []byte
|
||||
path string
|
||||
generated bool
|
||||
err string
|
||||
expected []*capella.SignedBLSToExecutionChange
|
||||
}{
|
||||
{
|
||||
name: "PathInvalid",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
chainInfo: chainInfo,
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
},
|
||||
seed: []byte{0x40, 0x8b, 0x28, 0x5c, 0x12, 0x38, 0x36, 0x00, 0x4f, 0x4b, 0x88, 0x42, 0xc8, 0x93, 0x24, 0xc1, 0xf0, 0x13, 0x82, 0x45, 0x0c, 0x0d, 0x43, 0x9a, 0xf3, 0x45, 0xba, 0x7f, 0xc4, 0x9a, 0xcf, 0x70, 0x54, 0x89, 0xc6, 0xfc, 0x77, 0xdb, 0xd4, 0xe3, 0xdc, 0x1d, 0xd8, 0xcc, 0x6b, 0xc9, 0xf0, 0x43, 0xdb, 0x8a, 0xda, 0x1e, 0x24, 0x3c, 0x4a, 0x0e, 0xaf, 0xb2, 0x90, 0xd3, 0x99, 0x48, 0x08, 0x40},
|
||||
path: "invalid",
|
||||
err: "failed to generate validator private key: not master at path component 0",
|
||||
},
|
||||
{
|
||||
name: "ValidatorUnknown",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
chainInfo: chainInfo,
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
},
|
||||
seed: []byte{0x40, 0x8b, 0x28, 0x5c, 0x12, 0x38, 0x36, 0x00, 0x4f, 0x4b, 0x88, 0x42, 0xc8, 0x93, 0x24, 0xc1, 0xf0, 0x13, 0x82, 0x45, 0x0c, 0x0d, 0x43, 0x9a, 0xf3, 0x45, 0xba, 0x7f, 0xc4, 0x9a, 0xcf, 0x70, 0x54, 0x89, 0xc6, 0xfc, 0x77, 0xdb, 0xd4, 0xe3, 0xdc, 0x1d, 0xd8, 0xcc, 0x6b, 0xc9, 0xf0, 0x43, 0xdb, 0x8a, 0xda, 0x1e, 0x24, 0x3c, 0x4a, 0x0e, 0xaf, 0xb2, 0x90, 0xd3, 0x99, 0x48, 0x08, 0x40},
|
||||
path: "m/12381/3600/999/0/0",
|
||||
generated: false,
|
||||
},
|
||||
{
|
||||
name: "ValidatorCredentialsAlreadySet",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
chainInfo: chainInfo,
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
},
|
||||
seed: []byte{0x40, 0x8b, 0x28, 0x5c, 0x12, 0x38, 0x36, 0x00, 0x4f, 0x4b, 0x88, 0x42, 0xc8, 0x93, 0x24, 0xc1, 0xf0, 0x13, 0x82, 0x45, 0x0c, 0x0d, 0x43, 0x9a, 0xf3, 0x45, 0xba, 0x7f, 0xc4, 0x9a, 0xcf, 0x70, 0x54, 0x89, 0xc6, 0xfc, 0x77, 0xdb, 0xd4, 0xe3, 0xdc, 0x1d, 0xd8, 0xcc, 0x6b, 0xc9, 0xf0, 0x43, 0xdb, 0x8a, 0xda, 0x1e, 0x24, 0x3c, 0x4a, 0x0e, 0xaf, 0xb2, 0x90, 0xd3, 0x99, 0x48, 0x08, 0x40},
|
||||
path: "m/12381/3600/2/0/0",
|
||||
generated: false,
|
||||
},
|
||||
{
|
||||
name: "PrivateKeyInvalid",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
chainInfo: chainInfo,
|
||||
privateKey: "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
},
|
||||
seed: []byte{0x40, 0x8b, 0x28, 0x5c, 0x12, 0x38, 0x36, 0x00, 0x4f, 0x4b, 0x88, 0x42, 0xc8, 0x93, 0x24, 0xc1, 0xf0, 0x13, 0x82, 0x45, 0x0c, 0x0d, 0x43, 0x9a, 0xf3, 0x45, 0xba, 0x7f, 0xc4, 0x9a, 0xcf, 0x70, 0x54, 0x89, 0xc6, 0xfc, 0x77, 0xdb, 0xd4, 0xe3, 0xdc, 0x1d, 0xd8, 0xcc, 0x6b, 0xc9, 0xf0, 0x43, 0xdb, 0x8a, 0xda, 0x1e, 0x24, 0x3c, 0x4a, 0x0e, 0xaf, 0xb2, 0x90, 0xd3, 0x99, 0x48, 0x08, 0x40},
|
||||
path: "m/12381/3600/0/0/0",
|
||||
err: "failed to create account from private key: invalid private key: err blsSecretKeyDeserialize ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
},
|
||||
{
|
||||
name: "PrivateKeyDoesNotMatch",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
chainInfo: chainInfo,
|
||||
privateKey: "0x67775f030068b4610d6e1bd04948f547305b2502423fcece4c1091d065b44638",
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
},
|
||||
seed: []byte{0x40, 0x8b, 0x28, 0x5c, 0x12, 0x38, 0x36, 0x00, 0x4f, 0x4b, 0x88, 0x42, 0xc8, 0x93, 0x24, 0xc1, 0xf0, 0x13, 0x82, 0x45, 0x0c, 0x0d, 0x43, 0x9a, 0xf3, 0x45, 0xba, 0x7f, 0xc4, 0x9a, 0xcf, 0x70, 0x54, 0x89, 0xc6, 0xfc, 0x77, 0xdb, 0xd4, 0xe3, 0xdc, 0x1d, 0xd8, 0xcc, 0x6b, 0xc9, 0xf0, 0x43, 0xdb, 0x8a, 0xda, 0x1e, 0x24, 0x3c, 0x4a, 0x0e, 0xaf, 0xb2, 0x90, 0xd3, 0x99, 0x48, 0x08, 0x40},
|
||||
path: "m/12381/3600/4/0/0",
|
||||
generated: false,
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
chainInfo: chainInfo,
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
},
|
||||
seed: []byte{0x40, 0x8b, 0x28, 0x5c, 0x12, 0x38, 0x36, 0x00, 0x4f, 0x4b, 0x88, 0x42, 0xc8, 0x93, 0x24, 0xc1, 0xf0, 0x13, 0x82, 0x45, 0x0c, 0x0d, 0x43, 0x9a, 0xf3, 0x45, 0xba, 0x7f, 0xc4, 0x9a, 0xcf, 0x70, 0x54, 0x89, 0xc6, 0xfc, 0x77, 0xdb, 0xd4, 0xe3, 0xdc, 0x1d, 0xd8, 0xcc, 0x6b, 0xc9, 0xf0, 0x43, 0xdb, 0x8a, 0xda, 0x1e, 0x24, 0x3c, 0x4a, 0x0e, 0xaf, 0xb2, 0x90, 0xd3, 0x99, 0x48, 0x08, 0x40},
|
||||
path: "m/12381/3600/0/0/0",
|
||||
generated: true,
|
||||
expected: []*capella.SignedBLSToExecutionChange{
|
||||
{
|
||||
Message: &capella.BLSToExecutionChange{
|
||||
ValidatorIndex: 0,
|
||||
FromBLSPubkey: phase0.BLSPubKey{0x99, 0xb1, 0xf1, 0xd8, 0x4d, 0x76, 0x18, 0x54, 0x66, 0xd8, 0x6c, 0x34, 0xbd, 0xe1, 0x10, 0x13, 0x16, 0xaf, 0xdd, 0xae, 0x76, 0x21, 0x7a, 0xa8, 0x6c, 0xd0, 0x66, 0x97, 0x9b, 0x19, 0x85, 0x8c, 0x2c, 0x9d, 0x9e, 0x56, 0xee, 0xbc, 0x1e, 0x06, 0x7a, 0xc5, 0x42, 0x77, 0xa6, 0x17, 0x90, 0xdb},
|
||||
ToExecutionAddress: bellatrix.ExecutionAddress{0x8c, 0x1f, 0xf9, 0x78, 0x03, 0x6f, 0x2e, 0x9d, 0x7c, 0xc3, 0x82, 0xef, 0xf7, 0xb4, 0xc8, 0xc5, 0x3c, 0x22, 0xac, 0x15},
|
||||
},
|
||||
Signature: phase0.BLSSignature{0xb7, 0x8a, 0x05, 0xba, 0xd9, 0x27, 0xfc, 0x89, 0x6f, 0x14, 0x06, 0xb3, 0x2d, 0x64, 0x4a, 0xe1, 0x69, 0xce, 0xcd, 0x89, 0x86, 0xc1, 0xef, 0x8c, 0x0d, 0x03, 0x7d, 0x70, 0x86, 0xf8, 0x5f, 0x13, 0xe1, 0xe1, 0x88, 0xb4, 0x30, 0x96, 0x43, 0xa2, 0xc1, 0x3f, 0xfe, 0xfb, 0x0a, 0xe8, 0x05, 0x11, 0x09, 0x98, 0x53, 0xa0, 0x58, 0x1f, 0x4b, 0x2b, 0xd2, 0xe1, 0x45, 0x41, 0x04, 0x79, 0x01, 0xe2, 0x2a, 0x94, 0x0a, 0x9c, 0x7e, 0x3a, 0xc0, 0xa8, 0x82, 0xd1, 0xa8, 0xaf, 0x6b, 0xfa, 0xea, 0x81, 0x3a, 0x6a, 0x6b, 0xe7, 0x21, 0xf9, 0x26, 0x22, 0x04, 0xaa, 0x9d, 0xa4, 0xe4, 0x77, 0x27, 0xd0},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "GoodPrivateKey",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
chainInfo: chainInfo,
|
||||
withdrawalAddressStr: "0x8c1Ff978036F2e9d7CC382Eff7B4c8c53C22ac15",
|
||||
privateKey: "0x67775f030068b4610d6e1bd04948f547305b2502423fcece4c1091d065b44638",
|
||||
},
|
||||
seed: []byte{0x40, 0x8b, 0x28, 0x5c, 0x12, 0x38, 0x36, 0x00, 0x4f, 0x4b, 0x88, 0x42, 0xc8, 0x93, 0x24, 0xc1, 0xf0, 0x13, 0x82, 0x45, 0x0c, 0x0d, 0x43, 0x9a, 0xf3, 0x45, 0xba, 0x7f, 0xc4, 0x9a, 0xcf, 0x70, 0x54, 0x89, 0xc6, 0xfc, 0x77, 0xdb, 0xd4, 0xe3, 0xdc, 0x1d, 0xd8, 0xcc, 0x6b, 0xc9, 0xf0, 0x43, 0xdb, 0x8a, 0xda, 0x1e, 0x24, 0x3c, 0x4a, 0x0e, 0xaf, 0xb2, 0x90, 0xd3, 0x99, 0x48, 0x08, 0x40},
|
||||
path: "m/12381/3600/3/0/0",
|
||||
generated: true,
|
||||
expected: []*capella.SignedBLSToExecutionChange{
|
||||
{
|
||||
Message: &capella.BLSToExecutionChange{
|
||||
ValidatorIndex: 3,
|
||||
FromBLSPubkey: phase0.BLSPubKey{0x86, 0x71, 0x0a, 0xbb, 0x44, 0xb6, 0xcd, 0xa6, 0x66, 0x57, 0x7b, 0xbb, 0x25, 0x5e, 0x16, 0xd9, 0x8b, 0xf2, 0x52, 0x51, 0x76, 0x22, 0x3f, 0x35, 0x35, 0xc7, 0xdf, 0xf8, 0xe7, 0x0b, 0x3b, 0xc8, 0x92, 0xbb, 0x36, 0x11, 0x33, 0x95, 0x2b, 0x03, 0xd2, 0xb0, 0x78, 0xcd, 0x07, 0x18, 0xca, 0xf3},
|
||||
ToExecutionAddress: bellatrix.ExecutionAddress{0x8c, 0x1f, 0xf9, 0x78, 0x03, 0x6f, 0x2e, 0x9d, 0x7c, 0xc3, 0x82, 0xef, 0xf7, 0xb4, 0xc8, 0xc5, 0x3c, 0x22, 0xac, 0x15},
|
||||
},
|
||||
Signature: phase0.BLSSignature{0x8d, 0x92, 0xb9, 0x1c, 0x5d, 0xfd, 0x98, 0xc7, 0x98, 0xfc, 0x94, 0xe1, 0xe6, 0x69, 0xf3, 0xaa, 0xae, 0x72, 0xb2, 0x36, 0x47, 0xde, 0x88, 0x54, 0xea, 0x16, 0x74, 0x7f, 0xfe, 0xf0, 0x4d, 0x46, 0x5c, 0x07, 0x56, 0x34, 0x03, 0x30, 0x2f, 0xbc, 0x26, 0xa2, 0x6d, 0xec, 0x10, 0x20, 0xe7, 0x67, 0x10, 0xb0, 0x4a, 0x7e, 0x4e, 0x25, 0x89, 0x7e, 0x87, 0x88, 0xda, 0xaf, 0x2b, 0xb5, 0xb7, 0x73, 0x25, 0x64, 0x80, 0xc1, 0xba, 0xf3, 0x1d, 0x33, 0x8f, 0x17, 0xa5, 0x35, 0x74, 0x80, 0xf3, 0x37, 0x0e, 0xea, 0x19, 0x15, 0xd5, 0x69, 0x7e, 0xf6, 0x68, 0xaa, 0x9c, 0x3d, 0x47, 0x19, 0x75, 0xfc},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
generated, err := test.command.generateOperationFromSeedAndPath(ctx, validators, test.seed, test.path)
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.generated, generated)
|
||||
if generated {
|
||||
require.Equal(t, test.expected, test.command.signedOperations)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,6 @@ func output(ctx context.Context, data *dataOut) (string, error) {
|
||||
} else {
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
105
cmd/validator/exit/chaininfo.go
Normal file
105
cmd/validator/exit/chaininfo.go
Normal file
@@ -0,0 +1,105 @@
|
||||
// Copyright © 2023 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 validatorexit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/wealdtech/ethdo/beacon"
|
||||
)
|
||||
|
||||
// obtainChainInfo obtains the chain information required to create an exit operation.
|
||||
func (c *command) obtainChainInfo(ctx context.Context) error {
|
||||
var err error
|
||||
// Use the offline preparation file if present (and we haven't been asked to recreate it).
|
||||
if !c.prepareOffline {
|
||||
if err = c.obtainChainInfoFromFile(ctx); err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if c.offline {
|
||||
// If we are here it means that we are offline without chain information, and cannot continue.
|
||||
return fmt.Errorf("failed to obtain offline preparation file: %w", err)
|
||||
}
|
||||
|
||||
if err := c.obtainChainInfoFromNode(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// obtainChainInfoFromFile obtains chain information from a pre-generated file.
|
||||
func (c *command) obtainChainInfoFromFile(_ context.Context) error {
|
||||
_, err := os.Stat(offlinePreparationFilename)
|
||||
if err != nil {
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "Failed to read offline preparation file: %v\n", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "%s found; loading chain state\n", offlinePreparationFilename)
|
||||
}
|
||||
data, err := os.ReadFile(offlinePreparationFilename)
|
||||
if err != nil {
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "failed to load offline preparation file: %v\n", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
c.chainInfo = &beacon.ChainInfo{}
|
||||
if err := json.Unmarshal(data, c.chainInfo); err != nil {
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "offline preparation file invalid: %v\n", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// obtainChainInfoFromNode obtains chain info from a beacon node.
|
||||
func (c *command) obtainChainInfoFromNode(ctx context.Context) error {
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "Populating chain info from beacon node\n")
|
||||
}
|
||||
|
||||
var err error
|
||||
c.chainInfo, err = beacon.ObtainChainInfoFromNode(ctx, c.consensusClient, c.chainTime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeChainInfoToFile prepares for an offline run of this command by dumping
|
||||
// the chain information to a file.
|
||||
func (c *command) writeChainInfoToFile(_ context.Context) error {
|
||||
data, err := json.Marshal(c.chainInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.WriteFile(offlinePreparationFilename, data, 0o600); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
102
cmd/validator/exit/command.go
Normal file
102
cmd/validator/exit/command.go
Normal file
@@ -0,0 +1,102 @@
|
||||
// Copyright © 2023 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 validatorexit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
consensusclient "github.com/attestantio/go-eth2-client"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/wealdtech/ethdo/beacon"
|
||||
"github.com/wealdtech/ethdo/services/chaintime"
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
)
|
||||
|
||||
type command struct {
|
||||
quiet bool
|
||||
verbose bool
|
||||
debug bool
|
||||
offline bool
|
||||
json bool
|
||||
|
||||
// Input.
|
||||
passphrases []string
|
||||
mnemonic string
|
||||
path string
|
||||
privateKey string
|
||||
validator string
|
||||
forkVersion string
|
||||
genesisValidatorsRoot string
|
||||
prepareOffline bool
|
||||
signedOperationInput string
|
||||
|
||||
// Beacon node connection.
|
||||
timeout time.Duration
|
||||
connection string
|
||||
allowInsecureConnections bool
|
||||
|
||||
// Information required to generate the operations.
|
||||
chainInfo *beacon.ChainInfo
|
||||
domain phase0.Domain
|
||||
|
||||
// Processing.
|
||||
consensusClient consensusclient.Service
|
||||
chainTime chaintime.Service
|
||||
|
||||
// Output.
|
||||
signedOperation *phase0.SignedVoluntaryExit
|
||||
}
|
||||
|
||||
func newCommand(_ context.Context) (*command, error) {
|
||||
c := &command{
|
||||
quiet: viper.GetBool("quiet"),
|
||||
verbose: viper.GetBool("verbose"),
|
||||
debug: viper.GetBool("debug"),
|
||||
offline: viper.GetBool("offline"),
|
||||
json: viper.GetBool("json"),
|
||||
timeout: viper.GetDuration("timeout"),
|
||||
connection: viper.GetString("connection"),
|
||||
allowInsecureConnections: viper.GetBool("allow-insecure-connections"),
|
||||
prepareOffline: viper.GetBool("prepare-offline"),
|
||||
passphrases: util.GetPassphrases(),
|
||||
mnemonic: viper.GetString("mnemonic"),
|
||||
path: viper.GetString("path"),
|
||||
privateKey: viper.GetString("private-key"),
|
||||
signedOperationInput: viper.GetString("signed-operation"),
|
||||
validator: viper.GetString("validator"),
|
||||
forkVersion: viper.GetString("fork-version"),
|
||||
genesisValidatorsRoot: viper.GetString("genesis-validators-root"),
|
||||
}
|
||||
|
||||
// Account and validator are synonymous.
|
||||
if c.validator == "" {
|
||||
c.validator = viper.GetString("account")
|
||||
}
|
||||
|
||||
// Timeout is required.
|
||||
if c.timeout == 0 {
|
||||
return nil, errors.New("timeout is required")
|
||||
}
|
||||
|
||||
// We are generating information for offline use, we don't need any information
|
||||
// related to the accounts or signing.
|
||||
if c.prepareOffline {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
// Copyright © 2019, 2020 Weald Technology Trading
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validatorexit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
spec "github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
type dataIn struct {
|
||||
// System.
|
||||
timeout time.Duration
|
||||
quiet bool
|
||||
verbose bool
|
||||
debug bool
|
||||
// Operation.
|
||||
eth2Client eth2client.Service
|
||||
jsonOutput bool
|
||||
// Chain information.
|
||||
fork *spec.Fork
|
||||
currentEpoch spec.Epoch
|
||||
// Exit information.
|
||||
account e2wtypes.Account
|
||||
passphrases []string
|
||||
epoch spec.Epoch
|
||||
domain spec.Domain
|
||||
signedVoluntaryExit *spec.SignedVoluntaryExit
|
||||
}
|
||||
|
||||
func input(ctx context.Context) (*dataIn, error) {
|
||||
data := &dataIn{}
|
||||
|
||||
if viper.GetDuration("timeout") == 0 {
|
||||
return nil, errors.New("timeout is required")
|
||||
}
|
||||
data.timeout = viper.GetDuration("timeout")
|
||||
data.quiet = viper.GetBool("quiet")
|
||||
data.verbose = viper.GetBool("verbose")
|
||||
data.debug = viper.GetBool("debug")
|
||||
data.passphrases = util.GetPassphrases()
|
||||
data.jsonOutput = viper.GetBool("json")
|
||||
|
||||
switch {
|
||||
case viper.GetString("exit") != "":
|
||||
return inputJSON(ctx, data)
|
||||
case viper.GetString("account") != "":
|
||||
return inputAccount(ctx, data)
|
||||
case viper.GetString("key") != "":
|
||||
return inputKey(ctx, data)
|
||||
default:
|
||||
return nil, errors.New("must supply account, key, or pre-constructed JSON")
|
||||
}
|
||||
}
|
||||
|
||||
func inputJSON(ctx context.Context, data *dataIn) (*dataIn, error) {
|
||||
validatorData := &util.ValidatorExitData{}
|
||||
err := json.Unmarshal([]byte(viper.GetString("exit")), validatorData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data.signedVoluntaryExit = validatorData.Exit
|
||||
return inputChainData(ctx, data)
|
||||
}
|
||||
|
||||
func inputAccount(ctx context.Context, data *dataIn) (*dataIn, error) {
|
||||
var err error
|
||||
_, data.account, err = util.WalletAndAccountFromInput(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain acount")
|
||||
}
|
||||
return inputChainData(ctx, data)
|
||||
}
|
||||
|
||||
func inputKey(ctx context.Context, data *dataIn) (*dataIn, error) {
|
||||
privKeyBytes, err := hex.DecodeString(strings.TrimPrefix(viper.GetString("key"), "0x"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode key")
|
||||
}
|
||||
data.account, err = util.NewScratchAccount(privKeyBytes, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create acount from key")
|
||||
}
|
||||
if err := data.account.(e2wtypes.AccountLocker).Unlock(ctx, nil); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unlock account")
|
||||
}
|
||||
return inputChainData(ctx, data)
|
||||
}
|
||||
|
||||
func inputChainData(ctx context.Context, data *dataIn) (*dataIn, error) {
|
||||
var err error
|
||||
data.eth2Client, err = util.ConnectToBeaconNode(ctx, viper.GetString("connection"), viper.GetDuration("timeout"), viper.GetBool("allow-insecure-connections"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to connect to Ethereum 2 beacon node")
|
||||
}
|
||||
|
||||
// Current fork.
|
||||
data.fork, err = data.eth2Client.(eth2client.ForkProvider).Fork(ctx, "head")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to connect to obtain fork information")
|
||||
}
|
||||
|
||||
// Calculate current epoch.
|
||||
config, err := data.eth2Client.(eth2client.SpecProvider).Spec(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to connect to obtain configuration information")
|
||||
}
|
||||
genesis, err := data.eth2Client.(eth2client.GenesisProvider).Genesis(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to connect to obtain genesis information")
|
||||
}
|
||||
data.currentEpoch = spec.Epoch(uint64(time.Since(genesis.GenesisTime).Seconds()) / (uint64(config["SECONDS_PER_SLOT"].(time.Duration).Seconds()) * config["SLOTS_PER_EPOCH"].(uint64)))
|
||||
|
||||
// Epoch.
|
||||
if viper.GetInt64("epoch") == -1 {
|
||||
data.epoch = data.currentEpoch
|
||||
} else {
|
||||
data.epoch = spec.Epoch(viper.GetUint64("epoch"))
|
||||
}
|
||||
|
||||
// Domain.
|
||||
domain, err := data.eth2Client.(eth2client.DomainProvider).Domain(ctx, config["DOMAIN_VOLUNTARY_EXIT"].(spec.DomainType), data.epoch)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to calculate domain")
|
||||
}
|
||||
data.domain = domain
|
||||
|
||||
return data, nil
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
// Copyright © 2019, 2020 Weald Technology Trading
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validatorexit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/wealdtech/ethdo/testutil"
|
||||
e2types "github.com/wealdtech/go-eth2-types/v2"
|
||||
e2wallet "github.com/wealdtech/go-eth2-wallet"
|
||||
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
|
||||
nd "github.com/wealdtech/go-eth2-wallet-nd/v2"
|
||||
scratch "github.com/wealdtech/go-eth2-wallet-store-scratch"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
func TestInput(t *testing.T) {
|
||||
if os.Getenv("ETHDO_TEST_CONNECTION") == "" {
|
||||
t.Skip("ETHDO_TEST_CONNECTION not configured; cannot run tests")
|
||||
}
|
||||
|
||||
require.NoError(t, e2types.InitBLS())
|
||||
|
||||
store := scratch.New()
|
||||
require.NoError(t, e2wallet.UseStore(store))
|
||||
testWallet, err := nd.CreateWallet(context.Background(), "Test wallet", store, keystorev4.New())
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, testWallet.(e2wtypes.WalletLocker).Unlock(context.Background(), nil))
|
||||
viper.Set("passphrase", "pass")
|
||||
_, err = testWallet.(e2wtypes.WalletAccountImporter).ImportAccount(context.Background(),
|
||||
"Interop 0",
|
||||
testutil.HexToBytes("0x25295f0d1d592a90b333e26e85149708208e9f8e8bc18f6c77bd62f8ad7a6866"),
|
||||
[]byte("pass"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
vars map[string]interface{}
|
||||
res *dataIn
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "TimeoutMissing",
|
||||
vars: map[string]interface{}{
|
||||
"account": "Test wallet",
|
||||
"wallet-passphrase": "ce%NohGhah4ye5ra",
|
||||
"type": "nd",
|
||||
},
|
||||
err: "timeout is required",
|
||||
},
|
||||
{
|
||||
name: "NoMethod",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "5s",
|
||||
},
|
||||
err: "must supply account, key, or pre-constructed JSON",
|
||||
},
|
||||
{
|
||||
name: "KeyInvalid",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "5s",
|
||||
"key": "0xinvalid",
|
||||
},
|
||||
err: "failed to decode key: encoding/hex: invalid byte: U+0069 'i'",
|
||||
},
|
||||
{
|
||||
name: "KeyBad",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "5s",
|
||||
"key": "0x00",
|
||||
},
|
||||
err: "failed to create acount from key: private key must be 32 bytes",
|
||||
},
|
||||
{
|
||||
name: "KeyGood",
|
||||
vars: map[string]interface{}{
|
||||
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
|
||||
"allow-insecure-connections": true,
|
||||
"timeout": "5s",
|
||||
"key": "0x25295f0d1d592a90b333e26e85149708208e9f8e8bc18f6c77bd62f8ad7a6866",
|
||||
},
|
||||
res: &dataIn{
|
||||
timeout: 5 * time.Second,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "AccountUnknown",
|
||||
vars: map[string]interface{}{
|
||||
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
|
||||
"allow-insecure-connections": true,
|
||||
"timeout": "5s",
|
||||
"account": "Test wallet/unknown",
|
||||
},
|
||||
res: &dataIn{
|
||||
timeout: 5 * time.Second,
|
||||
},
|
||||
err: "failed to obtain acount: failed to obtain account: no account with name \"unknown\"",
|
||||
},
|
||||
{
|
||||
name: "AccountGood",
|
||||
vars: map[string]interface{}{
|
||||
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
|
||||
"allow-insecure-connections": true,
|
||||
"timeout": "5s",
|
||||
"account": "Test wallet/Interop 0",
|
||||
},
|
||||
res: &dataIn{
|
||||
timeout: 5 * time.Second,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "JSONInvalid",
|
||||
vars: map[string]interface{}{
|
||||
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
|
||||
"allow-insecure-connections": true,
|
||||
"timeout": "5s",
|
||||
"exit": `invalid`,
|
||||
},
|
||||
res: &dataIn{
|
||||
timeout: 5 * time.Second,
|
||||
},
|
||||
err: "invalid character 'i' looking for beginning of value",
|
||||
},
|
||||
{
|
||||
name: "JSONGood",
|
||||
vars: map[string]interface{}{
|
||||
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
|
||||
"allow-insecure-connections": true,
|
||||
"timeout": "5s",
|
||||
"exit": `{"exit":{"message":{"epoch":"123","validator_index":"456"},"signature":"0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f"},"fork_version":"0x00002009"}`,
|
||||
},
|
||||
res: &dataIn{
|
||||
timeout: 5 * time.Second,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ClientBad",
|
||||
vars: map[string]interface{}{
|
||||
"connection": "localhost:1",
|
||||
"allow-insecure-connections": true,
|
||||
"timeout": "5s",
|
||||
"key": "0x25295f0d1d592a90b333e26e85149708208e9f8e8bc18f6c77bd62f8ad7a6866",
|
||||
},
|
||||
err: "failed to connect to Ethereum 2 beacon node: failed to connect to beacon node: failed to confirm node connection: failed to fetch genesis: failed to request genesis: failed to call GET endpoint: Get \"http://localhost:1/eth/v1/beacon/genesis\": dial tcp 127.0.0.1:1: connect: connection refused",
|
||||
},
|
||||
{
|
||||
name: "EpochProvided",
|
||||
vars: map[string]interface{}{
|
||||
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
|
||||
"allow-insecure-connections": true,
|
||||
"timeout": "5s",
|
||||
"key": "0x25295f0d1d592a90b333e26e85149708208e9f8e8bc18f6c77bd62f8ad7a6866",
|
||||
"epoch": "123",
|
||||
},
|
||||
res: &dataIn{
|
||||
timeout: 5 * time.Second,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
viper.Reset()
|
||||
for k, v := range test.vars {
|
||||
viper.Set(k, v)
|
||||
}
|
||||
res, err := input(context.Background())
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.res.timeout, res.timeout)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2019, 2020 Weald Technology Trading
|
||||
// Copyright © 2023 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
|
||||
@@ -16,42 +16,35 @@ package validatorexit
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
spec "github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
)
|
||||
|
||||
type dataOut struct {
|
||||
jsonOutput bool
|
||||
forkVersion spec.Version
|
||||
signedVoluntaryExit *spec.SignedVoluntaryExit
|
||||
}
|
||||
|
||||
func output(ctx context.Context, data *dataOut) (string, error) {
|
||||
if data == nil {
|
||||
return "", errors.New("no data")
|
||||
//nolint:unparam
|
||||
func (c *command) output(_ context.Context) (string, error) {
|
||||
if c.quiet {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if data.signedVoluntaryExit == nil {
|
||||
return "", errors.New("no signed voluntary exit")
|
||||
if c.prepareOffline {
|
||||
return fmt.Sprintf("%s generated", offlinePreparationFilename), nil
|
||||
}
|
||||
|
||||
if data.jsonOutput {
|
||||
return outputJSON(ctx, data)
|
||||
if c.json || c.offline {
|
||||
data, err := json.Marshal(c.signedOperation)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to marshal signed operation")
|
||||
}
|
||||
if c.json {
|
||||
return string(data), nil
|
||||
}
|
||||
if err := os.WriteFile(exitOperationFilename, data, 0o600); err != nil {
|
||||
return "", errors.Wrap(err, fmt.Sprintf("failed to write %s", exitOperationFilename))
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func outputJSON(ctx context.Context, data *dataOut) (string, error) {
|
||||
validatorExitData := &util.ValidatorExitData{
|
||||
Exit: data.signedVoluntaryExit,
|
||||
ForkVersion: data.forkVersion,
|
||||
}
|
||||
bytes, err := json.Marshal(validatorExitData)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to generate JSON")
|
||||
}
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
// Copyright © 2019, 2020 Weald Technology Trading
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validatorexit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
spec "github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestOutput(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
dataOut *dataOut
|
||||
res string
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "Nil",
|
||||
err: "no data",
|
||||
},
|
||||
{
|
||||
name: "SignedVoluntaryExitNil",
|
||||
dataOut: &dataOut{
|
||||
jsonOutput: true,
|
||||
},
|
||||
err: "no signed voluntary exit",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
dataOut: &dataOut{
|
||||
forkVersion: spec.Version{0x01, 0x02, 0x03, 0x04},
|
||||
signedVoluntaryExit: &spec.SignedVoluntaryExit{
|
||||
Message: &spec.VoluntaryExit{
|
||||
Epoch: spec.Epoch(123),
|
||||
ValidatorIndex: spec.ValidatorIndex(456),
|
||||
},
|
||||
Signature: spec.BLSSignature{
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
|
||||
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
|
||||
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "JSON",
|
||||
dataOut: &dataOut{
|
||||
jsonOutput: true,
|
||||
forkVersion: spec.Version{0x01, 0x02, 0x03, 0x04},
|
||||
signedVoluntaryExit: &spec.SignedVoluntaryExit{
|
||||
Message: &spec.VoluntaryExit{
|
||||
Epoch: spec.Epoch(123),
|
||||
ValidatorIndex: spec.ValidatorIndex(456),
|
||||
},
|
||||
Signature: spec.BLSSignature{
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
|
||||
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
|
||||
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
||||
},
|
||||
},
|
||||
},
|
||||
res: `{"exit":{"message":{"epoch":"123","validator_index":"456"},"signature":"0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f"},"fork_version":"0x01020304"}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
res, err := output(context.Background(), test.dataOut)
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.res, res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2019, 2020 Weald Technology Trading
|
||||
// Copyright © 2023 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,120 +14,529 @@
|
||||
package validatorexit
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
api "github.com/attestantio/go-eth2-client/api/v1"
|
||||
spec "github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
consensusclient "github.com/attestantio/go-eth2-client"
|
||||
apiv1 "github.com/attestantio/go-eth2-client/api/v1"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/wealdtech/ethdo/beacon"
|
||||
standardchaintime "github.com/wealdtech/ethdo/services/chaintime/standard"
|
||||
"github.com/wealdtech/ethdo/signing"
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
e2types "github.com/wealdtech/go-eth2-types/v2"
|
||||
ethutil "github.com/wealdtech/go-eth2-util"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
// maxFutureEpochs is the farthest in the future for which an exit will be created.
|
||||
var maxFutureEpochs = spec.Epoch(1024)
|
||||
// minTimeout is the minimum timeout for this command.
|
||||
// It needs to be set here as we want timeouts to be low in general, but this can be pulling
|
||||
// a lot of data for an unsophisticated audience so it's easier to set a higher timeout..
|
||||
var minTimeout = 2 * time.Minute
|
||||
|
||||
func process(ctx context.Context, data *dataIn) (*dataOut, error) {
|
||||
if data == nil {
|
||||
return nil, errors.New("no data")
|
||||
// validatorPath is the regular expression that matches a validator path.
|
||||
var validatorPath = regexp.MustCompile("^m/12381/3600/[0-9]+/0/0$")
|
||||
|
||||
var (
|
||||
offlinePreparationFilename = "offline-preparation.json"
|
||||
exitOperationFilename = "exit-operation.json"
|
||||
)
|
||||
|
||||
func (c *command) process(ctx context.Context) error {
|
||||
if err := c.setup(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if data.epoch > data.currentEpoch {
|
||||
if data.epoch-data.currentEpoch > maxFutureEpochs {
|
||||
return nil, errors.New("not generating exit for an epoch in the far future")
|
||||
if err := c.obtainChainInfo(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.prepareOffline {
|
||||
return c.writeChainInfoToFile(ctx)
|
||||
}
|
||||
|
||||
if err := c.generateDomain(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.obtainOperation(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if validated, reason := c.validateOperation(ctx); !validated {
|
||||
return fmt.Errorf("operation failed validation: %s", reason)
|
||||
}
|
||||
|
||||
if c.json || c.offline {
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "Not broadcasting exit operation\n")
|
||||
}
|
||||
// Want JSON output, or cannot broadcast.
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.broadcastOperation(ctx)
|
||||
}
|
||||
|
||||
func (c *command) obtainOperation(ctx context.Context) error {
|
||||
if (c.mnemonic == "" || c.path == "") && c.privateKey == "" && c.validator == "" {
|
||||
// No input information; fetch the operation from a file.
|
||||
err := c.obtainOperationFromFileOrInput(ctx)
|
||||
if err == nil {
|
||||
// Success.
|
||||
return nil
|
||||
}
|
||||
if c.signedOperationInput != "" {
|
||||
return errors.Wrap(err, "failed to obtain supplied signed operation")
|
||||
}
|
||||
return errors.Wrap(err, fmt.Sprintf("no account, mnemonic or private key specified, and no %s file loaded", exitOperationFilename))
|
||||
}
|
||||
|
||||
if c.mnemonic != "" {
|
||||
switch {
|
||||
case c.path != "":
|
||||
// Have a mnemonic and path.
|
||||
return c.generateOperationFromMnemonicAndPath(ctx)
|
||||
case c.validator != "":
|
||||
// Have a mnemonic and validator.
|
||||
return c.generateOperationFromMnemonicAndValidator(ctx)
|
||||
default:
|
||||
return errors.New("mnemonic must be supplied with either a path or validator")
|
||||
}
|
||||
}
|
||||
results := &dataOut{
|
||||
forkVersion: data.fork.CurrentVersion,
|
||||
jsonOutput: data.jsonOutput,
|
||||
|
||||
if c.privateKey != "" {
|
||||
return c.generateOperationFromPrivateKey(ctx)
|
||||
}
|
||||
|
||||
validator, err := fetchValidator(ctx, data)
|
||||
if c.validator != "" {
|
||||
return c.generateOperationFromValidator(ctx)
|
||||
}
|
||||
|
||||
return errors.New("unsupported combination of inputs; see help for details of supported combinations")
|
||||
}
|
||||
|
||||
func (c *command) generateOperationFromMnemonicAndPath(ctx context.Context) error {
|
||||
seed, err := util.SeedFromMnemonic(c.mnemonic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Turn the validators in to a map for easy lookup.
|
||||
validators := make(map[string]*beacon.ValidatorInfo, 0)
|
||||
for _, validator := range c.chainInfo.Validators {
|
||||
validators[fmt.Sprintf("%#x", validator.Pubkey)] = validator
|
||||
}
|
||||
|
||||
validatorKeyPath := c.path
|
||||
match := validatorPath.Match([]byte(c.path))
|
||||
if !match {
|
||||
return fmt.Errorf("path %s does not match EIP-2334 format for a validator", c.path)
|
||||
}
|
||||
|
||||
if err := c.generateOperationFromSeedAndPath(ctx, validators, seed, validatorKeyPath); err != nil {
|
||||
return errors.Wrap(err, "failed to generate operation from seed and path")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) generateOperationFromMnemonicAndValidator(ctx context.Context) error {
|
||||
seed, err := util.SeedFromMnemonic(c.mnemonic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
validatorInfo, err := c.chainInfo.FetchValidatorInfo(ctx, c.validator)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Scan the keys from the seed to find the path.
|
||||
maxDistance := 1024
|
||||
// Start scanning the validator keys.
|
||||
for i := 0; ; i++ {
|
||||
if i == maxDistance {
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "Gone %d indices without finding the validator, not scanning any further\n", maxDistance)
|
||||
}
|
||||
break
|
||||
}
|
||||
validatorKeyPath := fmt.Sprintf("m/12381/3600/%d/0/0", i)
|
||||
validatorPrivkey, err := ethutil.PrivateKeyFromSeedAndPath(seed, validatorKeyPath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to generate validator private key")
|
||||
}
|
||||
validatorPubkey := validatorPrivkey.PublicKey().Marshal()
|
||||
if bytes.Equal(validatorPubkey, validatorInfo.Pubkey[:]) {
|
||||
validatorAccount, err := util.ParseAccount(ctx, c.mnemonic, []string{validatorKeyPath}, true)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create withdrawal account")
|
||||
}
|
||||
|
||||
if err := c.generateOperationFromAccount(ctx, validatorAccount); err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) generateOperationFromPrivateKey(ctx context.Context) error {
|
||||
validatorAccount, err := util.ParseAccount(ctx, c.privateKey, nil, true)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to parse validator account")
|
||||
}
|
||||
|
||||
if err = c.generateOperationFromAccount(ctx, validatorAccount); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) generateOperationFromValidator(ctx context.Context) error {
|
||||
validatorAccount, err := util.ParseAccount(ctx, c.validator, nil, true)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to parse validator account")
|
||||
}
|
||||
|
||||
if err := c.generateOperationFromAccount(ctx, validatorAccount); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) obtainOperationFromFileOrInput(ctx context.Context) error {
|
||||
// Start off by attempting to use the provided signed operation.
|
||||
if c.signedOperationInput != "" {
|
||||
return c.obtainOperationFromInput(ctx)
|
||||
}
|
||||
// If not, read it from the file with the standard name.
|
||||
return c.obtainOperationFromFile(ctx)
|
||||
}
|
||||
|
||||
func (c *command) obtainOperationFromFile(ctx context.Context) error {
|
||||
_, err := os.Stat(exitOperationFilename)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to read exit operation file")
|
||||
}
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "%s found; loading operation\n", exitOperationFilename)
|
||||
}
|
||||
data, err := os.ReadFile(exitOperationFilename)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to read exit operation file")
|
||||
}
|
||||
if err := json.Unmarshal(data, &c.signedOperation); err != nil {
|
||||
return errors.Wrap(err, "failed to parse exit operation file")
|
||||
}
|
||||
|
||||
if err := c.verifySignedOperation(ctx, c.signedOperation); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) obtainOperationFromInput(ctx context.Context) error {
|
||||
if !strings.HasPrefix(c.signedOperationInput, "{") {
|
||||
// This looks like a file; read it in.
|
||||
data, err := os.ReadFile(c.signedOperationInput)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to read input file")
|
||||
}
|
||||
c.signedOperationInput = string(data)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal([]byte(c.signedOperationInput), &c.signedOperation); err != nil {
|
||||
return errors.Wrap(err, "failed to parse exit operation input")
|
||||
}
|
||||
|
||||
if err := c.verifySignedOperation(ctx, c.signedOperation); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) generateOperationFromSeedAndPath(ctx context.Context,
|
||||
validators map[string]*beacon.ValidatorInfo,
|
||||
seed []byte,
|
||||
path string,
|
||||
) error {
|
||||
validatorPrivkey, err := ethutil.PrivateKeyFromSeedAndPath(seed, path)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to generate validator private key")
|
||||
}
|
||||
|
||||
c.privateKey = fmt.Sprintf("%#x", validatorPrivkey.Marshal())
|
||||
return c.generateOperationFromPrivateKey(ctx)
|
||||
}
|
||||
|
||||
func (c *command) generateOperationFromAccount(ctx context.Context,
|
||||
account e2wtypes.Account,
|
||||
) error {
|
||||
pubKey, err := util.BestPublicKey(account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info, err := c.chainInfo.FetchValidatorInfo(ctx, fmt.Sprintf("%#x", pubKey.Marshal()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.signedOperation, err = c.createSignedOperation(ctx, info, account, c.chainInfo.Epoch)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *command) createSignedOperation(ctx context.Context,
|
||||
validator *beacon.ValidatorInfo,
|
||||
account e2wtypes.Account,
|
||||
epoch phase0.Epoch,
|
||||
) (
|
||||
*phase0.SignedVoluntaryExit,
|
||||
error,
|
||||
) {
|
||||
pubkey, err := util.BestPublicKey(account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
exit, err := generateExit(ctx, data, validator)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to generate voluntary exit")
|
||||
}
|
||||
root, err := exit.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to generate root for voluntary exit")
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "Using %#x as best public key for %s\n", pubkey.Marshal(), account.Name())
|
||||
}
|
||||
blsPubkey := phase0.BLSPubKey{}
|
||||
copy(blsPubkey[:], pubkey.Marshal())
|
||||
|
||||
if data.account != nil {
|
||||
signature, err := signing.SignRoot(ctx, data.account, data.passphrases, root, data.domain)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to sign voluntary exit")
|
||||
}
|
||||
|
||||
results.signedVoluntaryExit = &spec.SignedVoluntaryExit{
|
||||
Message: exit,
|
||||
Signature: signature,
|
||||
}
|
||||
} else {
|
||||
results.signedVoluntaryExit = data.signedVoluntaryExit
|
||||
}
|
||||
|
||||
if !data.jsonOutput {
|
||||
if err := broadcastExit(ctx, data, results); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to broadcast voluntary exit")
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func generateExit(ctx context.Context, data *dataIn, validator *api.Validator) (*spec.VoluntaryExit, error) {
|
||||
if data == nil {
|
||||
return nil, errors.New("no data")
|
||||
}
|
||||
|
||||
if data.signedVoluntaryExit != nil {
|
||||
return data.signedVoluntaryExit.Message, nil
|
||||
}
|
||||
|
||||
if validator == nil {
|
||||
return nil, errors.New("no validator")
|
||||
}
|
||||
|
||||
exit := &spec.VoluntaryExit{
|
||||
Epoch: data.epoch,
|
||||
operation := &phase0.VoluntaryExit{
|
||||
Epoch: epoch,
|
||||
ValidatorIndex: validator.Index,
|
||||
}
|
||||
return exit, nil
|
||||
}
|
||||
|
||||
func broadcastExit(ctx context.Context, data *dataIn, results *dataOut) error {
|
||||
return data.eth2Client.(eth2client.VoluntaryExitSubmitter).SubmitVoluntaryExit(ctx, results.signedVoluntaryExit)
|
||||
}
|
||||
|
||||
func fetchValidator(ctx context.Context, data *dataIn) (*api.Validator, error) {
|
||||
// Validator.
|
||||
if data.account == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var validator *api.Validator
|
||||
validatorPubKeys := make([]spec.BLSPubKey, 1)
|
||||
pubKey, err := util.BestPublicKey(data.account)
|
||||
root, err := operation.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain public key for account")
|
||||
return nil, errors.Wrap(err, "failed to generate root for exit operation")
|
||||
}
|
||||
copy(validatorPubKeys[0][:], pubKey.Marshal())
|
||||
validators, err := data.eth2Client.(eth2client.ValidatorsProvider).ValidatorsByPubKey(ctx, "head", validatorPubKeys)
|
||||
|
||||
// Sign the operation.
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "Signing %#x with domain %#x by public key %#x\n", root, c.domain, account.PublicKey().Marshal())
|
||||
}
|
||||
signature, err := signing.SignRoot(ctx, account, nil, root, c.domain)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain validator from beacon node")
|
||||
return nil, errors.Wrap(err, "failed to sign exit operation")
|
||||
}
|
||||
if len(validators) == 0 {
|
||||
return nil, errors.New("validator not known by beacon node")
|
||||
}
|
||||
for _, v := range validators {
|
||||
validator = v
|
||||
}
|
||||
if validator.Status != api.ValidatorStateActiveOngoing {
|
||||
return nil, errors.New("validator is not active; cannot exit")
|
||||
}
|
||||
return validator, nil
|
||||
|
||||
return &phase0.SignedVoluntaryExit{
|
||||
Message: operation,
|
||||
Signature: signature,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *command) verifySignedOperation(ctx context.Context, op *phase0.SignedVoluntaryExit) error {
|
||||
root, err := op.Message.HashTreeRoot()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to generate message root")
|
||||
}
|
||||
|
||||
sigBytes := make([]byte, len(op.Signature))
|
||||
copy(sigBytes, op.Signature[:])
|
||||
sig, err := e2types.BLSSignatureFromBytes(sigBytes)
|
||||
if err != nil {
|
||||
if c.verbose {
|
||||
fmt.Fprintf(os.Stderr, "Invalid signature: %v\n", err.Error())
|
||||
}
|
||||
return errors.New("invalid signature")
|
||||
}
|
||||
|
||||
container := &phase0.SigningData{
|
||||
ObjectRoot: root,
|
||||
Domain: c.domain,
|
||||
}
|
||||
signingRoot, err := ssz.HashTreeRoot(container)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to generate signing root")
|
||||
}
|
||||
|
||||
validatorInfo, err := c.chainInfo.FetchValidatorInfo(ctx, fmt.Sprintf("%d", op.Message.ValidatorIndex))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pubkeyBytes := make([]byte, len(validatorInfo.Pubkey[:]))
|
||||
copy(pubkeyBytes, validatorInfo.Pubkey[:])
|
||||
pubkey, err := e2types.BLSPublicKeyFromBytes(pubkeyBytes)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "invalid public key")
|
||||
}
|
||||
|
||||
if !sig.Verify(signingRoot[:], pubkey) {
|
||||
return errors.New("signature does not verify")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) validateOperation(_ context.Context,
|
||||
) (
|
||||
bool,
|
||||
string,
|
||||
) {
|
||||
var validatorInfo *beacon.ValidatorInfo
|
||||
for _, chainValidatorInfo := range c.chainInfo.Validators {
|
||||
if chainValidatorInfo.Index == c.signedOperation.Message.ValidatorIndex {
|
||||
validatorInfo = chainValidatorInfo
|
||||
break
|
||||
}
|
||||
}
|
||||
if validatorInfo == nil {
|
||||
return false, "validator not known on chain"
|
||||
}
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "Validator exit operation: %v", c.signedOperation)
|
||||
fmt.Fprintf(os.Stderr, "On-chain validator info: %v\n", validatorInfo)
|
||||
}
|
||||
|
||||
if validatorInfo.State == apiv1.ValidatorStateActiveExiting ||
|
||||
validatorInfo.State == apiv1.ValidatorStateActiveSlashed ||
|
||||
validatorInfo.State == apiv1.ValidatorStateExitedUnslashed ||
|
||||
validatorInfo.State == apiv1.ValidatorStateExitedSlashed ||
|
||||
validatorInfo.State == apiv1.ValidatorStateWithdrawalPossible ||
|
||||
validatorInfo.State == apiv1.ValidatorStateWithdrawalDone {
|
||||
return false, fmt.Sprintf("validator is in state %v, not suitable to generate an exit", validatorInfo.State)
|
||||
}
|
||||
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func (c *command) broadcastOperation(ctx context.Context) error {
|
||||
return c.consensusClient.(consensusclient.VoluntaryExitSubmitter).SubmitVoluntaryExit(ctx, c.signedOperation)
|
||||
}
|
||||
|
||||
func (c *command) setup(ctx context.Context) error {
|
||||
if c.offline {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ensure timeout is at least the minimum.
|
||||
if c.timeout < minTimeout {
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "Increasing timeout to %v\n", minTimeout)
|
||||
c.timeout = minTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// Connect to the consensus node.
|
||||
var err error
|
||||
c.consensusClient, err = util.ConnectToBeaconNode(ctx, c.connection, c.timeout, c.allowInsecureConnections)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to connect to consensus node")
|
||||
}
|
||||
|
||||
// Set up chaintime.
|
||||
c.chainTime, err = standardchaintime.New(ctx,
|
||||
standardchaintime.WithGenesisTimeProvider(c.consensusClient.(consensusclient.GenesisTimeProvider)),
|
||||
standardchaintime.WithSpecProvider(c.consensusClient.(consensusclient.SpecProvider)),
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create chaintime service")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) generateDomain(ctx context.Context) error {
|
||||
genesisValidatorsRoot, err := c.obtainGenesisValidatorsRoot(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
forkVersion, err := c.obtainForkVersion(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
root, err := (&phase0.ForkData{
|
||||
CurrentVersion: forkVersion,
|
||||
GenesisValidatorsRoot: genesisValidatorsRoot,
|
||||
}).HashTreeRoot()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to calculate signature domain")
|
||||
}
|
||||
|
||||
copy(c.domain[:], c.chainInfo.VoluntaryExitDomainType[:])
|
||||
copy(c.domain[4:], root[:])
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "Domain is %#x\n", c.domain)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) obtainGenesisValidatorsRoot(ctx context.Context) (phase0.Root, error) {
|
||||
genesisValidatorsRoot := phase0.Root{}
|
||||
|
||||
if c.genesisValidatorsRoot != "" {
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "Genesis validators root supplied on the command line\n")
|
||||
}
|
||||
root, err := hex.DecodeString(strings.TrimPrefix(c.genesisValidatorsRoot, "0x"))
|
||||
if err != nil {
|
||||
return phase0.Root{}, errors.Wrap(err, "invalid genesis validators root supplied")
|
||||
}
|
||||
if len(root) != phase0.RootLength {
|
||||
return phase0.Root{}, errors.New("invalid length for genesis validators root")
|
||||
}
|
||||
copy(genesisValidatorsRoot[:], root)
|
||||
} else {
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "Genesis validators root obtained from chain info\n")
|
||||
}
|
||||
copy(genesisValidatorsRoot[:], c.chainInfo.GenesisValidatorsRoot[:])
|
||||
}
|
||||
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "Using genesis validators root %#x\n", genesisValidatorsRoot)
|
||||
}
|
||||
return genesisValidatorsRoot, nil
|
||||
}
|
||||
|
||||
func (c *command) obtainForkVersion(ctx context.Context) (phase0.Version, error) {
|
||||
forkVersion := phase0.Version{}
|
||||
|
||||
if c.forkVersion != "" {
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "Fork version supplied on the command line\n")
|
||||
}
|
||||
version, err := hex.DecodeString(strings.TrimPrefix(c.forkVersion, "0x"))
|
||||
if err != nil {
|
||||
return phase0.Version{}, errors.Wrap(err, "invalid fork version supplied")
|
||||
}
|
||||
if len(version) != phase0.ForkVersionLength {
|
||||
return phase0.Version{}, errors.New("invalid length for fork version")
|
||||
}
|
||||
copy(forkVersion[:], version)
|
||||
} else {
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "Fork version obtained from chain info\n")
|
||||
}
|
||||
// Use the current fork version for generating an exit as per the spec.
|
||||
copy(forkVersion[:], c.chainInfo.CurrentForkVersion[:])
|
||||
}
|
||||
|
||||
if c.debug {
|
||||
fmt.Fprintf(os.Stderr, "Using fork version %#x\n", forkVersion)
|
||||
}
|
||||
return forkVersion, nil
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2019, 2020 Weald Technology Trading
|
||||
// Copyright © 2023 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
|
||||
@@ -15,215 +15,467 @@ package validatorexit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
api "github.com/attestantio/go-eth2-client/api/v1"
|
||||
"github.com/attestantio/go-eth2-client/auto"
|
||||
spec "github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/wealdtech/ethdo/testutil"
|
||||
"github.com/wealdtech/ethdo/beacon"
|
||||
e2types "github.com/wealdtech/go-eth2-types/v2"
|
||||
e2wallet "github.com/wealdtech/go-eth2-wallet"
|
||||
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
|
||||
nd "github.com/wealdtech/go-eth2-wallet-nd/v2"
|
||||
scratch "github.com/wealdtech/go-eth2-wallet-store-scratch"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
func TestProcess(t *testing.T) {
|
||||
if os.Getenv("ETHDO_TEST_CONNECTION") == "" {
|
||||
t.Skip("ETHDO_TEST_CONNECTION not configured; cannot run tests")
|
||||
}
|
||||
func TestGenerateOperationFromMnemonicAndPath(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
require.NoError(t, e2types.InitBLS())
|
||||
|
||||
eth2Client, err := auto.New(context.Background(),
|
||||
auto.WithAddress(os.Getenv("ETHDO_TEST_CONNECTION")),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
store := scratch.New()
|
||||
require.NoError(t, e2wallet.UseStore(store))
|
||||
testWallet, err := nd.CreateWallet(context.Background(), "Test wallet", store, keystorev4.New())
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, testWallet.(e2wtypes.WalletLocker).Unlock(context.Background(), nil))
|
||||
viper.Set("passphrase", "pass")
|
||||
interop0, err := testWallet.(e2wtypes.WalletAccountImporter).ImportAccount(context.Background(),
|
||||
"Interop 0",
|
||||
testutil.HexToBytes("0x25295f0d1d592a90b333e26e85149708208e9f8e8bc18f6c77bd62f8ad7a6866"),
|
||||
[]byte("pass"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// activeValidator := &api.Validator{
|
||||
// Index: 123,
|
||||
// Balance: 32123456789,
|
||||
// Status: api.ValidatorStateActiveOngoing,
|
||||
// Validator: &spec.Validator{
|
||||
// PublicKey: testutil.HexToPubKey("0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c"),
|
||||
// WithdrawalCredentials: nil,
|
||||
// EffectiveBalance: 32000000000,
|
||||
// Slashed: false,
|
||||
// ActivationEligibilityEpoch: 0,
|
||||
// ActivationEpoch: 0,
|
||||
// ExitEpoch: 0,
|
||||
// WithdrawableEpoch: 0,
|
||||
// },
|
||||
// }
|
||||
|
||||
epochFork := &spec.Fork{
|
||||
PreviousVersion: spec.Version{0x00, 0x00, 0x00, 0x00},
|
||||
CurrentVersion: spec.Version{0x00, 0x00, 0x00, 0x00},
|
||||
Epoch: 0,
|
||||
chainInfo := &beacon.ChainInfo{
|
||||
Version: 1,
|
||||
Validators: []*beacon.ValidatorInfo{
|
||||
{
|
||||
Index: 0,
|
||||
Pubkey: phase0.BLSPubKey{0xb3, 0x84, 0xf7, 0x67, 0xd9, 0x64, 0xe1, 0x00, 0xc8, 0xa9, 0xb2, 0x10, 0x18, 0xd0, 0x8c, 0x25, 0xff, 0xeb, 0xae, 0x26, 0x8b, 0x3a, 0xb6, 0xd6, 0x10, 0x35, 0x38, 0x97, 0x54, 0x19, 0x71, 0x72, 0x6d, 0xbf, 0xc3, 0xc7, 0x46, 0x38, 0x84, 0xc6, 0x8a, 0x53, 0x15, 0x15, 0xaa, 0xb9, 0x4c, 0x87},
|
||||
WithdrawalCredentials: []byte{0x00, 0x8b, 0xa1, 0xcc, 0x4b, 0x09, 0x1b, 0x91, 0xc1, 0x20, 0x2b, 0xba, 0x3f, 0x50, 0x80, 0x75, 0xd6, 0xff, 0x56, 0x5c, 0x77, 0xe5, 0x59, 0xf0, 0x80, 0x3c, 0x07, 0x92, 0xe0, 0x30, 0x2b, 0xf1},
|
||||
},
|
||||
{
|
||||
Index: 1,
|
||||
Pubkey: phase0.BLSPubKey{0xb3, 0xd8, 0x9e, 0x2f, 0x29, 0xc7, 0x12, 0xc6, 0xa9, 0xf8, 0xe5, 0xa2, 0x69, 0xb9, 0x76, 0x17, 0xc4, 0xa9, 0x4d, 0xd6, 0xf6, 0x66, 0x2a, 0xb3, 0xb0, 0x7c, 0xe9, 0xe5, 0x43, 0x45, 0x73, 0xf1, 0x5b, 0x5c, 0x98, 0x8c, 0xd1, 0x4b, 0xbd, 0x58, 0x04, 0xf7, 0x71, 0x56, 0xa8, 0xaf, 0x1c, 0xfa},
|
||||
WithdrawalCredentials: []byte{0x00, 0x78, 0x6c, 0xb0, 0x2e, 0xd2, 0x8e, 0x5f, 0xbb, 0x1f, 0x7f, 0x9e, 0x93, 0x1a, 0x2b, 0x72, 0x69, 0x29, 0x06, 0xe6, 0xb1, 0x2c, 0xe4, 0x64, 0x39, 0x75, 0xe3, 0x2b, 0x51, 0x76, 0x91, 0xf2},
|
||||
},
|
||||
},
|
||||
GenesisValidatorsRoot: phase0.Root{},
|
||||
Epoch: 1,
|
||||
CurrentForkVersion: phase0.Version{},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
dataIn *dataIn
|
||||
err string
|
||||
name string
|
||||
command *command
|
||||
expected *phase0.SignedVoluntaryExit
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "Nil",
|
||||
err: "no data",
|
||||
},
|
||||
{
|
||||
name: "EpochTooLate",
|
||||
dataIn: &dataIn{
|
||||
timeout: 5 * time.Second,
|
||||
eth2Client: eth2Client,
|
||||
fork: epochFork,
|
||||
currentEpoch: 10,
|
||||
account: interop0,
|
||||
passphrases: []string{"pass"},
|
||||
epoch: 9999999,
|
||||
domain: spec.Domain{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f},
|
||||
name: "MnemonicInvalid",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon",
|
||||
path: "m/12381/3600/0/0/0",
|
||||
chainInfo: chainInfo,
|
||||
},
|
||||
err: "not generating exit for an epoch in the far future",
|
||||
err: "mnemonic is invalid",
|
||||
},
|
||||
{
|
||||
name: "AccountUnknown",
|
||||
dataIn: &dataIn{
|
||||
timeout: 5 * time.Second,
|
||||
eth2Client: eth2Client,
|
||||
fork: epochFork,
|
||||
currentEpoch: 10,
|
||||
account: interop0,
|
||||
passphrases: []string{"pass"},
|
||||
epoch: 10,
|
||||
domain: spec.Domain{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f},
|
||||
name: "PathInvalid",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
path: "m/12381/3600/0/0",
|
||||
chainInfo: chainInfo,
|
||||
},
|
||||
err: "validator not known by beacon node",
|
||||
},
|
||||
// {
|
||||
// name: "Good",
|
||||
// dataIn: &dataIn{
|
||||
// timeout: 5 * time.Second,
|
||||
// eth2Client: eth2Client,
|
||||
// fork: epochFork,
|
||||
// currentEpoch: 10,
|
||||
// account: interop0,
|
||||
// passphrases: []string{"pass"},
|
||||
// epoch: 10,
|
||||
// domain: spec.Domain{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f},
|
||||
// },
|
||||
// },
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
_, err := process(context.Background(), test.dataIn)
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateExit(t *testing.T) {
|
||||
activeValidator := &api.Validator{
|
||||
Index: 123,
|
||||
Balance: 32123456789,
|
||||
Status: api.ValidatorStateActiveOngoing,
|
||||
Validator: &spec.Validator{
|
||||
PublicKey: testutil.HexToPubKey("0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c"),
|
||||
WithdrawalCredentials: nil,
|
||||
EffectiveBalance: 32000000000,
|
||||
Slashed: false,
|
||||
ActivationEligibilityEpoch: 0,
|
||||
ActivationEpoch: 0,
|
||||
ExitEpoch: 0,
|
||||
WithdrawableEpoch: 0,
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
validator *api.Validator
|
||||
dataIn *dataIn
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "Nil",
|
||||
err: "no data",
|
||||
},
|
||||
{
|
||||
name: "SignedVoluntaryExitGood",
|
||||
dataIn: &dataIn{
|
||||
signedVoluntaryExit: &spec.SignedVoluntaryExit{
|
||||
Message: &spec.VoluntaryExit{
|
||||
Epoch: spec.Epoch(123),
|
||||
ValidatorIndex: spec.ValidatorIndex(456),
|
||||
},
|
||||
Signature: spec.BLSSignature{
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
|
||||
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
|
||||
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ValidatorMissing",
|
||||
dataIn: &dataIn{},
|
||||
err: "no validator",
|
||||
},
|
||||
{
|
||||
name: "ValidatorGood",
|
||||
dataIn: &dataIn{},
|
||||
validator: activeValidator,
|
||||
err: "path m/12381/3600/0/0 does not match EIP-2334 format for a validator",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
dataIn: &dataIn{
|
||||
signedVoluntaryExit: &spec.SignedVoluntaryExit{
|
||||
Message: &spec.VoluntaryExit{
|
||||
Epoch: spec.Epoch(123),
|
||||
ValidatorIndex: spec.ValidatorIndex(456),
|
||||
},
|
||||
Signature: spec.BLSSignature{
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
|
||||
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
|
||||
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
||||
},
|
||||
},
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
path: "m/12381/3600/0/0/0",
|
||||
chainInfo: chainInfo,
|
||||
},
|
||||
expected: &phase0.SignedVoluntaryExit{
|
||||
Message: &phase0.VoluntaryExit{
|
||||
Epoch: 1,
|
||||
ValidatorIndex: 0,
|
||||
},
|
||||
Signature: phase0.BLSSignature{0x89, 0xf5, 0xc4, 0x42, 0x88, 0xf9, 0x5e, 0x19, 0xb6, 0xc1, 0x39, 0xf2, 0x62, 0x30, 0x05, 0x66, 0x5b, 0x98, 0x34, 0x62, 0xa2, 0x28, 0x12, 0x09, 0x77, 0xd8, 0x1f, 0x2e, 0xf5, 0x47, 0x56, 0x0b, 0xe2, 0x24, 0x46, 0xde, 0x21, 0xa8, 0xa9, 0x37, 0xd9, 0xdd, 0xa4, 0xe2, 0xd2, 0xec, 0x41, 0x75, 0x19, 0x64, 0x96, 0xcd, 0xd1, 0x30, 0x6d, 0xec, 0x4a, 0x12, 0x5f, 0x8c, 0x86, 0x1f, 0x80, 0x61, 0x71, 0x50, 0x4a, 0x9d, 0x6a, 0x61, 0x0e, 0xc4, 0xe1, 0x35, 0x04, 0x7e, 0x4f, 0xb6, 0x70, 0x52, 0xec, 0xc4, 0x56, 0x13, 0x60, 0xd0, 0xc3, 0xde, 0x04, 0xb6, 0xfb, 0xc4, 0x47, 0x42, 0x23, 0xff},
|
||||
},
|
||||
validator: activeValidator,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
_, err := generateExit(context.Background(), test.dataIn, test.validator)
|
||||
err := test.command.generateOperationFromMnemonicAndPath(ctx)
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.expected, test.command.signedOperation)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateOperationFromMnemonicAndValidator(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
require.NoError(t, e2types.InitBLS())
|
||||
|
||||
chainInfo := &beacon.ChainInfo{
|
||||
Version: 1,
|
||||
Validators: []*beacon.ValidatorInfo{
|
||||
{
|
||||
Index: 0,
|
||||
Pubkey: phase0.BLSPubKey{0xb3, 0x84, 0xf7, 0x67, 0xd9, 0x64, 0xe1, 0x00, 0xc8, 0xa9, 0xb2, 0x10, 0x18, 0xd0, 0x8c, 0x25, 0xff, 0xeb, 0xae, 0x26, 0x8b, 0x3a, 0xb6, 0xd6, 0x10, 0x35, 0x38, 0x97, 0x54, 0x19, 0x71, 0x72, 0x6d, 0xbf, 0xc3, 0xc7, 0x46, 0x38, 0x84, 0xc6, 0x8a, 0x53, 0x15, 0x15, 0xaa, 0xb9, 0x4c, 0x87},
|
||||
WithdrawalCredentials: []byte{0x00, 0x8b, 0xa1, 0xcc, 0x4b, 0x09, 0x1b, 0x91, 0xc1, 0x20, 0x2b, 0xba, 0x3f, 0x50, 0x80, 0x75, 0xd6, 0xff, 0x56, 0x5c, 0x77, 0xe5, 0x59, 0xf0, 0x80, 0x3c, 0x07, 0x92, 0xe0, 0x30, 0x2b, 0xf1},
|
||||
},
|
||||
{
|
||||
Index: 1,
|
||||
Pubkey: phase0.BLSPubKey{0xb3, 0xd8, 0x9e, 0x2f, 0x29, 0xc7, 0x12, 0xc6, 0xa9, 0xf8, 0xe5, 0xa2, 0x69, 0xb9, 0x76, 0x17, 0xc4, 0xa9, 0x4d, 0xd6, 0xf6, 0x66, 0x2a, 0xb3, 0xb0, 0x7c, 0xe9, 0xe5, 0x43, 0x45, 0x73, 0xf1, 0x5b, 0x5c, 0x98, 0x8c, 0xd1, 0x4b, 0xbd, 0x58, 0x04, 0xf7, 0x71, 0x56, 0xa8, 0xaf, 0x1c, 0xfa},
|
||||
WithdrawalCredentials: []byte{0x00, 0x78, 0x6c, 0xb0, 0x2e, 0xd2, 0x8e, 0x5f, 0xbb, 0x1f, 0x7f, 0x9e, 0x93, 0x1a, 0x2b, 0x72, 0x69, 0x29, 0x06, 0xe6, 0xb1, 0x2c, 0xe4, 0x64, 0x39, 0x75, 0xe3, 0x2b, 0x51, 0x76, 0x91, 0xf2},
|
||||
},
|
||||
},
|
||||
GenesisValidatorsRoot: phase0.Root{},
|
||||
Epoch: 1,
|
||||
CurrentForkVersion: phase0.Version{},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
command *command
|
||||
expected *phase0.SignedVoluntaryExit
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "MnemonicInvalid",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon",
|
||||
validator: "0",
|
||||
chainInfo: chainInfo,
|
||||
},
|
||||
err: "mnemonic is invalid",
|
||||
},
|
||||
{
|
||||
name: "ValidatorMissing",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
chainInfo: chainInfo,
|
||||
},
|
||||
err: "no validator specified",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
validator: "0",
|
||||
chainInfo: chainInfo,
|
||||
},
|
||||
expected: &phase0.SignedVoluntaryExit{
|
||||
Message: &phase0.VoluntaryExit{
|
||||
Epoch: 1,
|
||||
ValidatorIndex: 0,
|
||||
},
|
||||
Signature: phase0.BLSSignature{0x89, 0xf5, 0xc4, 0x42, 0x88, 0xf9, 0x5e, 0x19, 0xb6, 0xc1, 0x39, 0xf2, 0x62, 0x30, 0x05, 0x66, 0x5b, 0x98, 0x34, 0x62, 0xa2, 0x28, 0x12, 0x09, 0x77, 0xd8, 0x1f, 0x2e, 0xf5, 0x47, 0x56, 0x0b, 0xe2, 0x24, 0x46, 0xde, 0x21, 0xa8, 0xa9, 0x37, 0xd9, 0xdd, 0xa4, 0xe2, 0xd2, 0xec, 0x41, 0x75, 0x19, 0x64, 0x96, 0xcd, 0xd1, 0x30, 0x6d, 0xec, 0x4a, 0x12, 0x5f, 0x8c, 0x86, 0x1f, 0x80, 0x61, 0x71, 0x50, 0x4a, 0x9d, 0x6a, 0x61, 0x0e, 0xc4, 0xe1, 0x35, 0x04, 0x7e, 0x4f, 0xb6, 0x70, 0x52, 0xec, 0xc4, 0x56, 0x13, 0x60, 0xd0, 0xc3, 0xde, 0x04, 0xb6, 0xfb, 0xc4, 0x47, 0x42, 0x23, 0xff},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "GoodPubkey",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
validator: "0xb384f767d964e100c8a9b21018d08c25ffebae268b3ab6d610353897541971726dbfc3c7463884c68a531515aab94c87",
|
||||
chainInfo: chainInfo,
|
||||
},
|
||||
expected: &phase0.SignedVoluntaryExit{
|
||||
Message: &phase0.VoluntaryExit{
|
||||
Epoch: 1,
|
||||
ValidatorIndex: 0,
|
||||
},
|
||||
Signature: phase0.BLSSignature{0x89, 0xf5, 0xc4, 0x42, 0x88, 0xf9, 0x5e, 0x19, 0xb6, 0xc1, 0x39, 0xf2, 0x62, 0x30, 0x05, 0x66, 0x5b, 0x98, 0x34, 0x62, 0xa2, 0x28, 0x12, 0x09, 0x77, 0xd8, 0x1f, 0x2e, 0xf5, 0x47, 0x56, 0x0b, 0xe2, 0x24, 0x46, 0xde, 0x21, 0xa8, 0xa9, 0x37, 0xd9, 0xdd, 0xa4, 0xe2, 0xd2, 0xec, 0x41, 0x75, 0x19, 0x64, 0x96, 0xcd, 0xd1, 0x30, 0x6d, 0xec, 0x4a, 0x12, 0x5f, 0x8c, 0x86, 0x1f, 0x80, 0x61, 0x71, 0x50, 0x4a, 0x9d, 0x6a, 0x61, 0x0e, 0xc4, 0xe1, 0x35, 0x04, 0x7e, 0x4f, 0xb6, 0x70, 0x52, 0xec, 0xc4, 0x56, 0x13, 0x60, 0xd0, 0xc3, 0xde, 0x04, 0xb6, 0xfb, 0xc4, 0x47, 0x42, 0x23, 0xff},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
err := test.command.generateOperationFromMnemonicAndValidator(ctx)
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.expected, test.command.signedOperation)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateOperationFromSeedAndPath(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
require.NoError(t, e2types.InitBLS())
|
||||
|
||||
chainInfo := &beacon.ChainInfo{
|
||||
Version: 1,
|
||||
Validators: []*beacon.ValidatorInfo{
|
||||
{
|
||||
Index: 0,
|
||||
Pubkey: phase0.BLSPubKey{0xb3, 0x84, 0xf7, 0x67, 0xd9, 0x64, 0xe1, 0x00, 0xc8, 0xa9, 0xb2, 0x10, 0x18, 0xd0, 0x8c, 0x25, 0xff, 0xeb, 0xae, 0x26, 0x8b, 0x3a, 0xb6, 0xd6, 0x10, 0x35, 0x38, 0x97, 0x54, 0x19, 0x71, 0x72, 0x6d, 0xbf, 0xc3, 0xc7, 0x46, 0x38, 0x84, 0xc6, 0x8a, 0x53, 0x15, 0x15, 0xaa, 0xb9, 0x4c, 0x87},
|
||||
WithdrawalCredentials: []byte{0x00, 0x8b, 0xa1, 0xcc, 0x4b, 0x09, 0x1b, 0x91, 0xc1, 0x20, 0x2b, 0xba, 0x3f, 0x50, 0x80, 0x75, 0xd6, 0xff, 0x56, 0x5c, 0x77, 0xe5, 0x59, 0xf0, 0x80, 0x3c, 0x07, 0x92, 0xe0, 0x30, 0x2b, 0xf1},
|
||||
},
|
||||
{
|
||||
Index: 1,
|
||||
Pubkey: phase0.BLSPubKey{0xb3, 0xd8, 0x9e, 0x2f, 0x29, 0xc7, 0x12, 0xc6, 0xa9, 0xf8, 0xe5, 0xa2, 0x69, 0xb9, 0x76, 0x17, 0xc4, 0xa9, 0x4d, 0xd6, 0xf6, 0x66, 0x2a, 0xb3, 0xb0, 0x7c, 0xe9, 0xe5, 0x43, 0x45, 0x73, 0xf1, 0x5b, 0x5c, 0x98, 0x8c, 0xd1, 0x4b, 0xbd, 0x58, 0x04, 0xf7, 0x71, 0x56, 0xa8, 0xaf, 0x1c, 0xfa},
|
||||
WithdrawalCredentials: []byte{0x00, 0x78, 0x6c, 0xb0, 0x2e, 0xd2, 0x8e, 0x5f, 0xbb, 0x1f, 0x7f, 0x9e, 0x93, 0x1a, 0x2b, 0x72, 0x69, 0x29, 0x06, 0xe6, 0xb1, 0x2c, 0xe4, 0x64, 0x39, 0x75, 0xe3, 0x2b, 0x51, 0x76, 0x91, 0xf2},
|
||||
},
|
||||
{
|
||||
Index: 2,
|
||||
Pubkey: phase0.BLSPubKey{0xaf, 0x9c, 0xe4, 0x4f, 0x50, 0x14, 0x8d, 0xb4, 0x12, 0x19, 0x4a, 0xf0, 0xba, 0xf0, 0xba, 0xb3, 0x6b, 0xd5, 0xc3, 0xe0, 0xc4, 0x93, 0x89, 0x11, 0xa4, 0xe5, 0x02, 0xe3, 0x98, 0xb5, 0x9e, 0x5c, 0xca, 0x7c, 0x78, 0xe3, 0xfe, 0x03, 0x41, 0x95, 0x47, 0x88, 0x79, 0xee, 0xb2, 0x3d, 0xb0, 0xa6},
|
||||
WithdrawalCredentials: []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x93, 0x1a, 0x2b, 0x72, 0x69, 0x29, 0x06, 0xe6, 0xb1, 0x2c, 0xe4, 0x64, 0x39, 0x75, 0xe3, 0x2b, 0x51, 0x76, 0x91, 0xf2},
|
||||
},
|
||||
{
|
||||
Index: 3,
|
||||
Pubkey: phase0.BLSPubKey{0x86, 0xd3, 0x30, 0xaf, 0x51, 0xfa, 0x59, 0x3f, 0xa9, 0xf9, 0x3e, 0xdb, 0x9d, 0x16, 0x64, 0x01, 0x86, 0xbe, 0x2e, 0x93, 0xea, 0x94, 0xd2, 0x59, 0x78, 0x1e, 0x1e, 0xb3, 0x4d, 0xeb, 0x84, 0x4c, 0x39, 0x68, 0xd7, 0x5e, 0xa9, 0x1d, 0x19, 0xf1, 0x59, 0xdb, 0xd0, 0x52, 0x3c, 0x6c, 0x5b, 0xa5},
|
||||
WithdrawalCredentials: []byte{0x00, 0x81, 0x68, 0x45, 0x6b, 0x6d, 0x9a, 0x32, 0x83, 0x93, 0x1f, 0xea, 0x52, 0x10, 0xda, 0x12, 0x2d, 0x1e, 0x65, 0xe8, 0xed, 0x50, 0xb8, 0xe8, 0xf5, 0x91, 0x11, 0x83, 0xb0, 0x2f, 0xd1, 0x25},
|
||||
},
|
||||
},
|
||||
GenesisValidatorsRoot: phase0.Root{},
|
||||
Epoch: 1,
|
||||
CurrentForkVersion: phase0.Version{},
|
||||
}
|
||||
validators := make(map[string]*beacon.ValidatorInfo, len(chainInfo.Validators))
|
||||
for i := range chainInfo.Validators {
|
||||
validators[fmt.Sprintf("%#x", chainInfo.Validators[i].Pubkey)] = chainInfo.Validators[i]
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
command *command
|
||||
seed []byte
|
||||
path string
|
||||
err string
|
||||
expected *phase0.SignedVoluntaryExit
|
||||
}{
|
||||
{
|
||||
name: "PathInvalid",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
chainInfo: chainInfo,
|
||||
},
|
||||
seed: []byte{0x40, 0x8b, 0x28, 0x5c, 0x12, 0x38, 0x36, 0x00, 0x4f, 0x4b, 0x88, 0x42, 0xc8, 0x93, 0x24, 0xc1, 0xf0, 0x13, 0x82, 0x45, 0x0c, 0x0d, 0x43, 0x9a, 0xf3, 0x45, 0xba, 0x7f, 0xc4, 0x9a, 0xcf, 0x70, 0x54, 0x89, 0xc6, 0xfc, 0x77, 0xdb, 0xd4, 0xe3, 0xdc, 0x1d, 0xd8, 0xcc, 0x6b, 0xc9, 0xf0, 0x43, 0xdb, 0x8a, 0xda, 0x1e, 0x24, 0x3c, 0x4a, 0x0e, 0xaf, 0xb2, 0x90, 0xd3, 0x99, 0x48, 0x08, 0x40},
|
||||
path: "invalid",
|
||||
err: "failed to generate validator private key: not master at path component 0",
|
||||
},
|
||||
{
|
||||
name: "ValidatorUnknown",
|
||||
command: &command{
|
||||
chainInfo: chainInfo,
|
||||
},
|
||||
seed: []byte{0x40, 0x8b, 0x28, 0x5c, 0x12, 0x38, 0x36, 0x00, 0x4f, 0x4b, 0x88, 0x42, 0xc8, 0x93, 0x24, 0xc1, 0xf0, 0x13, 0x82, 0x45, 0x0c, 0x0d, 0x43, 0x9a, 0xf3, 0x45, 0xba, 0x7f, 0xc4, 0x9a, 0xcf, 0x70, 0x54, 0x89, 0xc6, 0xfc, 0x77, 0xdb, 0xd4, 0xe3, 0xdc, 0x1d, 0xd8, 0xcc, 0x6b, 0xc9, 0xf0, 0x43, 0xdb, 0x8a, 0xda, 0x1e, 0x24, 0x3c, 0x4a, 0x0e, 0xaf, 0xb2, 0x90, 0xd3, 0x99, 0x48, 0x08, 0x40},
|
||||
path: "m/12381/3600/999/0/0",
|
||||
err: "unknown validator",
|
||||
},
|
||||
// {
|
||||
// name: "ValidatorAlreadyExited",
|
||||
// command: &command{
|
||||
// chainInfo: chainInfo,
|
||||
// },
|
||||
// seed: []byte{0x40, 0x8b, 0x28, 0x5c, 0x12, 0x38, 0x36, 0x00, 0x4f, 0x4b, 0x88, 0x42, 0xc8, 0x93, 0x24, 0xc1, 0xf0, 0x13, 0x82, 0x45, 0x0c, 0x0d, 0x43, 0x9a, 0xf3, 0x45, 0xba, 0x7f, 0xc4, 0x9a, 0xcf, 0x70, 0x54, 0x89, 0xc6, 0xfc, 0x77, 0xdb, 0xd4, 0xe3, 0xdc, 0x1d, 0xd8, 0xcc, 0x6b, 0xc9, 0xf0, 0x43, 0xdb, 0x8a, 0xda, 0x1e, 0x24, 0x3c, 0x4a, 0x0e, 0xaf, 0xb2, 0x90, 0xd3, 0x99, 0x48, 0x08, 0x40},
|
||||
// path: "m/12381/3600/2/0/0",
|
||||
// },
|
||||
{
|
||||
name: "GoodPath0",
|
||||
command: &command{
|
||||
chainInfo: chainInfo,
|
||||
},
|
||||
seed: []byte{0x40, 0x8b, 0x28, 0x5c, 0x12, 0x38, 0x36, 0x00, 0x4f, 0x4b, 0x88, 0x42, 0xc8, 0x93, 0x24, 0xc1, 0xf0, 0x13, 0x82, 0x45, 0x0c, 0x0d, 0x43, 0x9a, 0xf3, 0x45, 0xba, 0x7f, 0xc4, 0x9a, 0xcf, 0x70, 0x54, 0x89, 0xc6, 0xfc, 0x77, 0xdb, 0xd4, 0xe3, 0xdc, 0x1d, 0xd8, 0xcc, 0x6b, 0xc9, 0xf0, 0x43, 0xdb, 0x8a, 0xda, 0x1e, 0x24, 0x3c, 0x4a, 0x0e, 0xaf, 0xb2, 0x90, 0xd3, 0x99, 0x48, 0x08, 0x40},
|
||||
path: "m/12381/3600/0/0/0",
|
||||
expected: &phase0.SignedVoluntaryExit{
|
||||
Message: &phase0.VoluntaryExit{
|
||||
Epoch: 1,
|
||||
ValidatorIndex: 0,
|
||||
},
|
||||
Signature: phase0.BLSSignature{0x89, 0xf5, 0xc4, 0x42, 0x88, 0xf9, 0x5e, 0x19, 0xb6, 0xc1, 0x39, 0xf2, 0x62, 0x30, 0x05, 0x66, 0x5b, 0x98, 0x34, 0x62, 0xa2, 0x28, 0x12, 0x09, 0x77, 0xd8, 0x1f, 0x2e, 0xf5, 0x47, 0x56, 0x0b, 0xe2, 0x24, 0x46, 0xde, 0x21, 0xa8, 0xa9, 0x37, 0xd9, 0xdd, 0xa4, 0xe2, 0xd2, 0xec, 0x41, 0x75, 0x19, 0x64, 0x96, 0xcd, 0xd1, 0x30, 0x6d, 0xec, 0x4a, 0x12, 0x5f, 0x8c, 0x86, 0x1f, 0x80, 0x61, 0x71, 0x50, 0x4a, 0x9d, 0x6a, 0x61, 0x0e, 0xc4, 0xe1, 0x35, 0x04, 0x7e, 0x4f, 0xb6, 0x70, 0x52, 0xec, 0xc4, 0x56, 0x13, 0x60, 0xd0, 0xc3, 0xde, 0x04, 0xb6, 0xfb, 0xc4, 0x47, 0x42, 0x23, 0xff},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "GoodPath3",
|
||||
command: &command{
|
||||
chainInfo: chainInfo,
|
||||
},
|
||||
seed: []byte{0x40, 0x8b, 0x28, 0x5c, 0x12, 0x38, 0x36, 0x00, 0x4f, 0x4b, 0x88, 0x42, 0xc8, 0x93, 0x24, 0xc1, 0xf0, 0x13, 0x82, 0x45, 0x0c, 0x0d, 0x43, 0x9a, 0xf3, 0x45, 0xba, 0x7f, 0xc4, 0x9a, 0xcf, 0x70, 0x54, 0x89, 0xc6, 0xfc, 0x77, 0xdb, 0xd4, 0xe3, 0xdc, 0x1d, 0xd8, 0xcc, 0x6b, 0xc9, 0xf0, 0x43, 0xdb, 0x8a, 0xda, 0x1e, 0x24, 0x3c, 0x4a, 0x0e, 0xaf, 0xb2, 0x90, 0xd3, 0x99, 0x48, 0x08, 0x40},
|
||||
path: "m/12381/3600/3/0/0",
|
||||
expected: &phase0.SignedVoluntaryExit{
|
||||
Message: &phase0.VoluntaryExit{
|
||||
Epoch: 1,
|
||||
ValidatorIndex: 3,
|
||||
},
|
||||
Signature: phase0.BLSSignature{0x99, 0x78, 0xb4, 0x9c, 0x21, 0x60, 0x3f, 0x04, 0xa3, 0x04, 0x4e, 0x4c, 0x49, 0x0c, 0xb4, 0x68, 0x7c, 0x6e, 0x14, 0xc2, 0xda, 0xed, 0x25, 0x92, 0xe0, 0x02, 0x2d, 0xcd, 0x63, 0xeb, 0xe7, 0x4a, 0xf1, 0x1a, 0xca, 0xba, 0xae, 0x50, 0xe1, 0x8a, 0x1d, 0xae, 0x96, 0xd9, 0xd2, 0x56, 0xbf, 0x9f, 0x02, 0x48, 0x85, 0x05, 0xc1, 0xfb, 0xb3, 0x4a, 0x0b, 0x68, 0xec, 0xc5, 0xb5, 0xf5, 0xea, 0x53, 0xdb, 0xd0, 0x09, 0x08, 0xe3, 0x1e, 0xa8, 0xca, 0x9d, 0x02, 0x08, 0x3b, 0x9e, 0xf1, 0xc7, 0xd2, 0x32, 0xf4, 0xba, 0xd9, 0xea, 0x56, 0x4b, 0xc5, 0x87, 0xd5, 0x27, 0xb7, 0x74, 0x97, 0x8a, 0xee},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
err := test.command.generateOperationFromSeedAndPath(ctx, validators, test.seed, test.path)
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.expected, test.command.signedOperation)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyOperation(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
require.NoError(t, e2types.InitBLS())
|
||||
|
||||
chainInfo := &beacon.ChainInfo{
|
||||
Version: 1,
|
||||
Validators: []*beacon.ValidatorInfo{
|
||||
{
|
||||
Index: 0,
|
||||
Pubkey: phase0.BLSPubKey{0xb3, 0x84, 0xf7, 0x67, 0xd9, 0x64, 0xe1, 0x00, 0xc8, 0xa9, 0xb2, 0x10, 0x18, 0xd0, 0x8c, 0x25, 0xff, 0xeb, 0xae, 0x26, 0x8b, 0x3a, 0xb6, 0xd6, 0x10, 0x35, 0x38, 0x97, 0x54, 0x19, 0x71, 0x72, 0x6d, 0xbf, 0xc3, 0xc7, 0x46, 0x38, 0x84, 0xc6, 0x8a, 0x53, 0x15, 0x15, 0xaa, 0xb9, 0x4c, 0x87},
|
||||
WithdrawalCredentials: []byte{0x00, 0x8b, 0xa1, 0xcc, 0x4b, 0x09, 0x1b, 0x91, 0xc1, 0x20, 0x2b, 0xba, 0x3f, 0x50, 0x80, 0x75, 0xd6, 0xff, 0x56, 0x5c, 0x77, 0xe5, 0x59, 0xf0, 0x80, 0x3c, 0x07, 0x92, 0xe0, 0x30, 0x2b, 0xf1},
|
||||
},
|
||||
{
|
||||
Index: 1,
|
||||
Pubkey: phase0.BLSPubKey{0xb3, 0xd8, 0x9e, 0x2f, 0x29, 0xc7, 0x12, 0xc6, 0xa9, 0xf8, 0xe5, 0xa2, 0x69, 0xb9, 0x76, 0x17, 0xc4, 0xa9, 0x4d, 0xd6, 0xf6, 0x66, 0x2a, 0xb3, 0xb0, 0x7c, 0xe9, 0xe5, 0x43, 0x45, 0x73, 0xf1, 0x5b, 0x5c, 0x98, 0x8c, 0xd1, 0x4b, 0xbd, 0x58, 0x04, 0xf7, 0x71, 0x56, 0xa8, 0xaf, 0x1c, 0xfa},
|
||||
WithdrawalCredentials: []byte{0x00, 0x78, 0x6c, 0xb0, 0x2e, 0xd2, 0x8e, 0x5f, 0xbb, 0x1f, 0x7f, 0x9e, 0x93, 0x1a, 0x2b, 0x72, 0x69, 0x29, 0x06, 0xe6, 0xb1, 0x2c, 0xe4, 0x64, 0x39, 0x75, 0xe3, 0x2b, 0x51, 0x76, 0x91, 0xf2},
|
||||
},
|
||||
},
|
||||
GenesisValidatorsRoot: phase0.Root{},
|
||||
Epoch: 1,
|
||||
CurrentForkVersion: phase0.Version{},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
command *command
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "SignatureMissing",
|
||||
command: &command{
|
||||
chainInfo: chainInfo,
|
||||
signedOperation: &phase0.SignedVoluntaryExit{
|
||||
Message: &phase0.VoluntaryExit{
|
||||
Epoch: 1,
|
||||
ValidatorIndex: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
err: "invalid signature",
|
||||
},
|
||||
{
|
||||
name: "SignatureShort",
|
||||
command: &command{
|
||||
chainInfo: chainInfo,
|
||||
signedOperation: &phase0.SignedVoluntaryExit{
|
||||
Message: &phase0.VoluntaryExit{
|
||||
Epoch: 1,
|
||||
ValidatorIndex: 0,
|
||||
},
|
||||
Signature: phase0.BLSSignature{0xf5, 0xc4, 0x42, 0x88, 0xf9, 0x5e, 0x19, 0xb6, 0xc1, 0x39, 0xf2, 0x62, 0x30, 0x05, 0x66, 0x5b, 0x98, 0x34, 0x62, 0xa2, 0x28, 0x12, 0x09, 0x77, 0xd8, 0x1f, 0x2e, 0xf5, 0x47, 0x56, 0x0b, 0xe2, 0x24, 0x46, 0xde, 0x21, 0xa8, 0xa9, 0x37, 0xd9, 0xdd, 0xa4, 0xe2, 0xd2, 0xec, 0x41, 0x75, 0x19, 0x64, 0x96, 0xcd, 0xd1, 0x30, 0x6d, 0xec, 0x4a, 0x12, 0x5f, 0x8c, 0x86, 0x1f, 0x80, 0x61, 0x71, 0x50, 0x4a, 0x9d, 0x6a, 0x61, 0x0e, 0xc4, 0xe1, 0x35, 0x04, 0x7e, 0x4f, 0xb6, 0x70, 0x52, 0xec, 0xc4, 0x56, 0x13, 0x60, 0xd0, 0xc3, 0xde, 0x04, 0xb6, 0xfb, 0xc4, 0x47, 0x42, 0x23, 0xff},
|
||||
},
|
||||
},
|
||||
err: "invalid signature",
|
||||
},
|
||||
{
|
||||
name: "SignatureIncorrect",
|
||||
command: &command{
|
||||
chainInfo: chainInfo,
|
||||
signedOperation: &phase0.SignedVoluntaryExit{
|
||||
Message: &phase0.VoluntaryExit{
|
||||
Epoch: 1,
|
||||
ValidatorIndex: 0,
|
||||
},
|
||||
Signature: phase0.BLSSignature{0x99, 0x78, 0xb4, 0x9c, 0x21, 0x60, 0x3f, 0x04, 0xa3, 0x04, 0x4e, 0x4c, 0x49, 0x0c, 0xb4, 0x68, 0x7c, 0x6e, 0x14, 0xc2, 0xda, 0xed, 0x25, 0x92, 0xe0, 0x02, 0x2d, 0xcd, 0x63, 0xeb, 0xe7, 0x4a, 0xf1, 0x1a, 0xca, 0xba, 0xae, 0x50, 0xe1, 0x8a, 0x1d, 0xae, 0x96, 0xd9, 0xd2, 0x56, 0xbf, 0x9f, 0x02, 0x48, 0x85, 0x05, 0xc1, 0xfb, 0xb3, 0x4a, 0x0b, 0x68, 0xec, 0xc5, 0xb5, 0xf5, 0xea, 0x53, 0xdb, 0xd0, 0x09, 0x08, 0xe3, 0x1e, 0xa8, 0xca, 0x9d, 0x02, 0x08, 0x3b, 0x9e, 0xf1, 0xc7, 0xd2, 0x32, 0xf4, 0xba, 0xd9, 0xea, 0x56, 0x4b, 0xc5, 0x87, 0xd5, 0x27, 0xb7, 0x74, 0x97, 0x8a, 0xee},
|
||||
},
|
||||
},
|
||||
err: "signature does not verify",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
command: &command{
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
validator: "0",
|
||||
chainInfo: chainInfo,
|
||||
signedOperation: &phase0.SignedVoluntaryExit{
|
||||
Message: &phase0.VoluntaryExit{
|
||||
Epoch: 1,
|
||||
ValidatorIndex: 0,
|
||||
},
|
||||
Signature: phase0.BLSSignature{0x89, 0xf5, 0xc4, 0x42, 0x88, 0xf9, 0x5e, 0x19, 0xb6, 0xc1, 0x39, 0xf2, 0x62, 0x30, 0x05, 0x66, 0x5b, 0x98, 0x34, 0x62, 0xa2, 0x28, 0x12, 0x09, 0x77, 0xd8, 0x1f, 0x2e, 0xf5, 0x47, 0x56, 0x0b, 0xe2, 0x24, 0x46, 0xde, 0x21, 0xa8, 0xa9, 0x37, 0xd9, 0xdd, 0xa4, 0xe2, 0xd2, 0xec, 0x41, 0x75, 0x19, 0x64, 0x96, 0xcd, 0xd1, 0x30, 0x6d, 0xec, 0x4a, 0x12, 0x5f, 0x8c, 0x86, 0x1f, 0x80, 0x61, 0x71, 0x50, 0x4a, 0x9d, 0x6a, 0x61, 0x0e, 0xc4, 0xe1, 0x35, 0x04, 0x7e, 0x4f, 0xb6, 0x70, 0x52, 0xec, 0xc4, 0x56, 0x13, 0x60, 0xd0, 0xc3, 0xde, 0x04, 0xb6, 0xfb, 0xc4, 0x47, 0x42, 0x23, 0xff},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
err := test.command.verifySignedOperation(ctx, test.command.signedOperation)
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestObtainOperationFromInput(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
require.NoError(t, e2types.InitBLS())
|
||||
|
||||
chainInfo := &beacon.ChainInfo{
|
||||
Version: 1,
|
||||
Validators: []*beacon.ValidatorInfo{
|
||||
{
|
||||
Index: 0,
|
||||
Pubkey: phase0.BLSPubKey{0xb3, 0x84, 0xf7, 0x67, 0xd9, 0x64, 0xe1, 0x00, 0xc8, 0xa9, 0xb2, 0x10, 0x18, 0xd0, 0x8c, 0x25, 0xff, 0xeb, 0xae, 0x26, 0x8b, 0x3a, 0xb6, 0xd6, 0x10, 0x35, 0x38, 0x97, 0x54, 0x19, 0x71, 0x72, 0x6d, 0xbf, 0xc3, 0xc7, 0x46, 0x38, 0x84, 0xc6, 0x8a, 0x53, 0x15, 0x15, 0xaa, 0xb9, 0x4c, 0x87},
|
||||
WithdrawalCredentials: []byte{0x00, 0x8b, 0xa1, 0xcc, 0x4b, 0x09, 0x1b, 0x91, 0xc1, 0x20, 0x2b, 0xba, 0x3f, 0x50, 0x80, 0x75, 0xd6, 0xff, 0x56, 0x5c, 0x77, 0xe5, 0x59, 0xf0, 0x80, 0x3c, 0x07, 0x92, 0xe0, 0x30, 0x2b, 0xf1},
|
||||
},
|
||||
{
|
||||
Index: 1,
|
||||
Pubkey: phase0.BLSPubKey{0xb3, 0xd8, 0x9e, 0x2f, 0x29, 0xc7, 0x12, 0xc6, 0xa9, 0xf8, 0xe5, 0xa2, 0x69, 0xb9, 0x76, 0x17, 0xc4, 0xa9, 0x4d, 0xd6, 0xf6, 0x66, 0x2a, 0xb3, 0xb0, 0x7c, 0xe9, 0xe5, 0x43, 0x45, 0x73, 0xf1, 0x5b, 0x5c, 0x98, 0x8c, 0xd1, 0x4b, 0xbd, 0x58, 0x04, 0xf7, 0x71, 0x56, 0xa8, 0xaf, 0x1c, 0xfa},
|
||||
WithdrawalCredentials: []byte{0x00, 0x78, 0x6c, 0xb0, 0x2e, 0xd2, 0x8e, 0x5f, 0xbb, 0x1f, 0x7f, 0x9e, 0x93, 0x1a, 0x2b, 0x72, 0x69, 0x29, 0x06, 0xe6, 0xb1, 0x2c, 0xe4, 0x64, 0x39, 0x75, 0xe3, 0x2b, 0x51, 0x76, 0x91, 0xf2},
|
||||
},
|
||||
},
|
||||
GenesisValidatorsRoot: phase0.Root{},
|
||||
Epoch: 1,
|
||||
CurrentForkVersion: phase0.Version{},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
command *command
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "InvalidFilename",
|
||||
command: &command{
|
||||
signedOperationInput: `[]`,
|
||||
chainInfo: chainInfo,
|
||||
},
|
||||
err: "failed to read input file: open []: no such file or directory",
|
||||
},
|
||||
{
|
||||
name: "InvalidJSON",
|
||||
command: &command{
|
||||
signedOperationInput: `{invalid}`,
|
||||
chainInfo: chainInfo,
|
||||
},
|
||||
err: "failed to parse exit operation input: invalid character 'i' looking for beginning of object key string",
|
||||
},
|
||||
{
|
||||
name: "Unverifable",
|
||||
command: &command{
|
||||
signedOperationInput: `{"message":{"epoch":"1","validator_index":"0"},"signature":"0x9978b49c21603f04a3044e4c490cb4687c6e14c2daed2592e0022dcd63ebe74af11acabaae50e18a1dae96d9d256bf9f02488505c1fbb34a0b68ecc5b5f5ea53dbd00908e31ea8ca9d02083b9ef1c7d232f4bad9ea564bc587d527b774978aee"}`,
|
||||
chainInfo: chainInfo,
|
||||
},
|
||||
err: "signature does not verify",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
command: &command{
|
||||
signedOperationInput: `{"message":{"epoch":"1","validator_index":"0"},"signature":"0x89f5c44288f95e19b6c139f2623005665b983462a228120977d81f2ef547560be22446de21a8a937d9dda4e2d2ec4175196496cdd1306dec4a125f8c861f806171504a9d6a610ec4e135047e4fb67052ecc4561360d0c3de04b6fbc4474223ff"}`,
|
||||
chainInfo: chainInfo,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
err := test.command.obtainOperationFromInput(ctx)
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2019, 2020 Weald Technology Trading
|
||||
// Copyright © 2023 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
|
||||
@@ -21,19 +21,19 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// Run runs the wallet create data command.
|
||||
// Run runs the command.
|
||||
func Run(cmd *cobra.Command) (string, error) {
|
||||
ctx := context.Background()
|
||||
dataIn, err := input(ctx)
|
||||
|
||||
c, err := newCommand(ctx)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to obtain input")
|
||||
return "", errors.Wrap(err, "failed to set up command")
|
||||
}
|
||||
|
||||
// Further errors do not need a usage report.
|
||||
cmd.SilenceUsage = true
|
||||
|
||||
dataOut, err := process(ctx, dataIn)
|
||||
if err != nil {
|
||||
if err := c.process(ctx); err != nil {
|
||||
return "", errors.Wrap(err, "failed to process")
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ func Run(cmd *cobra.Command) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
results, err := output(ctx, dataOut)
|
||||
results, err := c.output(ctx)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to obtain output")
|
||||
}
|
||||
|
||||
@@ -130,7 +130,6 @@ func (c *command) setup(ctx context.Context) error {
|
||||
|
||||
chainTime, err := standardchaintime.New(ctx,
|
||||
standardchaintime.WithSpecProvider(c.eth2Client.(eth2client.SpecProvider)),
|
||||
standardchaintime.WithForkScheduleProvider(c.eth2Client.(eth2client.ForkScheduleProvider)),
|
||||
standardchaintime.WithGenesisTimeProvider(c.eth2Client.(eth2client.GenesisTimeProvider)),
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -384,7 +384,6 @@ func (c *command) setup(ctx context.Context) error {
|
||||
|
||||
c.chainTime, err = standardchaintime.New(ctx,
|
||||
standardchaintime.WithSpecProvider(c.eth2Client.(eth2client.SpecProvider)),
|
||||
standardchaintime.WithForkScheduleProvider(c.eth2Client.(eth2client.ForkScheduleProvider)),
|
||||
standardchaintime.WithGenesisTimeProvider(c.eth2Client.(eth2client.GenesisTimeProvider)),
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -40,13 +40,14 @@ func (c *command) process(ctx context.Context) error {
|
||||
return c.calculateYield(ctx)
|
||||
}
|
||||
|
||||
var weiPerGwei = decimal.New(1e9, 0)
|
||||
var one = decimal.New(1, 0)
|
||||
var epochsPerYear = decimal.New(225*365, 0)
|
||||
var (
|
||||
weiPerGwei = decimal.New(1e9, 0)
|
||||
one = decimal.New(1, 0)
|
||||
epochsPerYear = decimal.New(225*365, 0)
|
||||
)
|
||||
|
||||
// calculateYield calculates yield from the number of active validators.
|
||||
func (c *command) calculateYield(ctx context.Context) error {
|
||||
|
||||
spec, err := c.eth2Client.(eth2client.SpecProvider).Spec(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -119,7 +120,6 @@ func (c *command) setup(ctx context.Context) error {
|
||||
if c.validators == "" {
|
||||
chainTime, err := standardchaintime.New(ctx,
|
||||
standardchaintime.WithSpecProvider(c.eth2Client.(eth2client.SpecProvider)),
|
||||
standardchaintime.WithForkScheduleProvider(c.eth2Client.(eth2client.ForkScheduleProvider)),
|
||||
standardchaintime.WithGenesisTimeProvider(c.eth2Client.(eth2client.GenesisTimeProvider)),
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// validatorCredentialsCmd represents the validator credentials command
|
||||
// validatorCredentialsCmd represents the validator credentials command.
|
||||
var validatorCredentialsCmd = &cobra.Command{
|
||||
Use: "credentials",
|
||||
Short: "Manage Ethereum consensus validator credentials",
|
||||
|
||||
@@ -28,12 +28,14 @@ var validatorCredentialsSetCmd = &cobra.Command{
|
||||
|
||||
ethdo validator credentials set --validator=primary/validator --withdrawal-address=0x00...13 --private-key=0x00...1f
|
||||
|
||||
The existing account can be specified in one of a number of ways:
|
||||
The validator account can be specified in one of a number of ways:
|
||||
|
||||
- mnemonic using --mnemonic; this will scan the mnemonic and generate all required operations
|
||||
- mnemonic using --mnemonic; this will scan the mnemonic and generate all applicable operations
|
||||
- mnemonic and path to the validator key using --mnemonic and --path; this will generate a single operation
|
||||
- private key using --private-key; this will generate a single operation
|
||||
- account and passphrase using --account and --passphrase; this will generate a single operation
|
||||
- mnemonic and validator index or public key --mnemonic and --validator; this will generate a single operation
|
||||
- mnemonic and withdrawal private key using --mnemonic and --private-key; this will generate all applicable operations
|
||||
- validator and withdrawal private key using --validator and --private-key; this will generate a single operation
|
||||
- account and withdrawal account using --account and --withdrawal-account; this will generate a single operation
|
||||
|
||||
In quiet mode this will return 0 if the credentials operation has been generated (and successfully broadcast if online), otherwise 1.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
@@ -56,8 +58,9 @@ func init() {
|
||||
validatorCredentialsFlags(validatorCredentialsSetCmd)
|
||||
validatorCredentialsSetCmd.Flags().Bool("prepare-offline", false, "Create files for offline use")
|
||||
validatorCredentialsSetCmd.Flags().String("validator", "", "Validator for which to set validator credentials")
|
||||
validatorCredentialsSetCmd.Flags().String("withdrawal-account", "", "Account with which the validator's withdrawal credentials were set")
|
||||
validatorCredentialsSetCmd.Flags().String("withdrawal-address", "", "Execution address to which to direct withdrawals")
|
||||
validatorCredentialsSetCmd.Flags().String("signed-operation", "", "Use pre-defined JSON signed operation as created by --json to transmit the credentials change operation")
|
||||
validatorCredentialsSetCmd.Flags().String("signed-operations", "", "Use pre-defined JSON signed operation as created by --json to transmit the credentials change operation (reads from change-operations.json if not present)")
|
||||
validatorCredentialsSetCmd.Flags().Bool("json", false, "Generate JSON data containing a signed operation rather than broadcast it to the network (implied when offline)")
|
||||
validatorCredentialsSetCmd.Flags().Bool("offline", false, "Do not attempt to connect to a beacon node to obtain information for the operation")
|
||||
validatorCredentialsSetCmd.Flags().String("fork-version", "", "Fork version to use for signing (overrides fetching from beacon node)")
|
||||
@@ -71,7 +74,10 @@ func validatorCredentialsSetBindings() {
|
||||
if err := viper.BindPFlag("validator", validatorCredentialsSetCmd.Flags().Lookup("validator")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("signed-operation", validatorCredentialsSetCmd.Flags().Lookup("signed-operation")); err != nil {
|
||||
if err := viper.BindPFlag("signed-operations", validatorCredentialsSetCmd.Flags().Lookup("signed-operations")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("withdrawal-account", validatorCredentialsSetCmd.Flags().Lookup("withdrawal-account")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("withdrawal-address", validatorCredentialsSetCmd.Flags().Lookup("withdrawal-address")); err != nil {
|
||||
|
||||
@@ -32,7 +32,7 @@ If validatoraccount is provided with an account path it will generate deposit da
|
||||
|
||||
The information generated can be passed to ethereal to create a deposit from the Ethereum 1 chain.
|
||||
|
||||
In quiet mode this will return 0 if the the data can be generated correctly, otherwise 1.`,
|
||||
In quiet mode this will return 0 if the data can be generated correctly, otherwise 1.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
res, err := validatordepositdata.Run(cmd)
|
||||
if err != nil {
|
||||
|
||||
@@ -30,7 +30,7 @@ var validatorDutiesCmd = &cobra.Command{
|
||||
|
||||
Attester duties are known for the current and next epoch. Proposer duties are known for the current epoch.
|
||||
|
||||
In quiet mode this will return 0 if the the duties have been obtained, otherwise 1.`,
|
||||
In quiet mode this will return 0 if the duties have been obtained, otherwise 1.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
res, err := validatorduties.Run(cmd)
|
||||
if err != nil {
|
||||
|
||||
@@ -26,9 +26,16 @@ var validatorExitCmd = &cobra.Command{
|
||||
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
|
||||
ethdo validator exit --validator=12345
|
||||
|
||||
In quiet mode this will return 0 if the transaction has been generated, otherwise 1.`,
|
||||
The validator and key can be specified in one of a number of ways:
|
||||
|
||||
- mnemonic and path to the validator using --mnemonic and --path
|
||||
- mnemonic and validator index or public key using --mnemonic and --validator
|
||||
- validator private key using --private-key
|
||||
- validator account using --validator
|
||||
|
||||
In quiet mode this will return 0 if the exit operation has been generated (and successfully broadcast if online), otherwise 1.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
res, err := validatorexit.Run(cmd)
|
||||
if err != nil {
|
||||
@@ -48,22 +55,38 @@ func init() {
|
||||
validatorCmd.AddCommand(validatorExitCmd)
|
||||
validatorFlags(validatorExitCmd)
|
||||
validatorExitCmd.Flags().Int64("epoch", -1, "Epoch at which to exit (defaults to current epoch)")
|
||||
validatorExitCmd.Flags().String("key", "", "Private key if validator not known by ethdo")
|
||||
validatorExitCmd.Flags().String("exit", "", "Use pre-defined JSON data as created by --json to exit")
|
||||
validatorExitCmd.Flags().Bool("json", false, "Generate JSON data for an exit; do not broadcast to network")
|
||||
validatorExitCmd.Flags().Bool("prepare-offline", false, "Create files for offline use")
|
||||
validatorExitCmd.Flags().String("validator", "", "Validator to exit")
|
||||
validatorExitCmd.Flags().String("signed-operation", "", "Use pre-defined JSON signed operation as created by --json to transmit the exit operation (reads from exit-operation.json if not present)")
|
||||
validatorExitCmd.Flags().Bool("json", false, "Generate JSON data containing a signed operation rather than broadcast it to the network (implied when offline)")
|
||||
validatorExitCmd.Flags().Bool("offline", false, "Do not attempt to connect to a beacon node to obtain information for the operation")
|
||||
validatorExitCmd.Flags().String("fork-version", "", "Fork version to use for signing (overrides fetching from beacon node)")
|
||||
validatorExitCmd.Flags().String("genesis-validators-root", "", "Genesis validators root to use for signing (overrides fetching from beacon node)")
|
||||
}
|
||||
|
||||
func validatorExitBindings() {
|
||||
if err := viper.BindPFlag("epoch", validatorExitCmd.Flags().Lookup("epoch")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("key", validatorExitCmd.Flags().Lookup("key")); err != nil {
|
||||
if err := viper.BindPFlag("prepare-offline", validatorExitCmd.Flags().Lookup("prepare-offline")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("exit", validatorExitCmd.Flags().Lookup("exit")); err != nil {
|
||||
if err := viper.BindPFlag("validator", validatorExitCmd.Flags().Lookup("validator")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("signed-operation", validatorExitCmd.Flags().Lookup("signed-operation")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("json", validatorExitCmd.Flags().Lookup("json")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("offline", validatorExitCmd.Flags().Lookup("offline")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("fork-version", validatorExitCmd.Flags().Lookup("fork-version")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("genesis-validators-root", validatorExitCmd.Flags().Lookup("genesis-validators-root")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ In quiet mode this will return 0 if the validator information can be obtained, o
|
||||
},
|
||||
}
|
||||
|
||||
// graphData returns data from the graph about number and amount of deposits
|
||||
// graphData returns data from the graph about number and amount of deposits.
|
||||
func graphData(network string, validatorPubKey []byte) (uint64, spec.Gwei, error) {
|
||||
subgraph := ""
|
||||
if network == "Mainnet" {
|
||||
@@ -119,8 +119,12 @@ func graphData(network string, validatorPubKey []byte) (uint64, spec.Gwei, error
|
||||
}
|
||||
query := fmt.Sprintf(`{"query": "{deposits(where: {validatorPubKey:\"%#x\"}) { id amount withdrawalCredentials }}"}`, validatorPubKey)
|
||||
url := fmt.Sprintf("https://api.thegraph.com/subgraphs/name/%s", subgraph)
|
||||
// #nosec G107
|
||||
graphResp, err := http.Post(url, "application/json", bytes.NewBufferString(query))
|
||||
req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, url, bytes.NewBufferString(query))
|
||||
if err != nil {
|
||||
return 0, 0, errors.Wrap(err, "failed to start request")
|
||||
}
|
||||
req.Header.Set("Accept", "application/json")
|
||||
graphResp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return 0, 0, errors.Wrap(err, "failed to check if there is already a deposit for this validator")
|
||||
}
|
||||
@@ -131,8 +135,10 @@ func graphData(network string, validatorPubKey []byte) (uint64, spec.Gwei, error
|
||||
}
|
||||
|
||||
type graphDeposit struct {
|
||||
Index string `json:"index"`
|
||||
Amount string `json:"amount"`
|
||||
Index string `json:"index"`
|
||||
Amount string `json:"amount"`
|
||||
// Using graph API JSON names in camel case.
|
||||
//nolint:tagliatelle
|
||||
WithdrawalCredentials string `json:"withdrawalCredentials"`
|
||||
}
|
||||
type graphData struct {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2019 - 2022 Weald Technology Trading.
|
||||
// Copyright © 2019 - 2023 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
|
||||
@@ -24,9 +24,9 @@ import (
|
||||
|
||||
// ReleaseVersion is the release version of the codebase.
|
||||
// Usually overridden by tag names when building binaries.
|
||||
var ReleaseVersion = "local build (latest release 1.26.5)"
|
||||
var ReleaseVersion = "local build (latest release 1.28.1)"
|
||||
|
||||
// versionCmd represents the version command
|
||||
// versionCmd represents the version command.
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Version of ethdo",
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// walletCmd represents the wallet command
|
||||
// walletCmd represents the wallet command.
|
||||
var walletCmd = &cobra.Command{
|
||||
Use: "wallet",
|
||||
Short: "Manage wallets",
|
||||
|
||||
@@ -74,7 +74,7 @@ func process(ctx context.Context, data *dataIn) (*dataOut, error) {
|
||||
return nil, errors.Wrap(err, "failed to marshal shamir export")
|
||||
}
|
||||
|
||||
if err := os.WriteFile(data.file, sharedFile, 0600); err != nil {
|
||||
if err := os.WriteFile(data.file, sharedFile, 0o600); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to write export file")
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ func TestInput(t *testing.T) {
|
||||
dir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
datFile := filepath.Join(dir, "backup.dat")
|
||||
require.NoError(t, os.WriteFile(datFile, []byte("dummy"), 0600))
|
||||
require.NoError(t, os.WriteFile(datFile, []byte("dummy"), 0o600))
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
store := scratch.New()
|
||||
|
||||
@@ -39,7 +39,7 @@ func TestProcess(t *testing.T) {
|
||||
dir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
datFile := filepath.Join(dir, "backup.dat")
|
||||
require.NoError(t, os.WriteFile(datFile, export, 0600))
|
||||
require.NoError(t, os.WriteFile(datFile, export, 0o600))
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
tests := []struct {
|
||||
|
||||
@@ -16,7 +16,16 @@ Withdrawal credentials, held as part of a validator's on-chain definition, defin
|
||||
A private key is a hexadecimal string (_e.g._ 0x010203…a1a2a3) that can be used to generate a public key and (in the case of the execution chain) Ethereum address.
|
||||
|
||||
### Mnemonic
|
||||
A mnemonic is a 24-word phrase that can be used to generate multiple private keys with the use of _paths_.
|
||||
A mnemonic is a 24-word phrase that can be used to generate multiple private keys with the use of _paths_. Mnemonics are supported in the following languages:
|
||||
* chinese simplified
|
||||
* chinese traditional
|
||||
* czech
|
||||
* english
|
||||
* french
|
||||
* italian
|
||||
* japanese
|
||||
* korean
|
||||
* spanish
|
||||
|
||||
### Path
|
||||
A path is a string starting with "m" and containing a number of components separated by "/", for example "m/12381/3600/0/0". The process to obtain a key from a mnemonic and path is known as "hierarchical derivation".
|
||||
@@ -50,27 +59,33 @@ Here the copy of `ethdo` with access to private keys is on an offline computer,
|
||||
## Preparation
|
||||
Regardless of the method selected, preparation must take place on the online computer to ensure that `ethdo` can access your consensus node. `ethdo` will attempt to find a local consensus node automatically, but if not then an explicit connection value will be required. To find out if `ethdo` has access to the consensus node run:
|
||||
|
||||
```
|
||||
ethdo node info --verbose
|
||||
```sh
|
||||
ethdo node info
|
||||
```
|
||||
|
||||
The result should be something similar to the following:
|
||||
|
||||
```
|
||||
Version: teku/v22.9.1/linux-x86_64/-privatebuild-openjdk64bitservervm-java-14
|
||||
Syncing: false
|
||||
```
|
||||
|
||||
It is important to confirm that the "Syncing" value is "false". If this is "true" it means that the node is currently syncing, and you will need to wait for the process to finish before proceeding.
|
||||
Alternatively, the result may look like this:
|
||||
|
||||
If this command instead returns an error you will need to add an explicit connection string. For example, if your consensus node is serving its REST API on port 12345 then you should add `--connection=http://localhost:12345` to all `ethdo` commands in this process, for example:
|
||||
```
|
||||
No connection supplied; using mainnet public access endpoint
|
||||
Syncing: false
|
||||
```
|
||||
|
||||
which means that a local consensus node was not accessed and instead a public endpoint specifically assigned to handle these operations was used instead. If you do have a local consensus node but see this message it means that the local node could not be accessed, usually because it is running on a non-standard port. If this is the case for your configuration, you need to let `ethdo` know where the consensus node's REST API is. For example, if your consensus node is serving its REST API on port 12345 then you should add `--connection=http://localhost:12345` to all `ethdo` commands in this process, for example:
|
||||
|
||||
```sh
|
||||
ethdo --connection=http://localhost:12345 node info --verbose
|
||||
ethdo --connection=http://localhost:12345 node info
|
||||
```
|
||||
|
||||
Note that some consensus nodes may require configuration to serve their REST API. Please refer to the documentation of your specific consensus node to enable this.
|
||||
|
||||
Regardless of your method used above, it is important to confirm that the "Syncing" value is "false". If this is "true" it means that the node is currently syncing, and you will need to wait for the process to finish before proceeding.
|
||||
|
||||
Once the preparation is complete you should select either basic or advanced operation, depending on your requirements.
|
||||
|
||||
## Basic operation
|
||||
@@ -179,28 +194,44 @@ A mnemonic is a 24-word phrase from which withdrawal and validator keys are deri
|
||||
however this is only a standard and not a restriction, and it is possible for users to have created validators using paths of their own choice.
|
||||
|
||||
```
|
||||
ethdo validator credentials set --validator=123 --mnemonic="abandon abandon abandon … art" --path='m/12381/3600/0/0/0' --withdrawal-address=0x0123…cdef
|
||||
ethdo validator credentials set --mnemonic="abandon abandon abandon … art" --path='m/12381/3600/0/0/0' --withdrawal-address=0x0123…cdef
|
||||
```
|
||||
|
||||
replacing the path with the path to your _withdrawal_ key, and all other parameters with your own values.
|
||||
|
||||
#### Using a private key
|
||||
If you have the private key from which the current withdrawal credentials were derived this can be used to generate and broadcast the credentials change operation with the following command:
|
||||
#### Using a mnemonic and validator.
|
||||
Similar to the previous section, however instead of specifying a path instead the index, public key or account of the validator is provided.
|
||||
|
||||
```
|
||||
ethdo validator credentials set --validator=123 --withdrawal-address=0x8f…9F --private-key=0x3b…9c
|
||||
ethdo validator credentials set --mnemonic="abandon abandon abandon … art" --validator=123 --withdrawal-address=0x0123…cdef
|
||||
```
|
||||
|
||||
replacing the parameters with your own values.
|
||||
#### Using a mnemonic and withdrawal private key.
|
||||
If the withdrawal address was created using a non-standard method then it is possible that you have the private key for the withdrawal address. In this situation you can supply the withdrawal private key.
|
||||
|
||||
|
||||
```
|
||||
ethdo validator credentials set --mnemonic="abandon abandon abandon … art" --private-key=0x3b…9c
|
||||
```
|
||||
|
||||
Note that it is possible for there to be multiple validators that use the provided private key for a withdrawal address, in which case an operation will be generated for each validator that is eligible for change.
|
||||
|
||||
#### Using withdrawal private key only.
|
||||
Similar to the previous section, however instead of specifying the mnemonic, it will select multiple validators that use provided private key for their withdrawal address.
|
||||
|
||||
|
||||
```
|
||||
ethdo validator credentials set --private-key=0x3b…9c --withdrawal-address=0x8f…9F
|
||||
```
|
||||
|
||||
#### Using an account
|
||||
If you used `ethdo` to generate your validator deposit data you will likely have used a separate account to generate the withdrawal credentials. You can specify the account to generate and broadcast the credentials change operation with the following command:
|
||||
If you used `ethdo` to generate your validator deposit data you will likely have used a separate account to generate the withdrawal credentials. You can specify the accout of the validator and the accout of the withdrawal credentials to generate and broadcast the credentials change operation with the following command:
|
||||
|
||||
```
|
||||
ethdo validator credentials set --validator=123 --withdrawal-address=0x8f…9F --account=Wallet/Account --passphrase=secret
|
||||
ethdo validator credentials set --withdrawal-address=0x8f…9F --account=Wallet/Account --withdrawal-account=Withdrawals/Account --passphrase=secret
|
||||
```
|
||||
|
||||
replacing the parameters with your own values.
|
||||
replacing the parameters with your own values. Note that the passphrase here is the passphrsae of the withdrawal account, not the validator account.
|
||||
|
||||
## Confirming the process has succeeded
|
||||
The final step is confirming the operation has taken place. To do so, run the following command on an online server:
|
||||
|
||||
40
go.mod
40
go.mod
@@ -1,9 +1,9 @@
|
||||
module github.com/wealdtech/ethdo
|
||||
|
||||
go 1.18
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/attestantio/go-eth2-client v0.14.5
|
||||
github.com/attestantio/go-eth2-client v0.15.3
|
||||
github.com/ferranbt/fastssz v0.1.2
|
||||
github.com/gofrs/uuid v4.2.0+incompatible
|
||||
github.com/google/uuid v1.3.0
|
||||
@@ -19,55 +19,61 @@ require (
|
||||
github.com/spf13/cobra v1.3.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.13.0
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/tyler-smith/go-bip39 v1.1.0
|
||||
github.com/wealdtech/go-bytesutil v1.2.0
|
||||
github.com/wealdtech/go-ecodec v1.1.2
|
||||
github.com/wealdtech/go-eth2-types/v2 v2.8.0
|
||||
github.com/wealdtech/go-eth2-util v1.8.0
|
||||
github.com/wealdtech/go-eth2-wallet v1.15.0
|
||||
github.com/wealdtech/go-eth2-wallet-dirk v1.2.0
|
||||
github.com/wealdtech/go-eth2-wallet-dirk v1.4.2
|
||||
github.com/wealdtech/go-eth2-wallet-distributed v1.1.4
|
||||
github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.3.0
|
||||
github.com/wealdtech/go-eth2-wallet-hd/v2 v2.6.0
|
||||
github.com/wealdtech/go-eth2-wallet-nd/v2 v2.4.0
|
||||
github.com/wealdtech/go-eth2-wallet-store-filesystem v1.17.0
|
||||
github.com/wealdtech/go-eth2-wallet-store-s3 v1.11.0
|
||||
github.com/wealdtech/go-eth2-wallet-store-s3 v1.11.2
|
||||
github.com/wealdtech/go-eth2-wallet-store-scratch v1.7.0
|
||||
github.com/wealdtech/go-eth2-wallet-types/v2 v2.10.0
|
||||
github.com/wealdtech/go-string2eth v1.2.0
|
||||
golang.org/x/text v0.4.0
|
||||
golang.org/x/text v0.5.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/aws/aws-sdk-go v1.44.152 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/goccy/go-yaml v1.9.4 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/goccy/go-yaml v1.9.5 // indirect
|
||||
github.com/golang/glog v1.0.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jackc/puddle v1.3.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/klauspost/compress v1.15.11 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.1 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.2 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/minio/highwayhash v1.0.2 // indirect
|
||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.14.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/protolambda/zssz v0.1.5 // indirect
|
||||
github.com/r3labs/sse/v2 v2.7.4 // indirect
|
||||
github.com/r3labs/sse/v2 v2.8.1 // indirect
|
||||
github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 // indirect
|
||||
github.com/spf13/afero v1.9.2 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
@@ -75,13 +81,17 @@ require (
|
||||
github.com/subosito/gotenv v1.4.1 // indirect
|
||||
github.com/wealdtech/eth2-signer-api v1.7.1 // indirect
|
||||
github.com/wealdtech/go-indexer v1.0.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.37.0 // indirect
|
||||
go.opentelemetry.io/otel v1.11.2 // indirect
|
||||
go.opentelemetry.io/otel/metric v0.34.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.11.2 // indirect
|
||||
golang.org/x/crypto v0.3.0 // indirect
|
||||
golang.org/x/net v0.2.0 // indirect
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
||||
golang.org/x/net v0.4.0 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sys v0.3.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220930163606-c98284e70a91 // indirect
|
||||
google.golang.org/grpc v1.49.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37 // indirect
|
||||
google.golang.org/grpc v1.51.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
|
||||
200
go.sum
200
go.sum
@@ -29,21 +29,22 @@ cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW
|
||||
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
|
||||
cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM=
|
||||
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
|
||||
cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/compute v1.14.0 h1:hfm2+FfxVmnRlh6LpB7cg1ZNU+5edAHmW679JePztk0=
|
||||
cloud.google.com/go/compute/metadata v0.2.2 h1:aWKAjYaBaOSrpKl57+jnS/3fJRQnxL7TvR/u1VVbt6k=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU=
|
||||
cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/secretmanager v1.0.0/go.mod h1:+Qkm5qxIJ5mk74xxIXA+87fseaY1JLYBcFPQoc/GQxg=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
@@ -54,39 +55,30 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.1/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/attestantio/dirk v1.1.0 h1:hwMTYZkwj/Y0um3OD0LQxg2xSl4/5xqVWV2MRePE4ec=
|
||||
github.com/attestantio/dirk v1.1.0/go.mod h1:2jkOw/XHjvIDdhDcmj+Z3kuVPpxMcQ6zxzzjSSv71PY=
|
||||
github.com/attestantio/go-eth2-client v0.8.1/go.mod h1:kEK9iAAOBoADO5wEkd84FEOzjT1zXgVWveQsqn+uBGg=
|
||||
github.com/attestantio/go-eth2-client v0.14.5 h1:pKOTcbv9KOiVixVKM5Cr8nJrjD1VOIWmrlNFO0YtF64=
|
||||
github.com/attestantio/go-eth2-client v0.14.5/go.mod h1:5kLLzdlyPGboWr8tAwnG/4Kpi43BHd/HWp++WmmP6Ws=
|
||||
github.com/aws/aws-sdk-go v1.33.17/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/attestantio/go-eth2-client v0.15.3 h1:a4uLBkTkXr5Tq3elxT6IPlDH4910+boQdDS2hTg8THk=
|
||||
github.com/attestantio/go-eth2-client v0.15.3/go.mod h1:/Oh6YTuHmHhgLN/ZnQRKHGc7HdIzGlDkI2vjNZvOsvA=
|
||||
github.com/aws/aws-sdk-go v1.40.41/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
|
||||
github.com/aws/aws-sdk-go v1.41.19/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
|
||||
github.com/aws/aws-sdk-go v1.44.152 h1:L9aaepO8wHB67gwuGD8VgIYH/cmQDxieCt7FeLa0+fI=
|
||||
github.com/aws/aws-sdk-go v1.44.152/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||
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/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/census-instrumentation/opencensus-proto v0.3.0/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/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
@@ -107,28 +99,18 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH
|
||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
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/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/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
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/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk=
|
||||
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI=
|
||||
github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug=
|
||||
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-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
@@ -139,7 +121,6 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/go-control-plane v0.10.0/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ=
|
||||
github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws=
|
||||
@@ -148,15 +129,11 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL
|
||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/ferranbt/fastssz v0.0.0-20200728110133-0b6e349af87a/go.mod h1:DyEu2iuLBnb/T51BlsiO3yLYdJC6UbGMrIkqK1KmQxM=
|
||||
github.com/ferranbt/fastssz v0.0.0-20210526181520-7df50c8568f8/go.mod h1:DyEu2iuLBnb/T51BlsiO3yLYdJC6UbGMrIkqK1KmQxM=
|
||||
github.com/ferranbt/fastssz v0.0.0-20210905181407-59cf6761a7d5/go.mod h1:S8yiDeAXy8f88W4Ul+0dBMPx49S05byYbmZD6Uv94K4=
|
||||
github.com/ferranbt/fastssz v0.0.0-20211031100431-9823ca9021f1/go.mod h1:S8yiDeAXy8f88W4Ul+0dBMPx49S05byYbmZD6Uv94K4=
|
||||
github.com/ferranbt/fastssz v0.1.2 h1:Dky6dXlngF6Qjc+EfDipAkE83N5I5DE68bY6O0VLNPk=
|
||||
github.com/ferranbt/fastssz v0.1.2/go.mod h1:X5UPrE2u1UJjxHA8X54u04SBwdAQjG2sFtWs39YxyWs=
|
||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
@@ -167,9 +144,16 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||
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-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
@@ -177,19 +161,15 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/goccy/go-yaml v1.8.9/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA=
|
||||
github.com/goccy/go-yaml v1.9.2/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA=
|
||||
github.com/goccy/go-yaml v1.9.4 h1:S0GCYjwHKVI6IHqio7QWNKNThUl6NLzFd/g8Z65Axw8=
|
||||
github.com/goccy/go-yaml v1.9.4/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA=
|
||||
github.com/goccy/go-yaml v1.9.5 h1:Eh/+3uk9kLxG4koCX6lRMAPS1OaMSAi+FJcya0INdB0=
|
||||
github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
|
||||
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
|
||||
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
|
||||
@@ -242,7 +222,6 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
@@ -274,13 +253,9 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
|
||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
||||
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b h1:wDUNC2eKiL35DbLvsDhiblTUXHxcOPwQSCzi7xpQUN4=
|
||||
github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b/go.mod h1:VzxiSdG6j1pi7rwGm/xYI5RbtpBgM8sARDXlvEvxlu0=
|
||||
github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
|
||||
github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
|
||||
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@@ -315,7 +290,6 @@ github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpT
|
||||
github.com/herumi/bls-eth-go-binary v0.0.0-20210520070601-31246bfa8ac4/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U=
|
||||
github.com/herumi/bls-eth-go-binary v0.0.0-20210902234237-7763804ee078/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U=
|
||||
github.com/herumi/bls-eth-go-binary v0.0.0-20210917013441-d37c07cfda4e/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U=
|
||||
github.com/herumi/bls-eth-go-binary v0.0.0-20211117070716-2bdbdadbf8bb/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U=
|
||||
github.com/herumi/bls-eth-go-binary v1.28.1 h1:fcIZ48y5EE9973k05XjE8+P3YiQgjZz4JI/YabAm8KA=
|
||||
github.com/herumi/bls-eth-go-binary v1.28.1/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U=
|
||||
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
@@ -323,10 +297,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jackc/puddle v1.1.4/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0=
|
||||
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
@@ -341,20 +313,14 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
|
||||
github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
||||
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.1.2/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.1 h1:U33DW0aiEj633gHYw3LoDNfkDiYnE5Q8M/TKJn2f2jI=
|
||||
github.com/klauspost/cpuid/v2 v2.2.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.2 h1:xPMwiykqNK9VK0NYC3+jTMYv9I6Vl3YdjZgPZKG3zO0=
|
||||
github.com/klauspost/cpuid/v2 v2.2.2/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
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.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
@@ -365,11 +331,9 @@ github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
@@ -378,7 +342,6 @@ github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
@@ -390,6 +353,7 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
@@ -421,13 +385,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA=
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
@@ -449,51 +408,50 @@ github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3O
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
|
||||
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
|
||||
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/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
|
||||
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
|
||||
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||
github.com/protolambda/zssz v0.1.5 h1:7fjJjissZIIaa2QcvmhS/pZISMX21zVITt49sW1ouek=
|
||||
github.com/protolambda/zssz v0.1.5/go.mod h1:a4iwOX5FE7/JkKA+J/PH0Mjo9oXftN6P8NZyL28gpag=
|
||||
github.com/prysmaticlabs/ethereumapis v0.0.0-20200812153649-a842fc47c2c3/go.mod h1:k7b2dxy6RppCG6kmOJkNOXzRpEoTdsPygc2aQhsUsZk=
|
||||
github.com/prysmaticlabs/go-bitfield v0.0.0-20200322041314-62c2aee71669/go.mod h1:hCwmef+4qXWjv0jLDbQdWnL0Ol7cS7/lCSS26WR+u6s=
|
||||
github.com/prysmaticlabs/go-bitfield v0.0.0-20210607200045-4da71aaf6c2d/go.mod h1:hCwmef+4qXWjv0jLDbQdWnL0Ol7cS7/lCSS26WR+u6s=
|
||||
github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 h1:0tVE4tdWQK9ZpYygoV7+vS6QkDvQVySboMVEIxBJmXw=
|
||||
github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7/go.mod h1:wmuf/mdK4VMD+jA9ThwcUKjg3a2XWM9cVfFYjDyY4j4=
|
||||
github.com/prysmaticlabs/go-ssz v0.0.0-20210121151755-f6208871c388 h1:4bD+ujqGfY4zoDUF3q9MhdmpPXzdp03DYUIlXeQ72kk=
|
||||
github.com/prysmaticlabs/go-ssz v0.0.0-20210121151755-f6208871c388/go.mod h1:VecIJZrewdAuhVckySLFt2wAAHRME934bSDurP8ftkc=
|
||||
github.com/prysmaticlabs/gohashtree v0.0.1-alpha.0.20220714111606-acbb2962fb48 h1:cSo6/vk8YpvkLbk9v3FO97cakNmUoxwi2KMP8hd5WIw=
|
||||
github.com/prysmaticlabs/gohashtree v0.0.1-alpha.0.20220714111606-acbb2962fb48/go.mod h1:4pWaT30XoEx1j8KNJf3TV+E3mQkaufn7mf+jRNb/Fuk=
|
||||
github.com/r3labs/sse/v2 v2.3.0/go.mod h1:hUrYMKfu9WquG9MyI0r6TKiNH+6Sw/QPKm2YbNbU5g8=
|
||||
github.com/r3labs/sse/v2 v2.7.4 h1:pvCMswPDlXd/ZUFx1dry0LbXJNHXwWPulLcUGYwClc0=
|
||||
github.com/r3labs/sse/v2 v2.7.4/go.mod h1:hUrYMKfu9WquG9MyI0r6TKiNH+6Sw/QPKm2YbNbU5g8=
|
||||
github.com/r3labs/sse/v2 v2.8.1 h1:lZH+W4XOLIq88U5MIHOsLec7+R62uhz3bIi2yn0Sg8o=
|
||||
github.com/r3labs/sse/v2 v2.8.1/go.mod h1:Igau6Whc+F17QUgML1fYe1VPZzTV6EMCnYktEmkNJ7I=
|
||||
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/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.19.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
|
||||
github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM=
|
||||
github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo=
|
||||
github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc=
|
||||
github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY=
|
||||
github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE=
|
||||
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
|
||||
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=
|
||||
@@ -504,27 +462,19 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
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.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw=
|
||||
github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0=
|
||||
github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
|
||||
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=
|
||||
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.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4=
|
||||
github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
|
||||
github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU=
|
||||
github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw=
|
||||
@@ -532,6 +482,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
@@ -540,17 +491,15 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
|
||||
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8=
|
||||
github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U=
|
||||
github.com/uber/jaeger-client-go v2.29.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
|
||||
github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/wealdtech/eth2-signer-api v1.7.1 h1:XdwFuv3VWCwcPPPrfa77sUXL1GSvxDtsUZxlByz//b0=
|
||||
github.com/wealdtech/eth2-signer-api v1.7.1/go.mod h1:fX8XtN9Svyjs+e7TgoOfOcwRTHeblR5SXftAVV3T1ZA=
|
||||
github.com/wealdtech/go-bytesutil v1.0.1/go.mod h1:jENeMqeTEU8FNZyDFRVc7KqBdRKSnJ9CCh26TcuNb9s=
|
||||
@@ -570,8 +519,8 @@ github.com/wealdtech/go-eth2-util v1.8.0 h1:hhs3h2y3Ldty18lppFdpe46nZpdDAMbY7Qqi
|
||||
github.com/wealdtech/go-eth2-util v1.8.0/go.mod h1:rSuE0v5zX+uyZrqW/iUmXOxeDpB7lTvhcZvAVh0KlMU=
|
||||
github.com/wealdtech/go-eth2-wallet v1.15.0 h1:yff2rWCvHyr5bOU41wwilsNemFRYOFinZoIPjhzG10M=
|
||||
github.com/wealdtech/go-eth2-wallet v1.15.0/go.mod h1:BqXnen6XCa99yaUx/XIcyImaZLJMMj4wIMcUBywlMJs=
|
||||
github.com/wealdtech/go-eth2-wallet-dirk v1.2.0 h1:GNrxDo1ajDbUU8XvCHku1ImFhgtV79oNVHEZlRfu9f4=
|
||||
github.com/wealdtech/go-eth2-wallet-dirk v1.2.0/go.mod h1:1lnHDSdcRxj9CFJh+jPtdgVQCI+7jLwikjsxcqksXlg=
|
||||
github.com/wealdtech/go-eth2-wallet-dirk v1.4.2 h1:TV5rrXGtHPteV+Mk7v6KFdmSUDm38SDKBDHFIvKIL8w=
|
||||
github.com/wealdtech/go-eth2-wallet-dirk v1.4.2/go.mod h1:WGzvX3nxQnFNv4Hjx7jfBQhlVAEsRqe1tZwC8hZWM98=
|
||||
github.com/wealdtech/go-eth2-wallet-distributed v1.1.4 h1:EBk/FofiQWtbRYmwdCp0qM9TPDvlh1LN2yjlLr4W3ao=
|
||||
github.com/wealdtech/go-eth2-wallet-distributed v1.1.4/go.mod h1:Y31pDxcdyADwfQl65t8V6KhcmCes31EXkXZPDcT2SIk=
|
||||
github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.1.6/go.mod h1:hIeHsLJWVxFUUqQqaBm9nmUW3Y/eHneQclYyNayUeQg=
|
||||
@@ -588,8 +537,10 @@ github.com/wealdtech/go-eth2-wallet-store-filesystem v1.16.15/go.mod h1:v/JATYJQ
|
||||
github.com/wealdtech/go-eth2-wallet-store-filesystem v1.17.0 h1:cq7k9osiIkaYrdpetPQgk3ozl/dFvmxW364OC/uNuww=
|
||||
github.com/wealdtech/go-eth2-wallet-store-filesystem v1.17.0/go.mod h1:Fiw5If3/mgH+qYRKIH+kTpZZ3r6z2KgHUiE5Vf/QrfE=
|
||||
github.com/wealdtech/go-eth2-wallet-store-s3 v1.10.0/go.mod h1:DhAm7si8N/5qU1sZ/RLavm87LsOthnWuRyQaGWNFiyI=
|
||||
github.com/wealdtech/go-eth2-wallet-store-s3 v1.11.0 h1:qta5XIXe7o9djqB6L90dmf2CvnLmVunzmKeIAnrUyPs=
|
||||
github.com/wealdtech/go-eth2-wallet-store-s3 v1.11.0/go.mod h1:azzsylTwr1hnLisDWZJbUz3HToRsrG7ADpLG8TXJgOU=
|
||||
github.com/wealdtech/go-eth2-wallet-store-s3 v1.11.1 h1:q9/p/UfrT7AfR6MYZfr3nQOKdhcLCuKqldqplNHo3Ws=
|
||||
github.com/wealdtech/go-eth2-wallet-store-s3 v1.11.1/go.mod h1:azzsylTwr1hnLisDWZJbUz3HToRsrG7ADpLG8TXJgOU=
|
||||
github.com/wealdtech/go-eth2-wallet-store-s3 v1.11.2 h1:Lxxfu5YKTzfNlymI8kF04BbC8hIKPr3in06zgHDmow8=
|
||||
github.com/wealdtech/go-eth2-wallet-store-s3 v1.11.2/go.mod h1:azzsylTwr1hnLisDWZJbUz3HToRsrG7ADpLG8TXJgOU=
|
||||
github.com/wealdtech/go-eth2-wallet-store-scratch v1.6.3/go.mod h1:V4NUofSBIyzoqc5cNZaGciaDm2WFAgSQikRslOyh5Tg=
|
||||
github.com/wealdtech/go-eth2-wallet-store-scratch v1.7.0 h1:1dMKx9jtw1v9JrwOPFf2JaOQKmvpMp1GEeuMRiNfq5o=
|
||||
github.com/wealdtech/go-eth2-wallet-store-scratch v1.7.0/go.mod h1:O7BitrDeQVtBFNnvYmOYLzJCZAiCf5ur/4IRucIz+S0=
|
||||
@@ -600,10 +551,8 @@ github.com/wealdtech/go-eth2-wallet-types/v2 v2.10.0 h1:bivQYT1TALvU+3kC9gyx2qX+
|
||||
github.com/wealdtech/go-eth2-wallet-types/v2 v2.10.0/go.mod h1:CYlvo087PNZuihF0Pdnerfs9lhXAW/wLDxySNNkgpVo=
|
||||
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-majordomo v1.0.1/go.mod h1:QoT4S1nUQwdQK19+CfepDwV+Yr7cc3dbF+6JFdQnIqY=
|
||||
github.com/wealdtech/go-string2eth v1.2.0 h1:C0E5p78tecZTsGccJc9r/kreFah4EfDs5uUPnS6XXMs=
|
||||
github.com/wealdtech/go-string2eth v1.2.0/go.mod h1:RUzsLjJtbZaJ/3UKn9kY19a/vCCUHtEWoUW3uiK6yGU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@@ -611,11 +560,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||
go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
|
||||
go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
@@ -624,17 +570,20 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.37.0 h1:+uFejS4DCfNH6d3xODVIGsdhzgzhh45p9gpbHQMbdZI=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.37.0/go.mod h1:HSmzQvagH8pS2/xrK7ScWsk0vAMtRTGbMFgInXCi8Tc=
|
||||
go.opentelemetry.io/otel v1.11.2 h1:YBZcQlsVekzFsFbjygXMOXSs6pialIZxcjfO/mBDmR0=
|
||||
go.opentelemetry.io/otel v1.11.2/go.mod h1:7p4EUV+AqgdlNV9gL97IgUZiVR3yrFXYo53f9BM3tRI=
|
||||
go.opentelemetry.io/otel/metric v0.34.0 h1:MCPoQxcg/26EuuJwpYN1mZTeCYAUGx8ABxfW07YkjP8=
|
||||
go.opentelemetry.io/otel/metric v0.34.0/go.mod h1:ZFuI4yQGNCupurTXCwkeD/zHBt+C2bR7bw5JqUm/AP8=
|
||||
go.opentelemetry.io/otel/trace v1.11.2 h1:Xf7hWSF2Glv0DE3MH7fBHvtpSBsjcBUe5MYAmZM/+y0=
|
||||
go.opentelemetry.io/otel/trace v1.11.2/go.mod h1:4N+yC7QEz7TTsG9BSRLNAa63eg5E06ObSbKPmxQ/pKA=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@@ -650,10 +599,7 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/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=
|
||||
@@ -663,7 +609,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||
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=
|
||||
@@ -707,7 +652,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/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-20191116160921-f9c825593386/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -735,18 +679,16 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
|
||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211105192438-b53810dc28af/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211116231205-47ca1ff31462/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
|
||||
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
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=
|
||||
@@ -764,6 +706,8 @@ golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ
|
||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.2.0 h1:GtQkldQ9m7yvzCL1V+LrYow3Khe0eJH0w7RbX/VbaIU=
|
||||
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=
|
||||
@@ -775,14 +719,14 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/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-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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -793,7 +737,6 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
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-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -854,8 +797,6 @@ golang.org/x/sys v0.0.0-20210923061019-b8560ed6a9b7/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211106132015-ebca88c72f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -881,16 +822,14 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
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/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/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-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-20190206041539-40960b6deb8e/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-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
@@ -902,7 +841,6 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw
|
||||
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-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/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=
|
||||
@@ -953,10 +891,6 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
|
||||
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
|
||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
|
||||
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
|
||||
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=
|
||||
@@ -987,7 +921,6 @@ google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqiv
|
||||
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
|
||||
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
|
||||
google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU=
|
||||
google.golang.org/api v0.60.0/go.mod h1:d7rl65NZAkEQ90JFzqBjcRq1TVeG5ZoGV3sSpEnnVb4=
|
||||
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
|
||||
google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
@@ -996,6 +929,7 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
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=
|
||||
@@ -1005,7 +939,6 @@ google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRn
|
||||
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-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
@@ -1019,16 +952,13 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200528191852-705c0b31589b/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200731012542-8145dea6a485/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
@@ -1043,7 +973,6 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D
|
||||
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||
google.golang.org/genproto v0.0.0-20210406143921-e86de6bf7a46/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
|
||||
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
@@ -1059,26 +988,21 @@ google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEc
|
||||
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210921142501-181ce0d877f6/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211021150943-2b146023228c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211027162914-98a5263abeca/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211104193956-4c6863e31247/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211116182654-e63d96a377c4/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220930163606-c98284e70a91 h1:Ezh2cpcnP5Rq60sLensUsFnxh7P6513NLvNtCm9iyJ4=
|
||||
google.golang.org/genproto v0.0.0-20220930163606-c98284e70a91/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U=
|
||||
google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37 h1:jmIfw8+gSvXcZSgaFAGyInDXeWzUhvYH57G/5GKMn70=
|
||||
google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||
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.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.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
@@ -1103,8 +1027,8 @@ google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K
|
||||
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
|
||||
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw=
|
||||
google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
|
||||
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
|
||||
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
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=
|
||||
@@ -1128,13 +1052,10 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/jcmturner/gokrb5.v7 v7.5.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
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=
|
||||
@@ -1158,6 +1079,5 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
||||
@@ -20,6 +20,8 @@ import (
|
||||
)
|
||||
|
||||
// Service provides a number of functions for calculating chain-related times.
|
||||
//
|
||||
//nolint:interfacebloat
|
||||
type Service interface {
|
||||
// GenesisTime provides the time of the chain's genesis.
|
||||
GenesisTime() time.Time
|
||||
|
||||
@@ -20,10 +20,9 @@ import (
|
||||
)
|
||||
|
||||
type parameters struct {
|
||||
logLevel zerolog.Level
|
||||
genesisTimeProvider eth2client.GenesisTimeProvider
|
||||
specProvider eth2client.SpecProvider
|
||||
forkScheduleProvider eth2client.ForkScheduleProvider
|
||||
logLevel zerolog.Level
|
||||
genesisTimeProvider eth2client.GenesisTimeProvider
|
||||
specProvider eth2client.SpecProvider
|
||||
}
|
||||
|
||||
// Parameter is the interface for service parameters.
|
||||
@@ -58,13 +57,6 @@ func WithSpecProvider(provider eth2client.SpecProvider) Parameter {
|
||||
})
|
||||
}
|
||||
|
||||
// WithForkScheduleProvider sets the fork schedule provider.
|
||||
func WithForkScheduleProvider(provider eth2client.ForkScheduleProvider) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.forkScheduleProvider = provider
|
||||
})
|
||||
}
|
||||
|
||||
// parseAndCheckParameters parses and checks parameters to ensure that mandatory parameters are present and correct.
|
||||
func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
||||
parameters := parameters{
|
||||
@@ -82,9 +74,6 @@ func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
||||
if parameters.genesisTimeProvider == nil {
|
||||
return nil, errors.New("no genesis time provider specified")
|
||||
}
|
||||
if parameters.forkScheduleProvider == nil {
|
||||
return nil, errors.New("no fork schedule provider specified")
|
||||
}
|
||||
|
||||
return ¶meters, nil
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
package standard
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"time"
|
||||
|
||||
@@ -87,21 +86,21 @@ func New(ctx context.Context, params ...Parameter) (*Service, error) {
|
||||
epochsPerSyncCommitteePeriod = tmp2
|
||||
}
|
||||
|
||||
altairForkEpoch, err := fetchAltairForkEpoch(ctx, parameters.forkScheduleProvider)
|
||||
altairForkEpoch, err := fetchAltairForkEpoch(ctx, parameters.specProvider)
|
||||
if err != nil {
|
||||
// Set to far future epoch.
|
||||
altairForkEpoch = 0xffffffffffffffff
|
||||
}
|
||||
log.Trace().Uint64("epoch", uint64(altairForkEpoch)).Msg("Obtained Altair fork epoch")
|
||||
|
||||
bellatrixForkEpoch, err := fetchBellatrixForkEpoch(ctx, parameters.forkScheduleProvider)
|
||||
bellatrixForkEpoch, err := fetchBellatrixForkEpoch(ctx, parameters.specProvider)
|
||||
if err != nil {
|
||||
// Set to far future epoch.
|
||||
bellatrixForkEpoch = 0xffffffffffffffff
|
||||
}
|
||||
log.Trace().Uint64("epoch", uint64(bellatrixForkEpoch)).Msg("Obtained Bellatrix fork epoch")
|
||||
|
||||
capellaForkEpoch, err := fetchCapellaForkEpoch(ctx, parameters.forkScheduleProvider)
|
||||
capellaForkEpoch, err := fetchCapellaForkEpoch(ctx, parameters.specProvider)
|
||||
if err != nil {
|
||||
// Set to far future epoch.
|
||||
capellaForkEpoch = 0xffffffffffffffff
|
||||
@@ -217,19 +216,28 @@ func (s *Service) AltairInitialSyncCommitteePeriod() uint64 {
|
||||
return uint64(s.altairForkEpoch) / s.epochsPerSyncCommitteePeriod
|
||||
}
|
||||
|
||||
func fetchAltairForkEpoch(ctx context.Context, provider eth2client.ForkScheduleProvider) (phase0.Epoch, error) {
|
||||
forkSchedule, err := provider.ForkSchedule(ctx)
|
||||
func fetchAltairForkEpoch(ctx context.Context,
|
||||
specProvider eth2client.SpecProvider,
|
||||
) (
|
||||
phase0.Epoch,
|
||||
error,
|
||||
) {
|
||||
// Fetch the fork version.
|
||||
spec, err := specProvider.Spec(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return 0, errors.Wrap(err, "failed to obtain spec")
|
||||
}
|
||||
for i := range forkSchedule {
|
||||
if bytes.Equal(forkSchedule[i].CurrentVersion[:], forkSchedule[i].PreviousVersion[:]) {
|
||||
// This is the genesis fork; ignore it.
|
||||
continue
|
||||
}
|
||||
return forkSchedule[i].Epoch, nil
|
||||
tmp, exists := spec["ALTAIR_FORK_EPOCH"]
|
||||
if !exists {
|
||||
return 0, errors.New("altair fork version not known by chain")
|
||||
}
|
||||
return 0, errors.New("no altair fork obtained")
|
||||
epoch, isEpoch := tmp.(uint64)
|
||||
if !isEpoch {
|
||||
//nolint:revive
|
||||
return 0, errors.New("ALTAIR_FORK_EPOCH is not a uint64!")
|
||||
}
|
||||
|
||||
return phase0.Epoch(epoch), nil
|
||||
}
|
||||
|
||||
// BellatrixInitialEpoch provides the epoch at which the Bellatrix hard fork takes place.
|
||||
@@ -237,24 +245,28 @@ func (s *Service) BellatrixInitialEpoch() phase0.Epoch {
|
||||
return s.bellatrixForkEpoch
|
||||
}
|
||||
|
||||
func fetchBellatrixForkEpoch(ctx context.Context, provider eth2client.ForkScheduleProvider) (phase0.Epoch, error) {
|
||||
forkSchedule, err := provider.ForkSchedule(ctx)
|
||||
func fetchBellatrixForkEpoch(ctx context.Context,
|
||||
specProvider eth2client.SpecProvider,
|
||||
) (
|
||||
phase0.Epoch,
|
||||
error,
|
||||
) {
|
||||
// Fetch the fork version.
|
||||
spec, err := specProvider.Spec(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return 0, errors.Wrap(err, "failed to obtain spec")
|
||||
}
|
||||
count := 0
|
||||
for i := range forkSchedule {
|
||||
count++
|
||||
if bytes.Equal(forkSchedule[i].CurrentVersion[:], forkSchedule[i].PreviousVersion[:]) {
|
||||
// This is the genesis fork; ignore it.
|
||||
continue
|
||||
}
|
||||
if count == 1 {
|
||||
return forkSchedule[i].Epoch, nil
|
||||
}
|
||||
count++
|
||||
tmp, exists := spec["BELLATRIX_FORK_EPOCH"]
|
||||
if !exists {
|
||||
return 0, errors.New("bellatrix fork version not known by chain")
|
||||
}
|
||||
return 0, errors.New("no bellatrix fork obtained")
|
||||
epoch, isEpoch := tmp.(uint64)
|
||||
if !isEpoch {
|
||||
//nolint:revive
|
||||
return 0, errors.New("BELLATRIX_FORK_EPOCH is not a uint64!")
|
||||
}
|
||||
|
||||
return phase0.Epoch(epoch), nil
|
||||
}
|
||||
|
||||
// CapellaInitialEpoch provides the epoch at which the Capella hard fork takes place.
|
||||
@@ -262,22 +274,26 @@ func (s *Service) CapellaInitialEpoch() phase0.Epoch {
|
||||
return s.capellaForkEpoch
|
||||
}
|
||||
|
||||
func fetchCapellaForkEpoch(ctx context.Context, provider eth2client.ForkScheduleProvider) (phase0.Epoch, error) {
|
||||
forkSchedule, err := provider.ForkSchedule(ctx)
|
||||
func fetchCapellaForkEpoch(ctx context.Context,
|
||||
specProvider eth2client.SpecProvider,
|
||||
) (
|
||||
phase0.Epoch,
|
||||
error,
|
||||
) {
|
||||
// Fetch the fork version.
|
||||
spec, err := specProvider.Spec(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return 0, errors.Wrap(err, "failed to obtain spec")
|
||||
}
|
||||
count := 0
|
||||
for i := range forkSchedule {
|
||||
count++
|
||||
if bytes.Equal(forkSchedule[i].CurrentVersion[:], forkSchedule[i].PreviousVersion[:]) {
|
||||
// This is the genesis fork; ignore it.
|
||||
continue
|
||||
}
|
||||
if count == 2 {
|
||||
return forkSchedule[i].Epoch, nil
|
||||
}
|
||||
count++
|
||||
tmp, exists := spec["CAPELLA_FORK_EPOCH"]
|
||||
if !exists {
|
||||
return 0, errors.New("capella fork version not known by chain")
|
||||
}
|
||||
return 0, errors.New("no capella fork obtained")
|
||||
epoch, isEpoch := tmp.(uint64)
|
||||
if !isEpoch {
|
||||
//nolint:revive
|
||||
return 0, errors.New("CAPELLA_FORK_EPOCH is not a uint64!")
|
||||
}
|
||||
|
||||
return phase0.Epoch(epoch), nil
|
||||
}
|
||||
|
||||
@@ -31,22 +31,9 @@ func TestService(t *testing.T) {
|
||||
slotDuration := 12 * time.Second
|
||||
slotsPerEpoch := uint64(32)
|
||||
epochsPerSyncCommitteePeriod := uint64(256)
|
||||
forkSchedule := []*phase0.Fork{
|
||||
{
|
||||
PreviousVersion: phase0.Version{0x01, 0x02, 0x03, 0x04},
|
||||
CurrentVersion: phase0.Version{0x01, 0x02, 0x03, 0x04},
|
||||
Epoch: 0,
|
||||
},
|
||||
{
|
||||
PreviousVersion: phase0.Version{0x01, 0x02, 0x03, 0x04},
|
||||
CurrentVersion: phase0.Version{0x05, 0x06, 0x07, 0x08},
|
||||
Epoch: 10,
|
||||
},
|
||||
}
|
||||
|
||||
mockGenesisTimeProvider := mock.NewGenesisTimeProvider(genesisTime)
|
||||
mockSpecProvider := mock.NewSpecProvider(slotDuration, slotsPerEpoch, epochsPerSyncCommitteePeriod)
|
||||
mockForkScheduleProvider := mock.NewForkScheduleProvider(forkSchedule)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -58,7 +45,6 @@ func TestService(t *testing.T) {
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithSpecProvider(mockSpecProvider),
|
||||
standard.WithForkScheduleProvider(mockForkScheduleProvider),
|
||||
},
|
||||
err: "problem with parameters: no genesis time provider specified",
|
||||
},
|
||||
@@ -67,26 +53,15 @@ func TestService(t *testing.T) {
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithGenesisTimeProvider(mockGenesisTimeProvider),
|
||||
standard.WithForkScheduleProvider(mockForkScheduleProvider),
|
||||
},
|
||||
err: "problem with parameters: no spec provider specified",
|
||||
},
|
||||
{
|
||||
name: "ForkScheduleProviderMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithGenesisTimeProvider(mockGenesisTimeProvider),
|
||||
standard.WithSpecProvider(mockSpecProvider),
|
||||
},
|
||||
err: "problem with parameters: no fork schedule provider specified",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithGenesisTimeProvider(mockGenesisTimeProvider),
|
||||
standard.WithSpecProvider(mockSpecProvider),
|
||||
standard.WithForkScheduleProvider(mockForkScheduleProvider),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -123,11 +98,9 @@ func createService(genesisTime time.Time) (chaintime.Service, time.Duration, uin
|
||||
|
||||
mockGenesisTimeProvider := mock.NewGenesisTimeProvider(genesisTime)
|
||||
mockSpecProvider := mock.NewSpecProvider(slotDuration, slotsPerEpoch, epochsPerSyncCommitteePeriod)
|
||||
mockForkScheduleProvider := mock.NewForkScheduleProvider(forkSchedule)
|
||||
s, err := standard.New(context.Background(),
|
||||
standard.WithGenesisTimeProvider(mockGenesisTimeProvider),
|
||||
standard.WithSpecProvider(mockSpecProvider),
|
||||
standard.WithForkScheduleProvider(mockForkScheduleProvider),
|
||||
)
|
||||
return s, slotDuration, slotsPerEpoch, epochsPerSyncCommitteePeriod, forkSchedule, err
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ const (
|
||||
ShareOverhead = 1
|
||||
)
|
||||
|
||||
// polynomial represents a polynomial of arbitrary degree
|
||||
// polynomial represents a polynomial of arbitrary degree.
|
||||
type polynomial struct {
|
||||
coefficients []uint8
|
||||
}
|
||||
@@ -53,7 +53,7 @@ func makePolynomial(intercept, degree uint8) (polynomial, error) {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// evaluate returns the value of the polynomial for the given x
|
||||
// evaluate returns the value of the polynomial for the given x.
|
||||
func (p *polynomial) evaluate(x uint8) uint8 {
|
||||
// Special case the origin
|
||||
if x == 0 {
|
||||
@@ -92,7 +92,7 @@ func interpolatePolynomial(xSamples, ySamples []uint8, x uint8) uint8 {
|
||||
return result
|
||||
}
|
||||
|
||||
// div divides two numbers in GF(2^8)
|
||||
// div divides two numbers in GF(2^8).
|
||||
func div(a, b uint8) uint8 {
|
||||
if b == 0 {
|
||||
// leaks some timing information but we don't care anyways as this
|
||||
@@ -111,8 +111,8 @@ func div(a, b uint8) uint8 {
|
||||
return uint8(ret)
|
||||
}
|
||||
|
||||
// mult multiplies two numbers in GF(2^8)
|
||||
func mult(a, b uint8) (out uint8) {
|
||||
// mult multiplies two numbers in GF(2^8).
|
||||
func mult(a, b uint8) uint8 {
|
||||
logA := logTable[a]
|
||||
logB := logTable[b]
|
||||
sum := (int(logA) + int(logB)) % 255
|
||||
@@ -157,6 +157,7 @@ func Split(secret []byte, parts, threshold int) ([][]byte, error) {
|
||||
}
|
||||
|
||||
// Generate random list of x coordinates
|
||||
//nolint
|
||||
rnd := mathrand.New(&cryptoSource{})
|
||||
xCoordinates := rnd.Perm(255)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ package shamir
|
||||
// They use 0xe5 (229) as the generator
|
||||
|
||||
var (
|
||||
// logTable provides the log(X)/log(g) at each index X
|
||||
// logTable provides the log(X)/log(g) at each index X.
|
||||
logTable = [256]uint8{
|
||||
0x00, 0xff, 0xc8, 0x08, 0x91, 0x10, 0xd0, 0x36,
|
||||
0x5a, 0x3e, 0xd8, 0x43, 0x99, 0x77, 0xfe, 0x18,
|
||||
@@ -41,7 +41,7 @@ var (
|
||||
}
|
||||
|
||||
// expTable provides the anti-log or exponentiation value
|
||||
// for the equivalent index
|
||||
// for the equivalent index.
|
||||
expTable = [256]uint8{
|
||||
0x01, 0xe5, 0x4c, 0xb5, 0xfb, 0x9f, 0xfc, 0x12,
|
||||
0x03, 0x34, 0xd4, 0xc4, 0x16, 0xba, 0x1f, 0x36,
|
||||
|
||||
@@ -61,12 +61,12 @@ func ParseAccount(ctx context.Context,
|
||||
// Private key.
|
||||
account, err = newScratchAccountFromPrivKey(data)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create account from public key")
|
||||
return nil, errors.Wrap(err, "failed to create account from private key")
|
||||
}
|
||||
if unlock {
|
||||
_, err = UnlockAccount(ctx, account, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unlock account")
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
default:
|
||||
@@ -82,7 +82,7 @@ func ParseAccount(ctx context.Context,
|
||||
// Supplementary will be the unlock passphrase(s).
|
||||
_, err = UnlockAccount(ctx, account, supplementary)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unlock account")
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
case strings.Contains(accountStr, " "):
|
||||
|
||||
@@ -33,7 +33,7 @@ func AttestationHeadCorrect(ctx context.Context,
|
||||
for {
|
||||
header, err := headersCache.Fetch(ctx, slot)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
return false, err
|
||||
}
|
||||
if header == nil {
|
||||
// No block.
|
||||
@@ -63,7 +63,7 @@ func AttestationTargetCorrect(ctx context.Context,
|
||||
for {
|
||||
header, err := headersCache.Fetch(ctx, slot)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
return false, err
|
||||
}
|
||||
if header == nil {
|
||||
// No block.
|
||||
|
||||
@@ -24,11 +24,15 @@ import (
|
||||
|
||||
// ParseEpoch parses input to calculate the desired epoch.
|
||||
func ParseEpoch(ctx context.Context, chainTime chaintime.Service, epochStr string) (phase0.Epoch, error) {
|
||||
currentEpoch := chainTime.CurrentEpoch()
|
||||
switch epochStr {
|
||||
case "", "current":
|
||||
return chainTime.CurrentEpoch(), nil
|
||||
case "", "current", "-0":
|
||||
return currentEpoch, nil
|
||||
case "last":
|
||||
return chainTime.CurrentEpoch() - 1, nil
|
||||
if currentEpoch > 0 {
|
||||
currentEpoch--
|
||||
}
|
||||
return currentEpoch, nil
|
||||
default:
|
||||
val, err := strconv.ParseInt(epochStr, 10, 64)
|
||||
if err != nil {
|
||||
@@ -37,6 +41,9 @@ func ParseEpoch(ctx context.Context, chainTime chaintime.Service, epochStr strin
|
||||
if val >= 0 {
|
||||
return phase0.Epoch(val), nil
|
||||
}
|
||||
return chainTime.CurrentEpoch() + phase0.Epoch(val), nil
|
||||
if phase0.Epoch(-val) > currentEpoch {
|
||||
return 0, nil
|
||||
}
|
||||
return currentEpoch + phase0.Epoch(val), nil
|
||||
}
|
||||
}
|
||||
|
||||
105
util/epoch_test.go
Normal file
105
util/epoch_test.go
Normal file
@@ -0,0 +1,105 @@
|
||||
// Copyright © 2023 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_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/require"
|
||||
standardchaintime "github.com/wealdtech/ethdo/services/chaintime/standard"
|
||||
"github.com/wealdtech/ethdo/testing/mock"
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
)
|
||||
|
||||
func TestParseEpoch(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// genesis is 1 day ago.
|
||||
genesisTime := time.Now().AddDate(0, 0, -1)
|
||||
slotDuration := 12 * time.Second
|
||||
slotsPerEpoch := uint64(32)
|
||||
epochsPerSyncCommitteePeriod := uint64(256)
|
||||
mockGenesisTimeProvider := mock.NewGenesisTimeProvider(genesisTime)
|
||||
mockSpecProvider := mock.NewSpecProvider(slotDuration, slotsPerEpoch, epochsPerSyncCommitteePeriod)
|
||||
chainTime, err := standardchaintime.New(context.Background(),
|
||||
standardchaintime.WithLogLevel(zerolog.Disabled),
|
||||
standardchaintime.WithGenesisTimeProvider(mockGenesisTimeProvider),
|
||||
standardchaintime.WithSpecProvider(mockSpecProvider),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
err string
|
||||
expected phase0.Epoch
|
||||
}{
|
||||
{
|
||||
name: "Genesis",
|
||||
input: "0",
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
input: "invalid",
|
||||
err: `failed to parse epoch: strconv.ParseInt: parsing "invalid": invalid syntax`,
|
||||
},
|
||||
{
|
||||
name: "Absolute",
|
||||
input: "15",
|
||||
expected: 15,
|
||||
},
|
||||
{
|
||||
name: "Current",
|
||||
input: "current",
|
||||
expected: 225,
|
||||
},
|
||||
{
|
||||
name: "Last",
|
||||
input: "last",
|
||||
expected: 224,
|
||||
},
|
||||
{
|
||||
name: "RelativeZero",
|
||||
input: "-0",
|
||||
expected: 225,
|
||||
},
|
||||
{
|
||||
name: "Relative",
|
||||
input: "-5",
|
||||
expected: 220,
|
||||
},
|
||||
{
|
||||
name: "RelativeFar",
|
||||
input: "-500",
|
||||
expected: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
epoch, err := util.ParseEpoch(ctx, chainTime, test.input)
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.expected, epoch)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ var Log zerolog.Logger
|
||||
func InitLogging() error {
|
||||
// Change the output file.
|
||||
if viper.GetString("log-file") != "" {
|
||||
f, err := os.OpenFile(viper.GetString("log-file"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
|
||||
f, err := os.OpenFile(viper.GetString("log-file"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o600)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to open log file")
|
||||
}
|
||||
|
||||
12
util/misc.go
12
util/misc.go
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2019 Weald Technology Trading
|
||||
// Copyright © 2019, 2022 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
|
||||
@@ -47,10 +47,13 @@ func SetupStore() error {
|
||||
return errors.New("basedir does not apply to the s3 store")
|
||||
}
|
||||
store, err = s3.New(s3.WithPassphrase([]byte(GetStorePassphrase("s3"))),
|
||||
s3.WithID([]byte(viper.GetString("stores.s3.id"))),
|
||||
s3.WithEndpoint(viper.GetString("stores.s3.endpoint")),
|
||||
s3.WithRegion(viper.GetString("stores.s3.region")),
|
||||
s3.WithBucket(viper.GetString("stores.s3.bucket")),
|
||||
s3.WithPath(viper.GetString("stores.s3.path")),
|
||||
s3.WithCredentialsID(viper.GetString("stores.s3.credentials.id")),
|
||||
s3.WithCredentialsSecret(viper.GetString("stores.s3.credentials.secret")),
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to access Amazon S3 wallet store")
|
||||
@@ -111,7 +114,12 @@ func WalletFromPath(ctx context.Context, path string) (e2wtypes.Wallet, error) {
|
||||
return nil, errors.Wrap(err, "failed to parse remote servers")
|
||||
}
|
||||
|
||||
return dirk.OpenWallet(ctx, walletName, credentials, endpoints)
|
||||
return dirk.Open(ctx,
|
||||
dirk.WithName(walletName),
|
||||
dirk.WithCredentials(credentials),
|
||||
dirk.WithEndpoints(endpoints),
|
||||
dirk.WithTimeout(viper.GetDuration("timeout")),
|
||||
)
|
||||
}
|
||||
wallet, err := e2wallet.OpenWallet(walletName)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2020, 2022 Weald Technology Trading
|
||||
// Copyright © 2020 - 2023 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
|
||||
@@ -19,12 +19,25 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tyler-smith/go-bip39"
|
||||
"github.com/tyler-smith/go-bip39/wordlists"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
// hdPathRegex is the regular expression that matches an HD path.
|
||||
var hdPathRegex = regexp.MustCompile("^m/[0-9]+/[0-9]+(/[0-9+])+")
|
||||
|
||||
var mnemonicWordLists = [][]string{
|
||||
wordlists.English,
|
||||
wordlists.ChineseSimplified,
|
||||
wordlists.ChineseTraditional,
|
||||
wordlists.Czech,
|
||||
wordlists.French,
|
||||
wordlists.Italian,
|
||||
wordlists.Japanese,
|
||||
wordlists.Korean,
|
||||
wordlists.Spanish,
|
||||
}
|
||||
|
||||
// SeedFromMnemonic creates a seed from a mnemonic.
|
||||
func SeedFromMnemonic(mnemonic string) ([]byte, error) {
|
||||
// If there are more than 24 words we treat the additional characters as the passphrase.
|
||||
@@ -38,10 +51,14 @@ func SeedFromMnemonic(mnemonic string) ([]byte, error) {
|
||||
mnemonic = string(norm.NFKD.Bytes([]byte(mnemonic)))
|
||||
mnemonicPassphrase = string(norm.NFKD.Bytes([]byte(mnemonicPassphrase)))
|
||||
|
||||
if !bip39.IsMnemonicValid(mnemonic) {
|
||||
return nil, errors.New("mnemonic is invalid")
|
||||
// Try with the various word lists.
|
||||
for _, wl := range mnemonicWordLists {
|
||||
bip39.SetWordList(wl)
|
||||
seed, err := bip39.NewSeedWithErrorChecking(mnemonic, mnemonicPassphrase)
|
||||
if err == nil {
|
||||
return seed, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Create seed from mnemonic and passphrase.
|
||||
return bip39.NewSeed(mnemonic, mnemonicPassphrase), nil
|
||||
return nil, errors.New("mnemonic is invalid")
|
||||
}
|
||||
|
||||
72
util/mnemonic_test.go
Normal file
72
util/mnemonic_test.go
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright © 2023 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_test
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
)
|
||||
|
||||
func bytesStr(input string) []byte {
|
||||
bytes, err := hex.DecodeString(strings.TrimPrefix(input, "0x"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
||||
func TestSeedFromMnemonic(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
mnemonic string
|
||||
seed []byte
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "Empty",
|
||||
err: "mnemonic is invalid",
|
||||
},
|
||||
{
|
||||
name: "Default",
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
seed: bytesStr("0x408b285c123836004f4b8842c89324c1f01382450c0d439af345ba7fc49acf705489c6fc77dbd4e3dc1dd8cc6bc9f043db8ada1e243c4a0eafb290d399480840"),
|
||||
},
|
||||
{
|
||||
name: "English",
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
seed: bytesStr("0x408b285c123836004f4b8842c89324c1f01382450c0d439af345ba7fc49acf705489c6fc77dbd4e3dc1dd8cc6bc9f043db8ada1e243c4a0eafb290d399480840"),
|
||||
},
|
||||
{
|
||||
name: "Spanish",
|
||||
mnemonic: "ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ábaco ancla",
|
||||
seed: bytesStr("0x1e0de8aa97db3c7988f692d9c6151968be89debdbd71b1e34cab15d15ec10eed33412891129e1274fb84624565fd835f7e56df22a997439fca3da05c9c82a156"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
seed, err := util.SeedFromMnemonic(test.mnemonic)
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.seed, seed)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
// SignRoot signs the hash tree root of a data structure
|
||||
// SignRoot signs the hash tree root of a data structure.
|
||||
func SignRoot(account e2wtypes.Account, root spec.Root, domain spec.Domain) (e2types.Signature, error) {
|
||||
if _, isProtectingSigner := account.(e2wtypes.AccountProtectingSigner); isProtectingSigner {
|
||||
// Signer builds the signing data.
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
)
|
||||
|
||||
func TestUnmarshal(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
in []byte
|
||||
|
||||
Reference in New Issue
Block a user