Skip to content

Commit 3aa79c5

Browse files
Drop need to store pending inbound payments
and replace payment_secret with encrypted payment metadata See docs on create_inbound_payment and create_inbound_payment_for_hash for details
1 parent f7165c4 commit 3aa79c5

File tree

2 files changed

+205
-159
lines changed

2 files changed

+205
-159
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 205 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ use core::{cmp, mem};
6565
use core::cell::RefCell;
6666
use io::{Cursor, Read};
6767
use sync::{Arc, Condvar, Mutex, MutexGuard, RwLock, RwLockReadGuard};
68+
use core::convert::TryInto;
6869
use core::sync::atomic::{AtomicUsize, Ordering};
6970
use core::time::Duration;
7071
#[cfg(any(test, feature = "allow_wallclock_use"))]
@@ -2586,6 +2587,62 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
25862587
}
25872588
}
25882589

2590+
// Check that an inbound payment's `payment_data` field is sane.
2591+
fn verify_inbound_payment_data(&self, payment_hash: PaymentHash, payment_data: msgs::FinalOnionHopData) -> Result<Option<PaymentPreimage>, ()> {
2592+
let (iv_bytes, encrypted_metadata_bytes) = payment_data.payment_secret.0.split_at(16);
2593+
let mut chacha = ChaCha20::new(&self.our_network_key[..], &iv_bytes[..12]);
2594+
let mut chacha_bytes = [0; 16];
2595+
chacha.process_in_place(&mut chacha_bytes);
2596+
2597+
let mut metadata_bytes: [u8; 16] = [0; 16];
2598+
for i in 0..16 {
2599+
metadata_bytes[i] = chacha_bytes[i] ^ encrypted_metadata_bytes[i];
2600+
}
2601+
2602+
let expiry = u64::from_be_bytes(metadata_bytes[8..].try_into().unwrap());
2603+
if expiry < self.highest_seen_timestamp.load(Ordering::Acquire) as u64 {
2604+
log_trace!(self.logger, "Failing HTLC with payment_hash {}: expired payment", log_bytes!(payment_hash.0));
2605+
return Err(())
2606+
}
2607+
2608+
let is_user_payment_hash = metadata_bytes[0] & 1 << 7 != 0;
2609+
let mut payment_preimage = None;
2610+
if is_user_payment_hash {
2611+
let mut hmac = HmacEngine::<Sha256>::new(&self.our_network_key[..]);
2612+
hmac.input(&metadata_bytes[..]);
2613+
hmac.input(&payment_hash.0[..]);
2614+
if iv_bytes != Hmac::from_engine(hmac).into_inner().split_at_mut(16).0 {
2615+
log_trace!(self.logger, "Failing HTLC with user-generated payment_hash {}: unexpected payment_secret", log_bytes!(payment_hash.0));
2616+
return Err(())
2617+
}
2618+
// Reset the bit that was set to indicate that the payment hash was user-generated.
2619+
let mut amt_msat_bytes = [0; 8];
2620+
amt_msat_bytes.copy_from_slice(&metadata_bytes[..8]);
2621+
amt_msat_bytes[0] ^= 1 << 7;
2622+
let min_amt_msat = u64::from_be_bytes(amt_msat_bytes.try_into().unwrap());
2623+
if payment_data.total_msat < min_amt_msat {
2624+
log_trace!(self.logger, "Failing HTLC with user-generated payment_hash {} due to total_msat {} being less than the minimum amount of {} msat", log_bytes!(payment_hash.0), payment_data.total_msat, min_amt_msat);
2625+
return Err(())
2626+
}
2627+
} else {
2628+
let min_amt_msat = u64::from_be_bytes(metadata_bytes[..8].try_into().unwrap());
2629+
if payment_data.total_msat < min_amt_msat {
2630+
log_trace!(self.logger, "Failing HTLC with payment_hash {} due to total_msat {} being less than the minimum amount of {} msat", log_bytes!(payment_hash.0), payment_data.total_msat, min_amt_msat);
2631+
return Err(())
2632+
}
2633+
let mut hmac = HmacEngine::<Sha256>::new(&self.our_network_key[..]);
2634+
hmac.input(&iv_bytes);
2635+
hmac.input(&metadata_bytes);
2636+
let decoded_payment_preimage = Hmac::from_engine(hmac).into_inner();
2637+
if payment_hash.0 != Sha256::hash(&decoded_payment_preimage).into_inner() {
2638+
log_trace!(self.logger, "Failing HTLC with payment_hash {}: payment preimage {} did not match", log_bytes!(payment_hash.0), log_bytes!(decoded_payment_preimage));
2639+
return Err(())
2640+
}
2641+
payment_preimage = Some(PaymentPreimage(decoded_payment_preimage));
2642+
}
2643+
Ok(payment_preimage)
2644+
}
2645+
25892646
/// Processes HTLCs which are pending waiting on random forward delay.
25902647
///
25912648
/// Should only really ever be called in response to a PendingHTLCsForwardable event.
@@ -2801,6 +2858,56 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
28012858
}
28022859
}
28032860

2861+
macro_rules! check_total_value {
2862+
($payment_data_total_msat: expr, $payment_secret: expr, $payment_preimage: expr) => {
2863+
let mut total_value = 0;
2864+
let htlcs = channel_state.claimable_htlcs.entry(payment_hash)
2865+
.or_insert(Vec::new());
2866+
if htlcs.len() == 1 {
2867+
if let OnionPayload::Spontaneous(_) = htlcs[0].onion_payload {
2868+
log_trace!(self.logger, "Failing new HTLC with payment_hash {} as we already had an existing keysend HTLC with the same payment hash", log_bytes!(payment_hash.0));
2869+
fail_htlc!(claimable_htlc);
2870+
continue
2871+
}
2872+
}
2873+
htlcs.push(claimable_htlc);
2874+
for htlc in htlcs.iter() {
2875+
total_value += htlc.value;
2876+
match &htlc.onion_payload {
2877+
OnionPayload::Invoice(htlc_payment_data) => {
2878+
if htlc_payment_data.total_msat != $payment_data_total_msat {
2879+
log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the HTLCs had inconsistent total values (eg {} and {})",
2880+
log_bytes!(payment_hash.0), $payment_data_total_msat, htlc_payment_data.total_msat);
2881+
total_value = msgs::MAX_VALUE_MSAT;
2882+
}
2883+
if total_value >= msgs::MAX_VALUE_MSAT { break; }
2884+
},
2885+
_ => unreachable!(),
2886+
}
2887+
}
2888+
if total_value >= msgs::MAX_VALUE_MSAT || total_value > $payment_data_total_msat {
2889+
log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the total value {} ran over expected value {} (or HTLCs were inconsistent)",
2890+
log_bytes!(payment_hash.0), total_value, $payment_data_total_msat);
2891+
for htlc in htlcs.iter() {
2892+
fail_htlc!(htlc);
2893+
}
2894+
} else if total_value == $payment_data_total_msat {
2895+
new_events.push(events::Event::PaymentReceived {
2896+
payment_hash,
2897+
purpose: events::PaymentPurpose::InvoicePayment {
2898+
payment_preimage: $payment_preimage,
2899+
payment_secret: $payment_secret,
2900+
},
2901+
amt: total_value,
2902+
});
2903+
} else {
2904+
// Nothing to do - we haven't reached the total
2905+
// payment value yet, wait until we receive more
2906+
// MPP parts.
2907+
}
2908+
}
2909+
}
2910+
28042911
// Check that the payment hash and secret are known. Note that we
28052912
// MUST take care to handle the "unknown payment hash" and
28062913
// "incorrect payment secret" cases here identically or we'd expose
@@ -2811,9 +2918,17 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
28112918
match payment_secrets.entry(payment_hash) {
28122919
hash_map::Entry::Vacant(_) => {
28132920
match claimable_htlc.onion_payload {
2814-
OnionPayload::Invoice(_) => {
2815-
log_trace!(self.logger, "Failing new HTLC with payment_hash {} as we didn't have a corresponding inbound payment.", log_bytes!(payment_hash.0));
2816-
fail_htlc!(claimable_htlc);
2921+
OnionPayload::Invoice(ref payment_data) => {
2922+
let payment_preimage = match self.verify_inbound_payment_data(payment_hash, payment_data.clone()) {
2923+
Ok(payment_preimage) => payment_preimage,
2924+
Err(()) => {
2925+
fail_htlc!(claimable_htlc);
2926+
continue
2927+
}
2928+
};
2929+
let payment_data_total_msat = payment_data.total_msat;
2930+
let payment_secret = payment_data.payment_secret.clone();
2931+
check_total_value!(payment_data_total_msat, payment_secret, payment_preimage);
28172932
},
28182933
OnionPayload::Spontaneous(preimage) => {
28192934
match channel_state.claimable_htlcs.entry(payment_hash) {
@@ -2850,55 +2965,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
28502965
log_bytes!(payment_hash.0), payment_data.total_msat, inbound_payment.get().min_value_msat.unwrap());
28512966
fail_htlc!(claimable_htlc);
28522967
} else {
2853-
let mut total_value = 0;
2854-
let htlcs = channel_state.claimable_htlcs.entry(payment_hash)
2855-
.or_insert(Vec::new());
2856-
if htlcs.len() == 1 {
2857-
if let OnionPayload::Spontaneous(_) = htlcs[0].onion_payload {
2858-
log_trace!(self.logger, "Failing new HTLC with payment_hash {} as we already had an existing keysend HTLC with the same payment hash", log_bytes!(payment_hash.0));
2859-
fail_htlc!(claimable_htlc);
2860-
continue
2861-
}
2862-
}
2863-
htlcs.push(claimable_htlc);
2864-
for htlc in htlcs.iter() {
2865-
total_value += htlc.value;
2866-
match &htlc.onion_payload {
2867-
OnionPayload::Invoice(htlc_payment_data) => {
2868-
if htlc_payment_data.total_msat != payment_data.total_msat {
2869-
log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the HTLCs had inconsistent total values (eg {} and {})",
2870-
log_bytes!(payment_hash.0), payment_data.total_msat, htlc_payment_data.total_msat);
2871-
total_value = msgs::MAX_VALUE_MSAT;
2872-
}
2873-
if total_value >= msgs::MAX_VALUE_MSAT { break; }
2874-
},
2875-
_ => unreachable!(),
2876-
}
2877-
}
2878-
if total_value >= msgs::MAX_VALUE_MSAT || total_value > payment_data.total_msat {
2879-
log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the total value {} ran over expected value {} (or HTLCs were inconsistent)",
2880-
log_bytes!(payment_hash.0), total_value, payment_data.total_msat);
2881-
for htlc in htlcs.iter() {
2882-
fail_htlc!(htlc);
2883-
}
2884-
} else if total_value == payment_data.total_msat {
2885-
new_events.push(events::Event::PaymentReceived {
2886-
payment_hash,
2887-
purpose: events::PaymentPurpose::InvoicePayment {
2888-
payment_preimage: inbound_payment.get().payment_preimage,
2889-
payment_secret: payment_data.payment_secret,
2890-
},
2891-
amt: total_value,
2892-
});
2893-
// Only ever generate at most one PaymentReceived
2894-
// per registered payment_hash, even if it isn't
2895-
// claimed.
2896-
inbound_payment.remove_entry();
2897-
} else {
2898-
// Nothing to do - we haven't reached the total
2899-
// payment value yet, wait until we receive more
2900-
// MPP parts.
2901-
}
2968+
check_total_value!(payment_data.total_msat, payment_data.payment_secret, inbound_payment.get().payment_preimage);
29022969
}
29032970
},
29042971
};
@@ -4522,38 +4589,11 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
45224589
}
45234590
}
45244591

4525-
fn set_payment_hash_secret_map(&self, payment_hash: PaymentHash, payment_preimage: Option<PaymentPreimage>, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32) -> Result<PaymentSecret, APIError> {
4526-
assert!(invoice_expiry_delta_secs <= 60*60*24*365); // Sadly bitcoin timestamps are u32s, so panic before 2106
4527-
4528-
let payment_secret = PaymentSecret(self.keys_manager.get_secure_random_bytes());
4529-
4530-
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
4531-
let mut payment_secrets = self.pending_inbound_payments.lock().unwrap();
4532-
match payment_secrets.entry(payment_hash) {
4533-
hash_map::Entry::Vacant(e) => {
4534-
e.insert(PendingInboundPayment {
4535-
payment_secret, min_value_msat, payment_preimage,
4536-
user_payment_id: 0, // For compatibility with version 0.0.103 and earlier
4537-
// We assume that highest_seen_timestamp is pretty close to the current time -
4538-
// its updated when we receive a new block with the maximum time we've seen in
4539-
// a header. It should never be more than two hours in the future.
4540-
// Thus, we add two hours here as a buffer to ensure we absolutely
4541-
// never fail a payment too early.
4542-
// Note that we assume that received blocks have reasonably up-to-date
4543-
// timestamps.
4544-
expiry_time: self.highest_seen_timestamp.load(Ordering::Acquire) as u64 + invoice_expiry_delta_secs as u64 + 7200,
4545-
});
4546-
},
4547-
hash_map::Entry::Occupied(_) => return Err(APIError::APIMisuseError { err: "Duplicate payment hash".to_owned() }),
4548-
}
4549-
Ok(payment_secret)
4550-
}
4551-
45524592
/// Gets a payment secret and payment hash for use in an invoice given to a third party wishing
45534593
/// to pay us.
45544594
///
45554595
/// This differs from [`create_inbound_payment_for_hash`] only in that it generates the
4556-
/// [`PaymentHash`] and [`PaymentPreimage`] for you, returning the first and storing the second.
4596+
/// [`PaymentHash`] and [`PaymentPreimage`] for you.
45574597
///
45584598
/// The [`PaymentPreimage`] will ultimately be returned to you in the [`PaymentReceived`], which
45594599
/// will have the [`PaymentReceived::payment_preimage`] field filled in. That should then be
@@ -4566,12 +4606,47 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
45664606
/// [`PaymentReceived::payment_preimage`]: events::Event::PaymentReceived::payment_preimage
45674607
/// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
45684608
pub fn create_inbound_payment(&self, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32) -> (PaymentHash, PaymentSecret) {
4569-
let payment_preimage = PaymentPreimage(self.keys_manager.get_secure_random_bytes());
4570-
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
4609+
let min_amt_msat_bytes: [u8; 8] = match min_value_msat {
4610+
Some(amt) => amt.to_be_bytes(),
4611+
None => [0; 8],
4612+
};
4613+
// We assume that highest_seen_timestamp is pretty close to the current time - its updated when
4614+
// we receive a new block with the maximum time we've seen in a header. It should never be more
4615+
// than two hours in the future. Thus, we add two hours here as a buffer to ensure we
4616+
// absolutely never fail a payment too early.
4617+
// Note that we assume that received blocks have reasonably up-to-date timestamps.
4618+
let expiry_bytes = (self.highest_seen_timestamp.load(Ordering::Acquire) as u64 + invoice_expiry_delta_secs as u64 + 7200).to_be_bytes();
4619+
let mut metadata_bytes: [u8; 16] = [0; 16];
4620+
{
4621+
let (min_amt_msat_slice, expiry_slice) = metadata_bytes.split_at_mut(8);
4622+
min_amt_msat_slice.copy_from_slice(&min_amt_msat_bytes);
4623+
expiry_slice.copy_from_slice(&expiry_bytes);
4624+
}
4625+
4626+
let rand_bytes = self.keys_manager.get_secure_random_bytes();
4627+
let iv_bytes = &rand_bytes[..16];
4628+
4629+
let mut hmac = HmacEngine::<Sha256>::new(&self.our_network_key[..]);
4630+
hmac.input(iv_bytes);
4631+
hmac.input(&metadata_bytes);
4632+
let payment_preimage_bytes = Hmac::from_engine(hmac).into_inner();
45714633

4572-
(payment_hash,
4573-
self.set_payment_hash_secret_map(payment_hash, Some(payment_preimage), min_value_msat, invoice_expiry_delta_secs)
4574-
.expect("RNG Generated Duplicate PaymentHash"))
4634+
let mut payment_secret_bytes: [u8; 32] = [0; 32];
4635+
{
4636+
let (iv_slice, encrypted_metadata_slice) = payment_secret_bytes.split_at_mut(16);
4637+
iv_slice.copy_from_slice(iv_bytes);
4638+
4639+
let mut chacha = ChaCha20::new(&self.our_network_key[..], &iv_bytes[..12]);
4640+
let mut chacha_bytes = [0; 16];
4641+
chacha.process_in_place(&mut chacha_bytes);
4642+
4643+
for i in 0..16 {
4644+
encrypted_metadata_slice[i] = chacha_bytes[i] ^ metadata_bytes[i];
4645+
}
4646+
}
4647+
4648+
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage_bytes).into_inner());
4649+
(payment_hash, PaymentSecret(payment_secret_bytes))
45754650
}
45764651

45774652
/// Gets a [`PaymentSecret`] for a given [`PaymentHash`], for which the payment preimage is
@@ -4593,18 +4668,13 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
45934668
/// in excess of the current time. This should roughly match the expiry time set in the invoice.
45944669
/// After this many seconds, we will remove the inbound payment, resulting in any attempts to
45954670
/// pay the invoice failing. The BOLT spec suggests 3,600 secs as a default validity time for
4596-
/// invoices when no timeout is set.
4597-
///
4598-
/// Note that we use block header time to time-out pending inbound payments (with some margin
4599-
/// to compensate for the inaccuracy of block header timestamps). Thus, in practice we will
4671+
/// invoices when no timeout is set. Note that we use block header time to time-out pending
4672+
/// inbound payments (with some margin to compensate for the inaccuracy of block header
4673+
/// timestamps). Thus, in practice we will
46004674
/// accept a payment and generate a [`PaymentReceived`] event for some time after the expiry.
46014675
/// If you need exact expiry semantics, you should enforce them upon receipt of
46024676
/// [`PaymentReceived`].
46034677
///
4604-
/// Pending inbound payments are stored in memory and in serialized versions of this
4605-
/// [`ChannelManager`]. If potentially unbounded numbers of inbound payments may exist and
4606-
/// space is limited, you may wish to rate-limit inbound payment creation.
4607-
///
46084678
/// May panic if `invoice_expiry_delta_secs` is greater than one year.
46094679
///
46104680
/// Note that invoices generated for inbound payments should have their `min_final_cltv_expiry`
@@ -4613,7 +4683,48 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
46134683
/// [`create_inbound_payment`]: Self::create_inbound_payment
46144684
/// [`PaymentReceived`]: events::Event::PaymentReceived
46154685
pub fn create_inbound_payment_for_hash(&self, payment_hash: PaymentHash, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32) -> Result<PaymentSecret, APIError> {
4616-
self.set_payment_hash_secret_map(payment_hash, None, min_value_msat, invoice_expiry_delta_secs)
4686+
let mut min_amt_msat_bytes: [u8; 8] = match min_value_msat {
4687+
Some(amt) => amt.to_be_bytes(),
4688+
None => [0; 8],
4689+
};
4690+
// Flip the highest bit of the min_amt_msat field to indicate that this payment has a
4691+
// user-generated PaymentHash.
4692+
min_amt_msat_bytes[0] |= 1 << 7;
4693+
4694+
// We assume that highest_seen_timestamp is pretty close to the current time - its updated when
4695+
// we receive a new block with the maximum time we've seen in a header. It should never be more
4696+
// than two hours in the future. Thus, we add two hours here as a buffer to ensure we
4697+
// absolutely never fail a payment too early.
4698+
// Note that we assume that received blocks have reasonably up-to-date timestamps.
4699+
let expiry_bytes = (self.highest_seen_timestamp.load(Ordering::Acquire) as u64 + invoice_expiry_delta_secs as u64 + 7200).to_be_bytes();
4700+
4701+
let mut metadata_bytes: [u8; 16] = [0; 16];
4702+
{
4703+
let (min_amt_msat_slice, expiry_slice) = metadata_bytes.split_at_mut(8);
4704+
min_amt_msat_slice.copy_from_slice(&min_amt_msat_bytes);
4705+
expiry_slice.copy_from_slice(&expiry_bytes);
4706+
}
4707+
4708+
let mut hmac = HmacEngine::<Sha256>::new(&self.our_network_key[..]);
4709+
hmac.input(&metadata_bytes);
4710+
hmac.input(&payment_hash.0);
4711+
let hmac_bytes = Hmac::from_engine(hmac).into_inner();
4712+
let iv_bytes = &hmac_bytes[..16];
4713+
4714+
let mut payment_secret_bytes: [u8; 32] = [0; 32];
4715+
{
4716+
let (iv_slice, encrypted_metadata_slice) = payment_secret_bytes.split_at_mut(16);
4717+
iv_slice.copy_from_slice(iv_bytes);
4718+
4719+
let mut chacha = ChaCha20::new(&self.our_network_key[..], &iv_bytes[..12]);
4720+
let mut chacha_bytes = [0; 16];
4721+
chacha.process_in_place(&mut chacha_bytes);
4722+
4723+
for i in 0..16 {
4724+
encrypted_metadata_slice[i] = chacha_bytes[i] ^ metadata_bytes[i];
4725+
}
4726+
}
4727+
Ok(PaymentSecret(payment_secret_bytes))
46174728
}
46184729

46194730
#[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))]

0 commit comments

Comments
 (0)