This commit is contained in:
Justin Drake 2019-06-30 10:56:14 +01:00
parent 369c457d76
commit e8532ced79
12 changed files with 224 additions and 251 deletions

View File

@ -50,10 +50,10 @@
- [`BeaconState`](#beaconstate) - [`BeaconState`](#beaconstate)
- [Helper functions](#helper-functions) - [Helper functions](#helper-functions)
- [Math](#math) - [Math](#math)
- [`int_to_bytes`](#int_to_bytes)
- [`bytes_to_int`](#bytes_to_int)
- [`integer_squareroot`](#integer_squareroot) - [`integer_squareroot`](#integer_squareroot)
- [`xor`](#xor) - [`xor`](#xor)
- [`int_to_bytes`](#int_to_bytes)
- [`bytes_to_int`](#bytes_to_int)
- [Crypto](#crypto) - [Crypto](#crypto)
- [`hash`](#hash) - [`hash`](#hash)
- [`hash_tree_root`](#hash_tree_root) - [`hash_tree_root`](#hash_tree_root)
@ -64,42 +64,39 @@
- [Predicates](#predicates) - [Predicates](#predicates)
- [`is_active_validator`](#is_active_validator) - [`is_active_validator`](#is_active_validator)
- [`is_slashable_validator`](#is_slashable_validator) - [`is_slashable_validator`](#is_slashable_validator)
- [`is_valid_merkle_branch`](#is_valid_merkle_branch)
- [`is_slashable_attestation_data`](#is_slashable_attestation_data) - [`is_slashable_attestation_data`](#is_slashable_attestation_data)
- [`is_valid_merkle_branch`](#is_valid_merkle_branch)
- [Misc](#misc)
- [`shuffle_index`](#shuffle_index)
- [`compute_committee`](#compute_committee)
- [`validate_indexed_attestation`](#validate_indexed_attestation)
- [`slot_to_epoch`](#slot_to_epoch)
- [`epoch_first_slot`](#epoch_first_slot)
- [`delayed_activation_exit_epoch`](#delayed_activation_exit_epoch)
- [`bls_domain`](#bls_domain)
- [Beacon state getters](#beacon-state-getters) - [Beacon state getters](#beacon-state-getters)
- [`get_current_epoch`](#get_current_epoch) - [`get_current_epoch`](#get_current_epoch)
- [`get_previous_epoch`](#get_previous_epoch) - [`get_previous_epoch`](#get_previous_epoch)
- [`get_committee_count`](#get_committee_count)
- [`get_start_shard`](#get_start_shard)
- [`get_active_validator_indices`](#get_active_validator_indices)
- [`bls_domain`](#bls_domain)
- [`slot_to_epoch`](#slot_to_epoch)
- [`epoch_start_slot`](#epoch_start_slot)
- [`increase_balance`](#increase_balance)
- [`decrease_balance`](#decrease_balance)
- [`get_shard_delta`](#get_shard_delta)
- [`get_attestation_data_slot`](#get_attestation_data_slot)
- [`get_block_root_at_slot`](#get_block_root_at_slot)
- [`get_block_root`](#get_block_root) - [`get_block_root`](#get_block_root)
- [`get_block_root_at_slot`](#get_block_root_at_slot)
- [`get_randao_mix`](#get_randao_mix) - [`get_randao_mix`](#get_randao_mix)
- [`get_compact_committees_root`](#get_compact_committees_root) - [`get_active_validator_indices`](#get_active_validator_indices)
- [`get_churn_limit`](#get_churn_limit)
- [`get_committee_count`](#get_committee_count)
- [`get_seed`](#get_seed) - [`get_seed`](#get_seed)
- [`get_beacon_proposer_index`](#get_beacon_proposer_index)
- [`shuffle_index`](#shuffle_index)
- [`compute_committee`](#compute_committee)
- [`get_crosslink_committee`](#get_crosslink_committee) - [`get_crosslink_committee`](#get_crosslink_committee)
- [`get_attesting_indices`](#get_attesting_indices) - [`get_start_shard`](#get_start_shard)
- [`get_shard_delta`](#get_shard_delta)
- [`get_proposer_index`](#get_proposer_index)
- [`get_attestation_data_slot`](#get_attestation_data_slot)
- [`get_compact_committees_root`](#get_compact_committees_root)
- [`get_total_balance`](#get_total_balance) - [`get_total_balance`](#get_total_balance)
- [`get_domain`](#get_domain) - [`get_domain`](#get_domain)
- [`get_indexed_attestation`](#get_indexed_attestation) - [`get_indexed_attestation`](#get_indexed_attestation)
- [`validate_indexed_attestation`](#validate_indexed_attestation) - [`get_attesting_indices`](#get_attesting_indices)
- [`delayed_activation_exit_epoch`](#delayed_activation_exit_epoch) - [State mutators](#state-mutators)
- [`get_churn_limit`](#get_churn_limit) - [`increase_balance`](#increase_balance)
- [Routines for updating validator status](#routines-for-updating-validator-status) - [`decrease_balance`](#decrease_balance)
- [`initiate_validator_exit`](#initiate_validator_exit) - [`initiate_validator_exit`](#initiate_validator_exit)
- [`slash_validator`](#slash_validator) - [`slash_validator`](#slash_validator)
- [Genesis](#genesis) - [Genesis](#genesis)
@ -201,6 +198,7 @@ The following values are (non-configurable) constants used throughout the specif
| `MIN_GENESIS_ACTIVE_VALIDATOR_COUNT` | `2**16` (= 65,536) | | `MIN_GENESIS_ACTIVE_VALIDATOR_COUNT` | `2**16` (= 65,536) |
| `MIN_GENESIS_TIME` | `1578009600` (Jan 3, 2020) | | `MIN_GENESIS_TIME` | `1578009600` (Jan 3, 2020) |
| `JUSTIFICATION_BITS_LENGTH` | `4` | | `JUSTIFICATION_BITS_LENGTH` | `4` |
| `ENDIANNESS` | `'little'` |
* For the safety of crosslinks, `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](https://vitalik.ca/files/Ithaca201807_Sharding.pdf); with sufficient active validators (at least `SLOTS_PER_EPOCH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes of at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.) * For the safety of crosslinks, `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](https://vitalik.ca/files/Ithaca201807_Sharding.pdf); with sufficient active validators (at least `SLOTS_PER_EPOCH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes of at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.)
@ -559,18 +557,6 @@ class BeaconState(Container):
#### `int_to_bytes` #### `int_to_bytes`
```python
def int_to_bytes(integer: int, length: int) -> bytes:
return integer.to_bytes(length, 'little')
```
#### `bytes_to_int`
```python
def bytes_to_int(data: bytes) -> int:
return int.from_bytes(data, 'little')
```
#### `integer_squareroot` #### `integer_squareroot`
```python ```python
@ -597,11 +583,29 @@ def xor(bytes1: Bytes32, bytes2: Bytes32) -> Bytes32:
return Bytes32(a ^ b for a, b in zip(bytes1, bytes2)) return Bytes32(a ^ b for a, b in zip(bytes1, bytes2))
``` ```
```python
def int_to_bytes(integer: int, length: int) -> bytes:
"""
Return the ``length``-byte serialization of ``integer``.
"""
return integer.to_bytes(length, ENDIANNESS)
```
#### `bytes_to_int`
```python
def bytes_to_int(data: bytes) -> int:
"""
Return the integer deserialization of ``data``.
"""
return int.from_bytes(data, ENDIANNESS)
```
### Crypto ### Crypto
#### `hash` #### `hash`
The `hash` function is SHA256. `def hash(data: bytes) -> Hash` is SHA256.
#### `hash_tree_root` #### `hash_tree_root`
@ -625,22 +629,6 @@ The `hash` function is SHA256.
### Predicates ### Predicates
#### `is_valid_merkle_branch`
```python
def is_valid_merkle_branch(leaf: Hash, branch: Sequence[Hash], depth: int, index: int, root: Hash) -> bool:
"""
Check if ``leaf`` at ``index`` verifies against the Merkle ``root`` and ``branch``.
"""
value = leaf
for i in range(depth):
if index // (2**i) % 2:
value = hash(branch[i] + value)
else:
value = hash(value + branch[i])
return value == root
```
#### `is_active_validator` #### `is_active_validator`
```python ```python
@ -676,6 +664,137 @@ def is_slashable_attestation_data(data_1: AttestationData, data_2: AttestationDa
) )
``` ```
#### `is_valid_merkle_branch`
```python
def is_valid_merkle_branch(leaf: Hash, branch: Sequence[Hash], depth: int, index: int, root: Hash) -> bool:
"""
Check if ``leaf`` at ``index`` verifies against the Merkle ``root`` and ``branch``.
"""
value = leaf
for i in range(depth):
if index // (2**i) % 2:
value = hash(branch[i] + value)
else:
value = hash(value + branch[i])
return value == root
```
### Misc
#### `shuffle_index`
```python
def shuffle_index(index: ValidatorIndex, index_count: int, seed: Hash) -> ValidatorIndex:
"""
Return the shuffled validator index corresponding to ``seed`` (and ``index_count``).
"""
assert index < index_count
assert index_count <= 2**40
# Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf)
# See the 'generalized domain' algorithm on page 3
for current_round in range(SHUFFLE_ROUND_COUNT):
pivot = bytes_to_int(hash(seed + int_to_bytes(current_round, length=1))[0:8]) % index_count
flip = ValidatorIndex((pivot + index_count - index) % index_count)
position = max(index, flip)
source = hash(
seed + int_to_bytes(current_round, length=1) +
int_to_bytes(position // 256, length=4)
)
byte = source[(position % 256) // 8]
bit = (byte >> (position % 8)) % 2
index = flip if bit else index
return ValidatorIndex(index)
```
#### `compute_committee`
```python
def compute_committee(indices: Sequence[ValidatorIndex],
seed: Hash, index: int, count: int) -> Sequence[ValidatorIndex]:
"""
Return the committee corresponding to ``indices``, ``seed``, ``index`` and committee ``count``.
"""
start = (len(indices) * index) // count
end = (len(indices) * (index + 1)) // count
return [indices[shuffle_index(ValidatorIndex(i), len(indices), seed)] for i in range(start, end)]
```
#### `validate_indexed_attestation`
```python
def validate_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> None:
"""
Verify validity of ``indexed_attestation``.
"""
bit_0_indices = indexed_attestation.custody_bit_0_indices
bit_1_indices = indexed_attestation.custody_bit_1_indices
# Verify no index has custody bit equal to 1 [to be removed in phase 1]
assert len(bit_1_indices) == 0
# Verify max number of indices
assert len(bit_0_indices) + len(bit_1_indices) <= MAX_VALIDATORS_PER_COMMITTEE
# Verify index sets are disjoint
assert len(set(bit_0_indices).intersection(bit_1_indices)) == 0
# Verify indices are sorted
assert bit_0_indices == sorted(bit_0_indices) and bit_1_indices == sorted(bit_1_indices)
# Verify aggregate signature
assert bls_verify_multiple(
pubkeys=[
bls_aggregate_pubkeys([state.validators[i].pubkey for i in bit_0_indices]),
bls_aggregate_pubkeys([state.validators[i].pubkey for i in bit_1_indices]),
],
message_hashes=[
hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b0)),
hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b1)),
],
signature=indexed_attestation.signature,
domain=get_domain(state, DOMAIN_ATTESTATION, indexed_attestation.data.target.epoch),
)
```
#### `slot_to_epoch`
```python
def slot_to_epoch(slot: Slot) -> Epoch:
"""
Return the epoch number of ``slot``.
"""
return Epoch(slot // SLOTS_PER_EPOCH)
```
#### `epoch_first_slot`
```python
def epoch_first_slot(epoch: Epoch) -> Slot:
"""
Return the first slot of ``epoch``.
"""
return Slot(epoch * SLOTS_PER_EPOCH)
```
#### `delayed_activation_exit_epoch`
```python
def delayed_activation_exit_epoch(epoch: Epoch) -> Epoch:
"""
Return the epoch at which an activation or exit triggered in ``epoch`` takes effect.
"""
return Epoch(epoch + 1 + ACTIVATION_EXIT_DELAY)
```
#### `bls_domain`
```python
def bls_domain(domain_type: int, fork_version: bytes=b'\x00' * 4) -> int:
"""
Return the BLS domain for the ``domain_type`` and ``fork_version``.
"""
return bytes_to_int(int_to_bytes(domain_type, length=4) + fork_version)
```
### Beacon state getters ### Beacon state getters
#### `get_current_epoch` #### `get_current_epoch`
@ -706,7 +825,7 @@ def get_block_root(state: BeaconState, epoch: Epoch) -> Hash:
""" """
Return the block root at a recent ``epoch``. Return the block root at a recent ``epoch``.
""" """
return get_block_root_at_slot(state, epoch_start_slot(epoch)) return get_block_root_at_slot(state, epoch_first_slot(epoch))
``` ```
#### `get_block_root_at_slot` #### `get_block_root_at_slot`
@ -824,10 +943,10 @@ def get_shard_delta(state: BeaconState, epoch: Epoch) -> Shard:
return Shard(min(get_committee_count(state, epoch), SHARD_COUNT - SHARD_COUNT // SLOTS_PER_EPOCH)) return Shard(min(get_committee_count(state, epoch), SHARD_COUNT - SHARD_COUNT // SLOTS_PER_EPOCH))
``` ```
#### `get_beacon_proposer_index` #### `get_proposer_index`
```python ```python
def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex: def get_proposer_index(state: BeaconState) -> ValidatorIndex:
""" """
Return the beacon proposer index at the current epoch. Return the beacon proposer index at the current epoch.
""" """
@ -857,7 +976,7 @@ def get_attestation_data_slot(state: BeaconState, data: AttestationData) -> Slot
""" """
committee_count = get_committee_count(state, data.target.epoch) committee_count = get_committee_count(state, data.target.epoch)
offset = (data.crosslink.shard + SHARD_COUNT - get_start_shard(state, data.target.epoch)) % SHARD_COUNT offset = (data.crosslink.shard + SHARD_COUNT - get_start_shard(state, data.target.epoch)) % SHARD_COUNT
return Slot(epoch_start_slot(data.target.epoch) + offset // (committee_count // SLOTS_PER_EPOCH)) return Slot(epoch_first_slot(data.target.epoch) + offset // (committee_count // SLOTS_PER_EPOCH))
``` ```
#### `get_compact_committees_root` #### `get_compact_committees_root`
@ -936,63 +1055,14 @@ def get_attesting_indices(state: BeaconState,
return set(index for i, index in enumerate(committee) if bits[i]) return set(index for i, index in enumerate(committee) if bits[i])
``` ```
### State mutators
#### `bls_domain`
```python
def bls_domain(domain_type: int, fork_version: bytes=b'\x00\x00\x00\x00') -> int:
"""
Return the bls domain given by the ``domain_type`` and optional 4 byte ``fork_version`` (defaults to zero).
"""
return bytes_to_int(int_to_bytes(domain_type, length=4) + fork_version)
```
#### `slot_to_epoch`
```python
def slot_to_epoch(slot: Slot) -> Epoch:
"""
Return the epoch number of the given ``slot``.
"""
return Epoch(slot // SLOTS_PER_EPOCH)
```
#### `epoch_start_slot`
```python
def epoch_start_slot(epoch: Epoch) -> Slot:
"""
Return the starting slot of the given ``epoch``.
"""
return Slot(epoch * SLOTS_PER_EPOCH)
```
#### `delayed_activation_exit_epoch`
```python
def delayed_activation_exit_epoch(epoch: Epoch) -> Epoch:
"""
Return the epoch at which an activation or exit triggered in ``epoch`` takes effect.
"""
return Epoch(epoch + 1 + ACTIVATION_EXIT_DELAY)
```
#### `increase_balance` #### `increase_balance`
```python ```python
def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None: def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None:
""" """
Increase validator balance by ``delta``. Increase the validator balance at index ``index`` by ``delta``.
""" """
state.balances[index] += delta state.balances[index] += delta
``` ```
@ -1002,91 +1072,17 @@ def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) ->
```python ```python
def decrease_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None: def decrease_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None:
""" """
Decrease validator balance by ``delta`` with underflow protection. Decrease the validator balance at index ``index`` by ``delta``, with underflow protection.
""" """
state.balances[index] = 0 if delta > state.balances[index] else state.balances[index] - delta state.balances[index] = 0 if delta > state.balances[index] else state.balances[index] - delta
``` ```
#### `shuffle_index`
```python
def shuffle_index(index: ValidatorIndex, index_count: int, seed: Hash) -> ValidatorIndex:
"""
Return the shuffled validator index corresponding to ``seed`` (and ``index_count``).
"""
assert index < index_count
assert index_count <= 2**40
# Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf)
# See the 'generalized domain' algorithm on page 3
for current_round in range(SHUFFLE_ROUND_COUNT):
pivot = bytes_to_int(hash(seed + int_to_bytes(current_round, length=1))[0:8]) % index_count
flip = ValidatorIndex((pivot + index_count - index) % index_count)
position = max(index, flip)
source = hash(
seed + int_to_bytes(current_round, length=1) +
int_to_bytes(position // 256, length=4)
)
byte = source[(position % 256) // 8]
bit = (byte >> (position % 8)) % 2
index = flip if bit else index
return ValidatorIndex(index)
```
#### `compute_committee`
```python
def compute_committee(indices: Sequence[ValidatorIndex],
seed: Hash, index: int, count: int) -> Sequence[ValidatorIndex]:
start = (len(indices) * index) // count
end = (len(indices) * (index + 1)) // count
return [indices[shuffle_index(ValidatorIndex(i), len(indices), seed)] for i in range(start, end)]
```
#### `validate_indexed_attestation`
```python
def validate_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> None:
"""
Verify validity of ``indexed_attestation``.
"""
bit_0_indices = indexed_attestation.custody_bit_0_indices
bit_1_indices = indexed_attestation.custody_bit_1_indices
# Verify no index has custody bit equal to 1 [to be removed in phase 1]
assert len(bit_1_indices) == 0
# Verify max number of indices
assert len(bit_0_indices) + len(bit_1_indices) <= MAX_VALIDATORS_PER_COMMITTEE
# Verify index sets are disjoint
assert len(set(bit_0_indices).intersection(bit_1_indices)) == 0
# Verify indices are sorted
assert bit_0_indices == sorted(bit_0_indices) and bit_1_indices == sorted(bit_1_indices)
# Verify aggregate signature
assert bls_verify_multiple(
pubkeys=[
bls_aggregate_pubkeys([state.validators[i].pubkey for i in bit_0_indices]),
bls_aggregate_pubkeys([state.validators[i].pubkey for i in bit_1_indices]),
],
message_hashes=[
hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b0)),
hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b1)),
],
signature=indexed_attestation.signature,
domain=get_domain(state, DOMAIN_ATTESTATION, indexed_attestation.data.target.epoch),
)
```
### Routines for updating validator status
*Note*: All functions in this section mutate `state`.
#### `initiate_validator_exit` #### `initiate_validator_exit`
```python ```python
def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None:
""" """
Initiate the exit of the validator of the given ``index``. Initiate the exit of the validator with index ``index``.
""" """
# Return if validator already initiated exit # Return if validator already initiated exit
validator = state.validators[index] validator = state.validators[index]
@ -1123,7 +1119,7 @@ def slash_validator(state: BeaconState,
decrease_balance(state, slashed_index, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT) decrease_balance(state, slashed_index, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT)
# Apply proposer and whistleblower rewards # Apply proposer and whistleblower rewards
proposer_index = get_beacon_proposer_index(state) proposer_index = get_proposer_index(state)
if whistleblower_index is None: if whistleblower_index is None:
whistleblower_index = proposer_index whistleblower_index = proposer_index
whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT) whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT)
@ -1134,22 +1130,12 @@ def slash_validator(state: BeaconState,
## Genesis ## Genesis
### Genesis state Before the Ethereum 2.0 genesis has been triggered, and for every Ethereum 1.0 block, let `candidate_state = initialize_beacon_state_from_eth1(eth1_block_hash, eth1_timestamp, deposits)` where:
Before the Ethereum 2.0 genesis has been triggered, and for every Ethereum 1.0 block, call `initialize_beacon_state_from_eth1(eth1_block_hash, eth1_timestamp, deposits)` where:
* `eth1_block_hash` is the hash of the Ethereum 1.0 block * `eth1_block_hash` is the hash of the Ethereum 1.0 block
* `eth1_timestamp` is the Unix timestamp corresponding to `eth1_block_hash` * `eth1_timestamp` is the Unix timestamp corresponding to `eth1_block_hash`
* `deposits` is the sequence of all deposits, ordered chronologically, up to the block with hash `eth1_block_hash` * `deposits` is the sequence of all deposits, ordered chronologically, up to the block with hash `eth1_block_hash`
The genesis state `genesis_state` is the return value of calling `initialize_beacon_state_from_eth1(eth1_block_hash, eth1_timestamp, deposits)` only if `is_valid_genesis_state(genesis_state) is True`.
Implementations can choose to support different (more optimized) variations of the below initialization approach:
- Build the `genesis_state` from a stream of deposits by incrementally updating the `state.eth1_data.deposit_root`.
- Compute deposit proofs for the final `state.eth1_data.deposit_root`, and process as a pre-determined collection.
*Note*: The two constants `MIN_GENESIS_TIME` and `MIN_GENESIS_ACTIVE_VALIDATOR_COUNT` have yet to be agreed upon by the community, and can be updated as necessary.
```python ```python
def initialize_beacon_state_from_eth1(eth1_block_hash: Hash, def initialize_beacon_state_from_eth1(eth1_block_hash: Hash,
eth1_timestamp: uint64, eth1_timestamp: uint64,
@ -1175,22 +1161,27 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Hash,
validator.activation_epoch = GENESIS_EPOCH validator.activation_epoch = GENESIS_EPOCH
# Populate compact_committees_roots # Populate compact_committees_roots
genesis_committee_root = get_compact_committees_root(state, GENESIS_EPOCH) committee_root = get_compact_committees_root(state, GENESIS_EPOCH)
for index in range(EPOCHS_PER_HISTORICAL_VECTOR): for index in range(EPOCHS_PER_HISTORICAL_VECTOR):
state.compact_committees_roots[index] = genesis_committee_root state.compact_committees_roots[index] = committee_root
return state return state
``` ```
### Genesis state
Let `genesis_state = candidate_state` whenever `is_valid_genesis_state(candidate_state) is True` for the first time.
```python ```python
def is_valid_genesis_state(state: BeaconState) -> bool: def is_valid_genesis_state(state: BeaconState) -> bool:
if state.genesis_time < MIN_GENESIS_TIME: if state.genesis_time < MIN_GENESIS_TIME:
return False return False
elif len(get_active_validator_indices(state, GENESIS_EPOCH)) < MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: if len(get_active_validator_indices(state, GENESIS_EPOCH)) < MIN_GENESIS_ACTIVE_VALIDATOR_COUNT:
return False return False
else: return True
return True
``` ```
*Note*: The `is_valid_genesis_state` function (including `MIN_GENESIS_TIME` and `MIN_GENESIS_ACTIVE_VALIDATOR_COUNT`) is a placeholder for testing. It has yet to be finalized by the community, and can be updated as necessary.
### Genesis block ### Genesis block
Let `genesis_block = BeaconBlock(state_root=hash_tree_root(genesis_state))`. Let `genesis_block = BeaconBlock(state_root=hash_tree_root(genesis_state))`.
@ -1287,7 +1278,7 @@ def get_matching_head_attestations(state: BeaconState, epoch: Epoch) -> Sequence
```python ```python
def get_unslashed_attesting_indices(state: BeaconState, def get_unslashed_attesting_indices(state: BeaconState,
attestations: Sequence[PendingAttestation]) -> Set[ValidatorIndex]: attestations: Sequence[PendingAttestation]) -> Set[ValidatorIndex]:
output = set() # type: Set[ValidatorIndex] output = set() # Type: Set[ValidatorIndex]
for a in attestations: for a in attestations:
output = output.union(get_attesting_indices(state, a.data, a.aggregation_bits)) output = output.union(get_attesting_indices(state, a.data, a.aggregation_bits))
return set(filter(lambda index: not state.validators[index].slashed, list(output))) return set(filter(lambda index: not state.validators[index].slashed, list(output)))
@ -1566,7 +1557,7 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None:
body_root=hash_tree_root(block.body), body_root=hash_tree_root(block.body),
) )
# Verify proposer is not slashed # Verify proposer is not slashed
proposer = state.validators[get_beacon_proposer_index(state)] proposer = state.validators[get_proposer_index(state)]
assert not proposer.slashed assert not proposer.slashed
# Verify proposer signature # Verify proposer signature
assert bls_verify(proposer.pubkey, signing_root(block), block.signature, get_domain(state, DOMAIN_BEACON_PROPOSER)) assert bls_verify(proposer.pubkey, signing_root(block), block.signature, get_domain(state, DOMAIN_BEACON_PROPOSER))
@ -1578,7 +1569,7 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None:
def process_randao(state: BeaconState, body: BeaconBlockBody) -> None: def process_randao(state: BeaconState, body: BeaconBlockBody) -> None:
epoch = get_current_epoch(state) epoch = get_current_epoch(state)
# Verify RANDAO reveal # Verify RANDAO reveal
proposer = state.validators[get_beacon_proposer_index(state)] proposer = state.validators[get_proposer_index(state)]
assert bls_verify(proposer.pubkey, hash_tree_root(epoch), body.randao_reveal, get_domain(state, DOMAIN_RANDAO)) assert bls_verify(proposer.pubkey, hash_tree_root(epoch), body.randao_reveal, get_domain(state, DOMAIN_RANDAO))
# Mix in RANDAO reveal # Mix in RANDAO reveal
mix = xor(get_randao_mix(state, epoch), hash(body.randao_reveal)) mix = xor(get_randao_mix(state, epoch), hash(body.randao_reveal))
@ -1620,9 +1611,6 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
```python ```python
def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSlashing) -> None: def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSlashing) -> None:
"""
Process ``ProposerSlashing`` operation.
"""
proposer = state.validators[proposer_slashing.proposer_index] proposer = state.validators[proposer_slashing.proposer_index]
# Verify that the epoch is the same # Verify that the epoch is the same
assert slot_to_epoch(proposer_slashing.header_1.slot) == slot_to_epoch(proposer_slashing.header_2.slot) assert slot_to_epoch(proposer_slashing.header_1.slot) == slot_to_epoch(proposer_slashing.header_2.slot)
@ -1642,9 +1630,6 @@ def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSla
```python ```python
def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSlashing) -> None: def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSlashing) -> None:
"""
Process ``AttesterSlashing`` operation.
"""
attestation_1 = attester_slashing.attestation_1 attestation_1 = attester_slashing.attestation_1
attestation_2 = attester_slashing.attestation_2 attestation_2 = attester_slashing.attestation_2
assert is_slashable_attestation_data(attestation_1.data, attestation_2.data) assert is_slashable_attestation_data(attestation_1.data, attestation_2.data)
@ -1665,9 +1650,6 @@ def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSla
```python ```python
def process_attestation(state: BeaconState, attestation: Attestation) -> None: def process_attestation(state: BeaconState, attestation: Attestation) -> None:
"""
Process ``Attestation`` operation.
"""
data = attestation.data data = attestation.data
assert data.crosslink.shard < SHARD_COUNT assert data.crosslink.shard < SHARD_COUNT
assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state)) assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state))
@ -1679,7 +1661,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
data=data, data=data,
aggregation_bits=attestation.aggregation_bits, aggregation_bits=attestation.aggregation_bits,
inclusion_delay=state.slot - attestation_slot, inclusion_delay=state.slot - attestation_slot,
proposer_index=get_beacon_proposer_index(state), proposer_index=get_proposer_index(state),
) )
if data.target.epoch == get_current_epoch(state): if data.target.epoch == get_current_epoch(state):
@ -1705,9 +1687,6 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
```python ```python
def process_deposit(state: BeaconState, deposit: Deposit) -> None: def process_deposit(state: BeaconState, deposit: Deposit) -> None:
"""
Process an Eth1 deposit, registering a validator or increasing its balance.
"""
# Verify the Merkle branch # Verify the Merkle branch
assert is_valid_merkle_branch( assert is_valid_merkle_branch(
leaf=hash_tree_root(deposit.data), leaf=hash_tree_root(deposit.data),
@ -1754,9 +1733,6 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None:
```python ```python
def process_voluntary_exit(state: BeaconState, exit: VoluntaryExit) -> None: def process_voluntary_exit(state: BeaconState, exit: VoluntaryExit) -> None:
"""
Process ``VoluntaryExit`` operation.
"""
validator = state.validators[exit.validator_index] validator = state.validators[exit.validator_index]
# Verify the validator is active # Verify the validator is active
assert is_active_validator(validator, get_current_epoch(state)) assert is_active_validator(validator, get_current_epoch(state))
@ -1777,9 +1753,6 @@ def process_voluntary_exit(state: BeaconState, exit: VoluntaryExit) -> None:
```python ```python
def process_transfer(state: BeaconState, transfer: Transfer) -> None: def process_transfer(state: BeaconState, transfer: Transfer) -> None:
"""
Process ``Transfer`` operation.
"""
# Verify the balance the covers amount and fee (with overflow protection) # Verify the balance the covers amount and fee (with overflow protection)
assert state.balances[transfer.sender] >= max(transfer.amount + transfer.fee, transfer.amount, transfer.fee) assert state.balances[transfer.sender] >= max(transfer.amount + transfer.fee, transfer.amount, transfer.fee)
# A transfer is valid in only one slot # A transfer is valid in only one slot
@ -1803,7 +1776,7 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None:
# Process the transfer # Process the transfer
decrease_balance(state, transfer.sender, transfer.amount + transfer.fee) decrease_balance(state, transfer.sender, transfer.amount + transfer.fee)
increase_balance(state, transfer.recipient, transfer.amount) increase_balance(state, transfer.recipient, transfer.amount)
increase_balance(state, get_beacon_proposer_index(state), transfer.fee) increase_balance(state, get_proposer_index(state), transfer.fee)
# Verify balances are not dust # Verify balances are not dust
assert not (0 < state.balances[transfer.sender] < MIN_DEPOSIT_AMOUNT) assert not (0 < state.balances[transfer.sender] < MIN_DEPOSIT_AMOUNT)
assert not (0 < state.balances[transfer.recipient] < MIN_DEPOSIT_AMOUNT) assert not (0 < state.balances[transfer.recipient] < MIN_DEPOSIT_AMOUNT)

View File

@ -124,7 +124,7 @@ def get_latest_attesting_balance(store: Store, root: Hash) -> Gwei:
def get_head(store: Store) -> Hash: def get_head(store: Store) -> Hash:
# Execute the LMD-GHOST fork choice # Execute the LMD-GHOST fork choice
head = store.justified_checkpoint.root head = store.justified_checkpoint.root
justified_slot = epoch_start_slot(store.justified_checkpoint.epoch) justified_slot = epoch_first_slot(store.justified_checkpoint.epoch)
while True: while True:
children = [ children = [
root for root in store.blocks.keys() root for root in store.blocks.keys()
@ -162,7 +162,7 @@ def on_block(store: Store, block: BeaconBlock) -> None:
store.finalized_checkpoint.root store.finalized_checkpoint.root
) )
# Check that block is later than the finalized epoch slot # Check that block is later than the finalized epoch slot
assert block.slot > epoch_start_slot(store.finalized_checkpoint.epoch) assert block.slot > epoch_first_slot(store.finalized_checkpoint.epoch)
# Check the block is valid and compute the post-state # Check the block is valid and compute the post-state
state = state_transition(pre_state, block) state = state_transition(pre_state, block)
# Add new state for this block to the store # Add new state for this block to the store
@ -190,11 +190,11 @@ def on_attestation(store: Store, attestation: Attestation) -> None:
# Attestations cannot be from future epochs. If they are, delay consideration until the epoch arrivesr # Attestations cannot be from future epochs. If they are, delay consideration until the epoch arrivesr
base_state = store.block_states[target.root].copy() base_state = store.block_states[target.root].copy()
assert store.time >= base_state.genesis_time + epoch_start_slot(target.epoch) * SECONDS_PER_SLOT assert store.time >= base_state.genesis_time + epoch_first_slot(target.epoch) * SECONDS_PER_SLOT
# Store target checkpoint state if not yet seen # Store target checkpoint state if not yet seen
if target not in store.checkpoint_states: if target not in store.checkpoint_states:
process_slots(base_state, epoch_start_slot(target.epoch)) process_slots(base_state, epoch_first_slot(target.epoch))
store.checkpoint_states[target] = base_state store.checkpoint_states[target] = base_state
target_state = store.checkpoint_states[target] target_state = store.checkpoint_states[target]

View File

@ -382,7 +382,7 @@ def process_custody_key_reveal(state: BeaconState, reveal: CustodyKeyReveal) ->
revealer.next_custody_reveal_period += 1 revealer.next_custody_reveal_period += 1
# Reward Block Preposer # Reward Block Preposer
proposer_index = get_beacon_proposer_index(state) proposer_index = get_proposer_index(state)
increase_balance( increase_balance(
state, state,
proposer_index, proposer_index,
@ -452,7 +452,7 @@ def process_early_derived_secret_reveal(state: BeaconState, reveal: EarlyDerived
) )
# Apply penalty # Apply penalty
proposer_index = get_beacon_proposer_index(state) proposer_index = get_proposer_index(state)
whistleblower_index = reveal.masker_index whistleblower_index = reveal.masker_index
whistleblowing_reward = Gwei(penalty // WHISTLEBLOWER_REWARD_QUOTIENT) whistleblowing_reward = Gwei(penalty // WHISTLEBLOWER_REWARD_QUOTIENT)
proposer_reward = Gwei(whistleblowing_reward // PROPOSER_REWARD_QUOTIENT) proposer_reward = Gwei(whistleblowing_reward // PROPOSER_REWARD_QUOTIENT)
@ -493,7 +493,7 @@ def process_chunk_challenge(state: BeaconState, challenge: CustodyChunkChallenge
# Add new chunk challenge record # Add new chunk challenge record
new_record = CustodyChunkChallengeRecord( new_record = CustodyChunkChallengeRecord(
challenge_index=state.custody_challenge_index, challenge_index=state.custody_challenge_index,
challenger_index=get_beacon_proposer_index(state), challenger_index=get_proposer_index(state),
responder_index=challenge.responder_index, responder_index=challenge.responder_index,
inclusion_epoch=get_current_epoch(state), inclusion_epoch=get_current_epoch(state),
data_root=challenge.attestation.data.crosslink.data_root, data_root=challenge.attestation.data.crosslink.data_root,
@ -610,7 +610,7 @@ def process_chunk_challenge_response(state: BeaconState,
records = state.custody_chunk_challenge_records records = state.custody_chunk_challenge_records
records[records.index(challenge)] = CustodyChunkChallengeRecord() records[records.index(challenge)] = CustodyChunkChallengeRecord()
# Reward the proposer # Reward the proposer
proposer_index = get_beacon_proposer_index(state) proposer_index = get_proposer_index(state)
increase_balance(state, proposer_index, Gwei(get_base_reward(state, proposer_index) // MINOR_REWARD_QUOTIENT)) increase_balance(state, proposer_index, Gwei(get_base_reward(state, proposer_index) // MINOR_REWARD_QUOTIENT))
``` ```

View File

@ -146,7 +146,7 @@ def get_committee_assignment(
assert epoch <= next_epoch assert epoch <= next_epoch
committees_per_slot = get_committee_count(state, epoch) // SLOTS_PER_EPOCH committees_per_slot = get_committee_count(state, epoch) // SLOTS_PER_EPOCH
start_slot = epoch_start_slot(epoch) start_slot = epoch_first_slot(epoch)
for slot in range(start_slot, start_slot + SLOTS_PER_EPOCH): for slot in range(start_slot, start_slot + SLOTS_PER_EPOCH):
offset = committees_per_slot * (slot % SLOTS_PER_EPOCH) offset = committees_per_slot * (slot % SLOTS_PER_EPOCH)
slot_start_shard = (get_start_shard(state, epoch) + offset) % SHARD_COUNT slot_start_shard = (get_start_shard(state, epoch) + offset) % SHARD_COUNT
@ -162,7 +162,7 @@ A validator can use the following function to see if they are supposed to propos
```python ```python
def is_proposer(state: BeaconState, def is_proposer(state: BeaconState,
validator_index: ValidatorIndex) -> bool: validator_index: ValidatorIndex) -> bool:
return get_beacon_proposer_index(state) == validator_index return get_proposer_index(state) == validator_index
``` ```
*Note*: To see if a validator is assigned to propose during the slot, the beacon state must be in the epoch in question. At the epoch boundaries, the validator must run an epoch transition into the epoch to successfully check the proposal assignment of the first slot. *Note*: To see if a validator is assigned to propose during the slot, the beacon state must be in the epoch in question. At the epoch boundaries, the validator must run an epoch transition into the epoch to successfully check the proposal assignment of the first slot.
@ -307,7 +307,7 @@ Set `attestation_data.beacon_block_root = signing_root(head_block)`.
* Set `attestation_data.target_root = epoch_boundary_block_root` where `epoch_boundary_block_root` is the root of block at the most recent epoch boundary. * Set `attestation_data.target_root = epoch_boundary_block_root` where `epoch_boundary_block_root` is the root of block at the most recent epoch boundary.
*Note*: `epoch_boundary_block_root` can be looked up in the state using: *Note*: `epoch_boundary_block_root` can be looked up in the state using:
* Let `start_slot = epoch_start_slot(get_current_epoch(head_state))`. * Let `start_slot = epoch_first_slot(get_current_epoch(head_state))`.
* Let `epoch_boundary_block_root = signing_root(head_block) if start_slot == head_state.slot else get_block_root(state, start_slot)`. * Let `epoch_boundary_block_root = signing_root(head_block) if start_slot == head_state.slot else get_block_root(state, start_slot)`.
##### Crosslink vote ##### Crosslink vote

View File

@ -15,15 +15,15 @@ def build_attestation_data(spec, state, slot, shard):
else: else:
block_root = spec.get_block_root_at_slot(state, slot) block_root = spec.get_block_root_at_slot(state, slot)
current_epoch_start_slot = spec.epoch_start_slot(spec.get_current_epoch(state)) current_epoch_first_slot = spec.epoch_first_slot(spec.get_current_epoch(state))
if slot < current_epoch_start_slot: if slot < current_epoch_first_slot:
epoch_boundary_root = spec.get_block_root(state, spec.get_previous_epoch(state)) epoch_boundary_root = spec.get_block_root(state, spec.get_previous_epoch(state))
elif slot == current_epoch_start_slot: elif slot == current_epoch_first_slot:
epoch_boundary_root = block_root epoch_boundary_root = block_root
else: else:
epoch_boundary_root = spec.get_block_root(state, spec.get_current_epoch(state)) epoch_boundary_root = spec.get_block_root(state, spec.get_current_epoch(state))
if slot < current_epoch_start_slot: if slot < current_epoch_first_slot:
source_epoch = state.previous_justified_checkpoint.epoch source_epoch = state.previous_justified_checkpoint.epoch
source_root = state.previous_justified_checkpoint.root source_root = state.previous_justified_checkpoint.root
else: else:

View File

@ -12,7 +12,7 @@ def sign_block(spec, state, block, proposer_index=None):
if proposer_index is None: if proposer_index is None:
if block.slot == state.slot: if block.slot == state.slot:
proposer_index = spec.get_beacon_proposer_index(state) proposer_index = spec.get_proposer_index(state)
else: else:
if spec.slot_to_epoch(state.slot) + 1 > spec.slot_to_epoch(block.slot): if spec.slot_to_epoch(state.slot) + 1 > spec.slot_to_epoch(block.slot):
print("warning: block slot far away, and no proposer index manually given." print("warning: block slot far away, and no proposer index manually given."
@ -20,7 +20,7 @@ def sign_block(spec, state, block, proposer_index=None):
# use stub state to get proposer index of future slot # use stub state to get proposer index of future slot
stub_state = deepcopy(state) stub_state = deepcopy(state)
spec.process_slots(stub_state, block.slot) spec.process_slots(stub_state, block.slot)
proposer_index = spec.get_beacon_proposer_index(stub_state) proposer_index = spec.get_proposer_index(stub_state)
privkey = privkeys[proposer_index] privkey = privkeys[proposer_index]

View File

@ -30,7 +30,7 @@ def run_attester_slashing_processing(spec, state, attester_slashing, valid=True)
+ attester_slashing.attestation_1.custody_bit_1_indices + attester_slashing.attestation_1.custody_bit_1_indices
) )
proposer_index = spec.get_beacon_proposer_index(state) proposer_index = spec.get_proposer_index(state)
pre_proposer_balance = get_balance(state, proposer_index) pre_proposer_balance = get_balance(state, proposer_index)
pre_slashings = {slashed_index: get_balance(state, slashed_index) for slashed_index in slashed_indices} pre_slashings = {slashed_index: get_balance(state, slashed_index) for slashed_index in slashed_indices}
pre_withdrawalable_epochs = { pre_withdrawalable_epochs = {

View File

@ -75,7 +75,7 @@ def test_proposer_slashed(spec, state):
# use stub state to get proposer index of next slot # use stub state to get proposer index of next slot
stub_state = deepcopy(state) stub_state = deepcopy(state)
next_slot(spec, stub_state) next_slot(spec, stub_state)
proposer_index = spec.get_beacon_proposer_index(stub_state) proposer_index = spec.get_proposer_index(stub_state)
# set proposer to slashed # set proposer to slashed
state.validators[proposer_index].slashed = True state.validators[proposer_index].slashed = True

View File

@ -21,7 +21,7 @@ def run_transfer_processing(spec, state, transfer, valid=True):
yield 'post', None yield 'post', None
return return
proposer_index = spec.get_beacon_proposer_index(state) proposer_index = spec.get_proposer_index(state)
pre_transfer_sender_balance = state.balances[transfer.sender] pre_transfer_sender_balance = state.balances[transfer.sender]
pre_transfer_recipient_balance = state.balances[transfer.recipient] pre_transfer_recipient_balance = state.balances[transfer.recipient]
pre_transfer_proposer_balance = state.balances[proposer_index] pre_transfer_proposer_balance = state.balances[proposer_index]

View File

@ -33,7 +33,7 @@ def add_mock_attestations(spec, state, epoch, source, target, sufficient_support
total_balance = spec.get_total_active_balance(state) total_balance = spec.get_total_active_balance(state)
remaining_balance = total_balance * 2 // 3 remaining_balance = total_balance * 2 // 3
start_slot = spec.epoch_start_slot(epoch) start_slot = spec.epoch_first_slot(epoch)
for slot in range(start_slot, start_slot + spec.SLOTS_PER_EPOCH): for slot in range(start_slot, start_slot + spec.SLOTS_PER_EPOCH):
for shard in get_shards_for_slot(spec, state, slot): for shard in get_shards_for_slot(spec, state, slot):
# Check if we already have had sufficient balance. (and undone if we don't want it). # Check if we already have had sufficient balance. (and undone if we don't want it).
@ -80,7 +80,7 @@ def get_checkpoints(spec, epoch):
def put_checkpoints_in_block_roots(spec, state, checkpoints): def put_checkpoints_in_block_roots(spec, state, checkpoints):
for c in checkpoints: for c in checkpoints:
state.block_roots[spec.epoch_start_slot(c.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c.root state.block_roots[spec.epoch_first_slot(c.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c.root
def finalize_on_234(spec, state, epoch, sufficient_support): def finalize_on_234(spec, state, epoch, sufficient_support):

View File

@ -214,7 +214,7 @@ def test_attester_slashing(spec, state):
# lost whistleblower reward # lost whistleblower reward
assert get_balance(state, validator_index) < get_balance(pre_state, validator_index) assert get_balance(state, validator_index) < get_balance(pre_state, validator_index)
proposer_index = spec.get_beacon_proposer_index(state) proposer_index = spec.get_proposer_index(state)
# gained whistleblower reward # gained whistleblower reward
assert ( assert (
get_balance(state, proposer_index) > get_balance(state, proposer_index) >

View File

@ -43,7 +43,7 @@ def next_epoch_with_attestations(spec,
block = build_empty_block_for_next_slot(spec, post_state) block = build_empty_block_for_next_slot(spec, post_state)
if fill_cur_epoch and post_state.slot >= spec.MIN_ATTESTATION_INCLUSION_DELAY: if fill_cur_epoch and post_state.slot >= spec.MIN_ATTESTATION_INCLUSION_DELAY:
slot_to_attest = post_state.slot - spec.MIN_ATTESTATION_INCLUSION_DELAY + 1 slot_to_attest = post_state.slot - spec.MIN_ATTESTATION_INCLUSION_DELAY + 1
if slot_to_attest >= spec.epoch_start_slot(spec.get_current_epoch(post_state)): if slot_to_attest >= spec.epoch_first_slot(spec.get_current_epoch(post_state)):
cur_attestation = get_valid_attestation(spec, post_state, slot_to_attest) cur_attestation = get_valid_attestation(spec, post_state, slot_to_attest)
block.body.attestations.append(cur_attestation) block.body.attestations.append(cur_attestation)