diff --git a/setup.py b/setup.py index 4430ccec0..fa73e0824 100644 --- a/setup.py +++ b/setup.py @@ -532,7 +532,7 @@ class NoopExecutionEngine(ExecutionEngine): def finalize_block(self, block_hash: Hash32) -> bool: return True - def assemble_block(self, block_hash: Hash32, timestamp: uint64) -> ExecutionPayload: + def assemble_block(self, block_hash: Hash32, timestamp: uint64, random: Bytes32) -> ExecutionPayload: raise NotImplementedError("no default block production") diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 562adaab4..7823a664d 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -92,8 +92,9 @@ class ExecutionPayload(Container): parent_hash: Hash32 coinbase: Bytes20 # 'beneficiary' in the yellow paper state_root: Bytes32 - logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] receipt_root: Bytes32 # 'receipts root' in the yellow paper + logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] + random: Bytes32 # 'difficulty' in the yellow paper block_number: uint64 # 'number' in the yellow paper gas_limit: uint64 gas_used: uint64 @@ -111,8 +112,9 @@ class ExecutionPayloadHeader(Container): parent_hash: Hash32 coinbase: Bytes20 state_root: Bytes32 - logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] receipt_root: Bytes32 + logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] + random: Bytes32 block_number: uint64 gas_limit: uint64 gas_used: uint64 @@ -196,12 +198,15 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: #### `process_execution_payload` +*Note:* This function depends on `process_randao` function call as it retrieves the most recent randao mix from the `state`. Implementations that are considering parallel processing of execution payload with respect to beacon chain state transition function should work around this dependency. + ```python def process_execution_payload(state: BeaconState, payload: ExecutionPayload, execution_engine: ExecutionEngine) -> None: - # Verify consistency of the parent hash and block number + # Verify consistency of the parent hash, block number and random if is_merge_complete(state): assert payload.parent_hash == state.latest_execution_payload_header.block_hash assert payload.block_number == state.latest_execution_payload_header.block_number + uint64(1) + assert payload.random == get_randao_mix(state, get_current_epoch(state)) # Verify timestamp assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) # Verify the execution payload is valid @@ -211,8 +216,9 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe parent_hash=payload.parent_hash, coinbase=payload.coinbase, state_root=payload.state_root, - logs_bloom=payload.logs_bloom, receipt_root=payload.receipt_root, + logs_bloom=payload.logs_bloom, + random=payload.random, block_number=payload.block_number, gas_limit=payload.gas_limit, gas_used=payload.gas_used, @@ -266,6 +272,7 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Bytes32, # [New in Merge] Initialize the execution payload header (with block number set to 0) state.latest_execution_payload_header.block_hash = eth1_block_hash state.latest_execution_payload_header.timestamp = eth1_timestamp + state.latest_execution_payload_header.random = eth1_block_hash return state ``` diff --git a/specs/merge/validator.md b/specs/merge/validator.md index 21668b327..97e6a1deb 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, random: 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_timestamp_at_slot(state, state.slot) + randao_mix = compute_randao_mix(state, randao_reveal) + return execution_engine.assemble_block(parent_hash, timestamp, randao_mix) + + def get_execution_payload(state: BeaconState, transition_store: TransitionStore, + randao_reveal: BLSSignature, execution_engine: ExecutionEngine) -> ExecutionPayload: if not is_merge_complete(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, randao_reveal, execution_engine) # 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, randao_reveal, execution_engine) ``` diff --git a/tests/core/pyspec/eth2spec/test/helpers/block.py b/tests/core/pyspec/eth2spec/test/helpers/block.py index 0dd9dc98e..b8f7c4bcb 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/block.py +++ b/tests/core/pyspec/eth2spec/test/helpers/block.py @@ -93,13 +93,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 = spec.compute_randao_mix(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 238e2e00f..c41a05079 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=None): """ Assuming a pre-state of the same slot, build a valid ExecutionPayload without any transactions. """ @@ -6,13 +6,17 @@ def build_empty_execution_payload(spec, state): timestamp = spec.compute_time_at_slot(state, state.slot) empty_txs = spec.List[spec.Transaction, spec.MAX_TRANSACTIONS_PER_PAYLOAD]() + if randao_mix is None: + randao_mix = spec.get_randao_mix(state, spec.get_current_epoch(state)) + payload = spec.ExecutionPayload( parent_hash=latest.block_hash, coinbase=spec.Bytes20(), state_root=latest.state_root, # no changes to the state - logs_bloom=spec.ByteVector[spec.BYTES_PER_LOGS_BLOOM](), # TODO: zeroed logs bloom for empty logs ok? 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? block_number=latest.block_number + 1, + random=randao_mix, gas_limit=latest.gas_limit, # retain same limit gas_used=0, # empty block, 0 gas timestamp=timestamp, @@ -30,8 +34,9 @@ def get_execution_payload_header(spec, execution_payload): parent_hash=execution_payload.parent_hash, coinbase=execution_payload.coinbase, state_root=execution_payload.state_root, - logs_bloom=execution_payload.logs_bloom, receipt_root=execution_payload.receipt_root, + logs_bloom=execution_payload.logs_bloom, + random=execution_payload.random, block_number=execution_payload.block_number, gas_limit=execution_payload.gas_limit, gas_used=execution_payload.gas_used,