Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add randao to execution payload #2479

Merged
merged 8 commits into from
Jun 23, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion specs/merge/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -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]
```

Expand All @@ -126,6 +127,7 @@ class ExecutionPayloadHeader(Container):
timestamp: uint64
receipt_root: Bytes32
logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM]
randao: Bytes32
mkalinin marked this conversation as resolved.
Show resolved Hide resolved
transactions_root: Root
```

Expand Down Expand Up @@ -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]
mkalinin marked this conversation as resolved.
Show resolved Hide resolved
randao_mix = get_randao_mix(state, get_current_epoch(state))
mkalinin marked this conversation as resolved.
Show resolved Hide resolved
process_execution_payload(state, block.body.execution_payload, randao_mix, EXECUTION_ENGINE)
```

#### Execution payload processing
Expand All @@ -208,13 +212,15 @@ 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
"""
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)

Expand All @@ -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,
djrtwo marked this conversation as resolved.
Show resolved Hide resolved
transactions_root=hash_tree_root(execution_payload.transactions),
)
```
Expand Down Expand Up @@ -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,
mkalinin marked this conversation as resolved.
Show resolved Hide resolved
transactions_root=Root(),
)

Expand Down
25 changes: 19 additions & 6 deletions specs/merge/validator.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
...
```

Expand All @@ -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()
Expand All @@ -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)
mkalinin marked this conversation as resolved.
Show resolved Hide resolved
```
11 changes: 9 additions & 2 deletions tests/core/pyspec/eth2spec/test/helpers/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
mkalinin marked this conversation as resolved.
Show resolved Hide resolved
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):
Expand Down Expand Up @@ -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


Expand Down
7 changes: 4 additions & 3 deletions tests/core/pyspec/eth2spec/test/helpers/execution_payload.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
def build_empty_execution_payload(spec, state):
def build_empty_execution_payload(spec, state, randao_mix):
mkalinin marked this conversation as resolved.
Show resolved Hide resolved
"""
Assuming a pre-state of the same slot, build a valid ExecutionPayload without any transactions.
"""
Expand All @@ -17,14 +17,14 @@ 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.
payload.block_hash = spec.Hash32(spec.hash(payload.hash_tree_root() + b"FAKE RLP HASH"))

return payload


def get_execution_payload_header(spec, execution_payload):
return spec.ExecutionPayloadHeader(
block_hash=execution_payload.block_hash,
Expand All @@ -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)
)

Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
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')
Expand All @@ -32,11 +31,11 @@ def new_block(self, payload) -> bool:
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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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)