1use crate::{
4 backfill::BackfillAction,
5 chain::{ChainHandler, FromOrchestrator, HandlerEvent},
6 download::{BlockDownloader, DownloadAction, DownloadOutcome},
7};
8use alloy_primitives::{map::B256Set, B256};
9use crossbeam_channel::Sender;
10use futures::{Stream, StreamExt};
11use reth_engine_primitives::{BeaconEngineMessage, ConsensusEngineEvent};
12use reth_ethereum_primitives::EthPrimitives;
13use reth_network_p2p::full_block::SealedBlockWithAccessList;
14use reth_payload_primitives::{BuiltPayloadExecutedBlock, PayloadTypes};
15use reth_primitives_traits::{Block, NodePrimitives};
16use std::{
17 fmt::Display,
18 task::{ready, Context, Poll},
19};
20use tokio::sync::mpsc::UnboundedReceiver;
21
22#[derive(Debug)]
40pub struct EngineHandler<T, S, D> {
41 handler: T,
45 incoming_requests: S,
47 downloader: D,
49}
50
51impl<T, S, D> EngineHandler<T, S, D> {
52 pub const fn new(handler: T, downloader: D, incoming_requests: S) -> Self
55 where
56 T: EngineRequestHandler,
57 {
58 Self { handler, incoming_requests, downloader }
59 }
60
61 pub const fn handler_mut(&mut self) -> &mut T {
63 &mut self.handler
64 }
65}
66
67impl<T, S, D> ChainHandler for EngineHandler<T, S, D>
68where
69 T: EngineRequestHandler<Block = D::Block>,
70 S: Stream + Send + Sync + Unpin + 'static,
71 <S as Stream>::Item: Into<T::Request>,
72 D: BlockDownloader,
73{
74 type Event = T::Event;
75
76 fn on_event(&mut self, event: FromOrchestrator) {
77 self.handler.on_event(event.into());
79 }
80
81 fn poll(&mut self, cx: &mut Context<'_>) -> Poll<HandlerEvent<Self::Event>> {
82 loop {
83 while let Poll::Ready(ev) = self.handler.poll(cx) {
85 match ev {
86 RequestHandlerEvent::HandlerEvent(ev) => {
87 return match ev {
88 HandlerEvent::BackfillAction(target) => {
89 if matches!(target, BackfillAction::Start(_)) {
92 self.downloader.on_action(DownloadAction::Clear);
93 }
94 Poll::Ready(HandlerEvent::BackfillAction(target))
95 }
96 HandlerEvent::Event(ev) => {
97 Poll::Ready(HandlerEvent::Event(ev))
99 }
100 HandlerEvent::FatalError => Poll::Ready(HandlerEvent::FatalError),
101 }
102 }
103 RequestHandlerEvent::Download(req) => {
104 self.downloader.on_action(DownloadAction::Download(req));
106 }
107 }
108 }
109
110 if let Poll::Ready(Some(req)) = self.incoming_requests.poll_next_unpin(cx) {
112 self.handler.on_event(FromEngine::Request(req.into()));
114 continue
116 }
117
118 if let Poll::Ready(outcome) = self.downloader.poll(cx) {
120 if let DownloadOutcome::Blocks(blocks) = outcome {
121 self.handler.on_event(FromEngine::DownloadedBlocks(blocks));
123 }
124 continue
125 }
126
127 return Poll::Pending
128 }
129 }
130}
131
132pub trait EngineRequestHandler: Send + Sync {
141 type Event: Send;
143 type Request;
145 type Block: Block;
147
148 fn on_event(&mut self, event: FromEngine<Self::Request, Self::Block>);
150
151 fn poll(&mut self, cx: &mut Context<'_>) -> Poll<RequestHandlerEvent<Self::Event>>;
153}
154
155#[derive(Debug)]
175pub struct EngineApiRequestHandler<Request, N: NodePrimitives> {
176 to_tree: Sender<FromEngine<Request, N::Block>>,
178 from_tree: UnboundedReceiver<EngineApiEvent<N>>,
180}
181
182impl<Request, N: NodePrimitives> EngineApiRequestHandler<Request, N> {
183 pub const fn new(
185 to_tree: Sender<FromEngine<Request, N::Block>>,
186 from_tree: UnboundedReceiver<EngineApiEvent<N>>,
187 ) -> Self {
188 Self { to_tree, from_tree }
189 }
190}
191
192impl<Request, N: NodePrimitives> EngineRequestHandler for EngineApiRequestHandler<Request, N>
193where
194 Request: Send,
195{
196 type Event = ConsensusEngineEvent<N>;
197 type Request = Request;
198 type Block = N::Block;
199
200 fn on_event(&mut self, event: FromEngine<Self::Request, Self::Block>) {
201 let _ = self.to_tree.send(event);
203 }
204
205 fn poll(&mut self, cx: &mut Context<'_>) -> Poll<RequestHandlerEvent<Self::Event>> {
206 let Some(ev) = ready!(self.from_tree.poll_recv(cx)) else {
207 return Poll::Ready(RequestHandlerEvent::HandlerEvent(HandlerEvent::FatalError))
208 };
209
210 let ev = match ev {
211 EngineApiEvent::BeaconConsensus(ev) => {
212 RequestHandlerEvent::HandlerEvent(HandlerEvent::Event(ev))
213 }
214 EngineApiEvent::BackfillAction(action) => {
215 RequestHandlerEvent::HandlerEvent(HandlerEvent::BackfillAction(action))
216 }
217 EngineApiEvent::Download(action) => RequestHandlerEvent::Download(action),
218 };
219 Poll::Ready(ev)
220 }
221}
222
223#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
225pub enum EngineApiKind {
226 #[default]
228 Ethereum,
229 OpStack,
231}
232
233impl EngineApiKind {
234 pub const fn is_ethereum(&self) -> bool {
236 matches!(self, Self::Ethereum)
237 }
238
239 pub const fn is_opstack(&self) -> bool {
241 matches!(self, Self::OpStack)
242 }
243}
244
245#[derive(Debug)]
247pub enum EngineApiRequest<T: PayloadTypes, N: NodePrimitives> {
248 Beacon(BeaconEngineMessage<T>),
250 InsertExecutedBlock(BuiltPayloadExecutedBlock<N>),
252}
253
254impl<T: PayloadTypes, N: NodePrimitives> Display for EngineApiRequest<T, N> {
255 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
256 match self {
257 Self::Beacon(msg) => msg.fmt(f),
258 Self::InsertExecutedBlock(payload) => {
259 write!(f, "InsertExecutedBlock({:?})", payload.recovered_block.num_hash())
260 }
261 }
262 }
263}
264
265impl<T: PayloadTypes, N: NodePrimitives> From<BeaconEngineMessage<T>> for EngineApiRequest<T, N> {
266 fn from(msg: BeaconEngineMessage<T>) -> Self {
267 Self::Beacon(msg)
268 }
269}
270
271impl<T: PayloadTypes, N: NodePrimitives> From<EngineApiRequest<T, N>>
272 for FromEngine<EngineApiRequest<T, N>, N::Block>
273{
274 fn from(req: EngineApiRequest<T, N>) -> Self {
275 Self::Request(req)
276 }
277}
278
279#[derive(Debug)]
281pub enum EngineApiEvent<N: NodePrimitives = EthPrimitives> {
282 BeaconConsensus(ConsensusEngineEvent<N>),
285 BackfillAction(BackfillAction),
287 Download(DownloadRequest),
289}
290
291impl<N: NodePrimitives> EngineApiEvent<N> {
292 pub const fn is_backfill_action(&self) -> bool {
294 matches!(self, Self::BackfillAction(_))
295 }
296}
297
298impl<N: NodePrimitives> From<ConsensusEngineEvent<N>> for EngineApiEvent<N> {
299 fn from(event: ConsensusEngineEvent<N>) -> Self {
300 Self::BeaconConsensus(event)
301 }
302}
303
304#[derive(Debug)]
306pub enum FromEngine<Req, B: Block> {
307 Event(FromOrchestrator),
309 Request(Req),
311 DownloadedBlocks(Vec<SealedBlockWithAccessList<B>>),
313}
314
315impl<Req: Display, B: Block> Display for FromEngine<Req, B> {
316 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
317 match self {
318 Self::Event(ev) => write!(f, "Event({ev:?})"),
319 Self::Request(req) => write!(f, "Request({req})"),
320 Self::DownloadedBlocks(blocks) => {
321 write!(f, "DownloadedBlocks({} blocks)", blocks.len())
322 }
323 }
324 }
325}
326
327impl<Req, B: Block> From<FromOrchestrator> for FromEngine<Req, B> {
328 fn from(event: FromOrchestrator) -> Self {
329 Self::Event(event)
330 }
331}
332
333#[derive(Debug)]
335pub enum RequestHandlerEvent<T> {
336 HandlerEvent(HandlerEvent<T>),
338 Download(DownloadRequest),
340}
341
342#[derive(Debug)]
344pub enum DownloadRequest {
345 BlockSet {
347 hashes: B256Set,
349 access_lists: bool,
351 },
352 BlockRange {
354 hash: B256,
356 count: u64,
358 access_lists: bool,
360 },
361}
362
363impl DownloadRequest {
364 pub fn single_block(hash: B256) -> Self {
366 Self::block_set(B256Set::from_iter([hash]))
367 }
368
369 pub const fn block_set(hashes: B256Set) -> Self {
371 Self::BlockSet { hashes, access_lists: false }
372 }
373
374 pub const fn block_range(hash: B256, count: u64) -> Self {
376 Self::BlockRange { hash, count, access_lists: false }
377 }
378
379 pub const fn with_access_lists(mut self, access_lists: bool) -> Self {
384 match &mut self {
385 Self::BlockSet { access_lists: al, .. } | Self::BlockRange { access_lists: al, .. } => {
386 *al = access_lists
387 }
388 }
389 self
390 }
391}