2019-02-21 17:20:50 +00:00
|
|
|
# Nimbus
|
2021-03-15 14:11:51 +00:00
|
|
|
# Copyright (c) 2018-2021 Status Research & Development GmbH
|
2019-02-21 17:20:50 +00:00
|
|
|
# Licensed under either of
|
2019-11-25 15:30:02 +00:00
|
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0)
|
|
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT)
|
2019-02-21 17:20:50 +00:00
|
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
|
2019-11-14 10:47:55 +00:00
|
|
|
{.used.}
|
|
|
|
|
2021-03-15 14:11:51 +00:00
|
|
|
import algorithm, options, sequtils, unittest,
|
|
|
|
../beacon_chain/[beacon_chain_db, extras, interop, ssz],
|
2020-06-23 13:54:24 +00:00
|
|
|
../beacon_chain/spec/[
|
2020-07-07 23:02:14 +00:00
|
|
|
beaconstate, datatypes, digest, crypto, state_transition, presets],
|
2021-03-15 14:11:51 +00:00
|
|
|
../beacon_chain/consensus_object_pools/blockchain_dag,
|
2020-04-27 16:36:28 +00:00
|
|
|
eth/db/kvstore,
|
2019-05-27 12:48:13 +00:00
|
|
|
# test utilies
|
2021-03-15 14:11:51 +00:00
|
|
|
./testutil, ./testblockutil, ./teststateutil
|
|
|
|
|
|
|
|
when isMainModule:
|
|
|
|
import chronicles # or some random compile error happens...
|
2019-02-21 17:20:50 +00:00
|
|
|
|
2020-04-28 08:08:32 +00:00
|
|
|
proc getStateRef(db: BeaconChainDB, root: Eth2Digest): NilableBeaconStateRef =
|
2020-07-30 19:18:17 +00:00
|
|
|
# load beaconstate the way the block pool does it - into an existing instance
|
2020-04-28 08:08:32 +00:00
|
|
|
let res = BeaconStateRef()
|
|
|
|
if db.getState(root, res[], noRollback):
|
|
|
|
return res
|
|
|
|
|
2020-05-14 10:38:45 +00:00
|
|
|
template wrappedTimedTest(name: string, body: untyped) =
|
2020-05-28 08:28:14 +00:00
|
|
|
# `check` macro takes a copy of whatever it's checking, on the stack!
|
2020-05-14 10:38:45 +00:00
|
|
|
block: # Symbol namespacing
|
|
|
|
proc wrappedTest() =
|
|
|
|
timedTest name:
|
|
|
|
body
|
|
|
|
wrappedTest()
|
|
|
|
|
2020-07-16 13:16:51 +00:00
|
|
|
func withDigest(blck: TrustedBeaconBlock): TrustedSignedBeaconBlock =
|
|
|
|
TrustedSignedBeaconBlock(
|
|
|
|
message: blck,
|
|
|
|
root: hash_tree_root(blck)
|
|
|
|
)
|
|
|
|
|
2020-03-10 04:00:19 +00:00
|
|
|
suiteReport "Beacon chain DB" & preset():
|
2020-05-14 10:38:45 +00:00
|
|
|
wrappedTimedTest "empty database" & preset():
|
2019-03-08 16:40:17 +00:00
|
|
|
var
|
2021-04-06 18:56:45 +00:00
|
|
|
db = BeaconChainDB.new(defaultRuntimePreset, "", inMemory = true)
|
2019-02-21 17:20:50 +00:00
|
|
|
check:
|
2020-04-28 08:08:32 +00:00
|
|
|
db.getStateRef(Eth2Digest()).isNil
|
|
|
|
db.getBlock(Eth2Digest()).isNone
|
2019-02-21 17:20:50 +00:00
|
|
|
|
2020-05-14 10:38:45 +00:00
|
|
|
wrappedTimedTest "sanity check blocks" & preset():
|
2019-03-08 16:40:17 +00:00
|
|
|
var
|
2021-04-06 18:56:45 +00:00
|
|
|
db = BeaconChainDB.new(defaultRuntimePreset, "", inMemory = true)
|
2019-03-08 16:40:17 +00:00
|
|
|
|
|
|
|
let
|
2020-07-16 13:16:51 +00:00
|
|
|
signedBlock = withDigest(TrustedBeaconBlock())
|
2020-02-29 15:15:44 +00:00
|
|
|
root = hash_tree_root(signedBlock.message)
|
2019-03-08 16:40:17 +00:00
|
|
|
|
2020-02-29 15:15:44 +00:00
|
|
|
db.putBlock(signedBlock)
|
2019-03-08 16:40:17 +00:00
|
|
|
|
|
|
|
check:
|
|
|
|
db.containsBlock(root)
|
2020-02-29 15:15:44 +00:00
|
|
|
db.getBlock(root).get() == signedBlock
|
2019-03-08 16:40:17 +00:00
|
|
|
|
2020-02-29 15:15:44 +00:00
|
|
|
db.putStateRoot(root, signedBlock.message.slot, root)
|
2020-08-07 20:17:24 +00:00
|
|
|
var root2 = root
|
|
|
|
root2.data[0] = root.data[0] + 1
|
|
|
|
db.putStateRoot(root, signedBlock.message.slot + 1, root2)
|
|
|
|
|
2019-03-28 06:10:48 +00:00
|
|
|
check:
|
2020-02-29 15:15:44 +00:00
|
|
|
db.getStateRoot(root, signedBlock.message.slot).get() == root
|
2020-08-07 20:17:24 +00:00
|
|
|
db.getStateRoot(root, signedBlock.message.slot + 1).get() == root2
|
2019-03-28 06:10:48 +00:00
|
|
|
|
2020-09-12 05:35:58 +00:00
|
|
|
db.close()
|
|
|
|
|
2020-05-14 10:38:45 +00:00
|
|
|
wrappedTimedTest "sanity check states" & preset():
|
2019-03-08 16:40:17 +00:00
|
|
|
var
|
2021-03-15 14:11:51 +00:00
|
|
|
db = makeTestDB(SLOTS_PER_EPOCH)
|
|
|
|
dag = init(ChainDAGRef, defaultRuntimePreset, db)
|
|
|
|
testStates = getTestStates(dag.headState.data)
|
2019-03-08 16:40:17 +00:00
|
|
|
|
2021-03-15 14:11:51 +00:00
|
|
|
# Ensure transitions beyond just adding validators and increasing slots
|
|
|
|
sort(testStates) do (x, y: ref HashedBeaconState) -> int:
|
|
|
|
cmp($x.root, $y.root)
|
2019-03-08 16:40:17 +00:00
|
|
|
|
2021-03-15 14:11:51 +00:00
|
|
|
for state in testStates:
|
|
|
|
db.putState(state[].data)
|
|
|
|
let root = hash_tree_root(state[].data)
|
2019-03-08 16:40:17 +00:00
|
|
|
|
2021-03-15 14:11:51 +00:00
|
|
|
check:
|
|
|
|
db.containsState(root)
|
|
|
|
hash_tree_root(db.getStateRef(root)[]) == root
|
|
|
|
|
|
|
|
db.delState(root)
|
|
|
|
check: not db.containsState(root)
|
|
|
|
|
|
|
|
db.close()
|
|
|
|
|
|
|
|
wrappedTimedTest "sanity check states, reusing buffers" & preset():
|
|
|
|
var
|
|
|
|
db = makeTestDB(SLOTS_PER_EPOCH)
|
|
|
|
dag = init(ChainDAGRef, defaultRuntimePreset, db)
|
|
|
|
|
|
|
|
let stateBuffer = BeaconStateRef()
|
|
|
|
var testStates = getTestStates(dag.headState.data)
|
|
|
|
|
|
|
|
# Ensure transitions beyond just adding validators and increasing slots
|
|
|
|
sort(testStates) do (x, y: ref HashedBeaconState) -> int:
|
|
|
|
cmp($x.root, $y.root)
|
|
|
|
|
|
|
|
for state in testStates:
|
|
|
|
db.putState(state[].data)
|
|
|
|
let root = hash_tree_root(state[].data)
|
|
|
|
|
|
|
|
check:
|
|
|
|
db.getState(root, stateBuffer[], noRollback)
|
|
|
|
db.containsState(root)
|
|
|
|
hash_tree_root(stateBuffer[]) == root
|
|
|
|
|
|
|
|
db.delState(root)
|
|
|
|
check: not db.containsState(root)
|
|
|
|
|
|
|
|
db.close()
|
|
|
|
|
|
|
|
wrappedTimedTest "sanity check full states" & preset():
|
|
|
|
var
|
|
|
|
db = makeTestDB(SLOTS_PER_EPOCH)
|
|
|
|
dag = init(ChainDAGRef, defaultRuntimePreset, db)
|
|
|
|
testStates = getTestStates(dag.headState.data)
|
|
|
|
|
|
|
|
# Ensure transitions beyond just adding validators and increasing slots
|
|
|
|
sort(testStates) do (x, y: ref HashedBeaconState) -> int:
|
|
|
|
cmp($x.root, $y.root)
|
|
|
|
|
|
|
|
for state in testStates:
|
|
|
|
db.putStateFull(state[].data)
|
|
|
|
let root = hash_tree_root(state[].data)
|
|
|
|
|
|
|
|
check:
|
|
|
|
db.containsState(root)
|
|
|
|
hash_tree_root(db.getStateRef(root)[]) == root
|
|
|
|
|
|
|
|
db.delState(root)
|
|
|
|
check: not db.containsState(root)
|
|
|
|
|
|
|
|
db.close()
|
|
|
|
|
|
|
|
wrappedTimedTest "sanity check full states, reusing buffers" & preset():
|
|
|
|
var
|
|
|
|
db = makeTestDB(SLOTS_PER_EPOCH)
|
|
|
|
dag = init(ChainDAGRef, defaultRuntimePreset, db)
|
|
|
|
|
|
|
|
let stateBuffer = BeaconStateRef()
|
|
|
|
var testStates = getTestStates(dag.headState.data)
|
|
|
|
|
|
|
|
# Ensure transitions beyond just adding validators and increasing slots
|
|
|
|
sort(testStates) do (x, y: ref HashedBeaconState) -> int:
|
|
|
|
cmp($x.root, $y.root)
|
|
|
|
|
|
|
|
for state in testStates:
|
|
|
|
db.putStateFull(state[].data)
|
|
|
|
let root = hash_tree_root(state[].data)
|
|
|
|
|
|
|
|
check:
|
|
|
|
db.getState(root, stateBuffer[], noRollback)
|
|
|
|
db.containsState(root)
|
|
|
|
hash_tree_root(stateBuffer[]) == root
|
|
|
|
|
|
|
|
db.delState(root)
|
|
|
|
check: not db.containsState(root)
|
2019-03-08 16:40:17 +00:00
|
|
|
|
2020-09-12 05:35:58 +00:00
|
|
|
db.close()
|
|
|
|
|
2020-05-14 10:38:45 +00:00
|
|
|
wrappedTimedTest "find ancestors" & preset():
|
2019-03-08 16:40:17 +00:00
|
|
|
var
|
2021-04-06 18:56:45 +00:00
|
|
|
db = BeaconChainDB.new(defaultRuntimePreset, "", inMemory = true)
|
2019-02-21 17:20:50 +00:00
|
|
|
|
|
|
|
let
|
2020-07-16 13:16:51 +00:00
|
|
|
a0 = withDigest(
|
2020-06-25 10:23:10 +00:00
|
|
|
TrustedBeaconBlock(slot: GENESIS_SLOT + 0))
|
2020-07-16 13:16:51 +00:00
|
|
|
a1 = withDigest(
|
|
|
|
TrustedBeaconBlock(slot: GENESIS_SLOT + 1, parent_root: a0.root))
|
|
|
|
a2 = withDigest(
|
|
|
|
TrustedBeaconBlock(slot: GENESIS_SLOT + 2, parent_root: a1.root))
|
2019-02-21 17:20:50 +00:00
|
|
|
|
2020-07-16 13:16:51 +00:00
|
|
|
doAssert toSeq(db.getAncestors(a0.root)) == []
|
|
|
|
doAssert toSeq(db.getAncestors(a2.root)) == []
|
2019-02-21 17:20:50 +00:00
|
|
|
|
2020-11-03 22:30:43 +00:00
|
|
|
doAssert toSeq(db.getAncestorSummaries(a0.root)).len == 0
|
|
|
|
doAssert toSeq(db.getAncestorSummaries(a2.root)).len == 0
|
|
|
|
|
2019-02-21 21:38:26 +00:00
|
|
|
db.putBlock(a2)
|
2019-02-21 17:20:50 +00:00
|
|
|
|
2020-07-16 13:16:51 +00:00
|
|
|
doAssert toSeq(db.getAncestors(a0.root)) == []
|
|
|
|
doAssert toSeq(db.getAncestors(a2.root)) == [a2]
|
2019-02-21 17:20:50 +00:00
|
|
|
|
2020-11-03 22:30:43 +00:00
|
|
|
doAssert toSeq(db.getAncestorSummaries(a0.root)).len == 0
|
|
|
|
doAssert toSeq(db.getAncestorSummaries(a2.root)).len == 1
|
|
|
|
|
2019-02-21 21:38:26 +00:00
|
|
|
db.putBlock(a1)
|
2019-02-21 17:20:50 +00:00
|
|
|
|
2020-07-16 13:16:51 +00:00
|
|
|
doAssert toSeq(db.getAncestors(a0.root)) == []
|
|
|
|
doAssert toSeq(db.getAncestors(a2.root)) == [a2, a1]
|
2019-02-21 17:20:50 +00:00
|
|
|
|
2020-11-03 22:30:43 +00:00
|
|
|
doAssert toSeq(db.getAncestorSummaries(a0.root)).len == 0
|
|
|
|
doAssert toSeq(db.getAncestorSummaries(a2.root)).len == 2
|
|
|
|
|
2019-02-21 21:38:26 +00:00
|
|
|
db.putBlock(a0)
|
2019-02-21 17:20:50 +00:00
|
|
|
|
2020-07-16 13:16:51 +00:00
|
|
|
doAssert toSeq(db.getAncestors(a0.root)) == [a0]
|
|
|
|
doAssert toSeq(db.getAncestors(a2.root)) == [a2, a1, a0]
|
2019-09-05 14:27:28 +00:00
|
|
|
|
2020-11-03 22:30:43 +00:00
|
|
|
doAssert toSeq(db.getAncestorSummaries(a0.root)).len == 1
|
|
|
|
doAssert toSeq(db.getAncestorSummaries(a2.root)).len == 3
|
|
|
|
|
2020-05-14 10:38:45 +00:00
|
|
|
wrappedTimedTest "sanity check genesis roundtrip" & preset():
|
2019-09-05 14:27:28 +00:00
|
|
|
# This is a really dumb way of checking that we can roundtrip a genesis
|
|
|
|
# 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
|
2021-04-06 18:56:45 +00:00
|
|
|
db = BeaconChainDB.new(defaultRuntimePreset, "", inMemory = true)
|
2019-09-05 14:27:28 +00:00
|
|
|
|
|
|
|
let
|
|
|
|
state = initialize_beacon_state_from_eth1(
|
2020-07-07 23:02:14 +00:00
|
|
|
defaultRuntimePreset, eth1BlockHash, 0,
|
|
|
|
makeInitialDeposits(SLOTS_PER_EPOCH), {skipBlsValidation})
|
2020-04-29 20:12:07 +00:00
|
|
|
root = hash_tree_root(state[])
|
2019-09-05 14:27:28 +00:00
|
|
|
|
2020-04-28 08:08:32 +00:00
|
|
|
db.putState(state[])
|
2019-09-05 14:27:28 +00:00
|
|
|
|
2020-05-28 08:28:14 +00:00
|
|
|
check db.containsState(root)
|
|
|
|
let state2 = db.getStateRef(root)
|
2021-01-18 20:34:41 +00:00
|
|
|
db.delState(root)
|
|
|
|
check not db.containsState(root)
|
|
|
|
db.close()
|
|
|
|
|
|
|
|
check:
|
|
|
|
hash_tree_root(state2[]) == root
|
|
|
|
|
|
|
|
wrappedTimedTest "sanity check state diff roundtrip" & preset():
|
|
|
|
var
|
2021-04-06 18:56:45 +00:00
|
|
|
db = BeaconChainDB.new(defaultRuntimePreset, "", inMemory = true)
|
2021-01-18 20:34:41 +00:00
|
|
|
|
|
|
|
# TODO htr(diff) probably not interesting/useful, but stand-in
|
|
|
|
let
|
|
|
|
stateDiff = BeaconStateDiff()
|
|
|
|
root = hash_tree_root(stateDiff)
|
|
|
|
|
|
|
|
db.putStateDiff(root, stateDiff)
|
|
|
|
|
|
|
|
check db.containsStateDiff(root)
|
|
|
|
let state2 = db.getStateDiff(root)
|
|
|
|
db.delStateDiff(root)
|
|
|
|
check not db.containsStateDiff(root)
|
2020-09-12 05:35:58 +00:00
|
|
|
db.close()
|
2020-05-28 08:28:14 +00:00
|
|
|
|
2019-09-05 14:27:28 +00:00
|
|
|
check:
|
2020-05-28 08:28:14 +00:00
|
|
|
hash_tree_root(state2[]) == root
|