}
/// Create a one-hop blinded path for a payment.
- pub fn one_hop_for_payment<ES: EntropySource, T: secp256k1::Signing + secp256k1::Verification>(
+ pub fn one_hop_for_payment<ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification>(
payee_node_id: PublicKey, payee_tlvs: payment::ReceiveTlvs, entropy_source: &ES,
secp_ctx: &Secp256k1<T>
) -> Result<(BlindedPayInfo, Self), ()> {
///
/// [`ForwardTlvs`]: crate::blinded_path::payment::ForwardTlvs
// TODO: make all payloads the same size with padding + add dummy hops
- pub(crate) fn new_for_payment<ES: EntropySource, T: secp256k1::Signing + secp256k1::Verification>(
+ pub(crate) fn new_for_payment<ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification>(
intermediate_nodes: &[payment::ForwardNode], payee_node_id: PublicKey,
payee_tlvs: payment::ReceiveTlvs, htlc_maximum_msat: u64, entropy_source: &ES,
secp_ctx: &Secp256k1<T>
/// [`BlindedHop`]: crate::blinded_path::BlindedHop
#[derive(Clone, Debug)]
pub struct PaymentConstraints {
- /// The maximum total CLTV delta that is acceptable when relaying a payment over this
- /// [`BlindedHop`].
+ /// The maximum total CLTV that is acceptable when relaying a payment over this [`BlindedHop`].
pub max_cltv_expiry: u32,
/// The minimum value, in msat, that may be accepted by the node corresponding to this
/// [`BlindedHop`].
sender_intended_total_msat: Option<u64>,
},
/// Indicates a request for an invoice failed to yield a response in a reasonable amount of time
- /// or was explicitly abandoned by [`ChannelManager::abandon_payment`].
+ /// or was explicitly abandoned by [`ChannelManager::abandon_payment`]. This may be for an
+ /// [`InvoiceRequest`] sent for an [`Offer`] or for a [`Refund`] that hasn't been redeemed.
///
/// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment
- #[cfg(invreqfailed)]
+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
+ /// [`Offer`]: crate::offers::offer::Offer
+ /// [`Refund`]: crate::offers::refund::Refund
InvoiceRequestFailed {
/// The `payment_id` to have been associated with payment for the requested invoice.
payment_id: PaymentId,
(8, funding_txo, required),
});
},
- #[cfg(invreqfailed)]
&Event::InvoiceRequestFailed { ref payment_id } => {
33u8.write(writer)?;
write_tlv_fields!(writer, {
};
f()
},
- #[cfg(invreqfailed)]
33u8 => {
let f = || {
_init_and_read_len_prefixed_tlv_fields!(reader, {
use crate::events::MessageSendEventsProvider;
use crate::ln::channelmanager;
use crate::ln::channelmanager::{PaymentId, RecipientOnionFields};
-use crate::ln::features::Bolt12InvoiceFeatures;
use crate::ln::functional_test_utils::*;
use crate::ln::outbound_payment::Retry;
use crate::prelude::*;
nodes[3].node.get_our_node_id(), payee_tlvs, &chanmon_cfgs[3].keys_manager, &secp_ctx
).unwrap();
- let bolt12_features: Bolt12InvoiceFeatures =
- channelmanager::provided_invoice_features(&UserConfig::default()).to_context();
+ let bolt12_features =
+ channelmanager::provided_bolt12_invoice_features(&UserConfig::default());
let route_params = RouteParameters::from_payment_params_and_value(
- PaymentParameters::blinded(vec![blinded_path])
- .with_bolt12_features(bolt12_features).unwrap(),
+ PaymentParameters::blinded(vec![blinded_path]).with_bolt12_features(bolt12_features).unwrap(),
amt_msat,
);
nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap();
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};
// 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;
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::{Bolt12PaymentError, OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs, StaleExpiration};
use crate::ln::wire::Encode;
-use crate::offers::offer::{DerivedMetadata, OfferBuilder};
+use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, InvoiceBuilder};
+use crate::offers::invoice_error::InvoiceError;
+use crate::offers::merkle::SignError;
+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, OffersMessageHandler, PendingOnionMessage};
use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider, WriteableEcdsaChannelSigner};
use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate};
use crate::util::wakers::{Future, Notifier};
//
// Lock order tree:
//
+// `pending_offers_messages`
+//
// `total_consistency_lock`
// |
// |__`forward_htlcs`
// | |__`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<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref>
where
event_persist_notifier: Notifier,
needs_persist_flag: AtomicBool,
+ pending_offers_messages: Mutex<Vec<PendingOnionMessage<OffersMessage>>>,
+
entropy_source: ES,
node_signer: NS,
signer_provider: SP,
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,
self.pending_outbound_payments.test_set_payment_metadata(payment_id, new_payment_metadata);
}
+ pub(super) fn send_payment_for_bolt12_invoice(&self, invoice: &Bolt12Invoice, payment_id: PaymentId) -> Result<(), Bolt12PaymentError> {
+ let best_block_height = self.best_block.read().unwrap().height();
+ let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
+ self.pending_outbound_payments
+ .send_payment_for_bolt12_invoice(
+ invoice, payment_id, &self.router, self.list_usable_channels(),
+ || self.compute_inflight_htlcs(), &self.entropy_source, &self.node_signer,
+ best_block_height, &self.logger, &self.pending_events,
+ |args| self.send_payment_along_path(args)
+ )
+ }
/// Signals that no further attempts for the given payment should occur. Useful if you have a
/// pending outbound payment with retries remaining, but wish to stop retrying the payment before
/// wait until you receive either a [`Event::PaymentFailed`] or [`Event::PaymentSent`] event to
/// determine the ultimate status of a payment.
///
+ /// # Requested Invoices
+ ///
+ /// In the case of paying a [`Bolt12Invoice`] via [`ChannelManager::pay_for_offer`], abandoning
+ /// the payment prior to receiving the invoice will result in an [`Event::InvoiceRequestFailed`]
+ /// and prevent any attempts at paying it once received. The other events may only be generated
+ /// once the invoice has been received.
+ ///
/// # Restart Behavior
///
/// If an [`Event::PaymentFailed`] is generated and we restart without first persisting the
- /// [`ChannelManager`], another [`Event::PaymentFailed`] may be generated.
+ /// [`ChannelManager`], another [`Event::PaymentFailed`] may be generated; likewise for
+ /// [`Event::InvoiceRequestFailed`].
+ ///
+ /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
pub fn abandon_payment(&self, payment_id: PaymentId) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
self.pending_outbound_payments.abandon_payment(payment_id, PaymentFailureReason::UserAbandoned, &self.pending_events);
/// * 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.
/// [`ChannelManager`] when handling [`InvoiceRequest`] messages for the offer. The offer will
/// not have an expiration unless otherwise set on the builder.
///
+ /// # Privacy
+ ///
/// Uses a one-hop [`BlindedPath`] for the offer with [`ChannelManager::get_our_node_id`] as the
/// introduction node and a derived signing pubkey for recipient privacy. As such, currently,
/// the node must be announced. Otherwise, there is no way to find a path to the introduction
/// node in order to send the [`InvoiceRequest`].
///
+ /// # Limitations
+ ///
+ /// Requires a direct connection to the introduction node in the responding [`InvoiceRequest`]'s
+ /// reply path.
+ ///
/// [`Offer`]: crate::offers::offer::Offer
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
pub fn create_offer_builder(
}
/// 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.
+ ///
+ /// # Payment
///
/// The provided `payment_id` is used to ensure that only one invoice is paid for the refund.
+ /// See [Avoiding Duplicate Payments] for other requirements once the payment has been sent.
+ ///
+ /// 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.
+ ///
+ /// To revoke the refund, use [`ChannelManager::abandon_payment`] prior to receiving the
+ /// invoice. If abandoned, or an invoice isn't received before expiration, the payment will fail
+ /// with an [`Event::InvoiceRequestFailed`].
+ ///
+ /// # Privacy
///
/// 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
+ /// the introduction node and a derived payer id for payer privacy. As such, currently, the
/// node must be announced. Otherwise, there is no way to find a path to the introduction node
/// in order to send the [`Bolt12Invoice`].
///
+ /// # Limitations
+ ///
+ /// Requires a direct connection to an introduction node in the responding
+ /// [`Bolt12Invoice::payment_paths`].
+ ///
+ /// # Errors
+ ///
+ /// Errors if a duplicate `payment_id` is provided given the caveats in the aforementioned link
+ /// or if `amount_msats` is invalid.
+ ///
/// [`Refund`]: crate::offers::refund::Refund
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
+ /// [`Bolt12Invoice::payment_paths`]: crate::offers::invoice::Bolt12Invoice::payment_paths
pub fn create_refund_builder(
&self, description: String, amount_msats: u64, absolute_expiry: Duration,
payment_id: PaymentId, retry_strategy: Retry, max_total_routing_fee_msat: Option<u64>
.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`].
+ ///
+ /// # Payment
+ ///
+ /// 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. If abandoned, or an invoice isn't received in a reasonable amount of time, the
+ /// payment will fail with an [`Event::InvoiceRequestFailed`].
+ ///
+ /// # Privacy
+ ///
+ /// Uses a one-hop [`BlindedPath`] for the reply path with [`ChannelManager::get_our_node_id`]
+ /// as the introduction node and a derived payer id for payer privacy. As such, currently, the
+ /// node must be announced. Otherwise, there is no way to find a path to the introduction node
+ /// in order to send the [`Bolt12Invoice`].
+ ///
+ /// # Limitations
+ ///
+ /// Requires a direct connection to an introduction node in [`Offer::paths`] or to
+ /// [`Offer::signing_pubkey`], if empty. A similar restriction applies to the responding
+ /// [`Bolt12Invoice::payment_paths`].
+ ///
+ /// # Errors
+ ///
+ /// Errors if a duplicate `payment_id` is provided given the caveats in the aforementioned link
+ /// or if the provided parameters are invalid for the offer.
+ ///
+ /// [`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
+ /// [`Bolt12Invoice::payment_paths`]: crate::offers::invoice::Bolt12Invoice::payment_paths
+ /// [Avoiding Duplicate Payments]: #avoiding-duplicate-payments
+ pub fn pay_for_offer(
+ &self, offer: &Offer, quantity: Option<u64>, amount_msats: Option<u64>,
+ payer_note: Option<String>, payment_id: PaymentId, retry_strategy: Retry,
+ max_total_routing_fee_msat: Option<u64>
+ ) -> 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`].
+ ///
+ /// # Limitations
+ ///
+ /// Requires a direct connection to an introduction node in [`Refund::paths`] or to
+ /// [`Refund::payer_id`], if empty. This request is best effort; an invoice will be sent to each
+ /// node meeting the aforementioned criteria, but there's no guarantee that they will be
+ /// received and no retries will be made.
+ ///
+ /// [`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.
///
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.
///
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)
}
}
-/// Fetches the set of [`NodeFeatures`] flags which are provided by or required by
+impl<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref>
+OffersMessageHandler for ChannelManager<M, T, ES, NS, SP, F, R, L>
+where
+ M::Target: chain::Watch<<SP::Target as SignerProvider>::Signer>,
+ T::Target: BroadcasterInterface,
+ ES::Target: EntropySource,
+ NS::Target: NodeSigner,
+ SP::Target: SignerProvider,
+ F::Target: FeeEstimator,
+ R::Target: Router,
+ L::Target: Logger,
+{
+ fn handle_message(&self, message: OffersMessage) -> Option<OffersMessage> {
+ let secp_ctx = &self.secp_ctx;
+ let expanded_key = &self.inbound_payment_key;
+
+ match message {
+ OffersMessage::InvoiceRequest(invoice_request) => {
+ let amount_msats = match InvoiceBuilder::<DerivedSigningPubkey>::amount_msats(
+ &invoice_request
+ ) {
+ Ok(amount_msats) => Some(amount_msats),
+ Err(error) => return Some(OffersMessage::InvoiceError(error.into())),
+ };
+ let invoice_request = match invoice_request.verify(expanded_key, secp_ctx) {
+ Ok(invoice_request) => invoice_request,
+ Err(()) => {
+ let error = Bolt12SemanticError::InvalidMetadata;
+ return Some(OffersMessage::InvoiceError(error.into()));
+ },
+ };
+ let relative_expiry = DEFAULT_RELATIVE_EXPIRY.as_secs() as u32;
+
+ match self.create_inbound_payment(amount_msats, relative_expiry, None) {
+ Ok((payment_hash, payment_secret)) if invoice_request.keys.is_some() => {
+ let payment_paths = vec![
+ self.create_one_hop_blinded_payment_path(payment_secret),
+ ];
+ #[cfg(not(feature = "no-std"))]
+ let builder = invoice_request.respond_using_derived_keys(
+ payment_paths, payment_hash
+ );
+ #[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 = invoice_request.respond_using_derived_keys_no_std(
+ payment_paths, payment_hash, created_at
+ );
+ match builder.and_then(|b| b.allow_mpp().build_and_sign(secp_ctx)) {
+ Ok(invoice) => Some(OffersMessage::Invoice(invoice)),
+ Err(error) => Some(OffersMessage::InvoiceError(error.into())),
+ }
+ },
+ 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 = invoice_request.respond_with(payment_paths, payment_hash);
+ #[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 = invoice_request.respond_with_no_std(
+ payment_paths, payment_hash, created_at
+ );
+ let response = builder.and_then(|builder| builder.allow_mpp().build())
+ .map_err(|e| OffersMessage::InvoiceError(e.into()))
+ .and_then(|invoice|
+ match invoice.sign(|invoice| self.node_signer.sign_bolt12_invoice(invoice)) {
+ Ok(invoice) => Ok(OffersMessage::Invoice(invoice)),
+ Err(SignError::Signing(())) => Err(OffersMessage::InvoiceError(
+ InvoiceError::from_str("Failed signing invoice")
+ )),
+ Err(SignError::Verification(_)) => Err(OffersMessage::InvoiceError(
+ InvoiceError::from_str("Failed invoice signature verification")
+ )),
+ });
+ match response {
+ Ok(invoice) => Some(invoice),
+ Err(error) => Some(error),
+ }
+ },
+ Err(()) => {
+ Some(OffersMessage::InvoiceError(Bolt12SemanticError::InvalidAmount.into()))
+ },
+ }
+ },
+ OffersMessage::Invoice(invoice) => {
+ match invoice.verify(expanded_key, secp_ctx) {
+ Err(()) => {
+ Some(OffersMessage::InvoiceError(InvoiceError::from_str("Unrecognized invoice")))
+ },
+ Ok(_) if invoice.invoice_features().requires_unknown_bits_from(&self.bolt12_invoice_features()) => {
+ Some(OffersMessage::InvoiceError(Bolt12SemanticError::UnknownRequiredFeatures.into()))
+ },
+ Ok(payment_id) => {
+ if let Err(e) = self.send_payment_for_bolt12_invoice(&invoice, payment_id) {
+ log_trace!(self.logger, "Failed paying invoice: {:?}", e);
+ Some(OffersMessage::InvoiceError(InvoiceError::from_str(&format!("{:?}", e))))
+ } else {
+ None
+ }
+ },
+ }
+ },
+ OffersMessage::InvoiceError(invoice_error) => {
+ log_trace!(self.logger, "Received invoice_error: {}", invoice_error);
+ None
+ },
+ }
+ }
+
+ fn release_pending_messages(&self) -> Vec<PendingOnionMessage<OffersMessage>> {
+ core::mem::take(&mut self.pending_offers_messages.lock().unwrap())
+ }
+}
+
+/// 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();
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
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,
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;
macro_rules! get_route_and_payment_hash {
($send_node: expr, $recv_node: expr, $recv_value: expr) => {{
let payment_params = $crate::routing::router::PaymentParameters::from_node_id($recv_node.node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features($recv_node.node.invoice_features()).unwrap();
+ .with_bolt11_features($recv_node.node.bolt11_invoice_features()).unwrap();
$crate::get_route_and_payment_hash!($send_node, $recv_node, payment_params, $recv_value)
}};
($send_node: expr, $recv_node: expr, $payment_params: expr, $recv_value: expr) => {{
pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash, PaymentSecret, PaymentId) {
let payment_params = PaymentParameters::from_node_id(expected_route.last().unwrap().node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(expected_route.last().unwrap().node.invoice_features()).unwrap();
+ .with_bolt11_features(expected_route.last().unwrap().node.bolt11_invoice_features()).unwrap();
let route_params = RouteParameters::from_payment_params_and_value(payment_params, recv_value);
let route = get_route(origin_node, &route_params).unwrap();
assert_eq!(route.paths.len(), 1);
pub fn route_over_limit<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) {
let payment_params = PaymentParameters::from_node_id(expected_route.last().unwrap().node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(expected_route.last().unwrap().node.invoice_features()).unwrap();
+ .with_bolt11_features(expected_route.last().unwrap().node.bolt11_invoice_features()).unwrap();
let route_params = RouteParameters::from_payment_params_and_value(payment_params, recv_value);
let network_graph = origin_node.network_graph.read_only();
let scorer = test_utils::TestScorer::new();
// attempt to send amt_msat > their_max_htlc_value_in_flight_msat
{
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[2].node.invoice_features()).unwrap().with_max_channel_saturation_power_of_half(0);
+ .with_bolt11_features(nodes[2].node.bolt11_invoice_features()).unwrap().with_max_channel_saturation_power_of_half(0);
let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, recv_value_0);
route.paths[0].hops.last_mut().unwrap().fee_msat += 1;
assert!(route.paths[0].hops.iter().rev().skip(1).all(|h| h.fee_msat == feemsat));
}
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[2].node.invoice_features()).unwrap().with_max_channel_saturation_power_of_half(0);
+ .with_bolt11_features(nodes[2].node.bolt11_invoice_features()).unwrap().with_max_channel_saturation_power_of_half(0);
let route = get_route!(nodes[0], payment_params, recv_value_0).unwrap();
let (payment_preimage, ..) = send_along_route(&nodes[0], route, &[&nodes[1], &nodes[2]], recv_value_0);
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage);
// script push size limit so that the below script length checks match
// ACCEPTED_HTLC_SCRIPT_WEIGHT.
let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV - 40)
- .with_bolt11_features(nodes[3].node.invoice_features()).unwrap();
+ .with_bolt11_features(nodes[3].node.bolt11_invoice_features()).unwrap();
let (route, _, _, _) = get_route_and_payment_hash!(nodes[0], nodes[3], payment_params, 800_000);
send_along_route_with_secret(&nodes[0], route, &[&[&nodes[1], &nodes[2], &nodes[3]]], 800_000, duplicate_payment_hash, payment_secret);
let _chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1000000, 0);
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), 0)
- .with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
+ .with_bolt11_features(nodes[1].node.bolt11_invoice_features()).unwrap();
let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], payment_params, 100000000);
route.paths[0].hops.last_mut().unwrap().cltv_expiry_delta = 500000001;
unwrap_send_err!(nodes[0].node.send_payment_with_route(&route, our_payment_hash,
let scorer = test_utils::TestScorer::new();
let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
- let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(),
- TEST_FINAL_CLTV).with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
+ .with_bolt11_features(nodes[1].node.bolt11_invoice_features()).unwrap();
let route_params = RouteParameters::from_payment_params_and_value(payment_params, 10_000);
let route = get_route(&nodes[0].node.get_our_node_id(), &route_params, &nodes[0].network_graph.read_only(),
None, nodes[0].logger, &scorer, &Default::default(), &random_seed_bytes).unwrap();
let payment_preimage = route_payment(&nodes[0], &vec!(&nodes[1])[..], 3000000).0;
let payment_params = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id(), 30)
- .with_bolt11_features(nodes[0].node.invoice_features()).unwrap();
+ .with_bolt11_features(nodes[0].node.bolt11_invoice_features()).unwrap();
let (route,_, _, _) = get_route_and_payment_hash!(nodes[1], nodes[0], payment_params, 3000000);
send_along_route(&nodes[1], route, &vec!(&nodes[0])[..], 3000000);
let chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1000000, 59000000);
// Lock HTLC in both directions (using a slightly lower CLTV delay to provide timely RBF bumps)
- let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), 50).with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), 50).with_bolt11_features(nodes[1].node.bolt11_invoice_features()).unwrap();
let scorer = test_utils::TestScorer::new();
let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
let route_params = RouteParameters::from_payment_params_and_value(payment_params, 3_000_000);
let route = get_route(&nodes[0].node.get_our_node_id(), &route_params, &nodes[0].network_graph.read_only(), None,
nodes[0].logger, &scorer, &Default::default(), &random_seed_bytes).unwrap();
let payment_preimage = send_along_route(&nodes[0], route, &[&nodes[1]], 3_000_000).0;
- let payment_params = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id(), 50).with_bolt11_features(nodes[0].node.invoice_features()).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id(), 50)
+ .with_bolt11_features(nodes[0].node.bolt11_invoice_features()).unwrap();
let route_params = RouteParameters::from_payment_params_and_value(payment_params, 3_000_000);
let route = get_route(&nodes[1].node.get_our_node_id(), &route_params, &nodes[1].network_graph.read_only(), None,
nodes[0].logger, &scorer, &Default::default(), &random_seed_bytes).unwrap();
let _chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 10001);
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
+ .with_bolt11_features(nodes[1].node.bolt11_invoice_features()).unwrap();
let route = get_route!(nodes[0], payment_params, 10_000).unwrap();
let (our_payment_preimage, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(&nodes[1]);
let chan_2_3 =create_announced_chan_between_nodes_with_value(&nodes, 2, 3, 100_000, 0);
let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[3].node.invoice_features()).unwrap();
+ .with_bolt11_features(nodes[3].node.bolt11_invoice_features()).unwrap();
let mut route = get_route!(nodes[0], payment_params, 15_000_000).unwrap();
assert_eq!(route.paths.len(), 2);
route.paths.sort_by(|path_a, _| {
#[cfg(feature = "std")]
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
+ #[cfg(feature = "std")]
use crate::ln::msgs::SocketAddressParseError;
#[test]
htlc_minimum_msat: None,
}])];
let payment_params = PaymentParameters::from_node_id(*channel_to_update_counterparty, TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
+ .with_bolt11_features(nodes[2].node.bolt11_invoice_features()).unwrap()
.with_route_hints(hop_hints).unwrap();
get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, PAYMENT_AMT)
};
let phantom_pubkey = $nodes[1].keys_manager.get_node_id(Recipient::PhantomNode).unwrap();
let phantom_route_hint = $nodes[1].node.get_phantom_route_hints();
let payment_params = PaymentParameters::from_node_id(phantom_pubkey, TEST_FINAL_CLTV)
- .with_bolt11_features($nodes[1].node.invoice_features()).unwrap()
+ .with_bolt11_features($nodes[1].node.bolt11_invoice_features()).unwrap()
.with_route_hints(vec![RouteHint(vec![
RouteHintHop {
src_node_id: $nodes[0].node.get_our_node_id(),
session_privs: HashSet<[u8; 32]>,
},
AwaitingInvoice {
- absolute_expiry: Duration,
+ expiration: StaleExpiration,
retry_strategy: Retry,
max_total_routing_fee_msat: Option<u64>,
},
}
}
+/// How long before a [`PendingOutboundPayment::AwaitingInvoice`] should be considered stale and
+/// candidate for removal in [`OutboundPayments::remove_stale_payments`].
+#[derive(Clone, Copy)]
+pub(crate) enum StaleExpiration {
+ /// Number of times [`OutboundPayments::remove_stale_payments`] is called.
+ TimerTicks(u64),
+ /// Duration since the Unix epoch.
+ AbsoluteTimeout(core::time::Duration),
+}
+
+impl_writeable_tlv_based_enum!(StaleExpiration,
+ ;
+ (0, TimerTicks),
+ (2, AbsoluteTimeout)
+);
+
/// Indicates an immediate error on [`ChannelManager::send_payment`]. Further errors may be
/// surfaced later via [`Event::PaymentPathFailed`] and [`Event::PaymentFailed`].
///
}
pub(super) fn add_new_awaiting_invoice(
- &self, payment_id: PaymentId, absolute_expiry: Duration, retry_strategy: Retry,
+ &self, payment_id: PaymentId, expiration: StaleExpiration, retry_strategy: Retry,
max_total_routing_fee_msat: Option<u64>
) -> Result<(), ()> {
let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();
hash_map::Entry::Occupied(_) => Err(()),
hash_map::Entry::Vacant(entry) => {
entry.insert(PendingOutboundPayment::AwaitingInvoice {
- absolute_expiry,
+ expiration,
retry_strategy,
max_total_routing_fee_msat,
});
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>)
{
let mut pending_outbound_payments = self.pending_outbound_payments.lock().unwrap();
- #[cfg(not(invreqfailed))]
- let pending_events = pending_events.lock().unwrap();
- #[cfg(invreqfailed)]
let mut pending_events = pending_events.lock().unwrap();
pending_outbound_payments.retain(|payment_id, payment| match payment {
// If an outbound payment was completed, and no pending HTLCs remain, we should remove it
true
}
},
- PendingOutboundPayment::AwaitingInvoice { absolute_expiry, .. } => {
- if duration_since_epoch < *absolute_expiry {
- true
- } else {
- #[cfg(invreqfailed)]
+ PendingOutboundPayment::AwaitingInvoice { expiration, .. } => {
+ let is_stale = match expiration {
+ StaleExpiration::AbsoluteTimeout(absolute_expiry) => {
+ *absolute_expiry <= duration_since_epoch
+ },
+ StaleExpiration::TimerTicks(timer_ticks_remaining) => {
+ if *timer_ticks_remaining > 0 {
+ *timer_ticks_remaining -= 1;
+ false
+ } else {
+ true
+ }
+ },
+ };
+ if is_stale {
pending_events.push_back(
(events::Event::InvoiceRequestFailed { payment_id: *payment_id }, None)
);
false
+ } else {
+ true
}
},
_ => true,
payment.remove();
}
} else if let PendingOutboundPayment::AwaitingInvoice { .. } = payment.get() {
- #[cfg(invreqfailed)]
pending_events.lock().unwrap().push_back((events::Event::InvoiceRequestFailed {
payment_id,
}, None));
(2, payment_hash, required),
},
(5, AwaitingInvoice) => {
- (0, absolute_expiry, required),
+ (0, expiration, required),
(2, retry_strategy, required),
(4, max_total_routing_fee_msat, option),
},
use crate::ln::channelmanager::{PaymentId, RecipientOnionFields};
use crate::ln::features::{ChannelFeatures, NodeFeatures};
use crate::ln::msgs::{ErrorAction, LightningError};
- use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, Retry, RetryableSendFailure};
+ use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, Retry, RetryableSendFailure, StaleExpiration};
+ #[cfg(feature = "std")]
use crate::offers::invoice::DEFAULT_RELATIVE_EXPIRY;
use crate::offers::offer::OfferBuilder;
use crate::offers::test_utils::*;
}
#[test]
- #[cfg(invreqfailed)]
- fn removes_stale_awaiting_invoice() {
+ fn removes_stale_awaiting_invoice_using_absolute_timeout() {
let pending_events = Mutex::new(VecDeque::new());
let outbound_payments = OutboundPayments::new();
let payment_id = PaymentId([0; 32]);
let absolute_expiry = 100;
let tick_interval = 10;
+ let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(absolute_expiry));
assert!(!outbound_payments.has_pending_payments());
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0), None
+ payment_id, expiration, Retry::Attempts(0), None
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, Duration::from_secs(absolute_expiry + 1), Retry::Attempts(0), None
+ payment_id, expiration, Retry::Attempts(0), None
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, Duration::from_secs(absolute_expiry + 1), Retry::Attempts(0), None
+ payment_id, expiration, Retry::Attempts(0), None
+ ).is_err()
+ );
+ }
+
+ #[test]
+ fn removes_stale_awaiting_invoice_using_timer_ticks() {
+ let pending_events = Mutex::new(VecDeque::new());
+ let outbound_payments = OutboundPayments::new();
+ let payment_id = PaymentId([0; 32]);
+ let timer_ticks = 3;
+ let expiration = StaleExpiration::TimerTicks(timer_ticks);
+
+ assert!(!outbound_payments.has_pending_payments());
+ assert!(
+ outbound_payments.add_new_awaiting_invoice(
+ payment_id, expiration, Retry::Attempts(0), None
+ ).is_ok()
+ );
+ assert!(outbound_payments.has_pending_payments());
+
+ for i in 0..timer_ticks {
+ let duration_since_epoch = Duration::from_secs(i * 60);
+ outbound_payments.remove_stale_payments(duration_since_epoch, &pending_events);
+
+ assert!(outbound_payments.has_pending_payments());
+ assert!(pending_events.lock().unwrap().is_empty());
+ }
+
+ let duration_since_epoch = Duration::from_secs(timer_ticks * 60);
+ outbound_payments.remove_stale_payments(duration_since_epoch, &pending_events);
+
+ assert!(!outbound_payments.has_pending_payments());
+ assert!(!pending_events.lock().unwrap().is_empty());
+ assert_eq!(
+ pending_events.lock().unwrap().pop_front(),
+ Some((Event::InvoiceRequestFailed { payment_id }, None)),
+ );
+ assert!(pending_events.lock().unwrap().is_empty());
+
+ assert!(
+ outbound_payments.add_new_awaiting_invoice(
+ payment_id, expiration, Retry::Attempts(0), None
+ ).is_ok()
+ );
+ assert!(outbound_payments.has_pending_payments());
+
+ assert!(
+ outbound_payments.add_new_awaiting_invoice(
+ payment_id, expiration, Retry::Attempts(0), None
).is_err()
);
}
#[test]
- #[cfg(invreqfailed)]
fn removes_abandoned_awaiting_invoice() {
let pending_events = Mutex::new(VecDeque::new());
let outbound_payments = OutboundPayments::new();
let payment_id = PaymentId([0; 32]);
- let absolute_expiry = 100;
+ let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
assert!(!outbound_payments.has_pending_payments());
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0), None
+ payment_id, expiration, Retry::Attempts(0), None
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
let pending_events = Mutex::new(VecDeque::new());
let outbound_payments = OutboundPayments::new();
let payment_id = PaymentId([0; 32]);
- let absolute_expiry = 100;
+ let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0), None
+ payment_id, expiration, Retry::Attempts(0), None
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
let pending_events = Mutex::new(VecDeque::new());
let outbound_payments = OutboundPayments::new();
let payment_id = PaymentId([0; 32]);
- let absolute_expiry = 100;
+ let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
.amount_msats(1000)
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0),
+ payment_id, expiration, Retry::Attempts(0),
Some(invoice.amount_msats() / 100 + 50_000)
).is_ok()
);
let pending_events = Mutex::new(VecDeque::new());
let outbound_payments = OutboundPayments::new();
let payment_id = PaymentId([0; 32]);
- let absolute_expiry = 100;
+ let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
.amount_msats(1000)
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0),
+ payment_id, expiration, Retry::Attempts(0),
Some(invoice.amount_msats() / 100 + 50_000)
).is_ok()
);
let pending_events = Mutex::new(VecDeque::new());
let outbound_payments = OutboundPayments::new();
let payment_id = PaymentId([0; 32]);
- let absolute_expiry = 100;
+ let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
.amount_msats(1000)
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0), Some(1234)
+ payment_id, expiration, Retry::Attempts(0), Some(1234)
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
let amt_msat = 1_000_000;
let max_total_routing_fee_msat = 50_000;
let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[3].node.invoice_features()).unwrap();
+ .with_bolt11_features(nodes[3].node.bolt11_invoice_features()).unwrap();
let (mut route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(
nodes[0], nodes[3], payment_params, amt_msat, Some(max_total_routing_fee_msat));
let path = route.paths[0].clone();
let max_total_routing_fee_msat = Some(1_000_000);
let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[3].node.invoice_features()).unwrap();
+ .with_bolt11_features(nodes[3].node.bolt11_invoice_features()).unwrap();
let (mut route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(
nodes[0], nodes[3], payment_params, amt_msat, max_total_routing_fee_msat);
let (payment_hash, payment_secret) = nodes[1].node.create_inbound_payment(Some(amt_msat), expiry_secs, None).unwrap();
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
+ .with_bolt11_features(nodes[1].node.bolt11_invoice_features()).unwrap();
let scorer = test_utils::TestScorer::new();
let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
htlc_maximum_msat: None,
}])
]).unwrap()
- .with_bolt11_features(nodes[2].node.invoice_features()).unwrap();
- let route_params = RouteParameters::from_payment_params_and_value(payment_params, amt_msat,);
- let route = get_route(&nodes[0].node.get_our_node_id(), &route_params,
- &nodes[0].network_graph.read_only(), None, nodes[0].logger, &scorer, &Default::default(),
- &random_seed_bytes).unwrap();
+ .with_bolt11_features(nodes[2].node.bolt11_invoice_features()).unwrap();
+ let route_params = RouteParameters::from_payment_params_and_value(payment_params, amt_msat);
+ let route = get_route(
+ &nodes[0].node.get_our_node_id(), &route_params, &nodes[0].network_graph.read_only(), None,
+ nodes[0].logger, &scorer, &(), &random_seed_bytes
+ ).unwrap();
let (payment_hash, payment_secret) = nodes[2].node.create_inbound_payment(Some(amt_msat), 60 * 60, None).unwrap();
nodes[0].node.send_payment_with_route(&route, payment_hash,
}
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)
.with_route_hints(route_hints).unwrap()
- .with_bolt11_features(nodes[2].node.invoice_features()).unwrap();
+ .with_bolt11_features(nodes[2].node.bolt11_invoice_features()).unwrap();
let route_params = RouteParameters::from_payment_params_and_value(payment_params, amt_msat);
let (payment_hash, payment_secret) = nodes[2].node.create_inbound_payment(Some(amt_msat), 60 * 60, None).unwrap();
nodes[0].node.send_payment(payment_hash, RecipientOnionFields::secret_only(payment_secret),
create_announced_chan_between_nodes(&nodes, 2, 3);
let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[3]);
- let mut route_params = RouteParameters::from_payment_params_and_value(
- PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[1].node.invoice_features()).unwrap(),
- 10_000_000);
+ let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV)
+ .with_bolt11_features(nodes[1].node.bolt11_invoice_features()).unwrap();
+ let mut route_params = RouteParameters::from_payment_params_and_value(payment_params, 10_000_000);
let mut route = nodes[0].router.find_route(&nodes[0].node.get_our_node_id(), &route_params,
None, nodes[0].node.compute_inflight_htlcs()).unwrap();
// Make sure the route is ordered as the B->D path before C->D
let chan_2_3 = create_announced_chan_between_nodes_with_value(&nodes, 2, 3, 100_000, 0);
let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[3].node.invoice_features()).unwrap();
+ .with_bolt11_features(nodes[3].node.bolt11_invoice_features()).unwrap();
let mut route = get_route!(nodes[0], payment_params, 15_000_000).unwrap();
assert_eq!(route.paths.len(), 2);
route.paths.sort_by(|path_a, _| {
let payment_metadata = vec![44, 49, 52, 142];
let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
+ .with_bolt11_features(nodes[1].node.bolt11_invoice_features()).unwrap();
let mut route_params = RouteParameters::from_payment_params_and_value(payment_params, amt_msat);
// Send the MPP payment, delivering the updated commitment state to nodes[1].
SD,
Arc<SimpleArcChannelManager<M, T, F, L>>,
Arc<P2PGossipSync<Arc<NetworkGraph<Arc<L>>>, C, Arc<L>>>,
- Arc<SimpleArcOnionMessenger<L>>,
+ Arc<SimpleArcOnionMessenger<M, T, F, L>>,
Arc<L>,
IgnoringMessageHandler,
Arc<KeysManager>
///
/// This is not exported to bindings users as general type aliases don't make sense in bindings.
pub type SimpleRefPeerManager<
- 'a, 'b, 'c, 'd, 'e, 'f, 'logger, 'h, 'i, 'j, 'graph, SD, M, T, F, C, L
+ 'a, 'b, 'c, 'd, 'e, 'f, 'logger, 'h, 'i, 'j, 'graph, 'k, SD, M, T, F, C, L
> = PeerManager<
SD,
&'j SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, 'graph, 'logger, 'i, M, T, F, L>,
&'f P2PGossipSync<&'graph NetworkGraph<&'logger L>, C, &'logger L>,
- &'h SimpleRefOnionMessenger<'logger, 'i, 'j, L>,
+ &'h SimpleRefOnionMessenger<'a, 'b, 'c, 'd, 'e, 'graph, 'logger, 'i, 'j, 'k, M, T, F, L>,
&'logger L,
IgnoringMessageHandler,
&'c KeysManager
}]);
let last_hops = vec![route_hint];
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
+ .with_bolt11_features(nodes[2].node.bolt11_invoice_features()).unwrap()
.with_route_hints(last_hops).unwrap();
let (route, our_payment_hash, our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 10_000);
htlc_minimum_msat: None,
}])];
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), 42)
- .with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
+ .with_bolt11_features(nodes[2].node.bolt11_invoice_features()).unwrap()
.with_route_hints(hop_hints).unwrap();
let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 100_000);
assert_eq!(route.paths[0].hops[1].short_channel_id, last_hop[0].inbound_scid_alias.unwrap());
htlc_minimum_msat: None,
}])];
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), 42)
- .with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
+ .with_bolt11_features(nodes[2].node.bolt11_invoice_features()).unwrap()
.with_route_hints(hop_hints.clone()).unwrap();
let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 100_000);
assert_eq!(route.paths[0].hops[1].short_channel_id, last_hop[0].inbound_scid_alias.unwrap());
hop_hints[0].0[0].short_channel_id = last_hop[0].short_channel_id.unwrap();
let payment_params_2 = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), 42)
- .with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
+ .with_bolt11_features(nodes[2].node.bolt11_invoice_features()).unwrap()
.with_route_hints(hop_hints).unwrap();
let (route_2, payment_hash_2, _, payment_secret_2) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params_2, 100_000);
assert_eq!(route_2.paths[0].hops[1].short_channel_id, last_hop[0].short_channel_id.unwrap());
htlc_minimum_msat: None,
}])];
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), 42)
- .with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
+ .with_bolt11_features(nodes[2].node.bolt11_invoice_features()).unwrap()
.with_route_hints(hop_hints).unwrap();
let (mut route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 10_000);
assert_eq!(route.paths[0].hops[1].short_channel_id, nodes[2].node.list_usable_channels()[0].inbound_scid_alias.unwrap());
let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[0]);
- let payment_params_1 = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV).with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
+ let payment_params_1 = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
+ .with_bolt11_features(nodes[1].node.bolt11_invoice_features()).unwrap();
let route_params = RouteParameters::from_payment_params_and_value(payment_params_1, 100_000);
let route_1 = get_route(&nodes[0].node.get_our_node_id(), &route_params,
&nodes[0].network_graph.read_only(), None, &logger, &scorer, &Default::default(), &random_seed_bytes).unwrap();
- let payment_params_2 = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id(),
- TEST_FINAL_CLTV).with_bolt11_features(nodes[0].node.invoice_features()).unwrap();
+ let payment_params_2 = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id(), TEST_FINAL_CLTV)
+ .with_bolt11_features(nodes[0].node.bolt11_invoice_features()).unwrap();
let route_params = RouteParameters::from_payment_params_and_value(payment_params_2, 100_000);
let route_2 = get_route(&nodes[1].node.get_our_node_id(), &route_params,
&nodes[1].network_graph.read_only(), None, &logger, &scorer, &Default::default(), &random_seed_bytes).unwrap();
invoice_request: &'a InvoiceRequest, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
created_at: Duration, payment_hash: PaymentHash
) -> Result<Self, Bolt12SemanticError> {
- let amount_msats = Self::check_amount_msats(invoice_request)?;
+ let amount_msats = Self::amount_msats(invoice_request)?;
let signing_pubkey = invoice_request.contents.inner.offer.signing_pubkey();
let contents = InvoiceContents::ForOffer {
invoice_request: invoice_request.contents.clone(),
invoice_request: &'a InvoiceRequest, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
created_at: Duration, payment_hash: PaymentHash, keys: KeyPair
) -> Result<Self, Bolt12SemanticError> {
- let amount_msats = Self::check_amount_msats(invoice_request)?;
+ let amount_msats = Self::amount_msats(invoice_request)?;
let signing_pubkey = invoice_request.contents.inner.offer.signing_pubkey();
let contents = InvoiceContents::ForOffer {
invoice_request: invoice_request.contents.clone(),
}
impl<'a, S: SigningPubkeyStrategy> InvoiceBuilder<'a, S> {
- fn check_amount_msats(invoice_request: &InvoiceRequest) -> Result<u64, Bolt12SemanticError> {
+ pub(crate) fn amount_msats(
+ invoice_request: &InvoiceRequest
+ ) -> Result<u64, Bolt12SemanticError> {
match invoice_request.amount_msats() {
Some(amount_msats) => Ok(amount_msats),
None => match invoice_request.contents.inner.offer.amount() {
}
}
+ #[cfg(not(feature = "std"))] {
+ if self.invoice.is_offer_or_refund_expired_no_std(self.invoice.created_at()) {
+ return Err(Bolt12SemanticError::AlreadyExpired);
+ }
+ }
+
let InvoiceBuilder { invreq_bytes, invoice, .. } = self;
Ok(UnsignedBolt12Invoice::new(invreq_bytes, invoice))
}
}
}
+ #[cfg(not(feature = "std"))] {
+ if self.invoice.is_offer_or_refund_expired_no_std(self.invoice.created_at()) {
+ return Err(Bolt12SemanticError::AlreadyExpired);
+ }
+ }
+
let InvoiceBuilder {
invreq_bytes, invoice, signing_pubkey_strategy: DerivedSigningPubkey(keys)
} = self;
}
}
+ #[cfg(not(feature = "std"))]
+ fn is_offer_or_refund_expired_no_std(&self, duration_since_epoch: Duration) -> bool {
+ match self {
+ InvoiceContents::ForOffer { invoice_request, .. } =>
+ invoice_request.inner.offer.is_expired_no_std(duration_since_epoch),
+ InvoiceContents::ForRefund { refund, .. } =>
+ refund.is_expired_no_std(duration_since_epoch),
+ }
+ }
+
fn offer_chains(&self) -> Option<Vec<ChainHash>> {
match self {
InvoiceContents::ForOffer { invoice_request, .. } =>
pub suggested_value: Option<Vec<u8>>,
}
+impl InvoiceError {
+ /// Creates an [`InvoiceError`] with the given message.
+ pub fn from_str(s: &str) -> Self {
+ Self {
+ erroneous_field: None,
+ message: UntrustedString(s.to_string()),
+ }
+ }
+}
+
impl core::fmt::Display for InvoiceError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
self.message.fmt(f)
/// by the offer.
///
/// Successive calls to this method will override the previous setting.
- pub fn chain(mut self, network: Network) -> Result<Self, Bolt12SemanticError> {
- let chain = ChainHash::using_genesis_block(network);
+ pub fn chain(self, network: Network) -> Result<Self, Bolt12SemanticError> {
+ self.chain_hash(ChainHash::using_genesis_block(network))
+ }
+
+ /// Sets the [`InvoiceRequest::chain`] for paying an invoice. If not called, the chain hash of
+ /// [`Network::Bitcoin`] is assumed. Errors if the chain for `network` is not supported by the
+ /// offer.
+ ///
+ /// Successive calls to this method will override the previous setting.
+ pub(crate) fn chain_hash(mut self, chain: ChainHash) -> Result<Self, Bolt12SemanticError> {
if !self.offer.supports_chain(chain) {
return Err(Bolt12SemanticError::UnsupportedChain);
}
#[cfg(feature = "std")]
pub(super) fn is_expired(&self) -> bool {
- match self.absolute_expiry {
- Some(seconds_from_epoch) => match SystemTime::UNIX_EPOCH.elapsed() {
- Ok(elapsed) => elapsed > seconds_from_epoch,
- Err(_) => false,
- },
- None => false,
- }
+ SystemTime::UNIX_EPOCH
+ .elapsed()
+ .map(|duration_since_epoch| self.is_expired_no_std(duration_since_epoch))
+ .unwrap_or(false)
+ }
+
+ pub(super) fn is_expired_no_std(&self, duration_since_epoch: Duration) -> bool {
+ self.absolute_expiry
+ .map(|absolute_expiry| duration_since_epoch > absolute_expiry)
+ .unwrap_or(false)
}
pub fn issuer(&self) -> Option<PrintableString> {
#[cfg(feature = "std")]
pub(super) fn is_expired(&self) -> bool {
- match self.absolute_expiry {
- Some(seconds_from_epoch) => match SystemTime::UNIX_EPOCH.elapsed() {
- Ok(elapsed) => elapsed > seconds_from_epoch,
- Err(_) => false,
- },
- None => false,
- }
+ SystemTime::UNIX_EPOCH
+ .elapsed()
+ .map(|duration_since_epoch| self.is_expired_no_std(duration_since_epoch))
+ .unwrap_or(false)
+ }
+
+ pub(super) fn is_expired_no_std(&self, duration_since_epoch: Duration) -> bool {
+ self.absolute_expiry
+ .map(|absolute_expiry| duration_since_epoch > absolute_expiry)
+ .unwrap_or(false)
}
pub fn issuer(&self) -> Option<PrintableString> {
use crate::blinded_path::message::{advance_path_by_one, ForwardTlvs, ReceiveTlvs};
use crate::blinded_path::utils;
use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient};
+use crate::ln::channelmanager::{SimpleArcChannelManager, SimpleRefChannelManager};
use crate::ln::features::{InitFeatures, NodeFeatures};
use crate::ln::msgs::{self, OnionMessage, OnionMessageHandler};
use crate::ln::onion_utils;
) -> Result<OnionMessagePath, ()>;
}
-/// A [`MessageRouter`] that always fails.
+/// A [`MessageRouter`] that can only route to a directly connected [`Destination`].
pub struct DefaultMessageRouter;
impl MessageRouter for DefaultMessageRouter {
fn find_path(
- &self, _sender: PublicKey, _peers: Vec<PublicKey>, _destination: Destination
+ &self, _sender: PublicKey, peers: Vec<PublicKey>, destination: Destination
) -> Result<OnionMessagePath, ()> {
- Err(())
+ if peers.contains(&destination.first_node()) {
+ Ok(OnionMessagePath { intermediate_nodes: vec![], destination })
+ } else {
+ Err(())
+ }
}
}
Destination::BlindedPath(BlindedPath { blinded_hops, .. }) => blinded_hops.len(),
}
}
+
+ fn first_node(&self) -> PublicKey {
+ match self {
+ Destination::Node(node_id) => *node_id,
+ Destination::BlindedPath(BlindedPath { introduction_node_id: node_id, .. }) => *node_id,
+ }
+ }
}
/// Errors that may occur when [sending an onion message].
///
/// [`SimpleArcChannelManager`]: crate::ln::channelmanager::SimpleArcChannelManager
/// [`SimpleArcPeerManager`]: crate::ln::peer_handler::SimpleArcPeerManager
-pub type SimpleArcOnionMessenger<L> = OnionMessenger<
+pub type SimpleArcOnionMessenger<M, T, F, L> = OnionMessenger<
Arc<KeysManager>,
Arc<KeysManager>,
Arc<L>,
Arc<DefaultMessageRouter>,
- IgnoringMessageHandler,
+ Arc<SimpleArcChannelManager<M, T, F, L>>,
IgnoringMessageHandler
>;
///
/// [`SimpleRefChannelManager`]: crate::ln::channelmanager::SimpleRefChannelManager
/// [`SimpleRefPeerManager`]: crate::ln::peer_handler::SimpleRefPeerManager
-pub type SimpleRefOnionMessenger<'a, 'b, 'c, L> = OnionMessenger<
+pub type SimpleRefOnionMessenger<
+ 'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, M, T, F, L
+> = OnionMessenger<
&'a KeysManager,
&'a KeysManager,
&'b L,
- &'c DefaultMessageRouter,
- IgnoringMessageHandler,
+ &'i DefaultMessageRouter,
+ &'j SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, M, T, F, L>,
IgnoringMessageHandler
>;
use crate::chain::transaction::OutPoint;
use crate::sign::EntropySource;
use crate::ln::ChannelId;
- use crate::ln::features::{BlindedHopFeatures, Bolt12InvoiceFeatures, ChannelFeatures, InitFeatures, NodeFeatures};
+ use crate::ln::features::{BlindedHopFeatures, ChannelFeatures, InitFeatures, NodeFeatures};
use crate::ln::msgs::{ErrorAction, LightningError, UnsignedChannelUpdate, MAX_VALUE_MSAT};
use crate::ln::channelmanager;
use crate::offers::invoice::BlindedPayInfo;
let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[2], 42)
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
let scorer = ln_test_utils::TestScorer::new();
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph();
let (_, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config)).unwrap();
let scorer = ln_test_utils::TestScorer::new();
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[2], 42)
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
// We will use a simple single-path route from
// our node to node2 via node0: channels {1, 3}.
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[3], 42)
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
// Path via {node7, node2, node4} is channels {12, 13, 6, 11}.
// {12, 13, 11} have the capacities of 100, {6} has a capacity of 50.
let (_, _, _, nodes) = get_nodes(&secp_ctx);
let config = UserConfig::default();
let clear_payment_params = PaymentParameters::from_node_id(nodes[2], 42)
- .with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
do_simple_mpp_route_test(clear_payment_params);
// MPP to a 1-hop blinded path for nodes[2]
- let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
+ let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
let blinded_path = BlindedPath {
introduction_node_id: nodes[2],
blinding_point: ln_test_utils::pubkey(42),
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
let payment_params = PaymentParameters::from_node_id(nodes[3], 42)
- .with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
// We need a route consisting of 3 paths:
// From our node to node3 via {node0, node2}, {node7, node2, node4} and {node7, node2}.
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[3], 42)
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
// This test checks that if we have two cheaper paths and one more expensive path,
// so that liquidity-wise any 2 of 3 combination is sufficient,
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[3], 42)
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
// We need a route consisting of 2 paths:
// From our node to node3 via {node0, node2} and {node7, node2, node4}.
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(PublicKey::from_slice(&[02; 33]).unwrap(), 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap()
+ let payment_params = PaymentParameters::from_node_id(PublicKey::from_slice(&[02; 33]).unwrap(), 42)
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config)).unwrap()
.with_route_hints(vec![RouteHint(vec![RouteHintHop {
src_node_id: nodes[2],
short_channel_id: 42,
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap()
+ let payment_params = PaymentParameters::from_node_id(nodes[2], 42)
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap()
.with_max_channel_saturation_power_of_half(0);
// We need a route consisting of 3 paths:
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[2], 42)
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
// We modify the graph to set the htlc_minimum of channel 2 and 4 as needed - channel 2
// gets an htlc_maximum_msat of 80_000 and channel 4 an htlc_minimum_msat of 90_000. We
assert_eq!(route.paths[0].hops[1].short_channel_id, 13);
assert_eq!(route.paths[0].hops[1].fee_msat, 90_000);
assert_eq!(route.paths[0].hops[1].cltv_expiry_delta, 42);
- assert_eq!(route.paths[0].hops[1].node_features.le_flags(), channelmanager::provided_invoice_features(&config).le_flags());
+ assert_eq!(route.paths[0].hops[1].node_features.le_flags(), channelmanager::provided_bolt11_invoice_features(&config).le_flags());
assert_eq!(route.paths[0].hops[1].channel_features.le_flags(), &id_to_feature_flags(13));
}
}
let network_graph = NetworkGraph::new(Network::Testnet, Arc::clone(&logger));
let scorer = ln_test_utils::TestScorer::new();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[0], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[0], 42)
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
});
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[2], 42)
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
// 100,000 sats is less than the available liquidity on each channel, set above.
let params = ProbabilisticScoringFeeParameters::default();
let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &graph, &logger);
- let features = channelmanager::provided_invoice_features(&UserConfig::default());
+ let features = channelmanager::provided_bolt11_invoice_features(&UserConfig::default());
super::bench_utils::generate_test_routes(&graph, &mut scorer, ¶ms, features, random_init_seed(), 0, 2);
}
let params = ProbabilisticScoringFeeParameters::default();
let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &graph, &logger);
- let features = channelmanager::provided_invoice_features(&UserConfig::default());
+ let features = channelmanager::provided_bolt11_invoice_features(&UserConfig::default());
super::bench_utils::generate_test_routes(&graph, &mut scorer, ¶ms, features, random_init_seed(), 1_000_000, 2);
}
let dest_node_id = ln_test_utils::pubkey(42);
let payment_params = PaymentParameters::from_node_id(dest_node_id, 42)
.with_route_hints(vec![route_hint_1.clone()]).unwrap()
- .with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
// Make sure we'll error if our route hints don't have enough liquidity according to their
// htlc_maximum_msat.
route_hint_2.0[0].short_channel_id = 43;
let payment_params = PaymentParameters::from_node_id(dest_node_id, 42)
.with_route_hints(vec![route_hint_1, route_hint_2]).unwrap()
- .with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
let mut route_params = RouteParameters::from_payment_params_and_value(
payment_params, max_htlc_msat + 1);
route_params.max_total_routing_fee_msat = Some(max_htlc_msat * 2);
let dest_node_id = ln_test_utils::pubkey(44);
let payment_params = PaymentParameters::from_node_id(dest_node_id, 42)
.with_route_hints(vec![route_hint_1, route_hint_2]).unwrap()
- .with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
let route_params = RouteParameters::from_payment_params_and_value(
payment_params, amt_msat);
cltv_expiry_delta: 10,
features: BlindedHopFeatures::empty(),
};
- let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
+ let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
let payment_params = PaymentParameters::blinded(vec![
(blinded_payinfo.clone(), blinded_path.clone()),
(blinded_payinfo.clone(), blinded_path.clone())])
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
+ let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
let blinded_path_1 = BlindedPath {
introduction_node_id: nodes[2],
blinding_point: ln_test_utils::pubkey(42),
(blinded_payinfo_2.clone(), blinded_path_2.clone()),
];
let payment_params = PaymentParameters::blinded(blinded_hints.clone())
- .with_bolt12_features(bolt12_features.clone()).unwrap();
+ .with_bolt12_features(bolt12_features).unwrap();
let mut route_params = RouteParameters::from_payment_params_and_value(payment_params, 100_000);
route_params.max_total_routing_fee_msat = Some(100_000);
blinded_hints[1].0.htlc_maximum_msat = 2_8089_0861_1584_0000;
blinded_hints[1].0.cltv_expiry_delta = 0;
- let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
+ let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
let payment_params = PaymentParameters::blinded(blinded_hints.clone())
- .with_bolt12_features(bolt12_features.clone()).unwrap();
+ .with_bolt12_features(bolt12_features).unwrap();
let netgraph = network_graph.read_only();
let route_params = RouteParameters::from_payment_params_and_value(
];
blinded_hints[1].1.introduction_node_id = nodes[6];
- let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
+ let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
let payment_params = PaymentParameters::blinded(blinded_hints.clone())
.with_bolt12_features(bolt12_features.clone()).unwrap();
blinded_hints[2].1.introduction_node_id = nodes[6];
- let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
+ let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
let payment_params = PaymentParameters::blinded(blinded_hints.clone())
.with_bolt12_features(bolt12_features.clone()).unwrap();
cltv_expiry_delta: 0,
features: BlindedHopFeatures::empty(),
};
- let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
+ let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
PaymentParameters::blinded(vec![(blinded_payinfo, blinded_path)])
.with_bolt12_features(bolt12_features.clone()).unwrap()
} else {
PaymentParameters::from_node_id(nodes[1], 42)
.with_route_hints(vec![route_hint]).unwrap()
- .with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap()
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config)).unwrap()
};
let netgraph = network_graph.read_only();
features: BlindedHopFeatures::empty(),
}, blinded_path.clone()));
}
- let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
+ let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
PaymentParameters::blinded(blinded_hints.clone())
.with_bolt12_features(bolt12_features.clone()).unwrap()
} else {
}
PaymentParameters::from_node_id(nodes[1], 42)
.with_route_hints(route_hints).unwrap()
- .with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap()
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config)).unwrap()
};
let netgraph = network_graph.read_only();
],
})
];
- let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
+ let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
let payment_params = PaymentParameters::blinded(blinded_hints.clone())
.with_bolt12_features(bolt12_features.clone()).unwrap();
let route_params = RouteParameters::from_payment_params_and_value(
features: BlindedHopFeatures::empty(),
}, blinded_path.clone()));
}
- let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
+ let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
PaymentParameters::blinded(blinded_hints.clone())
.with_bolt12_features(bolt12_features.clone()).unwrap()
};
let payment_params = PaymentParameters::from_node_id(dest_node_id, 42)
.with_route_hints(vec![route_hint]).unwrap()
- .with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config)).unwrap();
let route_params = RouteParameters::from_payment_params_and_value(
payment_params, amt_msat);
let payment_params = PaymentParameters::from_node_id(dest_node_id, 42)
.with_route_hints(vec![route_hint]).unwrap()
- .with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config)).unwrap();
let route_params = RouteParameters::from_payment_params_and_value(
payment_params, amt_msat);
// Generate fail/success paths for a wider range of potential amounts with
// MPP enabled to give us a chance to apply penalties for more potential
// routes.
- let mpp_features = channelmanager::provided_invoice_features(&UserConfig::default());
+ let mpp_features = channelmanager::provided_bolt11_invoice_features(&UserConfig::default());
let params = PaymentParameters::from_node_id(dst, 42)
.with_bolt11_features(mpp_features).unwrap();
let route_params = RouteParameters::from_payment_params_and_value(
let network_graph = bench_utils::read_network_graph(&logger).unwrap();
let scorer = FixedPenaltyScorer::with_penalty(0);
generate_routes(bench, &network_graph, scorer, &Default::default(),
- channelmanager::provided_invoice_features(&UserConfig::default()), 0,
+ channelmanager::provided_bolt11_invoice_features(&UserConfig::default()), 0,
"generate_mpp_routes_with_zero_penalty_scorer");
}
let params = ProbabilisticScoringFeeParameters::default();
let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
generate_routes(bench, &network_graph, scorer, ¶ms,
- channelmanager::provided_invoice_features(&UserConfig::default()), 0,
+ channelmanager::provided_bolt11_invoice_features(&UserConfig::default()), 0,
"generate_mpp_routes_with_probabilistic_scorer");
}
let params = ProbabilisticScoringFeeParameters::default();
let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
generate_routes(bench, &network_graph, scorer, ¶ms,
- channelmanager::provided_invoice_features(&UserConfig::default()), 100_000_000,
+ channelmanager::provided_bolt11_invoice_features(&UserConfig::default()), 100_000_000,
"generate_large_mpp_routes_with_probabilistic_scorer");
}
let scorer = ProbabilisticScorer::new(
ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
generate_routes(bench, &network_graph, scorer, ¶ms,
- channelmanager::provided_invoice_features(&UserConfig::default()), 0,
+ channelmanager::provided_bolt11_invoice_features(&UserConfig::default()), 0,
"generate_routes_with_nonlinear_probabilistic_scorer");
}
let scorer = ProbabilisticScorer::new(
ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
generate_routes(bench, &network_graph, scorer, ¶ms,
- channelmanager::provided_invoice_features(&UserConfig::default()), 0,
+ channelmanager::provided_bolt11_invoice_features(&UserConfig::default()), 0,
"generate_mpp_routes_with_nonlinear_probabilistic_scorer");
}
let scorer = ProbabilisticScorer::new(
ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
generate_routes(bench, &network_graph, scorer, ¶ms,
- channelmanager::provided_invoice_features(&UserConfig::default()), 100_000_000,
+ channelmanager::provided_bolt11_invoice_features(&UserConfig::default()), 100_000_000,
"generate_large_mpp_routes_with_nonlinear_probabilistic_scorer");
}