From: Matt Corallo <649246+TheBlueMatt@users.noreply.github.com> Date: Fri, 20 Mar 2020 23:58:51 +0000 (+0000) Subject: Merge pull request #554 from TheBlueMatt/2020-03-stale-mon-fail-man-deser X-Git-Tag: v0.0.12~96 X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;h=ed0d5d1f6d6534fd434df404593750aa19f6b9fd;hp=4aa95af27219b73badae4d237f8921b874c5025c;p=rust-lightning Merge pull request #554 from TheBlueMatt/2020-03-stale-mon-fail-man-deser Fail to deserialize ChannelManager if it is ahead of any monitor(s) --- diff --git a/lightning/src/chain/keysinterface.rs b/lightning/src/chain/keysinterface.rs index 43a04452..d023c31d 100644 --- a/lightning/src/chain/keysinterface.rs +++ b/lightning/src/chain/keysinterface.rs @@ -37,6 +37,7 @@ use ln::msgs::DecodeError; /// spend on-chain. The information needed to do this is provided in this enum, including the /// outpoint describing which txid and output index is available, the full output which exists at /// that txid/index, and any keys or other information required to sign. +#[derive(Clone, PartialEq)] pub enum SpendableOutputDescriptor { /// An output to a script which was provided via KeysInterface, thus you should already know /// how to spend it. No keys are provided as rust-lightning was never given any keys - only the diff --git a/lightning/src/ln/channelmonitor.rs b/lightning/src/ln/channelmonitor.rs index 8c26139c..9b25da5b 100644 --- a/lightning/src/ln/channelmonitor.rs +++ b/lightning/src/ln/channelmonitor.rs @@ -406,7 +406,6 @@ enum Storage { htlc_base_key: SecretKey, delayed_payment_base_key: SecretKey, payment_base_key: SecretKey, - shutdown_pubkey: PublicKey, funding_info: Option<(OutPoint, Script)>, current_remote_commitment_txid: Option, prev_remote_commitment_txid: Option, @@ -595,6 +594,9 @@ enum OnchainEvent { HTLCUpdate { htlc_update: (HTLCSource, PaymentHash), }, + MaturingOutput { + descriptor: SpendableOutputDescriptor, + }, } const SERIALIZATION_VERSION: u8 = 1; @@ -759,6 +761,11 @@ pub struct ChannelMonitor { latest_update_id: u64, commitment_transaction_number_obscure_factor: u64, + destination_script: Script, + broadcasted_local_revokable_script: Option<(Script, SecretKey, Script)>, + broadcasted_remote_payment_script: Option<(Script, SecretKey)>, + shutdown_script: Script, + key_storage: Storage, their_htlc_base_key: Option, their_delayed_payment_base_key: Option, @@ -800,11 +807,6 @@ pub struct ChannelMonitor { pending_htlcs_updated: Vec, pending_events: Vec, - // Thanks to data loss protection, we may be able to claim our non-htlc funds - // back, this is the script we have to spend from but we need to - // scan every commitment transaction for that - to_remote_rescue: Option<(Script, SecretKey)>, - // 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. @@ -838,6 +840,9 @@ impl PartialEq for ChannelMonitor { fn eq(&self, other: &Self) -> bool { if self.latest_update_id != other.latest_update_id || self.commitment_transaction_number_obscure_factor != other.commitment_transaction_number_obscure_factor || + self.destination_script != other.destination_script || + self.broadcasted_local_revokable_script != other.broadcasted_local_revokable_script || + self.broadcasted_remote_payment_script != other.broadcasted_remote_payment_script || self.key_storage != other.key_storage || self.their_htlc_base_key != other.their_htlc_base_key || self.their_delayed_payment_base_key != other.their_delayed_payment_base_key || @@ -856,7 +861,6 @@ impl PartialEq for ChannelMonitor { self.payment_preimages != other.payment_preimages || self.pending_htlcs_updated != other.pending_htlcs_updated || self.pending_events.len() != other.pending_events.len() || // We trust events to round-trip properly - self.to_remote_rescue != other.to_remote_rescue || self.onchain_events_waiting_threshold_conf != other.onchain_events_waiting_threshold_conf || self.outputs_to_watch != other.outputs_to_watch { @@ -880,8 +884,27 @@ impl ChannelMonitor { // Set in initial Channel-object creation, so should always be set by now: U48(self.commitment_transaction_number_obscure_factor).write(writer)?; + self.destination_script.write(writer)?; + if let Some(ref broadcasted_local_revokable_script) = self.broadcasted_local_revokable_script { + writer.write_all(&[0; 1])?; + broadcasted_local_revokable_script.0.write(writer)?; + broadcasted_local_revokable_script.1.write(writer)?; + broadcasted_local_revokable_script.2.write(writer)?; + } else { + writer.write_all(&[1; 1])?; + } + + if let Some(ref broadcasted_remote_payment_script) = self.broadcasted_remote_payment_script { + writer.write_all(&[0; 1])?; + broadcasted_remote_payment_script.0.write(writer)?; + broadcasted_remote_payment_script.1.write(writer)?; + } else { + writer.write_all(&[1; 1])?; + } + self.shutdown_script.write(writer)?; + match self.key_storage { - Storage::Local { ref keys, ref funding_key, ref revocation_base_key, ref htlc_base_key, ref delayed_payment_base_key, ref payment_base_key, ref shutdown_pubkey, ref funding_info, ref current_remote_commitment_txid, ref prev_remote_commitment_txid } => { + Storage::Local { ref keys, ref funding_key, ref revocation_base_key, ref htlc_base_key, ref delayed_payment_base_key, ref payment_base_key, ref funding_info, ref current_remote_commitment_txid, ref prev_remote_commitment_txid } => { writer.write_all(&[0; 1])?; keys.write(writer)?; writer.write_all(&funding_key[..])?; @@ -889,7 +912,6 @@ impl ChannelMonitor { writer.write_all(&htlc_base_key[..])?; writer.write_all(&delayed_payment_base_key[..])?; writer.write_all(&payment_base_key[..])?; - writer.write_all(&shutdown_pubkey.serialize())?; match funding_info { &Some((ref outpoint, ref script)) => { writer.write_all(&outpoint.txid[..])?; @@ -1034,13 +1056,6 @@ impl ChannelMonitor { } self.last_block_hash.write(writer)?; - if let Some((ref to_remote_script, ref local_key)) = self.to_remote_rescue { - writer.write_all(&[1; 1])?; - to_remote_script.write(writer)?; - local_key.write(writer)?; - } else { - writer.write_all(&[0; 1])?; - } 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() { @@ -1053,6 +1068,10 @@ impl ChannelMonitor { htlc_update.0.write(writer)?; htlc_update.1.write(writer)?; }, + OnchainEvent::MaturingOutput { ref descriptor } => { + 1u8.write(writer)?; + descriptor.write(writer)?; + }, } } } @@ -1107,10 +1126,17 @@ impl ChannelMonitor { let htlc_base_key = keys.htlc_base_key().clone(); let delayed_payment_base_key = keys.delayed_payment_base_key().clone(); let payment_base_key = keys.payment_base_key().clone(); + 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(); ChannelMonitor { latest_update_id: 0, commitment_transaction_number_obscure_factor, + destination_script: destination_script.clone(), + broadcasted_local_revokable_script: None, + broadcasted_remote_payment_script: None, + shutdown_script, + key_storage: Storage::Local { keys, funding_key, @@ -1118,7 +1144,6 @@ impl ChannelMonitor { htlc_base_key, delayed_payment_base_key, payment_base_key, - shutdown_pubkey: shutdown_pubkey.clone(), funding_info: Some(funding_info), current_remote_commitment_txid: None, prev_remote_commitment_txid: None, @@ -1145,8 +1170,6 @@ impl ChannelMonitor { pending_htlcs_updated: Vec::new(), pending_events: Vec::new(), - to_remote_rescue: None, - onchain_events_waiting_threshold_conf: HashMap::new(), outputs_to_watch: HashMap::new(), @@ -1262,7 +1285,7 @@ impl ChannelMonitor { .push_slice(&Hash160::hash(&payment_key.serialize())[..]) .into_script(); if let Ok(to_remote_key) = chan_utils::derive_private_key(&self.secp_ctx, &their_revocation_point, &payment_base_key) { - self.to_remote_rescue = Some((to_remote_script, to_remote_key)); + self.broadcasted_remote_payment_script = Some((to_remote_script, to_remote_key)); } } }, @@ -1450,12 +1473,11 @@ 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) { + fn check_spend_remote_transaction(&mut self, tx: &Transaction, height: u32) -> (Vec, (Sha256dHash, 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 claimable_outpoints = Vec::new(); let mut watch_outputs = Vec::new(); - let mut spendable_outputs = 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); @@ -1464,7 +1486,7 @@ impl ChannelMonitor { ( $thing : expr ) => { match $thing { Ok(a) => a, - Err(_) => return (claimable_outpoints, (commitment_txid, watch_outputs), spendable_outputs) + Err(_) => return (claimable_outpoints, (commitment_txid, watch_outputs)) } }; } @@ -1479,7 +1501,7 @@ impl ChannelMonitor { (ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, &per_commitment_point, &keys.pubkeys().revocation_basepoint)), ignore_error!(chan_utils::derive_private_revocation_key(&self.secp_ctx, &per_commitment_key, &revocation_base_key)), ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &per_commitment_point, &keys.pubkeys().htlc_basepoint)), - Some(ignore_error!(chan_utils::derive_private_key(&self.secp_ctx, &per_commitment_point, &payment_base_key)))) + ignore_error!(chan_utils::derive_private_key(&self.secp_ctx, &per_commitment_point, &payment_base_key))) }, Storage::Watchtower { .. } => { unimplemented!() @@ -1487,31 +1509,25 @@ 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 (claimable_outpoints, (commitment_txid, watch_outputs), spendable_outputs), + None => return (claimable_outpoints, (commitment_txid, watch_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)), }; let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.our_to_self_delay, &delayed_key); let revokeable_p2wsh = revokeable_redeemscript.to_v0_p2wsh(); - let local_payment_p2wpkh = if let Some(payment_key) = local_payment_key { + self.broadcasted_remote_payment_script = { // 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()) - } else { None }; + // script_pubkey version + let payment_hash160 = Hash160::hash(&PublicKey::from_secret_key(&self.secp_ctx, &local_payment_key).serialize()); + Some((Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&payment_hash160[..]).into_script(), local_payment_key)) + }; // First, process non-htlc outputs (to_local & to_remote) for (idx, outp) in tx.output.iter().enumerate() { if outp.script_pubkey == revokeable_p2wsh { let witness_data = InputMaterial::Revoked { witness_script: revokeable_redeemscript.clone(), pubkey: Some(revocation_pubkey), key: revocation_key, is_htlc: false, amount: outp.value }; claimable_outpoints.push(ClaimRequest { absolute_timelock: height + self.our_to_self_delay as u32, aggregable: true, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: idx as u32 }, witness_data}); - } 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 }, - key: local_payment_key.unwrap(), - output: outp.clone(), - }); } } @@ -1523,7 +1539,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 (claimable_outpoints, (commitment_txid, watch_outputs), spendable_outputs); // Corrupted per_commitment_data, fuck this user + return (claimable_outpoints, (commitment_txid, watch_outputs)); // Corrupted per_commitment_data, fuck this user } let witness_data = InputMaterial::Revoked { witness_script: expected_script, pubkey: Some(revocation_pubkey), key: revocation_key, is_htlc: true, amount: tx.output[transaction_output_index as usize].value }; claimable_outpoints.push(ClaimRequest { absolute_timelock: htlc.cltv_expiry, aggregable: true, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: transaction_output_index }, witness_data }); @@ -1552,6 +1568,7 @@ impl ChannelMonitor { OnchainEvent::HTLCUpdate { ref htlc_update } => { return htlc_update.0 != **source }, + _ => true } }); e.push(OnchainEvent::HTLCUpdate { htlc_update: ((**source).clone(), htlc.payment_hash.clone())}); @@ -1616,6 +1633,7 @@ impl ChannelMonitor { OnchainEvent::HTLCUpdate { ref htlc_update } => { return htlc_update.0 != **source }, + _ => true } }); e.push(OnchainEvent::HTLCUpdate { htlc_update: ((**source).clone(), htlc.payment_hash.clone())}); @@ -1645,37 +1663,26 @@ impl ChannelMonitor { if revocation_points.0 == commitment_number + 1 { Some(point) } else { None } } else { None }; if let Some(revocation_point) = revocation_point_option { - let (revocation_pubkey, b_htlc_key, htlc_privkey) = match self.key_storage { - Storage::Local { ref keys, ref htlc_base_key, .. } => { + let (revocation_pubkey, b_htlc_key, htlc_privkey, local_payment_key) = match self.key_storage { + Storage::Local { ref keys, ref htlc_base_key, ref payment_base_key, .. } => { (ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, revocation_point, &keys.pubkeys().revocation_basepoint)), ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, revocation_point, &keys.pubkeys().htlc_basepoint)), - ignore_error!(chan_utils::derive_private_key(&self.secp_ctx, revocation_point, &htlc_base_key))) + ignore_error!(chan_utils::derive_private_key(&self.secp_ctx, revocation_point, &htlc_base_key)), + ignore_error!(chan_utils::derive_private_key(&self.secp_ctx, revocation_point, &payment_base_key))) }, Storage::Watchtower { .. } => { unimplemented!() } }; let a_htlc_key = match self.their_htlc_base_key { - None => return (claimable_outpoints, (commitment_txid, watch_outputs), spendable_outputs), + None => return (claimable_outpoints, (commitment_txid, watch_outputs)), Some(their_htlc_base_key) => ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, revocation_point, &their_htlc_base_key)), }; - // First, mark as spendable our to_remote output - for (idx, outp) in tx.output.iter().enumerate() { - if outp.script_pubkey.is_v0_p2wpkh() { - match self.key_storage { - Storage::Local { ref payment_base_key, .. } => { - if let Ok(local_key) = chan_utils::derive_private_key(&self.secp_ctx, &revocation_point, &payment_base_key) { - spendable_outputs.push(SpendableOutputDescriptor::DynamicOutputP2WPKH { - outpoint: BitcoinOutPoint { txid: commitment_txid, vout: idx as u32 }, - key: local_key, - output: outp.clone(), - }); - } - }, - Storage::Watchtower { .. } => {} - } - break; // Only to_remote ouput is claimable - } - } + self.broadcasted_remote_payment_script = { + // 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, &local_payment_key).serialize()); + Some((Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&payment_hash160[..]).into_script(), local_payment_key)) + }; // Then, try to find htlc outputs for (_, &(ref htlc, _)) in per_commitment_data.iter().enumerate() { @@ -1684,7 +1691,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 (claimable_outpoints, (commitment_txid, watch_outputs), spendable_outputs); // Corrupted per_commitment_data, fuck this user + return (claimable_outpoints, (commitment_txid, watch_outputs)); // Corrupted per_commitment_data, fuck this user } let preimage = if htlc.offered { if let Some(p) = self.payment_preimages.get(&htlc.payment_hash) { Some(*p) } else { None } } else { None }; let aggregable = if !htlc.offered { false } else { true }; @@ -1696,18 +1703,8 @@ impl ChannelMonitor { } } } - } else if let Some((ref to_remote_rescue, ref local_key)) = self.to_remote_rescue { - for (idx, outp) in tx.output.iter().enumerate() { - if to_remote_rescue == &outp.script_pubkey { - spendable_outputs.push(SpendableOutputDescriptor::DynamicOutputP2WPKH { - outpoint: BitcoinOutPoint { txid: commitment_txid, vout: idx as u32 }, - key: local_key.clone(), - output: outp.clone(), - }); - } - } } - (claimable_outpoints, (commitment_txid, watch_outputs), spendable_outputs) + (claimable_outpoints, (commitment_txid, watch_outputs)) } /// Attempts to claim a remote HTLC-Success/HTLC-Timeout's outputs using the revocation key @@ -1748,33 +1745,14 @@ impl ChannelMonitor { (claimable_outpoints, Some((htlc_txid, tx.output.clone()))) } - fn broadcast_by_local_state(&self, local_tx: &LocalSignedTx, delayed_payment_base_key: &SecretKey) -> (Vec, Vec, Vec) { + fn broadcast_by_local_state(&self, local_tx: &LocalSignedTx, delayed_payment_base_key: &SecretKey) -> (Vec, Vec, Option<(Script, SecretKey, Script)>) { let mut res = Vec::with_capacity(local_tx.htlc_outputs.len()); - let mut spendable_outputs = Vec::with_capacity(local_tx.htlc_outputs.len()); let mut watch_outputs = Vec::with_capacity(local_tx.htlc_outputs.len()); - macro_rules! add_dynamic_output { - ($father_tx: expr, $vout: expr) => { - if let Ok(local_delayedkey) = chan_utils::derive_private_key(&self.secp_ctx, &local_tx.per_commitment_point, delayed_payment_base_key) { - spendable_outputs.push(SpendableOutputDescriptor::DynamicOutputP2WSH { - outpoint: BitcoinOutPoint { txid: $father_tx.txid(), vout: $vout }, - key: local_delayedkey, - witness_script: chan_utils::get_revokeable_redeemscript(&local_tx.revocation_key, self.our_to_self_delay, &local_tx.delayed_payment_key), - to_self_delay: self.our_to_self_delay, - output: $father_tx.output[$vout as usize].clone(), - }); - } - } - } - let redeemscript = chan_utils::get_revokeable_redeemscript(&local_tx.revocation_key, self.their_to_self_delay.unwrap(), &local_tx.delayed_payment_key); - let revokeable_p2wsh = redeemscript.to_v0_p2wsh(); - for (idx, output) in local_tx.tx.without_valid_witness().output.iter().enumerate() { - if output.script_pubkey == revokeable_p2wsh { - add_dynamic_output!(local_tx.tx.without_valid_witness(), idx as u32); - break; - } - } + let broadcasted_local_revokable_script = if let Ok(local_delayedkey) = chan_utils::derive_private_key(&self.secp_ctx, &local_tx.per_commitment_point, delayed_payment_base_key) { + Some((redeemscript.to_v0_p2wsh(), local_delayedkey, redeemscript)) + } else { None }; if let &Storage::Local { ref htlc_base_key, .. } = &self.key_storage { for &(ref htlc, ref sigs, _) in local_tx.htlc_outputs.iter() { @@ -1789,7 +1767,6 @@ impl ChannelMonitor { Err(_) => continue, }; - add_dynamic_output!(htlc_timeout_tx, 0); let mut per_input_material = HashMap::with_capacity(1); per_input_material.insert(htlc_timeout_tx.input[0].previous_output, InputMaterial::LocalHTLC { witness_script: htlc_script, sigs: (*their_sig, our_sig), preimage: None, amount: htlc.amount_msat / 1000}); //TODO: with option_simplified_commitment track outpoint too @@ -1805,7 +1782,6 @@ impl ChannelMonitor { Err(_) => continue, }; - add_dynamic_output!(htlc_success_tx, 0); let mut per_input_material = HashMap::with_capacity(1); per_input_material.insert(htlc_success_tx.input[0].previous_output, InputMaterial::LocalHTLC { witness_script: htlc_script, sigs: (*their_sig, our_sig), preimage: Some(*payment_preimage), amount: htlc.amount_msat / 1000}); //TODO: with option_simplified_commitment track outpoint too @@ -1819,16 +1795,15 @@ impl ChannelMonitor { } } - (res, spendable_outputs, watch_outputs) + (res, watch_outputs, broadcasted_local_revokable_script) } /// 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(&mut self, tx: &Transaction, height: u32) -> (Vec, Vec, (Sha256dHash, Vec)) { + fn check_spend_local_transaction(&mut self, tx: &Transaction, height: u32) -> (Vec, (Sha256dHash, Vec)) { let commitment_txid = tx.txid(); let mut local_txn = Vec::new(); - let mut spendable_outputs = Vec::new(); let mut watch_outputs = Vec::new(); macro_rules! wait_threshold_conf { @@ -1842,6 +1817,7 @@ impl ChannelMonitor { OnchainEvent::HTLCUpdate { ref htlc_update } => { return htlc_update.0 != $source }, + _ => true } }); e.push(OnchainEvent::HTLCUpdate { htlc_update: ($source, $payment_hash)}); @@ -1856,8 +1832,8 @@ impl ChannelMonitor { 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); + watch_outputs.append(&mut $updates.1); + self.broadcasted_local_revokable_script = $updates.2; } } @@ -1934,32 +1910,7 @@ impl ChannelMonitor { } } - (local_txn, spendable_outputs, (commitment_txid, watch_outputs)) - } - - /// Generate a spendable output event when closing_transaction get registered onchain. - fn check_spend_closing_transaction(&self, tx: &Transaction) -> Option { - if tx.input[0].sequence == 0xFFFFFFFF && !tx.input[0].witness.is_empty() && tx.input[0].witness.last().unwrap().len() == 71 { - 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(); - for (idx, output) in tx.output.iter().enumerate() { - if shutdown_script == output.script_pubkey { - return Some(SpendableOutputDescriptor::StaticOutput { - outpoint: BitcoinOutPoint { txid: tx.txid(), vout: idx as u32 }, - output: output.clone(), - }); - } - } - } - Storage::Watchtower { .. } => { - //TODO: we need to ensure an offline client will generate the event when it - // comes back online after only the watchtower saw the transaction - } - } - } - None + (local_txn, (commitment_txid, watch_outputs)) } /// Used by ChannelManager deserialization to broadcast the latest local state if its copy of @@ -2020,7 +1971,6 @@ impl ChannelMonitor { log_trace!(self, "Block {} at height {} connected with {} txn matched", block_hash, height, txn_matched.len()); let mut watch_outputs = Vec::new(); - let mut spendable_outputs = Vec::new(); let mut claimable_outpoints = Vec::new(); for tx in txn_matched { if tx.input.len() == 1 { @@ -2039,14 +1989,12 @@ 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) { if (tx.input[0].sequence >> 8*3) as u8 == 0x80 && (tx.lock_time >> 8*3) as u8 == 0x20 { - let (mut new_outpoints, new_outputs, mut spendable_output) = self.check_spend_remote_transaction(&tx, height); - spendable_outputs.append(&mut spendable_output); + let (mut new_outpoints, new_outputs) = self.check_spend_remote_transaction(&tx, height); if !new_outputs.1.is_empty() { watch_outputs.push(new_outputs); } if new_outpoints.is_empty() { - let (local_txn, mut spendable_output, new_outputs) = self.check_spend_local_transaction(&tx, height); - spendable_outputs.append(&mut spendable_output); + let (local_txn, new_outputs) = self.check_spend_local_transaction(&tx, height); for tx in local_txn.iter() { log_trace!(self, "Broadcast onchain {}", log_tx!(tx)); broadcaster.broadcast_transaction(tx); @@ -2057,11 +2005,6 @@ impl ChannelMonitor { } claimable_outpoints.append(&mut new_outpoints); } - if !funding_txo.is_none() && claimable_outpoints.is_empty() { - if let Some(spendable_output) = self.check_spend_closing_transaction(&tx) { - spendable_outputs.push(spendable_output); - } - } } else { if let Some(&(commitment_number, _)) = self.remote_commitment_txn_on_chain.get(&prevout.txid) { let (mut new_outpoints, new_outputs_option) = self.check_spend_remote_htlc(&tx, commitment_number, height); @@ -2076,6 +2019,8 @@ impl ChannelMonitor { // 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. self.is_resolving_htlc_output(&tx, height); + + self.is_paying_spendable_output(&tx, height); } let should_broadcast = if let Some(_) = self.current_local_signed_commitment_tx { self.would_broadcast_at_height(height) @@ -2096,8 +2041,7 @@ impl ChannelMonitor { broadcaster.broadcast_transaction(&cur_local_tx.tx.with_valid_witness()); match self.key_storage { Storage::Local { ref delayed_payment_base_key, .. } => { - let (txs, mut spendable_output, new_outputs) = self.broadcast_by_local_state(&cur_local_tx, delayed_payment_base_key); - spendable_outputs.append(&mut spendable_output); + let (txs, new_outputs, _) = self.broadcast_by_local_state(&cur_local_tx, delayed_payment_base_key); if !new_outputs.is_empty() { watch_outputs.push((cur_local_tx.txid.clone(), new_outputs)); } @@ -2121,23 +2065,22 @@ impl ChannelMonitor { source: htlc_update.0, }); }, + OnchainEvent::MaturingOutput { descriptor } => { + log_trace!(self, "Descriptor {} has got enough confirmations to be passed upstream", log_spendable!(descriptor)); + self.pending_events.push(events::Event::SpendableOutputs { + outputs: vec![descriptor] + }); + } } } } - let mut spendable_output = self.onchain_tx_handler.block_connected(txn_matched, claimable_outpoints, height, &*broadcaster, &*fee_estimator); - spendable_outputs.append(&mut spendable_output); + self.onchain_tx_handler.block_connected(txn_matched, claimable_outpoints, height, &*broadcaster, &*fee_estimator); self.last_block_hash = block_hash.clone(); for &(ref txid, ref output_scripts) in watch_outputs.iter() { self.outputs_to_watch.insert(txid.clone(), output_scripts.iter().map(|o| o.script_pubkey.clone()).collect()); } - if spendable_outputs.len() > 0 { - self.pending_events.push(events::Event::SpendableOutputs { - outputs: spendable_outputs, - }); - } - watch_outputs } @@ -2149,6 +2092,7 @@ impl ChannelMonitor { if let Some(_) = self.onchain_events_waiting_threshold_conf.remove(&(height + ANTI_REORG_DELAY - 1)) { //We may discard: //- htlc update there as failure-trigger tx (revoked commitment tx, non-revoked commitment tx, HTLC-timeout tx) has been disconnected + //- maturing spendable output has transaction paying us has been disconnected } self.onchain_tx_handler.block_disconnected(height, broadcaster, fee_estimator); @@ -2351,6 +2295,7 @@ impl ChannelMonitor { OnchainEvent::HTLCUpdate { ref htlc_update } => { return htlc_update.0 != source }, + _ => true } }); e.push(OnchainEvent::HTLCUpdate { htlc_update: (source, payment_hash)}); @@ -2363,6 +2308,57 @@ impl ChannelMonitor { } } } + + /// Check if any transaction broadcasted is paying fund back to some address we can assume to own + fn is_paying_spendable_output(&mut self, tx: &Transaction, height: u32) { + let mut spendable_output = None; + for (i, outp) in tx.output.iter().enumerate() { // There is max one spendable output for any channel tx, including ones generated by us + if outp.script_pubkey == self.destination_script { + spendable_output = Some(SpendableOutputDescriptor::StaticOutput { + outpoint: BitcoinOutPoint { txid: tx.txid(), vout: i as u32 }, + output: outp.clone(), + }); + break; + } else if let Some(ref broadcasted_local_revokable_script) = self.broadcasted_local_revokable_script { + if broadcasted_local_revokable_script.0 == outp.script_pubkey { + spendable_output = Some(SpendableOutputDescriptor::DynamicOutputP2WSH { + outpoint: BitcoinOutPoint { txid: tx.txid(), vout: i as u32 }, + key: broadcasted_local_revokable_script.1, + witness_script: broadcasted_local_revokable_script.2.clone(), + to_self_delay: self.their_to_self_delay.unwrap(), + output: outp.clone(), + }); + break; + } + } else if let Some(ref broadcasted_remote_payment_script) = self.broadcasted_remote_payment_script { + if broadcasted_remote_payment_script.0 == outp.script_pubkey { + spendable_output = Some(SpendableOutputDescriptor::DynamicOutputP2WPKH { + outpoint: BitcoinOutPoint { txid: tx.txid(), vout: i as u32 }, + key: broadcasted_remote_payment_script.1, + output: outp.clone(), + }); + break; + } + } else if outp.script_pubkey == self.shutdown_script { + spendable_output = Some(SpendableOutputDescriptor::StaticOutput { + outpoint: BitcoinOutPoint { txid: tx.txid(), vout: i as u32 }, + output: outp.clone(), + }); + } + } + if let Some(spendable_output) = spendable_output { + log_trace!(self, "Maturing {} until {}", log_spendable!(spendable_output), height + ANTI_REORG_DELAY - 1); + match self.onchain_events_waiting_threshold_conf.entry(height + ANTI_REORG_DELAY - 1) { + hash_map::Entry::Occupied(mut entry) => { + let e = entry.get_mut(); + e.push(OnchainEvent::MaturingOutput { descriptor: spendable_output }); + } + hash_map::Entry::Vacant(entry) => { + entry.insert(vec![OnchainEvent::MaturingOutput { descriptor: spendable_output }]); + } + } + } + } } const MAX_ALLOC_SIZE: usize = 64*1024; @@ -2387,6 +2383,28 @@ impl ReadableArgs> for (Sha256dH let latest_update_id: u64 = Readable::read(reader)?; let commitment_transaction_number_obscure_factor = ::read(reader)?.0; + let destination_script = Readable::read(reader)?; + let broadcasted_local_revokable_script = match ::read(reader)? { + 0 => { + let revokable_address = Readable::read(reader)?; + let local_delayedkey = Readable::read(reader)?; + let revokable_script = Readable::read(reader)?; + Some((revokable_address, local_delayedkey, revokable_script)) + }, + 1 => { None }, + _ => return Err(DecodeError::InvalidValue), + }; + let broadcasted_remote_payment_script = match ::read(reader)? { + 0 => { + let payment_address = Readable::read(reader)?; + let payment_key = Readable::read(reader)?; + Some((payment_address, payment_key)) + }, + 1 => { None }, + _ => return Err(DecodeError::InvalidValue), + }; + let shutdown_script = Readable::read(reader)?; + let key_storage = match ::read(reader)? { 0 => { let keys = Readable::read(reader)?; @@ -2395,7 +2413,6 @@ impl ReadableArgs> for (Sha256dH let htlc_base_key = Readable::read(reader)?; let delayed_payment_base_key = Readable::read(reader)?; let payment_base_key = Readable::read(reader)?; - let shutdown_pubkey = 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 { @@ -2412,7 +2429,6 @@ impl ReadableArgs> for (Sha256dH htlc_base_key, delayed_payment_base_key, payment_base_key, - shutdown_pubkey, funding_info, current_remote_commitment_txid, prev_remote_commitment_txid, @@ -2576,15 +2592,6 @@ impl ReadableArgs> for (Sha256dH } let last_block_hash: Sha256dHash = Readable::read(reader)?; - let to_remote_rescue = match ::read(reader)? { - 0 => None, - 1 => { - let to_remote_script = Readable::read(reader)?; - let local_key = Readable::read(reader)?; - Some((to_remote_script, local_key)) - } - _ => return Err(DecodeError::InvalidValue), - }; 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)); @@ -2601,6 +2608,12 @@ impl ReadableArgs> for (Sha256dH htlc_update: (htlc_source, hash) } }, + 1 => { + let descriptor = Readable::read(reader)?; + OnchainEvent::MaturingOutput { + descriptor + } + }, _ => return Err(DecodeError::InvalidValue), }; events.push(ev); @@ -2627,6 +2640,11 @@ impl ReadableArgs> for (Sha256dH latest_update_id, commitment_transaction_number_obscure_factor, + destination_script, + broadcasted_local_revokable_script, + broadcasted_remote_payment_script, + shutdown_script, + key_storage, their_htlc_base_key, their_delayed_payment_base_key, @@ -2650,8 +2668,6 @@ impl ReadableArgs> for (Sha256dH pending_htlcs_updated, pending_events, - to_remote_rescue, - onchain_events_waiting_threshold_conf, outputs_to_watch, diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 7cd546db..8ea9dcf7 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -4081,6 +4081,8 @@ fn test_claim_sizeable_push_msat() { let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![node_txn[0].clone()] }, 0); + connect_blocks(&nodes[1].block_notifier, ANTI_REORG_DELAY - 1, 1, true, header.bitcoin_hash()); + let spend_txn = check_spendable_outputs!(nodes[1], 1); assert_eq!(spend_txn.len(), 1); check_spends!(spend_txn[0], node_txn[0]); @@ -4109,6 +4111,8 @@ fn test_claim_on_remote_sizeable_push_msat() { nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![node_txn[0].clone()] }, 0); check_closed_broadcast!(nodes[1], false); check_added_monitors!(nodes[1], 1); + connect_blocks(&nodes[1].block_notifier, ANTI_REORG_DELAY - 1, 1, true, header.bitcoin_hash()); + let spend_txn = check_spendable_outputs!(nodes[1], 1); assert_eq!(spend_txn.len(), 2); assert_eq!(spend_txn[0], spend_txn[1]); @@ -4132,17 +4136,21 @@ fn test_claim_on_remote_revoked_sizeable_push_msat() { assert_eq!(revoked_local_txn[0].input[0].previous_output.txid, chan.3.txid()); claim_payment(&nodes[0], &vec!(&nodes[1])[..], payment_preimage, 3_000_000); - let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; - nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1); + let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; + nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 0); check_closed_broadcast!(nodes[1], false); check_added_monitors!(nodes[1], 1); let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap(); + let header_1 = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; + nodes[1].block_notifier.block_connected(&Block { header: header_1, txdata: vec![node_txn[0].clone()] }, 1); + connect_blocks(&nodes[1].block_notifier, ANTI_REORG_DELAY - 1, 1, true, header.bitcoin_hash()); + let spend_txn = check_spendable_outputs!(nodes[1], 1); assert_eq!(spend_txn.len(), 3); - assert_eq!(spend_txn[0], spend_txn[2]); // to_remote output on revoked remote commitment_tx + assert_eq!(spend_txn[0], spend_txn[1]); // to_remote output on revoked remote commitment_tx check_spends!(spend_txn[0], revoked_local_txn[0]); - check_spends!(spend_txn[1], node_txn[0]); + check_spends!(spend_txn[2], node_txn[0]); } #[test] @@ -4182,15 +4190,72 @@ fn test_static_spendable_outputs_preimage_tx() { assert_eq!(node_txn.len(), 3); check_spends!(node_txn[0], commitment_tx[0]); assert_eq!(node_txn[0].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT); -eprintln!("{:?}", node_txn[1]); check_spends!(node_txn[1], chan_1.3); check_spends!(node_txn[2], node_txn[1]); - let spend_txn = check_spendable_outputs!(nodes[1], 1); // , 0, 0, 1, 1); + let header_1 = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; + nodes[1].block_notifier.block_connected(&Block { header: header_1, txdata: vec![node_txn[0].clone()] }, 1); + connect_blocks(&nodes[1].block_notifier, ANTI_REORG_DELAY - 1, 1, true, header.bitcoin_hash()); + + let spend_txn = check_spendable_outputs!(nodes[1], 1); assert_eq!(spend_txn.len(), 1); check_spends!(spend_txn[0], node_txn[0]); } +#[test] +fn test_static_spendable_outputs_timeout_tx() { + let chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); + + // Create some initial channels + let chan_1 = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::supported(), InitFeatures::supported()); + + // Rebalance the network a bit by relaying one payment through all the channels ... + send_payment(&nodes[0], &vec!(&nodes[1])[..], 8000000, 8_000_000); + + let (_, our_payment_hash) = route_payment(&nodes[1], &vec!(&nodes[0])[..], 3_000_000); + + let commitment_tx = get_local_commitment_txn!(nodes[0], chan_1.2); + assert_eq!(commitment_tx[0].input.len(), 1); + assert_eq!(commitment_tx[0].input[0].previous_output.txid, chan_1.3.txid()); + + // Settle A's commitment tx on B' chain + let header = BlockHeader { version: 0x2000_0000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42}; + nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![commitment_tx[0].clone()] }, 0); + check_added_monitors!(nodes[1], 1); + let events = nodes[1].node.get_and_clear_pending_msg_events(); + match events[0] { + MessageSendEvent::BroadcastChannelUpdate { .. } => {}, + _ => panic!("Unexpected event"), + } + + // Check B's monitor was able to send back output descriptor event for timeout tx on A's commitment tx + let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap(); + assert_eq!(node_txn.len(), 3); // ChannelManager : 2 (local commitent tx + HTLC-timeout), ChannelMonitor: timeout tx + check_spends!(node_txn[0], commitment_tx[0].clone()); + assert_eq!(node_txn[0].input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT); + check_spends!(node_txn[1], chan_1.3.clone()); + check_spends!(node_txn[2], node_txn[1]); + + let header_1 = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; + nodes[1].block_notifier.block_connected(&Block { header: header_1, txdata: vec![node_txn[0].clone()] }, 1); + connect_blocks(&nodes[1].block_notifier, ANTI_REORG_DELAY - 1, 1, true, header.bitcoin_hash()); + let events = nodes[1].node.get_and_clear_pending_events(); + assert_eq!(events.len(), 1); + match events[0] { + Event::PaymentFailed { payment_hash, .. } => { + assert_eq!(payment_hash, our_payment_hash); + }, + _ => panic!("Unexpected event"), + } + + let spend_txn = check_spendable_outputs!(nodes[1], 1); + assert_eq!(spend_txn.len(), 3); // SpendableOutput: remote_commitment_tx.to_remote (*2), timeout_tx.output (*1) + check_spends!(spend_txn[2], node_txn[0].clone()); +} + #[test] fn test_static_spendable_outputs_justice_tx_revoked_commitment_tx() { let chanmon_cfgs = create_chanmon_cfgs(2); @@ -4209,7 +4274,7 @@ fn test_static_spendable_outputs_justice_tx_revoked_commitment_tx() { claim_payment(&nodes[0], &vec!(&nodes[1])[..], payment_preimage, 3_000_000); let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; - nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1); + nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 0); check_closed_broadcast!(nodes[1], false); check_added_monitors!(nodes[1], 1); @@ -4218,6 +4283,10 @@ fn test_static_spendable_outputs_justice_tx_revoked_commitment_tx() { assert_eq!(node_txn[0].input.len(), 2); check_spends!(node_txn[0], revoked_local_txn[0]); + let header_1 = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; + nodes[1].block_notifier.block_connected(&Block { header: header_1, txdata: vec![node_txn[0].clone()] }, 1); + connect_blocks(&nodes[1].block_notifier, ANTI_REORG_DELAY - 1, 1, true, header.bitcoin_hash()); + let spend_txn = check_spendable_outputs!(nodes[1], 1); assert_eq!(spend_txn.len(), 1); check_spends!(spend_txn[0], node_txn[0]); @@ -4255,7 +4324,7 @@ fn test_static_spendable_outputs_justice_tx_revoked_htlc_timeout_tx() { check_spends!(revoked_htlc_txn[1], chan_1.3); // B will generate justice tx from A's revoked commitment/HTLC tx - nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![revoked_local_txn[0].clone(), revoked_htlc_txn[0].clone()] }, 1); + nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![revoked_local_txn[0].clone(), revoked_htlc_txn[0].clone()] }, 0); check_closed_broadcast!(nodes[1], false); check_added_monitors!(nodes[1], 1); @@ -4269,6 +4338,10 @@ fn test_static_spendable_outputs_justice_tx_revoked_htlc_timeout_tx() { assert_eq!(node_txn[3].input.len(), 1); check_spends!(node_txn[3], revoked_local_txn[0]); + let header_1 = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; + nodes[1].block_notifier.block_connected(&Block { header: header_1, txdata: vec![node_txn[0].clone(), node_txn[2].clone()] }, 1); + connect_blocks(&nodes[1].block_notifier, ANTI_REORG_DELAY - 1, 1, true, header.bitcoin_hash()); + // Check B's ChannelMonitor was able to generate the right spendable output descriptor let spend_txn = check_spendable_outputs!(nodes[1], 1); assert_eq!(spend_txn.len(), 2); @@ -4316,13 +4389,18 @@ fn test_static_spendable_outputs_justice_tx_revoked_htlc_success_tx() { assert_eq!(node_txn[2].input.len(), 1); check_spends!(node_txn[2], revoked_htlc_txn[0]); + let header_1 = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; + nodes[0].block_notifier.block_connected(&Block { header: header_1, txdata: vec![node_txn[0].clone(), node_txn[2].clone()] }, 1); + connect_blocks(&nodes[0].block_notifier, ANTI_REORG_DELAY - 1, 1, true, header.bitcoin_hash()); + // Check A's ChannelMonitor was able to generate the right spendable output descriptor let spend_txn = check_spendable_outputs!(nodes[0], 1); assert_eq!(spend_txn.len(), 5); // Duplicated SpendableOutput due to block rescan after revoked htlc output tracking + assert_eq!(spend_txn[0], spend_txn[1]); assert_eq!(spend_txn[0], spend_txn[2]); check_spends!(spend_txn[0], revoked_local_txn[0]); // spending to_remote output from revoked local tx - check_spends!(spend_txn[1], node_txn[0]); // spending justice tx output from revoked local tx htlc received output - check_spends!(spend_txn[3], node_txn[2]); // spending justice tx output on htlc success tx + check_spends!(spend_txn[3], node_txn[0]); // spending justice tx output from revoked local tx htlc received output + check_spends!(spend_txn[4], node_txn[2]); // spending justice tx output on htlc success tx } #[test] @@ -4580,16 +4658,23 @@ fn test_dynamic_spendable_outputs_local_htlc_success_tx() { MessageSendEvent::BroadcastChannelUpdate { .. } => {}, _ => panic!("Unexepected event"), } - let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap(); - assert_eq!(node_txn[0].input.len(), 1); - assert_eq!(node_txn[0].input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT); - check_spends!(node_txn[0], local_txn[0]); + let node_txn = { + let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap(); + assert_eq!(node_txn[0].input.len(), 1); + assert_eq!(node_txn[0].input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT); + check_spends!(node_txn[0], local_txn[0]); + vec![node_txn[0].clone(), node_txn[2].clone()] + }; + + let header_201 = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; + nodes[1].block_notifier.block_connected(&Block { header: header_201, txdata: node_txn.clone() }, 201); + connect_blocks(&nodes[1].block_notifier, ANTI_REORG_DELAY - 1, 201, true, header_201.bitcoin_hash()); // Verify that B is able to spend its own HTLC-Success tx thanks to spendable output event given back by its ChannelMonitor let spend_txn = check_spendable_outputs!(nodes[1], 1); assert_eq!(spend_txn.len(), 2); check_spends!(spend_txn[0], node_txn[0]); - check_spends!(spend_txn[1], node_txn[2]); + check_spends!(spend_txn[1], node_txn[1]); } fn do_test_fail_backwards_unrevoked_remote_announce(deliver_last_raa: bool, announce_latest: bool) { @@ -4855,7 +4940,7 @@ fn test_dynamic_spendable_outputs_local_htlc_timeout_tx() { // Create some initial channels let chan_1 = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::supported(), InitFeatures::supported()); - route_payment(&nodes[0], &vec!(&nodes[1])[..], 9000000).0; + let (_, our_payment_hash) = route_payment(&nodes[0], &vec!(&nodes[1])[..], 9000000); let local_txn = get_local_commitment_txn!(nodes[0], chan_1.2); assert_eq!(local_txn[0].input.len(), 1); check_spends!(local_txn[0], chan_1.3); @@ -4866,22 +4951,32 @@ fn test_dynamic_spendable_outputs_local_htlc_timeout_tx() { check_closed_broadcast!(nodes[0], false); check_added_monitors!(nodes[0], 1); - let node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap(); - assert_eq!(node_txn[0].input.len(), 1); - assert_eq!(node_txn[0].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT); - check_spends!(node_txn[0], local_txn[0]); + let htlc_timeout = { + let node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap(); + assert_eq!(node_txn[0].input.len(), 1); + assert_eq!(node_txn[0].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT); + check_spends!(node_txn[0], local_txn[0]); + node_txn[0].clone() + }; + + let header_201 = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; + nodes[0].block_notifier.block_connected(&Block { header: header_201, txdata: vec![htlc_timeout.clone()] }, 201); + connect_blocks(&nodes[0].block_notifier, ANTI_REORG_DELAY - 1, 201, true, header_201.bitcoin_hash()); + let events = nodes[0].node.get_and_clear_pending_events(); + assert_eq!(events.len(), 1); + match events[0] { + Event::PaymentFailed { payment_hash, .. } => { + assert_eq!(payment_hash, our_payment_hash); + }, + _ => panic!("Unexpected event"), + } // Verify that A is able to spend its own HTLC-Timeout tx thanks to spendable output event given back by its ChannelMonitor let spend_txn = check_spendable_outputs!(nodes[0], 1); - assert_eq!(spend_txn.len(), 8); - assert_eq!(spend_txn[0], spend_txn[2]); - assert_eq!(spend_txn[0], spend_txn[4]); - assert_eq!(spend_txn[0], spend_txn[6]); - assert_eq!(spend_txn[1], spend_txn[3]); - assert_eq!(spend_txn[1], spend_txn[5]); - assert_eq!(spend_txn[1], spend_txn[7]); + assert_eq!(spend_txn.len(), 3); + assert_eq!(spend_txn[0], spend_txn[1]); check_spends!(spend_txn[0], local_txn[0]); - check_spends!(spend_txn[1], node_txn[0]); + check_spends!(spend_txn[2], htlc_timeout); } #[test] @@ -4897,12 +4992,16 @@ fn test_static_output_closing_tx() { let closing_tx = close_channel(&nodes[0], &nodes[1], &chan.2, chan.3, true).2; let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; - nodes[0].block_notifier.block_connected(&Block { header, txdata: vec![closing_tx.clone()] }, 1); + nodes[0].block_notifier.block_connected(&Block { header, txdata: vec![closing_tx.clone()] }, 0); + connect_blocks(&nodes[0].block_notifier, ANTI_REORG_DELAY - 1, 0, true, header.bitcoin_hash()); + let spend_txn = check_spendable_outputs!(nodes[0], 2); assert_eq!(spend_txn.len(), 1); check_spends!(spend_txn[0], closing_tx); - nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![closing_tx.clone()] }, 1); + nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![closing_tx.clone()] }, 0); + connect_blocks(&nodes[1].block_notifier, ANTI_REORG_DELAY - 1, 0, true, header.bitcoin_hash()); + let spend_txn = check_spendable_outputs!(nodes[1], 2); assert_eq!(spend_txn.len(), 1); check_spends!(spend_txn[0], closing_tx); @@ -6754,7 +6853,8 @@ fn test_data_loss_protect() { check_spends!(node_txn[0], chan.3); assert_eq!(node_txn[0].output.len(), 2); let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42}; - nodes[0].block_notifier.block_connected(&Block { header, txdata: vec![node_txn[0].clone()]}, 1); + nodes[0].block_notifier.block_connected(&Block { header, txdata: vec![node_txn[0].clone()]}, 0); + connect_blocks(&nodes[0].block_notifier, ANTI_REORG_DELAY - 1, 0, true, header.bitcoin_hash()); let spend_txn = check_spendable_outputs!(nodes[0], 1); assert_eq!(spend_txn.len(), 1); check_spends!(spend_txn[0], node_txn[0]); diff --git a/lightning/src/ln/onchaintx.rs b/lightning/src/ln/onchaintx.rs index 3f21d4c1..8323a58f 100644 --- a/lightning/src/ln/onchaintx.rs +++ b/lightning/src/ln/onchaintx.rs @@ -17,7 +17,6 @@ use ln::msgs::DecodeError; use ln::channelmonitor::{ANTI_REORG_DELAY, CLTV_SHARED_CLAIM_BUFFER, InputMaterial, ClaimRequest}; use ln::chan_utils::HTLCType; use chain::chaininterface::{FeeEstimator, BroadcasterInterface, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT}; -use chain::keysinterface::SpendableOutputDescriptor; use util::logger::Logger; use util::ser::{ReadableArgs, Readable, Writer, Writeable}; use util::byte_utils; @@ -478,7 +477,7 @@ impl OnchainTxHandler { Some((new_timer, new_feerate, bumped_tx)) } - pub(super) fn block_connected(&mut self, txn_matched: &[&Transaction], claimable_outpoints: Vec, height: u32, broadcaster: B, fee_estimator: F) -> Vec + pub(super) fn block_connected(&mut self, txn_matched: &[&Transaction], claimable_outpoints: Vec, height: u32, broadcaster: B, fee_estimator: F) where B::Target: BroadcasterInterface, F::Target: FeeEstimator { @@ -486,7 +485,6 @@ impl OnchainTxHandler { let mut new_claims = Vec::new(); let mut aggregated_claim = HashMap::new(); let mut aggregated_soonest = ::std::u32::MAX; - let mut spendable_outputs = Vec::new(); // Try to aggregate outputs if their timelock expiration isn't imminent (absolute_timelock // <= CLTV_SHARED_CLAIM_BUFFER) and they don't require an immediate nLockTime (aggregable). @@ -522,10 +520,6 @@ impl OnchainTxHandler { self.claimable_outpoints.insert(k.clone(), (txid, height)); } log_trace!(self, "Broadcast onchain {}", log_tx!(tx)); - spendable_outputs.push(SpendableOutputDescriptor::StaticOutput { - outpoint: BitcoinOutPoint { txid: tx.txid(), vout: 0 }, - output: tx.output[0].clone(), - }); broadcaster.broadcast_transaction(&tx); } } @@ -656,8 +650,6 @@ impl OnchainTxHandler { } else { unreachable!(); } } } - - spendable_outputs } pub(super) fn block_disconnected(&mut self, height: u32, broadcaster: B, fee_estimator: F) diff --git a/lightning/src/util/macro_logger.rs b/lightning/src/util/macro_logger.rs index e3a431ed..5a702414 100644 --- a/lightning/src/util/macro_logger.rs +++ b/lightning/src/util/macro_logger.rs @@ -1,4 +1,5 @@ use chain::transaction::OutPoint; +use chain::keysinterface::SpendableOutputDescriptor; use bitcoin_hashes::sha256d::Hash as Sha256dHash; use bitcoin::blockdata::transaction::Transaction; @@ -128,6 +129,30 @@ macro_rules! log_tx { } } +pub(crate) struct DebugSpendable<'a>(pub &'a SpendableOutputDescriptor); +impl<'a> std::fmt::Display for DebugSpendable<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + match self.0 { + &SpendableOutputDescriptor::StaticOutput { ref outpoint, .. } => { + write!(f, "StaticOutput {}:{} marked for spending", outpoint.txid, outpoint.vout)?; + } + &SpendableOutputDescriptor::DynamicOutputP2WSH { ref outpoint, .. } => { + write!(f, "DynamicOutputP2WSH {}:{} marked for spending", outpoint.txid, outpoint.vout)?; + } + &SpendableOutputDescriptor::DynamicOutputP2WPKH { ref outpoint, .. } => { + write!(f, "DynamicOutputP2WPKH {}:{} marked for spending", outpoint.txid, outpoint.vout)?; + } + } + Ok(()) + } +} + +macro_rules! log_spendable { + ($obj: expr) => { + ::util::macro_logger::DebugSpendable(&$obj) + } +} + macro_rules! log_internal { ($self: ident, $lvl:expr, $($arg:tt)+) => ( &$self.logger.log(&::util::logger::Record::new($lvl, format_args!($($arg)+), module_path!(), file!(), line!()));