Merge pull request #896 from ethereum/start-at-zero

GENESIS_SLOT == 0
This commit is contained in:
Danny Ryan 2019-04-17 19:31:51 -06:00 committed by GitHub
commit cca545c30f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 134 additions and 32 deletions

View File

@ -208,16 +208,14 @@ These configurations are updated for releases, but may be out of sync during `de
| Name | Value | | Name | Value |
| - | - | | - | - |
| `GENESIS_FORK_VERSION` | `int_to_bytes4(0)` | | `GENESIS_FORK_VERSION` | `int_to_bytes4(0)` |
| `GENESIS_SLOT` | `2**32` | | `GENESIS_SLOT` | `0` |
| `GENESIS_EPOCH` | `slot_to_epoch(GENESIS_SLOT)` | | `GENESIS_EPOCH` | `0` |
| `GENESIS_START_SHARD` | `0` | | `GENESIS_START_SHARD` | `0` |
| `FAR_FUTURE_EPOCH` | `2**64 - 1` | | `FAR_FUTURE_EPOCH` | `2**64 - 1` |
| `ZERO_HASH` | `int_to_bytes32(0)` | | `ZERO_HASH` | `int_to_bytes32(0)` |
| `EMPTY_SIGNATURE` | `int_to_bytes96(0)` | | `EMPTY_SIGNATURE` | `int_to_bytes96(0)` |
| `BLS_WITHDRAWAL_PREFIX_BYTE` | `int_to_bytes1(0)` | | `BLS_WITHDRAWAL_PREFIX_BYTE` | `int_to_bytes1(0)` |
* `GENESIS_SLOT` should be at least as large in terms of time as the largest of the time parameters or state list lengths below (ie. it should be at least as large as any value measured in slots, and at least `SLOTS_PER_EPOCH` times as large as any value measured in epochs).
### Time parameters ### Time parameters
| Name | Value | Unit | Duration | | Name | Value | Unit | Duration |
@ -678,8 +676,10 @@ def slot_to_epoch(slot: Slot) -> Epoch:
def get_previous_epoch(state: BeaconState) -> Epoch: def get_previous_epoch(state: BeaconState) -> Epoch:
"""` """`
Return the previous epoch of the given ``state``. Return the previous epoch of the given ``state``.
Return the current epoch if it's genesis epoch.
""" """
return get_current_epoch(state) - 1 current_epoch = get_current_epoch(state)
return (current_epoch - 1) if current_epoch > GENESIS_EPOCH else current_epoch
``` ```
### `get_current_epoch` ### `get_current_epoch`
@ -974,6 +974,7 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex:
Return the beacon proposer index at ``state.slot``. Return the beacon proposer index at ``state.slot``.
""" """
current_epoch = get_current_epoch(state) current_epoch = get_current_epoch(state)
first_committee, _ = get_crosslink_committees_at_slot(state, state.slot)[0] first_committee, _ = get_crosslink_committees_at_slot(state, state.slot)[0]
i = 0 i = 0
while True: while True:
@ -1260,14 +1261,13 @@ Note: All functions in this section mutate `state`.
#### `activate_validator` #### `activate_validator`
```python ```python
def activate_validator(state: BeaconState, index: ValidatorIndex, is_genesis: bool) -> None: def activate_validator(state: BeaconState, index: ValidatorIndex) -> None:
""" """
Activate the validator of the given ``index``. Activate the validator of the given ``index``.
Note that this function mutates ``state``. Note that this function mutates ``state``.
""" """
validator = state.validator_registry[index] validator = state.validator_registry[index]
if state.slot == GENESIS_SLOT:
if is_genesis:
validator.activation_eligibility_epoch = GENESIS_EPOCH validator.activation_eligibility_epoch = GENESIS_EPOCH
validator.activation_epoch = GENESIS_EPOCH validator.activation_epoch = GENESIS_EPOCH
else: else:
@ -1435,7 +1435,7 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit],
# Finality # Finality
previous_epoch_attestations=[], previous_epoch_attestations=[],
current_epoch_attestations=[], current_epoch_attestations=[],
previous_justified_epoch=GENESIS_EPOCH - 1, previous_justified_epoch=GENESIS_EPOCH,
current_justified_epoch=GENESIS_EPOCH, current_justified_epoch=GENESIS_EPOCH,
previous_justified_root=ZERO_HASH, previous_justified_root=ZERO_HASH,
current_justified_root=ZERO_HASH, current_justified_root=ZERO_HASH,
@ -1465,7 +1465,7 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit],
# Process genesis activations # Process genesis activations
for index in range(len(state.validator_registry)): for index in range(len(state.validator_registry)):
if get_effective_balance(state, index) >= MAX_DEPOSIT_AMOUNT: if get_effective_balance(state, index) >= MAX_DEPOSIT_AMOUNT:
activate_validator(state, index, is_genesis=True) activate_validator(state, index)
genesis_active_index_root = hash_tree_root(get_active_validator_indices(state, 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(LATEST_ACTIVE_INDEX_ROOTS_LENGTH):
@ -1488,7 +1488,7 @@ For a beacon chain block, `block`, to be processed by a node, the following cond
* The parent block with root `block.previous_block_root` has been processed and accepted. * The parent block with root `block.previous_block_root` has been processed and accepted.
* An Ethereum 1.0 block pointed to by the `state.latest_eth1_data.block_hash` has been processed and accepted. * An Ethereum 1.0 block pointed to by the `state.latest_eth1_data.block_hash` has been processed and accepted.
* The node's Unix time is greater than or equal to `state.genesis_time + (block.slot - GENESIS_SLOT) * SECONDS_PER_SLOT`. (Note that leap seconds mean that slots will occasionally last `SECONDS_PER_SLOT + 1` or `SECONDS_PER_SLOT - 1` seconds, possibly several times a year.) * The node's Unix time is greater than or equal to `state.genesis_time + block.slot * SECONDS_PER_SLOT`. (Note that leap seconds mean that slots will occasionally last `SECONDS_PER_SLOT + 1` or `SECONDS_PER_SLOT - 1` seconds, possibly several times a year.)
If these conditions are not met, the client should delay processing the beacon block until the conditions are all satisfied. If these conditions are not met, the client should delay processing the beacon block until the conditions are all satisfied.
@ -1575,17 +1575,17 @@ At every `slot > GENESIS_SLOT` run the following function:
```python ```python
def cache_state(state: BeaconState) -> None: def cache_state(state: BeaconState) -> None:
previous_slot_state_root = hash_tree_root(state) # Cache latest known state root (for previous slot)
latest_state_root = hash_tree_root(state)
state.latest_state_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = latest_state_root
# store the previous slot's post state transition root # Store latest known state root (for previous slot) in latest_block_header if it is empty
state.latest_state_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_slot_state_root
# cache state root in stored latest_block_header if empty
if state.latest_block_header.state_root == ZERO_HASH: if state.latest_block_header.state_root == ZERO_HASH:
state.latest_block_header.state_root = previous_slot_state_root state.latest_block_header.state_root = latest_state_root
# store latest known block for previous slot # Cache latest known block root (for previous slot)
state.latest_block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = signing_root(state.latest_block_header) latest_block_root = signing_root(state.latest_block_header)
state.latest_block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = latest_block_root
``` ```
### Per-epoch processing ### Per-epoch processing
@ -1680,6 +1680,9 @@ Run the following function:
```python ```python
def update_justification_and_finalization(state: BeaconState) -> None: def update_justification_and_finalization(state: BeaconState) -> None:
if get_current_epoch(state) <= GENESIS_EPOCH + 1:
return
antepenultimate_justified_epoch = state.previous_justified_epoch antepenultimate_justified_epoch = state.previous_justified_epoch
# Process justifications # Process justifications
@ -1724,9 +1727,8 @@ Run the following function:
```python ```python
def process_crosslinks(state: BeaconState) -> None: def process_crosslinks(state: BeaconState) -> None:
current_epoch = get_current_epoch(state) previous_epoch = get_previous_epoch(state)
previous_epoch = max(current_epoch - 1, GENESIS_EPOCH) next_epoch = get_current_epoch(state) + 1
next_epoch = current_epoch + 1
for slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch)): for slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch)):
for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot): for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot):
winning_root, participants = get_winning_root_and_participants(state, shard) winning_root, participants = get_winning_root_and_participants(state, shard)
@ -1845,6 +1847,9 @@ Run the following:
```python ```python
def apply_rewards(state: BeaconState) -> None: def apply_rewards(state: BeaconState) -> None:
if get_current_epoch(state) == GENESIS_EPOCH:
return
rewards1, penalties1 = get_justification_and_finalization_deltas(state) rewards1, penalties1 = get_justification_and_finalization_deltas(state)
rewards2, penalties2 = get_crosslink_deltas(state) rewards2, penalties2 = get_crosslink_deltas(state)
for i in range(len(state.validator_registry)): for i in range(len(state.validator_registry)):
@ -2100,8 +2105,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
Process ``Attestation`` operation. Process ``Attestation`` operation.
Note that this function mutates ``state``. Note that this function mutates ``state``.
""" """
assert max(GENESIS_SLOT, state.slot - SLOTS_PER_EPOCH) <= attestation.data.slot assert attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= attestation.data.slot + SLOTS_PER_EPOCH
assert attestation.data.slot <= state.slot - MIN_ATTESTATION_INCLUSION_DELAY
# Check target epoch, source epoch, and source root # Check target epoch, source epoch, and source root
target_epoch = slot_to_epoch(attestation.data.slot) target_epoch = slot_to_epoch(attestation.data.slot)

View File

@ -9,6 +9,7 @@ from eth2spec.phase0.spec import (
) )
from tests.helpers import ( from tests.helpers import (
get_valid_attester_slashing, get_valid_attester_slashing,
next_epoch,
) )
# mark entire file as 'attester_slashing' # mark entire file as 'attester_slashing'
@ -58,6 +59,8 @@ def test_success_double(state):
def test_success_surround(state): def test_success_surround(state):
next_epoch(state)
state.current_justified_epoch += 1
attester_slashing = get_valid_attester_slashing(state) attester_slashing = get_valid_attester_slashing(state)
# set attestion1 to surround attestation 2 # set attestion1 to surround attestation 2

View File

@ -14,7 +14,6 @@ MINIMAL_CONFIG = {
"MIN_ATTESTATION_INCLUSION_DELAY": 2, "MIN_ATTESTATION_INCLUSION_DELAY": 2,
"TARGET_COMMITTEE_SIZE": 4, "TARGET_COMMITTEE_SIZE": 4,
"SLOTS_PER_EPOCH": 8, "SLOTS_PER_EPOCH": 8,
"GENESIS_EPOCH": spec.GENESIS_SLOT // 8,
"SLOTS_PER_HISTORICAL_ROOT": 64, "SLOTS_PER_HISTORICAL_ROOT": 64,
"LATEST_RANDAO_MIXES_LENGTH": 64, "LATEST_RANDAO_MIXES_LENGTH": 64,
"LATEST_ACTIVE_INDEX_ROOTS_LENGTH": 64, "LATEST_ACTIVE_INDEX_ROOTS_LENGTH": 64,

View File

@ -33,6 +33,8 @@ from eth2spec.phase0.spec import (
get_empty_block, get_empty_block,
get_epoch_start_slot, get_epoch_start_slot,
get_genesis_beacon_state, get_genesis_beacon_state,
get_previous_epoch,
get_shard_delta,
slot_to_epoch, slot_to_epoch,
verify_merkle_branch, verify_merkle_branch,
hash, hash,
@ -49,6 +51,19 @@ pubkeys = [bls.privtopub(privkey) for privkey in privkeys]
pubkey_to_privkey = {pubkey: privkey for privkey, pubkey in zip(privkeys, pubkeys)} pubkey_to_privkey = {pubkey: privkey for privkey, pubkey in zip(privkeys, pubkeys)}
def set_bitfield_bit(bitfield, i):
"""
Set the bit in ``bitfield`` at position ``i`` to ``1``.
"""
byte_index = i // 8
bit_index = i % 8
return (
bitfield[:byte_index] +
bytes([bitfield[byte_index] | (1 << bit_index)]) +
bitfield[byte_index+1:]
)
def create_mock_genesis_validator_deposits(num_validators, deposit_data_leaves=None): def create_mock_genesis_validator_deposits(num_validators, deposit_data_leaves=None):
if not deposit_data_leaves: if not deposit_data_leaves:
deposit_data_leaves = [] deposit_data_leaves = []
@ -132,24 +147,31 @@ def build_deposit_data(state, pubkey, privkey, amount):
def build_attestation_data(state, slot, shard): def build_attestation_data(state, slot, shard):
assert state.slot >= slot assert state.slot >= slot
block_root = build_empty_block_for_next_slot(state).previous_block_root if slot == state.slot:
block_root = build_empty_block_for_next_slot(state).previous_block_root
else:
block_root = get_block_root(state, slot)
epoch_start_slot = get_epoch_start_slot(get_current_epoch(state)) current_epoch_start_slot = get_epoch_start_slot(get_current_epoch(state))
if epoch_start_slot == slot: if slot < current_epoch_start_slot:
epoch_boundary_root = get_block_root(state, get_epoch_start_slot(get_previous_epoch(state)))
elif slot == current_epoch_start_slot:
epoch_boundary_root = block_root epoch_boundary_root = block_root
else: else:
get_block_root(state, epoch_start_slot) epoch_boundary_root = get_block_root(state, current_epoch_start_slot)
if slot < epoch_start_slot: if slot < current_epoch_start_slot:
justified_epoch = state.previous_justified_epoch
justified_block_root = state.previous_justified_root justified_block_root = state.previous_justified_root
else: else:
justified_epoch = state.current_justified_epoch
justified_block_root = state.current_justified_root justified_block_root = state.current_justified_root
return AttestationData( return AttestationData(
slot=slot, slot=slot,
shard=shard, shard=shard,
beacon_block_root=block_root, beacon_block_root=block_root,
source_epoch=state.current_justified_epoch, source_epoch=justified_epoch,
source_root=justified_block_root, source_root=justified_block_root,
target_root=epoch_boundary_root, target_root=epoch_boundary_root,
crosslink_data_root=spec.ZERO_HASH, crosslink_data_root=spec.ZERO_HASH,
@ -254,7 +276,13 @@ def get_valid_attester_slashing(state):
def get_valid_attestation(state, slot=None): def get_valid_attestation(state, slot=None):
if slot is None: if slot is None:
slot = state.slot slot = state.slot
shard = state.latest_start_shard
if slot_to_epoch(slot) == get_current_epoch(state):
shard = (state.latest_start_shard + slot) % spec.SLOTS_PER_EPOCH
else:
previous_shard_delta = get_shard_delta(state, get_previous_epoch(state))
shard = (state.latest_start_shard - previous_shard_delta + slot) % spec.SHARD_COUNT
attestation_data = build_attestation_data(state, slot, shard) attestation_data = build_attestation_data(state, slot, shard)
crosslink_committee = get_crosslink_committee_for_attestation(state, attestation_data) crosslink_committee = get_crosslink_committee_for_attestation(state, attestation_data)
@ -308,6 +336,18 @@ def get_attestation_signature(state, attestation_data, privkey, custody_bit=0b0)
) )
def fill_aggregate_attestation(state, attestation):
crosslink_committee = get_crosslink_committee_for_attestation(state, attestation.data)
for i in range(len(crosslink_committee)):
attestation.aggregation_bitfield = set_bitfield_bit(attestation.aggregation_bitfield, i)
def next_slot(state): def next_slot(state):
block = build_empty_block_for_next_slot(state) block = build_empty_block_for_next_slot(state)
state_transition(state, block) state_transition(state, block)
def next_epoch(state):
block = build_empty_block_for_next_slot(state)
block.slot += spec.SLOTS_PER_EPOCH - (state.slot % spec.SLOTS_PER_EPOCH)
state_transition(state, block)

View File

@ -39,9 +39,11 @@ from eth2spec.utils.merkle_minimal import (
from .helpers import ( from .helpers import (
build_deposit_data, build_deposit_data,
build_empty_block_for_next_slot, build_empty_block_for_next_slot,
fill_aggregate_attestation,
get_valid_attestation, get_valid_attestation,
get_valid_attester_slashing, get_valid_attester_slashing,
get_valid_proposer_slashing, get_valid_proposer_slashing,
next_slot,
privkeys, privkeys,
pubkeys, pubkeys,
) )
@ -51,6 +53,33 @@ from .helpers import (
pytestmark = pytest.mark.sanity pytestmark = pytest.mark.sanity
def check_finality(state,
prev_state,
current_justified_changed,
previous_justified_changed,
finalized_changed):
if current_justified_changed:
assert state.current_justified_epoch > prev_state.current_justified_epoch
assert state.current_justified_root != prev_state.current_justified_root
else:
assert state.current_justified_epoch == prev_state.current_justified_epoch
assert state.current_justified_root == prev_state.current_justified_root
if previous_justified_changed:
assert state.previous_justified_epoch > prev_state.previous_justified_epoch
assert state.previous_justified_root != prev_state.previous_justified_root
else:
assert state.previous_justified_epoch == prev_state.previous_justified_epoch
assert state.previous_justified_root == prev_state.previous_justified_root
if finalized_changed:
assert state.finalized_epoch > prev_state.finalized_epoch
assert state.finalized_root != prev_state.finalized_root
else:
assert state.finalized_epoch == prev_state.finalized_epoch
assert state.finalized_root == prev_state.finalized_root
def test_slot_transition(state): def test_slot_transition(state):
test_state = deepcopy(state) test_state = deepcopy(state)
cache_state(test_state) cache_state(test_state)
@ -115,6 +144,33 @@ def test_empty_epoch_transition_not_finalizing(state):
return state, [block], test_state return state, [block], test_state
def test_full_attestations_finalizing(state):
test_state = deepcopy(state)
for slot in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
next_slot(test_state)
for epoch in range(5):
for slot in range(spec.SLOTS_PER_EPOCH):
print(test_state.slot)
attestation = get_valid_attestation(test_state, test_state.slot - spec.MIN_ATTESTATION_INCLUSION_DELAY)
fill_aggregate_attestation(test_state, attestation)
block = build_empty_block_for_next_slot(test_state)
block.body.attestations.append(attestation)
state_transition(test_state, block)
if epoch == 0:
check_finality(test_state, state, False, False, False)
elif epoch == 1:
check_finality(test_state, state, False, False, False)
elif epoch == 2:
check_finality(test_state, state, True, False, False)
elif epoch == 3:
check_finality(test_state, state, True, True, False)
elif epoch == 4:
check_finality(test_state, state, True, True, True)
def test_proposer_slashing(state): def test_proposer_slashing(state):
test_state = deepcopy(state) test_state = deepcopy(state)
proposer_slashing = get_valid_proposer_slashing(state) proposer_slashing = get_valid_proposer_slashing(state)