X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=src%2Fln%2Fchannelmonitor.rs;h=2ddb2690ad8ed00de00090c7d3449b9397cba8f9;hb=041b04c31870f5ae054665ea23d0425195b55e6d;hp=c71800e594acde70ce4e2df59b86b2e19bb27d72;hpb=d8707c2b205c1fd387dc2a393b46205ae4b73113;p=rust-lightning diff --git a/src/ln/channelmonitor.rs b/src/ln/channelmonitor.rs index c71800e5..2ddb2690 100644 --- a/src/ln/channelmonitor.rs +++ b/src/ln/channelmonitor.rs @@ -17,14 +17,15 @@ 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,Message,Signature}; +use secp256k1::{Secp256k1,Signature}; use secp256k1::key::{SecretKey,PublicKey}; use secp256k1; @@ -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,13 +221,14 @@ 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); res } - /// Adds or udpates the monitor which monitors the channel referred to by the given key. + /// Adds or updates the monitor which monitors the channel referred to by the given key. pub fn add_update_monitor_by_key(&self, key: Key, monitor: ChannelMonitor) -> Result<(), MonitorUpdateError> { let mut monitors = self.monitors.lock().unwrap(); match monitors.get_mut(&key) { @@ -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 dont actually use this...we should pub(crate) const HTLC_FAIL_ANTI_REORG_DELAY: u32 = 6; #[derive(Clone, PartialEq)] @@ -335,6 +343,32 @@ 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 +} + +/// Upon discovering of some classes of onchain tx by ChannelMonitor, we may have to take actions on it +/// once they mature to enough confirmations (HTLC_FAIL_ANTI_REORG_DELAY) +#[derive(Clone, PartialEq)] +enum OnchainEvent { + /// Outpoint under claim process by our own tx, once this one get enough confirmations, we remove it from + /// bump-txn candidate buffer. + Claim { + outpoint: BitcoinOutPoint, + }, + /// 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), + }, +} + const SERIALIZATION_VERSION: u8 = 1; const MIN_SERIALIZATION_VERSION: u8 = 1; @@ -385,6 +419,11 @@ pub struct ChannelMonitor { destination_script: Script, + // 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>, + // 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 +434,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 +485,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.onchain_events_waiting_threshold_conf != other.onchain_events_waiting_threshold_conf { false } else { @@ -464,12 +536,44 @@ impl ChannelMonitor { payment_preimages: HashMap::new(), destination_script: destination_script, + onchain_events_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 { @@ -642,7 +746,7 @@ impl ChannelMonitor { let our_funding_info = funding_info; if let Storage::Local { ref funding_info, .. } = other.key_storage { if funding_info.is_none() { return Err(MonitorUpdateError("Try to combine a Local monitor without funding_info")); } - // We should be able to compare the entire funding_txo, but in fuzztarget its trivially + // We should be able to compare the entire funding_txo, but in fuzztarget it's trivially // easy to collide the funding_txo hash and have a different scriptPubKey. if funding_info.as_ref().unwrap().0 != our_funding_info.as_ref().unwrap().0 { return Err(MonitorUpdateError("Funding transaction outputs are not identical!")); @@ -796,18 +900,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 +912,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 +953,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)?; } } @@ -913,8 +1007,8 @@ impl ChannelMonitor { serialize_htlc_in_commitment!(htlc_output); if let &Some((ref their_sig, ref our_sig)) = sigs { 1u8.write(writer)?; - writer.write_all(&their_sig.serialize_compact(&self.secp_ctx))?; - writer.write_all(&our_sig.serialize_compact(&self.secp_ctx))?; + writer.write_all(&their_sig.serialize_compact())?; + writer.write_all(&our_sig.serialize_compact())?; } else { 0u8.write(writer)?; } @@ -951,6 +1045,25 @@ impl ChannelMonitor { self.last_block_hash.write(writer)?; self.destination_script.write(writer)?; + 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::Claim { ref outpoint } => { + writer.write_all(&[0; 1])?; + outpoint.write(writer)?; + }, + OnchainEvent::HTLCUpdate { ref htlc_update } => { + writer.write_all(&[1; 1])?; + htlc_update.0.write(writer)?; + htlc_update.1.write(writer)?; + } + } + } + } + Ok(()) } @@ -1014,13 +1127,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 +1141,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) } }; } @@ -1037,7 +1149,7 @@ impl ChannelMonitor { let commitment_number = 0xffffffffffff - ((((tx.input[0].sequence as u64 & 0xffffff) << 3*8) | (tx.lock_time as u64 & 0xffffff)) ^ self.commitment_transaction_number_obscure_factor); if commitment_number >= self.get_min_seen_secret() { let secret = self.get_secret(commitment_number).unwrap(); - let per_commitment_key = ignore_error!(SecretKey::from_slice(&self.secp_ctx, &secret)); + let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret)); let (revocation_pubkey, b_htlc_key, local_payment_key) = match self.key_storage { Storage::Local { ref revocation_base_key, ref htlc_base_key, ref payment_base_key, .. } => { let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key); @@ -1054,7 +1166,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)), }; @@ -1065,13 +1177,14 @@ impl ChannelMonitor { // Note that the Network here is ignored as we immediately drop the address for the // script_pubkey version. let payment_hash160 = Hash160::hash(&PublicKey::from_secret_key(&self.secp_ctx, &payment_key).serialize()); - Some(Builder::new().push_opcode(opcodes::All::OP_PUSHBYTES_0).push_slice(&payment_hash160[..]).into_script()) + Some(Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&payment_hash160[..]).into_script()) } else { None }; let mut total_value = 0; 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 +1200,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 }, @@ -1105,7 +1219,7 @@ impl ChannelMonitor { let htlc = &per_commitment_option.unwrap()[$htlc_idx.unwrap()].0; chan_utils::get_htlc_redeemscript_with_explicit_keys(htlc, &a_htlc_key, &b_htlc_key, &revocation_pubkey) }; - let sighash = ignore_error!(Message::from_slice(&$sighash_parts.sighash_all(&$input, &redeemscript, $amount)[..])); + let sighash = hash_to_message!(&$sighash_parts.sighash_all(&$input, &redeemscript, $amount)[..]); let revocation_key = ignore_error!(chan_utils::derive_private_revocation_key(&self.secp_ctx, &per_commitment_key, &revocation_base_key)); (self.secp_ctx.sign(&sighash, &revocation_key), redeemscript) }, @@ -1113,7 +1227,7 @@ impl ChannelMonitor { unimplemented!(); } }; - $input.witness.push(sig.serialize_der(&self.secp_ctx).to_vec()); + $input.witness.push(sig.serialize_der().to_vec()); $input.witness[0].push(SigHashType::All as u8); if $htlc_idx.is_none() { $input.witness.push(vec!(1)); @@ -1134,7 +1248,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 +1264,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 +1272,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 +1293,29 @@ 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.onchain_events_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 event| { + match **event { + OnchainEvent::HTLCUpdate { ref htlc_update } => { + return htlc_update.0 != **source + }, + _ => return true + } + }); + e.push(OnchainEvent::HTLCUpdate { htlc_update: ((**source).clone(), htlc.payment_hash.clone())}); + } + hash_map::Entry::Vacant(entry) => { + entry.insert(vec![OnchainEvent::HTLCUpdate { htlc_update: ((**source).clone(), htlc.payment_hash.clone())}]); + } + } } } } @@ -1199,11 +1331,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 +1343,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 +1356,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 +1376,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 +1396,23 @@ 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.onchain_events_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 event| { + match **event { + OnchainEvent::HTLCUpdate { ref htlc_update } => { + return htlc_update.0 != **source + }, + _ => return true + } + }); + e.push(OnchainEvent::HTLCUpdate { htlc_update: ((**source).clone(), htlc.payment_hash.clone())}); + } + hash_map::Entry::Vacant(entry) => { + entry.insert(vec![OnchainEvent::HTLCUpdate { htlc_update: ((**source).clone(), htlc.payment_hash.clone())}]); + } + } } } } @@ -1294,7 +1445,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 +1470,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) => { @@ -1327,7 +1479,7 @@ impl ChannelMonitor { Storage::Local { ref htlc_base_key, .. } => { let htlc = &per_commitment_option.unwrap()[$input.sequence as usize].0; let redeemscript = chan_utils::get_htlc_redeemscript_with_explicit_keys(htlc, &a_htlc_key, &b_htlc_key, &revocation_pubkey); - let sighash = ignore_error!(Message::from_slice(&$sighash_parts.sighash_all(&$input, &redeemscript, $amount)[..])); + let sighash = hash_to_message!(&$sighash_parts.sighash_all(&$input, &redeemscript, $amount)[..]); let htlc_key = ignore_error!(chan_utils::derive_private_key(&self.secp_ctx, revocation_point, &htlc_base_key)); (self.secp_ctx.sign(&sighash, &htlc_key), redeemscript) }, @@ -1335,7 +1487,7 @@ impl ChannelMonitor { unimplemented!(); } }; - $input.witness.push(sig.serialize_der(&self.secp_ctx).to_vec()); + $input.witness.push(sig.serialize_der().to_vec()); $input.witness[0].push(SigHashType::All as u8); $input.witness.push($preimage); $input.witness.push(redeemscript.into_bytes()); @@ -1349,7 +1501,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 +1517,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 +1525,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 +1569,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 +1581,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 +1594,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 +1604,11 @@ impl ChannelMonitor { } } - (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated) + (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs) } - /// Attempst 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) { + /// 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, fee_estimator: &FeeEstimator) -> (Option, Option) { if tx.input.len() != 1 || tx.output.len() != 1 { return (None, None) } @@ -1461,7 +1623,7 @@ impl ChannelMonitor { } let secret = if let Some(secret) = self.get_secret(commitment_number) { secret } else { return (None, None); }; - let per_commitment_key = ignore_error!(SecretKey::from_slice(&self.secp_ctx, &secret)); + let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret)); let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key); let revocation_pubkey = match self.key_storage { Storage::Local { ref revocation_base_key, .. } => { @@ -1498,7 +1660,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,12 +1669,16 @@ 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); let sig = match self.key_storage { Storage::Local { ref revocation_base_key, .. } => { - let sighash = ignore_error!(Message::from_slice(&sighash_parts.sighash_all(&spend_tx.input[0], &redeemscript, amount)[..])); + let sighash = hash_to_message!(&sighash_parts.sighash_all(&spend_tx.input[0], &redeemscript, amount)[..]); let revocation_key = ignore_error!(chan_utils::derive_private_revocation_key(&self.secp_ctx, &per_commitment_key, &revocation_base_key)); self.secp_ctx.sign(&sighash, &revocation_key) } @@ -1520,11 +1686,12 @@ impl ChannelMonitor { unimplemented!(); } }; - spend_tx.input[0].witness.push(sig.serialize_der(&self.secp_ctx).to_vec()); + spend_tx.input[0].witness.push(sig.serialize_der().to_vec()); spend_tx.input[0].witness[0].push(SigHashType::All as u8); 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 })) @@ -1573,9 +1740,9 @@ impl ChannelMonitor { htlc_timeout_tx.input[0].witness.push(Vec::new()); // First is the multisig dummy - htlc_timeout_tx.input[0].witness.push(their_sig.serialize_der(&self.secp_ctx).to_vec()); + htlc_timeout_tx.input[0].witness.push(their_sig.serialize_der().to_vec()); htlc_timeout_tx.input[0].witness[1].push(SigHashType::All as u8); - htlc_timeout_tx.input[0].witness.push(our_sig.serialize_der(&self.secp_ctx).to_vec()); + htlc_timeout_tx.input[0].witness.push(our_sig.serialize_der().to_vec()); htlc_timeout_tx.input[0].witness[2].push(SigHashType::All as u8); htlc_timeout_tx.input[0].witness.push(Vec::new()); @@ -1590,9 +1757,9 @@ impl ChannelMonitor { htlc_success_tx.input[0].witness.push(Vec::new()); // First is the multisig dummy - htlc_success_tx.input[0].witness.push(their_sig.serialize_der(&self.secp_ctx).to_vec()); + htlc_success_tx.input[0].witness.push(their_sig.serialize_der().to_vec()); htlc_success_tx.input[0].witness[1].push(SigHashType::All as u8); - htlc_success_tx.input[0].witness.push(our_sig.serialize_der(&self.secp_ctx).to_vec()); + htlc_success_tx.input[0].witness.push(our_sig.serialize_der().to_vec()); htlc_success_tx.input[0].witness[2].push(SigHashType::All as u8); htlc_success_tx.input[0].witness.push(payment_preimage.0.to_vec()); @@ -1613,42 +1780,97 @@ impl ChannelMonitor { /// Attempts to claim any claimable HTLCs in a commitment transaction which was not (yet) /// revoked using data in local_claimable_outpoints. /// Should not be used if check_spend_revoked_transaction succeeds. - fn check_spend_local_transaction(&self, tx: &Transaction, _height: u32) -> (Vec, Vec, (Sha256dHash, Vec)) { + fn check_spend_local_transaction(&mut self, tx: &Transaction, height: u32) -> (Vec, Vec, (Sha256dHash, Vec)) { let commitment_txid = tx.txid(); - // TODO: If we find a match here we need to fail back HTLCs that were't included in the - // broadcast commitment transaction, either because they didn't meet dust or because they - // weren't yet included in our commitment transaction(s). + let mut local_txn = Vec::new(); + let mut spendable_outputs = 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!(self, "Failing HTLC with payment_hash {} from {} local commitment tx due to broadcast of transaction, waiting confirmation (at height{})", log_bytes!($payment_hash.0), $commitment_tx, height + HTLC_FAIL_ANTI_REORG_DELAY - 1); + match self.onchain_events_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 event| { + match **event { + OnchainEvent::HTLCUpdate { ref htlc_update } => { + return htlc_update.0 != $source + }, + _ => return true + } + }); + e.push(OnchainEvent::HTLCUpdate { htlc_update: ($source, $payment_hash)}); + } + hash_map::Entry::Vacant(entry) => { + entry.insert(vec![OnchainEvent::HTLCUpdate { htlc_update: ($source, $payment_hash)}]); + } + } + } + } + + macro_rules! append_onchain_update { + ($updates: expr) => { + local_txn.append(&mut $updates.0); + spendable_outputs.append(&mut $updates.1); + watch_outputs.append(&mut $updates.2); + } + } + + // HTLCs set may differ between last and previous local commitment txn, in case of one them hitting chain, ensure we cancel all HTLCs backward + let mut is_local_tx = false; + if let &Some(ref local_tx) = &self.current_local_signed_commitment_tx { if local_tx.txid == commitment_txid { + is_local_tx = true; log_trace!(self, "Got latest local commitment tx broadcast, searching for available HTLCs to claim"); match self.key_storage { Storage::Local { ref delayed_payment_base_key, ref latest_per_commitment_point, .. } => { - let (local_txn, spendable_outputs, watch_outputs) = self.broadcast_by_local_state(local_tx, latest_per_commitment_point, &Some(*delayed_payment_base_key)); - return (local_txn, spendable_outputs, (commitment_txid, watch_outputs)); + append_onchain_update!(self.broadcast_by_local_state(local_tx, latest_per_commitment_point, &Some(*delayed_payment_base_key))); }, Storage::Watchtower { .. } => { - let (local_txn, spendable_outputs, watch_outputs) = self.broadcast_by_local_state(local_tx, &None, &None); - return (local_txn, spendable_outputs, (commitment_txid, watch_outputs)); + append_onchain_update!(self.broadcast_by_local_state(local_tx, &None, &None)); } } } } if let &Some(ref local_tx) = &self.prev_local_signed_commitment_tx { if local_tx.txid == commitment_txid { + is_local_tx = true; log_trace!(self, "Got previous local commitment tx broadcast, searching for available HTLCs to claim"); match self.key_storage { Storage::Local { ref delayed_payment_base_key, ref prev_latest_per_commitment_point, .. } => { - let (local_txn, spendable_outputs, watch_outputs) = self.broadcast_by_local_state(local_tx, prev_latest_per_commitment_point, &Some(*delayed_payment_base_key)); - return (local_txn, spendable_outputs, (commitment_txid, watch_outputs)); + append_onchain_update!(self.broadcast_by_local_state(local_tx, prev_latest_per_commitment_point, &Some(*delayed_payment_base_key))); }, Storage::Watchtower { .. } => { - let (local_txn, spendable_outputs, watch_outputs) = self.broadcast_by_local_state(local_tx, &None, &None); - return (local_txn, spendable_outputs, (commitment_txid, watch_outputs)); + append_onchain_update!(self.broadcast_by_local_state(local_tx, &None, &None)); } } } } - (Vec::new(), Vec::new(), (commitment_txid, Vec::new())) + + macro_rules! fail_dust_htlcs_after_threshold_conf { + ($local_tx: expr) => { + for &(ref htlc, _, ref source) in &$local_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()); + } + } + } + } + } + + if is_local_tx { + if let &Some(ref local_tx) = &self.current_local_signed_commitment_tx { + fail_dust_htlcs_after_threshold_conf!(local_tx); + } + if let &Some(ref local_tx) = &self.prev_local_signed_commitment_tx { + fail_dust_htlcs_after_threshold_conf!(local_tx); + } + } + + (local_txn, spendable_outputs, (commitment_txid, watch_outputs)) } /// Generate a spendable output event when closing_transaction get registered onchain. @@ -1657,7 +1879,7 @@ impl ChannelMonitor { match self.key_storage { Storage::Local { ref shutdown_pubkey, .. } => { let our_channel_close_key_hash = Hash160::hash(&shutdown_pubkey.serialize()); - let shutdown_script = Builder::new().push_opcode(opcodes::All::OP_PUSHBYTES_0).push_slice(&our_channel_close_key_hash[..]).into_script(); + let shutdown_script = Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&our_channel_close_key_hash[..]).into_script(); for (idx, output) in tx.output.iter().enumerate() { if shutdown_script == output.script_pubkey { return Some(SpendableOutputDescriptor::StaticOutput { @@ -1669,7 +1891,7 @@ impl ChannelMonitor { } Storage::Watchtower { .. } => { //TODO: we need to ensure an offline client will generate the event when it - // cames back online after only the watchtower saw the transaction + // comes back online after only the watchtower saw the transaction } } } @@ -1693,7 +1915,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 +1936,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 +1955,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); } @@ -1754,7 +1973,7 @@ impl ChannelMonitor { // While all commitment/HTLC-Success/HTLC-Timeout transactions have one input, HTLCs // can also be resolved in a few other ways which can have more than one output. Thus, // we call is_resolving_htlc_output here outside of the tx.input.len() == 1 check. - let mut updated = self.is_resolving_htlc_output(tx); + let mut updated = self.is_resolving_htlc_output(tx, height); if updated.len() > 0 { htlc_updated.append(&mut updated); } @@ -1786,10 +2005,31 @@ impl ChannelMonitor { } } } + if let Some(events) = self.onchain_events_waiting_threshold_conf.remove(&height) { + for ev in events { + match ev { + OnchainEvent::Claim { outpoint: _ } => { + }, + OnchainEvent::HTLCUpdate { htlc_update } => { + log_trace!(self, "HTLC {} failure update has got enough confirmations to be passed upstream", log_bytes!((htlc_update.1).0)); + htlc_updated.push((htlc_update.0, None, htlc_update.1)); + }, + } + } + } 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.onchain_events_waiting_threshold_conf.remove(&(height + HTLC_FAIL_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 + //- our claim tx on a commitment tx output + } + 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 @@ -1821,9 +2061,12 @@ impl ChannelMonitor { // least CLTV_CLAIM_BUFFER blocks prior to the inbound HTLC. // aka outbound_cltv + HTLC_FAIL_TIMEOUT_BLOCKS == height - CLTV_CLAIM_BUFFER // inbound_cltv == height + CLTV_CLAIM_BUFFER - // outbound_cltv + HTLC_FAIL_TIMEOUT_BLOCKS + CLTV_CLAIM_BUFER <= inbound_cltv - CLTV_CLAIM_BUFFER - // HTLC_FAIL_TIMEOUT_BLOCKS + 2*CLTV_CLAIM_BUFER <= inbound_cltv - outbound_cltv - // HTLC_FAIL_TIMEOUT_BLOCKS + 2*CLTV_CLAIM_BUFER <= CLTV_EXPIRY_DELTA + // outbound_cltv + HTLC_FAIL_TIMEOUT_BLOCKS + CLTV_CLAIM_BUFFER <= inbound_cltv - CLTV_CLAIM_BUFFER + // HTLC_FAIL_TIMEOUT_BLOCKS + 2*CLTV_CLAIM_BUFFER <= inbound_cltv - outbound_cltv + // CLTV_EXPIRY_DELTA <= inbound_cltv - outbound_cltv (by check in ChannelManager::decode_update_add_htlc_onion) + // HTLC_FAIL_TIMEOUT_BLOCKS + 2*CLTV_CLAIM_BUFFER <= CLTV_EXPIRY_DELTA + // The final, above, condition is checked for statically in channelmanager + // with CHECK_CLTV_EXPIRY_SANITY_2. let htlc_outbound = $local_tx == htlc.offered; if ( htlc_outbound && htlc.cltv_expiry + HTLC_FAIL_TIMEOUT_BLOCKS <= height) || (!htlc_outbound && htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER && self.payment_preimages.contains_key(&htlc.payment_hash)) { @@ -1856,7 +2099,7 @@ impl ChannelMonitor { /// Check if any transaction broadcasted is resolving HTLC output by a success or timeout on a local /// or remote commitment tx, if so send back the source, preimage if found and payment_hash of resolved HTLC - fn is_resolving_htlc_output(&mut self, tx: &Transaction) -> Vec<(HTLCSource, Option, PaymentHash)> { + fn is_resolving_htlc_output(&mut self, tx: &Transaction, height: u32) -> Vec<(HTLCSource, Option, PaymentHash)> { let mut htlc_updated = Vec::new(); 'outer_loop: for input in &tx.input { @@ -1869,8 +2112,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)) { @@ -1887,6 +2131,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 { @@ -1899,7 +2159,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; } @@ -1936,7 +2206,24 @@ impl ChannelMonitor { payment_preimage.0.copy_from_slice(&input.witness[1]); htlc_updated.push((source, Some(payment_preimage), payment_hash)); } else { - htlc_updated.push((source, None, payment_hash)); + log_info!(self, "Failing HTLC with payment_hash {} timeout by a spend tx, waiting for confirmation (at height{})", log_bytes!(payment_hash.0), height + HTLC_FAIL_ANTI_REORG_DELAY - 1); + match self.onchain_events_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 event| { + match **event { + OnchainEvent::HTLCUpdate { ref htlc_update } => { + return htlc_update.0 != source + }, + _ => return true + } + }); + e.push(OnchainEvent::HTLCUpdate { htlc_update: (source, payment_hash)}); + } + hash_map::Entry::Vacant(entry) => { + entry.insert(vec![OnchainEvent::HTLCUpdate { htlc_update: (source, payment_hash)}]); + } + } } } } @@ -1957,13 +2244,6 @@ impl ReadableArgs> for (Sha256dHash, ChannelM } } } - macro_rules! read_option { () => { - match >::read(reader)? { - 0 => None, - 1 => Some(Readable::read(reader)?), - _ => return Err(DecodeError::InvalidValue), - } - } } let _ver: u8 = Readable::read(reader)?; let min_ver: u8 = Readable::read(reader)?; @@ -1980,16 +2260,8 @@ impl ReadableArgs> for (Sha256dHash, ChannelM let delayed_payment_base_key = Readable::read(reader)?; let payment_base_key = Readable::read(reader)?; let shutdown_pubkey = Readable::read(reader)?; - let prev_latest_per_commitment_point = match >::read(reader)? { - 0 => None, - 1 => Some(Readable::read(reader)?), - _ => return Err(DecodeError::InvalidValue), - }; - let latest_per_commitment_point = match >::read(reader)? { - 0 => None, - 1 => Some(Readable::read(reader)?), - _ => return Err(DecodeError::InvalidValue), - }; + let prev_latest_per_commitment_point = Readable::read(reader)?; + let latest_per_commitment_point = Readable::read(reader)?; // Technically this can fail and serialize fail a round-trip, but only for serialization of // barely-init'd ChannelMonitors that we can't do anything with. let outpoint = OutPoint { @@ -1997,8 +2269,8 @@ impl ReadableArgs> for (Sha256dHash, ChannelM index: Readable::read(reader)?, }; let funding_info = Some((outpoint, Readable::read(reader)?)); - let current_remote_commitment_txid = read_option!(); - let prev_remote_commitment_txid = read_option!(); + let current_remote_commitment_txid = Readable::read(reader)?; + let prev_remote_commitment_txid = Readable::read(reader)?; Storage::Local { revocation_base_key, htlc_base_key, @@ -2028,7 +2300,7 @@ impl ReadableArgs> for (Sha256dHash, ChannelM if second_point_slice[0..32] == [0; 32] && second_point_slice[32] == 0 { Some((first_idx, first_point, None)) } else { - Some((first_idx, first_point, Some(unwrap_obj!(PublicKey::from_slice(&secp_ctx, &second_point_slice))))) + Some((first_idx, first_point, Some(unwrap_obj!(PublicKey::from_slice(&second_point_slice))))) } } }; @@ -2049,7 +2321,7 @@ impl ReadableArgs> for (Sha256dHash, ChannelM let amount_msat: u64 = Readable::read(reader)?; let cltv_expiry: u32 = Readable::read(reader)?; let payment_hash: PaymentHash = Readable::read(reader)?; - let transaction_output_index: Option = read_option!(); + let transaction_output_index: Option = Readable::read(reader)?; HTLCOutputInCommitment { offered, amount_msat, cltv_expiry, payment_hash, transaction_output_index @@ -2065,7 +2337,7 @@ impl ReadableArgs> for (Sha256dHash, ChannelM let htlcs_count: u64 = Readable::read(reader)?; let mut htlcs = Vec::with_capacity(cmp::min(htlcs_count as usize, MAX_ALLOC_SIZE / 32)); for _ in 0..htlcs_count { - htlcs.push((read_htlc_in_commitment!(), read_option!().map(|o: HTLCSource| Box::new(o)))); + htlcs.push((read_htlc_in_commitment!(), as Readable>::read(reader)?.map(|o: HTLCSource| Box::new(o)))); } if let Some(_) = remote_claimable_outpoints.insert(txid, htlcs) { return Err(DecodeError::InvalidValue); @@ -2128,7 +2400,7 @@ impl ReadableArgs> for (Sha256dHash, ChannelM 1 => Some((Readable::read(reader)?, Readable::read(reader)?)), _ => return Err(DecodeError::InvalidValue), }; - htlcs.push((htlc, sigs, read_option!())); + htlcs.push((htlc, sigs, Readable::read(reader)?)); } LocalSignedTx { @@ -2171,6 +2443,34 @@ 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 onchain_events_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 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 outpoint = Readable::read(reader)?; + OnchainEvent::Claim { + outpoint + } + }, + 1 => { + let htlc_source = Readable::read(reader)?; + let hash = Readable::read(reader)?; + OnchainEvent::HTLCUpdate { + htlc_update: (htlc_source, hash) + } + }, + _ => return Err(DecodeError::InvalidValue), + }; + events.push(ev); + } + onchain_events_waiting_threshold_conf.insert(height_target, events); + } + Ok((last_block_hash.clone(), ChannelMonitor { commitment_transaction_number_obscure_factor, @@ -2194,6 +2494,9 @@ impl ReadableArgs> for (Sha256dHash, ChannelM payment_preimages, destination_script, + + onchain_events_waiting_threshold_conf, + last_block_hash, secp_ctx, logger, @@ -2204,13 +2507,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}; @@ -2240,7 +2549,7 @@ mod tests { { // insert_secret correct sequence - monitor = ChannelMonitor::new(&SecretKey::from_slice(&secp_ctx, &[42; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[43; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[44; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[44; 32]).unwrap(), &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&secp_ctx, &[45; 32]).unwrap()), 0, Script::new(), logger.clone()); + monitor = ChannelMonitor::new(&SecretKey::from_slice(&[42; 32]).unwrap(), &SecretKey::from_slice(&[43; 32]).unwrap(), &SecretKey::from_slice(&[44; 32]).unwrap(), &SecretKey::from_slice(&[44; 32]).unwrap(), &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[45; 32]).unwrap()), 0, Script::new(), logger.clone()); secrets.clear(); secrets.push([0; 32]); @@ -2286,7 +2595,7 @@ mod tests { { // insert_secret #1 incorrect - monitor = ChannelMonitor::new(&SecretKey::from_slice(&secp_ctx, &[42; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[43; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[44; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[44; 32]).unwrap(), &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&secp_ctx, &[45; 32]).unwrap()), 0, Script::new(), logger.clone()); + monitor = ChannelMonitor::new(&SecretKey::from_slice(&[42; 32]).unwrap(), &SecretKey::from_slice(&[43; 32]).unwrap(), &SecretKey::from_slice(&[44; 32]).unwrap(), &SecretKey::from_slice(&[44; 32]).unwrap(), &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[45; 32]).unwrap()), 0, Script::new(), logger.clone()); secrets.clear(); secrets.push([0; 32]); @@ -2302,7 +2611,7 @@ mod tests { { // insert_secret #2 incorrect (#1 derived from incorrect) - monitor = ChannelMonitor::new(&SecretKey::from_slice(&secp_ctx, &[42; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[43; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[44; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[44; 32]).unwrap(), &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&secp_ctx, &[45; 32]).unwrap()), 0, Script::new(), logger.clone()); + monitor = ChannelMonitor::new(&SecretKey::from_slice(&[42; 32]).unwrap(), &SecretKey::from_slice(&[43; 32]).unwrap(), &SecretKey::from_slice(&[44; 32]).unwrap(), &SecretKey::from_slice(&[44; 32]).unwrap(), &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[45; 32]).unwrap()), 0, Script::new(), logger.clone()); secrets.clear(); secrets.push([0; 32]); @@ -2328,7 +2637,7 @@ mod tests { { // insert_secret #3 incorrect - monitor = ChannelMonitor::new(&SecretKey::from_slice(&secp_ctx, &[42; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[43; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[44; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[44; 32]).unwrap(), &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&secp_ctx, &[45; 32]).unwrap()), 0, Script::new(), logger.clone()); + monitor = ChannelMonitor::new(&SecretKey::from_slice(&[42; 32]).unwrap(), &SecretKey::from_slice(&[43; 32]).unwrap(), &SecretKey::from_slice(&[44; 32]).unwrap(), &SecretKey::from_slice(&[44; 32]).unwrap(), &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[45; 32]).unwrap()), 0, Script::new(), logger.clone()); secrets.clear(); secrets.push([0; 32]); @@ -2354,7 +2663,7 @@ mod tests { { // insert_secret #4 incorrect (1,2,3 derived from incorrect) - monitor = ChannelMonitor::new(&SecretKey::from_slice(&secp_ctx, &[42; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[43; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[44; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[44; 32]).unwrap(), &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&secp_ctx, &[45; 32]).unwrap()), 0, Script::new(), logger.clone()); + monitor = ChannelMonitor::new(&SecretKey::from_slice(&[42; 32]).unwrap(), &SecretKey::from_slice(&[43; 32]).unwrap(), &SecretKey::from_slice(&[44; 32]).unwrap(), &SecretKey::from_slice(&[44; 32]).unwrap(), &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[45; 32]).unwrap()), 0, Script::new(), logger.clone()); secrets.clear(); secrets.push([0; 32]); @@ -2400,7 +2709,7 @@ mod tests { { // insert_secret #5 incorrect - monitor = ChannelMonitor::new(&SecretKey::from_slice(&secp_ctx, &[42; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[43; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[44; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[44; 32]).unwrap(), &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&secp_ctx, &[45; 32]).unwrap()), 0, Script::new(), logger.clone()); + monitor = ChannelMonitor::new(&SecretKey::from_slice(&[42; 32]).unwrap(), &SecretKey::from_slice(&[43; 32]).unwrap(), &SecretKey::from_slice(&[44; 32]).unwrap(), &SecretKey::from_slice(&[44; 32]).unwrap(), &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[45; 32]).unwrap()), 0, Script::new(), logger.clone()); secrets.clear(); secrets.push([0; 32]); @@ -2436,7 +2745,7 @@ mod tests { { // insert_secret #6 incorrect (5 derived from incorrect) - monitor = ChannelMonitor::new(&SecretKey::from_slice(&secp_ctx, &[42; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[43; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[44; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[44; 32]).unwrap(), &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&secp_ctx, &[45; 32]).unwrap()), 0, Script::new(), logger.clone()); + monitor = ChannelMonitor::new(&SecretKey::from_slice(&[42; 32]).unwrap(), &SecretKey::from_slice(&[43; 32]).unwrap(), &SecretKey::from_slice(&[44; 32]).unwrap(), &SecretKey::from_slice(&[44; 32]).unwrap(), &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[45; 32]).unwrap()), 0, Script::new(), logger.clone()); secrets.clear(); secrets.push([0; 32]); @@ -2482,7 +2791,7 @@ mod tests { { // insert_secret #7 incorrect - monitor = ChannelMonitor::new(&SecretKey::from_slice(&secp_ctx, &[42; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[43; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[44; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[44; 32]).unwrap(), &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&secp_ctx, &[45; 32]).unwrap()), 0, Script::new(), logger.clone()); + monitor = ChannelMonitor::new(&SecretKey::from_slice(&[42; 32]).unwrap(), &SecretKey::from_slice(&[43; 32]).unwrap(), &SecretKey::from_slice(&[44; 32]).unwrap(), &SecretKey::from_slice(&[44; 32]).unwrap(), &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[45; 32]).unwrap()), 0, Script::new(), logger.clone()); secrets.clear(); secrets.push([0; 32]); @@ -2528,7 +2837,7 @@ mod tests { { // insert_secret #8 incorrect - monitor = ChannelMonitor::new(&SecretKey::from_slice(&secp_ctx, &[42; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[43; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[44; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[44; 32]).unwrap(), &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&secp_ctx, &[45; 32]).unwrap()), 0, Script::new(), logger.clone()); + monitor = ChannelMonitor::new(&SecretKey::from_slice(&[42; 32]).unwrap(), &SecretKey::from_slice(&[43; 32]).unwrap(), &SecretKey::from_slice(&[44; 32]).unwrap(), &SecretKey::from_slice(&[44; 32]).unwrap(), &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[45; 32]).unwrap()), 0, Script::new(), logger.clone()); secrets.clear(); secrets.push([0; 32]); @@ -2578,7 +2887,7 @@ mod tests { let secp_ctx = Secp256k1::new(); let logger = Arc::new(TestLogger::new()); - let dummy_key = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&secp_ctx, &[42; 32]).unwrap()); + let dummy_key = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); macro_rules! dummy_keys { () => { { @@ -2643,7 +2952,7 @@ mod tests { // Prune with one old state and a local commitment tx holding a few overlaps with the // old state. - let mut monitor = ChannelMonitor::new(&SecretKey::from_slice(&secp_ctx, &[42; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[43; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[44; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[44; 32]).unwrap(), &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&secp_ctx, &[45; 32]).unwrap()), 0, Script::new(), logger.clone()); + let mut monitor = ChannelMonitor::new(&SecretKey::from_slice(&[42; 32]).unwrap(), &SecretKey::from_slice(&[43; 32]).unwrap(), &SecretKey::from_slice(&[44; 32]).unwrap(), &SecretKey::from_slice(&[44; 32]).unwrap(), &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[45; 32]).unwrap()), 0, Script::new(), logger.clone()); monitor.set_their_to_self_delay(10); monitor.provide_latest_local_commitment_tx_info(dummy_tx.clone(), dummy_keys!(), 0, preimages_to_local_htlcs!(preimages[0..10])); @@ -2687,5 +2996,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. }