mirror of
https://github.com/wealdtech/ethdo.git
synced 2026-01-10 14:37:57 -05:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d274ab3db0 | ||
|
|
ad3d8606fd | ||
|
|
30455e7c43 | ||
|
|
7eb2c68a19 | ||
|
|
2d96f7cb13 | ||
|
|
fd1e4a97bb | ||
|
|
be2270c543 | ||
|
|
0c36239b8b | ||
|
|
f78b2922ec | ||
|
|
bcf6ffdaf0 | ||
|
|
9fc184f6a1 | ||
|
|
c9a30a6e4b | ||
|
|
1ec6ddc914 | ||
|
|
2c96ef958e | ||
|
|
3c10131c45 | ||
|
|
fe0bfd4f87 | ||
|
|
290413f115 | ||
|
|
4aa6bef6a3 | ||
|
|
1b0f4e2803 | ||
|
|
301224748c | ||
|
|
1e15b836c2 | ||
|
|
1e709b7592 | ||
|
|
8744a85cb7 | ||
|
|
92ad77d8f5 | ||
|
|
2298640e4c | ||
|
|
5baef59672 | ||
|
|
e54e8affa7 |
31
.github/workflows/golangci-lint.yml
vendored
31
.github/workflows/golangci-lint.yml
vendored
@@ -1,23 +1,22 @@
|
||||
name: golangci-lint
|
||||
on: [ push, pull_request ]
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
golangci:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
||||
version: v1.45
|
||||
|
||||
# Optional: working directory, useful for monorepos
|
||||
# working-directory: somedir
|
||||
|
||||
# Optional: golangci-lint command line arguments.
|
||||
args: --timeout=10m
|
||||
|
||||
# Optional: show only new issues if it's a pull request. The default value is `false`.
|
||||
# only-new-issues: true
|
||||
go-version: 1.17
|
||||
- uses: actions/checkout@v3
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: latest
|
||||
args: --timeout 5m
|
||||
|
||||
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
@@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
- 't*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -12,10 +13,9 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ^1.17
|
||||
id: go
|
||||
go-version: 1.17
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
@@ -36,9 +36,9 @@ jobs:
|
||||
echo "RELEASE_TAG=${RELEASE_TAG}" >> $GITHUB_ENV
|
||||
echo "::set-output name=RELEASE_TAG::${RELEASE_TAG}"
|
||||
# Ensure the release tag has expected format.
|
||||
echo ${RELEASE_TAG} | grep -q '^v' || exit 1
|
||||
echo ${RELEASE_TAG} | grep -q '^[vt]' || exit 1
|
||||
# Release version is same as release tag without leading 'v'.
|
||||
RELEASE_VERSION=$(echo ${GITHUB_REF} | sed -e 's!.*/v!!')
|
||||
RELEASE_VERSION=$(echo ${GITHUB_REF} | sed -e 's!.*/[vt]!!')
|
||||
echo "RELEASE_VERSION=${RELEASE_VERSION}" >> $GITHUB_ENV
|
||||
echo "::set-output name=RELEASE_VERSION::${RELEASE_VERSION}"
|
||||
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
|
||||
- name: Fetch xgo
|
||||
run: |
|
||||
go install github.com/crazy-max/xgo@v0.14.0
|
||||
go install github.com/wealdtech/xgo@latest
|
||||
|
||||
- name: Cross-compile linux
|
||||
run: |
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -18,5 +18,8 @@ coverage.html
|
||||
# Vim
|
||||
*.sw?
|
||||
|
||||
# Local JSON files
|
||||
*.json
|
||||
|
||||
# Local TODO
|
||||
TODO.md
|
||||
|
||||
12
CHANGELOG.md
12
CHANGELOG.md
@@ -1,3 +1,15 @@
|
||||
1.26.0
|
||||
- add commands and documentation to set user validator credentials (not usable until capella)
|
||||
|
||||
1.25.3
|
||||
- add more information to "epoch summary"
|
||||
- add "validator summary"
|
||||
|
||||
1.25.2:
|
||||
- no longer require connection parameter
|
||||
- support "block analyze" on bellatrix (thanks @tcrossland)
|
||||
- check deposit message root match for verifying deposits (thanks @aaron-alderman)
|
||||
|
||||
1.25.0:
|
||||
- add "proposer duties"
|
||||
- add deposit signature verification to "deposit verify"
|
||||
|
||||
@@ -35,7 +35,7 @@ docker pull wealdtech/ethdo
|
||||
`ethdo` is a standard Go program which can be installed with:
|
||||
|
||||
```sh
|
||||
GO111MODULE=on go get github.com/wealdtech/ethdo
|
||||
go install github.com/wealdtech/ethdo@latest
|
||||
```
|
||||
|
||||
Note that `ethdo` requires at least version 1.13 of go to operate. The version of go can be found with `go version`.
|
||||
|
||||
@@ -1,315 +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 accountcreate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/attestantio/dirk/testing/daemon"
|
||||
"github.com/attestantio/dirk/testing/resources"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
e2types "github.com/wealdtech/go-eth2-types/v2"
|
||||
dirk "github.com/wealdtech/go-eth2-wallet-dirk"
|
||||
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
|
||||
hd "github.com/wealdtech/go-eth2-wallet-hd/v2"
|
||||
nd "github.com/wealdtech/go-eth2-wallet-nd/v2"
|
||||
scratch "github.com/wealdtech/go-eth2-wallet-store-scratch"
|
||||
"google.golang.org/grpc/credentials"
|
||||
)
|
||||
|
||||
func TestProcess(t *testing.T) {
|
||||
require.NoError(t, e2types.InitBLS())
|
||||
|
||||
testNDWallet, err := nd.CreateWallet(context.Background(),
|
||||
"Test",
|
||||
scratch.New(),
|
||||
keystorev4.New(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
testHDWallet, err := hd.CreateWallet(context.Background(),
|
||||
"Test",
|
||||
[]byte("pass"),
|
||||
scratch.New(),
|
||||
keystorev4.New(),
|
||||
[]byte{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// #nosec G404
|
||||
port1 := uint32(12000 + rand.Intn(4000))
|
||||
// #nosec G404
|
||||
port2 := uint32(12000 + rand.Intn(4000))
|
||||
// #nosec G404
|
||||
port3 := uint32(12000 + rand.Intn(4000))
|
||||
peers := map[uint64]string{
|
||||
1: fmt.Sprintf("signer-test01:%d", port1),
|
||||
2: fmt.Sprintf("signer-test02:%d", port2),
|
||||
3: fmt.Sprintf("signer-test03:%d", port3),
|
||||
}
|
||||
_, path, err := daemon.New(context.Background(), "", 1, port1, peers)
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(path)
|
||||
_, path, err = daemon.New(context.Background(), "", 2, port2, peers)
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(path)
|
||||
_, path, err = daemon.New(context.Background(), "", 3, port3, peers)
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(path)
|
||||
endpoints := []*dirk.Endpoint{
|
||||
dirk.NewEndpoint("signer-test01", port1),
|
||||
dirk.NewEndpoint("signer-test02", port2),
|
||||
dirk.NewEndpoint("signer-test03", port3),
|
||||
}
|
||||
credentials, err := credentialsFromCerts(context.Background(), resources.ClientTest01Crt, resources.ClientTest01Key, resources.CACrt)
|
||||
require.NoError(t, err)
|
||||
testDistributedWallet, err := dirk.OpenWallet(context.Background(), "Wallet 3", credentials, endpoints)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
dataIn *dataIn
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "Nil",
|
||||
err: "no data",
|
||||
},
|
||||
{
|
||||
name: "WalletPassphraseIncorrect",
|
||||
dataIn: &dataIn{
|
||||
timeout: 5 * time.Second,
|
||||
wallet: testHDWallet,
|
||||
accountName: "Good",
|
||||
passphrase: "ce%NohGhah4ye5ra",
|
||||
walletPassphrase: "bad",
|
||||
participants: 1,
|
||||
signingThreshold: 1,
|
||||
},
|
||||
err: "failed to unlock wallet: incorrect passphrase",
|
||||
},
|
||||
{
|
||||
name: "PassphraseMissing",
|
||||
dataIn: &dataIn{
|
||||
timeout: 5 * time.Second,
|
||||
wallet: testHDWallet,
|
||||
accountName: "Good",
|
||||
passphrase: "",
|
||||
walletPassphrase: "pass",
|
||||
participants: 1,
|
||||
signingThreshold: 1,
|
||||
},
|
||||
err: "passphrase is required",
|
||||
},
|
||||
{
|
||||
name: "PassphraseWeak",
|
||||
dataIn: &dataIn{
|
||||
timeout: 5 * time.Second,
|
||||
wallet: testHDWallet,
|
||||
accountName: "Good",
|
||||
passphrase: "poor",
|
||||
walletPassphrase: "pass",
|
||||
participants: 1,
|
||||
signingThreshold: 1,
|
||||
},
|
||||
err: "supplied passphrase is weak; use a stronger one or run with the --allow-weak-passphrases flag",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
dataIn: &dataIn{
|
||||
timeout: 5 * time.Second,
|
||||
wallet: testHDWallet,
|
||||
accountName: "Good",
|
||||
passphrase: "ce%NohGhah4ye5ra",
|
||||
walletPassphrase: "pass",
|
||||
participants: 1,
|
||||
signingThreshold: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "PathMalformed",
|
||||
dataIn: &dataIn{
|
||||
timeout: 5 * time.Second,
|
||||
wallet: testHDWallet,
|
||||
accountName: "Pathed",
|
||||
passphrase: "ce%NohGhah4ye5ra",
|
||||
walletPassphrase: "pass",
|
||||
participants: 1,
|
||||
signingThreshold: 1,
|
||||
path: "n/12381/3600/1/2/3",
|
||||
},
|
||||
err: "path does not match expected format m/…",
|
||||
},
|
||||
{
|
||||
name: "PathPassphraseMissing",
|
||||
dataIn: &dataIn{
|
||||
timeout: 5 * time.Second,
|
||||
wallet: testHDWallet,
|
||||
accountName: "Pathed",
|
||||
passphrase: "",
|
||||
walletPassphrase: "pass",
|
||||
participants: 1,
|
||||
signingThreshold: 1,
|
||||
path: "m/12381/3600/1/2/3",
|
||||
},
|
||||
err: "passphrase is required",
|
||||
},
|
||||
{
|
||||
name: "PathNotSupported",
|
||||
dataIn: &dataIn{
|
||||
timeout: 5 * time.Second,
|
||||
wallet: testNDWallet,
|
||||
accountName: "Pathed",
|
||||
passphrase: "ce%NohGhah4ye5ra",
|
||||
walletPassphrase: "pass",
|
||||
participants: 1,
|
||||
signingThreshold: 1,
|
||||
path: "m/12381/3600/1/2/3",
|
||||
},
|
||||
err: "wallet does not support account creation with an explicit path",
|
||||
},
|
||||
{
|
||||
name: "GoodWithPath",
|
||||
dataIn: &dataIn{
|
||||
timeout: 5 * time.Second,
|
||||
wallet: testHDWallet,
|
||||
accountName: "Pathed",
|
||||
passphrase: "ce%NohGhah4ye5ra",
|
||||
walletPassphrase: "pass",
|
||||
participants: 1,
|
||||
signingThreshold: 1,
|
||||
path: "m/12381/3600/1/2/3",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "DistributedSigningThresholdZero",
|
||||
dataIn: &dataIn{
|
||||
timeout: 5 * time.Second,
|
||||
wallet: testDistributedWallet,
|
||||
accountName: "Remote",
|
||||
passphrase: "ce%NohGhah4ye5ra",
|
||||
walletPassphrase: "pass",
|
||||
participants: 3,
|
||||
signingThreshold: 0,
|
||||
},
|
||||
err: "signing threshold required",
|
||||
},
|
||||
{
|
||||
name: "DistributedSigningThresholdNotHalf",
|
||||
dataIn: &dataIn{
|
||||
timeout: 5 * time.Second,
|
||||
wallet: testDistributedWallet,
|
||||
accountName: "Remote",
|
||||
passphrase: "ce%NohGhah4ye5ra",
|
||||
walletPassphrase: "pass",
|
||||
participants: 3,
|
||||
signingThreshold: 1,
|
||||
},
|
||||
err: "signing threshold must be more than half the number of participants",
|
||||
},
|
||||
{
|
||||
name: "DistributedSigningThresholdTooHigh",
|
||||
dataIn: &dataIn{
|
||||
timeout: 5 * time.Second,
|
||||
wallet: testDistributedWallet,
|
||||
accountName: "Remote",
|
||||
passphrase: "ce%NohGhah4ye5ra",
|
||||
walletPassphrase: "pass",
|
||||
participants: 3,
|
||||
signingThreshold: 4,
|
||||
},
|
||||
err: "signing threshold cannot be higher than the number of participants",
|
||||
},
|
||||
{
|
||||
name: "DistributedNotSupported",
|
||||
dataIn: &dataIn{
|
||||
timeout: 5 * time.Second,
|
||||
wallet: testNDWallet,
|
||||
accountName: "Remote",
|
||||
passphrase: "ce%NohGhah4ye5ra",
|
||||
walletPassphrase: "pass",
|
||||
participants: 3,
|
||||
signingThreshold: 2,
|
||||
},
|
||||
err: "wallet does not support distributed account creation",
|
||||
},
|
||||
{
|
||||
name: "DistributedGood",
|
||||
dataIn: &dataIn{
|
||||
timeout: 5 * time.Second,
|
||||
wallet: testDistributedWallet,
|
||||
accountName: "Remote",
|
||||
passphrase: "ce%NohGhah4ye5ra",
|
||||
walletPassphrase: "pass",
|
||||
participants: 3,
|
||||
signingThreshold: 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
res, err := process(context.Background(), test.dataIn)
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.dataIn.accountName, res.account.Name())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNilData(t *testing.T) {
|
||||
_, err := processStandard(context.Background(), nil)
|
||||
require.EqualError(t, err, "no data")
|
||||
_, err = processPathed(context.Background(), nil)
|
||||
require.EqualError(t, err, "no data")
|
||||
_, err = processDistributed(context.Background(), nil)
|
||||
require.EqualError(t, err, "no data")
|
||||
}
|
||||
|
||||
func credentialsFromCerts(ctx context.Context, clientCert []byte, clientKey []byte, caCert []byte) (credentials.TransportCredentials, error) {
|
||||
clientPair, err := tls.X509KeyPair(clientCert, clientKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to load client keypair")
|
||||
}
|
||||
|
||||
tlsCfg := &tls.Config{
|
||||
Certificates: []tls.Certificate{clientPair},
|
||||
MinVersion: tls.VersionTLS13,
|
||||
}
|
||||
|
||||
if caCert != nil {
|
||||
cp := x509.NewCertPool()
|
||||
if !cp.AppendCertsFromPEM(caCert) {
|
||||
return nil, errors.New("failed to add CA certificate")
|
||||
}
|
||||
tlsCfg.RootCAs = cp
|
||||
}
|
||||
|
||||
return credentials.NewTLS(tlsCfg), nil
|
||||
}
|
||||
@@ -42,11 +42,13 @@ func output(ctx context.Context, data *dataOut) (string, error) {
|
||||
if data.showPrivateKey {
|
||||
builder.WriteString(fmt.Sprintf("Private key: %#x\n", data.key.Marshal()))
|
||||
}
|
||||
builder.WriteString(fmt.Sprintf("Public key: %#x", data.key.PublicKey().Marshal()))
|
||||
if data.showWithdrawalCredentials {
|
||||
withdrawalCredentials := util.SHA256(data.key.PublicKey().Marshal())
|
||||
withdrawalCredentials[0] = byte(0) // BLS_WITHDRAWAL_PREFIX
|
||||
builder.WriteString(fmt.Sprintf("\nWithdrawal credentials: %#x", withdrawalCredentials))
|
||||
builder.WriteString(fmt.Sprintf("Withdrawal credentials: %#x\n", withdrawalCredentials))
|
||||
}
|
||||
if !(data.showPrivateKey || data.showWithdrawalCredentials) {
|
||||
builder.WriteString(fmt.Sprintf("Public key: %#x\n", data.key.PublicKey().Marshal()))
|
||||
}
|
||||
|
||||
return builder.String(), nil
|
||||
|
||||
@@ -64,7 +64,7 @@ func TestOutput(t *testing.T) {
|
||||
key: blsPrivateKey("0x068dce0c90cb428ab37a74af0191eac49648035f1aaef077734b91e05985ec55"),
|
||||
showPrivateKey: true,
|
||||
},
|
||||
needs: []string{"Public key", "Private key"},
|
||||
needs: []string{"Private key"},
|
||||
},
|
||||
{
|
||||
name: "WithdrawalCredentials",
|
||||
@@ -72,7 +72,7 @@ func TestOutput(t *testing.T) {
|
||||
key: blsPrivateKey("0x068dce0c90cb428ab37a74af0191eac49648035f1aaef077734b91e05985ec55"),
|
||||
showWithdrawalCredentials: true,
|
||||
},
|
||||
needs: []string{"Public key", "Withdrawal credentials"},
|
||||
needs: []string{"Withdrawal credentials"},
|
||||
},
|
||||
{
|
||||
name: "All",
|
||||
@@ -81,7 +81,7 @@ func TestOutput(t *testing.T) {
|
||||
showPrivateKey: true,
|
||||
showWithdrawalCredentials: true,
|
||||
},
|
||||
needs: []string{"Public key", "Private key", "Withdrawal credentials"},
|
||||
needs: []string{"Private key", "Withdrawal credentials"},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -15,57 +15,32 @@ package accountderive
|
||||
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tyler-smith/go-bip39"
|
||||
util "github.com/wealdtech/go-eth2-util"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
e2types "github.com/wealdtech/go-eth2-types/v2"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
// pathRegex is the regular expression that matches an HD path.
|
||||
var pathRegex = regexp.MustCompile("^m/[0-9]+/[0-9]+(/[0-9+])+")
|
||||
|
||||
func process(ctx context.Context, data *dataIn) (*dataOut, error) {
|
||||
if data == nil {
|
||||
return nil, errors.New("no data")
|
||||
}
|
||||
|
||||
// If there are more than 24 words we treat the additional characters as the passphrase.
|
||||
mnemonicParts := strings.Split(data.mnemonic, " ")
|
||||
mnemonicPassphrase := ""
|
||||
if len(mnemonicParts) > 24 {
|
||||
data.mnemonic = strings.Join(mnemonicParts[:24], " ")
|
||||
mnemonicPassphrase = strings.Join(mnemonicParts[24:], " ")
|
||||
}
|
||||
// Normalise the input.
|
||||
data.mnemonic = string(norm.NFKD.Bytes([]byte(data.mnemonic)))
|
||||
mnemonicPassphrase = string(norm.NFKD.Bytes([]byte(mnemonicPassphrase)))
|
||||
|
||||
if !bip39.IsMnemonicValid(data.mnemonic) {
|
||||
return nil, errors.New("mnemonic is invalid")
|
||||
}
|
||||
|
||||
// Create seed from mnemonic and passphrase.
|
||||
seed := bip39.NewSeed(data.mnemonic, mnemonicPassphrase)
|
||||
|
||||
// Ensure the path is valid.
|
||||
match := pathRegex.Match([]byte(data.path))
|
||||
if !match {
|
||||
return nil, errors.New("path does not match expected format m/…")
|
||||
}
|
||||
|
||||
// Derive private key from seed and path.
|
||||
key, err := util.PrivateKeyFromSeedAndPath(seed, data.path)
|
||||
account, err := util.ParseAccount(ctx, data.mnemonic, []string{data.path}, true)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to generate key")
|
||||
return nil, errors.Wrap(err, "failed to derive account")
|
||||
}
|
||||
|
||||
key, err := account.(e2wtypes.AccountPrivateKeyProvider).PrivateKey(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain account private key")
|
||||
}
|
||||
|
||||
results := &dataOut{
|
||||
showPrivateKey: data.showPrivateKey,
|
||||
showPrivateKey: data.showPrivateKey,
|
||||
showWithdrawalCredentials: data.showWithdrawalCredentials,
|
||||
key: key,
|
||||
key: key.(*e2types.BLSPrivateKey),
|
||||
}
|
||||
|
||||
return results, nil
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestProcess(t *testing.T) {
|
||||
dataIn: &dataIn{
|
||||
path: "m/12381/3600/0/0",
|
||||
},
|
||||
err: "mnemonic is invalid",
|
||||
err: "failed to derive account: no account specified",
|
||||
},
|
||||
{
|
||||
name: "MnemonicInvalid",
|
||||
@@ -48,14 +48,14 @@ func TestProcess(t *testing.T) {
|
||||
mnemonic: "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",
|
||||
},
|
||||
err: "mnemonic is invalid",
|
||||
err: "failed to derive account: mnemonic is invalid",
|
||||
},
|
||||
{
|
||||
name: "PathMissing",
|
||||
dataIn: &dataIn{
|
||||
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",
|
||||
},
|
||||
err: "path does not match expected format m/…",
|
||||
err: "failed to derive account: path does not match expected format m/…",
|
||||
},
|
||||
{
|
||||
name: "PathInvalid",
|
||||
@@ -63,7 +63,7 @@ func TestProcess(t *testing.T) {
|
||||
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: "n/12381/3600/0/0",
|
||||
},
|
||||
err: "path does not match expected format m/…",
|
||||
err: "failed to derive account: path does not match expected format m/…",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
|
||||
@@ -15,6 +15,7 @@ package accountderive
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -45,5 +46,5 @@ func Run(cmd *cobra.Command) (string, error) {
|
||||
return "", errors.Wrap(err, "failed to obtain output")
|
||||
}
|
||||
|
||||
return results, nil
|
||||
return strings.TrimSuffix(results, "\n"), nil
|
||||
}
|
||||
|
||||
@@ -49,7 +49,6 @@ func init() {
|
||||
accountFlags(accountCreateCmd)
|
||||
accountCreateCmd.Flags().Uint32("participants", 1, "Number of participants (1 for non-distributed accounts, >1 for distributed accounts)")
|
||||
accountCreateCmd.Flags().Uint32("signing-threshold", 1, "Signing threshold (1 for non-distributed accounts)")
|
||||
accountCreateCmd.Flags().String("path", "", "path of account (for hierarchical deterministic accounts)")
|
||||
}
|
||||
|
||||
func accountCreateBindings() {
|
||||
@@ -59,7 +58,4 @@ func accountCreateBindings() {
|
||||
if err := viper.BindPFlag("signing-threshold", accountCreateCmd.Flags().Lookup("signing-threshold")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("path", accountCreateCmd.Flags().Lookup("path")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,19 +47,11 @@ In quiet mode this will return 0 if the inputs can derive an account account, ot
|
||||
func init() {
|
||||
accountCmd.AddCommand(accountDeriveCmd)
|
||||
accountFlags(accountDeriveCmd)
|
||||
accountDeriveCmd.Flags().String("mnemonic", "", "mnemonic from which to derive the HD seed")
|
||||
accountDeriveCmd.Flags().String("path", "", "path from which to derive the account")
|
||||
accountDeriveCmd.Flags().Bool("show-private-key", false, "show private key for derived account")
|
||||
accountDeriveCmd.Flags().Bool("show-withdrawal-credentials", false, "show withdrawal credentials for derived account")
|
||||
}
|
||||
|
||||
func accountDeriveBindings() {
|
||||
if err := viper.BindPFlag("mnemonic", accountDeriveCmd.Flags().Lookup("mnemonic")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("path", accountDeriveCmd.Flags().Lookup("path")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("show-private-key", accountDeriveCmd.Flags().Lookup("show-private-key")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -122,9 +122,6 @@ func newCommand(ctx context.Context) (*command, error) {
|
||||
}
|
||||
c.timeout = viper.GetDuration("timeout")
|
||||
|
||||
if viper.GetString("connection") == "" {
|
||||
return nil, errors.New("connection is required")
|
||||
}
|
||||
c.connection = viper.GetString("connection")
|
||||
c.allowInsecureConnections = viper.GetBool("allow-insecure-connections")
|
||||
|
||||
|
||||
@@ -37,14 +37,6 @@ func TestInput(t *testing.T) {
|
||||
vars: map[string]interface{}{},
|
||||
err: "timeout is required",
|
||||
},
|
||||
{
|
||||
name: "ConnectionMissing",
|
||||
vars: map[string]interface{}{
|
||||
"blockid": "1",
|
||||
"timeout": "5s",
|
||||
},
|
||||
err: "connection is required",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
vars: map[string]interface{}{
|
||||
|
||||
@@ -425,6 +425,13 @@ func (c *command) analyzeSyncCommittees(ctx context.Context, block *spec.Version
|
||||
c.analysis.SyncCommitee.Value = c.analysis.SyncCommitee.Score * float64(c.analysis.SyncCommitee.Contributions)
|
||||
c.analysis.Value += c.analysis.SyncCommitee.Value
|
||||
return nil
|
||||
case spec.DataVersionBellatrix:
|
||||
c.analysis.SyncCommitee.Contributions = int(block.Bellatrix.Message.Body.SyncAggregate.SyncCommitteeBits.Count())
|
||||
c.analysis.SyncCommitee.PossibleContributions = int(block.Bellatrix.Message.Body.SyncAggregate.SyncCommitteeBits.Len())
|
||||
c.analysis.SyncCommitee.Score = float64(c.syncRewardWeight) / float64(c.weightDenominator)
|
||||
c.analysis.SyncCommitee.Value = c.analysis.SyncCommitee.Score * float64(c.analysis.SyncCommitee.Contributions)
|
||||
c.analysis.Value += c.analysis.SyncCommitee.Value
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("unsupported block version %d", block.Version)
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@ func outputBellatrixBlockText(ctx context.Context, data *dataOut, signedBlock *b
|
||||
bodyRoot,
|
||||
signedBlock.Message.ParentRoot,
|
||||
signedBlock.Message.StateRoot,
|
||||
signedBlock.Message.Body.Graffiti,
|
||||
signedBlock.Message.Body.Graffiti[:],
|
||||
data.genesisTime,
|
||||
data.slotDuration,
|
||||
data.slotsPerEpoch)
|
||||
@@ -386,7 +386,7 @@ func outputAltairBlockText(ctx context.Context, data *dataOut, signedBlock *alta
|
||||
bodyRoot,
|
||||
signedBlock.Message.ParentRoot,
|
||||
signedBlock.Message.StateRoot,
|
||||
signedBlock.Message.Body.Graffiti,
|
||||
signedBlock.Message.Body.Graffiti[:],
|
||||
data.genesisTime,
|
||||
data.slotDuration,
|
||||
data.slotsPerEpoch)
|
||||
@@ -469,7 +469,7 @@ func outputPhase0BlockText(ctx context.Context, data *dataOut, signedBlock *phas
|
||||
bodyRoot,
|
||||
signedBlock.Message.ParentRoot,
|
||||
signedBlock.Message.StateRoot,
|
||||
signedBlock.Message.Body.Graffiti,
|
||||
signedBlock.Message.Body.Graffiti[:],
|
||||
data.genesisTime,
|
||||
data.slotDuration,
|
||||
data.slotsPerEpoch)
|
||||
@@ -540,6 +540,8 @@ func outputBlockExecutionPayload(ctx context.Context,
|
||||
if !verbose {
|
||||
res.WriteString("Execution block number: ")
|
||||
res.WriteString(fmt.Sprintf("%d\n", payload.BlockNumber))
|
||||
res.WriteString("Transactions: ")
|
||||
res.WriteString(fmt.Sprintf("%d\n", len(payload.Transactions)))
|
||||
} else {
|
||||
res.WriteString("Execution payload:\n")
|
||||
res.WriteString(" Execution block number: ")
|
||||
@@ -573,6 +575,8 @@ func outputBlockExecutionPayload(ctx context.Context,
|
||||
res.WriteString(fmt.Sprintf("%#x\n", payload.ExtraData))
|
||||
res.WriteString(" Logs bloom: ")
|
||||
res.WriteString(fmt.Sprintf("%#x\n", payload.LogsBloom))
|
||||
res.WriteString(" Transactions: ")
|
||||
res.WriteString(fmt.Sprintf("%d\n", len(payload.Transactions)))
|
||||
}
|
||||
|
||||
return res.String(), nil
|
||||
|
||||
@@ -82,6 +82,9 @@ func process(ctx context.Context, data *dataIn) (*dataOut, error) {
|
||||
if data.stream {
|
||||
jsonOutput = data.jsonOutput
|
||||
sszOutput = data.sszOutput
|
||||
if !jsonOutput && !sszOutput {
|
||||
fmt.Println("")
|
||||
}
|
||||
err := data.eth2Client.(eth2client.EventsProvider).Events(ctx, []string{"head"}, headEventHandler)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to start block stream")
|
||||
@@ -101,13 +104,13 @@ func headEventHandler(event *api.Event) {
|
||||
blockID := fmt.Sprintf("%#x", event.Data.(*api.HeadEvent).Block[:])
|
||||
signedBlock, err := results.eth2Client.(eth2client.SignedBeaconBlockProvider).SignedBeaconBlock(context.Background(), blockID)
|
||||
if err != nil {
|
||||
if !jsonOutput {
|
||||
if !jsonOutput && !sszOutput {
|
||||
fmt.Printf("Failed to obtain block: %v\n", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if signedBlock == nil {
|
||||
if !jsonOutput {
|
||||
if !jsonOutput && !sszOutput {
|
||||
fmt.Println("Empty beacon block")
|
||||
}
|
||||
return
|
||||
@@ -115,31 +118,34 @@ func headEventHandler(event *api.Event) {
|
||||
switch signedBlock.Version {
|
||||
case spec.DataVersionPhase0:
|
||||
if err := outputPhase0Block(context.Background(), jsonOutput, signedBlock.Phase0); err != nil {
|
||||
if !jsonOutput {
|
||||
if !jsonOutput && !sszOutput {
|
||||
fmt.Printf("Failed to output block: %v\n", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
case spec.DataVersionAltair:
|
||||
if err := outputAltairBlock(context.Background(), jsonOutput, sszOutput, signedBlock.Altair); err != nil {
|
||||
if !jsonOutput {
|
||||
if !jsonOutput && !sszOutput {
|
||||
fmt.Printf("Failed to output block: %v\n", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
case spec.DataVersionBellatrix:
|
||||
if err := outputBellatrixBlock(context.Background(), jsonOutput, sszOutput, signedBlock.Bellatrix); err != nil {
|
||||
if !jsonOutput {
|
||||
if !jsonOutput && !sszOutput {
|
||||
fmt.Printf("Failed to output block: %v\n", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
default:
|
||||
if !jsonOutput {
|
||||
if !jsonOutput && !sszOutput {
|
||||
fmt.Printf("Unknown block version: %v\n", signedBlock.Version)
|
||||
}
|
||||
return
|
||||
}
|
||||
if !jsonOutput && !sszOutput {
|
||||
fmt.Println("")
|
||||
}
|
||||
}
|
||||
|
||||
func outputPhase0Block(ctx context.Context, jsonOutput bool, signedBlock *phase0.SignedBeaconBlock) error {
|
||||
@@ -155,7 +161,7 @@ func outputPhase0Block(ctx context.Context, jsonOutput bool, signedBlock *phase0
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to generate text")
|
||||
}
|
||||
fmt.Printf("%s\n", data)
|
||||
fmt.Print(data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -179,7 +185,7 @@ func outputAltairBlock(ctx context.Context, jsonOutput bool, sszOutput bool, sig
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to generate text")
|
||||
}
|
||||
fmt.Printf("%s\n", data)
|
||||
fmt.Print(data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -203,7 +209,7 @@ func outputBellatrixBlock(ctx context.Context, jsonOutput bool, sszOutput bool,
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to generate text")
|
||||
}
|
||||
fmt.Printf("%s\n", data)
|
||||
fmt.Print(data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -77,9 +77,6 @@ func newCommand(ctx context.Context) (*command, error) {
|
||||
c.xepoch = viper.GetString("epoch")
|
||||
c.xperiod = viper.GetString("period")
|
||||
|
||||
if viper.GetString("connection") == "" {
|
||||
return nil, errors.New("connection is required")
|
||||
}
|
||||
c.connection = viper.GetString("connection")
|
||||
c.allowInsecureConnections = viper.GetBool("allow-insecure-connections")
|
||||
|
||||
|
||||
@@ -37,14 +37,6 @@ func TestInput(t *testing.T) {
|
||||
vars: map[string]interface{}{},
|
||||
err: "timeout is required",
|
||||
},
|
||||
{
|
||||
name: "ConnectionMissing",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "5s",
|
||||
"data": "{}",
|
||||
},
|
||||
err: "connection is required",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
vars: map[string]interface{}{
|
||||
|
||||
@@ -65,9 +65,6 @@ func newCommand(ctx context.Context) (*command, error) {
|
||||
c.epoch = viper.GetString("epoch")
|
||||
}
|
||||
|
||||
if viper.GetString("connection") == "" {
|
||||
return nil, errors.New("connection is required")
|
||||
}
|
||||
c.connection = viper.GetString("connection")
|
||||
c.allowInsecureConnections = viper.GetBool("allow-insecure-connections")
|
||||
|
||||
|
||||
@@ -37,14 +37,6 @@ func TestInput(t *testing.T) {
|
||||
vars: map[string]interface{}{},
|
||||
err: "timeout is required",
|
||||
},
|
||||
{
|
||||
name: "ConnectionMissing",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "5s",
|
||||
"data": "{}",
|
||||
},
|
||||
err: "connection is required",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
vars: map[string]interface{}{
|
||||
|
||||
@@ -71,9 +71,6 @@ func input(ctx context.Context) (*dataIn, error) {
|
||||
return nil, errors.New("one of timestamp, slot or epoch required")
|
||||
}
|
||||
|
||||
if viper.GetString("connection") == "" {
|
||||
return nil, errors.New("connection is required")
|
||||
}
|
||||
data.connection = viper.GetString("connection")
|
||||
data.allowInsecureConnections = viper.GetBool("allow-insecure-connections")
|
||||
|
||||
|
||||
@@ -60,14 +60,6 @@ func TestInput(t *testing.T) {
|
||||
vars: map[string]interface{}{},
|
||||
err: "timeout is required",
|
||||
},
|
||||
{
|
||||
name: "ConnectionMissing",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "5s",
|
||||
"slot": "1",
|
||||
},
|
||||
err: "connection is required",
|
||||
},
|
||||
{
|
||||
name: "IDMissing",
|
||||
vars: map[string]interface{}{
|
||||
|
||||
@@ -76,9 +76,6 @@ func newCommand(ctx context.Context) (*command, error) {
|
||||
}
|
||||
c.data = viper.GetString("data")
|
||||
|
||||
if viper.GetString("connection") == "" {
|
||||
return nil, errors.New("connection is required")
|
||||
}
|
||||
c.connection = viper.GetString("connection")
|
||||
c.allowInsecureConnections = viper.GetBool("allow-insecure-connections")
|
||||
|
||||
|
||||
@@ -44,14 +44,6 @@ func TestInput(t *testing.T) {
|
||||
},
|
||||
err: "data is required",
|
||||
},
|
||||
{
|
||||
name: "ConnectionMissing",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "5s",
|
||||
"data": "{}",
|
||||
},
|
||||
err: "connection is required",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
vars: map[string]interface{}{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2020 Weald Technology Trading
|
||||
// Copyright © 2020, 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
|
||||
@@ -53,6 +53,11 @@ In quiet mode this will return 0 if the chain information can be obtained, other
|
||||
os.Exit(_exitSuccess)
|
||||
}
|
||||
|
||||
if viper.GetBool("prepare-offline") {
|
||||
fmt.Printf("Add the following to your command to run it offline:\n --offline --genesis-validators=root=%#x --fork-version=%#x\n", genesis.GenesisValidatorsRoot, fork.CurrentVersion)
|
||||
os.Exit(_exitSuccess)
|
||||
}
|
||||
|
||||
if genesis.GenesisTime.Unix() == 0 {
|
||||
fmt.Println("Genesis time: undefined")
|
||||
} else {
|
||||
@@ -84,4 +89,11 @@ In quiet mode this will return 0 if the chain information can be obtained, other
|
||||
func init() {
|
||||
chainCmd.AddCommand(chainInfoCmd)
|
||||
chainFlags(chainInfoCmd)
|
||||
chainInfoCmd.Flags().Bool("prepare-offline", false, "Provide information useful for offline commands")
|
||||
}
|
||||
|
||||
func chainInfoBindings() {
|
||||
if err := viper.BindPFlag("prepare-offline", chainInfoCmd.Flags().Lookup("prepare-offline")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,6 +275,13 @@ func verifyDeposit(deposit *util.DepositInfo, withdrawalCredentials []byte, vali
|
||||
return false, errors.Wrap(err, "failed to generate deposit message root")
|
||||
}
|
||||
|
||||
if bytes.Equal(deposit.DepositMessageRoot, depositMessageRoot[:]) {
|
||||
outputIf(!quiet, "Deposit message root verified")
|
||||
} else {
|
||||
outputIf(!quiet, "Deposit message root incorrect")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
domainBytes := e2types.Domain(e2types.DomainDeposit, forkVersion, e2types.ZeroGenesisValidatorsRoot)
|
||||
var domain phase0.Domain
|
||||
copy(domain[:], domainBytes)
|
||||
|
||||
@@ -40,13 +40,14 @@ type command struct {
|
||||
jsonOutput bool
|
||||
|
||||
// Data access.
|
||||
eth2Client eth2client.Service
|
||||
chainTime chaintime.Service
|
||||
proposerDutiesProvider eth2client.ProposerDutiesProvider
|
||||
blocksProvider eth2client.SignedBeaconBlockProvider
|
||||
syncCommitteesProvider eth2client.SyncCommitteesProvider
|
||||
validatorsProvider eth2client.ValidatorsProvider
|
||||
beaconCommitteesProvider eth2client.BeaconCommitteesProvider
|
||||
eth2Client eth2client.Service
|
||||
chainTime chaintime.Service
|
||||
proposerDutiesProvider eth2client.ProposerDutiesProvider
|
||||
blocksProvider eth2client.SignedBeaconBlockProvider
|
||||
syncCommitteesProvider eth2client.SyncCommitteesProvider
|
||||
validatorsProvider eth2client.ValidatorsProvider
|
||||
beaconCommitteesProvider eth2client.BeaconCommitteesProvider
|
||||
beaconBlockHeadersProvider eth2client.BeaconBlockHeadersProvider
|
||||
|
||||
// Results.
|
||||
summary *epochSummary
|
||||
@@ -60,6 +61,11 @@ type epochSummary struct {
|
||||
SyncCommittee []*epochSyncCommittee `json:"sync_committees"`
|
||||
ActiveValidators int `json:"active_validators"`
|
||||
ParticipatingValidators int `json:"participating_validators"`
|
||||
HeadCorrectValidators int `json:"head_correct_validators"`
|
||||
HeadTimelyValidators int `json:"head_timely_validators"`
|
||||
SourceTimelyValidators int `json:"source_timely_validators"`
|
||||
TargetCorrectValidators int `json:"target_correct_validators"`
|
||||
TargetTimelyValidators int `json:"target_timely_validators"`
|
||||
NonParticipatingValidators []*nonParticipatingValidator `json:"nonparticipating_validators"`
|
||||
}
|
||||
|
||||
@@ -94,9 +100,6 @@ func newCommand(ctx context.Context) (*command, error) {
|
||||
}
|
||||
c.timeout = viper.GetDuration("timeout")
|
||||
|
||||
if viper.GetString("connection") == "" {
|
||||
return nil, errors.New("connection is required")
|
||||
}
|
||||
c.connection = viper.GetString("connection")
|
||||
c.allowInsecureConnections = viper.GetBool("allow-insecure-connections")
|
||||
|
||||
|
||||
@@ -37,13 +37,6 @@ func TestInput(t *testing.T) {
|
||||
vars: map[string]interface{}{},
|
||||
err: "timeout is required",
|
||||
},
|
||||
{
|
||||
name: "ConnectionMissing",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "5s",
|
||||
},
|
||||
err: "connection is required",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
vars: map[string]interface{}{
|
||||
|
||||
@@ -70,6 +70,11 @@ func (c *command) outputTxt(_ context.Context) (string, error) {
|
||||
}
|
||||
|
||||
builder.WriteString(fmt.Sprintf("\n Attestations: %d/%d (%0.2f%%)", c.summary.ParticipatingValidators, c.summary.ActiveValidators, 100.0*float64(c.summary.ParticipatingValidators)/float64(c.summary.ActiveValidators)))
|
||||
builder.WriteString(fmt.Sprintf("\n Source timely: %d/%d (%0.2f%%)", c.summary.SourceTimelyValidators, c.summary.ActiveValidators, 100.0*float64(c.summary.SourceTimelyValidators)/float64(c.summary.ActiveValidators)))
|
||||
builder.WriteString(fmt.Sprintf("\n Target correct: %d/%d (%0.2f%%)", c.summary.TargetCorrectValidators, c.summary.ActiveValidators, 100.0*float64(c.summary.TargetCorrectValidators)/float64(c.summary.ActiveValidators)))
|
||||
builder.WriteString(fmt.Sprintf("\n Target timely: %d/%d (%0.2f%%)", c.summary.TargetTimelyValidators, c.summary.ActiveValidators, 100.0*float64(c.summary.TargetTimelyValidators)/float64(c.summary.ActiveValidators)))
|
||||
builder.WriteString(fmt.Sprintf("\n Head correct: %d/%d (%0.2f%%)", c.summary.HeadCorrectValidators, c.summary.ActiveValidators, 100.0*float64(c.summary.HeadCorrectValidators)/float64(c.summary.ActiveValidators)))
|
||||
builder.WriteString(fmt.Sprintf("\n Head timely: %d/%d (%0.2f%%)", c.summary.HeadTimelyValidators, c.summary.ActiveValidators, 100.0*float64(c.summary.HeadTimelyValidators)/float64(c.summary.ActiveValidators)))
|
||||
if c.verbose {
|
||||
// Sort list by validator index.
|
||||
for _, validator := range c.summary.NonParticipatingValidators {
|
||||
|
||||
@@ -79,11 +79,10 @@ func (c *command) processProposerDuties(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) processAttesterDuties(ctx context.Context) error {
|
||||
// Obtain all active validators for the given epoch.
|
||||
func (c *command) activeValidators(ctx context.Context) (map[phase0.ValidatorIndex]*apiv1.Validator, error) {
|
||||
validators, err := c.validatorsProvider.Validators(ctx, fmt.Sprintf("%d", c.chainTime.FirstSlotOfEpoch(c.summary.Epoch)), nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to obtain validators for epoch")
|
||||
return nil, errors.Wrap(err, "failed to obtain validators for epoch")
|
||||
}
|
||||
activeValidators := make(map[phase0.ValidatorIndex]*apiv1.Validator)
|
||||
for _, validator := range validators {
|
||||
@@ -92,6 +91,15 @@ func (c *command) processAttesterDuties(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
return activeValidators, nil
|
||||
}
|
||||
func (c *command) processAttesterDuties(ctx context.Context) error {
|
||||
activeValidators, err := c.activeValidators(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.summary.ActiveValidators = len(activeValidators)
|
||||
|
||||
// Obtain number of validators that voted for blocks in the epoch.
|
||||
// These votes can be included anywhere from the second slot of
|
||||
// the epoch to the first slot of the next-but-one epoch.
|
||||
@@ -101,61 +109,13 @@ func (c *command) processAttesterDuties(ctx context.Context) error {
|
||||
lastSlot = c.chainTime.CurrentSlot()
|
||||
}
|
||||
|
||||
votes := make(map[phase0.ValidatorIndex]struct{})
|
||||
allCommittees := make(map[phase0.Slot]map[phase0.CommitteeIndex][]phase0.ValidatorIndex)
|
||||
participations := make(map[phase0.ValidatorIndex]*nonParticipatingValidator)
|
||||
|
||||
for slot := firstSlot; slot <= lastSlot; slot++ {
|
||||
block, err := c.blocksProvider.SignedBeaconBlock(ctx, fmt.Sprintf("%d", slot))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, fmt.Sprintf("failed to obtain block for slot %d", slot))
|
||||
}
|
||||
if block == nil {
|
||||
// No block at this slot; that's fine.
|
||||
continue
|
||||
}
|
||||
attestations, err := block.Attestations()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, attestation := range attestations {
|
||||
if attestation.Data.Slot < c.chainTime.FirstSlotOfEpoch(c.summary.Epoch) || attestation.Data.Slot >= c.chainTime.FirstSlotOfEpoch(c.summary.Epoch+1) {
|
||||
// Outside of this epoch's range.
|
||||
continue
|
||||
}
|
||||
slotCommittees, exists := allCommittees[attestation.Data.Slot]
|
||||
if !exists {
|
||||
beaconCommittees, err := c.beaconCommitteesProvider.BeaconCommittees(ctx, fmt.Sprintf("%d", attestation.Data.Slot))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, fmt.Sprintf("failed to obtain committees for slot %d", attestation.Data.Slot))
|
||||
}
|
||||
for _, beaconCommittee := range beaconCommittees {
|
||||
if _, exists := allCommittees[beaconCommittee.Slot]; !exists {
|
||||
allCommittees[beaconCommittee.Slot] = make(map[phase0.CommitteeIndex][]phase0.ValidatorIndex)
|
||||
}
|
||||
allCommittees[beaconCommittee.Slot][beaconCommittee.Index] = beaconCommittee.Validators
|
||||
for _, index := range beaconCommittee.Validators {
|
||||
participations[index] = &nonParticipatingValidator{
|
||||
Validator: index,
|
||||
Slot: beaconCommittee.Slot,
|
||||
Committee: beaconCommittee.Index,
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
slotCommittees = allCommittees[attestation.Data.Slot]
|
||||
}
|
||||
committee := slotCommittees[attestation.Data.Index]
|
||||
for i := uint64(0); i < attestation.AggregationBits.Len(); i++ {
|
||||
if attestation.AggregationBits.BitAt(i) {
|
||||
votes[committee[int(i)]] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
var votes map[phase0.ValidatorIndex]struct{}
|
||||
var participations map[phase0.ValidatorIndex]*nonParticipatingValidator
|
||||
c.summary.ParticipatingValidators, c.summary.HeadCorrectValidators, c.summary.HeadTimelyValidators, c.summary.SourceTimelyValidators, c.summary.TargetCorrectValidators, c.summary.TargetTimelyValidators, votes, participations, err = c.processSlots(ctx, firstSlot, lastSlot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.summary.ActiveValidators = len(activeValidators)
|
||||
c.summary.ParticipatingValidators = len(votes)
|
||||
c.summary.NonParticipatingValidators = make([]*nonParticipatingValidator, 0, len(activeValidators)-len(votes))
|
||||
for activeValidatorIndex := range activeValidators {
|
||||
if _, exists := votes[activeValidatorIndex]; !exists {
|
||||
@@ -177,6 +137,121 @@ func (c *command) processAttesterDuties(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) processSlots(ctx context.Context,
|
||||
firstSlot phase0.Slot,
|
||||
lastSlot phase0.Slot,
|
||||
) (
|
||||
int,
|
||||
int,
|
||||
int,
|
||||
int,
|
||||
int,
|
||||
int,
|
||||
map[phase0.ValidatorIndex]struct{},
|
||||
map[phase0.ValidatorIndex]*nonParticipatingValidator,
|
||||
error,
|
||||
) {
|
||||
votes := make(map[phase0.ValidatorIndex]struct{})
|
||||
headCorrects := make(map[phase0.ValidatorIndex]struct{})
|
||||
headTimelys := make(map[phase0.ValidatorIndex]struct{})
|
||||
sourceTimelys := make(map[phase0.ValidatorIndex]struct{})
|
||||
targetCorrects := make(map[phase0.ValidatorIndex]struct{})
|
||||
targetTimelys := make(map[phase0.ValidatorIndex]struct{})
|
||||
allCommittees := make(map[phase0.Slot]map[phase0.CommitteeIndex][]phase0.ValidatorIndex)
|
||||
participations := make(map[phase0.ValidatorIndex]*nonParticipatingValidator)
|
||||
|
||||
// Need a cache of beacon block headers to reduce lookup times.
|
||||
headersCache := util.NewBeaconBlockHeaderCache(c.beaconBlockHeadersProvider)
|
||||
|
||||
for slot := firstSlot; slot <= lastSlot; slot++ {
|
||||
block, err := c.blocksProvider.SignedBeaconBlock(ctx, fmt.Sprintf("%d", slot))
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, 0, 0, nil, nil, errors.Wrap(err, fmt.Sprintf("failed to obtain block for slot %d", slot))
|
||||
}
|
||||
if block == nil {
|
||||
// No block at this slot; that's fine.
|
||||
continue
|
||||
}
|
||||
slot, err := block.Slot()
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, 0, 0, nil, nil, err
|
||||
}
|
||||
attestations, err := block.Attestations()
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, 0, 0, nil, nil, err
|
||||
}
|
||||
for _, attestation := range attestations {
|
||||
if attestation.Data.Slot < c.chainTime.FirstSlotOfEpoch(c.summary.Epoch) || attestation.Data.Slot >= c.chainTime.FirstSlotOfEpoch(c.summary.Epoch+1) {
|
||||
// Outside of this epoch's range.
|
||||
continue
|
||||
}
|
||||
slotCommittees, exists := allCommittees[attestation.Data.Slot]
|
||||
if !exists {
|
||||
beaconCommittees, err := c.beaconCommitteesProvider.BeaconCommittees(ctx, fmt.Sprintf("%d", attestation.Data.Slot))
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, 0, 0, nil, nil, errors.Wrap(err, fmt.Sprintf("failed to obtain committees for slot %d", attestation.Data.Slot))
|
||||
}
|
||||
for _, beaconCommittee := range beaconCommittees {
|
||||
if _, exists := allCommittees[beaconCommittee.Slot]; !exists {
|
||||
allCommittees[beaconCommittee.Slot] = make(map[phase0.CommitteeIndex][]phase0.ValidatorIndex)
|
||||
}
|
||||
allCommittees[beaconCommittee.Slot][beaconCommittee.Index] = beaconCommittee.Validators
|
||||
for _, index := range beaconCommittee.Validators {
|
||||
participations[index] = &nonParticipatingValidator{
|
||||
Validator: index,
|
||||
Slot: beaconCommittee.Slot,
|
||||
Committee: beaconCommittee.Index,
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
slotCommittees = allCommittees[attestation.Data.Slot]
|
||||
}
|
||||
committee := slotCommittees[attestation.Data.Index]
|
||||
|
||||
inclusionDistance := slot - attestation.Data.Slot
|
||||
headCorrect, err := util.AttestationHeadCorrect(ctx, headersCache, attestation)
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, 0, 0, nil, nil, err
|
||||
}
|
||||
targetCorrect, err := util.AttestationTargetCorrect(ctx, headersCache, c.chainTime, attestation)
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, 0, 0, nil, nil, err
|
||||
}
|
||||
|
||||
for i := uint64(0); i < attestation.AggregationBits.Len(); i++ {
|
||||
if attestation.AggregationBits.BitAt(i) {
|
||||
votes[committee[int(i)]] = struct{}{}
|
||||
if _, exists := headCorrects[committee[int(i)]]; !exists && headCorrect {
|
||||
headCorrects[committee[int(i)]] = struct{}{}
|
||||
}
|
||||
if _, exists := headTimelys[committee[int(i)]]; !exists && headCorrect && inclusionDistance == 1 {
|
||||
headTimelys[committee[int(i)]] = struct{}{}
|
||||
}
|
||||
if _, exists := sourceTimelys[committee[int(i)]]; !exists && inclusionDistance <= 5 {
|
||||
sourceTimelys[committee[int(i)]] = struct{}{}
|
||||
}
|
||||
if _, exists := targetCorrects[committee[int(i)]]; !exists && targetCorrect {
|
||||
targetCorrects[committee[int(i)]] = struct{}{}
|
||||
}
|
||||
if _, exists := targetTimelys[committee[int(i)]]; !exists && targetCorrect && inclusionDistance <= 32 {
|
||||
targetTimelys[committee[int(i)]] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return len(votes),
|
||||
len(headCorrects),
|
||||
len(headTimelys),
|
||||
len(sourceTimelys),
|
||||
len(targetCorrects),
|
||||
len(targetTimelys),
|
||||
votes,
|
||||
participations,
|
||||
nil
|
||||
}
|
||||
|
||||
func (c *command) processSyncCommitteeDuties(ctx context.Context) error {
|
||||
if c.summary.Epoch < c.chainTime.AltairInitialEpoch() {
|
||||
// The epoch is pre-Altair. No info but no error.
|
||||
@@ -286,6 +361,10 @@ func (c *command) setup(ctx context.Context) error {
|
||||
if !isProvider {
|
||||
return errors.New("connection does not provide beacon committees")
|
||||
}
|
||||
c.beaconBlockHeadersProvider, isProvider = c.eth2Client.(eth2client.BeaconBlockHeadersProvider)
|
||||
if !isProvider {
|
||||
return errors.New("connection does not provide beacon block headers")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -67,9 +67,6 @@ func newCommand(ctx context.Context) (*command, error) {
|
||||
}
|
||||
c.timeout = viper.GetDuration("timeout")
|
||||
|
||||
if viper.GetString("connection") == "" {
|
||||
return nil, errors.New("connection is required")
|
||||
}
|
||||
c.connection = viper.GetString("connection")
|
||||
c.allowInsecureConnections = viper.GetBool("allow-insecure-connections")
|
||||
|
||||
|
||||
@@ -37,13 +37,6 @@ func TestInput(t *testing.T) {
|
||||
vars: map[string]interface{}{},
|
||||
err: "timeout is required",
|
||||
},
|
||||
{
|
||||
name: "ConnectionMissing",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "5s",
|
||||
},
|
||||
err: "connection is required",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
vars: map[string]interface{}{
|
||||
|
||||
42
cmd/root.go
42
cmd/root.go
@@ -96,6 +96,8 @@ func includeCommandBindings(cmd *cobra.Command) {
|
||||
blockInfoBindings()
|
||||
case "chain/eth1votes":
|
||||
chainEth1VotesBindings()
|
||||
case "chain/info":
|
||||
chainInfoBindings()
|
||||
case "chain/queues":
|
||||
chainQueuesBindings()
|
||||
case "chain/time":
|
||||
@@ -118,6 +120,8 @@ func includeCommandBindings(cmd *cobra.Command) {
|
||||
synccommitteeMembersBindings()
|
||||
case "validator/credentials/get":
|
||||
validatorCredentialsGetBindings()
|
||||
case "validator/credentials/set":
|
||||
validatorCredentialsSetBindings()
|
||||
case "validator/depositdata":
|
||||
validatorDepositdataBindings()
|
||||
case "validator/duties":
|
||||
@@ -128,6 +132,8 @@ func includeCommandBindings(cmd *cobra.Command) {
|
||||
validatorInfoBindings()
|
||||
case "validator/keycheck":
|
||||
validatorKeycheckBindings()
|
||||
case "validator/summary":
|
||||
validatorSummaryBindings()
|
||||
case "validator/yield":
|
||||
validatorYieldBindings()
|
||||
case "validator/expectation":
|
||||
@@ -169,10 +175,26 @@ func init() {
|
||||
if err := viper.BindPFlag("store", RootCmd.PersistentFlags().Lookup("store")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
RootCmd.PersistentFlags().String("account", "", "Account name (in format \"wallet/account\")")
|
||||
RootCmd.PersistentFlags().String("account", "", `Account name (in format "<wallet>/<account>")`)
|
||||
if err := viper.BindPFlag("account", RootCmd.PersistentFlags().Lookup("account")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
RootCmd.PersistentFlags().String("mnemonic", "", "Mnemonic to provide access to an account")
|
||||
if err := viper.BindPFlag("mnemonic", RootCmd.PersistentFlags().Lookup("mnemonic")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
RootCmd.PersistentFlags().String("path", "", "Hierarchical derivation path used with mnemonic to provide access to an account")
|
||||
if err := viper.BindPFlag("path", RootCmd.PersistentFlags().Lookup("path")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
RootCmd.PersistentFlags().String("private-key", "", "Private key to provide access to an account")
|
||||
if err := viper.BindPFlag("private-key", RootCmd.PersistentFlags().Lookup("private-key")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
RootCmd.PersistentFlags().String("public-key", "", "public key to provide access to an account")
|
||||
if err := viper.BindPFlag("public-key", RootCmd.PersistentFlags().Lookup("public-key")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
RootCmd.PersistentFlags().String("basedir", "", "Base directory for filesystem wallets")
|
||||
if err := viper.BindPFlag("basedir", RootCmd.PersistentFlags().Lookup("basedir")); err != nil {
|
||||
panic(err)
|
||||
@@ -375,24 +397,6 @@ func walletAndAccountFromPath(ctx context.Context, path string) (e2wtypes.Wallet
|
||||
return wallet, account, nil
|
||||
}
|
||||
|
||||
// bestPublicKey returns the best public key for operations.
|
||||
// It prefers the composite public key if present, otherwise the public key.
|
||||
func bestPublicKey(account e2wtypes.Account) (e2types.PublicKey, error) {
|
||||
var pubKey e2types.PublicKey
|
||||
publicKeyProvider, isCompositePublicKeyProvider := account.(e2wtypes.AccountCompositePublicKeyProvider)
|
||||
if isCompositePublicKeyProvider {
|
||||
pubKey = publicKeyProvider.CompositePublicKey()
|
||||
} else {
|
||||
publicKeyProvider, isPublicKeyProvider := account.(e2wtypes.AccountPublicKeyProvider)
|
||||
if isPublicKeyProvider {
|
||||
pubKey = publicKeyProvider.PublicKey()
|
||||
} else {
|
||||
return nil, errors.New("account does not provide a public key")
|
||||
}
|
||||
}
|
||||
return pubKey, nil
|
||||
}
|
||||
|
||||
// remotesToEndpoints generates endpoints from remote addresses.
|
||||
func remotesToEndpoints(remotes []string) ([]*dirk.Endpoint, error) {
|
||||
endpoints := make([]*dirk.Endpoint, 0)
|
||||
|
||||
@@ -37,7 +37,7 @@ func TestOutput(t *testing.T) {
|
||||
dataOut: &dataOut{
|
||||
startTime: time.Unix(1606824023, 0),
|
||||
},
|
||||
res: "2020-12-01 12:00:23 +0000 GMT",
|
||||
res: "2020-12-01 12:00:23 +0000 UTC",
|
||||
},
|
||||
{
|
||||
name: "Verbose",
|
||||
@@ -46,7 +46,7 @@ func TestOutput(t *testing.T) {
|
||||
endTime: time.Unix(1606824035, 0),
|
||||
verbose: true,
|
||||
},
|
||||
res: "2020-12-01 12:00:23 +0000 GMT - 2020-12-01 12:00:35 +0000 GMT",
|
||||
res: "2020-12-01 12:00:23 +0000 UTC - 2020-12-01 12:00:35 +0000 UTC",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -30,3 +30,6 @@ func init() {
|
||||
|
||||
func validatorFlags(cmd *cobra.Command) {
|
||||
}
|
||||
|
||||
func validatorBindings() {
|
||||
}
|
||||
|
||||
@@ -29,9 +29,7 @@ type command struct {
|
||||
debug bool
|
||||
|
||||
// Input.
|
||||
account string
|
||||
index string
|
||||
pubKey string
|
||||
validator string
|
||||
|
||||
// Beacon node connection.
|
||||
timeout time.Duration
|
||||
@@ -43,7 +41,7 @@ type command struct {
|
||||
validatorsProvider eth2client.ValidatorsProvider
|
||||
|
||||
// Output.
|
||||
validator *apiv1.Validator
|
||||
validatorInfo *apiv1.Validator
|
||||
}
|
||||
|
||||
func newCommand(ctx context.Context) (*command, error) {
|
||||
@@ -59,31 +57,13 @@ func newCommand(ctx context.Context) (*command, error) {
|
||||
}
|
||||
c.timeout = viper.GetDuration("timeout")
|
||||
|
||||
if viper.GetString("connection") == "" {
|
||||
return nil, errors.New("connection is required")
|
||||
}
|
||||
c.connection = viper.GetString("connection")
|
||||
c.allowInsecureConnections = viper.GetBool("allow-insecure-connections")
|
||||
|
||||
c.account = viper.GetString("account")
|
||||
c.index = viper.GetString("index")
|
||||
c.pubKey = viper.GetString("pubkey")
|
||||
nonNil := 0
|
||||
if c.account != "" {
|
||||
nonNil++
|
||||
}
|
||||
if c.index != "" {
|
||||
nonNil++
|
||||
}
|
||||
if c.pubKey != "" {
|
||||
nonNil++
|
||||
}
|
||||
if nonNil == 0 {
|
||||
return nil, errors.New("one of account, index or pubkey required")
|
||||
}
|
||||
if nonNil > 1 {
|
||||
return nil, errors.New("only one of account, index and pubkey allowed")
|
||||
if viper.GetString("validator") == "" {
|
||||
return nil, errors.New("validator is required")
|
||||
}
|
||||
c.validator = viper.GetString("validator")
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
@@ -37,14 +37,6 @@ func TestInput(t *testing.T) {
|
||||
vars: map[string]interface{}{},
|
||||
err: "timeout is required",
|
||||
},
|
||||
{
|
||||
name: "ConnectionMissing",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "5s",
|
||||
"index": "1",
|
||||
},
|
||||
err: "connection is required",
|
||||
},
|
||||
{
|
||||
name: "NoValidatorInfo",
|
||||
vars: map[string]interface{}{
|
||||
|
||||
@@ -17,6 +17,8 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
ethutil "github.com/wealdtech/go-eth2-util"
|
||||
)
|
||||
|
||||
func (c *command) output(ctx context.Context) (string, error) {
|
||||
@@ -26,19 +28,38 @@ func (c *command) output(ctx context.Context) (string, error) {
|
||||
|
||||
builder := strings.Builder{}
|
||||
|
||||
switch c.validator.Validator.WithdrawalCredentials[0] {
|
||||
switch c.validatorInfo.Validator.WithdrawalCredentials[0] {
|
||||
case 0:
|
||||
builder.WriteString("BLS credentials: ")
|
||||
builder.WriteString(fmt.Sprintf("%#x", c.validator.Validator.WithdrawalCredentials))
|
||||
builder.WriteString(fmt.Sprintf("%#x", c.validatorInfo.Validator.WithdrawalCredentials))
|
||||
case 1:
|
||||
builder.WriteString("Ethereum execution address: ")
|
||||
builder.WriteString(fmt.Sprintf("%#x", c.validator.Validator.WithdrawalCredentials[12:]))
|
||||
builder.WriteString(addressBytesToEIP55(c.validatorInfo.Validator.WithdrawalCredentials[12:]))
|
||||
if c.verbose {
|
||||
builder.WriteString("\n")
|
||||
builder.WriteString("Withdrawal credentials: ")
|
||||
builder.WriteString(fmt.Sprintf("%#x", c.validator.Validator.WithdrawalCredentials))
|
||||
builder.WriteString(fmt.Sprintf("%#x", c.validatorInfo.Validator.WithdrawalCredentials))
|
||||
}
|
||||
}
|
||||
|
||||
return builder.String(), nil
|
||||
}
|
||||
|
||||
// addressBytesToEIP55 converts a byte array in to an EIP-55 string format.
|
||||
func addressBytesToEIP55(address []byte) string {
|
||||
bytes := []byte(fmt.Sprintf("%x", address))
|
||||
hash := ethutil.Keccak256(bytes)
|
||||
for i := 0; i < len(bytes); i++ {
|
||||
hashByte := hash[i/2]
|
||||
if i%2 == 0 {
|
||||
hashByte >>= 4
|
||||
} else {
|
||||
hashByte &= 0xf
|
||||
}
|
||||
if bytes[i] > '9' && hashByte > 7 {
|
||||
bytes[i] -= 32
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("0x%s", string(bytes))
|
||||
}
|
||||
|
||||
@@ -15,14 +15,10 @@ package validatorcredentialsget
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
)
|
||||
@@ -68,71 +64,10 @@ func (c *command) setup(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (c *command) fetchValidator(ctx context.Context) error {
|
||||
if c.account != "" {
|
||||
_, account, err := util.WalletAndAccountFromInput(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to obtain account")
|
||||
}
|
||||
|
||||
accPubKey, err := util.BestPublicKey(account)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to obtain public key for account")
|
||||
}
|
||||
pubKey := phase0.BLSPubKey{}
|
||||
copy(pubKey[:], accPubKey.Marshal())
|
||||
validators, err := c.validatorsProvider.ValidatorsByPubKey(ctx,
|
||||
"head",
|
||||
[]phase0.BLSPubKey{pubKey},
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to obtain validator information")
|
||||
}
|
||||
if len(validators) == 0 {
|
||||
return errors.New("unknown validator")
|
||||
}
|
||||
for _, validator := range validators {
|
||||
c.validator = validator
|
||||
}
|
||||
}
|
||||
if c.index != "" {
|
||||
tmp, err := strconv.ParseUint(c.index, 10, 64)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "invalid validator index")
|
||||
}
|
||||
index := phase0.ValidatorIndex(tmp)
|
||||
validators, err := c.validatorsProvider.Validators(ctx,
|
||||
"head",
|
||||
[]phase0.ValidatorIndex{index},
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to obtain validator information")
|
||||
}
|
||||
if _, exists := validators[index]; !exists {
|
||||
return errors.New("unknown validator")
|
||||
}
|
||||
c.validator = validators[index]
|
||||
}
|
||||
if c.pubKey != "" {
|
||||
bytes, err := hex.DecodeString(strings.TrimPrefix(c.pubKey, "0x"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "invalid validator public key")
|
||||
}
|
||||
pubKey := phase0.BLSPubKey{}
|
||||
copy(pubKey[:], bytes)
|
||||
|
||||
validators, err := c.validatorsProvider.ValidatorsByPubKey(ctx,
|
||||
"head",
|
||||
[]phase0.BLSPubKey{pubKey},
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to obtain validator information")
|
||||
}
|
||||
if len(validators) == 0 {
|
||||
return errors.New("unknown validator")
|
||||
}
|
||||
for _, validator := range validators {
|
||||
c.validator = validator
|
||||
}
|
||||
var err error
|
||||
c.validatorInfo, err = util.ParseValidator(ctx, c.validatorsProvider, c.validator, "head")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to obtain validator information")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
127
cmd/validator/credentials/set/chaininfo.go
Normal file
127
cmd/validator/credentials/set/chaininfo.go
Normal file
@@ -0,0 +1,127 @@
|
||||
// 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 (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
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")
|
||||
}
|
||||
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")
|
||||
}
|
||||
|
||||
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(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
|
||||
}
|
||||
104
cmd/validator/credentials/set/command.go
Normal file
104
cmd/validator/credentials/set/command.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// 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"
|
||||
"time"
|
||||
|
||||
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/pkg/errors"
|
||||
"github.com/spf13/viper"
|
||||
"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.
|
||||
account string
|
||||
passphrases []string
|
||||
mnemonic string
|
||||
path string
|
||||
privateKey string
|
||||
validator string
|
||||
withdrawalAddressStr string
|
||||
forkVersion string
|
||||
genesisValidatorsRoot string
|
||||
prepareOffline bool
|
||||
|
||||
// Beacon node connection.
|
||||
timeout time.Duration
|
||||
connection string
|
||||
allowInsecureConnections bool
|
||||
|
||||
// Information required to generate the operations.
|
||||
withdrawalAddress bellatrix.ExecutionAddress
|
||||
chainInfo *chainInfo
|
||||
|
||||
// Processing.
|
||||
consensusClient consensusclient.Service
|
||||
chainTime chaintime.Service
|
||||
|
||||
// Output.
|
||||
signedOperations []*capella.SignedBLSToExecutionChange
|
||||
}
|
||||
|
||||
func newCommand(ctx 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"),
|
||||
account: viper.GetString("account"),
|
||||
passphrases: util.GetPassphrases(),
|
||||
mnemonic: viper.GetString("mnemonic"),
|
||||
path: viper.GetString("path"),
|
||||
privateKey: viper.GetString("private-key"),
|
||||
|
||||
validator: viper.GetString("validator"),
|
||||
withdrawalAddressStr: viper.GetString("withdrawal-address"),
|
||||
forkVersion: viper.GetString("fork-version"),
|
||||
genesisValidatorsRoot: viper.GetString("genesis-validators-root"),
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
if c.account != "" && len(c.passphrases) == 0 {
|
||||
return nil, errors.New("passphrase required with account")
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
83
cmd/validator/credentials/set/command_internal_test.go
Normal file
83
cmd/validator/credentials/set/command_internal_test.go
Normal file
@@ -0,0 +1,83 @@
|
||||
// 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"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestInput(t *testing.T) {
|
||||
if os.Getenv("ETHDO_TEST_CONNECTION") == "" {
|
||||
t.Skip("ETHDO_TEST_CONNECTION not configured; cannot run tests")
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
vars map[string]interface{}
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "TimeoutMissing",
|
||||
vars: map[string]interface{}{},
|
||||
err: "timeout is required",
|
||||
},
|
||||
{
|
||||
name: "NoValidatorInfo",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "5s",
|
||||
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
|
||||
},
|
||||
err: "one of account, index or pubkey required",
|
||||
},
|
||||
{
|
||||
name: "MultipleValidatorInfo",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "5s",
|
||||
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
|
||||
"index": "1",
|
||||
"pubkey": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
},
|
||||
err: "only one of account, index and pubkey allowed",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "5s",
|
||||
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
|
||||
"index": "1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
viper.Reset()
|
||||
|
||||
for k, v := range test.vars {
|
||||
viper.Set(k, v)
|
||||
}
|
||||
_, err := newCommand(context.Background())
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
41
cmd/validator/credentials/set/output.go
Normal file
41
cmd/validator/credentials/set/output.go
Normal file
@@ -0,0 +1,41 @@
|
||||
// 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"
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (c *command) output(ctx context.Context) (string, error) {
|
||||
if c.quiet {
|
||||
return "", 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("credentials-operations.json", data, 0600); err != nil {
|
||||
return "", errors.Wrap(err, "failed to write credentials-operations.json")
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
626
cmd/validator/credentials/set/process.go
Normal file
626
cmd/validator/credentials/set/process.go
Normal file
@@ -0,0 +1,626 @@
|
||||
// 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 (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
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"
|
||||
standardchaintime "github.com/wealdtech/ethdo/services/chaintime/standard"
|
||||
"github.com/wealdtech/ethdo/signing"
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
ethutil "github.com/wealdtech/go-eth2-util"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
// 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"
|
||||
var changeOperationsFilename = "change-operations.json"
|
||||
|
||||
func (c *command) process(ctx context.Context) error {
|
||||
if err := c.setup(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.obtainRequiredInformation(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.prepareOffline {
|
||||
return c.dumpRequiredInformation(ctx)
|
||||
}
|
||||
|
||||
if err := c.generateOperations(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if validated, reason := c.validateOperations(ctx); !validated {
|
||||
return fmt.Errorf("operation failed validation: %s", reason)
|
||||
}
|
||||
|
||||
if c.json || c.offline {
|
||||
// Want JSON output, or cannot broadcast.
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.broadcastOperations(ctx)
|
||||
}
|
||||
|
||||
// obtainRequiredInformation obtains the information required to create a
|
||||
// withdrawal credentials change operation.
|
||||
func (c *command) obtainRequiredInformation(ctx context.Context) error {
|
||||
c.chainInfo = &chainInfo{
|
||||
Validators: make([]*validatorInfo, 0),
|
||||
}
|
||||
|
||||
// Use the offline preparation file if present (and we haven't been asked to recreate it).
|
||||
if !c.prepareOffline {
|
||||
err := c.loadChainInfo(ctx)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if c.offline {
|
||||
return fmt.Errorf("could not find the %s file; this is required to have been previously generated using --offline-preparation on an online mcahine and be readable in the directory in which this command is being run", offlinePreparationFilename)
|
||||
}
|
||||
|
||||
if err := c.populateChainInfo(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// populateChainInfo populates chain info structure from a beacon node.
|
||||
func (c *command) populateChainInfo(ctx context.Context) error {
|
||||
// Obtain validators.
|
||||
validators, err := c.consensusClient.(consensusclient.ValidatorsProvider).Validators(ctx, "head", nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to obtain validators")
|
||||
}
|
||||
|
||||
for _, validator := range validators {
|
||||
c.chainInfo.Validators = append(c.chainInfo.Validators, &validatorInfo{
|
||||
Index: validator.Index,
|
||||
Pubkey: validator.Validator.PublicKey,
|
||||
WithdrawalCredentials: validator.Validator.WithdrawalCredentials,
|
||||
})
|
||||
}
|
||||
|
||||
// Obtain genesis validators root.
|
||||
genesis, err := c.consensusClient.(consensusclient.GenesisProvider).Genesis(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to obtain genesis information")
|
||||
}
|
||||
c.chainInfo.GenesisValidatorsRoot = genesis.GenesisValidatorsRoot
|
||||
|
||||
// Obtain epoch.
|
||||
c.chainInfo.Epoch = c.chainTime.CurrentEpoch()
|
||||
|
||||
// Obtain fork version.
|
||||
forkSchedule, err := c.consensusClient.(consensusclient.ForkScheduleProvider).ForkSchedule(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to obtain fork schedule")
|
||||
}
|
||||
for i := range forkSchedule {
|
||||
if forkSchedule[i].Epoch <= c.chainInfo.Epoch {
|
||||
c.chainInfo.ForkVersion = forkSchedule[i].CurrentVersion
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate domain.
|
||||
spec, err := c.consensusClient.(consensusclient.SpecProvider).Spec(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to obtain spec")
|
||||
}
|
||||
domainType, exists := spec["DOMAIN_BLS_TO_EXECUTION_CHANGE"].(phase0.DomainType)
|
||||
if !exists {
|
||||
return errors.New("failed to obtain DOMAIN_BLS_TO_EXECUTION_CHANGE")
|
||||
}
|
||||
domainProvider, isProvider := c.consensusClient.(consensusclient.DomainProvider)
|
||||
if !isProvider {
|
||||
return errors.New("consensus node does not provide domain information")
|
||||
}
|
||||
c.chainInfo.Domain, err = domainProvider.Domain(ctx, domainType, c.chainInfo.Epoch)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to obtain domain")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// dumpRequiredInformation prepares for an offline run of this command by dumping
|
||||
// the chain information to a file.
|
||||
func (c *command) dumpRequiredInformation(ctx context.Context) error {
|
||||
data, err := json.Marshal(c.chainInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.WriteFile(offlinePreparationFilename, data, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) generateOperations(ctx context.Context) error {
|
||||
// Ensure that we are beyond the capella hard fork epoch.
|
||||
if c.chainTime.CurrentEpoch() < c.chainTime.CapellaInitialEpoch() {
|
||||
return errors.New("chain not yet activated capella hard fork")
|
||||
}
|
||||
|
||||
if c.account == "" && c.mnemonic == "" && c.privateKey == "" {
|
||||
// No input information; fetch the operations from a file.
|
||||
if err := c.loadOperations(ctx); err == nil {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("no account, mnemonic or private key specified and no %s file found; cannot proceed", changeOperationsFilename)
|
||||
}
|
||||
|
||||
if c.mnemonic != "" && c.path == "" {
|
||||
// Have a mnemonic and no path; scan mnemonic.
|
||||
return c.generateOperationsFromMnemonic(ctx)
|
||||
}
|
||||
|
||||
if c.mnemonic != "" && c.path != "" {
|
||||
// Have a mnemonic and path.
|
||||
return c.generateOperationsFromMnemonicAndPath(ctx)
|
||||
}
|
||||
|
||||
// Have a validator index or public key ; fetch the validator info.
|
||||
validatorInfo, err := c.fetchValidatorInfo(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Fetch the individual account.
|
||||
withdrawalAccount, err := c.fetchAccount(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate the operation.
|
||||
if err := c.generateOperationFromAccount(ctx, validatorInfo, withdrawalAccount); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) loadChainInfo(ctx context.Context) error {
|
||||
_, err := os.Stat(offlinePreparationFilename)
|
||||
if err != nil {
|
||||
if c.debug {
|
||||
fmt.Printf("Failed to read offline preparation file: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
if c.debug {
|
||||
fmt.Printf("%s found; loading chain state\n", offlinePreparationFilename)
|
||||
}
|
||||
data, err := os.ReadFile(offlinePreparationFilename)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to read offline preparation file")
|
||||
}
|
||||
if err := json.Unmarshal(data, c.chainInfo); err != nil {
|
||||
return errors.Wrap(err, "failed to parse offline preparation file")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) loadOperations(ctx context.Context) error {
|
||||
_, err := os.Stat(changeOperationsFilename)
|
||||
if err != nil {
|
||||
if c.debug {
|
||||
fmt.Printf("Failed to read change operations file: %v\n", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if c.debug {
|
||||
fmt.Printf("%s found; loading operations\n", changeOperationsFilename)
|
||||
}
|
||||
data, err := os.ReadFile(changeOperationsFilename)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to read change operations file")
|
||||
}
|
||||
if err := json.Unmarshal(data, &c.signedOperations); err != nil {
|
||||
return errors.Wrap(err, "failed to parse change operations file")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) generateOperationsFromMnemonic(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]*validatorInfo, 0)
|
||||
for _, validator := range c.chainInfo.Validators {
|
||||
validators[fmt.Sprintf("%#x", validator.Pubkey)] = validator
|
||||
}
|
||||
|
||||
maxDistance := 1024
|
||||
// Start scanning the validator keys.
|
||||
lastFoundIndex := 0
|
||||
for i := 0; ; i++ {
|
||||
if i-lastFoundIndex > maxDistance {
|
||||
if c.debug {
|
||||
fmt.Printf("Gone %d indices without finding a validator, not scanning any further\n", maxDistance)
|
||||
}
|
||||
break
|
||||
}
|
||||
validatorKeyPath := fmt.Sprintf("m/12381/3600/%d/0/0", i)
|
||||
|
||||
found, err := c.generateOperationFromSeedAndPath(ctx, validators, seed, validatorKeyPath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to generate operation from seed and path")
|
||||
}
|
||||
if found {
|
||||
lastFoundIndex = i
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) generateOperationFromSeedAndPath(ctx context.Context,
|
||||
validators map[string]*validatorInfo,
|
||||
seed []byte,
|
||||
path string,
|
||||
) (
|
||||
bool,
|
||||
error,
|
||||
) {
|
||||
validatorPrivkey, err := ethutil.PrivateKeyFromSeedAndPath(seed, path)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "failed to generate validator private key")
|
||||
}
|
||||
validatorPubkey := fmt.Sprintf("%#x", validatorPrivkey.PublicKey().Marshal())
|
||||
validator, exists := validators[validatorPubkey]
|
||||
if !exists {
|
||||
if c.debug {
|
||||
fmt.Printf("No validator found with public key %s at path %s\n", validatorPubkey, path)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if c.verbose {
|
||||
fmt.Printf("Validator %d found with public key %s at path %s\n", validator.Index, validatorPubkey, path)
|
||||
}
|
||||
|
||||
if validator.WithdrawalCredentials[0] != byte(0) {
|
||||
if c.debug {
|
||||
fmt.Printf("Validator %s has non-BLS withdrawal credentials %#x\n", validatorPubkey, validator.WithdrawalCredentials)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Recreate the withdrawal credentials to ensure a match.
|
||||
withdrawalKeyPath := strings.TrimSuffix(path, "/0")
|
||||
withdrawalPrivkey, err := ethutil.PrivateKeyFromSeedAndPath(seed, withdrawalKeyPath)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "failed to generate withdrawal private key")
|
||||
}
|
||||
withdrawalPubkey := withdrawalPrivkey.PublicKey()
|
||||
withdrawalCredentials := ethutil.SHA256(withdrawalPubkey.Marshal())
|
||||
withdrawalCredentials[0] = byte(0) // BLS_WITHDRAWAL_PREFIX
|
||||
if !bytes.Equal(withdrawalCredentials, validator.WithdrawalCredentials) {
|
||||
if c.verbose {
|
||||
fmt.Printf("Validator %s withdrawal credentials %#x do not match expected credentials, cannot update\n", validatorPubkey, validator.WithdrawalCredentials)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if c.debug {
|
||||
fmt.Printf("Validator %s eligible for setting credentials\n", validatorPubkey)
|
||||
}
|
||||
|
||||
withdrawalAccount, err := util.ParseAccount(ctx, c.mnemonic, []string{withdrawalKeyPath}, true)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "failed to create withdrawal account")
|
||||
}
|
||||
|
||||
err = c.generateOperationFromAccount(ctx, validator, withdrawalAccount)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (c *command) generateOperationFromAccount(ctx context.Context,
|
||||
validator *validatorInfo,
|
||||
withdrawalAccount e2wtypes.Account,
|
||||
) error {
|
||||
signedOperation, err := c.createSignedOperation(ctx, validator, withdrawalAccount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.signedOperations = append(c.signedOperations, signedOperation)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) createSignedOperation(ctx context.Context,
|
||||
validator *validatorInfo,
|
||||
withdrawalAccount e2wtypes.Account,
|
||||
) (
|
||||
*capella.SignedBLSToExecutionChange,
|
||||
error,
|
||||
) {
|
||||
pubkey, err := util.BestPublicKey(withdrawalAccount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blsPubkey := phase0.BLSPubKey{}
|
||||
copy(blsPubkey[:], pubkey.Marshal())
|
||||
|
||||
if err := c.parseWithdrawalAddress(ctx); err != nil {
|
||||
return nil, errors.Wrap(err, "invalid withdrawal address")
|
||||
}
|
||||
|
||||
operation := &capella.BLSToExecutionChange{
|
||||
ValidatorIndex: validator.Index,
|
||||
FromBLSPubkey: blsPubkey,
|
||||
ToExecutionAddress: c.withdrawalAddress,
|
||||
}
|
||||
root, err := operation.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to generate root for credentials change operation")
|
||||
}
|
||||
|
||||
// Sign the operation.
|
||||
signature, err := signing.SignRoot(ctx, withdrawalAccount, nil, root, c.chainInfo.Domain)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to sign credentials change operation")
|
||||
}
|
||||
|
||||
return &capella.SignedBLSToExecutionChange{
|
||||
Message: operation,
|
||||
Signature: signature,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *command) parseWithdrawalAddress(ctx context.Context) error {
|
||||
withdrawalAddressBytes, err := hex.DecodeString(strings.TrimPrefix(c.withdrawalAddressStr, "0x"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to obtain execution address")
|
||||
}
|
||||
if len(withdrawalAddressBytes) != bellatrix.ExecutionAddressLength {
|
||||
return errors.New("withdrawal address must be exactly 20 bytes in length")
|
||||
}
|
||||
// Ensure the address is properly checksummed.
|
||||
checksummedAddress := addressBytesToEIP55(withdrawalAddressBytes)
|
||||
if checksummedAddress != c.withdrawalAddressStr {
|
||||
return fmt.Errorf("withdrawal address checksum does not match (expected %s)", checksummedAddress)
|
||||
}
|
||||
copy(c.withdrawalAddress[:], withdrawalAddressBytes)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) validateOperations(ctx context.Context) (bool, string) {
|
||||
// Turn the validators in to a map for easy lookup.
|
||||
validators := make(map[phase0.ValidatorIndex]*validatorInfo, 0)
|
||||
for _, validator := range c.chainInfo.Validators {
|
||||
validators[validator.Index] = validator
|
||||
}
|
||||
|
||||
for _, signedOperation := range c.signedOperations {
|
||||
if validated, reason := c.validateOperation(ctx, validators, signedOperation); !validated {
|
||||
return validated, reason
|
||||
}
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func (c *command) validateOperation(ctx context.Context,
|
||||
validators map[phase0.ValidatorIndex]*validatorInfo,
|
||||
signedOperation *capella.SignedBLSToExecutionChange,
|
||||
) (
|
||||
bool,
|
||||
string,
|
||||
) {
|
||||
validator, exists := validators[signedOperation.Message.ValidatorIndex]
|
||||
if !exists {
|
||||
return false, "validator not known on chain"
|
||||
}
|
||||
if c.debug {
|
||||
fmt.Printf("Credentials change operation: %v", signedOperation)
|
||||
fmt.Printf("On-chain validator info: %v\n", validator)
|
||||
}
|
||||
|
||||
if validator.WithdrawalCredentials[0] != byte(0) {
|
||||
return false, "validator is not using BLS withdrawal credentials"
|
||||
}
|
||||
|
||||
withdrawalCredentials := ethutil.SHA256(signedOperation.Message.FromBLSPubkey[:])
|
||||
withdrawalCredentials[0] = byte(0) // BLS_WITHDRAWAL_PREFIX
|
||||
if !bytes.Equal(withdrawalCredentials, validator.WithdrawalCredentials) {
|
||||
if c.debug {
|
||||
fmt.Printf("validator withdrawal credentials %#x do not match calculated operation withdrawal credentials %#x\n", validator.WithdrawalCredentials, withdrawalCredentials)
|
||||
}
|
||||
return false, "validator withdrawal credentials do not match those in the operation"
|
||||
}
|
||||
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func (c *command) broadcastOperations(ctx context.Context) error {
|
||||
// Broadcast the operations.
|
||||
for _, signedOperation := range c.signedOperations {
|
||||
if err := c.consensusClient.(consensusclient.BLSToExecutionChangeSubmitter).SubmitBLSToExecutionChange(ctx, signedOperation); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) setup(ctx context.Context) error {
|
||||
if c.offline {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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.WithForkScheduleProvider(c.consensusClient.(consensusclient.ForkScheduleProvider)),
|
||||
standardchaintime.WithSpecProvider(c.consensusClient.(consensusclient.SpecProvider)),
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create chaintime service")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) fetchValidatorInfo(ctx context.Context) (*validatorInfo, error) {
|
||||
var validatorInfo *validatorInfo
|
||||
switch {
|
||||
case c.validator == "":
|
||||
return nil, errors.New("no validator specified")
|
||||
case strings.HasPrefix(c.validator, "0x"):
|
||||
// A public key
|
||||
for _, validator := range c.chainInfo.Validators {
|
||||
if strings.EqualFold(c.validator, fmt.Sprintf("%#x", validator.Pubkey)) {
|
||||
validatorInfo = validator
|
||||
break
|
||||
}
|
||||
}
|
||||
case strings.Contains(c.validator, "/"):
|
||||
// An account.
|
||||
_, account, err := util.WalletAndAccountFromPath(ctx, c.validator)
|
||||
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.chainInfo.Validators {
|
||||
if strings.EqualFold(pubkey, fmt.Sprintf("%#x", validator.Pubkey)) {
|
||||
validatorInfo = validator
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
// An index.
|
||||
index, err := strconv.ParseUint(c.validator, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse validator index")
|
||||
}
|
||||
validatorIndex := phase0.ValidatorIndex(index)
|
||||
for _, validator := range c.chainInfo.Validators {
|
||||
if validator.Index == validatorIndex {
|
||||
validatorInfo = validator
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if validatorInfo == nil {
|
||||
return nil, errors.New("unknown validator")
|
||||
}
|
||||
|
||||
return validatorInfo, nil
|
||||
}
|
||||
|
||||
func (c *command) fetchAccount(ctx context.Context) (e2wtypes.Account, error) {
|
||||
var account e2wtypes.Account
|
||||
var err error
|
||||
|
||||
switch {
|
||||
case c.account != "":
|
||||
account, err = util.ParseAccount(ctx, c.account, c.passphrases, true)
|
||||
case c.mnemonic != "":
|
||||
account, err = util.ParseAccount(ctx, c.mnemonic, []string{c.path}, true)
|
||||
case c.privateKey != "":
|
||||
account, err = util.ParseAccount(ctx, c.privateKey, nil, true)
|
||||
default:
|
||||
err = errors.New("account, mnemonic or private key must be supplied")
|
||||
}
|
||||
|
||||
return account, err
|
||||
}
|
||||
|
||||
// addressBytesToEIP55 converts a byte array in to an EIP-55 string format.
|
||||
func addressBytesToEIP55(address []byte) string {
|
||||
bytes := []byte(fmt.Sprintf("%x", address))
|
||||
hash := ethutil.Keccak256(bytes)
|
||||
for i := 0; i < len(bytes); i++ {
|
||||
hashByte := hash[i/2]
|
||||
if i%2 == 0 {
|
||||
hashByte >>= 4
|
||||
} else {
|
||||
hashByte &= 0xf
|
||||
}
|
||||
if bytes[i] > '9' && hashByte > 7 {
|
||||
bytes[i] -= 32
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("0x%s", string(bytes))
|
||||
}
|
||||
|
||||
func (c *command) generateOperationsFromMnemonicAndPath(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]*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", 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
|
||||
}
|
||||
50
cmd/validator/credentials/set/run.go
Normal file
50
cmd/validator/credentials/set/run.go
Normal file
@@ -0,0 +1,50 @@
|
||||
// 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"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// Run runs the command.
|
||||
func Run(cmd *cobra.Command) (string, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
c, err := newCommand(ctx)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to set up command")
|
||||
}
|
||||
|
||||
// Further errors do not need a usage report.
|
||||
cmd.SilenceUsage = true
|
||||
|
||||
if err := c.process(ctx); err != nil {
|
||||
return "", errors.Wrap(err, "failed to process")
|
||||
}
|
||||
|
||||
if viper.GetBool("quiet") {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
results, err := c.output(ctx)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to obtain output")
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
97
cmd/validator/credentials/set/validatorinfo.go
Normal file
97
cmd/validator/credentials/set/validatorinfo.go
Normal file
@@ -0,0 +1,97 @@
|
||||
// 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 (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type validatorInfo struct {
|
||||
Index phase0.ValidatorIndex
|
||||
Pubkey phase0.BLSPubKey
|
||||
WithdrawalCredentials []byte
|
||||
}
|
||||
|
||||
type validatorInfoJSON struct {
|
||||
Index string `json:"index"`
|
||||
Pubkey string `json:"pubkey"`
|
||||
WithdrawalCredentials string `json:"withdrawal_credentials"`
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (v *validatorInfo) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&validatorInfoJSON{
|
||||
Index: fmt.Sprintf("%d", v.Index),
|
||||
Pubkey: fmt.Sprintf("%#x", v.Pubkey),
|
||||
WithdrawalCredentials: fmt.Sprintf("%#x", v.WithdrawalCredentials),
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (v *validatorInfo) UnmarshalJSON(input []byte) error {
|
||||
var data validatorInfoJSON
|
||||
if err := json.Unmarshal(input, &data); err != nil {
|
||||
return errors.Wrap(err, "invalid JSON")
|
||||
}
|
||||
|
||||
if data.Index == "" {
|
||||
return errors.New("index missing")
|
||||
}
|
||||
index, err := strconv.ParseUint(data.Index, 10, 64)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "invalid value for index")
|
||||
}
|
||||
v.Index = phase0.ValidatorIndex(index)
|
||||
|
||||
if data.Pubkey == "" {
|
||||
return errors.New("public key missing")
|
||||
}
|
||||
pubkey, err := hex.DecodeString(strings.TrimPrefix(data.Pubkey, "0x"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "invalid value for public key")
|
||||
}
|
||||
if len(pubkey) != phase0.PublicKeyLength {
|
||||
return fmt.Errorf("incorrect length %d for public key", len(pubkey))
|
||||
}
|
||||
copy(v.Pubkey[:], pubkey)
|
||||
|
||||
if data.WithdrawalCredentials == "" {
|
||||
return errors.New("withdrawal credentials missing")
|
||||
}
|
||||
v.WithdrawalCredentials, err = hex.DecodeString(strings.TrimPrefix(data.WithdrawalCredentials, "0x"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "invalid value for withdrawal credentials")
|
||||
}
|
||||
if len(v.WithdrawalCredentials) != phase0.HashLength {
|
||||
return fmt.Errorf("incorrect length %d for withdrawal credentials", len(v.WithdrawalCredentials))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// String implements the Stringer interface.
|
||||
func (v *validatorInfo) String() string {
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Err: %v\n", err)
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
@@ -49,9 +49,6 @@ func input(ctx context.Context) (*dataIn, error) {
|
||||
|
||||
// Ethereum 2 connection.
|
||||
data.eth2Client = viper.GetString("connection")
|
||||
if data.eth2Client == "" {
|
||||
return nil, errors.New("connection is required")
|
||||
}
|
||||
data.allowInsecure = viper.GetBool("allow-insecure-connections")
|
||||
|
||||
// Account.
|
||||
|
||||
@@ -71,14 +71,6 @@ func TestInput(t *testing.T) {
|
||||
},
|
||||
err: "account, pubkey or index required",
|
||||
},
|
||||
{
|
||||
name: "ConnectionMissing",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "5s",
|
||||
"pubkey": "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c",
|
||||
},
|
||||
err: "connection is required",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -58,9 +58,6 @@ func newCommand(ctx context.Context) (*command, error) {
|
||||
}
|
||||
c.timeout = viper.GetDuration("timeout")
|
||||
|
||||
if viper.GetString("connection") == "" {
|
||||
return nil, errors.New("connection is required")
|
||||
}
|
||||
c.connection = viper.GetString("connection")
|
||||
c.allowInsecureConnections = viper.GetBool("allow-insecure-connections")
|
||||
|
||||
|
||||
@@ -37,14 +37,6 @@ func TestInput(t *testing.T) {
|
||||
vars: map[string]interface{}{},
|
||||
err: "timeout is required",
|
||||
},
|
||||
{
|
||||
name: "ConnectionMissing",
|
||||
vars: map[string]interface{}{
|
||||
"validators": "1",
|
||||
"timeout": "5s",
|
||||
},
|
||||
err: "connection is required",
|
||||
},
|
||||
{
|
||||
name: "ValidatorsZero",
|
||||
vars: map[string]interface{}{
|
||||
|
||||
140
cmd/validator/summary/command.go
Normal file
140
cmd/validator/summary/command.go
Normal file
@@ -0,0 +1,140 @@
|
||||
// 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 validatorsummary
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
eth2client "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/spf13/viper"
|
||||
"github.com/wealdtech/ethdo/services/chaintime"
|
||||
)
|
||||
|
||||
type command struct {
|
||||
quiet bool
|
||||
verbose bool
|
||||
debug bool
|
||||
|
||||
// Beacon node connection.
|
||||
timeout time.Duration
|
||||
connection string
|
||||
allowInsecureConnections bool
|
||||
|
||||
// Operation.
|
||||
epoch string
|
||||
validators []string
|
||||
jsonOutput bool
|
||||
|
||||
// Data access.
|
||||
eth2Client eth2client.Service
|
||||
chainTime chaintime.Service
|
||||
proposerDutiesProvider eth2client.ProposerDutiesProvider
|
||||
attesterDutiesProvider eth2client.AttesterDutiesProvider
|
||||
blocksProvider eth2client.SignedBeaconBlockProvider
|
||||
syncCommitteesProvider eth2client.SyncCommitteesProvider
|
||||
validatorsProvider eth2client.ValidatorsProvider
|
||||
beaconCommitteesProvider eth2client.BeaconCommitteesProvider
|
||||
beaconBlockHeadersProvider eth2client.BeaconBlockHeadersProvider
|
||||
|
||||
// Processing.
|
||||
validatorsByIndex map[phase0.ValidatorIndex]*apiv1.Validator
|
||||
|
||||
// Results.
|
||||
summary *validatorSummary
|
||||
}
|
||||
|
||||
type validatorSummary struct {
|
||||
Epoch phase0.Epoch `json:"epoch"`
|
||||
Validators []*apiv1.Validator `json:"validators"`
|
||||
FirstSlot phase0.Slot `json:"first_slot"`
|
||||
LastSlot phase0.Slot `json:"last_slot"`
|
||||
ActiveValidators int `json:"active_validators"`
|
||||
ParticipatingValidators int `json:"participating_validators"`
|
||||
NonParticipatingValidators []*nonParticipatingValidator `json:"non_participating_validators"`
|
||||
IncorrectHeadValidators []*validatorFault `json:"incorrect_head_validators"`
|
||||
UntimelyHeadValidators []*validatorFault `json:"untimely_head_validators"`
|
||||
UntimelySourceValidators []*validatorFault `json:"untimely_source_validators"`
|
||||
IncorrectTargetValidators []*validatorFault `json:"incorrect_target_validators"`
|
||||
UntimelyTargetValidators []*validatorFault `json:"untimely_target_validators"`
|
||||
Slots []*slot `json:"slots"`
|
||||
Proposals []*epochProposal `json:"-"`
|
||||
SyncCommittee []*epochSyncCommittee `json:"-"`
|
||||
}
|
||||
|
||||
type slot struct {
|
||||
Slot phase0.Slot `json:"slot"`
|
||||
Attestations *slotAttestations `json:"attestations"`
|
||||
}
|
||||
|
||||
type slotAttestations struct {
|
||||
Expected int `json:"expected"`
|
||||
Included int `json:"included"`
|
||||
CorrectHead int `json:"correct_head"`
|
||||
TimelyHead int `json:"timely_head"`
|
||||
CorrectTarget int `json:"correct_target"`
|
||||
TimelyTarget int `json:"timely_target"`
|
||||
TimelySource int `json:"timely_source"`
|
||||
}
|
||||
|
||||
type epochProposal struct {
|
||||
Slot phase0.Slot `json:"slot"`
|
||||
Proposer phase0.ValidatorIndex `json:"proposer"`
|
||||
Block bool `json:"block"`
|
||||
}
|
||||
|
||||
type epochSyncCommittee struct {
|
||||
Index phase0.ValidatorIndex `json:"index"`
|
||||
Missed int `json:"missed"`
|
||||
}
|
||||
|
||||
type validatorFault struct {
|
||||
Validator phase0.ValidatorIndex `json:"validator_index"`
|
||||
AttestationData *phase0.AttestationData `json:"attestation_data,omitempty"`
|
||||
InclusionDistance int `json:"inclusion_delay"`
|
||||
}
|
||||
|
||||
type nonParticipatingValidator struct {
|
||||
Validator phase0.ValidatorIndex `json:"validator_index"`
|
||||
Slot phase0.Slot `json:"slot"`
|
||||
Committee phase0.CommitteeIndex `json:"committee_index"`
|
||||
}
|
||||
|
||||
func newCommand(ctx context.Context) (*command, error) {
|
||||
c := &command{
|
||||
quiet: viper.GetBool("quiet"),
|
||||
verbose: viper.GetBool("verbose"),
|
||||
debug: viper.GetBool("debug"),
|
||||
validatorsByIndex: make(map[phase0.ValidatorIndex]*apiv1.Validator),
|
||||
summary: &validatorSummary{},
|
||||
}
|
||||
|
||||
// Timeout.
|
||||
if viper.GetDuration("timeout") == 0 {
|
||||
return nil, errors.New("timeout is required")
|
||||
}
|
||||
c.timeout = viper.GetDuration("timeout")
|
||||
|
||||
c.connection = viper.GetString("connection")
|
||||
c.allowInsecureConnections = viper.GetBool("allow-insecure-connections")
|
||||
|
||||
c.epoch = viper.GetString("epoch")
|
||||
c.validators = viper.GetStringSlice("validators")
|
||||
c.jsonOutput = viper.GetBool("json")
|
||||
|
||||
return c, nil
|
||||
}
|
||||
64
cmd/validator/summary/command_internal_test.go
Normal file
64
cmd/validator/summary/command_internal_test.go
Normal file
@@ -0,0 +1,64 @@
|
||||
// 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 validatorsummary
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestInput(t *testing.T) {
|
||||
if os.Getenv("ETHDO_TEST_CONNECTION") == "" {
|
||||
t.Skip("ETHDO_TEST_CONNECTION not configured; cannot run tests")
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
vars map[string]interface{}
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "TimeoutMissing",
|
||||
vars: map[string]interface{}{},
|
||||
err: "timeout is required",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "5s",
|
||||
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
viper.Reset()
|
||||
|
||||
for k, v := range test.vars {
|
||||
viper.Set(k, v)
|
||||
}
|
||||
_, err := newCommand(context.Background())
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
86
cmd/validator/summary/output.go
Normal file
86
cmd/validator/summary/output.go
Normal file
@@ -0,0 +1,86 @@
|
||||
// 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 validatorsummary
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (c *command) output(ctx context.Context) (string, error) {
|
||||
if c.quiet {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if c.jsonOutput {
|
||||
return c.outputJSON(ctx)
|
||||
}
|
||||
|
||||
return c.outputTxt(ctx)
|
||||
}
|
||||
|
||||
func (c *command) outputJSON(_ context.Context) (string, error) {
|
||||
data, err := json.Marshal(c.summary)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
func (c *command) outputTxt(_ context.Context) (string, error) {
|
||||
builder := strings.Builder{}
|
||||
|
||||
builder.WriteString("Epoch ")
|
||||
builder.WriteString(fmt.Sprintf("%d:\n", c.summary.Epoch))
|
||||
if len(c.summary.NonParticipatingValidators) > 0 {
|
||||
builder.WriteString(" Non-participating validators:\n")
|
||||
for _, validator := range c.summary.NonParticipatingValidators {
|
||||
builder.WriteString(fmt.Sprintf(" %d (slot %d, committee %d)\n", validator.Validator, validator.Slot, validator.Committee))
|
||||
}
|
||||
}
|
||||
if len(c.summary.IncorrectHeadValidators) > 0 {
|
||||
builder.WriteString(" Incorrect head validators:\n")
|
||||
for _, validator := range c.summary.IncorrectHeadValidators {
|
||||
builder.WriteString(fmt.Sprintf(" %d (slot %d, committee %d)\n", validator.Validator, validator.AttestationData.Slot, validator.AttestationData.Index))
|
||||
}
|
||||
}
|
||||
if len(c.summary.UntimelyHeadValidators) > 0 {
|
||||
builder.WriteString(" Untimely head validators:\n")
|
||||
for _, validator := range c.summary.UntimelyHeadValidators {
|
||||
builder.WriteString(fmt.Sprintf(" %d (slot %d, committee %d, inclusion distance %d)\n", validator.Validator, validator.AttestationData.Slot, validator.AttestationData.Index, validator.InclusionDistance))
|
||||
}
|
||||
}
|
||||
if len(c.summary.UntimelySourceValidators) > 0 {
|
||||
builder.WriteString(" Untimely source validators:\n")
|
||||
for _, validator := range c.summary.UntimelySourceValidators {
|
||||
builder.WriteString(fmt.Sprintf(" %d (slot %d, committee %d, inclusion distance %d)\n", validator.Validator, validator.AttestationData.Slot, validator.AttestationData.Index, validator.InclusionDistance))
|
||||
}
|
||||
}
|
||||
if len(c.summary.IncorrectTargetValidators) > 0 {
|
||||
builder.WriteString(" Incorrect target validators:\n")
|
||||
for _, validator := range c.summary.IncorrectTargetValidators {
|
||||
builder.WriteString(fmt.Sprintf(" %d (slot %d, committee %d)\n", validator.Validator, validator.AttestationData.Slot, validator.AttestationData.Index))
|
||||
}
|
||||
}
|
||||
if len(c.summary.UntimelyTargetValidators) > 0 {
|
||||
builder.WriteString(" Untimely target validators:\n")
|
||||
for _, validator := range c.summary.UntimelyTargetValidators {
|
||||
builder.WriteString(fmt.Sprintf(" %d (slot %d, committee %d, inclusion distance %d)\n", validator.Validator, validator.AttestationData.Slot, validator.AttestationData.Index, validator.InclusionDistance))
|
||||
}
|
||||
}
|
||||
|
||||
return builder.String(), nil
|
||||
}
|
||||
425
cmd/validator/summary/process.go
Normal file
425
cmd/validator/summary/process.go
Normal file
@@ -0,0 +1,425 @@
|
||||
// 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 validatorsummary
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
eth2client "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"
|
||||
standardchaintime "github.com/wealdtech/ethdo/services/chaintime/standard"
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
)
|
||||
|
||||
func (c *command) process(ctx context.Context) error {
|
||||
// Obtain information we need to process.
|
||||
err := c.setup(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.summary.Epoch, err = util.ParseEpoch(ctx, c.chainTime, c.epoch)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to parse epoch")
|
||||
}
|
||||
c.summary.FirstSlot = c.chainTime.FirstSlotOfEpoch(c.summary.Epoch)
|
||||
c.summary.LastSlot = c.chainTime.FirstSlotOfEpoch(c.summary.Epoch+1) - 1
|
||||
c.summary.Slots = make([]*slot, 1+int(c.summary.LastSlot)-int(c.summary.FirstSlot))
|
||||
for i := range c.summary.Slots {
|
||||
c.summary.Slots[i] = &slot{
|
||||
Slot: c.summary.FirstSlot + phase0.Slot(i),
|
||||
}
|
||||
}
|
||||
|
||||
c.summary.Validators, err = util.ParseValidators(ctx, c.validatorsProvider, c.validators, fmt.Sprintf("%d", c.summary.FirstSlot))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to parse validators")
|
||||
}
|
||||
// Reorder validators by index.
|
||||
sort.Slice(c.summary.Validators, func(i int, j int) bool {
|
||||
return c.summary.Validators[i].Index < c.summary.Validators[j].Index
|
||||
})
|
||||
|
||||
// Create a map for validator indices for easy lookup.
|
||||
c.validatorsByIndex = make(map[phase0.ValidatorIndex]*apiv1.Validator)
|
||||
for _, validator := range c.summary.Validators {
|
||||
c.validatorsByIndex[validator.Index] = validator
|
||||
}
|
||||
|
||||
if err := c.processProposerDuties(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.processAttesterDuties(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// if err := c.processSyncCommitteeDuties(ctx); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) processProposerDuties(ctx context.Context) error {
|
||||
duties, err := c.proposerDutiesProvider.ProposerDuties(ctx, c.summary.Epoch, nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to obtain proposer duties")
|
||||
}
|
||||
if duties == nil {
|
||||
return errors.New("empty proposer duties")
|
||||
}
|
||||
for _, duty := range duties {
|
||||
if _, exists := c.validatorsByIndex[duty.ValidatorIndex]; !exists {
|
||||
continue
|
||||
}
|
||||
block, err := c.blocksProvider.SignedBeaconBlock(ctx, fmt.Sprintf("%d", duty.Slot))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, fmt.Sprintf("failed to obtain block for slot %d", duty.Slot))
|
||||
}
|
||||
present := block != nil
|
||||
c.summary.Proposals = append(c.summary.Proposals, &epochProposal{
|
||||
Slot: duty.Slot,
|
||||
Proposer: duty.ValidatorIndex,
|
||||
Block: present,
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) activeValidators() (map[phase0.ValidatorIndex]*apiv1.Validator, []phase0.ValidatorIndex) {
|
||||
activeValidators := make(map[phase0.ValidatorIndex]*apiv1.Validator)
|
||||
activeValidatorIndices := make([]phase0.ValidatorIndex, 0, len(c.validatorsByIndex))
|
||||
for _, validator := range c.summary.Validators {
|
||||
if validator.Validator.ActivationEpoch <= c.summary.Epoch && validator.Validator.ExitEpoch > c.summary.Epoch {
|
||||
activeValidators[validator.Index] = validator
|
||||
activeValidatorIndices = append(activeValidatorIndices, validator.Index)
|
||||
}
|
||||
}
|
||||
|
||||
return activeValidators, activeValidatorIndices
|
||||
}
|
||||
|
||||
func (c *command) processAttesterDuties(ctx context.Context) error {
|
||||
activeValidators, activeValidatorIndices := c.activeValidators()
|
||||
|
||||
// Obtain number of validators that voted for blocks in the epoch.
|
||||
// These votes can be included anywhere from the second slot of
|
||||
// the epoch to the first slot of the next-but-one epoch.
|
||||
firstSlot := c.chainTime.FirstSlotOfEpoch(c.summary.Epoch) + 1
|
||||
lastSlot := c.chainTime.FirstSlotOfEpoch(c.summary.Epoch + 2)
|
||||
if lastSlot > c.chainTime.CurrentSlot() {
|
||||
lastSlot = c.chainTime.CurrentSlot()
|
||||
}
|
||||
|
||||
// Obtain the duties for the validators to know where they should be attesting.
|
||||
duties, err := c.attesterDutiesProvider.AttesterDuties(ctx, c.summary.Epoch, activeValidatorIndices)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to obtain attester duties")
|
||||
}
|
||||
for slot := c.chainTime.FirstSlotOfEpoch(c.summary.Epoch); slot < c.chainTime.FirstSlotOfEpoch(c.summary.Epoch+1); slot++ {
|
||||
index := int(slot - c.chainTime.FirstSlotOfEpoch(c.summary.Epoch))
|
||||
c.summary.Slots[index].Attestations = &slotAttestations{}
|
||||
}
|
||||
|
||||
// Need a cache of beacon block headers to reduce lookup times.
|
||||
headersCache := util.NewBeaconBlockHeaderCache(c.beaconBlockHeadersProvider)
|
||||
|
||||
// Need a map of duties to easily find the attestations we care about.
|
||||
dutiesBySlot := make(map[phase0.Slot]map[phase0.CommitteeIndex][]*apiv1.AttesterDuty)
|
||||
dutiesByValidatorIndex := make(map[phase0.ValidatorIndex]*apiv1.AttesterDuty)
|
||||
for _, duty := range duties {
|
||||
index := int(duty.Slot - c.chainTime.FirstSlotOfEpoch(c.summary.Epoch))
|
||||
dutiesByValidatorIndex[duty.ValidatorIndex] = duty
|
||||
c.summary.Slots[index].Attestations.Expected++
|
||||
if _, exists := dutiesBySlot[duty.Slot]; !exists {
|
||||
dutiesBySlot[duty.Slot] = make(map[phase0.CommitteeIndex][]*apiv1.AttesterDuty)
|
||||
}
|
||||
if _, exists := dutiesBySlot[duty.Slot][duty.CommitteeIndex]; !exists {
|
||||
dutiesBySlot[duty.Slot][duty.CommitteeIndex] = make([]*apiv1.AttesterDuty, 0)
|
||||
}
|
||||
dutiesBySlot[duty.Slot][duty.CommitteeIndex] = append(dutiesBySlot[duty.Slot][duty.CommitteeIndex], duty)
|
||||
}
|
||||
|
||||
c.summary.IncorrectHeadValidators = make([]*validatorFault, 0)
|
||||
c.summary.UntimelyHeadValidators = make([]*validatorFault, 0)
|
||||
c.summary.UntimelySourceValidators = make([]*validatorFault, 0)
|
||||
c.summary.IncorrectTargetValidators = make([]*validatorFault, 0)
|
||||
c.summary.UntimelyTargetValidators = make([]*validatorFault, 0)
|
||||
|
||||
// Hunt through the blocks looking for attestations from the validators.
|
||||
votes := make(map[phase0.ValidatorIndex]struct{})
|
||||
for slot := firstSlot; slot <= lastSlot; slot++ {
|
||||
if err := c.processAttesterDutiesSlot(ctx, slot, dutiesBySlot, votes, headersCache, activeValidatorIndices); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Use dutiesMap and votes to work out which validators didn't participate.
|
||||
c.summary.NonParticipatingValidators = make([]*nonParticipatingValidator, 0)
|
||||
for _, index := range activeValidatorIndices {
|
||||
if _, exists := votes[index]; !exists {
|
||||
// Didn't vote.
|
||||
duty := dutiesByValidatorIndex[index]
|
||||
c.summary.NonParticipatingValidators = append(c.summary.NonParticipatingValidators, &nonParticipatingValidator{
|
||||
Validator: index,
|
||||
Slot: duty.Slot,
|
||||
Committee: duty.CommitteeIndex,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the non-participating validators list.
|
||||
sort.Slice(c.summary.NonParticipatingValidators, func(i int, j int) bool {
|
||||
if c.summary.NonParticipatingValidators[i].Slot != c.summary.NonParticipatingValidators[j].Slot {
|
||||
return c.summary.NonParticipatingValidators[i].Slot < c.summary.NonParticipatingValidators[j].Slot
|
||||
}
|
||||
if c.summary.NonParticipatingValidators[i].Committee != c.summary.NonParticipatingValidators[j].Committee {
|
||||
return c.summary.NonParticipatingValidators[i].Committee < c.summary.NonParticipatingValidators[j].Committee
|
||||
}
|
||||
return c.summary.NonParticipatingValidators[i].Validator < c.summary.NonParticipatingValidators[j].Validator
|
||||
})
|
||||
|
||||
c.summary.ActiveValidators = len(activeValidators)
|
||||
c.summary.ParticipatingValidators = len(votes)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) processAttesterDutiesSlot(ctx context.Context,
|
||||
slot phase0.Slot,
|
||||
dutiesBySlot map[phase0.Slot]map[phase0.CommitteeIndex][]*apiv1.AttesterDuty,
|
||||
votes map[phase0.ValidatorIndex]struct{},
|
||||
headersCache *util.BeaconBlockHeaderCache,
|
||||
activeValidatorIndices []phase0.ValidatorIndex,
|
||||
) error {
|
||||
block, err := c.blocksProvider.SignedBeaconBlock(ctx, fmt.Sprintf("%d", slot))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, fmt.Sprintf("failed to obtain block for slot %d", slot))
|
||||
}
|
||||
if block == nil {
|
||||
// No block at this slot; that's fine.
|
||||
return nil
|
||||
}
|
||||
attestations, err := block.Attestations()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, attestation := range attestations {
|
||||
if _, exists := dutiesBySlot[attestation.Data.Slot]; !exists {
|
||||
// We do not have any attestations for this slot.
|
||||
continue
|
||||
}
|
||||
if _, exists := dutiesBySlot[attestation.Data.Slot][attestation.Data.Index]; !exists {
|
||||
// We do not have any attestations for this committee.
|
||||
continue
|
||||
}
|
||||
for _, duty := range dutiesBySlot[attestation.Data.Slot][attestation.Data.Index] {
|
||||
if attestation.AggregationBits.BitAt(duty.ValidatorCommitteeIndex) {
|
||||
// Found it.
|
||||
if _, exists := votes[duty.ValidatorIndex]; exists {
|
||||
// Duplicate; ignore.
|
||||
continue
|
||||
}
|
||||
votes[duty.ValidatorIndex] = struct{}{}
|
||||
|
||||
// Update the metrics for the attestation.
|
||||
index := int(attestation.Data.Slot - c.chainTime.FirstSlotOfEpoch(c.summary.Epoch))
|
||||
c.summary.Slots[index].Attestations.Included++
|
||||
inclusionDelay := slot - duty.Slot
|
||||
|
||||
fault := &validatorFault{
|
||||
Validator: duty.ValidatorIndex,
|
||||
AttestationData: attestation.Data,
|
||||
InclusionDistance: int(inclusionDelay),
|
||||
}
|
||||
|
||||
headCorrect, err := util.AttestationHeadCorrect(ctx, headersCache, attestation)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to calculate if attestation had correct head vote")
|
||||
}
|
||||
if headCorrect {
|
||||
c.summary.Slots[index].Attestations.CorrectHead++
|
||||
if inclusionDelay == 1 {
|
||||
c.summary.Slots[index].Attestations.TimelyHead++
|
||||
} else {
|
||||
c.summary.UntimelyHeadValidators = append(c.summary.UntimelyHeadValidators, fault)
|
||||
}
|
||||
} else {
|
||||
c.summary.IncorrectHeadValidators = append(c.summary.IncorrectHeadValidators, fault)
|
||||
if inclusionDelay > 1 {
|
||||
c.summary.UntimelyHeadValidators = append(c.summary.UntimelyHeadValidators, fault)
|
||||
}
|
||||
}
|
||||
|
||||
if inclusionDelay <= 5 {
|
||||
c.summary.Slots[index].Attestations.TimelySource++
|
||||
} else {
|
||||
c.summary.UntimelySourceValidators = append(c.summary.UntimelySourceValidators, fault)
|
||||
}
|
||||
|
||||
targetCorrect, err := util.AttestationTargetCorrect(ctx, headersCache, c.chainTime, attestation)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to calculate if attestation had correct target vote")
|
||||
}
|
||||
if targetCorrect {
|
||||
c.summary.Slots[index].Attestations.CorrectTarget++
|
||||
if inclusionDelay <= 32 {
|
||||
c.summary.Slots[index].Attestations.TimelyTarget++
|
||||
} else {
|
||||
c.summary.UntimelyTargetValidators = append(c.summary.UntimelyTargetValidators, fault)
|
||||
}
|
||||
} else {
|
||||
c.summary.IncorrectTargetValidators = append(c.summary.IncorrectTargetValidators, fault)
|
||||
if inclusionDelay > 32 {
|
||||
c.summary.UntimelyTargetValidators = append(c.summary.UntimelyTargetValidators, fault)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(votes) == len(activeValidatorIndices) {
|
||||
// Found them all.
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// func (c *command) processSyncCommitteeDuties(ctx context.Context) error {
|
||||
// if c.summary.Epoch < c.chainTime.AltairInitialEpoch() {
|
||||
// // The epoch is pre-Altair. No info but no error.
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// committee, err := c.syncCommitteesProvider.SyncCommittee(ctx, fmt.Sprintf("%d", c.summary.FirstSlot))
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "failed to obtain sync committee")
|
||||
// }
|
||||
// if len(committee.Validators) == 0 {
|
||||
// return errors.Wrap(err, "empty sync committee")
|
||||
// }
|
||||
//
|
||||
// missed := make(map[phase0.ValidatorIndex]int)
|
||||
// for _, index := range committee.Validators {
|
||||
// missed[index] = 0
|
||||
// }
|
||||
//
|
||||
// for slot := c.summary.FirstSlot; slot <= c.summary.LastSlot; slot++ {
|
||||
// block, err := c.blocksProvider.SignedBeaconBlock(ctx, fmt.Sprintf("%d", slot))
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, fmt.Sprintf("failed to obtain block for slot %d", slot))
|
||||
// }
|
||||
// if block == nil {
|
||||
// // If the block is missed we don't count the sync aggregate miss.
|
||||
// continue
|
||||
// }
|
||||
// var aggregate *altair.SyncAggregate
|
||||
// switch block.Version {
|
||||
// case spec.DataVersionPhase0:
|
||||
// // No sync committees in this fork.
|
||||
// return nil
|
||||
// case spec.DataVersionAltair:
|
||||
// aggregate = block.Altair.Message.Body.SyncAggregate
|
||||
// case spec.DataVersionBellatrix:
|
||||
// aggregate = block.Bellatrix.Message.Body.SyncAggregate
|
||||
// default:
|
||||
// return fmt.Errorf("unhandled block version %v", block.Version)
|
||||
// }
|
||||
// for i := uint64(0); i < aggregate.SyncCommitteeBits.Len(); i++ {
|
||||
// if !aggregate.SyncCommitteeBits.BitAt(i) {
|
||||
// missed[committee.Validators[int(i)]]++
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// c.summary.SyncCommittee = make([]*epochSyncCommittee, 0, len(missed))
|
||||
// for index, count := range missed {
|
||||
// if count > 0 {
|
||||
// c.summary.SyncCommittee = append(c.summary.SyncCommittee, &epochSyncCommittee{
|
||||
// Index: index,
|
||||
// Missed: count,
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// sort.Slice(c.summary.SyncCommittee, func(i int, j int) bool {
|
||||
// missedDiff := c.summary.SyncCommittee[i].Missed - c.summary.SyncCommittee[j].Missed
|
||||
// if missedDiff != 0 {
|
||||
// // Actually want to order by missed descending, so invert the expected condition.
|
||||
// return missedDiff > 0
|
||||
// }
|
||||
// // Then order by validator index.
|
||||
// return c.summary.SyncCommittee[i].Index < c.summary.SyncCommittee[j].Index
|
||||
// })
|
||||
//
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func (c *command) setup(ctx context.Context) error {
|
||||
var err error
|
||||
|
||||
// Connect to the client.
|
||||
c.eth2Client, err = util.ConnectToBeaconNode(ctx, c.connection, c.timeout, c.allowInsecureConnections)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to connect to beacon node")
|
||||
}
|
||||
|
||||
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 {
|
||||
return errors.Wrap(err, "failed to set up chaintime service")
|
||||
}
|
||||
|
||||
var isProvider bool
|
||||
c.proposerDutiesProvider, isProvider = c.eth2Client.(eth2client.ProposerDutiesProvider)
|
||||
if !isProvider {
|
||||
return errors.New("connection does not provide proposer duties")
|
||||
}
|
||||
c.attesterDutiesProvider, isProvider = c.eth2Client.(eth2client.AttesterDutiesProvider)
|
||||
if !isProvider {
|
||||
return errors.New("connection does not provide attester duties")
|
||||
}
|
||||
c.blocksProvider, isProvider = c.eth2Client.(eth2client.SignedBeaconBlockProvider)
|
||||
if !isProvider {
|
||||
return errors.New("connection does not provide signed beacon blocks")
|
||||
}
|
||||
c.syncCommitteesProvider, isProvider = c.eth2Client.(eth2client.SyncCommitteesProvider)
|
||||
if !isProvider {
|
||||
return errors.New("connection does not provide sync committee duties")
|
||||
}
|
||||
c.validatorsProvider, isProvider = c.eth2Client.(eth2client.ValidatorsProvider)
|
||||
if !isProvider {
|
||||
return errors.New("connection does not provide validators")
|
||||
}
|
||||
c.beaconCommitteesProvider, isProvider = c.eth2Client.(eth2client.BeaconCommitteesProvider)
|
||||
if !isProvider {
|
||||
return errors.New("connection does not provide beacon committees")
|
||||
}
|
||||
c.beaconBlockHeadersProvider, isProvider = c.eth2Client.(eth2client.BeaconBlockHeadersProvider)
|
||||
if !isProvider {
|
||||
return errors.New("connection does not provide beacon block headers")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
62
cmd/validator/summary/process_internal_test.go
Normal file
62
cmd/validator/summary/process_internal_test.go
Normal file
@@ -0,0 +1,62 @@
|
||||
// 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 validatorsummary
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestProcess(t *testing.T) {
|
||||
if os.Getenv("ETHDO_TEST_CONNECTION") == "" {
|
||||
t.Skip("ETHDO_TEST_CONNECTION not configured; cannot run tests")
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
vars map[string]interface{}
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "InvalidData",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "60s",
|
||||
"data": "[[",
|
||||
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
viper.Reset()
|
||||
|
||||
for k, v := range test.vars {
|
||||
viper.Set(k, v)
|
||||
}
|
||||
cmd, err := newCommand(context.Background())
|
||||
require.NoError(t, err)
|
||||
err = cmd.process(context.Background())
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
50
cmd/validator/summary/run.go
Normal file
50
cmd/validator/summary/run.go
Normal file
@@ -0,0 +1,50 @@
|
||||
// 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 validatorsummary
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// Run runs the command.
|
||||
func Run(cmd *cobra.Command) (string, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
c, err := newCommand(ctx)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to set up command")
|
||||
}
|
||||
|
||||
// Further errors do not need a usage report.
|
||||
cmd.SilenceUsage = true
|
||||
|
||||
if err := c.process(ctx); err != nil {
|
||||
return "", errors.Wrap(err, "failed to process")
|
||||
}
|
||||
|
||||
if viper.GetBool("quiet") {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
results, err := c.output(ctx)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to obtain output")
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
@@ -72,9 +72,6 @@ func newCommand(ctx context.Context) (*command, error) {
|
||||
}
|
||||
c.timeout = viper.GetDuration("timeout")
|
||||
|
||||
if viper.GetString("connection") == "" {
|
||||
return nil, errors.New("connection is required")
|
||||
}
|
||||
c.connection = viper.GetString("connection")
|
||||
c.allowInsecureConnections = viper.GetBool("allow-insecure-connections")
|
||||
|
||||
|
||||
@@ -37,14 +37,6 @@ func TestInput(t *testing.T) {
|
||||
vars: map[string]interface{}{},
|
||||
err: "timeout is required",
|
||||
},
|
||||
{
|
||||
name: "ConnectionMissing",
|
||||
vars: map[string]interface{}{
|
||||
"validators": "1",
|
||||
"timeout": "5s",
|
||||
},
|
||||
err: "connection is required",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
vars: map[string]interface{}{
|
||||
|
||||
@@ -26,7 +26,7 @@ var validatorCredentialsGetCmd = &cobra.Command{
|
||||
Short: "Obtain withdrawal credentials for an Ethereum consensus validator",
|
||||
Long: `Obtain withdrawal credentials for an Ethereum consensus validator. For example:
|
||||
|
||||
ethdo validator credentials get --account=primary/validator
|
||||
ethdo validator credentials get --validator=primary/validator
|
||||
|
||||
In quiet mode this will return 0 if the validator exists, otherwise 1.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
@@ -47,19 +47,11 @@ In quiet mode this will return 0 if the validator exists, otherwise 1.`,
|
||||
func init() {
|
||||
validatorCredentialsCmd.AddCommand(validatorCredentialsGetCmd)
|
||||
validatorCredentialsFlags(validatorCredentialsGetCmd)
|
||||
validatorCredentialsGetCmd.Flags().String("account", "", "Account for which to fetch validator credentials")
|
||||
validatorCredentialsGetCmd.Flags().String("index", "", "Validator index for which to fetch validator credentials")
|
||||
validatorCredentialsGetCmd.Flags().String("pubkey", "", "Validator public key for which to fetch validator credentials")
|
||||
validatorCredentialsGetCmd.Flags().String("validator", "", "Validator for which to get validator credentials")
|
||||
}
|
||||
|
||||
func validatorCredentialsGetBindings() {
|
||||
if err := viper.BindPFlag("account", validatorCredentialsGetCmd.Flags().Lookup("account")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("index", validatorCredentialsGetCmd.Flags().Lookup("index")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("pubkey", validatorCredentialsGetCmd.Flags().Lookup("pubkey")); err != nil {
|
||||
if err := viper.BindPFlag("validator", validatorCredentialsGetCmd.Flags().Lookup("validator")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
92
cmd/validatorcredentialsset.go
Normal file
92
cmd/validatorcredentialsset.go
Normal file
@@ -0,0 +1,92 @@
|
||||
// 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 cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
validatorcredentialsset "github.com/wealdtech/ethdo/cmd/validator/credentials/set"
|
||||
)
|
||||
|
||||
var validatorCredentialsSetCmd = &cobra.Command{
|
||||
Use: "set",
|
||||
Short: "Set withdrawal credentials for an Ethereum consensus validator",
|
||||
Long: `Set withdrawal credentials for an Ethereum consensus validator via a "change credentials" operation. For example:
|
||||
|
||||
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:
|
||||
|
||||
- mnemonic using --mnemonic; this will scan the mnemonic and generate all required 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
|
||||
|
||||
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 {
|
||||
res, err := validatorcredentialsset.Run(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if viper.GetBool("quiet") {
|
||||
return nil
|
||||
}
|
||||
if res != "" {
|
||||
fmt.Println(res)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
validatorCredentialsCmd.AddCommand(validatorCredentialsSetCmd)
|
||||
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-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().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 (offline only)")
|
||||
validatorCredentialsSetCmd.Flags().String("genesis-validators-root", "", "Genesis validators root to use for signing (offline only)")
|
||||
}
|
||||
|
||||
func validatorCredentialsSetBindings() {
|
||||
if err := viper.BindPFlag("prepare-offline", validatorCredentialsSetCmd.Flags().Lookup("prepare-offline")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
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 {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("withdrawal-address", validatorCredentialsSetCmd.Flags().Lookup("withdrawal-address")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("json", validatorCredentialsSetCmd.Flags().Lookup("json")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("offline", validatorCredentialsSetCmd.Flags().Lookup("offline")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("fork-version", validatorCredentialsSetCmd.Flags().Lookup("fork-version")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("genesis-validators-root", validatorCredentialsSetCmd.Flags().Lookup("genesis-validators-root")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2020, 2021 Weald Technology Trading
|
||||
// Copyright © 2020 - 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
|
||||
@@ -16,7 +16,6 @@ package cmd
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
@@ -32,7 +31,6 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
string2eth "github.com/wealdtech/go-string2eth"
|
||||
)
|
||||
|
||||
@@ -41,7 +39,7 @@ var validatorInfoCmd = &cobra.Command{
|
||||
Short: "Obtain information about a validator",
|
||||
Long: `Obtain information about validator. For example:
|
||||
|
||||
ethdo validator info --account=primary/validator
|
||||
ethdo validator info --validator=primary/validator
|
||||
|
||||
In quiet mode this will return 0 if the validator information can be obtained, otherwise 1.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
@@ -54,32 +52,21 @@ In quiet mode this will return 0 if the validator information can be obtained, o
|
||||
)
|
||||
errCheck(err, "Failed to connect to Ethereum 2 beacon node")
|
||||
|
||||
account, err := validatorInfoAccount(ctx, eth2Client)
|
||||
errCheck(err, "Failed to obtain validator account")
|
||||
|
||||
pubKeys := make([]spec.BLSPubKey, 1)
|
||||
pubKey, err := util.BestPublicKey(account)
|
||||
errCheck(err, "Failed to obtain validator public key")
|
||||
copy(pubKeys[0][:], pubKey.Marshal())
|
||||
validators, err := eth2Client.(eth2client.ValidatorsProvider).ValidatorsByPubKey(ctx, "head", pubKeys)
|
||||
errCheck(err, "Failed to obtain validator information")
|
||||
if len(validators) == 0 {
|
||||
fmt.Println("Validator not known by beacon node")
|
||||
os.Exit(_exitSuccess)
|
||||
if viper.GetString("validator") == "" {
|
||||
fmt.Println("validator is required")
|
||||
os.Exit(_exitFailure)
|
||||
}
|
||||
|
||||
var validator *api.Validator
|
||||
for _, v := range validators {
|
||||
validator = v
|
||||
}
|
||||
validator, err := util.ParseValidator(ctx, eth2Client.(eth2client.ValidatorsProvider), viper.GetString("validator"), "head")
|
||||
errCheck(err, "Failed to obtain validator")
|
||||
|
||||
if verbose {
|
||||
network, err := util.Network(ctx, eth2Client)
|
||||
errCheck(err, "Failed to obtain network")
|
||||
outputIf(debug, fmt.Sprintf("Network is %s", network))
|
||||
pubKey, err := bestPublicKey(account)
|
||||
pubKey, err := validator.PubKey(ctx)
|
||||
if err == nil {
|
||||
deposits, totalDeposited, err := graphData(network, pubKey.Marshal())
|
||||
deposits, totalDeposited, err := graphData(network, pubKey[:])
|
||||
if err == nil && deposits > 0 {
|
||||
fmt.Printf("Number of deposits: %d\n", deposits)
|
||||
fmt.Printf("Total deposited: %s\n", string2eth.GWeiToString(uint64(totalDeposited), true))
|
||||
@@ -122,54 +109,6 @@ In quiet mode this will return 0 if the validator information can be obtained, o
|
||||
},
|
||||
}
|
||||
|
||||
// validatorInfoAccount obtains the account for the validator info command.
|
||||
func validatorInfoAccount(ctx context.Context, eth2Client eth2client.Service) (e2wtypes.Account, error) {
|
||||
var account e2wtypes.Account
|
||||
var err error
|
||||
switch {
|
||||
case 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")
|
||||
}
|
||||
case viper.GetString("pubkey") != "":
|
||||
pubKeyBytes, err := hex.DecodeString(strings.TrimPrefix(viper.GetString("pubkey"), "0x"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("failed to decode public key %s", viper.GetString("pubkey")))
|
||||
}
|
||||
account, err = util.NewScratchAccount(nil, pubKeyBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("invalid public key %s", viper.GetString("pubkey")))
|
||||
}
|
||||
case viper.GetInt64("index") != -1:
|
||||
validatorsProvider, isValidatorsProvider := eth2Client.(eth2client.ValidatorsProvider)
|
||||
if !isValidatorsProvider {
|
||||
return nil, errors.New("client does not provide validator information")
|
||||
}
|
||||
index := spec.ValidatorIndex(viper.GetInt64("index"))
|
||||
validators, err := validatorsProvider.Validators(ctx, "head", []spec.ValidatorIndex{
|
||||
index,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain validator information")
|
||||
}
|
||||
if len(validators) == 0 {
|
||||
return nil, errors.New("unknown validator index")
|
||||
}
|
||||
pubKeyBytes := make([]byte, 48)
|
||||
copy(pubKeyBytes, validators[index].Validator.PublicKey[:])
|
||||
account, err = util.NewScratchAccount(nil, pubKeyBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("invalid public key %s", viper.GetString("pubkey")))
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("neither account nor public key supplied")
|
||||
}
|
||||
return account, nil
|
||||
}
|
||||
|
||||
// graphData returns data from the graph about number and amount of deposits
|
||||
func graphData(network string, validatorPubKey []byte) (uint64, spec.Gwei, error) {
|
||||
subgraph := ""
|
||||
@@ -224,16 +163,12 @@ func graphData(network string, validatorPubKey []byte) (uint64, spec.Gwei, error
|
||||
|
||||
func init() {
|
||||
validatorCmd.AddCommand(validatorInfoCmd)
|
||||
validatorInfoCmd.Flags().String("pubkey", "", "Public key for which to obtain status")
|
||||
validatorInfoCmd.Flags().Int64("index", -1, "Index for which to obtain status")
|
||||
validatorInfoCmd.Flags().String("validator", "", "Public key for which to obtain status")
|
||||
validatorFlags(validatorInfoCmd)
|
||||
}
|
||||
|
||||
func validatorInfoBindings() {
|
||||
if err := viper.BindPFlag("pubkey", validatorInfoCmd.Flags().Lookup("pubkey")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("index", validatorInfoCmd.Flags().Lookup("index")); err != nil {
|
||||
if err := viper.BindPFlag("validator", validatorInfoCmd.Flags().Lookup("validator")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@ func init() {
|
||||
validatorCmd.AddCommand(validatorKeycheckCmd)
|
||||
validatorFlags(validatorKeycheckCmd)
|
||||
validatorKeycheckCmd.Flags().String("withdrawal-credentials", "", "Withdrawal credentials to check (can run offline)")
|
||||
validatorKeycheckCmd.Flags().String("mnemonic", "", "Mnemonic from which to generate withdrawal credentials")
|
||||
validatorKeycheckCmd.Flags().String("privkey", "", "Private key from which to generate withdrawal credentials")
|
||||
}
|
||||
|
||||
@@ -58,9 +57,6 @@ func validatorKeycheckBindings() {
|
||||
if err := viper.BindPFlag("withdrawal-credentials", validatorKeycheckCmd.Flags().Lookup("withdrawal-credentials")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("mnemonic", validatorKeycheckCmd.Flags().Lookup("mnemonic")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("privkey", validatorKeycheckCmd.Flags().Lookup("privkey")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
66
cmd/validatorsummary.go
Normal file
66
cmd/validatorsummary.go
Normal file
@@ -0,0 +1,66 @@
|
||||
// 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 cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
validatorsummary "github.com/wealdtech/ethdo/cmd/validator/summary"
|
||||
)
|
||||
|
||||
var validatorSummaryCmd = &cobra.Command{
|
||||
Use: "summary",
|
||||
Short: "Obtain summary information about validator(s) in an epoch",
|
||||
Long: `Obtain summary information about one or more validators in an epoch. For example:
|
||||
|
||||
ethdo validator summary --validators=1,2,3 --epoch=12345
|
||||
|
||||
In quiet mode this will return 0 if information for the epoch is found, otherwise 1.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
res, err := validatorsummary.Run(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if viper.GetBool("quiet") {
|
||||
return nil
|
||||
}
|
||||
if res != "" {
|
||||
fmt.Println(res)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
validatorCmd.AddCommand(validatorSummaryCmd)
|
||||
validatorFlags(validatorSummaryCmd)
|
||||
validatorSummaryCmd.Flags().String("epoch", "", "the epoch for which to obtain information ()")
|
||||
validatorSummaryCmd.Flags().StringSlice("validators", nil, "the list of validators for which to obtain information")
|
||||
validatorSummaryCmd.Flags().Bool("json", false, "output data in JSON format")
|
||||
}
|
||||
|
||||
func validatorSummaryBindings() {
|
||||
validatorBindings()
|
||||
if err := viper.BindPFlag("epoch", validatorSummaryCmd.Flags().Lookup("epoch")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("validators", validatorSummaryCmd.Flags().Lookup("validators")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("json", validatorSummaryCmd.Flags().Lookup("json")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
|
||||
// ReleaseVersion is the release version of the codebase.
|
||||
// Usually overridden by tag names when building binaries.
|
||||
var ReleaseVersion = "local build (latest release 1.25.0)"
|
||||
var ReleaseVersion = "local build (latest release 1.26.0)"
|
||||
|
||||
// versionCmd represents the version command
|
||||
var versionCmd = &cobra.Command{
|
||||
|
||||
@@ -45,14 +45,10 @@ func init() {
|
||||
walletCmd.AddCommand(walletCreateCmd)
|
||||
walletFlags(walletCreateCmd)
|
||||
walletCreateCmd.Flags().String("type", "non-deterministic", "Type of wallet to create (non-deterministic or hierarchical deterministic)")
|
||||
walletCreateCmd.Flags().String("mnemonic", "", "The 24-word mnemonic for a hierarchical deterministic wallet")
|
||||
}
|
||||
|
||||
func walletCreateBindings() {
|
||||
if err := viper.BindPFlag("type", walletCreateCmd.Flags().Lookup("type")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("mnemonic", walletCreateCmd.Flags().Lookup("mnemonic")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
218
docs/changingwithdrawalcredentials.md
Normal file
218
docs/changingwithdrawalcredentials.md
Normal file
@@ -0,0 +1,218 @@
|
||||
# Changing withdrawal credentials
|
||||
When creating a validator it is possible to set its withdrawal credentials to those based upon a BLS private key (known as BLS withdrawal credentials, or "type 0" withdrawal credentials) or based upon an Ethereum execution address (known as execution withdrawal credentials, or "type 1" withdrawal credentials). With the advent of the Capella hard fork, it is possible for rewards accrued on the consensus chain (also known as the beacon chain) to be sent to the execution chain. However, for this to occur the validator's withdrawal credentials must be type 1. Capella also brings a mechanism to change existing type 0 withdrawal credentials to type 1 withdrawal credentials, and this document outlines the process to change withdrawal credentials from type 0 to type 1 so that consensus rewards can be accessed.
|
||||
|
||||
**Once a validator has Ethereum execution credentials set they cannot be changed. Please be careful when following this or any similar process to ensure that you have access to the private key (either as a software file, a hardware key or a mnemonic) of the withdrawal address you use so that you have the ability to access your rewards.**
|
||||
|
||||
## Concepts
|
||||
The following concepts are useful when understanding the rest of this guide.
|
||||
|
||||
### Validator
|
||||
A validator is a logical entity that secures the Ethereum beacon chain (and hence the execution chain) by proposing blocks and attesting to blocks proposed by other validators.
|
||||
|
||||
### Withdrawal credentials
|
||||
Withdrawal credentials, held as part of a validator's on-chain definition, define where consensus rewards will be sent.
|
||||
|
||||
### Private key
|
||||
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_.
|
||||
|
||||
### 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".
|
||||
|
||||
### Withdrawal address
|
||||
A withdrawal address is an Ethereum execution address that will receive consensus rewards periodically during the operation of the validator and, ultimately, to which the initial deposit will be returned when the validator is exited. It is important to understand that at time of writing this value cannot be changed, so it is critical that one of the following criteria are met:
|
||||
|
||||
- the private keys for the Ethereum address are known
|
||||
- the Ethereum address is secured by a hardware wallet
|
||||
- the Ethereum address is that of a smart contract with the ability to withdraw funds
|
||||
|
||||
The execution address must be supplied in [EIP-55](https://eips.ethereum.org/EIPS/eip-55) format, _i.e._ using mixed case for checksum. An example of a mixed-case Ethereum address is `0x8f0844Fd51E31ff6Bf5baBe21DCcf7328E19Fd9F`
|
||||
|
||||
### Online and Offline
|
||||
An _online_ computer is one that is is connected to the internet. It should be running a consensus node connected to the larger Ethereum network. An online computer is required to carry out the process, to obtain information from the consensus node and to broadcast your actions to the rest of the Ethereum network.
|
||||
|
||||
An _offline_ computer is one that is not connected to the internet. As such, it will not be running a consensus node. It can optionally be used in conjunction with an online computer to provide higher levels of security for your mnemonic or private key, but is less convenient because it requires manual transfer of files from the online computer to the offline computer, and back.
|
||||
|
||||
With only an online computer the flow of information is roughly as follows:
|
||||
|
||||

|
||||
|
||||
Here it can be seen that a copy of `ethdo` with access to private keys connects to a consensus node with access to the internet. Due to its connection to the internet it is possible that the computer on which `ethdo` and the consensus node runs has been compromised, and as such would expose the private keys to an attacker.
|
||||
|
||||
With both an offline and an online computer the flow of information is roughly as follows:
|
||||
|
||||

|
||||
|
||||
Here the copy of `ethdo` with access to private keys is on an offline computer, which protects it from being compromised via the internet. Data is physically moved from the offline to the online computer via a USB storage key or similar, and none of the information on the online computer is sensitive.
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
|
||||
```sh
|
||||
ethdo --connection=http://localhost:12345 node info --verbose
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
Once the preparation is complete you should select either basic or advanced operation, depending on your requirements.
|
||||
|
||||
## Basic operation
|
||||
Given the above concepts, the purpose of this guide is to allow a change of validators' withdrawal credentials to be changed to a withdrawal address, allowing validator rewards to be accessed on the Ethereum execution chain.
|
||||
|
||||
Basic operation is suitable in the majority of cases. If you:
|
||||
|
||||
- generated your validators using a mnemonic (_e.g._ using the deposit CLI or launchpad)
|
||||
- want to change all of your validators to have the same withdrawal address
|
||||
- want to change all of your validators' withdrawal credentials at the same time
|
||||
|
||||
then this method is for you. If any of the above does not apply then please go to the "Advanced operation" section.
|
||||
|
||||
### Online process
|
||||
The online process generates and broadcasts the operations to change withdrawal credentials for all of your validators tied to a mnemonic in a single action.
|
||||
|
||||
Two pieces of information are required for carrying out this process online: the mnemonic and withdrawal address.
|
||||
|
||||
On your _online_ computer run the following:
|
||||
|
||||
```
|
||||
ethdo validator credentials set --mnemonic="abandon abandon abandon … art" --withdrawal-address=0x0123…cdef
|
||||
```
|
||||
|
||||
Replacing the `mnemonic` and `withdrawal-address` values with your own values. This command will:
|
||||
|
||||
1. obtain information from your consensus node about all currently-running validators and various additional information required to generate the operations
|
||||
2. scan your mnemonic to find any validators that were generated by it, and create the operations to change their credentials
|
||||
3. broadcast the credentials change operations to the Ethereum network
|
||||
|
||||
### Online and Offline process
|
||||
The online and offline process contains three steps. In the first, data is gathered on the online computer. In the second, the credentials change operations are generated on the offline computer. In the third, the operations are broadcast on the online computer.
|
||||
|
||||
Two pieces of information are required for carrying out this process online: the mnemonic and withdrawal address.
|
||||
|
||||
On your _online_ computer run the following:
|
||||
|
||||
```
|
||||
ethdo validator credentials set --prepare-offline
|
||||
```
|
||||
|
||||
This command will:
|
||||
|
||||
1. obtain information from your consensus node about all currently-running validators and various additional information required to generate the operations
|
||||
2. write this information to a file called `offline-preparation.json`
|
||||
|
||||
The `offline-preparation.json` file must be copied to your _offline_ computer. Once this has been done, on your _offline_ computer run the following:
|
||||
|
||||
```
|
||||
ethdo validator credentials set --offline --mnemonic="abandon abandon abandon … art" --withdrawal-address=0x0123…cdef
|
||||
```
|
||||
|
||||
Replacing the `mnemonic` and `withdrawal-address` values with your own values. This command will:
|
||||
|
||||
1. read the `offline-preparation.json` file to obtain information about all currently-running validators and various additional information required to generate the operations
|
||||
2. scan your mnemonic to find any validators that were generated by it, and create the operations to change their credentials
|
||||
3. write this information to a file called `change-operations.json`
|
||||
|
||||
The `change-operations.json` file must be copied to your _online_ computer. Once this has been done, on your _online_ computer run the following:
|
||||
|
||||
```
|
||||
ethdo validator credentials set
|
||||
```
|
||||
|
||||
This command will:
|
||||
|
||||
1. read the `change-operations.json` file to obtain the operations to change the validators' credentials
|
||||
2. broadcast the credentials change operations to the Ethereum network
|
||||
|
||||
## Advanced operation
|
||||
Advanced operation is required when any of the following conditions are met:
|
||||
|
||||
- your validators were created using something other than the deposit CLI or launchpad (_e.g._ `ethdo`)
|
||||
- you want to set your validators to have different withdrawal addresses
|
||||
- you want to change your validators' withdrawal credentials individually
|
||||
|
||||
### Validator reference
|
||||
There are three options to reference a validator:
|
||||
|
||||
- the `ethdo` account of the validator (in format wallet/account)
|
||||
- the validator's public key (in format 0x…)
|
||||
- the validator's on-chain index (in format 123…)
|
||||
|
||||
Any of these can be passed to the following commands with the `--validator` parameter. You need to ensure that you have this information before starting the process.
|
||||
|
||||
**In the following examples we will use the validator with index 123. Please replace this with the reference to your validator in all commands.**
|
||||
|
||||
### Withdrawal address
|
||||
The withdrawal address is defined above in the concepts section.
|
||||
|
||||
**In the following examples we will use a withdrawal address of 0x8f…9F. Please replace this with the your withdrawal address in all commands.**
|
||||
|
||||
### Generating credentials change operations
|
||||
Note that if you are carrying out this process offline then you still need to carry out the first and third steps outlined in the "Basic operation" section above. This is to ensure that the offline computer has the correct information to generate the operations, and that the operations are made available to the online computer for broadcasting to the network.
|
||||
|
||||
If using the online and offline process run the commands below on the offline computer, and add the `--offline` flag to the commands below. You will need to copy the resultant `change-operations.json` file to the online computer to broadcast to the network.
|
||||
|
||||
If using the online process run the commands below on the online computer. The operation will be broadcast to the network automatically.
|
||||
|
||||
#### Using a mnemonic and path.
|
||||
A mnemonic is a 24-word phrase from which withdrawal and validator keys are derived using a _path_. Commonly, keys will have been generated using two paths:
|
||||
|
||||
- m/12381/3600/_i_/0 is the path to a withdrawal key, where _i_ starts at 0 for the first validator, 1 for the second validator, _etc._
|
||||
- m/12381/3600/_i_/0/0 is the path to a validator key, where _i_ starts at 0 for the first validator, 1 for the second validator, _etc._
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```
|
||||
ethdo validator credentials set --validator=123 --withdrawal-address=0x8f…9F --private-key=0x3b…9c
|
||||
```
|
||||
|
||||
replacing the parameters with your own values.
|
||||
|
||||
#### 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:
|
||||
|
||||
```
|
||||
ethdo validator credentials set --validator=123 --withdrawal-address=0x8f…9F --account=Wallet/Account --passphrase=secret
|
||||
```
|
||||
|
||||
replacing the parameters with your own values.
|
||||
|
||||
## 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:
|
||||
|
||||
```sh
|
||||
ethdo validator credentials get --validator=123
|
||||
```
|
||||
|
||||
The result should start with the phrase "Ethereum execution address" and display the execution address you chose at the beginning of the process, for example:
|
||||
|
||||
```
|
||||
Ethereum execution address: 0x8f0844Fd51E31ff6Bf5baBe21DCcf7328E19Fd9F
|
||||
```
|
||||
|
||||
If the result starts with the phrase "BLS credentials" then it may be that the operation has yet to be incorporated on the chain, please wait a few minutes and check again. If this continues to be the case please obtain help to understand why the change operation failed to work.
|
||||
BIN
docs/images/credentials-change-offline.png
Normal file
BIN
docs/images/credentials-change-offline.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 152 KiB |
BIN
docs/images/credentials-change-online.png
Normal file
BIN
docs/images/credentials-change-online.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 128 KiB |
923
docs/images/diagrams.svg
Normal file
923
docs/images/diagrams.svg
Normal file
@@ -0,0 +1,923 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generated by graphviz version 2.40.1 (20161225.0304)
|
||||
-->
|
||||
|
||||
<!-- Title: MerkleTree Pages: 1 -->
|
||||
|
||||
<svg
|
||||
width="1200"
|
||||
height="500"
|
||||
version="1.1"
|
||||
id="svg4363"
|
||||
sodipodi:docname="diagrams.svg"
|
||||
inkscape:export-filename="/home/jgm/src/go/wealdtech/ethdo/docs/images/credentials-change-online.png"
|
||||
inkscape:export-xdpi="180"
|
||||
inkscape:export-ydpi="180"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<metadata
|
||||
id="metadata4369">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs4367">
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
id="marker4440"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lend">
|
||||
<path
|
||||
transform="scale(0.8) rotate(180) translate(12.5,0)"
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
id="path4438" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker4436"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lstart">
|
||||
<path
|
||||
transform="scale(0.8) translate(12.5,0)"
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
id="path4434" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="marker4412"
|
||||
style="overflow:visible;"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path4410"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
transform="scale(0.8) rotate(180) translate(12.5,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lstart"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="marker4408"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path4406"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
transform="scale(0.8) translate(12.5,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="marker4318"
|
||||
style="overflow:visible;"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path4316"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
transform="scale(0.8) rotate(180) translate(12.5,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lstart"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="marker4314"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path4312"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
transform="scale(0.8) translate(12.5,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker4120"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lstart">
|
||||
<path
|
||||
transform="scale(0.8) translate(12.5,0)"
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
id="path4118" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
id="marker4016"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lend">
|
||||
<path
|
||||
transform="scale(0.8) rotate(180) translate(12.5,0)"
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
id="path4014" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker4012"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lstart">
|
||||
<path
|
||||
transform="scale(0.8) translate(12.5,0)"
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
id="path4010" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
id="marker3717"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lend">
|
||||
<path
|
||||
transform="scale(0.8) rotate(180) translate(12.5,0)"
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
id="path3715" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker3713"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lstart">
|
||||
<path
|
||||
transform="scale(0.8) translate(12.5,0)"
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
id="path3711" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
id="marker3637"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lend">
|
||||
<path
|
||||
transform="scale(0.8) rotate(180) translate(12.5,0)"
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
id="path3635" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker3633"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lstart">
|
||||
<path
|
||||
transform="scale(0.8) translate(12.5,0)"
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
id="path3631" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="marker3617"
|
||||
style="overflow:visible;"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path3615"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
transform="scale(0.8) rotate(180) translate(12.5,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lstart"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="marker3613"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path3611"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
transform="scale(0.8) translate(12.5,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="marker3607"
|
||||
style="overflow:visible;"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path3605"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
transform="scale(0.8) rotate(180) translate(12.5,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lstart"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="marker3603"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path3601"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
transform="scale(0.8) translate(12.5,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker3453"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lstart">
|
||||
<path
|
||||
transform="scale(0.8) translate(12.5,0)"
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
id="path3451" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
id="marker3353"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lend">
|
||||
<path
|
||||
transform="scale(0.8) rotate(180) translate(12.5,0)"
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
id="path3351" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker3349"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lstart">
|
||||
<path
|
||||
transform="scale(0.8) translate(12.5,0)"
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
id="path3347" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
id="marker3035"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lend">
|
||||
<path
|
||||
transform="scale(0.8) rotate(180) translate(12.5,0)"
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
id="path3033" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker3027"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lstart">
|
||||
<path
|
||||
transform="scale(0.8) translate(12.5,0)"
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
id="path3025" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
id="marker3001"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lend">
|
||||
<path
|
||||
transform="scale(0.8) rotate(180) translate(12.5,0)"
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
id="path2999" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker2997"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lstart">
|
||||
<path
|
||||
transform="scale(0.8) translate(12.5,0)"
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
id="path2995" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mend"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="marker2565"
|
||||
style="overflow:visible;"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path2563"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#d0d0d0;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
transform="scale(0.6) rotate(180) translate(0,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mend"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="marker2555"
|
||||
style="overflow:visible;"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path2553"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#d0d0d0;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
transform="scale(0.6) rotate(180) translate(0,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
id="marker2532"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Mend">
|
||||
<path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#d0d0d0;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
id="path2530" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
id="marker15035"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Mend">
|
||||
<path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#d0d0d0;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
id="path15033" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
id="marker14901"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Mend">
|
||||
<path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#d0d0d0;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
id="path14899" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mend"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="marker12697"
|
||||
style="overflow:visible;"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path12695"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#d0d0d0;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
transform="scale(0.6) rotate(180) translate(0,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
id="marker5569"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Mend">
|
||||
<path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#d0d0d0;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
id="path5567" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
id="marker3116"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lend">
|
||||
<path
|
||||
transform="scale(0.8) rotate(180) translate(12.5,0)"
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
id="path3114" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="marker3102"
|
||||
style="overflow:visible;"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path2842"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#d0d0d0;stroke-width:1pt;stroke-opacity:1;fill:#d0d0d0;fill-opacity:1"
|
||||
transform="scale(0.8) rotate(180) translate(12.5,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lstart"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="Arrow1Lstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path1073"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
transform="scale(0.8) translate(12.5,0)" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1140"
|
||||
id="namedview4365"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.82"
|
||||
inkscape:cx="600"
|
||||
inkscape:cy="249.39024"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer9"
|
||||
units="px"
|
||||
inkscape:pagecheckerboard="0" />
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer7"
|
||||
inkscape:label="Base withdrawal credentials change"
|
||||
style="display:inline">
|
||||
<g
|
||||
id="g3352"
|
||||
transform="translate(330.851,-33.97924)"
|
||||
style="display:inline">
|
||||
<path
|
||||
id="path3344"
|
||||
d="m 542.8631,233.6392 a 48.64,48.64 0 0 0 -48.63984,48.63984 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 48.63984,48.64024 48.64,48.64 0 0 0 48.63984,-48.64024 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -48.63984,-48.63984 z"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.19;stroke-linecap:square;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<g
|
||||
id="g3350"
|
||||
transform="translate(117.34562,2.27034)">
|
||||
<path
|
||||
id="path3346"
|
||||
d="m 425.51748,231.16886 a 48.64,48.64 0 0 0 -48.63984,48.63984 48.64,48.64 0 0 0 48.63984,48.64024 48.64,48.64 0 0 0 48.63984,-48.64024 48.64,48.64 0 0 0 -48.63984,-48.63984 z m -26.54023,24.08008 h 3.35625 c 1.14095,0 1.86236,1.20829 1.32656,2.21719 -1.57129,2.9583 -4.71309,11.01741 0.016,19.88281 0.53769,1.00985 -0.1835,2.21992 -1.3254,2.21992 h -3.30976 c -0.58805,0 -1.13899,-0.33312 -1.38789,-0.86797 -2.23915,-4.80035 -2.53555,-8.94055 -2.53555,-11.2918 0,-4.02325 0.89283,-7.83393 2.45938,-11.27578 0.24699,-0.5434 0.80474,-0.88437 1.40039,-0.88437 z m 49.71171,0 h 3.36485 c 0.59565,0 1.15222,0.34036 1.39922,0.88281 1.5694,3.44185 2.46445,7.25314 2.46445,11.27734 0,4.0242 -0.89567,7.8355 -2.46602,11.27735 -0.247,0.54245 -0.80395,0.88242 -1.3996,0.88242 h -3.34688 c -1.15045,0 -1.86488,-1.21977 -1.32148,-2.23438 4.5733,-8.53194 1.70908,-16.61901 -0.0266,-19.87656 -0.5358,-1.00605 0.19393,-2.20898 1.33203,-2.20898 z m -39.23203,6.08008 h 3.2043 c 1.04595,0 1.76892,1.02886 1.41172,2.01211 -0.46835,1.2901 -0.71719,2.65816 -0.71719,4.06796 0,1.4098 0.24884,2.77787 0.71719,4.06797 0.35625,0.98325 -0.36577,2.01211 -1.41172,2.01211 h -3.2043 c -0.66595,0 -1.27898,-0.42767 -1.46328,-1.06797 -0.4655,-1.61215 -0.71601,-3.29071 -0.71601,-5.01211 0,-1.7214 0.25068,-3.40018 0.71523,-5.01328 0.18525,-0.6403 0.79811,-1.06679 1.46406,-1.06679 z m 16.06055,0 c 3.35635,0 6.07617,2.72182 6.07617,6.08007 0,1.27395 -0.39431,2.4537 -1.06406,3.43125 l 12.39766,29.77383 c 0.32205,0.77425 -0.0437,1.6645 -0.81797,1.9875 l -2.80352,1.16953 c -0.77425,0.323 -1.66333,-0.0428 -1.98633,-0.81796 l -4.67422,-11.22422 h -14.25664 l -4.67304,11.22422 c -0.32205,0.77424 -1.21208,1.14096 -1.98633,0.81796 l -2.80352,-1.16953 c -0.77425,-0.323 -1.14097,-1.21325 -0.81797,-1.9875 l 12.39766,-29.77383 c -0.66975,-0.97755 -1.06406,-2.1573 -1.06406,-3.43125 0,-3.35825 2.72077,-6.08007 6.07617,-6.08007 z m 12.85625,0 h 3.2043 c 0.66595,0 1.27898,0.42671 1.46328,1.06796 0.4655,1.61215 0.71601,3.29071 0.71601,5.01211 0,1.7214 -0.24973,3.39996 -0.71523,5.01211 -0.18525,0.64125 -0.79716,1.06797 -1.46406,1.06797 h -3.2043 c -1.04595,0 -1.76892,-1.02886 -1.41172,-2.01211 0.46835,-1.2901 0.71719,-2.65817 0.71719,-4.06797 0,-1.4098 -0.24884,-2.77786 -0.71719,-4.06796 -0.35625,-0.98325 0.36577,-2.01211 1.41172,-2.01211 z m -12.85625,13.28203 -4.59609,11.03789 h 9.19218 z"
|
||||
style="fill:#d0d0d0;fill-opacity:1;stroke:none;stroke-width:0.19;stroke-linecap:square;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<path
|
||||
d="m 409.45678,275.48901 h 3.20435 c 1.04595,0 1.76795,-1.02885 1.4117,-2.0121 -0.46835,-1.2901 -0.71725,-2.6581 -0.71725,-4.0679 0,-1.4098 0.2489,-2.7778 0.71725,-4.0679 0.3572,-0.98325 -0.36575,-2.0121 -1.4117,-2.0121 h -3.20435 c -0.66595,0 -1.2787,0.42655 -1.46395,1.06685 -0.46455,1.6131 -0.71535,3.29175 -0.71535,5.01315 0,1.7214 0.2508,3.40005 0.7163,5.0122 0.1843,0.6403 0.79705,1.0678 1.463,1.0678 z m -5.7969,-16.0227 c 0.5358,-1.0089 -0.18525,-2.2173 -1.3262,-2.2173 h -3.35635 c -0.59565,0 -1.1533,0.34105 -1.4003,0.88445 -1.56655,3.44185 -2.45955,7.2523 -2.45955,11.27555 0,2.35125 0.2964,6.49135 2.53555,11.2917 0.2489,0.53485 0.7999,0.8683 1.38795,0.8683 h 3.3098 c 1.1419,0 1.86295,-1.2103 1.32525,-2.22015 -4.7291,-8.8654 -1.58745,-16.92425 -0.0162,-19.88255 z m 49.7933,-1.33475 c -0.247,-0.54245 -0.8037,-0.88255 -1.39935,-0.88255 h -3.3649 c -1.1381,0 -1.8677,1.2027 -1.3319,2.20875 1.73565,3.25755 4.5999,11.3449 0.0266,19.87685 -0.5434,1.0146 0.171,2.2344 1.32145,2.2344 h 3.34685 c 0.59565,0 1.15235,-0.3401 1.39935,-0.88255 1.57035,-3.44185 2.4662,-7.25325 2.4662,-11.27745 0,-4.0242 -0.8949,-7.8356 -2.4643,-11.27745 z m -11.875,5.19745 h -3.20435 c -1.04595,0 -1.76795,1.02885 -1.4117,2.0121 0.46835,1.2901 0.71725,2.6581 0.71725,4.0679 0,1.4098 -0.2489,2.7778 -0.71725,4.0679 -0.3572,0.98325 0.36575,2.0121 1.4117,2.0121 h 3.20435 c 0.6669,0 1.2787,-0.42655 1.46395,-1.0678 0.4655,-1.61215 0.71535,-3.2908 0.71535,-5.0122 0,-1.7214 -0.2508,-3.40005 -0.7163,-5.0122 -0.1843,-0.64125 -0.79705,-1.0678 -1.463,-1.0678 z m -11.0485,9.5114 c 0.66975,-0.97755 1.064,-2.15745 1.064,-3.4314 0,-3.35825 -2.71985,-6.08 -6.0762,-6.08 -3.3554,0 -6.0762,2.72175 -6.0762,6.08 0,1.27395 0.39425,2.45385 1.064,3.4314 l -12.3975,29.77395 c -0.323,0.77425 0.0437,1.6644 0.81795,1.9874 l 2.80345,1.16945 c 0.77425,0.323 1.6644,-0.0437 1.98645,-0.81795 l 4.67305,-11.22425 h 14.25665 l 4.674,11.22425 c 0.323,0.7752 1.2122,1.14095 1.98645,0.81795 l 2.80345,-1.16945 c 0.77425,-0.323 1.14,-1.21315 0.81795,-1.9874 z m -9.6083,14.8086 4.5961,-11.03805 4.5961,11.03805 z"
|
||||
id="path3348"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b0b0b0;fill-opacity:1;stroke-width:0.095" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g10761"
|
||||
transform="matrix(0.19161542,0,0,0.19161542,-2.1459431,201.48294)"
|
||||
style="display:inline">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:0.95"
|
||||
d="m 224,2 c -67.165,0 -121.59961,54.434608 -121.59961,121.59961 0,0.33436 0.009,0.66628 0.0117,1 -0.003,0.33372 -0.0117,0.66564 -0.0117,1 0,0.33436 0.009,0.66628 0.0117,1 -0.003,0.33372 -0.0117,0.66564 -0.0117,1 0,0.33436 0.009,0.66628 0.0117,1 -0.003,0.33372 -0.0117,0.66564 -0.0117,1 0,0.33436 0.009,0.66628 0.0117,1 -0.003,0.33372 -0.0117,0.66564 -0.0117,1 0,0.33436 0.009,0.66628 0.0117,1 -0.003,0.33372 -0.0117,0.66564 -0.0117,1 0,0.33436 0.009,0.66628 0.0117,1 -0.003,0.33372 -0.0117,0.66564 -0.0117,1 0,0.33436 0.009,0.66628 0.0117,1 -0.003,0.33372 -0.0117,0.66564 -0.0117,1 0,0.33436 0.009,0.66628 0.0117,1 -0.003,0.33372 -0.0117,0.66564 -0.0117,1 0,0.33436 0.009,0.66628 0.0117,1 -0.003,0.33372 -0.0117,0.66564 -0.0117,1 0,67.165 54.43461,121.59961 121.59961,121.59961 67.165,0 121.59961,-54.43461 121.59961,-121.59961 0,-0.33436 -0.009,-0.66628 -0.0117,-1 0.003,-0.33372 0.0117,-0.66564 0.0117,-1 0,-0.33436 -0.009,-0.66628 -0.0117,-1 0.003,-0.33372 0.0117,-0.66564 0.0117,-1 0,-0.33436 -0.009,-0.66628 -0.0117,-1 0.003,-0.33372 0.0117,-0.66564 0.0117,-1 0,-0.33436 -0.009,-0.66628 -0.0117,-1 0.003,-0.33372 0.0117,-0.66564 0.0117,-1 0,-0.33436 -0.009,-0.66628 -0.0117,-1 0.003,-0.33372 0.0117,-0.66564 0.0117,-1 0,-0.33436 -0.009,-0.66628 -0.0117,-1 0.003,-0.33372 0.0117,-0.66564 0.0117,-1 0,-0.33436 -0.009,-0.66628 -0.0117,-1 0.003,-0.33372 0.0117,-0.66564 0.0117,-1 0,-0.33436 -0.009,-0.66628 -0.0117,-1 0.003,-0.33372 0.0117,-0.66564 0.0117,-1 0,-0.33436 -0.009,-0.66628 -0.0117,-1 0.003,-0.33372 0.0117,-0.66564 0.0117,-1 C 345.59961,56.434608 291.165,2 224,2 Z M 132.99023,276.16992 C 65.255238,279.39992 11.199219,334.7843 11.199219,403.2793 v 18 21.52148 2 2 2 2 2 2 2 2 2 c 0,25.175 20.426562,45.59961 45.601562,45.59961 H 391.19922 c 25.175,0 45.60156,-20.42461 45.60156,-45.59961 v -2 -2 -2 -2 -2 -2 -2 -2 -2 -21.52148 -18 c 0,-68.495 -54.05601,-123.87938 -121.79101,-127.10938 L 269.59961,458 243.5,347.06641 h -39 L 178.40039,458 Z"
|
||||
id="path7306" />
|
||||
<path
|
||||
d="m 224,243.2 c 67.165,0 121.6,-54.435 121.6,-121.6 C 345.6,54.435 291.165,0 224,0 156.835,0 102.4,54.435 102.4,121.6 c 0,67.165 54.435,121.6 121.6,121.6 z M 315.01,274.17 269.6,456 239.2,326.8 269.6,273.6 H 178.4 L 208.8,326.8 178.4,456 132.99,274.17 C 65.255,277.4 11.2,332.785 11.2,401.28 v 39.52 c 0,25.175 20.425,45.6 45.6,45.6 h 334.4 c 25.175,0 45.6,-20.425 45.6,-45.6 v -39.52 c 0,-68.495 -54.055,-123.88 -121.79,-127.11 z"
|
||||
id="path1534"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#d0d0d0;fill-opacity:1;stroke-width:0.95" />
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:square;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="m 224.11719,272.89258 c -30.84856,-1e-5 -61.38197,0.44231 -91.30664,1.25 v 0.0391 c 0.0601,-0.003 0.11956,-0.009 0.17968,-0.0117 L 178.40039,456 208.80078,326.80078 178.40039,273.59961 h 91.19922 L 239.19922,326.80078 269.59961,456 315.00977,274.16992 c 0.16788,0.008 0.33423,0.0206 0.50195,0.0293 v -0.0547 c -29.94323,-0.80978 -60.50517,-1.25196 -91.39453,-1.25195 z"
|
||||
id="path10733"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
id="g4284"
|
||||
transform="translate(608.49706,-233.4392)"
|
||||
style="display:inline">
|
||||
<path
|
||||
id="path4276"
|
||||
d="m 542.8631,233.6392 a 48.64,48.64 0 0 0 -48.63984,48.63984 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 48.63984,48.64024 48.64,48.64 0 0 0 48.63984,-48.64024 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -48.63984,-48.63984 z"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.19;stroke-linecap:square;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<g
|
||||
id="g4282"
|
||||
transform="translate(117.34562,2.27034)">
|
||||
<path
|
||||
id="path4278"
|
||||
d="m 425.51748,231.16886 a 48.64,48.64 0 0 0 -48.63984,48.63984 48.64,48.64 0 0 0 48.63984,48.64024 48.64,48.64 0 0 0 48.63984,-48.64024 48.64,48.64 0 0 0 -48.63984,-48.63984 z m -26.54023,24.08008 h 3.35625 c 1.14095,0 1.86236,1.20829 1.32656,2.21719 -1.57129,2.9583 -4.71309,11.01741 0.016,19.88281 0.53769,1.00985 -0.1835,2.21992 -1.3254,2.21992 h -3.30976 c -0.58805,0 -1.13899,-0.33312 -1.38789,-0.86797 -2.23915,-4.80035 -2.53555,-8.94055 -2.53555,-11.2918 0,-4.02325 0.89283,-7.83393 2.45938,-11.27578 0.24699,-0.5434 0.80474,-0.88437 1.40039,-0.88437 z m 49.71171,0 h 3.36485 c 0.59565,0 1.15222,0.34036 1.39922,0.88281 1.5694,3.44185 2.46445,7.25314 2.46445,11.27734 0,4.0242 -0.89567,7.8355 -2.46602,11.27735 -0.247,0.54245 -0.80395,0.88242 -1.3996,0.88242 h -3.34688 c -1.15045,0 -1.86488,-1.21977 -1.32148,-2.23438 4.5733,-8.53194 1.70908,-16.61901 -0.0266,-19.87656 -0.5358,-1.00605 0.19393,-2.20898 1.33203,-2.20898 z m -39.23203,6.08008 h 3.2043 c 1.04595,0 1.76892,1.02886 1.41172,2.01211 -0.46835,1.2901 -0.71719,2.65816 -0.71719,4.06796 0,1.4098 0.24884,2.77787 0.71719,4.06797 0.35625,0.98325 -0.36577,2.01211 -1.41172,2.01211 h -3.2043 c -0.66595,0 -1.27898,-0.42767 -1.46328,-1.06797 -0.4655,-1.61215 -0.71601,-3.29071 -0.71601,-5.01211 0,-1.7214 0.25068,-3.40018 0.71523,-5.01328 0.18525,-0.6403 0.79811,-1.06679 1.46406,-1.06679 z m 16.06055,0 c 3.35635,0 6.07617,2.72182 6.07617,6.08007 0,1.27395 -0.39431,2.4537 -1.06406,3.43125 l 12.39766,29.77383 c 0.32205,0.77425 -0.0437,1.6645 -0.81797,1.9875 l -2.80352,1.16953 c -0.77425,0.323 -1.66333,-0.0428 -1.98633,-0.81796 l -4.67422,-11.22422 h -14.25664 l -4.67304,11.22422 c -0.32205,0.77424 -1.21208,1.14096 -1.98633,0.81796 l -2.80352,-1.16953 c -0.77425,-0.323 -1.14097,-1.21325 -0.81797,-1.9875 l 12.39766,-29.77383 c -0.66975,-0.97755 -1.06406,-2.1573 -1.06406,-3.43125 0,-3.35825 2.72077,-6.08007 6.07617,-6.08007 z m 12.85625,0 h 3.2043 c 0.66595,0 1.27898,0.42671 1.46328,1.06796 0.4655,1.61215 0.71601,3.29071 0.71601,5.01211 0,1.7214 -0.24973,3.39996 -0.71523,5.01211 -0.18525,0.64125 -0.79716,1.06797 -1.46406,1.06797 h -3.2043 c -1.04595,0 -1.76892,-1.02886 -1.41172,-2.01211 0.46835,-1.2901 0.71719,-2.65817 0.71719,-4.06797 0,-1.4098 -0.24884,-2.77786 -0.71719,-4.06796 -0.35625,-0.98325 0.36577,-2.01211 1.41172,-2.01211 z m -12.85625,13.28203 -4.59609,11.03789 h 9.19218 z"
|
||||
style="fill:#d0d0d0;fill-opacity:1;stroke:none;stroke-width:0.19;stroke-linecap:square;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<path
|
||||
d="m 409.45678,275.48901 h 3.20435 c 1.04595,0 1.76795,-1.02885 1.4117,-2.0121 -0.46835,-1.2901 -0.71725,-2.6581 -0.71725,-4.0679 0,-1.4098 0.2489,-2.7778 0.71725,-4.0679 0.3572,-0.98325 -0.36575,-2.0121 -1.4117,-2.0121 h -3.20435 c -0.66595,0 -1.2787,0.42655 -1.46395,1.06685 -0.46455,1.6131 -0.71535,3.29175 -0.71535,5.01315 0,1.7214 0.2508,3.40005 0.7163,5.0122 0.1843,0.6403 0.79705,1.0678 1.463,1.0678 z m -5.7969,-16.0227 c 0.5358,-1.0089 -0.18525,-2.2173 -1.3262,-2.2173 h -3.35635 c -0.59565,0 -1.1533,0.34105 -1.4003,0.88445 -1.56655,3.44185 -2.45955,7.2523 -2.45955,11.27555 0,2.35125 0.2964,6.49135 2.53555,11.2917 0.2489,0.53485 0.7999,0.8683 1.38795,0.8683 h 3.3098 c 1.1419,0 1.86295,-1.2103 1.32525,-2.22015 -4.7291,-8.8654 -1.58745,-16.92425 -0.0162,-19.88255 z m 49.7933,-1.33475 c -0.247,-0.54245 -0.8037,-0.88255 -1.39935,-0.88255 h -3.3649 c -1.1381,0 -1.8677,1.2027 -1.3319,2.20875 1.73565,3.25755 4.5999,11.3449 0.0266,19.87685 -0.5434,1.0146 0.171,2.2344 1.32145,2.2344 h 3.34685 c 0.59565,0 1.15235,-0.3401 1.39935,-0.88255 1.57035,-3.44185 2.4662,-7.25325 2.4662,-11.27745 0,-4.0242 -0.8949,-7.8356 -2.4643,-11.27745 z m -11.875,5.19745 h -3.20435 c -1.04595,0 -1.76795,1.02885 -1.4117,2.0121 0.46835,1.2901 0.71725,2.6581 0.71725,4.0679 0,1.4098 -0.2489,2.7778 -0.71725,4.0679 -0.3572,0.98325 0.36575,2.0121 1.4117,2.0121 h 3.20435 c 0.6669,0 1.2787,-0.42655 1.46395,-1.0678 0.4655,-1.61215 0.71535,-3.2908 0.71535,-5.0122 0,-1.7214 -0.2508,-3.40005 -0.7163,-5.0122 -0.1843,-0.64125 -0.79705,-1.0678 -1.463,-1.0678 z m -11.0485,9.5114 c 0.66975,-0.97755 1.064,-2.15745 1.064,-3.4314 0,-3.35825 -2.71985,-6.08 -6.0762,-6.08 -3.3554,0 -6.0762,2.72175 -6.0762,6.08 0,1.27395 0.39425,2.45385 1.064,3.4314 l -12.3975,29.77395 c -0.323,0.77425 0.0437,1.6644 0.81795,1.9874 l 2.80345,1.16945 c 0.77425,0.323 1.6644,-0.0437 1.98645,-0.81795 l 4.67305,-11.22425 h 14.25665 l 4.674,11.22425 c 0.323,0.7752 1.2122,1.14095 1.98645,0.81795 l 2.80345,-1.16945 c 0.77425,-0.323 1.14,-1.21315 0.81795,-1.9874 z m -9.6083,14.8086 4.5961,-11.03805 4.5961,11.03805 z"
|
||||
id="path4280"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b0b0b0;fill-opacity:1;stroke-width:0.095" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g4294"
|
||||
transform="translate(608.49706,-33.97924)"
|
||||
style="display:inline">
|
||||
<path
|
||||
id="path4286"
|
||||
d="m 542.8631,233.6392 a 48.64,48.64 0 0 0 -48.63984,48.63984 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 48.63984,48.64024 48.64,48.64 0 0 0 48.63984,-48.64024 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -48.63984,-48.63984 z"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.19;stroke-linecap:square;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<g
|
||||
id="g4292"
|
||||
transform="translate(117.34562,2.27034)">
|
||||
<path
|
||||
id="path4288"
|
||||
d="m 425.51748,231.16886 a 48.64,48.64 0 0 0 -48.63984,48.63984 48.64,48.64 0 0 0 48.63984,48.64024 48.64,48.64 0 0 0 48.63984,-48.64024 48.64,48.64 0 0 0 -48.63984,-48.63984 z m -26.54023,24.08008 h 3.35625 c 1.14095,0 1.86236,1.20829 1.32656,2.21719 -1.57129,2.9583 -4.71309,11.01741 0.016,19.88281 0.53769,1.00985 -0.1835,2.21992 -1.3254,2.21992 h -3.30976 c -0.58805,0 -1.13899,-0.33312 -1.38789,-0.86797 -2.23915,-4.80035 -2.53555,-8.94055 -2.53555,-11.2918 0,-4.02325 0.89283,-7.83393 2.45938,-11.27578 0.24699,-0.5434 0.80474,-0.88437 1.40039,-0.88437 z m 49.71171,0 h 3.36485 c 0.59565,0 1.15222,0.34036 1.39922,0.88281 1.5694,3.44185 2.46445,7.25314 2.46445,11.27734 0,4.0242 -0.89567,7.8355 -2.46602,11.27735 -0.247,0.54245 -0.80395,0.88242 -1.3996,0.88242 h -3.34688 c -1.15045,0 -1.86488,-1.21977 -1.32148,-2.23438 4.5733,-8.53194 1.70908,-16.61901 -0.0266,-19.87656 -0.5358,-1.00605 0.19393,-2.20898 1.33203,-2.20898 z m -39.23203,6.08008 h 3.2043 c 1.04595,0 1.76892,1.02886 1.41172,2.01211 -0.46835,1.2901 -0.71719,2.65816 -0.71719,4.06796 0,1.4098 0.24884,2.77787 0.71719,4.06797 0.35625,0.98325 -0.36577,2.01211 -1.41172,2.01211 h -3.2043 c -0.66595,0 -1.27898,-0.42767 -1.46328,-1.06797 -0.4655,-1.61215 -0.71601,-3.29071 -0.71601,-5.01211 0,-1.7214 0.25068,-3.40018 0.71523,-5.01328 0.18525,-0.6403 0.79811,-1.06679 1.46406,-1.06679 z m 16.06055,0 c 3.35635,0 6.07617,2.72182 6.07617,6.08007 0,1.27395 -0.39431,2.4537 -1.06406,3.43125 l 12.39766,29.77383 c 0.32205,0.77425 -0.0437,1.6645 -0.81797,1.9875 l -2.80352,1.16953 c -0.77425,0.323 -1.66333,-0.0428 -1.98633,-0.81796 l -4.67422,-11.22422 h -14.25664 l -4.67304,11.22422 c -0.32205,0.77424 -1.21208,1.14096 -1.98633,0.81796 l -2.80352,-1.16953 c -0.77425,-0.323 -1.14097,-1.21325 -0.81797,-1.9875 l 12.39766,-29.77383 c -0.66975,-0.97755 -1.06406,-2.1573 -1.06406,-3.43125 0,-3.35825 2.72077,-6.08007 6.07617,-6.08007 z m 12.85625,0 h 3.2043 c 0.66595,0 1.27898,0.42671 1.46328,1.06796 0.4655,1.61215 0.71601,3.29071 0.71601,5.01211 0,1.7214 -0.24973,3.39996 -0.71523,5.01211 -0.18525,0.64125 -0.79716,1.06797 -1.46406,1.06797 h -3.2043 c -1.04595,0 -1.76892,-1.02886 -1.41172,-2.01211 0.46835,-1.2901 0.71719,-2.65817 0.71719,-4.06797 0,-1.4098 -0.24884,-2.77786 -0.71719,-4.06796 -0.35625,-0.98325 0.36577,-2.01211 1.41172,-2.01211 z m -12.85625,13.28203 -4.59609,11.03789 h 9.19218 z"
|
||||
style="fill:#d0d0d0;fill-opacity:1;stroke:none;stroke-width:0.19;stroke-linecap:square;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<path
|
||||
d="m 409.45678,275.48901 h 3.20435 c 1.04595,0 1.76795,-1.02885 1.4117,-2.0121 -0.46835,-1.2901 -0.71725,-2.6581 -0.71725,-4.0679 0,-1.4098 0.2489,-2.7778 0.71725,-4.0679 0.3572,-0.98325 -0.36575,-2.0121 -1.4117,-2.0121 h -3.20435 c -0.66595,0 -1.2787,0.42655 -1.46395,1.06685 -0.46455,1.6131 -0.71535,3.29175 -0.71535,5.01315 0,1.7214 0.2508,3.40005 0.7163,5.0122 0.1843,0.6403 0.79705,1.0678 1.463,1.0678 z m -5.7969,-16.0227 c 0.5358,-1.0089 -0.18525,-2.2173 -1.3262,-2.2173 h -3.35635 c -0.59565,0 -1.1533,0.34105 -1.4003,0.88445 -1.56655,3.44185 -2.45955,7.2523 -2.45955,11.27555 0,2.35125 0.2964,6.49135 2.53555,11.2917 0.2489,0.53485 0.7999,0.8683 1.38795,0.8683 h 3.3098 c 1.1419,0 1.86295,-1.2103 1.32525,-2.22015 -4.7291,-8.8654 -1.58745,-16.92425 -0.0162,-19.88255 z m 49.7933,-1.33475 c -0.247,-0.54245 -0.8037,-0.88255 -1.39935,-0.88255 h -3.3649 c -1.1381,0 -1.8677,1.2027 -1.3319,2.20875 1.73565,3.25755 4.5999,11.3449 0.0266,19.87685 -0.5434,1.0146 0.171,2.2344 1.32145,2.2344 h 3.34685 c 0.59565,0 1.15235,-0.3401 1.39935,-0.88255 1.57035,-3.44185 2.4662,-7.25325 2.4662,-11.27745 0,-4.0242 -0.8949,-7.8356 -2.4643,-11.27745 z m -11.875,5.19745 h -3.20435 c -1.04595,0 -1.76795,1.02885 -1.4117,2.0121 0.46835,1.2901 0.71725,2.6581 0.71725,4.0679 0,1.4098 -0.2489,2.7778 -0.71725,4.0679 -0.3572,0.98325 0.36575,2.0121 1.4117,2.0121 h 3.20435 c 0.6669,0 1.2787,-0.42655 1.46395,-1.0678 0.4655,-1.61215 0.71535,-3.2908 0.71535,-5.0122 0,-1.7214 -0.2508,-3.40005 -0.7163,-5.0122 -0.1843,-0.64125 -0.79705,-1.0678 -1.463,-1.0678 z m -11.0485,9.5114 c 0.66975,-0.97755 1.064,-2.15745 1.064,-3.4314 0,-3.35825 -2.71985,-6.08 -6.0762,-6.08 -3.3554,0 -6.0762,2.72175 -6.0762,6.08 0,1.27395 0.39425,2.45385 1.064,3.4314 l -12.3975,29.77395 c -0.323,0.77425 0.0437,1.6644 0.81795,1.9874 l 2.80345,1.16945 c 0.77425,0.323 1.6644,-0.0437 1.98645,-0.81795 l 4.67305,-11.22425 h 14.25665 l 4.674,11.22425 c 0.323,0.7752 1.2122,1.14095 1.98645,0.81795 l 2.80345,-1.16945 c 0.77425,-0.323 1.14,-1.21315 0.81795,-1.9874 z m -9.6083,14.8086 4.5961,-11.03805 4.5961,11.03805 z"
|
||||
id="path4290"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b0b0b0;fill-opacity:1;stroke-width:0.095" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g4304"
|
||||
transform="translate(608.49706,165.48072)"
|
||||
style="display:inline">
|
||||
<path
|
||||
id="path4296"
|
||||
d="m 542.8631,233.6392 a 48.64,48.64 0 0 0 -48.63984,48.63984 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 48.63984,48.64024 48.64,48.64 0 0 0 48.63984,-48.64024 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -48.63984,-48.63984 z"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.19;stroke-linecap:square;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<g
|
||||
id="g4302"
|
||||
transform="translate(117.34562,2.27034)">
|
||||
<path
|
||||
id="path4298"
|
||||
d="m 425.51748,231.16886 a 48.64,48.64 0 0 0 -48.63984,48.63984 48.64,48.64 0 0 0 48.63984,48.64024 48.64,48.64 0 0 0 48.63984,-48.64024 48.64,48.64 0 0 0 -48.63984,-48.63984 z m -26.54023,24.08008 h 3.35625 c 1.14095,0 1.86236,1.20829 1.32656,2.21719 -1.57129,2.9583 -4.71309,11.01741 0.016,19.88281 0.53769,1.00985 -0.1835,2.21992 -1.3254,2.21992 h -3.30976 c -0.58805,0 -1.13899,-0.33312 -1.38789,-0.86797 -2.23915,-4.80035 -2.53555,-8.94055 -2.53555,-11.2918 0,-4.02325 0.89283,-7.83393 2.45938,-11.27578 0.24699,-0.5434 0.80474,-0.88437 1.40039,-0.88437 z m 49.71171,0 h 3.36485 c 0.59565,0 1.15222,0.34036 1.39922,0.88281 1.5694,3.44185 2.46445,7.25314 2.46445,11.27734 0,4.0242 -0.89567,7.8355 -2.46602,11.27735 -0.247,0.54245 -0.80395,0.88242 -1.3996,0.88242 h -3.34688 c -1.15045,0 -1.86488,-1.21977 -1.32148,-2.23438 4.5733,-8.53194 1.70908,-16.61901 -0.0266,-19.87656 -0.5358,-1.00605 0.19393,-2.20898 1.33203,-2.20898 z m -39.23203,6.08008 h 3.2043 c 1.04595,0 1.76892,1.02886 1.41172,2.01211 -0.46835,1.2901 -0.71719,2.65816 -0.71719,4.06796 0,1.4098 0.24884,2.77787 0.71719,4.06797 0.35625,0.98325 -0.36577,2.01211 -1.41172,2.01211 h -3.2043 c -0.66595,0 -1.27898,-0.42767 -1.46328,-1.06797 -0.4655,-1.61215 -0.71601,-3.29071 -0.71601,-5.01211 0,-1.7214 0.25068,-3.40018 0.71523,-5.01328 0.18525,-0.6403 0.79811,-1.06679 1.46406,-1.06679 z m 16.06055,0 c 3.35635,0 6.07617,2.72182 6.07617,6.08007 0,1.27395 -0.39431,2.4537 -1.06406,3.43125 l 12.39766,29.77383 c 0.32205,0.77425 -0.0437,1.6645 -0.81797,1.9875 l -2.80352,1.16953 c -0.77425,0.323 -1.66333,-0.0428 -1.98633,-0.81796 l -4.67422,-11.22422 h -14.25664 l -4.67304,11.22422 c -0.32205,0.77424 -1.21208,1.14096 -1.98633,0.81796 l -2.80352,-1.16953 c -0.77425,-0.323 -1.14097,-1.21325 -0.81797,-1.9875 l 12.39766,-29.77383 c -0.66975,-0.97755 -1.06406,-2.1573 -1.06406,-3.43125 0,-3.35825 2.72077,-6.08007 6.07617,-6.08007 z m 12.85625,0 h 3.2043 c 0.66595,0 1.27898,0.42671 1.46328,1.06796 0.4655,1.61215 0.71601,3.29071 0.71601,5.01211 0,1.7214 -0.24973,3.39996 -0.71523,5.01211 -0.18525,0.64125 -0.79716,1.06797 -1.46406,1.06797 h -3.2043 c -1.04595,0 -1.76892,-1.02886 -1.41172,-2.01211 0.46835,-1.2901 0.71719,-2.65817 0.71719,-4.06797 0,-1.4098 -0.24884,-2.77786 -0.71719,-4.06796 -0.35625,-0.98325 0.36577,-2.01211 1.41172,-2.01211 z m -12.85625,13.28203 -4.59609,11.03789 h 9.19218 z"
|
||||
style="fill:#d0d0d0;fill-opacity:1;stroke:none;stroke-width:0.19;stroke-linecap:square;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<path
|
||||
d="m 409.45678,275.48901 h 3.20435 c 1.04595,0 1.76795,-1.02885 1.4117,-2.0121 -0.46835,-1.2901 -0.71725,-2.6581 -0.71725,-4.0679 0,-1.4098 0.2489,-2.7778 0.71725,-4.0679 0.3572,-0.98325 -0.36575,-2.0121 -1.4117,-2.0121 h -3.20435 c -0.66595,0 -1.2787,0.42655 -1.46395,1.06685 -0.46455,1.6131 -0.71535,3.29175 -0.71535,5.01315 0,1.7214 0.2508,3.40005 0.7163,5.0122 0.1843,0.6403 0.79705,1.0678 1.463,1.0678 z m -5.7969,-16.0227 c 0.5358,-1.0089 -0.18525,-2.2173 -1.3262,-2.2173 h -3.35635 c -0.59565,0 -1.1533,0.34105 -1.4003,0.88445 -1.56655,3.44185 -2.45955,7.2523 -2.45955,11.27555 0,2.35125 0.2964,6.49135 2.53555,11.2917 0.2489,0.53485 0.7999,0.8683 1.38795,0.8683 h 3.3098 c 1.1419,0 1.86295,-1.2103 1.32525,-2.22015 -4.7291,-8.8654 -1.58745,-16.92425 -0.0162,-19.88255 z m 49.7933,-1.33475 c -0.247,-0.54245 -0.8037,-0.88255 -1.39935,-0.88255 h -3.3649 c -1.1381,0 -1.8677,1.2027 -1.3319,2.20875 1.73565,3.25755 4.5999,11.3449 0.0266,19.87685 -0.5434,1.0146 0.171,2.2344 1.32145,2.2344 h 3.34685 c 0.59565,0 1.15235,-0.3401 1.39935,-0.88255 1.57035,-3.44185 2.4662,-7.25325 2.4662,-11.27745 0,-4.0242 -0.8949,-7.8356 -2.4643,-11.27745 z m -11.875,5.19745 h -3.20435 c -1.04595,0 -1.76795,1.02885 -1.4117,2.0121 0.46835,1.2901 0.71725,2.6581 0.71725,4.0679 0,1.4098 -0.2489,2.7778 -0.71725,4.0679 -0.3572,0.98325 0.36575,2.0121 1.4117,2.0121 h 3.20435 c 0.6669,0 1.2787,-0.42655 1.46395,-1.0678 0.4655,-1.61215 0.71535,-3.2908 0.71535,-5.0122 0,-1.7214 -0.2508,-3.40005 -0.7163,-5.0122 -0.1843,-0.64125 -0.79705,-1.0678 -1.463,-1.0678 z m -11.0485,9.5114 c 0.66975,-0.97755 1.064,-2.15745 1.064,-3.4314 0,-3.35825 -2.71985,-6.08 -6.0762,-6.08 -3.3554,0 -6.0762,2.72175 -6.0762,6.08 0,1.27395 0.39425,2.45385 1.064,3.4314 l -12.3975,29.77395 c -0.323,0.77425 0.0437,1.6644 0.81795,1.9874 l 2.80345,1.16945 c 0.77425,0.323 1.6644,-0.0437 1.98645,-0.81795 l 4.67305,-11.22425 h 14.25665 l 4.674,11.22425 c 0.323,0.7752 1.2122,1.14095 1.98645,0.81795 l 2.80345,-1.16945 c 0.77425,-0.323 1.14,-1.21315 0.81795,-1.9874 z m -9.6083,14.8086 4.5961,-11.03805 4.5961,11.03805 z"
|
||||
id="path4300"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b0b0b0;fill-opacity:1;stroke-width:0.095" />
|
||||
</g>
|
||||
</g>
|
||||
<path
|
||||
style="display:inline;fill:none;stroke:#d0d0d0;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker4440)"
|
||||
d="M 657.63833,252.24403 H 804.3783"
|
||||
id="path6146"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="display:inline;fill:none;stroke:#d0d0d0;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker4440)"
|
||||
d="M 935.28443,252.24403 H 1082.0244"
|
||||
id="path6247"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="display:inline;fill:none;stroke:#d0d0d0;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker4440)"
|
||||
d="M 936.44494,192.24403 1083.1849,97.693151"
|
||||
id="path6249"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="display:inline;fill:none;stroke:#d0d0d0;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker4440)"
|
||||
d="M 936.65042,312.24403 1083.3904,421.52638"
|
||||
id="path6251"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:18.6667px;line-height:1.25;font-family:Lato;-inkscape-font-specification:Lato;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
x="873.41541"
|
||||
y="317.23297"
|
||||
id="text8140"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan8138"
|
||||
x="873.41541"
|
||||
y="317.23297"
|
||||
style="text-align:center;text-anchor:middle;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1">Local</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
x="873.41541"
|
||||
y="340.56635"
|
||||
style="text-align:center;text-anchor:middle;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
id="tspan11236">consensus node</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:18.6667px;line-height:1.25;font-family:Lato;-inkscape-font-specification:Lato;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
x="1151.2808"
|
||||
y="145.29068"
|
||||
id="text11322"><tspan
|
||||
sodipodi:role="line"
|
||||
x="1151.2808"
|
||||
y="145.29068"
|
||||
style="text-align:center;text-anchor:middle;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
id="tspan11320">Consensus</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
x="1151.2808"
|
||||
y="168.62405"
|
||||
style="text-align:center;text-anchor:middle;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
id="tspan12530">network</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:18.6667px;line-height:1.25;font-family:Lato;-inkscape-font-specification:Lato;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
x="1151.2808"
|
||||
y="344.75064"
|
||||
id="text12616"><tspan
|
||||
sodipodi:role="line"
|
||||
x="1151.2808"
|
||||
y="344.75064"
|
||||
style="text-align:center;text-anchor:middle;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
id="tspan12612">Consensus</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
x="1151.2808"
|
||||
y="368.08401"
|
||||
style="text-align:center;text-anchor:middle;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
id="tspan12614">network</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:18.6667px;line-height:1.25;font-family:Lato;-inkscape-font-specification:Lato;display:inline;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
x="40.075897"
|
||||
y="321.42487"
|
||||
id="text12622"><tspan
|
||||
sodipodi:role="line"
|
||||
x="40.075897"
|
||||
y="321.42487"
|
||||
style="text-align:center;text-anchor:middle;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
id="tspan14902">User</tspan></text>
|
||||
<g
|
||||
id="g27545"
|
||||
transform="translate(2.7996094e-5,147.10741)"
|
||||
style="display:inline">
|
||||
<path
|
||||
id="path27529"
|
||||
d="m 596.06796,54.89213 a 48.64,48.64 0 0 0 -48.63984,48.63984 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 48.63984,48.64024 48.64,48.64 0 0 0 48.63984,-48.64024 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -48.63984,-48.63984 z"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.19;stroke-linecap:square;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<circle
|
||||
r="48.639999"
|
||||
cy="103.13213"
|
||||
cx="596.06799"
|
||||
id="circle27531"
|
||||
style="fill:#d0d0d0;fill-opacity:1;stroke:none;stroke-width:0.19;stroke-linecap:square;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<g
|
||||
id="g27537"
|
||||
transform="translate(-165.65975,5.478358)">
|
||||
<path
|
||||
d="m 780.23799,97.443192 a 2.8922269,2.8922269 0 0 0 -3.15252,0.54952 l -14.92389,14.056218 a 0.69413446,0.69413446 0 0 1 -0.98336,0 l -14.8082,-14.027298 a 2.8922269,2.8922269 0 0 0 -4.88787,2.082398 v 13.767 a 2.8922269,2.8922269 0 0 0 0.89659,2.11133 l 17.35337,16.37 a 2.8922269,2.8922269 0 0 0 3.99127,0 l 17.35336,-16.37 a 2.8922269,2.8922269 0 0 0 0.89659,-2.11133 v -13.767 a 2.8922269,2.8922269 0 0 0 -1.73534,-2.660838 z"
|
||||
fill="#000000"
|
||||
id="path27533"
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:2.89223" />
|
||||
<path
|
||||
d="m 780.23799,66.409593 a 2.8922269,2.8922269 0 0 0 -3.15252,0.520601 l -14.92389,14.056222 a 0.72305672,0.72305672 0 0 1 -0.98336,0 l -14.8082,-14.0273 a 2.8922269,2.8922269 0 0 0 -4.88787,2.111325 v 13.767001 a 2.8922269,2.8922269 0 0 0 0.89659,2.082403 l 17.35337,16.370005 a 2.8922269,2.8922269 0 0 0 1.99563,0.80982 2.8922269,2.8922269 0 0 0 1.99564,-0.80982 l 17.35336,-16.370005 a 2.8922269,2.8922269 0 0 0 0.89659,-2.111326 v -13.767 a 2.8922269,2.8922269 0 0 0 -1.73534,-2.631926 z"
|
||||
fill="#000000"
|
||||
id="path27535"
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:2.89223" />
|
||||
</g>
|
||||
<g
|
||||
id="g27543">
|
||||
<path
|
||||
d="m 614.57824,100.92155 a 2.8922269,2.8922269 0 0 0 -3.15252,0.54952 l -14.92389,14.05622 a 0.69413446,0.69413446 0 0 1 -0.98336,0 l -14.8082,-14.0273 a 2.8922269,2.8922269 0 0 0 -4.88787,2.0824 v 13.767 a 2.8922269,2.8922269 0 0 0 0.89659,2.11133 l 17.35337,16.37 a 2.8922269,2.8922269 0 0 0 3.99127,0 l 17.35336,-16.37 a 2.8922269,2.8922269 0 0 0 0.89659,-2.11133 v -13.767 a 2.8922269,2.8922269 0 0 0 -1.73534,-2.66084 z"
|
||||
fill="#000000"
|
||||
id="path27539"
|
||||
style="fill:#b0b0b0;fill-opacity:1;stroke-width:2.89223" />
|
||||
<path
|
||||
d="m 614.57824,69.887949 a 2.8922269,2.8922269 0 0 0 -3.15252,0.520601 l -14.92389,14.056222 a 0.72305672,0.72305672 0 0 1 -0.98336,0 l -14.8082,-14.0273 a 2.8922269,2.8922269 0 0 0 -4.88787,2.111325 v 13.767001 a 2.8922269,2.8922269 0 0 0 0.89659,2.082403 l 17.35337,16.370009 a 2.8922269,2.8922269 0 0 0 1.99563,0.80982 2.8922269,2.8922269 0 0 0 1.99564,-0.80982 l 17.35336,-16.370009 a 2.8922269,2.8922269 0 0 0 0.89659,-2.111326 v -13.767 a 2.8922269,2.8922269 0 0 0 -1.73534,-2.631926 z"
|
||||
fill="#000000"
|
||||
id="path27541"
|
||||
style="fill:#b0b0b0;fill-opacity:1;stroke-width:2.89223" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer9"
|
||||
inkscape:label="Online withdrawal credentials change"
|
||||
style="display:inline">
|
||||
<path
|
||||
style="fill:none;stroke:#d0d0d0;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker4440)"
|
||||
d="M 104.72769,252.24403 H 524.35081"
|
||||
id="path5578" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:18.6667px;line-height:1.25;font-family:Lato;-inkscape-font-specification:Lato;fill:#d0d0d0;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
x="596.06799"
|
||||
y="321.42487"
|
||||
id="text11108"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan11106"
|
||||
x="596.06799"
|
||||
y="321.42487"
|
||||
style="text-align:center;text-anchor:middle;fill:#d0d0d0;fill-opacity:1;stroke:none;stroke-opacity:1">ethdo</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
x="596.06799"
|
||||
y="344.75824"
|
||||
style="text-align:center;text-anchor:middle;fill:#d0d0d0;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
id="tspan11132">(with keys)</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:32px;line-height:1.25;font-family:Lato;-inkscape-font-specification:Lato;display:inline;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
x="599.86401"
|
||||
y="26.809605"
|
||||
id="text27911"><tspan
|
||||
sodipodi:role="line"
|
||||
x="599.86401"
|
||||
y="26.809605"
|
||||
style="font-size:32px;text-align:center;text-anchor:middle;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
id="tspan27909">Online</tspan></text>
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer8"
|
||||
inkscape:label="Offline withdrawal credentials change"
|
||||
style="display:none">
|
||||
<g
|
||||
id="g27447"
|
||||
transform="translate(-277.64603,147.10741)">
|
||||
<path
|
||||
id="path27431"
|
||||
d="m 596.06796,54.89213 a 48.64,48.64 0 0 0 -48.63984,48.63984 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 0.008,0.23438 48.64,48.64 0 0 0 -0.008,0.16562 48.64,48.64 0 0 0 48.63984,48.64024 48.64,48.64 0 0 0 48.63984,-48.64024 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -0.008,-0.23437 48.64,48.64 0 0 0 0.008,-0.16563 48.64,48.64 0 0 0 -48.63984,-48.63984 z"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.19;stroke-linecap:square;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<circle
|
||||
r="48.639999"
|
||||
cy="103.13213"
|
||||
cx="596.06799"
|
||||
id="circle27433"
|
||||
style="fill:#d0d0d0;fill-opacity:1;stroke:none;stroke-width:0.19;stroke-linecap:square;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<g
|
||||
id="g27439"
|
||||
transform="translate(-165.65975,5.478358)">
|
||||
<path
|
||||
d="m 780.23799,97.443192 a 2.8922269,2.8922269 0 0 0 -3.15252,0.54952 l -14.92389,14.056218 a 0.69413446,0.69413446 0 0 1 -0.98336,0 l -14.8082,-14.027298 a 2.8922269,2.8922269 0 0 0 -4.88787,2.082398 v 13.767 a 2.8922269,2.8922269 0 0 0 0.89659,2.11133 l 17.35337,16.37 a 2.8922269,2.8922269 0 0 0 3.99127,0 l 17.35336,-16.37 a 2.8922269,2.8922269 0 0 0 0.89659,-2.11133 v -13.767 a 2.8922269,2.8922269 0 0 0 -1.73534,-2.660838 z"
|
||||
fill="#000000"
|
||||
id="path27435"
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:2.89223" />
|
||||
<path
|
||||
d="m 780.23799,66.409593 a 2.8922269,2.8922269 0 0 0 -3.15252,0.520601 l -14.92389,14.056222 a 0.72305672,0.72305672 0 0 1 -0.98336,0 l -14.8082,-14.0273 a 2.8922269,2.8922269 0 0 0 -4.88787,2.111325 v 13.767001 a 2.8922269,2.8922269 0 0 0 0.89659,2.082403 l 17.35337,16.370005 a 2.8922269,2.8922269 0 0 0 1.99563,0.80982 2.8922269,2.8922269 0 0 0 1.99564,-0.80982 l 17.35336,-16.370005 a 2.8922269,2.8922269 0 0 0 0.89659,-2.111326 v -13.767 a 2.8922269,2.8922269 0 0 0 -1.73534,-2.631926 z"
|
||||
fill="#000000"
|
||||
id="path27437"
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:2.89223" />
|
||||
</g>
|
||||
<g
|
||||
id="g27445">
|
||||
<path
|
||||
d="m 614.57824,100.92155 a 2.8922269,2.8922269 0 0 0 -3.15252,0.54952 l -14.92389,14.05622 a 0.69413446,0.69413446 0 0 1 -0.98336,0 l -14.8082,-14.0273 a 2.8922269,2.8922269 0 0 0 -4.88787,2.0824 v 13.767 a 2.8922269,2.8922269 0 0 0 0.89659,2.11133 l 17.35337,16.37 a 2.8922269,2.8922269 0 0 0 3.99127,0 l 17.35336,-16.37 a 2.8922269,2.8922269 0 0 0 0.89659,-2.11133 v -13.767 a 2.8922269,2.8922269 0 0 0 -1.73534,-2.66084 z"
|
||||
fill="#000000"
|
||||
id="path27441"
|
||||
style="fill:#b0b0b0;fill-opacity:1;stroke-width:2.89223" />
|
||||
<path
|
||||
d="m 614.57824,69.887949 a 2.8922269,2.8922269 0 0 0 -3.15252,0.520601 l -14.92389,14.056222 a 0.72305672,0.72305672 0 0 1 -0.98336,0 l -14.8082,-14.0273 a 2.8922269,2.8922269 0 0 0 -4.88787,2.111325 v 13.767001 a 2.8922269,2.8922269 0 0 0 0.89659,2.082403 l 17.35337,16.370009 a 2.8922269,2.8922269 0 0 0 1.99563,0.80982 2.8922269,2.8922269 0 0 0 1.99564,-0.80982 l 17.35336,-16.370009 a 2.8922269,2.8922269 0 0 0 0.89659,-2.111326 v -13.767 a 2.8922269,2.8922269 0 0 0 -1.73534,-2.631926 z"
|
||||
fill="#000000"
|
||||
id="path27443"
|
||||
style="fill:#b0b0b0;fill-opacity:1;stroke-width:2.89223" />
|
||||
</g>
|
||||
</g>
|
||||
<path
|
||||
style="fill:none;stroke:#b0b0b0;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:8, 16;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 452.22833,1.446294 V 498.3684"
|
||||
id="path4358" />
|
||||
<path
|
||||
style="display:inline;fill:none;stroke:#d0d0d0;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker4440)"
|
||||
d="M 102.34622,252.24403 H 249.08619"
|
||||
id="path6402"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="display:inline;fill:none;stroke:#d0d0d0;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker4440)"
|
||||
d="M 379.99228,252.24403 H 526.73225"
|
||||
id="path6404"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:18.6667px;line-height:1.25;font-family:Lato;-inkscape-font-specification:Lato;display:inline;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
x="596.06799"
|
||||
y="321.42487"
|
||||
id="text15698"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan15694"
|
||||
x="596.06799"
|
||||
y="321.42487"
|
||||
style="text-align:center;text-anchor:middle;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1">ethdo</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
x="596.06799"
|
||||
y="344.75824"
|
||||
style="text-align:center;text-anchor:middle;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
id="tspan15696">(without keys)</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:18.6667px;line-height:1.25;font-family:Lato;-inkscape-font-specification:Lato;display:inline;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
x="318.42194"
|
||||
y="321.42487"
|
||||
id="text15707"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan15703"
|
||||
x="318.42194"
|
||||
y="321.42487"
|
||||
style="text-align:center;text-anchor:middle;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1">ethdo</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
x="318.42194"
|
||||
y="344.75824"
|
||||
style="text-align:center;text-anchor:middle;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
id="tspan15705">(with keys)</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:32px;line-height:1.25;font-family:Lato;-inkscape-font-specification:Lato;display:inline;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
x="246.36612"
|
||||
y="26.809605"
|
||||
id="text16335"><tspan
|
||||
sodipodi:role="line"
|
||||
x="246.36612"
|
||||
y="26.809605"
|
||||
style="font-size:32px;text-align:center;text-anchor:middle;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
id="tspan16333">Offline</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:32px;line-height:1.25;font-family:Lato;-inkscape-font-specification:Lato;display:inline;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
x="801.6582"
|
||||
y="26.809605"
|
||||
id="text18181"><tspan
|
||||
sodipodi:role="line"
|
||||
x="801.6582"
|
||||
y="26.809605"
|
||||
style="font-size:32px;text-align:center;text-anchor:middle;fill:#b0b0b0;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
id="tspan18179">Online</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 66 KiB |
@@ -581,12 +581,18 @@ Validator commands focus on interaction with Ethereum 2 validators.
|
||||
#### `credentials get`
|
||||
|
||||
`ethdo validator credentials get` provides information about the withdrawal credentials for the provided validator. Options include:
|
||||
- `account` the account for which to obtain the withdrawal credentials (in format "wallet/account")
|
||||
- `pubkey` the public key of the validator for which to obtain the withdrawal credentials
|
||||
- `index` the index of the validator for which to obtain the withdrawal credentials
|
||||
- `validator` the account, public key or index for which to obtain the withdrawal credentials
|
||||
|
||||
```sh
|
||||
$ ethdo validator credentials get --account=Validators/1
|
||||
$ ethdo validator credentials get --validator=Validators/1
|
||||
```
|
||||
|
||||
#### `credentials set`
|
||||
|
||||
`ethdo validator credentials set` updates withdrawal credentials from BLS "type 0" credentials to execution "type 1" credentials. Full information about using this command can be found in the [specific documentation](./changingwithdrawalcredentials.md).
|
||||
|
||||
```sh
|
||||
$ ethdo validator credentials set --validator=Validators/1 --execution-address=0x8f…9F --private-key=0x3b…9c
|
||||
```
|
||||
|
||||
#### `depositdata`
|
||||
@@ -622,7 +628,7 @@ $ ethdo validator exit --key=0x01e748d098d3bcb477d636f19d510399ae18205fadf9814ee
|
||||
`ethdo validator info` provides information for a given validator.
|
||||
|
||||
```sh
|
||||
$ ethdo validator info --account=Validators/1
|
||||
$ ethdo validator info --validator=Validators/1
|
||||
Status: Active
|
||||
Balance: 3.203823585 Ether
|
||||
Effective balance: 3.1 Ether
|
||||
@@ -641,7 +647,7 @@ Effective balance: 3.1 Ether
|
||||
Withdrawal credentials: 0x0033ef3cb10b36d0771ffe8a02bc5bfc7e64ea2f398ce77e25bb78989edbee36
|
||||
```
|
||||
|
||||
If the validator is not an account it can be queried directly with `--pubkey`.
|
||||
If the validator is not an account then `--validator` option can be supplied with a validator index or public key.
|
||||
|
||||
```sh
|
||||
$ ethdo validator info --pubkey=0x842dd66cfeaeff4397fc7c94f7350d2131ca0c4ad14ff727963be9a1edb4526604970df6010c3da6474a9820fa81642b
|
||||
@@ -711,6 +717,12 @@ $ ethdo validator yield
|
||||
Yield: 4.64%
|
||||
```
|
||||
|
||||
#### `summary`
|
||||
`ethdo validator summary` provides a summary of the given epoch for the given validators. Options include:
|
||||
- `epoch`: the epoch for which to provide a summary; defaults to last complete epoch
|
||||
- `validators`: the list of validators for which to provide a summary
|
||||
- `json`: provide JSON output
|
||||
|
||||
### `proposer` commands
|
||||
|
||||
Proposer commands focus on Ethereum 2 validators' actions as proposers.
|
||||
|
||||
39
go.mod
39
go.mod
@@ -3,17 +3,18 @@ module github.com/wealdtech/ethdo
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/OneOfOne/xxhash v1.2.5 // indirect
|
||||
github.com/attestantio/dirk v1.1.0
|
||||
github.com/attestantio/go-eth2-client v0.11.0
|
||||
github.com/aws/aws-sdk-go v1.42.44 // indirect
|
||||
github.com/ferranbt/fastssz v0.0.0-20220103083642-bc5fefefa28b
|
||||
github.com/attestantio/go-eth2-client v0.14.0
|
||||
github.com/aws/aws-sdk-go v1.44.111 // indirect
|
||||
github.com/ferranbt/fastssz v0.1.2
|
||||
github.com/gofrs/uuid v4.2.0+incompatible
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b
|
||||
github.com/hashicorp/hcl v1.0.1-vault-3 // indirect
|
||||
github.com/herumi/bls-eth-go-binary v0.0.0-20220103074059-01b0ca9e9ef7
|
||||
github.com/jackc/puddle v1.2.1 // indirect
|
||||
github.com/herumi/bls-eth-go-binary v1.28.1
|
||||
github.com/jackc/puddle v1.3.0 // indirect
|
||||
github.com/klauspost/compress v1.15.11 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/minio/highwayhash v1.0.2 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354
|
||||
@@ -21,22 +22,22 @@ require (
|
||||
github.com/protolambda/zssz v0.1.5 // indirect
|
||||
github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7
|
||||
github.com/prysmaticlabs/go-ssz v0.0.0-20210121151755-f6208871c388
|
||||
github.com/rs/zerolog v1.26.1
|
||||
github.com/rs/zerolog v1.28.0
|
||||
github.com/shopspring/decimal v1.3.1
|
||||
github.com/spf13/afero v1.8.0 // indirect
|
||||
github.com/spf13/afero v1.9.2 // indirect
|
||||
github.com/spf13/cobra v1.3.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.10.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/spf13/viper v1.13.0
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/tyler-smith/go-bip39 v1.1.0
|
||||
github.com/wealdtech/go-bytesutil v1.1.1
|
||||
github.com/wealdtech/go-ecodec v1.1.2
|
||||
github.com/wealdtech/go-eth2-types/v2 v2.6.0
|
||||
github.com/wealdtech/go-eth2-types/v2 v2.7.0
|
||||
github.com/wealdtech/go-eth2-util v1.7.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-distributed v1.1.4
|
||||
github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.2.0
|
||||
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
|
||||
@@ -44,9 +45,11 @@ require (
|
||||
github.com/wealdtech/go-eth2-wallet-store-scratch v1.7.0
|
||||
github.com/wealdtech/go-eth2-wallet-types/v2 v2.9.0
|
||||
github.com/wealdtech/go-string2eth v1.2.0
|
||||
golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed // indirect
|
||||
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b // indirect
|
||||
golang.org/x/net v0.0.0-20221004154528-8021a29435af // indirect
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect
|
||||
golang.org/x/text v0.3.7
|
||||
google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350 // indirect
|
||||
google.golang.org/grpc v1.44.0
|
||||
gopkg.in/ini.v1 v1.66.3 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220930163606-c98284e70a91 // indirect
|
||||
)
|
||||
|
||||
383
go.sum
383
go.sum
@@ -29,35 +29,156 @@ 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.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
|
||||
cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
|
||||
cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU=
|
||||
cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA=
|
||||
cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw=
|
||||
cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY=
|
||||
cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI=
|
||||
cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4=
|
||||
cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4=
|
||||
cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0=
|
||||
cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ=
|
||||
cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk=
|
||||
cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o=
|
||||
cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s=
|
||||
cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY=
|
||||
cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw=
|
||||
cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0=
|
||||
cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8=
|
||||
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/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA=
|
||||
cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY=
|
||||
cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s=
|
||||
cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM=
|
||||
cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI=
|
||||
cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY=
|
||||
cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI=
|
||||
cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
|
||||
cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
|
||||
cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
|
||||
cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
|
||||
cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
|
||||
cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
|
||||
cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I=
|
||||
cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4=
|
||||
cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0=
|
||||
cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs=
|
||||
cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc=
|
||||
cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM=
|
||||
cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ=
|
||||
cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo=
|
||||
cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE=
|
||||
cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I=
|
||||
cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ=
|
||||
cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo=
|
||||
cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA=
|
||||
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/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo=
|
||||
cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ=
|
||||
cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4=
|
||||
cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0=
|
||||
cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU=
|
||||
cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU=
|
||||
cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y=
|
||||
cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg=
|
||||
cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk=
|
||||
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/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk=
|
||||
cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg=
|
||||
cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM=
|
||||
cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA=
|
||||
cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o=
|
||||
cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A=
|
||||
cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0=
|
||||
cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0=
|
||||
cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc=
|
||||
cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
|
||||
cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic=
|
||||
cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI=
|
||||
cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8=
|
||||
cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08=
|
||||
cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4=
|
||||
cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w=
|
||||
cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE=
|
||||
cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM=
|
||||
cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY=
|
||||
cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s=
|
||||
cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA=
|
||||
cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o=
|
||||
cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ=
|
||||
cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU=
|
||||
cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY=
|
||||
cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34=
|
||||
cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs=
|
||||
cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg=
|
||||
cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E=
|
||||
cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU=
|
||||
cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0=
|
||||
cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA=
|
||||
cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0=
|
||||
cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI=
|
||||
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/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4=
|
||||
cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o=
|
||||
cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk=
|
||||
cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg=
|
||||
cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4=
|
||||
cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg=
|
||||
cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c=
|
||||
cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y=
|
||||
cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A=
|
||||
cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4=
|
||||
cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY=
|
||||
cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s=
|
||||
cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI=
|
||||
cloud.google.com/go/secretmanager v1.0.0/go.mod h1:+Qkm5qxIJ5mk74xxIXA+87fseaY1JLYBcFPQoc/GQxg=
|
||||
cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA=
|
||||
cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4=
|
||||
cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0=
|
||||
cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU=
|
||||
cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU=
|
||||
cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc=
|
||||
cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs=
|
||||
cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg=
|
||||
cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM=
|
||||
cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ=
|
||||
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=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
|
||||
cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc=
|
||||
cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw=
|
||||
cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g=
|
||||
cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU=
|
||||
cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4=
|
||||
cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0=
|
||||
cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo=
|
||||
cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo=
|
||||
cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE=
|
||||
cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg=
|
||||
cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0=
|
||||
cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
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/OneOfOne/xxhash v1.2.5 h1:zl/OfRA6nftbBK9qTohYBJ5xvw6C/oNKizR7cZGl3cI=
|
||||
github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
|
||||
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=
|
||||
@@ -75,16 +196,15 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI
|
||||
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.11.0 h1:8/Jn5AAfd+4tOggLi+FvOv9/ORaObECv42ab7vK2FJc=
|
||||
github.com/attestantio/go-eth2-client v0.11.0/go.mod h1:zXL/BxC0cBBhxj+tP7QG7t9Ufoa8GwQLdlbvZRd9+dM=
|
||||
github.com/attestantio/go-eth2-client v0.14.0 h1:usWgI0KIfGWQ8G7XUsMM2bTe+yuJEh4d3HEO1vPD5Go=
|
||||
github.com/attestantio/go-eth2-client v0.14.0/go.mod h1:bcg5gfjVcm+MtcaZfzv/uSWNHU4i8hGamVG+9JCZnC0=
|
||||
github.com/aws/aws-sdk-go v1.33.17/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
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.42.44 h1:vPlF4cUsdN5ETfvb7ewZFbFZyB6Rsfndt3kS2XqLXKo=
|
||||
github.com/aws/aws-sdk-go v1.42.44/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc=
|
||||
github.com/aws/aws-sdk-go v1.44.111 h1:AcWfOgeedSQ4gQVwcIe6aLxpQNJMloZQyqnr7Dzki+s=
|
||||
github.com/aws/aws-sdk-go v1.44.111/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
|
||||
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=
|
||||
@@ -116,6 +236,7 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
|
||||
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=
|
||||
@@ -143,6 +264,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m
|
||||
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/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
||||
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=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
@@ -154,12 +276,16 @@ github.com/ferranbt/fastssz v0.0.0-20200728110133-0b6e349af87a/go.mod h1:DyEu2iu
|
||||
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.0.0-20220103083642-bc5fefefa28b h1:Jea4sHxe4sTegJgpfhWvxSjFF2nyq4/R/qWm6AziPiI=
|
||||
github.com/ferranbt/fastssz v0.0.0-20220103083642-bc5fefefa28b/go.mod h1:S8yiDeAXy8f88W4Ul+0dBMPx49S05byYbmZD6Uv94K4=
|
||||
github.com/ferranbt/fastssz v0.1.1/go.mod h1:U2ZsxlYyvGeQGmadhz8PlEqwkBzDIhHwd3xuKrg2JIs=
|
||||
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/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
||||
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=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@@ -188,7 +314,6 @@ github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZg
|
||||
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 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
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=
|
||||
@@ -241,8 +366,11 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
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=
|
||||
@@ -269,13 +397,21 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/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/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
|
||||
github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
|
||||
github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
|
||||
github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo=
|
||||
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
|
||||
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/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
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=
|
||||
@@ -290,6 +426,7 @@ github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
@@ -304,9 +441,8 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/hcl v1.0.1-vault-3 h1:V95v5KSTu6DB5huDSKiq4uAfILEuNigK/+qPET6H/Mg=
|
||||
github.com/hashicorp/hcl v1.0.1-vault-3/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
|
||||
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
|
||||
@@ -314,20 +450,21 @@ github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOn
|
||||
github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
|
||||
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
|
||||
github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
|
||||
github.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
|
||||
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 v0.0.0-20220103074059-01b0ca9e9ef7 h1:gMN4oOdFLVR7ye4SCacHD4SIB64NPUNE+V9w8o7bR6A=
|
||||
github.com/herumi/bls-eth-go-binary v0.0.0-20220103074059-01b0ca9e9ef7/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=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
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.2.1 h1:gI8os0wpRXFd4FiAY2dWiqRK037tjj3t7rKFeO4X5iw=
|
||||
github.com/jackc/puddle v1.2.1/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=
|
||||
@@ -348,20 +485,23 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL
|
||||
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 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
||||
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.0.11 h1:i2lw1Pm7Yi/4O6XCSyJWqEHI2MDw2FzUK6o/D21xn2A=
|
||||
github.com/klauspost/cpuid/v2 v2.0.11/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||
github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdydm0=
|
||||
github.com/klauspost/cpuid/v2 v2.1.1/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 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
|
||||
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=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
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=
|
||||
@@ -370,24 +510,26 @@ 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 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
|
||||
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=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
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 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
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=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
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/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=
|
||||
@@ -406,8 +548,9 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
|
||||
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
|
||||
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
@@ -425,8 +568,11 @@ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYr
|
||||
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 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
|
||||
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=
|
||||
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
|
||||
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -442,25 +588,22 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn
|
||||
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||
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 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk=
|
||||
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
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 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
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 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
|
||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
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 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/protolambda/zssz v0.1.5 h1:7fjJjissZIIaa2QcvmhS/pZISMX21zVITt49sW1ouek=
|
||||
github.com/protolambda/zssz v0.1.5/go.mod h1:a4iwOX5FE7/JkKA+J/PH0Mjo9oXftN6P8NZyL28gpag=
|
||||
@@ -471,24 +614,30 @@ github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 h1:0tVE4
|
||||
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/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/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
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 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc=
|
||||
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/sagikazarmark/crypt v0.4.0/go.mod h1:ALv2SRj7GxYV4HO9elxH9nS6M9gW+xDNxqmyJ6RfDFM=
|
||||
github.com/sagikazarmark/crypt v0.6.0/go.mod h1:U8+INwJo3nBv1m6A/8OBXAq7Jnpspk5AxSgDyEQcea8=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 h1:Xuk8ma/ibJ1fOy4Ee11vHhUFHQNpHhrBneOCNHVXS5w=
|
||||
github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0/go.mod h1:7AwjWCpdPhkSmNAgUv5C7EJ4AbmjEB3r047r3DXWu3Y=
|
||||
@@ -496,19 +645,19 @@ github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5g
|
||||
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
|
||||
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 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/afero v1.8.0 h1:5MmtuhAgYeU6qpa7w7bP0dv6MBYuup0vekhSpSkoq60=
|
||||
github.com/spf13/afero v1.8.0/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
|
||||
github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
|
||||
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 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
|
||||
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=
|
||||
@@ -521,21 +670,26 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
||||
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.10.1 h1:nuJZuYpG7gTj/XqiUwg8bA0cp1+M2mC3J4g5luUYBKk=
|
||||
github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU=
|
||||
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=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
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=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
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/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=
|
||||
@@ -552,8 +706,9 @@ github.com/wealdtech/go-ecodec v1.1.2 h1:CcNBnFV6b8IoDtu8wj6cfrmuBRBw00VYqsvpT4+
|
||||
github.com/wealdtech/go-ecodec v1.1.2/go.mod h1:0Rgn8mEr0GX4fDyuU3aUL+xp4n17Wviscasp6rWCh10=
|
||||
github.com/wealdtech/go-eth2-types/v2 v2.5.5/go.mod h1:QY9t7nwxc26C9/iZHh1wzc+hZbGGn7ipgjMYPhKnN/0=
|
||||
github.com/wealdtech/go-eth2-types/v2 v2.5.6/go.mod h1:SX7+QxnhXGxeW8tESzJ/BSIXvoflBHUi21chqRsZ9wE=
|
||||
github.com/wealdtech/go-eth2-types/v2 v2.6.0 h1:djgMv1A40bstgkg6L1ZA7eowR/Gbmj1ZWnBdrK39lhY=
|
||||
github.com/wealdtech/go-eth2-types/v2 v2.6.0/go.mod h1:psOez/ZRBzZSDl5hiNDwRf5ZqQujNE6h5FxAz09Koxg=
|
||||
github.com/wealdtech/go-eth2-types/v2 v2.7.0 h1:TV2jrNkane2nxTiVhyG3npVtg825WvRdCZZ4j+jTENA=
|
||||
github.com/wealdtech/go-eth2-types/v2 v2.7.0/go.mod h1:aQ5dk+KNPE/A2r3lLhKpW+f5B24aJO68tRz6wO4Zo/g=
|
||||
github.com/wealdtech/go-eth2-util v1.7.0 h1:bgCnZ4H5K5VE+CiAwFTKhrICItq5rI+5VYaODFWC6is=
|
||||
github.com/wealdtech/go-eth2-util v1.7.0/go.mod h1:L0q3G7S1Dm1WJPye5m2wjuNweXC98o1CmLTKPmBTlpg=
|
||||
github.com/wealdtech/go-eth2-wallet v1.15.0 h1:yff2rWCvHyr5bOU41wwilsNemFRYOFinZoIPjhzG10M=
|
||||
@@ -563,8 +718,9 @@ github.com/wealdtech/go-eth2-wallet-dirk v1.2.0/go.mod h1:1lnHDSdcRxj9CFJh+jPtdg
|
||||
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=
|
||||
github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.2.0 h1:HgIK30YsXxgR6Ra6pvxW1KFUPbx7BpIIK1VGhOlmCm4=
|
||||
github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.2.0/go.mod h1:qqIU42c9sXcNYsiEjUQoOOWYZfZDL1zmyLtz3t+wN2s=
|
||||
github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.3.0 h1:3Kx2QvKU/4PP0d7+e3+q9G+QiXQprNQPeAXsWOcfKRI=
|
||||
github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.3.0/go.mod h1:qqIU42c9sXcNYsiEjUQoOOWYZfZDL1zmyLtz3t+wN2s=
|
||||
github.com/wealdtech/go-eth2-wallet-encryptor-unencrypted v1.0.1 h1:x6bq8cVgRgfhwtSQSYo/9AqJ8qEeaS6af28cW0cVj5U=
|
||||
github.com/wealdtech/go-eth2-wallet-encryptor-unencrypted v1.0.1/go.mod h1:49K88T/4LNQpB8ghVcjTKeRRi/bZHeYjN8Ef5S23yps=
|
||||
github.com/wealdtech/go-eth2-wallet-hd/v2 v2.6.0 h1:Oy5TsD6HqZzb3MwGOe+5WVCj4pCKKUYYc2OKyN42QCM=
|
||||
@@ -586,8 +742,6 @@ github.com/wealdtech/go-eth2-wallet-types/v2 v2.9.0/go.mod h1:7Ad2xp27vOQRQWQsIe
|
||||
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.1.0 h1:USJQmysUrBYYmZs7d45pMb90hRSyEwizP7lZaOZLDAw=
|
||||
github.com/wealdtech/go-string2eth v1.1.0/go.mod h1:RUzsLjJtbZaJ/3UKn9kY19a/vCCUHtEWoUW3uiK6yGU=
|
||||
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=
|
||||
@@ -599,10 +753,14 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
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/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
|
||||
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/pkg/v3 v3.5.4/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.etcd.io/etcd/client/v2 v2.305.4/go.mod h1:Ud+VUwIi9/uQHOMA+4ekToJ12lTxlv0zB/+DHwTGEbU=
|
||||
go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
@@ -634,8 +792,9 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed h1:YoWVYYAfvQ4ddHv3OKmIvX7NCAhFGTj62VP2l2kfBbA=
|
||||
golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b h1:huxqepDufQpLLIRXiVkTvnxrzJlpwmIWAObmcCcUFr0=
|
||||
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
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=
|
||||
@@ -728,9 +887,18 @@ golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
golang.org/x/net v0.0.0-20211105192438-b53810dc28af/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211116231205-47ca1ff31462/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
|
||||
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-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.0.0-20221004154528-8021a29435af h1:wv66FM3rLZGPdxpYL+ApnDe2HzHcTFta3z5nsc13wI4=
|
||||
golang.org/x/net v0.0.0-20221004154528-8021a29435af/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
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=
|
||||
@@ -748,6 +916,13 @@ 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.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
|
||||
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
|
||||
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
||||
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
||||
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=
|
||||
@@ -758,8 +933,11 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
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 h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/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/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=
|
||||
@@ -844,8 +1022,23 @@ golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -924,8 +1117,12 @@ golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
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=
|
||||
@@ -964,6 +1161,22 @@ google.golang.org/api v0.60.0/go.mod h1:d7rl65NZAkEQ90JFzqBjcRq1TVeG5ZoGV3sSpEnn
|
||||
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/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
|
||||
google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
|
||||
google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
|
||||
google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
|
||||
google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
|
||||
google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
|
||||
google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
|
||||
google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
|
||||
google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
|
||||
google.golang.org/api v0.81.0/go.mod h1:FA6Mb/bZxj706H2j+j2d6mHEEaHBmbbWnkfvmorOCko=
|
||||
google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
|
||||
google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g=
|
||||
google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
|
||||
google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
|
||||
google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI=
|
||||
google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
|
||||
google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
@@ -1016,6 +1229,7 @@ google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6D
|
||||
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
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-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||
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=
|
||||
@@ -1046,8 +1260,46 @@ google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ6
|
||||
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-20220126215142-9970aeb2e350 h1:YxHp5zqIcAShDEvRr5/0rVESVS+njYF68PSdazrNLJo=
|
||||
google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||
google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||
google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||
google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
|
||||
google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
||||
google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
||||
google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
||||
google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
||||
google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
||||
google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||
google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||
google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||
google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||
google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||
google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE=
|
||||
google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc=
|
||||
google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
|
||||
google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
|
||||
google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
|
||||
google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
|
||||
google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
|
||||
google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
|
||||
google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
|
||||
google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
|
||||
google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
|
||||
google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
|
||||
google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw=
|
||||
google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI=
|
||||
google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI=
|
||||
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/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=
|
||||
@@ -1077,9 +1329,14 @@ 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.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc v1.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg=
|
||||
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
|
||||
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
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/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=
|
||||
@@ -1093,8 +1350,10 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/cenkalti/backoff.v1 v1.1.0 h1:Arh75ttbsvlpVA7WtVpH4u9h6Zl46xuptxqLxPiSo4Y=
|
||||
gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UDAkHu8BrjI=
|
||||
@@ -1106,8 +1365,8 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
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.66.3 h1:jRskFVxYaMGAMUbN0UZ7niA9gzL9B49DOqE78vg0k3w=
|
||||
gopkg.in/ini.v1 v1.66.3/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=
|
||||
@@ -1119,8 +1378,9 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
@@ -1134,3 +1394,4 @@ 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=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
|
||||
@@ -54,4 +54,6 @@ type Service interface {
|
||||
AltairInitialEpoch() phase0.Epoch
|
||||
// AltairInitialSyncCommitteePeriod provides the sync committee period in which the Altair hard fork takes place.
|
||||
AltairInitialSyncCommitteePeriod() uint64
|
||||
// CapellaInitialEpoch provides the epoch at which the Capella hard fork takes place.
|
||||
CapellaInitialEpoch() phase0.Epoch
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ type Service struct {
|
||||
epochsPerSyncCommitteePeriod uint64
|
||||
altairForkEpoch phase0.Epoch
|
||||
bellatrixForkEpoch phase0.Epoch
|
||||
capellaForkEpoch phase0.Epoch
|
||||
}
|
||||
|
||||
// module-wide log.
|
||||
@@ -100,6 +101,13 @@ func New(ctx context.Context, params ...Parameter) (*Service, error) {
|
||||
}
|
||||
log.Trace().Uint64("epoch", uint64(bellatrixForkEpoch)).Msg("Obtained Bellatrix fork epoch")
|
||||
|
||||
capellaForkEpoch, err := fetchCapellaForkEpoch(ctx, parameters.forkScheduleProvider)
|
||||
if err != nil {
|
||||
// Set to far future epoch.
|
||||
capellaForkEpoch = 0xffffffffffffffff
|
||||
}
|
||||
log.Trace().Uint64("epoch", uint64(capellaForkEpoch)).Msg("Obtained Capella fork epoch")
|
||||
|
||||
s := &Service{
|
||||
genesisTime: genesisTime,
|
||||
slotDuration: slotDuration,
|
||||
@@ -107,6 +115,7 @@ func New(ctx context.Context, params ...Parameter) (*Service, error) {
|
||||
epochsPerSyncCommitteePeriod: epochsPerSyncCommitteePeriod,
|
||||
altairForkEpoch: altairForkEpoch,
|
||||
bellatrixForkEpoch: bellatrixForkEpoch,
|
||||
capellaForkEpoch: capellaForkEpoch,
|
||||
}
|
||||
|
||||
return s, nil
|
||||
@@ -247,3 +256,28 @@ func fetchBellatrixForkEpoch(ctx context.Context, provider eth2client.ForkSchedu
|
||||
}
|
||||
return 0, errors.New("no bellatrix fork obtained")
|
||||
}
|
||||
|
||||
// CapellaInitialEpoch provides the epoch at which the Capella hard fork takes place.
|
||||
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)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
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++
|
||||
}
|
||||
return 0, errors.New("no capella fork obtained")
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package shamir
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
cryptorand "crypto/rand"
|
||||
"crypto/subtle"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
mathrand "math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -20,6 +20,20 @@ type polynomial struct {
|
||||
coefficients []uint8
|
||||
}
|
||||
|
||||
type cryptoSource struct{}
|
||||
|
||||
func (s cryptoSource) Int63() int64 {
|
||||
bytes := make([]byte, 8)
|
||||
if _, err := cryptorand.Read(bytes); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return int64(binary.BigEndian.Uint64(bytes) >> 1)
|
||||
}
|
||||
|
||||
func (s cryptoSource) Seed(seed int64) {
|
||||
panic("seed")
|
||||
}
|
||||
|
||||
// makePolynomial constructs a random polynomial of the given
|
||||
// degree but with the provided intercept value.
|
||||
func makePolynomial(intercept, degree uint8) (polynomial, error) {
|
||||
@@ -32,7 +46,7 @@ func makePolynomial(intercept, degree uint8) (polynomial, error) {
|
||||
p.coefficients[0] = intercept
|
||||
|
||||
// Assign random co-efficients to the polynomial
|
||||
if _, err := rand.Read(p.coefficients[1:]); err != nil {
|
||||
if _, err := cryptorand.Read(p.coefficients[1:]); err != nil {
|
||||
return p, err
|
||||
}
|
||||
|
||||
@@ -143,8 +157,8 @@ func Split(secret []byte, parts, threshold int) ([][]byte, error) {
|
||||
}
|
||||
|
||||
// Generate random list of x coordinates
|
||||
mathrand.Seed(time.Now().UnixNano())
|
||||
xCoordinates := mathrand.Perm(255)
|
||||
rnd := mathrand.New(&cryptoSource{})
|
||||
xCoordinates := rnd.Perm(255)
|
||||
|
||||
// Allocate the output array, initialize the final byte
|
||||
// of the output with the offset. The representation of each
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// Code generated by fastssz. DO NOT EDIT.
|
||||
// Hash: c953c4b72fdc3f250f4227b181e286fe53fd663c5aa4f5cc3de6cc9998c7fdb7
|
||||
// Version: 0.1.2
|
||||
package signing
|
||||
|
||||
import (
|
||||
@@ -15,15 +17,15 @@ func (c *Container) MarshalSSZTo(buf []byte) (dst []byte, err error) {
|
||||
dst = buf
|
||||
|
||||
// Field (0) 'Root'
|
||||
if len(c.Root) != 32 {
|
||||
err = ssz.ErrBytesLength
|
||||
if size := len(c.Root); size != 32 {
|
||||
err = ssz.ErrBytesLengthFn("Container.Root", size, 32)
|
||||
return
|
||||
}
|
||||
dst = append(dst, c.Root...)
|
||||
|
||||
// Field (1) 'Domain'
|
||||
if len(c.Domain) != 32 {
|
||||
err = ssz.ErrBytesLength
|
||||
if size := len(c.Domain); size != 32 {
|
||||
err = ssz.ErrBytesLengthFn("Container.Domain", size, 32)
|
||||
return
|
||||
}
|
||||
dst = append(dst, c.Domain...)
|
||||
@@ -66,19 +68,19 @@ func (c *Container) HashTreeRoot() ([32]byte, error) {
|
||||
}
|
||||
|
||||
// HashTreeRootWith ssz hashes the Container object with a hasher
|
||||
func (c *Container) HashTreeRootWith(hh *ssz.Hasher) (err error) {
|
||||
func (c *Container) HashTreeRootWith(hh ssz.HashWalker) (err error) {
|
||||
indx := hh.Index()
|
||||
|
||||
// Field (0) 'Root'
|
||||
if len(c.Root) != 32 {
|
||||
err = ssz.ErrBytesLength
|
||||
if size := len(c.Root); size != 32 {
|
||||
err = ssz.ErrBytesLengthFn("Container.Root", size, 32)
|
||||
return
|
||||
}
|
||||
hh.PutBytes(c.Root)
|
||||
|
||||
// Field (1) 'Domain'
|
||||
if len(c.Domain) != 32 {
|
||||
err = ssz.ErrBytesLength
|
||||
if size := len(c.Domain); size != 32 {
|
||||
err = ssz.ErrBytesLengthFn("Container.Domain", size, 32)
|
||||
return
|
||||
}
|
||||
hh.PutBytes(c.Domain)
|
||||
@@ -86,3 +88,8 @@ func (c *Container) HashTreeRootWith(hh *ssz.Hasher) (err error) {
|
||||
hh.Merkleize(indx)
|
||||
return
|
||||
}
|
||||
|
||||
// GetTree ssz hashes the Container object
|
||||
func (c *Container) GetTree() (*ssz.Node, error) {
|
||||
return ssz.ProofTree(c)
|
||||
}
|
||||
|
||||
122
util/account.go
122
util/account.go
@@ -15,11 +15,126 @@ package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
util "github.com/wealdtech/go-eth2-util"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
// ParseAccount parses input to obtain an account.
|
||||
func ParseAccount(ctx context.Context,
|
||||
accountStr string,
|
||||
supplementary []string,
|
||||
unlock bool,
|
||||
) (
|
||||
e2wtypes.Account,
|
||||
error,
|
||||
) {
|
||||
if accountStr == "" {
|
||||
return nil, errors.New("no account specified")
|
||||
}
|
||||
|
||||
var account e2wtypes.Account
|
||||
var err error
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(accountStr, "0x"):
|
||||
// A key. Could be public or private.
|
||||
data, err := hex.DecodeString(strings.TrimPrefix(accountStr, "0x"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse account key")
|
||||
}
|
||||
switch len(data) {
|
||||
case 48:
|
||||
// Public key.
|
||||
account, err = newScratchAccountFromPubKey(data)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create account from public key")
|
||||
}
|
||||
if unlock {
|
||||
return nil, errors.New("cannot unlock an account specified by its public key")
|
||||
}
|
||||
case 32:
|
||||
// Private key.
|
||||
account, err = newScratchAccountFromPrivKey(data)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create account from public key")
|
||||
}
|
||||
if unlock {
|
||||
_, err = UnlockAccount(ctx, account, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unlock account")
|
||||
}
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("key of length %d neither public nor private key", len(data))
|
||||
}
|
||||
case strings.Contains(accountStr, "/"):
|
||||
// An account.
|
||||
_, account, err = WalletAndAccountFromPath(ctx, accountStr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to obtain account")
|
||||
}
|
||||
if unlock {
|
||||
// Supplementary will be the unlock passphrase(s).
|
||||
_, err = UnlockAccount(ctx, account, supplementary)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unlock account")
|
||||
}
|
||||
}
|
||||
case strings.Contains(accountStr, " "):
|
||||
// A mnemonic.
|
||||
// Supplementary will be the path.
|
||||
if len(supplementary) == 0 {
|
||||
return nil, errors.New("missing derivation path")
|
||||
}
|
||||
account, err = accountFromMnemonicAndPath(accountStr, supplementary[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if unlock {
|
||||
err = account.(e2wtypes.AccountLocker).Unlock(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unlock account")
|
||||
}
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown account specifier %s", accountStr)
|
||||
}
|
||||
|
||||
return account, nil
|
||||
}
|
||||
|
||||
func accountFromMnemonicAndPath(mnemonic string, path string) (e2wtypes.Account, error) {
|
||||
seed, err := SeedFromMnemonic(mnemonic)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Ensure the path is valid.
|
||||
match := hdPathRegex.Match([]byte(path))
|
||||
if !match {
|
||||
return nil, errors.New("path does not match expected format m/…")
|
||||
}
|
||||
|
||||
// Derive private key from seed and path.
|
||||
key, err := util.PrivateKeyFromSeedAndPath(seed, path)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to generate key")
|
||||
}
|
||||
|
||||
// Create a scratch account given the private key.
|
||||
account, err := newScratchAccountFromPrivKey(key.Marshal())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to generate scratch account")
|
||||
}
|
||||
|
||||
return account, nil
|
||||
}
|
||||
|
||||
// UnlockAccount attempts to unlock an account. It returns true if the account was already unlocked.
|
||||
func UnlockAccount(ctx context.Context, account e2wtypes.Account, passphrases []string) (bool, error) {
|
||||
locker, isAccountLocker := account.(e2wtypes.AccountLocker)
|
||||
@@ -46,6 +161,13 @@ func UnlockAccount(ctx context.Context, account e2wtypes.Account, passphrases []
|
||||
}
|
||||
}
|
||||
|
||||
// Also attempt to unlock without any passphrase.
|
||||
err = locker.Unlock(ctx, nil)
|
||||
if err == nil {
|
||||
// Unlocked.
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Failed to unlock it.
|
||||
return false, errors.New("failed to unlock account")
|
||||
}
|
||||
|
||||
103
util/account_test.go
Normal file
103
util/account_test.go
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright © 2020 Weald Technology Trading
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package util_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
e2types "github.com/wealdtech/go-eth2-types/v2"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
func TestParseAccount(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
require.NoError(t, e2types.InitBLS())
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
accountStr string
|
||||
supplementary []string
|
||||
unlock bool
|
||||
err string
|
||||
expectedPubkey string
|
||||
expectedUnlocked bool
|
||||
}{
|
||||
{
|
||||
name: "Zero",
|
||||
err: "no account specified",
|
||||
},
|
||||
{
|
||||
name: "Bad",
|
||||
accountStr: "bad",
|
||||
err: "unknown account specifier bad",
|
||||
},
|
||||
{
|
||||
name: "PublicKey",
|
||||
accountStr: "0x99b1f1d84d76185466d86c34bde1101316afddae76217aa86cd066979b19858c2c9d9e56eebc1e067ac54277a61790db",
|
||||
expectedPubkey: "0x99b1f1d84d76185466d86c34bde1101316afddae76217aa86cd066979b19858c2c9d9e56eebc1e067ac54277a61790db",
|
||||
},
|
||||
{
|
||||
name: "PublicKeyUnlocked",
|
||||
accountStr: "0x99b1f1d84d76185466d86c34bde1101316afddae76217aa86cd066979b19858c2c9d9e56eebc1e067ac54277a61790db",
|
||||
unlock: true,
|
||||
err: "cannot unlock an account specified by its public key",
|
||||
},
|
||||
{
|
||||
name: "PrivateKey",
|
||||
accountStr: "0x068dce0c90cb428ab37a74af0191eac49648035f1aaef077734b91e05985ec55",
|
||||
expectedPubkey: "0x99b1f1d84d76185466d86c34bde1101316afddae76217aa86cd066979b19858c2c9d9e56eebc1e067ac54277a61790db",
|
||||
},
|
||||
{
|
||||
name: "PrivateKeyUnlocked",
|
||||
accountStr: "0x068dce0c90cb428ab37a74af0191eac49648035f1aaef077734b91e05985ec55",
|
||||
expectedPubkey: "0x99b1f1d84d76185466d86c34bde1101316afddae76217aa86cd066979b19858c2c9d9e56eebc1e067ac54277a61790db",
|
||||
unlock: true,
|
||||
expectedUnlocked: true,
|
||||
},
|
||||
{
|
||||
name: "Mnemonic",
|
||||
accountStr: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
supplementary: []string{"m/12381/3600/0/0"},
|
||||
expectedPubkey: "0x99b1f1d84d76185466d86c34bde1101316afddae76217aa86cd066979b19858c2c9d9e56eebc1e067ac54277a61790db",
|
||||
},
|
||||
{
|
||||
name: "MnemonicUnlocked",
|
||||
accountStr: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
supplementary: []string{"m/12381/3600/0/0"},
|
||||
expectedPubkey: "0x99b1f1d84d76185466d86c34bde1101316afddae76217aa86cd066979b19858c2c9d9e56eebc1e067ac54277a61790db",
|
||||
unlock: true,
|
||||
expectedUnlocked: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
account, err := util.ParseAccount(ctx, test.accountStr, test.supplementary, test.unlock)
|
||||
if test.err == "" {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, account)
|
||||
unlocked, err := account.(e2wtypes.AccountLocker).IsUnlocked(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.expectedPubkey, fmt.Sprintf("%#x", account.PublicKey().Marshal()))
|
||||
require.Equal(t, test.expectedUnlocked, unlocked)
|
||||
} else {
|
||||
require.EqualError(t, err, test.err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
80
util/attestations.go
Normal file
80
util/attestations.go
Normal file
@@ -0,0 +1,80 @@
|
||||
// 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 util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/wealdtech/ethdo/services/chaintime"
|
||||
)
|
||||
|
||||
// AttestationHeadCorrect returns true if the given attestation had the correct head.
|
||||
func AttestationHeadCorrect(ctx context.Context,
|
||||
headersCache *BeaconBlockHeaderCache,
|
||||
attestation *phase0.Attestation,
|
||||
) (
|
||||
bool,
|
||||
error,
|
||||
) {
|
||||
slot := attestation.Data.Slot
|
||||
for {
|
||||
header, err := headersCache.Fetch(ctx, slot)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
if header == nil {
|
||||
// No block.
|
||||
slot--
|
||||
continue
|
||||
}
|
||||
if !header.Canonical {
|
||||
// Not canonical.
|
||||
slot--
|
||||
continue
|
||||
}
|
||||
return bytes.Equal(header.Root[:], attestation.Data.BeaconBlockRoot[:]), nil
|
||||
}
|
||||
}
|
||||
|
||||
// AttestationTargetCorrect returns true if the given attestation had the correct target.
|
||||
func AttestationTargetCorrect(ctx context.Context,
|
||||
headersCache *BeaconBlockHeaderCache,
|
||||
chainTime chaintime.Service,
|
||||
attestation *phase0.Attestation,
|
||||
) (
|
||||
bool,
|
||||
error,
|
||||
) {
|
||||
// Start with first slot of the target epoch.
|
||||
slot := chainTime.FirstSlotOfEpoch(attestation.Data.Target.Epoch)
|
||||
for {
|
||||
header, err := headersCache.Fetch(ctx, slot)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
if header == nil {
|
||||
// No block.
|
||||
slot--
|
||||
continue
|
||||
}
|
||||
if !header.Canonical {
|
||||
// Not canonical.
|
||||
slot--
|
||||
continue
|
||||
}
|
||||
return bytes.Equal(header.Root[:], attestation.Data.Target.Root[:]), nil
|
||||
}
|
||||
}
|
||||
70
util/beaconheadercache.go
Normal file
70
util/beaconheadercache.go
Normal file
@@ -0,0 +1,70 @@
|
||||
// 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 util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
apiv1 "github.com/attestantio/go-eth2-client/api/v1"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
)
|
||||
|
||||
// BeaconBlockHeaderCache is a cache of beacon block headers.
|
||||
type BeaconBlockHeaderCache struct {
|
||||
beaconBlockHeadersProvider eth2client.BeaconBlockHeadersProvider
|
||||
entries map[phase0.Slot]*beaconBlockHeaderEntry
|
||||
}
|
||||
|
||||
// NewBeaconBlockHeaderCache makes a new beacon block header cache.
|
||||
func NewBeaconBlockHeaderCache(provider eth2client.BeaconBlockHeadersProvider) *BeaconBlockHeaderCache {
|
||||
return &BeaconBlockHeaderCache{
|
||||
beaconBlockHeadersProvider: provider,
|
||||
entries: make(map[phase0.Slot]*beaconBlockHeaderEntry),
|
||||
}
|
||||
}
|
||||
|
||||
type beaconBlockHeaderEntry struct {
|
||||
present bool
|
||||
value *apiv1.BeaconBlockHeader
|
||||
}
|
||||
|
||||
// Fetch the beacon block header for the given slot.
|
||||
func (b *BeaconBlockHeaderCache) Fetch(ctx context.Context,
|
||||
slot phase0.Slot,
|
||||
) (
|
||||
*apiv1.BeaconBlockHeader,
|
||||
error,
|
||||
) {
|
||||
entry, exists := b.entries[slot]
|
||||
if !exists {
|
||||
header, err := b.beaconBlockHeadersProvider.BeaconBlockHeader(ctx, fmt.Sprintf("%d", slot))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if header == nil {
|
||||
entry = &beaconBlockHeaderEntry{
|
||||
present: false,
|
||||
}
|
||||
} else {
|
||||
entry = &beaconBlockHeaderEntry{
|
||||
present: true,
|
||||
value: header,
|
||||
}
|
||||
}
|
||||
b.entries[slot] = entry
|
||||
}
|
||||
return entry.value, nil
|
||||
}
|
||||
47
util/mnemonic.go
Normal file
47
util/mnemonic.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright © 2020, 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 util
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tyler-smith/go-bip39"
|
||||
"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+])+")
|
||||
|
||||
// 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.
|
||||
mnemonicParts := strings.Split(mnemonic, " ")
|
||||
mnemonicPassphrase := ""
|
||||
if len(mnemonicParts) > 24 {
|
||||
mnemonic = strings.Join(mnemonicParts[:24], " ")
|
||||
mnemonicPassphrase = strings.Join(mnemonicParts[24:], " ")
|
||||
}
|
||||
// Normalise the input.
|
||||
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")
|
||||
}
|
||||
|
||||
// Create seed from mnemonic and passphrase.
|
||||
return bip39.NewSeed(mnemonic, mnemonicPassphrase), nil
|
||||
}
|
||||
@@ -75,6 +75,14 @@ func (a *ScratchAccount) PublicKey() e2types.PublicKey {
|
||||
return a.pubKey
|
||||
}
|
||||
|
||||
// PrivateKey returns the account private key.
|
||||
func (a *ScratchAccount) PrivateKey(ctx context.Context) (e2types.PrivateKey, error) {
|
||||
if a.privKey == nil {
|
||||
return nil, errors.New("no private key available")
|
||||
}
|
||||
return a.privKey, nil
|
||||
}
|
||||
|
||||
// Path returns the account path.
|
||||
func (a *ScratchAccount) Path() string {
|
||||
return ""
|
||||
|
||||
132
util/validators.go
Normal file
132
util/validators.go
Normal file
@@ -0,0 +1,132 @@
|
||||
// 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 util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
eth2client "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"
|
||||
)
|
||||
|
||||
// ParseValidators parses input to obtain the list of validators.
|
||||
func ParseValidators(ctx context.Context, validatorsProvider eth2client.ValidatorsProvider, validatorsStr []string, stateID string) ([]*apiv1.Validator, error) {
|
||||
validators := make([]*apiv1.Validator, 0, len(validatorsStr))
|
||||
for i := range validatorsStr {
|
||||
if strings.Contains(validatorsStr[i], "-") {
|
||||
// Range.
|
||||
bits := strings.Split(validatorsStr[i], "-")
|
||||
if len(bits) != 2 {
|
||||
return nil, fmt.Errorf("invalid range %s", validatorsStr[i])
|
||||
}
|
||||
low, err := strconv.ParseUint(bits[0], 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "invalid range start")
|
||||
}
|
||||
high, err := strconv.ParseUint(bits[1], 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "invalid range end")
|
||||
}
|
||||
indices := make([]phase0.ValidatorIndex, 0)
|
||||
for index := low; index <= high; index++ {
|
||||
indices = append(indices, phase0.ValidatorIndex(index))
|
||||
}
|
||||
rangeValidators, err := validatorsProvider.Validators(ctx, stateID, indices)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("failed to obtain validators %s", validatorsStr[i]))
|
||||
}
|
||||
for _, validator := range rangeValidators {
|
||||
validators = append(validators, validator)
|
||||
}
|
||||
} else {
|
||||
validator, err := ParseValidator(ctx, validatorsProvider, validatorsStr[i], stateID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("unknown validator %s", validatorsStr[i]))
|
||||
}
|
||||
validators = append(validators, validator)
|
||||
}
|
||||
}
|
||||
return validators, nil
|
||||
}
|
||||
|
||||
// ParseValidator parses input to obtain the validator.
|
||||
func ParseValidator(ctx context.Context,
|
||||
validatorsProvider eth2client.ValidatorsProvider,
|
||||
validatorStr string,
|
||||
stateID string,
|
||||
) (
|
||||
*apiv1.Validator,
|
||||
error,
|
||||
) {
|
||||
var validators map[phase0.ValidatorIndex]*apiv1.Validator
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(validatorStr, "0x"):
|
||||
// A public key.
|
||||
data, err := hex.DecodeString(strings.TrimPrefix(validatorStr, "0x"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse validator public key")
|
||||
}
|
||||
pubKey := phase0.BLSPubKey{}
|
||||
copy(pubKey[:], data)
|
||||
validators, err = validatorsProvider.ValidatorsByPubKey(ctx,
|
||||
stateID,
|
||||
[]phase0.BLSPubKey{pubKey},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain validator information")
|
||||
}
|
||||
case strings.Contains(validatorStr, "/"):
|
||||
// An account.
|
||||
_, account, err := WalletAndAccountFromPath(ctx, validatorStr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to obtain account")
|
||||
}
|
||||
accPubKey, err := BestPublicKey(account)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to obtain public key for account")
|
||||
}
|
||||
pubKey := phase0.BLSPubKey{}
|
||||
copy(pubKey[:], accPubKey.Marshal())
|
||||
validators, err = validatorsProvider.ValidatorsByPubKey(ctx,
|
||||
stateID,
|
||||
[]phase0.BLSPubKey{pubKey},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain validator information")
|
||||
}
|
||||
default:
|
||||
// An index.
|
||||
index, err := strconv.ParseUint(validatorStr, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse validator index")
|
||||
}
|
||||
validators, err = validatorsProvider.Validators(ctx, stateID, []phase0.ValidatorIndex{phase0.ValidatorIndex(index)})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain validator information")
|
||||
}
|
||||
}
|
||||
// Validator is first entry in the map.
|
||||
for _, validator := range validators {
|
||||
return validator, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("unknown validator")
|
||||
}
|
||||
Reference in New Issue
Block a user