From 5f8edd6b55d13a7eb91a8ea7950896bab7a165fa Mon Sep 17 00:00:00 2001 From: Carl Beekhuizen Date: Thu, 20 Jun 2019 20:50:17 +0200 Subject: [PATCH 1/7] Genesis block store uses genesis time Co-Authored-By: Hsiao-Wei Wang --- specs/core/0_fork-choice.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/specs/core/0_fork-choice.md b/specs/core/0_fork-choice.md index 6b7e4bdbe..5a4a52db4 100644 --- a/specs/core/0_fork-choice.md +++ b/specs/core/0_fork-choice.md @@ -82,7 +82,12 @@ class Store(object): def get_genesis_store(genesis_state: BeaconState) -> Store: genesis_block = BeaconBlock(state_root=hash_tree_root(genesis_state)) root = signing_root(genesis_block) - return Store(blocks={root: genesis_block}, states={root: genesis_state}, justified_root=root, finalized_root=root) + return Store( + blocks={root: genesis_block}, + states={root: genesis_state}, + time=genesis_state.genesis_time, + justified_root=root, finalized_root=root, + ) ``` #### `get_ancestor` From c26fffc15481ca282c75307a122f2012f4090cd1 Mon Sep 17 00:00:00 2001 From: Carl Beekhuizen Date: Thu, 20 Jun 2019 20:55:28 +0200 Subject: [PATCH 2/7] Moves copy into SSZ container --- scripts/build_spec.py | 4 ---- specs/core/0_fork-choice.md | 5 +++-- test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py | 4 ++++ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/scripts/build_spec.py b/scripts/build_spec.py index 9232cf00b..a16fa79ac 100644 --- a/scripts/build_spec.py +++ b/scripts/build_spec.py @@ -25,8 +25,6 @@ from dataclasses import ( field, ) -from copy import deepcopy - from eth2spec.utils.ssz.ssz_impl import ( hash_tree_root, signing_root, @@ -60,8 +58,6 @@ from dataclasses import ( field, ) -from copy import deepcopy - from eth2spec.utils.ssz.ssz_impl import ( hash_tree_root, signing_root, diff --git a/specs/core/0_fork-choice.md b/specs/core/0_fork-choice.md index 5a4a52db4..ba97398a2 100644 --- a/specs/core/0_fork-choice.md +++ b/specs/core/0_fork-choice.md @@ -86,7 +86,8 @@ def get_genesis_store(genesis_state: BeaconState) -> Store: blocks={root: genesis_block}, states={root: genesis_state}, time=genesis_state.genesis_time, - justified_root=root, finalized_root=root, + justified_root=root, + finalized_root=root, ) ``` @@ -143,7 +144,7 @@ def on_block(store: Store, block: BeaconBlock) -> None: # Check block is a descendant of the finalized block assert get_ancestor(store, signing_root(block), store.blocks[store.finalized_root].slot) == store.finalized_root # Check block slot against Unix time - pre_state = deepcopy(store.states[block.parent_root]) + pre_state = store.states[block.parent_root].copy() assert store.time >= pre_state.genesis_time + block.slot * SECONDS_PER_SLOT # Check the block is valid and compute the post-state state = state_transition(pre_state, block) diff --git a/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py b/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py index 64d50b579..077f94b49 100644 --- a/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py +++ b/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py @@ -1,3 +1,4 @@ +import copy from types import GeneratorType from typing import ( List, @@ -151,6 +152,9 @@ class Container(object): def __hash__(self): return hash(self.hash_tree_root()) + def copy(self): + return copy.deepcopy(self) + @classmethod def get_fields_dict(cls): return dict(cls.__annotations__) From e88a96c45e2cd724f7a3949e78d4c4c5707d1d40 Mon Sep 17 00:00:00 2001 From: Carl Beekhuizen Date: Fri, 21 Jun 2019 12:13:22 +0200 Subject: [PATCH 3/7] Apply suggestions from @drjtwo's code review Co-Authored-By: Danny Ryan --- specs/core/0_fork-choice.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specs/core/0_fork-choice.md b/specs/core/0_fork-choice.md index ba97398a2..6ae7e11fa 100644 --- a/specs/core/0_fork-choice.md +++ b/specs/core/0_fork-choice.md @@ -47,10 +47,11 @@ The head block root associated with a `store` is defined as `get_head(store)`. A *Notes*: -1) **Leap seconds**: Slots will last `SECONDS_PER_SLOT + 1` or `SECONDS_PER_SLOT - 1` seconds around leap seconds. +1) **Leap seconds**: Slots will last `SECONDS_PER_SLOT + 1` or `SECONDS_PER_SLOT - 1` seconds around leap seconds. This is automatically handled by [UNIX time](https://en.wikipedia.org/wiki/Unix_time). 2) **Honest clocks**: Honest nodes are assumed to have clocks synchronized within `SECONDS_PER_SLOT` seconds of each other. 3) **Eth1 data**: The large `ETH1_FOLLOW_DISTANCE` specified in the [honest validator document](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/validator/0_beacon-chain-validator.md) should ensure that `state.latest_eth1_data` of the canonical Ethereum 2.0 chain remains consistent with the canonical Ethereum 1.0 chain. If not, emergency manual intervention will be required. 4) **Manual forks**: Manual forks may arbitrarily change the fork choice rule but are expected to be enacted at epoch transitions, with the fork details reflected in `state.fork`. +5) **Implementation**: The implementation found in this specification is constructed for ease of understanding rather than for optimization in computation, space, or any other resource. A number optimized alternatives can be found [here](https://github.com/protolambda/lmd-ghost). ### Helpers From f90469ea25c3b28ec20881d1a4e17033a70a129a Mon Sep 17 00:00:00 2001 From: Carl Beekhuizen Date: Fri, 21 Jun 2019 12:19:08 +0200 Subject: [PATCH 4/7] Move block timing assertion 1st --- specs/core/0_fork-choice.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specs/core/0_fork-choice.md b/specs/core/0_fork-choice.md index 6ae7e11fa..eb7c1a579 100644 --- a/specs/core/0_fork-choice.md +++ b/specs/core/0_fork-choice.md @@ -140,13 +140,14 @@ def on_tick(store: Store, time: int) -> None: ```python def on_block(store: Store, block: BeaconBlock) -> None: + # Blocks cannot be in the future. If they are, their consideration must be delayed until the are in the past. + assert store.time >= pre_state.genesis_time + block.slot * SECONDS_PER_SLOT # Add new block to the store store.blocks[signing_root(block)] = block # Check block is a descendant of the finalized block assert get_ancestor(store, signing_root(block), store.blocks[store.finalized_root].slot) == store.finalized_root # Check block slot against Unix time pre_state = store.states[block.parent_root].copy() - assert store.time >= pre_state.genesis_time + block.slot * SECONDS_PER_SLOT # Check the block is valid and compute the post-state state = state_transition(pre_state, block) # Add new state to the store From 0e59c6676a66bf1f0b40bf0eb76c3040da01656f Mon Sep 17 00:00:00 2001 From: Carl Beekhuizen Date: Fri, 21 Jun 2019 13:00:42 +0200 Subject: [PATCH 5/7] Stop yielding from fork-choie tests --- specs/core/0_fork-choice.md | 4 ++-- .../pyspec/eth2spec/test/test_fork_choice.py | 16 +++++----------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/specs/core/0_fork-choice.md b/specs/core/0_fork-choice.md index eb7c1a579..3d85cf571 100644 --- a/specs/core/0_fork-choice.md +++ b/specs/core/0_fork-choice.md @@ -140,14 +140,14 @@ def on_tick(store: Store, time: int) -> None: ```python def on_block(store: Store, block: BeaconBlock) -> None: + # Make a copy of the state to avoid mutability issues + pre_state = store.states[block.parent_root].copy() # Blocks cannot be in the future. If they are, their consideration must be delayed until the are in the past. assert store.time >= pre_state.genesis_time + block.slot * SECONDS_PER_SLOT # Add new block to the store store.blocks[signing_root(block)] = block # Check block is a descendant of the finalized block assert get_ancestor(store, signing_root(block), store.blocks[store.finalized_root].slot) == store.finalized_root - # Check block slot against Unix time - pre_state = store.states[block.parent_root].copy() # Check the block is valid and compute the post-state state = state_transition(pre_state, block) # Add new state to the store diff --git a/test_libs/pyspec/eth2spec/test/test_fork_choice.py b/test_libs/pyspec/eth2spec/test/test_fork_choice.py index ab1728251..4bc7e8b0a 100644 --- a/test_libs/pyspec/eth2spec/test/test_fork_choice.py +++ b/test_libs/pyspec/eth2spec/test/test_fork_choice.py @@ -2,7 +2,7 @@ from typing import List from eth2spec.utils.ssz.ssz_impl import signing_root, hash_tree_root -from eth2spec.test.context import with_all_phases, spec_state_test +from eth2spec.test.context import with_all_phases, with_state, bls_switch from eth2spec.test.helpers.block import build_empty_block_for_next_slot from eth2spec.test.helpers.attestations import get_valid_attestation @@ -10,10 +10,10 @@ from eth2spec.test.helpers.state import next_slot @with_all_phases -@spec_state_test +@with_state +@bls_switch def test_basic(spec, state): state.latest_block_header = spec.BeaconBlockHeader(body_root=hash_tree_root(spec.BeaconBlockBody())) - yield 'pre', state # Initialization store = spec.get_genesis_store(state) @@ -36,17 +36,14 @@ def test_basic(spec, state): spec.on_block(store, block) assert store.blocks[signing_root(block)] == block - yield 'blocks', blocks, List[spec.BeaconBlock] # TODO: add tests for justified_root and finalized_root - yield 'post', state @with_all_phases -@spec_state_test +@with_state +@bls_switch def test_on_attestation(spec, state): - yield 'pre', state - store = spec.get_genesis_store(state) time = 100 spec.on_tick(store, time) @@ -54,7 +51,6 @@ def test_on_attestation(spec, state): next_slot(spec, state) attestation = get_valid_attestation(spec, state, slot=1) - yield 'attestation', attestation indexed_attestation = spec.convert_to_indexed(state, attestation) spec.on_attestation(store, attestation) assert ( @@ -64,5 +60,3 @@ def test_on_attestation(spec, state): root=attestation.data.target_root, ) ) - - yield 'post', state From b46e047baa4fc06a0980904c25ef2137f51a06a2 Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Fri, 21 Jun 2019 12:57:30 +0100 Subject: [PATCH 6/7] Minor simplification from #1198 --- specs/core/0_fork-choice.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_fork-choice.md b/specs/core/0_fork-choice.md index 3d85cf571..892c592e9 100644 --- a/specs/core/0_fork-choice.md +++ b/specs/core/0_fork-choice.md @@ -106,7 +106,7 @@ def get_ancestor(store: Store, root: Hash, slot: Slot) -> Hash: ```python def get_latest_attesting_balance(store: Store, root: Hash) -> Gwei: state = store.states[store.justified_root] - active_indices = get_active_validator_indices(state.validator_registry, slot_to_epoch(state.slot)) + active_indices = get_active_validator_indices(state.validator_registry, 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 From acbccbc2a8eeed367167e22732c91987cdd06637 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 21 Jun 2019 11:18:24 -0600 Subject: [PATCH 7/7] minor typo --- specs/core/0_fork-choice.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_fork-choice.md b/specs/core/0_fork-choice.md index 892c592e9..25b33ab30 100644 --- a/specs/core/0_fork-choice.md +++ b/specs/core/0_fork-choice.md @@ -51,7 +51,7 @@ The head block root associated with a `store` is defined as `get_head(store)`. A 2) **Honest clocks**: Honest nodes are assumed to have clocks synchronized within `SECONDS_PER_SLOT` seconds of each other. 3) **Eth1 data**: The large `ETH1_FOLLOW_DISTANCE` specified in the [honest validator document](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/validator/0_beacon-chain-validator.md) should ensure that `state.latest_eth1_data` of the canonical Ethereum 2.0 chain remains consistent with the canonical Ethereum 1.0 chain. If not, emergency manual intervention will be required. 4) **Manual forks**: Manual forks may arbitrarily change the fork choice rule but are expected to be enacted at epoch transitions, with the fork details reflected in `state.fork`. -5) **Implementation**: The implementation found in this specification is constructed for ease of understanding rather than for optimization in computation, space, or any other resource. A number optimized alternatives can be found [here](https://github.com/protolambda/lmd-ghost). +5) **Implementation**: The implementation found in this specification is constructed for ease of understanding rather than for optimization in computation, space, or any other resource. A number of optimized alternatives can be found [here](https://github.com/protolambda/lmd-ghost). ### Helpers