diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/UInt64.scala b/eclair-core/src/main/scala/fr/acinq/eclair/UInt64.scala index 597c03f346..312b3222c0 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/UInt64.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/UInt64.scala @@ -17,14 +17,13 @@ package fr.acinq.eclair import com.google.common.primitives.UnsignedLongs -import scodec.bits.ByteVector -import scodec.bits.HexStringSyntax +import scodec.bits.{ByteVector, HexStringSyntax} case class UInt64(private val underlying: Long) extends Ordered[UInt64] { override def compare(o: UInt64): Int = UnsignedLongs.compare(underlying, o.underlying) private def compare(other: MilliSatoshi): Int = other.toLong match { - case l if l < 0 => 1 // if @param 'other' is negative then is always smaller than 'this' + case l if l < 0 => 1 // if @param 'other' is negative then is always smaller than 'this' case _ => compare(UInt64(other.toLong)) // we must do an unsigned comparison here because the uint64 can exceed the capacity of MilliSatoshi class } @@ -33,6 +32,9 @@ case class UInt64(private val underlying: Long) extends Ordered[UInt64] { def <=(other: MilliSatoshi): Boolean = compare(other) <= 0 def >=(other: MilliSatoshi): Boolean = compare(other) >= 0 + def +(other: Int) = UInt64(underlying + other) + def +(other: Long) = UInt64(underlying + other) + def toByteVector: ByteVector = ByteVector.fromLong(underlying) def toBigInt: BigInt = (BigInt(underlying >>> 1) << 1) + (underlying & 1) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/offer/PayerProof.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/offer/PayerProof.scala new file mode 100644 index 0000000000..f8756bb451 --- /dev/null +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/offer/PayerProof.scala @@ -0,0 +1,387 @@ +/* + * Copyright 2026 ACINQ SAS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fr.acinq.eclair.payment.offer + +import fr.acinq.bitcoin.Bech32 +import fr.acinq.bitcoin.scalacompat.Crypto.PrivateKey +import fr.acinq.bitcoin.scalacompat.{ByteVector32, Crypto, LexicographicalOrdering} +import fr.acinq.eclair.UInt64 +import fr.acinq.eclair.payment.Bolt12Invoice +import fr.acinq.eclair.wire.protocol.CommonCodecs.varint +import fr.acinq.eclair.wire.protocol.OfferTypes._ +import fr.acinq.eclair.wire.protocol.OnionRoutingCodecs.{InvalidTlvPayload, InvalidTlvValue, MissingRequiredTlv} +import fr.acinq.eclair.wire.protocol._ +import scodec.bits.ByteVector + +import scala.util.{Failure, Try} + +/** + * Created by t-bast on 28/05/2026. + */ + +case class PayerProof(records: TlvStream[PayerProofTlv]) { + + import PayerProof._ + + def verifySigs(): Boolean = { + PayerProof.validate(records) match { + case Left(_) => false + case Right(_) => + // The proof signature must be valid: it signs all non-signature TLVs using the invreq_payer_id. + val payerSig = records.get[ProofSignature].get.signature + val payerId = records.get[InvoiceRequestPayerId].get.publicKey + val proofRootHash = rootHash(TlvStream(records.records.filterNot(r => r.isInstanceOf[Signature] || r.isInstanceOf[ProofSignature]), records.unknown), OfferCodecs.payerProofCodec) + if (!verifySchnorr(PayerProof.signatureTag, proofRootHash, payerSig, payerId)) { + return false + } + // The proof must contain enough data to reconstruct the invoice merkle root. + val leafNonces = records.get[LeafHashes].map(_.hashes).getOrElse(Nil) + val markers = records.get[OmittedTlvs].map(_.missing).getOrElse(Nil) + val missingHashes = records.get[MissingHashes].map(_.missing).getOrElse(Nil) + // Disclosed invoice merkle-tree leaves: invoice fields (excluding the invoice signature) and unknown TLVs. + val knownLeaves = records.records.toSeq + .filterNot(_.isInstanceOf[Signature]) + .collect { case tlv: InvoiceTlv => tlv } + .map(tlv => TlvCodecs.genericTlv.decode(OfferCodecs.payerProofTlvCodec.encode(tlv).require).require.value) + val disclosedLeaves = (knownLeaves ++ records.unknown).sortBy(_.tag) + val tagSet = disclosedLeaves.map(_.tag).toSet + // The omitted_tlvs field cannot contain the number of an included invoice TLV field. + // The leaf_hashes field must contain exactly one hash for each non-signature invoice TLV field. + if (leafNonces.size != disclosedLeaves.size || markers.exists(tagSet.contains)) { + return false + } + // We combine disclosed and omitted leaves and sort them. + val allLeavesExceptMetadata = disclosedLeaves + .zip(leafNonces) + .map { case (tlv, nonce) => (tlv.tag, Some(LeafWithNonce(tlv, nonce))) } + .appendedAll(markers.map(m => (m, None))) + .sortBy(_._1) + // Each omitted leaf's marker must equal the previous leaf's value + 1 (with 0 implied for the always-omitted invreq_metadata at tag 0). + val omittedLeavesOk = allLeavesExceptMetadata.zipWithIndex.forall { + case ((_, Some(_)), _) => true + case ((marker, None), idx) if idx == 0 => marker == UInt64(1) + case ((marker, None), idx) => + val previous = allLeavesExceptMetadata(idx - 1)._1 + val expected = if (previous == UInt64(239)) UInt64(1_000_000_000) else previous + 1 + marker == expected + } + if (!omittedLeavesOk) { + return false + } + // The first leaf is always the implicitly-omitted invreq_metadata. + val allLeaves = (Option.empty[LeafWithNonce] +: allLeavesExceptMetadata.map(_._2)).toIndexedSeq + PayerProofTree.merkleRoot(allLeaves, missingHashes) match { + case Left(_) => false + case Right(invoiceRootHash) => + // The invoice signature must be valid against invoice_node_id over the reconstructed merkle root. + val invoiceSig = records.get[Signature].get.signature + val invoiceNodeId = records.get[InvoiceNodeId].get.nodeId + verifySchnorr(Bolt12Invoice.signatureTag, invoiceRootHash, invoiceSig, invoiceNodeId) + } + } + } + + override def toString: String = { + val data = OfferCodecs.payerProofCodec.encode(records).require.bytes + Bech32.encodeBytes(PayerProof.hrp, data.toArray, Bech32.Encoding.Beck32WithoutChecksum) + } + +} + +object PayerProof { + + val hrp = "lnp" + val signatureTag: ByteVector = ByteVector(("lightning" + "payer_proof" + "proof_signature").getBytes) + + /** Specifies which optional fields from the invoice should be included in the payer proof. */ + case class IncludedFields(offerChains: Boolean = false, + offerMetadata: Boolean = false, + offerCurrency: Boolean = false, + offerAmount: Boolean = false, + offerDescription: Boolean = false, + offerFeatures: Boolean = false, + offerAbsoluteExpiry: Boolean = false, + offerPaths: Boolean = false, + offerIssuer: Boolean = false, + offerQuantityMax: Boolean = false, + offerNodeId: Boolean = false, + invoiceRequestChain: Boolean = false, + invoiceRequestAmount: Boolean = false, + invoiceRequestFeatures: Boolean = false, + invoiceRequestQuantity: Boolean = false, + invoiceRequestPayerNote: Boolean = false, + invoicePaths: Boolean = false, + invoiceAccountable: Boolean = false, + invoiceBlindedPay: Boolean = false, + invoiceCreatedAt: Boolean = false, + invoiceRelativeExpiry: Boolean = false, + invoiceAmount: Boolean = false, + invoiceFallbacks: Boolean = false, + unknown: Set[UInt64] = Set.empty) + + def create(invoice: Bolt12Invoice, preimage: ByteVector32, payerKey: PrivateKey, fields: IncludedFields, note_opt: Option[String]): PayerProof = { + // Valid Bolt12 invoices always contain the invreq_metadata field: it is used as a hashing nonce to prevent brute-forcing private fields. + val invreqMetadata = invoice.records.get[InvoiceRequestMetadata].get + // We select invoice fields that we want to include in our payer proof. + val knownLeaves: Set[(InvoiceTlv, Boolean)] = invoice.records.records + .filterNot(_.isInstanceOf[Signature]) + .map { + case tlv: OfferChains => (tlv, fields.offerChains) + case tlv: OfferMetadata => (tlv, fields.offerMetadata) + case tlv: OfferCurrency => (tlv, fields.offerCurrency) + case tlv: OfferAmount => (tlv, fields.offerAmount) + case tlv: OfferDescription => (tlv, fields.offerDescription) + case tlv: OfferFeatures => (tlv, fields.offerFeatures) + case tlv: OfferAbsoluteExpiry => (tlv, fields.offerAbsoluteExpiry) + case tlv: OfferPaths => (tlv, fields.offerPaths) + case tlv: OfferIssuer => (tlv, fields.offerIssuer) + case tlv: OfferQuantityMax => (tlv, fields.offerQuantityMax) + case tlv: OfferNodeId => (tlv, fields.offerNodeId) + case tlv: InvoiceRequestMetadata => (tlv, false) + case tlv: InvoiceRequestChain => (tlv, fields.invoiceRequestChain) + case tlv: InvoiceRequestAmount => (tlv, fields.invoiceRequestAmount) + case tlv: InvoiceRequestFeatures => (tlv, fields.invoiceRequestFeatures) + case tlv: InvoiceRequestQuantity => (tlv, fields.invoiceRequestQuantity) + case tlv: InvoiceRequestPayerId => (tlv, true) + case tlv: InvoiceRequestPayerNote => (tlv, fields.invoiceRequestPayerNote) + case tlv: InvoicePaths => (tlv, fields.invoicePaths) + case tlv: InvoiceAccountable => (tlv, fields.invoiceAccountable) + case tlv: InvoiceBlindedPay => (tlv, fields.invoiceBlindedPay) + case tlv: InvoiceCreatedAt => (tlv, fields.invoiceCreatedAt) + case tlv: InvoiceRelativeExpiry => (tlv, fields.invoiceRelativeExpiry) + case tlv: InvoicePaymentHash => (tlv, true) + case tlv: InvoiceAmount => (tlv, fields.invoiceAmount) + case tlv: InvoiceFallbacks => (tlv, fields.invoiceFallbacks) + case tlv: InvoiceFeatures => (tlv, true) + case tlv: InvoiceNodeId => (tlv, true) + case tlv: Signature => (tlv, false) // already filtered out above + } + // We must also include unknown TLVs, otherwise the merkle tree will be incomplete. + val unknownLeaves: Set[(GenericTlv, Boolean)] = invoice.records.unknown.map { + tlv => (tlv, fields.unknown.contains(tlv.tag)) + } + // We convert everything to generic TLVs to combine known and unknown leaves. + val leaves = knownLeaves + .map { case (tlv, included) => (TlvCodecs.genericTlv.decode(OfferCodecs.payerProofTlvCodec.encode(tlv).require).require.value, included) } + .concat(unknownLeaves) + .toSeq + .sortBy(_._1.tag) + .map { case (tlv, included) => PayerProofTree.Leaf(tlv, included) } + // We include the leaf nonce for each invoice TLV we include in the payer proof. + val leafNonces = leaves.collect { + case leaf if leaf.included => PayerProofTree.leafNonce(invreqMetadata, leaf.tlv) + }.toList + val omittedTlvs = leaves.foldLeft((Seq.empty[UInt64], Option.empty[UInt64])) { + case ((omitted, _), leaf) if leaf.included => + // This TLV is included in the payer proof, so we don't add an entry (it is not omitted). + // However, we tell the next TLV that they need to use a marker higher than the current TLV tag. + (omitted, Some(leaf.tlv.tag)) + case ((omitted, previousTlv_opt), leaf) if leaf.tlv.tag == UInt64(0) => + // The invreq_metadata field is always implicitly omitted (not included in omitted fields). + (omitted, previousTlv_opt) + case ((omitted, previousTlv_opt), _) => + // This TLV is not included in the payer proof: we add an entry with a marker. + val markerNumber = previousTlv_opt match { + case Some(tag) => tag + 1 + case None => omitted.lastOption match { + case Some(tag) if tag == UInt64(239) => UInt64(1_000_000_000) + case Some(tag) => tag + 1 + case None => UInt64(1) + } + } + (omitted :+ markerNumber, None) + }._1.toList + val proofTree = PayerProofTree(leaves) + val missingHashes = PayerProofTree.computeMissingHashes(proofTree, invreqMetadata) + val includedInvoiceTlvs = knownLeaves.collect { case (tlv, true) => tlv }.toSet[PayerProofTlv] + val payerProofTlvs = Set( + Some(InvoicePreimage(preimage)), + Some(LeafHashes(leafNonces)), + if (omittedTlvs.nonEmpty) Some(OmittedTlvs(omittedTlvs)) else None, + if (missingHashes.nonEmpty) Some(MissingHashes(missingHashes)) else None, + note_opt.map(note => ProofNote(note)), + ).flatten[PayerProofTlv] + val includedUnknownTlvs = unknownLeaves.collect { case (tlv, true) => tlv } + // The invoice signature is included in the final payer proof but is excluded from the proof merkle root (signature + // fields are never signed). Note that the invreq_metadata isn't disclosed in the proof: we will use the first TLV + // as a hashing nonce instead. + val proofSig = signSchnorr(signatureTag, rootHash(TlvStream[PayerProofTlv](includedInvoiceTlvs ++ payerProofTlvs, includedUnknownTlvs), OfferCodecs.payerProofCodec), payerKey) + val finalTlvs = TlvStream[PayerProofTlv](includedInvoiceTlvs ++ invoice.records.get[Signature].toSet ++ payerProofTlvs + ProofSignature(proofSig), includedUnknownTlvs) + PayerProof(finalTlvs) + } + + /** When validating a proof, the leaf nonce is directly provided to avoid brute-forcing omitted fields. */ + private case class LeafWithNonce(tlv: GenericTlv, nonce: ByteVector32) + + sealed trait PayerProofTree { + /** True if all leaves of that subtree are included. */ + def included: Boolean + + /** The invreq_metadata (mandatory) field is used as a nonce to provide randomness. */ + def hash(invreqMetadata: InvoiceRequestMetadata): ByteVector32 + } + + object PayerProofTree { + /** NB: leaves must usually be ordered by tag by the caller. */ + def apply(leaves: Seq[Leaf]): PayerProofTree = { + merkleTree(leaves, 0, leaves.length) + } + + case class Leaf(tlv: GenericTlv, included: Boolean) extends PayerProofTree { + override def hash(invreqMetadata: InvoiceRequestMetadata): ByteVector32 = { + val nonce = leafNonce(invreqMetadata, tlv) + val encodedTlv = TlvCodecs.genericTlv.encode(tlv).require.bytes + combine(nonce, OfferTypes.hash(ByteVector("LnLeaf".getBytes), encodedTlv)) + } + } + + case class Branch(left: PayerProofTree, right: PayerProofTree, included: Boolean) extends PayerProofTree { + override def hash(invreqMetadata: InvoiceRequestMetadata): ByteVector32 = { + combine(left.hash(invreqMetadata), right.hash(invreqMetadata)) + } + } + + def computeMissingHashes(tree: PayerProofTree, invreqMetadata: InvoiceRequestMetadata): List[ByteVector32] = { + if (!tree.included) { + tree.hash(invreqMetadata) :: Nil + } else { + tree match { + case _: Leaf => Nil + case Branch(left, right, _) => + // Post-order DFS: at each internal node with exactly one fully-omitted branch, emit that branch's hash + // *after* recursing into the other (mixed) branch. + (left.included, right.included) match { + case (true, true) => computeMissingHashes(left, invreqMetadata) ++ computeMissingHashes(right, invreqMetadata) + case (false, true) => computeMissingHashes(right, invreqMetadata) :+ left.hash(invreqMetadata) + case (true, false) => computeMissingHashes(left, invreqMetadata) :+ right.hash(invreqMetadata) + case (false, false) => Nil // unreachable: parent's `included` would be false + } + } + } + } + + /** Leaves are combined with a nonce acting as a neighbor leaf. */ + def leafNonce(invreqMetadata: InvoiceRequestMetadata, tlv: GenericTlv): ByteVector32 = { + val encodedMetadata = OfferCodecs.payerProofTlvCodec.encode(invreqMetadata).require.bytes + val encodedTlvTag = varint.encode(tlv.tag).require.bytes + OfferTypes.hash(ByteVector("LnNonce".getBytes) ++ encodedMetadata, encodedTlvTag) + } + + private def combine(h1: ByteVector32, h2: ByteVector32): ByteVector32 = { + if (LexicographicalOrdering.isLessThan(h1, h2)) { + OfferTypes.hash(ByteVector("LnBranch".getBytes), h1 ++ h2) + } else { + OfferTypes.hash(ByteVector("LnBranch".getBytes), h2 ++ h1) + } + } + + private def merkleTree(leaves: Seq[Leaf], i: Int, j: Int): PayerProofTree = { + if (j - i == 1) { + leaves(i) + } else { + val k = i + previousPowerOfTwo(j - i) + val left = merkleTree(leaves, i, k) + val right = merkleTree(leaves, k, j) + Branch(left, right, left.included || right.included) + } + } + + def merkleRoot(leaves: Seq[Option[LeafWithNonce]], missingHashes: Seq[ByteVector32]): Either[String, ByteVector32] = { + merkleRoot(leaves, missingHashes, 0, leaves.length) match { + case Left(f) => Left(f) + case Right((_, remainingHashes)) if remainingHashes.nonEmpty => Left("invalid payer proof: missing_hashes not empty after recomputing invoice merkle root") + case Right((None, _)) => Left("invalid payer proof: cannot recompute invoice merkle root") + case Right((Some(root), _)) => Right(root) + } + } + + /** Recompute the merkle root, consuming missing_hashes in post-order DFS (smallest TLV first). */ + private def merkleRoot(leaves: Seq[Option[LeafWithNonce]], missingHashes: Seq[ByteVector32], i: Int, j: Int): Either[String, (Option[ByteVector32], Seq[ByteVector32])] = { + if (j - i == 1) { + // We've reached a leaf: it is either included or omitted, but doesn't consume any missing hash yet. + leaves(i) match { + case Some(leaf) => + val encodedTlv = TlvCodecs.genericTlv.encode(leaf.tlv).require.bytes + val hash = combine(leaf.nonce, OfferTypes.hash(ByteVector("LnLeaf".getBytes), encodedTlv)) + Right((Some(hash), missingHashes)) + case None => + Right((None, missingHashes)) + } + } else { + val k = i + previousPowerOfTwo(j - i) + merkleRoot(leaves, missingHashes, i, k) match { + case Left(f) => Left(f) + case Right((leftRoot_opt, remainingHashesAfterLeft)) => + merkleRoot(leaves, remainingHashesAfterLeft, k, j) match { + case Left(f) => Left(f) + case Right((rightRoot_opt, remainingHashes)) => + (leftRoot_opt, rightRoot_opt, remainingHashes.headOption) match { + // Both subtrees are (at least partially) included. + case (Some(left), Some(right), _) => Right((Some(combine(left, right)), remainingHashes)) + // Both subtrees are fully omitted: the parent tree is fully omitted as well. + case (None, None, _) => Right((None, remainingHashes)) + // One of the subtrees is omitted: we use missing_hashes to get its merkle root. + case (Some(left), None, Some(missingHash)) => Right((Some(combine(left, missingHash)), remainingHashes.tail)) + case (None, Some(right), Some(missingHash)) => Right((Some(combine(missingHash, right)), remainingHashes.tail)) + // One of the subtrees is omitted, but we don't have missing hashes to consume left. + case (_, _, None) => Left("cannot recompute invoice merkle root: missing_hashes is incomplete") + } + } + } + } + } + + private def previousPowerOfTwo(n: Int): Int = { + var p = 1 + while (p < n) { + p = p << 1 + } + p >> 1 + } + } + + private def validate(records: TlvStream[PayerProofTlv]): Either[InvalidTlvPayload, PayerProof] = { + if (records.get[InvoiceRequestPayerId].isEmpty) return Left(MissingRequiredTlv(UInt64(88))) + if (records.get[InvoicePaymentHash].isEmpty) return Left(MissingRequiredTlv(UInt64(168))) + if (records.get[InvoiceNodeId].isEmpty) return Left(MissingRequiredTlv(UInt64(176))) + if (records.get[Signature].isEmpty) return Left(MissingRequiredTlv(UInt64(240))) + if (records.get[ProofSignature].isEmpty) return Left(MissingRequiredTlv(UInt64(241))) + if (records.get[InvoicePreimage].isEmpty) return Left(MissingRequiredTlv(UInt64(1001))) + // The preimage must match the invoice's payment_hash. + if (Crypto.sha256(records.get[InvoicePreimage].get.preimage) != records.get[InvoicePaymentHash].get.hash) return Left(InvalidTlvValue(UInt64(1001))) + val omittedTlvs = records.get[OmittedTlvs].map(_.missing).getOrElse(Nil) + if (omittedTlvs.length != omittedTlvs.distinct.length) return Left(InvalidTlvValue(UInt64(1002))) + if (omittedTlvs.sorted != omittedTlvs) return Left(InvalidTlvValue(UInt64(1002))) + if (!omittedTlvs.forall(tag => (tag >= UInt64(1) && tag <= UInt64(239)) || (tag >= UInt64(1_000_000_000L) && tag <= UInt64(3_999_999_999L)))) return Left(InvalidTlvValue(UInt64(1002))) + Right(PayerProof(records)) + } + + def fromString(input: String): Try[PayerProof] = Try { + val triple = Bech32.decodeBytes(input.toLowerCase, true) + val prefix = triple.getFirst + val encoded = triple.getSecond + val encoding = triple.getThird + require(prefix == hrp) + require(encoding == Bech32.Encoding.Beck32WithoutChecksum) + val tlvs = OfferCodecs.payerProofCodec.decode(ByteVector(encoded).bits).require.value + validate(tlvs) match { + case Left(f) => return Failure(new IllegalArgumentException(f.toString)) + case Right(payerProof) => payerProof + } + } + +} diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/OfferCodecs.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/OfferCodecs.scala index 45dfcf56ba..d32cbd4eff 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/OfferCodecs.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/OfferCodecs.scala @@ -201,6 +201,54 @@ object OfferCodecs { .typecase(UInt64(240), signature) ).complete) + private val proofSig: Codec[ProofSignature] = tlvField(bytes64) + private val preimage: Codec[InvoicePreimage] = tlvField(bytes32) + private val omitted: Codec[OmittedTlvs] = tlvField(list(varint)) + private val missingHashes: Codec[MissingHashes] = tlvField(list(bytes32)) + private val leafHashes: Codec[LeafHashes] = tlvField(list(bytes32)) + private val proofNote: Codec[ProofNote] = tlvField(utf8) + + val payerProofTlvCodec: DiscriminatorCodec[PayerProofTlv, UInt64] = discriminated[PayerProofTlv].by(varint) + // Invoice part that must be copy-pasted from above + .typecase(UInt64(0), invoiceRequestMetadata) + .typecase(UInt64(2), offerChains) + .typecase(UInt64(4), offerMetadata) + .typecase(UInt64(6), offerCurrency) + .typecase(UInt64(8), offerAmount) + .typecase(UInt64(10), offerDescription) + .typecase(UInt64(12), offerFeatures) + .typecase(UInt64(14), offerAbsoluteExpiry) + .typecase(UInt64(16), offerPaths) + .typecase(UInt64(18), offerIssuer) + .typecase(UInt64(20), offerQuantityMax) + .typecase(UInt64(22), offerNodeId) + .typecase(UInt64(80), invoiceRequestChain) + .typecase(UInt64(82), invoiceRequestAmount) + .typecase(UInt64(84), invoiceRequestFeatures) + .typecase(UInt64(86), invoiceRequestQuantity) + .typecase(UInt64(88), invoiceRequestPayerId) + .typecase(UInt64(89), invoiceRequestPayerNote) + .typecase(UInt64(160), invoicePaths) + .typecase(UInt64(161), invoiceAccountable) + .typecase(UInt64(162), invoiceBlindedPay) + .typecase(UInt64(164), invoiceCreatedAt) + .typecase(UInt64(166), invoiceRelativeExpiry) + .typecase(UInt64(168), invoicePaymentHash) + .typecase(UInt64(170), invoiceAmount) + .typecase(UInt64(172), invoiceFallbacks) + .typecase(UInt64(174), invoiceFeatures) + .typecase(UInt64(176), invoiceNodeId) + .typecase(UInt64(240), signature) + // Payer proof part + .typecase(UInt64(241), proofSig) + .typecase(UInt64(1001), preimage) + .typecase(UInt64(1002), omitted) + .typecase(UInt64(1003), missingHashes) + .typecase(UInt64(1004), leafHashes) + .typecase(UInt64(1005), proofNote) + + val payerProofCodec: Codec[TlvStream[PayerProofTlv]] = catchAllCodec(TlvCodecs.tlvStream[PayerProofTlv](payerProofTlvCodec).complete) + private val invoiceErrorTlvCodec: Codec[TlvStream[InvoiceErrorTlv]] = catchAllCodec(TlvCodecs.tlvStream[InvoiceErrorTlv](discriminated[InvoiceErrorTlv].by(varint) .typecase(UInt64(1), tlvField(tu64overflow.as[ErroneousField])) .typecase(UInt64(3), tlvField(bytes.as[SuggestedValue])) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/OfferTypes.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/OfferTypes.scala index d1717a1985..a99dfe594e 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/OfferTypes.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/OfferTypes.scala @@ -51,7 +51,9 @@ object OfferTypes { sealed trait Bolt12Tlv extends Tlv - sealed trait InvoiceTlv extends Bolt12Tlv + sealed trait PayerProofTlv extends Bolt12Tlv + + sealed trait InvoiceTlv extends PayerProofTlv sealed trait InvoiceRequestTlv extends InvoiceTlv @@ -220,7 +222,25 @@ object OfferTypes { * Signature from the sender when used in an invoice request. * Signature from the recipient when used in an invoice. */ - case class Signature(signature: ByteVector64) extends InvoiceRequestTlv with InvoiceTlv + case class Signature(signature: ByteVector64) extends InvoiceRequestTlv with InvoiceTlv with PayerProofTlv + + /** The payer signs the payer proof. */ + case class ProofSignature(signature: ByteVector64) extends PayerProofTlv + + /** Preimage matching the invoice's [[InvoicePaymentHash]]. */ + case class InvoicePreimage(preimage: ByteVector32) extends PayerProofTlv + + /** The payer may omit some invoice TLVs for privacy reasons. */ + case class OmittedTlvs(missing: List[UInt64]) extends PayerProofTlv + + /** The payer must include the missing parts of the invoice TLV merkle tree. */ + case class MissingHashes(missing: List[ByteVector32]) extends PayerProofTlv + + /** The payer must include a nonce hash for each invoice TLV included in the payer proof. */ + case class LeafHashes(hashes: List[ByteVector32]) extends PayerProofTlv + + /** An optional challenge may be included in the payer proof. */ + case class ProofNote(note: String) extends PayerProofTlv private def isOfferTlv(tlv: GenericTlv): Boolean = // Offer TLVs are in the range [1, 79] or [1000000000, 1999999999]. @@ -467,9 +487,12 @@ object OfferTypes { // Encoding tlvs is always safe, unless we have a bug in our codecs, so we can call `.require` here. val encoded = codec.encode(tlvs).require // Decoding tlvs that we just encoded is safe as well. - // This encoding/decoding step ensures that the resulting tlvs are ordered. + // This encoding/decoding step ensures that the resulting tlvs are ordered and that we combine known and unknown TLVs. val genericTlvs = vector(genericTlv).decode(encoded).require.value - val firstTlv = genericTlvs.minBy(_.tag) + // The first TLV in the stream is used as a hashing nonce for all leaves of the tree, to ensure that + // their values cannot be brute-forced when omitted in payer proofs. For Bolt12 invoices and invoice + // requests this is invreq_metadata (tag 0); for payer proofs it's the lowest-tag disclosed TLV. + val firstTlv = genericTlvs.headOption.getOrElse(GenericTlv(UInt64(0), ByteVector.empty)) val nonceKey = ByteVector("LnNonce".getBytes) ++ genericTlv.encode(firstTlv).require.bytes def previousPowerOfTwo(n: Int): Int = { @@ -499,7 +522,7 @@ object OfferTypes { merkleTree(0, genericTlvs.length) } - private def hash(tag: ByteVector, msg: ByteVector): ByteVector32 = { + def hash(tag: ByteVector, msg: ByteVector): ByteVector32 = { val tagHash = Crypto.sha256(tag) Crypto.sha256(tagHash ++ tagHash ++ msg) } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/OnionRouting.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/OnionRouting.scala index cbabce50b7..3e86da8630 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/OnionRouting.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/OnionRouting.scala @@ -37,6 +37,7 @@ object OnionRoutingCodecs { def failureMessage: FailureMessage = InvalidOnionPayload(tag, 0) } case class MissingRequiredTlv(tag: UInt64) extends InvalidTlvPayload + case class InvalidTlvValue(tag: UInt64) extends InvalidTlvPayload case class ForbiddenTlv(tag: UInt64) extends InvalidTlvPayload // @formatter:on diff --git a/eclair-core/src/test/resources/payer-proof-test.json b/eclair-core/src/test/resources/payer-proof-test.json new file mode 100644 index 0000000000..5e8c2f4f86 --- /dev/null +++ b/eclair-core/src/test/resources/payer-proof-test.json @@ -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": [ + { + "tag": 0, + "len": 16, + "hex": "00000000000000000000000000000000", + "included": false + }, + { + "tag": 22, + "len": 33, + "hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382", + "included": true + }, + { + "tag": 82, + "len": 2, + "hex": "03e8", + "included": true + }, + { + "tag": 88, + "len": 33, + "hex": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c", + "included": true + }, + { + "tag": 160, + "len": 118, + "hex": "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000", + "included": true + }, + { + "tag": 162, + "len": 28, + "hex": "00000001000000020003000000000000000400000000000000050000", + "included": true + }, + { + "tag": 164, + "len": 4, + "hex": "67527988", + "included": true + }, + { + "tag": 168, + "len": 32, + "hex": "72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793", + "included": true + }, + { + "tag": 170, + "len": 2, + "hex": "03e8", + "included": true + }, + { + "tag": 174, + "len": 13, + "hex": "08000000000000000000000000", + "included": true + }, + { + "tag": 176, + "len": 33, + "hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382", + "included": true + }, + { + "tag": 240, + "len": 64, + "hex": "fbb932e6a9d5b4d88ca0ddc9cf9f8cc880ef41e3ec9574da89f624db898ab3e9d3ed6caa8744633b855167da009119d9834ae71f7b06f02732dc4c1debab0577", + "included": false + }, + { + "tag": 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": [ + { + "tag": 22, + "len": 33, + "hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382" + }, + { + "tag": 82, + "len": 2, + "hex": "03e8" + }, + { + "tag": 88, + "len": 33, + "hex": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c" + }, + { + "tag": 160, + "len": 118, + "hex": "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000" + }, + { + "tag": 162, + "len": 28, + "hex": "00000001000000020003000000000000000400000000000000050000" + }, + { + "tag": 164, + "len": 4, + "hex": "67527988" + }, + { + "tag": 168, + "len": 32, + "hex": "72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793" + }, + { + "tag": 170, + "len": 2, + "hex": "03e8" + }, + { + "tag": 174, + "len": 13, + "hex": "08000000000000000000000000" + }, + { + "tag": 176, + "len": 33, + "hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382" + }, + { + "tag": 240, + "len": 64, + "hex": "fbb932e6a9d5b4d88ca0ddc9cf9f8cc880ef41e3ec9574da89f624db898ab3e9d3ed6caa8744633b855167da009119d9834ae71f7b06f02732dc4c1debab0577" + }, + { + "tag": 241, + "len": 64, + "hex": "4961333409f453b5518fcbc662936fcb46e6c1db8d963c44b67d5677f0ffce3ac5c42293d1bc1298b0d67320a772e20a06c069dfa7079df9207b675357735dd7" + }, + { + "tag": 1001, + "len": 32, + "hex": "0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "tag": 1003, + "len": 32, + "hex": "0b510ba4c6884d603159ced2f0ca21e772424b59e52a2191bbfbcf07377805a1" + }, + { + "tag": 1004, + "len": 352, + "hex": "8c9057ed88f3c5a6b6441dcac3b5e4cefb3615904d7362b86e78427fb695f4618dc54a97453dee6f207fa5216a30f1567442712ca98852bc789b73885029283cf2deaf5f30be3ced89fc7c24d422819bf06af0e48a31423bbd0e2634f3c3de67f54f80c94a87383f2a8ef7c3e461c62b67a51da5bccf6cd96a7dbab29bea51fa7849b8b856e1d2a63d9ce7dc1a78e05cbb2def1f5d7709c48e8707e0a59fe51e19e7e4eee6bf56c6c589fe50035490c1a7c91b753cb8007c4b52838a6772f997f0191c35000247554b8d0a196898a794bf3de89982571178d931affb654f0c1adc0b8de03f1a0b0531bff146982d7d613ef6e1ef8d3bdd9590971fc18d835ffbc14cfffaa314261bcbb2ed4ca24d5717bb608d8a6cc9910790bc1d49af7858ab7e92b77b9e3843650f6cd7ee94b6753ea9df3533710b04dee686ad376515a5cbabaab91b367e30fea7026daf9f2590bb7e9cc31db8221f4013c67289e38f22c8" + }, + { + "tag": 3000000001, + "len": 1, + "hex": "42" + } + ], + "bech32": "lnp1zcssyj7z5vfx29flqlnsuzatppeyu6u9ugtl3ntz3n4k996zg7a5jvuz2gpq86zcyypjgef743p5fzqq9nqxh0ah7y87rzv3ud0eleps9kl2d5348hq2k89qwcp87v0tc4rzc87uuxmn0m8l2tfh6aw75s7wz8r56fd299ckt74zqpcr9s9he72nyjs86pfe3vjqzaxups47g3xedv2e4fk877c7v6rgpxgszqhd4w73ddqusdcmjthj7pxprpd57qakmn2jh2dh3kwhezwg7gs3g5qpqqqqqqqqqqqqqqqqqqqqqqqqqq9zrsqqqqqpqqqqqqsqqvqqqqqqqqqqqpqqqqqqqqqqqqzsqq9yq3n4y7vg4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0ya2qgp73tsdpqqqqqqqqqqqqqqqqqqqpvppqf9u9gcjv52n7pl8pc96kzrjfe4ctcshlrxk9r8tv2t5y3amfyec9uzqlwun9e4f6k6d3r9qmhyul8uvezqw7s0raj2hfk5f7cjdhzv2k05a8mtv42r5gcems4gk0ksqjyvanq62uu0hkphsyuedcnqaaw4s2al3gpykzve5p8698d233l9uvc5ndl95dekpmwxev0zyke74valsll8r43wyy2far0qjnzcdvueq5aewyzsxcp5alfc8nhujq7m82dthxhwhl5p7jgqpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpq87s86eqpdgshfxx3pxkqv2eemf0pj3puaeyyj6eu54zrydml08swdmcqksl6qlvl5qkprys2lkc3u7956myg8w2cw67fnhmxc2eqntnv2uxu7zz07mftarp3hz549698hhx7grl55sk5v832e6yyufv4xy990rcndecs5pf9q709h40tuctu08d3878cfx5y2qehur27rjg5v2z8w7suf3570pauel4f7qvjj588qlj4rhhc0jxr33tv7j3mfdueakdj6nah2efh6j3lfuynw9c2msa9f3annnacxncupwtkt00rawhwzwy36rs0c99nlj3ux08unhwd06kcmzcnljsqd2fpsd8eydh209cqp7yk55r3fnh97vh7qv3cdgqqfr42judpgvk3x98jjlnm6yesft3z7xexxhlke20psddczuduql35zc9xxllz35c947kz0hku8hc6w7ajkgfw87p3kp4l77pfnll4gc5ycduhvhdfj3y64chhdsgmznvexgs0y9ur4y677zc4dlf9dmmncuyxeg0dnt7a99kw5l2nhe4xdcskpx7u6r26dm9zkjuh2a2hydnvl3sl6nsymd0nujepwm7nnp3mwpzraqp83nj383c7gkgl6edqhspq9pq" + } + }, + { + "name": "minimal_disclosure", + "input": { + "invoice": "lni1qqgqqqqqqqqqqqqqqqqqqqqqqqqqq93pqf9u9gcjv52n7pl8pc96kzrjfe4ctcshlrxk9r8tv2t5y3amfyecy5szq059sggry3jnatzrgjyqqtxqdwlm0ug0uxyerc6lnljrqtd75mfr20wq4vw2qasz0uc7h32x9s0aecdhxlk075kn046aafpuuyw8f5j652t3vha2yqrsxtqt0nu4xf9q05znnzeyq96dcrptu3zdj6c4n2nv0aa3ue5xszv3qypwm2aaz66peqm3hyh09uzvzxzmfupmdhx49w5m0rva0jyu3u3pz3gqzqqqqqqqqqqqqqqqqqqqqqqqqqq2y8qqqqqqzqqqqqpqqqcqqqqqqqqqqqzqqqqqqqqqqqq9qqq2gpr82fuc32pqwtxkappzcsrlkmgfs6g0zyct0hkhashh7hsaxz7e65slq9fkx7f65qsrazczzqjtc233yeg48ur7wrst4vy8ynntsh3p07xdv2xwkc5hgfrmkjfnstcyqz3nyfzk3d42umkj2gqjh4l7zpevq04aefl6gnu4kql3e5ymu29s4q7fcvsst9uvmqx6q6yhje3vsraqple9pnxuf5vtwz0l68r6uvvs", + "invoice_hex": "0010000000000000000000000000000000001621024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382520203e858210324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1ca076027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000a21c00000001000000020003000000000000000400000000000000050000a40467527988a82072cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793aa0203e8b021024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382f0400a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319", + "preimage": "0101010101010101010101010101010101010101010101010101010101010101", + "invoice_fields": [ + { + "tag": 0, + "len": 16, + "hex": "00000000000000000000000000000000", + "included": false + }, + { + "tag": 22, + "len": 33, + "hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382", + "included": false + }, + { + "tag": 82, + "len": 2, + "hex": "03e8", + "included": false + }, + { + "tag": 88, + "len": 33, + "hex": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c", + "included": true + }, + { + "tag": 160, + "len": 118, + "hex": "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000", + "included": false + }, + { + "tag": 162, + "len": 28, + "hex": "00000001000000020003000000000000000400000000000000050000", + "included": false + }, + { + "tag": 164, + "len": 4, + "hex": "67527988", + "included": false + }, + { + "tag": 168, + "len": 32, + "hex": "72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793", + "included": true + }, + { + "tag": 170, + "len": 2, + "hex": "03e8", + "included": false + }, + { + "tag": 176, + "len": 33, + "hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382", + "included": true + }, + { + "tag": 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": [ + { + "tag": 88, + "len": 33, + "hex": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c" + }, + { + "tag": 168, + "len": 32, + "hex": "72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793" + }, + { + "tag": 176, + "len": 33, + "hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382" + }, + { + "tag": 240, + "len": 64, + "hex": "0a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319" + }, + { + "tag": 241, + "len": 64, + "hex": "cebc25d40a2d927b5ebcab8400f542fbb7a8f462e8dd802bb13e050b9bf293b7457c19b46a476740f97c9d6ec141f23434c5d4fa253a1d2eb8896ebad99455cc" + }, + { + "tag": 1001, + "len": 32, + "hex": "0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "tag": 1002, + "len": 6, + "hex": "0102595a5ba9" + }, + { + "tag": 1003, + "len": 160, + "hex": "bf8cb2b1d6fa9bcdcab501b59f82c65c506b7f43514737f7197f1fcfeaebad41b9406f4ce526a6a0d4e0b3a63ed89a832e31cb9939dfe1a7b5dd7232d32c02abcd9c44b53b31700c9ed0e3330ce425f7f18fac2fc1d566a34468439274f0e3169f9830f2c3070cfbad13fde30ee36cd7143591164ed12040a9cd595c96840ac9998ab7fa9c743fb9dbdb0d8d46fbe3ad333400bd07f328dcdb6008790bc9d2db" + }, + { + "tag": 1004, + "len": 96, + "hex": "f2deaf5f30be3ced89fc7c24d422819bf06af0e48a31423bbd0e2634f3c3de67f0191c35000247554b8d0a196898a794bf3de89982571178d931affb654f0c1a7e92b77b9e3843650f6cd7ee94b6753ea9df3533710b04dee686ad376515a5cb" + } + ], + "bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qva0p96s9zmynmt672hpqq74p0hdag733w3hvq9wcnupgtn0ef8d690svmg6j8vaq0jlyadmq5ru35xnzaf7398gwjawyfd6adn9z4en7s86fqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyql6ql2qcqsyk26tw5l6qlt5zlcev436mafhnw2k5qmt8uzcew9q6mlgdg5wdlhr9l3lnl2awk5rw2qdaxw2f4x5r2wpvax8mvf4qewx89ejwwluxnmthtjxtfjcq4tekwyfdfmx9cqe8ksuveseep97lccltp0c82kdg6ydppeya8suvtflxps7tpswr8m45flmccwudkdw9p4jytya5fqgz5u6k2uj6zq4jve32ml48r587uahkcd34r0hcadxv6qp0g87v5dekmqppushjwjm07s8mrq7t027heshc7wmz0u0sjdgg5pn0cx4u8y3gc5ywaapcnrfu7rmenlqxgux5qqy364fwxs5xtgnznef0eaazvcy4c30rvnrtlmv48scxn7j2mhh83cgdjs7mxha62tvaf7480n2vm3pvzdae5x45mk29d9ev" + } + }, + { + "name": "with_note", + "input": { + "invoice": "lni1qqgqqqqqqqqqqqqqqqqqqqqqqqqqq93pqf9u9gcjv52n7pl8pc96kzrjfe4ctcshlrxk9r8tv2t5y3amfyecy5szq059sggry3jnatzrgjyqqtxqdwlm0ug0uxyerc6lnljrqtd75mfr20wq4vw2qasz0uc7h32x9s0aecdhxlk075kn046aafpuuyw8f5j652t3vha2yqrsxtqt0nu4xf9q05znnzeyq96dcrptu3zdj6c4n2nv0aa3ue5xszv3qypwm2aaz66peqm3hyh09uzvzxzmfupmdhx49w5m0rva0jyu3u3pz3gqzqqqqqqqqqqqqqqqqqqqqqqqqqq2y8qqqqqqzqqqqqpqqqcqqqqqqqqqqqzqqqqqqqqqqqq9qqq2gpr82fuc32pqwtxkappzcsrlkmgfs6g0zyct0hkhashh7hsaxz7e65slq9fkx7f65qsrazczzqjtc233yeg48ur7wrst4vy8ynntsh3p07xdv2xwkc5hgfrmkjfnstcyqz3nyfzk3d42umkj2gqjh4l7zpevq04aefl6gnu4kql3e5ymu29s4q7fcvsst9uvmqx6q6yhje3vsraqple9pnxuf5vtwz0l68r6uvvs", + "invoice_hex": "0010000000000000000000000000000000001621024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382520203e858210324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1ca076027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000a21c00000001000000020003000000000000000400000000000000050000a40467527988a82072cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793aa0203e8b021024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382f0400a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319", + "preimage": "0101010101010101010101010101010101010101010101010101010101010101", + "note": "test note", + "invoice_fields": [ + { + "tag": 0, + "len": 16, + "hex": "00000000000000000000000000000000", + "included": false + }, + { + "tag": 22, + "len": 33, + "hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382", + "included": false + }, + { + "tag": 82, + "len": 2, + "hex": "03e8", + "included": false + }, + { + "tag": 88, + "len": 33, + "hex": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c", + "included": true + }, + { + "tag": 160, + "len": 118, + "hex": "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000", + "included": false + }, + { + "tag": 162, + "len": 28, + "hex": "00000001000000020003000000000000000400000000000000050000", + "included": false + }, + { + "tag": 164, + "len": 4, + "hex": "67527988", + "included": false + }, + { + "tag": 168, + "len": 32, + "hex": "72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793", + "included": true + }, + { + "tag": 170, + "len": 2, + "hex": "03e8", + "included": false + }, + { + "tag": 176, + "len": 33, + "hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382", + "included": true + }, + { + "tag": 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": [ + { + "tag": 88, + "len": 33, + "hex": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c" + }, + { + "tag": 168, + "len": 32, + "hex": "72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793" + }, + { + "tag": 176, + "len": 33, + "hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382" + }, + { + "tag": 240, + "len": 64, + "hex": "0a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319" + }, + { + "tag": 241, + "len": 64, + "hex": "2a47f98770ae814119d682a7b19f7ce9e050a3bb49d9b0a031c46d9cb57a3075ddc23a742f6d33bc4ec8e1778327494bea76d2ee564625e99bfb95cc209a7722" + }, + { + "tag": 1001, + "len": 32, + "hex": "0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "tag": 1002, + "len": 6, + "hex": "0102595a5ba9" + }, + { + "tag": 1003, + "len": 160, + "hex": "bf8cb2b1d6fa9bcdcab501b59f82c65c506b7f43514737f7197f1fcfeaebad41b9406f4ce526a6a0d4e0b3a63ed89a832e31cb9939dfe1a7b5dd7232d32c02abcd9c44b53b31700c9ed0e3330ce425f7f18fac2fc1d566a34468439274f0e3169f9830f2c3070cfbad13fde30ee36cd7143591164ed12040a9cd595c96840ac9998ab7fa9c743fb9dbdb0d8d46fbe3ad333400bd07f328dcdb6008790bc9d2db" + }, + { + "tag": 1004, + "len": 96, + "hex": "f2deaf5f30be3ced89fc7c24d422819bf06af0e48a31423bbd0e2634f3c3de67f0191c35000247554b8d0a196898a794bf3de89982571178d931affb654f0c1a7e92b77b9e3843650f6cd7ee94b6753ea9df3533710b04dee686ad376515a5cb" + }, + { + "tag": 1005, + "len": 9, + "hex": "74657374206e6f7465" + } + ], + "bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0yasyypyhs4rzfj320c8uu8qh2cgwf8xhp0zzluv6c5vad3fwsj8hdyn8qhsgq9rxgj9dzm24ehdy5sp90tluyrjcqltmjnl538etvplrngfhc5tp2punsepqktcekqd5p5f09nzeq86qrlj2rxdcngckuyll5w84cce79qz53lesac2aq2pr8tg9fa3na7wnczs5wa5nkds5qcugmvuk4arqawacga8gtmdxw7yaj8pw7pjwj2tafmd9mjkgcj7nxlmjhxzpxnhyt7s86fqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyql6ql2qcqsyk26tw5l6qlt5zlcev436mafhnw2k5qmt8uzcew9q6mlgdg5wdlhr9l3lnl2awk5rw2qdaxw2f4x5r2wpvax8mvf4qewx89ejwwluxnmthtjxtfjcq4tekwyfdfmx9cqe8ksuveseep97lccltp0c82kdg6ydppeya8suvtflxps7tpswr8m45flmccwudkdw9p4jytya5fqgz5u6k2uj6zq4jve32ml48r587uahkcd34r0hcadxv6qp0g87v5dekmqppushjwjm07s8mrq7t027heshc7wmz0u0sjdgg5pn0cx4u8y3gc5ywaapcnrfu7rmenlqxgux5qqy364fwxs5xtgnznef0eaazvcy4c30rvnrtlmv48scxn7j2mhh83cgdjs7mxha62tvaf7480n2vm3pvzdae5x45mk29d9e07s8mgfw3jhxapqdehhgeg" + } + }, + { + "name": "left_subtree_omitted", + "input": { + "invoice": "lni1qqgqqqqqqqqqqqqqqqqqqqqqqqqqq93pqf9u9gcjv52n7pl8pc96kzrjfe4ctcshlrxk9r8tv2t5y3amfyecy5szq059sggry3jnatzrgjyqqtxqdwlm0ug0uxyerc6lnljrqtd75mfr20wq4vw2qasz0uc7h32x9s0aecdhxlk075kn046aafpuuyw8f5j652t3vha2yqrsxtqt0nu4xf9q05znnzeyq96dcrptu3zdj6c4n2nv0aa3ue5xszv3qypwm2aaz66peqm3hyh09uzvzxzmfupmdhx49w5m0rva0jyu3u3pz3gqzqqqqqqqqqqqqqqqqqqqqqqqqqq2y8qqqqqqzqqqqqpqqqcqqqqqqqqqqqzqqqqqqqqqqqq9qqq2gpr82fuc32pqwtxkappzcsrlkmgfs6g0zyct0hkhashh7hsaxz7e65slq9fkx7f65qsrazczzqjtc233yeg48ur7wrst4vy8ynntsh3p07xdv2xwkc5hgfrmkjfnstcyqz3nyfzk3d42umkj2gqjh4l7zpevq04aefl6gnu4kql3e5ymu29s4q7fcvsst9uvmqx6q6yhje3vsraqple9pnxuf5vtwz0l68r6uvvs", + "invoice_hex": "0010000000000000000000000000000000001621024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382520203e858210324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1ca076027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000a21c00000001000000020003000000000000000400000000000000050000a40467527988a82072cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793aa0203e8b021024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382f0400a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319", + "preimage": "0101010101010101010101010101010101010101010101010101010101010101", + "invoice_fields": [ + { + "tag": 0, + "len": 16, + "hex": "00000000000000000000000000000000", + "included": false + }, + { + "tag": 22, + "len": 33, + "hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382", + "included": false + }, + { + "tag": 82, + "len": 2, + "hex": "03e8", + "included": false + }, + { + "tag": 88, + "len": 33, + "hex": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c", + "included": true + }, + { + "tag": 160, + "len": 118, + "hex": "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000", + "included": false + }, + { + "tag": 162, + "len": 28, + "hex": "00000001000000020003000000000000000400000000000000050000", + "included": false + }, + { + "tag": 164, + "len": 4, + "hex": "67527988", + "included": false + }, + { + "tag": 168, + "len": 32, + "hex": "72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793", + "included": true + }, + { + "tag": 170, + "len": 2, + "hex": "03e8", + "included": true + }, + { + "tag": 176, + "len": 33, + "hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382", + "included": true + }, + { + "tag": 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": [ + { + "tag": 88, + "len": 33, + "hex": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c" + }, + { + "tag": 168, + "len": 32, + "hex": "72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793" + }, + { + "tag": 170, + "len": 2, + "hex": "03e8" + }, + { + "tag": 176, + "len": 33, + "hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382" + }, + { + "tag": 240, + "len": 64, + "hex": "0a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319" + }, + { + "tag": 241, + "len": 64, + "hex": "ed03ec58d5ac676539486bb6e8d6f389b6375b6693ffdf30a93919590d744fa393f945a5de3bb57d65c7f61def1cefa8aa038725b15bf0fa797dfd08ca97538a" + }, + { + "tag": 1001, + "len": 32, + "hex": "0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "tag": 1002, + "len": 5, + "hex": "0102595a5b" + }, + { + "tag": 1003, + "len": 128, + "hex": "bf8cb2b1d6fa9bcdcab501b59f82c65c506b7f43514737f7197f1fcfeaebad41b9406f4ce526a6a0d4e0b3a63ed89a832e31cb9939dfe1a7b5dd7232d32c02abcd9c44b53b31700c9ed0e3330ce425f7f18fac2fc1d566a34468439274f0e3169f9830f2c3070cfbad13fde30ee36cd7143591164ed12040a9cd595c96840ac9" + }, + { + "tag": 1004, + "len": 128, + "hex": "f2deaf5f30be3ced89fc7c24d422819bf06af0e48a31423bbd0e2634f3c3de67f0191c35000247554b8d0a196898a794bf3de89982571178d931affb654f0c1adc0b8de03f1a0b0531bff146982d7d613ef6e1ef8d3bdd9590971fc18d835ffb7e92b77b9e3843650f6cd7ee94b6753ea9df3533710b04dee686ad376515a5cb" + } + ], + "bech32": "lnp1tqssxfr986kyx3ygqqkvq6alklcslcvfj834l8lyxqkmafkjx57up2cu4qs89ntwss3vgplmd5ycdy83zv9hmmt7ctmltcwnp0va2g0sz5mr0ya2qgp73vppqf9u9gcjv52n7pl8pc96kzrjfe4ctcshlrxk9r8tv2t5y3amfyec9uzqpgejy3tgk64wdmf9yqft6llpqukq867u5layf72mq0cu6zd79zc2s0yuxgg9j7xdsrdqdztevckgp7sqlujsenwy6x9hp8lar3awxx03grks8mzc6kkxwefefp4md6xk7wymvd6mv6fllhes4yu3jkgdw3868ylegkjauwa404ju0asaauwwl292qwrjtv2m7ra8jl0apr9fw5u2l5p7jgqpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpq87s86s9qyp9jkjml5p7hq9l3jetr4h6n0xu4dgpkk0c93ju2p4h7s63gumlwxtlrl8746adgxu5qm6vu5n2dgx5uze6v0kcn2pjuvwtnyualcd8khwhyvkn9sp2hnvugj6nkvtspj0dpcenpnjztal337kzlsw4v635g6zrjf60pcckn7vrpukrqux0htgnlh3sacmv6u2rtygkfmgjqs9fe4v4e95yptyl6qlvsredat6lxzlremvfl37zf4pzsxdlq6hsuj9rzs3mh58zvd8nc00x0uqers6sqqj8249c6zsedzv2099l8h5fnqjhz9udjvd0ldj57rq6ms9cmcplrg9s2vdl79rfsttavyl0dc0035aam9vsju0urrvrtlahay4h0w0rssm9pakd0m55ke6na2wlx5ehzzcymmngdtfhv526tjc" + } + }, + { + "name": "empty_proof_omitted_tlvs_explicit", + "input": { + "invoice": "lni1qqgqqqqqqqqqqqqqqqqqqqqqqqqqq93pqf9u9gcjv52n7pl8pc96kzrjfe4ctcshlrxk9r8tv2t5y3amfyecy5szq059sggry3jnatzrgjyqqtxqdwlm0ug0uxyerc6lnljrqtd75mfr20wq4vw2qasz0uc7h32x9s0aecdhxlk075kn046aafpuuyw8f5j652t3vha2yqrsxtqt0nu4xf9q05znnzeyq96dcrptu3zdj6c4n2nv0aa3ue5xszv3qypwm2aaz66peqm3hyh09uzvzxzmfupmdhx49w5m0rva0jyu3u3pz3gqzqqqqqqqqqqqqqqqqqqqqqqqqqq2y8qqqqqqzqqqqqpqqqcqqqqqqqqqqqzqqqqqqqqqqqq9qqq2gpr82fuc32pqwtxkappzcsrlkmgfs6g0zyct0hkhashh7hsaxz7e65slq9fkx7f65qsrazczzqjtc233yeg48ur7wrst4vy8ynntsh3p07xdv2xwkc5hgfrmkjfnstcyqz3nyfzk3d42umkj2gqjh4l7zpevq04aefl6gnu4kql3e5ymu29s4q7fcvsst9uvmqx6q6yhje3vsraqple9pnxuf5vtwz0l68r6uvvs", + "invoice_hex": "0010000000000000000000000000000000001621024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382520203e858210324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1ca076027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000a21c00000001000000020003000000000000000400000000000000050000a40467527988a82072cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793aa0203e8b021024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382f0400a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319", + "preimage": "0101010101010101010101010101010101010101010101010101010101010101", + "invoice_fields": [ + { + "tag": 0, + "len": 16, + "hex": "00000000000000000000000000000000", + "included": false + }, + { + "tag": 22, + "len": 33, + "hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382", + "included": true + }, + { + "tag": 82, + "len": 2, + "hex": "03e8", + "included": true + }, + { + "tag": 88, + "len": 33, + "hex": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c", + "included": true + }, + { + "tag": 160, + "len": 118, + "hex": "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000", + "included": true + }, + { + "tag": 162, + "len": 28, + "hex": "00000001000000020003000000000000000400000000000000050000", + "included": true + }, + { + "tag": 164, + "len": 4, + "hex": "67527988", + "included": true + }, + { + "tag": 168, + "len": 32, + "hex": "72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793", + "included": true + }, + { + "tag": 170, + "len": 2, + "hex": "03e8", + "included": true + }, + { + "tag": 176, + "len": 33, + "hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382", + "included": true + }, + { + "tag": 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": [ + { + "tag": 22, + "len": 33, + "hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382" + }, + { + "tag": 82, + "len": 2, + "hex": "03e8" + }, + { + "tag": 88, + "len": 33, + "hex": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c" + }, + { + "tag": 160, + "len": 118, + "hex": "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145001000000000000000000000000000000000" + }, + { + "tag": 162, + "len": 28, + "hex": "00000001000000020003000000000000000400000000000000050000" + }, + { + "tag": 164, + "len": 4, + "hex": "67527988" + }, + { + "tag": 168, + "len": 32, + "hex": "72cd6e8422c407fb6d098690f1130b7ded7ec2f7f5e1d30bd9d521f015363793" + }, + { + "tag": 170, + "len": 2, + "hex": "03e8" + }, + { + "tag": 176, + "len": 33, + "hex": "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382" + }, + { + "tag": 240, + "len": 64, + "hex": "0a33224568b6aae6ed252012bd7fe1072c03ebdca7fa44f95b03f1cd09be28b0a83c9c32105978cd80da068979662c80fa00ff250ccdc4d18b709ffd1c7ae319" + }, + { + "tag": 241, + "len": 64, + "hex": "dffe62d6446c182f2503e780fce09fc5cf310ef14a54f65dd1df124039b7a897f2940470ac0f9857f826b232289c0ac4e6927ca67d805167fb0add352f88add1" + }, + { + "tag": 1001, + "len": 32, + "hex": "0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "tag": 1002, + "len": 0, + "hex": "" + }, + { + "tag": 1003, + "len": 32, + "hex": "0b510ba4c6884d603159ced2f0ca21e772424b59e52a2191bbfbcf07377805a1" + }, + { + "tag": 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" + } + ] +} diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/offer/PayerProofSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/offer/PayerProofSpec.scala new file mode 100644 index 0000000000..4fa67a896b --- /dev/null +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/offer/PayerProofSpec.scala @@ -0,0 +1,116 @@ +/* + * Copyright 2026 ACINQ SAS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fr.acinq.eclair.payment.offer + +import fr.acinq.bitcoin.scalacompat.Crypto.PrivateKey +import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64} +import fr.acinq.eclair.UInt64 +import fr.acinq.eclair.payment.Bolt12Invoice +import fr.acinq.eclair.payment.offer.PayerProof.IncludedFields +import fr.acinq.eclair.wire.protocol.OfferTypes.{LeafHashes, MissingHashes, OmittedTlvs, ProofSignature} +import fr.acinq.eclair.wire.protocol.{OfferCodecs, OfferTypes} +import org.json4s.DefaultFormats +import org.json4s.jackson.JsonMethods +import org.scalatest.funsuite.AnyFunSuite +import scodec.bits.HexStringSyntax + +import java.io.File +import scala.io.Source + +class PayerProofSpec extends AnyFunSuite { + + case class TestVectors(valid_vectors: Seq[ValidTestVector], invalid_vectors: Seq[InvalidTestVector]) + + case class ValidTestVector(name: String, input: TestVectorInput, working: TestVectorWorking, result: TestVectorResult) + + case class TestVectorInput(invoice: String, preimage: String, note: Option[String], invoice_fields: Seq[InvoiceField]) + + case class InvoiceField(tag: Long, included: Boolean) + + case class TestVectorWorking(invoice_merkle_root: String, proof_leaf_hashes: Seq[String], proof_omitted_tlvs: Seq[Long], proof_missing_hashes: Seq[String]) + + case class TestVectorResult(payer_sig: String, bech32: String) + + case class InvalidTestVector(reason: String, bech32: String) + + test("official test vectors") { + implicit val formats: DefaultFormats.type = DefaultFormats + + val src = Source.fromFile(new File(getClass.getResource("/payer-proof-test.json").getFile)) + val f = JsonMethods.parse(src.mkString).extract[TestVectors] + src.close() + + val payerKey = PrivateKey(hex"4242424242424242424242424242424242424242424242424242424242424242") + + f.valid_vectors.foreach(t => { + val invoice = Bolt12Invoice.fromString(t.input.invoice).get + assert(OfferTypes.rootHash(OfferTypes.removeSignature(invoice.records), OfferCodecs.invoiceTlvCodec) == ByteVector32.fromValidHex(t.working.invoice_merkle_root)) + val preimage = ByteVector32.fromValidHex(t.input.preimage) + val includedFields = IncludedFields( + offerChains = t.input.invoice_fields.exists(f => f.tag == 2 && f.included), + offerMetadata = t.input.invoice_fields.exists(f => f.tag == 4 && f.included), + offerCurrency = t.input.invoice_fields.exists(f => f.tag == 6 && f.included), + offerAmount = t.input.invoice_fields.exists(f => f.tag == 8 && f.included), + offerDescription = t.input.invoice_fields.exists(f => f.tag == 10 && f.included), + offerFeatures = t.input.invoice_fields.exists(f => f.tag == 12 && f.included), + offerAbsoluteExpiry = t.input.invoice_fields.exists(f => f.tag == 14 && f.included), + offerPaths = t.input.invoice_fields.exists(f => f.tag == 16 && f.included), + offerIssuer = t.input.invoice_fields.exists(f => f.tag == 18 && f.included), + offerQuantityMax = t.input.invoice_fields.exists(f => f.tag == 20 && f.included), + offerNodeId = t.input.invoice_fields.exists(f => f.tag == 22 && f.included), + invoiceRequestChain = t.input.invoice_fields.exists(f => f.tag == 80 && f.included), + invoiceRequestAmount = t.input.invoice_fields.exists(f => f.tag == 82 && f.included), + invoiceRequestFeatures = t.input.invoice_fields.exists(f => f.tag == 84 && f.included), + invoiceRequestQuantity = t.input.invoice_fields.exists(f => f.tag == 86 && f.included), + invoiceRequestPayerNote = t.input.invoice_fields.exists(f => f.tag == 89 && f.included), + invoicePaths = t.input.invoice_fields.exists(f => f.tag == 160 && f.included), + invoiceAccountable = t.input.invoice_fields.exists(f => f.tag == 161 && f.included), + invoiceBlindedPay = t.input.invoice_fields.exists(f => f.tag == 162 && f.included), + invoiceCreatedAt = t.input.invoice_fields.exists(f => f.tag == 164 && f.included), + invoiceRelativeExpiry = t.input.invoice_fields.exists(f => f.tag == 166 && f.included), + invoiceAmount = t.input.invoice_fields.exists(f => f.tag == 170 && f.included), + invoiceFallbacks = t.input.invoice_fields.exists(f => f.tag == 172 && f.included), + unknown = t.input.invoice_fields.filter(f => f.tag > 250 && f.included).map(f => UInt64(f.tag)).toSet, + ) + if (t.name == "empty_proof_omitted_tlvs_explicit") { + // This test vector verifies that we correctly handle an explicitly included empty proof_omitted_tlvs field. + val decoded = PayerProof.fromString(t.result.bech32) + assert(decoded.isSuccess) + assert(decoded.get.records.get[ProofSignature].get.signature == ByteVector64.fromValidHex(t.result.payer_sig)) + assert(decoded.get.verifySigs()) + // Since we omit the proof_omitted_tlvs field when it's empty, we don't generate the same proof, which is fine. + assert(PayerProof.create(invoice, preimage, payerKey, includedFields, t.input.note).toString != t.result.bech32) + } else { + val payerProof = PayerProof.create(invoice, preimage, payerKey, includedFields, t.input.note) + assert(payerProof.records.get[LeafHashes].nonEmpty) + payerProof.records.get[LeafHashes].foreach(h => assert(h.hashes == t.working.proof_leaf_hashes.map(ByteVector32.fromValidHex))) + payerProof.records.get[OmittedTlvs].foreach(o => assert(o.missing == t.working.proof_omitted_tlvs.map(UInt64(_)))) + payerProof.records.get[MissingHashes].foreach(h => assert(h.missing == t.working.proof_missing_hashes.map(ByteVector32.fromValidHex))) + assert(payerProof.records.get[ProofSignature].nonEmpty) + assert(payerProof.records.get[ProofSignature].get.signature == ByteVector64.fromValidHex(t.result.payer_sig)) + assert(payerProof.toString == t.result.bech32) + assert(PayerProof.fromString(t.result.bech32).isSuccess) + assert(PayerProof.fromString(t.result.bech32).get == payerProof) + assert(payerProof.verifySigs()) + } + }) + f.invalid_vectors.foreach(t => { + assert(!PayerProof.fromString(t.bech32).map(_.verifySigs()).getOrElse(false)) + }) + } + +}