From 50b6acb6967e19718cf264537c1a32c122c6815b Mon Sep 17 00:00:00 2001 From: Antoine Riard Date: Mon, 8 Apr 2019 21:11:16 -0400 Subject: [PATCH 1/4] Add FeeEstimator in ChannelMonitor --- fuzz/fuzz_targets/chanmon_fail_consistency.rs | 6 +- fuzz/fuzz_targets/full_stack_target.rs | 2 +- src/ln/channelmonitor.rs | 69 ++++++++++++++++--- src/ln/functional_test_utils.rs | 2 +- src/ln/functional_tests.rs | 6 +- src/util/test_utils.rs | 4 +- 6 files changed, 69 insertions(+), 20 deletions(-) diff --git a/fuzz/fuzz_targets/chanmon_fail_consistency.rs b/fuzz/fuzz_targets/chanmon_fail_consistency.rs index f457a30913b..dd8023ecddc 100644 --- a/fuzz/fuzz_targets/chanmon_fail_consistency.rs +++ b/fuzz/fuzz_targets/chanmon_fail_consistency.rs @@ -69,9 +69,9 @@ pub struct TestChannelMonitor { pub update_ret: Mutex>, } impl TestChannelMonitor { - pub fn new(chain_monitor: Arc, broadcaster: Arc, logger: Arc) -> Self { + pub fn new(chain_monitor: Arc, broadcaster: Arc, logger: Arc, feeest: Arc) -> Self { Self { - simple_monitor: channelmonitor::SimpleManyChannelMonitor::new(chain_monitor, broadcaster, logger), + simple_monitor: channelmonitor::SimpleManyChannelMonitor::new(chain_monitor, broadcaster, logger, feeest), update_ret: Mutex::new(Ok(())), } } @@ -142,7 +142,7 @@ pub fn do_test(data: &[u8]) { ($node_id: expr) => { { let logger: Arc = Arc::new(test_logger::TestLogger::new($node_id.to_string())); let watch = Arc::new(ChainWatchInterfaceUtil::new(Network::Bitcoin, Arc::clone(&logger))); - let monitor = Arc::new(TestChannelMonitor::new(watch.clone(), broadcast.clone(), logger.clone())); + let monitor = Arc::new(TestChannelMonitor::new(watch.clone(), broadcast.clone(), logger.clone(), fee_est.clone())); let keys_manager = Arc::new(KeyProvider { node_id: $node_id }); let mut config = UserConfig::new(); diff --git a/fuzz/fuzz_targets/full_stack_target.rs b/fuzz/fuzz_targets/full_stack_target.rs index 937c735064b..8b477dfa905 100644 --- a/fuzz/fuzz_targets/full_stack_target.rs +++ b/fuzz/fuzz_targets/full_stack_target.rs @@ -344,7 +344,7 @@ pub fn do_test(data: &[u8], logger: &Arc) { let watch = Arc::new(ChainWatchInterfaceUtil::new(Network::Bitcoin, Arc::clone(&logger))); let broadcast = Arc::new(TestBroadcaster{}); - let monitor = channelmonitor::SimpleManyChannelMonitor::new(watch.clone(), broadcast.clone(), Arc::clone(&logger)); + let monitor = channelmonitor::SimpleManyChannelMonitor::new(watch.clone(), broadcast.clone(), Arc::clone(&logger), fee_est.clone()); let keys_manager = Arc::new(KeyProvider { node_secret: our_network_key.clone() }); let mut config = UserConfig::new(); diff --git a/src/ln/channelmonitor.rs b/src/ln/channelmonitor.rs index 25f41d88296..00e14754973 100644 --- a/src/ln/channelmonitor.rs +++ b/src/ln/channelmonitor.rs @@ -34,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; @@ -143,6 +143,7 @@ pub struct SimpleManyChannelMonitor { pending_events: Mutex>, pending_htlc_updated: Mutex)>>>, logger: Arc, + fee_estimator: Arc } impl ChainListener for SimpleManyChannelMonitor { @@ -153,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, @@ -210,7 +211,7 @@ impl ChainListener for SimpleManyChannelMonit 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, @@ -218,6 +219,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); @@ -340,6 +342,14 @@ struct LocalSignedTx { htlc_outputs: Vec<(HTLCOutputInCommitment, Option<(Signature, Signature)>, Option)>, } +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; @@ -475,6 +485,36 @@ impl ChannelMonitor { } } + 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 { @@ -1019,7 +1059,7 @@ 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, Vec<(HTLCSource, Option, PaymentHash)>) { // 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(); @@ -1077,6 +1117,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 { @@ -1092,6 +1133,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 }, @@ -1155,6 +1197,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, @@ -1162,9 +1205,10 @@ 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, }), }; + single_htlc_tx.output[0].value -= fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) * (single_htlc_tx.get_weight() + Self::get_witnesses_weight(&[if htlc.offered { InputDescriptors::RevokedOfferedHTLC } else { InputDescriptors::RevokedReceivedHTLC }])) / 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); @@ -1208,7 +1252,7 @@ impl ChannelMonitor { 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, @@ -1216,6 +1260,7 @@ impl ChannelMonitor { input: inputs, output: outputs, }; + spend_tx.output[0].value -= fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) * (spend_tx.get_weight() + Self::get_witnesses_weight(&input_descriptors[..])) / 1000; let mut values_drain = values.drain(..); let sighash_parts = bip143::SighashComponents::new(&spend_tx); @@ -1324,6 +1369,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) => { @@ -1370,6 +1416,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, @@ -1377,9 +1424,10 @@ 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, }), }; + single_htlc_tx.output[0].value -= fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) * (single_htlc_tx.get_weight() + Self::get_witnesses_weight(&[if htlc.offered { InputDescriptors::OfferedHTLC } else { InputDescriptors::ReceivedHTLC }])) / 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 { @@ -1421,7 +1469,7 @@ impl ChannelMonitor { 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, @@ -1429,6 +1477,7 @@ impl ChannelMonitor { input: inputs, output: outputs, }; + spend_tx.output[0].value -= fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) * (spend_tx.get_weight() + Self::get_witnesses_weight(&input_descriptors[..])) / 1000; let mut values_drain = values.drain(..); let sighash_parts = bip143::SighashComponents::new(&spend_tx); @@ -1698,7 +1747,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(); @@ -1719,7 +1768,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, mut updated) = self.check_spend_remote_transaction(tx, height, fee_estimator); txn = remote_txn; spendable_outputs.append(&mut spendable_output); if !new_outputs.1.is_empty() { diff --git a/src/ln/functional_test_utils.rs b/src/ln/functional_test_utils.rs index 387c83761a6..c5d6751d9a9 100644 --- a/src/ln/functional_test_utils.rs +++ b/src/ln/functional_test_utils.rs @@ -821,7 +821,7 @@ pub fn create_network(node_count: usize) -> Vec { let mut seed = [0; 32]; rng.fill_bytes(&mut seed); let keys_manager = Arc::new(test_utils::TestKeysInterface::new(&seed, Network::Testnet, Arc::clone(&logger))); - let chan_monitor = Arc::new(test_utils::TestChannelMonitor::new(chain_monitor.clone(), tx_broadcaster.clone(), logger.clone())); + let chan_monitor = Arc::new(test_utils::TestChannelMonitor::new(chain_monitor.clone(), tx_broadcaster.clone(), logger.clone(), feeest.clone())); let mut config = UserConfig::new(); config.channel_options.announced_channel = true; config.channel_limits.force_announced_channel_preference = false; diff --git a/src/ln/functional_tests.rs b/src/ln/functional_tests.rs index 0e82b4e696b..00225f18bc9 100644 --- a/src/ln/functional_tests.rs +++ b/src/ln/functional_tests.rs @@ -3210,7 +3210,7 @@ fn test_no_txn_manager_serialize_deserialize() { let mut chan_0_monitor_serialized = test_utils::TestVecWriter(Vec::new()); nodes[0].chan_monitor.simple_monitor.monitors.lock().unwrap().iter().next().unwrap().1.write_for_disk(&mut chan_0_monitor_serialized).unwrap(); - nodes[0].chan_monitor = Arc::new(test_utils::TestChannelMonitor::new(nodes[0].chain_monitor.clone(), nodes[0].tx_broadcaster.clone(), Arc::new(test_utils::TestLogger::new()))); + nodes[0].chan_monitor = Arc::new(test_utils::TestChannelMonitor::new(nodes[0].chain_monitor.clone(), nodes[0].tx_broadcaster.clone(), Arc::new(test_utils::TestLogger::new()), Arc::new(test_utils::TestFeeEstimator { sat_per_kw: 253 }))); let mut chan_0_monitor_read = &chan_0_monitor_serialized.0[..]; let (_, chan_0_monitor) = <(Sha256dHash, ChannelMonitor)>::read(&mut chan_0_monitor_read, Arc::new(test_utils::TestLogger::new())).unwrap(); assert!(chan_0_monitor_read.is_empty()); @@ -3276,7 +3276,7 @@ fn test_simple_manager_serialize_deserialize() { let mut chan_0_monitor_serialized = test_utils::TestVecWriter(Vec::new()); nodes[0].chan_monitor.simple_monitor.monitors.lock().unwrap().iter().next().unwrap().1.write_for_disk(&mut chan_0_monitor_serialized).unwrap(); - nodes[0].chan_monitor = Arc::new(test_utils::TestChannelMonitor::new(nodes[0].chain_monitor.clone(), nodes[0].tx_broadcaster.clone(), Arc::new(test_utils::TestLogger::new()))); + nodes[0].chan_monitor = Arc::new(test_utils::TestChannelMonitor::new(nodes[0].chain_monitor.clone(), nodes[0].tx_broadcaster.clone(), Arc::new(test_utils::TestLogger::new()), Arc::new(test_utils::TestFeeEstimator { sat_per_kw: 253 }))); let mut chan_0_monitor_read = &chan_0_monitor_serialized.0[..]; let (_, chan_0_monitor) = <(Sha256dHash, ChannelMonitor)>::read(&mut chan_0_monitor_read, Arc::new(test_utils::TestLogger::new())).unwrap(); assert!(chan_0_monitor_read.is_empty()); @@ -3336,7 +3336,7 @@ fn test_manager_serialize_deserialize_inconsistent_monitor() { node_0_monitors_serialized.push(writer.0); } - nodes[0].chan_monitor = Arc::new(test_utils::TestChannelMonitor::new(nodes[0].chain_monitor.clone(), nodes[0].tx_broadcaster.clone(), Arc::new(test_utils::TestLogger::new()))); + nodes[0].chan_monitor = Arc::new(test_utils::TestChannelMonitor::new(nodes[0].chain_monitor.clone(), nodes[0].tx_broadcaster.clone(), Arc::new(test_utils::TestLogger::new()), Arc::new(test_utils::TestFeeEstimator { sat_per_kw: 253 }))); let mut node_0_monitors = Vec::new(); for serialized in node_0_monitors_serialized.iter() { let mut read = &serialized[..]; diff --git a/src/util/test_utils.rs b/src/util/test_utils.rs index b9c12a0761e..5d9b006882e 100644 --- a/src/util/test_utils.rs +++ b/src/util/test_utils.rs @@ -46,10 +46,10 @@ pub struct TestChannelMonitor { pub update_ret: Mutex>, } impl TestChannelMonitor { - pub fn new(chain_monitor: Arc, broadcaster: Arc, logger: Arc) -> Self { + pub fn new(chain_monitor: Arc, broadcaster: Arc, logger: Arc, fee_estimator: Arc) -> Self { Self { added_monitors: Mutex::new(Vec::new()), - simple_monitor: channelmonitor::SimpleManyChannelMonitor::new(chain_monitor, broadcaster, logger), + simple_monitor: channelmonitor::SimpleManyChannelMonitor::new(chain_monitor, broadcaster, logger, fee_estimator), update_ret: Mutex::new(Ok(())), } } From 0843c22c67408b2f6b1c1f745c245a3180f6cd9d Mon Sep 17 00:00:00 2001 From: Antoine Riard Date: Tue, 9 Apr 2019 19:43:03 -0400 Subject: [PATCH 2/4] Add fee estimation in check_spend_remote_htlc --- src/ln/channelmonitor.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ln/channelmonitor.rs b/src/ln/channelmonitor.rs index 00e14754973..44206458b12 100644 --- a/src/ln/channelmonitor.rs +++ b/src/ln/channelmonitor.rs @@ -1500,7 +1500,7 @@ impl ChannelMonitor { } /// 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) } @@ -1552,7 +1552,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 { @@ -1561,6 +1561,7 @@ impl ChannelMonitor { input: inputs, output: outputs, }; + spend_tx.output[0].value -= fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) * (spend_tx.get_weight() + Self::get_witnesses_weight(&vec![InputDescriptors::RevokedOutput])) / 1000; let sighash_parts = bip143::SighashComponents::new(&spend_tx); @@ -1792,7 +1793,7 @@ impl ChannelMonitor { } } 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); } From bca12ea48e0f7be2f28dd96b5fbdda89c8cbde53 Mon Sep 17 00:00:00 2001 From: Antoine Riard Date: Wed, 10 Apr 2019 18:56:22 -0400 Subject: [PATCH 3/4] Add unit test for get_witnesses_weight --- src/ln/channelmonitor.rs | 125 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 3 deletions(-) diff --git a/src/ln/channelmonitor.rs b/src/ln/channelmonitor.rs index 44206458b12..15db1426735 100644 --- a/src/ln/channelmonitor.rs +++ b/src/ln/channelmonitor.rs @@ -342,6 +342,7 @@ struct LocalSignedTx { htlc_outputs: Vec<(HTLCOutputInCommitment, Option<(Signature, Signature)>, Option)>, } +#[derive(PartialEq)] enum InputDescriptors { RevokedOfferedHTLC, RevokedReceivedHTLC, @@ -2274,13 +2275,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}; @@ -2757,5 +2764,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. } From 1c7ba90b0967aa6cba9806599ec48da439357a48 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Sun, 14 Apr 2019 16:13:44 -0400 Subject: [PATCH 4/4] Assert that our weight prediction can't result in unspendable txn --- src/ln/channelmonitor.rs | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/ln/channelmonitor.rs b/src/ln/channelmonitor.rs index 15db1426735..0b0838781d1 100644 --- a/src/ln/channelmonitor.rs +++ b/src/ln/channelmonitor.rs @@ -1209,9 +1209,11 @@ impl ChannelMonitor { value: htlc.amount_msat / 1000, }), }; - single_htlc_tx.output[0].value -= fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) * (single_htlc_tx.get_weight() + Self::get_witnesses_weight(&[if htlc.offered { InputDescriptors::RevokedOfferedHTLC } else { InputDescriptors::RevokedReceivedHTLC }])) / 1000; + let predicted_weight = single_htlc_tx.get_weight() + Self::get_witnesses_weight(&[if htlc.offered { InputDescriptors::RevokedOfferedHTLC } else { InputDescriptors::RevokedReceivedHTLC }]); + single_htlc_tx.output[0].value -= fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) * predicted_weight / 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); + assert!(predicted_weight >= single_htlc_tx.get_weight()); txn_to_broadcast.push(single_htlc_tx); } } @@ -1261,7 +1263,8 @@ impl ChannelMonitor { input: inputs, output: outputs, }; - spend_tx.output[0].value -= fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) * (spend_tx.get_weight() + Self::get_witnesses_weight(&input_descriptors[..])) / 1000; + let predicted_weight = spend_tx.get_weight() + Self::get_witnesses_weight(&input_descriptors[..]); + spend_tx.output[0].value -= fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) * predicted_weight / 1000; let mut values_drain = values.drain(..); let sighash_parts = bip143::SighashComponents::new(&spend_tx); @@ -1270,6 +1273,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 }, @@ -1428,9 +1432,11 @@ impl ChannelMonitor { value: htlc.amount_msat / 1000, }), }; - single_htlc_tx.output[0].value -= fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) * (single_htlc_tx.get_weight() + Self::get_witnesses_weight(&[if htlc.offered { InputDescriptors::OfferedHTLC } else { InputDescriptors::ReceivedHTLC }])) / 1000; + let predicted_weight = single_htlc_tx.get_weight() + Self::get_witnesses_weight(&[if htlc.offered { InputDescriptors::OfferedHTLC } else { InputDescriptors::ReceivedHTLC }]); + single_htlc_tx.output[0].value -= fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) * predicted_weight / 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()); + 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(), @@ -1478,7 +1484,8 @@ impl ChannelMonitor { input: inputs, output: outputs, }; - spend_tx.output[0].value -= fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) * (spend_tx.get_weight() + Self::get_witnesses_weight(&input_descriptors[..])) / 1000; + let predicted_weight = spend_tx.get_weight() + Self::get_witnesses_weight(&input_descriptors[..]); + spend_tx.output[0].value -= fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) * predicted_weight / 1000; let mut values_drain = values.drain(..); let sighash_parts = bip143::SighashComponents::new(&spend_tx); @@ -1488,6 +1495,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(), @@ -1562,7 +1570,8 @@ impl ChannelMonitor { input: inputs, output: outputs, }; - spend_tx.output[0].value -= fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) * (spend_tx.get_weight() + Self::get_witnesses_weight(&vec![InputDescriptors::RevokedOutput])) / 1000; + let predicted_weight = spend_tx.get_weight() + Self::get_witnesses_weight(&[InputDescriptors::RevokedOutput]); + spend_tx.output[0].value -= fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) * predicted_weight / 1000; let sighash_parts = bip143::SighashComponents::new(&spend_tx); @@ -1581,6 +1590,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 })) @@ -2831,7 +2841,7 @@ mod tests { 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)); + 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(); @@ -2853,7 +2863,7 @@ mod tests { 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)); + 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(); @@ -2873,7 +2883,7 @@ mod tests { 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)); + 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.