From 228195d89d464e5c4af7c4223808ffd33b96efda Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 25 Jun 2019 11:48:55 -0600 Subject: [PATCH 1/2] get head tests --- specs/core/0_fork-choice.md | 9 +- .../test/fork_choice/test_get_head.py | 118 ++++++++++++++++++ 2 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 test_libs/pyspec/eth2spec/test/fork_choice/test_get_head.py diff --git a/specs/core/0_fork-choice.md b/specs/core/0_fork-choice.md index 71920d2bc..f74f487a1 100644 --- a/specs/core/0_fork-choice.md +++ b/specs/core/0_fork-choice.md @@ -91,7 +91,7 @@ def get_genesis_store(genesis_state: BeaconState) -> Store: justified_checkpoint=justified_checkpoint, finalized_checkpoint=finalized_checkpoint, blocks={root: genesis_block}, - block_states={root: genesis_state}, + block_states={root: genesis_state.copy()}, checkpoint_states={justified_checkpoint: genesis_state.copy()}, ) ``` @@ -110,10 +110,11 @@ def get_ancestor(store: Store, root: Hash, slot: Slot) -> Hash: ```python def get_latest_attesting_balance(store: Store, root: Hash) -> Gwei: state = store.checkpoint_states[store.justified_checkpoint] - active_indices = get_active_validator_indices(state.validator_registry, get_current_epoch(state)) + active_indices = get_active_validator_indices(state, get_current_epoch(state)) return Gwei(sum( - state.validator_registry[i].effective_balance for i in active_indices - if get_ancestor(store, store.latest_targets[i].root, store.blocks[root].slot) == root + state.validators[i].effective_balance for i in active_indices + if (i in store.latest_targets and + get_ancestor(store, store.latest_targets[i].root, store.blocks[root].slot) == root) )) ``` diff --git a/test_libs/pyspec/eth2spec/test/fork_choice/test_get_head.py b/test_libs/pyspec/eth2spec/test/fork_choice/test_get_head.py new file mode 100644 index 000000000..6ac46ba6c --- /dev/null +++ b/test_libs/pyspec/eth2spec/test/fork_choice/test_get_head.py @@ -0,0 +1,118 @@ +from eth2spec.test.context import with_all_phases, with_state, bls_switch +from eth2spec.test.helpers.attestations import get_valid_attestation +from eth2spec.test.helpers.block import build_empty_block_for_next_slot +from eth2spec.test.helpers.state import state_transition_and_sign_block + + +def add_block_to_store(spec, store, block): + pre_state = store.block_states[block.parent_root] + block_time = pre_state.genesis_time + block.slot * spec.SECONDS_PER_SLOT + + if store.time < block_time: + spec.on_tick(store, block_time) + + spec.on_block(store, block) + + +def add_attestation_to_store(spec, store, attestation): + parent_block = store.blocks[attestation.data.beacon_block_root] + pre_state = store.block_states[spec.signing_root(parent_block)] + block_time = pre_state.genesis_time + parent_block.slot * spec.SECONDS_PER_SLOT + next_epoch_time = block_time + spec.SLOTS_PER_EPOCH * spec.SECONDS_PER_SLOT + + if store.time < next_epoch_time: + spec.on_tick(store, next_epoch_time) + + spec.on_attestation(store, attestation) + + +@with_all_phases +@with_state +@bls_switch +def test_genesis(spec, state): + # Initialization + store = spec.get_genesis_store(state) + genesis_block = spec.BeaconBlock(state_root=state.hash_tree_root()) + assert spec.get_head(store) == spec.signing_root(genesis_block) + + +@with_all_phases +@with_state +@bls_switch +def test_chain_no_attestations(spec, state): + # Initialization + store = spec.get_genesis_store(state) + genesis_block = spec.BeaconBlock(state_root=state.hash_tree_root()) + assert spec.get_head(store) == spec.signing_root(genesis_block) + + # On receiving a block of `GENESIS_SLOT + 1` slot + block_1 = build_empty_block_for_next_slot(spec, state) + state_transition_and_sign_block(spec, state, block_1) + add_block_to_store(spec, store, block_1) + + # On receiving a block of next epoch + block_2 = build_empty_block_for_next_slot(spec, state) + state_transition_and_sign_block(spec, state, block_2) + add_block_to_store(spec, store, block_2) + + assert spec.get_head(store) == spec.signing_root(block_2) + + +@with_all_phases +@with_state +@bls_switch +def test_split_tie_breaker_no_attestations(spec, state): + genesis_state = state.copy() + + # Initialization + store = spec.get_genesis_store(state) + genesis_block = spec.BeaconBlock(state_root=state.hash_tree_root()) + assert spec.get_head(store) == spec.signing_root(genesis_block) + + # block at slot 1 + block_1_state = genesis_state.copy() + block_1 = build_empty_block_for_next_slot(spec, block_1_state) + state_transition_and_sign_block(spec, block_1_state, block_1) + add_block_to_store(spec, store, block_1) + + # additional block at slot 1 + block_2_state = genesis_state.copy() + block_2 = build_empty_block_for_next_slot(spec, block_2_state) + block_2.body.graffiti = b'\x42' * 32 + state_transition_and_sign_block(spec, block_2_state, block_2) + add_block_to_store(spec, store, block_2) + + highest_root = max(spec.signing_root(block_1), spec.signing_root(block_2)) + + assert spec.get_head(store) == highest_root + + +@with_all_phases +@with_state +@bls_switch +def test_shorter_chain_but_heavier_weight(spec, state): + genesis_state = state.copy() + + # Initialization + store = spec.get_genesis_store(state) + genesis_block = spec.BeaconBlock(state_root=state.hash_tree_root()) + assert spec.get_head(store) == spec.signing_root(genesis_block) + + # build longer tree + long_state = genesis_state.copy() + for i in range(3): + long_block = build_empty_block_for_next_slot(spec, long_state) + state_transition_and_sign_block(spec, long_state, long_block) + add_block_to_store(spec, store, long_block) + + # build short tree + short_state = genesis_state.copy() + short_block = build_empty_block_for_next_slot(spec, short_state) + short_block.body.graffiti = b'\x42' * 32 + state_transition_and_sign_block(spec, short_state, short_block) + add_block_to_store(spec, store, short_block) + + short_attestation = get_valid_attestation(spec, short_state, short_block.slot, signed=True) + add_attestation_to_store(spec, store, short_attestation) + + assert spec.get_head(store) == spec.signing_root(short_block) From c64289677f0a5372dd1d8b6990ccc6510e0a14bc Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 25 Jun 2019 14:42:37 -0600 Subject: [PATCH 2/2] fix gethead tests --- specs/core/0_fork-choice.md | 22 ++++++++++++++----- .../test/fork_choice/test_on_attestation.py | 6 ++--- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/specs/core/0_fork-choice.md b/specs/core/0_fork-choice.md index f74f487a1..55ff91a81 100644 --- a/specs/core/0_fork-choice.md +++ b/specs/core/0_fork-choice.md @@ -64,6 +64,16 @@ class Checkpoint(object): root: Hash ``` +#### `LatestMessage` + +```python +@dataclass(eq=True, frozen=True) +class LatestMessage(object): + epoch: Epoch + root: Hash +``` + + #### `Store` ```python @@ -75,7 +85,7 @@ class Store(object): blocks: Dict[Hash, BeaconBlock] = field(default_factory=dict) block_states: Dict[Hash, BeaconState] = field(default_factory=dict) checkpoint_states: Dict[Checkpoint, BeaconState] = field(default_factory=dict) - latest_targets: Dict[ValidatorIndex, Checkpoint] = field(default_factory=dict) + latest_messages: Dict[ValidatorIndex, LatestMessage] = field(default_factory=dict) ``` #### `get_genesis_store` @@ -113,8 +123,8 @@ def get_latest_attesting_balance(store: Store, root: Hash) -> Gwei: active_indices = get_active_validator_indices(state, get_current_epoch(state)) return Gwei(sum( state.validators[i].effective_balance for i in active_indices - if (i in store.latest_targets and - get_ancestor(store, store.latest_targets[i].root, store.blocks[root].slot) == root) + if (i in store.latest_messages and + get_ancestor(store, store.latest_messages[i].root, store.blocks[root].slot) == root) )) ``` @@ -207,8 +217,8 @@ def on_attestation(store: Store, attestation: Attestation) -> None: indexed_attestation = convert_to_indexed(target_state, attestation) validate_indexed_attestation(target_state, indexed_attestation) - # Update latest targets + # Update latest messages for i in indexed_attestation.custody_bit_0_indices + indexed_attestation.custody_bit_1_indices: - if i not in store.latest_targets or target.epoch > store.latest_targets[i].epoch: - store.latest_targets[i] = target + if i not in store.latest_messages or target.epoch > store.latest_messages[i].epoch: + store.latest_messages[i] = LatestMessage(epoch=target.epoch, root=attestation.data.beacon_block_root) ``` diff --git a/test_libs/pyspec/eth2spec/test/fork_choice/test_on_attestation.py b/test_libs/pyspec/eth2spec/test/fork_choice/test_on_attestation.py index 48f4bf46f..0e52e7fd0 100644 --- a/test_libs/pyspec/eth2spec/test/fork_choice/test_on_attestation.py +++ b/test_libs/pyspec/eth2spec/test/fork_choice/test_on_attestation.py @@ -17,10 +17,10 @@ def run_on_attestation(spec, state, store, attestation, valid=True): indexed_attestation = spec.convert_to_indexed(state, attestation) spec.on_attestation(store, attestation) assert ( - store.latest_targets[indexed_attestation.custody_bit_0_indices[0]] == - spec.Checkpoint( + store.latest_messages[indexed_attestation.custody_bit_0_indices[0]] == + spec.LatestMessage( epoch=attestation.data.target_epoch, - root=attestation.data.target_root, + root=attestation.data.beacon_block_root, ) )