X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=src%2Fln%2Fchannelmonitor.rs;h=379ae31c3ebb68cdab7542a5295d54fbca852971;hb=587af43eca7dc254bf0feb96d4732fd56cf6fcc3;hp=3b5905e702bc55dd3789a600350dee35ec1c4376;hpb=a6f0281017f1223ef961fb3a000915e37b5719b0;p=rust-lightning diff --git a/src/ln/channelmonitor.rs b/src/ln/channelmonitor.rs index 3b5905e7..379ae31c 100644 --- a/src/ln/channelmonitor.rs +++ b/src/ln/channelmonitor.rs @@ -17,12 +17,13 @@ use bitcoin::blockdata::transaction::OutPoint as BitcoinOutPoint; use bitcoin::blockdata::script::{Script, Builder}; use bitcoin::blockdata::opcodes; use bitcoin::consensus::encode::{self, Decodable, Encodable}; -use bitcoin::util::hash::{BitcoinHash,Sha256dHash}; +use bitcoin::util::hash::BitcoinHash; use bitcoin::util::bip143; use bitcoin_hashes::Hash; use bitcoin_hashes::sha256::Hash as Sha256; use bitcoin_hashes::hash160::Hash as Hash160; +use bitcoin_hashes::sha256d::Hash as Sha256dHash; use secp256k1::{Secp256k1,Signature}; use secp256k1::key::{SecretKey,PublicKey}; @@ -33,7 +34,7 @@ use ln::chan_utils; use ln::chan_utils::HTLCOutputInCommitment; use ln::channelmanager::{HTLCSource, PaymentPreimage, PaymentHash}; use ln::channel::{ACCEPTED_HTLC_SCRIPT_WEIGHT, OFFERED_HTLC_SCRIPT_WEIGHT}; -use chain::chaininterface::{ChainListener, ChainWatchInterface, BroadcasterInterface}; +use chain::chaininterface::{ChainListener, ChainWatchInterface, BroadcasterInterface, FeeEstimator, ConfirmationTarget}; use chain::transaction::OutPoint; use chain::keysinterface::SpendableOutputDescriptor; use util::logger::Logger; @@ -47,8 +48,8 @@ use std::{hash,cmp, mem}; /// An error enum representing a failure to persist a channel monitor update. #[derive(Clone)] pub enum ChannelMonitorUpdateErr { - /// Used to indicate a temporary failure (eg connection to a watchtower failed, but is expected - /// to succeed at some point in the future). + /// Used to indicate a temporary failure (eg connection to a watchtower or remote backup of + /// our state failed, but is expected to succeed at some point in the future). /// /// Such a failure will "freeze" a channel, preventing us from revoking old states or /// submitting new commitment transactions to the remote party. @@ -70,6 +71,10 @@ pub enum ChannelMonitorUpdateErr { /// Note that even if updates made after TemporaryFailure succeed you must still call /// test_restore_channel_monitor to ensure you have the latest monitor and re-enable normal /// channel operation. + /// + /// For deployments where a copy of ChannelMonitors and other local state are backed up in a + /// remote location (with local copies persisted immediately), it is anticipated that all + /// updates will return TemporaryFailure until the remote copies could be updated. TemporaryFailure, /// Used to indicate no further channel monitor updates will be allowed (eg we've moved on to a /// different watchtower and cannot update with all watchtowers that were previously informed @@ -138,6 +143,7 @@ pub struct SimpleManyChannelMonitor { pending_events: Mutex>, pending_htlc_updated: Mutex)>>>, logger: Arc, + fee_estimator: Arc } impl ChainListener for SimpleManyChannelMonitor { @@ -148,7 +154,7 @@ impl ChainListener for SimpleManyChannelMonit { let mut monitors = self.monitors.lock().unwrap(); for monitor in monitors.values_mut() { - let (txn_outputs, spendable_outputs, mut htlc_updated) = monitor.block_connected(txn_matched, height, &block_hash, &*self.broadcaster); + let (txn_outputs, spendable_outputs, mut htlc_updated) = monitor.block_connected(txn_matched, height, &block_hash, &*self.broadcaster, &*self.fee_estimator); if spendable_outputs.len() > 0 { new_events.push(events::Event::SpendableOutputs { outputs: spendable_outputs, @@ -172,10 +178,6 @@ impl ChainListener for SimpleManyChannelMonit // In case of reorg we may have htlc outputs solved in a different way so // we prefer to keep claims but don't store duplicate updates for a given // (payment_hash, HTLCSource) pair. - // TODO: Note that we currently don't really use this as ChannelManager - // will fail/claim backwards after the first block. We really should delay - // a few blocks before failing backwards (but can claim backwards - // immediately) as long as we have a few blocks of headroom. let mut existing_claim = false; e.get_mut().retain(|htlc_data| { if htlc.0 == htlc_data.0 { @@ -199,13 +201,19 @@ impl ChainListener for SimpleManyChannelMonit pending_events.append(&mut new_events); } - fn block_disconnected(&self, _: &BlockHeader) { } + fn block_disconnected(&self, header: &BlockHeader, disconnected_height: u32) { + let block_hash = header.bitcoin_hash(); + let mut monitors = self.monitors.lock().unwrap(); + for monitor in monitors.values_mut() { + monitor.block_disconnected(disconnected_height, &block_hash); + } + } } impl SimpleManyChannelMonitor { /// Creates a new object which can be used to monitor several channels given the chain /// interface with which to register to receive notifications. - pub fn new(chain_monitor: Arc, broadcaster: Arc, logger: Arc) -> Arc> { + pub fn new(chain_monitor: Arc, broadcaster: Arc, logger: Arc, feeest: Arc) -> Arc> { let res = Arc::new(SimpleManyChannelMonitor { monitors: Mutex::new(HashMap::new()), chain_monitor, @@ -213,6 +221,7 @@ impl SimpleManyChannelMonitor pending_events: Mutex::new(Vec::new()), pending_htlc_updated: Mutex::new(HashMap::new()), logger, + fee_estimator: feeest, }); let weak_res = Arc::downgrade(&res); res.chain_monitor.register_listener(weak_res); @@ -299,7 +308,6 @@ pub(crate) const HTLC_FAIL_TIMEOUT_BLOCKS: u32 = 3; /// Number of blocks we wait on seeing a confirmed HTLC-Timeout or previous revoked commitment /// transaction before we fail corresponding inbound HTLCs. This prevents us from failing backwards /// and then getting a reorg resulting in us losing money. -//TODO: We currently don't actually use this...we should pub(crate) const HTLC_FAIL_ANTI_REORG_DELAY: u32 = 6; #[derive(Clone, PartialEq)] @@ -335,6 +343,15 @@ struct LocalSignedTx { htlc_outputs: Vec<(HTLCOutputInCommitment, Option<(Signature, Signature)>, Option)>, } +#[derive(PartialEq)] +enum InputDescriptors { + RevokedOfferedHTLC, + RevokedReceivedHTLC, + OfferedHTLC, + ReceivedHTLC, + RevokedOutput, // either a revoked to_local output on commitment tx, a revoked HTLC-Timeout output or a revoked HTLC-Success output +} + const SERIALIZATION_VERSION: u8 = 1; const MIN_SERIALIZATION_VERSION: u8 = 1; @@ -385,6 +402,8 @@ pub struct ChannelMonitor { destination_script: Script, + htlc_updated_waiting_threshold_conf: HashMap, PaymentHash)>>, + // We simply modify last_block_hash 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 insert_combine to ensure any local user copies keep @@ -395,6 +414,38 @@ pub struct ChannelMonitor { logger: Arc, } +macro_rules! subtract_high_prio_fee { + ($self: ident, $fee_estimator: expr, $value: expr, $predicted_weight: expr, $spent_txid: expr) => { + { + let mut fee = $fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) * $predicted_weight / 1000; + if $value <= fee { + fee = $fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Normal) * $predicted_weight / 1000; + if $value <= fee { + fee = $fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background) * $predicted_weight / 1000; + if $value <= fee { + log_error!($self, "Failed to generate an on-chain punishment tx spending {} as even low priority fee ({} sat) was more than the entire claim balance ({} sat)", + $spent_txid, fee, $value); + false + } else { + log_warn!($self, "Used low priority fee for on-chain punishment tx spending {} as high priority fee was more than the entire claim balance ({} sat)", + $spent_txid, $value); + $value -= fee; + true + } + } else { + log_warn!($self, "Used medium priority fee for on-chain punishment tx spending {} as high priority fee was more than the entire claim balance ({} sat)", + $spent_txid, $value); + $value -= fee; + true + } + } else { + $value -= fee; + true + } + } + } +} + #[cfg(any(test, feature = "fuzztarget"))] /// Used only in testing and fuzztarget to check serialization roundtrips don't change the /// underlying object @@ -414,7 +465,8 @@ impl PartialEq for ChannelMonitor { self.current_remote_commitment_number != other.current_remote_commitment_number || self.current_local_signed_commitment_tx != other.current_local_signed_commitment_tx || self.payment_preimages != other.payment_preimages || - self.destination_script != other.destination_script + self.destination_script != other.destination_script || + self.htlc_updated_waiting_threshold_conf != other.htlc_updated_waiting_threshold_conf { false } else { @@ -464,12 +516,44 @@ impl ChannelMonitor { payment_preimages: HashMap::new(), destination_script: destination_script, + htlc_updated_waiting_threshold_conf: HashMap::new(), + last_block_hash: Default::default(), secp_ctx: Secp256k1::new(), logger, } } + fn get_witnesses_weight(inputs: &[InputDescriptors]) -> u64 { + let mut tx_weight = 2; // count segwit flags + for inp in inputs { + // We use expected weight (and not actual) as signatures and time lock delays may vary + tx_weight += match inp { + // number_of_witness_elements + sig_length + revocation_sig + pubkey_length + revocationpubkey + witness_script_length + witness_script + &InputDescriptors::RevokedOfferedHTLC => { + 1 + 1 + 73 + 1 + 33 + 1 + 133 + }, + // number_of_witness_elements + sig_length + revocation_sig + pubkey_length + revocationpubkey + witness_script_length + witness_script + &InputDescriptors::RevokedReceivedHTLC => { + 1 + 1 + 73 + 1 + 33 + 1 + 139 + }, + // number_of_witness_elements + sig_length + remotehtlc_sig + preimage_length + preimage + witness_script_length + witness_script + &InputDescriptors::OfferedHTLC => { + 1 + 1 + 73 + 1 + 32 + 1 + 133 + }, + // number_of_witness_elements + sig_length + revocation_sig + pubkey_length + revocationpubkey + witness_script_length + witness_script + &InputDescriptors::ReceivedHTLC => { + 1 + 1 + 73 + 1 + 1 + 1 + 139 + }, + // number_of_witness_elements + sig_length + revocation_sig + true_length + op_true + witness_script_length + witness_script + &InputDescriptors::RevokedOutput => { + 1 + 1 + 73 + 1 + 1 + 1 + 77 + }, + }; + } + tx_weight + } + #[inline] fn place_secret(idx: u64) -> u8 { for i in 0..48 { @@ -796,18 +880,8 @@ impl ChannelMonitor { writer.write_all(&delayed_payment_base_key[..])?; writer.write_all(&payment_base_key[..])?; writer.write_all(&shutdown_pubkey.serialize())?; - if let Some(ref prev_latest_per_commitment_point) = *prev_latest_per_commitment_point { - writer.write_all(&[1; 1])?; - writer.write_all(&prev_latest_per_commitment_point.serialize())?; - } else { - writer.write_all(&[0; 1])?; - } - if let Some(ref latest_per_commitment_point) = *latest_per_commitment_point { - writer.write_all(&[1; 1])?; - writer.write_all(&latest_per_commitment_point.serialize())?; - } else { - writer.write_all(&[0; 1])?; - } + prev_latest_per_commitment_point.write(writer)?; + latest_per_commitment_point.write(writer)?; match funding_info { &Some((ref outpoint, ref script)) => { writer.write_all(&outpoint.txid[..])?; @@ -818,8 +892,8 @@ impl ChannelMonitor { debug_assert!(false, "Try to serialize a useless Local monitor !"); }, } - write_option!(current_remote_commitment_txid); - write_option!(prev_remote_commitment_txid); + current_remote_commitment_txid.write(writer)?; + prev_remote_commitment_txid.write(writer)?; }, Storage::Watchtower { .. } => unimplemented!(), } @@ -859,7 +933,7 @@ impl ChannelMonitor { writer.write_all(&byte_utils::be64_to_array($htlc_output.amount_msat))?; writer.write_all(&byte_utils::be32_to_array($htlc_output.cltv_expiry))?; writer.write_all(&$htlc_output.payment_hash.0[..])?; - write_option!(&$htlc_output.transaction_output_index); + $htlc_output.transaction_output_index.write(writer)?; } } @@ -951,6 +1025,17 @@ impl ChannelMonitor { self.last_block_hash.write(writer)?; self.destination_script.write(writer)?; + writer.write_all(&byte_utils::be64_to_array(self.htlc_updated_waiting_threshold_conf.len() as u64))?; + for (ref target, ref updates) in self.htlc_updated_waiting_threshold_conf.iter() { + writer.write_all(&byte_utils::be32_to_array(**target))?; + writer.write_all(&byte_utils::be64_to_array(updates.len() as u64))?; + for ref update in updates.iter() { + update.0.write(writer)?; + update.1.write(writer)?; + update.2.write(writer)?; + } + } + Ok(()) } @@ -1014,13 +1099,12 @@ impl ChannelMonitor { /// HTLC-Success/HTLC-Timeout transactions. /// Return updates for HTLC pending in the channel and failed automatically by the broadcast of /// revoked remote commitment tx - fn check_spend_remote_transaction(&mut self, tx: &Transaction, height: u32) -> (Vec, (Sha256dHash, Vec), Vec, Vec<(HTLCSource, Option, PaymentHash)>) { + fn check_spend_remote_transaction(&mut self, tx: &Transaction, height: u32, fee_estimator: &FeeEstimator) -> (Vec, (Sha256dHash, Vec), Vec) { // 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 txn_to_broadcast = Vec::new(); let mut watch_outputs = Vec::new(); let mut spendable_outputs = Vec::new(); - let mut htlc_updated = Vec::new(); let commitment_txid = tx.txid(); //TODO: This is gonna be a performance bottleneck for watchtowers! let per_commitment_option = self.remote_claimable_outpoints.get(&commitment_txid); @@ -1029,7 +1113,7 @@ impl ChannelMonitor { ( $thing : expr ) => { match $thing { Ok(a) => a, - Err(_) => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated) + Err(_) => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs) } }; } @@ -1054,7 +1138,7 @@ impl ChannelMonitor { }; let delayed_key = ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key), &self.their_delayed_payment_base_key.unwrap())); let a_htlc_key = match self.their_htlc_base_key { - None => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated), + None => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs), Some(their_htlc_base_key) => ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key), &their_htlc_base_key)), }; @@ -1072,6 +1156,7 @@ impl ChannelMonitor { let mut values = Vec::new(); let mut inputs = Vec::new(); let mut htlc_idxs = Vec::new(); + let mut input_descriptors = Vec::new(); for (idx, outp) in tx.output.iter().enumerate() { if outp.script_pubkey == revokeable_p2wsh { @@ -1087,6 +1172,7 @@ impl ChannelMonitor { htlc_idxs.push(None); values.push(outp.value); total_value += outp.value; + input_descriptors.push(InputDescriptors::RevokedOutput); } else if Some(&outp.script_pubkey) == local_payment_p2wpkh.as_ref() { spendable_outputs.push(SpendableOutputDescriptor::DynamicOutputP2WPKH { outpoint: BitcoinOutPoint { txid: commitment_txid, vout: idx as u32 }, @@ -1134,7 +1220,7 @@ impl ChannelMonitor { if transaction_output_index as usize >= tx.output.len() || tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 || tx.output[transaction_output_index as usize].script_pubkey != expected_script.to_v0_p2wsh() { - return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated); // Corrupted per_commitment_data, fuck this user + return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs); // Corrupted per_commitment_data, fuck this user } let input = TxIn { previous_output: BitcoinOutPoint { @@ -1150,6 +1236,7 @@ impl ChannelMonitor { htlc_idxs.push(Some(idx)); values.push(tx.output[transaction_output_index as usize].value); total_value += htlc.amount_msat / 1000; + input_descriptors.push(if htlc.offered { InputDescriptors::RevokedOfferedHTLC } else { InputDescriptors::RevokedReceivedHTLC }); } else { let mut single_htlc_tx = Transaction { version: 2, @@ -1157,12 +1244,16 @@ impl ChannelMonitor { input: vec![input], output: vec!(TxOut { script_pubkey: self.destination_script.clone(), - value: htlc.amount_msat / 1000, //TODO: - fee + value: htlc.amount_msat / 1000, }), }; - let sighash_parts = bip143::SighashComponents::new(&single_htlc_tx); - sign_input!(sighash_parts, single_htlc_tx.input[0], Some(idx), htlc.amount_msat / 1000); - txn_to_broadcast.push(single_htlc_tx); + let predicted_weight = single_htlc_tx.get_weight() + Self::get_witnesses_weight(&[if htlc.offered { InputDescriptors::RevokedOfferedHTLC } else { InputDescriptors::RevokedReceivedHTLC }]); + if subtract_high_prio_fee!(self, fee_estimator, single_htlc_tx.output[0].value, predicted_weight, tx.txid()) { + let sighash_parts = bip143::SighashComponents::new(&single_htlc_tx); + sign_input!(sighash_parts, single_htlc_tx.input[0], Some(idx), htlc.amount_msat / 1000); + assert!(predicted_weight >= single_htlc_tx.get_weight()); + txn_to_broadcast.push(single_htlc_tx); + } } } } @@ -1174,16 +1265,22 @@ impl ChannelMonitor { watch_outputs.append(&mut tx.output.clone()); self.remote_commitment_txn_on_chain.insert(commitment_txid, (commitment_number, tx.output.iter().map(|output| { output.script_pubkey.clone() }).collect())); - // TODO: We really should only fail backwards after our revocation claims have been - // confirmed, but we also need to do more other tracking of in-flight pre-confirm - // on-chain claims, so we can do that at the same time. macro_rules! check_htlc_fails { ($txid: expr, $commitment_tx: expr) => { - if let Some(ref outpoints) = self.remote_claimable_outpoints.get(&$txid) { + if let Some(ref outpoints) = self.remote_claimable_outpoints.get($txid) { for &(ref htlc, ref source_option) in outpoints.iter() { if let &Some(ref source) = source_option { - log_trace!(self, "Failing HTLC with payment_hash {} from {} remote commitment tx due to broadcast of revoked remote commitment transaction", log_bytes!(htlc.payment_hash.0), $commitment_tx); - htlc_updated.push(((**source).clone(), None, htlc.payment_hash.clone())); + log_info!(self, "Failing HTLC with payment_hash {} from {} remote commitment tx due to broadcast of revoked remote commitment transaction, waiting for confirmation (at height {})", log_bytes!(htlc.payment_hash.0), $commitment_tx, height + HTLC_FAIL_ANTI_REORG_DELAY - 1); + match self.htlc_updated_waiting_threshold_conf.entry(height + HTLC_FAIL_ANTI_REORG_DELAY - 1) { + hash_map::Entry::Occupied(mut entry) => { + let e = entry.get_mut(); + e.retain(|ref update| update.0 != **source); + e.push(((**source).clone(), None, htlc.payment_hash.clone())); + } + hash_map::Entry::Vacant(entry) => { + entry.insert(vec![((**source).clone(), None, htlc.payment_hash.clone())]); + } + } } } } @@ -1199,11 +1296,11 @@ impl ChannelMonitor { } // No need to check local commitment txn, symmetric HTLCSource must be present as per-htlc data on remote commitment tx } - if inputs.is_empty() { return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated); } // Nothing to be done...probably a false positive/local tx + if inputs.is_empty() { return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs); } // Nothing to be done...probably a false positive/local tx let outputs = vec!(TxOut { script_pubkey: self.destination_script.clone(), - value: total_value, //TODO: - fee + value: total_value, }); let mut spend_tx = Transaction { version: 2, @@ -1211,6 +1308,11 @@ impl ChannelMonitor { input: inputs, output: outputs, }; + let predicted_weight = spend_tx.get_weight() + Self::get_witnesses_weight(&input_descriptors[..]); + + if !subtract_high_prio_fee!(self, fee_estimator, spend_tx.output[0].value, predicted_weight, tx.txid()) { + return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs); + } let mut values_drain = values.drain(..); let sighash_parts = bip143::SighashComponents::new(&spend_tx); @@ -1219,6 +1321,7 @@ impl ChannelMonitor { let value = values_drain.next().unwrap(); sign_input!(sighash_parts, input, htlc_idx, value); } + assert!(predicted_weight >= spend_tx.get_weight()); spendable_outputs.push(SpendableOutputDescriptor::StaticOutput { outpoint: BitcoinOutPoint { txid: spend_tx.txid(), vout: 0 }, @@ -1238,12 +1341,9 @@ impl ChannelMonitor { log_trace!(self, "Got broadcast of non-revoked remote commitment transaction {}", commitment_txid); - // TODO: We really should only fail backwards after our revocation claims have been - // confirmed, but we also need to do more other tracking of in-flight pre-confirm - // on-chain claims, so we can do that at the same time. macro_rules! check_htlc_fails { ($txid: expr, $commitment_tx: expr, $id: tt) => { - if let Some(ref latest_outpoints) = self.remote_claimable_outpoints.get(&$txid) { + if let Some(ref latest_outpoints) = self.remote_claimable_outpoints.get($txid) { $id: for &(ref htlc, ref source_option) in latest_outpoints.iter() { if let &Some(ref source) = source_option { // Check if the HTLC is present in the commitment transaction that was @@ -1261,7 +1361,16 @@ impl ChannelMonitor { } } log_trace!(self, "Failing HTLC with payment_hash {} from {} remote commitment tx due to broadcast of remote commitment transaction", log_bytes!(htlc.payment_hash.0), $commitment_tx); - htlc_updated.push(((**source).clone(), None, htlc.payment_hash.clone())); + match self.htlc_updated_waiting_threshold_conf.entry(height + HTLC_FAIL_ANTI_REORG_DELAY - 1) { + hash_map::Entry::Occupied(mut entry) => { + let e = entry.get_mut(); + e.retain(|ref update| update.0 != **source); + e.push(((**source).clone(), None, htlc.payment_hash.clone())); + } + hash_map::Entry::Vacant(entry) => { + entry.insert(vec![((**source).clone(), None, htlc.payment_hash.clone())]); + } + } } } } @@ -1294,7 +1403,7 @@ impl ChannelMonitor { }, }; let a_htlc_key = match self.their_htlc_base_key { - None => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated), + None => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs), Some(their_htlc_base_key) => ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, revocation_point, &their_htlc_base_key)), }; @@ -1319,6 +1428,7 @@ impl ChannelMonitor { let mut total_value = 0; let mut values = Vec::new(); let mut inputs = Vec::new(); + let mut input_descriptors = Vec::new(); macro_rules! sign_input { ($sighash_parts: expr, $input: expr, $amount: expr, $preimage: expr) => { @@ -1349,7 +1459,7 @@ impl ChannelMonitor { if transaction_output_index as usize >= tx.output.len() || tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 || tx.output[transaction_output_index as usize].script_pubkey != expected_script.to_v0_p2wsh() { - return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated); // Corrupted per_commitment_data, fuck this user + return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs); // Corrupted per_commitment_data, fuck this user } if let Some(payment_preimage) = self.payment_preimages.get(&htlc.payment_hash) { let input = TxIn { @@ -1365,6 +1475,7 @@ impl ChannelMonitor { inputs.push(input); values.push((tx.output[transaction_output_index as usize].value, payment_preimage)); total_value += htlc.amount_msat / 1000; + input_descriptors.push(if htlc.offered { InputDescriptors::OfferedHTLC } else { InputDescriptors::ReceivedHTLC }); } else { let mut single_htlc_tx = Transaction { version: 2, @@ -1372,16 +1483,20 @@ impl ChannelMonitor { input: vec![input], output: vec!(TxOut { script_pubkey: self.destination_script.clone(), - value: htlc.amount_msat / 1000, //TODO: - fee + value: htlc.amount_msat / 1000, }), }; - let sighash_parts = bip143::SighashComponents::new(&single_htlc_tx); - sign_input!(sighash_parts, single_htlc_tx.input[0], htlc.amount_msat / 1000, payment_preimage.0.to_vec()); - spendable_outputs.push(SpendableOutputDescriptor::StaticOutput { - outpoint: BitcoinOutPoint { txid: single_htlc_tx.txid(), vout: 0 }, - output: single_htlc_tx.output[0].clone(), - }); - txn_to_broadcast.push(single_htlc_tx); + let predicted_weight = single_htlc_tx.get_weight() + Self::get_witnesses_weight(&[if htlc.offered { InputDescriptors::OfferedHTLC } else { InputDescriptors::ReceivedHTLC }]); + if subtract_high_prio_fee!(self, fee_estimator, single_htlc_tx.output[0].value, predicted_weight, tx.txid()) { + let sighash_parts = bip143::SighashComponents::new(&single_htlc_tx); + sign_input!(sighash_parts, single_htlc_tx.input[0], htlc.amount_msat / 1000, payment_preimage.0.to_vec()); + assert!(predicted_weight >= single_htlc_tx.get_weight()); + spendable_outputs.push(SpendableOutputDescriptor::StaticOutput { + outpoint: BitcoinOutPoint { txid: single_htlc_tx.txid(), vout: 0 }, + output: single_htlc_tx.output[0].clone(), + }); + txn_to_broadcast.push(single_htlc_tx); + } } } if !htlc.offered { @@ -1412,11 +1527,11 @@ impl ChannelMonitor { } } - if inputs.is_empty() { return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated); } // Nothing to be done...probably a false positive/local tx + if inputs.is_empty() { return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs); } // Nothing to be done...probably a false positive/local tx let outputs = vec!(TxOut { script_pubkey: self.destination_script.clone(), - value: total_value, //TODO: - fee + value: total_value }); let mut spend_tx = Transaction { version: 2, @@ -1424,6 +1539,10 @@ impl ChannelMonitor { input: inputs, output: outputs, }; + let predicted_weight = spend_tx.get_weight() + Self::get_witnesses_weight(&input_descriptors[..]); + if !subtract_high_prio_fee!(self, fee_estimator, spend_tx.output[0].value, predicted_weight, tx.txid()) { + return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs); + } let mut values_drain = values.drain(..); let sighash_parts = bip143::SighashComponents::new(&spend_tx); @@ -1433,6 +1552,7 @@ impl ChannelMonitor { sign_input!(sighash_parts, input, value.0, (value.1).0.to_vec()); } + assert!(predicted_weight >= spend_tx.get_weight()); spendable_outputs.push(SpendableOutputDescriptor::StaticOutput { outpoint: BitcoinOutPoint { txid: spend_tx.txid(), vout: 0 }, output: spend_tx.output[0].clone(), @@ -1442,11 +1562,11 @@ impl ChannelMonitor { } } - (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated) + (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs) } /// Attempts to claim a remote HTLC-Success/HTLC-Timeout's outputs using the revocation key - fn check_spend_remote_htlc(&self, tx: &Transaction, commitment_number: u64) -> (Option, Option) { + fn check_spend_remote_htlc(&self, tx: &Transaction, commitment_number: u64, fee_estimator: &FeeEstimator) -> (Option, Option) { if tx.input.len() != 1 || tx.output.len() != 1 { return (None, None) } @@ -1498,7 +1618,7 @@ impl ChannelMonitor { if !inputs.is_empty() { let outputs = vec!(TxOut { script_pubkey: self.destination_script.clone(), - value: amount, //TODO: - fee + value: amount }); let mut spend_tx = Transaction { @@ -1507,6 +1627,10 @@ impl ChannelMonitor { input: inputs, output: outputs, }; + let predicted_weight = spend_tx.get_weight() + Self::get_witnesses_weight(&[InputDescriptors::RevokedOutput]); + if !subtract_high_prio_fee!(self, fee_estimator, spend_tx.output[0].value, predicted_weight, tx.txid()) { + return (None, None); + } let sighash_parts = bip143::SighashComponents::new(&spend_tx); @@ -1525,6 +1649,7 @@ impl ChannelMonitor { spend_tx.input[0].witness.push(vec!(1)); spend_tx.input[0].witness.push(redeemscript.into_bytes()); + assert!(predicted_weight >= spend_tx.get_weight()); let outpoint = BitcoinOutPoint { txid: spend_tx.txid(), vout: 0 }; let output = spend_tx.output[0].clone(); (Some(spend_tx), Some(SpendableOutputDescriptor::StaticOutput { outpoint, output })) @@ -1693,7 +1818,7 @@ impl ChannelMonitor { } } - fn block_connected(&mut self, txn_matched: &[&Transaction], height: u32, block_hash: &Sha256dHash, broadcaster: &BroadcasterInterface)-> (Vec<(Sha256dHash, Vec)>, Vec, Vec<(HTLCSource, Option, PaymentHash)>) { + fn block_connected(&mut self, txn_matched: &[&Transaction], height: u32, block_hash: &Sha256dHash, broadcaster: &BroadcasterInterface, fee_estimator: &FeeEstimator)-> (Vec<(Sha256dHash, Vec)>, Vec, Vec<(HTLCSource, Option, PaymentHash)>) { let mut watch_outputs = Vec::new(); let mut spendable_outputs = Vec::new(); let mut htlc_updated = Vec::new(); @@ -1714,7 +1839,7 @@ impl ChannelMonitor { } }; if funding_txo.is_none() || (prevout.txid == funding_txo.as_ref().unwrap().0.txid && prevout.vout == funding_txo.as_ref().unwrap().0.index as u32) { - let (remote_txn, new_outputs, mut spendable_output, mut updated) = self.check_spend_remote_transaction(tx, height); + let (remote_txn, new_outputs, mut spendable_output) = self.check_spend_remote_transaction(tx, height, fee_estimator); txn = remote_txn; spendable_outputs.append(&mut spendable_output); if !new_outputs.1.is_empty() { @@ -1733,12 +1858,9 @@ impl ChannelMonitor { spendable_outputs.push(spendable_output); } } - if updated.len() > 0 { - htlc_updated.append(&mut updated); - } } else { if let Some(&(commitment_number, _)) = self.remote_commitment_txn_on_chain.get(&prevout.txid) { - let (tx, spendable_output) = self.check_spend_remote_htlc(tx, commitment_number); + let (tx, spendable_output) = self.check_spend_remote_htlc(tx, commitment_number, fee_estimator); if let Some(tx) = tx { txn.push(tx); } @@ -1786,10 +1908,23 @@ impl ChannelMonitor { } } } + if let Some(updates) = self.htlc_updated_waiting_threshold_conf.remove(&height) { + for update in updates { + log_trace!(self, "HTLC {} failure update has get enough confirmation to be pass upstream", log_bytes!((update.2).0)); + htlc_updated.push(update); + } + } self.last_block_hash = block_hash.clone(); (watch_outputs, spendable_outputs, htlc_updated) } + fn block_disconnected(&mut self, height: u32, block_hash: &Sha256dHash) { + if let Some(_) = self.htlc_updated_waiting_threshold_conf.remove(&(height + HTLC_FAIL_ANTI_REORG_DELAY - 1)) { + //We discard htlc update there as failure-trigger tx (revoked commitment tx, non-revoked commitment tx, HTLC-timeout tx) has been disconnected + } + self.last_block_hash = block_hash.clone(); + } + pub(super) fn would_broadcast_at_height(&self, height: u32) -> bool { // We need to consider all HTLCs which are: // * in any unrevoked remote commitment transaction, as they could broadcast said @@ -1872,8 +2007,9 @@ impl ChannelMonitor { macro_rules! log_claim { ($tx_info: expr, $local_tx: expr, $htlc: expr, $source_avail: expr) => { // We found the output in question, but aren't failing it backwards - // as we have no corresponding source. This implies either it is an - // inbound HTLC or an outbound HTLC on a revoked transaction. + // as we have no corresponding source and no valid remote commitment txid + // to try a weak source binding with same-hash, same-value still-valid offered HTLC. + // This implies either it is an inbound HTLC or an outbound HTLC on a revoked transaction. let outbound_htlc = $local_tx == $htlc.offered; if ($local_tx && revocation_sig_claim) || (outbound_htlc && !$source_avail && (accepted_preimage_claim || offered_preimage_claim)) { @@ -1890,6 +2026,22 @@ impl ChannelMonitor { } } + macro_rules! check_htlc_valid_remote { + ($remote_txid: expr, $htlc_output: expr) => { + if let &Some(txid) = $remote_txid { + for &(ref pending_htlc, ref pending_source) in self.remote_claimable_outpoints.get(&txid).unwrap() { + if pending_htlc.payment_hash == $htlc_output.payment_hash && pending_htlc.amount_msat == $htlc_output.amount_msat { + if let &Some(ref source) = pending_source { + log_claim!("revoked remote commitment tx", false, pending_htlc, true); + payment_data = Some(((**source).clone(), $htlc_output.payment_hash)); + break; + } + } + } + } + } + } + macro_rules! scan_commitment { ($htlcs: expr, $tx_info: expr, $local_tx: expr) => { for (ref htlc_output, source_option) in $htlcs { @@ -1902,7 +2054,17 @@ impl ChannelMonitor { // has timed out, or we screwed up. In any case, we should now // resolve the source HTLC with the original sender. payment_data = Some(((*source).clone(), htlc_output.payment_hash)); - } else { + } else if !$local_tx { + if let Storage::Local { ref current_remote_commitment_txid, .. } = self.key_storage { + check_htlc_valid_remote!(current_remote_commitment_txid, htlc_output); + } + if payment_data.is_none() { + if let Storage::Local { ref prev_remote_commitment_txid, .. } = self.key_storage { + check_htlc_valid_remote!(prev_remote_commitment_txid, htlc_output); + } + } + } + if payment_data.is_none() { log_claim!($tx_info, $local_tx, htlc_output, false); continue 'outer_loop; } @@ -2159,6 +2321,21 @@ impl ReadableArgs> for (Sha256dHash, ChannelM let last_block_hash: Sha256dHash = Readable::read(reader)?; let destination_script = Readable::read(reader)?; + let waiting_threshold_conf_len: u64 = Readable::read(reader)?; + let mut htlc_updated_waiting_threshold_conf = HashMap::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 updates_len: u64 = Readable::read(reader)?; + let mut updates = Vec::with_capacity(cmp::min(updates_len as usize, MAX_ALLOC_SIZE / 128)); + for _ in 0..updates_len { + let htlc_source = Readable::read(reader)?; + let preimage = Readable::read(reader)?; + let hash = Readable::read(reader)?; + updates.push((htlc_source, preimage, hash)); + } + htlc_updated_waiting_threshold_conf.insert(height_target, updates); + } + Ok((last_block_hash.clone(), ChannelMonitor { commitment_transaction_number_obscure_factor, @@ -2182,6 +2359,9 @@ impl ReadableArgs> for (Sha256dHash, ChannelM payment_preimages, destination_script, + + htlc_updated_waiting_threshold_conf, + last_block_hash, secp_ctx, logger, @@ -2192,13 +2372,19 @@ impl ReadableArgs> for (Sha256dHash, ChannelM #[cfg(test)] mod tests { - use bitcoin::blockdata::script::Script; - use bitcoin::blockdata::transaction::Transaction; + use bitcoin::blockdata::script::{Script, Builder}; + use bitcoin::blockdata::opcodes; + use bitcoin::blockdata::transaction::{Transaction, TxIn, TxOut, SigHashType}; + use bitcoin::blockdata::transaction::OutPoint as BitcoinOutPoint; + use bitcoin::util::bip143; use bitcoin_hashes::Hash; use bitcoin_hashes::sha256::Hash as Sha256; + use bitcoin_hashes::sha256d::Hash as Sha256dHash; + use bitcoin_hashes::hex::FromHex; use hex; use ln::channelmanager::{PaymentPreimage, PaymentHash}; - use ln::channelmonitor::ChannelMonitor; + use ln::channelmonitor::{ChannelMonitor, InputDescriptors}; + use ln::chan_utils; use ln::chan_utils::{HTLCOutputInCommitment, TxCreationKeys}; use util::test_utils::TestLogger; use secp256k1::key::{SecretKey,PublicKey}; @@ -2675,5 +2861,117 @@ mod tests { test_preimages_exist!(&preimages[0..5], monitor); } + #[test] + fn test_claim_txn_weight_computation() { + // We test Claim txn weight, knowing that we want expected weigth and + // not actual case to avoid sigs and time-lock delays hell variances. + + let secp_ctx = Secp256k1::new(); + let privkey = SecretKey::from_slice(&hex::decode("0101010101010101010101010101010101010101010101010101010101010101").unwrap()[..]).unwrap(); + let pubkey = PublicKey::from_secret_key(&secp_ctx, &privkey); + let mut sum_actual_sigs: u64 = 0; + + macro_rules! sign_input { + ($sighash_parts: expr, $input: expr, $idx: expr, $amount: expr, $input_type: expr, $sum_actual_sigs: expr) => { + let htlc = HTLCOutputInCommitment { + offered: if *$input_type == InputDescriptors::RevokedOfferedHTLC || *$input_type == InputDescriptors::OfferedHTLC { true } else { false }, + amount_msat: 0, + cltv_expiry: 2 << 16, + payment_hash: PaymentHash([1; 32]), + transaction_output_index: Some($idx), + }; + 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 sighash = hash_to_message!(&$sighash_parts.sighash_all(&$input, &redeem_script, $amount)[..]); + let sig = secp_ctx.sign(&sighash, &privkey); + $input.witness.push(sig.serialize_der().to_vec()); + $input.witness[0].push(SigHashType::All as u8); + sum_actual_sigs += $input.witness[0].len() as u64; + if *$input_type == InputDescriptors::RevokedOutput { + $input.witness.push(vec!(1)); + } else if *$input_type == InputDescriptors::RevokedOfferedHTLC || *$input_type == InputDescriptors::RevokedReceivedHTLC { + $input.witness.push(pubkey.clone().serialize().to_vec()); + } else if *$input_type == InputDescriptors::ReceivedHTLC { + $input.witness.push(vec![0]); + } else { + $input.witness.push(PaymentPreimage([1; 32]).0.to_vec()); + } + $input.witness.push(redeem_script.into_bytes()); + println!("witness[0] {}", $input.witness[0].len()); + println!("witness[1] {}", $input.witness[1].len()); + println!("witness[2] {}", $input.witness[2].len()); + } + } + + let script_pubkey = Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(); + let txid = Sha256dHash::from_hex("56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d").unwrap(); + + // Justice tx with 1 to_local, 2 revoked offered HTLCs, 1 revoked received HTLCs + let mut claim_tx = Transaction { version: 0, lock_time: 0, input: Vec::new(), output: Vec::new() }; + for i in 0..4 { + claim_tx.input.push(TxIn { + previous_output: BitcoinOutPoint { + txid, + vout: i, + }, + script_sig: Script::new(), + sequence: 0xfffffffd, + witness: Vec::new(), + }); + } + claim_tx.output.push(TxOut { + script_pubkey: script_pubkey.clone(), + value: 0, + }); + let base_weight = claim_tx.get_weight(); + let sighash_parts = bip143::SighashComponents::new(&claim_tx); + let inputs_des = vec![InputDescriptors::RevokedOutput, InputDescriptors::RevokedOfferedHTLC, InputDescriptors::RevokedOfferedHTLC, InputDescriptors::RevokedReceivedHTLC]; + for (idx, inp) in claim_tx.input.iter_mut().zip(inputs_des.iter()).enumerate() { + sign_input!(sighash_parts, inp.0, idx as u32, 0, inp.1, sum_actual_sigs); + } + assert_eq!(base_weight + ChannelMonitor::get_witnesses_weight(&inputs_des[..]), claim_tx.get_weight() + /* max_length_sig */ (73 * inputs_des.len() as u64 - sum_actual_sigs)); + + // Claim tx with 1 offered HTLCs, 3 received HTLCs + claim_tx.input.clear(); + sum_actual_sigs = 0; + for i in 0..4 { + claim_tx.input.push(TxIn { + previous_output: BitcoinOutPoint { + txid, + vout: i, + }, + script_sig: Script::new(), + sequence: 0xfffffffd, + witness: Vec::new(), + }); + } + let base_weight = claim_tx.get_weight(); + let sighash_parts = bip143::SighashComponents::new(&claim_tx); + let inputs_des = vec![InputDescriptors::OfferedHTLC, InputDescriptors::ReceivedHTLC, InputDescriptors::ReceivedHTLC, InputDescriptors::ReceivedHTLC]; + for (idx, inp) in claim_tx.input.iter_mut().zip(inputs_des.iter()).enumerate() { + sign_input!(sighash_parts, inp.0, idx as u32, 0, inp.1, sum_actual_sigs); + } + assert_eq!(base_weight + ChannelMonitor::get_witnesses_weight(&inputs_des[..]), claim_tx.get_weight() + /* max_length_sig */ (73 * inputs_des.len() as u64 - sum_actual_sigs)); + + // Justice tx with 1 revoked HTLC-Success tx output + claim_tx.input.clear(); + sum_actual_sigs = 0; + claim_tx.input.push(TxIn { + previous_output: BitcoinOutPoint { + txid, + vout: 0, + }, + script_sig: Script::new(), + sequence: 0xfffffffd, + witness: Vec::new(), + }); + let base_weight = claim_tx.get_weight(); + let sighash_parts = bip143::SighashComponents::new(&claim_tx); + let inputs_des = vec![InputDescriptors::RevokedOutput]; + for (idx, inp) in claim_tx.input.iter_mut().zip(inputs_des.iter()).enumerate() { + sign_input!(sighash_parts, inp.0, idx as u32, 0, inp.1, sum_actual_sigs); + } + assert_eq!(base_weight + ChannelMonitor::get_witnesses_weight(&inputs_des[..]), claim_tx.get_weight() + /* max_length_isg */ (73 * inputs_des.len() as u64 - sum_actual_sigs)); + } + // Further testing is done in the ChannelManager integration tests. }