Skip to content

Commit 9803d69

Browse files
authored
Implement status v2 version (#7590)
N/A Implements status v2 as defined in ethereum/consensus-specs#4374
1 parent 5f208bb commit 9803d69

File tree

13 files changed

+208
-78
lines changed

13 files changed

+208
-78
lines changed

beacon_node/lighthouse_network/src/peer_manager/peerdb.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,7 @@ impl<E: EthSpec> PeerDB<E> {
746746
head_root: Hash256::ZERO,
747747
finalized_epoch: Epoch::new(0),
748748
finalized_root: Hash256::ZERO,
749+
earliest_available_slot: Some(Slot::new(0)),
749750
},
750751
},
751752
);

beacon_node/lighthouse_network/src/peer_manager/peerdb/sync_status.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub struct SyncInfo {
2525
pub head_root: Hash256,
2626
pub finalized_epoch: Epoch,
2727
pub finalized_root: Hash256,
28+
pub earliest_available_slot: Option<Slot>,
2829
}
2930

3031
impl std::cmp::PartialEq for SyncStatus {

beacon_node/lighthouse_network/src/rpc/codec.rs

Lines changed: 81 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,13 @@ impl<E: EthSpec> SSZSnappyInboundCodec<E> {
6767
) -> Result<(), RPCError> {
6868
let bytes = match &item {
6969
RpcResponse::Success(resp) => match &resp {
70-
RpcSuccessResponse::Status(res) => res.as_ssz_bytes(),
70+
RpcSuccessResponse::Status(res) => match self.protocol.versioned_protocol {
71+
SupportedProtocol::StatusV1 => res.status_v1().as_ssz_bytes(),
72+
SupportedProtocol::StatusV2 => res.status_v2().as_ssz_bytes(),
73+
_ => {
74+
unreachable!("We only send status responses on negotiating status protocol")
75+
}
76+
},
7177
RpcSuccessResponse::BlocksByRange(res) => res.as_ssz_bytes(),
7278
RpcSuccessResponse::BlocksByRoot(res) => res.as_ssz_bytes(),
7379
RpcSuccessResponse::BlobsByRange(res) => res.as_ssz_bytes(),
@@ -329,7 +335,16 @@ impl<E: EthSpec> Encoder<RequestType<E>> for SSZSnappyOutboundCodec<E> {
329335

330336
fn encode(&mut self, item: RequestType<E>, dst: &mut BytesMut) -> Result<(), Self::Error> {
331337
let bytes = match item {
332-
RequestType::Status(req) => req.as_ssz_bytes(),
338+
RequestType::Status(req) => {
339+
// Send the status message based on the negotiated protocol
340+
match self.protocol.versioned_protocol {
341+
SupportedProtocol::StatusV1 => req.status_v1().as_ssz_bytes(),
342+
SupportedProtocol::StatusV2 => req.status_v2().as_ssz_bytes(),
343+
_ => {
344+
unreachable!("We only send status requests on negotiating status protocol")
345+
}
346+
}
347+
}
333348
RequestType::Goodbye(req) => req.as_ssz_bytes(),
334349
RequestType::BlocksByRange(r) => match r {
335350
OldBlocksByRangeRequest::V1(req) => req.as_ssz_bytes(),
@@ -553,9 +568,12 @@ fn handle_rpc_request<E: EthSpec>(
553568
spec: &ChainSpec,
554569
) -> Result<Option<RequestType<E>>, RPCError> {
555570
match versioned_protocol {
556-
SupportedProtocol::StatusV1 => Ok(Some(RequestType::Status(
557-
StatusMessage::from_ssz_bytes(decoded_buffer)?,
558-
))),
571+
SupportedProtocol::StatusV1 => Ok(Some(RequestType::Status(StatusMessage::V1(
572+
StatusMessageV1::from_ssz_bytes(decoded_buffer)?,
573+
)))),
574+
SupportedProtocol::StatusV2 => Ok(Some(RequestType::Status(StatusMessage::V2(
575+
StatusMessageV2::from_ssz_bytes(decoded_buffer)?,
576+
)))),
559577
SupportedProtocol::GoodbyeV1 => Ok(Some(RequestType::Goodbye(
560578
GoodbyeReason::from_ssz_bytes(decoded_buffer)?,
561579
))),
@@ -666,9 +684,12 @@ fn handle_rpc_response<E: EthSpec>(
666684
fork_name: Option<ForkName>,
667685
) -> Result<Option<RpcSuccessResponse<E>>, RPCError> {
668686
match versioned_protocol {
669-
SupportedProtocol::StatusV1 => Ok(Some(RpcSuccessResponse::Status(
670-
StatusMessage::from_ssz_bytes(decoded_buffer)?,
671-
))),
687+
SupportedProtocol::StatusV1 => Ok(Some(RpcSuccessResponse::Status(StatusMessage::V1(
688+
StatusMessageV1::from_ssz_bytes(decoded_buffer)?,
689+
)))),
690+
SupportedProtocol::StatusV2 => Ok(Some(RpcSuccessResponse::Status(StatusMessage::V2(
691+
StatusMessageV2::from_ssz_bytes(decoded_buffer)?,
692+
)))),
672693
// This case should be unreachable as `Goodbye` has no response.
673694
SupportedProtocol::GoodbyeV1 => Err(RPCError::InvalidData(
674695
"Goodbye RPC message has no valid response".to_string(),
@@ -1036,14 +1057,25 @@ mod tests {
10361057
SignedBeaconBlock::from_block(block, Signature::empty())
10371058
}
10381059

1039-
fn status_message() -> StatusMessage {
1040-
StatusMessage {
1060+
fn status_message_v1() -> StatusMessage {
1061+
StatusMessage::V1(StatusMessageV1 {
10411062
fork_digest: [0; 4],
10421063
finalized_root: Hash256::zero(),
10431064
finalized_epoch: Epoch::new(1),
10441065
head_root: Hash256::zero(),
10451066
head_slot: Slot::new(1),
1046-
}
1067+
})
1068+
}
1069+
1070+
fn status_message_v2() -> StatusMessage {
1071+
StatusMessage::V2(StatusMessageV2 {
1072+
fork_digest: [0; 4],
1073+
finalized_root: Hash256::zero(),
1074+
finalized_epoch: Epoch::new(1),
1075+
head_root: Hash256::zero(),
1076+
head_slot: Slot::new(1),
1077+
earliest_available_slot: Slot::new(0),
1078+
})
10471079
}
10481080

10491081
fn bbrange_request_v1() -> OldBlocksByRangeRequest {
@@ -1284,11 +1316,22 @@ mod tests {
12841316
assert_eq!(
12851317
encode_then_decode_response(
12861318
SupportedProtocol::StatusV1,
1287-
RpcResponse::Success(RpcSuccessResponse::Status(status_message())),
1319+
RpcResponse::Success(RpcSuccessResponse::Status(status_message_v1())),
12881320
ForkName::Base,
12891321
&chain_spec,
12901322
),
1291-
Ok(Some(RpcSuccessResponse::Status(status_message())))
1323+
Ok(Some(RpcSuccessResponse::Status(status_message_v1())))
1324+
);
1325+
1326+
// A StatusV2 still encodes as a StatusV1 since version is Version::V1
1327+
assert_eq!(
1328+
encode_then_decode_response(
1329+
SupportedProtocol::StatusV1,
1330+
RpcResponse::Success(RpcSuccessResponse::Status(status_message_v2())),
1331+
ForkName::Fulu,
1332+
&chain_spec,
1333+
),
1334+
Ok(Some(RpcSuccessResponse::Status(status_message_v1())))
12921335
);
12931336

12941337
assert_eq!(
@@ -1716,6 +1759,27 @@ mod tests {
17161759
),
17171760
Ok(Some(RpcSuccessResponse::MetaData(metadata_v2())))
17181761
);
1762+
1763+
// A StatusV1 still encodes as a StatusV2 since version is Version::V2
1764+
assert_eq!(
1765+
encode_then_decode_response(
1766+
SupportedProtocol::StatusV2,
1767+
RpcResponse::Success(RpcSuccessResponse::Status(status_message_v1())),
1768+
ForkName::Fulu,
1769+
&chain_spec,
1770+
),
1771+
Ok(Some(RpcSuccessResponse::Status(status_message_v2())))
1772+
);
1773+
1774+
assert_eq!(
1775+
encode_then_decode_response(
1776+
SupportedProtocol::StatusV2,
1777+
RpcResponse::Success(RpcSuccessResponse::Status(status_message_v2())),
1778+
ForkName::Fulu,
1779+
&chain_spec,
1780+
),
1781+
Ok(Some(RpcSuccessResponse::Status(status_message_v2())))
1782+
);
17191783
}
17201784

17211785
// Test RPCResponse encoding/decoding for V2 messages
@@ -1901,7 +1965,8 @@ mod tests {
19011965

19021966
let requests: &[RequestType<Spec>] = &[
19031967
RequestType::Ping(ping_message()),
1904-
RequestType::Status(status_message()),
1968+
RequestType::Status(status_message_v1()),
1969+
RequestType::Status(status_message_v2()),
19051970
RequestType::Goodbye(GoodbyeReason::Fault),
19061971
RequestType::BlocksByRange(bbrange_request_v1()),
19071972
RequestType::BlocksByRange(bbrange_request_v2()),
@@ -1948,7 +2013,7 @@ mod tests {
19482013
let malicious_padding: &'static [u8] = b"\xFE\x00\x00\x00";
19492014

19502015
// Status message is 84 bytes uncompressed. `max_compressed_len` is 32 + 84 + 84/6 = 130.
1951-
let status_message_bytes = StatusMessage {
2016+
let status_message_bytes = StatusMessageV1 {
19522017
fork_digest: [0; 4],
19532018
finalized_root: Hash256::zero(),
19542019
finalized_epoch: Epoch::new(1),
@@ -2071,7 +2136,7 @@ mod tests {
20712136
assert_eq!(stream_identifier.len(), 10);
20722137

20732138
// Status message is 84 bytes uncompressed. `max_compressed_len` is 32 + 84 + 84/6 = 130.
2074-
let status_message_bytes = StatusMessage {
2139+
let status_message_bytes = StatusMessageV1 {
20752140
fork_digest: [0; 4],
20762141
finalized_root: Hash256::zero(),
20772142
finalized_epoch: Epoch::new(1),

beacon_node/lighthouse_network/src/rpc/methods.rs

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,11 @@ impl Display for ErrorType {
6363
/* Requests */
6464

6565
/// The STATUS request/response handshake message.
66-
#[derive(Encode, Decode, Clone, Debug, PartialEq)]
66+
#[superstruct(
67+
variants(V1, V2),
68+
variant_attributes(derive(Encode, Decode, Clone, Debug, PartialEq),)
69+
)]
70+
#[derive(Clone, Debug, PartialEq)]
6771
pub struct StatusMessage {
6872
/// The fork version of the chain we are broadcasting.
6973
pub fork_digest: [u8; 4],
@@ -79,6 +83,43 @@ pub struct StatusMessage {
7983

8084
/// The slot associated with the latest block root.
8185
pub head_slot: Slot,
86+
87+
/// The slot after which we guarantee to have all the blocks
88+
/// and blobs/data columns that we currently advertise.
89+
#[superstruct(only(V2))]
90+
pub earliest_available_slot: Slot,
91+
}
92+
93+
impl StatusMessage {
94+
pub fn status_v1(&self) -> StatusMessageV1 {
95+
match &self {
96+
Self::V1(status) => status.clone(),
97+
Self::V2(status) => StatusMessageV1 {
98+
fork_digest: status.fork_digest,
99+
finalized_root: status.finalized_root,
100+
finalized_epoch: status.finalized_epoch,
101+
head_root: status.head_root,
102+
head_slot: status.head_slot,
103+
},
104+
}
105+
}
106+
107+
pub fn status_v2(&self) -> StatusMessageV2 {
108+
match &self {
109+
Self::V1(status) => StatusMessageV2 {
110+
fork_digest: status.fork_digest,
111+
finalized_root: status.finalized_root,
112+
finalized_epoch: status.finalized_epoch,
113+
head_root: status.head_root,
114+
head_slot: status.head_slot,
115+
// Note: we always produce a V2 message as our local
116+
// status message, so this match arm should ideally never
117+
// be invoked in lighthouse.
118+
earliest_available_slot: Slot::new(0),
119+
},
120+
Self::V2(status) => status.clone(),
121+
}
122+
}
82123
}
83124

84125
/// The PING request/response message.
@@ -726,7 +767,7 @@ impl std::fmt::Display for RpcErrorResponse {
726767

727768
impl std::fmt::Display for StatusMessage {
728769
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
729-
write!(f, "Status Message: Fork Digest: {:?}, Finalized Root: {}, Finalized Epoch: {}, Head Root: {}, Head Slot: {}", self.fork_digest, self.finalized_root, self.finalized_epoch, self.head_root, self.head_slot)
770+
write!(f, "Status Message: Fork Digest: {:?}, Finalized Root: {}, Finalized Epoch: {}, Head Root: {}, Head Slot: {}, Earliest available slot: {:?}", self.fork_digest(), self.finalized_root(), self.finalized_epoch(), self.head_root(), self.head_slot(), self.earliest_available_slot())
730771
}
731772
}
732773

beacon_node/lighthouse_network/src/rpc/protocol.rs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ pub enum Encoding {
298298
#[derive(Debug, Clone, Copy, PartialEq)]
299299
pub enum SupportedProtocol {
300300
StatusV1,
301+
StatusV2,
301302
GoodbyeV1,
302303
BlocksByRangeV1,
303304
BlocksByRangeV2,
@@ -321,6 +322,7 @@ impl SupportedProtocol {
321322
pub fn version_string(&self) -> &'static str {
322323
match self {
323324
SupportedProtocol::StatusV1 => "1",
325+
SupportedProtocol::StatusV2 => "2",
324326
SupportedProtocol::GoodbyeV1 => "1",
325327
SupportedProtocol::BlocksByRangeV1 => "1",
326328
SupportedProtocol::BlocksByRangeV2 => "2",
@@ -344,6 +346,7 @@ impl SupportedProtocol {
344346
pub fn protocol(&self) -> Protocol {
345347
match self {
346348
SupportedProtocol::StatusV1 => Protocol::Status,
349+
SupportedProtocol::StatusV2 => Protocol::Status,
347350
SupportedProtocol::GoodbyeV1 => Protocol::Goodbye,
348351
SupportedProtocol::BlocksByRangeV1 => Protocol::BlocksByRange,
349352
SupportedProtocol::BlocksByRangeV2 => Protocol::BlocksByRange,
@@ -368,6 +371,7 @@ impl SupportedProtocol {
368371

369372
fn currently_supported(fork_context: &ForkContext) -> Vec<ProtocolId> {
370373
let mut supported = vec![
374+
ProtocolId::new(Self::StatusV2, Encoding::SSZSnappy),
371375
ProtocolId::new(Self::StatusV1, Encoding::SSZSnappy),
372376
ProtocolId::new(Self::GoodbyeV1, Encoding::SSZSnappy),
373377
// V2 variants have higher preference then V1
@@ -492,8 +496,8 @@ impl ProtocolId {
492496
pub fn rpc_request_limits(&self, spec: &ChainSpec) -> RpcLimits {
493497
match self.versioned_protocol.protocol() {
494498
Protocol::Status => RpcLimits::new(
495-
<StatusMessage as Encode>::ssz_fixed_len(),
496-
<StatusMessage as Encode>::ssz_fixed_len(),
499+
<StatusMessageV1 as Encode>::ssz_fixed_len(),
500+
<StatusMessageV2 as Encode>::ssz_fixed_len(),
497501
),
498502
Protocol::Goodbye => RpcLimits::new(
499503
<GoodbyeReason as Encode>::ssz_fixed_len(),
@@ -537,8 +541,8 @@ impl ProtocolId {
537541
pub fn rpc_response_limits<E: EthSpec>(&self, fork_context: &ForkContext) -> RpcLimits {
538542
match self.versioned_protocol.protocol() {
539543
Protocol::Status => RpcLimits::new(
540-
<StatusMessage as Encode>::ssz_fixed_len(),
541-
<StatusMessage as Encode>::ssz_fixed_len(),
544+
<StatusMessageV1 as Encode>::ssz_fixed_len(),
545+
<StatusMessageV2 as Encode>::ssz_fixed_len(),
542546
),
543547
Protocol::Goodbye => RpcLimits::new(0, 0), // Goodbye request has no response
544548
Protocol::BlocksByRange => rpc_block_limits_by_fork(fork_context.current_fork()),
@@ -589,6 +593,7 @@ impl ProtocolId {
589593
| SupportedProtocol::LightClientFinalityUpdateV1
590594
| SupportedProtocol::LightClientUpdatesByRangeV1 => true,
591595
SupportedProtocol::StatusV1
596+
| SupportedProtocol::StatusV2
592597
| SupportedProtocol::BlocksByRootV1
593598
| SupportedProtocol::BlocksByRangeV1
594599
| SupportedProtocol::PingV1
@@ -758,7 +763,10 @@ impl<E: EthSpec> RequestType<E> {
758763
/// Gives the corresponding `SupportedProtocol` to this request.
759764
pub fn versioned_protocol(&self) -> SupportedProtocol {
760765
match self {
761-
RequestType::Status(_) => SupportedProtocol::StatusV1,
766+
RequestType::Status(req) => match req {
767+
StatusMessage::V1(_) => SupportedProtocol::StatusV1,
768+
StatusMessage::V2(_) => SupportedProtocol::StatusV2,
769+
},
762770
RequestType::Goodbye(_) => SupportedProtocol::GoodbyeV1,
763771
RequestType::BlocksByRange(req) => match req {
764772
OldBlocksByRangeRequest::V1(_) => SupportedProtocol::BlocksByRangeV1,
@@ -817,10 +825,10 @@ impl<E: EthSpec> RequestType<E> {
817825
pub fn supported_protocols(&self) -> Vec<ProtocolId> {
818826
match self {
819827
// add more protocols when versions/encodings are supported
820-
RequestType::Status(_) => vec![ProtocolId::new(
821-
SupportedProtocol::StatusV1,
822-
Encoding::SSZSnappy,
823-
)],
828+
RequestType::Status(_) => vec![
829+
ProtocolId::new(SupportedProtocol::StatusV1, Encoding::SSZSnappy),
830+
ProtocolId::new(SupportedProtocol::StatusV2, Encoding::SSZSnappy),
831+
],
824832
RequestType::Goodbye(_) => vec![ProtocolId::new(
825833
SupportedProtocol::GoodbyeV1,
826834
Encoding::SSZSnappy,

0 commit comments

Comments
 (0)