Altair transition tests (#2624)
* Working Altair transition tests * with fixed upstream test vectors, remove state root workaround * switch upgrade_to_altair() to returning a reference * remove test_state_transition * fix invalid fork state/block combinations error messages * avoid memory copies by reintroducing state_transition_slots(var SomeHashedBeaconState)
This commit is contained in:
parent
1a76007858
commit
8ebd496fbe
|
@ -113,15 +113,6 @@ OK: 4/4 Fail: 0/4 Skip: 0/4
|
|||
+ updateStateData sanity [Preset: mainnet] OK
|
||||
```
|
||||
OK: 7/7 Fail: 0/7 Skip: 0/7
|
||||
## Block processing [Preset: mainnet]
|
||||
```diff
|
||||
+ Attestation gets processed at epoch [Preset: mainnet] OK
|
||||
+ Passes from genesis state, empty block [Preset: mainnet] OK
|
||||
+ Passes from genesis state, no block [Preset: mainnet] OK
|
||||
+ Passes through epoch update, empty block [Preset: mainnet] OK
|
||||
+ Passes through epoch update, no block [Preset: mainnet] OK
|
||||
```
|
||||
OK: 5/5 Fail: 0/5 Skip: 0/5
|
||||
## BlockRef and helpers [Preset: mainnet]
|
||||
```diff
|
||||
+ epochAncestor sanity [Preset: mainnet] OK
|
||||
|
@ -317,4 +308,4 @@ OK: 3/3 Fail: 0/3 Skip: 0/3
|
|||
OK: 1/1 Fail: 0/1 Skip: 0/1
|
||||
|
||||
---TOTAL---
|
||||
OK: 179/187 Fail: 0/187 Skip: 8/187
|
||||
OK: 174/182 Fail: 0/182 Skip: 8/182
|
||||
|
|
|
@ -2,6 +2,14 @@ FixtureAll-mainnet
|
|||
===
|
||||
##
|
||||
```diff
|
||||
+ Official - Altair - Transition - normal_transition [Preset: mainnet] OK
|
||||
+ Official - Altair - Transition - transition_missing_first_post_block [Preset: mainnet] OK
|
||||
+ Official - Altair - Transition - transition_missing_last_pre_fork_block [Preset: mainnet] OK
|
||||
+ Official - Altair - Transition - transition_only_blocks_post_fork [Preset: mainnet] OK
|
||||
+ Official - Altair - Transition - transition_with_finality [Preset: mainnet] OK
|
||||
+ Official - Altair - Transition - transition_with_no_attestations_until_after_fork [Preset: OK
|
||||
+ Official - Altair - Transition - transition_with_random_half_participation [Preset: mainne OK
|
||||
+ Official - Altair - Transition - transition_with_random_three_quarters_participation [Pres OK
|
||||
+ Rewards - all_balances_too_low_for_reward [Preset: mainnet] OK
|
||||
+ Rewards - duplicate_attestations_at_later_slots [Preset: mainnet] OK
|
||||
+ Rewards - empty [Preset: mainnet] OK
|
||||
|
@ -257,7 +265,7 @@ FixtureAll-mainnet
|
|||
+ fork_random_low_balances OK
|
||||
+ fork_random_misc_balances OK
|
||||
```
|
||||
OK: 254/254 Fail: 0/254 Skip: 0/254
|
||||
OK: 262/262 Fail: 0/262 Skip: 0/262
|
||||
## Official - Altair - Epoch Processing - Effective balance updates [Preset: mainnet]
|
||||
```diff
|
||||
+ Effective balance updates - effective_balance_hysteresis [Preset: mainnet] OK
|
||||
|
@ -475,4 +483,4 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
|
|||
OK: 27/27 Fail: 0/27 Skip: 0/27
|
||||
|
||||
---TOTAL---
|
||||
OK: 385/385 Fail: 0/385 Skip: 0/385
|
||||
OK: 393/393 Fail: 0/393 Skip: 0/393
|
||||
|
|
|
@ -172,10 +172,9 @@ proc checkStateTransition(
|
|||
blck = shortLog(signedBlock.message)
|
||||
blockRoot = shortLog(signedBlock.root)
|
||||
|
||||
var rewards: RewardInfo
|
||||
if not state_transition_block(
|
||||
dag.runtimePreset, dag.clearanceState.data, signedBlock,
|
||||
cache, rewards, dag.updateFlags, restore):
|
||||
cache, dag.updateFlags, restore):
|
||||
info "Invalid block"
|
||||
|
||||
return (ValidationResult.Reject, Invalid)
|
||||
|
|
|
@ -854,7 +854,7 @@ func translate_participation(
|
|||
state.previous_epoch_participation[index] =
|
||||
add_flag(state.previous_epoch_participation[index], flag_index)
|
||||
|
||||
proc upgrade_to_altair*(pre: phase0.BeaconState): altair.BeaconState =
|
||||
proc upgrade_to_altair*(pre: phase0.BeaconState): ref altair.BeaconState =
|
||||
let epoch = get_current_epoch(pre)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.6/specs/altair/fork.md#configuration
|
||||
|
@ -869,7 +869,7 @@ proc upgrade_to_altair*(pre: phase0.BeaconState): altair.BeaconState =
|
|||
for _ in 0 ..< len(pre.validators):
|
||||
doAssert inactivity_scores.add 0'u64
|
||||
|
||||
var post = altair.BeaconState(
|
||||
var post = (ref altair.BeaconState)(
|
||||
genesis_time: pre.genesis_time,
|
||||
genesis_validators_root: pre.genesis_validators_root,
|
||||
slot: pre.slot,
|
||||
|
@ -916,12 +916,12 @@ proc upgrade_to_altair*(pre: phase0.BeaconState): altair.BeaconState =
|
|||
|
||||
# Fill in previous epoch participation from the pre state's pending
|
||||
# attestations
|
||||
translate_participation(post, pre.previous_epoch_attestations.asSeq)
|
||||
translate_participation(post[], pre.previous_epoch_attestations.asSeq)
|
||||
|
||||
# Fill in sync committees
|
||||
# Note: A duplicate committee is assigned for the current and next committee
|
||||
# at the fork boundary
|
||||
post.current_sync_committee = get_next_sync_committee(post)
|
||||
post.next_sync_committee = get_next_sync_committee(post)
|
||||
post[].current_sync_committee = get_next_sync_committee(post[])
|
||||
post[].next_sync_committee = get_next_sync_committee(post[])
|
||||
|
||||
post
|
||||
|
|
|
@ -45,7 +45,7 @@ import
|
|||
chronicles,
|
||||
stew/results,
|
||||
../extras, ../ssz/merkleization, metrics,
|
||||
./datatypes/[phase0, altair], ./crypto, ./digest, ./helpers, ./signatures, ./validator,
|
||||
./datatypes/[phase0, altair], ./crypto, ./digest, ./helpers, ./signatures, ./validator, ./beaconstate,
|
||||
./state_transition_block, ./state_transition_epoch,
|
||||
../../nbench/bench_lab
|
||||
|
||||
|
@ -89,6 +89,20 @@ func verifyStateRoot(state: phase0.BeaconState, blck: phase0.TrustedBeaconBlock)
|
|||
# This is inlined in state_transition(...) in spec.
|
||||
true
|
||||
|
||||
func verifyStateRoot(state: altair.BeaconState, blck: altair.TrustedBeaconBlock): bool =
|
||||
# This is inlined in state_transition(...) in spec.
|
||||
true
|
||||
|
||||
# one of these can happen on the fork block itself (it's a phase 0 block which
|
||||
# creates an Altair state)
|
||||
func verifyStateRoot(state: altair.BeaconState, blck: phase0.TrustedBeaconBlock): bool =
|
||||
# This is inlined in state_transition(...) in spec.
|
||||
true
|
||||
|
||||
func verifyStateRoot(state: phase0.BeaconState, blck: altair.TrustedBeaconBlock): bool =
|
||||
# This is inlined in state_transition(...) in spec.
|
||||
true
|
||||
|
||||
type
|
||||
RollbackProc* = proc(v: var phase0.BeaconState) {.gcsafe, raises: [Defect].}
|
||||
|
||||
|
@ -174,31 +188,106 @@ proc process_slots*(state: var SomeHashedBeaconState, slot: Slot,
|
|||
# will be incorrect
|
||||
state.root = hash_tree_root(state.data)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.6/specs/altair/fork.md#upgrading-the-state
|
||||
# says to put upgrading here too, TODO. It may not work in state reply
|
||||
# otherwise, since updateStateData() uses this function.
|
||||
|
||||
true
|
||||
|
||||
func noRollback*(state: var phase0.HashedBeaconState) =
|
||||
trace "Skipping rollback of broken state"
|
||||
|
||||
type
|
||||
BeaconStateFork* = enum
|
||||
forkPhase0,
|
||||
forkAltair
|
||||
|
||||
ForkedHashedBeaconState* = object
|
||||
case beaconStateFork*: BeaconStateFork
|
||||
of forkPhase0: hbsPhase0*: phase0.HashedBeaconState
|
||||
of forkAltair: hbsAltair*: altair.HashedBeaconState
|
||||
|
||||
# Dispatch functions
|
||||
template getStateField*(x: ForkedHashedBeaconState, y: untyped): untyped =
|
||||
case x.beaconStateFork:
|
||||
of forkPhase0: x.hbsPhase0.data.y
|
||||
of forkAltair: x.hbsAltair.data.y
|
||||
|
||||
template getStateField*(x: var ForkedHashedBeaconState, y: untyped): untyped =
|
||||
case x.beaconStateFork:
|
||||
of forkPhase0: x.hbsPhase0.data.y
|
||||
of forkAltair: x.hbsAltair.data.y
|
||||
|
||||
template getStateRoot*(x: ForkedHashedBeaconState): Eth2Digest =
|
||||
case x.beaconStateFork:
|
||||
of forkPhase0: x.hbsPhase0.root
|
||||
of forkAltair: x.hbsAltair.root
|
||||
|
||||
template hash_tree_root*(x: ForkedHashedBeaconState): Eth2Digest =
|
||||
case x.beaconStateFork:
|
||||
of forkPhase0: hash_tree_root(x.hbsPhase0.data)
|
||||
of forkAltair: hash_tree_root(x.hbsAltair.data)
|
||||
|
||||
template callWithBS*(op: untyped, y: ForkedHashedBeaconState): untyped =
|
||||
let bs {.inject.} =
|
||||
case y.beaconStateFork:
|
||||
of forkPhase0: y.hbsPhase0.data
|
||||
of forkAltair: y.hbsAltair.data
|
||||
op
|
||||
|
||||
proc maybeUpgradeStateToAltair(
|
||||
state: var ForkedHashedBeaconState, altairForkSlot: Slot) =
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.6/specs/altair/fork.md#upgrading-the-state
|
||||
|
||||
# Both state_transition_slots() and state_transition_block() call this, so
|
||||
# only run it once by checking for existing fork.
|
||||
if getStateField(state, slot) == altairForkSlot and
|
||||
state.beaconStateFork == forkPhase0:
|
||||
var newState = upgrade_to_altair(state.hbsPhase0.data)
|
||||
state = ForkedHashedBeaconState(
|
||||
beaconStateFork: forkAltair,
|
||||
hbsAltair: altair.HashedBeaconState(
|
||||
root: hash_tree_root(newState[]), data: newState[]))
|
||||
|
||||
proc state_transition_slots(
|
||||
preset: RuntimePreset,
|
||||
#state: var phase0.HashedBeaconState, signedBlock: phase0.SomeSignedBeaconBlock,
|
||||
state: var (phase0.HashedBeaconState | altair.HashedBeaconState), signedBlock: phase0.SignedBeaconBlock | phase0.SigVerifiedSignedBeaconBlock | phase0.TrustedSignedBeaconBlock | altair.SignedBeaconBlock,
|
||||
cache: var StateCache, rewards: var RewardInfo, flags: UpdateFlags): bool {.nbench.} =
|
||||
## Apply a block to the state, advancing the slot counter as necessary. The
|
||||
## given state must be of a lower slot, or, in case the `slotProcessed` flag
|
||||
## is set, can be the slot state of the same slot as the block (where the
|
||||
## slot state is the state without any block applied). To create a slot state,
|
||||
## advance the state corresponding to the the parent block using
|
||||
## `process_slots`.
|
||||
##
|
||||
## To run the state transition function in preparation for block production,
|
||||
## use `makeBeaconBlock` instead.
|
||||
##
|
||||
## `rollback` is called if the transition fails and the given state has been
|
||||
## partially changed. If a temporary state was given to `state_transition`,
|
||||
## it is safe to use `noRollback` and leave it broken, else the state
|
||||
## object should be rolled back to a consistent state. If the transition fails
|
||||
## before the state has been updated, `rollback` will not be called.
|
||||
state: var ForkedHashedBeaconState,
|
||||
signedBlock: phase0.SignedBeaconBlock | phase0.SigVerifiedSignedBeaconBlock | phase0.TrustedSignedBeaconBlock | altair.SignedBeaconBlock,
|
||||
cache: var StateCache, rewards: var RewardInfo, flags: UpdateFlags,
|
||||
altairForkSlot: Slot): bool {.nbench.} =
|
||||
let slot = signedBlock.message.slot
|
||||
|
||||
# Update the state so its slot matches that of the block
|
||||
while getStateField(state, slot) < slot:
|
||||
case state.beaconStateFork:
|
||||
of forkPhase0:
|
||||
advance_slot(
|
||||
state.hbsPhase0.data, state.hbsPhase0.root, flags, cache, rewards)
|
||||
|
||||
if state.hbsPhase0.data.slot < slot:
|
||||
# Don't update state root for the slot of the block
|
||||
state.hbsPhase0.root = hash_tree_root(state.hbsPhase0.data)
|
||||
of forkAltair:
|
||||
advance_slot(
|
||||
state.hbsAltair.data, state.hbsAltair.root, flags, cache, rewards)
|
||||
|
||||
if getStateField(state, slot) < slot:
|
||||
# Don't update state root for the slot of the block
|
||||
state.hbsAltair.root = hash_tree_root(state.hbsAltair.data)
|
||||
|
||||
maybeUpgradeStateToAltair(state, altairForkSlot)
|
||||
|
||||
true
|
||||
|
||||
proc state_transition_slots(
|
||||
preset: RuntimePreset,
|
||||
state: var SomeHashedBeaconState,
|
||||
signedBlock: phase0.SignedBeaconBlock | phase0.SigVerifiedSignedBeaconBlock |
|
||||
phase0.TrustedSignedBeaconBlock | altair.SignedBeaconBlock,
|
||||
cache: var StateCache, rewards: var RewardInfo, flags: UpdateFlags):
|
||||
bool {.nbench.} =
|
||||
# TODO remove when the HashedBeaconState state_transition is removed; it's
|
||||
# to avoid requiring a wrapped/memory-copied version
|
||||
let slot = signedBlock.message.slot
|
||||
if not (state.data.slot < slot):
|
||||
if slotProcessed notin flags or state.data.slot != slot:
|
||||
|
@ -220,10 +309,17 @@ proc state_transition_slots(
|
|||
|
||||
proc state_transition_block*(
|
||||
preset: RuntimePreset,
|
||||
#state: var phase0.HashedBeaconState, signedBlock: phase0.SomeSignedBeaconBlock,
|
||||
state: var (phase0.HashedBeaconState | altair.HashedBeaconState), signedBlock: phase0.SignedBeaconBlock | phase0.SigVerifiedSignedBeaconBlock | phase0.TrustedSignedBeaconBlock | altair.SignedBeaconBlock,
|
||||
cache: var StateCache, rewards: var RewardInfo, flags: UpdateFlags,
|
||||
rollback: RollbackHashedProc): bool {.nbench.} =
|
||||
state: var SomeHashedBeaconState,
|
||||
signedBlock: phase0.SignedBeaconBlock | phase0.SigVerifiedSignedBeaconBlock |
|
||||
phase0.TrustedSignedBeaconBlock | altair.SignedBeaconBlock,
|
||||
cache: var StateCache, flags: UpdateFlags, rollback: RollbackHashedProc):
|
||||
bool {.nbench.} =
|
||||
## `rollback` is called if the transition fails and the given state has been
|
||||
## partially changed. If a temporary state was given to `state_transition`,
|
||||
## it is safe to use `noRollback` and leave it broken, else the state
|
||||
## object should be rolled back to a consistent state. If the transition fails
|
||||
## before the state has been updated, `rollback` will not be called.
|
||||
|
||||
# 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
|
||||
|
@ -270,15 +366,92 @@ proc state_transition_block*(
|
|||
|
||||
true
|
||||
|
||||
proc state_transition_block*(
|
||||
preset: RuntimePreset,
|
||||
state: var ForkedHashedBeaconState,
|
||||
signedBlock: phase0.SignedBeaconBlock | phase0.SigVerifiedSignedBeaconBlock |
|
||||
phase0.TrustedSignedBeaconBlock |
|
||||
altair.SignedBeaconBlock | altair.SigVerifiedSignedBeaconBlock,
|
||||
cache: var StateCache, flags: UpdateFlags,
|
||||
rollback: RollbackHashedProc, altairForkSlot: Slot): bool {.nbench.} =
|
||||
## `rollback` is called if the transition fails and the given state has been
|
||||
## partially changed. If a temporary state was given to `state_transition`,
|
||||
## it is safe to use `noRollback` and leave it broken, else the state
|
||||
## object should be rolled back to a consistent state. If the transition fails
|
||||
## before the state has been updated, `rollback` will not be called.
|
||||
|
||||
# Ensure state_transition_block()-only callers trigger this
|
||||
maybeUpgradeStateToAltair(state, altairForkSlot)
|
||||
|
||||
case state.beaconStateFork:
|
||||
of forkPhase0: state_transition_block(
|
||||
preset, state.hbsPhase0, signedBlock, cache, flags, rollback)
|
||||
of forkAltair: state_transition_block(
|
||||
preset, state.hbsAltair, signedBlock, cache, flags, rollback)
|
||||
|
||||
proc state_transition*(
|
||||
preset: RuntimePreset,
|
||||
#state: var phase0.HashedBeaconState, signedBlock: phase0.SomeSignedBeaconBlock,
|
||||
state: var (phase0.HashedBeaconState | altair.HashedBeaconState), signedBlock: phase0.SignedBeaconBlock | phase0.SigVerifiedSignedBeaconBlock | phase0.TrustedSignedBeaconBlock | altair.SignedBeaconBlock,
|
||||
state: var ForkedHashedBeaconState,
|
||||
signedBlock: phase0.SignedBeaconBlock | phase0.SigVerifiedSignedBeaconBlock |
|
||||
phase0.TrustedSignedBeaconBlock | altair.SignedBeaconBlock,
|
||||
cache: var StateCache, rewards: var RewardInfo, flags: UpdateFlags,
|
||||
rollback: RollbackHashedProc): bool {.nbench.} =
|
||||
if not state_transition_slots(preset, state, signedBlock, cache, rewards, flags):
|
||||
rollback: RollbackHashedProc,
|
||||
altairForkEpoch: Epoch = FAR_FUTURE_EPOCH): bool {.nbench.} =
|
||||
## Apply a block to the state, advancing the slot counter as necessary. The
|
||||
## given state must be of a lower slot, or, in case the `slotProcessed` flag
|
||||
## is set, can be the slot state of the same slot as the block (where the
|
||||
## slot state is the state without any block applied). To create a slot state,
|
||||
## advance the state corresponding to the the parent block using
|
||||
## `process_slots`.
|
||||
##
|
||||
## To run the state transition function in preparation for block production,
|
||||
## use `makeBeaconBlock` instead.
|
||||
##
|
||||
## `rollback` is called if the transition fails and the given state has been
|
||||
## partially changed. If a temporary state was given to `state_transition`,
|
||||
## it is safe to use `noRollback` and leave it broken, else the state
|
||||
## object should be rolled back to a consistent state. If the transition fails
|
||||
## before the state has been updated, `rollback` will not be called.
|
||||
let slot = signedBlock.message.slot
|
||||
if not (getStateField(state, slot) < slot):
|
||||
if slotProcessed notin flags or getStateField(state, slot) != slot:
|
||||
notice "State must precede block",
|
||||
state_root = shortLog(getStateRoot(state)),
|
||||
current_slot = getStateField(state, slot),
|
||||
blck = shortLog(signedBlock)
|
||||
return false
|
||||
state_transition_block(preset, state, signedBlock, cache, rewards, flags, rollback)
|
||||
|
||||
if not state_transition_slots(
|
||||
preset, state, signedBlock, cache, rewards, flags,
|
||||
altairForkEpoch.compute_start_slot_at_epoch):
|
||||
return false
|
||||
state_transition_block(
|
||||
preset, state, signedBlock, cache, flags, rollback,
|
||||
altairForkEpoch.compute_start_slot_at_epoch)
|
||||
|
||||
proc state_transition*(
|
||||
preset: RuntimePreset,
|
||||
state: var SomeHashedBeaconState,
|
||||
signedBlock: phase0.SignedBeaconBlock | phase0.SigVerifiedSignedBeaconBlock |
|
||||
phase0.TrustedSignedBeaconBlock | altair.SignedBeaconBlock,
|
||||
cache: var StateCache, rewards: var RewardInfo, flags: UpdateFlags,
|
||||
rollback: RollbackHashedProc): bool =
|
||||
# Does not follow hard forks; suitable only where that's irrelevant.
|
||||
# TODO remove when callers gone
|
||||
let slot = signedBlock.message.slot
|
||||
if not (state.data.slot < slot):
|
||||
if slotProcessed notin flags or state.data.slot != slot:
|
||||
notice "State must precede block",
|
||||
state_root = shortLog(state.root),
|
||||
current_slot = state.data.slot,
|
||||
blck = shortLog(signedBlock)
|
||||
return false
|
||||
|
||||
if not state_transition_slots(
|
||||
preset, state, signedBlock, cache, rewards, flags):
|
||||
return false
|
||||
state_transition_block(
|
||||
preset, state, signedBlock, cache, flags, rollback)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/validator.md#preparing-for-a-beaconblock
|
||||
proc makeBeaconBlock*(
|
||||
|
|
|
@ -412,6 +412,13 @@ proc process_block*(
|
|||
|
||||
ok()
|
||||
|
||||
proc process_block*(
|
||||
preset: RuntimePreset,
|
||||
state: var altair.BeaconState, blck: SomePhase0Block, flags: UpdateFlags,
|
||||
cache: var StateCache): Result[void, cstring] {.nbench.} =
|
||||
# The transition-triggering block creates, not acts on, an Altair state
|
||||
err("process_block: Altair state with Phase 0 block")
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.6/specs/altair/beacon-chain.md#block-processing
|
||||
# TODO workaround for https://github.com/nim-lang/Nim/issues/18095
|
||||
# copy of datatypes/altair.nim
|
||||
|
@ -432,3 +439,9 @@ proc process_block*(
|
|||
? process_sync_committee(state, blck.body.sync_aggregate, cache) # [New in Altair]
|
||||
|
||||
ok()
|
||||
|
||||
proc process_block*(
|
||||
preset: RuntimePreset,
|
||||
state: var phase0.BeaconState, blck: SomeAltairBlock, flags: UpdateFlags,
|
||||
cache: var StateCache): Result[void, cstring] {.nbench.}=
|
||||
err("process_block: Phase 0 state with Altair block")
|
||||
|
|
|
@ -208,7 +208,7 @@ proc cmdBench(conf: DbConf, runtimePreset: RuntimePreset) =
|
|||
if conf.resetCache:
|
||||
cache = StateCache()
|
||||
if not state_transition_block(
|
||||
runtimePreset, state[].data, b, cache, rewards, {}, noRollback):
|
||||
runtimePreset, state[].data, b, cache, {}, noRollback):
|
||||
dump("./", b)
|
||||
echo "State transition failed (!)"
|
||||
quit 1
|
||||
|
@ -530,7 +530,7 @@ proc cmdValidatorPerf(conf: DbConf, runtimePreset: RuntimePreset) =
|
|||
processEpoch()
|
||||
|
||||
if not state_transition_block(
|
||||
runtimePreset, state[].data, blck, cache, rewards, {}, noRollback):
|
||||
runtimePreset, state[].data, blck, cache, {}, noRollback):
|
||||
echo "State transition failed (!)"
|
||||
quit 1
|
||||
|
||||
|
@ -757,7 +757,7 @@ proc cmdValidatorDb(conf: DbConf, runtimePreset: RuntimePreset) =
|
|||
processEpoch()
|
||||
|
||||
if not state_transition_block(
|
||||
runtimePreset, state[].data, blck, cache, rewards, {}, noRollback):
|
||||
runtimePreset, state[].data, blck, cache, {}, noRollback):
|
||||
echo "State transition failed (!)"
|
||||
quit 1
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ import # Unit test
|
|||
./test_interop,
|
||||
./test_peer_pool,
|
||||
./test_ssz,
|
||||
./test_state_transition,
|
||||
./test_statediff,
|
||||
./test_sync_manager,
|
||||
./test_zero_signature,
|
||||
|
|
|
@ -23,4 +23,5 @@ import
|
|||
./test_fixture_operations_proposer_slashings,
|
||||
./test_fixture_operations_sync_committee,
|
||||
./test_fixture_operations_voluntary_exit,
|
||||
./test_fixture_fork
|
||||
./test_fixture_fork,
|
||||
./test_fixture_transition
|
||||
|
|
|
@ -36,7 +36,7 @@ proc runTest(identifier: string) =
|
|||
parseTest(testDir/"pre.ssz_snappy", SSZ, phase0.BeaconState))
|
||||
postState = newClone(
|
||||
parseTest(testDir/"post.ssz_snappy", SSZ, altair.BeaconState))
|
||||
upgradedState = newClone(upgrade_to_altair(preState[]))
|
||||
upgradedState = upgrade_to_altair(preState[])
|
||||
check: upgradedState[].hash_tree_root() == postState[].hash_tree_root()
|
||||
reportDiff(upgradedState, postState)
|
||||
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
# beacon_chain
|
||||
# Copyright (c) 2021-Present 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).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
{.used.}
|
||||
|
||||
import
|
||||
chronicles, yaml,
|
||||
# Standard library
|
||||
os, sequtils,
|
||||
# Status internal
|
||||
faststreams, streams,
|
||||
# Beacon chain internals
|
||||
../../../beacon_chain/spec/[crypto, state_transition, presets],
|
||||
../../../beacon_chain/spec/datatypes/[phase0, altair],
|
||||
../../../beacon_chain/[extras, ssz],
|
||||
# Test utilities
|
||||
../../testutil,
|
||||
../fixtures_utils
|
||||
|
||||
const
|
||||
TransitionDir = SszTestsDir/const_preset/"altair"/"transition"/"core"/"pyspec_tests"
|
||||
|
||||
type
|
||||
TransitionEpoch = object
|
||||
post_fork: string
|
||||
fork_epoch: int
|
||||
blocks_count: int
|
||||
fork_block {.defaultVal: 0.}: int
|
||||
|
||||
proc runTest(testName, testDir, unitTestName: string) =
|
||||
# We wrap the tests in a proc to avoid running out of globals
|
||||
# in the future: Nim supports up to 3500 globals
|
||||
# but unittest with the macro/templates put everything as globals
|
||||
# https://github.com/nim-lang/Nim/issues/12084#issue-486866402
|
||||
|
||||
let testPath = testDir / unitTestName
|
||||
|
||||
var transitionEpoch: TransitionEpoch
|
||||
var s = openFileStream(testPath/"meta.yaml")
|
||||
defer: close(s)
|
||||
yaml.load(s, transitionEpoch)
|
||||
|
||||
proc `testImpl _ blck _ testName`() =
|
||||
test testName & " - " & unitTestName & preset():
|
||||
var
|
||||
preState = newClone(parseTest(testPath/"pre.ssz_snappy", SSZ, phase0.BeaconState))
|
||||
sdPreState = (ref ForkedHashedBeaconState)(hbsPhase0: phase0.HashedBeaconState(
|
||||
data: preState[], root: hash_tree_root(preState[])), beaconStateFork: forkPhase0)
|
||||
cache = StateCache()
|
||||
rewards = RewardInfo()
|
||||
|
||||
# In test cases with more than 10 blocks the first 10 aren't 0-prefixed,
|
||||
# so purely lexicographic sorting wouldn't sort properly.
|
||||
let numBlocks = toSeq(walkPattern(testPath/"blocks_*.ssz_snappy")).len
|
||||
for i in 0 ..< numBlocks:
|
||||
let inBeforeTimes = i <= transitionEpoch.fork_block and transitionEpoch.fork_block > 0
|
||||
if inBeforeTimes:
|
||||
let blck = parseTest(testPath/"blocks_" & $i & ".ssz_snappy", SSZ, phase0.SignedBeaconBlock)
|
||||
|
||||
let success = state_transition(
|
||||
defaultRuntimePreset, sdPreState[], blck,
|
||||
cache, rewards,
|
||||
flags = {skipStateRootValidation}, noRollback,
|
||||
transitionEpoch.fork_epoch.Epoch)
|
||||
doAssert success, "Failure when applying block " & $i
|
||||
else:
|
||||
let blck = parseTest(testPath/"blocks_" & $i & ".ssz_snappy", SSZ, altair.SignedBeaconBlock)
|
||||
|
||||
let success = state_transition(
|
||||
defaultRuntimePreset, sdPreState[], blck,
|
||||
cache, rewards,
|
||||
flags = {skipStateRootValidation}, noRollback,
|
||||
transitionEpoch.fork_epoch.Epoch)
|
||||
doAssert success, "Failure when applying block " & $i
|
||||
|
||||
let postState = newClone(parseTest(testPath/"post.ssz_snappy", SSZ, altair.BeaconState))
|
||||
when false:
|
||||
reportDiff(sdPreState.data, postState)
|
||||
doAssert getStateRoot(sdPreState[]) == postState[].hash_tree_root()
|
||||
|
||||
`testImpl _ blck _ testName`()
|
||||
|
||||
suite "Official - Altair - Transition " & preset():
|
||||
for kind, path in walkDir(TransitionDir, true):
|
||||
runTest("Official - Altair - Transition", TransitionDir, path)
|
|
@ -1,131 +0,0 @@
|
|||
# beacon_chain
|
||||
# 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).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
{.used.}
|
||||
|
||||
import
|
||||
chronicles,
|
||||
unittest2,
|
||||
./testutil, ./testblockutil,
|
||||
../beacon_chain/spec/[beaconstate, datatypes, digest, crypto,
|
||||
validator, state_transition, presets],
|
||||
../beacon_chain/ssz
|
||||
|
||||
proc makeAttestation(
|
||||
state: BeaconState, beacon_block_root: Eth2Digest,
|
||||
validator_index: ValidatorIndex, cache: var StateCache): Attestation =
|
||||
# The called functions don't use the extra-BeaconState parts of StateData.
|
||||
let
|
||||
stateData = (ref StateData)(
|
||||
data: HashedBeaconState(data: state),
|
||||
blck: BlockRef(root: beacon_block_root, slot: state.slot))
|
||||
(committee, slot, index) =
|
||||
find_beacon_committee(stateData[], validator_index, cache)
|
||||
makeAttestation(stateData[], beacon_block_root, committee, slot, index,
|
||||
validator_index, cache)
|
||||
|
||||
suite "Block processing" & preset():
|
||||
## For now just test that we can compile and execute block processing with
|
||||
## mock data.
|
||||
|
||||
let
|
||||
# Genesis state with minimal number of deposits
|
||||
# TODO bls verification is a bit of a bottleneck here
|
||||
genesisState = newClone(initialize_hashed_beacon_state_from_eth1(
|
||||
defaultRuntimePreset, Eth2Digest(), 0, makeInitialDeposits(), {}))
|
||||
genesisBlock = get_initial_beacon_block(genesisState.data)
|
||||
genesisRoot = hash_tree_root(genesisBlock.message)
|
||||
|
||||
setup:
|
||||
var
|
||||
state = newClone(genesisState[])
|
||||
cache = StateCache()
|
||||
rewards = RewardInfo()
|
||||
|
||||
test "Passes from genesis state, no block" & preset():
|
||||
check:
|
||||
process_slots(state[], state.data.slot + 1, cache, rewards)
|
||||
state.data.slot == genesisState.data.slot + 1
|
||||
|
||||
test "Passes from genesis state, empty block" & preset():
|
||||
var
|
||||
previous_block_root = genesisBlock.root
|
||||
new_block = makeTestBlock(state[], previous_block_root, cache)
|
||||
|
||||
let block_ok = state_transition(
|
||||
defaultRuntimePreset, state[], new_block, cache, rewards, {}, noRollback)
|
||||
|
||||
check:
|
||||
block_ok
|
||||
|
||||
state.data.slot == genesisState.data.slot + 1
|
||||
|
||||
test "Passes through epoch update, no block" & preset():
|
||||
check:
|
||||
process_slots(state[], Slot(SLOTS_PER_EPOCH), cache, rewards)
|
||||
state.data.slot == genesisState.data.slot + SLOTS_PER_EPOCH
|
||||
|
||||
test "Passes through epoch update, empty block" & preset():
|
||||
var
|
||||
previous_block_root = genesisRoot
|
||||
|
||||
for i in 1..SLOTS_PER_EPOCH:
|
||||
let new_block = makeTestBlock(state[], previous_block_root, cache)
|
||||
|
||||
let block_ok = state_transition(
|
||||
defaultRuntimePreset, state[], new_block, cache, rewards, {}, noRollback)
|
||||
|
||||
check:
|
||||
block_ok
|
||||
|
||||
previous_block_root = new_block.root
|
||||
|
||||
check:
|
||||
state.data.slot == genesisState.data.slot + SLOTS_PER_EPOCH
|
||||
|
||||
test "Attestation gets processed at epoch" & preset():
|
||||
var
|
||||
previous_block_root = genesisRoot
|
||||
|
||||
# Slot 0 is a finalized slot - won't be making attestations for it..
|
||||
check:
|
||||
process_slots(state[], state.data.slot + 1, cache, rewards)
|
||||
|
||||
let
|
||||
# Create an attestation for slot 1 signed by the only attester we have!
|
||||
beacon_committee =
|
||||
get_beacon_committee(state.data, state.data.slot, 0.CommitteeIndex, cache)
|
||||
attestation = makeAttestation(
|
||||
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
|
||||
check:
|
||||
process_slots(
|
||||
state[], GENESIS_SLOT + MIN_ATTESTATION_INCLUSION_DELAY + 1, cache,
|
||||
rewards)
|
||||
|
||||
let
|
||||
new_block = makeTestBlock(state[], previous_block_root, cache,
|
||||
attestations = @[attestation]
|
||||
)
|
||||
check state_transition(
|
||||
defaultRuntimePreset, state[], new_block, cache, rewards, {}, noRollback)
|
||||
|
||||
check:
|
||||
# TODO epoch attestations can get multiplied now; clean up paths to
|
||||
# enable exact 1-check again and keep finalization.
|
||||
state.data.current_epoch_attestations.len >= 1
|
||||
|
||||
check:
|
||||
process_slots(state[], Slot(191), cache, rewards)
|
||||
|
||||
# Would need to process more epochs for the attestation to be removed from
|
||||
# the state! (per above bug)
|
||||
#
|
||||
# check:
|
||||
# state.latest_attestations.len == 0
|
Loading…
Reference in New Issue