From 95775e1b904b1f97d19ec69669bd6ddc1114aef4 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 11 Jun 2021 16:05:19 +0600 Subject: [PATCH] Add randao to execution payload --- specs/merge/beacon-chain.md | 10 +++- specs/merge/validator.md | 25 ++++++--- .../pyspec/eth2spec/test/helpers/block.py | 11 +++- .../test/helpers/execution_payload.py | 7 +-- .../test_process_execution_payload.py | 51 +++++++++---------- 5 files changed, 66 insertions(+), 38 deletions(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 697bd0c96..c19a406f0 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -105,6 +105,7 @@ class ExecutionPayload(Container): timestamp: uint64 receipt_root: Bytes32 logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] + randao: Bytes32 # 'difficulty' in the yellow paper transactions: List[OpaqueTransaction, MAX_EXECUTION_TRANSACTIONS] ``` @@ -126,6 +127,7 @@ class ExecutionPayloadHeader(Container): timestamp: uint64 receipt_root: Bytes32 logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] + randao: Bytes32 transactions_root: Root ``` @@ -198,7 +200,9 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: process_operations(state, block.body) # Pre-merge, skip execution payload processing if is_execution_enabled(state, block): - process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [New in Merge] + # [New in Merge] + randao_mix = get_randao_mix(state, get_current_epoch(state)) + process_execution_payload(state, block.body.execution_payload, randao_mix, EXECUTION_ENGINE) ``` #### Execution payload processing @@ -208,6 +212,7 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: ```python def process_execution_payload(state: BeaconState, execution_payload: ExecutionPayload, + randao_mix: Bytes32, execution_engine: ExecutionEngine) -> None: """ Note: This function is designed to be able to be run in parallel with the other `process_block` sub-functions @@ -215,6 +220,7 @@ def process_execution_payload(state: BeaconState, if is_transition_completed(state): assert execution_payload.parent_hash == state.latest_execution_payload_header.block_hash assert execution_payload.number == state.latest_execution_payload_header.number + 1 + assert execution_payload.randao == randao_mix assert execution_payload.timestamp == compute_time_at_slot(state, state.slot) @@ -231,6 +237,7 @@ def process_execution_payload(state: BeaconState, timestamp=execution_payload.timestamp, receipt_root=execution_payload.receipt_root, logs_bloom=execution_payload.logs_bloom, + randao=execution_payload.randao, transactions_root=hash_tree_root(execution_payload.transactions), ) ``` @@ -289,6 +296,7 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Bytes32, timestamp=eth1_timestamp, receipt_root=Bytes32(), logs_bloom=ByteVector[BYTES_PER_LOGS_BLOOM](), + randao=eth1_block_hash, transactions_root=Root(), ) diff --git a/specs/merge/validator.md b/specs/merge/validator.md index c5a7a4c78..6b86aae75 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -49,7 +49,7 @@ The body of this function is implementation dependent. The Consensus API may be used to implement this with an external execution engine. ```python -def assemble_block(self: ExecutionEngine, block_hash: Hash32, timestamp: uint64) -> ExecutionPayload: +def assemble_block(self: ExecutionEngine, block_hash: Hash32, timestamp: uint64, randao: Bytes32) -> ExecutionPayload: ... ``` @@ -70,8 +70,23 @@ Let `get_pow_chain_head() -> PowBlock` be the function that returns the head of * Set `block.body.execution_payload = get_execution_payload(state, transition_store, execution_engine)` where: ```python +def compute_randao_mix(state: BeaconState, randao_reveal: BLSSignature) -> Bytes32: + epoch = get_current_epoch(state) + return xor(get_randao_mix(state, epoch), hash(randao_reveal)) + + +def produce_execution_payload(state: BeaconState, + parent_hash: Hash32, + randao_reveal: BLSSignature, + execution_engine: ExecutionEngine) -> ExecutionPayload: + timestamp = compute_time_at_slot(state, state.slot) + randao = compute_randao_mix(state, randao_reveal) + return execution_engine.assemble_block(parent_hash, timestamp, randao) + + def get_execution_payload(state: BeaconState, transition_store: TransitionStore, + randao_reveal: BLSSignature, execution_engine: ExecutionEngine) -> ExecutionPayload: if not is_transition_completed(state): pow_block = get_pow_chain_head() @@ -80,11 +95,9 @@ def get_execution_payload(state: BeaconState, return ExecutionPayload() else: # Signify merge via producing on top of the last PoW block - timestamp = compute_time_at_slot(state, state.slot) - return execution_engine.assemble_block(pow_block.block_hash, timestamp) + return produce_execution_payload(state, pow_block.block_hash, timestamp, randao_reveal) # Post-merge, normal payload - execution_parent_hash = state.latest_execution_payload_header.block_hash - timestamp = compute_time_at_slot(state, state.slot) - return execution_engine.assemble_block(execution_parent_hash, timestamp) + parent_hash = state.latest_execution_payload_header.block_hash + return produce_execution_payload(state, parent_hash, timestamp, randao_reveal) ``` diff --git a/tests/core/pyspec/eth2spec/test/helpers/block.py b/tests/core/pyspec/eth2spec/test/helpers/block.py index 0dd9dc98e..3781f0d50 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/block.py +++ b/tests/core/pyspec/eth2spec/test/helpers/block.py @@ -35,6 +35,11 @@ def apply_randao_reveal(spec, state, block, proposer_index=None): block.body.randao_reveal = bls.Sign(privkey, signing_root) +def compute_randao_mix(spec, state, randao_reveal): + epoch = spec.get_current_epoch(state) + return spec.xor(spec.get_randao_mix(state, epoch), spec.hash(randao_reveal)) + + # Fully ignore the function if BLS is off, beacon-proposer index calculation is slow. @only_with_bls() def apply_sig(spec, state, signed_block, proposer_index=None): @@ -93,13 +98,15 @@ def build_empty_block(spec, state, slot=None): empty_block.body.eth1_data.deposit_count = state.eth1_deposit_index empty_block.parent_root = parent_block_root + apply_randao_reveal(spec, state, empty_block) + if is_post_altair(spec): empty_block.body.sync_aggregate.sync_committee_signature = spec.G2_POINT_AT_INFINITY if is_post_merge(spec): - empty_block.body.execution_payload = build_empty_execution_payload(spec, state) + randao_mix = compute_randao_mix(spec, state, empty_block.body.randao_reveal) + empty_block.body.execution_payload = build_empty_execution_payload(spec, state, randao_mix) - apply_randao_reveal(spec, state, empty_block) return empty_block diff --git a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py index 7774aa4d9..661abe964 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py @@ -1,4 +1,4 @@ -def build_empty_execution_payload(spec, state): +def build_empty_execution_payload(spec, state, randao_mix): """ Assuming a pre-state of the same slot, build a valid ExecutionPayload without any transactions. """ @@ -17,6 +17,7 @@ def build_empty_execution_payload(spec, state): timestamp=timestamp, receipt_root=b"no receipts here" + b"\x00" * 16, # TODO: root of empty MPT may be better. logs_bloom=spec.ByteVector[spec.BYTES_PER_LOGS_BLOOM](), # TODO: zeroed logs bloom for empty logs ok? + randao=randao_mix, transactions=empty_txs, ) # TODO: real RLP + block hash logic would be nice, requires RLP and keccak256 dependency however. @@ -24,7 +25,6 @@ def build_empty_execution_payload(spec, state): return payload - def get_execution_payload_header(spec, execution_payload): return spec.ExecutionPayloadHeader( block_hash=execution_payload.block_hash, @@ -37,6 +37,7 @@ def get_execution_payload_header(spec, execution_payload): timestamp=execution_payload.timestamp, receipt_root=execution_payload.receipt_root, logs_bloom=execution_payload.logs_bloom, + randao=execution_payload.randao, transactions_root=spec.hash_tree_root(execution_payload.transactions) ) @@ -46,7 +47,7 @@ def build_state_with_incomplete_transition(spec, state): def build_state_with_complete_transition(spec, state): - pre_state_payload = build_empty_execution_payload(spec, state) + pre_state_payload = build_empty_execution_payload(spec, state, spec.Bytes32()) payload_header = get_execution_payload_header(spec, pre_state_payload) return build_state_with_execution_payload_header(spec, state, payload_header) diff --git a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py index 5edd31960..04fb0df30 100644 --- a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py @@ -7,8 +7,7 @@ from eth2spec.test.helpers.execution_payload import ( from eth2spec.test.context import spec_state_test, expect_assertion_error, with_merge_and_later from eth2spec.test.helpers.state import next_slot - -def run_execution_payload_processing(spec, state, execution_payload, valid=True, execution_valid=True): +def run_execution_payload_processing(spec, state, execution_payload, randao_mix, valid=True, execution_valid=True): """ Run ``process_execution_payload``, yielding: - pre-state ('pre') @@ -32,11 +31,11 @@ def run_execution_payload_processing(spec, state, execution_payload, valid=True, return execution_valid if not valid: - expect_assertion_error(lambda: spec.process_execution_payload(state, execution_payload, TestEngine())) + expect_assertion_error(lambda: spec.process_execution_payload(state, execution_payload, randao_mix, TestEngine())) yield 'post', None return - spec.process_execution_payload(state, execution_payload, TestEngine()) + spec.process_execution_payload(state, execution_payload, randao_mix, TestEngine()) # Make sure we called the engine assert called_new_block @@ -54,9 +53,9 @@ def test_success_first_payload(spec, state): next_slot(spec, state) # execution payload - execution_payload = build_empty_execution_payload(spec, state) + execution_payload = build_empty_execution_payload(spec, state, spec.Bytes32()) - yield from run_execution_payload_processing(spec, state, execution_payload) + yield from run_execution_payload_processing(spec, state, execution_payload, spec.Bytes32()) @with_merge_and_later @@ -67,9 +66,9 @@ def test_success_regular_payload(spec, state): next_slot(spec, state) # execution payload - execution_payload = build_empty_execution_payload(spec, state) + execution_payload = build_empty_execution_payload(spec, state, spec.Bytes32()) - yield from run_execution_payload_processing(spec, state, execution_payload) + yield from run_execution_payload_processing(spec, state, execution_payload, spec.Bytes32()) @with_merge_and_later @@ -81,9 +80,9 @@ def test_success_first_payload_with_gap_slot(spec, state): next_slot(spec, state) # execution payload - execution_payload = build_empty_execution_payload(spec, state) + execution_payload = build_empty_execution_payload(spec, state, spec.Bytes32()) - yield from run_execution_payload_processing(spec, state, execution_payload) + yield from run_execution_payload_processing(spec, state, execution_payload, spec.Bytes32()) @with_merge_and_later @@ -95,9 +94,9 @@ def test_success_regular_payload_with_gap_slot(spec, state): next_slot(spec, state) # execution payload - execution_payload = build_empty_execution_payload(spec, state) + execution_payload = build_empty_execution_payload(spec, state, spec.Bytes32()) - yield from run_execution_payload_processing(spec, state, execution_payload) + yield from run_execution_payload_processing(spec, state, execution_payload, spec.Bytes32()) @with_merge_and_later @@ -110,9 +109,9 @@ def test_bad_execution_first_payload(spec, state): next_slot(spec, state) # execution payload - execution_payload = build_empty_execution_payload(spec, state) + execution_payload = build_empty_execution_payload(spec, state, spec.Bytes32()) - yield from run_execution_payload_processing(spec, state, execution_payload, valid=False, execution_valid=False) + yield from run_execution_payload_processing(spec, state, execution_payload, spec.Bytes32(), valid=False, execution_valid=False) @with_merge_and_later @@ -125,9 +124,9 @@ def test_bad_execution_regular_payload(spec, state): next_slot(spec, state) # execution payload - execution_payload = build_empty_execution_payload(spec, state) + execution_payload = build_empty_execution_payload(spec, state, spec.Bytes32()) - yield from run_execution_payload_processing(spec, state, execution_payload, valid=False, execution_valid=False) + yield from run_execution_payload_processing(spec, state, execution_payload, spec.Bytes32(), valid=False, execution_valid=False) @with_merge_and_later @@ -138,10 +137,10 @@ def test_bad_parent_hash_regular_payload(spec, state): next_slot(spec, state) # execution payload - execution_payload = build_empty_execution_payload(spec, state) + execution_payload = build_empty_execution_payload(spec, state, spec.Bytes32()) execution_payload.parent_hash = spec.Hash32() - yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) + yield from run_execution_payload_processing(spec, state, execution_payload, spec.Bytes32(), valid=False) @with_merge_and_later @@ -152,10 +151,10 @@ def test_bad_number_regular_payload(spec, state): next_slot(spec, state) # execution payload - execution_payload = build_empty_execution_payload(spec, state) + execution_payload = build_empty_execution_payload(spec, state, spec.Bytes32()) execution_payload.number = execution_payload.number + 1 - yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) + yield from run_execution_payload_processing(spec, state, execution_payload, spec.Bytes32(), valid=False) @with_merge_and_later @@ -166,11 +165,11 @@ def test_bad_everything_regular_payload(spec, state): next_slot(spec, state) # execution payload - execution_payload = build_empty_execution_payload(spec, state) + execution_payload = build_empty_execution_payload(spec, state, spec.Bytes32()) execution_payload.parent_hash = spec.Hash32() execution_payload.number = execution_payload.number + 1 - yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) + yield from run_execution_payload_processing(spec, state, execution_payload, spec.Bytes32(), valid=False) @with_merge_and_later @@ -181,10 +180,10 @@ def test_bad_timestamp_first_payload(spec, state): next_slot(spec, state) # execution payload - execution_payload = build_empty_execution_payload(spec, state) + execution_payload = build_empty_execution_payload(spec, state, spec.Bytes32()) execution_payload.timestamp = execution_payload.timestamp + 1 - yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) + yield from run_execution_payload_processing(spec, state, execution_payload, spec.Bytes32(), valid=False) @with_merge_and_later @@ -195,7 +194,7 @@ def test_bad_timestamp_regular_payload(spec, state): next_slot(spec, state) # execution payload - execution_payload = build_empty_execution_payload(spec, state) + execution_payload = build_empty_execution_payload(spec, state, spec.Bytes32()) execution_payload.timestamp = execution_payload.timestamp + 1 - yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) + yield from run_execution_payload_processing(spec, state, execution_payload, spec.Bytes32(), valid=False)