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
Merged
47 changes: 26 additions & 21 deletions specs/core/0_beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,16 +216,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 @@ -702,7 +700,8 @@ 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 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 @@ -1008,7 +1007,7 @@ def get_beacon_proposer_index(state: BeaconState,
the epoch in question, this can only be run for the current epoch.
"""
current_epoch = get_current_epoch(state)
assert slot_to_epoch(slot) == current_epoch
# assert slot_to_epoch(slot) == current_epoch
djrtwo marked this conversation as resolved.
Show resolved Hide resolved

first_committee, _ = get_crosslink_committees_at_slot(state, slot)[0]
i = 0
Expand Down Expand Up @@ -1044,12 +1043,12 @@ def verify_merkle_branch(leaf: Bytes32, proof: List[Bytes32], depth: int, index:

```python
def get_crosslink_committee_for_attestation(state: BeaconState,
attestation_data: AttestationData) -> List[ValidatorIndex]:
attestation_data: AttestationData) -> List[ValidatorIndex]:
"""
Return the crosslink committee corresponding to ``attestation_data``.
"""
"""
crosslink_committees = get_crosslink_committees_at_slot(state, attestation_data.slot)

# Find the committee in the list with the desired shard
assert attestation_data.shard in [shard for _, shard in crosslink_committees]
crosslink_committee = [committee for committee, shard in crosslink_committees if shard == attestation_data.shard][0]
Expand Down Expand Up @@ -1367,14 +1366,16 @@ 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]

validator.activation_epoch = GENESIS_EPOCH if is_genesis else get_delayed_activation_exit_epoch(get_current_epoch(state))
if state.slot == GENESIS_SLOT:
validator.activation_epoch = GENESIS_EPOCH
else:
validator.activation_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember that @vbuterin chose to use the flag as a cleaner solution (#374 (comment)). :p I'm fine either way!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine either way. But if we are going to have the Genesis slot be 0 why not just remove the constant?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which, GENESIS_EPOCH?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for removing the constants gensis-slot and genesis-epoch. 0 is perfectly clear.

```

#### `initiate_validator_exit`
Expand Down Expand Up @@ -1554,7 +1555,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 @@ -1584,7 +1585,7 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit],
# Process genesis activations
for validator_index, _ in enumerate(state.validator_registry):
if get_effective_balance(state, validator_index) >= MAX_DEPOSIT_AMOUNT:
activate_validator(state, validator_index, is_genesis=True)
activate_validator(state, validator_index)

genesis_active_index_root = hash_tree_root(get_active_validator_indices(state.validator_registry, GENESIS_EPOCH))
for index in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH):
Expand All @@ -1607,7 +1608,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 @@ -1814,6 +1815,9 @@ Run the following function:

```python
def update_justification_and_finalization(state: BeaconState) -> None:
if get_current_epoch(state) == GENESIS_EPOCH:
return

new_justified_epoch = state.current_justified_epoch
new_finalized_epoch = state.finalized_epoch

Expand Down Expand Up @@ -1863,9 +1867,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 @@ -1999,6 +2002,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 @@ -2057,7 +2063,7 @@ def update_validator_registry(state: BeaconState) -> None:
break

# Activate validator
activate_validator(state, index, is_genesis=False)
activate_validator(state, index)

# Exit validators within the allowable balance churn
if current_epoch < state.validator_registry_update_epoch + LATEST_SLASHED_EXIT_LENGTH:
Expand Down Expand Up @@ -2327,8 +2333,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.phase0.helpers import (
get_valid_attester_slashing,
next_epoch,
)

# mark entire file as 'attester_slashing'
Expand Down Expand Up @@ -59,6 +60,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 tests/phase0/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
55 changes: 48 additions & 7 deletions tests/phase0/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

import build.phase0.spec as spec
from build.phase0.utils.minimal_ssz import signing_root
from build.phase0.state_transition import (
state_transition,
)
from build.phase0.spec import (
# constants
EMPTY_SIGNATURE,
Expand Down Expand Up @@ -31,6 +34,7 @@
get_empty_block,
get_epoch_start_slot,
get_genesis_beacon_state,
get_previous_epoch,
slot_to_epoch,
verify_merkle_branch,
hash,
Expand All @@ -47,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 @@ -138,24 +155,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 @@ -260,7 +284,7 @@ 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
shard = state.latest_start_shard + slot % spec.SLOTS_PER_EPOCH
attestation_data = build_attestation_data(state, slot, shard)

crosslink_committee = get_crosslink_committee_for_attestation(state, attestation_data)
Expand Down Expand Up @@ -312,3 +336,20 @@ def get_attestation_signature(state, attestation_data, privkey, custody_bit=0b0)
domain_type=spec.DOMAIN_ATTESTATION,
)
)


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)
55 changes: 55 additions & 0 deletions tests/phase0/test_sanity.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@
from tests.phase0.helpers import (
build_deposit_data,
build_empty_block_for_next_slot,
fill_aggregate_attestation,
force_registry_change_at_next_epoch,
get_valid_attestation,
get_valid_attester_slashing,
get_valid_proposer_slashing,
next_slot,
privkeys,
pubkeys,
)
Expand All @@ -52,6 +54,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 @@ -116,6 +145,32 @@ 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):
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