feat(bolt12): verify compressed payer proofs via merkle reconstruction

Real BOLT12 wallets emit selective-disclosure payer proofs: `invreq_metadata`
is always withheld and other invoice fields may be elided for privacy, with
`proof_omitted_tlvs` / `proof_missing_hashes` / `proof_leaf_hashes` carrying
enough to rebuild the invoice signature's merkle root. The verifier previously
reported these as unsupported (cryptoVerified = false), so a zap paid through a
real wallet never counted locally.

Implement the lightning/bolts#1346 reader:

- Bolt12Merkle.reconstructRoot rebuilds the invoice root from the disclosed
  LnLeaf hashes + supplied nonce leaves (proof_leaf_hashes) + omitted-field
  markers + missing subtree hashes (consumed post-order DFS, smallest-to-largest).
  Add emitMissingHashes as the writer dual, unify both on one tree builder.
- Fix two latent interop bugs the vectors exposed: the nonce leaf hashes the
  record's type bytes (not the full encoded TLV), and the payer proof signs
  under fieldname `proof_signature` (not `signature`).
- Bolt12PayerProof gains marker/leaf/missing accessors and the invoice-field
  range predicate; the verifier reconstructs on every proof (type 0 is always
  the implied first omitted leaf) and drops the Unsupported result.
- Add Bolt12ProofBuilder to mint spec-compliant proofs (tests + future interop),
  and rewire Bolt12ProofFixture onto it.

Validated byte-for-byte against the draft's own conformance suite
(bolt12/payer-proof-test.json): all 5 valid vectors verify, all 23 invalid are
rejected, and the writer reproduces every vector's compression fields exactly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SpgpWLKzgD7vS9Fs4CXTR3
This commit is contained in:
Claude
2026-07-25 02:35:16 +00:00
parent f62ecb7ad2
commit 7535d791f3
15 changed files with 1662 additions and 183 deletions
+10 -8
View File
@@ -123,10 +123,12 @@ but is **not** wired into the pay path — we'd add the gate ourselves.
A wallet may settle the offer and return no proof → payment succeeds but we
can't publish a zap. Phase 1 is unaffected; Phase 2 must degrade gracefully
("paid, but no zap receipt available").
3. **Our verifier can't check compressed proofs yet** (see
`quartz/plans/2026-07-23-bolt12-zap-interop-vectors.md`). Real wallet proofs are
compressed, so a zap we send may show locally as unverified. Publishing is fine;
local counting waits on the merkle-reconstruction work.
3. ~~**Our verifier can't check compressed proofs yet.**~~ **Resolved.** The
compressed-proof merkle reconstruction shipped and is validated byte-for-byte
against the lightning/bolts#1346 conformance vectors (see
`quartz/plans/2026-07-23-bolt12-zap-interop-vectors.md`). Real wallet
(selective-disclosure) proofs now reconstruct and verify, so a bound zap counts
locally as `cryptoVerified = true`.
4. **Maturity.** Both nwc#2 and NIP-2421 are unmerged; few/no wallets implement
`pay` today. Gate hard on capability (Phase 3) and keep the intent fallback.
@@ -191,7 +193,7 @@ BOLT12 rail even if it supports `pay`. Both fail safe toward lightning.
Known limitations (follow-ons, not blockers):
- **Compressed proofs aren't locally counted.** A zap we send with a compressed
proof is published and valid, but our own `updateZapTotal` won't count it until
the merkle-reconstruction lands (see the interop-vectors plan). Other clients with
full BOLT12 support can count it.
- ~~**Compressed proofs aren't locally counted.**~~ **Resolved.** Compressed
(selective-disclosure) proofs now reconstruct and verify, so `updateZapTotal`
counts a bound zap locally. Validated against the lightning/bolts#1346
conformance vectors — see `quartz/plans/2026-07-23-bolt12-zap-interop-vectors.md`.
@@ -81,7 +81,7 @@ object Bolt12ZapActions {
val proof = Bolt12PayerProof.parse(raw) ?: return null
return runCatching {
buildMap {
put("has_all_required_fields", proof.hasAllRequiredFields())
put("has_all_required_fields", proof.hasAllCryptoFields())
put("compressed", proof.isCompressed())
proof.invreqPayerNote()?.let { put("invreq_payer_note", it) }
proof.invreqPayerId()?.let { put("invreq_payer_id", Hex.encode(it)) }
@@ -1,67 +1,44 @@
# BOLT12 zap proof verification — interop test vectors (follow-up)
Status: **blocked on upstream.** The NIP-XX BOLT12-zap layer (`quartz/…/nipXXBolt12Zaps/`)
verifies fully-disclosed payer proofs and reports compressed ones as
`Bolt12ProofResult.Unsupported` (surfaced as `cryptoVerified = false`). Two pieces
of work are gated on the BOLT12 payer-proof spec ([lightning/bolts#1346]) merging
with published test vectors.
Status: **done** (both work items shipped), with one standing upstream caveat.
`Bolt12ProofVerifier` now reconstructs and fully verifies **compressed** BOLT12
payer proofs — the selective-disclosure case real wallets emit — so they count as
`cryptoVerified = true` (subject to the usual offer-binding rule). Verified
byte-for-byte against the draft's own conformance vectors.
## Why it's gated
## What shipped
Today the crypto path (`Bolt12ProofVerifier` + `Bolt12Merkle`) is validated only by
**self-consistent round-trips** (our own encoder ↔ our own verifier, see
`Bolt12ProofFixture` + `Bolt12MerkleTest` + `Bolt12ZapValidatorTest`). That proves
internal correctness, not agreement with CLN/LDK. Several constants are our best
reading of the still-draft spec and MUST be reconciled against real vectors before
we trust wallet-produced proofs:
- **Vector-driven interop test.** `bolt12/payer-proof-test.json` from
[lightning/bolts#1346] is vendored into `quartz/src/commonTest/resources/bolt12/`
and driven by `Bolt12PayerProofVectorTest`: all 5 `valid_vectors` verify, all 23
`invalid_vectors` are rejected, and the writer (`Bolt12ProofBuilder`) reproduces
each valid vector's `proof_omitted_tlvs` / `proof_missing_hashes` /
`proof_leaf_hashes` exactly. The vectors disproved two of our earlier guesses,
now fixed:
- the **nonce leaf** hashes the record's *type* bytes, not the full encoded TLV
(`Bolt12Merkle.nonceLeafHash`);
- the **proof signature** field name is `proof_signature`, not `signature`
(`Bolt12ProofVerifier.PROOF_SIG_FIELD`).
- TLV type numbers (`Bolt12PayerProof` companion): 240/241, 10011005, 22, 8091,
160176.
- Signature digest tags (`Bolt12ProofVerifier`): `"lightning" + messagename + fieldname`
`INVOICE_MESSAGE`/`PROOF_MESSAGE`/`SIGNATURE_FIELD`. The proof-signature field
name especially is a guess.
- Merkle leaf/branch tag strings + odd-node promotion (`Bolt12Merkle`) — believed to
match LDK, not checked byte-for-byte.
- 33-byte compressed `point` → BIP-340 x-only handling / even-y convention for
`invoice_node_id` and `invreq_payer_id`.
- **Compressed-proof merkle reconstruction.** `Bolt12Merkle.reconstructRoot`
rebuilds the invoice root from the disclosed `LnLeaf` hashes + `proof_leaf_hashes`
(nonce leaves) + `proof_omitted_tlvs` markers + `proof_missing_hashes` (consumed
post-order DFS smallest-to-largest). `invreq_metadata` (type 0) is always the
implied first omitted leaf. The `isCompressed()` short-circuit is gone; every
proof now goes through reconstruction.
## Work item 1 — vector-driven interop test
## Standing caveat (upstream)
When `bolt12/payer-proof-test.json` exists in #1346:
#1346 is still an unmerged draft. The TLV type numbers, signature digest tag
strings, leaf/branch tags, and the invoice/proof field ranges track the current
PR head (`vincenzopalazzo/bolts@1be97b2`) and MUST be re-checked if the spec
changes before it merges. The vector test is the tripwire: refresh the resource
from the merged BOLT and it will flag any drift.
1. Vendor the vectors into `quartz/src/commonTest/resources/` (or inline the hex).
2. Add `Bolt12PayerProofVectorTest`: for each `valid` proof assert
`Bolt12ProofVerifier.verify(...) is Valid`; for each `invalid` proof assert the
specific rejection reason.
3. Fix any constant above that the vectors disprove. If a fix is needed, the
round-trip tests will still pass (they move with our encoder) — the vector test
is the real gate.
## Not covered here
## Work item 2 — compressed-proof merkle reconstruction
Real wallet proofs omit non-required invoice TLVs (blinded paths, etc.), which still
contributed to the invoice signature's merkle root — so `Bolt12ProofVerifier.verify`
currently returns `Unsupported` for them. Implement the reconstruction in
`Bolt12Merkle`, rebuilding the invoice root from:
- disclosed invoice TLVs → compute their `LnLeaf` hashes locally;
- `proof_leaf_hashes` (1004) → the `LnNonce` leaves for disclosed fields (can't be
computed locally — the nonce tag embeds the possibly-omitted first TLV);
- `proof_omitted_tlvs` (1002) → markers for where omitted fields sit in
TLV-ascending order;
- `proof_missing_hashes` (1003) → sibling subtree hashes for omitted branches,
consumed post-order DFS smallest-to-largest.
Then verify the invoice signature against the reconstructed root and drop the
`isCompressed()` short-circuit. Gate acceptance behind Work item 1's vectors — a
reconstruction that only round-trips against our own encoder proves nothing about
real-wallet interop.
## Not gated on this
Runtime validation is fully offline (no network) and everything else in the feature
— events, accounting, display, the fully-disclosed crypto path — is done. This
document only covers making compressed real-wallet proofs count as
`cryptoVerified = true`.
Offer↔recipient-identity binding is still out of scope — a verified proof only
proves payment to the *embedded* offer, not that the offer belongs to the
p-tagged recipient (see `Bolt12ZapValidator.isInvoiceBoundToOffer`).
[lightning/bolts#1346]: https://github.com/lightning/bolts/pull/1346
@@ -32,19 +32,22 @@ import com.vitorpamplona.quartz.utils.sha256.sha256
* - Tagged hash: `H(tag, msg) = SHA256(SHA256(tag) || SHA256(tag) || msg)`.
* - For each signable TLV record (types outside the 240..1000 signature range),
* two leaves are produced, in TLV-ascending order:
* 1. `H("LnLeaf", tlv)`
* 2. `H("LnNonce" || first-tlv, tlv)` where `first-tlv` is the encoded bytes
* of the numerically-first signable record.
* 1. `H("LnLeaf", tlv)` — over the record's full `type || length || value`.
* 2. `H("LnNonce" || first-tlv, type)` — over just the record's `type` field
* (its BigSize bytes), where `first-tlv` is the encoded bytes of the
* numerically-first signable record. (This is the subtle bit the BOLT
* spec's worked example pins down: the nonce leaf hashes the *type*, not
* the whole record.)
* - Inner nodes: `H("LnBranch", lesser || greater)` (children sorted by their
* 32-byte value). Odd nodes are promoted unchanged to the next level.
* - The signature message digest is `H("lightning" || messagename || fieldname,
* merkle_root)`, verified with BIP-340 against the signing key.
*
* NOTE: this computes the root over a **fully-disclosed** record set. Compressed
* payer proofs (which omit some invoice TLVs and supply `proof_missing_hashes` /
* `proof_leaf_hashes` to reconstruct the tree) are not reconstructed here; the
* verifier reports those as unverifiable pending validation against the
* lightning/bolts#1346 test vectors.
* [rootHash] computes the root over a **fully-disclosed** record set.
* [reconstructRoot] rebuilds it from a selectively-disclosed payer proof
* (lightning/bolts#1346), pulling omitted subtrees from `proof_missing_hashes`.
* Both are exercised byte-for-byte against the spec's `payer-proof-test.json`
* vectors.
*/
object Bolt12Merkle {
private val LN_NONCE = "LnNonce".encodeToByteArray()
@@ -82,7 +85,7 @@ object Bolt12Merkle {
var nodes = ArrayList<ByteArray>(signableRecords.size * 2)
for (record in signableRecords) {
nodes.add(taggedHashPrecomputed(LN_LEAF_TAG_HASH, record.encoded))
nodes.add(taggedHashPrecomputed(nonceTagHash, record.encoded))
nodes.add(taggedHashPrecomputed(nonceTagHash, BigSize.encode(record.type)))
}
while (nodes.size > 1) {
@@ -102,6 +105,155 @@ object Bolt12Merkle {
return nodes[0]
}
/**
* The `H("LnLeaf", tlv)` leaf hash of a single record (over its full encoded
* `type || length || value`).
*/
fun leafHash(encodedRecord: ByteArray): ByteArray = taggedHashPrecomputed(LN_LEAF_TAG_HASH, encodedRecord)
/**
* The `H("LnNonce" || first-tlv, type)` nonce leaf hash for a record of the
* given [type], where [firstTlvEncoded] is the encoded numerically-first
* signable record of the message.
*/
fun nonceLeafHash(
firstTlvEncoded: ByteArray,
type: Long,
): ByteArray = taggedHash(LN_NONCE + firstTlvEncoded, BigSize.encode(type))
/**
* The per-field merkle node — `branch(H("LnLeaf", tlv), nonceLeafHash)` — the
* hash that sits directly above a single TLV's leaf pair. A compressed proof
* supplies [nonceLeafHash] (from `proof_leaf_hashes`) because it depends on
* `first-tlv`, which the proof may have omitted.
*/
fun fieldNode(
encodedRecord: ByteArray,
nonceLeafHash: ByteArray,
): ByteArray = branch(leafHash(encodedRecord), nonceLeafHash)
/**
* A node of the reconstruction/emission tree. A leaf carries a per-field node
* hash ([leafHash], null only for a reader's omitted position) and whether it
* was omitted; an inner node carries [left]/[right].
*/
private class TreeNode private constructor(
val left: TreeNode?,
val right: TreeNode?,
val leafHash: ByteArray?,
val omitted: Boolean,
) {
val isLeaf: Boolean get() = left == null
companion object {
fun leaf(
hash: ByteArray?,
omitted: Boolean,
) = TreeNode(null, null, hash, omitted)
fun fork(
left: TreeNode,
right: TreeNode,
) = TreeNode(left, right, null, false)
}
}
/** Rebuilds the pair-adjacent / promote-odd tree shape [rootHash] flattens. */
private fun buildTree(leaves: List<TreeNode>): TreeNode {
var level = leaves
while (level.size > 1) {
val next = ArrayList<TreeNode>((level.size + 1) / 2)
var i = 0
while (i < level.size) {
if (i + 1 < level.size) {
next.add(TreeNode.fork(level[i], level[i + 1]))
i += 2
} else {
next.add(level[i])
i += 1
}
}
level = next
}
return level[0]
}
/**
* Reconstructs the merkle root of a selectively-disclosed BOLT12 message
* (lightning/bolts#1346). [leafNodeHashes] holds one entry per non-signature
* leaf of the **original** message in ascending-type order: the per-field node
* hash ([fieldNode]) for a disclosed field, or `null` for an omitted one. When
* exactly one child of an inner node is entirely omitted, its hash is pulled
* from [missingHashes] in the post-order depth-first (smallest-to-largest)
* order the writer emitted them.
*
* Returns `null` if the tree cannot be closed with exactly the supplied
* missing hashes (too few, too many, or an entirely-omitted root) — i.e. an
* unverifiable proof.
*/
fun reconstructRoot(
leafNodeHashes: List<ByteArray?>,
missingHashes: List<ByteArray>,
): ByteArray? {
if (leafNodeHashes.isEmpty()) return null
val tree = buildTree(leafNodeHashes.map { TreeNode.leaf(it, omitted = it == null) })
var idx = 0
var failed = false
fun eval(node: TreeNode): ByteArray? {
if (failed) return null
if (node.isLeaf) return node.leafHash
val a = eval(node.left!!)
val b = eval(node.right!!)
return when {
failed -> null
a == null && b == null -> null
a != null && b != null -> branch(a, b)
else -> {
if (idx >= missingHashes.size) {
failed = true
null
} else {
branch(a ?: b!!, missingHashes[idx++])
}
}
}
}
val root = eval(tree)
return if (failed || root == null || idx != missingHashes.size) null else root
}
/**
* The writer dual of [reconstructRoot]: given every non-signature leaf's
* per-field node hash ([leafNodeHashes]) and whether each was omitted
* ([leafOmitted]), emits the `proof_missing_hashes` — the hash of each subtree
* that is the lone entirely-omitted child of an inner node — in post-order
* depth-first (smallest-to-largest) order. Used to mint payer proofs (tests +
* interop harness), never on the hot verification path.
*/
fun emitMissingHashes(
leafNodeHashes: List<ByteArray>,
leafOmitted: List<Boolean>,
): List<ByteArray> {
require(leafNodeHashes.size == leafOmitted.size) { "leaf hash / omitted-flag size mismatch" }
if (leafNodeHashes.isEmpty()) return emptyList()
val tree = buildTree(leafNodeHashes.indices.map { TreeNode.leaf(leafNodeHashes[it], leafOmitted[it]) })
val missing = ArrayList<ByteArray>()
// Returns the subtree hash and whether it is entirely omitted.
fun emit(node: TreeNode): Pair<ByteArray, Boolean> {
if (node.isLeaf) return node.leafHash!! to node.omitted
val (a, aOmitted) = emit(node.left!!)
val (b, bOmitted) = emit(node.right!!)
if (aOmitted != bOmitted) missing.add(if (aOmitted) a else b)
return branch(a, b) to (aOmitted && bOmitted)
}
emit(tree)
return missing
}
private fun branch(
a: ByteArray,
b: ByteArray,
@@ -24,9 +24,11 @@ package com.vitorpamplona.quartz.nipXXBolt12Zaps.bolt12
* A parsed BOLT12 payer proof (`lnp1...`), per lightning/bolts#1346.
*
* A payer proof copies the relevant offer / invoice-request / invoice TLV fields,
* plus the invoice's `signature`, and adds the payer's own `proof_signature`,
* the `proof_preimage`, and (for compressed proofs) the merkle-reconstruction
* fields `proof_missing_hashes` / `proof_leaf_hashes` / `proof_omitted_tlvs`.
* plus the invoice's `signature`, and adds the payer's own `proof_signature`, the
* `proof_preimage`, and the merkle-reconstruction fields `proof_missing_hashes` /
* `proof_leaf_hashes` / `proof_omitted_tlvs` that let [Bolt12ProofVerifier] rebuild
* the invoice root even though `invreq_metadata` (and optionally other fields) are
* withheld for privacy.
*
* The type numbers below are the ones proposed in lightning/bolts#1346 and MUST
* be reconciled against the final merged BOLT if they change.
@@ -61,32 +63,59 @@ class Bolt12PayerProof(
fun proofLeafHashes(): ByteArray? = tlv.value(TYPE_PROOF_LEAF_HASHES)
/**
* True when the proof omits some of the original invoice's TLV fields and
* relies on `proof_missing_hashes` to reconstruct the merkle tree. Such
* proofs need the compressed-tree reconstruction to verify the invoice
* signature (not yet implemented — see [Bolt12ProofVerifier]).
* The `proof_omitted_tlvs` marker numbers (BigSize-decoded), empty when the
* field is absent. Returns null if the bytes don't decode as a BigSize list —
* a malformed proof the verifier must reject.
*/
fun isCompressed(): Boolean {
if (tlv.has(TYPE_PROOF_OMITTED_TLVS)) return true
val missing = proofMissingHashes()
return missing != null && missing.isNotEmpty()
fun omittedTlvMarkers(): List<Long>? {
val bytes = proofOmittedTlvs() ?: return emptyList()
return try {
val reader = TlvReader(bytes)
buildList { while (reader.remaining() > 0) add(reader.readBigSize()) }
} catch (_: Exception) {
null
}
}
/** The signable invoice records (types < 240) — used to recompute the invoice merkle root when fully disclosed. */
fun invoiceSignableRecords(): List<TlvRecord> = tlv.records.filter { it.type < TlvRecord.SIGNATURE_TYPE_MIN }
/** `proof_leaf_hashes` split into 32-byte hashes, or null if not a whole multiple of 32. */
fun leafHashList(): List<ByteArray>? = split32(proofLeafHashes())
/** `proof_missing_hashes` split into 32-byte hashes, or null if not a whole multiple of 32. */
fun missingHashList(): List<ByteArray>? = split32(proofMissingHashes())
/**
* The disclosed invoice (offer / invoice-request / invoice) records — the
* fields whose types fall in the invoice ranges 1..239 and
* 1_000_000_000..3_999_999_999 (lightning/bolts#1346), excluding the signature
* elements and the proof-specific fields (240..999_999_999).
*/
fun invoiceIncludedRecords(): List<TlvRecord> = tlv.records.filter { isInvoiceField(it.type) }
/**
* True when the proof omits some of the original invoice's TLV fields (i.e.
* carries `proof_omitted_tlvs` markers). Every proof reconstructs the invoice
* root through the merkle machinery — `invreq_metadata` (type 0) is always
* omitted — but this flags the extra selective disclosure for display.
*/
fun isCompressed(): Boolean = omittedTlvMarkers()?.isNotEmpty() == true
/** The signable proof records (everything but the 240..1000 signature elements) — used for the payer proof signature. */
fun proofSignableRecords(): List<TlvRecord> = tlv.records.filter { !it.isSignatureElement() }
/** True when every field NIP-XX validation requires is present. */
fun hasAllRequiredFields(): Boolean =
/**
* True when every field the BOLT12 crypto verification requires is present and
* well-sized (lightning/bolts#1346 reader rules). `invreq_payer_note` is *not*
* required here — the NIP-XX zap binding checks it separately in the validator.
*/
fun hasAllCryptoFields(): Boolean =
invreqPayerId() != null &&
invreqPayerNote() != null &&
invoicePaymentHash() != null &&
invoicePaymentHash()?.size == 32 &&
invoiceNodeId() != null &&
invoiceSignature()?.size == 64 &&
proofSignature()?.size == 64 &&
proofPreimage()?.size == 32
proofPreimage()?.size == 32 &&
tlv.has(TYPE_PROOF_MISSING_HASHES) &&
tlv.has(TYPE_PROOF_LEAF_HASHES)
companion object {
// Offer / invoice-request fields copied into the proof.
@@ -122,6 +151,20 @@ class Bolt12PayerProof(
const val TYPE_PROOF_LEAF_HASHES = 1004L
const val TYPE_PROOF_NOTE = 1005L
/**
* The invoice TLV type ranges that participate in the invoice merkle tree,
* per lightning/bolts#1346: 1..239 (offer/invreq/invoice) and
* 1_000_000_000..3_999_999_999 (high/unknown invoice fields). Excludes the
* signature range 240..1000 and the proof-specific fields 1001..999_999_999.
*/
fun isInvoiceField(type: Long): Boolean = type in 1L..239L || type in 1_000_000_000L..3_999_999_999L
private fun split32(bytes: ByteArray?): List<ByteArray>? {
if (bytes == null) return null
if (bytes.size % 32 != 0) return null
return (0 until bytes.size / 32).map { bytes.copyOfRange(it * 32, it * 32 + 32) }
}
fun parse(canonicalProof: String): Bolt12PayerProof? {
val bytes = Bolt12Bech32.decodeToBytesOrNull(canonicalProof, Bolt12Bech32.PAYER_PROOF_HRP) ?: return null
val tlv = TlvStream.readOrNull(bytes) ?: return null
@@ -0,0 +1,155 @@
/*
* Copyright (c) 2025 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nipXXBolt12Zaps.bolt12
/**
* Mints a canonical BOLT12 `lnp1...` payer proof from a full invoice, per
* lightning/bolts#1346 — the writer dual of [Bolt12ProofVerifier]. Production never
* *creates* payer proofs (a paying wallet does); this exists for the self-consistent
* test fixtures and the interop harness, and to prove the reader against
* writer-produced streams. It reproduces the spec `payer-proof-test.json` vectors'
* `proof_omitted_tlvs` / `proof_missing_hashes` / `proof_leaf_hashes` byte-for-byte.
*/
object Bolt12ProofBuilder {
/**
* One non-signature invoice TLV. [include] flags whether it is disclosed in the
* proof; `invreq_metadata` (type 0) is always omitted regardless (it is the
* hashing nonce and must never be revealed).
*/
class InvoiceField(
val type: Long,
val value: ByteArray,
val include: Boolean,
)
/**
* @param invoiceFields ALL non-signature invoice TLVs in ascending type order,
* including `invreq_metadata` (type 0).
* @param preimage the `proof_preimage`; the caller ensures the invoice's
* `invoice_payment_hash` (type 168) is its SHA-256 (or deliberately corrupt).
* @param signInvoiceDigest signs the 32-byte invoice merkle digest (node key).
* @param signProofDigest signs the 32-byte proof merkle digest (payer key).
*/
fun build(
invoiceFields: List<InvoiceField>,
preimage: ByteArray,
proofNote: String? = null,
signInvoiceDigest: (ByteArray) -> ByteArray,
signProofDigest: (ByteArray) -> ByteArray,
): String {
val firstTlv = TlvRecord(invoiceFields.first().type, invoiceFields.first().value).encoded
// Invoice signature (240) over the full invoice merkle root.
val invoiceRecords = invoiceFields.map { TlvRecord(it.type, it.value) }
val invoiceRoot = Bolt12Merkle.rootHash(invoiceRecords)
val invoiceSig = signInvoiceDigest(Bolt12Merkle.signatureDigest("invoice", INVOICE_SIG_FIELD, invoiceRoot))
// Disclosed invoice fields (type 0 is always withheld).
val disclosed = invoiceFields.filter { it.include && it.type != 0L }
val leafHashes = disclosed.map { Bolt12Merkle.nonceLeafHash(firstTlv, it.type) }
val markers = renumberOmitted(invoiceFields)
val leafNodeHashes =
invoiceFields.map { Bolt12Merkle.fieldNode(TlvRecord(it.type, it.value).encoded, Bolt12Merkle.nonceLeafHash(firstTlv, it.type)) }
val leafOmitted = invoiceFields.map { !(it.include && it.type != 0L) }
val missingHashes = Bolt12Merkle.emitMissingHashes(leafNodeHashes, leafOmitted)
// Records the proof signature (241) commits to: everything but the 240..1000
// signature elements — disclosed invoice fields plus the proof-specific fields.
val proofSignable =
buildList {
disclosed.forEach { add(TlvRecord(it.type, it.value)) }
add(TlvRecord(Bolt12PayerProof.TYPE_PROOF_PREIMAGE, preimage))
if (markers.isNotEmpty()) add(TlvRecord(Bolt12PayerProof.TYPE_PROOF_OMITTED_TLVS, encodeMarkers(markers)))
add(TlvRecord(Bolt12PayerProof.TYPE_PROOF_MISSING_HASHES, concat(missingHashes)))
add(TlvRecord(Bolt12PayerProof.TYPE_PROOF_LEAF_HASHES, concat(leafHashes)))
if (proofNote != null) add(TlvRecord(Bolt12PayerProof.TYPE_PROOF_NOTE, proofNote.encodeToByteArray()))
}.sortedBy { it.type }
val proofRoot = Bolt12Merkle.rootHash(proofSignable)
val proofSig = signProofDigest(Bolt12Merkle.signatureDigest("payer_proof", PROOF_SIG_FIELD, proofRoot))
val all =
(
proofSignable +
TlvRecord(Bolt12PayerProof.TYPE_SIGNATURE, invoiceSig) +
TlvRecord(Bolt12PayerProof.TYPE_PROOF_SIGNATURE, proofSig)
).sortedBy { it.type }
return Bolt12Bech32.encode(Bolt12Bech32.PAYER_PROOF_HRP, TlvStream(all).encode())
}
/**
* Minimal renumbering of omitted fields into `proof_omitted_tlvs` markers
* (lightning/bolts#1346): `invreq_metadata` (type 0) is implied and never
* emitted; every other omitted field takes the previous included type + 1, or
* the next value after the previous marker (starting at 1), with the
* 1_000_000_000 jump once the low range (≤239) is exhausted.
*/
private fun renumberOmitted(invoiceFields: List<InvoiceField>): List<Long> {
val markers = ArrayList<Long>()
var prevIncludedType: Long? = null
for (f in invoiceFields) {
if (f.include && f.type != 0L) {
prevIncludedType = f.type
continue
}
if (f.type == 0L) {
prevIncludedType = null
continue
}
markers.add(
when {
prevIncludedType != null -> prevIncludedType + 1
markers.isEmpty() -> 1L
markers.last() == 239L -> 1_000_000_000L
else -> markers.last() + 1
},
)
prevIncludedType = null
}
return markers
}
private fun encodeMarkers(markers: List<Long>): ByteArray {
var size = 0
for (m in markers) size += BigSize.encodedSize(m)
val out = ByteArray(size)
var offset = 0
for (m in markers) {
val enc = BigSize.encode(m)
enc.copyInto(out, offset)
offset += enc.size
}
return out
}
private fun concat(hashes: List<ByteArray>): ByteArray {
val out = ByteArray(hashes.size * 32)
var offset = 0
for (h in hashes) {
h.copyInto(out, offset)
offset += 32
}
return out
}
private const val INVOICE_SIG_FIELD = "signature"
private const val PROOF_SIG_FIELD = "proof_signature"
}
@@ -52,22 +52,18 @@ sealed interface Bolt12ProofResult {
val reason: Reason,
) : Bolt12ProofResult
/**
* The proof could not be verified with the currently-implemented checks (e.g.
* a compressed proof needing the not-yet-validated merkle reconstruction).
* Whether to surface it as unverified or drop it is the caller's policy.
*/
@Immutable
data class Unsupported(
val reason: Reason,
) : Bolt12ProofResult
enum class Reason {
MISSING_REQUIRED_FIELDS,
PREIMAGE_MISMATCH,
/**
* The selective-disclosure fields (`proof_omitted_tlvs` /
* `proof_missing_hashes` / `proof_leaf_hashes`) are malformed or don't
* describe a closable merkle tree, so the invoice root can't be rebuilt.
*/
RECONSTRUCTION_FAILED,
INVOICE_SIGNATURE_INVALID,
PROOF_SIGNATURE_INVALID,
MALFORMED_KEY,
COMPRESSED_PROOF_UNSUPPORTED,
}
}
@@ -23,59 +23,56 @@ package com.vitorpamplona.quartz.nipXXBolt12Zaps.verify
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01Crypto
import com.vitorpamplona.quartz.nipXXBolt12Zaps.bolt12.Bolt12Merkle
import com.vitorpamplona.quartz.nipXXBolt12Zaps.bolt12.Bolt12PayerProof
import com.vitorpamplona.quartz.nipXXBolt12Zaps.bolt12.TlvRecord
import com.vitorpamplona.quartz.utils.sha256.sha256
/**
* Cryptographic verification of a BOLT12 `lnp` payer proof, per lightning/bolts#1346:
*
* 1. `SHA256(proof_preimage) == invoice_payment_hash` — proves the payment settled.
* 2. The invoice `signature` (240) is valid over the invoice merkle root, signed
* by `invoice_node_id`.
* 3. The `proof_signature` (241) is valid over the proof merkle root, signed by
* `invreq_payer_id`.
* 2. The invoice `signature` (240) is valid over the **reconstructed** invoice
* merkle root, signed by `invoice_node_id`. The proof discloses only some of
* the invoice's TLV fields (`invreq_metadata` is always withheld); the omitted
* branches are rebuilt from `proof_missing_hashes` / `proof_leaf_hashes` /
* `proof_omitted_tlvs` via [Bolt12Merkle.reconstructRoot].
* 3. The `proof_signature` (241) is valid over the proof's own (fully-disclosed)
* merkle root, signed by `invreq_payer_id`.
*
* The merkle machinery ([Bolt12Merkle]) and the BIP-340 checks ([Nip01Crypto.verify])
* are exercised end-to-end by the round-trip tests. **Interop caveat:** the exact
* signature field names and, especially, the compressed-proof merkle
* reconstruction (`proof_missing_hashes` / `proof_leaf_hashes` / `proof_omitted_tlvs`)
* have not been checked against lightning/bolts#1346's `payer-proof-test.json`
* vectors — that spec is still an unmerged draft. Until then, this verifier only
* fully validates signatures for **fully-disclosed** proofs and reports
* compressed proofs as [Bolt12ProofResult.Unsupported]. Callers decide whether an
* unsupported crypto check may still be surfaced (labeled unverified) or dropped.
* The full reader path — reconstruction and both BIP-340 checks — is exercised
* byte-for-byte against the spec's `payer-proof-test.json` vectors
* ([Bolt12PayerProofVectorTest]). The BOLT signature message/field names below
* still track the unmerged draft and must be reconciled if it changes on merge.
*/
class Bolt12ProofVerifier {
fun verify(proof: Bolt12PayerProof): Bolt12ProofResult {
if (!proof.hasAllRequiredFields()) {
if (!proof.hasAllCryptoFields()) {
return Bolt12ProofResult.Invalid(Bolt12ProofResult.Reason.MISSING_REQUIRED_FIELDS)
}
val preimage = proof.proofPreimage() ?: return Bolt12ProofResult.Invalid(Bolt12ProofResult.Reason.MISSING_REQUIRED_FIELDS)
val paymentHash = proof.invoicePaymentHash() ?: return Bolt12ProofResult.Invalid(Bolt12ProofResult.Reason.MISSING_REQUIRED_FIELDS)
val preimage = proof.proofPreimage()!!
val paymentHash = proof.invoicePaymentHash()!!
// 1. Settlement proof: the preimage must hash to the invoice payment hash.
if (!sha256(preimage).contentEquals(paymentHash)) {
return Bolt12ProofResult.Invalid(Bolt12ProofResult.Reason.PREIMAGE_MISMATCH)
}
// 2/3. Signature checks require reconstructing the invoice merkle root; for a
// compressed proof that needs the (unverified) missing-hash reconstruction.
if (proof.isCompressed()) {
return Bolt12ProofResult.Unsupported(Bolt12ProofResult.Reason.COMPRESSED_PROOF_UNSUPPORTED)
}
// 2. Invoice signature over the reconstructed invoice merkle root.
val invoiceRoot =
reconstructInvoiceRoot(proof)
?: return Bolt12ProofResult.Invalid(Bolt12ProofResult.Reason.RECONSTRUCTION_FAILED)
val invoiceSig = proof.invoiceSignature()!!
val nodeId = xOnly(proof.invoiceNodeId()!!) ?: return Bolt12ProofResult.Invalid(Bolt12ProofResult.Reason.MALFORMED_KEY)
val invoiceRoot = Bolt12Merkle.rootHash(proof.invoiceSignableRecords())
val invoiceDigest = Bolt12Merkle.signatureDigest(INVOICE_MESSAGE, SIGNATURE_FIELD, invoiceRoot)
val invoiceDigest = Bolt12Merkle.signatureDigest(INVOICE_MESSAGE, INVOICE_SIG_FIELD, invoiceRoot)
if (!Nip01Crypto.verify(invoiceSig, invoiceDigest, nodeId)) {
return Bolt12ProofResult.Invalid(Bolt12ProofResult.Reason.INVOICE_SIGNATURE_INVALID)
}
// 3. Payer proof signature over the proof's own (disclosed) records.
val proofSig = proof.proofSignature()!!
val payerId = xOnly(proof.invreqPayerId()!!) ?: return Bolt12ProofResult.Invalid(Bolt12ProofResult.Reason.MALFORMED_KEY)
val proofRoot = Bolt12Merkle.rootHash(proof.proofSignableRecords())
val proofDigest = Bolt12Merkle.signatureDigest(PROOF_MESSAGE, SIGNATURE_FIELD, proofRoot)
val proofDigest = Bolt12Merkle.signatureDigest(PROOF_MESSAGE, PROOF_SIG_FIELD, proofRoot)
if (!Nip01Crypto.verify(proofSig, proofDigest, payerId)) {
return Bolt12ProofResult.Invalid(Bolt12ProofResult.Reason.PROOF_SIGNATURE_INVALID)
}
@@ -83,6 +80,71 @@ class Bolt12ProofVerifier {
return Bolt12ProofResult.Valid(paymentHash = paymentHash, invoiceAmountMillisats = proof.invoiceAmount())
}
/**
* Rebuilds the invoice merkle root from a selectively-disclosed proof. Returns
* null when the proof's compression fields are malformed or don't describe a
* closable tree — an unverifiable proof the caller must not count.
*/
private fun reconstructInvoiceRoot(proof: Bolt12PayerProof): ByteArray? {
val markers = proof.omittedTlvMarkers() ?: return null
val leafHashes = proof.leafHashList() ?: return null
val missingHashes = proof.missingHashList() ?: return null
val included = proof.invoiceIncludedRecords().sortedBy { it.type }
if (leafHashes.size != included.size) return null
if (!markersValid(markers, included)) return null
// Ordered non-signature leaves of the original invoice: the implied
// `invreq_metadata` (type 0) plus every omitted marker plus every disclosed
// field, merged in ascending numeric order. Disclosed fields carry their
// per-field node hash; omitted positions are null (filled from missing hashes).
val positions = ArrayList<Pair<Long, TlvRecord?>>(included.size + markers.size + 1)
positions.add(0L to null) // type 0 (invreq_metadata) is always omitted
for (m in markers) positions.add(m to null)
for (r in included) positions.add(r.type to r)
positions.sortBy { it.first }
var leafIdx = 0
val leafNodeHashes =
positions.map { (_, record) ->
if (record == null) {
null
} else {
Bolt12Merkle.fieldNode(record.encoded, leafHashes[leafIdx++])
}
}
return Bolt12Merkle.reconstructRoot(leafNodeHashes, missingHashes)
}
/**
* The lightning/bolts#1346 reader rules for `proof_omitted_tlvs`: strictly
* ascending, non-zero, within the invoice ranges, never the number of a
* disclosed field, and each a valid *minimal renumbering* successor — one more
* than a disclosed field, one more than the previous marker (or 0 for the
* first), or the 1_000_000_000 jump after 239.
*/
private fun markersValid(
markers: List<Long>,
included: List<TlvRecord>,
): Boolean {
val includedTypes = included.mapTo(HashSet()) { it.type }
var prev = 0L
for ((i, m) in markers.withIndex()) {
if (i > 0 && m <= markers[i - 1]) return false // strict ascending, no duplicates
if (m == 0L) return false
if (!Bolt12PayerProof.isInvoiceField(m)) return false
if (m in includedTypes) return false
val validSuccessor =
(m - 1) in includedTypes ||
m == prev + 1 ||
(m == 1_000_000_000L && prev == 239L)
if (!validSuccessor) return false
prev = m
}
return true
}
/**
* A BOLT12 `point` is a 33-byte compressed secp256k1 key; BIP-340 uses the
* 32-byte x-only form. Drop the parity prefix. (Already-x-only 32-byte input
@@ -100,6 +162,11 @@ class Bolt12ProofVerifier {
// These strings track lightning/bolts#1346 and must be reconciled on merge.
const val INVOICE_MESSAGE = "invoice"
const val PROOF_MESSAGE = "payer_proof"
const val SIGNATURE_FIELD = "signature"
/** The invoice's own signature field is named `signature`. */
const val INVOICE_SIG_FIELD = "signature"
/** The payer proof's signature field is named `proof_signature`. */
const val PROOF_SIG_FIELD = "proof_signature"
}
}
@@ -36,10 +36,12 @@ sealed interface Bolt12ZapValidation {
* @property paymentHashHex the proof's `invoice_payment_hash`, hex-encoded —
* the key clients MUST deduplicate on before summing.
* @property proofCryptoVerified true when the BOLT12 payer-proof signatures
* were fully verified; false when the proof is structurally valid and bound
* but its signatures could not yet be checked (a compressed proof — see
* [Bolt12ProofVerifier]). Callers decide whether to count or merely display
* the latter, and MUST label it as unverified.
* verified **and** the settled invoice is provably the embedded offer's (the
* offer publishes an `offer_issuer_id`, uses no blinded paths, and the
* invoice node key equals it). False when the signatures verify but the offer
* hides its destination (blinded paths / no issuer id), so paying *this* offer
* isn't proven — see [Bolt12ProofVerifier]. Callers decide whether to count or
* merely display the latter, and MUST label it as unverified.
*/
@Immutable
data class Valid(
@@ -94,6 +96,7 @@ sealed interface Bolt12ZapValidation {
OFFER_PROOF_MISMATCH,
PROOF_MISSING_REQUIRED_FIELDS,
PROOF_PREIMAGE_MISMATCH,
PROOF_RECONSTRUCTION_FAILED,
PROOF_INVOICE_SIGNATURE_INVALID,
PROOF_SIGNATURE_INVALID,
PROOF_MALFORMED_KEY,
@@ -138,7 +138,6 @@ class Bolt12ZapValidator(
val cryptoOk =
when (cryptoResult) {
is Bolt12ProofResult.Valid -> true
is Bolt12ProofResult.Unsupported -> false
is Bolt12ProofResult.Invalid -> return invalid(mapProofReason(cryptoResult.reason))
}
@@ -225,11 +224,10 @@ class Bolt12ZapValidator(
when (reason) {
Bolt12ProofResult.Reason.MISSING_REQUIRED_FIELDS -> Reason.PROOF_MISSING_REQUIRED_FIELDS
Bolt12ProofResult.Reason.PREIMAGE_MISMATCH -> Reason.PROOF_PREIMAGE_MISMATCH
Bolt12ProofResult.Reason.RECONSTRUCTION_FAILED -> Reason.PROOF_RECONSTRUCTION_FAILED
Bolt12ProofResult.Reason.INVOICE_SIGNATURE_INVALID -> Reason.PROOF_INVOICE_SIGNATURE_INVALID
Bolt12ProofResult.Reason.PROOF_SIGNATURE_INVALID -> Reason.PROOF_SIGNATURE_INVALID
Bolt12ProofResult.Reason.MALFORMED_KEY -> Reason.PROOF_MALFORMED_KEY
// A compressed proof never reaches here (it returns Unsupported, not Invalid).
Bolt12ProofResult.Reason.COMPRESSED_PROOF_UNSUPPORTED -> Reason.PROOF_MISSING_REQUIRED_FIELDS
}
private fun invalid(reason: Reason) = Bolt12ZapValidation.Invalid(reason)
@@ -36,8 +36,8 @@ import kotlin.test.assertTrue
* Proves the send-side assembly ([Bolt12ZapBuilder.buildProfileIntent] →
* [Bolt12ZapBuilder.payerNote] → [Bolt12ZapBuilder.buildZap]) — the exact path
* `Account.sendBolt12Zap` drives — produces a kind:9736 the validator accepts. Uses
* a self-consistent fixture proof bound to the built intent (not a wallet interop
* vector), so it exercises structure + binding, not the compressed-merkle gap.
* a self-consistent fixture proof bound to the built intent; byte-exact wallet
* interop is covered separately by [Bolt12PayerProofVectorTest].
*/
class Bolt12ZapBuilderTest {
private val validator = Bolt12ZapValidator()
@@ -0,0 +1,130 @@
/*
* Copyright (c) 2025 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nipXXBolt12Zaps.verify
import com.vitorpamplona.quartz.TestResourceLoader
import com.vitorpamplona.quartz.nipXXBolt12Zaps.bolt12.Bolt12PayerProof
import com.vitorpamplona.quartz.nipXXBolt12Zaps.bolt12.Bolt12ProofBuilder
import com.vitorpamplona.quartz.utils.Hex
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs
import kotlin.test.assertTrue
import kotlin.test.fail
/**
* Byte-exact interop against the lightning/bolts#1346 conformance suite
* (`bolt12/payer-proof-test.json`), the draft's own CLN/LDK-generated vectors.
*
* - Every `valid_vectors` entry must fully verify ([Bolt12ProofVerifier] returns
* [Bolt12ProofResult.Valid]) — this exercises the compressed-proof merkle
* reconstruction and both BIP-340 signature checks against real proofs.
* - Every `invalid_vectors` entry must be rejected (never [Bolt12ProofResult.Valid]).
* - The writer ([Bolt12ProofBuilder]) reproduces each valid vector's
* `proof_omitted_tlvs` / `proof_missing_hashes` / `proof_leaf_hashes` exactly.
*/
class Bolt12PayerProofVectorTest {
private val vectors by lazy {
Json.parseToJsonElement(TestResourceLoader().loadString("bolt12/payer-proof-test.json")).jsonObject
}
private val verifier = Bolt12ProofVerifier()
@Test
fun everyValidVectorVerifies() {
val valid = vectors["valid_vectors"]!!.jsonArray
assertTrue(valid.size >= 5, "expected the full valid vector set")
for (vector in valid) {
val obj = vector.jsonObject
val name = obj["name"]!!.jsonPrimitive.content
val bech32 = obj["result"]!!.jsonObject["bech32"]!!.jsonPrimitive.content
val proof = Bolt12PayerProof.parse(bech32) ?: fail("valid vector '$name' failed to parse")
val result = verifier.verify(proof)
assertIs<Bolt12ProofResult.Valid>(result, "valid vector '$name' did not verify: $result")
}
}
@Test
fun everyInvalidVectorIsRejected() {
val invalid = vectors["invalid_vectors"]!!.jsonArray
assertTrue(invalid.size >= 20, "expected the full invalid vector set")
for (vector in invalid) {
val obj = vector.jsonObject
val reason = obj["reason"]?.jsonPrimitive?.content ?: "?"
val bech32 = obj["bech32"]!!.jsonPrimitive.content
val proof = Bolt12PayerProof.parse(bech32)
// Rejection is either an unparseable stream or any non-Valid crypto result.
val verified = proof?.let { verifier.verify(it) }
assertTrue(
verified !is Bolt12ProofResult.Valid,
"invalid vector '$reason' was accepted",
)
}
}
@Test
fun writerReproducesEveryValidVectorCompressionFields() {
val valid = vectors["valid_vectors"]!!.jsonArray
for (vector in valid) {
val obj = vector.jsonObject
val name = obj["name"]!!.jsonPrimitive.content
val working = obj["working"]!!.jsonObject
val invoiceFields =
obj["input"]!!
.jsonObject["invoice_fields"]!!
.jsonArray
.map { it.jsonObject }
.filter { it["type"]!!.jsonPrimitive.content.toLong() !in 240L..1000L }
.map {
Bolt12ProofBuilder.InvoiceField(
type = it["type"]!!.jsonPrimitive.content.toLong(),
value = Hex.decode(it["hex"]!!.jsonPrimitive.content),
include = it["included"]!!.jsonPrimitive.content.toBoolean(),
)
}
// Deterministic compression fields don't depend on the signatures, so
// dummy signers suffice; we read the minted proof back and compare.
val minted =
Bolt12ProofBuilder.build(
invoiceFields = invoiceFields,
preimage = Hex.decode(obj["input"]!!.jsonObject["preimage"]!!.jsonPrimitive.content),
signInvoiceDigest = { ByteArray(64) },
signProofDigest = { ByteArray(64) },
)
val proof = Bolt12PayerProof.parse(minted) ?: fail("writer output for '$name' failed to parse")
val expectedMarkers = working["proof_omitted_tlvs"]!!.jsonArray.map { it.jsonPrimitive.content.toLong() }
assertEquals(expectedMarkers, proof.omittedTlvMarkers(), "proof_omitted_tlvs mismatch for '$name'")
val expectedMissing = working["proof_missing_hashes"]!!.jsonArray.map { it.jsonPrimitive.content }
assertEquals(expectedMissing, proof.missingHashList()!!.map { Hex.encode(it) }, "proof_missing_hashes mismatch for '$name'")
val expectedLeaves = working["proof_leaf_hashes"]!!.jsonArray.map { it.jsonPrimitive.content }
assertEquals(expectedLeaves, proof.leafHashList()!!.map { Hex.encode(it) }, "proof_leaf_hashes mismatch for '$name'")
}
}
}
@@ -23,19 +23,20 @@ package com.vitorpamplona.quartz.nipXXBolt12Zaps.verify
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01Crypto
import com.vitorpamplona.quartz.nipXXBolt12Zaps.bolt12.Bolt12Bech32
import com.vitorpamplona.quartz.nipXXBolt12Zaps.bolt12.Bolt12Merkle
import com.vitorpamplona.quartz.nipXXBolt12Zaps.bolt12.Bolt12Offer
import com.vitorpamplona.quartz.nipXXBolt12Zaps.bolt12.Bolt12PayerProof
import com.vitorpamplona.quartz.nipXXBolt12Zaps.bolt12.Bolt12ProofBuilder
import com.vitorpamplona.quartz.nipXXBolt12Zaps.bolt12.Bolt12Values
import com.vitorpamplona.quartz.nipXXBolt12Zaps.bolt12.TlvRecord
import com.vitorpamplona.quartz.nipXXBolt12Zaps.bolt12.TlvStream
import com.vitorpamplona.quartz.utils.sha256.sha256
/**
* Builds matched BOLT12 offers and payer proofs for tests, self-signing them with
* Quartz's own secp256k1 so the whole merkle + BIP-340 path is exercised. This is
* a self-consistent construction, not a CLN/LDK interop vector — see
* [Bolt12ProofVerifier].
* Builds matched BOLT12 offers and payer proofs for tests, minting spec-compliant
* (lightning/bolts#1346) proofs through [Bolt12ProofBuilder] and self-signing them
* with Quartz's own secp256k1 so the whole reconstruction + merkle + BIP-340 path
* is exercised. This is a self-consistent construction; byte-exact CLN/LDK interop
* is covered separately by [Bolt12PayerProofVectorTest].
*/
object Bolt12ProofFixture {
/** A 33-byte compressed point (even parity) wrapping an x-only key. */
@@ -73,48 +74,31 @@ object Bolt12ProofFixture {
// the preimage check (SHA256(preimage) != invoice_payment_hash) is what rejects it.
val paymentHash = sha256(preimage).also { if (corruptPaymentHash) it[0] = (it[0] + 1).toByte() }
// The invoice's signed records (types < 240), in ascending order.
val invoiceRecords =
// The full invoice's non-signature TLVs, ascending. invreq_metadata (type 0)
// is always present and always withheld; invreq_amount (82) is the field the
// `compressed` flag selectively omits.
val invoiceFields =
listOf(
TlvRecord(Bolt12PayerProof.TYPE_OFFER_ISSUER_ID, nodePoint),
TlvRecord(Bolt12PayerProof.TYPE_INVREQ_AMOUNT, Bolt12Values.tu64ToBytes(amountMillisats)),
TlvRecord(Bolt12PayerProof.TYPE_INVREQ_PAYER_ID, payerPoint),
TlvRecord(Bolt12PayerProof.TYPE_INVREQ_PAYER_NOTE, payerNote.encodeToByteArray()),
TlvRecord(Bolt12PayerProof.TYPE_INVOICE_PAYMENT_HASH, paymentHash),
TlvRecord(Bolt12PayerProof.TYPE_INVOICE_AMOUNT, Bolt12Values.tu64ToBytes(amountMillisats)),
TlvRecord(Bolt12PayerProof.TYPE_INVOICE_NODE_ID, nodePoint),
Bolt12ProofBuilder.InvoiceField(TYPE_INVREQ_METADATA, ByteArray(16), include = false),
Bolt12ProofBuilder.InvoiceField(Bolt12PayerProof.TYPE_OFFER_ISSUER_ID, nodePoint, include = true),
Bolt12ProofBuilder.InvoiceField(Bolt12PayerProof.TYPE_INVREQ_AMOUNT, Bolt12Values.tu64ToBytes(amountMillisats), include = !compressed),
Bolt12ProofBuilder.InvoiceField(Bolt12PayerProof.TYPE_INVREQ_PAYER_ID, payerPoint, include = true),
Bolt12ProofBuilder.InvoiceField(Bolt12PayerProof.TYPE_INVREQ_PAYER_NOTE, payerNote.encodeToByteArray(), include = true),
Bolt12ProofBuilder.InvoiceField(Bolt12PayerProof.TYPE_INVOICE_PAYMENT_HASH, paymentHash, include = true),
Bolt12ProofBuilder.InvoiceField(Bolt12PayerProof.TYPE_INVOICE_AMOUNT, Bolt12Values.tu64ToBytes(amountMillisats), include = true),
Bolt12ProofBuilder.InvoiceField(Bolt12PayerProof.TYPE_INVOICE_NODE_ID, nodePoint, include = true),
)
val invoiceRoot = Bolt12Merkle.rootHash(invoiceRecords)
val invoiceSigningKey = if (breakInvoiceSignature) payerLightningKey else nodeKey
val invoiceSig =
Nip01Crypto.sign(
Bolt12Merkle.signatureDigest(Bolt12ProofVerifier.INVOICE_MESSAGE, Bolt12ProofVerifier.SIGNATURE_FIELD, invoiceRoot),
invoiceSigningKey.privKey!!,
)
val preimageRecord = TlvRecord(Bolt12PayerProof.TYPE_PROOF_PREIMAGE, preimage)
// The payer proof signs everything but the 240..1000 signature elements.
val proofSignable = invoiceRecords + preimageRecord
val proofRoot = Bolt12Merkle.rootHash(proofSignable)
val proofSigningKey = if (breakProofSignature) nodeKey else payerLightningKey
val proofSig =
Nip01Crypto.sign(
Bolt12Merkle.signatureDigest(Bolt12ProofVerifier.PROOF_MESSAGE, Bolt12ProofVerifier.SIGNATURE_FIELD, proofRoot),
proofSigningKey.privKey!!,
)
val records =
buildList {
addAll(invoiceRecords)
add(TlvRecord(Bolt12PayerProof.TYPE_SIGNATURE, invoiceSig))
add(TlvRecord(Bolt12PayerProof.TYPE_PROOF_SIGNATURE, proofSig))
add(preimageRecord)
if (compressed) {
// A non-empty proof_missing_hashes marks the proof as compressed.
add(TlvRecord(Bolt12PayerProof.TYPE_PROOF_MISSING_HASHES, ByteArray(32) { 9 }))
}
}
return Bolt12Bech32.encode(Bolt12Bech32.PAYER_PROOF_HRP, TlvStream(records).encode())
return Bolt12ProofBuilder.build(
invoiceFields = invoiceFields,
preimage = preimage,
signInvoiceDigest = { digest -> Nip01Crypto.sign(digest, invoiceSigningKey.privKey!!) },
signProofDigest = { digest -> Nip01Crypto.sign(digest, proofSigningKey.privKey!!) },
)
}
private const val TYPE_INVREQ_METADATA = 0L
}
@@ -76,8 +76,11 @@ class Bolt12ZapValidatorTest {
}
@Test
fun acceptsButFlagsACompressedProofAsUnverified() =
fun verifiesACompressedProofThroughMerkleReconstruction() =
runTest {
// The proof withholds `invreq_amount`, forcing the verifier to rebuild the
// invoice merkle root from `proof_missing_hashes` before checking the
// invoice signature. Bound to a directly-addressed offer, it is fully verified.
val signer = NostrSignerInternal(KeyPair())
val nodeKey = KeyPair()
val preimage = ByteArray(32) { (it + 1).toByte() }
@@ -88,7 +91,7 @@ class Bolt12ZapValidatorTest {
val result = validator.validate(signedZap(signer, intent, proof))
assertIs<Bolt12ZapValidation.Valid>(result)
assertTrue(!result.proofCryptoVerified, "a compressed proof is bound but not yet crypto-verified")
assertTrue(result.proofCryptoVerified, "a compressed proof bound to the offer must verify")
}
@Test
@@ -0,0 +1,969 @@
{
"payer_secret": "4242424242424242424242424242424242424242424242424242424242424242",
"keys": {
"offer_issuer_id": {
"secret": "4646464646464646464646464646464646464646464646464646464646464646",
"pubkey": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382"
},
"invreq_payer_id": {
"secret": "4242424242424242424242424242424242424242424242424242424242424242",
"pubkey": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c"
},
"first_node_id": {
"secret": "4343434343434343434343434343434343434343434343434343434343434343",
"pubkey": "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007"
},
"first_path_key": {
"secret": "4444444444444444444444444444444444444444444444444444444444444444",
"pubkey": "032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991"
},
"blinded_node_id": {
"secret": "4545454545454545454545454545454545454545454545454545454545454545",
"pubkey": "02edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145"
},
"invoice_node_id": {
"secret": "4646464646464646464646464646464646464646464646464646464646464646",
"pubkey": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382"
}
},
"valid_vectors": [
{
"name": "full_disclosure",
"input": {
"invoice": "lni1qqgqqqqqqqqqqqqqqqqqqqqqqqqqq93pqf9u9gcjv52n7pl8pc96kzrjfe4ctcshlrxk9r8tv2t5y3amfyecy5szq059sggry3jnatzrgjyqqtxqdwlm0ug0uxyerc6lnljrqtd75mfr20wq4vw2qasz0uc7h32x9s0aecdhxlk075kn046aafpuuyw8f5j652t3vha2yqrsxtqt0nu4xf9q05znnzeyq96dcrptu3zdj6c4n2nv0aa3ue5xszv3qypwm2aaz66peqm3hyh09uzvzxzmfupmdhx49w5m0rva0jyu3u3pz3gqzqqqqqqqqqqqqqqqqqqqqqqqqqq2y8qqqqqqzqqqqqpqqqcqqqqqqqqqqqzqqqqqqqqqqqq9qqq2gpr82fuc32pqwtxkappzcsrlkmgfs6g0zyct0hkhashh7hsaxz7e65slq9fkx7f65qsrazhq6zqqqqqqqqqqqqqqqqqqqzczzqjtc233yeg48ur7wrst4vy8ynntsh3p07xdv2xwkc5hgfrmkjfnstcyp7aextn2n4d5mzx2phwfe70cejyqaaq78my4wndgna3ymwyc4vlf60kke258g33nhp23vldqpygemxp54ecl0vr0qfejm3xpm6atq4mlavkstcqszss",
"invoice_hex": "0010000000000000000000000000000000001621024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382520203e858210324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1ca076027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000a21c00000001000000020003000000000000000400000000000000050000a40467527988a82072cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793aa0203e8ae0d08000000000000000000000000b021024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382f040fbb932e6a9d5b4d88ca0ddc9cf9f8cc880ef41e3ec9574da89f624db898ab3e9d3ed6caa8744633b855167da009119d9834ae71f7b06f02732dc4c1debab0577feb2d05e010142",
"preimage": "0101010101010101010101010101010101010101010101010101010101010101",
"invoice_fields": [
{
"type": 0,
"len": 16,
"hex": "00000000000000000000000000000000",
"included": false
},
{
"type": 22,
"len": 33,
"hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382",
"included": true
},
{
"type": 82,
"len": 2,
"hex": "03e8",
"included": true
},
{
"type": 88,
"len": 33,
"hex": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c",
"included": true
},
{
"type": 160,
"len": 118,
"hex": "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000",
"included": true
},
{
"type": 162,
"len": 28,
"hex": "00000001000000020003000000000000000400000000000000050000",
"included": true
},
{
"type": 164,
"len": 4,
"hex": "67527988",
"included": true
},
{
"type": 168,
"len": 32,
"hex": "72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793",
"included": true
},
{
"type": 170,
"len": 2,
"hex": "03e8",
"included": true
},
{
"type": 174,
"len": 13,
"hex": "08000000000000000000000000",
"included": true
},
{
"type": 176,
"len": 33,
"hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382",
"included": true
},
{
"type": 240,
"len": 64,
"hex": "fbb932e6a9d5b4d88ca0ddc9cf9f8cc880ef41e3ec9574da89f624db898ab3e9d3ed6caa8744633b855167da009119d9834ae71f7b06f02732dc4c1debab0577",
"included": false
},
{
"type": 3000000001,
"len": 1,
"hex": "42",
"included": true
}
]
},
"working": {
"invoice_merkle_root": "cb9e0c81bb39fc244f9f523c748ab4de0e09f1a5fef74359c2e1f7cc7cdc7447",
"invoice_sighash": "538aab18f82285032db132cecf7275ba2cbb462ef1f4d151588f836d0ce47aae",
"invoice_signature": "fbb932e6a9d5b4d88ca0ddc9cf9f8cc880ef41e3ec9574da89f624db898ab3e9d3ed6caa8744633b855167da009119d9834ae71f7b06f02732dc4c1debab0577",
"proof_merkle_root": "d461a2dd3099e2c43ccef4c3c8d36f720948fe21712ea94dceccb4bddb0c9e5e",
"proof_leaf_hashes": [
"8c9057ed88f3c5a6b6441dcac3b5e4cefb3615904d7362b86e78427fb695f461",
"8dc54a97453dee6f207fa5216a30f1567442712ca98852bc789b73885029283c",
"f2deaf5f30be3ced89fc7c24d422819bf06af0e48a31423bbd0e2634f3c3de67",
"f54f80c94a87383f2a8ef7c3e461c62b67a51da5bccf6cd96a7dbab29bea51fa",
"7849b8b856e1d2a63d9ce7dc1a78e05cbb2def1f5d7709c48e8707e0a59fe51e",
"19e7e4eee6bf56c6c589fe50035490c1a7c91b753cb8007c4b52838a6772f997",
"f0191c35000247554b8d0a196898a794bf3de89982571178d931affb654f0c1a",
"dc0b8de03f1a0b0531bff146982d7d613ef6e1ef8d3bdd9590971fc18d835ffb",
"c14cfffaa314261bcbb2ed4ca24d5717bb608d8a6cc9910790bc1d49af7858ab",
"7e92b77b9e3843650f6cd7ee94b6753ea9df3533710b04dee686ad376515a5cb",
"abaab91b367e30fea7026daf9f2590bb7e9cc31db8221f4013c67289e38f22c8"
],
"proof_omitted_tlvs": [],
"proof_missing_hashes": [
"0b510ba4c6884d603159ced2f0ca21e772424b59e52a2191bbfbcf07377805a1"
]
},
"result": {
"payer_sig": "4961333409f453b5518fcbc662936fcb46e6c1db8d963c44b67d5677f0ffce3ac5c42293d1bc1298b0d67320a772e20a06c069dfa7079df9207b675357735dd7",
"proof_fields": [
{
"type": 22,
"len": 33,
"hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382"
},
{
"type": 82,
"len": 2,
"hex": "03e8"
},
{
"type": 88,
"len": 33,
"hex": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c"
},
{
"type": 160,
"len": 118,
"hex": "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000"
},
{
"type": 162,
"len": 28,
"hex": "00000001000000020003000000000000000400000000000000050000"
},
{
"type": 164,
"len": 4,
"hex": "67527988"
},
{
"type": 168,
"len": 32,
"hex": "72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793"
},
{
"type": 170,
"len": 2,
"hex": "03e8"
},
{
"type": 174,
"len": 13,
"hex": "08000000000000000000000000"
},
{
"type": 176,
"len": 33,
"hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382"
},
{
"type": 240,
"len": 64,
"hex": "fbb932e6a9d5b4d88ca0ddc9cf9f8cc880ef41e3ec9574da89f624db898ab3e9d3ed6caa8744633b855167da009119d9834ae71f7b06f02732dc4c1debab0577"
},
{
"type": 241,
"len": 64,
"hex": "4961333409f453b5518fcbc662936fcb46e6c1db8d963c44b67d5677f0ffce3ac5c42293d1bc1298b0d67320a772e20a06c069dfa7079df9207b675357735dd7"
},
{
"type": 1001,
"len": 32,
"hex": "0101010101010101010101010101010101010101010101010101010101010101"
},
{
"type": 1003,
"len": 32,
"hex": "0b510ba4c6884d603159ced2f0ca21e772424b59e52a2191bbfbcf07377805a1"
},
{
"type": 1004,
"len": 352,
"hex": "8c9057ed88f3c5a6b6441dcac3b5e4cefb3615904d7362b86e78427fb695f4618dc54a97453dee6f207fa5216a30f1567442712ca98852bc789b73885029283cf2deaf5f30be3ced89fc7c24d422819bf06af0e48a31423bbd0e2634f3c3de67f54f80c94a87383f2a8ef7c3e461c62b67a51da5bccf6cd96a7dbab29bea51fa7849b8b856e1d2a63d9ce7dc1a78e05cbb2def1f5d7709c48e8707e0a59fe51e19e7e4eee6bf56c6c589fe50035490c1a7c91b753cb8007c4b52838a6772f997f0191c35000247554b8d0a196898a794bf3de89982571178d931affb654f0c1adc0b8de03f1a0b0531bff146982d7d613ef6e1ef8d3bdd9590971fc18d835ffbc14cfffaa314261bcbb2ed4ca24d5717bb608d8a6cc9910790bc1d49af7858ab7e92b77b9e3843650f6cd7ee94b6753ea9df3533710b04dee686ad376515a5cbabaab91b367e30fea7026daf9f2590bb7e9cc31db8221f4013c67289e38f22c8"
},
{
"type": 3000000001,
"len": 1,
"hex": "42"
}
],
"bech32": "lnp1zcssyj7z5vfx29flqlnsuzatppeyu6u9ugtl3ntz3n4k996zg7a5jvuz2gpq86zcyypjgef743p5fzqq9nqxh0ah7y87rzv3ud0eleps9kl2d5348hq2k89qwcp87v0tc4rzc87uuxmn0m8l2tfh6aw75s7wz8r56fd299ckt74zqpcr9s9he72nyjs86pfe3vjqzaxups47g3xedv2e4fk877c7v6rgpxgszqhd4w73ddqusdcmjthj7pxprpd57qakmn2jh2dh3kwhezwg7gs3g5qpqqqqqqqqqqqqqqqqqqqqqqqqqq9zrsqqqqqpqqqqqqsqqvqqqqqqqqqqqpqqqqqqqqqqqqzsqq9yq3n4y7vg4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0ya2qgp73tsdpqqqqqqqqqqqqqqqqqqqpvppqf9u9gcjv52n7pl8pc96kzrjfe4ctcshlrxk9r8tv2t5y3amfyec9uzqlwun9e4f6k6d3r9qmhyul8uvezqw7s0raj2hfk5f7cjdhzv2k05a8mtv42r5gcems4gk0ksqjyvanq62uu0hkphsyuedcnqaaw4s2al3gpykzve5p8698d233l9uvc5ndl95dekpmwxev0zyke74valsll8r43wyy2far0qjnzcdvueq5aewyzsxcp5alfc8nhujq7m82dthxhwhl5p7jgqpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpq87s86eqpdgshfxx3pxkqv2eemf0pj3puaeyyj6eu54zrydml08swdmcqksl6qlvl5qkprys2lkc3u7956myg8w2cw67fnhmxc2eqntnv2uxu7zz07mftarp3hz549698hhx7grl55sk5v832e6yyufv4xy990rcndecs5pf9q709h40tuctu08d3878cfx5y2qehur27rjg5v2z8w7suf3570pauel4f7qvjj588qlj4rhhc0jxr33tv7j3mfdueakdj6nah2efh6j3lfuynw9c2msa9f3annnacxncupwtkt00rawhwzwy36rs0c99nlj3ux08unhwd06kcmzcnljsqd2fpsd8eydh209cqp7yk55r3fnh97vh7qv3cdgqqfr42judpgvk3x98jjlnm6yesft3z7xexxhlke20psddczuduql35zc9xxllz35c947kz0hku8hc6w7ajkgfw87p3kp4l77pfnll4gc5ycduhvhdfj3y64chhdsgmznvexgs0y9ur4y677zc4dlf9dmmncuyxeg0dnt7a99kw5l2nhe4xdcskpx7u6r26dm9zkjuh2a2hydnvl3sl6nsymd0nujepwm7nnp3mwpzraqp83nj383c7gkgl6edqhspq9pq"
}
},
{
"name": "minimal_disclosure",
"input": {
"invoice": "lni1qqgqqqqqqqqqqqqqqqqqqqqqqqqqq93pqf9u9gcjv52n7pl8pc96kzrjfe4ctcshlrxk9r8tv2t5y3amfyecy5szq059sggry3jnatzrgjyqqtxqdwlm0ug0uxyerc6lnljrqtd75mfr20wq4vw2qasz0uc7h32x9s0aecdhxlk075kn046aafpuuyw8f5j652t3vha2yqrsxtqt0nu4xf9q05znnzeyq96dcrptu3zdj6c4n2nv0aa3ue5xszv3qypwm2aaz66peqm3hyh09uzvzxzmfupmdhx49w5m0rva0jyu3u3pz3gqzqqqqqqqqqqqqqqqqqqqqqqqqqq2y8qqqqqqzqqqqqpqqqcqqqqqqqqqqqzqqqqqqqqqqqq9qqq2gpr82fuc32pqwtxkappzcsrlkmgfs6g0zyct0hkhashh7hsaxz7e65slq9fkx7f65qsrazczzqjtc233yeg48ur7wrst4vy8ynntsh3p07xdv2xwkc5hgfrmkjfnstcyqz3nyfzk3d42umkj2gqjh4l7zpevq04aefl6gnu4kql3e5ymu29s4q7fcvsst9uvmqx6q6yhje3vsraqple9pnxuf5vtwz0l68r6uvvs",
"invoice_hex": "0010000000000000000000000000000000001621024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382520203e858210324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1ca076027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000a21c00000001000000020003000000000000000400000000000000050000a40467527988a82072cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793aa0203e8b021024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382f0400a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319",
"preimage": "0101010101010101010101010101010101010101010101010101010101010101",
"invoice_fields": [
{
"type": 0,
"len": 16,
"hex": "00000000000000000000000000000000",
"included": false
},
{
"type": 22,
"len": 33,
"hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382",
"included": false
},
{
"type": 82,
"len": 2,
"hex": "03e8",
"included": false
},
{
"type": 88,
"len": 33,
"hex": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c",
"included": true
},
{
"type": 160,
"len": 118,
"hex": "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000",
"included": false
},
{
"type": 162,
"len": 28,
"hex": "00000001000000020003000000000000000400000000000000050000",
"included": false
},
{
"type": 164,
"len": 4,
"hex": "67527988",
"included": false
},
{
"type": 168,
"len": 32,
"hex": "72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793",
"included": true
},
{
"type": 170,
"len": 2,
"hex": "03e8",
"included": false
},
{
"type": 176,
"len": 33,
"hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382",
"included": true
},
{
"type": 240,
"len": 64,
"hex": "0a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319",
"included": false
}
]
},
"working": {
"invoice_merkle_root": "0501ea6d4ad9fe7fce7edd5e3795987bd409d66c5709c2a17f9c0dfb839e3d8e",
"invoice_sighash": "41ce7b274b0e73e60dd6abf4fa51ccae892b161adc24d4099f620b12c59a03e5",
"invoice_signature": "0a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319",
"proof_merkle_root": "ccc704b73e8190c71bf2ac3661d631c8c5ac502b78641708c8e61c0a1c8ebb72",
"proof_leaf_hashes": [
"f2deaf5f30be3ced89fc7c24d422819bf06af0e48a31423bbd0e2634f3c3de67",
"f0191c35000247554b8d0a196898a794bf3de89982571178d931affb654f0c1a",
"7e92b77b9e3843650f6cd7ee94b6753ea9df3533710b04dee686ad376515a5cb"
],
"proof_omitted_tlvs": [
1,
2,
89,
90,
91,
169
],
"proof_missing_hashes": [
"bf8cb2b1d6fa9bcdcab501b59f82c65c506b7f43514737f7197f1fcfeaebad41",
"b9406f4ce526a6a0d4e0b3a63ed89a832e31cb9939dfe1a7b5dd7232d32c02ab",
"cd9c44b53b31700c9ed0e3330ce425f7f18fac2fc1d566a34468439274f0e316",
"9f9830f2c3070cfbad13fde30ee36cd7143591164ed12040a9cd595c96840ac9",
"998ab7fa9c743fb9dbdb0d8d46fbe3ad333400bd07f328dcdb6008790bc9d2db"
]
},
"result": {
"payer_sig": "cebc25d40a2d927b5ebcab8400f542fbb7a8f462e8dd802bb13e050b9bf293b7457c19b46a476740f97c9d6ec141f23434c5d4fa253a1d2eb8896ebad99455cc",
"proof_fields": [
{
"type": 88,
"len": 33,
"hex": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c"
},
{
"type": 168,
"len": 32,
"hex": "72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793"
},
{
"type": 176,
"len": 33,
"hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382"
},
{
"type": 240,
"len": 64,
"hex": "0a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319"
},
{
"type": 241,
"len": 64,
"hex": "cebc25d40a2d927b5ebcab8400f542fbb7a8f462e8dd802bb13e050b9bf293b7457c19b46a476740f97c9d6ec141f23434c5d4fa253a1d2eb8896ebad99455cc"
},
{
"type": 1001,
"len": 32,
"hex": "0101010101010101010101010101010101010101010101010101010101010101"
},
{
"type": 1002,
"len": 6,
"hex": "0102595a5ba9"
},
{
"type": 1003,
"len": 160,
"hex": "bf8cb2b1d6fa9bcdcab501b59f82c65c506b7f43514737f7197f1fcfeaebad41b9406f4ce526a6a0d4e0b3a63ed89a832e31cb9939dfe1a7b5dd7232d32c02abcd9c44b53b31700c9ed0e3330ce425f7f18fac2fc1d566a34468439274f0e3169f9830f2c3070cfbad13fde30ee36cd7143591164ed12040a9cd595c96840ac9998ab7fa9c743fb9dbdb0d8d46fbe3ad333400bd07f328dcdb6008790bc9d2db"
},
{
"type": 1004,
"len": 96,
"hex": "f2deaf5f30be3ced89fc7c24d422819bf06af0e48a31423bbd0e2634f3c3de67f0191c35000247554b8d0a196898a794bf3de89982571178d931affb654f0c1a7e92b77b9e3843650f6cd7ee94b6753ea9df3533710b04dee686ad376515a5cb"
}
],
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qva0p96s9zmynmt672hpqq74p0hdag733w3hvq9wcnupgtn0ef8d690svmg6j8vaq0jlyadmq5ru35xnzaf7398gwjawyfd6adn9z4en7s86fqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyql6ql2qcqsyk26tw5l6qlt5zlcev436mafhnw2k5qmt8uzcew9q6mlgdg5wdlhr9l3lnl2awk5rw2qdaxw2f4x5r2wpvax8mvf4qewx89ejwwluxnmthtjxtfjcq4tekwyfdfmx9cqe8ksuveseep97lccltp0c82kdg6ydppeya8suvtflxps7tpswr8m45flmccwudkdw9p4jytya5fqgz5u6k2uj6zq4jve32ml48r587uahkcd34r0hcadxv6qp0g87v5dekmqppushjwjm07s8mrq7t027heshc7wmz0u0sjdgg5pn0cx4u8y3gc5ywaapcnrfu7rmenlqxgux5qqy364fwxs5xtgnznef0eaazvcy4c30rvnrtlmv48scxn7j2mhh83cgdjs7mxha62tvaf7480n2vm3pvzdae5x45mk29d9ev"
}
},
{
"name": "with_note",
"input": {
"invoice": "lni1qqgqqqqqqqqqqqqqqqqqqqqqqqqqq93pqf9u9gcjv52n7pl8pc96kzrjfe4ctcshlrxk9r8tv2t5y3amfyecy5szq059sggry3jnatzrgjyqqtxqdwlm0ug0uxyerc6lnljrqtd75mfr20wq4vw2qasz0uc7h32x9s0aecdhxlk075kn046aafpuuyw8f5j652t3vha2yqrsxtqt0nu4xf9q05znnzeyq96dcrptu3zdj6c4n2nv0aa3ue5xszv3qypwm2aaz66peqm3hyh09uzvzxzmfupmdhx49w5m0rva0jyu3u3pz3gqzqqqqqqqqqqqqqqqqqqqqqqqqqq2y8qqqqqqzqqqqqpqqqcqqqqqqqqqqqzqqqqqqqqqqqq9qqq2gpr82fuc32pqwtxkappzcsrlkmgfs6g0zyct0hkhashh7hsaxz7e65slq9fkx7f65qsrazczzqjtc233yeg48ur7wrst4vy8ynntsh3p07xdv2xwkc5hgfrmkjfnstcyqz3nyfzk3d42umkj2gqjh4l7zpevq04aefl6gnu4kql3e5ymu29s4q7fcvsst9uvmqx6q6yhje3vsraqple9pnxuf5vtwz0l68r6uvvs",
"invoice_hex": "0010000000000000000000000000000000001621024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382520203e858210324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1ca076027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000a21c00000001000000020003000000000000000400000000000000050000a40467527988a82072cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793aa0203e8b021024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382f0400a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319",
"preimage": "0101010101010101010101010101010101010101010101010101010101010101",
"note": "test note",
"invoice_fields": [
{
"type": 0,
"len": 16,
"hex": "00000000000000000000000000000000",
"included": false
},
{
"type": 22,
"len": 33,
"hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382",
"included": false
},
{
"type": 82,
"len": 2,
"hex": "03e8",
"included": false
},
{
"type": 88,
"len": 33,
"hex": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c",
"included": true
},
{
"type": 160,
"len": 118,
"hex": "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000",
"included": false
},
{
"type": 162,
"len": 28,
"hex": "00000001000000020003000000000000000400000000000000050000",
"included": false
},
{
"type": 164,
"len": 4,
"hex": "67527988",
"included": false
},
{
"type": 168,
"len": 32,
"hex": "72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793",
"included": true
},
{
"type": 170,
"len": 2,
"hex": "03e8",
"included": false
},
{
"type": 176,
"len": 33,
"hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382",
"included": true
},
{
"type": 240,
"len": 64,
"hex": "0a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319",
"included": false
}
]
},
"working": {
"invoice_merkle_root": "0501ea6d4ad9fe7fce7edd5e3795987bd409d66c5709c2a17f9c0dfb839e3d8e",
"invoice_sighash": "41ce7b274b0e73e60dd6abf4fa51ccae892b161adc24d4099f620b12c59a03e5",
"invoice_signature": "0a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319",
"proof_merkle_root": "327c38426d1d557f3669f19cdb440187e312679eadcfb61a9a8dcb0098639467",
"proof_leaf_hashes": [
"f2deaf5f30be3ced89fc7c24d422819bf06af0e48a31423bbd0e2634f3c3de67",
"f0191c35000247554b8d0a196898a794bf3de89982571178d931affb654f0c1a",
"7e92b77b9e3843650f6cd7ee94b6753ea9df3533710b04dee686ad376515a5cb"
],
"proof_omitted_tlvs": [
1,
2,
89,
90,
91,
169
],
"proof_missing_hashes": [
"bf8cb2b1d6fa9bcdcab501b59f82c65c506b7f43514737f7197f1fcfeaebad41",
"b9406f4ce526a6a0d4e0b3a63ed89a832e31cb9939dfe1a7b5dd7232d32c02ab",
"cd9c44b53b31700c9ed0e3330ce425f7f18fac2fc1d566a34468439274f0e316",
"9f9830f2c3070cfbad13fde30ee36cd7143591164ed12040a9cd595c96840ac9",
"998ab7fa9c743fb9dbdb0d8d46fbe3ad333400bd07f328dcdb6008790bc9d2db"
]
},
"result": {
"payer_sig": "2a47f98770ae814119d682a7b19f7ce9e050a3bb49d9b0a031c46d9cb57a3075ddc23a742f6d33bc4ec8e1778327494bea76d2ee564625e99bfb95cc209a7722",
"proof_fields": [
{
"type": 88,
"len": 33,
"hex": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c"
},
{
"type": 168,
"len": 32,
"hex": "72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793"
},
{
"type": 176,
"len": 33,
"hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382"
},
{
"type": 240,
"len": 64,
"hex": "0a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319"
},
{
"type": 241,
"len": 64,
"hex": "2a47f98770ae814119d682a7b19f7ce9e050a3bb49d9b0a031c46d9cb57a3075ddc23a742f6d33bc4ec8e1778327494bea76d2ee564625e99bfb95cc209a7722"
},
{
"type": 1001,
"len": 32,
"hex": "0101010101010101010101010101010101010101010101010101010101010101"
},
{
"type": 1002,
"len": 6,
"hex": "0102595a5ba9"
},
{
"type": 1003,
"len": 160,
"hex": "bf8cb2b1d6fa9bcdcab501b59f82c65c506b7f43514737f7197f1fcfeaebad41b9406f4ce526a6a0d4e0b3a63ed89a832e31cb9939dfe1a7b5dd7232d32c02abcd9c44b53b31700c9ed0e3330ce425f7f18fac2fc1d566a34468439274f0e3169f9830f2c3070cfbad13fde30ee36cd7143591164ed12040a9cd595c96840ac9998ab7fa9c743fb9dbdb0d8d46fbe3ad333400bd07f328dcdb6008790bc9d2db"
},
{
"type": 1004,
"len": 96,
"hex": "f2deaf5f30be3ced89fc7c24d422819bf06af0e48a31423bbd0e2634f3c3de67f0191c35000247554b8d0a196898a794bf3de89982571178d931affb654f0c1a7e92b77b9e3843650f6cd7ee94b6753ea9df3533710b04dee686ad376515a5cb"
},
{
"type": 1005,
"len": 9,
"hex": "74657374206e6f7465"
}
],
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qz53lesac2aq2pr8tg9fa3na7wnczs5wa5nkds5qcugmvuk4arqawacga8gtmdxw7yaj8pw7pjwj2tafmd9mjkgcj7nxlmjhxzpxnhyt7s86fqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyql6ql2qcqsyk26tw5l6qlt5zlcev436mafhnw2k5qmt8uzcew9q6mlgdg5wdlhr9l3lnl2awk5rw2qdaxw2f4x5r2wpvax8mvf4qewx89ejwwluxnmthtjxtfjcq4tekwyfdfmx9cqe8ksuveseep97lccltp0c82kdg6ydppeya8suvtflxps7tpswr8m45flmccwudkdw9p4jytya5fqgz5u6k2uj6zq4jve32ml48r587uahkcd34r0hcadxv6qp0g87v5dekmqppushjwjm07s8mrq7t027heshc7wmz0u0sjdgg5pn0cx4u8y3gc5ywaapcnrfu7rmenlqxgux5qqy364fwxs5xtgnznef0eaazvcy4c30rvnrtlmv48scxn7j2mhh83cgdjs7mxha62tvaf7480n2vm3pvzdae5x45mk29d9e07s8mgfw3jhxapqdehhgeg"
}
},
{
"name": "left_subtree_omitted",
"input": {
"invoice": "lni1qqgqqqqqqqqqqqqqqqqqqqqqqqqqq93pqf9u9gcjv52n7pl8pc96kzrjfe4ctcshlrxk9r8tv2t5y3amfyecy5szq059sggry3jnatzrgjyqqtxqdwlm0ug0uxyerc6lnljrqtd75mfr20wq4vw2qasz0uc7h32x9s0aecdhxlk075kn046aafpuuyw8f5j652t3vha2yqrsxtqt0nu4xf9q05znnzeyq96dcrptu3zdj6c4n2nv0aa3ue5xszv3qypwm2aaz66peqm3hyh09uzvzxzmfupmdhx49w5m0rva0jyu3u3pz3gqzqqqqqqqqqqqqqqqqqqqqqqqqqq2y8qqqqqqzqqqqqpqqqcqqqqqqqqqqqzqqqqqqqqqqqq9qqq2gpr82fuc32pqwtxkappzcsrlkmgfs6g0zyct0hkhashh7hsaxz7e65slq9fkx7f65qsrazczzqjtc233yeg48ur7wrst4vy8ynntsh3p07xdv2xwkc5hgfrmkjfnstcyqz3nyfzk3d42umkj2gqjh4l7zpevq04aefl6gnu4kql3e5ymu29s4q7fcvsst9uvmqx6q6yhje3vsraqple9pnxuf5vtwz0l68r6uvvs",
"invoice_hex": "0010000000000000000000000000000000001621024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382520203e858210324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1ca076027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000a21c00000001000000020003000000000000000400000000000000050000a40467527988a82072cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793aa0203e8b021024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382f0400a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319",
"preimage": "0101010101010101010101010101010101010101010101010101010101010101",
"invoice_fields": [
{
"type": 0,
"len": 16,
"hex": "00000000000000000000000000000000",
"included": false
},
{
"type": 22,
"len": 33,
"hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382",
"included": false
},
{
"type": 82,
"len": 2,
"hex": "03e8",
"included": false
},
{
"type": 88,
"len": 33,
"hex": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c",
"included": true
},
{
"type": 160,
"len": 118,
"hex": "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000",
"included": false
},
{
"type": 162,
"len": 28,
"hex": "00000001000000020003000000000000000400000000000000050000",
"included": false
},
{
"type": 164,
"len": 4,
"hex": "67527988",
"included": false
},
{
"type": 168,
"len": 32,
"hex": "72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793",
"included": true
},
{
"type": 170,
"len": 2,
"hex": "03e8",
"included": true
},
{
"type": 176,
"len": 33,
"hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382",
"included": true
},
{
"type": 240,
"len": 64,
"hex": "0a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319",
"included": false
}
]
},
"working": {
"invoice_merkle_root": "0501ea6d4ad9fe7fce7edd5e3795987bd409d66c5709c2a17f9c0dfb839e3d8e",
"invoice_sighash": "41ce7b274b0e73e60dd6abf4fa51ccae892b161adc24d4099f620b12c59a03e5",
"invoice_signature": "0a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319",
"proof_merkle_root": "1b0bab09a5fcccec19eb3aaafbbfa4075944fd1f65691ed715fbbc17a59b3651",
"proof_leaf_hashes": [
"f2deaf5f30be3ced89fc7c24d422819bf06af0e48a31423bbd0e2634f3c3de67",
"f0191c35000247554b8d0a196898a794bf3de89982571178d931affb654f0c1a",
"dc0b8de03f1a0b0531bff146982d7d613ef6e1ef8d3bdd9590971fc18d835ffb",
"7e92b77b9e3843650f6cd7ee94b6753ea9df3533710b04dee686ad376515a5cb"
],
"proof_omitted_tlvs": [
1,
2,
89,
90,
91
],
"proof_missing_hashes": [
"bf8cb2b1d6fa9bcdcab501b59f82c65c506b7f43514737f7197f1fcfeaebad41",
"b9406f4ce526a6a0d4e0b3a63ed89a832e31cb9939dfe1a7b5dd7232d32c02ab",
"cd9c44b53b31700c9ed0e3330ce425f7f18fac2fc1d566a34468439274f0e316",
"9f9830f2c3070cfbad13fde30ee36cd7143591164ed12040a9cd595c96840ac9"
]
},
"result": {
"payer_sig": "ed03ec58d5ac676539486bb6e8d6f389b6375b6693ffdf30a93919590d744fa393f945a5de3bb57d65c7f61def1cefa8aa038725b15bf0fa797dfd08ca97538a",
"proof_fields": [
{
"type": 88,
"len": 33,
"hex": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c"
},
{
"type": 168,
"len": 32,
"hex": "72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793"
},
{
"type": 170,
"len": 2,
"hex": "03e8"
},
{
"type": 176,
"len": 33,
"hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382"
},
{
"type": 240,
"len": 64,
"hex": "0a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319"
},
{
"type": 241,
"len": 64,
"hex": "ed03ec58d5ac676539486bb6e8d6f389b6375b6693ffdf30a93919590d744fa393f945a5de3bb57d65c7f61def1cefa8aa038725b15bf0fa797dfd08ca97538a"
},
{
"type": 1001,
"len": 32,
"hex": "0101010101010101010101010101010101010101010101010101010101010101"
},
{
"type": 1002,
"len": 5,
"hex": "0102595a5b"
},
{
"type": 1003,
"len": 128,
"hex": "bf8cb2b1d6fa9bcdcab501b59f82c65c506b7f43514737f7197f1fcfeaebad41b9406f4ce526a6a0d4e0b3a63ed89a832e31cb9939dfe1a7b5dd7232d32c02abcd9c44b53b31700c9ed0e3330ce425f7f18fac2fc1d566a34468439274f0e3169f9830f2c3070cfbad13fde30ee36cd7143591164ed12040a9cd595c96840ac9"
},
{
"type": 1004,
"len": 128,
"hex": "f2deaf5f30be3ced89fc7c24d422819bf06af0e48a31423bbd0e2634f3c3de67f0191c35000247554b8d0a196898a794bf3de89982571178d931affb654f0c1adc0b8de03f1a0b0531bff146982d7d613ef6e1ef8d3bdd9590971fc18d835ffb7e92b77b9e3843650f6cd7ee94b6753ea9df3533710b04dee686ad376515a5cb"
}
],
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0ya2qgp73vppqf9u9gcjv52n7pl8pc96kzrjfe4ctcshlrxk9r8tv2t5y3amfyec9uzqpgejy3tgk64wdmf9yqft6llpqukq867u5layf72mq0cu6zd79zc2s0yuxgg9j7xdsrdqdztevckgp7sqlujsenwy6x9hp8lar3awxx03grks8mzc6kkxwefefp4md6xk7wymvd6mv6fllhes4yu3jkgdw3868ylegkjauwa404ju0asaauwwl292qwrjtv2m7ra8jl0apr9fw5u2l5p7jgqpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpq87s86s9qyp9jkjml5p7hq9l3jetr4h6n0xu4dgpkk0c93ju2p4h7s63gumlwxtlrl8746adgxu5qm6vu5n2dgx5uze6v0kcn2pjuvwtnyualcd8khwhyvkn9sp2hnvugj6nkvtspj0dpcenpnjztal337kzlsw4v635g6zrjf60pcckn7vrpukrqux0htgnlh3sacmv6u2rtygkfmgjqs9fe4v4e95yptyl6qlvsredat6lxzlremvfl37zf4pzsxdlq6hsuj9rzs3mh58zvd8nc00x0uqers6sqqj8249c6zsedzv2099l8h5fnqjhz9udjvd0ldj57rq6ms9cmcplrg9s2vdl79rfsttavyl0dc0035aam9vsju0urrvrtlahay4h0w0rssm9pakd0m55ke6na2wlx5ehzzcymmngdtfhv526tjc"
}
},
{
"name": "empty_proof_omitted_tlvs_explicit",
"input": {
"invoice": "lni1qqgqqqqqqqqqqqqqqqqqqqqqqqqqq93pqf9u9gcjv52n7pl8pc96kzrjfe4ctcshlrxk9r8tv2t5y3amfyecy5szq059sggry3jnatzrgjyqqtxqdwlm0ug0uxyerc6lnljrqtd75mfr20wq4vw2qasz0uc7h32x9s0aecdhxlk075kn046aafpuuyw8f5j652t3vha2yqrsxtqt0nu4xf9q05znnzeyq96dcrptu3zdj6c4n2nv0aa3ue5xszv3qypwm2aaz66peqm3hyh09uzvzxzmfupmdhx49w5m0rva0jyu3u3pz3gqzqqqqqqqqqqqqqqqqqqqqqqqqqq2y8qqqqqqzqqqqqpqqqcqqqqqqqqqqqzqqqqqqqqqqqq9qqq2gpr82fuc32pqwtxkappzcsrlkmgfs6g0zyct0hkhashh7hsaxz7e65slq9fkx7f65qsrazczzqjtc233yeg48ur7wrst4vy8ynntsh3p07xdv2xwkc5hgfrmkjfnstcyqz3nyfzk3d42umkj2gqjh4l7zpevq04aefl6gnu4kql3e5ymu29s4q7fcvsst9uvmqx6q6yhje3vsraqple9pnxuf5vtwz0l68r6uvvs",
"invoice_hex": "0010000000000000000000000000000000001621024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382520203e858210324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1ca076027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000a21c00000001000000020003000000000000000400000000000000050000a40467527988a82072cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793aa0203e8b021024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382f0400a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319",
"preimage": "0101010101010101010101010101010101010101010101010101010101010101",
"invoice_fields": [
{
"type": 0,
"len": 16,
"hex": "00000000000000000000000000000000",
"included": false
},
{
"type": 22,
"len": 33,
"hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382",
"included": true
},
{
"type": 82,
"len": 2,
"hex": "03e8",
"included": true
},
{
"type": 88,
"len": 33,
"hex": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c",
"included": true
},
{
"type": 160,
"len": 118,
"hex": "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000",
"included": true
},
{
"type": 162,
"len": 28,
"hex": "00000001000000020003000000000000000400000000000000050000",
"included": true
},
{
"type": 164,
"len": 4,
"hex": "67527988",
"included": true
},
{
"type": 168,
"len": 32,
"hex": "72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793",
"included": true
},
{
"type": 170,
"len": 2,
"hex": "03e8",
"included": true
},
{
"type": 176,
"len": 33,
"hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382",
"included": true
},
{
"type": 240,
"len": 64,
"hex": "0a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319",
"included": false
}
]
},
"working": {
"invoice_merkle_root": "0501ea6d4ad9fe7fce7edd5e3795987bd409d66c5709c2a17f9c0dfb839e3d8e",
"invoice_sighash": "41ce7b274b0e73e60dd6abf4fa51ccae892b161adc24d4099f620b12c59a03e5",
"invoice_signature": "0a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319",
"proof_merkle_root": "a98cbee700b100ede32c19f9525264d51a105751fb27e790dad011bb38415b89",
"proof_leaf_hashes": [
"8c9057ed88f3c5a6b6441dcac3b5e4cefb3615904d7362b86e78427fb695f461",
"8dc54a97453dee6f207fa5216a30f1567442712ca98852bc789b73885029283c",
"f2deaf5f30be3ced89fc7c24d422819bf06af0e48a31423bbd0e2634f3c3de67",
"f54f80c94a87383f2a8ef7c3e461c62b67a51da5bccf6cd96a7dbab29bea51fa",
"7849b8b856e1d2a63d9ce7dc1a78e05cbb2def1f5d7709c48e8707e0a59fe51e",
"19e7e4eee6bf56c6c589fe50035490c1a7c91b753cb8007c4b52838a6772f997",
"f0191c35000247554b8d0a196898a794bf3de89982571178d931affb654f0c1a",
"dc0b8de03f1a0b0531bff146982d7d613ef6e1ef8d3bdd9590971fc18d835ffb",
"7e92b77b9e3843650f6cd7ee94b6753ea9df3533710b04dee686ad376515a5cb"
],
"proof_omitted_tlvs": [],
"proof_missing_hashes": [
"0b510ba4c6884d603159ced2f0ca21e772424b59e52a2191bbfbcf07377805a1"
]
},
"result": {
"payer_sig": "dffe62d6446c182f2503e780fce09fc5cf310ef14a54f65dd1df124039b7a897f2940470ac0f9857f826b232289c0ac4e6927ca67d805167fb0add352f88add1",
"proof_fields": [
{
"type": 22,
"len": 33,
"hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382"
},
{
"type": 82,
"len": 2,
"hex": "03e8"
},
{
"type": 88,
"len": 33,
"hex": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c"
},
{
"type": 160,
"len": 118,
"hex": "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000"
},
{
"type": 162,
"len": 28,
"hex": "00000001000000020003000000000000000400000000000000050000"
},
{
"type": 164,
"len": 4,
"hex": "67527988"
},
{
"type": 168,
"len": 32,
"hex": "72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793"
},
{
"type": 170,
"len": 2,
"hex": "03e8"
},
{
"type": 176,
"len": 33,
"hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382"
},
{
"type": 240,
"len": 64,
"hex": "0a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319"
},
{
"type": 241,
"len": 64,
"hex": "dffe62d6446c182f2503e780fce09fc5cf310ef14a54f65dd1df124039b7a897f2940470ac0f9857f826b232289c0ac4e6927ca67d805167fb0add352f88add1"
},
{
"type": 1001,
"len": 32,
"hex": "0101010101010101010101010101010101010101010101010101010101010101"
},
{
"type": 1002,
"len": 0,
"hex": ""
},
{
"type": 1003,
"len": 32,
"hex": "0b510ba4c6884d603159ced2f0ca21e772424b59e52a2191bbfbcf07377805a1"
},
{
"type": 1004,
"len": 288,
"hex": "8c9057ed88f3c5a6b6441dcac3b5e4cefb3615904d7362b86e78427fb695f4618dc54a97453dee6f207fa5216a30f1567442712ca98852bc789b73885029283cf2deaf5f30be3ced89fc7c24d422819bf06af0e48a31423bbd0e2634f3c3de67f54f80c94a87383f2a8ef7c3e461c62b67a51da5bccf6cd96a7dbab29bea51fa7849b8b856e1d2a63d9ce7dc1a78e05cbb2def1f5d7709c48e8707e0a59fe51e19e7e4eee6bf56c6c589fe50035490c1a7c91b753cb8007c4b52838a6772f997f0191c35000247554b8d0a196898a794bf3de89982571178d931affb654f0c1adc0b8de03f1a0b0531bff146982d7d613ef6e1ef8d3bdd9590971fc18d835ffb7e92b77b9e3843650f6cd7ee94b6753ea9df3533710b04dee686ad376515a5cb"
}
],
"bech32": "lnp1zcssyj7z5vfx29flqlnsuzatppeyu6u9ugtl3ntz3n4k996zg7a5jvuz2gpq86zcyypjgef743p5fzqq9nqxh0ah7y87rzv3ud0eleps9kl2d5348hq2k89qwcp87v0tc4rzc87uuxmn0m8l2tfh6aw75s7wz8r56fd299ckt74zqpcr9s9he72nyjs86pfe3vjqzaxups47g3xedv2e4fk877c7v6rgpxgszqhd4w73ddqusdcmjthj7pxprpd57qakmn2jh2dh3kwhezwg7gs3g5qpqqqqqqqqqqqqqqqqqqqqqqqqqq9zrsqqqqqpqqqqqqsqqvqqqqqqqqqqqpqqqqqqqqqqqqzsqq9yq3n4y7vg4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0ya2qgp73vppqf9u9gcjv52n7pl8pc96kzrjfe4ctcshlrxk9r8tv2t5y3amfyec9uzqpgejy3tgk64wdmf9yqft6llpqukq867u5layf72mq0cu6zd79zc2s0yuxgg9j7xdsrdqdztevckgp7sqlujsenwy6x9hp8lar3awxx03gr0luckkg3kpste9q0ncpl8qnlzu7vgw7999faja6803yspek75f0u55q3c2cruc2luzdv3j9zwq438xjf72vlvq29nlkzkax5hc3tw3l5p7jgqpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpq87s86sql5p7kgqt2y96f35gf4srzkww6tcv5g08wfpykk099gserwlmeurnw7q9587s8m8aqysgeyzhaky083dxkezpmjkrkhjva7ekzkgy6umzhph8ssnlk62lgcvdc49fw3faaehjqla9y94rpu2kw3p8zt9f3pftc7ymwwy9q2fg8nedat6lxzlremvfl37zf4pzsxdlq6hsuj9rzs3mh58zvd8nc00x0a20sry54pec8u4gaa7ru3suv2m855w6t0x0dnvk5ld6k2d755060pym3wzku8f2v0vuulwp578qtjajmmclt4msn3ywsur7pfvlu50pnelyamnt74kxckylu5qr2jgvrf7frd6newqq03949qu2vae0n9lsrywr2qqzga25hrg2r95f3fu5hu773xvz2ugh3kf34lak2ncvrtwqhr0q8udqkpf3hlc5dxpd04snaahpa7xnhhv4jzt3lsvdsd0lkl5jkaaeuwzrv58ke4lwjjm8204fmu6nxugtqn0wdp4dxaj3tfwt"
}
}
],
"invalid_vectors": [
{
"reason": "missing_invreq_payer_id",
"bech32": "lnp14qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qva0p96s9zmynmt672hpqq74p0hdag733w3hvq9wcnupgtn0ef8d690svmg6j8vaq0jlyadmq5ru35xnzaf7398gwjawyfd6adn9z4en7s86fqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyql6ql2qcqsyk26tw5l6qlt5zlcev436mafhnw2k5qmt8uzcew9q6mlgdg5wdlhr9l3lnl2awk5rw2qdaxw2f4x5r2wpvax8mvf4qewx89ejwwluxnmthtjxtfjcq4tekwyfdfmx9cqe8ksuveseep97lccltp0c82kdg6ydppeya8suvtflxps7tpswr8m45flmccwudkdw9p4jytya5fqgz5u6k2uj6zq4jve32ml48r587uahkcd34r0hcadxv6qp0g87v5dekmqppushjwjm07s8mrq7t027heshc7wmz0u0sjdgg5pn0cx4u8y3gc5ywaapcnrfu7rmenlqxgux5qqy364fwxs5xtgnznef0eaazvcy4c30rvnrtlmv48scxn7j2mhh83cgdjs7mxha62tvaf7480n2vm3pvzdae5x45mk29d9ev"
},
{
"reason": "missing_invoice_payment_hash",
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cukqssyj7z5vfx29flqlnsuzatppeyu6u9ugtl3ntz3n4k996zg7a5jvuz7pqq5vezg45td2hxa5jjqy4a0lsswtqra0w207jyl9ds8uwdpxlz3v9g8jwryyze0rxcpksx39ukvtyqlgq07fgvehzdrzmsnl73c7hrr8c5pn4uyh2q5tvj0d0te2uyqr6597ah4r6x96xasq4mz0s9pwdl9yahg47pndr2gan5p7tun4hvzs0jxs6vt486y5ap6t4c39ht4kv52hx06qlfyqqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsrlgragrqzqjetfd6nlgrawstlr9jk8t04x7de26srdvlstr9c5rt0ap4z3eh7uvh7870at466sdegph5eefx56sdfc9n5cld3x5r9ccuhxfemls60dwawgedxtqz40xec3948vchqry76r3nxr8yyhmlrrav9lqa2e4rg35y8yn57r33d8ucxrevxpcvlwk38l0rpm3ke4c5xkg3vnk3ypq2nn2etjtggzkfnx9t075uwslmnk7mpkx5d7lr45engq9aqlej3hxmvqy8jz7f6tdl6qlvvredat6lxzlremvfl37zf4pzsxdlq6hsuj9rzs3mh58zvd8nc00x0uqers6sqqj8249c6zsedzv2099l8h5fnqjhz9udjvd0ldj57rq606ftw7u78ppk2rmv6lhffdn4865a7dfnwy9sfhhxs6knweg45h9s"
},
{
"reason": "missing_invoice_node_id",
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0ylsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qva0p96s9zmynmt672hpqq74p0hdag733w3hvq9wcnupgtn0ef8d690svmg6j8vaq0jlyadmq5ru35xnzaf7398gwjawyfd6adn9z4en7s86fqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyql6ql2qcqsyk26tw5l6qlt5zlcev436mafhnw2k5qmt8uzcew9q6mlgdg5wdlhr9l3lnl2awk5rw2qdaxw2f4x5r2wpvax8mvf4qewx89ejwwluxnmthtjxtfjcq4tekwyfdfmx9cqe8ksuveseep97lccltp0c82kdg6ydppeya8suvtflxps7tpswr8m45flmccwudkdw9p4jytya5fqgz5u6k2uj6zq4jve32ml48r587uahkcd34r0hcadxv6qp0g87v5dekmqppushjwjm07s8mrq7t027heshc7wmz0u0sjdgg5pn0cx4u8y3gc5ywaapcnrfu7rmenlqxgux5qqy364fwxs5xtgnznef0eaazvcy4c30rvnrtlmv48scxn7j2mhh83cgdjs7mxha62tvaf7480n2vm3pvzdae5x45mk29d9ev"
},
{
"reason": "missing_signature",
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qh3gr8tcfw5pgkey767hj4cgq84gtam0285vt5dmqptkylq2zum72fmw3turx6x53m8gruhe8twc9qlydp5ch205ff6r5ht3ztwhtveg4wvl5p7jgqpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpq87s86sxqyp9jkjm487s86aqh7xt9vwkl2dumj44qx6elqkxt3gxkl6r29rn0ace0u0ul6ht44qmjsr0fnjjdf4q6nst8f37mzdgxt33ewvnnhlp576a6u3j6vkq927dn3zt2we3wqxfa58rxvxwgf0h7x86ct7p64n2x3rggwf8fu8rz60esv8jcvrse7adz077xrhrdnt3gdv3ze8dzgzq48x4jhykss9vnxv2klafcaplh8dakrvdgma78tfnxsqt6pln9rwdkcqg0y9un5kml5p7cc8jm6h47v978nkcnlruyn2z9qvm7p40pey2x9prh0gwyc608s77vlcpj8p4qqpyw42t359pj6yc572t700gnxp9wytcmyc6l7m9fuxp5l5jkaaeuwzrv58ke4lwjjm8204fmu6nxugtqn0wdp4dxaj3tfwt"
},
{
"reason": "missing_proof_preimage",
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qva0p96s9zmynmt672hpqq74p0hdag733w3hvq9wcnupgtn0ef8d690svmg6j8vaq0jlyadmq5ru35xnzaf7398gwjawyfd6adn9z4en7s86sxqyp9jkjm487s86aqh7xt9vwkl2dumj44qx6elqkxt3gxkl6r29rn0ace0u0ul6ht44qmjsr0fnjjdf4q6nst8f37mzdgxt33ewvnnhlp576a6u3j6vkq927dn3zt2we3wqxfa58rxvxwgf0h7x86ct7p64n2x3rggwf8fu8rz60esv8jcvrse7adz077xrhrdnt3gdv3ze8dzgzq48x4jhykss9vnxv2klafcaplh8dakrvdgma78tfnxsqt6pln9rwdkcqg0y9un5kml5p7cc8jm6h47v978nkcnlruyn2z9qvm7p40pey2x9prh0gwyc608s77vlcpj8p4qqpyw42t359pj6yc572t700gnxp9wytcmyc6l7m9fuxp5l5jkaaeuwzrv58ke4lwjjm8204fmu6nxugtqn0wdp4dxaj3tfwt"
},
{
"reason": "missing_proof_missing_hashes",
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qva0p96s9zmynmt672hpqq74p0hdag733w3hvq9wcnupgtn0ef8d690svmg6j8vaq0jlyadmq5ru35xnzaf7398gwjawyfd6adn9z4en7s86fqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyql6ql2qcqsyk26tw5l6qlvvredat6lxzlremvfl37zf4pzsxdlq6hsuj9rzs3mh58zvd8nc00x0uqers6sqqj8249c6zsedzv2099l8h5fnqjhz9udjvd0ldj57rq606ftw7u78ppk2rmv6lhffdn4865a7dfnwy9sfhhxs6knweg45h9s"
},
{
"reason": "missing_proof_leaf_hashes",
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qva0p96s9zmynmt672hpqq74p0hdag733w3hvq9wcnupgtn0ef8d690svmg6j8vaq0jlyadmq5ru35xnzaf7398gwjawyfd6adn9z4en7s86fqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyql6ql2qcqsyk26tw5l6qlt5zlcev436mafhnw2k5qmt8uzcew9q6mlgdg5wdlhr9l3lnl2awk5rw2qdaxw2f4x5r2wpvax8mvf4qewx89ejwwluxnmthtjxtfjcq4tekwyfdfmx9cqe8ksuveseep97lccltp0c82kdg6ydppeya8suvtflxps7tpswr8m45flmccwudkdw9p4jytya5fqgz5u6k2uj6zq4jve32ml48r587uahkcd34r0hcadxv6qp0g87v5dekmqppushjwjmv"
},
{
"reason": "missing_proof_signature",
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84ccel5p7jgqpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpq87s86sxqyp9jkjm487s86aqh7xt9vwkl2dumj44qx6elqkxt3gxkl6r29rn0ace0u0ul6ht44qmjsr0fnjjdf4q6nst8f37mzdgxt33ewvnnhlp576a6u3j6vkq927dn3zt2we3wqxfa58rxvxwgf0h7x86ct7p64n2x3rggwf8fu8rz60esv8jcvrse7adz077xrhrdnt3gdv3ze8dzgzq48x4jhykss9vnxv2klafcaplh8dakrvdgma78tfnxsqt6pln9rwdkcqg0y9un5kml5p7cc8jm6h47v978nkcnlruyn2z9qvm7p40pey2x9prh0gwyc608s77vlcpj8p4qqpyw42t359pj6yc572t700gnxp9wytcmyc6l7m9fuxp5l5jkaaeuwzrv58ke4lwjjm8204fmu6nxugtqn0wdp4dxaj3tfwt"
},
{
"reason": "wrong_proof_preimage",
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qva0p96s9zmynmt672hpqq74p0hdag733w3hvq9wcnupgtn0ef8d690svmg6j8vaq0jlyadmq5ru35xnzaf7398gwjawyfd6adn9z4en7s86fqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq06ql2qcqsyk26tw5l6qlt5zlcev436mafhnw2k5qmt8uzcew9q6mlgdg5wdlhr9l3lnl2awk5rw2qdaxw2f4x5r2wpvax8mvf4qewx89ejwwluxnmthtjxtfjcq4tekwyfdfmx9cqe8ksuveseep97lccltp0c82kdg6ydppeya8suvtflxps7tpswr8m45flmccwudkdw9p4jytya5fqgz5u6k2uj6zq4jve32ml48r587uahkcd34r0hcadxv6qp0g87v5dekmqppushjwjm07s8mrq7t027heshc7wmz0u0sjdgg5pn0cx4u8y3gc5ywaapcnrfu7rmenlqxgux5qqy364fwxs5xtgnznef0eaazvcy4c30rvnrtlmv48scxn7j2mhh83cgdjs7mxha62tvaf7480n2vm3pvzdae5x45mk29d9ev"
},
{
"reason": "proof_omitted_tlvs_not_ascending",
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qva0p96s9zmynmt672hpqq74p0hdag733w3hvq9wcnupgtn0ef8d690svmg6j8vaq0jlyadmq5ru35xnzaf7398gwjawyfd6adn9z4en7s86fqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyql6ql2qcqsyk2649dl6qlt5zlcev436mafhnw2k5qmt8uzcew9q6mlgdg5wdlhr9l3lnl2awk5rw2qdaxw2f4x5r2wpvax8mvf4qewx89ejwwluxnmthtjxtfjcq4tekwyfdfmx9cqe8ksuveseep97lccltp0c82kdg6ydppeya8suvtflxps7tpswr8m45flmccwudkdw9p4jytya5fqgz5u6k2uj6zq4jve32ml48r587uahkcd34r0hcadxv6qp0g87v5dekmqppushjwjm07s8mrq7t027heshc7wmz0u0sjdgg5pn0cx4u8y3gc5ywaapcnrfu7rmenlqxgux5qqy364fwxs5xtgnznef0eaazvcy4c30rvnrtlmv48scxn7j2mhh83cgdjs7mxha62tvaf7480n2vm3pvzdae5x45mk29d9ev"
},
{
"reason": "proof_omitted_tlvs_contains_zero",
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qva0p96s9zmynmt672hpqq74p0hdag733w3hvq9wcnupgtn0ef8d690svmg6j8vaq0jlyadmq5ru35xnzaf7398gwjawyfd6adn9z4en7s86fqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyql6ql2quqqzqjetfd6nlgrawstlr9jk8t04x7de26srdvlstr9c5rt0ap4z3eh7uvh7870at466sdegph5eefx56sdfc9n5cld3x5r9ccuhxfemls60dwawgedxtqz40xec3948vchqry76r3nxr8yyhmlrrav9lqa2e4rg35y8yn57r33d8ucxrevxpcvlwk38l0rpm3ke4c5xkg3vnk3ypq2nn2etjtggzkfnx9t075uwslmnk7mpkx5d7lr45engq9aqlej3hxmvqy8jz7f6tdl6qlvvredat6lxzlremvfl37zf4pzsxdlq6hsuj9rzs3mh58zvd8nc00x0uqers6sqqj8249c6zsedzv2099l8h5fnqjhz9udjvd0ldj57rq606ftw7u78ppk2rmv6lhffdn4865a7dfnwy9sfhhxs6knweg45h9s"
},
{
"reason": "proof_omitted_tlvs_contains_signature_field",
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qva0p96s9zmynmt672hpqq74p0hdag733w3hvq9wcnupgtn0ef8d690svmg6j8vaq0jlyadmq5ru35xnzaf7398gwjawyfd6adn9z4en7s86fqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyql6ql2quqsyk26tw5lrlgrawstlr9jk8t04x7de26srdvlstr9c5rt0ap4z3eh7uvh7870at466sdegph5eefx56sdfc9n5cld3x5r9ccuhxfemls60dwawgedxtqz40xec3948vchqry76r3nxr8yyhmlrrav9lqa2e4rg35y8yn57r33d8ucxrevxpcvlwk38l0rpm3ke4c5xkg3vnk3ypq2nn2etjtggzkfnx9t075uwslmnk7mpkx5d7lr45engq9aqlej3hxmvqy8jz7f6tdl6qlvvredat6lxzlremvfl37zf4pzsxdlq6hsuj9rzs3mh58zvd8nc00x0uqers6sqqj8249c6zsedzv2099l8h5fnqjhz9udjvd0ldj57rq606ftw7u78ppk2rmv6lhffdn4865a7dfnwy9sfhhxs6knweg45h9s"
},
{
"reason": "proof_omitted_tlvs_contains_proof_field",
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qva0p96s9zmynmt672hpqq74p0hdag733w3hvq9wcnupgtn0ef8d690svmg6j8vaq0jlyadmq5ru35xnzaf7398gwjawyfd6adn9z4en7s86fqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyql6ql2pyqsyk26tw5l6qlfl5p7hg9l3jetr4h6n0xu4dgpkk0c93ju2p4h7s63gumlwxtlrl8746adgxu5qm6vu5n2dgx5uze6v0kcn2pjuvwtnyualcd8khwhyvkn9sp2hnvugj6nkvtspj0dpcenpnjztal337kzlsw4v635g6zrjf60pcckn7vrpukrqux0htgnlh3sacmv6u2rtygkfmgjqs9fe4v4e95yptyenz4hl2w8g0aem0dsmr2xl0366ve5qz7s0uegmndkqzrep0ya9klaq0kxpuk74a0np03uakylclpy6s3grxlsdtcwfz33ggam6r3xxneu8hn87qv3cdgqqfr42judpgvk3x98jjlnm6yesft3z7xexxhlke20psd8ay4h0w0rssm9pakd0m55ke6na2wlx5ehzzcymmngdtfhv526tjc"
},
{
"reason": "proof_omitted_tlvs_contains_high_field",
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qva0p96s9zmynmt672hpqq74p0hdag733w3hvq9wcnupgtn0ef8d690svmg6j8vaq0jlyadmq5ru35xnzaf7398gwjawyfd6adn9z4en7s86fqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyql6ql2pvqsyk26tw5lamnt9qq06qlt5zlcev436mafhnw2k5qmt8uzcew9q6mlgdg5wdlhr9l3lnl2awk5rw2qdaxw2f4x5r2wpvax8mvf4qewx89ejwwluxnmthtjxtfjcq4tekwyfdfmx9cqe8ksuveseep97lccltp0c82kdg6ydppeya8suvtflxps7tpswr8m45flmccwudkdw9p4jytya5fqgz5u6k2uj6zq4jve32ml48r587uahkcd34r0hcadxv6qp0g87v5dekmqppushjwjm07s8mrq7t027heshc7wmz0u0sjdgg5pn0cx4u8y3gc5ywaapcnrfu7rmenlqxgux5qqy364fwxs5xtgnznef0eaazvcy4c30rvnrtlmv48scxn7j2mhh83cgdjs7mxha62tvaf7480n2vm3pvzdae5x45mk29d9ev"
},
{
"reason": "proof_omitted_tlvs_contains_included_tlv_field",
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qva0p96s9zmynmt672hpqq74p0hdag733w3hvq9wcnupgtn0ef8d690svmg6j8vaq0jlyadmq5ru35xnzaf7398gwjawyfd6adn9z4en7s86fqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyql6ql2quqsykzetfd6nlgrawstlr9jk8t04x7de26srdvlstr9c5rt0ap4z3eh7uvh7870at466sdegph5eefx56sdfc9n5cld3x5r9ccuhxfemls60dwawgedxtqz40xec3948vchqry76r3nxr8yyhmlrrav9lqa2e4rg35y8yn57r33d8ucxrevxpcvlwk38l0rpm3ke4c5xkg3vnk3ypq2nn2etjtggzkfnx9t075uwslmnk7mpkx5d7lr45engq9aqlej3hxmvqy8jz7f6tdl6qlvvredat6lxzlremvfl37zf4pzsxdlq6hsuj9rzs3mh58zvd8nc00x0uqers6sqqj8249c6zsedzv2099l8h5fnqjhz9udjvd0ldj57rq606ftw7u78ppk2rmv6lhffdn4865a7dfnwy9sfhhxs6knweg45h9s"
},
{
"reason": "proof_omitted_tlvs_not_sequential",
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qva0p96s9zmynmt672hpqq74p0hdag733w3hvq9wcnupgtn0ef8d690svmg6j8vaq0jlyadmq5ru35xnzaf7398gwjawyfd6adn9z4en7s86fqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyql6ql2qcqsyktyv4n06qlt5zlcev436mafhnw2k5qmt8uzcew9q6mlgdg5wdlhr9l3lnl2awk5rw2qdaxw2f4x5r2wpvax8mvf4qewx89ejwwluxnmthtjxtfjcq4tekwyfdfmx9cqe8ksuveseep97lccltp0c82kdg6ydppeya8suvtflxps7tpswr8m45flmccwudkdw9p4jytya5fqgz5u6k2uj6zq4jve32ml48r587uahkcd34r0hcadxv6qp0g87v5dekmqppushjwjm07s8mrq7t027heshc7wmz0u0sjdgg5pn0cx4u8y3gc5ywaapcnrfu7rmenlqxgux5qqy364fwxs5xtgnznef0eaazvcy4c30rvnrtlmv48scxn7j2mhh83cgdjs7mxha62tvaf7480n2vm3pvzdae5x45mk29d9ev"
},
{
"reason": "proof_leaf_hashes_too_few",
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qva0p96s9zmynmt672hpqq74p0hdag733w3hvq9wcnupgtn0ef8d690svmg6j8vaq0jlyadmq5ru35xnzaf7398gwjawyfd6adn9z4en7s86fqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyql6ql2qcqsyk26tw5l6qlt5zlcev436mafhnw2k5qmt8uzcew9q6mlgdg5wdlhr9l3lnl2awk5rw2qdaxw2f4x5r2wpvax8mvf4qewx89ejwwluxnmthtjxtfjcq4tekwyfdfmx9cqe8ksuveseep97lccltp0c82kdg6ydppeya8suvtflxps7tpswr8m45flmccwudkdw9p4jytya5fqgz5u6k2uj6zq4jve32ml48r587uahkcd34r0hcadxv6qp0g87v5dekmqppushjwjm07s8mzq7t027heshc7wmz0u0sjdgg5pn0cx4u8y3gc5ywaapcnrfu7rmenlqxgux5qqy364fwxs5xtgnznef0eaazvcy4c30rvnrtlmv48scxs"
},
{
"reason": "proof_leaf_hashes_too_many",
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qva0p96s9zmynmt672hpqq74p0hdag733w3hvq9wcnupgtn0ef8d690svmg6j8vaq0jlyadmq5ru35xnzaf7398gwjawyfd6adn9z4en7s86fqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyql6ql2qcqsyk26tw5l6qlt5zlcev436mafhnw2k5qmt8uzcew9q6mlgdg5wdlhr9l3lnl2awk5rw2qdaxw2f4x5r2wpvax8mvf4qewx89ejwwluxnmthtjxtfjcq4tekwyfdfmx9cqe8ksuveseep97lccltp0c82kdg6ydppeya8suvtflxps7tpswr8m45flmccwudkdw9p4jytya5fqgz5u6k2uj6zq4jve32ml48r587uahkcd34r0hcadxv6qp0g87v5dekmqppushjwjm07s8myq7t027heshc7wmz0u0sjdgg5pn0cx4u8y3gc5ywaapcnrfu7rmenlqxgux5qqy364fwxs5xtgnznef0eaazvcy4c30rvnrtlmv48scxn7j2mhh83cgdjs7mxha62tvaf7480n2vm3pvzdae5x45mk29d9evqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"
},
{
"reason": "proof_missing_hashes_too_few",
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qva0p96s9zmynmt672hpqq74p0hdag733w3hvq9wcnupgtn0ef8d690svmg6j8vaq0jlyadmq5ru35xnzaf7398gwjawyfd6adn9z4en7s86fqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyql6ql2qcqsyk26tw5l6qltszlcev436mafhnw2k5qmt8uzcew9q6mlgdg5wdlhr9l3lnl2awk5rw2qdaxw2f4x5r2wpvax8mvf4qewx89ejwwluxnmthtjxtfjcq4tekwyfdfmx9cqe8ksuveseep97lccltp0c82kdg6ydppeya8suvtflxps7tpswr8m45flmccwudkdw9p4jytya5fqgz5u6k2uj6zq4j0aq0kxpuk74a0np03uakylclpy6s3grxlsdtcwfz33ggam6r3xxneu8hn87qv3cdgqqfr42judpgvk3x98jjlnm6yesft3z7xexxhlke20psd8ay4h0w0rssm9pakd0m55ke6na2wlx5ehzzcymmngdtfhv526tjc"
},
{
"reason": "proof_missing_hashes_too_many",
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qva0p96s9zmynmt672hpqq74p0hdag733w3hvq9wcnupgtn0ef8d690svmg6j8vaq0jlyadmq5ru35xnzaf7398gwjawyfd6adn9z4en7s86fqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyql6ql2qcqsyk26tw5l6qltczlcev436mafhnw2k5qmt8uzcew9q6mlgdg5wdlhr9l3lnl2awk5rw2qdaxw2f4x5r2wpvax8mvf4qewx89ejwwluxnmthtjxtfjcq4tekwyfdfmx9cqe8ksuveseep97lccltp0c82kdg6ydppeya8suvtflxps7tpswr8m45flmccwudkdw9p4jytya5fqgz5u6k2uj6zq4jve32ml48r587uahkcd34r0hcadxv6qp0g87v5dekmqppushjwjmvqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplgra3s09h40tuctu08d3878cfx5y2qehur27rjg5v2z8w7suf3570pauelsrywr2qqzga25hrg2r95f3fu5hu773xvz2ugh3kf34lak2ncvrflf9dmmncuyxeg0dnt7a99kw5l2nhe4xdcskpx7u6r26dm9zkjuk"
},
{
"reason": "wrong_invoice_signature",
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9nxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qva0p96s9zmynmt672hpqq74p0hdag733w3hvq9wcnupgtn0ef8d690svmg6j8vaq0jlyadmq5ru35xnzaf7398gwjawyfd6adn9z4en7s86fqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyql6ql2qcqsyk26tw5l6qlt5zlcev436mafhnw2k5qmt8uzcew9q6mlgdg5wdlhr9l3lnl2awk5rw2qdaxw2f4x5r2wpvax8mvf4qewx89ejwwluxnmthtjxtfjcq4tekwyfdfmx9cqe8ksuveseep97lccltp0c82kdg6ydppeya8suvtflxps7tpswr8m45flmccwudkdw9p4jytya5fqgz5u6k2uj6zq4jve32ml48r587uahkcd34r0hcadxv6qp0g87v5dekmqppushjwjm07s8mrq7t027heshc7wmz0u0sjdgg5pn0cx4u8y3gc5ywaapcnrfu7rmenlqxgux5qqy364fwxs5xtgnznef0eaazvcy4c30rvnrtlmv48scxn7j2mhh83cgdjs7mxha62tvaf7480n2vm3pvzdae5x45mk29d9ev"
},
{
"reason": "wrong_proof_signature",
"bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qvl0p96s9zmynmt672hpqq74p0hdag733w3hvq9wcnupgtn0ef8d690svmg6j8vaq0jlyadmq5ru35xnzaf7398gwjawyfd6adn9z4en7s86fqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyql6ql2qcqsyk26tw5l6qlt5zlcev436mafhnw2k5qmt8uzcew9q6mlgdg5wdlhr9l3lnl2awk5rw2qdaxw2f4x5r2wpvax8mvf4qewx89ejwwluxnmthtjxtfjcq4tekwyfdfmx9cqe8ksuveseep97lccltp0c82kdg6ydppeya8suvtflxps7tpswr8m45flmccwudkdw9p4jytya5fqgz5u6k2uj6zq4jve32ml48r587uahkcd34r0hcadxv6qp0g87v5dekmqppushjwjm07s8mrq7t027heshc7wmz0u0sjdgg5pn0cx4u8y3gc5ywaapcnrfu7rmenlqxgux5qqy364fwxs5xtgnznef0eaazvcy4c30rvnrtlmv48scxn7j2mhh83cgdjs7mxha62tvaf7480n2vm3pvzdae5x45mk29d9ev"
},
{
"reason": "contains_invreq_metadata",
"bech32": "lnp1qqq5ykppqvjx204vgdzgsqpvcp4mldl3plscny0rt707gvpdh6ndydfacz43e2pqwtxkappzcsrlkmgfs6g0zyct0hkhashh7hsaxz7e65slq9fkx7fmqggzf0p2xyn9z5ls0ecwpw4ssujwdwz7y9lce43ge6mzjapy0w6fxwp0qsq2xv3y269k4tnw6ffqz27hlcg89sp7hh98lfz0jkcr78xsn03gkz5re8pjzpvh3nvqmgrgj7tx9jq05q8ly5xvm3x33dcfllgu0t33nu2qe67zt4q29kf8kh4u4wzqpa2zlwm63arzarwcq2a38czshxljjwm52lqek34ywe6ql97f6mkpg8ergdx96naz2wsa96ugjm46mx29tn8aq05jqqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpl5p75pspqfv45kafl5p7hg9l3jetr4h6n0xu4dgpkk0c93ju2p4h7s63gumlwxtlrl8746adgxu5qm6vu5n2dgx5uze6v0kcn2pjuvwtnyualcd8khwhyvkn9sp2hnvugj6nkvtspj0dpcenpnjztal337kzlsw4v635g6zrjf60pcckn7vrpukrqux0htgnlh3sacmv6u2rtygkfmgjqs9fe4v4e95yptyenz4hl2w8g0aem0dsmr2xl0366ve5qz7s0uegmndkqzrep0ya9klaq0kxpuk74a0np03uakylclpy6s3grxlsdtcwfz33ggam6r3xxneu8hn87qv3cdgqqfr42judpgvk3x98jjlnm6yesft3z7xexxhlke20psd8ay4h0w0rssm9pakd0m55ke6na2wlx5ehzzcymmngdtfhv526tjc"
}
]
}