Skip to content

Commit 499897d

Browse files
channelmanager: add fake_last_hops and create_fake_last_hop
This allows users to insert a fake last-hop, where actually any of the previous second-to-last hops can receive the payment because they have the last hop's secret key
1 parent bf9cdb9 commit 499897d

File tree

1 file changed

+48
-8
lines changed

1 file changed

+48
-8
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -544,23 +544,29 @@ struct ExpandedPmtKey {
544544
metadata_key: [u8; 32],
545545
ldk_pmt_hash_key: [u8; 32],
546546
user_pmt_hash_key: [u8; 32],
547+
node_secret_key: Option<SecretKey>, // Only set for fake last hops
547548
}
548549

549550
impl Writeable for ExpandedPmtKey {
550551
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
551-
self.key_material.write(w)
552+
self.key_material.write(w)?;
553+
if self.node_secret_key.is_some() {
554+
Ok(true.write(w)?)
555+
} else { Ok(false.write(w)?) }
552556
}
553557
}
554558

555559
impl Readable for ExpandedPmtKey {
556560
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
557561
let key_material: KeyMaterial = Readable::read(r)?;
558-
let (metadata_key, ldk_pmt_hash_key, user_pmt_hash_key) = hkdf_extract_expand(&vec![0], &key_material);
562+
let fake_node_key: bool = Readable::read(r)?; // TODO amend #1177 to write this value too
563+
let (metadata_key, ldk_pmt_hash_key, user_pmt_hash_key, node_key_bytes) = hkdf_extract_expand(&vec![0], &key_material);
559564
Ok(ExpandedPmtKey {
560565
key_material,
561566
metadata_key,
562567
ldk_pmt_hash_key,
563568
user_pmt_hash_key,
569+
node_secret_key: if fake_node_key { Some(SecretKey::from_slice(&node_key_bytes).unwrap()) } else { None },
564570
})
565571
}
566572
}
@@ -644,6 +650,8 @@ pub struct ChannelManager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref,
644650
#[cfg(not(any(test, feature = "_test_utils")))]
645651
channel_state: Mutex<ChannelHolder<Signer>>,
646652

653+
fake_last_hops: Mutex<HashMap<u64, ExpandedPmtKey>>,
654+
647655
/// Storage for PaymentSecrets and any requirements on future inbound payments before we will
648656
/// expose them to users via a PaymentReceived event. HTLCs which do not meet the requirements
649657
/// here are failed when we process them as pending-forwardable-HTLCs, and entries are removed
@@ -1347,12 +1355,13 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
13471355
let mut secp_ctx = Secp256k1::new();
13481356
secp_ctx.seeded_randomize(&keys_manager.get_secure_random_bytes());
13491357
let inbound_pmt_key_material = keys_manager.get_inbound_payment_key_material();
1350-
let (metadata_key, ldk_pmt_hash_key, user_pmt_hash_key) = hkdf_extract_expand(&vec![0], &inbound_pmt_key_material);
1358+
let (metadata_key, ldk_pmt_hash_key, user_pmt_hash_key, _) = hkdf_extract_expand(&vec![0], &inbound_pmt_key_material);
13511359
let expanded_inbound_key = ExpandedPmtKey {
13521360
key_material: inbound_pmt_key_material,
13531361
metadata_key,
13541362
ldk_pmt_hash_key,
1355-
user_pmt_hash_key
1363+
user_pmt_hash_key,
1364+
node_secret_key: None,
13561365
};
13571366

13581367
ChannelManager {
@@ -1371,6 +1380,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
13711380
claimable_htlcs: HashMap::new(),
13721381
pending_msg_events: Vec::new(),
13731382
}),
1383+
fake_last_hops: Mutex::new(HashMap::new()),
13741384
pending_inbound_payments: Mutex::new(HashMap::new()),
13751385
pending_outbound_payments: Mutex::new(HashMap::new()),
13761386

@@ -1736,6 +1746,25 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
17361746
}
17371747
}
17381748

1749+
/// Insert a fake last hop. This allows you to generate invoices for payments that can
1750+
/// actually be received by one of multiple nodes. `last_hop_key` should consist of 32 random
1751+
/// bytes.
1752+
// TODO we prob don't want to return the node secret, but how to sign invoices?
1753+
pub fn create_fake_last_hop(&self, last_hop_key: KeyMaterial, last_hop_scid: u64) -> SecretKey {
1754+
let mut fake_hops = self.fake_last_hops.lock().unwrap();
1755+
let (metadata_key, ldk_pmt_hash_key, user_pmt_hash_key, node_key) = hkdf_extract_expand(&vec![0], &last_hop_key);
1756+
let node_secret = SecretKey::from_slice(&node_key).unwrap();
1757+
fake_hops.insert(last_hop_scid, ExpandedPmtKey {
1758+
key_material: last_hop_key,
1759+
metadata_key,
1760+
ldk_pmt_hash_key,
1761+
user_pmt_hash_key,
1762+
node_secret_key: Some(node_secret),
1763+
});
1764+
1765+
node_secret
1766+
}
1767+
17391768
fn decode_update_add_htlc_onion(&self, msg: &msgs::UpdateAddHTLC) -> (PendingHTLCStatus, MutexGuard<ChannelHolder<Signer>>) {
17401769
macro_rules! return_malformed_err {
17411770
($msg: expr, $err_code: expr) => {
@@ -5882,9 +5911,11 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable f
58825911
_ => {},
58835912
}
58845913
}
5914+
let fake_last_hops = self.fake_last_hops.lock().unwrap();
58855915
write_tlv_fields!(writer, {
58865916
(1, pending_outbound_payments_no_retry, required),
58875917
(3, pending_outbound_payments, required),
5918+
(5, fake_last_hops, required),
58885919
});
58895920

58905921
Ok(())
@@ -6179,9 +6210,11 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
61796210
// pending_outbound_payments_no_retry is for compatibility with 0.0.101 clients.
61806211
let mut pending_outbound_payments_no_retry: Option<HashMap<PaymentId, HashSet<[u8; 32]>>> = None;
61816212
let mut pending_outbound_payments = None;
6213+
let mut fake_last_hops = Some(HashMap::new());
61826214
read_tlv_fields!(reader, {
61836215
(1, pending_outbound_payments_no_retry, option),
61846216
(3, pending_outbound_payments, option),
6217+
(5, fake_last_hops, option),
61856218
});
61866219
if pending_outbound_payments.is_none() && pending_outbound_payments_no_retry.is_none() {
61876220
pending_outbound_payments = Some(pending_outbound_payments_compat);
@@ -6246,12 +6279,13 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
62466279
}
62476280

62486281
let inbound_pmt_key_material = args.keys_manager.get_inbound_payment_key_material();
6249-
let (metadata_key, ldk_pmt_hash_key, user_pmt_hash_key) = hkdf_extract_expand(&vec![0], &inbound_pmt_key_material);
6282+
let (metadata_key, ldk_pmt_hash_key, user_pmt_hash_key, _) = hkdf_extract_expand(&vec![0], &inbound_pmt_key_material);
62506283
let expanded_inbound_key = ExpandedPmtKey {
62516284
key_material: inbound_pmt_key_material,
62526285
metadata_key,
62536286
ldk_pmt_hash_key,
6254-
user_pmt_hash_key
6287+
user_pmt_hash_key,
6288+
node_secret_key: None,
62556289
};
62566290

62576291
let channel_manager = ChannelManager {
@@ -6270,6 +6304,7 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
62706304
pending_msg_events: Vec::new(),
62716305
}),
62726306
inbound_payment_key: expanded_inbound_key,
6307+
fake_last_hops: Mutex::new(fake_last_hops.unwrap()),
62736308
pending_inbound_payments: Mutex::new(pending_inbound_payments),
62746309
pending_outbound_payments: Mutex::new(pending_outbound_payments.unwrap()),
62756310

@@ -6303,7 +6338,7 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
63036338
}
63046339
}
63056340

6306-
fn hkdf_extract_expand(salt: &[u8], ikm: &KeyMaterial) -> ([u8; 32], [u8; 32], [u8; 32]) {
6341+
fn hkdf_extract_expand(salt: &[u8], ikm: &KeyMaterial) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
63076342
let mut hmac = HmacEngine::<Sha256>::new(salt);
63086343
hmac.input(&ikm.0);
63096344
let prk = Hmac::from_engine(hmac).into_inner();
@@ -6321,7 +6356,12 @@ fn hkdf_extract_expand(salt: &[u8], ikm: &KeyMaterial) -> ([u8; 32], [u8; 32], [
63216356
hmac.input(&[3; 1]);
63226357
let t3 = Hmac::from_engine(hmac).into_inner();
63236358

6324-
(t1, t2, t3)
6359+
let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
6360+
hmac.input(&t3);
6361+
hmac.input(&[4; 1]);
6362+
let t4 = Hmac::from_engine(hmac).into_inner();
6363+
6364+
(t1, t2, t3, t4)
63256365
}
63266366

63276367
#[cfg(test)]

0 commit comments

Comments
 (0)