Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 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
1,923 changes: 1,495 additions & 428 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ default-features = false

[workspace.dependencies.snarkvm]
git = "https://github.com/ProvableHQ/snarkVM.git"
rev = "60322dd"
rev = "77ec1e15592d861e1364bc8a2abaae9c27466438" # NOTE: Change back
#version = "=4.6.0"
default-features = false

Expand Down
19 changes: 9 additions & 10 deletions cli/src/commands/start.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,11 +302,11 @@ pub struct Start {
#[clap(long)]
pub auto_migrate_node_data: bool,

/// Paths to Slipstream plugin config files (JSON5). May be repeated for multiple plugins.
/// Requires the node to be compiled with --features slipstream-plugins.
/// Specify paths to slipstream plugin config files (JSON5). May be provided multiple times.
/// Only available when the node is built with the `slipstream-plugins` feature.
#[cfg(feature = "slipstream-plugins")]
#[clap(long = "slipstream-config", value_name = "PATH", verbatim_doc_comment)]
pub slipstream_configs: Vec<PathBuf>,
#[clap(long = "slipstream-plugins", num_args = 0.., value_delimiter = ',')]
pub slipstream_plugins: Vec<PathBuf>,
}

impl Start {
Expand Down Expand Up @@ -864,18 +864,17 @@ impl Start {
// Register the signal handler.
let signal_handler = SignalHandler::new(Some(handle));

// Collect slipstream plugin config paths (empty slice when feature is disabled).
// Collect slipstream plugin config paths (empty when feature is disabled).
#[cfg(feature = "slipstream-plugins")]
let slipstream_configs: &[PathBuf] = &self.slipstream_configs;
let slipstream_plugin_configs = self.slipstream_plugins.clone();
#[cfg(not(feature = "slipstream-plugins"))]
let slipstream_configs: &[PathBuf] = &[];
let slipstream_plugin_configs: Vec<PathBuf> = vec![];

// Initialize the node.
let node = match node_type {
// NodeType::Validator => Node::new_validator(node_ip, self.bft, rest_ip, self.rest_rps, account, &trusted_peers, &trusted_validators, genesis, cdn, storage_mode, node_data_dir, self.trusted_peers_only, self.auto_db_checkpoints.clone(), dev_txs, self.dev, slipstream_configs, signal_handler.clone()).await,
NodeType::Validator => Node::new_validator(node_ip, self.bft, rest_ip, self.rest_rps, account, &trusted_peers, &trusted_validators, genesis, cdn, storage_mode, node_data_dir, self.trusted_peers_only, self.auto_db_checkpoints.clone(), dev_txs, self.dev, slipstream_configs, dev_num_validators_for_committee_hotswap, signal_handler.clone()).await,
NodeType::Validator => Node::new_validator(node_ip, self.bft, rest_ip, self.rest_rps, account, &trusted_peers, &trusted_validators, genesis, cdn, storage_mode, node_data_dir, self.trusted_peers_only, self.auto_db_checkpoints.clone(), dev_txs, self.dev, dev_num_validators_for_committee_hotswap, signal_handler.clone(), slipstream_plugin_configs).await,
NodeType::Prover => Node::new_prover(node_ip, account, &trusted_peers, genesis, node_data_dir, self.trusted_peers_only, self.dev, signal_handler.clone()).await,
NodeType::Client => Node::new_client(node_ip, rest_ip, self.rest_rps, account, &trusted_peers, genesis, cdn, storage_mode, node_data_dir, self.trusted_peers_only, self.auto_db_checkpoints.clone(), self.dev, slipstream_configs, signal_handler.clone()).await,
NodeType::Client => Node::new_client(node_ip, rest_ip, self.rest_rps, account, &trusted_peers, genesis, cdn, storage_mode, node_data_dir, self.trusted_peers_only, self.auto_db_checkpoints.clone(), self.dev, signal_handler.clone(), slipstream_plugin_configs).await,
NodeType::BootstrapClient => Node::new_bootstrap_client(node_ip, account, *genesis.header(), self.dev).await,
}?;

Expand Down
23 changes: 20 additions & 3 deletions node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,14 @@ metrics = [
"snarkos-node-router/metrics",
"snarkos-node-tcp/metrics"
]
history = [ "snarkos-node-rest/history", "snarkvm/history" ]
history-staking-rewards = [ "snarkos-node-rest/history-staking-rewards", "snarkvm/history-staking-rewards" ]
slipstream-plugins = [ "snarkos-node-rest/slipstream-plugins", "snarkvm/slipstream-plugins" ]
history = [ "snarkos-node-rest/history" ]
history-staking-rewards = [ "snarkos-node-rest/history-staking-rewards" ]
slipstream-plugins = [
"snarkvm/slipstream-plugins",
"dep:slipstream-plugin-postgres",
"dep:json5",
"dep:inventory",
]
telemetry = [ "snarkos-node-bft/telemetry", "snarkos-node-consensus/telemetry", "snarkos-node-rest/telemetry" ]
cuda = [
"snarkvm/cuda",
Expand Down Expand Up @@ -139,6 +144,18 @@ workspace = true
[dependencies.snarkvm]
workspace = true

[dependencies.slipstream-plugin-postgres]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

TODO: Remove

path = "../../slipstream-plugin-postgres"
optional = true

[dependencies.json5]
version = "0.4"
optional = true

[dependencies.inventory]
version = "0.3"
optional = true

[dependencies.time]
workspace = true

Expand Down
9 changes: 2 additions & 7 deletions node/rest/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ impl<N: Network, C: ConsensusStorage<N>, R: Routing<N>> Rest<N, C, R> {
fn build_routes(&self, rest_rps: u32) -> axum::Router {
let cors = CorsLayer::new()
.allow_origin(Any)
.allow_methods([Method::GET, Method::POST, Method::DELETE, Method::OPTIONS])
.allow_methods([Method::GET, Method::POST, Method::OPTIONS])
.allow_headers([CONTENT_TYPE]);

// Prepare the rate limiting setup.
Expand Down Expand Up @@ -182,12 +182,7 @@ impl<N: Network, C: ConsensusStorage<N>, R: Routing<N>> Rest<N, C, R> {
// Slipstream plugin management endpoints require auth.
#[cfg(feature = "slipstream-plugins")]
let auth_routes = auth_routes
.route("/slipstream/plugins", get(Self::slipstream_list_plugins).post(Self::slipstream_load_plugin))
.route(
"/slipstream/plugins/{name}",
// TODO: PUT (reload) is not yet implemented.
axum::routing::delete(Self::slipstream_unload_plugin),
);
.route("/slipstream/plugins", get(Self::slipstream_list_plugins));

let routes = axum::Router::new()
.merge(auth_routes.route_layer(middleware::from_fn(auth_middleware)))
Expand Down
65 changes: 0 additions & 65 deletions node/rest/src/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1213,71 +1213,6 @@ impl<N: Network, C: ConsensusStorage<N>, R: Routing<N>> Rest<N, C, R> {
Ok((StatusCode::OK, ErasedJson::pretty(plugins)))
}

/// POST /{network}/slipstream/plugins
#[cfg(feature = "slipstream-plugins")]
pub(crate) async fn slipstream_load_plugin(
State(rest): State<Self>,
Json(body): Json<serde_json::Value>,
) -> Result<impl axum::response::IntoResponse, RestError> {
use snarkvm::slipstream_plugin_manager::slipstream_manager::SlipstreamPluginManagerError;

let config_file = body
.get("config_file")
.and_then(|v| v.as_str())
.ok_or_else(|| RestError::bad_request(anyhow!("Missing required field: config_file")))?
.to_owned();
let mgr_arc = rest.ledger.vm().finalize_store().slipstream_plugin_manager();
if mgr_arc.read().is_none() {
return Err(RestError::service_unavailable(anyhow!("No Slipstream plugin manager is installed")));
}
let name = tokio::task::spawn_blocking(move || -> Result<String, SlipstreamPluginManagerError> {
// Safety: manager is set exactly once and never cleared; verified Some above.
mgr_arc.write().as_mut().expect("plugin manager verified present").load_plugin(&config_file)
})
.await
.map_err(|e| RestError::internal_server_error(anyhow!("Task join error: {e}")))?
.map_err(|e| match e {
SlipstreamPluginManagerError::PluginAlreadyLoaded(_) => RestError::unprocessable_entity(anyhow!("{e}")),
other => RestError::internal_server_error(anyhow!("{other}")),
})?;
Ok((StatusCode::OK, ErasedJson::pretty(serde_json::json!({ "loaded": name }))))
}

/// DELETE /{network}/slipstream/plugins/{name}
#[cfg(feature = "slipstream-plugins")]
pub(crate) async fn slipstream_unload_plugin(
State(rest): State<Self>,
Path(name): Path<String>,
) -> Result<impl axum::response::IntoResponse, RestError> {
use snarkvm::slipstream_plugin_manager::slipstream_manager::SlipstreamPluginManagerError;

let mgr_arc = rest.ledger.vm().finalize_store().slipstream_plugin_manager();
if mgr_arc.read().is_none() {
return Err(RestError::service_unavailable(anyhow!("No Slipstream plugin manager is installed")));
}
tokio::task::spawn_blocking(move || -> anyhow::Result<()> {
// Safety: manager is set exactly once and never cleared; verified Some above.
mgr_arc.write().as_mut().expect("plugin manager verified present").unload_plugin(&name).map_err(
|e: SlipstreamPluginManagerError| match e {
SlipstreamPluginManagerError::PluginNotLoaded(_) => anyhow!("404: {e}"),
other => anyhow!("{other}"),
},
)
})
.await
.map_err(|e| RestError::internal_server_error(anyhow!("Task join error: {e}")))?
.map_err(|e| {
let msg = e.to_string();
if let Some(stripped) = msg.strip_prefix("404: ") {
RestError::not_found(anyhow!("{stripped}"))
} else {
RestError::internal_server_error(e)
}
})?;
Ok((StatusCode::OK, ErasedJson::pretty(serde_json::json!({ "unloaded": true }))))
}

// TODO: PUT /{network}/slipstream/plugins/{name} (reload) is not yet implemented.
}

#[cfg(all(test, feature = "history"))]
Expand Down
26 changes: 18 additions & 8 deletions node/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,9 @@ impl<N: Network, C: ConsensusStorage<N>> Client<N, C> {
node_data_dir: NodeDataDir,
trusted_peers_only: bool,
dev: Option<u16>,
_slipstream_configs: &[std::path::PathBuf],
signal_handler: Arc<SignalHandler>,
#[cfg(feature = "slipstream-plugins")] slipstream_plugin_configs: Vec<std::path::PathBuf>,
#[cfg(not(feature = "slipstream-plugins"))] _slipstream_plugin_configs: Vec<std::path::PathBuf>,
) -> Result<Self> {
// Initialize the ledger.
let ledger = {
Expand All @@ -156,15 +157,24 @@ impl<N: Network, C: ConsensusStorage<N>> Client<N, C> {
}
.with_context(|| "Failed to initialize the ledger")?;

// Initialize the Slipstream plugin manager (if any config files were provided).
// Initialize slipstream plugins (if any are configured).
#[cfg(feature = "slipstream-plugins")]
if !_slipstream_configs.is_empty() {
let manager =
snarkvm::slipstream_plugin_manager::SlipstreamPluginManager::from_config_files(_slipstream_configs)
.context("Failed to initialize Slipstream plugin manager")?;
if !slipstream_plugin_configs.is_empty() {
let mut manager = snarkvm::slipstream_plugin_manager::SlipstreamPluginManager::new();
for config_path in &slipstream_plugin_configs {
let raw = std::fs::read_to_string(config_path)
.with_context(|| format!("Failed to read slipstream config {config_path:?}"))?;
let val: serde_json::Value = json5::from_str(&raw)
.with_context(|| format!("Invalid JSON5 in slipstream config {config_path:?}"))?;
let plugin = crate::build_static_slipstream_plugin(&val).ok_or_else(|| {
let name = val.get("name").and_then(|v| v.as_str()).unwrap_or("<missing>");
anyhow::anyhow!("Unknown static slipstream plugin '{name}' in {config_path:?}")
})?;
manager
.register(plugin, config_path)
.map_err(|e| anyhow::anyhow!("Failed to register slipstream plugin: {e}"))?;
}
ledger.vm().finalize_store().set_slipstream_plugin_manager(manager);
let num_plugins = _slipstream_configs.len();
tracing::info!(target: "slipstream", "Slipstream plugin manager registered ({num_plugins} plugin(s))");
}

// Initialize the ledger service.
Expand Down
16 changes: 16 additions & 0 deletions node/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@ pub use node::*;
mod traits;
pub use traits::*;

/// Returns a boxed static slipstream plugin instance for the given config, or
/// `None` if the plugin name is not recognized.
///
/// The config is already parsed from the JSON5 file; the `name` field selects
/// the plugin to instantiate. Plugins self-register via `inventory::submit!` at
/// link time — no per-plugin code is needed here.
#[cfg(feature = "slipstream-plugins")]
pub(crate) fn build_static_slipstream_plugin(
config: &serde_json::Value,
) -> Option<Box<dyn snarkvm::slipstream_plugin_interface::slipstream_plugin_interface::SlipstreamPlugin>>
{
use snarkvm::slipstream_plugin_interface::slipstream_plugin_interface::PluginRegistration;
let name = config.get("name").and_then(|v| v.as_str())?;
inventory::iter::<PluginRegistration>().find(|r| r.name == name).map(|r| (r.factory)())
}

use aleo_std::StorageMode;

/// A helper to log instructions to recover.
Expand Down
8 changes: 4 additions & 4 deletions node/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ impl<N: Network> Node<N> {
auto_db_checkpoints: Option<PathBuf>,
dev_txs: bool,
dev: Option<u16>,
slipstream_configs: &[PathBuf],
dev_num_validators_for_committee_hotswap: Option<u16>,
signal_handler: Arc<SignalHandler>,
slipstream_plugin_configs: Vec<PathBuf>,
) -> Result<Self> {
let validator = Arc::new(
Validator::new(
Expand All @@ -118,9 +118,9 @@ impl<N: Network> Node<N> {
trusted_peers_only,
dev_txs,
dev,
slipstream_configs,
dev_num_validators_for_committee_hotswap,
signal_handler,
slipstream_plugin_configs,
)
.await?,
);
Expand Down Expand Up @@ -177,8 +177,8 @@ impl<N: Network> Node<N> {
trusted_peers_only: bool,
auto_db_checkpoints: Option<PathBuf>,
dev: Option<u16>,
slipstream_configs: &[PathBuf],
signal_handler: Arc<SignalHandler>,
slipstream_plugin_configs: Vec<PathBuf>,
) -> Result<Self> {
let client = Arc::new(
Client::new(
Expand All @@ -193,8 +193,8 @@ impl<N: Network> Node<N> {
node_data_dir,
trusted_peers_only,
dev,
slipstream_configs,
signal_handler,
slipstream_plugin_configs,
)
.await?,
);
Expand Down
26 changes: 18 additions & 8 deletions node/src/validator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,11 @@ impl<N: Network, C: ConsensusStorage<N>> Validator<N, C> {
trusted_peers_only: bool,
dev_txs: bool,
dev: Option<u16>,
_slipstream_configs: &[std::path::PathBuf],
#[cfg(feature = "test_network")] dev_num_validators_for_committee_hotswap: Option<u16>,
#[cfg(not(feature = "test_network"))] _dev_num_validators_for_committee_hotswap: Option<u16>,
signal_handler: Arc<SignalHandler>,
#[cfg(feature = "slipstream-plugins")] slipstream_plugin_configs: Vec<std::path::PathBuf>,
#[cfg(not(feature = "slipstream-plugins"))] _slipstream_plugin_configs: Vec<std::path::PathBuf>,
) -> Result<Self> {
// Initialize the ledger.
let ledger = {
Expand All @@ -106,15 +107,24 @@ impl<N: Network, C: ConsensusStorage<N>> Validator<N, C> {
}
.with_context(|| "Failed to initialize the ledger")?;

// Initialize the Slipstream plugin manager (if any config files were provided).
// Initialize slipstream plugins (if any are configured).
#[cfg(feature = "slipstream-plugins")]
if !_slipstream_configs.is_empty() {
let manager =
snarkvm::slipstream_plugin_manager::SlipstreamPluginManager::from_config_files(_slipstream_configs)
.context("Failed to initialize Slipstream plugin manager")?;
if !slipstream_plugin_configs.is_empty() {
let mut manager = snarkvm::slipstream_plugin_manager::SlipstreamPluginManager::new();
for config_path in &slipstream_plugin_configs {
let raw = std::fs::read_to_string(config_path)
.with_context(|| format!("Failed to read slipstream config {config_path:?}"))?;
let val: serde_json::Value = json5::from_str(&raw)
.with_context(|| format!("Invalid JSON5 in slipstream config {config_path:?}"))?;
let plugin = crate::build_static_slipstream_plugin(&val).ok_or_else(|| {
let name = val.get("name").and_then(|v| v.as_str()).unwrap_or("<missing>");
anyhow::anyhow!("Unknown static slipstream plugin '{name}' in {config_path:?}")
})?;
manager
.register(plugin, config_path)
.map_err(|e| anyhow::anyhow!("Failed to register slipstream plugin: {e}"))?;
}
ledger.vm().finalize_store().set_slipstream_plugin_manager(manager);
let num_plugins = _slipstream_configs.len();
tracing::info!(target: "slipstream", "Slipstream plugin manager registered ({num_plugins} plugin(s))");
}

// Initialize the ledger service.
Expand Down