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

add previous and current crosslinks #874

Merged
merged 33 commits into from
Apr 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
529cf42
add previous and current crosslinks
djrtwo Apr 2, 2019
d8df789
simplify get_winning_root logic
djrtwo Apr 2, 2019
39b4ef3
Merge branch 'dev' into prev-cur-crosslinks
djrtwo Apr 3, 2019
1fa88fb
remove previous crosslink check from process_crosslinks
djrtwo Apr 3, 2019
d1af914
Update 0_beacon-chain.md
JustinDrake Apr 3, 2019
a790afa
Update 0_beacon-chain.md
JustinDrake Apr 4, 2019
3e6dc59
Update helpers.py
JustinDrake Apr 4, 2019
dc325f7
clean up a few things from PR
djrtwo Apr 5, 2019
f677af2
Merge branch 'dev' into prev-cur-crosslinks
djrtwo Apr 5, 2019
26df4f4
Merge branch 'dev' into prev-cur-crosslinks
djrtwo Apr 7, 2019
42dc003
add previous_crosslink_root and enforce crosslinks form a chain
djrtwo Apr 7, 2019
e246c3f
source_crosslink_root to previous_crosslink_root
djrtwo Apr 8, 2019
71a28aa
fix tests
djrtwo Apr 8, 2019
0a5a5b7
Merge branch 'dev' into prev-cur-crosslinks
djrtwo Apr 13, 2019
a6b3b11
ensure no reward for crosslinks taht can't form a chain
djrtwo Apr 13, 2019
9489ae5
upate validator guide to new crosslink format
djrtwo Apr 13, 2019
eafcab7
check crosslinks validity root against previous
djrtwo Apr 13, 2019
3555ab8
Merge branch 'dev' into prev-cur-crosslinks
djrtwo Apr 14, 2019
cc68df8
Merge branch 'dev' into prev-cur-crosslinks
hwwhww Apr 17, 2019
ef14396
Merge branch 'dev' into prev-cur-crosslinks
djrtwo Apr 17, 2019
8c5f7a5
Merge branch 'dev' into prev-cur-crosslinks
djrtwo Apr 18, 2019
fbaf771
Update 0_beacon-chain.md
JustinDrake Apr 18, 2019
9ecafb2
Update 0_beacon-chain.md
JustinDrake Apr 18, 2019
40b55cf
More fixes
JustinDrake Apr 18, 2019
cae5c22
Simplify get_crosslink_committee_for_attestation and move to test hel…
JustinDrake Apr 18, 2019
964b4d3
Fix `pyspec/tests/helpers.py`
hwwhww Apr 18, 2019
743193a
nitpicks
hwwhww Apr 18, 2019
4244db9
More cleanups
JustinDrake Apr 18, 2019
2e09f1a
Merge
JustinDrake Apr 18, 2019
172e106
merge
JustinDrake Apr 18, 2019
857d9b2
Merge branch 'dev' into prev-cur-crosslinks
JustinDrake Apr 18, 2019
7a01648
Moar
JustinDrake Apr 18, 2019
741a74a
re-add crosslink tests and ensure pass
djrtwo Apr 18, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 66 additions & 83 deletions specs/core/0_beacon-chain.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions specs/core/1_custody-game.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ def process_chunk_challenge(state: BeaconState,
responder = state.validator_registry[challenge.responder_index]
assert responder.exit_epoch >= get_current_epoch(state) - MAX_CHUNK_CHALLENGE_DELAY
# Verify the responder participated in the attestation
attesters = get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield)
attesters = get_attesting_indices(state, attestation.data, attestation.aggregation_bitfield)
assert challenge.responder_index in attesters
# Verify the challenge is not a duplicate
for record in state.custody_chunk_challenge_records:
Expand Down Expand Up @@ -359,9 +359,9 @@ def process_bit_challenge(state: BeaconState,
# Verify the attestation is eligible for challenging
responder = state.validator_registry[challenge.responder_index]
min_challengeable_epoch = responder.exit_epoch - EPOCHS_PER_CUSTODY_PERIOD * (1 + responder.max_reveal_lateness)
assert min_challengeable_epoch <= slot_to_epoch(challenge.attestation.data.slot)
assert min_challengeable_epoch <= slot_to_epoch(challenge.attestation.data.slot)
# Verify the responder participated in the attestation
attesters = get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield)
attesters = get_attesting_indices(state, attestation.data, attestation.aggregation_bitfield)
assert challenge.responder_index in attesters
# A validator can be the challenger or responder for at most one challenge at a time
for record in state.custody_bit_challenge_records:
Expand Down
40 changes: 20 additions & 20 deletions specs/validator/0_beacon-chain-validator.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ __NOTICE__: This document is a work-in-progress for researchers and implementers
- [Attestations](#attestations-1)
- [Attestation data](#attestation-data)
- [Slot](#slot-1)
- [Shard](#shard)
- [Beacon block root](#beacon-block-root)
- [Target root](#target-root)
- [Crosslink data root](#crosslink-data-root)
- [Latest crosslink](#latest-crosslink)
- [Source epoch](#source-epoch)
- [Source root](#source-root)
- [Target root](#target-root)
- [Shard](#shard)
- [Previous crosslink root](#previous-crosslink-root)
- [Crosslink data root](#crosslink-data-root)
- [Construct attestation](#construct-attestation)
- [Data](#data)
- [Aggregation bitfield](#aggregation-bitfield)
Expand Down Expand Up @@ -250,14 +250,18 @@ First the validator should construct `attestation_data`, an [`AttestationData`](

Set `attestation_data.slot = head_state.slot`.

##### Shard

Set `attestation_data.shard = shard` where `shard` is the shard associated with the validator's committee defined by `get_crosslink_committees_at_slot`.

##### Beacon block root

Set `attestation_data.beacon_block_root = signing_root(head_block)`.

##### Source epoch

Set `attestation_data.source_epoch = head_state.justified_epoch`.

##### Source root

Set `attestation_data.source_root = head_state.current_justified_root`.

##### Target root

Set `attestation_data.target_root = signing_root(epoch_boundary)` where `epoch_boundary` is the block at the most recent epoch boundary.
Expand All @@ -266,23 +270,19 @@ _Note:_ This can be looked up in the state using:
* Let `epoch_start_slot = get_epoch_start_slot(get_current_epoch(head_state))`.
* Set `epoch_boundary = head if epoch_start_slot == head_state.slot else get_block_root(state, epoch_start_slot)`.

##### Crosslink data root

Set `attestation_data.crosslink_data_root = ZERO_HASH`.

_Note:_ This is a stub for phase 0.
##### Shard

##### Latest crosslink
Set `attestation_data.shard = shard` where `shard` is the shard associated with the validator's committee defined by `get_crosslink_committees_at_slot`.

Set `attestation_data.previous_crosslink = head_state.latest_crosslinks[shard]`.
##### Previous crosslink root

##### Source epoch
Set `attestation_data.previous_crosslink_root = hash_tree_root(head_state.current_crosslinks[shard])`.

Set `attestation_data.source_epoch = head_state.justified_epoch`.
##### Crosslink data root

##### Source root
Set `attestation_data.crosslink_data_root = ZERO_HASH`.

Set `attestation_data.source_root = head_state.current_justified_root`.
_Note:_ This is a stub for phase 0.

#### Construct attestation

Expand All @@ -299,7 +299,7 @@ Set `attestation.data = attestation_data` where `attestation_data` is the `Attes
* Set `aggregation_bitfield[index_into_committee // 8] |= 2 ** (index_into_committee % 8)`.
* Set `attestation.aggregation_bitfield = aggregation_bitfield`.

_Note_: Calling `get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield)` should return a list of length equal to 1, containing `validator_index`.
_Note_: Calling `get_attesting_indices(state, attestation.data, attestation.aggregation_bitfield)` should return a list of length equal to 1, containing `validator_index`.

##### Custody bitfield

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
from tests.helpers import (
build_empty_block_for_next_slot,
get_valid_attestation,
next_epoch,
next_slot,
)


Expand Down Expand Up @@ -120,10 +122,12 @@ def test_non_zero_crosslink_data_root(state):


def test_bad_previous_crosslink(state):
next_epoch(state)
attestation = get_valid_attestation(state)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
next_slot(state)

state.latest_crosslinks[attestation.data.shard].epoch += 10
state.current_crosslinks[attestation.data.shard].epoch += 10

pre_state, post_state = run_attestation_processing(state, attestation, False)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
get_valid_proposer_slashing,
)

# mark entire file as 'header'
# mark entire file as 'proposer_slashings'
pytestmark = pytest.mark.proposer_slashings


Expand Down
3 changes: 2 additions & 1 deletion test_libs/pyspec/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ def overwrite_spec_config(config):
if field == "LATEST_RANDAO_MIXES_LENGTH":
spec.BeaconState.fields['latest_randao_mixes'][1] = config[field]
elif field == "SHARD_COUNT":
spec.BeaconState.fields['latest_crosslinks'][1] = config[field]
spec.BeaconState.fields['current_crosslinks'][1] = config[field]
spec.BeaconState.fields['previous_crosslinks'][1] = config[field]
elif field == "SLOTS_PER_HISTORICAL_ROOT":
spec.BeaconState.fields['latest_block_roots'][1] = config[field]
spec.BeaconState.fields['latest_state_roots'][1] = config[field]
Expand Down
136 changes: 136 additions & 0 deletions test_libs/pyspec/tests/epoch_processing/test_process_crosslinks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
from copy import deepcopy
import pytest

import eth2spec.phase0.spec as spec

from eth2spec.phase0.state_transition import (
state_transition,
)
from eth2spec.phase0.spec import (
cache_state,
get_crosslink_deltas,
process_crosslinks,
)
from tests.helpers import (
add_attestation_to_state,
build_empty_block_for_next_slot,
fill_aggregate_attestation,
get_crosslink_committee_for_attestation,
get_valid_attestation,
next_epoch,
next_slot,
set_bitfield_bit,
)


# mark entire file as 'crosslinks'
pytestmark = pytest.mark.crosslinks


def run_process_crosslinks(state, valid=True):
# transition state to slot before state transition
slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH) - 1
block = build_empty_block_for_next_slot(state)
block.slot = slot
state_transition(state, block)

# cache state before epoch transition
cache_state(state)

post_state = deepcopy(state)
process_crosslinks(post_state)

return state, post_state


def test_no_attestations(state):
pre_state, post_state = run_process_crosslinks(state)

for shard in range(spec.SHARD_COUNT):
assert post_state.previous_crosslinks[shard] == post_state.current_crosslinks[shard]

return pre_state, post_state


def test_single_crosslink_update_from_current_epoch(state):
next_epoch(state)

attestation = get_valid_attestation(state)

fill_aggregate_attestation(state, attestation)
add_attestation_to_state(state, attestation, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY)

assert len(state.current_epoch_attestations) == 1

pre_state, post_state = run_process_crosslinks(state)

shard = attestation.data.shard
assert post_state.previous_crosslinks[shard] != post_state.current_crosslinks[shard]
assert pre_state.current_crosslinks[shard] != post_state.current_crosslinks[shard]

return pre_state, post_state


def test_single_crosslink_update_from_previous_epoch(state):
next_epoch(state)

attestation = get_valid_attestation(state)

fill_aggregate_attestation(state, attestation)
add_attestation_to_state(state, attestation, state.slot + spec.SLOTS_PER_EPOCH)

assert len(state.previous_epoch_attestations) == 1

pre_state, post_state = run_process_crosslinks(state)
crosslink_deltas = get_crosslink_deltas(state)

shard = attestation.data.shard
assert post_state.previous_crosslinks[shard] != post_state.current_crosslinks[shard]
assert pre_state.current_crosslinks[shard] != post_state.current_crosslinks[shard]
# ensure rewarded
for index in get_crosslink_committee_for_attestation(state, attestation.data):
assert crosslink_deltas[0][index] > 0
assert crosslink_deltas[1][index] == 0

return pre_state, post_state


def test_double_late_crosslink(state):
next_epoch(state)
state.slot += 4

attestation_1 = get_valid_attestation(state)
fill_aggregate_attestation(state, attestation_1)

# add attestation_1 in the next epoch
next_epoch(state)
add_attestation_to_state(state, attestation_1, state.slot + 1)

for slot in range(spec.SLOTS_PER_EPOCH):
attestation_2 = get_valid_attestation(state)
if attestation_2.data.shard == attestation_1.data.shard:
break
next_slot(state)
fill_aggregate_attestation(state, attestation_2)

# add attestation_2 in the next epoch after attestation_1 has
# already updated the relevant crosslink
next_epoch(state)
add_attestation_to_state(state, attestation_2, state.slot + 1)

assert len(state.previous_epoch_attestations) == 1
assert len(state.current_epoch_attestations) == 0

pre_state, post_state = run_process_crosslinks(state)
crosslink_deltas = get_crosslink_deltas(state)

shard = attestation_2.data.shard

# ensure that the current crosslinks were not updated by the second attestation
assert post_state.previous_crosslinks[shard] == post_state.current_crosslinks[shard]
# ensure no reward, only penalties for the failed crosslink
for index in get_crosslink_committee_for_attestation(state, attestation_2.data):
assert crosslink_deltas[0][index] == 0
assert crosslink_deltas[1][index] > 0

return pre_state, post_state
26 changes: 22 additions & 4 deletions test_libs/pyspec/tests/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,17 @@
# functions
convert_to_indexed,
get_active_validator_indices,
get_attestation_participants,
get_attesting_indices,
get_block_root,
get_crosslink_committee_for_attestation,
get_crosslink_committees_at_slot,
get_current_epoch,
get_domain,
get_empty_block,
get_epoch_start_slot,
get_genesis_beacon_state,
get_previous_epoch,
get_shard_delta,
hash_tree_root,
slot_to_epoch,
verify_merkle_branch,
hash,
Expand Down Expand Up @@ -154,6 +155,7 @@ def build_attestation_data(state, slot, shard):

current_epoch_start_slot = get_epoch_start_slot(get_current_epoch(state))
if slot < current_epoch_start_slot:
print(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
Expand All @@ -167,6 +169,7 @@ def build_attestation_data(state, slot, shard):
justified_epoch = state.current_justified_epoch
justified_block_root = state.current_justified_root

crosslinks = state.current_crosslinks if slot_to_epoch(slot) == get_current_epoch(state) else state.previous_crosslinks
return AttestationData(
slot=slot,
shard=shard,
Expand All @@ -175,7 +178,7 @@ def build_attestation_data(state, slot, shard):
source_root=justified_block_root,
target_root=epoch_boundary_root,
crosslink_data_root=spec.ZERO_HASH,
previous_crosslink=deepcopy(state.latest_crosslinks[shard]),
previous_crosslink_root=hash_tree_root(crosslinks[shard]),
)


Expand Down Expand Up @@ -273,6 +276,14 @@ def get_valid_attester_slashing(state):
)


def get_crosslink_committee_for_attestation(state, attestation_data):
"""
Return the crosslink committee corresponding to ``attestation_data``.
"""
crosslink_committees = get_crosslink_committees_at_slot(state, attestation_data.slot)
return [committee for committee, shard in crosslink_committees if shard == attestation_data.shard][0]


def get_valid_attestation(state, slot=None):
if slot is None:
slot = state.slot
Expand All @@ -297,7 +308,7 @@ def get_valid_attestation(state, slot=None):
custody_bitfield=custody_bitfield,
aggregate_signature=EMPTY_SIGNATURE,
)
participants = get_attestation_participants(
participants = get_attesting_indices(
state,
attestation.data,
attestation.aggregation_bitfield,
Expand Down Expand Up @@ -342,6 +353,13 @@ def fill_aggregate_attestation(state, attestation):
attestation.aggregation_bitfield = set_bitfield_bit(attestation.aggregation_bitfield, i)


def add_attestation_to_state(state, attestation, slot):
block = build_empty_block_for_next_slot(state)
block.slot = slot
block.body.attestations.append(attestation)
state_transition(state, block)


def next_slot(state):
block = build_empty_block_for_next_slot(state)
state_transition(state, block)
Expand Down
Loading