-use lightning::chain::WatchedOutput;
-use bitcoin::{Txid, BlockHash, Transaction, OutPoint};
-use bitcoin::blockdata::block::Header;
-
-use std::collections::{HashSet, HashMap};
+use bitcoin::block::Header;
+use bitcoin::{BlockHash, OutPoint, Transaction, Txid};
+use lightning::chain::channelmonitor::ANTI_REORG_DELAY;
+use lightning::chain::{Confirm, WatchedOutput};
+use std::collections::{HashMap, HashSet};
+use std::ops::Deref;
// Represents the current state.
pub(crate) struct SyncState {
// Outputs that were previously processed, but must not be forgotten yet as
// as we still need to monitor any spends on-chain.
pub watched_outputs: HashMap<OutPoint, WatchedOutput>,
+ // Outputs for which we previously saw a spend on-chain but kept around until the spends reach
+ // sufficient depth.
+ pub outputs_spends_pending_threshold_conf: Vec<(Txid, u32, OutPoint, WatchedOutput)>,
// The tip hash observed during our last sync.
pub last_sync_hash: Option<BlockHash>,
// Indicates whether we need to resync, e.g., after encountering an error.
Self {
watched_transactions: HashSet::new(),
watched_outputs: HashMap::new(),
+ outputs_spends_pending_threshold_conf: Vec::new(),
last_sync_hash: None,
pending_sync: false,
}
}
-}
+ pub fn sync_unconfirmed_transactions<C: Deref>(
+ &mut self, confirmables: &Vec<C>, unconfirmed_txs: Vec<Txid>,
+ ) where
+ C::Target: Confirm,
+ {
+ for txid in unconfirmed_txs {
+ for c in confirmables {
+ c.transaction_unconfirmed(&txid);
+ }
+
+ self.watched_transactions.insert(txid);
+
+ // If a previously-confirmed output spend is unconfirmed, re-add the watched output to
+ // the tracking map.
+ self.outputs_spends_pending_threshold_conf.retain(
+ |(conf_txid, _, prev_outpoint, output)| {
+ if txid == *conf_txid {
+ self.watched_outputs.insert(*prev_outpoint, output.clone());
+ false
+ } else {
+ true
+ }
+ },
+ )
+ }
+ }
+ pub fn sync_confirmed_transactions<C: Deref>(
+ &mut self, confirmables: &Vec<C>, confirmed_txs: Vec<ConfirmedTx>,
+ ) where
+ C::Target: Confirm,
+ {
+ for ctx in confirmed_txs {
+ for c in confirmables {
+ c.transactions_confirmed(
+ &ctx.block_header,
+ &[(ctx.pos, &ctx.tx)],
+ ctx.block_height,
+ );
+ }
+
+ self.watched_transactions.remove(&ctx.tx.txid());
+
+ for input in &ctx.tx.input {
+ if let Some(output) = self.watched_outputs.remove(&input.previous_output) {
+ let spent = (ctx.tx.txid(), ctx.block_height, input.previous_output, output);
+ self.outputs_spends_pending_threshold_conf.push(spent);
+ }
+ }
+ }
+ }
+
+ pub fn prune_output_spends(&mut self, cur_height: u32) {
+ self.outputs_spends_pending_threshold_conf
+ .retain(|(_, conf_height, _, _)| cur_height < conf_height + ANTI_REORG_DELAY - 1);
+ }
+}
// A queue that is to be filled by `Filter` and drained during the next syncing round.
pub(crate) struct FilterQueue {
impl FilterQueue {
pub fn new() -> Self {
- Self {
- transactions: HashSet::new(),
- outputs: HashMap::new(),
- }
+ Self { transactions: HashSet::new(), outputs: HashMap::new() }
}
// Processes the transaction and output queues and adds them to the given [`SyncState`].
}
}
+#[derive(Debug)]
pub(crate) struct ConfirmedTx {
pub tx: Transaction,
+ pub txid: Txid,
pub block_header: Header,
pub block_height: u32,
pub pos: usize,