/// [`Event::PaymentClaimable::onion_fields`] as
/// [`RecipientOnionFields::custom_tlvs`].
custom_tlvs: Vec<(u64, Vec<u8>)>,
+ /// Set if this HTLC is the final hop in a multi-hop blinded path.
+ requires_blinded_error: bool,
},
/// The onion indicates that this is for payment to us but which contains the preimage for
/// claiming included, and is unrelated to any invoice we'd previously generated (aka a
impl PendingHTLCRouting {
// Used to override the onion failure code and data if the HTLC is blinded.
fn blinded_failure(&self) -> Option<BlindedFailure> {
- // TODO: needs update when we support receiving to multi-hop blinded paths
- if let Self::Forward { blinded: Some(_), .. } = self {
- Some(BlindedFailure::FromIntroductionNode)
- } else {
- None
+ // TODO: needs update when we support forwarding blinded HTLCs as non-intro node
+ match self {
+ Self::Forward { blinded: Some(_), .. } => Some(BlindedFailure::FromIntroductionNode),
+ Self::Receive { requires_blinded_error: true, .. } => Some(BlindedFailure::FromBlindedNode),
+ _ => None,
}
}
}
htlc_id: u64,
err_packet: msgs::OnionErrorPacket,
},
+ FailMalformedHTLC {
+ htlc_id: u64,
+ failure_code: u16,
+ sha256_of_onion: [u8; 32],
+ },
}
// Used for failing blinded HTLCs backwards correctly.
-#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
enum BlindedFailure {
FromIntroductionNode,
- // Another variant will be added here for non-intro nodes.
+ FromBlindedNode,
}
/// Tracks the inbound corresponding to an outbound HTLC
fn close_channel_internal(&self, channel_id: &ChannelId, counterparty_node_id: &PublicKey, target_feerate_sats_per_1000_weight: Option<u32>, override_shutdown_script: Option<ShutdownScript>) -> Result<(), APIError> {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
- let mut failed_htlcs: Vec<(HTLCSource, PaymentHash)>;
- let shutdown_result;
- loop {
+ let mut failed_htlcs: Vec<(HTLCSource, PaymentHash)> = Vec::new();
+ let mut shutdown_result = None;
+
+ {
let per_peer_state = self.per_peer_state.read().unwrap();
let peer_state_mutex = per_peer_state.get(counterparty_node_id)
let (shutdown_msg, mut monitor_update_opt, htlcs) =
chan.get_shutdown(&self.signer_provider, their_features, target_feerate_sats_per_1000_weight, override_shutdown_script)?;
failed_htlcs = htlcs;
- shutdown_result = None;
- debug_assert_eq!(shutdown_result.is_some(), chan.is_shutdown());
// We can send the `shutdown` message before updating the `ChannelMonitor`
// here as we don't need the monitor update to complete until we send a
if let Some(monitor_update) = monitor_update_opt.take() {
handle_new_monitor_update!(self, funding_txo_opt.unwrap(), monitor_update,
peer_state_lock, peer_state, per_peer_state, chan);
- break;
- }
-
- if chan.is_shutdown() {
- if let ChannelPhase::Funded(chan) = remove_channel_phase!(self, chan_phase_entry) {
- if let Ok(channel_update) = self.get_channel_update_for_broadcast(&chan) {
- peer_state.pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
- msg: channel_update
- });
- }
- self.issue_channel_close_events(&chan.context, ClosureReason::HolderForceClosed);
- }
}
- break;
+ } else {
+ self.issue_channel_close_events(chan_phase_entry.get().context(), ClosureReason::HolderForceClosed);
+ let mut chan_phase = remove_channel_phase!(self, chan_phase_entry);
+ shutdown_result = Some(chan_phase.context_mut().force_shutdown(false));
}
},
hash_map::Entry::Vacant(_) => {
- // If we reach this point, it means that the channel_id either refers to an unfunded channel or
- // it does not exist for this peer. Either way, we can attempt to force-close it.
- //
- // An appropriate error will be returned for non-existence of the channel if that's the case.
- mem::drop(peer_state_lock);
- mem::drop(per_peer_state);
- return self.force_close_channel_with_peer(&channel_id, counterparty_node_id, None, false).map(|_| ())
+ return Err(APIError::ChannelUnavailable {
+ err: format!(
+ "Channel with id {} not found for the passed counterparty node_id {}",
+ channel_id, counterparty_node_id,
+ )
+ });
},
}
}
let phantom_shared_secret = self.node_signer.ecdh(Recipient::PhantomNode, &onion_packet.public_key.unwrap(), None).unwrap().secret_bytes();
let next_hop = match onion_utils::decode_next_payment_hop(
phantom_shared_secret, &onion_packet.hop_data, onion_packet.hmac,
- payment_hash, &self.node_signer
+ payment_hash, None, &self.node_signer
) {
Ok(res) => res,
Err(onion_utils::OnionDecodeErr::Malformed { err_msg, err_code }) => {
fail_forward!(format!("Unknown short channel id {} for forward HTLC", short_chan_id), 0x4000 | 10, Vec::new(), None);
}
},
- HTLCForwardInfo::FailHTLC { .. } => {
+ HTLCForwardInfo::FailHTLC { .. } | HTLCForwardInfo::FailMalformedHTLC { .. } => {
// Channel went away before we could fail it. This implies
// the channel is now on chain and our counterparty is
// trying to broadcast the HTLC-Timeout, but that's their
continue;
}
},
+ HTLCForwardInfo::FailMalformedHTLC { .. } => {
+ todo!()
+ },
}
}
} else {
}) => {
let blinded_failure = routing.blinded_failure();
let (cltv_expiry, onion_payload, payment_data, phantom_shared_secret, mut onion_fields) = match routing {
- PendingHTLCRouting::Receive { payment_data, payment_metadata, incoming_cltv_expiry, phantom_shared_secret, custom_tlvs } => {
+ PendingHTLCRouting::Receive {
+ payment_data, payment_metadata, incoming_cltv_expiry, phantom_shared_secret,
+ custom_tlvs, requires_blinded_error: _
+ } => {
let _legacy_hop_data = Some(payment_data.clone());
let onion_fields = RecipientOnionFields { payment_secret: Some(payment_data.payment_secret),
payment_metadata, custom_tlvs };
htlc_id: $htlc.prev_hop.htlc_id,
incoming_packet_shared_secret: $htlc.prev_hop.incoming_packet_shared_secret,
phantom_shared_secret,
- blinded_failure: None,
+ blinded_failure,
}), payment_hash,
HTLCFailReason::reason(0x4000 | 15, htlc_msat_height_data),
HTLCDestination::FailedPayment { payment_hash: $payment_hash },
},
};
},
- HTLCForwardInfo::FailHTLC { .. } => {
+ HTLCForwardInfo::FailHTLC { .. } | HTLCForwardInfo::FailMalformedHTLC { .. } => {
panic!("Got pending fail of our own HTLC");
}
}
incoming_packet_shared_secret, phantom_shared_secret
)
},
+ Some(BlindedFailure::FromBlindedNode) => todo!(),
None => {
onion_error.get_encrypted_failure_packet(incoming_packet_shared_secret, phantom_shared_secret)
}
(2, incoming_cltv_expiry, required),
(3, payment_metadata, option),
(5, custom_tlvs, optional_vec),
+ (7, requires_blinded_error, (default_value, false)),
},
(2, ReceiveKeysend) => {
(0, payment_preimage, required),
);
impl_writeable_tlv_based_enum!(BlindedFailure,
- (0, FromIntroductionNode) => {}, ;
+ (0, FromIntroductionNode) => {},
+ (2, FromBlindedNode) => {}, ;
);
impl_writeable_tlv_based!(HTLCPreviousHopData, {
(6, prev_funding_outpoint, required),
});
-impl_writeable_tlv_based_enum!(HTLCForwardInfo,
- (1, FailHTLC) => {
- (0, htlc_id, required),
- (2, err_packet, required),
- };
- (0, AddHTLC)
-);
+impl Writeable for HTLCForwardInfo {
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+ const FAIL_HTLC_VARIANT_ID: u8 = 1;
+ match self {
+ Self::AddHTLC(info) => {
+ 0u8.write(w)?;
+ info.write(w)?;
+ },
+ Self::FailHTLC { htlc_id, err_packet } => {
+ FAIL_HTLC_VARIANT_ID.write(w)?;
+ write_tlv_fields!(w, {
+ (0, htlc_id, required),
+ (2, err_packet, required),
+ });
+ },
+ Self::FailMalformedHTLC { htlc_id, failure_code, sha256_of_onion } => {
+ // Since this variant was added in 0.0.119, write this as `::FailHTLC` with an empty error
+ // packet so older versions have something to fail back with, but serialize the real data as
+ // optional TLVs for the benefit of newer versions.
+ FAIL_HTLC_VARIANT_ID.write(w)?;
+ let dummy_err_packet = msgs::OnionErrorPacket { data: Vec::new() };
+ write_tlv_fields!(w, {
+ (0, htlc_id, required),
+ (1, failure_code, required),
+ (2, dummy_err_packet, required),
+ (3, sha256_of_onion, required),
+ });
+ },
+ }
+ Ok(())
+ }
+}
+
+impl Readable for HTLCForwardInfo {
+ fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+ let id: u8 = Readable::read(r)?;
+ Ok(match id {
+ 0 => Self::AddHTLC(Readable::read(r)?),
+ 1 => {
+ _init_and_read_len_prefixed_tlv_fields!(r, {
+ (0, htlc_id, required),
+ (1, malformed_htlc_failure_code, option),
+ (2, err_packet, required),
+ (3, sha256_of_onion, option),
+ });
+ if let Some(failure_code) = malformed_htlc_failure_code {
+ Self::FailMalformedHTLC {
+ htlc_id: _init_tlv_based_struct_field!(htlc_id, required),
+ failure_code,
+ sha256_of_onion: sha256_of_onion.ok_or(DecodeError::InvalidValue)?,
+ }
+ } else {
+ Self::FailHTLC {
+ htlc_id: _init_tlv_based_struct_field!(htlc_id, required),
+ err_packet: _init_tlv_based_struct_field!(err_packet, required),
+ }
+ }
+ },
+ _ => return Err(DecodeError::InvalidValue),
+ })
+ }
+}
impl_writeable_tlv_based!(PendingInboundPayment, {
(0, payment_secret, required),