Skip to content
This repository was archived by the owner on Jan 16, 2026. It is now read-only.

Commit 31679c6

Browse files
op-willtheochap
andauthored
chore(node/actor): Adds tests for Sequencer Admin API (#3101)
Also: - Adds admin RPC method to inspect recovery mode status - Adds handling for new admin RPC method in SequencerActor - Adds test utils via automock - References existing test utils in crates/protocol/derive Ref: #3070 --------- Co-authored-by: theo <80177219+theochap@users.noreply.github.com>
1 parent 471eedb commit 31679c6

20 files changed

Lines changed: 499 additions & 27 deletions

File tree

crates/node/rpc/src/admin.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,18 @@ impl<S: SequencerAdminAPIClient + 'static> AdminApiServer for AdminRpc<S> {
140140
.map_err(|_| ErrorObject::from(ErrorCode::InternalError))
141141
}
142142

143+
async fn admin_recover_mode(&self) -> RpcResult<bool> {
144+
// If the sequencer is not enabled (mode runs in validator mode), return an error.
145+
let Some(ref sequencer_client) = self.sequencer_admin_client else {
146+
return Err(ErrorObject::from(ErrorCode::MethodNotFound));
147+
};
148+
149+
sequencer_client
150+
.is_recovery_mode()
151+
.await
152+
.map_err(|_| ErrorObject::from(ErrorCode::InternalError))
153+
}
154+
143155
async fn admin_set_recover_mode(&self, mode: bool) -> RpcResult<()> {
144156
// If the sequencer is not enabled (mode runs in validator mode), return an error.
145157
let Some(ref sequencer_client) = self.sequencer_admin_client else {
@@ -208,6 +220,9 @@ pub trait SequencerAdminAPIClient: Send + Sync + Debug {
208220
/// Check if the conductor is enabled.
209221
async fn is_conductor_enabled(&self) -> Result<bool, SequencerAdminAPIError>;
210222

223+
/// Check if in recovery mode.
224+
async fn is_recovery_mode(&self) -> Result<bool, SequencerAdminAPIError>;
225+
211226
/// Start the sequencer.
212227
async fn start_sequencer(&self) -> Result<(), SequencerAdminAPIError>;
213228

@@ -234,9 +249,17 @@ pub enum SequencerAdminAPIError {
234249

235250
/// Error stopping sequencer.
236251
#[error("Error stopping sequencer: {0}.")]
237-
StopError(String),
252+
StopError(#[from] StopSequencerError),
238253

239254
/// Error overriding leader.
240255
#[error("Error overriding leader: {0}.")]
241256
LeaderOverrideError(String),
242257
}
258+
259+
/// Errors that can occur when using the sequencer admin API.
260+
#[derive(Debug, Error)]
261+
pub enum StopSequencerError {
262+
/// Sequencer stopped successfully, followed by some error.
263+
#[error("Sequencer stopped successfully, followed by error: {0}.")]
264+
ErrorAfterSequencerWasStopped(String),
265+
}

crates/node/rpc/src/jsonrpsee.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,10 @@ pub trait AdminApi {
188188
#[method(name = "conductorEnabled")]
189189
async fn admin_conductor_enabled(&self) -> RpcResult<bool>;
190190

191+
/// Gets the recover mode.
192+
#[method(name = "adminRecoverMode")]
193+
async fn admin_recover_mode(&self) -> RpcResult<bool>;
194+
191195
/// Sets the recover mode.
192196
#[method(name = "setRecoverMode")]
193197
async fn admin_set_recover_mode(&self, mode: bool) -> RpcResult<()>;

crates/node/rpc/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ extern crate tracing;
1212
mod admin;
1313
pub use admin::{
1414
AdminRpc, NetworkAdminQuery, RollupBoostAdminQuery, SequencerAdminAPIClient,
15-
SequencerAdminAPIError,
15+
SequencerAdminAPIError, StopSequencerError,
1616
};
1717

1818
mod config;

crates/node/service/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ alloy-primitives = { workspace = true, features = ["k256"] }
8282
alloy-rpc-types-engine = { workspace = true, features = ["arbitrary"] }
8383
alloy-consensus = { workspace = true, features = ["arbitrary"] }
8484
op-alloy-consensus = { workspace = true, features = ["arbitrary", "k256"] }
85+
kona-derive = {workspace = true, features = ["test-utils"]}
8586

8687
[features]
8788
default = []

crates/node/service/src/actors/engine/api.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use tokio::sync::{mpsc, watch};
1212
/// Trait to be referenced by those interacting with EngineActor for block building
1313
/// operations. The EngineActor requires the use of channels for communication, but
1414
/// this interface allows that to be abstracted from callers and allows easy testing.
15+
#[cfg_attr(test, mockall::automock)]
1516
#[async_trait]
1617
pub trait BlockBuildingClient: Debug + Send + Sync {
1718
/// Resets the engine's forkchoice, awaiting confirmation that it succeeded or returning the

crates/node/service/src/actors/engine/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@ mod finalizer;
1919
pub use finalizer::L2Finalizer;
2020

2121
mod rollup_boost;
22+
23+
#[cfg(test)]
24+
pub use api::MockBlockBuildingClient;

crates/node/service/src/actors/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,10 @@ pub use sequencer::{
4141
SequencerActor, SequencerActorBuilder, SequencerActorError, SequencerAdminQuery,
4242
SequencerConfig,
4343
};
44+
45+
#[cfg(test)]
46+
pub use engine::MockBlockBuildingClient;
47+
#[cfg(test)]
48+
pub use network::MockUnsafePayloadGossipClient;
49+
#[cfg(test)]
50+
pub use sequencer::{MockConductor, MockOriginSelector};

crates/node/service/src/actors/network/gossip.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use thiserror::Error;
66
use tokio::sync::mpsc;
77

88
/// Client used to schedule unsafe [`OpExecutionPayloadEnvelope`] to be gossiped.
9+
#[cfg_attr(test, mockall::automock)]
910
#[async_trait]
1011
pub trait UnsafePayloadGossipClient: Send + Sync + Debug {
1112
/// This is a fire-and-forget function that schedules the provided

crates/node/service/src/actors/network/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,6 @@ pub use gossip::{
2222
};
2323

2424
pub use config::NetworkConfig;
25+
26+
#[cfg(test)]
27+
pub use gossip::MockUnsafePayloadGossipClient;

crates/node/service/src/actors/sequencer/admin_api_client.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ pub enum SequencerAdminQuery {
2626
StopSequencer(oneshot::Sender<Result<B256, SequencerAdminAPIError>>),
2727
/// A query to check if the conductor is enabled.
2828
ConductorEnabled(oneshot::Sender<Result<bool, SequencerAdminAPIError>>),
29+
/// A query to check if the sequencer is in recovery mode.
30+
RecoveryMode(oneshot::Sender<Result<bool, SequencerAdminAPIError>>),
2931
/// A query to set the recovery mode.
3032
SetRecoveryMode(bool, oneshot::Sender<Result<(), SequencerAdminAPIError>>),
3133
/// A query to override the leader.
@@ -56,6 +58,17 @@ impl SequencerAdminAPIClient for QueuedSequencerAdminAPIClient {
5658
})?
5759
}
5860

61+
async fn is_recovery_mode(&self) -> Result<bool, SequencerAdminAPIError> {
62+
let (tx, rx) = oneshot::channel();
63+
64+
self.request_tx.send(SequencerAdminQuery::RecoveryMode(tx)).await.map_err(|_| {
65+
SequencerAdminAPIError::RequestError("request channel closed".to_string())
66+
})?;
67+
rx.await.map_err(|_| {
68+
SequencerAdminAPIError::ResponseError("response channel closed".to_string())
69+
})?
70+
}
71+
5972
async fn start_sequencer(&self) -> Result<(), SequencerAdminAPIError> {
6073
let (tx, rx) = oneshot::channel();
6174

0 commit comments

Comments
 (0)