diff --git a/beacon_chain/attestation_pool.nim b/beacon_chain/attestation_pool.nim index 54b6a355d..531afa276 100644 --- a/beacon_chain/attestation_pool.nim +++ b/beacon_chain/attestation_pool.nim @@ -38,7 +38,7 @@ proc validate( # TODO half of this stuff is from beaconstate.validateAttestation - merge? - let attestationSlot = get_attestation_slot(state, attestation) + let attestationSlot = get_attestation_data_slot(state, attestation.data) if attestationSlot < state.finalized_epoch.get_epoch_start_slot(): debug "Old attestation", @@ -99,7 +99,7 @@ proc validate( ], attestation.signature, get_domain(state, DOMAIN_ATTESTATION, - slot_to_epoch(get_attestation_slot(state, attestation))), + slot_to_epoch(get_attestation_data_slot(state, attestation.data))), ): notice "Invalid signature", participants return false @@ -174,7 +174,7 @@ proc add*(pool: var AttestationPool, # TODO inefficient data structures.. let - attestationSlot = get_attestation_slot(state, attestation) + attestationSlot = get_attestation_data_slot(state, attestation.data) idx = pool.slotIndex(state, attestationSlot) slotData = addr pool.slots[idx] validation = Validation( @@ -326,7 +326,7 @@ proc resolve*(pool: var AttestationPool, state: BeaconState) = var resolved: seq[Attestation] for k, v in pool.unresolved.mpairs(): - let attestation_slot = get_attestation_slot(state, v.attestation) + let attestation_slot = get_attestation_data_slot(state, v.attestation.data) if v.tries > 8 or attestation_slot < pool.startingSlot: done.add(k) else: diff --git a/beacon_chain/spec/beaconstate.nim b/beacon_chain/spec/beaconstate.nim index a77f95641..d9eade442 100644 --- a/beacon_chain/spec/beaconstate.nim +++ b/beacon_chain/spec/beaconstate.nim @@ -246,10 +246,6 @@ func get_genesis_beacon_state*( deposit_index: 0, ) - for i in 0 ..< SHARD_COUNT: - state.current_crosslinks[i] = Crosslink( - epoch: GENESIS_EPOCH, crosslink_data_root: ZERO_HASH) - # Process genesis deposits for deposit in genesis_validator_deposits: discard process_deposit(state, deposit, flags) @@ -278,25 +274,22 @@ func get_initial_beacon_block*(state: BeaconState): BeaconBlock = # initialized to default values. ) -# https://github.com/ethereum/eth2.0-specs/blob/v0.6.2/specs/core/0_beacon-chain.md#get_attestation_slot -func get_attestation_slot*(state: BeaconState, - attestation: Attestation|PendingAttestation, - committee_count: uint64): Slot = +# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#get_attestation_data_slot +func get_attestation_data_slot*(state: BeaconState, + data: AttestationData, committee_count: uint64): Slot = let - epoch = attestation.data.target_epoch - offset = (attestation.data.shard + SHARD_COUNT - - get_epoch_start_shard(state, epoch)) mod SHARD_COUNT + offset = (data.crosslink.shard + SHARD_COUNT - + get_epoch_start_shard(state, data.target_epoch)) mod SHARD_COUNT # TODO re-instate once double-check correct conditions in attestation pool - #get_epoch_start_slot(epoch) + offset div (committee_count div SLOTS_PER_EPOCH) - attestation.data.slot + #get_epoch_start_slot(data.target_epoch) + offset div (committee_count div SLOTS_PER_EPOCH) + data.slot # This is the slower (O(n)), spec-compatible signature. -func get_attestation_slot*(state: BeaconState, - attestation: Attestation|PendingAttestation): Slot = - let epoch = attestation.data.target_epoch - get_attestation_slot( - state, attestation, get_epoch_committee_count(state, epoch)) +func get_attestation_data_slot*(state: BeaconState, + data: AttestationData): Slot = + get_attestation_data_slot( + state, data, get_epoch_committee_count(state, data.target_epoch)) # https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#get_block_root_at_slot func get_block_root_at_slot*(state: BeaconState, @@ -425,8 +418,9 @@ func get_attesting_indices*(state: BeaconState, ## possible to follow the spec more literally. result = initSet[ValidatorIndex]() let committee = - get_crosslink_committee(state, attestation_data.target_epoch, - attestation_data.shard, stateCache) + get_crosslink_committee( + state, attestation_data.target_epoch, attestation_data.crosslink.shard, + stateCache) doAssert verify_bitfield(bitfield, len(committee)) for i, index in committee: if get_bitfield_bit(bitfield, i): @@ -508,7 +502,7 @@ proc checkAttestation*( let data = attestation.data - attestation_slot = get_attestation_slot(state, attestation) + attestation_slot = get_attestation_data_slot(state, attestation.data) if not (attestation_slot + MIN_ATTESTATION_INCLUSION_DELAY <= stateSlot): warn("Attestation too new", @@ -602,13 +596,15 @@ proc makeAttestationData*( AttestationData( slot: state.slot, - # 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 - previous_crosslink_root: hash_tree_root(state.current_crosslinks[shard]), source_epoch: state.current_justified_epoch, source_root: state.current_justified_root, - target_epoch: slot_to_epoch(state.slot) + target_epoch: slot_to_epoch(state.slot), + crosslink: Crosslink( + # Alternative is to put this offset into all callers + shard: shard + get_epoch_start_shard(state, slot_to_epoch(state.slot)), + parent_root: hash_tree_root(state.current_crosslinks[shard]), + data_root: Eth2Digest(), # Stub in phase0 + ) ) diff --git a/beacon_chain/spec/datatypes.nim b/beacon_chain/spec/datatypes.nim index 31ad5b119..11967fbe1 100644 --- a/beacon_chain/spec/datatypes.nim +++ b/beacon_chain/spec/datatypes.nim @@ -130,9 +130,7 @@ type target_root*: Eth2Digest # Crosslink vote - shard*: uint64 - previous_crosslink_root*: Eth2Digest - crosslink_data_root*: Eth2Digest + crosslink*: Crosslink # https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#attestationdataandcustodybit AttestationDataAndCustodyBit* = object @@ -309,16 +307,20 @@ type effective_balance*: uint64 ##\ ## Effective balance - # https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#crosslink + # https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#crosslink Crosslink* = object - epoch*: Epoch ##\ - ## Epoch number + shard*: Shard ##\ + ## Shard number - previous_crosslink_root*: Eth2Digest ##\ + start_epoch*: Epoch + end_epoch*: Epoch ##\ + ## Crosslinking data from epochs [start....end-1] + + parent_root*: Eth2Digest ##\ ## Root of the previous crosslink - crosslink_data_root*: Eth2Digest ##\ - ## Shard data since the previous crosslink + data_root*: Eth2Digest ##\ + ## Root of the crosslinked shard data since the previous crosslink # https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#pendingattestation PendingAttestation* = object @@ -451,8 +453,7 @@ func shortLog*(v: AttestationData): auto = shortLog(v.beacon_block_root), humaneEpochNum(v.source_epoch), shortLog(v.target_root), shortLog(v.source_root), - v.shard, v.previous_crosslink_root, - shortLog(v.crosslink_data_root) + v.crosslink ) chronicles.formatIt Slot: it.humaneSlotNum diff --git a/beacon_chain/state_transition.nim b/beacon_chain/state_transition.nim index a1088e088..73fdf852b 100644 --- a/beacon_chain/state_transition.nim +++ b/beacon_chain/state_transition.nim @@ -264,7 +264,7 @@ proc processAttestations( # Spec content let attestation_slot = - get_attestation_slot(state, attestation, committee_count) + get_attestation_data_slot(state, attestation.data, committee_count) let pending_attestation = PendingAttestation( data: attestation.data, aggregation_bitfield: attestation.aggregation_bitfield, @@ -507,7 +507,7 @@ func get_matching_head_attestations(state: BeaconState, epoch: Epoch): filterIt( get_matching_source_attestations(state, epoch), it.data.beacon_block_root == - get_block_root_at_slot(state, get_attestation_slot(state, it)) + get_block_root_at_slot(state, get_attestation_data_slot(state, it.data)) ) func get_unslashed_attesting_indices( @@ -528,15 +528,6 @@ func get_attesting_balance( get_total_balance(state, get_unslashed_attesting_indices( state, attestations, stateCache)) -func get_crosslink_from_attestation_data( - state: BeaconState, data: AttestationData): Crosslink = - Crosslink( - epoch: min(data.target_epoch, - state.current_crosslinks[data.shard].epoch + MAX_CROSSLINK_EPOCHS), - previous_crosslink_root: data.previous_crosslink_root, - crosslink_data_root: data.crosslink_data_root, - ) - # Not exactly in spec, but for get_winning_crosslink_and_attesting_indices func lowerThan(candidate, current: Eth2Digest): bool = # return true iff candidate is "lower" than current, per spec rule: @@ -549,62 +540,48 @@ func get_winning_crosslink_and_attesting_indices( 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 - shard_attestations = + attestations = filterIt( - get_matching_source_attestations(state, epoch), it.data.shard == shard) - shard_crosslinks = - mapIt(shard_attestations, - get_crosslink_from_attestation_data(state, it.data)) - # TODO this seems like a lot of hash_tree_root'ing on same data - candidate_crosslinks = - filterIt(shard_crosslinks, + get_matching_source_attestations(state, epoch), + it.data.crosslink.shard == shard) + # TODO don't keep h_t_r'ing state.current_crosslinks[shard] + crosslinks = + filterIt( + mapIt(attestations, it.data.crosslink), hash_tree_root(state.current_crosslinks[shard]) in # TODO pointless memory allocation, etc. - @[it.previous_crosslink_root, hash_tree_root(it)]) + @[it.parent_root, hash_tree_root(it)]) - if len(candidate_crosslinks) == 0: + # default=Crosslink() + if len(crosslinks) == 0: return (Crosslink(), initSet[ValidatorIndex]()) - ## TODO check if should cache this again, as with 0.5 - ## var attestations_for = initTable[Eth2Digest, seq[PendingAttestation]]() - ## for valid_attestation in valid_attestations: - ## if valid_attestation.data.crosslink_data_root in attestations_for: - ## attestations_for[valid_attestation.data.crosslink_data_root].add( - ## valid_attestation) - ## else: - ## attestations_for[valid_attestation.data.crosslink_data_root] = - ## @[valid_attestation] - ## TODO either way, this nested function not great; {.fastcall.} pragma - ## not directly applicable either, since it does need some closure - func get_attestations_for(crosslink: Crosslink): seq[PendingAttestation] = - filterIt(shard_attestations, - get_crosslink_from_attestation_data(state, it.data) == crosslink) - ## Winning crosslink has the crosslink data root with the most balance voting ## for it (ties broken lexicographically) var winning_crosslink: Crosslink winning_crosslink_balance = 0.Gwei - for candidate_crosslink in candidate_crosslinks: + for candidate_crosslink in crosslinks: ## TODO check if should cache this again ## 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), stateCache) + state, + filterIt(attestations, it.data.crosslink == candidate_crosslink), + stateCache) if (crosslink_balance > winning_crosslink_balance or (winning_crosslink_balance == crosslink_balance and - lowerThan(winning_crosslink.crosslink_data_root, - candidate_crosslink.crosslink_data_root))): + lowerThan(winning_crosslink.data_root, + candidate_crosslink.data_root))): winning_crosslink = candidate_crosslink winning_crosslink_balance = crosslink_balance + let winning_attestations = + filterIt(attestations, it.data.crosslink == winning_crosslink) (winning_crosslink, - get_unslashed_attesting_indices(state, - get_attestations_for(winning_crosslink), stateCache)) + get_unslashed_attesting_indices(state, winning_attestations, 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(