X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fonchaintx.rs;h=67a82a480848aeec8d86325be36c6841c261cc8d;hb=8e3744813a8730a0b4fa6219d314635167947f27;hp=7228a687aca6eee88a670ab35a46bba3bb77443c;hpb=94bb0c9128c2635ea08ec089f79fab04880dedd0;p=rust-lightning diff --git a/lightning/src/ln/onchaintx.rs b/lightning/src/ln/onchaintx.rs index 7228a687..67a82a48 100644 --- a/lightning/src/ln/onchaintx.rs +++ b/lightning/src/ln/onchaintx.rs @@ -32,16 +32,37 @@ use util::logger::Logger; use util::ser::{Readable, ReadableArgs, Writer, Writeable, VecWriter}; use util::byte_utils; -use std::collections::{HashMap, hash_map}; +use std::collections::HashMap; use std::cmp; use std::ops::Deref; use std::mem::replace; const MAX_ALLOC_SIZE: usize = 64*1024; +/// An entry for an [`OnchainEvent`], stating the block height when the event was observed and the +/// transaction causing it. +/// +/// Used to determine when the on-chain event can be considered safe from a chain reorganization. +#[derive(PartialEq)] +struct OnchainEventEntry { + txid: Txid, + height: u32, + event: OnchainEvent, +} + +impl OnchainEventEntry { + fn confirmation_threshold(&self) -> u32 { + self.height + ANTI_REORG_DELAY - 1 + } + + fn has_reached_confirmation_threshold(&self, height: u32) -> bool { + height >= self.confirmation_threshold() + } +} + /// 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)] +#[derive(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. @@ -280,7 +301,7 @@ pub struct OnchainTxHandler { #[cfg(not(test))] claimable_outpoints: HashMap, - onchain_events_waiting_threshold_conf: HashMap>, + onchain_events_waiting_threshold_conf: Vec, latest_height: u32, @@ -318,20 +339,18 @@ impl OnchainTxHandler { } writer.write_all(&byte_utils::be64_to_array(self.onchain_events_waiting_threshold_conf.len() as u64))?; - for (ref target, ref events) in self.onchain_events_waiting_threshold_conf.iter() { - writer.write_all(&byte_utils::be32_to_array(**target))?; - writer.write_all(&byte_utils::be64_to_array(events.len() as u64))?; - for ev in events.iter() { - match *ev { - OnchainEvent::Claim { ref claim_request } => { - writer.write_all(&[0; 1])?; - claim_request.write(writer)?; - }, - OnchainEvent::ContentiousOutpoint { ref outpoint, ref input_material } => { - writer.write_all(&[1; 1])?; - outpoint.write(writer)?; - input_material.write(writer)?; - } + for ref entry in self.onchain_events_waiting_threshold_conf.iter() { + entry.txid.write(writer)?; + writer.write_all(&byte_utils::be32_to_array(entry.height))?; + match entry.event { + OnchainEvent::Claim { ref claim_request } => { + writer.write_all(&[0; 1])?; + claim_request.write(writer)?; + }, + OnchainEvent::ContentiousOutpoint { ref outpoint, ref input_material } => { + writer.write_all(&[1; 1])?; + outpoint.write(writer)?; + input_material.write(writer)?; } } } @@ -377,35 +396,34 @@ impl<'a, K: KeysInterface> ReadableArgs<&'a K> for OnchainTxHandler { claimable_outpoints.insert(outpoint, (ancestor_claim_txid, height)); } 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)); + let mut onchain_events_waiting_threshold_conf = Vec::with_capacity(cmp::min(waiting_threshold_conf_len as usize, MAX_ALLOC_SIZE / 128)); for _ in 0..waiting_threshold_conf_len { - let height_target = Readable::read(reader)?; - let events_len: u64 = Readable::read(reader)?; - let mut events = Vec::with_capacity(cmp::min(events_len as usize, MAX_ALLOC_SIZE / 128)); - for _ in 0..events_len { - let ev = match ::read(reader)? { - 0 => { - let claim_request = Readable::read(reader)?; - OnchainEvent::Claim { - claim_request - } - }, - 1 => { - let outpoint = Readable::read(reader)?; - let input_material = Readable::read(reader)?; - OnchainEvent::ContentiousOutpoint { - outpoint, - input_material - } + let txid = Readable::read(reader)?; + let height = Readable::read(reader)?; + let event = match ::read(reader)? { + 0 => { + let claim_request = Readable::read(reader)?; + OnchainEvent::Claim { + claim_request } - _ => return Err(DecodeError::InvalidValue), - }; - events.push(ev); - } - onchain_events_waiting_threshold_conf.insert(height_target, events); + }, + 1 => { + let outpoint = Readable::read(reader)?; + let input_material = Readable::read(reader)?; + OnchainEvent::ContentiousOutpoint { + outpoint, + input_material + } + } + _ => return Err(DecodeError::InvalidValue), + }; + onchain_events_waiting_threshold_conf.push(OnchainEventEntry { txid, height, event }); } let latest_height = Readable::read(reader)?; + let mut secp_ctx = Secp256k1::new(); + secp_ctx.seeded_randomize(&keys_manager.get_secure_random_bytes()); + Ok(OnchainTxHandler { destination_script, holder_commitment, @@ -418,13 +436,13 @@ impl<'a, K: KeysInterface> ReadableArgs<&'a K> for OnchainTxHandler { pending_claim_requests, onchain_events_waiting_threshold_conf, latest_height, - secp_ctx: Secp256k1::new(), + secp_ctx, }) } } impl OnchainTxHandler { - pub(crate) fn new(destination_script: Script, signer: ChannelSigner, channel_parameters: ChannelTransactionParameters, holder_commitment: HolderCommitmentTransaction) -> Self { + pub(crate) fn new(destination_script: Script, signer: ChannelSigner, channel_parameters: ChannelTransactionParameters, holder_commitment: HolderCommitmentTransaction, secp_ctx: Secp256k1) -> Self { OnchainTxHandler { destination_script, holder_commitment, @@ -435,10 +453,10 @@ impl OnchainTxHandler { channel_transaction_parameters: channel_parameters, pending_claim_requests: HashMap::new(), claimable_outpoints: HashMap::new(), - onchain_events_waiting_threshold_conf: HashMap::new(), + onchain_events_waiting_threshold_conf: Vec::new(), latest_height: 0, - secp_ctx: Secp256k1::new(), + secp_ctx, } } @@ -753,16 +771,13 @@ impl OnchainTxHandler { macro_rules! clean_claim_request_after_safety_delay { () => { - let new_event = OnchainEvent::Claim { claim_request: first_claim_txid_height.0.clone() }; - match self.onchain_events_waiting_threshold_conf.entry(height + ANTI_REORG_DELAY - 1) { - hash_map::Entry::Occupied(mut entry) => { - if !entry.get().contains(&new_event) { - entry.get_mut().push(new_event); - } - }, - hash_map::Entry::Vacant(entry) => { - entry.insert(vec![new_event]); - } + let entry = OnchainEventEntry { + txid: tx.txid(), + height, + event: OnchainEvent::Claim { claim_request: first_claim_txid_height.0.clone() } + }; + if !self.onchain_events_waiting_threshold_conf.contains(&entry) { + self.onchain_events_waiting_threshold_conf.push(entry); } } } @@ -796,24 +811,23 @@ impl OnchainTxHandler { } } for (outpoint, input_material) in claimed_outputs_material.drain(..) { - let new_event = OnchainEvent::ContentiousOutpoint { outpoint, input_material }; - match self.onchain_events_waiting_threshold_conf.entry(height + ANTI_REORG_DELAY - 1) { - hash_map::Entry::Occupied(mut entry) => { - if !entry.get().contains(&new_event) { - entry.get_mut().push(new_event); - } - }, - hash_map::Entry::Vacant(entry) => { - entry.insert(vec![new_event]); - } + let entry = OnchainEventEntry { + txid: tx.txid(), + height, + event: OnchainEvent::ContentiousOutpoint { outpoint, input_material }, + }; + if !self.onchain_events_waiting_threshold_conf.contains(&entry) { + self.onchain_events_waiting_threshold_conf.push(entry); } } } // After security delay, either our claim tx got enough confs or outpoint is definetely out of reach - if let Some(events) = self.onchain_events_waiting_threshold_conf.remove(&height) { - for ev in events { - match ev { + let onchain_events_waiting_threshold_conf = + self.onchain_events_waiting_threshold_conf.drain(..).collect::>(); + for entry in onchain_events_waiting_threshold_conf { + if entry.has_reached_confirmation_threshold(height) { + match entry.event { OnchainEvent::Claim { claim_request } => { // We may remove a whole set of claim outpoints here, as these one may have // been aggregated in a single tx and claimed so atomically @@ -827,13 +841,15 @@ impl OnchainTxHandler { self.claimable_outpoints.remove(&outpoint); } } + } else { + self.onchain_events_waiting_threshold_conf.push(entry); } } // Check if any pending claim request must be rescheduled for (first_claim_txid, ref claim_data) in self.pending_claim_requests.iter() { - if let Some(h) = claim_data.height_timer { - if h == height { + if let Some(height_timer) = claim_data.height_timer { + if height >= height_timer { bump_candidates.insert(*first_claim_txid, (*claim_data).clone()); } } @@ -859,11 +875,13 @@ impl OnchainTxHandler { L::Target: Logger, { let mut bump_candidates = HashMap::new(); - if let Some(events) = self.onchain_events_waiting_threshold_conf.remove(&(height + ANTI_REORG_DELAY - 1)) { - //- our claim tx on a commitment tx output - //- resurect outpoint back in its claimable set and regenerate tx - for ev in events { - match ev { + let onchain_events_waiting_threshold_conf = + self.onchain_events_waiting_threshold_conf.drain(..).collect::>(); + for entry in onchain_events_waiting_threshold_conf { + if entry.height >= height { + //- our claim tx on a commitment tx output + //- resurect outpoint back in its claimable set and regenerate tx + match entry.event { OnchainEvent::ContentiousOutpoint { outpoint, input_material } => { if let Some(ancestor_claimable_txid) = self.claimable_outpoints.get(&outpoint) { if let Some(claim_material) = self.pending_claim_requests.get_mut(&ancestor_claimable_txid.0) { @@ -876,6 +894,8 @@ impl OnchainTxHandler { }, _ => {}, } + } else { + self.onchain_events_waiting_threshold_conf.push(entry); } } for (_, claim_material) in bump_candidates.iter_mut() { @@ -892,7 +912,7 @@ impl OnchainTxHandler { // right now if one of the outpoint get disconnected, just erase whole pending claim request. let mut remove_request = Vec::new(); self.claimable_outpoints.retain(|_, ref v| - if v.1 == height { + if v.1 >= height { remove_request.push(v.0.clone()); false } else { true });