Merge pull request #1796 from tnull/2022-10-track-confirmation-block-hash

Track confirmation block hash and return via `Confirm::get_relevant_txids`
This commit is contained in:
Matt Corallo 2022-11-09 20:24:10 +00:00 committed by GitHub
commit b6fce3d9cc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 129 additions and 60 deletions

View file

@ -24,7 +24,7 @@
//! servicing [`ChannelMonitor`] updates from the client.
use bitcoin::blockdata::block::BlockHeader;
use bitcoin::hash_types::Txid;
use bitcoin::hash_types::{Txid, BlockHash};
use crate::chain;
use crate::chain::{ChannelMonitorUpdateStatus, Filter, WatchedOutput};
@ -561,7 +561,7 @@ where
});
}
fn get_relevant_txids(&self) -> Vec<Txid> {
fn get_relevant_txids(&self) -> Vec<(Txid, Option<BlockHash>)> {
let mut txids = Vec::new();
let monitor_states = self.monitors.read().unwrap();
for monitor_state in monitor_states.values() {

View file

@ -332,14 +332,15 @@ impl Readable for CounterpartyCommitmentParameters {
}
}
/// An entry for an [`OnchainEvent`], stating the block height when the event was observed and the
/// transaction causing it.
/// An entry for an [`OnchainEvent`], stating the block height and hash when the event was
/// observed, as well as the transaction causing it.
///
/// Used to determine when the on-chain event can be considered safe from a chain reorganization.
#[derive(PartialEq, Eq)]
struct OnchainEventEntry {
txid: Txid,
height: u32,
block_hash: Option<BlockHash>, // Added as optional, will be filled in for any entry generated on 0.0.113 or after
event: OnchainEvent,
transaction: Option<Transaction>, // Added as optional, but always filled in, in LDK 0.0.110
}
@ -440,6 +441,7 @@ impl Writeable for OnchainEventEntry {
(0, self.txid, required),
(1, self.transaction, option),
(2, self.height, required),
(3, self.block_hash, option),
(4, self.event, required),
});
Ok(())
@ -450,16 +452,18 @@ impl MaybeReadable for OnchainEventEntry {
fn read<R: io::Read>(reader: &mut R) -> Result<Option<Self>, DecodeError> {
let mut txid = Txid::all_zeros();
let mut transaction = None;
let mut block_hash = None;
let mut height = 0;
let mut event = None;
read_tlv_fields!(reader, {
(0, txid, required),
(1, transaction, option),
(2, height, required),
(3, block_hash, option),
(4, event, ignorable),
});
if let Some(ev) = event {
Ok(Some(Self { txid, transaction, height, event: ev }))
Ok(Some(Self { txid, transaction, height, block_hash, event: ev }))
} else {
Ok(None)
}
@ -1484,11 +1488,11 @@ impl<Signer: Sign> ChannelMonitor<Signer> {
}
/// Returns the set of txids that should be monitored for re-organization out of the chain.
pub fn get_relevant_txids(&self) -> Vec<Txid> {
pub fn get_relevant_txids(&self) -> Vec<(Txid, Option<BlockHash>)> {
let inner = self.inner.lock().unwrap();
let mut txids: Vec<Txid> = inner.onchain_events_awaiting_threshold_conf
let mut txids: Vec<(Txid, Option<BlockHash>)> = inner.onchain_events_awaiting_threshold_conf
.iter()
.map(|entry| entry.txid)
.map(|entry| (entry.txid, entry.block_hash))
.chain(inner.onchain_tx_handler.get_relevant_txids().into_iter())
.collect();
txids.sort_unstable();
@ -1941,7 +1945,7 @@ impl<Signer: Sign> ChannelMonitor<Signer> {
/// been revoked yet, the previous one, we we will never "forget" to resolve an HTLC.
macro_rules! fail_unbroadcast_htlcs {
($self: expr, $commitment_tx_type: expr, $commitment_txid_confirmed: expr, $commitment_tx_confirmed: expr,
$commitment_tx_conf_height: expr, $confirmed_htlcs_list: expr, $logger: expr) => { {
$commitment_tx_conf_height: expr, $commitment_tx_conf_hash: expr, $confirmed_htlcs_list: expr, $logger: expr) => { {
debug_assert_eq!($commitment_tx_confirmed.txid(), $commitment_txid_confirmed);
macro_rules! check_htlc_fails {
@ -1985,6 +1989,7 @@ macro_rules! fail_unbroadcast_htlcs {
txid: $commitment_txid_confirmed,
transaction: Some($commitment_tx_confirmed.clone()),
height: $commitment_tx_conf_height,
block_hash: Some(*$commitment_tx_conf_hash),
event: OnchainEvent::HTLCUpdate {
source: (**source).clone(),
payment_hash: htlc.payment_hash.clone(),
@ -2172,7 +2177,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
macro_rules! claim_htlcs {
($commitment_number: expr, $txid: expr) => {
let (htlc_claim_reqs, _) = self.get_counterparty_output_claim_info($commitment_number, $txid, None);
self.onchain_tx_handler.update_claims_view(&Vec::new(), htlc_claim_reqs, self.best_block.height(), self.best_block.height(), broadcaster, fee_estimator, logger);
self.onchain_tx_handler.update_claims_view_from_requests(htlc_claim_reqs, self.best_block.height(), self.best_block.height(), broadcaster, fee_estimator, logger);
}
}
if let Some(txid) = self.current_counterparty_commitment_txid {
@ -2198,10 +2203,10 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
// 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(&Vec::new(), claim_reqs, self.best_block.height(), self.best_block.height(), broadcaster, fee_estimator, logger);
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());
self.onchain_tx_handler.update_claims_view(&Vec::new(), claim_reqs, self.best_block.height(), self.best_block.height(), broadcaster, fee_estimator, logger);
self.onchain_tx_handler.update_claims_view_from_requests(claim_reqs, self.best_block.height(), self.best_block.height(), broadcaster, fee_estimator, logger);
}
}
}
@ -2288,8 +2293,8 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
PackageSolvingData::HolderFundingOutput(funding_output),
best_block_height, false, best_block_height,
);
self.onchain_tx_handler.update_claims_view(
&[], vec![commitment_package], best_block_height, best_block_height,
self.onchain_tx_handler.update_claims_view_from_requests(
vec![commitment_package], best_block_height, best_block_height,
broadcaster, &bounded_fee_estimator, logger,
);
}
@ -2403,7 +2408,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
/// Returns packages to claim the revoked output(s), as well as additional outputs to watch and
/// general information about the output that is to the counterparty in the commitment
/// transaction.
fn check_spend_counterparty_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L)
fn check_spend_counterparty_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, block_hash: &BlockHash, logger: &L)
-> (Vec<PackageTemplate>, TransactionOutputs, CommitmentTxCounterpartyOutputInfo)
where L::Target: Logger {
// Most secp and related errors trying to create keys means we have no hope of constructing
@ -2474,13 +2479,13 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
if let Some(per_commitment_data) = per_commitment_option {
fail_unbroadcast_htlcs!(self, "revoked_counterparty", commitment_txid, tx, height,
per_commitment_data.iter().map(|(htlc, htlc_source)|
block_hash, per_commitment_data.iter().map(|(htlc, htlc_source)|
(htlc, htlc_source.as_ref().map(|htlc_source| htlc_source.as_ref()))
), logger);
} else {
debug_assert!(false, "We should have per-commitment option for any recognized old commitment txn");
fail_unbroadcast_htlcs!(self, "revoked counterparty", commitment_txid, tx, height,
[].iter().map(|reference| *reference), logger);
block_hash, [].iter().map(|reference| *reference), logger);
}
}
} else if let Some(per_commitment_data) = per_commitment_option {
@ -2497,7 +2502,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
self.counterparty_commitment_txn_on_chain.insert(commitment_txid, commitment_number);
log_info!(logger, "Got broadcast of non-revoked counterparty commitment transaction {}", commitment_txid);
fail_unbroadcast_htlcs!(self, "counterparty", commitment_txid, tx, height,
fail_unbroadcast_htlcs!(self, "counterparty", commitment_txid, tx, height, block_hash,
per_commitment_data.iter().map(|(htlc, htlc_source)|
(htlc, htlc_source.as_ref().map(|htlc_source| htlc_source.as_ref()))
), logger);
@ -2633,7 +2638,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
(claimable_outpoints, Some((htlc_txid, outputs)))
}
// Returns (1) `PackageTemplate`s that can be given to the OnChainTxHandler, so that the handler can
// Returns (1) `PackageTemplate`s that can be given to the OnchainTxHandler, so that the handler can
// broadcast transactions claiming holder HTLC commitment outputs and (2) a holder revokable
// script so we can detect whether a holder transaction has been seen on-chain.
fn get_broadcasted_holder_claims(&self, holder_tx: &HolderSignedTx, conf_height: u32) -> (Vec<PackageTemplate>, Option<(Script, PublicKey, PublicKey)>) {
@ -2678,7 +2683,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
/// revoked using data in holder_claimable_outpoints.
/// Should not be used if check_spend_revoked_transaction succeeds.
/// Returns None unless the transaction is definitely one of our commitment transactions.
fn check_spend_holder_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) -> Option<(Vec<PackageTemplate>, TransactionOutputs)> where L::Target: Logger {
fn check_spend_holder_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, block_hash: &BlockHash, logger: &L) -> Option<(Vec<PackageTemplate>, TransactionOutputs)> where L::Target: Logger {
let commitment_txid = tx.txid();
let mut claim_requests = Vec::new();
let mut watch_outputs = Vec::new();
@ -2701,7 +2706,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
let mut to_watch = self.get_broadcasted_holder_watch_outputs(&self.current_holder_commitment_tx, tx);
append_onchain_update!(res, to_watch);
fail_unbroadcast_htlcs!(self, "latest holder", commitment_txid, tx, height,
self.current_holder_commitment_tx.htlc_outputs.iter()
block_hash, self.current_holder_commitment_tx.htlc_outputs.iter()
.map(|(htlc, _, htlc_source)| (htlc, htlc_source.as_ref())), logger);
} else if let &Some(ref holder_tx) = &self.prev_holder_signed_commitment_tx {
if holder_tx.txid == commitment_txid {
@ -2710,7 +2715,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
let res = self.get_broadcasted_holder_claims(holder_tx, height);
let mut to_watch = self.get_broadcasted_holder_watch_outputs(holder_tx, tx);
append_onchain_update!(res, to_watch);
fail_unbroadcast_htlcs!(self, "previous holder", commitment_txid, tx, height,
fail_unbroadcast_htlcs!(self, "previous holder", commitment_txid, tx, height, block_hash,
holder_tx.htlc_outputs.iter().map(|(htlc, _, htlc_source)| (htlc, htlc_source.as_ref())),
logger);
}
@ -2818,7 +2823,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
if height > self.best_block.height() {
self.best_block = BestBlock::new(block_hash, height);
self.block_confirmed(height, vec![], vec![], vec![], &broadcaster, &fee_estimator, &logger)
self.block_confirmed(height, block_hash, vec![], vec![], vec![], &broadcaster, &fee_estimator, &logger)
} else if block_hash != self.best_block.block_hash() {
self.best_block = BestBlock::new(block_hash, height);
self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.height <= height);
@ -2870,14 +2875,14 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
let mut commitment_tx_to_counterparty_output = None;
if (tx.input[0].sequence.0 >> 8*3) as u8 == 0x80 && (tx.lock_time.0 >> 8*3) as u8 == 0x20 {
let (mut new_outpoints, new_outputs, counterparty_output_idx_sats) =
self.check_spend_counterparty_transaction(&tx, height, &logger);
self.check_spend_counterparty_transaction(&tx, height, &block_hash, &logger);
commitment_tx_to_counterparty_output = counterparty_output_idx_sats;
if !new_outputs.1.is_empty() {
watch_outputs.push(new_outputs);
}
claimable_outpoints.append(&mut new_outpoints);
if new_outpoints.is_empty() {
if let Some((mut new_outpoints, new_outputs)) = self.check_spend_holder_transaction(&tx, height, &logger) {
if let Some((mut new_outpoints, new_outputs)) = self.check_spend_holder_transaction(&tx, height, &block_hash, &logger) {
debug_assert!(commitment_tx_to_counterparty_output.is_none(),
"A commitment transaction matched as both a counterparty and local commitment tx?");
if !new_outputs.1.is_empty() {
@ -2893,6 +2898,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
txid,
transaction: Some((*tx).clone()),
height,
block_hash: Some(block_hash),
event: OnchainEvent::FundingSpendConfirmation {
on_local_output_csv: balance_spendable_csv,
commitment_tx_to_counterparty_output,
@ -2911,28 +2917,30 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
// While all commitment/HTLC-Success/HTLC-Timeout transactions have one input, HTLCs
// can also be resolved in a few other ways which can have more than one output. Thus,
// we call is_resolving_htlc_output here outside of the tx.input.len() == 1 check.
self.is_resolving_htlc_output(&tx, height, &logger);
self.is_resolving_htlc_output(&tx, height, &block_hash, &logger);
self.is_paying_spendable_output(&tx, height, &logger);
self.is_paying_spendable_output(&tx, height, &block_hash, &logger);
}
if height > self.best_block.height() {
self.best_block = BestBlock::new(block_hash, height);
}
self.block_confirmed(height, txn_matched, watch_outputs, claimable_outpoints, &broadcaster, &fee_estimator, &logger)
self.block_confirmed(height, block_hash, txn_matched, watch_outputs, claimable_outpoints, &broadcaster, &fee_estimator, &logger)
}
/// Update state for new block(s)/transaction(s) confirmed. Note that the caller must update
/// `self.best_block` before calling if a new best blockchain tip is available. More
/// concretely, `self.best_block` must never be at a lower height than `conf_height`, avoiding
/// complexity especially in `OnchainTx::update_claims_view`.
/// complexity especially in
/// `OnchainTx::update_claims_view_from_requests`/`OnchainTx::update_claims_view_from_matched_txn`.
///
/// `conf_height` should be set to the height at which any new transaction(s)/block(s) were
/// confirmed at, even if it is not the current best height.
fn block_confirmed<B: Deref, F: Deref, L: Deref>(
&mut self,
conf_height: u32,
conf_hash: BlockHash,
txn_matched: Vec<&Transaction>,
mut watch_outputs: Vec<TransactionOutputs>,
mut claimable_outpoints: Vec<PackageTemplate>,
@ -3048,7 +3056,8 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
}
}
self.onchain_tx_handler.update_claims_view(&txn_matched, claimable_outpoints, conf_height, self.best_block.height(), broadcaster, fee_estimator, logger);
self.onchain_tx_handler.update_claims_view_from_requests(claimable_outpoints, conf_height, self.best_block.height(), broadcaster, fee_estimator, logger);
self.onchain_tx_handler.update_claims_view_from_matched_txn(&txn_matched, conf_height, conf_hash, self.best_block.height(), broadcaster, fee_estimator, logger);
// Determine new outputs to watch by comparing against previously known outputs to watch,
// updating the latter in the process.
@ -3237,7 +3246,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
/// Check if any transaction broadcasted is resolving HTLC output by a success or timeout on a holder
/// or counterparty commitment tx, if so send back the source, preimage if found and payment_hash of resolved HTLC
fn is_resolving_htlc_output<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) where L::Target: Logger {
fn is_resolving_htlc_output<L: Deref>(&mut self, tx: &Transaction, height: u32, block_hash: &BlockHash, logger: &L) where L::Target: Logger {
'outer_loop: for input in &tx.input {
let mut payment_data = None;
let htlc_claim = HTLCClaim::from_witness(&input.witness);
@ -3322,7 +3331,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
log_claim!($tx_info, $holder_tx, htlc_output, false);
let outbound_htlc = $holder_tx == htlc_output.offered;
self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
txid: tx.txid(), height, transaction: Some(tx.clone()),
txid: tx.txid(), height, block_hash: Some(*block_hash), transaction: Some(tx.clone()),
event: OnchainEvent::HTLCSpendConfirmation {
commitment_tx_output_idx: input.previous_output.vout,
preimage: if accepted_preimage_claim || offered_preimage_claim {
@ -3366,6 +3375,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
txid: tx.txid(),
height,
block_hash: Some(*block_hash),
transaction: Some(tx.clone()),
event: OnchainEvent::HTLCSpendConfirmation {
commitment_tx_output_idx: input.previous_output.vout,
@ -3389,6 +3399,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
txid: tx.txid(),
transaction: Some(tx.clone()),
height,
block_hash: Some(*block_hash),
event: OnchainEvent::HTLCSpendConfirmation {
commitment_tx_output_idx: input.previous_output.vout,
preimage: Some(payment_preimage),
@ -3416,6 +3427,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
txid: tx.txid(),
transaction: Some(tx.clone()),
height,
block_hash: Some(*block_hash),
event: OnchainEvent::HTLCUpdate {
source, payment_hash,
htlc_value_satoshis: Some(amount_msat / 1000),
@ -3430,7 +3442,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
}
/// 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, logger: &L) where L::Target: Logger {
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 {
@ -3490,6 +3502,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
txid: tx.txid(),
transaction: Some(tx.clone()),
height,
block_hash: Some(*block_hash),
event: OnchainEvent::MaturingOutput { descriptor: spendable_output.clone() },
};
log_info!(logger, "Received spendable output {}, spendable at height {}", log_spendable!(spendable_output), entry.confirmation_threshold());
@ -3531,7 +3544,7 @@ where
self.0.best_block_updated(header, height, &*self.1, &*self.2, &*self.3);
}
fn get_relevant_txids(&self) -> Vec<Txid> {
fn get_relevant_txids(&self) -> Vec<(Txid, Option<BlockHash>)> {
self.0.get_relevant_txids()
}
}

View file

@ -171,7 +171,8 @@ pub trait Confirm {
/// if they become available at the same time.
fn best_block_updated(&self, header: &BlockHeader, height: u32);
/// Returns transactions that should be monitored for reorganization out of the chain.
/// Returns transactions that should be monitored for reorganization out of the chain along
/// with the hash of the block as part of which had been previously confirmed.
///
/// Will include any transactions passed to [`transactions_confirmed`] that have insufficient
/// confirmations to be safe from a chain reorganization. Will not include any transactions
@ -180,11 +181,13 @@ pub trait Confirm {
/// May be called to determine the subset of transactions that must still be monitored for
/// reorganization. Will be idempotent between calls but may change as a result of calls to the
/// other interface methods. Thus, this is useful to determine which transactions may need to be
/// given to [`transaction_unconfirmed`].
/// given to [`transaction_unconfirmed`]. If any of the returned transactions are confirmed in
/// a block other than the one with the given hash, they need to be unconfirmed and reconfirmed
/// via [`transaction_unconfirmed`] and [`transactions_confirmed`], respectively.
///
/// [`transactions_confirmed`]: Self::transactions_confirmed
/// [`transaction_unconfirmed`]: Self::transaction_unconfirmed
fn get_relevant_txids(&self) -> Vec<Txid>;
fn get_relevant_txids(&self) -> Vec<(Txid, Option<BlockHash>)>;
}
/// An enum representing the status of a channel monitor update persistence.

View file

@ -16,7 +16,7 @@ use bitcoin::blockdata::transaction::Transaction;
use bitcoin::blockdata::transaction::OutPoint as BitcoinOutPoint;
use bitcoin::blockdata::script::Script;
use bitcoin::hash_types::Txid;
use bitcoin::hash_types::{Txid, BlockHash};
use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature};
use bitcoin::secp256k1;
@ -58,6 +58,7 @@ const MAX_ALLOC_SIZE: usize = 64*1024;
struct OnchainEventEntry {
txid: Txid,
height: u32,
block_hash: Option<BlockHash>, // Added as optional, will be filled in for any entry generated on 0.0.113 or after
event: OnchainEvent,
}
@ -92,6 +93,7 @@ impl Writeable for OnchainEventEntry {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
write_tlv_fields!(writer, {
(0, self.txid, required),
(1, self.block_hash, option),
(2, self.height, required),
(4, self.event, required),
});
@ -103,14 +105,16 @@ impl MaybeReadable for OnchainEventEntry {
fn read<R: io::Read>(reader: &mut R) -> Result<Option<Self>, DecodeError> {
let mut txid = Txid::all_zeros();
let mut height = 0;
let mut block_hash = None;
let mut event = None;
read_tlv_fields!(reader, {
(0, txid, required),
(1, block_hash, option),
(2, height, required),
(4, event, ignorable),
});
if let Some(ev) = event {
Ok(Some(Self { txid, height, event: ev }))
Ok(Some(Self { txid, height, block_hash, event: ev }))
} else {
Ok(None)
}
@ -543,17 +547,22 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
/// Upon channelmonitor.block_connected(..) or upon provision of a preimage on the forward link
/// for this channel, provide new relevant on-chain transactions and/or new claim requests.
/// Formerly this was named `block_connected`, but it is now also used for claiming an HTLC output
/// if we receive a preimage after force-close.
/// `conf_height` represents the height at which the transactions in `txn_matched` were
/// confirmed. This does not need to equal the current blockchain tip height, which should be
/// provided via `cur_height`, however it must never be higher than `cur_height`.
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)
where B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
L::Target: Logger,
/// Together with `update_claims_view_from_matched_txn` this used to be named
/// `block_connected`, but it is now also used for claiming an HTLC output if we receive a
/// preimage after force-close.
///
/// `conf_height` represents the height at which the request was generated. This
/// does not need to equal the current blockchain tip height, which should be provided via
/// `cur_height`, however it must never be higher than `cur_height`.
pub(crate) fn update_claims_view_from_requests<B: Deref, F: Deref, L: Deref>(
&mut self, requests: Vec<PackageTemplate>, conf_height: u32, cur_height: u32,
broadcaster: &B, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L
) where
B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
L::Target: Logger,
{
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());
log_debug!(logger, "Updating claims view at height {} with {} claim requests", cur_height, requests.len());
let mut preprocessed_requests = Vec::with_capacity(requests.len());
let mut aggregated_request = None;
@ -633,7 +642,25 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
self.pending_claim_requests.insert(txid, req);
}
}
}
/// Upon channelmonitor.block_connected(..) or upon provision of a preimage on the forward link
/// for this channel, provide new relevant on-chain transactions and/or new claim requests.
/// Together with `update_claims_view_from_requests` this used to be named `block_connected`,
/// but it is now also used for claiming an HTLC output if we receive a preimage after force-close.
///
/// `conf_height` represents the height at which the transactions in `txn_matched` were
/// confirmed. This does not need to equal the current blockchain tip height, which should be
/// provided via `cur_height`, however it must never be higher than `cur_height`.
pub(crate) fn update_claims_view_from_matched_txn<B: Deref, F: Deref, L: Deref>(
&mut self, txn_matched: &[&Transaction], conf_height: u32, conf_hash: BlockHash,
cur_height: u32, broadcaster: &B, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L
) where
B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
L::Target: Logger,
{
log_debug!(logger, "Updating claims view at height {} with {} matched transactions in block {}", cur_height, txn_matched.len(), conf_height);
let mut bump_candidates = HashMap::new();
for tx in txn_matched {
// 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> {
let entry = OnchainEventEntry {
txid: tx.txid(),
height: conf_height,
block_hash: Some(conf_hash),
event: OnchainEvent::Claim { claim_request: first_claim_txid_height.0.clone() }
};
if !self.onchain_events_awaiting_threshold_conf.contains(&entry) {
@ -701,6 +729,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
let entry = OnchainEventEntry {
txid: tx.txid(),
height: conf_height,
block_hash: Some(conf_hash),
event: OnchainEvent::ContentiousOutpoint { package },
};
if !self.onchain_events_awaiting_threshold_conf.contains(&entry) {
@ -860,12 +889,12 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
self.claimable_outpoints.get(outpoint).is_some()
}
pub(crate) fn get_relevant_txids(&self) -> Vec<Txid> {
let mut txids: Vec<Txid> = self.onchain_events_awaiting_threshold_conf
pub(crate) fn get_relevant_txids(&self) -> Vec<(Txid, Option<BlockHash>)> {
let mut txids: Vec<(Txid, Option<BlockHash>)> = self.onchain_events_awaiting_threshold_conf
.iter()
.map(|entry| entry.txid)
.map(|entry| (entry.txid, entry.block_hash))
.collect();
txids.sort_unstable();
txids.sort_unstable_by_key(|(txid, _)| *txid);
txids.dedup();
txids
}

View file

@ -4520,6 +4520,11 @@ impl<Signer: Sign> Channel<Signer> {
self.channel_transaction_parameters.funding_outpoint
}
/// Returns the block hash in which our funding transaction was confirmed.
pub fn get_funding_tx_confirmed_in(&self) -> Option<BlockHash> {
self.funding_tx_confirmed_in
}
fn get_holder_selected_contest_delay(&self) -> u16 {
self.channel_transaction_parameters.holder_selected_contest_delay
}

View file

@ -5906,12 +5906,12 @@ where
});
}
fn get_relevant_txids(&self) -> Vec<Txid> {
fn get_relevant_txids(&self) -> Vec<(Txid, Option<BlockHash>)> {
let channel_state = self.channel_state.lock().unwrap();
let mut res = Vec::with_capacity(channel_state.by_id.len());
for chan in channel_state.by_id.values() {
if let Some(funding_txo) = chan.get_funding_txo() {
res.push(funding_txo.txid);
if let (Some(funding_txo), block_hash) = (chan.get_funding_txo(), chan.get_funding_tx_confirmed_in()) {
res.push((funding_txo.txid, block_hash));
}
}
res

View file

@ -271,6 +271,7 @@ fn do_test_unconf_chan(reload_node: bool, reorg_after_reload: bool, use_funding_
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
*nodes[0].connect_style.borrow_mut() = connect_style;
let chan_conf_height = core::cmp::max(nodes[0].best_block_info().1 + 1, nodes[1].best_block_info().1 + 1);
let chan = create_announced_chan_between_nodes(&nodes, 0, 1, channelmanager::provided_init_features(), channelmanager::provided_init_features());
let channel_state = nodes[0].node.channel_state.lock().unwrap();
@ -281,8 +282,13 @@ fn do_test_unconf_chan(reload_node: bool, reorg_after_reload: bool, use_funding_
if !reorg_after_reload {
if use_funding_unconfirmed {
let relevant_txids = nodes[0].node.get_relevant_txids();
assert_eq!(&relevant_txids[..], &[chan.3.txid()]);
nodes[0].node.transaction_unconfirmed(&relevant_txids[0]);
assert_eq!(relevant_txids.len(), 1);
let block_hash_opt = relevant_txids[0].1;
let expected_hash = nodes[0].get_block_header(chan_conf_height).block_hash();
assert_eq!(block_hash_opt, Some(expected_hash));
let txid = relevant_txids[0].0;
assert_eq!(txid, chan.3.txid());
nodes[0].node.transaction_unconfirmed(&txid);
} else if connect_style == ConnectStyle::FullBlockViaListen {
disconnect_blocks(&nodes[0], CHAN_CONFIRM_DEPTH - 1);
assert_eq!(nodes[0].node.list_usable_channels().len(), 1);
@ -290,6 +296,10 @@ fn do_test_unconf_chan(reload_node: bool, reorg_after_reload: bool, use_funding_
} else {
disconnect_all_blocks(&nodes[0]);
}
let relevant_txids = nodes[0].node.get_relevant_txids();
assert_eq!(relevant_txids.len(), 0);
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.");
check_added_monitors!(nodes[1], 1);
{
@ -350,8 +360,13 @@ fn do_test_unconf_chan(reload_node: bool, reorg_after_reload: bool, use_funding_
if reorg_after_reload {
if use_funding_unconfirmed {
let relevant_txids = nodes[0].node.get_relevant_txids();
assert_eq!(&relevant_txids[..], &[chan.3.txid()]);
nodes[0].node.transaction_unconfirmed(&relevant_txids[0]);
assert_eq!(relevant_txids.len(), 1);
let block_hash_opt = relevant_txids[0].1;
let expected_hash = nodes[0].get_block_header(chan_conf_height).block_hash();
assert_eq!(block_hash_opt, Some(expected_hash));
let txid = relevant_txids[0].0;
assert_eq!(txid, chan.3.txid());
nodes[0].node.transaction_unconfirmed(&txid);
} else if connect_style == ConnectStyle::FullBlockViaListen {
disconnect_blocks(&nodes[0], CHAN_CONFIRM_DEPTH - 1);
assert_eq!(nodes[0].node.list_channels().len(), 1);
@ -359,6 +374,10 @@ fn do_test_unconf_chan(reload_node: bool, reorg_after_reload: bool, use_funding_
} else {
disconnect_all_blocks(&nodes[0]);
}
let relevant_txids = nodes[0].node.get_relevant_txids();
assert_eq!(relevant_txids.len(), 0);
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.");
check_added_monitors!(nodes[1], 1);
{