htlc_id: u64,
err_packet: msgs::OnionErrorPacket,
},
+ FailMalformedHTLC {
+ htlc_id: u64,
+ failure_code: u16,
+ sha256_of_onion: [u8; 32],
+ },
}
macro_rules! define_state_flags {
pub(super) struct SignerResumeUpdates {
pub commitment_update: Option<msgs::CommitmentUpdate>,
pub funding_signed: Option<msgs::FundingSigned>,
- pub funding_created: Option<msgs::FundingCreated>,
pub channel_ready: Option<msgs::ChannelReady>,
}
}
}
- /// Only allowed after [`Self::channel_transaction_parameters`] is set.
- fn get_funding_created_msg<L: Deref>(&mut self, logger: &L) -> Option<msgs::FundingCreated> where L::Target: Logger {
- let counterparty_keys = self.build_remote_transaction_keys();
- let counterparty_initial_commitment_tx = self.build_commitment_transaction(self.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx;
- let signature = match &self.holder_signer {
- // TODO (taproot|arik): move match into calling method for Taproot
- ChannelSignerType::Ecdsa(ecdsa) => {
- ecdsa.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), Vec::new(), &self.secp_ctx)
- .map(|(sig, _)| sig).ok()?
- },
- // TODO (taproot|arik)
- #[cfg(taproot)]
- _ => todo!()
- };
-
- if self.signer_pending_funding {
- log_trace!(logger, "Counterparty commitment signature ready for funding_created message: clearing signer_pending_funding");
- self.signer_pending_funding = false;
- }
-
- Some(msgs::FundingCreated {
- temporary_channel_id: self.temporary_channel_id.unwrap(),
- funding_txid: self.channel_transaction_parameters.funding_outpoint.as_ref().unwrap().txid,
- funding_output_index: self.channel_transaction_parameters.funding_outpoint.as_ref().unwrap().index,
- signature,
- #[cfg(taproot)]
- partial_signature_with_nonce: None,
- #[cfg(taproot)]
- next_local_nonce: None,
- })
- }
-
/// Only allowed after [`Self::channel_transaction_parameters`] is set.
fn get_funding_signed_msg<L: Deref>(&mut self, logger: &L) -> (CommitmentTransaction, Option<msgs::FundingSigned>) where L::Target: Logger {
let counterparty_keys = self.build_remote_transaction_keys();
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
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:
-
- /// Handles a funding_signed message from the remote end.
- /// If this call is successful, broadcast the funding transaction (and not before!)
- pub fn funding_signed<L: Deref>(
- &mut self, msg: &msgs::FundingSigned, best_block: BestBlock, signer_provider: &SP, logger: &L
- ) -> Result<ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>, ChannelError>
- where
- L::Target: Logger
- {
- if !self.context.is_outbound() {
- return Err(ChannelError::Close("Received funding_signed for an inbound channel?".to_owned()));
- }
- if !matches!(self.context.channel_state, ChannelState::FundingNegotiated) {
- return Err(ChannelError::Close("Received funding_signed in strange state!".to_owned()));
- }
- if self.context.commitment_secrets.get_min_seen_secret() != (1 << 48) ||
- self.context.cur_counterparty_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER ||
- self.context.cur_holder_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER {
- panic!("Should not have advanced channel commitment tx numbers prior to funding_created");
- }
-
- let funding_script = self.context.get_funding_redeemscript();
-
- let counterparty_keys = self.context.build_remote_transaction_keys();
- let counterparty_initial_commitment_tx = self.context.build_commitment_transaction(self.context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx;
- let counterparty_trusted_tx = counterparty_initial_commitment_tx.trust();
- let counterparty_initial_bitcoin_tx = counterparty_trusted_tx.built_transaction();
-
- log_trace!(logger, "Initial counterparty tx for channel {} is: txid {} tx {}",
- &self.context.channel_id(), counterparty_initial_bitcoin_tx.txid, encode::serialize_hex(&counterparty_initial_bitcoin_tx.transaction));
-
- let holder_signer = self.context.build_holder_transaction_keys(self.context.cur_holder_commitment_transaction_number);
- let initial_commitment_tx = self.context.build_commitment_transaction(self.context.cur_holder_commitment_transaction_number, &holder_signer, true, false, logger).tx;
- {
- let trusted_tx = initial_commitment_tx.trust();
- let initial_commitment_bitcoin_tx = trusted_tx.built_transaction();
- let sighash = initial_commitment_bitcoin_tx.get_sighash_all(&funding_script, self.context.channel_value_satoshis);
- // They sign our commitment transaction, allowing us to broadcast the tx if we wish.
- if let Err(_) = self.context.secp_ctx.verify_ecdsa(&sighash, &msg.signature, &self.context.get_counterparty_pubkeys().funding_pubkey) {
- return Err(ChannelError::Close("Invalid funding_signed signature from peer".to_owned()));
- }
- }
-
- let holder_commitment_tx = HolderCommitmentTransaction::new(
- initial_commitment_tx,
- msg.signature,
- Vec::new(),
- &self.context.get_holder_pubkeys().funding_pubkey,
- self.context.counterparty_funding_pubkey()
- );
-
- self.context.holder_signer.as_ref().validate_holder_commitment(&holder_commitment_tx, Vec::new())
- .map_err(|_| ChannelError::Close("Failed to validate our commitment".to_owned()))?;
-
-
- let funding_redeemscript = self.context.get_funding_redeemscript();
- let funding_txo = self.context.get_funding_txo().unwrap();
- let funding_txo_script = funding_redeemscript.to_v0_p2wsh();
- let obscure_factor = get_commitment_transaction_number_obscure_factor(&self.context.get_holder_pubkeys().payment_point, &self.context.get_counterparty_pubkeys().payment_point, self.context.is_outbound());
- let shutdown_script = self.context.shutdown_scriptpubkey.clone().map(|script| script.into_inner());
- let mut monitor_signer = signer_provider.derive_channel_signer(self.context.channel_value_satoshis, self.context.channel_keys_id);
- monitor_signer.provide_channel_parameters(&self.context.channel_transaction_parameters);
- let channel_monitor = ChannelMonitor::new(self.context.secp_ctx.clone(), monitor_signer,
- shutdown_script, self.context.get_holder_selected_contest_delay(),
- &self.context.destination_script, (funding_txo, funding_txo_script),
- &self.context.channel_transaction_parameters,
- funding_redeemscript.clone(), self.context.channel_value_satoshis,
- obscure_factor,
- holder_commitment_tx, best_block, self.context.counterparty_node_id);
- channel_monitor.provide_initial_counterparty_commitment_tx(
- counterparty_initial_bitcoin_tx.txid, Vec::new(),
- self.context.cur_counterparty_commitment_transaction_number,
- self.context.counterparty_cur_commitment_point.unwrap(),
- counterparty_initial_commitment_tx.feerate_per_kw(),
- counterparty_initial_commitment_tx.to_broadcaster_value_sat(),
- counterparty_initial_commitment_tx.to_countersignatory_value_sat(), logger);
-
- assert!(!self.context.channel_state.is_monitor_update_in_progress()); // We have no had any monitor(s) yet to fail update!
- if self.context.is_batch_funding() {
- self.context.channel_state = ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags::WAITING_FOR_BATCH);
- } else {
- self.context.channel_state = ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags::new());
- }
- self.context.cur_holder_commitment_transaction_number -= 1;
- self.context.cur_counterparty_commitment_transaction_number -= 1;
-
- log_info!(logger, "Received funding_signed from peer for channel {}", &self.context.channel_id());
-
- let need_channel_ready = self.check_get_channel_ready(0).is_some();
- self.monitor_updating_paused(false, false, need_channel_ready, Vec::new(), Vec::new(), Vec::new());
- Ok(channel_monitor)
- }
-
/// Updates the state of the channel to indicate that all channels in the batch have received
/// funding_signed and persisted their monitors.
/// The funding transaction is consequently allowed to be broadcast, and the channel can be
}
}
},
+ &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() {
let channel_ready = if funding_signed.is_some() {
self.check_get_channel_ready(0)
} else { None };
- let funding_created = if self.context.signer_pending_funding && self.context.is_outbound() {
- self.context.get_funding_created_msg(logger)
- } else { None };
- log_trace!(logger, "Signer unblocked with {} commitment_update, {} funding_signed, {} funding_created, and {} channel_ready",
+ log_trace!(logger, "Signer unblocked with {} commitment_update, {} funding_signed and {} channel_ready",
if commitment_update.is_some() { "a" } else { "no" },
if funding_signed.is_some() { "a" } else { "no" },
- if funding_created.is_some() { "a" } else { "no" },
if channel_ready.is_some() { "a" } else { "no" });
SignerResumeUpdates {
commitment_update,
funding_signed,
- funding_created,
channel_ready,
}
}
// 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?");
/// 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 {
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)> {
})
}
+ /// Only allowed after [`ChannelContext::channel_transaction_parameters`] is set.
+ fn get_funding_created_msg<L: Deref>(&mut self, logger: &L) -> Option<msgs::FundingCreated> where L::Target: Logger {
+ let counterparty_keys = self.context.build_remote_transaction_keys();
+ let counterparty_initial_commitment_tx = self.context.build_commitment_transaction(self.context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx;
+ let signature = match &self.context.holder_signer {
+ // TODO (taproot|arik): move match into calling method for Taproot
+ ChannelSignerType::Ecdsa(ecdsa) => {
+ ecdsa.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), Vec::new(), &self.context.secp_ctx)
+ .map(|(sig, _)| sig).ok()?
+ },
+ // TODO (taproot|arik)
+ #[cfg(taproot)]
+ _ => todo!()
+ };
+
+ if self.context.signer_pending_funding {
+ log_trace!(logger, "Counterparty commitment signature ready for funding_created message: clearing signer_pending_funding");
+ self.context.signer_pending_funding = false;
+ }
+
+ Some(msgs::FundingCreated {
+ temporary_channel_id: self.context.temporary_channel_id.unwrap(),
+ funding_txid: self.context.channel_transaction_parameters.funding_outpoint.as_ref().unwrap().txid,
+ funding_output_index: self.context.channel_transaction_parameters.funding_outpoint.as_ref().unwrap().index,
+ signature,
+ #[cfg(taproot)]
+ partial_signature_with_nonce: None,
+ #[cfg(taproot)]
+ next_local_nonce: None,
+ })
+ }
+
/// Updates channel state with knowledge of the funding transaction's txid/index, and generates
/// a funding_created message for the remote peer.
/// Panics if called at some time other than immediately after initial handshake, if called twice,
/// Note that channel_id changes during this call!
/// Do NOT broadcast the funding transaction until after a successful funding_signed call!
/// If an Err is returned, it is a ChannelError::Close.
- pub fn get_funding_created<L: Deref>(mut self, funding_transaction: Transaction, funding_txo: OutPoint, is_batch_funding: bool, logger: &L)
- -> Result<(Channel<SP>, Option<msgs::FundingCreated>), (Self, ChannelError)> where L::Target: Logger {
+ pub fn get_funding_created<L: Deref>(&mut self, funding_transaction: Transaction, funding_txo: OutPoint, is_batch_funding: bool, logger: &L)
+ -> Result<Option<msgs::FundingCreated>, (Self, ChannelError)> where L::Target: Logger {
if !self.context.is_outbound() {
panic!("Tried to create outbound funding_created message on an inbound channel!");
}
self.context.funding_transaction = Some(funding_transaction);
self.context.is_batch_funding = Some(()).filter(|_| is_batch_funding);
- let funding_created = self.context.get_funding_created_msg(logger);
+ 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");
}
}
- let channel = Channel {
- context: self.context,
- };
-
- Ok((channel, funding_created))
+ Ok(funding_created)
}
fn get_initial_channel_type(config: &UserConfig, their_features: &InitFeatures) -> ChannelTypeFeatures {
Ok(())
}
+
+ /// Handles a funding_signed message from the remote end.
+ /// If this call is successful, broadcast the funding transaction (and not before!)
+ pub fn funding_signed<L: Deref>(
+ mut self, msg: &msgs::FundingSigned, best_block: BestBlock, signer_provider: &SP, logger: &L
+ ) -> Result<(Channel<SP>, ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>), (OutboundV1Channel<SP>, ChannelError)>
+ where
+ L::Target: Logger
+ {
+ if !self.context.is_outbound() {
+ return Err((self, ChannelError::Close("Received funding_signed for an inbound channel?".to_owned())));
+ }
+ if !matches!(self.context.channel_state, ChannelState::FundingNegotiated) {
+ return Err((self, ChannelError::Close("Received funding_signed in strange state!".to_owned())));
+ }
+ if self.context.commitment_secrets.get_min_seen_secret() != (1 << 48) ||
+ self.context.cur_counterparty_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER ||
+ self.context.cur_holder_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER {
+ panic!("Should not have advanced channel commitment tx numbers prior to funding_created");
+ }
+
+ let funding_script = self.context.get_funding_redeemscript();
+
+ let counterparty_keys = self.context.build_remote_transaction_keys();
+ let counterparty_initial_commitment_tx = self.context.build_commitment_transaction(self.context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx;
+ let counterparty_trusted_tx = counterparty_initial_commitment_tx.trust();
+ let counterparty_initial_bitcoin_tx = counterparty_trusted_tx.built_transaction();
+
+ log_trace!(logger, "Initial counterparty tx for channel {} is: txid {} tx {}",
+ &self.context.channel_id(), counterparty_initial_bitcoin_tx.txid, encode::serialize_hex(&counterparty_initial_bitcoin_tx.transaction));
+
+ let holder_signer = self.context.build_holder_transaction_keys(self.context.cur_holder_commitment_transaction_number);
+ let initial_commitment_tx = self.context.build_commitment_transaction(self.context.cur_holder_commitment_transaction_number, &holder_signer, true, false, logger).tx;
+ {
+ let trusted_tx = initial_commitment_tx.trust();
+ let initial_commitment_bitcoin_tx = trusted_tx.built_transaction();
+ let sighash = initial_commitment_bitcoin_tx.get_sighash_all(&funding_script, self.context.channel_value_satoshis);
+ // They sign our commitment transaction, allowing us to broadcast the tx if we wish.
+ if let Err(_) = self.context.secp_ctx.verify_ecdsa(&sighash, &msg.signature, &self.context.get_counterparty_pubkeys().funding_pubkey) {
+ return Err((self, ChannelError::Close("Invalid funding_signed signature from peer".to_owned())));
+ }
+ }
+
+ let holder_commitment_tx = HolderCommitmentTransaction::new(
+ initial_commitment_tx,
+ msg.signature,
+ Vec::new(),
+ &self.context.get_holder_pubkeys().funding_pubkey,
+ self.context.counterparty_funding_pubkey()
+ );
+
+ let validated =
+ self.context.holder_signer.as_ref().validate_holder_commitment(&holder_commitment_tx, Vec::new());
+ if validated.is_err() {
+ return Err((self, ChannelError::Close("Failed to validate our commitment".to_owned())));
+ }
+
+ let funding_redeemscript = self.context.get_funding_redeemscript();
+ let funding_txo = self.context.get_funding_txo().unwrap();
+ let funding_txo_script = funding_redeemscript.to_v0_p2wsh();
+ let obscure_factor = get_commitment_transaction_number_obscure_factor(&self.context.get_holder_pubkeys().payment_point, &self.context.get_counterparty_pubkeys().payment_point, self.context.is_outbound());
+ let shutdown_script = self.context.shutdown_scriptpubkey.clone().map(|script| script.into_inner());
+ let mut monitor_signer = signer_provider.derive_channel_signer(self.context.channel_value_satoshis, self.context.channel_keys_id);
+ monitor_signer.provide_channel_parameters(&self.context.channel_transaction_parameters);
+ let channel_monitor = ChannelMonitor::new(self.context.secp_ctx.clone(), monitor_signer,
+ shutdown_script, self.context.get_holder_selected_contest_delay(),
+ &self.context.destination_script, (funding_txo, funding_txo_script),
+ &self.context.channel_transaction_parameters,
+ funding_redeemscript.clone(), self.context.channel_value_satoshis,
+ obscure_factor,
+ holder_commitment_tx, best_block, self.context.counterparty_node_id);
+ channel_monitor.provide_initial_counterparty_commitment_tx(
+ counterparty_initial_bitcoin_tx.txid, Vec::new(),
+ self.context.cur_counterparty_commitment_transaction_number,
+ self.context.counterparty_cur_commitment_point.unwrap(),
+ counterparty_initial_commitment_tx.feerate_per_kw(),
+ counterparty_initial_commitment_tx.to_broadcaster_value_sat(),
+ counterparty_initial_commitment_tx.to_countersignatory_value_sat(), logger);
+
+ assert!(!self.context.channel_state.is_monitor_update_in_progress()); // We have no had any monitor(s) yet to fail update!
+ if self.context.is_batch_funding() {
+ self.context.channel_state = ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags::WAITING_FOR_BATCH);
+ } else {
+ self.context.channel_state = ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags::new());
+ }
+ self.context.cur_holder_commitment_transaction_number -= 1;
+ self.context.cur_counterparty_commitment_transaction_number -= 1;
+
+ log_info!(logger, "Received funding_signed from peer for channel {}", &self.context.channel_id());
+
+ let mut channel = Channel { context: self.context };
+
+ let need_channel_ready = channel.check_get_channel_ready(0).is_some();
+ channel.monitor_updating_paused(false, false, need_channel_ready, Vec::new(), Vec::new(), Vec::new());
+ Ok((channel, channel_monitor))
+ }
+
+ /// Indicates that the signer may have some signatures for us, so we should retry if we're
+ /// blocked.
+ #[allow(unused)]
+ 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");
+ self.get_funding_created_msg(logger)
+ } else { None }
+ }
}
/// A not-yet-funded inbound (from counterparty) channel using V1 channel establishment.
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};
value: 10000000, script_pubkey: output_script.clone(),
}]};
let funding_outpoint = OutPoint{ txid: tx.txid(), index: 0 };
- let (mut node_a_chan, funding_created_msg) = node_a_chan.get_funding_created(tx.clone(), funding_outpoint, false, &&logger).map_err(|_| ()).unwrap();
+ let funding_created_msg = node_a_chan.get_funding_created(tx.clone(), funding_outpoint, false, &&logger).map_err(|_| ()).unwrap();
let (_, funding_signed_msg, _) = node_b_chan.funding_created(&funding_created_msg.unwrap(), best_block, &&keys_provider, &&logger).map_err(|_| ()).unwrap();
// Node B --> Node A: funding signed
- let _ = node_a_chan.funding_signed(&funding_signed_msg.unwrap(), best_block, &&keys_provider, &&logger).unwrap();
+ let res = node_a_chan.funding_signed(&funding_signed_msg.unwrap(), best_block, &&keys_provider, &&logger);
+ let (mut node_a_chan, _) = if let Ok(res) = res { res } else { panic!(); };
// Put some inbound and outbound HTLCs in A's channel.
let htlc_amount_msat = 11_092_000; // put an amount below A's effective dust limit but above B's.
value: 10000000, script_pubkey: output_script.clone(),
}]};
let funding_outpoint = OutPoint{ txid: tx.txid(), index: 0 };
- let (mut node_a_chan, funding_created_msg) = node_a_chan.get_funding_created(tx.clone(), funding_outpoint, false, &&logger).map_err(|_| ()).unwrap();
+ let funding_created_msg = node_a_chan.get_funding_created(tx.clone(), funding_outpoint, false, &&logger).map_err(|_| ()).unwrap();
let (mut node_b_chan, funding_signed_msg, _) = node_b_chan.funding_created(&funding_created_msg.unwrap(), best_block, &&keys_provider, &&logger).map_err(|_| ()).unwrap();
// Node B --> Node A: funding signed
- let _ = node_a_chan.funding_signed(&funding_signed_msg.unwrap(), best_block, &&keys_provider, &&logger).unwrap();
+ let res = node_a_chan.funding_signed(&funding_signed_msg.unwrap(), best_block, &&keys_provider, &&logger);
+ let (mut node_a_chan, _) = if let Ok(res) = res { res } else { panic!(); };
// Now disconnect the two nodes and check that the commitment point in
// Node B's channel_reestablish message is sane.
value: 10000000, script_pubkey: output_script.clone(),
}]};
let funding_outpoint = OutPoint{ txid: tx.txid(), index: 0 };
- let (mut node_a_chan, funding_created_msg) = node_a_chan.get_funding_created(tx.clone(), funding_outpoint, false, &&logger).map_err(|_| ()).unwrap();
+ let funding_created_msg = node_a_chan.get_funding_created(tx.clone(), funding_outpoint, false, &&logger).map_err(|_| ()).unwrap();
let (_, funding_signed_msg, _) = node_b_chan.funding_created(&funding_created_msg.unwrap(), best_block, &&keys_provider, &&logger).map_err(|_| ()).unwrap();
// Node B --> Node A: funding signed
- let _ = node_a_chan.funding_signed(&funding_signed_msg.unwrap(), best_block, &&keys_provider, &&logger).unwrap();
+ let res = node_a_chan.funding_signed(&funding_signed_msg.unwrap(), best_block, &&keys_provider, &&logger);
+ let (mut node_a_chan, _) = if let Ok(res) = res { res } else { panic!(); };
// Make sure that receiving a channel update will update the Channel as expected.
let update = ChannelUpdate {
}
#[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(
},
]};
let funding_outpoint = OutPoint{ txid: tx.txid(), index: 0 };
- let (mut node_a_chan, funding_created_msg) = node_a_chan.get_funding_created(
- tx.clone(),
- funding_outpoint,
- true,
- &&logger,
+ let funding_created_msg = node_a_chan.get_funding_created(
+ tx.clone(), funding_outpoint, true, &&logger,
).map_err(|_| ()).unwrap();
let (mut node_b_chan, funding_signed_msg, _) = node_b_chan.funding_created(
&funding_created_msg.unwrap(),
// Receive funding_signed, but the channel will be configured to hold sending channel_ready and
// broadcasting the funding transaction until the batch is ready.
- let _ = node_a_chan.funding_signed(
- &funding_signed_msg.unwrap(),
- best_block,
- &&keys_provider,
- &&logger,
- ).unwrap();
+ let res = node_a_chan.funding_signed(
+ &funding_signed_msg.unwrap(), best_block, &&keys_provider, &&logger,
+ );
+ let (mut node_a_chan, _) = if let Ok(res) = res { res } else { panic!(); };
let node_a_updates = node_a_chan.monitor_updating_restored(
&&logger,
&&keys_provider,