mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-03-15 15:39:09 +01:00
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:
commit
b6fce3d9cc
7 changed files with 129 additions and 60 deletions
|
@ -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() {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue