X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fchannelmanager.rs;h=7861078ff9522797cc54986174651aed3415c0fe;hb=b191fd48d502a3476ac0063c3d694092505198f4;hp=9af65d96c3b088e931ecd029fcff6961fa27b5b1;hpb=d7a6d0d10db17f8c737bf3ad40e7b9f4343e60d8;p=rust-lightning diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 9af65d96..7861078f 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -31,6 +31,7 @@ use bitcoin::secp256k1::Secp256k1; use bitcoin::{LockTime, secp256k1, Sequence}; use crate::blinded_path::BlindedPath; +use crate::blinded_path::payment::{PaymentConstraints, ReceiveTlvs}; use crate::chain; use crate::chain::{Confirm, ChannelMonitorUpdateStatus, Watch, BestBlock}; use crate::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator, LowerBoundedFeeEstimator}; @@ -42,7 +43,7 @@ use crate::events::{Event, EventHandler, EventsProvider, MessageSendEvent, Messa // construct one themselves. use crate::ln::{inbound_payment, ChannelId, PaymentHash, PaymentPreimage, PaymentSecret}; use crate::ln::channel::{Channel, ChannelPhase, ChannelContext, ChannelError, ChannelUpdateStatus, ShutdownResult, UnfundedChannelContext, UpdateFulfillCommitFetch, OutboundV1Channel, InboundV1Channel}; -use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures}; +use crate::ln::features::{Bolt12InvoiceFeatures, ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures}; #[cfg(any(feature = "_test_utils", test))] use crate::ln::features::Bolt11InvoiceFeatures; use crate::routing::gossip::NetworkGraph; @@ -54,11 +55,13 @@ use crate::ln::onion_utils::HTLCFailReason; use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError}; #[cfg(test)] use crate::ln::outbound_payment; -use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs}; +use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs, StaleExpiration}; use crate::ln::wire::Encode; -use crate::offers::offer::{DerivedMetadata, OfferBuilder}; +use crate::offers::invoice::{BlindedPayInfo, DEFAULT_RELATIVE_EXPIRY}; +use crate::offers::offer::{DerivedMetadata, Offer, OfferBuilder}; use crate::offers::parse::Bolt12SemanticError; -use crate::offers::refund::RefundBuilder; +use crate::offers::refund::{Refund, RefundBuilder}; +use crate::onion_message::{Destination, OffersMessage, PendingOnionMessage}; use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider, WriteableEcdsaChannelSigner}; use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate}; use crate::util::wakers::{Future, Notifier}; @@ -1008,6 +1011,8 @@ where // // Lock order tree: // +// `pending_offers_messages` +// // `total_consistency_lock` // | // |__`forward_htlcs` @@ -1015,26 +1020,26 @@ where // | |__`pending_intercepted_htlcs` // | // |__`per_peer_state` -// | | -// | |__`pending_inbound_payments` -// | | -// | |__`claimable_payments` -// | | -// | |__`pending_outbound_payments` // This field's struct contains a map of pending outbounds -// | | -// | |__`peer_state` -// | | -// | |__`id_to_peer` -// | | -// | |__`short_to_chan_info` -// | | -// | |__`outbound_scid_aliases` -// | | -// | |__`best_block` -// | | -// | |__`pending_events` -// | | -// | |__`pending_background_events` +// | +// |__`pending_inbound_payments` +// | +// |__`claimable_payments` +// | +// |__`pending_outbound_payments` // This field's struct contains a map of pending outbounds +// | +// |__`peer_state` +// | +// |__`id_to_peer` +// | +// |__`short_to_chan_info` +// | +// |__`outbound_scid_aliases` +// | +// |__`best_block` +// | +// |__`pending_events` +// | +// |__`pending_background_events` // pub struct ChannelManager where @@ -1246,6 +1251,8 @@ where event_persist_notifier: Notifier, needs_persist_flag: AtomicBool, + pending_offers_messages: Mutex>>, + entropy_source: ES, node_signer: NS, signer_provider: SP, @@ -2326,6 +2333,8 @@ where needs_persist_flag: AtomicBool::new(false), funding_batch_states: Mutex::new(BTreeMap::new()), + pending_offers_messages: Mutex::new(Vec::new()), + entropy_source, node_signer, signer_provider, @@ -4822,8 +4831,8 @@ where /// * Force-closing and removing channels which have not completed establishment in a timely manner. /// * Forgetting about stale outbound payments, either those that have already been fulfilled /// or those awaiting an invoice that hasn't been delivered in the necessary amount of time. - /// The latter is determined using the system clock in `std` and the block time minus two - /// hours in `no-std`. + /// The latter is determined using the system clock in `std` and the highest seen block time + /// minus two hours in `no-std`. /// /// Note that this may cause reentrancy through [`chain::Watch::update_channel`] calls or feerate /// estimate fetches. @@ -7308,11 +7317,15 @@ where } /// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the - /// [`ChannelManager`] when handling [`Bolt12Invoice`] messages for the refund. The builder will - /// have the provided expiration set. Any changes to the expiration on the returned builder will - /// not be honored by [`ChannelManager`]. + /// [`ChannelManager`] when handling [`Bolt12Invoice`] messages for the refund. + /// + /// The builder will have the provided expiration set. Any changes to the expiration on the + /// returned builder will not be honored by [`ChannelManager`]. For `no-std`, the highest seen + /// block time minus two hours is used for the current time when determining if the refund has + /// expired. /// - /// The provided `payment_id` is used to ensure that only one invoice is paid for the refund. + /// The provided `payment_id` is used to ensure that only one invoice is paid for the refund. To + /// revoke the refund, use [`ChannelManager::abandon_payment`] prior to receiving the invoice. /// /// Uses a one-hop [`BlindedPath`] for the refund with [`ChannelManager::get_our_node_id`] as /// the introduction node and a derived payer id for sender privacy. As such, currently, the @@ -7338,15 +7351,163 @@ where .absolute_expiry(absolute_expiry) .path(path); + let expiration = StaleExpiration::AbsoluteTimeout(absolute_expiry); self.pending_outbound_payments .add_new_awaiting_invoice( - payment_id, absolute_expiry, retry_strategy, max_total_routing_fee_msat, + payment_id, expiration, retry_strategy, max_total_routing_fee_msat, ) .map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?; Ok(builder) } + /// Pays for an [`Offer`] using the given parameters by creating an [`InvoiceRequest`] and + /// enqueuing it to be sent via an onion message. [`ChannelManager`] will pay the actual + /// [`Bolt12Invoice`] once it is received. + /// + /// Uses [`InvoiceRequestBuilder`] such that the [`InvoiceRequest`] it builds is recognized by + /// the [`ChannelManager`] when handling a [`Bolt12Invoice`] message in response to the request. + /// The optional parameters are used in the builder, if `Some`: + /// - `quantity` for [`InvoiceRequest::quantity`] which must be set if + /// [`Offer::expects_quantity`] is `true`. + /// - `amount_msats` if overpaying what is required for the given `quantity` is desired, and + /// - `payer_note` for [`InvoiceRequest::payer_note`]. + /// + /// The provided `payment_id` is used to ensure that only one invoice is paid for the request + /// when received. See [Avoiding Duplicate Payments] for other requirements once the payment has + /// been sent. To revoke the request, use [`ChannelManager::abandon_payment`] prior to receiving + /// the invoice. + /// + /// Errors if a duplicate `payment_id` is provided given the caveats in the aforementioned link. + /// + /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest + /// [`InvoiceRequest::quantity`]: crate::offers::invoice_request::InvoiceRequest::quantity + /// [`InvoiceRequest::payer_note`]: crate::offers::invoice_request::InvoiceRequest::payer_note + /// [`InvoiceRequestBuilder`]: crate::offers::invoice_request::InvoiceRequestBuilder + /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice + /// [Avoiding Duplicate Payments]: #avoiding-duplicate-payments + pub fn pay_for_offer( + &self, offer: &Offer, quantity: Option, amount_msats: Option, + payer_note: Option, payment_id: PaymentId, retry_strategy: Retry, + max_total_routing_fee_msat: Option + ) -> Result<(), Bolt12SemanticError> { + let expanded_key = &self.inbound_payment_key; + let entropy = &*self.entropy_source; + let secp_ctx = &self.secp_ctx; + + let builder = offer + .request_invoice_deriving_payer_id(expanded_key, entropy, secp_ctx, payment_id)? + .chain_hash(self.chain_hash)?; + let builder = match quantity { + None => builder, + Some(quantity) => builder.quantity(quantity)?, + }; + let builder = match amount_msats { + None => builder, + Some(amount_msats) => builder.amount_msats(amount_msats)?, + }; + let builder = match payer_note { + None => builder, + Some(payer_note) => builder.payer_note(payer_note), + }; + + let invoice_request = builder.build_and_sign()?; + let reply_path = self.create_one_hop_blinded_path(); + + let expiration = StaleExpiration::TimerTicks(1); + self.pending_outbound_payments + .add_new_awaiting_invoice( + payment_id, expiration, retry_strategy, max_total_routing_fee_msat + ) + .map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?; + + let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap(); + if offer.paths().is_empty() { + let message = PendingOnionMessage { + contents: OffersMessage::InvoiceRequest(invoice_request), + destination: Destination::Node(offer.signing_pubkey()), + reply_path: Some(reply_path), + }; + pending_offers_messages.push(message); + } else { + // Send as many invoice requests as there are paths in the offer (with an upper bound). + // Using only one path could result in a failure if the path no longer exists. But only + // one invoice for a given payment id will be paid, even if more than one is received. + const REQUEST_LIMIT: usize = 10; + for path in offer.paths().into_iter().take(REQUEST_LIMIT) { + let message = PendingOnionMessage { + contents: OffersMessage::InvoiceRequest(invoice_request.clone()), + destination: Destination::BlindedPath(path.clone()), + reply_path: Some(reply_path.clone()), + }; + pending_offers_messages.push(message); + } + } + + Ok(()) + } + + /// Creates a [`Bolt12Invoice`] for a [`Refund`] and enqueues it to be sent via an onion + /// message. + /// + /// The resulting invoice uses a [`PaymentHash`] recognized by the [`ChannelManager`] and a + /// [`BlindedPath`] containing the [`PaymentSecret`] needed to reconstruct the corresponding + /// [`PaymentPreimage`]. + /// + /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice + pub fn request_refund_payment(&self, refund: &Refund) -> Result<(), Bolt12SemanticError> { + let expanded_key = &self.inbound_payment_key; + let entropy = &*self.entropy_source; + let secp_ctx = &self.secp_ctx; + + let amount_msats = refund.amount_msats(); + let relative_expiry = DEFAULT_RELATIVE_EXPIRY.as_secs() as u32; + + match self.create_inbound_payment(Some(amount_msats), relative_expiry, None) { + Ok((payment_hash, payment_secret)) => { + let payment_paths = vec![ + self.create_one_hop_blinded_payment_path(payment_secret), + ]; + #[cfg(not(feature = "no-std"))] + let builder = refund.respond_using_derived_keys( + payment_paths, payment_hash, expanded_key, entropy + )?; + #[cfg(feature = "no-std")] + let created_at = Duration::from_secs( + self.highest_seen_timestamp.load(Ordering::Acquire) as u64 + ); + #[cfg(feature = "no-std")] + let builder = refund.respond_using_derived_keys_no_std( + payment_paths, payment_hash, created_at, expanded_key, entropy + )?; + let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?; + let reply_path = self.create_one_hop_blinded_path(); + + let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap(); + if refund.paths().is_empty() { + let message = PendingOnionMessage { + contents: OffersMessage::Invoice(invoice), + destination: Destination::Node(refund.payer_id()), + reply_path: Some(reply_path), + }; + pending_offers_messages.push(message); + } else { + for path in refund.paths() { + let message = PendingOnionMessage { + contents: OffersMessage::Invoice(invoice.clone()), + destination: Destination::BlindedPath(path.clone()), + reply_path: Some(reply_path.clone()), + }; + pending_offers_messages.push(message); + } + } + + Ok(()) + }, + Err(()) => Err(Bolt12SemanticError::InvalidAmount), + } + } + /// Gets a payment secret and payment hash for use in an invoice given to a third party wishing /// to pay us. /// @@ -7455,6 +7616,29 @@ where BlindedPath::one_hop_for_message(self.get_our_node_id(), entropy_source, secp_ctx).unwrap() } + /// Creates a one-hop blinded path with [`ChannelManager::get_our_node_id`] as the introduction + /// node. + fn create_one_hop_blinded_payment_path( + &self, payment_secret: PaymentSecret + ) -> (BlindedPayInfo, BlindedPath) { + let entropy_source = self.entropy_source.deref(); + let secp_ctx = &self.secp_ctx; + + let payee_node_id = self.get_our_node_id(); + let max_cltv_expiry = self.best_block.read().unwrap().height() + LATENCY_GRACE_PERIOD_BLOCKS; + let payee_tlvs = ReceiveTlvs { + payment_secret, + payment_constraints: PaymentConstraints { + max_cltv_expiry, + htlc_minimum_msat: 1, + }, + }; + // TODO: Err for overflow? + BlindedPath::one_hop_for_payment( + payee_node_id, payee_tlvs, entropy_source, secp_ctx + ).unwrap() + } + /// Gets a fake short channel id for use in receiving [phantom node payments]. These fake scids /// are used when constructing the phantom invoice's route hints. /// @@ -8060,35 +8244,41 @@ where self.best_block.read().unwrap().clone() } - /// Fetches the set of [`NodeFeatures`] flags which are provided by or required by + /// Fetches the set of [`NodeFeatures`] flags that are provided by or required by /// [`ChannelManager`]. pub fn node_features(&self) -> NodeFeatures { provided_node_features(&self.default_configuration) } - /// Fetches the set of [`Bolt11InvoiceFeatures`] flags which are provided by or required by + /// Fetches the set of [`Bolt11InvoiceFeatures`] flags that are provided by or required by /// [`ChannelManager`]. /// /// Note that the invoice feature flags can vary depending on if the invoice is a "phantom invoice" /// or not. Thus, this method is not public. #[cfg(any(feature = "_test_utils", test))] - pub fn invoice_features(&self) -> Bolt11InvoiceFeatures { - provided_invoice_features(&self.default_configuration) + pub fn bolt11_invoice_features(&self) -> Bolt11InvoiceFeatures { + provided_bolt11_invoice_features(&self.default_configuration) } - /// Fetches the set of [`ChannelFeatures`] flags which are provided by or required by + /// Fetches the set of [`Bolt12InvoiceFeatures`] flags that are provided by or required by + /// [`ChannelManager`]. + fn bolt12_invoice_features(&self) -> Bolt12InvoiceFeatures { + provided_bolt12_invoice_features(&self.default_configuration) + } + + /// Fetches the set of [`ChannelFeatures`] flags that are provided by or required by /// [`ChannelManager`]. pub fn channel_features(&self) -> ChannelFeatures { provided_channel_features(&self.default_configuration) } - /// Fetches the set of [`ChannelTypeFeatures`] flags which are provided by or required by + /// Fetches the set of [`ChannelTypeFeatures`] flags that are provided by or required by /// [`ChannelManager`]. pub fn channel_type_features(&self) -> ChannelTypeFeatures { provided_channel_type_features(&self.default_configuration) } - /// Fetches the set of [`InitFeatures`] flags which are provided by or required by + /// Fetches the set of [`InitFeatures`] flags that are provided by or required by /// [`ChannelManager`]. pub fn init_features(&self) -> InitFeatures { provided_init_features(&self.default_configuration) @@ -8619,7 +8809,7 @@ where } } -/// Fetches the set of [`NodeFeatures`] flags which are provided by or required by +/// Fetches the set of [`NodeFeatures`] flags that are provided by or required by /// [`ChannelManager`]. pub(crate) fn provided_node_features(config: &UserConfig) -> NodeFeatures { let mut node_features = provided_init_features(config).to_context(); @@ -8627,29 +8817,35 @@ pub(crate) fn provided_node_features(config: &UserConfig) -> NodeFeatures { node_features } -/// Fetches the set of [`Bolt11InvoiceFeatures`] flags which are provided by or required by +/// Fetches the set of [`Bolt11InvoiceFeatures`] flags that are provided by or required by /// [`ChannelManager`]. /// /// Note that the invoice feature flags can vary depending on if the invoice is a "phantom invoice" /// or not. Thus, this method is not public. #[cfg(any(feature = "_test_utils", test))] -pub(crate) fn provided_invoice_features(config: &UserConfig) -> Bolt11InvoiceFeatures { +pub(crate) fn provided_bolt11_invoice_features(config: &UserConfig) -> Bolt11InvoiceFeatures { + provided_init_features(config).to_context() +} + +/// Fetches the set of [`Bolt12InvoiceFeatures`] flags that are provided by or required by +/// [`ChannelManager`]. +pub(crate) fn provided_bolt12_invoice_features(config: &UserConfig) -> Bolt12InvoiceFeatures { provided_init_features(config).to_context() } -/// Fetches the set of [`ChannelFeatures`] flags which are provided by or required by +/// Fetches the set of [`ChannelFeatures`] flags that are provided by or required by /// [`ChannelManager`]. pub(crate) fn provided_channel_features(config: &UserConfig) -> ChannelFeatures { provided_init_features(config).to_context() } -/// Fetches the set of [`ChannelTypeFeatures`] flags which are provided by or required by +/// Fetches the set of [`ChannelTypeFeatures`] flags that are provided by or required by /// [`ChannelManager`]. pub(crate) fn provided_channel_type_features(config: &UserConfig) -> ChannelTypeFeatures { ChannelTypeFeatures::from_init(&provided_init_features(config)) } -/// Fetches the set of [`InitFeatures`] flags which are provided by or required by +/// Fetches the set of [`InitFeatures`] flags that are provided by or required by /// [`ChannelManager`]. pub fn provided_init_features(config: &UserConfig) -> InitFeatures { // Note that if new features are added here which other peers may (eventually) require, we @@ -10325,6 +10521,8 @@ where funding_batch_states: Mutex::new(BTreeMap::new()), + pending_offers_messages: Mutex::new(Vec::new()), + entropy_source: args.entropy_source, node_signer: args.node_signer, signer_provider: args.signer_provider, @@ -11802,7 +12000,7 @@ pub mod bench { macro_rules! send_payment { ($node_a: expr, $node_b: expr) => { let payment_params = PaymentParameters::from_node_id($node_b.get_our_node_id(), TEST_FINAL_CLTV) - .with_bolt11_features($node_b.invoice_features()).unwrap(); + .with_bolt11_features($node_b.bolt11_invoice_features()).unwrap(); let mut payment_preimage = PaymentPreimage([0; 32]); payment_preimage.0[0..8].copy_from_slice(&payment_count.to_le_bytes()); payment_count += 1;