Skip to content

Commit

Permalink
Merge pull request #125 from Layr-Labs/jb/calldata-sim2
Browse files Browse the repository at this point in the history
`--print-calldata` for later proof submission
  • Loading branch information
jbrower95 authored Aug 13, 2024
2 parents 46cda8c + 01c6b6c commit dd69d47
Show file tree
Hide file tree
Showing 7 changed files with 362 additions and 167 deletions.
18 changes: 13 additions & 5 deletions cli/core/beaconClient.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ type BeaconClient interface {

type beaconClient struct {
eth2client eth2client.Service
verbose bool
}

func NewBeaconClient(endpoint string) (BeaconClient, context.CancelFunc, error) {
beaconClient := beaconClient{}
func NewBeaconClient(endpoint string, verbose bool) (BeaconClient, context.CancelFunc, error) {
beaconClient := beaconClient{verbose: verbose}
ctx, cancel := context.WithCancel(context.Background())

client, err := http.New(ctx,
Expand All @@ -42,7 +43,10 @@ func NewBeaconClient(endpoint string) (BeaconClient, context.CancelFunc, error)
if err != nil {
return nil, cancel, err
}
log.Info().Msgf("Connected to %s\n", client.Name())

if verbose {
log.Info().Msgf("Connected to %s\n", client.Name())
}

beaconClient.eth2client = client
return &beaconClient, cancel, nil
Expand All @@ -64,7 +68,9 @@ func (b *beaconClient) GetBeaconHeader(ctx context.Context, blockId string) (*v1
func (b *beaconClient) GetBeaconState(ctx context.Context, stateId string) (*spec.VersionedBeaconState, error) {
timeout, _ := time.ParseDuration("200s")
if provider, ok := b.eth2client.(eth2client.BeaconStateProvider); ok {
log.Info().Msgf("downloading beacon state %s", stateId)
if b.verbose {
log.Info().Msgf("downloading beacon state %s", stateId)
}
opts := &api.BeaconStateOpts{State: stateId, Common: api.CommonOpts{
Timeout: timeout,
}}
Expand All @@ -77,7 +83,9 @@ func (b *beaconClient) GetBeaconState(ctx context.Context, stateId string) (*spe
return nil, errors.New("beacon state is nil")
}

log.Info().Msg("finished download")
if b.verbose {
log.Info().Msg("finished download")
}
return beaconState.Data, nil
}

Expand Down
19 changes: 13 additions & 6 deletions cli/core/checkpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"github.com/fatih/color"
)

func SubmitCheckpointProof(ctx context.Context, owner, eigenpodAddress string, chainId *big.Int, proof *eigenpodproofs.VerifyCheckpointProofsCallParams, eth *ethclient.Client, batchSize uint64, noPrompt bool) ([]*types.Transaction, error) {
func SubmitCheckpointProof(ctx context.Context, owner, eigenpodAddress string, chainId *big.Int, proof *eigenpodproofs.VerifyCheckpointProofsCallParams, eth *ethclient.Client, batchSize uint64, noPrompt bool, noSend bool) ([]*types.Transaction, error) {
tracing := GetContextTracingCallbacks(ctx)

allProofChunks := chunk(proof.BalanceProofs, batchSize)
Expand All @@ -32,7 +32,7 @@ func SubmitCheckpointProof(ctx context.Context, owner, eigenpodAddress string, c
tracing.OnStartSection("pepe::proof::checkpoint::batch::submit", map[string]string{
"chunk": fmt.Sprintf("%d", i),
})
txn, err := SubmitCheckpointProofBatch(ctx, owner, eigenpodAddress, chainId, proof.ValidatorBalancesRootProof, balanceProofs, eth)
txn, err := SubmitCheckpointProofBatch(ctx, owner, eigenpodAddress, chainId, proof.ValidatorBalancesRootProof, balanceProofs, eth, noSend)
tracing.OnEndSection()
if err != nil {
// failed to submit batch.
Expand All @@ -43,19 +43,26 @@ func SubmitCheckpointProof(ctx context.Context, owner, eigenpodAddress string, c
tracing.OnStartSection("pepe::proof::checkpoint::batch::wait", map[string]string{
"chunk": fmt.Sprintf("%d", i),
})
bind.WaitMined(ctx, eth, txn)

if !noSend {
bind.WaitMined(ctx, eth, txn)
}
tracing.OnEndSection()
color.Green("OK")
}

color.Green("Complete! re-run with `status` to see the updated Eigenpod state.")
if !noSend {
color.Green("Complete! re-run with `status` to see the updated Eigenpod state.")
} else {
color.Yellow("Submit these proofs to network and re-run with `status` to see the updated Eigenpod state.")
}
return transactions, nil
}

func SubmitCheckpointProofBatch(ctx context.Context, owner, eigenpodAddress string, chainId *big.Int, proof *eigenpodproofs.ValidatorBalancesRootProof, balanceProofs []*eigenpodproofs.BalanceProof, eth *ethclient.Client) (*types.Transaction, error) {
func SubmitCheckpointProofBatch(ctx context.Context, owner, eigenpodAddress string, chainId *big.Int, proof *eigenpodproofs.ValidatorBalancesRootProof, balanceProofs []*eigenpodproofs.BalanceProof, eth *ethclient.Client, noSend bool) (*types.Transaction, error) {
tracing := GetContextTracingCallbacks(ctx)

ownerAccount, err := PrepareAccount(&owner, chainId)
ownerAccount, err := PrepareAccount(&owner, chainId, noSend)
if err != nil {
return nil, err
}
Expand Down
71 changes: 47 additions & 24 deletions cli/core/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
gethCommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/params"
Expand Down Expand Up @@ -113,46 +114,36 @@ type Owner = struct {
FromAddress gethCommon.Address
PublicKey *ecdsa.PublicKey
TransactionOptions *bind.TransactOpts
IsDryRun bool
}

func StartCheckpoint(ctx context.Context, eigenpodAddress string, ownerPrivateKey string, chainId *big.Int, eth *ethclient.Client, forceCheckpoint bool) (uint64, error) {
ownerAccount, err := PrepareAccount(&ownerPrivateKey, chainId)
func StartCheckpoint(ctx context.Context, eigenpodAddress string, ownerPrivateKey string, chainId *big.Int, eth *ethclient.Client, forceCheckpoint bool, noSend bool) (*types.Transaction, error) {
ownerAccount, err := PrepareAccount(&ownerPrivateKey, chainId, noSend)
if err != nil {
return 0, fmt.Errorf("failed to parse private key: %w", err)
return nil, fmt.Errorf("failed to parse private key: %w", err)
}

eigenPod, err := onchain.NewEigenPod(gethCommon.HexToAddress(eigenpodAddress), eth)
if err != nil {
return 0, fmt.Errorf("failed to reach eigenpod: %w", err)
return nil, fmt.Errorf("failed to reach eigenpod: %w", err)
}

revertIfNoBalance := !forceCheckpoint

txn, err := eigenPod.StartCheckpoint(ownerAccount.TransactionOptions, revertIfNoBalance)
if err != nil {
if !forceCheckpoint {
return 0, fmt.Errorf("failed to start checkpoint (try running again with `--force`): %w", err)
return nil, fmt.Errorf("failed to start checkpoint (try running again with `--force`): %w", err)
}

return 0, fmt.Errorf("failed to start checkpoint: %w", err)
return nil, fmt.Errorf("failed to start checkpoint: %w", err)
}

color.Green("starting checkpoint: %s.. (waiting for txn to be mined)...", txn.Hash().Hex())

bind.WaitMined(ctx, eth, txn)

color.Green("started checkpoint! txn: %s", txn.Hash().Hex())

currentCheckpoint, err := GetCurrentCheckpoint(eigenpodAddress, eth)
if err != nil {
return 0, fmt.Errorf("failed to fetch current checkpoint: %w", err)
}

return currentCheckpoint, nil
return txn, nil
}

func GetBeaconClient(beaconUri string) (BeaconClient, error) {
beaconClient, _, err := NewBeaconClient(beaconUri)
func GetBeaconClient(beaconUri string, verbose bool) (BeaconClient, error) {
beaconClient, _, err := NewBeaconClient(beaconUri, verbose)
return beaconClient, err
}

Expand Down Expand Up @@ -278,7 +269,7 @@ func GetCurrentCheckpointBlockRoot(eigenpodAddress string, eth *ethclient.Client
return &checkpoint.BeaconBlockRoot, nil
}

func GetClients(ctx context.Context, node, beaconNodeUri string) (*ethclient.Client, BeaconClient, *big.Int, error) {
func GetClients(ctx context.Context, node, beaconNodeUri string, enableLogs bool) (*ethclient.Client, BeaconClient, *big.Int, error) {
eth, err := ethclient.Dial(node)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to reach eth --node: %w", err)
Expand All @@ -290,10 +281,10 @@ func GetClients(ctx context.Context, node, beaconNodeUri string) (*ethclient.Cli
}

if chainId == nil || chainId.Int64() != 17000 {
return nil, nil, nil, fmt.Errorf("This tool only supports the Holesky network.")
return nil, nil, nil, errors.New("this tool only supports the Holesky network")
}

beaconClient, err := GetBeaconClient(beaconNodeUri)
beaconClient, err := GetBeaconClient(beaconNodeUri, enableLogs)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to reach beacon client: %w", err)
}
Expand Down Expand Up @@ -328,7 +319,38 @@ func PanicIfNoConsent(prompt string) {
}
}

func PrepareAccount(owner *string, chainID *big.Int) (*Owner, error) {
func PrepareAccount(owner *string, chainID *big.Int, noSend bool) (*Owner, error) {
if noSend {
privateKey, err := crypto.HexToECDSA("372d94b8645091147a5dfc10a454d0d539773d2431293bf0a195b44fa5ddbb33") // this is a RANDOM private key. Do not use this for anything.
if err != nil {
return nil, err
}

publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
log.Fatal("error casting public key to ECDSA")
}
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
auth, err := bind.NewKeyedTransactorWithChainID(privateKey, chainID)
if err != nil {
return nil, err
}

auth.GasPrice = nil // big.NewInt(10) // Gas price to use for the transaction execution (nil = gas price oracle)
auth.GasFeeCap = big.NewInt(10) // big.NewInt(10) // Gas fee cap to use for the 1559 transaction execution (nil = gas price oracle)
auth.GasTipCap = big.NewInt(2) // big.NewInt(2) // Gas priority fee cap to use for the 1559 transaction execution (nil = gas price oracle)
auth.GasLimit = 21000
auth.NoSend = true

return &Owner{
FromAddress: fromAddress,
PublicKey: nil,
TransactionOptions: auth,
IsDryRun: true,
}, nil
}

if owner == nil {
return nil, errors.New("no owner")
}
Expand All @@ -353,6 +375,7 @@ func PrepareAccount(owner *string, chainID *big.Int) (*Owner, error) {
FromAddress: fromAddress,
PublicKey: publicKeyECDSA,
TransactionOptions: auth,
IsDryRun: noSend,
}, nil
}

Expand Down
52 changes: 34 additions & 18 deletions cli/core/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,31 +38,39 @@ func LoadValidatorProofFromFile(path string) (*SerializableCredentialProof, erro
return &res, nil
}

func SubmitValidatorProof(ctx context.Context, owner, eigenpodAddress string, chainId *big.Int, eth *ethclient.Client, batchSize uint64, proofs *eigenpodproofs.VerifyValidatorFieldsCallParams, oracleBeaconTimesetamp uint64, noPrompt bool) ([]*types.Transaction, error) {
ownerAccount, err := PrepareAccount(&owner, chainId)
func SubmitValidatorProof(ctx context.Context, owner, eigenpodAddress string, chainId *big.Int, eth *ethclient.Client, batchSize uint64, proofs *eigenpodproofs.VerifyValidatorFieldsCallParams, oracleBeaconTimesetamp uint64, noPrompt bool, noSend bool, verbose bool) ([]*types.Transaction, [][]*big.Int, error) {
ownerAccount, err := PrepareAccount(&owner, chainId, noSend)
if err != nil {
return nil, err
return nil, [][]*big.Int{}, err
}
PanicOnError("failed to parse private key", err)

eigenPod, err := onchain.NewEigenPod(common.HexToAddress(eigenpodAddress), eth)
if err != nil {
return nil, err
return nil, [][]*big.Int{}, err
}

indices := Uint64ArrayToBigIntArray(proofs.ValidatorIndices)
validatorIndicesChunks := chunk(indices, batchSize)
validatorProofsChunks := chunk(proofs.ValidatorFieldsProofs, batchSize)
validatorFieldsChunks := chunk(proofs.ValidatorFields, batchSize)
if !noPrompt {
if !noPrompt && !noSend {
PanicIfNoConsent(SubmitCredentialsProofConsent(len(validatorFieldsChunks)))
}

transactions := []*types.Transaction{}
numChunks := len(validatorIndicesChunks)

color.Green("calling EigenPod.VerifyWithdrawalCredentials() (using %d txn(s), max(%d) proofs per txn)", numChunks, batchSize)
color.Green("Submitting proofs with %d transactions", numChunks)
if verbose {
color.Green("calling EigenPod.VerifyWithdrawalCredentials() (using %d txn(s), max(%d) proofs per txn [%s])", numChunks, batchSize, func() string {
if ownerAccount.TransactionOptions.NoSend {
return "simulated"
} else {
return "live"
}
}())
color.Green("Submitting proofs with %d transactions", numChunks)
}

for i := 0; i < numChunks; i++ {
curValidatorIndices := validatorIndicesChunks[i]
Expand All @@ -75,20 +83,24 @@ func SubmitValidatorProof(ctx context.Context, owner, eigenpodAddress string, ch
}
var curValidatorFields [][][32]byte = CastValidatorFields(validatorFieldsChunks[i])

fmt.Printf("Submitted chunk %d/%d -- waiting for transaction...: ", i+1, numChunks)
txn, err := SubmitValidatorProofChunk(ctx, ownerAccount, eigenPod, chainId, eth, curValidatorIndices, curValidatorFields, proofs.StateRootProof, validatorFieldsProofs, oracleBeaconTimesetamp)
if verbose {
fmt.Printf("Submitted chunk %d/%d -- waiting for transaction...: ", i+1, numChunks)
}
txn, err := SubmitValidatorProofChunk(ctx, ownerAccount, eigenPod, chainId, eth, curValidatorIndices, curValidatorFields, proofs.StateRootProof, validatorFieldsProofs, oracleBeaconTimesetamp, verbose)
if err != nil {
return transactions, err
return transactions, validatorIndicesChunks, err
}

transactions = append(transactions, txn)
}

return transactions, err
return transactions, validatorIndicesChunks, err
}

func SubmitValidatorProofChunk(ctx context.Context, ownerAccount *Owner, eigenPod *onchain.EigenPod, chainId *big.Int, eth *ethclient.Client, indices []*big.Int, validatorFields [][][32]byte, stateRootProofs *eigenpodproofs.StateRootProof, validatorFieldsProofs [][]byte, oracleBeaconTimesetamp uint64) (*types.Transaction, error) {
color.Green("submitting onchain...")
func SubmitValidatorProofChunk(ctx context.Context, ownerAccount *Owner, eigenPod *onchain.EigenPod, chainId *big.Int, eth *ethclient.Client, indices []*big.Int, validatorFields [][][32]byte, stateRootProofs *eigenpodproofs.StateRootProof, validatorFieldsProofs [][]byte, oracleBeaconTimesetamp uint64, verbose bool) (*types.Transaction, error) {
if verbose {
color.Green("submitting onchain...")
}
txn, err := eigenPod.VerifyWithdrawalCredentials(
ownerAccount.TransactionOptions,
oracleBeaconTimesetamp,
Expand All @@ -108,7 +120,7 @@ func SubmitValidatorProofChunk(ctx context.Context, ownerAccount *Owner, eigenPo
* Generates a .ProveValidatorContainers() proof for all eligible validators on the pod. If `validatorIndex` is set, it will only generate a proof
* against that validator, regardless of the validator's state.
*/
func GenerateValidatorProof(ctx context.Context, eigenpodAddress string, eth *ethclient.Client, chainId *big.Int, beaconClient BeaconClient, validatorIndex *big.Int) (*eigenpodproofs.VerifyValidatorFieldsCallParams, uint64, error) {
func GenerateValidatorProof(ctx context.Context, eigenpodAddress string, eth *ethclient.Client, chainId *big.Int, beaconClient BeaconClient, validatorIndex *big.Int, verbose bool) (*eigenpodproofs.VerifyValidatorFieldsCallParams, uint64, error) {
latestBlock, err := eth.BlockByNumber(ctx, nil)
if err != nil {
return nil, 0, fmt.Errorf("failed to load latest block: %w", err)
Expand Down Expand Up @@ -139,11 +151,11 @@ func GenerateValidatorProof(ctx context.Context, eigenpodAddress string, eth *et
return nil, 0, fmt.Errorf("failed to initialize provider: %w", err)
}

proofs, err := GenerateValidatorProofAtState(proofExecutor, eigenpodAddress, beaconState, eth, chainId, header, latestBlock.Time(), validatorIndex)
proofs, err := GenerateValidatorProofAtState(proofExecutor, eigenpodAddress, beaconState, eth, chainId, header, latestBlock.Time(), validatorIndex, verbose)
return proofs, latestBlock.Time(), err
}

func GenerateValidatorProofAtState(proofs *eigenpodproofs.EigenPodProofs, eigenpodAddress string, beaconState *spec.VersionedBeaconState, eth *ethclient.Client, chainId *big.Int, header *v1.BeaconBlockHeader, blockTimestamp uint64, forSpecificValidatorIndex *big.Int) (*eigenpodproofs.VerifyValidatorFieldsCallParams, error) {
func GenerateValidatorProofAtState(proofs *eigenpodproofs.EigenPodProofs, eigenpodAddress string, beaconState *spec.VersionedBeaconState, eth *ethclient.Client, chainId *big.Int, header *v1.BeaconBlockHeader, blockTimestamp uint64, forSpecificValidatorIndex *big.Int, verbose bool) (*eigenpodproofs.VerifyValidatorFieldsCallParams, error) {
allValidators, err := FindAllValidatorsForEigenpod(eigenpodAddress, beaconState)
if err != nil {
return nil, fmt.Errorf("failed to find validators: %w", err)
Expand Down Expand Up @@ -172,10 +184,14 @@ func GenerateValidatorProofAtState(proofs *eigenpodproofs.EigenPodProofs, eigenp
}

if len(awaitingCredentialValidators) == 0 {
color.Red("You have no inactive validators to verify. Everything up-to-date.")
if verbose {
color.Red("You have no inactive validators to verify. Everything up-to-date.")
}
return nil, nil
} else {
color.Blue("Verifying %d inactive validators", len(awaitingCredentialValidators))
if verbose {
color.Blue("Verifying %d inactive validators", len(awaitingCredentialValidators))
}
}

validatorIndices := make([]uint64, len(awaitingCredentialValidators))
Expand Down
Loading

0 comments on commit dd69d47

Please sign in to comment.