* comment on last finalized state
* use explicitly named proc's instead of type tricks
This commit is contained in:
Jacek Sieka 2019-02-21 15:38:26 -06:00
parent 1d9c91d230
commit 4c2d212781
No known key found for this signature in database
GPG Key ID: 6299FEB3EB6FA465
4 changed files with 53 additions and 38 deletions

View File

@ -13,8 +13,6 @@ type
kHashToBlock
kHeadBlock
DbTypes = BeaconState | BeaconBlock
func subkey(kind: DbKeyKind): array[1, byte] =
result[0] = byte ord(kind)
@ -33,17 +31,32 @@ proc init*(T: type BeaconChainDB, backend: TrieDatabaseRef): BeaconChainDB =
new result
result.backend = backend
proc put*(db: BeaconChainDB, key: Eth2Digest, value: BeaconBlock) =
proc putBlock*(db: BeaconChainDB, key: Eth2Digest, value: BeaconBlock) =
db.backend.put(subkey(type value, key), ssz.serialize(value))
proc putHead*(db: BeaconChainDB, key: Eth2Digest) =
db.backend.put(subkey(kHeadBlock), key.data) # TODO head block?
proc put*(db: BeaconChainDB, key: Eth2Digest, value: BeaconState) =
proc putState*(db: BeaconChainDB, key: Eth2Digest, value: BeaconState) =
# TODO: prune old states
# TODO: it might be necessary to introduce the concept of a "last finalized
# state" to the storage, so that clients with limited storage have
# a natural state to start recovering from. One idea is to keep a
# special pointer to the state that has ben finalized, and prune all
# other states.
# One issue is that what will become a finalized is revealed only
# long after that state has passed, meaning that we need to keep
# a history of "finalized state candidates" or possibly replay from
# the previous finalized state, if we have that stored. To consider
# here is that the gap between finalized and present state might be
# significant (days), meaning replay might be expensive.
db.backend.put(subkey(type value, key), ssz.serialize(value))
proc put*(db: BeaconChainDB, value: DbTypes) =
db.put(hash_tree_root_final(value), value)
proc putBlock*(db: BeaconChainDB, value: BeaconBlock) =
db.putBlock(hash_tree_root_final(value), value)
proc putState*(db: BeaconChainDB, value: BeaconState) =
db.putState(hash_tree_root_final(value), value)
proc get(db: BeaconChainDB, key: auto, T: typedesc): Option[T] =
let res = db.backend.get(key)
@ -52,26 +65,29 @@ proc get(db: BeaconChainDB, key: auto, T: typedesc): Option[T] =
else:
none(T)
# TODO: T: type DbTypes fails with compiler error.. investigate
proc get*(db: BeaconChainDB, key: Eth2Digest, T: type BeaconBlock): Option[T] =
db.get(subkey(T, key), T)
proc getBlock*(db: BeaconChainDB, key: Eth2Digest): Option[BeaconBlock] =
db.get(subkey(BeaconBlock, key), BeaconBlock)
proc get*(db: BeaconChainDB, key: Eth2Digest, T: type BeaconState): Option[T] =
db.get(subkey(T, key), T)
proc getState*(db: BeaconChainDB, key: Eth2Digest): Option[BeaconState] =
db.get(subkey(BeaconState, key), BeaconState)
proc getHead*(db: BeaconChainDB, T: type BeaconBlock): Option[T] =
proc getHead*(db: BeaconChainDB): Option[BeaconBlock] =
let key = db.backend.get(subkey(kHeadBlock))
if key.len == sizeof(Eth2Digest):
var tmp: Eth2Digest
copyMem(addr tmp, unsafeAddr key[0], sizeof(tmp))
db.get(tmp, T)
db.getBlock(tmp)
else:
none(T)
none(BeaconBlock)
proc contains*(
db: BeaconChainDB, key: Eth2Digest, T: type DbTypes): bool =
db.backend.contains(subkey(T, key))
proc containsBlock*(
db: BeaconChainDB, key: Eth2Digest): bool =
db.backend.contains(subkey(BeaconBlock, key))
proc containsState*(
db: BeaconChainDB, key: Eth2Digest): bool =
db.backend.contains(subkey(BeaconBlock, key))
proc getAncestors*(
db: BeaconChainDB, blck: BeaconBlock,
@ -87,7 +103,7 @@ proc getAncestors*(
result = @[blck]
while result[^1].slot > 0.Slot:
let parent = db.get(result[^1].parent_root, BeaconBlock)
let parent = db.getBlock(result[^1].parent_root)
if parent.isNone(): break

View File

@ -59,7 +59,7 @@ proc init*(T: type BeaconNode, conf: BeaconNodeConf): T =
# TODO does it really make sense to load from DB if a state snapshot has been
# specified on command line? potentially, this should be the other way
# around...
if (let head = result.db.getHead(BeaconBlock) ; head.isSome()):
if (let head = result.db.getHead(); head.isSome()):
info "Loading head from database",
blockSlot = humaneSlotNum(head.get().slot)
updateHeadBlock(result, head.get())
@ -70,10 +70,10 @@ proc init*(T: type BeaconNode, conf: BeaconNodeConf): T =
info "Loaded state from snapshot",
stateSlot = humaneSlotNum(result.beaconState.slot)
result.db.put(result.beaconState)
result.db.putState(result.beaconState)
# The genesis block is special in that we have to store it at hash 0 - in
# the genesis state, this block has not been applied..
result.db.put(result.headBlock)
result.db.putBlock(result.headBlock)
result.keys = ensureNetworkKeys(string conf.dataDir)
@ -406,7 +406,7 @@ proc onAttestation(node: BeaconNode, attestation: Attestation) =
node.attestationPool.add(attestation, node.beaconState)
if not node.db.contains(attestation.data.beacon_block_root, BeaconBlock):
if not node.db.containsBlock(attestation.data.beacon_block_root):
notice "Attestation block root missing",
beaconBlockRoot = shortHash(attestation.data.beacon_block_root)
# TODO download...
@ -453,12 +453,12 @@ proc updateHeadBlock(node: BeaconNode, blck: BeaconBlock) =
# in the database.
let
ancestors = node.db.getAncestors(blck) do (bb: BeaconBlock) -> bool:
node.db.contains(bb.state_root, BeaconState)
node.db.containsState(bb.state_root)
ancestor = ancestors[^1]
# 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()):
if (let state = node.db.getState(ancestor.state_root); state.isSome()):
# Got it!
notice "Replaying state transitions",
stateSlot = humaneSlotNum(node.beaconState.slot),
@ -525,7 +525,7 @@ proc onBeaconBlock(node: BeaconNode, blck: BeaconBlock) =
blockRoot = hash_tree_root_final(blck)
stateSlot = node.beaconState.slot
if node.db.contains(blockRoot, BeaconBlock):
if node.db.containsBlock(blockRoot):
debug "Block already seen",
slot = humaneSlotNum(blck.slot),
stateRoot = shortHash(blck.state_root),
@ -546,7 +546,7 @@ proc onBeaconBlock(node: BeaconNode, blck: BeaconBlock) =
# The block has been validated and it's not in the database yet - first, let's
# store it there, just to be safe
node.db.put(blck)
node.db.putBlock(blck)
# Since this is a good block, we should add its attestations in case we missed
# any. If everything checks out, this should lead to the fork choice selecting
@ -578,7 +578,7 @@ proc onBeaconBlock(node: BeaconNode, blck: BeaconBlock) =
updateHeadBlock(node, blck)
if stateNeedsSaving(node.beaconState):
node.db.put(node.beaconState)
node.db.putState(node.beaconState)
proc run*(node: BeaconNode) =
node.network.subscribe(topicBeaconBlocks) do (blck: BeaconBlock):

View File

@ -46,7 +46,7 @@ import
proc get_ancestor(
store: BeaconChainDB, blck: Eth2Digest, slot: Slot): Eth2Digest =
## Find the ancestor with a specific slot number
let blk = store.get(blck, BeaconBlock).get()
let blk = store.getBlock(blck).get()
if blk.slot == slot:
blck
else:
@ -110,16 +110,16 @@ proc lmdGhost*(
while true: # TODO use a O(log N) implementation instead of O(N^2)
let children = blocksChildren[head]
if children.len == 0:
return store.get(head, BeaconBlock).get()
return store.getBlock(head).get()
# For now we assume that all children are direct descendant of the current head
let next_slot = store.get(head, BeaconBlock).get().slot + 1
let next_slot = store.getBlock(head).get().slot + 1
for child in children:
doAssert store.get(child, BeaconBlock).get().slot == next_slot
doAssert store.getBlock(child).get().slot == next_slot
childVotes.clear()
for target, votes in rawVoteCount.pairs:
if store.get(target, BeaconBlock).get().slot >= next_slot:
if store.getBlock(target).get().slot >= next_slot:
childVotes.inc(store.get_ancestor(target, next_slot), votes)
head = childVotes.largest().key

View File

@ -15,9 +15,8 @@ suite "Beacon chain DB":
test "empty database":
check:
db.get(Eth2Digest(), BeaconState).isNone
db.get(Eth2Digest(), BeaconBlock).isNone
db.getState(Eth2Digest()).isNone
db.getBlock(Eth2Digest()).isNone
test "find ancestors":
var x: ValidatorSig
@ -34,17 +33,17 @@ suite "Beacon chain DB":
doAssert db.getAncestors(a0) == [a0]
doAssert db.getAncestors(a2) == [a2]
db.put(a2)
db.putBlock(a2)
doAssert db.getAncestors(a0) == [a0]
doAssert db.getAncestors(a2) == [a2]
db.put(a1)
db.putBlock(a1)
doAssert db.getAncestors(a0) == [a0]
doAssert db.getAncestors(a2) == [a2, a1]
db.put(a0)
db.putBlock(a0)
doAssert db.getAncestors(a0) == [a0]
doAssert db.getAncestors(a2) == [a2, a1, a0]