Keyboard shortcuts

Press ← or β†’ to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

πŸ”₯ Crucible Simulator

CI MSRV License Pitch video

SIMULATE β€” a deterministic environment for reproducing and testing Stellar Confidential Token flows.

πŸ“– Documentation site for all three repositories: https://crucible-docs-flame.vercel.app

β–Ά Watch the pitch β€” 4:52

Crucible product pitch video

Click the thumbnail to watch the full product pitch. It covers the problem, the three-layer architecture, where the simulator sits beneath the prover and above the state transitions, and the limits this project states about itself.

Every frame is a live capture β€” the deployed documentation site, the public repositories, and the deployed Stellar testnet verifier contract. Nothing in it is a mock-up, and nothing in it is a slide about a roadmap.

Pitch preview

                    CRUCIBLE
                       |
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β–Ό              β–Ό              β–Ό
    SIMULATOR        PROVER       SCENARIOS
        β”‚              β”‚              β”‚
     Execute         Prove         Challenge
     State           Crypto        Behavior
     Flows           Validity      Resilience

crucible-simulator is the execution and state-simulation foundation of the Crucible ecosystem. It reproduces the lifecycle of Confidential Token operations β€” Register β†’ Deposit β†’ Merge β†’ Confidential Transfer β†’ Withdraw β€” locally, deterministically, and inspectably, before any real prover or integration environment is involved. It is one of the three source repositories, joined by a fourth that renders their documentation:

RepositoryLayerResponsibility
crucible-simulatorSIMULATEreproduce Confidential Token flows and state transitions
crucible-proverPROVEgenerate and verify mock and real cryptographic proofs
crucible-scenariosSTRESS-TESTexecute conformance, failure, and adversarial scenarios
crucible-docsREADthe rendered documentation for all three layers β€” a build of their markdown, not a source of it

Why does this exist?

Testing confidential flows against a full application stack is slow and unreproducible. Crucible Simulator lets a developer say:

Create environment β†’ Create token β†’ Create accounts β†’ Register accounts
β†’ Deposit assets β†’ Generate state β†’ Execute confidential operation
β†’ Update state β†’ Inspect result β†’ Assert expected state

…and reproduce it bit-for-bit, every time.

The defining property is determinism:

same configuration + same initial state + same operation sequence + same seed
   β†’ same resulting state + same events + same commitments + same outcomes

Repository layout

crates/
β”œβ”€β”€ core/        # Pure domain model: accounts, tokens, commitments,
β”‚                #   balances, operations, transactions, events, errors
β”œβ”€β”€ state/       # Deterministic state engine: store, commitment lifecycle,
β”‚                #   nullifiers, transitions, rollback, snapshots, Merkle
β”œβ”€β”€ flows/       # Lifecycle flows + ProofProvider boundary + validation
β”œβ”€β”€ simulator/   # High-level API: Configuration, Environment, Simulator
β”œβ”€β”€ fixtures/    # Versioned fixtures, loader, scenario runner
β”œβ”€β”€ cli/         # The `crucible` command-line interface (stateful)
└── adapters/    # soroban: local vs Soroban/Testnet execution boundary
                 # testnet: network configuration + transcript runner
examples/        # Runnable demonstrations, one directory per example
fixtures/        # The canonical JSON fixture corpus (accounts, tokens,
                 #   commitments, balances, transactions, successful +
                 #   failure scenarios; environments are embedded)
schemas/         # JSON Schema mirrors of the fixture contracts
tests/           # Defining-property suites: integration, determinism,
                 #   flows (fuzz), state (invariants), regression
docs/            # Architecture and design documentation
benches/         # Criterion benchmarks: transfer, state, commitments,
                 #   fixtures

Dependency direction is strictly one way:

core  β†’  state  β†’  flows  β†’  simulator  β†’  fixtures

crucible-prover will implement the ProofProvider trait (defined in flows); nothing here imports a proving system.

Command-line interface

A stateful CLI runs the same environment across commands (one operation per ledger), persisted under --dir (default .crucible/):

cargo run -p crucible-cli -- init --seed 42
cargo run -p crucible-cli -- account create alice
cargo run -p crucible-cli -- account create bob
cargo run -p crucible-cli -- token create CCT --decimals 7
cargo run -p crucible-cli -- register 1 1
cargo run -p crucible-cli -- simulate deposit 1 1 1000 --reference pub-1
cargo run -p crucible-cli -- simulate transfer 1 1 300 --recipient 2
cargo run -p crucible-cli -- state inspect --json
cargo run -p crucible-cli -- snapshot create baseline
cargo run -p crucible-cli -- snapshot restore baseline
cargo run -p crucible-cli -- fixture run full-lifecycle

Examples and benchmarks

# Runnable demonstrations (register -> deposit -> transfer -> withdraw ...)
cargo run -p crucible --example basic-flow
cargo run -p crucible --example deposit-and-transfer
cargo run -p crucible --example transfer-and-withdraw
cargo run -p crucible --example multi-account
cargo run -p crucible --example state-snapshot
cargo run -p crucible --example testnet

# Performance targets for the pieces scenarios touch
cargo bench -p crucible --bench transfer
cargo bench -p crucible --bench state
cargo bench -p crucible --bench commitments
cargo bench -p crucible --bench fixtures

Installation & quick start

Requires stable Rust (see rust-toolchain.toml).

cargo build --workspace
cargo test  --workspace
cargo clippy --workspace --all-targets

The fastest way to see the whole lifecycle run is the integration suite:

cargo test -p crucible --test integration

The other defining-property suites live under tests/ on the root package (a deterministic tests/determinism, the invariant suite tests/state, regression fixtures tests/regression, and the seeded stress harness tests/flows). Per-crate unit tests stay in their crates β€” Cargo can only host one harness per test directory, so tests/unit maps to in-crate #[cfg(test)] modules by necessity.

A minimal simulation in code:

#![allow(unused)]
fn main() {
use crucible_simulator::{Simulator, Configuration};
use crucible_core::{AccountId, TokenId, types::SyntheticIdentity};

let mut sim = Simulator::local(42); // deterministic seed 42

sim.create_account(SyntheticIdentity::Alice)?;
sim.create_account(SyntheticIdentity::Bob)?;
sim.create_account(SyntheticIdentity::Issuer)?;
sim.create_confidential_token("CCT", 7, AccountId::new(4))?;

sim.register(AccountId::new(1), TokenId::new(1))?;
sim.register(AccountId::new(2), TokenId::new(1))?;
sim.deposit(AccountId::new(1), TokenId::new(1), 1000, "pub-ref")?;
sim.transfer(AccountId::new(1), AccountId::new(2), TokenId::new(1), 300)?;
sim.withdraw(AccountId::new(2), TokenId::new(1), 100, "pub-ref")?;

// Assert (private testing view; never emit into production output):
assert_eq!(sim.inspect_private_balance(AccountId::new(1), TokenId::new(1)), 700);
}

Replaying from a versioned fixture:

#![allow(unused)]
fn main() {
use crucible_fixtures::{load_fixture, run_scenario};
let loaded = load_fixture("fixtures/successful/full-lifecycle.json")?;
if let LoadedFixture::Scenario(scenario) = loaded {
    let outcome = run_scenario(&scenario)?;   // deterministic outcome
}
}

Core concepts

ConceptWhereNotes
AccountcoreSynthetic identities (Alice/Bob/…), registration + permission state
TokencoreNative test asset and configured confidential tokens
Commitmentcore/stateDeterministic digest over (owner, token, value, nonce); Active/Consumed lifecycle
BalancecoreObservable anchor (commitment) vs private simulation value
OperationcoreThe five lifecycle operations as one enum
TransactioncoreDeterministic-ID record of every operation β€” Success/Rejected/Failed with the error embedded
EventcoreValue-free observable output (success + operation_rejected)
StateStorestateBTree-backed deterministic store with a state-root digest
NullifierstateConsumed-commitment registry; replay protection
TransitionstateExplicit State0 β†’ Transition β†’ State1, audited on record
SnapshotstateNamed full-state captures with exact restore
ProofProviderflowsThe interface crucible-prover will implement; mock today
SimulatorsimulatorThe high-level deterministic API

Confidential flows

Every flow validates structure β†’ authorization β†’ state β†’ proof, applies its changes inside a TransactionScope (failed flows roll back completely), and records a transaction, a value-free event, and an audited transition. Rejected and failed operations are recorded too: they produce a Rejected /Failed transaction and an operation_rejected event without ever mutating state β€” so conformance suites can assert on the rejection record.

  • Register β€” the gate; per-account, per-token; duplicates rejected.
  • Deposit β€” public asset β†’ fresh commitment with a deterministic blinding nonce.
  • Merge β€” consolidate several owned commitments into one (sum).
  • Confidential transfer β€” spend the sender’s commitments (each is nullified), prove through the ProofProvider, create recipient + change commitments. The simulator never proves itself.
  • Withdraw β€” spend commitments back to the public world; change stays confidential.

Determinism & snapshots

Everything is a pure function of configuration + state + operation sequence

  • seed. The suite in tests/determinism compares full transcripts across runs. Snapshots let you capture state, run operations, restore, and rerun the exact sequence β€” the debugging workflow crucible-scenarios relies on.

The privacy boundary

The simulator is a testing environment, so it can expose internal state β€” but only through explicitly labeled private inspection APIs (inspect_private_balance, inspect_private_head, and the state store itself). Observable output (events, transaction records, proofs, fixtures for publication) never contains amounts or commitment values. This distinction mirrors Stellar’s Confidential Token design: addresses are public; balances and amounts are private.

Documentation

The documentation in this repository is rendered together with crucible-prover and crucible-scenarios at https://crucible-docs-flame.vercel.app. The markdown files here are the source; the site is a build of them. Rebuilds are nightly and on demand β€” a push here does not itself trigger one β€” so a change appears on the site within a day, or immediately if the documentation-site workflow is dispatched. To change a published page, change the file that owns it in this repository.

See docs/ for the architecture, the simulator model, the environment, the state model, commitments, deterministic execution, confidential flows, fixtures, snapshots, the Soroban adapter, the testnet configuration/runner, the security model and threat model, and how this repository integrates with crucible-prover and crucible-scenarios.

docs/error-codes.md is worth calling out separately: the stable machine-readable code attached to every failure is a public interface, because consumers branch on the string rather than the message. That document lists every code, what raises it, and whether retrying the same operation could help β€” and scripts/check-error-codes.py runs in CI so the table cannot drift away from the code it describes. See SECURITY.md for how to report issues, and CONTRIBUTING.md to contribute.

License

Licensed under either of

at your option, matching crucible-prover and crucible-scenarios.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this repository by you, as defined in the Apache License, shall be dual licensed as above, without any additional terms or conditions.