Skip to content

Commit 5b53670

Browse files
committed
Add new payment type and metadata bytes
Adds two new payment `Method`s for identifying payments with custom `min_final_cltv_expiry_delta` as payments with LDK or user payment hashes. The `min_final_cltv_expiry_delta` value is packed into the first 2 bytes of the expiry timestamp in the payment secret metadata.
1 parent 1d72e87 commit 5b53670

File tree

9 files changed

+210
-46
lines changed

9 files changed

+210
-46
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ fn get_payment_secret_hash(dest: &ChanMan, payment_id: &mut u8) -> Option<(Payme
340340
let mut payment_hash;
341341
for _ in 0..256 {
342342
payment_hash = PaymentHash(Sha256::hash(&[*payment_id; 1]).into_inner());
343-
if let Ok(payment_secret) = dest.create_inbound_payment_for_hash(payment_hash, None, 3600) {
343+
if let Ok(payment_secret) = dest.create_inbound_payment_for_hash(payment_hash, None, 3600, None) {
344344
return Some((payment_secret, payment_hash));
345345
}
346346
*payment_id = payment_id.wrapping_add(1);

fuzz/src/full_stack.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,7 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
605605
let payment_hash = PaymentHash(Sha256::from_engine(sha).into_inner());
606606
// Note that this may fail - our hashes may collide and we'll end up trying to
607607
// double-register the same payment_hash.
608-
let _ = channelmanager.create_inbound_payment_for_hash(payment_hash, None, 1);
608+
let _ = channelmanager.create_inbound_payment_for_hash(payment_hash, None, 1, None);
609609
},
610610
9 => {
611611
for payment in payments_received.drain(..) {

lightning-invoice/src/utils.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ where
166166
.duration_since(UNIX_EPOCH)
167167
.expect("Time must be > 1970")
168168
.as_secs(),
169+
min_final_cltv_expiry_delta,
169170
)
170171
.map_err(|_| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
171172
(payment_hash, payment_secret)
@@ -179,6 +180,7 @@ where
179180
.duration_since(UNIX_EPOCH)
180181
.expect("Time must be > 1970")
181182
.as_secs(),
183+
min_final_cltv_expiry_delta,
182184
)
183185
.map_err(|_| SignOrCreationError::CreationError(CreationError::InvalidAmount))?
184186
};
@@ -397,7 +399,7 @@ fn _create_invoice_from_channelmanager_and_duration_since_epoch<M: Deref, T: Der
397399
// `create_inbound_payment` only returns an error if the amount is greater than the total bitcoin
398400
// supply.
399401
let (payment_hash, payment_secret) = channelmanager
400-
.create_inbound_payment(amt_msat, invoice_expiry_delta_secs)
402+
.create_inbound_payment(amt_msat, invoice_expiry_delta_secs, min_final_cltv_expiry_delta)
401403
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
402404
_create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash(
403405
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_
424426
L::Target: Logger,
425427
{
426428
let payment_secret = channelmanager
427-
.create_inbound_payment_for_hash(payment_hash,amt_msat, invoice_expiry_delta_secs)
429+
.create_inbound_payment_for_hash(payment_hash, amt_msat, invoice_expiry_delta_secs,
430+
min_final_cltv_expiry_delta)
428431
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
429432
_create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash(
430433
channelmanager, node_signer, logger, network, amt_msat,
@@ -1170,7 +1173,7 @@ mod test {
11701173
create_unannounced_chan_between_nodes_with_value(&nodes, 0, 2, 100000, 10001);
11711174

11721175
let payment_amt = 20_000;
1173-
let (payment_hash, _payment_secret) = nodes[1].node.create_inbound_payment(Some(payment_amt), 3600).unwrap();
1176+
let (payment_hash, _payment_secret) = nodes[1].node.create_inbound_payment(Some(payment_amt), 3600, None).unwrap();
11741177
let route_hints = vec![
11751178
nodes[1].node.get_phantom_route_hints(),
11761179
nodes[2].node.get_phantom_route_hints(),

lightning/src/ln/channelmanager.rs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1914,6 +1914,7 @@ where
19141914
// final_expiry_too_soon
19151915
// We have to have some headroom to broadcast on chain if we have the preimage, so make sure
19161916
// we have at least HTLC_FAIL_BACK_BUFFER blocks to go.
1917+
//
19171918
// Also, ensure that, in the case of an unknown preimage for the received payment hash, our
19181919
// payment logic has enough time to fail the HTLC backward before our onchain logic triggers a
19191920
// channel closure (see HTLC_FAIL_BACK_BUFFER rationale).
@@ -3181,13 +3182,23 @@ where
31813182
match claimable_htlc.onion_payload {
31823183
OnionPayload::Invoice { .. } => {
31833184
let payment_data = payment_data.unwrap();
3184-
let payment_preimage = match inbound_payment::verify(payment_hash, &payment_data, self.highest_seen_timestamp.load(Ordering::Acquire) as u64, &self.inbound_payment_key, &self.logger) {
3185-
Ok(payment_preimage) => payment_preimage,
3185+
let (payment_preimage, min_final_cltv_expiry_delta) = match inbound_payment::verify(payment_hash, &payment_data, self.highest_seen_timestamp.load(Ordering::Acquire) as u64, &self.inbound_payment_key, &self.logger) {
3186+
Ok(result) => result,
31863187
Err(()) => {
3188+
log_trace!(self.logger, "Failing new HTLC with payment_hash {} as payment verification failed", log_bytes!(payment_hash.0));
31873189
fail_htlc!(claimable_htlc, payment_hash);
31883190
continue
31893191
}
31903192
};
3193+
if let Some(min_final_cltv_expiry_delta) = min_final_cltv_expiry_delta {
3194+
let expected_min_expiry_height = (self.current_best_block().height() + min_final_cltv_expiry_delta as u32) as u64;
3195+
if (cltv_expiry as u64) < expected_min_expiry_height {
3196+
log_trace!(self.logger, "Failing new HTLC with payment_hash {} as its CLTV expiry was too soon (had {}, earliest expected {})",
3197+
log_bytes!(payment_hash.0), cltv_expiry, expected_min_expiry_height);
3198+
fail_htlc!(claimable_htlc, payment_hash);
3199+
continue;
3200+
}
3201+
}
31913202
check_total_value!(payment_data, payment_preimage);
31923203
},
31933204
OnionPayload::Spontaneous(preimage) => {
@@ -5273,12 +5284,18 @@ where
52735284
///
52745285
/// Errors if `min_value_msat` is greater than total bitcoin supply.
52755286
///
5287+
/// If `min_final_cltv_expiry_delta` is set to some value, then the payment will not be receivable
5288+
/// on versions of LDK prior to 0.0.114.
5289+
///
52765290
/// [`claim_funds`]: Self::claim_funds
52775291
/// [`PaymentClaimable`]: events::Event::PaymentClaimable
52785292
/// [`PaymentClaimable::payment_preimage`]: events::Event::PaymentClaimable::payment_preimage
52795293
/// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
5280-
pub fn create_inbound_payment(&self, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32) -> Result<(PaymentHash, PaymentSecret), ()> {
5281-
inbound_payment::create(&self.inbound_payment_key, min_value_msat, invoice_expiry_delta_secs, &self.entropy_source, self.highest_seen_timestamp.load(Ordering::Acquire) as u64)
5294+
pub fn create_inbound_payment(&self, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32,
5295+
min_final_cltv_expiry_delta: Option<u16>) -> Result<(PaymentHash, PaymentSecret), ()> {
5296+
inbound_payment::create(&self.inbound_payment_key, min_value_msat, invoice_expiry_delta_secs,
5297+
&self.entropy_source, self.highest_seen_timestamp.load(Ordering::Acquire) as u64,
5298+
min_final_cltv_expiry_delta)
52825299
}
52835300

52845301
/// Legacy version of [`create_inbound_payment`]. Use this method if you wish to share
@@ -5339,10 +5356,16 @@ where
53395356
///
53405357
/// Errors if `min_value_msat` is greater than total bitcoin supply.
53415358
///
5359+
/// If `min_final_cltv_expiry_delta` is set to some value, then the payment will not be receivable
5360+
/// on versions of LDK prior to 0.0.114.
5361+
///
53425362
/// [`create_inbound_payment`]: Self::create_inbound_payment
53435363
/// [`PaymentClaimable`]: events::Event::PaymentClaimable
5344-
pub fn create_inbound_payment_for_hash(&self, payment_hash: PaymentHash, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32) -> Result<PaymentSecret, ()> {
5345-
inbound_payment::create_from_hash(&self.inbound_payment_key, min_value_msat, payment_hash, invoice_expiry_delta_secs, self.highest_seen_timestamp.load(Ordering::Acquire) as u64)
5364+
pub fn create_inbound_payment_for_hash(&self, payment_hash: PaymentHash, min_value_msat: Option<u64>,
5365+
invoice_expiry_delta_secs: u32, min_final_cltv_expiry: Option<u16>) -> Result<PaymentSecret, ()> {
5366+
inbound_payment::create_from_hash(&self.inbound_payment_key, min_value_msat, payment_hash,
5367+
invoice_expiry_delta_secs, self.highest_seen_timestamp.load(Ordering::Acquire) as u64,
5368+
min_final_cltv_expiry)
53465369
}
53475370

53485371
/// Legacy version of [`create_inbound_payment_for_hash`]. Use this method if you wish to share
@@ -7369,7 +7392,7 @@ where
73697392
payment_preimage: match pending_inbound_payments.get(&payment_hash) {
73707393
Some(inbound_payment) => inbound_payment.payment_preimage,
73717394
None => match inbound_payment::verify(payment_hash, &hop_data, 0, &expanded_inbound_key, &args.logger) {
7372-
Ok(payment_preimage) => payment_preimage,
7395+
Ok((payment_preimage, _)) => payment_preimage,
73737396
Err(()) => {
73747397
log_error!(args.logger, "Failed to read claimable payment data for HTLC with payment hash {} - was not a pending inbound payment and didn't match our payment key", log_bytes!(payment_hash.0));
73757398
return Err(DecodeError::InvalidValue);
@@ -8505,7 +8528,7 @@ pub mod bench {
85058528
payment_preimage.0[0..8].copy_from_slice(&payment_count.to_le_bytes());
85068529
payment_count += 1;
85078530
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).into_inner());
8508-
let payment_secret = $node_b.create_inbound_payment_for_hash(payment_hash, None, 7200).unwrap();
8531+
let payment_secret = $node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None).unwrap();
85098532

85108533
$node_a.send_payment(&route, payment_hash, &Some(payment_secret), PaymentId(payment_hash.0)).unwrap();
85118534
let payment_event = SendEvent::from_event($node_a.get_and_clear_pending_msg_events().pop().unwrap());

lightning/src/ln/functional_test_utils.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1447,17 +1447,22 @@ macro_rules! get_payment_preimage_hash {
14471447
}
14481448
};
14491449
($dest_node: expr, $min_value_msat: expr) => {
1450+
{
1451+
crate::get_payment_preimage_hash!($dest_node, $min_value_msat, None)
1452+
}
1453+
};
1454+
($dest_node: expr, $min_value_msat: expr, $min_final_cltv_expiry_delta: expr) => {
14501455
{
14511456
use bitcoin::hashes::Hash as _;
14521457
let mut payment_count = $dest_node.network_payment_count.borrow_mut();
14531458
let payment_preimage = $crate::ln::PaymentPreimage([*payment_count; 32]);
14541459
*payment_count += 1;
14551460
let payment_hash = $crate::ln::PaymentHash(
14561461
bitcoin::hashes::sha256::Hash::hash(&payment_preimage.0[..]).into_inner());
1457-
let payment_secret = $dest_node.node.create_inbound_payment_for_hash(payment_hash, $min_value_msat, 7200).unwrap();
1462+
let payment_secret = $dest_node.node.create_inbound_payment_for_hash(payment_hash, $min_value_msat, 7200, $min_final_cltv_expiry_delta).unwrap();
14581463
(payment_preimage, payment_hash, payment_secret)
14591464
}
1460-
}
1465+
};
14611466
}
14621467

14631468
#[macro_export]

0 commit comments

Comments
 (0)