mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 23:18:15 -05:00
prysmctl: changeblstoexecutionaddress ( withdrawals) (#11790)
* adding unit test for withdrawal * updating to handle api changes * Update cmd/prysmctl/withdrawal/withdrawal.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update cmd/prysmctl/withdrawal/cmd.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update cmd/prysmctl/withdrawal/withdrawal.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update cmd/prysmctl/withdrawal/cmd.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update cmd/prysmctl/withdrawal/withdrawal.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update cmd/prysmctl/withdrawal/withdrawal.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update cmd/prysmctl/withdrawal/withdrawal.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * updating based on feedback * rewording prints * Update cmd/prysmctl/withdrawal/withdrawal.go Co-authored-by: Potuz <potuz@prysmaticlabs.com> * add error handling when status not ok * addressing some of the feedback, adding new tests * adding unit test with error return * adjusting based on review comments * addressing more comments and fixing unit test * updating prompts * migrating some functions to api/client * removing unused function * adding review feedback * removing unused functions * migrating command, old command under accounts still exist * reverting tos.go * fixing bazel lint * fixing build * resolving unused parameter caught by deepsource * fixing unit tests * Update cmd/prysmctl/validator/cmd.go Co-authored-by: Potuz <potuz@prysmaticlabs.com> * Update cmd/prysmctl/validator/cmd.go Co-authored-by: Potuz <potuz@prysmaticlabs.com> * adding in website * addressing review comment * adding new verify only flag * Update cmd/prysmctl/validator/withdraw.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update cmd/prysmctl/validator/withdraw.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update cmd/prysmctl/validator/withdraw.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update cmd/prysmctl/validator/withdraw.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update api/client/beacon/client.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update api/client/beacon/client.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update api/client/beacon/client.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update api/client/beacon/client.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update cmd/prysmctl/validator/withdraw.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * addressing comments * fixing unit test * Update cmd/prysmctl/validator/cmd.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update cmd/prysmctl/validator/cmd.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update cmd/prysmctl/validator/cmd.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update cmd/prysmctl/validator/cmd.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * address comments * Update cmd/prysmctl/validator/cmd.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * adjusting object for new provided format * adding omit empty * fixing unit test, and linting * adjusting based on cli changes * fixing test data and metadata property * addressing backwards compatability * reverting based on review comment Co-authored-by: Radosław Kapka <rkapka@wp.pl> Co-authored-by: Potuz <potuz@prysmaticlabs.com> Co-authored-by: Raul Jordan <raul@prysmaticlabs.com> Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
This commit is contained in:
@@ -28,13 +28,14 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
getSignedBlockPath = "/eth/v2/beacon/blocks"
|
||||
getBlockRootPath = "/eth/v1/beacon/blocks/{{.Id}}/root"
|
||||
getForkForStatePath = "/eth/v1/beacon/states/{{.Id}}/fork"
|
||||
getWeakSubjectivityPath = "/eth/v1/beacon/weak_subjectivity"
|
||||
getForkSchedulePath = "/eth/v1/config/fork_schedule"
|
||||
getStatePath = "/eth/v2/debug/beacon/states"
|
||||
getNodeVersionPath = "/eth/v1/node/version"
|
||||
getSignedBlockPath = "/eth/v2/beacon/blocks"
|
||||
getBlockRootPath = "/eth/v1/beacon/blocks/{{.Id}}/root"
|
||||
getForkForStatePath = "/eth/v1/beacon/states/{{.Id}}/fork"
|
||||
getWeakSubjectivityPath = "/eth/v1/beacon/weak_subjectivity"
|
||||
getForkSchedulePath = "/eth/v1/config/fork_schedule"
|
||||
getStatePath = "/eth/v2/debug/beacon/states"
|
||||
getNodeVersionPath = "/eth/v1/node/version"
|
||||
changeBLStoExecutionPath = "/eth/v1/beacon/pool/bls_to_execution_changes"
|
||||
)
|
||||
|
||||
// StateOrBlockId represents the block_id / state_id parameters that several of the Eth Beacon API methods accept.
|
||||
@@ -146,7 +147,6 @@ func withSSZEncoding() reqOption {
|
||||
// get is a generic, opinionated GET function to reduce boilerplate amongst the getters in this package.
|
||||
func (c *Client) get(ctx context.Context, path string, opts ...reqOption) ([]byte, error) {
|
||||
u := c.baseURL.ResolveReference(&url.URL{Path: path})
|
||||
log.Printf("requesting %s", u.String())
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -343,6 +343,60 @@ func (c *Client) GetWeakSubjectivity(ctx context.Context) (*WeakSubjectivityData
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SubmitChangeBLStoExecution calls a beacon API endpoint to set the withdrawal addresses based on the given signed messages.
|
||||
// If the API responds with something other than OK there will be failure messages associated to the corresponding request message.
|
||||
func (c *Client) SubmitChangeBLStoExecution(ctx context.Context, request []*apimiddleware.SignedBLSToExecutionChangeJson) error {
|
||||
u := c.baseURL.ResolveReference(&url.URL{Path: changeBLStoExecutionPath})
|
||||
body, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to marshal JSON")
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, u.String(), bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "invalid format, failed to create new POST request object")
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp, err := c.hc.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = resp.Body.Close()
|
||||
}()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
decoder := json.NewDecoder(resp.Body)
|
||||
decoder.DisallowUnknownFields()
|
||||
errorJson := &apimiddleware.IndexedVerificationFailureErrorJson{}
|
||||
if err := decoder.Decode(errorJson); err != nil {
|
||||
return errors.Wrapf(err, "failed to decode error JSON for %s", resp.Request.URL)
|
||||
}
|
||||
for _, failure := range errorJson.Failures {
|
||||
w := request[failure.Index].Message
|
||||
log.WithFields(log.Fields{
|
||||
"validator_index": w.ValidatorIndex,
|
||||
"withdrawal_address": w.ToExecutionAddress,
|
||||
}).Error(failure.Message)
|
||||
}
|
||||
return errors.Errorf("POST error %d: %s", errorJson.Code, errorJson.Message)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBLStoExecutionChanges gets all the set withdrawal messages in the node's operation pool.
|
||||
// Returns a struct representation of json response.
|
||||
func (c *Client) GetBLStoExecutionChanges(ctx context.Context) (*apimiddleware.BLSToExecutionChangesPoolResponseJson, error) {
|
||||
body, err := c.get(ctx, changeBLStoExecutionPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
poolResponse := &apimiddleware.BLSToExecutionChangesPoolResponseJson{}
|
||||
err = json.Unmarshal(body, poolResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return poolResponse, nil
|
||||
}
|
||||
|
||||
func non200Err(response *http.Response) error {
|
||||
bodyBytes, err := io.ReadAll(response.Body)
|
||||
var body string
|
||||
|
||||
@@ -15,8 +15,8 @@ go_library(
|
||||
"//cmd/prysmctl/db:go_default_library",
|
||||
"//cmd/prysmctl/deprecated:go_default_library",
|
||||
"//cmd/prysmctl/p2p:go_default_library",
|
||||
"//cmd/prysmctl/signing:go_default_library",
|
||||
"//cmd/prysmctl/testnet:go_default_library",
|
||||
"//cmd/prysmctl/validator:go_default_library",
|
||||
"//cmd/prysmctl/weaksubjectivity:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_urfave_cli_v2//:go_default_library",
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/prysmctl/db"
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/prysmctl/deprecated"
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/prysmctl/p2p"
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/prysmctl/signing"
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/prysmctl/testnet"
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/prysmctl/validator"
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/prysmctl/weaksubjectivity"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
@@ -36,5 +36,5 @@ func init() {
|
||||
prysmctlCommands = append(prysmctlCommands, p2p.Commands...)
|
||||
prysmctlCommands = append(prysmctlCommands, testnet.Commands...)
|
||||
prysmctlCommands = append(prysmctlCommands, weaksubjectivity.Commands...)
|
||||
prysmctlCommands = append(prysmctlCommands, signing.Commands...)
|
||||
prysmctlCommands = append(prysmctlCommands, validator.Commands...)
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["cmd.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v3/cmd/prysmctl/signing",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//cmd:go_default_library",
|
||||
"//cmd/validator/accounts:go_default_library",
|
||||
"//cmd/validator/flags:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//runtime/tos:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_urfave_cli_v2//:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -1,64 +0,0 @@
|
||||
package signing
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd"
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/validator/accounts"
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/validator/flags"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v3/runtime/tos"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var Commands = []*cli.Command{
|
||||
{
|
||||
Name: "sign",
|
||||
Usage: "signs a message and broadcasts it to the network through the beacon node",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "voluntary-exit",
|
||||
Description: "Performs a voluntary exit on selected accounts",
|
||||
Flags: cmd.WrapFlags([]cli.Flag{
|
||||
flags.WalletDirFlag,
|
||||
flags.WalletPasswordFileFlag,
|
||||
flags.AccountPasswordFileFlag,
|
||||
flags.VoluntaryExitPublicKeysFlag,
|
||||
flags.BeaconRPCProviderFlag,
|
||||
flags.Web3SignerURLFlag,
|
||||
flags.Web3SignerPublicValidatorKeysFlag,
|
||||
flags.InteropNumValidators,
|
||||
flags.InteropStartIndex,
|
||||
cmd.GrpcMaxCallRecvMsgSizeFlag,
|
||||
flags.CertFlag,
|
||||
flags.GrpcHeadersFlag,
|
||||
flags.GrpcRetriesFlag,
|
||||
flags.GrpcRetryDelayFlag,
|
||||
flags.ExitAllFlag,
|
||||
flags.ForceExitFlag,
|
||||
features.Mainnet,
|
||||
features.PraterTestnet,
|
||||
features.RopstenTestnet,
|
||||
features.SepoliaTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
Before: func(cliCtx *cli.Context) error {
|
||||
if err := cmd.LoadFlagsFromConfig(cliCtx, cliCtx.Command.Flags); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tos.VerifyTosAcceptedOrPrompt(cliCtx); err != nil {
|
||||
return err
|
||||
}
|
||||
return features.ConfigureValidator(cliCtx)
|
||||
},
|
||||
Action: func(cliCtx *cli.Context) error {
|
||||
if err := accounts.AccountsExit(cliCtx, os.Stdin); err != nil {
|
||||
log.WithError(err).Fatal("Could not perform voluntary exit")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
41
cmd/prysmctl/validator/BUILD.bazel
Normal file
41
cmd/prysmctl/validator/BUILD.bazel
Normal file
@@ -0,0 +1,41 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"cmd.go",
|
||||
"withdraw.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v3/cmd/prysmctl/validator",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//api/client/beacon:go_default_library",
|
||||
"//beacon-chain/rpc/apimiddleware:go_default_library",
|
||||
"//cmd:go_default_library",
|
||||
"//cmd/validator/accounts:go_default_library",
|
||||
"//cmd/validator/flags:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//runtime/tos:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_logrusorgru_aurora//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_urfave_cli_v2//:go_default_library",
|
||||
"@io_opencensus_go//trace:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["withdraw_test.go"],
|
||||
data = glob(["testdata/**"]),
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/rpc/apimiddleware:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||
"@com_github_urfave_cli_v2//:go_default_library",
|
||||
],
|
||||
)
|
||||
137
cmd/prysmctl/validator/cmd.go
Normal file
137
cmd/prysmctl/validator/cmd.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/logrusorgru/aurora"
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd"
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/validator/accounts"
|
||||
"github.com/prysmaticlabs/prysm/v3/cmd/validator/flags"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v3/runtime/tos"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
BeaconHostFlag = &cli.StringFlag{
|
||||
Name: "beacon-node-host",
|
||||
Usage: "host:port for beacon node to query",
|
||||
Value: "127.0.0.1:3500",
|
||||
}
|
||||
PathFlag = &cli.StringFlag{
|
||||
Name: "path",
|
||||
Aliases: []string{"p"},
|
||||
Usage: "path to the signed withdrawal messages JSON",
|
||||
}
|
||||
ConfirmFlag = &cli.BoolFlag{
|
||||
Name: "confirm",
|
||||
Aliases: []string{"c"},
|
||||
Usage: "WARNING: User confirms and accepts responsibility of all input data provided and actions for setting their withdrawal address for their validator key. " +
|
||||
"This action is not reversible and withdrawal addresses can not be changed once set.",
|
||||
}
|
||||
VerifyOnlyFlag = &cli.BoolFlag{
|
||||
Name: "verify-only",
|
||||
Aliases: []string{"vo"},
|
||||
Usage: "overrides withdrawal command to only verify whether requests are in the pool and does not submit withdrawal requests",
|
||||
}
|
||||
)
|
||||
|
||||
var Commands = []*cli.Command{
|
||||
{
|
||||
Name: "validator",
|
||||
Aliases: []string{"v", "sign"}, // remove sign command should be depreciated but having as backwards compatability.
|
||||
Usage: "commands that affect the state of validators such as exiting or withdrawing",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "withdraw",
|
||||
Aliases: []string{"w"},
|
||||
Usage: "Assign Ethereum withdrawal addresses to validator keys. WARNING: once set values are included they can no longer be updated.",
|
||||
Flags: []cli.Flag{
|
||||
BeaconHostFlag,
|
||||
PathFlag,
|
||||
ConfirmFlag,
|
||||
VerifyOnlyFlag,
|
||||
cmd.ConfigFileFlag,
|
||||
cmd.AcceptTosFlag,
|
||||
},
|
||||
Before: func(cliCtx *cli.Context) error {
|
||||
if err := cmd.LoadFlagsFromConfig(cliCtx, cliCtx.Command.Flags); err != nil {
|
||||
return err
|
||||
}
|
||||
au := aurora.NewAurora(true)
|
||||
if !cliCtx.Bool(cmd.AcceptTosFlag.Name) || !cliCtx.Bool(ConfirmFlag.Name) {
|
||||
fmt.Println(au.Red("===============IMPORTANT==============="))
|
||||
fmt.Println(au.Red("Please read the following carefully"))
|
||||
fmt.Print("This action will allow the partial withdrawal of amounts over the 32 staked ETH in your active validator balance. \n" +
|
||||
"You will also be entitled to the full withdrawal of the entire validator balance if your validator has exited. \n" +
|
||||
"Please navigate to our website (https://docs.prylabs.network/) and make sure you understand the full implications of setting your withdrawal address. \n")
|
||||
fmt.Println(au.Red("THIS ACTION WILL NOT BE REVERSIBLE ONCE INCLUDED. "))
|
||||
fmt.Println(au.Red("You will NOT be able to change the address again once changed. "))
|
||||
return fmt.Errorf("both the `--%s` and `--%s` flags are required to run this command. \n"+
|
||||
"By providing these flags the user has read and accepts the TERMS AND CONDITIONS: https://github.com/prysmaticlabs/prysm/blob/master/TERMS_OF_SERVICE.md "+
|
||||
"and confirms the action of setting withdrawals addresses", cmd.AcceptTosFlag.Name, ConfirmFlag.Name)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
},
|
||||
Action: func(cliCtx *cli.Context) error {
|
||||
if cliCtx.Bool(VerifyOnlyFlag.Name) {
|
||||
if err := verifyWithdrawalsInPool(cliCtx); err != nil {
|
||||
log.WithError(err).Fatal("Could not verify withdrawal addresses")
|
||||
}
|
||||
} else {
|
||||
if err := setWithdrawalAddresses(cliCtx); err != nil {
|
||||
log.WithError(err).Fatal("Could not set withdrawal addresses")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "exit",
|
||||
Aliases: []string{"e", "voluntary-exit"},
|
||||
Usage: "Performs a voluntary exit on selected accounts",
|
||||
Flags: cmd.WrapFlags([]cli.Flag{
|
||||
flags.WalletDirFlag,
|
||||
flags.WalletPasswordFileFlag,
|
||||
flags.AccountPasswordFileFlag,
|
||||
flags.VoluntaryExitPublicKeysFlag,
|
||||
flags.BeaconRPCProviderFlag,
|
||||
flags.Web3SignerURLFlag,
|
||||
flags.Web3SignerPublicValidatorKeysFlag,
|
||||
flags.InteropNumValidators,
|
||||
flags.InteropStartIndex,
|
||||
cmd.GrpcMaxCallRecvMsgSizeFlag,
|
||||
flags.CertFlag,
|
||||
flags.GrpcHeadersFlag,
|
||||
flags.GrpcRetriesFlag,
|
||||
flags.GrpcRetryDelayFlag,
|
||||
flags.ExitAllFlag,
|
||||
flags.ForceExitFlag,
|
||||
features.Mainnet,
|
||||
features.PraterTestnet,
|
||||
features.RopstenTestnet,
|
||||
features.SepoliaTestnet,
|
||||
cmd.AcceptTosFlag,
|
||||
}),
|
||||
Before: func(cliCtx *cli.Context) error {
|
||||
if err := cmd.LoadFlagsFromConfig(cliCtx, cliCtx.Command.Flags); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tos.VerifyTosAcceptedOrPrompt(cliCtx); err != nil {
|
||||
return err
|
||||
}
|
||||
return features.ConfigureValidator(cliCtx)
|
||||
},
|
||||
Action: func(cliCtx *cli.Context) error {
|
||||
if err := accounts.AccountsExit(cliCtx, os.Stdin); err != nil {
|
||||
log.WithError(err).Fatal("Could not perform voluntary exit")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
1
cmd/prysmctl/validator/testdata/change-operations-multiple.json
vendored
Normal file
1
cmd/prysmctl/validator/testdata/change-operations-multiple.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
[{"message":{"validator_index":"0","from_bls_pubkey":"0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c","to_execution_address":"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"},"signature":"0xb6e640f0fc58e9f22585dbf434b6a0e8fc36b98e2f2a963e158716cfc84034141289f7898027de1ec56754937f1a837e01c7b066a6a56af7a379f8aec823d050788a5ecc799e9bc39f73d45b7c389c961cbaace61823e4c7bf2f93bd06c03127"},{"message":{"validator_index":"1","from_bls_pubkey":"0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b","to_execution_address":"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"},"signature":"0xa97103e15d3dbdaa75fb15cea782e4a11329eea77d155864ec682d7907b3b70c7771960bef7be1b1c4e08fe735888b950c1a22053f6049b35736f48e6dd018392efa3896c9e427ea4e100e86e9131b5ea2673388a4bf188407a630ba405b7dc5"}]
|
||||
1
cmd/prysmctl/validator/testdata/change-operations-multiple_notfound.json
vendored
Normal file
1
cmd/prysmctl/validator/testdata/change-operations-multiple_notfound.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
[{"message":{"validator_index":"3","from_bls_pubkey":"0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c","to_execution_address":"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"},"signature":"0xb6e640f0fc58e9f22585dbf434b6a0e8fc36b98e2f2a963e158716cfc84034141289f7898027de1ec56754937f1a837e01c7b066a6a56af7a379f8aec823d050788a5ecc799e9bc39f73d45b7c389c961cbaace61823e4c7bf2f93bd06c03127"},{"message":{"validator_index":"5","from_bls_pubkey":"0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b","to_execution_address":"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"},"signature":"0xa97103e15d3dbdaa75fb15cea782e4a11329eea77d155864ec682d7907b3b70c7771960bef7be1b1c4e08fe735888b950c1a22053f6049b35736f48e6dd018392efa3896c9e427ea4e100e86e9131b5ea2673388a4bf188407a630ba405b7dc5"}]
|
||||
1
cmd/prysmctl/validator/testdata/change-operations.json
vendored
Executable file
1
cmd/prysmctl/validator/testdata/change-operations.json
vendored
Executable file
@@ -0,0 +1 @@
|
||||
[{"message":{"validator_index":"1","from_bls_pubkey":"0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b","to_execution_address":"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"},"signature":"0xa97103e15d3dbdaa75fb15cea782e4a11329eea77d155864ec682d7907b3b70c7771960bef7be1b1c4e08fe735888b950c1a22053f6049b35736f48e6dd018392efa3896c9e427ea4e100e86e9131b5ea2673388a4bf188407a630ba405b7dc5"}]
|
||||
1
cmd/prysmctl/validator/testdata/staking-cli-change-operations-multiple.json
vendored
Normal file
1
cmd/prysmctl/validator/testdata/staking-cli-change-operations-multiple.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
[{"message":{"validator_index":"0","from_bls_pubkey":"a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c","to_execution_address":"a94f5374fce5edbc8e2a8697c15331677e6ebf0b"},"signature":"b6e640f0fc58e9f22585dbf434b6a0e8fc36b98e2f2a963e158716cfc84034141289f7898027de1ec56754937f1a837e01c7b066a6a56af7a379f8aec823d050788a5ecc799e9bc39f73d45b7c389c961cbaace61823e4c7bf2f93bd06c03127", "metadata":{ "network_name": "mainnet", "genesis_validators_root": "4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95", "deposit_cli_version": "2.4.0"}},{"message":{"validator_index":"1","from_bls_pubkey":"b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b","to_execution_address":"a94f5374fce5edbc8e2a8697c15331677e6ebf0b"},"signature":"a97103e15d3dbdaa75fb15cea782e4a11329eea77d155864ec682d7907b3b70c7771960bef7be1b1c4e08fe735888b950c1a22053f6049b35736f48e6dd018392efa3896c9e427ea4e100e86e9131b5ea2673388a4bf188407a630ba405b7dc5", "metadata":{ "network_name": "mainnet", "genesis_validators_root": "4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95", "deposit_cli_version": "2.4.0"}}]
|
||||
171
cmd/prysmctl/validator/withdraw.go
Normal file
171
cmd/prysmctl/validator/withdraw.go
Normal file
@@ -0,0 +1,171 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/logrusorgru/aurora"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/api/client/beacon"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/rpc/apimiddleware"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
func setWithdrawalAddresses(c *cli.Context) error {
|
||||
ctx, span := trace.StartSpan(c.Context, "withdrawal.setWithdrawalAddresses")
|
||||
defer span.End()
|
||||
au := aurora.NewAurora(true)
|
||||
beaconNodeHost := c.String(BeaconHostFlag.Name)
|
||||
if !c.IsSet(PathFlag.Name) {
|
||||
return fmt.Errorf("no --%s flag value was provided", PathFlag.Name)
|
||||
}
|
||||
setWithdrawalAddressJsons, err := getWithdrawalMessagesFromPathFlag(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, request := range setWithdrawalAddressJsons {
|
||||
fmt.Println("SETTING VALIDATOR INDEX " + au.Red(request.Message.ValidatorIndex).String() + " TO WITHDRAWAL ADDRESS " + au.Red(request.Message.ToExecutionAddress).String())
|
||||
}
|
||||
return callWithdrawalEndpoints(ctx, beaconNodeHost, setWithdrawalAddressJsons)
|
||||
}
|
||||
|
||||
func getWithdrawalMessagesFromPathFlag(c *cli.Context) ([]*apimiddleware.SignedBLSToExecutionChangeJson, error) {
|
||||
setWithdrawalAddressJsons := make([]*apimiddleware.SignedBLSToExecutionChangeJson, 0)
|
||||
foundFilePaths, err := findWithdrawalFiles(c.String(PathFlag.Name))
|
||||
if err != nil {
|
||||
return setWithdrawalAddressJsons, errors.Wrap(err, "failed to find withdrawal files")
|
||||
}
|
||||
for _, foundFilePath := range foundFilePaths {
|
||||
b, err := os.ReadFile(filepath.Clean(foundFilePath))
|
||||
if err != nil {
|
||||
return setWithdrawalAddressJsons, errors.Wrap(err, "failed to open file")
|
||||
}
|
||||
var to []*apimiddleware.SignedBLSToExecutionChangeJson
|
||||
if err := json.Unmarshal(b, &to); err != nil {
|
||||
log.Warnf("provided file: %s, is not a list of signed withdrawal messages", foundFilePath)
|
||||
continue
|
||||
}
|
||||
// verify 0x from file and add if needed
|
||||
for i, obj := range to {
|
||||
if len(obj.Message.FromBLSPubkey) == fieldparams.BLSPubkeyLength*2 {
|
||||
to[i].Message.FromBLSPubkey = fmt.Sprintf("0x%s", obj.Message.FromBLSPubkey)
|
||||
}
|
||||
if len(obj.Message.ToExecutionAddress) == common.AddressLength*2 {
|
||||
to[i].Message.ToExecutionAddress = fmt.Sprintf("0x%s", obj.Message.ToExecutionAddress)
|
||||
}
|
||||
if len(obj.Signature) == fieldparams.BLSSignatureLength*2 {
|
||||
to[i].Signature = fmt.Sprintf("0x%s", obj.Signature)
|
||||
}
|
||||
setWithdrawalAddressJsons = append(setWithdrawalAddressJsons, &apimiddleware.SignedBLSToExecutionChangeJson{
|
||||
Message: &apimiddleware.BLSToExecutionChangeJson{
|
||||
ValidatorIndex: to[i].Message.ValidatorIndex,
|
||||
FromBLSPubkey: to[i].Message.FromBLSPubkey,
|
||||
ToExecutionAddress: to[i].Message.ToExecutionAddress,
|
||||
},
|
||||
Signature: to[i].Signature,
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
if len(setWithdrawalAddressJsons) == 0 {
|
||||
return setWithdrawalAddressJsons, errors.New("the list of signed requests is empty")
|
||||
}
|
||||
return setWithdrawalAddressJsons, nil
|
||||
}
|
||||
|
||||
func callWithdrawalEndpoints(ctx context.Context, host string, request []*apimiddleware.SignedBLSToExecutionChangeJson) error {
|
||||
client, err := beacon.NewClient(host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := client.SubmitChangeBLStoExecution(ctx, request); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Infof("Successfully published messages to update %d withdrawal addresses.", len(request))
|
||||
return checkIfWithdrawsAreInPool(ctx, client, request)
|
||||
}
|
||||
|
||||
func checkIfWithdrawsAreInPool(ctx context.Context, client *beacon.Client, request []*apimiddleware.SignedBLSToExecutionChangeJson) error {
|
||||
log.Info("Verifying requested withdrawal messages known to node...")
|
||||
poolResponse, err := client.GetBLStoExecutionChanges(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
requestMap := make(map[string]string)
|
||||
for _, w := range request {
|
||||
requestMap[w.Message.ValidatorIndex] = w.Message.ToExecutionAddress
|
||||
}
|
||||
totalMessages := len(requestMap)
|
||||
for _, resp := range poolResponse.Data {
|
||||
value, found := requestMap[resp.Message.ValidatorIndex]
|
||||
if found && value == resp.Message.ToExecutionAddress {
|
||||
delete(requestMap, resp.Message.ValidatorIndex)
|
||||
}
|
||||
}
|
||||
if len(requestMap) != 0 {
|
||||
for key, address := range requestMap {
|
||||
log.WithFields(log.Fields{
|
||||
"validator_index": key,
|
||||
"execution_address:": address,
|
||||
}).Warn("Set withdrawal address message not found in the node's operations pool.")
|
||||
}
|
||||
log.Warn("Please check before resubmitting. Set withdrawal address messages that were not found in the pool may have been already included into a block.")
|
||||
} else {
|
||||
log.Infof("All (total:%d) signed withdrawal messages were found in the pool.", totalMessages)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func findWithdrawalFiles(path string) ([]string, error) {
|
||||
var foundpaths []string
|
||||
maxdepth := 3
|
||||
cleanpath := filepath.Clean(path)
|
||||
if err := filepath.WalkDir(cleanpath, func(s string, d fs.DirEntry, e error) error {
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
if d.IsDir() && strings.Count(cleanpath, string(os.PathSeparator)) > maxdepth {
|
||||
return fs.SkipDir
|
||||
}
|
||||
|
||||
if filepath.Ext(d.Name()) == ".json" {
|
||||
foundpaths = append(foundpaths, s)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, errors.Wrap(err, "unable to find compatible files")
|
||||
}
|
||||
if len(foundpaths) == 0 {
|
||||
return nil, errors.New("no compatible files were found")
|
||||
}
|
||||
log.Infof("found JSON files for setting withdrawals: %v", foundpaths)
|
||||
return foundpaths, nil
|
||||
}
|
||||
|
||||
func verifyWithdrawalsInPool(c *cli.Context) error {
|
||||
ctx, span := trace.StartSpan(c.Context, "withdrawal.verifyWithdrawalsInPool")
|
||||
defer span.End()
|
||||
beaconNodeHost := c.String(BeaconHostFlag.Name)
|
||||
if !c.IsSet(PathFlag.Name) {
|
||||
return fmt.Errorf("no --%s flag value was provided", PathFlag.Name)
|
||||
}
|
||||
client, err := beacon.NewClient(beaconNodeHost)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
request, err := getWithdrawalMessagesFromPathFlag(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return checkIfWithdrawsAreInPool(ctx, client, request)
|
||||
}
|
||||
305
cmd/prysmctl/validator/withdraw_test.go
Normal file
305
cmd/prysmctl/validator/withdraw_test.go
Normal file
@@ -0,0 +1,305 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/rpc/apimiddleware"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
logtest "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func TestCallWithdrawalEndpoint(t *testing.T) {
|
||||
file := "./testdata/change-operations.json"
|
||||
baseurl := "127.0.0.1:3500"
|
||||
l, err := net.Listen("tcp", baseurl)
|
||||
require.NoError(t, err)
|
||||
srv := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(200)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if r.Method == http.MethodGet {
|
||||
b, err := os.ReadFile(filepath.Clean(file))
|
||||
require.NoError(t, err)
|
||||
var to []*apimiddleware.SignedBLSToExecutionChangeJson
|
||||
err = json.Unmarshal(b, &to)
|
||||
require.NoError(t, err)
|
||||
err = json.NewEncoder(w).Encode(&apimiddleware.BLSToExecutionChangesPoolResponseJson{
|
||||
Data: to,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}))
|
||||
err = srv.Listener.Close()
|
||||
require.NoError(t, err)
|
||||
srv.Listener = l
|
||||
srv.Start()
|
||||
defer srv.Close()
|
||||
hook := logtest.NewGlobal()
|
||||
|
||||
app := cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.String("beacon-node-host", baseurl, "")
|
||||
set.String("path", file, "")
|
||||
set.Bool("confirm", true, "")
|
||||
set.Bool("accept-terms-of-use", true, "")
|
||||
assert.NoError(t, set.Set("beacon-node-host", baseurl))
|
||||
assert.NoError(t, set.Set("path", file))
|
||||
cliCtx := cli.NewContext(&app, set, nil)
|
||||
|
||||
err = setWithdrawalAddresses(cliCtx)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.LogsContain(t, hook, "Successfully published")
|
||||
}
|
||||
|
||||
func TestCallWithdrawalEndpoint_Mutiple(t *testing.T) {
|
||||
file := "./testdata/change-operations-multiple.json"
|
||||
baseurl := "127.0.0.1:3500"
|
||||
l, err := net.Listen("tcp", baseurl)
|
||||
require.NoError(t, err)
|
||||
srv := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(200)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if r.Method == http.MethodGet {
|
||||
b, err := os.ReadFile(filepath.Clean(file))
|
||||
require.NoError(t, err)
|
||||
var to []*apimiddleware.SignedBLSToExecutionChangeJson
|
||||
err = json.Unmarshal(b, &to)
|
||||
require.NoError(t, err)
|
||||
err = json.NewEncoder(w).Encode(&apimiddleware.BLSToExecutionChangesPoolResponseJson{
|
||||
Data: to,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}))
|
||||
err = srv.Listener.Close()
|
||||
require.NoError(t, err)
|
||||
srv.Listener = l
|
||||
srv.Start()
|
||||
defer srv.Close()
|
||||
hook := logtest.NewGlobal()
|
||||
|
||||
app := cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.String("beacon-node-host", baseurl, "")
|
||||
set.String("path", file, "")
|
||||
set.Bool("confirm", true, "")
|
||||
set.Bool("accept-terms-of-use", true, "")
|
||||
assert.NoError(t, set.Set("beacon-node-host", baseurl))
|
||||
assert.NoError(t, set.Set("path", file))
|
||||
cliCtx := cli.NewContext(&app, set, nil)
|
||||
|
||||
err = setWithdrawalAddresses(cliCtx)
|
||||
require.NoError(t, err)
|
||||
assert.LogsContain(t, hook, "Successfully published")
|
||||
assert.LogsContain(t, hook, "to update 2 withdrawal")
|
||||
assert.LogsContain(t, hook, "All (total:2) signed withdrawal messages were found in the pool.")
|
||||
assert.LogsDoNotContain(t, hook, "Set withdrawal address message not found in the node's operations pool.")
|
||||
}
|
||||
|
||||
func TestCallWithdrawalEndpoint_Mutiple_stakingcli(t *testing.T) {
|
||||
stakingcliFile := "./testdata/staking-cli-change-operations-multiple.json"
|
||||
file := "./testdata/change-operations-multiple.json"
|
||||
baseurl := "127.0.0.1:3500"
|
||||
l, err := net.Listen("tcp", baseurl)
|
||||
require.NoError(t, err)
|
||||
srv := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(200)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if r.Method == http.MethodGet {
|
||||
b, err := os.ReadFile(filepath.Clean(file))
|
||||
require.NoError(t, err)
|
||||
var to []*apimiddleware.SignedBLSToExecutionChangeJson
|
||||
err = json.Unmarshal(b, &to)
|
||||
require.NoError(t, err)
|
||||
err = json.NewEncoder(w).Encode(&apimiddleware.BLSToExecutionChangesPoolResponseJson{
|
||||
Data: to,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}))
|
||||
err = srv.Listener.Close()
|
||||
require.NoError(t, err)
|
||||
srv.Listener = l
|
||||
srv.Start()
|
||||
defer srv.Close()
|
||||
hook := logtest.NewGlobal()
|
||||
|
||||
app := cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.String("beacon-node-host", baseurl, "")
|
||||
set.String("path", stakingcliFile, "")
|
||||
set.Bool("confirm", true, "")
|
||||
set.Bool("accept-terms-of-use", true, "")
|
||||
assert.NoError(t, set.Set("beacon-node-host", baseurl))
|
||||
assert.NoError(t, set.Set("path", stakingcliFile))
|
||||
cliCtx := cli.NewContext(&app, set, nil)
|
||||
|
||||
err = setWithdrawalAddresses(cliCtx)
|
||||
require.NoError(t, err)
|
||||
assert.LogsContain(t, hook, "Successfully published")
|
||||
assert.LogsContain(t, hook, "to update 2 withdrawal")
|
||||
assert.LogsContain(t, hook, "All (total:2) signed withdrawal messages were found in the pool.")
|
||||
assert.LogsDoNotContain(t, hook, "Set withdrawal address message not found in the node's operations pool.")
|
||||
}
|
||||
|
||||
func TestCallWithdrawalEndpoint_Mutiple_notfound(t *testing.T) {
|
||||
respFile := "./testdata/change-operations-multiple_notfound.json"
|
||||
file := "./testdata/change-operations-multiple.json"
|
||||
baseurl := "127.0.0.1:3500"
|
||||
l, err := net.Listen("tcp", baseurl)
|
||||
require.NoError(t, err)
|
||||
srv := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(200)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if r.Method == http.MethodGet {
|
||||
b, err := os.ReadFile(filepath.Clean(respFile))
|
||||
require.NoError(t, err)
|
||||
var to []*apimiddleware.SignedBLSToExecutionChangeJson
|
||||
err = json.Unmarshal(b, &to)
|
||||
require.NoError(t, err)
|
||||
err = json.NewEncoder(w).Encode(&apimiddleware.BLSToExecutionChangesPoolResponseJson{
|
||||
Data: to,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}))
|
||||
err = srv.Listener.Close()
|
||||
require.NoError(t, err)
|
||||
srv.Listener = l
|
||||
srv.Start()
|
||||
defer srv.Close()
|
||||
hook := logtest.NewGlobal()
|
||||
|
||||
app := cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.String("beacon-node-host", baseurl, "")
|
||||
set.String("path", file, "")
|
||||
set.Bool("confirm", true, "")
|
||||
set.Bool("accept-terms-of-use", true, "")
|
||||
assert.NoError(t, set.Set("beacon-node-host", baseurl))
|
||||
assert.NoError(t, set.Set("path", file))
|
||||
cliCtx := cli.NewContext(&app, set, nil)
|
||||
|
||||
err = setWithdrawalAddresses(cliCtx)
|
||||
require.NoError(t, err)
|
||||
assert.LogsContain(t, hook, "Successfully published")
|
||||
assert.LogsContain(t, hook, "to update 2 withdrawal")
|
||||
assert.LogsContain(t, hook, "Set withdrawal address message not found in the node's operations pool.")
|
||||
assert.LogsContain(t, hook, "Please check before resubmitting. Set withdrawal address messages that were not found in the pool may have been already included into a block.")
|
||||
assert.LogsDoNotContain(t, hook, "Set withdrawal address message found in the node's operations pool.")
|
||||
}
|
||||
|
||||
func TestCallWithdrawalEndpoint_Empty(t *testing.T) {
|
||||
baseurl := "127.0.0.1:3500"
|
||||
content := []byte("[]")
|
||||
tmpfile, err := os.CreateTemp("./testdata", "*.json")
|
||||
require.NoError(t, err)
|
||||
_, err = tmpfile.Write(content)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err := os.Remove(tmpfile.Name())
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
app := cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.String("beacon-node-host", baseurl, "")
|
||||
set.String("path", tmpfile.Name(), "")
|
||||
set.Bool("confirm", true, "")
|
||||
set.Bool("accept-terms-of-use", true, "")
|
||||
assert.NoError(t, set.Set("beacon-node-host", baseurl))
|
||||
assert.NoError(t, set.Set("path", tmpfile.Name()))
|
||||
cliCtx := cli.NewContext(&app, set, nil)
|
||||
err = setWithdrawalAddresses(cliCtx)
|
||||
assert.ErrorContains(t, "the list of signed requests is empty", err)
|
||||
}
|
||||
|
||||
func TestCallWithdrawalEndpoint_Errors(t *testing.T) {
|
||||
file := "./testdata/change-operations.json"
|
||||
baseurl := "127.0.0.1:3500"
|
||||
l, err := net.Listen("tcp", baseurl)
|
||||
require.NoError(t, err)
|
||||
srv := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(400)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
err = json.NewEncoder(w).Encode(&apimiddleware.IndexedVerificationFailureErrorJson{
|
||||
Failures: []*apimiddleware.SingleIndexedVerificationFailureJson{
|
||||
{Index: 0, Message: "Could not validate SignedBLSToExecutionChange"},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}))
|
||||
err = srv.Listener.Close()
|
||||
require.NoError(t, err)
|
||||
srv.Listener = l
|
||||
srv.Start()
|
||||
defer srv.Close()
|
||||
hook := logtest.NewGlobal()
|
||||
|
||||
app := cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.String("beacon-node-host", baseurl, "")
|
||||
set.String("path", file, "")
|
||||
set.Bool("confirm", true, "")
|
||||
set.Bool("accept-terms-of-use", true, "")
|
||||
assert.NoError(t, set.Set("beacon-node-host", baseurl))
|
||||
assert.NoError(t, set.Set("path", file))
|
||||
cliCtx := cli.NewContext(&app, set, nil)
|
||||
|
||||
err = setWithdrawalAddresses(cliCtx)
|
||||
assert.ErrorContains(t, "POST error", err)
|
||||
|
||||
assert.LogsContain(t, hook, "Could not validate SignedBLSToExecutionChange")
|
||||
}
|
||||
|
||||
func TestVerifyWithdrawal_Mutiple(t *testing.T) {
|
||||
file := "./testdata/change-operations-multiple.json"
|
||||
baseurl := "127.0.0.1:3500"
|
||||
l, err := net.Listen("tcp", baseurl)
|
||||
require.NoError(t, err)
|
||||
srv := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(200)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if r.Method == http.MethodGet {
|
||||
b, err := os.ReadFile(filepath.Clean(file))
|
||||
require.NoError(t, err)
|
||||
var to []*apimiddleware.SignedBLSToExecutionChangeJson
|
||||
err = json.Unmarshal(b, &to)
|
||||
require.NoError(t, err)
|
||||
err = json.NewEncoder(w).Encode(&apimiddleware.BLSToExecutionChangesPoolResponseJson{
|
||||
Data: to,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}))
|
||||
err = srv.Listener.Close()
|
||||
require.NoError(t, err)
|
||||
srv.Listener = l
|
||||
srv.Start()
|
||||
defer srv.Close()
|
||||
hook := logtest.NewGlobal()
|
||||
|
||||
app := cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.String("beacon-node-host", baseurl, "")
|
||||
set.String("path", file, "")
|
||||
set.Bool("confirm", true, "")
|
||||
set.Bool("accept-terms-of-use", true, "")
|
||||
set.Bool("verify-only", true, "")
|
||||
assert.NoError(t, set.Set("beacon-node-host", baseurl))
|
||||
assert.NoError(t, set.Set("path", file))
|
||||
cliCtx := cli.NewContext(&app, set, nil)
|
||||
|
||||
err = verifyWithdrawalsInPool(cliCtx)
|
||||
require.NoError(t, err)
|
||||
assert.LogsContain(t, hook, "All (total:2) signed withdrawal messages were found in the pool.")
|
||||
assert.LogsDoNotContain(t, hook, "set withdrawal address message not found in the node's operations pool.")
|
||||
}
|
||||
@@ -186,7 +186,7 @@ var Commands = &cli.Command{
|
||||
return features.ConfigureValidator(cliCtx)
|
||||
},
|
||||
Action: func(cliCtx *cli.Context) error {
|
||||
log.Info("This command will be deprecated in the future in favor of `prysmctl sign validator-exit`")
|
||||
log.Info("This command will be deprecated in the future in favor of `prysmctl validator exit`")
|
||||
if err := AccountsExit(cliCtx, os.Stdin); err != nil {
|
||||
log.WithError(err).Fatal("Could not perform voluntary exit")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user