Add randao to execution payload

This commit is contained in:
Mikhail Kalinin 2021-06-11 16:05:19 +06:00
parent 973a874105
commit 95775e1b90
5 changed files with 66 additions and 38 deletions

View File

@ -105,6 +105,7 @@ class ExecutionPayload(Container):
timestamp: uint64 timestamp: uint64
receipt_root: Bytes32 receipt_root: Bytes32
logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM]
randao: Bytes32 # 'difficulty' in the yellow paper
transactions: List[OpaqueTransaction, MAX_EXECUTION_TRANSACTIONS] transactions: List[OpaqueTransaction, MAX_EXECUTION_TRANSACTIONS]
``` ```
@ -126,6 +127,7 @@ class ExecutionPayloadHeader(Container):
timestamp: uint64 timestamp: uint64
receipt_root: Bytes32 receipt_root: Bytes32
logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM]
randao: Bytes32
transactions_root: Root transactions_root: Root
``` ```
@ -198,7 +200,9 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None:
process_operations(state, block.body) process_operations(state, block.body)
# Pre-merge, skip execution payload processing # Pre-merge, skip execution payload processing
if is_execution_enabled(state, block): 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 #### Execution payload processing
@ -208,6 +212,7 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None:
```python ```python
def process_execution_payload(state: BeaconState, def process_execution_payload(state: BeaconState,
execution_payload: ExecutionPayload, execution_payload: ExecutionPayload,
randao_mix: Bytes32,
execution_engine: ExecutionEngine) -> None: execution_engine: ExecutionEngine) -> None:
""" """
Note: This function is designed to be able to be run in parallel with the other `process_block` sub-functions 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): if is_transition_completed(state):
assert execution_payload.parent_hash == state.latest_execution_payload_header.block_hash 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.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) 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, timestamp=execution_payload.timestamp,
receipt_root=execution_payload.receipt_root, receipt_root=execution_payload.receipt_root,
logs_bloom=execution_payload.logs_bloom, logs_bloom=execution_payload.logs_bloom,
randao=execution_payload.randao,
transactions_root=hash_tree_root(execution_payload.transactions), 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, timestamp=eth1_timestamp,
receipt_root=Bytes32(), receipt_root=Bytes32(),
logs_bloom=ByteVector[BYTES_PER_LOGS_BLOOM](), logs_bloom=ByteVector[BYTES_PER_LOGS_BLOOM](),
randao=eth1_block_hash,
transactions_root=Root(), transactions_root=Root(),
) )

View File

@ -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. The Consensus API may be used to implement this with an external execution engine.
```python ```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: * Set `block.body.execution_payload = get_execution_payload(state, transition_store, execution_engine)` where:
```python ```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, def get_execution_payload(state: BeaconState,
transition_store: TransitionStore, transition_store: TransitionStore,
randao_reveal: BLSSignature,
execution_engine: ExecutionEngine) -> ExecutionPayload: execution_engine: ExecutionEngine) -> ExecutionPayload:
if not is_transition_completed(state): if not is_transition_completed(state):
pow_block = get_pow_chain_head() pow_block = get_pow_chain_head()
@ -80,11 +95,9 @@ def get_execution_payload(state: BeaconState,
return ExecutionPayload() return ExecutionPayload()
else: else:
# Signify merge via producing on top of the last PoW block # Signify merge via producing on top of the last PoW block
timestamp = compute_time_at_slot(state, state.slot) return produce_execution_payload(state, pow_block.block_hash, timestamp, randao_reveal)
return execution_engine.assemble_block(pow_block.block_hash, timestamp)
# Post-merge, normal payload # Post-merge, normal payload
execution_parent_hash = state.latest_execution_payload_header.block_hash parent_hash = state.latest_execution_payload_header.block_hash
timestamp = compute_time_at_slot(state, state.slot) return produce_execution_payload(state, parent_hash, timestamp, randao_reveal)
return execution_engine.assemble_block(execution_parent_hash, timestamp)
``` ```

View File

@ -35,6 +35,11 @@ def apply_randao_reveal(spec, state, block, proposer_index=None):
block.body.randao_reveal = bls.Sign(privkey, signing_root) 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. # Fully ignore the function if BLS is off, beacon-proposer index calculation is slow.
@only_with_bls() @only_with_bls()
def apply_sig(spec, state, signed_block, proposer_index=None): 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.body.eth1_data.deposit_count = state.eth1_deposit_index
empty_block.parent_root = parent_block_root empty_block.parent_root = parent_block_root
apply_randao_reveal(spec, state, empty_block)
if is_post_altair(spec): if is_post_altair(spec):
empty_block.body.sync_aggregate.sync_committee_signature = spec.G2_POINT_AT_INFINITY empty_block.body.sync_aggregate.sync_committee_signature = spec.G2_POINT_AT_INFINITY
if is_post_merge(spec): 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 return empty_block

View File

@ -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. 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, timestamp=timestamp,
receipt_root=b"no receipts here" + b"\x00" * 16, # TODO: root of empty MPT may be better. 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? logs_bloom=spec.ByteVector[spec.BYTES_PER_LOGS_BLOOM](), # TODO: zeroed logs bloom for empty logs ok?
randao=randao_mix,
transactions=empty_txs, transactions=empty_txs,
) )
# TODO: real RLP + block hash logic would be nice, requires RLP and keccak256 dependency however. # 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 return payload
def get_execution_payload_header(spec, execution_payload): def get_execution_payload_header(spec, execution_payload):
return spec.ExecutionPayloadHeader( return spec.ExecutionPayloadHeader(
block_hash=execution_payload.block_hash, block_hash=execution_payload.block_hash,
@ -37,6 +37,7 @@ def get_execution_payload_header(spec, execution_payload):
timestamp=execution_payload.timestamp, timestamp=execution_payload.timestamp,
receipt_root=execution_payload.receipt_root, receipt_root=execution_payload.receipt_root,
logs_bloom=execution_payload.logs_bloom, logs_bloom=execution_payload.logs_bloom,
randao=execution_payload.randao,
transactions_root=spec.hash_tree_root(execution_payload.transactions) 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): 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) payload_header = get_execution_payload_header(spec, pre_state_payload)
return build_state_with_execution_payload_header(spec, state, payload_header) return build_state_with_execution_payload_header(spec, state, payload_header)

View File

@ -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.context import spec_state_test, expect_assertion_error, with_merge_and_later
from eth2spec.test.helpers.state import next_slot from eth2spec.test.helpers.state import next_slot
def run_execution_payload_processing(spec, state, execution_payload, randao_mix, valid=True, execution_valid=True):
def run_execution_payload_processing(spec, state, execution_payload, valid=True, execution_valid=True):
""" """
Run ``process_execution_payload``, yielding: Run ``process_execution_payload``, yielding:
- pre-state ('pre') - pre-state ('pre')
@ -32,11 +31,11 @@ def run_execution_payload_processing(spec, state, execution_payload, valid=True,
return execution_valid return execution_valid
if not 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 yield 'post', None
return 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 # Make sure we called the engine
assert called_new_block assert called_new_block
@ -54,9 +53,9 @@ def test_success_first_payload(spec, state):
next_slot(spec, state) next_slot(spec, state)
# execution payload # 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 @with_merge_and_later
@ -67,9 +66,9 @@ def test_success_regular_payload(spec, state):
next_slot(spec, state) next_slot(spec, state)
# execution payload # 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 @with_merge_and_later
@ -81,9 +80,9 @@ def test_success_first_payload_with_gap_slot(spec, state):
next_slot(spec, state) next_slot(spec, state)
# execution payload # 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 @with_merge_and_later
@ -95,9 +94,9 @@ def test_success_regular_payload_with_gap_slot(spec, state):
next_slot(spec, state) next_slot(spec, state)
# execution payload # 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 @with_merge_and_later
@ -110,9 +109,9 @@ def test_bad_execution_first_payload(spec, state):
next_slot(spec, state) next_slot(spec, state)
# execution payload # 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 @with_merge_and_later
@ -125,9 +124,9 @@ def test_bad_execution_regular_payload(spec, state):
next_slot(spec, state) next_slot(spec, state)
# execution payload # 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 @with_merge_and_later
@ -138,10 +137,10 @@ def test_bad_parent_hash_regular_payload(spec, state):
next_slot(spec, state) next_slot(spec, state)
# execution payload # 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.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 @with_merge_and_later
@ -152,10 +151,10 @@ def test_bad_number_regular_payload(spec, state):
next_slot(spec, state) next_slot(spec, state)
# execution payload # 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 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 @with_merge_and_later
@ -166,11 +165,11 @@ def test_bad_everything_regular_payload(spec, state):
next_slot(spec, state) next_slot(spec, state)
# execution payload # 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.parent_hash = spec.Hash32()
execution_payload.number = execution_payload.number + 1 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 @with_merge_and_later
@ -181,10 +180,10 @@ def test_bad_timestamp_first_payload(spec, state):
next_slot(spec, state) next_slot(spec, state)
# execution payload # 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 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 @with_merge_and_later
@ -195,7 +194,7 @@ def test_bad_timestamp_regular_payload(spec, state):
next_slot(spec, state) next_slot(spec, state)
# execution payload # 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 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)