Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GENESIS_SLOT == 0 #896

Merged
merged 15 commits into from
Apr 18, 2019
39 changes: 22 additions & 17 deletions specs/core/0_beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down Expand Up @@ -680,8 +678,10 @@ def slot_to_epoch(slot: Slot) -> Epoch:
def get_previous_epoch(state: BeaconState) -> Epoch:
"""`
Return the previous epoch of the given ``state``.
djrtwo marked this conversation as resolved.
Show resolved Hide resolved
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`
Expand Down Expand Up @@ -976,6 +976,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:
Expand Down Expand Up @@ -1262,14 +1263,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:
Expand Down Expand Up @@ -1437,7 +1437,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,
Expand Down Expand Up @@ -1467,7 +1467,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):
Expand All @@ -1490,7 +1490,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.

Expand Down Expand Up @@ -1587,7 +1587,8 @@ def cache_state(state: BeaconState) -> None:
state.latest_block_header.state_root = previous_slot_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)
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
Expand Down Expand Up @@ -1682,6 +1683,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
Expand Down Expand Up @@ -1726,9 +1730,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)
Expand Down Expand Up @@ -1847,6 +1850,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)):
Expand Down Expand Up @@ -2102,8 +2108,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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
)
from tests.helpers import (
get_valid_attester_slashing,
next_epoch,
)

# mark entire file as 'attester_slashing'
Expand Down Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion test_libs/pyspec/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"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,
Expand Down
54 changes: 47 additions & 7 deletions test_libs/pyspec/tests/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
get_empty_block,
get_epoch_start_slot,
get_genesis_beacon_state,
get_previous_epoch,
get_shard_delta,
slot_to_epoch,
verify_merkle_branch,
hash,
Expand All @@ -49,6 +51,19 @@
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 = []
Expand Down Expand Up @@ -132,24 +147,31 @@ def build_deposit_data(state, pubkey, privkey, amount):
def build_attestation_data(state, slot, shard):
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))
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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
56 changes: 56 additions & 0 deletions test_libs/pyspec/tests/test_sanity.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@
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,
)
Expand All @@ -51,6 +53,33 @@
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)
Expand Down Expand Up @@ -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)
Expand Down