π₯ Crucible Simulator
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
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.
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:
| Repository | Layer | Responsibility |
|---|---|---|
crucible-simulator | SIMULATE | reproduce Confidential Token flows and state transitions |
crucible-prover | PROVE | generate and verify mock and real cryptographic proofs |
crucible-scenarios | STRESS-TEST | execute conformance, failure, and adversarial scenarios |
crucible-docs | READ | the 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
| Concept | Where | Notes |
|---|---|---|
| Account | core | Synthetic identities (Alice/Bob/β¦), registration + permission state |
| Token | core | Native test asset and configured confidential tokens |
| Commitment | core/state | Deterministic digest over (owner, token, value, nonce); Active/Consumed lifecycle |
| Balance | core | Observable anchor (commitment) vs private simulation value |
| Operation | core | The five lifecycle operations as one enum |
| Transaction | core | Deterministic-ID record of every operation β Success/Rejected/Failed with the error embedded |
| Event | core | Value-free observable output (success + operation_rejected) |
| StateStore | state | BTree-backed deterministic store with a state-root digest |
| Nullifier | state | Consumed-commitment registry; replay protection |
| Transition | state | Explicit State0 β Transition β State1, audited on record |
| Snapshot | state | Named full-state captures with exact restore |
| ProofProvider | flows | The interface crucible-prover will implement; mock today |
| Simulator | simulator | The 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/determinismcompares full transcripts across runs. Snapshots let you capture state, run operations, restore, and rerun the exact sequence β the debugging workflowcrucible-scenariosrelies 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
- Apache License, Version 2.0 (
LICENSE-APACHE) - MIT license (
LICENSE-MIT)
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.

