diff --git a/simulators/eth2/common/clients/node.go b/simulators/eth2/common/clients/node.go index 15019b1737..7db783340d 100644 --- a/simulators/eth2/common/clients/node.go +++ b/simulators/eth2/common/clients/node.go @@ -85,21 +85,27 @@ func (nodes NodeDefinitions) Shares() []uint64 { return shares } -func (all NodeDefinitions) FilterByCL(filter []string) NodeDefinitions { +func (all NodeDefinitions) FilterByCL(filters []string) NodeDefinitions { ret := make(NodeDefinitions, 0) for _, n := range all { - if slices.Contains(filter, n.ConsensusClient) { - ret = append(ret, n) + for _, filter := range filters { + if strings.Contains(n.ConsensusClient, filter) { + ret = append(ret, n) + break + } } } return ret } -func (all NodeDefinitions) FilterByEL(filter []string) NodeDefinitions { +func (all NodeDefinitions) FilterByEL(filters []string) NodeDefinitions { ret := make(NodeDefinitions, 0) for _, n := range all { - if slices.Contains(filter, n.ExecutionClient) { - ret = append(ret, n) + for _, filter := range filters { + if strings.Contains(n.ExecutionClient, filter) { + ret = append(ret, n) + break + } } } return ret @@ -333,21 +339,27 @@ func (all Nodes) Running() Nodes { return res } -func (all Nodes) FilterByCL(filter []string) Nodes { +func (all Nodes) FilterByCL(filters []string) Nodes { ret := make(Nodes, 0) for _, n := range all { - if slices.Contains(filter, n.BeaconClient.ClientName()) { - ret = append(ret, n) + for _, filter := range filters { + if strings.Contains(n.BeaconClient.ClientName(), filter) { + ret = append(ret, n) + break + } } } return ret } -func (all Nodes) FilterByEL(filter []string) Nodes { +func (all Nodes) FilterByEL(filters []string) Nodes { ret := make(Nodes, 0) for _, n := range all { - if slices.Contains(filter, n.ExecutionClient.ClientType) { - ret = append(ret, n) + for _, filter := range filters { + if strings.Contains(n.ExecutionClient.ClientType, filter) { + ret = append(ret, n) + break + } } } return ret diff --git a/simulators/eth2/common/config/consensus/genesis.go b/simulators/eth2/common/config/consensus/genesis.go index 1cb752bd12..1416c89178 100644 --- a/simulators/eth2/common/config/consensus/genesis.go +++ b/simulators/eth2/common/config/consensus/genesis.go @@ -102,10 +102,12 @@ func BuildBeaconState( var state common.BeaconState var forkVersion common.Version + var previousForkVersion common.Version var emptyBodyRoot common.Root if spec.CAPELLA_FORK_EPOCH == 0 { stateView := capella.NewBeaconStateView(spec) forkVersion = spec.CAPELLA_FORK_VERSION + previousForkVersion = spec.BELLATRIX_FORK_VERSION emptyBodyRoot = capella.BeaconBlockBodyType(configs.Mainnet). New(). HashTreeRoot(hFn) @@ -114,6 +116,7 @@ func BuildBeaconState( } else if spec.BELLATRIX_FORK_EPOCH == 0 { stateView := bellatrix.NewBeaconStateView(spec) forkVersion = spec.BELLATRIX_FORK_VERSION + previousForkVersion = spec.ALTAIR_FORK_VERSION emptyBodyRoot = bellatrix.BeaconBlockBodyType(configs.Mainnet). New(). HashTreeRoot(hFn) @@ -121,12 +124,14 @@ func BuildBeaconState( } else if spec.ALTAIR_FORK_EPOCH == 0 { state = bellatrix.NewBeaconStateView(spec) forkVersion = spec.ALTAIR_FORK_VERSION + previousForkVersion = spec.GENESIS_FORK_VERSION emptyBodyRoot = altair.BeaconBlockBodyType(configs.Mainnet). New(). HashTreeRoot(hFn) } else { state = phase0.NewBeaconStateView(spec) forkVersion = spec.GENESIS_FORK_VERSION + previousForkVersion = spec.GENESIS_FORK_VERSION emptyBodyRoot = phase0.BeaconBlockBodyType(configs.Mainnet). New(). HashTreeRoot(hFn) @@ -137,7 +142,7 @@ func BuildBeaconState( } if err := state.SetFork(common.Fork{ - PreviousVersion: forkVersion, // duplicate, since there is nothing before genesis. + PreviousVersion: previousForkVersion, CurrentVersion: forkVersion, Epoch: common.GENESIS_EPOCH, }); err != nil { diff --git a/simulators/eth2/common/testnet/prepared_testnet.go b/simulators/eth2/common/testnet/prepared_testnet.go index 530865d1d0..4daea65544 100644 --- a/simulators/eth2/common/testnet/prepared_testnet.go +++ b/simulators/eth2/common/testnet/prepared_testnet.go @@ -190,6 +190,8 @@ func prepareTestnet( // Validators can withdraw immediately spec.Config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY = 0 + spec.Config.PROPOSER_SCORE_BOOST = 40 + // Generate keys opts for validators shares := config.NodeDefinitions.Shares() // ExtraShares defines an extra set of keys that none of the nodes will have. @@ -249,6 +251,10 @@ func prepareTestnet( clients.PortMetrics, ), "HIVE_ETH2_CONFIG_DEPOSIT_CONTRACT_ADDRESS": depositAddress.String(), + "HIVE_ETH2_DEPOSIT_DEPLOY_BLOCK_HASH": fmt.Sprintf( + "%s", + eth1Genesis.Genesis.ToBlock().Hash(), + ), } beaconOpts := hivesim.Bundle( commonOpts, diff --git a/simulators/eth2/withdrawals/main.go b/simulators/eth2/withdrawals/main.go index 3d25348223..8af01c1f39 100644 --- a/simulators/eth2/withdrawals/main.go +++ b/simulators/eth2/withdrawals/main.go @@ -55,7 +55,18 @@ var tests = []TestSpec{ }, BaseWithdrawalsTestSpec{ - Name: "test-bls-to-execution-changes-on-capella", + Name: "test-large-withdrawal-amounts", + Description: ` + Test withdrawing large amounts, such that: + - The amount multiplied by 1e9 overflows uint64 (on conversion from gwei to wei) + `, + CapellaGenesis: true, + GenesisExecutionWithdrawalCredentialsShares: 1, + ExtraGwei: 68719476736, // ((2 ** 35) * (1e9)) > (2 ** 64) + }, + + BaseWithdrawalsTestSpec{ + Name: "test-bls-to-execution-changes-on-capella-bellatrix-genesis", Description: ` Send BLS-To-Execution-Changes after capella fork has happened. Half of the validators still on BLS withdrawal credentials, and the diff --git a/simulators/eth2/withdrawals/scenarios.go b/simulators/eth2/withdrawals/scenarios.go index 3ed8a16622..2b9fb970e4 100644 --- a/simulators/eth2/withdrawals/scenarios.go +++ b/simulators/eth2/withdrawals/scenarios.go @@ -8,7 +8,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/hive/hivesim" "github.com/ethereum/hive/simulators/eth2/common/clients" - "github.com/ethereum/hive/simulators/eth2/common/testnet" tn "github.com/ethereum/hive/simulators/eth2/common/testnet" "github.com/protolambda/eth2api" beacon "github.com/protolambda/zrnt/eth2/beacon/common" @@ -16,13 +15,14 @@ import ( var ConsensusClientsSupportingBLSChangesBeforeCapella = []string{ "prysm", + "lodestar", } // Generic withdrawals test routine, capable of running most of the test // scenarios. func (ts BaseWithdrawalsTestSpec) Execute( t *hivesim.T, - env *testnet.Environment, + env *tn.Environment, n []clients.NodeDefinition, ) { config := ts.GetTestnetConfig(n) @@ -80,6 +80,8 @@ func (ts BaseWithdrawalsTestSpec) Execute( "FAIL: Unable to submit bls-to-execution changes: %v", err, ) + } else { + t.Logf("INFO: Sent validator %d BLS-To-Exec-Change on Bellatrix (%s)", v.Index, b.ClientName()) } } @@ -117,6 +119,8 @@ func (ts BaseWithdrawalsTestSpec) Execute( "FAIL: Unable to submit bls-to-execution changes: %v", err, ) + } else { + t.Logf("INFO: Sent validator %d BLS-To-Exec-Change on Capella (%s)", v.Index, b.ClientName()) } } @@ -222,5 +226,21 @@ loop: // Lastly check all clients are on the same head testnet.VerifyELHeads(ctx) - // TODO: Should we wait for finalization every time? + // Check for optimistic sync + for i, n := range testnet.Nodes.Running() { + bc := n.BeaconClient + if op, err := bc.BlockIsOptimistic(ctx, eth2api.BlockHead); op { + t.Fatalf( + "FAIL: client %d (%s) is optimistic, it should be synced.", + i, + n.ClientNames(), + ) + } else if err != nil { + t.Fatalf("FAIL: error querying optimistic state on client %d (%s): %v", i, n.ClientNames(), err) + } + } + + if ts.WaitForFinality { + testnet.WaitForFinality(ctx) + } } diff --git a/simulators/eth2/withdrawals/specs.go b/simulators/eth2/withdrawals/specs.go index f9594b5af0..1d00bb32c5 100644 --- a/simulators/eth2/withdrawals/specs.go +++ b/simulators/eth2/withdrawals/specs.go @@ -33,18 +33,25 @@ type BaseWithdrawalsTestSpec struct { // Other Testing Configuration SubmitBLSChangesOnBellatrix bool + + // Verifications + WaitForFinality bool + + // Extra Gwei + ExtraGwei beacon.Gwei } var ( DEFAULT_VALIDATOR_COUNT uint64 = 128 - DEFAULT_SLOT_TIME uint64 = 6 + MAINNET_SLOT_TIME int64 = 12 + MINIMAL_SLOT_TIME int64 = 6 EPOCHS_TO_FINALITY beacon.Epoch = 4 // Default config used for all tests unless a client specific config exists DEFAULT_CONFIG = &testnet.Config{ ValidatorCount: big.NewInt(int64(DEFAULT_VALIDATOR_COUNT)), - SlotTime: big.NewInt(int64(DEFAULT_SLOT_TIME)), + SlotTime: big.NewInt(MAINNET_SLOT_TIME), TerminalTotalDifficulty: common.Big0, AltairForkEpoch: common.Big0, BellatrixForkEpoch: common.Big0, @@ -52,6 +59,13 @@ var ( Eth1Consensus: &el.ExecutionCliqueConsensus{}, } + MINIMAL_SLOT_TIME_CLIENTS = []string{ + "lighthouse", + "teku", + "prysm", + "lodestar", + } + // Clients that do not support starting on epoch 0 with all forks enabled. // Tests take longer for these clients. /* @@ -65,12 +79,13 @@ var ( "prysm": true, } */ + ) func (ts BaseWithdrawalsTestSpec) GetTestnetConfig( - allNodeDefinitions []clients.NodeDefinition, + allNodeDefinitions clients.NodeDefinitions, ) *testnet.Config { - config := DEFAULT_CONFIG + config := *DEFAULT_CONFIG /* if INCREMENTAL_FORKS_CLIENTS[n.ConsensusClient] { @@ -78,6 +93,16 @@ func (ts BaseWithdrawalsTestSpec) GetTestnetConfig( } */ + if len( + allNodeDefinitions.FilterByCL(MINIMAL_SLOT_TIME_CLIENTS), + ) == len( + allNodeDefinitions, + ) { + // If all clients support using minimal 6 second slot time, use it + config.SlotTime = big.NewInt(MINIMAL_SLOT_TIME) + } + fmt.Printf("INFO: using %d second slot time\n", config.SlotTime) + if ts.CapellaGenesis { config.CapellaForkEpoch = common.Big0 } @@ -146,7 +171,7 @@ func (ts BaseWithdrawalsTestSpec) GetValidatorKeys( for index, key := range keys { // All validators have idiosyncratic balance amounts to identify them - key.ExtraInitialBalance = beacon.Gwei(index + 1) + key.ExtraInitialBalance = beacon.Gwei(index+1) + ts.ExtraGwei if ts.GenesisExecutionWithdrawalCredentialsShares > 0 && (index%ts.GenesisExecutionWithdrawalCredentialsShares) == 0 {