more abstraction over BeaconState (#2509)

* more abstraction over BeaconState

* use HashedBeaconState copy of htr
This commit is contained in:
tersec 2021-04-16 08:49:37 +00:00 committed by GitHub
parent 85acf55ad8
commit 99fccaee6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 117 additions and 60 deletions

View File

@ -0,0 +1,24 @@
# beacon_chain
# Copyright (c) 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).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
{.push raises: [Defect].}
import
../spec/[datatypes, digest, helpers, presets],
./block_pools_types, ./blockchain_dag
# State-related functions implemented based on StateData instead of BeaconState
# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_current_epoch
func get_current_epoch*(stateData: StateData): Epoch =
## Return the current epoch.
getStateField(stateData, slot).epoch
template hash_tree_root*(stateData: StateData): Eth2Digest =
# Dispatch here based on type/fork of state. Since StateData is a ref object
# type, if Nim chooses the wrong overload, it will simply fail to compile.
stateData.data.root

View File

@ -1,4 +1,4 @@
# 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).
@ -9,7 +9,7 @@ import
chronicles,
nimcrypto/utils as ncrutils,
../beacon_node_common, ../networking/eth2_network,
../consensus_object_pools/[blockchain_dag, exit_pool],
../consensus_object_pools/[blockchain_dag, exit_pool, statedata_helpers],
../gossip_processing/gossip_validation,
../validators/validator_duties,
../spec/[crypto, digest, validator, datatypes, network],
@ -127,9 +127,9 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
router.api(MethodGet, "/api/eth/v1/beacon/genesis") do () -> RestApiResponse:
return RestApiResponse.jsonResponse(
(
genesis_time: node.chainDag.headState.data.data.genesis_time,
genesis_time: getStateField(node.chainDag.headState, genesis_time),
genesis_validators_root:
node.chainDag.headState.data.data.genesis_validators_root,
getStateField(node.chainDag.headState, genesis_validators_root),
genesis_fork_version: node.runtimePreset.GENESIS_FORK_VERSION
)
)
@ -268,7 +268,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
(res1, res2)
node.withStateForBlockSlot(bslot):
let current_epoch = get_current_epoch(node.chainDag.headState.data.data)
let current_epoch = get_current_epoch(node.chainDag.headState)
var res: seq[RestValidatorTuple]
for index, validator in getStateField(stateData, validators).pairs():
let includeFlag =
@ -309,7 +309,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http400, InvalidValidatorIdValueError,
$validator_id.error())
node.withStateForBlockSlot(bslot):
let current_epoch = get_current_epoch(node.chainDag.headState.data.data)
let current_epoch = get_current_epoch(node.chainDag.headState)
let vid = validator_id.get()
case vid.kind
of ValidatorQueryKind.Key:
@ -416,7 +416,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
res2.incl(vitem)
(res1, res2)
node.withStateForBlockSlot(bslot):
let current_epoch = get_current_epoch(node.chainDag.headState.data.data)
let current_epoch = get_current_epoch(node.chainDag.headState)
var res: seq[RestValidatorBalanceTuple]
for index, validator in getStateField(stateData, validators).pairs():
let includeFlag =

View File

@ -1,4 +1,4 @@
# 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).
@ -28,7 +28,7 @@ proc installConfigApiHandlers*(router: var RestRouter, node: BeaconNode) =
# TODO: Implemenation needs a fix, when forks infrastructure will be
# established.
return RestApiResponse.jsonResponse(
[node.chainDag.headState.data.data.fork]
[getStateField(node.chainDag.headState, fork)]
)
router.api(MethodGet,

View File

@ -1,4 +1,4 @@
# 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).
@ -96,8 +96,9 @@ proc installNimbusApiHandlers*(router: var RestRouter, node: BeaconNode) =
router.api(MethodGet, "/api/nimbus/v1/chain/head") do() -> RestApiResponse:
let
head = node.chainDag.head
finalized = node.chainDag.headState.data.data.finalized_checkpoint
justified = node.chainDag.headState.data.data.current_justified_checkpoint
finalized = getStateField(node.chainDag.headState, finalized_checkpoint)
justified =
getStateField(node.chainDag.headState, current_justified_checkpoint)
return RestApiResponse.jsonResponse(
(
head_slot: head.slot,

View File

@ -509,7 +509,7 @@ proc getBlockSlot*(node: BeaconNode,
ok(node.chainDag.finalizedHead)
of StateIdentType.Justified:
ok(node.chainDag.head.atEpochStart(
node.chainDag.headState.data.data.current_justified_checkpoint.epoch))
getStateField(node.chainDag.headState, current_justified_checkpoint).epoch))
proc getBlockDataFromBlockIdent*(node: BeaconNode,
id: BlockIdent): Result[BlockData, cstring] =
@ -537,7 +537,7 @@ proc getBlockDataFromBlockIdent*(node: BeaconNode,
template withStateForBlockSlot*(node: BeaconNode,
blockSlot: BlockSlot, body: untyped): untyped =
template isState(state: StateData): bool =
state.blck.atSlot(state.data.data.slot) == blockSlot
state.blck.atSlot(getStateField(state, slot)) == blockSlot
if isState(node.chainDag.headState):
withStateVars(node.chainDag.headState):

View File

@ -1,4 +1,4 @@
# 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).
@ -362,10 +362,10 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
block:
let idx = request.validator_index
if uint64(idx) >=
lenu64(node.chainDag.headState.data.data.validators):
lenu64(getStateField(node.chainDag.headState, validators)):
return RestApiResponse.jsonError(Http400,
InvalidValidatorIndexValueError)
node.chainDag.headState.data.data.validators[idx].pubkey
getStateField(node.chainDag.headState, validators)[idx].pubkey
let wallSlot = node.beaconClock.now.slotOrZero
if wallSlot > request.slot + 1:

View File

@ -66,16 +66,17 @@ proc findValidator(validators: auto, pubKey: ValidatorPubKey):
else:
some(idx.ValidatorIndex)
proc addLocalValidator*(node: BeaconNode,
state: BeaconState,
privKey: ValidatorPrivKey) =
proc addLocalValidator(node: BeaconNode,
stateData: StateData,
privKey: ValidatorPrivKey) =
let pubKey = privKey.toPubKey()
node.attachedValidators[].addLocalValidator(
pubKey, privKey, findValidator(state.validators, pubKey))
pubKey, privKey,
findValidator(getStateField(stateData, validators), pubKey))
proc addLocalValidators*(node: BeaconNode) =
for validatorKey in node.config.validatorKeys:
node.addLocalValidator node.chainDag.headState.data.data, validatorKey
node.addLocalValidator node.chainDag.headState, validatorKey
proc addRemoteValidators*(node: BeaconNode) {.raises: [Defect, OSError, IOError].} =
# load all the validators from the child process - loop until `end`

View File

@ -224,7 +224,7 @@ cli do(slots = SLOTS_PER_EPOCH * 5,
# TODO if attestation pool was smarter, it would include older attestations
# too!
verifyConsensus(chainDag.headState.data.data, attesterRatio * blockRatio)
verifyConsensus(chainDag.headState, attesterRatio * blockRatio)
if t == tEpoch:
echo &". slot: {shortLog(slot)} ",
@ -241,4 +241,4 @@ cli do(slots = SLOTS_PER_EPOCH * 5,
echo "Done!"
printTimers(chainDag.headState.data.data, attesters, true, timers)
printTimers(chainDag.headState, attesters, true, timers)

View File

@ -1,9 +1,11 @@
import
stats, os, strformat, times,
../tests/[testblockutil],
../tests/testblockutil,
../beacon_chain/[extras, beacon_chain_db],
../beacon_chain/ssz/[merkleization, ssz_serialization],
../beacon_chain/spec/[beaconstate, crypto, datatypes, digest, helpers, presets],
../beacon_chain/consensus_object_pools/[
blockchain_dag, block_pools_types, statedata_helpers],
../beacon_chain/eth1/eth1_monitor
template withTimer*(stats: var RunningStat, body: untyped) =
@ -41,6 +43,24 @@ func verifyConsensus*(state: BeaconState, attesterRatio: auto) =
if current_epoch >= 4:
doAssert state.finalized_checkpoint.epoch + 2 >= current_epoch
func verifyConsensus*(state: StateData, attesterRatio: auto) =
if attesterRatio < 0.63:
doAssert getStateField(state, current_justified_checkpoint).epoch == 0
doAssert getStateField(state, finalized_checkpoint).epoch == 0
# Quorum is 2/3 of validators, and at low numbers, quantization effects
# can dominate, so allow for play above/below attesterRatio of 2/3.
if attesterRatio < 0.72:
return
let current_epoch = get_current_epoch(state)
if current_epoch >= 3:
doAssert getStateField(
state, current_justified_checkpoint).epoch + 1 >= current_epoch
if current_epoch >= 4:
doAssert getStateField(
state, finalized_checkpoint).epoch + 2 >= current_epoch
proc loadGenesis*(validators: Natural, validate: bool):
(ref HashedBeaconState, DepositContractSnapshot) =
let
@ -122,3 +142,10 @@ proc printTimers*[Timers: enum](
echo "Validators: ", state.validators.len, ", epoch length: ", SLOTS_PER_EPOCH
echo "Validators per attestation (mean): ", attesters.mean
printTimers(validate, timers)
proc printTimers*[Timers: enum](
state: StateData, attesters: RunningStat, validate: bool,
timers: array[Timers, RunningStat]) =
echo "Validators: ", getStateField(state, validators).len, ", epoch length: ", SLOTS_PER_EPOCH
echo "Validators per attestation (mean): ", attesters.mean
printTimers(validate, timers)

View File

@ -67,13 +67,13 @@ suiteReport "Attestation pool processing" & preset():
cache = StateCache()
# Slot 0 is a finalized slot - won't be making attestations for it..
check:
process_slots(state.data, state.data.data.slot + 1, cache)
process_slots(state.data, getStateField(state, slot) + 1, cache)
timedTest "Can add and retrieve simple attestations" & preset():
let
# Create an attestation for slot 1!
bc0 = get_beacon_committee(
state.data.data, state.data.data.slot, 0.CommitteeIndex, cache)
state.data.data, getStateField(state, slot), 0.CommitteeIndex, cache)
attestation = makeAttestation(
state.data.data, state.blck.root, bc0[0], cache)
@ -98,7 +98,8 @@ suiteReport "Attestation pool processing" & preset():
none(Slot), some(CommitteeIndex(attestation.data.index + 1)))) == []
process_slots(
state.data, state.data.data.slot + MIN_ATTESTATION_INCLUSION_DELAY, cache)
state.data,
getStateField(state, slot) + MIN_ATTESTATION_INCLUSION_DELAY, cache)
let attestations = pool[].getAttestationsForBlock(state.data.data, cache)
@ -111,13 +112,14 @@ suiteReport "Attestation pool processing" & preset():
state.data, state.blck.root,
cache, attestations = attestations, nextSlot = false).root
bc1 = get_beacon_committee(
state.data.data, state.data.data.slot, 0.CommitteeIndex, cache)
state.data.data, getStateField(state, slot), 0.CommitteeIndex, cache)
att1 = makeAttestation(
state.data.data, root1, bc1[0], cache)
check:
process_slots(
state.data, state.data.data.slot + MIN_ATTESTATION_INCLUSION_DELAY, cache)
state.data,
getStateField(state, slot) + MIN_ATTESTATION_INCLUSION_DELAY, cache)
check:
# shouldn't include already-included attestations
@ -176,7 +178,7 @@ suiteReport "Attestation pool processing" & preset():
let
# Create an attestation for slot 1!
bc0 = get_beacon_committee(
state.data.data, state.data.data.slot, 0.CommitteeIndex, cache)
state.data.data, getStateField(state, slot), 0.CommitteeIndex, cache)
var
att0 = makeAttestation(state.data.data, state.blck.root, bc0[0], cache)
@ -194,7 +196,8 @@ suiteReport "Attestation pool processing" & preset():
check:
process_slots(
state.data, state.data.data.slot + MIN_ATTESTATION_INCLUSION_DELAY, cache)
state.data,
getStateField(state, slot) + MIN_ATTESTATION_INCLUSION_DELAY, cache)
check:
pool[].getAttestationsForBlock(state.data.data, cache).len() == 2
@ -230,7 +233,7 @@ suiteReport "Attestation pool processing" & preset():
root.data[0..<8] = toBytesBE(i.uint64)
let
bc0 = get_beacon_committee(
state.data.data, state.data.data.slot, 0.CommitteeIndex, cache)
state.data.data, getStateField(state, slot), 0.CommitteeIndex, cache)
for j in 0..<bc0.len():
root.data[8..<16] = toBytesBE(j.uint64)
@ -239,7 +242,7 @@ suiteReport "Attestation pool processing" & preset():
inc attestations
check:
process_slots(state.data, state.data.data.slot + 1, cache)
process_slots(state.data, getStateField(state, slot) + 1, cache)
doAssert attestations.uint64 > MAX_ATTESTATIONS,
"6*SLOTS_PER_EPOCH validators > 128 mainnet MAX_ATTESTATIONS"
@ -247,23 +250,24 @@ suiteReport "Attestation pool processing" & preset():
# Fill block with attestations
pool[].getAttestationsForBlock(state.data.data, cache).lenu64() ==
MAX_ATTESTATIONS
pool[].getAggregatedAttestation(state.data.data.slot - 1, 0.CommitteeIndex).isSome()
pool[].getAggregatedAttestation(
getStateField(state, slot) - 1, 0.CommitteeIndex).isSome()
timedTest "Attestations may arrive in any order" & preset():
var cache = StateCache()
let
# Create an attestation for slot 1!
bc0 = get_beacon_committee(
state.data.data, state.data.data.slot, 0.CommitteeIndex, cache)
state.data.data, getStateField(state, slot), 0.CommitteeIndex, cache)
attestation0 = makeAttestation(
state.data.data, state.blck.root, bc0[0], cache)
check:
process_slots(state.data, state.data.data.slot + 1, cache)
process_slots(state.data, getStateField(state, slot) + 1, cache)
let
bc1 = get_beacon_committee(state.data.data,
state.data.data.slot, 0.CommitteeIndex, cache)
getStateField(state, slot), 0.CommitteeIndex, cache)
attestation1 = makeAttestation(
state.data.data, state.blck.root, bc1[0], cache)
@ -286,7 +290,7 @@ suiteReport "Attestation pool processing" & preset():
let
# Create an attestation for slot 1!
bc0 = get_beacon_committee(
state.data.data, state.data.data.slot, 0.CommitteeIndex, cache)
state.data.data, getStateField(state, slot), 0.CommitteeIndex, cache)
attestation0 = makeAttestation(
state.data.data, state.blck.root, bc0[0], cache)
attestation1 = makeAttestation(
@ -311,7 +315,7 @@ suiteReport "Attestation pool processing" & preset():
var
# Create an attestation for slot 1!
bc0 = get_beacon_committee(
state.data.data, state.data.data.slot, 0.CommitteeIndex, cache)
state.data.data, getStateField(state, slot), 0.CommitteeIndex, cache)
attestation0 = makeAttestation(
state.data.data, state.blck.root, bc0[0], cache)
attestation1 = makeAttestation(
@ -337,7 +341,7 @@ suiteReport "Attestation pool processing" & preset():
var
# Create an attestation for slot 1!
bc0 = get_beacon_committee(state.data.data,
state.data.data.slot, 0.CommitteeIndex, cache)
getStateField(state, slot), 0.CommitteeIndex, cache)
attestation0 = makeAttestation(
state.data.data, state.blck.root, bc0[0], cache)
attestation1 = makeAttestation(
@ -412,7 +416,7 @@ suiteReport "Attestation pool processing" & preset():
pool[].addForkChoice(epochRef, blckRef, signedBlock.message, blckRef.slot)
bc1 = get_beacon_committee(
state.data.data, state.data.data.slot - 1, 1.CommitteeIndex, cache)
state.data.data, getStateField(state, slot) - 1, 1.CommitteeIndex, cache)
attestation0 = makeAttestation(state.data.data, b10.root, bc1[0], cache)
pool[].addAttestation(
@ -521,7 +525,8 @@ suiteReport "Attestation pool processing" & preset():
attestations.setlen(0)
for index in 0'u64 ..< committees_per_slot:
let committee = get_beacon_committee(
state.data.data, state.data.data.slot, index.CommitteeIndex, cache)
state.data.data, getStateField(state, slot), index.CommitteeIndex,
cache)
# Create a bitfield filled with the given count per attestation,
# exactly on the right-most part of the committee field.
@ -532,7 +537,7 @@ suiteReport "Attestation pool processing" & preset():
attestations.add Attestation(
aggregation_bits: aggregation_bits,
data: makeAttestationData(
state.data.data, state.data.data.slot,
state.data.data, getStateField(state, slot),
index.CommitteeIndex, blockroot)
# signature: ValidatorSig()
)
@ -569,7 +574,7 @@ suiteReport "Attestation validation " & preset():
batchCrypto = BatchCrypto.new(keys.newRng())
# Slot 0 is a finalized slot - won't be making attestations for it..
check:
process_slots(state.data, state.data.data.slot + 1, cache)
process_slots(state.data, getStateField(state, slot) + 1, cache)
timedTest "Validation sanity":
# TODO: refactor tests to avoid skipping BLS validation

View File

@ -15,7 +15,8 @@ import
../beacon_chain/spec/[datatypes, digest, helpers, state_transition, presets],
../beacon_chain/beacon_node_types,
../beacon_chain/ssz,
../beacon_chain/consensus_object_pools/[blockchain_dag, block_quarantine, block_clearance]
../beacon_chain/consensus_object_pools/[
blockchain_dag, block_quarantine, block_clearance, statedata_helpers]
when isMainModule:
import chronicles # or some random compile error happens...
@ -174,7 +175,7 @@ suiteReport "Block pool processing" & preset():
# Skip one slot to get a gap
check:
process_slots(stateData.data, stateData.data.data.slot + 1, cache)
process_slots(stateData.data, getStateField(stateData, slot) + 1, cache)
let
b4 = addTestBlock(stateData.data, b2.root, cache)
@ -262,7 +263,7 @@ suiteReport "Block pool processing" & preset():
check:
# ensure we loaded the correct head state
dag2.head.root == b2.root
hash_tree_root(dag2.headState.data.data) == b2.message.state_root
hash_tree_root(dag2.headState) == b2.message.state_root
dag2.get(b1.root).isSome()
dag2.get(b2.root).isSome()
dag2.heads.len == 1
@ -286,7 +287,7 @@ suiteReport "Block pool processing" & preset():
check:
dag.head == b1Add[]
dag.headState.data.data.slot == b1Add[].slot
getStateField(dag.headState, slot) == b1Add[].slot
wrappedTimedTest "updateStateData sanity" & preset():
let
@ -304,38 +305,38 @@ suiteReport "Block pool processing" & preset():
check:
tmpState.blck == b1Add[]
tmpState.data.data.slot == bs1.slot
getStateField(tmpState, slot) == bs1.slot
# Skip slots
dag.updateStateData(tmpState[], bs1_3, false, cache) # skip slots
check:
tmpState.blck == b1Add[]
tmpState.data.data.slot == bs1_3.slot
getStateField(tmpState, slot) == bs1_3.slot
# Move back slots, but not blocks
dag.updateStateData(tmpState[], bs1_3.parent(), false, cache)
check:
tmpState.blck == b1Add[]
tmpState.data.data.slot == bs1_3.parent().slot
getStateField(tmpState, slot) == bs1_3.parent().slot
# Move to different block and slot
dag.updateStateData(tmpState[], bs2_3, false, cache)
check:
tmpState.blck == b2Add[]
tmpState.data.data.slot == bs2_3.slot
getStateField(tmpState, slot) == bs2_3.slot
# Move back slot and block
dag.updateStateData(tmpState[], bs1, false, cache)
check:
tmpState.blck == b1Add[]
tmpState.data.data.slot == bs1.slot
getStateField(tmpState, slot) == bs1.slot
# Move back to genesis
dag.updateStateData(tmpState[], bs1.parent(), false, cache)
check:
tmpState.blck == b1Add[].parent
tmpState.data.data.slot == bs1.parent.slot
getStateField(tmpState, slot) == bs1.parent.slot
suiteReport "chain DAG finalization tests" & preset():
setup:
@ -431,8 +432,7 @@ suiteReport "chain DAG finalization tests" & preset():
dag2.head.root == dag.head.root
dag2.finalizedHead.blck.root == dag.finalizedHead.blck.root
dag2.finalizedHead.slot == dag.finalizedHead.slot
hash_tree_root(dag2.headState.data.data) ==
hash_tree_root(dag.headState.data.data)
hash_tree_root(dag2.headState) == hash_tree_root(dag.headState)
wrappedTimedTest "orphaned epoch block" & preset():
var prestate = (ref HashedBeaconState)()
@ -496,7 +496,7 @@ suiteReport "chain DAG finalization tests" & preset():
dag.headState.data, dag.head.root, cache,
attestations = makeFullAttestations(
dag.headState.data.data, dag.head.root,
dag.headState.data.data.slot, cache, {}))
getStateField(dag.headState, slot), cache, {}))
let added = dag.addRawBlock(quarantine, blck, nil)
check: added.isOk()
@ -512,5 +512,4 @@ suiteReport "chain DAG finalization tests" & preset():
dag2.head.root == dag.head.root
dag2.finalizedHead.blck.root == dag.finalizedHead.blck.root
dag2.finalizedHead.slot == dag.finalizedHead.slot
hash_tree_root(dag2.headState.data.data) ==
hash_tree_root(dag.headState.data.data)
hash_tree_root(dag2.headState) == hash_tree_root(dag.headState)