Inspiration

Most Bitcoin wallets pick UTXOs to only minimise fees. But which coins you spend reveals information: chain-analysis firms use the Common Input Ownership Heuristic (CIOH) to link addresses, and a single careless transaction can merge a KYC'ed exchange UTXO with a private coinjoined one, doxxing your entire wallet history. We wanted to make the tradeoff visible rather than hidden.

What it does

UTXO Planner takes a Bitcoin wallet snapshot and a spending task (pay, consolidate, or wait) and recommends how to spend across three strategies:

  • Cheapest — minimises transaction fee using exact-match (no change output) selection before falling back to a minimum-fee subset search
  • Most Private — never spends protected-cluster or high-sensitivity coins; prefers single-cluster, single-script-type inputs with no change output
  • Balanced — exhaustively scores all input subsets and picks the one with the lowest combined fee + privacy + fragmentation score

Each result shows a full score breakdown with ASCII progress bars, a pros/cons/warnings explanation in plain English, and a side-by-side comparison table so the user can make an informed choice.

How we built it

Pure Python CLI with five cleanly separated modules: scorer.py (fee/privacy/fragmentation math), selector.py (three strategies), explainer.py (human-readable rationale), demo_wallets.py (realistic test scenarios), and cli.py (Rich terminal UI + JSON output). The scoring model is fully documented with penalty weights derived from Bitcoin privacy literature. We wrote 66 unit tests covering every scoring component, all three strategies, oracle scenario validation, and edge cases like all-frozen wallets and zero feerate.

Challenges we ran into

The hardest part was getting resolve_change() right. When a UTXO set covers the target exactly (no change), the intermediate calculation produces a negative "change" value. Naively returning None here would reject a valid exact-cover selection. We had to restructure the logic to fall through to a no-change feasibility check. Similarly, select_cheapest initially used greedy-largest-first, which preferred large P2WPKH UTXOs over smaller P2TR ones even though P2TR inputs are cheaper in vbytes. We fixed this by iterating all subset sizes and picking the minimum-fee selection at each size.

Accomplishments that we're proud of

Building a multi-dimensional scoring model grounded in real Bitcoin protocol constants (BIP-141 dust limit, per-script-type vbyte tables, segwit weight discount) and privacy research. Every penalty weight has a documented rationale. The privacy hard constraints (frozen and protected-cluster UTXOs are never spent, across all strategies, with test coverage) also hold as invariants, not just guidelines.

What we learned

Bitcoin UTXO management is a deep optimisation problem with three competing objectives that don't reduce to a single scalar. A single transaction leaks much information — script type uniformity, change address patterns, and input count all leave fingerprints that chain-analysis tools actively exploit.

What's next for UTXO Planner

Connect to Bitcoin Core via RPC or parse descriptor wallets to load live UTXO sets.

Built With

Share this project:

Updates