From 35ac0c4ddccbb3b0cad8cc198711e3b569193edd Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Fri, 14 Dec 2018 10:12:39 -0600 Subject: [PATCH] spec updates (#39) * some minor renames * fix some leftover block slot references (replaced by state slot) * fix some more uint underflows * update epoch calculations (reward et al) * now works past 100 epochs at least with empty blocks (very slow) --- beacon_chain/beacon_node.nim | 2 +- beacon_chain/spec/beaconstate.nim | 19 +- beacon_chain/spec/datatypes.nim | 2 +- beacon_chain/spec/helpers.nim | 19 +- beacon_chain/state_transition.nim | 574 +++++++++++++++++------------- tests/testutil.nim | 7 +- 6 files changed, 343 insertions(+), 280 deletions(-) diff --git a/beacon_chain/beacon_node.nim b/beacon_chain/beacon_node.nim index 2c98d2f35..35cfdbd0b 100644 --- a/beacon_chain/beacon_node.nim +++ b/beacon_chain/beacon_node.nim @@ -168,7 +168,7 @@ proc scheduleCycleActions(node: BeaconNode) = # Schedule attestations let - committeesIdx = get_shard_and_committees_index(node.beaconState, slot.uint64) + committeesIdx = get_shard_committees_index(node.beaconState, slot.uint64) for shard in node.beaconState.shard_committees_at_slots[committees_idx]: for validatorIdx in shard.committee: diff --git a/beacon_chain/spec/beaconstate.nim b/beacon_chain/spec/beaconstate.nim index 2c2c87a33..ded145658 100644 --- a/beacon_chain/spec/beaconstate.nim +++ b/beacon_chain/spec/beaconstate.nim @@ -188,7 +188,6 @@ func on_startup*(initial_validator_deposits: openArray[Deposit], # Recent state latest_state_recalculation_slot: INITIAL_SLOT_NUMBER, - latest_block_roots: repeat(ZERO_HASH, LATEST_BLOCK_ROOTS_COUNT), # PoW receipt root processed_pow_receipt_root: processed_pow_receipt_root, @@ -228,18 +227,10 @@ func on_startup*(initial_validator_deposits: openArray[Deposit], func get_block_root*(state: BeaconState, slot: uint64): Eth2Digest = - let earliest_slot_in_array = - state.slot - len(state.latest_block_roots).uint64 - assert earliest_slot_in_array <= slot - assert slot < state.slot - state.latest_block_roots[(slot - earliest_slot_in_array).int] - -func append_to_recent_block_roots*(old_block_roots: seq[Eth2Digest], - parent_slot, current_slot: uint64, - parent_hash: Eth2Digest): seq[Eth2Digest] = - let d = current_slot - parent_slot - result = old_block_roots - result.add repeat(parent_hash, d) + doAssert slot + len(state.latest_block_roots).uint64 > state.slot + doAssert slot < state.slot + state.latest_block_roots[ + (slot + len(state.latest_block_roots).uint64 - state.slot).int] func get_attestation_participants*(state: BeaconState, attestation_data: AttestationData, @@ -252,7 +243,7 @@ func get_attestation_participants*(state: BeaconState, # TODO bitfield type needed, once bit order settles down # TODO iterator candidate let - sncs_for_slot = get_shard_and_committees_for_slot( + sncs_for_slot = get_shard_committees_at_slot( state, attestation_data.slot) for snc in sncs_for_slot: diff --git a/beacon_chain/spec/datatypes.nim b/beacon_chain/spec/datatypes.nim index bba9f45a0..9d6ee5293 100644 --- a/beacon_chain/spec/datatypes.nim +++ b/beacon_chain/spec/datatypes.nim @@ -277,7 +277,7 @@ type latest_crosslinks*: array[SHARD_COUNT, CrosslinkRecord] latest_state_recalculation_slot*: uint64 - latest_block_roots*: seq[Eth2Digest] ##\ + latest_block_roots*: array[LATEST_BLOCK_ROOTS_COUNT, Eth2Digest] ##\ ## Needed to process attestations, older to newer latest_penalized_exit_balances*: seq[uint64] ##\ ## Balances penalized in the current withdrawal period diff --git a/beacon_chain/spec/helpers.nim b/beacon_chain/spec/helpers.nim index 14dccfeb6..32cb053bd 100644 --- a/beacon_chain/spec/helpers.nim +++ b/beacon_chain/spec/helpers.nim @@ -92,21 +92,14 @@ func repeat_hash*(v: Eth2Digest, n: SomeInteger): Eth2Digest = result = eth2hash(result.data) dec n -func get_shard_and_committees_index*(state: BeaconState, slot: uint64): uint64 = +func get_shard_committees_index*(state: BeaconState, slot: uint64): uint64 = # TODO spec unsigned-unsafe here - let earliest_slot_in_array = - if state.latest_state_recalculation_slot > EPOCH_LENGTH.uint64: - state.latest_state_recalculation_slot - EPOCH_LENGTH - else: - 0 + doAssert slot + (state.slot mod EPOCH_LENGTH) + EPOCH_LENGTH > state.slot + slot + (state.slot mod EPOCH_LENGTH) + EPOCH_LENGTH - state.slot - doAssert earliest_slot_in_array <= slot and - slot < earliest_slot_in_array + EPOCH_LENGTH * 2 - slot - earliest_slot_in_array - -proc get_shard_and_committees_for_slot*( +proc get_shard_committees_at_slot*( state: BeaconState, slot: uint64): seq[ShardCommittee] = - let index = state.get_shard_and_committees_index(slot) + let index = state.get_shard_committees_index(slot) state.shard_committees_at_slots[index] func get_beacon_proposer_index*(state: BeaconState, slot: uint64): Uint24 = @@ -119,7 +112,7 @@ func get_beacon_proposer_index*(state: BeaconState, slot: uint64): Uint24 = ## ## idx in Vidx == p(i mod N), pi being a random permutation of validators indices (i.e. a committee) - let idx = get_shard_and_committees_index(state, slot) + let idx = get_shard_committees_index(state, slot) state.shard_committees_at_slots[idx][0].committee.mod_get(slot) func int_sqrt*(n: SomeInteger): SomeInteger = diff --git a/beacon_chain/state_transition.nim b/beacon_chain/state_transition.nim index 10c48d683..119364402 100644 --- a/beacon_chain/state_transition.nim +++ b/beacon_chain/state_transition.nim @@ -354,7 +354,8 @@ func processSlot(state: var BeaconState, latest_block: BeaconBlock) = previous_block_root = Eth2Digest(data: hash_tree_root(latest_block)) for i in 0 ..< state.latest_block_roots.len - 1: state.latest_block_roots[i] = state.latest_block_roots[i + 1] - state.latest_block_roots[^1] = previous_block_root + state.latest_block_roots[state.latest_block_roots.len - 1] = + previous_block_root if state.slot mod LATEST_BLOCK_ROOTS_COUNT == 0: state.batched_block_roots.add(merkle_root(state.latest_block_roots)) @@ -393,278 +394,355 @@ func lowerThan(candidate, current: Eth2Digest): bool = if v > candidate.data[i]: return true return false -func processEpoch(state: var BeaconState, blck: BeaconBlock) = - ## Epoch processing happens every time we've passed EPOCH_LENGTH blocks. - ## Because some slots may be skipped, it may happen that we go through the - ## loop more than once - each time the latest_state_recalculation_slot will be - ## increased by EPOCH_LENGTH. - ## +func processEpoch(state: var BeaconState) = ## https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#per-epoch-processing - while blck.slot >= EPOCH_LENGTH.uint64 + state.latest_state_recalculation_slot: - # Convenience shortcut, from spec - let s = state.latest_state_recalculation_slot + if state.slot mod EPOCH_LENGTH != 0: + return - # Precomputation + # Precomputation + let + active_validator_indices = + get_active_validator_indices(state.validator_registry) + total_balance = sum_effective_balances(state, active_validator_indices) + total_balance_in_eth = total_balance div GWEI_PER_ETH + + # The per-slot maximum interest rate is `2/reward_quotient`.) + base_reward_quotient = BASE_REWARD_QUOTIENT * int_sqrt(total_balance_in_eth) + + func base_reward(v: ValidatorRecord): uint64 = + get_effective_balance(v) div base_reward_quotient.uint64 div 4 + + func inactivity_penalty( + v: ValidatorRecord, slots_since_finality: uint64): uint64 = + base_reward(v) + + get_effective_balance(v) * + slots_since_finality div INACTIVITY_PENALTY_QUOTIENT + + # TODO doing this with iterators failed: + # https://github.com/nim-lang/Nim/issues/9827 + let + this_epoch_attestations = + filterIt(state.latest_attestations, + state.slot <= it.data.slot + EPOCH_LENGTH and + it.data.slot < state.slot) + + this_epoch_boundary_attestations = + boundary_attestations( + state, get_block_root(state, state.slot-EPOCH_LENGTH), + this_epoch_attestations) + + this_epoch_boundary_attesters = + get_epoch_boundary_attesters(state, this_epoch_attestations) + + this_epoch_boundary_attesting_balance = + sum_effective_balances(state, this_epoch_boundary_attesters) + + let + previous_epoch_attestations = filterIt( + state.latest_attestations, + state.slot <= it.data.slot + 2 * EPOCH_LENGTH and + it.data.slot + EPOCH_LENGTH < state.slot) + + previous_epoch_attesters = flatten(mapIt( + previous_epoch_attestations, + get_attestation_participants(state, it.data, it.participation_bitfield) + )) + + let # Previous epoch justified + previous_epoch_justified_attestations = filterIt( + concat(this_epoch_attestations, previous_epoch_attestations), + it.data.justified_slot == state.previous_justified_slot + ) + + previous_epoch_justified_attesters = flatten(mapIt( + previous_epoch_justified_attestations, + get_attestation_participants(state, it.data, it.participation_bitfield) + )) + + previous_epoch_justified_attesting_balance = + sum_effective_balances(state, previous_epoch_justified_attesters) + + let # Previous epoch boundary + # TODO check this with spec... + negative_uint_hack = + if state.slot < 2 * EPOCH_LENGTH: 0'u64 else: state.slot - 2 * EPOCH_LENGTH + previous_epoch_boundary_attestations = + boundary_attestations( + state, get_block_root(state, negative_uint_hack), + previous_epoch_attestations) + + previous_epoch_boundary_attesters = flatten(mapIt( + previous_epoch_boundary_attestations, + get_attestation_participants(state, it.data, it.participation_bitfield) + )) + + previous_epoch_boundary_attesting_balance = + sum_effective_balances(state, previous_epoch_boundary_attesters) + + let # Previous epoch head + previous_epoch_head_attestations = + filterIt( + previous_epoch_attestations, + it.data.beacon_block_root == get_block_root(state, it.data.slot)) + + previous_epoch_head_attesters = flatten(mapIt( + previous_epoch_head_attestations, + get_attestation_participants(state, it.data, it.participation_bitfield) + )) + + previous_epoch_head_attesting_balance = + sum_effective_balances(state, previous_epoch_head_attesters) + + # TODO this is really hairy - we cannot capture `state` directly, but we + # can capture a pointer to it - this is safe because we don't leak + # these closures outside this scope, but still.. + let statePtr = state.addr + func attesting_validators( + shard_committee: ShardCommittee, shard_block_root: Eth2Digest): seq[Uint24] = + flatten( + mapIt( + filterIt(concat(this_epoch_attestations, previous_epoch_attestations), + it.data.shard == shard_committee.shard and + it.data.shard_block_root == shard_block_root), + get_attestation_participants(statePtr[], it.data, it.participation_bitfield))) + + func winning_hash(obj: ShardCommittee): Eth2Digest = + # * Let `winning_hash(obj)` be the winning `shard_block_root` value. + # ... such that `sum([get_effective_balance(v) for v in attesting_validators(obj, shard_block_root)])` + # is maximized (ties broken by favoring lower `shard_block_root` values). + let candidates = + mapIt( + filterIt(concat(this_epoch_attestations, previous_epoch_attestations), + it.data.shard == obj.shard), + it.data.shard_block_root) + + # TODO not covered by spec! + if candidates.len == 0: + return + + var max_hash = candidates[0] + var max_val = + sum_effective_balances(statePtr[], attesting_validators(obj, max_hash)) + for candidate in candidates[1..^1]: + let val = sum_effective_balances(statePtr[], attesting_validators(obj, candidate)) + if val > max_val or (val == max_val and candidate.lowerThan(max_hash)): + max_hash = candidate + max_val = val + max_hash + + func attesting_validators(obj: ShardCommittee): seq[Uint24] = + attesting_validators(obj, winning_hash(obj)) + + func total_attesting_balance(obj: ShardCommittee): uint64 = + sum_effective_balances(statePtr[], attesting_validators(obj)) + + func total_balance_sac(obj: ShardCommittee): uint64 = + sum_effective_balances(statePtr[], obj.committee) + + func inclusion_slot(v: Uint24): uint64 = + for a in statePtr[].latest_attestations: + if v in get_attestation_participants(statePtr[], a.data, a.participation_bitfield): + return a.slot_included + assert false # shouldn't happen.. + + func inclusion_distance(v: Uint24): uint64 = + for a in statePtr[].latest_attestations: + if v in get_attestation_participants(statePtr[], a.data, a.participation_bitfield): + return a.slot_included - a.data.slot + assert false # shouldn't happen.. + + block: # Receipt roots + if state.slot mod POW_RECEIPT_ROOT_VOTING_PERIOD == 0: + for x in state.candidate_pow_receipt_roots: + if x.votes * 2 >= POW_RECEIPT_ROOT_VOTING_PERIOD: + state.processed_pow_receipt_root = x.candidate_pow_receipt_root + break + state.candidate_pow_receipt_roots = @[] + + block: # Justification + state.previous_justified_slot = state.justified_slot + + # TODO where's that bitfield type when you need it? + # TODO why are all bits kept? + state.justification_bitfield = state.justification_bitfield shl 1 + + if 3'u64 * previous_epoch_boundary_attesting_balance >= + 2'u64 * total_balance: + state.justification_bitfield = state.justification_bitfield or 2 + state.justified_slot = state.slot - 2 * EPOCH_LENGTH + + if 3'u64 * this_epoch_boundary_attesting_balance >= + 2'u64 * total_balance: + state.justification_bitfield = state.justification_bitfield or 1 + state.justified_slot = state.slot - 1 * EPOCH_LENGTH + + block: # Finalization + if + (state.previous_justified_slot == state.slot - 2 * EPOCH_LENGTH and + state.justification_bitfield mod 4 == 3) or + (state.previous_justified_slot == state.slot - 3 * EPOCH_LENGTH and + state.justification_bitfield mod 8 == 7) or + (state.previous_justified_slot == state.slot - 4 * EPOCH_LENGTH and + state.justification_bitfield mod 16 in [15'u64, 14]): + state.finalized_slot = state.justified_slot + + block: # Crosslinks + for sac in state.shard_committees_at_slots: + for shard_committee in sac: + if 3'u64 * total_attesting_balance(shard_committee) >= + 2'u64 * total_balance_sac(shard_committee): + state.latest_crosslinks[shard_committee.shard] = CrosslinkRecord( + slot: state.latest_state_recalculation_slot + EPOCH_LENGTH, + shard_block_root: winning_hash(shard_committee)) + + block: # Justification and finalization let - active_validator_indices = - get_active_validator_indices(state.validator_registry) - total_balance = sum_effective_balances(state, active_validator_indices) - total_balance_in_eth = total_balance div GWEI_PER_ETH + slots_since_finality = state.slot - state.finalized_slot - # The per-slot maximum interest rate is `2/reward_quotient`.) - reward_quotient = BASE_REWARD_QUOTIENT * int_sqrt(total_balance_in_eth) + if slots_since_finality <= 4'u64 * EPOCH_LENGTH: + # Expected FFG source + for v in previous_epoch_justified_attesters: + state.validator_registry[v].balance += adjust_for_inclusion_distance( + base_reward(state.validator_registry[v]) * + previous_epoch_justified_attesting_balance div total_balance, + inclusion_distance(v)) - # TODO not in spec, convenient - epoch_boundary_root = get_block_root(state, s) + for v in active_validator_indices: + if v notin previous_epoch_justified_attesters: + state.validator_registry[v].balance -= + base_reward(state.validator_registry[v]) - proc base_reward(v: ValidatorRecord): uint64 = - get_effective_balance(v) div reward_quotient.uint64 + # Expected FFG target: + for v in previous_epoch_boundary_attesters: + state.validator_registry[v].balance += adjust_for_inclusion_distance( + base_reward(state.validator_registry[v]) * + previous_epoch_boundary_attesting_balance div total_balance, + inclusion_distance(v)) - # TODO doing this with iterators failed: - # https://github.com/nim-lang/Nim/issues/9827 - let - this_epoch_attestations = filterIt(state.latest_attestations, - s <= it.data.slot and it.data.slot < s + EPOCH_LENGTH) + for v in active_validator_indices: + if v notin previous_epoch_boundary_attesters: + state.validator_registry[v].balance -= + base_reward(state.validator_registry[v]) - this_epoch_boundary_attestations = - boundary_attestations(state, epoch_boundary_root, - this_epoch_attestations) + # Expected beacon chain head: + for v in previous_epoch_head_attesters: + state.validator_registry[v].balance += adjust_for_inclusion_distance( + base_reward(state.validator_registry[v]) * + previous_epoch_head_attesting_balance div total_balance, + inclusion_distance(v)) - this_epoch_boundary_attesters = - get_epoch_boundary_attesters(state, this_epoch_attestations) + for v in active_validator_indices: + if v notin previous_epoch_head_attesters: + state.validator_registry[v].balance -= + base_reward(state.validator_registry[v]) - this_epoch_boundary_attesting_balance = - sum_effective_balances(state, this_epoch_boundary_attesters) + else: + for v in active_validator_indices: + let validator = addr state.validator_registry[v] + if v notin previous_epoch_justified_attesters: + validator[].balance -= + inactivity_penalty(validator[], slots_since_finality) + if v notin previous_epoch_boundary_attesters: + validator[].balance -= + inactivity_penalty(validator[], slots_since_finality) + if v notin previous_epoch_head_attesters: + validator[].balance -= + inactivity_penalty(validator[], slots_since_finality) + if validator[].status == EXITED_WITH_PENALTY: + validator[].balance -= + 3'u64 * inactivity_penalty(validator[], slots_since_finality) - let - previous_epoch_attestations = filterIt(state.latest_attestations, - s <= it.data.slot + EPOCH_LENGTH and it.data.slot < s) + block: # Attestation inclusion + for v in previous_epoch_attesters: + let proposer_index = + get_beacon_proposer_index(state, inclusion_slot(v)) + state.validator_registry[proposer_index].balance += + base_reward(state.validator_registry[v]) div INCLUDER_REWARD_QUOTIENT - previous_epoch_boundary_attestations = - boundary_attestations(state, epoch_boundary_root, - previous_epoch_attestations) + block: # Crosslinks + for sac in state.shard_committees_at_slots[0 ..< EPOCH_LENGTH]: + for obj in sac: + for vindex in obj.committee: + let v = state.validator_registry[vindex].addr - previous_epoch_boundary_attesters = - get_epoch_boundary_attesters(state, previous_epoch_boundary_attestations) + if vindex in attesting_validators(obj): + v.balance += adjust_for_inclusion_distance( + base_reward(v[]) * total_attesting_balance(obj) div + total_balance_sac(obj), + inclusion_distance(vindex)) + else: + v.balance -= base_reward(v[]) - previous_epoch_boundary_attesting_balance = - sum_effective_balances(state, this_epoch_boundary_attesters) + block: # Validator registry + if state.finalized_slot > state.validator_registry_latest_change_slot and + allIt(state.shard_committees_at_slots, + allIt(it, + state.latest_crosslinks[it.shard].slot > + state.validator_registry_latest_change_slot)): + update_validator_registry(state) + state.validator_registry_latest_change_slot = state.slot + for i in 0.. max_val or (val == max_val and candidate.lowerThan(max_hash)): - max_hash = candidate - max_val = val - max_hash + else: + # If a validator registry change does NOT happen + for i in 0..= POW_RECEIPT_ROOT_VOTING_PERIOD: - state.processed_pow_receipt_root = x.candidate_pow_receipt_root - break - state.candidate_pow_receipt_roots = @[] - - block: # Justification - state.previous_justified_slot = state.justified_slot - - # TODO where's that bitfield type when you need it? - # TODO why are all bits kept? - state.justification_bitfield = state.justification_bitfield shl 1 - - if 3'u64 * previous_epoch_boundary_attesting_balance >= - 2'u64 * total_balance: - state.justification_bitfield = state.justification_bitfield or 2 - state.justified_slot = state.slot - 2 * EPOCH_LENGTH - - if 3'u64 * this_epoch_boundary_attesting_balance >= - 2'u64 * total_balance: - state.justification_bitfield = state.justification_bitfield or 1 - state.justified_slot = state.slot - 1 * EPOCH_LENGTH - - block: # Finalization - if - (state.previous_justified_slot == state.slot - 2 * EPOCH_LENGTH and - state.justification_bitfield mod 4 == 3) or - (state.previous_justified_slot == state.slot - 3 * EPOCH_LENGTH and - state.justification_bitfield mod 8 == 7) or - (state.previous_justified_slot == state.slot - 4 * EPOCH_LENGTH and - state.justification_bitfield mod 16 in [15'u64, 14]): - state.finalized_slot = state.justified_slot - - block: # Crosslinks - for sac in state.shard_committees_at_slots: - for obj in sac: - if 3'u64 * total_attesting_balance(obj) >= - 2'u64 * total_balance_sac(obj): - state.latest_crosslinks[obj.shard] = CrosslinkRecord( - slot: state.latest_state_recalculation_slot + EPOCH_LENGTH, - shard_block_root: winning_hash(obj)) - - block: # Justification and finalization rewards and penalties - let - slots_since_finality = blck.slot - state.finalized_slot - - if slots_since_finality <= 4'u64 * EPOCH_LENGTH: - for v in previous_epoch_boundary_attesters: - state.validator_registry[v].balance += adjust_for_inclusion_distance( - base_reward(state.validator_registry[v]) * - previous_epoch_boundary_attesting_balance div total_balance, - inclusion_distance(v)) - - for v in active_validator_indices: - if v notin previous_epoch_boundary_attesters: - state.validator_registry[v].balance -= - base_reward(state.validator_registry[v]) - else: - # Any validator in `prev_cycle_boundary_attesters` sees their balance - # unchanged. - # Others might get penalized: - for vindex, v in state.validator_registry.mpairs(): - if (v.status == ACTIVE and - vindex.Uint24 notin previous_epoch_boundary_attesters) or - v.status == EXITED_WITH_PENALTY: - v.balance -= base_reward(v) + - get_effective_balance(v) * slots_since_finality div - INACTIVITY_PENALTY_QUOTIENT - - for v in previous_epoch_boundary_attesters: - let proposer_index = - get_beacon_proposer_index(state, inclusion_slot(v)) - state.validator_registry[proposer_index].balance += - base_reward(state.validator_registry[v]) div - INCLUDER_REWARD_QUOTIENT - - block: # Crosslink rewards and penalties - for sac in state.shard_committees_at_slots[0 ..< EPOCH_LENGTH]: - for obj in sac: - for vindex in obj.committee: - let v = state.validator_registry[vindex].addr - - if vindex in attesting_validators(obj): - v.balance += adjust_for_inclusion_distance( - base_reward(v[]) * total_attesting_balance(obj) div total_balance_sac(obj), - inclusion_distance(vindex)) - else: - v.balance -= base_reward(v[]) - - block: # Validator registry - - if state.finalized_slot > state.validator_registry_latest_change_slot and - allIt(state.shard_committees_at_slots, - allIt(it, - state.latest_crosslinks[it.shard].slot > - state.validator_registry_latest_change_slot)): - update_validator_registry(state) - state.validator_registry_latest_change_slot = state.slot - for i in 0.. 0 and - state.persistent_committee_reassignments[0].slot <= s: - let reassignment = state.persistent_committee_reassignments[0] - state.persistent_committee_reassignments.delete(0) - for committee in state.persistent_committees.mitems(): - if reassignment.validator_index in committee: - committee.delete(committee.find(reassignment.validator_index)) - state.persistent_committees[reassignment.shard.int].add( - reassignment.validator_index) + while len(state.persistent_committee_reassignments) > 0 and + state.persistent_committee_reassignments[0].slot <= state.slot: + let reassignment = state.persistent_committee_reassignments[0] + state.persistent_committee_reassignments.delete(0) + for committee in state.persistent_committees.mitems(): + if reassignment.validator_index in committee: + committee.delete(committee.find(reassignment.validator_index)) + state.persistent_committees[reassignment.shard.int].add( + reassignment.validator_index) - block: # Final updates - # TODO Remove all attestation records older than slot `s`. - state.latest_block_roots = state.latest_block_roots[EPOCH_LENGTH..^1] + block: # Final updates + state.latest_attestations.keepItIf( + not (it.data.slot + EPOCH_LENGTH < state.slot) + ) proc updateState*(state: BeaconState, latest_block: BeaconBlock, new_block: Option[BeaconBlock]): @@ -716,7 +794,7 @@ proc updateState*(state: BeaconState, latest_block: BeaconBlock, true # Heavy updates that happen for every epoch - these never fail (or so we hope) - processEpoch(new_state, latest_block) + processEpoch(new_state) # State update never fails, but block validation might... (new_state, block_ok) diff --git a/tests/testutil.nim b/tests/testutil.nim index 2032e2050..b8248094e 100644 --- a/tests/testutil.nim +++ b/tests/testutil.nim @@ -73,13 +73,14 @@ func makeGenesisBlock*(state: BeaconState): BeaconBlock = func makeBlock*( state: BeaconState, latest_block: BeaconBlock): BeaconBlock = + var next_state = state + next_state.slot += 1 let - new_slot = state.slot + 1 proposer = state.validator_registry[ - get_beacon_proposer_index(state, new_slot)] + get_beacon_proposer_index(next_state, next_state.slot)] var new_block = BeaconBlock( - slot: new_slot, + slot: next_state.slot, state_root: Eth2Digest(data: hash_tree_root(state)), randao_reveal: hackReveal(proposer) )