feat: BTC/LTC wallet handlers (electrum proxy, authenticated)

First wallet-handler slice. Thin, non-custodial, JWT-gated proxies over the
ElectrumClient — the wallet derives addresses and signs locally; these
endpoints only relay reads and a finalized broadcast.

Endpoints (POST, under /api/v1/wallet/utxo, asset = btc|ltc in body):
- balance  -> {confirmed, unconfirmed} (separate integers; client sums)
- utxos    -> [{txid, vout, value, height}]
- history  -> [{txid, height, fee}]
- tip      -> {height}
- fee      -> {sat_per_vb}
- broadcast-> {txid}

- JWT-gated for abuse control; the client-supplied address is NOT persisted
  against the user (no address<->identity graph stored here).
- Feature-gated dispatch: unknown/disabled asset -> 400; the Electrum client
  validates the address + network before any network call.
- broadcast validates tx_hex (non-empty, even-length, hex, <=200 KB) before
  reaching the node.
- Registered in build_router + openapi.json (6 paths, btc_ltc tag); spec
  regenerated, drift-clean.

5 L1 integration tests (auth gate on balance/broadcast/fee; unknown-asset and
malformed-tx-hex 400s — all deterministic, no network) + tx_hex unit test.
This commit is contained in:
jwinterm
2026-06-23 11:57:43 -04:00
parent b7e3199200
commit 1ec4639652
7 changed files with 1000 additions and 2 deletions
+20 -1
View File
@@ -32,6 +32,12 @@ use utoipa::OpenApi;
crate::api::users::register_key,
crate::api::users::get_user_keys,
crate::api::users::get_user_key_for_asset,
crate::api::wallet::btc_ltc::balance,
crate::api::wallet::btc_ltc::utxos,
crate::api::wallet::btc_ltc::history,
crate::api::wallet::btc_ltc::tip,
crate::api::wallet::btc_ltc::fee,
crate::api::wallet::btc_ltc::broadcast,
crate::api::nip05::well_known_nostr,
),
components(schemas(
@@ -59,11 +65,24 @@ use utoipa::OpenApi;
crate::api::users::RegisterKeyRequest,
crate::api::users::UserKeyInfo,
crate::api::users::UserKeysResponse,
crate::api::wallet::btc_ltc::AddressRequest,
crate::api::wallet::btc_ltc::BalanceResponse,
crate::api::wallet::btc_ltc::Utxo,
crate::api::wallet::btc_ltc::UtxosResponse,
crate::api::wallet::btc_ltc::HistoryEntry,
crate::api::wallet::btc_ltc::HistoryResponse,
crate::api::wallet::btc_ltc::AssetRequest,
crate::api::wallet::btc_ltc::TipResponse,
crate::api::wallet::btc_ltc::FeeRequest,
crate::api::wallet::btc_ltc::FeeResponse,
crate::api::wallet::btc_ltc::BroadcastRequest,
crate::api::wallet::btc_ltc::BroadcastResponse,
crate::api::nip05::WellKnownResponse,
)),
tags(
(name = "system", description = "Service health and metadata."),
(name = "auth", description = "Wallet authentication: registration, restore, refresh, sessions, Nostr identity.")
(name = "auth", description = "Wallet authentication: registration, restore, refresh, sessions, Nostr identity."),
(name = "btc_ltc", description = "Bitcoin/Litecoin chain access via Electrum (balance, UTXOs, history, tip, fee, broadcast).")
)
)]
pub struct ApiDoc;