#[cfg(any(feature = "_test_utils", test))]
use crate::ln::features::InvoiceFeatures;
use crate::routing::gossip::NetworkGraph;
-use crate::routing::router::{DefaultRouter, InFlightHtlcs, PaymentParameters, Route, RouteHop, RouteParameters, RoutePath, Router};
+use crate::routing::router::{BlindedTail, DefaultRouter, InFlightHtlcs, Path, PaymentParameters, Route, RouteHop, RouteParameters, Router};
use crate::routing::scoring::ProbabilisticScorer;
use crate::ln::msgs;
use crate::ln::onion_utils;
use crate::ln::outbound_payment;
use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment};
use crate::ln::wire::Encode;
-use crate::chain::keysinterface::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider, ChannelSigner, WriteableEcdsaChannelSigner};
+use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider, ChannelSigner, WriteableEcdsaChannelSigner};
use crate::util::config::{UserConfig, ChannelConfig};
use crate::util::wakers::{Future, Notifier};
use crate::util::scid_utils::fake_scid;
use core::cell::RefCell;
use crate::io::Read;
use crate::sync::{Arc, Mutex, RwLock, RwLockReadGuard, FairRwLock, LockTestExt, LockHeldState};
-use core::sync::atomic::{AtomicUsize, Ordering};
+use core::sync::atomic::{AtomicUsize, AtomicBool, Ordering};
use core::time::Duration;
use core::ops::Deref;
// Re-export this for use in the public API.
pub use crate::ln::outbound_payment::{PaymentSendFailure, Retry, RetryableSendFailure, RecipientOnionFields};
+use crate::ln::script::ShutdownScript;
// We hold various information about HTLC relay in the HTLC objects in Channel itself:
//
pub(crate) enum HTLCSource {
PreviousHopData(HTLCPreviousHopData),
OutboundRoute {
- path: Vec<RouteHop>,
+ path: Path,
session_priv: SecretKey,
/// Technically we can recalculate this from the route, but we cache it here to avoid
/// doing a double-pass on route when we get a failure back
#[cfg(test)]
pub fn dummy() -> Self {
HTLCSource::OutboundRoute {
- path: Vec::new(),
+ path: Path { hops: Vec::new(), blinded_tail: None },
session_priv: SecretKey::from_slice(&[1; 32]).unwrap(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([2; 32]),
/// See `ChannelManager` struct-level documentation for lock order requirements.
pending_events: Mutex<Vec<events::Event>>,
+ /// A simple atomic flag to ensure only one task at a time can be processing events asynchronously.
+ pending_events_processor: AtomicBool,
/// See `ChannelManager` struct-level documentation for lock order requirements.
pending_background_events: Mutex<Vec<BackgroundEvent>>,
/// Used when we have to take a BIG lock to make sure everything is self-consistent.
/// Route hints used in constructing invoices for [phantom node payents].
///
-/// [phantom node payments]: crate::chain::keysinterface::PhantomKeysManager
+/// [phantom node payments]: crate::sign::PhantomKeysManager
#[derive(Clone)]
pub struct PhantomRouteHints {
/// The list of channels to be included in the invoice route hints.
macro_rules! process_events_body {
($self: expr, $event_to_handle: expr, $handle_event: expr) => {
- // We'll acquire our total consistency lock until the returned future completes so that
- // we can be sure no other persists happen while processing events.
- let _read_guard = $self.total_consistency_lock.read().unwrap();
+ let mut processed_all_events = false;
+ while !processed_all_events {
+ if $self.pending_events_processor.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed).is_err() {
+ return;
+ }
- let mut result = NotifyOption::SkipPersist;
+ let mut result = NotifyOption::SkipPersist;
- // TODO: This behavior should be documented. It's unintuitive that we query
- // ChannelMonitors when clearing other events.
- if $self.process_pending_monitor_events() {
- result = NotifyOption::DoPersist;
- }
+ {
+ // We'll acquire our total consistency lock so that we can be sure no other
+ // persists happen while processing monitor events.
+ let _read_guard = $self.total_consistency_lock.read().unwrap();
+
+ // TODO: This behavior should be documented. It's unintuitive that we query
+ // ChannelMonitors when clearing other events.
+ if $self.process_pending_monitor_events() {
+ result = NotifyOption::DoPersist;
+ }
+ }
- let pending_events = mem::replace(&mut *$self.pending_events.lock().unwrap(), vec![]);
- if !pending_events.is_empty() {
- result = NotifyOption::DoPersist;
- }
+ let pending_events = $self.pending_events.lock().unwrap().clone();
+ let num_events = pending_events.len();
+ if !pending_events.is_empty() {
+ result = NotifyOption::DoPersist;
+ }
- for event in pending_events {
- $event_to_handle = event;
- $handle_event;
- }
+ for event in pending_events {
+ $event_to_handle = event;
+ $handle_event;
+ }
+
+ {
+ let mut pending_events = $self.pending_events.lock().unwrap();
+ pending_events.drain(..num_events);
+ processed_all_events = pending_events.is_empty();
+ $self.pending_events_processor.store(false, Ordering::Release);
+ }
- if result == NotifyOption::DoPersist {
- $self.persistence_notifier.notify();
+ if result == NotifyOption::DoPersist {
+ $self.persistence_notifier.notify();
+ }
}
}
}
per_peer_state: FairRwLock::new(HashMap::new()),
pending_events: Mutex::new(Vec::new()),
+ pending_events_processor: AtomicBool::new(false),
pending_background_events: Mutex::new(Vec::new()),
total_consistency_lock: RwLock::new(()),
persistence_notifier: Notifier::new(),
/// Raises [`APIError::APIMisuseError`] when `channel_value_satoshis` > 2**24 or `push_msat` is
/// greater than `channel_value_satoshis * 1k` or `channel_value_satoshis < 1000`.
///
+ /// Raises [`APIError::ChannelUnavailable`] if the channel cannot be opened due to failing to
+ /// generate a shutdown scriptpubkey or destination script set by
+ /// [`SignerProvider::get_shutdown_scriptpubkey`] or [`SignerProvider::get_destination_script`].
+ ///
/// Note that we do not check if you are currently connected to the given peer. If no
/// connection is available, the outbound `open_channel` message may fail to send, resulting in
/// the channel eventually being silently forgotten (dropped on reload).
});
}
- fn close_channel_internal(&self, channel_id: &[u8; 32], counterparty_node_id: &PublicKey, target_feerate_sats_per_1000_weight: Option<u32>) -> Result<(), APIError> {
+ fn close_channel_internal(&self, channel_id: &[u8; 32], counterparty_node_id: &PublicKey, target_feerate_sats_per_1000_weight: Option<u32>, override_shutdown_script: Option<ShutdownScript>) -> Result<(), APIError> {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
let mut failed_htlcs: Vec<(HTLCSource, PaymentHash)>;
let funding_txo_opt = chan_entry.get().get_funding_txo();
let their_features = &peer_state.latest_features;
let (shutdown_msg, mut monitor_update_opt, htlcs) = chan_entry.get_mut()
- .get_shutdown(&self.signer_provider, their_features, target_feerate_sats_per_1000_weight)?;
+ .get_shutdown(&self.signer_provider, their_features, target_feerate_sats_per_1000_weight, override_shutdown_script)?;
failed_htlcs = htlcs;
// We can send the `shutdown` message before updating the `ChannelMonitor`
///
/// May generate a [`SendShutdown`] message event on success, which should be relayed.
///
+ /// Raises [`APIError::ChannelUnavailable`] if the channel cannot be closed due to failing to
+ /// generate a shutdown scriptpubkey or destination script set by
+ /// [`SignerProvider::get_shutdown_scriptpubkey`]. A force-closure may be needed to close the
+ /// channel.
+ ///
/// [`ChannelConfig::force_close_avoidance_max_fee_satoshis`]: crate::util::config::ChannelConfig::force_close_avoidance_max_fee_satoshis
/// [`Background`]: crate::chain::chaininterface::ConfirmationTarget::Background
/// [`Normal`]: crate::chain::chaininterface::ConfirmationTarget::Normal
/// [`SendShutdown`]: crate::events::MessageSendEvent::SendShutdown
pub fn close_channel(&self, channel_id: &[u8; 32], counterparty_node_id: &PublicKey) -> Result<(), APIError> {
- self.close_channel_internal(channel_id, counterparty_node_id, None)
+ self.close_channel_internal(channel_id, counterparty_node_id, None, None)
}
/// Begins the process of closing a channel. After this call (plus some timeout), no new HTLCs
/// transaction feerate below `target_feerate_sat_per_1000_weight` (or the feerate which
/// will appear on a force-closure transaction, whichever is lower).
///
+ /// The `shutdown_script` provided will be used as the `scriptPubKey` for the closing transaction.
+ /// Will fail if a shutdown script has already been set for this channel by
+ /// ['ChannelHandshakeConfig::commit_upfront_shutdown_pubkey`]. The given shutdown script must
+ /// also be compatible with our and the counterparty's features.
+ ///
/// May generate a [`SendShutdown`] message event on success, which should be relayed.
///
+ /// Raises [`APIError::ChannelUnavailable`] if the channel cannot be closed due to failing to
+ /// generate a shutdown scriptpubkey or destination script set by
+ /// [`SignerProvider::get_shutdown_scriptpubkey`]. A force-closure may be needed to close the
+ /// channel.
+ ///
/// [`ChannelConfig::force_close_avoidance_max_fee_satoshis`]: crate::util::config::ChannelConfig::force_close_avoidance_max_fee_satoshis
/// [`Background`]: crate::chain::chaininterface::ConfirmationTarget::Background
/// [`Normal`]: crate::chain::chaininterface::ConfirmationTarget::Normal
/// [`SendShutdown`]: crate::events::MessageSendEvent::SendShutdown
- pub fn close_channel_with_target_feerate(&self, channel_id: &[u8; 32], counterparty_node_id: &PublicKey, target_feerate_sats_per_1000_weight: u32) -> Result<(), APIError> {
- self.close_channel_internal(channel_id, counterparty_node_id, Some(target_feerate_sats_per_1000_weight))
+ pub fn close_channel_with_feerate_and_script(&self, channel_id: &[u8; 32], counterparty_node_id: &PublicKey, target_feerate_sats_per_1000_weight: Option<u32>, shutdown_script: Option<ShutdownScript>) -> Result<(), APIError> {
+ self.close_channel_internal(channel_id, counterparty_node_id, target_feerate_sats_per_1000_weight, shutdown_script)
}
#[inline]
}
#[cfg(test)]
- pub(crate) fn test_send_payment_along_path(&self, path: &Vec<RouteHop>, payment_hash: &PaymentHash, recipient_onion: RecipientOnionFields, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option<PaymentPreimage>, session_priv_bytes: [u8; 32]) -> Result<(), APIError> {
+ pub(crate) fn test_send_payment_along_path(&self, path: &Path, payment_hash: &PaymentHash, recipient_onion: RecipientOnionFields, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option<PaymentPreimage>, session_priv_bytes: [u8; 32]) -> Result<(), APIError> {
let _lck = self.total_consistency_lock.read().unwrap();
self.send_payment_along_path(path, payment_hash, recipient_onion, total_value, cur_height, payment_id, keysend_preimage, session_priv_bytes)
}
- fn send_payment_along_path(&self, path: &Vec<RouteHop>, payment_hash: &PaymentHash, recipient_onion: RecipientOnionFields, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option<PaymentPreimage>, session_priv_bytes: [u8; 32]) -> Result<(), APIError> {
+ fn send_payment_along_path(&self, path: &Path, payment_hash: &PaymentHash, recipient_onion: RecipientOnionFields, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option<PaymentPreimage>, session_priv_bytes: [u8; 32]) -> Result<(), APIError> {
// The top-level caller should hold the total_consistency_lock read lock.
debug_assert!(self.total_consistency_lock.try_write().is_err());
- log_trace!(self.logger, "Attempting to send payment for path with next hop {}", path.first().unwrap().short_channel_id);
+ log_trace!(self.logger, "Attempting to send payment for path with next hop {}", path.hops.first().unwrap().short_channel_id);
let prng_seed = self.entropy_source.get_secure_random_bytes();
let session_priv = SecretKey::from_slice(&session_priv_bytes[..]).expect("RNG is busted");
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, prng_seed, payment_hash);
let err: Result<(), _> = loop {
- let (counterparty_node_id, id) = match self.short_to_chan_info.read().unwrap().get(&path.first().unwrap().short_channel_id) {
+ let (counterparty_node_id, id) = match self.short_to_chan_info.read().unwrap().get(&path.hops.first().unwrap().short_channel_id) {
None => return Err(APIError::ChannelUnavailable{err: "No channel available with first hop!".to_owned()}),
Some((cp_id, chan_id)) => (cp_id.clone(), chan_id.clone()),
};
return Ok(());
};
- match handle_error!(self, err, path.first().unwrap().pubkey) {
+ match handle_error!(self, err, path.hops.first().unwrap().pubkey) {
Ok(_) => unreachable!(),
Err(e) => {
Err(APIError::ChannelUnavailable { err: e.err })
/// Send a payment that is probing the given route for liquidity. We calculate the
/// [`PaymentHash`] of probes based on a static secret and a random [`PaymentId`], which allows
/// us to easily discern them from real payments.
- pub fn send_probe(&self, hops: Vec<RouteHop>) -> Result<(PaymentHash, PaymentId), PaymentSendFailure> {
+ pub fn send_probe(&self, path: Path) -> Result<(PaymentHash, PaymentId), PaymentSendFailure> {
let best_block_height = self.best_block.read().unwrap().height();
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
- self.pending_outbound_payments.send_probe(hops, self.probing_cookie_secret, &self.entropy_source, &self.node_signer, best_block_height,
+ self.pending_outbound_payments.send_probe(path, self.probing_cookie_secret, &self.entropy_source, &self.node_signer, best_block_height,
|path, payment_hash, recipient_onion, total_value, cur_height, payment_id, keysend_preimage, session_priv|
self.send_payment_along_path(path, payment_hash, recipient_onion, total_value, cur_height, payment_id, keysend_preimage, session_priv))
}
if let Some(tx) = funding_broadcastable {
log_info!(self.logger, "Broadcasting funding transaction with txid {}", tx.txid());
- self.tx_broadcaster.broadcast_transaction(&tx);
+ self.tx_broadcaster.broadcast_transactions(&[&tx]);
}
{
};
if let Some(broadcast_tx) = tx {
log_info!(self.logger, "Broadcasting {}", log_tx!(broadcast_tx));
- self.tx_broadcaster.broadcast_transaction(&broadcast_tx);
+ self.tx_broadcaster.broadcast_transactions(&[&broadcast_tx]);
}
if let Some(chan) = chan_option {
if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
self.issue_channel_close_events(chan, ClosureReason::CooperativeClosure);
log_info!(self.logger, "Broadcasting {}", log_tx!(tx));
- self.tx_broadcaster.broadcast_transaction(&tx);
+ self.tx_broadcaster.broadcast_transactions(&[&tx]);
update_maps_on_chan_removal!(self, chan);
false
} else { true }
/// Gets a fake short channel id for use in receiving [phantom node payments]. These fake scids
/// are used when constructing the phantom invoice's route hints.
///
- /// [phantom node payments]: crate::chain::keysinterface::PhantomKeysManager
+ /// [phantom node payments]: crate::sign::PhantomKeysManager
pub fn get_phantom_scid(&self) -> u64 {
let best_block_height = self.best_block.read().unwrap().height();
let short_to_chan_info = self.short_to_chan_info.read().unwrap();
/// Gets route hints for use in receiving [phantom node payments].
///
- /// [phantom node payments]: crate::chain::keysinterface::PhantomKeysManager
+ /// [phantom node payments]: crate::sign::PhantomKeysManager
pub fn get_phantom_route_hints(&self) -> PhantomRouteHints {
PhantomRouteHints {
channels: self.list_usable_channels(),
// should also add the corresponding (optional) bit to the [`ChannelMessageHandler`] impl for
// [`ErroringMessageHandler`].
let mut features = InitFeatures::empty();
- features.set_data_loss_protect_optional();
+ features.set_data_loss_protect_required();
features.set_upfront_shutdown_script_optional();
features.set_variable_length_onion_required();
features.set_static_remote_key_required();
0 => {
let mut session_priv: crate::util::ser::RequiredWrapper<SecretKey> = crate::util::ser::RequiredWrapper(None);
let mut first_hop_htlc_msat: u64 = 0;
- let mut path: Option<Vec<RouteHop>> = Some(Vec::new());
+ let mut path_hops: Option<Vec<RouteHop>> = Some(Vec::new());
let mut payment_id = None;
let mut payment_params: Option<PaymentParameters> = None;
+ let mut blinded_tail: Option<BlindedTail> = None;
read_tlv_fields!(reader, {
(0, session_priv, required),
(1, payment_id, option),
(2, first_hop_htlc_msat, required),
- (4, path, vec_type),
+ (4, path_hops, vec_type),
(5, payment_params, (option: ReadableArgs, 0)),
+ (6, blinded_tail, option),
});
if payment_id.is_none() {
// For backwards compat, if there was no payment_id written, use the session_priv bytes
// instead.
payment_id = Some(PaymentId(*session_priv.0.unwrap().as_ref()));
}
- if path.is_none() || path.as_ref().unwrap().is_empty() {
+ let path = Path { hops: path_hops.ok_or(DecodeError::InvalidValue)?, blinded_tail };
+ if path.hops.len() == 0 {
return Err(DecodeError::InvalidValue);
}
- let path = path.unwrap();
if let Some(params) = payment_params.as_mut() {
if params.final_cltv_expiry_delta == 0 {
- params.final_cltv_expiry_delta = path.last().unwrap().cltv_expiry_delta;
+ params.final_cltv_expiry_delta = path.final_cltv_expiry_delta().ok_or(DecodeError::InvalidValue)?;
}
}
Ok(HTLCSource::OutboundRoute {
(1, payment_id_opt, option),
(2, first_hop_htlc_msat, required),
// 3 was previously used to write a PaymentSecret for the payment.
- (4, *path, vec_type),
+ (4, path.hops, vec_type),
(5, None::<PaymentParameters>, option), // payment_params in LDK versions prior to 0.0.115
+ (6, path.blinded_tail, option),
});
}
HTLCSource::PreviousHopData(ref field) => {
if id_to_peer.get(&monitor.get_funding_txo().0.to_channel_id()).is_none() {
for (htlc_source, (htlc, _)) in monitor.get_pending_or_resolved_outbound_htlcs() {
if let HTLCSource::OutboundRoute { payment_id, session_priv, path, .. } = htlc_source {
- if path.is_empty() {
+ if path.hops.is_empty() {
log_error!(args.logger, "Got an empty path for a pending payment");
return Err(DecodeError::InvalidValue);
}
- let path_amt = path.last().unwrap().fee_msat;
+ let path_amt = path.final_value_msat();
let mut session_priv_bytes = [0; 32];
session_priv_bytes[..].copy_from_slice(&session_priv[..]);
match pending_outbounds.pending_outbound_payments.lock().unwrap().entry(payment_id) {
if newly_added { "Added" } else { "Had" }, path_amt, log_bytes!(session_priv_bytes), log_bytes!(htlc.payment_hash.0));
},
hash_map::Entry::Vacant(entry) => {
- let path_fee = path.get_path_fees();
+ let path_fee = path.fee_msat();
entry.insert(PendingOutboundPayment::Retryable {
retry_strategy: None,
attempts: PaymentAttempts::new(),
per_peer_state: FairRwLock::new(per_peer_state),
pending_events: Mutex::new(pending_events_read),
+ pending_events_processor: AtomicBool::new(false),
pending_background_events: Mutex::new(pending_background_events),
total_consistency_lock: RwLock::new(()),
persistence_notifier: Notifier::new(),
use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
- #[cfg(feature = "std")]
- use core::time::Duration;
use core::sync::atomic::Ordering;
use crate::events::{Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, ClosureReason};
use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
use crate::util::errors::APIError;
use crate::util::test_utils;
use crate::util::config::ChannelConfig;
- use crate::chain::keysinterface::EntropySource;
+ use crate::sign::EntropySource;
#[test]
fn test_notify_limits() {
let (mut route, payment_hash, _, _) = get_route_and_payment_hash!(&nodes[0], nodes[3], 100000);
let path = route.paths[0].clone();
route.paths.push(path);
- route.paths[0][0].pubkey = nodes[1].node.get_our_node_id();
- route.paths[0][0].short_channel_id = chan_1_id;
- route.paths[0][1].short_channel_id = chan_3_id;
- route.paths[1][0].pubkey = nodes[2].node.get_our_node_id();
- route.paths[1][0].short_channel_id = chan_2_id;
- route.paths[1][1].short_channel_id = chan_4_id;
+ route.paths[0].hops[0].pubkey = nodes[1].node.get_our_node_id();
+ route.paths[0].hops[0].short_channel_id = chan_1_id;
+ route.paths[0].hops[1].short_channel_id = chan_3_id;
+ route.paths[1].hops[0].pubkey = nodes[2].node.get_our_node_id();
+ route.paths[1].hops[0].short_channel_id = chan_2_id;
+ route.paths[1].hops[1].short_channel_id = chan_4_id;
match nodes[0].node.send_payment_with_route(&route, payment_hash,
RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0))
pub mod bench {
use crate::chain::Listen;
use crate::chain::chainmonitor::{ChainMonitor, Persist};
- use crate::chain::keysinterface::{KeysManager, InMemorySigner};
+ use crate::sign::{KeysManager, InMemorySigner};
use crate::events::{Event, MessageSendEvent, MessageSendEventsProvider};
use crate::ln::channelmanager::{BestBlock, ChainParameters, ChannelManager, PaymentHash, PaymentPreimage, PaymentId, RecipientOnionFields, Retry};
use crate::ln::functional_test_utils::*;