BOLT 12: first draft of payer proofs#1295
BOLT 12: first draft of payer proofs#1295rustyrussell wants to merge 1 commit intolightning:masterfrom
Conversation
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
jkczyz
left a comment
There was a problem hiding this comment.
Some minor comments from a first pass. Should have more feedback once we attempt to implement this.
| * [`bip340sig`:`sig`] | ||
| 1. type: 242 (`preimage`) | ||
| 2. data: | ||
| * [`32*byte`:`premage`] |
|
|
||
| The non-signature elements of a payer proof are identical to the | ||
| `invoice` tlv_stream, with the exception that `invreq_metadata` cannot | ||
| be included. Various fields are omitted for privacy: numbers |
|
|
||
| A writer of a payer_proof: | ||
| - MUST NOT include `invreq_metadata`. | ||
| - MUST include `invreq_payer_id`, `invoice_payment_hash`, `invoice_node_id`, `signature` and (if present) `invoice_features` from the invoice. |
There was a problem hiding this comment.
Could you include a rationale for requiring invoice_features?
| - `omitted_tlvs` contains 0. | ||
| - `omitted_tlvs` contains signature TLV element number (240 through 1000 inclusive). | ||
| - `omitted_tlvs` contains the number of an included TLV field. | ||
| - `omitted_tlvs` contains more than one number larger than the largest included non-signature TLV element. |
There was a problem hiding this comment.
Not sure I understand this restriction. Why can't more than one such TLV be omitted? The example contradicts this (41 and 42), IIUC.
There was a problem hiding this comment.
Oops, this was from an earlier draft, and should be removed!
|
|
||
| Note that the signature TLV 250 is not included in the merkle tree. | ||
|
|
||
| `leaf_hashes` contains the nonce hashes for the present non-signature TLVS: |
| 1. type: 250 (`payer_signature`) | ||
| 2. data: | ||
| * [`bip340sig`:`sig`] | ||
| * [`...*utf8`:`note`] |
There was a problem hiding this comment.
Presumably, this may be empty?
Implements the payer proof extension to BOLT 12 as specified in lightning/bolts#1295. This allows proving that a BOLT 12 invoice was paid by demonstrating possession of the payment preimage, a valid invoice signature, and a payer signature. Key additions: - Extend merkle.rs with selective disclosure primitives for creating and reconstructing merkle trees with partial TLV disclosure - Add payer_proof.rs with PayerProof, PayerProofBuilder, and UnsignedPayerProof types for building and verifying payer proofs - Support bech32 encoding with "lnp" prefix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
vincenzopalazzo
left a comment
There was a problem hiding this comment.
Thanks for this spec! I am finishing to implement it in ldk and have some feedback from implementation experience:
- The nonce hash notation needs clarification (see inline comment)
- Test vectors would be extremely valuable for cross-implementation compatibility. Wondering if you already had some draft implementation where we can compare the tests vectors?
Happy to provide my implementation's test vectors once the ambiguities are resolved.
| - For each non-signature TLV in the invoice in ascending-type order: | ||
| - If the field is to be included in the payer_proof: | ||
| - MUST copy it into the payer_proof. | ||
| - MUST append the nonce (H("LnNonce"||TLV0,type)) to `leaf_hashes`. |
There was a problem hiding this comment.
nit: is ambiguous. It could mean:
- H(H("LnNonce"||TLV0) || H("LnNonce"||TLV0) || type) (tagged hash style), or
- H("LnNonce" || TLV0 || type) (simple concatenation)
| Thus, `missing_hashes` contains the following hashes in left-to-right | ||
| order: | ||
|
|
||
| 1. Merkle of H("LnLeaf",TLV0) and H("LnNonce"||TLV0,0) | ||
| 2. Merkle of (Merkle of H("LnLeaf",TLV20) and H("LnNonce"||TLV0,20)) | ||
| and (Merkle of H("LnLeaf",TLV30) and H("LnNonce"||TLV0,30)) | ||
| 3. Merkle of H("LnLeaf",TLV50) and H("LnNonce"||TLV0,50) |
There was a problem hiding this comment.
The example shows only 3 missing_hashes but TLV 60 is also omitted.
Looking at the tree, when the subtree (40, [50]) combines with [60] at level 2, shouldn't we need a 4th hash for 60?
Am I misunderstanding the tree structure, or is this example incomplete?
Implements the payer proof extension to BOLT 12 as specified in lightning/bolts#1295. This allows proving that a BOLT 12 invoice was paid by demonstrating possession of the payment preimage, a valid invoice signature, and a payer signature. Key additions: - Extend merkle.rs with selective disclosure primitives for creating and reconstructing merkle trees with partial TLV disclosure - Add payer_proof.rs with PayerProof, PayerProofBuilder, and UnsignedPayerProof types for building and verifying payer proofs - Support bech32 encoding with "lnp" prefix
Implements the payer proof extension to BOLT 12 as specified in lightning/bolts#1295. This allows proving that a BOLT 12 invoice was paid by demonstrating possession of the payment preimage, a valid invoice signature, and a payer signature. Key additions: - Extend merkle.rs with selective disclosure primitives for creating and reconstructing merkle trees with partial TLV disclosure - Add payer_proof.rs with PayerProof, PayerProofBuilder, and UnsignedPayerProof types for building and verifying payer proofs - Support bech32 encoding with "lnp" prefix
Implements the payer proof extension to BOLT 12 as specified in lightning/bolts#1295. This allows proving that a BOLT 12 invoice was paid by demonstrating possession of the payment preimage, a valid invoice signature, and a payer signature. Key additions: - Extend merkle.rs with selective disclosure primitives for creating and reconstructing merkle trees with partial TLV disclosure - Add payer_proof.rs with PayerProof, PayerProofBuilder, and UnsignedPayerProof types for building and verifying payer proofs - Support bech32 encoding with "lnp" prefix
Implements the payer proof extension to BOLT 12 as specified in lightning/bolts#1295. This allows proving that a BOLT 12 invoice was paid by demonstrating possession of the payment preimage, a valid invoice signature, and a payer signature. Key additions: - Extend merkle.rs with selective disclosure primitives for creating and reconstructing merkle trees with partial TLV disclosure - Add payer_proof.rs with PayerProof, PayerProofBuilder, and UnsignedPayerProof types for building and verifying payer proofs - Support bech32 encoding with "lnp" prefix
| * [`32*byte`:`premage`] | ||
| 1. type: 244 (`omitted_tlvs`) | ||
| 2. data: | ||
| * [`...*bigsize`:`missing`] |
There was a problem hiding this comment.
we should strictly require that these be "minimized" on the read side.
Implements the payer proof extension to BOLT 12 as specified in lightning/bolts#1295. This allows proving that a BOLT 12 invoice was paid by demonstrating possession of the payment preimage, a valid invoice signature, and a payer signature. Key additions: - Extend merkle.rs with selective disclosure primitives for creating and reconstructing merkle trees with partial TLV disclosure - Add payer_proof.rs with PayerProof, PayerProofBuilder, and UnsignedPayerProof types for building and verifying payer proofs - Support bech32 encoding with "lnp" prefix
Add a Rust CLI tool that generates and verifies test vectors for BOLT 12 payer proofs as specified in lightning/bolts#1295. The tool uses the rust-lightning implementation from lightningdevkit/rust-lightning#4297. Features: - Generate deterministic test vectors with configurable seed - Verify test vectors from JSON files - Support for basic proofs, proofs with notes, and invalid test cases - Uses refund flow for explicit payer key control Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements payer proofs for BOLT 12 invoices as specified in lightning/bolts#1295. A PayerProof cryptographically proves that a BOLT 12 invoice was paid by demonstrating possession of the payment preimage, a valid invoice signature over a merkle root (via selective disclosure), and a payer signature proving who authorized the payment. Key components: - `PayerProofBuilder`: constructs proofs with selective disclosure, supporting both direct signing and derived key signing from `ExpandedKey` + `Nonce` - `PayerProof`: verifiable proof with bech32 encoding (lnp HRP) - Selective disclosure via merkle tree: omitted TLV fields are represented by minimized markers and missing hashes, allowing verification of the invoice signature without revealing all fields - `compute_selective_disclosure` / `reconstruct_merkle_root`: build and verify merkle proofs using n-node in-place tree traversal
Implements payer proofs for BOLT 12 invoices as specified in lightning/bolts#1295. A payer proof cryptographically demonstrates that a BOLT 12 invoice was paid using selective disclosure of invoice fields, the payment preimage, and signatures from both the invoice issuer and the payer.
Implements payer proofs for BOLT 12 invoices as specified in lightning/bolts#1295. A payer proof cryptographically demonstrates that a BOLT 12 invoice was paid using selective disclosure of invoice fields, the payment preimage, and signatures from both the invoice issuer and the payer.
Implement payer proofs for BOLT 12 invoices as specified in lightning/bolts#1295. A payer proof cryptographically demonstrates that a BOLT 12 invoice was paid using selective disclosure of invoice fields, the payment preimage, and signatures from both the invoice issuer and the payer. The selective disclosure mechanism uses a merkle tree over the invoice's TLV fields, allowing the payer to reveal only chosen fields while proving the full invoice was signed by the issuer. Privacy-preserving omitted markers hide the actual TLV type numbers of undisclosed fields. PayerProofBuilder provides two signing modes: an explicit signing function for callers with direct key access, and automatic key re-derivation from ExpandedKey + Nonce + PaymentId for the common case where invoice requests used deriving_signing_pubkey. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement payer proofs for BOLT 12 invoices as specified in lightning/bolts#1295. A payer proof cryptographically demonstrates that a BOLT 12 invoice was paid using selective disclosure of invoice fields, the payment preimage, and signatures from both the invoice issuer and the payer. The selective disclosure mechanism uses a merkle tree over the invoice's TLV fields, allowing the payer to reveal only chosen fields while proving the full invoice was signed by the issuer. Privacy-preserving omitted markers hide the actual TLV type numbers of undisclosed fields. PayerProofBuilder provides two signing modes: an explicit signing function for callers with direct key access, and automatic key re-derivation from ExpandedKey + Nonce + PaymentId for the common case where invoice requests used deriving_signing_pubkey. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Needs: