Skip to content

[RFC] Add BOLT 12 payer proof primitives#4297

Open
vincenzopalazzo wants to merge 5 commits intolightningdevkit:mainfrom
vincenzopalazzo:macros/proof-of-payment-bolt12-spec
Open

[RFC] Add BOLT 12 payer proof primitives#4297
vincenzopalazzo wants to merge 5 commits intolightningdevkit:mainfrom
vincenzopalazzo:macros/proof-of-payment-bolt12-spec

Conversation

@vincenzopalazzo
Copy link
Copy Markdown
Contributor

This is a first draft implementation of the payer proof extension to BOLT 12 as proposed in lightning/bolts#1295. The goal is to get early feedback on the API design before the spec is finalized.

Payer proofs allow proving that a BOLT 12 invoice was paid by demonstrating possession of:

  • The payment preimage
  • A valid invoice signature over a merkle root
  • The payer's signature

This PR adds the core building blocks:

  • Extends merkle.rs with selective disclosure primitives that allow creating and reconstructing merkle trees with partial TLV disclosure. This enables proving invoice authenticity while omitting sensitive fields.
  • Adds payer_proof.rs with PayerProof, PayerProofBuilder, and UnsignedPayerProof types. The builder pattern allows callers to selectively include invoice fields (description, amount, etc.) in the proof.
  • Implements bech32 encoding/decoding with the lnp prefix and proper TLV stream parsing with validation (ascending order, no duplicates, hash length checks).

This is explicitly a PoC to validate the API surface - the spec itself is still being refined. Looking for feedback on:

  • Whether the builder pattern makes sense for selective disclosure
  • The verification API
  • Integration points with the rest of the offers module

cc @TheBlueMatt @jkczyz

@ldk-reviews-bot
Copy link
Copy Markdown

ldk-reviews-bot commented Jan 5, 2026

👋 Thanks for assigning @jkczyz as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@codecov
Copy link
Copy Markdown

codecov bot commented Jan 5, 2026

Codecov Report

❌ Patch coverage is 93.85113% with 95 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.08%. Comparing base (4c398bb) to head (1ee2f1a).
⚠️ Report is 36 commits behind head on main.

Files with missing lines Patch % Lines
lightning/src/offers/payer_proof.rs 92.46% 32 Missing and 46 partials ⚠️
lightning/src/offers/merkle.rs 96.70% 7 Missing and 4 partials ⚠️
lightning/src/ln/channelmanager.rs 88.46% 3 Missing ⚠️
lightning/src/ln/outbound_payment.rs 97.72% 1 Missing ⚠️
lightning/src/offers/invoice.rs 97.91% 1 Missing ⚠️
lightning/src/offers/signer.rs 97.29% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4297      +/-   ##
==========================================
+ Coverage   86.99%   87.08%   +0.08%     
==========================================
  Files         163      164       +1     
  Lines      108706   110509    +1803     
  Branches   108706   110509    +1803     
==========================================
+ Hits        94571    96235    +1664     
- Misses      11655    11735      +80     
- Partials     2480     2539      +59     
Flag Coverage Δ
fuzzing 37.82% <2.70%> (-2.45%) ⬇️
tests 86.20% <93.85%> (+0.11%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few notes, though I didn't dig into the code at a particularly low level.

Comment thread lightning/src/offers/payer_proof.rs Outdated
Comment thread lightning/src/offers/payer_proof.rs Outdated
Comment thread lightning/src/offers/payer_proof.rs
Comment thread lightning/src/offers/payer_proof.rs Outdated
Comment thread lightning/src/offers/merkle.rs Outdated
Comment thread lightning/src/offers/merkle.rs Outdated
Comment thread lightning/src/offers/merkle.rs Outdated
Comment thread lightning/src/offers/merkle.rs Outdated
Comment thread lightning/src/offers/payer_proof.rs Outdated
Comment thread lightning/src/offers/payer_proof.rs
@vincenzopalazzo vincenzopalazzo marked this pull request as ready for review January 20, 2026 17:00
@vincenzopalazzo vincenzopalazzo force-pushed the macros/proof-of-payment-bolt12-spec branch 2 times, most recently from 2324361 to 9f84e19 Compare January 20, 2026 17:42
vincenzopalazzo added a commit to vincenzopalazzo/payer-proof-test-vectors that referenced this pull request Jan 20, 2026
Add a Rust CLI tool that generates and verifies test vectors for BOLT 12
payer proofs as specified in lightning/bolts#1295. The tool uses the
rust-lightning implementation from lightningdevkit/rust-lightning#4297.

Features:
- Generate deterministic test vectors with configurable seed
- Verify test vectors from JSON files
- Support for basic proofs, proofs with notes, and invalid test cases
- Uses refund flow for explicit payer key control

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@ldk-reviews-bot
Copy link
Copy Markdown

🔔 1st Reminder

Hey @valentinewallace! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

Copy link
Copy Markdown
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some API comments. I'll review the actual code somewhat later (are we locked on on the spec or is it still in flux at all?), but would be nice to reduce allocations in it first anyway.

Comment thread lightning/src/offers/merkle.rs Outdated
Comment thread lightning/src/offers/merkle.rs Outdated
Comment thread lightning/src/offers/payer_proof.rs Outdated
@ldk-reviews-bot
Copy link
Copy Markdown

🔔 2nd Reminder

Hey @valentinewallace! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@valentinewallace valentinewallace removed their request for review January 26, 2026 17:25
@jkczyz jkczyz self-requested a review January 27, 2026 18:59
@ldk-reviews-bot
Copy link
Copy Markdown

🔔 1st Reminder

Hey @jkczyz! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 2nd Reminder

Hey @jkczyz! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 3rd Reminder

Hey @jkczyz! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 4th Reminder

Hey @jkczyz! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 5th Reminder

Hey @jkczyz! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 6th Reminder

Hey @jkczyz! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 7th Reminder

Hey @jkczyz! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 8th Reminder

Hey @jkczyz! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 9th Reminder

Hey @jkczyz! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@TheBlueMatt TheBlueMatt added this to the 0.3 milestone Feb 18, 2026
@vincenzopalazzo vincenzopalazzo force-pushed the macros/proof-of-payment-bolt12-spec branch 5 times, most recently from fb8c68c to 9ad5c35 Compare February 24, 2026 18:13
@vincenzopalazzo vincenzopalazzo force-pushed the macros/proof-of-payment-bolt12-spec branch from 3a500c8 to 564682f Compare April 10, 2026 07:23
@vincenzopalazzo
Copy link
Copy Markdown
Contributor Author

Thanks for the catch @jkczyz need to remind myself not to work with git magic command late at night, so I should have restored the old history with the changes requested in the last review from you and Matt.

Thanks!

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 4th Reminder

Hey @jkczyz! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 5th Reminder

Hey @jkczyz! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

Comment thread lightning/src/offers/merkle.rs Outdated
Comment thread lightning/src/offers/merkle.rs Outdated
Comment thread lightning/src/offers/payer_proof.rs Outdated
Comment thread lightning/src/offers/payer_proof.rs Outdated
/// values or out-of-bounds lengths. This function validates the framing first,
/// returning an error instead of panicking on untrusted input. It also checks
/// strict ascending TLV type ordering (which covers duplicates).
fn validate_tlv_framing(bytes: &[u8]) -> Result<(), DecodeError> {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this and the manual TLV record parsing in impl TryFrom<Vec<u8>> for PayerProof {? Can't we use the tlv_stream macros that we use in the other offer types?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it comes down to needing the bytes after parsing to pass to reconstruct_merkle_root, so tlv_stream alone is not sufficient unless we want to re-serialize there. See #4297 (comment).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we still need the manual path here because reconstruct_merkle_root needs the original disclosed TLV record bytes, not just parsed field values. tlv_stream! / ParsedMessage works well for the other BOLT 12 types because they parse a fixed known set of TLVs, but here we also need to carry through an arbitrary subset of invoice TLVs as opaque records for merkle reconstruction. Re-serializing after parsing felt wrong since the merkle logic is defined over the original record bytes, not a normalized encoding. I think we could factor this better if we taught the TLV helpers to preserve/filter raw records alongside typed fields, but that seemed like a larger abstraction change than I wanted to take on in this PR. Happy to go that route if you think it is worth it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-serializing after parsing felt wrong since the merkle logic is defined over the original record bytes, not a normalized encoding.

We shouldn't need to re-serialize though as we retain the bytes to include in PayerProof. So we can just wrap that in TlvStream and iterate over its TlvRecords as you are currently doing. It's just we wouldn't need to do the parsing in that loop anymore. It would require an additional pass over the bytes, though.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I vibe some experimentation on this based on our offline discussion, and I added a fixup commit 664617f

I am kind of unsure regarding the impl CursorReadable for FullPayerProofTlvStream if this is the correct approach tho

Comment thread lightning/src/offers/payer_proof.rs Outdated
/// values or out-of-bounds lengths. This function validates the framing first,
/// returning an error instead of panicking on untrusted input. It also checks
/// strict ascending TLV type ordering (which covers duplicates).
fn validate_tlv_framing(bytes: &[u8]) -> Result<(), DecodeError> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it comes down to needing the bytes after parsing to pass to reconstruct_merkle_root, so tlv_stream alone is not sufficient unless we want to re-serialize there. See #4297 (comment).

Comment thread lightning/src/offers/payer_proof.rs Outdated
Comment on lines +717 to +726
if !SIGNATURE_TYPES.contains(&tlv_type) {
// Included invoice TLV record (passthrough for merkle
// reconstruction).
included_types.insert(tlv_type);
included_records.push(record);
} else if tlv_type % 2 == 0 {
// Unknown even types are mandatory-to-understand per
// BOLT convention — reject them.
return Err(Bolt12ParseError::Decode(DecodeError::InvalidValue));
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I mean is why does this check only apply to SIGNATURE_TYPES? Is this mentioned in the spec somewhere?

Comment thread lightning/src/offers/payer_proof.rs Outdated
Comment on lines +620 to +632
_ => {
if tlv_type == TLV_INVREQ_METADATA {
return Err(Bolt12ParseError::Decode(DecodeError::InvalidValue));
}
if !SIGNATURE_TYPES.contains(&tlv_type) {
included_types.insert(tlv_type);
included_records.push((
tlv_type,
record.end - record.record_bytes.len(),
record.end,
));
}
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't quite tell from the spec where it defines these distinctions. Why isn't any unknown even TLV rejected? Seems we are only reject unknown even signature TLV types.

Comment thread lightning/src/offers/payer_proof.rs Outdated
Move the invoice/refund payer key derivation logic into reusable helpers so
payer proofs can derive the same signing keys without duplicating the metadata
and signer flow.
Comment thread lightning/src/offers/payer_proof.rs
Comment thread lightning/src/offers/payer_proof.rs Outdated
let offer_records = TlvStream::new(bytes).range(OFFER_TYPES);
let invoice_request_records = TlvStream::new(bytes)
.range(INVOICE_REQUEST_TYPES)
.filter(|record| record.r#type != PAYER_METADATA_TYPE);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This filter is dead code. PAYER_METADATA_TYPE is 0, and INVOICE_REQUEST_TYPES is 80..160. Type 0 can never appear in range 80..160, so the filter never matches. Additionally, the producer (serialize_payer_proof) never emits type 0 since include_type rejects it, so even the OFFER_TYPES range (1..80) wouldn't contain it.

Suggested change
.filter(|record| record.r#type != PAYER_METADATA_TYPE);
let invoice_request_records = TlvStream::new(bytes)
.range(INVOICE_REQUEST_TYPES);

Comment thread lightning/src/offers/payer_proof.rs Outdated
Comment thread lightning/src/offers/payer_proof.rs Outdated
Comment thread lightning/src/offers/payer_proof.rs Outdated
Comment thread lightning/src/offers/payer_proof.rs Outdated
Comment thread lightning/src/offers/payer_proof.rs Outdated
Comment thread lightning/src/offers/payer_proof.rs
Comment thread lightning/src/offers/payer_proof.rs Outdated
@vincenzopalazzo vincenzopalazzo force-pushed the macros/proof-of-payment-bolt12-spec branch from 664617f to dbfd2a5 Compare April 15, 2026 17:31
Comment thread lightning/src/ln/channelmanager.rs
Comment thread lightning/src/ln/channelmanager.rs
@vincenzopalazzo vincenzopalazzo force-pushed the macros/proof-of-payment-bolt12-spec branch 5 times, most recently from 3e056b2 to a1064e2 Compare April 15, 2026 18:35
vincenzopalazzo and others added 4 commits April 15, 2026 22:17
Add the payer proof types, selective disclosure merkle support, parsing, and
tests for constructing and validating BOLT 12 payer proofs from invoices.
Rename the old PaidBolt12Invoice enum to Bolt12InvoiceType, move it out of events, and update outbound payment plumbing to store the renamed invoice type directly.
Encapsulate invoice, preimage, and nonce in PaidBolt12Invoice and
surface it in PaymentSent. Rework builder to return UnsignedPayerProof
with SignFn/sign_message integration, use encode_tlv_stream! for
serialization, move helpers to DisclosedFields methods, and address
naming conventions and TLV validation feedback.

Co-Authored-By: Jeffrey Czyz <jkczyz@gmail.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Don't require payer proof missing hashes to remain TLV-sorted now that the spec uses DFS traversal order.

(cherry picked from commit 334bb05)
@vincenzopalazzo vincenzopalazzo force-pushed the macros/proof-of-payment-bolt12-spec branch from 56efb64 to 1ee2f1a Compare April 15, 2026 20:20
Comment on lines +490 to +495
/// Compound value for the payer signature TLV (type 250): a schnorr signature
/// followed by optional UTF-8 note bytes.
struct PayerSignatureWithNoteRef<'a> {
signature: &'a Signature,
note: Option<&'a str>,
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this?


impl PayerProof {
/// The payment preimage proving the invoice was paid.
pub fn preimage(&self) -> PaymentPreimage {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not done

Comment on lines +660 to +663
/// The payer's public key (who paid).
pub fn payer_id(&self) -> PublicKey {
self.contents.payer_id
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In InvoiceRequest, we use payer_signing_pubkey so we should do the same here.

Comment on lines +512 to +515
/// The payer's note, if any.
pub fn payer_note(&self) -> Option<PrintableString<'_>> {
self.contents.payer_note.as_deref().map(PrintableString)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not done.

Comment on lines +1575 to +1583
fn test_parsing_rejects_payer_metadata() {
let proof = build_round_trip_proof_with_multiple_trailing_omitted_tlvs();
let mut bytes = Vec::new();
write_tlv_record_bytes(&mut bytes, PAYER_METADATA_TYPE, &[1; 32]);
bytes.extend_from_slice(proof.bytes());

let result = PayerProof::try_from(bytes);
assert!(matches!(result, Err(Bolt12ParseError::Decode(DecodeError::InvalidValue))));
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: This test asserts the wrong error variant and should fail at runtime.

FullPayerProofTlvStream starts with OfferTlvStream (range OFFER_TYPES = 1..80). Since type 0 falls below this range, the CursorReadable for OfferTlvStream reads the type, finds it out of range, rewinds, and breaks. All 7 CursorReadable components do the same — they all skip type 0. The cursor stays at position 0 forever.

Result: all TLV fields are None. ParsedPayerProofFields::try_from then fails at:

let payer_id = payer_id.ok_or(Bolt12ParseError::InvalidSemantics(
    Bolt12SemanticError::MissingPayerSigningPubkey,
))?;

This produces Bolt12ParseError::InvalidSemantics(MissingPayerSigningPubkey), not Bolt12ParseError::Decode(DecodeError::InvalidValue) as asserted here.

Fix: either change the assertion to match the actual error, or (better) add an explicit type-0 rejection in the parser so the error is clear:

Suggested change
fn test_parsing_rejects_payer_metadata() {
let proof = build_round_trip_proof_with_multiple_trailing_omitted_tlvs();
let mut bytes = Vec::new();
write_tlv_record_bytes(&mut bytes, PAYER_METADATA_TYPE, &[1; 32]);
bytes.extend_from_slice(proof.bytes());
let result = PayerProof::try_from(bytes);
assert!(matches!(result, Err(Bolt12ParseError::Decode(DecodeError::InvalidValue))));
}
fn test_parsing_rejects_payer_metadata() {
let proof = build_round_trip_proof_with_multiple_trailing_omitted_tlvs();
let mut bytes = Vec::new();
write_tlv_record_bytes(&mut bytes, PAYER_METADATA_TYPE, &[1; 32]);
bytes.extend_from_slice(proof.bytes());
let result = PayerProof::try_from(bytes);
assert!(result.is_err(), "Payer metadata (type 0) should be rejected");
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ParsedMessage checks that all bytes are consumed, so it should fail before then.

Comment on lines +848 to +853
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
let parsed_proof = ParsedMessage::<FullPayerProofTlvStream>::try_from(bytes)?;
let ParsedMessage { bytes, tlv_stream } = parsed_proof;
let ParsedPayerProofFields { contents, omitted_markers, missing_hashes, leaf_hashes } =
ParsedPayerProofFields::try_from(tlv_stream)?;
let included_records: Vec<_> = tlv_stream_iter(&bytes).collect();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Residual panic on crafted input (extends prior TlvStream issue):

ParsedMessage<FullPayerProofTlvStream> validates TLV framing only for types in covered ranges. There's a type gap at 1001–999,999,999 (between PayerProofTlvStream range 240..=1000 and ExperimentalOfferTlvStream range 1,000,000,000+). The CursorReadable for each component reads the type BigSize, finds it out of range, rewinds, and breaks — without reading the length or value bytes.

If a crafted proof contains a TLV with a type in this gap and a malformed BigSize length (e.g., 0xFD prefix followed by only 1 byte), ParsedMessage::try_from succeeds (cursor stuck at the gap TLV), but tlv_stream_iter(&bytes) here calls TlvStream::new(&bytes) which iterates over the full bytes. When it reaches the gap TLV and tries to read the truncated length, BigSize::read().unwrap() panics.

This is a DoS vector on untrusted input. Fix options:

  1. Check that ParsedMessage consumed all bytes (cursor at end) and reject if not
  2. Make TlvStream::next() return None instead of panicking on malformed BigSize
  3. Add a validate_tlv_framing(&bytes) pass before tlv_stream_iter

Option 1 is simplest and also catches other gap issues (e.g., type 0 from the test above):

let parsed_proof = ParsedMessage::<FullPayerProofTlvStream>::try_from(bytes)?;
// Reject if ParsedMessage didn't consume all bytes (gap TLVs, type 0, etc.)
// (would need ParsedMessage to expose cursor position or a "fully consumed" check)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe (1) is exactly what ParsedMessage does, but wouldn't hurt to add a test.

Comment on lines +551 to +559
type FullPayerProofTlvStream = (
OfferTlvStream,
InvoiceRequestTlvStream,
InvoiceTlvStream,
PayerProofTlvStream,
ExperimentalOfferTlvStream,
ExperimentalInvoiceRequestTlvStream,
ExperimentalInvoiceTlvStream,
);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Design concern: FullPayerProofTlvStream omits PayerTlvStream (type 0) from its component list. This means type 0 TLVs in untrusted input are silently skipped rather than explicitly rejected, causing confusing downstream errors (MissingPayerSigningPubkey instead of a clear "payer metadata not allowed" error).

Consider either:

  1. Adding PayerTlvStream as the first component (then checking payer_metadata.is_none() in ParsedPayerProofFields::try_from)
  2. Or adding a post-parse check that no types below OFFER_TYPES.start exist in the bytes

This would also fix the test_parsing_rejects_payer_metadata test which currently asserts the wrong error variant.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe this is the case just as it isn't for FullOfferTlvStream. @vincenzopalazzo Could you write a test that confirms this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specifically, when using PayerProof::try_from, which uses ParsedMessage::<FullPayerProofTlvStream>::try_from.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

6 participants