From 32be063f0f8cc9b1dfc46da1e583ec99f232253f Mon Sep 17 00:00:00 2001 From: GeemoCandama <104614073+GeemoCandama@users.noreply.github.com> Date: Tue, 9 Apr 2024 16:23:39 +0900 Subject: [PATCH] Support `LightClientFinalityUpdate` and `LightClientOptimisticUpdate` rpcs (#3849) * add light client optimistic and finality update rpc * Arc the updates in the response * add conditional advertisement for both LightClientOptimisticUpdate and LightClientFinalityUpdate * alter display for inboundrequest light client optimistic and finality updates * remove LightClientOptimistic/FinalityReuest struct and some minor fixes * rebase * failing rpc_test for LightClientBootstrap and beginning of MockLib2pLightClient * minor change * added MockRPCHandler by importing everything except OutboundRequest. Need to implement the ConnectionHandler trait now should be copy pastable * almost there but ran into issue where needed to implement BaseOutboundRequest. * failing but running with a light client service of sorts * small test change * changed Protocol::LightClientBootstrap response limit * deleted some stuff from ConnectionHandler Implementation for the mock light client if you need to make something with multiple requests work maybe check here * deleted purging expired inbound/outbound streams code * deleted drive inbound streams that need to be processed * removed unused imports * made things private again * deleted inject_fully_negotiated_inbound * made more things private again * more * turned the logger off in the test * added failing test for new rpc * add rate limit for new rpcs * change InboundUpgrade function to use new rpcs. fmt. add test for LightClientFinalityUpdate * rebasing fix * add LightClientUpdate to handle_rpc functions * added context bytes * fmt * use correct unsed_tcp4_port function * fix for recent config changes and adding context_bytes for the light client protocols * fix clippy complaint * Merge branch 'unstable' into lc-reqresp # Conflicts: # beacon_node/beacon_processor/src/lib.rs # beacon_node/lighthouse_network/src/peer_manager/mod.rs # beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs # beacon_node/lighthouse_network/src/rpc/config.rs # beacon_node/lighthouse_network/src/rpc/methods.rs # beacon_node/lighthouse_network/src/rpc/mod.rs # beacon_node/lighthouse_network/src/rpc/outbound.rs # beacon_node/lighthouse_network/src/rpc/protocol.rs # beacon_node/lighthouse_network/src/rpc/rate_limiter.rs # beacon_node/lighthouse_network/src/rpc/self_limiter.rs # beacon_node/lighthouse_network/src/service/api_types.rs # beacon_node/lighthouse_network/tests/common/mod.rs # beacon_node/lighthouse_network/tests/rpc_tests.rs # beacon_node/network/src/network_beacon_processor/rpc_methods.rs # beacon_node/network/src/router.rs * Error handling updates and various cleanups. * Moar minor clean ups. * Do not ban peer for rate limiting light client requests * Merge branch 'unstable' into lc-reqresp. Also removed the mock light client tests to make it compile (See #4940). # Conflicts: # beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs # beacon_node/lighthouse_network/src/rpc/methods.rs # beacon_node/lighthouse_network/src/rpc/mod.rs # beacon_node/lighthouse_network/src/rpc/protocol.rs # beacon_node/lighthouse_network/src/service/api_types.rs # beacon_node/lighthouse_network/tests/common/mod.rs # beacon_node/network/src/network_beacon_processor/rpc_methods.rs # beacon_node/network/src/router.rs # consensus/types/src/light_client_bootstrap.rs # consensus/types/src/light_client_finality_update.rs # consensus/types/src/light_client_optimistic_update.rs * Remove unnecessary changes * Add missing light client queue handling. * Merge branch 'unstable' into lc-reqresp * Merge branch 'unstable' into lc-reqresp # Conflicts: # beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs # beacon_node/lighthouse_network/src/service/api_types.rs # consensus/types/src/light_client_finality_update.rs # consensus/types/src/light_client_optimistic_update.rs * Add context bytes for light client RPC responses. * Add RPC limits for light client object. * Fix lint * Fix incorrect light client max size computation. * Merge branch 'unstable' into lc-reqresp # Conflicts: # beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs # beacon_node/lighthouse_network/src/rpc/protocol.rs # beacon_node/lighthouse_network/src/service/api_types.rs * Remove unwanted local changes. * Merge branch 'unstable' into lc-reqresp * Replace `unimplemented` electra code path with deneb values. --- beacon_node/beacon_processor/src/lib.rs | 41 +++++- .../src/peer_manager/mod.rs | 10 +- .../src/rpc/codec/ssz_snappy.rs | 112 +++++++++++---- .../lighthouse_network/src/rpc/config.rs | 21 +++ .../lighthouse_network/src/rpc/methods.rs | 27 +++- .../lighthouse_network/src/rpc/protocol.rs | 133 +++++++++++++++++- .../src/rpc/rate_limiter.rs | 42 +++++- .../src/service/api_types.rs | 23 ++- .../lighthouse_network/src/service/mod.rs | 34 +++++ .../src/network_beacon_processor/mod.rs | 31 ++++ .../network_beacon_processor/rpc_methods.rs | 56 +++++++- beacon_node/network/src/router.rs | 13 +- .../per_epoch_processing/registry_updates.rs | 4 +- .../src/per_epoch_processing/slashings.rs | 4 +- .../types/src/execution_payload_header.rs | 19 ++- consensus/types/src/light_client_bootstrap.rs | 31 +++- .../types/src/light_client_finality_update.rs | 35 ++++- consensus/types/src/light_client_header.rs | 11 +- .../src/light_client_optimistic_update.rs | 35 ++++- 19 files changed, 612 insertions(+), 70 deletions(-) diff --git a/beacon_node/beacon_processor/src/lib.rs b/beacon_node/beacon_processor/src/lib.rs index 35082324fab..9dd72845180 100644 --- a/beacon_node/beacon_processor/src/lib.rs +++ b/beacon_node/beacon_processor/src/lib.rs @@ -187,6 +187,14 @@ const MAX_BLS_TO_EXECUTION_CHANGE_QUEUE_LEN: usize = 16_384; /// will be stored before we start dropping them. const MAX_LIGHT_CLIENT_BOOTSTRAP_QUEUE_LEN: usize = 1_024; +/// The maximum number of queued `LightClientOptimisticUpdateRequest` objects received from the network RPC that +/// will be stored before we start dropping them. +const MAX_LIGHT_CLIENT_OPTIMISTIC_UPDATE_QUEUE_LEN: usize = 512; + +/// The maximum number of queued `LightClientFinalityUpdateRequest` objects received from the network RPC that +/// will be stored before we start dropping them. +const MAX_LIGHT_CLIENT_FINALITY_UPDATE_QUEUE_LEN: usize = 512; + /// The maximum number of priority-0 (highest priority) messages that will be queued before /// they begin to be dropped. const MAX_API_REQUEST_P0_QUEUE_LEN: usize = 1_024; @@ -243,6 +251,8 @@ pub const BLOCKS_BY_ROOTS_REQUEST: &str = "blocks_by_roots_request"; pub const BLOBS_BY_RANGE_REQUEST: &str = "blobs_by_range_request"; pub const BLOBS_BY_ROOTS_REQUEST: &str = "blobs_by_roots_request"; pub const LIGHT_CLIENT_BOOTSTRAP_REQUEST: &str = "light_client_bootstrap"; +pub const LIGHT_CLIENT_FINALITY_UPDATE_REQUEST: &str = "light_client_finality_update_request"; +pub const LIGHT_CLIENT_OPTIMISTIC_UPDATE_REQUEST: &str = "light_client_optimistic_update_request"; pub const UNKNOWN_BLOCK_ATTESTATION: &str = "unknown_block_attestation"; pub const UNKNOWN_BLOCK_AGGREGATE: &str = "unknown_block_aggregate"; pub const UNKNOWN_LIGHT_CLIENT_UPDATE: &str = "unknown_light_client_update"; @@ -620,6 +630,8 @@ pub enum Work { BlobsByRootsRequest(BlockingFn), GossipBlsToExecutionChange(BlockingFn), LightClientBootstrapRequest(BlockingFn), + LightClientOptimisticUpdateRequest(BlockingFn), + LightClientFinalityUpdateRequest(BlockingFn), ApiRequestP0(BlockingOrAsync), ApiRequestP1(BlockingOrAsync), } @@ -659,6 +671,8 @@ impl Work { Work::BlobsByRangeRequest(_) => BLOBS_BY_RANGE_REQUEST, Work::BlobsByRootsRequest(_) => BLOBS_BY_ROOTS_REQUEST, Work::LightClientBootstrapRequest(_) => LIGHT_CLIENT_BOOTSTRAP_REQUEST, + Work::LightClientOptimisticUpdateRequest(_) => LIGHT_CLIENT_OPTIMISTIC_UPDATE_REQUEST, + Work::LightClientFinalityUpdateRequest(_) => LIGHT_CLIENT_FINALITY_UPDATE_REQUEST, Work::UnknownBlockAttestation { .. } => UNKNOWN_BLOCK_ATTESTATION, Work::UnknownBlockAggregate { .. } => UNKNOWN_BLOCK_AGGREGATE, Work::GossipBlsToExecutionChange(_) => GOSSIP_BLS_TO_EXECUTION_CHANGE, @@ -820,7 +834,11 @@ impl BeaconProcessor { let mut gossip_bls_to_execution_change_queue = FifoQueue::new(MAX_BLS_TO_EXECUTION_CHANGE_QUEUE_LEN); - let mut lcbootstrap_queue = FifoQueue::new(MAX_LIGHT_CLIENT_BOOTSTRAP_QUEUE_LEN); + let mut lc_bootstrap_queue = FifoQueue::new(MAX_LIGHT_CLIENT_BOOTSTRAP_QUEUE_LEN); + let mut lc_optimistic_update_queue = + FifoQueue::new(MAX_LIGHT_CLIENT_OPTIMISTIC_UPDATE_QUEUE_LEN); + let mut lc_finality_update_queue = + FifoQueue::new(MAX_LIGHT_CLIENT_FINALITY_UPDATE_QUEUE_LEN); let mut api_request_p0_queue = FifoQueue::new(MAX_API_REQUEST_P0_QUEUE_LEN); let mut api_request_p1_queue = FifoQueue::new(MAX_API_REQUEST_P1_QUEUE_LEN); @@ -1137,9 +1155,14 @@ impl BeaconProcessor { // Handle backfill sync chain segments. } else if let Some(item) = backfill_chain_segment.pop() { self.spawn_worker(item, idle_tx); - // This statement should always be the final else statement. - } else if let Some(item) = lcbootstrap_queue.pop() { + // Handle light client requests. + } else if let Some(item) = lc_bootstrap_queue.pop() { self.spawn_worker(item, idle_tx); + } else if let Some(item) = lc_optimistic_update_queue.pop() { + self.spawn_worker(item, idle_tx); + } else if let Some(item) = lc_finality_update_queue.pop() { + self.spawn_worker(item, idle_tx); + // This statement should always be the final else statement. } else { // Let the journal know that a worker is freed and there's nothing else // for it to do. @@ -1249,7 +1272,13 @@ impl BeaconProcessor { blbrange_queue.push(work, work_id, &self.log) } Work::LightClientBootstrapRequest { .. } => { - lcbootstrap_queue.push(work, work_id, &self.log) + lc_bootstrap_queue.push(work, work_id, &self.log) + } + Work::LightClientOptimisticUpdateRequest { .. } => { + lc_optimistic_update_queue.push(work, work_id, &self.log) + } + Work::LightClientFinalityUpdateRequest { .. } => { + lc_finality_update_queue.push(work, work_id, &self.log) } Work::UnknownBlockAttestation { .. } => { unknown_block_attestation_queue.push(work) @@ -1480,7 +1509,9 @@ impl BeaconProcessor { | Work::GossipLightClientOptimisticUpdate(process_fn) | Work::Status(process_fn) | Work::GossipBlsToExecutionChange(process_fn) - | Work::LightClientBootstrapRequest(process_fn) => { + | Work::LightClientBootstrapRequest(process_fn) + | Work::LightClientOptimisticUpdateRequest(process_fn) + | Work::LightClientFinalityUpdateRequest(process_fn) => { task_spawner.spawn_blocking(process_fn) } }; diff --git a/beacon_node/lighthouse_network/src/peer_manager/mod.rs b/beacon_node/lighthouse_network/src/peer_manager/mod.rs index b2033d55fc5..0d9a7c60dd2 100644 --- a/beacon_node/lighthouse_network/src/peer_manager/mod.rs +++ b/beacon_node/lighthouse_network/src/peer_manager/mod.rs @@ -553,7 +553,11 @@ impl PeerManager { Protocol::BlocksByRange => PeerAction::MidToleranceError, Protocol::BlocksByRoot => PeerAction::MidToleranceError, Protocol::BlobsByRange => PeerAction::MidToleranceError, - Protocol::LightClientBootstrap => PeerAction::LowToleranceError, + // Lighthouse does not currently make light client requests; therefore, this + // is an unexpected scenario. We do not ban the peer for rate limiting. + Protocol::LightClientBootstrap => return, + Protocol::LightClientOptimisticUpdate => return, + Protocol::LightClientFinalityUpdate => return, Protocol::BlobsByRoot => PeerAction::MidToleranceError, Protocol::Goodbye => PeerAction::LowToleranceError, Protocol::MetaData => PeerAction::LowToleranceError, @@ -575,6 +579,8 @@ impl PeerManager { Protocol::BlobsByRoot => return, Protocol::Goodbye => return, Protocol::LightClientBootstrap => return, + Protocol::LightClientOptimisticUpdate => return, + Protocol::LightClientFinalityUpdate => return, Protocol::MetaData => PeerAction::Fatal, Protocol::Status => PeerAction::Fatal, } @@ -592,6 +598,8 @@ impl PeerManager { Protocol::BlobsByRange => PeerAction::MidToleranceError, Protocol::BlobsByRoot => PeerAction::MidToleranceError, Protocol::LightClientBootstrap => return, + Protocol::LightClientOptimisticUpdate => return, + Protocol::LightClientFinalityUpdate => return, Protocol::Goodbye => return, Protocol::MetaData => return, Protocol::Status => return, diff --git a/beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs b/beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs index 45c722167a3..4cbb6582583 100644 --- a/beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs +++ b/beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs @@ -15,12 +15,11 @@ use std::io::{Read, Write}; use std::marker::PhantomData; use std::sync::Arc; use tokio_util::codec::{Decoder, Encoder}; -use types::ChainSpec; use types::{ - BlobSidecar, EthSpec, ForkContext, ForkName, Hash256, LightClientBootstrap, - RuntimeVariableList, SignedBeaconBlock, SignedBeaconBlockAltair, SignedBeaconBlockBase, - SignedBeaconBlockCapella, SignedBeaconBlockDeneb, SignedBeaconBlockElectra, - SignedBeaconBlockMerge, + BlobSidecar, ChainSpec, EthSpec, ForkContext, ForkName, Hash256, LightClientBootstrap, + LightClientFinalityUpdate, LightClientOptimisticUpdate, RuntimeVariableList, SignedBeaconBlock, + SignedBeaconBlockAltair, SignedBeaconBlockBase, SignedBeaconBlockCapella, + SignedBeaconBlockDeneb, SignedBeaconBlockElectra, SignedBeaconBlockMerge, }; use unsigned_varint::codec::Uvi; @@ -72,6 +71,8 @@ impl Encoder> for SSZSnappyInboundCodec { RPCResponse::BlobsByRange(res) => res.as_ssz_bytes(), RPCResponse::BlobsByRoot(res) => res.as_ssz_bytes(), RPCResponse::LightClientBootstrap(res) => res.as_ssz_bytes(), + RPCResponse::LightClientOptimisticUpdate(res) => res.as_ssz_bytes(), + RPCResponse::LightClientFinalityUpdate(res) => res.as_ssz_bytes(), RPCResponse::Pong(res) => res.data.as_ssz_bytes(), RPCResponse::MetaData(res) => // Encode the correct version of the MetaData response based on the negotiated version. @@ -387,32 +388,51 @@ fn context_bytes( // Add the context bytes if required if protocol.has_context_bytes() { if let RPCCodedResponse::Success(rpc_variant) = resp { - if let RPCResponse::BlocksByRange(ref_box_block) - | RPCResponse::BlocksByRoot(ref_box_block) = rpc_variant - { - return match **ref_box_block { - // NOTE: If you are adding another fork type here, be sure to modify the - // `fork_context.to_context_bytes()` function to support it as well! - SignedBeaconBlock::Electra { .. } => { - fork_context.to_context_bytes(ForkName::Electra) - } - SignedBeaconBlock::Deneb { .. } => { - fork_context.to_context_bytes(ForkName::Deneb) - } - SignedBeaconBlock::Capella { .. } => { - fork_context.to_context_bytes(ForkName::Capella) - } - SignedBeaconBlock::Merge { .. } => { - fork_context.to_context_bytes(ForkName::Merge) - } - SignedBeaconBlock::Altair { .. } => { - fork_context.to_context_bytes(ForkName::Altair) - } - SignedBeaconBlock::Base { .. } => Some(fork_context.genesis_context_bytes()), - }; - } - if let RPCResponse::BlobsByRange(_) | RPCResponse::BlobsByRoot(_) = rpc_variant { - return fork_context.to_context_bytes(ForkName::Deneb); + match rpc_variant { + RPCResponse::BlocksByRange(ref_box_block) + | RPCResponse::BlocksByRoot(ref_box_block) => { + return match **ref_box_block { + // NOTE: If you are adding another fork type here, be sure to modify the + // `fork_context.to_context_bytes()` function to support it as well! + SignedBeaconBlock::Electra { .. } => { + fork_context.to_context_bytes(ForkName::Electra) + } + SignedBeaconBlock::Deneb { .. } => { + fork_context.to_context_bytes(ForkName::Deneb) + } + SignedBeaconBlock::Capella { .. } => { + fork_context.to_context_bytes(ForkName::Capella) + } + SignedBeaconBlock::Merge { .. } => { + fork_context.to_context_bytes(ForkName::Merge) + } + SignedBeaconBlock::Altair { .. } => { + fork_context.to_context_bytes(ForkName::Altair) + } + SignedBeaconBlock::Base { .. } => { + Some(fork_context.genesis_context_bytes()) + } + }; + } + RPCResponse::BlobsByRange(_) | RPCResponse::BlobsByRoot(_) => { + return fork_context.to_context_bytes(ForkName::Deneb); + } + RPCResponse::LightClientBootstrap(lc_bootstrap) => { + return lc_bootstrap + .map_with_fork_name(|fork_name| fork_context.to_context_bytes(fork_name)); + } + RPCResponse::LightClientOptimisticUpdate(lc_optimistic_update) => { + return lc_optimistic_update + .map_with_fork_name(|fork_name| fork_context.to_context_bytes(fork_name)); + } + RPCResponse::LightClientFinalityUpdate(lc_finality_update) => { + return lc_finality_update + .map_with_fork_name(|fork_name| fork_context.to_context_bytes(fork_name)); + } + // These will not pass the has_context_bytes() check + RPCResponse::Status(_) | RPCResponse::Pong(_) | RPCResponse::MetaData(_) => { + return None; + } } } } @@ -500,6 +520,12 @@ fn handle_rpc_request( root: Hash256::from_ssz_bytes(decoded_buffer)?, }), )), + SupportedProtocol::LightClientOptimisticUpdateV1 => { + Ok(Some(InboundRequest::LightClientOptimisticUpdate)) + } + SupportedProtocol::LightClientFinalityUpdateV1 => { + Ok(Some(InboundRequest::LightClientFinalityUpdate)) + } // MetaData requests return early from InboundUpgrade and do not reach the decoder. // Handle this case just for completeness. SupportedProtocol::MetaDataV2 => { @@ -596,6 +622,30 @@ fn handle_rpc_response( ), )), }, + SupportedProtocol::LightClientOptimisticUpdateV1 => match fork_name { + Some(fork_name) => Ok(Some(RPCResponse::LightClientOptimisticUpdate(Arc::new( + LightClientOptimisticUpdate::from_ssz_bytes(decoded_buffer, fork_name)?, + )))), + None => Err(RPCError::ErrorResponse( + RPCResponseErrorCode::InvalidRequest, + format!( + "No context bytes provided for {:?} response", + versioned_protocol + ), + )), + }, + SupportedProtocol::LightClientFinalityUpdateV1 => match fork_name { + Some(fork_name) => Ok(Some(RPCResponse::LightClientFinalityUpdate(Arc::new( + LightClientFinalityUpdate::from_ssz_bytes(decoded_buffer, fork_name)?, + )))), + None => Err(RPCError::ErrorResponse( + RPCResponseErrorCode::InvalidRequest, + format!( + "No context bytes provided for {:?} response", + versioned_protocol + ), + )), + }, // MetaData V2 responses have no context bytes, so behave similarly to V1 responses SupportedProtocol::MetaDataV2 => Ok(Some(RPCResponse::MetaData(MetaData::V2( MetaDataV2::from_ssz_bytes(decoded_buffer)?, diff --git a/beacon_node/lighthouse_network/src/rpc/config.rs b/beacon_node/lighthouse_network/src/rpc/config.rs index 9895149198a..08b81c7eae5 100644 --- a/beacon_node/lighthouse_network/src/rpc/config.rs +++ b/beacon_node/lighthouse_network/src/rpc/config.rs @@ -92,6 +92,8 @@ pub struct RateLimiterConfig { pub(super) blobs_by_range_quota: Quota, pub(super) blobs_by_root_quota: Quota, pub(super) light_client_bootstrap_quota: Quota, + pub(super) light_client_optimistic_update_quota: Quota, + pub(super) light_client_finality_update_quota: Quota, } impl RateLimiterConfig { @@ -104,6 +106,8 @@ impl RateLimiterConfig { pub const DEFAULT_BLOBS_BY_RANGE_QUOTA: Quota = Quota::n_every(768, 10); pub const DEFAULT_BLOBS_BY_ROOT_QUOTA: Quota = Quota::n_every(128, 10); pub const DEFAULT_LIGHT_CLIENT_BOOTSTRAP_QUOTA: Quota = Quota::one_every(10); + pub const DEFAULT_LIGHT_CLIENT_OPTIMISTIC_UPDATE_QUOTA: Quota = Quota::one_every(10); + pub const DEFAULT_LIGHT_CLIENT_FINALITY_UPDATE_QUOTA: Quota = Quota::one_every(10); } impl Default for RateLimiterConfig { @@ -118,6 +122,9 @@ impl Default for RateLimiterConfig { blobs_by_range_quota: Self::DEFAULT_BLOBS_BY_RANGE_QUOTA, blobs_by_root_quota: Self::DEFAULT_BLOBS_BY_ROOT_QUOTA, light_client_bootstrap_quota: Self::DEFAULT_LIGHT_CLIENT_BOOTSTRAP_QUOTA, + light_client_optimistic_update_quota: + Self::DEFAULT_LIGHT_CLIENT_OPTIMISTIC_UPDATE_QUOTA, + light_client_finality_update_quota: Self::DEFAULT_LIGHT_CLIENT_FINALITY_UPDATE_QUOTA, } } } @@ -164,6 +171,8 @@ impl FromStr for RateLimiterConfig { let mut blobs_by_range_quota = None; let mut blobs_by_root_quota = None; let mut light_client_bootstrap_quota = None; + let mut light_client_optimistic_update_quota = None; + let mut light_client_finality_update_quota = None; for proto_def in s.split(';') { let ProtocolQuota { protocol, quota } = proto_def.parse()?; @@ -180,6 +189,14 @@ impl FromStr for RateLimiterConfig { Protocol::LightClientBootstrap => { light_client_bootstrap_quota = light_client_bootstrap_quota.or(quota) } + Protocol::LightClientOptimisticUpdate => { + light_client_optimistic_update_quota = + light_client_optimistic_update_quota.or(quota) + } + Protocol::LightClientFinalityUpdate => { + light_client_finality_update_quota = + light_client_finality_update_quota.or(quota) + } } } Ok(RateLimiterConfig { @@ -196,6 +213,10 @@ impl FromStr for RateLimiterConfig { blobs_by_root_quota: blobs_by_root_quota.unwrap_or(Self::DEFAULT_BLOBS_BY_ROOT_QUOTA), light_client_bootstrap_quota: light_client_bootstrap_quota .unwrap_or(Self::DEFAULT_LIGHT_CLIENT_BOOTSTRAP_QUOTA), + light_client_optimistic_update_quota: light_client_optimistic_update_quota + .unwrap_or(Self::DEFAULT_LIGHT_CLIENT_OPTIMISTIC_UPDATE_QUOTA), + light_client_finality_update_quota: light_client_finality_update_quota + .unwrap_or(Self::DEFAULT_LIGHT_CLIENT_FINALITY_UPDATE_QUOTA), }) } } diff --git a/beacon_node/lighthouse_network/src/rpc/methods.rs b/beacon_node/lighthouse_network/src/rpc/methods.rs index 76a799cdbc5..67eea09ea77 100644 --- a/beacon_node/lighthouse_network/src/rpc/methods.rs +++ b/beacon_node/lighthouse_network/src/rpc/methods.rs @@ -15,7 +15,8 @@ use superstruct::superstruct; use types::blob_sidecar::BlobIdentifier; use types::{ blob_sidecar::BlobSidecar, ChainSpec, Epoch, EthSpec, Hash256, LightClientBootstrap, - RuntimeVariableList, SignedBeaconBlock, Slot, + LightClientFinalityUpdate, LightClientOptimisticUpdate, RuntimeVariableList, SignedBeaconBlock, + Slot, }; /// Maximum length of error message. @@ -390,6 +391,12 @@ pub enum RPCResponse { /// A response to a get LIGHT_CLIENT_BOOTSTRAP request. LightClientBootstrap(Arc>), + /// A response to a get LIGHT_CLIENT_OPTIMISTIC_UPDATE request. + LightClientOptimisticUpdate(Arc>), + + /// A response to a get LIGHT_CLIENT_FINALITY_UPDATE request. + LightClientFinalityUpdate(Arc>), + /// A response to a get BLOBS_BY_ROOT request. BlobsByRoot(Arc>), @@ -488,6 +495,8 @@ impl RPCCodedResponse { RPCResponse::Pong(_) => false, RPCResponse::MetaData(_) => false, RPCResponse::LightClientBootstrap(_) => false, + RPCResponse::LightClientOptimisticUpdate(_) => false, + RPCResponse::LightClientFinalityUpdate(_) => false, }, RPCCodedResponse::Error(_, _) => true, // Stream terminations are part of responses that have chunks @@ -526,6 +535,8 @@ impl RPCResponse { RPCResponse::Pong(_) => Protocol::Ping, RPCResponse::MetaData(_) => Protocol::MetaData, RPCResponse::LightClientBootstrap(_) => Protocol::LightClientBootstrap, + RPCResponse::LightClientOptimisticUpdate(_) => Protocol::LightClientOptimisticUpdate, + RPCResponse::LightClientFinalityUpdate(_) => Protocol::LightClientFinalityUpdate, } } } @@ -571,6 +582,20 @@ impl std::fmt::Display for RPCResponse { RPCResponse::LightClientBootstrap(bootstrap) => { write!(f, "LightClientBootstrap Slot: {}", bootstrap.get_slot()) } + RPCResponse::LightClientOptimisticUpdate(update) => { + write!( + f, + "LightClientOptimisticUpdate Slot: {}", + update.signature_slot() + ) + } + RPCResponse::LightClientFinalityUpdate(update) => { + write!( + f, + "LightClientFinalityUpdate Slot: {}", + update.signature_slot() + ) + } } } } diff --git a/beacon_node/lighthouse_network/src/rpc/protocol.rs b/beacon_node/lighthouse_network/src/rpc/protocol.rs index f72afacc849..6ff72f658a8 100644 --- a/beacon_node/lighthouse_network/src/rpc/protocol.rs +++ b/beacon_node/lighthouse_network/src/rpc/protocol.rs @@ -19,7 +19,9 @@ use tokio_util::{ use types::{ BeaconBlock, BeaconBlockAltair, BeaconBlockBase, BeaconBlockCapella, BeaconBlockElectra, BeaconBlockMerge, BlobSidecar, ChainSpec, EmptyBlock, EthSpec, ForkContext, ForkName, - MainnetEthSpec, Signature, SignedBeaconBlock, + LightClientBootstrap, LightClientBootstrapAltair, LightClientFinalityUpdate, + LightClientFinalityUpdateAltair, LightClientOptimisticUpdate, + LightClientOptimisticUpdateAltair, MainnetEthSpec, Signature, SignedBeaconBlock, }; lazy_static! { @@ -110,6 +112,16 @@ lazy_static! { ]) .as_ssz_bytes() .len(); + + pub static ref LIGHT_CLIENT_FINALITY_UPDATE_CAPELLA_MAX: usize = LightClientFinalityUpdate::::ssz_max_len_for_fork(ForkName::Capella); + pub static ref LIGHT_CLIENT_FINALITY_UPDATE_DENEB_MAX: usize = LightClientFinalityUpdate::::ssz_max_len_for_fork(ForkName::Deneb); + pub static ref LIGHT_CLIENT_FINALITY_UPDATE_ELECTRA_MAX: usize = LightClientFinalityUpdate::::ssz_max_len_for_fork(ForkName::Electra); + pub static ref LIGHT_CLIENT_OPTIMISTIC_UPDATE_CAPELLA_MAX: usize = LightClientOptimisticUpdate::::ssz_max_len_for_fork(ForkName::Capella); + pub static ref LIGHT_CLIENT_OPTIMISTIC_UPDATE_DENEB_MAX: usize = LightClientOptimisticUpdate::::ssz_max_len_for_fork(ForkName::Deneb); + pub static ref LIGHT_CLIENT_OPTIMISTIC_UPDATE_ELECTRA_MAX: usize = LightClientOptimisticUpdate::::ssz_max_len_for_fork(ForkName::Electra); + pub static ref LIGHT_CLIENT_BOOTSTRAP_CAPELLA_MAX: usize = LightClientBootstrap::::ssz_max_len_for_fork(ForkName::Capella); + pub static ref LIGHT_CLIENT_BOOTSTRAP_DENEB_MAX: usize = LightClientBootstrap::::ssz_max_len_for_fork(ForkName::Deneb); + pub static ref LIGHT_CLIENT_BOOTSTRAP_ELECTRA_MAX: usize = LightClientBootstrap::::ssz_max_len_for_fork(ForkName::Electra); } /// The protocol prefix the RPC protocol id. @@ -161,6 +173,56 @@ pub fn rpc_block_limits_by_fork(current_fork: ForkName) -> RpcLimits { } } +fn rpc_light_client_finality_update_limits_by_fork(current_fork: ForkName) -> RpcLimits { + let altair_fixed_len = LightClientFinalityUpdateAltair::::ssz_fixed_len(); + + match ¤t_fork { + ForkName::Base => RpcLimits::new(0, 0), + ForkName::Altair | ForkName::Merge => RpcLimits::new(altair_fixed_len, altair_fixed_len), + ForkName::Capella => { + RpcLimits::new(altair_fixed_len, *LIGHT_CLIENT_FINALITY_UPDATE_CAPELLA_MAX) + } + ForkName::Deneb => { + RpcLimits::new(altair_fixed_len, *LIGHT_CLIENT_FINALITY_UPDATE_DENEB_MAX) + } + ForkName::Electra => { + RpcLimits::new(altair_fixed_len, *LIGHT_CLIENT_FINALITY_UPDATE_ELECTRA_MAX) + } + } +} + +fn rpc_light_client_optimistic_update_limits_by_fork(current_fork: ForkName) -> RpcLimits { + let altair_fixed_len = LightClientOptimisticUpdateAltair::::ssz_fixed_len(); + + match ¤t_fork { + ForkName::Base => RpcLimits::new(0, 0), + ForkName::Altair | ForkName::Merge => RpcLimits::new(altair_fixed_len, altair_fixed_len), + ForkName::Capella => RpcLimits::new( + altair_fixed_len, + *LIGHT_CLIENT_OPTIMISTIC_UPDATE_CAPELLA_MAX, + ), + ForkName::Deneb => { + RpcLimits::new(altair_fixed_len, *LIGHT_CLIENT_OPTIMISTIC_UPDATE_DENEB_MAX) + } + ForkName::Electra => RpcLimits::new( + altair_fixed_len, + *LIGHT_CLIENT_OPTIMISTIC_UPDATE_ELECTRA_MAX, + ), + } +} + +fn rpc_light_client_bootstrap_limits_by_fork(current_fork: ForkName) -> RpcLimits { + let altair_fixed_len = LightClientBootstrapAltair::::ssz_fixed_len(); + + match ¤t_fork { + ForkName::Base => RpcLimits::new(0, 0), + ForkName::Altair | ForkName::Merge => RpcLimits::new(altair_fixed_len, altair_fixed_len), + ForkName::Capella => RpcLimits::new(altair_fixed_len, *LIGHT_CLIENT_BOOTSTRAP_CAPELLA_MAX), + ForkName::Deneb => RpcLimits::new(altair_fixed_len, *LIGHT_CLIENT_BOOTSTRAP_DENEB_MAX), + ForkName::Electra => RpcLimits::new(altair_fixed_len, *LIGHT_CLIENT_BOOTSTRAP_ELECTRA_MAX), + } +} + /// Protocol names to be used. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumString, AsRefStr, Display)] #[strum(serialize_all = "snake_case")] @@ -189,6 +251,12 @@ pub enum Protocol { /// The `LightClientBootstrap` protocol name. #[strum(serialize = "light_client_bootstrap")] LightClientBootstrap, + /// The `LightClientOptimisticUpdate` protocol name. + #[strum(serialize = "light_client_optimistic_update")] + LightClientOptimisticUpdate, + /// The `LightClientFinalityUpdate` protocol name. + #[strum(serialize = "light_client_finality_update")] + LightClientFinalityUpdate, } impl Protocol { @@ -203,6 +271,8 @@ impl Protocol { Protocol::Ping => None, Protocol::MetaData => None, Protocol::LightClientBootstrap => None, + Protocol::LightClientOptimisticUpdate => None, + Protocol::LightClientFinalityUpdate => None, } } } @@ -228,6 +298,8 @@ pub enum SupportedProtocol { MetaDataV1, MetaDataV2, LightClientBootstrapV1, + LightClientOptimisticUpdateV1, + LightClientFinalityUpdateV1, } impl SupportedProtocol { @@ -245,6 +317,8 @@ impl SupportedProtocol { SupportedProtocol::MetaDataV1 => "1", SupportedProtocol::MetaDataV2 => "2", SupportedProtocol::LightClientBootstrapV1 => "1", + SupportedProtocol::LightClientOptimisticUpdateV1 => "1", + SupportedProtocol::LightClientFinalityUpdateV1 => "1", } } @@ -262,6 +336,10 @@ impl SupportedProtocol { SupportedProtocol::MetaDataV1 => Protocol::MetaData, SupportedProtocol::MetaDataV2 => Protocol::MetaData, SupportedProtocol::LightClientBootstrapV1 => Protocol::LightClientBootstrap, + SupportedProtocol::LightClientOptimisticUpdateV1 => { + Protocol::LightClientOptimisticUpdate + } + SupportedProtocol::LightClientFinalityUpdateV1 => Protocol::LightClientFinalityUpdate, } } @@ -318,6 +396,14 @@ impl UpgradeInfo for RPCProtocol { SupportedProtocol::LightClientBootstrapV1, Encoding::SSZSnappy, )); + supported_protocols.push(ProtocolId::new( + SupportedProtocol::LightClientOptimisticUpdateV1, + Encoding::SSZSnappy, + )); + supported_protocols.push(ProtocolId::new( + SupportedProtocol::LightClientFinalityUpdateV1, + Encoding::SSZSnappy, + )); } supported_protocols } @@ -392,6 +478,8 @@ impl ProtocolId { ::ssz_fixed_len(), ::ssz_fixed_len(), ), + Protocol::LightClientOptimisticUpdate => RpcLimits::new(0, 0), + Protocol::LightClientFinalityUpdate => RpcLimits::new(0, 0), Protocol::MetaData => RpcLimits::new(0, 0), // Metadata requests are empty } } @@ -416,10 +504,15 @@ impl ProtocolId { as Encode>::ssz_fixed_len(), as Encode>::ssz_fixed_len(), ), - Protocol::LightClientBootstrap => RpcLimits::new( - ::ssz_fixed_len(), - ::ssz_fixed_len(), - ), + Protocol::LightClientBootstrap => { + rpc_light_client_bootstrap_limits_by_fork(fork_context.current_fork()) + } + Protocol::LightClientOptimisticUpdate => { + rpc_light_client_optimistic_update_limits_by_fork(fork_context.current_fork()) + } + Protocol::LightClientFinalityUpdate => { + rpc_light_client_finality_update_limits_by_fork(fork_context.current_fork()) + } } } @@ -431,7 +524,9 @@ impl ProtocolId { | SupportedProtocol::BlocksByRootV2 | SupportedProtocol::BlobsByRangeV1 | SupportedProtocol::BlobsByRootV1 - | SupportedProtocol::LightClientBootstrapV1 => true, + | SupportedProtocol::LightClientBootstrapV1 + | SupportedProtocol::LightClientOptimisticUpdateV1 + | SupportedProtocol::LightClientFinalityUpdateV1 => true, SupportedProtocol::StatusV1 | SupportedProtocol::BlocksByRootV1 | SupportedProtocol::BlocksByRangeV1 @@ -515,6 +610,12 @@ where SupportedProtocol::MetaDataV2 => { Ok((InboundRequest::MetaData(MetadataRequest::new_v2()), socket)) } + SupportedProtocol::LightClientOptimisticUpdateV1 => { + Ok((InboundRequest::LightClientOptimisticUpdate, socket)) + } + SupportedProtocol::LightClientFinalityUpdateV1 => { + Ok((InboundRequest::LightClientFinalityUpdate, socket)) + } _ => { match tokio::time::timeout( Duration::from_secs(REQUEST_TIMEOUT), @@ -543,6 +644,8 @@ pub enum InboundRequest { BlobsByRange(BlobsByRangeRequest), BlobsByRoot(BlobsByRootRequest), LightClientBootstrap(LightClientBootstrapRequest), + LightClientOptimisticUpdate, + LightClientFinalityUpdate, Ping(Ping), MetaData(MetadataRequest), } @@ -563,6 +666,8 @@ impl InboundRequest { InboundRequest::Ping(_) => 1, InboundRequest::MetaData(_) => 1, InboundRequest::LightClientBootstrap(_) => 1, + InboundRequest::LightClientOptimisticUpdate => 1, + InboundRequest::LightClientFinalityUpdate => 1, } } @@ -587,6 +692,12 @@ impl InboundRequest { MetadataRequest::V2(_) => SupportedProtocol::MetaDataV2, }, InboundRequest::LightClientBootstrap(_) => SupportedProtocol::LightClientBootstrapV1, + InboundRequest::LightClientOptimisticUpdate => { + SupportedProtocol::LightClientOptimisticUpdateV1 + } + InboundRequest::LightClientFinalityUpdate => { + SupportedProtocol::LightClientFinalityUpdateV1 + } } } @@ -605,6 +716,8 @@ impl InboundRequest { InboundRequest::Ping(_) => unreachable!(), InboundRequest::MetaData(_) => unreachable!(), InboundRequest::LightClientBootstrap(_) => unreachable!(), + InboundRequest::LightClientFinalityUpdate => unreachable!(), + InboundRequest::LightClientOptimisticUpdate => unreachable!(), } } } @@ -711,7 +824,13 @@ impl std::fmt::Display for InboundRequest { InboundRequest::Ping(ping) => write!(f, "Ping: {}", ping.data), InboundRequest::MetaData(_) => write!(f, "MetaData request"), InboundRequest::LightClientBootstrap(bootstrap) => { - write!(f, "LightClientBootstrap: {}", bootstrap.root) + write!(f, "Light client boostrap: {}", bootstrap.root) + } + InboundRequest::LightClientOptimisticUpdate => { + write!(f, "Light client optimistic update request") + } + InboundRequest::LightClientFinalityUpdate => { + write!(f, "Light client finality update request") } } } diff --git a/beacon_node/lighthouse_network/src/rpc/rate_limiter.rs b/beacon_node/lighthouse_network/src/rpc/rate_limiter.rs index e26f07a9c13..801a4af54b3 100644 --- a/beacon_node/lighthouse_network/src/rpc/rate_limiter.rs +++ b/beacon_node/lighthouse_network/src/rpc/rate_limiter.rs @@ -98,7 +98,11 @@ pub struct RPCRateLimiter { /// BlobsByRoot rate limiter. blbroot_rl: Limiter, /// LightClientBootstrap rate limiter. - lcbootstrap_rl: Limiter, + lc_bootstrap_rl: Limiter, + /// LightClientOptimisticUpdate rate limiter. + lc_optimistic_update_rl: Limiter, + /// LightClientFinalityUpdate rate limiter. + lc_finality_update_rl: Limiter, } /// Error type for non conformant requests @@ -131,6 +135,10 @@ pub struct RPCRateLimiterBuilder { blbroot_quota: Option, /// Quota for the LightClientBootstrap protocol. lcbootstrap_quota: Option, + /// Quota for the LightClientOptimisticUpdate protocol. + lc_optimistic_update_quota: Option, + /// Quota for the LightClientOptimisticUpdate protocol. + lc_finality_update_quota: Option, } impl RPCRateLimiterBuilder { @@ -147,6 +155,8 @@ impl RPCRateLimiterBuilder { Protocol::BlobsByRange => self.blbrange_quota = q, Protocol::BlobsByRoot => self.blbroot_quota = q, Protocol::LightClientBootstrap => self.lcbootstrap_quota = q, + Protocol::LightClientOptimisticUpdate => self.lc_optimistic_update_quota = q, + Protocol::LightClientFinalityUpdate => self.lc_finality_update_quota = q, } self } @@ -163,9 +173,15 @@ impl RPCRateLimiterBuilder { let bbrange_quota = self .bbrange_quota .ok_or("BlocksByRange quota not specified")?; - let lcbootstrap_quote = self + let lc_bootstrap_quota = self .lcbootstrap_quota .ok_or("LightClientBootstrap quota not specified")?; + let lc_optimistic_update_quota = self + .lc_optimistic_update_quota + .ok_or("LightClientOptimisticUpdate quota not specified")?; + let lc_finality_update_quota = self + .lc_finality_update_quota + .ok_or("LightClientFinalityUpdate quota not specified")?; let blbrange_quota = self .blbrange_quota @@ -184,7 +200,9 @@ impl RPCRateLimiterBuilder { let bbrange_rl = Limiter::from_quota(bbrange_quota)?; let blbrange_rl = Limiter::from_quota(blbrange_quota)?; let blbroot_rl = Limiter::from_quota(blbroots_quota)?; - let lcbootstrap_rl = Limiter::from_quota(lcbootstrap_quote)?; + let lc_bootstrap_rl = Limiter::from_quota(lc_bootstrap_quota)?; + let lc_optimistic_update_rl = Limiter::from_quota(lc_optimistic_update_quota)?; + let lc_finality_update_rl = Limiter::from_quota(lc_finality_update_quota)?; // check for peers to prune every 30 seconds, starting in 30 seconds let prune_every = tokio::time::Duration::from_secs(30); @@ -200,7 +218,9 @@ impl RPCRateLimiterBuilder { bbrange_rl, blbrange_rl, blbroot_rl, - lcbootstrap_rl, + lc_bootstrap_rl, + lc_optimistic_update_rl, + lc_finality_update_rl, init_time: Instant::now(), }) } @@ -243,6 +263,8 @@ impl RPCRateLimiter { blobs_by_range_quota, blobs_by_root_quota, light_client_bootstrap_quota, + light_client_optimistic_update_quota, + light_client_finality_update_quota, } = config; Self::builder() @@ -255,6 +277,14 @@ impl RPCRateLimiter { .set_quota(Protocol::BlobsByRange, blobs_by_range_quota) .set_quota(Protocol::BlobsByRoot, blobs_by_root_quota) .set_quota(Protocol::LightClientBootstrap, light_client_bootstrap_quota) + .set_quota( + Protocol::LightClientOptimisticUpdate, + light_client_optimistic_update_quota, + ) + .set_quota( + Protocol::LightClientFinalityUpdate, + light_client_finality_update_quota, + ) .build() } @@ -282,7 +312,9 @@ impl RPCRateLimiter { Protocol::BlocksByRoot => &mut self.bbroots_rl, Protocol::BlobsByRange => &mut self.blbrange_rl, Protocol::BlobsByRoot => &mut self.blbroot_rl, - Protocol::LightClientBootstrap => &mut self.lcbootstrap_rl, + Protocol::LightClientBootstrap => &mut self.lc_bootstrap_rl, + Protocol::LightClientOptimisticUpdate => &mut self.lc_optimistic_update_rl, + Protocol::LightClientFinalityUpdate => &mut self.lc_finality_update_rl, }; check(limiter) } diff --git a/beacon_node/lighthouse_network/src/service/api_types.rs b/beacon_node/lighthouse_network/src/service/api_types.rs index 008451c504c..2ea41502489 100644 --- a/beacon_node/lighthouse_network/src/service/api_types.rs +++ b/beacon_node/lighthouse_network/src/service/api_types.rs @@ -1,7 +1,10 @@ use std::sync::Arc; use libp2p::swarm::ConnectionId; -use types::{BlobSidecar, EthSpec, LightClientBootstrap, SignedBeaconBlock}; +use types::{ + BlobSidecar, EthSpec, LightClientBootstrap, LightClientFinalityUpdate, + LightClientOptimisticUpdate, SignedBeaconBlock, +}; use crate::rpc::methods::{BlobsByRangeRequest, BlobsByRootRequest}; use crate::rpc::{ @@ -40,6 +43,10 @@ pub enum Request { BlocksByRoot(BlocksByRootRequest), // light client bootstrap request LightClientBootstrap(LightClientBootstrapRequest), + // light client optimistic update request + LightClientOptimisticUpdate, + // light client finality update request + LightClientFinalityUpdate, /// A request blobs root request. BlobsByRoot(BlobsByRootRequest), } @@ -64,7 +71,9 @@ impl std::convert::From for OutboundRequest { }), ), }, - Request::LightClientBootstrap(_) => { + Request::LightClientBootstrap(_) + | Request::LightClientOptimisticUpdate + | Request::LightClientFinalityUpdate => { unreachable!("Lighthouse never makes an outbound light client request") } Request::BlobsByRange(r) => OutboundRequest::BlobsByRange(r), @@ -94,6 +103,10 @@ pub enum Response { BlobsByRoot(Option>>), /// A response to a LightClientUpdate request. LightClientBootstrap(Arc>), + /// A response to a LightClientOptimisticUpdate request. + LightClientOptimisticUpdate(Arc>), + /// A response to a LightClientFinalityUpdate request. + LightClientFinalityUpdate(Arc>), } impl std::convert::From> for RPCCodedResponse { @@ -119,6 +132,12 @@ impl std::convert::From> for RPCCodedResponse { Response::LightClientBootstrap(b) => { RPCCodedResponse::Success(RPCResponse::LightClientBootstrap(b)) } + Response::LightClientOptimisticUpdate(o) => { + RPCCodedResponse::Success(RPCResponse::LightClientOptimisticUpdate(o)) + } + Response::LightClientFinalityUpdate(f) => { + RPCCodedResponse::Success(RPCResponse::LightClientFinalityUpdate(f)) + } } } } diff --git a/beacon_node/lighthouse_network/src/service/mod.rs b/beacon_node/lighthouse_network/src/service/mod.rs index daf9b7cb020..3dd1d9b50c9 100644 --- a/beacon_node/lighthouse_network/src/service/mod.rs +++ b/beacon_node/lighthouse_network/src/service/mod.rs @@ -1170,6 +1170,14 @@ impl Network { Request::LightClientBootstrap(_) => { metrics::inc_counter_vec(&metrics::TOTAL_RPC_REQUESTS, &["light_client_bootstrap"]) } + Request::LightClientOptimisticUpdate => metrics::inc_counter_vec( + &metrics::TOTAL_RPC_REQUESTS, + &["light_client_optimistic_update"], + ), + Request::LightClientFinalityUpdate => metrics::inc_counter_vec( + &metrics::TOTAL_RPC_REQUESTS, + &["light_client_finality_update"], + ), Request::BlocksByRange { .. } => { metrics::inc_counter_vec(&metrics::TOTAL_RPC_REQUESTS, &["blocks_by_range"]) } @@ -1508,6 +1516,22 @@ impl Network { ); Some(event) } + InboundRequest::LightClientOptimisticUpdate => { + let event = self.build_request( + peer_request_id, + peer_id, + Request::LightClientOptimisticUpdate, + ); + Some(event) + } + InboundRequest::LightClientFinalityUpdate => { + let event = self.build_request( + peer_request_id, + peer_id, + Request::LightClientFinalityUpdate, + ); + Some(event) + } } } HandlerEvent::Ok(RPCReceived::Response(id, resp)) => { @@ -1545,6 +1569,16 @@ impl Network { RPCResponse::LightClientBootstrap(bootstrap) => { self.build_response(id, peer_id, Response::LightClientBootstrap(bootstrap)) } + RPCResponse::LightClientOptimisticUpdate(update) => self.build_response( + id, + peer_id, + Response::LightClientOptimisticUpdate(update), + ), + RPCResponse::LightClientFinalityUpdate(update) => self.build_response( + id, + peer_id, + Response::LightClientFinalityUpdate(update), + ), } } HandlerEvent::Ok(RPCReceived::EndOfStream(id, termination)) => { diff --git a/beacon_node/network/src/network_beacon_processor/mod.rs b/beacon_node/network/src/network_beacon_processor/mod.rs index e7d3a7ce213..ce368d5d6d5 100644 --- a/beacon_node/network/src/network_beacon_processor/mod.rs +++ b/beacon_node/network/src/network_beacon_processor/mod.rs @@ -605,6 +605,37 @@ impl NetworkBeaconProcessor { }) } + /// Create a new work event to process a `LightClientOptimisticUpdate` request from the RPC network. + pub fn send_light_client_optimistic_update_request( + self: &Arc, + peer_id: PeerId, + request_id: PeerRequestId, + ) -> Result<(), Error> { + let processor = self.clone(); + let process_fn = + move || processor.handle_light_client_optimistic_update(peer_id, request_id); + + self.try_send(BeaconWorkEvent { + drop_during_sync: true, + work: Work::LightClientOptimisticUpdateRequest(Box::new(process_fn)), + }) + } + + /// Create a new work event to process a `LightClientFinalityUpdate` request from the RPC network. + pub fn send_light_client_finality_update_request( + self: &Arc, + peer_id: PeerId, + request_id: PeerRequestId, + ) -> Result<(), Error> { + let processor = self.clone(); + let process_fn = move || processor.handle_light_client_finality_update(peer_id, request_id); + + self.try_send(BeaconWorkEvent { + drop_during_sync: true, + work: Work::LightClientFinalityUpdateRequest(Box::new(process_fn)), + }) + } + /// Send a message to `sync_tx`. /// /// Creates a log if there is an internal error. diff --git a/beacon_node/network/src/network_beacon_processor/rpc_methods.rs b/beacon_node/network/src/network_beacon_processor/rpc_methods.rs index af5593de23d..1b4b93adb0c 100644 --- a/beacon_node/network/src/network_beacon_processor/rpc_methods.rs +++ b/beacon_node/network/src/network_beacon_processor/rpc_methods.rs @@ -293,7 +293,7 @@ impl NetworkBeaconProcessor { } } - /// Handle a `BlocksByRoot` request from the peer. + /// Handle a `LightClientBootstrap` request from the peer. pub fn handle_light_client_bootstrap( self: &Arc, peer_id: PeerId, @@ -329,6 +329,60 @@ impl NetworkBeaconProcessor { }; } + /// Handle a `LightClientOptimisticUpdate` request from the peer. + pub fn handle_light_client_optimistic_update( + self: &Arc, + peer_id: PeerId, + request_id: PeerRequestId, + ) { + let Some(light_client_optimistic_update) = self + .chain + .light_client_server_cache + .get_latest_optimistic_update() + else { + self.send_error_response( + peer_id, + RPCResponseErrorCode::ResourceUnavailable, + "Latest optimistic update not available".into(), + request_id, + ); + return; + }; + + self.send_response( + peer_id, + Response::LightClientOptimisticUpdate(Arc::new(light_client_optimistic_update)), + request_id, + ) + } + + /// Handle a `LightClientFinalityUpdate` request from the peer. + pub fn handle_light_client_finality_update( + self: &Arc, + peer_id: PeerId, + request_id: PeerRequestId, + ) { + let Some(light_client_finality_update) = self + .chain + .light_client_server_cache + .get_latest_finality_update() + else { + self.send_error_response( + peer_id, + RPCResponseErrorCode::ResourceUnavailable, + "Latest finality update not available".into(), + request_id, + ); + return; + }; + + self.send_response( + peer_id, + Response::LightClientFinalityUpdate(Arc::new(light_client_finality_update)), + request_id, + ) + } + /// Handle a `BlocksByRange` request from the peer. pub fn handle_blocks_by_range_request( self: Arc, diff --git a/beacon_node/network/src/router.rs b/beacon_node/network/src/router.rs index 0363695a371..a0dd0665acf 100644 --- a/beacon_node/network/src/router.rs +++ b/beacon_node/network/src/router.rs @@ -220,6 +220,14 @@ impl Router { self.network_beacon_processor .send_light_client_bootstrap_request(peer_id, request_id, request), ), + Request::LightClientOptimisticUpdate => self.handle_beacon_processor_send_result( + self.network_beacon_processor + .send_light_client_optimistic_update_request(peer_id, request_id), + ), + Request::LightClientFinalityUpdate => self.handle_beacon_processor_send_result( + self.network_beacon_processor + .send_light_client_finality_update_request(peer_id, request_id), + ), } } @@ -250,7 +258,10 @@ impl Router { Response::BlobsByRoot(blob) => { self.on_blobs_by_root_response(peer_id, request_id, blob); } - Response::LightClientBootstrap(_) => unreachable!(), + // Light client responses should not be received + Response::LightClientBootstrap(_) + | Response::LightClientOptimisticUpdate(_) + | Response::LightClientFinalityUpdate(_) => unreachable!(), } } diff --git a/consensus/state_processing/src/per_epoch_processing/registry_updates.rs b/consensus/state_processing/src/per_epoch_processing/registry_updates.rs index 6b86f9c1e76..4b2f940e5f8 100644 --- a/consensus/state_processing/src/per_epoch_processing/registry_updates.rs +++ b/consensus/state_processing/src/per_epoch_processing/registry_updates.rs @@ -56,8 +56,8 @@ pub fn process_registry_updates( Ok(()) } -pub fn process_registry_updates_slow( - state: &mut BeaconState, +pub fn process_registry_updates_slow( + state: &mut BeaconState, spec: &ChainSpec, ) -> Result<(), Error> { process_epoch_single_pass( diff --git a/consensus/state_processing/src/per_epoch_processing/slashings.rs b/consensus/state_processing/src/per_epoch_processing/slashings.rs index a1770478008..6104208ee65 100644 --- a/consensus/state_processing/src/per_epoch_processing/slashings.rs +++ b/consensus/state_processing/src/per_epoch_processing/slashings.rs @@ -47,8 +47,8 @@ pub fn process_slashings( Ok(()) } -pub fn process_slashings_slow( - state: &mut BeaconState, +pub fn process_slashings_slow( + state: &mut BeaconState, spec: &ChainSpec, ) -> Result<(), Error> { process_epoch_single_pass( diff --git a/consensus/types/src/execution_payload_header.rs b/consensus/types/src/execution_payload_header.rs index f10f449d6de..3d0b0aca41d 100644 --- a/consensus/types/src/execution_payload_header.rs +++ b/consensus/types/src/execution_payload_header.rs @@ -1,7 +1,7 @@ use crate::{test_utils::TestRandom, *}; use derivative::Derivative; use serde::{Deserialize, Serialize}; -use ssz::Decode; +use ssz::{Decode, Encode}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash::TreeHash; @@ -109,6 +109,23 @@ impl ExecutionPayloadHeader { } } } + + #[allow(clippy::arithmetic_side_effects)] + pub fn ssz_max_var_len_for_fork(fork_name: ForkName) -> usize { + // Matching here in case variable fields are added in future forks. + // TODO(electra): review electra changes + match fork_name { + ForkName::Base + | ForkName::Altair + | ForkName::Merge + | ForkName::Capella + | ForkName::Deneb + | ForkName::Electra => { + // Max size of variable length `extra_data` field + E::max_extra_data_bytes() * ::ssz_fixed_len() + } + } + } } impl<'a, E: EthSpec> ExecutionPayloadHeaderRef<'a, E> { diff --git a/consensus/types/src/light_client_bootstrap.rs b/consensus/types/src/light_client_bootstrap.rs index 43bab325f3c..33fbf214c80 100644 --- a/consensus/types/src/light_client_bootstrap.rs +++ b/consensus/types/src/light_client_bootstrap.rs @@ -1,4 +1,4 @@ -use super::{BeaconState, EthSpec, FixedVector, Hash256, SyncCommittee}; +use super::{BeaconState, EthSpec, FixedVector, Hash256, LightClientHeader, SyncCommittee}; use crate::{ light_client_update::*, test_utils::TestRandom, ChainSpec, ForkName, ForkVersionDeserialize, LightClientHeaderAltair, LightClientHeaderCapella, LightClientHeaderDeneb, SignedBeaconBlock, @@ -7,7 +7,7 @@ use crate::{ use derivative::Derivative; use serde::{Deserialize, Deserializer, Serialize}; use serde_json::Value; -use ssz::Decode; +use ssz::{Decode, Encode}; use ssz_derive::{Decode, Encode}; use std::sync::Arc; use superstruct::superstruct; @@ -59,6 +59,17 @@ pub struct LightClientBootstrap { } impl LightClientBootstrap { + pub fn map_with_fork_name(&self, func: F) -> R + where + F: Fn(ForkName) -> R, + { + match self { + Self::Altair(_) => func(ForkName::Altair), + Self::Capella(_) => func(ForkName::Capella), + Self::Deneb(_) => func(ForkName::Deneb), + } + } + pub fn get_slot<'a>(&'a self) -> Slot { map_light_client_bootstrap_ref!(&'a _, self.to_ref(), |inner, cons| { cons(inner); @@ -85,6 +96,22 @@ impl LightClientBootstrap { Ok(bootstrap) } + #[allow(clippy::arithmetic_side_effects)] + pub fn ssz_max_len_for_fork(fork_name: ForkName) -> usize { + // TODO(electra): review electra changes + match fork_name { + ForkName::Base => 0, + ForkName::Altair + | ForkName::Merge + | ForkName::Capella + | ForkName::Deneb + | ForkName::Electra => { + as Encode>::ssz_fixed_len() + + LightClientHeader::::ssz_max_var_len_for_fork(fork_name) + } + } + } + pub fn from_beacon_state( beacon_state: &mut BeaconState, block: &SignedBeaconBlock, diff --git a/consensus/types/src/light_client_finality_update.rs b/consensus/types/src/light_client_finality_update.rs index 288527e91cb..c5c730e8b83 100644 --- a/consensus/types/src/light_client_finality_update.rs +++ b/consensus/types/src/light_client_finality_update.rs @@ -1,4 +1,4 @@ -use super::{EthSpec, FixedVector, Hash256, Slot, SyncAggregate}; +use super::{EthSpec, FixedVector, Hash256, LightClientHeader, Slot, SyncAggregate}; use crate::ChainSpec; use crate::{ light_client_update::*, test_utils::TestRandom, ForkName, ForkVersionDeserialize, @@ -7,7 +7,7 @@ use crate::{ use derivative::Derivative; use serde::{Deserialize, Deserializer, Serialize}; use serde_json::Value; -use ssz::Decode; +use ssz::{Decode, Encode}; use ssz_derive::Decode; use ssz_derive::Encode; use superstruct::superstruct; @@ -59,9 +59,9 @@ pub struct LightClientFinalityUpdate { pub finalized_header: LightClientHeaderDeneb, /// Merkle proof attesting finalized header. pub finality_branch: FixedVector, - /// current sync aggreggate + /// current sync aggregate pub sync_aggregate: SyncAggregate, - /// Slot of the sync aggregated singature + /// Slot of the sync aggregated signature pub signature_slot: Slot, } @@ -126,6 +126,17 @@ impl LightClientFinalityUpdate { Ok(finality_update) } + pub fn map_with_fork_name(&self, func: F) -> R + where + F: Fn(ForkName) -> R, + { + match self { + Self::Altair(_) => func(ForkName::Altair), + Self::Capella(_) => func(ForkName::Capella), + Self::Deneb(_) => func(ForkName::Deneb), + } + } + pub fn get_attested_header_slot<'a>(&'a self) -> Slot { map_light_client_finality_update_ref!(&'a _, self.to_ref(), |inner, cons| { cons(inner); @@ -153,6 +164,22 @@ impl LightClientFinalityUpdate { Ok(finality_update) } + + #[allow(clippy::arithmetic_side_effects)] + pub fn ssz_max_len_for_fork(fork_name: ForkName) -> usize { + // TODO(electra): review electra changes + match fork_name { + ForkName::Base => 0, + ForkName::Altair + | ForkName::Merge + | ForkName::Capella + | ForkName::Deneb + | ForkName::Electra => { + as Encode>::ssz_fixed_len() + + 2 * LightClientHeader::::ssz_max_var_len_for_fork(fork_name) + } + } + } } impl ForkVersionDeserialize for LightClientFinalityUpdate { diff --git a/consensus/types/src/light_client_header.rs b/consensus/types/src/light_client_header.rs index 5dfa42e7e89..647ece99499 100644 --- a/consensus/types/src/light_client_header.rs +++ b/consensus/types/src/light_client_header.rs @@ -1,4 +1,3 @@ -use crate::BeaconBlockHeader; use crate::ChainSpec; use crate::ForkName; use crate::ForkVersionDeserialize; @@ -7,6 +6,7 @@ use crate::{ test_utils::TestRandom, EthSpec, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, FixedVector, Hash256, SignedBeaconBlock, }; +use crate::{BeaconBlockHeader, ExecutionPayloadHeader}; use derivative::Derivative; use serde::{Deserialize, Serialize}; use ssz::Decode; @@ -116,6 +116,15 @@ impl LightClientHeader { ) -> Result { Self::from_ssz_bytes(bytes, fork_name) } + + pub fn ssz_max_var_len_for_fork(fork_name: ForkName) -> usize { + match fork_name { + ForkName::Base | ForkName::Altair | ForkName::Merge => 0, + ForkName::Capella | ForkName::Deneb | ForkName::Electra => { + ExecutionPayloadHeader::::ssz_max_var_len_for_fork(fork_name) + } + } + } } impl LightClientHeaderAltair { diff --git a/consensus/types/src/light_client_optimistic_update.rs b/consensus/types/src/light_client_optimistic_update.rs index 96e04b1c9e1..d22c4535f1e 100644 --- a/consensus/types/src/light_client_optimistic_update.rs +++ b/consensus/types/src/light_client_optimistic_update.rs @@ -1,4 +1,4 @@ -use super::{EthSpec, ForkName, ForkVersionDeserialize, Slot, SyncAggregate}; +use super::{EthSpec, ForkName, ForkVersionDeserialize, LightClientHeader, Slot, SyncAggregate}; use crate::test_utils::TestRandom; use crate::{ light_client_update::*, ChainSpec, LightClientHeaderAltair, LightClientHeaderCapella, @@ -7,7 +7,7 @@ use crate::{ use derivative::Derivative; use serde::{Deserialize, Deserializer, Serialize}; use serde_json::Value; -use ssz::Decode; +use ssz::{Decode, Encode}; use ssz_derive::Decode; use ssz_derive::Encode; use superstruct::superstruct; @@ -53,9 +53,9 @@ pub struct LightClientOptimisticUpdate { pub attested_header: LightClientHeaderCapella, #[superstruct(only(Deneb), partial_getter(rename = "attested_header_deneb"))] pub attested_header: LightClientHeaderDeneb, - /// current sync aggreggate + /// current sync aggregate pub sync_aggregate: SyncAggregate, - /// Slot of the sync aggregated singature + /// Slot of the sync aggregated signature pub signature_slot: Slot, } @@ -97,6 +97,17 @@ impl LightClientOptimisticUpdate { Ok(optimistic_update) } + pub fn map_with_fork_name(&self, func: F) -> R + where + F: Fn(ForkName) -> R, + { + match self { + Self::Altair(_) => func(ForkName::Altair), + Self::Capella(_) => func(ForkName::Capella), + Self::Deneb(_) => func(ForkName::Deneb), + } + } + pub fn get_slot<'a>(&'a self) -> Slot { map_light_client_optimistic_update_ref!(&'a _, self.to_ref(), |inner, cons| { cons(inner); @@ -138,6 +149,22 @@ impl LightClientOptimisticUpdate { Ok(optimistic_update) } + + #[allow(clippy::arithmetic_side_effects)] + pub fn ssz_max_len_for_fork(fork_name: ForkName) -> usize { + // TODO(electra): review electra changes + match fork_name { + ForkName::Base => 0, + ForkName::Altair + | ForkName::Merge + | ForkName::Capella + | ForkName::Deneb + | ForkName::Electra => { + as Encode>::ssz_fixed_len() + + LightClientHeader::::ssz_max_var_len_for_fork(fork_name) + } + } + } } impl ForkVersionDeserialize for LightClientOptimisticUpdate {