X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning-invoice%2Fsrc%2Fpayment.rs;h=42408540ee41e7a9e1f9d8ba6c711c6def7b565d;hb=8e2b70d91a0f7322042d1b0935527c90c59afe45;hp=00b17db7899d60e235d95057d45b3e97886eadf7;hpb=74328bd6e75a9458990efed58a117d4f061119b6;p=rust-lightning diff --git a/lightning-invoice/src/payment.rs b/lightning-invoice/src/payment.rs index 00b17db7..42408540 100644 --- a/lightning-invoice/src/payment.rs +++ b/lightning-invoice/src/payment.rs @@ -9,13 +9,13 @@ //! Convenient utilities for paying Lightning invoices and sending spontaneous payments. -use crate::Invoice; +use crate::Bolt11Invoice; use bitcoin_hashes::Hash; use lightning::chain; use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator}; -use lightning::chain::keysinterface::{NodeSigner, SignerProvider, EntropySource}; +use lightning::sign::{NodeSigner, SignerProvider, EntropySource}; use lightning::ln::PaymentHash; use lightning::ln::channelmanager::{ChannelManager, PaymentId, Retry, RetryableSendFailure, RecipientOnionFields}; use lightning::routing::router::{PaymentParameters, RouteParameters, Router}; @@ -25,15 +25,15 @@ use core::fmt::Debug; use core::ops::Deref; use core::time::Duration; -/// Pays the given [`Invoice`], retrying if needed based on [`Retry`]. +/// Pays the given [`Bolt11Invoice`], retrying if needed based on [`Retry`]. /// -/// [`Invoice::payment_hash`] is used as the [`PaymentId`], which ensures idempotency as long -/// as the payment is still pending. Once the payment completes or fails, you must ensure that -/// a second payment with the same [`PaymentHash`] is never sent. +/// [`Bolt11Invoice::payment_hash`] is used as the [`PaymentId`], which ensures idempotency as long +/// as the payment is still pending. If the payment succeeds, you must ensure that a second payment +/// with the same [`PaymentHash`] is never sent. /// /// If you wish to use a different payment idempotency token, see [`pay_invoice_with_id`]. pub fn pay_invoice( - invoice: &Invoice, retry_strategy: Retry, + invoice: &Bolt11Invoice, retry_strategy: Retry, channelmanager: &ChannelManager ) -> Result where @@ -51,17 +51,18 @@ where .map(|()| payment_id) } -/// Pays the given [`Invoice`] with a custom idempotency key, retrying if needed based on [`Retry`]. +/// Pays the given [`Bolt11Invoice`] with a custom idempotency key, retrying if needed based on +/// [`Retry`]. /// /// Note that idempotency is only guaranteed as long as the payment is still pending. Once the /// payment completes or fails, no idempotency guarantees are made. /// -/// You should ensure that the [`Invoice::payment_hash`] is unique and the same [`PaymentHash`] -/// has never been paid before. +/// You should ensure that the [`Bolt11Invoice::payment_hash`] is unique and the same +/// [`PaymentHash`] has never been paid before. /// /// See [`pay_invoice`] for a variant which uses the [`PaymentHash`] for the idempotency token. pub fn pay_invoice_with_id( - invoice: &Invoice, payment_id: PaymentId, retry_strategy: Retry, + invoice: &Bolt11Invoice, payment_id: PaymentId, retry_strategy: Retry, channelmanager: &ChannelManager ) -> Result<(), PaymentError> where @@ -78,17 +79,17 @@ where pay_invoice_using_amount(invoice, amt_msat, payment_id, retry_strategy, channelmanager) } -/// Pays the given zero-value [`Invoice`] using the given amount, retrying if needed based on +/// Pays the given zero-value [`Bolt11Invoice`] using the given amount, retrying if needed based on /// [`Retry`]. /// -/// [`Invoice::payment_hash`] is used as the [`PaymentId`], which ensures idempotency as long -/// as the payment is still pending. Once the payment completes or fails, you must ensure that -/// a second payment with the same [`PaymentHash`] is never sent. +/// [`Bolt11Invoice::payment_hash`] is used as the [`PaymentId`], which ensures idempotency as long +/// as the payment is still pending. If the payment succeeds, you must ensure that a second payment +/// with the same [`PaymentHash`] is never sent. /// /// If you wish to use a different payment idempotency token, see /// [`pay_zero_value_invoice_with_id`]. pub fn pay_zero_value_invoice( - invoice: &Invoice, amount_msats: u64, retry_strategy: Retry, + invoice: &Bolt11Invoice, amount_msats: u64, retry_strategy: Retry, channelmanager: &ChannelManager ) -> Result where @@ -107,19 +108,19 @@ where .map(|()| payment_id) } -/// Pays the given zero-value [`Invoice`] using the given amount and custom idempotency key, -/// , retrying if needed based on [`Retry`]. +/// Pays the given zero-value [`Bolt11Invoice`] using the given amount and custom idempotency key, +/// retrying if needed based on [`Retry`]. /// /// Note that idempotency is only guaranteed as long as the payment is still pending. Once the /// payment completes or fails, no idempotency guarantees are made. /// -/// You should ensure that the [`Invoice::payment_hash`] is unique and the same [`PaymentHash`] -/// has never been paid before. +/// You should ensure that the [`Bolt11Invoice::payment_hash`] is unique and the same +/// [`PaymentHash`] has never been paid before. /// /// See [`pay_zero_value_invoice`] for a variant which uses the [`PaymentHash`] for the /// idempotency token. pub fn pay_zero_value_invoice_with_id( - invoice: &Invoice, amount_msats: u64, payment_id: PaymentId, retry_strategy: Retry, + invoice: &Bolt11Invoice, amount_msats: u64, payment_id: PaymentId, retry_strategy: Retry, channelmanager: &ChannelManager ) -> Result<(), PaymentError> where @@ -141,18 +142,20 @@ where } fn pay_invoice_using_amount( - invoice: &Invoice, amount_msats: u64, payment_id: PaymentId, retry_strategy: Retry, + invoice: &Bolt11Invoice, amount_msats: u64, payment_id: PaymentId, retry_strategy: Retry, payer: P ) -> Result<(), PaymentError> where P::Target: Payer { let payment_hash = PaymentHash((*invoice.payment_hash()).into_inner()); - let payment_secret = Some(*invoice.payment_secret()); - let recipient_onion = RecipientOnionFields { payment_secret }; + let recipient_onion = RecipientOnionFields { + payment_secret: Some(*invoice.payment_secret()), + payment_metadata: invoice.payment_metadata().map(|v| v.clone()), + }; let mut payment_params = PaymentParameters::from_node_id(invoice.recover_payee_pub_key(), invoice.min_final_cltv_expiry_delta() as u32) .with_expiry_time(expiry_time_from_unix_epoch(invoice).as_secs()) - .with_route_hints(invoice.route_hints()); + .with_route_hints(invoice.route_hints()).unwrap(); if let Some(features) = invoice.features() { - payment_params = payment_params.with_features(features.clone()); + payment_params = payment_params.with_bolt11_features(features.clone()).unwrap(); } let route_params = RouteParameters { payment_params, @@ -162,20 +165,20 @@ fn pay_invoice_using_amount( payer.send_payment(payment_hash, recipient_onion, payment_id, route_params, retry_strategy) } -fn expiry_time_from_unix_epoch(invoice: &Invoice) -> Duration { +fn expiry_time_from_unix_epoch(invoice: &Bolt11Invoice) -> Duration { invoice.signed_invoice.raw_invoice.data.timestamp.0 + invoice.expiry_time() } /// An error that may occur when making a payment. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum PaymentError { - /// An error resulting from the provided [`Invoice`] or payment hash. + /// An error resulting from the provided [`Bolt11Invoice`] or payment hash. Invoice(&'static str), /// An error occurring when sending a payment. Sending(RetryableSendFailure), } -/// A trait defining behavior of an [`Invoice`] payer. +/// A trait defining behavior of a [`Bolt11Invoice`] payer. /// /// Useful for unit testing internal methods. trait Payer { @@ -213,6 +216,8 @@ mod tests { use super::*; use crate::{InvoiceBuilder, Currency}; use bitcoin_hashes::sha256::Hash as Sha256; + use lightning::events::Event; + use lightning::ln::msgs::ChannelMessageHandler; use lightning::ln::{PaymentPreimage, PaymentSecret}; use lightning::ln::functional_test_utils::*; use secp256k1::{SecretKey, Secp256k1}; @@ -279,7 +284,7 @@ mod tests { duration_since_epoch } - fn invoice(payment_preimage: PaymentPreimage) -> Invoice { + fn invoice(payment_preimage: PaymentPreimage) -> Bolt11Invoice { let payment_hash = Sha256::hash(&payment_preimage.0); let private_key = SecretKey::from_slice(&[42; 32]).unwrap(); @@ -296,7 +301,7 @@ mod tests { .unwrap() } - fn zero_value_invoice(payment_preimage: PaymentPreimage) -> Invoice { + fn zero_value_invoice(payment_preimage: PaymentPreimage) -> Bolt11Invoice { let payment_hash = Sha256::hash(&payment_preimage.0); let private_key = SecretKey::from_slice(&[42; 32]).unwrap(); @@ -350,4 +355,52 @@ mod tests { _ => panic!() } } + + #[test] + #[cfg(feature = "std")] + fn payment_metadata_end_to_end() { + // Test that a payment metadata read from an invoice passed to `pay_invoice` makes it all + // the way out through the `PaymentClaimable` event. + let chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); + create_announced_chan_between_nodes(&nodes, 0, 1); + + let payment_metadata = vec![42, 43, 44, 45, 46, 47, 48, 49, 42]; + + let (payment_hash, payment_secret) = + nodes[1].node.create_inbound_payment(None, 7200, None).unwrap(); + + let invoice = InvoiceBuilder::new(Currency::Bitcoin) + .description("test".into()) + .payment_hash(Sha256::from_slice(&payment_hash.0).unwrap()) + .payment_secret(payment_secret) + .current_timestamp() + .min_final_cltv_expiry_delta(144) + .amount_milli_satoshis(50_000) + .payment_metadata(payment_metadata.clone()) + .build_signed(|hash| { + Secp256k1::new().sign_ecdsa_recoverable(hash, + &nodes[1].keys_manager.backing.get_node_secret_key()) + }) + .unwrap(); + + pay_invoice(&invoice, Retry::Attempts(0), nodes[0].node).unwrap(); + check_added_monitors(&nodes[0], 1); + let send_event = SendEvent::from_node(&nodes[0]); + nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &send_event.msgs[0]); + commitment_signed_dance!(nodes[1], nodes[0], &send_event.commitment_msg, false); + + expect_pending_htlcs_forwardable!(nodes[1]); + + let mut events = nodes[1].node.get_and_clear_pending_events(); + assert_eq!(events.len(), 1); + match events.pop().unwrap() { + Event::PaymentClaimable { onion_fields, .. } => { + assert_eq!(Some(payment_metadata), onion_fields.unwrap().payment_metadata); + }, + _ => panic!("Unexpected event") + } + } }