Skip to content

Commit 585280c

Browse files
authored
feat: implement portal_*Ping rpc with ping extension support (#1710)
1 parent 9894d25 commit 585280c

File tree

41 files changed

+1093
-457
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1093
-457
lines changed

bin/portal-bridge/src/census/network.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ use discv5::enr::NodeId;
55
use ethportal_api::{
66
generate_random_node_ids,
77
jsonrpsee::http_client::HttpClient,
8-
types::{distance::Distance, network::Subnetwork, portal::PongInfo, portal_wire::OfferTrace},
8+
types::{
9+
network::Subnetwork, ping_extensions::decode::PingExtension, portal::PongInfo,
10+
portal_wire::OfferTrace,
11+
},
912
BeaconNetworkApiClient, Enr, HistoryNetworkApiClient, StateNetworkApiClient,
1013
};
1114
use futures::{future::JoinAll, StreamExt};
@@ -251,7 +254,16 @@ impl Network {
251254
return LivenessResult::Fail;
252255
};
253256

254-
let radius = Distance::from(pong_info.data_radius);
257+
let radius = match PingExtension::decode_json(pong_info.payload_type, pong_info.payload) {
258+
Ok(PingExtension::Capabilities(payload)) => payload.data_radius,
259+
_ => {
260+
warn!(
261+
subnetwork = %self.subnetwork,
262+
"liveness_check: received unexpected ping extension: {}", pong_info.payload_type,
263+
);
264+
return LivenessResult::Fail;
265+
}
266+
};
255267

256268
// If ENR seq is not the latest one, fetch fresh ENR
257269
let enr = if enr.seq() < pong_info.enr_seq {
@@ -279,9 +291,13 @@ impl Network {
279291
ensure!(self.is_eligible(enr), "ping: peer is filtered out");
280292

281293
match self.subnetwork {
282-
Subnetwork::History => HistoryNetworkApiClient::ping(&self.client, enr.clone()),
283-
Subnetwork::State => StateNetworkApiClient::ping(&self.client, enr.clone()),
284-
Subnetwork::Beacon => BeaconNetworkApiClient::ping(&self.client, enr.clone()),
294+
Subnetwork::History => {
295+
HistoryNetworkApiClient::ping(&self.client, enr.clone(), None, None)
296+
}
297+
Subnetwork::State => StateNetworkApiClient::ping(&self.client, enr.clone(), None, None),
298+
Subnetwork::Beacon => {
299+
BeaconNetworkApiClient::ping(&self.client, enr.clone(), None, None)
300+
}
285301
_ => unreachable!("ping: unsupported subnetwork: {}", self.subnetwork),
286302
}
287303
.await

crates/ethportal-api/src/beacon.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use alloy::primitives::B256;
22
use discv5::enr::NodeId;
33
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
4+
use serde_json::Value;
45

56
use crate::{
67
consensus::header::BeaconBlockHeader,
@@ -12,6 +13,7 @@ use crate::{
1213
},
1314
content_key::beacon::BeaconContentKey,
1415
enr::Enr,
16+
ping_extensions::extension_types::PingExtensionType,
1517
portal::{
1618
AcceptInfo, DataRadius, FindContentInfo, FindNodesInfo, GetContentInfo,
1719
PaginateLocalContentInfo, PongInfo, PutContentInfo, TraceContentInfo,
@@ -55,7 +57,12 @@ pub trait BeaconNetworkApi {
5557

5658
/// Send a PING message to the designated node and wait for a PONG response
5759
#[method(name = "beaconPing")]
58-
async fn ping(&self, enr: Enr) -> RpcResult<PongInfo>;
60+
async fn ping(
61+
&self,
62+
enr: Enr,
63+
payload_type: Option<PingExtensionType>,
64+
payload: Option<Value>,
65+
) -> RpcResult<PongInfo>;
5966

6067
/// Get the finalized state root of the finalized beacon header.
6168
#[method(name = "beaconFinalizedStateRoot")]

crates/ethportal-api/src/history.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use discv5::enr::NodeId;
22
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
3+
use serde_json::Value;
34

45
use crate::{
56
types::{
67
content_key::history::HistoryContentKey,
78
enr::Enr,
9+
ping_extensions::extension_types::PingExtensionType,
810
portal::{
911
AcceptInfo, DataRadius, FindContentInfo, FindNodesInfo, GetContentInfo,
1012
PaginateLocalContentInfo, PongInfo, PutContentInfo, TraceContentInfo,
@@ -44,7 +46,12 @@ pub trait HistoryNetworkApi {
4446

4547
/// Send a PING message to the designated node and wait for a PONG response
4648
#[method(name = "historyPing")]
47-
async fn ping(&self, enr: Enr) -> RpcResult<PongInfo>;
49+
async fn ping(
50+
&self,
51+
enr: Enr,
52+
payload_type: Option<PingExtensionType>,
53+
payload: Option<Value>,
54+
) -> RpcResult<PongInfo>;
4855

4956
/// Send a FINDNODES request for nodes that fall within the given set of distances, to the
5057
/// designated peer and wait for a response

crates/ethportal-api/src/state.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use discv5::enr::NodeId;
22
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
3+
use serde_json::Value;
34

45
use crate::{
56
types::{
67
content_key::state::StateContentKey,
78
enr::Enr,
9+
ping_extensions::extension_types::PingExtensionType,
810
portal::{
911
AcceptInfo, DataRadius, FindContentInfo, FindNodesInfo, GetContentInfo,
1012
PaginateLocalContentInfo, PongInfo, PutContentInfo, TraceContentInfo,
@@ -44,7 +46,12 @@ pub trait StateNetworkApi {
4446

4547
/// Send a PING message to the designated node and wait for a PONG response
4648
#[method(name = "statePing")]
47-
async fn ping(&self, enr: Enr) -> RpcResult<PongInfo>;
49+
async fn ping(
50+
&self,
51+
enr: Enr,
52+
payload_type: Option<PingExtensionType>,
53+
payload: Option<Value>,
54+
) -> RpcResult<PongInfo>;
4855

4956
/// Send a FINDNODES request for nodes that fall within the given set of distances, to the
5057
/// designated peer and wait for a response

crates/ethportal-api/src/types/distance.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
11
use std::{fmt, ops::Deref};
22

33
use alloy::primitives::U256;
4+
use serde::{Deserialize, Serialize};
45
use ssz_derive::{Decode, Encode};
56

6-
pub type DataRadius = U256;
7-
87
/// Represents a distance between two keys in the DHT key space.
9-
#[derive(Copy, Clone, PartialEq, Eq, Default, PartialOrd, Ord, Debug, Encode, Decode)]
8+
#[derive(
9+
Copy,
10+
Clone,
11+
PartialEq,
12+
Eq,
13+
Default,
14+
PartialOrd,
15+
Ord,
16+
Debug,
17+
Encode,
18+
Decode,
19+
Serialize,
20+
Deserialize,
21+
)]
22+
#[serde(transparent)]
1023
#[ssz(struct_behaviour = "transparent")]
1124
pub struct Distance(U256);
1225

crates/ethportal-api/src/types/jsonrpc/endpoints.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
use discv5::enr::NodeId;
22

33
use crate::{
4-
types::enr::Enr, BeaconContentKey, BeaconContentValue, HistoryContentKey, HistoryContentValue,
5-
StateContentKey, StateContentValue,
4+
types::{
5+
enr::Enr,
6+
ping_extensions::{decode::PingExtension, extension_types::PingExtensionType},
7+
},
8+
BeaconContentKey, BeaconContentValue, HistoryContentKey, HistoryContentValue, StateContentKey,
9+
StateContentValue,
610
};
711

812
/// Discv5 JSON-RPC endpoints. Start with "discv5_" prefix
@@ -17,8 +21,8 @@ pub enum Discv5Endpoint {
1721
pub enum StateEndpoint {
1822
/// params: None
1923
RoutingTableInfo,
20-
/// params: [enr]
21-
Ping(Enr),
24+
/// params: [enr, payload_type, payload]
25+
Ping(Enr, Option<PingExtensionType>, Option<PingExtension>),
2226
/// params: [enr]
2327
AddEnr(Enr),
2428
/// params: [node_id]
@@ -82,8 +86,8 @@ pub enum HistoryEndpoint {
8286
Offer(Enr, Vec<(HistoryContentKey, HistoryContentValue)>),
8387
/// params: [enr, content_key, content_value]
8488
TraceOffer(Enr, HistoryContentKey, HistoryContentValue),
85-
/// params: [enr]
86-
Ping(Enr),
89+
/// params: [enr, payload_type, payload]
90+
Ping(Enr, Option<PingExtensionType>, Option<PingExtension>),
8791
/// params: content_key
8892
GetContent(HistoryContentKey),
8993
/// params: content_key
@@ -138,8 +142,8 @@ pub enum BeaconEndpoint {
138142
Offer(Enr, Vec<(BeaconContentKey, BeaconContentValue)>),
139143
/// params: [enr, content_key, content_value]
140144
TraceOffer(Enr, BeaconContentKey, BeaconContentValue),
141-
/// params: enr
142-
Ping(Enr),
145+
/// params: [enr, payload_type, payload]
146+
Ping(Enr, Option<PingExtensionType>, Option<PingExtension>),
143147
/// params: content_key
144148
GetContent(BeaconContentKey),
145149
/// params: content_key
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
use super::extension_types::PingExtensionType;
2+
3+
pub const BEACON_SUPPORTED_EXTENSIONS: &[PingExtensionType] = &[
4+
PingExtensionType::Capabilities,
5+
PingExtensionType::BasicRadius,
6+
PingExtensionType::Error,
7+
];
8+
pub const HISTORY_SUPPORTED_EXTENSIONS: &[PingExtensionType] = &[
9+
PingExtensionType::Capabilities,
10+
PingExtensionType::HistoryRadius,
11+
PingExtensionType::Error,
12+
];
13+
pub const STATE_SUPPORTED_EXTENSIONS: &[PingExtensionType] = &[
14+
PingExtensionType::Capabilities,
15+
PingExtensionType::BasicRadius,
16+
PingExtensionType::Error,
17+
];
Lines changed: 74 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,105 @@
11
use anyhow::{anyhow, bail};
2+
use serde::Serialize;
3+
use serde_json::Value;
24
use ssz::Decode;
35

46
use super::{
5-
extension_types::Extensions,
7+
extension_types::PingExtensionType,
68
extensions::{
79
type_0::ClientInfoRadiusCapabilities, type_1::BasicRadius, type_2::HistoryRadius,
810
type_65535::PingError,
911
},
1012
};
1113
use crate::{types::portal_wire::CustomPayload, utils::bytes::hex_encode};
1214

13-
#[derive(Debug, Clone)]
14-
pub enum DecodedExtension {
15+
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
16+
#[serde(untagged)]
17+
pub enum PingExtension {
1518
Capabilities(ClientInfoRadiusCapabilities),
1619
BasicRadius(BasicRadius),
1720
HistoryRadius(HistoryRadius),
1821
Error(PingError),
1922
}
2023

21-
impl From<DecodedExtension> for Extensions {
22-
fn from(value: DecodedExtension) -> Self {
24+
impl From<PingExtension> for PingExtensionType {
25+
fn from(value: PingExtension) -> Self {
2326
match value {
24-
DecodedExtension::Capabilities(_) => Extensions::Capabilities,
25-
DecodedExtension::BasicRadius(_) => Extensions::BasicRadius,
26-
DecodedExtension::HistoryRadius(_) => Extensions::HistoryRadius,
27-
DecodedExtension::Error(_) => Extensions::Error,
27+
PingExtension::Capabilities(_) => PingExtensionType::Capabilities,
28+
PingExtension::BasicRadius(_) => PingExtensionType::BasicRadius,
29+
PingExtension::HistoryRadius(_) => PingExtensionType::HistoryRadius,
30+
PingExtension::Error(_) => PingExtensionType::Error,
2831
}
2932
}
3033
}
3134

32-
impl DecodedExtension {
33-
pub fn decode_extension(payload_type: u16, payload: CustomPayload) -> anyhow::Result<Self> {
34-
let Ok(extension_type) = Extensions::try_from(payload_type) else {
35-
bail!("Failed to decode extension type {payload_type}");
35+
impl From<PingExtension> for CustomPayload {
36+
fn from(value: PingExtension) -> Self {
37+
match value {
38+
PingExtension::Capabilities(capabilities) => CustomPayload::from(capabilities),
39+
PingExtension::BasicRadius(basic_radius) => CustomPayload::from(basic_radius),
40+
PingExtension::HistoryRadius(history_radius) => CustomPayload::from(history_radius),
41+
PingExtension::Error(error) => CustomPayload::from(error),
42+
}
43+
}
44+
}
45+
46+
impl PingExtension {
47+
pub fn decode_ssz(
48+
extension_type: PingExtensionType,
49+
payload: CustomPayload,
50+
) -> anyhow::Result<Self> {
51+
let ping_extension = match extension_type {
52+
PingExtensionType::Capabilities => {
53+
ClientInfoRadiusCapabilities::from_ssz_bytes(&payload.payload)
54+
.map(PingExtension::Capabilities)
55+
}
56+
PingExtensionType::BasicRadius => {
57+
BasicRadius::from_ssz_bytes(&payload.payload).map(PingExtension::BasicRadius)
58+
}
59+
PingExtensionType::HistoryRadius => {
60+
HistoryRadius::from_ssz_bytes(&payload.payload).map(PingExtension::HistoryRadius)
61+
}
62+
PingExtensionType::Error => {
63+
PingError::from_ssz_bytes(&payload.payload).map(PingExtension::Error)
64+
}
65+
PingExtensionType::NonSupportedExtension(non_supported_extension) => {
66+
bail!("Non supported extension type: {non_supported_extension}")
67+
}
3668
};
69+
ping_extension.map_err(|err| {
70+
anyhow!(
71+
"Failed to decode ping extension {extension_type}: {err:?}, payload: {:?}",
72+
hex_encode(&*payload.payload)
73+
)
74+
})
75+
}
3776

38-
match extension_type {
39-
Extensions::Capabilities => {
40-
let capabilities = ClientInfoRadiusCapabilities::from_ssz_bytes(&payload.payload)
41-
.map_err(|err| {
42-
anyhow!(
43-
"Failed to decode ClientInfoRadiusCapabilities: {err:?}, payload: {:?}",
44-
hex_encode(&*payload.payload)
45-
)
46-
})?;
47-
Ok(DecodedExtension::Capabilities(capabilities))
77+
pub fn decode_json(extension_type: PingExtensionType, payload: Value) -> anyhow::Result<Self> {
78+
let ping_extension = match extension_type {
79+
PingExtensionType::Capabilities => {
80+
serde_json::from_value(payload).map(PingExtension::Capabilities)
4881
}
49-
Extensions::BasicRadius => {
50-
let basic_radius =
51-
BasicRadius::from_ssz_bytes(&payload.payload).map_err(|err| {
52-
anyhow!(
53-
"Failed to decode BasicRadius: {err:?}, payload: {:?}",
54-
hex_encode(&*payload.payload)
55-
)
56-
})?;
57-
Ok(DecodedExtension::BasicRadius(basic_radius))
82+
PingExtensionType::BasicRadius => {
83+
serde_json::from_value(payload).map(PingExtension::BasicRadius)
5884
}
59-
Extensions::HistoryRadius => {
60-
let history_radius =
61-
HistoryRadius::from_ssz_bytes(&payload.payload).map_err(|err| {
62-
anyhow!(
63-
"Failed to decode HistoryRadius: {err:?}, payload: {:?}",
64-
hex_encode(&*payload.payload)
65-
)
66-
})?;
67-
Ok(DecodedExtension::HistoryRadius(history_radius))
85+
PingExtensionType::HistoryRadius => {
86+
serde_json::from_value(payload).map(PingExtension::HistoryRadius)
6887
}
69-
Extensions::Error => {
70-
let error = PingError::from_ssz_bytes(&payload.payload).map_err(|err| {
71-
anyhow!(
72-
"Failed to decode PingError: {err:?}, payload: {:?}",
73-
hex_encode(&*payload.payload)
74-
)
75-
})?;
76-
Ok(DecodedExtension::Error(error))
88+
PingExtensionType::Error => serde_json::from_value(payload).map(PingExtension::Error),
89+
PingExtensionType::NonSupportedExtension(non_supported_extension) => {
90+
bail!("Non supported extension type: {non_supported_extension}")
7791
}
92+
};
93+
ping_extension
94+
.map_err(|err| anyhow!("Failed to decode ping extension {extension_type}: {err:?} "))
95+
}
96+
97+
pub fn ping_extension_type(&self) -> PingExtensionType {
98+
match self {
99+
PingExtension::Capabilities(_) => PingExtensionType::Capabilities,
100+
PingExtension::BasicRadius(_) => PingExtensionType::BasicRadius,
101+
PingExtension::HistoryRadius(_) => PingExtensionType::HistoryRadius,
102+
PingExtension::Error(_) => PingExtensionType::Error,
78103
}
79104
}
80105
}

0 commit comments

Comments
 (0)