diff --git a/beacon_chain/spec/beaconstate.nim b/beacon_chain/spec/beaconstate.nim index d49419093..2a759b9d2 100644 --- a/beacon_chain/spec/beaconstate.nim +++ b/beacon_chain/spec/beaconstate.nim @@ -295,7 +295,9 @@ func process_ejections*(state: var BeaconState) = ## Iterate through the validator registry ## and eject active validators with balance below ``EJECTION_BALANCE``. - for index in get_active_validator_indices(state.validator_registry, state.slot): + for index in get_active_validator_indices( + # TODO v0.3.0 spec bug, has this as current_epoch(state) + state.validator_registry, get_current_epoch(state)): if state.validator_balances[index] < EJECTION_BALANCE: exit_validator(state, index) @@ -304,12 +306,15 @@ func get_total_balance*(state: BeaconState, validators: seq[ValidatorIndex]): Gw # Return the combined effective balance of an array of validators. foldl(validators, a + get_effective_balance(state, b), 0'u64) +# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#validator-registry-and-shuffling-seed-data func update_validator_registry*(state: var BeaconState) = + ## Update validator registry. + ## Note that this function mutates ``state``. let current_epoch = get_current_epoch(state) - next_epoch = current_epoch + 1 + # The active validators active_validator_indices = - get_active_validator_indices(state.validator_registry, state.slot) + get_active_validator_indices(state.validator_registry, current_epoch) # The total effective balance of active validators total_balance = get_total_balance(state, active_validator_indices) @@ -347,13 +352,6 @@ func update_validator_registry*(state: var BeaconState) = state.validator_registry_update_epoch = current_epoch - # Perform additional updates - state.current_shuffling_epoch = next_epoch - state.current_shuffling_start_shard = (state.current_shuffling_start_shard + get_current_epoch_committee_count(state)) mod SHARD_COUNT - state.current_shuffling_seed = generate_seed(state, state.current_shuffling_epoch) - - # TODO "If a validator registry update does not happen do the following: ..." - # https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#attestations-1 proc checkAttestation*( state: BeaconState, attestation: Attestation, flags: UpdateFlags): bool = diff --git a/beacon_chain/spec/crypto.nim b/beacon_chain/spec/crypto.nim index 626b78f53..8fe610bcf 100644 --- a/beacon_chain/spec/crypto.nim +++ b/beacon_chain/spec/crypto.nim @@ -1,5 +1,5 @@ # beacon_chain -# Copyright (c) 2018 Status Research & Development GmbH +# Copyright (c) 2018-2019 Status Research & Development GmbH # Licensed and distributed under either of # * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). # * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). @@ -45,6 +45,7 @@ import + sequtils, hashes, blscurve, json_serialization @@ -84,14 +85,22 @@ func bls_verify*( # name from spec! sig.verify(msg, domain, pubkey) -# https://github.com/ethereum/eth2.0-specs/blob/master/specs/bls_signature.md#bls_verify_multiple +# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/bls_signature.md#bls_verify_multiple func bls_verify_multiple*( - pubkeys: seq[ValidatorPubKey], messages: seq[array[0..31, byte]], + pubkeys: seq[ValidatorPubKey], message_hashes: seq[array[0..31, byte]], sig: ValidatorSig, domain: uint64): bool = let L = len(pubkeys) - assert L == len(messages) + doAssert L == len(message_hashes) + + # TODO optimize using multiPairing + for pubkey_message_hash in zip(pubkeys, message_hashes): + let (pubkey, message_hash) = pubkey_message_hash + # TODO spec doesn't say to handle this specially, but it's silly to + # validate without any actual public keys. + if pubkey != ValidatorPubKey() and + not sig.verify(message_hash, domain, pubkey): + return false - # TODO calculate product of ate pairings; check how sig.verify works true func bls_sign*(key: ValidatorPrivKey, msg: openarray[byte], diff --git a/beacon_chain/spec/datatypes.nim b/beacon_chain/spec/datatypes.nim index 506a650e0..2279add3b 100644 --- a/beacon_chain/spec/datatypes.nim +++ b/beacon_chain/spec/datatypes.nim @@ -420,10 +420,10 @@ type ## Epoch when validator exited withdrawable_epoch*: uint64 ##\ - ## Epoch when validator withdrew + ## Epoch when validator is eligible to withdraw slashed_epoch*: uint64 ##\ - ## Epoch when validator penalized + ## Epoch when validator slashed status_flags*: uint64 diff --git a/beacon_chain/state_transition.nim b/beacon_chain/state_transition.nim index 13f926c36..a987163bc 100644 --- a/beacon_chain/state_transition.nim +++ b/beacon_chain/state_transition.nim @@ -711,9 +711,6 @@ func processEpoch(state: var BeaconState) = get_total_balance( statePtr[], attesting_validator_indices(crosslink_committee)) - func total_balance_sac(crosslink_committee: CrosslinkCommittee): uint64 = - get_total_balance(statePtr[], crosslink_committee.committee) - # https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#eth1-data-1 block: if next_epoch mod EPOCHS_PER_ETH1_VOTING_PERIOD == 0: @@ -791,11 +788,12 @@ func processEpoch(state: var BeaconState) = return a.inclusion_slot - a.data.slot doAssert false - let active_validator_indices = - get_active_validator_indices(state.validator_registry, state.slot) - block: # Justification and finalization - let epochs_since_finality = next_epoch - state.finalized_epoch + let + active_validator_indices = + get_active_validator_indices( + state.validator_registry, slot_to_epoch(state.slot)) + epochs_since_finality = next_epoch - state.finalized_epoch proc update_balance(attesters: openArray[ValidatorIndex], attesting_balance: uint64) = # TODO Spec - add helper? @@ -810,6 +808,7 @@ func processEpoch(state: var BeaconState) = statePtr.validator_balances[v] -= base_reward(statePtr[], v) if epochs_since_finality <= 4'u64: + # Case 1: epochs_since_finality <= 4 # Expected FFG source update_balance( previous_epoch_attester_indices, @@ -832,6 +831,7 @@ func processEpoch(state: var BeaconState) = MIN_ATTESTATION_INCLUSION_DELAY div inclusion_distance(state, v) else: + # Case 2: epochs_since_finality > 4 for index in active_validator_indices: # TODO underflows? if index notin previous_epoch_attester_indices: @@ -841,29 +841,36 @@ func processEpoch(state: var BeaconState) = state.validator_balances[index] -= inactivity_penalty(state, index, epochs_since_finality) if index notin previous_epoch_head_attester_indices: + state.validator_balances[index] -= base_reward(state, index) + if state.validator_registry[index].slashed_epoch <= current_epoch: state.validator_balances[index] -= - inactivity_penalty(state, index, epochs_since_finality) + 2'u64 * inactivity_penalty( + state, index, epochs_since_finality) + base_reward(state, index) + if index in previous_epoch_attester_indices: + state.validator_balances[index] -= + base_reward(state, index) - + base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY div + inclusion_distance(state, index) - block: # Attestation inclusion + # https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#attestation-inclusion + block: for v in previous_epoch_attester_indices: let proposer_index = get_beacon_proposer_index(state, inclusion_slot(state, v)) state.validator_balances[proposer_index] += base_reward(state, v) div ATTESTATION_INCLUSION_REWARD_QUOTIENT - block: # Crosslinks - # https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#crosslinks-1 + # https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#crosslinks-1 + block: for slot in get_epoch_start_slot(previous_epoch) ..< get_epoch_start_slot(current_epoch): let crosslink_committees_at_slot = get_crosslink_committees_at_slot(state, slot) for crosslink_committee in crosslink_committees_at_slot: - # TODO https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#crosslinks-1 - # but this is a best guess based on reasonableness of what "index" is for index in crosslink_committee.committee: if index in attesting_validators(crosslink_committee): state.validator_balances[index.int] += base_reward(state, index) * total_attesting_balance(crosslink_committee) div - total_balance_sac(crosslink_committee) + get_total_balance(state, crosslink_committee.committee) else: # TODO underflows? state.validator_balances[index] -= base_reward(state, index) @@ -877,13 +884,17 @@ func processEpoch(state: var BeaconState) = state.previous_shuffling_start_shard = state.current_shuffling_start_shard state.previous_shuffling_seed = state.current_shuffling_seed - # TODO verify this shard list if state.finalized_epoch > state.validator_registry_update_epoch and allIt( - 0 ..< get_current_epoch_committee_count(state).int * SLOTS_PER_EPOCH, - state.latest_crosslinks[(state.current_shuffling_start_shard + it.uint64) mod SHARD_COUNT].epoch > state.validator_registry_update_epoch): + 0 ..< get_current_epoch_committee_count(state).int, + state.latest_crosslinks[ + (state.current_shuffling_start_shard + it.uint64) mod + SHARD_COUNT].epoch > state.validator_registry_update_epoch): + + # update the validator registry and associated fields by running update_validator_registry(state) + # and perform the following updates state.current_shuffling_epoch = next_epoch state.current_shuffling_start_shard = (state.current_shuffling_start_shard + @@ -892,8 +903,10 @@ func processEpoch(state: var BeaconState) = state, state.current_shuffling_epoch) else: # If a validator registry change does NOT happen - let epochs_since_last_registry_change = current_epoch - state.validator_registry_update_epoch - if is_power_of_2(epochs_since_last_registry_change): + let epochs_since_last_registry_update = + current_epoch - state.validator_registry_update_epoch + if epochs_since_last_registry_update > 1'u64 and + is_power_of_2(epochs_since_last_registry_update): state.current_shuffling_epoch = next_epoch state.current_shuffling_seed = generate_seed(state, state.current_shuffling_epoch) # /Note/ that state.current_shuffling_start_shard is left unchanged diff --git a/tests/testutil.nim b/tests/testutil.nim index 5e909958b..4cacc6a23 100644 --- a/tests/testutil.nim +++ b/tests/testutil.nim @@ -187,12 +187,15 @@ proc makeAttestation*( bitSet(aggregation_bitfield, sac_index) let - msg = hash_tree_root_final(data) - # TODO: domain - domain = 0'u64 + msg = hash_tree_root_final(AttestationDataAndCustodyBit(data: data, custody_bit: false)) sig = if skipValidation notin flags: - bls_sign(hackPrivKey(validator), @(msg.data) & @[0'u8], domain) + bls_sign( + hackPrivKey(validator), @(msg.data), + get_domain( + state.fork, + slot_to_epoch(state.slot), + DOMAIN_ATTESTATION)) else: ValidatorSig()