Waterfalls is a Rust project providing blockchain data to Liquid and Bitcoin light-client wallets.
Use Nix (defined in flake.nix): nix develop or direnv allow.
Provides: Rust toolchain (via rust-overlay), RocksDB, OpenSSL, bitcoind, elementsd, libclang.
When the nix env is not already active (e.g. sandbox), prefer direnv exec . <command>
(e.g. direnv exec . cargo check) over nix develop --command <command> — it uses the
cached nix-direnv environment and avoids flake re-evaluation overhead.
cargo build # Debug build
cargo build --release # Release build
cargo check # Fast type-check
cargo check --no-default-features # Check without default features
cargo check --no-default-features --features test_env
cargo check --benches
cargo check --testscargo test # Run all tests (uses default features: test_env, db)
cargo test test_name # Run a single test by name
cargo test test_name -- --exact # Run exactly one test (no substring match)
cargo test -- --nocapture # Show stdout/stderr
cargo test -- --ignored # Run ignored tests (require internet)
cargo test --test integration # Run only integration tests
cargo bench --features bench_test # Run benchmarks (criterion)test_env(default) — enablesbitcoinddep for tests requiring local nodesdb(default) — enables RocksDB storage backendsynced_node— tests requiring a locally running synced nodebench_test— long-running benchmark testsexamine_logs— log inspection tests
export BITCOIND_EXEC=/path/to/bitcoind # Provided by nix develop
export ELEMENTSD_EXEC=/path/to/elementsd # Provided by nix develop
export RUST_LOG=debug # Optional: enable debug loggingcargo fmt # Format code (default rustfmt settings)
cargo clippy # Lint
cargo clippy -- -D warnings # Lint, fail on warnings (CI enforced)No rustfmt.toml or clippy.toml — default settings are used.
Grouped in this order (separated by blank lines when practical):
std::— standard library- External crates —
anyhow,elements,hyper,serde,tokio, etc. crate::/super::— internal modules
Use absolute paths: use waterfalls::be::Address (in tests/benches), use crate::be::Address (within the crate).
- Application-level:
anyhow::Resultfor fallible operations in fetch, threads, startup. - Server routes: custom
Errorenum insrc/server/mod.rsmapped to HTTP status codes.CannotDecrypt→ 422,BodyTooLarge→ 413,BodyReadTimeout→ 408, input errors → 400, others → 500.
- Fetch layer: custom
Errorenum insrc/fetch.rs(TxNotFound,BlockNotFound, etc.). - Unrecoverable:
error_panic!macro (defined insrc/lib.rs) — logs vialog::error!then panics. - Log errors with
log::error!before returning them.
- Use the
logcrate:log::info!,log::warn!,log::error!,log::debug! - Initialized via
env_loggerinmain.rs(default filter:info) - For systemd integration: set
RUST_LOG_STYLE=SYSTEMD
- Runtime:
tokiowithrt-multi-thread - Use
tokio::select!for concurrent operations (e.g., signal handling) - Use
#[tokio::test]for async tests
- JSON:
serde/serde_jsonwithSerialize/Deserializederives - CBOR:
minicborwithEncode/Decodederives and customwithhelpers insrc/cbor.rs - Field annotations:
#[cbor(n(X))]for CBOR field indices,#[serde(skip_serializing_if = ...)]
#[tokio::test]for async tests#[cfg(feature = "test_env")]gates tests needing bitcoind/elementsd#[cfg(all(feature = "test_env", feature = "db"))]for DB-backed integration tests#[ignore = "requires internet"]for tests hitting remote endpointsenv_logger::try_init()at test start (ignore the error if already initialized)- Test infrastructure in
src/test_env.rs:TestEnv,WaterfallClient,launch(),launch_with_node() - Integration tests in
tests/integration.rsuselaunch_memory()/test_env::launch()to spin up node + server
src/
├── lib.rs # Library root: types, error_panic! macro, prometheus metrics
├── main.rs # Binary entry: clap parsing, logging, signal handling
├── fetch.rs # Blockchain data fetching (esplora / local node REST)
├── cbor.rs # CBOR encoding helpers for block hashes
├── test_env.rs # Test utilities (TestEnv, WaterfallClient)
├── be/ # Backend types (Address, Block, BlockHeader, Descriptor, Tx, Txid)
├── server/ # HTTP server: Arguments (clap), Network, Error enum, routing,
│ # state, mempool, signing, encryption, derivation_cache, preload
├── store/ # Store trait + AnyStore, memory.rs, db.rs (RocksDB, behind `db` feature)
└── threads/ # Background tasks: block indexing, mempool sync
build.rs # Injects GIT_COMMIT_HASH at build time
tests/integration.rs # Integration tests
benches/benches.rs # Criterion benchmarks
Runs on push/PR to master:
- tests: downloads bitcoind 28.0 & elementsd 23.2.4, runs
cargo testandcargo test -- --ignored - checks:
cargo checkwith various feature combinations - nix:
nix build .with cachix
From .cursor/rules/my-custom-rule.mdc (always applied):
- The developer uses a Nix environment from
flake.nix— use it when proposing commands - Add new structs at the end of files, or just before
#[cfg(test)] mod testsif present - Never add new dependencies unless explicitly asked
cargo run -- --network liquid --use-esplora # Run server against esplora
cargo test --features "test_env db" # Run tests with DB backend
nix build .#dockerImage && docker load < result # Build Docker image
cargo bench --features bench_test # Run benchmarks