introduce database support for Altair (#2667)

* introduce immutable Altair BeaconState

* add database support for Altair blocks and states

* add tests for Altair get/put/contains/delete state

* enable blockchain_dag Altair state database storing

* properly return error on getting missing altair block
This commit is contained in:
tersec 2021-06-24 07:11:47 +00:00 committed by GitHub
parent ae1abf24af
commit 41e0a7abc0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 324 additions and 41 deletions

View File

@ -82,13 +82,16 @@ OK: 11/11 Fail: 0/11 Skip: 0/11
```diff ```diff
+ empty database [Preset: mainnet] OK + empty database [Preset: mainnet] OK
+ find ancestors [Preset: mainnet] OK + find ancestors [Preset: mainnet] OK
+ sanity check blocks [Preset: mainnet] OK + sanity check Altair blocks [Preset: mainnet] OK
+ sanity check Altair states [Preset: mainnet] OK
+ sanity check Altair states, reusing buffers [Preset: mainnet] OK
+ sanity check genesis roundtrip [Preset: mainnet] OK + sanity check genesis roundtrip [Preset: mainnet] OK
+ sanity check phase 0 blocks [Preset: mainnet] OK
+ sanity check phase 0 states [Preset: mainnet] OK
+ sanity check phase 0 states, reusing buffers [Preset: mainnet] OK
+ sanity check state diff roundtrip [Preset: mainnet] OK + sanity check state diff roundtrip [Preset: mainnet] OK
+ sanity check states [Preset: mainnet] OK
+ sanity check states, reusing buffers [Preset: mainnet] OK
``` ```
OK: 7/7 Fail: 0/7 Skip: 0/7 OK: 10/10 Fail: 0/10 Skip: 0/10
## Beacon state [Preset: mainnet] ## Beacon state [Preset: mainnet]
```diff ```diff
+ Smoke test initialize_beacon_state_from_eth1 [Preset: mainnet] OK + Smoke test initialize_beacon_state_from_eth1 [Preset: mainnet] OK
@ -308,4 +311,4 @@ OK: 3/3 Fail: 0/3 Skip: 0/3
OK: 1/1 Fail: 0/1 Skip: 0/1 OK: 1/1 Fail: 0/1 Skip: 0/1
---TOTAL--- ---TOTAL---
OK: 174/182 Fail: 0/182 Skip: 8/182 OK: 177/185 Fail: 0/185 Skip: 8/185

View File

@ -89,9 +89,11 @@ type
checkpoint*: proc() {.gcsafe, raises: [Defect].} checkpoint*: proc() {.gcsafe, raises: [Defect].}
keyValues: KvStoreRef # Random stuff using DbKeyKind - suitable for small values mainly! keyValues: KvStoreRef # Random stuff using DbKeyKind - suitable for small values mainly!
blocks: KvStoreRef # BlockRoot -> TrustedBeaconBlock blocks: KvStoreRef # BlockRoot -> phase0.TrustedBeaconBlock
altairBlocks: KvStoreRef # BlockRoot -> altair.TrustedBeaconBlock
stateRoots: KvStoreRef # (Slot, BlockRoot) -> StateRoot stateRoots: KvStoreRef # (Slot, BlockRoot) -> StateRoot
statesNoVal: KvStoreRef # StateRoot -> BeaconStateNoImmutableValidators statesNoVal: KvStoreRef # StateRoot -> BeaconStateNoImmutableValidators
altairStatesNoVal: KvStoreRef # StateRoot -> AltairBeaconStateNoImmutableValidators
stateDiffs: KvStoreRef ##\ stateDiffs: KvStoreRef ##\
## StateRoot -> BeaconStateDiff ## StateRoot -> BeaconStateDiff
## Instead of storing full BeaconStates, one can store only the diff from ## Instead of storing full BeaconStates, one can store only the diff from
@ -301,8 +303,10 @@ proc new*(T: type BeaconChainDB,
# V1 - expected-to-be small rows get without rowid optimizations # V1 - expected-to-be small rows get without rowid optimizations
keyValues = kvStore db.openKvStore("key_values", true).expectDb() keyValues = kvStore db.openKvStore("key_values", true).expectDb()
blocks = kvStore db.openKvStore("blocks").expectDb() blocks = kvStore db.openKvStore("blocks").expectDb()
altairBlocks = kvStore db.openKvStore("altair_blocks").expectDb()
stateRoots = kvStore db.openKvStore("state_roots", true).expectDb() stateRoots = kvStore db.openKvStore("state_roots", true).expectDb()
statesNoVal = kvStore db.openKvStore("state_no_validators2").expectDb() statesNoVal = kvStore db.openKvStore("state_no_validators2").expectDb()
altairStatesNoVal = kvStore db.openKvStore("altair_state_no_validators").expectDb()
stateDiffs = kvStore db.openKvStore("state_diffs").expectDb() stateDiffs = kvStore db.openKvStore("state_diffs").expectDb()
summaries = kvStore db.openKvStore("beacon_block_summaries", true).expectDb() summaries = kvStore db.openKvStore("beacon_block_summaries", true).expectDb()
@ -337,8 +341,10 @@ proc new*(T: type BeaconChainDB,
checkpoint: proc() = db.checkpoint(), checkpoint: proc() = db.checkpoint(),
keyValues: keyValues, keyValues: keyValues,
blocks: blocks, blocks: blocks,
altair_blocks: altair_blocks,
stateRoots: stateRoots, stateRoots: stateRoots,
statesNoVal: statesNoVal, statesNoVal: statesNoVal,
altairStatesNoVal: statesNoVal,
stateDiffs: stateDiffs, stateDiffs: stateDiffs,
summaries: summaries, summaries: summaries,
) )
@ -449,8 +455,10 @@ proc close*(db: BeaconchainDB) =
# Close things in reverse order # Close things in reverse order
discard db.summaries.close() discard db.summaries.close()
discard db.stateDiffs.close() discard db.stateDiffs.close()
discard db.altairStatesNoVal.close()
discard db.statesNoVal.close() discard db.statesNoVal.close()
discard db.stateRoots.close() discard db.stateRoots.close()
discard db.altairBlocks.close()
discard db.blocks.close() discard db.blocks.close()
discard db.keyValues.close() discard db.keyValues.close()
db.immutableValidatorsDb.close() db.immutableValidatorsDb.close()
@ -475,6 +483,10 @@ proc putBlock*(db: BeaconChainDB, value: phase0.TrustedSignedBeaconBlock) =
db.blocks.putSnappySSZ(value.root.data, value) db.blocks.putSnappySSZ(value.root.data, value)
db.putBeaconBlockSummary(value.root, value.message.toBeaconBlockSummary()) db.putBeaconBlockSummary(value.root, value.message.toBeaconBlockSummary())
proc putBlock*(db: BeaconChainDB, value: altair.TrustedSignedBeaconBlock) =
db.altairBlocks.putSnappySSZ(value.root.data, value)
db.putBeaconBlockSummary(value.root, value.message.toBeaconBlockSummary())
proc updateImmutableValidators*( proc updateImmutableValidators*(
db: BeaconChainDB, validators: openArray[Validator]) = db: BeaconChainDB, validators: openArray[Validator]) =
# Must be called before storing a state that references the new validators # Must be called before storing a state that references the new validators
@ -492,7 +504,14 @@ proc putState*(db: BeaconChainDB, key: Eth2Digest, value: phase0.BeaconState) =
key.data, key.data,
isomorphicCast[BeaconStateNoImmutableValidators](value)) isomorphicCast[BeaconStateNoImmutableValidators](value))
proc putState*(db: BeaconChainDB, value: phase0.BeaconState) = proc putState*(db: BeaconChainDB, key: Eth2Digest, value: altair.BeaconState) =
db.updateImmutableValidators(value.validators.asSeq())
db.altairStatesNoVal.putSnappySSZ(
key.data,
isomorphicCast[AltairBeaconStateNoImmutableValidators](value))
proc putState*(
db: BeaconChainDB, value: phase0.BeaconState | altair.BeaconState) =
db.putState(hash_tree_root(value), value) db.putState(hash_tree_root(value), value)
func stateRootKey(root: Eth2Digest, slot: Slot): array[40, byte] = func stateRootKey(root: Eth2Digest, slot: Slot): array[40, byte] =
@ -512,10 +531,12 @@ proc putStateDiff*(db: BeaconChainDB, root: Eth2Digest, value: BeaconStateDiff)
proc delBlock*(db: BeaconChainDB, key: Eth2Digest) = proc delBlock*(db: BeaconChainDB, key: Eth2Digest) =
db.blocks.del(key.data).expectDb() db.blocks.del(key.data).expectDb()
db.altairBlocks.del(key.data).expectDb()
db.summaries.del(key.data).expectDb() db.summaries.del(key.data).expectDb()
proc delState*(db: BeaconChainDB, key: Eth2Digest) = proc delState*(db: BeaconChainDB, key: Eth2Digest) =
db.statesNoVal.del(key.data).expectDb() db.statesNoVal.del(key.data).expectDb()
db.altairStatesNoVal.del(key.data).expectDb()
proc delStateRoot*(db: BeaconChainDB, root: Eth2Digest, slot: Slot) = proc delStateRoot*(db: BeaconChainDB, root: Eth2Digest, slot: Slot) =
db.stateRoots.del(stateRootKey(root, slot)).expectDb() db.stateRoots.del(stateRootKey(root, slot)).expectDb()
@ -556,6 +577,16 @@ proc getBlock*(db: BeaconChainDB, key: Eth2Digest):
# set root after deserializing (so it doesn't get zeroed) # set root after deserializing (so it doesn't get zeroed)
result.get().root = key result.get().root = key
proc getAltairBlock*(db: BeaconChainDB, key: Eth2Digest):
Opt[altair.TrustedSignedBeaconBlock] =
# We only store blocks that we trust in the database
result.ok(default(altair.TrustedSignedBeaconBlock))
if db.altairBlocks.getSnappySSZ(key.data, result.get) == GetResult.found:
# set root after deserializing (so it doesn't get zeroed)
result.get().root = key
else:
result.err()
proc getStateOnlyMutableValidators( proc getStateOnlyMutableValidators(
immutableValidators: openArray[ImmutableValidatorData2], immutableValidators: openArray[ImmutableValidatorData2],
store: KvStoreRef, key: openArray[byte], output: var phase0.BeaconState, store: KvStoreRef, key: openArray[byte], output: var phase0.BeaconState,
@ -598,6 +629,48 @@ proc getStateOnlyMutableValidators(
rollback(output) rollback(output)
false false
proc getAltairStateOnlyMutableValidators(
immutableValidators: openArray[ImmutableValidatorData2],
store: KvStoreRef, key: openArray[byte], output: var altair.BeaconState,
rollback: AltairRollbackProc): bool =
## Load state into `output` - BeaconState is large so we want to avoid
## re-allocating it if possible
## Return `true` iff the entry was found in the database and `output` was
## overwritten.
## Rollback will be called only if output was partially written - if it was
## not found at all, rollback will not be called
# TODO rollback is needed to deal with bug - use `noRollback` to ignore:
# https://github.com/nim-lang/Nim/issues/14126
# TODO RVO is inefficient for large objects:
# https://github.com/nim-lang/Nim/issues/13879
case store.getSnappySSZ(
key, isomorphicCast[AltairBeaconStateNoImmutableValidators](output))
of GetResult.found:
let numValidators = output.validators.len
doAssert immutableValidators.len >= numValidators
for i in 0 ..< numValidators:
let
# Bypass hash cache invalidation
dstValidator = addr output.validators.data[i]
assign(
dstValidator.pubkey,
immutableValidators[i].pubkey.loadValid().toPubKey())
assign(
dstValidator.withdrawal_credentials,
immutableValidators[i].withdrawal_credentials)
output.validators.resetCache()
true
of GetResult.notFound:
false
of GetResult.corrupted:
rollback(output)
false
proc getState( proc getState(
db: BeaconChainDBV0, db: BeaconChainDBV0,
immutableValidators: openArray[ImmutableValidatorData2], immutableValidators: openArray[ImmutableValidatorData2],
@ -646,6 +719,22 @@ proc getState*(
else: else:
true true
proc getAltairState*(
db: BeaconChainDB, key: Eth2Digest, output: var altair.BeaconState,
rollback: AltairRollbackProc): bool =
## Load state into `output` - BeaconState is large so we want to avoid
## re-allocating it if possible
## Return `true` iff the entry was found in the database and `output` was
## overwritten.
## Rollback will be called only if output was partially written - if it was
## not found at all, rollback will not be called
# TODO rollback is needed to deal with bug - use `noRollback` to ignore:
# https://github.com/nim-lang/Nim/issues/14126
# TODO RVO is inefficient for large objects:
# https://github.com/nim-lang/Nim/issues/13879
getAltairStateOnlyMutableValidators(
db.immutableValidators, db.altairStatesNoVal, key.data, output, rollback)
proc getStateRoot(db: BeaconChainDBV0, proc getStateRoot(db: BeaconChainDBV0,
root: Eth2Digest, root: Eth2Digest,
slot: Slot): Opt[Eth2Digest] = slot: Slot): Opt[Eth2Digest] =
@ -698,7 +787,9 @@ proc containsBlock*(db: BeaconChainDBV0, key: Eth2Digest): bool =
db.backend.contains(subkey(phase0.SignedBeaconBlock, key)).expectDb() db.backend.contains(subkey(phase0.SignedBeaconBlock, key)).expectDb()
proc containsBlock*(db: BeaconChainDB, key: Eth2Digest): bool = proc containsBlock*(db: BeaconChainDB, key: Eth2Digest): bool =
db.blocks.contains(key.data).expectDb() or db.v0.containsBlock(key) db.altairBlocks.contains(key.data).expectDb() or
db.blocks.contains(key.data).expectDb() or
db.v0.containsBlock(key)
proc containsState*(db: BeaconChainDBV0, key: Eth2Digest): bool = proc containsState*(db: BeaconChainDBV0, key: Eth2Digest): bool =
let sk = subkey(BeaconStateNoImmutableValidators, key) let sk = subkey(BeaconStateNoImmutableValidators, key)
@ -707,6 +798,7 @@ proc containsState*(db: BeaconChainDBV0, key: Eth2Digest): bool =
db.backend.contains(subkey(phase0.BeaconState, key)).expectDb() db.backend.contains(subkey(phase0.BeaconState, key)).expectDb()
proc containsState*(db: BeaconChainDB, key: Eth2Digest, legacy: bool = true): bool = proc containsState*(db: BeaconChainDB, key: Eth2Digest, legacy: bool = true): bool =
db.altairStatesNoVal.contains(key.data).expectDb or
db.statesNoVal.contains(key.data).expectDb or db.statesNoVal.contains(key.data).expectDb or
(legacy and db.v0.containsState(key)) (legacy and db.v0.containsState(key))

View File

@ -12,13 +12,14 @@ import
stew/[assign2, io2, objects, results], stew/[assign2, io2, objects, results],
serialization, serialization,
eth/db/[kvstore, kvstore_sqlite3], eth/db/[kvstore, kvstore_sqlite3],
./spec/[crypto, datatypes, digest], ./spec/[crypto, digest],
./spec/datatypes/[base, altair],
./ssz/[ssz_serialization, merkleization], ./ssz/[ssz_serialization, merkleization],
filepath filepath
type type
# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#beaconstate # https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#beaconstate
# Memory-representation-equivalent to a v1.0.1 BeaconState for in-place SSZ reading and writing # Memory-representation-equivalent to a phase0 BeaconState for in-place SSZ reading and writing
BeaconStateNoImmutableValidators* = object BeaconStateNoImmutableValidators* = object
# Versioning # Versioning
genesis_time*: uint64 genesis_time*: uint64
@ -71,6 +72,68 @@ type
current_justified_checkpoint*: Checkpoint current_justified_checkpoint*: Checkpoint
finalized_checkpoint*: Checkpoint finalized_checkpoint*: Checkpoint
# https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.7/specs/altair/beacon-chain.md#beaconstate
# Memory-representation-equivalent to an Altair BeaconState for in-place SSZ
# reading and writing
AltairBeaconStateNoImmutableValidators* = object
# Versioning
genesis_time*: uint64
genesis_validators_root*: Eth2Digest
slot*: Slot
fork*: Fork
# History
latest_block_header*: BeaconBlockHeader ##\
## `latest_block_header.state_root == ZERO_HASH` temporarily
block_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest] ##\
## Needed to process attestations, older to newer
state_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest]
historical_roots*: HashList[Eth2Digest, Limit HISTORICAL_ROOTS_LIMIT]
# Eth1
eth1_data*: Eth1Data
eth1_data_votes*:
HashList[Eth1Data, Limit(EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH)]
eth1_deposit_index*: uint64
# Registry
validators*: HashList[ValidatorStatus, Limit VALIDATOR_REGISTRY_LIMIT]
balances*: HashList[uint64, Limit VALIDATOR_REGISTRY_LIMIT]
# Randomness
randao_mixes*: HashArray[Limit EPOCHS_PER_HISTORICAL_VECTOR, Eth2Digest]
# Slashings
slashings*: HashArray[Limit EPOCHS_PER_SLASHINGS_VECTOR, uint64] ##\
## Per-epoch sums of slashed effective balances
# Participation
previous_epoch_participation*:
HashList[ParticipationFlags, Limit VALIDATOR_REGISTRY_LIMIT]
current_epoch_participation*:
HashList[ParticipationFlags, Limit VALIDATOR_REGISTRY_LIMIT]
# Finality
justification_bits*: uint8 ##\
## Bit set for every recent justified epoch
## Model a Bitvector[4] as a one-byte uint, which should remain consistent
## with ssz/hashing.
previous_justified_checkpoint*: Checkpoint ##\
## Previous epoch snapshot
current_justified_checkpoint*: Checkpoint
finalized_checkpoint*: Checkpoint
# Inactivity
inactivity_scores*: HashList[uint64, Limit VALIDATOR_REGISTRY_LIMIT] # [New in Altair]
# Light client sync committees
current_sync_committee*: SyncCommittee # [New in Altair]
next_sync_committee*: SyncCommittee # [New in Altair]
func getSizeofSig(x: auto, n: int = 0): seq[(string, int, int)] = func getSizeofSig(x: auto, n: int = 0): seq[(string, int, int)] =
for name, value in x.fieldPairs: for name, value in x.fieldPairs:
when value is tuple|object: when value is tuple|object:

View File

@ -559,9 +559,11 @@ proc putState(dag: ChainDAGRef, state: var StateData) =
# Ideally we would save the state and the root lookup cache in a single # Ideally we would save the state and the root lookup cache in a single
# transaction to prevent database inconsistencies, but the state loading code # transaction to prevent database inconsistencies, but the state loading code
# is resilient against one or the other going missing # is resilient against one or the other going missing
if state.data.beaconStateFork != forkAltair: case state.data.beaconStateFork:
# TODO re-enable for Altair of forkPhase0:
dag.db.putState(getStateRoot(state.data), state.data.hbsPhase0.data) dag.db.putState(getStateRoot(state.data), state.data.hbsPhase0.data)
of forkAltair:
dag.db.putState(getStateRoot(state.data), state.data.hbsAltair.data)
dag.db.putStateRoot( dag.db.putStateRoot(
state.blck.root, getStateField(state.data, slot), getStateRoot(state.data)) state.blck.root, getStateField(state.data, slot), getStateRoot(state.data))

View File

@ -105,9 +105,13 @@ func verifyStateRoot(state: phase0.BeaconState, blck: altair.TrustedBeaconBlock)
type type
RollbackProc* = proc(v: var phase0.BeaconState) {.gcsafe, raises: [Defect].} RollbackProc* = proc(v: var phase0.BeaconState) {.gcsafe, raises: [Defect].}
AltairRollbackProc* = proc(v: var altair.BeaconState) {.gcsafe, raises: [Defect].}
func noRollback*(state: var phase0.BeaconState) = func noRollback*(state: var phase0.BeaconState) =
trace "Skipping rollback of broken state" trace "Skipping rollback of broken phase 0 state"
func noRollback*(state: var altair.BeaconState) =
trace "Skipping rollback of broken Altair state"
type type
RollbackHashedProc* = proc(state: var phase0.HashedBeaconState) {.gcsafe, raises: [Defect].} RollbackHashedProc* = proc(state: var phase0.HashedBeaconState) {.gcsafe, raises: [Defect].}
@ -170,7 +174,7 @@ func noRollback*(state: var phase0.HashedBeaconState) =
func noRollback*(state: var altair.HashedBeaconState) = func noRollback*(state: var altair.HashedBeaconState) =
trace "Skipping rollback of broken Altair state" trace "Skipping rollback of broken Altair state"
proc maybeUpgradeStateToAltair( proc maybeUpgradeStateToAltair*(
state: var ForkedHashedBeaconState, altairForkSlot: Slot) = state: var ForkedHashedBeaconState, altairForkSlot: Slot) =
# Both process_slots() and state_transition_block() call this, so only run it # Both process_slots() and state_transition_block() call this, so only run it
# once by checking for existing fork. # once by checking for existing fork.

View File

@ -1,5 +1,5 @@
# beacon_chain # beacon_chain
# Copyright (c) 2018-2020 Status Research & Development GmbH # Copyright (c) 2018-2021 Status Research & Development GmbH
# Licensed and distributed under either of # Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
@ -13,8 +13,8 @@ import
math, math,
# Specs # Specs
../../beacon_chain/spec/[datatypes, crypto, digest, ../../beacon_chain/spec/[crypto, digest, keystore, signatures, presets],
keystore, signatures, presets], ../../beacon_chain/spec/datatypes/base,
# Internals # Internals
../../beacon_chain/extras, ../../beacon_chain/extras,
@ -103,8 +103,8 @@ proc mockGenesisBalancedDeposits*(
mockGenesisDepositsImpl(result, validatorCount,amount,flags): mockGenesisDepositsImpl(result, validatorCount,amount,flags):
discard discard
proc mockUpdateStateForNewDeposit*( proc mockUpdateStateForNewDeposit*[T](
state: var BeaconState, state: var T,
validator_index: uint64, validator_index: uint64,
amount: uint64, amount: uint64,
# withdrawal_credentials: Eth2Digest # withdrawal_credentials: Eth2Digest

View File

@ -12,8 +12,9 @@ import
unittest2, unittest2,
../beacon_chain/[beacon_chain_db, extras, interop, ssz], ../beacon_chain/[beacon_chain_db, extras, interop, ssz],
../beacon_chain/spec/[ ../beacon_chain/spec/[
beaconstate, crypto, datatypes, digest, forkedbeaconstate_helpers, presets, beaconstate, crypto, digest, forkedbeaconstate_helpers, presets,
state_transition], state_transition],
../beacon_chain/spec/datatypes/[phase0, altair],
../beacon_chain/consensus_object_pools/blockchain_dag, ../beacon_chain/consensus_object_pools/blockchain_dag,
eth/db/kvstore, eth/db/kvstore,
# test utilies # test utilies
@ -22,14 +23,30 @@ import
when isMainModule: when isMainModule:
import chronicles # or some random compile error happens... import chronicles # or some random compile error happens...
proc getStateRef(db: BeaconChainDB, root: Eth2Digest): NilableBeaconStateRef = proc getPhase0StateRef(db: BeaconChainDB, root: Eth2Digest):
phase0.NilableBeaconStateRef =
# load beaconstate the way the block pool does it - into an existing instance # load beaconstate the way the block pool does it - into an existing instance
let res = BeaconStateRef() let res = (phase0.BeaconStateRef)()
if db.getState(root, res[], noRollback): if db.getState(root, res[], noRollback):
return res return res
func withDigest(blck: TrustedBeaconBlock): TrustedSignedBeaconBlock = proc getAltairStateRef(db: BeaconChainDB, root: Eth2Digest):
TrustedSignedBeaconBlock( altair.NilableBeaconStateRef =
# load beaconstate the way the block pool does it - into an existing instance
let res = (altair.BeaconStateRef)()
if db.getAltairState(root, res[], noRollback):
return res
func withDigest(blck: phase0.TrustedBeaconBlock):
phase0.TrustedSignedBeaconBlock =
phase0.TrustedSignedBeaconBlock(
message: blck,
root: hash_tree_root(blck)
)
func withDigest(blck: altair.TrustedBeaconBlock):
altair.TrustedSignedBeaconBlock =
altair.TrustedSignedBeaconBlock(
message: blck, message: blck,
root: hash_tree_root(blck) root: hash_tree_root(blck)
) )
@ -39,15 +56,15 @@ suite "Beacon chain DB" & preset():
var var
db = BeaconChainDB.new(defaultRuntimePreset, "", inMemory = true) db = BeaconChainDB.new(defaultRuntimePreset, "", inMemory = true)
check: check:
db.getStateRef(Eth2Digest()).isNil db.getPhase0StateRef(Eth2Digest()).isNil
db.getBlock(Eth2Digest()).isNone db.getBlock(Eth2Digest()).isNone
test "sanity check blocks" & preset(): test "sanity check phase 0 blocks" & preset():
var var
db = BeaconChainDB.new(defaultRuntimePreset, "", inMemory = true) db = BeaconChainDB.new(defaultRuntimePreset, "", inMemory = true)
let let
signedBlock = withDigest(TrustedBeaconBlock()) signedBlock = withDigest((phase0.TrustedBeaconBlock)())
root = hash_tree_root(signedBlock.message) root = hash_tree_root(signedBlock.message)
db.putBlock(signedBlock) db.putBlock(signedBlock)
@ -56,6 +73,11 @@ suite "Beacon chain DB" & preset():
db.containsBlock(root) db.containsBlock(root)
db.getBlock(root).get() == signedBlock db.getBlock(root).get() == signedBlock
db.delBlock(root)
check:
not db.containsBlock(root)
db.getBlock(root).isErr()
db.putStateRoot(root, signedBlock.message.slot, root) db.putStateRoot(root, signedBlock.message.slot, root)
var root2 = root var root2 = root
root2.data[0] = root.data[0] + 1 root2.data[0] = root.data[0] + 1
@ -67,7 +89,37 @@ suite "Beacon chain DB" & preset():
db.close() db.close()
test "sanity check states" & preset(): test "sanity check Altair blocks" & preset():
var
db = BeaconChainDB.new(defaultRuntimePreset, "", inMemory = true)
let
signedBlock = withDigest((altair.TrustedBeaconBlock)())
root = hash_tree_root(signedBlock.message)
db.putBlock(signedBlock)
check:
db.containsBlock(root)
db.getAltairBlock(root).get() == signedBlock
db.delBlock(root)
check:
not db.containsBlock(root)
db.getAltairBlock(root).isErr()
db.putStateRoot(root, signedBlock.message.slot, root)
var root2 = root
root2.data[0] = root.data[0] + 1
db.putStateRoot(root, signedBlock.message.slot + 1, root2)
check:
db.getStateRoot(root, signedBlock.message.slot).get() == root
db.getStateRoot(root, signedBlock.message.slot + 1).get() == root2
db.close()
test "sanity check phase 0 states" & preset():
var var
db = makeTestDB(SLOTS_PER_EPOCH) db = makeTestDB(SLOTS_PER_EPOCH)
dag = init(ChainDAGRef, defaultRuntimePreset, db) dag = init(ChainDAGRef, defaultRuntimePreset, db)
@ -83,19 +135,46 @@ suite "Beacon chain DB" & preset():
check: check:
db.containsState(root) db.containsState(root)
hash_tree_root(db.getStateRef(root)[]) == root hash_tree_root(db.getPhase0StateRef(root)[]) == root
db.delState(root) db.delState(root)
check: not db.containsState(root) check:
not db.containsState(root)
db.getPhase0StateRef(root).isNil
db.close() db.close()
test "sanity check states, reusing buffers" & preset(): test "sanity check Altair states" & preset():
var
db = makeTestDB(SLOTS_PER_EPOCH)
dag = init(ChainDAGRef, defaultRuntimePreset, db)
testStates = getTestStates(dag.headState.data, true)
# Ensure transitions beyond just adding validators and increasing slots
sort(testStates) do (x, y: ref ForkedHashedBeaconState) -> int:
cmp($getStateRoot(x[]), $getStateRoot(y[]))
for state in testStates:
db.putState(state[].hbsAltair.data)
let root = hash_tree_root(state[])
check:
db.containsState(root)
hash_tree_root(db.getAltairStateRef(root)[]) == root
db.delState(root)
check:
not db.containsState(root)
db.getAltairStateRef(root).isNil
db.close()
test "sanity check phase 0 states, reusing buffers" & preset():
var var
db = makeTestDB(SLOTS_PER_EPOCH) db = makeTestDB(SLOTS_PER_EPOCH)
dag = init(ChainDAGRef, defaultRuntimePreset, db) dag = init(ChainDAGRef, defaultRuntimePreset, db)
let stateBuffer = BeaconStateRef() let stateBuffer = (phase0.BeaconStateRef)()
var testStates = getTestStates(dag.headState.data) var testStates = getTestStates(dag.headState.data)
# Ensure transitions beyond just adding validators and increasing slots # Ensure transitions beyond just adding validators and increasing slots
@ -112,7 +191,37 @@ suite "Beacon chain DB" & preset():
hash_tree_root(stateBuffer[]) == root hash_tree_root(stateBuffer[]) == root
db.delState(root) db.delState(root)
check: not db.containsState(root) check:
not db.containsState(root)
not db.getState(root, stateBuffer[], noRollback)
db.close()
test "sanity check Altair states, reusing buffers" & preset():
var
db = makeTestDB(SLOTS_PER_EPOCH)
dag = init(ChainDAGRef, defaultRuntimePreset, db)
let stateBuffer = (altair.BeaconStateRef)()
var testStates = getTestStates(dag.headState.data, true)
# Ensure transitions beyond just adding validators and increasing slots
sort(testStates) do (x, y: ref ForkedHashedBeaconState) -> int:
cmp($getStateRoot(x[]), $getStateRoot(y[]))
for state in testStates:
db.putState(state[].hbsAltair.data)
let root = hash_tree_root(state[])
check:
db.getAltairState(root, stateBuffer[], noRollback)
db.containsState(root)
hash_tree_root(stateBuffer[]) == root
db.delState(root)
check:
not db.containsState(root)
not db.getAltairState(root, stateBuffer[], noRollback)
db.close() db.close()
@ -122,11 +231,11 @@ suite "Beacon chain DB" & preset():
let let
a0 = withDigest( a0 = withDigest(
TrustedBeaconBlock(slot: GENESIS_SLOT + 0)) (phase0.TrustedBeaconBlock)(slot: GENESIS_SLOT + 0))
a1 = withDigest( a1 = withDigest(
TrustedBeaconBlock(slot: GENESIS_SLOT + 1, parent_root: a0.root)) (phase0.TrustedBeaconBlock)(slot: GENESIS_SLOT + 1, parent_root: a0.root))
a2 = withDigest( a2 = withDigest(
TrustedBeaconBlock(slot: GENESIS_SLOT + 2, parent_root: a1.root)) (phase0.TrustedBeaconBlock)(slot: GENESIS_SLOT + 2, parent_root: a1.root))
doAssert toSeq(db.getAncestors(a0.root)) == [] doAssert toSeq(db.getAncestors(a0.root)) == []
doAssert toSeq(db.getAncestors(a2.root)) == [] doAssert toSeq(db.getAncestors(a2.root)) == []
@ -175,7 +284,7 @@ suite "Beacon chain DB" & preset():
db.putState(state[]) db.putState(state[])
check db.containsState(root) check db.containsState(root)
let state2 = db.getStateRef(root) let state2 = db.getPhase0StateRef(root)
db.delState(root) db.delState(root)
check not db.containsState(root) check not db.containsState(root)
db.close() db.close()

View File

@ -16,7 +16,7 @@ import
beaconstate, crypto, datatypes, forkedbeaconstate_helpers, helpers, beaconstate, crypto, datatypes, forkedbeaconstate_helpers, helpers,
presets, state_transition] presets, state_transition]
proc valid_deposit(state: var BeaconState) = proc valid_deposit[T](state: var T) =
const deposit_amount = MAX_EFFECTIVE_BALANCE const deposit_amount = MAX_EFFECTIVE_BALANCE
let validator_index = state.validators.len let validator_index = state.validators.len
let deposit = mockUpdateStateForNewDeposit( let deposit = mockUpdateStateForNewDeposit(
@ -41,7 +41,8 @@ proc valid_deposit(state: var BeaconState) =
EFFECTIVE_BALANCE_INCREMENT EFFECTIVE_BALANCE_INCREMENT
) )
proc getTestStates*(initialState: ForkedHashedBeaconState): proc getTestStates*(
initialState: ForkedHashedBeaconState, useAltair: bool = false):
seq[ref ForkedHashedBeaconState] = seq[ref ForkedHashedBeaconState] =
# Randomly generated slot numbers, with a jump to around # Randomly generated slot numbers, with a jump to around
# SLOTS_PER_HISTORICAL_ROOT to force wraparound of those # SLOTS_PER_HISTORICAL_ROOT to force wraparound of those
@ -68,7 +69,16 @@ proc getTestStates*(initialState: ForkedHashedBeaconState):
if getStateField(tmpState[], slot) < slot: if getStateField(tmpState[], slot) < slot:
doAssert process_slots( doAssert process_slots(
tmpState[], slot, cache, rewards, {}, FAR_FUTURE_SLOT) tmpState[], slot, cache, rewards, {}, FAR_FUTURE_SLOT)
if useAltair and epoch == 1:
maybeUpgradeStateToAltair(tmpState[], slot)
if i mod 3 == 0: if i mod 3 == 0:
valid_deposit(tmpState.hbsPhase0.data) if tmpState[].beaconStateFork == forkPhase0:
valid_deposit(tmpState[].hbsPhase0.data)
else:
valid_deposit(tmpState[].hbsAltair.data)
doAssert getStateField(tmpState[], slot) == slot doAssert getStateField(tmpState[], slot) == slot
result.add assignClone(tmpState[])
if useAltair == (tmpState[].beaconStateFork == forkAltair):
result.add assignClone(tmpState[])