Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions fuzz/src/chanmon_consistency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ impl TestChainMonitor {
Arc::clone(&persister),
Arc::clone(&keys),
keys.get_peer_storage_key(),
channelmonitor::CLTV_CLAIM_BUFFER,
)),
logger,
keys,
Expand Down
2 changes: 2 additions & 0 deletions fuzz/src/full_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use lightning::chain::chaininterface::{
BroadcasterInterface, ConfirmationTarget, FeeEstimator, TransactionType,
};
use lightning::chain::chainmonitor;
use lightning::chain::channelmonitor;
use lightning::chain::transaction::OutPoint;
use lightning::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen};
use lightning::events::Event;
Expand Down Expand Up @@ -603,6 +604,7 @@ pub fn do_test(mut data: &[u8], logger: &Arc<dyn Logger + MaybeSend + MaybeSync>
Arc::new(TestPersister { update_ret: Mutex::new(ChannelMonitorUpdateStatus::Completed) }),
Arc::clone(&keys_manager),
keys_manager.get_peer_storage_key(),
channelmonitor::CLTV_CLAIM_BUFFER,
));

let network = Network::Bitcoin;
Expand Down
3 changes: 2 additions & 1 deletion fuzz/src/lsps_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
use bitcoin::Network;

use lightning::chain::Filter;
use lightning::chain::{chainmonitor, BestBlock};
use lightning::chain::{chainmonitor, channelmonitor, BestBlock};
use lightning::ln::channelmanager::{ChainParameters, ChannelManager};
use lightning::ln::peer_handler::CustomMessageHandler;
use lightning::ln::wire::CustomMessageReader;
Expand Down Expand Up @@ -59,6 +59,7 @@ pub fn do_test(data: &[u8]) {
Arc::clone(&kv_store),
Arc::clone(&keys_manager),
keys_manager.get_peer_storage_key(),
channelmonitor::CLTV_CLAIM_BUFFER,
));
let best_block = BestBlock::from_network(network);
let params = ChainParameters { network, best_block };
Expand Down
3 changes: 2 additions & 1 deletion lightning-background-processor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1896,7 +1896,7 @@ mod tests {
use bitcoin::transaction::{Transaction, TxOut};
use bitcoin::{Amount, ScriptBuf, Txid};
use core::sync::atomic::{AtomicBool, Ordering};
use lightning::chain::channelmonitor::ANTI_REORG_DELAY;
use lightning::chain::channelmonitor::{self as channelmonitor, ANTI_REORG_DELAY};
use lightning::chain::transaction::OutPoint;
use lightning::chain::{chainmonitor, BestBlock, Confirm, Filter};
use lightning::events::{Event, PathFailure, ReplayEvent};
Expand Down Expand Up @@ -2444,6 +2444,7 @@ mod tests {
Arc::clone(&kv_store),
Arc::clone(&keys_manager),
keys_manager.get_peer_storage_key(),
channelmonitor::CLTV_CLAIM_BUFFER,
));
let best_block = BestBlock::from_network(network);
let params = ChainParameters { network, best_block };
Expand Down
35 changes: 34 additions & 1 deletion lightning/src/chain/chainmonitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use crate::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
#[cfg(peer_storage)]
use crate::chain::channelmonitor::write_chanmon_internal;
use crate::chain::channelmonitor::{
Balance, ChannelMonitor, ChannelMonitorUpdate, MonitorEvent, TransactionOutputs,
self, Balance, ChannelMonitor, ChannelMonitorUpdate, MonitorEvent, TransactionOutputs,
WithChannelMonitor,
};
use crate::chain::transaction::{OutPoint, TransactionData};
Expand Down Expand Up @@ -350,6 +350,17 @@ pub struct ChainMonitor<
P::Target: Persist<ChannelSigner>,
{
monitors: RwLock<HashMap<ChannelId, MonitorHolder<ChannelSigner>>>,

/// The number of blocks before an inbound HTLC's CLTV expiry at which we will force-close
/// the channel to claim it on-chain. This is a global, memory-only setting configured at
/// construction time.
///
/// Must be between [`CLTV_CLAIM_BUFFER`] and [`HTLC_FAIL_BACK_BUFFER`] inclusive.
///
/// [`CLTV_CLAIM_BUFFER`]: channelmonitor::CLTV_CLAIM_BUFFER
/// [`HTLC_FAIL_BACK_BUFFER`]: channelmonitor::HTLC_FAIL_BACK_BUFFER
force_close_claimable_htlc_cltv_buffer: u32,

chain_source: Option<C>,
broadcaster: T,
logger: L,
Expand Down Expand Up @@ -398,10 +409,19 @@ where
chain_source: Option<C>, broadcaster: T, logger: L, feeest: F,
persister: MonitorUpdatingPersisterAsync<K, S, L, ES, SP, T, F>, _entropy_source: ES,
_our_peerstorage_encryption_key: PeerStorageKey,
force_close_claimable_htlc_cltv_buffer: u32,
) -> Self {
assert!(
force_close_claimable_htlc_cltv_buffer >= channelmonitor::CLTV_CLAIM_BUFFER
&& force_close_claimable_htlc_cltv_buffer <= channelmonitor::HTLC_FAIL_BACK_BUFFER,
"force_close_claimable_htlc_cltv_buffer must be between {} and {} inclusive",
channelmonitor::CLTV_CLAIM_BUFFER,
channelmonitor::HTLC_FAIL_BACK_BUFFER,
);
let event_notifier = Arc::new(Notifier::new());
Self {
monitors: RwLock::new(new_hash_map()),
force_close_claimable_htlc_cltv_buffer,
chain_source,
broadcaster,
logger,
Expand Down Expand Up @@ -604,9 +624,18 @@ where
pub fn new(
chain_source: Option<C>, broadcaster: T, logger: L, feeest: F, persister: P,
_entropy_source: ES, _our_peerstorage_encryption_key: PeerStorageKey,
force_close_claimable_htlc_cltv_buffer: u32,
) -> Self {
assert!(
force_close_claimable_htlc_cltv_buffer >= channelmonitor::CLTV_CLAIM_BUFFER
&& force_close_claimable_htlc_cltv_buffer <= channelmonitor::HTLC_FAIL_BACK_BUFFER,
"force_close_claimable_htlc_cltv_buffer must be between {} and {} inclusive",
channelmonitor::CLTV_CLAIM_BUFFER,
channelmonitor::HTLC_FAIL_BACK_BUFFER,
);
Self {
monitors: RwLock::new(new_hash_map()),
force_close_claimable_htlc_cltv_buffer,
chain_source,
broadcaster,
logger,
Expand Down Expand Up @@ -1132,6 +1161,7 @@ where
header,
txdata,
height,
self.force_close_claimable_htlc_cltv_buffer,
&self.broadcaster,
&self.fee_estimator,
&self.logger,
Expand Down Expand Up @@ -1192,6 +1222,7 @@ where
header,
txdata,
height,
self.force_close_claimable_htlc_cltv_buffer,
&self.broadcaster,
&self.fee_estimator,
&self.logger,
Expand Down Expand Up @@ -1228,6 +1259,7 @@ where
monitor.best_block_updated(
header,
height,
self.force_close_claimable_htlc_cltv_buffer,
&self.broadcaster,
&self.fee_estimator,
&self.logger,
Expand Down Expand Up @@ -1306,6 +1338,7 @@ where
monitor,
pending_monitor_updates: Mutex::new(pending_monitor_updates),
});

Ok(persist_res)
}

Expand Down
52 changes: 37 additions & 15 deletions lightning/src/chain/channelmonitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,9 @@ pub(crate) const MAX_BLOCKS_FOR_CONF: u32 = 18;
/// If an HTLC expires within this many blocks, force-close the channel to broadcast the
/// HTLC-Success transaction.
///
/// This is two times [`MAX_BLOCKS_FOR_CONF`] as we need to first get the commitment transaction
/// This is two times `MAX_BLOCKS_FOR_CONF` as we need to first get the commitment transaction
/// confirmed, then get an HTLC transaction confirmed.
pub(crate) const CLTV_CLAIM_BUFFER: u32 = MAX_BLOCKS_FOR_CONF * 2;
pub const CLTV_CLAIM_BUFFER: u32 = MAX_BLOCKS_FOR_CONF * 2;
/// Number of blocks by which point we expect our counterparty to have seen new blocks on the
/// network and done a full update_fail_htlc/commitment_signed dance (+ we've updated all our
/// copies of ChannelMonitors, including watchtowers). We could enforce the contract by failing
Expand Down Expand Up @@ -2365,14 +2365,15 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
header: &Header,
txdata: &TransactionData,
height: u32,
force_close_buffer: u32,
broadcaster: B,
fee_estimator: F,
logger: &L,
) -> Vec<TransactionOutputs> {
let mut inner = self.inner.lock().unwrap();
let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
inner.block_connected(
header, txdata, height, broadcaster, fee_estimator, &logger)
header, txdata, height, force_close_buffer, broadcaster, fee_estimator, &logger)
}

/// Determines if the disconnected block contained any transactions of interest and updates
Expand All @@ -2398,6 +2399,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
header: &Header,
txdata: &TransactionData,
height: u32,
force_close_buffer: u32,
broadcaster: B,
fee_estimator: F,
logger: &L,
Expand All @@ -2406,7 +2408,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
let mut inner = self.inner.lock().unwrap();
let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
inner.transactions_confirmed(
header, txdata, height, broadcaster, &bounded_fee_estimator, &logger)
header, txdata, height, force_close_buffer, broadcaster, &bounded_fee_estimator, &logger)
}

/// Processes a transaction that was reorganized out of the chain.
Expand Down Expand Up @@ -2443,6 +2445,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
&self,
header: &Header,
height: u32,
force_close_buffer: u32,
broadcaster: B,
fee_estimator: F,
logger: &L,
Expand All @@ -2451,7 +2454,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
let mut inner = self.inner.lock().unwrap();
let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
inner.best_block_updated(
header, height, broadcaster, &bounded_fee_estimator, &logger
header, height, force_close_buffer, broadcaster, &bounded_fee_estimator, &logger
)
}

Expand Down Expand Up @@ -5214,21 +5217,22 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {

#[rustfmt::skip]
fn block_connected<B: BroadcasterInterface, F: FeeEstimator, L: Logger>(
&mut self, header: &Header, txdata: &TransactionData, height: u32, broadcaster: B,
&mut self, header: &Header, txdata: &TransactionData, height: u32, force_close_buffer: u32, broadcaster: B,
fee_estimator: F, logger: &WithContext<L>,
) -> Vec<TransactionOutputs> {
let block_hash = header.block_hash();
self.best_block = BestBlock::new(block_hash, height);

let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
self.transactions_confirmed(header, txdata, height, broadcaster, &bounded_fee_estimator, logger)
self.transactions_confirmed(header, txdata, height, force_close_buffer, broadcaster, &bounded_fee_estimator, logger)
}

#[rustfmt::skip]
fn best_block_updated<B: BroadcasterInterface, F: FeeEstimator, L: Logger>(
&mut self,
header: &Header,
height: u32,
force_close_buffer: u32,
broadcaster: B,
fee_estimator: &LowerBoundedFeeEstimator<F>,
logger: &WithContext<L>,
Expand All @@ -5238,7 +5242,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
if height > self.best_block.height {
self.best_block = BestBlock::new(block_hash, height);
log_trace!(logger, "Connecting new block {} at height {}", block_hash, height);
self.block_confirmed(height, block_hash, vec![], vec![], vec![], &broadcaster, &fee_estimator, logger)
self.block_confirmed(height, block_hash, vec![], vec![], vec![], force_close_buffer, &broadcaster, &fee_estimator, logger)
} else if block_hash != self.best_block.block_hash {
self.best_block = BestBlock::new(block_hash, height);
log_trace!(logger, "Best block re-orged, replaced with new block {} at height {}", block_hash, height);
Expand All @@ -5257,6 +5261,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
header: &Header,
txdata: &TransactionData,
height: u32,
force_close_buffer: u32,
broadcaster: B,
fee_estimator: &LowerBoundedFeeEstimator<F>,
logger: &WithContext<L>,
Expand Down Expand Up @@ -5520,7 +5525,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
watch_outputs.append(&mut outputs);
}

self.block_confirmed(height, block_hash, txn_matched, watch_outputs, claimable_outpoints, &broadcaster, &fee_estimator, logger)
self.block_confirmed(height, block_hash, txn_matched, watch_outputs, claimable_outpoints, force_close_buffer, &broadcaster, &fee_estimator, logger)
}

/// Update state for new block(s)/transaction(s) confirmed. Note that the caller must update
Expand All @@ -5539,6 +5544,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
txn_matched: Vec<&Transaction>,
mut watch_outputs: Vec<TransactionOutputs>,
mut claimable_outpoints: Vec<PackageTemplate>,
force_close_buffer: u32,
broadcaster: &B,
fee_estimator: &LowerBoundedFeeEstimator<F>,
logger: &WithContext<L>,
Expand All @@ -5548,7 +5554,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {

// Only generate claims if we haven't already done so (e.g., in transactions_confirmed).
if claimable_outpoints.is_empty() {
let should_broadcast = self.should_broadcast_holder_commitment_txn(logger);
let should_broadcast = self.should_broadcast_holder_commitment_txn(force_close_buffer, logger);
if let Some(payment_hash) = should_broadcast {
let reason = ClosureReason::HTLCsTimedOut { payment_hash: Some(payment_hash) };
let (mut new_outpoints, mut new_outputs) =
Expand Down Expand Up @@ -5914,7 +5920,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {

#[rustfmt::skip]
fn should_broadcast_holder_commitment_txn<L: Logger>(
&self, logger: &WithContext<L>
&self, force_close_buffer: u32, logger: &WithContext<L>
) -> Option<PaymentHash> {
// There's no need to broadcast our commitment transaction if we've seen one confirmed (even
// with 1 confirmation) as it'll be rejected as duplicate/conflicting.
Expand Down Expand Up @@ -5953,7 +5959,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
// on-chain for an expired HTLC.
let htlc_outbound = $holder_tx == htlc.offered;
if ( htlc_outbound && htlc.cltv_expiry + LATENCY_GRACE_PERIOD_BLOCKS <= height) ||
(!htlc_outbound && htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER && self.payment_preimages.contains_key(&htlc.payment_hash)) {
(!htlc_outbound && htlc.cltv_expiry <= height + force_close_buffer && self.payment_preimages.contains_key(&htlc.payment_hash)) {
log_info!(logger, "Force-closing channel due to {} HTLC timeout - HTLC with payment hash {} expires at {}", if htlc_outbound { "outbound" } else { "inbound"}, htlc.payment_hash, htlc.cltv_expiry);
return Some(htlc.payment_hash);
}
Expand Down Expand Up @@ -6263,7 +6269,15 @@ impl<Signer: EcdsaChannelSigner, T: BroadcasterInterface, F: FeeEstimator, L: Lo
for (ChannelMonitor<Signer>, T, F, L)
{
fn filtered_block_connected(&self, header: &Header, txdata: &TransactionData, height: u32) {
self.0.block_connected(header, txdata, height, &self.1, &self.2, &self.3);
self.0.block_connected(
header,
txdata,
height,
CLTV_CLAIM_BUFFER,
&self.1,
&self.2,
&self.3,
);
}

fn blocks_disconnected(&self, fork_point: BestBlock) {
Expand All @@ -6277,15 +6291,23 @@ where
M: Deref<Target = ChannelMonitor<Signer>>,
{
fn transactions_confirmed(&self, header: &Header, txdata: &TransactionData, height: u32) {
self.0.transactions_confirmed(header, txdata, height, &self.1, &self.2, &self.3);
self.0.transactions_confirmed(
header,
txdata,
height,
CLTV_CLAIM_BUFFER,
&self.1,
&self.2,
&self.3,
);
}

fn transaction_unconfirmed(&self, txid: &Txid) {
self.0.transaction_unconfirmed(txid, &self.1, &self.2, &self.3);
}

fn best_block_updated(&self, header: &Header, height: u32) {
self.0.best_block_updated(header, height, &self.1, &self.2, &self.3);
self.0.best_block_updated(header, height, CLTV_CLAIM_BUFFER, &self.1, &self.2, &self.3);
}

fn get_relevant_txids(&self) -> Vec<(Txid, u32, Option<BlockHash>)> {
Expand Down
5 changes: 4 additions & 1 deletion lightning/src/ln/chanmon_update_fail_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@

use crate::chain::chaininterface::LowerBoundedFeeEstimator;
use crate::chain::chainmonitor::ChainMonitor;
use crate::chain::channelmonitor::{ChannelMonitor, MonitorEvent, ANTI_REORG_DELAY};
use crate::chain::channelmonitor::{
ChannelMonitor, MonitorEvent, ANTI_REORG_DELAY, CLTV_CLAIM_BUFFER,
};
use crate::chain::transaction::OutPoint;
use crate::chain::{ChannelMonitorUpdateStatus, Listen, Watch};
use crate::events::{ClosureReason, Event, HTLCHandlingFailureType, PaymentPurpose};
Expand Down Expand Up @@ -4927,6 +4929,7 @@ fn native_async_persist() {
native_async_persister,
Arc::clone(&keys_manager),
keys_manager.get_peer_storage_key(),
CLTV_CLAIM_BUFFER,
);

// Write the initial ChannelMonitor async, testing primarily that the `MonitorEvent::Completed`
Expand Down
Loading
Loading