Implement Metadatav3 (#6303)

* Add a V3 variant for metadata

* Add v3 for requests; persistence logic

* Set custody_subnets on setting metadata

* Fix tests

* Address some comments

* fmt

* Address more comments

* Fix tests

* Update metadata rpc limits

* Update method doc.
This commit is contained in:
Pawan Dhananjay
2024-08-27 23:43:12 -07:00
committed by GitHub
parent f75a2cf65b
commit bcff4aa825
11 changed files with 226 additions and 60 deletions

View File

@@ -82,9 +82,10 @@ impl<E: EthSpec> Encoder<RPCCodedResponse<E>> for SSZSnappyInboundCodec<E> {
{
match self.protocol.versioned_protocol {
SupportedProtocol::MetaDataV1 => res.metadata_v1().as_ssz_bytes(),
// We always send V2 metadata responses from the behaviour
// No change required.
SupportedProtocol::MetaDataV2 => res.metadata_v2().as_ssz_bytes(),
SupportedProtocol::MetaDataV3 => {
res.metadata_v3(&self.fork_context.spec).as_ssz_bytes()
}
_ => unreachable!(
"We only send metadata responses on negotiating metadata requests"
),
@@ -136,6 +137,9 @@ impl<E: EthSpec> Decoder for SSZSnappyInboundCodec<E> {
if self.protocol.versioned_protocol == SupportedProtocol::MetaDataV2 {
return Ok(Some(InboundRequest::MetaData(MetadataRequest::new_v2())));
}
if self.protocol.versioned_protocol == SupportedProtocol::MetaDataV3 {
return Ok(Some(InboundRequest::MetaData(MetadataRequest::new_v3())));
}
let Some(length) = handle_length(&mut self.inner, &mut self.len, src)? else {
return Ok(None);
};
@@ -549,6 +553,15 @@ fn handle_rpc_request<E: EthSpec>(
}
// MetaData requests return early from InboundUpgrade and do not reach the decoder.
// Handle this case just for completeness.
SupportedProtocol::MetaDataV3 => {
if !decoded_buffer.is_empty() {
Err(RPCError::InternalError(
"Metadata requests shouldn't reach decoder",
))
} else {
Ok(Some(InboundRequest::MetaData(MetadataRequest::new_v3())))
}
}
SupportedProtocol::MetaDataV2 => {
if !decoded_buffer.is_empty() {
Err(RPCError::InternalError(
@@ -712,7 +725,10 @@ fn handle_rpc_response<E: EthSpec>(
),
)),
},
// MetaData V2 responses have no context bytes, so behave similarly to V1 responses
// MetaData V2/V3 responses have no context bytes, so behave similarly to V1 responses
SupportedProtocol::MetaDataV3 => Ok(Some(RPCResponse::MetaData(MetaData::V3(
MetaDataV3::from_ssz_bytes(decoded_buffer)?,
)))),
SupportedProtocol::MetaDataV2 => Ok(Some(RPCResponse::MetaData(MetaData::V2(
MetaDataV2::from_ssz_bytes(decoded_buffer)?,
)))),
@@ -984,6 +1000,15 @@ mod tests {
})
}
fn metadata_v3() -> MetaData<Spec> {
MetaData::V3(MetaDataV3 {
seq_number: 1,
attnets: EnrAttestationBitfield::<Spec>::default(),
syncnets: EnrSyncCommitteeBitfield::<Spec>::default(),
custody_subnet_count: 1,
})
}
/// Encodes the given protocol response as bytes.
fn encode_response(
protocol: SupportedProtocol,
@@ -1217,6 +1242,17 @@ mod tests {
Ok(Some(RPCResponse::MetaData(metadata()))),
);
// A MetaDataV3 still encodes as a MetaDataV2 since version is Version::V2
assert_eq!(
encode_then_decode_response(
SupportedProtocol::MetaDataV2,
RPCCodedResponse::Success(RPCResponse::MetaData(metadata_v3())),
ForkName::Base,
&chain_spec,
),
Ok(Some(RPCResponse::MetaData(metadata_v2()))),
);
assert_eq!(
encode_then_decode_response(
SupportedProtocol::BlobsByRangeV1,

View File

@@ -89,7 +89,7 @@ pub struct Ping {
/// The METADATA request structure.
#[superstruct(
variants(V1, V2),
variants(V1, V2, V3),
variant_attributes(derive(Clone, Debug, PartialEq, Serialize),)
)]
#[derive(Clone, Debug, PartialEq)]
@@ -109,11 +109,17 @@ impl<E: EthSpec> MetadataRequest<E> {
_phantom_data: PhantomData,
})
}
pub fn new_v3() -> Self {
Self::V3(MetadataRequestV3 {
_phantom_data: PhantomData,
})
}
}
/// The METADATA response structure.
#[superstruct(
variants(V1, V2),
variants(V1, V2, V3),
variant_attributes(
derive(Encode, Decode, Clone, Debug, PartialEq, Serialize),
serde(bound = "E: EthSpec", deny_unknown_fields),
@@ -127,8 +133,10 @@ pub struct MetaData<E: EthSpec> {
/// The persistent attestation subnet bitfield.
pub attnets: EnrAttestationBitfield<E>,
/// The persistent sync committee bitfield.
#[superstruct(only(V2))]
#[superstruct(only(V2, V3))]
pub syncnets: EnrSyncCommitteeBitfield<E>,
#[superstruct(only(V3))]
pub custody_subnet_count: u64,
}
impl<E: EthSpec> MetaData<E> {
@@ -140,6 +148,10 @@ impl<E: EthSpec> MetaData<E> {
seq_number: metadata.seq_number,
attnets: metadata.attnets.clone(),
}),
MetaData::V3(metadata) => MetaData::V1(MetaDataV1 {
seq_number: metadata.seq_number,
attnets: metadata.attnets.clone(),
}),
}
}
@@ -152,6 +164,30 @@ impl<E: EthSpec> MetaData<E> {
syncnets: Default::default(),
}),
md @ MetaData::V2(_) => md.clone(),
MetaData::V3(metadata) => MetaData::V2(MetaDataV2 {
seq_number: metadata.seq_number,
attnets: metadata.attnets.clone(),
syncnets: metadata.syncnets.clone(),
}),
}
}
/// Returns a V3 MetaData response from self by filling unavailable fields with default.
pub fn metadata_v3(&self, spec: &ChainSpec) -> Self {
match self {
MetaData::V1(metadata) => MetaData::V3(MetaDataV3 {
seq_number: metadata.seq_number,
attnets: metadata.attnets.clone(),
syncnets: Default::default(),
custody_subnet_count: spec.custody_requirement,
}),
MetaData::V2(metadata) => MetaData::V3(MetaDataV3 {
seq_number: metadata.seq_number,
attnets: metadata.attnets.clone(),
syncnets: metadata.syncnets.clone(),
custody_subnet_count: spec.custody_requirement,
}),
md @ MetaData::V3(_) => md.clone(),
}
}
@@ -159,6 +195,7 @@ impl<E: EthSpec> MetaData<E> {
match self {
MetaData::V1(md) => md.as_ssz_bytes(),
MetaData::V2(md) => md.as_ssz_bytes(),
MetaData::V3(md) => md.as_ssz_bytes(),
}
}
}

View File

@@ -21,7 +21,9 @@ use std::time::Duration;
use types::{EthSpec, ForkContext};
pub(crate) use handler::{HandlerErr, HandlerEvent};
pub(crate) use methods::{MetaData, MetaDataV1, MetaDataV2, Ping, RPCCodedResponse, RPCResponse};
pub(crate) use methods::{
MetaData, MetaDataV1, MetaDataV2, MetaDataV3, Ping, RPCCodedResponse, RPCResponse,
};
pub(crate) use protocol::InboundRequest;
pub use handler::SubstreamId;

View File

@@ -94,6 +94,7 @@ impl<E: EthSpec> OutboundRequest<E> {
Encoding::SSZSnappy,
)],
OutboundRequest::MetaData(_) => vec![
ProtocolId::new(SupportedProtocol::MetaDataV3, Encoding::SSZSnappy),
ProtocolId::new(SupportedProtocol::MetaDataV2, Encoding::SSZSnappy),
ProtocolId::new(SupportedProtocol::MetaDataV1, Encoding::SSZSnappy),
],
@@ -153,6 +154,7 @@ impl<E: EthSpec> OutboundRequest<E> {
OutboundRequest::MetaData(req) => match req {
MetadataRequest::V1(_) => SupportedProtocol::MetaDataV1,
MetadataRequest::V2(_) => SupportedProtocol::MetaDataV2,
MetadataRequest::V3(_) => SupportedProtocol::MetaDataV3,
},
}
}

View File

@@ -332,6 +332,7 @@ pub enum SupportedProtocol {
PingV1,
MetaDataV1,
MetaDataV2,
MetaDataV3,
LightClientBootstrapV1,
LightClientOptimisticUpdateV1,
LightClientFinalityUpdateV1,
@@ -353,6 +354,7 @@ impl SupportedProtocol {
SupportedProtocol::PingV1 => "1",
SupportedProtocol::MetaDataV1 => "1",
SupportedProtocol::MetaDataV2 => "2",
SupportedProtocol::MetaDataV3 => "3",
SupportedProtocol::LightClientBootstrapV1 => "1",
SupportedProtocol::LightClientOptimisticUpdateV1 => "1",
SupportedProtocol::LightClientFinalityUpdateV1 => "1",
@@ -374,6 +376,7 @@ impl SupportedProtocol {
SupportedProtocol::PingV1 => Protocol::Ping,
SupportedProtocol::MetaDataV1 => Protocol::MetaData,
SupportedProtocol::MetaDataV2 => Protocol::MetaData,
SupportedProtocol::MetaDataV3 => Protocol::MetaData,
SupportedProtocol::LightClientBootstrapV1 => Protocol::LightClientBootstrap,
SupportedProtocol::LightClientOptimisticUpdateV1 => {
Protocol::LightClientOptimisticUpdate
@@ -392,9 +395,20 @@ impl SupportedProtocol {
ProtocolId::new(Self::BlocksByRootV2, Encoding::SSZSnappy),
ProtocolId::new(Self::BlocksByRootV1, Encoding::SSZSnappy),
ProtocolId::new(Self::PingV1, Encoding::SSZSnappy),
ProtocolId::new(Self::MetaDataV2, Encoding::SSZSnappy),
ProtocolId::new(Self::MetaDataV1, Encoding::SSZSnappy),
];
if fork_context.spec.is_peer_das_scheduled() {
supported.extend_from_slice(&[
// V3 variants have higher preference for protocol negotation
ProtocolId::new(Self::MetaDataV3, Encoding::SSZSnappy),
ProtocolId::new(Self::MetaDataV2, Encoding::SSZSnappy),
ProtocolId::new(Self::MetaDataV1, Encoding::SSZSnappy),
]);
} else {
supported.extend_from_slice(&[
ProtocolId::new(Self::MetaDataV2, Encoding::SSZSnappy),
ProtocolId::new(Self::MetaDataV1, Encoding::SSZSnappy),
]);
}
if fork_context.fork_exists(ForkName::Deneb) {
supported.extend_from_slice(&[
ProtocolId::new(SupportedProtocol::BlobsByRootV1, Encoding::SSZSnappy),
@@ -554,7 +568,7 @@ impl ProtocolId {
),
Protocol::MetaData => RpcLimits::new(
<MetaDataV1<E> as Encode>::ssz_fixed_len(),
<MetaDataV2<E> as Encode>::ssz_fixed_len(),
<MetaDataV3<E> as Encode>::ssz_fixed_len(),
),
Protocol::LightClientBootstrap => {
rpc_light_client_bootstrap_limits_by_fork(fork_context.current_fork())
@@ -587,6 +601,7 @@ impl ProtocolId {
| SupportedProtocol::PingV1
| SupportedProtocol::MetaDataV1
| SupportedProtocol::MetaDataV2
| SupportedProtocol::MetaDataV3
| SupportedProtocol::GoodbyeV1 => false,
}
}
@@ -671,6 +686,9 @@ where
SupportedProtocol::MetaDataV2 => {
Ok((InboundRequest::MetaData(MetadataRequest::new_v2()), socket))
}
SupportedProtocol::MetaDataV3 => {
Ok((InboundRequest::MetaData(MetadataRequest::new_v3()), socket))
}
SupportedProtocol::LightClientOptimisticUpdateV1 => {
Ok((InboundRequest::LightClientOptimisticUpdate, socket))
}
@@ -757,6 +775,7 @@ impl<E: EthSpec> InboundRequest<E> {
InboundRequest::MetaData(req) => match req {
MetadataRequest::V1(_) => SupportedProtocol::MetaDataV1,
MetadataRequest::V2(_) => SupportedProtocol::MetaDataV2,
MetadataRequest::V3(_) => SupportedProtocol::MetaDataV3,
},
InboundRequest::LightClientBootstrap(_) => SupportedProtocol::LightClientBootstrapV1,
InboundRequest::LightClientOptimisticUpdate => {