Skip to main content

Testing

The SDK includes a built-in simulator for testing transactions without connecting to a real network. This enables fast, deterministic testing of your Chia applications.

Overview​

The chia-sdk-test crate provides:

  • Simulator - An in-memory blockchain for validation
  • Test utilities - Helpers for creating keys and coins
  • Spend validation - Verify transactions before broadcast

Setting Up the Simulator​

use chia_wallet_sdk::prelude::*;

// Create a new simulator instance
let mut sim = Simulator::new();

The simulator starts with an empty state. You'll need to create coins before you can spend them.

Creating Test Keys and Coins​

The simulator provides helpers for creating BLS key pairs with funded coins:

// Create a key pair with a coin worth 1000 mojos
let alice = sim.bls(1_000);

// alice contains:
// - alice.pk: PublicKey
// - alice.sk: SecretKey
// - alice.puzzle_hash: Bytes32
// - alice.coin: Coin

// Create multiple test identities
let alice = sim.bls(1_000_000);
let bob = sim.bls(500_000);
let charlie = sim.bls(0); // No initial funds

Validating Transactions​

After building a transaction, validate it with the simulator:

let ctx = &mut SpendContext::new();

// Build your transaction
let conditions = Conditions::new()
.create_coin(bob.puzzle_hash, 900, Memos::None)
.reserve_fee(100);

StandardLayer::new(alice.pk).spend(ctx, alice.coin, conditions)?;

// Extract spends and validate
let coin_spends = ctx.take();
sim.spend_coins(coin_spends, &[alice.sk])?;

If the transaction is invalid, spend_coins returns an error describing the failure.

Simulator State​

The simulator maintains blockchain state:

// Check if a coin exists and is unspent
let coin_state = sim.coin_state(coin_id);

// The simulator tracks:
// - Created coins
// - Spent coins
// - Current block height

Testing Patterns​

Basic Transaction Test​

#[test]
fn test_simple_transfer() -> Result<()> {
let mut sim = Simulator::new();
let ctx = &mut SpendContext::new();

let alice = sim.bls(1_000);
let bob_puzzle_hash = sim.bls(0).puzzle_hash;

let conditions = Conditions::new()
.create_coin(bob_puzzle_hash, 900, Memos::None)
.reserve_fee(100);

StandardLayer::new(alice.pk).spend(ctx, alice.coin, conditions)?;
sim.spend_coins(ctx.take(), &[alice.sk])?;

Ok(())
}

Testing CAT Operations​

#[test]
fn test_cat_issuance() -> Result<()> {
let mut sim = Simulator::new();
let ctx = &mut SpendContext::new();

let alice = sim.bls(1_000);
let p2 = StandardLayer::new(alice.pk);
let memos = ctx.hint(alice.puzzle_hash)?;

// Issue CAT
let conditions = Conditions::new()
.create_coin(alice.puzzle_hash, 1_000, memos);

let (issue_cat, cats) = Cat::issue_with_coin(
ctx,
alice.coin.coin_id(),
1_000,
conditions,
)?;

p2.spend(ctx, alice.coin, issue_cat)?;
sim.spend_coins(ctx.take(), &[alice.sk])?;

// Verify CAT was created
assert_eq!(cats.len(), 1);
assert_eq!(cats[0].coin.amount, 1_000);

Ok(())
}

Testing Invalid Transactions​

#[test]
fn test_insufficient_funds_fails() {
let mut sim = Simulator::new();
let ctx = &mut SpendContext::new();

let alice = sim.bls(1_000);

// Try to create more than we have
let conditions = Conditions::new()
.create_coin(alice.puzzle_hash, 2_000, Memos::None);

StandardLayer::new(alice.pk)
.spend(ctx, alice.coin, conditions)
.unwrap();

// This should fail
let result = sim.spend_coins(ctx.take(), &[alice.sk]);
assert!(result.is_err());
}

Testing Multi-Spend Transactions​

#[test]
fn test_multi_spend() -> Result<()> {
let mut sim = Simulator::new();
let ctx = &mut SpendContext::new();

let alice = sim.bls(1_000);
let bob = sim.bls(500);
let charlie_ph = sim.bls(0).puzzle_hash;

// Alice sends 900
StandardLayer::new(alice.pk).spend(
ctx,
alice.coin,
Conditions::new().create_coin(charlie_ph, 900, Memos::None),
)?;

// Bob pays the fee
StandardLayer::new(bob.pk).spend(
ctx,
bob.coin,
Conditions::new()
.create_coin(bob.puzzle_hash, 400, Memos::None)
.reserve_fee(100),
)?;

sim.spend_coins(ctx.take(), &[alice.sk, bob.sk])?;

Ok(())
}

Organizing Test Code​

Test Module Structure​

#[cfg(test)]
mod tests {
use super::*;
use chia_wallet_sdk::prelude::*;

#[test]
fn test_feature_a() -> Result<()> {
// ...
}

#[test]
fn test_feature_b() -> Result<()> {
// ...
}
}

Reusable Test Helpers​

#[cfg(test)]
mod tests {
use super::*;

fn setup_test() -> (Simulator, SpendContext, BlsPairWithCoin) {
let sim = Simulator::new();
let ctx = SpendContext::new();
let alice = sim.bls(1_000_000);
(sim, ctx, alice)
}

#[test]
fn test_with_helper() -> Result<()> {
let (mut sim, ctx, alice) = setup_test();
let ctx = &mut ctx;
// ...
}
}

Limitations​

The simulator is designed for transaction validation, not full blockchain simulation:

  • No mempool simulation
  • No block timing
  • No network latency
  • Simplified fee handling

For integration testing against real network behavior, use testnet.

API Reference​

For the complete testing API, see chia-sdk-test on docs.rs.