Skip to content

Slipstream Plugin Integration/Interface#3190

Open
dgrkotsonis wants to merge 33 commits intostagingfrom
stream_plugin_testing
Open

Slipstream Plugin Integration/Interface#3190
dgrkotsonis wants to merge 33 commits intostagingfrom
stream_plugin_testing

Conversation

@dgrkotsonis
Copy link
Copy Markdown
Collaborator

@dgrkotsonis dgrkotsonis commented Mar 16, 2026

Overview

Adds a Slipstream Plugin system for streaming real-time mappings and staking data to external services without relying on RPC calls.

Related PRs/Repos:


New Crates

plugins/slipstream_plugin_interface

Defines the SlipstreamPlugin trait — the interface all plugins must implement.

Method Description
on_load / on_unload Lifecycle hooks
notify_mapping_update Called when a mapping key-value is inserted/updated during canonical finalize; args are serialized to bytes for object-safety
notify_staking_reward Called once per staker per block during staking reward distribution
history_enabled / history_staking_rewards_enabled Flags plugins use to opt in to data streams

Note: At the moment, any mappings filtering must be handled by the plugins themselves, notify_mapping_update gets called for all mappings

plugins/slipstream_plugin_manager

Manages loaded plugins and their backing libloading::Library handles.

  • LoadedSlipstreamPlugin — wrapper holding a boxed plugin + its name; implements Deref/DerefMut
  • SlipstreamPluginManager
    • unload() — fires on_unload() on each plugin then drops the libraries
    • history_mappings_enabled() / history_staking_rewards_enabled() — aggregate opt-in checks
    • notify_mapping_update() — fan-out broadcast to all interested plugins
  • SlipstreamService — async service wrapping the manager (separate file)

Plugin Config File (JSON5)

Each plugin requires a config file:

{
  "libpath": "/path/to/libmy_plugin.so",  // required; relative paths resolve from the config file's dir
  "name": "my_plugin"                      // optional; overrides the plugin's name() return value
}

Plugin Library Convention

The shared library (.so / .dylib / .dll) must export a C function:

#[no_mangle]
pub extern "C" fn _create_plugin() -> *mut dyn SlipstreamPlugin {
    Box::into_raw(Box::new(MyPlugin::new()))
}

Startup

SlipstreamPluginService::new() takes a slice of config file paths:

let service = SlipstreamPluginService::new(&[
    PathBuf::from("/etc/aleo/plugins/my_plugin.json5"),
])?;

Note: Errors from plugin callbacks (notify_mapping_update, notify_staking_reward) are logged as warnings and never propagated — a misbehaving plugin will not crash the node.

@dgrkotsonis dgrkotsonis marked this pull request as draft March 16, 2026 13:42
@dgrkotsonis dgrkotsonis force-pushed the stream_plugin_testing branch from 0341a32 to b77006b Compare March 16, 2026 14:00
@dgrkotsonis dgrkotsonis changed the title save place to check on first iteration of diffs First Iteration of Stream Plugin (Draft) Mar 16, 2026
@dgrkotsonis dgrkotsonis changed the title First Iteration of Stream Plugin (Draft) Slipstream Plugin Integration/Interface (Draft) Mar 16, 2026
@miazn miazn requested a review from Copilot March 20, 2026 15:17
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a Slipstream plugin system to stream canonical mapping updates and staking rewards to external services via dynamically loaded plugins, avoiding RPC-based polling.

Changes:

  • Introduces new plugin interface + plugin manager crates (dynamic loading via libloading, JSON5 config parsing).
  • Wires canonical-finalize lifecycle signaling into the VM + finalize store so storage writes can fan out events to plugins.
  • Extends ledger/store feature flags to include the plugin manager dependency for history and history-staking-rewards.

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 16 comments.

Show a summary per file
File Description
utilities/src/bytes.rs Minor refactor using is_multiple_of(8) for clarity.
synthesizer/src/vm/finalize.rs Sets/reset canonical-finalize mode flag; emits staking reward notifications; some whitespace-only edits.
ledger/store/src/program/finalize.rs Adds finalize-mode flag + plugin manager installation; notifies plugins on mapping updates and staking rewards.
ledger/store/Cargo.toml history* features now pull in slipstream plugin manager.
plugins/slipstream_plugin_interface/src/slipstream_plugin_interface.rs Defines the SlipstreamPlugin trait and callback surface.
plugins/slipstream_plugin_interface/src/lib.rs Exposes the interface module.
plugins/slipstream_plugin_interface/README.md Documents plugin config and library export convention.
plugins/slipstream_plugin_interface/Cargo.toml New crate manifest for the interface.
plugins/slipstream_plugin_manager/src/slipstream_manager.rs Implements plugin loading/unloading + fan-out notification logic + tests.
plugins/slipstream_plugin_manager/src/slipstream_service.rs Adds a small service wrapper around the manager.
plugins/slipstream_plugin_manager/src/lib.rs Module exports for manager/service.
plugins/slipstream_plugin_manager/Cargo.toml New crate manifest for the manager.
Cargo.toml Adds the new plugin crates to the workspace and workspace dependencies.
Cargo.lock Adds transitive deps (e.g., json5, pest*) and the new crates.

@dgrkotsonis dgrkotsonis force-pushed the stream_plugin_testing branch from adf0c33 to 77faa25 Compare March 27, 2026 17:28
@dgrkotsonis dgrkotsonis marked this pull request as ready for review April 1, 2026 21:21
@dgrkotsonis dgrkotsonis changed the title Slipstream Plugin Integration/Interface (Draft) Slipstream Plugin Integration/Interface Apr 1, 2026
@dgrkotsonis dgrkotsonis requested a review from ljedrz April 1, 2026 21:26
@dgrkotsonis dgrkotsonis force-pushed the stream_plugin_testing branch from 7fb6b76 to 933fbf9 Compare April 3, 2026 18:04
Copy link
Copy Markdown
Collaborator

@vicsn vicsn left a comment

Choose a reason for hiding this comment

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

Mostly LGTM! Few questions

#[cfg(all(feature = "history", feature = "slipstream-plugins"))]
if let Some((pid, mname, k, v)) = plugin_data {
#[cfg(feature = "history")]
let height = self.storage.current_block_height().load(Ordering::SeqCst);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is this used?

#[cfg(all(feature = "history", feature = "slipstream-plugins"))]
if let Some((pid, mname, serialized_entries)) = plugin_data {
#[cfg(feature = "history")]
let height = self.storage.current_block_height().load(Ordering::SeqCst);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Same note as above

])?;
```

> **Note:** Not yet wired up to any CLI flags or environment variables. How/where `SlipstreamPluginService` gets constructed and passed into the VM still needs to be plumbed in (likely in snarkOS or wherever the VM is instantiated).
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should we reword/remove?


> **Note:** Not yet wired up to any CLI flags or environment variables. How/where `SlipstreamPluginService` gets constructed and passed into the VM still needs to be plumbed in (likely in snarkOS or wherever the VM is instantiated).

> Errors from plugin callbacks (`notify_mapping_update`, `notify_staking_reward`) are logged as warnings and never propagated — a misbehaving plugin will not crash the node. No newline at end of file
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I personally like the "fail loudly" philosophy. But proper error handling/metrics is also more work so can be left for the future.

// Signal to Slipstream plugins that canonical finalize is starting.
#[cfg(feature = "slipstream-plugins")]
{
tracing::info!(target: "slipstream", "atomic_finalize: is_finalize_mode → true");
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is debug maybe a better log level?

Can/should this code block be hidden inside of the atomic_finalize! macro?

// limitations under the License.

use snarkvm_slipstream_plugin_interface::slipstream_plugin_interface::SlipstreamPlugin;
use tokio::sync::oneshot::Sender as OneShotSender;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nit: separate deps local to this repo and external to this repo with a newline

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants