|
| 1 | +use crate::journal::JournalDb; |
| 2 | +use futures_util::StreamExt; |
| 3 | +use reth::{ |
| 4 | + primitives::SealedHeader, |
| 5 | + providers::{ |
| 6 | + CanonChainTracker, DatabaseProviderFactory, DatabaseProviderRW, ProviderResult, |
| 7 | + providers::BlockchainProvider, |
| 8 | + }, |
| 9 | + rpc::types::engine::ForkchoiceState, |
| 10 | +}; |
| 11 | +use signet_journal::{Journal, JournalStream}; |
| 12 | +use signet_node_types::{NodeTypesDbTrait, SignetNodeTypes}; |
| 13 | +use tokio::task::JoinHandle; |
| 14 | + |
| 15 | +/// A task that processes journal updates for a specific database, and calls |
| 16 | +/// the appropriate methods on a [`BlockchainProvider`] to update the in-memory |
| 17 | +/// chain view. |
| 18 | +#[derive(Debug, Clone)] |
| 19 | +pub struct JournalProviderTask<Db: NodeTypesDbTrait> { |
| 20 | + provider: BlockchainProvider<SignetNodeTypes<Db>>, |
| 21 | +} |
| 22 | + |
| 23 | +impl<Db: NodeTypesDbTrait> JournalProviderTask<Db> { |
| 24 | + /// Instantiate a new task. |
| 25 | + pub const fn new(provider: BlockchainProvider<SignetNodeTypes<Db>>) -> Self { |
| 26 | + Self { provider } |
| 27 | + } |
| 28 | + |
| 29 | + /// Get a reference to the provider. |
| 30 | + pub const fn provider(&self) -> &BlockchainProvider<SignetNodeTypes<Db>> { |
| 31 | + &self.provider |
| 32 | + } |
| 33 | + |
| 34 | + /// Deconstruct the task into its provider. |
| 35 | + pub fn into_inner(self) -> BlockchainProvider<SignetNodeTypes<Db>> { |
| 36 | + self.provider |
| 37 | + } |
| 38 | + |
| 39 | + /// Create a future for the task, suitable for [`tokio::spawn`] or another |
| 40 | + /// task-spawning system. |
| 41 | + pub async fn task_future<S>(self, mut journals: S) -> ProviderResult<()> |
| 42 | + where |
| 43 | + S: JournalStream<'static> + Send + Unpin + 'static, |
| 44 | + { |
| 45 | + loop { |
| 46 | + let Some(Journal::V1(journal)) = journals.next().await else { break }; |
| 47 | + |
| 48 | + let rw = self.provider.database_provider_rw().map(DatabaseProviderRW); |
| 49 | + |
| 50 | + let r_header = SealedHeader::new_unhashed(journal.header().clone()); |
| 51 | + let block_hash = r_header.hash(); |
| 52 | + |
| 53 | + // DB interaction is sync, so we spawn a blocking task for it. We |
| 54 | + // immediately await that task. This prevents blocking the worker |
| 55 | + // thread |
| 56 | + tokio::task::spawn_blocking(move || rw?.ingest(journal)) |
| 57 | + .await |
| 58 | + .expect("ingestion should not panic")?; |
| 59 | + |
| 60 | + self.provider.set_canonical_head(r_header.clone()); |
| 61 | + self.provider.set_safe(r_header.clone()); |
| 62 | + self.provider.set_finalized(r_header); |
| 63 | + self.provider.on_forkchoice_update_received(&ForkchoiceState { |
| 64 | + head_block_hash: block_hash, |
| 65 | + safe_block_hash: block_hash, |
| 66 | + finalized_block_hash: block_hash, |
| 67 | + }); |
| 68 | + } |
| 69 | + |
| 70 | + Ok(()) |
| 71 | + } |
| 72 | + |
| 73 | + /// Spawn the journal provider task. |
| 74 | + pub fn spawn<S>(self, journals: S) -> JoinHandle<ProviderResult<()>> |
| 75 | + where |
| 76 | + S: JournalStream<'static> + Send + Unpin + 'static, |
| 77 | + { |
| 78 | + tokio::spawn(self.task_future(journals)) |
| 79 | + } |
| 80 | +} |
0 commit comments