-
Notifications
You must be signed in to change notification settings - Fork 997
/
p2p-interface.md
1685 lines (1229 loc) · 99.9 KB
/
p2p-interface.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Phase 0 -- Networking
## Table of contents
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Introduction](#introduction)
- [Network fundamentals](#network-fundamentals)
- [Transport](#transport)
- [Encryption and identification](#encryption-and-identification)
- [Protocol Negotiation](#protocol-negotiation)
- [Multiplexing](#multiplexing)
- [Consensus-layer network interaction domains](#consensus-layer-network-interaction-domains)
- [Custom types](#custom-types)
- [Constants](#constants)
- [Configuration](#configuration)
- [MetaData](#metadata)
- [The gossip domain: gossipsub](#the-gossip-domain-gossipsub)
- [Topics and messages](#topics-and-messages)
- [Global topics](#global-topics)
- [`beacon_block`](#beacon_block)
- [`beacon_aggregate_and_proof`](#beacon_aggregate_and_proof)
- [`voluntary_exit`](#voluntary_exit)
- [`proposer_slashing`](#proposer_slashing)
- [`attester_slashing`](#attester_slashing)
- [Attestation subnets](#attestation-subnets)
- [`beacon_attestation_{subnet_id}`](#beacon_attestation_subnet_id)
- [Attestations and Aggregation](#attestations-and-aggregation)
- [Encodings](#encodings)
- [The Req/Resp domain](#the-reqresp-domain)
- [Protocol identification](#protocol-identification)
- [Req/Resp interaction](#reqresp-interaction)
- [Requesting side](#requesting-side)
- [Responding side](#responding-side)
- [Encoding strategies](#encoding-strategies)
- [SSZ-snappy encoding strategy](#ssz-snappy-encoding-strategy)
- [Messages](#messages)
- [Status](#status)
- [Goodbye](#goodbye)
- [BeaconBlocksByRange](#beaconblocksbyrange)
- [BeaconBlocksByRoot](#beaconblocksbyroot)
- [Ping](#ping)
- [GetMetaData](#getmetadata)
- [The discovery domain: discv5](#the-discovery-domain-discv5)
- [Integration into libp2p stacks](#integration-into-libp2p-stacks)
- [ENR structure](#enr-structure)
- [Attestation subnet bitfield](#attestation-subnet-bitfield)
- [`eth2` field](#eth2-field)
- [Attestation subnet subscription](#attestation-subnet-subscription)
- [Design decision rationale](#design-decision-rationale)
- [Transport](#transport-1)
- [Why are we defining specific transports?](#why-are-we-defining-specific-transports)
- [Can clients support other transports/handshakes than the ones mandated by the spec?](#can-clients-support-other-transportshandshakes-than-the-ones-mandated-by-the-spec)
- [What are the advantages of using TCP/QUIC/Websockets?](#what-are-the-advantages-of-using-tcpquicwebsockets)
- [Why do we not just support a single transport?](#why-do-we-not-just-support-a-single-transport)
- [Why are we not using QUIC from the start?](#why-are-we-not-using-quic-from-the-start)
- [Multiplexing](#multiplexing-1)
- [Why are we using mplex/yamux?](#why-are-we-using-mplexyamux)
- [Protocol Negotiation](#protocol-negotiation-1)
- [When is multiselect 2.0 due and why do we plan to migrate to it?](#when-is-multiselect-20-due-and-why-do-we-plan-to-migrate-to-it)
- [What is the difference between connection-level and stream-level protocol negotiation?](#what-is-the-difference-between-connection-level-and-stream-level-protocol-negotiation)
- [Encryption](#encryption)
- [Why are we not supporting SecIO?](#why-are-we-not-supporting-secio)
- [Why are we using Noise?](#why-are-we-using-noise)
- [Why are we using encryption at all?](#why-are-we-using-encryption-at-all)
- [Gossipsub](#gossipsub)
- [Why are we using a pub/sub algorithm for block and attestation propagation?](#why-are-we-using-a-pubsub-algorithm-for-block-and-attestation-propagation)
- [Why are we using topics to segregate encodings, yet only support one encoding?](#why-are-we-using-topics-to-segregate-encodings-yet-only-support-one-encoding)
- [How do we upgrade gossip channels (e.g. changes in encoding, compression)?](#how-do-we-upgrade-gossip-channels-eg-changes-in-encoding-compression)
- [Why must all clients use the same gossip topic instead of one negotiated between each peer pair?](#why-must-all-clients-use-the-same-gossip-topic-instead-of-one-negotiated-between-each-peer-pair)
- [Why are the topics strings and not hashes?](#why-are-the-topics-strings-and-not-hashes)
- [Why are we using the `StrictNoSign` signature policy?](#why-are-we-using-the-strictnosign-signature-policy)
- [Why are we overriding the default libp2p pubsub `message-id`?](#why-are-we-overriding-the-default-libp2p-pubsub-message-id)
- [Why are these specific gossip parameters chosen?](#why-are-these-specific-gossip-parameters-chosen)
- [Why is there `MAXIMUM_GOSSIP_CLOCK_DISPARITY` when validating slot ranges of messages in gossip subnets?](#why-is-there-maximum_gossip_clock_disparity-when-validating-slot-ranges-of-messages-in-gossip-subnets)
- [Why are there `ATTESTATION_SUBNET_COUNT` attestation subnets?](#why-are-there-attestation_subnet_count-attestation-subnets)
- [Why are attestations limited to be broadcast on gossip channels within `SLOTS_PER_EPOCH` slots?](#why-are-attestations-limited-to-be-broadcast-on-gossip-channels-within-slots_per_epoch-slots)
- [Why are aggregate attestations broadcast to the global topic as `AggregateAndProof`s rather than just as `Attestation`s?](#why-are-aggregate-attestations-broadcast-to-the-global-topic-as-aggregateandproofs-rather-than-just-as-attestations)
- [Why are we sending entire objects in the pubsub and not just hashes?](#why-are-we-sending-entire-objects-in-the-pubsub-and-not-just-hashes)
- [Should clients gossip blocks if they *cannot* validate the proposer signature due to not yet being synced, not knowing the head block, etc?](#should-clients-gossip-blocks-if-they-cannot-validate-the-proposer-signature-due-to-not-yet-being-synced-not-knowing-the-head-block-etc)
- [How are we going to discover peers in a gossipsub topic?](#how-are-we-going-to-discover-peers-in-a-gossipsub-topic)
- [How should fork version be used in practice?](#how-should-fork-version-be-used-in-practice)
- [Req/Resp](#reqresp)
- [Why segregate requests into dedicated protocol IDs?](#why-segregate-requests-into-dedicated-protocol-ids)
- [Why are messages length-prefixed with a protobuf varint in the SSZ-encoding?](#why-are-messages-length-prefixed-with-a-protobuf-varint-in-the-ssz-encoding)
- [Why do we version protocol strings with ordinals instead of semver?](#why-do-we-version-protocol-strings-with-ordinals-instead-of-semver)
- [Why is it called Req/Resp and not RPC?](#why-is-it-called-reqresp-and-not-rpc)
- [What is a typical rate limiting strategy?](#what-is-a-typical-rate-limiting-strategy)
- [Why do we allow empty responses in block requests?](#why-do-we-allow-empty-responses-in-block-requests)
- [Why does `BeaconBlocksByRange` let the server choose which branch to send blocks from?](#why-does-beaconblocksbyrange-let-the-server-choose-which-branch-to-send-blocks-from)
- [Why are `BlocksByRange` requests only required to be served for the latest `MIN_EPOCHS_FOR_BLOCK_REQUESTS` epochs?](#why-are-blocksbyrange-requests-only-required-to-be-served-for-the-latest-min_epochs_for_block_requests-epochs)
- [Why must the proposer signature be checked when backfilling blocks in the database?](#why-must-the-proposer-signature-be-checked-when-backfilling-blocks-in-the-database)
- [What's the effect of empty slots on the sync algorithm?](#whats-the-effect-of-empty-slots-on-the-sync-algorithm)
- [Discovery](#discovery)
- [Why are we using discv5 and not libp2p Kademlia DHT?](#why-are-we-using-discv5-and-not-libp2p-kademlia-dht)
- [What is the difference between an ENR and a multiaddr, and why are we using ENRs?](#what-is-the-difference-between-an-enr-and-a-multiaddr-and-why-are-we-using-enrs)
- [Why do we not form ENRs and find peers until genesis block/state is known?](#why-do-we-not-form-enrs-and-find-peers-until-genesis-blockstate-is-known)
- [Compression/Encoding](#compressionencoding)
- [Why are we using SSZ for encoding?](#why-are-we-using-ssz-for-encoding)
- [Why are we compressing, and at which layers?](#why-are-we-compressing-and-at-which-layers)
- [Why are we using Snappy for compression?](#why-are-we-using-snappy-for-compression)
- [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)
- [What are SSZ type size bounds?](#what-are-ssz-type-size-bounds)
- [libp2p implementations matrix](#libp2p-implementations-matrix)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Introduction
This document contains the networking specification for Phase 0.
It consists of four main sections:
1. A specification of the network fundamentals.
2. A specification of the three network interaction *domains* of the proof-of-stake consensus layer: (a) the gossip domain, (b) the discovery domain, and (c) the Req/Resp domain.
3. The rationale and further explanation for the design choices made in the previous two sections.
4. An analysis of the maturity/state of the libp2p features required by this spec across the languages in which clients are being developed.
## Network fundamentals
This section outlines the specification for the networking stack in Ethereum consensus-layer clients.
### Transport
Even though libp2p is a multi-transport stack (designed to listen on multiple simultaneous transports and endpoints transparently),
we hereby define a profile for basic interoperability.
All implementations MUST support the TCP libp2p transport, and it MUST be enabled for both dialing and listening (i.e. outbound and inbound connections).
The libp2p TCP transport supports listening on IPv4 and IPv6 addresses (and on multiple simultaneously).
Clients must support listening on at least one of IPv4 or IPv6.
Clients that do _not_ have support for listening on IPv4 SHOULD be cognizant of the potential disadvantages in terms of
Internet-wide routability/support. Clients MAY choose to listen only on IPv6, but MUST be capable of dialing both IPv4 and IPv6 addresses.
All listening endpoints must be publicly dialable, and thus not rely on libp2p circuit relay, AutoNAT, or AutoRelay facilities.
(Usage of circuit relay, AutoNAT, or AutoRelay will be specifically re-examined soon.)
Nodes operating behind a NAT, or otherwise undialable by default (e.g. container runtime, firewall, etc.),
MUST have their infrastructure configured to enable inbound traffic on the announced public listening endpoint.
### Encryption and identification
The [Libp2p-noise](https://github.com/libp2p/specs/tree/master/noise) secure
channel handshake with `secp256k1` identities will be used for encryption.
As specified in the libp2p specification, clients MUST support the `XX` handshake pattern.
### Protocol Negotiation
Clients MUST use exact equality when negotiating protocol versions to use and MAY use the version to give priority to higher version numbers.
Clients MUST support [multistream-select 1.0](https://github.com/multiformats/multistream-select/)
and MAY support [multiselect 2.0](https://github.com/libp2p/specs/pull/95) when the spec solidifies.
Once all clients have implementations for multiselect 2.0, multistream-select 1.0 MAY be phased out.
### Multiplexing
During connection bootstrapping, libp2p dynamically negotiates a mutually supported multiplexing method to conduct parallel conversations.
This applies to transports that are natively incapable of multiplexing (e.g. TCP, WebSockets, WebRTC),
and is omitted for capable transports (e.g. QUIC).
Two multiplexers are commonplace in libp2p implementations:
[mplex](https://github.com/libp2p/specs/tree/master/mplex) and [yamux](https://github.com/libp2p/specs/blob/master/yamux/README.md).
Their protocol IDs are, respectively: `/mplex/6.7.0` and `/yamux/1.0.0`.
Clients MUST support [mplex](https://github.com/libp2p/specs/tree/master/mplex)
and MAY support [yamux](https://github.com/libp2p/specs/blob/master/yamux/README.md).
If both are supported by the client, yamux MUST take precedence during negotiation.
See the [Rationale](#design-decision-rationale) section below for tradeoffs.
## Consensus-layer network interaction domains
### Custom types
We define the following Python custom types for type hinting and readability:
| Name | SSZ equivalent | Description |
| - | - | - |
| `NodeID` | `uint256` | node identifier |
| `SubnetID` | `uint64` | subnet identifier |
### Constants
| Name | Value | Unit | Duration |
| - | - | :-: | :-: |
| `NODE_ID_BITS` | `256` | The bit length of uint256 is 256 |
### Configuration
This section outlines configurations that are used in this spec.
| Name | Value | Description |
|---|---|---|
| `GOSSIP_MAX_SIZE` | `10 * 2**20` (= 10485760, 10 MiB) | The maximum allowed size of uncompressed gossip messages. |
| `MAX_REQUEST_BLOCKS` | `2**10` (= 1024) | Maximum number of blocks in a single request |
| `EPOCHS_PER_SUBNET_SUBSCRIPTION` | `2**8` (= 256) | Number of epochs on a subnet subscription (~27 hours) |
| `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 |
| `MAX_CHUNK_SIZE` | `10 * 2**20` (=10485760, 10 MiB) | The maximum allowed size of uncompressed req/resp chunked responses. |
| `ATTESTATION_PROPAGATION_SLOT_RANGE` | `32` | The maximum number of slots during which an attestation can be propagated. |
| `MAXIMUM_GOSSIP_CLOCK_DISPARITY` | `500` | The maximum **milliseconds** of clock disparity assumed between honest nodes. |
| `MESSAGE_DOMAIN_INVALID_SNAPPY` | `DomainType('0x00000000')` | 4-byte domain for gossip message-id isolation of *invalid* snappy messages |
| `MESSAGE_DOMAIN_VALID_SNAPPY` | `DomainType('0x01000000')` | 4-byte domain for gossip message-id isolation of *valid* snappy messages |
| `SUBNETS_PER_NODE` | `2` | The number of long-lived subnets a beacon node should be subscribed to. |
| `ATTESTATION_SUBNET_COUNT` | `2**6` (= 64) | The number of attestation subnets used in the gossipsub protocol. |
| `ATTESTATION_SUBNET_EXTRA_BITS` | `0` | The number of extra bits of a NodeId to use when mapping to a subscribed subnet |
| `ATTESTATION_SUBNET_PREFIX_BITS` | `int(ceillog2(ATTESTATION_SUBNET_COUNT) + ATTESTATION_SUBNET_EXTRA_BITS)` | |
| `MAX_CONCURRENT_REQUESTS` | `2` | Maximum number of concurrent requests per protocol ID that a client may issue |
### MetaData
Clients MUST locally store the following `MetaData`:
```
(
seq_number: uint64
attnets: Bitvector[ATTESTATION_SUBNET_COUNT]
)
```
Where
- `seq_number` is a `uint64` starting at `0` used to version the node's metadata.
If any other field in the local `MetaData` changes, the node MUST increment `seq_number` by 1.
- `attnets` is a `Bitvector` representing the node's persistent attestation subnet subscriptions.
*Note*: `MetaData.seq_number` is used for versioning of the node's metadata,
is entirely independent of the ENR sequence number,
and will in most cases be out of sync with the ENR sequence number.
### The gossip domain: gossipsub
Clients MUST support the [gossipsub v1](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.0.md) libp2p Protocol
including the [gossipsub v1.1](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md) extension.
**Protocol ID:** `/meshsub/1.1.0`
**Gossipsub Parameters**
The following gossipsub [parameters](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.0.md#parameters) will be used:
- `D` (topic stable mesh target count): 8
- `D_low` (topic stable mesh low watermark): 6
- `D_high` (topic stable mesh high watermark): 12
- `D_lazy` (gossip target): 6
- `heartbeat_interval` (frequency of heartbeat, seconds): 0.7
- `fanout_ttl` (ttl for fanout maps for topics we are not subscribed to but have published to, seconds): 60
- `mcache_len` (number of windows to retain full messages in cache for `IWANT` responses): 6
- `mcache_gossip` (number of windows to gossip about): 3
- `seen_ttl` (expiry time for cache of seen message ids, seconds): SECONDS_PER_SLOT * SLOTS_PER_EPOCH * 2
*Note*: Gossipsub v1.1 introduces a number of
[additional parameters](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md#overview-of-new-parameters)
for peer scoring and other attack mitigations.
These are currently under investigation and will be spec'd and released to mainnet when they are ready.
#### Topics and messages
Topics are plain UTF-8 strings and are encoded on the wire as determined by protobuf (gossipsub messages are enveloped in protobuf messages).
Topic strings have form: `/eth2/ForkDigestValue/Name/Encoding`.
This defines both the type of data being sent on the topic and how the data field of the message is encoded.
- `ForkDigestValue` - the lowercase hex-encoded (no "0x" prefix) bytes of `compute_fork_digest(current_fork_version, genesis_validators_root)` where
- `current_fork_version` is the fork version of the epoch of the message to be sent on the topic
- `genesis_validators_root` is the static `Root` found in `state.genesis_validators_root`
- `Name` - see table below
- `Encoding` - the encoding strategy describes a specific representation of bytes that will be transmitted over the wire.
See the [Encodings](#Encodings) section for further details.
*Note*: `ForkDigestValue` is composed of values that are not known until the genesis block/state are available.
Due to this, clients SHOULD NOT subscribe to gossipsub topics until these genesis values are known.
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`.
Clients MUST reject (fail validation) messages that are over this size limit.
Likewise, clients MUST NOT emit or propagate messages larger than this limit.
The optional `from` (1), `seqno` (3), `signature` (5) and `key` (6) protobuf fields are omitted from the message,
since messages are identified by content, anonymous, and signed where necessary in the application layer.
Starting from Gossipsub v1.1, clients MUST enforce this by applying the `StrictNoSign`
[signature policy](https://github.com/libp2p/specs/blob/master/pubsub/README.md#signature-policy-options).
The `message-id` of a gossipsub message MUST be the following 20 byte value computed from the message data:
* If `message.data` has a valid snappy decompression, set `message-id` to the first 20 bytes of the `SHA256` hash of
the concatenation of `MESSAGE_DOMAIN_VALID_SNAPPY` with the snappy decompressed message data,
i.e. `SHA256(MESSAGE_DOMAIN_VALID_SNAPPY + snappy_decompress(message.data))[:20]`.
* Otherwise, set `message-id` to the first 20 bytes of the `SHA256` hash of
the concatenation of `MESSAGE_DOMAIN_INVALID_SNAPPY` with the raw message data,
i.e. `SHA256(MESSAGE_DOMAIN_INVALID_SNAPPY + message.data)[:20]`.
*Note*: The above logic handles two exceptional cases:
(1) multiple snappy `data` can decompress to the same value,
and (2) some message `data` can fail to snappy decompress altogether.
The payload is carried in the `data` field of a gossipsub message, and varies depending on the topic:
| Name | Message Type |
|----------------------------------|---------------------------|
| `beacon_block` | `SignedBeaconBlock` |
| `beacon_aggregate_and_proof` | `SignedAggregateAndProof` |
| `beacon_attestation_{subnet_id}` | `Attestation` |
| `voluntary_exit` | `SignedVoluntaryExit` |
| `proposer_slashing` | `ProposerSlashing` |
| `attester_slashing` | `AttesterSlashing` |
Clients MUST reject (fail validation) messages containing an incorrect type, or invalid payload.
When processing incoming gossip, clients MAY descore or disconnect peers who fail to observe these constraints.
For any optional queueing, clients SHOULD maintain maximum queue sizes to avoid DoS vectors.
Gossipsub v1.1 introduces [Extended Validators](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md#extended-validators)
for the application to aid in the gossipsub peer-scoring scheme.
We utilize `ACCEPT`, `REJECT`, and `IGNORE`. For each gossipsub topic, there are application specific validations.
If all validations pass, return `ACCEPT`.
If one or more validations fail while processing the items in order, return either `REJECT` or `IGNORE` as specified in the prefix of the particular condition.
##### Global topics
There are two primary global topics used to propagate beacon blocks (`beacon_block`)
and aggregate attestations (`beacon_aggregate_and_proof`) to all nodes on the network.
There are three additional global topics that are used to propagate lower frequency validator messages
(`voluntary_exit`, `proposer_slashing`, and `attester_slashing`).
###### `beacon_block`
The `beacon_block` topic is used solely for propagating new signed beacon blocks to all nodes on the networks.
Signed blocks are sent in their entirety.
The following validations MUST pass before forwarding the `signed_beacon_block` on the network.
- _[IGNORE]_ The block is not from a future slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) --
i.e. validate that `signed_beacon_block.message.slot <= current_slot`
(a client MAY queue future blocks for processing at the appropriate slot).
- _[IGNORE]_ The block is from a slot greater than the latest finalized slot --
i.e. validate that `signed_beacon_block.message.slot > compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)`
(a client MAY choose to validate and store such blocks for additional purposes -- e.g. slashing detection, archive nodes, etc).
- _[IGNORE]_ The block is the first block with valid signature received for the proposer for the slot, `signed_beacon_block.message.slot`.
- _[REJECT]_ The proposer signature, `signed_beacon_block.signature`, is valid with respect to the `proposer_index` pubkey.
- _[IGNORE]_ The block's parent (defined by `block.parent_root`) has been seen
(via both gossip and non-gossip sources)
(a client MAY queue blocks for processing once the parent block is retrieved).
- _[REJECT]_ The block's parent (defined by `block.parent_root`) passes validation.
- _[REJECT]_ The block is from a higher slot than its parent.
- _[REJECT]_ The current `finalized_checkpoint` is an ancestor of `block` -- i.e.
`get_checkpoint_block(store, block.parent_root, store.finalized_checkpoint.epoch)
== store.finalized_checkpoint.root`
- _[REJECT]_ The block is proposed by the expected `proposer_index` for the block's slot
in the context of the current shuffling (defined by `parent_root`/`slot`).
If the `proposer_index` cannot immediately be verified against the expected shuffling,
the block MAY be queued for later processing while proposers for the block's branch are calculated --
in such a case _do not_ `REJECT`, instead `IGNORE` this message.
###### `beacon_aggregate_and_proof`
The `beacon_aggregate_and_proof` topic is used to propagate aggregated attestations (as `SignedAggregateAndProof`s)
to subscribing nodes (typically validators) to be included in future blocks.
We define the following variables for convenience:
- `aggregate_and_proof = signed_aggregate_and_proof.message`
- `aggregate = aggregate_and_proof.aggregate`
- `index = aggregate.data.index`
- `aggregation_bits = attestation.aggregation_bits`
The following validations MUST pass before forwarding the `signed_aggregate_and_proof` on the network.
- _[REJECT]_ The committee index is within the expected range -- i.e. `index < get_committee_count_per_slot(state, aggregate.data.target.epoch)`.
- _[IGNORE]_ `aggregate.data.slot` is within the last `ATTESTATION_PROPAGATION_SLOT_RANGE` slots (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) --
i.e. `aggregate.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= current_slot >= aggregate.data.slot`
(a client MAY queue future aggregates for processing at the appropriate slot).
- _[REJECT]_ The aggregate attestation's epoch matches its target -- i.e. `aggregate.data.target.epoch ==
compute_epoch_at_slot(aggregate.data.slot)`
- _[REJECT]_ The number of aggregation bits matches the committee size -- i.e.
`len(aggregation_bits) == len(get_beacon_committee(state, aggregate.data.slot, index))`.
- _[REJECT]_ The aggregate attestation has participants --
that is, `len(get_attesting_indices(state, aggregate)) >= 1`.
- _[IGNORE]_ A valid aggregate attestation defined by `hash_tree_root(aggregate.data)` whose `aggregation_bits` is a non-strict superset has _not_ already been seen.
(via aggregate gossip, within a verified block, or through the creation of an equivalent aggregate locally).
- _[IGNORE]_ The `aggregate` is the first valid aggregate received for the aggregator
with index `aggregate_and_proof.aggregator_index` for the epoch `aggregate.data.target.epoch`.
- _[REJECT]_ The attestation has participants -- that is, `len(get_attesting_indices(state, aggregate)) >= 1`.
- _[REJECT]_ `aggregate_and_proof.selection_proof` selects the validator as an aggregator for the slot --
i.e. `is_aggregator(state, aggregate.data.slot, index, aggregate_and_proof.selection_proof)` returns `True`.
- _[REJECT]_ The aggregator's validator index is within the committee --
i.e. `aggregate_and_proof.aggregator_index in get_beacon_committee(state, aggregate.data.slot, index)`.
- _[REJECT]_ The `aggregate_and_proof.selection_proof` is a valid signature
of the `aggregate.data.slot` by the validator with index `aggregate_and_proof.aggregator_index`.
- _[REJECT]_ The aggregator signature, `signed_aggregate_and_proof.signature`, is valid.
- _[REJECT]_ The signature of `aggregate` is valid.
- _[IGNORE]_ The block being voted for (`aggregate.data.beacon_block_root`) has been seen
(via both gossip and non-gossip sources)
(a client MAY queue aggregates for processing once block is retrieved).
- _[REJECT]_ The block being voted for (`aggregate.data.beacon_block_root`) passes validation.
- _[REJECT]_ The aggregate attestation's target block is an ancestor of the block named in the LMD vote -- i.e.
`get_checkpoint_block(store, aggregate.data.beacon_block_root, aggregate.data.target.epoch) == aggregate.data.target.root`
- _[IGNORE]_ The current `finalized_checkpoint` is an ancestor of the `block` defined by `aggregate.data.beacon_block_root` -- i.e.
`get_checkpoint_block(store, aggregate.data.beacon_block_root, finalized_checkpoint.epoch)
== store.finalized_checkpoint.root`
###### `voluntary_exit`
The `voluntary_exit` topic is used solely for propagating signed voluntary validator exits to proposers on the network.
Signed voluntary exits are sent in their entirety.
The following validations MUST pass before forwarding the `signed_voluntary_exit` on to the network.
- _[IGNORE]_ The voluntary exit is the first valid voluntary exit received
for the validator with index `signed_voluntary_exit.message.validator_index`.
- _[REJECT]_ All of the conditions within `process_voluntary_exit` pass validation.
###### `proposer_slashing`
The `proposer_slashing` topic is used solely for propagating proposer slashings to proposers on the network.
Proposer slashings are sent in their entirety.
The following validations MUST pass before forwarding the `proposer_slashing` on to the network.
- _[IGNORE]_ The proposer slashing is the first valid proposer slashing received
for the proposer with index `proposer_slashing.signed_header_1.message.proposer_index`.
- _[REJECT]_ All of the conditions within `process_proposer_slashing` pass validation.
###### `attester_slashing`
The `attester_slashing` topic is used solely for propagating attester slashings to proposers on the network.
Attester slashings are sent in their entirety.
Clients who receive an attester slashing on this topic MUST validate the conditions within `process_attester_slashing` before forwarding it across the network.
- _[IGNORE]_ At least one index in the intersection of the attesting indices of each attestation
has not yet been seen in any prior `attester_slashing`
(i.e. `attester_slashed_indices = set(attestation_1.attesting_indices).intersection(attestation_2.attesting_indices)`,
verify if `any(attester_slashed_indices.difference(prior_seen_attester_slashed_indices))`).
- _[REJECT]_ All of the conditions within `process_attester_slashing` pass validation.
##### Attestation subnets
Attestation subnets are used to propagate unaggregated attestations to subsections of the network.
###### `beacon_attestation_{subnet_id}`
The `beacon_attestation_{subnet_id}` topics are used to propagate unaggregated attestations
to the subnet `subnet_id` (typically beacon and persistent committees) to be aggregated before being gossiped to `beacon_aggregate_and_proof`.
We define the following variables for convenience:
- `index = attestation.data.index`
- `aggregation_bits = attestation.aggregation_bits`
The following validations MUST pass before forwarding the `attestation` on the subnet.
- _[REJECT]_ The committee index is within the expected range -- i.e. `index < get_committee_count_per_slot(state, attestation.data.target.epoch)`.
- _[REJECT]_ The attestation is for the correct subnet --
i.e. `compute_subnet_for_attestation(committees_per_slot, attestation.data.slot, index) == subnet_id`,
where `committees_per_slot = get_committee_count_per_slot(state, attestation.data.target.epoch)`,
which may be pre-computed along with the committee information for the signature check.
- _[IGNORE]_ `attestation.data.slot` is within the last `ATTESTATION_PROPAGATION_SLOT_RANGE` slots
(within a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) --
i.e. `attestation.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= current_slot >= attestation.data.slot`
(a client MAY queue future attestations for processing at the appropriate slot).
- _[REJECT]_ The attestation's epoch matches its target -- i.e. `attestation.data.target.epoch ==
compute_epoch_at_slot(attestation.data.slot)`
- _[REJECT]_ The attestation is unaggregated --
that is, it has exactly one participating validator (`len([bit for bit in aggregation_bits if bit]) == 1`, i.e. exactly 1 bit is set).
- _[REJECT]_ The number of aggregation bits matches the committee size -- i.e.
`len(aggregation_bits) == len(get_beacon_committee(state, attestation.data.slot, index))`.
- _[IGNORE]_ There has been no other valid attestation seen on an attestation subnet
that has an identical `attestation.data.target.epoch` and participating validator index.
- _[REJECT]_ The signature of `attestation` is valid.
- _[IGNORE]_ The block being voted for (`attestation.data.beacon_block_root`) has been seen
(via both gossip and non-gossip sources)
(a client MAY queue attestations for processing once block is retrieved).
- _[REJECT]_ The block being voted for (`attestation.data.beacon_block_root`) passes validation.
- _[REJECT]_ The attestation's target block is an ancestor of the block named in the LMD vote -- i.e.
`get_checkpoint_block(store, attestation.data.beacon_block_root, attestation.data.target.epoch) == attestation.data.target.root`
- _[IGNORE]_ The current `finalized_checkpoint` is an ancestor of the `block` defined by `attestation.data.beacon_block_root` -- i.e.
`get_checkpoint_block(store, attestation.data.beacon_block_root, store.finalized_checkpoint.epoch)
== store.finalized_checkpoint.root`
##### Attestations and Aggregation
Attestation broadcasting is grouped into subnets defined by a topic.
The number of subnets is defined via `ATTESTATION_SUBNET_COUNT`.
The correct subnet for an attestation can be calculated with `compute_subnet_for_attestation`.
`beacon_attestation_{subnet_id}` topics, are rotated through throughout the epoch in a similar fashion to rotating through shards in committees (future beacon chain upgrade).
The subnets are rotated through with `committees_per_slot = get_committee_count_per_slot(state, attestation.data.target.epoch)` subnets per slot.
Unaggregated attestations are sent as `Attestation`s to the subnet topic,
`beacon_attestation_{compute_subnet_for_attestation(committees_per_slot, attestation.data.slot, attestation.data.index)}` as `Attestation`s.
Aggregated attestations are sent to the `beacon_aggregate_and_proof` topic as `AggregateAndProof`s.
#### Encodings
Topics are post-fixed with an encoding. Encodings define how the payload of a gossipsub message is encoded.
- `ssz_snappy` - All objects are SSZ-encoded and then compressed with [Snappy](https://github.com/google/snappy) block compression.
Example: The beacon aggregate attestation topic string is `/eth2/446a7232/beacon_aggregate_and_proof/ssz_snappy`,
the fork digest is `446a7232` and the data field of a gossipsub message is an `AggregateAndProof`
that has been SSZ-encoded and then compressed with Snappy.
Snappy has two formats: "block" and "frames" (streaming).
Gossip messages remain relatively small (100s of bytes to 100s of kilobytes)
so [basic snappy block compression](https://github.com/google/snappy/blob/master/format_description.txt) is used to avoid the additional overhead associated with snappy frames.
Implementations MUST use a single encoding for gossip.
Changing an encoding will require coordination between participating implementations.
### The Req/Resp domain
#### Protocol identification
Each message type is segregated into its own libp2p protocol ID, which is a case-sensitive UTF-8 string of the form:
```
/ProtocolPrefix/MessageName/SchemaVersion/Encoding
```
With:
- `ProtocolPrefix` - messages are grouped into families identified by a shared libp2p protocol name prefix.
In this case, we use `/eth2/beacon_chain/req`.
- `MessageName` - each request is identified by a name consisting of English alphabet, digits and underscores (`_`).
- `SchemaVersion` - an ordinal version number (e.g. 1, 2, 3…).
Each schema is versioned to facilitate backward and forward-compatibility when possible.
- `Encoding` - while the schema defines the data types in more abstract terms,
the encoding strategy describes a specific representation of bytes that will be transmitted over the wire.
See the [Encodings](#Encoding-strategies) section for further details.
This protocol segregation allows libp2p `multistream-select 1.0` / `multiselect 2.0`
to handle the request type, version, and encoding negotiation before establishing the underlying streams.
#### Req/Resp interaction
We use ONE stream PER request/response interaction.
Streams are closed when the interaction finishes, whether in success or in error.
Request/response messages MUST adhere to the encoding specified in the protocol name and follow this structure (relaxed BNF grammar):
```
request ::= <encoding-dependent-header> | <encoded-payload>
response ::= <response_chunk>*
response_chunk ::= <result> | <encoding-dependent-header> | <encoded-payload>
result ::= “0” | “1” | “2” | [“128” ... ”255”]
```
The encoding-dependent header may carry metadata or assertions such as the encoded payload length, for integrity and attack proofing purposes.
Because req/resp streams are single-use and stream closures implicitly delimit the boundaries, it is not strictly necessary to length-prefix payloads;
however, certain encodings like SSZ do, for added security.
A `response` is formed by zero or more `response_chunk`s.
Responses that consist of a single SSZ-list (such as `BlocksByRange` and `BlocksByRoot`) send each list item as a `response_chunk`.
All other response types (non-Lists) send a single `response_chunk`.
For both `request`s and `response`s, the `encoding-dependent-header` MUST be valid,
and the `encoded-payload` must be valid within the constraints of the `encoding-dependent-header`.
This includes type-specific bounds on payload size for some encoding strategies.
Regardless of these type specific bounds, a global maximum uncompressed byte size of `MAX_CHUNK_SIZE` MUST be applied to all method response chunks.
Clients MUST ensure that lengths are within these bounds; if not, they SHOULD reset the stream immediately.
Clients tracking peer reputation MAY decrement the score of the misbehaving peer under this circumstance.
##### Requesting side
Once a new stream with the protocol ID for the request type has been negotiated, the full request message SHOULD be sent immediately.
The request MUST be encoded according to the encoding strategy.
The requester MUST close the write side of the stream once it finishes writing the request message.
At this point, the stream will be half-closed.
The requester MUST NOT make more than `MAX_CONCURRENT_REQUESTS` concurrent requests with the same protocol ID.
If a timeout occurs or the response is no longer relevant, the requester SHOULD reset the stream.
A requester SHOULD read from the stream until either:
1. An error result is received in one of the chunks (the error payload MAY be read before stopping).
2. The responder closes the stream.
3. Any part of the `response_chunk` fails validation.
4. The maximum number of requested chunks are read.
For requests consisting of a single valid `response_chunk`,
the requester SHOULD read the chunk fully, as defined by the `encoding-dependent-header`, before closing the stream.
##### Responding side
Once a new stream with the protocol ID for the request type has been negotiated,
the responder SHOULD process the incoming request and MUST validate it before processing it.
Request processing and validation MUST be done according to the encoding strategy, until EOF (denoting stream half-closure by the requester).
The responder MUST:
1. Use the encoding strategy to read the optional header.
2. If there are any length assertions for length `N`, it should read exactly `N` bytes from the stream, at which point an EOF should arise (no more bytes).
Should this not be the case, it should be treated as a failure.
3. Deserialize the expected type, and process the request.
4. Write the response which may consist of zero or more `response_chunk`s (result, optional header, payload).
5. Close their write side of the stream. At this point, the stream will be fully closed.
If steps (1), (2), or (3) fail due to invalid, malformed, or inconsistent data, the responder MUST respond in error.
Clients tracking peer reputation MAY record such failures, as well as unexpected events, e.g. early stream resets.
The responder MAY rate-limit chunks by withholding each chunk until capacity is available. The responder MUST NOT respond with an error or close the stream when rate limiting.
When rate limiting, the responder MUST send each `response_chunk` in full promptly but may introduce delays between each chunk.
Chunks start with a **single-byte** response code which determines the contents of the `response_chunk` (`result` particle in the BNF grammar above).
For multiple chunks, only the last chunk is allowed to have a non-zero error code (i.e. The chunk stream is terminated once an error occurs).
The response code can have one of the following values, encoded as a single unsigned byte:
- 0: **Success** -- a normal response follows, with contents matching the expected message schema and encoding specified in the request.
- 1: **InvalidRequest** -- the contents of the request are semantically invalid, or the payload is malformed, or could not be understood.
The response payload adheres to the `ErrorMessage` schema (described below).
- 2: **ServerError** -- the responder encountered an error while processing the request.
The response payload adheres to the `ErrorMessage` schema (described below).
- 3: **ResourceUnavailable** -- the responder does not have requested resource.
The response payload adheres to the `ErrorMessage` schema (described below).
*Note*: This response code is only valid as a response where specified.
Clients MAY use response codes above `128` to indicate alternative, erroneous request-specific responses.
The range `[4, 127]` is RESERVED for future usages, and should be treated as error if not recognized expressly.
The `ErrorMessage` schema is:
```
(
error_message: List[byte, 256]
)
```
*Note*: By convention, the `error_message` is a sequence of bytes that MAY be interpreted as a UTF-8 string (for debugging purposes).
Clients MUST treat as valid any byte sequences.
The responder MAY penalise peers that concurrently open more than `MAX_CONCURRENT_REQUESTS` streams for the same request type, for the protocol IDs defined in this specification.
#### Encoding strategies
The token of the negotiated protocol ID specifies the type of encoding to be used for the req/resp interaction.
Only one value is possible at this time:
- `ssz_snappy`: The contents are first [SSZ-encoded](../../ssz/simple-serialize.md)
and then compressed with [Snappy](https://github.com/google/snappy) frames compression.
For objects containing a single field, only the field is SSZ-encoded not a container with a single field.
For example, the `BeaconBlocksByRoot` request is an SSZ-encoded list of `Root`'s.
This encoding type MUST be supported by all clients.
##### SSZ-snappy encoding strategy
The [SimpleSerialize (SSZ) specification](../../ssz/simple-serialize.md) outlines how objects are SSZ-encoded.
To achieve snappy encoding on top of SSZ, we feed the serialized form of the object to the Snappy compressor on encoding.
The inverse happens on decoding.
Snappy has two formats: "block" and "frames" (streaming).
To support large requests and response chunks, snappy-framing is used.
Since snappy frame contents [have a maximum size of `65536` bytes](https://github.com/google/snappy/blob/master/framing_format.txt#L104)
and frame headers are just `identifier (1) + checksum (4)` bytes, the expected buffering of a single frame is acceptable.
**Encoding-dependent header:** Req/Resp protocols using the `ssz_snappy` encoding strategy MUST encode the length of the raw SSZ bytes,
encoded as an unsigned [protobuf varint](https://developers.google.com/protocol-buffers/docs/encoding#varints).
*Writing*: By first computing and writing the SSZ byte length, the SSZ encoder can then directly write the chunk contents to the stream.
When Snappy is applied, it can be passed through a buffered Snappy writer to compress frame by frame.
*Reading*: After reading the expected SSZ byte length, the SSZ decoder can directly read the contents from the stream.
When snappy is applied, it can be passed through a buffered Snappy reader to decompress frame by frame.
Before reading the payload, the header MUST be validated:
- The unsigned protobuf varint used for the length-prefix MUST not be longer than 10 bytes, which is sufficient for any `uint64`.
- The length-prefix is within the expected [size bounds derived from the payload SSZ type](#what-are-ssz-type-size-bounds).
After reading a valid header, the payload MAY be read, while maintaining the size constraints from the header.
A reader SHOULD NOT read more than `max_encoded_len(n)` bytes after reading the SSZ length-prefix `n` from the header.
- For `ssz_snappy` this is: `32 + n + n // 6`.
This is considered the [worst-case compression result](https://github.com/google/snappy/blob/537f4ad6240e586970fe554614542e9717df7902/snappy.cc#L98) by Snappy.
A reader SHOULD consider the following cases as invalid input:
- Any remaining bytes, after having read the `n` SSZ bytes. An EOF is expected if more bytes are read than required.
- An early EOF, before fully reading the declared length-prefix worth of SSZ bytes.
In case of an invalid input (header or payload), a reader MUST:
- From requests: send back an error message, response code `InvalidRequest`. The request itself is ignored.
- From responses: ignore the response, the response MUST be considered bad server behavior.
All messages that contain only a single field MUST be encoded directly as the type of that field and MUST NOT be encoded as an SSZ container.
Responses that are SSZ-lists (for example `List[SignedBeaconBlock, ...]`) send their
constituents individually as `response_chunk`s. For example, the
`List[SignedBeaconBlock, ...]` response type sends zero or more `response_chunk`s.
Each _successful_ `response_chunk` contains a single `SignedBeaconBlock` payload.
#### Messages
##### Status
**Protocol ID:** ``/eth2/beacon_chain/req/status/1/``
Request, Response Content:
```
(
fork_digest: ForkDigest
finalized_root: Root
finalized_epoch: Epoch
head_root: Root
head_slot: Slot
)
```
The fields are, as seen by the client at the time of sending the message:
- `fork_digest`: The node's `ForkDigest` (`compute_fork_digest(current_fork_version, genesis_validators_root)`) where
- `current_fork_version` is the fork version at the node's current epoch defined by the wall-clock time
(not necessarily the epoch to which the node is sync)
- `genesis_validators_root` is the static `Root` found in `state.genesis_validators_root`
- `finalized_root`: `store.finalized_checkpoint.root` according to [fork choice](./fork-choice.md).
(Note this defaults to `Root(b'\x00' * 32)` for the genesis finalized checkpoint).
- `finalized_epoch`: `store.finalized_checkpoint.epoch` according to [fork choice](./fork-choice.md).
- `head_root`: The `hash_tree_root` root of the current head block (`BeaconBlock`).
- `head_slot`: The slot of the block corresponding to the `head_root`.
The dialing client MUST send a `Status` request upon connection.
The request/response MUST be encoded as an SSZ-container.
The response MUST consist of a single `response_chunk`.
Clients SHOULD immediately disconnect from one another following the handshake above under the following conditions:
1. If `fork_digest` does not match the node's local `fork_digest`, since the client’s chain is on another fork.
2. If the (`finalized_root`, `finalized_epoch`) shared by the peer is not in the client's chain at the expected epoch.
For example, if Peer 1 sends (root, epoch) of (A, 5) and Peer 2 sends (B, 3) but Peer 1 has root C at epoch 3,
then Peer 1 would disconnect because it knows that their chains are irreparably disjoint.
Once the handshake completes, the client with the lower `finalized_epoch` or `head_slot` (if the clients have equal `finalized_epoch`s)
SHOULD request beacon blocks from its counterparty via the `BeaconBlocksByRange` request.
*Note*: Under abnormal network condition or after some rounds of `BeaconBlocksByRange` requests,
the client might need to send `Status` request again to learn if the peer has a higher head.
Implementers are free to implement such behavior in their own way.
##### Goodbye
**Protocol ID:** ``/eth2/beacon_chain/req/goodbye/1/``
Request, Response Content:
```
(
uint64
)
```
Client MAY send goodbye messages upon disconnection. The reason field MAY be one of the following values:
- 1: Client shut down.
- 2: Irrelevant network.
- 3: Fault/error.
Clients MAY use reason codes above `128` to indicate alternative, erroneous request-specific responses.
The range `[4, 127]` is RESERVED for future usage.
The request/response MUST be encoded as a single SSZ-field.
The response MUST consist of a single `response_chunk`.
##### BeaconBlocksByRange
**Protocol ID:** `/eth2/beacon_chain/req/beacon_blocks_by_range/1/`
Request Content:
```
(
start_slot: Slot
count: uint64
step: uint64 # Deprecated, must be set to 1
)
```
Response Content:
```
(
List[SignedBeaconBlock, MAX_REQUEST_BLOCKS]
)
```
Requests beacon blocks in the slot range `[start_slot, start_slot + count)`, leading up to the current head block as selected by fork choice.
For example, requesting blocks starting at `start_slot=2` and `count=4` would return the blocks at slots `[2, 3, 4, 5]`.
In cases where a slot is empty for a given slot number, no block is returned.
For example, if slot 4 were empty in the previous example, the returned array would contain `[2, 3, 5]`.
`step` is deprecated and must be set to 1. Clients may respond with a single block if a larger step is returned during the deprecation transition period.
`/eth2/beacon_chain/req/beacon_blocks_by_range/1/` is deprecated. Clients MAY respond with an empty list during the deprecation transition period.
`BeaconBlocksByRange` is primarily used to sync historical blocks.
The request MUST be encoded as an SSZ-container.
The response MUST consist of zero or more `response_chunk`.
Each _successful_ `response_chunk` MUST contain a single `SignedBeaconBlock` payload.
Clients MUST keep a record of signed blocks seen on the epoch range
`[max(GENESIS_EPOCH, current_epoch - MIN_EPOCHS_FOR_BLOCK_REQUESTS), current_epoch]`
where `current_epoch` is defined by the current wall-clock time,
and clients MUST support serving requests of blocks on this range.
Peers that are unable to reply to block requests within the `MIN_EPOCHS_FOR_BLOCK_REQUESTS`
epoch range SHOULD respond with error code `3: ResourceUnavailable`.
Such peers that are unable to successfully reply to this range of requests MAY get descored
or disconnected at any time.
*Note*: The above requirement implies that nodes that start from a recent weak subjectivity checkpoint
MUST backfill the local block database to at least epoch `current_epoch - MIN_EPOCHS_FOR_BLOCK_REQUESTS`
to be fully compliant with `BlocksByRange` requests. To safely perform such a
backfill of blocks to the recent state, the node MUST validate both (1) the
proposer signatures and (2) that the blocks form a valid chain up to the most
recent block referenced in the weak subjectivity state.
*Note*: Although clients that bootstrap from a weak subjectivity checkpoint can begin
participating in the networking immediately, other peers MAY
disconnect and/or temporarily ban such an un-synced or semi-synced client.
Clients MUST respond with at least the first block that exists in the range, if they have it,
and no more than `MAX_REQUEST_BLOCKS` blocks.
The following blocks, where they exist, MUST be sent in consecutive order.
Clients MAY limit the number of blocks in the response.
The response MUST contain no more than `count` blocks.
Clients MUST respond with blocks from their view of the current fork choice
-- that is, blocks from the single chain defined by the current head.
Of note, blocks from slots before the finalization MUST lead to the finalized block reported in the `Status` handshake.
Clients MUST respond with blocks that are consistent from a single chain within the context of the request.
This applies to any `step` value.
In particular when `step == 1`, each `parent_root` MUST match the `hash_tree_root` of the preceding block.
After the initial block, clients MAY stop in the process of responding
if their fork choice changes the view of the chain in the context of the request.
##### BeaconBlocksByRoot
**Protocol ID:** `/eth2/beacon_chain/req/beacon_blocks_by_root/1/`
Request Content:
```
(
List[Root, MAX_REQUEST_BLOCKS]
)
```
Response Content:
```
(
List[SignedBeaconBlock, MAX_REQUEST_BLOCKS]
)
```
Requests blocks by block root (= `hash_tree_root(SignedBeaconBlock.message)`).
The response is a list of `SignedBeaconBlock` whose length is less than or equal to the number of requested blocks.
It may be less in the case that the responding peer is missing blocks.
No more than `MAX_REQUEST_BLOCKS` may be requested at a time.
`BeaconBlocksByRoot` is primarily used to recover recent blocks (e.g. when receiving a block or attestation whose parent is unknown).
The request MUST be encoded as an SSZ-field.
The response MUST consist of zero or more `response_chunk`.
Each _successful_ `response_chunk` MUST contain a single `SignedBeaconBlock` payload.
Clients MUST support requesting blocks since the latest finalized epoch.
Clients MUST respond with at least one block, if they have it.
Clients MAY limit the number of blocks in the response.
Clients MAY include a block in the response as soon as it passes the gossip validation rules.
Clients SHOULD NOT respond with blocks that fail the beacon chain state transition.
`/eth2/beacon_chain/req/beacon_blocks_by_root/1/` is deprecated. Clients MAY respond with an empty list during the deprecation transition period.
##### Ping
**Protocol ID:** `/eth2/beacon_chain/req/ping/1/`
Request Content:
```
(
uint64
)
```
Response Content:
```
(
uint64
)
```
Sent intermittently, the `Ping` protocol checks liveness of connected peers.
Peers request and respond with their local metadata sequence number (`MetaData.seq_number`).
If the peer does not respond to the `Ping` request, the client MAY disconnect from the peer.
A client can then determine if their local record of a peer's MetaData is up to date
and MAY request an updated version via the `MetaData` RPC method if not.
The request MUST be encoded as an SSZ-field.
The response MUST consist of a single `response_chunk`.
##### GetMetaData
**Protocol ID:** `/eth2/beacon_chain/req/metadata/1/`
No Request Content.
Response Content:
```
(
MetaData
)
```
Requests the MetaData of a peer.
The request opens and negotiates the stream without sending any request content.
Once established the receiving peer responds with
it's local most up-to-date MetaData.
The response MUST be encoded as an SSZ-container.
The response MUST consist of a single `response_chunk`.
### The discovery domain: discv5
Discovery Version 5 ([discv5](https://github.com/ethereum/devp2p/blob/master/discv5/discv5.md)) (Protocol version v5.1) is used for peer discovery.
`discv5` is a standalone protocol, running on UDP on a dedicated port, meant for peer discovery only.
`discv5` supports self-certified, flexible peer records (ENRs) and topic-based advertisement, both of which are (or will be) requirements in this context.
#### Integration into libp2p stacks
`discv5` SHOULD be integrated into the client’s libp2p stack by implementing an adaptor
to make it conform to the [service discovery](https://github.com/libp2p/go-libp2p-core/blob/master/discovery/discovery.go)
and [peer routing](https://github.com/libp2p/go-libp2p-core/blob/master/routing/routing.go#L36-L44) abstractions and interfaces (go-libp2p links provided).
Inputs to operations include peer IDs (when locating a specific peer) or capabilities (when searching for peers with a specific capability),
and the outputs will be multiaddrs converted from the ENR records returned by the discv5 backend.
This integration enables the libp2p stack to subsequently form connections and streams with discovered peers.
#### ENR structure
The Ethereum Node Record (ENR) for an Ethereum consensus client MUST contain the following entries
(exclusive of the sequence number and signature, which MUST be present in an ENR):
- The compressed secp256k1 publickey, 33 bytes (`secp256k1` field).
The ENR MAY contain the following entries:
- An IPv4 address (`ip` field) and/or IPv6 address (`ip6` field).
- A TCP port (`tcp` field) representing the local libp2p TCP listening port.
- A QUIC port (`quic` field) representing the local libp2p QUIC (UDP) listening port.
- A UDP port (`udp` field) representing the local discv5 listening port.
Specifications of these parameters can be found in the [ENR Specification](http://eips.ethereum.org/EIPS/eip-778).
##### Attestation subnet bitfield
The ENR `attnets` entry signifies the attestation subnet bitfield with the following form
to more easily discover peers participating in particular attestation gossip subnets.
| Key | Value |
|:-------------|:-------------------------------------------------|
| `attnets` | SSZ `Bitvector[ATTESTATION_SUBNET_COUNT]` |
If a node's `MetaData.attnets` has any non-zero bit, the ENR MUST include the `attnets` entry with the same value as `MetaData.attnets`.
If a node's `MetaData.attnets` is composed of all zeros, the ENR MAY optionally include the `attnets` entry or leave it out entirely.
##### `eth2` field
ENRs MUST carry a generic `eth2` key with an 16-byte value of the node's current fork digest, next fork version,
and next fork epoch to ensure connections are made with peers on the intended Ethereum network.
| Key | Value |
|:-------------|:--------------------|
| `eth2` | SSZ `ENRForkID` |
Specifically, the value of the `eth2` key MUST be the following SSZ encoded object (`ENRForkID`)
```
(
fork_digest: ForkDigest
next_fork_version: Version
next_fork_epoch: Epoch