cleanups
* add ancestor getter to db layer, and use * remove head block call * add trivial beacon chain db test
This commit is contained in:
parent
2d307e2257
commit
1d9c91d230
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
Loading…
Reference in New Issue