mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-23 11:57:18 +00:00
8428a79971
* Enable whitespace linter & fix findings * Fix new finding * fix new violation --------- Co-authored-by: Radosław Kapka <rkapka@wp.pl> Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com> Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
198 lines
6.9 KiB
Go
198 lines
6.9 KiB
Go
package validator
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/logrusorgru/aurora"
|
|
"github.com/pkg/errors"
|
|
"github.com/prysmaticlabs/prysm/v4/api/client/beacon"
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/apimiddleware"
|
|
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
|
|
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
|
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. Error:%s", foundFilePath, err.Error())
|
|
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
|
|
}
|
|
fork, err := client.GetFork(ctx, "head")
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not retrieve current fork information")
|
|
}
|
|
spec, err := client.GetConfigSpec(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
forkEpoch, ok := spec.Data["CAPELLA_FORK_EPOCH"]
|
|
if !ok {
|
|
return errors.New("Configs used on beacon node do not contain CAPELLA_FORK_EPOCH")
|
|
}
|
|
capellaForkEpoch, err := strconv.Atoi(forkEpoch)
|
|
if err != nil {
|
|
return errors.New("could not convert CAPELLA_FORK_EPOCH to a number")
|
|
}
|
|
if fork.Epoch < primitives.Epoch(capellaForkEpoch) {
|
|
return errors.New("setting withdrawals using the BLStoExecutionChange endpoint is only available after the Capella/Shanghai hard fork.")
|
|
}
|
|
err = client.SubmitChangeBLStoExecution(ctx, request)
|
|
if err != nil && strings.Contains(err.Error(), "POST error") {
|
|
// just log the error, so we can check the pool for partial inclusions.
|
|
log.Error(err)
|
|
} else if err != nil {
|
|
return err
|
|
} else {
|
|
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)
|
|
log.Infof("There are a total of %d messages known to the node's pool.", len(poolResponse.Data))
|
|
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)
|
|
}
|