# Test PSBTs — how to generate them The harness needs **two pre-crafted PSBTs**: | Fixture | Amount | Policy path expected | |------------------|------------------|------------------------------| | `small.psbt` | ≤ auto-approve cap (e.g. 9,000 sats if your Rule #2 cap is 10,000) | Signs without TOTP | | `large.psbt` | > auto-approve cap, ≤ user-auth cap (e.g. 100,000 sats) | Rejected without TOTP; signs with TOTP | Both PSBTs must: - be **spendable by the Coldcard** bound to your CKBunker (same seed / xpub) - spend to **an address you control** (or a burn address — they are test inputs, you never broadcast them) - use a real UTXO the Coldcard can see (watch-only wallet) --- ## Method 1 — Sparrow Wallet (recommended for first-time setup) 1. In Sparrow, open or create a **watch-only wallet** loaded with your Coldcard's xpub. (The Coldcard's HSM-Mode QR or a `coldcard.txt` export works.) 2. Send yourself a small amount on testnet **or** signet so you have a UTXO to spend without losing real sats. (For mainnet demos, 10k sats is ~AUD $1.) 3. Build two transactions: - `Small demo` — pay **9,000 sats** (or 90% of your Rule #2 per-txn cap) to any receive address in the same wallet. Sparrow → Send → *Save PSBT* → write to `fixtures/small.psbt`. - `Large demo` — pay **100,000 sats** (or mid-range of your Rule #1 cap) the same way. Save as `fixtures/large.psbt`. 4. Both PSBTs should show **Coldcard as a required signer** in Sparrow. > Do NOT broadcast these. The harness signs them, but you verify the > signatures in Sparrow and then discard — there's no reason to spend real > sats on a validation run. --- ## Method 2 — bitcoind (CI / automation) If you're wiring the harness into CI against a regtest or signet deployment, scripting PSBT generation is a one-off: ```bash #!/usr/bin/env bash # Requires bitcoin-cli on PATH, pointed at a node that sees your wallet. set -euo pipefail WALLET="ckbunker-watch" FEE_RATE=10 # sat/vB recipient=$(bitcoin-cli -rpcwallet=$WALLET getnewaddress) small_raw=$(bitcoin-cli -rpcwallet=$WALLET walletcreatefundedpsbt \ '[]' "[{\"$recipient\":0.00009000}]" 0 \ "{\"fee_rate\":$FEE_RATE}" | jq -r '.psbt') echo "$small_raw" | base64 -d > fixtures/small.psbt large_raw=$(bitcoin-cli -rpcwallet=$WALLET walletcreatefundedpsbt \ '[]' "[{\"$recipient\":0.00100000}]" 0 \ "{\"fee_rate\":$FEE_RATE}" | jq -r '.psbt') echo "$large_raw" | base64 -d > fixtures/large.psbt ``` --- ## Method 3 — use the same PSBT file over and over Nothing in the harness requires the PSBT to be spendable *right now* for the reject-path test (`test_04`). The Coldcard rejects on **amount**, not on whether the UTXO is still unspent. So: - `small.psbt` can be reused until the UTXO is spent elsewhere. - `large.psbt` can be reused indefinitely — every validation run that tests Rule #1 rejection produces a rejection regardless of UTXO state. If you run the full suite frequently, consider crafting `large.psbt` deliberately against an **already-spent UTXO** so the success path (`test_05`) fails at signature verification (not policy evaluation) — this is arguably safer than running with signable funds live. --- ## File format Either **binary** (`psbt\xff...` magic bytes) or **base64**-encoded text is accepted by the harness — it auto-detects via magic bytes. Sparrow exports binary by default; bitcoin-cli returns base64. --- ## What NOT to do - Do not commit real PSBTs to git — `.gitignore` already blocks `*.psbt` in this directory. - Do not use a PSBT that spends a UTXO you can't afford to move. The harness does not broadcast, but a leaked signed PSBT *can* be broadcast by anyone. - Do not reuse production keys for generating fixtures — prefer testnet or signet.