Skip to content

Commit 2d954c4

Browse files
committed
Add expiry to inbound payment util functions
1 parent 03f6550 commit 2d954c4

File tree

1 file changed

+67
-41
lines changed

1 file changed

+67
-41
lines changed

lightning-invoice/src/utils.rs

Lines changed: 67 additions & 41 deletions
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,28 @@ 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+
/// `payment_hash` can come from [`ChannelManager::create_inbound_payment`] or
4243
/// [`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`.
44+
/// 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
5555
/// [`PhantomRouteHints::channels`]: lightning::ln::channelmanager::PhantomRouteHints::channels
5656
pub fn create_phantom_invoice<Signer: Sign, K: Deref>(
57-
amt_msat: Option<u64>, description: String, payment_hash: PaymentHash, payment_secret: PaymentSecret,
57+
amt_msat: Option<u64>, payment_hash: Option<PaymentHash>, description: String, invoice_expiry_delta_secs: u32,
5858
phantom_route_hints: Vec<PhantomRouteHints>, keys_manager: K, network: Currency,
5959
) -> Result<Invoice, SignOrCreationError<()>> where K::Target: KeysInterface {
6060
let description = Description::new(description).map_err(SignOrCreationError::CreationError)?;
6161
let description = InvoiceDescription::Direct(&description,);
6262
_create_phantom_invoice::<Signer, K>(
63-
amt_msat, description, payment_hash, payment_secret, phantom_route_hints, keys_manager, network,
63+
amt_msat, payment_hash, description, invoice_expiry_delta_secs, phantom_route_hints, keys_manager, network,
6464
)
6565
}
6666

@@ -80,42 +80,39 @@ pub fn create_phantom_invoice<Signer: Sign, K: Deref>(
8080
///
8181
/// `description_hash` is a SHA-256 hash of the description text
8282
///
83-
/// `payment_hash` and `payment_secret` can come from [`ChannelManager::create_inbound_payment`] or
83+
/// `payment_hash` can come from [`ChannelManager::create_inbound_payment`] or
8484
/// [`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+
/// participating node. If `None` is provided for `payment_hash`, then one will be created.
86+
///
87+
/// `invoice_expiry_delta_secs` describes the number of seconds that the invoice is valid for
88+
/// in excess of the current time.
8889
///
8990
/// Note that the provided `keys_manager`'s `KeysInterface` implementation must support phantom
9091
/// invoices in its `sign_invoice` implementation ([`PhantomKeysManager`] satisfies this
9192
/// requirement).
9293
///
9394
/// [`PhantomKeysManager`]: lightning::chain::keysinterface::PhantomKeysManager
9495
/// [`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
9796
/// [`PhantomRouteHints::channels`]: lightning::ln::channelmanager::PhantomRouteHints::channels
9897
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,
98+
amt_msat: Option<u64>, payment_hash: Option<PaymentHash>, invoice_expiry_delta_secs: u32,
99+
description_hash: Sha256, phantom_route_hints: Vec<PhantomRouteHints>, keys_manager: K, network: Currency,
102100
) -> Result<Invoice, SignOrCreationError<()>> where K::Target: KeysInterface
103101
{
104-
105102
_create_phantom_invoice::<Signer, K>(
106-
amt_msat,
107-
InvoiceDescription::Hash(&description_hash),
108-
payment_hash, payment_secret, phantom_route_hints, keys_manager, network,
103+
amt_msat, payment_hash, InvoiceDescription::Hash(&description_hash),
104+
invoice_expiry_delta_secs, phantom_route_hints, keys_manager, network,
109105
)
110106
}
111107

112108
#[cfg(feature = "std")]
113109
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,
110+
amt_msat: Option<u64>, payment_hash: Option<PaymentHash>, description: InvoiceDescription,
111+
invoice_expiry_delta_secs: u32, phantom_route_hints: Vec<PhantomRouteHints>, keys_manager: K, network: Currency,
117112
) -> Result<Invoice, SignOrCreationError<()>> where K::Target: KeysInterface
118113
{
114+
use std::time::{SystemTime, UNIX_EPOCH};
115+
119116
if phantom_route_hints.len() == 0 {
120117
return Err(SignOrCreationError::CreationError(
121118
CreationError::MissingRouteHints,
@@ -128,6 +125,34 @@ fn _create_phantom_invoice<Signer: Sign, K: Deref>(
128125
InvoiceDescription::Hash(hash) => InvoiceBuilder::new(network).description_hash(hash.0),
129126
};
130127

128+
let keys = ExpandedKey::new(&keys_manager.get_inbound_payment_key_material());
129+
let (payment_hash, payment_secret) = if let Some(payment_hash) = payment_hash {
130+
let payment_secret = create_from_hash(
131+
&keys,
132+
amt_msat,
133+
payment_hash,
134+
invoice_expiry_delta_secs,
135+
SystemTime::now()
136+
.duration_since(UNIX_EPOCH)
137+
.expect("Time must be > 1970")
138+
.as_secs(),
139+
)
140+
.map_err(|_| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
141+
(payment_hash, payment_secret)
142+
} else {
143+
create(
144+
&keys,
145+
amt_msat,
146+
invoice_expiry_delta_secs,
147+
&keys_manager,
148+
SystemTime::now()
149+
.duration_since(UNIX_EPOCH)
150+
.expect("Time must be > 1970")
151+
.as_secs(),
152+
)
153+
.map_err(|_| SignOrCreationError::CreationError(CreationError::InvalidAmount))?
154+
};
155+
131156
let mut invoice = invoice
132157
.current_timestamp()
133158
.payment_hash(Hash::from_slice(&payment_hash.0).unwrap())
@@ -755,23 +780,25 @@ mod test {
755780
nodes[2].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_2.0);
756781

757782
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-
};
770783
let route_hints = vec![
771784
nodes[1].node.get_phantom_route_hints(),
772785
nodes[2].node.get_phantom_route_hints(),
773786
];
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();
787+
788+
let user_payment_preimage = PaymentPreimage([1; 32]);
789+
let payment_hash = if user_generated_pmt_hash {
790+
Some(PaymentHash(Sha256::hash(&user_payment_preimage.0[..]).into_inner()))
791+
} else {
792+
None
793+
};
794+
795+
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();
796+
let (payment_hash, payment_secret) = (PaymentHash(invoice.payment_hash().into_inner()), *invoice.payment_secret());
797+
let payment_preimage = if user_generated_pmt_hash {
798+
user_payment_preimage
799+
} else {
800+
nodes[1].node.get_payment_preimage(payment_hash, payment_secret).unwrap()
801+
};
775802

776803
assert_eq!(invoice.min_final_cltv_expiry(), MIN_FINAL_CLTV_EXPIRY as u64);
777804
assert_eq!(invoice.description(), InvoiceDescription::Direct(&Description("test".to_string())));
@@ -821,6 +848,7 @@ mod test {
821848
// Note that we have to "forward pending HTLCs" twice before we see the PaymentReceived as
822849
// this "emulates" the payment taking two hops, providing some privacy to make phantom node
823850
// payments "look real" by taking more time.
851+
824852
expect_pending_htlcs_forwardable_ignore!(nodes[fwd_idx]);
825853
nodes[fwd_idx].node.process_pending_htlc_forwards();
826854
expect_pending_htlcs_forwardable_ignore!(nodes[fwd_idx]);
@@ -856,14 +884,13 @@ mod test {
856884
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
857885

858886
let payment_amt = 20_000;
859-
let (payment_hash, payment_secret) = nodes[1].node.create_inbound_payment(Some(payment_amt), 3600).unwrap();
860887
let route_hints = vec![
861888
nodes[1].node.get_phantom_route_hints(),
862889
nodes[2].node.get_phantom_route_hints(),
863890
];
864891

865892
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();
893+
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();
867894

868895
assert_eq!(invoice.amount_pico_btc(), Some(200_000));
869896
assert_eq!(invoice.min_final_cltv_expiry(), MIN_FINAL_CLTV_EXPIRY as u64);
@@ -1166,15 +1193,14 @@ mod test {
11661193
mut chan_ids_to_match: HashSet<u64>,
11671194
nodes_contains_public_channels: bool
11681195
){
1169-
let (payment_hash, payment_secret) = invoice_node.node.create_inbound_payment(invoice_amt, 3600).unwrap();
11701196
let phantom_route_hints = network_multi_nodes.iter()
11711197
.map(|node| node.node.get_phantom_route_hints())
11721198
.collect::<Vec<PhantomRouteHints>>();
11731199
let phantom_scids = phantom_route_hints.iter()
11741200
.map(|route_hint| route_hint.phantom_scid)
11751201
.collect::<HashSet<u64>>();
11761202

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();
1203+
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();
11781204

11791205
let invoice_hints = invoice.private_routes();
11801206

0 commit comments

Comments
 (0)