From fa8b7e380f697652812bce95bb655e5739c49b43 Mon Sep 17 00:00:00 2001 From: tersec Date: Fri, 27 Sep 2024 05:53:58 +0000 Subject: [PATCH] add Electra beacon chain database state tests (#6584) --- AllTests-mainnet.md | 7 +- .../consensus_object_pools/blockchain_dag.nim | 3 - tests/test_beacon_chain_db.nim | 109 +++++++++++++++--- tests/teststateutil.nim | 11 +- 4 files changed, 104 insertions(+), 26 deletions(-) diff --git a/AllTests-mainnet.md b/AllTests-mainnet.md index 6f4fbef04b..637efca967 100644 --- a/AllTests-mainnet.md +++ b/AllTests-mainnet.md @@ -57,7 +57,10 @@ OK: 4/4 Fail: 0/4 Skip: 0/4 + sanity check Deneb blocks [Preset: mainnet] OK + sanity check Deneb states [Preset: mainnet] OK + sanity check Deneb states, reusing buffers [Preset: mainnet] OK ++ sanity check Electra and cross-fork getState rollback [Preset: mainnet] OK + sanity check Electra blocks [Preset: mainnet] OK ++ sanity check Electra states [Preset: mainnet] OK ++ sanity check Electra states, reusing buffers [Preset: mainnet] OK + sanity check blobs [Preset: mainnet] OK + sanity check genesis roundtrip [Preset: mainnet] OK + sanity check phase 0 blocks [Preset: mainnet] OK @@ -66,7 +69,7 @@ OK: 4/4 Fail: 0/4 Skip: 0/4 + sanity check phase 0 states, reusing buffers [Preset: mainnet] OK + sanity check state diff roundtrip [Preset: mainnet] OK ``` -OK: 26/26 Fail: 0/26 Skip: 0/26 +OK: 29/29 Fail: 0/29 Skip: 0/29 ## Beacon state [Preset: mainnet] ```diff + Smoke test initialize_beacon_state_from_eth1 [Preset: mainnet] OK @@ -1125,4 +1128,4 @@ OK: 2/2 Fail: 0/2 Skip: 0/2 OK: 9/9 Fail: 0/9 Skip: 0/9 ---TOTAL--- -OK: 762/767 Fail: 0/767 Skip: 5/767 +OK: 765/770 Fail: 0/770 Skip: 5/770 diff --git a/beacon_chain/consensus_object_pools/blockchain_dag.nim b/beacon_chain/consensus_object_pools/blockchain_dag.nim index a8cc9531a3..f886ab8178 100644 --- a/beacon_chain/consensus_object_pools/blockchain_dag.nim +++ b/beacon_chain/consensus_object_pools/blockchain_dag.nim @@ -14,12 +14,9 @@ import ../spec/[beaconstate, eth2_merkleization, eth2_ssz_serialization, helpers, state_transition, validator], ../spec/forks, - ../spec/datatypes/[phase0, altair, bellatrix, capella], ".."/[beacon_chain_db, beacon_clock, era_db], "."/[block_pools_types, block_quarantine] -from ../spec/datatypes/deneb import shortLog - export eth2_merkleization, eth2_ssz_serialization, block_pools_types, results, beacon_chain_db diff --git a/tests/test_beacon_chain_db.nim b/tests/test_beacon_chain_db.nim index 08b47696c9..79bc8c91b1 100644 --- a/tests/test_beacon_chain_db.nim +++ b/tests/test_beacon_chain_db.nim @@ -11,16 +11,24 @@ import unittest2, ../beacon_chain/beacon_chain_db, - ../beacon_chain/spec/[beaconstate, forks, state_transition], - ../beacon_chain/consensus_object_pools/blockchain_dag, - eth/db/kvstore, - # test utilies - ./mocking/mock_genesis, - ./testutil, ./testdbutil, ./testblockutil, ./teststateutil + ../beacon_chain/consensus_object_pools/block_dag, + ../beacon_chain/spec/forks, + ./testutil from std/algorithm import sort from std/sequtils import toSeq from snappy import encodeFramed, uncompressedLenFramed +from ../beacon_chain/consensus_object_pools/block_pools_types import + ChainDAGRef +from ../beacon_chain/consensus_object_pools/blockchain_dag import init +from ../beacon_chain/spec/beaconstate import + initialize_hashed_beacon_state_from_eth1 +from ../beacon_chain/spec/state_transition import noRollback +from ../beacon_chain/validators/validator_monitor import ValidatorMonitor +from ./mocking/mock_genesis import mockEth1BlockHash +from ./testblockutil import makeInitialDeposits +from ./testdbutil import makeTestDB +from ./teststateutil import getTestStates when isMainModule: import chronicles # or some random compile error happens... @@ -46,9 +54,6 @@ proc getBellatrixStateRef(db: BeaconChainDB, root: Eth2Digest): if db.getState(root, res[], noRollback): return res -from ../beacon_chain/spec/datatypes/capella import - BeaconStateRef, NilableBeaconStateRef - proc getCapellaStateRef(db: BeaconChainDB, root: Eth2Digest): capella.NilableBeaconStateRef = # load beaconstate the way the block pool does it - into an existing instance @@ -56,8 +61,6 @@ proc getCapellaStateRef(db: BeaconChainDB, root: Eth2Digest): if db.getState(root, res[], noRollback): return res -from ../beacon_chain/spec/datatypes/deneb import TrustedSignedBeaconBlock - proc getDenebStateRef(db: BeaconChainDB, root: Eth2Digest): deneb.NilableBeaconStateRef = # load beaconstate the way the block pool does it - into an existing instance @@ -65,6 +68,13 @@ proc getDenebStateRef(db: BeaconChainDB, root: Eth2Digest): if db.getState(root, res[], noRollback): return res +proc getElectraStateRef(db: BeaconChainDB, root: Eth2Digest): + electra.NilableBeaconStateRef = + # load beaconstate the way the block pool does it - into an existing instance + let res = (electra.BeaconStateRef)() + if db.getState(root, res[], noRollback): + return res + func withDigest(blck: phase0.TrustedBeaconBlock): phase0.TrustedSignedBeaconBlock = phase0.TrustedSignedBeaconBlock( @@ -120,8 +130,6 @@ proc getTestStates(consensusFork: ConsensusFork): auto = testStates -debugComment "add some electra states, and test electra state loading/etc" - # Each set of states gets used twice, so scope them to module let testStatesPhase0 = getTestStates(ConsensusFork.Phase0) @@ -129,11 +137,13 @@ let testStatesBellatrix = getTestStates(ConsensusFork.Bellatrix) testStatesCapella = getTestStates(ConsensusFork.Capella) testStatesDeneb = getTestStates(ConsensusFork.Deneb) + testStatesElectra = getTestStates(ConsensusFork.Electra) doAssert len(testStatesPhase0) > 8 doAssert len(testStatesAltair) > 8 doAssert len(testStatesBellatrix) > 8 doAssert len(testStatesCapella) > 8 doAssert len(testStatesDeneb) > 8 +doAssert len(testStatesElectra) > 8 suite "Beacon chain DB" & preset(): test "empty database" & preset(): @@ -527,6 +537,24 @@ suite "Beacon chain DB" & preset(): db.close() + test "sanity check Electra states" & preset(): + let db = makeTestDB(SLOTS_PER_EPOCH) + + for state in testStatesElectra: + let root = state[].electraData.root + db.putState(root, state[].electraData.data) + + check: + db.containsState(root) + hash_tree_root(db.getElectraStateRef(root)[]) == root + + db.delState(ConsensusFork.Electra, root) + check: + not db.containsState(root) + db.getElectraStateRef(root).isNil + + db.close() + test "sanity check phase 0 states, reusing buffers" & preset(): let db = makeTestDB(SLOTS_PER_EPOCH) let stateBuffer = (phase0.BeaconStateRef)() @@ -627,6 +655,26 @@ suite "Beacon chain DB" & preset(): db.close() + test "sanity check Electra states, reusing buffers" & preset(): + let db = makeTestDB(SLOTS_PER_EPOCH) + let stateBuffer = (electra.BeaconStateRef)() + + for state in testStatesElectra: + let root = state[].electraData.root + db.putState(root, state[].electraData.data) + + check: + db.getState(root, stateBuffer[], noRollback) + db.containsState(root) + hash_tree_root(stateBuffer[]) == root + + db.delState(ConsensusFork.Electra, root) + check: + not db.containsState(root) + not db.getState(root, stateBuffer[], noRollback) + + db.close() + test "sanity check phase 0 getState rollback" & preset(): var db = makeTestDB(SLOTS_PER_EPOCH) @@ -754,9 +802,34 @@ suite "Beacon chain DB" & preset(): state[].kind == ConsensusFork.Phase0 state[].phase0Data.data.slot != 10.Slot - test "find ancestors" & preset(): + test "sanity check Electra and cross-fork getState rollback" & preset(): var - db = BeaconChainDB.new("", inMemory = true) + db = makeTestDB(SLOTS_PER_EPOCH) + validatorMonitor = newClone(ValidatorMonitor.init()) + dag = init(ChainDAGRef, defaultRuntimeConfig, db, validatorMonitor, {}) + state = (ref ForkedHashedBeaconState)( + kind: ConsensusFork.Electra, + electraData: electra.HashedBeaconState(data: electra.BeaconState( + slot: 10.Slot))) + root = Eth2Digest() + + db.putCorruptState(ConsensusFork.Electra, root) + + let restoreAddr = addr dag.headState + + func restore() = + assign(state[], restoreAddr[]) + + check: + state[].electraData.data.slot == 10.Slot + not db.getState(root, state[].electraData.data, restore) + + # assign() has switched the case object fork + state[].kind == ConsensusFork.Phase0 + state[].phase0Data.data.slot != 10.Slot + + test "find ancestors" & preset(): + var db = BeaconChainDB.new("", inMemory = true) let a0 = withDigest( @@ -791,8 +864,7 @@ suite "Beacon chain DB" & preset(): # state. We've been bit by this because we've had a bug in the BLS # serialization where an all-zero default-initialized bls signature could # not be deserialized because the deserialization was too strict. - var - db = BeaconChainDB.new("", inMemory = true) + var db = BeaconChainDB.new("", inMemory = true) let state = newClone(initialize_hashed_beacon_state_from_eth1( @@ -811,8 +883,7 @@ suite "Beacon chain DB" & preset(): hash_tree_root(state2[]) == state[].root test "sanity check state diff roundtrip" & preset(): - var - db = BeaconChainDB.new("", inMemory = true) + var db = BeaconChainDB.new("", inMemory = true) # TODO htr(diff) probably not interesting/useful, but stand-in let diff --git a/tests/teststateutil.nim b/tests/teststateutil.nim index 93105cc0a0..88d6ff0d60 100644 --- a/tests/teststateutil.nim +++ b/tests/teststateutil.nim @@ -40,8 +40,15 @@ proc valid_deposit(state: var ForkyHashedBeaconState) = defaultRuntimeConfig, state.data, sortValidatorBuckets(state.data.validators.asSeq)[], deposit, {}).isOk doAssert state.data.validators.len == pre_val_count + 1 - doAssert state.data.balances.len == pre_val_count + 1 - doAssert state.data.balances.item(validator_index) == pre_balance + deposit.data.amount + when typeof(state).kind >= ConsensusFork.Electra: + doAssert state.data.pending_balance_deposits.asSeq[^1] == + PendingBalanceDeposit(index: pre_val_count.uint64, + amount: deposit.data.amount) + doAssert state.data.balances.item(validator_index) == pre_balance + else: + doAssert state.data.balances.item(validator_index) == + pre_balance + deposit.data.amount + doAssert state.data.validators.item(validator_index).effective_balance == round_multiple_down( min(