Skip to content

Commit 88f629e

Browse files
prestwichclaude
andcommitted
feat: send ChainEvent::Reorg from node on host revert
Wire up reorg notifications in on_host_revert() so the RPC layer receives removed rollup block hashes and logs before storage is unwound. Uses the new drain_above() method from signet-storage which reads headers from hot storage and receipts from cold storage before performing the unwind. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e6c8b7e commit 88f629e

2 files changed

Lines changed: 33 additions & 10 deletions

File tree

Cargo.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,11 @@ url = "2.5.4"
113113
# Test Utils
114114
tempfile = "3.17.0"
115115

116-
# [patch.crates-io]
117-
# signet-cold = { path = "../storage/crates/cold" }
118-
# signet-hot = { path = "../storage/crates/hot" }
119-
# signet-storage = { path = "../storage/crates/storage" }
120-
# signet-storage-types = { path = "../storage/crates/types" }
116+
[patch.crates-io]
117+
signet-cold = { git = "https://github.com/init4tech/storage.git", branch = "james/eng-1978" }
118+
signet-hot = { git = "https://github.com/init4tech/storage.git", branch = "james/eng-1978" }
119+
signet-storage = { git = "https://github.com/init4tech/storage.git", branch = "james/eng-1978" }
120+
signet-storage-types = { git = "https://github.com/init4tech/storage.git", branch = "james/eng-1978" }
121121

122122
# signet-bundle = { path = "../sdk/crates/bundle"}
123123
# signet-constants = { path = "../sdk/crates/constants"}

crates/node/src/node.rs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ use signet_block_processor::{AliasOracleFactory, SignetBlockProcessorV1};
1515
use signet_evm::EthereumHardfork;
1616
use signet_extract::Extractor;
1717
use signet_node_config::SignetNodeConfig;
18-
use signet_rpc::{ChainNotifier, NewBlockNotification, RpcServerGuard};
19-
use signet_storage::{HistoryRead, HotKv, HotKvRead, UnifiedStorage};
18+
use signet_rpc::{ChainNotifier, NewBlockNotification, ReorgNotification, RpcServerGuard};
19+
use signet_storage::{DrainedBlock, HistoryRead, HotKv, HotKvRead, UnifiedStorage};
2020
use signet_types::{PairedHeights, constants::SignetSystemConstants};
2121
use std::{fmt, sync::Arc};
2222
use tokio::sync::watch;
@@ -296,7 +296,8 @@ where
296296

297297
// NB: REVERTS MUST RUN FIRST
298298
if let Some(chain) = notification.reverted_chain() {
299-
changed |= self.on_host_revert(&chain).wrap_err("error encountered during revert")?;
299+
changed |=
300+
self.on_host_revert(&chain).await.wrap_err("error encountered during revert")?;
300301
}
301302

302303
if let Some(chain) = notification.committed_chain() {
@@ -360,6 +361,20 @@ where
360361
let _ = self.chain.send_new_block(notif);
361362
}
362363

364+
/// Send a reorg notification on the broadcast channel.
365+
fn notify_reorg(&self, drained: Vec<DrainedBlock>, common_ancestor: u64) {
366+
let removed_hashes = drained.iter().map(|d| d.header.hash()).collect();
367+
let removed_logs = drained
368+
.into_iter()
369+
.flat_map(|d| d.receipts)
370+
.flat_map(|r| r.receipt.logs)
371+
.map(|l| l.inner)
372+
.collect();
373+
let notif = ReorgNotification { common_ancestor, removed_hashes, removed_logs };
374+
// Ignore send errors — no subscribers is fine.
375+
let _ = self.chain.send_reorg(notif);
376+
}
377+
363378
/// Update the status channel and block tags. This keeps the RPC node
364379
/// in sync with the latest block information.
365380
fn update_status(&self) -> eyre::Result<()> {
@@ -476,7 +491,7 @@ where
476491
///
477492
/// Returns `true` if any rollup state was unwound.
478493
#[instrument(skip_all, fields(first = chain.first().number(), tip = chain.tip().number()))]
479-
pub fn on_host_revert(&self, chain: &Arc<Chain<Host>>) -> eyre::Result<bool> {
494+
pub async fn on_host_revert(&self, chain: &Arc<Chain<Host>>) -> eyre::Result<bool> {
480495
// If the end is before the RU genesis, nothing to do.
481496
if chain.tip().number() <= self.constants.host_deploy_height() {
482497
return Ok(false);
@@ -489,7 +504,15 @@ where
489504
.unwrap_or_default()
490505
.saturating_sub(1);
491506

492-
self.storage.unwind_above(target)?;
507+
let drained = self.storage.drain_above(target).await?;
508+
509+
// The early return above guards against no-op reverts, so drained
510+
// should always contain at least one block. Guard defensively.
511+
debug_assert!(!drained.is_empty(), "drain_above returned empty after host revert");
512+
if !drained.is_empty() {
513+
self.notify_reorg(drained, target);
514+
}
515+
493516
Ok(true)
494517
}
495518
}

0 commit comments

Comments
 (0)