Skip to content

Commit 93deccc

Browse files
committed
Altair
1 parent 8e184de commit 93deccc

File tree

1 file changed

+201
-1
lines changed

1 file changed

+201
-1
lines changed

spec/p2p-interface.md

Lines changed: 201 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ Clients MUST locally store the following `MetaData`:
213213
(
214214
seq_number: uint64
215215
attnets: Bitvector[ATTESTATION_SUBNET_COUNT]
216+
syncnets: Bitvector[SYNC_COMMITTEE_SUBNET_COUNT]
216217
)
217218
```
218219

@@ -221,6 +222,7 @@ Where
221222
- `seq_number` is a `uint64` starting at `0` used to version the node's metadata.
222223
If any other field in the local `MetaData` changes, the node MUST increment `seq_number` by 1.
223224
- `attnets` is a `Bitvector` representing the node's persistent attestation subnet subscriptions.
225+
- `syncnets` is a `Bitvector` representing the node's sync committee subnet subscriptions. This field should mirror the data in the node's ENR as outlined in the [validator guide](https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/validator.md#sync-committee-subnet-stability).
224226

225227
*Note*: `MetaData.seq_number` is used for versioning of the node's metadata,
226228
is entirely independent of the ENR sequence number,
@@ -289,6 +291,23 @@ The `message-id` of a gossipsub message MUST be the following 20 byte value comp
289291
(1) multiple snappy `data` can decompress to the same value,
290292
and (2) some message `data` can fail to snappy decompress altogether.
291293

294+
The derivation of the `message-id` has changed starting with Altair to incorporate the message `topic` along with the message `data`. These are fields of the `Message` Protobuf, and interpreted as empty byte strings if missing.
295+
The `message-id` MUST be the following 20 byte value computed from the message:
296+
* If `message.data` has a valid snappy decompression, set `message-id` to the first 20 bytes of the `SHA256` hash of
297+
the concatenation of the following data: `MESSAGE_DOMAIN_VALID_SNAPPY`, the length of the topic byte string (encoded as little-endian `uint64`),
298+
the topic byte string, and the snappy decompressed message data:
299+
i.e. `SHA256(MESSAGE_DOMAIN_VALID_SNAPPY + uint_to_bytes(uint64(len(message.topic))) + message.topic + snappy_decompress(message.data))[:20]`.
300+
* Otherwise, set `message-id` to the first 20 bytes of the `SHA256` hash of
301+
the concatenation of the following data: `MESSAGE_DOMAIN_INVALID_SNAPPY`, the length of the topic byte string (encoded as little-endian `uint64`),
302+
the topic byte string, and the raw message data:
303+
i.e. `SHA256(MESSAGE_DOMAIN_INVALID_SNAPPY + uint_to_bytes(uint64(len(message.topic))) + message.topic + message.data)[:20]`.
304+
305+
Implementations may need to carefully handle the function that computes the `message-id`. In particular, messages on topics with the Phase 0
306+
fork digest should use the `message-id` procedure specified in the Phase 0 document.
307+
Messages on topics with the Altair fork digest should use the `message-id` procedure defined here.
308+
If an implementation only supports a single `message-id` function, it can define a switch inline;
309+
for example, `if topic in phase0_topics: return phase0_msg_id_fn(message) else return altair_msg_id_fn(message)`.
310+
292311
The payload is carried in the `data` field of a gossipsub message, and varies depending on the topic:
293312

294313
| Name | Message Type |
@@ -300,6 +319,14 @@ The payload is carried in the `data` field of a gossipsub message, and varies de
300319
| `proposer_slashing` | `ProposerSlashing` |
301320
| `attester_slashing` | `AttesterSlashing` |
302321

322+
Altair topics:
323+
324+
| Name | Message Type |
325+
| - | - |
326+
| `beacon_block` | `SignedBeaconBlock` (modified) |
327+
| `sync_committee_contribution_and_proof` | `SignedContributionAndProof` |
328+
| `sync_committee_{subnet_id}` | `SyncCommitteeMessage` |
329+
303330
Clients MUST reject (fail validation) messages containing an incorrect type, or invalid payload.
304331

305332
When processing incoming gossip, clients MAY descore or disconnect peers who fail to observe these constraints.
@@ -325,6 +352,8 @@ There are three additional global topics that are used to propagate lower freque
325352
The `beacon_block` topic is used solely for propagating new signed beacon blocks to all nodes on the networks.
326353
Signed blocks are sent in their entirety.
327354

355+
Modified in Altair due to the inner `BeaconBlockBody` change.
356+
328357
The following validations MUST pass before forwarding the `signed_beacon_block` on the network.
329358
- _[IGNORE]_ The block is not from a future slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) --
330359
i.e. validate that `signed_beacon_block.message.slot <= current_slot`
@@ -415,6 +444,43 @@ Clients who receive an attester slashing on this topic MUST validate the conditi
415444
verify if `any(attester_slashed_indices.difference(prior_seen_attester_slashed_indices))`).
416445
- _[REJECT]_ All of the conditions within `process_attester_slashing` pass validation.
417446

447+
###### `sync_committee_contribution_and_proof`
448+
449+
This topic is used to propagate partially aggregated sync committee messages to be included in future blocks.
450+
451+
The following validations MUST pass before forwarding the `signed_contribution_and_proof` on the network; define `contribution_and_proof = signed_contribution_and_proof.message`, `contribution = contribution_and_proof.contribution`, and the following function `get_sync_subcommittee_pubkeys` for convenience:
452+
453+
```python
454+
def get_sync_subcommittee_pubkeys(state: BeaconState, subcommittee_index: uint64) -> Sequence[BLSPubkey]:
455+
# Committees assigned to `slot` sign for `slot - 1`
456+
# This creates the exceptional logic below when transitioning between sync committee periods
457+
next_slot_epoch = compute_epoch_at_slot(Slot(state.slot + 1))
458+
if compute_sync_committee_period(get_current_epoch(state)) == compute_sync_committee_period(next_slot_epoch):
459+
sync_committee = state.current_sync_committee
460+
else:
461+
sync_committee = state.next_sync_committee
462+
463+
# Return pubkeys for the subcommittee index
464+
sync_subcommittee_size = SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT
465+
i = subcommittee_index * sync_subcommittee_size
466+
return sync_committee.pubkeys[i:i + sync_subcommittee_size]
467+
```
468+
469+
- _[IGNORE]_ The contribution's slot is for the current slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance), i.e. `contribution.slot == current_slot`.
470+
- _[REJECT]_ The subcommittee index is in the allowed range, i.e. `contribution.subcommittee_index < SYNC_COMMITTEE_SUBNET_COUNT`.
471+
- _[REJECT]_ The contribution has participants --
472+
that is, `any(contribution.aggregation_bits)`.
473+
- _[REJECT]_ `contribution_and_proof.selection_proof` selects the validator as an aggregator for the slot -- i.e. `is_sync_committee_aggregator(contribution_and_proof.selection_proof)` returns `True`.
474+
- _[REJECT]_ The aggregator's validator index is in the declared subcommittee of the current sync committee --
475+
i.e. `state.validators[contribution_and_proof.aggregator_index].pubkey in get_sync_subcommittee_pubkeys(state, contribution.subcommittee_index)`.
476+
- _[IGNORE]_ A valid sync committee contribution with equal `slot`, `beacon_block_root` and `subcommittee_index` whose `aggregation_bits` is non-strict superset has _not_ already been seen.
477+
- _[IGNORE]_ The sync committee contribution is the first valid contribution received for the aggregator with index `contribution_and_proof.aggregator_index`
478+
for the slot `contribution.slot` and subcommittee index `contribution.subcommittee_index`
479+
(this requires maintaining a cache of size `SYNC_COMMITTEE_SIZE` for this topic that can be flushed after each slot).
480+
- _[REJECT]_ The `contribution_and_proof.selection_proof` is a valid signature of the `SyncAggregatorSelectionData` derived from the `contribution` by the validator with index `contribution_and_proof.aggregator_index`.
481+
- _[REJECT]_ The aggregator signature, `signed_contribution_and_proof.signature`, is valid.
482+
- _[REJECT]_ The aggregate signature is valid for the message `beacon_block_root` and aggregate pubkey derived from the participation info in `aggregation_bits` for the subcommittee specified by the `contribution.subcommittee_index`.
483+
418484
##### Attestation subnets
419485

420486
Attestation subnets are used to propagate unaggregated attestations to subsections of the network.
@@ -468,6 +534,59 @@ Unaggregated attestations are sent as `Attestation`s to the subnet topic,
468534

469535
Aggregated attestations are sent to the `beacon_aggregate_and_proof` topic as `AggregateAndProof`s.
470536

537+
##### Sync committee subnets
538+
539+
Sync committee subnets are used to propagate unaggregated sync committee messages to subsections of the network.
540+
541+
###### `sync_committee_{subnet_id}`
542+
543+
The `sync_committee_{subnet_id}` topics are used to propagate unaggregated sync committee messages to the subnet `subnet_id` to be aggregated before being gossiped to the global `sync_committee_contribution_and_proof` topic.
544+
545+
The following validations MUST pass before forwarding the `sync_committee_message` on the network:
546+
547+
- _[IGNORE]_ The message's slot is for the current slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance), i.e. `sync_committee_message.slot == current_slot`.
548+
- _[REJECT]_ The `subnet_id` is valid for the given validator, i.e. `subnet_id in compute_subnets_for_sync_committee(state, sync_committee_message.validator_index)`.
549+
Note this validation implies the validator is part of the broader current sync committee along with the correct subcommittee.
550+
- _[IGNORE]_ There has been no other valid sync committee message for the declared `slot` for the validator referenced by `sync_committee_message.validator_index`
551+
(this requires maintaining a cache of size `SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT` for each subnet that can be flushed after each slot).
552+
Note this validation is _per topic_ so that for a given `slot`, multiple messages could be forwarded with the same `validator_index` as long as the `subnet_id`s are distinct.
553+
- _[REJECT]_ The `signature` is valid for the message `beacon_block_root` for the validator referenced by `validator_index`.
554+
555+
##### Sync committees and aggregation
556+
557+
The aggregation scheme closely follows the design of the attestation aggregation scheme.
558+
Sync committee messages are broadcast into "subnets" defined by a topic.
559+
The number of subnets is defined by `SYNC_COMMITTEE_SUBNET_COUNT` in the [Altair validator guide](./validator.md#constants).
560+
Sync committee members are divided into "subcommittees" which are then assigned to a subnet for the duration of tenure in the sync committee.
561+
Individual validators can be duplicated in the broader sync committee such that they are included multiple times in a given subcommittee or across multiple subcommittees.
562+
563+
Unaggregated messages (along with metadata) are sent as `SyncCommitteeMessage`s on the `sync_committee_{subnet_id}` topics.
564+
565+
Aggregated sync committee messages are packaged into (signed) `SyncCommitteeContribution` along with proofs and gossiped to the `sync_committee_contribution_and_proof` topic.
566+
567+
#### Transitioning the gossip
568+
569+
With any fork, the fork version, and thus the `ForkDigestValue`, change.
570+
Message types are unique per topic, and so for a smooth transition a node must temporarily subscribe to both the old and new topics.
571+
572+
The topics that are not removed in a fork are updated with a new `ForkDigestValue`. In advance of the fork, a node SHOULD subscribe to the post-fork variants of the topics.
573+
574+
Subscriptions are expected to be well-received, all updated nodes should subscribe as well.
575+
Topic-meshes can be grafted quickly as the nodes are already connected and exchanging gossip control messages.
576+
577+
Messages SHOULD NOT be re-broadcast from one fork to the other.
578+
A node's behavior before the fork and after the fork are as follows:
579+
Pre-fork:
580+
- Peers who propagate messages on the post-fork topics MAY be scored negatively proportionally to time till fork,
581+
to account for clock discrepancy.
582+
- Messages can be IGNORED on the post-fork topics, with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` margin.
583+
584+
Post-fork:
585+
- Peers who propagate messages on the pre-fork topics MUST NOT be scored negatively. Lagging IWANT may force them to.
586+
- Messages on pre and post-fork variants of topics share application-level caches.
587+
E.g. an attestation on the both the old and new topic is ignored like any duplicate.
588+
- Two epochs after the fork, pre-fork topics SHOULD be unsubscribed from. This is well after the configured `seen_ttl`.
589+
471590
#### Encodings
472591

473592
Topics are post-fixed with an encoding. Encodings define how the payload of a gossipsub message is encoded.
@@ -518,10 +637,15 @@ Request/response messages MUST adhere to the encoding specified in the protocol
518637
```
519638
request ::= <encoding-dependent-header> | <encoded-payload>
520639
response ::= <response_chunk>*
521-
response_chunk ::= <result> | <encoding-dependent-header> | <encoded-payload>
640+
response_chunk ::= <result> | <context-bytes> | <encoding-dependent-header> | <encoded-payload>
522641
result ::= “0” | “1” | “2” | [“128” ... ”255”]
523642
```
524643

644+
`<context-bytes>` is empty by default.
645+
On a non-zero `<result>` with `ErrorMessage` payload, the `<context-bytes>` is also empty.
646+
In Altair and later forks, `<context-bytes>` functions as a short meta-data,
647+
defined per req-resp method, and can parametrize the payload decoder.
648+
525649
The encoding-dependent header may carry metadata or assertions such as the encoded payload length, for integrity and attack proofing purposes.
526650
Because req/resp streams are single-use and stream closures implicitly delimit the boundaries, it is not strictly necessary to length-prefix payloads;
527651
however, certain encodings like SSZ do, for added security.
@@ -538,6 +662,14 @@ Regardless of these type specific bounds, a global maximum uncompressed byte siz
538662
Clients MUST ensure that lengths are within these bounds; if not, they SHOULD reset the stream immediately.
539663
Clients tracking peer reputation MAY decrement the score of the misbehaving peer under this circumstance.
540664

665+
##### `ForkDigest`-context
666+
667+
Starting with Altair, and in future forks, SSZ type definitions may change.
668+
For this common case, we define the `ForkDigest`-context:
669+
670+
A fixed-width 4 byte `<context-bytes>`, set to the `ForkDigest` matching the chunk:
671+
`compute_fork_digest(fork_version, genesis_validators_root)`.
672+
541673
##### Requesting side
542674

543675
Once a new stream with the protocol ID for the request type has been negotiated, the full request message SHOULD be sent immediately.
@@ -818,6 +950,21 @@ In particular when `step == 1`, each `parent_root` MUST match the `hash_tree_roo
818950
After the initial block, clients MAY stop in the process of responding
819951
if their fork choice changes the view of the chain in the context of the request.
820952

953+
##### BeaconBlocksByRange v2
954+
955+
**Protocol ID:** `/eth2/beacon_chain/req/beacon_blocks_by_range/2/`
956+
957+
Request and Response remain unchanged. A `ForkDigest`-context is used to select the fork namespace of the Response type.
958+
959+
Per `context = compute_fork_digest(fork_version, genesis_validators_root)`:
960+
961+
[0]: # (eth2spec: skip)
962+
963+
| `fork_version` | Chunk SSZ type |
964+
| ------------------------ | -------------------------- |
965+
| `GENESIS_FORK_VERSION` | `phase0.SignedBeaconBlock` |
966+
| `ALTAIR_FORK_VERSION` | `altair.SignedBeaconBlock` |
967+
821968
##### BeaconBlocksByRoot
822969

823970
**Protocol ID:** `/eth2/beacon_chain/req/beacon_blocks_by_root/1/`
@@ -858,6 +1005,21 @@ Clients MAY limit the number of blocks in the response.
8581005

8591006
`/eth2/beacon_chain/req/beacon_blocks_by_root/1/` is deprecated. Clients MAY respond with an empty list during the deprecation transition period.
8601007

1008+
##### BeaconBlocksByRoot v2
1009+
1010+
**Protocol ID:** `/eth2/beacon_chain/req/beacon_blocks_by_root/2/`
1011+
1012+
Request and Response remain unchanged. A `ForkDigest`-context is used to select the fork namespace of the Response type.
1013+
1014+
Per `context = compute_fork_digest(fork_version, genesis_validators_root)`:
1015+
1016+
[1]: # (eth2spec: skip)
1017+
1018+
| `fork_version` | Chunk SSZ type |
1019+
| ------------------------ | -------------------------- |
1020+
| `GENESIS_FORK_VERSION` | `phase0.SignedBeaconBlock` |
1021+
| `ALTAIR_FORK_VERSION` | `altair.SignedBeaconBlock` |
1022+
8611023
##### Ping
8621024

8631025
**Protocol ID:** `/eth2/beacon_chain/req/ping/1/`
@@ -913,6 +1075,36 @@ The response MUST be encoded as an SSZ-container.
9131075

9141076
The response MUST consist of a single `response_chunk`.
9151077

1078+
##### GetMetaData v2
1079+
1080+
**Protocol ID:** `/eth2/beacon_chain/req/metadata/2/`
1081+
1082+
No Request Content.
1083+
1084+
Response Content:
1085+
1086+
```
1087+
(
1088+
MetaData
1089+
)
1090+
```
1091+
1092+
Requests the MetaData of a peer, using the new `MetaData` definition given above
1093+
that is extended from phase 0 in Altair. Other conditions for the `GetMetaData`
1094+
protocol are unchanged from the phase 0 p2p networking document.
1095+
1096+
#### Transitioning from v1 to v2
1097+
1098+
In advance of the fork, implementations can opt in to both run the v1 and v2 for a smooth transition.
1099+
This is non-breaking, and is recommended as soon as the fork specification is stable.
1100+
1101+
The v1 variants will be deprecated, and implementations should use v2 when available
1102+
(as negotiated with peers via LibP2P multistream-select).
1103+
1104+
The v1 method MAY be unregistered at the fork boundary.
1105+
In the event of a request on v1 for an Altair specific payload,
1106+
the responder MUST return the **InvalidRequest** response code.
1107+
9161108
### The discovery domain: discv5
9171109

9181110
Discovery Version 5 ([discv5](https://github.com/ethereum/devp2p/blob/master/discv5/discv5.md)) (Protocol version v5.1) is used for peer discovery.
@@ -959,6 +1151,14 @@ If a node's `MetaData.attnets` has any non-zero bit, the ENR MUST include the `a
9591151

9601152
If a node's `MetaData.attnets` is composed of all zeros, the ENR MAY optionally include the `attnets` entry or leave it out entirely.
9611153

1154+
##### `syncnets` bitfield
1155+
1156+
An additional bitfield is added to the ENR under the key `syncnets` to facilitate sync committee subnet discovery.
1157+
The length of this bitfield is `SYNC_COMMITTEE_SUBNET_COUNT` where each bit corresponds to a distinct `subnet_id` for a specific sync committee subnet.
1158+
The `i`th bit is set in this bitfield if the validator is currently subscribed to the `sync_committee_{i}` topic.
1159+
1160+
See the [validator document](./validator.md#sync-committee-subnet-stability) for further details on how the new bits are used.
1161+
9621162
##### `eth2` field
9631163

9641164
ENRs MUST carry a generic `eth2` key with an 16-byte value of the node's current fork digest, next fork version,

0 commit comments

Comments
 (0)