From 8b316c6db4bcbd96416a1a12e76f57a626f03df6 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 1 May 2019 12:04:27 +0100 Subject: [PATCH 01/17] Start moving state_transition.py to state transitition spec The state transition spec should be reasonably self-contained, limiting the amount of "magic" outside of it. This PR is a first step in this direction, specifically for operation processing. --- specs/core/0_beacon-chain.md | 44 ++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 55791e25f..bfb51e99a 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1691,12 +1691,28 @@ def process_eth1_data(state: BeaconState, block: BeaconBlock) -> None: #### Operations +The sub-sections below define helper functions, one per operation type. The full processing of operations is done by running `process_operations(state, block.body)`. + +```python +def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: + assert len(body.deposits) == min(MAX_DEPOSITS, state.latest_eth1_data.deposit_count - state.deposit_index) + assert len(body.transfers) == len(set(body.transfers)) + + for operations, max_operations, function in { + (body.proposer_slashings, MAX_PROPOSER_SLASHINGS, process_proposer_slashing), + (body.attester_slashings, MAX_ATTESTER_SLASHINGS, attester_slashings), + (body.attestations, MAX_ATTESTATIONS, process_attestation), + (body.deposits, MAX_DEPOSITS, process_deposit), + (body.voluntary_exits, MAX_VOLUNTARY_EXITS, process_voluntary_exit), + (body.transfers, MAX_TRANSFERS, process_transfer), + }: + assert len(operations) <= max_operations + for operation in operations: + function(state, operation) +``` + ##### Proposer slashings -Verify that `len(block.body.proposer_slashings) <= MAX_PROPOSER_SLASHINGS`. - -For each `proposer_slashing` in `block.body.proposer_slashings`, run the following function: - ```python def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSlashing) -> None: @@ -1721,10 +1737,6 @@ def process_proposer_slashing(state: BeaconState, ##### Attester slashings -Verify that `len(block.body.attester_slashings) <= MAX_ATTESTER_SLASHINGS`. - -For each `attester_slashing` in `block.body.attester_slashings`, run the following function: - ```python def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSlashing) -> None: @@ -1759,10 +1771,6 @@ def process_attester_slashing(state: BeaconState, ##### Attestations -Verify that `len(block.body.attestations) <= MAX_ATTESTATIONS`. - -For each `attestation` in `block.body.attestations`, run the following function: - ```python def process_attestation(state: BeaconState, attestation: Attestation) -> None: """ @@ -1801,10 +1809,6 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: ##### Deposits -Verify that `len(block.body.deposits) == min(MAX_DEPOSITS, state.latest_eth1_data.deposit_count - state.deposit_index)`. - -For each `deposit` in `block.body.deposits`, run the following function: - ```python def process_deposit(state: BeaconState, deposit: Deposit) -> None: """ @@ -1851,10 +1855,6 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: ##### Voluntary exits -Verify that `len(block.body.voluntary_exits) <= MAX_VOLUNTARY_EXITS`. - -For each `exit` in `block.body.voluntary_exits`, run the following function: - ```python def process_voluntary_exit(state: BeaconState, exit: VoluntaryExit) -> None: """ @@ -1879,10 +1879,6 @@ def process_voluntary_exit(state: BeaconState, exit: VoluntaryExit) -> None: ##### Transfers -Verify that `len(block.body.transfers) <= MAX_TRANSFERS` and that all transfers are distinct. - -For each `transfer` in `block.body.transfers`, run the following function: - ```python def process_transfer(state: BeaconState, transfer: Transfer) -> None: """ From 591a2b47c8af28a43f5349435749d7f60f9894d4 Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Wed, 1 May 2019 12:08:15 +0100 Subject: [PATCH 02/17] Simplify state_transition.py --- specs/core/0_beacon-chain.md | 6 +- .../eth2spec/phase0/state_transition.py | 69 +------------------ 2 files changed, 5 insertions(+), 70 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index bfb51e99a..e57fce037 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1698,14 +1698,14 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: assert len(body.deposits) == min(MAX_DEPOSITS, state.latest_eth1_data.deposit_count - state.deposit_index) assert len(body.transfers) == len(set(body.transfers)) - for operations, max_operations, function in { + for operations, max_operations, function in ( (body.proposer_slashings, MAX_PROPOSER_SLASHINGS, process_proposer_slashing), - (body.attester_slashings, MAX_ATTESTER_SLASHINGS, attester_slashings), + (body.attester_slashings, MAX_ATTESTER_SLASHINGS, process_attester_slashing), (body.attestations, MAX_ATTESTATIONS, process_attestation), (body.deposits, MAX_DEPOSITS, process_deposit), (body.voluntary_exits, MAX_VOLUNTARY_EXITS, process_voluntary_exit), (body.transfers, MAX_TRANSFERS, process_transfer), - }: + ): assert len(operations) <= max_operations for operation in operations: function(state, operation) diff --git a/test_libs/pyspec/eth2spec/phase0/state_transition.py b/test_libs/pyspec/eth2spec/phase0/state_transition.py index 1bef358d4..2aa0a38a6 100644 --- a/test_libs/pyspec/eth2spec/phase0/state_transition.py +++ b/test_libs/pyspec/eth2spec/phase0/state_transition.py @@ -14,76 +14,11 @@ from .spec import ( ) -def expected_deposit_count(state: BeaconState) -> int: - return min( - spec.MAX_DEPOSITS, - state.latest_eth1_data.deposit_count - state.deposit_index - ) - - -def process_operation_type(state: BeaconState, - operations: List[Any], - max_operations: int, - tx_fn: Callable[[BeaconState, Any], None]) -> None: - assert len(operations) <= max_operations - for operation in operations: - tx_fn(state, operation) - - -def process_operations(state: BeaconState, block: BeaconBlock) -> None: - process_operation_type( - state, - block.body.proposer_slashings, - spec.MAX_PROPOSER_SLASHINGS, - spec.process_proposer_slashing, - ) - - process_operation_type( - state, - block.body.attester_slashings, - spec.MAX_ATTESTER_SLASHINGS, - spec.process_attester_slashing, - ) - - process_operation_type( - state, - block.body.attestations, - spec.MAX_ATTESTATIONS, - spec.process_attestation, - ) - - assert len(block.body.deposits) == expected_deposit_count(state) - process_operation_type( - state, - block.body.deposits, - spec.MAX_DEPOSITS, - spec.process_deposit, - ) - - process_operation_type( - state, - block.body.voluntary_exits, - spec.MAX_VOLUNTARY_EXITS, - spec.process_voluntary_exit, - ) - - assert len(block.body.transfers) == len(set(block.body.transfers)) - process_operation_type( - state, - block.body.transfers, - spec.MAX_TRANSFERS, - spec.process_transfer, - ) - - -def process_block(state: BeaconState, - block: BeaconBlock, - verify_state_root: bool=False) -> None: +def process_block(state: BeaconState, block: BeaconBlock, verify_state_root) -> None: spec.process_block_header(state, block) spec.process_randao(state, block) spec.process_eth1_data(state, block) - - process_operations(state, block) + spec.process_operations(state, block.body) if verify_state_root: spec.verify_block_state_root(state, block) From 5df79d7565fd4fc49fe4367cb40afa7b121d5718 Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Wed, 1 May 2019 13:14:10 +0100 Subject: [PATCH 03/17] Get rid of state_transition.py --- specs/core/0_beacon-chain.md | 81 ++++++++----------- .../eth2spec/phase0/state_transition.py | 47 ----------- .../test_process_attestation.py | 4 +- .../test_process_block_header.py | 2 +- .../test_process_crosslinks.py | 4 +- test_libs/pyspec/tests/helpers.py | 8 +- test_libs/pyspec/tests/test_finality.py | 5 +- test_libs/pyspec/tests/test_sanity.py | 6 +- 8 files changed, 44 insertions(+), 113 deletions(-) delete mode 100644 test_libs/pyspec/eth2spec/phase0/state_transition.py diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index e57fce037..50e390326 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -105,7 +105,6 @@ - [Registry updates](#registry-updates) - [Slashings](#slashings) - [Final updates](#final-updates) - - [Per-slot processing](#per-slot-processing) - [Per-block processing](#per-block-processing) - [Block header](#block-header) - [RANDAO](#randao) @@ -1264,27 +1263,27 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], ## Beacon chain state transition function -We now define the state transition function. At a high level, the state transition is made up of four parts: +The post-state corresponding to a pre-state `state` and a block `block` is defined as `state_transition(state, block)`. -1. State caching, which happens at the start of every slot. -2. The per-epoch transitions, which happens at the start of the first slot of every epoch. -3. The per-slot transitions, which happens at every slot. -4. The per-block transitions, which happens at every block. +```python +def state_transition(state: BeaconState, block: BeaconBlock) -> BeaconState: + assert state.slot < block.slot + while state.slot < block.slot: + # State caching at the start of every slot + cache_state(state) + # Per-epoch processing at the start of the first slot of every epoch + if (state.slot + 1) % SLOTS_PER_EPOCH == 0: + process_epoch_transition(state) + # Slot incrementing + state.slot += 1 + # Block processing at every block + process_block(state, block) +``` -Transition section notes: -* The state caching caches the state root of the previous slot and updates block and state roots records. -* The per-epoch transitions focus on the [validator](#dfn-validator) registry, including adjusting balances and activating and exiting [validators](#dfn-validator), as well as processing crosslinks and managing block justification/finalization. -* The per-slot transitions focus on the slot counter. -* The per-block transitions generally focus on verifying aggregate signatures and saving temporary records relating to the per-block activity in the `BeaconState`. - -Beacon blocks that trigger unhandled Python exceptions (e.g. out-of-range list accesses) and failed `assert`s during the state transition are considered invalid. - -Note: If there are skipped slots between a block and its parent block, run the steps in the [state-root](#state-caching), [per-epoch](#per-epoch-processing), and [per-slot](#per-slot-processing) sections once for each skipped slot and then once for the slot containing the new block. +Note: Beacon blocks that trigger unhandled Python exceptions (e.g. out-of-range list accesses) and failed `assert`s during the state transition are considered invalid. ### State caching -At every `slot > GENESIS_SLOT` run the following function: - ```python def cache_state(state: BeaconState) -> None: # Cache latest known state root (for previous slot) @@ -1302,12 +1301,18 @@ def cache_state(state: BeaconState) -> None: ### Per-epoch processing -The steps below happen when `state.slot > GENESIS_SLOT and (state.slot + 1) % SLOTS_PER_EPOCH == 0`. +```python +def process_epoch_transition(state: BeaconState) -> None: + process_justification_and_finalization(state) + process_crosslinks(state) + process_rewards_and_penalties(state) + process_registry_updates(state) + process_slashings(state) + process_final_updates(state) +``` #### Helper functions -We define epoch transition helper functions: - ```python def get_total_active_balance(state: BeaconState) -> Gwei: return get_total_balance(state, get_active_validator_indices(state, get_current_epoch(state))) @@ -1387,8 +1392,6 @@ def get_earliest_attestation(state: BeaconState, attestations: List[PendingAttes #### Justification and finalization -Run the following function: - ```python def process_justification_and_finalization(state: BeaconState) -> None: if get_current_epoch(state) <= GENESIS_EPOCH + 1: @@ -1436,8 +1439,6 @@ def process_justification_and_finalization(state: BeaconState) -> None: #### Crosslinks -Run the following function: - ```python def process_crosslinks(state: BeaconState) -> None: state.previous_crosslinks = [c for c in state.current_crosslinks] @@ -1453,8 +1454,6 @@ def process_crosslinks(state: BeaconState) -> None: #### Rewards and penalties -First, we define additional helpers: - ```python def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei: adjusted_quotient = integer_squareroot(get_total_active_balance(state)) // BASE_REWARD_QUOTIENT @@ -1526,8 +1525,6 @@ def get_crosslink_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]: return [rewards, penalties] ``` -Run the following function: - ```python def process_rewards_and_penalties(state: BeaconState) -> None: if get_current_epoch(state) == GENESIS_EPOCH: @@ -1542,8 +1539,6 @@ def process_rewards_and_penalties(state: BeaconState) -> None: #### Registry updates -Run the following function: - ```python def process_registry_updates(state: BeaconState) -> None: # Process activation eligibility and ejections @@ -1568,8 +1563,6 @@ def process_registry_updates(state: BeaconState) -> None: #### Slashings -Run the following function: - ```python def process_slashings(state: BeaconState) -> None: current_epoch = get_current_epoch(state) @@ -1592,8 +1585,6 @@ def process_slashings(state: BeaconState) -> None: #### Final updates -Run the following function: - ```python def process_final_updates(state: BeaconState) -> None: current_epoch = get_current_epoch(state) @@ -1632,18 +1623,16 @@ def process_final_updates(state: BeaconState) -> None: state.current_epoch_attestations = [] ``` -### Per-slot processing - -At every `slot > GENESIS_SLOT` run the following function: - -```python -def advance_slot(state: BeaconState) -> None: - state.slot += 1 -``` - ### Per-block processing -For every `block` except the genesis block, run `process_block_header(state, block)`, `process_randao(state, block)` and `process_eth1_data(state, block)`. +```python +def process_block(state: BeaconState, block: BeaconBlock) -> None: + process_block_header(state, block) + process_randao(state, block) + process_eth1_data(state, block) + process_operations(state, block.body) + # verify_block_state_root(state, block) +``` #### Block header @@ -1691,8 +1680,6 @@ def process_eth1_data(state: BeaconState, block: BeaconBlock) -> None: #### Operations -The sub-sections below define helper functions, one per operation type. The full processing of operations is done by running `process_operations(state, block.body)`. - ```python def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: assert len(body.deposits) == min(MAX_DEPOSITS, state.latest_eth1_data.deposit_count - state.deposit_index) @@ -1913,8 +1900,6 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None: #### State root verification -Verify the block's `state_root` by running the following function: - ```python def verify_block_state_root(state: BeaconState, block: BeaconBlock) -> None: assert block.state_root == hash_tree_root(state) diff --git a/test_libs/pyspec/eth2spec/phase0/state_transition.py b/test_libs/pyspec/eth2spec/phase0/state_transition.py deleted file mode 100644 index 2aa0a38a6..000000000 --- a/test_libs/pyspec/eth2spec/phase0/state_transition.py +++ /dev/null @@ -1,47 +0,0 @@ -from . import spec - - -from typing import ( - Any, - Callable, - List -) - -from .spec import ( - BeaconState, - BeaconBlock, - Slot, -) - - -def process_block(state: BeaconState, block: BeaconBlock, verify_state_root) -> None: - spec.process_block_header(state, block) - spec.process_randao(state, block) - spec.process_eth1_data(state, block) - spec.process_operations(state, block.body) - if verify_state_root: - spec.verify_block_state_root(state, block) - - -def process_epoch_transition(state: BeaconState) -> None: - spec.process_justification_and_finalization(state) - spec.process_crosslinks(state) - spec.process_rewards_and_penalties(state) - spec.process_registry_updates(state) - spec.process_slashings(state) - spec.process_final_updates(state) - - -def state_transition_to(state: BeaconState, up_to: Slot) -> BeaconState: - while state.slot < up_to: - spec.cache_state(state) - if (state.slot + 1) % spec.SLOTS_PER_EPOCH == 0: - process_epoch_transition(state) - spec.advance_slot(state) - - -def state_transition(state: BeaconState, - block: BeaconBlock, - verify_state_root: bool=False) -> BeaconState: - state_transition_to(state, block.slot) - process_block(state, block, verify_state_root) diff --git a/test_libs/pyspec/tests/block_processing/test_process_attestation.py b/test_libs/pyspec/tests/block_processing/test_process_attestation.py index 1be60c860..105d1e0a4 100644 --- a/test_libs/pyspec/tests/block_processing/test_process_attestation.py +++ b/test_libs/pyspec/tests/block_processing/test_process_attestation.py @@ -3,13 +3,11 @@ import pytest import eth2spec.phase0.spec as spec -from eth2spec.phase0.state_transition import ( - state_transition, -) from eth2spec.phase0.spec import ( get_current_epoch, process_attestation, slot_to_epoch, + state_transition, ) from tests.helpers import ( build_empty_block_for_next_slot, diff --git a/test_libs/pyspec/tests/block_processing/test_process_block_header.py b/test_libs/pyspec/tests/block_processing/test_process_block_header.py index b35b0a9c1..6fd6e674e 100644 --- a/test_libs/pyspec/tests/block_processing/test_process_block_header.py +++ b/test_libs/pyspec/tests/block_processing/test_process_block_header.py @@ -5,10 +5,10 @@ import pytest from eth2spec.phase0.spec import ( get_beacon_proposer_index, cache_state, - advance_slot, process_block_header, ) from tests.helpers import ( + advance_slot, build_empty_block_for_next_slot, next_slot, ) diff --git a/test_libs/pyspec/tests/epoch_processing/test_process_crosslinks.py b/test_libs/pyspec/tests/epoch_processing/test_process_crosslinks.py index fe694724a..60e0dec53 100644 --- a/test_libs/pyspec/tests/epoch_processing/test_process_crosslinks.py +++ b/test_libs/pyspec/tests/epoch_processing/test_process_crosslinks.py @@ -3,13 +3,11 @@ 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, + state_transition, ) from tests.helpers import ( add_attestation_to_state, diff --git a/test_libs/pyspec/tests/helpers.py b/test_libs/pyspec/tests/helpers.py index 63e4cd710..2cb272ff3 100644 --- a/test_libs/pyspec/tests/helpers.py +++ b/test_libs/pyspec/tests/helpers.py @@ -2,9 +2,6 @@ from copy import deepcopy from py_ecc import bls -from eth2spec.phase0.state_transition import ( - state_transition, -) import eth2spec.phase0.spec as spec from eth2spec.utils.minimal_ssz import signing_root from eth2spec.phase0.spec import ( @@ -38,6 +35,7 @@ from eth2spec.phase0.spec import ( get_shard_delta, hash_tree_root, slot_to_epoch, + state_transition, verify_merkle_branch, hash, ) @@ -53,6 +51,10 @@ pubkeys = [bls.privtopub(privkey) for privkey in privkeys] pubkey_to_privkey = {pubkey: privkey for privkey, pubkey in zip(privkeys, pubkeys)} +def advance_slot(state) -> None: + state.slot += 1 + + def get_balance(state, index): return state.balances[index] diff --git a/test_libs/pyspec/tests/test_finality.py b/test_libs/pyspec/tests/test_finality.py index ca048c2b2..816dfd6bd 100644 --- a/test_libs/pyspec/tests/test_finality.py +++ b/test_libs/pyspec/tests/test_finality.py @@ -4,9 +4,6 @@ import pytest import eth2spec.phase0.spec as spec -from eth2spec.phase0.state_transition import ( - state_transition, -) from .helpers import ( build_empty_block_for_next_slot, fill_aggregate_attestation, @@ -67,7 +64,7 @@ def next_epoch_with_attestations(state, fill_aggregate_attestation(post_state, prev_attestation) block.body.attestations.append(prev_attestation) - state_transition(post_state, block) + spec.state_transition(post_state, block) blocks.append(block) return state, blocks, post_state diff --git a/test_libs/pyspec/tests/test_sanity.py b/test_libs/pyspec/tests/test_sanity.py index b7d31f122..4d826fc13 100644 --- a/test_libs/pyspec/tests/test_sanity.py +++ b/test_libs/pyspec/tests/test_sanity.py @@ -20,13 +20,10 @@ from eth2spec.phase0.spec import ( get_state_root, get_current_epoch, get_domain, - advance_slot, cache_state, verify_merkle_branch, - hash, -) -from eth2spec.phase0.state_transition import ( state_transition, + hash, ) from eth2spec.utils.merkle_minimal import ( calc_merkle_tree_from_leaves, @@ -34,6 +31,7 @@ from eth2spec.utils.merkle_minimal import ( get_merkle_root, ) from .helpers import ( + advance_slot, get_balance, build_deposit_data, build_empty_block_for_next_slot, From 2e63a9b5a0322e0e87f72baee3a279999dbdddc7 Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Wed, 1 May 2019 13:29:03 +0100 Subject: [PATCH 04/17] clean up --- specs/core/0_beacon-chain.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 50e390326..8f69213bf 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -97,7 +97,7 @@ - [On genesis](#on-genesis) - [Beacon chain state transition function](#beacon-chain-state-transition-function) - [State caching](#state-caching) - - [Per-epoch processing](#per-epoch-processing) + - [Epoch processing](#epoch-processing) - [Helper functions](#helper-functions-1) - [Justification and finalization](#justification-and-finalization) - [Crosslinks](#crosslinks) @@ -105,7 +105,7 @@ - [Registry updates](#registry-updates) - [Slashings](#slashings) - [Final updates](#final-updates) - - [Per-block processing](#per-block-processing) + - [Block processing](#block-processing) - [Block header](#block-header) - [RANDAO](#randao) - [Eth1 data](#eth1-data) @@ -1263,7 +1263,7 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], ## Beacon chain state transition function -The post-state corresponding to a pre-state `state` and a block `block` is defined as `state_transition(state, block)`. +The post-state corresponding to a pre-state `state` and a block `block` is defined as `state_transition(state, block)`. State transitions that trigger an unhandled exception (e.g. failed `assert`s and out-of-range list accesses) are considered invalid. ```python def state_transition(state: BeaconState, block: BeaconBlock) -> BeaconState: @@ -1271,17 +1271,15 @@ def state_transition(state: BeaconState, block: BeaconBlock) -> BeaconState: while state.slot < block.slot: # State caching at the start of every slot cache_state(state) - # Per-epoch processing at the start of the first slot of every epoch + # Epoch processing at the start of the first slot of every epoch if (state.slot + 1) % SLOTS_PER_EPOCH == 0: - process_epoch_transition(state) + process_epoch(state) # Slot incrementing state.slot += 1 # Block processing at every block process_block(state, block) ``` -Note: Beacon blocks that trigger unhandled Python exceptions (e.g. out-of-range list accesses) and failed `assert`s during the state transition are considered invalid. - ### State caching ```python @@ -1299,10 +1297,10 @@ def cache_state(state: BeaconState) -> None: state.latest_block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = latest_block_root ``` -### Per-epoch processing +### Epoch processing ```python -def process_epoch_transition(state: BeaconState) -> None: +def process_epoch(state: BeaconState) -> None: process_justification_and_finalization(state) process_crosslinks(state) process_rewards_and_penalties(state) @@ -1623,7 +1621,7 @@ def process_final_updates(state: BeaconState) -> None: state.current_epoch_attestations = [] ``` -### Per-block processing +### Block processing ```python def process_block(state: BeaconState, block: BeaconBlock) -> None: From 9b24d06b2cbfa494a8169eebc3f97f32b9ab3399 Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Wed, 1 May 2019 14:16:55 +0100 Subject: [PATCH 05/17] Cleanup --- specs/core/0_beacon-chain.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 8f69213bf..153e42cf6 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1263,18 +1263,19 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], ## Beacon chain state transition function -The post-state corresponding to a pre-state `state` and a block `block` is defined as `state_transition(state, block)`. State transitions that trigger an unhandled exception (e.g. failed `assert`s and out-of-range list accesses) are considered invalid. +The post-state corresponding to a pre-state `state` and a block `block` is defined as `state_transition(state, block)`. State transitions that trigger an unhandled exception (e.g. a failed `assert` or an out-of-range list access) are considered invalid. ```python def state_transition(state: BeaconState, block: BeaconBlock) -> BeaconState: assert state.slot < block.slot + # Slot processing (including slots with no blocks) while state.slot < block.slot: # State caching at the start of every slot cache_state(state) # Epoch processing at the start of the first slot of every epoch if (state.slot + 1) % SLOTS_PER_EPOCH == 0: process_epoch(state) - # Slot incrementing + # Increment slot number state.slot += 1 # Block processing at every block process_block(state, block) @@ -1284,17 +1285,15 @@ def state_transition(state: BeaconState, block: BeaconBlock) -> BeaconState: ```python def cache_state(state: BeaconState) -> None: - # Cache latest known state root (for previous slot) - latest_state_root = hash_tree_root(state) - state.latest_state_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = latest_state_root + # Cache state and block roots of previous slot + previous_state_root = hash_tree_root(state) + state.latest_state_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_state_root + previous_block_root = signing_root(state.latest_block_header) + state.latest_block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_block_root - # Store latest known state root (for previous slot) in latest_block_header if it is empty + # Cache previous state root in latest_block_header, if empty if state.latest_block_header.state_root == ZERO_HASH: - state.latest_block_header.state_root = latest_state_root - - # Cache latest known block root (for previous slot) - latest_block_root = signing_root(state.latest_block_header) - state.latest_block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = latest_block_root + state.latest_block_header.state_root = previous_state_root ``` ### Epoch processing From 7980cf3ef79ee4f7ee418de5c8169907280169a2 Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Wed, 1 May 2019 15:07:55 +0100 Subject: [PATCH 06/17] Fix --- specs/core/0_beacon-chain.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 153e42cf6..4c1498ccf 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1285,15 +1285,18 @@ def state_transition(state: BeaconState, block: BeaconBlock) -> BeaconState: ```python def cache_state(state: BeaconState) -> None: - # Cache state and block roots of previous slot + # Cache state root of previous slot previous_state_root = hash_tree_root(state) state.latest_state_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_state_root - previous_block_root = signing_root(state.latest_block_header) - state.latest_block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_block_root # Cache previous state root in latest_block_header, if empty if state.latest_block_header.state_root == ZERO_HASH: state.latest_block_header.state_root = previous_state_root + + # Cache block root of previous slot + previous_block_root = signing_root(state.latest_block_header) + state.latest_block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_block_root + ``` ### Epoch processing From 9f2cdd9c7807265edcb259dd91d37a9ca51988ca Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 2 May 2019 11:07:25 +0100 Subject: [PATCH 07/17] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 4c1498ccf..86caf7402 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1266,19 +1266,22 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], The post-state corresponding to a pre-state `state` and a block `block` is defined as `state_transition(state, block)`. State transitions that trigger an unhandled exception (e.g. a failed `assert` or an out-of-range list access) are considered invalid. ```python -def state_transition(state: BeaconState, block: BeaconBlock) -> BeaconState: +def state_transition(state: BeaconState, block: BeaconBlock) -> BeaconBlock: + # Block must post-date the state assert state.slot < block.slot - # Slot processing (including slots with no blocks) + # Process slots (including those with no blocks) since block while state.slot < block.slot: - # State caching at the start of every slot + # Cache state at the start of every slot cache_state(state) - # Epoch processing at the start of the first slot of every epoch + # Process epoch at the start of the first slot of every epoch if (state.slot + 1) % SLOTS_PER_EPOCH == 0: process_epoch(state) # Increment slot number state.slot += 1 - # Block processing at every block + # Process block process_block(state, block) + # Return post-state + return state ``` ### State caching From 197a7200efaf1bf9024910e3c41563e3b0c96f77 Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Fri, 3 May 2019 21:21:42 +0100 Subject: [PATCH 08/17] Add HW and Danny comments --- specs/core/0_beacon-chain.md | 61 +++++++++---------- .../test_process_block_header.py | 4 +- .../test_process_crosslinks.py | 4 +- test_libs/pyspec/tests/test_sanity.py | 4 +- 4 files changed, 36 insertions(+), 37 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 86caf7402..b4b44e628 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -96,7 +96,6 @@ - [`slash_validator`](#slash_validator) - [On genesis](#on-genesis) - [Beacon chain state transition function](#beacon-chain-state-transition-function) - - [State caching](#state-caching) - [Epoch processing](#epoch-processing) - [Helper functions](#helper-functions-1) - [Justification and finalization](#justification-and-finalization) @@ -1267,36 +1266,36 @@ The post-state corresponding to a pre-state `state` and a block `block` is defin ```python def state_transition(state: BeaconState, block: BeaconBlock) -> BeaconBlock: - # Block must post-date the state - assert state.slot < block.slot # Process slots (including those with no blocks) since block - while state.slot < block.slot: - # Cache state at the start of every slot - cache_state(state) - # Process epoch at the start of the first slot of every epoch - if (state.slot + 1) % SLOTS_PER_EPOCH == 0: - process_epoch(state) - # Increment slot number - state.slot += 1 + process_slots(state, block.slot) # Process block process_block(state, block) # Return post-state return state ``` -### State caching +```python +def process_slots(state: BeaconState, slot: Slot) -> None: + assert state.slot < slot + while state.slot < slot: + process_slot(state) + # Process epoch on the last slot of every epoch + if (state.slot + 1) % SLOTS_PER_EPOCH == 0: + process_epoch(state) + state.slot += 1 +``` ```python -def cache_state(state: BeaconState) -> None: - # Cache state root of previous slot +def process_slot(state: BeaconState) -> None: + # Cache state root previous_state_root = hash_tree_root(state) state.latest_state_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_state_root - # Cache previous state root in latest_block_header, if empty + # Cache latest block header state root if state.latest_block_header.state_root == ZERO_HASH: state.latest_block_header.state_root = previous_state_root - # Cache block root of previous slot + # Cache block root previous_block_root = signing_root(state.latest_block_header) state.latest_block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_block_root @@ -1631,10 +1630,10 @@ def process_final_updates(state: BeaconState) -> None: ```python def process_block(state: BeaconState, block: BeaconBlock) -> None: process_block_header(state, block) - process_randao(state, block) - process_eth1_data(state, block) + process_randao(state, block.body) + process_eth1_data(state, block.body) process_operations(state, block.body) - # verify_block_state_root(state, block) + # process_state_root(state, block) ``` #### Block header @@ -1661,31 +1660,33 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None: #### RANDAO ```python -def process_randao(state: BeaconState, block: BeaconBlock) -> None: +def process_randao(state: BeaconState, body: BeaconBlockBody) -> None: proposer = state.validator_registry[get_beacon_proposer_index(state)] # Verify that the provided randao value is valid - assert bls_verify(proposer.pubkey, hash_tree_root(get_current_epoch(state)), block.body.randao_reveal, get_domain(state, DOMAIN_RANDAO)) + assert bls_verify(proposer.pubkey, hash_tree_root(get_current_epoch(state)), body.randao_reveal, get_domain(state, DOMAIN_RANDAO)) # Mix it in state.latest_randao_mixes[get_current_epoch(state) % LATEST_RANDAO_MIXES_LENGTH] = ( xor(get_randao_mix(state, get_current_epoch(state)), - hash(block.body.randao_reveal)) + hash(body.randao_reveal)) ) ``` #### Eth1 data ```python -def process_eth1_data(state: BeaconState, block: BeaconBlock) -> None: - state.eth1_data_votes.append(block.body.eth1_data) - if state.eth1_data_votes.count(block.body.eth1_data) * 2 > SLOTS_PER_ETH1_VOTING_PERIOD: - state.latest_eth1_data = block.body.eth1_data +def process_eth1_data(state: BeaconState, body: BeaconBlockBody) -> None: + state.eth1_data_votes.append(body.eth1_data) + if state.eth1_data_votes.count(body.eth1_data) * 2 > SLOTS_PER_ETH1_VOTING_PERIOD: + state.latest_eth1_data = body.eth1_data ``` #### Operations ```python def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: + # Verify that outstanding deposits are processed up to the maximum number of deposits assert len(body.deposits) == min(MAX_DEPOSITS, state.latest_eth1_data.deposit_count - state.deposit_index) + # Verify that there are no duplicate transfers assert len(body.transfers) == len(set(body.transfers)) for operations, max_operations, function in ( @@ -1704,8 +1705,7 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: ##### Proposer slashings ```python -def process_proposer_slashing(state: BeaconState, - proposer_slashing: ProposerSlashing) -> None: +def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSlashing) -> None: """ Process ``ProposerSlashing`` operation. Note that this function mutates ``state``. @@ -1728,8 +1728,7 @@ def process_proposer_slashing(state: BeaconState, ##### Attester slashings ```python -def process_attester_slashing(state: BeaconState, - attester_slashing: AttesterSlashing) -> None: +def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSlashing) -> None: """ Process ``AttesterSlashing`` operation. Note that this function mutates ``state``. @@ -1904,6 +1903,6 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None: #### State root verification ```python -def verify_block_state_root(state: BeaconState, block: BeaconBlock) -> None: +def process_state_root(state: BeaconState, block: BeaconBlock) -> None: assert block.state_root == hash_tree_root(state) ``` diff --git a/test_libs/pyspec/tests/block_processing/test_process_block_header.py b/test_libs/pyspec/tests/block_processing/test_process_block_header.py index 6fd6e674e..149bab514 100644 --- a/test_libs/pyspec/tests/block_processing/test_process_block_header.py +++ b/test_libs/pyspec/tests/block_processing/test_process_block_header.py @@ -4,7 +4,7 @@ import pytest from eth2spec.phase0.spec import ( get_beacon_proposer_index, - cache_state, + process_slot, process_block_header, ) from tests.helpers import ( @@ -18,7 +18,7 @@ pytestmark = pytest.mark.header def prepare_state_for_header_processing(state): - cache_state(state) + process_slot(state) advance_slot(state) diff --git a/test_libs/pyspec/tests/epoch_processing/test_process_crosslinks.py b/test_libs/pyspec/tests/epoch_processing/test_process_crosslinks.py index 60e0dec53..4e3d4b5d8 100644 --- a/test_libs/pyspec/tests/epoch_processing/test_process_crosslinks.py +++ b/test_libs/pyspec/tests/epoch_processing/test_process_crosslinks.py @@ -4,7 +4,7 @@ import pytest import eth2spec.phase0.spec as spec from eth2spec.phase0.spec import ( - cache_state, + process_slot, get_crosslink_deltas, process_crosslinks, state_transition, @@ -33,7 +33,7 @@ def run_process_crosslinks(state, valid=True): state_transition(state, block) # cache state before epoch transition - cache_state(state) + process_slot(state) post_state = deepcopy(state) process_crosslinks(post_state) diff --git a/test_libs/pyspec/tests/test_sanity.py b/test_libs/pyspec/tests/test_sanity.py index 4d826fc13..7fff6fb55 100644 --- a/test_libs/pyspec/tests/test_sanity.py +++ b/test_libs/pyspec/tests/test_sanity.py @@ -20,7 +20,7 @@ from eth2spec.phase0.spec import ( get_state_root, get_current_epoch, get_domain, - cache_state, + process_slot, verify_merkle_branch, state_transition, hash, @@ -51,7 +51,7 @@ pytestmark = pytest.mark.sanity def test_slot_transition(state): test_state = deepcopy(state) - cache_state(test_state) + process_slot(test_state) advance_slot(test_state) assert test_state.slot == state.slot + 1 assert get_state_root(test_state, state.slot) == state.hash_tree_root() From 4c1073fa2f898de7ffe26fd8aa197d38c25a8f35 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 3 May 2019 22:22:19 +0100 Subject: [PATCH 09/17] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index b4b44e628..8d999c65c 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1279,7 +1279,7 @@ def process_slots(state: BeaconState, slot: Slot) -> None: assert state.slot < slot while state.slot < slot: process_slot(state) - # Process epoch on the last slot of every epoch + # Process epoch on the first slot of the next epoch if (state.slot + 1) % SLOTS_PER_EPOCH == 0: process_epoch(state) state.slot += 1 From f371daeb204c1ede12fcb1bf6e5b15263f1bc504 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 7 May 2019 09:01:07 +0100 Subject: [PATCH 10/17] Update specs/core/0_beacon-chain.md Co-Authored-By: JustinDrake --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 8d999c65c..aaa85e425 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1265,7 +1265,7 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], The post-state corresponding to a pre-state `state` and a block `block` is defined as `state_transition(state, block)`. State transitions that trigger an unhandled exception (e.g. a failed `assert` or an out-of-range list access) are considered invalid. ```python -def state_transition(state: BeaconState, block: BeaconBlock) -> BeaconBlock: +def state_transition(state: BeaconState, block: BeaconBlock) -> BeaconState: # Process slots (including those with no blocks) since block process_slots(state, block.slot) # Process block From 50009ea85bd1fd96b81c3f3b6fb3f39b58662050 Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Tue, 7 May 2019 10:12:33 +0100 Subject: [PATCH 11/17] Implement HW's exception-handling suggestion --- scripts/phase0/build_spec.py | 5 +++-- specs/core/0_beacon-chain.md | 17 +++++++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/scripts/phase0/build_spec.py b/scripts/phase0/build_spec.py index 54adfdde7..a1f0209bb 100644 --- a/scripts/phase0/build_spec.py +++ b/scripts/phase0/build_spec.py @@ -5,7 +5,8 @@ import function_puller def build_phase0_spec(sourcefile, outfile): code_lines = [] code_lines.append(""" - + +import copy from typing import ( Any, Callable, @@ -88,7 +89,7 @@ def apply_constants_preset(preset: Dict[str, Any]): # Deal with derived constants global_vars['GENESIS_EPOCH'] = slot_to_epoch(GENESIS_SLOT) - + # Initialize SSZ types again, to account for changed lengths init_SSZ_types() """) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index aaa85e425..84d780ad6 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1262,16 +1262,21 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], ## Beacon chain state transition function -The post-state corresponding to a pre-state `state` and a block `block` is defined as `state_transition(state, block)`. State transitions that trigger an unhandled exception (e.g. a failed `assert` or an out-of-range list access) are considered invalid. +The post-state corresponding to a pre-state `state` and a block `block` is defined as `state_transition(state, block)`. ```python def state_transition(state: BeaconState, block: BeaconBlock) -> BeaconState: - # Process slots (including those with no blocks) since block - process_slots(state, block.slot) - # Process block - process_block(state, block) - # Return post-state + pre_state = copy.deepcopy(state) + try: + # Process slots (including those with no blocks) since block + process_slots(state, block.slot) + # Process block + process_block(state, block) + except Exception: + # State is not advanced on exceptions (e.g. a failed `assert` or an out-of-range list access) + return pre_state return state + ``` ```python From c37157ead1a97fd3fb2fccdda4f0b67ea258a1ea Mon Sep 17 00:00:00 2001 From: Justin Drake Date: Wed, 8 May 2019 19:15:23 +0100 Subject: [PATCH 12/17] Revert exception handling --- specs/core/0_beacon-chain.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 3470459e1..bf210c60c 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1216,19 +1216,15 @@ Let `genesis_block = BeaconBlock(state_root=hash_tree_root(genesis_state))`. ## Beacon chain state transition function -The post-state corresponding to a pre-state `state` and a block `block` is defined as `state_transition(state, block)`. +The post-state corresponding to a pre-state `state` and a block `block` is defined as `state_transition(state, block)`. State transitions that trigger an unhandled excpetion (e.g. a failed `assert` or an out-of-range list access) are considered invalid. ```python def state_transition(state: BeaconState, block: BeaconBlock) -> BeaconState: - pre_state = copy.deepcopy(state) - try: - # Process slots (including those with no blocks) since block - process_slots(state, block.slot) - # Process block - process_block(state, block) - except Exception: - # State is not advanced on exceptions (e.g. a failed `assert` or an out-of-range list access) - return pre_state + # Process slots (including those with no blocks) since block + process_slots(state, block.slot) + # Process block + process_block(state, block) + # Return post-state return state ``` From 8da4b8173e38bec3f46820beab731e173384c689 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 8 May 2019 14:49:53 -0600 Subject: [PATCH 13/17] remove unnecessary import of copy --- scripts/phase0/build_spec.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/phase0/build_spec.py b/scripts/phase0/build_spec.py index ef7c055d2..f7587bad5 100644 --- a/scripts/phase0/build_spec.py +++ b/scripts/phase0/build_spec.py @@ -6,7 +6,6 @@ def build_phase0_spec(sourcefile, outfile): code_lines = [] code_lines.append(""" -import copy from typing import ( Any, Dict, From 7bb85a69ed5759bbf64a73bac111865fc53b2412 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 13 May 2019 16:34:30 -0400 Subject: [PATCH 14/17] add process_slots usage to validator guide --- specs/validator/0_beacon-chain-validator.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 49290b432..ef6e8bf25 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -238,7 +238,7 @@ A validator should create and broadcast the attestation halfway through the `slo First the validator should construct `attestation_data`, an [`AttestationData`](../core/0_beacon-chain.md#attestationdata) object based upon the state at the assigned slot. * Let `head_block` be the result of running the fork choice during the assigned slot. -* Let `head_state` be the state of `head_block` processed through any empty slots up to the assigned slot. +* Let `head_state` be the state of `head_block` processed through any empty slots up to the assigned slot using `process_slots(state, slot)`. ##### LMD GHOST vote @@ -360,7 +360,7 @@ def is_proposer_at_slot(state: BeaconState, return get_beacon_proposer_index(state) == validator_index ``` -*Note*: To see if a validator is assigned to proposer during the slot, the validator must run an empty slot transition from the previous state to the current slot. +*Note*: To see if a validator is assigned to proposer during the slot, the validator must run an empty slot transition from the previous state to the current slot using `process_slots(state, current_slot)`. ### Lookahead From a0a2aa90de166d840ba69dcc487079c7b26571f7 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 13 May 2019 16:40:45 -0400 Subject: [PATCH 15/17] lint --- specs/core/0_beacon-chain.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 0d0e37679..524a40b17 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1253,7 +1253,6 @@ def process_slot(state: BeaconState) -> None: # Cache block root previous_block_root = signing_root(state.latest_block_header) state.latest_block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_block_root - ``` ### Epoch processing @@ -1610,7 +1609,12 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None: def process_randao(state: BeaconState, body: BeaconBlockBody) -> None: proposer = state.validator_registry[get_beacon_proposer_index(state)] # Verify that the provided randao value is valid - assert bls_verify(proposer.pubkey, hash_tree_root(get_current_epoch(state)), body.randao_reveal, get_domain(state, DOMAIN_RANDAO)) + assert bls_verify( + proposer.pubkey, + hash_tree_root(get_current_epoch(state)), + body.randao_reveal, + get_domain(state, DOMAIN_RANDAO), + ) # Mix it in state.latest_randao_mixes[get_current_epoch(state) % LATEST_RANDAO_MIXES_LENGTH] = ( xor(get_randao_mix(state, get_current_epoch(state)), From 5ba90d68e194310816f22f36caa8a7cc3d95cf52 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 13 May 2019 16:53:28 -0400 Subject: [PATCH 16/17] add flag for validate state root --- specs/core/0_beacon-chain.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 524a40b17..a9c4f8811 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1220,11 +1220,14 @@ Let `genesis_block = BeaconBlock(state_root=hash_tree_root(genesis_state))`. The post-state corresponding to a pre-state `state` and a block `block` is defined as `state_transition(state, block)`. State transitions that trigger an unhandled excpetion (e.g. a failed `assert` or an out-of-range list access) are considered invalid. ```python -def state_transition(state: BeaconState, block: BeaconBlock) -> BeaconState: +def state_transition(state: BeaconState, block: BeaconBlock, validate_state_root: bool=False) -> BeaconState: # Process slots (including those with no blocks) since block process_slots(state, block.slot) # Process block process_block(state, block) + # Validate state root if received from external source + if validate_state_root: + process_state_root(state, block) # Return post-state return state ``` @@ -1579,7 +1582,6 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: process_randao(state, block.body) process_eth1_data(state, block.body) process_operations(state, block.body) - # process_state_root(state, block) ``` #### Block header From c60635d2c9c829865189b4856ef4748cff7a3d9d Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 14 May 2019 06:15:03 +0100 Subject: [PATCH 17/17] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index a9c4f8811..ee37d9217 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -117,7 +117,6 @@ - [Deposits](#deposits) - [Voluntary exits](#voluntary-exits) - [Transfers](#transfers) - - [State root verification](#state-root-verification) @@ -373,6 +372,7 @@ The types are defined topologically to aid in facilitating an executable version 'signature': 'bytes96', } ``` + #### `Validator` ```python @@ -577,9 +577,7 @@ The types are defined topologically to aid in facilitating an executable version 'latest_block_roots': ['bytes32', SLOTS_PER_HISTORICAL_ROOT], 'latest_state_roots': ['bytes32', SLOTS_PER_HISTORICAL_ROOT], 'latest_active_index_roots': ['bytes32', LATEST_ACTIVE_INDEX_ROOTS_LENGTH], - # Balances slashed at every withdrawal period 'latest_slashed_balances': ['uint64', LATEST_SLASHED_EXIT_LENGTH], - # `latest_block_header.state_root == ZERO_HASH` temporarily 'latest_block_header': BeaconBlockHeader, 'historical_roots': ['bytes32'], @@ -1225,9 +1223,9 @@ def state_transition(state: BeaconState, block: BeaconBlock, validate_state_root process_slots(state, block.slot) # Process block process_block(state, block) - # Validate state root if received from external source + # Validate state root (`validate_state_root == True` in production) if validate_state_root: - process_state_root(state, block) + assert block.state_root == hash_tree_root(state) # Return post-state return state ``` @@ -1838,10 +1836,3 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None: assert not (0 < state.balances[transfer.sender] < MIN_DEPOSIT_AMOUNT) assert not (0 < state.balances[transfer.recipient] < MIN_DEPOSIT_AMOUNT) ``` - -#### State root verification - -```python -def process_state_root(state: BeaconState, block: BeaconBlock) -> None: - assert block.state_root == hash_tree_root(state) -```