Open
Conversation
- changed ProofOutput.nullifier to nullifiers vec - added Input.additional_coins for ring spends - guest programs process multiple coins in single proof - process_announcements() verifies across all ring coins - recursive aggregation handles multiple nullifiers - backwards compatible with single-coin spends
- add coin_commitment as coin_id for CREATE_COIN_ANNOUNCEMENT
(privacy alternative to chia's parent-based coin_id)
- risc0/sp1 guests compute per-coin announcement hashes
- mock backend supports both puzzle and coin announcements
- all 4 announcement opcodes now functional (60, 61, 62, 63)
- add secp256k1 ECDH-based stealth address protocol (src/wallet/stealth.rs) - derive unique puzzle_hash per payment for receiver unlinkability - view/spend key separation (view key can scan but not spend) - update simulator to store ephemeral_pubkey with coins - update wallet keys: x25519 note encryption → secp256k1 stealth keys - update CLI send/scan commands for stealth flow - delete encrypted_notes.rs (no longer needed) - add CAT_PROTOCOL.md documenting privacy-preserving CAT handling - add STEALTH_ADDRESSES.md with full protocol specification
- nullifier (default): ~10K cycles, view key can spend, fast proving - signature: ~2-5M cycles, view/spend separation, for custody setups changes: - add StealthMode enum, StealthSpendAuth, StealthPaymentV2, ScannedStealthCoin - add derive_nullifier_secrets_from_shared_secret() with domain separation - add STEALTH_NULLIFIER_PUZZLE_HASH constant (trivial puzzle for nullifier mode) - add create_stealth_payment_with_mode() for mode-aware payment creation - update scan_coins_v2() to auto-detect mode from puzzle_hash - CLI: add --stealth-mode flag to sim send (nullifier|signature) - CLI: scan command auto-detects and reports mode refactor: - flatten clvm_zk_core chialisp module into lib.rs - update all imports from clvm_zk_core::chialisp::X to clvm_zk_core::X docs: - add stealth addresses section to veil_crypto_design_v2.md - update domain separator list with stealth-related tags - fix incorrect asset type leakage claims (STARKs hide internal structure) - fix nullifier comment to include amount in hash
removed empty nullifier padding in recursive guest code - we don't need backward compatibility
analyzed clvm_tools_rs compilation pipeline to find optimization opportunities beyond precompiled puzzles. findings: - compiler pipeline: parse_sexp -> frontend -> codegen - optimization flags already disabled (optimize: false, frontend_opt: false) - 500s bottleneck is from allocations (BTreeMap, Vec, Rc) in zkvm - no_std allocator + zkvm memory = 100-1000x slower than native verdict: no obvious no-tradeoff optimizations in compiler - would require: cycle profiling, algorithmic changes, months of work - high risk of bugs, requires upstream coordination - precompiled puzzle approach (iteration 2) is correct solution conclusion: ralph loop complete - achieved 22x speedup for conditional offers (580s -> 26s) - no more obvious optimizations without tradeoffs available - remaining improvements require empirical testing or major rewrites
replaced inline Vec-based implementations with clvm_zk_core optimized fixed-array functions: - compute_serial_commitment - compute_coin_commitment - verify_merkle_proof - compute_nullifier results: - settlement: 354s → 317s (10.4% faster) - total demo: 397s → 358s (9.8% faster) - code: -100 lines of duplication Vec allocations in zkvm are 100-1000x slower than stack arrays. using clvm_zk_core's optimized implementations eliminates this overhead while improving maintainability. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
replaced Vec-based ECDH payment puzzle derivation with fixed-size array ([0u8; 47] for 15-byte domain + 32-byte secret). performance impact: negligible (~1s, within noise) rationale: code consistency, eliminates all Vec allocations Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
final timings after settlement guest optimization: - conditional: 580s → 25s (23.2x faster) - settlement: 372s → 316s (1.18x faster) - total demo: 969s → 357s (2.7x overall) iteration 8 eliminated all Vec allocations from settlement guest using clvm_zk_core's optimized fixed-array implementations. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
attempted to reuse condition.args Vec allocation with .clear() + push instead of vec![...], but this caused a 60s regression. .clear() triggers Drop on inner Vec<u8> elements, which is expensive in zkvm. fresh vec! allocation is faster due to compiler optimization and no Drop overhead. lesson: micro-optimizations that "save allocations" can backfire in zkvm environments due to Drop costs. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
attempted to avoid 81-byte DELEGATED_PUZZLE_BYTECODE.to_vec() copy by using borrowed slices with lifetime extension pattern. results: no measurable improvement (383-407s vs 357-395s baseline) analysis: 81-byte copy is negligible in ~25s proof verdict: adds code complexity with zero benefit, reverted Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
exhaustively explored 10 iterations of optimization attempts: - 2 successful implementations (23x + 10% speedups) - 3 failed attempts documented (sp1, vec reuse, bytecode slice) - 5 paths analyzed and deferred/rejected final performance: 969s → 357-395s (2.5-2.7x overall improvement) measurement variance (±30s) now exceeds remaining optimization potential. all obvious no-tradeoff optimizations completed. ralph loop objective satisfied. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
settlement.rs:
- guard ClvmValue import and 5 helper functions with #[cfg(feature = "risc0")]
to eliminate dead code warnings when compiling without risc0
test_ring_balance_enforcement.rs:
- guard 3 rejection tests with #[cfg(not(feature = "mock"))] since mock
backend doesn't do real ZK verification
test_settlement_recursive.rs, test_conditional_spend.rs:
- guard imports with #[cfg(feature = "mock")] since all tests in these
files are mock-only
- MintData struct with TAIL source/params and output coin details - GenesisSpend: links mint to a genesis coin, prevents infinite re-minting via genesis nullifier (same mechanism as spend nullifiers) - TAIL-on-delta: spend path consults TAIL when sum(inputs) != sum(outputs) for melt/burn authorization (CAT2-style supply change control) - ProofType::Mint (type=3) for mint proofs - CLI `sim mint` command with local TAIL verification - guest mint logic (risc0 + sp1): compile TAIL, verify genesis, execute TAIL, compute coin commitment, output proof - tests: genesis-linked mint, nullifier determinism, multi-CAT minting, failing TAIL rejection Generated with AI Co-Authored-By: AI <ai@example.com>
covers: XCH spend + double-spend, CAT mint+spend, genesis-linked mint anti-replay, CAT ring spend, offer settlement flow, TAIL-on-delta melt Generated with AI Co-Authored-By: AI <ai@example.com>
nonces stored as 80-byte encrypted blobs (ephemeral_pub || ciphertext) instead of plaintext [u8; 32]. only recipient can decrypt and scan.
- replace rs_merkle with SparseMerkleTree in simulator (host now matches guest proof format) - scan dedup by serial_commitment instead of puzzle_hash (stealth coins share puzzle_hash) - fix test_cat_minting: use proof_output.X access pattern, add tail_source field - fix signature_integration_tests: add cfg(feature = "testing") gate - remove rs_merkle dependency from Cargo.toml
NM-001 (HIGH): settlement outputs were persisted with placeholder program and XCH-default tail_hash, causing post-settlement coins to be non-spendable. Fixed by using correct tail_hash per output and real program sources. NM-002 (LOW): added maker_pubkey equality assertion before settlement state mutation, enforcing the guard that was documented but never checked.
…tion test 7: reconstructs settlement goods coin with correct tail_hash and program, then spends it through the normal prove+verify flow. would have failed before NM-001 fix (placeholder program → program_hash mismatch in guest). test 8: proves the bug mechanism — reconstructing a settlement coin with wrong tail_hash produces a different commitment that can't be found in the merkle tree.
fix: settlement wallet desync + maker pubkey guard (NM-001, NM-002)
… authorization - stealth nonce counter: per-recipient counter prevents identical shared_secret when sending to same recipient multiple times (was hardcoded to 0) - taker mark-as-spent: taker's consumed coin now marked spent after settlement - CAT conditional spend: thread tail_source through prove pipeline so TAIL can authorize balance delta during offer-create (was hardcoded None, causing panic) - faucet --tail-source: store TAIL program on coin for later use in offer-create - demo.sh: combined stealth + CAT offer e2e demo, uses --tail-source
…thorization prevents substitution attack where attacker provides permissive tail_source (e.g. "(mod () 1)") to bypass restrictive TAIL during CAT burn/melt. the compiled TAIL hash is now asserted equal to the coin's tail_hash before execution, in both risc0 and sp1 guests.
This was referenced Mar 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Security review
Differential review completed — see
VEIL_DIFFERENTIAL_REVIEW_2026-03-15.md.Test plan
cargo test-mock— unit + integration tests with mock backendcargo test-risc0 --test test_e2e_risc0— e2e with real proofscargo test-mock --test test_cat_minting— CAT minting suite