verify blocks before storing in block pool / database (#158)
* fix state db lookup typo * fix randao reveal slot when proposing blocks * only store blocks that can be applied to a state * store state at every epoch boundary (yes, needs pruning!) * split out state advancement function when there's no block * default state sim to 0.9 attestation ratio
This commit is contained in:
parent
72749f4d04
commit
6bcefc0e42
|
@ -112,7 +112,7 @@ proc containsBlock*(
|
|||
|
||||
proc containsState*(
|
||||
db: BeaconChainDB, key: Eth2Digest): bool =
|
||||
db.backend.contains(subkey(BeaconBlock, key))
|
||||
db.backend.contains(subkey(BeaconState, key))
|
||||
|
||||
iterator getAncestors*(db: BeaconChainDB, root: Eth2Digest):
|
||||
tuple[root: Eth2Digest, blck: BeaconBlock] =
|
||||
|
|
|
@ -330,13 +330,13 @@ proc proposeBlock(node: BeaconNode,
|
|||
var newBlock = BeaconBlock(
|
||||
slot: slot,
|
||||
parent_root: node.state.blck.root,
|
||||
randao_reveal: validator.genRandaoReveal(state, state.slot),
|
||||
randao_reveal: validator.genRandaoReveal(state, slot),
|
||||
eth1_data: node.mainchainMonitor.getBeaconBlockRef(),
|
||||
signature: ValidatorSig(), # we need the rest of the block first!
|
||||
body: blockBody)
|
||||
|
||||
let ok =
|
||||
updateState(state, node.state.blck.root, some(newBlock), {skipValidation})
|
||||
updateState(state, node.state.blck.root, newBlock, {skipValidation})
|
||||
doAssert ok # TODO: err, could this fail somehow?
|
||||
|
||||
newBlock.state_root = Eth2Digest(data: hash_tree_root(state))
|
||||
|
@ -428,15 +428,12 @@ proc scheduleEpochActions(node: BeaconNode, epoch: Epoch) =
|
|||
epoch = humaneEpochNum(epoch),
|
||||
stateEpoch = humaneEpochNum(node.state.data.slot.slot_to_epoch())
|
||||
|
||||
# Sanity check - verify that the current head block is not too far behind
|
||||
if node.state.data.slot.slot_to_epoch() + 1 < epoch:
|
||||
# Normally, we update the head state lazily, just before making an
|
||||
# attestation. However, if we skip scheduling attestations, we'll never
|
||||
# run the head update - thus we make an attempt now:
|
||||
# In case some late blocks dropped in
|
||||
node.updateHead()
|
||||
|
||||
# Sanity check - verify that the current head block is not too far behind
|
||||
if node.state.data.slot.slot_to_epoch() + 1 < epoch:
|
||||
# We're still behind!
|
||||
# We're hopelessly behind!
|
||||
#
|
||||
# There's a few ways this can happen:
|
||||
#
|
||||
|
@ -580,7 +577,14 @@ proc onBeaconBlock(node: BeaconNode, blck: BeaconBlock) =
|
|||
voluntary_exits = blck.body.voluntary_exits.len,
|
||||
transfers = blck.body.transfers.len
|
||||
|
||||
if not node.blockPool.add(blockRoot, blck):
|
||||
var
|
||||
# TODO We could avoid this copy by having node.state as a general cache
|
||||
# that just holds a random recent state - that would however require
|
||||
# rethinking scheduling etc, which relies on there being a fairly
|
||||
# accurate representation of the state available. Notably, when there's
|
||||
# a reorg, the scheduling might change!
|
||||
stateTmp = node.state
|
||||
if not node.blockPool.add(stateTmp, blockRoot, blck):
|
||||
# TODO the fact that add returns a bool that causes the parent block to be
|
||||
# pre-emptively fetched is quite ugly - fix.
|
||||
node.fetchBlocks(@[blck.parent_root])
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import
|
||||
bitops, chronicles, options, sequtils, sets, tables,
|
||||
ssz, beacon_chain_db, state_transition, extras,
|
||||
spec/[crypto, datatypes, digest]
|
||||
spec/[crypto, datatypes, digest, helpers]
|
||||
|
||||
type
|
||||
BlockPool* = ref object
|
||||
|
@ -140,10 +140,18 @@ proc init*(T: type BlockPool, db: BeaconChainDB): BlockPool =
|
|||
db: db
|
||||
)
|
||||
|
||||
proc add*(pool: var BlockPool, blockRoot: Eth2Digest, blck: BeaconBlock): bool =
|
||||
proc updateState*(
|
||||
pool: BlockPool, state: var StateData, blck: BlockRef) {.gcsafe.}
|
||||
|
||||
proc add*(
|
||||
pool: var BlockPool, state: var StateData, blockRoot: Eth2Digest,
|
||||
blck: BeaconBlock): bool {.gcsafe.} =
|
||||
## return false indicates that the block parent was missing and should be
|
||||
## fetched
|
||||
## TODO reevaluate this API - it's pretty ugly with the bool return
|
||||
## the state parameter may be updated to include the given block, if
|
||||
## everything checks out
|
||||
# TODO reevaluate passing the state in like this
|
||||
# TODO reevaluate this API - it's pretty ugly with the bool return
|
||||
doAssert blockRoot == hash_tree_root_final(blck)
|
||||
|
||||
# Already seen this block??
|
||||
|
@ -169,23 +177,40 @@ proc add*(pool: var BlockPool, blockRoot: Eth2Digest, blck: BeaconBlock): bool =
|
|||
|
||||
return true
|
||||
|
||||
# TODO we should now validate the block to ensure that it's sane - but the
|
||||
# only way to do that is to apply it to the state... for now, we assume
|
||||
# all blocks are good!
|
||||
let parent = pool.blocks.getOrDefault(blck.parent_root)
|
||||
|
||||
if parent != nil:
|
||||
# The block is resolved, nothing more to do!
|
||||
# The block might have been in either of these - we don't want any more
|
||||
# work done on its behalf
|
||||
pool.unresolved.del(blockRoot)
|
||||
pool.pending.del(blockRoot)
|
||||
|
||||
# The block is resolved, now it's time to validate it to ensure that the
|
||||
# blocks we add to the database are clean for the given state
|
||||
updateState(pool, state, parent)
|
||||
skipSlots(state.data, parent.root, blck.slot - 1)
|
||||
|
||||
if not updateState(state.data, parent.root, blck, {}):
|
||||
# TODO find a better way to log all this block data
|
||||
notice "Invalid block",
|
||||
blockRoot = shortLog(blockRoot),
|
||||
slot = humaneSlotNum(blck.slot),
|
||||
stateRoot = shortLog(blck.state_root),
|
||||
parentRoot = shortLog(blck.parent_root),
|
||||
signature = shortLog(blck.signature),
|
||||
proposer_slashings = blck.body.proposer_slashings.len,
|
||||
attester_slashings = blck.body.attester_slashings.len,
|
||||
attestations = blck.body.attestations.len,
|
||||
deposits = blck.body.deposits.len,
|
||||
voluntary_exits = blck.body.voluntary_exits.len,
|
||||
transfers = blck.body.transfers.len
|
||||
|
||||
let blockRef = BlockRef(
|
||||
root: blockRoot
|
||||
)
|
||||
link(parent, blockRef)
|
||||
|
||||
pool.blocks[blockRoot] = blockRef
|
||||
# The block might have been in either of these - we don't want any more
|
||||
# work done on its behalf
|
||||
pool.unresolved.del(blockRoot)
|
||||
pool.pending.del(blockRoot)
|
||||
|
||||
# Resolved blocks should be stored in database
|
||||
pool.db.putBlock(blockRoot, blck)
|
||||
|
@ -209,7 +234,7 @@ proc add*(pool: var BlockPool, blockRoot: Eth2Digest, blck: BeaconBlock): bool =
|
|||
# running out of stack etc
|
||||
let retries = pool.pending
|
||||
for k, v in retries:
|
||||
discard pool.add(k, v)
|
||||
discard pool.add(state, k, v)
|
||||
|
||||
return true
|
||||
|
||||
|
@ -271,6 +296,8 @@ proc checkUnresolved*(pool: var BlockPool): seq[Eth2Digest] =
|
|||
inc v.tries
|
||||
|
||||
for k in done:
|
||||
# TODO Need to potentially remove from pool.pending - this is currently a
|
||||
# memory leak here!
|
||||
pool.unresolved.del(k)
|
||||
|
||||
# simple (simplistic?) exponential backoff for retries..
|
||||
|
@ -279,24 +306,43 @@ proc checkUnresolved*(pool: var BlockPool): seq[Eth2Digest] =
|
|||
result.add(k)
|
||||
|
||||
proc skipAndUpdateState(
|
||||
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool =
|
||||
skipSlots(state, blck.parent_root, blck.slot - 1)
|
||||
updateState(state, blck.parent_root, some(blck), flags)
|
||||
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags,
|
||||
afterUpdate: proc (state: BeaconState)): bool =
|
||||
skipSlots(state, blck.parent_root, blck.slot - 1, afterUpdate)
|
||||
let ok = updateState(state, blck.parent_root, blck, flags)
|
||||
|
||||
afterUpdate(state)
|
||||
|
||||
ok
|
||||
|
||||
proc maybePutState(pool: BlockPool, state: BeaconState) =
|
||||
# TODO we save state at every epoch start but never remove them - we also
|
||||
# potentially save multiple states per slot if reorgs happen, meaning
|
||||
# we could easily see a state explosion
|
||||
if state.slot mod SLOTS_PER_EPOCH == 0:
|
||||
info "Storing state",
|
||||
stateSlot = humaneSlotNum(state.slot),
|
||||
stateRoot = hash_tree_root_final(state) # TODO cache?
|
||||
pool.db.putState(state)
|
||||
|
||||
proc updateState*(
|
||||
pool: BlockPool, state: var StateData, blck: BlockRef) =
|
||||
if state.blck.root == blck.root:
|
||||
return # State already at the right spot
|
||||
|
||||
# TODO this blockref should never be created, since we trace every blockref
|
||||
# back to the tail block
|
||||
doAssert (not blck.parent.isNil), "trying to apply genesis block!"
|
||||
|
||||
# Rewind or advance state such that it matches the given block - this may
|
||||
# include replaying from an earlier snapshot if blck is on a different branch
|
||||
# or has advanced to a higher slot number than blck
|
||||
var ancestors = @[pool.get(blck)]
|
||||
|
||||
# We need to check the slot because the state might have moved forwards
|
||||
# without blocks
|
||||
if state.blck.root == blck.root and state.data.slot == ancestors[0].data.slot:
|
||||
return # State already at the right spot
|
||||
|
||||
# Common case: blck points to a block that is one step ahead of state
|
||||
if state.blck.root == blck.parent.root:
|
||||
let ok = skipAndUpdateState(state.data, ancestors[0].data, {skipValidation})
|
||||
if state.blck.root == ancestors[0].data.parent_root and
|
||||
state.data.slot + 1 == ancestors[0].data.slot:
|
||||
let ok = skipAndUpdateState(
|
||||
state.data, ancestors[0].data, {skipValidation}) do (state: BeaconState):
|
||||
pool.maybePutState(state)
|
||||
doAssert ok, "Blocks in database should never fail to apply.."
|
||||
state.blck = blck
|
||||
state.root = ancestors[0].data.state_root
|
||||
|
@ -329,6 +375,7 @@ proc updateState*(
|
|||
|
||||
notice "Replaying state transitions",
|
||||
stateSlot = humaneSlotNum(state.data.slot),
|
||||
stateRoot = shortLog(ancestor.data.state_root),
|
||||
prevStateSlot = humaneSlotNum(ancestorState.get().slot),
|
||||
ancestors = ancestors.len
|
||||
|
||||
|
@ -345,18 +392,21 @@ proc updateState*(
|
|||
for i in countdown(ancestors.len - 2, 0):
|
||||
let last = ancestors[i]
|
||||
|
||||
skipSlots(state.data, last.data.parent_root, last.data.slot - 1)
|
||||
skipSlots(
|
||||
state.data, last.data.parent_root,
|
||||
last.data.slot - 1) do(state: BeaconState):
|
||||
pool.maybePutState(state)
|
||||
|
||||
# TODO technically, we should be adding states to the database here because
|
||||
# we're going down a different fork..
|
||||
let ok = updateState(
|
||||
state.data, last.data.parent_root, some(last.data), {skipValidation})
|
||||
|
||||
doAssert(ok)
|
||||
state.data, last.data.parent_root, last.data, {skipValidation})
|
||||
doAssert ok,
|
||||
"We only keep validated blocks in the database, should never fail"
|
||||
|
||||
state.blck = blck
|
||||
state.root = ancestors[0].data.state_root
|
||||
|
||||
pool.maybePutState(state.data)
|
||||
|
||||
proc loadTailState*(pool: BlockPool): StateData =
|
||||
## Load the state associated with the current tail in the pool
|
||||
StateData(
|
||||
|
|
|
@ -1006,14 +1006,16 @@ proc verifyStateRoot(state: BeaconState, blck: BeaconBlock): bool =
|
|||
else:
|
||||
true
|
||||
|
||||
proc updateState*(state: var BeaconState, previous_block_root: Eth2Digest,
|
||||
new_block: Option[BeaconBlock], flags: UpdateFlags): bool =
|
||||
proc updateState*(
|
||||
state: var BeaconState, previous_block_root: Eth2Digest,
|
||||
new_block: BeaconBlock, flags: UpdateFlags): bool =
|
||||
## Time in the beacon chain moves by slots. Every time (haha.) that happens,
|
||||
## we will update the beacon state. Normally, the state updates will be driven
|
||||
## by the contents of a new block, but it may happen that the block goes
|
||||
## missing - the state updates happen regardless.
|
||||
##
|
||||
## Each call to this function will advance the state by one slot - new_block,
|
||||
## if present, must match that slot.
|
||||
## must match that slot. If the update fails, the state will remain unchanged.
|
||||
##
|
||||
## The flags are used to specify that certain validations should be skipped
|
||||
## for the new block. This is done during block proposal, to create a state
|
||||
|
@ -1025,59 +1027,67 @@ proc updateState*(state: var BeaconState, previous_block_root: Eth2Digest,
|
|||
# One reason to keep it this way is that you need to look ahead if you're
|
||||
# the block proposer, though in reality we only need a partial update for
|
||||
# that
|
||||
# TODO There's a discussion about what this function should do, and when:
|
||||
# https://github.com/ethereum/eth2.0-specs/issues/284
|
||||
|
||||
# TODO check to which extent this copy can be avoided (considering forks etc),
|
||||
# for now, it serves as a reminder that we need to handle invalid blocks
|
||||
# somewhere..
|
||||
# TODO many functions will mutate `state` partially without rolling back
|
||||
# many functions will mutate `state` partially without rolling back
|
||||
# the changes in case of failure (look out for `var BeaconState` and
|
||||
# bool return values...)
|
||||
# TODO There's a discussion about what this function should do, and when:
|
||||
# https://github.com/ethereum/eth2.0-specs/issues/284
|
||||
var old_state = state
|
||||
|
||||
# Per-slot updates - these happen regardless if there is a block or not
|
||||
processSlot(state, previous_block_root)
|
||||
|
||||
if new_block.isSome():
|
||||
# Block updates - these happen when there's a new block being suggested
|
||||
# by the block proposer. Every actor in the network will update its state
|
||||
# according to the contents of this block - but first they will validate
|
||||
# that the block is sane.
|
||||
# TODO what should happen if block processing fails?
|
||||
# https://github.com/ethereum/eth2.0-specs/issues/293
|
||||
if processBlock(state, new_block.get(), flags):
|
||||
if processBlock(state, new_block, flags):
|
||||
# Block ok so far, proceed with state update
|
||||
processEpoch(state)
|
||||
|
||||
# This is a bit awkward - at the end of processing we verify that the
|
||||
# state we arrive at is what the block producer thought it would be -
|
||||
# meaning that potentially, it could fail verification
|
||||
if skipValidation in flags or verifyStateRoot(state, new_block.get()):
|
||||
if skipValidation in flags or verifyStateRoot(state, new_block):
|
||||
# State root is what it should be - we're done!
|
||||
return true
|
||||
|
||||
# Block processing failed, have to start over
|
||||
# Block processing failed, roll back changes
|
||||
state = old_state
|
||||
processSlot(state, previous_block_root)
|
||||
processEpoch(state)
|
||||
false
|
||||
else:
|
||||
# Skip all per-block processing. Move directly to epoch processing
|
||||
# prison. Do not do any block updates when passing go.
|
||||
|
||||
proc advanceState*(
|
||||
state: var BeaconState, previous_block_root: Eth2Digest) =
|
||||
## Sometimes we need to update the state even though we don't have a block at
|
||||
## hand - this happens for example when a block proposer fails to produce a
|
||||
## a block.
|
||||
# TODO In the current spec, this can fail only when the state is inconsistent
|
||||
# or buggy - how do we handle that? crash?
|
||||
|
||||
# Per-slot updates - these happen regardless if there is a block or not
|
||||
processSlot(state, previous_block_root)
|
||||
|
||||
# Heavy updates that happen for every epoch - these never fail (or so we hope)
|
||||
processEpoch(state)
|
||||
true
|
||||
|
||||
proc skipSlots*(state: var BeaconState, parentRoot: Eth2Digest, slot: Slot) =
|
||||
proc skipSlots*(state: var BeaconState, parentRoot: Eth2Digest, slot: Slot,
|
||||
afterSlot: proc (state: BeaconState) = nil) =
|
||||
if state.slot < slot:
|
||||
info "Advancing state past slot gap",
|
||||
debug "Advancing state past slot gap",
|
||||
targetSlot = humaneSlotNum(slot),
|
||||
stateSlot = humaneSlotNum(state.slot)
|
||||
|
||||
while state.slot < slot:
|
||||
let ok = updateState(state, parentRoot, none[BeaconBlock](), {})
|
||||
doAssert ok, "Empty block state update should never fail!"
|
||||
advanceState(state, parentRoot)
|
||||
|
||||
if not afterSlot.isNil:
|
||||
afterSlot(state)
|
||||
|
||||
# TODO document this:
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ cli do(slots = 1945,
|
|||
validators = SLOTS_PER_EPOCH, # One per shard is minimum
|
||||
json_interval = SLOTS_PER_EPOCH,
|
||||
prefix = 0,
|
||||
attesterRatio {.desc: "ratio of validators that attest in each round"} = 0.0,
|
||||
attesterRatio {.desc: "ratio of validators that attest in each round"} = 0.9,
|
||||
validate = false):
|
||||
let
|
||||
flags = if validate: {} else: {skipValidation}
|
||||
|
|
|
@ -29,8 +29,7 @@ suite "Attestation pool processing":
|
|||
pool = AttestationPool.init(blockPool)
|
||||
state = blockPool.loadTailState()
|
||||
# Slot 0 is a finalized slot - won't be making attestations for it..
|
||||
discard updateState(
|
||||
state.data, state.blck.root, none(BeaconBlock), {skipValidation})
|
||||
advanceState(state.data, state.blck.root)
|
||||
|
||||
let
|
||||
# Create an attestation for slot 1 signed by the only attester we have!
|
||||
|
@ -47,14 +46,12 @@ suite "Attestation pool processing":
|
|||
check:
|
||||
attestations.len == 1
|
||||
|
||||
|
||||
test "Attestations may arrive in any order":
|
||||
var
|
||||
pool = AttestationPool.init(blockPool)
|
||||
state = blockPool.loadTailState()
|
||||
# Slot 0 is a finalized slot - won't be making attestations for it..
|
||||
discard updateState(
|
||||
state.data, state.blck.root, none(BeaconBlock), {skipValidation})
|
||||
advanceState(state.data, state.blck.root)
|
||||
|
||||
let
|
||||
# Create an attestation for slot 1 signed by the only attester we have!
|
||||
|
@ -63,8 +60,7 @@ suite "Attestation pool processing":
|
|||
attestation1 = makeAttestation(
|
||||
state.data, state.blck.root, crosslink_committees1[0].committee[0])
|
||||
|
||||
discard updateState(
|
||||
state.data, state.blck.root, none(BeaconBlock), {skipValidation})
|
||||
advanceState(state.data, state.blck.root)
|
||||
|
||||
let
|
||||
crosslink_committees2 =
|
||||
|
|
|
@ -10,18 +10,50 @@ import options, unittest, sequtils, strutils, eth/trie/[db],
|
|||
../beacon_chain/spec/[datatypes, digest, crypto]
|
||||
|
||||
suite "Beacon chain DB":
|
||||
|
||||
test "empty database":
|
||||
var
|
||||
db = init(BeaconChainDB, newMemoryDB())
|
||||
|
||||
test "empty database":
|
||||
check:
|
||||
db.getState(Eth2Digest()).isNone
|
||||
db.getBlock(Eth2Digest()).isNone
|
||||
|
||||
test "find ancestors":
|
||||
var x: ValidatorSig
|
||||
var y = init(ValidatorSig, x.getBytes())
|
||||
test "sanity check blocks":
|
||||
var
|
||||
db = init(BeaconChainDB, newMemoryDB())
|
||||
|
||||
let
|
||||
blck = BeaconBlock()
|
||||
root = hash_tree_root_final(blck)
|
||||
|
||||
db.putBlock(blck)
|
||||
|
||||
check:
|
||||
db.containsBlock(root)
|
||||
db.getBlock(root).get() == blck
|
||||
|
||||
test "sanity check states":
|
||||
var
|
||||
db = init(BeaconChainDB, newMemoryDB())
|
||||
|
||||
let
|
||||
state = BeaconState()
|
||||
root = hash_tree_root_final(state)
|
||||
|
||||
db.putState(state)
|
||||
|
||||
check:
|
||||
db.containsState(root)
|
||||
db.getState(root).get() == state
|
||||
|
||||
test "find ancestors":
|
||||
var
|
||||
db = init(BeaconChainDB, newMemoryDB())
|
||||
x: ValidatorSig
|
||||
y = init(ValidatorSig, x.getBytes())
|
||||
|
||||
# Silly serialization check that fails without the right import
|
||||
check: x == y
|
||||
|
||||
let
|
||||
|
|
|
@ -12,7 +12,7 @@ import
|
|||
../beacon_chain/[block_pool, beacon_chain_db, extras, state_transition, ssz]
|
||||
|
||||
suite "Block pool processing":
|
||||
var
|
||||
let
|
||||
genState = get_genesis_beacon_state(
|
||||
makeInitialDeposits(flags = {skipValidation}), 0, Eth1Data(),
|
||||
{skipValidation})
|
||||
|
@ -38,7 +38,7 @@ suite "Block pool processing":
|
|||
b1Root = hash_tree_root_final(b1)
|
||||
|
||||
# TODO the return value is ugly here, need to fix and test..
|
||||
discard pool.add(b1Root, b1)
|
||||
discard pool.add(state, b1Root, b1)
|
||||
|
||||
let b1Ref = pool.get(b1Root)
|
||||
|
||||
|
@ -53,19 +53,18 @@ suite "Block pool processing":
|
|||
state = pool.loadTailState()
|
||||
|
||||
let
|
||||
b1 = addBlock(
|
||||
state.data, state.blck.root, BeaconBlockBody(), {skipValidation})
|
||||
b1 = addBlock(state.data, state.blck.root, BeaconBlockBody(), {})
|
||||
b1Root = hash_tree_root_final(b1)
|
||||
b2 = addBlock(state.data, b1Root, BeaconBlockBody(), {skipValidation})
|
||||
b2 = addBlock(state.data, b1Root, BeaconBlockBody(), {})
|
||||
b2Root = hash_tree_root_final(b2)
|
||||
|
||||
discard pool.add(b2Root, b2)
|
||||
discard pool.add(state, b2Root, b2)
|
||||
|
||||
check:
|
||||
pool.get(b2Root).isNone() # Unresolved, shouldn't show up
|
||||
b1Root in pool.checkUnresolved()
|
||||
|
||||
discard pool.add(b1Root, b1)
|
||||
discard pool.add(state, b1Root, b1)
|
||||
|
||||
let
|
||||
b1r = pool.get(b1Root)
|
||||
|
|
|
@ -25,24 +25,19 @@ suite "Block processing":
|
|||
test "Passes from genesis state, no block":
|
||||
var
|
||||
state = genesisState
|
||||
proposer_index = getNextBeaconProposerIndex(state)
|
||||
previous_block_root = hash_tree_root_final(genesisBlock)
|
||||
let block_ok =
|
||||
updateState(state, previous_block_root, none(BeaconBlock), {})
|
||||
check:
|
||||
block_ok
|
||||
|
||||
advanceState(state, previous_block_root)
|
||||
check:
|
||||
state.slot == genesisState.slot + 1
|
||||
|
||||
test "Passes from genesis state, empty block":
|
||||
var
|
||||
state = genesisState
|
||||
proposer_index = getNextBeaconProposerIndex(state)
|
||||
previous_block_root = hash_tree_root_final(genesisBlock)
|
||||
new_block = makeBlock(state, previous_block_root, BeaconBlockBody())
|
||||
|
||||
let block_ok = updateState(
|
||||
state, previous_block_root, some(new_block), {})
|
||||
let block_ok = updateState(state, previous_block_root, new_block, {})
|
||||
|
||||
check:
|
||||
block_ok
|
||||
|
@ -55,10 +50,7 @@ suite "Block processing":
|
|||
previous_block_root = hash_tree_root_final(genesisBlock)
|
||||
|
||||
for i in 1..SLOTS_PER_EPOCH.int:
|
||||
let block_ok = updateState(
|
||||
state, previous_block_root, none(BeaconBlock), {})
|
||||
check:
|
||||
block_ok
|
||||
advanceState(state, previous_block_root)
|
||||
|
||||
check:
|
||||
state.slot == genesisState.slot + SLOTS_PER_EPOCH
|
||||
|
@ -72,7 +64,7 @@ suite "Block processing":
|
|||
var new_block = makeBlock(state, previous_block_root, BeaconBlockBody())
|
||||
|
||||
let block_ok = updateState(
|
||||
state, previous_block_root, some(new_block), {})
|
||||
state, previous_block_root, new_block, {})
|
||||
|
||||
check:
|
||||
block_ok
|
||||
|
@ -88,8 +80,7 @@ suite "Block processing":
|
|||
previous_block_root = hash_tree_root_final(genesisBlock)
|
||||
|
||||
# Slot 0 is a finalized slot - won't be making attestations for it..
|
||||
discard updateState(
|
||||
state, previous_block_root, none(BeaconBlock), {})
|
||||
advanceState(state, previous_block_root)
|
||||
|
||||
let
|
||||
# Create an attestation for slot 1 signed by the only attester we have!
|
||||
|
@ -101,21 +92,19 @@ suite "Block processing":
|
|||
# Some time needs to pass before attestations are included - this is
|
||||
# to let the attestation propagate properly to interested participants
|
||||
while state.slot < GENESIS_SLOT + MIN_ATTESTATION_INCLUSION_DELAY + 1:
|
||||
discard updateState(
|
||||
state, previous_block_root, none(BeaconBlock), {})
|
||||
advanceState(state, previous_block_root)
|
||||
|
||||
let
|
||||
new_block = makeBlock(state, previous_block_root, BeaconBlockBody(
|
||||
attestations: @[attestation]
|
||||
))
|
||||
discard updateState(state, previous_block_root, some(new_block), {})
|
||||
discard updateState(state, previous_block_root, new_block, {})
|
||||
|
||||
check:
|
||||
state.latest_attestations.len == 1
|
||||
|
||||
while state.slot < 191:
|
||||
discard updateState(
|
||||
state, previous_block_root, none(BeaconBlock), {})
|
||||
advanceState(state, previous_block_root)
|
||||
|
||||
# Would need to process more epochs for the attestation to be removed from
|
||||
# the state! (per above bug)
|
||||
|
|
|
@ -107,7 +107,7 @@ proc addBlock*(
|
|||
)
|
||||
|
||||
let block_ok = updateState(
|
||||
state, previous_block_root, some(new_block), {skipValidation})
|
||||
state, previous_block_root, new_block, {skipValidation})
|
||||
assert block_ok
|
||||
|
||||
# Ok, we have the new state as it would look with the block applied - now we
|
||||
|
@ -174,7 +174,7 @@ proc makeAttestation*(
|
|||
shard: sac.shard,
|
||||
beacon_block_root: beacon_block_root,
|
||||
epoch_boundary_root: Eth2Digest(), # TODO
|
||||
latest_crosslink: Crosslink(epoch: state.latest_crosslinks[sac.shard].epoch), # TODO
|
||||
latest_crosslink: state.latest_crosslinks[sac.shard],
|
||||
shard_block_root: Eth2Digest(), # TODO
|
||||
justified_epoch: state.justified_epoch,
|
||||
justified_block_root: get_block_root(state, get_epoch_start_slot(state.justified_epoch)),
|
||||
|
|
Loading…
Reference in New Issue