diff --git a/beacon_chain/spec/datatypes.nim b/beacon_chain/spec/datatypes.nim index c08cce78c..4efa4a286 100644 --- a/beacon_chain/spec/datatypes.nim +++ b/beacon_chain/spec/datatypes.nim @@ -301,8 +301,10 @@ type current_crosslinks*: array[SHARD_COUNT, Crosslink] # Finality - justification_bits*: uint64 ##\ + justification_bits*: uint8 ##\ ## Bit set for every recent justified epoch + ## Model a Bitvector[4] as a one-byte uint, which should remain consistent + ## with ssz/hashing. previous_justified_checkpoint*: Checkpoint ##\ ## Previous epoch snapshot diff --git a/beacon_chain/spec/state_transition_epoch.nim b/beacon_chain/spec/state_transition_epoch.nim index 066779fdc..da7a77867 100644 --- a/beacon_chain/spec/state_transition_epoch.nim +++ b/beacon_chain/spec/state_transition_epoch.nim @@ -67,7 +67,7 @@ func get_matching_head_attestations(state: BeaconState, epoch: Epoch): get_block_root_at_slot(state, get_attestation_data_slot(state, it.data)) ) -func get_unslashed_attesting_indices( +func get_attesting_indices( state: BeaconState, attestations: openarray[PendingAttestation], stateCache: var StateCache): HashSet[ValidatorIndex] = result = initSet[ValidatorIndex]() @@ -75,6 +75,10 @@ func get_unslashed_attesting_indices( result = result.union(get_attesting_indices( state, a.data, a.aggregation_bits, stateCache)) +func get_unslashed_attesting_indices( + state: BeaconState, attestations: openarray[PendingAttestation], + stateCache: var StateCache): HashSet[ValidatorIndex] = + result = get_attesting_indices(state, attestations, stateCache) for index in result: if state.validators[index].slashed: result.excl index @@ -181,8 +185,8 @@ func get_winning_crosslink_and_attesting_indices( (winning_crosslink, 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( +# https://github.com/ethereum/eth2.0-specs/blob/v0.8.1/specs/core/0_beacon-chain.md#justification-and-finalization +proc process_justification_and_finalization( state: var BeaconState, stateCache: var StateCache) = if get_current_epoch(state) <= GENESIS_EPOCH + 1: return @@ -190,33 +194,59 @@ func process_justification_and_finalization( let previous_epoch = get_previous_epoch(state) current_epoch = get_current_epoch(state) - old_previous_justified_epoch = state.previous_justified_checkpoint.epoch - old_current_justified_epoch = state.current_justified_checkpoint.epoch + old_previous_justified_checkpoint = state.previous_justified_checkpoint + old_current_justified_checkpoint = state.current_justified_checkpoint + + ## Bitvector[4] <-> uint8 mapping: + ## state.justification_bits[0] is (state.justification_bits shr 0) and 1 + ## state.justification_bits[1] is (state.justification_bits shr 1) and 1 + ## state.justification_bits[2] is (state.justification_bits shr 2) and 1 + ## state.justification_bits[3] is (state.justification_bits shr 3) and 1 + ## https://github.com/ethereum/eth2.0-specs/blob/v0.8.1/specs/simple-serialize.md#bitvectorn # Process justifications - state.previous_justified_checkpoint.epoch = - state.current_justified_checkpoint.epoch - state.previous_justified_checkpoint.root = - state.current_justified_checkpoint.root - state.justification_bits = (state.justification_bits shl 1) - let previous_epoch_matching_target_balance = - get_attesting_balance(state, - get_matching_target_attestations(state, previous_epoch), stateCache) - if previous_epoch_matching_target_balance * 3 >= - get_total_active_balance(state) * 2: - state.current_justified_checkpoint.epoch = previous_epoch - state.current_justified_checkpoint.root = - get_block_root(state, state.current_justified_checkpoint.epoch) + state.previous_justified_checkpoint = state.current_justified_checkpoint + + ## Spec: + ## state.justification_bits[1:] = state.justification_bits[:-1] + ## state.justification_bits[0] = 0b0 + state.justification_bits = state.justification_bits shl 1 + + # This is a somewhat expensive approach + let active_validator_indices = + toSet(mapIt( + get_active_validator_indices(state, get_current_epoch(state)), it.int)) + + let matching_target_attestations_previous = + get_matching_target_attestations(state, previous_epoch) # Previous epoch + debug "Non-attesting indices in previous epoch: ", + missing_all_validators= + difference(active_validator_indices, + toSet(mapIt(get_attesting_indices(state, + matching_target_attestations_previous, stateCache), it.int))), + missing_unslashed_validators= + difference(active_validator_indices, + toSet(mapIt(get_unslashed_attesting_indices(state, + matching_target_attestations_previous, stateCache), it.int))), + num_active_validators=len(active_validator_indices) + if get_attesting_balance(state, matching_target_attestations_previous, + stateCache) * 3 >= get_total_active_balance(state) * 2: + state.current_justified_checkpoint = + Checkpoint(epoch: previous_epoch, + root: get_block_root(state, previous_epoch)) + + # Spec: state.justification_bits[1] = 0b1 state.justification_bits = state.justification_bits or (1 shl 1) - let current_epoch_matching_target_balance = - get_attesting_balance(state, - get_matching_target_attestations(state, current_epoch), - stateCache) - if current_epoch_matching_target_balance * 3 >= - get_total_active_balance(state) * 2: - state.current_justified_checkpoint.epoch = current_epoch - state.current_justified_checkpoint.root = - get_block_root(state, state.current_justified_checkpoint.epoch) + + let matching_target_attestations_current = + get_matching_target_attestations(state, current_epoch) # Current epoch + if get_attesting_balance(state, matching_target_attestations_current, + stateCache) * 3 >= get_total_active_balance(state) * 2: + state.current_justified_checkpoint = + Checkpoint(epoch: current_epoch, + root: get_block_root(state, current_epoch)) + + # Spec: state.justification_bits[0] = 0b1 state.justification_bits = state.justification_bits or (1 shl 0) # Process finalizations @@ -224,35 +254,27 @@ func process_justification_and_finalization( ## The 2nd/3rd/4th most recent epochs are justified, the 2nd using the 4th ## as source - if (bitfield shr 1) mod 8 == 0b111 and old_previous_justified_epoch + 3 == - current_epoch: - state.finalized_checkpoint.epoch = old_previous_justified_epoch - state.finalized_checkpoint.root = - get_block_root(state, state.finalized_checkpoint.epoch) + if (bitfield shr 1) mod 8 == 0b111 and + old_previous_justified_checkpoint.epoch + 3 == current_epoch: + state.finalized_checkpoint = old_previous_justified_checkpoint ## The 2nd/3rd most recent epochs are justified, the 2nd using the 3rd as ## source - if (bitfield shr 1) mod 4 == 0b11 and old_previous_justified_epoch + 2 == - current_epoch: - state.finalized_checkpoint.epoch = old_previous_justified_epoch - state.finalized_checkpoint.root = - get_block_root(state, state.finalized_checkpoint.epoch) + if (bitfield shr 1) mod 4 == 0b11 and + old_previous_justified_checkpoint.epoch + 2 == current_epoch: + state.finalized_checkpoint = old_previous_justified_checkpoint ## The 1st/2nd/3rd most recent epochs are justified, the 1st using the 3rd as ## source - if (bitfield shr 0) mod 8 == 0b111 and old_current_justified_epoch + 2 == - current_epoch: - state.finalized_checkpoint.epoch = old_current_justified_epoch - state.finalized_checkpoint.root = - get_block_root(state, state.finalized_checkpoint.epoch) + if (bitfield shr 0) mod 8 == 0b111 and + old_current_justified_checkpoint.epoch + 2 == current_epoch: + state.finalized_checkpoint = old_current_justified_checkpoint ## The 1st/2nd most recent epochs are justified, the 1st using the 2nd as ## source - if (bitfield shr 0) mod 4 == 0b11 and old_current_justified_epoch + 1 == - current_epoch: - state.finalized_checkpoint.epoch = old_current_justified_epoch - state.finalized_checkpoint.root = - get_block_root(state, state.finalized_checkpoint.epoch) + if (bitfield shr 0) mod 4 == 0b11 and + old_current_justified_checkpoint.epoch + 1 == current_epoch: + state.finalized_checkpoint = old_current_justified_checkpoint # https://github.com/ethereum/eth2.0-specs/blob/v0.8.1/specs/core/0_beacon-chain.md#crosslinks func process_crosslinks(state: var BeaconState, stateCache: var StateCache) = @@ -486,7 +508,7 @@ func process_final_updates(state: var BeaconState) = state.current_epoch_attestations = @[] # https://github.com/ethereum/eth2.0-specs/blob/v0.8.1/specs/core/0_beacon-chain.md#per-epoch-processing -func process_epoch*(state: var BeaconState) = +proc process_epoch*(state: var BeaconState) = # @proc are placeholders var per_epoch_cache = get_empty_per_epoch_cache() diff --git a/beacon_chain/state_transition.nim b/beacon_chain/state_transition.nim index f6f4c547e..6c2d8fc70 100644 --- a/beacon_chain/state_transition.nim +++ b/beacon_chain/state_transition.nim @@ -55,7 +55,7 @@ func process_slot(state: var BeaconState) = signing_root(state.latest_block_header) # https://github.com/ethereum/eth2.0-specs/blob/v0.8.1/specs/core/0_beacon-chain.md#beacon-chain-state-transition-function -func process_slots*(state: var BeaconState, slot: Slot) = +proc process_slots*(state: var BeaconState, slot: Slot) = doAssert state.slot <= slot # Catch up to the target slot