pub(super) enum PendingHTLCRouting {
Forward {
onion_packet: msgs::OnionPacket,
- /// The SCID from the onion that we should forward to. This could be a "real" SCID, an
- /// outbound SCID alias, or a phantom node SCID.
+ /// The SCID from the onion that we should forward to. This could be a real SCID or a fake one
+ /// generated using `get_fake_scid` from the scid_utils::fake_scid module.
short_channel_id: u64, // This should be NonZero<u64> eventually when we bump MSRV
},
Receive {
prev_short_channel_id: u64,
prev_htlc_id: u64,
prev_funding_outpoint: OutPoint,
+ prev_user_channel_id: u128,
}
pub(super) enum HTLCForwardInfo {
Ok(PaymentId(buf))
}
}
+
+/// An identifier used to uniquely identify an intercepted HTLC to LDK.
+/// (C-not exported) as we just use [u8; 32] directly
+#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)]
+pub struct InterceptId(pub [u8; 32]);
+
+impl Writeable for InterceptId {
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+ self.0.write(w)
+ }
+}
+
+impl Readable for InterceptId {
+ fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+ let buf: [u8; 32] = Readable::read(r)?;
+ Ok(InterceptId(buf))
+ }
+}
/// Tracks the inbound corresponding to an outbound HTLC
#[allow(clippy::derive_hash_xor_eq)] // Our Hash is faithful to the data, we just don't have SecretKey::hash
#[derive(Clone, PartialEq, Eq)]
// `total_consistency_lock`
// |
// |__`forward_htlcs`
+// | |
+// | |__`pending_intercepted_htlcs`
// |
// |__`pending_inbound_payments`
// | |
pub(super) forward_htlcs: Mutex<HashMap<u64, Vec<HTLCForwardInfo>>>,
#[cfg(not(test))]
forward_htlcs: Mutex<HashMap<u64, Vec<HTLCForwardInfo>>>,
+ /// Storage for HTLCs that have been intercepted and bubbled up to the user. We hold them here
+ /// until the user tells us what we should do with them.
+ ///
+ /// See `ChannelManager` struct-level documentation for lock order requirements.
+ pending_intercepted_htlcs: Mutex<HashMap<InterceptId, PendingAddHTLCInfo>>,
/// Map from payment hash to the payment data and any HTLCs which are to us and can be
/// failed/claimed by the user.
/// [`ChannelHandshakeConfig::minimum_depth`]: crate::util::config::ChannelHandshakeConfig::minimum_depth
/// [`ChannelHandshakeLimits::max_minimum_depth`]: crate::util::config::ChannelHandshakeLimits::max_minimum_depth
pub confirmations_required: Option<u32>,
+ /// The current number of confirmations on the funding transaction.
+ ///
+ /// This value will be `None` for objects serialized with LDK versions prior to 0.0.113.
+ pub confirmations: Option<u32>,
/// The number of blocks (after our commitment transaction confirms) that we will need to wait
/// until we can claim our funds after we force-close the channel. During this time our
/// counterparty is allowed to punish us if we broadcasted a stale state. If our counterparty
pending_outbound_payments: Mutex::new(HashMap::new()),
forward_htlcs: Mutex::new(HashMap::new()),
claimable_htlcs: Mutex::new(HashMap::new()),
+ pending_intercepted_htlcs: Mutex::new(HashMap::new()),
id_to_peer: Mutex::new(HashMap::new()),
short_to_chan_info: FairRwLock::new(HashMap::new()),
let mut res = Vec::new();
{
let channel_state = self.channel_state.lock().unwrap();
+ let best_block_height = self.best_block.read().unwrap().height();
res.reserve(channel_state.by_id.len());
for (channel_id, channel) in channel_state.by_id.iter().filter(f) {
let balance = channel.get_available_balances();
next_outbound_htlc_limit_msat: balance.next_outbound_htlc_limit_msat,
user_channel_id: channel.get_user_id(),
confirmations_required: channel.minimum_depth(),
+ confirmations: Some(channel.get_funding_tx_confirmations(best_block_height)),
force_close_spend_delay: channel.get_counterparty_selected_contest_delay(),
is_outbound: channel.is_outbound(),
is_channel_ready: channel.is_usable(),
let forwarding_id_opt = match id_option {
None => { // unknown_next_peer
// Note that this is likely a timing oracle for detecting whether an scid is a
- // phantom.
- if fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash) {
+ // phantom or an intercept.
+ if fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash) ||
+ fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash)
+ {
None
} else {
break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
Ok(())
}
+ /// Attempts to forward an intercepted HTLC over the provided channel id and with the provided
+ /// amount to forward. Should only be called in response to an [`HTLCIntercepted`] event.
+ ///
+ /// Intercepted HTLCs can be useful for Lightning Service Providers (LSPs) to open a just-in-time
+ /// channel to a receiving node if the node lacks sufficient inbound liquidity.
+ ///
+ /// To make use of intercepted HTLCs, use [`ChannelManager::get_intercept_scid`] to generate short
+ /// channel id(s) to put in the receiver's invoice route hints. These route hints will signal to
+ /// LDK to generate an [`HTLCIntercepted`] event when it receives the forwarded HTLC.
+ ///
+ /// Note that LDK does not enforce fee requirements in `amt_to_forward_msat`, and will not stop
+ /// you from forwarding more than you received.
+ ///
+ /// [`HTLCIntercepted`]: events::Event::HTLCIntercepted
+ // TODO: when we move to deciding the best outbound channel at forward time, only take
+ // `next_node_id` and not `next_hop_channel_id`
+ pub fn forward_intercepted_htlc(&self, intercept_id: InterceptId, next_hop_channel_id: &[u8; 32], _next_node_id: PublicKey, amt_to_forward_msat: u64) -> Result<(), APIError> {
+ let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
+
+ let next_hop_scid = match self.channel_state.lock().unwrap().by_id.get(next_hop_channel_id) {
+ Some(chan) => chan.get_short_channel_id().unwrap_or(chan.outbound_scid_alias()),
+ None => return Err(APIError::APIMisuseError {
+ err: format!("Channel with id {:?} not found", next_hop_channel_id)
+ })
+ };
+
+ let payment = self.pending_intercepted_htlcs.lock().unwrap().remove(&intercept_id)
+ .ok_or_else(|| APIError::APIMisuseError {
+ err: format!("Payment with intercept id {:?} not found", intercept_id.0)
+ })?;
+
+ let routing = match payment.forward_info.routing {
+ PendingHTLCRouting::Forward { onion_packet, .. } => {
+ PendingHTLCRouting::Forward { onion_packet, short_channel_id: next_hop_scid }
+ },
+ _ => unreachable!() // Only `PendingHTLCRouting::Forward`s are intercepted
+ };
+ let pending_htlc_info = PendingHTLCInfo {
+ outgoing_amt_msat: amt_to_forward_msat, routing, ..payment.forward_info
+ };
+
+ let mut per_source_pending_forward = [(
+ payment.prev_short_channel_id,
+ payment.prev_funding_outpoint,
+ payment.prev_user_channel_id,
+ vec![(pending_htlc_info, payment.prev_htlc_id)]
+ )];
+ self.forward_htlcs(&mut per_source_pending_forward);
+ Ok(())
+ }
+
/// Processes HTLCs which are pending waiting on random forward delay.
///
/// Should only really ever be called in response to a PendingHTLCsForwardable event.
let mut new_events = Vec::new();
let mut failed_forwards = Vec::new();
- let mut phantom_receives: Vec<(u64, OutPoint, Vec<(PendingHTLCInfo, u64)>)> = Vec::new();
+ let mut phantom_receives: Vec<(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)> = Vec::new();
let mut handle_errors = Vec::new();
{
let mut forward_htlcs = HashMap::new();
for forward_info in pending_forwards.drain(..) {
match forward_info {
HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
- prev_short_channel_id, prev_htlc_id, prev_funding_outpoint,
+ prev_short_channel_id, prev_htlc_id, prev_funding_outpoint, prev_user_channel_id,
forward_info: PendingHTLCInfo {
routing, incoming_shared_secret, payment_hash, outgoing_amt_msat,
outgoing_cltv_value, incoming_amt_msat: _
match next_hop {
onion_utils::Hop::Receive(hop_data) => {
match self.construct_recv_pending_htlc_info(hop_data, incoming_shared_secret, payment_hash, outgoing_amt_msat, outgoing_cltv_value, Some(phantom_shared_secret)) {
- Ok(info) => phantom_receives.push((prev_short_channel_id, prev_funding_outpoint, vec![(info, prev_htlc_id)])),
+ Ok(info) => phantom_receives.push((prev_short_channel_id, prev_funding_outpoint, prev_user_channel_id, vec![(info, prev_htlc_id)])),
Err(ReceiveError { err_code, err_data, msg }) => failed_payment!(msg, err_code, err_data, Some(phantom_shared_secret))
}
},
for forward_info in pending_forwards.drain(..) {
match forward_info {
HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
- prev_short_channel_id, prev_htlc_id, prev_funding_outpoint ,
+ prev_short_channel_id, prev_htlc_id, prev_funding_outpoint, prev_user_channel_id: _,
forward_info: PendingHTLCInfo {
incoming_shared_secret, payment_hash, outgoing_amt_msat, outgoing_cltv_value,
routing: PendingHTLCRouting::Forward { onion_packet, .. }, incoming_amt_msat: _,
for forward_info in pending_forwards.drain(..) {
match forward_info {
HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
- prev_short_channel_id, prev_htlc_id, prev_funding_outpoint,
+ prev_short_channel_id, prev_htlc_id, prev_funding_outpoint, prev_user_channel_id,
forward_info: PendingHTLCInfo {
routing, incoming_shared_secret, payment_hash, outgoing_amt_msat, ..
}
));
}
}
+ let phantom_shared_secret = claimable_htlc.prev_hop.phantom_shared_secret;
+ let mut receiver_node_id = self.our_network_pubkey;
+ if phantom_shared_secret.is_some() {
+ receiver_node_id = self.keys_manager.get_node_id(Recipient::PhantomNode)
+ .expect("Failed to get node_id for phantom node recipient");
+ }
macro_rules! check_total_value {
($payment_data: expr, $payment_preimage: expr) => {{
log_bytes!(payment_hash.0), total_value, $payment_data.total_msat);
fail_htlc!(claimable_htlc, payment_hash);
} else if total_value == $payment_data.total_msat {
+ let prev_channel_id = prev_funding_outpoint.to_channel_id();
htlcs.push(claimable_htlc);
new_events.push(events::Event::PaymentReceived {
+ receiver_node_id: Some(receiver_node_id),
payment_hash,
purpose: purpose(),
amount_msat: total_value,
+ via_channel_id: Some(prev_channel_id),
+ via_user_channel_id: Some(prev_user_channel_id),
});
payment_received_generated = true;
} else {
hash_map::Entry::Vacant(e) => {
let purpose = events::PaymentPurpose::SpontaneousPayment(preimage);
e.insert((purpose.clone(), vec![claimable_htlc]));
+ let prev_channel_id = prev_funding_outpoint.to_channel_id();
new_events.push(events::Event::PaymentReceived {
+ receiver_node_id: Some(receiver_node_id),
payment_hash,
amount_msat: outgoing_amt_msat,
purpose,
+ via_channel_id: Some(prev_channel_id),
+ via_user_channel_id: Some(prev_user_channel_id),
});
},
hash_map::Entry::Occupied(_) => {
htlc.timer_ticks += 1;
return htlc.timer_ticks >= MPP_TIMEOUT_TICKS
}) {
- timed_out_mpp_htlcs.extend(htlcs.into_iter().map(|htlc| (htlc.prev_hop.clone(), payment_hash.clone())));
+ timed_out_mpp_htlcs.extend(htlcs.drain(..).map(|htlc: ClaimableHTLC| (htlc.prev_hop, *payment_hash)));
return false;
}
}
let mut claimed_any_htlcs = false;
let mut channel_state_lock = self.channel_state.lock().unwrap();
let channel_state = &mut *channel_state_lock;
+ let mut receiver_node_id = Some(self.our_network_pubkey);
for htlc in sources.iter() {
let chan_id = match self.short_to_chan_info.read().unwrap().get(&htlc.prev_hop.short_channel_id) {
Some((_cp_id, chan_id)) => chan_id.clone(),
break;
}
}
+ let phantom_shared_secret = htlc.prev_hop.phantom_shared_secret;
+ if phantom_shared_secret.is_some() {
+ let phantom_pubkey = self.keys_manager.get_node_id(Recipient::PhantomNode)
+ .expect("Failed to get node_id for phantom node recipient");
+ receiver_node_id = Some(phantom_pubkey)
+ }
claimable_amt_msat += htlc.value;
}
if claimed_any_htlcs {
self.pending_events.lock().unwrap().push(events::Event::PaymentClaimed {
+ receiver_node_id,
payment_hash,
purpose: payment_purpose,
amount_msat: claimable_amt_msat,
fn claim_funds_from_hop(&self, channel_state_lock: &mut MutexGuard<ChannelHolder<<K::Target as KeysInterface>::Signer>>, prev_hop: HTLCPreviousHopData, payment_preimage: PaymentPreimage) -> ClaimFundsFromHop {
//TODO: Delay the claimed_funds relaying just like we do outbound relay!
- let channel_state = &mut **channel_state_lock;
- let chan_id = match self.short_to_chan_info.read().unwrap().get(&prev_hop.short_channel_id) {
- Some((_cp_id, chan_id)) => chan_id.clone(),
- None => {
- return ClaimFundsFromHop::PrevHopForceClosed
- }
- };
+ let chan_id = prev_hop.outpoint.to_channel_id();
+ let channel_state = &mut **channel_state_lock;
if let hash_map::Entry::Occupied(mut chan) = channel_state.by_id.entry(chan_id) {
match chan.get_mut().get_update_fulfill_htlc_and_commit(prev_hop.htlc_id, payment_preimage, &self.logger) {
Ok(msgs_monitor_option) => {
commitment_update: Option<msgs::CommitmentUpdate>, order: RAACommitmentOrder,
pending_forwards: Vec<(PendingHTLCInfo, u64)>, funding_broadcastable: Option<Transaction>,
channel_ready: Option<msgs::ChannelReady>, announcement_sigs: Option<msgs::AnnouncementSignatures>)
- -> Option<(u64, OutPoint, Vec<(PendingHTLCInfo, u64)>)> {
+ -> Option<(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)> {
let mut htlc_forwards = None;
let counterparty_node_id = channel.get_counterparty_node_id();
if !pending_forwards.is_empty() {
htlc_forwards = Some((channel.get_short_channel_id().unwrap_or(channel.outbound_scid_alias()),
- channel.get_funding_txo().unwrap(), pending_forwards));
+ channel.get_funding_txo().unwrap(), channel.get_user_id(), pending_forwards));
}
if let Some(msg) = channel_ready {
}
#[inline]
- fn forward_htlcs(&self, per_source_pending_forwards: &mut [(u64, OutPoint, Vec<(PendingHTLCInfo, u64)>)]) {
- for &mut (prev_short_channel_id, prev_funding_outpoint, ref mut pending_forwards) in per_source_pending_forwards {
+ fn forward_htlcs(&self, per_source_pending_forwards: &mut [(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)]) {
+ for &mut (prev_short_channel_id, prev_funding_outpoint, prev_user_channel_id, ref mut pending_forwards) in per_source_pending_forwards {
let mut forward_event = None;
+ let mut new_intercept_events = Vec::new();
+ let mut failed_intercept_forwards = Vec::new();
if !pending_forwards.is_empty() {
- let mut forward_htlcs = self.forward_htlcs.lock().unwrap();
- if forward_htlcs.is_empty() {
- forward_event = Some(Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS))
- }
for (forward_info, prev_htlc_id) in pending_forwards.drain(..) {
- match forward_htlcs.entry(match forward_info.routing {
- PendingHTLCRouting::Forward { short_channel_id, .. } => short_channel_id,
- PendingHTLCRouting::Receive { .. } => 0,
- PendingHTLCRouting::ReceiveKeysend { .. } => 0,
- }) {
+ let scid = match forward_info.routing {
+ PendingHTLCRouting::Forward { short_channel_id, .. } => short_channel_id,
+ PendingHTLCRouting::Receive { .. } => 0,
+ PendingHTLCRouting::ReceiveKeysend { .. } => 0,
+ };
+ // Pull this now to avoid introducing a lock order with `forward_htlcs`.
+ let is_our_scid = self.short_to_chan_info.read().unwrap().contains_key(&scid);
+
+ let mut forward_htlcs = self.forward_htlcs.lock().unwrap();
+ let forward_htlcs_empty = forward_htlcs.is_empty();
+ match forward_htlcs.entry(scid) {
hash_map::Entry::Occupied(mut entry) => {
entry.get_mut().push(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
- prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, forward_info }));
+ prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info }));
},
hash_map::Entry::Vacant(entry) => {
- entry.insert(vec!(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
- prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, forward_info })));
+ if !is_our_scid && forward_info.incoming_amt_msat.is_some() &&
+ fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, scid, &self.genesis_hash)
+ {
+ let intercept_id = InterceptId(Sha256::hash(&forward_info.incoming_shared_secret).into_inner());
+ let mut pending_intercepts = self.pending_intercepted_htlcs.lock().unwrap();
+ match pending_intercepts.entry(intercept_id) {
+ hash_map::Entry::Vacant(entry) => {
+ new_intercept_events.push(events::Event::HTLCIntercepted {
+ requested_next_hop_scid: scid,
+ payment_hash: forward_info.payment_hash,
+ inbound_amount_msat: forward_info.incoming_amt_msat.unwrap(),
+ expected_outbound_amount_msat: forward_info.outgoing_amt_msat,
+ intercept_id
+ });
+ entry.insert(PendingAddHTLCInfo {
+ prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info });
+ },
+ hash_map::Entry::Occupied(_) => {
+ log_info!(self.logger, "Failed to forward incoming HTLC: detected duplicate intercepted payment over short channel id {}", scid);
+ let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
+ short_channel_id: prev_short_channel_id,
+ outpoint: prev_funding_outpoint,
+ htlc_id: prev_htlc_id,
+ incoming_packet_shared_secret: forward_info.incoming_shared_secret,
+ phantom_shared_secret: None,
+ });
+
+ failed_intercept_forwards.push((htlc_source, forward_info.payment_hash,
+ HTLCFailReason::Reason { failure_code: 0x4000 | 10, data: Vec::new() },
+ HTLCDestination::InvalidForward { requested_forward_scid: scid },
+ ));
+ }
+ }
+ } else {
+ // We don't want to generate a PendingHTLCsForwardable event if only intercepted
+ // payments are being processed.
+ if forward_htlcs_empty {
+ forward_event = Some(Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS));
+ }
+ entry.insert(vec!(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
+ prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info })));
+ }
}
}
}
}
+
+ for (htlc_source, payment_hash, failure_reason, destination) in failed_intercept_forwards.drain(..) {
+ self.fail_htlc_backwards_internal(htlc_source, &payment_hash, failure_reason, destination);
+ }
+
+ if !new_intercept_events.is_empty() {
+ let mut events = self.pending_events.lock().unwrap();
+ events.append(&mut new_intercept_events);
+ }
+
match forward_event {
Some(time) => {
let mut pending_events = self.pending_events.lock().unwrap();
raa_updates.finalized_claimed_htlcs,
chan.get().get_short_channel_id()
.unwrap_or(chan.get().outbound_scid_alias()),
- chan.get().get_funding_txo().unwrap()))
+ chan.get().get_funding_txo().unwrap(),
+ chan.get().get_user_id()))
},
hash_map::Entry::Vacant(_) => break Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id))
}
self.fail_holding_cell_htlcs(htlcs_to_fail, msg.channel_id, counterparty_node_id);
match res {
Ok((pending_forwards, mut pending_failures, finalized_claim_htlcs,
- short_channel_id, channel_outpoint)) =>
+ short_channel_id, channel_outpoint, user_channel_id)) =>
{
for failure in pending_failures.drain(..) {
let receiver = HTLCDestination::NextHopChannel { node_id: Some(*counterparty_node_id), channel_id: channel_outpoint.to_channel_id() };
self.fail_htlc_backwards_internal(failure.0, &failure.1, failure.2, receiver);
}
- self.forward_htlcs(&mut [(short_channel_id, channel_outpoint, pending_forwards)]);
+ self.forward_htlcs(&mut [(short_channel_id, channel_outpoint, user_channel_id, pending_forwards)]);
self.finalize_claims(finalized_claim_htlcs);
Ok(())
},
}
}
+ /// Gets a fake short channel id for use in receiving intercepted payments. These fake scids are
+ /// used when constructing the route hints for HTLCs intended to be intercepted. See
+ /// [`ChannelManager::forward_intercepted_htlc`].
+ ///
+ /// Note that this method is not guaranteed to return unique values, you may need to call it a few
+ /// times to get a unique scid.
+ pub fn get_intercept_scid(&self) -> u64 {
+ let best_block_height = self.best_block.read().unwrap().height();
+ let short_to_chan_info = self.short_to_chan_info.read().unwrap();
+ loop {
+ let scid_candidate = fake_scid::Namespace::Intercept.get_fake_scid(best_block_height, &self.genesis_hash, &self.fake_scid_rand_bytes, &self.keys_manager);
+ // Ensure the generated scid doesn't conflict with a real channel.
+ if short_to_chan_info.contains_key(&scid_candidate) { continue }
+ return scid_candidate
+ }
+ }
+
/// Gets inflight HTLC information by processing pending outbound payments that are in
/// our channels. May be used during pathfinding to account for in-use channel liquidity.
pub fn compute_inflight_htlcs(&self) -> InFlightHtlcs {
}
}
-impl<M: Deref , T: Deref , K: Deref , F: Deref , L: Deref >
+impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref >
ChannelMessageHandler for ChannelManager<M, T, K, F, L>
where M::Target: chain::Watch<<K::Target as KeysInterface>::Signer>,
T::Target: BroadcasterInterface,
(6, self.funding_txo, option),
(7, self.config, option),
(8, self.short_channel_id, option),
+ (9, self.confirmations, option),
(10, self.channel_value_satoshis, required),
(12, self.unspendable_punishment_reserve, option),
(14, user_channel_id_low, required),
(6, funding_txo, option),
(7, config, option),
(8, short_channel_id, option),
+ (9, confirmations, option),
(10, channel_value_satoshis, required),
(12, unspendable_punishment_reserve, option),
(14, user_channel_id_low, required),
next_outbound_htlc_limit_msat: next_outbound_htlc_limit_msat.0.unwrap(),
inbound_capacity_msat: inbound_capacity_msat.0.unwrap(),
confirmations_required,
+ confirmations,
force_close_spend_delay,
is_outbound: is_outbound.0.unwrap(),
is_channel_ready: is_channel_ready.0.unwrap(),
impl_writeable_tlv_based!(PendingAddHTLCInfo, {
(0, forward_info, required),
+ (1, prev_user_channel_id, (default_value, 0)),
(2, prev_short_channel_id, required),
(4, prev_htlc_id, required),
(6, prev_funding_outpoint, required),
_ => {},
}
}
+
+ let mut pending_intercepted_htlcs = None;
+ let our_pending_intercepts = self.pending_intercepted_htlcs.lock().unwrap();
+ if our_pending_intercepts.len() != 0 {
+ pending_intercepted_htlcs = Some(our_pending_intercepts);
+ }
write_tlv_fields!(writer, {
(1, pending_outbound_payments_no_retry, required),
+ (2, pending_intercepted_htlcs, option),
(3, pending_outbound_payments, required),
(5, self.our_network_pubkey, required),
(7, self.fake_scid_rand_bytes, required),
// pending_outbound_payments_no_retry is for compatibility with 0.0.101 clients.
let mut pending_outbound_payments_no_retry: Option<HashMap<PaymentId, HashSet<[u8; 32]>>> = None;
let mut pending_outbound_payments = None;
+ let mut pending_intercepted_htlcs: Option<HashMap<InterceptId, PendingAddHTLCInfo>> = Some(HashMap::new());
let mut received_network_pubkey: Option<PublicKey> = None;
let mut fake_scid_rand_bytes: Option<[u8; 32]> = None;
let mut probing_cookie_secret: Option<[u8; 32]> = None;
let mut claimable_htlc_purposes = None;
read_tlv_fields!(reader, {
(1, pending_outbound_payments_no_retry, option),
+ (2, pending_intercepted_htlcs, option),
(3, pending_outbound_payments, option),
(5, received_network_pubkey, option),
(7, fake_scid_rand_bytes, option),
if let Some((payment_purpose, claimable_htlcs)) = claimable_htlcs.remove(&payment_hash) {
log_info!(args.logger, "Re-claiming HTLCs with payment hash {} as we've released the preimage to a ChannelMonitor!", log_bytes!(payment_hash.0));
let mut claimable_amt_msat = 0;
+ let mut receiver_node_id = Some(our_network_pubkey);
+ let phantom_shared_secret = claimable_htlcs[0].prev_hop.phantom_shared_secret;
+ if phantom_shared_secret.is_some() {
+ let phantom_pubkey = args.keys_manager.get_node_id(Recipient::PhantomNode)
+ .expect("Failed to get node_id for phantom node recipient");
+ receiver_node_id = Some(phantom_pubkey)
+ }
for claimable_htlc in claimable_htlcs {
claimable_amt_msat += claimable_htlc.value;
}
}
pending_events_read.push(events::Event::PaymentClaimed {
+ receiver_node_id,
payment_hash,
purpose: payment_purpose,
amount_msat: claimable_amt_msat,
inbound_payment_key: expanded_inbound_key,
pending_inbound_payments: Mutex::new(pending_inbound_payments),
pending_outbound_payments: Mutex::new(pending_outbound_payments.unwrap()),
+ pending_intercepted_htlcs: Mutex::new(pending_intercepted_htlcs.unwrap()),
forward_htlcs: Mutex::new(forward_htlcs),
claimable_htlcs: Mutex::new(claimable_htlcs),