X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=src%2Fln%2Fchannelmonitor.rs;h=5dddd1e1557db9e9300ba9cf8d65c086bf2702bc;hb=757bcc2951b6750d39ef1d841593be1e7f1c57cd;hp=bb6b36323218f07954015f5f21de24648a29c0f7;hpb=273f2fc14ad4ce4dc9f09559289afdf2e17c1b92;p=rust-lightning diff --git a/src/ln/channelmonitor.rs b/src/ln/channelmonitor.rs index bb6b3632..5dddd1e1 100644 --- a/src/ln/channelmonitor.rs +++ b/src/ln/channelmonitor.rs @@ -303,12 +303,24 @@ const CLTV_SHARED_CLAIM_BUFFER: u32 = 12; pub(crate) const CLTV_CLAIM_BUFFER: u32 = 6; /// Number of blocks by which point we expect our counterparty to have seen new blocks on the /// network and done a full update_fail_htlc/commitment_signed dance (+ we've updated all our -/// copies of ChannelMonitors, including watchtowers). -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. -pub(crate) const HTLC_FAIL_ANTI_REORG_DELAY: u32 = 6; +/// copies of ChannelMonitors, including watchtowers). We could enforce the contract by failing +/// at CLTV expiration height but giving a grace period to our peer may be profitable for us if he +/// can provide an over-late preimage. Nevertheless, grace period has to be accounted in our +/// CLTV_EXPIRY_DELTA to be secure. Following this policy we may decrease the rate of channel failures +/// due to expiration but increase the cost of funds being locked longuer in case of failure. +/// This delay also cover a low-power peer being slow to process blocks and so being behind us on +/// accurate block height. +/// In case of onchain failure to be pass backward we may see the last block of ANTI_REORG_DELAY +/// with at worst this delay, so we are not only using this value as a mercy for them but also +/// us as a safeguard to delay with enough time. +pub(crate) const LATENCY_GRACE_PERIOD_BLOCKS: u32 = 3; +/// Number of blocks we wait on seeing a HTLC output being solved before we fail corresponding inbound +/// HTLCs. This prevents us from failing backwards and then getting a reorg resulting in us losing money. +/// We use also this delay to be sure we can remove our in-flight claim txn from bump candidates buffer. +/// It may cause spurrious generation of bumped claim txn but that's allright given the outpoint is already +/// solved by a previous claim tx. What we want to avoid is reorg evicting our claim tx and us not +/// keeping bumping another claim tx to solve the outpoint. +pub(crate) const ANTI_REORG_DELAY: u32 = 6; #[derive(Clone, PartialEq)] enum Storage { @@ -352,6 +364,49 @@ enum InputDescriptors { RevokedOutput, // either a revoked to_local output on commitment tx, a revoked HTLC-Timeout output or a revoked HTLC-Success output } +/// When ChannelMonitor discovers an onchain outpoint being a step of a channel and that it needs +/// to generate a tx to push channel state forward, we cache outpoint-solving tx material to build +/// a new bumped one in case of lenghty confirmation delay +#[derive(Clone, PartialEq)] +enum TxMaterial { + Revoked { + script: Script, + pubkey: Option, + key: SecretKey, + is_htlc: bool, + amount: u64, + }, + RemoteHTLC { + script: Script, + key: SecretKey, + preimage: Option, + amount: u64, + }, + LocalHTLC { + script: Script, + sigs: (Signature, Signature), + preimage: Option, + amount: u64, + } +} + +/// Upon discovering of some classes of onchain tx by ChannelMonitor, we may have to take actions on it +/// once they mature to enough confirmations (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; @@ -402,7 +457,18 @@ pub struct ChannelMonitor { destination_script: Script, - htlc_updated_waiting_threshold_conf: HashMap, PaymentHash)>>, + // Used to track outpoint in the process of being claimed by our transactions. We need to scan all transactions + // for inputs spending this. If height timer (u32) is expired and claim tx hasn't reached enough confirmations + // before, use TxMaterial to regenerate a new claim tx with a satoshis-per-1000-weight-units higher than last + // one (u64), if timelock expiration (u32) is near, decrease height timer, the in-between bumps delay. + // Last field cached (u32) is height of outpoint confirmation, which is needed to flush this tracker + // in case of reorgs, given block timer are scaled on timer expiration we can't deduce from it original height. + our_claim_txn_waiting_first_conf: HashMap, + + // 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. @@ -415,13 +481,16 @@ pub struct ChannelMonitor { } macro_rules! subtract_high_prio_fee { - ($self: ident, $fee_estimator: expr, $value: expr, $predicted_weight: expr, $spent_txid: expr) => { + ($self: ident, $fee_estimator: expr, $value: expr, $predicted_weight: expr, $spent_txid: expr, $used_feerate: expr) => { { - let mut fee = $fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) * $predicted_weight / 1000; + $used_feerate = $fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority); + let mut fee = $used_feerate * $predicted_weight / 1000; if $value <= fee { - fee = $fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Normal) * $predicted_weight / 1000; + $used_feerate = $fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Normal); + fee = $used_feerate * $predicted_weight / 1000; if $value <= fee { - fee = $fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background) * $predicted_weight / 1000; + $used_feerate = $fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background); + fee = $used_feerate * $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); @@ -466,7 +535,8 @@ impl PartialEq for ChannelMonitor { 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.htlc_updated_waiting_threshold_conf != other.htlc_updated_waiting_threshold_conf + self.our_claim_txn_waiting_first_conf != other.our_claim_txn_waiting_first_conf || + self.onchain_events_waiting_threshold_conf != other.onchain_events_waiting_threshold_conf { false } else { @@ -516,7 +586,9 @@ impl ChannelMonitor { payment_preimages: HashMap::new(), destination_script: destination_script, - htlc_updated_waiting_threshold_conf: HashMap::new(), + our_claim_txn_waiting_first_conf: HashMap::new(), + + onchain_events_waiting_threshold_conf: HashMap::new(), last_block_hash: Default::default(), secp_ctx: Secp256k1::new(), @@ -554,6 +626,15 @@ impl ChannelMonitor { tx_weight } + fn get_height_timer(current_height: u32, timelock_expiration: u32) -> u32 { + if timelock_expiration <= current_height || timelock_expiration - current_height <= 3 { + return current_height + 1 + } else if timelock_expiration - current_height <= 15 { + return current_height + 3 + } + current_height + 15 + } + #[inline] fn place_secret(idx: u64) -> u8 { for i in 0..48 { @@ -1025,14 +1106,60 @@ impl ChannelMonitor { self.last_block_hash.write(writer)?; self.destination_script.write(writer)?; - writer.write_all(&byte_utils::be64_to_array(self.htlc_updated_waiting_threshold_conf.len() as u64))?; - for (ref target, ref updates) in self.htlc_updated_waiting_threshold_conf.iter() { + writer.write_all(&byte_utils::be64_to_array(self.our_claim_txn_waiting_first_conf.len() as u64))?; + for (ref outpoint, claim_tx_data) in self.our_claim_txn_waiting_first_conf.iter() { + outpoint.write(writer)?; + writer.write_all(&byte_utils::be32_to_array(claim_tx_data.0))?; + match claim_tx_data.1 { + TxMaterial::Revoked { ref script, ref pubkey, ref key, ref is_htlc, ref amount} => { + writer.write_all(&[0; 1])?; + script.write(writer)?; + pubkey.write(writer)?; + writer.write_all(&key[..])?; + if *is_htlc { + writer.write_all(&[0; 1])?; + } else { + writer.write_all(&[1; 1])?; + } + writer.write_all(&byte_utils::be64_to_array(*amount))?; + }, + TxMaterial::RemoteHTLC { ref script, ref key, ref preimage, ref amount } => { + writer.write_all(&[1; 1])?; + script.write(writer)?; + key.write(writer)?; + preimage.write(writer)?; + writer.write_all(&byte_utils::be64_to_array(*amount))?; + }, + TxMaterial::LocalHTLC { ref script, ref sigs, ref preimage, ref amount } => { + writer.write_all(&[2; 1])?; + script.write(writer)?; + sigs.0.write(writer)?; + sigs.1.write(writer)?; + preimage.write(writer)?; + writer.write_all(&byte_utils::be64_to_array(*amount))?; + } + } + writer.write_all(&byte_utils::be64_to_array(claim_tx_data.2))?; + writer.write_all(&byte_utils::be32_to_array(claim_tx_data.3))?; + writer.write_all(&byte_utils::be32_to_array(claim_tx_data.4))?; + } + + 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(updates.len() as u64))?; - for ref update in updates.iter() { - update.0.write(writer)?; - update.1.write(writer)?; - update.2.write(writer)?; + 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)?; + } + } } } @@ -1153,10 +1280,9 @@ impl ChannelMonitor { } 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(); + let mut inputs_info = Vec::new(); + let mut inputs_desc = Vec::new(); for (idx, outp) in tx.output.iter().enumerate() { if outp.script_pubkey == revokeable_p2wsh { @@ -1169,10 +1295,9 @@ impl ChannelMonitor { sequence: 0xfffffffd, witness: Vec::new(), }); - htlc_idxs.push(None); - values.push(outp.value); + inputs_desc.push(InputDescriptors::RevokedOutput); + inputs_info.push((None, outp.value, self.our_to_self_delay as u32)); 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 }, @@ -1185,7 +1310,7 @@ impl ChannelMonitor { macro_rules! sign_input { ($sighash_parts: expr, $input: expr, $htlc_idx: expr, $amount: expr) => { { - let (sig, redeemscript) = match self.key_storage { + let (sig, redeemscript, revocation_key) = match self.key_storage { Storage::Local { ref revocation_base_key, .. } => { let redeemscript = if $htlc_idx.is_none() { revokeable_redeemscript.clone() } else { let htlc = &per_commitment_option.unwrap()[$htlc_idx.unwrap()].0; @@ -1193,7 +1318,7 @@ impl ChannelMonitor { }; 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) + (self.secp_ctx.sign(&sighash, &revocation_key), redeemscript, revocation_key) }, Storage::Watchtower { .. } => { unimplemented!(); @@ -1206,7 +1331,8 @@ impl ChannelMonitor { } else { $input.witness.push(revocation_pubkey.serialize().to_vec()); } - $input.witness.push(redeemscript.into_bytes()); + $input.witness.push(redeemscript.clone().into_bytes()); + (redeemscript, revocation_key) } } } @@ -1233,10 +1359,9 @@ impl ChannelMonitor { }; if htlc.cltv_expiry > height + CLTV_SHARED_CLAIM_BUFFER { inputs.push(input); - 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 }); + inputs_desc.push(if htlc.offered { InputDescriptors::RevokedOfferedHTLC } else { InputDescriptors::RevokedReceivedHTLC }); + inputs_info.push((Some(idx), tx.output[transaction_output_index as usize].value, htlc.cltv_expiry)); + total_value += tx.output[transaction_output_index as usize].value; } else { let mut single_htlc_tx = Transaction { version: 2, @@ -1248,10 +1373,16 @@ impl ChannelMonitor { }), }; 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 height_timer = Self::get_height_timer(height, htlc.cltv_expiry); + let mut used_feerate; + if subtract_high_prio_fee!(self, fee_estimator, single_htlc_tx.output[0].value, predicted_weight, tx.txid(), used_feerate) { let sighash_parts = bip143::SighashComponents::new(&single_htlc_tx); - sign_input!(sighash_parts, single_htlc_tx.input[0], Some(idx), htlc.amount_msat / 1000); + let (redeemscript, revocation_key) = sign_input!(sighash_parts, single_htlc_tx.input[0], Some(idx), htlc.amount_msat / 1000); assert!(predicted_weight >= single_htlc_tx.get_weight()); + match self.our_claim_txn_waiting_first_conf.entry(single_htlc_tx.input[0].previous_output.clone()) { + hash_map::Entry::Occupied(_) => {}, + hash_map::Entry::Vacant(entry) => { entry.insert((height_timer, TxMaterial::Revoked { script: redeemscript, pubkey: Some(revocation_pubkey), key: revocation_key, is_htlc: true, amount: htlc.amount_msat / 1000 }, used_feerate, htlc.cltv_expiry, height)); } + } txn_to_broadcast.push(single_htlc_tx); } } @@ -1270,15 +1401,22 @@ impl ChannelMonitor { 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_info!(self, "Failing HTLC with payment_hash {} from {} remote commitment tx due to broadcast of revoked remote commitment transaction, waiting for confirmation (at height {})", log_bytes!(htlc.payment_hash.0), $commitment_tx, height + HTLC_FAIL_ANTI_REORG_DELAY - 1); - match self.htlc_updated_waiting_threshold_conf.entry(height + HTLC_FAIL_ANTI_REORG_DELAY - 1) { + 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 + 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.retain(|ref update| update.0 != **source); - e.push(((**source).clone(), None, htlc.payment_hash.clone())); + 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![((**source).clone(), None, htlc.payment_hash.clone())]); + entry.insert(vec![OnchainEvent::HTLCUpdate { htlc_update: ((**source).clone(), htlc.payment_hash.clone())}]); } } } @@ -1308,18 +1446,23 @@ 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()) { + let predicted_weight = spend_tx.get_weight() + Self::get_witnesses_weight(&inputs_desc[..]); + + let mut used_feerate; + if !subtract_high_prio_fee!(self, fee_estimator, spend_tx.output[0].value, predicted_weight, tx.txid(), used_feerate) { return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs); } - let mut values_drain = values.drain(..); let sighash_parts = bip143::SighashComponents::new(&spend_tx); - for (input, htlc_idx) in spend_tx.input.iter_mut().zip(htlc_idxs.iter()) { - let value = values_drain.next().unwrap(); - sign_input!(sighash_parts, input, htlc_idx, value); + for (input, info) in spend_tx.input.iter_mut().zip(inputs_info.iter()) { + let (redeemscript, revocation_key) = sign_input!(sighash_parts, input, info.0, info.1); + let height_timer = Self::get_height_timer(height, info.2); + match self.our_claim_txn_waiting_first_conf.entry(input.previous_output.clone()) { + hash_map::Entry::Occupied(_) => {}, + hash_map::Entry::Vacant(entry) => { entry.insert((height_timer, TxMaterial::Revoked { script: redeemscript, pubkey: if info.0.is_some() { Some(revocation_pubkey) } else { None }, key: revocation_key, is_htlc: if info.0.is_some() { true } else { false }, amount: info.1 }, used_feerate, if !info.0.is_some() { height + info.2 } else { info.2 }, height)); } + } } assert!(predicted_weight >= spend_tx.get_weight()); @@ -1361,14 +1504,21 @@ 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); - match self.htlc_updated_waiting_threshold_conf.entry(height + HTLC_FAIL_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.retain(|ref update| update.0 != **source); - e.push(((**source).clone(), None, htlc.payment_hash.clone())); + 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![((**source).clone(), None, htlc.payment_hash.clone())]); + entry.insert(vec![OnchainEvent::HTLCUpdate { htlc_update: ((**source).clone(), htlc.payment_hash.clone())}]); } } } @@ -1426,20 +1576,20 @@ impl ChannelMonitor { } let mut total_value = 0; - let mut values = Vec::new(); let mut inputs = Vec::new(); - let mut input_descriptors = Vec::new(); + let mut inputs_desc = Vec::new(); + let mut inputs_info = Vec::new(); macro_rules! sign_input { ($sighash_parts: expr, $input: expr, $amount: expr, $preimage: expr) => { { - let (sig, redeemscript) = match self.key_storage { + let (sig, redeemscript, htlc_key) = match self.key_storage { 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 = 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) + (self.secp_ctx.sign(&sighash, &htlc_key), redeemscript, htlc_key) }, Storage::Watchtower { .. } => { unimplemented!(); @@ -1448,7 +1598,8 @@ impl ChannelMonitor { $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()); + $input.witness.push(redeemscript.clone().into_bytes()); + (redeemscript, htlc_key) } } } @@ -1473,9 +1624,9 @@ impl ChannelMonitor { }; if htlc.cltv_expiry > height + CLTV_SHARED_CLAIM_BUFFER { 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 }); + inputs_desc.push(if htlc.offered { InputDescriptors::OfferedHTLC } else { InputDescriptors::ReceivedHTLC }); + inputs_info.push((payment_preimage, tx.output[transaction_output_index as usize].value, htlc.cltv_expiry)); + total_value += tx.output[transaction_output_index as usize].value; } else { let mut single_htlc_tx = Transaction { version: 2, @@ -1487,14 +1638,20 @@ impl ChannelMonitor { }), }; 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 height_timer = Self::get_height_timer(height, htlc.cltv_expiry); + let mut used_feerate; + if subtract_high_prio_fee!(self, fee_estimator, single_htlc_tx.output[0].value, predicted_weight, tx.txid(), used_feerate) { 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()); + let (redeemscript, htlc_key) = 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(), }); + match self.our_claim_txn_waiting_first_conf.entry(single_htlc_tx.input[0].previous_output.clone()) { + hash_map::Entry::Occupied(_) => {}, + hash_map::Entry::Vacant(entry) => { entry.insert((height_timer, TxMaterial::RemoteHTLC { script: redeemscript, key: htlc_key, preimage: Some(*payment_preimage), amount: htlc.amount_msat / 1000 }, used_feerate, htlc.cltv_expiry, height)); } + } txn_to_broadcast.push(single_htlc_tx); } } @@ -1520,8 +1677,19 @@ impl ChannelMonitor { value: htlc.amount_msat / 1000, }), }; - let sighash_parts = bip143::SighashComponents::new(&timeout_tx); - sign_input!(sighash_parts, timeout_tx.input[0], htlc.amount_msat / 1000, vec![0]); + let predicted_weight = timeout_tx.get_weight() + Self::get_witnesses_weight(&[InputDescriptors::ReceivedHTLC]); + let height_timer = Self::get_height_timer(height, htlc.cltv_expiry); + let mut used_feerate; + if subtract_high_prio_fee!(self, fee_estimator, timeout_tx.output[0].value, predicted_weight, tx.txid(), used_feerate) { + let sighash_parts = bip143::SighashComponents::new(&timeout_tx); + let (redeemscript, htlc_key) = sign_input!(sighash_parts, timeout_tx.input[0], htlc.amount_msat / 1000, vec![0]); + assert!(predicted_weight >= timeout_tx.get_weight()); + //TODO: track SpendableOutputDescriptor + match self.our_claim_txn_waiting_first_conf.entry(timeout_tx.input[0].previous_output.clone()) { + hash_map::Entry::Occupied(_) => {}, + hash_map::Entry::Vacant(entry) => { entry.insert((height_timer, TxMaterial::RemoteHTLC { script : redeemscript, key: htlc_key, preimage: None, amount: htlc.amount_msat / 1000 }, used_feerate, htlc.cltv_expiry, height)); } + } + } txn_to_broadcast.push(timeout_tx); } } @@ -1539,19 +1707,24 @@ 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()) { + + let mut predicted_weight = spend_tx.get_weight() + Self::get_witnesses_weight(&inputs_desc[..]); + + let mut used_feerate; + if !subtract_high_prio_fee!(self, fee_estimator, spend_tx.output[0].value, predicted_weight, tx.txid(), used_feerate) { return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs); } - let mut values_drain = values.drain(..); let sighash_parts = bip143::SighashComponents::new(&spend_tx); - for input in spend_tx.input.iter_mut() { - let value = values_drain.next().unwrap(); - sign_input!(sighash_parts, input, value.0, (value.1).0.to_vec()); + for (input, info) in spend_tx.input.iter_mut().zip(inputs_info.iter()) { + let (redeemscript, htlc_key) = sign_input!(sighash_parts, input, info.1, (info.0).0.to_vec()); + let height_timer = Self::get_height_timer(height, info.2); + match self.our_claim_txn_waiting_first_conf.entry(input.previous_output.clone()) { + hash_map::Entry::Occupied(_) => {}, + hash_map::Entry::Vacant(entry) => { entry.insert((height_timer, TxMaterial::RemoteHTLC { script: redeemscript, key: htlc_key, preimage: Some(*(info.0)), amount: info.1}, used_feerate, info.2, height)); } + } } - assert!(predicted_weight >= spend_tx.get_weight()); spendable_outputs.push(SpendableOutputDescriptor::StaticOutput { outpoint: BitcoinOutPoint { txid: spend_tx.txid(), vout: 0 }, @@ -1566,7 +1739,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, fee_estimator: &FeeEstimator) -> (Option, Option) { + fn check_spend_remote_htlc(&mut self, tx: &Transaction, commitment_number: u64, height: u32, fee_estimator: &FeeEstimator) -> (Option, Option) { if tx.input.len() != 1 || tx.output.len() != 1 { return (None, None) } @@ -1628,17 +1801,18 @@ impl ChannelMonitor { 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()) { + let mut used_feerate; + if !subtract_high_prio_fee!(self, fee_estimator, spend_tx.output[0].value, predicted_weight, tx.txid(), used_feerate) { return (None, None); } let sighash_parts = bip143::SighashComponents::new(&spend_tx); - let sig = match self.key_storage { + let (sig, revocation_key) = match self.key_storage { Storage::Local { ref revocation_base_key, .. } => { 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) + (self.secp_ctx.sign(&sighash, &revocation_key), revocation_key) } Storage::Watchtower { .. } => { unimplemented!(); @@ -1647,19 +1821,25 @@ impl ChannelMonitor { 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()); + spend_tx.input[0].witness.push(redeemscript.clone().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(); + let height_timer = Self::get_height_timer(height, self.their_to_self_delay.unwrap() as u32); // We can safely unwrap given we are past channel opening + match self.our_claim_txn_waiting_first_conf.entry(spend_tx.input[0].previous_output.clone()) { + hash_map::Entry::Occupied(_) => {}, + hash_map::Entry::Vacant(entry) => { entry.insert((height_timer, TxMaterial::Revoked { script: redeemscript, pubkey: None, key: revocation_key, is_htlc: false, amount: tx.output[0].value }, used_feerate, height + self.our_to_self_delay as u32, height)); } + } (Some(spend_tx), Some(SpendableOutputDescriptor::StaticOutput { outpoint, output })) } else { (None, None) } } - fn broadcast_by_local_state(&self, local_tx: &LocalSignedTx, per_commitment_point: &Option, delayed_payment_base_key: &Option) -> (Vec, Vec, Vec) { + fn broadcast_by_local_state(&self, local_tx: &LocalSignedTx, per_commitment_point: &Option, delayed_payment_base_key: &Option, height: u32) -> (Vec, Vec, Vec, Vec<(BitcoinOutPoint, (u32, TxMaterial, u64, u32, u32))>) { 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()); + let mut pending_claims = Vec::with_capacity(local_tx.htlc_outputs.len()); macro_rules! add_dynamic_output { ($father_tx: expr, $vout: expr) => { @@ -1704,9 +1884,12 @@ impl ChannelMonitor { htlc_timeout_tx.input[0].witness[2].push(SigHashType::All as u8); htlc_timeout_tx.input[0].witness.push(Vec::new()); - htlc_timeout_tx.input[0].witness.push(chan_utils::get_htlc_redeemscript_with_explicit_keys(htlc, &local_tx.a_htlc_key, &local_tx.b_htlc_key, &local_tx.revocation_key).into_bytes()); + let htlc_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(htlc, &local_tx.a_htlc_key, &local_tx.b_htlc_key, &local_tx.revocation_key); + htlc_timeout_tx.input[0].witness.push(htlc_script.clone().into_bytes()); add_dynamic_output!(htlc_timeout_tx, 0); + let height_timer = Self::get_height_timer(height, htlc.cltv_expiry); + pending_claims.push((htlc_timeout_tx.input[0].previous_output.clone(), (height_timer, TxMaterial::LocalHTLC { script: htlc_script, sigs: (*their_sig, *our_sig), preimage: None, amount: htlc.amount_msat / 1000}, 0, htlc.cltv_expiry, height))); res.push(htlc_timeout_tx); } else { if let Some(payment_preimage) = self.payment_preimages.get(&htlc.payment_hash) { @@ -1721,9 +1904,12 @@ impl ChannelMonitor { htlc_success_tx.input[0].witness[2].push(SigHashType::All as u8); htlc_success_tx.input[0].witness.push(payment_preimage.0.to_vec()); - htlc_success_tx.input[0].witness.push(chan_utils::get_htlc_redeemscript_with_explicit_keys(htlc, &local_tx.a_htlc_key, &local_tx.b_htlc_key, &local_tx.revocation_key).into_bytes()); + let htlc_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(htlc, &local_tx.a_htlc_key, &local_tx.b_htlc_key, &local_tx.revocation_key); + htlc_success_tx.input[0].witness.push(htlc_script.clone().into_bytes()); add_dynamic_output!(htlc_success_tx, 0); + let height_timer = Self::get_height_timer(height, htlc.cltv_expiry); + pending_claims.push((htlc_success_tx.input[0].previous_output.clone(), (height_timer, TxMaterial::LocalHTLC { script: htlc_script, sigs: (*their_sig, *our_sig), preimage: Some(*payment_preimage), amount: htlc.amount_msat / 1000}, 0, htlc.cltv_expiry, height))); res.push(htlc_success_tx); } } @@ -1732,7 +1918,7 @@ impl ChannelMonitor { } } - (res, spendable_outputs, watch_outputs) + (res, spendable_outputs, watch_outputs, pending_claims) } /// Attempts to claim any claimable HTLCs in a commitment transaction which was not (yet) @@ -1745,16 +1931,23 @@ impl ChannelMonitor { let mut watch_outputs = Vec::new(); macro_rules! wait_threshold_conf { - ($height: expr, $source: expr, $update: expr, $commitment_tx: expr, $payment_hash: expr) => { - log_info!(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.htlc_updated_waiting_threshold_conf.entry($height + HTLC_FAIL_ANTI_REORG_DELAY - 1) { + ($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 + 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.retain(|ref update| update.0 != $source); - e.push(($source, $update, $payment_hash)); + 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![($source, $update, $payment_hash)]); + entry.insert(vec![OnchainEvent::HTLCUpdate { htlc_update: ($source, $payment_hash)}]); } } } @@ -1765,6 +1958,12 @@ impl ChannelMonitor { local_txn.append(&mut $updates.0); spendable_outputs.append(&mut $updates.1); watch_outputs.append(&mut $updates.2); + for claim in $updates.3 { + match self.our_claim_txn_waiting_first_conf.entry(claim.0) { + hash_map::Entry::Occupied(_) => {}, + hash_map::Entry::Vacant(entry) => { entry.insert(claim.1); } + } + } } } @@ -1777,10 +1976,10 @@ impl ChannelMonitor { 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, .. } => { - append_onchain_update!(self.broadcast_by_local_state(local_tx, latest_per_commitment_point, &Some(*delayed_payment_base_key))); + append_onchain_update!(self.broadcast_by_local_state(local_tx, latest_per_commitment_point, &Some(*delayed_payment_base_key), height)); }, Storage::Watchtower { .. } => { - append_onchain_update!(self.broadcast_by_local_state(local_tx, &None, &None)); + append_onchain_update!(self.broadcast_by_local_state(local_tx, &None, &None, height)); } } } @@ -1791,10 +1990,10 @@ impl ChannelMonitor { 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, .. } => { - append_onchain_update!(self.broadcast_by_local_state(local_tx, prev_latest_per_commitment_point, &Some(*delayed_payment_base_key))); + append_onchain_update!(self.broadcast_by_local_state(local_tx, prev_latest_per_commitment_point, &Some(*delayed_payment_base_key), height)); }, Storage::Watchtower { .. } => { - append_onchain_update!(self.broadcast_by_local_state(local_tx, &None, &None)); + append_onchain_update!(self.broadcast_by_local_state(local_tx, &None, &None, height)); } } } @@ -1805,7 +2004,7 @@ impl ChannelMonitor { 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(), None, "lastest", htlc.payment_hash.clone()); + wait_threshold_conf!(height, source.clone(), "lastest", htlc.payment_hash.clone()); } } } @@ -1856,7 +2055,9 @@ impl ChannelMonitor { let mut res = vec![local_tx.tx.clone()]; match self.key_storage { Storage::Local { ref delayed_payment_base_key, ref prev_latest_per_commitment_point, .. } => { - res.append(&mut self.broadcast_by_local_state(local_tx, prev_latest_per_commitment_point, &Some(*delayed_payment_base_key)).0); + res.append(&mut self.broadcast_by_local_state(local_tx, prev_latest_per_commitment_point, &Some(*delayed_payment_base_key), 0).0); + // We throw away the generated waiting_first_conf data as we aren't (yet) confirmed and we don't actually know what the caller wants to do. + // The data will be re-generated and tracked in check_spend_local_transaction if we get a confirmation. }, _ => panic!("Can only broadcast by local channelmonitor"), }; @@ -1908,7 +2109,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, fee_estimator); + let (tx, spendable_output) = self.check_spend_remote_htlc(tx, commitment_number, height, fee_estimator); if let Some(tx) = tx { txn.push(tx); } @@ -1928,14 +2129,37 @@ impl ChannelMonitor { if updated.len() > 0 { htlc_updated.append(&mut updated); } + for inp in &tx.input { + if self.our_claim_txn_waiting_first_conf.contains_key(&inp.previous_output) { + 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.retain(|ref event| { + match **event { + OnchainEvent::Claim { outpoint } => { + return outpoint != inp.previous_output + }, + _ => return true + } + }); + e.push(OnchainEvent::Claim { outpoint: inp.previous_output.clone()}); + } + hash_map::Entry::Vacant(entry) => { + entry.insert(vec![OnchainEvent::Claim { outpoint: inp.previous_output.clone()}]); + } + } + } + } } + let mut pending_claims = Vec::new(); if let Some(ref cur_local_tx) = self.current_local_signed_commitment_tx { if self.would_broadcast_at_height(height) { broadcaster.broadcast_transaction(&cur_local_tx.tx); match self.key_storage { Storage::Local { ref delayed_payment_base_key, ref latest_per_commitment_point, .. } => { - let (txs, mut spendable_output, new_outputs) = self.broadcast_by_local_state(&cur_local_tx, latest_per_commitment_point, &Some(*delayed_payment_base_key)); + let (txs, mut spendable_output, new_outputs, mut pending_txn) = self.broadcast_by_local_state(&cur_local_tx, latest_per_commitment_point, &Some(*delayed_payment_base_key), height); spendable_outputs.append(&mut spendable_output); + pending_claims.append(&mut pending_txn); if !new_outputs.is_empty() { watch_outputs.push((cur_local_tx.txid.clone(), new_outputs)); } @@ -1944,8 +2168,9 @@ impl ChannelMonitor { } }, Storage::Watchtower { .. } => { - let (txs, mut spendable_output, new_outputs) = self.broadcast_by_local_state(&cur_local_tx, &None, &None); + let (txs, mut spendable_output, new_outputs, mut pending_txn) = self.broadcast_by_local_state(&cur_local_tx, &None, &None, height); spendable_outputs.append(&mut spendable_output); + pending_claims.append(&mut pending_txn); if !new_outputs.is_empty() { watch_outputs.push((cur_local_tx.txid.clone(), new_outputs)); } @@ -1956,20 +2181,37 @@ impl ChannelMonitor { } } } - if let Some(updates) = self.htlc_updated_waiting_threshold_conf.remove(&height) { - for update in updates { - log_trace!(self, "HTLC {} failure update has get enough confirmation to be pass upstream", log_bytes!((update.2).0)); - htlc_updated.push(update); + for claim in pending_claims { + match self.our_claim_txn_waiting_first_conf.entry(claim.0) { + hash_map::Entry::Occupied(_) => {}, + hash_map::Entry::Vacant(entry) => { entry.insert(claim.1); } + } + } + if let Some(events) = self.onchain_events_waiting_threshold_conf.remove(&height) { + for ev in events { + match ev { + OnchainEvent::Claim { outpoint } => { + self.our_claim_txn_waiting_first_conf.remove(&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)); + }, + } } } + //TODO: iter on buffered TxMaterial in our_claim_txn_waiting_first_conf, if block timer is expired generate a bumped claim tx (RBF or CPFP accordingly) self.last_block_hash = block_hash.clone(); (watch_outputs, spendable_outputs, htlc_updated) } fn block_disconnected(&mut self, height: u32, block_hash: &Sha256dHash) { - if let Some(_) = self.htlc_updated_waiting_threshold_conf.remove(&(height + HTLC_FAIL_ANTI_REORG_DELAY - 1)) { - //We discard htlc update there as failure-trigger tx (revoked commitment tx, non-revoked commitment tx, HTLC-timeout tx) has been disconnected + 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 + //- our claim tx on a commitment tx output } + self.our_claim_txn_waiting_first_conf.retain(|_, ref mut v| if v.3 == height { false } else { true }); self.last_block_hash = block_hash.clone(); } @@ -2002,16 +2244,16 @@ impl ChannelMonitor { // from us until we've reached the point where we go on-chain with the // corresponding inbound HTLC, we must ensure that outbound HTLCs go on chain at // least CLTV_CLAIM_BUFFER blocks prior to the inbound HTLC. - // aka outbound_cltv + HTLC_FAIL_TIMEOUT_BLOCKS == height - CLTV_CLAIM_BUFFER + // aka outbound_cltv + LATENCY_GRACE_PERIOD_BLOCKS == height - CLTV_CLAIM_BUFFER // inbound_cltv == height + CLTV_CLAIM_BUFFER - // 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 + // outbound_cltv + LATENCY_GRACE_PERIOD_BLOCKS + CLTV_CLAIM_BUFFER <= inbound_cltv - CLTV_CLAIM_BUFFER + // LATENCY_GRACE_PERIOD_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 + // LATENCY_GRACE_PERIOD_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) || + if ( htlc_outbound && htlc.cltv_expiry + LATENCY_GRACE_PERIOD_BLOCKS <= height) || (!htlc_outbound && htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER && self.payment_preimages.contains_key(&htlc.payment_hash)) { log_info!(self, "Force-closing channel due to {} HTLC timeout, HTLC expiry is {}", if htlc_outbound { "outbound" } else { "inbound "}, htlc.cltv_expiry); return true; @@ -2149,15 +2391,22 @@ impl ChannelMonitor { payment_preimage.0.copy_from_slice(&input.witness[1]); htlc_updated.push((source, Some(payment_preimage), payment_hash)); } else { - 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.htlc_updated_waiting_threshold_conf.entry(height + HTLC_FAIL_ANTI_REORG_DELAY - 1) { + log_info!(self, "Failing HTLC with payment_hash {} timeout by a spend tx, waiting for confirmation (at height{})", log_bytes!(payment_hash.0), 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.retain(|ref update| update.0 != source); - e.push((source, None, payment_hash.clone())); + 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![(source, None, payment_hash)]); + entry.insert(vec![OnchainEvent::HTLCUpdate { htlc_update: (source, payment_hash)}]); } } } @@ -2379,19 +2628,89 @@ impl ReadableArgs> for (Sha256dHash, ChannelM let last_block_hash: Sha256dHash = Readable::read(reader)?; let destination_script = Readable::read(reader)?; + let our_claim_txn_waiting_first_conf_len: u64 = Readable::read(reader)?; + let mut our_claim_txn_waiting_first_conf = HashMap::with_capacity(cmp::min(our_claim_txn_waiting_first_conf_len as usize, MAX_ALLOC_SIZE / 128)); + for _ in 0..our_claim_txn_waiting_first_conf_len { + let outpoint = Readable::read(reader)?; + let height_target = Readable::read(reader)?; + let tx_material = match >::read(reader)? { + 0 => { + let script = Readable::read(reader)?; + let pubkey = Readable::read(reader)?; + let key = Readable::read(reader)?; + let is_htlc = match >::read(reader)? { + 0 => true, + 1 => false, + _ => return Err(DecodeError::InvalidValue), + }; + let amount = Readable::read(reader)?; + TxMaterial::Revoked { + script, + pubkey, + key, + is_htlc, + amount + } + }, + 1 => { + let script = Readable::read(reader)?; + let key = Readable::read(reader)?; + let preimage = Readable::read(reader)?; + let amount = Readable::read(reader)?; + TxMaterial::RemoteHTLC { + script, + key, + preimage, + amount + } + }, + 2 => { + let script = Readable::read(reader)?; + let their_sig = Readable::read(reader)?; + let our_sig = Readable::read(reader)?; + let preimage = Readable::read(reader)?; + let amount = Readable::read(reader)?; + TxMaterial::LocalHTLC { + script, + sigs: (their_sig, our_sig), + preimage, + amount + } + } + _ => return Err(DecodeError::InvalidValue), + }; + let last_fee = Readable::read(reader)?; + let timelock_expiration = Readable::read(reader)?; + let height = Readable::read(reader)?; + our_claim_txn_waiting_first_conf.insert(outpoint, (height_target, tx_material, last_fee, timelock_expiration, height)); + } + let waiting_threshold_conf_len: u64 = Readable::read(reader)?; - let mut htlc_updated_waiting_threshold_conf = HashMap::with_capacity(cmp::min(waiting_threshold_conf_len as usize, MAX_ALLOC_SIZE / 128)); + 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 updates_len: u64 = Readable::read(reader)?; - let mut updates = Vec::with_capacity(cmp::min(updates_len as usize, MAX_ALLOC_SIZE / 128)); - for _ in 0..updates_len { - let htlc_source = Readable::read(reader)?; - let preimage = Readable::read(reader)?; - let hash = Readable::read(reader)?; - updates.push((htlc_source, preimage, hash)); + 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); } - htlc_updated_waiting_threshold_conf.insert(height_target, updates); + onchain_events_waiting_threshold_conf.insert(height_target, events); } Ok((last_block_hash.clone(), ChannelMonitor { @@ -2418,7 +2737,9 @@ impl ReadableArgs> for (Sha256dHash, ChannelM destination_script, - htlc_updated_waiting_threshold_conf, + our_claim_txn_waiting_first_conf, + + onchain_events_waiting_threshold_conf, last_block_hash, secp_ctx,