* add ancestor getter to db layer, and use
* remove head block call
* add trivial beacon chain db test
This commit is contained in:
Jacek Sieka 2019-02-21 11:20:50 -06:00
parent 2d307e2257
commit 1d9c91d230
No known key found for this signature in database
GPG Key ID: 6299FEB3EB6FA465
5 changed files with 127 additions and 55 deletions

View File

@ -72,3 +72,25 @@ proc getHead*(db: BeaconChainDB, T: type BeaconBlock): Option[T] =
proc contains*(
db: BeaconChainDB, key: Eth2Digest, T: type DbTypes): bool =
db.backend.contains(subkey(T, key))
proc getAncestors*(
db: BeaconChainDB, blck: BeaconBlock,
predicate: proc(blck: BeaconBlock): bool = nil): seq[BeaconBlock] =
## Load a chain of ancestors for blck - returns a list of blocks with the
## oldest block last (blck will be at result[0]).
##
## The search will go on until the ancestor cannot be found (or slot 0) or
## the predicate returns true (you found what you were looking for) - the list
## will include the last block as well
## TODO maybe turn into iterator? or add iterator also?
result = @[blck]
while result[^1].slot > 0.Slot:
let parent = db.get(result[^1].parent_root, BeaconBlock)
if parent.isNone(): break
result.add parent.get()
if predicate != nil and predicate(parent.get()): break

View File

@ -451,70 +451,66 @@ proc updateHeadBlock(node: BeaconNode, blck: BeaconBlock) =
# chain of ancestors of the new block. We will do this by loading each
# successive parent block and checking if we can find the corresponding state
# in the database.
var parents = @[blck]
while true:
let top = parents[^1]
let
ancestors = node.db.getAncestors(blck) do (bb: BeaconBlock) -> bool:
node.db.contains(bb.state_root, BeaconState)
ancestor = ancestors[^1]
# We're looking for the most recent state that we have in the database
# that also exists on the ancestor chain.
if (let prevState = node.db.get(top.state_root, BeaconState);
prevState.isSome()):
# Got it!
notice "Replaying state transitions",
stateSlot = humaneSlotNum(node.beaconState.slot),
prevStateSlot = humaneSlotNum(prevState.get().slot)
node.beaconState = prevState.get()
break
# Several things can happen, but the most common one should be that we found
# a beacon state
if (let state = node.db.get(ancestor.state_root, BeaconState); state.isSome()):
# Got it!
notice "Replaying state transitions",
stateSlot = humaneSlotNum(node.beaconState.slot),
prevStateSlot = humaneSlotNum(state.get().slot)
node.beaconState = state.get()
if top.slot == 0:
# We've arrived at the genesis block and still haven't found what we're
# looking for. This is very bad - are we receiving blocks from a different
# chain? What's going on?
# TODO crashing like this is the wrong thing to do, obviously, but
# we'll do it anyway just to see if it ever happens - if it does,
# it's likely a bug :)
error "Couldn't find ancestor state",
blockSlot = humaneSlotNum(blck.slot),
blockRoot = shortHash(hash_tree_root_final(blck))
doAssert false, "Oh noes, we passed big bang!"
if (let parent = node.db.get(top.parent_root, BeaconBlock); parent.isSome):
# We're lucky this time - we found the parent block in the database, so
# we put it on the stack and keep looking
parents.add(parent.get())
else:
# We don't have the parent block. This is a bit strange, but may happen
# if things are happening seriously out of order or if we're back after
# a net split or restart, for example. Once the missing block arrives,
# we should retry setting the head block..
# TODO implement block sync here
# TODO instead of doing block sync here, make sure we are sync already
# elsewhere, so as to simplify the logic of finding the block
# here..
error "Parent missing! Too bad, because sync is also missing :/",
parentRoot = shortHash(top.parent_root),
blockSlot = humaneSlotNum(top.slot)
quit("So long")
elif ancestor.slot == 0:
# We've arrived at the genesis block and still haven't found what we're
# looking for. This is very bad - are we receiving blocks from a different
# chain? What's going on?
# TODO crashing like this is the wrong thing to do, obviously, but
# we'll do it anyway just to see if it ever happens - if it does,
# it's likely a bug :)
error "Couldn't find ancestor state",
blockSlot = humaneSlotNum(blck.slot),
blockRoot = shortHash(hash_tree_root_final(blck))
doAssert false, "Oh noes, we passed big bang!"
else:
# We don't have the parent block. This is a bit strange, but may happen
# if things are happening seriously out of order or if we're back after
# a net split or restart, for example. Once the missing block arrives,
# we should retry setting the head block..
# TODO implement block sync here
# TODO instead of doing block sync here, make sure we are sync already
# elsewhere, so as to simplify the logic of finding the block
# here..
error "Parent missing! Too bad, because sync is also missing :/",
parentRoot = shortHash(ancestor.parent_root),
blockSlot = humaneSlotNum(ancestor.slot)
doAssert false, "So long"
# If we come this far, we found the state root. The last block on the stack
# is the one that produced this particular state, so we can pop it
# TODO it might be possible to use the latest block hashes from the state to
# do this more efficiently.. whatever!
discard parents.pop()
# Time to replay all the blocks between then and now.
while parents.len > 0:
let last = parents.pop()
# Time to replay all the blocks between then and now. We skip the one because
# it's the one that we found the state with, and it has already been
# applied
for i in countdown(ancestors.len - 2, 0):
let last = ancestors[i]
skipSlots(node.beaconState, last.parent_root, last.slot)
# TODO technically, we should be storing states here, because we're now
# going down a different fork
let ok = updateState(
node.beaconState, last.parent_root, some(last),
if parents.len == 0: {} else: {skipValidation})
if ancestors.len == 0: {} else: {skipValidation})
doAssert(ok)
doAssert hash_tree_root_final(node.beaconState) == blck.state_root
node.headBlock = blck
node.headBlockRoot = hash_tree_root_final(blck)
node.db.putHead(node.headBlockRoot)
@ -536,7 +532,6 @@ proc onBeaconBlock(node: BeaconNode, blck: BeaconBlock) =
blockRoot = shortHash(blockRoot),
stateSlot = humaneSlotNum(stateSlot)
updateHeadBlock(node, node.headBlock)
return
info "Block received",

View File

@ -7,10 +7,11 @@
import
./test_attestation_pool,
./test_beacon_chain_db,
./test_beacon_node,
./test_beaconstate,
./test_state_transition,
./test_helpers,
./test_ssz,
./test_validator,
./test_beacon_node,
./test_sync_protocol
./test_state_transition,
./test_sync_protocol,
./test_validator

View File

@ -29,7 +29,7 @@ VALIDATOR_KEYGEN_BIN=$BUILD_OUTPUTS_DIR/validator_keygen
# Run with "SHARD_COUNT=4 ./start.sh" to change these
DEFS="-d:SHARD_COUNT=${SHARD_COUNT:-4} " # Spec default: 1024
DEFS+="-d:EPOCH_LENGTH=${EPOCH_LENGTH:-8} " # Spec default: 64
DEFS+="-d:SECONDS_PER_SLOT=${SECONDS_PER_SLOT:-4} " # Spec default: 6
DEFS+="-d:SECONDS_PER_SLOT=${SECONDS_PER_SLOT:-6} " # Spec default: 6
if [[ -z "$SKIP_BUILDS" ]]; then
nim c -o:"$VALIDATOR_KEYGEN_BIN" $DEFS -d:release beacon_chain/validator_keygen

View File

@ -0,0 +1,54 @@
# Nimbus
# Copyright (c) 2018 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import options, unittest, strutils, eth/trie/[db],
../beacon_chain/[beacon_chain_db, ssz],
../beacon_chain/spec/[datatypes, digest, crypto]
suite "Beacon chain DB":
var
db = init(BeaconChainDB, newMemoryDB())
test "empty database":
check:
db.get(Eth2Digest(), BeaconState).isNone
db.get(Eth2Digest(), BeaconBlock).isNone
test "find ancestors":
var x: ValidatorSig
var y = init(ValidatorSig, x.getBytes())
check: x == y
let
a0 = BeaconBlock(slot: 0)
a1 = BeaconBlock(slot: 1, parent_root: hash_tree_root_final(a0))
a2 = BeaconBlock(slot: 2, parent_root: hash_tree_root_final(a1))
# TODO check completely kills compile times here
doAssert db.getAncestors(a0) == [a0]
doAssert db.getAncestors(a2) == [a2]
db.put(a2)
doAssert db.getAncestors(a0) == [a0]
doAssert db.getAncestors(a2) == [a2]
db.put(a1)
doAssert db.getAncestors(a0) == [a0]
doAssert db.getAncestors(a2) == [a2, a1]
db.put(a0)
doAssert db.getAncestors(a0) == [a0]
doAssert db.getAncestors(a2) == [a2, a1, a0]
let tmp = db.getAncestors(a2) do (b: BeaconBlock) -> bool:
b.slot == 1
doAssert tmp == [a2, a1]