Merge pull request #2071 from TheBlueMatt/2023-01-fix-fast-extra-ready-panic
[rust-lightning] / lightning-invoice / src / utils.rs
index 5d25516b9582888ee7ccfe5d32c523a8612c88cb..11a779b1028a8ddceb825cc833cceb57856d52f0 100644 (file)
@@ -1,7 +1,6 @@
 //! Convenient utilities to create an invoice.
 
 use crate::{CreationError, Currency, Invoice, InvoiceBuilder, SignOrCreationError};
-use crate::payment::Payer;
 
 use crate::{prelude::*, Description, InvoiceDescription, Sha256};
 use bech32::ToBase32;
@@ -9,19 +8,17 @@ use bitcoin_hashes::Hash;
 use lightning::chain;
 use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
 use lightning::chain::keysinterface::{Recipient, NodeSigner, SignerProvider, EntropySource};
-use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
-use lightning::ln::channelmanager::{ChannelDetails, ChannelManager, PaymentId, PaymentSendFailure, MIN_FINAL_CLTV_EXPIRY_DELTA};
-#[cfg(feature = "std")]
+use lightning::ln::{PaymentHash, PaymentSecret};
+use lightning::ln::channelmanager::{ChannelDetails, ChannelManager, MIN_FINAL_CLTV_EXPIRY_DELTA};
 use lightning::ln::channelmanager::{PhantomRouteHints, MIN_CLTV_EXPIRY_DELTA};
 use lightning::ln::inbound_payment::{create, create_from_hash, ExpandedKey};
 use lightning::routing::gossip::RoutingFees;
-use lightning::routing::router::{InFlightHtlcs, Route, RouteHint, RouteHintHop, Router};
+use lightning::routing::router::{RouteHint, RouteHintHop, Router};
 use lightning::util::logger::Logger;
 use secp256k1::PublicKey;
 use core::ops::Deref;
 use core::time::Duration;
 
-#[cfg(feature = "std")]
 /// Utility to create an invoice that can be paid to one of multiple nodes, or a "phantom invoice."
 /// See [`PhantomKeysManager`] for more information on phantom node payments.
 ///
@@ -41,6 +38,8 @@ use core::time::Duration;
 ///
 /// `invoice_expiry_delta_secs` describes the number of seconds that the invoice is valid for
 /// in excess of the current time.
+/// 
+/// `duration_since_epoch` is the current time since epoch in seconds.
 ///
 /// You can specify a custom `min_final_cltv_expiry_delta`, or let LDK default it to
 /// [`MIN_FINAL_CLTV_EXPIRY_DELTA`]. The provided expiry must be at least [`MIN_FINAL_CLTV_EXPIRY_DELTA`] - 3.
@@ -57,10 +56,13 @@ use core::time::Duration;
 /// [`ChannelManager::create_inbound_payment_for_hash`]: lightning::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash
 /// [`PhantomRouteHints::channels`]: lightning::ln::channelmanager::PhantomRouteHints::channels
 /// [`MIN_FINAL_CLTV_EXPIRY_DETLA`]: lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA
+/// 
+/// This can be used in a `no_std` environment, where [`std::time::SystemTime`] is not
+/// available and the current time is supplied by the caller.
 pub fn create_phantom_invoice<ES: Deref, NS: Deref, L: Deref>(
        amt_msat: Option<u64>, payment_hash: Option<PaymentHash>, description: String,
        invoice_expiry_delta_secs: u32, phantom_route_hints: Vec<PhantomRouteHints>, entropy_source: ES,
-       node_signer: NS, logger: L, network: Currency, min_final_cltv_expiry_delta: Option<u16>,
+       node_signer: NS, logger: L, network: Currency, min_final_cltv_expiry_delta: Option<u16>, duration_since_epoch: Duration,
 ) -> Result<Invoice, SignOrCreationError<()>>
 where
        ES::Target: EntropySource,
@@ -71,11 +73,10 @@ where
        let description = InvoiceDescription::Direct(&description,);
        _create_phantom_invoice::<ES, NS, L>(
                amt_msat, payment_hash, description, invoice_expiry_delta_secs, phantom_route_hints,
-               entropy_source, node_signer, logger, network, min_final_cltv_expiry_delta,
+               entropy_source, node_signer, logger, network, min_final_cltv_expiry_delta, duration_since_epoch,
        )
 }
 
-#[cfg(feature = "std")]
 /// Utility to create an invoice that can be paid to one of multiple nodes, or a "phantom invoice."
 /// See [`PhantomKeysManager`] for more information on phantom node payments.
 ///
@@ -97,6 +98,8 @@ where
 ///
 /// `invoice_expiry_delta_secs` describes the number of seconds that the invoice is valid for
 /// in excess of the current time.
+/// 
+/// `duration_since_epoch` is the current time since epoch in seconds.
 ///
 /// Note that the provided `keys_manager`'s `NodeSigner` implementation must support phantom
 /// invoices in its `sign_invoice` implementation ([`PhantomKeysManager`] satisfies this
@@ -107,10 +110,13 @@ where
 /// [`ChannelManager::create_inbound_payment`]: lightning::ln::channelmanager::ChannelManager::create_inbound_payment
 /// [`ChannelManager::create_inbound_payment_for_hash`]: lightning::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash
 /// [`PhantomRouteHints::channels`]: lightning::ln::channelmanager::PhantomRouteHints::channels
+/// 
+/// This can be used in a `no_std` environment, where [`std::time::SystemTime`] is not
+/// available and the current time is supplied by the caller.
 pub fn create_phantom_invoice_with_description_hash<ES: Deref, NS: Deref, L: Deref>(
        amt_msat: Option<u64>, payment_hash: Option<PaymentHash>, invoice_expiry_delta_secs: u32,
        description_hash: Sha256, phantom_route_hints: Vec<PhantomRouteHints>, entropy_source: ES,
-       node_signer: NS, logger: L, network: Currency, min_final_cltv_expiry_delta: Option<u16>,
+       node_signer: NS, logger: L, network: Currency, min_final_cltv_expiry_delta: Option<u16>, duration_since_epoch: Duration,
 ) -> Result<Invoice, SignOrCreationError<()>>
 where
        ES::Target: EntropySource,
@@ -120,22 +126,20 @@ where
        _create_phantom_invoice::<ES, NS, L>(
                amt_msat, payment_hash, InvoiceDescription::Hash(&description_hash),
                invoice_expiry_delta_secs, phantom_route_hints, entropy_source, node_signer, logger, network,
-               min_final_cltv_expiry_delta,
+               min_final_cltv_expiry_delta, duration_since_epoch,
        )
 }
 
-#[cfg(feature = "std")]
 fn _create_phantom_invoice<ES: Deref, NS: Deref, L: Deref>(
        amt_msat: Option<u64>, payment_hash: Option<PaymentHash>, description: InvoiceDescription,
        invoice_expiry_delta_secs: u32, phantom_route_hints: Vec<PhantomRouteHints>, entropy_source: ES,
-       node_signer: NS, logger: L, network: Currency, min_final_cltv_expiry_delta: Option<u16>,
+       node_signer: NS, logger: L, network: Currency, min_final_cltv_expiry_delta: Option<u16>, duration_since_epoch: Duration,
 ) -> Result<Invoice, SignOrCreationError<()>>
 where
        ES::Target: EntropySource,
        NS::Target: NodeSigner,
        L::Target: Logger,
 {
-       use std::time::{SystemTime, UNIX_EPOCH};
 
        if phantom_route_hints.len() == 0 {
                return Err(SignOrCreationError::CreationError(
@@ -162,10 +166,9 @@ where
                        amt_msat,
                        payment_hash,
                        invoice_expiry_delta_secs,
-                       SystemTime::now()
-                               .duration_since(UNIX_EPOCH)
-                               .expect("Time must be > 1970")
+                       duration_since_epoch
                                .as_secs(),
+                       min_final_cltv_expiry_delta,
                )
                .map_err(|_| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
                (payment_hash, payment_secret)
@@ -175,10 +178,9 @@ where
                        amt_msat,
                        invoice_expiry_delta_secs,
                        &entropy_source,
-                       SystemTime::now()
-                               .duration_since(UNIX_EPOCH)
-                               .expect("Time must be > 1970")
+                       duration_since_epoch
                                .as_secs(),
+                       min_final_cltv_expiry_delta,
                )
                .map_err(|_| SignOrCreationError::CreationError(CreationError::InvalidAmount))?
        };
@@ -187,7 +189,7 @@ where
                phantom_route_hints.len(), log_bytes!(payment_hash.0));
 
        let mut invoice = invoice
-               .current_timestamp()
+               .duration_since_epoch(duration_since_epoch)
                .payment_hash(Hash::from_slice(&payment_hash.0).unwrap())
                .payment_secret(payment_secret)
                .min_final_cltv_expiry_delta(
@@ -397,7 +399,7 @@ fn _create_invoice_from_channelmanager_and_duration_since_epoch<M: Deref, T: Der
        // `create_inbound_payment` only returns an error if the amount is greater than the total bitcoin
        // supply.
        let (payment_hash, payment_secret) = channelmanager
-               .create_inbound_payment(amt_msat, invoice_expiry_delta_secs)
+               .create_inbound_payment(amt_msat, invoice_expiry_delta_secs, min_final_cltv_expiry_delta)
                .map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
        _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash(
                channelmanager, node_signer, logger, network, amt_msat, description, duration_since_epoch,
@@ -424,7 +426,8 @@ pub fn create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_
                L::Target: Logger,
 {
        let payment_secret = channelmanager
-               .create_inbound_payment_for_hash(payment_hash,amt_msat, invoice_expiry_delta_secs)
+               .create_inbound_payment_for_hash(payment_hash, amt_msat, invoice_expiry_delta_secs,
+                       min_final_cltv_expiry_delta)
                .map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
        _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash(
                channelmanager, node_signer, logger, network, amt_msat,
@@ -630,51 +633,6 @@ fn filter_channels<L: Deref>(
                .collect::<Vec<RouteHint>>()
 }
 
-impl<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref> Payer 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 node_id(&self) -> PublicKey {
-               self.get_our_node_id()
-       }
-
-       fn first_hops(&self) -> Vec<ChannelDetails> {
-               self.list_usable_channels()
-       }
-
-       fn send_payment(
-               &self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>,
-               payment_id: PaymentId
-       ) -> Result<(), PaymentSendFailure> {
-               self.send_payment(route, payment_hash, payment_secret, payment_id)
-       }
-
-       fn send_spontaneous_payment(
-               &self, route: &Route, payment_preimage: PaymentPreimage, payment_id: PaymentId,
-       ) -> Result<(), PaymentSendFailure> {
-               self.send_spontaneous_payment(route, Some(payment_preimage), payment_id).map(|_| ())
-       }
-
-       fn retry_payment(
-               &self, route: &Route, payment_id: PaymentId
-       ) -> Result<(), PaymentSendFailure> {
-               self.retry_payment(route, payment_id)
-       }
-
-       fn abandon_payment(&self, payment_id: PaymentId) {
-               self.abandon_payment(payment_id)
-       }
-
-       fn inflight_htlcs(&self) -> InFlightHtlcs { self.compute_inflight_htlcs() }
-}
-
 #[cfg(test)]
 mod test {
        use core::time::Duration;
@@ -721,18 +679,18 @@ mod test {
                assert_eq!(invoice.route_hints()[0].0[0].htlc_minimum_msat, chan.inbound_htlc_minimum_msat);
                assert_eq!(invoice.route_hints()[0].0[0].htlc_maximum_msat, chan.inbound_htlc_maximum_msat);
 
-               let payment_params = PaymentParameters::from_node_id(invoice.recover_payee_pub_key())
+               let payment_params = PaymentParameters::from_node_id(invoice.recover_payee_pub_key(),
+                               invoice.min_final_cltv_expiry_delta() as u32)
                        .with_features(invoice.features().unwrap().clone())
                        .with_route_hints(invoice.route_hints());
                let route_params = RouteParameters {
                        payment_params,
                        final_value_msat: invoice.amount_milli_satoshis().unwrap(),
-                       final_cltv_expiry_delta: invoice.min_final_cltv_expiry_delta() as u32,
                };
                let first_hops = nodes[0].node.list_usable_channels();
                let network_graph = &node_cfgs[0].network_graph;
                let logger = test_utils::TestLogger::new();
-               let scorer = test_utils::TestScorer::with_penalty(0);
+               let scorer = test_utils::TestScorer::new();
                let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
                let route = find_route(
                        &nodes[0].node.get_our_node_id(), &route_params, &network_graph,
@@ -883,13 +841,13 @@ mod test {
 
                // With only one sufficient-value peer connected we should only get its hint
                scid_aliases.remove(&chan_b.0.short_channel_id_alias.unwrap());
-               nodes[0].node.peer_disconnected(&nodes[2].node.get_our_node_id(), false);
+               nodes[0].node.peer_disconnected(&nodes[2].node.get_our_node_id());
                match_invoice_routes(Some(1_000_000_000), &nodes[0], scid_aliases.clone());
 
                // If we don't have any sufficient-value peers connected we should get all hints with
                // sufficient value, even though there is a connected insufficient-value peer.
                scid_aliases.insert(chan_b.0.short_channel_id_alias.unwrap());
-               nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
+               nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
                match_invoice_routes(Some(1_000_000_000), &nodes[0], scid_aliases);
        }
 
@@ -1069,7 +1027,7 @@ mod test {
                        crate::utils::create_phantom_invoice::<&test_utils::TestKeysInterface, &test_utils::TestKeysInterface, &test_utils::TestLogger>(
                                Some(payment_amt), payment_hash, "test".to_string(), non_default_invoice_expiry_secs,
                                route_hints, &nodes[1].keys_manager, &nodes[1].keys_manager, &nodes[1].logger,
-                               Currency::BitcoinTestnet, None,
+                               Currency::BitcoinTestnet, None, Duration::from_secs(1234567)
                        ).unwrap();
                let (payment_hash, payment_secret) = (PaymentHash(invoice.payment_hash().into_inner()), *invoice.payment_secret());
                let payment_preimage = if user_generated_pmt_hash {
@@ -1084,18 +1042,18 @@ mod test {
                assert_eq!(invoice.expiry_time(), Duration::from_secs(non_default_invoice_expiry_secs.into()));
                assert!(!invoice.features().unwrap().supports_basic_mpp());
 
-               let payment_params = PaymentParameters::from_node_id(invoice.recover_payee_pub_key())
+               let payment_params = PaymentParameters::from_node_id(invoice.recover_payee_pub_key(),
+                               invoice.min_final_cltv_expiry_delta() as u32)
                        .with_features(invoice.features().unwrap().clone())
                        .with_route_hints(invoice.route_hints());
                let params = RouteParameters {
                        payment_params,
                        final_value_msat: invoice.amount_milli_satoshis().unwrap(),
-                       final_cltv_expiry_delta: invoice.min_final_cltv_expiry_delta() as u32,
                };
                let first_hops = nodes[0].node.list_usable_channels();
                let network_graph = &node_cfgs[0].network_graph;
                let logger = test_utils::TestLogger::new();
-               let scorer = test_utils::TestScorer::with_penalty(0);
+               let scorer = test_utils::TestScorer::new();
                let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
                let route = find_route(
                        &nodes[0].node.get_our_node_id(), &params, &network_graph,
@@ -1170,7 +1128,7 @@ mod test {
                create_unannounced_chan_between_nodes_with_value(&nodes, 0, 2, 100000, 10001);
 
                let payment_amt = 20_000;
-               let (payment_hash, _payment_secret) = nodes[1].node.create_inbound_payment(Some(payment_amt), 3600).unwrap();
+               let (payment_hash, _payment_secret) = nodes[1].node.create_inbound_payment(Some(payment_amt), 3600, None).unwrap();
                let route_hints = vec![
                        nodes[1].node.get_phantom_route_hints(),
                        nodes[2].node.get_phantom_route_hints(),
@@ -1179,7 +1137,7 @@ mod test {
                let invoice = crate::utils::create_phantom_invoice::<&test_utils::TestKeysInterface,
                        &test_utils::TestKeysInterface, &test_utils::TestLogger>(Some(payment_amt), Some(payment_hash),
                                "test".to_string(), 3600, route_hints, &nodes[1].keys_manager, &nodes[1].keys_manager,
-                               &nodes[1].logger, Currency::BitcoinTestnet, None).unwrap();
+                               &nodes[1].logger, Currency::BitcoinTestnet, None, Duration::from_secs(1234567)).unwrap();
 
                let chan_0_1 = &nodes[1].node.list_usable_channels()[0];
                assert_eq!(invoice.route_hints()[0].0[0].htlc_minimum_msat, chan_0_1.inbound_htlc_minimum_msat);
@@ -1211,7 +1169,7 @@ mod test {
                >(
                        Some(payment_amt), None, non_default_invoice_expiry_secs, description_hash,
                        route_hints, &nodes[1].keys_manager, &nodes[1].keys_manager, &nodes[1].logger,
-                       Currency::BitcoinTestnet, None,
+                       Currency::BitcoinTestnet, None, Duration::from_secs(1234567),
                )
                .unwrap();
                assert_eq!(invoice.amount_pico_btc(), Some(200_000));
@@ -1237,10 +1195,11 @@ mod test {
                let payment_hash = Some(PaymentHash(Sha256::hash(&user_payment_preimage.0[..]).into_inner()));
                let non_default_invoice_expiry_secs = 4200;
                let min_final_cltv_expiry_delta = Some(100);
+               let duration_since_epoch = Duration::from_secs(1234567);
                let invoice = crate::utils::create_phantom_invoice::<&test_utils::TestKeysInterface,
                        &test_utils::TestKeysInterface, &test_utils::TestLogger>(Some(payment_amt), payment_hash,
                                "".to_string(), non_default_invoice_expiry_secs, route_hints, &nodes[1].keys_manager, &nodes[1].keys_manager,
-                               &nodes[1].logger, Currency::BitcoinTestnet, min_final_cltv_expiry_delta).unwrap();
+                               &nodes[1].logger, Currency::BitcoinTestnet, min_final_cltv_expiry_delta, duration_since_epoch).unwrap();
                assert_eq!(invoice.amount_pico_btc(), Some(200_000));
                assert_eq!(invoice.min_final_cltv_expiry_delta(), (min_final_cltv_expiry_delta.unwrap() + 3) as u64);
                assert_eq!(invoice.expiry_time(), Duration::from_secs(non_default_invoice_expiry_secs.into()));
@@ -1554,7 +1513,7 @@ mod test {
                let invoice = crate::utils::create_phantom_invoice::<&test_utils::TestKeysInterface,
                        &test_utils::TestKeysInterface, &test_utils::TestLogger>(invoice_amt, None, "test".to_string(),
                                3600, phantom_route_hints, &invoice_node.keys_manager, &invoice_node.keys_manager,
-                               &invoice_node.logger, Currency::BitcoinTestnet, None).unwrap();
+                               &invoice_node.logger, Currency::BitcoinTestnet, None, Duration::from_secs(1234567)).unwrap();
 
                let invoice_hints = invoice.private_routes();