2025-12-15 20:26:07 +01:00

7.5 KiB
Raw Blame History

Quickstart

Get a working example running quickly.

Prerequisites

  • Rust toolchain (nightly)
  • This repository cloned
  • Unix-like system (tested on Linux and macOS)
  • For Docker Compose examples: Docker daemon running
  • For Docker Desktop on Apple silicon (compose/k8s): set NOMOS_BUNDLE_DOCKER_PLATFORM=linux/arm64 to avoid slow/fragile amd64 emulation builds
  • versions.env file at repository root (defines VERSION, NOMOS_NODE_REV, NOMOS_BUNDLE_VERSION)

Note: nomos-node binaries are built automatically on demand or can be provided via prebuilt bundles.

Important: The versions.env file is required by helper scripts. If missing, the scripts will fail with an error. The file should already exist in the repository root.

Your First Test

The framework ships with runnable example binaries in examples/src/bin/.

Recommended: Use the convenience script:

# From the logos-blockchain-testing directory
scripts/run-examples.sh -t 60 -v 1 -e 1 host

This handles circuit setup, binary building, and runs a complete scenario: 1 validator + 1 executor, mixed transaction + DA workload (5 tx/block + 1 channel + 1 blob), 60s duration.

Alternative: Direct cargo run (requires manual setup):

# Requires circuits in place and NOMOS_NODE_BIN/NOMOS_EXECUTOR_BIN set
POL_PROOF_DEV_MODE=true cargo run -p runner-examples --bin local_runner

Core API Pattern (simplified example):

use testing_framework_core::scenario::{Deployer, ScenarioBuilder};
use testing_framework_runner_local::LocalDeployer;
use testing_framework_workflows::ScenarioBuilderExt;
use std::time::Duration;

// Define the scenario (1 validator + 1 executor, tx + DA workload)
let mut plan = ScenarioBuilder::topology_with(|t| {
        t.network_star()
            .validators(1)
            .executors(1)
    })
    .wallets(1_000)
    .transactions_with(|txs| {
        txs.rate(5)                 // 5 transactions per block
            .users(500)             // use 500 of the seeded wallets
    })
    .da_with(|da| {
        da.channel_rate(1)          // 1 channel
            .blob_rate(1)           // target 1 blob per block
            .headroom_percent(20)   // default headroom when sizing channels
    })
    .expect_consensus_liveness()
    .with_run_duration(Duration::from_secs(60))
    .build();

// Deploy and run
let deployer = LocalDeployer::default();
let runner = deployer.deploy(&plan).await?;
let _handle = runner.run(&mut plan).await?;

Note: The examples are binaries with #[tokio::main], not test functions. If you want to write integration tests, wrap this pattern in #[tokio::test] functions in your own test suite.

Important: POL_PROOF_DEV_MODE=true disables expensive Groth16 zero-knowledge proof generation for leader election. Without it, proof generation is CPU-intensive and tests will timeout. This is required for all runners (local, compose, k8s) for practical testing. Never use in production.

What you should see:

  • Nodes spawn as local processes
  • Consensus starts producing blocks
  • Scenario runs for the configured duration
  • Node logs written to temporary directories in working directory (auto-cleaned up after test)
  • To persist logs: set NOMOS_TESTS_TRACING=true and NOMOS_LOG_DIR=/path/to/logs (files will have prefix like nomos-node-0*, may include timestamps)

What Just Happened?

Let's unpack the code:

1. Topology Configuration

ScenarioBuilder::topology_with(|t| {
        t.network_star()      // Star topology: all nodes connect to seed
            .validators(1)    // 1 validator node
            .executors(1)     // 1 executor node (validator + DA dispersal)
    })

This defines what your test network looks like.

2. Wallet Seeding

.wallets(1_000)              // Seed 1,000 funded wallet accounts

Provides funded accounts for transaction submission.

3. Workloads

.transactions_with(|txs| {
    txs.rate(5)              // 5 transactions per block
        .users(500)          // Use 500 of the 1,000 wallets
})
.da_with(|da| {
    da.channel_rate(1)       // 1 DA channel (more spawned with headroom)
        .blob_rate(1)        // target 1 blob per block
        .headroom_percent(20)// default headroom when sizing channels
})

Generates both transaction and DA traffic to stress both subsystems.

4. Expectation

.expect_consensus_liveness()

This says what success means: blocks must be produced continuously.

5. Run Duration

.with_run_duration(Duration::from_secs(60))

Run for 60 seconds (~27 blocks with default 2s slots, 0.9 coefficient). Framework ensures this is at least 2× the consensus slot duration.

6. Deploy and Execute

let deployer = LocalDeployer::default();  // Use local process deployer
let runner = deployer.deploy(&plan).await?;  // Provision infrastructure
let _handle = runner.run(&mut plan).await?;  // Execute workloads & expectations

Deployer provisions the infrastructure. Runner orchestrates execution.

Adjust the Topology

With run-examples.sh (recommended):

# Scale up to 3 validators + 2 executors, run for 2 minutes
scripts/run-examples.sh -t 120 -v 3 -e 2 host

With direct cargo run:

# Uses NOMOS_DEMO_* env vars (or legacy *_DEMO_* vars)
NOMOS_DEMO_VALIDATORS=3 \
NOMOS_DEMO_EXECUTORS=2 \
NOMOS_DEMO_RUN_SECS=120 \
POL_PROOF_DEV_MODE=true \
cargo run -p runner-examples --bin local_runner

Try Docker Compose

Use the same API with a different deployer for reproducible containerized environment.

Recommended: Use the convenience script (handles everything):

scripts/run-examples.sh -t 60 -v 1 -e 1 compose

This automatically:

  • Fetches circuit assets (to testing-framework/assets/stack/kzgrs_test_params/kzgrs_test_params)
  • Builds/uses prebuilt binaries (via NOMOS_BINARIES_TAR if available)
  • Builds the Docker image
  • Runs the compose scenario

Alternative: Direct cargo run with manual setup:

# Option 1: Use prebuilt bundle (recommended for compose/k8s)
scripts/build-bundle.sh --platform linux  # Creates .tmp/nomos-binaries-linux-v0.3.1.tar.gz
export NOMOS_BINARIES_TAR=.tmp/nomos-binaries-linux-v0.3.1.tar.gz

# Option 2: Manual circuit/image setup (rebuilds during image build)
scripts/setup-nomos-circuits.sh v0.3.1 /tmp/nomos-circuits
cp -r /tmp/nomos-circuits/* testing-framework/assets/stack/kzgrs_test_params/
testing-framework/assets/stack/scripts/build_test_image.sh

# Run with Compose
NOMOS_TESTNET_IMAGE=logos-blockchain-testing:local \
POL_PROOF_DEV_MODE=true \
cargo run -p runner-examples --bin compose_runner

Benefit: Reproducible containerized environment with Prometheus at http://localhost:9090.

Note: Compose expects KZG parameters at /kzgrs_test_params/kzgrs_test_params inside containers (the directory name is repeated as the filename).

In code: Just swap the deployer:

use testing_framework_runner_compose::ComposeDeployer;

// ... same scenario definition ...

let deployer = ComposeDeployer::default();  // Use Docker Compose
let runner = deployer.deploy(&plan).await?;
let _handle = runner.run(&mut plan).await?;

Next Steps

Now that you have a working test: