Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 28 additions & 12 deletions cli/src/commands/node/mod.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
use std::{fs::File, path::PathBuf, sync::Arc};

use anyhow::Context;
use ledger::proofs::provers::BlockProver;
use node::{
account::AccountSecretKey,
snark::{BlockVerifier, TransactionVerifier},
transition_frontier::genesis::GenesisConfig,
};

use mina_node_account::AccountPublicKey;
use reqwest::Url;

use mina_node_native::{archive::config::ArchiveStorageOptions, tracing, NodeBuilder};
use node::{
account::AccountSecretKey,
core::log::inner::Level,
p2p::{connection::outgoing::P2pConnectionOutgoingInitOpts, identity::SecretKey},
service::Recorder,
snark::{BlockVerifier, TransactionVerifier},
transition_frontier::genesis::GenesisConfig,
SnarkerStrategy,
};

use mina_node_native::{archive::config::ArchiveStorageOptions, tracing, NodeBuilder};
use reqwest::Url;
use std::{fs::File, path::PathBuf, sync::Arc};

/// Mina node configuration and runtime options
///
Expand Down Expand Up @@ -216,6 +210,28 @@ pub struct Node {
#[arg(long, requires = "producer")]
pub coinbase_receiver: Option<AccountPublicKey>,

/// Enable recording of node state and actions for debugging and replay
///
/// Recording captures the node's state transitions and input actions,
/// enabling deterministic replay for debugging and testing purposes.
///
/// Available modes:
/// - `none`: No recording (default)
/// - `state-with-input-actions`: Records initial state and all input
/// actions to the `recorder/` directory within the working directory
///
/// Recorded data can be replayed using the `mina replay` command to
/// reproduce the exact sequence of state transitions for debugging.
///
/// # Example
///
/// ```bash
/// # Record node execution
/// mina node --network devnet --record state-with-input-actions
///
/// # Replay recorded execution
/// mina replay state-with-input-actions ~/.mina/recorder
/// ```
#[arg(long, default_value = "none", env)]
pub record: String,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are already adding docs, it would be better if record parsing was handled by clap so that if invalid value is passed, node doesn't panic


Expand Down
4 changes: 1 addition & 3 deletions node/common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
pub mod tracing;

mod service;
pub mod tracing;
pub use service::*;

mod node;
pub use node::*;
47 changes: 32 additions & 15 deletions node/common/src/service/service.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
use std::sync::Arc;

use super::{
archive::ArchiveService,
block_producer::BlockProducerService,
p2p::webrtc_with_libp2p::P2pServiceCtx,
replay::ReplayerState,
rpc::{RpcSender, RpcService},
snark_worker::SnarkWorker,
snarks::SnarkBlockVerifyArgs,
EventReceiver, EventSender,
};
use crate::rpc::RpcReceiver;
use node::{
core::{channels::mpsc, invariants::InvariantsState},
event_source::Event,
Expand All @@ -14,43 +23,51 @@ use sha3::{
digest::{core_api::XofReaderCoreWrapper, ExtendableOutput, Update},
Shake256, Shake256ReaderCore,
};

use crate::rpc::RpcReceiver;

use super::{
archive::ArchiveService,
block_producer::BlockProducerService,
p2p::webrtc_with_libp2p::P2pServiceCtx,
replay::ReplayerState,
rpc::{RpcSender, RpcService},
snark_worker::SnarkWorker,
snarks::SnarkBlockVerifyArgs,
EventReceiver, EventSender,
};
use std::sync::Arc;

pub struct NodeService {
/// Master seed for deterministic random number generation.
pub rng_seed: [u8; 32],
/// XOF-based RNG for ephemeral keys (derived from seed + "ephemeral").
pub rng_ephemeral: XofReaderCoreWrapper<Shake256ReaderCore>,
/// XOF-based RNG for static operations (derived from seed + "static").
pub rng_static: XofReaderCoreWrapper<Shake256ReaderCore>,
/// Standard RNG for general-purpose randomness.
pub rng: StdRng,

/// Events sent on this channel are retrieved and processed in the
/// `event_source` state machine defined in the `mina-node` crate.
pub event_sender: EventSender,
/// Channel for consuming events in the event source state machine.
pub event_receiver: EventReceiver,

/// Channel for asynchronous block proof verification requests.
pub snark_block_proof_verify: mpsc::TrackedUnboundedSender<SnarkBlockVerifyArgs>,

/// Manages ledger operations, database access, and staged ledger state.
pub ledger_manager: LedgerManager,
/// SNARK proof worker for generating transaction proofs (enabled when node
/// acts as SNARK worker).
pub snark_worker: Option<SnarkWorker>,
/// Block production service including VRF evaluation and block proving
/// (enabled when node acts as block producer).
pub block_producer: Option<BlockProducerService>,
/// Archive service for storing full blockchain history (enabled when node
/// acts as archive node).
pub archive: Option<ArchiveService>,
/// P2P networking context (WebRTC and optionally libp2p transports).
pub p2p: P2pServiceCtx,

/// Runtime statistics and metrics collection.
pub stats: Option<Stats>,
/// RPC service for external API queries.
pub rpc: RpcService,
/// Records node state and actions for debugging and replay.
pub recorder: Recorder,
/// Replayer state for deterministic action replay (only set in replay
/// mode).
pub replayer: Option<ReplayerState>,
/// State for runtime invariant checking and validation.
pub invariants_state: InvariantsState,
}

Expand Down
6 changes: 2 additions & 4 deletions node/native/src/replay.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use std::cell::RefCell;

use crate::NodeService;
use node::{
core::thread,
recorder::StateWithInputActionsReader,
snark::{BlockVerifier, TransactionVerifier},
ActionWithMeta, BuildEnv, Store,
};

use crate::NodeService;
use std::cell::RefCell;

pub fn replay_state_with_input_actions(
dir: &str,
Expand Down
8 changes: 2 additions & 6 deletions node/testing/src/hosts.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
use crate::scenario::ListenerNode;
use node::p2p::connection::outgoing::P2pConnectionOutgoingInitOpts;
/// This should be the only place where environment variables are converted to addresses.
///
use std::{env, str::FromStr};

use node::p2p::connection::outgoing::P2pConnectionOutgoingInitOpts;

use crate::scenario::ListenerNode;

pub fn replayer() -> P2pConnectionOutgoingInitOpts {
// "/dns4/1.k8.openmina.com/tcp/31968/p2p/12D3KooWPayQEdprqY2m3biReUUybA5LoULpJE7YWu6wetEKKELv",

let multiaddr = env::var("REPLAYER_MULTIADDR")
.expect("must set variable `REPLAYER_MULTIADDR`")
.parse::<libp2p::Multiaddr>()
Expand Down
10 changes: 4 additions & 6 deletions node/testing/src/scenarios/solo_node/bootstrap.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
use std::time::Duration;

use mina_core::constants::constraint_constants;
use node::transition_frontier::sync::TransitionFrontierSyncState;
use redux::Instant;

use crate::{
hosts,
node::RustNodeTestingConfig,
scenario::{ListenerNode, ScenarioStep},
scenarios::ClusterRunner,
};
use mina_core::constants::constraint_constants;
use node::transition_frontier::sync::TransitionFrontierSyncState;
use redux::Instant;
use std::time::Duration;

/// Set up single Rust node and bootstrap snarked ledger, bootstrap ledger and blocks.
///
Expand Down
14 changes: 14 additions & 0 deletions website/docs/developers/scripts/replayer/record-node-docker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash
# Record node execution using Docker

# Create directory for recording
mkdir -p mina-replay-test

# Run node with recording enabled using Docker
docker run --rm \
-v "$(pwd)/mina-replay-test:/root/.mina" \
o1labs/mina-rust:latest \
node \
--network devnet \
--record state-with-input-actions \
--work-dir /root/.mina
8 changes: 8 additions & 0 deletions website/docs/developers/scripts/replayer/record-node.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash
# Record node execution for debugging and replay

# Run node with recording enabled
mina node \
--network devnet \
--record state-with-input-actions \
--work-dir ~/.mina-replay-test
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash
# Record with custom directory using Docker

# Create custom directory for recording
mkdir -p my-custom-replay-dir

# Run node with custom recording directory
docker run --rm \
-v "$(pwd)/my-custom-replay-dir:/root/.mina" \
o1labs/mina-rust:latest \
node \
--network devnet \
--record state-with-input-actions \
--work-dir /root/.mina
14 changes: 14 additions & 0 deletions website/docs/developers/scripts/replayer/record-with-custom-dir.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash
# Record node execution with custom working directory

WORK_DIR="$1"
if [ -z "$WORK_DIR" ]; then
echo "Usage: $0 <work-directory>"
exit 1
fi

# Run node with recording enabled using custom directory
mina node \
--network devnet \
--record state-with-input-actions \
--work-dir "$WORK_DIR"
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash
# Replay with dynamic effects using Docker

docker run --rm \
-v "$(pwd)/mina-replay-test:/root/.mina" \
-v "$(pwd)/my-effects.so:/effects/my-effects.so" \
o1labs/mina-rust:latest \
replay state-with-input-actions \
--dir /root/.mina/recorder \
--dynamic-effects-lib /effects/my-effects.so
10 changes: 10 additions & 0 deletions website/docs/developers/scripts/replayer/replay-dynamic-effects.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash
# Build custom effects library and replay with custom effects

# Build custom effects library
cargo build --release -p replay_dynamic_effects

# Replay with custom effects
mina replay state-with-input-actions \
--dir ~/.mina-replay-test/recorder \
--dynamic-effects-lib ./target/release/libreplay_dynamic_effects.so
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash
# Replay with build environment mismatch ignored using Docker

docker run --rm \
-v "$(pwd)/mina-replay-test:/root/.mina" \
o1labs/mina-rust:latest \
replay state-with-input-actions \
--dir /root/.mina/recorder \
--ignore-build-env-mismatch
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash
# Replay and ignore build environment differences

mina replay state-with-input-actions \
--dir ~/.mina-replay-test/recorder \
--ignore-mismatch
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash
# Replay recorded node execution using Docker

# Replay from the recorded directory
docker run --rm \
-v "$(pwd)/mina-replay-test:/root/.mina" \
o1labs/mina-rust:latest \
replay state-with-input-actions --dir /root/.mina/recorder
5 changes: 5 additions & 0 deletions website/docs/developers/scripts/replayer/replay-node.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash
# Replay recorded node execution

# Replay from the recorded directory
mina replay state-with-input-actions --dir ~/.mina-replay-test/recorder
Loading
Loading