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

Simplify spec, update based on recent discussions #82

Merged
merged 11 commits into from
Apr 8, 2022
252 changes: 153 additions & 99 deletions docs/specification.md
Original file line number Diff line number Diff line change
@@ -1,153 +1,207 @@
# Version 0.1 (current, milestone 1, running on [Kiln testnet](https://kiln.themerge.dev/))
# Version 0.2

This initial milestone provides simple sidecar logic with minimal consensus client changes, simple networking, no validator authentication, and manual safety mechanism.

1. _mev-boost_ is initialized with a list of `RelayURI`s from trusted relays.

2. _mev-boost_ receives a [`engine_forkchoiceUpdatedV1`](#engine_forkchoiceupdatedv1) call from a beacon node and forwards it to all connected relays to communicate `feeRecipient`.

3. _mev_boost_ receives [`builder_getPayloadHeaderV1`](#builder_getpayloadheaderv1) request from the beacon node, and forwards it to all relays as `relay_getPayloadHeaderV1`. _mev-boost_ must return the [`ExecutionPayloadHeaderV1`](https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/bellatrix/beacon-chain.md#executionpayloadheader) with the highest associated `feeRecipientDiff`.

4. The _beacon node_ must use the [`ExecutionPayloadHeaderV1`](https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/merge/beacon-chain.md#executionpayloadheader) received to assemble and sign a [`SignedBlindedBeaconBlock`](#signedblindedbeaconblock) and return it to _mev-boost_ using [`builder_proposeBlindedBlockV1`](#builder_proposeblindedblockv1).

5. _mev-boost_ must forward the [`SignedBlindedBeaconBlock`](#signedblindedbeaconblock) to all connected relays and attach the matching [`SignedMEVPayloadHeader`](#signedmevpayloadheader) using [`relay_proposeBlindedBlockV1`](#relay_proposeblindedblockv1) to inform the network of which relay created this payload.

6. If an [`ExecutionPayloadV1`](https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/merge/beacon-chain.md#executionpayload) is returned, _mev-boost_ must verify that the root of the transaction list matches the expected transaction root from the [`SignedBlindedBeaconBlock`](#signedblindedbeaconblock) before returning it to the _beacon node_.
This document specifies the Builder API methods that the Consensus Layer uses to interact with external block builders.

```mermaid
sequenceDiagram
participant consensus
participant mev_boost
participant relays
Title: Block Proposal
consensus-->+mev_boost: engine_forkchoiceUpdatedV1
mev_boost->>-relays: engine_forkchoiceUpdatedV1
Note over consensus: sign fee recipient announcement
consensus->>mev_boost: builder_setFeeRecipient
mev_boost->>relays: builder_setFeeRecipient
Note over consensus: wait for allocated slot
consensus->>+mev_boost: builder_getPayloadHeaderV1
mev_boost->>relays: relay_getPayloadHeaderV1
Note over mev_boost: select most valuable payload
mev_boost-->>-consensus: builder_getPayloadHeaderV1 response
consensus->>mev_boost: builder_getHeader
mev_boost->>relays: builder_getHeader
relays-->>mev_boost: builder_getHeader response
Note over mev_boost: verify response matches expected
Note over mev_boost: select best payload
mev_boost-->>consensus: builder_getHeader response
Note over consensus: sign the block
consensus->>+mev_boost: builder_proposeBlindedBlockV1
consensus->>mev_boost: builder_getPayload
Note over mev_boost: identify payload source
mev_boost->>relays: relay_proposeBlindedBlockV1
mev_boost->>relays: builder_getPayload
Note over relays: validate signature
relays-->>mev_boost: relay_proposeBlindedBlockV1 response
mev_boost-->>-consensus: builder_proposeBlindedBlockV1 response
relays-->>mev_boost: builder_getPayload response
Note over mev_boost: verify response matches expected
mev_boost-->>consensus: builder_getPayload response
```

## Required client modifications
## Structures

- consensus client must implement [blind transaction signing](https://hackmd.io/@paulhauner/H1XifIQ_t#Change-1-Blind-Transaction-Signing)
### `ExecutionPayloadV1`

## API
Mirror of [`ExecutionPayloadV1`][execution-payload].

Methods are prefixed using the following convention:
### `ExecutionPayloadHeaderV1`

- `engine` prefix indicates calls made to the execution client. These methods are specified in the [execution engine APIs](https://github.com/ethereum/execution-apis/blob/v1.0.0-alpha.8/src/engine/specification.md).
- `builder` prefix indicates calls made to the mev-boost sidecar.
- `relay` prefix indicates calls made to a relay.
Equivalent to `ExecutionPayloadV1`, except `transactions` is replaced with `transactionsRoot`.
- `parentHash`: `DATA`, 32 Bytes
- `feeRecipient`: `DATA`, 20 Bytes
- `stateRoot`: `DATA`, 32 Bytes
- `receiptsRoot`: `DATA`, 32 Bytes
- `logsBloom`: `DATA`, 256 Bytes
- `prevRandao`: `DATA`, 32 Bytes
- `blockNumber`: `QUANTITY`, 64 Bits
- `gasLimit`: `QUANTITY`, 64 Bits
- `gasUsed`: `QUANTITY`, 64 Bits
- `timestamp`: `QUANTITY`, 64 Bits
- `extraData`: `DATA`, 0 to 32 Bytes
- `baseFeePerGas`: `QUANTITY`, 256 Bits
- `blockHash`: `DATA`, 32 Bytes
- `transactionsRoot`: `DATA`, 32 Bytes

### engine_forkchoiceUpdatedV1
#### SSZ Objects

See [engine_forkchoiceUpdatedV1](https://github.com/ethereum/execution-apis/blob/v1.0.0-alpha.8/src/engine/specification.md#engine_forkchoiceupdatedv1).
Consider the following definitions supplementary to the definitions in [`consensus-specs`][consensus-specs].

### builder_proposeBlindedBlockV1
##### `builder_setFeeRecipientV1` Request

#### Request
```python
class Request(Container):
feeRecipient: Bytes20
timestamp: uint64
```
lightclient marked this conversation as resolved.
Show resolved Hide resolved

- method: `builder_proposeBlindedBlockV1`
- params:
1. [`SignedBlindedBeaconBlock`](#signedblindedbeaconblock)
##### `builder_getPayloadV1` Response

#### Response
```python
class Response(Container):
payload: ExecutionPayloadHeader
value: uint256
```

- result: [`ExecutionPayloadV1`](https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/bellatrix/beacon-chain.md#executionpayload).
- error: code and message set in case an exception happens while proposing the payload.
##### `builder_getPayloadV1` Request

Technically, this call only needs to return the `transactions` field of [`ExecutionPayloadV1`](https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/bellatrix/beacon-chain.md#executionpayload), but we return the full payload for simplicity.
###### `BlindBeaconBlock`

### builder_getPayloadHeaderV1
```python
class BlindBeaconBlock(Container):
slot: Slot
proposer_index: ValidatorIndex
parent_root: Root
state_root: Root
body: BlindBeaconBlockBody
```

#### Request
###### `BlindBeaconBlockBody`

```python
class BlindBeaconBlockBody(Container):
randao_reveal: BLSSignature
eth1_data: Eth1Data
graffiti: Bytes32
proposer_slashings: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS]
attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS]
attestations: List[Attestation, MAX_ATTESTATIONS]
deposits: List[Deposit, MAX_DEPOSITS]
voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS]
sync_aggregate: SyncAggregate
execution_payload: ExecutionPayloadHeader
```

- method: `builder_getPayloadHeaderV1`
- params:
1. `payloadId`: `DATA`, 8 Bytes - Identifier of the payload build process
## Errors

#### Response
The list of error codes introduced by this specification can be found below.

- result: [`ExecutionPayloadHeaderV1`](https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/bellatrix/beacon-chain.md#executionpayloadheader)
- error: code and message set in case an exception happens while getting the payload.
| Code | Message | Meaning |
| - | - | - |
| -32000 | Server error | Generic client error while processing request. |
| -32001 | Unknown hash | No block with the provided hash is known. |
| -32002 | Unknown validator | No known mapping between validator and feeRecipient. |
| -32003 | Invalid SSZ | Unable to decode SSZ. |
| -32004 | Unknown block | Block does not match the provided header. |
| -32005 | Invalid signature | Provided signature is invalid. |
| -32006 | Invalid timestamp | Provided timestamp was invalid. |
| -32600 | Invalid request | The JSON sent is not a valid Request object. |
| -32601 | Method not found | The method does not exist / is not available. |
| -32602 | Invalid params | Invalid method parameter(s). |
| -32603 | Internal error | Internal JSON-RPC error. |
| -32700 | Parse error | Invalid JSON was received by the server. |

### relay_getPayloadHeaderV1
## Routines

#### Request
### Signing

- method: `relay_getPayloadHeaderV1`
- params:
1. `payloadId`: `DATA`, 8 Bytes - Identifier of the payload build process
All signature operations should follow the [standard BLS operations][bls] interface defined in `consensus-specs`.

#### Response
There are two types of data to sign over in the Builder API:
* In-protocol messages, e.g. [`BlindBeaconBlock`](#blindbeaconblock), which should compute the signing root using [`computer_signing_root`][compute-signing-root] and use the domain specified for beacon block proposals.
* Builder API messages, e.g. [`builder_setFeeRecipientV1`](#builder_setFeeRecipientV1) and the response to [`builder_getHeader`](#response-2), which should compute the signing root using [`compute_signing_root`][compute-signing-root] and the domain `DomainType('0xXXXXXXXX')` (TODO: get a proper domain).

- result: [`SignedMEVPayloadHeader`](#signedmevpayloadheader)
- error: code and message set in case an exception happens while getting the payload.
As `compute_signing_root` takes `SSZObject` as input, client software should convert in-protocol messages to their SSZ representation to compute the signing root and Builder API messages to the SSZ representations defined [above](#sszobjects).

## Methods

### relay_proposeBlindedBlockV1
### `builder_setFeeRecipientV1`

#### Request

- method: `relay_proposeBlindedBlockV1`
- method: `builder_setFeeRecipientV1`
- params:
1. [`SignedBlindedBeaconBlock`](#signedblindedbeaconblock)
2. [`SignedMEVPayloadHeader`](#signedmevpayloadheader)
1. `feeRecipient`: `DATA`, 20 Bytes - Address of account which should receive fees.
2. `timestamp`: `QUANTITY`, uint64 - Unix timestamp of announcement.
2. `publicKey`: `DATA`, 48 Bytes - Public key of validator.
3. `signature`: `DATA`, 96 Bytes - Signature over `feeRecipient` and `timestamp`.

#### Response

- result: [`ExecutionPayloadV1`](https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/bellatrix/beacon-chain.md#executionpayload)
- error: code and message set in case an exception happens while proposing the payload.

Technically, this call only needs to return the `transactions` field of [`ExecutionPayloadV1`](https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/bellatrix/beacon-chain.md#executionpayload), but we return the full payload for simplicity.

### Types

#### SignedMEVPayloadHeader
- result: `null`
- error: code and message set in case an exception happens while getting the payload.

See [here](https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#custom-types) for the definition of fields like `BLSSignature`
#### Specification
1. Builder software **MUST** verify `signature` is valid under `publicKey`.
2. Builder software **MUST** respond to requests where `timestamp` is before the latest announcement from the validator or more than +/- 10 seconds from the current time with `-32006: Invalid timestamp`.
3. Builder software **MUST** store `feeRecipient` in a map keyed by `publicKey`.

- message: [MEVPayloadHeader](#mevpayloadheader)
- signature: BLSSignature
### `builder_getHeaderV1`

#### MEVPayloadHeader
#### Request

- payloadHeader: [`ExecutionPayloadHeaderV1`](https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/bellatrix/beacon-chain.md#executionpayloadheader)
- feeRecipientDiff: Quantity, 256 Bits - the change in balance of the feeRecipient address
- method: `builder_getHeaderV1`
- params:
1. `hash`: `DATA`, 32 Bytes - Hash of block which the validator intends to use as the parent for its proposal.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should the validator public key be part of the request params or else how does the builder return Unknown validator if the validator public key has not been mapped to a feeRecipient?

Copy link
Collaborator Author

@lightclient lightclient Apr 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My expectation was that the builder would know what validator is planning to propose for the current slot and would return Unknown validator if it had not received a setFeeRecipient message from the validator. I will update the wording here, because "recovered" doesn't make much sense in this context.

This does make me wonder if the builder should also return the slot it is building for in case there is a mismatch?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There could skip slots on top of hash

hash -> request/propose
hash -> skipped_slot -> request/propoase
hash -> skipped_slot -> skipped_slot -> request/propose

The builder may utilize the current wall block but if there's a tiny clock synchronization mismatch between the BN and builder then it may fall apart. It may be safer to include an additional slot or proposer_index or pub_key in the request as an additional anchor point

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@terencechain thanks for the brainstorming on this. the PR is now merged, please open an issue/PR to continue the thread, if applicable 🙏


#### SignedBlindedBeaconBlock
#### Response

See [here](https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#custom-types) for the definition of fields like `BLSSignature`
- result: `object`
- `header`: [`ExecutionPayloadHeaderV1`](#executionpayloadheaderv1).
- `value`: `DATA`, 32 Bytes - the change in wei balance of the `feeRecipient` account.
- `publicKey`: `DATA`, 48 Bytes - the public key associated with the builder.
- `signature`: `DATA`, 96 Bytes - BLS signature of the builder over `payload` and `value`.
- error: code and message set in case an exception happens while getting the payload.

- message: [BlindedBeaconBlock](#blindedbeaconblock)
- signature: BLSSignature
#### Specification
1. Builder software **SHOULD** respond immediately with the `header` that increases the `feeRecipient`'s balance by the most.
2. Builder software **MUST** return `-32001: Unknown hash` if the block identified by `hash` does not exist.
3. Builder software **MUST** return `-32002: Unknown validator` if the validator the builder expects to propose in the current slot has not been mapped to a `feeRecipient`.

#### BlindedBeaconBlock
### `builder_getPayloadV1`

This is forked from [here](https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#beaconblock) with `body` replaced with `BlindedBeaconBlockBody`
#### Request

- slot: Slot
- proposer_index: ValidatorIndex
- parent_root: Root
- state_root: Root
- body: BlindedBeaconBlockBody
- method: `builder_getPayloadV1`
- params:
1. `block`: `DATA`, arbitray length - SSZ encoded [`BlindBeaconBlock`](#blindbeaconblock).
2. `signature`: `DATA`, 96 Bytes - BLS signature of the validator over `block`.
Comment on lines +186 to +187
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option is to pass in SignedBlindBeaconBlock which is basically 1. + 2.

Have we considered adding publicKey as an input? We don't need it if the builder has the beacon state and the ability to look up the block. proposer_index's public key

Copy link
Collaborator Author

@lightclient lightclient Apr 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned in this comment, I am making the assumption that builders have the ability to look up the block.proposer_index's public key. I think this is a reasonable and important assumption, because without it, the builder may receive may builder_getHeader requests with different feeRecipients, which will cause them to calculate blocks that have no possibility of being included.

I'm not necessarily opposed to passing SignedBlindBeaconBlock - the rationale for not doing so currently is it breaks from the current pattern of signature always being an RPC parameter / response and I'm not sure what the value would be in disrupting that pattern.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries. I think block and signature are great. Let's see if others have feedback on this


#### BlindedBeaconBlockBody
#### Response

This is forked from [here](https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/merge/beacon-chain.md#beaconblockbody) with `execution_payload` replaced with [execution_payload_header](https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/bellatrix/beacon-chain.md#executionpayloadheader)
- result: [`ExecutionPayloadV1`](#executionpayloadv1).
- error: code and message set in case an exception happens while proposing the payload.

- randao_reveal: BLSSignature
- eth1_data: Eth1Data
- graffiti: Bytes32
- proposer_slashings: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS]
- attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS]
- attestations: List[Attestation, MAX_ATTESTATIONS]
- deposits: List[Deposit, MAX_DEPOSITS]
- voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS]
- sync_aggregate: SyncAggregate
- execution_payload_header: ExecutionPayloadHeader
#### Specification
1. Builder software **MUST** verify that `block` is an SSZ encoded [`BlindBeaconBlock`](#blindbeaconblock). If the block is encoded incorrectly, the builder **MUST** return `-32003: Invalid SSZ`. If the block is encoded correctly, but does not match the `ExecutionPayloadHeaderV1` provided in `builder_getExecutionPayloadHeaderV1`, the builder **SHOULD** return `-32004: Unknown block`. If the CL modifies the payload in such a way that it is still valid and the builder is able to unblind it, the builder **MAY** update the payload on it's end to reflect the CL's changes before returning it.
2. Builder software **MUST** verify that `signature` is a BLS signature over `block`, verifiable using [`verify_block_signature`][verify-block-signature] and the validator public key that is expected to propose in the given slot. If the signature is determined to be invalid or from a different validator than expected, the builder **MUST** return `-32005: Invalid signature`.

[consensus-specs]: https://github.com/ethereum/consensus-specs
[bls]: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#bls-signatures
[compute-signing-root]: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#compute_signing_root
[bytes20]: https://github.com/ethereum/consensus-specs/blob/dev/ssz/simple-serialize.md#aliases
[uint256]: https://github.com/ethereum/consensus-specs/blob/dev/ssz/simple-serialize.md#basic-types
[execution-payload-header]: https://github.com/ethereum/consensus-specs/blob/dev/specs/bellatrix/beacon-chain.md#executionpayloadheader
[execution-payload]: https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md#executionpayloadv1
[hash-tree-root]: https://github.com/ethereum/consensus-specs/blob/dev/ssz/simple-serialize.md#merkleization
[beacon-block]: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconblock
[verify-block-signature]: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beacon-chain-state-transition-function