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
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(),
)

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.
```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)
```

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)
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

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.
"""
@ -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)

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.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)