use crate::ln::{PaymentHash, PaymentPreimage};
use crate::ln::msgs::DecodeError;
use crate::ln::chan_utils;
-use crate::ln::chan_utils::{CounterpartyCommitmentSecrets, HTLCOutputInCommitment, HTLCClaim, ChannelTransactionParameters, HolderCommitmentTransaction};
+use crate::ln::chan_utils::{CommitmentTransaction, CounterpartyCommitmentSecrets, HTLCOutputInCommitment, HTLCClaim, ChannelTransactionParameters, HolderCommitmentTransaction, TxCreationKeys};
use crate::ln::channelmanager::{HTLCSource, SentHTLCId};
use crate::chain;
use crate::chain::{BestBlock, WatchedOutput};
/// We use this to track static counterparty commitment transaction data and to generate any
/// justice or 2nd-stage preimage/timeout transactions.
-#[derive(PartialEq, Eq)]
+#[derive(Clone, PartialEq, Eq)]
struct CounterpartyCommitmentParameters {
counterparty_delayed_payment_base_key: PublicKey,
counterparty_htlc_base_key: PublicKey,
/// observed, as well as the transaction causing it.
///
/// Used to determine when the on-chain event can be considered safe from a chain reorganization.
-#[derive(PartialEq, Eq)]
+#[derive(Clone, PartialEq, Eq)]
struct OnchainEventEntry {
txid: Txid,
height: u32,
/// Upon discovering of some classes of onchain tx by ChannelMonitor, we may have to take actions on it
/// once they mature to enough confirmations (ANTI_REORG_DELAY)
-#[derive(PartialEq, Eq)]
+#[derive(Clone, PartialEq, Eq)]
enum OnchainEvent {
/// An outbound HTLC failing after a transaction is confirmed. Used
/// * when an outbound HTLC output is spent by us after the HTLC timed out
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>,
commitment_number: u64,
their_per_commitment_point: PublicKey,
+ feerate_per_kw: Option<u32>,
+ to_broadcaster_value_sat: Option<u64>,
+ to_countersignatory_value_sat: Option<u64>,
},
PaymentPreimage {
payment_preimage: PaymentPreimage,
},
(1, LatestCounterpartyCommitmentTXInfo) => {
(0, commitment_txid, required),
+ (1, feerate_per_kw, option),
(2, commitment_number, required),
+ (3, to_broadcaster_value_sat, option),
(4, their_per_commitment_point, required),
+ (5, to_countersignatory_value_sat, option),
(6, htlc_outputs, required_vec),
},
(2, PaymentPreimage) => {
ClaimableOnChannelClose {
/// The amount available to claim, in satoshis, excluding the on-chain fees which will be
/// required to do so.
- claimable_amount_satoshis: u64,
+ amount_satoshis: u64,
},
/// The channel has been closed, and the given balance is ours but awaiting confirmations until
/// we consider it spendable.
ClaimableAwaitingConfirmations {
/// The amount available to claim, in satoshis, possibly excluding the on-chain fees which
/// were spent in broadcasting the transaction.
- claimable_amount_satoshis: u64,
+ amount_satoshis: u64,
/// The height at which an [`Event::SpendableOutputs`] event will be generated for this
/// amount.
confirmation_height: u32,
ContentiousClaimable {
/// The amount available to claim, in satoshis, excluding the on-chain fees which will be
/// required to do so.
- claimable_amount_satoshis: u64,
+ amount_satoshis: u64,
/// The height at which the counterparty may be able to claim the balance if we have not
/// done so.
timeout_height: u32,
MaybeTimeoutClaimableHTLC {
/// The amount potentially available to claim, in satoshis, excluding the on-chain fees
/// which will be required to do so.
- claimable_amount_satoshis: u64,
+ amount_satoshis: u64,
/// The height at which we will be able to claim the balance if our counterparty has not
/// done so.
claimable_height: u32,
MaybePreimageClaimableHTLC {
/// The amount potentially available to claim, in satoshis, excluding the on-chain fees
/// which will be required to do so.
- claimable_amount_satoshis: u64,
+ amount_satoshis: u64,
/// The height at which our counterparty will be able to claim the balance if we have not
/// yet received the preimage and claimed it ourselves.
expiry_height: u32,
///
/// Note that for outputs from HTLC balances this may be excluding some on-chain fees that
/// were already spent.
- claimable_amount_satoshis: u64,
+ amount_satoshis: u64,
},
}
/// On-chain fees required to claim the balance are not included in this amount.
pub fn claimable_amount_satoshis(&self) -> u64 {
match self {
- Balance::ClaimableOnChannelClose {
- claimable_amount_satoshis,
- } => *claimable_amount_satoshis,
- Balance::ClaimableAwaitingConfirmations {
- claimable_amount_satoshis,
- ..
- } => *claimable_amount_satoshis,
- Balance::ContentiousClaimable {
- claimable_amount_satoshis,
- ..
- } => *claimable_amount_satoshis,
- Balance::MaybeTimeoutClaimableHTLC {
- ..
- } => 0,
- Balance::MaybePreimageClaimableHTLC {
- ..
- } => 0,
- Balance::CounterpartyRevokedOutputClaimable {
- claimable_amount_satoshis,
- ..
- } => *claimable_amount_satoshis,
+ Balance::ClaimableOnChannelClose { amount_satoshis, .. }|
+ Balance::ClaimableAwaitingConfirmations { amount_satoshis, .. }|
+ Balance::ContentiousClaimable { amount_satoshis, .. }|
+ Balance::CounterpartyRevokedOutputClaimable { amount_satoshis, .. }
+ => *amount_satoshis,
+ Balance::MaybeTimeoutClaimableHTLC { .. }|
+ Balance::MaybePreimageClaimableHTLC { .. }
+ => 0,
}
}
}
/// An HTLC which has been irrevocably resolved on-chain, and has reached ANTI_REORG_DELAY.
-#[derive(PartialEq, Eq)]
+#[derive(Clone, PartialEq, Eq)]
struct IrrevocablyResolvedHTLC {
commitment_tx_output_idx: Option<u32>,
/// The txid of the transaction which resolved the HTLC, this may be a commitment (if the HTLC
pub(super) inner: Mutex<ChannelMonitorImpl<Signer>>,
}
-#[derive(PartialEq)]
+impl<Signer: WriteableEcdsaChannelSigner> Clone for ChannelMonitor<Signer> where Signer: Clone {
+ fn clone(&self) -> Self {
+ let inner = self.inner.lock().unwrap().clone();
+ ChannelMonitor::from_impl(inner)
+ }
+}
+
+#[derive(Clone, PartialEq)]
pub(crate) struct ChannelMonitorImpl<Signer: WriteableEcdsaChannelSigner> {
latest_update_id: u64,
commitment_transaction_number_obscure_factor: u64,
ret
}
+ /// Gets all of the counterparty commitment transactions provided by the given update. This
+ /// may be empty if the update doesn't include any new counterparty commitments. Returned
+ /// commitment transactions are unsigned.
+ ///
+ /// This is provided so that watchtower clients in the persistence pipeline are able to build
+ /// justice transactions for each counterparty commitment upon each update. It's intended to be
+ /// used within an implementation of [`Persist::update_persisted_channel`], which is provided
+ /// with a monitor and an update.
+ ///
+ /// It is expected that a watchtower client may use this method to retrieve the latest counterparty
+ /// commitment transaction(s), and then hold the necessary data until a later update in which
+ /// the monitor has been updated with the corresponding revocation data, at which point the
+ /// monitor can sign the justice transaction.
+ ///
+ /// This will only return a non-empty list for monitor updates that have been created after
+ /// upgrading to LDK 0.0.117+. Note that no restriction lies on the monitors themselves, which
+ /// may have been created prior to upgrading.
+ ///
+ /// [`Persist::update_persisted_channel`]: crate::chain::chainmonitor::Persist::update_persisted_channel
+ pub fn counterparty_commitment_txs_from_update(&self, update: &ChannelMonitorUpdate) -> Vec<CommitmentTransaction> {
+ self.inner.lock().unwrap().counterparty_commitment_txs_from_update(update)
+ }
+
pub(crate) fn get_min_seen_secret(&self) -> u64 {
self.inner.lock().unwrap().get_min_seen_secret()
}
if let Some(conf_thresh) = holder_delayed_output_pending {
debug_assert!(holder_commitment);
return Some(Balance::ClaimableAwaitingConfirmations {
- claimable_amount_satoshis: htlc.amount_msat / 1000,
+ amount_satoshis: htlc.amount_msat / 1000,
confirmation_height: conf_thresh,
});
} else if htlc_resolved.is_some() && !htlc_output_spend_pending {
debug_assert!(!htlc.offered || htlc_spend_pending.is_none() || !htlc_spend_pending.unwrap().1,
"We don't (currently) generate preimage claims against revoked outputs, where did you get one?!");
return Some(Balance::CounterpartyRevokedOutputClaimable {
- claimable_amount_satoshis: htlc.amount_msat / 1000,
+ amount_satoshis: htlc.amount_msat / 1000,
});
}
} else if htlc.offered == holder_commitment {
// and awaiting confirmations on it.
if let Some(conf_thresh) = holder_timeout_spend_pending {
return Some(Balance::ClaimableAwaitingConfirmations {
- claimable_amount_satoshis: htlc.amount_msat / 1000,
+ amount_satoshis: htlc.amount_msat / 1000,
confirmation_height: conf_thresh,
});
} else {
return Some(Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: htlc.amount_msat / 1000,
+ amount_satoshis: htlc.amount_msat / 1000,
claimable_height: htlc.cltv_expiry,
payment_hash: htlc.payment_hash,
});
debug_assert!(holder_timeout_spend_pending.is_none());
if let Some((conf_thresh, true)) = htlc_spend_pending {
return Some(Balance::ClaimableAwaitingConfirmations {
- claimable_amount_satoshis: htlc.amount_msat / 1000,
+ amount_satoshis: htlc.amount_msat / 1000,
confirmation_height: conf_thresh,
});
} else {
return Some(Balance::ContentiousClaimable {
- claimable_amount_satoshis: htlc.amount_msat / 1000,
+ amount_satoshis: htlc.amount_msat / 1000,
timeout_height: htlc.cltv_expiry,
payment_hash: htlc.payment_hash,
payment_preimage: *payment_preimage,
}
} else if htlc_resolved.is_none() {
return Some(Balance::MaybePreimageClaimableHTLC {
- claimable_amount_satoshis: htlc.amount_msat / 1000,
+ amount_satoshis: htlc.amount_msat / 1000,
expiry_height: htlc.cltv_expiry,
payment_hash: htlc.payment_hash,
});
} else { None }
}) {
res.push(Balance::ClaimableAwaitingConfirmations {
- claimable_amount_satoshis: value,
+ amount_satoshis: value,
confirmation_height: conf_thresh,
});
} else {
descriptor: SpendableOutputDescriptor::StaticOutput { output, .. }
} = &event.event {
res.push(Balance::ClaimableAwaitingConfirmations {
- claimable_amount_satoshis: output.value,
+ amount_satoshis: output.value,
confirmation_height: event.confirmation_threshold(),
});
if let Some(confirmed_to_self_idx) = confirmed_counterparty_output.map(|(idx, _)| idx) {
.is_output_spend_pending(&BitcoinOutPoint::new(txid, confirmed_to_self_idx));
if output_spendable {
res.push(Balance::CounterpartyRevokedOutputClaimable {
- claimable_amount_satoshis: amt,
+ amount_satoshis: amt,
});
}
} else {
walk_htlcs!(true, false, us.current_holder_commitment_tx.htlc_outputs.iter().map(|(a, _, _)| a));
if let Some(conf_thresh) = pending_commitment_tx_conf_thresh {
res.push(Balance::ClaimableAwaitingConfirmations {
- claimable_amount_satoshis: us.current_holder_commitment_tx.to_self_value_sat,
+ amount_satoshis: us.current_holder_commitment_tx.to_self_value_sat,
confirmation_height: conf_thresh,
});
}
walk_htlcs!(true, false, prev_commitment.htlc_outputs.iter().map(|(a, _, _)| a));
if let Some(conf_thresh) = pending_commitment_tx_conf_thresh {
res.push(Balance::ClaimableAwaitingConfirmations {
- claimable_amount_satoshis: prev_commitment.to_self_value_sat,
+ amount_satoshis: prev_commitment.to_self_value_sat,
confirmation_height: conf_thresh,
});
}
// neither us nor our counterparty misbehaved. At worst we've under-estimated
// the amount we can claim as we'll punish a misbehaving counterparty.
res.push(Balance::ClaimableAwaitingConfirmations {
- claimable_amount_satoshis: us.current_holder_commitment_tx.to_self_value_sat,
+ amount_satoshis: us.current_holder_commitment_tx.to_self_value_sat,
confirmation_height: conf_thresh,
});
}
if htlc.transaction_output_index.is_none() { continue; }
if htlc.offered {
res.push(Balance::MaybeTimeoutClaimableHTLC {
- claimable_amount_satoshis: htlc.amount_msat / 1000,
+ amount_satoshis: htlc.amount_msat / 1000,
claimable_height: htlc.cltv_expiry,
payment_hash: htlc.payment_hash,
});
// As long as the HTLC is still in our latest commitment state, treat
// it as potentially claimable, even if it has long-since expired.
res.push(Balance::MaybePreimageClaimableHTLC {
- claimable_amount_satoshis: htlc.amount_msat / 1000,
+ amount_satoshis: htlc.amount_msat / 1000,
expiry_height: htlc.cltv_expiry,
payment_hash: htlc.payment_hash,
});
}
}
res.push(Balance::ClaimableOnChannelClose {
- claimable_amount_satoshis: us.current_holder_commitment_tx.to_self_value_sat + claimable_inbound_htlc_value_sat,
+ amount_satoshis: us.current_holder_commitment_tx.to_self_value_sat + claimable_inbound_htlc_value_sat,
});
}
},
};
log_trace!($logger, "Failing HTLC with payment_hash {} from {} counterparty commitment tx due to broadcast of {} commitment transaction {}, waiting for confirmation (at height {})",
- log_bytes!(htlc.payment_hash.0), $commitment_tx, $commitment_tx_type,
+ &htlc.payment_hash, $commitment_tx, $commitment_tx_type,
$commitment_txid_confirmed, entry.confirmation_threshold());
$self.onchain_events_awaiting_threshold_conf.push(entry);
}
ret = Err(());
}
}
- ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { commitment_txid, htlc_outputs, commitment_number, their_per_commitment_point } => {
+ ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { commitment_txid, htlc_outputs, commitment_number, their_per_commitment_point, .. } => {
log_trace!(logger, "Updating ChannelMonitor with latest counterparty commitment transaction info");
self.provide_latest_counterparty_commitment_tx(*commitment_txid, htlc_outputs.clone(), *commitment_number, *their_per_commitment_point, logger)
},
}
}
+ #[cfg(debug_assertions)] {
+ self.counterparty_commitment_txs_from_update(updates);
+ }
+
// If the updates succeeded and we were in an already closed channel state, then there's no
// need to refuse any updates we expect to receive afer seeing a confirmed commitment.
if ret.is_ok() && updates.update_id == CLOSED_CHANNEL_UPDATE_ID && self.latest_update_id == updates.update_id {
ret
}
+ fn build_counterparty_commitment_tx(
+ &self, commitment_number: u64, their_per_commitment_point: &PublicKey,
+ to_broadcaster_value: u64, to_countersignatory_value: u64, feerate_per_kw: u32,
+ mut nondust_htlcs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>
+ ) -> CommitmentTransaction {
+ let broadcaster_keys = &self.onchain_tx_handler.channel_transaction_parameters
+ .counterparty_parameters.as_ref().unwrap().pubkeys;
+ let countersignatory_keys =
+ &self.onchain_tx_handler.channel_transaction_parameters.holder_pubkeys;
+
+ let broadcaster_funding_key = broadcaster_keys.funding_pubkey;
+ let countersignatory_funding_key = countersignatory_keys.funding_pubkey;
+ let keys = TxCreationKeys::from_channel_static_keys(&their_per_commitment_point,
+ &broadcaster_keys, &countersignatory_keys, &self.onchain_tx_handler.secp_ctx);
+ let channel_parameters =
+ &self.onchain_tx_handler.channel_transaction_parameters.as_counterparty_broadcastable();
+
+ CommitmentTransaction::new_with_auxiliary_htlc_data(commitment_number,
+ to_broadcaster_value, to_countersignatory_value, broadcaster_funding_key,
+ countersignatory_funding_key, keys, feerate_per_kw, &mut nondust_htlcs,
+ channel_parameters)
+ }
+
+ pub(crate) fn counterparty_commitment_txs_from_update(&self, update: &ChannelMonitorUpdate) -> Vec<CommitmentTransaction> {
+ update.updates.iter().filter_map(|update| {
+ match update {
+ &ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { commitment_txid,
+ ref htlc_outputs, commitment_number, their_per_commitment_point,
+ feerate_per_kw: Some(feerate_per_kw),
+ to_broadcaster_value_sat: Some(to_broadcaster_value),
+ to_countersignatory_value_sat: Some(to_countersignatory_value) } => {
+
+ let nondust_htlcs = htlc_outputs.iter().filter_map(|(htlc, _)| {
+ htlc.transaction_output_index.map(|_| (htlc.clone(), None))
+ }).collect::<Vec<_>>();
+
+ let commitment_tx = self.build_counterparty_commitment_tx(commitment_number,
+ &their_per_commitment_point, to_broadcaster_value,
+ to_countersignatory_value, feerate_per_kw, nondust_htlcs);
+
+ debug_assert_eq!(commitment_tx.trust().txid(), commitment_txid);
+
+ Some(commitment_tx)
+ },
+ _ => None,
+ }
+ }).collect()
+ }
+
/// Can only fail if idx is < get_min_seen_secret
fn get_secret(&self, idx: u64) -> Option<[u8; 32]> {
self.commitment_secrets.get_secret(idx)
}
log_debug!(logger, "HTLC {} failure update in {} has got enough confirmations to be passed upstream",
- log_bytes!(payment_hash.0), entry.txid);
+ &payment_hash, entry.txid);
self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate {
payment_hash,
payment_preimage: None,
OnchainEvent::MaturingOutput { descriptor } => {
log_debug!(logger, "Descriptor {} has got enough confirmations to be passed upstream", log_spendable!(descriptor));
self.pending_events.push(Event::SpendableOutputs {
- outputs: vec![descriptor]
+ outputs: vec![descriptor],
+ channel_id: Some(self.funding_info.0.to_channel_id()),
});
self.spendable_txids_confirmed.push(entry.txid);
},
(outbound_htlc && !$source_avail && (accepted_preimage_claim || offered_preimage_claim)) {
log_error!(logger, "Input spending {} ({}:{}) in {} resolves {} HTLC with payment hash {} with {}!",
$tx_info, input.previous_output.txid, input.previous_output.vout, tx.txid(),
- if outbound_htlc { "outbound" } else { "inbound" }, log_bytes!($htlc.payment_hash.0),
+ if outbound_htlc { "outbound" } else { "inbound" }, &$htlc.payment_hash,
if revocation_sig_claim { "revocation sig" } else { "preimage claim after we'd passed the HTLC resolution back. We can likely claim the HTLC output with a revocation claim" });
} else {
log_info!(logger, "Input spending {} ({}:{}) in {} resolves {} HTLC with payment hash {} with {}",
$tx_info, input.previous_output.txid, input.previous_output.vout, tx.txid(),
- if outbound_htlc { "outbound" } else { "inbound" }, log_bytes!($htlc.payment_hash.0),
+ if outbound_htlc { "outbound" } else { "inbound" }, &$htlc.payment_hash,
if revocation_sig_claim { "revocation sig" } else if accepted_preimage_claim || offered_preimage_claim { "preimage" } else { "timeout" });
}
}
commitment_tx_output_idx: Some(input.previous_output.vout),
},
};
- log_info!(logger, "Failing HTLC with payment_hash {} timeout by a spend tx, waiting for confirmation (at height {})", log_bytes!(payment_hash.0), entry.confirmation_threshold());
+ log_info!(logger, "Failing HTLC with payment_hash {} timeout by a spend tx, waiting for confirmation (at height {})", &payment_hash, entry.confirmation_threshold());
self.onchain_events_awaiting_threshold_conf.push(entry);
}
}
assert!(err.contains("ChannelMonitor storage failure")));
check_added_monitors!(nodes[1], 2); // After the failure we generate a close-channel monitor update
check_closed_broadcast!(nodes[1], true);
- check_closed_event!(nodes[1], 1, ClosureReason::ProcessingError { err: "ChannelMonitor storage failure".to_string() });
+ check_closed_event!(nodes[1], 1, ClosureReason::ProcessingError { err: "ChannelMonitor storage failure".to_string() },
+ [nodes[0].node.get_our_node_id()], 100000);
// Build a new ChannelMonitorUpdate which contains both the failing commitment tx update
// and provides the claim preimages for the two pending HTLCs. The first update generates