From 68be98cafa431418690aba271b4982e1caf9e4be Mon Sep 17 00:00:00 2001 From: Dustin Brody Date: Fri, 5 Jul 2019 08:30:05 +0000 Subject: [PATCH] More 0.8.0 updates (#311) * replace BeaconState.finalized_{epoch,root} with BeaconState.finalized_checkpoint; rename get_delayed_activation_exit_epoch(...) to compute_activation_exit_epoch(...) and mark as 0.8.0; update get_churn_limit(...)/get_validator_churn_limit(...) to 0.8.0; update process_registry_updates(...) to 0.8.0 * update process_crosslinks(...) to 0.8.0; mark compute_start_slot_of_epoch(...) and get_committee_count(...) as 0.8.0 * mark Fork, is_slashable_validator(...), and get_beacon_proposer_index(...) as 0.8.0 * rename LATEST_SLASHED_EXIT_LENGTH to EPOCHS_PER_SLASHINGS_VECTOR; update process_slashings(...) to 0.8.0; remove pointless type conversion warning in get_previous_epoch(...) * convert remaining references to finalized_epoch to finalized_checkpoint.epoch * update slash_validator(...) to 0.8.0; mark inital value, Gwei, and time constants as 0.8.0; mark hash(...) and processBlockHeader(...) as 0.8.0 * rename WHISTLEBLOWING_REWARD_QUOTIENT to WHISTLEBLOWER_REWARD_QUOTIENT; rename LATEST_ACTIVE_INDEX_ROOTS_LENGTH to EPOCHS_PER_HISTORICAL_VECTOR (randao will also get merged into this); remove get_active_index_root(...); mark time parameter, signature domain types, and max operations per block constants as 0.8.0; update rewards and penalties constants to 0.8.0 * update is_valid_indexed_attestation(...) to 0.8.0; mark process_slot(...) as 0.8.0 * replace BeaconState.{current,previous}_justified_{epoch,root} with BeaconState.{current,previous}_justified_checkpoint --- beacon_chain/attestation_pool.nim | 19 ++-- beacon_chain/block_pool.nim | 27 ++++-- beacon_chain/spec/beaconstate.nim | 79 ++++++++-------- beacon_chain/spec/datatypes.nim | 53 +++++++---- beacon_chain/spec/digest.nim | 2 +- beacon_chain/spec/helpers.nim | 23 ++--- beacon_chain/spec/presets/mainnet.nim | 15 +-- beacon_chain/spec/presets/minimal.nim | 18 ++-- beacon_chain/spec/state_transition_block.nim | 5 +- beacon_chain/spec/state_transition_epoch.nim | 96 +++++++++----------- beacon_chain/spec/validator.nim | 7 +- beacon_chain/state_transition.nim | 2 +- tests/official/fixtures_utils.nim | 6 +- 13 files changed, 180 insertions(+), 172 deletions(-) diff --git a/beacon_chain/attestation_pool.nim b/beacon_chain/attestation_pool.nim index f3f7c36bc..2943e3669 100644 --- a/beacon_chain/attestation_pool.nim +++ b/beacon_chain/attestation_pool.nim @@ -40,12 +40,13 @@ proc validate( let attestationSlot = get_attestation_data_slot(state, attestation.data) - if attestationSlot < state.finalized_epoch.compute_start_slot_of_epoch(): + if attestationSlot < + state.finalized_checkpoint.epoch.compute_start_slot_of_epoch(): debug "Old attestation", attestationSlot = humaneSlotNum(attestationSlot), attestationEpoch = humaneEpochNum(attestationSlot.compute_epoch_of_slot), stateSlot = humaneSlotNum(state.slot), - finalizedEpoch = humaneEpochNum(state.finalized_epoch) + finalizedEpoch = humaneEpochNum(state.finalized_checkpoint.epoch) return @@ -57,7 +58,7 @@ proc validate( attestationSlot = humaneSlotNum(attestationSlot), attestationEpoch = humaneEpochNum(attestationSlot.compute_epoch_of_slot), stateSlot = humaneSlotNum(state.slot), - finalizedEpoch = humaneEpochNum(state.finalized_epoch) + finalizedEpoch = humaneEpochNum(state.finalized_checkpoint.epoch) return if not allIt(attestation.custody_bits.bits, it == 0): @@ -130,7 +131,8 @@ proc slotIndex( # earlier than that is thrown out by the above check info "First attestation!", attestationSlot = $humaneSlotNum(attestationSlot) - pool.startingSlot = state.finalized_epoch.compute_start_slot_of_epoch() + pool.startingSlot = + state.finalized_checkpoint.epoch.compute_start_slot_of_epoch() if pool.startingSlot + pool.slots.len.uint64 <= attestationSlot: debug "Growing attestation pool", @@ -141,14 +143,17 @@ proc slotIndex( while pool.startingSlot + pool.slots.len.uint64 <= attestationSlot: pool.slots.addLast(SlotData()) - if pool.startingSlot < state.finalized_epoch.compute_start_slot_of_epoch(): + if pool.startingSlot < + state.finalized_checkpoint.epoch.compute_start_slot_of_epoch(): debug "Pruning attestation pool", startingSlot = $humaneSlotNum(pool.startingSlot), finalizedSlot = - $humaneSlotNum(state.finalized_epoch.compute_start_slot_of_epoch()) + $humaneSlotNum( + state.finalized_checkpoint.epoch.compute_start_slot_of_epoch()) # TODO there should be a better way to remove a whole epoch of stuff.. - while pool.startingSlot < state.finalized_epoch.compute_start_slot_of_epoch(): + while pool.startingSlot < + state.finalized_checkpoint.epoch.compute_start_slot_of_epoch(): pool.slots.popFirst() pool.startingSlot += 1 diff --git a/beacon_chain/block_pool.nim b/beacon_chain/block_pool.nim index e0a19bce3..b9a2a41d0 100644 --- a/beacon_chain/block_pool.nim +++ b/beacon_chain/block_pool.nim @@ -107,8 +107,10 @@ proc init*(T: type BlockPool, db: BeaconChainDB): BlockPool = # will need revisiting however headState = db.getState(headStateRoot).get() finalizedHead = - headRef.findAncestorBySlot(headState.finalized_epoch.compute_start_slot_of_epoch()) - justifiedSlot = headState.current_justified_epoch.compute_start_slot_of_epoch() + headRef.findAncestorBySlot( + headState.finalized_checkpoint.epoch.compute_start_slot_of_epoch()) + justifiedSlot = + headState.current_justified_checkpoint.epoch.compute_start_slot_of_epoch() justifiedHead = headRef.findAncestorBySlot(justifiedSlot) head = Head(blck: headRef, justified: justifiedHead) @@ -163,7 +165,8 @@ proc addResolvedBlock( # This block *might* have caused a justification - make sure we stow away # that information: - let justifiedSlot = state.data.data.current_justified_epoch.compute_start_slot_of_epoch() + let justifiedSlot = + state.data.data.current_justified_checkpoint.epoch.compute_start_slot_of_epoch() var foundHead: Option[Head] for head in pool.heads.mitems(): @@ -515,7 +518,8 @@ proc updateHead*(pool: BlockPool, state: var StateData, blck: BlockRef) = # Start off by making sure we have the right state updateStateData(pool, state, BlockSlot(blck: blck, slot: blck.slot)) - let justifiedSlot = state.data.data.current_justified_epoch.compute_start_slot_of_epoch() + let justifiedSlot = + state.data.data.current_justified_checkpoint.epoch.compute_start_slot_of_epoch() pool.head = Head(blck: blck, justified: blck.findAncestorBySlot(justifiedSlot)) if lastHead.blck != blck.parent: @@ -525,20 +529,25 @@ proc updateHead*(pool: BlockPool, state: var StateData, blck: BlockRef) = stateRoot = shortLog(state.data.root), headBlockRoot = shortLog(state.blck.root), stateSlot = humaneSlotNum(state.data.data.slot), - justifiedEpoch = humaneEpochNum(state.data.data.current_justified_epoch), - finalizedEpoch = humaneEpochNum(state.data.data.finalized_epoch) + justifiedEpoch = + humaneEpochNum(state.data.data.current_justified_checkpoint.epoch), + finalizedEpoch = + humaneEpochNum(state.data.data.finalized_checkpoint.epoch) else: info "Updated head", stateRoot = shortLog(state.data.root), headBlockRoot = shortLog(state.blck.root), stateSlot = humaneSlotNum(state.data.data.slot), - justifiedEpoch = humaneEpochNum(state.data.data.current_justified_epoch), - finalizedEpoch = humaneEpochNum(state.data.data.finalized_epoch) + justifiedEpoch = + humaneEpochNum(state.data.data.current_justified_checkpoint.epoch), + finalizedEpoch = + humaneEpochNum(state.data.data.finalized_checkpoint.epoch) let # TODO there might not be a block at the epoch boundary - what then? finalizedHead = - blck.findAncestorBySlot(state.data.data.finalized_epoch.compute_start_slot_of_epoch()) + blck.findAncestorBySlot( + state.data.data.finalized_checkpoint.epoch.compute_start_slot_of_epoch()) doAssert (not finalizedHead.blck.isNil), "Block graph should always lead to a finalized block" diff --git a/beacon_chain/spec/beaconstate.nim b/beacon_chain/spec/beaconstate.nim index 235af722a..ef1120757 100644 --- a/beacon_chain/spec/beaconstate.nim +++ b/beacon_chain/spec/beaconstate.nim @@ -103,19 +103,19 @@ func process_deposit*( true -# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#get_delayed_activation_exit_epoch -func get_delayed_activation_exit_epoch*(epoch: Epoch): Epoch = - ## Return the epoch at which an activation or exit triggered in ``epoch`` - ## takes effect. +# https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#compute_activation_exit_epoch +func compute_activation_exit_epoch*(epoch: Epoch): Epoch = + ## Return the epoch during which validator activations and exits initiated in + ## ``epoch`` take effect. epoch + 1 + ACTIVATION_EXIT_DELAY -# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#get_churn_limit -func get_churn_limit(state: BeaconState): uint64 = - max( - MIN_PER_EPOCH_CHURN_LIMIT, - len(get_active_validator_indices(state, get_current_epoch(state))) div - CHURN_LIMIT_QUOTIENT - ).uint64 +# https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#get_validator_churn_limit +func get_validator_churn_limit(state: BeaconState): uint64 = + # Return the validator churn limit for the current epoch. + let active_validator_indices = + get_active_validator_indices(state, get_current_epoch(state)) + max(MIN_PER_EPOCH_CHURN_LIMIT, + len(active_validator_indices) div CHURN_LIMIT_QUOTIENT).uint64 # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#initiate_validator_exit func initiate_validator_exit*(state: var BeaconState, @@ -134,13 +134,13 @@ func initiate_validator_exit*(state: var BeaconState, it.exit_epoch) var exit_queue_epoch = max(max(exit_epochs), - get_delayed_activation_exit_epoch(get_current_epoch(state))) + compute_activation_exit_epoch(get_current_epoch(state))) let exit_queue_churn = foldl( state.validators, a + (if b.exit_epoch == exit_queue_epoch: 1'u64 else: 0'u64), 0'u64) - if exit_queue_churn >= get_churn_limit(state): + if exit_queue_churn >= get_validator_churn_limit(state): exit_queue_epoch += 1 # Set validator exit epoch and withdrawable epoch @@ -148,32 +148,32 @@ func initiate_validator_exit*(state: var BeaconState, validator.withdrawable_epoch = 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 +# https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#slash_validator 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) + let epoch = get_current_epoch(state) initiate_validator_exit(state, slashed_index) - state.validators[slashed_index].slashed = true - state.validators[slashed_index].withdrawable_epoch = - current_epoch + LATEST_SLASHED_EXIT_LENGTH - let slashed_balance = - state.validators[slashed_index].effective_balance - state.slashings[current_epoch mod LATEST_SLASHED_EXIT_LENGTH] += - slashed_balance + let validator = addr state.validators[slashed_index] + validator.slashed = true + validator.withdrawable_epoch = + max(validator.withdrawable_epoch, epoch + EPOCHS_PER_SLASHINGS_VECTOR) + state.slashings[epoch mod EPOCHS_PER_SLASHINGS_VECTOR] += + validator.effective_balance + decrease_balance(state, slashed_index, + validator.effective_balance div MIN_SLASHING_PENALTY_QUOTIENT) let 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 + whistleblowing_reward = + (validator.effective_balance div WHISTLEBLOWER_REWARD_QUOTIENT).Gwei proposer_reward = whistleblowing_reward div PROPOSER_REWARD_QUOTIENT increase_balance(state, proposer_index, proposer_reward) increase_balance( state, whistleblower_index, whistleblowing_reward - proposer_reward) - decrease_balance(state, slashed_index, whistleblowing_reward) -# https://github.com/ethereum/eth2.0-specs/blob/v0.5.1/specs/core/0_beacon-chain.md#on-genesis func get_temporary_block_header(blck: BeaconBlock): BeaconBlockHeader = ## Return the block header corresponding to a block with ``state_root`` set ## to ``ZERO_HASH``. @@ -240,7 +240,7 @@ func get_genesis_beacon_state*( let genesis_active_index_root = hash_tree_root( get_active_validator_indices(state, GENESIS_EPOCH)) - for index in 0 ..< LATEST_ACTIVE_INDEX_ROOTS_LENGTH: + for index in 0 ..< EPOCHS_PER_HISTORICAL_VECTOR: state.active_index_roots[index] = genesis_active_index_root state @@ -292,14 +292,14 @@ func get_total_balance*(state: BeaconState, validators: auto): Gwei = foldl(validators, a + state.validators[b].effective_balance, 0'u64) ) -# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#registry-updates +# https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#registry-updates func process_registry_updates*(state: var BeaconState) = ## Process activation eligibility and ejections ## Try to avoid caching here, since this could easily become undefined for index, validator in state.validators: if validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and - validator.effective_balance >= MAX_EFFECTIVE_BALANCE: + validator.effective_balance == MAX_EFFECTIVE_BALANCE: state.validators[index].activation_eligibility_epoch = get_current_epoch(state) @@ -313,7 +313,7 @@ func process_registry_updates*(state: var BeaconState) = for index, validator in state.validators: if validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH and validator.activation_epoch >= - get_delayed_activation_exit_epoch(state.finalized_epoch): + compute_activation_exit_epoch(state.finalized_checkpoint.epoch): activation_queue.add ( state.validators[index].activation_eligibility_epoch, index) @@ -321,7 +321,7 @@ func process_registry_updates*(state: var BeaconState) = ## Dequeued validators for activation up to churn limit (without resetting ## activation epoch) - let churn_limit = get_churn_limit(state) + let churn_limit = get_validator_churn_limit(state) for i, epoch_and_index in activation_queue: if i.uint64 >= churn_limit: break @@ -330,9 +330,9 @@ func process_registry_updates*(state: var BeaconState) = validator = addr state.validators[index] if validator.activation_epoch == FAR_FUTURE_EPOCH: validator.activation_epoch = - get_delayed_activation_exit_epoch(get_current_epoch(state)) + compute_activation_exit_epoch(get_current_epoch(state)) -# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#is_valid_indexed_attestation +# https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#is_valid_indexed_attestation func is_valid_indexed_attestation*( state: BeaconState, indexed_attestation: IndexedAttestation): bool = # Verify validity of ``indexed_attestation`` fields. @@ -347,7 +347,7 @@ func is_valid_indexed_attestation*( # Verify max number of indices let combined_len = len(bit_0_indices) + len(bit_1_indices) - if not (1 <= combined_len and combined_len <= MAX_VALIDATORS_PER_COMMITTEE): + if not (combined_len <= MAX_VALIDATORS_PER_COMMITTEE): return false # Verify index sets are disjoint @@ -510,8 +510,8 @@ proc process_attestation*( 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))): + if not (ffg_check_data == (state.current_justified_checkpoint.epoch, + state.current_justified_checkpoint.root, get_current_epoch(state))): warn("FFG data not matching current justified epoch") return @@ -522,8 +522,8 @@ proc process_attestation*( #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))): + if not (ffg_check_data == (state.previous_justified_checkpoint.epoch, + state.previous_justified_checkpoint.root, get_previous_epoch(state))): warn("FFG data not matching current justified epoch") return @@ -586,10 +586,7 @@ proc makeAttestationData*( AttestationData( beacon_block_root: beacon_block_root, - source: Checkpoint( - epoch: state.current_justified_epoch, - root: state.current_justified_root - ), + source: state.current_justified_checkpoint, target: Checkpoint( root: target_root, epoch: target_epoch diff --git a/beacon_chain/spec/datatypes.nim b/beacon_chain/spec/datatypes.nim index 17f1d6598..39a7a950d 100644 --- a/beacon_chain/spec/datatypes.nim +++ b/beacon_chain/spec/datatypes.nim @@ -53,7 +53,7 @@ else: {.fatal: "Preset \"" & const_preset ".nim\" is not supported.".} const - SPEC_VERSION* = "0.7.1" ## \ + SPEC_VERSION* = "0.8.0" ## \ ## Spec version we're aiming to be compatible with, right now ## TODO: improve this scheme once we can negotiate versions in protocol @@ -62,9 +62,11 @@ const # Initial values # --------------------------------------------------------------- - # https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#initial-values + # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#initial-values GENESIS_EPOCH* = (GENESIS_SLOT.uint64 div SLOTS_PER_EPOCH).Epoch ##\ ## compute_epoch_of_slot(GENESIS_SLOT) + + # Not part of spec. Still useful, pending removing usage if appropriate. ZERO_HASH* = Eth2Digest() type @@ -254,25 +256,38 @@ type ## Needed to process attestations, older to newer state_roots*: array[SLOTS_PER_HISTORICAL_ROOT, Eth2Digest] - historical_roots*: seq[Eth2Digest] + + historical_roots*: seq[Eth2Digest] ##\ + ## model List with HISTORICAL_ROOTS_LIMIT limit as seq + ## TODO bound explicitly somewhere # Eth1 eth1_data*: Eth1Data - eth1_data_votes*: seq[Eth1Data] + + eth1_data_votes*: seq[Eth1Data] ##\ + ## As with `hitorical_roots`, this is a `List`. TODO bound explicitly. + eth1_deposit_index*: uint64 # Registry validators*: seq[Validator] balances*: seq[uint64] ##\ ## Validator balances in Gwei! + ## Also more `List`s which need to be bounded explicitly at + ## VALIDATOR_REGISTRY_LIMIT # Shuffling start_shard*: Shard randao_mixes*: array[LATEST_RANDAO_MIXES_LENGTH, Eth2Digest] - active_index_roots*: array[LATEST_ACTIVE_INDEX_ROOTS_LENGTH, Eth2Digest] + + active_index_roots*: array[EPOCHS_PER_HISTORICAL_VECTOR, Eth2Digest] ##\ + ## Active index digests for light clients + + compact_committees_roots*: array[EPOCHS_PER_HISTORICAL_VECTOR, Eth2Digest] ##\ + ## Committee digests for light clients # Slashings - slashings*: array[LATEST_SLASHED_EXIT_LENGTH, uint64] ##\ + slashings*: array[EPOCHS_PER_SLASHINGS_VECTOR, uint64] ##\ ## Per-epoch sums of slashed effective balances # Attestations @@ -284,13 +299,14 @@ type current_crosslinks*: array[SHARD_COUNT, Crosslink] # Finality - justification_bits*: uint64 - previous_justified_epoch*: Epoch - current_justified_epoch*: Epoch - previous_justified_root*: Eth2Digest - current_justified_root*: Eth2Digest - finalized_epoch*: Epoch - finalized_root*: Eth2Digest + justification_bits*: uint64 ##\ + ## Bit set for every recent justified epoch + + previous_justified_checkpoint*: Checkpoint ##\ + ## Previous epoch snapshot + + current_justified_checkpoint*: Checkpoint + finalized_checkpoint*: Checkpoint # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#validator Validator* = object @@ -338,16 +354,13 @@ type block_roots* : array[SLOTS_PER_HISTORICAL_ROOT, Eth2Digest] state_roots* : array[SLOTS_PER_HISTORICAL_ROOT, Eth2Digest] - # https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#fork + # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#fork Fork* = object - previous_version*: array[4, byte] ##\ - ## Previous fork version - - current_version*: array[4, byte] ##\ - ## Current fork version + previous_version*: array[4, byte] + current_version*: array[4, byte] epoch*: Epoch ##\ - ## Fork epoch number + ## Epoch of latest fork # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#eth1data Eth1Data* = object diff --git a/beacon_chain/spec/digest.nim b/beacon_chain/spec/digest.nim index 2bea2436a..d62beac88 100644 --- a/beacon_chain/spec/digest.nim +++ b/beacon_chain/spec/digest.nim @@ -7,7 +7,7 @@ # Serenity hash function / digest # -# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#hash +# https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#hash # # In Phase 0 the beacon chain is deployed with SHA256 (SHA2-256). # Note that is is different from Keccak256 (often mistakenly called SHA3-256) diff --git a/beacon_chain/spec/helpers.nim b/beacon_chain/spec/helpers.nim index ede7c26f6..bd33d2d81 100644 --- a/beacon_chain/spec/helpers.nim +++ b/beacon_chain/spec/helpers.nim @@ -62,9 +62,9 @@ func compute_epoch_of_slot*(slot: Slot|uint64): Epoch = # Return the epoch number of the given ``slot``. (slot div SLOTS_PER_EPOCH).Epoch -# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#compute_start_slot_of_epoch +# https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#compute_start_slot_of_epoch func compute_start_slot_of_epoch*(epoch: Epoch): Slot = - # Return the starting slot of the given ``epoch``. + # Return the start slot of ``epoch``. (epoch * SLOTS_PER_EPOCH).Slot # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#is_active_validator @@ -80,13 +80,14 @@ func get_active_validator_indices*(state: BeaconState, epoch: Epoch): if is_active_validator(val, epoch): result.add idx.ValidatorIndex -# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#get_committee_count +# https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#get_committee_count func get_committee_count*(state: BeaconState, epoch: Epoch): uint64 = # Return the number of committees at ``epoch``. let active_validator_indices = get_active_validator_indices(state, epoch) - clamp( + let committees_per_slot = clamp( len(active_validator_indices) div SLOTS_PER_EPOCH div TARGET_COMMITTEE_SIZE, - 1, SHARD_COUNT div SLOTS_PER_EPOCH).uint64 * SLOTS_PER_EPOCH + 1, SHARD_COUNT div SLOTS_PER_EPOCH).uint64 + committees_per_slot * SLOTS_PER_EPOCH # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#get_current_epoch func get_current_epoch*(state: BeaconState): Epoch = @@ -102,15 +103,6 @@ func get_randao_mix*(state: BeaconState, ## LATEST_RANDAO_MIXES_LENGTH, current_epoch]. state.randao_mixes[epoch mod LATEST_RANDAO_MIXES_LENGTH] -# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#get_active_index_root -func get_active_index_root(state: BeaconState, epoch: Epoch): Eth2Digest = - # Returns the index root at a recent ``epoch``. - ## ``epoch`` expected to be between - ## (current_epoch - LATEST_ACTIVE_INDEX_ROOTS_LENGTH + ACTIVATION_EXIT_DELAY, current_epoch + ACTIVATION_EXIT_DELAY]. - ## TODO maybe assert this, but omission of such seems conspicuously - ## intentional - state.active_index_roots[epoch mod LATEST_ACTIVE_INDEX_ROOTS_LENGTH] - # https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#bytes_to_int func bytes_to_int*(data: openarray[byte]): uint64 = doAssert data.len == 8 @@ -186,6 +178,7 @@ func get_seed*(state: BeaconState, epoch: Epoch): Eth2Digest = seed_input[0..31] = get_randao_mix(state, epoch + LATEST_RANDAO_MIXES_LENGTH - MIN_SEED_LOOKAHEAD).data - seed_input[32..63] = get_active_index_root(state, epoch).data + seed_input[32..63] = + state.active_index_roots[epoch mod EPOCHS_PER_HISTORICAL_VECTOR].data seed_input[64..95] = int_to_bytes32(epoch) eth2hash(seed_input) diff --git a/beacon_chain/spec/presets/mainnet.nim b/beacon_chain/spec/presets/mainnet.nim index 241c7f1a6..89592f6d0 100644 --- a/beacon_chain/spec/presets/mainnet.nim +++ b/beacon_chain/spec/presets/mainnet.nim @@ -87,11 +87,12 @@ const # Time parameters # --------------------------------------------------------------- - # https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_fork-choice.md#time-parameters + # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_fork-choice.md#time-parameters SECONDS_PER_SLOT*{.intdefine.} = 6'u64 # Compile with -d:SECONDS_PER_SLOT=1 for 6x faster slots ## TODO consistent time unit across projects, similar to C++ chrono? + # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#time-parameters MIN_ATTESTATION_INCLUSION_DELAY* = 2'u64^2 ##\ ## (24 seconds) ## Number of slots that attestations stay in the attestation @@ -138,14 +139,14 @@ const # --------------------------------------------------------------- # https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#state-list-lengths LATEST_RANDAO_MIXES_LENGTH* = 8192 - LATEST_ACTIVE_INDEX_ROOTS_LENGTH* = 8192 # 2'u64^13, epochs - LATEST_SLASHED_EXIT_LENGTH* = 8192 # epochs + EPOCHS_PER_HISTORICAL_VECTOR* = 8192 # 2'u64^13, epochs + EPOCHS_PER_SLASHINGS_VECTOR* = 8192 # epochs # Reward and penalty quotients # --------------------------------------------------------------- - # https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#rewards-and-penalties - BASE_REWARD_FACTOR* = 2'u64^5 - WHISTLEBLOWING_REWARD_QUOTIENT* = 2'u64^9 + # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#rewards-and-penalties + BASE_REWARD_FACTOR* = 2'u64^6 + WHISTLEBLOWER_REWARD_QUOTIENT* = 2'u64^9 PROPOSER_REWARD_QUOTIENT* = 2'u64^3 INACTIVITY_PENALTY_QUOTIENT* = 2'u64^25 MIN_SLASHING_PENALTY_QUOTIENT* = 32 # 2^5 @@ -163,7 +164,7 @@ const type # Signature domains # --------------------------------------------------------------- - # https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#signature-domains + # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#signature-domain-types DomainType* {.pure.} = enum DOMAIN_BEACON_PROPOSER = 0 DOMAIN_RANDAO = 1 diff --git a/beacon_chain/spec/presets/minimal.nim b/beacon_chain/spec/presets/minimal.nim index 600377dfb..d3f164aa6 100644 --- a/beacon_chain/spec/presets/minimal.nim +++ b/beacon_chain/spec/presets/minimal.nim @@ -50,7 +50,7 @@ const # Gwei values # --------------------------------------------------------------- - # https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#gwei-values + # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#gwei-values # Unchanged MIN_DEPOSIT_AMOUNT* = 2'u64^0 * 10'u64^9 @@ -70,7 +70,7 @@ const # Time parameters # --------------------------------------------------------------- - # https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_fork-choice.md#time-parameters + # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_fork-choice.md#time-parameters # Unchanged SECONDS_PER_SLOT*{.intdefine.} = 6'u64 @@ -102,23 +102,23 @@ const # Changed LATEST_RANDAO_MIXES_LENGTH* = 64 - LATEST_ACTIVE_INDEX_ROOTS_LENGTH* = 64 - LATEST_SLASHED_EXIT_LENGTH* = 64 + EPOCHS_PER_HISTORICAL_VECTOR* = 64 + EPOCHS_PER_SLASHINGS_VECTOR* = 64 # Reward and penalty quotients # --------------------------------------------------------------- - # https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#rewards-and-penalties + # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#rewards-and-penalties # Unchanged - BASE_REWARD_FACTOR* = 2'u64^5 - WHISTLEBLOWING_REWARD_QUOTIENT* = 2'u64^9 + BASE_REWARD_FACTOR* = 2'u64^6 + WHISTLEBLOWER_REWARD_QUOTIENT* = 2'u64^9 PROPOSER_REWARD_QUOTIENT* = 2'u64^3 INACTIVITY_PENALTY_QUOTIENT* = 2'u64^25 MIN_SLASHING_PENALTY_QUOTIENT* = 32 # 2^5 # Max operations per block # --------------------------------------------------------------- - # https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#max-operations-per-block + # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#max-operations-per-block # Unchanged MAX_PROPOSER_SLASHINGS* = 2^4 @@ -131,7 +131,7 @@ const type # Signature domains # --------------------------------------------------------------- - # https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#signature-domains + # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#signature-domain-types DomainType* {.pure.} = enum DOMAIN_BEACON_PROPOSER = 0 DOMAIN_RANDAO = 1 diff --git a/beacon_chain/spec/state_transition_block.nim b/beacon_chain/spec/state_transition_block.nim index 7c22b6265..a0394cc28 100644 --- a/beacon_chain/spec/state_transition_block.nim +++ b/beacon_chain/spec/state_transition_block.nim @@ -37,7 +37,7 @@ import # TODO - cleanup imports ../extras, ../ssz, ../beacon_node_types, beaconstate, bitfield, crypto, datatypes, digest, helpers, validator -# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#block-header +# https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#block-header proc processBlockHeader( state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags, stateCache: var StateCache): bool = @@ -61,6 +61,7 @@ proc processBlockHeader( state.latest_block_header = BeaconBlockHeader( slot: blck.slot, parent_root: blck.parent_root, + state_root: Eth2Digest(), # Overwritten in the next `process_slot` call body_root: hash_tree_root(blck.body), ) @@ -124,7 +125,7 @@ func processEth1Data(state: var BeaconState, body: BeaconBlockBody) = SLOTS_PER_ETH1_VOTING_PERIOD: state.eth1_data = body.eth1_data -# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#is_slashable_validator +# https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#is_slashable_validator func is_slashable_validator(validator: Validator, epoch: Epoch): bool = # Check if ``validator`` is slashable. (not validator.slashed) and diff --git a/beacon_chain/spec/state_transition_epoch.nim b/beacon_chain/spec/state_transition_epoch.nim index fcd6fb7e9..f8d580530 100644 --- a/beacon_chain/spec/state_transition_epoch.nim +++ b/beacon_chain/spec/state_transition_epoch.nim @@ -148,21 +148,23 @@ 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_epoch - old_current_justified_epoch = state.current_justified_epoch + old_previous_justified_epoch = state.previous_justified_checkpoint.epoch + old_current_justified_epoch = state.current_justified_checkpoint.epoch # Process justifications - state.previous_justified_epoch = state.current_justified_epoch - state.previous_justified_root = state.current_justified_root + 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_epoch = previous_epoch - state.current_justified_root = - get_block_root(state, state.current_justified_epoch) + state.current_justified_checkpoint.epoch = previous_epoch + state.current_justified_checkpoint.root = + get_block_root(state, state.current_justified_checkpoint.epoch) state.justification_bits = state.justification_bits or (1 shl 1) let current_epoch_matching_target_balance = get_attesting_balance(state, @@ -170,9 +172,9 @@ func process_justification_and_finalization( stateCache) if current_epoch_matching_target_balance * 3 >= get_total_active_balance(state) * 2: - state.current_justified_epoch = current_epoch - state.current_justified_root = - get_block_root(state, state.current_justified_epoch) + state.current_justified_checkpoint.epoch = current_epoch + state.current_justified_checkpoint.root = + get_block_root(state, state.current_justified_checkpoint.epoch) state.justification_bits = state.justification_bits or (1 shl 0) # Process finalizations @@ -182,48 +184,44 @@ func process_justification_and_finalization( ## as source if (bitfield shr 1) mod 8 == 0b111 and old_previous_justified_epoch + 3 == current_epoch: - state.finalized_epoch = old_previous_justified_epoch - state.finalized_root = get_block_root(state, state.finalized_epoch) + state.finalized_checkpoint.epoch = old_previous_justified_epoch + state.finalized_checkpoint.root = + get_block_root(state, state.finalized_checkpoint.epoch) ## 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_epoch = old_previous_justified_epoch - state.finalized_root = get_block_root(state, state.finalized_epoch) + state.finalized_checkpoint.epoch = old_previous_justified_epoch + state.finalized_checkpoint.root = + get_block_root(state, state.finalized_checkpoint.epoch) ## 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_epoch = old_current_justified_epoch - state.finalized_root = get_block_root(state, state.finalized_epoch) + state.finalized_checkpoint.epoch = old_current_justified_epoch + state.finalized_checkpoint.root = + get_block_root(state, state.finalized_checkpoint.epoch) ## 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_epoch = old_current_justified_epoch - state.finalized_root = get_block_root(state, state.finalized_epoch) + state.finalized_checkpoint.epoch = old_current_justified_epoch + state.finalized_checkpoint.root = + get_block_root(state, state.finalized_checkpoint.epoch) -# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#crosslinks +# https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#crosslinks 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 - ## [c for c in state.current_crosslinks] from spec. state.previous_crosslinks = state.current_crosslinks - for epoch_int in get_previous_epoch(state).uint64 .. - get_current_epoch(state).uint64: - # This issue comes up regularly -- iterating means an int type, - # which then needs re-conversion back to specialized type. - let epoch = epoch_int.Epoch + for epoch in @[get_previous_epoch(state), get_current_epoch(state)]: for offset in 0'u64 ..< get_committee_count(state, epoch): let shard = (get_start_shard(state, epoch) + offset) mod SHARD_COUNT crosslink_committee = - get_crosslink_committee(state, epoch, shard, stateCache) + toSet(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. @@ -302,7 +300,7 @@ func get_attestation_deltas(state: BeaconState, stateCache: var StateCache): attestation.inclusion_delay # Inactivity penalty - let finality_delay = previous_epoch - state.finalized_epoch + let finality_delay = previous_epoch - state.finalized_checkpoint.epoch if finality_delay > MIN_EPOCHS_TO_INACTIVITY_PENALTY: let matching_target_attesting_indices = get_unslashed_attesting_indices( @@ -359,28 +357,18 @@ func process_rewards_and_penalties( increase_balance(state, i.ValidatorIndex, rewards1[i] + rewards2[i]) decrease_balance(state, i.ValidatorIndex, penalties1[i] + penalties2[i]) -# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#slashings +# https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#slashings func process_slashings(state: var BeaconState) = let - current_epoch = get_current_epoch(state) + epoch = get_current_epoch(state) total_balance = get_total_active_balance(state) - # Compute `total_penalties` - total_at_start = state.slashings[ - (current_epoch + 1) mod LATEST_SLASHED_EXIT_LENGTH] - total_at_end = - state.slashings[current_epoch mod - LATEST_SLASHED_EXIT_LENGTH] - total_penalties = total_at_end - total_at_start - for index, validator in state.validators: - if validator.slashed and current_epoch == validator.withdrawable_epoch - - LATEST_SLASHED_EXIT_LENGTH div 2: - let - penalty = max( - validator.effective_balance * - min(total_penalties * 3, total_balance) div total_balance, - validator.effective_balance div MIN_SLASHING_PENALTY_QUOTIENT) + if validator.slashed and epoch + EPOCHS_PER_SLASHINGS_VECTOR div 2 == + validator.withdrawable_epoch: + let penalty = + validator.effective_balance * + min(sum(state.slashings) * 3, total_balance) div total_balance decrease_balance(state, index.ValidatorIndex, penalty) # https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#final-updates @@ -410,8 +398,8 @@ func process_final_updates(state: var BeaconState) = SHARD_COUNT # Set total slashed balances - state.slashings[next_epoch mod LATEST_SLASHED_EXIT_LENGTH] = ( - state.slashings[current_epoch mod LATEST_SLASHED_EXIT_LENGTH] + state.slashings[next_epoch mod EPOCHS_PER_SLASHINGS_VECTOR] = ( + state.slashings[current_epoch mod EPOCHS_PER_SLASHINGS_VECTOR] ) # Set randao mix @@ -438,13 +426,13 @@ func processEpoch*(state: var BeaconState) = 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 + # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/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 + # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#crosslinks process_crosslinks(state, per_epoch_cache) - # https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#rewards-and-penalties-1 + # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#rewards-and-penalties-1 process_rewards_and_penalties(state, per_epoch_cache) # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#registry-updates @@ -455,8 +443,8 @@ func processEpoch*(state: var BeaconState) = ## 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 + # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#slashings process_slashings(state) - # https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#final-updates + # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#final-updates process_final_updates(state) diff --git a/beacon_chain/spec/validator.nim b/beacon_chain/spec/validator.nim index 321ce82f7..6c40b80f0 100644 --- a/beacon_chain/spec/validator.nim +++ b/beacon_chain/spec/validator.nim @@ -86,7 +86,7 @@ func get_previous_epoch*(state: BeaconState): Epoch = if current_epoch == GENESIS_EPOCH: current_epoch else: - (current_epoch - 1).Epoch + current_epoch - 1 # https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#get_shard_delta func get_shard_delta*(state: BeaconState, epoch: Epoch): uint64 = @@ -166,10 +166,10 @@ func get_empty_per_epoch_cache*(): StateCache = 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 +# https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#get_beacon_proposer_index func get_beacon_proposer_index*(state: BeaconState, stateCache: var StateCache): ValidatorIndex = - # Return the current beacon proposer index. + # Return the beacon proposer index at the current slot. const MAX_RANDOM_BYTE = 255 @@ -187,6 +187,7 @@ func get_beacon_proposer_index*(state: BeaconState, stateCache: var StateCache): buffer: array[(32+8), byte] buffer[0..31] = seed.data while true: + # TODO update to new int_to_bytes interface buffer[32..39] = int_to_bytes8(i.uint64 div 32) let candidate_index = first_committee[((epoch + i.uint64) mod diff --git a/beacon_chain/state_transition.nim b/beacon_chain/state_transition.nim index a38360629..ea717973a 100644 --- a/beacon_chain/state_transition.nim +++ b/beacon_chain/state_transition.nim @@ -45,7 +45,7 @@ func advance_slot(state: var BeaconState) = state.slot += 1 -# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#beacon-chain-state-transition-function +# https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#beacon-chain-state-transition-function func process_slot(state: var BeaconState) = # Cache state root let previous_state_root = hash_tree_root(state) diff --git a/tests/official/fixtures_utils.nim b/tests/official/fixtures_utils.nim index 620384d6e..5cff3a094 100644 --- a/tests/official/fixtures_utils.nim +++ b/tests/official/fixtures_utils.nim @@ -43,10 +43,10 @@ type MIN_VALIDATOR_WITHDRAWABILITY_DELAY*: uint64 PERSISTENT_COMMITTEE_PERIOD*: uint64 LATEST_RANDAO_MIXES_LENGTH*: int - LATEST_ACTIVE_INDEX_ROOTS_LENGTH*: int - LATEST_SLASHED_EXIT_LENGTH*: int + EPOCHS_PER_HISTORICAL_VECTOR*: int + EPOCHS_PER_SLASHINGS_VECTOR*: int BASE_REWARD_FACTOR*: uint64 - WHISTLEBLOWING_REWARD_QUOTIENT*: uint64 + WHISTLEBLOWER_REWARD_QUOTIENT*: uint64 PROPOSER_REWARD_QUOTIENT*: uint64 INACTIVITY_PENALTY_QUOTIENT*: uint64 MIN_SLASHING_PENALTY_QUOTIENT*: int