diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 55791e25f5..b19993540a 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -16,7 +16,6 @@ - [Gwei values](#gwei-values) - [Initial values](#initial-values) - [Time parameters](#time-parameters) - - [State list lengths](#state-list-lengths) - [Reward and penalty quotients](#reward-and-penalty-quotients) - [Max operations per block](#max-operations-per-block) - [Signature domains](#signature-domains) @@ -79,8 +78,6 @@ - [`bytes_to_int`](#bytes_to_int) - [`get_total_balance`](#get_total_balance) - [`get_domain`](#get_domain) - - [`get_bitfield_bit`](#get_bitfield_bit) - - [`verify_bitfield`](#verify_bitfield) - [`convert_to_indexed`](#convert_to_indexed) - [`verify_indexed_attestation`](#verify_indexed_attestation) - [`is_double_vote`](#is_double_vote) @@ -209,17 +206,10 @@ These configurations are updated for releases, but may be out of sync during `de | `PERSISTENT_COMMITTEE_PERIOD` | `2**11` (= 2,048) | epochs | 9 days | | `MAX_CROSSLINK_EPOCHS` | `2**6` (= 64) | epochs | ~7 hours | | `MIN_EPOCHS_TO_INACTIVITY_PENALTY` | `2**2` (= 4) | epochs | 25.6 minutes | +| `EPOCHS_PER_HISTORICAL_VECTOR` | `2**13` (= 8,192) | epochs | ~36 days | * `MAX_CROSSLINK_EPOCHS` should be a small constant times `SHARD_COUNT // SLOTS_PER_EPOCH` -### State list lengths - -| Name | Value | Unit | Duration | -| - | - | :-: | :-: | -| `LATEST_RANDAO_MIXES_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | -| `LATEST_ACTIVE_INDEX_ROOTS_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | -| `LATEST_SLASHED_EXIT_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | - ### Reward and penalty quotients | Name | Value | @@ -329,7 +319,7 @@ The types are defined topologically to aid in facilitating an executable version # Attestation data 'data': AttestationData, # Custody bit - 'custody_bit': 'bool', + 'custody_bit': 'bit', } ``` @@ -401,7 +391,7 @@ The types are defined topologically to aid in facilitating an executable version ```python { # Attester aggregation bitfield - 'aggregation_bitfield': 'bytes', + 'aggregation_bitfield': ['bit'], # Attestation data 'data': AttestationData, # Inclusion slot @@ -453,11 +443,11 @@ The types are defined topologically to aid in facilitating an executable version ```python { # Attester aggregation bitfield - 'aggregation_bitfield': 'bytes', + 'aggregation_bitfield': ['bit'], # Attestation data 'data': AttestationData, # Custody bitfield - 'custody_bitfield': 'bytes', + 'custody_bitfield': ['bit'], # BLS aggregate signature 'signature': 'bytes96', } @@ -556,7 +546,7 @@ The types are defined topologically to aid in facilitating an executable version 'balances': ['uint64'], # Randomness and committees - 'latest_randao_mixes': ['bytes32', LATEST_RANDAO_MIXES_LENGTH], + 'latest_randao_mixes': ['bytes32', EPOCHS_PER_HISTORICAL_VECTOR], 'latest_start_shard': 'uint64', # Finality @@ -566,7 +556,7 @@ The types are defined topologically to aid in facilitating an executable version 'current_justified_epoch': 'uint64', 'previous_justified_root': 'bytes32', 'current_justified_root': 'bytes32', - 'justification_bitfield': 'uint64', + 'justification_bitfield': ['bit', EPOCHS_PER_HISTORICAL_VECTOR], 'finalized_epoch': 'uint64', 'finalized_root': 'bytes32', @@ -575,8 +565,8 @@ The types are defined topologically to aid in facilitating an executable version 'previous_crosslinks': [Crosslink, SHARD_COUNT], 'latest_block_roots': ['bytes32', SLOTS_PER_HISTORICAL_ROOT], 'latest_state_roots': ['bytes32', SLOTS_PER_HISTORICAL_ROOT], - 'latest_active_index_roots': ['bytes32', LATEST_ACTIVE_INDEX_ROOTS_LENGTH], - 'latest_slashed_balances': ['uint64', LATEST_SLASHED_EXIT_LENGTH], # Balances slashed at every withdrawal period + 'latest_active_index_roots': ['bytes32', EPOCHS_PER_HISTORICAL_VECTOR], + 'latest_slashed_balances': ['uint64', EPOCHS_PER_HISTORICAL_VECTOR], # Balances slashed at every withdrawal period 'latest_block_header': BeaconBlockHeader, # `latest_block_header.state_root == ZERO_HASH` temporarily 'historical_roots': ['bytes32'], @@ -888,8 +878,8 @@ def get_randao_mix(state: BeaconState, """ Return the randao mix at a recent ``epoch``. """ - assert get_current_epoch(state) - LATEST_RANDAO_MIXES_LENGTH < epoch <= get_current_epoch(state) - return state.latest_randao_mixes[epoch % LATEST_RANDAO_MIXES_LENGTH] + assert get_current_epoch(state) - EPOCHS_PER_HISTORICAL_VECTOR < epoch <= get_current_epoch(state) + return state.latest_randao_mixes[epoch % EPOCHS_PER_HISTORICAL_VECTOR] ``` ### `get_active_index_root` @@ -900,8 +890,8 @@ def get_active_index_root(state: BeaconState, """ Return the index root at a recent ``epoch``. """ - assert get_current_epoch(state) - LATEST_ACTIVE_INDEX_ROOTS_LENGTH + ACTIVATION_EXIT_DELAY < epoch <= get_current_epoch(state) + ACTIVATION_EXIT_DELAY - return state.latest_active_index_roots[epoch % LATEST_ACTIVE_INDEX_ROOTS_LENGTH] + assert get_current_epoch(state) - EPOCHS_PER_HISTORICAL_VECTOR + ACTIVATION_EXIT_DELAY < epoch <= get_current_epoch(state) + ACTIVATION_EXIT_DELAY + return state.latest_active_index_roots[epoch % EPOCHS_PER_HISTORICAL_VECTOR] ``` ### `generate_seed` @@ -967,8 +957,8 @@ def get_attesting_indices(state: BeaconState, """ crosslink_committees = get_crosslink_committees_at_slot(state, attestation_data.slot) crosslink_committee = [committee for committee, shard in crosslink_committees if shard == attestation_data.shard][0] - assert verify_bitfield(bitfield, len(crosslink_committee)) - return sorted([index for i, index in enumerate(crosslink_committee) if get_bitfield_bit(bitfield, i) == 0b1]) + assert len(bitfield) == len(crosslink_committee) + return sorted([index for position, index in enumerate(crosslink_committee) if bitfield[position]]) ``` ### `int_to_bytes1`, `int_to_bytes2`, ... @@ -1006,34 +996,6 @@ def get_domain(state: BeaconState, return bytes_to_int(fork_version + int_to_bytes4(domain_type)) ``` -### `get_bitfield_bit` - -```python -def get_bitfield_bit(bitfield: bytes, i: int) -> int: - """ - Extract the bit in ``bitfield`` at position ``i``. - """ - return (bitfield[i // 8] >> (i % 8)) % 2 -``` - -### `verify_bitfield` - -```python -def verify_bitfield(bitfield: bytes, committee_size: int) -> bool: - """ - Verify ``bitfield`` against the ``committee_size``. - """ - if len(bitfield) != (committee_size + 7) // 8: - return False - - # Check `bitfield` is padded with zero bits only - for i in range(committee_size, len(bitfield) * 8): - if get_bitfield_bit(bitfield, i) == 0b1: - return False - - return True -``` - ### `convert_to_indexed` ```python @@ -1209,9 +1171,9 @@ def slash_validator(state: BeaconState, slashed_index: ValidatorIndex, whistlebl current_epoch = get_current_epoch(state) initiate_validator_exit(state, slashed_index) state.validator_registry[slashed_index].slashed = True - state.validator_registry[slashed_index].withdrawable_epoch = current_epoch + LATEST_SLASHED_EXIT_LENGTH + state.validator_registry[slashed_index].withdrawable_epoch = current_epoch + EPOCHS_PER_HISTORICAL_VECTOR slashed_balance = state.validator_registry[slashed_index].effective_balance - state.latest_slashed_balances[current_epoch % LATEST_SLASHED_EXIT_LENGTH] += slashed_balance + state.latest_slashed_balances[current_epoch % EPOCHS_PER_HISTORICAL_VECTOR] += slashed_balance proposer_index = get_beacon_proposer_index(state) if whistleblower_index is None: @@ -1256,7 +1218,7 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], validator.activation_epoch = GENESIS_EPOCH genesis_active_index_root = hash_tree_root(get_active_validator_indices(state, GENESIS_EPOCH)) - for index in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH): + for index in range(EPOCHS_PER_HISTORICAL_VECTOR): state.latest_active_index_roots[index] = genesis_active_index_root return state @@ -1402,34 +1364,36 @@ def process_justification_and_finalization(state: BeaconState) -> None: # Process justifications state.previous_justified_epoch = state.current_justified_epoch state.previous_justified_root = state.current_justified_root - state.justification_bitfield = (state.justification_bitfield << 1) % 2**64 previous_epoch_matching_target_balance = get_attesting_balance(state, get_matching_target_attestations(state, previous_epoch)) 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.justification_bitfield |= (1 << 1) + state.justification_bitfield[previous_epoch % EPOCHS_PER_HISTORICAL_VECTOR] = True current_epoch_matching_target_balance = get_attesting_balance(state, get_matching_target_attestations(state, current_epoch)) 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.justification_bitfield |= (1 << 0) + state.justification_bitfield[current_epoch % EPOCHS_PER_HISTORICAL_VECTOR] = True # Process finalizations - bitfield = state.justification_bitfield + bit_0 = state.justification_bitfield[(current_epoch - 0) % EPOCHS_PER_HISTORICAL_VECTOR] + bit_1 = state.justification_bitfield[(current_epoch - 1) % EPOCHS_PER_HISTORICAL_VECTOR] + bit_2 = state.justification_bitfield[(current_epoch - 2) % EPOCHS_PER_HISTORICAL_VECTOR] + bit_3 = state.justification_bitfield[(current_epoch - 3) % EPOCHS_PER_HISTORICAL_VECTOR] # The 2nd/3rd/4th most recent epochs are justified, the 2nd using the 4th as source - if (bitfield >> 1) % 8 == 0b111 and old_previous_justified_epoch == current_epoch - 3: + if bit_1 and bit_2 and bit_3 and old_previous_justified_epoch == current_epoch - 3: state.finalized_epoch = old_previous_justified_epoch state.finalized_root = get_block_root(state, state.finalized_epoch) # The 2nd/3rd most recent epochs are justified, the 2nd using the 3rd as source - if (bitfield >> 1) % 4 == 0b11 and old_previous_justified_epoch == current_epoch - 2: + if bit_1 and bit_2 and old_previous_justified_epoch == current_epoch - 2: state.finalized_epoch = old_previous_justified_epoch state.finalized_root = get_block_root(state, state.finalized_epoch) # The 1st/2nd/3rd most recent epochs are justified, the 1st using the 3rd as source - if (bitfield >> 0) % 8 == 0b111 and old_current_justified_epoch == current_epoch - 2: + if bit_0 and bit_1 and bit_2 and old_current_justified_epoch == current_epoch - 2: state.finalized_epoch = old_current_justified_epoch state.finalized_root = get_block_root(state, state.finalized_epoch) # The 1st/2nd most recent epochs are justified, the 1st using the 2nd as source - if (bitfield >> 0) % 4 == 0b11 and old_current_justified_epoch == current_epoch - 1: + if bit_0 and bit_1 and old_current_justified_epoch == current_epoch - 1: state.finalized_epoch = old_current_justified_epoch state.finalized_root = get_block_root(state, state.finalized_epoch) ``` @@ -1577,12 +1541,12 @@ def process_slashings(state: BeaconState) -> None: total_balance = get_total_balance(state, active_validator_indices) # Compute `total_penalties` - total_at_start = state.latest_slashed_balances[(current_epoch + 1) % LATEST_SLASHED_EXIT_LENGTH] - total_at_end = state.latest_slashed_balances[current_epoch % LATEST_SLASHED_EXIT_LENGTH] + total_at_start = state.latest_slashed_balances[(current_epoch + 1) % EPOCHS_PER_HISTORICAL_VECTOR] + total_at_end = state.latest_slashed_balances[current_epoch % EPOCHS_PER_HISTORICAL_VECTOR] total_penalties = total_at_end - total_at_start for index, validator in enumerate(state.validator_registry): - if validator.slashed and current_epoch == validator.withdrawable_epoch - LATEST_SLASHED_EXIT_LENGTH // 2: + if validator.slashed and current_epoch == validator.withdrawable_epoch - EPOCHS_PER_HISTORICAL_VECTOR // 2: penalty = max( validator.effective_balance * min(total_penalties * 3, total_balance) // total_balance, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT @@ -1610,16 +1574,16 @@ def process_final_updates(state: BeaconState) -> None: # Update start shard state.latest_start_shard = (state.latest_start_shard + get_shard_delta(state, current_epoch)) % SHARD_COUNT # Set active index root - index_root_position = (next_epoch + ACTIVATION_EXIT_DELAY) % LATEST_ACTIVE_INDEX_ROOTS_LENGTH + index_root_position = (next_epoch + ACTIVATION_EXIT_DELAY) % EPOCHS_PER_HISTORICAL_VECTOR state.latest_active_index_roots[index_root_position] = hash_tree_root( get_active_validator_indices(state, next_epoch + ACTIVATION_EXIT_DELAY) ) # Set total slashed balances - state.latest_slashed_balances[next_epoch % LATEST_SLASHED_EXIT_LENGTH] = ( - state.latest_slashed_balances[current_epoch % LATEST_SLASHED_EXIT_LENGTH] + state.latest_slashed_balances[next_epoch % EPOCHS_PER_HISTORICAL_VECTOR] = ( + state.latest_slashed_balances[current_epoch % EPOCHS_PER_HISTORICAL_VECTOR] ) # Set randao mix - state.latest_randao_mixes[next_epoch % LATEST_RANDAO_MIXES_LENGTH] = get_randao_mix(state, current_epoch) + state.latest_randao_mixes[next_epoch % EPOCHS_PER_HISTORICAL_VECTOR] = get_randao_mix(state, current_epoch) # Set historical root accumulator if next_epoch % (SLOTS_PER_HISTORICAL_ROOT // SLOTS_PER_EPOCH) == 0: historical_batch = HistoricalBatch( @@ -1674,7 +1638,7 @@ def process_randao(state: BeaconState, block: BeaconBlock) -> None: # Verify that the provided randao value is valid assert bls_verify(proposer.pubkey, hash_tree_root(get_current_epoch(state)), block.body.randao_reveal, get_domain(state, DOMAIN_RANDAO)) # Mix it in - state.latest_randao_mixes[get_current_epoch(state) % LATEST_RANDAO_MIXES_LENGTH] = ( + state.latest_randao_mixes[get_current_epoch(state) % EPOCHS_PER_HISTORICAL_VECTOR] = ( xor(get_randao_mix(state, get_current_epoch(state)), hash(block.body.randao_reveal)) )