Skip to content

Conversation

@Frando
Copy link
Member

@Frando Frando commented Oct 20, 2025

Description

This changes the simulation spawner API to be generic over a Ctx trait that combines the existing SetupData with a new Config, to allow for dynamic simulation config down the road. It also adds the spawn method to the Node trait, to make things easier and less convoluted.

(The following API change summary is AI generated but checked by me to be accurate)

  • Introduced a Ctx trait to model simulation context (config + setup) and round labeling.
  • Unified node spawning into the Node<C> trait and removed the old Spawn<D> trait.
  • Made SpawnContext and RoundContext generic over the new Ctx and added access to config.
  • Added optional Node::node_label(&SpawnContext<C>) for per-node labels in traces.
  • Refactored builder types to be generic over Ctx, with new Builder::with_config.

Added

  • trait Ctx with:
    • type Config: SetupData + Default
    • type Setup: SetupData
    • async fn setup(config: &Self::Config) -> Result<Self::Setup>
    • fn round_label(config: &Self::Config, round: u32) -> Option<String>
  • SpawnContext<'_, C>::config() and RoundContext<'_, C>::config() accessors.
  • Node::node_label(&SpawnContext<C>) -> Option<String> to label nodes.
  • Builder::with_config(config: C::Config) alongside Builder::new() (uses Default config).

Breaking Changes

  • Removed Spawn<D>; node spawning is now defined on Node<C>:
    • Before: impl Spawn<D> for MyNode { async fn spawn(&mut SpawnContext<'_, D>) { ... } }
    • After: impl Node<MyCtx> for MyNode { async fn spawn(&mut SpawnContext<'_, MyCtx>) { ... } }
  • Builder and contexts now use Ctx instead of a free SetupData type:
    • Builder<D>Builder<C> where C: Ctx.
    • SpawnContext<'_, D> / RoundContext<'_, D>SpawnContext<'_, C> / RoundContext<'_, C>.
    • Setup is no longer provided via a builder closure; implement Ctx::setup instead.
  • NodeBuilder<N, D>NodeBuilder<N, C> with bound N: Node<C>.
  • BoxNode and DynNode are now generic over C: Ctx.
  • Round labeling moved from the old Spawn::round_label(&D, round) to Ctx::round_label(&C::Config, round).
  • run_sim_fn now returns/consumes Builder<C> (where C: Ctx).

Migration Example

Below is a minimal example showing how to convert an existing simulation.

Before (old API with Spawn<D> and builder-provided setup):

use anyhow::Result;
use iroh_n0des::simulation::{Builder, Node, NodeBuilder, RoundContext, SpawnContext, SetupData};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
struct Data { topic_id: u64 }

struct MyNode;

impl Node for MyNode {
    fn endpoint(&self) -> Option<&iroh_n0des::iroh::Endpoint> { None }
}

impl Spawn<Data> for MyNode {
    async fn spawn(_ctx: &mut SpawnContext<'_, Data>) -> Result<Self> { Ok(Self) }
    fn round_label(_d: &Data, round: u32) -> Option<String> { Some(format!("Round {round}")) }
}

async fn build_sim() -> Result<Builder<Data>> {
    async fn round(_node: &mut MyNode, _ctx: &RoundContext<'_, Data>) -> Result<bool> { Ok(true) }

    Ok(Builder::with_setup(|| async {
        Ok(Data { topic_id: 123 })
    })
    .spawn(2, NodeBuilder::new(round))
    .rounds(3))
}

After (new API with Ctx and Node<C>):

use anyhow::Result;
use iroh_n0des::simulation::{Builder, Ctx, Node, NodeBuilder, RoundContext, SpawnContext};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
struct Data { topic_id: u64 }

impl Ctx for Data {
    type Config = ();            // or your own config type
    type Setup = Self;           // share Data across nodes

    async fn setup(_cfg: &Self::Config) -> Result<Self::Setup> {
        Ok(Data { topic_id: 123 })
    }

    // optionally implement `round_label` to provide a label to a round number
    fn round_label(_cfg: &Self::Config, round: u32) -> Option<String> {
        Some(format!("Round {round}"))
    }
}

struct MyNode;

impl Node<Data> for MyNode {
    fn endpoint(&self) -> Option<&iroh_n0des::iroh::Endpoint> { None }

    async fn spawn(_ctx: &mut SpawnContext<'_, Data>) -> Result<Self> { 
        Ok(Self)
    }

    // optionally provide a label for this node
    fn node_label(ctx: &SpawnContext<'_, Data>) -> Option<String> {
        Some(format!("MyNode no. {}", ctx.node_index()"))
    }
}

async fn build_sim() -> Result<Builder<Data>> {
    async fn round(_node: &mut MyNode, _ctx: &RoundContext<'_, Data>) -> Result<bool> { Ok(true) }

    Ok(Builder::new()                // or Builder::with_config(cfg)
        .spawn(2, NodeBuilder::new(round))
        .rounds(3))
}

Notes:

  • Access shared setup via context.setup_data() or ctx.setup_data() in spawn/round functions; access config via context.config().
  • To label nodes individually, implement fn node_label(&self, ctx: &SpawnContext<C>) -> Option<String> in your Node<C> impl.

Change checklist

  • Self-review.
  • Documentation updates following the style guide, if relevant.
  • Tests if relevant.
  • All breaking changes documented.

@Frando Frando changed the title refactor: improve simulation builder API refactor!: improve simulation builder API Oct 20, 2025
@Frando Frando force-pushed the Frando/refactor-simulation-spawn branch from 2f3c3a1 to 8aabda1 Compare October 20, 2025 12:55
@github-actions
Copy link

github-actions bot commented Oct 20, 2025

Documentation for this PR has been generated and is available at: https://n0-computer.github.io/iroh-n0des/pr/45/docs/iroh_n0des/

Last updated: 2025-10-20T13:19:14Z

@Frando Frando force-pushed the Frando/refactor-simulation-spawn branch from 8aabda1 to 2b7f368 Compare October 20, 2025 13:17
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.

2 participants