Remove get_crosslink_committees_at_slot and fix research/state_sim (#291)
* migrate attestation pool tests from get_crosslink_committees_at_slot(...) to get_crosslink_committee(...) * rm obsolete, unused get_crosslink_committees_at_slot_cached(...) and migrate tests/test_state_transition from get_crosslink_committees_at_slot(...) to get_crosslink_committee(...) * migrate tests/testutil from get_crosslink_committees_at_slot(...) to get_crosslink_committee(...) * use more pervasive caching infrastructure, initially of compute_committee; remove buggy (and per-index) shuffling to fix research/state_sim, which was noticing validators on multiple shard committees; rm now-unused specific get_attesting_balance_cached * add some get_active_validator_index caching * rm obsolete/unused get_attesting_indices_cached * rm get_crosslink_committees_at_slot(...) * some more 0.7 changes -- some of which can't be completed pending some data structure updates -- and shard handling changes to offset with epoch start shards
This commit is contained in:
parent
c751285112
commit
c7e06374f4
|
@ -260,6 +260,7 @@ proc getAttestationsForBlock*(
|
|||
newBlockSlot = humaneSlotNum(newBlockSlot)
|
||||
return
|
||||
|
||||
var cache = get_empty_per_epoch_cache()
|
||||
let
|
||||
# TODO in theory we could include attestations from other slots also, but
|
||||
# we're currently not tracking which attestations have already been included
|
||||
|
@ -296,7 +297,8 @@ proc getAttestationsForBlock*(
|
|||
# attestations into the pool in general is an open question that needs
|
||||
# revisiting - for example, when attestations are added, against which
|
||||
# state should they be validated, if at all?
|
||||
if not checkAttestation(state, attestation, {skipValidation, nextSlot}):
|
||||
if not checkAttestation(
|
||||
state, attestation, {skipValidation, nextSlot}, cache):
|
||||
continue
|
||||
|
||||
for v in a.validations[1..^1]:
|
||||
|
|
|
@ -468,13 +468,20 @@ proc handleAttestations(node: BeaconNode, head: BlockRef, slot: Slot) =
|
|||
# In case blocks went missing, this means advancing past the latest block
|
||||
# using empty slots as fillers.
|
||||
node.blockPool.withState(node.stateCache, attestationHead):
|
||||
for crosslink_committee in get_crosslink_committees_at_slot(state, slot):
|
||||
for i, validatorIdx in crosslink_committee.committee:
|
||||
var cache = get_empty_per_epoch_cache()
|
||||
let epoch = slot_to_epoch(slot)
|
||||
for committee_index in 0'u64 ..< get_epoch_committee_count(state, epoch):
|
||||
## TODO verify that this is the correct mapping; it's consistent with
|
||||
## other code
|
||||
let
|
||||
shard = committee_index mod SHARD_COUNT
|
||||
committee = get_crosslink_committee(state, epoch, shard, cache)
|
||||
for i, validatorIdx in committee:
|
||||
let validator = node.getAttachedValidator(state, validatorIdx)
|
||||
if validator != nil:
|
||||
attestations.add (
|
||||
makeAttestationData(state, crosslink_committee.shard, blck.root),
|
||||
crosslink_committee.committee.len, i, validator)
|
||||
makeAttestationData(state, shard, blck.root),
|
||||
committee.len, i, validator)
|
||||
|
||||
for a in attestations:
|
||||
traceAsyncErrors sendAttestation(
|
||||
|
@ -489,9 +496,10 @@ proc handleProposal(node: BeaconNode, head: BlockRef, slot: Slot):
|
|||
# proposing for it - basically, we're selecting proposer based on an
|
||||
# empty slot.. wait for the committee selection to settle, then
|
||||
# revisit this - we should be able to advance behind
|
||||
var cache = get_empty_per_epoch_cache()
|
||||
node.blockPool.withState(node.stateCache, BlockSlot(blck: head, slot: slot)):
|
||||
let
|
||||
proposerIdx = get_beacon_proposer_index(state)
|
||||
proposerIdx = get_beacon_proposer_index(state, cache)
|
||||
validator = node.getAttachedValidator(state, proposerIdx)
|
||||
|
||||
if validator != nil:
|
||||
|
|
|
@ -206,10 +206,9 @@ type
|
|||
|
||||
StateCache* = object
|
||||
crosslink_committee_cache*:
|
||||
Table[tuple[a: uint64, b: bool], seq[CrosslinkCommittee]]
|
||||
|
||||
winning_root_participants_cache*:
|
||||
Table[Shard, HashSet[ValidatorIndex]]
|
||||
Table[tuple[a: int, b: Eth2Digest], seq[ValidatorIndex]]
|
||||
active_validator_indices_cache*:
|
||||
Table[Epoch, seq[ValidatorIndex]]
|
||||
|
||||
BlockSlot* = object
|
||||
## Unique identifier for a particular fork in the block chain - normally,
|
||||
|
|
|
@ -148,7 +148,8 @@ func initiate_validator_exit*(state: var BeaconState,
|
|||
validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#slash_validator
|
||||
func slash_validator*(state: var BeaconState, slashed_index: ValidatorIndex) =
|
||||
func slash_validator*(state: var BeaconState, slashed_index: ValidatorIndex,
|
||||
stateCache: var StateCache) =
|
||||
# Slash the validator with index ``index``.
|
||||
let current_epoch = get_current_epoch(state)
|
||||
initiate_validator_exit(state, slashed_index)
|
||||
|
@ -161,7 +162,7 @@ func slash_validator*(state: var BeaconState, slashed_index: ValidatorIndex) =
|
|||
slashed_balance
|
||||
|
||||
let
|
||||
proposer_index = get_beacon_proposer_index(state)
|
||||
proposer_index = get_beacon_proposer_index(state, stateCache)
|
||||
# Spec has whistleblower_index as optional param, but it's never used.
|
||||
whistleblower_index = proposer_index
|
||||
whistleblowing_reward = slashed_balance div WHISTLEBLOWING_REWARD_QUOTIENT
|
||||
|
@ -321,7 +322,9 @@ func get_total_balance*(state: BeaconState, validators: auto): Gwei =
|
|||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#registry-updates
|
||||
func process_registry_updates*(state: var BeaconState) =
|
||||
# Process activation eligibility and ejections
|
||||
## Process activation eligibility and ejections
|
||||
## Try to avoid caching here, since this could easily become undefined
|
||||
|
||||
for index, validator in state.validator_registry:
|
||||
if validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and
|
||||
validator.effective_balance >= MAX_EFFECTIVE_BALANCE:
|
||||
|
@ -411,7 +414,9 @@ func validate_indexed_attestation*(
|
|||
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#get_attesting_indices
|
||||
func get_attesting_indices*(state: BeaconState,
|
||||
attestation_data: AttestationData,
|
||||
bitfield: BitField): HashSet[ValidatorIndex] =
|
||||
bitfield: BitField,
|
||||
stateCache: var StateCache):
|
||||
HashSet[ValidatorIndex] =
|
||||
## Return the sorted attesting indices corresponding to ``attestation_data``
|
||||
## and ``bitfield``.
|
||||
## The spec goes through a lot of hoops to sort things, and sometimes
|
||||
|
@ -421,7 +426,7 @@ func get_attesting_indices*(state: BeaconState,
|
|||
result = initSet[ValidatorIndex]()
|
||||
let committee =
|
||||
get_crosslink_committee(state, attestation_data.target_epoch,
|
||||
attestation_data.shard)
|
||||
attestation_data.shard, stateCache)
|
||||
doAssert verify_bitfield(bitfield, len(committee))
|
||||
for i, index in committee:
|
||||
if get_bitfield_bit(bitfield, i):
|
||||
|
@ -430,25 +435,29 @@ func get_attesting_indices*(state: BeaconState,
|
|||
func get_attesting_indices_seq*(
|
||||
state: BeaconState, attestation_data: AttestationData, bitfield: BitField):
|
||||
seq[ValidatorIndex] =
|
||||
toSeq(items(get_attesting_indices(state, attestation_data, bitfield)))
|
||||
var cache = get_empty_per_epoch_cache()
|
||||
toSeq(items(get_attesting_indices(
|
||||
state, attestation_data, bitfield, cache)))
|
||||
|
||||
# TODO legacy function name; rename, reimplement caching if useful, blob/v0.6.2
|
||||
iterator get_attestation_participants_cached*(
|
||||
state: BeaconState, attestation_data: AttestationData, bitfield: BitField,
|
||||
cache: var StateCache): ValidatorIndex =
|
||||
for participant in get_attesting_indices(state, attestation_data, bitfield):
|
||||
for participant in get_attesting_indices(
|
||||
state, attestation_data, bitfield, cache):
|
||||
yield participant
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#convert_to_indexed
|
||||
func convert_to_indexed(state: BeaconState, attestation: Attestation): IndexedAttestation =
|
||||
func convert_to_indexed(state: BeaconState, attestation: Attestation,
|
||||
stateCache: var StateCache): IndexedAttestation =
|
||||
# Convert ``attestation`` to (almost) indexed-verifiable form.
|
||||
let
|
||||
attesting_indices =
|
||||
get_attesting_indices(
|
||||
state, attestation.data, attestation.aggregation_bitfield)
|
||||
state, attestation.data, attestation.aggregation_bitfield, stateCache)
|
||||
custody_bit_1_indices =
|
||||
get_attesting_indices(
|
||||
state, attestation.data, attestation.custody_bitfield)
|
||||
state, attestation.data, attestation.custody_bitfield, stateCache)
|
||||
|
||||
## TODO quadratic, .items, but first-class iterators, etc
|
||||
## filterIt can't work on HashSets directly because it is
|
||||
|
@ -486,7 +495,8 @@ func convert_to_indexed(state: BeaconState, attestation: Attestation): IndexedAt
|
|||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#attestations
|
||||
proc checkAttestation*(
|
||||
state: BeaconState, attestation: Attestation, flags: UpdateFlags): bool =
|
||||
state: BeaconState, attestation: Attestation, flags: UpdateFlags,
|
||||
stateCache: var StateCache): bool =
|
||||
## Process ``Attestation`` operation.
|
||||
## Check that an attestation follows the rules of being included in the state
|
||||
## at the current slot. When acting as a proposer, the same rules need to
|
||||
|
@ -496,7 +506,10 @@ proc checkAttestation*(
|
|||
if nextSlot in flags: state.slot + 1
|
||||
else: state.slot
|
||||
|
||||
let attestation_slot = get_attestation_slot(state, attestation)
|
||||
let
|
||||
data = attestation.data
|
||||
attestation_slot = get_attestation_slot(state, attestation)
|
||||
|
||||
if not (attestation_slot + MIN_ATTESTATION_INCLUSION_DELAY <= stateSlot):
|
||||
warn("Attestation too new",
|
||||
attestation_slot = humaneSlotNum(attestation_slot),
|
||||
|
@ -509,29 +522,66 @@ proc checkAttestation*(
|
|||
state_slot = humaneSlotNum(stateSlot))
|
||||
return
|
||||
|
||||
let pending_attestation = PendingAttestation(
|
||||
data: data,
|
||||
aggregation_bitfield: attestation.aggregation_bitfield,
|
||||
inclusion_delay: state.slot - attestation_slot,
|
||||
proposer_index: get_beacon_proposer_index(state, stateCache),
|
||||
)
|
||||
|
||||
# Check target epoch, source epoch, source root, and source crosslink
|
||||
let data = attestation.data
|
||||
if not (
|
||||
(data.target_epoch, data.source_epoch, data.source_root, data.previous_crosslink_root) ==
|
||||
(get_current_epoch(state), state.current_justified_epoch,
|
||||
state.current_justified_root,
|
||||
hash_tree_root(state.current_crosslinks[data.shard])) or
|
||||
(data.target_epoch, data.source_epoch, data.source_root, data.previous_crosslink_root) ==
|
||||
(get_previous_epoch(state), state.previous_justified_epoch,
|
||||
state.previous_justified_root,
|
||||
hash_tree_root(state.previous_crosslinks[data.shard]))):
|
||||
warn("checkAttestation: target epoch, source epoch, source root, or source crosslink invalid")
|
||||
if not (data.target_epoch == get_previous_epoch(state) or
|
||||
data.target_epoch == get_current_epoch(state)):
|
||||
warn("Target epoch not current or previous epoch")
|
||||
return
|
||||
|
||||
## Check crosslink data root
|
||||
## [to be removed in phase 1]
|
||||
if attestation.data.crosslink_data_root != ZERO_HASH:
|
||||
warn("Invalid crosslink data root")
|
||||
return
|
||||
# Check FFG data, crosslink data, and signature
|
||||
let ffg_check_data = (data.source_epoch, data.source_root, data.target_epoch)
|
||||
|
||||
if data.target_epoch == get_current_epoch(state):
|
||||
if not (ffg_check_data == (state.current_justified_epoch,
|
||||
state.current_justified_root, get_current_epoch(state))):
|
||||
warn("FFG data not matching current justified epoch")
|
||||
return
|
||||
|
||||
#if not (data.crosslink.parent_root ==
|
||||
# hash_tree_root(state.current_crosslinks[data.crosslink.shard])):
|
||||
# warn("Crosslink shard's current crosslinks not matching crosslink parent root")
|
||||
# return
|
||||
|
||||
#state.current_epoch_attestations.add(pending_attestation)
|
||||
else:
|
||||
if not (ffg_check_data == (state.previous_justified_epoch,
|
||||
state.previous_justified_root, get_previous_epoch(state))):
|
||||
warn("FFG data not matching current justified epoch")
|
||||
return
|
||||
|
||||
#if not (data.crosslink.parent_root ==
|
||||
# hash_tree_root(state.previous_crosslinks[data.crosslink.shard])):
|
||||
# warn("Crosslink shard's previous crosslinks not matching crosslink parent root")
|
||||
# return
|
||||
|
||||
#state.previous_epoch_attestations.add(pending_attestation)
|
||||
|
||||
# TODO un-comment when changed Crosslink to 0.7 structure
|
||||
#if not (data.crosslink.start_epoch == parent_crosslink.end_epoch):
|
||||
# warn("Crosslink start and end epochs not the same")
|
||||
# return
|
||||
|
||||
#if not (data.crosslink.end_epoch == min(data.target_epoch, parent_crosslink.end_epoch + MAX_EPOCHS_PER_CROSSLINK)):
|
||||
# warn("Crosslink end epoch incorrect")
|
||||
# return
|
||||
|
||||
# Simlarly, these depend on 0.7 data structures
|
||||
#assert data.crosslink.parent_root == hash_tree_root(parent_crosslink)
|
||||
|
||||
#if not (data.crosslink.data_root == ZERO_HASH): # [to be removed in phase 1]
|
||||
# warn("Crosslink data root not zero")
|
||||
# return
|
||||
|
||||
# Check signature and bitfields
|
||||
if not validate_indexed_attestation(
|
||||
state, convert_to_indexed(state, attestation)):
|
||||
state, convert_to_indexed(state, attestation, stateCache)):
|
||||
warn("checkAttestation: signature or bitfields incorrect")
|
||||
return
|
||||
|
||||
|
@ -552,7 +602,8 @@ proc makeAttestationData*(
|
|||
|
||||
AttestationData(
|
||||
slot: state.slot,
|
||||
shard: shard,
|
||||
# Alternative is to put this offset into all callers
|
||||
shard: shard + get_epoch_start_shard(state, slot_to_epoch(state.slot)),
|
||||
beacon_block_root: beacon_block_root,
|
||||
target_root: target_root,
|
||||
crosslink_data_root: Eth2Digest(), # Stub in phase0
|
||||
|
|
|
@ -363,9 +363,6 @@ type
|
|||
Activation = 0
|
||||
Exit = 1
|
||||
|
||||
# TODO: not in spec
|
||||
CrosslinkCommittee* = tuple[committee: seq[ValidatorIndex], shard: uint64]
|
||||
|
||||
# TODO to be replaced with some magic hash caching
|
||||
HashedBeaconState* = object
|
||||
data*: BeaconState
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
# Helpers and functions pertaining to managing the validator set
|
||||
|
||||
import
|
||||
options, nimcrypto, sequtils, math, chronicles,
|
||||
eth/common,
|
||||
options, nimcrypto, sequtils, math, tables, chronicles,
|
||||
../ssz, ../beacon_node_types,
|
||||
./crypto, ./datatypes, ./digest, ./helpers
|
||||
|
||||
|
@ -80,44 +79,6 @@ func get_shuffled_seq*(seed: Eth2Digest,
|
|||
|
||||
result = shuffled_active_validator_indices
|
||||
|
||||
func get_shuffled_index(index: ValidatorIndex, index_count: uint64, seed: Eth2Digest): uint64 =
|
||||
## Return the shuffled validator index corresponding to ``seed`` (and
|
||||
## ``index_count``).
|
||||
## https://github.com/status-im/nim-beacon-chain/blob/f77016af6818ad2c853f6c9e2751b17548e0222e/beacon_chain/spec/validator.nim#L15
|
||||
|
||||
doAssert index.uint64 < index_count
|
||||
doAssert index_count <= 2'u64^40
|
||||
|
||||
result = index
|
||||
var pivot_buffer: array[(32+1), byte]
|
||||
var source_buffer: array[(32+1+4), byte]
|
||||
|
||||
# Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf)
|
||||
# See the 'generalized domain' algorithm on page 3
|
||||
for round in 0 ..< SHUFFLE_ROUND_COUNT:
|
||||
pivot_buffer[0..31] = seed.data
|
||||
let round_bytes1 = int_to_bytes1(round)[0]
|
||||
pivot_buffer[32] = round_bytes1
|
||||
|
||||
let
|
||||
pivot = bytes_to_int(eth2hash(pivot_buffer).data[0..7]) mod index_count
|
||||
flip = (pivot - index) mod index_count
|
||||
position = max(index.uint64, flip)
|
||||
|
||||
## Tradeoff between slicing (if reusing one larger buffer) and additional
|
||||
## copies here of seed and `int_to_bytes1(round)`.
|
||||
source_buffer[0..31] = seed.data
|
||||
source_buffer[32] = round_bytes1
|
||||
source_buffer[33..36] = int_to_bytes4(position div 256)
|
||||
|
||||
let
|
||||
source = eth2hash(source_buffer).data
|
||||
byte_value = source[(position mod 256) div 8]
|
||||
bit = (byte_value shr (position mod 8)) mod 2
|
||||
|
||||
if bit != 0:
|
||||
result = flip
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#get_previous_epoch
|
||||
func get_previous_epoch*(state: BeaconState): Epoch =
|
||||
## Return the previous epoch of the given ``state``.
|
||||
|
@ -151,69 +112,63 @@ func get_epoch_start_shard*(state: BeaconState, epoch: Epoch): Shard =
|
|||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#compute_committee
|
||||
func compute_committee(indices: seq[ValidatorIndex], seed: Eth2Digest,
|
||||
index: uint64, count: uint64): seq[ValidatorIndex] =
|
||||
index: uint64, count: uint64, stateCache: var StateCache): seq[ValidatorIndex] =
|
||||
|
||||
let
|
||||
start = (len(indices).uint64 * index) div count
|
||||
endIdx = (len(indices).uint64 * (index + 1)) div count
|
||||
key = (indices.len, seed)
|
||||
doAssert endIdx.int - start.int > 0
|
||||
|
||||
if key notin stateCache.crosslink_committee_cache:
|
||||
stateCache.crosslink_committee_cache[key] =
|
||||
get_shuffled_seq(seed, len(indices).uint64)
|
||||
|
||||
# These assertions from get_shuffled_index(...)
|
||||
let index_count = indices.len().uint64
|
||||
doAssert endIdx <= index_count
|
||||
doAssert index_count <= 2'u64^40
|
||||
|
||||
# In spec, this calls get_shuffled_index() every time, but that's wasteful
|
||||
mapIt(
|
||||
start.int .. (endIdx.int-1),
|
||||
indices[
|
||||
get_shuffled_index(it.ValidatorIndex, len(indices).uint64, seed).int])
|
||||
stateCache.crosslink_committee_cache[key][it]])
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#get_crosslink_committee
|
||||
func get_crosslink_committee*(state: BeaconState, epoch: Epoch, shard: Shard):
|
||||
seq[ValidatorIndex] =
|
||||
func get_crosslink_committee*(state: BeaconState, epoch: Epoch, shard: Shard,
|
||||
stateCache: var StateCache): seq[ValidatorIndex] =
|
||||
|
||||
doAssert shard >= 0'u64
|
||||
# This seems to be required, basically, to be true? But I'm not entirely sure
|
||||
#doAssert shard >= get_epoch_start_shard(state, epoch)
|
||||
doAssert shard < SHARD_COUNT
|
||||
|
||||
## This is a somewhat more fragile, but high-ROI, caching setup --
|
||||
## get_active_validator_indices() is slow to run in a loop and only
|
||||
## changes once per epoch.
|
||||
if epoch notin stateCache.active_validator_indices_cache:
|
||||
stateCache.active_validator_indices_cache[epoch] =
|
||||
get_active_validator_indices(state, epoch)
|
||||
|
||||
compute_committee(
|
||||
get_active_validator_indices(state, epoch),
|
||||
stateCache.active_validator_indices_cache[epoch],
|
||||
generate_seed(state, epoch),
|
||||
(shard + SHARD_COUNT - get_epoch_start_shard(state, epoch)) mod SHARD_COUNT,
|
||||
get_epoch_committee_count(state, epoch),
|
||||
stateCache
|
||||
)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#get_crosslink_committees_at_slot
|
||||
func get_crosslink_committees_at_slot*(state: BeaconState, slot: Slot|uint64):
|
||||
seq[CrosslinkCommittee] =
|
||||
## Returns the list of ``(committee, shard)`` tuples for the ``slot``.
|
||||
|
||||
let
|
||||
epoch = slot_to_epoch(slot) # TODO, enforce slot to be a Slot
|
||||
current_epoch = get_current_epoch(state)
|
||||
previous_epoch = get_previous_epoch(state)
|
||||
next_epoch = current_epoch + 1
|
||||
|
||||
doAssert previous_epoch <= epoch,
|
||||
"Previous epoch: " & $humaneEpochNum(previous_epoch) &
|
||||
", epoch: " & $humaneEpochNum(epoch) &
|
||||
" (slot: " & $humaneSlotNum(slot.Slot) & ")" &
|
||||
", Next epoch: " & $humaneEpochNum(next_epoch)
|
||||
|
||||
doAssert epoch <= next_epoch,
|
||||
"Previous epoch: " & $humaneEpochNum(previous_epoch) &
|
||||
", epoch: " & $humaneEpochNum(epoch) &
|
||||
" (slot: " & $humaneSlotNum(slot.Slot) & ")" &
|
||||
", Next epoch: " & $humaneEpochNum(next_epoch)
|
||||
|
||||
for i in 0'u64 ..< get_epoch_committee_count(state, epoch):
|
||||
let shard = i mod SHARD_COUNT
|
||||
result.add (
|
||||
get_crosslink_committee(state, epoch, shard),
|
||||
shard
|
||||
)
|
||||
|
||||
iterator get_crosslink_committees_at_slot_cached*(
|
||||
state: BeaconState, slot: Slot|uint64, cache: var StateCache):
|
||||
CrosslinkCommittee =
|
||||
let key = (slot_to_epoch(slot).uint64, false)
|
||||
if key in cache.crosslink_committee_cache:
|
||||
for v in cache.crosslink_committee_cache[key]: yield v
|
||||
#debugEcho "get_crosslink_committees_at_slot_cached: MISS"
|
||||
let result = get_crosslink_committees_at_slot(state, slot)
|
||||
cache.crosslink_committee_cache[key] = result
|
||||
for v in result: yield v
|
||||
# Not from spec
|
||||
func get_empty_per_epoch_cache*(): StateCache =
|
||||
result.crosslink_committee_cache =
|
||||
initTable[tuple[a: int, b: Eth2Digest], seq[ValidatorIndex]]()
|
||||
result.active_validator_indices_cache =
|
||||
initTable[Epoch, seq[ValidatorIndex]]()
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#get_beacon_proposer_index
|
||||
func get_beacon_proposer_index*(state: BeaconState): ValidatorIndex =
|
||||
func get_beacon_proposer_index*(state: BeaconState, stateCache: var StateCache):
|
||||
ValidatorIndex =
|
||||
# Return the current beacon proposer index.
|
||||
const
|
||||
MAX_RANDOM_BYTE = 255
|
||||
|
@ -224,7 +179,7 @@ func get_beacon_proposer_index*(state: BeaconState): ValidatorIndex =
|
|||
get_epoch_committee_count(state, epoch) div SLOTS_PER_EPOCH
|
||||
offset = committees_per_slot * (state.slot mod SLOTS_PER_EPOCH)
|
||||
shard = (get_epoch_start_shard(state, epoch) + offset) mod SHARD_COUNT
|
||||
first_committee = get_crosslink_committee(state, epoch, shard)
|
||||
first_committee = get_crosslink_committee(state, epoch, shard, stateCache)
|
||||
seed = generate_seed(state, epoch)
|
||||
|
||||
var
|
||||
|
|
|
@ -37,7 +37,8 @@ import
|
|||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#block-header
|
||||
proc processBlockHeader(
|
||||
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool =
|
||||
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags,
|
||||
stateCache: var StateCache): bool =
|
||||
# Verify that the slots match
|
||||
if not (blck.slot == state.slot):
|
||||
notice "Block header: slot mismatch",
|
||||
|
@ -62,7 +63,8 @@ proc processBlockHeader(
|
|||
)
|
||||
|
||||
# Verify proposer is not slashed
|
||||
let proposer = state.validator_registry[get_beacon_proposer_index(state)]
|
||||
let proposer =
|
||||
state.validator_registry[get_beacon_proposer_index(state, stateCache)]
|
||||
if proposer.slashed:
|
||||
notice "Block header: proposer slashed"
|
||||
return false
|
||||
|
@ -83,9 +85,10 @@ proc processBlockHeader(
|
|||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#randao
|
||||
proc processRandao(
|
||||
state: var BeaconState, body: BeaconBlockBody, flags: UpdateFlags): bool =
|
||||
state: var BeaconState, body: BeaconBlockBody, flags: UpdateFlags,
|
||||
stateCache: var StateCache): bool =
|
||||
let
|
||||
proposer_index = get_beacon_proposer_index(state)
|
||||
proposer_index = get_beacon_proposer_index(state, stateCache)
|
||||
proposer = addr state.validator_registry[proposer_index]
|
||||
|
||||
# Verify that the provided randao value is valid
|
||||
|
@ -128,7 +131,8 @@ func is_slashable_validator(validator: Validator, epoch: Epoch): bool =
|
|||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#proposer-slashings
|
||||
proc processProposerSlashings(
|
||||
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool =
|
||||
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags,
|
||||
stateCache: var StateCache): bool =
|
||||
if len(blck.body.proposer_slashings) > MAX_PROPOSER_SLASHINGS:
|
||||
notice "PropSlash: too many!",
|
||||
proposer_slashings = len(blck.body.proposer_slashings)
|
||||
|
@ -166,7 +170,8 @@ proc processProposerSlashings(
|
|||
signature_index = i
|
||||
return false
|
||||
|
||||
slashValidator(state, proposer_slashing.proposer_index.ValidatorIndex)
|
||||
slashValidator(
|
||||
state, proposer_slashing.proposer_index.ValidatorIndex, stateCache)
|
||||
|
||||
true
|
||||
|
||||
|
@ -183,7 +188,8 @@ func is_slashable_attestation_data(
|
|||
data_2.target_epoch < data_1.target_epoch)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#attester-slashings
|
||||
proc processAttesterSlashings(state: var BeaconState, blck: BeaconBlock): bool =
|
||||
proc processAttesterSlashings(state: var BeaconState, blck: BeaconBlock,
|
||||
stateCache: var StateCache): bool =
|
||||
# Process ``AttesterSlashing`` operation.
|
||||
if len(blck.body.attester_slashings) > MAX_ATTESTER_SLASHINGS:
|
||||
notice "CaspSlash: too many!"
|
||||
|
@ -221,13 +227,14 @@ proc processAttesterSlashings(state: var BeaconState, blck: BeaconBlock): bool =
|
|||
toSet(attesting_indices_2)).items), system.cmp):
|
||||
if is_slashable_validator(state.validator_registry[index.int],
|
||||
get_current_epoch(state)):
|
||||
slash_validator(state, index.ValidatorIndex)
|
||||
slash_validator(state, index.ValidatorIndex, stateCache)
|
||||
slashed_any = true
|
||||
result = result and slashed_any
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#attestations
|
||||
proc processAttestations(
|
||||
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool =
|
||||
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags,
|
||||
stateCache: var StateCache): bool =
|
||||
## Each block includes a number of attestations that the proposer chose. Each
|
||||
## attestation represents an update to a specific shard and is signed by a
|
||||
## committee of validators.
|
||||
|
@ -238,7 +245,7 @@ proc processAttestations(
|
|||
notice "Attestation: too many!", attestations = blck.body.attestations.len
|
||||
return false
|
||||
|
||||
if not blck.body.attestations.allIt(checkAttestation(state, it, flags)):
|
||||
if not blck.body.attestations.allIt(checkAttestation(state, it, flags, stateCache)):
|
||||
return false
|
||||
|
||||
# All checks passed - update state
|
||||
|
@ -262,7 +269,7 @@ proc processAttestations(
|
|||
data: attestation.data,
|
||||
aggregation_bitfield: attestation.aggregation_bitfield,
|
||||
inclusion_delay: state.slot - attestation_slot,
|
||||
proposer_index: get_beacon_proposer_index(state),
|
||||
proposer_index: get_beacon_proposer_index(state, stateCache),
|
||||
)
|
||||
|
||||
if attestation.data.target_epoch == get_current_epoch(state):
|
||||
|
@ -334,7 +341,7 @@ proc processVoluntaryExits(
|
|||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#transfers
|
||||
proc processTransfers(state: var BeaconState, blck: BeaconBlock,
|
||||
flags: UpdateFlags): bool =
|
||||
flags: UpdateFlags, stateCache: var StateCache): bool =
|
||||
if not (len(blck.body.transfers) <= MAX_TRANSFERS):
|
||||
notice "Transfer: too many transfers"
|
||||
return false
|
||||
|
@ -387,7 +394,8 @@ proc processTransfers(state: var BeaconState, blck: BeaconBlock,
|
|||
state, transfer.sender.ValidatorIndex, transfer.amount + transfer.fee)
|
||||
increase_balance(
|
||||
state, transfer.recipient.ValidatorIndex, transfer.amount)
|
||||
increase_balance(state, get_beacon_proposer_index(state), transfer.fee)
|
||||
increase_balance(
|
||||
state, get_beacon_proposer_index(state, stateCache), transfer.fee)
|
||||
|
||||
# Verify balances are not dust
|
||||
if not (
|
||||
|
@ -429,32 +437,33 @@ func process_slot(state: var BeaconState) =
|
|||
signing_root(state.latest_block_header)
|
||||
|
||||
proc processBlock(
|
||||
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool =
|
||||
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags,
|
||||
stateCache: var StateCache): bool =
|
||||
## When there's a new block, we need to verify that the block is sane and
|
||||
## update the state accordingly
|
||||
|
||||
# TODO when there's a failure, we should reset the state!
|
||||
# TODO probably better to do all verification first, then apply state changes
|
||||
|
||||
if not processBlockHeader(state, blck, flags):
|
||||
if not processBlockHeader(state, blck, flags, stateCache):
|
||||
notice "Block header not valid", slot = humaneSlotNum(state.slot)
|
||||
return false
|
||||
|
||||
if not processRandao(state, blck.body, flags):
|
||||
if not processRandao(state, blck.body, flags, stateCache):
|
||||
debug "[Block processing] Randao failure", slot = humaneSlotNum(state.slot)
|
||||
return false
|
||||
|
||||
processEth1Data(state, blck.body)
|
||||
|
||||
if not processProposerSlashings(state, blck, flags):
|
||||
if not processProposerSlashings(state, blck, flags, stateCache):
|
||||
debug "[Block processing] Proposer slashing failure", slot = humaneSlotNum(state.slot)
|
||||
return false
|
||||
|
||||
if not processAttesterSlashings(state, blck):
|
||||
if not processAttesterSlashings(state, blck, stateCache):
|
||||
debug "[Block processing] Attester slashing failure", slot = humaneSlotNum(state.slot)
|
||||
return false
|
||||
|
||||
if not processAttestations(state, blck, flags):
|
||||
if not processAttestations(state, blck, flags, stateCache):
|
||||
debug "[Block processing] Attestation processing failure", slot = humaneSlotNum(state.slot)
|
||||
return false
|
||||
|
||||
|
@ -466,26 +475,12 @@ proc processBlock(
|
|||
debug "[Block processing] Exit processing failure", slot = humaneSlotNum(state.slot)
|
||||
return false
|
||||
|
||||
if not processTransfers(state, blck, flags):
|
||||
if not processTransfers(state, blck, flags, stateCache):
|
||||
debug "[Block processing] Transfer processing failure", slot = humaneSlotNum(state.slot)
|
||||
return false
|
||||
|
||||
true
|
||||
|
||||
# TODO this cached version corresponds to the blob/v0.5.1ish get_attesting_indices
|
||||
# rm/make consistent with 0.6 version above
|
||||
func get_attesting_indices_cached(
|
||||
state: BeaconState,
|
||||
attestations: openArray[PendingAttestation], cache: var StateCache):
|
||||
HashSet[ValidatorIndex] =
|
||||
# Union of attesters that participated in some attestations
|
||||
result = initSet[ValidatorIndex]()
|
||||
for attestation in attestations:
|
||||
for validator_index in get_attestation_participants_cached(
|
||||
state, attestation.data, attestation.aggregation_bitfield,
|
||||
cache):
|
||||
result.incl validator_index
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#helper-functions-1
|
||||
func get_total_active_balance(state: BeaconState): Gwei =
|
||||
return get_total_balance(
|
||||
|
@ -516,26 +511,22 @@ func get_matching_head_attestations(state: BeaconState, epoch: Epoch):
|
|||
)
|
||||
|
||||
func get_unslashed_attesting_indices(
|
||||
state: BeaconState, attestations: seq[PendingAttestation]):
|
||||
HashSet[ValidatorIndex] =
|
||||
state: BeaconState, attestations: seq[PendingAttestation],
|
||||
stateCache: var StateCache): HashSet[ValidatorIndex] =
|
||||
result = initSet[ValidatorIndex]()
|
||||
for a in attestations:
|
||||
result = result.union(get_attesting_indices(
|
||||
state, a.data, a.aggregation_bitfield))
|
||||
state, a.data, a.aggregation_bitfield, stateCache))
|
||||
|
||||
for index in result:
|
||||
if state.validator_registry[index].slashed:
|
||||
result.excl index
|
||||
|
||||
func get_attesting_balance(
|
||||
state: BeaconState, attestations: seq[PendingAttestation]): Gwei =
|
||||
get_total_balance(state, get_unslashed_attesting_indices(state, attestations))
|
||||
|
||||
func get_attesting_balance_cached(
|
||||
state: BeaconState, attestations: seq[PendingAttestation],
|
||||
cache: var StateCache): Gwei =
|
||||
get_total_balance(state, get_attesting_indices_cached(
|
||||
state, attestations, cache))
|
||||
stateCache: var StateCache): Gwei =
|
||||
get_total_balance(state, get_unslashed_attesting_indices(
|
||||
state, attestations, stateCache))
|
||||
|
||||
func get_crosslink_from_attestation_data(
|
||||
state: BeaconState, data: AttestationData): Crosslink =
|
||||
|
@ -554,9 +545,9 @@ func lowerThan(candidate, current: Eth2Digest): bool =
|
|||
if v > candidate.data[i]: return true
|
||||
false
|
||||
|
||||
# TODO check/profile if should add cache: var StateCache param
|
||||
func get_winning_crosslink_and_attesting_indices(
|
||||
state: BeaconState, epoch: Epoch, shard: Shard): tuple[a: Crosslink, b: HashSet[ValidatorIndex]] =
|
||||
state: BeaconState, epoch: Epoch, shard: Shard,
|
||||
stateCache: var StateCache): tuple[a: Crosslink, b: HashSet[ValidatorIndex]] =
|
||||
let
|
||||
## TODO Z-F could help here
|
||||
## TODO get_winning_crosslink_and_attesting_indices was profiling hotspot
|
||||
|
@ -602,7 +593,8 @@ func get_winning_crosslink_and_attesting_indices(
|
|||
## let root_balance = get_attesting_balance_cached(
|
||||
## state, attestations_for.getOrDefault(r), cache)
|
||||
let crosslink_balance =
|
||||
get_attesting_balance(state, get_attestations_for(candidate_crosslink))
|
||||
get_attesting_balance(
|
||||
state, get_attestations_for(candidate_crosslink), stateCache)
|
||||
if (crosslink_balance > winning_crosslink_balance or
|
||||
(winning_crosslink_balance == crosslink_balance and
|
||||
lowerThan(winning_crosslink.crosslink_data_root,
|
||||
|
@ -612,10 +604,11 @@ func get_winning_crosslink_and_attesting_indices(
|
|||
|
||||
(winning_crosslink,
|
||||
get_unslashed_attesting_indices(state,
|
||||
get_attestations_for(winning_crosslink)))
|
||||
get_attestations_for(winning_crosslink), stateCache))
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#justification-and-finalization
|
||||
func process_justification_and_finalization(state: var BeaconState) =
|
||||
func process_justification_and_finalization(
|
||||
state: var BeaconState, stateCache: var StateCache) =
|
||||
if get_current_epoch(state) <= GENESIS_EPOCH + 1:
|
||||
return
|
||||
|
||||
|
@ -631,7 +624,7 @@ func process_justification_and_finalization(state: var BeaconState) =
|
|||
state.justification_bitfield = (state.justification_bitfield shl 1)
|
||||
let previous_epoch_matching_target_balance =
|
||||
get_attesting_balance(state,
|
||||
get_matching_target_attestations(state, previous_epoch))
|
||||
get_matching_target_attestations(state, previous_epoch), stateCache)
|
||||
if previous_epoch_matching_target_balance * 3 >=
|
||||
get_total_active_balance(state) * 2:
|
||||
state.current_justified_epoch = previous_epoch
|
||||
|
@ -640,7 +633,8 @@ func process_justification_and_finalization(state: var BeaconState) =
|
|||
state.justification_bitfield = state.justification_bitfield or (1 shl 1)
|
||||
let current_epoch_matching_target_balance =
|
||||
get_attesting_balance(state,
|
||||
get_matching_target_attestations(state, current_epoch))
|
||||
get_matching_target_attestations(state, current_epoch),
|
||||
stateCache)
|
||||
if current_epoch_matching_target_balance * 3 >=
|
||||
get_total_active_balance(state) * 2:
|
||||
state.current_justified_epoch = current_epoch
|
||||
|
@ -680,7 +674,7 @@ func process_justification_and_finalization(state: var BeaconState) =
|
|||
state.finalized_root = get_block_root(state, state.finalized_epoch)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#crosslinks
|
||||
func process_crosslinks(state: var BeaconState, per_epoch_cache: var StateCache) =
|
||||
func process_crosslinks(state: var BeaconState, stateCache: var StateCache) =
|
||||
## TODO is there a semantic reason for this, or is this just a way to force
|
||||
## copying? If so, why not just `list(foo)` or similar? This is strange. In
|
||||
## this case, for type reasons, don't do weird
|
||||
|
@ -695,13 +689,15 @@ func process_crosslinks(state: var BeaconState, per_epoch_cache: var StateCache)
|
|||
for offset in 0'u64 ..< get_epoch_committee_count(state, epoch):
|
||||
let
|
||||
shard = (get_epoch_start_shard(state, epoch) + offset) mod SHARD_COUNT
|
||||
crosslink_committee = get_crosslink_committee(state, epoch, shard)
|
||||
crosslink_committee =
|
||||
get_crosslink_committee(state, epoch, shard, stateCache)
|
||||
# In general, it'll loop over the same shards twice, and
|
||||
# get_winning_root_and_participants is defined to return
|
||||
# the same results from the previous epoch as current.
|
||||
# TODO cache like before, in 0.5 version of this function
|
||||
(winning_crosslink, attesting_indices) =
|
||||
get_winning_crosslink_and_attesting_indices(state, epoch, shard)
|
||||
get_winning_crosslink_and_attesting_indices(
|
||||
state, epoch, shard, stateCache)
|
||||
if 3'u64 * get_total_balance(state, attesting_indices) >=
|
||||
2'u64 * get_total_balance(state, crosslink_committee):
|
||||
state.current_crosslinks[shard] = winning_crosslink
|
||||
|
@ -715,7 +711,7 @@ func get_base_reward(state: BeaconState, index: ValidatorIndex): Gwei =
|
|||
integer_squareroot(total_balance) div BASE_REWARDS_PER_EPOCH
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#rewards-and-penalties
|
||||
func get_attestation_deltas(state: BeaconState):
|
||||
func get_attestation_deltas(state: BeaconState, stateCache: var StateCache):
|
||||
tuple[a: seq[Gwei], b: seq[Gwei]] =
|
||||
let
|
||||
previous_epoch = get_previous_epoch(state)
|
||||
|
@ -743,8 +739,8 @@ func get_attestation_deltas(state: BeaconState):
|
|||
matching_head_attestations]:
|
||||
let
|
||||
unslashed_attesting_indices =
|
||||
get_unslashed_attesting_indices(state, attestations)
|
||||
attesting_balance = get_attesting_balance(state, attestations)
|
||||
get_unslashed_attesting_indices(state, attestations, stateCache)
|
||||
attesting_balance = get_attesting_balance(state, attestations, stateCache)
|
||||
for index in eligible_validator_indices:
|
||||
if index in unslashed_attesting_indices:
|
||||
rewards[index] +=
|
||||
|
@ -756,11 +752,13 @@ func get_attestation_deltas(state: BeaconState):
|
|||
return (rewards, penalties)
|
||||
|
||||
# Proposer and inclusion delay micro-rewards
|
||||
for index in get_unslashed_attesting_indices(state, matching_source_attestations):
|
||||
for index in get_unslashed_attesting_indices(
|
||||
state, matching_source_attestations, stateCache):
|
||||
doAssert matching_source_attestations.len > 0
|
||||
var attestation = matching_source_attestations[0]
|
||||
for a in matching_source_attestations:
|
||||
if index notin get_attesting_indices(state, a.data, a.aggregation_bitfield):
|
||||
if index notin get_attesting_indices(
|
||||
state, a.data, a.aggregation_bitfield, stateCache):
|
||||
continue
|
||||
if a.inclusion_delay < attestation.inclusion_delay:
|
||||
attestation = a
|
||||
|
@ -774,7 +772,8 @@ func get_attestation_deltas(state: BeaconState):
|
|||
let finality_delay = previous_epoch - state.finalized_epoch
|
||||
if finality_delay > MIN_EPOCHS_TO_INACTIVITY_PENALTY:
|
||||
let matching_target_attesting_indices =
|
||||
get_unslashed_attesting_indices(state, matching_target_attestations)
|
||||
get_unslashed_attesting_indices(
|
||||
state, matching_target_attestations, stateCache)
|
||||
for index in eligible_validator_indices:
|
||||
penalties[index] +=
|
||||
BASE_REWARDS_PER_EPOCH.uint64 * get_base_reward(state, index)
|
||||
|
@ -796,9 +795,11 @@ func get_crosslink_deltas(state: BeaconState, cache: var StateCache):
|
|||
for offset in 0'u64 ..< get_epoch_committee_count(state, epoch):
|
||||
let
|
||||
shard = (get_epoch_start_shard(state, epoch) + offset) mod SHARD_COUNT
|
||||
crosslink_committee = get_crosslink_committee(state, epoch, shard)
|
||||
crosslink_committee =
|
||||
get_crosslink_committee(state, epoch, shard, cache)
|
||||
(winning_crosslink, attesting_indices) =
|
||||
get_winning_crosslink_and_attesting_indices(state, epoch, shard)
|
||||
get_winning_crosslink_and_attesting_indices(
|
||||
state, epoch, shard, cache)
|
||||
attesting_balance = get_total_balance(state, attesting_indices)
|
||||
committee_balance = get_total_balance(state, crosslink_committee)
|
||||
for index in crosslink_committee:
|
||||
|
@ -819,7 +820,7 @@ func process_rewards_and_penalties(
|
|||
return
|
||||
|
||||
let
|
||||
(rewards1, penalties1) = get_attestation_deltas(state)
|
||||
(rewards1, penalties1) = get_attestation_deltas(state, cache)
|
||||
(rewards2, penalties2) = get_crosslink_deltas(state, cache)
|
||||
for i in 0 ..< len(state.validator_registry):
|
||||
increase_balance(state, i.ValidatorIndex, rewards1[i] + rewards2[i])
|
||||
|
@ -897,22 +898,16 @@ func process_final_updates(state: var BeaconState) =
|
|||
state.current_epoch_attestations = @[]
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#per-epoch-processing
|
||||
func get_empty_per_epoch_cache(): StateCache =
|
||||
result.crosslink_committee_cache =
|
||||
initTable[tuple[a: uint64, b: bool], seq[CrosslinkCommittee]]()
|
||||
result.winning_root_participants_cache =
|
||||
initTable[Shard, HashSet[ValidatorIndex]]()
|
||||
|
||||
func processEpoch(state: var BeaconState) =
|
||||
if not (state.slot > GENESIS_SLOT and
|
||||
(state.slot + 1) mod SLOTS_PER_EPOCH == 0):
|
||||
return
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#justification-and-finalization
|
||||
process_justification_and_finalization(state)
|
||||
|
||||
var per_epoch_cache = get_empty_per_epoch_cache()
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#justification-and-finalization
|
||||
process_justification_and_finalization(state, per_epoch_cache)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#crosslinks
|
||||
process_crosslinks(state, per_epoch_cache)
|
||||
|
||||
|
@ -920,8 +915,13 @@ func processEpoch(state: var BeaconState) =
|
|||
process_rewards_and_penalties(state, per_epoch_cache)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#registry-updates
|
||||
# Don't rely on caching here.
|
||||
process_registry_updates(state)
|
||||
|
||||
## Caching here for get_crosslink_committee(...) can break otherwise, since
|
||||
## get_active_validator_indices(...) usually changes.
|
||||
clear(per_epoch_cache.crosslink_committee_cache)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#slashings
|
||||
process_slashings(state)
|
||||
|
||||
|
@ -1006,7 +1006,9 @@ proc updateState*(
|
|||
# that the block is sane.
|
||||
# TODO what should happen if block processing fails?
|
||||
# https://github.com/ethereum/eth2.0-specs/issues/293
|
||||
if processBlock(state, new_block, flags):
|
||||
var per_epoch_cache = get_empty_per_epoch_cache()
|
||||
|
||||
if processBlock(state, new_block, 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
|
||||
|
@ -1059,7 +1061,8 @@ proc updateState*(
|
|||
var old_state = state
|
||||
advanceState(state)
|
||||
|
||||
if processBlock(state.data, blck, flags):
|
||||
var per_epoch_cache = get_empty_per_epoch_cache()
|
||||
if processBlock(state.data, blck, flags, per_epoch_cache):
|
||||
if skipValidation in flags or verifyStateRoot(state.data, blck):
|
||||
# State root is what it should be - we're done!
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ type Timers = enum
|
|||
tBlock = "Process non-epoch slot with block"
|
||||
tEpoch = "Process epoch slot with block"
|
||||
tHashBlock = "Tree-hash block"
|
||||
tShuffle = "Retrieve committee once using get_crosslink_committees_at_slot"
|
||||
tShuffle = "Retrieve committee once using get_crosslink_committee"
|
||||
tAttest = "Combine committee attestations"
|
||||
|
||||
template withTimer(stats: var RunningStat, body: untyped) =
|
||||
|
@ -65,6 +65,7 @@ cli do(slots = 1945,
|
|||
attesters: RunningStat
|
||||
r: Rand
|
||||
blck: BeaconBlock
|
||||
cache = get_empty_per_epoch_cache()
|
||||
|
||||
proc maybeWrite() =
|
||||
if state.slot mod json_interval.uint64 == 0:
|
||||
|
@ -96,18 +97,24 @@ cli do(slots = 1945,
|
|||
# attesterRatio is the fraction of attesters that actually do their
|
||||
# work for every slot - we'll randomize it deterministically to give
|
||||
# some variation
|
||||
let scass = withTimerRet(timers[tShuffle]):
|
||||
get_crosslink_committees_at_slot(state, state.slot)
|
||||
let
|
||||
epoch = slot_to_epoch(state.slot)
|
||||
scass = withTimerRet(timers[tShuffle]):
|
||||
mapIt(
|
||||
0'u64 .. (get_epoch_committee_count(state, epoch) - 1),
|
||||
get_crosslink_committee(state, epoch,
|
||||
(it + get_epoch_start_shard(state, epoch)) mod SHARD_COUNT,
|
||||
cache))
|
||||
|
||||
for scas in scass:
|
||||
var
|
||||
attestation: Attestation
|
||||
first = true
|
||||
|
||||
attesters.push scas.committee.len()
|
||||
attesters.push scas.len()
|
||||
|
||||
withTimer(timers[tAttest]):
|
||||
for v in scas.committee:
|
||||
for v in scas:
|
||||
if (rand(r, high(int)).float * attesterRatio).int <= high(int):
|
||||
if first:
|
||||
attestation = makeAttestation(state, latest_block_root, v, flags)
|
||||
|
|
|
@ -35,13 +35,14 @@ suite "Attestation pool processing" & preset():
|
|||
genBlock = get_initial_beacon_block(genState)
|
||||
|
||||
test "Can add and retrieve simple attestation" & preset():
|
||||
var cache = get_empty_per_epoch_cache()
|
||||
withPool:
|
||||
let
|
||||
# Create an attestation for slot 1!
|
||||
crosslink_committees =
|
||||
get_crosslink_committees_at_slot(state.data.data, state.data.data.slot)
|
||||
crosslink_committee = get_crosslink_committee(state.data.data,
|
||||
slot_to_epoch(state.data.data.slot), 0, cache)
|
||||
attestation = makeAttestation(
|
||||
state.data.data, state.blck.root, crosslink_committees[0].committee[0])
|
||||
state.data.data, state.blck.root, crosslink_committee[0])
|
||||
|
||||
pool.add(state.data.data, attestation)
|
||||
|
||||
|
@ -55,21 +56,22 @@ suite "Attestation pool processing" & preset():
|
|||
attestations.len == 1
|
||||
|
||||
test "Attestations may arrive in any order" & preset():
|
||||
var cache = get_empty_per_epoch_cache()
|
||||
withPool:
|
||||
let
|
||||
# Create an attestation for slot 1!
|
||||
cc0 =
|
||||
get_crosslink_committees_at_slot(state.data.data, state.data.data.slot)
|
||||
cc0 = get_crosslink_committee(state.data.data,
|
||||
slot_to_epoch(state.data.data.slot), 0, cache)
|
||||
attestation0 = makeAttestation(
|
||||
state.data.data, state.blck.root, cc0[0].committee[0])
|
||||
state.data.data, state.blck.root, cc0[0])
|
||||
|
||||
advanceState(state.data)
|
||||
|
||||
let
|
||||
cc1 =
|
||||
get_crosslink_committees_at_slot(state.data.data, state.data.data.slot)
|
||||
cc1 = get_crosslink_committee(state.data.data,
|
||||
slot_to_epoch(state.data.data.slot), 0, cache)
|
||||
attestation1 = makeAttestation(
|
||||
state.data.data, state.blck.root, cc1[0].committee[0])
|
||||
state.data.data, state.blck.root, cc1[0])
|
||||
|
||||
# test reverse order
|
||||
pool.add(state.data.data, attestation1)
|
||||
|
@ -84,15 +86,16 @@ suite "Attestation pool processing" & preset():
|
|||
attestations.len == 1
|
||||
|
||||
test "Attestations should be combined" & preset():
|
||||
var cache = get_empty_per_epoch_cache()
|
||||
withPool:
|
||||
let
|
||||
# Create an attestation for slot 1!
|
||||
cc0 =
|
||||
get_crosslink_committees_at_slot(state.data.data, state.data.data.slot)
|
||||
cc0 = get_crosslink_committee(state.data.data,
|
||||
slot_to_epoch(state.data.data.slot), 0, cache)
|
||||
attestation0 = makeAttestation(
|
||||
state.data.data, state.blck.root, cc0[0].committee[0])
|
||||
state.data.data, state.blck.root, cc0[0])
|
||||
attestation1 = makeAttestation(
|
||||
state.data.data, state.blck.root, cc0[0].committee[1])
|
||||
state.data.data, state.blck.root, cc0[1])
|
||||
|
||||
pool.add(state.data.data, attestation0)
|
||||
pool.add(state.data.data, attestation1)
|
||||
|
@ -106,16 +109,17 @@ suite "Attestation pool processing" & preset():
|
|||
attestations.len == 1
|
||||
|
||||
test "Attestations may overlap, bigger first" & preset():
|
||||
var cache = get_empty_per_epoch_cache()
|
||||
withPool:
|
||||
|
||||
var
|
||||
# Create an attestation for slot 1!
|
||||
cc0 =
|
||||
get_crosslink_committees_at_slot(state.data.data, state.data.data.slot)
|
||||
cc0 = get_crosslink_committee(state.data.data,
|
||||
slot_to_epoch(state.data.data.slot), 0, cache)
|
||||
attestation0 = makeAttestation(
|
||||
state.data.data, state.blck.root, cc0[0].committee[0])
|
||||
state.data.data, state.blck.root, cc0[0])
|
||||
attestation1 = makeAttestation(
|
||||
state.data.data, state.blck.root, cc0[0].committee[1])
|
||||
state.data.data, state.blck.root, cc0[1])
|
||||
|
||||
attestation0.combine(attestation1, {skipValidation})
|
||||
|
||||
|
@ -131,15 +135,16 @@ suite "Attestation pool processing" & preset():
|
|||
attestations.len == 1
|
||||
|
||||
test "Attestations may overlap, smaller first" & preset():
|
||||
var cache = get_empty_per_epoch_cache()
|
||||
withPool:
|
||||
var
|
||||
# Create an attestation for slot 1!
|
||||
cc0 =
|
||||
get_crosslink_committees_at_slot(state.data.data, state.data.data.slot)
|
||||
cc0 = get_crosslink_committee(state.data.data,
|
||||
slot_to_epoch(state.data.data.slot), 0, cache)
|
||||
attestation0 = makeAttestation(
|
||||
state.data.data, state.blck.root, cc0[0].committee[0])
|
||||
state.data.data, state.blck.root, cc0[0])
|
||||
attestation1 = makeAttestation(
|
||||
state.data.data, state.blck.root, cc0[0].committee[1])
|
||||
state.data.data, state.blck.root, cc0[1])
|
||||
|
||||
attestation0.combine(attestation1, {skipValidation})
|
||||
|
||||
|
|
|
@ -76,16 +76,17 @@ suite "Block processing" & preset():
|
|||
var
|
||||
state = genesisState
|
||||
previous_block_root = genesisRoot
|
||||
cache = get_empty_per_epoch_cache()
|
||||
|
||||
# Slot 0 is a finalized slot - won't be making attestations for it..
|
||||
advanceState(state)
|
||||
|
||||
let
|
||||
# Create an attestation for slot 1 signed by the only attester we have!
|
||||
crosslink_committees = get_crosslink_committees_at_slot(state, state.slot)
|
||||
crosslink_committee =
|
||||
get_crosslink_committee(state, slot_to_epoch(state.slot), 0, cache)
|
||||
attestation = makeAttestation(
|
||||
state, previous_block_root,
|
||||
crosslink_committees[0].committee[0])
|
||||
state, previous_block_root, crosslink_committee[0])
|
||||
|
||||
# Some time needs to pass before attestations are included - this is
|
||||
# to let the attestation propagate properly to interested participants
|
||||
|
|
|
@ -63,9 +63,12 @@ func getNextBeaconProposerIndex*(state: BeaconState): ValidatorIndex =
|
|||
# TODO: This is a special version of get_beacon_proposer_index that takes into
|
||||
# account the partial update done at the start of slot processing -
|
||||
# see get_shard_committees_index
|
||||
var next_state = state
|
||||
var
|
||||
next_state = state
|
||||
cache = get_empty_per_epoch_cache()
|
||||
|
||||
next_state.slot += 1
|
||||
get_beacon_proposer_index(next_state)
|
||||
get_beacon_proposer_index(next_state, cache)
|
||||
|
||||
proc addBlock*(
|
||||
state: var BeaconState, previous_block_root: Eth2Digest,
|
||||
|
@ -76,7 +79,8 @@ proc addBlock*(
|
|||
# but avoids some slow block copies
|
||||
|
||||
state.slot += 1
|
||||
let proposer_index = get_beacon_proposer_index(state)
|
||||
var cache = get_empty_per_epoch_cache()
|
||||
let proposer_index = get_beacon_proposer_index(state, cache)
|
||||
state.slot -= 1
|
||||
|
||||
let
|
||||
|
@ -138,25 +142,29 @@ proc makeBlock*(
|
|||
addBlock(next_state, previous_block_root, body)
|
||||
|
||||
proc find_shard_committee(
|
||||
sacs: openArray[CrosslinkCommittee], validator_index: ValidatorIndex): CrosslinkCommittee =
|
||||
for sac in sacs:
|
||||
if validator_index in sac.committee: return sac
|
||||
state: BeaconState, validator_index: ValidatorIndex): auto =
|
||||
let epoch = slot_to_epoch(state.slot)
|
||||
var cache = get_empty_per_epoch_cache()
|
||||
for shard in 0'u64 ..< get_epoch_committee_count(state, epoch):
|
||||
let committee = get_crosslink_committee(state, epoch,
|
||||
(shard + get_epoch_start_shard(state, epoch)) mod SHARD_COUNT, cache)
|
||||
if validator_index in committee:
|
||||
return (committee, shard)
|
||||
doAssert false
|
||||
|
||||
proc makeAttestation*(
|
||||
state: BeaconState, beacon_block_root: Eth2Digest,
|
||||
validator_index: ValidatorIndex, flags: UpdateFlags = {}): Attestation =
|
||||
let
|
||||
sac = find_shard_committee(
|
||||
get_crosslink_committees_at_slot(state, state.slot), validator_index)
|
||||
(committee, shard) = find_shard_committee(state, validator_index)
|
||||
validator = state.validator_registry[validator_index]
|
||||
sac_index = sac.committee.find(validator_index)
|
||||
data = makeAttestationData(state, sac.shard, beacon_block_root)
|
||||
sac_index = committee.find(validator_index)
|
||||
data = makeAttestationData(state, shard, beacon_block_root)
|
||||
|
||||
doAssert sac_index != -1, "find_shard_committe should guarantee this"
|
||||
doAssert sac_index != -1, "find_shard_committee should guarantee this"
|
||||
|
||||
var
|
||||
aggregation_bitfield = BitField.init(sac.committee.len)
|
||||
aggregation_bitfield = BitField.init(committee.len)
|
||||
set_bitfield_bit(aggregation_bitfield, sac_index)
|
||||
|
||||
let
|
||||
|
@ -177,7 +185,7 @@ proc makeAttestation*(
|
|||
data: data,
|
||||
aggregation_bitfield: aggregation_bitfield,
|
||||
signature: sig,
|
||||
custody_bitfield: BitField.init(sac.committee.len)
|
||||
custody_bitfield: BitField.init(committee.len)
|
||||
)
|
||||
|
||||
proc makeTestDB*(tailState: BeaconState, tailBlock: BeaconBlock): BeaconChainDB =
|
||||
|
|
Loading…
Reference in New Issue