Testing with Mock Services
When developing a delta domain, you'll want to test your implementation without connecting to the live network or generating expensive cryptographic proofs. There are several mock services that let you develop and test your domain locally.
Overview​
The domain SDK provides mock implementations for three key components:
| Component | Mock Implementation | Use Case |
|---|---|---|
| Storage | In-Memory Storage | Ephemeral storage for tests |
| Base Layer RPC | Mock RPC | Simulates base layer validator RPC without network |
| Proving | Mock Proving Client | Validates logic without generating proofs |
All three can be configured in your domain.yaml file. By default, the generic
domain uses the mock RPC and mock proving.
Configuration File​
A minimal mock configuration requires shard and keypair. We also set
storage: in_memory to ensure ephemeral test storage (see In-Memory
Storage for why):
shard: 123
keypair: ./path/to/key.json
# Mock storage (in-memory)
storage: in_memory
# Mock RPC (omit rpc_url to use mock)
# Mock proving client
proving:
global_laws:
type: mock
These components can also be configured programmatically via
Domain::builder using methods like with_mock_rpc,
with_storage, and with_proving_client.
In-Memory Storage​
In-memory storage keeps all vault data and domain state in RAM. Data is lost when the process exits. This is ideal for testing because each test run starts with a clean slate, there's no I/O overhead, and you don't need to clean up database files between runs.
storage: in_memory
If the storage key is omitted from the configuration file, the default storage
depends on the rocksdb cargo feature. The generic domain has this feature
enabled, and therefore always uses RocksDB storage. In-memory storage is not
supported on the generic domain. However, running the generic domain as a docker
image with docker run --rm has the same effect of purging the storage on exit.
For production, use RocksDB and configure the persistent storage's path:
storage:
rocksdb:
path: /var/lib/my-domain/data
If you're building your own domain in Rust without the rocksdb feature,
in-memory storage is the default. Use with_storage on the DomainBuilder to
configure it programmatically.
Mock Base Layer (RPC)​
The mock RPC simulates a validator RPC without requiring network connectivity. It maintains an in-memory state of vaults, SDLs, and proofs.
If rpc_url is omitted in your configuration file, a mock RPC is used. For
production, add your RPC endpoint:
rpc_url: https://rpc.delta.network
To configure it programmatically, use with_rpc or with_mock_rpc on the
DomainBuilder.
Pre-populating Vaults​
When testing transaction flows, your domain needs access to vaults with balances to debit from. Since the mock RPC doesn't connect to a real network, you need to pre-populate vaults programmatically in code:
use delta_domain_sdk::{
base::vaults::{Address, Vault},
Config, Domain,
};
use std::collections::HashMap;
let config = Config::load()?;
// Create a vault with an initial balance
let user_keypair = crypto::ed25519::PrivKey::generate();
let user_address = Address::new(user_keypair.pub_key().owner(), config.shard.get());
let mut vault = Vault::new(config.shard);
vault.set_balance(1_000_000); // 1 million native token Plancks 🤑
// Configure the domain with pre-populated vaults
let Domain { runner, client, views } = Domain::from_config(config)
.with_mock_rpc(HashMap::from([
(user_address, vault),
// ... add more vaults as needed
]))
.build()
.await?;
Simulating Base Layer Events​
When connected to a real validator network, your domain receives events from the base layer (e.g. when proofs are finalized or the epoch changes). To test how your domain reacts to such events, you can simulate them:
use delta_domain_sdk::base::{
events::BaseLayerEvent,
sdl::{SdlStatus, SdlUpdate},
vaults::Address,
};
// Simulate a vault migrating into your domain
let address = Address::new(some_owner_id, shard.get());
client.mock_base_layer_event(Ok(
BaseLayerEvent::VaultImmigrated(address)
)).await?;
// Simulate an SDL being fully proven on the base layer
client.mock_base_layer_event(Ok(
BaseLayerEvent::SdlUpdate(SdlUpdate {
id: sdl_id,
originator: shard.get(),
status: SdlStatus::FullyProven,
})
)).await?;
Mock Proving Client​
The mock proving client validates your transaction logic without generating cryptographic proofs. It runs the same validation code that would run inside the zkVM but skips the expensive proof generation. This lets you catch logic errors quickly during development.
When proving is omitted from your configuration file, the domain uses a mock
proving client that only validates global laws. This is equivalent to setting:
proving:
global_laws:
type: mock
To test your local laws with the mock client, you need to add them programmatically:
use delta_domain_sdk::{proving::mock, Config, Domain};
let config = Config::load()?;
let Domain { runner, client, views } = Domain::from_config(config)
.with_proving_client(
mock::Client::global_laws()
.with_local_laws::<MyLocalLaws>()
)
.build()
.await?;
For more details on implementing and testing local laws, see the Implementing Local Laws guide.
Transitioning to Production​
When you're ready to move from testing to production, you'll need to replace the mock services with real implementations.
1. Replace In-Memory Storage with RocksDB​
Configure the RocksDB persistent storage:
storage:
rocksdb:
path: /var/lib/my-domain/data
If you're building your own domain in Rust, make sure to enable the rocksdb
cargo feature:
[dependencies]
delta_domain_sdk = { version = "...", features = ["rocksdb"] }
2. Replace Mock RPC with Real RPC​
Set rpc_url in your domain.yaml file or programmatically connect to a real
base layer network:
let domain = Domain::builder(shard, keypair)
.with_rpc("https://rpc.delta.network")
.build()
.await?;
3. Replace Mock Proving with SP1​
Switch to the SP1 proving client for cryptographic proofs. Configure the
proving backend in your domain.yaml. For local CPU-based proving:
proving:
global_laws:
type: sp1
backend:
type: cpu
mode: compressed
Other backends include the Succinct proving network and cuda for
GPU-accelerated proving. See the SP1 client API docs for all
options.
If you're building your own domain in Rust, make sure to enable the sp1
cargo feature:
[dependencies]
delta_domain_sdk = { version = "...", features = ["rocksdb", "sp1"] }
To configure the proving client programmatically, use with_proving_client on
the DomainBuilder.
4. Ensure Domain Agreement​
Before your domain can productively run and connect to the base layer, it needs
a valid Domain Agreement. The generic domain handles this automatically. If
you're building your own domain in Rust use
DomainClient::ensure_domain_agreement.