Skip to content

Commit 33ac6c8

Browse files
committed
Support (de)serializing payment_data in onion TLVs and track them
This is the first step in Base AMP support, just tracking the relevant data in internal datastructures.
1 parent 917e707 commit 33ac6c8

File tree

3 files changed

+128
-37
lines changed

3 files changed

+128
-37
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ enum PendingForwardReceiveHTLCInfo {
7474
onion_packet: msgs::OnionPacket,
7575
short_channel_id: u64, // This should be NonZero<u64> eventually
7676
},
77-
Receive {},
77+
Receive {
78+
payment_data: Option<msgs::FinalOnionHopData>,
79+
},
7880
}
7981

8082
#[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
@@ -119,6 +121,12 @@ pub(super) struct HTLCPreviousHopData {
119121
incoming_packet_shared_secret: [u8; 32],
120122
}
121123

124+
struct ClaimableHTLC {
125+
prev_hop: HTLCPreviousHopData,
126+
value: u64,
127+
payment_data: Option<msgs::FinalOnionHopData>,
128+
}
129+
122130
/// Tracks the inbound corresponding to an outbound HTLC
123131
#[derive(Clone, PartialEq)]
124132
pub(super) enum HTLCSource {
@@ -276,12 +284,11 @@ pub(super) struct ChannelHolder<ChanSigner: ChannelKeys> {
276284
/// guarantees are made about the existence of a channel with the short id here, nor the short
277285
/// ids in the PendingHTLCInfo!
278286
pub(super) forward_htlcs: HashMap<u64, Vec<HTLCForwardInfo>>,
279-
/// payment_hash -> Vec<(amount_received, htlc_source)> for tracking things that were to us and
280-
/// can be failed/claimed by the user
287+
/// Tracks HTLCs that were to us and can be failed/claimed by the user
281288
/// Note that while this is held in the same mutex as the channels themselves, no consistency
282289
/// guarantees are made about the channels given here actually existing anymore by the time you
283290
/// go to read them!
284-
pub(super) claimable_htlcs: HashMap<PaymentHash, Vec<(u64, HTLCPreviousHopData)>>,
291+
claimable_htlcs: HashMap<PaymentHash, Vec<ClaimableHTLC>>,
285292
/// Messages to send to peers - pushed to in the same lock that they are generated in (except
286293
/// for broadcast messages, where ordering isn't as strict).
287294
pub(super) pending_msg_events: Vec<events::MessageSendEvent>,
@@ -987,13 +994,19 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
987994
return_err!("Upstream node set CLTV to the wrong value", 18, &byte_utils::be32_to_array(msg.cltv_expiry));
988995
}
989996

997+
let payment_data = match next_hop_data.format {
998+
msgs::OnionHopDataFormat::Legacy { .. } => None,
999+
msgs::OnionHopDataFormat::NonFinalNode { .. } => return_err!("Got non final data with an HMAC of 0", 0x4000 | 22, &[0;0]),
1000+
msgs::OnionHopDataFormat::FinalNode { payment_data } => payment_data,
1001+
};
1002+
9901003
// Note that we could obviously respond immediately with an update_fulfill_htlc
9911004
// message, however that would leak that we are the recipient of this payment, so
9921005
// instead we stay symmetric with the forwarding case, only responding (after a
9931006
// delay) once they've send us a commitment_signed!
9941007

9951008
PendingHTLCStatus::Forward(PendingHTLCInfo {
996-
forward_or_receive: PendingForwardReceiveHTLCInfo::Receive {},
1009+
forward_or_receive: PendingForwardReceiveHTLCInfo::Receive { payment_data },
9971010
payment_hash: msg.payment_hash.clone(),
9981011
incoming_shared_secret: shared_secret,
9991012
amt_to_forward: next_hop_data.amt_to_forward,
@@ -1567,17 +1580,18 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
15671580
for forward_info in pending_forwards.drain(..) {
15681581
match forward_info {
15691582
HTLCForwardInfo::AddHTLC { prev_short_channel_id, prev_htlc_id, forward_info: PendingHTLCInfo {
1570-
forward_or_receive: PendingForwardReceiveHTLCInfo::Receive { },
1583+
forward_or_receive: PendingForwardReceiveHTLCInfo::Receive { payment_data },
15711584
incoming_shared_secret, payment_hash, amt_to_forward, .. }, } => {
1572-
let prev_hop_data = HTLCPreviousHopData {
1585+
let prev_hop = HTLCPreviousHopData {
15731586
short_channel_id: prev_short_channel_id,
15741587
htlc_id: prev_htlc_id,
15751588
incoming_packet_shared_secret: incoming_shared_secret,
15761589
};
1577-
match channel_state.claimable_htlcs.entry(payment_hash) {
1578-
hash_map::Entry::Occupied(mut entry) => entry.get_mut().push((amt_to_forward, prev_hop_data)),
1579-
hash_map::Entry::Vacant(entry) => { entry.insert(vec![(amt_to_forward, prev_hop_data)]); },
1580-
};
1590+
channel_state.claimable_htlcs.entry(payment_hash).or_insert(Vec::new()).push(ClaimableHTLC {
1591+
prev_hop,
1592+
value: amt_to_forward,
1593+
payment_data,
1594+
});
15811595
new_events.push(events::Event::PaymentReceived {
15821596
payment_hash: payment_hash,
15831597
amt: amt_to_forward,
@@ -1650,11 +1664,11 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
16501664
let mut channel_state = Some(self.channel_state.lock().unwrap());
16511665
let removed_source = channel_state.as_mut().unwrap().claimable_htlcs.remove(payment_hash);
16521666
if let Some(mut sources) = removed_source {
1653-
for (recvd_value, htlc_with_hash) in sources.drain(..) {
1667+
for htlc in sources.drain(..) {
16541668
if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap()); }
16551669
self.fail_htlc_backwards_internal(channel_state.take().unwrap(),
1656-
HTLCSource::PreviousHopData(htlc_with_hash), payment_hash,
1657-
HTLCFailReason::Reason { failure_code: 0x4000 | 15, data: byte_utils::be64_to_array(recvd_value).to_vec() });
1670+
HTLCSource::PreviousHopData(htlc.prev_hop), payment_hash,
1671+
HTLCFailReason::Reason { failure_code: 0x4000 | 15, data: byte_utils::be64_to_array(htlc.value).to_vec() });
16581672
}
16591673
true
16601674
} else { false }
@@ -1778,17 +1792,17 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
17781792
let mut channel_state = Some(self.channel_state.lock().unwrap());
17791793
let removed_source = channel_state.as_mut().unwrap().claimable_htlcs.remove(&payment_hash);
17801794
if let Some(mut sources) = removed_source {
1781-
for (received_amount, htlc_with_hash) in sources.drain(..) {
1795+
for htlc in sources.drain(..) {
17821796
if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap()); }
1783-
if received_amount < expected_amount || received_amount > expected_amount * 2 {
1784-
let mut htlc_msat_data = byte_utils::be64_to_array(received_amount).to_vec();
1797+
if htlc.value < expected_amount || htlc.value > expected_amount * 2 {
1798+
let mut htlc_msat_data = byte_utils::be64_to_array(htlc.value).to_vec();
17851799
let mut height_data = byte_utils::be32_to_array(self.latest_block_height.load(Ordering::Acquire) as u32).to_vec();
17861800
htlc_msat_data.append(&mut height_data);
17871801
self.fail_htlc_backwards_internal(channel_state.take().unwrap(),
1788-
HTLCSource::PreviousHopData(htlc_with_hash), &payment_hash,
1802+
HTLCSource::PreviousHopData(htlc.prev_hop), &payment_hash,
17891803
HTLCFailReason::Reason { failure_code: 0x4000|15, data: htlc_msat_data });
17901804
} else {
1791-
self.claim_funds_internal(channel_state.take().unwrap(), HTLCSource::PreviousHopData(htlc_with_hash), payment_preimage);
1805+
self.claim_funds_internal(channel_state.take().unwrap(), HTLCSource::PreviousHopData(htlc.prev_hop), payment_preimage);
17921806
}
17931807
}
17941808
true
@@ -3151,8 +3165,9 @@ impl Writeable for PendingHTLCInfo {
31513165
onion_packet.write(writer)?;
31523166
short_channel_id.write(writer)?;
31533167
},
3154-
&PendingForwardReceiveHTLCInfo::Receive { } => {
3168+
&PendingForwardReceiveHTLCInfo::Receive { ref payment_data } => {
31553169
1u8.write(writer)?;
3170+
payment_data.write(writer)?;
31563171
},
31573172
}
31583173
self.incoming_shared_secret.write(writer)?;
@@ -3171,7 +3186,9 @@ impl Readable for PendingHTLCInfo {
31713186
onion_packet: Readable::read(reader)?,
31723187
short_channel_id: Readable::read(reader)?,
31733188
},
3174-
1u8 => PendingForwardReceiveHTLCInfo::Receive { },
3189+
1u8 => PendingForwardReceiveHTLCInfo::Receive {
3190+
payment_data: Readable::read(reader)?,
3191+
},
31753192
_ => return Err(DecodeError::InvalidValue),
31763193
},
31773194
incoming_shared_secret: Readable::read(reader)?,
@@ -3381,9 +3398,10 @@ impl<ChanSigner: ChannelKeys + Writeable, M: Deref, T: Deref, K: Deref, F: Deref
33813398
for (payment_hash, previous_hops) in channel_state.claimable_htlcs.iter() {
33823399
payment_hash.write(writer)?;
33833400
(previous_hops.len() as u64).write(writer)?;
3384-
for &(recvd_amt, ref previous_hop) in previous_hops.iter() {
3385-
recvd_amt.write(writer)?;
3386-
previous_hop.write(writer)?;
3401+
for htlc in previous_hops.iter() {
3402+
htlc.prev_hop.write(writer)?;
3403+
htlc.value.write(writer)?;
3404+
htlc.payment_data.write(writer)?;
33873405
}
33883406
}
33893407

@@ -3553,7 +3571,11 @@ impl<'a, ChanSigner: ChannelKeys + Readable, M: Deref, T: Deref, K: Deref, F: De
35533571
let previous_hops_len: u64 = Readable::read(reader)?;
35543572
let mut previous_hops = Vec::with_capacity(cmp::min(previous_hops_len as usize, 2));
35553573
for _ in 0..previous_hops_len {
3556-
previous_hops.push((Readable::read(reader)?, Readable::read(reader)?));
3574+
previous_hops.push(ClaimableHTLC {
3575+
prev_hop: Readable::read(reader)?,
3576+
value: Readable::read(reader)?,
3577+
payment_data: Readable::read(reader)?,
3578+
});
35573579
}
35583580
claimable_htlcs.insert(payment_hash, previous_hops);
35593581
}

lightning/src/ln/msgs.rs

Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,11 @@ pub trait RoutingMessageHandler : Send + Sync {
614614
mod fuzzy_internal_msgs {
615615
// These types aren't intended to be pub, but are exposed for direct fuzzing (as we deserialize
616616
// them from untrusted input):
617+
#[derive(Clone)]
618+
pub(crate) struct FinalOnionHopData {
619+
pub(crate) payment_secret: [u8; 32],
620+
pub(crate) total_msat: u64,
621+
}
617622

618623
pub(crate) enum OnionHopDataFormat {
619624
Legacy { // aka Realm-0
@@ -622,7 +627,9 @@ mod fuzzy_internal_msgs {
622627
NonFinalNode {
623628
short_channel_id: u64,
624629
},
625-
FinalNode,
630+
FinalNode {
631+
payment_data: Option<FinalOnionHopData>,
632+
},
626633
}
627634

628635
pub struct OnionHopData {
@@ -965,6 +972,22 @@ impl_writeable!(UpdateAddHTLC, 32+8+8+32+4+1366, {
965972
onion_routing_packet
966973
});
967974

975+
impl Writeable for FinalOnionHopData {
976+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> {
977+
w.size_hint(32 + 8 - (self.total_msat.leading_zeros()/8) as usize);
978+
self.payment_secret.write(w)?;
979+
HighZeroBytesDroppedVarInt(self.total_msat).write(w)
980+
}
981+
}
982+
983+
impl Readable for FinalOnionHopData {
984+
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
985+
let payment_secret = Readable::read(r)?;
986+
let amt: HighZeroBytesDroppedVarInt<u64> = Readable::read(r)?;
987+
Ok(Self { payment_secret, total_msat: amt.0 })
988+
}
989+
}
990+
968991
impl Writeable for OnionHopData {
969992
fn write<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> {
970993
w.size_hint(33);
@@ -983,11 +1006,19 @@ impl Writeable for OnionHopData {
9831006
(6, short_channel_id)
9841007
});
9851008
},
986-
OnionHopDataFormat::FinalNode => {
987-
encode_varint_length_prefixed_tlv!(w, {
988-
(2, HighZeroBytesDroppedVarInt(self.amt_to_forward)),
989-
(4, HighZeroBytesDroppedVarInt(self.outgoing_cltv_value))
990-
});
1009+
OnionHopDataFormat::FinalNode { ref payment_data } => {
1010+
if let &Some(ref final_data) = payment_data {
1011+
encode_varint_length_prefixed_tlv!(w, {
1012+
(2, HighZeroBytesDroppedVarInt(self.amt_to_forward)),
1013+
(4, HighZeroBytesDroppedVarInt(self.outgoing_cltv_value)),
1014+
(8, final_data)
1015+
});
1016+
} else {
1017+
encode_varint_length_prefixed_tlv!(w, {
1018+
(2, HighZeroBytesDroppedVarInt(self.amt_to_forward)),
1019+
(4, HighZeroBytesDroppedVarInt(self.outgoing_cltv_value))
1020+
});
1021+
}
9911022
},
9921023
}
9931024
Ok(())
@@ -1008,19 +1039,24 @@ impl Readable for OnionHopData {
10081039
let mut amt = HighZeroBytesDroppedVarInt(0u64);
10091040
let mut cltv_value = HighZeroBytesDroppedVarInt(0u32);
10101041
let mut short_id: Option<u64> = None;
1042+
let mut payment_data: Option<FinalOnionHopData> = None;
10111043
decode_tlv!(&mut rd, {
10121044
(2, amt),
10131045
(4, cltv_value)
10141046
}, {
1015-
(6, short_id)
1047+
(6, short_id),
1048+
(8, payment_data)
10161049
});
10171050
rd.eat_remaining().map_err(|_| DecodeError::ShortRead)?;
10181051
let format = if let Some(short_channel_id) = short_id {
1052+
if payment_data.is_some() { return Err(DecodeError::InvalidValue); }
10191053
OnionHopDataFormat::NonFinalNode {
10201054
short_channel_id,
10211055
}
10221056
} else {
1023-
OnionHopDataFormat::FinalNode
1057+
OnionHopDataFormat::FinalNode {
1058+
payment_data
1059+
}
10241060
};
10251061
(format, amt.0, cltv_value.0)
10261062
} else {
@@ -1305,7 +1341,7 @@ impl_writeable_len_match!(NodeAnnouncement, {
13051341
mod tests {
13061342
use hex;
13071343
use ln::msgs;
1308-
use ln::msgs::{ChannelFeatures, InitFeatures, NodeFeatures, OptionalField, OnionErrorPacket, OnionHopDataFormat};
1344+
use ln::msgs::{ChannelFeatures, FinalOnionHopData, InitFeatures, NodeFeatures, OptionalField, OnionErrorPacket, OnionHopDataFormat};
13091345
use ln::channelmanager::{PaymentPreimage, PaymentHash};
13101346
use util::ser::{Writeable, Readable};
13111347

@@ -1998,15 +2034,46 @@ mod tests {
19982034
#[test]
19992035
fn encoding_final_onion_hop_data() {
20002036
let mut msg = msgs::OnionHopData {
2001-
format: OnionHopDataFormat::FinalNode,
2037+
format: OnionHopDataFormat::FinalNode {
2038+
payment_data: None,
2039+
},
20022040
amt_to_forward: 0x0badf00d01020304,
20032041
outgoing_cltv_value: 0xffffffff,
20042042
};
20052043
let encoded_value = msg.encode();
20062044
let target_value = hex::decode("1002080badf00d010203040404ffffffff").unwrap();
20072045
assert_eq!(encoded_value, target_value);
20082046
msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
2009-
if let OnionHopDataFormat::FinalNode = msg.format { } else { panic!(); }
2047+
if let OnionHopDataFormat::FinalNode { payment_data: None } = msg.format { } else { panic!(); }
2048+
assert_eq!(msg.amt_to_forward, 0x0badf00d01020304);
2049+
assert_eq!(msg.outgoing_cltv_value, 0xffffffff);
2050+
}
2051+
2052+
#[test]
2053+
fn encoding_final_onion_hop_data_with_secret() {
2054+
let expected_payment_secret = [0x42u8; 32];
2055+
let mut msg = msgs::OnionHopData {
2056+
format: OnionHopDataFormat::FinalNode {
2057+
payment_data: Some(FinalOnionHopData {
2058+
payment_secret: expected_payment_secret,
2059+
total_msat: 0x1badca1f
2060+
}),
2061+
},
2062+
amt_to_forward: 0x0badf00d01020304,
2063+
outgoing_cltv_value: 0xffffffff,
2064+
};
2065+
let encoded_value = msg.encode();
2066+
let target_value = hex::decode("3602080badf00d010203040404ffffffff082442424242424242424242424242424242424242424242424242424242424242421badca1f").unwrap();
2067+
assert_eq!(encoded_value, target_value);
2068+
msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
2069+
if let OnionHopDataFormat::FinalNode {
2070+
payment_data: Some(FinalOnionHopData {
2071+
payment_secret,
2072+
total_msat: 0x1badca1f
2073+
})
2074+
} = msg.format {
2075+
assert_eq!(payment_secret, expected_payment_secret);
2076+
} else { panic!(); }
20102077
assert_eq!(msg.amt_to_forward, 0x0badf00d01020304);
20112078
assert_eq!(msg.outgoing_cltv_value, 0xffffffff);
20122079
}

lightning/src/ln/onion_utils.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,9 @@ pub(super) fn build_onion_payloads(route: &Route, starting_htlc_offset: u32) ->
123123
res.insert(0, msgs::OnionHopData {
124124
format: if hop.node_features.supports_variable_length_onion() {
125125
if idx == 0 {
126-
msgs::OnionHopDataFormat::FinalNode
126+
msgs::OnionHopDataFormat::FinalNode {
127+
payment_data: None,
128+
}
127129
} else {
128130
msgs::OnionHopDataFormat::NonFinalNode {
129131
short_channel_id: last_short_channel_id,

0 commit comments

Comments
 (0)