X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fchain%2Fchannelmonitor.rs;h=7fc512a5a1859429b265d2b7ffc967b92fec3f4b;hb=56491d91bbc2d73b97e222ccb33b69f8e17cd4a2;hp=c68ee39bb47a9c1b01b7ec72c52604f356267962;hpb=3be185ad13408da141ec3d7a6396f6f7625aaca2;p=rust-lightning diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index c68ee39b..7fc512a5 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,29 @@ 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::{BestBlock, 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::byte_utils; use util::events::Event; +use prelude::*; use std::collections::{HashMap, HashSet}; -use std::{cmp, mem}; +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,7 +86,7 @@ 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> { @@ -100,7 +102,7 @@ impl Readable for ChannelMonitorUpdate { fn read(r: &mut R) -> Result { 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)?); } @@ -205,7 +207,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 +223,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. /// @@ -320,151 +322,6 @@ 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, - } -} - -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)?; - } - } - Ok(()) - } -} - -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) - } -} - -/// 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 -} - /// An entry for an [`OnchainEvent`], stating the block height when the event was observed and the /// transaction causing it. /// @@ -501,9 +358,6 @@ enum OnchainEvent { }, } -const SERIALIZATION_VERSION: u8 = 1; -const MIN_SERIALIZATION_VERSION: u8 = 1; - #[cfg_attr(any(test, feature = "fuzztarget", feature = "_test_utils"), derive(PartialEq))] #[derive(Clone)] pub(crate) enum ChannelMonitorUpdateStep { @@ -804,17 +658,17 @@ 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) } } +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: @@ -990,6 +844,8 @@ impl Writeable for ChannelMonitorImpl { self.lockdown_from_offchain.write(writer)?; self.holder_tx_signed.write(writer)?; + write_tlv_fields!(writer, {}, {}); + Ok(()) } } @@ -1553,10 +1409,10 @@ 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); + 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, None, broadcaster, fee_estimator, logger); if let Some(ref tx) = self.prev_holder_signed_commitment_tx { - let (claim_reqs, _) = self.get_broadcasted_holder_claims(&tx); + let (claim_reqs, _) = self.get_broadcasted_holder_claims(&tx, 0); self.onchain_tx_handler.update_claims_view(&Vec::new(), claim_reqs, None, broadcaster, fee_estimator, logger); } } @@ -1567,6 +1423,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)); @@ -1682,7 +1539,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, TransactionOutputs) 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(); @@ -1714,8 +1571,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); } } @@ -1727,8 +1585,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); } } } @@ -1850,8 +1709,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 = @@ -1870,30 +1729,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) 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) @@ -1913,16 +1768,17 @@ impl ChannelMonitorImpl { 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))) } - // 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); @@ -1930,18 +1786,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); } } @@ -1962,7 +1819,7 @@ 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, TransactionOutputs) 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(); @@ -2002,14 +1859,14 @@ 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); + 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); + 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); } @@ -2201,7 +2058,7 @@ impl ChannelMonitorImpl { height: u32, txn_matched: Vec<&Transaction>, mut watch_outputs: Vec, - mut claimable_outpoints: Vec, + mut claimable_outpoints: Vec, broadcaster: B, fee_estimator: F, logger: L, @@ -2213,11 +2070,13 @@ impl ChannelMonitorImpl { { 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() }}); + 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)); @@ -2592,7 +2451,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 @@ -2752,11 +2611,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; @@ -2977,6 +2832,8 @@ 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()); @@ -3047,9 +2904,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::{BestBlock, 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}; @@ -3057,6 +2915,7 @@ mod tests { use bitcoin::secp256k1::Secp256k1; use std::sync::{Arc, Mutex}; use chain::keysinterface::InMemorySigner; + use prelude::*; #[test] fn test_prune_preimages() { @@ -3205,25 +3064,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()); @@ -3256,14 +3115,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(); @@ -3280,14 +3141,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(); @@ -3302,14 +3165,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.