Skip to content

Commit

Permalink
Add new payment type and metadata bytes
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dunxen committed Jan 24, 2023
1 parent 1d72e87 commit 5b53670
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 46 deletions.
2 changes: 1 addition & 1 deletion fuzz/src/chanmon_consistency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ fn get_payment_secret_hash(dest: &ChanMan, payment_id: &mut u8) -> Option<(Payme
let mut payment_hash;
for _ in 0..256 {
payment_hash = PaymentHash(Sha256::hash(&[*payment_id; 1]).into_inner());
if let Ok(payment_secret) = dest.create_inbound_payment_for_hash(payment_hash, None, 3600) {
if let Ok(payment_secret) = dest.create_inbound_payment_for_hash(payment_hash, None, 3600, None) {
return Some((payment_secret, payment_hash));
}
*payment_id = payment_id.wrapping_add(1);
Expand Down
2 changes: 1 addition & 1 deletion fuzz/src/full_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,7 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
let payment_hash = PaymentHash(Sha256::from_engine(sha).into_inner());
// Note that this may fail - our hashes may collide and we'll end up trying to
// double-register the same payment_hash.
let _ = channelmanager.create_inbound_payment_for_hash(payment_hash, None, 1);
let _ = channelmanager.create_inbound_payment_for_hash(payment_hash, None, 1, None);
},
9 => {
for payment in payments_received.drain(..) {
Expand Down
9 changes: 6 additions & 3 deletions lightning-invoice/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ where
.duration_since(UNIX_EPOCH)
.expect("Time must be > 1970")
.as_secs(),
min_final_cltv_expiry_delta,
)
.map_err(|_| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
(payment_hash, payment_secret)
Expand All @@ -179,6 +180,7 @@ where
.duration_since(UNIX_EPOCH)
.expect("Time must be > 1970")
.as_secs(),
min_final_cltv_expiry_delta,
)
.map_err(|_| SignOrCreationError::CreationError(CreationError::InvalidAmount))?
};
Expand Down Expand Up @@ -397,7 +399,7 @@ fn _create_invoice_from_channelmanager_and_duration_since_epoch<M: Deref, T: Der
// `create_inbound_payment` only returns an error if the amount is greater than the total bitcoin
// supply.
let (payment_hash, payment_secret) = channelmanager
.create_inbound_payment(amt_msat, invoice_expiry_delta_secs)
.create_inbound_payment(amt_msat, invoice_expiry_delta_secs, min_final_cltv_expiry_delta)
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
_create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash(
channelmanager, node_signer, logger, network, amt_msat, description, duration_since_epoch,
Expand All @@ -424,7 +426,8 @@ pub fn create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_
L::Target: Logger,
{
let payment_secret = channelmanager
.create_inbound_payment_for_hash(payment_hash,amt_msat, invoice_expiry_delta_secs)
.create_inbound_payment_for_hash(payment_hash, amt_msat, invoice_expiry_delta_secs,
min_final_cltv_expiry_delta)
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
_create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash(
channelmanager, node_signer, logger, network, amt_msat,
Expand Down Expand Up @@ -1170,7 +1173,7 @@ mod test {
create_unannounced_chan_between_nodes_with_value(&nodes, 0, 2, 100000, 10001);

let payment_amt = 20_000;
let (payment_hash, _payment_secret) = nodes[1].node.create_inbound_payment(Some(payment_amt), 3600).unwrap();
let (payment_hash, _payment_secret) = nodes[1].node.create_inbound_payment(Some(payment_amt), 3600, None).unwrap();
let route_hints = vec![
nodes[1].node.get_phantom_route_hints(),
nodes[2].node.get_phantom_route_hints(),
Expand Down
39 changes: 31 additions & 8 deletions lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1914,6 +1914,7 @@ where
// final_expiry_too_soon
// We have to have some headroom to broadcast on chain if we have the preimage, so make sure
// we have at least HTLC_FAIL_BACK_BUFFER blocks to go.
//
// Also, ensure that, in the case of an unknown preimage for the received payment hash, our
// payment logic has enough time to fail the HTLC backward before our onchain logic triggers a
// channel closure (see HTLC_FAIL_BACK_BUFFER rationale).
Expand Down Expand Up @@ -3181,13 +3182,23 @@ where
match claimable_htlc.onion_payload {
OnionPayload::Invoice { .. } => {
let payment_data = payment_data.unwrap();
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) {
Ok(payment_preimage) => payment_preimage,
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) {
Ok(result) => result,
Err(()) => {
log_trace!(self.logger, "Failing new HTLC with payment_hash {} as payment verification failed", log_bytes!(payment_hash.0));
fail_htlc!(claimable_htlc, payment_hash);
continue
}
};
if let Some(min_final_cltv_expiry_delta) = min_final_cltv_expiry_delta {
let expected_min_expiry_height = (self.current_best_block().height() + min_final_cltv_expiry_delta as u32) as u64;
if (cltv_expiry as u64) < expected_min_expiry_height {
log_trace!(self.logger, "Failing new HTLC with payment_hash {} as its CLTV expiry was too soon (had {}, earliest expected {})",
log_bytes!(payment_hash.0), cltv_expiry, expected_min_expiry_height);
fail_htlc!(claimable_htlc, payment_hash);
continue;
}
}
check_total_value!(payment_data, payment_preimage);
},
OnionPayload::Spontaneous(preimage) => {
Expand Down Expand Up @@ -5273,12 +5284,18 @@ where
///
/// Errors if `min_value_msat` is greater than total bitcoin supply.
///
/// If `min_final_cltv_expiry_delta` is set to some value, then the payment will not be receivable
/// on versions of LDK prior to 0.0.114.
///
/// [`claim_funds`]: Self::claim_funds
/// [`PaymentClaimable`]: events::Event::PaymentClaimable
/// [`PaymentClaimable::payment_preimage`]: events::Event::PaymentClaimable::payment_preimage
/// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
pub fn create_inbound_payment(&self, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32) -> Result<(PaymentHash, PaymentSecret), ()> {
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)
pub fn create_inbound_payment(&self, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32,
min_final_cltv_expiry_delta: Option<u16>) -> Result<(PaymentHash, PaymentSecret), ()> {
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,
min_final_cltv_expiry_delta)
}

/// Legacy version of [`create_inbound_payment`]. Use this method if you wish to share
Expand Down Expand Up @@ -5339,10 +5356,16 @@ where
///
/// Errors if `min_value_msat` is greater than total bitcoin supply.
///
/// If `min_final_cltv_expiry_delta` is set to some value, then the payment will not be receivable
/// on versions of LDK prior to 0.0.114.
///
/// [`create_inbound_payment`]: Self::create_inbound_payment
/// [`PaymentClaimable`]: events::Event::PaymentClaimable
pub fn create_inbound_payment_for_hash(&self, payment_hash: PaymentHash, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32) -> Result<PaymentSecret, ()> {
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)
pub fn create_inbound_payment_for_hash(&self, payment_hash: PaymentHash, min_value_msat: Option<u64>,
invoice_expiry_delta_secs: u32, min_final_cltv_expiry: Option<u16>) -> Result<PaymentSecret, ()> {
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,
min_final_cltv_expiry)
}

/// Legacy version of [`create_inbound_payment_for_hash`]. Use this method if you wish to share
Expand Down Expand Up @@ -7369,7 +7392,7 @@ where
payment_preimage: match pending_inbound_payments.get(&payment_hash) {
Some(inbound_payment) => inbound_payment.payment_preimage,
None => match inbound_payment::verify(payment_hash, &hop_data, 0, &expanded_inbound_key, &args.logger) {
Ok(payment_preimage) => payment_preimage,
Ok((payment_preimage, _)) => payment_preimage,
Err(()) => {
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));
return Err(DecodeError::InvalidValue);
Expand Down Expand Up @@ -8505,7 +8528,7 @@ pub mod bench {
payment_preimage.0[0..8].copy_from_slice(&payment_count.to_le_bytes());
payment_count += 1;
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).into_inner());
let payment_secret = $node_b.create_inbound_payment_for_hash(payment_hash, None, 7200).unwrap();
let payment_secret = $node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None).unwrap();

$node_a.send_payment(&route, payment_hash, &Some(payment_secret), PaymentId(payment_hash.0)).unwrap();
let payment_event = SendEvent::from_event($node_a.get_and_clear_pending_msg_events().pop().unwrap());
Expand Down
9 changes: 7 additions & 2 deletions lightning/src/ln/functional_test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1447,17 +1447,22 @@ macro_rules! get_payment_preimage_hash {
}
};
($dest_node: expr, $min_value_msat: expr) => {
{
crate::get_payment_preimage_hash!($dest_node, $min_value_msat, None)
}
};
($dest_node: expr, $min_value_msat: expr, $min_final_cltv_expiry_delta: expr) => {
{
use bitcoin::hashes::Hash as _;
let mut payment_count = $dest_node.network_payment_count.borrow_mut();
let payment_preimage = $crate::ln::PaymentPreimage([*payment_count; 32]);
*payment_count += 1;
let payment_hash = $crate::ln::PaymentHash(
bitcoin::hashes::sha256::Hash::hash(&payment_preimage.0[..]).into_inner());
let payment_secret = $dest_node.node.create_inbound_payment_for_hash(payment_hash, $min_value_msat, 7200).unwrap();
let payment_secret = $dest_node.node.create_inbound_payment_for_hash(payment_hash, $min_value_msat, 7200, $min_final_cltv_expiry_delta).unwrap();
(payment_preimage, payment_hash, payment_secret)
}
}
};
}

#[macro_export]
Expand Down
Loading

0 comments on commit 5b53670

Please sign in to comment.