Skip to content

Commit 5a42be0

Browse files
Implement receiving keysend payments
1 parent d1e8d9c commit 5a42be0

File tree

1 file changed

+156
-35
lines changed

1 file changed

+156
-35
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 156 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -157,14 +157,20 @@ pub(crate) struct HTLCPreviousHopData {
157157
outpoint: OutPoint,
158158
}
159159

160-
struct ClaimableHTLC {
161-
prev_hop: HTLCPreviousHopData,
162-
value: u64,
160+
enum OnionPayload {
163161
/// Contains a total_msat (which may differ from value if this is a Multi-Path Payment) and a
164162
/// payment_secret which prevents path-probing attacks and can associate different HTLCs which
165163
/// are part of the same payment.
166-
payment_data: msgs::FinalOnionHopData,
164+
Invoice(msgs::FinalOnionHopData),
165+
/// Contains the payer-provided preimage.
166+
Spontaneous(PaymentPreimage),
167+
}
168+
169+
struct ClaimableHTLC {
170+
prev_hop: HTLCPreviousHopData,
167171
cltv_expiry: u32,
172+
value: u64,
173+
onion_payload: OnionPayload,
168174
}
169175

170176
/// Tracks the inbound corresponding to an outbound HTLC
@@ -1475,26 +1481,45 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
14751481
return_err!("Upstream node set CLTV to the wrong value", 18, &byte_utils::be32_to_array(msg.cltv_expiry));
14761482
}
14771483

1478-
let payment_data = match next_hop_data.format {
1479-
msgs::OnionHopDataFormat::Legacy { .. } => None,
1484+
let routing = match next_hop_data.format {
1485+
msgs::OnionHopDataFormat::Legacy { .. } => return_err!("We require payment_secrets", 0x4000|0x2000|3, &[0;0]),
14801486
msgs::OnionHopDataFormat::NonFinalNode { .. } => return_err!("Got non final data with an HMAC of 0", 0x4000 | 22, &[0;0]),
1481-
msgs::OnionHopDataFormat::FinalNode { payment_data, .. } => payment_data,
1482-
};
1487+
msgs::OnionHopDataFormat::FinalNode { payment_data, keysend_preimage } => {
1488+
if payment_data.is_some() && keysend_preimage.is_some() {
1489+
return_err!("We don't support MPP keysend payments", 0x4000|22, &[0;0]);
1490+
} else if let Some(data) = payment_data {
1491+
PendingHTLCRouting::Receive {
1492+
payment_data: data,
1493+
incoming_cltv_expiry: msg.cltv_expiry,
1494+
}
1495+
} else if let Some(payment_preimage) = keysend_preimage {
1496+
// We need to check that the sender knows the keysend preimage before processing this
1497+
// payment further. Otherwise, an intermediary routing hop forwarding non-keysend-HTLC X
1498+
// could discover the final destination of X, by probing the adjacent nodes on the route
1499+
// with a keysend payment of identical payment hash to X and observing the processing
1500+
// time discrepancies due to a hash collision with X.
1501+
let hashed_preimage = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
1502+
if hashed_preimage != msg.payment_hash {
1503+
return_err!("Payment preimage didn't match payment hash", 0x4000|22, &[0;0]);
1504+
}
14831505

1484-
if payment_data.is_none() {
1485-
return_err!("We require payment_secrets", 0x4000|0x2000|3, &[0;0]);
1486-
}
1506+
PendingHTLCRouting::ReceiveKeysend {
1507+
payment_preimage,
1508+
incoming_cltv_expiry: msg.cltv_expiry,
1509+
}
1510+
} else {
1511+
return_err!("We require payment_secrets", 0x4000|0x2000|3, &[0;0]);
1512+
}
1513+
},
1514+
};
14871515

14881516
// Note that we could obviously respond immediately with an update_fulfill_htlc
14891517
// message, however that would leak that we are the recipient of this payment, so
14901518
// instead we stay symmetric with the forwarding case, only responding (after a
14911519
// delay) once they've send us a commitment_signed!
14921520

14931521
PendingHTLCStatus::Forward(PendingHTLCInfo {
1494-
routing: PendingHTLCRouting::Receive {
1495-
payment_data: payment_data.unwrap(),
1496-
incoming_cltv_expiry: msg.cltv_expiry,
1497-
},
1522+
routing,
14981523
payment_hash: msg.payment_hash.clone(),
14991524
incoming_shared_secret: shared_secret,
15001525
amt_to_forward: next_hop_data.amt_to_forward,
@@ -2252,9 +2277,17 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
22522277
for forward_info in pending_forwards.drain(..) {
22532278
match forward_info {
22542279
HTLCForwardInfo::AddHTLC { prev_short_channel_id, prev_htlc_id, forward_info: PendingHTLCInfo {
2255-
routing: PendingHTLCRouting::Receive { payment_data, incoming_cltv_expiry },
2256-
incoming_shared_secret, payment_hash, amt_to_forward, .. },
2280+
routing, incoming_shared_secret, payment_hash, amt_to_forward, .. },
22572281
prev_funding_outpoint } => {
2282+
let (cltv_expiry, onion_payload) = match routing {
2283+
PendingHTLCRouting::Receive { payment_data, incoming_cltv_expiry } =>
2284+
(incoming_cltv_expiry, OnionPayload::Invoice(payment_data)),
2285+
PendingHTLCRouting::ReceiveKeysend { payment_preimage, incoming_cltv_expiry } =>
2286+
(incoming_cltv_expiry, OnionPayload::Spontaneous(payment_preimage)),
2287+
_ => {
2288+
panic!("short_channel_id == 0 should imply any pending_forward entries are of type Receive");
2289+
}
2290+
};
22582291
let claimable_htlc = ClaimableHTLC {
22592292
prev_hop: HTLCPreviousHopData {
22602293
short_channel_id: prev_short_channel_id,
@@ -2263,8 +2296,8 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
22632296
incoming_packet_shared_secret: incoming_shared_secret,
22642297
},
22652298
value: amt_to_forward,
2266-
payment_data: payment_data.clone(),
2267-
cltv_expiry: incoming_cltv_expiry,
2299+
cltv_expiry,
2300+
onion_payload,
22682301
};
22692302

22702303
macro_rules! fail_htlc {
@@ -2293,10 +2326,38 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
22932326
let mut payment_secrets = self.pending_inbound_payments.lock().unwrap();
22942327
match payment_secrets.entry(payment_hash) {
22952328
hash_map::Entry::Vacant(_) => {
2296-
log_trace!(self.logger, "Failing new HTLC with payment_hash {} as we didn't have a corresponding inbound payment.", log_bytes!(payment_hash.0));
2297-
fail_htlc!(claimable_htlc);
2329+
match claimable_htlc.onion_payload {
2330+
OnionPayload::Invoice(_) => {
2331+
log_trace!(self.logger, "Failing new HTLC with payment_hash {} as we didn't have a corresponding inbound payment.", log_bytes!(payment_hash.0));
2332+
fail_htlc!(claimable_htlc);
2333+
},
2334+
OnionPayload::Spontaneous(preimage) => {
2335+
match channel_state.claimable_htlcs.entry(payment_hash) {
2336+
hash_map::Entry::Vacant(e) => {
2337+
e.insert(vec![claimable_htlc]);
2338+
new_events.push(events::Event::PaymentReceived {
2339+
payment_hash,
2340+
amt: amt_to_forward,
2341+
purpose: events::PaymentPurpose::SpontaneousPayment(preimage),
2342+
});
2343+
},
2344+
hash_map::Entry::Occupied(_) => {
2345+
log_trace!(self.logger, "Failing new keysend HTLC with payment_hash {} for a duplicative payment hash", log_bytes!(payment_hash.0));
2346+
fail_htlc!(claimable_htlc);
2347+
}
2348+
}
2349+
}
2350+
}
22982351
},
22992352
hash_map::Entry::Occupied(inbound_payment) => {
2353+
let payment_data =
2354+
if let OnionPayload::Invoice(ref data) = claimable_htlc.onion_payload {
2355+
data.clone()
2356+
} else {
2357+
log_trace!(self.logger, "Failing new keysend HTLC with payment_hash {} because we already have an inbound payment with the same payment hash", log_bytes!(payment_hash.0));
2358+
fail_htlc!(claimable_htlc);
2359+
continue
2360+
};
23002361
if inbound_payment.get().payment_secret != payment_data.payment_secret {
23012362
log_trace!(self.logger, "Failing new HTLC with payment_hash {} as it didn't match our expected payment secret.", log_bytes!(payment_hash.0));
23022363
fail_htlc!(claimable_htlc);
@@ -2308,15 +2369,27 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
23082369
let mut total_value = 0;
23092370
let htlcs = channel_state.claimable_htlcs.entry(payment_hash)
23102371
.or_insert(Vec::new());
2372+
if htlcs.len() == 1 {
2373+
if let OnionPayload::Spontaneous(_) = htlcs[0].onion_payload {
2374+
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));
2375+
fail_htlc!(claimable_htlc);
2376+
continue
2377+
}
2378+
}
23112379
htlcs.push(claimable_htlc);
23122380
for htlc in htlcs.iter() {
23132381
total_value += htlc.value;
2314-
if htlc.payment_data.total_msat != payment_data.total_msat {
2315-
log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the HTLCs had inconsistent total values (eg {} and {})",
2316-
log_bytes!(payment_hash.0), payment_data.total_msat, htlc.payment_data.total_msat);
2317-
total_value = msgs::MAX_VALUE_MSAT;
2382+
match &htlc.onion_payload {
2383+
OnionPayload::Invoice(htlc_payment_data) => {
2384+
if htlc_payment_data.total_msat != payment_data.total_msat {
2385+
log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the HTLCs had inconsistent total values (eg {} and {})",
2386+
log_bytes!(payment_hash.0), payment_data.total_msat, htlc_payment_data.total_msat);
2387+
total_value = msgs::MAX_VALUE_MSAT;
2388+
}
2389+
if total_value >= msgs::MAX_VALUE_MSAT { break; }
2390+
},
2391+
_ => unreachable!(),
23182392
}
2319-
if total_value >= msgs::MAX_VALUE_MSAT { break; }
23202393
}
23212394
if total_value >= msgs::MAX_VALUE_MSAT || total_value > payment_data.total_msat {
23222395
log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the total value {} ran over expected value {} (or HTLCs were inconsistent)",
@@ -2347,9 +2420,6 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
23472420
},
23482421
};
23492422
},
2350-
HTLCForwardInfo::AddHTLC { .. } => {
2351-
panic!("short_channel_id == 0 should imply any pending_forward entries are of type Receive");
2352-
},
23532423
HTLCForwardInfo::FailHTLC { .. } => {
23542424
panic!("Got pending fail of our own HTLC");
23552425
}
@@ -4509,12 +4579,63 @@ impl_writeable_tlv_based!(HTLCPreviousHopData, {
45094579
(6, incoming_packet_shared_secret, required)
45104580
});
45114581

4512-
impl_writeable_tlv_based!(ClaimableHTLC, {
4513-
(0, prev_hop, required),
4514-
(2, value, required),
4515-
(4, payment_data, required),
4516-
(6, cltv_expiry, required),
4517-
});
4582+
impl Writeable for ClaimableHTLC {
4583+
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
4584+
let payment_data = match &self.onion_payload {
4585+
OnionPayload::Invoice(data) => Some(data.clone()),
4586+
_ => None,
4587+
};
4588+
let keysend_preimage = match self.onion_payload {
4589+
OnionPayload::Invoice(_) => None,
4590+
OnionPayload::Spontaneous(preimage) => Some(preimage.clone()),
4591+
};
4592+
write_tlv_fields!
4593+
(writer,
4594+
{
4595+
(0, self.prev_hop, required), (2, self.value, required),
4596+
(4, payment_data, option), (6, self.cltv_expiry, required),
4597+
(8, keysend_preimage, option),
4598+
});
4599+
Ok(())
4600+
}
4601+
}
4602+
4603+
impl Readable for ClaimableHTLC {
4604+
fn read<R: Read>(reader: &mut R) -> Result<Self, DecodeError> {
4605+
let mut prev_hop = ::util::ser::OptionDeserWrapper(None);
4606+
let mut value = 0;
4607+
let mut payment_data: Option<msgs::FinalOnionHopData> = None;
4608+
let mut cltv_expiry = 0;
4609+
let mut keysend_preimage: Option<PaymentPreimage> = None;
4610+
read_tlv_fields!
4611+
(reader,
4612+
{
4613+
(0, prev_hop, required), (2, value, required),
4614+
(4, payment_data, option), (6, cltv_expiry, required),
4615+
(8, keysend_preimage, option)
4616+
});
4617+
let onion_payload = match keysend_preimage {
4618+
Some(p) => {
4619+
if payment_data.is_some() {
4620+
return Err(DecodeError::InvalidValue)
4621+
}
4622+
OnionPayload::Spontaneous(p)
4623+
},
4624+
None => {
4625+
if payment_data.is_none() {
4626+
return Err(DecodeError::InvalidValue)
4627+
}
4628+
OnionPayload::Invoice(payment_data.unwrap())
4629+
},
4630+
};
4631+
Ok(Self {
4632+
prev_hop: prev_hop.0.unwrap(),
4633+
value,
4634+
onion_payload,
4635+
cltv_expiry,
4636+
})
4637+
}
4638+
}
45184639

45194640
impl_writeable_tlv_based_enum!(HTLCSource,
45204641
(0, OutboundRoute) => {

0 commit comments

Comments
 (0)