//! 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;
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.
///
///
/// `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.
/// [`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,
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.
///
///
/// `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
/// [`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,
_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(
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,
)
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,
)
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(
.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;
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 {
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,
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 {
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 {
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(), ¶ms, &network_graph,
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);
>(
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));
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()));
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();