force_close_spend_delay: None,
is_outbound: true, is_channel_ready: true,
is_usable: true, is_public: true,
+ balance_msat: 0,
outbound_capacity_msat: capacity.saturating_mul(1000),
next_outbound_htlc_limit_msat: capacity.saturating_mul(1000),
next_outbound_htlc_minimum_msat: 0,
}
// Test that if the store's path to channel data is read-only, writing a
- // monitor to it results in the store returning an InProgress.
+ // monitor to it results in the store returning an UnrecoverableError.
// Windows ignores the read-only flag for folders, so this test is Unix-only.
#[cfg(not(target_os = "windows"))]
#[test]
let update_id = update_map.get(&added_monitors[0].0.to_channel_id()).unwrap();
// Set the store's directory to read-only, which should result in
- // returning a permanent failure when we then attempt to persist a
+ // returning an unrecoverable failure when we then attempt to persist a
// channel update.
let path = &store.get_data_dir();
let mut perms = fs::metadata(path).unwrap().permissions();
/// If at some point no further progress can be made towards persisting the pending updates, the
/// node should simply shut down.
///
-/// * If the persistence has failed and cannot be retried further (e.g. because of some timeout),
+/// * If the persistence has failed and cannot be retried further (e.g. because of an outage),
/// [`ChannelMonitorUpdateStatus::UnrecoverableError`] can be used, though this will result in
/// an immediate panic and future operations in LDK generally failing.
///
/// [`ChainMonitor::channel_monitor_updated`] must be called once for *each* update which occurs.
///
/// If at some point no further progress can be made towards persisting a pending update, the node
-/// should simply shut down.
+/// should simply shut down. Until then, the background task should either loop indefinitely, or
+/// persistence should be regularly retried with [`ChainMonitor::list_pending_monitor_updates`]
+/// and [`ChainMonitor::get_monitor`] (note that if a full monitor is persisted all pending
+/// monitor updates may be marked completed).
///
/// # Using remote watchtowers
///
/// claims which are awaiting confirmation.
///
/// Includes the balances from each [`ChannelMonitor`] *except* those included in
- /// `ignored_channels`.
+ /// `ignored_channels`, allowing you to filter out balances from channels which are still open
+ /// (and whose balance should likely be pulled from the [`ChannelDetails`]).
///
/// See [`ChannelMonitor::get_claimable_balances`] for more details on the exact criteria for
/// inclusion in the return value.
use bitcoin::blockdata::block::BlockHeader;
use bitcoin::blockdata::transaction::{OutPoint as BitcoinOutPoint, TxOut, Transaction};
-use bitcoin::blockdata::script::{Script, Builder};
-use bitcoin::blockdata::opcodes;
+use bitcoin::blockdata::script::Script;
use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256::Hash as Sha256;
-use bitcoin::hash_types::{Txid, BlockHash, WPubkeyHash};
+use bitcoin::hash_types::{Txid, BlockHash};
use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature};
use bitcoin::secp256k1::{SecretKey, PublicKey};
best_block: BestBlock, counterparty_node_id: PublicKey) -> ChannelMonitor<Signer> {
assert!(commitment_transaction_number_obscure_factor <= (1 << 48));
- let payment_key_hash = WPubkeyHash::hash(&keys.pubkeys().payment_point.serialize());
- let counterparty_payment_script = Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&payment_key_hash[..]).into_script();
+ let counterparty_payment_script = chan_utils::get_counterparty_payment_script(
+ &channel_parameters.channel_type_features, &keys.pubkeys().payment_point
+ );
let counterparty_channel_parameters = channel_parameters.counterparty_parameters.as_ref().unwrap();
let counterparty_delayed_payment_base_key = counterparty_channel_parameters.pubkeys.delayed_payment_basepoint;
current_height, &broadcaster, &fee_estimator, &logger,
);
}
+
+ /// Returns the descriptors for relevant outputs (i.e., those that we can spend) within the
+ /// transaction if they exist and the transaction has at least [`ANTI_REORG_DELAY`]
+ /// confirmations. For [`SpendableOutputDescriptor::DelayedPaymentOutput`] descriptors to be
+ /// returned, the transaction must have at least `max(ANTI_REORG_DELAY, to_self_delay)`
+ /// confirmations.
+ ///
+ /// Descriptors returned by this method are primarily exposed via [`Event::SpendableOutputs`]
+ /// once they are no longer under reorg risk. This method serves as a way to retrieve these
+ /// descriptors at a later time, either for historical purposes, or to replay any
+ /// missed/unhandled descriptors. For the purpose of gathering historical records, if the
+ /// channel close has fully resolved (i.e., [`ChannelMonitor::get_claimable_balances`] returns
+ /// an empty set), you can retrieve all spendable outputs by providing all descendant spending
+ /// transactions starting from the channel's funding transaction and going down three levels.
+ ///
+ /// `tx` is a transaction we'll scan the outputs of. Any transaction can be provided. If any
+ /// outputs which can be spent by us are found, at least one descriptor is returned.
+ ///
+ /// `confirmation_height` must be the height of the block in which `tx` was included in.
+ pub fn get_spendable_outputs(&self, tx: &Transaction, confirmation_height: u32) -> Vec<SpendableOutputDescriptor> {
+ let inner = self.inner.lock().unwrap();
+ let current_height = inner.best_block.height;
+ let mut spendable_outputs = inner.get_spendable_outputs(tx);
+ spendable_outputs.retain(|descriptor| {
+ let mut conf_threshold = current_height.saturating_sub(ANTI_REORG_DELAY) + 1;
+ if let SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) = descriptor {
+ conf_threshold = cmp::min(conf_threshold,
+ current_height.saturating_sub(descriptor.to_self_delay as u32) + 1);
+ }
+ conf_threshold >= confirmation_height
+ });
+ spendable_outputs
+ }
+
+ #[cfg(test)]
+ pub fn get_counterparty_payment_script(&self) -> Script{
+ self.inner.lock().unwrap().counterparty_payment_script.clone()
+ }
+
+ #[cfg(test)]
+ pub fn set_counterparty_payment_script(&self, script: Script) {
+ self.inner.lock().unwrap().counterparty_payment_script = script;
+ }
}
impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
#[cfg(test)]
pub fn deliberately_bogus_accepted_htlc_witness_program() -> Vec<u8> {
+ use bitcoin::blockdata::opcodes;
let mut ret = [opcodes::all::OP_NOP.to_u8(); 136];
ret[131] = opcodes::all::OP_DROP.to_u8();
ret[132] = opcodes::all::OP_DROP.to_u8();
{
self.payment_preimages.insert(payment_hash.clone(), payment_preimage.clone());
+ let confirmed_spend_txid = self.funding_spend_confirmed.or_else(|| {
+ self.onchain_events_awaiting_threshold_conf.iter().find_map(|event| match event.event {
+ OnchainEvent::FundingSpendConfirmation { .. } => Some(event.txid),
+ _ => None,
+ })
+ });
+ let confirmed_spend_txid = if let Some(txid) = confirmed_spend_txid {
+ txid
+ } else {
+ return;
+ };
+
// If the channel is force closed, try to claim the output from this preimage.
// First check if a counterparty commitment transaction has been broadcasted:
macro_rules! claim_htlcs {
}
}
if let Some(txid) = self.current_counterparty_commitment_txid {
- if let Some(commitment_number) = self.counterparty_commitment_txn_on_chain.get(&txid) {
- claim_htlcs!(*commitment_number, txid);
+ if txid == confirmed_spend_txid {
+ if let Some(commitment_number) = self.counterparty_commitment_txn_on_chain.get(&txid) {
+ claim_htlcs!(*commitment_number, txid);
+ } else {
+ debug_assert!(false);
+ log_error!(logger, "Detected counterparty commitment tx on-chain without tracking commitment number");
+ }
return;
}
}
if let Some(txid) = self.prev_counterparty_commitment_txid {
- if let Some(commitment_number) = self.counterparty_commitment_txn_on_chain.get(&txid) {
- claim_htlcs!(*commitment_number, txid);
+ if txid == confirmed_spend_txid {
+ if let Some(commitment_number) = self.counterparty_commitment_txn_on_chain.get(&txid) {
+ claim_htlcs!(*commitment_number, txid);
+ } else {
+ debug_assert!(false);
+ log_error!(logger, "Detected counterparty commitment tx on-chain without tracking commitment number");
+ }
return;
}
}
// *we* sign a holder commitment transaction, not when e.g. a watchtower broadcasts one of our
// holder commitment transactions.
if self.broadcasted_holder_revokable_script.is_some() {
- // Assume that the broadcasted commitment transaction confirmed in the current best
- // block. Even if not, its a reasonable metric for the bump criteria on the HTLC
- // transactions.
- let (claim_reqs, _) = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx, self.best_block.height());
- self.onchain_tx_handler.update_claims_view_from_requests(claim_reqs, self.best_block.height(), self.best_block.height(), broadcaster, fee_estimator, logger);
- if let Some(ref tx) = self.prev_holder_signed_commitment_tx {
- let (claim_reqs, _) = self.get_broadcasted_holder_claims(&tx, self.best_block.height());
+ let holder_commitment_tx = if self.current_holder_commitment_tx.txid == confirmed_spend_txid {
+ Some(&self.current_holder_commitment_tx)
+ } else if let Some(prev_holder_commitment_tx) = &self.prev_holder_signed_commitment_tx {
+ if prev_holder_commitment_tx.txid == confirmed_spend_txid {
+ Some(prev_holder_commitment_tx)
+ } else {
+ None
+ }
+ } else {
+ None
+ };
+ if let Some(holder_commitment_tx) = holder_commitment_tx {
+ // Assume that the broadcasted commitment transaction confirmed in the current best
+ // block. Even if not, its a reasonable metric for the bump criteria on the HTLC
+ // transactions.
+ let (claim_reqs, _) = self.get_broadcasted_holder_claims(&holder_commitment_tx, self.best_block.height());
self.onchain_tx_handler.update_claims_view_from_requests(claim_reqs, self.best_block.height(), self.best_block.height(), broadcaster, fee_estimator, logger);
}
}
}
self.is_resolving_htlc_output(&tx, height, &block_hash, &logger);
- self.is_paying_spendable_output(&tx, height, &block_hash, &logger);
+ self.check_tx_and_push_spendable_outputs(&tx, height, &block_hash, &logger);
}
}
}
}
- /// Check if any transaction broadcasted is paying fund back to some address we can assume to own
- fn is_paying_spendable_output<L: Deref>(&mut self, tx: &Transaction, height: u32, block_hash: &BlockHash, logger: &L) where L::Target: Logger {
- let mut spendable_output = None;
- for (i, outp) in tx.output.iter().enumerate() { // There is max one spendable output for any channel tx, including ones generated by us
- if i > ::core::u16::MAX as usize {
- // While it is possible that an output exists on chain which is greater than the
- // 2^16th output in a given transaction, this is only possible if the output is not
- // in a lightning transaction and was instead placed there by some third party who
- // wishes to give us money for no reason.
- // Namely, any lightning transactions which we pre-sign will never have anywhere
- // near 2^16 outputs both because such transactions must have ~2^16 outputs who's
- // scripts are not longer than one byte in length and because they are inherently
- // non-standard due to their size.
- // Thus, it is completely safe to ignore such outputs, and while it may result in
- // us ignoring non-lightning fund to us, that is only possible if someone fills
- // nearly a full block with garbage just to hit this case.
- continue;
- }
+ fn get_spendable_outputs(&self, tx: &Transaction) -> Vec<SpendableOutputDescriptor> {
+ let mut spendable_outputs = Vec::new();
+ for (i, outp) in tx.output.iter().enumerate() {
if outp.script_pubkey == self.destination_script {
- spendable_output = Some(SpendableOutputDescriptor::StaticOutput {
+ spendable_outputs.push(SpendableOutputDescriptor::StaticOutput {
outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
output: outp.clone(),
});
- break;
}
if let Some(ref broadcasted_holder_revokable_script) = self.broadcasted_holder_revokable_script {
if broadcasted_holder_revokable_script.0 == outp.script_pubkey {
- spendable_output = Some(SpendableOutputDescriptor::DelayedPaymentOutput(DelayedPaymentOutputDescriptor {
+ spendable_outputs.push(SpendableOutputDescriptor::DelayedPaymentOutput(DelayedPaymentOutputDescriptor {
outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
per_commitment_point: broadcasted_holder_revokable_script.1,
to_self_delay: self.on_holder_tx_csv,
channel_keys_id: self.channel_keys_id,
channel_value_satoshis: self.channel_value_satoshis,
}));
- break;
}
}
if self.counterparty_payment_script == outp.script_pubkey {
- spendable_output = Some(SpendableOutputDescriptor::StaticPaymentOutput(StaticPaymentOutputDescriptor {
+ spendable_outputs.push(SpendableOutputDescriptor::StaticPaymentOutput(StaticPaymentOutputDescriptor {
outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
output: outp.clone(),
channel_keys_id: self.channel_keys_id,
channel_value_satoshis: self.channel_value_satoshis,
+ channel_transaction_parameters: Some(self.onchain_tx_handler.channel_transaction_parameters.clone()),
}));
- break;
}
if self.shutdown_script.as_ref() == Some(&outp.script_pubkey) {
- spendable_output = Some(SpendableOutputDescriptor::StaticOutput {
+ spendable_outputs.push(SpendableOutputDescriptor::StaticOutput {
outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
output: outp.clone(),
});
- break;
}
}
- if let Some(spendable_output) = spendable_output {
+ spendable_outputs
+ }
+
+ /// Checks if the confirmed transaction is paying funds back to some address we can assume to
+ /// own.
+ fn check_tx_and_push_spendable_outputs<L: Deref>(
+ &mut self, tx: &Transaction, height: u32, block_hash: &BlockHash, logger: &L,
+ ) where L::Target: Logger {
+ for spendable_output in self.get_spendable_outputs(tx) {
let entry = OnchainEventEntry {
txid: tx.txid(),
transaction: Some(tx.clone()),
1 => { None },
_ => return Err(DecodeError::InvalidValue),
};
- let counterparty_payment_script = Readable::read(reader)?;
+ let mut counterparty_payment_script: Script = Readable::read(reader)?;
let shutdown_script = {
let script = <Script as Readable>::read(reader)?;
if script.is_empty() { None } else { Some(script) }
(17, initial_counterparty_commitment_info, option),
});
+ // Monitors for anchor outputs channels opened in v0.0.116 suffered from a bug in which the
+ // wrong `counterparty_payment_script` was being tracked. Fix it now on deserialization to
+ // give them a chance to recognize the spendable output.
+ if onchain_tx_handler.channel_type_features().supports_anchors_zero_fee_htlc_tx() &&
+ counterparty_payment_script.is_v0_p2wpkh()
+ {
+ let payment_point = onchain_tx_handler.channel_transaction_parameters.holder_pubkeys.payment_point;
+ counterparty_payment_script =
+ chan_utils::get_to_countersignatory_with_anchors_redeemscript(&payment_point).to_v0_p2wsh();
+ }
+
Ok((best_block.block_hash(), ChannelMonitor::from_impl(ChannelMonitorImpl {
latest_update_id,
commitment_transaction_number_obscure_factor,
use crate::ln::features::ChannelTypeFeatures;
use crate::ln::PaymentPreimage;
use crate::prelude::*;
-use crate::sign::{EcdsaChannelSigner, SignerProvider, WriteableEcdsaChannelSigner};
+use crate::sign::{EcdsaChannelSigner, SignerProvider, WriteableEcdsaChannelSigner, P2WPKH_WITNESS_WEIGHT};
use crate::sync::Mutex;
use crate::util::logger::Logger;
}
impl Utxo {
- const P2WPKH_WITNESS_WEIGHT: u64 = 1 /* num stack items */ +
- 1 /* sig length */ +
- 73 /* sig including sighash flag */ +
- 1 /* pubkey length */ +
- 33 /* pubkey */;
-
/// Returns a `Utxo` with the `satisfaction_weight` estimate for a legacy P2PKH output.
pub fn new_p2pkh(outpoint: OutPoint, value: u64, pubkey_hash: &PubkeyHash) -> Self {
let script_sig_size = 1 /* script_sig length */ +
value,
script_pubkey: Script::new_p2sh(&Script::new_v0_p2wpkh(pubkey_hash).script_hash()),
},
- satisfaction_weight: script_sig_size * WITNESS_SCALE_FACTOR as u64 + Self::P2WPKH_WITNESS_WEIGHT,
+ satisfaction_weight: script_sig_size * WITNESS_SCALE_FACTOR as u64 + P2WPKH_WITNESS_WEIGHT,
}
}
value,
script_pubkey: Script::new_v0_p2wpkh(pubkey_hash),
},
- satisfaction_weight: EMPTY_SCRIPT_SIG_WEIGHT + Self::P2WPKH_WITNESS_WEIGHT,
+ satisfaction_weight: EMPTY_SCRIPT_SIG_WEIGHT + P2WPKH_WITNESS_WEIGHT,
}
}
}
/// or was explicitly abandoned by [`ChannelManager::abandon_payment`].
///
/// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment
+ #[cfg(invreqfailed)]
InvoiceRequestFailed {
/// The `payment_id` to have been associated with payment for the requested invoice.
payment_id: PaymentId,
(8, funding_txo, required),
});
},
+ #[cfg(invreqfailed)]
&Event::InvoiceRequestFailed { ref payment_id } => {
33u8.write(writer)?;
write_tlv_fields!(writer, {
};
f()
},
+ #[cfg(invreqfailed)]
33u8 => {
let f = || {
_init_and_read_len_prefixed_tlv_fields!(reader, {
use bitcoin::hashes::{Hash, HashEngine};
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::ripemd160::Hash as Ripemd160;
-use bitcoin::hash_types::{Txid, PubkeyHash};
+use bitcoin::hash_types::{Txid, PubkeyHash, WPubkeyHash};
use crate::chain::chaininterface::fee_for_weight;
use crate::chain::package::WEIGHT_REVOKED_OUTPUT;
});
/// One counterparty's public keys which do not change over the life of a channel.
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct ChannelPublicKeys {
/// The public key which is used to sign all commitment transactions, as it appears in the
/// on-chain channel lock-in 2-of-2 multisig output.
res
}
+/// Returns the script for the counterparty's output on a holder's commitment transaction based on
+/// the channel type.
+pub fn get_counterparty_payment_script(channel_type_features: &ChannelTypeFeatures, payment_key: &PublicKey) -> Script {
+ if channel_type_features.supports_anchors_zero_fee_htlc_tx() {
+ get_to_countersignatory_with_anchors_redeemscript(payment_key).to_v0_p2wsh()
+ } else {
+ Script::new_v0_p2wpkh(&WPubkeyHash::hash(&payment_key.serialize()))
+ }
+}
+
/// Information about an HTLC as it appears in a commitment transaction
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HTLCOutputInCommitment {
///
/// Normally, this is converted to the broadcaster/countersignatory-organized DirectedChannelTransactionParameters
/// before use, via the as_holder_broadcastable and as_counterparty_broadcastable functions.
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct ChannelTransactionParameters {
/// Holder public keys
pub holder_pubkeys: ChannelPublicKeys,
}
/// Late-bound per-channel counterparty data used to build transactions.
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct CounterpartyChannelTransactionParameters {
/// Counter-party public keys
pub pubkeys: ChannelPublicKeys,
}
pub struct AvailableBalances {
+ /// The amount that would go to us if we close the channel, ignoring any on-chain fees.
+ pub balance_msat: u64,
/// Total amount available for our counterparty to send to us.
pub inbound_capacity_msat: u64,
/// Total amount available for us to send to our counterparty.
let inbound_stats = context.get_inbound_pending_htlc_stats(None);
let outbound_stats = context.get_outbound_pending_htlc_stats(None);
+ let mut balance_msat = context.value_to_self_msat;
+ for ref htlc in context.pending_inbound_htlcs.iter() {
+ if let InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::Fulfill(_)) = htlc.state {
+ balance_msat += htlc.amount_msat;
+ }
+ }
+ balance_msat -= outbound_stats.pending_htlcs_value_msat;
+
let outbound_capacity_msat = context.value_to_self_msat
.saturating_sub(outbound_stats.pending_htlcs_value_msat)
.saturating_sub(
outbound_capacity_msat,
next_outbound_htlc_limit_msat: available_capacity_msat,
next_outbound_htlc_minimum_msat,
+ balance_msat,
}
}
NS::Target: NodeSigner,
L::Target: Logger
{
+ let mut msgs = (None, None);
if let Some(funding_txo) = self.context.get_funding_txo() {
for &(index_in_block, tx) in txdata.iter() {
// Check if the transaction is the expected funding transaction, and if it is,
if let Some(channel_ready) = self.check_get_channel_ready(height) {
log_info!(logger, "Sending a channel_ready to our peer for channel {}", &self.context.channel_id);
let announcement_sigs = self.get_announcement_sigs(node_signer, genesis_block_hash, user_config, height, logger);
- return Ok((Some(channel_ready), announcement_sigs));
+ msgs = (Some(channel_ready), announcement_sigs);
}
}
for inp in tx.input.iter() {
}
}
}
- Ok((None, None))
+ Ok(msgs)
}
/// When a new block is connected, we check the height of the block against outbound holding
}
}
- pub fn channel_update(&mut self, msg: &msgs::ChannelUpdate) -> Result<(), ChannelError> {
- self.context.counterparty_forwarding_info = Some(CounterpartyForwardingInfo {
+ /// Applies the `ChannelUpdate` and returns a boolean indicating whether a change actually
+ /// happened.
+ pub fn channel_update(&mut self, msg: &msgs::ChannelUpdate) -> Result<bool, ChannelError> {
+ let new_forwarding_info = Some(CounterpartyForwardingInfo {
fee_base_msat: msg.contents.fee_base_msat,
fee_proportional_millionths: msg.contents.fee_proportional_millionths,
cltv_expiry_delta: msg.contents.cltv_expiry_delta
});
+ let did_change = self.context.counterparty_forwarding_info != new_forwarding_info;
+ if did_change {
+ self.context.counterparty_forwarding_info = new_forwarding_info;
+ }
- Ok(())
+ Ok(did_change)
}
/// Begins the shutdown process, getting a message for the remote peer and returning all
},
signature: Signature::from(unsafe { FFISignature::new() })
};
- node_a_chan.channel_update(&update).unwrap();
+ assert!(node_a_chan.channel_update(&update).unwrap());
// The counterparty can send an update with a higher minimum HTLC, but that shouldn't
// change our official htlc_minimum_msat.
},
None => panic!("expected counterparty forwarding info to be Some")
}
+
+ assert!(!node_a_chan.channel_update(&update).unwrap());
}
#[cfg(feature = "_test_vectors")]
}
/// Details of a channel, as returned by [`ChannelManager::list_channels`] and [`ChannelManager::list_usable_channels`]
-///
-/// Balances of a channel are available through [`ChainMonitor::get_claimable_balances`] and
-/// [`ChannelMonitor::get_claimable_balances`], calculated with respect to the corresponding on-chain
-/// transactions.
-///
-/// [`ChainMonitor::get_claimable_balances`]: crate::chain::chainmonitor::ChainMonitor::get_claimable_balances
#[derive(Clone, Debug, PartialEq)]
pub struct ChannelDetails {
/// The channel's ID (prior to funding transaction generation, this is a random 32 bytes,
///
/// This value will be `None` for objects serialized with LDK versions prior to 0.0.115.
pub feerate_sat_per_1000_weight: Option<u32>,
+ /// Our total balance. This is the amount we would get if we close the channel.
+ /// This value is not exact. Due to various in-flight changes and feerate changes, exactly this
+ /// amount is not likely to be recoverable on close.
+ ///
+ /// This does not include any pending HTLCs which are not yet fully resolved (and, thus, whose
+ /// balance is not available for inclusion in new outbound HTLCs). This further does not include
+ /// any pending outgoing HTLCs which are awaiting some other resolution to be sent.
+ /// This does not consider any on-chain fees.
+ ///
+ /// See also [`ChannelDetails::outbound_capacity_msat`]
+ pub balance_msat: u64,
/// The available outbound capacity for sending HTLCs to the remote peer. This does not include
/// any pending HTLCs which are not yet fully resolved (and, thus, whose balance is not
/// available for inclusion in new outbound HTLCs). This further does not include any pending
/// outgoing HTLCs which are awaiting some other resolution to be sent.
///
+ /// See also [`ChannelDetails::balance_msat`]
+ ///
/// This value is not exact. Due to various in-flight changes, feerate changes, and our
/// conflict-avoidance policy, exactly this amount is not likely to be spendable. However, we
/// should be able to spend nearly this amount.
/// the current state and per-HTLC limit(s). This is intended for use when routing, allowing us
/// to use a limit as close as possible to the HTLC limit we can currently send.
///
- /// See also [`ChannelDetails::next_outbound_htlc_minimum_msat`] and
- /// [`ChannelDetails::outbound_capacity_msat`].
+ /// See also [`ChannelDetails::next_outbound_htlc_minimum_msat`],
+ /// [`ChannelDetails::balance_msat`], and [`ChannelDetails::outbound_capacity_msat`].
pub next_outbound_htlc_limit_msat: u64,
/// The minimum value for sending a single HTLC to the remote peer. This is the equivalent of
/// [`ChannelDetails::next_outbound_htlc_limit_msat`] but represents a lower-bound, rather than
channel_value_satoshis: context.get_value_satoshis(),
feerate_sat_per_1000_weight: Some(context.get_feerate_sat_per_1000_weight()),
unspendable_punishment_reserve: to_self_reserve_satoshis,
+ balance_msat: balance.balance_msat,
inbound_capacity_msat: balance.inbound_capacity_msat,
outbound_capacity_msat: balance.outbound_capacity_msat,
next_outbound_htlc_limit_msat: balance.next_outbound_htlc_limit_msat,
/// In general, a path may raise:
/// * [`APIError::InvalidRoute`] when an invalid route or forwarding parameter (cltv_delta, fee,
/// node public key) is specified.
- /// * [`APIError::ChannelUnavailable`] if the next-hop channel is not available for updates
- /// (including due to previous monitor update failure or new permanent monitor update
- /// failure).
+ /// * [`APIError::ChannelUnavailable`] if the next-hop channel is not available as it has been
+ /// closed, doesn't exist, or the peer is currently disconnected.
/// * [`APIError::MonitorUpdateInProgress`] if a new monitor update failure prevented sending the
/// relevant updates.
///
/// wait until you receive either a [`Event::PaymentFailed`] or [`Event::PaymentSent`] event to
/// determine the ultimate status of a payment.
///
- /// # Requested Invoices
- ///
- /// In the case of paying a [`Bolt12Invoice`], abandoning the payment prior to receiving the
- /// invoice will result in an [`Event::InvoiceRequestFailed`] and prevent any attempts at paying
- /// it once received. The other events may only be generated once the invoice has been received.
- ///
/// # Restart Behavior
///
/// If an [`Event::PaymentFailed`] is generated and we restart without first persisting the
- /// [`ChannelManager`], another [`Event::PaymentFailed`] may be generated; likewise for
- /// [`Event::InvoiceRequestFailed`].
- ///
- /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
+ /// [`ChannelManager`], another [`Event::PaymentFailed`] may be generated.
pub fn abandon_payment(&self, payment_id: PaymentId) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
self.pending_outbound_payments.abandon_payment(payment_id, PaymentFailureReason::UserAbandoned, &self.pending_events);
btree_map::Entry::Vacant(vacant) => Some(vacant.insert(Vec::new())),
}
});
- for (channel_idx, &(temporary_channel_id, counterparty_node_id)) in temporary_channels.iter().enumerate() {
+ for &(temporary_channel_id, counterparty_node_id) in temporary_channels.iter() {
result = result.and_then(|_| self.funding_transaction_generated_intern(
temporary_channel_id,
counterparty_node_id,
return Ok(NotifyOption::SkipPersistNoEvents);
} else {
log_debug!(self.logger, "Received channel_update {:?} for channel {}.", msg, chan_id);
- try_chan_phase_entry!(self, chan.channel_update(&msg), chan_phase_entry);
+ let did_change = try_chan_phase_entry!(self, chan.channel_update(&msg), chan_phase_entry);
+ // If nothing changed after applying their update, we don't need to bother
+ // persisting.
+ if !did_change {
+ return Ok(NotifyOption::SkipPersistNoEvents);
+ }
}
} else {
return try_chan_phase_entry!(self, Err(ChannelError::Close(
fn maybe_generate_initial_closing_signed(&self) -> bool {
let mut handle_errors: Vec<(PublicKey, Result<(), _>)> = Vec::new();
let mut has_update = false;
- let mut shutdown_result = None;
- let mut unbroadcasted_batch_funding_txid = None;
+ let mut shutdown_results = Vec::new();
{
let per_peer_state = self.per_peer_state.read().unwrap();
peer_state.channel_by_id.retain(|channel_id, phase| {
match phase {
ChannelPhase::Funded(chan) => {
- unbroadcasted_batch_funding_txid = chan.context.unbroadcasted_batch_funding_txid();
+ let unbroadcasted_batch_funding_txid = chan.context.unbroadcasted_batch_funding_txid();
match chan.maybe_propose_closing_signed(&self.fee_estimator, &self.logger) {
Ok((msg_opt, tx_opt)) => {
if let Some(msg) = msg_opt {
log_info!(self.logger, "Broadcasting {}", log_tx!(tx));
self.tx_broadcaster.broadcast_transactions(&[&tx]);
update_maps_on_chan_removal!(self, &chan.context);
- shutdown_result = Some((None, Vec::new(), unbroadcasted_batch_funding_txid));
+ shutdown_results.push((None, Vec::new(), unbroadcasted_batch_funding_txid));
false
} else { true }
},
let _ = handle_error!(self, err, counterparty_node_id);
}
- if let Some(shutdown_result) = shutdown_result {
+ for shutdown_result in shutdown_results.drain(..) {
self.finish_close_channel(shutdown_result);
}
(10, self.channel_value_satoshis, required),
(12, self.unspendable_punishment_reserve, option),
(14, user_channel_id_low, required),
- (16, self.next_outbound_htlc_limit_msat, required), // Forwards compatibility for removed balance_msat field.
+ (16, self.balance_msat, required),
(18, self.outbound_capacity_msat, required),
(19, self.next_outbound_htlc_limit_msat, required),
(20, self.inbound_capacity_msat, required),
(10, channel_value_satoshis, required),
(12, unspendable_punishment_reserve, option),
(14, user_channel_id_low, required),
- (16, _balance_msat, option), // Backwards compatibility for removed balance_msat field.
+ (16, balance_msat, required),
(18, outbound_capacity_msat, required),
// Note that by the time we get past the required read above, outbound_capacity_msat will be
// filled in, so we can safely unwrap it here.
let user_channel_id = user_channel_id_low as u128 +
((user_channel_id_high_opt.unwrap_or(0 as u64) as u128) << 64);
- let _balance_msat: Option<u64> = _balance_msat;
-
Ok(Self {
inbound_scid_alias,
channel_id: channel_id.0.unwrap(),
channel_value_satoshis: channel_value_satoshis.0.unwrap(),
unspendable_punishment_reserve,
user_channel_id,
+ balance_msat: balance_msat.0.unwrap(),
outbound_capacity_msat: outbound_capacity_msat.0.unwrap(),
next_outbound_htlc_limit_msat: next_outbound_htlc_limit_msat.0.unwrap(),
next_outbound_htlc_minimum_msat: next_outbound_htlc_minimum_msat.0.unwrap(),
// Ensure the channels don't exist anymore.
assert!(nodes[0].node.list_channels().is_empty());
}
+
+fn do_test_funding_and_commitment_tx_confirm_same_block(confirm_remote_commitment: bool) {
+ // Tests that a node will forget the channel (when it only requires 1 confirmation) if the
+ // funding and commitment transaction confirm in the same block.
+ let chanmon_cfgs = create_chanmon_cfgs(2);
+ let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+ let mut min_depth_1_block_cfg = test_default_channel_config();
+ min_depth_1_block_cfg.channel_handshake_config.minimum_depth = 1;
+ let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(min_depth_1_block_cfg), Some(min_depth_1_block_cfg)]);
+ let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+ let funding_tx = create_chan_between_nodes_with_value_init(&nodes[0], &nodes[1], 1_000_000, 0);
+ let chan_id = chain::transaction::OutPoint { txid: funding_tx.txid(), index: 0 }.to_channel_id();
+
+ assert_eq!(nodes[0].node.list_channels().len(), 1);
+ assert_eq!(nodes[1].node.list_channels().len(), 1);
+
+ let (closing_node, other_node) = if confirm_remote_commitment {
+ (&nodes[1], &nodes[0])
+ } else {
+ (&nodes[0], &nodes[1])
+ };
+
+ closing_node.node.force_close_broadcasting_latest_txn(&chan_id, &other_node.node.get_our_node_id()).unwrap();
+ let mut msg_events = closing_node.node.get_and_clear_pending_msg_events();
+ assert_eq!(msg_events.len(), 1);
+ match msg_events.pop().unwrap() {
+ MessageSendEvent::HandleError { action: msgs::ErrorAction::SendErrorMessage { .. }, .. } => {},
+ _ => panic!("Unexpected event"),
+ }
+ check_added_monitors(closing_node, 1);
+ check_closed_event(closing_node, 1, ClosureReason::HolderForceClosed, false, &[other_node.node.get_our_node_id()], 1_000_000);
+
+ let commitment_tx = {
+ let mut txn = closing_node.tx_broadcaster.txn_broadcast();
+ assert_eq!(txn.len(), 1);
+ let commitment_tx = txn.pop().unwrap();
+ check_spends!(commitment_tx, funding_tx);
+ commitment_tx
+ };
+
+ mine_transactions(&nodes[0], &[&funding_tx, &commitment_tx]);
+ mine_transactions(&nodes[1], &[&funding_tx, &commitment_tx]);
+
+ check_closed_broadcast(other_node, 1, true);
+ check_added_monitors(other_node, 1);
+ check_closed_event(other_node, 1, ClosureReason::CommitmentTxConfirmed, false, &[closing_node.node.get_our_node_id()], 1_000_000);
+
+ assert!(nodes[0].node.list_channels().is_empty());
+ assert!(nodes[1].node.list_channels().is_empty());
+}
+
+#[test]
+fn test_funding_and_commitment_tx_confirm_same_block() {
+ do_test_funding_and_commitment_tx_confirm_same_block(false);
+ do_test_funding_and_commitment_tx_confirm_same_block(true);
+}
//! Further functional tests which test blockchain reorganizations.
-use crate::sign::EcdsaChannelSigner;
+use crate::sign::{EcdsaChannelSigner, SpendableOutputDescriptor};
use crate::chain::channelmonitor::{ANTI_REORG_DELAY, LATENCY_GRACE_PERIOD_BLOCKS, Balance};
use crate::chain::transaction::OutPoint;
use crate::chain::chaininterface::{LowerBoundedFeeEstimator, compute_feerate_sat_per_1000_weight};
use crate::util::config::UserConfig;
use crate::util::crypto::sign;
use crate::util::ser::Writeable;
+use crate::util::scid_utils::block_from_scid;
use crate::util::test_utils;
use bitcoin::blockdata::transaction::EcdsaSighashType;
expect_payment_failed_with_update!(nodes[0], payment_hash, false, update_a.contents.short_channel_id, true);
}
-fn test_spendable_output<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, spendable_tx: &Transaction) {
+fn test_spendable_output<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, spendable_tx: &Transaction) -> Vec<SpendableOutputDescriptor> {
let mut spendable = node.chain_monitor.chain_monitor.get_and_clear_pending_events();
assert_eq!(spendable.len(), 1);
if let Event::SpendableOutputs { outputs, .. } = spendable.pop().unwrap() {
let spend_tx = node.keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(),
Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, None, &Secp256k1::new()).unwrap();
check_spends!(spend_tx, spendable_tx);
+ outputs
} else { panic!(); }
}
assert_eq!(shutdown_tx, nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0));
assert_eq!(shutdown_tx.len(), 1);
- mine_transaction(&nodes[0], &shutdown_tx[0]);
- mine_transaction(&nodes[1], &shutdown_tx[0]);
+ let shutdown_tx_conf_height_a = block_from_scid(&mine_transaction(&nodes[0], &shutdown_tx[0]));
+ let shutdown_tx_conf_height_b = block_from_scid(&mine_transaction(&nodes[1], &shutdown_tx[0]));
assert!(nodes[0].node.list_channels().is_empty());
assert!(nodes[1].node.list_channels().is_empty());
}],
nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
- connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
- connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
+ connect_blocks(&nodes[0], ANTI_REORG_DELAY - 2);
+ connect_blocks(&nodes[1], ANTI_REORG_DELAY - 2);
+
+ assert!(get_monitor!(nodes[0], chan_id)
+ .get_spendable_outputs(&shutdown_tx[0], shutdown_tx_conf_height_a).is_empty());
+ assert!(get_monitor!(nodes[1], chan_id)
+ .get_spendable_outputs(&shutdown_tx[0], shutdown_tx_conf_height_b).is_empty());
+
+ connect_blocks(&nodes[0], 1);
+ connect_blocks(&nodes[1], 1);
assert_eq!(Vec::<Balance>::new(),
nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
assert_eq!(Vec::<Balance>::new(),
nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
- test_spendable_output(&nodes[0], &shutdown_tx[0]);
- test_spendable_output(&nodes[1], &shutdown_tx[0]);
+ let spendable_outputs_a = test_spendable_output(&nodes[0], &shutdown_tx[0]);
+ assert_eq!(
+ get_monitor!(nodes[0], chan_id).get_spendable_outputs(&shutdown_tx[0], shutdown_tx_conf_height_a),
+ spendable_outputs_a
+ );
+
+ let spendable_outputs_b = test_spendable_output(&nodes[1], &shutdown_tx[0]);
+ assert_eq!(
+ get_monitor!(nodes[1], chan_id).get_spendable_outputs(&shutdown_tx[0], shutdown_tx_conf_height_b),
+ spendable_outputs_b
+ );
check_closed_event!(nodes[0], 1, ClosureReason::CooperativeClosure, [nodes[1].node.get_our_node_id()], 1000000);
check_closed_event!(nodes[1], 1, ClosureReason::CooperativeClosure, [nodes[0].node.get_our_node_id()], 1000000);
// First confirm the commitment transaction on nodes[0], which should leave us with three
// claimable balances.
let node_a_commitment_claimable = nodes[0].best_block_info().1 + BREAKDOWN_TIMEOUT as u32;
- mine_transaction(&nodes[0], &as_txn[0]);
+ let commitment_tx_conf_height_a = block_from_scid(&mine_transaction(&nodes[0], &as_txn[0]));
check_added_monitors!(nodes[0], 1);
check_closed_broadcast!(nodes[0], true);
check_closed_event!(nodes[0], 1, ClosureReason::CommitmentTxConfirmed, [nodes[1].node.get_our_node_id()], 1000000);
// Connect blocks until the commitment transaction's CSV expires, providing us the relevant
// `SpendableOutputs` event and removing the claimable balance entry.
- connect_blocks(&nodes[0], node_a_commitment_claimable - nodes[0].best_block_info().1);
+ connect_blocks(&nodes[0], node_a_commitment_claimable - nodes[0].best_block_info().1 - 1);
+ assert!(get_monitor!(nodes[0], chan_id)
+ .get_spendable_outputs(&as_txn[0], commitment_tx_conf_height_a).is_empty());
+ connect_blocks(&nodes[0], 1);
assert_eq!(vec![Balance::ClaimableAwaitingConfirmations {
amount_satoshis: 10_000,
confirmation_height: node_a_htlc_claimable,
}],
nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
- test_spendable_output(&nodes[0], &as_txn[0]);
+ let to_self_spendable_output = test_spendable_output(&nodes[0], &as_txn[0]);
+ assert_eq!(
+ get_monitor!(nodes[0], chan_id).get_spendable_outputs(&as_txn[0], commitment_tx_conf_height_a),
+ to_self_spendable_output
+ );
// Connect blocks until the HTLC-Timeout's CSV expires, providing us the relevant
// `SpendableOutputs` event and removing the claimable balance entry.
assert!(nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events().is_empty());
let spendable_output_events = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events();
- assert_eq!(spendable_output_events.len(), 2);
- for event in spendable_output_events.iter() {
+ assert_eq!(spendable_output_events.len(), 4);
+ for event in spendable_output_events {
if let Event::SpendableOutputs { outputs, channel_id } = event {
assert_eq!(outputs.len(), 1);
assert!(vec![chan_b.2, chan_a.2].contains(&channel_id.unwrap()));
&[&outputs[0]], Vec::new(), Script::new_op_return(&[]), 253, None, &Secp256k1::new(),
).unwrap();
- check_spends!(spend_tx, revoked_claim_transactions.get(&spend_tx.input[0].previous_output.txid).unwrap());
+ if let SpendableOutputDescriptor::StaticPaymentOutput(_) = &outputs[0] {
+ check_spends!(spend_tx, &revoked_commitment_a, &revoked_commitment_b);
+ } else {
+ check_spends!(spend_tx, revoked_claim_transactions.get(&spend_tx.input[0].previous_output.txid).unwrap());
+ }
} else {
panic!("unexpected event");
}
// revoked commitment which Bob has the preimage for.
assert_eq!(nodes[1].chain_monitor.chain_monitor.get_claimable_balances(&[]).len(), 6);
}
+
+fn do_test_anchors_monitor_fixes_counterparty_payment_script_on_reload(confirm_commitment_before_reload: bool) {
+ // Tests that we'll fix a ChannelMonitor's `counterparty_payment_script` for an anchor outputs
+ // channel upon deserialization.
+ let chanmon_cfgs = create_chanmon_cfgs(2);
+ let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+ let persister;
+ let chain_monitor;
+ let mut user_config = test_default_channel_config();
+ user_config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
+ user_config.manually_accept_inbound_channels = true;
+ let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(user_config), Some(user_config)]);
+ let node_deserialized;
+ let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+ let (_, _, chan_id, funding_tx) = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100_000, 50_000_000);
+
+ // Set the monitor's `counterparty_payment_script` to a dummy P2WPKH script.
+ let secp = Secp256k1::new();
+ let privkey = bitcoin::PrivateKey::from_slice(&[1; 32], bitcoin::Network::Testnet).unwrap();
+ let pubkey = bitcoin::PublicKey::from_private_key(&secp, &privkey);
+ let p2wpkh_script = Script::new_v0_p2wpkh(&pubkey.wpubkey_hash().unwrap());
+ get_monitor!(nodes[1], chan_id).set_counterparty_payment_script(p2wpkh_script.clone());
+ assert_eq!(get_monitor!(nodes[1], chan_id).get_counterparty_payment_script(), p2wpkh_script);
+
+ // Confirm the counterparty's commitment and reload the monitor (either before or after) such
+ // that we arrive at the correct `counterparty_payment_script` after the reload.
+ nodes[0].node.force_close_broadcasting_latest_txn(&chan_id, &nodes[1].node.get_our_node_id()).unwrap();
+ check_added_monitors(&nodes[0], 1);
+ check_closed_broadcast(&nodes[0], 1, true);
+ check_closed_event!(&nodes[0], 1, ClosureReason::HolderForceClosed, false,
+ [nodes[1].node.get_our_node_id()], 100000);
+
+ let commitment_tx = {
+ let mut txn = nodes[0].tx_broadcaster.unique_txn_broadcast();
+ assert_eq!(txn.len(), 1);
+ assert_eq!(txn[0].output.len(), 4);
+ check_spends!(txn[0], funding_tx);
+ txn.pop().unwrap()
+ };
+
+ mine_transaction(&nodes[0], &commitment_tx);
+ let commitment_tx_conf_height = if confirm_commitment_before_reload {
+ // We should expect our round trip serialization check to fail as we're writing the monitor
+ // with the incorrect P2WPKH script but reading it with the correct P2WSH script.
+ *nodes[1].chain_monitor.expect_monitor_round_trip_fail.lock().unwrap() = Some(chan_id);
+ let commitment_tx_conf_height = block_from_scid(&mine_transaction(&nodes[1], &commitment_tx));
+ let serialized_monitor = get_monitor!(nodes[1], chan_id).encode();
+ reload_node!(nodes[1], user_config, &nodes[1].node.encode(), &[&serialized_monitor], persister, chain_monitor, node_deserialized);
+ commitment_tx_conf_height
+ } else {
+ let serialized_monitor = get_monitor!(nodes[1], chan_id).encode();
+ reload_node!(nodes[1], user_config, &nodes[1].node.encode(), &[&serialized_monitor], persister, chain_monitor, node_deserialized);
+ let commitment_tx_conf_height = block_from_scid(&mine_transaction(&nodes[1], &commitment_tx));
+ check_added_monitors(&nodes[1], 1);
+ check_closed_broadcast(&nodes[1], 1, true);
+ commitment_tx_conf_height
+ };
+ check_closed_event!(&nodes[1], 1, ClosureReason::CommitmentTxConfirmed, false,
+ [nodes[0].node.get_our_node_id()], 100000);
+ assert!(get_monitor!(nodes[1], chan_id).get_counterparty_payment_script().is_v0_p2wsh());
+
+ connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
+ connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
+
+ if confirm_commitment_before_reload {
+ // If we saw the commitment before our `counterparty_payment_script` was fixed, we'll never
+ // get the spendable output event for the `to_remote` output, so we'll need to get it
+ // manually via `get_spendable_outputs`.
+ check_added_monitors(&nodes[1], 1);
+ let outputs = get_monitor!(nodes[1], chan_id).get_spendable_outputs(&commitment_tx, commitment_tx_conf_height);
+ assert_eq!(outputs.len(), 1);
+ let spend_tx = nodes[1].keys_manager.backing.spend_spendable_outputs(
+ &[&outputs[0]], Vec::new(), Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(),
+ 253, None, &secp
+ ).unwrap();
+ check_spends!(spend_tx, &commitment_tx);
+ } else {
+ test_spendable_output(&nodes[1], &commitment_tx);
+ }
+}
+
+#[test]
+fn test_anchors_monitor_fixes_counterparty_payment_script_on_reload() {
+ do_test_anchors_monitor_fixes_counterparty_payment_script_on_reload(false);
+ do_test_anchors_monitor_fixes_counterparty_payment_script_on_reload(true);
+}
&self, pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>)
{
let mut pending_outbound_payments = self.pending_outbound_payments.lock().unwrap();
+ #[cfg(not(invreqfailed))]
+ let pending_events = pending_events.lock().unwrap();
+ #[cfg(invreqfailed)]
let mut pending_events = pending_events.lock().unwrap();
pending_outbound_payments.retain(|payment_id, payment| {
// If an outbound payment was completed, and no pending HTLCs remain, we should remove it
if *timer_ticks_without_response <= INVOICE_REQUEST_TIMEOUT_TICKS {
true
} else {
+ #[cfg(invreqfailed)]
pending_events.push_back(
(events::Event::InvoiceRequestFailed { payment_id: *payment_id }, None)
);
payment.remove();
}
} else if let PendingOutboundPayment::AwaitingInvoice { .. } = payment.get() {
+ #[cfg(invreqfailed)]
pending_events.lock().unwrap().push_back((events::Event::InvoiceRequestFailed {
payment_id,
}, None));
use crate::ln::channelmanager::{PaymentId, RecipientOnionFields};
use crate::ln::features::{ChannelFeatures, NodeFeatures};
use crate::ln::msgs::{ErrorAction, LightningError};
- use crate::ln::outbound_payment::{Bolt12PaymentError, INVOICE_REQUEST_TIMEOUT_TICKS, OutboundPayments, Retry, RetryableSendFailure};
+ use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, Retry, RetryableSendFailure};
+ #[cfg(invreqfailed)]
+ use crate::ln::outbound_payment::INVOICE_REQUEST_TIMEOUT_TICKS;
use crate::offers::invoice::DEFAULT_RELATIVE_EXPIRY;
use crate::offers::offer::OfferBuilder;
use crate::offers::test_utils::*;
}
#[test]
+ #[cfg(invreqfailed)]
fn removes_stale_awaiting_invoice() {
let pending_events = Mutex::new(VecDeque::new());
let outbound_payments = OutboundPayments::new();
}
#[test]
+ #[cfg(invreqfailed)]
fn removes_abandoned_awaiting_invoice() {
let pending_events = Mutex::new(VecDeque::new());
let outbound_payments = OutboundPayments::new();
use crate::util::config::UserConfig;
use crate::util::string::UntrustedString;
-use bitcoin::{PackedLockTime, Transaction, TxOut};
use bitcoin::hash_types::BlockHash;
use crate::prelude::*;
//! Further functional tests which test blockchain reorganizations.
+use crate::chain::chaininterface::LowerBoundedFeeEstimator;
use crate::chain::channelmonitor::{ANTI_REORG_DELAY, LATENCY_GRACE_PERIOD_BLOCKS};
use crate::chain::transaction::OutPoint;
use crate::chain::Confirm;
-use crate::events::{Event, MessageSendEventsProvider, ClosureReason, HTLCDestination};
+use crate::events::{Event, MessageSendEventsProvider, ClosureReason, HTLCDestination, MessageSendEvent};
use crate::ln::msgs::{ChannelMessageHandler, Init};
use crate::util::test_utils;
use crate::util::ser::Writeable;
do_test_to_remote_after_local_detection(ConnectStyle::TransactionsFirstReorgsOnlyTip);
do_test_to_remote_after_local_detection(ConnectStyle::FullBlockViaListen);
}
+
+#[test]
+fn test_htlc_preimage_claim_holder_commitment_after_counterparty_commitment_reorg() {
+ // We detect a counterparty commitment confirm onchain, followed by a reorg and a confirmation
+ // of a holder commitment. Then, if we learn of the preimage for an HTLC in both commitments,
+ // test that we only claim the currently confirmed commitment.
+ let chanmon_cfgs = create_chanmon_cfgs(2);
+ let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None, None]);
+ let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+ let (_, _, chan_id, funding_tx) = create_announced_chan_between_nodes(&nodes, 0, 1);
+
+ // Route an HTLC which we will claim onchain with the preimage.
+ let (payment_preimage, payment_hash, ..) = route_payment(&nodes[0], &[&nodes[1]], 1_000_000);
+
+ // Force close with the latest counterparty commitment, confirm it, and reorg it with the latest
+ // holder commitment.
+ nodes[0].node.force_close_broadcasting_latest_txn(&chan_id, &nodes[1].node.get_our_node_id()).unwrap();
+ check_closed_broadcast(&nodes[0], 1, true);
+ check_added_monitors(&nodes[0], 1);
+ check_closed_event(&nodes[0], 1, ClosureReason::HolderForceClosed, false, &[nodes[1].node.get_our_node_id()], 100000);
+
+ nodes[1].node.force_close_broadcasting_latest_txn(&chan_id, &nodes[0].node.get_our_node_id()).unwrap();
+ check_closed_broadcast(&nodes[1], 1, true);
+ check_added_monitors(&nodes[1], 1);
+ check_closed_event(&nodes[1], 1, ClosureReason::HolderForceClosed, false, &[nodes[0].node.get_our_node_id()], 100000);
+
+ let mut txn = nodes[0].tx_broadcaster.txn_broadcast();
+ assert_eq!(txn.len(), 1);
+ let commitment_tx_a = txn.pop().unwrap();
+ check_spends!(commitment_tx_a, funding_tx);
+
+ let mut txn = nodes[1].tx_broadcaster.txn_broadcast();
+ assert_eq!(txn.len(), 1);
+ let commitment_tx_b = txn.pop().unwrap();
+ check_spends!(commitment_tx_b, funding_tx);
+
+ mine_transaction(&nodes[0], &commitment_tx_a);
+ mine_transaction(&nodes[1], &commitment_tx_a);
+
+ disconnect_blocks(&nodes[0], 1);
+ disconnect_blocks(&nodes[1], 1);
+
+ mine_transaction(&nodes[0], &commitment_tx_b);
+ mine_transaction(&nodes[1], &commitment_tx_b);
+
+ // Provide the preimage now, such that we only claim from the holder commitment (since it's
+ // currently confirmed) and not the counterparty's.
+ get_monitor!(nodes[1], chan_id).provide_payment_preimage(
+ &payment_hash, &payment_preimage, &nodes[1].tx_broadcaster,
+ &LowerBoundedFeeEstimator(nodes[1].fee_estimator), &nodes[1].logger
+ );
+
+ let mut txn = nodes[1].tx_broadcaster.txn_broadcast();
+ assert_eq!(txn.len(), 1);
+ let htlc_success_tx = txn.pop().unwrap();
+ check_spends!(htlc_success_tx, commitment_tx_b);
+}
+
+#[test]
+fn test_htlc_preimage_claim_prev_counterparty_commitment_after_current_counterparty_commitment_reorg() {
+ // We detect a counterparty commitment confirm onchain, followed by a reorg and a
+ // confirmation of the previous (still unrevoked) counterparty commitment. Then, if we learn
+ // of the preimage for an HTLC in both commitments, test that we only claim the currently
+ // confirmed commitment.
+ let chanmon_cfgs = create_chanmon_cfgs(2);
+ let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None, None]);
+ let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+ let (_, _, chan_id, funding_tx) = create_announced_chan_between_nodes(&nodes, 0, 1);
+
+ // Route an HTLC which we will claim onchain with the preimage.
+ let (payment_preimage, payment_hash, ..) = route_payment(&nodes[0], &[&nodes[1]], 1_000_000);
+
+ // Obtain the current commitment, which will become the previous after a fee update.
+ let prev_commitment_a = &get_local_commitment_txn!(nodes[0], chan_id)[0];
+
+ *nodes[0].fee_estimator.sat_per_kw.lock().unwrap() *= 4;
+ nodes[0].node.timer_tick_occurred();
+ check_added_monitors(&nodes[0], 1);
+ let mut msg_events = nodes[0].node.get_and_clear_pending_msg_events();
+ assert_eq!(msg_events.len(), 1);
+ let (update_fee, commit_sig) = if let MessageSendEvent::UpdateHTLCs { node_id, mut updates } = msg_events.pop().unwrap() {
+ assert_eq!(node_id, nodes[1].node.get_our_node_id());
+ (updates.update_fee.take().unwrap(), updates.commitment_signed)
+ } else {
+ panic!("Unexpected message send event");
+ };
+
+ // Handle the fee update on the other side, but don't send the last RAA such that the previous
+ // commitment is still valid (unrevoked).
+ nodes[1].node().handle_update_fee(&nodes[0].node.get_our_node_id(), &update_fee);
+ let _last_revoke_and_ack = commitment_signed_dance!(nodes[1], nodes[0], commit_sig, false, true, false, true);
+
+ // Force close with the latest commitment, confirm it, and reorg it with the previous commitment.
+ nodes[0].node.force_close_broadcasting_latest_txn(&chan_id, &nodes[1].node.get_our_node_id()).unwrap();
+ check_closed_broadcast(&nodes[0], 1, true);
+ check_added_monitors(&nodes[0], 1);
+ check_closed_event(&nodes[0], 1, ClosureReason::HolderForceClosed, false, &[nodes[1].node.get_our_node_id()], 100000);
+
+ let mut txn = nodes[0].tx_broadcaster.txn_broadcast();
+ assert_eq!(txn.len(), 1);
+ let current_commitment_a = txn.pop().unwrap();
+ assert_ne!(current_commitment_a.txid(), prev_commitment_a.txid());
+ check_spends!(current_commitment_a, funding_tx);
+
+ mine_transaction(&nodes[0], ¤t_commitment_a);
+ mine_transaction(&nodes[1], ¤t_commitment_a);
+
+ check_closed_broadcast(&nodes[1], 1, true);
+ check_added_monitors(&nodes[1], 1);
+ check_closed_event(&nodes[1], 1, ClosureReason::CommitmentTxConfirmed, false, &[nodes[0].node.get_our_node_id()], 100000);
+
+ disconnect_blocks(&nodes[0], 1);
+ disconnect_blocks(&nodes[1], 1);
+
+ mine_transaction(&nodes[0], &prev_commitment_a);
+ mine_transaction(&nodes[1], &prev_commitment_a);
+
+ // Provide the preimage now, such that we only claim from the previous commitment (since it's
+ // currently confirmed) and not the latest.
+ get_monitor!(nodes[1], chan_id).provide_payment_preimage(
+ &payment_hash, &payment_preimage, &nodes[1].tx_broadcaster,
+ &LowerBoundedFeeEstimator(nodes[1].fee_estimator), &nodes[1].logger
+ );
+
+ let mut txn = nodes[1].tx_broadcaster.txn_broadcast();
+ assert_eq!(txn.len(), 1);
+ let htlc_preimage_tx = txn.pop().unwrap();
+ check_spends!(htlc_preimage_tx, prev_commitment_a);
+ // Make sure it was indeed a preimage claim and not a revocation claim since the previous
+ // commitment (still unrevoked) is the currently confirmed closing transaction.
+ assert_eq!(htlc_preimage_tx.input[0].witness.second_to_last().unwrap(), &payment_preimage.0[..]);
+}
nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 1_000_000, 100_000, 0, None).unwrap();
let open_chan = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
- // P2WSH
+ // Create a dummy P2WPKH script
let script = Builder::new().push_int(0)
.push_slice(&[0; 20])
.into_script();
inbound_scid_alias: None,
channel_value_satoshis: 0,
user_channel_id: 0,
+ balance_msat: 0,
outbound_capacity_msat,
next_outbound_htlc_limit_msat: outbound_capacity_msat,
next_outbound_htlc_minimum_msat: 0,
outbound_scid_alias: None,
channel_value_satoshis: 10_000_000_000,
user_channel_id: 0,
+ balance_msat: 10_000_000_000,
outbound_capacity_msat: 10_000_000_000,
next_outbound_htlc_minimum_msat: 0,
next_outbound_htlc_limit_msat: 10_000_000_000,
(12, channel_value_satoshis, required),
});
+pub(crate) const P2WPKH_WITNESS_WEIGHT: u64 = 1 /* num stack items */ +
+ 1 /* sig length */ +
+ 73 /* sig including sighash flag */ +
+ 1 /* pubkey length */ +
+ 33 /* pubkey */;
+
/// Information about a spendable output to our "payment key".
///
/// See [`SpendableOutputDescriptor::StaticPaymentOutput`] for more details on how to spend this.
pub channel_keys_id: [u8; 32],
/// The value of the channel which this transactions spends.
pub channel_value_satoshis: u64,
+ /// The necessary channel parameters that need to be provided to the re-derived signer through
+ /// [`ChannelSigner::provide_channel_parameters`].
+ ///
+ /// Added as optional, but always `Some` if the descriptor was produced in v0.0.117 or later.
+ pub channel_transaction_parameters: Option<ChannelTransactionParameters>,
}
impl StaticPaymentOutputDescriptor {
+ /// Returns the `witness_script` of the spendable output.
+ ///
+ /// Note that this will only return `Some` for [`StaticPaymentOutputDescriptor`]s that
+ /// originated from an anchor outputs channel, as they take the form of a P2WSH script.
+ pub fn witness_script(&self) -> Option<Script> {
+ self.channel_transaction_parameters.as_ref()
+ .and_then(|channel_params|
+ if channel_params.channel_type_features.supports_anchors_zero_fee_htlc_tx() {
+ let payment_point = channel_params.holder_pubkeys.payment_point;
+ Some(chan_utils::get_to_countersignatory_with_anchors_redeemscript(&payment_point))
+ } else {
+ None
+ }
+ )
+ }
+
/// The maximum length a well-formed witness spending one of these should have.
/// Note: If you have the grind_signatures feature enabled, this will be at least 1 byte
/// shorter.
- // Calculated as 1 byte legnth + 73 byte signature, 1 byte empty vec push, 1 byte length plus
- // redeemscript push length.
- pub const MAX_WITNESS_LENGTH: usize = 1 + 73 + 34;
+ pub fn max_witness_length(&self) -> usize {
+ if self.channel_transaction_parameters.as_ref()
+ .map(|channel_params| channel_params.channel_type_features.supports_anchors_zero_fee_htlc_tx())
+ .unwrap_or(false)
+ {
+ let witness_script_weight = 1 /* pubkey push */ + 33 /* pubkey */ +
+ 1 /* OP_CHECKSIGVERIFY */ + 1 /* OP_1 */ + 1 /* OP_CHECKSEQUENCEVERIFY */;
+ 1 /* num witness items */ + 1 /* sig push */ + 73 /* sig including sighash flag */ +
+ 1 /* witness script push */ + witness_script_weight
+ } else {
+ P2WPKH_WITNESS_WEIGHT as usize
+ }
+ }
}
impl_writeable_tlv_based!(StaticPaymentOutputDescriptor, {
(0, outpoint, required),
(2, output, required),
(4, channel_keys_id, required),
(6, channel_value_satoshis, required),
+ (7, channel_transaction_parameters, option),
});
/// Describes the necessary information to spend a spendable output.
/// [`DelayedPaymentOutputDescriptor::to_self_delay`] contained here to
/// [`chan_utils::get_revokeable_redeemscript`].
DelayedPaymentOutput(DelayedPaymentOutputDescriptor),
- /// An output to a P2WPKH, spendable exclusively by our payment key (i.e., the private key
- /// which corresponds to the `payment_point` in [`ChannelSigner::pubkeys`]). The witness
- /// in the spending input is, thus, simply:
+ /// An output spendable exclusively by our payment key (i.e., the private key that corresponds
+ /// to the `payment_point` in [`ChannelSigner::pubkeys`]). The output type depends on the
+ /// channel type negotiated.
+ ///
+ /// On an anchor outputs channel, the witness in the spending input is:
+ /// ```bitcoin
+ /// <BIP 143 signature> <witness script>
+ /// ```
+ ///
+ /// Otherwise, it is:
/// ```bitcoin
/// <BIP 143 signature> <payment key>
/// ```
///
/// These are generally the result of our counterparty having broadcast the current state,
- /// allowing us to claim the non-HTLC-encumbered outputs immediately.
+ /// allowing us to claim the non-HTLC-encumbered outputs immediately, or after one confirmation
+ /// in the case of anchor outputs channels.
StaticPaymentOutput(StaticPaymentOutputDescriptor),
}
match outp {
SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => {
if !output_set.insert(descriptor.outpoint) { return Err(()); }
+ let sequence =
+ if descriptor.channel_transaction_parameters.as_ref()
+ .map(|channel_params| channel_params.channel_type_features.supports_anchors_zero_fee_htlc_tx())
+ .unwrap_or(false)
+ {
+ Sequence::from_consensus(1)
+ } else {
+ Sequence::ZERO
+ };
input.push(TxIn {
previous_output: descriptor.outpoint.into_bitcoin_outpoint(),
script_sig: Script::new(),
- sequence: Sequence::ZERO,
+ sequence,
witness: Witness::new(),
});
- witness_weight += StaticPaymentOutputDescriptor::MAX_WITNESS_LENGTH;
+ witness_weight += descriptor.max_witness_length();
#[cfg(feature = "grind_signatures")]
{ witness_weight -= 1; } // Guarantees a low R signature
input_value += descriptor.output.value;
if !spend_tx.input[input_idx].script_sig.is_empty() { return Err(()); }
if spend_tx.input[input_idx].previous_output != descriptor.outpoint.into_bitcoin_outpoint() { return Err(()); }
- let remotepubkey = self.pubkeys().payment_point;
- let witness_script = bitcoin::Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: remotepubkey}, Network::Testnet).script_pubkey();
+ let remotepubkey = bitcoin::PublicKey::new(self.pubkeys().payment_point);
+ let witness_script = if self.channel_type_features().supports_anchors_zero_fee_htlc_tx() {
+ chan_utils::get_to_countersignatory_with_anchors_redeemscript(&remotepubkey.inner)
+ } else {
+ Script::new_p2pkh(&remotepubkey.pubkey_hash())
+ };
let sighash = hash_to_message!(&sighash::SighashCache::new(spend_tx).segwit_signature_hash(input_idx, &witness_script, descriptor.output.value, EcdsaSighashType::All).unwrap()[..]);
let remotesig = sign_with_aux_rand(secp_ctx, &sighash, &self.payment_key, &self);
- let payment_script = bitcoin::Address::p2wpkh(&::bitcoin::PublicKey{compressed: true, inner: remotepubkey}, Network::Bitcoin).unwrap().script_pubkey();
+ let payment_script = if self.channel_type_features().supports_anchors_zero_fee_htlc_tx() {
+ witness_script.to_v0_p2wsh()
+ } else {
+ Script::new_v0_p2wpkh(&remotepubkey.wpubkey_hash().unwrap())
+ };
if payment_script != descriptor.output.script_pubkey { return Err(()); }
let mut witness = Vec::with_capacity(2);
witness.push(remotesig.serialize_der().to_vec());
witness[0].push(EcdsaSighashType::All as u8);
- witness.push(remotepubkey.serialize().to_vec());
+ if self.channel_type_features().supports_anchors_zero_fee_htlc_tx() {
+ witness.push(witness_script.to_bytes());
+ } else {
+ witness.push(remotepubkey.to_bytes());
+ }
Ok(witness)
}
SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => {
let input_idx = psbt.unsigned_tx.input.iter().position(|i| i.previous_output == descriptor.outpoint.into_bitcoin_outpoint()).ok_or(())?;
if keys_cache.is_none() || keys_cache.as_ref().unwrap().1 != descriptor.channel_keys_id {
- keys_cache = Some((
- self.derive_channel_keys(descriptor.channel_value_satoshis, &descriptor.channel_keys_id),
- descriptor.channel_keys_id));
+ let mut signer = self.derive_channel_keys(descriptor.channel_value_satoshis, &descriptor.channel_keys_id);
+ if let Some(channel_params) = descriptor.channel_transaction_parameters.as_ref() {
+ signer.provide_channel_parameters(channel_params);
+ }
+ keys_cache = Some((signer, descriptor.channel_keys_id));
}
let witness = Witness::from_vec(keys_cache.as_ref().unwrap().0.sign_counterparty_payment_input(&psbt.unsigned_tx, input_idx, &descriptor, &secp_ctx)?);
psbt.inputs[input_idx].final_script_witness = Some(witness);
}
}
+ #[allow(unused)]
pub(crate) fn as_mut_ecdsa(&mut self) -> Option<&mut ECS> {
match self {
ChannelSignerType::Ecdsa(ecs) => Some(ecs)
/// ChannelForceClosed event for the given channel_id with should_broadcast set to the given
/// boolean.
pub expect_channel_force_closed: Mutex<Option<(ChannelId, bool)>>,
+ /// If this is set to Some(), the next round trip serialization check will not hold after an
+ /// update_channel call (not watch_channel) for the given channel_id.
+ pub expect_monitor_round_trip_fail: Mutex<Option<ChannelId>>,
}
impl<'a> TestChainMonitor<'a> {
pub fn new(chain_source: Option<&'a TestChainSource>, broadcaster: &'a chaininterface::BroadcasterInterface, logger: &'a TestLogger, fee_estimator: &'a TestFeeEstimator, persister: &'a chainmonitor::Persist<TestChannelSigner>, keys_manager: &'a TestKeysInterface) -> Self {
chain_monitor: chainmonitor::ChainMonitor::new(chain_source, broadcaster, logger, fee_estimator, persister),
keys_manager,
expect_channel_force_closed: Mutex::new(None),
+ expect_monitor_round_trip_fail: Mutex::new(None),
}
}
monitor.write(&mut w).unwrap();
let new_monitor = <(BlockHash, channelmonitor::ChannelMonitor<TestChannelSigner>)>::read(
&mut io::Cursor::new(&w.0), (self.keys_manager, self.keys_manager)).unwrap().1;
- assert!(new_monitor == *monitor);
+ if let Some(chan_id) = self.expect_monitor_round_trip_fail.lock().unwrap().take() {
+ assert_eq!(chan_id, funding_txo.to_channel_id());
+ assert!(new_monitor != *monitor);
+ } else {
+ assert!(new_monitor == *monitor);
+ }
self.added_monitors.lock().unwrap().push((funding_txo, new_monitor));
update_res
}
+++ /dev/null
-* The `AvailableBalances::balance_msat` field has been removed in favor of `ChannelMonitor::get_claimable_balances`. `ChannelDetails` serialized with versions of LDK >= 0.0.117 will have their `balance_msat` field set to `next_outbound_htlc_limit_msat` when read by versions of LDK prior to 0.0.117 (#2476).