Skip to content

Commit 3170ac9

Browse files
committed
Add expiry to inbound payment util functions
1 parent 03f6550 commit 3170ac9

File tree

1 file changed

+73
-43
lines changed

1 file changed

+73
-43
lines changed

lightning-invoice/src/utils.rs

+73-43
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
1313
use lightning::ln::channelmanager::{ChannelDetails, ChannelManager, PaymentId, PaymentSendFailure, MIN_FINAL_CLTV_EXPIRY};
1414
#[cfg(feature = "std")]
1515
use lightning::ln::channelmanager::{PhantomRouteHints, MIN_CLTV_EXPIRY_DELTA};
16+
use lightning::ln::inbound_payment::{create, create_from_hash, ExpandedKey};
1617
use lightning::ln::msgs::LightningError;
1718
use lightning::routing::scoring::Score;
1819
use lightning::routing::network_graph::{NetworkGraph, RoutingFees};
@@ -38,29 +39,30 @@ use sync::Mutex;
3839
/// may be too long for QR code scanning. To fix this, `PhantomRouteHints::channels` may be pared
3940
/// down
4041
///
41-
/// `payment_hash` and `payment_secret` can come from [`ChannelManager::create_inbound_payment`] or
42-
/// [`ChannelManager::create_inbound_payment_for_hash`]. These values can be retrieved from any
43-
/// participating node. Alternatively, [`inbound_payment::create`] or
44-
/// [`inbound_payment::create_from_hash`] may be used to retrieve these values without a
45-
/// `ChannelManager`.
42+
/// `payment_hash` can be specified if you have a specific need for a custom payment hash (see the difference
43+
/// between [`ChannelManager::create_inbound_payment`] and [`ChannelManager::create_inbound_payment_for_hash`]).
44+
/// This value can be retrieved from any participating node. If `None` is provided for `payment_hash`, then one will be created.
45+
///
46+
/// `invoice_expiry_delta_secs` describes the number of seconds that the invoice is valid for
47+
/// in excess of the current time.
4648
///
4749
/// Note that the provided `keys_manager`'s `KeysInterface` implementation must support phantom
4850
/// invoices in its `sign_invoice` implementation ([`PhantomKeysManager`] satisfies this
4951
/// requirement).
5052
///
5153
/// [`PhantomKeysManager`]: lightning::chain::keysinterface::PhantomKeysManager
5254
/// [`ChannelManager::get_phantom_route_hints`]: lightning::ln::channelmanager::ChannelManager::get_phantom_route_hints
53-
/// [`inbound_payment::create`]: lightning::ln::inbound_payment::create
54-
/// [`inbound_payment::create_from_hash`]: lightning::ln::inbound_payment::create_from_hash
55+
/// [`ChannelManager::create_inbound_payment`]: lightning::ln::channelmanager::ChannelManager::create_inbound_payment
56+
/// [`ChannelManager::create_inbound_payment_for_hash`]: lightning::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash
5557
/// [`PhantomRouteHints::channels`]: lightning::ln::channelmanager::PhantomRouteHints::channels
5658
pub fn create_phantom_invoice<Signer: Sign, K: Deref>(
57-
amt_msat: Option<u64>, description: String, payment_hash: PaymentHash, payment_secret: PaymentSecret,
59+
amt_msat: Option<u64>, payment_hash: Option<PaymentHash>, description: String, invoice_expiry_delta_secs: u32,
5860
phantom_route_hints: Vec<PhantomRouteHints>, keys_manager: K, network: Currency,
5961
) -> Result<Invoice, SignOrCreationError<()>> where K::Target: KeysInterface {
6062
let description = Description::new(description).map_err(SignOrCreationError::CreationError)?;
6163
let description = InvoiceDescription::Direct(&description,);
6264
_create_phantom_invoice::<Signer, K>(
63-
amt_msat, description, payment_hash, payment_secret, phantom_route_hints, keys_manager, network,
65+
amt_msat, payment_hash, description, invoice_expiry_delta_secs, phantom_route_hints, keys_manager, network,
6466
)
6567
}
6668

@@ -80,42 +82,41 @@ pub fn create_phantom_invoice<Signer: Sign, K: Deref>(
8082
///
8183
/// `description_hash` is a SHA-256 hash of the description text
8284
///
83-
/// `payment_hash` and `payment_secret` can come from [`ChannelManager::create_inbound_payment`] or
84-
/// [`ChannelManager::create_inbound_payment_for_hash`]. These values can be retrieved from any
85-
/// participating node. Alternatively, [`inbound_payment::create`] or
86-
/// [`inbound_payment::create_from_hash`] may be used to retrieve these values without a
87-
/// `ChannelManager`.
85+
/// `payment_hash` can be specified if you have a specific need for a custom payment hash (see the difference
86+
/// between [`ChannelManager::create_inbound_payment`] and [`ChannelManager::create_inbound_payment_for_hash`]).
87+
/// This value can be retrieved from any participating node. If `None` is provided for `payment_hash`, then one will be created.
88+
///
89+
/// `invoice_expiry_delta_secs` describes the number of seconds that the invoice is valid for
90+
/// in excess of the current time.
8891
///
8992
/// Note that the provided `keys_manager`'s `KeysInterface` implementation must support phantom
9093
/// invoices in its `sign_invoice` implementation ([`PhantomKeysManager`] satisfies this
9194
/// requirement).
9295
///
9396
/// [`PhantomKeysManager`]: lightning::chain::keysinterface::PhantomKeysManager
9497
/// [`ChannelManager::get_phantom_route_hints`]: lightning::ln::channelmanager::ChannelManager::get_phantom_route_hints
95-
/// [`inbound_payment::create`]: lightning::ln::inbound_payment::create
96-
/// [`inbound_payment::create_from_hash`]: lightning::ln::inbound_payment::create_from_hash
98+
/// [`ChannelManager::create_inbound_payment`]: lightning::ln::channelmanager::ChannelManager::create_inbound_payment
99+
/// [`ChannelManager::create_inbound_payment_for_hash`]: lightning::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash
97100
/// [`PhantomRouteHints::channels`]: lightning::ln::channelmanager::PhantomRouteHints::channels
98101
pub fn create_phantom_invoice_with_description_hash<Signer: Sign, K: Deref>(
99-
amt_msat: Option<u64>, description_hash: Sha256, payment_hash: PaymentHash,
100-
payment_secret: PaymentSecret, phantom_route_hints: Vec<PhantomRouteHints>,
101-
keys_manager: K, network: Currency,
102+
amt_msat: Option<u64>, payment_hash: Option<PaymentHash>, invoice_expiry_delta_secs: u32,
103+
description_hash: Sha256, phantom_route_hints: Vec<PhantomRouteHints>, keys_manager: K, network: Currency,
102104
) -> Result<Invoice, SignOrCreationError<()>> where K::Target: KeysInterface
103105
{
104-
105106
_create_phantom_invoice::<Signer, K>(
106-
amt_msat,
107-
InvoiceDescription::Hash(&description_hash),
108-
payment_hash, payment_secret, phantom_route_hints, keys_manager, network,
107+
amt_msat, payment_hash, InvoiceDescription::Hash(&description_hash),
108+
invoice_expiry_delta_secs, phantom_route_hints, keys_manager, network,
109109
)
110110
}
111111

112112
#[cfg(feature = "std")]
113113
fn _create_phantom_invoice<Signer: Sign, K: Deref>(
114-
amt_msat: Option<u64>, description: InvoiceDescription, payment_hash: PaymentHash,
115-
payment_secret: PaymentSecret, phantom_route_hints: Vec<PhantomRouteHints>,
116-
keys_manager: K, network: Currency,
114+
amt_msat: Option<u64>, payment_hash: Option<PaymentHash>, description: InvoiceDescription,
115+
invoice_expiry_delta_secs: u32, phantom_route_hints: Vec<PhantomRouteHints>, keys_manager: K, network: Currency,
117116
) -> Result<Invoice, SignOrCreationError<()>> where K::Target: KeysInterface
118117
{
118+
use std::time::{SystemTime, UNIX_EPOCH};
119+
119120
if phantom_route_hints.len() == 0 {
120121
return Err(SignOrCreationError::CreationError(
121122
CreationError::MissingRouteHints,
@@ -128,6 +129,35 @@ fn _create_phantom_invoice<Signer: Sign, K: Deref>(
128129
InvoiceDescription::Hash(hash) => InvoiceBuilder::new(network).description_hash(hash.0),
129130
};
130131

132+
// If we ever see performance here being too slow then we should probably take this ExpandedKey as a parameter instead.
133+
let keys = ExpandedKey::new(&keys_manager.get_inbound_payment_key_material());
134+
let (payment_hash, payment_secret) = if let Some(payment_hash) = payment_hash {
135+
let payment_secret = create_from_hash(
136+
&keys,
137+
amt_msat,
138+
payment_hash,
139+
invoice_expiry_delta_secs,
140+
SystemTime::now()
141+
.duration_since(UNIX_EPOCH)
142+
.expect("Time must be > 1970")
143+
.as_secs(),
144+
)
145+
.map_err(|_| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
146+
(payment_hash, payment_secret)
147+
} else {
148+
create(
149+
&keys,
150+
amt_msat,
151+
invoice_expiry_delta_secs,
152+
&keys_manager,
153+
SystemTime::now()
154+
.duration_since(UNIX_EPOCH)
155+
.expect("Time must be > 1970")
156+
.as_secs(),
157+
)
158+
.map_err(|_| SignOrCreationError::CreationError(CreationError::InvalidAmount))?
159+
};
160+
131161
let mut invoice = invoice
132162
.current_timestamp()
133163
.payment_hash(Hash::from_slice(&payment_hash.0).unwrap())
@@ -755,23 +785,25 @@ mod test {
755785
nodes[2].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_2.0);
756786

757787
let payment_amt = 10_000;
758-
let (payment_preimage, payment_hash, payment_secret) = {
759-
if user_generated_pmt_hash {
760-
let payment_preimage = PaymentPreimage([1; 32]);
761-
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).into_inner());
762-
let payment_secret = nodes[1].node.create_inbound_payment_for_hash(payment_hash, Some(payment_amt), 3600).unwrap();
763-
(payment_preimage, payment_hash, payment_secret)
764-
} else {
765-
let (payment_hash, payment_secret) = nodes[1].node.create_inbound_payment(Some(payment_amt), 3600).unwrap();
766-
let payment_preimage = nodes[1].node.get_payment_preimage(payment_hash, payment_secret).unwrap();
767-
(payment_preimage, payment_hash, payment_secret)
768-
}
769-
};
770788
let route_hints = vec![
771789
nodes[1].node.get_phantom_route_hints(),
772790
nodes[2].node.get_phantom_route_hints(),
773791
];
774-
let invoice = ::utils::create_phantom_invoice::<EnforcingSigner, &test_utils::TestKeysInterface>(Some(payment_amt), "test".to_string(), payment_hash, payment_secret, route_hints, &nodes[1].keys_manager, Currency::BitcoinTestnet).unwrap();
792+
793+
let user_payment_preimage = PaymentPreimage([1; 32]);
794+
let payment_hash = if user_generated_pmt_hash {
795+
Some(PaymentHash(Sha256::hash(&user_payment_preimage.0[..]).into_inner()))
796+
} else {
797+
None
798+
};
799+
800+
let invoice = ::utils::create_phantom_invoice::<EnforcingSigner, &test_utils::TestKeysInterface>(Some(payment_amt), payment_hash, "test".to_string(), 3600, route_hints, &nodes[1].keys_manager, Currency::BitcoinTestnet).unwrap();
801+
let (payment_hash, payment_secret) = (PaymentHash(invoice.payment_hash().into_inner()), *invoice.payment_secret());
802+
let payment_preimage = if user_generated_pmt_hash {
803+
user_payment_preimage
804+
} else {
805+
nodes[1].node.get_payment_preimage(payment_hash, payment_secret).unwrap()
806+
};
775807

776808
assert_eq!(invoice.min_final_cltv_expiry(), MIN_FINAL_CLTV_EXPIRY as u64);
777809
assert_eq!(invoice.description(), InvoiceDescription::Direct(&Description("test".to_string())));
@@ -856,14 +888,13 @@ mod test {
856888
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
857889

858890
let payment_amt = 20_000;
859-
let (payment_hash, payment_secret) = nodes[1].node.create_inbound_payment(Some(payment_amt), 3600).unwrap();
860891
let route_hints = vec![
861892
nodes[1].node.get_phantom_route_hints(),
862893
nodes[2].node.get_phantom_route_hints(),
863894
];
864895

865896
let description_hash = crate::Sha256(Hash::hash("Description hash phantom invoice".as_bytes()));
866-
let invoice = ::utils::create_phantom_invoice_with_description_hash::<EnforcingSigner,&test_utils::TestKeysInterface>(Some(payment_amt), description_hash, payment_hash, payment_secret, route_hints, &nodes[1].keys_manager, Currency::BitcoinTestnet).unwrap();
897+
let invoice = ::utils::create_phantom_invoice_with_description_hash::<EnforcingSigner,&test_utils::TestKeysInterface>(Some(payment_amt), None, 3600, description_hash, route_hints, &nodes[1].keys_manager, Currency::BitcoinTestnet).unwrap();
867898

868899
assert_eq!(invoice.amount_pico_btc(), Some(200_000));
869900
assert_eq!(invoice.min_final_cltv_expiry(), MIN_FINAL_CLTV_EXPIRY as u64);
@@ -1166,15 +1197,14 @@ mod test {
11661197
mut chan_ids_to_match: HashSet<u64>,
11671198
nodes_contains_public_channels: bool
11681199
){
1169-
let (payment_hash, payment_secret) = invoice_node.node.create_inbound_payment(invoice_amt, 3600).unwrap();
11701200
let phantom_route_hints = network_multi_nodes.iter()
11711201
.map(|node| node.node.get_phantom_route_hints())
11721202
.collect::<Vec<PhantomRouteHints>>();
11731203
let phantom_scids = phantom_route_hints.iter()
11741204
.map(|route_hint| route_hint.phantom_scid)
11751205
.collect::<HashSet<u64>>();
11761206

1177-
let invoice = ::utils::create_phantom_invoice::<EnforcingSigner, &test_utils::TestKeysInterface>(invoice_amt, "test".to_string(), payment_hash, payment_secret, phantom_route_hints, &invoice_node.keys_manager, Currency::BitcoinTestnet).unwrap();
1207+
let invoice = ::utils::create_phantom_invoice::<EnforcingSigner, &test_utils::TestKeysInterface>(invoice_amt, None, "test".to_string(), 3600, phantom_route_hints, &invoice_node.keys_manager, Currency::BitcoinTestnet).unwrap();
11781208

11791209
let invoice_hints = invoice.private_routes();
11801210

0 commit comments

Comments
 (0)