feat(10-05): report BIP-32 key origin on lnd.create-psbt, and record the honest signing posture (D-07b/D-09)
With Bitcoin Core's wallet deleted, LND's PSBT round trip is the only external-signer path Archipelago has, and D-09's key-origin protection moves from Core descriptors (of which none remain) to the PSBT itself. Adds `psbt_key_origin_report(&str) -> Result<PsbtKeyOriginReport>` to lnd/wallet.rs, reporting `input_count`, `inputs_with_key_origin` and `all_inputs_have_key_origin`. An input counts as carrying key origin when either its `bip32_derivation` or `tap_key_origins` map is non-empty. A PSBT with zero inputs reports false rather than vacuous truth. Parsed with the already-present `bitcoin` and `base64` crates; no dependency added. `lnd.create-psbt` gains an additive `key_origin` object on its response and a `tracing::warn!` with the counts when key origin is missing, because that is the exact condition under which a hardware signer refuses the PSBT. Computed best-effort: a decode failure degrades to `null`, never to an error, so a user's send cannot fail because an inspection helper could not parse something. `handle_lnd_finalize_psbt` and `handle_lnd_create_raw_tx` (which deliberately auto-signs with LND's hot keys) are untouched. Three tests, with fixtures built programmatically from the `bitcoin` crate rather than pasted as opaque base64: with-derivations, without-derivations, and malformed-is-an-error-not-a-panic. KEY-03-SIGNING-POSTURE.md gains an honest per-step coverage map of the fund -> export -> sign offline -> import -> finalize -> broadcast round trip. Of six steps, only the new inspection has automated coverage; steps 1, 4, 5 and 6 have none, and there is no air-gap transport (no animated QR, no .psbt file exchange) — export/import is copy-paste of base64. Untested paths are named as untested. Records the verdict that decides whether any of this is an air gap: on a default node an external signer CANNOT meaningfully sign a PSBT from `lnd.create-psbt`, because LND holds the keys for every input it selects. Evidence: the PSBT is funded from LND's own wallet; `ensure_wallet_initialized` creates a full key-holding wallet via /v1/initwallet; the generated lnd.conf carries no `remotesigner.*` block; and a search of apps/, scripts/, core/archipelago/src and image-recipe/ for remotesigner/createwatchonly/ nochainbackend returns zero matches. No fleet node is provisioned watch-only. What ships is PSBT transport, not air-gapped custody — the gap is provisioning, not plumbing. Adds the standing honesty statement in its own subsection: Lightning channel, revocation and HTLC keys are NOT air-gappable at all. They must sign in real time to answer counterparty commitments; remote signing relocates them to a hardened host, it does not cool them. Also adds a status banner to PSBT-SIGNING-ARCHITECTURE.md recording that its Phase 1 was superseded by deletion rather than delivered, so §0's "single highest-value change" and §2.1's invariant now read against a code path that no longer exists. Banner only; §5.4's honesty table is byte-identical. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
40b77e392a
commit
262998747e
@@ -1,5 +1,33 @@
|
||||
# PSBT-First Signing Architecture
|
||||
|
||||
> ## ⚠️ Status update (2026-08-02): **§8 Phase 1 was superseded by deletion, not delivered**
|
||||
>
|
||||
> Phase 1 ("Descriptor watch-only read path", §8) planned to **rewrite**
|
||||
> `handle_bitcoin_init_wallet_from_seed` so Bitcoin Core's wallet held only the xpub. That is not
|
||||
> what happened. Under Phase 10 decision **D-07b**, the entire Bitcoin Core wallet path was
|
||||
> **deleted**: `handle_bitcoin_init_wallet_from_seed` and its `bitcoin.init-wallet-from-seed`
|
||||
> dispatch arm are gone. It had no caller, LND is the wallet the product drives, and the endpoint
|
||||
> was authenticated *and* password-gated, so F-13 was key-at-rest duplication rather than an
|
||||
> exposed endpoint.
|
||||
>
|
||||
> **Consequences for reading the rest of this document:**
|
||||
>
|
||||
> - **§0's "single highest-value change"** and **§2.1's invariant** now read against a code path
|
||||
> that no longer exists. Their goal — the BIP-84 private key existing in exactly one place —
|
||||
> is **achieved**, by removal rather than by conversion to watch-only.
|
||||
> - **§1.1, §2.2, §3.1 and §7.3** describe a Core watch-only wallet and a wallet migration.
|
||||
> **There is no such wallet and no migration was performed or is planned.**
|
||||
> - **§3.1's key-origin requirement** still holds, but it now applies to the **PSBT** rather than
|
||||
> to Archipelago-emitted descriptors, of which there are none left. `lnd.create-psbt` inspects
|
||||
> and reports it (`psbt_key_origin_report`, `core/archipelago/src/api/rpc/lnd/wallet.rs`).
|
||||
> - **§5 (LND) is unaffected and remains accurate**, including **§5.4's honesty table**, which is
|
||||
> correct as written and unchanged.
|
||||
>
|
||||
> **For the current state, read `docs/security/KEY-03-SIGNING-POSTURE.md`** — it records the
|
||||
> deletion with its evidence, an honest per-step coverage map of the LND PSBT round trip, and the
|
||||
> verdict on whether an external signer can sign a default node's PSBT today (it cannot: no fleet
|
||||
> node is provisioned watch-only). Phases 2-7 below are unaffected as design targets.
|
||||
|
||||
> **Status: specification.** No implementation. This document defines a target architecture and
|
||||
> a phased rollout that a future `/gsd-plan-phase` can consume directly. It deliberately
|
||||
> contains no code, adds no dependencies, and changes no wallet or signing behaviour.
|
||||
|
||||
Reference in New Issue
Block a user