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

feat(net): ress subprotocol #14687

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
27 changes: 27 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ members = [
"crates/net/network/",
"crates/net/p2p/",
"crates/net/peers/",
"crates/net/ress/",
"crates/node/api/",
"crates/node/builder/",
"crates/node/core/",
Expand Down Expand Up @@ -529,6 +530,7 @@ sha2 = { version = "0.10", default-features = false }
shellexpand = "3.0.0"
smallvec = "1"
strum = { version = "0.27", default-features = false }
strum_macros = "0.27"
syn = "2.0"
thiserror = { version = "2.0.0", default-features = false }
tracing = "0.1.0"
Expand Down
8 changes: 4 additions & 4 deletions crates/net/network/src/eth_requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,21 @@ use tokio_stream::wrappers::ReceiverStream;
/// Maximum number of receipts to serve.
///
/// Used to limit lookups.
const MAX_RECEIPTS_SERVE: usize = 1024;
pub const MAX_RECEIPTS_SERVE: usize = 1024;

/// Maximum number of block headers to serve.
///
/// Used to limit lookups.
const MAX_HEADERS_SERVE: usize = 1024;
pub const MAX_HEADERS_SERVE: usize = 1024;

/// Maximum number of block headers to serve.
///
/// Used to limit lookups. With 24KB block sizes nowadays, the practical limit will always be
/// `SOFT_RESPONSE_LIMIT`.
const MAX_BODIES_SERVE: usize = 1024;
pub const MAX_BODIES_SERVE: usize = 1024;

/// Maximum size of replies to data retrievals: 2MB
const SOFT_RESPONSE_LIMIT: usize = 2 * 1024 * 1024;
pub const SOFT_RESPONSE_LIMIT: usize = 2 * 1024 * 1024;

/// Manages eth related requests on top of the p2p network.
///
Expand Down
57 changes: 57 additions & 0 deletions crates/net/ress/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
[package]
name = "reth-network-ress"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true

[lints]
workspace = true

[dependencies]
# reth
reth-eth-wire.workspace = true
reth-network-api.workspace = true
reth-network.workspace = true
reth-storage-errors.workspace = true
reth-primitives.workspace = true

# alloy
alloy-primitives.workspace = true
alloy-rlp.workspace = true

# misc
futures.workspace = true
tokio = { workspace = true, features = ["sync"] }
tokio-stream.workspace = true
tracing.workspace = true

# feature `test-utils`
parking_lot = { workspace = true, optional = true }

# feature `arbitrary`
arbitrary = { workspace = true, features = ["derive"], optional = true }

[dev-dependencies]
reth-eth-wire = { workspace = true, features = ["arbitrary"] }
reth-network = { workspace = true, features = ["test-utils"] }
reth-provider = { workspace = true, features = ["test-utils"] }
reth-tracing.workspace = true

# enable `test-utils` feature on this crate
reth-network-ress = { path = ".", features = ["test-utils"] }

tokio.workspace = true
parking_lot.workspace = true
strum.workspace = true
strum_macros.workspace = true
arbitrary = { workspace = true, features = ["derive"] }
proptest.workspace = true
proptest-arbitrary-interop.workspace = true
assert_matches.workspace = true

[features]
test-utils = ["parking_lot"]
arbitrary = ["dep:arbitrary", "reth-eth-wire/arbitrary"]
96 changes: 96 additions & 0 deletions crates/net/ress/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Ress Protocol (RESS)

The `ress` protocol runs on top of [RLPx], allowing a stateless nodes to fetch necessary state data (witness, block, bytecode) from a stateful node. The protocol is an optional extension for peers that support (or are interested in) stateless Ethereum full nodes.

**Note**: In this context, “stateless nodes” does not imply holding zero state on disk. Rather, such nodes still maintain minimal or partial state (e.g., essential bytecodes), which is relatively small. For simplicity, we continue to use the term “stateless” throughout these documents.

The current version is `ress/0`.

## Overview

The `ress` protocol is designed to provide support for stateless nodes. Its goal is to enable the exchange of necessary block execution state from stateful nodes to a stateless nodes so that the latter can store in disk only the minimal required state (such as bytecode) and lazily fetch other state data. It supports retrieving a state witness for a target new payload from a stateful peer, as well as contract bytecode and full block data (headers and bodies). The `ress` protocol is intended to be run alongside other protocols (e.g., `eth`), rather than as a standalone protocol.

## Basic operation

Once a connection is established, a [NodeType] message must be sent. After the peer's node type is validated according to the connection rules, any other protocol messages may be sent. The `ress` session will be terminated if the peer combination is invalid (e.g., stateful-to-stateful connections are not needed).

Within a session, four types of messages can be exchanged: headers, bodies, bytecode, and witness.

During the startup phase, a stateless node downloads the necessary ancestor blocks (headers and bodies) via the header and body messages. When the stateless node receives a new payload through the engine API, it requests a witness—a compressed multi Merkle proof of state—using the witness message. From this witness, the stateless node can determine if any contract bytecode is missing by comparing it with its disk. It then requests any missing bytecode. All requests are sent to the connected stateful peer and occur synchronously.

## Protocol Messages

In most messages, the first element of the message data list is the request-id. For requests, this is a 64-bit integer value chosen by the requesting peer. The responding peer must mirror the value in the request-id element of the response message.

### NodeType (0x00)

`[nodetype]`

Informs a peer of its node type. This message should be sent immediately after the connection is established and before any other RESS protocol messages.

There are two types of nodes in the network:

| ID | Node Type |
| --- | --------- |
| 0 | Stateless |
| 1 | Stateful |


The following table shows which connections between node types are valid:

| | stateless | stateful |
| --------- | --------- | -------- |
| stateless | true | true |
| stateful | true | false |


### GetHeaders (0x01)

`[request-id: P, [blockhash: B_32, limit: P]]`

Require the peer to return a Headers message. The response must contain up to limit block headers, beginning at blockhash in the canonical chain and traversing towards the genesis block (descending order).

### Headers (0x02)

`[request-id: P, [header₁, header₂, ...]]`

This is the response to GetHeaders, containing the requested headers. The header list may be empty if none of the requested block headers were found. The number of headers that can be requested in a single message may be subject to implementation-defined limits.

### GetBlockBodies (0x03)

`[request-id: P, [blockhash₁: B_32, blockhash₂: B_32, ...]]`

This message requests block body data by hash. The number of blocks that can be requested in a single message may be subject to implementation-defined limits.

### BlockBodies (0x04)

`[request-id: P, [block-body₁, block-body₂, ...]]`

This is the response to GetBlockBodies. The items in the list contain the body data of the requested blocks. The list may be empty if none of the requested blocks were available.

### GetBytecode (0x05)

`[request-id: P, [codehash: B_32]]`

Require peer to return a bytecode message containing the bytecode of the given code hash.

### Bytecode (0x06)

`[request-id: P, [bytes]]`

This is the response to GetBytecode, providing the requested bytecode. Response corresponds to a code hash of the GetBytecode request.

### GetWitness (0x07)

`[request-id: P, [blockhash: B_32]]`

Require peer to return a state witness message containing the witness of the given block hash.

### Witness (0x08)

`[request-id: P, [statewitness]]`

This is the response to GetWitness, providing the requested state witness. Response corresponds to a block hash of the GetWitness request.

[NodeType]: #NodeType-0x00
[RLPx]: https://github.com/ethereum/devp2p/blob/master/rlpx.md
Loading
Loading