Transaction Flow
zkCoins supports three operations: create account (mint), send, and receive. All operations interact with the Rust backend, which manages account state and publishes nullifiers to Bitcoin.
Create Account (Mint)
Account creation generates a new HD wallet and mints initial testnet coins.
Browser (WASM) Backend Bitcoin
│ │ │
│ Generate BIP32 master key │ │
│ Derive address from pubkey[0] │ │
│ Store account in localStorage │ │
│ │ │
│ POST /api/mint {address} │ │
│─────────────────────────────────>│ │
│ │ │
│ │ Create CoinProof │
│ │ Generate SP1 proof │
│ │ Publish commitment │
│ │──────────────────────────>│
│ │ │
│ │ Taproot Inscription │
│ │ (64-byte nullifier) │
│ │ │
│ {proof_id} │ │
│<─────────────────────────────────│ │
Steps:
- WASM module generates a random 256-bit seed
- BIP32 master key (Xpriv) is derived
- Address is derived from the first public key, blinded with random bytes for privacy
- Account is stored in localStorage:
{address, numPubkeys, xpriv} - Backend mints initial coins from its minting account
- Commitment is published as a Bitcoin Taproot Inscription
Send Coins
Sending transfers coins from one account to another with a Zero-Knowledge proof.
Browser Backend Bitcoin
│ │ │
│ POST /api/send │ │
│ {sender, recipient, amount, │ │
│ sender_public_key, │ │
│ sender_next_public_key} │ │
│─────────────────────────────────>│ │
│ │ │
│ │ 1. Verify balance │
│ │ 2. Collect received coins│
│ │ 3. Get Merkle proofs │
│ │ 4. Build SP1 inputs │
│ │ 5. Generate ZK proof │
│ │ 6. Create output coins │
│ │ 7. Publish commitment │
│ │──────────────────────────>│
│ │ │
│ {proof_id} │ │
│<─────────────────────────────────│ │
│ │ │
│ Increment numPubkeys │ │
│ Update localStorage │ │
Steps:
- Browser sends transfer request with sender/recipient addresses and amount
- Backend verifies the sender has sufficient balance
- Backend collects unspent coins from the sender's queue
- Merkle proofs are fetched for all input coins (Sparse Merkle Tree + MMR)
- SP1 circuit generates a recursive Zero-Knowledge proof
- Output coins are created: one for the recipient, one for change (if needed)
- Commitment is published as a Bitcoin Taproot Inscription
- Recipient's coin proof is stored for later retrieval
Key Rotation
Each transaction rotates the account's public key:
- The sender provides
sender_public_key(current) andsender_next_public_key(next) - Both are derived from the BIP32 master key at sequential indices
- The SP1 circuit commits to the key rotation
- This ensures forward secrecy — compromising a past key doesn't help an attacker
Blockchain Scanner
The backend runs a continuous scanner that:
- Polls Bitcoin every 30 seconds for new blocks
- Filters transactions by the marker prefix (
4242) - Extracts Taproot Inscription data from witness
- Deserializes and verifies Schnorr signatures on commitments
- Updates the Sparse Merkle Tree and Merkle Mountain Range