Skip to content

Commit 9685d6c

Browse files
committed
Track block hash, return via get_relevant_txids
Previously, `Confirm::get_relevant_txids()` only returned a list of transactions that have to be monitored for reorganization out of the chain. This interface however required double bookkeeping: while we internally keep track of the best block, height, etc, it would also require the user to keep track which transaction was previously confirmed in which block and to take actions based on any change, e.g, to reconfirm them when the block would be reorged-out and the transactions had been reconfirmed in another block. Here, we track the confirmation block hash internally and return it via `Confirm::get_relevant_txids()` to the user, which alleviates the requirement for double bookkeeping: the user can now simply check whether the given transaction is still confirmed and in the given block, and take action if not. We also split `update_claims_view`: Previously it was one, now it's two methods: `update_claims_view_from_matched_txn` and `update_claims_view_from_requests`.
1 parent e55e0d5 commit 9685d6c

File tree

7 files changed

+129
-60
lines changed

7 files changed

+129
-60
lines changed

lightning/src/chain/chainmonitor.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
//! servicing [`ChannelMonitor`] updates from the client.
2525
2626
use bitcoin::blockdata::block::BlockHeader;
27-
use bitcoin::hash_types::Txid;
27+
use bitcoin::hash_types::{Txid, BlockHash};
2828

2929
use crate::chain;
3030
use crate::chain::{ChannelMonitorUpdateStatus, Filter, WatchedOutput};
@@ -544,7 +544,7 @@ where
544544
});
545545
}
546546

547-
fn get_relevant_txids(&self) -> Vec<Txid> {
547+
fn get_relevant_txids(&self) -> Vec<(Txid, Option<BlockHash>)> {
548548
let mut txids = Vec::new();
549549
let monitor_states = self.monitors.read().unwrap();
550550
for monitor_state in monitor_states.values() {

lightning/src/chain/channelmonitor.rs

Lines changed: 45 additions & 32 deletions
Large diffs are not rendered by default.

lightning/src/chain/mod.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,8 @@ pub trait Confirm {
171171
/// if they become available at the same time.
172172
fn best_block_updated(&self, header: &BlockHeader, height: u32);
173173

174-
/// Returns transactions that should be monitored for reorganization out of the chain.
174+
/// Returns transactions that should be monitored for reorganization out of the chain along
175+
/// with the hash of the block as part of which had been previously confirmed.
175176
///
176177
/// Will include any transactions passed to [`transactions_confirmed`] that have insufficient
177178
/// confirmations to be safe from a chain reorganization. Will not include any transactions
@@ -180,11 +181,13 @@ pub trait Confirm {
180181
/// May be called to determine the subset of transactions that must still be monitored for
181182
/// reorganization. Will be idempotent between calls but may change as a result of calls to the
182183
/// other interface methods. Thus, this is useful to determine which transactions may need to be
183-
/// given to [`transaction_unconfirmed`].
184+
/// given to [`transaction_unconfirmed`]. If any of the returned transactions are confirmed in
185+
/// a block other than the one with the given hash, they need to be unconfirmed and reconfirmed
186+
/// via [`transaction_unconfirmed`] and [`transactions_confirmed`], respectively.
184187
///
185188
/// [`transactions_confirmed`]: Self::transactions_confirmed
186189
/// [`transaction_unconfirmed`]: Self::transaction_unconfirmed
187-
fn get_relevant_txids(&self) -> Vec<Txid>;
190+
fn get_relevant_txids(&self) -> Vec<(Txid, Option<BlockHash>)>;
188191
}
189192

190193
/// An enum representing the status of a channel monitor update persistence.

lightning/src/chain/onchaintx.rs

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use bitcoin::blockdata::transaction::Transaction;
1616
use bitcoin::blockdata::transaction::OutPoint as BitcoinOutPoint;
1717
use bitcoin::blockdata::script::Script;
1818

19-
use bitcoin::hash_types::Txid;
19+
use bitcoin::hash_types::{Txid, BlockHash};
2020

2121
use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature};
2222
use bitcoin::secp256k1;
@@ -58,6 +58,7 @@ const MAX_ALLOC_SIZE: usize = 64*1024;
5858
struct OnchainEventEntry {
5959
txid: Txid,
6060
height: u32,
61+
block_hash: Option<BlockHash>, // Added as optional, will be filled in for any entry generated on 0.0.113 or after
6162
event: OnchainEvent,
6263
}
6364

@@ -92,6 +93,7 @@ impl Writeable for OnchainEventEntry {
9293
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
9394
write_tlv_fields!(writer, {
9495
(0, self.txid, required),
96+
(1, self.block_hash, option),
9597
(2, self.height, required),
9698
(4, self.event, required),
9799
});
@@ -103,14 +105,16 @@ impl MaybeReadable for OnchainEventEntry {
103105
fn read<R: io::Read>(reader: &mut R) -> Result<Option<Self>, DecodeError> {
104106
let mut txid = Txid::all_zeros();
105107
let mut height = 0;
108+
let mut block_hash = None;
106109
let mut event = None;
107110
read_tlv_fields!(reader, {
108111
(0, txid, required),
112+
(1, block_hash, option),
109113
(2, height, required),
110114
(4, event, ignorable),
111115
});
112116
if let Some(ev) = event {
113-
Ok(Some(Self { txid, height, event: ev }))
117+
Ok(Some(Self { txid, height, block_hash, event: ev }))
114118
} else {
115119
Ok(None)
116120
}
@@ -543,17 +547,22 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
543547

544548
/// Upon channelmonitor.block_connected(..) or upon provision of a preimage on the forward link
545549
/// for this channel, provide new relevant on-chain transactions and/or new claim requests.
546-
/// Formerly this was named `block_connected`, but it is now also used for claiming an HTLC output
547-
/// if we receive a preimage after force-close.
548-
/// `conf_height` represents the height at which the transactions in `txn_matched` were
549-
/// confirmed. This does not need to equal the current blockchain tip height, which should be
550-
/// provided via `cur_height`, however it must never be higher than `cur_height`.
551-
pub(crate) fn update_claims_view<B: Deref, F: Deref, L: Deref>(&mut self, txn_matched: &[&Transaction], requests: Vec<PackageTemplate>, conf_height: u32, cur_height: u32, broadcaster: &B, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L)
552-
where B::Target: BroadcasterInterface,
553-
F::Target: FeeEstimator,
554-
L::Target: Logger,
550+
/// Together with `update_claims_view_from_matched_txn` this used to be named
551+
/// `block_connected`, but it is now also used for claiming an HTLC output if we receive a
552+
/// preimage after force-close.
553+
///
554+
/// `conf_height` represents the height at which the request was generated. This
555+
/// does not need to equal the current blockchain tip height, which should be provided via
556+
/// `cur_height`, however it must never be higher than `cur_height`.
557+
pub(crate) fn update_claims_view_from_requests<B: Deref, F: Deref, L: Deref>(
558+
&mut self, requests: Vec<PackageTemplate>, conf_height: u32, cur_height: u32,
559+
broadcaster: &B, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L
560+
) where
561+
B::Target: BroadcasterInterface,
562+
F::Target: FeeEstimator,
563+
L::Target: Logger,
555564
{
556-
log_debug!(logger, "Updating claims view at height {} with {} matched transactions in block {} and {} claim requests", cur_height, txn_matched.len(), conf_height, requests.len());
565+
log_debug!(logger, "Updating claims view at height {} with {} claim requests", cur_height, requests.len());
557566
let mut preprocessed_requests = Vec::with_capacity(requests.len());
558567
let mut aggregated_request = None;
559568

@@ -633,7 +642,25 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
633642
self.pending_claim_requests.insert(txid, req);
634643
}
635644
}
645+
}
636646

647+
/// Upon channelmonitor.block_connected(..) or upon provision of a preimage on the forward link
648+
/// for this channel, provide new relevant on-chain transactions and/or new claim requests.
649+
/// Together with `update_claims_view_from_requests` this used to be named `block_connected`,
650+
/// but it is now also used for claiming an HTLC output if we receive a preimage after force-close.
651+
///
652+
/// `conf_height` represents the height at which the transactions in `txn_matched` were
653+
/// confirmed. This does not need to equal the current blockchain tip height, which should be
654+
/// provided via `cur_height`, however it must never be higher than `cur_height`.
655+
pub(crate) fn update_claims_view_from_matched_txn<B: Deref, F: Deref, L: Deref>(
656+
&mut self, txn_matched: &[&Transaction], conf_height: u32, conf_hash: BlockHash,
657+
cur_height: u32, broadcaster: &B, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L
658+
) where
659+
B::Target: BroadcasterInterface,
660+
F::Target: FeeEstimator,
661+
L::Target: Logger,
662+
{
663+
log_debug!(logger, "Updating claims view at height {} with {} matched transactions in block {}", cur_height, txn_matched.len(), conf_height);
637664
let mut bump_candidates = HashMap::new();
638665
for tx in txn_matched {
639666
// Scan all input to verify is one of the outpoint spent is of interest for us
@@ -661,6 +688,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
661688
let entry = OnchainEventEntry {
662689
txid: tx.txid(),
663690
height: conf_height,
691+
block_hash: Some(conf_hash),
664692
event: OnchainEvent::Claim { claim_request: first_claim_txid_height.0.clone() }
665693
};
666694
if !self.onchain_events_awaiting_threshold_conf.contains(&entry) {
@@ -701,6 +729,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
701729
let entry = OnchainEventEntry {
702730
txid: tx.txid(),
703731
height: conf_height,
732+
block_hash: Some(conf_hash),
704733
event: OnchainEvent::ContentiousOutpoint { package },
705734
};
706735
if !self.onchain_events_awaiting_threshold_conf.contains(&entry) {
@@ -860,12 +889,12 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
860889
self.claimable_outpoints.get(outpoint).is_some()
861890
}
862891

863-
pub(crate) fn get_relevant_txids(&self) -> Vec<Txid> {
864-
let mut txids: Vec<Txid> = self.onchain_events_awaiting_threshold_conf
892+
pub(crate) fn get_relevant_txids(&self) -> Vec<(Txid, Option<BlockHash>)> {
893+
let mut txids: Vec<(Txid, Option<BlockHash>)> = self.onchain_events_awaiting_threshold_conf
865894
.iter()
866-
.map(|entry| entry.txid)
895+
.map(|entry| (entry.txid, entry.block_hash))
867896
.collect();
868-
txids.sort_unstable();
897+
txids.sort_unstable_by_key(|(txid, _)| *txid);
869898
txids.dedup();
870899
txids
871900
}

lightning/src/ln/channel.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4520,6 +4520,11 @@ impl<Signer: Sign> Channel<Signer> {
45204520
self.channel_transaction_parameters.funding_outpoint
45214521
}
45224522

4523+
/// Returns the block hash in which our funding transaction was confirmed.
4524+
pub fn get_funding_tx_confirmed_in(&self) -> Option<BlockHash> {
4525+
self.funding_tx_confirmed_in
4526+
}
4527+
45234528
fn get_holder_selected_contest_delay(&self) -> u16 {
45244529
self.channel_transaction_parameters.holder_selected_contest_delay
45254530
}

lightning/src/ln/channelmanager.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5853,12 +5853,12 @@ where
58535853
});
58545854
}
58555855

5856-
fn get_relevant_txids(&self) -> Vec<Txid> {
5856+
fn get_relevant_txids(&self) -> Vec<(Txid, Option<BlockHash>)> {
58575857
let channel_state = self.channel_state.lock().unwrap();
58585858
let mut res = Vec::with_capacity(channel_state.short_to_chan_info.len());
58595859
for chan in channel_state.by_id.values() {
5860-
if let Some(funding_txo) = chan.get_funding_txo() {
5861-
res.push(funding_txo.txid);
5860+
if let (Some(funding_txo), block_hash) = (chan.get_funding_txo(), chan.get_funding_tx_confirmed_in()) {
5861+
res.push((funding_txo.txid, block_hash));
58625862
}
58635863
}
58645864
res

lightning/src/ln/reorg_tests.rs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ fn do_test_unconf_chan(reload_node: bool, reorg_after_reload: bool, use_funding_
271271
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
272272
*nodes[0].connect_style.borrow_mut() = connect_style;
273273

274+
let chan_conf_height = core::cmp::max(nodes[0].best_block_info().1 + 1, nodes[1].best_block_info().1 + 1);
274275
let chan = create_announced_chan_between_nodes(&nodes, 0, 1, channelmanager::provided_init_features(), channelmanager::provided_init_features());
275276

276277
let channel_state = nodes[0].node.channel_state.lock().unwrap();
@@ -281,15 +282,24 @@ fn do_test_unconf_chan(reload_node: bool, reorg_after_reload: bool, use_funding_
281282
if !reorg_after_reload {
282283
if use_funding_unconfirmed {
283284
let relevant_txids = nodes[0].node.get_relevant_txids();
284-
assert_eq!(&relevant_txids[..], &[chan.3.txid()]);
285-
nodes[0].node.transaction_unconfirmed(&relevant_txids[0]);
285+
assert_eq!(relevant_txids.len(), 1);
286+
let block_hash_opt = relevant_txids[0].1;
287+
let expected_hash = nodes[0].get_block_header(chan_conf_height).block_hash();
288+
assert_eq!(block_hash_opt, Some(expected_hash));
289+
let txid = relevant_txids[0].0;
290+
assert_eq!(txid, chan.3.txid());
291+
nodes[0].node.transaction_unconfirmed(&txid);
286292
} else if connect_style == ConnectStyle::FullBlockViaListen {
287293
disconnect_blocks(&nodes[0], CHAN_CONFIRM_DEPTH - 1);
288294
assert_eq!(nodes[0].node.list_usable_channels().len(), 1);
289295
disconnect_blocks(&nodes[0], 1);
290296
} else {
291297
disconnect_all_blocks(&nodes[0]);
292298
}
299+
300+
let relevant_txids = nodes[0].node.get_relevant_txids();
301+
assert_eq!(relevant_txids.len(), 0);
302+
293303
handle_announce_close_broadcast_events(&nodes, 0, 1, true, "Channel closed because of an exception: Funding transaction was un-confirmed. Locked at 6 confs, now have 0 confs.");
294304
check_added_monitors!(nodes[1], 1);
295305
{
@@ -350,15 +360,24 @@ fn do_test_unconf_chan(reload_node: bool, reorg_after_reload: bool, use_funding_
350360
if reorg_after_reload {
351361
if use_funding_unconfirmed {
352362
let relevant_txids = nodes[0].node.get_relevant_txids();
353-
assert_eq!(&relevant_txids[..], &[chan.3.txid()]);
354-
nodes[0].node.transaction_unconfirmed(&relevant_txids[0]);
363+
assert_eq!(relevant_txids.len(), 1);
364+
let block_hash_opt = relevant_txids[0].1;
365+
let expected_hash = nodes[0].get_block_header(chan_conf_height).block_hash();
366+
assert_eq!(block_hash_opt, Some(expected_hash));
367+
let txid = relevant_txids[0].0;
368+
assert_eq!(txid, chan.3.txid());
369+
nodes[0].node.transaction_unconfirmed(&txid);
355370
} else if connect_style == ConnectStyle::FullBlockViaListen {
356371
disconnect_blocks(&nodes[0], CHAN_CONFIRM_DEPTH - 1);
357372
assert_eq!(nodes[0].node.list_channels().len(), 1);
358373
disconnect_blocks(&nodes[0], 1);
359374
} else {
360375
disconnect_all_blocks(&nodes[0]);
361376
}
377+
378+
let relevant_txids = nodes[0].node.get_relevant_txids();
379+
assert_eq!(relevant_txids.len(), 0);
380+
362381
handle_announce_close_broadcast_events(&nodes, 0, 1, true, "Channel closed because of an exception: Funding transaction was un-confirmed. Locked at 6 confs, now have 0 confs.");
363382
check_added_monitors!(nodes[1], 1);
364383
{

0 commit comments

Comments
 (0)