htlc_id: u64,
err_packet: msgs::OnionErrorPacket,
},
+ FailMalformedHTLC {
+ htlc_id: u64,
+ failure_code: u16,
+ sha256_of_onion: [u8; 32],
+ },
}
macro_rules! define_state_flags {
/// The result of a shutdown that should be handled.
#[must_use]
pub(crate) struct ShutdownResult {
+ pub(crate) closure_reason: ClosureReason,
/// A channel monitor update to apply.
pub(crate) monitor_update: Option<(PublicKey, OutPoint, ChannelMonitorUpdate)>,
/// A list of dropped outbound HTLCs that can safely be failed backwards immediately.
/// propagated to the remainder of the batch.
pub(crate) unbroadcasted_batch_funding_txid: Option<Txid>,
pub(crate) channel_id: ChannelId,
+ pub(crate) user_channel_id: u128,
+ pub(crate) channel_capacity_satoshis: u64,
pub(crate) counterparty_node_id: PublicKey,
+ pub(crate) unbroadcasted_funding_tx: Option<Transaction>,
}
/// If the majority of the channels funds are to the fundee and the initiator holds only just
res
}
- fn if_unbroadcasted_funding<F, O>(&self, f: F) -> Option<O>
- where F: Fn() -> Option<O> {
+ fn if_unbroadcasted_funding<F, O>(&self, f: F) -> Option<O> where F: Fn() -> Option<O> {
match self.channel_state {
ChannelState::FundingNegotiated => f(),
- ChannelState::AwaitingChannelReady(flags) => if flags.is_set(AwaitingChannelReadyFlags::WAITING_FOR_BATCH) {
- f()
- } else {
- None
- },
+ ChannelState::AwaitingChannelReady(flags) =>
+ if flags.is_set(AwaitingChannelReadyFlags::WAITING_FOR_BATCH) ||
+ flags.is_set(FundedStateFlags::MONITOR_UPDATE_IN_PROGRESS.into())
+ {
+ f()
+ } else {
+ None
+ },
_ => None,
}
}
/// those explicitly stated to be allowed after shutdown completes, eg some simple getters).
/// Also returns the list of payment_hashes for channels which we can safely fail backwards
/// immediately (others we will have to allow to time out).
- pub fn force_shutdown(&mut self, should_broadcast: bool) -> ShutdownResult {
+ pub fn force_shutdown(&mut self, should_broadcast: bool, closure_reason: ClosureReason) -> ShutdownResult {
// Note that we MUST only generate a monitor update that indicates force-closure - we're
// called during initialization prior to the chain_monitor in the encompassing ChannelManager
// being fully configured in some cases. Thus, its likely any monitor events we generate will
self.latest_monitor_update_id = CLOSED_CHANNEL_UPDATE_ID;
Some((self.get_counterparty_node_id(), funding_txo, ChannelMonitorUpdate {
update_id: self.latest_monitor_update_id,
+ counterparty_node_id: Some(self.counterparty_node_id),
updates: vec![ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast }],
}))
} else { None }
} else { None };
let unbroadcasted_batch_funding_txid = self.unbroadcasted_batch_funding_txid();
+ let unbroadcasted_funding_tx = self.unbroadcasted_funding();
self.channel_state = ChannelState::ShutdownComplete;
self.update_time_counter += 1;
ShutdownResult {
+ closure_reason,
monitor_update,
dropped_outbound_htlcs,
unbroadcasted_batch_funding_txid,
channel_id: self.channel_id,
+ user_channel_id: self.user_id,
+ channel_capacity_satoshis: self.channel_value_satoshis,
counterparty_node_id: self.counterparty_node_id,
+ unbroadcasted_funding_tx,
}
}
.ok();
if funding_signed.is_none() {
- log_trace!(logger, "Counterparty commitment signature not available for funding_signed message; setting signer_pending_funding");
- self.signer_pending_funding = true;
+ #[cfg(not(async_signing))] {
+ panic!("Failed to get signature for funding_signed");
+ }
+ #[cfg(async_signing)] {
+ log_trace!(logger, "Counterparty commitment signature not available for funding_signed message; setting signer_pending_funding");
+ self.signer_pending_funding = true;
+ }
} else if self.signer_pending_funding {
log_trace!(logger, "Counterparty commitment signature available for funding_signed message; clearing signer_pending_funding");
self.signer_pending_funding = false;
feerate: u32,
}
+/// Contents of a wire message that fails an HTLC backwards. Useful for [`Channel::fail_htlc`] to
+/// fail with either [`msgs::UpdateFailMalformedHTLC`] or [`msgs::UpdateFailHTLC`] as needed.
+trait FailHTLCContents {
+ type Message: FailHTLCMessageName;
+ fn to_message(self, htlc_id: u64, channel_id: ChannelId) -> Self::Message;
+ fn to_inbound_htlc_state(self) -> InboundHTLCState;
+ fn to_htlc_update_awaiting_ack(self, htlc_id: u64) -> HTLCUpdateAwaitingACK;
+}
+impl FailHTLCContents for msgs::OnionErrorPacket {
+ type Message = msgs::UpdateFailHTLC;
+ fn to_message(self, htlc_id: u64, channel_id: ChannelId) -> Self::Message {
+ msgs::UpdateFailHTLC { htlc_id, channel_id, reason: self }
+ }
+ fn to_inbound_htlc_state(self) -> InboundHTLCState {
+ InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailRelay(self))
+ }
+ fn to_htlc_update_awaiting_ack(self, htlc_id: u64) -> HTLCUpdateAwaitingACK {
+ HTLCUpdateAwaitingACK::FailHTLC { htlc_id, err_packet: self }
+ }
+}
+impl FailHTLCContents for (u16, [u8; 32]) {
+ type Message = msgs::UpdateFailMalformedHTLC; // (failure_code, sha256_of_onion)
+ fn to_message(self, htlc_id: u64, channel_id: ChannelId) -> Self::Message {
+ msgs::UpdateFailMalformedHTLC {
+ htlc_id,
+ channel_id,
+ failure_code: self.0,
+ sha256_of_onion: self.1
+ }
+ }
+ fn to_inbound_htlc_state(self) -> InboundHTLCState {
+ InboundHTLCState::LocalRemoved(
+ InboundHTLCRemovalReason::FailMalformed((self.1, self.0))
+ )
+ }
+ fn to_htlc_update_awaiting_ack(self, htlc_id: u64) -> HTLCUpdateAwaitingACK {
+ HTLCUpdateAwaitingACK::FailMalformedHTLC {
+ htlc_id,
+ failure_code: self.0,
+ sha256_of_onion: self.1
+ }
+ }
+}
+
+trait FailHTLCMessageName {
+ fn name() -> &'static str;
+}
+impl FailHTLCMessageName for msgs::UpdateFailHTLC {
+ fn name() -> &'static str {
+ "update_fail_htlc"
+ }
+}
+impl FailHTLCMessageName for msgs::UpdateFailMalformedHTLC {
+ fn name() -> &'static str {
+ "update_fail_malformed_htlc"
+ }
+}
+
impl<SP: Deref> Channel<SP> where
SP::Target: SignerProvider,
<SP::Target as SignerProvider>::EcdsaSigner: WriteableEcdsaChannelSigner
self.context.latest_monitor_update_id += 1;
let monitor_update = ChannelMonitorUpdate {
update_id: self.context.latest_monitor_update_id,
+ counterparty_node_id: Some(self.context.counterparty_node_id),
updates: vec![ChannelMonitorUpdateStep::PaymentPreimage {
payment_preimage: payment_preimage_arg.clone(),
}],
return UpdateFulfillFetch::DuplicateClaim {};
}
},
- &HTLCUpdateAwaitingACK::FailHTLC { htlc_id, .. } => {
+ &HTLCUpdateAwaitingACK::FailHTLC { htlc_id, .. } |
+ &HTLCUpdateAwaitingACK::FailMalformedHTLC { htlc_id, .. } =>
+ {
if htlc_id_arg == htlc_id {
log_warn!(logger, "Have preimage and want to fulfill HTLC with pending failure against channel {}", &self.context.channel_id());
// TODO: We may actually be able to switch to a fulfill here, though its
.map(|msg_opt| assert!(msg_opt.is_none(), "We forced holding cell?"))
}
+ /// Used for failing back with [`msgs::UpdateFailMalformedHTLC`]. For now, this is used when we
+ /// want to fail blinded HTLCs where we are not the intro node.
+ ///
+ /// See [`Self::queue_fail_htlc`] for more info.
+ pub fn queue_fail_malformed_htlc<L: Deref>(
+ &mut self, htlc_id_arg: u64, failure_code: u16, sha256_of_onion: [u8; 32], logger: &L
+ ) -> Result<(), ChannelError> where L::Target: Logger {
+ self.fail_htlc(htlc_id_arg, (failure_code, sha256_of_onion), true, logger)
+ .map(|msg_opt| assert!(msg_opt.is_none(), "We forced holding cell?"))
+ }
+
/// We can only have one resolution per HTLC. In some cases around reconnect, we may fulfill
/// an HTLC more than once or fulfill once and then attempt to fail after reconnect. We cannot,
/// however, fail more than once as we wait for an upstream failure to be irrevocably committed
/// If we do fail twice, we `debug_assert!(false)` and return `Ok(None)`. Thus, this will always
/// return `Ok(_)` if preconditions are met. In any case, `Err`s will only be
/// [`ChannelError::Ignore`].
- fn fail_htlc<L: Deref>(&mut self, htlc_id_arg: u64, err_packet: msgs::OnionErrorPacket, mut force_holding_cell: bool, logger: &L)
- -> Result<Option<msgs::UpdateFailHTLC>, ChannelError> where L::Target: Logger {
+ fn fail_htlc<L: Deref, E: FailHTLCContents + Clone>(
+ &mut self, htlc_id_arg: u64, err_packet: E, mut force_holding_cell: bool,
+ logger: &L
+ ) -> Result<Option<E::Message>, ChannelError> where L::Target: Logger {
if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
panic!("Was asked to fail an HTLC when channel was not in an operational state");
}
return Ok(None);
}
},
- &HTLCUpdateAwaitingACK::FailHTLC { htlc_id, .. } => {
+ &HTLCUpdateAwaitingACK::FailHTLC { htlc_id, .. } |
+ &HTLCUpdateAwaitingACK::FailMalformedHTLC { htlc_id, .. } =>
+ {
if htlc_id_arg == htlc_id {
debug_assert!(false, "Tried to fail an HTLC that was already failed");
return Err(ChannelError::Ignore("Unable to find a pending HTLC which matched the given HTLC ID".to_owned()));
}
}
log_trace!(logger, "Placing failure for HTLC ID {} in holding cell in channel {}.", htlc_id_arg, &self.context.channel_id());
- self.context.holding_cell_htlc_updates.push(HTLCUpdateAwaitingACK::FailHTLC {
- htlc_id: htlc_id_arg,
- err_packet,
- });
+ self.context.holding_cell_htlc_updates.push(err_packet.to_htlc_update_awaiting_ack(htlc_id_arg));
return Ok(None);
}
- log_trace!(logger, "Failing HTLC ID {} back with a update_fail_htlc message in channel {}.", htlc_id_arg, &self.context.channel_id());
+ log_trace!(logger, "Failing HTLC ID {} back with {} message in channel {}.", htlc_id_arg,
+ E::Message::name(), &self.context.channel_id());
{
let htlc = &mut self.context.pending_inbound_htlcs[pending_idx];
- htlc.state = InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailRelay(err_packet.clone()));
+ htlc.state = err_packet.clone().to_inbound_htlc_state();
}
- Ok(Some(msgs::UpdateFailHTLC {
- channel_id: self.context.channel_id(),
- htlc_id: htlc_id_arg,
- reason: err_packet
- }))
+ Ok(Some(err_packet.to_message(htlc_id_arg, self.context.channel_id())))
}
// Message handlers:
self.context.channel_state.clear_waiting_for_batch();
}
+ /// Unsets the existing funding information.
+ ///
+ /// This must only be used if the channel has not yet completed funding and has not been used.
+ ///
+ /// Further, the channel must be immediately shut down after this with a call to
+ /// [`ChannelContext::force_shutdown`].
+ pub fn unset_funding_info(&mut self, temporary_channel_id: ChannelId) {
+ debug_assert!(matches!(
+ self.context.channel_state, ChannelState::AwaitingChannelReady(_)
+ ));
+ self.context.channel_transaction_parameters.funding_outpoint = None;
+ self.context.channel_id = temporary_channel_id;
+ }
+
/// Handles a channel_ready message from our peer. If we've already sent our channel_ready
/// and the channel is now usable (and public), this may generate an announcement_signatures to
/// reply with.
self.context.latest_monitor_update_id += 1;
let mut monitor_update = ChannelMonitorUpdate {
update_id: self.context.latest_monitor_update_id,
+ counterparty_node_id: Some(self.context.counterparty_node_id),
updates: vec![ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo {
commitment_tx: holder_commitment_tx,
htlc_outputs: htlcs_and_sigs,
let mut monitor_update = ChannelMonitorUpdate {
update_id: self.context.latest_monitor_update_id + 1, // We don't increment this yet!
+ counterparty_node_id: Some(self.context.counterparty_node_id),
updates: Vec::new(),
};
}
}
},
+ &HTLCUpdateAwaitingACK::FailMalformedHTLC { htlc_id, failure_code, sha256_of_onion } => {
+ match self.fail_htlc(htlc_id, (failure_code, sha256_of_onion), false, logger) {
+ Ok(update_fail_malformed_opt) => {
+ debug_assert!(update_fail_malformed_opt.is_some()); // See above comment
+ update_fail_count += 1;
+ },
+ Err(e) => {
+ if let ChannelError::Ignore(_) = e {}
+ else {
+ panic!("Got a non-IgnoreError action trying to fail holding cell HTLC");
+ }
+ }
+ }
+ },
}
}
if update_add_count == 0 && update_fulfill_count == 0 && update_fail_count == 0 && self.context.holding_cell_update_fee.is_none() {
self.context.latest_monitor_update_id += 1;
let mut monitor_update = ChannelMonitorUpdate {
update_id: self.context.latest_monitor_update_id,
+ counterparty_node_id: Some(self.context.counterparty_node_id),
updates: vec![ChannelMonitorUpdateStep::CommitmentSecret {
idx: self.context.cur_counterparty_commitment_transaction_number + 1,
secret: msg.per_commitment_secret,
/// Indicates that the signer may have some signatures for us, so we should retry if we're
/// blocked.
- #[allow(unused)]
+ #[cfg(async_signing)]
pub fn signer_maybe_unblocked<L: Deref>(&mut self, logger: &L) -> SignerResumeUpdates where L::Target: Logger {
let commitment_update = if self.context.signer_pending_commitment_update {
self.get_last_commitment_update_for_send(logger).ok()
}
update
} else {
- if !self.context.signer_pending_commitment_update {
- log_trace!(logger, "Commitment update awaiting signer: setting signer_pending_commitment_update");
- self.context.signer_pending_commitment_update = true;
+ #[cfg(not(async_signing))] {
+ panic!("Failed to get signature for new commitment state");
+ }
+ #[cfg(async_signing)] {
+ if !self.context.signer_pending_commitment_update {
+ log_trace!(logger, "Commitment update awaiting signer: setting signer_pending_commitment_update");
+ self.context.signer_pending_commitment_update = true;
+ }
+ return Err(());
}
- return Err(());
};
Ok(msgs::CommitmentUpdate {
update_add_htlcs, update_fulfill_htlcs, update_fail_htlcs, update_fail_malformed_htlcs, update_fee,
self.context.latest_monitor_update_id += 1;
let monitor_update = ChannelMonitorUpdate {
update_id: self.context.latest_monitor_update_id,
+ counterparty_node_id: Some(self.context.counterparty_node_id),
updates: vec![ChannelMonitorUpdateStep::ShutdownScript {
scriptpubkey: self.get_closing_scriptpubkey(),
}],
if let Some((last_fee, sig)) = self.context.last_sent_closing_fee {
if last_fee == msg.fee_satoshis {
let shutdown_result = ShutdownResult {
+ closure_reason: ClosureReason::CooperativeClosure,
monitor_update: None,
dropped_outbound_htlcs: Vec::new(),
unbroadcasted_batch_funding_txid: self.context.unbroadcasted_batch_funding_txid(),
channel_id: self.context.channel_id,
+ user_channel_id: self.context.user_id,
+ channel_capacity_satoshis: self.context.channel_value_satoshis,
counterparty_node_id: self.context.counterparty_node_id,
+ unbroadcasted_funding_tx: self.context.unbroadcasted_funding(),
};
let tx = self.build_signed_closing_transaction(&mut closing_tx, &msg.signature, &sig);
self.context.channel_state = ChannelState::ShutdownComplete;
.map_err(|_| ChannelError::Close("External signer refused to sign closing transaction".to_owned()))?;
let (signed_tx, shutdown_result) = if $new_fee == msg.fee_satoshis {
let shutdown_result = ShutdownResult {
+ closure_reason: ClosureReason::CooperativeClosure,
monitor_update: None,
dropped_outbound_htlcs: Vec::new(),
unbroadcasted_batch_funding_txid: self.context.unbroadcasted_batch_funding_txid(),
channel_id: self.context.channel_id,
+ user_channel_id: self.context.user_id,
+ channel_capacity_satoshis: self.context.channel_value_satoshis,
counterparty_node_id: self.context.counterparty_node_id,
+ unbroadcasted_funding_tx: self.context.unbroadcasted_funding(),
};
self.context.channel_state = ChannelState::ShutdownComplete;
self.context.update_time_counter += 1;
// larger. If we don't know that time has moved forward, we can just set it to the last
// time we saw and it will be ignored.
let best_time = self.context.update_time_counter;
- match self.do_best_block_updated(reorg_height, best_time, None::<(ChainHash, &&NodeSigner, &UserConfig)>, logger) {
+ match self.do_best_block_updated(reorg_height, best_time, None::<(ChainHash, &&dyn NodeSigner, &UserConfig)>, logger) {
Ok((channel_ready, timed_out_htlcs, announcement_sigs)) => {
assert!(channel_ready.is_none(), "We can't generate a funding with 0 confirmations?");
assert!(timed_out_htlcs.is_empty(), "We can't have accepted HTLCs with a timeout before our funding confirmation?");
self.context.latest_monitor_update_id += 1;
let monitor_update = ChannelMonitorUpdate {
update_id: self.context.latest_monitor_update_id,
+ counterparty_node_id: Some(self.context.counterparty_node_id),
updates: vec![ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo {
commitment_txid: counterparty_commitment_txid,
htlc_outputs: htlcs.clone(),
/// Begins the shutdown process, getting a message for the remote peer and returning all
/// holding cell HTLCs for payment failure.
- ///
- /// May jump to the channel being fully shutdown (see [`Self::is_shutdown`]) in which case no
- /// [`ChannelMonitorUpdate`] will be returned).
pub fn get_shutdown(&mut self, signer_provider: &SP, their_features: &InitFeatures,
target_feerate_sats_per_kw: Option<u32>, override_shutdown_script: Option<ShutdownScript>)
- -> Result<(msgs::Shutdown, Option<ChannelMonitorUpdate>, Vec<(HTLCSource, PaymentHash)>, Option<ShutdownResult>), APIError>
+ -> Result<(msgs::Shutdown, Option<ChannelMonitorUpdate>, Vec<(HTLCSource, PaymentHash)>), APIError>
{
for htlc in self.context.pending_outbound_htlcs.iter() {
if let OutboundHTLCState::LocalAnnounced(_) = htlc.state {
return Err(APIError::ChannelUnavailable{err: "Cannot begin shutdown while peer is disconnected or we're waiting on a monitor update, maybe force-close instead?".to_owned()});
}
- // If we haven't funded the channel yet, we don't need to bother ensuring the shutdown
- // script is set, we just force-close and call it a day.
- let mut chan_closed = false;
- if self.context.channel_state.is_pre_funded_state() {
- chan_closed = true;
- }
-
let update_shutdown_script = match self.context.shutdown_scriptpubkey {
Some(_) => false,
- None if !chan_closed => {
+ None => {
// use override shutdown script if provided
let shutdown_scriptpubkey = match override_shutdown_script {
Some(script) => script,
self.context.shutdown_scriptpubkey = Some(shutdown_scriptpubkey);
true
},
- None => false,
};
// From here on out, we may not fail!
self.context.target_closing_feerate_sats_per_kw = target_feerate_sats_per_kw;
- let shutdown_result = if self.context.channel_state.is_pre_funded_state() {
- let shutdown_result = ShutdownResult {
- monitor_update: None,
- dropped_outbound_htlcs: Vec::new(),
- unbroadcasted_batch_funding_txid: self.context.unbroadcasted_batch_funding_txid(),
- channel_id: self.context.channel_id,
- counterparty_node_id: self.context.counterparty_node_id,
- };
- self.context.channel_state = ChannelState::ShutdownComplete;
- Some(shutdown_result)
- } else {
- self.context.channel_state.set_local_shutdown_sent();
- None
- };
+ self.context.channel_state.set_local_shutdown_sent();
self.context.update_time_counter += 1;
let monitor_update = if update_shutdown_script {
self.context.latest_monitor_update_id += 1;
let monitor_update = ChannelMonitorUpdate {
update_id: self.context.latest_monitor_update_id,
+ counterparty_node_id: Some(self.context.counterparty_node_id),
updates: vec![ChannelMonitorUpdateStep::ShutdownScript {
scriptpubkey: self.get_closing_scriptpubkey(),
}],
debug_assert!(!self.is_shutdown() || monitor_update.is_none(),
"we can't both complete shutdown and return a monitor update");
- Ok((shutdown, monitor_update, dropped_outbound_htlcs, shutdown_result))
+ Ok((shutdown, monitor_update, dropped_outbound_htlcs))
}
pub fn inflight_htlc_sources(&self) -> impl Iterator<Item=(&HTLCSource, &PaymentHash)> {
let funding_created = self.get_funding_created_msg(logger);
if funding_created.is_none() {
- if !self.context.signer_pending_funding {
- log_trace!(logger, "funding_created awaiting signer; setting signer_pending_funding");
- self.context.signer_pending_funding = true;
+ #[cfg(not(async_signing))] {
+ panic!("Failed to get signature for new funding creation");
+ }
+ #[cfg(async_signing)] {
+ if !self.context.signer_pending_funding {
+ log_trace!(logger, "funding_created awaiting signer; setting signer_pending_funding");
+ self.context.signer_pending_funding = true;
+ }
}
}
/// Indicates that the signer may have some signatures for us, so we should retry if we're
/// blocked.
- #[allow(unused)]
+ #[cfg(async_signing)]
pub fn signer_maybe_unblocked<L: Deref>(&mut self, logger: &L) -> Option<msgs::FundingCreated> where L::Target: Logger {
if self.context.signer_pending_funding && self.context.is_outbound() {
log_trace!(logger, "Signer unblocked a funding_created");
let mut holding_cell_skimmed_fees: Vec<Option<u64>> = Vec::new();
let mut holding_cell_blinding_points: Vec<Option<PublicKey>> = Vec::new();
+ // Vec of (htlc_id, failure_code, sha256_of_onion)
+ let mut malformed_htlcs: Vec<(u64, u16, [u8; 32])> = Vec::new();
(self.context.holding_cell_htlc_updates.len() as u64).write(writer)?;
for update in self.context.holding_cell_htlc_updates.iter() {
match update {
htlc_id.write(writer)?;
err_packet.write(writer)?;
}
+ &HTLCUpdateAwaitingACK::FailMalformedHTLC {
+ htlc_id, failure_code, sha256_of_onion
+ } => {
+ // We don't want to break downgrading by adding a new variant, so write a dummy
+ // `::FailHTLC` variant and write the real malformed error as an optional TLV.
+ malformed_htlcs.push((htlc_id, failure_code, sha256_of_onion));
+
+ let dummy_err_packet = msgs::OnionErrorPacket { data: Vec::new() };
+ 2u8.write(writer)?;
+ htlc_id.write(writer)?;
+ dummy_err_packet.write(writer)?;
+ }
}
}
(38, self.context.is_batch_funding, option),
(39, pending_outbound_blinding_points, optional_vec),
(41, holding_cell_blinding_points, optional_vec),
+ (43, malformed_htlcs, optional_vec), // Added in 0.0.119
});
Ok(())
let mut pending_outbound_blinding_points_opt: Option<Vec<Option<PublicKey>>> = None;
let mut holding_cell_blinding_points_opt: Option<Vec<Option<PublicKey>>> = None;
+ let mut malformed_htlcs: Option<Vec<(u64, u16, [u8; 32])>> = None;
+
read_tlv_fields!(reader, {
(0, announcement_sigs, option),
(1, minimum_depth, option),
(38, is_batch_funding, option),
(39, pending_outbound_blinding_points_opt, optional_vec),
(41, holding_cell_blinding_points_opt, optional_vec),
+ (43, malformed_htlcs, optional_vec), // Added in 0.0.119
});
let (channel_keys_id, holder_signer) = if let Some(channel_keys_id) = channel_keys_id {
if iter.next().is_some() { return Err(DecodeError::InvalidValue) }
}
+ if let Some(malformed_htlcs) = malformed_htlcs {
+ for (malformed_htlc_id, failure_code, sha256_of_onion) in malformed_htlcs {
+ let htlc_idx = holding_cell_htlc_updates.iter().position(|htlc| {
+ if let HTLCUpdateAwaitingACK::FailHTLC { htlc_id, err_packet } = htlc {
+ let matches = *htlc_id == malformed_htlc_id;
+ if matches { debug_assert!(err_packet.data.is_empty()) }
+ matches
+ } else { false }
+ }).ok_or(DecodeError::InvalidValue)?;
+ let malformed_htlc = HTLCUpdateAwaitingACK::FailMalformedHTLC {
+ htlc_id: malformed_htlc_id, failure_code, sha256_of_onion
+ };
+ let _ = core::mem::replace(&mut holding_cell_htlc_updates[htlc_idx], malformed_htlc);
+ }
+ }
+
Ok(Channel {
context: ChannelContext {
user_id,
use bitcoin::blockdata::transaction::{Transaction, TxOut};
use bitcoin::blockdata::opcodes;
use bitcoin::network::constants::Network;
+ use crate::ln::onion_utils::INVALID_ONION_BLINDING;
use crate::ln::{PaymentHash, PaymentPreimage};
use crate::ln::channel_keys::{RevocationKey, RevocationBasepoint};
use crate::ln::channelmanager::{self, HTLCSource, PaymentId};
}
#[test]
- fn blinding_point_skimmed_fee_ser() {
- // Ensure that channel blinding points and skimmed fees are (de)serialized properly.
+ fn blinding_point_skimmed_fee_malformed_ser() {
+ // Ensure that channel blinding points, skimmed fees, and malformed HTLCs are (de)serialized
+ // properly.
let feeest = LowerBoundedFeeEstimator::new(&TestFeeEstimator{fee_est: 15000});
let secp_ctx = Secp256k1::new();
let seed = [42; 32];
payment_preimage: PaymentPreimage([42; 32]),
htlc_id: 0,
};
- let mut holding_cell_htlc_updates = Vec::with_capacity(10);
- for i in 0..10 {
- if i % 3 == 0 {
+ let dummy_holding_cell_failed_htlc = |htlc_id| HTLCUpdateAwaitingACK::FailHTLC {
+ htlc_id, err_packet: msgs::OnionErrorPacket { data: vec![42] }
+ };
+ let dummy_holding_cell_malformed_htlc = |htlc_id| HTLCUpdateAwaitingACK::FailMalformedHTLC {
+ htlc_id, failure_code: INVALID_ONION_BLINDING, sha256_of_onion: [0; 32],
+ };
+ let mut holding_cell_htlc_updates = Vec::with_capacity(12);
+ for i in 0..12 {
+ if i % 5 == 0 {
holding_cell_htlc_updates.push(dummy_holding_cell_add_htlc.clone());
- } else if i % 3 == 1 {
+ } else if i % 5 == 1 {
holding_cell_htlc_updates.push(dummy_holding_cell_claim_htlc.clone());
- } else {
+ } else if i % 5 == 2 {
let mut dummy_add = dummy_holding_cell_add_htlc.clone();
if let HTLCUpdateAwaitingACK::AddHTLC {
ref mut blinding_point, ref mut skimmed_fee_msat, ..
*skimmed_fee_msat = Some(42);
} else { panic!() }
holding_cell_htlc_updates.push(dummy_add);
+ } else if i % 5 == 3 {
+ holding_cell_htlc_updates.push(dummy_holding_cell_malformed_htlc(i as u64));
+ } else {
+ holding_cell_htlc_updates.push(dummy_holding_cell_failed_htlc(i as u64));
}
}
chan.context.holding_cell_htlc_updates = holding_cell_htlc_updates.clone();
assert_eq!(decoded_chan.context.holding_cell_htlc_updates, holding_cell_htlc_updates);
}
- #[cfg(feature = "_test_vectors")]
+ #[cfg(all(feature = "_test_vectors", not(feature = "grind_signatures")))]
#[test]
fn outbound_commitment_test() {
use bitcoin::sighash;
// Test vectors from BOLT 3 Appendices C and F (anchors):
let feeest = TestFeeEstimator{fee_est: 15000};
- let logger : Arc<Logger> = Arc::new(test_utils::TestLogger::new());
+ let logger : Arc<dyn Logger> = Arc::new(test_utils::TestLogger::new());
let secp_ctx = Secp256k1::new();
let mut signer = InMemorySigner::new(