|  | 
|  | 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