@@ -18,10 +18,14 @@ use lightning::ln::channelmanager::{OptionalOfferPaymentParams, PaymentId};
1818use lightning:: ln:: outbound_payment:: Retry ;
1919use lightning:: offers:: offer:: { Amount , Offer as LdkOffer , OfferFromHrn , Quantity } ;
2020use lightning:: offers:: parse:: Bolt12SemanticError ;
21+ use lightning:: offers:: payer_proof:: PaidBolt12Invoice as LdkPaidBolt12Invoice ;
22+ #[ cfg( not( feature = "uniffi" ) ) ]
23+ use lightning:: offers:: payer_proof:: PayerProof as LdkPayerProof ;
2124use lightning:: routing:: router:: RouteParametersConfig ;
22- use lightning:: sign:: EntropySource ;
25+ use lightning:: sign:: { EntropySource , NodeSigner } ;
2326#[ cfg( feature = "uniffi" ) ]
2427use lightning:: util:: ser:: { Readable , Writeable } ;
28+ use lightning_types:: payment:: PaymentPreimage ;
2529use lightning_types:: string:: UntrustedString ;
2630
2731use crate :: config:: { AsyncPaymentsRole , Config , LDK_PAYMENT_RETRY_TIMEOUT } ;
@@ -52,6 +56,33 @@ type HumanReadableName = lightning::onion_message::dns_resolution::HumanReadable
5256#[ cfg( feature = "uniffi" ) ]
5357type HumanReadableName = Arc < crate :: ffi:: HumanReadableName > ;
5458
59+ #[ cfg( not( feature = "uniffi" ) ) ]
60+ type PayerProof = LdkPayerProof ;
61+ #[ cfg( feature = "uniffi" ) ]
62+ type PayerProof = Arc < crate :: ffi:: PayerProof > ;
63+
64+ /// Options controlling which optional fields are disclosed in a [BOLT 12] payer proof.
65+ ///
66+ /// A payer proof always commits to the payer id, the payment hash, and the issuer signing
67+ /// pubkey. Everything else is disclosed only if requested here, allowing to reveal just as much
68+ /// of the invoice as the verifier needs to see.
69+ ///
70+ /// [BOLT 12]: https://github.com/lightning/bolts/blob/master/12-offer-encoding.md
71+ #[ derive( Clone , Debug , PartialEq , Eq , Default ) ]
72+ #[ cfg_attr( feature = "uniffi" , derive( uniffi:: Record ) ) ]
73+ pub struct PayerProofOptions {
74+ /// An optional note to attach to the payer proof itself.
75+ pub note : Option < String > ,
76+ /// Whether to disclose the offer description.
77+ pub include_offer_description : bool ,
78+ /// Whether to disclose the offer issuer.
79+ pub include_offer_issuer : bool ,
80+ /// Whether to disclose the invoice amount.
81+ pub include_invoice_amount : bool ,
82+ /// Whether to disclose the invoice creation timestamp.
83+ pub include_invoice_created_at : bool ,
84+ }
85+
5586/// A payment handler allowing to create and pay [BOLT 12] offers and refunds.
5687///
5788/// Should be retrieved by calling [`Node::bolt12_payment`].
@@ -389,6 +420,74 @@ impl Bolt12Payment {
389420 Ok ( payment_id)
390421 }
391422
423+ /// Creates a [BOLT 12] payer proof for a payment this node made.
424+ ///
425+ /// A payer proof lets the payer demonstrate to a third party that they paid a particular
426+ /// [BOLT 12] invoice, disclosing only the invoice fields they choose to reveal via
427+ /// [`PayerProofOptions`].
428+ ///
429+ /// All inputs are taken straight from [`Event::PaymentSuccessful`]: pass its `payment_id` and
430+ /// `payment_preimage`, plus the [`Bolt12Invoice`] out of its `bolt12_invoice` field. Nothing
431+ /// is read from or written to the payment store, so it's up to you to hold on to the invoice
432+ /// if you want to build a proof later on.
433+ ///
434+ /// Note that payments settled via a static invoice, i.e., async payments, can't be proven this
435+ /// way, which is why this takes a [`Bolt12Invoice`] rather than the event's
436+ /// [`PaidBolt12Invoice`]: those payments simply won't yield one.
437+ ///
438+ /// [BOLT 12]: https://github.com/lightning/bolts/blob/master/12-offer-encoding.md
439+ /// [`Event::PaymentSuccessful`]: crate::Event::PaymentSuccessful
440+ /// [`Bolt12Invoice`]: lightning::offers::invoice::Bolt12Invoice
441+ /// [`PaidBolt12Invoice`]: lightning::offers::payer_proof::PaidBolt12Invoice
442+ pub fn create_payer_proof (
443+ & self , payment_id : PaymentId , payment_preimage : PaymentPreimage , invoice : & Bolt12Invoice ,
444+ options : Option < PayerProofOptions > ,
445+ ) -> Result < PayerProof , Error > {
446+ let invoice = maybe_deref ( invoice) ;
447+ let paid_invoice = LdkPaidBolt12Invoice :: Bolt12Invoice ( invoice. clone ( ) ) ;
448+
449+ let options = options. unwrap_or_default ( ) ;
450+ let expanded_key = self . keys_manager . get_expanded_key ( ) ;
451+ let secp_ctx = bitcoin:: secp256k1:: Secp256k1 :: new ( ) ;
452+
453+ let mut builder = paid_invoice
454+ . prove_payer_derived ( payment_preimage, & expanded_key, payment_id, & secp_ctx)
455+ . map_err ( |e| {
456+ log_error ! (
457+ self . logger,
458+ "Failed to initialize payer proof builder for {}: {:?}" ,
459+ payment_id,
460+ e
461+ ) ;
462+ Error :: PayerProofCreationFailed
463+ } ) ?;
464+
465+ if options. include_offer_description {
466+ builder = builder. include_offer_description ( ) ;
467+ }
468+ if options. include_offer_issuer {
469+ builder = builder. include_offer_issuer ( ) ;
470+ }
471+ if options. include_invoice_amount {
472+ builder = builder. include_invoice_amount ( ) ;
473+ }
474+ if options. include_invoice_created_at {
475+ builder = builder. include_invoice_created_at ( ) ;
476+ }
477+ if let Some ( note) = options. note {
478+ builder = builder. with_proof_note ( note) ;
479+ }
480+
481+ let proof = builder. build_and_sign ( ) . map_err ( |e| {
482+ log_error ! ( self . logger, "Failed to build payer proof for {}: {:?}" , payment_id, e) ;
483+ Error :: PayerProofCreationFailed
484+ } ) ?;
485+
486+ log_info ! ( self . logger, "Created payer proof for payment {}" , payment_id) ;
487+
488+ Ok ( maybe_wrap ( proof) )
489+ }
490+
392491 /// Returns a payable offer that can be used to request and receive a payment of the amount
393492 /// given.
394493 pub fn receive (
0 commit comments