//! 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::BlockHeader;
+use bitcoin::blockdata::block::{Block, BlockHeader};
use bitcoin::blockdata::transaction::{TxOut,Transaction};
-use bitcoin::blockdata::transaction::OutPoint as BitcoinOutPoint;
use bitcoin::blockdata::script::{Script, Builder};
use bitcoin::blockdata::opcodes;
-use bitcoin::consensus::encode;
use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256::Hash as Sha256;
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, HolderCommitmentTransaction, HTLCType};
-use ln::channelmanager::{HTLCSource, PaymentPreimage, PaymentHash};
-use ln::onchaintx::{OnchainTxHandler, InputDescriptors};
+use ln::chan_utils::{CounterpartyCommitmentSecrets, HTLCOutputInCommitment, HTLCType, ChannelTransactionParameters, HolderCommitmentTransaction};
+use ln::channelmanager::{BestBlock, HTLCSource};
+use chain;
+use chain::WatchedOutput;
use chain::chaininterface::{BroadcasterInterface, FeeEstimator};
use chain::transaction::{OutPoint, TransactionData};
-use chain::keysinterface::{SpendableOutputDescriptor, ChannelKeys};
+use chain::keysinterface::{SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, Sign, KeysInterface};
+use chain::onchaintx::OnchainTxHandler;
+use chain::package::{CounterpartyOfferedHTLCOutput, CounterpartyReceivedHTLCOutput, HolderFundingOutput, HolderHTLCOutput, PackageSolvingData, PackageTemplate, RevokedOutput, RevokedHTLCOutput};
+use chain::Filter;
use util::logger::Logger;
-use util::ser::{Readable, MaybeReadable, Writer, Writeable, U48};
+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::{cmp, mem};
-use std::ops::Deref;
+use prelude::*;
+use std::collections::{HashMap, HashSet};
+use core::{cmp, mem};
use std::io::Error;
+use core::ops::Deref;
+use std::sync::Mutex;
/// An update generated by the underlying Channel itself which contains some new information the
/// ChannelMonitor should be made aware of.
-#[cfg_attr(test, derive(PartialEq))]
+#[cfg_attr(any(test, feature = "fuzztarget", feature = "_test_utils"), derive(PartialEq))]
#[derive(Clone)]
#[must_use]
pub struct ChannelMonitorUpdate {
pub(crate) updates: Vec<ChannelMonitorUpdateStep>,
/// The sequence number of this update. Updates *must* be replayed in-order according to this
/// sequence number (and updates may panic if they are not). The update_id values are strictly
- /// increasing and increase by one for each new update.
+ /// increasing and increase by one for each new update, with one exception specified below.
///
/// This sequence number is also used to track up to which points updates which returned
/// ChannelMonitorUpdateErr::TemporaryFailure have been applied to all copies of a given
/// ChannelMonitor when ChannelManager::channel_monitor_updated is called.
+ ///
+ /// 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.
pub update_id: u64,
}
+/// If:
+/// (1) a channel has been force closed and
+/// (2) we receive a preimage from a forward link that allows us to spend an HTLC output on
+/// this channel's (the backward link's) broadcasted commitment transaction
+/// then we allow the `ChannelManager` to send a `ChannelMonitorUpdate` with this update ID,
+/// with the update providing said payment preimage. No other update types are allowed after
+/// force-close.
+pub const CLOSED_CHANNEL_UPDATE_ID: u64 = core::u64::MAX;
+
impl Writeable for ChannelMonitorUpdate {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> {
self.update_id.write(w)?;
fn read<R: ::std::io::Read>(r: &mut R) -> Result<Self, DecodeError> {
let update_id: u64 = Readable::read(r)?;
let len: u64 = Readable::read(r)?;
- let mut updates = Vec::with_capacity(cmp::min(len as usize, MAX_ALLOC_SIZE / ::std::mem::size_of::<ChannelMonitorUpdateStep>()));
+ let mut updates = Vec::with_capacity(cmp::min(len as usize, MAX_ALLOC_SIZE / ::core::mem::size_of::<ChannelMonitorUpdateStep>()));
for _ in 0..len {
updates.push(Readable::read(r)?);
}
}
/// An error enum representing a failure to persist a channel monitor update.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub enum ChannelMonitorUpdateErr {
/// Used to indicate a temporary failure (eg connection to a watchtower or remote backup of
/// our state failed, but is expected to succeed at some point in the future).
/// inconsistent with the ChannelMonitor being called. eg for ChannelMonitor::update_monitor this
/// means you tried to update a monitor for a different channel or the ChannelMonitorUpdate was
/// corrupted.
-/// Contains a human-readable error message.
-#[derive(Debug)]
+/// Contains a developer-readable error message.
+#[derive(Clone, Debug)]
pub struct MonitorUpdateError(pub &'static str);
/// An event to be processed by the ChannelManager.
-#[derive(PartialEq)]
+#[derive(Clone, PartialEq)]
pub enum MonitorEvent {
/// A monitor event containing an HTLCUpdate.
HTLCEvent(HTLCUpdate),
/// 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,
pub(crate) payment_preimage: Option<PaymentPreimage>,
pub(crate) source: HTLCSource
}
-impl_writeable!(HTLCUpdate, 0, { payment_hash, payment_preimage, source });
+impl_writeable_tlv_based!(HTLCUpdate, {
+ (0, payment_hash),
+ (2, source),
+}, {
+ (4, payment_preimage)
+}, {});
/// If an HTLC expires within this many blocks, don't try to claim it in a shared transaction,
/// instead claiming it in its own individual transaction.
/// 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) const LATENCY_GRACE_PERIOD_BLOCKS: u32 = 3;
/// Number of blocks we wait on seeing a HTLC output being solved before we fail corresponding inbound
/// HTLCs. This prevents us from failing backwards and then getting a reorg resulting in us losing money.
-/// We use also this delay to be sure we can remove our in-flight claim txn from bump candidates buffer.
-/// It may cause spurrious generation of bumped claim txn but that's allright given the outpoint is already
-/// solved by a previous claim tx. What we want to avoid is reorg evicting our claim tx and us not
-/// keeping bumping another claim tx to solve the outpoint.
-pub(crate) const ANTI_REORG_DELAY: u32 = 6;
+// We also use this delay to be sure we can remove our in-flight claim txn from bump candidates buffer.
+// It may cause spurious generation of bumped claim txn but that's alright given the outpoint is already
+// solved by a previous claim tx. What we want to avoid is reorg evicting our claim tx and us not
+// keep bumping another claim tx to solve the outpoint.
+pub const ANTI_REORG_DELAY: u32 = 6;
/// Number of blocks before confirmation at which we fail back an un-relayed HTLC or at which we
/// refuse to accept a new HTLC.
///
/// end up force-closing the channel on us to claim it.
pub(crate) const HTLC_FAIL_BACK_BUFFER: u32 = CLTV_CLAIM_BUFFER + LATENCY_GRACE_PERIOD_BLOCKS;
+// TODO(devrandom) replace this with HolderCommitmentTransaction
#[derive(Clone, PartialEq)]
struct HolderSignedTx {
/// txid of the transaction in tx, just used to make comparison faster
feerate_per_kw: u32,
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
}
+impl_writeable_tlv_based!(HolderSignedTx, {
+ (0, txid),
+ (2, revocation_key),
+ (4, a_htlc_key),
+ (6, b_htlc_key),
+ (8, delayed_payment_key),
+ (10, per_commitment_point),
+ (12, feerate_per_kw),
+}, {}, {
+ (14, htlc_outputs)
+});
/// We use this to track counterparty commitment transactions and htlcs outputs and
/// use it to generate any justice or 2nd-stage preimage/timeout transactions.
}
}
-/// When ChannelMonitor discovers an onchain outpoint being a step of a channel and that it needs
-/// to generate a tx to push channel state forward, we cache outpoint-solving tx material to build
-/// a new bumped one in case of lenghty confirmation delay
-#[derive(Clone, PartialEq)]
-pub(crate) enum InputMaterial {
- Revoked {
- per_commitment_point: PublicKey,
- counterparty_delayed_payment_base_key: PublicKey,
- counterparty_htlc_base_key: PublicKey,
- per_commitment_key: SecretKey,
- input_descriptor: InputDescriptors,
- amount: u64,
- htlc: Option<HTLCOutputInCommitment>,
- on_counterparty_tx_csv: u16,
- },
- CounterpartyHTLC {
- per_commitment_point: PublicKey,
- counterparty_delayed_payment_base_key: PublicKey,
- counterparty_htlc_base_key: PublicKey,
- preimage: Option<PaymentPreimage>,
- htlc: HTLCOutputInCommitment
- },
- HolderHTLC {
- preimage: Option<PaymentPreimage>,
- amount: u64,
- },
- Funding {
- funding_redeemscript: Script,
- }
+/// 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 Writeable for InputMaterial {
- fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
- match self {
- &InputMaterial::Revoked { ref per_commitment_point, ref counterparty_delayed_payment_base_key, ref counterparty_htlc_base_key, ref per_commitment_key, ref input_descriptor, ref amount, ref htlc, ref on_counterparty_tx_csv} => {
- writer.write_all(&[0; 1])?;
- per_commitment_point.write(writer)?;
- counterparty_delayed_payment_base_key.write(writer)?;
- counterparty_htlc_base_key.write(writer)?;
- writer.write_all(&per_commitment_key[..])?;
- input_descriptor.write(writer)?;
- writer.write_all(&byte_utils::be64_to_array(*amount))?;
- htlc.write(writer)?;
- on_counterparty_tx_csv.write(writer)?;
- },
- &InputMaterial::CounterpartyHTLC { ref per_commitment_point, ref counterparty_delayed_payment_base_key, ref counterparty_htlc_base_key, ref preimage, ref htlc} => {
- writer.write_all(&[1; 1])?;
- per_commitment_point.write(writer)?;
- counterparty_delayed_payment_base_key.write(writer)?;
- counterparty_htlc_base_key.write(writer)?;
- preimage.write(writer)?;
- htlc.write(writer)?;
- },
- &InputMaterial::HolderHTLC { ref preimage, ref amount } => {
- writer.write_all(&[2; 1])?;
- preimage.write(writer)?;
- writer.write_all(&byte_utils::be64_to_array(*amount))?;
- },
- &InputMaterial::Funding { ref funding_redeemscript } => {
- writer.write_all(&[3; 1])?;
- funding_redeemscript.write(writer)?;
- }
- }
- Ok(())
+impl OnchainEventEntry {
+ fn confirmation_threshold(&self) -> u32 {
+ self.height + ANTI_REORG_DELAY - 1
}
-}
-impl Readable for InputMaterial {
- fn read<R: ::std::io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
- let input_material = match <u8 as Readable>::read(reader)? {
- 0 => {
- let per_commitment_point = Readable::read(reader)?;
- let counterparty_delayed_payment_base_key = Readable::read(reader)?;
- let counterparty_htlc_base_key = Readable::read(reader)?;
- let per_commitment_key = Readable::read(reader)?;
- let input_descriptor = Readable::read(reader)?;
- let amount = Readable::read(reader)?;
- let htlc = Readable::read(reader)?;
- let on_counterparty_tx_csv = Readable::read(reader)?;
- InputMaterial::Revoked {
- per_commitment_point,
- counterparty_delayed_payment_base_key,
- counterparty_htlc_base_key,
- per_commitment_key,
- input_descriptor,
- amount,
- htlc,
- on_counterparty_tx_csv
- }
- },
- 1 => {
- let per_commitment_point = Readable::read(reader)?;
- let counterparty_delayed_payment_base_key = Readable::read(reader)?;
- let counterparty_htlc_base_key = Readable::read(reader)?;
- let preimage = Readable::read(reader)?;
- let htlc = Readable::read(reader)?;
- InputMaterial::CounterpartyHTLC {
- per_commitment_point,
- counterparty_delayed_payment_base_key,
- counterparty_htlc_base_key,
- preimage,
- htlc
- }
- },
- 2 => {
- let preimage = Readable::read(reader)?;
- let amount = Readable::read(reader)?;
- InputMaterial::HolderHTLC {
- preimage,
- amount,
- }
- },
- 3 => {
- InputMaterial::Funding {
- funding_redeemscript: Readable::read(reader)?,
- }
- }
- _ => return Err(DecodeError::InvalidValue),
- };
- Ok(input_material)
+ fn has_reached_confirmation_threshold(&self, height: u32) -> bool {
+ height >= self.confirmation_threshold()
}
}
-/// ClaimRequest is a descriptor structure to communicate between detection
-/// and reaction module. They are generated by ChannelMonitor while parsing
-/// onchain txn leaked from a channel and handed over to OnchainTxHandler which
-/// is responsible for opportunistic aggregation, selecting and enforcing
-/// bumping logic, building and signing transactions.
-pub(crate) struct ClaimRequest {
- // Block height before which claiming is exclusive to one party,
- // after reaching it, claiming may be contentious.
- pub(crate) absolute_timelock: u32,
- // Timeout tx must have nLocktime set which means aggregating multiple
- // ones must take the higher nLocktime among them to satisfy all of them.
- // Sadly it has few pitfalls, a) it takes longuer to get fund back b) CLTV_DELTA
- // of a sooner-HTLC could be swallowed by the highest nLocktime of the HTLC set.
- // Do simplify we mark them as non-aggregable.
- pub(crate) aggregable: bool,
- // Basic bitcoin outpoint (txid, vout)
- pub(crate) outpoint: BitcoinOutPoint,
- // Following outpoint type, set of data needed to generate transaction digest
- // and satisfy witness program.
- pub(crate) witness_data: InputMaterial
-}
-
/// 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
/// only win from it, so it's never an OnchainEvent
HTLCUpdate {
- htlc_update: (HTLCSource, PaymentHash),
+ source: HTLCSource,
+ payment_hash: PaymentHash,
},
MaturingOutput {
descriptor: SpendableOutputDescriptor,
},
}
-const SERIALIZATION_VERSION: u8 = 1;
-const MIN_SERIALIZATION_VERSION: u8 = 1;
-
-#[cfg_attr(test, derive(PartialEq))]
+impl_writeable_tlv_based!(OnchainEventEntry, {
+ (0, txid),
+ (2, height),
+ (4, event),
+}, {}, {});
+
+impl_writeable_tlv_based_enum!(OnchainEvent,
+ (0, HTLCUpdate) => {
+ (0, source),
+ (2, payment_hash),
+ }, {}, {},
+ (1, MaturingOutput) => {
+ (0, descriptor),
+ }, {}, {},
+;);
+
+#[cfg_attr(any(test, feature = "fuzztarget", feature = "_test_utils"), derive(PartialEq))]
#[derive(Clone)]
pub(crate) enum ChannelMonitorUpdateStep {
LatestHolderCommitmentTXInfo {
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
},
LatestCounterpartyCommitmentTXInfo {
- unsigned_commitment_tx: Transaction, // TODO: We should actually only need the txid here
+ commitment_txid: Txid,
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>,
commitment_number: u64,
their_revocation_point: PublicKey,
},
}
-impl Writeable for ChannelMonitorUpdateStep {
- fn write<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> {
- match self {
- &ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { ref commitment_tx, ref htlc_outputs } => {
- 0u8.write(w)?;
- commitment_tx.write(w)?;
- (htlc_outputs.len() as u64).write(w)?;
- for &(ref output, ref signature, ref source) in htlc_outputs.iter() {
- output.write(w)?;
- signature.write(w)?;
- source.write(w)?;
- }
- }
- &ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { ref unsigned_commitment_tx, ref htlc_outputs, ref commitment_number, ref their_revocation_point } => {
- 1u8.write(w)?;
- unsigned_commitment_tx.write(w)?;
- commitment_number.write(w)?;
- their_revocation_point.write(w)?;
- (htlc_outputs.len() as u64).write(w)?;
- for &(ref output, ref source) in htlc_outputs.iter() {
- output.write(w)?;
- source.as_ref().map(|b| b.as_ref()).write(w)?;
- }
- },
- &ChannelMonitorUpdateStep::PaymentPreimage { ref payment_preimage } => {
- 2u8.write(w)?;
- payment_preimage.write(w)?;
- },
- &ChannelMonitorUpdateStep::CommitmentSecret { ref idx, ref secret } => {
- 3u8.write(w)?;
- idx.write(w)?;
- secret.write(w)?;
- },
- &ChannelMonitorUpdateStep::ChannelForceClosed { ref should_broadcast } => {
- 4u8.write(w)?;
- should_broadcast.write(w)?;
- },
- }
- Ok(())
- }
-}
-impl Readable for ChannelMonitorUpdateStep {
- fn read<R: ::std::io::Read>(r: &mut R) -> Result<Self, DecodeError> {
- match Readable::read(r)? {
- 0u8 => {
- Ok(ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo {
- commitment_tx: Readable::read(r)?,
- htlc_outputs: {
- let len: u64 = Readable::read(r)?;
- let mut res = Vec::new();
- for _ in 0..len {
- res.push((Readable::read(r)?, Readable::read(r)?, Readable::read(r)?));
- }
- res
- },
- })
- },
- 1u8 => {
- Ok(ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo {
- unsigned_commitment_tx: Readable::read(r)?,
- commitment_number: Readable::read(r)?,
- their_revocation_point: Readable::read(r)?,
- htlc_outputs: {
- let len: u64 = Readable::read(r)?;
- let mut res = Vec::new();
- for _ in 0..len {
- res.push((Readable::read(r)?, <Option<HTLCSource> as Readable>::read(r)?.map(|o| Box::new(o))));
- }
- res
- },
- })
- },
- 2u8 => {
- Ok(ChannelMonitorUpdateStep::PaymentPreimage {
- payment_preimage: Readable::read(r)?,
- })
- },
- 3u8 => {
- Ok(ChannelMonitorUpdateStep::CommitmentSecret {
- idx: Readable::read(r)?,
- secret: Readable::read(r)?,
- })
- },
- 4u8 => {
- Ok(ChannelMonitorUpdateStep::ChannelForceClosed {
- should_broadcast: Readable::read(r)?
- })
- },
- _ => Err(DecodeError::InvalidValue),
- }
- }
-}
+impl_writeable_tlv_based_enum!(ChannelMonitorUpdateStep,
+ (0, LatestHolderCommitmentTXInfo) => {
+ (0, commitment_tx),
+ }, {}, {
+ (2, htlc_outputs),
+ },
+ (1, LatestCounterpartyCommitmentTXInfo) => {
+ (0, commitment_txid),
+ (2, commitment_number),
+ (4, their_revocation_point),
+ }, {}, {
+ (6, htlc_outputs),
+ },
+ (2, PaymentPreimage) => {
+ (0, payment_preimage),
+ }, {}, {},
+ (3, CommitmentSecret) => {
+ (0, idx),
+ (2, secret),
+ }, {}, {},
+ (4, ChannelForceClosed) => {
+ (0, should_broadcast),
+ }, {}, {},
+;);
/// A ChannelMonitor handles chain events (blocks connected and disconnected) and generates
/// on-chain transactions to ensure no loss of funds occurs.
/// get_and_clear_pending_monitor_events or get_and_clear_pending_events are serialized to disk and
/// reloaded at deserialize-time. Thus, you must ensure that, when handling events, all events
/// gotten are fully handled before re-serializing the new state.
-pub struct ChannelMonitor<ChanSigner: ChannelKeys> {
+///
+/// Note that the deserializer is only implemented for (BlockHash, ChannelMonitor), which
+/// tells you the last block hash which was block_connect()ed. You MUST rescan any blocks along
+/// the "reorg path" (ie disconnecting blocks until you find a common ancestor from both the
+/// returned block hash and the the current chain and then reconnecting blocks to get to the
+/// best chain) upon deserializing the object!
+pub struct ChannelMonitor<Signer: Sign> {
+ #[cfg(test)]
+ pub(crate) inner: Mutex<ChannelMonitorImpl<Signer>>,
+ #[cfg(not(test))]
+ inner: Mutex<ChannelMonitorImpl<Signer>>,
+}
+
+pub(crate) struct ChannelMonitorImpl<Signer: Sign> {
latest_update_id: u64,
commitment_transaction_number_obscure_factor: u64,
counterparty_payment_script: Script,
shutdown_script: Script,
- keys: ChanSigner,
+ channel_keys_id: [u8; 32],
+ holder_revocation_basepoint: PublicKey,
funding_info: (OutPoint, Script),
current_counterparty_commitment_txid: Option<Txid>,
prev_counterparty_commitment_txid: Option<Txid>,
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
outputs_to_watch: HashMap<Txid, Vec<(u32, Script)>>,
#[cfg(test)]
- pub onchain_tx_handler: OnchainTxHandler<ChanSigner>,
+ pub onchain_tx_handler: OnchainTxHandler<Signer>,
#[cfg(not(test))]
- onchain_tx_handler: OnchainTxHandler<ChanSigner>,
+ onchain_tx_handler: OnchainTxHandler<Signer>,
// This is set when the Channel[Manager] generated a ChannelMonitorUpdate which indicated the
// channel has been force-closed. After this is set, no further holder commitment transaction
// 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...
}
-#[cfg(any(test, feature = "fuzztarget"))]
+/// 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
+impl<Signer: Sign> PartialEq for ChannelMonitor<Signer> {
+ fn eq(&self, other: &Self) -> bool {
+ let inner = self.inner.lock().unwrap();
+ let other = other.inner.lock().unwrap();
+ inner.eq(&other)
+ }
+}
+
+#[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))]
/// Used only in testing and fuzztarget to check serialization roundtrips don't change the
/// underlying object
-impl<ChanSigner: ChannelKeys> PartialEq for ChannelMonitor<ChanSigner> {
+impl<Signer: Sign> PartialEq for ChannelMonitorImpl<Signer> {
fn eq(&self, other: &Self) -> bool {
if self.latest_update_id != other.latest_update_id ||
self.commitment_transaction_number_obscure_factor != other.commitment_transaction_number_obscure_factor ||
self.destination_script != other.destination_script ||
self.broadcasted_holder_revokable_script != other.broadcasted_holder_revokable_script ||
self.counterparty_payment_script != other.counterparty_payment_script ||
- self.keys.pubkeys() != other.keys.pubkeys() ||
+ self.channel_keys_id != other.channel_keys_id ||
+ self.holder_revocation_basepoint != other.holder_revocation_basepoint ||
self.funding_info != other.funding_info ||
self.current_counterparty_commitment_txid != other.current_counterparty_commitment_txid ||
self.prev_counterparty_commitment_txid != other.prev_counterparty_commitment_txid ||
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
}
}
-impl<ChanSigner: ChannelKeys + Writeable> ChannelMonitor<ChanSigner> {
- /// Writes this monitor into the given writer, suitable for writing to disk.
- ///
- /// Note that the deserializer is only implemented for (Sha256dHash, ChannelMonitor), which
- /// tells you the last block hash which was block_connect()ed. You MUST rescan any blocks along
- /// the "reorg path" (ie disconnecting blocks until you find a common ancestor from both the
- /// returned block hash and the the current chain and then reconnecting blocks to get to the
- /// best chain) upon deserializing the object!
- pub fn write_for_disk<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
- //TODO: We still write out all the serialization here manually instead of using the fancy
- //serialization framework we have, we should migrate things over to it.
- writer.write_all(&[SERIALIZATION_VERSION; 1])?;
- writer.write_all(&[MIN_SERIALIZATION_VERSION; 1])?;
+impl<Signer: Sign> Writeable for ChannelMonitor<Signer> {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
+ self.inner.lock().unwrap().write(writer)
+ }
+}
+
+const SERIALIZATION_VERSION: u8 = 1;
+const MIN_SERIALIZATION_VERSION: u8 = 1;
+
+impl<Signer: Sign> Writeable for ChannelMonitorImpl<Signer> {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
+ write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
self.latest_update_id.write(writer)?;
self.counterparty_payment_script.write(writer)?;
self.shutdown_script.write(writer)?;
- self.keys.write(writer)?;
+ self.channel_keys_id.write(writer)?;
+ self.holder_revocation_basepoint.write(writer)?;
writer.write_all(&self.funding_info.0.txid[..])?;
writer.write_all(&byte_utils::be16_to_array(self.funding_info.0.index))?;
self.funding_info.1.write(writer)?;
writer.write_all(&byte_utils::be48_to_array(*commitment_number))?;
}
- macro_rules! serialize_holder_tx {
- ($holder_tx: expr) => {
- $holder_tx.txid.write(writer)?;
- writer.write_all(&$holder_tx.revocation_key.serialize())?;
- writer.write_all(&$holder_tx.a_htlc_key.serialize())?;
- writer.write_all(&$holder_tx.b_htlc_key.serialize())?;
- writer.write_all(&$holder_tx.delayed_payment_key.serialize())?;
- writer.write_all(&$holder_tx.per_commitment_point.serialize())?;
-
- writer.write_all(&byte_utils::be32_to_array($holder_tx.feerate_per_kw))?;
- writer.write_all(&byte_utils::be64_to_array($holder_tx.htlc_outputs.len() as u64))?;
- for &(ref htlc_output, ref sig, ref htlc_source) in $holder_tx.htlc_outputs.iter() {
- serialize_htlc_in_commitment!(htlc_output);
- if let &Some(ref their_sig) = sig {
- 1u8.write(writer)?;
- writer.write_all(&their_sig.serialize_compact())?;
- } else {
- 0u8.write(writer)?;
- }
- htlc_source.write(writer)?;
- }
- }
- }
-
if let Some(ref prev_holder_tx) = self.prev_holder_signed_commitment_tx {
writer.write_all(&[1; 1])?;
- serialize_holder_tx!(prev_holder_tx);
+ prev_holder_tx.write(writer)?;
} else {
writer.write_all(&[0; 1])?;
}
- serialize_holder_tx!(self.current_holder_commitment_tx);
+ self.current_holder_commitment_tx.write(writer)?;
writer.write_all(&byte_utils::be48_to_array(self.current_counterparty_commitment_number))?;
writer.write_all(&byte_utils::be48_to_array(self.current_holder_commitment_number))?;
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.write(writer)?;
}
(self.outputs_to_watch.len() as u64).write(writer)?;
self.lockdown_from_offchain.write(writer)?;
self.holder_tx_signed.write(writer)?;
+ write_tlv_fields!(writer, {}, {});
+
Ok(())
}
}
-impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
- pub(crate) fn new(keys: ChanSigner, shutdown_pubkey: &PublicKey,
- on_counterparty_tx_csv: u16, destination_script: &Script, funding_info: (OutPoint, Script),
- counterparty_htlc_base_key: &PublicKey, counterparty_delayed_payment_base_key: &PublicKey,
- on_holder_tx_csv: u16, funding_redeemscript: Script, channel_value_satoshis: u64,
- commitment_transaction_number_obscure_factor: u64,
- initial_holder_commitment_tx: HolderCommitmentTransaction) -> ChannelMonitor<ChanSigner> {
+impl<Signer: Sign> ChannelMonitor<Signer> {
+ pub(crate) fn new(secp_ctx: Secp256k1<secp256k1::All>, keys: Signer, shutdown_pubkey: &PublicKey,
+ on_counterparty_tx_csv: u16, destination_script: &Script, funding_info: (OutPoint, Script),
+ channel_parameters: &ChannelTransactionParameters,
+ funding_redeemscript: Script, channel_value_satoshis: u64,
+ commitment_transaction_number_obscure_factor: u64,
+ initial_holder_commitment_tx: HolderCommitmentTransaction,
+ best_block: BestBlock) -> ChannelMonitor<Signer> {
assert!(commitment_transaction_number_obscure_factor <= (1 << 48));
let our_channel_close_key_hash = WPubkeyHash::hash(&shutdown_pubkey.serialize());
let payment_key_hash = WPubkeyHash::hash(&keys.pubkeys().payment_point.serialize());
let counterparty_payment_script = Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&payment_key_hash[..]).into_script();
- let counterparty_tx_cache = CounterpartyCommitmentTransaction { counterparty_delayed_payment_base_key: *counterparty_delayed_payment_base_key, counterparty_htlc_base_key: *counterparty_htlc_base_key, on_counterparty_tx_csv, per_htlc: HashMap::new() };
-
- let mut onchain_tx_handler = OnchainTxHandler::new(destination_script.clone(), keys.clone(), on_holder_tx_csv);
-
- let holder_tx_sequence = initial_holder_commitment_tx.unsigned_tx.input[0].sequence as u64;
- let holder_tx_locktime = initial_holder_commitment_tx.unsigned_tx.lock_time as u64;
- let holder_commitment_tx = HolderSignedTx {
- txid: initial_holder_commitment_tx.txid(),
- revocation_key: initial_holder_commitment_tx.keys.revocation_key,
- a_htlc_key: initial_holder_commitment_tx.keys.broadcaster_htlc_key,
- b_htlc_key: initial_holder_commitment_tx.keys.countersignatory_htlc_key,
- delayed_payment_key: initial_holder_commitment_tx.keys.broadcaster_delayed_payment_key,
- per_commitment_point: initial_holder_commitment_tx.keys.per_commitment_point,
- feerate_per_kw: initial_holder_commitment_tx.feerate_per_kw,
- htlc_outputs: Vec::new(), // There are never any HTLCs in the initial commitment transactions
+ let counterparty_channel_parameters = channel_parameters.counterparty_parameters.as_ref().unwrap();
+ let counterparty_delayed_payment_base_key = counterparty_channel_parameters.pubkeys.delayed_payment_basepoint;
+ let counterparty_htlc_base_key = counterparty_channel_parameters.pubkeys.htlc_basepoint;
+ let counterparty_tx_cache = CounterpartyCommitmentTransaction { counterparty_delayed_payment_base_key, counterparty_htlc_base_key, on_counterparty_tx_csv, per_htlc: HashMap::new() };
+
+ let channel_keys_id = keys.channel_keys_id();
+ let holder_revocation_basepoint = keys.pubkeys().revocation_basepoint;
+
+ // block for Rust 1.34 compat
+ let (holder_commitment_tx, current_holder_commitment_number) = {
+ let trusted_tx = initial_holder_commitment_tx.trust();
+ let txid = trusted_tx.txid();
+
+ let tx_keys = trusted_tx.keys();
+ let holder_commitment_tx = HolderSignedTx {
+ txid,
+ revocation_key: tx_keys.revocation_key,
+ a_htlc_key: tx_keys.broadcaster_htlc_key,
+ b_htlc_key: tx_keys.countersignatory_htlc_key,
+ delayed_payment_key: tx_keys.broadcaster_delayed_payment_key,
+ per_commitment_point: tx_keys.per_commitment_point,
+ feerate_per_kw: trusted_tx.feerate_per_kw(),
+ htlc_outputs: Vec::new(), // There are never any HTLCs in the initial commitment transactions
+ };
+ (holder_commitment_tx, trusted_tx.commitment_number())
};
- onchain_tx_handler.provide_latest_holder_tx(initial_holder_commitment_tx);
+
+ let onchain_tx_handler =
+ OnchainTxHandler::new(destination_script.clone(), keys,
+ channel_parameters.clone(), initial_holder_commitment_tx, secp_ctx.clone());
let mut outputs_to_watch = HashMap::new();
outputs_to_watch.insert(funding_info.0.txid, vec![(funding_info.0.index as u32, funding_info.1.clone())]);
ChannelMonitor {
- latest_update_id: 0,
- commitment_transaction_number_obscure_factor,
+ inner: Mutex::new(ChannelMonitorImpl {
+ latest_update_id: 0,
+ commitment_transaction_number_obscure_factor,
+
+ destination_script: destination_script.clone(),
+ broadcasted_holder_revokable_script: None,
+ counterparty_payment_script,
+ shutdown_script,
- destination_script: destination_script.clone(),
- broadcasted_holder_revokable_script: None,
- counterparty_payment_script,
- shutdown_script,
+ channel_keys_id,
+ holder_revocation_basepoint,
+ funding_info,
+ current_counterparty_commitment_txid: None,
+ prev_counterparty_commitment_txid: None,
- keys,
- funding_info,
- current_counterparty_commitment_txid: None,
- prev_counterparty_commitment_txid: None,
+ counterparty_tx_cache,
+ funding_redeemscript,
+ channel_value_satoshis,
+ their_cur_revocation_points: None,
- counterparty_tx_cache,
- funding_redeemscript,
- channel_value_satoshis,
- their_cur_revocation_points: None,
+ on_holder_tx_csv: counterparty_channel_parameters.selected_contest_delay,
- on_holder_tx_csv,
+ commitment_secrets: CounterpartyCommitmentSecrets::new(),
+ counterparty_claimable_outpoints: HashMap::new(),
+ counterparty_commitment_txn_on_chain: HashMap::new(),
+ counterparty_hash_commitment_number: HashMap::new(),
- commitment_secrets: CounterpartyCommitmentSecrets::new(),
- counterparty_claimable_outpoints: HashMap::new(),
- counterparty_commitment_txn_on_chain: HashMap::new(),
- counterparty_hash_commitment_number: HashMap::new(),
+ prev_holder_signed_commitment_tx: None,
+ current_holder_commitment_tx: holder_commitment_tx,
+ current_counterparty_commitment_number: 1 << 48,
+ current_holder_commitment_number,
- prev_holder_signed_commitment_tx: None,
- current_holder_commitment_tx: holder_commitment_tx,
- current_counterparty_commitment_number: 1 << 48,
- current_holder_commitment_number: 0xffff_ffff_ffff - ((((holder_tx_sequence & 0xffffff) << 3*8) | (holder_tx_locktime as u64 & 0xffffff)) ^ commitment_transaction_number_obscure_factor),
+ payment_preimages: HashMap::new(),
+ pending_monitor_events: Vec::new(),
+ pending_events: Vec::new(),
- payment_preimages: HashMap::new(),
- pending_monitor_events: Vec::new(),
- pending_events: Vec::new(),
+ onchain_events_awaiting_threshold_conf: Vec::new(),
+ outputs_to_watch,
- onchain_events_waiting_threshold_conf: HashMap::new(),
- outputs_to_watch,
+ onchain_tx_handler,
- onchain_tx_handler,
+ lockdown_from_offchain: false,
+ holder_tx_signed: false,
- lockdown_from_offchain: false,
- holder_tx_signed: false,
+ best_block,
- last_block_hash: Default::default(),
- secp_ctx: Secp256k1::new(),
+ secp_ctx,
+ }),
}
}
+ #[cfg(test)]
+ fn provide_secret(&self, idx: u64, secret: [u8; 32]) -> Result<(), MonitorUpdateError> {
+ self.inner.lock().unwrap().provide_secret(idx, secret)
+ }
+
+ /// Informs this monitor of the latest counterparty (ie non-broadcastable) commitment transaction.
+ /// The monitor watches for it to be broadcasted and then uses the HTLC information (and
+ /// possibly future revocation/preimage information) to claim outputs where possible.
+ /// We cache also the mapping hash:commitment number to lighten pruning of old preimages by watchtowers.
+ pub(crate) fn provide_latest_counterparty_commitment_tx<L: Deref>(
+ &self,
+ txid: Txid,
+ htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>,
+ commitment_number: u64,
+ their_revocation_point: PublicKey,
+ logger: &L,
+ ) where L::Target: Logger {
+ self.inner.lock().unwrap().provide_latest_counterparty_commitment_tx(
+ txid, htlc_outputs, commitment_number, their_revocation_point, logger)
+ }
+
+ #[cfg(test)]
+ fn provide_latest_holder_commitment_tx(
+ &self,
+ holder_commitment_tx: HolderCommitmentTransaction,
+ htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
+ ) -> Result<(), MonitorUpdateError> {
+ self.inner.lock().unwrap().provide_latest_holder_commitment_tx(
+ holder_commitment_tx, htlc_outputs)
+ }
+
+ #[cfg(test)]
+ pub(crate) fn provide_payment_preimage<B: Deref, F: Deref, L: Deref>(
+ &self,
+ payment_hash: &PaymentHash,
+ payment_preimage: &PaymentPreimage,
+ broadcaster: &B,
+ fee_estimator: &F,
+ logger: &L,
+ ) where
+ B::Target: BroadcasterInterface,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
+ {
+ self.inner.lock().unwrap().provide_payment_preimage(
+ payment_hash, payment_preimage, broadcaster, fee_estimator, logger)
+ }
+
+ pub(crate) fn broadcast_latest_holder_commitment_txn<B: Deref, L: Deref>(
+ &self,
+ broadcaster: &B,
+ logger: &L,
+ ) where
+ B::Target: BroadcasterInterface,
+ L::Target: Logger,
+ {
+ self.inner.lock().unwrap().broadcast_latest_holder_commitment_txn(broadcaster, logger)
+ }
+
+ /// Updates a ChannelMonitor on the basis of some new information provided by the Channel
+ /// itself.
+ ///
+ /// panics if the given update is not the next update by update_id.
+ pub fn update_monitor<B: Deref, F: Deref, L: Deref>(
+ &self,
+ updates: &ChannelMonitorUpdate,
+ broadcaster: &B,
+ fee_estimator: &F,
+ logger: &L,
+ ) -> Result<(), MonitorUpdateError>
+ where
+ B::Target: BroadcasterInterface,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
+ {
+ self.inner.lock().unwrap().update_monitor(updates, broadcaster, fee_estimator, logger)
+ }
+
+ /// Gets the update_id from the latest ChannelMonitorUpdate which was applied to this
+ /// ChannelMonitor.
+ pub fn get_latest_update_id(&self) -> u64 {
+ self.inner.lock().unwrap().get_latest_update_id()
+ }
+
+ /// Gets the funding transaction outpoint of the channel this ChannelMonitor is monitoring for.
+ pub fn get_funding_txo(&self) -> (OutPoint, Script) {
+ self.inner.lock().unwrap().get_funding_txo().clone()
+ }
+
+ /// 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().
+ 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`].
+ pub fn get_and_clear_pending_monitor_events(&self) -> Vec<MonitorEvent> {
+ self.inner.lock().unwrap().get_and_clear_pending_monitor_events()
+ }
+
+ /// Gets the list of pending events which were generated by previous actions, clearing the list
+ /// in the process.
+ ///
+ /// This is called by ChainMonitor::get_and_clear_pending_events() and is equivalent to
+ /// EventsProvider::get_and_clear_pending_events() except that it requires &mut self as we do
+ /// no internal locking in ChannelMonitors.
+ pub fn get_and_clear_pending_events(&self) -> Vec<Event> {
+ self.inner.lock().unwrap().get_and_clear_pending_events()
+ }
+
+ pub(crate) fn get_min_seen_secret(&self) -> u64 {
+ self.inner.lock().unwrap().get_min_seen_secret()
+ }
+
+ pub(crate) fn get_cur_counterparty_commitment_number(&self) -> u64 {
+ self.inner.lock().unwrap().get_cur_counterparty_commitment_number()
+ }
+
+ pub(crate) fn get_cur_holder_commitment_number(&self) -> u64 {
+ self.inner.lock().unwrap().get_cur_holder_commitment_number()
+ }
+
+ /// Used by ChannelManager deserialization to broadcast the latest holder state if its copy of
+ /// the Channel was out-of-date. You may use it to get a broadcastable holder toxic tx in case of
+ /// fallen-behind, i.e when receiving a channel_reestablish with a proof that our counterparty side knows
+ /// a higher revocation secret than the holder commitment number we are aware of. Broadcasting these
+ /// transactions are UNSAFE, as they allow counterparty side to punish you. Nevertheless you may want to
+ /// broadcast them if counterparty don't close channel with his higher commitment transaction after a
+ /// substantial amount of time (a month or even a year) to get back funds. Best may be to contact
+ /// out-of-band the other node operator to coordinate with him if option is available to you.
+ /// In any-case, choice is up to the user.
+ pub fn get_latest_holder_commitment_txn<L: Deref>(&self, logger: &L) -> Vec<Transaction>
+ where L::Target: Logger {
+ self.inner.lock().unwrap().get_latest_holder_commitment_txn(logger)
+ }
+
+ /// Unsafe test-only version of get_latest_holder_commitment_txn used by our test framework
+ /// to bypass HolderCommitmentTransaction state update lockdown after signature and generate
+ /// revoked commitment transaction.
+ #[cfg(any(test, feature = "unsafe_revoked_tx_signing"))]
+ pub fn unsafe_get_latest_holder_commitment_txn<L: Deref>(&self, logger: &L) -> Vec<Transaction>
+ where L::Target: Logger {
+ self.inner.lock().unwrap().unsafe_get_latest_holder_commitment_txn(logger)
+ }
+
+ /// Processes transactions in a newly connected block, which may result in any of the following:
+ /// - update the monitor's state against resolved HTLCs
+ /// - punish the counterparty in the case of seeing a revoked commitment transaction
+ /// - force close the channel and claim/timeout incoming/outgoing HTLCs if near expiration
+ /// - detect settled outputs for later spending
+ /// - schedule and bump any in-flight claims
+ ///
+ /// Returns any new outputs to watch from `txdata`; after called, these are also included in
+ /// [`get_outputs_to_watch`].
+ ///
+ /// [`get_outputs_to_watch`]: #method.get_outputs_to_watch
+ pub fn block_connected<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().block_connected(
+ header, txdata, height, broadcaster, fee_estimator, logger)
+ }
+
+ /// Determines if the disconnected block contained any transactions of interest and updates
+ /// appropriately.
+ pub fn block_disconnected<B: Deref, F: Deref, L: Deref>(
+ &self,
+ header: &BlockHeader,
+ height: u32,
+ broadcaster: B,
+ fee_estimator: F,
+ logger: L,
+ ) where
+ B::Target: BroadcasterInterface,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
+ {
+ 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> {
/// Inserts a revocation secret into this channel monitor. Prunes old preimages if neither
/// needed by holder commitment transactions HTCLs nor by counterparty ones. Unless we haven't already seen
/// counterparty commitment transaction's secret, they are de facto pruned (we can use revocation key).
Ok(())
}
- /// Informs this monitor of the latest counterparty (ie non-broadcastable) commitment transaction.
- /// The monitor watches for it to be broadcasted and then uses the HTLC information (and
- /// possibly future revocation/preimage information) to claim outputs where possible.
- /// We cache also the mapping hash:commitment number to lighten pruning of old preimages by watchtowers.
- pub(crate) fn provide_latest_counterparty_commitment_tx_info<L: Deref>(&mut self, unsigned_commitment_tx: &Transaction, htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>, commitment_number: u64, their_revocation_point: PublicKey, logger: &L) where L::Target: Logger {
+ pub(crate) fn provide_latest_counterparty_commitment_tx<L: Deref>(&mut self, txid: Txid, htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>, commitment_number: u64, their_revocation_point: PublicKey, logger: &L) where L::Target: Logger {
// TODO: Encrypt the htlc_outputs data with the single-hash of the commitment transaction
// so that a remote monitor doesn't learn anything unless there is a malicious close.
// (only maybe, sadly we cant do the same for local info, as we need to be aware of
self.counterparty_hash_commitment_number.insert(htlc.payment_hash, commitment_number);
}
- let new_txid = unsigned_commitment_tx.txid();
- log_trace!(logger, "Tracking new counterparty commitment transaction with txid {} at commitment number {} with {} HTLC outputs", new_txid, commitment_number, htlc_outputs.len());
- log_trace!(logger, "New potential counterparty commitment transaction: {}", encode::serialize_hex(unsigned_commitment_tx));
+ log_trace!(logger, "Tracking new counterparty commitment transaction with txid {} at commitment number {} with {} HTLC outputs", txid, commitment_number, htlc_outputs.len());
self.prev_counterparty_commitment_txid = self.current_counterparty_commitment_txid.take();
- self.current_counterparty_commitment_txid = Some(new_txid);
- self.counterparty_claimable_outpoints.insert(new_txid, htlc_outputs.clone());
+ self.current_counterparty_commitment_txid = Some(txid);
+ self.counterparty_claimable_outpoints.insert(txid, htlc_outputs.clone());
self.current_counterparty_commitment_number = commitment_number;
//TODO: Merge this into the other per-counterparty-transaction output storage stuff
match self.their_cur_revocation_points {
htlcs.push(htlc.0);
}
}
- self.counterparty_tx_cache.per_htlc.insert(new_txid, htlcs);
+ self.counterparty_tx_cache.per_htlc.insert(txid, htlcs);
}
/// Informs this monitor of the latest holder (ie broadcastable) commitment transaction. The
/// is important that any clones of this channel monitor (including remote clones) by kept
/// up-to-date as our holder commitment transaction is updated.
/// Panics if set_on_holder_tx_csv has never been called.
- fn provide_latest_holder_commitment_tx_info(&mut self, commitment_tx: HolderCommitmentTransaction, htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>) -> Result<(), MonitorUpdateError> {
- let txid = commitment_tx.txid();
- let sequence = commitment_tx.unsigned_tx.input[0].sequence as u64;
- let locktime = commitment_tx.unsigned_tx.lock_time as u64;
- let mut new_holder_commitment_tx = HolderSignedTx {
- txid,
- revocation_key: commitment_tx.keys.revocation_key,
- a_htlc_key: commitment_tx.keys.broadcaster_htlc_key,
- b_htlc_key: commitment_tx.keys.countersignatory_htlc_key,
- delayed_payment_key: commitment_tx.keys.broadcaster_delayed_payment_key,
- per_commitment_point: commitment_tx.keys.per_commitment_point,
- feerate_per_kw: commitment_tx.feerate_per_kw,
- htlc_outputs,
+ fn provide_latest_holder_commitment_tx(&mut self, holder_commitment_tx: HolderCommitmentTransaction, htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>) -> Result<(), MonitorUpdateError> {
+ // block for Rust 1.34 compat
+ let mut new_holder_commitment_tx = {
+ let trusted_tx = holder_commitment_tx.trust();
+ let txid = trusted_tx.txid();
+ let tx_keys = trusted_tx.keys();
+ self.current_holder_commitment_number = trusted_tx.commitment_number();
+ HolderSignedTx {
+ txid,
+ revocation_key: tx_keys.revocation_key,
+ a_htlc_key: tx_keys.broadcaster_htlc_key,
+ b_htlc_key: tx_keys.countersignatory_htlc_key,
+ delayed_payment_key: tx_keys.broadcaster_delayed_payment_key,
+ per_commitment_point: tx_keys.per_commitment_point,
+ feerate_per_kw: trusted_tx.feerate_per_kw(),
+ htlc_outputs,
+ }
};
- self.onchain_tx_handler.provide_latest_holder_tx(commitment_tx);
- self.current_holder_commitment_number = 0xffff_ffff_ffff - ((((sequence & 0xffffff) << 3*8) | (locktime as u64 & 0xffffff)) ^ self.commitment_transaction_number_obscure_factor);
+ self.onchain_tx_handler.provide_latest_holder_tx(holder_commitment_tx);
mem::swap(&mut new_holder_commitment_tx, &mut self.current_holder_commitment_tx);
self.prev_holder_signed_commitment_tx = Some(new_holder_commitment_tx);
if self.holder_tx_signed {
/// Provides a payment_hash->payment_preimage mapping. Will be automatically pruned when all
/// commitment_tx_infos which contain the payment hash have been revoked.
- pub(crate) fn provide_payment_preimage(&mut self, payment_hash: &PaymentHash, payment_preimage: &PaymentPreimage) {
+ fn provide_payment_preimage<B: Deref, F: Deref, L: Deref>(&mut self, payment_hash: &PaymentHash, payment_preimage: &PaymentPreimage, broadcaster: &B, fee_estimator: &F, logger: &L)
+ where B::Target: BroadcasterInterface,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
+ {
self.payment_preimages.insert(payment_hash.clone(), payment_preimage.clone());
+
+ // If the channel is force closed, try to claim the output from this preimage.
+ // First check if a counterparty commitment transaction has been broadcasted:
+ macro_rules! claim_htlcs {
+ ($commitment_number: expr, $txid: expr) => {
+ let htlc_claim_reqs = self.get_counterparty_htlc_output_claim_reqs($commitment_number, $txid, None);
+ self.onchain_tx_handler.update_claims_view(&Vec::new(), htlc_claim_reqs, self.best_block.height(), broadcaster, fee_estimator, logger);
+ }
+ }
+ if let Some(txid) = self.current_counterparty_commitment_txid {
+ if let Some(commitment_number) = self.counterparty_commitment_txn_on_chain.get(&txid) {
+ claim_htlcs!(*commitment_number, txid);
+ return;
+ }
+ }
+ if let Some(txid) = self.prev_counterparty_commitment_txid {
+ if let Some(commitment_number) = self.counterparty_commitment_txn_on_chain.get(&txid) {
+ claim_htlcs!(*commitment_number, txid);
+ return;
+ }
+ }
+
+ // Then if a holder commitment transaction has been seen on-chain, broadcast transactions
+ // claiming the HTLC output from each of the holder commitment transactions.
+ // Note that we can't just use `self.holder_tx_signed`, because that only covers the case where
+ // *we* sign a holder commitment transaction, not when e.g. a watchtower broadcasts one of our
+ // holder commitment transactions.
+ if self.broadcasted_holder_revokable_script.is_some() {
+ let (claim_reqs, _) = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx, 0);
+ self.onchain_tx_handler.update_claims_view(&Vec::new(), claim_reqs, 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, 0);
+ self.onchain_tx_handler.update_claims_view(&Vec::new(), claim_reqs, self.best_block.height(), broadcaster, fee_estimator, logger);
+ }
+ }
}
pub(crate) fn broadcast_latest_holder_commitment_txn<B: Deref, L: Deref>(&mut self, broadcaster: &B, logger: &L)
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));
}
- /// Updates a ChannelMonitor on the basis of some new information provided by the Channel
- /// itself.
- ///
- /// panics if the given update is not the next update by update_id.
- pub fn update_monitor<B: Deref, L: Deref>(&mut self, mut updates: ChannelMonitorUpdate, broadcaster: &B, logger: &L) -> Result<(), MonitorUpdateError>
- where B::Target: BroadcasterInterface,
- L::Target: Logger,
+ pub fn update_monitor<B: Deref, F: Deref, L: Deref>(&mut self, updates: &ChannelMonitorUpdate, broadcaster: &B, fee_estimator: &F, logger: &L) -> Result<(), MonitorUpdateError>
+ where B::Target: BroadcasterInterface,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
{
- if self.latest_update_id + 1 != updates.update_id {
+ // ChannelMonitor updates may be applied after force close if we receive a
+ // preimage for a broadcasted commitment transaction HTLC output that we'd
+ // like to claim on-chain. If this is the case, we no longer have guaranteed
+ // access to the monitor's update ID, so we use a sentinel value instead.
+ if updates.update_id == CLOSED_CHANNEL_UPDATE_ID {
+ match updates.updates[0] {
+ ChannelMonitorUpdateStep::PaymentPreimage { .. } => {},
+ _ => panic!("Attempted to apply post-force-close ChannelMonitorUpdate that wasn't providing a payment preimage"),
+ }
+ assert_eq!(updates.updates.len(), 1);
+ } else if self.latest_update_id + 1 != updates.update_id {
panic!("Attempted to apply ChannelMonitorUpdates out of order, check the update_id before passing an update to update_monitor!");
}
- for update in updates.updates.drain(..) {
+ for update in updates.updates.iter() {
match update {
ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs } => {
+ log_trace!(logger, "Updating ChannelMonitor with latest holder commitment transaction info");
if self.lockdown_from_offchain { panic!(); }
- self.provide_latest_holder_commitment_tx_info(commitment_tx, htlc_outputs)?
+ self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone())?
+ }
+ ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { commitment_txid, htlc_outputs, commitment_number, their_revocation_point } => {
+ log_trace!(logger, "Updating ChannelMonitor with latest counterparty commitment transaction info");
+ self.provide_latest_counterparty_commitment_tx(*commitment_txid, htlc_outputs.clone(), *commitment_number, *their_revocation_point, logger)
+ },
+ ChannelMonitorUpdateStep::PaymentPreimage { payment_preimage } => {
+ log_trace!(logger, "Updating ChannelMonitor with payment preimage");
+ self.provide_payment_preimage(&PaymentHash(Sha256::hash(&payment_preimage.0[..]).into_inner()), &payment_preimage, broadcaster, fee_estimator, logger)
+ },
+ ChannelMonitorUpdateStep::CommitmentSecret { idx, secret } => {
+ log_trace!(logger, "Updating ChannelMonitor with commitment secret");
+ self.provide_secret(*idx, *secret)?
},
- ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { unsigned_commitment_tx, htlc_outputs, commitment_number, their_revocation_point } =>
- self.provide_latest_counterparty_commitment_tx_info(&unsigned_commitment_tx, htlc_outputs, commitment_number, their_revocation_point, logger),
- ChannelMonitorUpdateStep::PaymentPreimage { payment_preimage } =>
- self.provide_payment_preimage(&PaymentHash(Sha256::hash(&payment_preimage.0[..]).into_inner()), &payment_preimage),
- ChannelMonitorUpdateStep::CommitmentSecret { idx, secret } =>
- self.provide_secret(idx, secret)?,
ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast } => {
+ log_trace!(logger, "Updating ChannelMonitor: channel force closed, should broadcast: {}", should_broadcast);
self.lockdown_from_offchain = true;
- if should_broadcast {
+ 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.");
}
}
}
Ok(())
}
- /// Gets the update_id from the latest ChannelMonitorUpdate which was applied to this
- /// ChannelMonitor.
pub fn get_latest_update_id(&self) -> u64 {
self.latest_update_id
}
- /// Gets the funding transaction outpoint of the channel this ChannelMonitor is monitoring for.
pub fn get_funding_txo(&self) -> &(OutPoint, Script) {
&self.funding_info
}
- /// 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)>> {
// If we've detected a counterparty commitment tx on chain, we must include it in the set
// of outputs to watch for spends of, otherwise we're likely to lose user funds. Because
&self.outputs_to_watch
}
- /// 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(&mut self) -> Vec<MonitorEvent> {
let mut ret = Vec::new();
mem::swap(&mut ret, &mut self.pending_monitor_events);
ret
}
- /// Gets the list of pending events which were generated by previous actions, clearing the list
- /// in the process.
- ///
- /// This is called by ChainMonitor::get_and_clear_pending_events() and is equivalent to
- /// EventsProvider::get_and_clear_pending_events() except that it requires &mut self as we do
- /// no internal locking in ChannelMonitors.
pub fn get_and_clear_pending_events(&mut self) -> Vec<Event> {
let mut ret = Vec::new();
mem::swap(&mut ret, &mut self.pending_events);
/// 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<PackageTemplate>, 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();
let secret = self.get_secret(commitment_number).unwrap();
let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret));
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
- let revocation_pubkey = ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, &per_commitment_point, &self.keys.pubkeys().revocation_basepoint));
+ let revocation_pubkey = ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, &per_commitment_point, &self.holder_revocation_basepoint));
let delayed_key = ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key), &self.counterparty_tx_cache.counterparty_delayed_payment_base_key));
let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.counterparty_tx_cache.on_counterparty_tx_csv, &delayed_key);
// First, process non-htlc outputs (to_holder & to_counterparty)
for (idx, outp) in tx.output.iter().enumerate() {
if outp.script_pubkey == revokeable_p2wsh {
- let witness_data = InputMaterial::Revoked { per_commitment_point, counterparty_delayed_payment_base_key: self.counterparty_tx_cache.counterparty_delayed_payment_base_key, counterparty_htlc_base_key: self.counterparty_tx_cache.counterparty_htlc_base_key, per_commitment_key, input_descriptor: InputDescriptors::RevokedOutput, amount: outp.value, htlc: None, on_counterparty_tx_csv: self.counterparty_tx_cache.on_counterparty_tx_csv};
- claimable_outpoints.push(ClaimRequest { absolute_timelock: height + self.counterparty_tx_cache.on_counterparty_tx_csv as u32, aggregable: true, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: idx as u32 }, witness_data});
+ let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, per_commitment_key, outp.value, self.counterparty_tx_cache.on_counterparty_tx_csv);
+ let justice_package = PackageTemplate::build_package(commitment_txid, idx as u32, PackageSolvingData::RevokedOutput(revk_outp), height + self.counterparty_tx_cache.on_counterparty_tx_csv as u32, true, height);
+ claimable_outpoints.push(justice_package);
}
}
tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
return (claimable_outpoints, (commitment_txid, watch_outputs)); // Corrupted per_commitment_data, fuck this user
}
- let witness_data = InputMaterial::Revoked { per_commitment_point, counterparty_delayed_payment_base_key: self.counterparty_tx_cache.counterparty_delayed_payment_base_key, counterparty_htlc_base_key: self.counterparty_tx_cache.counterparty_htlc_base_key, per_commitment_key, input_descriptor: if htlc.offered { InputDescriptors::RevokedOfferedHTLC } else { InputDescriptors::RevokedReceivedHTLC }, amount: tx.output[transaction_output_index as usize].value, htlc: Some(htlc.clone()), on_counterparty_tx_csv: self.counterparty_tx_cache.on_counterparty_tx_csv};
- claimable_outpoints.push(ClaimRequest { absolute_timelock: htlc.cltv_expiry, aggregable: true, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: transaction_output_index }, witness_data });
+ let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone());
+ let justice_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp), htlc.cltv_expiry, true, height);
+ claimable_outpoints.push(justice_package);
}
}
}
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())});
- }
- 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 { source: ref update_source, .. } => {
+ *update_source != **source
+ },
+ _ => true,
}
- }
+ });
+ let entry = OnchainEventEntry {
+ txid: *$txid,
+ height,
+ event: OnchainEvent::HTLCUpdate {
+ source: (**source).clone(),
+ payment_hash: 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 { source: ref update_source, .. } => {
+ *update_source != **source
+ },
+ _ => true,
}
- }
+ });
+ self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
+ txid: *$txid,
+ height,
+ event: OnchainEvent::HTLCUpdate {
+ source: (**source).clone(),
+ payment_hash: htlc.payment_hash.clone(),
+ },
+ });
}
}
}
check_htlc_fails!(txid, "previous", 'prev_loop);
}
+ let htlc_claim_reqs = self.get_counterparty_htlc_output_claim_reqs(commitment_number, commitment_txid, Some(tx));
+ for req in htlc_claim_reqs {
+ claimable_outpoints.push(req);
+ }
+
+ }
+ (claimable_outpoints, (commitment_txid, watch_outputs))
+ }
+
+ fn get_counterparty_htlc_output_claim_reqs(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>) -> Vec<PackageTemplate> {
+ let mut claimable_outpoints = Vec::new();
+ if let Some(htlc_outputs) = self.counterparty_claimable_outpoints.get(&commitment_txid) {
if let Some(revocation_points) = self.their_cur_revocation_points {
let revocation_point_option =
+ // If the counterparty commitment tx is the latest valid state, use their latest
+ // per-commitment point
if revocation_points.0 == commitment_number { Some(&revocation_points.1) }
else if let Some(point) = revocation_points.2.as_ref() {
+ // If counterparty commitment tx is the state previous to the latest valid state, use
+ // their previous per-commitment point (non-atomicity of revocation means it's valid for
+ // them to temporarily have two valid commitment txns from our viewpoint)
if revocation_points.0 == commitment_number + 1 { Some(point) } else { None }
} else { None };
if let Some(revocation_point) = revocation_point_option {
- self.counterparty_payment_script = {
- // Note that the Network here is ignored as we immediately drop the address for the
- // script_pubkey version
- let payment_hash160 = WPubkeyHash::hash(&self.keys.pubkeys().payment_point.serialize());
- Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&payment_hash160[..]).into_script()
- };
-
- // Then, try to find htlc outputs
- for (_, &(ref htlc, _)) in per_commitment_data.iter().enumerate() {
+ for (_, &(ref htlc, _)) in htlc_outputs.iter().enumerate() {
if let Some(transaction_output_index) = htlc.transaction_output_index {
- if transaction_output_index as usize >= tx.output.len() ||
- tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
- return (claimable_outpoints, (commitment_txid, watch_outputs)); // Corrupted per_commitment_data, fuck this user
+ if let Some(transaction) = tx {
+ if transaction_output_index as usize >= transaction.output.len() ||
+ transaction.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
+ return claimable_outpoints; // Corrupted per_commitment_data, fuck this user
+ }
}
let preimage = if htlc.offered { if let Some(p) = self.payment_preimages.get(&htlc.payment_hash) { Some(*p) } else { None } } else { None };
- let aggregable = if !htlc.offered { false } else { true };
if preimage.is_some() || !htlc.offered {
- let witness_data = InputMaterial::CounterpartyHTLC { per_commitment_point: *revocation_point, counterparty_delayed_payment_base_key: self.counterparty_tx_cache.counterparty_delayed_payment_base_key, counterparty_htlc_base_key: self.counterparty_tx_cache.counterparty_htlc_base_key, preimage, htlc: htlc.clone() };
- claimable_outpoints.push(ClaimRequest { absolute_timelock: htlc.cltv_expiry, aggregable, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: transaction_output_index }, witness_data });
+ let counterparty_htlc_outp = if htlc.offered { PackageSolvingData::CounterpartyOfferedHTLCOutput(CounterpartyOfferedHTLCOutput::build(*revocation_point, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, preimage.unwrap(), htlc.clone())) } else { PackageSolvingData::CounterpartyReceivedHTLCOutput(CounterpartyReceivedHTLCOutput::build(*revocation_point, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, htlc.clone())) };
+ let aggregation = if !htlc.offered { false } else { true };
+ let counterparty_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, counterparty_htlc_outp, htlc.cltv_expiry,aggregation, 0);
+ claimable_outpoints.push(counterparty_package);
}
}
}
}
}
}
- (claimable_outpoints, (commitment_txid, watch_outputs))
+ claimable_outpoints
}
/// 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<PackageTemplate>, 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)
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
log_trace!(logger, "Counterparty HTLC broadcast {}:{}", htlc_txid, 0);
- let witness_data = InputMaterial::Revoked { per_commitment_point, counterparty_delayed_payment_base_key: self.counterparty_tx_cache.counterparty_delayed_payment_base_key, counterparty_htlc_base_key: self.counterparty_tx_cache.counterparty_htlc_base_key, per_commitment_key, input_descriptor: InputDescriptors::RevokedOutput, amount: tx.output[0].value, htlc: None, on_counterparty_tx_csv: self.counterparty_tx_cache.on_counterparty_tx_csv };
- let claimable_outpoints = vec!(ClaimRequest { absolute_timelock: height + self.counterparty_tx_cache.on_counterparty_tx_csv as u32, aggregable: true, outpoint: BitcoinOutPoint { txid: htlc_txid, vout: 0}, witness_data });
+ let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, per_commitment_key, tx.output[0].value, self.counterparty_tx_cache.on_counterparty_tx_csv);
+ let justice_package = PackageTemplate::build_package(htlc_txid, 0, PackageSolvingData::RevokedOutput(revk_outp), height + self.counterparty_tx_cache.on_counterparty_tx_csv as u32, true, height);
+ let claimable_outpoints = vec!(justice_package);
let outputs = vec![(0, tx.output[0].clone())];
(claimable_outpoints, Some((htlc_txid, outputs)))
}
- fn broadcast_by_holder_state(&self, commitment_tx: &Transaction, holder_tx: &HolderSignedTx) -> (Vec<ClaimRequest>, Vec<(u32, TxOut)>, Option<(Script, PublicKey, PublicKey)>) {
+ // 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, height: u32) -> (Vec<PackageTemplate>, Option<(Script, PublicKey, PublicKey)>) {
let mut claim_requests = Vec::with_capacity(holder_tx.htlc_outputs.len());
- let mut watch_outputs = Vec::with_capacity(holder_tx.htlc_outputs.len());
let redeemscript = chan_utils::get_revokeable_redeemscript(&holder_tx.revocation_key, self.on_holder_tx_csv, &holder_tx.delayed_payment_key);
let broadcasted_holder_revokable_script = Some((redeemscript.to_v0_p2wsh(), holder_tx.per_commitment_point.clone(), holder_tx.revocation_key.clone()));
for &(ref htlc, _, _) in holder_tx.htlc_outputs.iter() {
if let Some(transaction_output_index) = htlc.transaction_output_index {
- claim_requests.push(ClaimRequest { absolute_timelock: ::std::u32::MAX, aggregable: false, outpoint: BitcoinOutPoint { txid: holder_tx.txid, vout: transaction_output_index as u32 },
- witness_data: InputMaterial::HolderHTLC {
- preimage: if !htlc.offered {
- if let Some(preimage) = self.payment_preimages.get(&htlc.payment_hash) {
- Some(preimage.clone())
- } else {
- // We can't build an HTLC-Success transaction without the preimage
- continue;
- }
- } else { None },
- amount: htlc.amount_msat,
- }});
- watch_outputs.push((transaction_output_index, commitment_tx.output[transaction_output_index as usize].clone()));
+ let htlc_output = if htlc.offered {
+ HolderHTLCOutput::build_offered(htlc.amount_msat, htlc.cltv_expiry)
+ } else {
+ let payment_preimage = if let Some(preimage) = self.payment_preimages.get(&htlc.payment_hash) {
+ preimage.clone()
+ } else {
+ // We can't build an HTLC-Success transaction without the preimage
+ continue;
+ };
+ HolderHTLCOutput::build_accepted(payment_preimage, htlc.amount_msat)
+ };
+ let htlc_package = PackageTemplate::build_package(holder_tx.txid, transaction_output_index, PackageSolvingData::HolderHTLCOutput(htlc_output), height, false, height);
+ claim_requests.push(htlc_package);
}
}
- (claim_requests, watch_outputs, broadcasted_holder_revokable_script)
+ (claim_requests, broadcasted_holder_revokable_script)
+ }
+
+ // Returns holder HTLC outputs to watch and react to in case of spending.
+ fn get_broadcasted_holder_watch_outputs(&self, holder_tx: &HolderSignedTx, commitment_tx: &Transaction) -> Vec<(u32, TxOut)> {
+ let mut watch_outputs = Vec::with_capacity(holder_tx.htlc_outputs.len());
+ for &(ref htlc, _, _) in holder_tx.htlc_outputs.iter() {
+ if let Some(transaction_output_index) = htlc.transaction_output_index {
+ watch_outputs.push((transaction_output_index, commitment_tx.output[transaction_output_index as usize].clone()));
+ }
+ }
+ watch_outputs
}
/// 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<PackageTemplate>, 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)});
- }
- hash_map::Entry::Vacant(entry) => {
- entry.insert(vec![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 { source: ref update_source, .. } => {
+ *update_source != $source
+ },
+ _ => true,
}
- }
+ });
+ let entry = OnchainEventEntry {
+ txid: commitment_txid,
+ height,
+ event: OnchainEvent::HTLCUpdate { source: $source, payment_hash: $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);
}
}
macro_rules! append_onchain_update {
- ($updates: expr) => {
+ ($updates: expr, $to_watch: expr) => {
claim_requests = $updates.0;
- watch_outputs.append(&mut $updates.1);
- self.broadcasted_holder_revokable_script = $updates.2;
+ self.broadcasted_holder_revokable_script = $updates.1;
+ watch_outputs.append(&mut $to_watch);
}
}
if self.current_holder_commitment_tx.txid == commitment_txid {
is_holder_tx = true;
log_trace!(logger, "Got latest holder commitment tx broadcast, searching for available HTLCs to claim");
- let mut res = self.broadcast_by_holder_state(tx, &self.current_holder_commitment_tx);
- append_onchain_update!(res);
+ let res = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx, height);
+ let mut to_watch = self.get_broadcasted_holder_watch_outputs(&self.current_holder_commitment_tx, tx);
+ append_onchain_update!(res, to_watch);
} else if let &Some(ref holder_tx) = &self.prev_holder_signed_commitment_tx {
if holder_tx.txid == commitment_txid {
is_holder_tx = true;
log_trace!(logger, "Got previous holder commitment tx broadcast, searching for available HTLCs to claim");
- let mut res = self.broadcast_by_holder_state(tx, holder_tx);
- append_onchain_update!(res);
+ 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);
}
}
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());
}
}
}
(claim_requests, (commitment_txid, watch_outputs))
}
- /// Used by ChannelManager deserialization to broadcast the latest holder state if its copy of
- /// the Channel was out-of-date. You may use it to get a broadcastable holder toxic tx in case of
- /// fallen-behind, i.e when receiving a channel_reestablish with a proof that our counterparty side knows
- /// a higher revocation secret than the holder commitment number we are aware of. Broadcasting these
- /// transactions are UNSAFE, as they allow counterparty side to punish you. Nevertheless you may want to
- /// broadcast them if counterparty don't close channel with his higher commitment transaction after a
- /// substantial amount of time (a month or even a year) to get back funds. Best may be to contact
- /// out-of-band the other node operator to coordinate with him if option is available to you.
- /// In any-case, choice is up to the user.
pub fn get_latest_holder_commitment_txn<L: Deref>(&mut self, logger: &L) -> Vec<Transaction> where L::Target: Logger {
log_trace!(logger, "Getting signed latest holder commitment transaction!");
self.holder_tx_signed = true;
- if let Some(commitment_tx) = self.onchain_tx_handler.get_fully_signed_holder_tx(&self.funding_redeemscript) {
- let txid = commitment_tx.txid();
- let mut res = vec![commitment_tx];
- for htlc in self.current_holder_commitment_tx.htlc_outputs.iter() {
- if let Some(vout) = htlc.0.transaction_output_index {
- let preimage = if !htlc.0.offered {
- if let Some(preimage) = self.payment_preimages.get(&htlc.0.payment_hash) { Some(preimage.clone()) } else {
- // We can't build an HTLC-Success transaction without the preimage
- continue;
- }
- } else { None };
- if let Some(htlc_tx) = self.onchain_tx_handler.get_fully_signed_htlc_tx(
- &::bitcoin::OutPoint { txid, vout }, &preimage) {
- res.push(htlc_tx);
+ let commitment_tx = self.onchain_tx_handler.get_fully_signed_holder_tx(&self.funding_redeemscript);
+ let txid = commitment_tx.txid();
+ let mut holder_transactions = vec![commitment_tx];
+ for htlc in self.current_holder_commitment_tx.htlc_outputs.iter() {
+ if let Some(vout) = htlc.0.transaction_output_index {
+ let preimage = if !htlc.0.offered {
+ if let Some(preimage) = self.payment_preimages.get(&htlc.0.payment_hash) { Some(preimage.clone()) } else {
+ // We can't build an HTLC-Success transaction without the preimage
+ continue;
}
+ } else if htlc.0.cltv_expiry > self.best_block.height() + 1 {
+ // Don't broadcast HTLC-Timeout transactions immediately as they don't meet the
+ // current locktime requirements on-chain. We will broadcast them in
+ // `block_confirmed` when `would_broadcast_at_height` returns true.
+ // Note that we add + 1 as transactions are broadcastable when they can be
+ // confirmed in the next block.
+ continue;
+ } else { None };
+ if let Some(htlc_tx) = self.onchain_tx_handler.get_fully_signed_htlc_tx(
+ &::bitcoin::OutPoint { txid, vout }, &preimage) {
+ holder_transactions.push(htlc_tx);
}
}
- // We throw away the generated waiting_first_conf data as we aren't (yet) confirmed and we don't actually know what the caller wants to do.
- // The data will be re-generated and tracked in check_spend_holder_transaction if we get a confirmation.
- return res
}
- Vec::new()
+ // We throw away the generated waiting_first_conf data as we aren't (yet) confirmed and we don't actually know what the caller wants to do.
+ // The data will be re-generated and tracked in check_spend_holder_transaction if we get a confirmation.
+ holder_transactions
}
- /// Unsafe test-only version of get_latest_holder_commitment_txn used by our test framework
- /// to bypass HolderCommitmentTransaction state update lockdown after signature and generate
- /// revoked commitment transaction.
#[cfg(any(test,feature = "unsafe_revoked_tx_signing"))]
- pub fn unsafe_get_latest_holder_commitment_txn<L: Deref>(&mut self, logger: &L) -> Vec<Transaction> where L::Target: Logger {
+ /// Note that this includes possibly-locktimed-in-the-future transactions!
+ fn unsafe_get_latest_holder_commitment_txn<L: Deref>(&mut self, logger: &L) -> Vec<Transaction> where L::Target: Logger {
log_trace!(logger, "Getting signed copy of latest holder commitment transaction!");
- if let Some(commitment_tx) = self.onchain_tx_handler.get_fully_signed_copy_holder_tx(&self.funding_redeemscript) {
- let txid = commitment_tx.txid();
- let mut res = vec![commitment_tx];
- for htlc in self.current_holder_commitment_tx.htlc_outputs.iter() {
- if let Some(vout) = htlc.0.transaction_output_index {
- let preimage = if !htlc.0.offered {
- if let Some(preimage) = self.payment_preimages.get(&htlc.0.payment_hash) { Some(preimage.clone()) } else {
- // We can't build an HTLC-Success transaction without the preimage
- continue;
- }
- } else { None };
- if let Some(htlc_tx) = self.onchain_tx_handler.unsafe_get_fully_signed_htlc_tx(
- &::bitcoin::OutPoint { txid, vout }, &preimage) {
- res.push(htlc_tx);
+ let commitment_tx = self.onchain_tx_handler.get_fully_signed_copy_holder_tx(&self.funding_redeemscript);
+ let txid = commitment_tx.txid();
+ let mut holder_transactions = vec![commitment_tx];
+ for htlc in self.current_holder_commitment_tx.htlc_outputs.iter() {
+ if let Some(vout) = htlc.0.transaction_output_index {
+ let preimage = if !htlc.0.offered {
+ if let Some(preimage) = self.payment_preimages.get(&htlc.0.payment_hash) { Some(preimage.clone()) } else {
+ // We can't build an HTLC-Success transaction without the preimage
+ continue;
}
+ } else { None };
+ if let Some(htlc_tx) = self.onchain_tx_handler.unsafe_get_fully_signed_htlc_tx(
+ &::bitcoin::OutPoint { txid, vout }, &preimage) {
+ holder_transactions.push(htlc_tx);
}
}
- return res
}
- Vec::new()
+ holder_transactions
}
- /// Processes transactions in a newly connected block, which may result in any of the following:
- /// - update the monitor's state against resolved HTLCs
- /// - punish the counterparty in the case of seeing a revoked commitment transaction
- /// - force close the channel and claim/timeout incoming/outgoing HTLCs if near expiration
- /// - detect settled outputs for later spending
- /// - schedule and bump any in-flight claims
- ///
- /// Returns any new outputs to watch from `txdata`; after called, these are also included in
- /// [`get_outputs_to_watch`].
- ///
- /// [`get_outputs_to_watch`]: #method.get_outputs_to_watch
- 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<PackageTemplate>,
+ 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 {
+ let funding_outp = HolderFundingOutput::build(self.funding_redeemscript.clone());
+ let commitment_package = PackageTemplate::build_package(self.funding_info.0.txid.clone(), self.funding_info.0.index as u32, PackageSolvingData::HolderFundingOutput(funding_outp), height, false, height);
+ claimable_outpoints.push(commitment_package);
self.pending_monitor_events.push(MonitorEvent::CommitmentTxBroadcasted(self.funding_info.0));
- if let Some(commitment_tx) = self.onchain_tx_handler.get_fully_signed_holder_tx(&self.funding_redeemscript) {
- self.holder_tx_signed = true;
- let (mut new_outpoints, new_outputs, _) = self.broadcast_by_holder_state(&commitment_tx, &self.current_holder_commitment_tx);
- if !new_outputs.is_empty() {
- watch_outputs.push((self.current_holder_commitment_tx.txid.clone(), new_outputs));
- }
- 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]
- });
+ let commitment_tx = self.onchain_tx_handler.get_fully_signed_holder_tx(&self.funding_redeemscript);
+ self.holder_tx_signed = true;
+ let (mut new_outpoints, _) = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx, height);
+ let new_outputs = self.get_broadcasted_holder_watch_outputs(&self.current_holder_commitment_tx, &commitment_tx);
+ if !new_outputs.is_empty() {
+ watch_outputs.push((self.current_holder_commitment_tx.txid.clone(), new_outputs));
+ }
+ claimable_outpoints.append(&mut new_outpoints);
+ }
+
+ // 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 { source, .. } => Some(source),
+ 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 { ref source, payment_hash } => {
+ // Check for duplicate HTLC resolutions.
+ #[cfg(debug_assertions)]
+ {
+ debug_assert!(
+ unmatured_htlcs.iter().find(|&htlc| htlc == &source).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 == source).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(source.clone());
}
+
+ log_trace!(logger, "HTLC {} failure update has got enough confirmations to be passed upstream", log_bytes!(payment_hash.0));
+ self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate {
+ payment_hash: payment_hash,
+ payment_preimage: None,
+ source: source.clone(),
+ }));
+ },
+ 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.block_connected(&txn_matched, claimable_outpoints, height, &*broadcaster, &*fee_estimator, &*logger);
- self.last_block_hash = block_hash;
+ self.onchain_tx_handler.update_claims_view(&txn_matched, claimable_outpoints, height, &&*broadcaster, &&*fee_estimator, &&*logger);
// Determine new outputs to watch by comparing against previously known outputs to watch,
// updating the latter in the process.
let idx_and_scripts = txouts.iter().map(|o| (o.0, o.1.script_pubkey.clone())).collect();
self.outputs_to_watch.insert(txid.clone(), idx_and_scripts).is_none()
});
+ #[cfg(test)]
+ {
+ // If we see a transaction for which we registered outputs previously,
+ // make sure the registered scriptpubkey at the expected index match
+ // the actual transaction output one. We failed this case before #653.
+ for tx in &txn_matched {
+ if let Some(outputs) = self.get_outputs_to_watch().get(&tx.txid()) {
+ for idx_and_script in outputs.iter() {
+ assert!((idx_and_script.0 as usize) < tx.output.len());
+ assert_eq!(tx.output[idx_and_script.0 as usize].script_pubkey, idx_and_script.1);
+ }
+ }
+ }
+ }
watch_outputs
}
- /// Determines if the disconnected block contained any transactions of interest and updates
- /// appropriately.
pub fn block_disconnected<B: Deref, F: Deref, L: Deref>(&mut self, header: &BlockHeader, height: u32, broadcaster: B, fee_estimator: F, logger: L)
where B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
L::Target: Logger,
{
- let block_hash = header.block_hash();
- log_trace!(logger, "Block {} at height {} disconnected", block_hash, height);
+ 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 = block_hash;
+ 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
if let Some(outputs) = self.get_outputs_to_watch().get(&input.previous_output.txid) {
for (idx, _script_pubkey) in outputs.iter() {
if *idx == input.previous_output.vout {
+ #[cfg(test)]
+ {
+ // If the expected script is a known type, check that the witness
+ // appears to be spending the correct type (ie that the match would
+ // actually succeed in BIP 158/159-style filters).
+ if _script_pubkey.is_v0_p2wsh() {
+ assert_eq!(&bitcoin::Address::p2wsh(&Script::from(input.witness.last().unwrap().clone()), bitcoin::Network::Bitcoin).script_pubkey(), _script_pubkey);
+ } else if _script_pubkey.is_v0_p2wpkh() {
+ assert_eq!(&bitcoin::Address::p2wpkh(&bitcoin::PublicKey::from_slice(&input.witness.last().unwrap()).unwrap(), bitcoin::Network::Bitcoin).unwrap().script_pubkey(), _script_pubkey);
+ } else { panic!(); }
+ }
return true;
}
}
}));
}
} 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 { source: ref htlc_source, .. } => {
+ *htlc_source != source
+ },
+ _ => true,
}
- }
+ });
+ let entry = OnchainEventEntry {
+ txid: tx.txid(),
+ height,
+ event: OnchainEvent::HTLCUpdate { source: source, payment_hash: 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);
}
}
}
fn is_paying_spendable_output<L: Deref>(&mut self, tx: &Transaction, height: u32, 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 > ::std::u16::MAX as usize {
+ if i > ::core::u16::MAX as usize {
// While it is possible that an output exists on chain which is greater than the
// 2^16th output in a given transaction, this is only possible if the output is not
// in a lightning transaction and was instead placed there by some third party who
break;
} else if let Some(ref broadcasted_holder_revokable_script) = self.broadcasted_holder_revokable_script {
if broadcasted_holder_revokable_script.0 == outp.script_pubkey {
- spendable_output = Some(SpendableOutputDescriptor::DynamicOutputP2WSH {
+ spendable_output = Some(SpendableOutputDescriptor::DelayedPaymentOutput(DelayedPaymentOutputDescriptor {
outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
per_commitment_point: broadcasted_holder_revokable_script.1,
to_self_delay: self.on_holder_tx_csv,
output: outp.clone(),
- key_derivation_params: self.keys.key_derivation_params(),
revocation_pubkey: broadcasted_holder_revokable_script.2.clone(),
- });
+ channel_keys_id: self.channel_keys_id,
+ channel_value_satoshis: self.channel_value_satoshis,
+ }));
break;
}
} else if self.counterparty_payment_script == outp.script_pubkey {
- spendable_output = Some(SpendableOutputDescriptor::StaticOutputCounterpartyPayment {
+ spendable_output = Some(SpendableOutputDescriptor::StaticPaymentOutput(StaticPaymentOutputDescriptor {
outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
output: outp.clone(),
- key_derivation_params: self.keys.key_derivation_params(),
- });
+ channel_keys_id: self.channel_keys_id,
+ channel_value_satoshis: self.channel_value_satoshis,
+ }));
break;
} else if outp.script_pubkey == self.shutdown_script {
spendable_output = Some(SpendableOutputDescriptor::StaticOutput {
}
}
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);
}
}
}
+/// `Persist` defines behavior for persisting channel monitors: this could mean
+/// writing once to disk, and/or uploading to one or more backup services.
+///
+/// Note that for every new monitor, you **must** persist the new `ChannelMonitor`
+/// to disk/backups. And, on every update, you **must** persist either the
+/// `ChannelMonitorUpdate` or the updated monitor itself. Otherwise, there is risk
+/// of situations such as revoking a transaction, then crashing before this
+/// revocation can be persisted, then unintentionally broadcasting a revoked
+/// 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> {
+ /// 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::write`] for writing out a `ChannelMonitor`,
+ /// and [`ChannelMonitorUpdateErr`] for requirements when returning errors.
+ fn persist_new_channel(&self, id: OutPoint, data: &ChannelMonitor<ChannelSigner>) -> Result<(), ChannelMonitorUpdateErr>;
+
+ /// Update one channel's data. The provided `ChannelMonitor` has already
+ /// applied the given update.
+ ///
+ /// Note that on every update, you **must** persist either the
+ /// `ChannelMonitorUpdate` or the updated monitor itself to disk/backups. See
+ /// the `Persist` trait documentation for more details.
+ ///
+ /// If an implementer chooses to persist the updates only, they need to make
+ /// sure that all the updates are applied to the `ChannelMonitors` *before*
+ /// the set of channel monitors is given to the `ChannelManager`
+ /// deserialization routine. See [`ChannelMonitor::update_monitor`] for
+ /// applying a monitor update to a monitor. If full `ChannelMonitors` are
+ /// persisted, then there is no need to persist individual updates.
+ ///
+ /// Note that there could be a performance tradeoff between persisting complete
+ /// channel monitors on every update vs. persisting only updates and applying
+ /// them in batches. The size of each monitor grows `O(number of state updates)`
+ /// whereas updates are small and `O(1)`.
+ ///
+ /// See [`ChannelMonitor::write`] for writing out a `ChannelMonitor`,
+ /// [`ChannelMonitorUpdate::write`] for writing out an update, and
+ /// [`ChannelMonitorUpdateErr`] for requirements when returning errors.
+ fn update_persisted_channel(&self, id: OutPoint, update: &ChannelMonitorUpdate, data: &ChannelMonitor<ChannelSigner>) -> Result<(), ChannelMonitorUpdateErr>;
+}
+
+impl<Signer: Sign, T: Deref, F: Deref, L: Deref> chain::Listen for (ChannelMonitor<Signer>, T, F, L)
+where
+ T::Target: BroadcasterInterface,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
+{
+ fn block_connected(&self, block: &Block, height: u32) {
+ let txdata: Vec<_> = block.txdata.iter().enumerate().collect();
+ self.0.block_connected(&block.header, &txdata, height, &*self.1, &*self.2, &*self.3);
+ }
+
+ fn block_disconnected(&self, header: &BlockHeader, height: u32) {
+ self.0.block_disconnected(header, height, &*self.1, &*self.2, &*self.3);
+ }
+}
+
+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<ChanSigner: ChannelKeys + Readable> Readable for (BlockHash, ChannelMonitor<ChanSigner>) {
- fn read<R: ::std::io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
+impl<'a, Signer: Sign, K: KeysInterface<Signer = Signer>> ReadableArgs<&'a K>
+ for (BlockHash, ChannelMonitor<Signer>) {
+ fn read<R: ::std::io::Read>(reader: &mut R, keys_manager: &'a K) -> Result<Self, DecodeError> {
macro_rules! unwrap_obj {
($key: expr) => {
match $key {
}
}
- let _ver: u8 = Readable::read(reader)?;
- let min_ver: u8 = Readable::read(reader)?;
- if min_ver > SERIALIZATION_VERSION {
- return Err(DecodeError::UnknownVersion);
- }
+ let _ver = read_ver_prefix!(reader, SERIALIZATION_VERSION);
let latest_update_id: u64 = Readable::read(reader)?;
let commitment_transaction_number_obscure_factor = <U48 as Readable>::read(reader)?.0;
let counterparty_payment_script = Readable::read(reader)?;
let shutdown_script = Readable::read(reader)?;
- let keys = Readable::read(reader)?;
+ let channel_keys_id = Readable::read(reader)?;
+ let holder_revocation_basepoint = Readable::read(reader)?;
// Technically this can fail and serialize fail a round-trip, but only for serialization of
// barely-init'd ChannelMonitors that we can't do anything with.
let outpoint = OutPoint {
}
}
- macro_rules! read_holder_tx {
- () => {
- {
- let txid = Readable::read(reader)?;
- let revocation_key = Readable::read(reader)?;
- let a_htlc_key = Readable::read(reader)?;
- let b_htlc_key = Readable::read(reader)?;
- let delayed_payment_key = Readable::read(reader)?;
- let per_commitment_point = Readable::read(reader)?;
- let feerate_per_kw: u32 = Readable::read(reader)?;
-
- let htlcs_len: u64 = Readable::read(reader)?;
- let mut htlcs = Vec::with_capacity(cmp::min(htlcs_len as usize, MAX_ALLOC_SIZE / 128));
- for _ in 0..htlcs_len {
- let htlc = read_htlc_in_commitment!();
- let sigs = match <u8 as Readable>::read(reader)? {
- 0 => None,
- 1 => Some(Readable::read(reader)?),
- _ => return Err(DecodeError::InvalidValue),
- };
- htlcs.push((htlc, sigs, Readable::read(reader)?));
- }
-
- HolderSignedTx {
- txid,
- revocation_key, a_htlc_key, b_htlc_key, delayed_payment_key, per_commitment_point, feerate_per_kw,
- htlc_outputs: htlcs
- }
- }
- }
- }
-
let prev_holder_signed_commitment_tx = match <u8 as Readable>::read(reader)? {
0 => None,
1 => {
- Some(read_holder_tx!())
+ Some(Readable::read(reader)?)
},
_ => return Err(DecodeError::InvalidValue),
};
- let current_holder_commitment_tx = read_holder_tx!();
+ let current_holder_commitment_tx = Readable::read(reader)?;
let current_counterparty_commitment_number = <U48 as Readable>::read(reader)?.0;
let current_holder_commitment_number = <U48 as Readable>::read(reader)?.0;
}
}
- 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);
+ onchain_events_awaiting_threshold_conf.push(Readable::read(reader)?);
}
let outputs_to_watch_len: u64 = Readable::read(reader)?;
return Err(DecodeError::InvalidValue);
}
}
- let onchain_tx_handler = Readable::read(reader)?;
+ let onchain_tx_handler = ReadableArgs::read(reader, keys_manager)?;
let lockdown_from_offchain = Readable::read(reader)?;
let holder_tx_signed = Readable::read(reader)?;
- Ok((last_block_hash.clone(), ChannelMonitor {
- latest_update_id,
- commitment_transaction_number_obscure_factor,
+ read_tlv_fields!(reader, {}, {});
+
+ let mut secp_ctx = Secp256k1::new();
+ secp_ctx.seeded_randomize(&keys_manager.get_secure_random_bytes());
+
+ Ok((best_block.block_hash(), ChannelMonitor {
+ inner: Mutex::new(ChannelMonitorImpl {
+ latest_update_id,
+ commitment_transaction_number_obscure_factor,
- destination_script,
- broadcasted_holder_revokable_script,
- counterparty_payment_script,
- shutdown_script,
+ destination_script,
+ broadcasted_holder_revokable_script,
+ counterparty_payment_script,
+ shutdown_script,
- keys,
- funding_info,
- current_counterparty_commitment_txid,
- prev_counterparty_commitment_txid,
+ channel_keys_id,
+ holder_revocation_basepoint,
+ funding_info,
+ current_counterparty_commitment_txid,
+ prev_counterparty_commitment_txid,
- counterparty_tx_cache,
- funding_redeemscript,
- channel_value_satoshis,
- their_cur_revocation_points,
+ counterparty_tx_cache,
+ funding_redeemscript,
+ channel_value_satoshis,
+ their_cur_revocation_points,
- on_holder_tx_csv,
+ on_holder_tx_csv,
- commitment_secrets,
- counterparty_claimable_outpoints,
- counterparty_commitment_txn_on_chain,
- counterparty_hash_commitment_number,
+ commitment_secrets,
+ counterparty_claimable_outpoints,
+ counterparty_commitment_txn_on_chain,
+ counterparty_hash_commitment_number,
- prev_holder_signed_commitment_tx,
- current_holder_commitment_tx,
- current_counterparty_commitment_number,
- current_holder_commitment_number,
+ prev_holder_signed_commitment_tx,
+ current_holder_commitment_tx,
+ current_counterparty_commitment_number,
+ current_holder_commitment_number,
- payment_preimages,
- pending_monitor_events,
- pending_events,
+ payment_preimages,
+ pending_monitor_events,
+ pending_events,
- onchain_events_waiting_threshold_conf,
- outputs_to_watch,
+ onchain_events_awaiting_threshold_conf,
+ outputs_to_watch,
- onchain_tx_handler,
+ onchain_tx_handler,
- lockdown_from_offchain,
- holder_tx_signed,
+ lockdown_from_offchain,
+ holder_tx_signed,
- last_block_hash,
- secp_ctx: Secp256k1::new(),
+ best_block,
+
+ secp_ctx,
+ }),
}))
}
}
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::hex::FromHex;
use bitcoin::hash_types::Txid;
+ use bitcoin::network::constants::Network;
use hex;
use chain::channelmonitor::ChannelMonitor;
+ use chain::package::{WEIGHT_OFFERED_HTLC, WEIGHT_RECEIVED_HTLC, WEIGHT_REVOKED_OFFERED_HTLC, WEIGHT_REVOKED_RECEIVED_HTLC, WEIGHT_REVOKED_OUTPUT};
use chain::transaction::OutPoint;
- use ln::channelmanager::{PaymentPreimage, PaymentHash};
- use ln::onchaintx::{OnchainTxHandler, InputDescriptors};
+ use ln::{PaymentPreimage, PaymentHash};
+ use ln::channelmanager::BestBlock;
use ln::chan_utils;
- use ln::chan_utils::{HTLCOutputInCommitment, HolderCommitmentTransaction};
- use util::test_utils::TestLogger;
+ use ln::chan_utils::{HTLCOutputInCommitment, ChannelPublicKeys, ChannelTransactionParameters, HolderCommitmentTransaction, CounterpartyChannelTransactionParameters};
+ use util::test_utils::{TestLogger, TestBroadcaster, TestFeeEstimator};
use bitcoin::secp256k1::key::{SecretKey,PublicKey};
use bitcoin::secp256k1::Secp256k1;
- use std::sync::Arc;
- use chain::keysinterface::InMemoryChannelKeys;
+ use std::sync::{Arc, Mutex};
+ use chain::keysinterface::InMemorySigner;
+ use prelude::*;
#[test]
fn test_prune_preimages() {
let secp_ctx = Secp256k1::new();
let logger = Arc::new(TestLogger::new());
+ let broadcaster = Arc::new(TestBroadcaster{txn_broadcasted: Mutex::new(Vec::new()), blocks: Arc::new(Mutex::new(Vec::new()))});
+ let fee_estimator = Arc::new(TestFeeEstimator { sat_per_kw: 253 });
let dummy_key = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let dummy_tx = Transaction { version: 0, lock_time: 0, input: Vec::new(), output: Vec::new() };
macro_rules! test_preimages_exist {
($preimages_slice: expr, $monitor: expr) => {
for preimage in $preimages_slice {
- assert!($monitor.payment_preimages.contains_key(&preimage.1));
+ assert!($monitor.inner.lock().unwrap().payment_preimages.contains_key(&preimage.1));
}
}
}
- let keys = InMemoryChannelKeys::new(
+ let keys = InMemorySigner::new(
&secp_ctx,
SecretKey::from_slice(&[41; 32]).unwrap(),
SecretKey::from_slice(&[41; 32]).unwrap(),
SecretKey::from_slice(&[41; 32]).unwrap(),
[41; 32],
0,
- (0, 0)
+ [0; 32]
);
+ let counterparty_pubkeys = ChannelPublicKeys {
+ funding_pubkey: PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[44; 32]).unwrap()),
+ revocation_basepoint: PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[45; 32]).unwrap()),
+ payment_point: PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[46; 32]).unwrap()),
+ delayed_payment_basepoint: PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[47; 32]).unwrap()),
+ htlc_basepoint: PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[48; 32]).unwrap())
+ };
+ let funding_outpoint = OutPoint { txid: Default::default(), index: u16::max_value() };
+ let channel_parameters = ChannelTransactionParameters {
+ holder_pubkeys: keys.holder_channel_pubkeys.clone(),
+ holder_selected_contest_delay: 66,
+ is_outbound_from_holder: true,
+ counterparty_parameters: Some(CounterpartyChannelTransactionParameters {
+ pubkeys: counterparty_pubkeys,
+ selected_contest_delay: 67,
+ }),
+ funding_outpoint: Some(funding_outpoint),
+ };
// Prune with one old state and a holder commitment tx holding a few overlaps with the
// old state.
- let mut monitor = ChannelMonitor::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()),
- &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[44; 32]).unwrap()),
- &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[45; 32]).unwrap()),
- 10, Script::new(), 46, 0, HolderCommitmentTransaction::dummy());
-
- monitor.provide_latest_holder_commitment_tx_info(HolderCommitmentTransaction::dummy(), preimages_to_holder_htlcs!(preimages[0..10])).unwrap();
- monitor.provide_latest_counterparty_commitment_tx_info(&dummy_tx, preimages_slice_to_htlc_outputs!(preimages[5..15]), 281474976710655, dummy_key, &logger);
- monitor.provide_latest_counterparty_commitment_tx_info(&dummy_tx, preimages_slice_to_htlc_outputs!(preimages[15..20]), 281474976710654, dummy_key, &logger);
- monitor.provide_latest_counterparty_commitment_tx_info(&dummy_tx, preimages_slice_to_htlc_outputs!(preimages[17..20]), 281474976710653, dummy_key, &logger);
- monitor.provide_latest_counterparty_commitment_tx_info(&dummy_tx, preimages_slice_to_htlc_outputs!(preimages[18..20]), 281474976710652, dummy_key, &logger);
+ 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(), best_block);
+
+ monitor.provide_latest_holder_commitment_tx(HolderCommitmentTransaction::dummy(), preimages_to_holder_htlcs!(preimages[0..10])).unwrap();
+ let dummy_txid = dummy_tx.txid();
+ monitor.provide_latest_counterparty_commitment_tx(dummy_txid, preimages_slice_to_htlc_outputs!(preimages[5..15]), 281474976710655, dummy_key, &logger);
+ monitor.provide_latest_counterparty_commitment_tx(dummy_txid, preimages_slice_to_htlc_outputs!(preimages[15..20]), 281474976710654, dummy_key, &logger);
+ monitor.provide_latest_counterparty_commitment_tx(dummy_txid, preimages_slice_to_htlc_outputs!(preimages[17..20]), 281474976710653, dummy_key, &logger);
+ monitor.provide_latest_counterparty_commitment_tx(dummy_txid, preimages_slice_to_htlc_outputs!(preimages[18..20]), 281474976710652, dummy_key, &logger);
for &(ref preimage, ref hash) in preimages.iter() {
- monitor.provide_payment_preimage(hash, preimage);
+ monitor.provide_payment_preimage(hash, preimage, &broadcaster, &fee_estimator, &logger);
}
// Now provide a secret, pruning preimages 10-15
let mut secret = [0; 32];
secret[0..32].clone_from_slice(&hex::decode("7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc").unwrap());
monitor.provide_secret(281474976710655, secret.clone()).unwrap();
- assert_eq!(monitor.payment_preimages.len(), 15);
+ assert_eq!(monitor.inner.lock().unwrap().payment_preimages.len(), 15);
test_preimages_exist!(&preimages[0..10], monitor);
test_preimages_exist!(&preimages[15..20], monitor);
// Now provide a further secret, pruning preimages 15-17
secret[0..32].clone_from_slice(&hex::decode("c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964").unwrap());
monitor.provide_secret(281474976710654, secret.clone()).unwrap();
- assert_eq!(monitor.payment_preimages.len(), 13);
+ assert_eq!(monitor.inner.lock().unwrap().payment_preimages.len(), 13);
test_preimages_exist!(&preimages[0..10], monitor);
test_preimages_exist!(&preimages[17..20], monitor);
// Now update holder commitment tx info, pruning only element 18 as we still care about the
// previous commitment tx's preimages too
- monitor.provide_latest_holder_commitment_tx_info(HolderCommitmentTransaction::dummy(), preimages_to_holder_htlcs!(preimages[0..5])).unwrap();
+ monitor.provide_latest_holder_commitment_tx(HolderCommitmentTransaction::dummy(), preimages_to_holder_htlcs!(preimages[0..5])).unwrap();
secret[0..32].clone_from_slice(&hex::decode("2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8").unwrap());
monitor.provide_secret(281474976710653, secret.clone()).unwrap();
- assert_eq!(monitor.payment_preimages.len(), 12);
+ assert_eq!(monitor.inner.lock().unwrap().payment_preimages.len(), 12);
test_preimages_exist!(&preimages[0..10], monitor);
test_preimages_exist!(&preimages[18..20], monitor);
// But if we do it again, we'll prune 5-10
- monitor.provide_latest_holder_commitment_tx_info(HolderCommitmentTransaction::dummy(), preimages_to_holder_htlcs!(preimages[0..3])).unwrap();
+ monitor.provide_latest_holder_commitment_tx(HolderCommitmentTransaction::dummy(), preimages_to_holder_htlcs!(preimages[0..3])).unwrap();
secret[0..32].clone_from_slice(&hex::decode("27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116").unwrap());
monitor.provide_secret(281474976710652, secret.clone()).unwrap();
- assert_eq!(monitor.payment_preimages.len(), 5);
+ assert_eq!(monitor.inner.lock().unwrap().payment_preimages.len(), 5);
test_preimages_exist!(&preimages[0..5], monitor);
}
let mut sum_actual_sigs = 0;
macro_rules! sign_input {
- ($sighash_parts: expr, $idx: expr, $amount: expr, $input_type: expr, $sum_actual_sigs: expr) => {
+ ($sighash_parts: expr, $idx: expr, $amount: expr, $weight: expr, $sum_actual_sigs: expr) => {
let htlc = HTLCOutputInCommitment {
- offered: if *$input_type == InputDescriptors::RevokedOfferedHTLC || *$input_type == InputDescriptors::OfferedHTLC { true } else { false },
+ offered: if *$weight == WEIGHT_REVOKED_OFFERED_HTLC || *$weight == WEIGHT_OFFERED_HTLC { true } else { false },
amount_msat: 0,
cltv_expiry: 2 << 16,
payment_hash: PaymentHash([1; 32]),
transaction_output_index: Some($idx as u32),
};
- let redeem_script = if *$input_type == InputDescriptors::RevokedOutput { chan_utils::get_revokeable_redeemscript(&pubkey, 256, &pubkey) } else { chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, &pubkey, &pubkey, &pubkey) };
+ let redeem_script = if *$weight == WEIGHT_REVOKED_OUTPUT { chan_utils::get_revokeable_redeemscript(&pubkey, 256, &pubkey) } else { chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, &pubkey, &pubkey, &pubkey) };
let sighash = hash_to_message!(&$sighash_parts.signature_hash($idx, &redeem_script, $amount, SigHashType::All)[..]);
let sig = secp_ctx.sign(&sighash, &privkey);
$sighash_parts.access_witness($idx).push(sig.serialize_der().to_vec());
$sighash_parts.access_witness($idx)[0].push(SigHashType::All as u8);
sum_actual_sigs += $sighash_parts.access_witness($idx)[0].len();
- if *$input_type == InputDescriptors::RevokedOutput {
+ if *$weight == WEIGHT_REVOKED_OUTPUT {
$sighash_parts.access_witness($idx).push(vec!(1));
- } else if *$input_type == InputDescriptors::RevokedOfferedHTLC || *$input_type == InputDescriptors::RevokedReceivedHTLC {
+ } else if *$weight == WEIGHT_REVOKED_OFFERED_HTLC || *$weight == WEIGHT_REVOKED_RECEIVED_HTLC {
$sighash_parts.access_witness($idx).push(pubkey.clone().serialize().to_vec());
- } else if *$input_type == InputDescriptors::ReceivedHTLC {
+ } else if *$weight == WEIGHT_RECEIVED_HTLC {
$sighash_parts.access_witness($idx).push(vec![0]);
} else {
$sighash_parts.access_witness($idx).push(PaymentPreimage([1; 32]).0.to_vec());
value: 0,
});
let base_weight = claim_tx.get_weight();
- let inputs_des = vec![InputDescriptors::RevokedOutput, InputDescriptors::RevokedOfferedHTLC, InputDescriptors::RevokedOfferedHTLC, InputDescriptors::RevokedReceivedHTLC];
+ let inputs_weight = vec![WEIGHT_REVOKED_OUTPUT, WEIGHT_REVOKED_OFFERED_HTLC, WEIGHT_REVOKED_OFFERED_HTLC, WEIGHT_REVOKED_RECEIVED_HTLC];
+ let mut inputs_total_weight = 2; // count segwit flags
{
let mut sighash_parts = bip143::SigHashCache::new(&mut claim_tx);
- for (idx, inp) in inputs_des.iter().enumerate() {
+ for (idx, inp) in inputs_weight.iter().enumerate() {
sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs);
+ inputs_total_weight += inp;
}
}
- assert_eq!(base_weight + OnchainTxHandler::<InMemoryChannelKeys>::get_witnesses_weight(&inputs_des[..]), claim_tx.get_weight() + /* max_length_sig */ (73 * inputs_des.len() - sum_actual_sigs));
+ assert_eq!(base_weight + inputs_total_weight as usize, claim_tx.get_weight() + /* max_length_sig */ (73 * inputs_weight.len() - sum_actual_sigs));
// Claim tx with 1 offered HTLCs, 3 received HTLCs
claim_tx.input.clear();
});
}
let base_weight = claim_tx.get_weight();
- let inputs_des = vec![InputDescriptors::OfferedHTLC, InputDescriptors::ReceivedHTLC, InputDescriptors::ReceivedHTLC, InputDescriptors::ReceivedHTLC];
+ let inputs_weight = vec![WEIGHT_OFFERED_HTLC, WEIGHT_RECEIVED_HTLC, WEIGHT_RECEIVED_HTLC, WEIGHT_RECEIVED_HTLC];
+ let mut inputs_total_weight = 2; // count segwit flags
{
let mut sighash_parts = bip143::SigHashCache::new(&mut claim_tx);
- for (idx, inp) in inputs_des.iter().enumerate() {
+ for (idx, inp) in inputs_weight.iter().enumerate() {
sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs);
+ inputs_total_weight += inp;
}
}
- assert_eq!(base_weight + OnchainTxHandler::<InMemoryChannelKeys>::get_witnesses_weight(&inputs_des[..]), claim_tx.get_weight() + /* max_length_sig */ (73 * inputs_des.len() - sum_actual_sigs));
+ assert_eq!(base_weight + inputs_total_weight as usize, claim_tx.get_weight() + /* max_length_sig */ (73 * inputs_weight.len() - sum_actual_sigs));
// Justice tx with 1 revoked HTLC-Success tx output
claim_tx.input.clear();
witness: Vec::new(),
});
let base_weight = claim_tx.get_weight();
- let inputs_des = vec![InputDescriptors::RevokedOutput];
+ let inputs_weight = vec![WEIGHT_REVOKED_OUTPUT];
+ let mut inputs_total_weight = 2; // count segwit flags
{
let mut sighash_parts = bip143::SigHashCache::new(&mut claim_tx);
- for (idx, inp) in inputs_des.iter().enumerate() {
+ for (idx, inp) in inputs_weight.iter().enumerate() {
sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs);
+ inputs_total_weight += inp;
}
}
- assert_eq!(base_weight + OnchainTxHandler::<InMemoryChannelKeys>::get_witnesses_weight(&inputs_des[..]), claim_tx.get_weight() + /* max_length_isg */ (73 * inputs_des.len() - sum_actual_sigs));
+ assert_eq!(base_weight + inputs_total_weight as usize, claim_tx.get_weight() + /* max_length_isg */ (73 * inputs_weight.len() - sum_actual_sigs));
}
// Further testing is done in the ChannelManager integration tests.