Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
djrtwo committed Nov 8, 2021
2 parents e45d016 + b963f7c commit 58b291b
Show file tree
Hide file tree
Showing 11 changed files with 258 additions and 198 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ def execute_payload(self: ExecutionEngine, execution_payload: ExecutionPayload)
def notify_forkchoice_updated(self: ExecutionEngine,
head_block_hash: Hash32,
finalized_block_hash: Hash32,
payload_attributes: Optional[PayloadAttributes]) -> None:
payload_attributes: Optional[PayloadAttributes]) -> Optional[PayloadId]:
pass
def get_payload(self: ExecutionEngine, payload_id: PayloadId) -> ExecutionPayload:
Expand Down
14 changes: 11 additions & 3 deletions specs/merge/fork-choice.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

- [Introduction](#introduction)
- [Custom types](#custom-types)
- [Protocols](#protocols)
- [`ExecutionEngine`](#executionengine)
- [`notify_forkchoice_updated`](#notify_forkchoice_updated)
Expand All @@ -29,6 +30,12 @@ This is the modification of the fork choice according to the executable beacon c

*Note*: It introduces the process of transition from the last PoW block to the first PoS block.

## Custom types

| Name | SSZ equivalent | Description |
| - | - | - |
| `PayloadId` | `Bytes8` | Identifier of a payload building process |

## Protocols

### `ExecutionEngine`
Expand All @@ -46,13 +53,13 @@ This function performs two actions *atomically*:
and corresponding state, up to and including `finalized_block_hash`.

Additionally, if `payload_attributes` is provided, this function sets in motion a payload build process on top of
`head_block_hash` with the result to be gathered by a followup call to `get_payload`.
`head_block_hash` and returns an identifier of initiated process.

```python
def notify_forkchoice_updated(self: ExecutionEngine,
head_block_hash: Hash32,
finalized_block_hash: Hash32,
payload_attributes: Optional[PayloadAttributes]) -> None:
payload_attributes: Optional[PayloadAttributes]) -> Optional[PayloadId]:
...
```

Expand Down Expand Up @@ -115,7 +122,8 @@ def validate_merge_block(block: BeaconBlock) -> None:
if TERMINAL_BLOCK_HASH != Hash32():
# If `TERMINAL_BLOCK_HASH` is used as an override, the activation epoch must be reached.
assert compute_epoch_at_slot(block.slot) >= TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH
return block.block_hash == TERMINAL_BLOCK_HASH
assert block.body.execution_payload.parent_hash == TERMINAL_BLOCK_HASH
return

pow_block = get_pow_block(block.body.execution_payload.parent_hash)
# Check if `pow_block` is available
Expand Down
33 changes: 3 additions & 30 deletions specs/merge/validator.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@

- [Introduction](#introduction)
- [Prerequisites](#prerequisites)
- [Custom types](#custom-types)
- [Helpers](#helpers)
- [`get_pow_block_at_terminal_total_difficulty`](#get_pow_block_at_terminal_total_difficulty)
- [`get_terminal_pow_block`](#get_terminal_pow_block)
- [`get_payload_id`](#get_payload_id)
- [Protocols](#protocols)
- [`ExecutionEngine`](#executionengine)
- [`get_payload`](#get_payload)
Expand All @@ -38,12 +36,6 @@ All behaviors and definitions defined in this document, and documents it extends
All terminology, constants, functions, and protocol mechanics defined in the updated Beacon Chain doc of [The Merge](./beacon-chain.md) are requisite for this document and used throughout.
Please see related Beacon Chain doc before continuing and use them as a reference throughout.

## Custom types

| Name | SSZ equivalent | Description |
| - | - | - |
| `PayloadId` | `Bytes8` | Identifier of a payload building process |

## Helpers

### `get_pow_block_at_terminal_total_difficulty`
Expand Down Expand Up @@ -75,24 +67,6 @@ def get_terminal_pow_block(pow_chain: Dict[Hash32, PowBlock]) -> Optional[PowBlo
return get_pow_block_at_terminal_total_difficulty(pow_chain)
```

### `get_payload_id`

Given the `head_block_hash` and the `payload_attributes` that were used to
initiate the build process via `notify_forkchoice_updated`, `get_payload_id()`
returns the `payload_id` used to retrieve the payload via `get_payload`.

```python
def get_payload_id(parent_hash: Hash32, payload_attributes: PayloadAttributes) -> PayloadId:
return PayloadId(
hash(
parent_hash
+ uint_to_bytes(payload_attributes.timestamp)
+ payload_attributes.random
+ payload_attributes.fee_recipient
)[0:8]
)
```

*Note*: This function does *not* use simple serialize `hash_tree_root` as to
avoid requiring simple serialize hashing capabilities in the Execution Layer.

Expand Down Expand Up @@ -147,8 +121,8 @@ def prepare_execution_payload(state: BeaconState,
execution_engine: ExecutionEngine) -> Optional[PayloadId]:
if not is_merge_complete(state):
is_terminal_block_hash_set = TERMINAL_BLOCK_HASH != Hash32()
is_activation_epoch_reached = get_current_epoch(state.slot) < TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH
if is_terminal_block_hash_set and is_activation_epoch_reached:
is_activation_epoch_reached = get_current_epoch(state.slot) >= TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH
if is_terminal_block_hash_set and not is_activation_epoch_reached:
# Terminal block hash is set but activation epoch is not yet reached, no prepare payload call is needed
return None

Expand All @@ -168,8 +142,7 @@ def prepare_execution_payload(state: BeaconState,
random=get_randao_mix(state, get_current_epoch(state)),
fee_recipient=fee_recipient,
)
execution_engine.notify_forkchoice_updated(parent_hash, finalized_block_hash, payload_attributes)
return get_payload_id(parent_hash, payload_attributes)
return execution_engine.notify_forkchoice_updated(parent_hash, finalized_block_hash, payload_attributes)
```

2. Set `block.body.execution_payload = get_execution_payload(payload_id, execution_engine)`, where:
Expand Down
2 changes: 1 addition & 1 deletion specs/phase0/validator.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ Specifically a validator should:

## Beacon chain responsibilities

A validator has two primary responsibilities to the beacon chain: [proposing blocks](#block-proposal) and [creating attestations](#attestations-1). Proposals happen infrequently, whereas attestations should be created once per epoch.
A validator has two primary responsibilities to the beacon chain: [proposing blocks](#block-proposal) and [creating attestations](#attesting). Proposals happen infrequently, whereas attestations should be created once per epoch.

### Block proposal

Expand Down
2 changes: 1 addition & 1 deletion tests/core/pyspec/eth2spec/VERSION.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.1.4
1.1.5
11 changes: 0 additions & 11 deletions tests/core/pyspec/eth2spec/test/helpers/fork_choice.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from random import Random
from eth_utils import encode_hex
from eth2spec.test.exceptions import BlockNotFoundException
from eth2spec.utils.ssz.ssz_typing import uint256
from eth2spec.test.helpers.attestations import (
next_epoch_with_attestations,
next_slots_with_attestations,
Expand Down Expand Up @@ -247,15 +245,6 @@ def apply_next_slots_with_attestations(spec,
return post_state, store, last_signed_block


def prepare_empty_pow_block(spec, rng=Random(3131)):
return spec.PowBlock(
block_hash=spec.Hash32(spec.hash(bytearray(rng.getrandbits(8) for _ in range(32)))),
parent_hash=spec.Hash32(spec.hash(bytearray(rng.getrandbits(8) for _ in range(32)))),
total_difficulty=uint256(0),
difficulty=uint256(0)
)


def get_pow_block_file_name(pow_block):
return f"pow_block_{encode_hex(pow_block.block_hash)}"

Expand Down
34 changes: 34 additions & 0 deletions tests/core/pyspec/eth2spec/test/helpers/pow_block.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from random import Random
from eth2spec.utils.ssz.ssz_typing import uint256


class PowChain:
blocks = []

def __init__(self, blocks):
self.blocks = blocks

def __iter__(self):
return iter(self.blocks)

def head(self, offset=0):
assert offset <= 0
return self.blocks[offset - 1]


def prepare_random_pow_block(spec, rng=Random(3131)):
return spec.PowBlock(
block_hash=spec.Hash32(spec.hash(bytearray(rng.getrandbits(8) for _ in range(32)))),
parent_hash=spec.Hash32(spec.hash(bytearray(rng.getrandbits(8) for _ in range(32)))),
total_difficulty=uint256(0),
difficulty=uint256(0)
)


def prepare_random_pow_chain(spec, length, rng=Random(3131)) -> PowChain:
assert length > 0
chain = [prepare_random_pow_block(spec, rng)]
for i in range(1, length):
chain.append(prepare_random_pow_block(spec, rng))
chain[i].parent_hash = chain[i - 1].block_hash
return PowChain(chain)
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
state_transition_and_sign_block,
)
from eth2spec.test.helpers.fork_choice import (
prepare_empty_pow_block,
add_pow_block,
)
from eth2spec.test.helpers.pow_block import (
prepare_random_pow_block,
)
from eth2spec.test.helpers.execution_payload import (
build_state_with_incomplete_transition,
)
Expand Down Expand Up @@ -58,9 +60,9 @@ def test_all_valid(spec, state):
on_tick_and_append_step(spec, store, current_time, test_steps)
assert store.time == current_time

pow_block_parent = prepare_empty_pow_block(spec)
pow_block_parent = prepare_random_pow_block(spec)
pow_block_parent.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
pow_block = prepare_empty_pow_block(spec)
pow_block = prepare_random_pow_block(spec)
pow_block.parent_hash = pow_block_parent.block_hash
pow_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
pow_blocks = [pow_block, pow_block_parent]
Expand Down Expand Up @@ -92,7 +94,7 @@ def test_block_lookup_failed(spec, state):
on_tick_and_append_step(spec, store, current_time, test_steps)
assert store.time == current_time

pow_block = prepare_empty_pow_block(spec)
pow_block = prepare_random_pow_block(spec)
pow_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
pow_blocks = [pow_block]
for pb in pow_blocks:
Expand Down Expand Up @@ -122,9 +124,9 @@ def test_too_early_for_merge(spec, state):
on_tick_and_append_step(spec, store, current_time, test_steps)
assert store.time == current_time

pow_block_parent = prepare_empty_pow_block(spec)
pow_block_parent = prepare_random_pow_block(spec)
pow_block_parent.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(2)
pow_block = prepare_empty_pow_block(spec)
pow_block = prepare_random_pow_block(spec)
pow_block.parent_hash = pow_block_parent.block_hash
pow_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
pow_blocks = [pow_block, pow_block_parent]
Expand Down Expand Up @@ -154,9 +156,9 @@ def test_too_late_for_merge(spec, state):
on_tick_and_append_step(spec, store, current_time, test_steps)
assert store.time == current_time

pow_block_parent = prepare_empty_pow_block(spec)
pow_block_parent = prepare_random_pow_block(spec)
pow_block_parent.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
pow_block = prepare_empty_pow_block(spec)
pow_block = prepare_random_pow_block(spec)
pow_block.parent_hash = pow_block_parent.block_hash
pow_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + uint256(1)
pow_blocks = [pow_block, pow_block_parent]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from eth2spec.utils.ssz.ssz_typing import uint256
from eth2spec.test.helpers.pow_block import (
prepare_random_pow_block,
)
from eth2spec.test.context import (
spec_state_test,
with_merge_and_later,
)


@with_merge_and_later
@spec_state_test
def test_is_valid_terminal_pow_block_success_valid(spec, state):
parent_block = prepare_random_pow_block(spec)
parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
block = prepare_random_pow_block(spec)
block.parent_hash = parent_block.block_hash
block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY

assert spec.is_valid_terminal_pow_block(block, parent_block)


@with_merge_and_later
@spec_state_test
def test_is_valid_terminal_pow_block_fail_before_terminal(spec, state):
parent_block = prepare_random_pow_block(spec)
parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(2)
block = prepare_random_pow_block(spec)
block.parent_hash = parent_block.block_hash
block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)

assert not spec.is_valid_terminal_pow_block(block, parent_block)


@with_merge_and_later
@spec_state_test
def test_is_valid_terminal_pow_block_fail_just_after_terminal(spec, state):
parent_block = prepare_random_pow_block(spec)
parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
block = prepare_random_pow_block(spec)
block.parent_hash = parent_block.block_hash
block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + uint256(1)

assert not spec.is_valid_terminal_pow_block(block, parent_block)
Loading

0 comments on commit 58b291b

Please sign in to comment.