Files
lighthouse/beacon_node/lighthouse_network/src/rpc/methods.rs
Michael Sproul d235f2c697 Delete RuntimeVariableList::from_vec (#7930)
This method is a footgun because it truncates the list. It is the source of a recent bug:

- https://github.com/sigp/lighthouse/pull/7927


  - Delete uses of `RuntimeVariableList::from_vec` and replace them with `::new` which does validation and can fail.
- Propagate errors where possible, unwrap in tests and use `expect` for obviously-safe uses (in `chain_spec.rs`).
2025-08-27 06:52:14 +00:00

940 lines
30 KiB
Rust

//! Available RPC methods types and ids.
use crate::types::{EnrAttestationBitfield, EnrSyncCommitteeBitfield};
use regex::bytes::Regex;
use serde::Serialize;
use ssz::Encode;
use ssz_derive::{Decode, Encode};
use ssz_types::{VariableList, typenum::U256};
use std::fmt::Display;
use std::marker::PhantomData;
use std::ops::Deref;
use std::sync::Arc;
use strum::IntoStaticStr;
use superstruct::superstruct;
use types::blob_sidecar::BlobIdentifier;
use types::light_client_update::MAX_REQUEST_LIGHT_CLIENT_UPDATES;
use types::{
ChainSpec, ColumnIndex, DataColumnSidecar, DataColumnsByRootIdentifier, Epoch, EthSpec,
ForkContext, Hash256, LightClientBootstrap, LightClientFinalityUpdate,
LightClientOptimisticUpdate, LightClientUpdate, RuntimeVariableList, SignedBeaconBlock, Slot,
blob_sidecar::BlobSidecar,
};
/// Maximum length of error message.
pub type MaxErrorLen = U256;
pub const MAX_ERROR_LEN: u64 = 256;
/// Wrapper over SSZ List to represent error message in rpc responses.
#[derive(Debug, Clone)]
pub struct ErrorType(pub VariableList<u8, MaxErrorLen>);
impl From<String> for ErrorType {
fn from(s: String) -> Self {
Self(VariableList::from(s.as_bytes().to_vec()))
}
}
impl From<&str> for ErrorType {
fn from(s: &str) -> Self {
Self(VariableList::from(s.as_bytes().to_vec()))
}
}
impl Deref for ErrorType {
type Target = VariableList<u8, MaxErrorLen>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Display for ErrorType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
#[allow(clippy::invalid_regex)]
let re = Regex::new("\\p{C}").expect("Regex is valid");
let error_type_str =
String::from_utf8_lossy(&re.replace_all(self.0.deref(), &b""[..])).to_string();
write!(f, "{}", error_type_str)
}
}
/* Request/Response data structures for RPC methods */
/* Requests */
/// The STATUS request/response handshake message.
#[superstruct(
variants(V1, V2),
variant_attributes(derive(Encode, Decode, Clone, Debug, PartialEq),)
)]
#[derive(Clone, Debug, PartialEq)]
pub struct StatusMessage {
/// The fork version of the chain we are broadcasting.
pub fork_digest: [u8; 4],
/// Latest finalized root.
pub finalized_root: Hash256,
/// Latest finalized epoch.
pub finalized_epoch: Epoch,
/// The latest block root.
pub head_root: Hash256,
/// The slot associated with the latest block root.
pub head_slot: Slot,
/// The slot after which we guarantee to have all the blocks
/// and blobs/data columns that we currently advertise.
#[superstruct(only(V2))]
pub earliest_available_slot: Slot,
}
impl StatusMessage {
pub fn status_v1(&self) -> StatusMessageV1 {
match &self {
Self::V1(status) => status.clone(),
Self::V2(status) => StatusMessageV1 {
fork_digest: status.fork_digest,
finalized_root: status.finalized_root,
finalized_epoch: status.finalized_epoch,
head_root: status.head_root,
head_slot: status.head_slot,
},
}
}
pub fn status_v2(&self) -> StatusMessageV2 {
match &self {
Self::V1(status) => StatusMessageV2 {
fork_digest: status.fork_digest,
finalized_root: status.finalized_root,
finalized_epoch: status.finalized_epoch,
head_root: status.head_root,
head_slot: status.head_slot,
// Note: we always produce a V2 message as our local
// status message, so this match arm should ideally never
// be invoked in lighthouse.
earliest_available_slot: Slot::new(0),
},
Self::V2(status) => status.clone(),
}
}
}
/// The PING request/response message.
#[derive(Encode, Decode, Copy, Clone, Debug, PartialEq)]
pub struct Ping {
/// The metadata sequence number.
pub data: u64,
}
/// The METADATA request structure.
#[superstruct(
variants(V1, V2, V3),
variant_attributes(derive(Clone, Debug, PartialEq, Serialize),)
)]
#[derive(Clone, Debug, PartialEq)]
pub struct MetadataRequest<E: EthSpec> {
_phantom_data: PhantomData<E>,
}
impl<E: EthSpec> MetadataRequest<E> {
pub fn new_v1() -> Self {
Self::V1(MetadataRequestV1 {
_phantom_data: PhantomData,
})
}
pub fn new_v2() -> Self {
Self::V2(MetadataRequestV2 {
_phantom_data: PhantomData,
})
}
pub fn new_v3() -> Self {
Self::V3(MetadataRequestV3 {
_phantom_data: PhantomData,
})
}
}
/// The METADATA response structure.
#[superstruct(
variants(V1, V2, V3),
variant_attributes(
derive(Encode, Decode, Clone, Debug, PartialEq, Serialize),
serde(bound = "E: EthSpec", deny_unknown_fields),
)
)]
#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(bound = "E: EthSpec")]
pub struct MetaData<E: EthSpec> {
/// A sequential counter indicating when data gets modified.
pub seq_number: u64,
/// The persistent attestation subnet bitfield.
pub attnets: EnrAttestationBitfield<E>,
/// The persistent sync committee bitfield.
#[superstruct(only(V2, V3))]
pub syncnets: EnrSyncCommitteeBitfield<E>,
#[superstruct(only(V3))]
pub custody_group_count: u64,
}
impl<E: EthSpec> MetaData<E> {
/// Returns a V1 MetaData response from self.
pub fn metadata_v1(&self) -> Self {
match self {
md @ MetaData::V1(_) => md.clone(),
MetaData::V2(metadata) => MetaData::V1(MetaDataV1 {
seq_number: metadata.seq_number,
attnets: metadata.attnets.clone(),
}),
MetaData::V3(metadata) => MetaData::V1(MetaDataV1 {
seq_number: metadata.seq_number,
attnets: metadata.attnets.clone(),
}),
}
}
/// Returns a V2 MetaData response from self by filling unavailable fields with default.
pub fn metadata_v2(&self) -> Self {
match self {
MetaData::V1(metadata) => MetaData::V2(MetaDataV2 {
seq_number: metadata.seq_number,
attnets: metadata.attnets.clone(),
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_group_count: spec.custody_requirement,
}),
MetaData::V2(metadata) => MetaData::V3(MetaDataV3 {
seq_number: metadata.seq_number,
attnets: metadata.attnets.clone(),
syncnets: metadata.syncnets.clone(),
custody_group_count: spec.custody_requirement,
}),
md @ MetaData::V3(_) => md.clone(),
}
}
pub fn as_ssz_bytes(&self) -> Vec<u8> {
match self {
MetaData::V1(md) => md.as_ssz_bytes(),
MetaData::V2(md) => md.as_ssz_bytes(),
MetaData::V3(md) => md.as_ssz_bytes(),
}
}
}
/// The reason given for a `Goodbye` message.
///
/// Note: any unknown `u64::into(n)` will resolve to `Goodbye::Unknown` for any unknown `n`,
/// however `GoodbyeReason::Unknown.into()` will go into `0_u64`. Therefore de-serializing then
/// re-serializing may not return the same bytes.
#[derive(Debug, Clone, PartialEq)]
pub enum GoodbyeReason {
/// This node has shutdown.
ClientShutdown = 1,
/// Incompatible networks.
IrrelevantNetwork = 2,
/// Error/fault in the RPC.
Fault = 3,
/// Teku uses this code for not being able to verify a network.
UnableToVerifyNetwork = 128,
/// The node has too many connected peers.
TooManyPeers = 129,
/// Scored poorly.
BadScore = 250,
/// The peer is banned
Banned = 251,
/// The IP address the peer is using is banned.
BannedIP = 252,
/// Unknown reason.
Unknown = 0,
}
impl From<u64> for GoodbyeReason {
fn from(id: u64) -> GoodbyeReason {
match id {
1 => GoodbyeReason::ClientShutdown,
2 => GoodbyeReason::IrrelevantNetwork,
3 => GoodbyeReason::Fault,
128 => GoodbyeReason::UnableToVerifyNetwork,
129 => GoodbyeReason::TooManyPeers,
250 => GoodbyeReason::BadScore,
251 => GoodbyeReason::Banned,
252 => GoodbyeReason::BannedIP,
_ => GoodbyeReason::Unknown,
}
}
}
impl From<GoodbyeReason> for u64 {
fn from(reason: GoodbyeReason) -> u64 {
reason as u64
}
}
impl ssz::Encode for GoodbyeReason {
fn is_ssz_fixed_len() -> bool {
<u64 as ssz::Encode>::is_ssz_fixed_len()
}
fn ssz_fixed_len() -> usize {
<u64 as ssz::Encode>::ssz_fixed_len()
}
fn ssz_bytes_len(&self) -> usize {
0_u64.ssz_bytes_len()
}
fn ssz_append(&self, buf: &mut Vec<u8>) {
let conv: u64 = self.clone().into();
conv.ssz_append(buf)
}
}
impl ssz::Decode for GoodbyeReason {
fn is_ssz_fixed_len() -> bool {
<u64 as ssz::Decode>::is_ssz_fixed_len()
}
fn ssz_fixed_len() -> usize {
<u64 as ssz::Decode>::ssz_fixed_len()
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
u64::from_ssz_bytes(bytes).map(|n| n.into())
}
}
/// Request a number of beacon block roots from a peer.
#[superstruct(
variants(V1, V2),
variant_attributes(derive(Encode, Decode, Clone, Debug, PartialEq))
)]
#[derive(Clone, Debug, PartialEq)]
pub struct BlocksByRangeRequest {
/// The starting slot to request blocks.
pub start_slot: u64,
/// The number of blocks from the start slot.
pub count: u64,
}
impl BlocksByRangeRequest {
/// The default request is V2
pub fn new(start_slot: u64, count: u64) -> Self {
Self::V2(BlocksByRangeRequestV2 { start_slot, count })
}
pub fn new_v1(start_slot: u64, count: u64) -> Self {
Self::V1(BlocksByRangeRequestV1 { start_slot, count })
}
}
/// Request a number of beacon blobs from a peer.
#[derive(Encode, Decode, Clone, Debug, PartialEq)]
pub struct BlobsByRangeRequest {
/// The starting slot to request blobs.
pub start_slot: u64,
/// The number of slots from the start slot.
pub count: u64,
}
impl BlobsByRangeRequest {
pub fn max_blobs_requested(&self, epoch: Epoch, spec: &ChainSpec) -> u64 {
let max_blobs_per_block = spec.max_blobs_per_block(epoch);
self.count.saturating_mul(max_blobs_per_block)
}
}
/// Request a number of beacon data columns from a peer.
#[derive(Encode, Decode, Clone, Debug, PartialEq)]
pub struct DataColumnsByRangeRequest {
/// The starting slot to request data columns.
pub start_slot: u64,
/// The number of slots from the start slot.
pub count: u64,
/// The list column indices being requested.
pub columns: Vec<ColumnIndex>,
}
impl DataColumnsByRangeRequest {
pub fn max_requested<E: EthSpec>(&self) -> u64 {
self.count.saturating_mul(self.columns.len() as u64)
}
pub fn ssz_min_len() -> usize {
DataColumnsByRangeRequest {
start_slot: 0,
count: 0,
columns: vec![0],
}
.as_ssz_bytes()
.len()
}
pub fn ssz_max_len<E: EthSpec>() -> usize {
DataColumnsByRangeRequest {
start_slot: 0,
count: 0,
columns: vec![0; E::number_of_columns()],
}
.as_ssz_bytes()
.len()
}
}
/// Request a number of beacon block roots from a peer.
#[superstruct(
variants(V1, V2),
variant_attributes(derive(Encode, Decode, Clone, Debug, PartialEq))
)]
#[derive(Clone, Debug, PartialEq)]
pub struct OldBlocksByRangeRequest {
/// The starting slot to request blocks.
pub start_slot: u64,
/// The number of blocks from the start slot.
pub count: u64,
/// The step increment to receive blocks.
///
/// A value of 1 returns every block.
/// A value of 2 returns every second block.
/// A value of 3 returns every third block and so on.
pub step: u64,
}
impl OldBlocksByRangeRequest {
/// The default request is V2
pub fn new(start_slot: u64, count: u64, step: u64) -> Self {
Self::V2(OldBlocksByRangeRequestV2 {
start_slot,
count,
step,
})
}
pub fn new_v1(start_slot: u64, count: u64, step: u64) -> Self {
Self::V1(OldBlocksByRangeRequestV1 {
start_slot,
count,
step,
})
}
}
impl From<BlocksByRangeRequest> for OldBlocksByRangeRequest {
fn from(req: BlocksByRangeRequest) -> Self {
match req {
BlocksByRangeRequest::V1(ref req) => {
OldBlocksByRangeRequest::V1(OldBlocksByRangeRequestV1 {
start_slot: req.start_slot,
count: req.count,
step: 1,
})
}
BlocksByRangeRequest::V2(ref req) => {
OldBlocksByRangeRequest::V2(OldBlocksByRangeRequestV2 {
start_slot: req.start_slot,
count: req.count,
step: 1,
})
}
}
}
}
/// Request a number of beacon block bodies from a peer.
#[superstruct(variants(V1, V2), variant_attributes(derive(Clone, Debug, PartialEq)))]
#[derive(Clone, Debug, PartialEq)]
pub struct BlocksByRootRequest {
/// The list of beacon block bodies being requested.
pub block_roots: RuntimeVariableList<Hash256>,
}
impl BlocksByRootRequest {
pub fn new(block_roots: Vec<Hash256>, fork_context: &ForkContext) -> Result<Self, String> {
let max_request_blocks = fork_context
.spec
.max_request_blocks(fork_context.current_fork_name());
let block_roots = RuntimeVariableList::new(block_roots, max_request_blocks)
.map_err(|e| format!("BlocksByRootRequestV2 too many roots: {e:?}"))?;
Ok(Self::V2(BlocksByRootRequestV2 { block_roots }))
}
pub fn new_v1(block_roots: Vec<Hash256>, fork_context: &ForkContext) -> Result<Self, String> {
let max_request_blocks = fork_context
.spec
.max_request_blocks(fork_context.current_fork_name());
let block_roots = RuntimeVariableList::new(block_roots, max_request_blocks)
.map_err(|e| format!("BlocksByRootRequestV1 too many roots: {e:?}"))?;
Ok(Self::V1(BlocksByRootRequestV1 { block_roots }))
}
}
/// Request a number of beacon blocks and blobs from a peer.
#[derive(Clone, Debug, PartialEq)]
pub struct BlobsByRootRequest {
/// The list of beacon block roots being requested.
pub blob_ids: RuntimeVariableList<BlobIdentifier>,
}
impl BlobsByRootRequest {
pub fn new(blob_ids: Vec<BlobIdentifier>, fork_context: &ForkContext) -> Result<Self, String> {
let max_request_blob_sidecars = fork_context
.spec
.max_request_blob_sidecars(fork_context.current_fork_name());
let blob_ids = RuntimeVariableList::new(blob_ids, max_request_blob_sidecars)
.map_err(|e| format!("BlobsByRootRequestV1 too many blob IDs: {e:?}"))?;
Ok(Self { blob_ids })
}
}
/// Request a number of data columns from a peer.
#[derive(Clone, Debug, PartialEq)]
pub struct DataColumnsByRootRequest<E: EthSpec> {
/// The list of beacon block roots and column indices being requested.
pub data_column_ids: RuntimeVariableList<DataColumnsByRootIdentifier<E>>,
}
impl<E: EthSpec> DataColumnsByRootRequest<E> {
pub fn new(
data_column_ids: Vec<DataColumnsByRootIdentifier<E>>,
max_request_blocks: usize,
) -> Result<Self, &'static str> {
let data_column_ids = RuntimeVariableList::new(data_column_ids, max_request_blocks)
.map_err(|_| "DataColumnsByRootRequest too many column IDs")?;
Ok(Self { data_column_ids })
}
pub fn max_requested(&self) -> usize {
self.data_column_ids.iter().map(|id| id.columns.len()).sum()
}
}
/// Request a number of beacon data columns from a peer.
#[derive(Encode, Decode, Clone, Debug, PartialEq)]
pub struct LightClientUpdatesByRangeRequest {
/// The starting period to request light client updates.
pub start_period: u64,
/// The number of periods from `start_period`.
pub count: u64,
}
impl LightClientUpdatesByRangeRequest {
pub fn max_requested(&self) -> u64 {
MAX_REQUEST_LIGHT_CLIENT_UPDATES
}
pub fn ssz_min_len() -> usize {
LightClientUpdatesByRangeRequest {
start_period: 0,
count: 0,
}
.as_ssz_bytes()
.len()
}
pub fn ssz_max_len() -> usize {
Self::ssz_min_len()
}
}
/* RPC Handling and Grouping */
// Collection of enums and structs used by the Codecs to encode/decode RPC messages
#[derive(Debug, Clone, PartialEq)]
pub enum RpcSuccessResponse<E: EthSpec> {
/// A HELLO message.
Status(StatusMessage),
/// A response to a get BLOCKS_BY_RANGE request. A None response signifies the end of the
/// batch.
BlocksByRange(Arc<SignedBeaconBlock<E>>),
/// A response to a get BLOCKS_BY_ROOT request.
BlocksByRoot(Arc<SignedBeaconBlock<E>>),
/// A response to a get BLOBS_BY_RANGE request
BlobsByRange(Arc<BlobSidecar<E>>),
/// A response to a get LIGHT_CLIENT_BOOTSTRAP request.
LightClientBootstrap(Arc<LightClientBootstrap<E>>),
/// A response to a get LIGHT_CLIENT_OPTIMISTIC_UPDATE request.
LightClientOptimisticUpdate(Arc<LightClientOptimisticUpdate<E>>),
/// A response to a get LIGHT_CLIENT_FINALITY_UPDATE request.
LightClientFinalityUpdate(Arc<LightClientFinalityUpdate<E>>),
/// A response to a get LIGHT_CLIENT_UPDATES_BY_RANGE request.
LightClientUpdatesByRange(Arc<LightClientUpdate<E>>),
/// A response to a get BLOBS_BY_ROOT request.
BlobsByRoot(Arc<BlobSidecar<E>>),
/// A response to a get DATA_COLUMN_SIDECARS_BY_ROOT request.
DataColumnsByRoot(Arc<DataColumnSidecar<E>>),
/// A response to a get DATA_COLUMN_SIDECARS_BY_RANGE request.
DataColumnsByRange(Arc<DataColumnSidecar<E>>),
/// A PONG response to a PING request.
Pong(Ping),
/// A response to a META_DATA request.
MetaData(Arc<MetaData<E>>),
}
/// Indicates which response is being terminated by a stream termination response.
#[derive(Debug, Clone)]
pub enum ResponseTermination {
/// Blocks by range stream termination.
BlocksByRange,
/// Blocks by root stream termination.
BlocksByRoot,
/// Blobs by range stream termination.
BlobsByRange,
/// Blobs by root stream termination.
BlobsByRoot,
/// Data column sidecars by root stream termination.
DataColumnsByRoot,
/// Data column sidecars by range stream termination.
DataColumnsByRange,
/// Light client updates by range stream termination.
LightClientUpdatesByRange,
}
impl ResponseTermination {
pub fn as_protocol(&self) -> Protocol {
match self {
ResponseTermination::BlocksByRange => Protocol::BlocksByRange,
ResponseTermination::BlocksByRoot => Protocol::BlocksByRoot,
ResponseTermination::BlobsByRange => Protocol::BlobsByRange,
ResponseTermination::BlobsByRoot => Protocol::BlobsByRoot,
ResponseTermination::DataColumnsByRoot => Protocol::DataColumnsByRoot,
ResponseTermination::DataColumnsByRange => Protocol::DataColumnsByRange,
ResponseTermination::LightClientUpdatesByRange => Protocol::LightClientUpdatesByRange,
}
}
}
/// The structured response containing a result/code indicating success or failure
/// and the contents of the response
#[derive(Debug, Clone)]
pub enum RpcResponse<E: EthSpec> {
/// The response is a successful.
Success(RpcSuccessResponse<E>),
Error(RpcErrorResponse, ErrorType),
/// Received a stream termination indicating which response is being terminated.
StreamTermination(ResponseTermination),
}
/// Request a light_client_bootstrap for light_clients peers.
#[derive(Encode, Decode, Clone, Debug, PartialEq)]
pub struct LightClientBootstrapRequest {
pub root: Hash256,
}
/// The code assigned to an erroneous `RPCResponse`.
#[derive(Debug, Clone, Copy, PartialEq, IntoStaticStr)]
#[strum(serialize_all = "snake_case")]
pub enum RpcErrorResponse {
RateLimited,
BlobsNotFoundForBlock,
InvalidRequest,
ServerError,
/// Error spec'd to indicate that a peer does not have blocks on a requested range.
ResourceUnavailable,
Unknown,
}
impl<E: EthSpec> RpcResponse<E> {
/// Used to encode the response in the codec.
pub fn as_u8(&self) -> Option<u8> {
match self {
RpcResponse::Success(_) => Some(0),
RpcResponse::Error(code, _) => Some(code.as_u8()),
RpcResponse::StreamTermination(_) => None,
}
}
/// Tells the codec whether to decode as an RPCResponse or an error.
pub fn is_response(response_code: u8) -> bool {
matches!(response_code, 0)
}
/// Builds an RPCCodedResponse from a response code and an ErrorMessage
pub fn from_error(response_code: u8, err: ErrorType) -> Self {
let code = match response_code {
1 => RpcErrorResponse::InvalidRequest,
2 => RpcErrorResponse::ServerError,
3 => RpcErrorResponse::ResourceUnavailable,
139 => RpcErrorResponse::RateLimited,
140 => RpcErrorResponse::BlobsNotFoundForBlock,
_ => RpcErrorResponse::Unknown,
};
RpcResponse::Error(code, err)
}
/// Returns true if this response always terminates the stream.
pub fn close_after(&self) -> bool {
!matches!(self, RpcResponse::Success(_))
}
}
impl RpcErrorResponse {
fn as_u8(&self) -> u8 {
match self {
RpcErrorResponse::InvalidRequest => 1,
RpcErrorResponse::ServerError => 2,
RpcErrorResponse::ResourceUnavailable => 3,
RpcErrorResponse::Unknown => 255,
RpcErrorResponse::RateLimited => 139,
RpcErrorResponse::BlobsNotFoundForBlock => 140,
}
}
}
use super::Protocol;
impl<E: EthSpec> RpcSuccessResponse<E> {
pub fn protocol(&self) -> Protocol {
match self {
RpcSuccessResponse::Status(_) => Protocol::Status,
RpcSuccessResponse::BlocksByRange(_) => Protocol::BlocksByRange,
RpcSuccessResponse::BlocksByRoot(_) => Protocol::BlocksByRoot,
RpcSuccessResponse::BlobsByRange(_) => Protocol::BlobsByRange,
RpcSuccessResponse::BlobsByRoot(_) => Protocol::BlobsByRoot,
RpcSuccessResponse::DataColumnsByRoot(_) => Protocol::DataColumnsByRoot,
RpcSuccessResponse::DataColumnsByRange(_) => Protocol::DataColumnsByRange,
RpcSuccessResponse::Pong(_) => Protocol::Ping,
RpcSuccessResponse::MetaData(_) => Protocol::MetaData,
RpcSuccessResponse::LightClientBootstrap(_) => Protocol::LightClientBootstrap,
RpcSuccessResponse::LightClientOptimisticUpdate(_) => {
Protocol::LightClientOptimisticUpdate
}
RpcSuccessResponse::LightClientFinalityUpdate(_) => Protocol::LightClientFinalityUpdate,
RpcSuccessResponse::LightClientUpdatesByRange(_) => Protocol::LightClientUpdatesByRange,
}
}
pub fn slot(&self) -> Option<Slot> {
match self {
Self::BlocksByRange(r) | Self::BlocksByRoot(r) => Some(r.slot()),
Self::BlobsByRange(r) | Self::BlobsByRoot(r) => {
Some(r.signed_block_header.message.slot)
}
Self::DataColumnsByRange(r) | Self::DataColumnsByRoot(r) => {
Some(r.signed_block_header.message.slot)
}
Self::LightClientBootstrap(r) => Some(r.get_slot()),
Self::LightClientFinalityUpdate(r) => Some(r.get_attested_header_slot()),
Self::LightClientOptimisticUpdate(r) => Some(r.get_slot()),
Self::LightClientUpdatesByRange(r) => Some(r.attested_header_slot()),
Self::MetaData(_) | Self::Status(_) | Self::Pong(_) => None,
}
}
}
impl std::fmt::Display for RpcErrorResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let repr = match self {
RpcErrorResponse::InvalidRequest => "The request was invalid",
RpcErrorResponse::ResourceUnavailable => "Resource unavailable",
RpcErrorResponse::ServerError => "Server error occurred",
RpcErrorResponse::Unknown => "Unknown error occurred",
RpcErrorResponse::RateLimited => "Rate limited",
RpcErrorResponse::BlobsNotFoundForBlock => "No blobs for the given root",
};
f.write_str(repr)
}
}
impl std::fmt::Display for StatusMessage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
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()
)
}
}
impl<E: EthSpec> std::fmt::Display for RpcSuccessResponse<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RpcSuccessResponse::Status(status) => write!(f, "{}", status),
RpcSuccessResponse::BlocksByRange(block) => {
write!(f, "BlocksByRange: Block slot: {}", block.slot())
}
RpcSuccessResponse::BlocksByRoot(block) => {
write!(f, "BlocksByRoot: Block slot: {}", block.slot())
}
RpcSuccessResponse::BlobsByRange(blob) => {
write!(f, "BlobsByRange: Blob slot: {}", blob.slot())
}
RpcSuccessResponse::BlobsByRoot(sidecar) => {
write!(f, "BlobsByRoot: Blob slot: {}", sidecar.slot())
}
RpcSuccessResponse::DataColumnsByRoot(sidecar) => {
write!(f, "DataColumnsByRoot: Data column slot: {}", sidecar.slot())
}
RpcSuccessResponse::DataColumnsByRange(sidecar) => {
write!(
f,
"DataColumnsByRange: Data column slot: {}",
sidecar.slot()
)
}
RpcSuccessResponse::Pong(ping) => write!(f, "Pong: {}", ping.data),
RpcSuccessResponse::MetaData(metadata) => {
write!(f, "Metadata: {}", metadata.seq_number())
}
RpcSuccessResponse::LightClientBootstrap(bootstrap) => {
write!(f, "LightClientBootstrap Slot: {}", bootstrap.get_slot())
}
RpcSuccessResponse::LightClientOptimisticUpdate(update) => {
write!(
f,
"LightClientOptimisticUpdate Slot: {}",
update.signature_slot()
)
}
RpcSuccessResponse::LightClientFinalityUpdate(update) => {
write!(
f,
"LightClientFinalityUpdate Slot: {}",
update.signature_slot()
)
}
RpcSuccessResponse::LightClientUpdatesByRange(update) => {
write!(
f,
"LightClientUpdatesByRange Slot: {}",
update.signature_slot(),
)
}
}
}
}
impl<E: EthSpec> std::fmt::Display for RpcResponse<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RpcResponse::Success(res) => write!(f, "{}", res),
RpcResponse::Error(code, err) => write!(f, "{}: {}", code, err),
RpcResponse::StreamTermination(_) => write!(f, "Stream Termination"),
}
}
}
impl std::fmt::Display for GoodbyeReason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GoodbyeReason::ClientShutdown => write!(f, "Client Shutdown"),
GoodbyeReason::IrrelevantNetwork => write!(f, "Irrelevant Network"),
GoodbyeReason::Fault => write!(f, "Fault"),
GoodbyeReason::UnableToVerifyNetwork => write!(f, "Unable to verify network"),
GoodbyeReason::TooManyPeers => write!(f, "Too many peers"),
GoodbyeReason::BadScore => write!(f, "Bad Score"),
GoodbyeReason::Banned => write!(f, "Banned"),
GoodbyeReason::BannedIP => write!(f, "BannedIP"),
GoodbyeReason::Unknown => write!(f, "Unknown Reason"),
}
}
}
impl std::fmt::Display for BlocksByRangeRequest {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Start Slot: {}, Count: {}",
self.start_slot(),
self.count()
)
}
}
impl std::fmt::Display for OldBlocksByRangeRequest {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Start Slot: {}, Count: {}, Step: {}",
self.start_slot(),
self.count(),
self.step()
)
}
}
impl std::fmt::Display for BlobsByRootRequest {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Request: BlobsByRoot: Number of Requested Roots: {}",
self.blob_ids.len()
)
}
}
impl std::fmt::Display for BlobsByRangeRequest {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Request: BlobsByRange: Start Slot: {}, Count: {}",
self.start_slot, self.count
)
}
}
impl<E: EthSpec> std::fmt::Display for DataColumnsByRootRequest<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Request: DataColumnsByRoot: Number of Requested Data Column Ids: {}",
self.data_column_ids.len()
)
}
}