use crate::chain::{BestBlock, WatchedOutput};
use crate::chain::chaininterface::{BroadcasterInterface, FeeEstimator, LowerBoundedFeeEstimator};
use crate::chain::transaction::{OutPoint, TransactionData};
-use crate::chain::keysinterface::{SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, WriteableEcdsaChannelSigner, SignerProvider, EntropySource};
-#[cfg(anchors)]
-use crate::chain::onchaintx::ClaimEvent;
-use crate::chain::onchaintx::OnchainTxHandler;
+use crate::sign::{SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, WriteableEcdsaChannelSigner, SignerProvider, EntropySource};
+use crate::chain::onchaintx::{ClaimEvent, OnchainTxHandler};
use crate::chain::package::{CounterpartyOfferedHTLCOutput, CounterpartyReceivedHTLCOutput, HolderFundingOutput, HolderHTLCOutput, PackageSolvingData, PackageTemplate, RevokedOutput, RevokedHTLCOutput};
use crate::chain::Filter;
use crate::util::logger::Logger;
use crate::util::ser::{Readable, ReadableArgs, RequiredWrapper, MaybeReadable, UpgradableRequired, Writer, Writeable, U48};
use crate::util::byte_utils;
-use crate::events::Event;
-#[cfg(anchors)]
-use crate::events::bump_transaction::{AnchorDescriptor, HTLCDescriptor, BumpTransactionEvent};
+use crate::events::{Event, EventHandler};
+use crate::events::bump_transaction::{ChannelDerivationParameters, AnchorDescriptor, HTLCDescriptor, BumpTransactionEvent};
use crate::prelude::*;
use core::{cmp, mem};
(8, delayed_payment_key, required),
(10, per_commitment_point, required),
(12, feerate_per_kw, required),
- (14, htlc_outputs, vec_type)
+ (14, htlc_outputs, required_vec)
});
-#[cfg(anchors)]
impl HolderSignedTx {
fn non_dust_htlcs(&self) -> Vec<HTLCOutputInCommitment> {
self.htlc_outputs.iter().filter_map(|(htlc, _, _)| {
/// 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
impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
(0, LatestHolderCommitmentTXInfo) => {
(0, commitment_tx, required),
- (1, claimed_htlcs, vec_type),
- (2, htlc_outputs, vec_type),
+ (1, claimed_htlcs, optional_vec),
+ (2, htlc_outputs, required_vec),
(4, nondust_htlc_sources, optional_vec),
},
(1, LatestCounterpartyCommitmentTXInfo) => {
(0, commitment_txid, required),
(2, commitment_number, required),
(4, their_per_commitment_point, required),
- (6, htlc_outputs, vec_type),
+ (6, htlc_outputs, required_vec),
},
(2, PaymentPreimage) => {
(0, payment_preimage, required),
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,
+ /// The payment hash that locks this HTLC.
+ payment_hash: PaymentHash,
+ /// The preimage that can be used to claim this HTLC.
+ payment_preimage: PaymentPreimage,
},
/// HTLCs which we sent to our counterparty which are claimable after a timeout (less on-chain
/// fees) if the counterparty does not know the preimage for the HTLCs. These are somewhat
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,
+ /// The payment hash whose preimage our counterparty needs to claim this HTLC.
+ payment_hash: PaymentHash,
},
/// HTLCs which we received from our counterparty which are claimable with a preimage which we
/// do not currently have. This will only be claimable if we receive the preimage from the node
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,
+ /// The payment hash whose preimage we need to claim this HTLC.
+ payment_hash: PaymentHash,
},
/// The channel has been closed, and our counterparty broadcasted a revoked commitment
/// transaction.
///
/// 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,
},
}
+impl Balance {
+ /// The amount claimable, in satoshis. This excludes balances that we are unsure if we are able
+ /// to claim, this is because we are waiting for a preimage or for a timeout to expire. For more
+ /// information on these balances see [`Balance::MaybeTimeoutClaimableHTLC`] and
+ /// [`Balance::MaybePreimageClaimableHTLC`].
+ ///
+ /// 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 { 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
/// You MUST ensure that no ChannelMonitors for a given channel anywhere contain out-of-date
/// information and are actively monitoring the chain.
///
-/// Pending Events or updated HTLCs which have not yet been read out by
-/// get_and_clear_pending_monitor_events or get_and_clear_pending_events are serialized to disk and
-/// reloaded at deserialize-time. Thus, you must ensure that, when handling events, all events
-/// gotten are fully handled before re-serializing the new state.
-///
/// Note that the deserializer is only implemented for (BlockHash, ChannelMonitor), which
/// tells you the last block hash which was block_connect()ed. You MUST rescan any blocks along
/// the "reorg path" (ie disconnecting blocks until you find a common ancestor from both the
#[cfg(test)]
pub(crate) inner: Mutex<ChannelMonitorImpl<Signer>>,
#[cfg(not(test))]
- inner: Mutex<ChannelMonitorImpl<Signer>>,
+ pub(super) inner: Mutex<ChannelMonitorImpl<Signer>>,
+}
+
+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(PartialEq)]
+#[derive(Clone, PartialEq)]
pub(crate) struct ChannelMonitorImpl<Signer: WriteableEcdsaChannelSigner> {
latest_update_id: u64,
commitment_transaction_number_obscure_factor: u64,
// we further MUST NOT generate events during block/transaction-disconnection.
pending_monitor_events: Vec<MonitorEvent>,
- pending_events: Vec<Event>,
+ pub(super) pending_events: Vec<Event>,
+ pub(super) is_processing_pending_events: bool,
// Used to track on-chain events (i.e., transactions part of channels confirmed on chain) on
// which to take actions once they reach enough confirmations. Each entry includes the
write_tlv_fields!(writer, {
(1, self.funding_spend_confirmed, option),
- (3, self.htlcs_resolved_on_chain, vec_type),
- (5, self.pending_monitor_events, vec_type),
+ (3, self.htlcs_resolved_on_chain, required_vec),
+ (5, self.pending_monitor_events, required_vec),
(7, self.funding_spend_seen, required),
(9, self.counterparty_node_id, option),
(11, self.confirmed_commitment_tx_counterparty_output, option),
- (13, self.spendable_txids_confirmed, vec_type),
+ (13, self.spendable_txids_confirmed, required_vec),
(15, self.counterparty_fulfilled_htlcs, required),
});
}
}
+macro_rules! _process_events_body {
+ ($self_opt: expr, $event_to_handle: expr, $handle_event: expr) => {
+ loop {
+ let (pending_events, repeated_events);
+ if let Some(us) = $self_opt {
+ let mut inner = us.inner.lock().unwrap();
+ if inner.is_processing_pending_events {
+ break;
+ }
+ inner.is_processing_pending_events = true;
+
+ pending_events = inner.pending_events.clone();
+ repeated_events = inner.get_repeated_events();
+ } else { break; }
+ let num_events = pending_events.len();
+
+ for event in pending_events.into_iter().chain(repeated_events.into_iter()) {
+ $event_to_handle = event;
+ $handle_event;
+ }
+
+ if let Some(us) = $self_opt {
+ let mut inner = us.inner.lock().unwrap();
+ inner.pending_events.drain(..num_events);
+ inner.is_processing_pending_events = false;
+ if !inner.pending_events.is_empty() {
+ // If there's more events to process, go ahead and do so.
+ continue;
+ }
+ }
+ break;
+ }
+ }
+}
+pub(super) use _process_events_body as process_events_body;
+
impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
/// For lockorder enforcement purposes, we need to have a single site which constructs the
/// `inner` mutex, otherwise cases where we lock two monitors at the same time (eg in our
payment_preimages: HashMap::new(),
pending_monitor_events: Vec::new(),
pending_events: Vec::new(),
+ is_processing_pending_events: false,
onchain_events_awaiting_threshold_conf: Vec::new(),
outputs_to_watch,
self.inner.lock().unwrap().get_and_clear_pending_monitor_events()
}
- /// Gets the list of pending events which were generated by previous actions, clearing the list
- /// in the process.
+ /// Processes [`SpendableOutputs`] events produced from each [`ChannelMonitor`] upon maturity.
+ ///
+ /// For channels featuring anchor outputs, this method will also process [`BumpTransaction`]
+ /// events produced from each [`ChannelMonitor`] while there is a balance to claim onchain
+ /// within each channel. As the confirmation of a commitment transaction may be critical to the
+ /// safety of funds, we recommend invoking this every 30 seconds, or lower if running in an
+ /// environment with spotty connections, like on mobile.
///
- /// This is called by the [`EventsProvider::process_pending_events`] implementation for
- /// [`ChainMonitor`].
+ /// An [`EventHandler`] may safely call back to the provider, though this shouldn't be needed in
+ /// order to handle these events.
+ ///
+ /// [`SpendableOutputs`]: crate::events::Event::SpendableOutputs
+ /// [`BumpTransaction`]: crate::events::Event::BumpTransaction
+ pub fn process_pending_events<H: Deref>(&self, handler: &H) where H::Target: EventHandler {
+ let mut ev;
+ process_events_body!(Some(self), ev, handler.handle_event(ev));
+ }
+
+ /// Processes any events asynchronously.
///
- /// [`EventsProvider::process_pending_events`]: crate::events::EventsProvider::process_pending_events
- /// [`ChainMonitor`]: crate::chain::chainmonitor::ChainMonitor
+ /// See [`Self::process_pending_events`] for more information.
+ pub async fn process_pending_events_async<Future: core::future::Future, H: Fn(Event) -> Future>(
+ &self, handler: &H
+ ) {
+ let mut ev;
+ process_events_body!(Some(self), ev, { handler(ev).await });
+ }
+
+ #[cfg(test)]
pub fn get_and_clear_pending_events(&self) -> Vec<Event> {
- self.inner.lock().unwrap().get_and_clear_pending_events()
+ let mut ret = Vec::new();
+ let mut lck = self.inner.lock().unwrap();
+ mem::swap(&mut ret, &mut lck.pending_events);
+ ret.append(&mut lck.get_repeated_events());
+ ret
}
pub(crate) fn get_min_seen_secret(&self) -> u64 {
pub fn current_best_block(&self) -> BestBlock {
self.inner.lock().unwrap().best_block.clone()
}
+
+ /// Triggers rebroadcasts/fee-bumps of pending claims from a force-closed channel. This is
+ /// crucial in preventing certain classes of pinning attacks, detecting substantial mempool
+ /// feerate changes between blocks, and ensuring reliability if broadcasting fails. We recommend
+ /// invoking this every 30 seconds, or lower if running in an environment with spotty
+ /// connections, like on mobile.
+ pub fn rebroadcast_pending_claims<B: Deref, F: Deref, L: Deref>(
+ &self, broadcaster: B, fee_estimator: F, logger: L,
+ )
+ where
+ B::Target: BroadcasterInterface,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
+ {
+ let fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
+ let mut inner = self.inner.lock().unwrap();
+ let current_height = inner.best_block.height;
+ inner.onchain_tx_handler.rebroadcast_pending_claims(
+ current_height, &broadcaster, &fee_estimator, &logger,
+ );
+ }
}
impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
debug_assert!(htlc_input_idx_opt.is_some());
BitcoinOutPoint::new(*txid, htlc_input_idx_opt.unwrap_or(0))
} else {
- debug_assert!(!self.onchain_tx_handler.opt_anchors());
+ debug_assert!(!self.onchain_tx_handler.channel_type_features().supports_anchors_zero_fee_htlc_tx());
BitcoinOutPoint::new(*txid, 0)
}
} else {
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,
});
}
- } else if self.payment_preimages.get(&htlc.payment_hash).is_some() {
+ } else if let Some(payment_preimage) = self.payment_preimages.get(&htlc.payment_hash) {
// Otherwise (the payment was inbound), only expose it as claimable if
// we know the preimage.
// Note that if there is a pending claim, but it did not use the
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,
});
}
None
} 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,
});
} else if us.payment_preimages.get(&htlc.payment_hash).is_some() {
claimable_inbound_htlc_value_sat += htlc.amount_msat / 1000;
// 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,
});
}
where B::Target: BroadcasterInterface,
L::Target: Logger,
{
- for tx in self.get_latest_holder_commitment_txn(logger).iter() {
+ let commit_txs = self.get_latest_holder_commitment_txn(logger);
+ let mut txs = vec![];
+ for tx in commit_txs.iter() {
log_info!(logger, "Broadcasting local {}", log_tx!(tx));
- broadcaster.broadcast_transaction(tx);
+ txs.push(tx);
}
+ broadcaster.broadcast_transactions(&txs);
self.pending_monitor_events.push(MonitorEvent::CommitmentTxConfirmed(self.funding_info.0));
}
F::Target: FeeEstimator,
L::Target: Logger,
{
- log_info!(logger, "Applying update to monitor {}, bringing update_id from {} to {} with {} changes.",
- log_funding_info!(self), self.latest_update_id, updates.update_id, updates.updates.len());
+ if self.latest_update_id == CLOSED_CHANNEL_UPDATE_ID && updates.update_id == CLOSED_CHANNEL_UPDATE_ID {
+ log_info!(logger, "Applying post-force-closed update to monitor {} with {} change(s).",
+ log_funding_info!(self), updates.updates.len());
+ } else if updates.update_id == CLOSED_CHANNEL_UPDATE_ID {
+ log_info!(logger, "Applying force close update to monitor {} with {} change(s).",
+ log_funding_info!(self), updates.updates.len());
+ } else {
+ log_info!(logger, "Applying update to monitor {}, bringing update_id from {} to {} with {} change(s).",
+ log_funding_info!(self), self.latest_update_id, updates.update_id, updates.updates.len());
+ }
// ChannelMonitor updates may be applied after force close if we receive a preimage for a
// broadcasted commitment transaction HTLC output that we'd like to claim on-chain. If this
// is the case, we no longer have guaranteed access to the monitor's update ID, so we use a
_ => false,
}).is_some();
if detected_funding_spend {
+ log_trace!(logger, "Avoiding commitment broadcast, already detected confirmed spend onchain");
continue;
}
self.broadcast_latest_holder_commitment_txn(broadcaster, logger);
// If the channel supports anchor outputs, we'll need to emit an external
// event to be consumed such that a child transaction is broadcast with a
// high enough feerate for the parent commitment transaction to confirm.
- if self.onchain_tx_handler.opt_anchors() {
+ if self.onchain_tx_handler.channel_type_features().supports_anchors_zero_fee_htlc_tx() {
let funding_output = HolderFundingOutput::build(
self.funding_redeemscript.clone(), self.channel_value_satoshis,
- self.onchain_tx_handler.opt_anchors(),
+ self.onchain_tx_handler.channel_type_features().clone(),
);
let best_block_height = self.best_block.height();
let commitment_package = PackageTemplate::build_package(
self.funding_info.0.txid.clone(), self.funding_info.0.index as u32,
PackageSolvingData::HolderFundingOutput(funding_output),
- best_block_height, false, best_block_height,
+ best_block_height, best_block_height
);
self.onchain_tx_handler.update_claims_view_from_requests(
vec![commitment_package], best_block_height, best_block_height,
self.latest_update_id = updates.update_id;
- if ret.is_ok() && self.funding_spend_seen {
+ // Refuse updates after we've detected a spend onchain, but only if we haven't processed a
+ // force closed monitor update yet.
+ if ret.is_ok() && self.funding_spend_seen && self.latest_update_id != CLOSED_CHANNEL_UPDATE_ID {
log_error!(logger, "Refusing Channel Monitor Update as counterparty attempted to update commitment after funding was spent");
Err(())
} else { ret }
ret
}
- pub fn get_and_clear_pending_events(&mut self) -> Vec<Event> {
- let mut ret = Vec::new();
- mem::swap(&mut ret, &mut self.pending_events);
- #[cfg(anchors)]
- for claim_event in self.onchain_tx_handler.get_and_clear_pending_claim_events().drain(..) {
+ /// Gets the set of events that are repeated regularly (e.g. those which RBF bump
+ /// transactions). We're okay if we lose these on restart as they'll be regenerated for us at
+ /// some regular interval via [`ChannelMonitor::rebroadcast_pending_claims`].
+ pub(super) fn get_repeated_events(&mut self) -> Vec<Event> {
+ let pending_claim_events = self.onchain_tx_handler.get_and_clear_pending_claim_events();
+ let mut ret = Vec::with_capacity(pending_claim_events.len());
+ for (claim_id, claim_event) in pending_claim_events {
match claim_event {
ClaimEvent::BumpCommitment {
package_target_feerate_sat_per_1000_weight, commitment_tx, anchor_output_idx,
let commitment_tx_fee_satoshis = self.channel_value_satoshis -
commitment_tx.output.iter().fold(0u64, |sum, output| sum + output.value);
ret.push(Event::BumpTransaction(BumpTransactionEvent::ChannelClose {
+ claim_id,
package_target_feerate_sat_per_1000_weight,
commitment_tx,
commitment_tx_fee_satoshis,
anchor_descriptor: AnchorDescriptor {
- channel_keys_id: self.channel_keys_id,
- channel_value_satoshis: self.channel_value_satoshis,
+ channel_derivation_parameters: ChannelDerivationParameters {
+ keys_id: self.channel_keys_id,
+ value_satoshis: self.channel_value_satoshis,
+ transaction_parameters: self.onchain_tx_handler.channel_transaction_parameters.clone(),
+ },
outpoint: BitcoinOutPoint {
txid: commitment_txid,
vout: anchor_output_idx,
let mut htlc_descriptors = Vec::with_capacity(htlcs.len());
for htlc in htlcs {
htlc_descriptors.push(HTLCDescriptor {
- channel_keys_id: self.channel_keys_id,
- channel_value_satoshis: self.channel_value_satoshis,
- channel_parameters: self.onchain_tx_handler.channel_transaction_parameters.clone(),
+ channel_derivation_parameters: ChannelDerivationParameters {
+ keys_id: self.channel_keys_id,
+ value_satoshis: self.channel_value_satoshis,
+ transaction_parameters: self.onchain_tx_handler.channel_transaction_parameters.clone(),
+ },
commitment_txid: htlc.commitment_txid,
per_commitment_number: htlc.per_commitment_number,
+ per_commitment_point: self.onchain_tx_handler.signer.get_per_commitment_point(
+ htlc.per_commitment_number, &self.onchain_tx_handler.secp_ctx,
+ ),
htlc: htlc.htlc,
preimage: htlc.preimage,
counterparty_sig: htlc.counterparty_sig,
});
}
ret.push(Event::BumpTransaction(BumpTransactionEvent::HTLCResolution {
+ claim_id,
target_feerate_sat_per_1000_weight,
htlc_descriptors,
tx_lock_time,
// First, process non-htlc outputs (to_holder & to_counterparty)
for (idx, outp) in tx.output.iter().enumerate() {
if outp.script_pubkey == revokeable_p2wsh {
- let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, outp.value, self.counterparty_commitment_params.on_counterparty_tx_csv);
- let justice_package = PackageTemplate::build_package(commitment_txid, idx as u32, PackageSolvingData::RevokedOutput(revk_outp), height + self.counterparty_commitment_params.on_counterparty_tx_csv as u32, true, height);
+ let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, outp.value, self.counterparty_commitment_params.on_counterparty_tx_csv, self.onchain_tx_handler.channel_type_features().supports_anchors_zero_fee_htlc_tx());
+ let justice_package = PackageTemplate::build_package(commitment_txid, idx as u32, PackageSolvingData::RevokedOutput(revk_outp), height + self.counterparty_commitment_params.on_counterparty_tx_csv as u32, height);
claimable_outpoints.push(justice_package);
to_counterparty_output_info =
Some((idx.try_into().expect("Txn can't have more than 2^32 outputs"), outp.value));
return (claimable_outpoints, (commitment_txid, watch_outputs),
to_counterparty_output_info);
}
- let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone(), self.onchain_tx_handler.channel_transaction_parameters.opt_anchors.is_some());
- let justice_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp), htlc.cltv_expiry, true, height);
+ let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone(), &self.onchain_tx_handler.channel_transaction_parameters.channel_type_features);
+ let justice_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp), htlc.cltv_expiry, height);
claimable_outpoints.push(justice_package);
}
}
CounterpartyOfferedHTLCOutput::build(*per_commitment_point,
self.counterparty_commitment_params.counterparty_delayed_payment_base_key,
self.counterparty_commitment_params.counterparty_htlc_base_key,
- preimage.unwrap(), htlc.clone(), self.onchain_tx_handler.opt_anchors()))
+ preimage.unwrap(), htlc.clone(), self.onchain_tx_handler.channel_type_features().clone()))
} else {
PackageSolvingData::CounterpartyReceivedHTLCOutput(
CounterpartyReceivedHTLCOutput::build(*per_commitment_point,
self.counterparty_commitment_params.counterparty_delayed_payment_base_key,
self.counterparty_commitment_params.counterparty_htlc_base_key,
- htlc.clone(), self.onchain_tx_handler.opt_anchors()))
+ htlc.clone(), self.onchain_tx_handler.channel_type_features().clone()))
};
- let aggregation = if !htlc.offered { false } else { true };
- let counterparty_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, counterparty_htlc_outp, htlc.cltv_expiry,aggregation, 0);
+ let counterparty_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, counterparty_htlc_outp, htlc.cltv_expiry, 0);
claimable_outpoints.push(counterparty_package);
}
}
let revk_outp = RevokedOutput::build(
per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key,
self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key,
- tx.output[idx].value, self.counterparty_commitment_params.on_counterparty_tx_csv
+ tx.output[idx].value, self.counterparty_commitment_params.on_counterparty_tx_csv,
+ false
);
let justice_package = PackageTemplate::build_package(
htlc_txid, idx as u32, PackageSolvingData::RevokedOutput(revk_outp),
- height + self.counterparty_commitment_params.on_counterparty_tx_csv as u32, true, height
+ height + self.counterparty_commitment_params.on_counterparty_tx_csv as u32, height
);
claimable_outpoints.push(justice_package);
if outputs_to_watch.is_none() {
for &(ref htlc, _, _) in holder_tx.htlc_outputs.iter() {
if let Some(transaction_output_index) = htlc.transaction_output_index {
- let (htlc_output, aggregable) = if htlc.offered {
+ let htlc_output = if htlc.offered {
let htlc_output = HolderHTLCOutput::build_offered(
- htlc.amount_msat, htlc.cltv_expiry, self.onchain_tx_handler.opt_anchors()
+ htlc.amount_msat, htlc.cltv_expiry, self.onchain_tx_handler.channel_type_features().clone()
);
- (htlc_output, false)
+ htlc_output
} else {
let payment_preimage = if let Some(preimage) = self.payment_preimages.get(&htlc.payment_hash) {
preimage.clone()
continue;
};
let htlc_output = HolderHTLCOutput::build_accepted(
- payment_preimage, htlc.amount_msat, self.onchain_tx_handler.opt_anchors()
+ payment_preimage, htlc.amount_msat, self.onchain_tx_handler.channel_type_features().clone()
);
- (htlc_output, self.onchain_tx_handler.opt_anchors())
+ htlc_output
};
let htlc_package = PackageTemplate::build_package(
holder_tx.txid, transaction_output_index,
PackageSolvingData::HolderHTLCOutput(htlc_output),
- htlc.cltv_expiry, aggregable, conf_height
+ htlc.cltv_expiry, conf_height
);
claim_requests.push(htlc_package);
}
let mut holder_transactions = vec![commitment_tx];
// When anchor outputs are present, the HTLC transactions are only valid once the commitment
// transaction confirms.
- if self.onchain_tx_handler.opt_anchors() {
+ if self.onchain_tx_handler.channel_type_features().supports_anchors_zero_fee_htlc_tx() {
return holder_transactions;
}
for htlc in self.current_holder_commitment_tx.htlc_outputs.iter() {
let mut holder_transactions = vec![commitment_tx];
// When anchor outputs are present, the HTLC transactions are only final once the commitment
// transaction confirms due to the CSV 1 encumberance.
- if self.onchain_tx_handler.opt_anchors() {
+ if self.onchain_tx_handler.channel_type_features().supports_anchors_zero_fee_htlc_tx() {
return holder_transactions;
}
for htlc in self.current_holder_commitment_tx.htlc_outputs.iter() {
let should_broadcast = self.should_broadcast_holder_commitment_txn(logger);
if should_broadcast {
- let funding_outp = HolderFundingOutput::build(self.funding_redeemscript.clone(), self.channel_value_satoshis, self.onchain_tx_handler.opt_anchors());
- let commitment_package = PackageTemplate::build_package(self.funding_info.0.txid.clone(), self.funding_info.0.index as u32, PackageSolvingData::HolderFundingOutput(funding_outp), self.best_block.height(), false, self.best_block.height());
+ let funding_outp = HolderFundingOutput::build(self.funding_redeemscript.clone(), self.channel_value_satoshis, self.onchain_tx_handler.channel_type_features().clone());
+ let commitment_package = PackageTemplate::build_package(self.funding_info.0.txid.clone(), self.funding_info.0.index as u32, PackageSolvingData::HolderFundingOutput(funding_outp), self.best_block.height(), self.best_block.height());
claimable_outpoints.push(commitment_package);
self.pending_monitor_events.push(MonitorEvent::CommitmentTxConfirmed(self.funding_info.0));
let commitment_tx = self.onchain_tx_handler.get_fully_signed_holder_tx(&self.funding_redeemscript);
// We can't broadcast our HTLC transactions while the commitment transaction is
// unconfirmed. We'll delay doing so until we detect the confirmed commitment in
// `transactions_confirmed`.
- if !self.onchain_tx_handler.opt_anchors() {
+ if !self.onchain_tx_handler.channel_type_features().supports_anchors_zero_fee_htlc_tx() {
// Because we're broadcasting a commitment transaction, we should construct the package
// assuming it gets confirmed in the next block. Sadly, we have code which considers
// "not yet confirmed" things as discardable, so we cannot do that here.
let mut counterparty_fulfilled_htlcs = Some(HashMap::new());
read_tlv_fields!(reader, {
(1, funding_spend_confirmed, option),
- (3, htlcs_resolved_on_chain, vec_type),
- (5, pending_monitor_events, vec_type),
+ (3, htlcs_resolved_on_chain, optional_vec),
+ (5, pending_monitor_events, optional_vec),
(7, funding_spend_seen, option),
(9, counterparty_node_id, option),
(11, confirmed_commitment_tx_counterparty_output, option),
- (13, spendable_txids_confirmed, vec_type),
+ (13, spendable_txids_confirmed, optional_vec),
(15, counterparty_fulfilled_htlcs, option),
});
payment_preimages,
pending_monitor_events: pending_monitor_events.unwrap(),
pending_events,
+ is_processing_pending_events: false,
onchain_events_awaiting_threshold_conf,
outputs_to_watch,
#[cfg(test)]
mod tests {
- use bitcoin::blockdata::block::BlockHeader;
use bitcoin::blockdata::script::{Script, Builder};
use bitcoin::blockdata::opcodes;
use bitcoin::blockdata::transaction::{Transaction, TxIn, TxOut, EcdsaSighashType};
use crate::chain::channelmonitor::ChannelMonitor;
use crate::chain::package::{weight_offered_htlc, weight_received_htlc, weight_revoked_offered_htlc, weight_revoked_received_htlc, WEIGHT_REVOKED_OUTPUT};
use crate::chain::transaction::OutPoint;
- use crate::chain::keysinterface::InMemorySigner;
+ use crate::sign::InMemorySigner;
use crate::events::ClosureReason;
use crate::ln::{PaymentPreimage, PaymentHash};
use crate::ln::chan_utils;
use crate::util::ser::{ReadableArgs, Writeable};
use crate::sync::{Arc, Mutex};
use crate::io;
- use bitcoin::{PackedLockTime, Sequence, TxMerkleNode, Witness};
+ use bitcoin::{PackedLockTime, Sequence, Witness};
+ use crate::ln::features::ChannelTypeFeatures;
use crate::prelude::*;
fn do_test_funding_spend_refuses_updates(use_local_txn: bool) {
// Connect a commitment transaction, but only to the ChainMonitor/ChannelMonitor. The
// channel is now closed, but the ChannelManager doesn't know that yet.
- let new_header = BlockHeader {
- version: 2, time: 0, bits: 0, nonce: 0,
- prev_blockhash: nodes[0].best_block_info().0,
- merkle_root: TxMerkleNode::all_zeros() };
+ let new_header = create_dummy_header(nodes[0].best_block_info().0, 0);
let conf_height = nodes[0].best_block_info().1 + 1;
nodes[1].chain_monitor.chain_monitor.transactions_confirmed(&new_header,
&[(0, broadcast_tx)], conf_height);
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
replay_update.updates.push(ChannelMonitorUpdateStep::PaymentPreimage { payment_preimage: payment_preimage_1 });
replay_update.updates.push(ChannelMonitorUpdateStep::PaymentPreimage { payment_preimage: payment_preimage_2 });
- let broadcaster = TestBroadcaster::new(Arc::clone(&nodes[1].blocks));
+ let broadcaster = TestBroadcaster::with_blocks(Arc::clone(&nodes[1].blocks));
assert!(
pre_update_monitor.update_monitor(&replay_update, &&broadcaster, &chanmon_cfgs[1].fee_estimator, &nodes[1].logger)
.is_err());
fn test_prune_preimages() {
let secp_ctx = Secp256k1::new();
let logger = Arc::new(TestLogger::new());
- let broadcaster = Arc::new(TestBroadcaster {
- txn_broadcasted: Mutex::new(Vec::new()),
- blocks: Arc::new(Mutex::new(Vec::new()))
- });
+ let broadcaster = Arc::new(TestBroadcaster::new(Network::Testnet));
let fee_estimator = TestFeeEstimator { sat_per_kw: Mutex::new(253) };
let dummy_key = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
[41; 32],
0,
[0; 32],
+ [0; 32],
);
let counterparty_pubkeys = ChannelPublicKeys {
selected_contest_delay: 67,
}),
funding_outpoint: Some(funding_outpoint),
- opt_anchors: None,
- opt_non_zero_fee_anchors: None,
+ channel_type_features: ChannelTypeFeatures::only_static_remote_key()
};
// Prune with one old state and a holder commitment tx holding a few overlaps with the
// old state.
let txid = Txid::from_hex("56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d").unwrap();
// Justice tx with 1 to_holder, 2 revoked offered HTLCs, 1 revoked received HTLCs
- for &opt_anchors in [false, true].iter() {
+ for channel_type_features in [ChannelTypeFeatures::only_static_remote_key(), ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies()].iter() {
let mut claim_tx = Transaction { version: 0, lock_time: PackedLockTime::ZERO, input: Vec::new(), output: Vec::new() };
let mut sum_actual_sigs = 0;
for i in 0..4 {
value: 0,
});
let base_weight = claim_tx.weight();
- let inputs_weight = vec![WEIGHT_REVOKED_OUTPUT, weight_revoked_offered_htlc(opt_anchors), weight_revoked_offered_htlc(opt_anchors), weight_revoked_received_htlc(opt_anchors)];
+ let inputs_weight = vec![WEIGHT_REVOKED_OUTPUT, weight_revoked_offered_htlc(channel_type_features), weight_revoked_offered_htlc(channel_type_features), weight_revoked_received_htlc(channel_type_features)];
let mut inputs_total_weight = 2; // count segwit flags
{
let mut sighash_parts = sighash::SighashCache::new(&mut claim_tx);
for (idx, inp) in inputs_weight.iter().enumerate() {
- sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs, opt_anchors);
+ sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs, channel_type_features);
inputs_total_weight += inp;
}
}
}
// Claim tx with 1 offered HTLCs, 3 received HTLCs
- for &opt_anchors in [false, true].iter() {
+ for channel_type_features in [ChannelTypeFeatures::only_static_remote_key(), ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies()].iter() {
let mut claim_tx = Transaction { version: 0, lock_time: PackedLockTime::ZERO, input: Vec::new(), output: Vec::new() };
let mut sum_actual_sigs = 0;
for i in 0..4 {
value: 0,
});
let base_weight = claim_tx.weight();
- let inputs_weight = vec![weight_offered_htlc(opt_anchors), weight_received_htlc(opt_anchors), weight_received_htlc(opt_anchors), weight_received_htlc(opt_anchors)];
+ let inputs_weight = vec![weight_offered_htlc(channel_type_features), weight_received_htlc(channel_type_features), weight_received_htlc(channel_type_features), weight_received_htlc(channel_type_features)];
let mut inputs_total_weight = 2; // count segwit flags
{
let mut sighash_parts = sighash::SighashCache::new(&mut claim_tx);
for (idx, inp) in inputs_weight.iter().enumerate() {
- sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs, opt_anchors);
+ sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs, channel_type_features);
inputs_total_weight += inp;
}
}
}
// Justice tx with 1 revoked HTLC-Success tx output
- for &opt_anchors in [false, true].iter() {
+ for channel_type_features in [ChannelTypeFeatures::only_static_remote_key(), ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies()].iter() {
let mut claim_tx = Transaction { version: 0, lock_time: PackedLockTime::ZERO, input: Vec::new(), output: Vec::new() };
let mut sum_actual_sigs = 0;
claim_tx.input.push(TxIn {
{
let mut sighash_parts = sighash::SighashCache::new(&mut claim_tx);
for (idx, inp) in inputs_weight.iter().enumerate() {
- sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs, opt_anchors);
+ sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs, channel_type_features);
inputs_total_weight += inp;
}
}