Skip to content

Commit c42f450

Browse files
add ability to intercept htlcs
1 parent 7544030 commit c42f450

File tree

3 files changed

+249
-15
lines changed

3 files changed

+249
-15
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 169 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ pub(super) struct PendingHTLCInfo {
114114
payment_hash: PaymentHash,
115115
pub(super) amt_to_forward: u64,
116116
pub(super) outgoing_cltv_value: u32,
117+
pub(super) amt_incoming: Option<u64>
117118
}
118119

119120
#[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
@@ -129,6 +130,13 @@ pub(super) enum PendingHTLCStatus {
129130
Fail(HTLCFailureMsg),
130131
}
131132

133+
pub(super) struct PendingInterceptedHTLC {
134+
forward_info: PendingHTLCInfo,
135+
prev_short_channel_id: u64,
136+
prev_htlc_id: u64,
137+
prev_funding_outpoint: OutPoint,
138+
}
139+
132140
pub(super) enum HTLCForwardInfo {
133141
AddHTLC {
134142
forward_info: PendingHTLCInfo,
@@ -186,6 +194,24 @@ struct ClaimableHTLC {
186194
total_msat: u64,
187195
}
188196

197+
/// An identifier used to uniquely identify an intercepted htlc to LDK.
198+
/// (C-not exported) as we just use [u8; 32] directly
199+
#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)]
200+
pub struct InterceptId(pub [u8; 32]);
201+
202+
impl Writeable for InterceptId {
203+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
204+
self.0.write(w)
205+
}
206+
}
207+
208+
impl Readable for InterceptId {
209+
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
210+
let buf: [u8; 32] = Readable::read(r)?;
211+
Ok(InterceptId(buf))
212+
}
213+
}
214+
189215
/// A payment identifier used to uniquely identify a payment to LDK.
190216
/// (C-not exported) as we just use [u8; 32] directly
191217
#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)]
@@ -761,6 +787,9 @@ pub struct ChannelManager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref,
761787
pub(super) forward_htlcs: Mutex<HashMap<u64, Vec<HTLCForwardInfo>>>,
762788
#[cfg(not(test))]
763789
forward_htlcs: Mutex<HashMap<u64, Vec<HTLCForwardInfo>>>,
790+
/// Storage for PendingInterceptedHTLC's that have been intercepted and bubbled up to the user.
791+
/// We hold them here until the user tells us what we should to with them.
792+
pending_intercepted_payments: Mutex<HashMap<InterceptId, PendingInterceptedHTLC>>,
764793

765794
/// The set of outbound SCID aliases across all our channels, including unconfirmed channels
766795
/// and some closed channels which reached a usable state prior to being closed. This is used
@@ -1634,6 +1663,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
16341663
pending_outbound_payments: Mutex::new(HashMap::new()),
16351664
forward_htlcs: Mutex::new(HashMap::new()),
16361665
id_to_peer: Mutex::new(HashMap::new()),
1666+
pending_intercepted_payments: Mutex::new(HashMap::new()),
16371667

16381668
our_network_key: keys_manager.get_node_secret(Recipient::Node).unwrap(),
16391669
our_network_pubkey: PublicKey::from_secret_key(&secp_ctx, &keys_manager.get_node_secret(Recipient::Node).unwrap()),
@@ -2163,6 +2193,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
21632193
payment_hash,
21642194
incoming_shared_secret: shared_secret,
21652195
amt_to_forward: amt_msat,
2196+
amt_incoming: Some(amt_msat),
21662197
outgoing_cltv_value: hop_data.outgoing_cltv_value,
21672198
})
21682199
}
@@ -2260,6 +2291,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
22602291
incoming_shared_secret: shared_secret,
22612292
amt_to_forward: next_hop_data.amt_to_forward,
22622293
outgoing_cltv_value: next_hop_data.outgoing_cltv_value,
2294+
amt_incoming: Some(msg.amount_msat)
22632295
})
22642296
}
22652297
};
@@ -3030,6 +3062,66 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
30303062
Ok(())
30313063
}
30323064

3065+
/// Fails the intercepted payment indicated by intercept_id. This should really only be called in response
3066+
/// to a PaymentIntercepted event
3067+
pub fn fail_intercepted_payment(&self, intercept_id: InterceptId) {
3068+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
3069+
3070+
let pending_intercept = {
3071+
let mut pending_intercepts = self.pending_intercepted_payments.lock().unwrap();
3072+
pending_intercepts.remove(&intercept_id)
3073+
};
3074+
3075+
if let Some(payment) = pending_intercept {
3076+
if let PendingHTLCRouting::Forward { short_channel_id, .. } = payment.forward_info.routing {
3077+
let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
3078+
short_channel_id: payment.prev_short_channel_id,
3079+
outpoint: payment.prev_funding_outpoint,
3080+
htlc_id: payment.prev_htlc_id,
3081+
incoming_packet_shared_secret: payment.forward_info.incoming_shared_secret,
3082+
phantom_shared_secret: None,
3083+
});
3084+
3085+
let failure_reason = HTLCFailReason::Reason { failure_code: 0x4000 | 10, data: Vec::new() };
3086+
let destination = HTLCDestination::UnknownNextHop { requested_forward_scid: short_channel_id };
3087+
self.fail_htlc_backwards_internal(htlc_source, &payment.forward_info.payment_hash, failure_reason, destination);
3088+
}
3089+
}
3090+
}
3091+
3092+
/// Attempts to forward an intercepted payment over the provided scid and with the provided amt_to_forward.
3093+
/// Should only really be called in response to a PaymentIntercepted event
3094+
pub fn forward_intercepted_payment(&self, intercept_id: InterceptId, scid: u64, amt_to_forward: u64) -> Result<(), APIError> {
3095+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
3096+
3097+
let pending_intercept = {
3098+
let mut pending_intercepts = self.pending_intercepted_payments.lock().unwrap();
3099+
pending_intercepts.remove(&intercept_id)
3100+
};
3101+
3102+
match pending_intercept {
3103+
None => Err(APIError::APIMisuseError { err: "Payment with that InterceptId not found".to_string() }),
3104+
Some(payment) => {
3105+
let routing = match payment.forward_info.routing {
3106+
PendingHTLCRouting::Forward { onion_packet, .. } => {
3107+
PendingHTLCRouting::Forward { onion_packet, short_channel_id: scid }
3108+
},
3109+
_ => payment.forward_info.routing
3110+
};
3111+
3112+
let pending_htlc_info = PendingHTLCInfo {
3113+
amt_to_forward,
3114+
routing,
3115+
..payment.forward_info
3116+
};
3117+
3118+
let mut per_source_pending_forward = vec![(payment.prev_short_channel_id, payment.prev_funding_outpoint, vec![(pending_htlc_info, payment.prev_htlc_id)])];
3119+
self.forward_htlcs(&mut per_source_pending_forward);
3120+
Ok(())
3121+
}
3122+
}
3123+
}
3124+
30333125
/// Processes HTLCs which are pending waiting on random forward delay.
30343126
///
30353127
/// Should only really ever be called in response to a PendingHTLCsForwardable event.
@@ -3054,9 +3146,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
30543146
None => {
30553147
for forward_info in pending_forwards.drain(..) {
30563148
match forward_info {
3057-
HTLCForwardInfo::AddHTLC { prev_short_channel_id, prev_htlc_id, forward_info: PendingHTLCInfo {
3058-
routing, incoming_shared_secret, payment_hash, amt_to_forward, outgoing_cltv_value },
3059-
prev_funding_outpoint } => {
3149+
HTLCForwardInfo::AddHTLC { prev_short_channel_id, prev_htlc_id, forward_info, prev_funding_outpoint } => {
30603150
macro_rules! failure_handler {
30613151
($msg: expr, $err_code: expr, $err_data: expr, $phantom_ss: expr, $next_hop_unknown: expr) => {
30623152
log_info!(self.logger, "Failed to accept/forward incoming HTLC: {}", $msg);
@@ -3065,17 +3155,17 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
30653155
short_channel_id: prev_short_channel_id,
30663156
outpoint: prev_funding_outpoint,
30673157
htlc_id: prev_htlc_id,
3068-
incoming_packet_shared_secret: incoming_shared_secret,
3158+
incoming_packet_shared_secret: forward_info.incoming_shared_secret,
30693159
phantom_shared_secret: $phantom_ss,
30703160
});
30713161

30723162
let reason = if $next_hop_unknown {
30733163
HTLCDestination::UnknownNextHop { requested_forward_scid: short_chan_id }
30743164
} else {
3075-
HTLCDestination::FailedPayment{ payment_hash }
3165+
HTLCDestination::FailedPayment{ payment_hash: forward_info.payment_hash }
30763166
};
30773167

3078-
failed_forwards.push((htlc_source, payment_hash,
3168+
failed_forwards.push((htlc_source, forward_info.payment_hash,
30793169
HTLCFailReason::Reason { failure_code: $err_code, data: $err_data },
30803170
reason
30813171
));
@@ -3096,11 +3186,11 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
30963186
}
30973187
}
30983188
}
3099-
if let PendingHTLCRouting::Forward { onion_packet, .. } = routing {
3189+
if let PendingHTLCRouting::Forward { ref onion_packet, .. } = forward_info.routing {
31003190
let phantom_secret_res = self.keys_manager.get_node_secret(Recipient::PhantomNode);
31013191
if phantom_secret_res.is_ok() && fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, short_chan_id) {
31023192
let phantom_shared_secret = SharedSecret::new(&onion_packet.public_key.unwrap(), &phantom_secret_res.unwrap()).secret_bytes();
3103-
let next_hop = match onion_utils::decode_next_payment_hop(phantom_shared_secret, &onion_packet.hop_data, onion_packet.hmac, payment_hash) {
3193+
let next_hop = match onion_utils::decode_next_payment_hop(phantom_shared_secret, &onion_packet.hop_data, onion_packet.hmac, forward_info.payment_hash) {
31043194
Ok(res) => res,
31053195
Err(onion_utils::OnionDecodeErr::Malformed { err_msg, err_code }) => {
31063196
let sha256_of_onion = Sha256::hash(&onion_packet.hop_data).into_inner();
@@ -3116,13 +3206,37 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
31163206
};
31173207
match next_hop {
31183208
onion_utils::Hop::Receive(hop_data) => {
3119-
match self.construct_recv_pending_htlc_info(hop_data, incoming_shared_secret, payment_hash, amt_to_forward, outgoing_cltv_value, Some(phantom_shared_secret)) {
3209+
match self.construct_recv_pending_htlc_info(hop_data, forward_info.incoming_shared_secret, forward_info.payment_hash, forward_info.amt_to_forward, forward_info.outgoing_cltv_value, Some(phantom_shared_secret)) {
31203210
Ok(info) => phantom_receives.push((prev_short_channel_id, prev_funding_outpoint, vec![(info, prev_htlc_id)])),
31213211
Err(ReceiveError { err_code, err_data, msg }) => failed_payment!(msg, err_code, err_data, Some(phantom_shared_secret))
31223212
}
31233213
},
31243214
_ => panic!(),
31253215
}
3216+
} else if forward_info.amt_incoming.is_some() && fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, short_chan_id) {
3217+
let intercept_id = InterceptId(Sha256::hash(&forward_info.incoming_shared_secret).into_inner());
3218+
let mut pending_intercepts = self.pending_intercepted_payments.lock().unwrap();
3219+
match pending_intercepts.entry(intercept_id) {
3220+
hash_map::Entry::Vacant(entry) => {
3221+
let pending_intercepted_payment = PendingInterceptedHTLC {
3222+
forward_info: forward_info.clone(),
3223+
prev_short_channel_id,
3224+
prev_htlc_id,
3225+
prev_funding_outpoint,
3226+
};
3227+
entry.insert(pending_intercepted_payment);
3228+
new_events.push(events::Event::PaymentIntercepted {
3229+
short_channel_id: short_chan_id,
3230+
payment_hash: forward_info.payment_hash,
3231+
inbound_amount_msats: forward_info.amt_incoming.unwrap(),
3232+
expected_outbound_amount_msats: forward_info.amt_to_forward,
3233+
intercept_id
3234+
});
3235+
},
3236+
hash_map::Entry::Occupied(_) => {
3237+
fail_forward!(format!("Detected duplicate intercepted payment over short channel id {} for forward HTLC", short_chan_id), 0x4000 | 10, Vec::new(), None);
3238+
}
3239+
}
31263240
} else {
31273241
fail_forward!(format!("Unknown short channel id {} for forward HTLC", short_chan_id), 0x4000 | 10, Vec::new(), None);
31283242
}
@@ -3149,8 +3263,8 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
31493263
HTLCForwardInfo::AddHTLC { prev_short_channel_id, prev_htlc_id, forward_info: PendingHTLCInfo {
31503264
routing: PendingHTLCRouting::Forward {
31513265
onion_packet, ..
3152-
}, incoming_shared_secret, payment_hash, amt_to_forward, outgoing_cltv_value },
3153-
prev_funding_outpoint } => {
3266+
}, incoming_shared_secret, payment_hash, amt_to_forward, outgoing_cltv_value, .. },
3267+
prev_funding_outpoint, .. } => {
31543268
log_trace!(self.logger, "Adding HTLC from short id {} with payment_hash {} to channel with short id {} after delay", prev_short_channel_id, log_bytes!(payment_hash.0), short_chan_id);
31553269
let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
31563270
short_channel_id: prev_short_channel_id,
@@ -3274,7 +3388,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
32743388
match forward_info {
32753389
HTLCForwardInfo::AddHTLC { prev_short_channel_id, prev_htlc_id, forward_info: PendingHTLCInfo {
32763390
routing, incoming_shared_secret, payment_hash, amt_to_forward, .. },
3277-
prev_funding_outpoint } => {
3391+
prev_funding_outpoint, .. } => {
32783392
let (cltv_expiry, onion_payload, payment_data, phantom_shared_secret) = match routing {
32793393
PendingHTLCRouting::Receive { payment_data, incoming_cltv_expiry, phantom_shared_secret } => {
32803394
let _legacy_hop_data = Some(payment_data.clone());
@@ -5521,6 +5635,21 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
55215635
inbound_payment::get_payment_preimage(payment_hash, payment_secret, &self.inbound_payment_key)
55225636
}
55235637

5638+
/// Gets a fake short channel id for use in receiving [intercepted payments]. These fake scids
5639+
/// are used when constructing the route hints for payments intended to be intercepted.
5640+
pub fn get_intercept_scid(&self) -> u64 {
5641+
let mut channel_state = self.channel_state.lock().unwrap();
5642+
let best_block = self.best_block.read().unwrap();
5643+
loop {
5644+
let scid_candidate = fake_scid::Namespace::Intercept.get_fake_scid(best_block.height(), &self.genesis_hash, &self.fake_scid_rand_bytes, &self.keys_manager);
5645+
// Ensure the generated scid doesn't conflict with a real channel.
5646+
match channel_state.short_to_chan_info.entry(scid_candidate) {
5647+
hash_map::Entry::Occupied(_) => continue,
5648+
hash_map::Entry::Vacant(_) => return scid_candidate
5649+
}
5650+
}
5651+
}
5652+
55245653
/// Gets a fake short channel id for use in receiving [phantom node payments]. These fake scids
55255654
/// are used when constructing the phantom invoice's route hints.
55265655
///
@@ -6324,7 +6453,8 @@ impl_writeable_tlv_based!(PendingHTLCInfo, {
63246453
(2, incoming_shared_secret, required),
63256454
(4, payment_hash, required),
63266455
(6, amt_to_forward, required),
6327-
(8, outgoing_cltv_value, required)
6456+
(8, outgoing_cltv_value, required),
6457+
(9, amt_incoming, option),
63286458
});
63296459

63306460

@@ -6567,6 +6697,13 @@ impl_writeable_tlv_based!(PendingInboundPayment, {
65676697
(8, min_value_msat, required),
65686698
});
65696699

6700+
impl_writeable_tlv_based!(PendingInterceptedHTLC, {
6701+
(0, forward_info, required),
6702+
(2, prev_short_channel_id, required),
6703+
(4, prev_htlc_id, required),
6704+
(6, prev_funding_outpoint, required)
6705+
});
6706+
65706707
impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
65716708
(0, Legacy) => {
65726709
(0, session_privs, required),
@@ -6661,6 +6798,8 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable f
66616798

66626799
let pending_inbound_payments = self.pending_inbound_payments.lock().unwrap();
66636800
let pending_outbound_payments = self.pending_outbound_payments.lock().unwrap();
6801+
let pending_intercepted_payments = self.pending_intercepted_payments.lock().unwrap();
6802+
66646803
let events = self.pending_events.lock().unwrap();
66656804
(events.len() as u64).write(writer)?;
66666805
for event in events.iter() {
@@ -6732,6 +6871,12 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable f
67326871
(11, self.probing_cookie_secret, required),
67336872
});
67346873

6874+
(pending_intercepted_payments.len() as u64).write(writer)?;
6875+
for (intercept_id, pending_intercepted_payment) in pending_intercepted_payments.iter() {
6876+
intercept_id.write(writer)?;
6877+
pending_intercepted_payment.write(writer)?;
6878+
}
6879+
67356880
Ok(())
67366881
}
67376882
}
@@ -7032,14 +7177,23 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
70327177
let mut fake_scid_rand_bytes: Option<[u8; 32]> = None;
70337178
let mut probing_cookie_secret: Option<[u8; 32]> = None;
70347179
let mut claimable_htlc_purposes = None;
7035-
read_tlv_fields!(reader, {
7180+
read_tlv_fields!(reader.by_ref(), {
70367181
(1, pending_outbound_payments_no_retry, option),
70377182
(3, pending_outbound_payments, option),
70387183
(5, received_network_pubkey, option),
70397184
(7, fake_scid_rand_bytes, option),
70407185
(9, claimable_htlc_purposes, vec_type),
70417186
(11, probing_cookie_secret, option),
70427187
});
7188+
7189+
let pending_intercepted_payment_count: u64 = Readable::read(reader)?;
7190+
let mut pending_intercepted_payments: HashMap<InterceptId, PendingInterceptedHTLC> = HashMap::with_capacity(cmp::min(pending_intercepted_payment_count as usize, MAX_ALLOC_SIZE/(3*32)));
7191+
for _ in 0..pending_intercepted_payment_count {
7192+
if pending_intercepted_payments.insert(Readable::read(reader)?, Readable::read(reader)?).is_some() {
7193+
return Err(DecodeError::InvalidValue);
7194+
}
7195+
}
7196+
70437197
if fake_scid_rand_bytes.is_none() {
70447198
fake_scid_rand_bytes = Some(args.keys_manager.get_secure_random_bytes());
70457199
}
@@ -7250,6 +7404,7 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
72507404
inbound_payment_key: expanded_inbound_key,
72517405
pending_inbound_payments: Mutex::new(pending_inbound_payments),
72527406
pending_outbound_payments: Mutex::new(pending_outbound_payments.unwrap()),
7407+
pending_intercepted_payments: Mutex::new(pending_intercepted_payments),
72537408

72547409
forward_htlcs: Mutex::new(forward_htlcs),
72557410
outbound_scid_aliases: Mutex::new(outbound_scid_aliases),

0 commit comments

Comments
 (0)