X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fchannelmanager.rs;h=39426b3fa7b2656b7667c29b94d94c50d6b4cc96;hb=8e3744813a8730a0b4fa6219d314635167947f27;hp=d2ea7dfea798baea181dec19838cf2b55ccaad22;hpb=eee1c30ea6592463d41eefad8306223ec5548014;p=rust-lightning diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index d2ea7dfe..39426b3f 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -28,7 +28,7 @@ use bitcoin::hashes::hmac::{Hmac, HmacEngine}; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::sha256d::Hash as Sha256dHash; use bitcoin::hashes::cmp::fixed_time_eq; -use bitcoin::hash_types::BlockHash; +use bitcoin::hash_types::{BlockHash, Txid}; use bitcoin::secp256k1::key::{SecretKey,PublicKey}; use bitcoin::secp256k1::Secp256k1; @@ -352,9 +352,6 @@ struct PeerState { latest_features: InitFeatures, } -#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))] -const ERR: () = "You need at least 32 bit pointers (well, usize, but we'll assume they're the same) for ChannelManager::latest_block_height"; - /// SimpleArcChannelManager is useful when you need a ChannelManager with a static lifetime, e.g. /// when you're using lightning-net-tokio (since tokio::spawn requires parameters with static /// lifetimes). Other times you can afford a reference, which is more efficient, in which case @@ -424,10 +421,9 @@ pub struct ChannelManager, #[cfg(not(test))] - latest_block_height: AtomicUsize, - last_block_hash: RwLock, + best_block: RwLock, secp_ctx: Secp256k1, #[cfg(any(test, feature = "_test_utils"))] @@ -475,13 +471,38 @@ pub struct ChainParameters { /// The network for determining the `chain_hash` in Lightning messages. pub network: Network, - /// The hash of the latest block successfully connected. - pub latest_hash: BlockHash, - - /// The height of the latest block successfully connected. + /// The hash and height of the latest block successfully connected. /// /// Used to track on-chain channel funding outputs and send payments with reliable timelocks. - pub latest_height: usize, + pub best_block: BestBlock, +} + +/// The best known block as identified by its hash and height. +#[derive(Clone, Copy)] +pub struct BestBlock { + block_hash: BlockHash, + height: u32, +} + +impl BestBlock { + /// Returns the best block from the genesis of the given network. + pub fn from_genesis(network: Network) -> Self { + BestBlock { + block_hash: genesis_block(network).header.block_hash(), + height: 0, + } + } + + /// Returns the best block as identified by the given block hash and height. + pub fn new(block_hash: BlockHash, height: u32) -> Self { + BestBlock { block_hash, height } + } + + /// Returns the best block hash. + pub fn block_hash(&self) -> BlockHash { self.block_hash } + + /// Returns the best block height. + pub fn height(&self) -> u32 { self.height } } /// Whenever we release the `ChannelManager`'s `total_consistency_lock`, from read mode, it is @@ -822,8 +843,7 @@ impl ChannelMana chain_monitor, tx_broadcaster, - latest_block_height: AtomicUsize::new(params.latest_height), - last_block_hash: RwLock::new(params.latest_hash), + best_block: RwLock::new(params.best_block), channel_state: Mutex::new(ChannelHolder{ by_id: HashMap::new(), @@ -851,6 +871,11 @@ impl ChannelMana } } + /// Gets the current configuration applied to all new channels, as + pub fn get_current_default_configuration(&self) -> &UserConfig { + &self.default_configuration + } + /// Creates a new outbound channel to the given remote node and with the given value. /// /// user_id will be provided back as user_channel_id in FundingGenerationReady events to allow @@ -1171,7 +1196,7 @@ impl ChannelMana // HTLC_FAIL_BACK_BUFFER blocks to go. // Also, ensure that, in the case of an unknown payment hash, our payment logic has enough time to fail the HTLC backward // before our onchain logic triggers a channel closure (see HTLC_FAIL_BACK_BUFFER rational). - if (msg.cltv_expiry as u64) <= self.latest_block_height.load(Ordering::Acquire) as u64 + HTLC_FAIL_BACK_BUFFER as u64 + 1 { + if (msg.cltv_expiry as u64) <= self.best_block.read().unwrap().height() as u64 + HTLC_FAIL_BACK_BUFFER as u64 + 1 { return_err!("The final CLTV expiry is too soon to handle", 17, &[0;0]); } // final_incorrect_htlc_amount @@ -1294,7 +1319,7 @@ impl ChannelMana if (msg.cltv_expiry as u64) < (*outgoing_cltv_value) as u64 + chan.get_cltv_expiry_delta() as u64 { // incorrect_cltv_expiry break Some(("Forwarding node has tampered with the intended HTLC values or origin node has an obsolete cltv_expiry_delta", 0x1000 | 13, Some(self.get_channel_update(chan).unwrap()))); } - let cur_height = self.latest_block_height.load(Ordering::Acquire) as u32 + 1; + let cur_height = self.best_block.read().unwrap().height() + 1; // Theoretically, channel counterparty shouldn't send us a HTLC expiring now, but we want to be robust wrt to counterparty // packet sanitization (see HTLC_FAIL_BACK_BUFFER rational) if msg.cltv_expiry <= cur_height + HTLC_FAIL_BACK_BUFFER as u32 { // expiry_too_soon @@ -1511,7 +1536,7 @@ impl ChannelMana return Err(PaymentSendFailure::PathParameterError(path_errs)); } - let cur_height = self.latest_block_height.load(Ordering::Acquire) as u32 + 1; + let cur_height = self.best_block.read().unwrap().height() + 1; let mut results = Vec::new(); for path in route.paths.iter() { results.push(self.send_payment_along_path(&path, &payment_hash, payment_secret, total_value, cur_height)); @@ -1905,10 +1930,7 @@ impl ChannelMana for htlc in htlcs.iter() { let mut htlc_msat_height_data = byte_utils::be64_to_array(htlc.value).to_vec(); htlc_msat_height_data.extend_from_slice( - &byte_utils::be32_to_array( - self.latest_block_height.load(Ordering::Acquire) - as u32, - ), + &byte_utils::be32_to_array(self.best_block.read().unwrap().height()), ); failed_forwards.push((HTLCSource::PreviousHopData(HTLCPreviousHopData { short_channel_id: htlc.prev_hop.short_channel_id, @@ -2028,8 +2050,7 @@ impl ChannelMana if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap()); } let mut htlc_msat_height_data = byte_utils::be64_to_array(htlc.value).to_vec(); htlc_msat_height_data.extend_from_slice(&byte_utils::be32_to_array( - self.latest_block_height.load(Ordering::Acquire) as u32, - )); + self.best_block.read().unwrap().height())); self.fail_htlc_backwards_internal(channel_state.take().unwrap(), HTLCSource::PreviousHopData(htlc.prev_hop), payment_hash, HTLCFailReason::Reason { failure_code: 0x4000 | 15, data: htlc_msat_height_data }); @@ -2243,8 +2264,7 @@ impl ChannelMana if (is_mpp && !valid_mpp) || (!is_mpp && (htlc.value < expected_amount || htlc.value > expected_amount * 2)) { let mut htlc_msat_height_data = byte_utils::be64_to_array(htlc.value).to_vec(); htlc_msat_height_data.extend_from_slice(&byte_utils::be32_to_array( - self.latest_block_height.load(Ordering::Acquire) as u32, - )); + self.best_block.read().unwrap().height())); self.fail_htlc_backwards_internal(channel_state.take().unwrap(), HTLCSource::PreviousHopData(htlc.prev_hop), &payment_hash, HTLCFailReason::Reason { failure_code: 0x4000|15, data: htlc_msat_height_data }); @@ -2529,7 +2549,7 @@ impl ChannelMana fn internal_funding_created(&self, counterparty_node_id: &PublicKey, msg: &msgs::FundingCreated) -> Result<(), MsgHandleErrInternal> { let ((funding_msg, monitor), mut chan) = { - let last_block_hash = *self.last_block_hash.read().unwrap(); + let best_block = *self.best_block.read().unwrap(); let mut channel_lock = self.channel_state.lock().unwrap(); let channel_state = &mut *channel_lock; match channel_state.by_id.entry(msg.temporary_channel_id.clone()) { @@ -2537,7 +2557,7 @@ impl ChannelMana if chan.get().get_counterparty_node_id() != *counterparty_node_id { return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!".to_owned(), msg.temporary_channel_id)); } - (try_chan_entry!(self, chan.get_mut().funding_created(msg, last_block_hash, &self.logger), channel_state, chan), chan.remove()) + (try_chan_entry!(self, chan.get_mut().funding_created(msg, best_block, &self.logger), channel_state, chan), chan.remove()) }, hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.temporary_channel_id)) } @@ -2586,7 +2606,7 @@ impl ChannelMana fn internal_funding_signed(&self, counterparty_node_id: &PublicKey, msg: &msgs::FundingSigned) -> Result<(), MsgHandleErrInternal> { let funding_tx = { - let last_block_hash = *self.last_block_hash.read().unwrap(); + let best_block = *self.best_block.read().unwrap(); let mut channel_lock = self.channel_state.lock().unwrap(); let channel_state = &mut *channel_lock; match channel_state.by_id.entry(msg.channel_id) { @@ -2594,7 +2614,7 @@ impl ChannelMana if chan.get().get_counterparty_node_id() != *counterparty_node_id { return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!".to_owned(), msg.channel_id)); } - let (monitor, funding_tx) = match chan.get_mut().funding_signed(&msg, last_block_hash, &self.logger) { + let (monitor, funding_tx) = match chan.get_mut().funding_signed(&msg, best_block, &self.logger) { Ok(update) => update, Err(e) => try_chan_entry!(self, Err(e), channel_state, chan), }; @@ -3334,26 +3354,32 @@ where L::Target: Logger, { fn block_connected(&self, block: &Block, height: u32) { - assert_eq!(*self.last_block_hash.read().unwrap(), block.header.prev_blockhash, - "Blocks must be connected in chain-order - the connected header must build on the last connected header"); - assert_eq!(self.latest_block_height.load(Ordering::Acquire) as u64, height as u64 - 1, - "Blocks must be connected in chain-order - the connected block height must be one greater than the previous height"); + { + let best_block = self.best_block.read().unwrap(); + assert_eq!(best_block.block_hash(), block.header.prev_blockhash, + "Blocks must be connected in chain-order - the connected header must build on the last connected header"); + assert_eq!(best_block.height(), height - 1, + "Blocks must be connected in chain-order - the connected block height must be one greater than the previous height"); + } + let txdata: Vec<_> = block.txdata.iter().enumerate().collect(); self.transactions_confirmed(&block.header, height, &txdata); self.update_best_block(&block.header, height); } fn block_disconnected(&self, header: &BlockHeader, height: u32) { - assert_eq!(*self.last_block_hash.read().unwrap(), header.block_hash(), - "Blocks must be disconnected in chain-order - the disconnected header must be the last connected header"); - let _persistence_guard = PersistenceNotifierGuard::new(&self.total_consistency_lock, &self.persistence_notifier); - let new_height = self.latest_block_height.fetch_sub(1, Ordering::AcqRel) as u32 - 1; - assert_eq!(new_height, height - 1, - "Blocks must be disconnected in chain-order - the disconnected block must have the correct height"); - *self.last_block_hash.write().unwrap() = header.prev_blockhash; + let new_height = height - 1; + { + let mut best_block = self.best_block.write().unwrap(); + assert_eq!(best_block.block_hash(), header.block_hash(), + "Blocks must be disconnected in chain-order - the disconnected header must be the last connected header"); + assert_eq!(best_block.height(), height, + "Blocks must be disconnected in chain-order - the disconnected block must have the correct height"); + *best_block = BestBlock::new(header.prev_blockhash, new_height) + } - self.do_chain_event(new_height, |channel| channel.update_best_block(new_height, header.time)); + self.do_chain_event(Some(new_height), |channel| channel.update_best_block(new_height, header.time)); } } @@ -3364,8 +3390,11 @@ impl ChannelMana F::Target: FeeEstimator, L::Target: Logger, { + /// Calls a function which handles an on-chain event (blocks dis/connected, transactions + /// un/confirmed, etc) on each channel, handling any resulting errors or messages generated by + /// the function. fn do_chain_event) -> Result<(Option, Vec<(HTLCSource, PaymentHash)>), msgs::ErrorMessage>> - (&self, height: u32, f: FN) { + (&self, height_opt: Option, f: FN) { // Note that we MUST NOT end up calling methods on self.chain_monitor here - we're called // during initialization prior to the chain_monitor being fully configured in some cases. // See the docs for `ChannelManagerReadArgs` for more. @@ -3424,24 +3453,26 @@ impl ChannelMana true }); - channel_state.claimable_htlcs.retain(|&(ref payment_hash, _), htlcs| { - htlcs.retain(|htlc| { - // If height is approaching the number of blocks we think it takes us to get - // our commitment transaction confirmed before the HTLC expires, plus the - // number of blocks we generally consider it to take to do a commitment update, - // just give up on it and fail the HTLC. - if height >= htlc.cltv_expiry - HTLC_FAIL_BACK_BUFFER { - let mut htlc_msat_height_data = byte_utils::be64_to_array(htlc.value).to_vec(); - htlc_msat_height_data.extend_from_slice(&byte_utils::be32_to_array(height)); - timed_out_htlcs.push((HTLCSource::PreviousHopData(htlc.prev_hop.clone()), payment_hash.clone(), HTLCFailReason::Reason { - failure_code: 0x4000 | 15, - data: htlc_msat_height_data - })); - false - } else { true } + if let Some(height) = height_opt { + channel_state.claimable_htlcs.retain(|&(ref payment_hash, _), htlcs| { + htlcs.retain(|htlc| { + // If height is approaching the number of blocks we think it takes us to get + // our commitment transaction confirmed before the HTLC expires, plus the + // number of blocks we generally consider it to take to do a commitment update, + // just give up on it and fail the HTLC. + if height >= htlc.cltv_expiry - HTLC_FAIL_BACK_BUFFER { + let mut htlc_msat_height_data = byte_utils::be64_to_array(htlc.value).to_vec(); + htlc_msat_height_data.extend_from_slice(&byte_utils::be32_to_array(height)); + timed_out_htlcs.push((HTLCSource::PreviousHopData(htlc.prev_hop.clone()), payment_hash.clone(), HTLCFailReason::Reason { + failure_code: 0x4000 | 15, + data: htlc_msat_height_data + })); + false + } else { true } + }); + !htlcs.is_empty() // Only retain this entry if htlcs has at least one entry. }); - !htlcs.is_empty() // Only retain this entry if htlcs has at least one entry. - }); + } } self.handle_init_event_channel_failures(failed_channels); @@ -3477,7 +3508,7 @@ impl ChannelMana log_trace!(self.logger, "{} transactions included in block {} at height {} provided", txdata.len(), block_hash, height); let _persistence_guard = PersistenceNotifierGuard::new(&self.total_consistency_lock, &self.persistence_notifier); - self.do_chain_event(height, |channel| channel.transactions_confirmed(&block_hash, height, txdata, &self.logger).map(|a| (a, Vec::new()))); + self.do_chain_event(Some(height), |channel| channel.transactions_confirmed(&block_hash, height, txdata, &self.logger).map(|a| (a, Vec::new()))); } /// Updates channel state with the current best blockchain tip. You should attempt to call this @@ -3503,10 +3534,9 @@ impl ChannelMana let _persistence_guard = PersistenceNotifierGuard::new(&self.total_consistency_lock, &self.persistence_notifier); - self.latest_block_height.store(height as usize, Ordering::Release); - *self.last_block_hash.write().unwrap() = block_hash; + *self.best_block.write().unwrap() = BestBlock::new(block_hash, height); - self.do_chain_event(height, |channel| channel.update_best_block(height, header.time)); + self.do_chain_event(Some(height), |channel| channel.update_best_block(height, header.time)); loop { // Update last_node_announcement_serial to be the max of its current value and the @@ -3522,6 +3552,61 @@ impl ChannelMana } } + /// Gets the set of txids which should be monitored for their confirmation state. + /// + /// If you're providing information about reorganizations via [`transaction_unconfirmed`], this + /// is the set of transactions which you may need to call [`transaction_unconfirmed`] for. + /// + /// This may be useful to poll to determine the set of transactions which must be registered + /// with an Electrum server or for which an Electrum server needs to be polled to determine + /// transaction confirmation state. + /// + /// This may update after any [`transactions_confirmed`] or [`block_connected`] call. + /// + /// Note that this is NOT the set of transactions which must be included in calls to + /// [`transactions_confirmed`] if they are confirmed, but a small subset of it. + /// + /// [`transactions_confirmed`]: Self::transactions_confirmed + /// [`transaction_unconfirmed`]: Self::transaction_unconfirmed + /// [`block_connected`]: chain::Listen::block_connected + pub fn get_relevant_txids(&self) -> Vec { + let channel_state = self.channel_state.lock().unwrap(); + let mut res = Vec::with_capacity(channel_state.short_to_id.len()); + for chan in channel_state.by_id.values() { + if let Some(funding_txo) = chan.get_funding_txo() { + res.push(funding_txo.txid); + } + } + res + } + + /// Marks a transaction as having been reorganized out of the blockchain. + /// + /// If a transaction is included in [`get_relevant_txids`], and is no longer in the main branch + /// of the blockchain, this function should be called to indicate that the transaction should + /// be considered reorganized out. + /// + /// Once this is called, the given transaction will no longer appear on [`get_relevant_txids`], + /// though this may be called repeatedly for a given transaction without issue. + /// + /// Note that if the transaction is confirmed on the main chain in a different block (indicated + /// via a call to [`transactions_confirmed`]), it may re-appear in [`get_relevant_txids`], thus + /// be very wary of race-conditions wherein the final state of a transaction indicated via + /// these APIs is not the same as its state on the blockchain. + /// + /// [`transactions_confirmed`]: Self::transactions_confirmed + /// [`get_relevant_txids`]: Self::get_relevant_txids + pub fn transaction_unconfirmed(&self, txid: &Txid) { + let _persistence_guard = PersistenceNotifierGuard::new(&self.total_consistency_lock, &self.persistence_notifier); + self.do_chain_event(None, |channel| { + if let Some(funding_txo) = channel.get_funding_txo() { + if funding_txo.txid == *txid { + channel.funding_transaction_unconfirmed().map(|_| (None, Vec::new())) + } else { Ok((None, Vec::new())) } + } else { Ok((None, Vec::new())) } + }); + } + /// Blocks until ChannelManager needs to be persisted or a timeout is reached. It returns a bool /// indicating whether persistence is necessary. Only one listener on /// `await_persistable_update` or `await_persistable_update_timeout` is guaranteed to be woken @@ -4082,8 +4167,11 @@ impl Writeable f writer.write_all(&[MIN_SERIALIZATION_VERSION; 1])?; self.genesis_hash.write(writer)?; - (self.latest_block_height.load(Ordering::Acquire) as u32).write(writer)?; - self.last_block_hash.read().unwrap().write(writer)?; + { + let best_block = self.best_block.read().unwrap(); + best_block.height().write(writer)?; + best_block.block_hash().write(writer)?; + } let channel_state = self.channel_state.lock().unwrap(); let mut unfunded_channels = 0; @@ -4275,8 +4363,8 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> } let genesis_hash: BlockHash = Readable::read(reader)?; - let latest_block_height: u32 = Readable::read(reader)?; - let last_block_hash: BlockHash = Readable::read(reader)?; + let best_block_height: u32 = Readable::read(reader)?; + let best_block_hash: BlockHash = Readable::read(reader)?; let mut failed_htlcs = Vec::new(); @@ -4384,8 +4472,7 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> chain_monitor: args.chain_monitor, tx_broadcaster: args.tx_broadcaster, - latest_block_height: AtomicUsize::new(latest_block_height as usize), - last_block_hash: RwLock::new(last_block_hash), + best_block: RwLock::new(BestBlock::new(best_block_hash, best_block_height)), channel_state: Mutex::new(ChannelHolder { by_id, @@ -4419,7 +4506,7 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> //TODO: Broadcast channel update for closed channels, but only after we've made a //connection or two. - Ok((last_block_hash.clone(), channel_manager)) + Ok((best_block_hash.clone(), channel_manager)) } } @@ -4480,7 +4567,7 @@ pub mod bench { use chain::chainmonitor::ChainMonitor; use chain::channelmonitor::Persist; use chain::keysinterface::{KeysManager, InMemorySigner}; - use ln::channelmanager::{ChainParameters, ChannelManager, PaymentHash, PaymentPreimage}; + use ln::channelmanager::{BestBlock, ChainParameters, ChannelManager, PaymentHash, PaymentPreimage}; use ln::features::InitFeatures; use ln::functional_test_utils::*; use ln::msgs::ChannelMessageHandler; @@ -4532,8 +4619,7 @@ pub mod bench { let keys_manager_a = KeysManager::new(&seed_a, 42, 42); let node_a = ChannelManager::new(&fee_estimator, &chain_monitor_a, &tx_broadcaster, &logger_a, &keys_manager_a, config.clone(), ChainParameters { network, - latest_hash: genesis_hash, - latest_height: 0, + best_block: BestBlock::from_genesis(network), }); let node_a_holder = NodeHolder { node: &node_a }; @@ -4543,8 +4629,7 @@ pub mod bench { let keys_manager_b = KeysManager::new(&seed_b, 42, 42); let node_b = ChannelManager::new(&fee_estimator, &chain_monitor_b, &tx_broadcaster, &logger_b, &keys_manager_b, config.clone(), ChainParameters { network, - latest_hash: genesis_hash, - latest_height: 0, + best_block: BestBlock::from_genesis(network), }); let node_b_holder = NodeHolder { node: &node_b };