//! ChannelMonitors should do so). Thus, if you're building rust-lightning into an HSM or other
//! security-domain-separated system design, you should consider having multiple paths for
//! ChannelMonitors to get out of the HSM and onto monitoring devices.
-//!
-//! [`chain::Watch`]: ../trait.Watch.html
use bitcoin::blockdata::block::{Block, BlockHeader};
use bitcoin::blockdata::transaction::{TxOut,Transaction};
use bitcoin::secp256k1::key::{SecretKey,PublicKey};
use bitcoin::secp256k1;
+use ln::{PaymentHash, PaymentPreimage};
use ln::msgs::DecodeError;
use ln::chan_utils;
use ln::chan_utils::{CounterpartyCommitmentSecrets, HTLCOutputInCommitment, HTLCType, ChannelTransactionParameters, HolderCommitmentTransaction};
-use ln::channelmanager::{HTLCSource, PaymentPreimage, PaymentHash};
+use ln::channelmanager::{BestBlock, HTLCSource};
use ln::onchaintx::{OnchainTxHandler, InputDescriptors};
use chain;
+use chain::WatchedOutput;
use chain::chaininterface::{BroadcasterInterface, FeeEstimator};
use chain::transaction::{OutPoint, TransactionData};
use chain::keysinterface::{SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, Sign, KeysInterface};
+use chain::Filter;
use util::logger::Logger;
use util::ser::{Readable, ReadableArgs, MaybeReadable, Writer, Writeable, U48};
use util::byte_utils;
use util::events::Event;
-use std::collections::{HashMap, HashSet, hash_map};
+use std::collections::{HashMap, HashSet};
use std::{cmp, mem};
use std::io::Error;
use std::ops::Deref;
/// The only instance where update_id values are not strictly increasing is the case where we
/// allow post-force-close updates with a special update ID of [`CLOSED_CHANNEL_UPDATE_ID`]. See
/// its docs for more details.
- ///
- /// [`CLOSED_CHANNEL_UPDATE_ID`]: constant.CLOSED_CHANNEL_UPDATE_ID.html
pub update_id: u64,
}
/// Simple structure sent back by `chain::Watch` when an HTLC from a forward channel is detected on
/// chain. Used to update the corresponding HTLC in the backward channel. Failing to pass the
/// preimage claim backward will lead to loss of funds.
-///
-/// [`chain::Watch`]: ../trait.Watch.html
#[derive(Clone, PartialEq)]
pub struct HTLCUpdate {
pub(crate) payment_hash: PaymentHash,
/// HTLC-Success transaction.
/// In other words, this is an upper bound on how many blocks we think it can take us to get a
/// transaction confirmed (and we use it in a few more, equivalent, places).
-pub(crate) const CLTV_CLAIM_BUFFER: u32 = 6;
+pub(crate) const CLTV_CLAIM_BUFFER: u32 = 18;
/// Number of blocks by which point we expect our counterparty to have seen new blocks on the
/// network and done a full update_fail_htlc/commitment_signed dance (+ we've updated all our
/// copies of ChannelMonitors, including watchtowers). We could enforce the contract by failing
pub(crate) witness_data: InputMaterial
}
+/// An entry for an [`OnchainEvent`], stating the block height when the event was observed and the
+/// transaction causing it.
+///
+/// Used to determine when the on-chain event can be considered safe from a chain reorganization.
+#[derive(PartialEq)]
+struct OnchainEventEntry {
+ txid: Txid,
+ height: u32,
+ event: OnchainEvent,
+}
+
+impl OnchainEventEntry {
+ fn confirmation_threshold(&self) -> u32 {
+ self.height + ANTI_REORG_DELAY - 1
+ }
+
+ fn has_reached_confirmation_threshold(&self, height: u32) -> bool {
+ height >= self.confirmation_threshold()
+ }
+}
+
/// Upon discovering of some classes of onchain tx by ChannelMonitor, we may have to take actions on it
/// once they mature to enough confirmations (ANTI_REORG_DELAY)
-#[derive(Clone, PartialEq)]
+#[derive(PartialEq)]
enum OnchainEvent {
/// HTLC output getting solved by a timeout, at maturation we pass upstream payment source information to solve
/// inbound HTLC in backward channel. Note, in case of preimage, we pass info to upstream without delay as we can
pending_monitor_events: Vec<MonitorEvent>,
pending_events: Vec<Event>,
- // Used to track onchain events, i.e transactions parts of channels confirmed on chain, on which
- // we have to take actions once they reach enough confs. Key is a block height timer, i.e we enforce
- // actions when we receive a block with given height. Actions depend on OnchainEvent type.
- onchain_events_waiting_threshold_conf: HashMap<u32, Vec<OnchainEvent>>,
+ // Used to track on-chain events (i.e., transactions part of channels confirmed on chain) on
+ // which to take actions once they reach enough confirmations. Each entry includes the
+ // transaction's id and the height when the transaction was confirmed on chain.
+ onchain_events_awaiting_threshold_conf: Vec<OnchainEventEntry>,
// If we get serialized out and re-read, we need to make sure that the chain monitoring
// interface knows about the TXOs that we want to be notified of spends of. We could probably
// remote monitor out-of-order with regards to the block view.
holder_tx_signed: bool,
- // We simply modify last_block_hash in Channel's block_connected so that serialization is
+ // We simply modify best_block in Channel's block_connected so that serialization is
// consistent but hopefully the users' copy handles block_connected in a consistent way.
// (we do *not*, however, update them in update_monitor to ensure any local user copies keep
- // their last_block_hash from its state and not based on updated copies that didn't run through
+ // their best_block from its state and not based on updated copies that didn't run through
// the full block_connected).
- last_block_hash: BlockHash,
+ best_block: BestBlock,
+
secp_ctx: Secp256k1<secp256k1::All>, //TODO: dedup this a bit...
}
+/// Transaction outputs to watch for on-chain spends.
+pub type TransactionOutputs = (Txid, Vec<(u32, TxOut)>);
+
#[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))]
/// Used only in testing and fuzztarget to check serialization roundtrips don't change the
/// underlying object
self.payment_preimages != other.payment_preimages ||
self.pending_monitor_events != other.pending_monitor_events ||
self.pending_events.len() != other.pending_events.len() || // We trust events to round-trip properly
- self.onchain_events_waiting_threshold_conf != other.onchain_events_waiting_threshold_conf ||
+ self.onchain_events_awaiting_threshold_conf != other.onchain_events_awaiting_threshold_conf ||
self.outputs_to_watch != other.outputs_to_watch ||
self.lockdown_from_offchain != other.lockdown_from_offchain ||
self.holder_tx_signed != other.holder_tx_signed
event.write(writer)?;
}
- self.last_block_hash.write(writer)?;
+ self.best_block.block_hash().write(writer)?;
+ writer.write_all(&byte_utils::be32_to_array(self.best_block.height()))?;
- writer.write_all(&byte_utils::be64_to_array(self.onchain_events_waiting_threshold_conf.len() as u64))?;
- for (ref target, ref events) in self.onchain_events_waiting_threshold_conf.iter() {
- writer.write_all(&byte_utils::be32_to_array(**target))?;
- writer.write_all(&byte_utils::be64_to_array(events.len() as u64))?;
- for ev in events.iter() {
- match *ev {
- OnchainEvent::HTLCUpdate { ref htlc_update } => {
- 0u8.write(writer)?;
- htlc_update.0.write(writer)?;
- htlc_update.1.write(writer)?;
- },
- OnchainEvent::MaturingOutput { ref descriptor } => {
- 1u8.write(writer)?;
- descriptor.write(writer)?;
- },
- }
+ writer.write_all(&byte_utils::be64_to_array(self.onchain_events_awaiting_threshold_conf.len() as u64))?;
+ for ref entry in self.onchain_events_awaiting_threshold_conf.iter() {
+ entry.txid.write(writer)?;
+ writer.write_all(&byte_utils::be32_to_array(entry.height))?;
+ match entry.event {
+ OnchainEvent::HTLCUpdate { ref htlc_update } => {
+ 0u8.write(writer)?;
+ htlc_update.0.write(writer)?;
+ htlc_update.1.write(writer)?;
+ },
+ OnchainEvent::MaturingOutput { ref descriptor } => {
+ 1u8.write(writer)?;
+ descriptor.write(writer)?;
+ },
}
}
funding_redeemscript: Script, channel_value_satoshis: u64,
commitment_transaction_number_obscure_factor: u64,
initial_holder_commitment_tx: HolderCommitmentTransaction,
- last_block_hash: BlockHash) -> ChannelMonitor<Signer> {
+ best_block: BestBlock) -> ChannelMonitor<Signer> {
assert!(commitment_transaction_number_obscure_factor <= (1 << 48));
let our_channel_close_key_hash = WPubkeyHash::hash(&shutdown_pubkey.serialize());
pending_monitor_events: Vec::new(),
pending_events: Vec::new(),
- onchain_events_waiting_threshold_conf: HashMap::new(),
+ onchain_events_awaiting_threshold_conf: Vec::new(),
outputs_to_watch,
onchain_tx_handler,
lockdown_from_offchain: false,
holder_tx_signed: false,
- last_block_hash,
+ best_block,
+
secp_ctx,
}),
}
/// Gets a list of txids, with their output scripts (in the order they appear in the
/// transaction), which we must learn about spends of via block_connected().
- ///
- /// (C-not exported) because we have no HashMap bindings
- pub fn get_outputs_to_watch(&self) -> HashMap<Txid, Vec<(u32, Script)>> {
- self.inner.lock().unwrap().get_outputs_to_watch().clone()
+ pub fn get_outputs_to_watch(&self) -> Vec<(Txid, Vec<(u32, Script)>)> {
+ self.inner.lock().unwrap().get_outputs_to_watch()
+ .iter().map(|(txid, outputs)| (*txid, outputs.clone())).collect()
+ }
+
+ /// Loads the funding txo and outputs to watch into the given `chain::Filter` by repeatedly
+ /// calling `chain::Filter::register_output` and `chain::Filter::register_tx` until all outputs
+ /// have been registered.
+ pub fn load_outputs_to_watch<F: Deref>(&self, filter: &F) where F::Target: chain::Filter {
+ let lock = self.inner.lock().unwrap();
+ filter.register_tx(&lock.get_funding_txo().0.txid, &lock.get_funding_txo().1);
+ for (txid, outputs) in lock.get_outputs_to_watch().iter() {
+ for (index, script_pubkey) in outputs.iter() {
+ assert!(*index <= u16::max_value() as u32);
+ filter.register_output(WatchedOutput {
+ block_hash: None,
+ outpoint: OutPoint { txid: *txid, index: *index as u16 },
+ script_pubkey: script_pubkey.clone(),
+ });
+ }
+ }
}
/// Get the list of HTLCs who's status has been updated on chain. This should be called by
/// ChannelManager via [`chain::Watch::release_pending_monitor_events`].
- ///
- /// [`chain::Watch::release_pending_monitor_events`]: ../trait.Watch.html#tymethod.release_pending_monitor_events
pub fn get_and_clear_pending_monitor_events(&self) -> Vec<MonitorEvent> {
self.inner.lock().unwrap().get_and_clear_pending_monitor_events()
}
broadcaster: B,
fee_estimator: F,
logger: L,
- ) -> Vec<(Txid, Vec<(u32, TxOut)>)>
+ ) -> Vec<TransactionOutputs>
where
B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
self.inner.lock().unwrap().block_disconnected(
header, height, broadcaster, fee_estimator, logger)
}
+
+ /// Processes transactions confirmed in a block with the given header and height, returning new
+ /// outputs to watch. See [`block_connected`] for details.
+ ///
+ /// Used instead of [`block_connected`] by clients that are notified of transactions rather than
+ /// blocks. See [`chain::Confirm`] for calling expectations.
+ ///
+ /// [`block_connected`]: Self::block_connected
+ pub fn transactions_confirmed<B: Deref, F: Deref, L: Deref>(
+ &self,
+ header: &BlockHeader,
+ txdata: &TransactionData,
+ height: u32,
+ broadcaster: B,
+ fee_estimator: F,
+ logger: L,
+ ) -> Vec<TransactionOutputs>
+ where
+ B::Target: BroadcasterInterface,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
+ {
+ self.inner.lock().unwrap().transactions_confirmed(
+ header, txdata, height, broadcaster, fee_estimator, logger)
+ }
+
+ /// Processes a transaction that was reorganized out of the chain.
+ ///
+ /// Used instead of [`block_disconnected`] by clients that are notified of transactions rather
+ /// than blocks. See [`chain::Confirm`] for calling expectations.
+ ///
+ /// [`block_disconnected`]: Self::block_disconnected
+ pub fn transaction_unconfirmed<B: Deref, F: Deref, L: Deref>(
+ &self,
+ txid: &Txid,
+ broadcaster: B,
+ fee_estimator: F,
+ logger: L,
+ ) where
+ B::Target: BroadcasterInterface,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
+ {
+ self.inner.lock().unwrap().transaction_unconfirmed(
+ txid, broadcaster, fee_estimator, logger);
+ }
+
+ /// Updates the monitor with the current best chain tip, returning new outputs to watch. See
+ /// [`block_connected`] for details.
+ ///
+ /// Used instead of [`block_connected`] by clients that are notified of transactions rather than
+ /// blocks. See [`chain::Confirm`] for calling expectations.
+ ///
+ /// [`block_connected`]: Self::block_connected
+ pub fn best_block_updated<B: Deref, F: Deref, L: Deref>(
+ &self,
+ header: &BlockHeader,
+ height: u32,
+ broadcaster: B,
+ fee_estimator: F,
+ logger: L,
+ ) -> Vec<TransactionOutputs>
+ where
+ B::Target: BroadcasterInterface,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
+ {
+ self.inner.lock().unwrap().best_block_updated(
+ header, height, broadcaster, fee_estimator, logger)
+ }
+
+ /// Returns the set of txids that should be monitored for re-organization out of the chain.
+ pub fn get_relevant_txids(&self) -> Vec<Txid> {
+ let inner = self.inner.lock().unwrap();
+ let mut txids: Vec<Txid> = inner.onchain_events_awaiting_threshold_conf
+ .iter()
+ .map(|entry| entry.txid)
+ .chain(inner.onchain_tx_handler.get_relevant_txids().into_iter())
+ .collect();
+ txids.sort_unstable();
+ txids.dedup();
+ txids
+ }
}
impl<Signer: Sign> ChannelMonitorImpl<Signer> {
L::Target: Logger,
{
for tx in self.get_latest_holder_commitment_txn(logger).iter() {
+ log_info!(logger, "Broadcasting local {}", log_tx!(tx));
broadcaster.broadcast_transaction(tx);
}
self.pending_monitor_events.push(MonitorEvent::CommitmentTxBroadcasted(self.funding_info.0));
self.lockdown_from_offchain = true;
if *should_broadcast {
self.broadcast_latest_holder_commitment_txn(broadcaster, logger);
- } else {
+ } else if !self.holder_tx_signed {
log_error!(logger, "You have a toxic holder commitment transaction avaible in channel monitor, read comment in ChannelMonitor::get_latest_holder_commitment_txn to be informed of manual action to take");
+ } else {
+ // If we generated a MonitorEvent::CommitmentTxBroadcasted, the ChannelManager
+ // will still give us a ChannelForceClosed event with !should_broadcast, but we
+ // shouldn't print the scary warning above.
+ log_info!(logger, "Channel off-chain state closed after we broadcasted our latest commitment transaction.");
}
}
}
/// HTLC-Success/HTLC-Timeout transactions.
/// Return updates for HTLC pending in the channel and failed automatically by the broadcast of
/// revoked counterparty commitment tx
- fn check_spend_counterparty_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) -> (Vec<ClaimRequest>, (Txid, Vec<(u32, TxOut)>)) where L::Target: Logger {
+ fn check_spend_counterparty_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) -> (Vec<ClaimRequest>, TransactionOutputs) where L::Target: Logger {
// Most secp and related errors trying to create keys means we have no hope of constructing
// a spend transaction...so we return no transactions to broadcast
let mut claimable_outpoints = Vec::new();
if let Some(ref outpoints) = self.counterparty_claimable_outpoints.get($txid) {
for &(ref htlc, ref source_option) in outpoints.iter() {
if let &Some(ref source) = source_option {
- log_info!(logger, "Failing HTLC with payment_hash {} from {} counterparty commitment tx due to broadcast of revoked counterparty commitment transaction, waiting for confirmation (at height {})", log_bytes!(htlc.payment_hash.0), $commitment_tx, height + ANTI_REORG_DELAY - 1);
- match self.onchain_events_waiting_threshold_conf.entry(height + ANTI_REORG_DELAY - 1) {
- hash_map::Entry::Occupied(mut entry) => {
- let e = entry.get_mut();
- e.retain(|ref event| {
- match **event {
- OnchainEvent::HTLCUpdate { ref htlc_update } => {
- return htlc_update.0 != **source
- },
- _ => true
- }
- });
- e.push(OnchainEvent::HTLCUpdate { htlc_update: ((**source).clone(), htlc.payment_hash.clone())});
+ self.onchain_events_awaiting_threshold_conf.retain(|ref entry| {
+ if entry.height != height { return true; }
+ match entry.event {
+ OnchainEvent::HTLCUpdate { ref htlc_update } => {
+ htlc_update.0 != **source
+ },
+ _ => true,
}
- hash_map::Entry::Vacant(entry) => {
- entry.insert(vec![OnchainEvent::HTLCUpdate { htlc_update: ((**source).clone(), htlc.payment_hash.clone())}]);
- }
- }
+ });
+ let entry = OnchainEventEntry {
+ txid: *$txid,
+ height,
+ event: OnchainEvent::HTLCUpdate {
+ htlc_update: ((**source).clone(), htlc.payment_hash.clone())
+ },
+ };
+ log_info!(logger, "Failing HTLC with payment_hash {} from {} counterparty commitment tx due to broadcast of revoked counterparty commitment transaction, waiting for confirmation (at height {})", log_bytes!(htlc.payment_hash.0), $commitment_tx, entry.confirmation_threshold());
+ self.onchain_events_awaiting_threshold_conf.push(entry);
}
}
}
}
}
log_trace!(logger, "Failing HTLC with payment_hash {} from {} counterparty commitment tx due to broadcast of counterparty commitment transaction", log_bytes!(htlc.payment_hash.0), $commitment_tx);
- match self.onchain_events_waiting_threshold_conf.entry(height + ANTI_REORG_DELAY - 1) {
- hash_map::Entry::Occupied(mut entry) => {
- let e = entry.get_mut();
- e.retain(|ref event| {
- match **event {
- OnchainEvent::HTLCUpdate { ref htlc_update } => {
- return htlc_update.0 != **source
- },
- _ => true
- }
- });
- e.push(OnchainEvent::HTLCUpdate { htlc_update: ((**source).clone(), htlc.payment_hash.clone())});
- }
- hash_map::Entry::Vacant(entry) => {
- entry.insert(vec![OnchainEvent::HTLCUpdate { htlc_update: ((**source).clone(), htlc.payment_hash.clone())}]);
+ self.onchain_events_awaiting_threshold_conf.retain(|ref entry| {
+ if entry.height != height { return true; }
+ match entry.event {
+ OnchainEvent::HTLCUpdate { ref htlc_update } => {
+ htlc_update.0 != **source
+ },
+ _ => true,
}
- }
+ });
+ self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
+ txid: *$txid,
+ height,
+ event: OnchainEvent::HTLCUpdate {
+ htlc_update: ((**source).clone(), htlc.payment_hash.clone())
+ },
+ });
}
}
}
}
/// Attempts to claim a counterparty HTLC-Success/HTLC-Timeout's outputs using the revocation key
- fn check_spend_counterparty_htlc<L: Deref>(&mut self, tx: &Transaction, commitment_number: u64, height: u32, logger: &L) -> (Vec<ClaimRequest>, Option<(Txid, Vec<(u32, TxOut)>)>) where L::Target: Logger {
+ fn check_spend_counterparty_htlc<L: Deref>(&mut self, tx: &Transaction, commitment_number: u64, height: u32, logger: &L) -> (Vec<ClaimRequest>, Option<TransactionOutputs>) where L::Target: Logger {
let htlc_txid = tx.txid();
if tx.input.len() != 1 || tx.output.len() != 1 || tx.input[0].witness.len() != 5 {
return (Vec::new(), None)
/// Attempts to claim any claimable HTLCs in a commitment transaction which was not (yet)
/// revoked using data in holder_claimable_outpoints.
/// Should not be used if check_spend_revoked_transaction succeeds.
- fn check_spend_holder_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) -> (Vec<ClaimRequest>, (Txid, Vec<(u32, TxOut)>)) where L::Target: Logger {
+ fn check_spend_holder_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) -> (Vec<ClaimRequest>, TransactionOutputs) where L::Target: Logger {
let commitment_txid = tx.txid();
let mut claim_requests = Vec::new();
let mut watch_outputs = Vec::new();
macro_rules! wait_threshold_conf {
- ($height: expr, $source: expr, $commitment_tx: expr, $payment_hash: expr) => {
- log_trace!(logger, "Failing HTLC with payment_hash {} from {} holder commitment tx due to broadcast of transaction, waiting confirmation (at height{})", log_bytes!($payment_hash.0), $commitment_tx, height + ANTI_REORG_DELAY - 1);
- match self.onchain_events_waiting_threshold_conf.entry($height + ANTI_REORG_DELAY - 1) {
- hash_map::Entry::Occupied(mut entry) => {
- let e = entry.get_mut();
- e.retain(|ref event| {
- match **event {
- OnchainEvent::HTLCUpdate { ref htlc_update } => {
- return htlc_update.0 != $source
- },
- _ => true
- }
- });
- e.push(OnchainEvent::HTLCUpdate { htlc_update: ($source, $payment_hash)});
+ ($source: expr, $commitment_tx: expr, $payment_hash: expr) => {
+ self.onchain_events_awaiting_threshold_conf.retain(|ref entry| {
+ if entry.height != height { return true; }
+ match entry.event {
+ OnchainEvent::HTLCUpdate { ref htlc_update } => {
+ htlc_update.0 != $source
+ },
+ _ => true,
}
- hash_map::Entry::Vacant(entry) => {
- entry.insert(vec![OnchainEvent::HTLCUpdate { htlc_update: ($source, $payment_hash)}]);
- }
- }
+ });
+ let entry = OnchainEventEntry {
+ txid: commitment_txid,
+ height,
+ event: OnchainEvent::HTLCUpdate { htlc_update: ($source, $payment_hash) },
+ };
+ log_trace!(logger, "Failing HTLC with payment_hash {} from {} holder commitment tx due to broadcast of transaction, waiting confirmation (at height{})", log_bytes!($payment_hash.0), $commitment_tx, entry.confirmation_threshold());
+ self.onchain_events_awaiting_threshold_conf.push(entry);
}
}
for &(ref htlc, _, ref source) in &$holder_tx.htlc_outputs {
if htlc.transaction_output_index.is_none() {
if let &Some(ref source) = source {
- wait_threshold_conf!(height, source.clone(), "lastest", htlc.payment_hash.clone());
+ wait_threshold_conf!(source.clone(), "lastest", htlc.payment_hash.clone());
}
}
}
return res
}
- pub fn block_connected<B: Deref, F: Deref, L: Deref>(&mut self, header: &BlockHeader, txdata: &TransactionData, height: u32, broadcaster: B, fee_estimator: F, logger: L)-> Vec<(Txid, Vec<(u32, TxOut)>)>
+ pub fn block_connected<B: Deref, F: Deref, L: Deref>(&mut self, header: &BlockHeader, txdata: &TransactionData, height: u32, broadcaster: B, fee_estimator: F, logger: L) -> Vec<TransactionOutputs>
where B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
L::Target: Logger,
+ {
+ let block_hash = header.block_hash();
+ log_trace!(logger, "New best block {} at height {}", block_hash, height);
+ self.best_block = BestBlock::new(block_hash, height);
+
+ self.transactions_confirmed(header, txdata, height, broadcaster, fee_estimator, logger)
+ }
+
+ fn best_block_updated<B: Deref, F: Deref, L: Deref>(
+ &mut self,
+ header: &BlockHeader,
+ height: u32,
+ broadcaster: B,
+ fee_estimator: F,
+ logger: L,
+ ) -> Vec<TransactionOutputs>
+ where
+ B::Target: BroadcasterInterface,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
+ {
+ let block_hash = header.block_hash();
+ log_trace!(logger, "New best block {} at height {}", block_hash, height);
+
+ 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)
+ } else {
+ self.best_block = BestBlock::new(block_hash, height);
+ self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.height <= height);
+ self.onchain_tx_handler.block_disconnected(height + 1, broadcaster, fee_estimator, logger);
+ Vec::new()
+ }
+ }
+
+ fn transactions_confirmed<B: Deref, F: Deref, L: Deref>(
+ &mut self,
+ header: &BlockHeader,
+ txdata: &TransactionData,
+ height: u32,
+ broadcaster: B,
+ fee_estimator: F,
+ logger: L,
+ ) -> Vec<TransactionOutputs>
+ where
+ B::Target: BroadcasterInterface,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
{
let txn_matched = self.filter_block(txdata);
for tx in &txn_matched {
self.is_paying_spendable_output(&tx, height, &logger);
}
+
+ self.block_confirmed(height, txn_matched, watch_outputs, claimable_outpoints, broadcaster, fee_estimator, logger)
+ }
+
+ fn block_confirmed<B: Deref, F: Deref, L: Deref>(
+ &mut self,
+ height: u32,
+ txn_matched: Vec<&Transaction>,
+ mut watch_outputs: Vec<TransactionOutputs>,
+ mut claimable_outpoints: Vec<ClaimRequest>,
+ broadcaster: B,
+ fee_estimator: F,
+ logger: L,
+ ) -> Vec<TransactionOutputs>
+ where
+ B::Target: BroadcasterInterface,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
+ {
let should_broadcast = self.would_broadcast_at_height(height, &logger);
if should_broadcast {
claimable_outpoints.push(ClaimRequest { absolute_timelock: height, aggregable: false, outpoint: BitcoinOutPoint { txid: self.funding_info.0.txid.clone(), vout: self.funding_info.0.index as u32 }, witness_data: InputMaterial::Funding { funding_redeemscript: self.funding_redeemscript.clone() }});
- }
- if should_broadcast {
self.pending_monitor_events.push(MonitorEvent::CommitmentTxBroadcasted(self.funding_info.0));
let commitment_tx = self.onchain_tx_handler.get_fully_signed_holder_tx(&self.funding_redeemscript);
self.holder_tx_signed = true;
}
claimable_outpoints.append(&mut new_outpoints);
}
- if let Some(events) = self.onchain_events_waiting_threshold_conf.remove(&height) {
- for ev in events {
- match ev {
- OnchainEvent::HTLCUpdate { htlc_update } => {
- log_trace!(logger, "HTLC {} failure update has got enough confirmations to be passed upstream", log_bytes!((htlc_update.1).0));
- self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate {
- payment_hash: htlc_update.1,
- payment_preimage: None,
- source: htlc_update.0,
- }));
- },
- OnchainEvent::MaturingOutput { descriptor } => {
- log_trace!(logger, "Descriptor {} has got enough confirmations to be passed upstream", log_spendable!(descriptor));
- self.pending_events.push(Event::SpendableOutputs {
- outputs: vec![descriptor]
- });
+
+ // Find which on-chain events have reached their confirmation threshold.
+ let onchain_events_awaiting_threshold_conf =
+ self.onchain_events_awaiting_threshold_conf.drain(..).collect::<Vec<_>>();
+ let mut onchain_events_reaching_threshold_conf = Vec::new();
+ for entry in onchain_events_awaiting_threshold_conf {
+ if entry.has_reached_confirmation_threshold(height) {
+ onchain_events_reaching_threshold_conf.push(entry);
+ } else {
+ self.onchain_events_awaiting_threshold_conf.push(entry);
+ }
+ }
+
+ // Used to check for duplicate HTLC resolutions.
+ #[cfg(debug_assertions)]
+ let unmatured_htlcs: Vec<_> = self.onchain_events_awaiting_threshold_conf
+ .iter()
+ .filter_map(|entry| match &entry.event {
+ OnchainEvent::HTLCUpdate { htlc_update } => Some(htlc_update.0.clone()),
+ OnchainEvent::MaturingOutput { .. } => None,
+ })
+ .collect();
+ #[cfg(debug_assertions)]
+ let mut matured_htlcs = Vec::new();
+
+ // Produce actionable events from on-chain events having reached their threshold.
+ for entry in onchain_events_reaching_threshold_conf.drain(..) {
+ match entry.event {
+ OnchainEvent::HTLCUpdate { htlc_update } => {
+ // Check for duplicate HTLC resolutions.
+ #[cfg(debug_assertions)]
+ {
+ debug_assert!(
+ unmatured_htlcs.iter().find(|&htlc| htlc == &htlc_update.0).is_none(),
+ "An unmature HTLC transaction conflicts with a maturing one; failed to \
+ call either transaction_unconfirmed for the conflicting transaction \
+ or block_disconnected for a block containing it.");
+ debug_assert!(
+ matured_htlcs.iter().find(|&htlc| htlc == &htlc_update.0).is_none(),
+ "A matured HTLC transaction conflicts with a maturing one; failed to \
+ call either transaction_unconfirmed for the conflicting transaction \
+ or block_disconnected for a block containing it.");
+ matured_htlcs.push(htlc_update.0.clone());
}
+
+ log_trace!(logger, "HTLC {} failure update has got enough confirmations to be passed upstream", log_bytes!((htlc_update.1).0));
+ self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate {
+ payment_hash: htlc_update.1,
+ payment_preimage: None,
+ source: htlc_update.0,
+ }));
+ },
+ OnchainEvent::MaturingOutput { descriptor } => {
+ log_trace!(logger, "Descriptor {} has got enough confirmations to be passed upstream", log_spendable!(descriptor));
+ self.pending_events.push(Event::SpendableOutputs {
+ outputs: vec![descriptor]
+ });
}
}
}
self.onchain_tx_handler.update_claims_view(&txn_matched, claimable_outpoints, Some(height), &&*broadcaster, &&*fee_estimator, &&*logger);
- self.last_block_hash = block_hash;
// Determine new outputs to watch by comparing against previously known outputs to watch,
// updating the latter in the process.
{
log_trace!(logger, "Block {} at height {} disconnected", header.block_hash(), height);
- if let Some(_) = self.onchain_events_waiting_threshold_conf.remove(&(height + ANTI_REORG_DELAY - 1)) {
- //We may discard:
- //- htlc update there as failure-trigger tx (revoked commitment tx, non-revoked commitment tx, HTLC-timeout tx) has been disconnected
- //- maturing spendable output has transaction paying us has been disconnected
- }
+ //We may discard:
+ //- htlc update there as failure-trigger tx (revoked commitment tx, non-revoked commitment tx, HTLC-timeout tx) has been disconnected
+ //- maturing spendable output has transaction paying us has been disconnected
+ self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.height < height);
self.onchain_tx_handler.block_disconnected(height, broadcaster, fee_estimator, logger);
- self.last_block_hash = header.prev_blockhash;
+ self.best_block = BestBlock::new(header.prev_blockhash, height - 1);
+ }
+
+ fn transaction_unconfirmed<B: Deref, F: Deref, L: Deref>(
+ &mut self,
+ txid: &Txid,
+ broadcaster: B,
+ fee_estimator: F,
+ logger: L,
+ ) where
+ B::Target: BroadcasterInterface,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
+ {
+ self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.txid != *txid);
+ self.onchain_tx_handler.transaction_unconfirmed(txid, broadcaster, fee_estimator, logger);
}
/// Filters a block's `txdata` for transactions spending watched outputs or for any child
}));
}
} else {
- log_info!(logger, "Failing HTLC with payment_hash {} timeout by a spend tx, waiting for confirmation (at height{})", log_bytes!(payment_hash.0), height + ANTI_REORG_DELAY - 1);
- match self.onchain_events_waiting_threshold_conf.entry(height + ANTI_REORG_DELAY - 1) {
- hash_map::Entry::Occupied(mut entry) => {
- let e = entry.get_mut();
- e.retain(|ref event| {
- match **event {
- OnchainEvent::HTLCUpdate { ref htlc_update } => {
- return htlc_update.0 != source
- },
- _ => true
- }
- });
- e.push(OnchainEvent::HTLCUpdate { htlc_update: (source, payment_hash)});
- }
- hash_map::Entry::Vacant(entry) => {
- entry.insert(vec![OnchainEvent::HTLCUpdate { htlc_update: (source, payment_hash)}]);
+ self.onchain_events_awaiting_threshold_conf.retain(|ref entry| {
+ if entry.height != height { return true; }
+ match entry.event {
+ OnchainEvent::HTLCUpdate { ref htlc_update } => {
+ htlc_update.0 != source
+ },
+ _ => true,
}
- }
+ });
+ let entry = OnchainEventEntry {
+ txid: tx.txid(),
+ height,
+ event: OnchainEvent::HTLCUpdate { htlc_update: (source, payment_hash) },
+ };
+ log_info!(logger, "Failing HTLC with payment_hash {} timeout by a spend tx, waiting for confirmation (at height{})", log_bytes!(payment_hash.0), entry.confirmation_threshold());
+ self.onchain_events_awaiting_threshold_conf.push(entry);
}
}
}
}
}
if let Some(spendable_output) = spendable_output {
- log_trace!(logger, "Maturing {} until {}", log_spendable!(spendable_output), height + ANTI_REORG_DELAY - 1);
- match self.onchain_events_waiting_threshold_conf.entry(height + ANTI_REORG_DELAY - 1) {
- hash_map::Entry::Occupied(mut entry) => {
- let e = entry.get_mut();
- e.push(OnchainEvent::MaturingOutput { descriptor: spendable_output });
- }
- hash_map::Entry::Vacant(entry) => {
- entry.insert(vec![OnchainEvent::MaturingOutput { descriptor: spendable_output }]);
- }
- }
+ let entry = OnchainEventEntry {
+ txid: tx.txid(),
+ height: height,
+ event: OnchainEvent::MaturingOutput { descriptor: spendable_output.clone() },
+ };
+ log_trace!(logger, "Maturing {} until {}", log_spendable!(spendable_output), entry.confirmation_threshold());
+ self.onchain_events_awaiting_threshold_conf.push(entry);
}
}
}
/// transaction and losing money. This is a risk because previous channel states
/// are toxic, so it's important that whatever channel state is persisted is
/// kept up-to-date.
-pub trait Persist<ChannelSigner: Sign>: Send + Sync {
+pub trait Persist<ChannelSigner: Sign> {
/// Persist a new channel's data. The data can be stored any way you want, but
/// the identifier provided by Rust-Lightning is the channel's outpoint (and
/// it is up to you to maintain a correct mapping between the outpoint and the
/// stored channel data). Note that you **must** persist every new monitor to
/// disk. See the `Persist` trait documentation for more details.
///
- /// See [`ChannelMonitor::serialize_for_disk`] for writing out a `ChannelMonitor`,
+ /// See [`ChannelMonitor::write`] for writing out a `ChannelMonitor`,
/// and [`ChannelMonitorUpdateErr`] for requirements when returning errors.
- ///
- /// [`ChannelMonitor::serialize_for_disk`]: struct.ChannelMonitor.html#method.serialize_for_disk
- /// [`ChannelMonitorUpdateErr`]: enum.ChannelMonitorUpdateErr.html
fn persist_new_channel(&self, id: OutPoint, data: &ChannelMonitor<ChannelSigner>) -> Result<(), ChannelMonitorUpdateErr>;
/// Update one channel's data. The provided `ChannelMonitor` has already
/// them in batches. The size of each monitor grows `O(number of state updates)`
/// whereas updates are small and `O(1)`.
///
- /// See [`ChannelMonitor::serialize_for_disk`] for writing out a `ChannelMonitor`,
+ /// See [`ChannelMonitor::write`] for writing out a `ChannelMonitor`,
/// [`ChannelMonitorUpdate::write`] for writing out an update, and
/// [`ChannelMonitorUpdateErr`] for requirements when returning errors.
- ///
- /// [`ChannelMonitor::update_monitor`]: struct.ChannelMonitor.html#impl-1
- /// [`ChannelMonitor::serialize_for_disk`]: struct.ChannelMonitor.html#method.serialize_for_disk
- /// [`ChannelMonitorUpdate::write`]: struct.ChannelMonitorUpdate.html#method.write
- /// [`ChannelMonitorUpdateErr`]: enum.ChannelMonitorUpdateErr.html
fn update_persisted_channel(&self, id: OutPoint, update: &ChannelMonitorUpdate, data: &ChannelMonitor<ChannelSigner>) -> Result<(), ChannelMonitorUpdateErr>;
}
}
}
+impl<Signer: Sign, T: Deref, F: Deref, L: Deref> chain::Confirm for (ChannelMonitor<Signer>, T, F, L)
+where
+ T::Target: BroadcasterInterface,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
+{
+ fn transactions_confirmed(&self, header: &BlockHeader, txdata: &TransactionData, height: u32) {
+ self.0.transactions_confirmed(header, txdata, height, &*self.1, &*self.2, &*self.3);
+ }
+
+ fn transaction_unconfirmed(&self, txid: &Txid) {
+ self.0.transaction_unconfirmed(txid, &*self.1, &*self.2, &*self.3);
+ }
+
+ fn best_block_updated(&self, header: &BlockHeader, height: u32) {
+ self.0.best_block_updated(header, height, &*self.1, &*self.2, &*self.3);
+ }
+
+ fn get_relevant_txids(&self) -> Vec<Txid> {
+ self.0.get_relevant_txids()
+ }
+}
+
const MAX_ALLOC_SIZE: usize = 64*1024;
impl<'a, Signer: Sign, K: KeysInterface<Signer = Signer>> ReadableArgs<&'a K>
}
}
- let last_block_hash: BlockHash = Readable::read(reader)?;
+ let best_block = BestBlock::new(Readable::read(reader)?, Readable::read(reader)?);
let waiting_threshold_conf_len: u64 = Readable::read(reader)?;
- let mut onchain_events_waiting_threshold_conf = HashMap::with_capacity(cmp::min(waiting_threshold_conf_len as usize, MAX_ALLOC_SIZE / 128));
+ let mut onchain_events_awaiting_threshold_conf = Vec::with_capacity(cmp::min(waiting_threshold_conf_len as usize, MAX_ALLOC_SIZE / 128));
for _ in 0..waiting_threshold_conf_len {
- let height_target = Readable::read(reader)?;
- let events_len: u64 = Readable::read(reader)?;
- let mut events = Vec::with_capacity(cmp::min(events_len as usize, MAX_ALLOC_SIZE / 128));
- for _ in 0..events_len {
- let ev = match <u8 as Readable>::read(reader)? {
- 0 => {
- let htlc_source = Readable::read(reader)?;
- let hash = Readable::read(reader)?;
- OnchainEvent::HTLCUpdate {
- htlc_update: (htlc_source, hash)
- }
- },
- 1 => {
- let descriptor = Readable::read(reader)?;
- OnchainEvent::MaturingOutput {
- descriptor
- }
- },
- _ => return Err(DecodeError::InvalidValue),
- };
- events.push(ev);
- }
- onchain_events_waiting_threshold_conf.insert(height_target, events);
+ let txid = Readable::read(reader)?;
+ let height = Readable::read(reader)?;
+ let event = match <u8 as Readable>::read(reader)? {
+ 0 => {
+ let htlc_source = Readable::read(reader)?;
+ let hash = Readable::read(reader)?;
+ OnchainEvent::HTLCUpdate {
+ htlc_update: (htlc_source, hash)
+ }
+ },
+ 1 => {
+ let descriptor = Readable::read(reader)?;
+ OnchainEvent::MaturingOutput {
+ descriptor
+ }
+ },
+ _ => return Err(DecodeError::InvalidValue),
+ };
+ onchain_events_awaiting_threshold_conf.push(OnchainEventEntry { txid, height, event });
}
let outputs_to_watch_len: u64 = Readable::read(reader)?;
let mut secp_ctx = Secp256k1::new();
secp_ctx.seeded_randomize(&keys_manager.get_secure_random_bytes());
- Ok((last_block_hash.clone(), ChannelMonitor {
+ Ok((best_block.block_hash(), ChannelMonitor {
inner: Mutex::new(ChannelMonitorImpl {
latest_update_id,
commitment_transaction_number_obscure_factor,
pending_monitor_events,
pending_events,
- onchain_events_waiting_threshold_conf,
+ onchain_events_awaiting_threshold_conf,
outputs_to_watch,
onchain_tx_handler,
lockdown_from_offchain,
holder_tx_signed,
- last_block_hash,
+ best_block,
+
secp_ctx,
}),
}))
#[cfg(test)]
mod tests {
- use bitcoin::blockdata::constants::genesis_block;
use bitcoin::blockdata::script::{Script, Builder};
use bitcoin::blockdata::opcodes;
use bitcoin::blockdata::transaction::{Transaction, TxIn, TxOut, SigHashType};
use hex;
use chain::channelmonitor::ChannelMonitor;
use chain::transaction::OutPoint;
- use ln::channelmanager::{PaymentPreimage, PaymentHash};
+ use ln::{PaymentPreimage, PaymentHash};
+ use ln::channelmanager::BestBlock;
use ln::onchaintx::{OnchainTxHandler, InputDescriptors};
use ln::chan_utils;
use ln::chan_utils::{HTLCOutputInCommitment, ChannelPublicKeys, ChannelTransactionParameters, HolderCommitmentTransaction, CounterpartyChannelTransactionParameters};
};
// Prune with one old state and a holder commitment tx holding a few overlaps with the
// old state.
- let last_block_hash = genesis_block(Network::Testnet).block_hash();
+ let best_block = BestBlock::from_genesis(Network::Testnet);
let monitor = ChannelMonitor::new(Secp256k1::new(), keys,
&PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()), 0, &Script::new(),
(OutPoint { txid: Txid::from_slice(&[43; 32]).unwrap(), index: 0 }, Script::new()),
&channel_parameters,
Script::new(), 46, 0,
- HolderCommitmentTransaction::dummy(), last_block_hash);
+ HolderCommitmentTransaction::dummy(), best_block);
monitor.provide_latest_holder_commitment_tx(HolderCommitmentTransaction::dummy(), preimages_to_holder_htlcs!(preimages[0..10])).unwrap();
let dummy_txid = dummy_tx.txid();