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:
parent
c74ba5c0c6
commit
3a56ddc5c4
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue