split state_transition() into slots/block parts and use only block where appropriate (#2630)

This commit is contained in:
tersec 2021-06-03 09:42:25 +00:00 committed by GitHub
parent 141c39cfaa
commit 28a5bca71a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 59 additions and 75 deletions

View File

@ -60,15 +60,7 @@ template asTrusted(x: SignedBeaconBlock or SigVerifiedBeaconBlock): TrustedSigne
cast[ptr TrustedSignedBeaconBlock](signedBlock.unsafeAddr)[]
func getOrResolve*(dag: ChainDAGRef, quarantine: QuarantineRef, root: Eth2Digest): BlockRef =
## Fetch a block ref, or nil if not found (will be added to list of
## blocks-to-resolve)
result = dag.getRef(root)
if result.isNil:
quarantine.addMissing(root)
proc batchVerify(quarantine: QuarantineRef, sigs: openArray[SignatureSet]): bool =
func batchVerify(quarantine: QuarantineRef, sigs: openArray[SignatureSet]): bool =
var secureRandomBytes: array[32, byte]
quarantine.rng[].brHmacDrbgGenerate(secureRandomBytes)
@ -181,8 +173,9 @@ proc checkStateTransition(
blockRoot = shortLog(signedBlock.root)
var rewards: RewardInfo
if not state_transition(dag.runtimePreset, dag.clearanceState.data, signedBlock,
cache, rewards, dag.updateFlags + {slotProcessed}, restore):
if not state_transition_block(
dag.runtimePreset, dag.clearanceState.data, signedBlock,
cache, rewards, dag.updateFlags, restore):
info "Invalid block"
return (ValidationResult.Reject, Invalid)
@ -323,7 +316,7 @@ proc addRawBlockUnresolved(
return err((ValidationResult.Ignore, MissingParent))
proc addRawBlock*(
proc addRawBlock(
dag: ChainDAGRef, quarantine: QuarantineRef,
signedBlock: SignedBeaconBlock,
onBlockAdded: OnBlockAdded

View File

@ -13,12 +13,13 @@ import
metrics, snappy, chronicles,
../ssz/[ssz_serialization, merkleization], ../beacon_chain_db, ../extras,
../spec/[
crypto, datatypes, digest, helpers, validator, state_transition,
crypto, digest, helpers, validator, state_transition,
beaconstate],
../spec/datatypes/[phase0, altair],
../beacon_clock,
"."/[block_pools_types, block_quarantine, statedata_helpers]
export block_pools_types, helpers, datatypes
export block_pools_types, helpers, phase0
# https://github.com/ethereum/eth2.0-metrics/blob/master/metrics.md#interop-metrics
declareGauge beacon_head_root, "Root of the head block of the beacon chain"
@ -44,7 +45,7 @@ declareGauge beacon_processed_deposits_total, "Number of total deposits included
logScope: topics = "chaindag"
proc putBlock*(
dag: ChainDAGRef, signedBlock: TrustedSignedBeaconBlock) =
dag: ChainDAGRef, signedBlock: phase0.TrustedSignedBeaconBlock) =
dag.db.putBlock(signedBlock)
proc updateStateData*(
@ -233,10 +234,6 @@ func atEpochStart*(blck: BlockRef, epoch: Epoch): BlockSlot =
## Return the BlockSlot corresponding to the first slot in the given epoch
atSlot(blck, epoch.compute_start_slot_at_epoch)
func atEpochEnd(blck: BlockRef, epoch: Epoch): BlockSlot =
## Return the BlockSlot corresponding to the last slot in the given epoch
atSlot(blck, (epoch + 1).compute_start_slot_at_epoch - 1)
func epochAncestor*(blck: BlockRef, epoch: Epoch): BlockSlot =
## The state transition works by storing information from blocks in a
## "working" area until the epoch transition, then batching work collected
@ -291,7 +288,8 @@ func init(T: type BlockRef, root: Eth2Digest, slot: Slot): BlockRef =
slot: slot
)
func init*(T: type BlockRef, root: Eth2Digest, blck: SomeBeaconBlock): BlockRef =
func init*(T: type BlockRef, root: Eth2Digest, blck: SomeSomeBeaconBlock):
BlockRef =
BlockRef.init(root, blck.slot)
func contains*(dag: ChainDAGRef, root: Eth2Digest): bool =
@ -506,7 +504,7 @@ proc getState(
else:
unsafeAddr dag.headState
func restore(v: var BeaconState) =
func restore(v: var phase0.BeaconState) =
assign(v, restoreAddr[].data.data)
if not dag.db.getState(stateRoot, state.data.data, restore):
@ -681,7 +679,7 @@ proc applyBlock(
doAssert state.blck == blck.refs.parent
var statePtr = unsafeAddr state # safe because `restore` is locally scoped
func restore(v: var HashedBeaconState) =
func restore(v: var phase0.HashedBeaconState) =
doAssert (addr(statePtr.data) == addr v)
statePtr[] = dag.headState
@ -1095,7 +1093,7 @@ proc isInitialized*(T: type ChainDAGRef, db: BeaconChainDB): bool =
proc preInit*(
T: type ChainDAGRef, db: BeaconChainDB,
genesisState, tailState: var BeaconState, tailBlock: TrustedSignedBeaconBlock) =
genesisState, tailState: var phase0.BeaconState, tailBlock: phase0.TrustedSignedBeaconBlock) =
# write a genesis state, the way the ChainDAGRef expects it to be stored in
# database
# TODO probably should just init a block pool with the freshly written
@ -1125,8 +1123,8 @@ proc preInit*(
db.putGenesisBlockRoot(genesisBlock.root)
func setTailState*(dag: ChainDAGRef,
checkpointState: BeaconState,
checkpointBlock: TrustedSignedBeaconBlock) =
checkpointState: phase0.BeaconState,
checkpointBlock: phase0.TrustedSignedBeaconBlock) =
# TODO(zah)
# Delete all records up to the tail node. If the tail node is not
# in the database, init the dabase in a way similar to `preInit`.

View File

@ -53,7 +53,7 @@ import
type Foo = phase0.SomeSignedBeaconBlock | altair.SomeSignedBeaconBlock | phase0.SignedBeaconBlock | altair.SignedBeaconBlock | phase0.TrustedSignedBeaconBlock | altair.TrustedSignedBeaconBlock | phase0.SigVerifiedSignedBeaconBlock | altair.SigVerifiedSignedBeaconBlock
# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#beacon-chain-state-transition-function
proc verify_block_signature*(
proc verify_block_signature(
#state: SomeBeaconState, signed_block: SomeSomeSignedBeaconBlock): bool {.nbench.} =
state: SomeBeaconState, signed_block: Foo): bool {.nbench.} =
#state: SomeBeaconState, signed_block: phase0.SomeSignedBeaconBlock | altair.SomeSignedBeaconBlock): bool {.nbench.} =
@ -85,14 +85,14 @@ proc verifyStateRoot(state: SomeBeaconState, blck: phase0.BeaconBlock or phase0.
else:
true
proc verifyStateRoot(state: phase0.BeaconState, blck: phase0.TrustedBeaconBlock): bool =
func verifyStateRoot(state: phase0.BeaconState, blck: phase0.TrustedBeaconBlock): bool =
# This is inlined in state_transition(...) in spec.
true
type
RollbackProc* = proc(v: var phase0.BeaconState) {.gcsafe, raises: [Defect].}
proc noRollback*(state: var phase0.BeaconState) =
func noRollback*(state: var phase0.BeaconState) =
trace "Skipping rollback of broken state"
type
@ -176,16 +176,14 @@ proc process_slots*(state: var SomeHashedBeaconState, slot: Slot,
true
proc noRollback*(state: var phase0.HashedBeaconState) =
func noRollback*(state: var phase0.HashedBeaconState) =
trace "Skipping rollback of broken state"
proc state_transition*(
proc state_transition_slots(
preset: RuntimePreset,
# TODO this will be StateData
#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.} =
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
@ -201,8 +199,6 @@ proc 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.
doAssert not rollback.isNil, "use noRollback if it's ok to mess up state"
let slot = signedBlock.message.slot
if not (state.data.slot < slot):
if slotProcessed notin flags or state.data.slot != slot:
@ -220,10 +216,20 @@ proc state_transition*(
# Don't update state root for the slot of the block
state.root = hash_tree_root(state.data)
true
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.} =
# Block updates - these happen when there's a new block being suggested
# by the block proposer. Every actor in the network will update its state
# according to the contents of this block - but first they will validate
# that the block is sane.
doAssert not rollback.isNil, "use noRollback if it's ok to mess up state"
if not (skipBLSValidation in flags or
verify_block_signature(state.data, signedBlock)):
when not (state is altair.HashedBeaconState):
@ -264,6 +270,16 @@ proc state_transition*(
true
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,
cache: var StateCache, rewards: var RewardInfo, flags: UpdateFlags,
rollback: RollbackHashedProc): bool {.nbench.} =
if not state_transition_slots(preset, state, signedBlock, cache, rewards, flags):
return false
state_transition_block(preset, state, signedBlock, cache, rewards, flags, rollback)
# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/validator.md#preparing-for-a-beaconblock
proc makeBeaconBlock*(
preset: RuntimePreset,

View File

@ -177,7 +177,7 @@ proc process_proposer_slashing*(
ok()
# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#is_slashable_attestation_data
func is_slashable_attestation_data*(
func is_slashable_attestation_data(
data_1: AttestationData, data_2: AttestationData): bool =
## Check if ``data_1`` and ``data_2`` are slashable according to Casper FFG
## rules.

View File

@ -667,7 +667,7 @@ func process_rewards_and_penalties(
decrease_balance(state.balances.asSeq()[idx], v.delta.penalties)
# https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.6/specs/altair/beacon-chain.md#rewards-and-penalties
proc process_rewards_and_penalties(
func process_rewards_and_penalties(
state: var altair.BeaconState, total_active_balance: Gwei) {.nbench.} =
if get_current_epoch(state) == GENESIS_EPOCH:
return
@ -828,17 +828,6 @@ func process_inactivity_updates*(state: var altair.BeaconState) =
if not is_in_inactivity_leak(state):
state.inactivity_scores[index] -= min(INACTIVITY_SCORE_RECOVERY_RATE.uint64, state.inactivity_scores[index])
# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#final-updates
func process_final_updates*(state: var phase0.BeaconState) {.nbench.} =
# This function's a wrapper over the HF1 split/refactored HF1 version. TODO
# remove once test vectors become available for each HF1 function.
process_eth1_data_reset(state)
process_effective_balance_updates(state)
process_slashings_reset(state)
process_randao_mixes_reset(state)
process_historical_roots_update(state)
process_participation_record_updates(state)
# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#epoch-processing
proc process_epoch*(
state: var phase0.BeaconState, flags: UpdateFlags, cache: var StateCache,
@ -873,7 +862,12 @@ proc process_epoch*(
process_slashings(state, rewards.total_balances.current_epoch)
# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#final-updates
process_final_updates(state)
process_eth1_data_reset(state)
process_effective_balance_updates(state)
process_slashings_reset(state)
process_randao_mixes_reset(state)
process_historical_roots_update(state)
process_participation_record_updates(state)
# https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.6/specs/altair/beacon-chain.md#epoch-processing
proc process_epoch*(

View File

@ -41,9 +41,6 @@ build/nbench cmdEpochProcessing --epochProcessingCat=catRegistryUpdates -d="${SC
# Slashings
build/nbench cmdEpochProcessing --epochProcessingCat=catSlashings -d="${SCENARIOS}"/epoch_processing/slashings/pyspec_tests/max_penalties/
# Final updates
build/nbench cmdEpochProcessing --epochProcessingCat=catFinalUpdates -d="${SCENARIOS}"/epoch_processing/final_updates/pyspec_tests/effective_balance_hysteresis/
# Block header processing
build/nbench cmdBlockProcessing --blockProcessingCat=catBlockHeader -d="${SCENARIOS}"/operations/block_header/pyspec_tests/proposer_slashed/

View File

@ -1,5 +1,5 @@
# beacon_chain
# Copyright (c) 2018 Status Research & Development GmbH
# Copyright (c) 2018-2021 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).
@ -117,11 +117,6 @@ proc main() =
scenario.scenarioDir.string,
scenario.preState
)
of catFinalUpdates:
runProcessFinalUpdates(
scenario.scenarioDir.string,
scenario.preState
)
else:
quit "Unsupported"

View File

@ -1,5 +1,5 @@
# beacon_chain
# Copyright (c) 2018 Status Research & Development GmbH
# Copyright (c) 2018-2021 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).
@ -62,9 +62,6 @@ proc collectBenchTargets(nbench, basePath: string): CmdLists =
block: # Slashings
let path = basePath/"phase0"/"epoch_processing"/"slashings"/"pyspec_tests"
result.collectTarget(nbench, "slashings", "cmdEpochProcessing", "catSlashings", path)
block: # Justification-Finalization
let path = basePath/"phase0"/"epoch_processing"/"final_updates"/"pyspec_tests"
result.collectTarget(nbench, "final_updates", "cmdEpochProcessing", "catFinalUpdates", path)
# Block processing
# -------------------------------------------------------------------------
block: # Attestation

View File

@ -1,5 +1,5 @@
# beacon_chain
# Copyright (c) 2018-2020 Status Research & Development GmbH
# Copyright (c) 2018-2021 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).
@ -40,7 +40,6 @@ type
catVoluntaryExits
EpochProcessingCat* = enum
catFinalUpdates
catJustificationFinalization
catRegistryUpdates
catSlashings
@ -270,10 +269,6 @@ genProcessEpochScenario(runProcessSlashings,
process_slashings,
needCache = false)
genProcessEpochScenario(runProcessFinalUpdates,
process_final_updates,
needCache = false)
genProcessBlockScenario(runProcessBlockHeader,
process_block_header,
block_header,

View File

@ -207,8 +207,8 @@ proc cmdBench(conf: DbConf, runtimePreset: RuntimePreset) =
withTimer(timers[tApplyBlock]):
if conf.resetCache:
cache = StateCache()
if not state_transition(
runtimePreset, state[].data, b, cache, rewards, {slotProcessed}, noRollback):
if not state_transition_block(
runtimePreset, state[].data, b, cache, rewards, {}, noRollback):
dump("./", b)
echo "State transition failed (!)"
quit 1
@ -529,8 +529,8 @@ proc cmdValidatorPerf(conf: DbConf, runtimePreset: RuntimePreset) =
if getStateField(state[], slot).isEpoch():
processEpoch()
if not state_transition(
runtimePreset, state[].data, blck, cache, rewards, {slotProcessed}, noRollback):
if not state_transition_block(
runtimePreset, state[].data, blck, cache, rewards, {}, noRollback):
echo "State transition failed (!)"
quit 1
@ -756,8 +756,8 @@ proc cmdValidatorDb(conf: DbConf, runtimePreset: RuntimePreset) =
if getStateField(state[], slot).isEpoch():
processEpoch()
if not state_transition(
runtimePreset, state[].data, blck, cache, rewards, {slotProcessed}, noRollback):
if not state_transition_block(
runtimePreset, state[].data, blck, cache, rewards, {}, noRollback):
echo "State transition failed (!)"
quit 1

View File

@ -43,7 +43,6 @@ template withTimerRet*(stats: var RunningStat, body: untyped): untyped =
var testTimes: seq[TestDuration]
var status = initOrderedTable[string, OrderedTable[string, Status]]()
var last: string
type TimingCollector = ref object of OutputFormatter