Skip to content

Commit bf41004

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

File tree

1 file changed

+59
-41
lines changed

1 file changed

+59
-41
lines changed

lightning-invoice/src/utils.rs

+59-41
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,25 @@ 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.
4645
///
4746
/// Note that the provided `keys_manager`'s `KeysInterface` implementation must support phantom
4847
/// invoices in its `sign_invoice` implementation ([`PhantomKeysManager`] satisfies this
4948
/// requirement).
5049
///
5150
/// [`PhantomKeysManager`]: lightning::chain::keysinterface::PhantomKeysManager
5251
/// [`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
5552
/// [`PhantomRouteHints::channels`]: lightning::ln::channelmanager::PhantomRouteHints::channels
5653
pub fn create_phantom_invoice<Signer: Sign, K: Deref>(
57-
amt_msat: Option<u64>, description: String, payment_hash: PaymentHash, payment_secret: PaymentSecret,
54+
amt_msat: Option<u64>, payment_hash: Option<PaymentHash>, description: String, invoice_expiry_delta_secs: u32,
5855
phantom_route_hints: Vec<PhantomRouteHints>, keys_manager: K, network: Currency,
5956
) -> Result<Invoice, SignOrCreationError<()>> where K::Target: KeysInterface {
6057
let description = Description::new(description).map_err(SignOrCreationError::CreationError)?;
6158
let description = InvoiceDescription::Direct(&description,);
6259
_create_phantom_invoice::<Signer, K>(
63-
amt_msat, description, payment_hash, payment_secret, phantom_route_hints, keys_manager, network,
60+
amt_msat, payment_hash, description, invoice_expiry_delta_secs, phantom_route_hints, keys_manager, network,
6461
)
6562
}
6663

@@ -80,42 +77,34 @@ pub fn create_phantom_invoice<Signer: Sign, K: Deref>(
8077
///
8178
/// `description_hash` is a SHA-256 hash of the description text
8279
///
83-
/// `payment_hash` and `payment_secret` can come from [`ChannelManager::create_inbound_payment`] or
80+
/// `payment_hash` can come from [`ChannelManager::create_inbound_payment`] or
8481
/// [`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`.
82+
/// participating node. If `None` is provided for `payment_hash`, then one will be created.
8883
///
8984
/// Note that the provided `keys_manager`'s `KeysInterface` implementation must support phantom
9085
/// invoices in its `sign_invoice` implementation ([`PhantomKeysManager`] satisfies this
9186
/// requirement).
9287
///
9388
/// [`PhantomKeysManager`]: lightning::chain::keysinterface::PhantomKeysManager
9489
/// [`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
9790
/// [`PhantomRouteHints::channels`]: lightning::ln::channelmanager::PhantomRouteHints::channels
9891
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,
92+
amt_msat: Option<u64>, payment_hash: Option<PaymentHash>, invoice_expiry_delta_secs: u32, description_hash: Sha256, phantom_route_hints: Vec<PhantomRouteHints>, keys_manager: K, network: Currency,
10293
) -> Result<Invoice, SignOrCreationError<()>> where K::Target: KeysInterface
10394
{
104-
10595
_create_phantom_invoice::<Signer, K>(
106-
amt_msat,
107-
InvoiceDescription::Hash(&description_hash),
108-
payment_hash, payment_secret, phantom_route_hints, keys_manager, network,
96+
amt_msat, payment_hash, InvoiceDescription::Hash(&description_hash), invoice_expiry_delta_secs, phantom_route_hints, keys_manager, network,
10997
)
11098
}
11199

112100
#[cfg(feature = "std")]
113101
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,
102+
amt_msat: Option<u64>, payment_hash: Option<PaymentHash>, description: InvoiceDescription, invoice_expiry_delta_secs: u32,
103+
phantom_route_hints: Vec<PhantomRouteHints>, keys_manager: K, network: Currency,
117104
) -> Result<Invoice, SignOrCreationError<()>> where K::Target: KeysInterface
118105
{
106+
use std::time::{SystemTime, UNIX_EPOCH};
107+
119108
if phantom_route_hints.len() == 0 {
120109
return Err(SignOrCreationError::CreationError(
121110
CreationError::MissingRouteHints,
@@ -128,6 +117,34 @@ fn _create_phantom_invoice<Signer: Sign, K: Deref>(
128117
InvoiceDescription::Hash(hash) => InvoiceBuilder::new(network).description_hash(hash.0),
129118
};
130119

120+
let keys = ExpandedKey::new(&keys_manager.get_inbound_payment_key_material());
121+
let (payment_hash, payment_secret) = if let Some(payment_hash) = payment_hash {
122+
let payment_secret = create_from_hash(
123+
&keys,
124+
amt_msat,
125+
payment_hash,
126+
invoice_expiry_delta_secs,
127+
SystemTime::now()
128+
.duration_since(UNIX_EPOCH)
129+
.expect("Time must be > 1970")
130+
.as_secs(),
131+
)
132+
.map_err(|_| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
133+
(payment_hash, payment_secret)
134+
} else {
135+
create(
136+
&keys,
137+
amt_msat,
138+
invoice_expiry_delta_secs,
139+
&keys_manager,
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+
};
147+
131148
let mut invoice = invoice
132149
.current_timestamp()
133150
.payment_hash(Hash::from_slice(&payment_hash.0).unwrap())
@@ -755,23 +772,25 @@ mod test {
755772
nodes[2].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_2.0);
756773

757774
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-
};
770775
let route_hints = vec![
771776
nodes[1].node.get_phantom_route_hints(),
772777
nodes[2].node.get_phantom_route_hints(),
773778
];
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();
779+
780+
let user_payment_preimage = PaymentPreimage([1; 32]);
781+
let payment_hash = if user_generated_pmt_hash {
782+
Some(PaymentHash(Sha256::hash(&user_payment_preimage.0[..]).into_inner()))
783+
} else {
784+
None
785+
};
786+
787+
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();
788+
let (payment_hash, payment_secret) = (PaymentHash(invoice.payment_hash().into_inner()), *invoice.payment_secret());
789+
let payment_preimage = if user_generated_pmt_hash {
790+
user_payment_preimage
791+
} else {
792+
nodes[1].node.get_payment_preimage(payment_hash, payment_secret).unwrap()
793+
};
775794

776795
assert_eq!(invoice.min_final_cltv_expiry(), MIN_FINAL_CLTV_EXPIRY as u64);
777796
assert_eq!(invoice.description(), InvoiceDescription::Direct(&Description("test".to_string())));
@@ -821,6 +840,7 @@ mod test {
821840
// Note that we have to "forward pending HTLCs" twice before we see the PaymentReceived as
822841
// this "emulates" the payment taking two hops, providing some privacy to make phantom node
823842
// payments "look real" by taking more time.
843+
824844
expect_pending_htlcs_forwardable_ignore!(nodes[fwd_idx]);
825845
nodes[fwd_idx].node.process_pending_htlc_forwards();
826846
expect_pending_htlcs_forwardable_ignore!(nodes[fwd_idx]);
@@ -856,14 +876,13 @@ mod test {
856876
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
857877

858878
let payment_amt = 20_000;
859-
let (payment_hash, payment_secret) = nodes[1].node.create_inbound_payment(Some(payment_amt), 3600).unwrap();
860879
let route_hints = vec![
861880
nodes[1].node.get_phantom_route_hints(),
862881
nodes[2].node.get_phantom_route_hints(),
863882
];
864883

865884
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();
885+
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();
867886

868887
assert_eq!(invoice.amount_pico_btc(), Some(200_000));
869888
assert_eq!(invoice.min_final_cltv_expiry(), MIN_FINAL_CLTV_EXPIRY as u64);
@@ -1166,15 +1185,14 @@ mod test {
11661185
mut chan_ids_to_match: HashSet<u64>,
11671186
nodes_contains_public_channels: bool
11681187
){
1169-
let (payment_hash, payment_secret) = invoice_node.node.create_inbound_payment(invoice_amt, 3600).unwrap();
11701188
let phantom_route_hints = network_multi_nodes.iter()
11711189
.map(|node| node.node.get_phantom_route_hints())
11721190
.collect::<Vec<PhantomRouteHints>>();
11731191
let phantom_scids = phantom_route_hints.iter()
11741192
.map(|route_hint| route_hint.phantom_scid)
11751193
.collect::<HashSet<u64>>();
11761194

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();
1195+
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();
11781196

11791197
let invoice_hints = invoice.private_routes();
11801198

0 commit comments

Comments
 (0)