Skip to content

Add stateless BOLT 12 payer proof support - #1045

Merged
tnull merged 1 commit into
lightningdevkit:mainfrom
vincenzopalazzo:claude/bolt12-payer-proof-stateless
Aug 12, 2026
Merged

Add stateless BOLT 12 payer proof support#1045
tnull merged 1 commit into
lightningdevkit:mainfrom
vincenzopalazzo:claude/bolt12-payer-proof-stateless

Conversation

@vincenzopalazzo

Copy link
Copy Markdown
Member

Supersedes #845, reworked per the review feedback there: no payment-store persistence, and the API is stateless so we stay free to change or drop it once the verification side is worked out.

Summary

Exposes Bolt12Payment::create_payer_proof, which builds a BOLT 12 payer proof for a payment this node made:

let proof = node.bolt12_payment().create_payer_proof(
payment_id,// from Event::PaymentSuccessful
payment_preimage,// from Event::PaymentSuccessful
paid_invoice,// Event::PaymentSuccessful::bolt12_invoiceSome(options),)?;

PayerProofOptions controls which optional invoice fields are selectively disclosed (offer description, offer issuer, invoice amount, invoice creation time, plus arbitrary extra TLV types) and lets the payer attach a note. A proof always commits to the payer id, payment hash, and issuer signing pubkey.

Nothing is persisted: PaymentKind and the payment store are untouched, so there's no serialization change and no compatibility note. Callers hold on to the invoice themselves if they want to build a proof later.

Payments settled via a static invoice, i.e., async payments, can't be proven this way and are rejected with PayerProofUnavailable.

Why a node-side method at all

It was suggested that, since the invoice is already exposed on Event::PaymentSuccessful, we could skip the API and just document building a proof with LDK's methods directly. That doesn't work today — both LDK entry points need key material LDK Node deliberately doesn't expose:

  • PaidBolt12Invoice::prove_payer_derived(preimage, expanded_key, payment_id, secp) takes an &ExpandedKey.
  • PaidBolt12Invoice::prove_payer(preimage) avoids that, but returns an UnsignedPayerProof that must be signed with the derived payer key, which means Bolt12Invoice::derive_payer_signing_keys(expanded_key, secp)ExpandedKey again.

In LDK Node ExpandedKey is only reachable via KeysManager, which is pub(crate), and Node::sign_message is a node-key BIP137 message signer, so it's no help here. Hence one thin method that supplies the expanded key; everything else is caller-provided.

Happy to expose a lower-level signing hook instead if that's preferred.

Test plan

  • cargo fmt --all -- --check
  • cargo check --lib --tests, cargo check --lib --features uniffi
  • cargo clippy --lib -- -A warnings -D clippy::unwrap_used -A clippy::tabs_in_doc_comments
  • cargo test --lib
  • cargo test --test integration_tests_rust simple_bolt12_send_receive — extended to build a proof from values captured off the event, asserting the preimage, disclosed amount and note, and that undisclosed fields stay absent
  • Python UniFFI bindings generate

Disclosure

This PR was prepared with AI assistance (Claude Code).

🤖 Generated with Claude Code

@ldk-reviews-bot

ldk-reviews-bot commented Aug 12, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @tnull 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.

@vincenzopalazzo
vincenzopalazzoforce-pushed the claude/bolt12-payer-proof-stateless branch from 2f704b6 to 395bd23CompareAugust 12, 2026 10:54

@tnulltnull left a comment

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.

One question, otherwise LGTM

Feel free to undraft.

Comment threadsrc/payment/bolt12.rs Outdated
@tnulltnull added this to the 0.8 milestone Aug 12, 2026
@vincenzopalazzo
vincenzopalazzoforce-pushed the claude/bolt12-payer-proof-stateless branch from 395bd23 to aa9f3fbCompareAugust 12, 2026 11:08
@vincenzopalazzo
vincenzopalazzo marked this pull request as ready for review August 12, 2026 11:09
Comment threadsrc/ffi/types.rs
Comment threadsrc/payment/bolt12.rs
Comment threadsrc/ffi/types.rs
Comment threadsrc/payment/bolt12.rs Outdated
Comment threadtests/integration_tests_rust.rs
Expose `Bolt12Payment::create_payer_proof`, which builds a BOLT 12 payer
proof for a payment this node made, with `PayerProofOptions` controlling
which optional invoice fields are selectively disclosed.
The method is stateless: the payment id, payment preimage, and paid
invoice are all taken from `Event::PaymentSuccessful` and handed back to
us by the caller, so nothing is read from or written to the payment
store. The node only contributes the expanded key needed to re-derive the
payer signing key, which is the one part users can't supply themselves.
Keeping it stateless means we don't have to decide up front where paid
BOLT 12 invoices should eventually live, and leaves us free to change or
drop this API once the verification side is worked out.
Payments settled via a static invoice, i.e., async payments, can't be
proven this way and are rejected with `PayerProofUnavailable`.
This commit was written with AI assistance.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vincenzopalazzo
vincenzopalazzoforce-pushed the claude/bolt12-payer-proof-stateless branch from aa9f3fb to d848ff5CompareAugust 12, 2026 12:09

@tnulltnull left a comment

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.

LGTM.

Two non-blocking nits: commit description still mentions PayerProofUnavailable and the commit shows as 'Unverified' here on Github, might want to start signing commits.

@tnull
tnull merged commit 5dfb695 into lightningdevkit:mainAug 12, 2026
22 of 25 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@vincenzopalazzo@ldk-reviews-bot@tnull
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Add stateless BOLT 12 payer proof support by vincenzopalazzo · Pull Request #1045 · lightningdevkit/ldk-node · GitHub
Skip to content

Add stateless BOLT 12 payer proof support - #1045

Merged
tnull merged 1 commit into
lightningdevkit:mainfrom
vincenzopalazzo:claude/bolt12-payer-proof-stateless
Aug 12, 2026
Merged

Add stateless BOLT 12 payer proof support#1045
tnull merged 1 commit into
lightningdevkit:mainfrom
vincenzopalazzo:claude/bolt12-payer-proof-stateless

Conversation

@vincenzopalazzo

Copy link
Copy Markdown
Member

Supersedes #845, reworked per the review feedback there: no payment-store persistence, and the API is stateless so we stay free to change or drop it once the verification side is worked out.

Summary

Exposes Bolt12Payment::create_payer_proof, which builds a BOLT 12 payer proof for a payment this node made:

let proof = node.bolt12_payment().create_payer_proof(
payment_id,// from Event::PaymentSuccessful
payment_preimage,// from Event::PaymentSuccessful
paid_invoice,// Event::PaymentSuccessful::bolt12_invoiceSome(options),)?;

PayerProofOptions controls which optional invoice fields are selectively disclosed (offer description, offer issuer, invoice amount, invoice creation time, plus arbitrary extra TLV types) and lets the payer attach a note. A proof always commits to the payer id, payment hash, and issuer signing pubkey.

Nothing is persisted: PaymentKind and the payment store are untouched, so there's no serialization change and no compatibility note. Callers hold on to the invoice themselves if they want to build a proof later.

Payments settled via a static invoice, i.e., async payments, can't be proven this way and are rejected with PayerProofUnavailable.

Why a node-side method at all

It was suggested that, since the invoice is already exposed on Event::PaymentSuccessful, we could skip the API and just document building a proof with LDK's methods directly. That doesn't work today — both LDK entry points need key material LDK Node deliberately doesn't expose:

  • PaidBolt12Invoice::prove_payer_derived(preimage, expanded_key, payment_id, secp) takes an &ExpandedKey.
  • PaidBolt12Invoice::prove_payer(preimage) avoids that, but returns an UnsignedPayerProof that must be signed with the derived payer key, which means Bolt12Invoice::derive_payer_signing_keys(expanded_key, secp)ExpandedKey again.

In LDK Node ExpandedKey is only reachable via KeysManager, which is pub(crate), and Node::sign_message is a node-key BIP137 message signer, so it's no help here. Hence one thin method that supplies the expanded key; everything else is caller-provided.

Happy to expose a lower-level signing hook instead if that's preferred.

Test plan

  • cargo fmt --all -- --check
  • cargo check --lib --tests, cargo check --lib --features uniffi
  • cargo clippy --lib -- -A warnings -D clippy::unwrap_used -A clippy::tabs_in_doc_comments
  • cargo test --lib
  • cargo test --test integration_tests_rust simple_bolt12_send_receive — extended to build a proof from values captured off the event, asserting the preimage, disclosed amount and note, and that undisclosed fields stay absent
  • Python UniFFI bindings generate

Disclosure

This PR was prepared with AI assistance (Claude Code).

🤖 Generated with Claude Code

@ldk-reviews-bot

ldk-reviews-bot commented Aug 12, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @tnull 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.

@vincenzopalazzo
vincenzopalazzoforce-pushed the claude/bolt12-payer-proof-stateless branch from 2f704b6 to 395bd23CompareAugust 12, 2026 10:54

@tnulltnull left a comment

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.

One question, otherwise LGTM

Feel free to undraft.

Comment threadsrc/payment/bolt12.rs Outdated
@tnulltnull added this to the 0.8 milestone Aug 12, 2026
@vincenzopalazzo
vincenzopalazzoforce-pushed the claude/bolt12-payer-proof-stateless branch from 395bd23 to aa9f3fbCompareAugust 12, 2026 11:08
@vincenzopalazzo
vincenzopalazzo marked this pull request as ready for review August 12, 2026 11:09
Comment threadsrc/ffi/types.rs
Comment threadsrc/payment/bolt12.rs
Comment threadsrc/ffi/types.rs
Comment threadsrc/payment/bolt12.rs Outdated
Comment threadtests/integration_tests_rust.rs
Expose `Bolt12Payment::create_payer_proof`, which builds a BOLT 12 payer
proof for a payment this node made, with `PayerProofOptions` controlling
which optional invoice fields are selectively disclosed.
The method is stateless: the payment id, payment preimage, and paid
invoice are all taken from `Event::PaymentSuccessful` and handed back to
us by the caller, so nothing is read from or written to the payment
store. The node only contributes the expanded key needed to re-derive the
payer signing key, which is the one part users can't supply themselves.
Keeping it stateless means we don't have to decide up front where paid
BOLT 12 invoices should eventually live, and leaves us free to change or
drop this API once the verification side is worked out.
Payments settled via a static invoice, i.e., async payments, can't be
proven this way and are rejected with `PayerProofUnavailable`.
This commit was written with AI assistance.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vincenzopalazzo
vincenzopalazzoforce-pushed the claude/bolt12-payer-proof-stateless branch from aa9f3fb to d848ff5CompareAugust 12, 2026 12:09

@tnulltnull left a comment

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.

LGTM.

Two non-blocking nits: commit description still mentions PayerProofUnavailable and the commit shows as 'Unverified' here on Github, might want to start signing commits.

@tnull
tnull merged commit 5dfb695 into lightningdevkit:mainAug 12, 2026
22 of 25 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@vincenzopalazzo@ldk-reviews-bot@tnull
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add stateless BOLT 12 payer proof support by vincenzopalazzo · Pull Request #1045 · lightningdevkit/ldk-node · GitHub
Skip to content

Add stateless BOLT 12 payer proof support - #1045

Merged
tnull merged 1 commit into
lightningdevkit:mainfrom
vincenzopalazzo:claude/bolt12-payer-proof-stateless
Aug 12, 2026
Merged

Add stateless BOLT 12 payer proof support#1045
tnull merged 1 commit into
lightningdevkit:mainfrom
vincenzopalazzo:claude/bolt12-payer-proof-stateless

Conversation

@vincenzopalazzo

Copy link
Copy Markdown
Member

Supersedes #845, reworked per the review feedback there: no payment-store persistence, and the API is stateless so we stay free to change or drop it once the verification side is worked out.

Summary

Exposes Bolt12Payment::create_payer_proof, which builds a BOLT 12 payer proof for a payment this node made:

let proof = node.bolt12_payment().create_payer_proof(
payment_id,// from Event::PaymentSuccessful
payment_preimage,// from Event::PaymentSuccessful
paid_invoice,// Event::PaymentSuccessful::bolt12_invoiceSome(options),)?;

PayerProofOptions controls which optional invoice fields are selectively disclosed (offer description, offer issuer, invoice amount, invoice creation time, plus arbitrary extra TLV types) and lets the payer attach a note. A proof always commits to the payer id, payment hash, and issuer signing pubkey.

Nothing is persisted: PaymentKind and the payment store are untouched, so there's no serialization change and no compatibility note. Callers hold on to the invoice themselves if they want to build a proof later.

Payments settled via a static invoice, i.e., async payments, can't be proven this way and are rejected with PayerProofUnavailable.

Why a node-side method at all

It was suggested that, since the invoice is already exposed on Event::PaymentSuccessful, we could skip the API and just document building a proof with LDK's methods directly. That doesn't work today — both LDK entry points need key material LDK Node deliberately doesn't expose:

  • PaidBolt12Invoice::prove_payer_derived(preimage, expanded_key, payment_id, secp) takes an &ExpandedKey.
  • PaidBolt12Invoice::prove_payer(preimage) avoids that, but returns an UnsignedPayerProof that must be signed with the derived payer key, which means Bolt12Invoice::derive_payer_signing_keys(expanded_key, secp)ExpandedKey again.

In LDK Node ExpandedKey is only reachable via KeysManager, which is pub(crate), and Node::sign_message is a node-key BIP137 message signer, so it's no help here. Hence one thin method that supplies the expanded key; everything else is caller-provided.

Happy to expose a lower-level signing hook instead if that's preferred.

Test plan

  • cargo fmt --all -- --check
  • cargo check --lib --tests, cargo check --lib --features uniffi
  • cargo clippy --lib -- -A warnings -D clippy::unwrap_used -A clippy::tabs_in_doc_comments
  • cargo test --lib
  • cargo test --test integration_tests_rust simple_bolt12_send_receive — extended to build a proof from values captured off the event, asserting the preimage, disclosed amount and note, and that undisclosed fields stay absent
  • Python UniFFI bindings generate

Disclosure

This PR was prepared with AI assistance (Claude Code).

🤖 Generated with Claude Code

@ldk-reviews-bot

ldk-reviews-bot commented Aug 12, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @tnull 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.

@vincenzopalazzo
vincenzopalazzoforce-pushed the claude/bolt12-payer-proof-stateless branch from 2f704b6 to 395bd23CompareAugust 12, 2026 10:54

@tnulltnull left a comment

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.

One question, otherwise LGTM

Feel free to undraft.

Comment threadsrc/payment/bolt12.rs Outdated
@tnulltnull added this to the 0.8 milestone Aug 12, 2026
@vincenzopalazzo
vincenzopalazzoforce-pushed the claude/bolt12-payer-proof-stateless branch from 395bd23 to aa9f3fbCompareAugust 12, 2026 11:08
@vincenzopalazzo
vincenzopalazzo marked this pull request as ready for review August 12, 2026 11:09
Comment threadsrc/ffi/types.rs
Comment threadsrc/payment/bolt12.rs
Comment threadsrc/ffi/types.rs
Comment threadsrc/payment/bolt12.rs Outdated
Comment threadtests/integration_tests_rust.rs
Expose `Bolt12Payment::create_payer_proof`, which builds a BOLT 12 payer
proof for a payment this node made, with `PayerProofOptions` controlling
which optional invoice fields are selectively disclosed.
The method is stateless: the payment id, payment preimage, and paid
invoice are all taken from `Event::PaymentSuccessful` and handed back to
us by the caller, so nothing is read from or written to the payment
store. The node only contributes the expanded key needed to re-derive the
payer signing key, which is the one part users can't supply themselves.
Keeping it stateless means we don't have to decide up front where paid
BOLT 12 invoices should eventually live, and leaves us free to change or
drop this API once the verification side is worked out.
Payments settled via a static invoice, i.e., async payments, can't be
proven this way and are rejected with `PayerProofUnavailable`.
This commit was written with AI assistance.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vincenzopalazzo
vincenzopalazzoforce-pushed the claude/bolt12-payer-proof-stateless branch from aa9f3fb to d848ff5CompareAugust 12, 2026 12:09

@tnulltnull left a comment

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.

LGTM.

Two non-blocking nits: commit description still mentions PayerProofUnavailable and the commit shows as 'Unverified' here on Github, might want to start signing commits.

@tnull
tnull merged commit 5dfb695 into lightningdevkit:mainAug 12, 2026
22 of 25 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@vincenzopalazzo@ldk-reviews-bot@tnull
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add stateless BOLT 12 payer proof support by vincenzopalazzo · Pull Request #1045 · lightningdevkit/ldk-node · GitHub
Skip to content

Add stateless BOLT 12 payer proof support - #1045

Merged
tnull merged 1 commit into
lightningdevkit:mainfrom
vincenzopalazzo:claude/bolt12-payer-proof-stateless
Aug 12, 2026
Merged

Add stateless BOLT 12 payer proof support#1045
tnull merged 1 commit into
lightningdevkit:mainfrom
vincenzopalazzo:claude/bolt12-payer-proof-stateless

Conversation

@vincenzopalazzo

Copy link
Copy Markdown
Member

Supersedes #845, reworked per the review feedback there: no payment-store persistence, and the API is stateless so we stay free to change or drop it once the verification side is worked out.

Summary

Exposes Bolt12Payment::create_payer_proof, which builds a BOLT 12 payer proof for a payment this node made:

let proof = node.bolt12_payment().create_payer_proof(
payment_id,// from Event::PaymentSuccessful
payment_preimage,// from Event::PaymentSuccessful
paid_invoice,// Event::PaymentSuccessful::bolt12_invoiceSome(options),)?;

PayerProofOptions controls which optional invoice fields are selectively disclosed (offer description, offer issuer, invoice amount, invoice creation time, plus arbitrary extra TLV types) and lets the payer attach a note. A proof always commits to the payer id, payment hash, and issuer signing pubkey.

Nothing is persisted: PaymentKind and the payment store are untouched, so there's no serialization change and no compatibility note. Callers hold on to the invoice themselves if they want to build a proof later.

Payments settled via a static invoice, i.e., async payments, can't be proven this way and are rejected with PayerProofUnavailable.

Why a node-side method at all

It was suggested that, since the invoice is already exposed on Event::PaymentSuccessful, we could skip the API and just document building a proof with LDK's methods directly. That doesn't work today — both LDK entry points need key material LDK Node deliberately doesn't expose:

  • PaidBolt12Invoice::prove_payer_derived(preimage, expanded_key, payment_id, secp) takes an &ExpandedKey.
  • PaidBolt12Invoice::prove_payer(preimage) avoids that, but returns an UnsignedPayerProof that must be signed with the derived payer key, which means Bolt12Invoice::derive_payer_signing_keys(expanded_key, secp)ExpandedKey again.

In LDK Node ExpandedKey is only reachable via KeysManager, which is pub(crate), and Node::sign_message is a node-key BIP137 message signer, so it's no help here. Hence one thin method that supplies the expanded key; everything else is caller-provided.

Happy to expose a lower-level signing hook instead if that's preferred.

Test plan

  • cargo fmt --all -- --check
  • cargo check --lib --tests, cargo check --lib --features uniffi
  • cargo clippy --lib -- -A warnings -D clippy::unwrap_used -A clippy::tabs_in_doc_comments
  • cargo test --lib
  • cargo test --test integration_tests_rust simple_bolt12_send_receive — extended to build a proof from values captured off the event, asserting the preimage, disclosed amount and note, and that undisclosed fields stay absent
  • Python UniFFI bindings generate

Disclosure

This PR was prepared with AI assistance (Claude Code).

🤖 Generated with Claude Code

@ldk-reviews-bot

ldk-reviews-bot commented Aug 12, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @tnull 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.

@vincenzopalazzo
vincenzopalazzoforce-pushed the claude/bolt12-payer-proof-stateless branch from 2f704b6 to 395bd23CompareAugust 12, 2026 10:54

@tnulltnull left a comment

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.

One question, otherwise LGTM

Feel free to undraft.

Comment threadsrc/payment/bolt12.rs Outdated
@tnulltnull added this to the 0.8 milestone Aug 12, 2026
@vincenzopalazzo
vincenzopalazzoforce-pushed the claude/bolt12-payer-proof-stateless branch from 395bd23 to aa9f3fbCompareAugust 12, 2026 11:08
@vincenzopalazzo
vincenzopalazzo marked this pull request as ready for review August 12, 2026 11:09
Comment threadsrc/ffi/types.rs
Comment threadsrc/payment/bolt12.rs
Comment threadsrc/ffi/types.rs
Comment threadsrc/payment/bolt12.rs Outdated
Comment threadtests/integration_tests_rust.rs
Expose `Bolt12Payment::create_payer_proof`, which builds a BOLT 12 payer
proof for a payment this node made, with `PayerProofOptions` controlling
which optional invoice fields are selectively disclosed.
The method is stateless: the payment id, payment preimage, and paid
invoice are all taken from `Event::PaymentSuccessful` and handed back to
us by the caller, so nothing is read from or written to the payment
store. The node only contributes the expanded key needed to re-derive the
payer signing key, which is the one part users can't supply themselves.
Keeping it stateless means we don't have to decide up front where paid
BOLT 12 invoices should eventually live, and leaves us free to change or
drop this API once the verification side is worked out.
Payments settled via a static invoice, i.e., async payments, can't be
proven this way and are rejected with `PayerProofUnavailable`.
This commit was written with AI assistance.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vincenzopalazzo
vincenzopalazzoforce-pushed the claude/bolt12-payer-proof-stateless branch from aa9f3fb to d848ff5CompareAugust 12, 2026 12:09

@tnulltnull left a comment

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.

LGTM.

Two non-blocking nits: commit description still mentions PayerProofUnavailable and the commit shows as 'Unverified' here on Github, might want to start signing commits.

@tnull
tnull merged commit 5dfb695 into lightningdevkit:mainAug 12, 2026
22 of 25 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@vincenzopalazzo@ldk-reviews-bot@tnull
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Add stateless BOLT 12 payer proof support by vincenzopalazzo · Pull Request #1045 · lightningdevkit/ldk-node · GitHub
Skip to content

Add stateless BOLT 12 payer proof support - #1045

Merged
tnull merged 1 commit into
lightningdevkit:mainfrom
vincenzopalazzo:claude/bolt12-payer-proof-stateless
Aug 12, 2026
Merged

Add stateless BOLT 12 payer proof support#1045
tnull merged 1 commit into
lightningdevkit:mainfrom
vincenzopalazzo:claude/bolt12-payer-proof-stateless

Conversation

@vincenzopalazzo

Copy link
Copy Markdown
Member

Supersedes #845, reworked per the review feedback there: no payment-store persistence, and the API is stateless so we stay free to change or drop it once the verification side is worked out.

Summary

Exposes Bolt12Payment::create_payer_proof, which builds a BOLT 12 payer proof for a payment this node made:

let proof = node.bolt12_payment().create_payer_proof(
payment_id,// from Event::PaymentSuccessful
payment_preimage,// from Event::PaymentSuccessful
paid_invoice,// Event::PaymentSuccessful::bolt12_invoiceSome(options),)?;

PayerProofOptions controls which optional invoice fields are selectively disclosed (offer description, offer issuer, invoice amount, invoice creation time, plus arbitrary extra TLV types) and lets the payer attach a note. A proof always commits to the payer id, payment hash, and issuer signing pubkey.

Nothing is persisted: PaymentKind and the payment store are untouched, so there's no serialization change and no compatibility note. Callers hold on to the invoice themselves if they want to build a proof later.

Payments settled via a static invoice, i.e., async payments, can't be proven this way and are rejected with PayerProofUnavailable.

Why a node-side method at all

It was suggested that, since the invoice is already exposed on Event::PaymentSuccessful, we could skip the API and just document building a proof with LDK's methods directly. That doesn't work today — both LDK entry points need key material LDK Node deliberately doesn't expose:

  • PaidBolt12Invoice::prove_payer_derived(preimage, expanded_key, payment_id, secp) takes an &ExpandedKey.
  • PaidBolt12Invoice::prove_payer(preimage) avoids that, but returns an UnsignedPayerProof that must be signed with the derived payer key, which means Bolt12Invoice::derive_payer_signing_keys(expanded_key, secp)ExpandedKey again.

In LDK Node ExpandedKey is only reachable via KeysManager, which is pub(crate), and Node::sign_message is a node-key BIP137 message signer, so it's no help here. Hence one thin method that supplies the expanded key; everything else is caller-provided.

Happy to expose a lower-level signing hook instead if that's preferred.

Test plan

  • cargo fmt --all -- --check
  • cargo check --lib --tests, cargo check --lib --features uniffi
  • cargo clippy --lib -- -A warnings -D clippy::unwrap_used -A clippy::tabs_in_doc_comments
  • cargo test --lib
  • cargo test --test integration_tests_rust simple_bolt12_send_receive — extended to build a proof from values captured off the event, asserting the preimage, disclosed amount and note, and that undisclosed fields stay absent
  • Python UniFFI bindings generate

Disclosure

This PR was prepared with AI assistance (Claude Code).

🤖 Generated with Claude Code

@ldk-reviews-bot

ldk-reviews-bot commented Aug 12, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @tnull 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.

@vincenzopalazzo
vincenzopalazzoforce-pushed the claude/bolt12-payer-proof-stateless branch from 2f704b6 to 395bd23CompareAugust 12, 2026 10:54

@tnulltnull left a comment

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.

One question, otherwise LGTM

Feel free to undraft.

Comment threadsrc/payment/bolt12.rs Outdated
@tnulltnull added this to the 0.8 milestone Aug 12, 2026
@vincenzopalazzo
vincenzopalazzoforce-pushed the claude/bolt12-payer-proof-stateless branch from 395bd23 to aa9f3fbCompareAugust 12, 2026 11:08
@vincenzopalazzo
vincenzopalazzo marked this pull request as ready for review August 12, 2026 11:09
Comment threadsrc/ffi/types.rs
Comment threadsrc/payment/bolt12.rs
Comment threadsrc/ffi/types.rs
Comment threadsrc/payment/bolt12.rs Outdated
Comment threadtests/integration_tests_rust.rs
Expose `Bolt12Payment::create_payer_proof`, which builds a BOLT 12 payer
proof for a payment this node made, with `PayerProofOptions` controlling
which optional invoice fields are selectively disclosed.
The method is stateless: the payment id, payment preimage, and paid
invoice are all taken from `Event::PaymentSuccessful` and handed back to
us by the caller, so nothing is read from or written to the payment
store. The node only contributes the expanded key needed to re-derive the
payer signing key, which is the one part users can't supply themselves.
Keeping it stateless means we don't have to decide up front where paid
BOLT 12 invoices should eventually live, and leaves us free to change or
drop this API once the verification side is worked out.
Payments settled via a static invoice, i.e., async payments, can't be
proven this way and are rejected with `PayerProofUnavailable`.
This commit was written with AI assistance.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vincenzopalazzo
vincenzopalazzoforce-pushed the claude/bolt12-payer-proof-stateless branch from aa9f3fb to d848ff5CompareAugust 12, 2026 12:09

@tnulltnull left a comment

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.

LGTM.

Two non-blocking nits: commit description still mentions PayerProofUnavailable and the commit shows as 'Unverified' here on Github, might want to start signing commits.

@tnull
tnull merged commit 5dfb695 into lightningdevkit:mainAug 12, 2026
22 of 25 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@vincenzopalazzo@ldk-reviews-bot@tnull
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add stateless BOLT 12 payer proof support by vincenzopalazzo · Pull Request #1045 · lightningdevkit/ldk-node · GitHub
Skip to content

Add stateless BOLT 12 payer proof support - #1045

Merged
tnull merged 1 commit into
lightningdevkit:mainfrom
vincenzopalazzo:claude/bolt12-payer-proof-stateless
Aug 12, 2026
Merged

Add stateless BOLT 12 payer proof support#1045
tnull merged 1 commit into
lightningdevkit:mainfrom
vincenzopalazzo:claude/bolt12-payer-proof-stateless

Conversation

@vincenzopalazzo

Copy link
Copy Markdown
Member

Supersedes #845, reworked per the review feedback there: no payment-store persistence, and the API is stateless so we stay free to change or drop it once the verification side is worked out.

Summary

Exposes Bolt12Payment::create_payer_proof, which builds a BOLT 12 payer proof for a payment this node made:

let proof = node.bolt12_payment().create_payer_proof(
payment_id,// from Event::PaymentSuccessful
payment_preimage,// from Event::PaymentSuccessful
paid_invoice,// Event::PaymentSuccessful::bolt12_invoiceSome(options),)?;

PayerProofOptions controls which optional invoice fields are selectively disclosed (offer description, offer issuer, invoice amount, invoice creation time, plus arbitrary extra TLV types) and lets the payer attach a note. A proof always commits to the payer id, payment hash, and issuer signing pubkey.

Nothing is persisted: PaymentKind and the payment store are untouched, so there's no serialization change and no compatibility note. Callers hold on to the invoice themselves if they want to build a proof later.

Payments settled via a static invoice, i.e., async payments, can't be proven this way and are rejected with PayerProofUnavailable.

Why a node-side method at all

It was suggested that, since the invoice is already exposed on Event::PaymentSuccessful, we could skip the API and just document building a proof with LDK's methods directly. That doesn't work today — both LDK entry points need key material LDK Node deliberately doesn't expose:

  • PaidBolt12Invoice::prove_payer_derived(preimage, expanded_key, payment_id, secp) takes an &ExpandedKey.
  • PaidBolt12Invoice::prove_payer(preimage) avoids that, but returns an UnsignedPayerProof that must be signed with the derived payer key, which means Bolt12Invoice::derive_payer_signing_keys(expanded_key, secp)ExpandedKey again.

In LDK Node ExpandedKey is only reachable via KeysManager, which is pub(crate), and Node::sign_message is a node-key BIP137 message signer, so it's no help here. Hence one thin method that supplies the expanded key; everything else is caller-provided.

Happy to expose a lower-level signing hook instead if that's preferred.

Test plan

  • cargo fmt --all -- --check
  • cargo check --lib --tests, cargo check --lib --features uniffi
  • cargo clippy --lib -- -A warnings -D clippy::unwrap_used -A clippy::tabs_in_doc_comments
  • cargo test --lib
  • cargo test --test integration_tests_rust simple_bolt12_send_receive — extended to build a proof from values captured off the event, asserting the preimage, disclosed amount and note, and that undisclosed fields stay absent
  • Python UniFFI bindings generate

Disclosure

This PR was prepared with AI assistance (Claude Code).

🤖 Generated with Claude Code

@ldk-reviews-bot

ldk-reviews-bot commented Aug 12, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @tnull 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.

@vincenzopalazzo
vincenzopalazzoforce-pushed the claude/bolt12-payer-proof-stateless branch from 2f704b6 to 395bd23CompareAugust 12, 2026 10:54

@tnulltnull left a comment

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.

One question, otherwise LGTM

Feel free to undraft.

Comment threadsrc/payment/bolt12.rs Outdated
@tnulltnull added this to the 0.8 milestone Aug 12, 2026
@vincenzopalazzo
vincenzopalazzoforce-pushed the claude/bolt12-payer-proof-stateless branch from 395bd23 to aa9f3fbCompareAugust 12, 2026 11:08
@vincenzopalazzo
vincenzopalazzo marked this pull request as ready for review August 12, 2026 11:09
Comment threadsrc/ffi/types.rs
Comment threadsrc/payment/bolt12.rs
Comment threadsrc/ffi/types.rs
Comment threadsrc/payment/bolt12.rs Outdated
Comment threadtests/integration_tests_rust.rs
Expose `Bolt12Payment::create_payer_proof`, which builds a BOLT 12 payer
proof for a payment this node made, with `PayerProofOptions` controlling
which optional invoice fields are selectively disclosed.
The method is stateless: the payment id, payment preimage, and paid
invoice are all taken from `Event::PaymentSuccessful` and handed back to
us by the caller, so nothing is read from or written to the payment
store. The node only contributes the expanded key needed to re-derive the
payer signing key, which is the one part users can't supply themselves.
Keeping it stateless means we don't have to decide up front where paid
BOLT 12 invoices should eventually live, and leaves us free to change or
drop this API once the verification side is worked out.
Payments settled via a static invoice, i.e., async payments, can't be
proven this way and are rejected with `PayerProofUnavailable`.
This commit was written with AI assistance.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vincenzopalazzo
vincenzopalazzoforce-pushed the claude/bolt12-payer-proof-stateless branch from aa9f3fb to d848ff5CompareAugust 12, 2026 12:09

@tnulltnull left a comment

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.

LGTM.

Two non-blocking nits: commit description still mentions PayerProofUnavailable and the commit shows as 'Unverified' here on Github, might want to start signing commits.

@tnull
tnull merged commit 5dfb695 into lightningdevkit:mainAug 12, 2026
22 of 25 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@vincenzopalazzo@ldk-reviews-bot@tnull
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Add stateless BOLT 12 payer proof support by vincenzopalazzo · Pull Request #1045 · lightningdevkit/ldk-node · GitHub
Skip to content

Add stateless BOLT 12 payer proof support - #1045

Merged
tnull merged 1 commit into
lightningdevkit:mainfrom
vincenzopalazzo:claude/bolt12-payer-proof-stateless
Aug 12, 2026
Merged

Add stateless BOLT 12 payer proof support#1045
tnull merged 1 commit into
lightningdevkit:mainfrom
vincenzopalazzo:claude/bolt12-payer-proof-stateless

Conversation

@vincenzopalazzo

Copy link
Copy Markdown
Member

Supersedes #845, reworked per the review feedback there: no payment-store persistence, and the API is stateless so we stay free to change or drop it once the verification side is worked out.

Summary

Exposes Bolt12Payment::create_payer_proof, which builds a BOLT 12 payer proof for a payment this node made:

let proof = node.bolt12_payment().create_payer_proof(
payment_id,// from Event::PaymentSuccessful
payment_preimage,// from Event::PaymentSuccessful
paid_invoice,// Event::PaymentSuccessful::bolt12_invoiceSome(options),)?;

PayerProofOptions controls which optional invoice fields are selectively disclosed (offer description, offer issuer, invoice amount, invoice creation time, plus arbitrary extra TLV types) and lets the payer attach a note. A proof always commits to the payer id, payment hash, and issuer signing pubkey.

Nothing is persisted: PaymentKind and the payment store are untouched, so there's no serialization change and no compatibility note. Callers hold on to the invoice themselves if they want to build a proof later.

Payments settled via a static invoice, i.e., async payments, can't be proven this way and are rejected with PayerProofUnavailable.

Why a node-side method at all

It was suggested that, since the invoice is already exposed on Event::PaymentSuccessful, we could skip the API and just document building a proof with LDK's methods directly. That doesn't work today — both LDK entry points need key material LDK Node deliberately doesn't expose:

  • PaidBolt12Invoice::prove_payer_derived(preimage, expanded_key, payment_id, secp) takes an &ExpandedKey.
  • PaidBolt12Invoice::prove_payer(preimage) avoids that, but returns an UnsignedPayerProof that must be signed with the derived payer key, which means Bolt12Invoice::derive_payer_signing_keys(expanded_key, secp)ExpandedKey again.

In LDK Node ExpandedKey is only reachable via KeysManager, which is pub(crate), and Node::sign_message is a node-key BIP137 message signer, so it's no help here. Hence one thin method that supplies the expanded key; everything else is caller-provided.

Happy to expose a lower-level signing hook instead if that's preferred.

Test plan

  • cargo fmt --all -- --check
  • cargo check --lib --tests, cargo check --lib --features uniffi
  • cargo clippy --lib -- -A warnings -D clippy::unwrap_used -A clippy::tabs_in_doc_comments
  • cargo test --lib
  • cargo test --test integration_tests_rust simple_bolt12_send_receive — extended to build a proof from values captured off the event, asserting the preimage, disclosed amount and note, and that undisclosed fields stay absent
  • Python UniFFI bindings generate

Disclosure

This PR was prepared with AI assistance (Claude Code).

🤖 Generated with Claude Code

@ldk-reviews-bot

ldk-reviews-bot commented Aug 12, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @tnull 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.

@vincenzopalazzo
vincenzopalazzoforce-pushed the claude/bolt12-payer-proof-stateless branch from 2f704b6 to 395bd23CompareAugust 12, 2026 10:54

@tnulltnull left a comment

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.

One question, otherwise LGTM

Feel free to undraft.

Comment threadsrc/payment/bolt12.rs Outdated
@tnulltnull added this to the 0.8 milestone Aug 12, 2026
@vincenzopalazzo
vincenzopalazzoforce-pushed the claude/bolt12-payer-proof-stateless branch from 395bd23 to aa9f3fbCompareAugust 12, 2026 11:08
@vincenzopalazzo
vincenzopalazzo marked this pull request as ready for review August 12, 2026 11:09
Comment threadsrc/ffi/types.rs
Comment threadsrc/payment/bolt12.rs
Comment threadsrc/ffi/types.rs
Comment threadsrc/payment/bolt12.rs Outdated
Comment threadtests/integration_tests_rust.rs
Expose `Bolt12Payment::create_payer_proof`, which builds a BOLT 12 payer
proof for a payment this node made, with `PayerProofOptions` controlling
which optional invoice fields are selectively disclosed.
The method is stateless: the payment id, payment preimage, and paid
invoice are all taken from `Event::PaymentSuccessful` and handed back to
us by the caller, so nothing is read from or written to the payment
store. The node only contributes the expanded key needed to re-derive the
payer signing key, which is the one part users can't supply themselves.
Keeping it stateless means we don't have to decide up front where paid
BOLT 12 invoices should eventually live, and leaves us free to change or
drop this API once the verification side is worked out.
Payments settled via a static invoice, i.e., async payments, can't be
proven this way and are rejected with `PayerProofUnavailable`.
This commit was written with AI assistance.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vincenzopalazzo
vincenzopalazzoforce-pushed the claude/bolt12-payer-proof-stateless branch from aa9f3fb to d848ff5CompareAugust 12, 2026 12:09

@tnulltnull left a comment

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.

LGTM.

Two non-blocking nits: commit description still mentions PayerProofUnavailable and the commit shows as 'Unverified' here on Github, might want to start signing commits.

@tnull
tnull merged commit 5dfb695 into lightningdevkit:mainAug 12, 2026
22 of 25 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@vincenzopalazzo@ldk-reviews-bot@tnull