BTC/LTC Electrum/Fulcrum client, reworked clean from the chain-audit
playbook (not ported). Every byte from a server is treated as hostile.
Security:
- TLS verified against the webpki root store WITH hostname checking; the
accept-any-certificate verifier is gone (closes the MITM hole). The
connector is built once and reused.
- Addresses decoded via the `bech32` crate (full checksum + bech32m +
witness version) and base58check (checksum); the hand-rolled bech32
underflow panic is removed. Coin/network enforced (reject ltc-on-btc,
testnet-on-mainnet) — no byte-indexing of untrusted input.
- Amounts pass a fallible checked f64->sat converter (rejects
non-finite/negative/>2^53) with checked_add summation; no silent
saturation to 0 / u64::MAX. estimatefee requires finite, clamps.
- Bounded response read + per-exchange timeout (no OOM, no slowloris).
- JSON-RPC response id correlated with the request.
- Untrusted upstream text is control-stripped + length-capped before it
reaches a (privately-logged) NodeError.
Single `call` primitive centralizes failover + id + envelope mapping;
public methods (balance/utxos/history/tip/verbose-tx/broadcast/fee) are
thin wrappers. Tip-attribution helpers dropped; total_sent_from now
declines (None) without resolved prevouts instead of mislabeling a
receive-with-change as a send.
20 L2 tests: address->script KATs (P2WPKH/P2WSH/taproot/legacy), network
rejection, panic-safety on short/garbage input, checked-sat bounds,
envelope id correlation, bounded/oversized read, duplex round-trip.
- minimal Axum server exposing GET /health
- OpenAPI contract generated from handlers (gen_openapi -> openapi.json),
drift-gated in CI so the spec can never silently diverge from the code
- CI: secret-scan + gitleaks, openapi drift gate, fmt/clippy/build/test/doc-test, cargo-deny
- Cargo.lock tracked for reproducible builds; publish=false; pinned toolchain
- README, SECURITY.md, deny.toml, .env.example