From b83dfa29ec818a86921ee75aa445cf1aad3e9c87 Mon Sep 17 00:00:00 2001 From: marioevz Date: Wed, 1 Mar 2023 14:49:58 -0600 Subject: [PATCH 01/10] simulators/eth2/common: add root fn to state object --- simulators/eth2/common/clients/beacon.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/simulators/eth2/common/clients/beacon.go b/simulators/eth2/common/clients/beacon.go index d1f0cc1f80..df3f81ce51 100644 --- a/simulators/eth2/common/clients/beacon.go +++ b/simulators/eth2/common/clients/beacon.go @@ -445,6 +445,20 @@ type VersionedBeaconStateResponse struct { spec *common.Spec } +func (vbs *VersionedBeaconStateResponse) Root() tree.Root { + switch state := vbs.Data.(type) { + case *phase0.BeaconState: + return state.HashTreeRoot(vbs.spec, tree.GetHashFn()) + case *altair.BeaconState: + return state.HashTreeRoot(vbs.spec, tree.GetHashFn()) + case *bellatrix.BeaconState: + return state.HashTreeRoot(vbs.spec, tree.GetHashFn()) + case *capella.BeaconState: + return state.HashTreeRoot(vbs.spec, tree.GetHashFn()) + } + panic("badly formatted beacon state") +} + func (vbs *VersionedBeaconStateResponse) CurrentVersion() common.Version { switch state := vbs.Data.(type) { case *phase0.BeaconState: From d2666b222325f6e8cbea599466c8125aba2482cf Mon Sep 17 00:00:00 2001 From: marioevz Date: Wed, 1 Mar 2023 14:50:37 -0600 Subject: [PATCH 02/10] simulators/eth2/withdrawals: always get state by slot/head --- simulators/eth2/withdrawals/helper.go | 24 +++++++++++++++++++++++- simulators/eth2/withdrawals/scenarios.go | 4 ++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/simulators/eth2/withdrawals/helper.go b/simulators/eth2/withdrawals/helper.go index 6cebffd68d..77ec09c40b 100644 --- a/simulators/eth2/withdrawals/helper.go +++ b/simulators/eth2/withdrawals/helper.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "context" "crypto/ecdsa" "fmt" @@ -76,6 +77,20 @@ type BeaconBlockState struct { type BeaconCache map[tree.Root]BeaconBlockState +// Clear the cache for when there was a known/expected re-org to query everything again +func (c BeaconCache) Clear() error { + roots := make([]tree.Root, len(c)) + i := 0 + for s := range c { + roots[i] = s + i++ + } + for _, s := range roots { + delete(c, s) + } + return nil +} + func (c BeaconCache) GetBlockStateByRoot( ctx context.Context, bc *clients.BeaconClient, @@ -88,10 +103,17 @@ func (c BeaconCache) GetBlockStateByRoot( if err != nil { return BeaconBlockState{}, err } - s, err := bc.BeaconStateV2(ctx, eth2api.StateIdRoot(b.StateRoot())) + s, err := bc.BeaconStateV2(ctx, eth2api.StateIdSlot(b.Slot())) if err != nil { return BeaconBlockState{}, err } + blockStateRoot := b.StateRoot() + stateRoot := s.Root() + if !bytes.Equal(blockStateRoot[:], stateRoot[:]) { + return BeaconBlockState{}, fmt.Errorf( + "state root missmatch while fetching state", + ) + } both := BeaconBlockState{ VersionedBeaconStateResponse: s, VersionedSignedBeaconBlock: b, diff --git a/simulators/eth2/withdrawals/scenarios.go b/simulators/eth2/withdrawals/scenarios.go index 4f10731714..94b98b925b 100644 --- a/simulators/eth2/withdrawals/scenarios.go +++ b/simulators/eth2/withdrawals/scenarios.go @@ -165,9 +165,9 @@ func (ts BaseWithdrawalsTestSpec) Execute( // Get the beacon state and verify the credentials were updated var versionedBeaconState *clients.VersionedBeaconStateResponse for _, bn := range testnet.BeaconClients().Running() { - versionedBeaconState, err = bn.BeaconStateV2ByBlock( + versionedBeaconState, err = bn.BeaconStateV2( ctx, - eth2api.BlockHead, + eth2api.StateHead, ) if err != nil || versionedBeaconState == nil { t.Logf("WARN: Unable to get latest beacon state: %v", err) From a935d98577c4cd47cb3d58bffe468a77df21dc9c Mon Sep 17 00:00:00 2001 From: marioevz Date: Wed, 1 Mar 2023 15:47:42 -0600 Subject: [PATCH 03/10] simulators/eth2/common: print container started clients --- simulators/eth2/common/clients/beacon.go | 5 +++++ simulators/eth2/common/clients/execution.go | 5 +++++ simulators/eth2/common/clients/validator.go | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/simulators/eth2/common/clients/beacon.go b/simulators/eth2/common/clients/beacon.go index df3f81ce51..1ca525a71f 100644 --- a/simulators/eth2/common/clients/beacon.go +++ b/simulators/eth2/common/clients/beacon.go @@ -98,6 +98,11 @@ func (bn *BeaconClient) Start(extraOptions ...hivesim.StartOption) error { Cli: &http.Client{}, Codec: eth2api.JSONCodec{}, } + bn.T.Logf( + "Started client %s, container: %s", + bn.ClientType, + bn.HiveClient.Container, + ) return nil } diff --git a/simulators/eth2/common/clients/execution.go b/simulators/eth2/common/clients/execution.go index a32a23ee12..e2fd31cfa7 100644 --- a/simulators/eth2/common/clients/execution.go +++ b/simulators/eth2/common/clients/execution.go @@ -123,6 +123,11 @@ func (en *ExecutionClient) Start(extraOptions ...hivesim.StartOption) error { opts = append(opts, extraOptions...) en.HiveClient = en.T.StartClient(en.ClientType, opts...) + en.T.Logf( + "Started client %s, container: %s", + en.ClientType, + en.HiveClient.Container, + ) // Prepare Eth/Engine RPCs engineRPCAddress, err := en.EngineRPCAddress() diff --git a/simulators/eth2/common/clients/validator.go b/simulators/eth2/common/clients/validator.go index 98988dfc01..1090b6b911 100644 --- a/simulators/eth2/common/clients/validator.go +++ b/simulators/eth2/common/clients/validator.go @@ -58,6 +58,11 @@ func (vc *ValidatorClient) Start(extraOptions ...hivesim.StartOption) error { } vc.HiveClient = vc.T.StartClient(vc.ClientType, opts...) + vc.T.Logf( + "Started client %s, container: %s", + vc.ClientType, + vc.HiveClient.Container, + ) return nil } From 555b86d93998a13f66828dd48b827798187d07b8 Mon Sep 17 00:00:00 2001 From: marioevz Date: Wed, 1 Mar 2023 18:11:09 -0600 Subject: [PATCH 04/10] simulators/eth2/common: withdrawal indexes from beacon state --- simulators/eth2/common/clients/beacon.go | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/simulators/eth2/common/clients/beacon.go b/simulators/eth2/common/clients/beacon.go index 1ca525a71f..e0f256f3ee 100644 --- a/simulators/eth2/common/clients/beacon.go +++ b/simulators/eth2/common/clients/beacon.go @@ -38,9 +38,7 @@ const ( PortValidatorAPI = 5000 ) -var ( - EMPTY_TREE_ROOT = tree.Root{} -) +var EMPTY_TREE_ROOT = tree.Root{} type BeaconClient struct { T *hivesim.T @@ -584,6 +582,24 @@ func (vbs *VersionedBeaconStateResponse) LatestExecutionPayloadHeaderHash() tree panic("badly formatted beacon state") } +func (vbs *VersionedBeaconStateResponse) NextWithdrawalIndex() (common.WithdrawalIndex, error) { + var wIndex common.WithdrawalIndex + switch state := vbs.Data.(type) { + case *capella.BeaconState: + wIndex = state.NextWithdrawalIndex + } + return wIndex, nil +} + +func (vbs *VersionedBeaconStateResponse) NextWithdrawalValidatorIndex() (common.ValidatorIndex, error) { + var wIndex common.ValidatorIndex + switch state := vbs.Data.(type) { + case *capella.BeaconState: + wIndex = state.NextWithdrawalValidatorIndex + } + return wIndex, nil +} + func (vbs *VersionedBeaconStateResponse) NextWithdrawals( slot common.Slot, ) (common.Withdrawals, error) { From 2685f2d920f6f47d905b56007b9fe7ef478341ad Mon Sep 17 00:00:00 2001 From: marioevz Date: Wed, 1 Mar 2023 18:12:45 -0600 Subject: [PATCH 05/10] simulators/eth2/common: fix execution payload header at genesis --- .../eth2/common/config/consensus/genesis.go | 82 ++++++++++++++++++- .../config/execution/execution_config.go | 20 +++++ 2 files changed, 99 insertions(+), 3 deletions(-) diff --git a/simulators/eth2/common/config/consensus/genesis.go b/simulators/eth2/common/config/consensus/genesis.go index 1416c89178..1e29b272c6 100644 --- a/simulators/eth2/common/config/consensus/genesis.go +++ b/simulators/eth2/common/config/consensus/genesis.go @@ -60,6 +60,59 @@ func genesisPayloadHeader( }, nil } +func genesisPayloadHeaderCapella( + eth1GenesisBlock *types.Block, + spec *common.Spec, +) (*capella.ExecutionPayloadHeader, error) { + extra := eth1GenesisBlock.Extra() + if len(extra) > common.MAX_EXTRA_DATA_BYTES { + return nil, fmt.Errorf( + "extra data is %d bytes, max is %d", + len(extra), + common.MAX_EXTRA_DATA_BYTES, + ) + } + if len(eth1GenesisBlock.Transactions()) != 0 { + return nil, fmt.Errorf( + "expected no transactions in genesis execution payload", + ) + } + if len(eth1GenesisBlock.Withdrawals()) != 0 { + return nil, fmt.Errorf( + "expected no withdrawals in genesis execution payload", + ) + } + + baseFee, overflow := uint256.FromBig(eth1GenesisBlock.BaseFee()) + if overflow { + return nil, fmt.Errorf("basefee larger than 2^256-1") + } + + return &capella.ExecutionPayloadHeader{ + ParentHash: common.Root(eth1GenesisBlock.ParentHash()), + FeeRecipient: common.Eth1Address(eth1GenesisBlock.Coinbase()), + StateRoot: common.Bytes32(eth1GenesisBlock.Root()), + ReceiptsRoot: common.Bytes32(eth1GenesisBlock.ReceiptHash()), + LogsBloom: common.LogsBloom(eth1GenesisBlock.Bloom()), + PrevRandao: common.Bytes32{}, + BlockNumber: view.Uint64View(eth1GenesisBlock.NumberU64()), + GasLimit: view.Uint64View(eth1GenesisBlock.GasLimit()), + GasUsed: view.Uint64View(eth1GenesisBlock.GasUsed()), + Timestamp: common.Timestamp(eth1GenesisBlock.Time()), + ExtraData: extra, + BaseFeePerGas: view.Uint256View(*baseFee), + BlockHash: common.Root(eth1GenesisBlock.Hash()), + // empty transactions root + TransactionsRoot: common.PayloadTransactionsType(spec). + DefaultNode(). + MerkleRoot(tree.GetHashFn()), + // empty withdrawals root + WithdrawalsRoot: common.WithdrawalsType(spec). + DefaultNode(). + MerkleRoot(tree.GetHashFn()), + }, nil +} + func createValidators( spec *common.Spec, keys []*KeyDetails, @@ -267,10 +320,10 @@ func BuildBeaconState( } } - if st, ok := state.(*bellatrix.BeaconStateView); ok { - // did we hit the TTD at genesis block? + switch st := state.(type) { + case *bellatrix.BeaconStateView: tdd := uint256.Int(spec.TERMINAL_TOTAL_DIFFICULTY) - embedExecAtGenesis := tdd.ToBig().Cmp(eth1Genesis.Difficulty) < 0 + embedExecAtGenesis := tdd.ToBig().Cmp(eth1Genesis.Difficulty) <= 0 var execPayloadHeader *bellatrix.ExecutionPayloadHeader if embedExecAtGenesis { @@ -287,6 +340,29 @@ func BuildBeaconState( execPayloadHeader = new(bellatrix.ExecutionPayloadHeader) } + if err := st.SetLatestExecutionPayloadHeader(execPayloadHeader); err != nil { + return nil, err + } + case *capella.BeaconStateView: + // did we hit the TTD at genesis block? + tdd := uint256.Int(spec.TERMINAL_TOTAL_DIFFICULTY) + embedExecAtGenesis := tdd.ToBig().Cmp(eth1Genesis.Difficulty) <= 0 + + var execPayloadHeader *capella.ExecutionPayloadHeader + if embedExecAtGenesis { + execPayloadHeader, err = genesisPayloadHeaderCapella( + eth1GenesisBlock, + spec, + ) + if err != nil { + return nil, err + } + } else { + // we didn't build any on the eth1 chain though, + // so we just put the genesis hash here (it could be any block from eth1 chain before TTD that is not ahead of eth2) + execPayloadHeader = new(capella.ExecutionPayloadHeader) + } + if err := st.SetLatestExecutionPayloadHeader(execPayloadHeader); err != nil { return nil, err } diff --git a/simulators/eth2/common/config/execution/execution_config.go b/simulators/eth2/common/config/execution/execution_config.go index ee0fda9627..022d1fa05e 100644 --- a/simulators/eth2/common/config/execution/execution_config.go +++ b/simulators/eth2/common/config/execution/execution_config.go @@ -126,6 +126,26 @@ func (c ExecutionPreChain) SecondsPerBlock() uint64 { return 1 } +// A pre-existing chain is imported by the client, and it is not +// expected that the client mines or produces any blocks. +type ExecutionPostMergeGenesis struct{} + +func (c ExecutionPostMergeGenesis) Configure(*ExecutionGenesis) error { + return nil +} + +func (c ExecutionPostMergeGenesis) HiveParams(node int) hivesim.Params { + return hivesim.Params{} +} + +func (c ExecutionPostMergeGenesis) DifficultyPerBlock() *big.Int { + return big.NewInt(0) +} + +func (c ExecutionPostMergeGenesis) SecondsPerBlock() uint64 { + return 12 +} + type ExecutionCliqueConsensus struct { CliquePeriod uint64 PrivateKey string From 6163bc559b1a5596449499b1b496dc06f5d6795c Mon Sep 17 00:00:00 2001 From: marioevz Date: Wed, 1 Mar 2023 18:15:50 -0600 Subject: [PATCH 06/10] simulators/eth2/withdrawals: print more withdrawals info --- simulators/eth2/withdrawals/helper.go | 27 ++++++++++++++++++++++++ simulators/eth2/withdrawals/scenarios.go | 3 +++ 2 files changed, 30 insertions(+) diff --git a/simulators/eth2/withdrawals/helper.go b/simulators/eth2/withdrawals/helper.go index 77ec09c40b..6afdb03e4a 100644 --- a/simulators/eth2/withdrawals/helper.go +++ b/simulators/eth2/withdrawals/helper.go @@ -6,6 +6,7 @@ import ( "crypto/ecdsa" "fmt" "math/big" + "sort" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -150,6 +151,32 @@ func (c BeaconCache) GetBlockStateBySlotFromHeadRoot( } } +func PrintWithdrawalHistory(c BeaconCache) error { + slotMap := make(map[beacon.Slot]tree.Root) + slots := make([]beacon.Slot, 0) + for r, s := range c { + slot := s.StateSlot() + slotMap[slot] = r + slots = append(slots, slot) + } + + sort.Slice(slots, func(i, j int) bool { return slots[j] > slots[i] }) + + for _, slot := range slots { + root := slotMap[slot] + s := c[root] + nextWithdrawalIndex, _ := s.NextWithdrawalIndex() + nextWithdrawalValidatorIndex, _ := s.NextWithdrawalValidatorIndex() + fmt.Printf( + "Slot=%d, NextWithdrawalIndex=%d, NextWithdrawalValidatorIndex=%d\n", + slot, + nextWithdrawalIndex, + nextWithdrawalValidatorIndex, + ) + } + return nil +} + // Helper struct to keep track of current status of a validator withdrawal state type Validator struct { Index beacon.ValidatorIndex diff --git a/simulators/eth2/withdrawals/scenarios.go b/simulators/eth2/withdrawals/scenarios.go index 94b98b925b..640c0473a7 100644 --- a/simulators/eth2/withdrawals/scenarios.go +++ b/simulators/eth2/withdrawals/scenarios.go @@ -231,6 +231,7 @@ loop: for { select { case <-slotCtx.Done(): + PrintWithdrawalHistory(allValidators[0].BlockStateCache) t.Fatalf("FAIL: Timeout waiting on all accounts to withdraw") case <-time.After(time.Duration(testnet.Spec().SECONDS_PER_SLOT) * time.Second): // Print all info @@ -256,6 +257,8 @@ loop: } } + PrintWithdrawalHistory(allValidators[0].BlockStateCache) + // Lastly check all clients are on the same head testnet.VerifyELHeads(ctx) From 8e967f62ac0f1dae79b8b1215eb0377f66921069 Mon Sep 17 00:00:00 2001 From: marioevz Date: Wed, 1 Mar 2023 18:15:59 -0600 Subject: [PATCH 07/10] simulators/eth2/withdrawals: fix eth1 consensus --- simulators/eth2/withdrawals/specs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simulators/eth2/withdrawals/specs.go b/simulators/eth2/withdrawals/specs.go index 10c7246db3..c992ce9eee 100644 --- a/simulators/eth2/withdrawals/specs.go +++ b/simulators/eth2/withdrawals/specs.go @@ -59,7 +59,7 @@ var ( AltairForkEpoch: common.Big0, BellatrixForkEpoch: common.Big0, CapellaForkEpoch: common.Big1, - Eth1Consensus: &el.ExecutionCliqueConsensus{}, + Eth1Consensus: &el.ExecutionPostMergeGenesis{}, } MINIMAL_SLOT_TIME_CLIENTS = []string{ From a7bde7f6230f1f36197009bed2728e51bcea648c Mon Sep 17 00:00:00 2001 From: marioevz Date: Wed, 1 Mar 2023 18:49:19 -0600 Subject: [PATCH 08/10] simulators/eth2/withdrawals: guarantee withdrawable balances --- simulators/eth2/withdrawals/specs.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/simulators/eth2/withdrawals/specs.go b/simulators/eth2/withdrawals/specs.go index c992ce9eee..db47fa1861 100644 --- a/simulators/eth2/withdrawals/specs.go +++ b/simulators/eth2/withdrawals/specs.go @@ -204,8 +204,9 @@ func (ts BaseWithdrawalsTestSpec) GetValidatorKeys( } for index, key := range keys { - // All validators have idiosyncratic balance amounts to identify them - key.ExtraInitialBalance = beacon.Gwei(index+1) + ts.ExtraGwei + // All validators have idiosyncratic balance amounts to identify them. + // Also include a high amount in order to guarantee withdrawals. + key.ExtraInitialBalance = beacon.Gwei((index+1)*1000000) + ts.ExtraGwei if ts.GenesisExecutionWithdrawalCredentialsShares > 0 && (index%ts.GenesisExecutionWithdrawalCredentialsShares) == 0 { From c4962a9b77e0187a4154a4bb3692024cc17df149 Mon Sep 17 00:00:00 2001 From: marioevz Date: Wed, 1 Mar 2023 18:50:02 -0600 Subject: [PATCH 09/10] simulators/eth2/common: withdrawals from block --- simulators/eth2/common/clients/beacon.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/simulators/eth2/common/clients/beacon.go b/simulators/eth2/common/clients/beacon.go index e0f256f3ee..16f4ad7db8 100644 --- a/simulators/eth2/common/clients/beacon.go +++ b/simulators/eth2/common/clients/beacon.go @@ -226,6 +226,14 @@ func (versionedBlock *VersionedSignedBeaconBlock) ExecutionPayload() (api.Execut return result, nil } +func (versionedBlock *VersionedSignedBeaconBlock) Withdrawals() (common.Withdrawals, error) { + switch v := versionedBlock.Data.(type) { + case *capella.SignedBeaconBlock: + return v.Message.Body.ExecutionPayload.Withdrawals, nil + } + return nil, nil +} + func (b *VersionedSignedBeaconBlock) StateRoot() tree.Root { switch v := b.Data.(type) { case *phase0.SignedBeaconBlock: From f9b134658311928b6aac5e062b3e4b8cdb4e1a24 Mon Sep 17 00:00:00 2001 From: marioevz Date: Wed, 1 Mar 2023 18:50:16 -0600 Subject: [PATCH 10/10] simulators/eth2/withdrawals: print withdrawals helper --- simulators/eth2/withdrawals/helper.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/simulators/eth2/withdrawals/helper.go b/simulators/eth2/withdrawals/helper.go index 6afdb03e4a..1f408d7298 100644 --- a/simulators/eth2/withdrawals/helper.go +++ b/simulators/eth2/withdrawals/helper.go @@ -173,6 +173,16 @@ func PrintWithdrawalHistory(c BeaconCache) error { nextWithdrawalIndex, nextWithdrawalValidatorIndex, ) + fmt.Printf("Withdrawals:\n") + ws, _ := s.Withdrawals() + for i, w := range ws { + fmt.Printf( + "%d: Validator Index: %s, Amount: %d\n", + i, + w.ValidatorIndex, + w.Amount, + ) + } } return nil }