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 |
| - | - |
| `GENESIS_FORK_VERSION` | `int_to_bytes4(0)` |
| `GENESIS_SLOT` | `2**32` |
| `GENESIS_EPOCH` | `slot_to_epoch(GENESIS_SLOT)` |
| `GENESIS_SLOT` | `0` |
| `GENESIS_EPOCH` | `0` |
| `GENESIS_START_SHARD` | `0` |
| `FAR_FUTURE_EPOCH` | `2**64 - 1` |
| `ZERO_HASH` | `int_to_bytes32(0)` |
| `EMPTY_SIGNATURE` | `int_to_bytes96(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
| Name | Value | Unit | Duration |
@ -678,8 +676,10 @@ def slot_to_epoch(slot: Slot) -> Epoch:
def get_previous_epoch(state: BeaconState) -> Epoch:
"""`
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`
@ -974,6 +974,7 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex:
Return the beacon proposer index at ``state.slot``.
"""
current_epoch = get_current_epoch(state)
first_committee, _ = get_crosslink_committees_at_slot(state, state.slot)[0]
i = 0
while True:
@ -1260,14 +1261,13 @@ Note: All functions in this section mutate `state`.
#### `activate_validator`
```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``.
Note that this function mutates ``state``.
"""
validator = state.validator_registry[index]
if is_genesis:
if state.slot == GENESIS_SLOT:
validator.activation_eligibility_epoch = GENESIS_EPOCH
validator.activation_epoch = GENESIS_EPOCH
else:
@ -1435,7 +1435,7 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit],
# Finality
previous_epoch_attestations=[],
current_epoch_attestations=[],
previous_justified_epoch=GENESIS_EPOCH - 1,
previous_justified_epoch=GENESIS_EPOCH,
current_justified_epoch=GENESIS_EPOCH,
previous_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
for index in range(len(state.validator_registry)):
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))
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.
* 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.
@ -1575,17 +1575,17 @@ At every `slot > GENESIS_SLOT` run the following function:
```python
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
state.latest_state_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_slot_state_root
# cache state root in stored latest_block_header if empty
# Store latest known state root (for previous slot) in latest_block_header if it is empty
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
state.latest_block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = signing_root(state.latest_block_header)
# Cache latest known block root (for previous slot)
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
@ -1680,6 +1680,9 @@ Run the following function:
```python
def update_justification_and_finalization(state: BeaconState) -> None:
if get_current_epoch(state) <= GENESIS_EPOCH + 1:
return
antepenultimate_justified_epoch = state.previous_justified_epoch
# Process justifications
@ -1724,9 +1727,8 @@ Run the following function:
```python
def process_crosslinks(state: BeaconState) -> None:
current_epoch = get_current_epoch(state)
previous_epoch = max(current_epoch - 1, GENESIS_EPOCH)
next_epoch = current_epoch + 1
previous_epoch = get_previous_epoch(state)
next_epoch = get_current_epoch(state) + 1
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):
winning_root, participants = get_winning_root_and_participants(state, shard)
@ -1845,6 +1847,9 @@ Run the following:
```python
def apply_rewards(state: BeaconState) -> None:
if get_current_epoch(state) == GENESIS_EPOCH:
return
rewards1, penalties1 = get_justification_and_finalization_deltas(state)
rewards2, penalties2 = get_crosslink_deltas(state)
for i in range(len(state.validator_registry)):
@ -2100,8 +2105,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
Process ``Attestation`` operation.
Note that this function mutates ``state``.
"""
assert max(GENESIS_SLOT, state.slot - SLOTS_PER_EPOCH) <= attestation.data.slot
assert attestation.data.slot <= state.slot - MIN_ATTESTATION_INCLUSION_DELAY
assert attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= attestation.data.slot + SLOTS_PER_EPOCH
# Check target epoch, source epoch, and source root
target_epoch = slot_to_epoch(attestation.data.slot)

View File

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

View File

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

View File

@ -33,6 +33,8 @@ from eth2spec.phase0.spec import (
get_empty_block,
get_epoch_start_slot,
get_genesis_beacon_state,
get_previous_epoch,
get_shard_delta,
slot_to_epoch,
verify_merkle_branch,
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)}
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):
if not 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):
assert state.slot >= slot
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))
if epoch_start_slot == slot:
current_epoch_start_slot = get_epoch_start_slot(get_current_epoch(state))
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
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
else:
justified_epoch = state.current_justified_epoch
justified_block_root = state.current_justified_root
return AttestationData(
slot=slot,
shard=shard,
beacon_block_root=block_root,
source_epoch=state.current_justified_epoch,
source_epoch=justified_epoch,
source_root=justified_block_root,
target_root=epoch_boundary_root,
crosslink_data_root=spec.ZERO_HASH,
@ -254,7 +276,13 @@ def get_valid_attester_slashing(state):
def get_valid_attestation(state, slot=None):
if slot is None:
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)
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):
block = build_empty_block_for_next_slot(state)
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 (
build_deposit_data,
build_empty_block_for_next_slot,
fill_aggregate_attestation,
get_valid_attestation,
get_valid_attester_slashing,
get_valid_proposer_slashing,
next_slot,
privkeys,
pubkeys,
)
@ -51,6 +53,33 @@ from .helpers import (
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):
test_state = deepcopy(state)
cache_state(test_state)
@ -115,6 +144,33 @@ def test_empty_epoch_transition_not_finalizing(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):
test_state = deepcopy(state)
proposer_slashing = get_valid_proposer_slashing(state)