Skip to content

Commit da17461

Browse files
authored
Merge pull request ethereum#4045 from status-im/max-gossip-limit
Clarify gossip limits
2 parents d34f1c5 + d867b84 commit da17461

File tree

5 files changed

+69
-23
lines changed

5 files changed

+69
-23
lines changed

.circleci/config.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@ commands:
3535
description: "Restore the cache with pyspec keys"
3636
steps:
3737
- restore_cached_venv:
38-
venv_name: v30-pyspec
38+
venv_name: v32-pyspec
3939
reqs_checksum: cache-{{ checksum "setup.py" }}-{{ checksum "requirements_preinstallation.txt" }}
4040
save_pyspec_cached_venv:
4141
description: Save a venv into a cache with pyspec keys"
4242
steps:
4343
- save_cached_venv:
44-
venv_name: v30-pyspec
44+
venv_name: v32-pyspec
4545
reqs_checksum: cache-{{ checksum "setup.py" }}-{{ checksum "requirements_preinstallation.txt" }}
4646
venv_path: ./venv
4747
jobs:

configs/mainnet.yaml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,15 +115,13 @@ DEPOSIT_CONTRACT_ADDRESS: 0x00000000219ab540356cBB839Cbe05303d7705Fa
115115
# Networking
116116
# ---------------------------------------------------------------
117117
# `10 * 2**20` (= 10485760, 10 MiB)
118-
GOSSIP_MAX_SIZE: 10485760
118+
MAX_PAYLOAD_SIZE: 10485760
119119
# `2**10` (= 1024)
120120
MAX_REQUEST_BLOCKS: 1024
121121
# `2**8` (= 256)
122122
EPOCHS_PER_SUBNET_SUBSCRIPTION: 256
123123
# `MIN_VALIDATOR_WITHDRAWABILITY_DELAY + CHURN_LIMIT_QUOTIENT // 2` (= 33024, ~5 months)
124124
MIN_EPOCHS_FOR_BLOCK_REQUESTS: 33024
125-
# `10 * 2**20` (=10485760, 10 MiB)
126-
MAX_CHUNK_SIZE: 10485760
127125
# 5s
128126
TTFB_TIMEOUT: 5
129127
# 10s

configs/minimal.yaml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,15 +116,13 @@ DEPOSIT_CONTRACT_ADDRESS: 0x1234567890123456789012345678901234567890
116116
# Networking
117117
# ---------------------------------------------------------------
118118
# `10 * 2**20` (= 10485760, 10 MiB)
119-
GOSSIP_MAX_SIZE: 10485760
119+
MAX_PAYLOAD_SIZE: 10485760
120120
# `2**10` (= 1024)
121121
MAX_REQUEST_BLOCKS: 1024
122122
# `2**8` (= 256)
123123
EPOCHS_PER_SUBNET_SUBSCRIPTION: 256
124124
# [customized] `MIN_VALIDATOR_WITHDRAWABILITY_DELAY + CHURN_LIMIT_QUOTIENT // 2` (= 272)
125125
MIN_EPOCHS_FOR_BLOCK_REQUESTS: 272
126-
# `10 * 2**20` (=10485760, 10 MiB)
127-
MAX_CHUNK_SIZE: 10485760
128126
# 5s
129127
TTFB_TIMEOUT: 5
130128
# 10s

specs/bellatrix/p2p-interface.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,8 @@ Per `context = compute_fork_digest(fork_version, genesis_validators_root)`:
148148
#### Why was the max gossip message size increased at Bellatrix?
149149

150150
With the addition of `ExecutionPayload` to `BeaconBlock`s, there is a dynamic
151-
field -- `transactions` -- which can validly exceed the `GOSSIP_MAX_SIZE` limit (1 MiB) put in
152-
place at Phase 0, so GOSSIP_MAX_SIZE has increased to 10 Mib on the network.
151+
field -- `transactions` -- which can validly exceed the `MAX_PAYLOAD_SIZE` limit (1 MiB) put in
152+
place at Phase 0, so MAX_PAYLOAD_SIZE has increased to 10 MiB on the network.
153153
At the `GAS_LIMIT` (~30M) currently seen on mainnet in 2021, a single transaction
154154
filled entirely with data at a cost of 16 gas per byte can create a valid
155155
`ExecutionPayload` of ~2 MiB. Thus we need a size limit to at least account for

specs/phase0/p2p-interface.md

Lines changed: 63 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
- [Constants](#constants)
1717
- [Configuration](#configuration)
1818
- [MetaData](#metadata)
19+
- [Maximum message sizes](#maximum-message-sizes)
20+
- [`max_compressed_len`](#max_compressed_len)
21+
- [`max_message_size`](#max_message_size)
1922
- [The gossip domain: gossipsub](#the-gossip-domain-gossipsub)
2023
- [Topics and messages](#topics-and-messages)
2124
- [Global topics](#global-topics)
@@ -28,6 +31,7 @@
2831
- [`beacon_attestation_{subnet_id}`](#beacon_attestation_subnet_id)
2932
- [Attestations and Aggregation](#attestations-and-aggregation)
3033
- [Encodings](#encodings)
34+
- [Gossipsub size limits](#gossipsub-size-limits)
3135
- [The Req/Resp domain](#the-reqresp-domain)
3236
- [Protocol identification](#protocol-identification)
3337
- [Req/Resp interaction](#reqresp-interaction)
@@ -102,6 +106,8 @@
102106
- [Why are we using Snappy for compression?](#why-are-we-using-snappy-for-compression)
103107
- [Can I get access to unencrypted bytes on the wire for debugging purposes?](#can-i-get-access-to-unencrypted-bytes-on-the-wire-for-debugging-purposes)
104108
- [What are SSZ type size bounds?](#what-are-ssz-type-size-bounds)
109+
- [Why is the message size defined in terms of application payload?](#why-is-the-message-size-defined-in-terms-of-application-payload)
110+
- [Why is there a limit on message sizes at all?](#why-is-there-a-limit-on-message-sizes-at-all)
105111
- [libp2p implementations matrix](#libp2p-implementations-matrix)
106112

107113
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@@ -193,11 +199,10 @@ This section outlines configurations that are used in this spec.
193199

194200
| Name | Value | Description |
195201
|---|---|---|
196-
| `GOSSIP_MAX_SIZE` | `10 * 2**20` (= 10485760, 10 MiB) | The maximum allowed size of uncompressed gossip messages. |
202+
| `MAX_PAYLOAD_SIZE` | `10 * 2**20` (= 10485760, 10 MiB) | The maximum allowed size of uncompressed payload in gossipsub messages and RPC chunks |
197203
| `MAX_REQUEST_BLOCKS` | `2**10` (= 1024) | Maximum number of blocks in a single request |
198204
| `EPOCHS_PER_SUBNET_SUBSCRIPTION` | `2**8` (= 256) | Number of epochs on a subnet subscription (~27 hours) |
199205
| `MIN_EPOCHS_FOR_BLOCK_REQUESTS` | `MIN_VALIDATOR_WITHDRAWABILITY_DELAY + CHURN_LIMIT_QUOTIENT // 2` (= 33024, ~5 months) | The minimum epoch range over which a node must serve blocks |
200-
| `MAX_CHUNK_SIZE` | `10 * 2**20` (=10485760, 10 MiB) | The maximum allowed size of uncompressed req/resp chunked responses. |
201206
| `ATTESTATION_PROPAGATION_SLOT_RANGE` | `32` | The maximum number of slots during which an attestation can be propagated. |
202207
| `MAXIMUM_GOSSIP_CLOCK_DISPARITY` | `500` | The maximum **milliseconds** of clock disparity assumed between honest nodes. |
203208
| `MESSAGE_DOMAIN_INVALID_SNAPPY` | `DomainType('0x00000000')` | 4-byte domain for gossip message-id isolation of *invalid* snappy messages |
@@ -229,6 +234,27 @@ Where
229234
is entirely independent of the ENR sequence number,
230235
and will in most cases be out of sync with the ENR sequence number.
231236

237+
### Maximum message sizes
238+
239+
Maximum message sizes are derived from the maximum payload size that the network can carry according to the following functions:
240+
241+
#### `max_compressed_len`
242+
243+
```python
244+
def max_compressed_len(n: uint64) -> uint64:
245+
# Worst-case compressed length for a given payload of size n when using snappy:
246+
# https://github.com/google/snappy/blob/32ded457c0b1fe78ceb8397632c416568d6714a0/snappy.cc#L218C1-L218C47
247+
return uint64(32 + n + n / 6)
248+
```
249+
250+
#### `max_message_size`
251+
252+
```python
253+
def max_message_size() -> uint64:
254+
# Allow 1024 bytes for framing and encoding overhead but at least 1MiB in case MAX_PAYLOAD_SIZE is small.
255+
return max(max_compressed_len(MAX_PAYLOAD_SIZE) + 1024, 1024 * 1024)
256+
```
257+
232258
### The gossip domain: gossipsub
233259

234260
Clients MUST support the [gossipsub v1](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.0.md) libp2p Protocol
@@ -268,13 +294,11 @@ This defines both the type of data being sent on the topic and how the data fiel
268294
- `Encoding` - the encoding strategy describes a specific representation of bytes that will be transmitted over the wire.
269295
See the [Encodings](#Encodings) section for further details.
270296

297+
Clients MUST reject messages with an unknown topic.
298+
271299
*Note*: `ForkDigestValue` is composed of values that are not known until the genesis block/state are available.
272300
Due to this, clients SHOULD NOT subscribe to gossipsub topics until these genesis values are known.
273301

274-
Each gossipsub [message](https://github.com/libp2p/go-libp2p-pubsub/blob/master/pb/rpc.proto#L17-L24) has a maximum size of `GOSSIP_MAX_SIZE`.
275-
Clients MUST reject (fail validation) messages that are over this size limit.
276-
Likewise, clients MUST NOT emit or propagate messages larger than this limit.
277-
278302
The optional `from` (1), `seqno` (3), `signature` (5) and `key` (6) protobuf fields are omitted from the message,
279303
since messages are identified by content, anonymous, and signed where necessary in the application layer.
280304
Starting from Gossipsub v1.1, clients MUST enforce this by applying the `StrictNoSign`
@@ -288,6 +312,8 @@ The `message-id` of a gossipsub message MUST be the following 20 byte value comp
288312
the concatenation of `MESSAGE_DOMAIN_INVALID_SNAPPY` with the raw message data,
289313
i.e. `SHA256(MESSAGE_DOMAIN_INVALID_SNAPPY + message.data)[:20]`.
290314

315+
Where relevant, clients MUST reject messages with `message-id` sizes other than 20 bytes.
316+
291317
*Note*: The above logic handles two exceptional cases:
292318
(1) multiple snappy `data` can decompress to the same value,
293319
and (2) some message `data` can fail to snappy decompress altogether.
@@ -502,6 +528,16 @@ so [basic snappy block compression](https://github.com/google/snappy/blob/master
502528
Implementations MUST use a single encoding for gossip.
503529
Changing an encoding will require coordination between participating implementations.
504530

531+
#### Gossipsub size limits
532+
533+
Size limits are placed both on the [`RPCMsg`](https://github.com/libp2p/specs/blob/b5f7fce29b32d4c7d0efe37b019936a11e5db872/pubsub/README.md#the-rpc) frame as well as the encoded payload in each [`Message`](https://github.com/libp2p/specs/blob/b5f7fce29b32d4c7d0efe37b019936a11e5db872/pubsub/README.md#the-message).
534+
535+
Clients MUST reject and MUST NOT emit or propagate messages whose size exceed the following limits:
536+
537+
* The size of the encoded `RPCMsg` (including control messages, framing, topics, etc) must not exceed `max_message_size()`.
538+
* The size of the compressed payload in the `Message.data` field must not exceed `max_compressed_len(MAX_PAYLOAD_SIZE)`.
539+
* The size of the uncompressed payload must not exceed `MAX_PAYLOAD_SIZE` or the [type-specific SSZ bound](#what-are-ssz-type-size-bounds), whichever is lower.
540+
505541
### The Req/Resp domain
506542

507543
#### Protocol identification
@@ -551,7 +587,7 @@ All other response types (non-Lists) send a single `response_chunk`.
551587
For both `request`s and `response`s, the `encoding-dependent-header` MUST be valid,
552588
and the `encoded-payload` must be valid within the constraints of the `encoding-dependent-header`.
553589
This includes type-specific bounds on payload size for some encoding strategies.
554-
Regardless of these type specific bounds, a global maximum uncompressed byte size of `MAX_CHUNK_SIZE` MUST be applied to all method response chunks.
590+
Regardless of these type specific bounds, a global maximum uncompressed byte size of `MAX_PAYLOAD_SIZE` MUST be applied to all method response chunks.
555591

556592
Clients MUST ensure that lengths are within these bounds; if not, they SHOULD reset the stream immediately.
557593
Clients tracking peer reputation MAY decrement the score of the misbehaving peer under this circumstance.
@@ -665,15 +701,13 @@ When snappy is applied, it can be passed through a buffered Snappy reader to dec
665701

666702
Before reading the payload, the header MUST be validated:
667703
- The unsigned protobuf varint used for the length-prefix MUST not be longer than 10 bytes, which is sufficient for any `uint64`.
668-
- The length-prefix is within the expected [size bounds derived from the payload SSZ type](#what-are-ssz-type-size-bounds).
704+
- The length-prefix is within the expected [size bounds derived from the payload SSZ type](#what-are-ssz-type-size-bounds) or `MAX_PAYLOAD_SIZE`, whichever is smaller.
669705

670706
After reading a valid header, the payload MAY be read, while maintaining the size constraints from the header.
671707

672-
A reader SHOULD NOT read more than `max_encoded_len(n)` bytes after reading the SSZ length-prefix `n` from the header.
673-
- For `ssz_snappy` this is: `32 + n + n // 6`.
674-
This is considered the [worst-case compression result](https://github.com/google/snappy/blob/537f4ad6240e586970fe554614542e9717df7902/snappy.cc#L98) by Snappy.
708+
A reader MUST NOT read more than `max_compressed_len(n)` bytes after reading the SSZ length-prefix `n` from the header.
675709

676-
A reader SHOULD consider the following cases as invalid input:
710+
A reader MUST consider the following cases as invalid input:
677711
- Any remaining bytes, after having read the `n` SSZ bytes. An EOF is expected if more bytes are read than required.
678712
- An early EOF, before fully reading the declared length-prefix worth of SSZ bytes.
679713

@@ -1430,7 +1464,7 @@ Nevertheless, in the case of `ssz_snappy`, messages are still length-prefixed wi
14301464
* Alignment with protocols like gRPC over HTTP/2 that prefix with length
14311465
* Sanity checking of message length, and enabling much stricter message length limiting based on SSZ type information,
14321466
to provide even more DOS protection than the global message length already does.
1433-
E.g. a small `Status` message does not nearly require `MAX_CHUNK_SIZE` bytes.
1467+
E.g. a small `Status` message does not nearly require `MAX_PAYLOAD_SIZE` bytes.
14341468

14351469
[Protobuf varint](https://developers.google.com/protocol-buffers/docs/encoding#varints) is an efficient technique to encode variable-length (unsigned here) ints.
14361470
Instead of reserving a fixed-size field of as many bytes as necessary to convey the maximum possible value, this field is elastic in exchange for 1-bit overhead per byte.
@@ -1679,6 +1713,22 @@ Other types are static, they have a fixed size: no dynamic-length content is inv
16791713
For reference, the type bounds can be computed ahead of time, [as per this example](https://gist.github.com/protolambda/db75c7faa1e94f2464787a480e5d613e).
16801714
It is advisable to derive these lengths from the SSZ type definitions in use, to ensure that version changes do not cause out-of-sync type bounds.
16811715

1716+
#### Why is the message size defined in terms of application payload?
1717+
1718+
When transmitting messages over gossipsub and/or the req/resp domain, we want to ensure that the same payload sizes are supported regardless of the underlying transport, decoupling the consensus layer from libp2p-induced overhead and the particular transmission strategy.
1719+
1720+
To derive "encoded size limits" from desired application sizes, we take into account snappy compression and framing overhead.
1721+
1722+
In the case of gossipsub, the protocol supports sending multiple application payloads as well as mixing application data with control messages in each gossipsub frame. The limit is set such that at least one max-sized application-level message together with a small amount (1 KiB) of gossipsub overhead is allowed. Implementations are free to pack multiple smaller application messages into a single gossipsub frame, and/or combine it with control messages as they see fit.
1723+
1724+
The limit is set on the uncompressed payload size in particular to protect against decompression bombs.
1725+
1726+
#### Why is there a limit on message sizes at all?
1727+
1728+
The message size limit protects against several forms of DoS and network-based amplification attacks and provides upper bounds for resource (network, memory) usage in the client based on protocol requirements to decode, buffer, cache, store and re-transmit messages which in turn translate into performance and protection tradeoffs, ensuring capacity to handle worst cases during recovery from network instability.
1729+
1730+
In particular, blocks—-currently the only message type without a practical SSZ-derived upper bound on size—-cannot be fully verified synchronously as part of gossipsub validity checks. This means that there exist cases where invalid messages signed by a validator may be amplified by the network.
1731+
16821732
## libp2p implementations matrix
16831733

16841734
This section will soon contain a matrix showing the maturity/state of the libp2p features required

0 commit comments

Comments
 (0)