Skip to content

Commit 20c2522

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 ea3322b commit 20c2522

File tree

3 files changed

+87
-36
lines changed

3 files changed

+87
-36
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ enum PendingForwardReceiveHTLCInfo {
7272
onion_packet: msgs::OnionPacket,
7373
short_channel_id: u64, // This should be NonZero<u64> eventually
7474
},
75-
Receive {},
75+
Receive {
76+
payment_data: Option<msgs::FinalOnionHopData>,
77+
},
7678
}
7779

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

122+
struct ClaimableHTLC {
123+
src: HTLCPreviousHopData,
124+
value: u64,
125+
payment_data: Option<msgs::FinalOnionHopData>,
126+
}
127+
120128
/// Tracks the inbound corresponding to an outbound HTLC
121129
#[derive(Clone, PartialEq)]
122130
pub(super) enum HTLCSource {
@@ -274,12 +282,11 @@ pub(super) struct ChannelHolder<ChanSigner: ChannelKeys> {
274282
/// guarantees are made about the existence of a channel with the short id here, nor the short
275283
/// ids in the PendingHTLCInfo!
276284
pub(super) forward_htlcs: HashMap<u64, Vec<HTLCForwardInfo>>,
277-
/// payment_hash -> Vec<(amount_received, htlc_source)> for tracking things that were to us and
278-
/// can be failed/claimed by the user
285+
/// Tracks things that were to us and can be failed/claimed by the user
279286
/// Note that while this is held in the same mutex as the channels themselves, no consistency
280287
/// guarantees are made about the channels given here actually existing anymore by the time you
281288
/// go to read them!
282-
pub(super) claimable_htlcs: HashMap<PaymentHash, Vec<(u64, HTLCPreviousHopData)>>,
289+
claimable_htlcs: HashMap<PaymentHash, Vec<ClaimableHTLC>>,
283290
/// Messages to send to peers - pushed to in the same lock that they are generated in (except
284291
/// for broadcast messages, where ordering isn't as strict).
285292
pub(super) pending_msg_events: Vec<events::MessageSendEvent>,
@@ -288,7 +295,7 @@ pub(super) struct MutChannelHolder<'a, ChanSigner: ChannelKeys + 'a> {
288295
pub(super) by_id: &'a mut HashMap<[u8; 32], Channel<ChanSigner>>,
289296
pub(super) short_to_id: &'a mut HashMap<u64, [u8; 32]>,
290297
pub(super) forward_htlcs: &'a mut HashMap<u64, Vec<HTLCForwardInfo>>,
291-
pub(super) claimable_htlcs: &'a mut HashMap<PaymentHash, Vec<(u64, HTLCPreviousHopData)>>,
298+
claimable_htlcs: &'a mut HashMap<PaymentHash, Vec<ClaimableHTLC>>,
292299
pub(super) pending_msg_events: &'a mut Vec<events::MessageSendEvent>,
293300
}
294301
impl<ChanSigner: ChannelKeys> ChannelHolder<ChanSigner> {
@@ -993,13 +1000,19 @@ impl<ChanSigner: ChannelKeys> ChannelManager<ChanSigner> {
9931000
return_err!("Upstream node set CLTV to the wrong value", 18, &byte_utils::be32_to_array(msg.cltv_expiry));
9941001
}
9951002

1003+
let payment_data = match next_hop_data.format {
1004+
msgs::OnionHopDataFormat::Legacy { .. } => None,
1005+
msgs::OnionHopDataFormat::NonFinalNode { .. } => return_err!("Got non final data with an HMAC of 0", 0x4000 | 22, &[0;0]),
1006+
msgs::OnionHopDataFormat::FinalNode { payment_data } => payment_data,
1007+
};
1008+
9961009
// Note that we could obviously respond immediately with an update_fulfill_htlc
9971010
// message, however that would leak that we are the recipient of this payment, so
9981011
// instead we stay symmetric with the forwarding case, only responding (after a
9991012
// delay) once they've send us a commitment_signed!
10001013

10011014
PendingHTLCStatus::Forward(PendingHTLCInfo {
1002-
type_data: PendingForwardReceiveHTLCInfo::Receive {},
1015+
type_data: PendingForwardReceiveHTLCInfo::Receive { payment_data },
10031016
payment_hash: msg.payment_hash.clone(),
10041017
incoming_shared_secret: shared_secret,
10051018
amt_to_forward: next_hop_data.amt_to_forward,
@@ -1548,17 +1561,18 @@ impl<ChanSigner: ChannelKeys> ChannelManager<ChanSigner> {
15481561
for forward_info in pending_forwards.drain(..) {
15491562
match forward_info {
15501563
HTLCForwardInfo::AddHTLC { prev_short_channel_id, prev_htlc_id, forward_info: PendingHTLCInfo {
1551-
type_data: PendingForwardReceiveHTLCInfo::Receive { },
1564+
type_data: PendingForwardReceiveHTLCInfo::Receive { payment_data },
15521565
incoming_shared_secret, payment_hash, amt_to_forward, .. }, } => {
15531566
let prev_hop_data = HTLCPreviousHopData {
15541567
short_channel_id: prev_short_channel_id,
15551568
htlc_id: prev_htlc_id,
15561569
incoming_packet_shared_secret: incoming_shared_secret,
15571570
};
1558-
match channel_state.claimable_htlcs.entry(payment_hash) {
1559-
hash_map::Entry::Occupied(mut entry) => entry.get_mut().push((amt_to_forward, prev_hop_data)),
1560-
hash_map::Entry::Vacant(entry) => { entry.insert(vec![(amt_to_forward, prev_hop_data)]); },
1561-
};
1571+
channel_state.claimable_htlcs.entry(payment_hash).or_insert(Vec::new()).push(ClaimableHTLC {
1572+
src: prev_hop_data,
1573+
value: amt_to_forward,
1574+
payment_data,
1575+
});
15621576
new_events.push(events::Event::PaymentReceived {
15631577
payment_hash: payment_hash,
15641578
amt: amt_to_forward,
@@ -1631,11 +1645,11 @@ impl<ChanSigner: ChannelKeys> ChannelManager<ChanSigner> {
16311645
let mut channel_state = Some(self.channel_state.lock().unwrap());
16321646
let removed_source = channel_state.as_mut().unwrap().claimable_htlcs.remove(payment_hash);
16331647
if let Some(mut sources) = removed_source {
1634-
for (recvd_value, htlc_with_hash) in sources.drain(..) {
1648+
for htlc in sources.drain(..) {
16351649
if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap()); }
16361650
self.fail_htlc_backwards_internal(channel_state.take().unwrap(),
1637-
HTLCSource::PreviousHopData(htlc_with_hash), payment_hash,
1638-
HTLCFailReason::Reason { failure_code: 0x4000 | 15, data: byte_utils::be64_to_array(recvd_value).to_vec() });
1651+
HTLCSource::PreviousHopData(htlc.src), payment_hash,
1652+
HTLCFailReason::Reason { failure_code: 0x4000 | 15, data: byte_utils::be64_to_array(htlc.value).to_vec() });
16391653
}
16401654
true
16411655
} else { false }
@@ -1759,17 +1773,17 @@ impl<ChanSigner: ChannelKeys> ChannelManager<ChanSigner> {
17591773
let mut channel_state = Some(self.channel_state.lock().unwrap());
17601774
let removed_source = channel_state.as_mut().unwrap().claimable_htlcs.remove(&payment_hash);
17611775
if let Some(mut sources) = removed_source {
1762-
for (received_amount, htlc_with_hash) in sources.drain(..) {
1776+
for htlc in sources.drain(..) {
17631777
if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap()); }
1764-
if received_amount < expected_amount || received_amount > expected_amount * 2 {
1765-
let mut htlc_msat_data = byte_utils::be64_to_array(received_amount).to_vec();
1778+
if htlc.value < expected_amount || htlc.value > expected_amount * 2 {
1779+
let mut htlc_msat_data = byte_utils::be64_to_array(htlc.value).to_vec();
17661780
let mut height_data = byte_utils::be32_to_array(self.latest_block_height.load(Ordering::Acquire) as u32).to_vec();
17671781
htlc_msat_data.append(&mut height_data);
17681782
self.fail_htlc_backwards_internal(channel_state.take().unwrap(),
1769-
HTLCSource::PreviousHopData(htlc_with_hash), &payment_hash,
1783+
HTLCSource::PreviousHopData(htlc.src), &payment_hash,
17701784
HTLCFailReason::Reason { failure_code: 0x4000|15, data: htlc_msat_data });
17711785
} else {
1772-
self.claim_funds_internal(channel_state.take().unwrap(), HTLCSource::PreviousHopData(htlc_with_hash), payment_preimage);
1786+
self.claim_funds_internal(channel_state.take().unwrap(), HTLCSource::PreviousHopData(htlc.src), payment_preimage);
17731787
}
17741788
}
17751789
true
@@ -3079,8 +3093,9 @@ impl Writeable for PendingHTLCInfo {
30793093
onion_packet.write(writer)?;
30803094
short_channel_id.write(writer)?;
30813095
},
3082-
&PendingForwardReceiveHTLCInfo::Receive { } => {
3096+
&PendingForwardReceiveHTLCInfo::Receive { ref payment_data } => {
30833097
1u8.write(writer)?;
3098+
payment_data.write(writer)?;
30843099
},
30853100
}
30863101
self.incoming_shared_secret.write(writer)?;
@@ -3099,7 +3114,9 @@ impl<R: ::std::io::Read> Readable<R> for PendingHTLCInfo {
30993114
onion_packet: Readable::read(reader)?,
31003115
short_channel_id: Readable::read(reader)?,
31013116
},
3102-
1u8 => PendingForwardReceiveHTLCInfo::Receive { },
3117+
1u8 => PendingForwardReceiveHTLCInfo::Receive {
3118+
payment_data: Readable::read(reader)?,
3119+
},
31033120
_ => return Err(DecodeError::InvalidValue),
31043121
},
31053122
incoming_shared_secret: Readable::read(reader)?,
@@ -3304,9 +3321,10 @@ impl<ChanSigner: ChannelKeys + Writeable> Writeable for ChannelManager<ChanSigne
33043321
for (payment_hash, previous_hops) in channel_state.claimable_htlcs.iter() {
33053322
payment_hash.write(writer)?;
33063323
(previous_hops.len() as u64).write(writer)?;
3307-
for &(recvd_amt, ref previous_hop) in previous_hops.iter() {
3308-
recvd_amt.write(writer)?;
3309-
previous_hop.write(writer)?;
3324+
for htlc in previous_hops.iter() {
3325+
htlc.src.write(writer)?;
3326+
htlc.value.write(writer)?;
3327+
htlc.payment_data.write(writer)?;
33103328
}
33113329
}
33123330

@@ -3448,7 +3466,11 @@ impl<'a, R : ::std::io::Read, ChanSigner: ChannelKeys + Readable<R>> ReadableArg
34483466
let previous_hops_len: u64 = Readable::read(reader)?;
34493467
let mut previous_hops = Vec::with_capacity(cmp::min(previous_hops_len as usize, 2));
34503468
for _ in 0..previous_hops_len {
3451-
previous_hops.push((Readable::read(reader)?, Readable::read(reader)?));
3469+
previous_hops.push(ClaimableHTLC {
3470+
src: Readable::read(reader)?,
3471+
value: Readable::read(reader)?,
3472+
payment_data: Readable::read(reader)?,
3473+
});
34523474
}
34533475
claimable_htlcs.insert(payment_hash, previous_hops);
34543476
}

lightning/src/ln/msgs.rs

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,11 @@ pub trait RoutingMessageHandler : Send + Sync {
660660
mod fuzzy_internal_msgs {
661661
// These types aren't intended to be pub, but are exposed for direct fuzzing (as we deserialize
662662
// them from untrusted input):
663+
#[derive(Clone)]
664+
pub(crate) struct FinalOnionHopData {
665+
pub(crate) payment_secret: [u8; 32],
666+
pub(crate) total_msat: u64,
667+
}
663668

664669
pub(crate) enum OnionHopDataFormat {
665670
Legacy { // aka Realm-0
@@ -668,7 +673,9 @@ mod fuzzy_internal_msgs {
668673
NonFinalNode {
669674
short_channel_id: u64,
670675
},
671-
FinalNode,
676+
FinalNode {
677+
payment_data: Option<FinalOnionHopData>,
678+
},
672679
}
673680

674681
pub struct OnionHopData {
@@ -1013,6 +1020,11 @@ impl_writeable!(UpdateAddHTLC, 32+8+8+32+4+1366, {
10131020
onion_routing_packet
10141021
});
10151022

1023+
impl_writeable!(FinalOnionHopData, 32+8, {
1024+
payment_secret,
1025+
total_msat
1026+
});
1027+
10161028
impl Writeable for OnionHopData {
10171029
fn write<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> {
10181030
w.size_hint(33);
@@ -1030,11 +1042,19 @@ impl Writeable for OnionHopData {
10301042
(6, short_channel_id)
10311043
});
10321044
},
1033-
OnionHopDataFormat::FinalNode => {
1034-
encode_varint_length_prefixed_tlv!(w, {
1035-
(2, self.amt_to_forward),
1036-
(4, self.outgoing_cltv_value)
1037-
});
1045+
OnionHopDataFormat::FinalNode { ref payment_data } => {
1046+
if let &Some(ref final_data) = payment_data {
1047+
encode_varint_length_prefixed_tlv!(w, {
1048+
(2, self.amt_to_forward),
1049+
(4, self.outgoing_cltv_value),
1050+
(8, final_data)
1051+
});
1052+
} else {
1053+
encode_varint_length_prefixed_tlv!(w, {
1054+
(2, self.amt_to_forward),
1055+
(4, self.outgoing_cltv_value)
1056+
});
1057+
}
10381058
},
10391059
}
10401060
match self.format {
@@ -1060,19 +1080,24 @@ impl<R: Read> Readable<R> for OnionHopData {
10601080
let mut amt: u64 = 0;
10611081
let mut cltv_value: u32 = 0;
10621082
let mut short_id: Option<u64> = None;
1083+
let mut payment_data: Option<FinalOnionHopData> = None;
10631084
decode_tlv!(&mut rd, {
10641085
(2, amt),
10651086
(4, cltv_value)
10661087
}, {
1067-
(6, short_id)
1088+
(6, short_id),
1089+
(8, payment_data)
10681090
});
10691091
rd.eat_remaining().map_err(|_| DecodeError::ShortRead)?;
10701092
let format = if let Some(short_channel_id) = short_id {
1093+
if payment_data.is_some() { return Err(DecodeError::InvalidValue); }
10711094
OnionHopDataFormat::NonFinalNode {
10721095
short_channel_id,
10731096
}
10741097
} else {
1075-
OnionHopDataFormat::FinalNode
1098+
OnionHopDataFormat::FinalNode {
1099+
payment_data
1100+
}
10761101
};
10771102
(format, amt, cltv_value)
10781103
} else {
@@ -2067,15 +2092,17 @@ mod tests {
20672092
#[test]
20682093
fn encoding_final_onion_hop_data() {
20692094
let mut msg = msgs::OnionHopData {
2070-
format: OnionHopDataFormat::FinalNode,
2095+
format: OnionHopDataFormat::FinalNode {
2096+
payment_data: None,
2097+
},
20712098
amt_to_forward: 0x0badf00d01020304,
20722099
outgoing_cltv_value: 0xffffffff,
20732100
};
20742101
let encoded_value = msg.encode();
20752102
let target_value = hex::decode("1002080badf00d010203040404ffffffff").unwrap();
20762103
assert_eq!(encoded_value, target_value);
20772104
msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
2078-
if let OnionHopDataFormat::FinalNode = msg.format { } else { panic!(); }
2105+
if let OnionHopDataFormat::FinalNode { payment_data: None } = msg.format { } else { panic!(); }
20792106
assert_eq!(msg.amt_to_forward, 0x0badf00d01020304);
20802107
assert_eq!(msg.outgoing_cltv_value, 0xffffffff);
20812108
}

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)