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;
-use ln::msgs::DecodeError;
-use ln::PaymentPreimage;
+use crate::ln::msgs::DecodeError;
+use crate::ln::PaymentPreimage;
#[cfg(anchors)]
-use ln::chan_utils;
-use ln::chan_utils::{ChannelTransactionParameters, HolderCommitmentTransaction};
+use crate::ln::chan_utils;
+use crate::ln::chan_utils::{ChannelTransactionParameters, HolderCommitmentTransaction};
#[cfg(anchors)]
-use chain::chaininterface::ConfirmationTarget;
-use chain::chaininterface::{FeeEstimator, BroadcasterInterface, LowerBoundedFeeEstimator};
-use chain::channelmonitor::{ANTI_REORG_DELAY, CLTV_SHARED_CLAIM_BUFFER};
-use chain::keysinterface::{Sign, KeysInterface};
+use crate::chain::chaininterface::ConfirmationTarget;
+use crate::chain::chaininterface::{FeeEstimator, BroadcasterInterface, LowerBoundedFeeEstimator};
+use crate::chain::channelmonitor::{ANTI_REORG_DELAY, CLTV_SHARED_CLAIM_BUFFER};
+use crate::chain::keysinterface::{Sign, KeysInterface};
#[cfg(anchors)]
-use chain::package::PackageSolvingData;
-use chain::package::PackageTemplate;
-use util::logger::Logger;
-use util::ser::{Readable, ReadableArgs, MaybeReadable, Writer, Writeable, VecWriter};
-use util::byte_utils;
-
-use io;
-use prelude::*;
+use crate::chain::package::PackageSolvingData;
+use crate::chain::package::PackageTemplate;
+use crate::util::logger::Logger;
+use crate::util::ser::{Readable, ReadableArgs, MaybeReadable, Writer, Writeable, VecWriter};
+use crate::util::byte_utils;
+
+use crate::io;
+use crate::prelude::*;
use alloc::collections::BTreeMap;
use core::cmp;
use core::ops::Deref;
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,
}
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),
});
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)
}
where F::Target: FeeEstimator,
L::Target: Logger,
{
- if cached_request.outpoints().len() == 0 { return None } // But don't prune pending claiming request yet, we may have to resurrect HTLCs
+ let request_outpoints = cached_request.outpoints();
+ if request_outpoints.is_empty() {
+ // Don't prune pending claiming request yet, we may have to resurrect HTLCs. Untractable
+ // packages cannot be aggregated and will never be split, so we cannot end up with an
+ // empty claim.
+ debug_assert!(cached_request.is_malleable());
+ return None;
+ }
+ // If we've seen transaction inclusion in the chain for all outpoints in our request, we
+ // don't need to continue generating more claims. We'll keep tracking the request to fully
+ // remove it once it reaches the confirmation threshold, or to generate a new claim if the
+ // transaction is reorged out.
+ let mut all_inputs_have_confirmed_spend = true;
+ for outpoint in &request_outpoints {
+ if let Some(first_claim_txid_height) = self.claimable_outpoints.get(outpoint) {
+ // We check for outpoint spends within claims individually rather than as a set
+ // since requests can have outpoints split off.
+ if !self.onchain_events_awaiting_threshold_conf.iter()
+ .any(|event_entry| if let OnchainEvent::Claim { claim_request } = event_entry.event {
+ first_claim_txid_height.0 == claim_request
+ } else {
+ // The onchain event is not a claim, keep seeking until we find one.
+ false
+ })
+ {
+ // Either we had no `OnchainEvent::Claim`, or we did but none matched the
+ // outpoint's registered spend.
+ all_inputs_have_confirmed_spend = false;
+ }
+ } else {
+ // The request's outpoint spend does not exist yet.
+ all_inputs_have_confirmed_spend = false;
+ }
+ }
+ if all_inputs_have_confirmed_spend {
+ return None;
+ }
// Compute new height timer to decide when we need to regenerate a new bumped version of the claim tx (if we
// didn't receive confirmation of it before, or not enough reorg-safe depth on top of it).
/// 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;
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
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) {
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) {
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
}