Skip to content

[feature]: Support external and NUMS internal keys for custom genesis anchors #2237

Description

@SadiqJaf

Summary

Follow-up to #721 after its custom genesis PSBT support is merged.

#721 enables caller-selected genesis inputs, output indexes and values, external PSBT finalization, and a batch internal key that is independent of the batch key. Its safe initial implementation requires the asset anchor internal key to be owned by the backing lnd wallet so tapd can validate and persist a real key descriptor.

The remaining capability is to support anchor internal keys that are deliberately not wallet-owned, including a NUMS key used to force script-path spending when the committed sibling is, for example, a 2-of-2 multisig. This was part of the additional requirements described in the #721 discussion.

Current limitation

A non-wallet or NUMS internal key has no valid lnd key locator/private key. Treating it as an ordinary managed key, synthesizing a locator such as 0/0, or substituting the batch key would make later output reconstruction, signing, backup and recovery unsafe.

The existing #721 wallet-owned path should remain unchanged. This issue should introduce an explicit custody/ownership model for externally controlled or script-path-only anchors.

Desired behavior

  • Allow a custom genesis anchor PSBT to declare an external or NUMS Taproot internal key.
  • Validate that the selected anchor output script exactly commits to that internal key, the asset commitment root, and any declared tapscript sibling/tree.
  • Preserve the caller's PSBT metadata and externally finalized transaction exactly across Prepare, Finalize, retries, and restart.
  • Store enough typed information to reconstruct proofs and the anchor output without inventing a wallet key descriptor.
  • Define how externally controlled/script-path-only anchor outputs are imported, monitored, backed up, recovered and later spent.
  • Keep the wallet-owned internal-key mode from [feature]: Enhance caretaker w/ ability to set user-defined UTXOs and GenesisAmtSats values #721 as the default and fully backward compatible.
  • Fail closed before publication when the declared ownership mode, internal key, script commitment, or required external-spend metadata is inconsistent.

Suggested design boundary

Use an explicit anchor-key ownership/type distinction rather than overloading KeyDescriptor, for example:

No external/NUMS path should insert a placeholder locator into internal_keys or allow code that assumes tapd/lnd can produce a key-path signature.

Acceptance criteria

  • A caller can provide a custom genesis PSBT whose selected anchor uses a declared external or NUMS internal key.
  • Prepare validates the exact Taproot output commitment and pauses for external signing without mutating caller-controlled PSBT fields.
  • Finalize validates the complete externally signed transaction before any import/publication side effect.
  • Pending, Frozen, Committed and Broadcast restart behavior is deterministic and preserves the exact transaction and ownership mode.
  • Minting proofs contain the exact declared internal key and reconstruct the confirmed anchor output script.
  • A supported script-path spend of the minted anchor can be completed with the declared external signer/script data, or the API explicitly records and enforces a non-spendable-by-tapd custody contract.
  • Backup/recovery behavior for external and NUMS anchors is defined and tested.
  • No fake/default key locator is persisted, and wallet-owned locator records cannot be overwritten or downgraded.
  • Malformed metadata, unavailable external-spend data, and key/script mismatches fail closed.
  • Existing default minting and [feature]: Enhance caretaker w/ ability to set user-defined UTXOs and GenesisAmtSats values #721 wallet-owned custom-anchor flows remain unchanged.
  • Stateful tests cover same-process and restart behavior, proof reconstruction, and a post-mint script-path spend.

Out of scope

Do not reopen the caller-selected input, output value/index, lease, external funding-signature, or immutable publication lifecycle delivered by #721. This issue is specifically about the anchor internal key ownership/custody boundary.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    Status
    🆕 New

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions