Skip to content

Commit 2c76ee5

Browse files
authored
Gloas lookup sync boilerplate (#9322)
Implements the boring boilerplate to send envelopes by root requests and process them. Pre-step to - #9155 Co-Authored-By: dapplion <35266934+dapplion@users.noreply.github.com>
1 parent 398efc3 commit 2c76ee5

12 files changed

Lines changed: 398 additions & 8 deletions

File tree

beacon_node/beacon_chain/src/beacon_chain.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6339,6 +6339,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
63396339
.contains_block(root)
63406340
}
63416341

6342+
pub fn envelope_is_known_to_fork_choice(&self, root: &Hash256) -> bool {
6343+
self.canonical_head
6344+
.fork_choice_read_lock()
6345+
.is_payload_received(root)
6346+
}
6347+
63426348
/// Determines the beacon proposer for the next slot. If that proposer is registered in the
63436349
/// `execution_layer`, provide the `execution_layer` with the necessary information to produce
63446350
/// `PayloadAttributes` for future calls to fork choice.

beacon_node/beacon_processor/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,7 @@ pub enum Work<E: EthSpec> {
418418
process_fn: AsyncFn,
419419
},
420420
RpcCustodyColumn(AsyncFn),
421+
RpcEnvelope(AsyncFn),
421422
ColumnReconstruction(AsyncFn),
422423
IgnoredRpcBlock {
423424
process_fn: BlockingFn,
@@ -485,6 +486,7 @@ pub enum WorkType {
485486
RpcBlock,
486487
RpcBlobs,
487488
RpcCustodyColumn,
489+
RpcEnvelope,
488490
ColumnReconstruction,
489491
IgnoredRpcBlock,
490492
ChainSegment,
@@ -548,6 +550,7 @@ impl<E: EthSpec> Work<E> {
548550
Work::RpcBlock { .. } => WorkType::RpcBlock,
549551
Work::RpcBlobs { .. } => WorkType::RpcBlobs,
550552
Work::RpcCustodyColumn { .. } => WorkType::RpcCustodyColumn,
553+
Work::RpcEnvelope(_) => WorkType::RpcEnvelope,
551554
Work::ColumnReconstruction(_) => WorkType::ColumnReconstruction,
552555
Work::IgnoredRpcBlock { .. } => WorkType::IgnoredRpcBlock,
553556
Work::ChainSegment { .. } => WorkType::ChainSegment,
@@ -825,6 +828,8 @@ impl<E: EthSpec> BeaconProcessor<E> {
825828
Some(item)
826829
} else if let Some(item) = work_queues.rpc_custody_column_queue.pop() {
827830
Some(item)
831+
} else if let Some(item) = work_queues.rpc_envelope_queue.pop() {
832+
Some(item)
828833
// Check delayed blocks before gossip blocks, the gossip blocks might rely
829834
// on the delayed ones.
830835
} else if let Some(item) = work_queues.delayed_block_queue.pop() {
@@ -1192,6 +1197,9 @@ impl<E: EthSpec> BeaconProcessor<E> {
11921197
work_queues.rpc_block_queue.push(work, work_id)
11931198
}
11941199
Work::RpcBlobs { .. } => work_queues.rpc_blob_queue.push(work, work_id),
1200+
Work::RpcEnvelope(_) => {
1201+
work_queues.rpc_envelope_queue.push(work, work_id)
1202+
}
11951203
Work::RpcCustodyColumn { .. } => {
11961204
work_queues.rpc_custody_column_queue.push(work, work_id)
11971205
}
@@ -1330,6 +1338,7 @@ impl<E: EthSpec> BeaconProcessor<E> {
13301338
WorkType::RpcBlobs | WorkType::IgnoredRpcBlock => {
13311339
work_queues.rpc_blob_queue.len()
13321340
}
1341+
WorkType::RpcEnvelope => work_queues.rpc_envelope_queue.len(),
13331342
WorkType::RpcCustodyColumn => work_queues.rpc_custody_column_queue.len(),
13341343
WorkType::ColumnReconstruction => {
13351344
work_queues.column_reconstruction_queue.len()
@@ -1523,6 +1532,7 @@ impl<E: EthSpec> BeaconProcessor<E> {
15231532
}
15241533
| Work::RpcBlobs { process_fn }
15251534
| Work::RpcCustodyColumn(process_fn)
1535+
| Work::RpcEnvelope(process_fn)
15261536
| Work::ColumnReconstruction(process_fn) => task_spawner.spawn_async(process_fn),
15271537
Work::IgnoredRpcBlock { process_fn } => task_spawner.spawn_blocking(process_fn),
15281538
Work::GossipBlock(work)

beacon_node/beacon_processor/src/scheduler/work_queue.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ pub struct BeaconProcessorQueueLengths {
120120
rpc_block_queue: usize,
121121
rpc_blob_queue: usize,
122122
rpc_custody_column_queue: usize,
123+
rpc_envelope_queue: usize,
123124
column_reconstruction_queue: usize,
124125
chain_segment_queue: usize,
125126
backfill_chain_segment: usize,
@@ -195,6 +196,8 @@ impl BeaconProcessorQueueLengths {
195196
// We don't request more than `PARENT_DEPTH_TOLERANCE` (32) lookups, so we can limit
196197
// this queue size. With 48 max blobs per block, each column sidecar list could be up to 12MB.
197198
rpc_custody_column_queue: 64,
199+
// Bounded by `PARENT_DEPTH_TOLERANCE`; one envelope per Gloas block.
200+
rpc_envelope_queue: 1024,
198201
column_reconstruction_queue: 1,
199202
chain_segment_queue: 64,
200203
backfill_chain_segment: 64,
@@ -253,6 +256,7 @@ pub struct WorkQueues<E: EthSpec> {
253256
pub rpc_block_queue: FifoQueue<Work<E>>,
254257
pub rpc_blob_queue: FifoQueue<Work<E>>,
255258
pub rpc_custody_column_queue: FifoQueue<Work<E>>,
259+
pub rpc_envelope_queue: FifoQueue<Work<E>>,
256260
pub column_reconstruction_queue: LifoQueue<Work<E>>,
257261
pub chain_segment_queue: FifoQueue<Work<E>>,
258262
pub backfill_chain_segment: FifoQueue<Work<E>>,
@@ -323,6 +327,7 @@ impl<E: EthSpec> WorkQueues<E> {
323327
let rpc_block_queue = FifoQueue::new(queue_lengths.rpc_block_queue);
324328
let rpc_blob_queue = FifoQueue::new(queue_lengths.rpc_blob_queue);
325329
let rpc_custody_column_queue = FifoQueue::new(queue_lengths.rpc_custody_column_queue);
330+
let rpc_envelope_queue = FifoQueue::new(queue_lengths.rpc_envelope_queue);
326331
let column_reconstruction_queue = LifoQueue::new(queue_lengths.column_reconstruction_queue);
327332
let chain_segment_queue = FifoQueue::new(queue_lengths.chain_segment_queue);
328333
let backfill_chain_segment = FifoQueue::new(queue_lengths.backfill_chain_segment);
@@ -391,6 +396,7 @@ impl<E: EthSpec> WorkQueues<E> {
391396
rpc_block_queue,
392397
rpc_blob_queue,
393398
rpc_custody_column_queue,
399+
rpc_envelope_queue,
394400
chain_segment_queue,
395401
column_reconstruction_queue,
396402
backfill_chain_segment,

beacon_node/lighthouse_network/src/service/api_types.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ pub enum SyncRequestId {
2323
SingleBlock { id: SingleLookupReqId },
2424
/// Request searching for a set of blobs given a hash.
2525
SingleBlob { id: SingleLookupReqId },
26+
/// Request searching for a payload envelope given a hash.
27+
SinglePayloadEnvelope { id: SingleLookupReqId },
2628
/// Request searching for a set of data columns given a hash and list of column indices.
2729
DataColumnsByRoot(DataColumnsByRootRequestId),
2830
/// Blocks by range request

beacon_node/network/src/network_beacon_processor/mod.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,25 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
588588
})
589589
}
590590

591+
/// Create a new `Work` event for an RPC-fetched payload envelope. `process_lookup_envelope`
592+
/// reports the result back to sync.
593+
pub fn send_lookup_envelope(
594+
self: &Arc<Self>,
595+
block_root: Hash256,
596+
envelope: Arc<SignedExecutionPayloadEnvelope<T::EthSpec>>,
597+
seen_timestamp: Duration,
598+
process_type: BlockProcessType,
599+
) -> Result<(), Error<T::EthSpec>> {
600+
let s = self.clone();
601+
self.try_send(BeaconWorkEvent {
602+
drop_during_sync: false,
603+
work: Work::RpcEnvelope(Box::pin(async move {
604+
s.process_lookup_envelope(block_root, envelope, seen_timestamp, process_type)
605+
.await;
606+
})),
607+
})
608+
}
609+
591610
/// Create a new `Work` event for some custody columns. `process_rpc_custody_columns` reports
592611
/// the result back to sync.
593612
pub fn send_rpc_custody_columns(

beacon_node/network/src/network_beacon_processor/sync_methods.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,63 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
426426
});
427427
}
428428

429+
/// Attempt to verify and import an execution payload envelope received via RPC.
430+
#[instrument(
431+
name = "lh_process_lookup_envelope",
432+
parent = None,
433+
level = "debug",
434+
skip_all,
435+
fields(?block_root),
436+
)]
437+
pub async fn process_lookup_envelope(
438+
self: Arc<NetworkBeaconProcessor<T>>,
439+
block_root: Hash256,
440+
envelope: Arc<types::SignedExecutionPayloadEnvelope<T::EthSpec>>,
441+
_seen_timestamp: Duration,
442+
process_type: BlockProcessType,
443+
) {
444+
debug!(
445+
?block_root,
446+
slot = %envelope.slot(),
447+
?process_type,
448+
"Processing RPC payload envelope"
449+
);
450+
451+
// Gossip verification runs the same signature / slot / builder-index / block-hash checks
452+
// independently of gossip propagation, so we can reuse it for RPC-fetched envelopes.
453+
#[allow(clippy::result_large_err)]
454+
let result = match self
455+
.chain
456+
.clone()
457+
.verify_envelope_for_gossip(envelope.clone())
458+
.await
459+
{
460+
Ok(verified) => {
461+
self.chain
462+
.process_execution_payload_envelope(
463+
block_root,
464+
verified,
465+
NotifyExecutionLayer::Yes,
466+
BlockImportSource::Lookup,
467+
|| Ok(()),
468+
)
469+
.await
470+
}
471+
Err(e) => Err(e),
472+
};
473+
474+
// TODO(gloas): structured penalty classification arrives with the envelope lookup state
475+
// machine; for now, fold the EnvelopeError into BlockError::InternalError so it flows
476+
// through the existing `BlockProcessingResult::Err` path.
477+
let result: Result<AvailabilityProcessingStatus, BlockError> =
478+
result.map_err(|e| BlockError::InternalError(format!("envelope: {e}")));
479+
480+
self.send_sync_message(SyncMessage::BlockComponentProcessed {
481+
process_type,
482+
result: result.into(),
483+
});
484+
}
485+
429486
pub fn process_historic_data_columns(
430487
&self,
431488
batch_id: CustodyBackfillBatchId,

beacon_node/network/src/router.rs

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use tokio_stream::wrappers::UnboundedReceiverStream;
2626
use tracing::{debug, error, trace, warn};
2727
use types::{
2828
BlobSidecar, DataColumnSidecar, EthSpec, ForkContext, PartialDataColumn, SignedBeaconBlock,
29+
SignedExecutionPayloadEnvelope,
2930
};
3031

3132
/// Handles messages from the network and routes them to the appropriate service to be handled.
@@ -348,10 +349,13 @@ impl<T: BeaconChainTypes> Router<T> {
348349
Response::DataColumnsByRange(data_column) => {
349350
self.on_data_columns_by_range_response(peer_id, app_request_id, data_column);
350351
}
351-
// TODO(EIP-7732): implement outgoing payload envelopes by range and root
352-
// responses once sync manager requests them.
353-
Response::PayloadEnvelopesByRoot(_) | Response::PayloadEnvelopesByRange(_) => {
354-
debug!("Requesting envelopes by root and by range not supported yet");
352+
Response::PayloadEnvelopesByRoot(envelope) => {
353+
self.on_payload_envelopes_by_root_response(peer_id, app_request_id, envelope);
354+
}
355+
// TODO(EIP-7732): implement outgoing payload envelopes by range responses
356+
// once sync manager requests them.
357+
Response::PayloadEnvelopesByRange(_) => {
358+
debug!("Requesting envelopes by range not supported yet");
355359
}
356360
// Lighthouse currently only serves BlocksByHead and does not issue it as a client,
357361
// so receiving a response is unexpected. Drop it without crashing.
@@ -821,6 +825,29 @@ impl<T: BeaconChainTypes> Router<T> {
821825
}
822826
}
823827

828+
/// Handle a `PayloadEnvelopesByRoot` response from the peer.
829+
pub fn on_payload_envelopes_by_root_response(
830+
&mut self,
831+
peer_id: PeerId,
832+
app_request_id: AppRequestId,
833+
envelope: Option<Arc<SignedExecutionPayloadEnvelope<T::EthSpec>>>,
834+
) {
835+
let sync_request_id = match app_request_id {
836+
AppRequestId::Sync(id @ SyncRequestId::SinglePayloadEnvelope { .. }) => id,
837+
other => {
838+
crit!(request = ?other, %peer_id, "PayloadEnvelopesByRoot response on incorrect request");
839+
return;
840+
}
841+
};
842+
843+
self.send_to_sync(SyncMessage::RpcPayloadEnvelope {
844+
sync_request_id,
845+
peer_id,
846+
envelope,
847+
seen_timestamp: self.chain.slot_clock.now_duration().unwrap_or_default(),
848+
});
849+
}
850+
824851
fn handle_beacon_processor_send_result(
825852
&mut self,
826853
result: Result<(), crate::network_beacon_processor::Error<T::EthSpec>>,

beacon_node/network/src/sync/block_lookups/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,8 @@ impl<T: BeaconChainTypes> BlockLookups<T> {
559559
BlockProcessType::SingleCustodyColumn(id) => {
560560
self.on_processing_result_inner::<CustodyRequestState<T::EthSpec>>(id, result, cx)
561561
}
562+
// TODO(gloas): route into the payload envelope lookup state machine.
563+
BlockProcessType::SinglePayloadEnvelope(_) => Ok(LookupResult::Pending),
562564
};
563565
self.on_lookup_result(process_type.id(), lookup_result, "processing_result", cx);
564566
}

beacon_node/network/src/sync/manager.rs

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ use strum::IntoStaticStr;
7373
use tokio::sync::mpsc;
7474
use tracing::{debug, error, info, trace};
7575
use types::{
76-
BlobSidecar, DataColumnSidecar, EthSpec, ForkContext, Hash256, SignedBeaconBlock, Slot,
76+
BlobSidecar, DataColumnSidecar, EthSpec, ForkContext, Hash256, SignedBeaconBlock,
77+
SignedExecutionPayloadEnvelope, Slot,
7778
};
7879

7980
/// The number of slots ahead of us that is allowed before requesting a long-range (batch) Sync
@@ -132,6 +133,14 @@ pub enum SyncMessage<E: EthSpec> {
132133
seen_timestamp: Duration,
133134
},
134135

136+
/// A payload envelope has been received from the RPC.
137+
RpcPayloadEnvelope {
138+
sync_request_id: SyncRequestId,
139+
peer_id: PeerId,
140+
envelope: Option<Arc<SignedExecutionPayloadEnvelope<E>>>,
141+
seen_timestamp: Duration,
142+
},
143+
135144
/// A block with an unknown parent has been received.
136145
UnknownParentBlock(PeerId, Arc<SignedBeaconBlock<E>>, Hash256),
137146

@@ -193,14 +202,16 @@ pub enum BlockProcessType {
193202
SingleBlock { id: Id },
194203
SingleBlob { id: Id },
195204
SingleCustodyColumn(Id),
205+
SinglePayloadEnvelope(Id),
196206
}
197207

198208
impl BlockProcessType {
199209
pub fn id(&self) -> Id {
200210
match self {
201211
BlockProcessType::SingleBlock { id }
202212
| BlockProcessType::SingleBlob { id }
203-
| BlockProcessType::SingleCustodyColumn(id) => *id,
213+
| BlockProcessType::SingleCustodyColumn(id)
214+
| BlockProcessType::SinglePayloadEnvelope(id) => *id,
204215
}
205216
}
206217
}
@@ -502,6 +513,9 @@ impl<T: BeaconChainTypes> SyncManager<T> {
502513
SyncRequestId::SingleBlob { id } => {
503514
self.on_single_blob_response(id, peer_id, RpcEvent::RPCError(error))
504515
}
516+
SyncRequestId::SinglePayloadEnvelope { id } => {
517+
self.on_single_payload_envelope_response(id, peer_id, RpcEvent::RPCError(error))
518+
}
505519
SyncRequestId::DataColumnsByRoot(req_id) => {
506520
self.on_data_columns_by_root_response(req_id, peer_id, RpcEvent::RPCError(error))
507521
}
@@ -848,6 +862,17 @@ impl<T: BeaconChainTypes> SyncManager<T> {
848862
} => {
849863
self.rpc_data_column_received(sync_request_id, peer_id, data_column, seen_timestamp)
850864
}
865+
SyncMessage::RpcPayloadEnvelope {
866+
sync_request_id,
867+
peer_id,
868+
envelope,
869+
seen_timestamp,
870+
} => self.rpc_payload_envelope_received(
871+
sync_request_id,
872+
peer_id,
873+
envelope,
874+
seen_timestamp,
875+
),
851876
SyncMessage::UnknownParentBlock(peer_id, block, block_root) => {
852877
let block_slot = block.slot();
853878
let parent_root = block.parent_root();
@@ -1209,6 +1234,27 @@ impl<T: BeaconChainTypes> SyncManager<T> {
12091234
}
12101235
}
12111236

1237+
// TODO(gloas): dispatch into block_lookups once the envelope lookup state machine lands.
1238+
fn rpc_payload_envelope_received(
1239+
&mut self,
1240+
sync_request_id: SyncRequestId,
1241+
peer_id: PeerId,
1242+
envelope: Option<Arc<SignedExecutionPayloadEnvelope<T::EthSpec>>>,
1243+
seen_timestamp: Duration,
1244+
) {
1245+
match sync_request_id {
1246+
SyncRequestId::SinglePayloadEnvelope { id } => self
1247+
.on_single_payload_envelope_response(
1248+
id,
1249+
peer_id,
1250+
RpcEvent::from_chunk(envelope, seen_timestamp),
1251+
),
1252+
_ => {
1253+
crit!(%peer_id, "bad request id for payload envelope");
1254+
}
1255+
}
1256+
}
1257+
12121258
fn rpc_data_column_received(
12131259
&mut self,
12141260
sync_request_id: SyncRequestId,
@@ -1237,6 +1283,22 @@ impl<T: BeaconChainTypes> SyncManager<T> {
12371283
}
12381284
}
12391285

1286+
fn on_single_payload_envelope_response(
1287+
&mut self,
1288+
id: SingleLookupReqId,
1289+
peer_id: PeerId,
1290+
envelope: RpcEvent<Arc<SignedExecutionPayloadEnvelope<T::EthSpec>>>,
1291+
) {
1292+
if let Some(_resp) = self
1293+
.network
1294+
.on_single_payload_envelope_response(id, peer_id, envelope)
1295+
{
1296+
// TODO(gloas): dispatch into
1297+
// `block_lookups.on_download_response::<PayloadEnvelopeRequestState<_>>(...)` once
1298+
// the envelope lookup state machine lands.
1299+
}
1300+
}
1301+
12401302
fn on_single_blob_response(
12411303
&mut self,
12421304
id: SingleLookupReqId,

0 commit comments

Comments
 (0)