X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fchain%2Fchannelmonitor.rs;h=d1e6e8114e720816416da16e36064d153c569c7b;hb=697033342e3f5cb8bb524843c409ede8a39368c1;hp=939337d7b42d572d64c7ab5b1a6224479f6c1ce9;hpb=e23c270720df2798c3e35e2ba804d98060d76d17;p=rust-lightning diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index 939337d7..d1e6e811 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -22,7 +22,6 @@ 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; @@ -34,26 +33,28 @@ use bitcoin::secp256k1::{Secp256k1,Signature}; use bitcoin::secp256k1::key::{SecretKey,PublicKey}; use bitcoin::secp256k1; +use ln::{PaymentHash, PaymentPreimage}; use ln::msgs::DecodeError; use ln::chan_utils; use ln::chan_utils::{CounterpartyCommitmentSecrets, HTLCOutputInCommitment, HTLCType, ChannelTransactionParameters, HolderCommitmentTransaction}; -use ln::channelmanager::{HTLCSource, PaymentPreimage, PaymentHash}; -use ln::onchaintx::{OnchainTxHandler, InputDescriptors}; +use ln::channelmanager::{BestBlock, HTLCSource}; use chain; use chain::WatchedOutput; use chain::chaininterface::{BroadcasterInterface, FeeEstimator}; use chain::transaction::{OutPoint, TransactionData}; use chain::keysinterface::{SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, Sign, KeysInterface}; +use chain::onchaintx::OnchainTxHandler; +use chain::package::{CounterpartyOfferedHTLCOutput, CounterpartyReceivedHTLCOutput, HolderFundingOutput, HolderHTLCOutput, PackageSolvingData, PackageTemplate, RevokedOutput, RevokedHTLCOutput}; use chain::Filter; use util::logger::Logger; -use util::ser::{Readable, ReadableArgs, MaybeReadable, Writer, Writeable, U48}; +use util::ser::{Readable, ReadableArgs, MaybeReadable, Writer, Writeable, U48, OptionDeserWrapper}; use util::byte_utils; use util::events::Event; -use std::collections::{HashMap, HashSet, hash_map}; -use std::{cmp, mem}; +use prelude::*; +use core::{cmp, mem}; use std::io::Error; -use std::ops::Deref; +use core::ops::Deref; use std::sync::Mutex; /// An update generated by the underlying Channel itself which contains some new information the @@ -84,26 +85,30 @@ pub struct ChannelMonitorUpdate { /// 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 = std::u64::MAX; +pub const CLOSED_CHANNEL_UPDATE_ID: u64 = core::u64::MAX; impl Writeable for ChannelMonitorUpdate { fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + write_ver_prefix!(w, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION); self.update_id.write(w)?; (self.updates.len() as u64).write(w)?; for update_step in self.updates.iter() { update_step.write(w)?; } + write_tlv_fields!(w, {}); Ok(()) } } impl Readable for ChannelMonitorUpdate { fn read(r: &mut R) -> Result { + let _ver = read_ver_prefix!(r, SERIALIZATION_VERSION); 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::())); + let mut updates = Vec::with_capacity(cmp::min(len as usize, MAX_ALLOC_SIZE / ::core::mem::size_of::())); for _ in 0..len { updates.push(Readable::read(r)?); } + read_tlv_fields!(r, {}); Ok(Self { update_id, updates }) } } @@ -196,7 +201,11 @@ pub struct HTLCUpdate { pub(crate) payment_preimage: Option, pub(crate) source: HTLCSource } -impl_writeable!(HTLCUpdate, 0, { payment_hash, payment_preimage, source }); +impl_writeable_tlv_based!(HTLCUpdate, { + (0, payment_hash, required), + (2, source, required), + (4, payment_preimage, option), +}); /// 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. @@ -205,7 +214,7 @@ pub(crate) const CLTV_SHARED_CLAIM_BUFFER: u32 = 12; /// 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 @@ -221,11 +230,11 @@ pub(crate) const CLTV_CLAIM_BUFFER: u32 = 6; 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. /// @@ -262,6 +271,16 @@ struct HolderSignedTx { feerate_per_kw: u32, htlc_outputs: Vec<(HTLCOutputInCommitment, Option, Option)>, } +impl_writeable_tlv_based!(HolderSignedTx, { + (0, txid, required), + (2, revocation_key, required), + (4, a_htlc_key, required), + (6, b_htlc_key, required), + (8, delayed_payment_key, required), + (10, per_commitment_point, required), + (12, feerate_per_kw, required), + (14, htlc_outputs, vec_type) +}); /// We use this to track counterparty commitment transactions and htlcs outputs and /// use it to generate any justice or 2nd-stage preimage/timeout transactions. @@ -275,9 +294,6 @@ struct CounterpartyCommitmentTransaction { impl Writeable for CounterpartyCommitmentTransaction { fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { - self.counterparty_delayed_payment_base_key.write(w)?; - self.counterparty_htlc_base_key.write(w)?; - w.write_all(&byte_utils::be16_to_array(self.on_counterparty_tx_csv))?; w.write_all(&byte_utils::be64_to_array(self.per_htlc.len() as u64))?; for (ref txid, ref htlcs) in self.per_htlc.iter() { w.write_all(&txid[..])?; @@ -286,15 +302,17 @@ impl Writeable for CounterpartyCommitmentTransaction { htlc.write(w)?; } } + write_tlv_fields!(w, { + (0, self.counterparty_delayed_payment_base_key, required), + (2, self.counterparty_htlc_base_key, required), + (4, self.on_counterparty_tx_csv, required), + }); Ok(()) } } impl Readable for CounterpartyCommitmentTransaction { fn read(r: &mut R) -> Result { let counterparty_commitment_transaction = { - let counterparty_delayed_payment_base_key = Readable::read(r)?; - let counterparty_htlc_base_key = Readable::read(r)?; - let on_counterparty_tx_csv: u16 = Readable::read(r)?; let per_htlc_len: u64 = Readable::read(r)?; let mut per_htlc = HashMap::with_capacity(cmp::min(per_htlc_len as usize, MAX_ALLOC_SIZE / 64)); for _ in 0..per_htlc_len { @@ -309,9 +327,17 @@ impl Readable for CounterpartyCommitmentTransaction { return Err(DecodeError::InvalidValue); } } + let mut counterparty_delayed_payment_base_key = OptionDeserWrapper(None); + let mut counterparty_htlc_base_key = OptionDeserWrapper(None); + let mut on_counterparty_tx_csv: u16 = 0; + read_tlv_fields!(r, { + (0, counterparty_delayed_payment_base_key, required), + (2, counterparty_htlc_base_key, required), + (4, on_counterparty_tx_csv, required), + }); CounterpartyCommitmentTransaction { - counterparty_delayed_payment_base_key, - counterparty_htlc_base_key, + counterparty_delayed_payment_base_key: counterparty_delayed_payment_base_key.0.unwrap(), + counterparty_htlc_base_key: counterparty_htlc_base_key.0.unwrap(), on_counterparty_tx_csv, per_htlc, } @@ -320,168 +346,66 @@ impl Readable for CounterpartyCommitmentTransaction { } } -/// 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, - on_counterparty_tx_csv: u16, - }, - CounterpartyHTLC { - per_commitment_point: PublicKey, - counterparty_delayed_payment_base_key: PublicKey, - counterparty_htlc_base_key: PublicKey, - preimage: Option, - htlc: HTLCOutputInCommitment - }, - HolderHTLC { - preimage: Option, - 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(&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)?; - } +impl OnchainEventEntry { + fn confirmation_threshold(&self) -> u32 { + let mut conf_threshold = self.height + ANTI_REORG_DELAY - 1; + if let OnchainEvent::MaturingOutput { + descriptor: SpendableOutputDescriptor::DelayedPaymentOutput(ref descriptor) + } = self.event { + // A CSV'd transaction is confirmable in block (input height) + CSV delay, which means + // it's broadcastable when we see the previous block. + conf_threshold = cmp::max(conf_threshold, self.height + descriptor.to_self_delay as u32 - 1); } - Ok(()) + conf_threshold } -} -impl Readable for InputMaterial { - fn read(reader: &mut R) -> Result { - let input_material = match ::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; +impl_writeable_tlv_based!(OnchainEventEntry, { + (0, txid, required), + (2, height, required), + (4, event, required), +}); + +impl_writeable_tlv_based_enum!(OnchainEvent, + (0, HTLCUpdate) => { + (0, source, required), + (2, payment_hash, required), + }, + (1, MaturingOutput) => { + (0, descriptor, required), + }, +;); #[cfg_attr(any(test, feature = "fuzztarget", feature = "_test_utils"), derive(PartialEq))] #[derive(Clone)] @@ -512,98 +436,28 @@ pub(crate) enum ChannelMonitorUpdateStep { }, } -impl Writeable for ChannelMonitorUpdateStep { - fn write(&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 { commitment_txid, ref htlc_outputs, ref commitment_number, ref their_revocation_point } => { - 1u8.write(w)?; - commitment_txid.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: &mut R) -> Result { - 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 { - commitment_txid: 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)?, 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, required), + (2, htlc_outputs, vec_type), + }, + (1, LatestCounterpartyCommitmentTXInfo) => { + (0, commitment_txid, required), + (2, commitment_number, required), + (4, their_revocation_point, required), + (6, htlc_outputs, vec_type), + }, + (2, PaymentPreimage) => { + (0, payment_preimage, required), + }, + (3, CommitmentSecret) => { + (0, idx, required), + (2, secret, required), + }, + (4, ChannelForceClosed) => { + (0, should_broadcast, required), + }, +;); /// A ChannelMonitor handles chain events (blocks connected and disconnected) and generates /// on-chain transactions to ensure no loss of funds occurs. @@ -684,10 +538,10 @@ pub(crate) struct ChannelMonitorImpl { pending_monitor_events: Vec, pending_events: Vec, - // 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>, + // 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, // 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 @@ -714,15 +568,19 @@ pub(crate) struct ChannelMonitorImpl { // 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, //TODO: dedup this a bit... } +/// Transaction outputs to watch for on-chain spends. +pub type TransactionOutputs = (Txid, Vec<(u32, TxOut)>); + #[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))] /// Used only in testing and fuzztarget to check serialization roundtrips don't change the /// underlying object @@ -765,7 +623,7 @@ impl PartialEq for ChannelMonitorImpl { 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 @@ -779,17 +637,18 @@ impl PartialEq for ChannelMonitorImpl { impl Writeable for ChannelMonitor { fn write(&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])?; - self.inner.lock().unwrap().write(writer) } } +// These are also used for ChannelMonitorUpdate, above. +const SERIALIZATION_VERSION: u8 = 1; +const MIN_SERIALIZATION_VERSION: u8 = 1; + impl Writeable for ChannelMonitorImpl { fn write(&self, writer: &mut W) -> Result<(), Error> { + write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION); + self.latest_update_id.write(writer)?; // Set in initial Channel-object creation, so should always be set by now: @@ -874,38 +733,14 @@ impl Writeable for ChannelMonitorImpl { 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))?; @@ -931,25 +766,12 @@ impl Writeable for ChannelMonitorImpl { 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)?; @@ -966,6 +788,8 @@ impl Writeable for ChannelMonitorImpl { self.lockdown_from_offchain.write(writer)?; self.holder_tx_signed.write(writer)?; + write_tlv_fields!(writer, {}); + Ok(()) } } @@ -977,7 +801,7 @@ impl ChannelMonitor { funding_redeemscript: Script, channel_value_satoshis: u64, commitment_transaction_number_obscure_factor: u64, initial_holder_commitment_tx: HolderCommitmentTransaction, - last_block_hash: BlockHash) -> ChannelMonitor { + best_block: BestBlock) -> ChannelMonitor { assert!(commitment_transaction_number_obscure_factor <= (1 << 48)); let our_channel_close_key_hash = WPubkeyHash::hash(&shutdown_pubkey.serialize()); @@ -1056,7 +880,7 @@ impl ChannelMonitor { pending_monitor_events: Vec::new(), pending_events: Vec::new(), - onchain_events_waiting_threshold_conf: HashMap::new(), + onchain_events_awaiting_threshold_conf: Vec::new(), outputs_to_watch, onchain_tx_handler, @@ -1064,7 +888,8 @@ impl ChannelMonitor { lockdown_from_offchain: false, holder_tx_signed: false, - last_block_hash, + best_block, + secp_ctx, }), } @@ -1254,7 +1079,7 @@ impl ChannelMonitor { broadcaster: B, fee_estimator: F, logger: L, - ) -> Vec<(Txid, Vec<(u32, TxOut)>)> + ) -> Vec where B::Target: BroadcasterInterface, F::Target: FeeEstimator, @@ -1281,6 +1106,89 @@ impl ChannelMonitor { 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( + &self, + header: &BlockHeader, + txdata: &TransactionData, + height: u32, + broadcaster: B, + fee_estimator: F, + logger: L, + ) -> Vec + 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( + &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( + &self, + header: &BlockHeader, + height: u32, + broadcaster: B, + fee_estimator: F, + logger: L, + ) -> Vec + 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 { + let inner = self.inner.lock().unwrap(); + let mut txids: Vec = 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 ChannelMonitorImpl { @@ -1423,7 +1331,7 @@ impl ChannelMonitorImpl { 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, None, broadcaster, fee_estimator, logger); + 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 { @@ -1445,11 +1353,11 @@ impl ChannelMonitorImpl { // *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); - self.onchain_tx_handler.update_claims_view(&Vec::new(), claim_reqs, None, broadcaster, fee_estimator, logger); + 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); - self.onchain_tx_handler.update_claims_view(&Vec::new(), claim_reqs, None, broadcaster, fee_estimator, logger); + 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); } } } @@ -1459,6 +1367,7 @@ impl ChannelMonitorImpl { 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)); @@ -1574,7 +1483,7 @@ impl ChannelMonitorImpl { /// 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(&mut self, tx: &Transaction, height: u32, logger: &L) -> (Vec, (Txid, Vec<(u32, TxOut)>)) where L::Target: Logger { + fn check_spend_counterparty_transaction(&mut self, tx: &Transaction, height: u32, logger: &L) -> (Vec, 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(); @@ -1606,8 +1515,9 @@ impl ChannelMonitorImpl { // 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); } } @@ -1619,8 +1529,9 @@ impl ChannelMonitorImpl { 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); } } } @@ -1628,7 +1539,7 @@ impl ChannelMonitorImpl { // Last, track onchain revoked commitment transaction and fail backward outgoing HTLCs as payment path is broken if !claimable_outpoints.is_empty() || per_commitment_option.is_some() { // ie we're confident this is actually ours // We're definitely a counterparty commitment transaction! - log_trace!(logger, "Got broadcast of revoked counterparty commitment transaction, going to generate general spend tx with {} inputs", claimable_outpoints.len()); + log_error!(logger, "Got broadcast of revoked counterparty commitment transaction, going to generate general spend tx with {} inputs", claimable_outpoints.len()); for (idx, outp) in tx.output.iter().enumerate() { watch_outputs.push((idx as u32, outp.clone())); } @@ -1639,24 +1550,25 @@ impl ChannelMonitorImpl { if let Some(ref outpoints) = self.counterparty_claimable_outpoints.get($txid) { for &(ref htlc, ref source_option) in outpoints.iter() { if let &Some(ref source) = source_option { - log_info!(logger, "Failing HTLC with payment_hash {} from {} counterparty commitment tx due to broadcast of revoked counterparty commitment transaction, waiting for confirmation (at height {})", log_bytes!(htlc.payment_hash.0), $commitment_tx, height + ANTI_REORG_DELAY - 1); - match self.onchain_events_waiting_threshold_conf.entry(height + ANTI_REORG_DELAY - 1) { - hash_map::Entry::Occupied(mut entry) => { - let e = entry.get_mut(); - e.retain(|ref event| { - match **event { - OnchainEvent::HTLCUpdate { ref htlc_update } => { - return htlc_update.0 != **source - }, - _ => true - } - }); - e.push(OnchainEvent::HTLCUpdate { htlc_update: ((**source).clone(), htlc.payment_hash.clone())}); + self.onchain_events_awaiting_threshold_conf.retain(|ref entry| { + if entry.height != height { return true; } + match entry.event { + OnchainEvent::HTLCUpdate { source: ref update_source, .. } => { + *update_source != **source + }, + _ => true, } - hash_map::Entry::Vacant(entry) => { - entry.insert(vec![OnchainEvent::HTLCUpdate { htlc_update: ((**source).clone(), htlc.payment_hash.clone())}]); - } - } + }); + let entry = OnchainEventEntry { + txid: *$txid, + height, + event: OnchainEvent::HTLCUpdate { + 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); } } } @@ -1683,7 +1595,7 @@ impl ChannelMonitorImpl { } self.counterparty_commitment_txn_on_chain.insert(commitment_txid, commitment_number); - log_trace!(logger, "Got broadcast of non-revoked counterparty commitment transaction {}", commitment_txid); + log_info!(logger, "Got broadcast of non-revoked counterparty commitment transaction {}", commitment_txid); macro_rules! check_htlc_fails { ($txid: expr, $commitment_tx: expr, $id: tt) => { @@ -1705,23 +1617,23 @@ impl ChannelMonitorImpl { } } 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())}); + 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, } - hash_map::Entry::Vacant(entry) => { - entry.insert(vec![OnchainEvent::HTLCUpdate { htlc_update: ((**source).clone(), htlc.payment_hash.clone())}]); - } - } + }); + self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry { + txid: *$txid, + height, + event: OnchainEvent::HTLCUpdate { + source: (**source).clone(), + payment_hash: htlc.payment_hash.clone(), + }, + }); } } } @@ -1743,8 +1655,8 @@ impl ChannelMonitorImpl { (claimable_outpoints, (commitment_txid, watch_outputs)) } - fn get_counterparty_htlc_output_claim_reqs(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>) -> Vec { - let mut claims = Vec::new(); + fn get_counterparty_htlc_output_claim_reqs(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>) -> Vec { + 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 = @@ -1763,30 +1675,26 @@ impl ChannelMonitorImpl { 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 claims; // Corrupted per_commitment_data, fuck this user + 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 }; + let preimage = if htlc.offered { if let Some(p) = self.payment_preimages.get(&htlc.payment_hash) { Some(*p) } else { None } } else { None }; 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() }; - claims.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); } } } } } } - claims + claimable_outpoints } /// Attempts to claim a counterparty HTLC-Success/HTLC-Timeout's outputs using the revocation key - fn check_spend_counterparty_htlc(&mut self, tx: &Transaction, commitment_number: u64, height: u32, logger: &L) -> (Vec, Option<(Txid, Vec<(u32, TxOut)>)>) where L::Target: Logger { + fn check_spend_counterparty_htlc(&mut self, tx: &Transaction, commitment_number: u64, height: u32, logger: &L) -> (Vec, Option) 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) @@ -1805,17 +1713,18 @@ impl ChannelMonitorImpl { let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret)); 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 }); + log_error!(logger, "Got broadcast of revoked counterparty HTLC transaction, spending {}:{}", htlc_txid, 0); + 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))) } - // Returns (1) `ClaimRequest`s that can be given to the OnChainTxHandler, so that the handler can + // Returns (1) `PackageTemplate`s that can be given to the OnChainTxHandler, so that the handler can // broadcast transactions claiming holder HTLC commitment outputs and (2) a holder revokable // script so we can detect whether a holder transaction has been seen on-chain. - fn get_broadcasted_holder_claims(&self, holder_tx: &HolderSignedTx) -> (Vec, Option<(Script, PublicKey, PublicKey)>) { + fn get_broadcasted_holder_claims(&self, holder_tx: &HolderSignedTx, height: u32) -> (Vec, Option<(Script, PublicKey, PublicKey)>) { let mut claim_requests = 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); @@ -1823,18 +1732,19 @@ impl ChannelMonitorImpl { 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, - }}); + 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); } } @@ -1855,31 +1765,29 @@ impl ChannelMonitorImpl { /// 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(&mut self, tx: &Transaction, height: u32, logger: &L) -> (Vec, (Txid, Vec<(u32, TxOut)>)) where L::Target: Logger { + fn check_spend_holder_transaction(&mut self, tx: &Transaction, height: u32, logger: &L) -> (Vec, TransactionOutputs) where L::Target: Logger { let commitment_txid = tx.txid(); let mut claim_requests = Vec::new(); let mut watch_outputs = Vec::new(); macro_rules! wait_threshold_conf { - ($height: expr, $source: expr, $commitment_tx: expr, $payment_hash: expr) => { - log_trace!(logger, "Failing HTLC with payment_hash {} from {} holder commitment tx due to broadcast of transaction, waiting confirmation (at height{})", log_bytes!($payment_hash.0), $commitment_tx, height + ANTI_REORG_DELAY - 1); - match self.onchain_events_waiting_threshold_conf.entry($height + ANTI_REORG_DELAY - 1) { - hash_map::Entry::Occupied(mut entry) => { - let e = entry.get_mut(); - e.retain(|ref event| { - match **event { - OnchainEvent::HTLCUpdate { ref htlc_update } => { - return htlc_update.0 != $source - }, - _ => true - } - }); - e.push(OnchainEvent::HTLCUpdate { htlc_update: ($source, $payment_hash)}); + ($source: expr, $commitment_tx: expr, $payment_hash: expr) => { + self.onchain_events_awaiting_threshold_conf.retain(|ref entry| { + if entry.height != height { return true; } + match entry.event { + OnchainEvent::HTLCUpdate { source: ref update_source, .. } => { + *update_source != $source + }, + _ => true, } - hash_map::Entry::Vacant(entry) => { - entry.insert(vec![OnchainEvent::HTLCUpdate { htlc_update: ($source, $payment_hash)}]); - } - } + }); + let entry = OnchainEventEntry { + txid: commitment_txid, + height, + event: OnchainEvent::HTLCUpdate { 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); } } @@ -1896,15 +1804,15 @@ impl ChannelMonitorImpl { 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 res = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx); + log_info!(logger, "Got broadcast of latest holder commitment tx {}, searching for available HTLCs to claim", commitment_txid); + 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 res = self.get_broadcasted_holder_claims(holder_tx); + log_info!(logger, "Got broadcast of previous holder commitment tx {}, searching for available HTLCs to claim", commitment_txid); + 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); } @@ -1915,7 +1823,7 @@ impl ChannelMonitorImpl { 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()); } } } @@ -1933,11 +1841,11 @@ impl ChannelMonitorImpl { } pub fn get_latest_holder_commitment_txn(&mut self, logger: &L) -> Vec where L::Target: Logger { - log_trace!(logger, "Getting signed latest holder commitment transaction!"); + log_debug!(logger, "Getting signed latest holder commitment transaction!"); self.holder_tx_signed = true; let 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]; + 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 { @@ -1945,24 +1853,32 @@ impl ChannelMonitorImpl { // 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) { - res.push(htlc_tx); + 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; + holder_transactions } #[cfg(any(test,feature = "unsafe_revoked_tx_signing"))] + /// Note that this includes possibly-locktimed-in-the-future transactions! fn unsafe_get_latest_holder_commitment_txn(&mut self, logger: &L) -> Vec where L::Target: Logger { - log_trace!(logger, "Getting signed copy of latest holder commitment transaction!"); + log_debug!(logger, "Getting signed copy of latest holder commitment transaction!"); let 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]; + 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 { @@ -1973,17 +1889,65 @@ impl ChannelMonitorImpl { } 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); + holder_transactions.push(htlc_tx); } } } - return res + holder_transactions } - pub fn block_connected(&mut self, header: &BlockHeader, txdata: &TransactionData, height: u32, broadcaster: B, fee_estimator: F, logger: L)-> Vec<(Txid, Vec<(u32, TxOut)>)> + pub fn block_connected(&mut self, header: &BlockHeader, txdata: &TransactionData, height: u32, broadcaster: B, fee_estimator: F, logger: L) -> Vec 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( + &mut self, + header: &BlockHeader, + height: u32, + broadcaster: B, + fee_estimator: F, + logger: L, + ) -> Vec + 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 if block_hash != self.best_block.block_hash() { + self.best_block = BestBlock::new(block_hash, height); + self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.height <= height); + self.onchain_tx_handler.block_disconnected(height + 1, broadcaster, fee_estimator, logger); + Vec::new() + } else { Vec::new() } + } + + fn transactions_confirmed( + &mut self, + header: &BlockHeader, + txdata: &TransactionData, + height: u32, + broadcaster: B, + fee_estimator: F, + logger: L, + ) -> Vec + where + B::Target: BroadcasterInterface, + F::Target: FeeEstimator, + L::Target: Logger, { let txn_matched = self.filter_block(txdata); for tx in &txn_matched { @@ -2039,44 +2003,102 @@ impl ChannelMonitorImpl { 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( + &mut self, + height: u32, + txn_matched: Vec<&Transaction>, + mut watch_outputs: Vec, + mut claimable_outpoints: Vec, + broadcaster: B, + fee_estimator: F, + logger: L, + ) -> Vec + 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)); 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); + 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); } - if let Some(events) = self.onchain_events_waiting_threshold_conf.remove(&height) { - for ev in events { - match ev { - OnchainEvent::HTLCUpdate { htlc_update } => { - log_trace!(logger, "HTLC {} failure update has got enough confirmations to be passed upstream", log_bytes!((htlc_update.1).0)); - self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate { - payment_hash: htlc_update.1, - payment_preimage: None, - source: htlc_update.0, - })); - }, - OnchainEvent::MaturingOutput { descriptor } => { - log_trace!(logger, "Descriptor {} has got enough confirmations to be passed upstream", log_spendable!(descriptor)); - self.pending_events.push(Event::SpendableOutputs { - outputs: vec![descriptor] - }); + + // Find which on-chain events have reached their confirmation threshold. + let onchain_events_awaiting_threshold_conf = + self.onchain_events_awaiting_threshold_conf.drain(..).collect::>(); + 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_debug!(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_debug!(logger, "Descriptor {} has got enough confirmations to be passed upstream", log_spendable!(descriptor)); + self.pending_events.push(Event::SpendableOutputs { + outputs: vec![descriptor] + }); } } } - self.onchain_tx_handler.update_claims_view(&txn_matched, claimable_outpoints, Some(height), &&*broadcaster, &&*fee_estimator, &&*logger); - self.last_block_hash = block_hash; + 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. @@ -2108,15 +2130,29 @@ impl ChannelMonitorImpl { { log_trace!(logger, "Block {} at height {} disconnected", header.block_hash(), height); - if let Some(_) = self.onchain_events_waiting_threshold_conf.remove(&(height + ANTI_REORG_DELAY - 1)) { - //We may discard: - //- htlc update there as failure-trigger tx (revoked commitment tx, non-revoked commitment tx, HTLC-timeout tx) has been disconnected - //- maturing spendable output has transaction paying us has been disconnected - } + //We may discard: + //- htlc update there as failure-trigger tx (revoked commitment tx, non-revoked commitment tx, HTLC-timeout tx) has been disconnected + //- maturing spendable output has transaction paying us has been disconnected + self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.height < height); self.onchain_tx_handler.block_disconnected(height, broadcaster, fee_estimator, logger); - self.last_block_hash = header.prev_blockhash; + self.best_block = BestBlock::new(header.prev_blockhash, height - 1); + } + + fn transaction_unconfirmed( + &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 @@ -2344,24 +2380,22 @@ impl ChannelMonitorImpl { })); } } 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)}); + 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, } - hash_map::Entry::Vacant(entry) => { - entry.insert(vec![OnchainEvent::HTLCUpdate { htlc_update: (source, payment_hash)}]); - } - } + }); + 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); } } } @@ -2371,7 +2405,7 @@ impl ChannelMonitorImpl { fn is_paying_spendable_output(&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 @@ -2420,16 +2454,13 @@ impl ChannelMonitorImpl { } } 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_info!(logger, "Received spendable output {}, spendable at height {}", log_spendable!(spendable_output), entry.confirmation_threshold()); + self.onchain_events_awaiting_threshold_conf.push(entry); } } } @@ -2445,7 +2476,7 @@ impl ChannelMonitorImpl { /// 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: Send + Sync { +pub trait Persist { /// 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 @@ -2497,6 +2528,29 @@ where } } +impl chain::Confirm for (ChannelMonitor, 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 { + self.0.get_relevant_txids() + } +} + const MAX_ALLOC_SIZE: usize = 64*1024; impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> @@ -2511,11 +2565,7 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> } } - 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 = ::read(reader)?.0; @@ -2619,46 +2669,14 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> } } - 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 ::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 ::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 = ::read(reader)?.0; let current_holder_commitment_number = ::read(reader)?.0; @@ -2692,34 +2710,12 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> } } - let last_block_hash: BlockHash = Readable::read(reader)?; + let best_block = BestBlock::new(Readable::read(reader)?, Readable::read(reader)?); let waiting_threshold_conf_len: u64 = Readable::read(reader)?; - let mut onchain_events_waiting_threshold_conf = HashMap::with_capacity(cmp::min(waiting_threshold_conf_len as usize, MAX_ALLOC_SIZE / 128)); + let mut onchain_events_awaiting_threshold_conf = Vec::with_capacity(cmp::min(waiting_threshold_conf_len as usize, MAX_ALLOC_SIZE / 128)); for _ in 0..waiting_threshold_conf_len { - let height_target = Readable::read(reader)?; - let events_len: u64 = Readable::read(reader)?; - let mut events = Vec::with_capacity(cmp::min(events_len as usize, MAX_ALLOC_SIZE / 128)); - for _ in 0..events_len { - let ev = match ::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)?; @@ -2740,10 +2736,12 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> let lockdown_from_offchain = Readable::read(reader)?; let holder_tx_signed = Readable::read(reader)?; + read_tlv_fields!(reader, {}); + let mut secp_ctx = Secp256k1::new(); secp_ctx.seeded_randomize(&keys_manager.get_secure_random_bytes()); - Ok((last_block_hash.clone(), ChannelMonitor { + Ok((best_block.block_hash(), ChannelMonitor { inner: Mutex::new(ChannelMonitorImpl { latest_update_id, commitment_transaction_number_obscure_factor, @@ -2780,7 +2778,7 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> pending_monitor_events, pending_events, - onchain_events_waiting_threshold_conf, + onchain_events_awaiting_threshold_conf, outputs_to_watch, onchain_tx_handler, @@ -2788,7 +2786,8 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> lockdown_from_offchain, holder_tx_signed, - last_block_hash, + best_block, + secp_ctx, }), })) @@ -2797,7 +2796,6 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> #[cfg(test)] mod tests { - use bitcoin::blockdata::constants::genesis_block; use bitcoin::blockdata::script::{Script, Builder}; use bitcoin::blockdata::opcodes; use bitcoin::blockdata::transaction::{Transaction, TxIn, TxOut, SigHashType}; @@ -2810,9 +2808,10 @@ mod tests { 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, ChannelPublicKeys, ChannelTransactionParameters, HolderCommitmentTransaction, CounterpartyChannelTransactionParameters}; use util::test_utils::{TestLogger, TestBroadcaster, TestFeeEstimator}; @@ -2820,12 +2819,13 @@ mod tests { use bitcoin::secp256k1::Secp256k1; 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())}); + 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()); @@ -2907,13 +2907,13 @@ mod tests { }; // Prune with one old state and a holder commitment tx holding a few overlaps with the // old state. - let last_block_hash = genesis_block(Network::Testnet).block_hash(); + let best_block = BestBlock::from_genesis(Network::Testnet); let monitor = ChannelMonitor::new(Secp256k1::new(), keys, &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()), 0, &Script::new(), (OutPoint { txid: Txid::from_slice(&[43; 32]).unwrap(), index: 0 }, Script::new()), &channel_parameters, Script::new(), 46, 0, - HolderCommitmentTransaction::dummy(), last_block_hash); + HolderCommitmentTransaction::dummy(), best_block); monitor.provide_latest_holder_commitment_tx(HolderCommitmentTransaction::dummy(), preimages_to_holder_htlcs!(preimages[0..10])).unwrap(); let dummy_txid = dummy_tx.txid(); @@ -2968,25 +2968,25 @@ mod tests { 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()); @@ -3019,14 +3019,16 @@ mod tests { 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::::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(); @@ -3043,14 +3045,16 @@ mod tests { }); } 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::::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(); @@ -3065,14 +3069,16 @@ mod tests { 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::::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.