mirror of
https://github.com/wealdtech/ethdo.git
synced 2026-01-10 14:37:57 -05:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
394b4a7cd2 | ||
|
|
fd574aae34 | ||
|
|
7fe503f51d | ||
|
|
6bfb0ef098 | ||
|
|
46c667d387 | ||
|
|
50f4a9cace | ||
|
|
cd20875744 | ||
|
|
84f682a0da | ||
|
|
6389b7dfbd | ||
|
|
0ef65b8bda | ||
|
|
4426c3279d |
2
.github/workflows/golangci-lint.yml
vendored
2
.github/workflows/golangci-lint.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
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.29
|
||||
version: v1.45
|
||||
|
||||
# Optional: working directory, useful for monorepos
|
||||
# working-directory: somedir
|
||||
|
||||
56
.github/workflows/release.yml
vendored
56
.github/workflows/release.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ^1.16
|
||||
go-version: ^1.17
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
@@ -50,11 +50,11 @@ jobs:
|
||||
|
||||
- name: Fetch xgo
|
||||
run: |
|
||||
go install github.com/wealdtech/xgo@latest
|
||||
go install github.com/crazy-max/xgo@v0.14.0
|
||||
|
||||
- name: Cross-compile linux
|
||||
run: |
|
||||
xgo -v -x -ldflags="-X github.com/wealdtech/ethdo/cmd.ReleaseVersion=${RELEASE_VERSION}" --targets="linux/amd64" github.com/wealdtech/ethdo
|
||||
xgo -v -x -ldflags="-X github.com/wealdtech/ethdo/cmd.ReleaseVersion=${RELEASE_VERSION}" --targets="linux/amd64,linux/arm64" github.com/wealdtech/ethdo
|
||||
|
||||
- name: Cross-compile windows
|
||||
run: |
|
||||
@@ -72,11 +72,11 @@ jobs:
|
||||
tar zcf ethdo-${RELEASE_VERSION}-linux-amd64.tar.gz ethdo
|
||||
sha256sum ethdo-${RELEASE_VERSION}-linux-amd64.tar.gz >ethdo-${RELEASE_VERSION}-linux-amd64.sha256
|
||||
|
||||
# - name: Create linux ARM64 tgz file
|
||||
# run: |
|
||||
# mv ethdo-linux-arm64 ethdo
|
||||
# tar zcf ethdo-${RELEASE_VERSION}-linux-arm64.tar.gz ethdo
|
||||
# sha256sum ethdo-${RELEASE_VERSION}-linux-arm64.tar.gz >ethdo-${RELEASE_VERSION}-linux-arm64.sha256
|
||||
- name: Create linux ARM64 tgz file
|
||||
run: |
|
||||
mv ethdo-linux-arm64 ethdo
|
||||
tar zcf ethdo-${RELEASE_VERSION}-linux-arm64.tar.gz ethdo
|
||||
sha256sum ethdo-${RELEASE_VERSION}-linux-arm64.tar.gz >ethdo-${RELEASE_VERSION}-linux-arm64.sha256
|
||||
|
||||
- name: Create release
|
||||
id: create_release
|
||||
@@ -133,24 +133,24 @@ jobs:
|
||||
asset_name: ethdo-${{ env.RELEASE_VERSION }}-linux-amd64.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
|
||||
# - name: Upload linux ARM64 checksum file
|
||||
# id: upload-release-asset-linux-arm64-checksum
|
||||
# uses: actions/upload-release-asset@v1
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# with:
|
||||
# upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
# asset_path: ./ethdo-${{ env.RELEASE_VERSION }}-linux-arm64.sha256
|
||||
# asset_name: ethdo-${{ env.RELEASE_VERSION }}-linux-arm64.sha256
|
||||
# asset_content_type: text/plain
|
||||
- name: Upload linux ARM64 checksum file
|
||||
id: upload-release-asset-linux-arm64-checksum
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./ethdo-${{ env.RELEASE_VERSION }}-linux-arm64.sha256
|
||||
asset_name: ethdo-${{ env.RELEASE_VERSION }}-linux-arm64.sha256
|
||||
asset_content_type: text/plain
|
||||
|
||||
# - name: Upload linux ARM64 tgz file
|
||||
# id: upload-release-asset-linux-arm64
|
||||
# uses: actions/upload-release-asset@v1
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# with:
|
||||
# upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
# asset_path: ./ethdo-${{ env.RELEASE_VERSION }}-linux-arm64.tar.gz
|
||||
# asset_name: ethdo-${{ env.RELEASE_VERSION }}-linux-arm64.tar.gz
|
||||
# asset_content_type: application/gzip
|
||||
- name: Upload linux ARM64 tgz file
|
||||
id: upload-release-asset-linux-arm64
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./ethdo-${{ env.RELEASE_VERSION }}-linux-arm64.tar.gz
|
||||
asset_name: ethdo-${{ env.RELEASE_VERSION }}-linux-arm64.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
|
||||
13
CHANGELOG.md
13
CHANGELOG.md
@@ -1,3 +1,16 @@
|
||||
1.22.0:
|
||||
- add "ropsten" to the list of supported networks
|
||||
|
||||
1.21.0:
|
||||
- add "validator credentials get"
|
||||
|
||||
1.20.0:
|
||||
- add "chain queues"
|
||||
|
||||
1.19.1:
|
||||
- add the ability to import keystores to ethdo wallets
|
||||
- use defaults to connect to beacon nodes if no explicit connection defined
|
||||
|
||||
1.19.0:
|
||||
- add "epoch summary"
|
||||
|
||||
|
||||
29
README.md
29
README.md
@@ -11,6 +11,7 @@ A command-line tool for managing common tasks in Ethereum 2.
|
||||
- [Binaries](#binaries)
|
||||
- [Docker](#docker)
|
||||
- [Source](#source)
|
||||
- [Setting up](#setting-up)
|
||||
- [Usage](#usage)
|
||||
- [Maintainers](#maintainers)
|
||||
- [Contribute](#contribute)
|
||||
@@ -61,13 +62,37 @@ docker run --network=host ethdo chain status
|
||||
|
||||
Alternatively, if the beacon node is running in a separate docker container a shared network can be created with `docker network create eth2` and accessed by adding `--network=eth2` added to both the beacon node and `ethdo` containers.
|
||||
|
||||
## Setting up
|
||||
|
||||
`ethdo` needs a connection to a beacon node for many of its features. `ethdo` can connect to any beacon node that fully supports the [standard REST API](https://ethereum.github.io/beacon-APIs/). The following changes are required to beacon nodes to make this available.
|
||||
|
||||
### Lighthouse
|
||||
Lighthouse disables the REST API by default. To enable it, the beacon node must be started with the `--http` parameter. If you want to access the REST API from a remote server then you should also look to change the `--http-address` and `--http-allow-origin` options as per the Lighthouse documentation.
|
||||
|
||||
The default port for the REST API is 5052, which can be changed with the `--http-port` parameter.
|
||||
|
||||
### Nimbus
|
||||
Nimbus disables the REST API by default. To enable it, the beacon node must be started with the `--rest` parameter. If you want to access the REST API from a remote server then you should also look to change the `--rest-address` and `--rest-allow-origin` options as per the Nimbus documentation.
|
||||
|
||||
The default port for the REST API is 5052, which can be changed with the `--rest-port` parameter.
|
||||
|
||||
### Prysm
|
||||
Prysm enables the REST API by default. You will need to add the parameter `--grpc-max-msg-size 268435456` to be obtain to obtain large sets of information such as the list of current validators. If you want to access the REST API from a remote server then you should also look to change the `--grpc-gateway-host` and `--grpc-gateway-corsdomain` options as per the Prysm documentation.
|
||||
|
||||
The default port for the REST API is 3500, which can be changed with the `--grpc-gateway-port` parameter.
|
||||
|
||||
### Teku
|
||||
Teku disables the REST API by default. To enable it, the beacon node must be started with the `--rest-api-enabled` parameter. If you want to access the REST API from a remote server then you should also look to change the `--rest-api-interface`, `--rest-api-host-allowlist` and `--rest-api-cors-origins` options as per the Teku documentation.
|
||||
|
||||
The default port for the REST API is 5051, which can be changed with the `--rest-api-port` parameter.
|
||||
|
||||
## Usage
|
||||
|
||||
ethdo contains a large number of features that are useful for day-to-day interactions with the Ethereum 2 blockchain.
|
||||
`ethdo` contains a large number of features that are useful for day-to-day interactions with the Ethereum 2 blockchain.
|
||||
|
||||
### Wallets and accounts
|
||||
|
||||
ethdo uses the [go-eth2-wallet](https://github.com/wealdtech/go-eth2-wallet) system to provide unified access to different wallet types. When on the filesystem the locations of the created wallets and accounts are:
|
||||
`ethdo` uses the [go-eth2-wallet](https://github.com/wealdtech/go-eth2-wallet) system to provide unified access to different wallet types. When on the filesystem the locations of the created wallets and accounts are:
|
||||
|
||||
- for Linux: $HOME/.config/ethereum2/wallets
|
||||
- for OSX: $HOME/Library/Application Support/ethereum2/wallets
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2019, 2020 Weald Technology Trading
|
||||
// Copyright © 2019 - 2022 Weald Technology Trading.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
@@ -16,6 +16,7 @@ package accountimport
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -27,12 +28,14 @@ import (
|
||||
)
|
||||
|
||||
type dataIn struct {
|
||||
timeout time.Duration
|
||||
wallet e2wtypes.Wallet
|
||||
key []byte
|
||||
accountName string
|
||||
passphrase string
|
||||
walletPassphrase string
|
||||
timeout time.Duration
|
||||
wallet e2wtypes.Wallet
|
||||
key []byte
|
||||
accountName string
|
||||
passphrase string
|
||||
walletPassphrase string
|
||||
keystore []byte
|
||||
keystorePassphrase []byte
|
||||
}
|
||||
|
||||
func input(ctx context.Context) (*dataIn, error) {
|
||||
@@ -74,14 +77,55 @@ func input(ctx context.Context) (*dataIn, error) {
|
||||
// Wallet passphrase.
|
||||
data.walletPassphrase = util.GetWalletPassphrase()
|
||||
|
||||
// Key.
|
||||
if viper.GetString("key") == "" {
|
||||
return nil, errors.New("key is required")
|
||||
if viper.GetString("key") == "" && viper.GetString("keystore") == "" {
|
||||
return nil, errors.New("key or keystore is required")
|
||||
}
|
||||
data.key, err = hex.DecodeString(strings.TrimPrefix(viper.GetString("key"), "0x"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "key is malformed")
|
||||
if viper.GetString("key") != "" && viper.GetString("keystore") != "" {
|
||||
return nil, errors.New("only one of key and keystore is required")
|
||||
}
|
||||
|
||||
if viper.GetString("key") != "" {
|
||||
data.key, err = hex.DecodeString(strings.TrimPrefix(viper.GetString("key"), "0x"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "key is malformed")
|
||||
}
|
||||
}
|
||||
|
||||
if viper.GetString("keystore") != "" {
|
||||
data.keystorePassphrase = []byte(viper.GetString("keystore-passphrase"))
|
||||
if len(data.keystorePassphrase) == 0 {
|
||||
return nil, errors.New("must supply keystore passphrase with keystore-passphrase when supplying keystore")
|
||||
}
|
||||
data.keystore, err = obtainKeystore(viper.GetString("keystore"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "invalid keystore")
|
||||
}
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// obtainKeystore obtains keystore from an input, could be JSON itself or a path to JSON.
|
||||
func obtainKeystore(input string) ([]byte, error) {
|
||||
var err error
|
||||
var data []byte
|
||||
// Input could be JSON or a path to JSON
|
||||
if strings.HasPrefix(input, "{") {
|
||||
// Looks like JSON
|
||||
data = []byte(input)
|
||||
} else {
|
||||
// Assume it's a path to JSON
|
||||
data, err = ioutil.ReadFile(input)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to find deposit data file")
|
||||
}
|
||||
}
|
||||
return data, nil
|
||||
// exitData := &util.ValidatorExitData{}
|
||||
// err = json.Unmarshal(data, exitData)
|
||||
// if err != nil {
|
||||
// return nil, errors.Wrap(err, "data is not valid JSON")
|
||||
// }
|
||||
|
||||
// return exitData, nil
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ func TestInput(t *testing.T) {
|
||||
"account": "Test wallet/Test account",
|
||||
"passphrase": "ce%NohGhah4ye5ra",
|
||||
},
|
||||
err: "key is required",
|
||||
err: "key or keystore is required",
|
||||
},
|
||||
{
|
||||
name: "KeyMalformed",
|
||||
@@ -114,6 +114,26 @@ func TestInput(t *testing.T) {
|
||||
},
|
||||
err: "key is malformed: encoding/hex: invalid byte: U+0069 'i'",
|
||||
},
|
||||
{
|
||||
name: "KeyandKeystore",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "5s",
|
||||
"account": "Test wallet/Test account",
|
||||
"passphrase": "ce%NohGhah4ye5ra",
|
||||
"key": "0x25295f0d1d592a90b333e26e85149708208e9f8e8bc18f6c77bd62f8ad7a6866",
|
||||
"keystore": "{}",
|
||||
},
|
||||
err: "only one of key and keystore is required",
|
||||
},
|
||||
{
|
||||
name: "KeystoreNoKeystorePassphrase",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "5s",
|
||||
"account": "Test wallet/Test account",
|
||||
"keystore": "{}",
|
||||
},
|
||||
err: "must supply keystore passphrase with keystore-passphrase when supplying keystore",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
vars: map[string]interface{}{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2019, 2020 Weald Technology Trading
|
||||
// Copyright © 2019 -2022 Weald Technology Trading
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
@@ -15,9 +15,14 @@ package accountimport
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/wealdtech/ethdo/util"
|
||||
"github.com/wealdtech/go-ecodec"
|
||||
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
|
||||
nd "github.com/wealdtech/go-eth2-wallet-nd/v2"
|
||||
scratch "github.com/wealdtech/go-eth2-wallet-store-scratch"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
@@ -43,9 +48,23 @@ func process(ctx context.Context, data *dataIn) (*dataOut, error) {
|
||||
}()
|
||||
}
|
||||
|
||||
if len(data.key) > 0 {
|
||||
return processFromKey(ctx, data)
|
||||
}
|
||||
if len(data.keystore) > 0 {
|
||||
return processFromKeystore(ctx, data)
|
||||
}
|
||||
return nil, errors.New("unsupported import mechanism")
|
||||
}
|
||||
|
||||
func processFromKey(ctx context.Context, data *dataIn) (*dataOut, error) {
|
||||
results := &dataOut{}
|
||||
|
||||
account, err := data.wallet.(e2wtypes.WalletAccountImporter).ImportAccount(ctx, data.accountName, data.key, []byte(data.passphrase))
|
||||
importer, isImporter := data.wallet.(e2wtypes.WalletAccountImporter)
|
||||
if !isImporter {
|
||||
return nil, fmt.Errorf("%s wallets do not support importing accounts", data.wallet.Type())
|
||||
}
|
||||
account, err := importer.ImportAccount(ctx, data.accountName, data.key, []byte(data.passphrase))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to import account")
|
||||
}
|
||||
@@ -53,3 +72,39 @@ func process(ctx context.Context, data *dataIn) (*dataOut, error) {
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func processFromKeystore(ctx context.Context, data *dataIn) (*dataOut, error) {
|
||||
// Need to import the keystore in to a temporary wallet to fetch the private key.
|
||||
store := scratch.New()
|
||||
encryptor := keystorev4.New()
|
||||
|
||||
// Need to add a couple of fields to the keystore to make it compliant.
|
||||
keystoreData := fmt.Sprintf(`{"name":"Import","encryptor":"keystore",%s`, string(data.keystore[1:]))
|
||||
walletData := fmt.Sprintf(`{"wallet":{"name":"ImportTest","type":"non-deterministic","uuid":"e1526407-1dc7-4f3f-9d05-ab696f40707c","version":1},"accounts":[%s]}`, keystoreData)
|
||||
encryptedData, err := ecodec.Encrypt([]byte(walletData), data.keystorePassphrase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
wallet, err := nd.Import(ctx, encryptedData, data.keystorePassphrase, store, encryptor)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to import wallet")
|
||||
}
|
||||
|
||||
account := <-wallet.Accounts(ctx)
|
||||
privateKeyProvider, isPrivateKeyProvider := account.(e2wtypes.AccountPrivateKeyProvider)
|
||||
if !isPrivateKeyProvider {
|
||||
return nil, errors.New("account does not provide its private key")
|
||||
}
|
||||
if locker, isLocker := account.(e2wtypes.AccountLocker); isLocker {
|
||||
if err = locker.Unlock(ctx, data.keystorePassphrase); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unlock account")
|
||||
}
|
||||
}
|
||||
key, err := privateKeyProvider.PrivateKey(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain private key")
|
||||
}
|
||||
data.key = key.Marshal()
|
||||
// We have the key from the keystore; import it.
|
||||
return processFromKey(ctx, data)
|
||||
}
|
||||
|
||||
@@ -48,10 +48,18 @@ func init() {
|
||||
accountCmd.AddCommand(accountImportCmd)
|
||||
accountFlags(accountImportCmd)
|
||||
accountImportCmd.Flags().String("key", "", "Private key of the account to import (0x...)")
|
||||
accountImportCmd.Flags().String("keystore", "", "Keystore, or path to keystore ")
|
||||
accountImportCmd.Flags().String("keystore-passphrase", "", "Passphrase of keystore")
|
||||
}
|
||||
|
||||
func accountImportBindings() {
|
||||
if err := viper.BindPFlag("key", accountImportCmd.Flags().Lookup("key")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("keystore", accountImportCmd.Flags().Lookup("keystore")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("keystore-passphrase", accountImportCmd.Flags().Lookup("keystore-passphrase")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
75
cmd/chain/queues/command.go
Normal file
75
cmd/chain/queues/command.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// 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 chainqueues
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/wealdtech/ethdo/services/chaintime"
|
||||
)
|
||||
|
||||
type command struct {
|
||||
quiet bool
|
||||
verbose bool
|
||||
debug bool
|
||||
json bool
|
||||
|
||||
// Beacon node connection.
|
||||
timeout time.Duration
|
||||
connection string
|
||||
allowInsecureConnections bool
|
||||
|
||||
// Input.
|
||||
epoch string
|
||||
|
||||
// Data access.
|
||||
eth2Client eth2client.Service
|
||||
validatorsProvider eth2client.ValidatorsProvider
|
||||
chainTime chaintime.Service
|
||||
|
||||
// Output.
|
||||
activationQueue int
|
||||
exitQueue int
|
||||
}
|
||||
|
||||
func newCommand(ctx context.Context) (*command, error) {
|
||||
c := &command{
|
||||
quiet: viper.GetBool("quiet"),
|
||||
verbose: viper.GetBool("verbose"),
|
||||
debug: viper.GetBool("debug"),
|
||||
json: viper.GetBool("json"),
|
||||
}
|
||||
|
||||
// Timeout.
|
||||
if viper.GetDuration("timeout") == 0 {
|
||||
return nil, errors.New("timeout is required")
|
||||
}
|
||||
c.timeout = viper.GetDuration("timeout")
|
||||
|
||||
if viper.GetString("epoch") != "" {
|
||||
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")
|
||||
|
||||
return c, nil
|
||||
}
|
||||
72
cmd/chain/queues/command_internal_test.go
Normal file
72
cmd/chain/queues/command_internal_test.go
Normal file
@@ -0,0 +1,72 @@
|
||||
// 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 chainqueues
|
||||
|
||||
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: "ConnectionMissing",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "5s",
|
||||
"data": "{}",
|
||||
},
|
||||
err: "connection 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
63
cmd/chain/queues/output.go
Normal file
63
cmd/chain/queues/output.go
Normal file
@@ -0,0 +1,63 @@
|
||||
// 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 chainqueues
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type jsonOutput struct {
|
||||
ActivationQueue int `json:"activation_queue"`
|
||||
ExitQueue int `json:"exit_queue"`
|
||||
}
|
||||
|
||||
func (c *command) output(ctx context.Context) (string, error) {
|
||||
if c.quiet {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if c.json {
|
||||
return c.outputJSON(ctx)
|
||||
}
|
||||
return c.outputText(ctx)
|
||||
}
|
||||
|
||||
func (c *command) outputJSON(ctx context.Context) (string, error) {
|
||||
output := &jsonOutput{
|
||||
ActivationQueue: c.activationQueue,
|
||||
ExitQueue: c.exitQueue,
|
||||
}
|
||||
data, err := json.Marshal(output)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
func (c *command) outputText(ctx context.Context) (string, error) {
|
||||
builder := strings.Builder{}
|
||||
|
||||
if c.activationQueue > 0 {
|
||||
builder.WriteString(fmt.Sprintf("Activation queue: %d\n", c.activationQueue))
|
||||
}
|
||||
if c.exitQueue > 0 {
|
||||
builder.WriteString(fmt.Sprintf("Exit queue: %d\n", c.exitQueue))
|
||||
}
|
||||
|
||||
return strings.TrimSuffix(builder.String(), "\n"), nil
|
||||
}
|
||||
82
cmd/chain/queues/process.go
Normal file
82
cmd/chain/queues/process.go
Normal file
@@ -0,0 +1,82 @@
|
||||
// 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 chainqueues
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
"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.
|
||||
if err := c.setup(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
epoch, err := util.ParseEpoch(ctx, c.chainTime, c.epoch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
validators, err := c.validatorsProvider.Validators(ctx, fmt.Sprintf("%d", c.chainTime.FirstSlotOfEpoch(epoch)), nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to obtain validators")
|
||||
}
|
||||
|
||||
for _, validator := range validators {
|
||||
if validator.Validator == nil {
|
||||
continue
|
||||
}
|
||||
if validator.Validator.ActivationEligibilityEpoch <= epoch && validator.Validator.ActivationEpoch > epoch {
|
||||
c.activationQueue++
|
||||
}
|
||||
if validator.Validator.ExitEpoch != 0xffffffffffffffff && validator.Validator.ExitEpoch > epoch {
|
||||
c.exitQueue++
|
||||
}
|
||||
}
|
||||
|
||||
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.validatorsProvider, isProvider = c.eth2Client.(eth2client.ValidatorsProvider)
|
||||
if !isProvider {
|
||||
return errors.New("connection does not provide validator information")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
66
cmd/chain/queues/process_internal_test.go
Normal file
66
cmd/chain/queues/process_internal_test.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 chainqueues
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"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")
|
||||
}
|
||||
|
||||
zerolog.SetGlobalLevel(zerolog.Disabled)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
vars map[string]interface{}
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "InvalidEpoch",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "5s",
|
||||
"epoch": "invalid",
|
||||
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
|
||||
},
|
||||
err: "failed to parse epoch: strconv.ParseInt: parsing \"invalid\": invalid syntax",
|
||||
},
|
||||
}
|
||||
|
||||
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/chain/queues/run.go
Normal file
50
cmd/chain/queues/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 chainqueues
|
||||
|
||||
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
|
||||
}
|
||||
61
cmd/chainqueues.go
Normal file
61
cmd/chainqueues.go
Normal file
@@ -0,0 +1,61 @@
|
||||
// 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"
|
||||
chainqueues "github.com/wealdtech/ethdo/cmd/chain/queues"
|
||||
)
|
||||
|
||||
var chainQueuesCmd = &cobra.Command{
|
||||
Use: "queues",
|
||||
Short: "Show chain queues",
|
||||
Long: `Show beacon chain activation and exit queues. For example:
|
||||
|
||||
ethdo chain queues
|
||||
|
||||
In quiet mode this will return 0 if the entry and exit queues are 0, otherwise 1.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
res, err := chainqueues.Run(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if viper.GetBool("quiet") {
|
||||
return nil
|
||||
}
|
||||
if res != "" {
|
||||
fmt.Println(res)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
chainCmd.AddCommand(chainQueuesCmd)
|
||||
chainFlags(chainQueuesCmd)
|
||||
chainQueuesCmd.Flags().String("epoch", "", "epoch for which to fetch the queues")
|
||||
chainQueuesCmd.Flags().Bool("json", false, "output data in JSON format")
|
||||
}
|
||||
|
||||
func chainQueuesBindings() {
|
||||
if err := viper.BindPFlag("epoch", chainQueuesCmd.Flags().Lookup("epoch")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := viper.BindPFlag("json", chainQueuesCmd.Flags().Lookup("json")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@@ -93,6 +93,8 @@ func includeCommandBindings(cmd *cobra.Command) {
|
||||
blockAnalyzeBindings()
|
||||
case "block/info":
|
||||
blockInfoBindings()
|
||||
case "chain/queues":
|
||||
chainQueuesBindings()
|
||||
case "chain/time":
|
||||
chainTimeBindings()
|
||||
case "chain/verify/signedcontributionandproof":
|
||||
@@ -109,6 +111,8 @@ func includeCommandBindings(cmd *cobra.Command) {
|
||||
synccommitteeInclusionBindings()
|
||||
case "synccommittee/members":
|
||||
synccommitteeMembersBindings()
|
||||
case "validator/credentials/get":
|
||||
validatorCredentialsGetBindings()
|
||||
case "validator/depositdata":
|
||||
validatorDepositdataBindings()
|
||||
case "validator/duties":
|
||||
@@ -211,7 +215,7 @@ func init() {
|
||||
if err := viper.BindPFlag("debug", RootCmd.PersistentFlags().Lookup("debug")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
RootCmd.PersistentFlags().String("connection", "http://localhost:3500", "URL to an Ethereum 2 node's RET API endpoint")
|
||||
RootCmd.PersistentFlags().String("connection", "", "URL to an Ethereum 2 node's RET API endpoint")
|
||||
if err := viper.BindPFlag("connection", RootCmd.PersistentFlags().Lookup("connection")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
89
cmd/validator/credentials/get/command.go
Normal file
89
cmd/validator/credentials/get/command.go
Normal file
@@ -0,0 +1,89 @@
|
||||
// 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 validatorcredentialsget
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
apiv1 "github.com/attestantio/go-eth2-client/api/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type command struct {
|
||||
quiet bool
|
||||
verbose bool
|
||||
debug bool
|
||||
|
||||
// Input.
|
||||
account string
|
||||
index string
|
||||
pubKey string
|
||||
|
||||
// Beacon node connection.
|
||||
timeout time.Duration
|
||||
connection string
|
||||
allowInsecureConnections bool
|
||||
|
||||
// Data access.
|
||||
consensusClient eth2client.Service
|
||||
validatorsProvider eth2client.ValidatorsProvider
|
||||
|
||||
// Output.
|
||||
validator *apiv1.Validator
|
||||
}
|
||||
|
||||
func newCommand(ctx context.Context) (*command, error) {
|
||||
c := &command{
|
||||
quiet: viper.GetBool("quiet"),
|
||||
verbose: viper.GetBool("verbose"),
|
||||
debug: viper.GetBool("debug"),
|
||||
}
|
||||
|
||||
// Timeout.
|
||||
if viper.GetDuration("timeout") == 0 {
|
||||
return nil, errors.New("timeout is required")
|
||||
}
|
||||
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")
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
91
cmd/validator/credentials/get/command_internal_test.go
Normal file
91
cmd/validator/credentials/get/command_internal_test.go
Normal file
@@ -0,0 +1,91 @@
|
||||
// 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 validatorcredentialsget
|
||||
|
||||
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: "ConnectionMissing",
|
||||
vars: map[string]interface{}{
|
||||
"timeout": "5s",
|
||||
"index": "1",
|
||||
},
|
||||
err: "connection 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
44
cmd/validator/credentials/get/output.go
Normal file
44
cmd/validator/credentials/get/output.go
Normal file
@@ -0,0 +1,44 @@
|
||||
// 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 validatorcredentialsget
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (c *command) output(ctx context.Context) (string, error) {
|
||||
if c.quiet {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
builder := strings.Builder{}
|
||||
|
||||
switch c.validator.Validator.WithdrawalCredentials[0] {
|
||||
case 0:
|
||||
builder.WriteString("BLS credentials: ")
|
||||
builder.WriteString(fmt.Sprintf("%#x", c.validator.Validator.WithdrawalCredentials))
|
||||
case 1:
|
||||
builder.WriteString("Ethereum execution address: ")
|
||||
builder.WriteString(fmt.Sprintf("%#x", c.validator.Validator.WithdrawalCredentials[12:]))
|
||||
if c.verbose {
|
||||
builder.WriteString("\n")
|
||||
builder.WriteString("Withdrawal credentials: ")
|
||||
builder.WriteString(fmt.Sprintf("%#x", c.validator.Validator.WithdrawalCredentials))
|
||||
}
|
||||
}
|
||||
|
||||
return builder.String(), nil
|
||||
}
|
||||
139
cmd/validator/credentials/get/process.go
Normal file
139
cmd/validator/credentials/get/process.go
Normal file
@@ -0,0 +1,139 @@
|
||||
// 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 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"
|
||||
)
|
||||
|
||||
func (c *command) process(ctx context.Context) error {
|
||||
// Obtain information we need to process.
|
||||
if err := c.setup(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Work out which validator we are dealing with.
|
||||
if err := c.fetchValidator(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.debug {
|
||||
data, err := json.Marshal(c.validator)
|
||||
if err == nil {
|
||||
fmt.Println(string(data))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) setup(ctx context.Context) error {
|
||||
var err error
|
||||
|
||||
// Connect to the consensus node.
|
||||
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")
|
||||
}
|
||||
|
||||
// Obtain the validators provider.
|
||||
var isProvider bool
|
||||
c.validatorsProvider, isProvider = c.consensusClient.(eth2client.ValidatorsProvider)
|
||||
if !isProvider {
|
||||
return errors.New("consensu node does not provide validator information")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
50
cmd/validator/credentials/get/run.go
Normal file
50
cmd/validator/credentials/get/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 validatorcredentialsget
|
||||
|
||||
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
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2019, 2020 Weald Technology Trading
|
||||
// Copyright © 2019 - 2022 Weald Technology Trading.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
@@ -110,6 +110,7 @@ func validatorDepositDataOutputLaunchpad(datum *dataOut) (string, error) {
|
||||
[4]byte{0x00, 0x00, 0x00, 0x00}: "mainnet",
|
||||
[4]byte{0x00, 0x00, 0x20, 0x09}: "pyrmont",
|
||||
[4]byte{0x00, 0x00, 0x10, 0x20}: "prater",
|
||||
[4]byte{0x80, 0x00, 0x00, 0x69}: "ropsten",
|
||||
}
|
||||
|
||||
if datum.validatorPubKey == nil {
|
||||
|
||||
32
cmd/validatorcredentials.go
Normal file
32
cmd/validatorcredentials.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// 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 (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// validatorCredentialsCmd represents the validator credentials command
|
||||
var validatorCredentialsCmd = &cobra.Command{
|
||||
Use: "credentials",
|
||||
Short: "Manage Ethereum consensu validator credentials",
|
||||
Long: `Manage Ethereum consensu validator credentials.`,
|
||||
}
|
||||
|
||||
func init() {
|
||||
validatorCmd.AddCommand(validatorCredentialsCmd)
|
||||
}
|
||||
|
||||
func validatorCredentialsFlags(cmd *cobra.Command) {
|
||||
}
|
||||
65
cmd/validatorcredentialsget.go
Normal file
65
cmd/validatorcredentialsget.go
Normal file
@@ -0,0 +1,65 @@
|
||||
// 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"
|
||||
validatorcredentialsget "github.com/wealdtech/ethdo/cmd/validator/credentials/get"
|
||||
)
|
||||
|
||||
var validatorCredentialsGetCmd = &cobra.Command{
|
||||
Use: "get",
|
||||
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
|
||||
|
||||
In quiet mode this will return 0 if the validator exists, otherwise 1.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
res, err := validatorcredentialsget.Run(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if viper.GetBool("quiet") {
|
||||
return nil
|
||||
}
|
||||
if res != "" {
|
||||
fmt.Println(res)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
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 {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright © 2019, 2020 Weald Technology Trading
|
||||
// Copyright © 2019 - 2022 Weald Technology Trading
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
@@ -55,7 +55,7 @@ func init() {
|
||||
validatorDepositDataCmd.Flags().String("withdrawaladdress", "", "Ethereum 1 address of the account to which the validator funds will be withdrawn")
|
||||
validatorDepositDataCmd.Flags().String("depositvalue", "", "Value of the amount to be deposited")
|
||||
validatorDepositDataCmd.Flags().Bool("raw", false, "Print raw deposit data transaction data")
|
||||
validatorDepositDataCmd.Flags().String("forkversion", "", "Use a hard-coded fork version (default is to fetch it from the node)")
|
||||
validatorDepositDataCmd.Flags().String("forkversion", "", "Use a hard-coded fork version (default is to use mainnet value)")
|
||||
validatorDepositDataCmd.Flags().Bool("launchpad", false, "Print launchpad-compatible JSON")
|
||||
}
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ func validatorInfoAccount(ctx context.Context, eth2Client eth2client.Service) (e
|
||||
index,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain validator information.")
|
||||
return nil, errors.Wrap(err, "failed to obtain validator information")
|
||||
}
|
||||
if len(validators) == 0 {
|
||||
return nil, errors.New("unknown validator index")
|
||||
|
||||
@@ -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.19.0)"
|
||||
var ReleaseVersion = "local build (latest release 1.22.0)"
|
||||
|
||||
// versionCmd represents the version command
|
||||
var versionCmd = &cobra.Command{
|
||||
|
||||
@@ -35,11 +35,11 @@ import (
|
||||
func TestInput(t *testing.T) {
|
||||
require.NoError(t, e2types.InitBLS())
|
||||
|
||||
dir := os.TempDir()
|
||||
datFile := filepath.Join(dir, "backup.dat")
|
||||
err := ioutil.WriteFile(datFile, []byte("dummy"), 0600)
|
||||
dir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
// defer os.RemoveAll(dir)
|
||||
datFile := filepath.Join(dir, "backup.dat")
|
||||
require.NoError(t, ioutil.WriteFile(datFile, []byte("dummy"), 0600))
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
store := scratch.New()
|
||||
require.NoError(t, e2wallet.UseStore(store))
|
||||
|
||||
@@ -37,10 +37,10 @@ func TestProcess(t *testing.T) {
|
||||
"ed2166659f7b5412a169ec83627386bc6ff1a31e67735d405b2bf7cb122ad7ced35c87e42c8e8f7ba90b5899a94be506687a9c5b353af2a216018d9f1bf61745a5",
|
||||
}
|
||||
|
||||
dir := os.TempDir()
|
||||
datFile := filepath.Join(dir, "backup.dat")
|
||||
err := ioutil.WriteFile(datFile, export, 0600)
|
||||
dir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
datFile := filepath.Join(dir, "backup.dat")
|
||||
require.NoError(t, ioutil.WriteFile(datFile, export, 0600))
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
tests := []struct {
|
||||
|
||||
@@ -174,6 +174,13 @@ Public key: 0x99b1f1d84d76185466d86c34bde1101316afddae76217aa86cd066979b19858c2c
|
||||
$ ethdo account import --account=Validators/123 --key=6dd12d588d1c05ba40e80880ac7e894aa20babdbf16da52eae26b3f267d68032 --passphrase="my account secret"
|
||||
```
|
||||
|
||||
You can also import from an existing keystore such as those generated by the deposit CLI. For this you need the keystore and the keystore passphrase. For example:
|
||||
|
||||
```sh
|
||||
$ ethdo account import --account=Validators/123 --keystore=/path/to/keystore.json --keystore-passphrase="the keystore secret" --passphrase="my account secret"
|
||||
```
|
||||
`--keystore` can either be the path to the keystore file, or the contents of the keystore file.
|
||||
|
||||
#### `info`
|
||||
|
||||
`ethdo account info` provides information about the given account. Options include:
|
||||
@@ -349,6 +356,17 @@ Seconds per slot: 12
|
||||
Slots per epoch: 32
|
||||
```
|
||||
|
||||
#### `queues`
|
||||
|
||||
`ethdo chain queues` obtains the activation and exit queue lengths of an Ethereum chain from the node's point of view. Options include:
|
||||
- `epoch` show the queue length at a given epoch
|
||||
- `json` provide JSON output
|
||||
|
||||
```sh
|
||||
$ ethdo chain queues
|
||||
Activation queue: 14798
|
||||
```
|
||||
|
||||
#### `status`
|
||||
|
||||
`ethdo chain status` obtains the status of an Ethereum 2 chain from the node's point of view. Options include:
|
||||
@@ -542,14 +560,26 @@ $ ethdo synccommittee members
|
||||
|
||||
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
|
||||
|
||||
```sh
|
||||
$ ethdo validator credentials get --account=Validators/1
|
||||
```
|
||||
|
||||
#### `depositdata`
|
||||
|
||||
`ethdo validator depositdata` generates the data required to deposit one or more Ethereum 2 validators. Options include:
|
||||
- `withdrawalaccount` specify the account to be used for the withdrawal credentials (if withdrawalpubkey is not supplied)
|
||||
- `withdrawaladdress` specify the Ethereum execution address to be used for the withdrawal credentials (if withdrawalpubkey is not supplied)
|
||||
- `withdrawalpubkey` specify the public key to be used for the withdrawal credentials (if withdrawalaccount is not supplied)
|
||||
- `validatoraccount` specify the account to be used for the validator
|
||||
- `depositvalue` specify the amount of the deposit
|
||||
- `forkversion` specify the fork version for the deposit signature; this should not be included unless the deposit is being generated offline. Note that supplying an incorrect value could result in the loss of your deposit, so only supply this value if you are sure you know what you are doing
|
||||
- `forkversion` specify the fork version for the deposit signature; this defaults to mainnet. Note that supplying an incorrect value could result in the loss of your deposit, so only supply this value if you are sure you know what you are doing. You can find the value for other chains by fetching the value supplied in "Genesis fork version" of the `ethdo chain info` command
|
||||
- `raw` generate raw hex output that can be supplied as the data to an Ethereum 1 deposit transaction
|
||||
|
||||
#### `exit`
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,17 +21,41 @@ import (
|
||||
"time"
|
||||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
"github.com/attestantio/go-eth2-client/auto"
|
||||
"github.com/attestantio/go-eth2-client/http"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
// defaultBeaconNodeAddresses are default REST endpoint addresses for beacon nodes.
|
||||
var defaultBeaconNodeAddresses = []string{
|
||||
"localhost:5052", // Lighthouse, Nimbus
|
||||
"localhost:5051", // Teku
|
||||
"localhost:3500", // Prysm
|
||||
}
|
||||
|
||||
// ConnectToBeaconNode connects to a beacon node at the given address.
|
||||
func ConnectToBeaconNode(ctx context.Context, address string, timeout time.Duration, allowInsecure bool) (eth2client.Service, error) {
|
||||
if timeout == 0 {
|
||||
return nil, errors.New("no timeout specified")
|
||||
}
|
||||
|
||||
if address != "" {
|
||||
// We have an explicit address; use it.
|
||||
return connectToBeaconNode(ctx, address, timeout, allowInsecure)
|
||||
}
|
||||
|
||||
// Try the defaults.
|
||||
for _, address := range defaultBeaconNodeAddresses {
|
||||
client, err := connectToBeaconNode(ctx, address, timeout, allowInsecure)
|
||||
if err == nil {
|
||||
return client, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("failed to connect to any beacon node")
|
||||
}
|
||||
|
||||
func connectToBeaconNode(ctx context.Context, address string, timeout time.Duration, allowInsecure bool) (eth2client.Service, error) {
|
||||
if !strings.HasPrefix(address, "http") {
|
||||
address = fmt.Sprintf("http://%s", address)
|
||||
}
|
||||
@@ -49,10 +73,10 @@ func ConnectToBeaconNode(ctx context.Context, address string, timeout time.Durat
|
||||
fmt.Println("Connections to remote beacon nodes should be secure. This warning can be silenced with --allow-insecure-connections")
|
||||
}
|
||||
}
|
||||
eth2Client, err := auto.New(ctx,
|
||||
auto.WithLogLevel(zerolog.Disabled),
|
||||
auto.WithAddress(address),
|
||||
auto.WithTimeout(timeout),
|
||||
eth2Client, err := http.New(ctx,
|
||||
http.WithLogLevel(zerolog.Disabled),
|
||||
http.WithAddress(address),
|
||||
http.WithTimeout(timeout),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to connect to beacon node")
|
||||
|
||||
@@ -27,6 +27,7 @@ var networks = map[string]string{
|
||||
"07b39f4fde4a38bace212b546dac87c58dfe3fdc": "Medalla",
|
||||
"8c5fecdc472e27bc447696f431e425d02dd46a8c": "Pyrmont",
|
||||
"ff50ed3d0ec03ac01d4c79aad74928bff48a7b2b": "Prater",
|
||||
"6f22ffbc56eff051aecf839396dd1ed9ad6bba9d": "Ropsten",
|
||||
}
|
||||
|
||||
// Network returns the name of the network., calculated from the deposit contract information.
|
||||
|
||||
Reference in New Issue
Block a user