fix state rewind

* rewind fast path comparison was not taking skipped slots into account
properly
* less messy blockref creation
This commit is contained in:
Jacek Sieka 2020-02-05 12:41:46 +01:00 committed by tersec
parent f20127f4c6
commit fb9c4fabf4
2 changed files with 61 additions and 13 deletions

View File

@ -249,9 +249,10 @@ proc init*(T: type BlockPool, db: BeaconChainDB): BlockPool =
res res
proc addResolvedBlock( proc addResolvedBlock(
pool: var BlockPool, state: var StateData, blockRoot: Eth2Digest, pool: var BlockPool, state: BeaconState, blockRoot: Eth2Digest,
signedBlock: SignedBeaconBlock, parent: BlockRef): BlockRef = signedBlock: SignedBeaconBlock, parent: BlockRef): BlockRef =
logScope: pcs = "block_resolution" logScope: pcs = "block_resolution"
doAssert state.slot == signedBlock.message.slot, "state must match block"
let blockRef = BlockRef.init(blockRoot, signedBlock.message) let blockRef = BlockRef.init(blockRoot, signedBlock.message)
link(parent, blockRef) link(parent, blockRef)
@ -262,17 +263,10 @@ proc addResolvedBlock(
# Resolved blocks should be stored in database # Resolved blocks should be stored in database
pool.db.putBlock(blockRoot, signedBlock) pool.db.putBlock(blockRoot, signedBlock)
# TODO this is a bit ugly - we update state.data outside of this function then
# set the rest here - need a blockRef to update it. Clean this up -
# hopefully it won't be necessary by the time hash caching and the rest
# is done..
doAssert state.data.data.slot == blockRef.slot
state.blck = blockRef
# This block *might* have caused a justification - make sure we stow away # This block *might* have caused a justification - make sure we stow away
# that information: # that information:
let justifiedSlot = let justifiedSlot =
state.data.data.current_justified_checkpoint.epoch.compute_start_slot_at_epoch() state.current_justified_checkpoint.epoch.compute_start_slot_at_epoch()
var foundHead: Option[Head] var foundHead: Option[Head]
for head in pool.heads.mitems(): for head in pool.heads.mitems():
@ -389,9 +383,12 @@ proc add*(
return return
# Careful, pool.tmpState is now partially inconsistent and will be updated # Careful, tmpState.data has been updated but not blck - we need to create
# inside addResolvedBlock # the BlockRef first!
return pool.addResolvedBlock(pool.tmpState, blockRoot, signedBlock, parent) pool.tmpState.blck = pool.addResolvedBlock(
pool.tmpState.data.data, blockRoot, signedBlock, parent)
return pool.tmpState.blck
# TODO already checked hash though? main reason to keep this is because # TODO already checked hash though? main reason to keep this is because
# the pending pool calls this function back later in a loop, so as long # the pending pool calls this function back later in a loop, so as long
@ -603,7 +600,7 @@ proc rewindState(pool: BlockPool, state: var StateData, bs: BlockSlot):
var ancestors = @[pool.get(bs.blck)] var ancestors = @[pool.get(bs.blck)]
# Common case: the last block applied is the parent of the block to apply: # Common case: the last block applied is the parent of the block to apply:
if not bs.blck.parent.isNil and state.blck.root == bs.blck.parent.root and if not bs.blck.parent.isNil and state.blck.root == bs.blck.parent.root and
state.data.data.slot < bs.slot: state.data.data.slot < bs.blck.slot:
return ancestors return ancestors
# It appears that the parent root of the proposed new block is different from # It appears that the parent root of the proposed new block is different from
@ -693,6 +690,7 @@ proc updateStateData*(pool: BlockPool, state: var StateData, bs: BlockSlot) =
if state.data.data.slot != bs.slot: if state.data.data.slot != bs.slot:
# Might be that we're moving to the same block but later slot # Might be that we're moving to the same block but later slot
process_slots(state.data, bs.slot) process_slots(state.data, bs.slot)
# TODO we will not save if multiple slots are skipped here
pool.maybePutState(state.data, bs.blck) pool.maybePutState(state.data, bs.blck)
return # State already at the right spot return # State already at the right spot
@ -715,6 +713,7 @@ proc updateStateData*(pool: BlockPool, state: var StateData, bs: BlockSlot) =
doAssert ok, "Blocks in database should never fail to apply.." doAssert ok, "Blocks in database should never fail to apply.."
# TODO check if this triggers rest of state transition, or should # TODO check if this triggers rest of state transition, or should
# TODO we will not save if multiple slots are skipped here
process_slots(state.data, bs.slot) process_slots(state.data, bs.slot)
pool.maybePutState(state.data, bs.blck) pool.maybePutState(state.data, bs.blck)

View File

@ -187,6 +187,55 @@ when const_preset == "minimal": # Too much stack space used on mainnet
pool.head.blck == b1Add pool.head.blck == b1Add
pool.headState.data.data.slot == b1Add.slot pool.headState.data.data.slot == b1Add.slot
timedTest "updateStateData sanity" & preset():
let
b1Add = pool.add(b1Root, b1)
b2Add = pool.add(b2Root, b2)
bs1 = BlockSlot(blck: b1Add, slot: b1.message.slot)
bs1_3 = b1Add.atSlot(3.Slot)
bs2 = BlockSlot(blck: b2Add, slot: b2.message.slot)
bs2_3 = b2Add.atSlot(3.Slot)
var tmpState = pool.headState
# move to specific block
pool.updateStateData(tmpState, bs1)
check:
tmpState.blck == b1Add
tmpState.data.data.slot == bs1.slot
# Skip slots
pool.updateStateData(tmpState, bs1_3) # skip slots
check:
tmpState.blck == b1Add
tmpState.data.data.slot == bs1_3.slot
# Move back slots, but not blocks
pool.updateStateData(tmpState, bs1_3.parent())
check:
tmpState.blck == b1Add
tmpState.data.data.slot == bs1_3.parent().slot
# Move to different block and slot
pool.updateStateData(tmpState, bs2_3)
check:
tmpState.blck == b2Add
tmpState.data.data.slot == bs2_3.slot
# Move back slot and block
pool.updateStateData(tmpState, bs1)
check:
tmpState.blck == b1Add
tmpState.data.data.slot == bs1.slot
# Move back to genesis
pool.updateStateData(tmpState, bs1.parent())
check:
tmpState.blck == b1Add.parent
tmpState.data.data.slot == bs1.parent.slot
suite "BlockPool finalization tests" & preset(): suite "BlockPool finalization tests" & preset():
setup: setup:
var var