begin removing plain BeaconState versions of state transition functions (#951)

* remove near-duplicate code paths: process_slot(), process_slots(), and state_transition() for BeaconState are now wrappers around the HashedBeaconState versions

* convert tests/test_state_transition.nim to use HashedBeaconState

* convert mocking infrastructure and spec_block/epoch_processing tests to use HashedBeaconBlock, and remove thus unused process_slot*(state: var BeaconState)
This commit is contained in:
tersec 2020-04-30 06:44:19 +00:00 committed by GitHub
parent c74ba5c0c6
commit 3a56ddc5c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 209 additions and 250 deletions

View File

@ -268,6 +268,15 @@ proc initialize_beacon_state_from_eth1*(
state
proc initialize_hashed_beacon_state_from_eth1*(
eth1_block_hash: Eth2Digest,
eth1_timestamp: uint64,
deposits: openArray[Deposit],
flags: UpdateFlags = {}): HashedBeaconState =
let genesisState = initialize_beacon_state_from_eth1(
eth1_block_hash, eth1_timestamp, deposits, flags)
HashedBeaconState(data: genesisState[], root: hash_tree_root(genesisState))
func is_valid_genesis_state*(state: BeaconState): bool =
if state.genesis_time < MIN_GENESIS_TIME:
return false

View File

@ -38,24 +38,6 @@ import
declareGauge beacon_current_validators, """Number of status="pending|active|exited|withdrawable" validators in current epoch""" # On epoch transition
declareGauge beacon_previous_validators, """Number of status="pending|active|exited|withdrawable" validators in previous epoch""" # On epoch transition
# Canonical state transition functions
# ---------------------------------------------------------------
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#beacon-chain-state-transition-function
func process_slot*(state: var BeaconState) {.nbench.}=
# Cache state root
let previous_state_root = hash_tree_root(state)
state.state_roots[state.slot mod SLOTS_PER_HISTORICAL_ROOT] =
previous_state_root
# Cache latest block header state root
if state.latest_block_header.state_root == ZERO_HASH:
state.latest_block_header.state_root = previous_state_root
# Cache block root
state.block_roots[state.slot mod SLOTS_PER_HISTORICAL_ROOT] =
hash_tree_root(state.latest_block_header)
func get_epoch_validator_count(state: BeaconState): int64 =
# https://github.com/ethereum/eth2.0-metrics/blob/master/metrics.md#additional-metrics
#
@ -78,32 +60,6 @@ func get_epoch_validator_count(state: BeaconState): int64 =
validator.withdrawable_epoch > get_current_epoch(state):
result += 1
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#beacon-chain-state-transition-function
proc process_slots*(state: var BeaconState, slot: Slot) {.nbench.}=
if not (state.slot <= slot):
warn("Trying to apply old block",
state_slot = state.slot,
slot = slot)
return
# Catch up to the target slot
while state.slot < slot:
process_slot(state)
let is_epoch_transition = (state.slot + 1) mod SLOTS_PER_EPOCH == 0
if is_epoch_transition:
# Note: Genesis epoch = 0, no need to test if before Genesis
try:
beacon_previous_validators.set(get_epoch_validator_count(state))
except Exception as e: # TODO https://github.com/status-im/nim-metrics/pull/22
trace "Couldn't update metrics", msg = e.msg
process_epoch(state)
state.slot += 1
if is_epoch_transition:
try:
beacon_current_validators.set(get_epoch_validator_count(state))
except Exception as e: # TODO https://github.com/status-im/nim-metrics/pull/22
trace "Couldn't update metrics", msg = e.msg
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#verify_block_signature
proc verify_block_signature*(
state: BeaconState, signedBlock: SignedBeaconBlock): bool {.nbench.} =
@ -144,9 +100,74 @@ type
proc noRollback*(state: var BeaconState) =
trace "Skipping rollback of broken state"
type
RollbackHashedProc* = proc(state: var HashedBeaconState) {.gcsafe.}
# Hashed-state transition functions
# ---------------------------------------------------------------
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#beacon-chain-state-transition-function
func process_slot*(state: var HashedBeaconState) {.nbench.} =
# Cache state root
let previous_slot_state_root = state.root
state.data.state_roots[state.data.slot mod SLOTS_PER_HISTORICAL_ROOT] =
previous_slot_state_root
# Cache latest block header state root
if state.data.latest_block_header.state_root == ZERO_HASH:
state.data.latest_block_header.state_root = previous_slot_state_root
# Cache block root
state.data.block_roots[state.data.slot mod SLOTS_PER_HISTORICAL_ROOT] =
hash_tree_root(state.data.latest_block_header)
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#beacon-chain-state-transition-function
proc process_slots*(state: var HashedBeaconState, slot: Slot) {.nbench.} =
# TODO: Eth specs strongly assert that state.data.slot <= slot
# This prevents receiving attestation in any order
# (see tests/test_attestation_pool)
# but it maybe an artifact of the test case
# as this was not triggered in the testnet1
# after a hour
if state.data.slot > slot:
notice(
"Unusual request for a slot in the past",
state_root = shortLog(state.root),
current_slot = state.data.slot,
target_slot = slot
)
# Catch up to the target slot
while state.data.slot < slot:
process_slot(state)
let is_epoch_transition = (state.data.slot + 1) mod SLOTS_PER_EPOCH == 0
if is_epoch_transition:
# Note: Genesis epoch = 0, no need to test if before Genesis
try:
beacon_previous_validators.set(get_epoch_validator_count(state.data))
except Exception as e: # TODO https://github.com/status-im/nim-metrics/pull/22
trace "Couldn't update metrics", msg = e.msg
process_epoch(state.data)
state.data.slot += 1
if is_epoch_transition:
try:
beacon_current_validators.set(get_epoch_validator_count(state.data))
except Exception as e: # TODO https://github.com/status-im/nim-metrics/pull/22
trace "Couldn't update metrics", msg = e.msg
state.root = hash_tree_root(state.data)
# TODO remove this once callers gone
proc process_slots*(state: var BeaconState, slot: Slot) =
var hashedState = HashedBeaconState(data: state, root: hash_tree_root(state))
process_slots(hashedState, slot)
state = hashedState.data
proc noRollback*(state: var HashedBeaconState) =
trace "Skipping rollback of broken state"
proc state_transition*(
state: var BeaconState, signedBlock: SignedBeaconBlock, flags: UpdateFlags,
rollback: RollbackProc): bool {.nbench.} =
state: var HashedBeaconState, signedBlock: SignedBeaconBlock,
flags: UpdateFlags, rollback: RollbackHashedProc): 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
@ -190,87 +211,6 @@ proc state_transition*(
# that the block is sane.
# TODO what should happen if block processing fails?
# https://github.com/ethereum/eth2.0-specs/issues/293
if skipBLSValidation in flags or
verify_block_signature(state, signedBlock):
var per_epoch_cache = get_empty_per_epoch_cache()
if processBlock(state, signedBlock.message, flags, per_epoch_cache):
# 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 skipStateRootValidation in flags or verifyStateRoot(state, signedBlock.message):
# State root is what it should be - we're done!
return true
# Block processing failed, roll back changes
rollback(state)
false
# Hashed-state transition functions
# ---------------------------------------------------------------
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#beacon-chain-state-transition-function
func process_slot(state: var HashedBeaconState) =
# Cache state root
let previous_slot_state_root = state.root
state.data.state_roots[state.data.slot mod SLOTS_PER_HISTORICAL_ROOT] =
previous_slot_state_root
# Cache latest block header state root
if state.data.latest_block_header.state_root == ZERO_HASH:
state.data.latest_block_header.state_root = previous_slot_state_root
# Cache block root
state.data.block_roots[state.data.slot mod SLOTS_PER_HISTORICAL_ROOT] =
hash_tree_root(state.data.latest_block_header)
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#beacon-chain-state-transition-function
proc process_slots*(state: var HashedBeaconState, slot: Slot) =
# TODO: Eth specs strongly assert that state.data.slot <= slot
# This prevents receiving attestation in any order
# (see tests/test_attestation_pool)
# but it maybe an artifact of the test case
# as this was not triggered in the testnet1
# after a hour
if state.data.slot > slot:
notice(
"Unusual request for a slot in the past",
state_root = shortLog(state.root),
current_slot = state.data.slot,
target_slot = slot
)
# Catch up to the target slot
while state.data.slot < slot:
process_slot(state)
let is_epoch_transition = (state.data.slot + 1) mod SLOTS_PER_EPOCH == 0
if is_epoch_transition:
# Note: Genesis epoch = 0, no need to test if before Genesis
try:
beacon_previous_validators.set(get_epoch_validator_count(state.data))
except Exception as e: # TODO https://github.com/status-im/nim-metrics/pull/22
trace "Couldn't update metrics", msg = e.msg
process_epoch(state.data)
state.data.slot += 1
if is_epoch_transition:
try:
beacon_current_validators.set(get_epoch_validator_count(state.data))
except Exception as e: # TODO https://github.com/status-im/nim-metrics/pull/22
trace "Couldn't update metrics", msg = e.msg
state.root = hash_tree_root(state.data)
type
RollbackHashedProc* = proc(state: var HashedBeaconState) {.gcsafe.}
proc noRollback*(state: var HashedBeaconState) =
trace "Skipping rollback of broken state"
proc state_transition*(
state: var HashedBeaconState, signedBlock: SignedBeaconBlock,
flags: UpdateFlags, rollback: RollbackHashedProc): bool =
doAssert not rollback.isNil, "use noRollback if it's ok to mess up state"
process_slots(state, signedBlock.message.slot)
if skipBLSValidation in flags or
verify_block_signature(state.data, signedBlock):
@ -292,3 +232,11 @@ proc state_transition*(
rollback(state)
false
# TODO remove this once callers gone
proc state_transition*(
state: var BeaconState, signedBlock: SignedBeaconBlock, flags: UpdateFlags,
rollback: RollbackHashedProc): bool {.nbench.} =
var hashedState = HashedBeaconState(data: state, root: hash_tree_root(state))
result = state_transition(hashedState, signedBlock, flags, rollback)
state = hashedState.data

View File

@ -123,12 +123,12 @@ proc fillAggregateAttestation*(state: BeaconState, attestation: var Attestation)
for i in 0 ..< beacon_committee.len:
attestation.aggregation_bits[i] = true
proc add*(state: var BeaconState, attestation: Attestation, slot: Slot) =
var signedBlock = mockBlockForNextSlot(state)
proc add*(state: var HashedBeaconState, attestation: Attestation, slot: Slot) =
var signedBlock = mockBlockForNextSlot(state.data)
signedBlock.message.slot = slot
signedBlock.message.body.attestations.add attestation
process_slots(state, slot)
signMockBlock(state, signedBlock)
signMockBlock(state.data, signedBlock)
doAssert state_transition(
state, signedBlock, flags = {skipStateRootValidation}, noRollback)

View File

@ -61,9 +61,9 @@ proc mockBlockForNextSlot*(state: BeaconState, flags: UpdateFlags = {}):
SignedBeaconBlock =
mockBlock(state, state.slot + 1, flags)
proc applyEmptyBlock*(state: var BeaconState) =
proc applyEmptyBlock*(state: var HashedBeaconState) =
## Do a state transition with an empty signed block
## on the current slot
let signedBlock = mockBlock(state, state.slot, flags = {})
let signedBlock = mockBlock(state.data, state.data.slot, flags = {})
doAssert state_transition(
state, signedBlock, {skipStateRootValidation}, noRollback)

View File

@ -17,14 +17,14 @@ import
./mock_deposits
proc initGenesisState*(num_validators: uint64, genesis_time: uint64 = 0): BeaconStateRef =
proc initGenesisState*(num_validators: uint64, genesis_time: uint64 = 0): HashedBeaconState =
let deposits = mockGenesisBalancedDeposits(
validatorCount = num_validators,
amountInEth = 32, # We create canonical validators with 32 Eth
flags = {skipBlsValidation}
)
initialize_beacon_state_from_eth1(
initialize_hashed_beacon_state_from_eth1(
eth1BlockHash, 0, deposits, {skipBlsValidation})
when isMainModule:

View File

@ -1,5 +1,5 @@
# beacon_chain
# Copyright (c) 2018-2019 Status Research & Development GmbH
# Copyright (c) 2018-2020 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
@ -14,11 +14,12 @@ import
# Internals
../../beacon_chain/state_transition
proc nextEpoch*(state: var BeaconState) =
proc nextEpoch*(state: var HashedBeaconState) =
## Transition to the start of the next epoch
let slot = state.slot + SLOTS_PER_EPOCH - (state.slot mod SLOTS_PER_EPOCH)
let slot =
state.data.slot + SLOTS_PER_EPOCH - (state.data.slot mod SLOTS_PER_EPOCH)
process_slots(state, slot)
proc nextSlot*(state: var BeaconState) =
proc nextSlot*(state: var HashedBeaconState) =
## Transition to the next slot
process_slots(state, state.slot + 1)
process_slots(state, state.data.slot + 1)

View File

@ -24,7 +24,7 @@ suiteReport "[Unit - Spec - Block processing] Attestations " & preset():
const NumValidators = uint64(8) * SLOTS_PER_EPOCH
let genesisState = initGenesisState(NumValidators)
doAssert genesisState.validators.len == int NumValidators
doAssert genesisState.data.validators.len == int NumValidators
template valid_attestation(name: string, body: untyped): untyped {.dirty.}=
# Process a valid attestation
@ -42,29 +42,29 @@ suiteReport "[Unit - Spec - Block processing] Attestations " & preset():
# Params for sanity checks
# ----------------------------------------
let
current_epoch_count = state.current_epoch_attestations.len
previous_epoch_count = state.previous_epoch_attestations.len
current_epoch_count = state.data.current_epoch_attestations.len
previous_epoch_count = state.data.previous_epoch_attestations.len
# State transition
# ----------------------------------------
var cache = get_empty_per_epoch_cache()
check process_attestation(
state[], attestation, flags = {}, cache
state.data, attestation, flags = {}, cache
)
# Check that the attestation was processed
if attestation.data.target.epoch == get_current_epoch(state[]):
check(state.current_epoch_attestations.len == current_epoch_count + 1)
if attestation.data.target.epoch == get_current_epoch(state.data):
check(state.data.current_epoch_attestations.len == current_epoch_count + 1)
else:
check(state.previous_epoch_attestations.len == previous_epoch_count + 1)
check(state.data.previous_epoch_attestations.len == previous_epoch_count + 1)
valid_attestation("Valid attestation"):
let attestation = mockAttestation(state[])
state.slot += MIN_ATTESTATION_INCLUSION_DELAY
let attestation = mockAttestation(state.data)
state.data.slot += MIN_ATTESTATION_INCLUSION_DELAY
valid_attestation("Valid attestation from previous epoch"):
let attestation = mockAttestation(state[])
state.slot = Slot(SLOTS_PER_EPOCH - 1)
let attestation = mockAttestation(state.data)
state.data.slot = Slot(SLOTS_PER_EPOCH - 1)
nextEpoch(state[])
applyEmptyBlock(state[])
@ -90,7 +90,7 @@ suiteReport "[Unit - Spec - Block processing] Attestations " & preset():
# valid_attestation("Empty aggregation bit"):
# var attestation = mockAttestation(state)
# state.slot += MIN_ATTESTATION_INCLUSION_DELAY
# state.data.slot += MIN_ATTESTATION_INCLUSION_DELAY
# # Overwrite committee
# attestation.aggregation_bits = init(CommitteeValidatorsBits, attestation.aggregation_bits.len)

View File

@ -27,7 +27,7 @@ suiteReport "[Unit - Spec - Block processing] Deposits " & preset():
const NumValidators = uint64 5 * SLOTS_PER_EPOCH
let genesisState = initGenesisState(NumValidators)
doAssert genesisState.validators.len == int NumValidators
doAssert genesisState.data.validators.len == int NumValidators
template valid_deposit(deposit_amount: uint64, name: string): untyped =
# TODO: BLS signature
@ -37,9 +37,9 @@ suiteReport "[Unit - Spec - Block processing] Deposits " & preset():
# Test configuration
# ----------------------------------------
let validator_index = state.validators.len
let validator_index = state.data.validators.len
let deposit = mockUpdateStateForNewDeposit(
state[],
state.data,
uint64 validator_index,
deposit_amount,
flags = {skipBlsValidation}
@ -47,25 +47,25 @@ suiteReport "[Unit - Spec - Block processing] Deposits " & preset():
# Params for sanity checks
# ----------------------------------------
let pre_val_count = state.validators.len
let pre_val_count = state.data.validators.len
let pre_balance = if validator_index < pre_val_count:
state.balances[validator_index]
state.data.balances[validator_index]
else:
0
# State transition
# ----------------------------------------
check: process_deposit(state[], deposit, {skipBlsValidation})
check: process_deposit(state.data, deposit, {skipBlsValidation})
# Check invariants
# ----------------------------------------
check:
state.validators.len == pre_val_count + 1
state.balances.len == pre_val_count + 1
state.balances[validator_index] == pre_balance + deposit.data.amount
state.validators[validator_index].effective_balance ==
state.data.validators.len == pre_val_count + 1
state.data.balances.len == pre_val_count + 1
state.data.balances[validator_index] == pre_balance + deposit.data.amount
state.data.validators[validator_index].effective_balance ==
round_multiple_down(
min(MAX_EFFECTIVE_BALANCE, state.balances[validator_index]),
min(MAX_EFFECTIVE_BALANCE, state.data.balances[validator_index]),
EFFECTIVE_BALANCE_INCREMENT
)
@ -81,7 +81,7 @@ suiteReport "[Unit - Spec - Block processing] Deposits " & preset():
let validator_index = 0
let deposit_amount = MAX_EFFECTIVE_BALANCE div 4
let deposit = mockUpdateStateForNewDeposit(
state[],
state.data,
uint64 validator_index,
deposit_amount,
flags = {skipBlsValidation}
@ -89,25 +89,25 @@ suiteReport "[Unit - Spec - Block processing] Deposits " & preset():
# Params for sanity checks
# ----------------------------------------
let pre_val_count = state.validators.len
let pre_val_count = state.data.validators.len
let pre_balance = if validator_index < pre_val_count:
state.balances[validator_index]
state.data.balances[validator_index]
else:
0
# State transition
# ----------------------------------------
check: process_deposit(state[], deposit, {skipBlsValidation})
check: process_deposit(state.data, deposit, {skipBlsValidation})
# Check invariants
# ----------------------------------------
check:
state.validators.len == pre_val_count
state.balances.len == pre_val_count
state.balances[validator_index] == pre_balance + deposit.data.amount
state.validators[validator_index].effective_balance ==
state.data.validators.len == pre_val_count
state.data.balances.len == pre_val_count
state.data.balances[validator_index] == pre_balance + deposit.data.amount
state.data.validators[validator_index].effective_balance ==
round_multiple_down(
min(MAX_EFFECTIVE_BALANCE, state.balances[validator_index]),
min(MAX_EFFECTIVE_BALANCE, state.data.balances[validator_index]),
EFFECTIVE_BALANCE_INCREMENT
)

View File

@ -1,5 +1,5 @@
# beacon_chain
# Copyright (c) 2018 Status Research & Development GmbH
# Copyright (c) 2018-2020 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
@ -11,9 +11,10 @@ import
# Internals
../../beacon_chain/[state_transition]
proc processSlotsUntilEndCurrentEpoch(state: var BeaconState) =
proc processSlotsUntilEndCurrentEpoch(state: var HashedBeaconState) =
# Process all slots until the end of the last slot of the current epoch
let slot = state.slot + SLOTS_PER_EPOCH - (state.slot mod SLOTS_PER_EPOCH)
let slot =
state.data.slot + SLOTS_PER_EPOCH - (state.data.slot mod SLOTS_PER_EPOCH)
# Transition to slot before the epoch state transition
process_slots(state, slot - 1)
@ -23,11 +24,11 @@ proc processSlotsUntilEndCurrentEpoch(state: var BeaconState) =
# (see process_slots())
process_slot(state)
proc transitionEpochUntilJustificationFinalization*(state: var BeaconState) =
proc transitionEpochUntilJustificationFinalization*(state: var HashedBeaconState) =
# Process slots and do the epoch transition until crosslinks
processSlotsUntilEndCurrentEpoch(state)
# From process_epoch()
var per_epoch_cache = get_empty_per_epoch_cache()
process_justification_and_finalization(state, per_epoch_cache)
process_justification_and_finalization(state.data, per_epoch_cache)

View File

@ -1,5 +1,5 @@
# beacon_chain
# Copyright (c) 2018 Status Research & Development GmbH
# Copyright (c) 2018-2020 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
@ -24,10 +24,10 @@ import
# (source) https://github.com/protolambda/eth2-docs#justification-and-finalization
# for a visualization of finalization rules
proc finalizeOn234(state: var BeaconState, epoch: Epoch, sufficient_support: bool) =
proc finalizeOn234(state: var HashedBeaconState, epoch: Epoch, sufficient_support: bool) =
## Check finalization on rule 1 "234"
doAssert epoch > 4
state.slot = Slot((epoch * SLOTS_PER_EPOCH) - 1) # Skip ahead to just before epoch
state.data.slot = Slot((epoch * SLOTS_PER_EPOCH) - 1) # Skip ahead to just before epoch
# 43210 -- epochs ago
# 3210x -- justification bitfields indices
@ -35,22 +35,22 @@ proc finalizeOn234(state: var BeaconState, epoch: Epoch, sufficient_support: boo
# checkpoints for epochs ago
let (c1, c2, c3, c4, _) = getCheckpoints(epoch)
putCheckpointsInBlockRoots(state, [c1, c2, c3, c4])
putCheckpointsInBlockRoots(state.data, [c1, c2, c3, c4])
# Save for final checks
let old_finalized = state.finalized_checkpoint
let old_finalized = state.data.finalized_checkpoint
# Mock the state
state.previous_justified_checkpoint = c4
state.current_justified_checkpoint = c3
state.justification_bits = 0'u8 # Bitvector of length 4
state.data.previous_justified_checkpoint = c4
state.data.current_justified_checkpoint = c3
state.data.justification_bits = 0'u8 # Bitvector of length 4
# mock 3rd and 4th latest epochs as justified
# indices are pre-shift
state.justification_bits.setBit 1
state.justification_bits.setBit 2
state.data.justification_bits.setBit 1
state.data.justification_bits.setBit 2
# mock the 2nd latest epoch as justifiable, with 4th as the source
addMockAttestations(
state,
state.data,
epoch = epoch - 2,
source = c4,
target = c2,
@ -61,18 +61,18 @@ proc finalizeOn234(state: var BeaconState, epoch: Epoch, sufficient_support: boo
transitionEpochUntilJustificationFinalization(state)
# Checks
doAssert state.previous_justified_checkpoint == c3 # changed to old current
doAssert state.data.previous_justified_checkpoint == c3 # changed to old current
if sufficient_support:
doAssert state.current_justified_checkpoint == c2 # changed to second latest
doAssert state.finalized_checkpoint == c4 # finalized old previous justified epoch
doAssert state.data.current_justified_checkpoint == c2 # changed to second latest
doAssert state.data.finalized_checkpoint == c4 # finalized old previous justified epoch
else:
doAssert state.current_justified_checkpoint == c3 # still old current
doAssert state.finalized_checkpoint == old_finalized # no new finalized checkpoint
doAssert state.data.current_justified_checkpoint == c3 # still old current
doAssert state.data.finalized_checkpoint == old_finalized # no new finalized checkpoint
proc finalizeOn23(state: var BeaconState, epoch: Epoch, sufficient_support: bool) =
proc finalizeOn23(state: var HashedBeaconState, epoch: Epoch, sufficient_support: bool) =
## Check finalization on rule 2 "23"
doAssert epoch > 3
state.slot = Slot((epoch * SLOTS_PER_EPOCH) - 1) # Skip ahead to just before epoch
state.data.slot = Slot((epoch * SLOTS_PER_EPOCH) - 1) # Skip ahead to just before epoch
# 43210 -- epochs ago
# 210xx -- justification bitfields indices preshift
@ -81,21 +81,21 @@ proc finalizeOn23(state: var BeaconState, epoch: Epoch, sufficient_support: bool
# checkpoints for epochs ago
let (c1, c2, c3, _, _) = getCheckpoints(epoch)
putCheckpointsInBlockRoots(state, [c1, c2, c3])
putCheckpointsInBlockRoots(state.data, [c1, c2, c3])
# Save for final checks
let old_finalized = state.finalized_checkpoint
let old_finalized = state.data.finalized_checkpoint
# Mock the state
state.previous_justified_checkpoint = c3
state.current_justified_checkpoint = c3
state.justification_bits = 0'u8 # Bitvector of length 4
state.data.previous_justified_checkpoint = c3
state.data.current_justified_checkpoint = c3
state.data.justification_bits = 0'u8 # Bitvector of length 4
# mock 3rd as justified
# indices are pre-shift
state.justification_bits.setBit 1
state.data.justification_bits.setBit 1
# mock the 2nd latest epoch as justifiable, with 3rd as the source
addMockAttestations(
state,
state.data,
epoch = epoch - 2,
source = c3,
target = c2,
@ -106,18 +106,18 @@ proc finalizeOn23(state: var BeaconState, epoch: Epoch, sufficient_support: bool
transitionEpochUntilJustificationFinalization(state)
# Checks
doAssert state.previous_justified_checkpoint == c3 # changed to old current
doAssert state.data.previous_justified_checkpoint == c3 # changed to old current
if sufficient_support:
doAssert state.current_justified_checkpoint == c2 # changed to second latest
doAssert state.finalized_checkpoint == c3 # finalized old previous justified epoch
doAssert state.data.current_justified_checkpoint == c2 # changed to second latest
doAssert state.data.finalized_checkpoint == c3 # finalized old previous justified epoch
else:
doAssert state.current_justified_checkpoint == c3 # still old current
doAssert state.finalized_checkpoint == old_finalized # no new finalized checkpoint
doAssert state.data.current_justified_checkpoint == c3 # still old current
doAssert state.data.finalized_checkpoint == old_finalized # no new finalized checkpoint
proc finalizeOn123(state: var BeaconState, epoch: Epoch, sufficient_support: bool) =
proc finalizeOn123(state: var HashedBeaconState, epoch: Epoch, sufficient_support: bool) =
## Check finalization on rule 3 "123"
doAssert epoch > 5
state.slot = Slot((epoch * SLOTS_PER_EPOCH) - 1) # Skip ahead to just before epoch
state.data.slot = Slot((epoch * SLOTS_PER_EPOCH) - 1) # Skip ahead to just before epoch
# 43210 -- epochs ago
# 210xx -- justification bitfields indices preshift
@ -126,21 +126,21 @@ proc finalizeOn123(state: var BeaconState, epoch: Epoch, sufficient_support: boo
# checkpoints for epochs ago
let (c1, c2, c3, c4, c5) = getCheckpoints(epoch)
putCheckpointsInBlockRoots(state, [c1, c2, c3, c4, c5])
putCheckpointsInBlockRoots(state.data, [c1, c2, c3, c4, c5])
# Save for final checks
let old_finalized = state.finalized_checkpoint
let old_finalized = state.data.finalized_checkpoint
# Mock the state
state.previous_justified_checkpoint = c5
state.current_justified_checkpoint = c3
state.justification_bits = 0'u8 # Bitvector of length 4
state.data.previous_justified_checkpoint = c5
state.data.current_justified_checkpoint = c3
state.data.justification_bits = 0'u8 # Bitvector of length 4
# mock 3rd as justified
# indices are pre-shift
state.justification_bits.setBit 1
state.data.justification_bits.setBit 1
# mock the 2nd latest epoch as justifiable, with 5th as the source
addMockAttestations(
state,
state.data,
epoch = epoch - 2,
source = c5,
target = c2,
@ -148,7 +148,7 @@ proc finalizeOn123(state: var BeaconState, epoch: Epoch, sufficient_support: boo
)
# mock the 1st latest epoch as justifiable with 3rd as source
addMockAttestations(
state,
state.data,
epoch = epoch - 1,
source = c3,
target = c1,
@ -159,18 +159,18 @@ proc finalizeOn123(state: var BeaconState, epoch: Epoch, sufficient_support: boo
transitionEpochUntilJustificationFinalization(state)
# Checks
doAssert state.previous_justified_checkpoint == c3 # changed to old current
doAssert state.data.previous_justified_checkpoint == c3 # changed to old current
if sufficient_support:
doAssert state.current_justified_checkpoint == c1 # changed to second latest
doAssert state.finalized_checkpoint == c3 # finalized old previous justified epoch
doAssert state.data.current_justified_checkpoint == c1 # changed to second latest
doAssert state.data.finalized_checkpoint == c3 # finalized old previous justified epoch
else:
doAssert state.current_justified_checkpoint == c3 # still old current
doAssert state.finalized_checkpoint == old_finalized # no new finalized checkpoint
doAssert state.data.current_justified_checkpoint == c3 # still old current
doAssert state.data.finalized_checkpoint == old_finalized # no new finalized checkpoint
proc finalizeOn12(state: var BeaconState, epoch: Epoch, sufficient_support: bool) =
proc finalizeOn12(state: var HashedBeaconState, epoch: Epoch, sufficient_support: bool) =
## Check finalization on rule 4 "12"
doAssert epoch > 2
state.slot = Slot((epoch * SLOTS_PER_EPOCH) - 1) # Skip ahead to just before epoch
state.data.slot = Slot((epoch * SLOTS_PER_EPOCH) - 1) # Skip ahead to just before epoch
# 43210 -- epochs ago
# 210xx -- justification bitfields indices preshift
@ -179,21 +179,21 @@ proc finalizeOn12(state: var BeaconState, epoch: Epoch, sufficient_support: bool
# checkpoints for epochs ago
let (c1, c2, _, _, _) = getCheckpoints(epoch)
putCheckpointsInBlockRoots(state, [c1, c2])
putCheckpointsInBlockRoots(state.data, [c1, c2])
# Save for final checks
let old_finalized = state.finalized_checkpoint
let old_finalized = state.data.finalized_checkpoint
# Mock the state
state.previous_justified_checkpoint = c2
state.current_justified_checkpoint = c2
state.justification_bits = 0'u8 # Bitvector of length 4
state.data.previous_justified_checkpoint = c2
state.data.current_justified_checkpoint = c2
state.data.justification_bits = 0'u8 # Bitvector of length 4
# mock 3rd as justified
# indices are pre-shift
state.justification_bits.setBit 0
state.data.justification_bits.setBit 0
# mock the 2nd latest epoch as justifiable, with 3rd as the source
addMockAttestations(
state,
state.data,
epoch = epoch - 1,
source = c2,
target = c1,
@ -204,13 +204,13 @@ proc finalizeOn12(state: var BeaconState, epoch: Epoch, sufficient_support: bool
transitionEpochUntilJustificationFinalization(state)
# Checks
doAssert state.previous_justified_checkpoint == c2 # changed to old current
doAssert state.data.previous_justified_checkpoint == c2 # changed to old current
if sufficient_support:
doAssert state.current_justified_checkpoint == c1 # changed to second latest
doAssert state.finalized_checkpoint == c2 # finalized old previous justified epoch
doAssert state.data.current_justified_checkpoint == c1 # changed to second latest
doAssert state.data.finalized_checkpoint == c2 # finalized old previous justified epoch
else:
doAssert state.current_justified_checkpoint == c2 # still old current
doAssert state.finalized_checkpoint == old_finalized # no new finalized checkpoint
doAssert state.data.current_justified_checkpoint == c2 # still old current
doAssert state.data.finalized_checkpoint == old_finalized # no new finalized checkpoint
proc payload =
suiteReport "[Unit - Spec - Epoch processing] Justification and Finalization " & preset():
@ -218,7 +218,7 @@ proc payload =
const NumValidators = uint64(8) * SLOTS_PER_EPOCH
let genesisState = initGenesisState(NumValidators)
doAssert genesisState.validators.len == int NumValidators
doAssert genesisState.data.validators.len == int NumValidators
setup:
var state = newClone(genesisState)

View File

@ -20,43 +20,43 @@ suiteReport "Block processing" & preset():
let
# Genesis state with minimal number of deposits
# TODO bls verification is a bit of a bottleneck here
genesisState = initialize_beacon_state_from_eth1(
genesisState = initialize_hashed_beacon_state_from_eth1(
Eth2Digest(), 0, makeInitialDeposits(), {})
genesisBlock = get_initial_beacon_block(genesisState[])
genesisBlock = get_initial_beacon_block(genesisState.data)
genesisRoot = hash_tree_root(genesisBlock.message)
setup:
var state = newClone(genesisState)
timedTest "Passes from genesis state, no block" & preset():
process_slots(state[], state.slot + 1)
process_slots(state[], state.data.slot + 1)
check:
state.slot == genesisState.slot + 1
state.data.slot == genesisState.data.slot + 1
timedTest "Passes from genesis state, empty block" & preset():
var
previous_block_root = hash_tree_root(genesisBlock.message)
new_block = makeTestBlock(state[], previous_block_root)
new_block = makeTestBlock(state.data, previous_block_root)
let block_ok = state_transition(state[], new_block, {}, noRollback)
check:
block_ok
state.slot == genesisState.slot + 1
state.data.slot == genesisState.data.slot + 1
timedTest "Passes through epoch update, no block" & preset():
process_slots(state[], Slot(SLOTS_PER_EPOCH))
check:
state.slot == genesisState.slot + SLOTS_PER_EPOCH
state.data.slot == genesisState.data.slot + SLOTS_PER_EPOCH
timedTest "Passes through epoch update, empty block" & preset():
var
previous_block_root = genesisRoot
for i in 1..SLOTS_PER_EPOCH.int:
let new_block = makeTestBlock(state[], previous_block_root)
let new_block = makeTestBlock(state.data, previous_block_root)
let block_ok = state_transition(state[], new_block, {}, noRollback)
@ -66,7 +66,7 @@ suiteReport "Block processing" & preset():
previous_block_root = hash_tree_root(new_block.message)
check:
state.slot == genesisState.slot + SLOTS_PER_EPOCH
state.data.slot == genesisState.data.slot + SLOTS_PER_EPOCH
timedTest "Attestation gets processed at epoch" & preset():
var
@ -74,21 +74,21 @@ suiteReport "Block processing" & preset():
cache = get_empty_per_epoch_cache()
# Slot 0 is a finalized slot - won't be making attestations for it..
process_slots(state[], state.slot + 1)
process_slots(state[], state.data.slot + 1)
let
# Create an attestation for slot 1 signed by the only attester we have!
beacon_committee =
get_beacon_committee(state[], state.slot, 0.CommitteeIndex, cache)
get_beacon_committee(state.data, state.data.slot, 0.CommitteeIndex, cache)
attestation = makeAttestation(
state[], previous_block_root, beacon_committee[0], cache)
state.data, previous_block_root, beacon_committee[0], cache)
# Some time needs to pass before attestations are included - this is
# to let the attestation propagate properly to interested participants
process_slots(state[], GENESIS_SLOT + MIN_ATTESTATION_INCLUSION_DELAY + 1)
let
new_block = makeTestBlock(state[], previous_block_root,
new_block = makeTestBlock(state.data, previous_block_root,
attestations = @[attestation]
)
check state_transition(state[], new_block, {}, noRollback)
@ -96,7 +96,7 @@ suiteReport "Block processing" & preset():
check:
# TODO epoch attestations can get multiplied now; clean up paths to
# enable exact 1-check again and keep finalization.
state.current_epoch_attestations.len >= 1
state.data.current_epoch_attestations.len >= 1
when const_preset=="minimal":
# Can take several minutes with mainnet settings