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 4 commits
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
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ def set_head(self, block_hash: Hash32) -> bool:
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, randao: Bytes32) -> ExecutionPayload:
raise NotImplementedError("no default block production")


Expand Down
14 changes: 11 additions & 3 deletions 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 @@ -196,9 +198,10 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None:
process_randao(state, block.body)
process_eth1_data(state, block.body)
process_operations(state, block.body)
# Pre-merge, skip execution payload processing
# [New in Merge] 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]
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 +211,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 +236,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 All @@ -245,6 +251,7 @@ This helper function is only for initializing the state for pure Merge testnets
def initialize_beacon_state_from_eth1(eth1_block_hash: Bytes32,
eth1_timestamp: uint64,
deposits: Sequence[Deposit]) -> BeaconState:
randao_seed = eth1_block_hash
fork = Fork(
previous_version=GENESIS_FORK_VERSION,
current_version=MERGE_FORK_VERSION, # [Modified in Merge]
Expand All @@ -255,7 +262,7 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Bytes32,
fork=fork,
eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=uint64(len(deposits))),
latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())),
randao_mixes=[eth1_block_hash] * EPOCHS_PER_HISTORICAL_VECTOR, # Seed RANDAO with Eth1 entropy
randao_mixes=[randao_seed] * EPOCHS_PER_HISTORICAL_VECTOR, # Seed RANDAO with Eth1 entropy
)

# Process deposits
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=randao_seed,
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, 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)
```
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
10 changes: 8 additions & 2 deletions tests/core/pyspec/eth2spec/test/helpers/execution_payload.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
def build_empty_execution_payload(spec, state):
def build_empty_execution_payload_with_randao(spec, state):
return build_empty_execution_payload(spec, state, spec.Bytes32())
mkalinin marked this conversation as resolved.
Show resolved Hide resolved


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,6 +21,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.
Expand All @@ -37,6 +42,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 +52,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
@@ -1,5 +1,5 @@
from eth2spec.test.helpers.execution_payload import (
build_empty_execution_payload,
build_empty_execution_payload_with_randao,
get_execution_payload_header,
build_state_with_incomplete_transition,
build_state_with_complete_transition,
Expand All @@ -22,6 +22,7 @@ def run_execution_payload_processing(spec, state, execution_payload, valid=True,
yield 'execution', {'execution_valid': execution_valid}
yield 'execution_payload', execution_payload

randao_mix = spec.Bytes32()
called_new_block = False

class TestEngine(spec.NoopExecutionEngine):
Expand All @@ -32,11 +33,12 @@ 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,7 +56,7 @@ 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_with_randao(spec, state)

yield from run_execution_payload_processing(spec, state, execution_payload)

Expand All @@ -67,7 +69,7 @@ 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_with_randao(spec, state)

yield from run_execution_payload_processing(spec, state, execution_payload)

Expand All @@ -81,7 +83,7 @@ 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_with_randao(spec, state)

yield from run_execution_payload_processing(spec, state, execution_payload)

Expand All @@ -95,7 +97,7 @@ 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_with_randao(spec, state)

yield from run_execution_payload_processing(spec, state, execution_payload)

Expand All @@ -110,7 +112,7 @@ 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_with_randao(spec, state)

yield from run_execution_payload_processing(spec, state, execution_payload, valid=False, execution_valid=False)

Expand All @@ -125,7 +127,7 @@ 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_with_randao(spec, state)

yield from run_execution_payload_processing(spec, state, execution_payload, valid=False, execution_valid=False)

Expand All @@ -138,7 +140,7 @@ 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_with_randao(spec, state)
execution_payload.parent_hash = spec.Hash32()

yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)
Expand All @@ -152,7 +154,7 @@ 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_with_randao(spec, state)
execution_payload.number = execution_payload.number + 1

yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)
Expand All @@ -166,7 +168,7 @@ 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_with_randao(spec, state)
execution_payload.parent_hash = spec.Hash32()
execution_payload.number = execution_payload.number + 1

Expand All @@ -181,7 +183,7 @@ 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_with_randao(spec, state)
execution_payload.timestamp = execution_payload.timestamp + 1

yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)
Expand All @@ -195,7 +197,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_with_randao(spec, state)
execution_payload.timestamp = execution_payload.timestamp + 1

yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)