Give peers which are sending us messages/receiving messages from us longer to respond to ping
fee_msat: amt,
cltv_expiry_delta: 200,
}]],
+ payee: None,
}, payment_hash, &Some(payment_secret)) {
check_payment_err(err);
false
fee_msat: amt,
cltv_expiry_delta: 200,
}]],
+ payee: None,
}, payment_hash, &Some(payment_secret)) {
check_payment_err(err);
false
0x08 => {
if let Some((id, _)) = monitor_a.latest_monitors.lock().unwrap().get(&chan_1_funding) {
- nodes[0].channel_monitor_updated(&chan_1_funding, *id);
+ monitor_a.chain_monitor.force_channel_monitor_updated(chan_1_funding, *id);
+ nodes[0].process_monitor_events();
}
},
0x09 => {
if let Some((id, _)) = monitor_b.latest_monitors.lock().unwrap().get(&chan_1_funding) {
- nodes[1].channel_monitor_updated(&chan_1_funding, *id);
+ monitor_b.chain_monitor.force_channel_monitor_updated(chan_1_funding, *id);
+ nodes[1].process_monitor_events();
}
},
0x0a => {
if let Some((id, _)) = monitor_b.latest_monitors.lock().unwrap().get(&chan_2_funding) {
- nodes[1].channel_monitor_updated(&chan_2_funding, *id);
+ monitor_b.chain_monitor.force_channel_monitor_updated(chan_2_funding, *id);
+ nodes[1].process_monitor_events();
}
},
0x0b => {
if let Some((id, _)) = monitor_c.latest_monitors.lock().unwrap().get(&chan_2_funding) {
- nodes[2].channel_monitor_updated(&chan_2_funding, *id);
+ monitor_c.chain_monitor.force_channel_monitor_updated(chan_2_funding, *id);
+ nodes[2].process_monitor_events();
}
},
// Test that no channel is in a stuck state where neither party can send funds even
// after we resolve all pending events.
// First make sure there are no pending monitor updates, resetting the error state
- // and calling channel_monitor_updated for each monitor.
+ // and calling force_channel_monitor_updated for each monitor.
*monitor_a.persister.update_ret.lock().unwrap() = Ok(());
*monitor_b.persister.update_ret.lock().unwrap() = Ok(());
*monitor_c.persister.update_ret.lock().unwrap() = Ok(());
if let Some((id, _)) = monitor_a.latest_monitors.lock().unwrap().get(&chan_1_funding) {
- nodes[0].channel_monitor_updated(&chan_1_funding, *id);
+ monitor_a.chain_monitor.force_channel_monitor_updated(chan_1_funding, *id);
+ nodes[0].process_monitor_events();
}
if let Some((id, _)) = monitor_b.latest_monitors.lock().unwrap().get(&chan_1_funding) {
- nodes[1].channel_monitor_updated(&chan_1_funding, *id);
+ monitor_b.chain_monitor.force_channel_monitor_updated(chan_1_funding, *id);
+ nodes[1].process_monitor_events();
}
if let Some((id, _)) = monitor_b.latest_monitors.lock().unwrap().get(&chan_2_funding) {
- nodes[1].channel_monitor_updated(&chan_2_funding, *id);
+ monitor_b.chain_monitor.force_channel_monitor_updated(chan_2_funding, *id);
+ nodes[1].process_monitor_events();
}
if let Some((id, _)) = monitor_c.latest_monitors.lock().unwrap().get(&chan_2_funding) {
- nodes[2].channel_monitor_updated(&chan_2_funding, *id);
+ monitor_c.chain_monitor.force_channel_monitor_updated(chan_2_funding, *id);
+ nodes[2].process_monitor_events();
}
// Next, make sure peers are all connected to each other
use lightning::ln::msgs::DecodeError;
use lightning::ln::script::ShutdownScript;
use lightning::routing::network_graph::{NetGraphMsgHandler, NetworkGraph};
-use lightning::routing::router::get_route;
+use lightning::routing::router::{find_route, Payee, RouteParameters};
use lightning::routing::scorer::Scorer;
use lightning::util::config::UserConfig;
use lightning::util::errors::APIError;
}
},
4 => {
- let value = slice_to_be24(get_slice!(3)) as u64;
- let route = match get_route(&our_id, &net_graph_msg_handler.network_graph, &get_pubkey!(), None, None, &Vec::new(), value, 42, Arc::clone(&logger), &scorer) {
+ let final_value_msat = slice_to_be24(get_slice!(3)) as u64;
+ let payee = Payee::new(get_pubkey!());
+ let params = RouteParameters {
+ payee,
+ final_value_msat,
+ final_cltv_expiry_delta: 42,
+ };
+ let route = match find_route(&our_id, ¶ms, &net_graph_msg_handler.network_graph, None, Arc::clone(&logger), &scorer) {
Ok(route) => route,
Err(_) => return,
};
}
},
15 => {
- let value = slice_to_be24(get_slice!(3)) as u64;
- let mut route = match get_route(&our_id, &net_graph_msg_handler.network_graph, &get_pubkey!(), None, None, &Vec::new(), value, 42, Arc::clone(&logger), &scorer) {
+ let final_value_msat = slice_to_be24(get_slice!(3)) as u64;
+ let payee = Payee::new(get_pubkey!());
+ let params = RouteParameters {
+ payee,
+ final_value_msat,
+ final_cltv_expiry_delta: 42,
+ };
+ let mut route = match find_route(&our_id, ¶ms, &net_graph_msg_handler.network_graph, None, Arc::clone(&logger), &scorer) {
Ok(route) => route,
Err(_) => return,
};
use lightning::ln::channelmanager::{ChannelDetails, ChannelCounterparty};
use lightning::ln::features::InitFeatures;
use lightning::ln::msgs;
-use lightning::routing::router::{get_route, RouteHint, RouteHintHop};
+use lightning::routing::router::{find_route, Payee, RouteHint, RouteHintHop, RouteParameters};
use lightning::routing::scorer::Scorer;
use lightning::util::logger::Logger;
use lightning::util::ser::Readable;
}
let scorer = Scorer::new(0);
for target in node_pks.iter() {
- let _ = get_route(&our_pubkey, &net_graph, target, None,
+ let params = RouteParameters {
+ payee: Payee::new(*target).with_route_hints(last_hops.clone()),
+ final_value_msat: slice_to_be64(get_slice!(8)),
+ final_cltv_expiry_delta: slice_to_be32(get_slice!(4)),
+ };
+ let _ = find_route(&our_pubkey, ¶ms, &net_graph,
first_hops.map(|c| c.iter().collect::<Vec<_>>()).as_ref().map(|a| a.as_slice()),
- &last_hops.iter().collect::<Vec<_>>(),
- slice_to_be64(get_slice!(8)), slice_to_be32(get_slice!(4)), Arc::clone(&logger), &scorer);
+ Arc::clone(&logger), &scorer);
}
},
}
use lightning::chain;
use lightning::chain::{chainmonitor, channelmonitor};
+use lightning::chain::chainmonitor::MonitorUpdateId;
use lightning::chain::transaction::OutPoint;
use lightning::util::enforcing_trait_impls::EnforcingSigner;
pub update_ret: Mutex<Result<(), chain::ChannelMonitorUpdateErr>>,
}
impl chainmonitor::Persist<EnforcingSigner> for TestPersister {
- fn persist_new_channel(&self, _funding_txo: OutPoint, _data: &channelmonitor::ChannelMonitor<EnforcingSigner>) -> Result<(), chain::ChannelMonitorUpdateErr> {
+ fn persist_new_channel(&self, _funding_txo: OutPoint, _data: &channelmonitor::ChannelMonitor<EnforcingSigner>, _update_id: MonitorUpdateId) -> Result<(), chain::ChannelMonitorUpdateErr> {
self.update_ret.lock().unwrap().clone()
}
- fn update_persisted_channel(&self, _funding_txo: OutPoint, _update: &channelmonitor::ChannelMonitorUpdate, _data: &channelmonitor::ChannelMonitor<EnforcingSigner>) -> Result<(), chain::ChannelMonitorUpdateErr> {
+ fn update_persisted_channel(&self, _funding_txo: OutPoint, _update: &Option<channelmonitor::ChannelMonitorUpdate>, _data: &channelmonitor::ChannelMonitor<EnforcingSigner>, _update_id: MonitorUpdateId) -> Result<(), chain::ChannelMonitorUpdateErr> {
self.update_ret.lock().unwrap().clone()
}
}
//! * For parsing use `str::parse::<Invoice>(&self)` (see the docs of `impl FromStr for Invoice`)
//! * For constructing invoices use the `InvoiceBuilder`
//! * For serializing invoices use the `Display`/`ToString` traits
+pub mod payment;
pub mod utils;
extern crate bech32;
extern crate bitcoin_hashes;
-extern crate lightning;
+#[macro_use] extern crate lightning;
extern crate num_traits;
extern crate secp256k1;
.unwrap_or(Duration::from_secs(DEFAULT_EXPIRY_TIME))
}
+ /// Returns whether the invoice has expired.
+ pub fn is_expired(&self) -> bool {
+ Self::is_expired_from_epoch(self.timestamp(), self.expiry_time())
+ }
+
+ /// Returns whether the expiry time from the given epoch has passed.
+ pub(crate) fn is_expired_from_epoch(epoch: &SystemTime, expiry_time: Duration) -> bool {
+ match epoch.elapsed() {
+ Ok(elapsed) => elapsed > expiry_time,
+ Err(_) => false,
+ }
+ }
+
/// Returns the invoice's `min_final_cltv_expiry` time, if present, otherwise
/// [`DEFAULT_MIN_FINAL_CLTV_EXPIRY`].
pub fn min_final_cltv_expiry(&self) -> u64 {
}
/// Returns a list of all routes included in the invoice as the underlying hints
- pub fn route_hints(&self) -> Vec<&RouteHint> {
+ pub fn route_hints(&self) -> Vec<RouteHint> {
find_all_extract!(
self.signed_invoice.known_tagged_fields(), TaggedField::PrivateRoute(ref x), x
- ).map(|route| &**route).collect()
+ ).map(|route| (**route).clone()).collect()
}
/// Returns the currency for which the invoice was issued
self.signed_invoice.currency()
}
+ /// Returns the amount if specified in the invoice as millisatoshis.
+ pub fn amount_milli_satoshis(&self) -> Option<u64> {
+ self.signed_invoice.amount_pico_btc().map(|v| v / 10)
+ }
+
/// Returns the amount if specified in the invoice as pico <currency>.
- pub fn amount_pico_btc(&self) -> Option<u64> {
+ fn amount_pico_btc(&self) -> Option<u64> {
self.signed_invoice.amount_pico_btc()
}
}
assert!(invoice.check_signature().is_ok());
assert_eq!(invoice.tagged_fields().count(), 10);
+ assert_eq!(invoice.amount_milli_satoshis(), Some(123));
assert_eq!(invoice.amount_pico_btc(), Some(1230));
assert_eq!(invoice.currency(), Currency::BitcoinTestnet);
assert_eq!(
assert_eq!(invoice.min_final_cltv_expiry(), DEFAULT_MIN_FINAL_CLTV_EXPIRY);
assert_eq!(invoice.expiry_time(), Duration::from_secs(DEFAULT_EXPIRY_TIME));
+ assert!(!invoice.is_expired());
+ }
+
+ #[test]
+ fn test_expiration() {
+ use ::*;
+ use secp256k1::Secp256k1;
+ use secp256k1::key::SecretKey;
+
+ let timestamp = SystemTime::now()
+ .checked_sub(Duration::from_secs(DEFAULT_EXPIRY_TIME * 2))
+ .unwrap();
+ let signed_invoice = InvoiceBuilder::new(Currency::Bitcoin)
+ .description("Test".into())
+ .payment_hash(sha256::Hash::from_slice(&[0;32][..]).unwrap())
+ .payment_secret(PaymentSecret([0; 32]))
+ .timestamp(timestamp)
+ .build_raw()
+ .unwrap()
+ .sign::<_, ()>(|hash| {
+ let privkey = SecretKey::from_slice(&[41; 32]).unwrap();
+ let secp_ctx = Secp256k1::new();
+ Ok(secp_ctx.sign_recoverable(hash, &privkey))
+ })
+ .unwrap();
+ let invoice = Invoice::from_signed(signed_invoice).unwrap();
+
+ assert!(invoice.is_expired());
}
}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+//! A module for paying Lightning invoices.
+//!
+//! Defines an [`InvoicePayer`] utility for paying invoices, parameterized by [`Payer`] and
+//! [`Router`] traits. Implementations of [`Payer`] provide the payer's node id, channels, and means
+//! to send a payment over a [`Route`]. Implementations of [`Router`] find a [`Route`] between payer
+//! and payee using information provided by the payer and from the payee's [`Invoice`].
+//!
+//! [`InvoicePayer`] is capable of retrying failed payments. It accomplishes this by implementing
+//! [`EventHandler`] which decorates a user-provided handler. It will intercept any
+//! [`Event::PaymentPathFailed`] events and retry the failed paths for a fixed number of total
+//! attempts or until retry is no longer possible. In such a situation, [`InvoicePayer`] will pass
+//! along the events to the user-provided handler.
+//!
+//! # Example
+//!
+//! ```
+//! # extern crate lightning;
+//! # extern crate lightning_invoice;
+//! # extern crate secp256k1;
+//! #
+//! # use lightning::ln::{PaymentHash, PaymentSecret};
+//! # use lightning::ln::channelmanager::{ChannelDetails, PaymentId, PaymentSendFailure};
+//! # use lightning::ln::msgs::LightningError;
+//! # use lightning::routing::router::{Route, RouteParameters};
+//! # use lightning::util::events::{Event, EventHandler, EventsProvider};
+//! # use lightning::util::logger::{Logger, Record};
+//! # use lightning_invoice::Invoice;
+//! # use lightning_invoice::payment::{InvoicePayer, Payer, RetryAttempts, Router};
+//! # use secp256k1::key::PublicKey;
+//! # use std::ops::Deref;
+//! #
+//! # struct FakeEventProvider {}
+//! # impl EventsProvider for FakeEventProvider {
+//! # fn process_pending_events<H: Deref>(&self, handler: H) where H::Target: EventHandler {}
+//! # }
+//! #
+//! # struct FakePayer {}
+//! # impl Payer for FakePayer {
+//! # fn node_id(&self) -> PublicKey { unimplemented!() }
+//! # fn first_hops(&self) -> Vec<ChannelDetails> { unimplemented!() }
+//! # fn send_payment(
+//! # &self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>
+//! # ) -> Result<PaymentId, PaymentSendFailure> { unimplemented!() }
+//! # fn retry_payment(
+//! # &self, route: &Route, payment_id: PaymentId
+//! # ) -> Result<(), PaymentSendFailure> { unimplemented!() }
+//! # }
+//! #
+//! # struct FakeRouter {};
+//! # impl Router for FakeRouter {
+//! # fn find_route(
+//! # &self, payer: &PublicKey, params: &RouteParameters,
+//! # first_hops: Option<&[&ChannelDetails]>
+//! # ) -> Result<Route, LightningError> { unimplemented!() }
+//! # }
+//! #
+//! # struct FakeLogger {};
+//! # impl Logger for FakeLogger {
+//! # fn log(&self, record: &Record) { unimplemented!() }
+//! # }
+//! #
+//! # fn main() {
+//! let event_handler = |event: &Event| {
+//! match event {
+//! Event::PaymentPathFailed { .. } => println!("payment failed after retries"),
+//! Event::PaymentSent { .. } => println!("payment successful"),
+//! _ => {},
+//! }
+//! };
+//! # let payer = FakePayer {};
+//! # let router = FakeRouter {};
+//! # let logger = FakeLogger {};
+//! let invoice_payer = InvoicePayer::new(&payer, router, &logger, event_handler, RetryAttempts(2));
+//!
+//! let invoice = "...";
+//! let invoice = invoice.parse::<Invoice>().unwrap();
+//! invoice_payer.pay_invoice(&invoice).unwrap();
+//!
+//! # let event_provider = FakeEventProvider {};
+//! loop {
+//! event_provider.process_pending_events(&invoice_payer);
+//! }
+//! # }
+//! ```
+//!
+//! # Note
+//!
+//! The [`Route`] is computed before each payment attempt. Any updates affecting path finding such
+//! as updates to the network graph or changes to channel scores should be applied prior to
+//! retries, typically by way of composing [`EventHandler`]s accordingly.
+
+use crate::Invoice;
+
+use bitcoin_hashes::Hash;
+
+use lightning::ln::{PaymentHash, PaymentSecret};
+use lightning::ln::channelmanager::{ChannelDetails, PaymentId, PaymentSendFailure};
+use lightning::ln::msgs::LightningError;
+use lightning::routing::router::{Payee, Route, RouteParameters};
+use lightning::util::events::{Event, EventHandler};
+use lightning::util::logger::Logger;
+
+use secp256k1::key::PublicKey;
+
+use std::collections::hash_map::{self, HashMap};
+use std::ops::Deref;
+use std::sync::Mutex;
+use std::time::{Duration, SystemTime};
+
+/// A utility for paying [`Invoice]`s.
+pub struct InvoicePayer<P: Deref, R, L: Deref, E>
+where
+ P::Target: Payer,
+ R: Router,
+ L::Target: Logger,
+ E: EventHandler,
+{
+ payer: P,
+ router: R,
+ logger: L,
+ event_handler: E,
+ payment_cache: Mutex<HashMap<PaymentHash, usize>>,
+ retry_attempts: RetryAttempts,
+}
+
+/// A trait defining behavior of an [`Invoice`] payer.
+pub trait Payer {
+ /// Returns the payer's node id.
+ fn node_id(&self) -> PublicKey;
+
+ /// Returns the payer's channels.
+ fn first_hops(&self) -> Vec<ChannelDetails>;
+
+ /// Sends a payment over the Lightning Network using the given [`Route`].
+ fn send_payment(
+ &self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>
+ ) -> Result<PaymentId, PaymentSendFailure>;
+
+ /// Retries a failed payment path for the [`PaymentId`] using the given [`Route`].
+ fn retry_payment(&self, route: &Route, payment_id: PaymentId) -> Result<(), PaymentSendFailure>;
+}
+
+/// A trait defining behavior for routing an [`Invoice`] payment.
+pub trait Router {
+ /// Finds a [`Route`] between `payer` and `payee` for a payment with the given values.
+ fn find_route(
+ &self, payer: &PublicKey, params: &RouteParameters, first_hops: Option<&[&ChannelDetails]>
+ ) -> Result<Route, LightningError>;
+}
+
+/// Number of attempts to retry payment path failures for an [`Invoice`].
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct RetryAttempts(pub usize);
+
+/// An error that may occur when making a payment.
+#[derive(Clone, Debug)]
+pub enum PaymentError {
+ /// An error resulting from the provided [`Invoice`] or payment hash.
+ Invoice(&'static str),
+ /// An error occurring when finding a route.
+ Routing(LightningError),
+ /// An error occurring when sending a payment.
+ Sending(PaymentSendFailure),
+}
+
+impl<P: Deref, R, L: Deref, E> InvoicePayer<P, R, L, E>
+where
+ P::Target: Payer,
+ R: Router,
+ L::Target: Logger,
+ E: EventHandler,
+{
+ /// Creates an invoice payer that retries failed payment paths.
+ ///
+ /// Will forward any [`Event::PaymentPathFailed`] events to the decorated `event_handler` once
+ /// `retry_attempts` has been exceeded for a given [`Invoice`].
+ pub fn new(
+ payer: P, router: R, logger: L, event_handler: E, retry_attempts: RetryAttempts
+ ) -> Self {
+ Self {
+ payer,
+ router,
+ logger,
+ event_handler,
+ payment_cache: Mutex::new(HashMap::new()),
+ retry_attempts,
+ }
+ }
+
+ /// Pays the given [`Invoice`], caching it for later use in case a retry is needed.
+ pub fn pay_invoice(&self, invoice: &Invoice) -> Result<PaymentId, PaymentError> {
+ if invoice.amount_milli_satoshis().is_none() {
+ Err(PaymentError::Invoice("amount missing"))
+ } else {
+ self.pay_invoice_internal(invoice, None)
+ }
+ }
+
+ /// Pays the given zero-value [`Invoice`] using the given amount, caching it for later use in
+ /// case a retry is needed.
+ pub fn pay_zero_value_invoice(
+ &self, invoice: &Invoice, amount_msats: u64
+ ) -> Result<PaymentId, PaymentError> {
+ if invoice.amount_milli_satoshis().is_some() {
+ Err(PaymentError::Invoice("amount unexpected"))
+ } else {
+ self.pay_invoice_internal(invoice, Some(amount_msats))
+ }
+ }
+
+ fn pay_invoice_internal(
+ &self, invoice: &Invoice, amount_msats: Option<u64>
+ ) -> Result<PaymentId, PaymentError> {
+ debug_assert!(invoice.amount_milli_satoshis().is_some() ^ amount_msats.is_some());
+ let payment_hash = PaymentHash(invoice.payment_hash().clone().into_inner());
+ let mut payment_cache = self.payment_cache.lock().unwrap();
+ match payment_cache.entry(payment_hash) {
+ hash_map::Entry::Vacant(entry) => {
+ let payer = self.payer.node_id();
+ let mut payee = Payee::new(invoice.recover_payee_pub_key())
+ .with_expiry_time(expiry_time_from_unix_epoch(&invoice).as_secs())
+ .with_route_hints(invoice.route_hints());
+ if let Some(features) = invoice.features() {
+ payee = payee.with_features(features.clone());
+ }
+ let params = RouteParameters {
+ payee,
+ final_value_msat: invoice.amount_milli_satoshis().or(amount_msats).unwrap(),
+ final_cltv_expiry_delta: invoice.min_final_cltv_expiry() as u32,
+ };
+ let first_hops = self.payer.first_hops();
+ let route = self.router.find_route(
+ &payer,
+ ¶ms,
+ Some(&first_hops.iter().collect::<Vec<_>>()),
+ ).map_err(|e| PaymentError::Routing(e))?;
+
+ let payment_hash = PaymentHash(invoice.payment_hash().clone().into_inner());
+ let payment_secret = Some(invoice.payment_secret().clone());
+ let payment_id = self.payer.send_payment(&route, payment_hash, &payment_secret)
+ .map_err(|e| PaymentError::Sending(e))?;
+ entry.insert(0);
+ Ok(payment_id)
+ },
+ hash_map::Entry::Occupied(_) => Err(PaymentError::Invoice("payment pending")),
+ }
+ }
+
+ fn retry_payment(
+ &self, payment_id: PaymentId, params: &RouteParameters
+ ) -> Result<(), PaymentError> {
+ let payer = self.payer.node_id();
+ let first_hops = self.payer.first_hops();
+ let route = self.router.find_route(
+ &payer, ¶ms, Some(&first_hops.iter().collect::<Vec<_>>())
+ ).map_err(|e| PaymentError::Routing(e))?;
+ self.payer.retry_payment(&route, payment_id).map_err(|e| PaymentError::Sending(e))
+ }
+
+ /// Removes the payment cached by the given payment hash.
+ ///
+ /// Should be called once a payment has failed or succeeded if not using [`InvoicePayer`] as an
+ /// [`EventHandler`]. Otherwise, calling this method is unnecessary.
+ pub fn remove_cached_payment(&self, payment_hash: &PaymentHash) {
+ self.payment_cache.lock().unwrap().remove(payment_hash);
+ }
+}
+
+fn expiry_time_from_unix_epoch(invoice: &Invoice) -> Duration {
+ invoice.timestamp().duration_since(SystemTime::UNIX_EPOCH).unwrap() + invoice.expiry_time()
+}
+
+fn has_expired(params: &RouteParameters) -> bool {
+ let expiry_time = Duration::from_secs(params.payee.expiry_time.unwrap());
+ Invoice::is_expired_from_epoch(&SystemTime::UNIX_EPOCH, expiry_time)
+}
+
+impl<P: Deref, R, L: Deref, E> EventHandler for InvoicePayer<P, R, L, E>
+where
+ P::Target: Payer,
+ R: Router,
+ L::Target: Logger,
+ E: EventHandler,
+{
+ fn handle_event(&self, event: &Event) {
+ match event {
+ Event::PaymentPathFailed { payment_id, payment_hash, rejected_by_dest, retry, .. } => {
+ let mut payment_cache = self.payment_cache.lock().unwrap();
+ let entry = loop {
+ let entry = payment_cache.entry(*payment_hash);
+ match entry {
+ hash_map::Entry::Occupied(_) => break entry,
+ hash_map::Entry::Vacant(entry) => entry.insert(0),
+ };
+ };
+ if let hash_map::Entry::Occupied(mut entry) = entry {
+ let max_payment_attempts = self.retry_attempts.0 + 1;
+ let attempts = entry.get_mut();
+ *attempts += 1;
+
+ if *rejected_by_dest {
+ log_trace!(self.logger, "Payment {} rejected by destination; not retrying (attempts: {})", log_bytes!(payment_hash.0), attempts);
+ } else if payment_id.is_none() {
+ log_trace!(self.logger, "Payment {} has no id; not retrying (attempts: {})", log_bytes!(payment_hash.0), attempts);
+ } else if *attempts >= max_payment_attempts {
+ log_trace!(self.logger, "Payment {} exceeded maximum attempts; not retrying (attempts: {})", log_bytes!(payment_hash.0), attempts);
+ } else if retry.is_none() {
+ log_trace!(self.logger, "Payment {} missing retry params; not retrying (attempts: {})", log_bytes!(payment_hash.0), attempts);
+ } else if has_expired(retry.as_ref().unwrap()) {
+ log_trace!(self.logger, "Invoice expired for payment {}; not retrying (attempts: {})", log_bytes!(payment_hash.0), attempts);
+ } else if self.retry_payment(*payment_id.as_ref().unwrap(), retry.as_ref().unwrap()).is_err() {
+ log_trace!(self.logger, "Error retrying payment {}; not retrying (attempts: {})", log_bytes!(payment_hash.0), attempts);
+ } else {
+ log_trace!(self.logger, "Payment {} failed; retrying (attempts: {})", log_bytes!(payment_hash.0), attempts);
+ return;
+ }
+
+ // Either the payment was rejected, the maximum attempts were exceeded, or an
+ // error occurred when attempting to retry.
+ entry.remove();
+ } else {
+ unreachable!();
+ }
+ },
+ Event::PaymentSent { payment_hash, .. } => {
+ let mut payment_cache = self.payment_cache.lock().unwrap();
+ let attempts = payment_cache
+ .remove(payment_hash)
+ .map_or(1, |attempts| attempts + 1);
+ log_trace!(self.logger, "Payment {} succeeded (attempts: {})", log_bytes!(payment_hash.0), attempts);
+ },
+ _ => {},
+ }
+
+ // Delegate to the decorated event handler unless the payment is retried.
+ self.event_handler.handle_event(event)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::{DEFAULT_EXPIRY_TIME, InvoiceBuilder, Currency};
+ use bitcoin_hashes::sha256::Hash as Sha256;
+ use lightning::ln::PaymentPreimage;
+ use lightning::ln::features::{ChannelFeatures, NodeFeatures};
+ use lightning::ln::msgs::{ErrorAction, LightningError};
+ use lightning::routing::router::{Route, RouteHop};
+ use lightning::util::test_utils::TestLogger;
+ use lightning::util::errors::APIError;
+ use lightning::util::events::Event;
+ use secp256k1::{SecretKey, PublicKey, Secp256k1};
+ use std::time::{SystemTime, Duration};
+
+ fn invoice(payment_preimage: PaymentPreimage) -> Invoice {
+ let payment_hash = Sha256::hash(&payment_preimage.0);
+ let private_key = SecretKey::from_slice(&[42; 32]).unwrap();
+ InvoiceBuilder::new(Currency::Bitcoin)
+ .description("test".into())
+ .payment_hash(payment_hash)
+ .payment_secret(PaymentSecret([0; 32]))
+ .current_timestamp()
+ .min_final_cltv_expiry(144)
+ .amount_milli_satoshis(128)
+ .build_signed(|hash| {
+ Secp256k1::new().sign_recoverable(hash, &private_key)
+ })
+ .unwrap()
+ }
+
+ fn zero_value_invoice(payment_preimage: PaymentPreimage) -> Invoice {
+ let payment_hash = Sha256::hash(&payment_preimage.0);
+ let private_key = SecretKey::from_slice(&[42; 32]).unwrap();
+ InvoiceBuilder::new(Currency::Bitcoin)
+ .description("test".into())
+ .payment_hash(payment_hash)
+ .payment_secret(PaymentSecret([0; 32]))
+ .current_timestamp()
+ .min_final_cltv_expiry(144)
+ .build_signed(|hash| {
+ Secp256k1::new().sign_recoverable(hash, &private_key)
+ })
+ .unwrap()
+ }
+
+ fn expired_invoice(payment_preimage: PaymentPreimage) -> Invoice {
+ let payment_hash = Sha256::hash(&payment_preimage.0);
+ let private_key = SecretKey::from_slice(&[42; 32]).unwrap();
+ let timestamp = SystemTime::now()
+ .checked_sub(Duration::from_secs(DEFAULT_EXPIRY_TIME * 2))
+ .unwrap();
+ InvoiceBuilder::new(Currency::Bitcoin)
+ .description("test".into())
+ .payment_hash(payment_hash)
+ .payment_secret(PaymentSecret([0; 32]))
+ .timestamp(timestamp)
+ .min_final_cltv_expiry(144)
+ .amount_milli_satoshis(128)
+ .build_signed(|hash| {
+ Secp256k1::new().sign_recoverable(hash, &private_key)
+ })
+ .unwrap()
+ }
+
+ #[test]
+ fn pays_invoice_on_first_attempt() {
+ let event_handled = core::cell::RefCell::new(false);
+ let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
+
+ let payment_preimage = PaymentPreimage([1; 32]);
+ let invoice = invoice(payment_preimage);
+ let payment_hash = PaymentHash(invoice.payment_hash().clone().into_inner());
+
+ let payer = TestPayer::new();
+ let router = TestRouter {};
+ let logger = TestLogger::new();
+ let invoice_payer =
+ InvoicePayer::new(&payer, router, &logger, event_handler, RetryAttempts(0));
+
+ let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
+ assert_eq!(*payer.attempts.borrow(), 1);
+
+ invoice_payer.handle_event(&Event::PaymentSent {
+ payment_id, payment_preimage, payment_hash, fee_paid_msat: None
+ });
+ assert_eq!(*event_handled.borrow(), true);
+ assert_eq!(*payer.attempts.borrow(), 1);
+ }
+
+ #[test]
+ fn pays_invoice_on_retry() {
+ let event_handled = core::cell::RefCell::new(false);
+ let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
+
+ let payment_preimage = PaymentPreimage([1; 32]);
+ let invoice = invoice(payment_preimage);
+ let payment_hash = PaymentHash(invoice.payment_hash().clone().into_inner());
+ let final_value_msat = invoice.amount_milli_satoshis().unwrap();
+
+ let payer = TestPayer::new()
+ .expect_value_msat(final_value_msat)
+ .expect_value_msat(final_value_msat / 2);
+ let router = TestRouter {};
+ let logger = TestLogger::new();
+ let invoice_payer =
+ InvoicePayer::new(&payer, router, &logger, event_handler, RetryAttempts(2));
+
+ let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
+ assert_eq!(*payer.attempts.borrow(), 1);
+
+ let event = Event::PaymentPathFailed {
+ payment_id,
+ payment_hash,
+ network_update: None,
+ rejected_by_dest: false,
+ all_paths_failed: false,
+ path: TestRouter::path_for_value(final_value_msat),
+ short_channel_id: None,
+ retry: Some(TestRouter::retry_for_invoice(&invoice)),
+ };
+ invoice_payer.handle_event(&event);
+ assert_eq!(*event_handled.borrow(), false);
+ assert_eq!(*payer.attempts.borrow(), 2);
+
+ invoice_payer.handle_event(&Event::PaymentSent {
+ payment_id, payment_preimage, payment_hash, fee_paid_msat: None
+ });
+ assert_eq!(*event_handled.borrow(), true);
+ assert_eq!(*payer.attempts.borrow(), 2);
+ }
+
+ #[test]
+ fn retries_payment_path_for_unknown_payment() {
+ let event_handled = core::cell::RefCell::new(false);
+ let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
+
+ let payment_preimage = PaymentPreimage([1; 32]);
+ let invoice = invoice(payment_preimage);
+ let payment_hash = PaymentHash(invoice.payment_hash().clone().into_inner());
+ let final_value_msat = invoice.amount_milli_satoshis().unwrap();
+
+ let payer = TestPayer::new();
+ let router = TestRouter {};
+ let logger = TestLogger::new();
+ let invoice_payer =
+ InvoicePayer::new(&payer, router, &logger, event_handler, RetryAttempts(2));
+
+ let payment_id = Some(PaymentId([1; 32]));
+ let event = Event::PaymentPathFailed {
+ payment_id,
+ payment_hash,
+ network_update: None,
+ rejected_by_dest: false,
+ all_paths_failed: false,
+ path: TestRouter::path_for_value(final_value_msat),
+ short_channel_id: None,
+ retry: Some(TestRouter::retry_for_invoice(&invoice)),
+ };
+ invoice_payer.handle_event(&event);
+ assert_eq!(*event_handled.borrow(), false);
+ assert_eq!(*payer.attempts.borrow(), 1);
+
+ invoice_payer.handle_event(&event);
+ assert_eq!(*event_handled.borrow(), false);
+ assert_eq!(*payer.attempts.borrow(), 2);
+
+ invoice_payer.handle_event(&Event::PaymentSent {
+ payment_id, payment_preimage, payment_hash, fee_paid_msat: None
+ });
+ assert_eq!(*event_handled.borrow(), true);
+ assert_eq!(*payer.attempts.borrow(), 2);
+ }
+
+ #[test]
+ fn fails_paying_invoice_after_max_retries() {
+ let event_handled = core::cell::RefCell::new(false);
+ let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
+
+ let payment_preimage = PaymentPreimage([1; 32]);
+ let invoice = invoice(payment_preimage);
+ let final_value_msat = invoice.amount_milli_satoshis().unwrap();
+
+ let payer = TestPayer::new()
+ .expect_value_msat(final_value_msat)
+ .expect_value_msat(final_value_msat / 2)
+ .expect_value_msat(final_value_msat / 2);
+ let router = TestRouter {};
+ let logger = TestLogger::new();
+ let invoice_payer =
+ InvoicePayer::new(&payer, router, &logger, event_handler, RetryAttempts(2));
+
+ let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
+ assert_eq!(*payer.attempts.borrow(), 1);
+
+ let event = Event::PaymentPathFailed {
+ payment_id,
+ payment_hash: PaymentHash(invoice.payment_hash().clone().into_inner()),
+ network_update: None,
+ rejected_by_dest: false,
+ all_paths_failed: true,
+ path: TestRouter::path_for_value(final_value_msat),
+ short_channel_id: None,
+ retry: Some(TestRouter::retry_for_invoice(&invoice)),
+ };
+ invoice_payer.handle_event(&event);
+ assert_eq!(*event_handled.borrow(), false);
+ assert_eq!(*payer.attempts.borrow(), 2);
+
+ let event = Event::PaymentPathFailed {
+ payment_id,
+ payment_hash: PaymentHash(invoice.payment_hash().clone().into_inner()),
+ network_update: None,
+ rejected_by_dest: false,
+ all_paths_failed: false,
+ path: TestRouter::path_for_value(final_value_msat / 2),
+ short_channel_id: None,
+ retry: Some(RouteParameters {
+ final_value_msat: final_value_msat / 2, ..TestRouter::retry_for_invoice(&invoice)
+ }),
+ };
+ invoice_payer.handle_event(&event);
+ assert_eq!(*event_handled.borrow(), false);
+ assert_eq!(*payer.attempts.borrow(), 3);
+
+ invoice_payer.handle_event(&event);
+ assert_eq!(*event_handled.borrow(), true);
+ assert_eq!(*payer.attempts.borrow(), 3);
+ }
+
+ #[test]
+ fn fails_paying_invoice_with_missing_retry_params() {
+ let event_handled = core::cell::RefCell::new(false);
+ let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
+
+ let payer = TestPayer::new();
+ let router = TestRouter {};
+ let logger = TestLogger::new();
+ let invoice_payer =
+ InvoicePayer::new(&payer, router, &logger, event_handler, RetryAttempts(2));
+
+ let payment_preimage = PaymentPreimage([1; 32]);
+ let invoice = invoice(payment_preimage);
+ let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
+ assert_eq!(*payer.attempts.borrow(), 1);
+
+ let event = Event::PaymentPathFailed {
+ payment_id,
+ payment_hash: PaymentHash(invoice.payment_hash().clone().into_inner()),
+ network_update: None,
+ rejected_by_dest: false,
+ all_paths_failed: false,
+ path: vec![],
+ short_channel_id: None,
+ retry: None,
+ };
+ invoice_payer.handle_event(&event);
+ assert_eq!(*event_handled.borrow(), true);
+ assert_eq!(*payer.attempts.borrow(), 1);
+ }
+
+ #[test]
+ fn fails_paying_invoice_after_expiration() {
+ let event_handled = core::cell::RefCell::new(false);
+ let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
+
+ let payer = TestPayer::new();
+ let router = TestRouter {};
+ let logger = TestLogger::new();
+ let invoice_payer =
+ InvoicePayer::new(&payer, router, &logger, event_handler, RetryAttempts(2));
+
+ let payment_preimage = PaymentPreimage([1; 32]);
+ let invoice = expired_invoice(payment_preimage);
+ let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
+ assert_eq!(*payer.attempts.borrow(), 1);
+
+ let event = Event::PaymentPathFailed {
+ payment_id,
+ payment_hash: PaymentHash(invoice.payment_hash().clone().into_inner()),
+ network_update: None,
+ rejected_by_dest: false,
+ all_paths_failed: false,
+ path: vec![],
+ short_channel_id: None,
+ retry: Some(TestRouter::retry_for_invoice(&invoice)),
+ };
+ invoice_payer.handle_event(&event);
+ assert_eq!(*event_handled.borrow(), true);
+ assert_eq!(*payer.attempts.borrow(), 1);
+ }
+
+ #[test]
+ fn fails_paying_invoice_after_retry_error() {
+ let event_handled = core::cell::RefCell::new(false);
+ let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
+
+ let payment_preimage = PaymentPreimage([1; 32]);
+ let invoice = invoice(payment_preimage);
+ let final_value_msat = invoice.amount_milli_satoshis().unwrap();
+
+ let payer = TestPayer::new()
+ .fails_on_attempt(2)
+ .expect_value_msat(final_value_msat);
+ let router = TestRouter {};
+ let logger = TestLogger::new();
+ let invoice_payer =
+ InvoicePayer::new(&payer, router, &logger, event_handler, RetryAttempts(2));
+
+ let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
+ assert_eq!(*payer.attempts.borrow(), 1);
+
+ let event = Event::PaymentPathFailed {
+ payment_id,
+ payment_hash: PaymentHash(invoice.payment_hash().clone().into_inner()),
+ network_update: None,
+ rejected_by_dest: false,
+ all_paths_failed: false,
+ path: TestRouter::path_for_value(final_value_msat / 2),
+ short_channel_id: None,
+ retry: Some(TestRouter::retry_for_invoice(&invoice)),
+ };
+ invoice_payer.handle_event(&event);
+ assert_eq!(*event_handled.borrow(), true);
+ assert_eq!(*payer.attempts.borrow(), 2);
+ }
+
+ #[test]
+ fn fails_paying_invoice_after_rejected_by_payee() {
+ let event_handled = core::cell::RefCell::new(false);
+ let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
+
+ let payer = TestPayer::new();
+ let router = TestRouter {};
+ let logger = TestLogger::new();
+ let invoice_payer =
+ InvoicePayer::new(&payer, router, &logger, event_handler, RetryAttempts(2));
+
+ let payment_preimage = PaymentPreimage([1; 32]);
+ let invoice = invoice(payment_preimage);
+ let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
+ assert_eq!(*payer.attempts.borrow(), 1);
+
+ let event = Event::PaymentPathFailed {
+ payment_id,
+ payment_hash: PaymentHash(invoice.payment_hash().clone().into_inner()),
+ network_update: None,
+ rejected_by_dest: true,
+ all_paths_failed: false,
+ path: vec![],
+ short_channel_id: None,
+ retry: Some(TestRouter::retry_for_invoice(&invoice)),
+ };
+ invoice_payer.handle_event(&event);
+ assert_eq!(*event_handled.borrow(), true);
+ assert_eq!(*payer.attempts.borrow(), 1);
+ }
+
+ #[test]
+ fn fails_repaying_invoice_with_pending_payment() {
+ let event_handled = core::cell::RefCell::new(false);
+ let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
+
+ let payer = TestPayer::new();
+ let router = TestRouter {};
+ let logger = TestLogger::new();
+ let invoice_payer =
+ InvoicePayer::new(&payer, router, &logger, event_handler, RetryAttempts(0));
+
+ let payment_preimage = PaymentPreimage([1; 32]);
+ let invoice = invoice(payment_preimage);
+ let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
+
+ // Cannot repay an invoice pending payment.
+ match invoice_payer.pay_invoice(&invoice) {
+ Err(PaymentError::Invoice("payment pending")) => {},
+ Err(_) => panic!("unexpected error"),
+ Ok(_) => panic!("expected invoice error"),
+ }
+
+ // Can repay an invoice once cleared from cache.
+ let payment_hash = PaymentHash(invoice.payment_hash().clone().into_inner());
+ invoice_payer.remove_cached_payment(&payment_hash);
+ assert!(invoice_payer.pay_invoice(&invoice).is_ok());
+
+ // Cannot retry paying an invoice if cleared from cache.
+ invoice_payer.remove_cached_payment(&payment_hash);
+ let event = Event::PaymentPathFailed {
+ payment_id,
+ payment_hash,
+ network_update: None,
+ rejected_by_dest: false,
+ all_paths_failed: false,
+ path: vec![],
+ short_channel_id: None,
+ retry: Some(TestRouter::retry_for_invoice(&invoice)),
+ };
+ invoice_payer.handle_event(&event);
+ assert_eq!(*event_handled.borrow(), true);
+ }
+
+ #[test]
+ fn fails_paying_invoice_with_routing_errors() {
+ let payer = TestPayer::new();
+ let router = FailingRouter {};
+ let logger = TestLogger::new();
+ let invoice_payer =
+ InvoicePayer::new(&payer, router, &logger, |_: &_| {}, RetryAttempts(0));
+
+ let payment_preimage = PaymentPreimage([1; 32]);
+ let invoice = invoice(payment_preimage);
+ match invoice_payer.pay_invoice(&invoice) {
+ Err(PaymentError::Routing(_)) => {},
+ Err(_) => panic!("unexpected error"),
+ Ok(_) => panic!("expected routing error"),
+ }
+ }
+
+ #[test]
+ fn fails_paying_invoice_with_sending_errors() {
+ let payer = TestPayer::new().fails_on_attempt(1);
+ let router = TestRouter {};
+ let logger = TestLogger::new();
+ let invoice_payer =
+ InvoicePayer::new(&payer, router, &logger, |_: &_| {}, RetryAttempts(0));
+
+ let payment_preimage = PaymentPreimage([1; 32]);
+ let invoice = invoice(payment_preimage);
+ match invoice_payer.pay_invoice(&invoice) {
+ Err(PaymentError::Sending(_)) => {},
+ Err(_) => panic!("unexpected error"),
+ Ok(_) => panic!("expected sending error"),
+ }
+ }
+
+ #[test]
+ fn pays_zero_value_invoice_using_amount() {
+ let event_handled = core::cell::RefCell::new(false);
+ let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
+
+ let payment_preimage = PaymentPreimage([1; 32]);
+ let invoice = zero_value_invoice(payment_preimage);
+ let payment_hash = PaymentHash(invoice.payment_hash().clone().into_inner());
+ let final_value_msat = 100;
+
+ let payer = TestPayer::new().expect_value_msat(final_value_msat);
+ let router = TestRouter {};
+ let logger = TestLogger::new();
+ let invoice_payer =
+ InvoicePayer::new(&payer, router, &logger, event_handler, RetryAttempts(0));
+
+ let payment_id =
+ Some(invoice_payer.pay_zero_value_invoice(&invoice, final_value_msat).unwrap());
+ assert_eq!(*payer.attempts.borrow(), 1);
+
+ invoice_payer.handle_event(&Event::PaymentSent {
+ payment_id, payment_preimage, payment_hash, fee_paid_msat: None
+ });
+ assert_eq!(*event_handled.borrow(), true);
+ assert_eq!(*payer.attempts.borrow(), 1);
+ }
+
+ #[test]
+ fn fails_paying_zero_value_invoice_with_amount() {
+ let event_handled = core::cell::RefCell::new(false);
+ let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
+
+ let payer = TestPayer::new();
+ let router = TestRouter {};
+ let logger = TestLogger::new();
+ let invoice_payer =
+ InvoicePayer::new(&payer, router, &logger, event_handler, RetryAttempts(0));
+
+ let payment_preimage = PaymentPreimage([1; 32]);
+ let invoice = invoice(payment_preimage);
+
+ // Cannot repay an invoice pending payment.
+ match invoice_payer.pay_zero_value_invoice(&invoice, 100) {
+ Err(PaymentError::Invoice("amount unexpected")) => {},
+ Err(_) => panic!("unexpected error"),
+ Ok(_) => panic!("expected invoice error"),
+ }
+ }
+
+ struct TestRouter;
+
+ impl TestRouter {
+ fn route_for_value(final_value_msat: u64) -> Route {
+ Route {
+ paths: vec![
+ vec![RouteHop {
+ pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(),
+ channel_features: ChannelFeatures::empty(),
+ node_features: NodeFeatures::empty(),
+ short_channel_id: 0, fee_msat: final_value_msat / 2, cltv_expiry_delta: 144
+ }],
+ vec![RouteHop {
+ pubkey: PublicKey::from_slice(&hex::decode("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap(),
+ channel_features: ChannelFeatures::empty(),
+ node_features: NodeFeatures::empty(),
+ short_channel_id: 1, fee_msat: final_value_msat / 2, cltv_expiry_delta: 144
+ }],
+ ],
+ payee: None,
+ }
+ }
+
+ fn path_for_value(final_value_msat: u64) -> Vec<RouteHop> {
+ TestRouter::route_for_value(final_value_msat).paths[0].clone()
+ }
+
+ fn retry_for_invoice(invoice: &Invoice) -> RouteParameters {
+ let mut payee = Payee::new(invoice.recover_payee_pub_key())
+ .with_expiry_time(expiry_time_from_unix_epoch(invoice).as_secs())
+ .with_route_hints(invoice.route_hints());
+ if let Some(features) = invoice.features() {
+ payee = payee.with_features(features.clone());
+ }
+ let final_value_msat = invoice.amount_milli_satoshis().unwrap() / 2;
+ RouteParameters {
+ payee,
+ final_value_msat,
+ final_cltv_expiry_delta: invoice.min_final_cltv_expiry() as u32,
+ }
+ }
+ }
+
+ impl Router for TestRouter {
+ fn find_route(
+ &self,
+ _payer: &PublicKey,
+ params: &RouteParameters,
+ _first_hops: Option<&[&ChannelDetails]>,
+ ) -> Result<Route, LightningError> {
+ Ok(Route {
+ payee: Some(params.payee.clone()), ..Self::route_for_value(params.final_value_msat)
+ })
+ }
+ }
+
+ struct FailingRouter;
+
+ impl Router for FailingRouter {
+ fn find_route(
+ &self,
+ _payer: &PublicKey,
+ _params: &RouteParameters,
+ _first_hops: Option<&[&ChannelDetails]>,
+ ) -> Result<Route, LightningError> {
+ Err(LightningError { err: String::new(), action: ErrorAction::IgnoreError })
+ }
+ }
+
+ struct TestPayer {
+ expectations: core::cell::RefCell<std::collections::VecDeque<u64>>,
+ attempts: core::cell::RefCell<usize>,
+ failing_on_attempt: Option<usize>,
+ }
+
+ impl TestPayer {
+ fn new() -> Self {
+ Self {
+ expectations: core::cell::RefCell::new(std::collections::VecDeque::new()),
+ attempts: core::cell::RefCell::new(0),
+ failing_on_attempt: None,
+ }
+ }
+
+ fn expect_value_msat(self, value_msat: u64) -> Self {
+ self.expectations.borrow_mut().push_back(value_msat);
+ self
+ }
+
+ fn fails_on_attempt(self, attempt: usize) -> Self {
+ Self {
+ expectations: core::cell::RefCell::new(self.expectations.borrow().clone()),
+ attempts: core::cell::RefCell::new(0),
+ failing_on_attempt: Some(attempt),
+ }
+ }
+
+ fn check_attempts(&self) -> bool {
+ let mut attempts = self.attempts.borrow_mut();
+ *attempts += 1;
+ match self.failing_on_attempt {
+ None => true,
+ Some(attempt) if attempt != *attempts => true,
+ Some(_) => false,
+ }
+ }
+
+ fn check_value_msats(&self, route: &Route) {
+ let expected_value_msats = self.expectations.borrow_mut().pop_front();
+ if let Some(expected_value_msats) = expected_value_msats {
+ let actual_value_msats = route.get_total_amount();
+ assert_eq!(actual_value_msats, expected_value_msats);
+ }
+ }
+ }
+
+ impl Drop for TestPayer {
+ fn drop(&mut self) {
+ if std::thread::panicking() {
+ return;
+ }
+
+ if !self.expectations.borrow().is_empty() {
+ panic!("Unsatisfied payment expectations: {:?}", self.expectations.borrow());
+ }
+ }
+ }
+
+ impl Payer for TestPayer {
+ fn node_id(&self) -> PublicKey {
+ let secp_ctx = Secp256k1::new();
+ PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap())
+ }
+
+ fn first_hops(&self) -> Vec<ChannelDetails> {
+ Vec::new()
+ }
+
+ fn send_payment(
+ &self,
+ route: &Route,
+ _payment_hash: PaymentHash,
+ _payment_secret: &Option<PaymentSecret>
+ ) -> Result<PaymentId, PaymentSendFailure> {
+ if self.check_attempts() {
+ self.check_value_msats(route);
+ Ok(PaymentId([1; 32]))
+ } else {
+ Err(PaymentSendFailure::ParameterError(APIError::MonitorUpdateFailed))
+ }
+ }
+
+ fn retry_payment(
+ &self, route: &Route, _payment_id: PaymentId
+ ) -> Result<(), PaymentSendFailure> {
+ if self.check_attempts() {
+ self.check_value_msats(route);
+ Ok(())
+ } else {
+ Err(PaymentSendFailure::ParameterError(APIError::MonitorUpdateFailed))
+ }
+ }
+ }
+}
//! Convenient utilities to create an invoice.
+
use {Currency, DEFAULT_EXPIRY_TIME, Invoice, InvoiceBuilder, SignOrCreationError, RawInvoice};
+use payment::{Payer, Router};
+
use bech32::ToBase32;
use bitcoin_hashes::Hash;
use lightning::chain;
use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
use lightning::chain::keysinterface::{Sign, KeysInterface};
-use lightning::ln::channelmanager::{ChannelManager, MIN_FINAL_CLTV_EXPIRY};
-use lightning::routing::network_graph::RoutingFees;
-use lightning::routing::router::{RouteHint, RouteHintHop};
+use lightning::ln::{PaymentHash, PaymentSecret};
+use lightning::ln::channelmanager::{ChannelDetails, ChannelManager, PaymentId, PaymentSendFailure, MIN_FINAL_CLTV_EXPIRY};
+use lightning::ln::msgs::LightningError;
+use lightning::routing::network_graph::{NetworkGraph, RoutingFees};
+use lightning::routing::router::{Route, RouteHint, RouteHintHop, RouteParameters, find_route};
+use lightning::routing::scorer::Scorer;
use lightning::util::logger::Logger;
+use secp256k1::key::PublicKey;
use std::convert::TryInto;
use std::ops::Deref;
}
}
+/// A [`Router`] implemented using [`find_route`].
+pub struct DefaultRouter<G, L: Deref> where G: Deref<Target = NetworkGraph>, L::Target: Logger {
+ network_graph: G,
+ logger: L,
+}
+
+impl<G, L: Deref> DefaultRouter<G, L> where G: Deref<Target = NetworkGraph>, L::Target: Logger {
+ /// Creates a new router using the given [`NetworkGraph`] and [`Logger`].
+ pub fn new(network_graph: G, logger: L) -> Self {
+ Self { network_graph, logger }
+ }
+}
+
+impl<G, L: Deref> Router for DefaultRouter<G, L>
+where G: Deref<Target = NetworkGraph>, L::Target: Logger {
+ fn find_route(
+ &self, payer: &PublicKey, params: &RouteParameters, first_hops: Option<&[&ChannelDetails]>,
+ ) -> Result<Route, LightningError> {
+ let scorer = Scorer::default();
+ find_route(payer, params, &*self.network_graph, first_hops, &*self.logger, &scorer)
+ }
+}
+
+impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Payer for ChannelManager<Signer, M, T, K, F, L>
+where
+ M::Target: chain::Watch<Signer>,
+ T::Target: BroadcasterInterface,
+ K::Target: KeysInterface<Signer = Signer>,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
+{
+ fn node_id(&self) -> PublicKey {
+ self.get_our_node_id()
+ }
+
+ fn first_hops(&self) -> Vec<ChannelDetails> {
+ self.list_usable_channels()
+ }
+
+ fn send_payment(
+ &self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>
+ ) -> Result<PaymentId, PaymentSendFailure> {
+ self.send_payment(route, payment_hash, payment_secret)
+ }
+
+ fn retry_payment(
+ &self, route: &Route, payment_id: PaymentId
+ ) -> Result<(), PaymentSendFailure> {
+ self.retry_payment(route, payment_id)
+ }
+}
+
#[cfg(test)]
mod test {
use {Currency, Description, InvoiceDescription};
use lightning::ln::functional_test_utils::*;
use lightning::ln::features::InitFeatures;
use lightning::ln::msgs::ChannelMessageHandler;
- use lightning::routing::router;
+ use lightning::routing::router::{Payee, RouteParameters, find_route};
use lightning::routing::scorer::Scorer;
use lightning::util::events::MessageSendEventsProvider;
use lightning::util::test_utils;
assert_eq!(invoice.min_final_cltv_expiry(), MIN_FINAL_CLTV_EXPIRY as u64);
assert_eq!(invoice.description(), InvoiceDescription::Direct(&Description("test".to_string())));
- let amt_msat = invoice.amount_pico_btc().unwrap() / 10;
+ let payee = Payee::new(invoice.recover_payee_pub_key())
+ .with_features(invoice.features().unwrap().clone())
+ .with_route_hints(invoice.route_hints());
+ let params = RouteParameters {
+ payee,
+ final_value_msat: invoice.amount_milli_satoshis().unwrap(),
+ final_cltv_expiry_delta: invoice.min_final_cltv_expiry() as u32,
+ };
let first_hops = nodes[0].node.list_usable_channels();
- let last_hops = invoice.route_hints();
let network_graph = &nodes[0].net_graph_msg_handler.network_graph;
let logger = test_utils::TestLogger::new();
let scorer = Scorer::new(0);
- let route = router::get_route(
- &nodes[0].node.get_our_node_id(),
- network_graph,
- &invoice.recover_payee_pub_key(),
- Some(invoice.features().unwrap().clone()),
- Some(&first_hops.iter().collect::<Vec<_>>()),
- &last_hops,
- amt_msat,
- invoice.min_final_cltv_expiry() as u32,
- &logger,
- &scorer,
+ let route = find_route(
+ &nodes[0].node.get_our_node_id(), ¶ms, network_graph,
+ Some(&first_hops.iter().collect::<Vec<_>>()), &logger, &scorer,
).unwrap();
let payment_event = {
}
impl<ChannelSigner: Sign> chainmonitor::Persist<ChannelSigner> for FilesystemPersister {
- fn persist_new_channel(&self, funding_txo: OutPoint, monitor: &ChannelMonitor<ChannelSigner>) -> Result<(), chain::ChannelMonitorUpdateErr> {
+ // TODO: We really need a way for the persister to inform the user that its time to crash/shut
+ // down once these start returning failure.
+ // A PermanentFailure implies we need to shut down since we're force-closing channels without
+ // even broadcasting!
+
+ fn persist_new_channel(&self, funding_txo: OutPoint, monitor: &ChannelMonitor<ChannelSigner>, _update_id: chainmonitor::MonitorUpdateId) -> Result<(), chain::ChannelMonitorUpdateErr> {
let filename = format!("{}_{}", funding_txo.txid.to_hex(), funding_txo.index);
util::write_to_file(self.path_to_monitor_data(), filename, monitor)
.map_err(|_| chain::ChannelMonitorUpdateErr::PermanentFailure)
}
- fn update_persisted_channel(&self, funding_txo: OutPoint, _update: &ChannelMonitorUpdate, monitor: &ChannelMonitor<ChannelSigner>) -> Result<(), chain::ChannelMonitorUpdateErr> {
+ fn update_persisted_channel(&self, funding_txo: OutPoint, _update: &Option<ChannelMonitorUpdate>, monitor: &ChannelMonitor<ChannelSigner>, _update_id: chainmonitor::MonitorUpdateId) -> Result<(), chain::ChannelMonitorUpdateErr> {
let filename = format!("{}_{}", funding_txo.txid.to_hex(), funding_txo.index);
util::write_to_file(self.path_to_monitor_data(), filename, monitor)
.map_err(|_| chain::ChannelMonitorUpdateErr::PermanentFailure)
nodes[1].node.force_close_channel(&chan.2).unwrap();
check_closed_event!(nodes[1], 1, ClosureReason::HolderForceClosed);
let mut added_monitors = nodes[1].chain_monitor.added_monitors.lock().unwrap();
+ let update_map = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap();
+ let update_id = update_map.get(&added_monitors[0].0.to_channel_id()).unwrap();
// Set the persister's directory to read-only, which should result in
// returning a permanent failure when we then attempt to persist a
txid: Txid::from_hex("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(),
index: 0
};
- match persister.persist_new_channel(test_txo, &added_monitors[0].1) {
+ match persister.persist_new_channel(test_txo, &added_monitors[0].1, update_id.2) {
Err(ChannelMonitorUpdateErr::PermanentFailure) => {},
_ => panic!("unexpected result from persisting new channel")
}
nodes[1].node.force_close_channel(&chan.2).unwrap();
check_closed_event!(nodes[1], 1, ClosureReason::HolderForceClosed);
let mut added_monitors = nodes[1].chain_monitor.added_monitors.lock().unwrap();
+ let update_map = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap();
+ let update_id = update_map.get(&added_monitors[0].0.to_channel_id()).unwrap();
// Create the persister with an invalid directory name and test that the
// channel fails to open because the directories fail to be created. There
txid: Txid::from_hex("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(),
index: 0
};
- match persister.persist_new_channel(test_txo, &added_monitors[0].1) {
+ match persister.persist_new_channel(test_txo, &added_monitors[0].1, update_id.2) {
Err(ChannelMonitorUpdateErr::PermanentFailure) => {},
_ => panic!("unexpected result from persisting new channel")
}
use chain;
use chain::{ChannelMonitorUpdateErr, Filter, WatchedOutput};
use chain::chaininterface::{BroadcasterInterface, FeeEstimator};
-use chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, Balance, MonitorEvent, TransactionOutputs};
+use chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, Balance, MonitorEvent, TransactionOutputs, LATENCY_GRACE_PERIOD_BLOCKS};
use chain::transaction::{OutPoint, TransactionData};
use chain::keysinterface::Sign;
+use util::atomic_counter::AtomicCounter;
use util::logger::Logger;
+use util::errors::APIError;
use util::events;
use util::events::EventHandler;
use ln::channelmanager::ChannelDetails;
use prelude::*;
-use sync::{RwLock, RwLockReadGuard};
+use sync::{RwLock, RwLockReadGuard, Mutex, MutexGuard};
use core::ops::Deref;
+use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
+
+#[derive(Clone, Copy, Hash, PartialEq, Eq)]
+/// A specific update's ID stored in a `MonitorUpdateId`, separated out to make the contents
+/// entirely opaque.
+enum UpdateOrigin {
+ /// An update that was generated by the `ChannelManager` (via our `chain::Watch`
+ /// implementation). This corresponds to an actual [`ChannelMonitorUpdate::update_id`] field
+ /// and [`ChannelMonitor::get_latest_update_id`].
+ OffChain(u64),
+ /// An update that was generated during blockchain processing. The ID here is specific to the
+ /// generating [`ChainMonitor`] and does *not* correspond to any on-disk IDs.
+ ChainSync(u64),
+}
+
+/// An opaque identifier describing a specific [`Persist`] method call.
+#[derive(Clone, Copy, Hash, PartialEq, Eq)]
+pub struct MonitorUpdateId {
+ contents: UpdateOrigin,
+}
+
+impl MonitorUpdateId {
+ pub(crate) fn from_monitor_update(update: &ChannelMonitorUpdate) -> Self {
+ Self { contents: UpdateOrigin::OffChain(update.update_id) }
+ }
+ pub(crate) fn from_new_monitor<ChannelSigner: Sign>(monitor: &ChannelMonitor<ChannelSigner>) -> Self {
+ Self { contents: UpdateOrigin::OffChain(monitor.get_latest_update_id()) }
+ }
+}
/// `Persist` defines behavior for persisting channel monitors: this could mean
/// writing once to disk, and/or uploading to one or more backup services.
///
-/// Note that for every new monitor, you **must** persist the new `ChannelMonitor`
-/// to disk/backups. And, on every update, you **must** persist either the
-/// `ChannelMonitorUpdate` or the updated monitor itself. Otherwise, there is risk
-/// of situations such as revoking a transaction, then crashing before this
-/// revocation can be persisted, then unintentionally broadcasting a revoked
-/// transaction and losing money. This is a risk because previous channel states
-/// are toxic, so it's important that whatever channel state is persisted is
-/// kept up-to-date.
+/// Each method can return three possible values:
+/// * If persistence (including any relevant `fsync()` calls) happens immediately, the
+/// implementation should return `Ok(())`, indicating normal channel operation should continue.
+/// * If persistence happens asynchronously, implementations should first ensure the
+/// [`ChannelMonitor`] or [`ChannelMonitorUpdate`] are written durably to disk, and then return
+/// `Err(ChannelMonitorUpdateErr::TemporaryFailure)` while the update continues in the
+/// background. Once the update completes, [`ChainMonitor::channel_monitor_updated`] should be
+/// called with the corresponding [`MonitorUpdateId`].
+///
+/// Note that unlike the direct [`chain::Watch`] interface,
+/// [`ChainMonitor::channel_monitor_updated`] must be called once for *each* update which occurs.
+///
+/// * If persistence fails for some reason, implementations should return
+/// `Err(ChannelMonitorUpdateErr::PermanentFailure)`, in which case the channel will likely be
+/// closed without broadcasting the latest state. See
+/// [`ChannelMonitorUpdateErr::PermanentFailure`] for more details.
pub trait Persist<ChannelSigner: Sign> {
- /// Persist a new channel's data. The data can be stored any way you want, but
- /// the identifier provided by Rust-Lightning is the channel's outpoint (and
- /// it is up to you to maintain a correct mapping between the outpoint and the
- /// stored channel data). Note that you **must** persist every new monitor to
- /// disk. See the `Persist` trait documentation for more details.
+ /// Persist a new channel's data in response to a [`chain::Watch::watch_channel`] call. This is
+ /// called by [`ChannelManager`] for new channels, or may be called directly, e.g. on startup.
+ ///
+ /// The data can be stored any way you want, but the identifier provided by LDK is the
+ /// channel's outpoint (and it is up to you to maintain a correct mapping between the outpoint
+ /// and the stored channel data). Note that you **must** persist every new monitor to disk.
+ ///
+ /// The `update_id` is used to identify this call to [`ChainMonitor::channel_monitor_updated`],
+ /// if you return [`ChannelMonitorUpdateErr::TemporaryFailure`].
///
/// See [`Writeable::write`] on [`ChannelMonitor`] for writing out a `ChannelMonitor`
/// and [`ChannelMonitorUpdateErr`] for requirements when returning errors.
///
+ /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
/// [`Writeable::write`]: crate::util::ser::Writeable::write
- fn persist_new_channel(&self, id: OutPoint, data: &ChannelMonitor<ChannelSigner>) -> Result<(), ChannelMonitorUpdateErr>;
+ fn persist_new_channel(&self, channel_id: OutPoint, data: &ChannelMonitor<ChannelSigner>, update_id: MonitorUpdateId) -> Result<(), ChannelMonitorUpdateErr>;
- /// Update one channel's data. The provided `ChannelMonitor` has already
- /// applied the given update.
+ /// Update one channel's data. The provided [`ChannelMonitor`] has already applied the given
+ /// update.
///
- /// Note that on every update, you **must** persist either the
- /// `ChannelMonitorUpdate` or the updated monitor itself to disk/backups. See
- /// the `Persist` trait documentation for more details.
+ /// Note that on every update, you **must** persist either the [`ChannelMonitorUpdate`] or the
+ /// updated monitor itself to disk/backups. See the [`Persist`] trait documentation for more
+ /// details.
+ ///
+ /// During blockchain synchronization operations, this may be called with no
+ /// [`ChannelMonitorUpdate`], in which case the full [`ChannelMonitor`] needs to be persisted.
+ /// Note that after the full [`ChannelMonitor`] is persisted any previous
+ /// [`ChannelMonitorUpdate`]s which were persisted should be discarded - they can no longer be
+ /// applied to the persisted [`ChannelMonitor`] as they were already applied.
///
/// If an implementer chooses to persist the updates only, they need to make
/// sure that all the updates are applied to the `ChannelMonitors` *before*
/// them in batches. The size of each monitor grows `O(number of state updates)`
/// whereas updates are small and `O(1)`.
///
+ /// The `update_id` is used to identify this call to [`ChainMonitor::channel_monitor_updated`],
+ /// if you return [`ChannelMonitorUpdateErr::TemporaryFailure`].
+ ///
/// See [`Writeable::write`] on [`ChannelMonitor`] for writing out a `ChannelMonitor`,
/// [`Writeable::write`] on [`ChannelMonitorUpdate`] for writing out an update, and
/// [`ChannelMonitorUpdateErr`] for requirements when returning errors.
///
/// [`Writeable::write`]: crate::util::ser::Writeable::write
- fn update_persisted_channel(&self, id: OutPoint, update: &ChannelMonitorUpdate, data: &ChannelMonitor<ChannelSigner>) -> Result<(), ChannelMonitorUpdateErr>;
+ fn update_persisted_channel(&self, channel_id: OutPoint, update: &Option<ChannelMonitorUpdate>, data: &ChannelMonitor<ChannelSigner>, update_id: MonitorUpdateId) -> Result<(), ChannelMonitorUpdateErr>;
}
struct MonitorHolder<ChannelSigner: Sign> {
monitor: ChannelMonitor<ChannelSigner>,
+ /// The full set of pending monitor updates for this Channel.
+ ///
+ /// Note that this lock must be held during updates to prevent a race where we call
+ /// update_persisted_channel, the user returns a TemporaryFailure, and then calls
+ /// channel_monitor_updated immediately, racing our insertion of the pending update into the
+ /// contained Vec.
+ ///
+ /// Beyond the synchronization of updates themselves, we cannot handle user events until after
+ /// any chain updates have been stored on disk. Thus, we scan this list when returning updates
+ /// to the ChannelManager, refusing to return any updates for a ChannelMonitor which is still
+ /// being persisted fully to disk after a chain update.
+ ///
+ /// This avoids the possibility of handling, e.g. an on-chain claim, generating a claim monitor
+ /// event, resulting in the relevant ChannelManager generating a PaymentSent event and dropping
+ /// the pending payment entry, and then reloading before the monitor is persisted, resulting in
+ /// the ChannelManager re-adding the same payment entry, before the same block is replayed,
+ /// resulting in a duplicate PaymentSent event.
+ pending_monitor_updates: Mutex<Vec<MonitorUpdateId>>,
+ /// When the user returns a PermanentFailure error from an update_persisted_channel call during
+ /// block processing, we inform the ChannelManager that the channel should be closed
+ /// asynchronously. In order to ensure no further changes happen before the ChannelManager has
+ /// processed the closure event, we set this to true and return PermanentFailure for any other
+ /// chain::Watch events.
+ channel_perm_failed: AtomicBool,
+ /// The last block height at which no [`UpdateOrigin::ChainSync`] monitor updates were present
+ /// in `pending_monitor_updates`.
+ /// If it's been more than [`LATENCY_GRACE_PERIOD_BLOCKS`] since we started waiting on a chain
+ /// sync event, we let monitor events return to `ChannelManager` because we cannot hold them up
+ /// forever or we'll end up with HTLC preimages waiting to feed back into an upstream channel
+ /// forever, risking funds loss.
+ last_chain_persist_height: AtomicUsize,
+}
+
+impl<ChannelSigner: Sign> MonitorHolder<ChannelSigner> {
+ fn has_pending_offchain_updates(&self, pending_monitor_updates_lock: &MutexGuard<Vec<MonitorUpdateId>>) -> bool {
+ pending_monitor_updates_lock.iter().any(|update_id|
+ if let UpdateOrigin::OffChain(_) = update_id.contents { true } else { false })
+ }
+ fn has_pending_chainsync_updates(&self, pending_monitor_updates_lock: &MutexGuard<Vec<MonitorUpdateId>>) -> bool {
+ pending_monitor_updates_lock.iter().any(|update_id|
+ if let UpdateOrigin::ChainSync(_) = update_id.contents { true } else { false })
+ }
}
/// A read-only reference to a current ChannelMonitor.
P::Target: Persist<ChannelSigner>,
{
monitors: RwLock<HashMap<OutPoint, MonitorHolder<ChannelSigner>>>,
+ /// When we generate a [`MonitorUpdateId`] for a chain-event monitor persistence, we need a
+ /// unique ID, which we calculate by simply getting the next value from this counter. Note that
+ /// the ID is never persisted so it's ok that they reset on restart.
+ sync_persistence_id: AtomicCounter,
chain_source: Option<C>,
broadcaster: T,
logger: L,
fee_estimator: F,
persister: P,
+ /// "User-provided" (ie persistence-completion/-failed) [`MonitorEvent`]s. These came directly
+ /// from the user and not from a [`ChannelMonitor`].
+ pending_monitor_events: Mutex<Vec<MonitorEvent>>,
+ /// The best block height seen, used as a proxy for the passage of time.
+ highest_chain_height: AtomicUsize,
}
impl<ChannelSigner: Sign, C: Deref, T: Deref, F: Deref, L: Deref, P: Deref> ChainMonitor<ChannelSigner, C, T, F, L, P>
/// calls must not exclude any transactions matching the new outputs nor any in-block
/// descendants of such transactions. It is not necessary to re-fetch the block to obtain
/// updated `txdata`.
- fn process_chain_data<FN>(&self, header: &BlockHeader, txdata: &TransactionData, process: FN)
+ ///
+ /// Calls which represent a new blockchain tip height should set `best_height`.
+ fn process_chain_data<FN>(&self, header: &BlockHeader, best_height: Option<u32>, txdata: &TransactionData, process: FN)
where
FN: Fn(&ChannelMonitor<ChannelSigner>, &TransactionData) -> Vec<TransactionOutputs>
{
let mut dependent_txdata = Vec::new();
- let monitor_states = self.monitors.read().unwrap();
- for monitor_state in monitor_states.values() {
- let mut txn_outputs = process(&monitor_state.monitor, txdata);
+ {
+ let monitor_states = self.monitors.write().unwrap();
+ if let Some(height) = best_height {
+ // If the best block height is being updated, update highest_chain_height under the
+ // monitors write lock.
+ let old_height = self.highest_chain_height.load(Ordering::Acquire);
+ let new_height = height as usize;
+ if new_height > old_height {
+ self.highest_chain_height.store(new_height, Ordering::Release);
+ }
+ }
- // Register any new outputs with the chain source for filtering, storing any dependent
- // transactions from within the block that previously had not been included in txdata.
- if let Some(ref chain_source) = self.chain_source {
- let block_hash = header.block_hash();
- for (txid, mut outputs) in txn_outputs.drain(..) {
- for (idx, output) in outputs.drain(..) {
- // Register any new outputs with the chain source for filtering and recurse
- // if it indicates that there are dependent transactions within the block
- // that had not been previously included in txdata.
- let output = WatchedOutput {
- block_hash: Some(block_hash),
- outpoint: OutPoint { txid, index: idx as u16 },
- script_pubkey: output.script_pubkey,
- };
- if let Some(tx) = chain_source.register_output(output) {
- dependent_txdata.push(tx);
+ for (funding_outpoint, monitor_state) in monitor_states.iter() {
+ let monitor = &monitor_state.monitor;
+ let mut txn_outputs;
+ {
+ txn_outputs = process(monitor, txdata);
+ let update_id = MonitorUpdateId {
+ contents: UpdateOrigin::ChainSync(self.sync_persistence_id.get_increment()),
+ };
+ let mut pending_monitor_updates = monitor_state.pending_monitor_updates.lock().unwrap();
+ if let Some(height) = best_height {
+ if !monitor_state.has_pending_chainsync_updates(&pending_monitor_updates) {
+ // If there are not ChainSync persists awaiting completion, go ahead and
+ // set last_chain_persist_height here - we wouldn't want the first
+ // TemporaryFailure to always immediately be considered "overly delayed".
+ monitor_state.last_chain_persist_height.store(height as usize, Ordering::Release);
+ }
+ }
+
+ log_trace!(self.logger, "Syncing Channel Monitor for channel {}", log_funding_info!(monitor));
+ match self.persister.update_persisted_channel(*funding_outpoint, &None, monitor, update_id) {
+ Ok(()) =>
+ log_trace!(self.logger, "Finished syncing Channel Monitor for channel {}", log_funding_info!(monitor)),
+ Err(ChannelMonitorUpdateErr::PermanentFailure) => {
+ monitor_state.channel_perm_failed.store(true, Ordering::Release);
+ self.pending_monitor_events.lock().unwrap().push(MonitorEvent::UpdateFailed(*funding_outpoint));
+ },
+ Err(ChannelMonitorUpdateErr::TemporaryFailure) => {
+ log_debug!(self.logger, "Channel Monitor sync for channel {} in progress, holding events until completion!", log_funding_info!(monitor));
+ pending_monitor_updates.push(update_id);
+ },
+ }
+ }
+
+ // Register any new outputs with the chain source for filtering, storing any dependent
+ // transactions from within the block that previously had not been included in txdata.
+ if let Some(ref chain_source) = self.chain_source {
+ let block_hash = header.block_hash();
+ for (txid, mut outputs) in txn_outputs.drain(..) {
+ for (idx, output) in outputs.drain(..) {
+ // Register any new outputs with the chain source for filtering and recurse
+ // if it indicates that there are dependent transactions within the block
+ // that had not been previously included in txdata.
+ let output = WatchedOutput {
+ block_hash: Some(block_hash),
+ outpoint: OutPoint { txid, index: idx as u16 },
+ script_pubkey: output.script_pubkey,
+ };
+ if let Some(tx) = chain_source.register_output(output) {
+ dependent_txdata.push(tx);
+ }
}
}
}
dependent_txdata.sort_unstable_by_key(|(index, _tx)| *index);
dependent_txdata.dedup_by_key(|(index, _tx)| *index);
let txdata: Vec<_> = dependent_txdata.iter().map(|(index, tx)| (*index, tx)).collect();
- self.process_chain_data(header, &txdata, process);
+ self.process_chain_data(header, None, &txdata, process); // We skip the best height the second go-around
}
}
pub fn new(chain_source: Option<C>, broadcaster: T, logger: L, feeest: F, persister: P) -> Self {
Self {
monitors: RwLock::new(HashMap::new()),
+ sync_persistence_id: AtomicCounter::new(),
chain_source,
broadcaster,
logger,
fee_estimator: feeest,
persister,
+ pending_monitor_events: Mutex::new(Vec::new()),
+ highest_chain_height: AtomicUsize::new(0),
}
}
self.monitors.write().unwrap().remove(funding_txo).unwrap().monitor
}
+ /// Indicates the persistence of a [`ChannelMonitor`] has completed after
+ /// [`ChannelMonitorUpdateErr::TemporaryFailure`] was returned from an update operation.
+ ///
+ /// Thus, the anticipated use is, at a high level:
+ /// 1) This [`ChainMonitor`] calls [`Persist::update_persisted_channel`] which stores the
+ /// update to disk and begins updating any remote (e.g. watchtower/backup) copies,
+ /// returning [`ChannelMonitorUpdateErr::TemporaryFailure`],
+ /// 2) once all remote copies are updated, you call this function with the
+ /// `completed_update_id` that completed, and once all pending updates have completed the
+ /// channel will be re-enabled.
+ // Note that we re-enable only after `UpdateOrigin::OffChain` updates complete, we don't
+ // care about `UpdateOrigin::ChainSync` updates for the channel state being updated. We
+ // only care about `UpdateOrigin::ChainSync` for returning `MonitorEvent`s.
+ ///
+ /// Returns an [`APIError::APIMisuseError`] if `funding_txo` does not match any currently
+ /// registered [`ChannelMonitor`]s.
+ pub fn channel_monitor_updated(&self, funding_txo: OutPoint, completed_update_id: MonitorUpdateId) -> Result<(), APIError> {
+ let monitors = self.monitors.read().unwrap();
+ let monitor_data = if let Some(mon) = monitors.get(&funding_txo) { mon } else {
+ return Err(APIError::APIMisuseError { err: format!("No ChannelMonitor matching funding outpoint {:?} found", funding_txo) });
+ };
+ let mut pending_monitor_updates = monitor_data.pending_monitor_updates.lock().unwrap();
+ pending_monitor_updates.retain(|update_id| *update_id != completed_update_id);
+
+ match completed_update_id {
+ MonitorUpdateId { contents: UpdateOrigin::OffChain(_) } => {
+ // Note that we only check for `UpdateOrigin::OffChain` failures here - if
+ // we're being told that a `UpdateOrigin::OffChain` monitor update completed,
+ // we only care about ensuring we don't tell the `ChannelManager` to restore
+ // the channel to normal operation until all `UpdateOrigin::OffChain` updates
+ // complete.
+ // If there's some `UpdateOrigin::ChainSync` update still pending that's okay
+ // - we can still update our channel state, just as long as we don't return
+ // `MonitorEvent`s from the monitor back to the `ChannelManager` until they
+ // complete.
+ let monitor_is_pending_updates = monitor_data.has_pending_offchain_updates(&pending_monitor_updates);
+ if monitor_is_pending_updates || monitor_data.channel_perm_failed.load(Ordering::Acquire) {
+ // If there are still monitor updates pending (or an old monitor update
+ // finished after a later one perm-failed), we cannot yet construct an
+ // UpdateCompleted event.
+ return Ok(());
+ }
+ self.pending_monitor_events.lock().unwrap().push(MonitorEvent::UpdateCompleted {
+ funding_txo,
+ monitor_update_id: monitor_data.monitor.get_latest_update_id(),
+ });
+ },
+ MonitorUpdateId { contents: UpdateOrigin::ChainSync(_) } => {
+ if !monitor_data.has_pending_chainsync_updates(&pending_monitor_updates) {
+ monitor_data.last_chain_persist_height.store(self.highest_chain_height.load(Ordering::Acquire), Ordering::Release);
+ // The next time release_pending_monitor_events is called, any events for this
+ // ChannelMonitor will be returned.
+ }
+ },
+ }
+ Ok(())
+ }
+
+ /// This wrapper avoids having to update some of our tests for now as they assume the direct
+ /// chain::Watch API wherein we mark a monitor fully-updated by just calling
+ /// channel_monitor_updated once with the highest ID.
+ #[cfg(any(test, feature = "fuzztarget"))]
+ pub fn force_channel_monitor_updated(&self, funding_txo: OutPoint, monitor_update_id: u64) {
+ self.pending_monitor_events.lock().unwrap().push(MonitorEvent::UpdateCompleted {
+ funding_txo,
+ monitor_update_id,
+ });
+ }
+
#[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))]
pub fn get_and_clear_pending_events(&self) -> Vec<events::Event> {
use util::events::EventsProvider;
let header = &block.header;
let txdata: Vec<_> = block.txdata.iter().enumerate().collect();
log_debug!(self.logger, "New best block {} at height {} provided via block_connected", header.block_hash(), height);
- self.process_chain_data(header, &txdata, |monitor, txdata| {
+ self.process_chain_data(header, Some(height), &txdata, |monitor, txdata| {
monitor.block_connected(
header, txdata, height, &*self.broadcaster, &*self.fee_estimator, &*self.logger)
});
{
fn transactions_confirmed(&self, header: &BlockHeader, txdata: &TransactionData, height: u32) {
log_debug!(self.logger, "{} provided transactions confirmed at height {} in block {}", txdata.len(), height, header.block_hash());
- self.process_chain_data(header, txdata, |monitor, txdata| {
+ self.process_chain_data(header, None, txdata, |monitor, txdata| {
monitor.transactions_confirmed(
header, txdata, height, &*self.broadcaster, &*self.fee_estimator, &*self.logger)
});
fn best_block_updated(&self, header: &BlockHeader, height: u32) {
log_debug!(self.logger, "New best block {} at height {} provided via best_block_updated", header.block_hash(), height);
- self.process_chain_data(header, &[], |monitor, txdata| {
+ self.process_chain_data(header, Some(height), &[], |monitor, txdata| {
// While in practice there shouldn't be any recursive calls when given empty txdata,
// it's still possible if a chain::Filter implementation returns a transaction.
debug_assert!(txdata.is_empty());
return Err(ChannelMonitorUpdateErr::PermanentFailure)},
hash_map::Entry::Vacant(e) => e,
};
- let persist_res = self.persister.persist_new_channel(funding_outpoint, &monitor);
+ let update_id = MonitorUpdateId::from_new_monitor(&monitor);
+ let mut pending_monitor_updates = Vec::new();
+ let persist_res = self.persister.persist_new_channel(funding_outpoint, &monitor, update_id);
if persist_res.is_err() {
log_error!(self.logger, "Failed to persist new channel data: {:?}", persist_res);
}
if persist_res == Err(ChannelMonitorUpdateErr::PermanentFailure) {
return persist_res;
+ } else if persist_res.is_err() {
+ pending_monitor_updates.push(update_id);
}
{
let funding_txo = monitor.get_funding_txo();
monitor.load_outputs_to_watch(chain_source);
}
}
- entry.insert(MonitorHolder { monitor });
+ entry.insert(MonitorHolder {
+ monitor,
+ pending_monitor_updates: Mutex::new(pending_monitor_updates),
+ channel_perm_failed: AtomicBool::new(false),
+ last_chain_persist_height: AtomicUsize::new(self.highest_chain_height.load(Ordering::Acquire)),
+ });
persist_res
}
}
// Even if updating the monitor returns an error, the monitor's state will
// still be changed. So, persist the updated monitor despite the error.
- let persist_res = self.persister.update_persisted_channel(funding_txo, &update, monitor);
- if let Err(ref e) = persist_res {
+ let update_id = MonitorUpdateId::from_monitor_update(&update);
+ let mut pending_monitor_updates = monitor_state.pending_monitor_updates.lock().unwrap();
+ let persist_res = self.persister.update_persisted_channel(funding_txo, &Some(update), monitor, update_id);
+ if let Err(e) = persist_res {
+ if e == ChannelMonitorUpdateErr::TemporaryFailure {
+ pending_monitor_updates.push(update_id);
+ } else {
+ monitor_state.channel_perm_failed.store(true, Ordering::Release);
+ }
log_error!(self.logger, "Failed to persist channel monitor update: {:?}", e);
}
if update_res.is_err() {
Err(ChannelMonitorUpdateErr::PermanentFailure)
+ } else if monitor_state.channel_perm_failed.load(Ordering::Acquire) {
+ Err(ChannelMonitorUpdateErr::PermanentFailure)
} else {
persist_res
}
}
fn release_pending_monitor_events(&self) -> Vec<MonitorEvent> {
- let mut pending_monitor_events = Vec::new();
+ let mut pending_monitor_events = self.pending_monitor_events.lock().unwrap().split_off(0);
for monitor_state in self.monitors.read().unwrap().values() {
- pending_monitor_events.append(&mut monitor_state.monitor.get_and_clear_pending_monitor_events());
+ let is_pending_monitor_update = monitor_state.has_pending_chainsync_updates(&monitor_state.pending_monitor_updates.lock().unwrap());
+ if is_pending_monitor_update &&
+ monitor_state.last_chain_persist_height.load(Ordering::Acquire) + LATENCY_GRACE_PERIOD_BLOCKS as usize
+ > self.highest_chain_height.load(Ordering::Acquire)
+ {
+ log_info!(self.logger, "A Channel Monitor sync is still in progress, refusing to provide monitor events!");
+ } else {
+ if monitor_state.channel_perm_failed.load(Ordering::Acquire) {
+ // If a `UpdateOrigin::ChainSync` persistence failed with `PermanantFailure`,
+ // we don't really know if the latest `ChannelMonitor` state is on disk or not.
+ // We're supposed to hold monitor updates until the latest state is on disk to
+ // avoid duplicate events, but the user told us persistence is screw-y and may
+ // not complete. We can't hold events forever because we may learn some payment
+ // preimage, so instead we just log and hope the user complied with the
+ // `PermanentFailure` requirements of having at least the local-disk copy
+ // updated.
+ log_info!(self.logger, "A Channel Monitor sync returned PermanentFailure. Returning monitor events but duplicate events may appear after reload!");
+ }
+ if is_pending_monitor_update {
+ log_error!(self.logger, "A ChannelMonitor sync took longer than {} blocks to complete.", LATENCY_GRACE_PERIOD_BLOCKS);
+ log_error!(self.logger, " To avoid funds-loss, we are allowing monitor updates to be released.");
+ log_error!(self.logger, " This may cause duplicate payment events to be generated.");
+ }
+ pending_monitor_events.append(&mut monitor_state.monitor.get_and_clear_pending_monitor_events());
+ }
}
pending_monitor_events
}
/// A monitor event that the Channel's commitment transaction was confirmed.
CommitmentTxConfirmed(OutPoint),
+
+ /// Indicates a [`ChannelMonitor`] update has completed. See
+ /// [`ChannelMonitorUpdateErr::TemporaryFailure`] for more information on how this is used.
+ ///
+ /// [`ChannelMonitorUpdateErr::TemporaryFailure`]: super::ChannelMonitorUpdateErr::TemporaryFailure
+ UpdateCompleted {
+ /// The funding outpoint of the [`ChannelMonitor`] that was updated
+ funding_txo: OutPoint,
+ /// The Update ID from [`ChannelMonitorUpdate::update_id`] which was applied or
+ /// [`ChannelMonitor::get_latest_update_id`].
+ ///
+ /// Note that this should only be set to a given update's ID if all previous updates for the
+ /// same [`ChannelMonitor`] have been applied and persisted.
+ monitor_update_id: u64,
+ },
+
+ /// Indicates a [`ChannelMonitor`] update has failed. See
+ /// [`ChannelMonitorUpdateErr::PermanentFailure`] for more information on how this is used.
+ ///
+ /// [`ChannelMonitorUpdateErr::PermanentFailure`]: super::ChannelMonitorUpdateErr::PermanentFailure
+ UpdateFailed(OutPoint),
}
+impl_writeable_tlv_based_enum_upgradable!(MonitorEvent,
+ // Note that UpdateCompleted and UpdateFailed are currently never serialized to disk as they are
+ // generated only in ChainMonitor
+ (0, UpdateCompleted) => {
+ (0, funding_txo, required),
+ (2, monitor_update_id, required),
+ },
+;
+ (2, HTLCEvent),
+ (4, CommitmentTxConfirmed),
+ (6, UpdateFailed),
+);
/// Simple structure sent back by `chain::Watch` when an HTLC from a forward channel is detected on
/// chain. Used to update the corresponding HTLC in the backward channel. Failing to pass the
payment_preimages: HashMap<PaymentHash, PaymentPreimage>,
+ // Note that `MonitorEvent`s MUST NOT be generated during update processing, only generated
+ // during chain data processing. This prevents a race in `ChainMonitor::update_channel` (and
+ // presumably user implementations thereof as well) where we update the in-memory channel
+ // object, then before the persistence finishes (as it's all under a read-lock), we return
+ // pending events to the user or to the relevant `ChannelManager`. Then, on reload, we'll have
+ // the pre-event state here, but have processed the event in the `ChannelManager`.
+ // Note that because the `event_lock` in `ChainMonitor` is only taken in
+ // block/transaction-connected events and *not* during block/transaction-disconnected events,
+ // we further MUST NOT generate events during block/transaction-disconnection.
pending_monitor_events: Vec<MonitorEvent>,
+
pending_events: Vec<Event>,
// Used to track on-chain events (i.e., transactions part of channels confirmed on chain) on
writer.write_all(&payment_preimage.0[..])?;
}
- writer.write_all(&byte_utils::be64_to_array(self.pending_monitor_events.len() as u64))?;
+ writer.write_all(&(self.pending_monitor_events.iter().filter(|ev| match ev {
+ MonitorEvent::HTLCEvent(_) => true,
+ MonitorEvent::CommitmentTxConfirmed(_) => true,
+ _ => false,
+ }).count() as u64).to_be_bytes())?;
for event in self.pending_monitor_events.iter() {
match event {
MonitorEvent::HTLCEvent(upd) => {
0u8.write(writer)?;
upd.write(writer)?;
},
- MonitorEvent::CommitmentTxConfirmed(_) => 1u8.write(writer)?
+ MonitorEvent::CommitmentTxConfirmed(_) => 1u8.write(writer)?,
+ _ => {}, // Covered in the TLV writes below
}
}
write_tlv_fields!(writer, {
(1, self.funding_spend_confirmed, option),
(3, self.htlcs_resolved_on_chain, vec_type),
+ (5, self.pending_monitor_events, vec_type),
});
Ok(())
res
}
+
+ /// Gets the set of outbound HTLCs which are pending resolution in this channel.
+ /// This is used to reconstruct pending outbound payments on restart in the ChannelManager.
+ pub(crate) fn get_pending_outbound_htlcs(&self) -> HashMap<HTLCSource, HTLCOutputInCommitment> {
+ let mut res = HashMap::new();
+ let us = self.inner.lock().unwrap();
+
+ macro_rules! walk_htlcs {
+ ($holder_commitment: expr, $htlc_iter: expr) => {
+ for (htlc, source) in $htlc_iter {
+ if us.htlcs_resolved_on_chain.iter().any(|v| Some(v.input_idx) == htlc.transaction_output_index) {
+ // We should assert that funding_spend_confirmed is_some() here, but we
+ // have some unit tests which violate HTLC transaction CSVs entirely and
+ // would fail.
+ // TODO: Once tests all connect transactions at consensus-valid times, we
+ // should assert here like we do in `get_claimable_balances`.
+ } else if htlc.offered == $holder_commitment {
+ // If the payment was outbound, check if there's an HTLCUpdate
+ // indicating we have spent this HTLC with a timeout, claiming it back
+ // and awaiting confirmations on it.
+ let htlc_update_confd = us.onchain_events_awaiting_threshold_conf.iter().any(|event| {
+ if let OnchainEvent::HTLCUpdate { input_idx: Some(input_idx), .. } = event.event {
+ // If the HTLC was timed out, we wait for ANTI_REORG_DELAY blocks
+ // before considering it "no longer pending" - this matches when we
+ // provide the ChannelManager an HTLC failure event.
+ Some(input_idx) == htlc.transaction_output_index &&
+ us.best_block.height() >= event.height + ANTI_REORG_DELAY - 1
+ } else if let OnchainEvent::HTLCSpendConfirmation { input_idx, .. } = event.event {
+ // If the HTLC was fulfilled with a preimage, we consider the HTLC
+ // immediately non-pending, matching when we provide ChannelManager
+ // the preimage.
+ Some(input_idx) == htlc.transaction_output_index
+ } else { false }
+ });
+ if !htlc_update_confd {
+ res.insert(source.clone(), htlc.clone());
+ }
+ }
+ }
+ }
+ }
+
+ // We're only concerned with the confirmation count of HTLC transactions, and don't
+ // actually care how many confirmations a commitment transaction may or may not have. Thus,
+ // we look for either a FundingSpendConfirmation event or a funding_spend_confirmed.
+ let confirmed_txid = us.funding_spend_confirmed.or_else(|| {
+ us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| {
+ if let OnchainEvent::FundingSpendConfirmation { .. } = event.event {
+ Some(event.txid)
+ } else { None }
+ })
+ });
+ if let Some(txid) = confirmed_txid {
+ if Some(txid) == us.current_counterparty_commitment_txid || Some(txid) == us.prev_counterparty_commitment_txid {
+ walk_htlcs!(false, us.counterparty_claimable_outpoints.get(&txid).unwrap().iter().filter_map(|(a, b)| {
+ if let &Some(ref source) = b {
+ Some((a, &**source))
+ } else { None }
+ }));
+ } else if txid == us.current_holder_commitment_tx.txid {
+ walk_htlcs!(true, us.current_holder_commitment_tx.htlc_outputs.iter().filter_map(|(a, _, c)| {
+ if let Some(source) = c { Some((a, source)) } else { None }
+ }));
+ } else if let Some(prev_commitment) = &us.prev_holder_signed_commitment_tx {
+ if txid == prev_commitment.txid {
+ walk_htlcs!(true, prev_commitment.htlc_outputs.iter().filter_map(|(a, _, c)| {
+ if let Some(source) = c { Some((a, source)) } else { None }
+ }));
+ }
+ }
+ } else {
+ // If we have not seen a commitment transaction on-chain (ie the channel is not yet
+ // closed), just examine the available counterparty commitment transactions. See docs
+ // on `fail_unbroadcast_htlcs`, below, for justification.
+ macro_rules! walk_counterparty_commitment {
+ ($txid: expr) => {
+ if let Some(ref latest_outpoints) = us.counterparty_claimable_outpoints.get($txid) {
+ for &(ref htlc, ref source_option) in latest_outpoints.iter() {
+ if let &Some(ref source) = source_option {
+ res.insert((**source).clone(), htlc.clone());
+ }
+ }
+ }
+ }
+ }
+ if let Some(ref txid) = us.current_counterparty_commitment_txid {
+ walk_counterparty_commitment!(txid);
+ }
+ if let Some(ref txid) = us.prev_counterparty_commitment_txid {
+ walk_counterparty_commitment!(txid);
+ }
+ }
+
+ res
+ }
}
/// Compares a broadcasted commitment transaction's HTLCs with those in the latest state,
}
let pending_monitor_events_len: u64 = Readable::read(reader)?;
- let mut pending_monitor_events = Vec::with_capacity(cmp::min(pending_monitor_events_len as usize, MAX_ALLOC_SIZE / (32 + 8*3)));
+ let mut pending_monitor_events = Some(
+ Vec::with_capacity(cmp::min(pending_monitor_events_len as usize, MAX_ALLOC_SIZE / (32 + 8*3))));
for _ in 0..pending_monitor_events_len {
let ev = match <u8 as Readable>::read(reader)? {
0 => MonitorEvent::HTLCEvent(Readable::read(reader)?),
1 => MonitorEvent::CommitmentTxConfirmed(funding_info.0),
_ => return Err(DecodeError::InvalidValue)
};
- pending_monitor_events.push(ev);
+ pending_monitor_events.as_mut().unwrap().push(ev);
}
let pending_events_len: u64 = Readable::read(reader)?;
read_tlv_fields!(reader, {
(1, funding_spend_confirmed, option),
(3, htlcs_resolved_on_chain, vec_type),
+ (5, pending_monitor_events, vec_type),
});
let mut secp_ctx = Secp256k1::new();
current_holder_commitment_number,
payment_preimages,
- pending_monitor_events,
+ pending_monitor_events: pending_monitor_events.unwrap(),
pending_events,
onchain_events_awaiting_threshold_conf,
/// our state failed, but is expected to succeed at some point in the future).
///
/// Such a failure will "freeze" a channel, preventing us from revoking old states or
- /// submitting new commitment transactions to the counterparty. Once the update(s) which failed
- /// have been successfully applied, ChannelManager::channel_monitor_updated can be used to
- /// restore the channel to an operational state.
+ /// submitting new commitment transactions to the counterparty. Once the update(s) that failed
+ /// have been successfully applied, a [`MonitorEvent::UpdateCompleted`] event should be returned
+ /// via [`Watch::release_pending_monitor_events`] which will then restore the channel to an
+ /// operational state.
///
/// Note that a given ChannelManager will *never* re-generate a given ChannelMonitorUpdate. If
/// you return a TemporaryFailure you must ensure that it is written to disk safely before
/// the channel which would invalidate previous ChannelMonitors are not made when a channel has
/// been "frozen".
///
- /// Note that even if updates made after TemporaryFailure succeed you must still call
- /// channel_monitor_updated to ensure you have the latest monitor and re-enable normal channel
- /// operation.
+ /// Note that even if updates made after TemporaryFailure succeed you must still provide a
+ /// [`MonitorEvent::UpdateCompleted`] to ensure you have the latest monitor and re-enable
+ /// normal channel operation. Note that this is normally generated through a call to
+ /// [`ChainMonitor::channel_monitor_updated`].
///
- /// Note that the update being processed here will not be replayed for you when you call
- /// ChannelManager::channel_monitor_updated, so you must store the update itself along
- /// with the persisted ChannelMonitor on your own local disk prior to returning a
+ /// Note that the update being processed here will not be replayed for you when you return a
+ /// [`MonitorEvent::UpdateCompleted`] event via [`Watch::release_pending_monitor_events`], so
+ /// you must store the update itself on your own local disk prior to returning a
/// TemporaryFailure. You may, of course, employ a journaling approach, storing only the
/// ChannelMonitorUpdate on disk without updating the monitor itself, replaying the journal at
/// reload-time.
/// For deployments where a copy of ChannelMonitors and other local state are backed up in a
/// remote location (with local copies persisted immediately), it is anticipated that all
/// updates will return TemporaryFailure until the remote copies could be updated.
+ ///
+ /// [`ChainMonitor::channel_monitor_updated`]: chainmonitor::ChainMonitor::channel_monitor_updated
TemporaryFailure,
/// Used to indicate no further channel monitor updates will be allowed (eg we've moved on to a
/// different watchtower and cannot update with all watchtowers that were previously informed
/// Returns any monitor events since the last call. Subsequent calls must only return new
/// events.
+ ///
+ /// Note that after any block- or transaction-connection calls to a [`ChannelMonitor`], no
+ /// further events may be returned here until the [`ChannelMonitor`] has been fully persisted
+ /// to disk.
+ ///
+ /// For details on asynchronous [`ChannelMonitor`] updating and returning
+ /// [`MonitorEvent::UpdateCompleted`] here, see [`ChannelMonitorUpdateErr::TemporaryFailure`].
fn release_pending_monitor_events(&self) -> Vec<MonitorEvent>;
}
}
chanmon_cfgs[0].persister.set_update_ret(Ok(()));
- let (outpoint, latest_update) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
- nodes[0].node.channel_monitor_updated(&outpoint, latest_update);
+ let (outpoint, latest_update, _) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
+ nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[0], 0);
let mut events_2 = nodes[0].node.get_and_clear_pending_msg_events();
let events_3 = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events_3.len(), 1);
match events_3[0] {
- Event::PaymentSent { ref payment_preimage, ref payment_hash } => {
+ Event::PaymentSent { ref payment_preimage, ref payment_hash, .. } => {
assert_eq!(*payment_preimage, payment_preimage_1);
assert_eq!(*payment_hash, payment_hash_1);
},
// Now fix monitor updating...
chanmon_cfgs[0].persister.set_update_ret(Ok(()));
- let (outpoint, latest_update) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
- nodes[0].node.channel_monitor_updated(&outpoint, latest_update);
+ let (outpoint, latest_update, _) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
+ nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[0], 0);
macro_rules! disconnect_reconnect_peers { () => { {
let events_3 = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events_3.len(), 1);
match events_3[0] {
- Event::PaymentSent { ref payment_preimage, ref payment_hash } => {
+ Event::PaymentSent { ref payment_preimage, ref payment_hash, .. } => {
assert_eq!(*payment_preimage, payment_preimage_1);
assert_eq!(*payment_hash, payment_hash_1);
},
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
chanmon_cfgs[1].persister.set_update_ret(Ok(()));
- let (outpoint, latest_update) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
- nodes[1].node.channel_monitor_updated(&outpoint, latest_update);
+ let (outpoint, latest_update, _) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
+ nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[1], 0);
let responses = nodes[1].node.get_and_clear_pending_msg_events();
assert_eq!(responses.len(), 2);
}
chanmon_cfgs[0].persister.set_update_ret(Ok(()));
- let (outpoint, latest_update) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
- nodes[0].node.channel_monitor_updated(&outpoint, latest_update);
+ let (outpoint, latest_update, _) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
+ nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[0], 0);
let final_raa = get_event_msg!(nodes[0], MessageSendEvent::SendRevokeAndACK, nodes[1].node.get_our_node_id());
check_added_monitors!(nodes[1], 1);
chanmon_cfgs[1].persister.set_update_ret(Ok(()));
- let (outpoint, latest_update) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
- nodes[1].node.channel_monitor_updated(&outpoint, latest_update);
+ let (outpoint, latest_update, _) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
+ nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
check_added_monitors!(nodes[1], 0);
expect_pending_htlcs_forwardable!(nodes[1]);
check_added_monitors!(nodes[0], 1);
chanmon_cfgs[0].persister.set_update_ret(Ok(()));
- let (outpoint, latest_update) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
- nodes[0].node.channel_monitor_updated(&outpoint, latest_update);
+ let (outpoint, latest_update, _) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
+ nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[0], 0);
let as_update_raa = get_revoke_commit_msgs!(nodes[0], nodes[1].node.get_our_node_id());
// Restore monitor updating, ensuring we immediately get a fail-back update and a
// update_add update.
chanmon_cfgs[1].persister.set_update_ret(Ok(()));
- let (outpoint, latest_update) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&chan_2.2).unwrap().clone();
- nodes[1].node.channel_monitor_updated(&outpoint, latest_update);
+ let (outpoint, latest_update, _) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&chan_2.2).unwrap().clone();
+ nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[1], 0);
expect_pending_htlcs_forwardable!(nodes[1]);
check_added_monitors!(nodes[1], 1);
.contents.flags & 2, 0); // The "disabled" bit should be unset as we just reconnected
chanmon_cfgs[1].persister.set_update_ret(Ok(()));
- let (outpoint, latest_update) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&chan_1.2).unwrap().clone();
- nodes[1].node.channel_monitor_updated(&outpoint, latest_update);
+ let (outpoint, latest_update, _) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&chan_1.2).unwrap().clone();
+ nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[1], 0);
updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
check_added_monitors!(nodes[1], 1);
chanmon_cfgs[1].persister.set_update_ret(Ok(()));
- let (outpoint, latest_update) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
- nodes[1].node.channel_monitor_updated(&outpoint, latest_update);
+ let (outpoint, latest_update, _) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
+ nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
// nodes[1] should be AwaitingRAA here!
check_added_monitors!(nodes[1], 0);
let bs_responses = get_revoke_commit_msgs!(nodes[1], nodes[0].node.get_our_node_id());
// Now un-fail the monitor, which will result in B sending its original commitment update,
// receiving the commitment update from A, and the resulting commitment dances.
chanmon_cfgs[1].persister.set_update_ret(Ok(()));
- let (outpoint, latest_update) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
- nodes[1].node.channel_monitor_updated(&outpoint, latest_update);
+ let (outpoint, latest_update, _) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
+ nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[1], 0);
let bs_msgs = nodes[1].node.get_and_clear_pending_msg_events();
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
- Event::PaymentSent { ref payment_preimage, ref payment_hash } => {
+ Event::PaymentSent { ref payment_preimage, ref payment_hash, .. } => {
assert_eq!(*payment_preimage, payment_preimage_1);
assert_eq!(*payment_hash, payment_hash_1);
},
let _as_channel_update = get_event_msg!(nodes[0], MessageSendEvent::SendChannelUpdate, nodes[1].node.get_our_node_id());
chanmon_cfgs[1].persister.set_update_ret(Ok(()));
- let (outpoint, latest_update) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
- nodes[1].node.channel_monitor_updated(&outpoint, latest_update);
+ let (outpoint, latest_update, _) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
+ nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[1], 0);
let bs_responses = get_revoke_commit_msgs!(nodes[1], nodes[0].node.get_our_node_id());
nodes[1].logger.assert_log("lightning::ln::channelmanager".to_string(), "Previous monitor update failure prevented generation of RAA".to_string(), 1);
chanmon_cfgs[1].persister.set_update_ret(Ok(()));
- let (outpoint, latest_update) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
- nodes[1].node.channel_monitor_updated(&outpoint, latest_update);
+ let (outpoint, latest_update, _) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
+ nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[1], 0);
expect_pending_htlcs_forwardable!(nodes[1]);
commitment_signed_dance!(nodes[1], nodes[2], payment_event.commitment_msg, false, true);
// Now restore monitor updating on the 0<->1 channel and claim the funds on B.
- let (outpoint, latest_update) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&chan_1.2).unwrap().clone();
- nodes[1].node.channel_monitor_updated(&outpoint, latest_update);
+ let (outpoint, latest_update, _) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&chan_1.2).unwrap().clone();
+ nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[1], 0);
let bs_fulfill_update = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
nodes[1].logger.assert_log("lightning::ln::channelmanager".to_string(), "Failed to update ChannelMonitor".to_string(), 1);
chanmon_cfgs[1].persister.set_update_ret(Ok(()));
- let (outpoint, latest_update) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&chan_1.2).unwrap().clone();
- nodes[1].node.channel_monitor_updated(&outpoint, latest_update);
+ let (outpoint, latest_update, _) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&chan_1.2).unwrap().clone();
+ nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[1], 0);
let bs_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
nodes[1].logger.assert_log("lightning::ln::channelmanager".to_string(), "Temporary failure claiming HTLC, treating as success: Failed to update ChannelMonitor".to_string(), 1);
chanmon_cfgs[1].persister.set_update_ret(Ok(()));
- let (outpoint, latest_update) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
- nodes[1].node.channel_monitor_updated(&outpoint, latest_update);
+ let (outpoint, latest_update, _) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
+ nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[1], 0);
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
- Event::PaymentSent { ref payment_preimage, ref payment_hash } => {
+ Event::PaymentSent { ref payment_preimage, ref payment_hash, .. } => {
assert_eq!(*payment_preimage, payment_preimage_1);
assert_eq!(*payment_hash, payment_hash_1);
},
check_added_monitors!(nodes[0], 1);
assert!(nodes[0].node.get_and_clear_pending_events().is_empty());
chanmon_cfgs[0].persister.set_update_ret(Ok(()));
- let (outpoint, latest_update) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
- nodes[0].node.channel_monitor_updated(&outpoint, latest_update);
+ let (outpoint, latest_update, _) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
+ nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[0], 0);
let events = nodes[0].node.get_and_clear_pending_events();
}
chanmon_cfgs[1].persister.set_update_ret(Ok(()));
- let (outpoint, latest_update) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
- nodes[1].node.channel_monitor_updated(&outpoint, latest_update);
+ let (outpoint, latest_update, _) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
+ nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[1], 0);
let (channel_id, (announcement, as_update, bs_update)) = if !confirm_a_first {
// And check that, after we successfully update the monitor for chan_2 we can pass the second
// HTLC along to nodes[3] and claim the whole payment back to nodes[0].
- let (outpoint, latest_update) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&chan_2_id).unwrap().clone();
- nodes[0].node.channel_monitor_updated(&outpoint, latest_update);
+ let (outpoint, latest_update, _) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&chan_2_id).unwrap().clone();
+ nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1);
pass_along_path(&nodes[0], &[&nodes[2], &nodes[3]], 200_000, payment_hash.clone(), Some(payment_secret), events.pop().unwrap(), true, None);
// If we finish updating the monitor, we should free the holding cell right away (this did
// not occur prior to #756).
chanmon_cfgs[0].persister.set_update_ret(Ok(()));
- let (funding_txo, mon_id) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&chan_id).unwrap().clone();
- nodes[0].node.channel_monitor_updated(&funding_txo, mon_id);
+ let (funding_txo, mon_id, _) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&chan_id).unwrap().clone();
+ nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(funding_txo, mon_id);
// New outbound messages should be generated immediately upon a call to
// get_and_clear_pending_msg_events (but not before).
chanmon_cfgs[0].persister.set_update_ret(Ok(()));
chanmon_cfgs[1].persister.set_update_ret(Ok(()));
- let (outpoint, latest_update) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
- nodes[0].node.channel_monitor_updated(&outpoint, latest_update);
+ let (outpoint, latest_update, _) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
+ nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
nodes[1].node.handle_closing_signed(&nodes[0].node.get_our_node_id(), &get_event_msg!(nodes[0], MessageSendEvent::SendClosingSigned, nodes[1].node.get_our_node_id()));
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
chanmon_cfgs[1].persister.set_update_ret(Ok(()));
- let (outpoint, latest_update) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
- nodes[1].node.channel_monitor_updated(&outpoint, latest_update);
+ let (outpoint, latest_update, _) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
+ nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
nodes[0].node.handle_closing_signed(&nodes[1].node.get_our_node_id(), &get_event_msg!(nodes[1], MessageSendEvent::SendClosingSigned, nodes[0].node.get_our_node_id()));
let (_, closing_signed_a) = get_closing_signed_broadcast!(nodes[0].node, nodes[1].node.get_our_node_id());
// `claim_funds` results in a ChannelMonitorUpdate.
assert!(nodes[1].node.claim_funds(payment_preimage_1));
check_added_monitors!(nodes[1], 1);
- let (funding_tx, latest_update_1) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
+ let (funding_tx, latest_update_1, _) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
chanmon_cfgs[1].persister.set_update_ret(Err(ChannelMonitorUpdateErr::TemporaryFailure));
// Previously, this would've panicked due to a double-call to `Channel::monitor_update_failed`,
check_added_monitors!(nodes[1], 1);
chanmon_cfgs[1].persister.set_update_ret(Ok(()));
- let (_, latest_update_2) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
- nodes[1].node.channel_monitor_updated(&funding_tx, latest_update_1);
+ let (_, latest_update_2, _) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
+ nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(funding_tx, latest_update_1);
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
check_added_monitors!(nodes[1], 0);
- nodes[1].node.channel_monitor_updated(&funding_tx, latest_update_2);
+ nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(funding_tx, latest_update_2);
// Complete the first HTLC.
let events = nodes[1].node.get_and_clear_pending_msg_events();
DuplicateClaim {},
}
+/// The return value of `revoke_and_ack` on success, primarily updates to other channels or HTLC
+/// state.
+pub(super) struct RAAUpdates {
+ pub commitment_update: Option<msgs::CommitmentUpdate>,
+ pub accepted_htlcs: Vec<(PendingHTLCInfo, u64)>,
+ pub failed_htlcs: Vec<(HTLCSource, PaymentHash, HTLCFailReason)>,
+ pub finalized_claimed_htlcs: Vec<HTLCSource>,
+ pub monitor_update: ChannelMonitorUpdate,
+ pub holding_cell_failed_htlcs: Vec<(HTLCSource, PaymentHash)>,
+}
+
+/// The return value of `monitor_updating_restored`
+pub(super) struct MonitorRestoreUpdates {
+ pub raa: Option<msgs::RevokeAndACK>,
+ pub commitment_update: Option<msgs::CommitmentUpdate>,
+ pub order: RAACommitmentOrder,
+ pub accepted_htlcs: Vec<(PendingHTLCInfo, u64)>,
+ pub failed_htlcs: Vec<(HTLCSource, PaymentHash, HTLCFailReason)>,
+ pub finalized_claimed_htlcs: Vec<HTLCSource>,
+ pub funding_broadcastable: Option<Transaction>,
+ pub funding_locked: Option<msgs::FundingLocked>,
+}
+
/// If the majority of the channels funds are to the fundee and the initiator holds only just
/// enough funds to cover their reserve value, channels are at risk of getting "stuck". Because the
/// initiator controls the feerate, if they then go to increase the channel fee, they may have no
monitor_pending_commitment_signed: bool,
monitor_pending_forwards: Vec<(PendingHTLCInfo, u64)>,
monitor_pending_failures: Vec<(HTLCSource, PaymentHash, HTLCFailReason)>,
+ monitor_pending_finalized_fulfills: Vec<HTLCSource>,
// pending_update_fee is filled when sending and receiving update_fee.
//
monitor_pending_commitment_signed: false,
monitor_pending_forwards: Vec::new(),
monitor_pending_failures: Vec::new(),
+ monitor_pending_finalized_fulfills: Vec::new(),
#[cfg(debug_assertions)]
holder_max_commitment_tx_output: Mutex::new((channel_value_satoshis * 1000 - push_msat, push_msat)),
monitor_pending_commitment_signed: false,
monitor_pending_forwards: Vec::new(),
monitor_pending_failures: Vec::new(),
+ monitor_pending_finalized_fulfills: Vec::new(),
#[cfg(debug_assertions)]
holder_max_commitment_tx_output: Mutex::new((msg.push_msat, msg.funding_satoshis * 1000 - msg.push_msat)),
/// waiting on this revoke_and_ack. The generation of this new commitment_signed may also fail,
/// generating an appropriate error *after* the channel state has been updated based on the
/// revoke_and_ack message.
- pub fn revoke_and_ack<L: Deref>(&mut self, msg: &msgs::RevokeAndACK, logger: &L) -> Result<(Option<msgs::CommitmentUpdate>, Vec<(PendingHTLCInfo, u64)>, Vec<(HTLCSource, PaymentHash, HTLCFailReason)>, ChannelMonitorUpdate, Vec<(HTLCSource, PaymentHash)>), ChannelError>
+ pub fn revoke_and_ack<L: Deref>(&mut self, msg: &msgs::RevokeAndACK, logger: &L) -> Result<RAAUpdates, ChannelError>
where L::Target: Logger,
{
if (self.channel_state & (ChannelState::ChannelFunded as u32)) != (ChannelState::ChannelFunded as u32) {
log_trace!(logger, "Updating HTLCs on receipt of RAA in channel {}...", log_bytes!(self.channel_id()));
let mut to_forward_infos = Vec::new();
let mut revoked_htlcs = Vec::new();
+ let mut finalized_claimed_htlcs = Vec::new();
let mut update_fail_htlcs = Vec::new();
let mut update_fail_malformed_htlcs = Vec::new();
let mut require_commitment = false;
if let Some(reason) = fail_reason.clone() { // We really want take() here, but, again, non-mut ref :(
revoked_htlcs.push((htlc.source.clone(), htlc.payment_hash, reason));
} else {
+ finalized_claimed_htlcs.push(htlc.source.clone());
// They fulfilled, so we sent them money
value_to_self_msat_diff -= htlc.amount_msat as i64;
}
}
self.monitor_pending_forwards.append(&mut to_forward_infos);
self.monitor_pending_failures.append(&mut revoked_htlcs);
+ self.monitor_pending_finalized_fulfills.append(&mut finalized_claimed_htlcs);
log_debug!(logger, "Received a valid revoke_and_ack for channel {} but awaiting a monitor update resolution to reply.", log_bytes!(self.channel_id()));
- return Ok((None, Vec::new(), Vec::new(), monitor_update, Vec::new()))
+ return Ok(RAAUpdates {
+ commitment_update: None, finalized_claimed_htlcs: Vec::new(),
+ accepted_htlcs: Vec::new(), failed_htlcs: Vec::new(),
+ monitor_update,
+ holding_cell_failed_htlcs: Vec::new()
+ });
}
match self.free_holding_cell_htlcs(logger)? {
self.latest_monitor_update_id = monitor_update.update_id;
monitor_update.updates.append(&mut additional_update.updates);
- Ok((Some(commitment_update), to_forward_infos, revoked_htlcs, monitor_update, htlcs_to_fail))
+ Ok(RAAUpdates {
+ commitment_update: Some(commitment_update),
+ finalized_claimed_htlcs,
+ accepted_htlcs: to_forward_infos,
+ failed_htlcs: revoked_htlcs,
+ monitor_update,
+ holding_cell_failed_htlcs: htlcs_to_fail
+ })
},
(None, htlcs_to_fail) => {
if require_commitment {
log_debug!(logger, "Received a valid revoke_and_ack for channel {}. Responding with a commitment update with {} HTLCs failed.",
log_bytes!(self.channel_id()), update_fail_htlcs.len() + update_fail_malformed_htlcs.len());
- Ok((Some(msgs::CommitmentUpdate {
- update_add_htlcs: Vec::new(),
- update_fulfill_htlcs: Vec::new(),
- update_fail_htlcs,
- update_fail_malformed_htlcs,
- update_fee: None,
- commitment_signed
- }), to_forward_infos, revoked_htlcs, monitor_update, htlcs_to_fail))
+ Ok(RAAUpdates {
+ commitment_update: Some(msgs::CommitmentUpdate {
+ update_add_htlcs: Vec::new(),
+ update_fulfill_htlcs: Vec::new(),
+ update_fail_htlcs,
+ update_fail_malformed_htlcs,
+ update_fee: None,
+ commitment_signed
+ }),
+ finalized_claimed_htlcs,
+ accepted_htlcs: to_forward_infos, failed_htlcs: revoked_htlcs,
+ monitor_update, holding_cell_failed_htlcs: htlcs_to_fail
+ })
} else {
log_debug!(logger, "Received a valid revoke_and_ack for channel {} with no reply necessary.", log_bytes!(self.channel_id()));
- Ok((None, to_forward_infos, revoked_htlcs, monitor_update, htlcs_to_fail))
+ Ok(RAAUpdates {
+ commitment_update: None,
+ finalized_claimed_htlcs,
+ accepted_htlcs: to_forward_infos, failed_htlcs: revoked_htlcs,
+ monitor_update, holding_cell_failed_htlcs: htlcs_to_fail
+ })
}
}
}
/// which failed. The messages which were generated from that call which generated the
/// monitor update failure must *not* have been sent to the remote end, and must instead
/// have been dropped. They will be regenerated when monitor_updating_restored is called.
- pub fn monitor_update_failed(&mut self, resend_raa: bool, resend_commitment: bool, mut pending_forwards: Vec<(PendingHTLCInfo, u64)>, mut pending_fails: Vec<(HTLCSource, PaymentHash, HTLCFailReason)>) {
+ pub fn monitor_update_failed(&mut self, resend_raa: bool, resend_commitment: bool,
+ mut pending_forwards: Vec<(PendingHTLCInfo, u64)>,
+ mut pending_fails: Vec<(HTLCSource, PaymentHash, HTLCFailReason)>,
+ mut pending_finalized_claimed_htlcs: Vec<HTLCSource>
+ ) {
self.monitor_pending_revoke_and_ack |= resend_raa;
self.monitor_pending_commitment_signed |= resend_commitment;
self.monitor_pending_forwards.append(&mut pending_forwards);
self.monitor_pending_failures.append(&mut pending_fails);
+ self.monitor_pending_finalized_fulfills.append(&mut pending_finalized_claimed_htlcs);
self.channel_state |= ChannelState::MonitorUpdateFailed as u32;
}
/// Indicates that the latest ChannelMonitor update has been committed by the client
/// successfully and we should restore normal operation. Returns messages which should be sent
/// to the remote side.
- pub fn monitor_updating_restored<L: Deref>(&mut self, logger: &L) -> (Option<msgs::RevokeAndACK>, Option<msgs::CommitmentUpdate>, RAACommitmentOrder, Vec<(PendingHTLCInfo, u64)>, Vec<(HTLCSource, PaymentHash, HTLCFailReason)>, Option<Transaction>, Option<msgs::FundingLocked>) where L::Target: Logger {
+ pub fn monitor_updating_restored<L: Deref>(&mut self, logger: &L) -> MonitorRestoreUpdates where L::Target: Logger {
assert_eq!(self.channel_state & ChannelState::MonitorUpdateFailed as u32, ChannelState::MonitorUpdateFailed as u32);
self.channel_state &= !(ChannelState::MonitorUpdateFailed as u32);
})
} else { None };
- let mut forwards = Vec::new();
- mem::swap(&mut forwards, &mut self.monitor_pending_forwards);
- let mut failures = Vec::new();
- mem::swap(&mut failures, &mut self.monitor_pending_failures);
+ let mut accepted_htlcs = Vec::new();
+ mem::swap(&mut accepted_htlcs, &mut self.monitor_pending_forwards);
+ let mut failed_htlcs = Vec::new();
+ mem::swap(&mut failed_htlcs, &mut self.monitor_pending_failures);
+ let mut finalized_claimed_htlcs = Vec::new();
+ mem::swap(&mut finalized_claimed_htlcs, &mut self.monitor_pending_finalized_fulfills);
if self.channel_state & (ChannelState::PeerDisconnected as u32) != 0 {
self.monitor_pending_revoke_and_ack = false;
self.monitor_pending_commitment_signed = false;
- return (None, None, RAACommitmentOrder::RevokeAndACKFirst, forwards, failures, funding_broadcastable, funding_locked);
+ return MonitorRestoreUpdates {
+ raa: None, commitment_update: None, order: RAACommitmentOrder::RevokeAndACKFirst,
+ accepted_htlcs, failed_htlcs, finalized_claimed_htlcs, funding_broadcastable, funding_locked
+ };
}
let raa = if self.monitor_pending_revoke_and_ack {
log_bytes!(self.channel_id()), if funding_broadcastable.is_some() { "a funding broadcastable, " } else { "" },
if commitment_update.is_some() { "a" } else { "no" }, if raa.is_some() { "an" } else { "no" },
match order { RAACommitmentOrder::CommitmentFirst => "commitment", RAACommitmentOrder::RevokeAndACKFirst => "RAA"});
- (raa, commitment_update, order, forwards, failures, funding_broadcastable, funding_locked)
+ MonitorRestoreUpdates {
+ raa, commitment_update, order, accepted_htlcs, failed_htlcs, finalized_claimed_htlcs, funding_broadcastable, funding_locked
+ }
}
pub fn update_fee<F: Deref>(&mut self, fee_estimator: &F, msg: &msgs::UpdateFee) -> Result<(), ChannelError>
(5, self.config, required),
(7, self.shutdown_scriptpubkey, option),
(9, self.target_closing_feerate_sats_per_kw, option),
+ (11, self.monitor_pending_finalized_fulfills, vec_type),
});
Ok(())
let mut announcement_sigs = None;
let mut target_closing_feerate_sats_per_kw = None;
+ let mut monitor_pending_finalized_fulfills = Some(Vec::new());
read_tlv_fields!(reader, {
(0, announcement_sigs, option),
(1, minimum_depth, option),
(5, config, option), // Note that if none is provided we will *not* overwrite the existing one.
(7, shutdown_scriptpubkey, option),
(9, target_closing_feerate_sats_per_kw, option),
+ (11, monitor_pending_finalized_fulfills, vec_type),
});
let mut secp_ctx = Secp256k1::new();
monitor_pending_commitment_signed,
monitor_pending_forwards,
monitor_pending_failures,
+ monitor_pending_finalized_fulfills: monitor_pending_finalized_fulfills.unwrap(),
pending_update_fee,
holding_cell_update_fee,
session_priv: SecretKey::from_slice(&hex::decode("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(),
first_hop_htlc_msat: 548,
payment_id: PaymentId([42; 32]),
+ payment_secret: None,
+ payee: None,
}
});
use ln::{PaymentHash, PaymentPreimage, PaymentSecret};
use ln::channel::{Channel, ChannelError, ChannelUpdateStatus, UpdateFulfillCommitFetch};
use ln::features::{InitFeatures, NodeFeatures};
-use routing::router::{Route, RouteHop};
+use routing::router::{Payee, Route, RouteHop, RoutePath, RouteParameters};
use ln::msgs;
use ln::msgs::NetAddress;
use ln::onion_utils;
}
/// Tracks the inbound corresponding to an outbound HTLC
-#[derive(Clone, PartialEq)]
+#[derive(Clone, Hash, PartialEq, Eq)]
pub(crate) struct HTLCPreviousHopData {
short_channel_id: u64,
htlc_id: u64,
}
}
/// Tracks the inbound corresponding to an outbound HTLC
-#[derive(Clone, PartialEq)]
+#[allow(clippy::derive_hash_xor_eq)] // Our Hash is faithful to the data, we just don't have SecretKey::hash
+#[derive(Clone, PartialEq, Eq)]
pub(crate) enum HTLCSource {
PreviousHopData(HTLCPreviousHopData),
OutboundRoute {
/// doing a double-pass on route when we get a failure back
first_hop_htlc_msat: u64,
payment_id: PaymentId,
+ payment_secret: Option<PaymentSecret>,
+ payee: Option<Payee>,
},
}
+#[allow(clippy::derive_hash_xor_eq)] // Our Hash is faithful to the data, we just don't have SecretKey::hash
+impl core::hash::Hash for HTLCSource {
+ fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) {
+ match self {
+ HTLCSource::PreviousHopData(prev_hop_data) => {
+ 0u8.hash(hasher);
+ prev_hop_data.hash(hasher);
+ },
+ HTLCSource::OutboundRoute { path, session_priv, payment_id, payment_secret, first_hop_htlc_msat, payee } => {
+ 1u8.hash(hasher);
+ path.hash(hasher);
+ session_priv[..].hash(hasher);
+ payment_id.hash(hasher);
+ payment_secret.hash(hasher);
+ first_hop_htlc_msat.hash(hasher);
+ payee.hash(hasher);
+ },
+ }
+ }
+}
#[cfg(test)]
impl HTLCSource {
pub fn dummy() -> Self {
session_priv: SecretKey::from_slice(&[1; 32]).unwrap(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([2; 32]),
+ payment_secret: None,
+ payee: None,
}
}
}
payment_hash: PaymentHash,
payment_secret: Option<PaymentSecret>,
pending_amt_msat: u64,
+ /// Used to track the fee paid. Only present if the payment was serialized on 0.0.103+.
+ pending_fee_msat: Option<u64>,
/// The total payment amount across all paths, used to verify that a retry is not overpaying.
total_msat: u64,
/// Our best known block height at the time this payment was initiated.
starting_block_height: u32,
},
+ /// When a pending payment is fulfilled, we continue tracking it until all pending HTLCs have
+ /// been resolved. This ensures we don't look up pending payments in ChannelMonitors on restart
+ /// and add a pending payment that was already fulfilled.
+ Fulfilled {
+ session_privs: HashSet<[u8; 32]>,
+ },
}
impl PendingOutboundPayment {
- fn remove(&mut self, session_priv: &[u8; 32], part_amt_msat: u64) -> bool {
+ fn is_retryable(&self) -> bool {
+ match self {
+ PendingOutboundPayment::Retryable { .. } => true,
+ _ => false,
+ }
+ }
+ fn is_fulfilled(&self) -> bool {
+ match self {
+ PendingOutboundPayment::Fulfilled { .. } => true,
+ _ => false,
+ }
+ }
+ fn get_pending_fee_msat(&self) -> Option<u64> {
+ match self {
+ PendingOutboundPayment::Retryable { pending_fee_msat, .. } => pending_fee_msat.clone(),
+ _ => None,
+ }
+ }
+
+ fn mark_fulfilled(&mut self) {
+ let mut session_privs = HashSet::new();
+ core::mem::swap(&mut session_privs, match self {
+ PendingOutboundPayment::Legacy { session_privs } |
+ PendingOutboundPayment::Retryable { session_privs, .. } |
+ PendingOutboundPayment::Fulfilled { session_privs }
+ => session_privs
+ });
+ *self = PendingOutboundPayment::Fulfilled { session_privs };
+ }
+
+ /// panics if path is None and !self.is_fulfilled
+ fn remove(&mut self, session_priv: &[u8; 32], path: Option<&Vec<RouteHop>>) -> bool {
let remove_res = match self {
PendingOutboundPayment::Legacy { session_privs } |
- PendingOutboundPayment::Retryable { session_privs, .. } => {
+ PendingOutboundPayment::Retryable { session_privs, .. } |
+ PendingOutboundPayment::Fulfilled { session_privs } => {
session_privs.remove(session_priv)
}
};
if remove_res {
- if let PendingOutboundPayment::Retryable { ref mut pending_amt_msat, .. } = self {
- *pending_amt_msat -= part_amt_msat;
+ if let PendingOutboundPayment::Retryable { ref mut pending_amt_msat, ref mut pending_fee_msat, .. } = self {
+ let path = path.expect("Fulfilling a payment should always come with a path");
+ let path_last_hop = path.last().expect("Outbound payments must have had a valid path");
+ *pending_amt_msat -= path_last_hop.fee_msat;
+ if let Some(fee_msat) = pending_fee_msat.as_mut() {
+ *fee_msat -= path.get_path_fees();
+ }
}
}
remove_res
}
- fn insert(&mut self, session_priv: [u8; 32], part_amt_msat: u64) -> bool {
+ fn insert(&mut self, session_priv: [u8; 32], path: &Vec<RouteHop>) -> bool {
let insert_res = match self {
PendingOutboundPayment::Legacy { session_privs } |
PendingOutboundPayment::Retryable { session_privs, .. } => {
session_privs.insert(session_priv)
}
+ PendingOutboundPayment::Fulfilled { .. } => false
};
if insert_res {
- if let PendingOutboundPayment::Retryable { ref mut pending_amt_msat, .. } = self {
- *pending_amt_msat += part_amt_msat;
+ if let PendingOutboundPayment::Retryable { ref mut pending_amt_msat, ref mut pending_fee_msat, .. } = self {
+ let path_last_hop = path.last().expect("Outbound payments must have had a valid path");
+ *pending_amt_msat += path_last_hop.fee_msat;
+ if let Some(fee_msat) = pending_fee_msat.as_mut() {
+ *fee_msat += path.get_path_fees();
+ }
}
}
insert_res
fn remaining_parts(&self) -> usize {
match self {
PendingOutboundPayment::Legacy { session_privs } |
- PendingOutboundPayment::Retryable { session_privs, .. } => {
+ PendingOutboundPayment::Retryable { session_privs, .. } |
+ PendingOutboundPayment::Fulfilled { session_privs } => {
session_privs.len()
}
}
($self: ident, $err: expr, $channel_state: expr, $entry: expr, $action_type: path, $resend_raa: expr, $resend_commitment: expr) => {
handle_monitor_err!($self, $err, $channel_state, $entry, $action_type, $resend_raa, $resend_commitment, Vec::new(), Vec::new())
};
- ($self: ident, $err: expr, $short_to_id: expr, $chan: expr, $action_type: path, $resend_raa: expr, $resend_commitment: expr, $failed_forwards: expr, $failed_fails: expr, $chan_id: expr) => {
+ ($self: ident, $err: expr, $short_to_id: expr, $chan: expr, $action_type: path, $resend_raa: expr, $resend_commitment: expr, $failed_forwards: expr, $failed_fails: expr, $failed_finalized_fulfills: expr, $chan_id: expr) => {
match $err {
ChannelMonitorUpdateErr::PermanentFailure => {
log_error!($self.logger, "Closing channel {} due to monitor update ChannelMonitorUpdateErr::PermanentFailure", log_bytes!($chan_id[..]));
(res, true)
},
ChannelMonitorUpdateErr::TemporaryFailure => {
- log_info!($self.logger, "Disabling channel {} due to monitor update TemporaryFailure. On restore will send {} and process {} forwards and {} fails",
+ log_info!($self.logger, "Disabling channel {} due to monitor update TemporaryFailure. On restore will send {} and process {} forwards, {} fails, and {} fulfill finalizations",
log_bytes!($chan_id[..]),
if $resend_commitment && $resend_raa {
match $action_type {
else if $resend_raa { "RAA" }
else { "nothing" },
(&$failed_forwards as &Vec<(PendingHTLCInfo, u64)>).len(),
- (&$failed_fails as &Vec<(HTLCSource, PaymentHash, HTLCFailReason)>).len());
+ (&$failed_fails as &Vec<(HTLCSource, PaymentHash, HTLCFailReason)>).len(),
+ (&$failed_finalized_fulfills as &Vec<HTLCSource>).len());
if !$resend_commitment {
debug_assert!($action_type == RAACommitmentOrder::RevokeAndACKFirst || !$resend_raa);
}
if !$resend_raa {
debug_assert!($action_type == RAACommitmentOrder::CommitmentFirst || !$resend_commitment);
}
- $chan.monitor_update_failed($resend_raa, $resend_commitment, $failed_forwards, $failed_fails);
+ $chan.monitor_update_failed($resend_raa, $resend_commitment, $failed_forwards, $failed_fails, $failed_finalized_fulfills);
(Err(MsgHandleErrInternal::from_chan_no_close(ChannelError::Ignore("Failed to update ChannelMonitor".to_owned()), *$chan_id)), false)
},
}
};
- ($self: ident, $err: expr, $channel_state: expr, $entry: expr, $action_type: path, $resend_raa: expr, $resend_commitment: expr, $failed_forwards: expr, $failed_fails: expr) => { {
- let (res, drop) = handle_monitor_err!($self, $err, $channel_state.short_to_id, $entry.get_mut(), $action_type, $resend_raa, $resend_commitment, $failed_forwards, $failed_fails, $entry.key());
+ ($self: ident, $err: expr, $channel_state: expr, $entry: expr, $action_type: path, $resend_raa: expr, $resend_commitment: expr, $failed_forwards: expr, $failed_fails: expr, $failed_finalized_fulfills: expr) => { {
+ let (res, drop) = handle_monitor_err!($self, $err, $channel_state.short_to_id, $entry.get_mut(), $action_type, $resend_raa, $resend_commitment, $failed_forwards, $failed_fails, $failed_finalized_fulfills, $entry.key());
if drop {
$entry.remove_entry();
}
res
} };
+ ($self: ident, $err: expr, $channel_state: expr, $entry: expr, $action_type: path, $resend_raa: expr, $resend_commitment: expr, $failed_forwards: expr, $failed_fails: expr) => {
+ handle_monitor_err!($self, $err, $channel_state, $entry, $action_type, $resend_raa, $resend_commitment, $failed_forwards, $failed_fails, Vec::new());
+ }
}
macro_rules! return_monitor_err {
if let Some(monitor_update) = monitor_update {
if let Err(e) = self.chain_monitor.update_channel(chan_entry.get().get_funding_txo().unwrap(), monitor_update) {
let (result, is_permanent) =
- handle_monitor_err!(self, e, channel_state.short_to_id, chan_entry.get_mut(), RAACommitmentOrder::CommitmentFirst, false, false, Vec::new(), Vec::new(), chan_entry.key());
+ handle_monitor_err!(self, e, channel_state.short_to_id, chan_entry.get_mut(), RAACommitmentOrder::CommitmentFirst, false, false, Vec::new(), Vec::new(), Vec::new(), chan_entry.key());
if is_permanent {
remove_channel!(channel_state, chan_entry);
break result;
}
// Only public for testing, this should otherwise never be called direcly
- pub(crate) fn send_payment_along_path(&self, path: &Vec<RouteHop>, payment_hash: &PaymentHash, payment_secret: &Option<PaymentSecret>, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option<PaymentPreimage>) -> Result<(), APIError> {
+ pub(crate) fn send_payment_along_path(&self, path: &Vec<RouteHop>, payee: &Option<Payee>, payment_hash: &PaymentHash, payment_secret: &Option<PaymentSecret>, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option<PaymentPreimage>) -> Result<(), APIError> {
log_trace!(self.logger, "Attempting to send payment for path with next hop {}", path.first().unwrap().short_channel_id);
let prng_seed = self.keys_manager.get_secure_random_bytes();
let session_priv_bytes = self.keys_manager.get_secure_random_bytes();
let err: Result<(), _> = loop {
let mut channel_lock = self.channel_state.lock().unwrap();
+
+ let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();
+ let payment_entry = pending_outbounds.entry(payment_id);
+ if let hash_map::Entry::Occupied(payment) = &payment_entry {
+ if !payment.get().is_retryable() {
+ return Err(APIError::RouteError {
+ err: "Payment already completed"
+ });
+ }
+ }
+
let id = match channel_lock.short_to_id.get(&path.first().unwrap().short_channel_id) {
None => return Err(APIError::ChannelUnavailable{err: "No channel available with first hop!".to_owned()}),
Some(id) => id.clone(),
session_priv: session_priv.clone(),
first_hop_htlc_msat: htlc_msat,
payment_id,
+ payment_secret: payment_secret.clone(),
+ payee: payee.clone(),
}, onion_packet, &self.logger),
channel_state, chan);
- let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();
- let payment = pending_outbounds.entry(payment_id).or_insert_with(|| PendingOutboundPayment::Retryable {
+ let payment = payment_entry.or_insert_with(|| PendingOutboundPayment::Retryable {
session_privs: HashSet::new(),
pending_amt_msat: 0,
+ pending_fee_msat: Some(0),
payment_hash: *payment_hash,
payment_secret: *payment_secret,
starting_block_height: self.best_block.read().unwrap().height(),
total_msat: total_value,
});
- assert!(payment.insert(session_priv_bytes, path.last().unwrap().fee_msat));
+ assert!(payment.insert(session_priv_bytes, path));
send_res
} {
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, payment_id, &keysend_preimage));
+ results.push(self.send_payment_along_path(&path, &route.payee, &payment_hash, payment_secret, total_value, cur_height, payment_id, &keysend_preimage));
}
let mut has_ok = false;
let mut has_err = false;
return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError {
err: "Unable to retry payments that were initially sent on LDK versions prior to 0.0.102".to_string()
}))
- }
+ },
+ PendingOutboundPayment::Fulfilled { .. } => {
+ return Err(PaymentSendFailure::ParameterError(APIError::RouteError {
+ err: "Payment already completed"
+ }));
+ },
}
} else {
return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError {
let ret_err = match res {
Ok(Some((update_fee, commitment_signed, monitor_update))) => {
if let Err(e) = self.chain_monitor.update_channel(chan.get_funding_txo().unwrap(), monitor_update) {
- let (res, drop) = handle_monitor_err!(self, e, short_to_id, chan, RAACommitmentOrder::CommitmentFirst, false, true, Vec::new(), Vec::new(), chan_id);
+ let (res, drop) = handle_monitor_err!(self, e, short_to_id, chan, RAACommitmentOrder::CommitmentFirst, false, true, Vec::new(), Vec::new(), Vec::new(), chan_id);
if drop { retain_channel = false; }
res
} else {
self.fail_htlc_backwards_internal(channel_state,
htlc_src, &payment_hash, HTLCFailReason::Reason { failure_code, data: onion_failure_data});
},
- HTLCSource::OutboundRoute { session_priv, payment_id, path, .. } => {
+ HTLCSource::OutboundRoute { session_priv, payment_id, path, payee, .. } => {
let mut session_priv_bytes = [0; 32];
session_priv_bytes.copy_from_slice(&session_priv[..]);
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
- if payment.get_mut().remove(&session_priv_bytes, path.last().unwrap().fee_msat) {
+ if payment.get_mut().remove(&session_priv_bytes, Some(&path)) && !payment.get().is_fulfilled() {
+ let retry = if let Some(payee_data) = payee {
+ let path_last_hop = path.last().expect("Outbound payments must have had a valid path");
+ Some(RouteParameters {
+ payee: payee_data,
+ final_value_msat: path_last_hop.fee_msat,
+ final_cltv_expiry_delta: path_last_hop.cltv_expiry_delta,
+ })
+ } else { None };
self.pending_events.lock().unwrap().push(
events::Event::PaymentPathFailed {
+ payment_id: Some(payment_id),
payment_hash,
rejected_by_dest: false,
network_update: None,
all_paths_failed: payment.get().remaining_parts() == 0,
path: path.clone(),
short_channel_id: None,
+ retry,
#[cfg(test)]
error_code: None,
#[cfg(test)]
// from block_connected which may run during initialization prior to the chain_monitor
// being fully configured. See the docs for `ChannelManagerReadArgs` for more.
match source {
- HTLCSource::OutboundRoute { ref path, session_priv, payment_id, .. } => {
+ HTLCSource::OutboundRoute { ref path, session_priv, payment_id, ref payee, .. } => {
let mut session_priv_bytes = [0; 32];
session_priv_bytes.copy_from_slice(&session_priv[..]);
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
let mut all_paths_failed = false;
- if let hash_map::Entry::Occupied(mut sessions) = outbounds.entry(payment_id) {
- if !sessions.get_mut().remove(&session_priv_bytes, path.last().unwrap().fee_msat) {
+ if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
+ if !payment.get_mut().remove(&session_priv_bytes, Some(&path)) {
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
return;
}
- if sessions.get().remaining_parts() == 0 {
+ if payment.get().is_fulfilled() {
+ log_trace!(self.logger, "Received failure of HTLC with payment_hash {} after payment completion", log_bytes!(payment_hash.0));
+ return;
+ }
+ if payment.get().remaining_parts() == 0 {
all_paths_failed = true;
}
} else {
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
return;
}
- log_trace!(self.logger, "Failing outbound payment HTLC with payment_hash {}", log_bytes!(payment_hash.0));
mem::drop(channel_state_lock);
+ let retry = if let Some(payee_data) = payee {
+ let path_last_hop = path.last().expect("Outbound payments must have had a valid path");
+ Some(RouteParameters {
+ payee: payee_data.clone(),
+ final_value_msat: path_last_hop.fee_msat,
+ final_cltv_expiry_delta: path_last_hop.cltv_expiry_delta,
+ })
+ } else { None };
+ log_trace!(self.logger, "Failing outbound payment HTLC with payment_hash {}", log_bytes!(payment_hash.0));
match &onion_error {
&HTLCFailReason::LightningError { ref err } => {
#[cfg(test)]
// next-hop is needlessly blaming us!
self.pending_events.lock().unwrap().push(
events::Event::PaymentPathFailed {
+ payment_id: Some(payment_id),
payment_hash: payment_hash.clone(),
rejected_by_dest: !payment_retryable,
network_update,
all_paths_failed,
path: path.clone(),
short_channel_id,
+ retry,
#[cfg(test)]
error_code: onion_error_code,
#[cfg(test)]
// channel here as we apparently can't relay through them anyway.
self.pending_events.lock().unwrap().push(
events::Event::PaymentPathFailed {
+ payment_id: Some(payment_id),
payment_hash: payment_hash.clone(),
rejected_by_dest: path.len() == 1,
network_update: None,
all_paths_failed,
path: path.clone(),
short_channel_id: Some(path.first().unwrap().short_channel_id),
+ retry,
#[cfg(test)]
error_code: Some(*failure_code),
#[cfg(test)]
} else { unreachable!(); }
}
+ fn finalize_claims(&self, mut sources: Vec<HTLCSource>) {
+ for source in sources.drain(..) {
+ if let HTLCSource::OutboundRoute { session_priv, payment_id, .. } = source {
+ let mut session_priv_bytes = [0; 32];
+ session_priv_bytes.copy_from_slice(&session_priv[..]);
+ let mut outbounds = self.pending_outbound_payments.lock().unwrap();
+ if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
+ assert!(payment.get().is_fulfilled());
+ payment.get_mut().remove(&session_priv_bytes, None);
+ if payment.get().remaining_parts() == 0 {
+ payment.remove();
+ }
+ }
+ }
+ }
+ }
+
fn claim_funds_internal(&self, mut channel_state_lock: MutexGuard<ChannelHolder<Signer>>, source: HTLCSource, payment_preimage: PaymentPreimage, forwarded_htlc_value_msat: Option<u64>, from_onchain: bool) {
match source {
HTLCSource::OutboundRoute { session_priv, payment_id, path, .. } => {
let mut session_priv_bytes = [0; 32];
session_priv_bytes.copy_from_slice(&session_priv[..]);
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
- let found_payment = if let Some(mut sessions) = outbounds.remove(&payment_id) {
- sessions.remove(&session_priv_bytes, path.last().unwrap().fee_msat)
- } else { false };
- if found_payment {
- let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
- self.pending_events.lock().unwrap().push(
- events::Event::PaymentSent {
- payment_preimage,
- payment_hash: payment_hash
+ if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
+ let found_payment = !payment.get().is_fulfilled();
+ let fee_paid_msat = payment.get().get_pending_fee_msat();
+ payment.get_mut().mark_fulfilled();
+ if from_onchain {
+ // We currently immediately remove HTLCs which were fulfilled on-chain.
+ // This could potentially lead to removing a pending payment too early,
+ // with a reorg of one block causing us to re-add the fulfilled payment on
+ // restart.
+ // TODO: We should have a second monitor event that informs us of payments
+ // irrevocably fulfilled.
+ payment.get_mut().remove(&session_priv_bytes, Some(&path));
+ if payment.get().remaining_parts() == 0 {
+ payment.remove();
}
- );
+ }
+ if found_payment {
+ let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
+ self.pending_events.lock().unwrap().push(
+ events::Event::PaymentSent {
+ payment_id: Some(payment_id),
+ payment_preimage,
+ payment_hash: payment_hash,
+ fee_paid_msat,
+ }
+ );
+ }
} else {
log_trace!(self.logger, "Received duplicative fulfill for HTLC with payment_preimage {}", log_bytes!(payment_preimage.0));
}
self.our_network_pubkey.clone()
}
- /// Restores a single, given channel to normal operation after a
- /// ChannelMonitorUpdateErr::TemporaryFailure was returned from a channel monitor update
- /// operation.
- ///
- /// All ChannelMonitor updates up to and including highest_applied_update_id must have been
- /// fully committed in every copy of the given channels' ChannelMonitors.
- ///
- /// Note that there is no effect to calling with a highest_applied_update_id other than the
- /// current latest ChannelMonitorUpdate and one call to this function after multiple
- /// ChannelMonitorUpdateErr::TemporaryFailures is fine. The highest_applied_update_id field
- /// exists largely only to prevent races between this and concurrent update_monitor calls.
- ///
- /// Thus, the anticipated use is, at a high level:
- /// 1) You register a chain::Watch with this ChannelManager,
- /// 2) it stores each update to disk, and begins updating any remote (eg watchtower) copies of
- /// said ChannelMonitors as it can, returning ChannelMonitorUpdateErr::TemporaryFailures
- /// any time it cannot do so instantly,
- /// 3) update(s) are applied to each remote copy of a ChannelMonitor,
- /// 4) once all remote copies are updated, you call this function with the update_id that
- /// completed, and once it is the latest the Channel will be re-enabled.
- pub fn channel_monitor_updated(&self, funding_txo: &OutPoint, highest_applied_update_id: u64) {
+ fn channel_monitor_updated(&self, funding_txo: &OutPoint, highest_applied_update_id: u64) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
let chan_restoration_res;
- let mut pending_failures = {
+ let (mut pending_failures, finalized_claims) = {
let mut channel_lock = self.channel_state.lock().unwrap();
let channel_state = &mut *channel_lock;
let mut channel = match channel_state.by_id.entry(funding_txo.to_channel_id()) {
return;
}
- let (raa, commitment_update, order, pending_forwards, pending_failures, funding_broadcastable, funding_locked) = channel.get_mut().monitor_updating_restored(&self.logger);
- let channel_update = if funding_locked.is_some() && channel.get().is_usable() && !channel.get().should_announce() {
+ let updates = channel.get_mut().monitor_updating_restored(&self.logger);
+ let channel_update = if updates.funding_locked.is_some() && channel.get().is_usable() && !channel.get().should_announce() {
// We only send a channel_update in the case where we are just now sending a
// funding_locked and the channel is in a usable state. Further, we rely on the
// normal announcement_signatures process to send a channel_update for public
msg: self.get_channel_update_for_unicast(channel.get()).unwrap(),
})
} else { None };
- chan_restoration_res = handle_chan_restoration_locked!(self, channel_lock, channel_state, channel, raa, commitment_update, order, None, pending_forwards, funding_broadcastable, funding_locked);
+ chan_restoration_res = handle_chan_restoration_locked!(self, channel_lock, channel_state, channel, updates.raa, updates.commitment_update, updates.order, None, updates.accepted_htlcs, updates.funding_broadcastable, updates.funding_locked);
if let Some(upd) = channel_update {
channel_state.pending_msg_events.push(upd);
}
- pending_failures
+ (updates.failed_htlcs, updates.finalized_claimed_htlcs)
};
post_handle_chan_restoration!(self, chan_restoration_res);
+ self.finalize_claims(finalized_claims);
for failure in pending_failures.drain(..) {
self.fail_htlc_backwards_internal(self.channel_state.lock().unwrap(), failure.0, &failure.1, failure.2);
}
// hasn't persisted to disk yet - we can't lose money on a transaction that we haven't
// accepted payment from yet. We do, however, need to wait to send our funding_locked
// until we have persisted our monitor.
- chan.monitor_update_failed(false, false, Vec::new(), Vec::new());
+ chan.monitor_update_failed(false, false, Vec::new(), Vec::new(), Vec::new());
},
}
}
if let Some(monitor_update) = monitor_update {
if let Err(e) = self.chain_monitor.update_channel(chan_entry.get().get_funding_txo().unwrap(), monitor_update) {
let (result, is_permanent) =
- handle_monitor_err!(self, e, channel_state.short_to_id, chan_entry.get_mut(), RAACommitmentOrder::CommitmentFirst, false, false, Vec::new(), Vec::new(), chan_entry.key());
+ handle_monitor_err!(self, e, channel_state.short_to_id, chan_entry.get_mut(), RAACommitmentOrder::CommitmentFirst, false, false, Vec::new(), Vec::new(), Vec::new(), chan_entry.key());
if is_permanent {
remove_channel!(channel_state, chan_entry);
break result;
break Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!".to_owned(), msg.channel_id));
}
let was_frozen_for_monitor = chan.get().is_awaiting_monitor_update();
- let (commitment_update, pending_forwards, pending_failures, monitor_update, htlcs_to_fail_in) =
- break_chan_entry!(self, chan.get_mut().revoke_and_ack(&msg, &self.logger), channel_state, chan);
- htlcs_to_fail = htlcs_to_fail_in;
- if let Err(e) = self.chain_monitor.update_channel(chan.get().get_funding_txo().unwrap(), monitor_update) {
+ let raa_updates = break_chan_entry!(self,
+ chan.get_mut().revoke_and_ack(&msg, &self.logger), channel_state, chan);
+ htlcs_to_fail = raa_updates.holding_cell_failed_htlcs;
+ if let Err(e) = self.chain_monitor.update_channel(chan.get().get_funding_txo().unwrap(), raa_updates.monitor_update) {
if was_frozen_for_monitor {
- assert!(commitment_update.is_none() && pending_forwards.is_empty() && pending_failures.is_empty());
+ assert!(raa_updates.commitment_update.is_none());
+ assert!(raa_updates.accepted_htlcs.is_empty());
+ assert!(raa_updates.failed_htlcs.is_empty());
+ assert!(raa_updates.finalized_claimed_htlcs.is_empty());
break Err(MsgHandleErrInternal::ignore_no_close("Previous monitor update failure prevented responses to RAA".to_owned()));
} else {
- if let Err(e) = handle_monitor_err!(self, e, channel_state, chan, RAACommitmentOrder::CommitmentFirst, false, commitment_update.is_some(), pending_forwards, pending_failures) {
+ if let Err(e) = handle_monitor_err!(self, e, channel_state, chan,
+ RAACommitmentOrder::CommitmentFirst, false,
+ raa_updates.commitment_update.is_some(),
+ raa_updates.accepted_htlcs, raa_updates.failed_htlcs,
+ raa_updates.finalized_claimed_htlcs) {
break Err(e);
} else { unreachable!(); }
}
}
- if let Some(updates) = commitment_update {
+ if let Some(updates) = raa_updates.commitment_update {
channel_state.pending_msg_events.push(events::MessageSendEvent::UpdateHTLCs {
node_id: counterparty_node_id.clone(),
updates,
});
}
- break Ok((pending_forwards, pending_failures, chan.get().get_short_channel_id().expect("RAA should only work on a short-id-available channel"), chan.get().get_funding_txo().unwrap()))
+ break Ok((raa_updates.accepted_htlcs, raa_updates.failed_htlcs,
+ raa_updates.finalized_claimed_htlcs,
+ chan.get().get_short_channel_id()
+ .expect("RAA should only work on a short-id-available channel"),
+ chan.get().get_funding_txo().unwrap()))
},
hash_map::Entry::Vacant(_) => break Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id))
}
};
self.fail_holding_cell_htlcs(htlcs_to_fail, msg.channel_id);
match res {
- Ok((pending_forwards, mut pending_failures, short_channel_id, channel_outpoint)) => {
+ Ok((pending_forwards, mut pending_failures, finalized_claim_htlcs,
+ short_channel_id, channel_outpoint)) =>
+ {
for failure in pending_failures.drain(..) {
self.fail_htlc_backwards_internal(self.channel_state.lock().unwrap(), failure.0, &failure.1, failure.2);
}
self.forward_htlcs(&mut [(short_channel_id, channel_outpoint, pending_forwards)]);
+ self.finalize_claims(finalized_claim_htlcs);
Ok(())
},
Err(e) => Err(e)
self.fail_htlc_backwards_internal(self.channel_state.lock().unwrap(), htlc_update.source, &htlc_update.payment_hash, HTLCFailReason::Reason { failure_code: 0x4000 | 8, data: Vec::new() });
}
},
- MonitorEvent::CommitmentTxConfirmed(funding_outpoint) => {
+ MonitorEvent::CommitmentTxConfirmed(funding_outpoint) |
+ MonitorEvent::UpdateFailed(funding_outpoint) => {
let mut channel_lock = self.channel_state.lock().unwrap();
let channel_state = &mut *channel_lock;
let by_id = &mut channel_state.by_id;
msg: update
});
}
- self.issue_channel_close_events(&chan, ClosureReason::CommitmentTxConfirmed);
+ let reason = if let MonitorEvent::UpdateFailed(_) = monitor_event {
+ ClosureReason::ProcessingError { err: "Failed to persist ChannelMonitor update during chain sync".to_string() }
+ } else {
+ ClosureReason::CommitmentTxConfirmed
+ };
+ self.issue_channel_close_events(&chan, reason);
pending_msg_events.push(events::MessageSendEvent::HandleError {
node_id: chan.get_counterparty_node_id(),
action: msgs::ErrorAction::SendErrorMessage {
});
}
},
+ MonitorEvent::UpdateCompleted { funding_txo, monitor_update_id } => {
+ self.channel_monitor_updated(&funding_txo, monitor_update_id);
+ },
}
}
has_pending_monitor_events
}
+ /// In chanmon_consistency_target, we'd like to be able to restore monitor updating without
+ /// handling all pending events (i.e. not PendingHTLCsForwardable). Thus, we expose monitor
+ /// update events as a separate process method here.
+ #[cfg(feature = "fuzztarget")]
+ pub fn process_monitor_events(&self) {
+ self.process_pending_monitor_events();
+ }
+
/// Check the holding cell in each channel and free any pending HTLCs in them if possible.
/// Returns whether there were any updates such as if pending HTLCs were freed or a monitor
/// update was applied.
if let Some((commitment_update, monitor_update)) = commitment_opt {
if let Err(e) = self.chain_monitor.update_channel(chan.get_funding_txo().unwrap(), monitor_update) {
has_monitor_update = true;
- let (res, close_channel) = handle_monitor_err!(self, e, short_to_id, chan, RAACommitmentOrder::CommitmentFirst, false, true, Vec::new(), Vec::new(), channel_id);
+ let (res, close_channel) = handle_monitor_err!(self, e, short_to_id, chan, RAACommitmentOrder::CommitmentFirst, false, true, Vec::new(), Vec::new(), Vec::new(), channel_id);
handle_errors.push((chan.get_counterparty_node_id(), res));
if close_channel { return false; }
} else {
let mut first_hop_htlc_msat: u64 = 0;
let mut path = Some(Vec::new());
let mut payment_id = None;
+ let mut payment_secret = None;
+ let mut payee = None;
read_tlv_fields!(reader, {
(0, session_priv, required),
(1, payment_id, option),
(2, first_hop_htlc_msat, required),
+ (3, payment_secret, option),
(4, path, vec_type),
+ (5, payee, option),
});
if payment_id.is_none() {
// For backwards compat, if there was no payment_id written, use the session_priv bytes
first_hop_htlc_msat: first_hop_htlc_msat,
path: path.unwrap(),
payment_id: payment_id.unwrap(),
+ payment_secret,
+ payee,
})
}
1 => Ok(HTLCSource::PreviousHopData(Readable::read(reader)?)),
impl Writeable for HTLCSource {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::io::Error> {
match self {
- HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id } => {
+ HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id, payment_secret, payee } => {
0u8.write(writer)?;
let payment_id_opt = Some(payment_id);
write_tlv_fields!(writer, {
(0, session_priv, required),
(1, payment_id_opt, option),
(2, first_hop_htlc_msat, required),
+ (3, payment_secret, option),
(4, path, vec_type),
+ (5, payee, option),
});
}
HTLCSource::PreviousHopData(ref field) => {
(8, min_value_msat, required),
});
-impl_writeable_tlv_based_enum!(PendingOutboundPayment,
+impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
(0, Legacy) => {
(0, session_privs, required),
},
+ (1, Fulfilled) => {
+ (0, session_privs, required),
+ },
(2, Retryable) => {
(0, session_privs, required),
+ (1, pending_fee_msat, option),
(2, payment_hash, required),
(4, payment_secret, option),
(6, total_msat, required),
(8, pending_amt_msat, required),
(10, starting_block_height, required),
},
-;);
+);
impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable for ChannelManager<Signer, M, T, K, F, L>
where M::Target: chain::Watch<Signer>,
// For backwards compat, write the session privs and their total length.
let mut num_pending_outbounds_compat: u64 = 0;
for (_, outbound) in pending_outbound_payments.iter() {
- num_pending_outbounds_compat += outbound.remaining_parts() as u64;
+ if !outbound.is_fulfilled() {
+ num_pending_outbounds_compat += outbound.remaining_parts() as u64;
+ }
}
num_pending_outbounds_compat.write(writer)?;
for (_, outbound) in pending_outbound_payments.iter() {
session_priv.write(writer)?;
}
}
+ PendingOutboundPayment::Fulfilled { .. } => {},
}
}
PendingOutboundPayment::Legacy { session_privs } |
PendingOutboundPayment::Retryable { session_privs, .. } => {
pending_outbound_payments_no_retry.insert(*id, session_privs.clone());
- }
+ },
+ _ => {},
}
}
write_tlv_fields!(writer, {
///
/// At a high-level, the process for deserializing a ChannelManager and resuming normal operation
/// is:
-/// 1) Deserialize all stored ChannelMonitors.
-/// 2) Deserialize the ChannelManager by filling in this struct and calling:
-/// <(BlockHash, ChannelManager)>::read(reader, args)
-/// This may result in closing some Channels if the ChannelMonitor is newer than the stored
-/// ChannelManager state to ensure no loss of funds. Thus, transactions may be broadcasted.
-/// 3) If you are not fetching full blocks, register all relevant ChannelMonitor outpoints the same
-/// way you would handle a `chain::Filter` call using ChannelMonitor::get_outputs_to_watch() and
-/// ChannelMonitor::get_funding_txo().
-/// 4) Reconnect blocks on your ChannelMonitors.
-/// 5) Disconnect/connect blocks on the ChannelManager.
-/// 6) Move the ChannelMonitors into your local chain::Watch.
+/// 1) Deserialize all stored [`ChannelMonitor`]s.
+/// 2) Deserialize the [`ChannelManager`] by filling in this struct and calling:
+/// `<(BlockHash, ChannelManager)>::read(reader, args)`
+/// This may result in closing some channels if the [`ChannelMonitor`] is newer than the stored
+/// [`ChannelManager`] state to ensure no loss of funds. Thus, transactions may be broadcasted.
+/// 3) If you are not fetching full blocks, register all relevant [`ChannelMonitor`] outpoints the
+/// same way you would handle a [`chain::Filter`] call using
+/// [`ChannelMonitor::get_outputs_to_watch`] and [`ChannelMonitor::get_funding_txo`].
+/// 4) Reconnect blocks on your [`ChannelMonitor`]s.
+/// 5) Disconnect/connect blocks on the [`ChannelManager`].
+/// 6) Re-persist the [`ChannelMonitor`]s to ensure the latest state is on disk.
+/// Note that if you're using a [`ChainMonitor`] for your [`chain::Watch`] implementation, you
+/// will likely accomplish this as a side-effect of calling [`chain::Watch::watch_channel`] in
+/// the next step.
+/// 7) Move the [`ChannelMonitor`]s into your local [`chain::Watch`]. If you're using a
+/// [`ChainMonitor`], this is done by calling [`chain::Watch::watch_channel`].
///
-/// Note that the ordering of #4-6 is not of importance, however all three must occur before you
-/// call any other methods on the newly-deserialized ChannelManager.
+/// Note that the ordering of #4-7 is not of importance, however all four must occur before you
+/// call any other methods on the newly-deserialized [`ChannelManager`].
///
/// Note that because some channels may be closed during deserialization, it is critical that you
/// always deserialize only the latest version of a ChannelManager and ChannelMonitors available to
/// broadcast), and then later deserialize a newer version of the same ChannelManager (which will
/// not force-close the same channels but consider them live), you may end up revoking a state for
/// which you've already broadcasted the transaction.
+///
+/// [`ChainMonitor`]: crate::chain::chainmonitor::ChainMonitor
pub struct ChannelManagerReadArgs<'a, Signer: 'a + Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
where M::Target: chain::Watch<Signer>,
T::Target: BroadcasterInterface,
outbounds.insert(id, PendingOutboundPayment::Legacy { session_privs });
}
pending_outbound_payments = Some(outbounds);
+ } else {
+ // If we're tracking pending payments, ensure we haven't lost any by looking at the
+ // ChannelMonitor data for any channels for which we do not have authorative state
+ // (i.e. those for which we just force-closed above or we otherwise don't have a
+ // corresponding `Channel` at all).
+ // This avoids several edge-cases where we would otherwise "forget" about pending
+ // payments which are still in-flight via their on-chain state.
+ // We only rebuild the pending payments map if we were most recently serialized by
+ // 0.0.102+
+ for (_, monitor) in args.channel_monitors {
+ if by_id.get(&monitor.get_funding_txo().0.to_channel_id()).is_none() {
+ for (htlc_source, htlc) in monitor.get_pending_outbound_htlcs() {
+ if let HTLCSource::OutboundRoute { payment_id, session_priv, path, payment_secret, .. } = htlc_source {
+ if path.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 mut session_priv_bytes = [0; 32];
+ session_priv_bytes[..].copy_from_slice(&session_priv[..]);
+ match pending_outbound_payments.as_mut().unwrap().entry(payment_id) {
+ hash_map::Entry::Occupied(mut entry) => {
+ let newly_added = entry.get_mut().insert(session_priv_bytes, &path);
+ log_info!(args.logger, "{} a pending payment path for {} msat for session priv {} on an existing pending payment with payment hash {}",
+ 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();
+ entry.insert(PendingOutboundPayment::Retryable {
+ session_privs: [session_priv_bytes].iter().map(|a| *a).collect(),
+ payment_hash: htlc.payment_hash,
+ payment_secret,
+ pending_amt_msat: path_amt,
+ pending_fee_msat: Some(path_fee),
+ total_msat: path_amt,
+ starting_block_height: best_block_height,
+ });
+ log_info!(args.logger, "Added a pending payment for {} msat with payment hash {} for path with session priv {}",
+ path_amt, log_bytes!(htlc.payment_hash.0), log_bytes!(session_priv_bytes));
+ }
+ }
+ }
+ }
+ }
+ }
}
let mut secp_ctx = Secp256k1::new();
use core::time::Duration;
use ln::{PaymentPreimage, PaymentHash, PaymentSecret};
use ln::channelmanager::{PaymentId, PaymentSendFailure};
- use ln::features::{InitFeatures, InvoiceFeatures};
+ use ln::features::InitFeatures;
use ln::functional_test_utils::*;
use ln::msgs;
use ln::msgs::ChannelMessageHandler;
- use routing::router::{get_keysend_route, get_route};
+ use routing::router::{Payee, RouteParameters, find_route};
use routing::scorer::Scorer;
use util::errors::APIError;
use util::events::{Event, MessageSendEvent, MessageSendEventsProvider};
- use util::test_utils;
#[cfg(feature = "std")]
#[test]
// Use the utility function send_payment_along_path to send the payment with MPP data which
// indicates there are more HTLCs coming.
let cur_height = CHAN_CONFIRM_DEPTH + 1; // route_payment calls send_payment, which adds 1 to the current height. So we do the same here to match.
- nodes[0].node.send_payment_along_path(&route.paths[0], &our_payment_hash, &Some(payment_secret), 200_000, cur_height, payment_id, &None).unwrap();
+ nodes[0].node.send_payment_along_path(&route.paths[0], &route.payee, &our_payment_hash, &Some(payment_secret), 200_000, cur_height, payment_id, &None).unwrap();
check_added_monitors!(nodes[0], 1);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1);
expect_payment_failed!(nodes[0], our_payment_hash, true);
// Send the second half of the original MPP payment.
- nodes[0].node.send_payment_along_path(&route.paths[0], &our_payment_hash, &Some(payment_secret), 200_000, cur_height, payment_id, &None).unwrap();
+ nodes[0].node.send_payment_along_path(&route.paths[0], &route.payee, &our_payment_hash, &Some(payment_secret), 200_000, cur_height, payment_id, &None).unwrap();
check_added_monitors!(nodes[0], 1);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1);
// further events will be generated for subsequence path successes.
let events = nodes[0].node.get_and_clear_pending_events();
match events[0] {
- Event::PaymentSent { payment_preimage: ref preimage, payment_hash: ref hash } => {
+ Event::PaymentSent { payment_id: ref id, payment_preimage: ref preimage, payment_hash: ref hash, .. } => {
+ assert_eq!(Some(payment_id), *id);
assert_eq!(payment_preimage, *preimage);
assert_eq!(our_payment_hash, *hash);
},
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
- let logger = test_utils::TestLogger::new();
let scorer = Scorer::new(0);
// To start (1), send a regular payment but don't claim it.
let (payment_preimage, payment_hash, _) = route_payment(&nodes[0], &expected_route, 100_000);
// Next, attempt a keysend payment and make sure it fails.
- let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph, &expected_route.last().unwrap().node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100_000, TEST_FINAL_CLTV, &logger, &scorer).unwrap();
+ let params = RouteParameters {
+ payee: Payee::for_keysend(expected_route.last().unwrap().node.get_our_node_id()),
+ final_value_msat: 100_000,
+ final_cltv_expiry_delta: TEST_FINAL_CLTV,
+ };
+ let route = find_route(
+ &nodes[0].node.get_our_node_id(), ¶ms,
+ &nodes[0].net_graph_msg_handler.network_graph, None, nodes[0].logger, &scorer
+ ).unwrap();
nodes[0].node.send_spontaneous_payment(&route, Some(payment_preimage)).unwrap();
check_added_monitors!(nodes[0], 1);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
// To start (2), send a keysend payment but don't claim it.
let payment_preimage = PaymentPreimage([42; 32]);
- let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph, &expected_route.last().unwrap().node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100_000, TEST_FINAL_CLTV, &logger, &scorer).unwrap();
+ let route = find_route(
+ &nodes[0].node.get_our_node_id(), ¶ms,
+ &nodes[0].net_graph_msg_handler.network_graph, None, nodes[0].logger, &scorer
+ ).unwrap();
let (payment_hash, _) = nodes[0].node.send_spontaneous_payment(&route, Some(payment_preimage)).unwrap();
check_added_monitors!(nodes[0], 1);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
nodes[1].node.peer_connected(&payer_pubkey, &msgs::Init { features: InitFeatures::known() });
let _chan = create_chan_between_nodes(&nodes[0], &nodes[1], InitFeatures::known(), InitFeatures::known());
+ let params = RouteParameters {
+ payee: Payee::for_keysend(payee_pubkey),
+ final_value_msat: 10000,
+ final_cltv_expiry_delta: 40,
+ };
let network_graph = &nodes[0].net_graph_msg_handler.network_graph;
let first_hops = nodes[0].node.list_usable_channels();
let scorer = Scorer::new(0);
- let route = get_keysend_route(&payer_pubkey, network_graph, &payee_pubkey,
- Some(&first_hops.iter().collect::<Vec<_>>()), &vec![], 10000, 40,
- nodes[0].logger, &scorer).unwrap();
+ let route = find_route(
+ &payer_pubkey, ¶ms, network_graph, Some(&first_hops.iter().collect::<Vec<_>>()),
+ nodes[0].logger, &scorer
+ ).unwrap();
let test_preimage = PaymentPreimage([42; 32]);
let mismatch_payment_hash = PaymentHash([43; 32]);
nodes[1].node.peer_connected(&payer_pubkey, &msgs::Init { features: InitFeatures::known() });
let _chan = create_chan_between_nodes(&nodes[0], &nodes[1], InitFeatures::known(), InitFeatures::known());
+ let params = RouteParameters {
+ payee: Payee::for_keysend(payee_pubkey),
+ final_value_msat: 10000,
+ final_cltv_expiry_delta: 40,
+ };
let network_graph = &nodes[0].net_graph_msg_handler.network_graph;
let first_hops = nodes[0].node.list_usable_channels();
let scorer = Scorer::new(0);
- let route = get_keysend_route(&payer_pubkey, network_graph, &payee_pubkey,
- Some(&first_hops.iter().collect::<Vec<_>>()), &vec![], 10000, 40,
- nodes[0].logger, &scorer).unwrap();
+ let route = find_route(
+ &payer_pubkey, ¶ms, network_graph, Some(&first_hops.iter().collect::<Vec<_>>()),
+ nodes[0].logger, &scorer
+ ).unwrap();
let test_preimage = PaymentPreimage([42; 32]);
let test_secret = PaymentSecret([43; 32]);
use ln::functional_test_utils::*;
use ln::msgs::{ChannelMessageHandler, Init};
use routing::network_graph::NetworkGraph;
- use routing::router::get_route;
+ use routing::router::{Payee, get_route};
use routing::scorer::Scorer;
use util::test_utils;
use util::config::UserConfig;
macro_rules! send_payment {
($node_a: expr, $node_b: expr) => {
let usable_channels = $node_a.list_usable_channels();
+ let payee = Payee::new($node_b.get_our_node_id())
+ .with_features(InvoiceFeatures::known());
let scorer = Scorer::new(0);
- let route = get_route(&$node_a.get_our_node_id(), &dummy_graph, &$node_b.get_our_node_id(), Some(InvoiceFeatures::known()),
- Some(&usable_channels.iter().map(|r| r).collect::<Vec<_>>()), &[], 10_000, TEST_FINAL_CLTV, &logger_a, &scorer).unwrap();
+ let route = get_route(&$node_a.get_our_node_id(), &payee, &dummy_graph,
+ Some(&usable_channels.iter().map(|r| r).collect::<Vec<_>>()), 10_000, TEST_FINAL_CLTV, &logger_a, &scorer).unwrap();
let mut payment_preimage = PaymentPreimage([0; 32]);
payment_preimage.0[0..8].copy_from_slice(&payment_count.to_le_bytes());
/// Getting a route for a keysend payment to a private node requires providing the payee's
/// features (since they were not announced in a node announcement). However, keysend payments
/// don't have an invoice to pull the payee's features from, so this method is provided for use in
- /// [`get_keysend_route`], thus omitting the need for payers to manually construct an
- /// `InvoiceFeatures` for [`get_route`].
+ /// [`Payee::for_keysend`], thus omitting the need for payers to manually construct an
+ /// `InvoiceFeatures` for [`find_route`].
///
- /// [`get_keysend_route`]: crate::routing::router::get_keysend_route
- /// [`get_route`]: crate::routing::router::get_route
+ /// [`Payee::for_keysend`]: crate::routing::router::Payee::for_keysend
+ /// [`find_route`]: crate::routing::router::find_route
pub(crate) fn for_keysend() -> InvoiceFeatures {
InvoiceFeatures::empty().set_variable_length_onion_optional()
}
use chain::channelmonitor::ChannelMonitor;
use chain::transaction::OutPoint;
use ln::{PaymentPreimage, PaymentHash, PaymentSecret};
-use ln::channelmanager::{ChainParameters, ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentSendFailure};
+use ln::channelmanager::{ChainParameters, ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentSendFailure, PaymentId};
use routing::network_graph::{NetGraphMsgHandler, NetworkGraph};
-use routing::router::{Route, get_route};
+use routing::router::{Payee, Route, get_route};
use routing::scorer::Scorer;
use ln::features::{InitFeatures, InvoiceFeatures};
use ln::msgs;
}};
($send_node: expr, $recv_node: expr, $last_hops: expr, $recv_value: expr, $cltv: expr) => {{
let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash!($recv_node, Some($recv_value));
+ let payee = $crate::routing::router::Payee::new($recv_node.node.get_our_node_id())
+ .with_features($crate::ln::features::InvoiceFeatures::known())
+ .with_route_hints($last_hops);
let net_graph_msg_handler = &$send_node.net_graph_msg_handler;
let scorer = ::routing::scorer::Scorer::new(0);
let route = ::routing::router::get_route(
- &$send_node.node.get_our_node_id(), &net_graph_msg_handler.network_graph,
- &$recv_node.node.get_our_node_id(), Some(::ln::features::InvoiceFeatures::known()),
+ &$send_node.node.get_our_node_id(), &payee, &net_graph_msg_handler.network_graph,
Some(&$send_node.node.list_usable_channels().iter().collect::<Vec<_>>()),
- &$last_hops, $recv_value, $cltv, $send_node.logger, &scorer
+ $recv_value, $cltv, $send_node.logger, &scorer
).unwrap();
(route, payment_hash, payment_preimage, payment_secret)
}}
macro_rules! expect_payment_sent {
($node: expr, $expected_payment_preimage: expr) => {
+ expect_payment_sent!($node, $expected_payment_preimage, None::<u64>);
+ };
+ ($node: expr, $expected_payment_preimage: expr, $expected_fee_msat_opt: expr) => {
let events = $node.node.get_and_clear_pending_events();
let expected_payment_hash = PaymentHash(Sha256::hash(&$expected_payment_preimage.0).into_inner());
assert_eq!(events.len(), 1);
match events[0] {
- Event::PaymentSent { ref payment_preimage, ref payment_hash } => {
+ Event::PaymentSent { payment_id: _, ref payment_preimage, ref payment_hash, ref fee_paid_msat } => {
assert_eq!($expected_payment_preimage, *payment_preimage);
assert_eq!(expected_payment_hash, *payment_hash);
+ assert!(fee_paid_msat.is_some());
+ if $expected_fee_msat_opt.is_some() {
+ assert_eq!(*fee_paid_msat, $expected_fee_msat_opt);
+ }
},
_ => panic!("Unexpected event"),
}
let events = $node.node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
- Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, ref network_update, ref error_code, ref error_data, .. } => {
+ Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, ref network_update, ref error_code, ref error_data, ref path, ref retry, .. } => {
assert_eq!(*payment_hash, $expected_payment_hash, "unexpected payment_hash");
assert_eq!(rejected_by_dest, $rejected_by_dest, "unexpected rejected_by_dest value");
+ assert!(retry.is_some(), "expected retry.is_some()");
+ assert_eq!(retry.as_ref().unwrap().final_value_msat, path.last().unwrap().fee_msat, "Retry amount should match last hop in path");
+ assert_eq!(retry.as_ref().unwrap().payee.pubkey, path.last().unwrap().pubkey, "Retry payee node_id should match last hop in path");
assert!(error_code.is_some(), "expected error_code.is_some() = true");
assert!(error_data.is_some(), "expected error_data.is_some() = true");
match network_update {
let events = $node.node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
- Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, network_update: _, ref error_code, ref error_data, .. } => {
+ Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, network_update: _, ref error_code, ref error_data, ref path, ref retry, .. } => {
assert_eq!(*payment_hash, $expected_payment_hash, "unexpected payment_hash");
assert_eq!(rejected_by_dest, $rejected_by_dest, "unexpected rejected_by_dest value");
+ assert!(retry.is_some(), "expected retry.is_some()");
+ assert_eq!(retry.as_ref().unwrap().final_value_msat, path.last().unwrap().fee_msat, "Retry amount should match last hop in path");
+ assert_eq!(retry.as_ref().unwrap().payee.pubkey, path.last().unwrap().pubkey, "Retry payee node_id should match last hop in path");
assert!(error_code.is_some(), "expected error_code.is_some() = true");
assert!(error_data.is_some(), "expected error_data.is_some() = true");
$(
}
}
-pub fn send_along_route_with_secret<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, route: Route, expected_paths: &[&[&Node<'a, 'b, 'c>]], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: PaymentSecret) {
- origin_node.node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
+pub fn send_along_route_with_secret<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, route: Route, expected_paths: &[&[&Node<'a, 'b, 'c>]], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: PaymentSecret) -> PaymentId {
+ let payment_id = origin_node.node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
check_added_monitors!(origin_node, expected_paths.len());
pass_along_route(origin_node, expected_paths, recv_value, our_payment_hash, our_payment_secret);
+ payment_id
}
pub fn pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_path: &[&Node<'a, 'b, 'c>], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: Option<PaymentSecret>, ev: MessageSendEvent, payment_received_expected: bool, expected_preimage: Option<PaymentPreimage>) {
}
}
-pub fn send_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, route: Route, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash, PaymentSecret) {
+pub fn send_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, route: Route, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash, PaymentSecret, PaymentId) {
let (our_payment_preimage, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(expected_route.last().unwrap());
- send_along_route_with_secret(origin_node, route, &[expected_route], recv_value, our_payment_hash, our_payment_secret);
- (our_payment_preimage, our_payment_hash, our_payment_secret)
+ let payment_id = send_along_route_with_secret(origin_node, route, &[expected_route], recv_value, our_payment_hash, our_payment_secret);
+ (our_payment_preimage, our_payment_hash, our_payment_secret, payment_id)
}
-pub fn claim_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_paths: &[&[&Node<'a, 'b, 'c>]], skip_last: bool, our_payment_preimage: PaymentPreimage) {
+pub fn do_claim_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_paths: &[&[&Node<'a, 'b, 'c>]], skip_last: bool, our_payment_preimage: PaymentPreimage) -> u64 {
for path in expected_paths.iter() {
assert_eq!(path.last().unwrap().node.get_our_node_id(), expected_paths[0].last().unwrap().node.get_our_node_id());
}
assert!(expected_paths[0].last().unwrap().node.claim_funds(our_payment_preimage));
check_added_monitors!(expected_paths[0].last().unwrap(), expected_paths.len());
+ let mut expected_total_fee_msat = 0;
+
macro_rules! msgs_from_ev {
($ev: expr) => {
match $ev {
$node.node.handle_update_fulfill_htlc(&$prev_node.node.get_our_node_id(), &next_msgs.as_ref().unwrap().0);
let fee = $node.node.channel_state.lock().unwrap().by_id.get(&next_msgs.as_ref().unwrap().0.channel_id).unwrap().config.forwarding_fee_base_msat;
expect_payment_forwarded!($node, Some(fee as u64), false);
+ expected_total_fee_msat += fee as u64;
check_added_monitors!($node, 1);
let new_next_msgs = if $new_msgs {
let events = $node.node.get_and_clear_pending_msg_events();
last_update_fulfill_dance!(origin_node, expected_route.first().unwrap());
}
}
+ expected_total_fee_msat
+}
+pub fn claim_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_paths: &[&[&Node<'a, 'b, 'c>]], skip_last: bool, our_payment_preimage: PaymentPreimage) {
+ let expected_total_fee_msat = do_claim_payment_along_route(origin_node, expected_paths, skip_last, our_payment_preimage);
if !skip_last {
- expect_payment_sent!(origin_node, our_payment_preimage);
+ expect_payment_sent!(origin_node, our_payment_preimage, Some(expected_total_fee_msat));
}
}
pub const TEST_FINAL_CLTV: u32 = 70;
pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash, PaymentSecret) {
+ let payee = Payee::new(expected_route.last().unwrap().node.get_our_node_id())
+ .with_features(InvoiceFeatures::known());
let net_graph_msg_handler = &origin_node.net_graph_msg_handler;
let scorer = Scorer::new(0);
- let route = get_route(&origin_node.node.get_our_node_id(), &net_graph_msg_handler.network_graph,
- &expected_route.last().unwrap().node.get_our_node_id(), Some(InvoiceFeatures::known()),
- Some(&origin_node.node.list_usable_channels().iter().collect::<Vec<_>>()), &[],
+ let route = get_route(
+ &origin_node.node.get_our_node_id(), &payee, &net_graph_msg_handler.network_graph,
+ Some(&origin_node.node.list_usable_channels().iter().collect::<Vec<_>>()),
recv_value, TEST_FINAL_CLTV, origin_node.logger, &scorer).unwrap();
assert_eq!(route.paths.len(), 1);
assert_eq!(route.paths[0].len(), expected_route.len());
assert_eq!(hop.pubkey, node.node.get_our_node_id());
}
- send_along_route(origin_node, route, expected_route, recv_value)
+ let res = send_along_route(origin_node, route, expected_route, recv_value);
+ (res.0, res.1, res.2)
}
pub fn route_over_limit<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) {
+ let payee = Payee::new(expected_route.last().unwrap().node.get_our_node_id())
+ .with_features(InvoiceFeatures::known());
let net_graph_msg_handler = &origin_node.net_graph_msg_handler;
let scorer = Scorer::new(0);
- let route = get_route(&origin_node.node.get_our_node_id(), &net_graph_msg_handler.network_graph, &expected_route.last().unwrap().node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), recv_value, TEST_FINAL_CLTV, origin_node.logger, &scorer).unwrap();
+ let route = get_route(&origin_node.node.get_our_node_id(), &payee, &net_graph_msg_handler.network_graph, None, recv_value, TEST_FINAL_CLTV, origin_node.logger, &scorer).unwrap();
assert_eq!(route.paths.len(), 1);
assert_eq!(route.paths[0].len(), expected_route.len());
for (node, hop) in expected_route.iter().zip(route.paths[0].iter()) {
use ln::{chan_utils, onion_utils};
use ln::chan_utils::HTLC_SUCCESS_TX_WEIGHT;
use routing::network_graph::{NetworkUpdate, RoutingFees};
-use routing::router::{Route, RouteHop, RouteHint, RouteHintHop, get_route, get_keysend_route};
+use routing::router::{Payee, Route, RouteHop, RouteHint, RouteHintHop, RouteParameters, find_route, get_route};
use routing::scorer::Scorer;
use ln::features::{ChannelFeatures, InitFeatures, InvoiceFeatures, NodeFeatures};
use ln::msgs;
});
hops[1].fee_msat = chan_4.1.contents.fee_base_msat as u64 + chan_4.1.contents.fee_proportional_millionths as u64 * hops[2].fee_msat as u64 / 1000000;
hops[0].fee_msat = chan_3.0.contents.fee_base_msat as u64 + chan_3.0.contents.fee_proportional_millionths as u64 * hops[1].fee_msat as u64 / 1000000;
- let payment_preimage_1 = send_along_route(&nodes[1], Route { paths: vec![hops] }, &vec!(&nodes[2], &nodes[3], &nodes[1])[..], 1000000).0;
+ let payment_preimage_1 = send_along_route(&nodes[1], Route { paths: vec![hops], payee: None }, &vec!(&nodes[2], &nodes[3], &nodes[1])[..], 1000000).0;
let mut hops = Vec::with_capacity(3);
hops.push(RouteHop {
});
hops[1].fee_msat = chan_2.1.contents.fee_base_msat as u64 + chan_2.1.contents.fee_proportional_millionths as u64 * hops[2].fee_msat as u64 / 1000000;
hops[0].fee_msat = chan_3.1.contents.fee_base_msat as u64 + chan_3.1.contents.fee_proportional_millionths as u64 * hops[1].fee_msat as u64 / 1000000;
- let payment_hash_2 = send_along_route(&nodes[1], Route { paths: vec![hops] }, &vec!(&nodes[3], &nodes[2], &nodes[1])[..], 1000000).1;
+ let payment_hash_2 = send_along_route(&nodes[1], Route { paths: vec![hops], payee: None }, &vec!(&nodes[3], &nodes[2], &nodes[1])[..], 1000000).1;
// Claim the rebalances...
fail_payment(&nodes[1], &vec!(&nodes[3], &nodes[2], &nodes[1])[..], payment_hash_2);
let mut first_claimed = false;
for event in events {
match event {
- Event::PaymentSent { payment_preimage, payment_hash } => {
+ Event::PaymentSent { payment_preimage, payment_hash, .. } => {
if payment_preimage == our_payment_preimage && payment_hash == payment_hash_1 {
assert!(!first_claimed);
first_claimed = true;
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 2);
match events[0] {
- Event::PaymentSent { payment_preimage, payment_hash } => {
+ Event::PaymentSent { payment_preimage, payment_hash, .. } => {
assert_eq!(payment_preimage, payment_preimage_3);
assert_eq!(payment_hash, payment_hash_3);
},
let events_4 = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events_4.len(), 1);
match events_4[0] {
- Event::PaymentSent { ref payment_preimage, ref payment_hash } => {
+ Event::PaymentSent { ref payment_preimage, ref payment_hash, .. } => {
assert_eq!(payment_preimage_1, *payment_preimage);
assert_eq!(payment_hash_1, *payment_hash);
},
let events_4 = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events_4.len(), 1);
match events_4[0] {
- Event::PaymentSent { ref payment_preimage, ref payment_hash } => {
+ Event::PaymentSent { ref payment_preimage, ref payment_hash, .. } => {
assert_eq!(payment_preimage_1, *payment_preimage);
assert_eq!(payment_hash_1, *payment_hash);
},
nodes[0].net_graph_msg_handler.handle_channel_update(&as_update).unwrap();
let (route, _, _, _) = get_route_and_payment_hash!(nodes[0], nodes[1], 1000000);
- let (payment_preimage, _, _) = send_along_route(&nodes[0], route, &[&nodes[1]], 1000000);
+ let payment_preimage = send_along_route(&nodes[0], route, &[&nodes[1]], 1000000).0;
claim_payment(&nodes[0], &[&nodes[1]], payment_preimage);
// Check that after deserialization and reconnection we can still generate an identical
let events_3 = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events_3.len(), 1);
match events_3[0] {
- Event::PaymentSent { ref payment_preimage, ref payment_hash } => {
+ Event::PaymentSent { ref payment_preimage, ref payment_hash, .. } => {
assert_eq!(*payment_preimage, payment_preimage_1);
assert_eq!(*payment_hash, payment_hash_1);
},
// indicates there are more HTLCs coming.
let cur_height = CHAN_CONFIRM_DEPTH + 1; // route_payment calls send_payment, which adds 1 to the current height. So we do the same here to match.
let payment_id = PaymentId([42; 32]);
- nodes[0].node.send_payment_along_path(&route.paths[0], &our_payment_hash, &Some(payment_secret), 200000, cur_height, payment_id, &None).unwrap();
+ nodes[0].node.send_payment_along_path(&route.paths[0], &route.payee, &our_payment_hash, &Some(payment_secret), 200000, cur_height, payment_id, &None).unwrap();
check_added_monitors!(nodes[0], 1);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1);
send_payment(&nodes[0], &[&nodes[1]], 1000000);
}
-#[test]
-fn test_dup_htlc_onchain_fails_on_reload() {
- // When a Channel is closed, any outbound HTLCs which were relayed through it are simply
- // dropped when the Channel is. From there, the ChannelManager relies on the ChannelMonitor
- // having a copy of the relevant fail-/claim-back data and processes the HTLC fail/claim when
- // the ChannelMonitor tells it to.
- //
- // If, due to an on-chain event, an HTLC is failed/claimed, and then we serialize the
- // ChannelManager, we generally expect there not to be a duplicate HTLC fail/claim (eg via a
- // PaymentPathFailed event appearing). However, because we may not serialize the relevant
- // ChannelMonitor at the same time, this isn't strictly guaranteed. In order to provide this
- // consistency, the ChannelManager explicitly tracks pending-onchain-resolution outbound HTLCs
- // and de-duplicates ChannelMonitor events.
- //
- // This tests that explicit tracking behavior.
- let chanmon_cfgs = create_chanmon_cfgs(2);
- let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
- let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
- let persister: test_utils::TestPersister;
- let new_chain_monitor: test_utils::TestChainMonitor;
- let nodes_0_deserialized: ChannelManager<EnforcingSigner, &test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestLogger>;
- let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
-
- let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known()).2;
-
- // Route a payment, but force-close the channel before the HTLC fulfill message arrives at
- // nodes[0].
- let (payment_preimage, _, _) = route_payment(&nodes[0], &[&nodes[1]], 10000000);
- nodes[0].node.force_close_channel(&nodes[0].node.list_channels()[0].channel_id).unwrap();
- check_closed_broadcast!(nodes[0], true);
- check_added_monitors!(nodes[0], 1);
- check_closed_event!(nodes[0], 1, ClosureReason::HolderForceClosed);
-
- nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
- nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
-
- // Connect blocks until the CLTV timeout is up so that we get an HTLC-Timeout transaction
- connect_blocks(&nodes[0], TEST_FINAL_CLTV + LATENCY_GRACE_PERIOD_BLOCKS + 1);
- let node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
- assert_eq!(node_txn.len(), 3);
- assert_eq!(node_txn[0], node_txn[1]);
-
- assert!(nodes[1].node.claim_funds(payment_preimage));
- check_added_monitors!(nodes[1], 1);
-
- let mut header = BlockHeader { version: 0x20000000, prev_blockhash: nodes[1].best_block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
- connect_block(&nodes[1], &Block { header, txdata: vec![node_txn[1].clone(), node_txn[2].clone()]});
- check_closed_broadcast!(nodes[1], true);
- check_added_monitors!(nodes[1], 1);
- check_closed_event!(nodes[1], 1, ClosureReason::CommitmentTxConfirmed);
- let claim_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
-
- header.prev_blockhash = nodes[0].best_block_hash();
- connect_block(&nodes[0], &Block { header, txdata: vec![node_txn[1].clone(), node_txn[2].clone()]});
-
- // Serialize out the ChannelMonitor before connecting the on-chain claim transactions. This is
- // fairly normal behavior as ChannelMonitor(s) are often not re-serialized when on-chain events
- // happen, unlike ChannelManager which tends to be re-serialized after any relevant event(s).
- let mut chan_0_monitor_serialized = test_utils::TestVecWriter(Vec::new());
- get_monitor!(nodes[0], chan_id).write(&mut chan_0_monitor_serialized).unwrap();
-
- header.prev_blockhash = nodes[0].best_block_hash();
- let claim_block = Block { header, txdata: claim_txn};
- connect_block(&nodes[0], &claim_block);
- expect_payment_sent!(nodes[0], payment_preimage);
-
- // ChannelManagers generally get re-serialized after any relevant event(s). Since we just
- // connected a highly-relevant block, it likely gets serialized out now.
- let mut chan_manager_serialized = test_utils::TestVecWriter(Vec::new());
- nodes[0].node.write(&mut chan_manager_serialized).unwrap();
-
- // Now reload nodes[0]...
- persister = test_utils::TestPersister::new();
- let keys_manager = &chanmon_cfgs[0].keys_manager;
- new_chain_monitor = test_utils::TestChainMonitor::new(Some(nodes[0].chain_source), nodes[0].tx_broadcaster.clone(), nodes[0].logger, node_cfgs[0].fee_estimator, &persister, keys_manager);
- nodes[0].chain_monitor = &new_chain_monitor;
- let mut chan_0_monitor_read = &chan_0_monitor_serialized.0[..];
- let (_, mut chan_0_monitor) = <(BlockHash, ChannelMonitor<EnforcingSigner>)>::read(
- &mut chan_0_monitor_read, keys_manager).unwrap();
- assert!(chan_0_monitor_read.is_empty());
-
- let (_, nodes_0_deserialized_tmp) = {
- let mut channel_monitors = HashMap::new();
- channel_monitors.insert(chan_0_monitor.get_funding_txo().0, &mut chan_0_monitor);
- <(BlockHash, ChannelManager<EnforcingSigner, &test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestLogger>)>
- ::read(&mut io::Cursor::new(&chan_manager_serialized.0[..]), ChannelManagerReadArgs {
- default_config: Default::default(),
- keys_manager,
- fee_estimator: node_cfgs[0].fee_estimator,
- chain_monitor: nodes[0].chain_monitor,
- tx_broadcaster: nodes[0].tx_broadcaster.clone(),
- logger: nodes[0].logger,
- channel_monitors,
- }).unwrap()
- };
- nodes_0_deserialized = nodes_0_deserialized_tmp;
-
- assert!(nodes[0].chain_monitor.watch_channel(chan_0_monitor.get_funding_txo().0, chan_0_monitor).is_ok());
- check_added_monitors!(nodes[0], 1);
- nodes[0].node = &nodes_0_deserialized;
-
- // Note that if we re-connect the block which exposed nodes[0] to the payment preimage (but
- // which the current ChannelMonitor has not seen), the ChannelManager's de-duplication of
- // payment events should kick in, leaving us with no pending events here.
- let height = nodes[0].blocks.lock().unwrap().len() as u32 - 1;
- nodes[0].chain_monitor.chain_monitor.block_connected(&claim_block, height);
- assert!(nodes[0].node.get_and_clear_pending_events().is_empty());
-}
-
#[test]
fn test_manager_serialize_deserialize_events() {
// This test makes sure the events field in ChannelManager survives de/serialization
let events = nodes[0].node.get_and_clear_pending_events();
match events[0] {
- Event::PaymentSent { ref payment_preimage, ref payment_hash } => {
+ Event::PaymentSent { ref payment_preimage, ref payment_hash, .. } => {
assert_eq!(*payment_preimage, our_payment_preimage);
assert_eq!(*payment_hash, duplicate_payment_hash);
}
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
- Event::PaymentSent { payment_preimage, payment_hash } => {
+ Event::PaymentSent { payment_preimage, payment_hash, .. } => {
assert_eq!(payment_preimage, our_payment_preimage);
assert_eq!(payment_hash, our_payment_hash);
},
let (route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], max_can_send);
// Send a payment which passes reserve checks but gets stuck in the holding cell.
- nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
+ let our_payment_id = nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
chan_stat = get_channel_value_stat!(nodes[0], chan.2);
assert_eq!(chan_stat.holding_cell_outbound_amount_msat, max_can_send);
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match &events[0] {
- &Event::PaymentPathFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref all_paths_failed, path: _, ref short_channel_id, ref error_code, ref error_data } => {
+ &Event::PaymentPathFailed { ref payment_id, ref payment_hash, ref rejected_by_dest, ref network_update, ref all_paths_failed, ref short_channel_id, ref error_code, ref error_data, .. } => {
+ assert_eq!(our_payment_id, *payment_id.as_ref().unwrap());
assert_eq!(our_payment_hash.clone(), *payment_hash);
assert_eq!(*rejected_by_dest, false);
assert_eq!(*all_paths_failed, true);
nodes[0].node.send_payment(&route_1, payment_hash_1, &Some(payment_secret_1)).unwrap();
chan_stat = get_channel_value_stat!(nodes[0], chan.2);
assert_eq!(chan_stat.holding_cell_outbound_amount_msat, amt_1);
- nodes[0].node.send_payment(&route_2, payment_hash_2, &Some(payment_secret_2)).unwrap();
+ let payment_id_2 = nodes[0].node.send_payment(&route_2, payment_hash_2, &Some(payment_secret_2)).unwrap();
chan_stat = get_channel_value_stat!(nodes[0], chan.2);
assert_eq!(chan_stat.holding_cell_outbound_amount_msat, amt_1 + amt_2);
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match &events[0] {
- &Event::PaymentPathFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref all_paths_failed, path: _, ref short_channel_id, ref error_code, ref error_data } => {
+ &Event::PaymentPathFailed { ref payment_id, ref payment_hash, ref rejected_by_dest, ref network_update, ref all_paths_failed, ref short_channel_id, ref error_code, ref error_data, .. } => {
+ assert_eq!(payment_id_2, *payment_id.as_ref().unwrap());
assert_eq!(payment_hash_2.clone(), *payment_hash);
assert_eq!(*rejected_by_dest, false);
assert_eq!(*all_paths_failed, true);
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
- Event::PaymentSent { ref payment_preimage, ref payment_hash } => {
+ Event::PaymentSent { ref payment_preimage, ref payment_hash, .. } => {
assert_eq!(*payment_preimage, payment_preimage_1);
assert_eq!(*payment_hash, payment_hash_1);
}
create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
let scorer = Scorer::new(0);
- let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 10_000, TEST_FINAL_CLTV, nodes[0].logger, &scorer).unwrap();
+ let payee = Payee::new(nodes[1].node.get_our_node_id()).with_features(InvoiceFeatures::known());
+ let route = get_route(&nodes[0].node.get_our_node_id(), &payee, &nodes[0].net_graph_msg_handler.network_graph, None, 10_000, TEST_FINAL_CLTV, nodes[0].logger, &scorer).unwrap();
let (_, our_payment_hash, _) = get_payment_preimage_hash!(nodes[0]);
let our_payment_secret = nodes[1].node.create_inbound_payment_for_hash(our_payment_hash, Some(100_000), 7200, 0).unwrap();
nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
htlc_minimum_msat: None,
htlc_maximum_msat: None,
}]);
- let last_hops = vec![&route_hint];
+ let last_hops = vec![route_hint];
let (route, our_payment_hash, our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], last_hops, 10_000, TEST_FINAL_CLTV);
nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
let chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1000000, 59000000, InitFeatures::known(), InitFeatures::known());
// Lock HTLC in both directions (using a slightly lower CLTV delay to provide timely RBF bumps)
+ let payee = Payee::new(nodes[1].node.get_our_node_id()).with_features(InvoiceFeatures::known());
let scorer = Scorer::new(0);
- let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph,
- &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 3_000_000, 50, nodes[0].logger, &scorer).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &payee, &nodes[0].net_graph_msg_handler.network_graph,
+ None, 3_000_000, 50, nodes[0].logger, &scorer).unwrap();
let payment_preimage = send_along_route(&nodes[0], route, &[&nodes[1]], 3_000_000).0;
- let route = get_route(&nodes[1].node.get_our_node_id(), &nodes[1].net_graph_msg_handler.network_graph,
- &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 3_000_000, 50, nodes[0].logger, &scorer).unwrap();
+ let payee = Payee::new(nodes[0].node.get_our_node_id()).with_features(InvoiceFeatures::known());
+ let route = get_route(&nodes[1].node.get_our_node_id(), &payee, &nodes[1].net_graph_msg_handler.network_graph,
+ None, 3_000_000, 50, nodes[0].logger, &scorer).unwrap();
send_along_route(&nodes[1], route, &[&nodes[0]], 3_000_000);
let revoked_local_txn = get_local_commitment_txn!(nodes[1], chan.2);
let network_graph = &nodes[0].net_graph_msg_handler.network_graph;
let payer_pubkey = nodes[0].node.get_our_node_id();
let payee_pubkey = nodes[1].node.get_our_node_id();
+ let params = RouteParameters {
+ payee: Payee::for_keysend(payee_pubkey),
+ final_value_msat: 10000,
+ final_cltv_expiry_delta: 40,
+ };
let scorer = Scorer::new(0);
- let route = get_keysend_route(
- &payer_pubkey, &network_graph, &payee_pubkey, None, &vec![], 10000, 40, nodes[0].logger, &scorer
- ).unwrap();
+ let route = find_route(&payer_pubkey, ¶ms, &network_graph, None, nodes[0].logger, &scorer).unwrap();
let test_preimage = PaymentPreimage([42; 32]);
let (payment_hash, _) = nodes[0].node.send_spontaneous_payment(&route, Some(test_preimage)).unwrap();
nodes[1].node.peer_connected(&payer_pubkey, &msgs::Init { features: InitFeatures::known() });
let _chan = create_chan_between_nodes(&nodes[0], &nodes[1], InitFeatures::known(), InitFeatures::known());
+ let params = RouteParameters {
+ payee: Payee::for_keysend(payee_pubkey),
+ final_value_msat: 10000,
+ final_cltv_expiry_delta: 40,
+ };
let network_graph = &nodes[0].net_graph_msg_handler.network_graph;
let first_hops = nodes[0].node.list_usable_channels();
let scorer = Scorer::new(0);
- let route = get_keysend_route(
- &payer_pubkey, &network_graph, &payee_pubkey, Some(&first_hops.iter().collect::<Vec<_>>()),
- &vec![], 10000, 40, nodes[0].logger, &scorer
+ let route = find_route(
+ &payer_pubkey, ¶ms, &network_graph, Some(&first_hops.iter().collect::<Vec<_>>()),
+ nodes[0].logger, &scorer
).unwrap();
let test_preimage = PaymentPreimage([42; 32]);
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
- if let &Event::PaymentPathFailed { payment_hash: _, ref rejected_by_dest, ref network_update, ref all_paths_failed, path: _, ref short_channel_id, ref error_code, error_data: _ } = &events[0] {
+ if let &Event::PaymentPathFailed { ref rejected_by_dest, ref network_update, ref all_paths_failed, ref short_channel_id, ref error_code, .. } = &events[0] {
assert_eq!(*rejected_by_dest, !expected_retryable);
assert_eq!(*all_paths_failed, true);
assert_eq!(*error_code, expected_error_code);
// Test a positive test-case with one extra msat, meeting the minimum.
bogus_route.paths[0][route_len-1].fee_msat = amt_to_forward + 1;
- let (preimage, _, _) = send_along_route(&nodes[0], bogus_route, &[&nodes[1], &nodes[2]], amt_to_forward+1);
+ let preimage = send_along_route(&nodes[0], bogus_route, &[&nodes[1], &nodes[2]], amt_to_forward+1).0;
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], preimage);
//TODO: with new config API, we will be able to generate both valid and
short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
},
]],
+ payee: None,
};
let session_priv = SecretKey::from_slice(&hex::decode("4141414141414141414141414141414141414141414141414141414141414141").unwrap()[..]).unwrap();
//! serialization ordering between ChannelManager/ChannelMonitors and ensuring we can still retry
//! payments thereafter.
+use chain::{ChannelMonitorUpdateErr, Confirm, Listen, Watch};
+use chain::channelmonitor::{ANTI_REORG_DELAY, ChannelMonitor, LATENCY_GRACE_PERIOD_BLOCKS};
+use chain::transaction::OutPoint;
use ln::{PaymentPreimage, PaymentHash};
-use ln::channelmanager::{PaymentId, PaymentSendFailure};
+use ln::channelmanager::{BREAKDOWN_TIMEOUT, ChannelManager, ChannelManagerReadArgs, PaymentId, PaymentSendFailure};
use ln::features::InitFeatures;
use ln::msgs;
-use ln::msgs::ChannelMessageHandler;
-use util::events::{Event, MessageSendEvent, MessageSendEventsProvider};
+use ln::msgs::{ChannelMessageHandler, ErrorAction};
+use util::events::{ClosureReason, Event, MessageSendEvent, MessageSendEventsProvider};
+use util::test_utils;
use util::errors::APIError;
+use util::enforcing_trait_impls::EnforcingSigner;
+use util::ser::{ReadableArgs, Writeable};
+use io;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::Hash;
+use bitcoin::{Block, BlockHeader, BlockHash};
use prelude::*;
assert!(!nodes[0].node.has_pending_payments());
}
+
+fn do_retry_with_no_persist(confirm_before_reload: bool) {
+ // If we send a pending payment and `send_payment` returns success, we should always either
+ // return a payment failure event or a payment success event, and on failure the payment should
+ // be retryable.
+ //
+ // In order to do so when the ChannelManager isn't immediately persisted (which is normal - its
+ // always persisted asynchronously), the ChannelManager has to reload some payment data from
+ // ChannelMonitor(s) in some cases. This tests that reloading.
+ //
+ // `confirm_before_reload` confirms the channel-closing commitment transaction on-chain prior
+ // to reloading the ChannelManager, increasing test coverage in ChannelMonitor HTLC tracking
+ // which has separate codepaths for "commitment transaction already confirmed" and not.
+ let chanmon_cfgs = create_chanmon_cfgs(3);
+ let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
+ let persister: test_utils::TestPersister;
+ let new_chain_monitor: test_utils::TestChainMonitor;
+ let nodes_0_deserialized: ChannelManager<EnforcingSigner, &test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestLogger>;
+ let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+
+ let (_, _, chan_id, funding_tx) = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
+ let (_, _, chan_id_2, _) = create_announced_chan_between_nodes(&nodes, 1, 2, InitFeatures::known(), InitFeatures::known());
+
+ // Serialize the ChannelManager prior to sending payments
+ let nodes_0_serialized = nodes[0].node.encode();
+
+ // Send two payments - one which will get to nodes[2] and will be claimed, one which we'll time
+ // out and retry.
+ let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], 1_000_000);
+ let (payment_preimage_1, _, _, payment_id_1) = send_along_route(&nodes[0], route.clone(), &[&nodes[1], &nodes[2]], 1_000_000);
+ let payment_id = nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
+ check_added_monitors!(nodes[0], 1);
+
+ let mut events = nodes[0].node.get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), 1);
+ let payment_event = SendEvent::from_event(events.pop().unwrap());
+ assert_eq!(payment_event.node_id, nodes[1].node.get_our_node_id());
+
+ // We relay the payment to nodes[1] while its disconnected from nodes[2], causing the payment
+ // to be returned immediately to nodes[0], without having nodes[2] fail the inbound payment
+ // which would prevent retry.
+ nodes[1].node.peer_disconnected(&nodes[2].node.get_our_node_id(), false);
+ nodes[2].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
+
+ nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]);
+ commitment_signed_dance!(nodes[1], nodes[0], payment_event.commitment_msg, false, true);
+ // nodes[1] now immediately fails the HTLC as the next-hop channel is disconnected
+ let _ = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
+
+ reconnect_nodes(&nodes[1], &nodes[2], (false, false), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (false, false));
+
+ let as_commitment_tx = get_local_commitment_txn!(nodes[0], chan_id)[0].clone();
+ if confirm_before_reload {
+ mine_transaction(&nodes[0], &as_commitment_tx);
+ nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().clear();
+ }
+
+ // The ChannelMonitor should always be the latest version, as we're required to persist it
+ // during the `commitment_signed_dance!()`.
+ let mut chan_0_monitor_serialized = test_utils::TestVecWriter(Vec::new());
+ get_monitor!(nodes[0], chan_id).write(&mut chan_0_monitor_serialized).unwrap();
+
+ persister = test_utils::TestPersister::new();
+ let keys_manager = &chanmon_cfgs[0].keys_manager;
+ new_chain_monitor = test_utils::TestChainMonitor::new(Some(nodes[0].chain_source), nodes[0].tx_broadcaster.clone(), nodes[0].logger, node_cfgs[0].fee_estimator, &persister, keys_manager);
+ nodes[0].chain_monitor = &new_chain_monitor;
+ let mut chan_0_monitor_read = &chan_0_monitor_serialized.0[..];
+ let (_, mut chan_0_monitor) = <(BlockHash, ChannelMonitor<EnforcingSigner>)>::read(
+ &mut chan_0_monitor_read, keys_manager).unwrap();
+ assert!(chan_0_monitor_read.is_empty());
+
+ let mut nodes_0_read = &nodes_0_serialized[..];
+ let (_, nodes_0_deserialized_tmp) = {
+ let mut channel_monitors = HashMap::new();
+ channel_monitors.insert(chan_0_monitor.get_funding_txo().0, &mut chan_0_monitor);
+ <(BlockHash, ChannelManager<EnforcingSigner, &test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestLogger>)>::read(&mut nodes_0_read, ChannelManagerReadArgs {
+ default_config: test_default_channel_config(),
+ keys_manager,
+ fee_estimator: node_cfgs[0].fee_estimator,
+ chain_monitor: nodes[0].chain_monitor,
+ tx_broadcaster: nodes[0].tx_broadcaster.clone(),
+ logger: nodes[0].logger,
+ channel_monitors,
+ }).unwrap()
+ };
+ nodes_0_deserialized = nodes_0_deserialized_tmp;
+ assert!(nodes_0_read.is_empty());
+
+ assert!(nodes[0].chain_monitor.watch_channel(chan_0_monitor.get_funding_txo().0, chan_0_monitor).is_ok());
+ nodes[0].node = &nodes_0_deserialized;
+ check_added_monitors!(nodes[0], 1);
+
+ // On reload, the ChannelManager should realize it is stale compared to the ChannelMonitor and
+ // force-close the channel.
+ check_closed_event!(nodes[0], 1, ClosureReason::OutdatedChannelManager);
+ assert!(nodes[0].node.list_channels().is_empty());
+ assert!(nodes[0].node.has_pending_payments());
+ let as_broadcasted_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
+ assert_eq!(as_broadcasted_txn.len(), 1);
+ assert_eq!(as_broadcasted_txn[0], as_commitment_tx);
+
+ nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::known()});
+ assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
+
+ // Now nodes[1] should send a channel reestablish, which nodes[0] will respond to with an
+ // error, as the channel has hit the chain.
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::known()});
+ let bs_reestablish = get_event_msg!(nodes[1], MessageSendEvent::SendChannelReestablish, nodes[0].node.get_our_node_id());
+ nodes[0].node.handle_channel_reestablish(&nodes[1].node.get_our_node_id(), &bs_reestablish);
+ let as_err = nodes[0].node.get_and_clear_pending_msg_events();
+ assert_eq!(as_err.len(), 1);
+ match as_err[0] {
+ MessageSendEvent::HandleError { node_id, action: msgs::ErrorAction::SendErrorMessage { ref msg } } => {
+ assert_eq!(node_id, nodes[1].node.get_our_node_id());
+ nodes[1].node.handle_error(&nodes[0].node.get_our_node_id(), msg);
+ check_closed_event!(nodes[1], 1, ClosureReason::CounterpartyForceClosed { peer_msg: "Failed to find corresponding channel".to_string() });
+ check_added_monitors!(nodes[1], 1);
+ assert_eq!(nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0).len(), 1);
+ },
+ _ => panic!("Unexpected event"),
+ }
+ check_closed_broadcast!(nodes[1], false);
+
+ // Now claim the first payment, which should allow nodes[1] to claim the payment on-chain when
+ // we close in a moment.
+ nodes[2].node.claim_funds(payment_preimage_1);
+ check_added_monitors!(nodes[2], 1);
+ let htlc_fulfill_updates = get_htlc_update_msgs!(nodes[2], nodes[1].node.get_our_node_id());
+ nodes[1].node.handle_update_fulfill_htlc(&nodes[2].node.get_our_node_id(), &htlc_fulfill_updates.update_fulfill_htlcs[0]);
+ check_added_monitors!(nodes[1], 1);
+ commitment_signed_dance!(nodes[1], nodes[2], htlc_fulfill_updates.commitment_signed, false);
+
+ if confirm_before_reload {
+ let best_block = nodes[0].blocks.lock().unwrap().last().unwrap().clone();
+ nodes[0].node.best_block_updated(&best_block.0, best_block.1);
+ }
+
+ // Create a new channel on which to retry the payment before we fail the payment via the
+ // HTLC-Timeout transaction. This avoids ChannelManager timing out the payment due to us
+ // connecting several blocks while creating the channel (implying time has passed).
+ create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
+ assert_eq!(nodes[0].node.list_usable_channels().len(), 1);
+
+ mine_transaction(&nodes[1], &as_commitment_tx);
+ let bs_htlc_claim_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
+ assert_eq!(bs_htlc_claim_txn.len(), 1);
+ check_spends!(bs_htlc_claim_txn[0], as_commitment_tx);
+ expect_payment_forwarded!(nodes[1], None, false);
+
+ mine_transaction(&nodes[0], &as_commitment_tx);
+ mine_transaction(&nodes[0], &bs_htlc_claim_txn[0]);
+ expect_payment_sent!(nodes[0], payment_preimage_1);
+ connect_blocks(&nodes[0], TEST_FINAL_CLTV*4 + 20);
+ let as_htlc_timeout_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
+ check_spends!(as_htlc_timeout_txn[2], funding_tx);
+ check_spends!(as_htlc_timeout_txn[0], as_commitment_tx);
+ check_spends!(as_htlc_timeout_txn[1], as_commitment_tx);
+ assert_eq!(as_htlc_timeout_txn.len(), 3);
+ if as_htlc_timeout_txn[0].input[0].previous_output == bs_htlc_claim_txn[0].input[0].previous_output {
+ confirm_transaction(&nodes[0], &as_htlc_timeout_txn[1]);
+ } else {
+ confirm_transaction(&nodes[0], &as_htlc_timeout_txn[0]);
+ }
+ nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().clear();
+ expect_payment_failed!(nodes[0], payment_hash, false);
+
+ // Finally, retry the payment (which was reloaded from the ChannelMonitor when nodes[0] was
+ // reloaded) via a route over the new channel, which work without issue and eventually be
+ // received and claimed at the recipient just like any other payment.
+ let (mut new_route, _, _, _) = get_route_and_payment_hash!(nodes[0], nodes[2], 1_000_000);
+
+ // Update the fee on the middle hop to ensure PaymentSent events have the correct (retried) fee
+ // and not the original fee. We also update node[1]'s relevant config as
+ // do_claim_payment_along_route expects us to never overpay.
+ nodes[1].node.channel_state.lock().unwrap().by_id.get_mut(&chan_id_2).unwrap().config.forwarding_fee_base_msat += 100_000;
+ new_route.paths[0][0].fee_msat += 100_000;
+
+ assert!(nodes[0].node.retry_payment(&new_route, payment_id_1).is_err()); // Shouldn't be allowed to retry a fulfilled payment
+ nodes[0].node.retry_payment(&new_route, payment_id).unwrap();
+ check_added_monitors!(nodes[0], 1);
+ let mut events = nodes[0].node.get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), 1);
+ pass_along_path(&nodes[0], &[&nodes[1], &nodes[2]], 1_000_000, payment_hash, Some(payment_secret), events.pop().unwrap(), true, None);
+ do_claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[2]]], false, payment_preimage);
+ expect_payment_sent!(nodes[0], payment_preimage, Some(new_route.paths[0][0].fee_msat));
+}
+
+#[test]
+fn retry_with_no_persist() {
+ do_retry_with_no_persist(true);
+ do_retry_with_no_persist(false);
+}
+
+fn do_test_dup_htlc_onchain_fails_on_reload(persist_manager_post_event: bool, confirm_commitment_tx: bool, payment_timeout: bool) {
+ // When a Channel is closed, any outbound HTLCs which were relayed through it are simply
+ // dropped when the Channel is. From there, the ChannelManager relies on the ChannelMonitor
+ // having a copy of the relevant fail-/claim-back data and processes the HTLC fail/claim when
+ // the ChannelMonitor tells it to.
+ //
+ // If, due to an on-chain event, an HTLC is failed/claimed, we should avoid providing the
+ // ChannelManager the HTLC event until after the monitor is re-persisted. This should prevent a
+ // duplicate HTLC fail/claim (e.g. via a PaymentPathFailed event).
+ let chanmon_cfgs = create_chanmon_cfgs(2);
+ let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+ let persister: test_utils::TestPersister;
+ let new_chain_monitor: test_utils::TestChainMonitor;
+ let nodes_0_deserialized: ChannelManager<EnforcingSigner, &test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestLogger>;
+ let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+ let (_, _, chan_id, funding_tx) = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
+
+ // Route a payment, but force-close the channel before the HTLC fulfill message arrives at
+ // nodes[0].
+ let (payment_preimage, payment_hash, _) = route_payment(&nodes[0], &[&nodes[1]], 10000000);
+ nodes[0].node.force_close_channel(&nodes[0].node.list_channels()[0].channel_id).unwrap();
+ check_closed_broadcast!(nodes[0], true);
+ check_added_monitors!(nodes[0], 1);
+ check_closed_event!(nodes[0], 1, ClosureReason::HolderForceClosed);
+
+ nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
+ nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
+
+ // Connect blocks until the CLTV timeout is up so that we get an HTLC-Timeout transaction
+ connect_blocks(&nodes[0], TEST_FINAL_CLTV + LATENCY_GRACE_PERIOD_BLOCKS + 1);
+ let node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
+ assert_eq!(node_txn.len(), 3);
+ assert_eq!(node_txn[0], node_txn[1]);
+ check_spends!(node_txn[1], funding_tx);
+ check_spends!(node_txn[2], node_txn[1]);
+ let timeout_txn = vec![node_txn[2].clone()];
+
+ assert!(nodes[1].node.claim_funds(payment_preimage));
+ check_added_monitors!(nodes[1], 1);
+
+ let mut header = BlockHeader { version: 0x20000000, prev_blockhash: nodes[1].best_block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
+ connect_block(&nodes[1], &Block { header, txdata: vec![node_txn[1].clone()]});
+ check_closed_broadcast!(nodes[1], true);
+ check_added_monitors!(nodes[1], 1);
+ check_closed_event!(nodes[1], 1, ClosureReason::CommitmentTxConfirmed);
+ let claim_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
+
+ header.prev_blockhash = nodes[0].best_block_hash();
+ connect_block(&nodes[0], &Block { header, txdata: vec![node_txn[1].clone()]});
+
+ if confirm_commitment_tx {
+ connect_blocks(&nodes[0], BREAKDOWN_TIMEOUT as u32 - 1);
+ }
+
+ header.prev_blockhash = nodes[0].best_block_hash();
+ let claim_block = Block { header, txdata: if payment_timeout { timeout_txn } else { claim_txn } };
+
+ if payment_timeout {
+ assert!(confirm_commitment_tx); // Otherwise we're spending below our CSV!
+ connect_block(&nodes[0], &claim_block);
+ connect_blocks(&nodes[0], ANTI_REORG_DELAY - 2);
+ }
+
+ // Now connect the HTLC claim transaction with the ChainMonitor-generated ChannelMonitor update
+ // returning TemporaryFailure. This should cause the claim event to never make its way to the
+ // ChannelManager.
+ chanmon_cfgs[0].persister.chain_sync_monitor_persistences.lock().unwrap().clear();
+ chanmon_cfgs[0].persister.set_update_ret(Err(ChannelMonitorUpdateErr::TemporaryFailure));
+
+ if payment_timeout {
+ connect_blocks(&nodes[0], 1);
+ } else {
+ connect_block(&nodes[0], &claim_block);
+ }
+
+ let funding_txo = OutPoint { txid: funding_tx.txid(), index: 0 };
+ let mon_updates: Vec<_> = chanmon_cfgs[0].persister.chain_sync_monitor_persistences.lock().unwrap()
+ .get_mut(&funding_txo).unwrap().drain().collect();
+ assert_eq!(mon_updates.len(), 1);
+ assert!(nodes[0].chain_monitor.release_pending_monitor_events().is_empty());
+ assert!(nodes[0].node.get_and_clear_pending_events().is_empty());
+
+ // If we persist the ChannelManager here, we should get the PaymentSent event after
+ // deserialization.
+ let mut chan_manager_serialized = test_utils::TestVecWriter(Vec::new());
+ if !persist_manager_post_event {
+ nodes[0].node.write(&mut chan_manager_serialized).unwrap();
+ }
+
+ // Now persist the ChannelMonitor and inform the ChainMonitor that we're done, generating the
+ // payment sent event.
+ chanmon_cfgs[0].persister.set_update_ret(Ok(()));
+ let mut chan_0_monitor_serialized = test_utils::TestVecWriter(Vec::new());
+ get_monitor!(nodes[0], chan_id).write(&mut chan_0_monitor_serialized).unwrap();
+ nodes[0].chain_monitor.chain_monitor.channel_monitor_updated(funding_txo, mon_updates[0]).unwrap();
+ if payment_timeout {
+ expect_payment_failed!(nodes[0], payment_hash, true);
+ } else {
+ expect_payment_sent!(nodes[0], payment_preimage);
+ }
+
+ // If we persist the ChannelManager after we get the PaymentSent event, we shouldn't get it
+ // twice.
+ if persist_manager_post_event {
+ nodes[0].node.write(&mut chan_manager_serialized).unwrap();
+ }
+
+ // Now reload nodes[0]...
+ persister = test_utils::TestPersister::new();
+ let keys_manager = &chanmon_cfgs[0].keys_manager;
+ new_chain_monitor = test_utils::TestChainMonitor::new(Some(nodes[0].chain_source), nodes[0].tx_broadcaster.clone(), nodes[0].logger, node_cfgs[0].fee_estimator, &persister, keys_manager);
+ nodes[0].chain_monitor = &new_chain_monitor;
+ let mut chan_0_monitor_read = &chan_0_monitor_serialized.0[..];
+ let (_, mut chan_0_monitor) = <(BlockHash, ChannelMonitor<EnforcingSigner>)>::read(
+ &mut chan_0_monitor_read, keys_manager).unwrap();
+ assert!(chan_0_monitor_read.is_empty());
+
+ let (_, nodes_0_deserialized_tmp) = {
+ let mut channel_monitors = HashMap::new();
+ channel_monitors.insert(chan_0_monitor.get_funding_txo().0, &mut chan_0_monitor);
+ <(BlockHash, ChannelManager<EnforcingSigner, &test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestLogger>)>
+ ::read(&mut io::Cursor::new(&chan_manager_serialized.0[..]), ChannelManagerReadArgs {
+ default_config: Default::default(),
+ keys_manager,
+ fee_estimator: node_cfgs[0].fee_estimator,
+ chain_monitor: nodes[0].chain_monitor,
+ tx_broadcaster: nodes[0].tx_broadcaster.clone(),
+ logger: nodes[0].logger,
+ channel_monitors,
+ }).unwrap()
+ };
+ nodes_0_deserialized = nodes_0_deserialized_tmp;
+
+ assert!(nodes[0].chain_monitor.watch_channel(chan_0_monitor.get_funding_txo().0, chan_0_monitor).is_ok());
+ check_added_monitors!(nodes[0], 1);
+ nodes[0].node = &nodes_0_deserialized;
+
+ if persist_manager_post_event {
+ assert!(nodes[0].node.get_and_clear_pending_events().is_empty());
+ } else if payment_timeout {
+ expect_payment_failed!(nodes[0], payment_hash, true);
+ } else {
+ expect_payment_sent!(nodes[0], payment_preimage);
+ }
+
+ // Note that if we re-connect the block which exposed nodes[0] to the payment preimage (but
+ // which the current ChannelMonitor has not seen), the ChannelManager's de-duplication of
+ // payment events should kick in, leaving us with no pending events here.
+ let height = nodes[0].blocks.lock().unwrap().len() as u32 - 1;
+ nodes[0].chain_monitor.chain_monitor.block_connected(&claim_block, height);
+ assert!(nodes[0].node.get_and_clear_pending_events().is_empty());
+}
+
+#[test]
+fn test_dup_htlc_onchain_fails_on_reload() {
+ do_test_dup_htlc_onchain_fails_on_reload(true, true, true);
+ do_test_dup_htlc_onchain_fails_on_reload(true, true, false);
+ do_test_dup_htlc_onchain_fails_on_reload(true, false, false);
+ do_test_dup_htlc_onchain_fails_on_reload(false, true, true);
+ do_test_dup_htlc_onchain_fails_on_reload(false, true, false);
+ do_test_dup_htlc_onchain_fails_on_reload(false, false, false);
+}
+
+#[test]
+fn test_fulfill_restart_failure() {
+ // When we receive an update_fulfill_htlc message, we immediately consider the HTLC fully
+ // fulfilled. At this point, the peer can reconnect and decide to either fulfill the HTLC
+ // again, or fail it, giving us free money.
+ //
+ // Of course probably they won't fail it and give us free money, but because we have code to
+ // handle it, we should test the logic for it anyway. We do that here.
+ let chanmon_cfgs = create_chanmon_cfgs(2);
+ let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+ let persister: test_utils::TestPersister;
+ let new_chain_monitor: test_utils::TestChainMonitor;
+ let nodes_1_deserialized: ChannelManager<EnforcingSigner, &test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestLogger>;
+ let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+ let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known()).2;
+ let (payment_preimage, payment_hash, _) = route_payment(&nodes[0], &[&nodes[1]], 100_000);
+
+ // The simplest way to get a failure after a fulfill is to reload nodes[1] from a state
+ // pre-fulfill, which we do by serializing it here.
+ let mut chan_manager_serialized = test_utils::TestVecWriter(Vec::new());
+ nodes[1].node.write(&mut chan_manager_serialized).unwrap();
+ let mut chan_0_monitor_serialized = test_utils::TestVecWriter(Vec::new());
+ get_monitor!(nodes[1], chan_id).write(&mut chan_0_monitor_serialized).unwrap();
+
+ nodes[1].node.claim_funds(payment_preimage);
+ check_added_monitors!(nodes[1], 1);
+ let htlc_fulfill_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
+ nodes[0].node.handle_update_fulfill_htlc(&nodes[1].node.get_our_node_id(), &htlc_fulfill_updates.update_fulfill_htlcs[0]);
+ expect_payment_sent!(nodes[0], payment_preimage);
+
+ // Now reload nodes[1]...
+ persister = test_utils::TestPersister::new();
+ let keys_manager = &chanmon_cfgs[1].keys_manager;
+ new_chain_monitor = test_utils::TestChainMonitor::new(Some(nodes[1].chain_source), nodes[1].tx_broadcaster.clone(), nodes[1].logger, node_cfgs[1].fee_estimator, &persister, keys_manager);
+ nodes[1].chain_monitor = &new_chain_monitor;
+ let mut chan_0_monitor_read = &chan_0_monitor_serialized.0[..];
+ let (_, mut chan_0_monitor) = <(BlockHash, ChannelMonitor<EnforcingSigner>)>::read(
+ &mut chan_0_monitor_read, keys_manager).unwrap();
+ assert!(chan_0_monitor_read.is_empty());
+
+ let (_, nodes_1_deserialized_tmp) = {
+ let mut channel_monitors = HashMap::new();
+ channel_monitors.insert(chan_0_monitor.get_funding_txo().0, &mut chan_0_monitor);
+ <(BlockHash, ChannelManager<EnforcingSigner, &test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestLogger>)>
+ ::read(&mut io::Cursor::new(&chan_manager_serialized.0[..]), ChannelManagerReadArgs {
+ default_config: Default::default(),
+ keys_manager,
+ fee_estimator: node_cfgs[1].fee_estimator,
+ chain_monitor: nodes[1].chain_monitor,
+ tx_broadcaster: nodes[1].tx_broadcaster.clone(),
+ logger: nodes[1].logger,
+ channel_monitors,
+ }).unwrap()
+ };
+ nodes_1_deserialized = nodes_1_deserialized_tmp;
+
+ assert!(nodes[1].chain_monitor.watch_channel(chan_0_monitor.get_funding_txo().0, chan_0_monitor).is_ok());
+ check_added_monitors!(nodes[1], 1);
+ nodes[1].node = &nodes_1_deserialized;
+
+ nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
+ reconnect_nodes(&nodes[0], &nodes[1], (false, false), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (false, false));
+
+ nodes[1].node.fail_htlc_backwards(&payment_hash);
+ expect_pending_htlcs_forwardable!(nodes[1]);
+ check_added_monitors!(nodes[1], 1);
+ let htlc_fail_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
+ nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &htlc_fail_updates.update_fail_htlcs[0]);
+ commitment_signed_dance!(nodes[0], nodes[1], htlc_fail_updates.commitment_signed, false);
+ // nodes[0] shouldn't generate any events here, while it just got a payment failure completion
+ // it had already considered the payment fulfilled, and now they just got free money.
+}
use util::ser::{VecWriter, Writeable, Writer};
use ln::peer_channel_encryptor::{PeerChannelEncryptor,NextNoiseStep};
use ln::wire;
-use util::byte_utils;
+use util::atomic_counter::AtomicCounter;
use util::events::{MessageSendEvent, MessageSendEventsProvider};
use util::logger::Logger;
use routing::network_graph::NetGraphMsgHandler;
use io;
use alloc::collections::LinkedList;
use sync::{Arc, Mutex};
-use core::sync::atomic::{AtomicUsize, Ordering};
use core::{cmp, hash, fmt, mem};
use core::ops::Deref;
use core::convert::Infallible;
node_id_to_descriptor: HashMap<PublicKey, Descriptor>,
}
-#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))]
-fn _check_usize_is_32_or_64() {
- // See below, less than 32 bit pointers may be unsafe here!
- unsafe { mem::transmute::<*const usize, [u8; 4]>(panic!()); }
-}
-
/// SimpleArcPeerManager is useful when you need a PeerManager 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
ephemeral_key_midstate: Sha256Engine,
custom_message_handler: CMH,
- // Usize needs to be at least 32 bits to avoid overflowing both low and high. If usize is 64
- // bits we will never realistically count into high:
- peer_counter_low: AtomicUsize,
- peer_counter_high: AtomicUsize,
+ peer_counter: AtomicCounter,
logger: L,
}
}),
our_node_secret,
ephemeral_key_midstate,
- peer_counter_low: AtomicUsize::new(0),
- peer_counter_high: AtomicUsize::new(0),
+ peer_counter: AtomicCounter::new(),
logger,
custom_message_handler,
}
fn get_ephemeral_key(&self) -> SecretKey {
let mut ephemeral_hash = self.ephemeral_key_midstate.clone();
- let low = self.peer_counter_low.fetch_add(1, Ordering::AcqRel);
- let high = if low == 0 {
- self.peer_counter_high.fetch_add(1, Ordering::AcqRel)
- } else {
- self.peer_counter_high.load(Ordering::Acquire)
- };
- ephemeral_hash.input(&byte_utils::le64_to_array(low as u64));
- ephemeral_hash.input(&byte_utils::le64_to_array(high as u64));
+ let counter = self.peer_counter.get_increment();
+ ephemeral_hash.input(&counter.to_le_bytes());
SecretKey::from_slice(&Sha256::from_engine(ephemeral_hash).into_inner()).expect("You broke SHA-256!")
}
use chain::transaction::OutPoint;
use ln::{PaymentPreimage, PaymentHash};
use ln::channelmanager::PaymentSendFailure;
-use routing::router::get_route;
+use routing::router::{Payee, get_route};
use routing::network_graph::NetworkUpdate;
use routing::scorer::Scorer;
use ln::features::{InitFeatures, InvoiceFeatures};
let net_graph_msg_handler0 = &nodes[0].net_graph_msg_handler;
let net_graph_msg_handler1 = &nodes[1].net_graph_msg_handler;
- let route_1 = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler0.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger, &scorer).unwrap();
- let route_2 = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler1.network_graph, &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger, &scorer).unwrap();
+ let payee_1 = Payee::new(nodes[1].node.get_our_node_id()).with_features(InvoiceFeatures::known());
+ let route_1 = get_route(&nodes[0].node.get_our_node_id(), &payee_1, &net_graph_msg_handler0.network_graph, None, 100000, TEST_FINAL_CLTV, &logger, &scorer).unwrap();
+ let payee_2 = Payee::new(nodes[0].node.get_our_node_id()).with_features(InvoiceFeatures::known());
+ let route_2 = get_route(&nodes[1].node.get_our_node_id(), &payee_2, &net_graph_msg_handler1.network_graph, None, 100000, TEST_FINAL_CLTV, &logger, &scorer).unwrap();
unwrap_send_err!(nodes[0].node.send_payment(&route_1, payment_hash, &Some(payment_secret)), true, APIError::ChannelUnavailable {..}, {});
unwrap_send_err!(nodes[1].node.send_payment(&route_2, payment_hash, &Some(payment_secret)), true, APIError::ChannelUnavailable {..}, {});
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
- Event::PaymentSent { ref payment_preimage, ref payment_hash } => {
+ Event::PaymentSent { ref payment_preimage, ref payment_hash, .. } => {
assert_eq!(our_payment_preimage, *payment_preimage);
assert_eq!(our_payment_hash, *payment_hash);
},
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
- Event::PaymentSent { ref payment_preimage, ref payment_hash } => {
+ Event::PaymentSent { ref payment_preimage, ref payment_hash, .. } => {
assert_eq!(our_payment_preimage, *payment_preimage);
assert_eq!(our_payment_hash, *payment_hash);
},
pub mod router;
pub mod scorer;
+use routing::network_graph::NodeId;
+
/// An interface used to score payment channels for path finding.
///
/// Scoring is in terms of fees willing to be paid in order to avoid routing through a channel.
pub trait Score {
- /// Returns the fee in msats willing to be paid to avoid routing through the given channel.
- fn channel_penalty_msat(&self, short_channel_id: u64) -> u64;
+ /// Returns the fee in msats willing to be paid to avoid routing through the given channel
+ /// in the direction from `source` to `target`.
+ fn channel_penalty_msat(&self, short_channel_id: u64, source: &NodeId, target: &NodeId) -> u64;
}
assert!(network_graph.read_only().channels().get(&short_channel_id).unwrap().one_to_two.is_none());
net_graph_msg_handler.handle_event(&Event::PaymentPathFailed {
+ payment_id: None,
payment_hash: PaymentHash([0; 32]),
rejected_by_dest: false,
all_paths_failed: true,
msg: valid_channel_update,
}),
short_channel_id: None,
+ retry: None,
error_code: None,
error_data: None,
});
};
net_graph_msg_handler.handle_event(&Event::PaymentPathFailed {
+ payment_id: None,
payment_hash: PaymentHash([0; 32]),
rejected_by_dest: false,
all_paths_failed: true,
is_permanent: false,
}),
short_channel_id: None,
+ retry: None,
error_code: None,
error_data: None,
});
// Permanent closing deletes a channel
{
net_graph_msg_handler.handle_event(&Event::PaymentPathFailed {
+ payment_id: None,
payment_hash: PaymentHash([0; 32]),
rejected_by_dest: false,
all_paths_failed: true,
is_permanent: true,
}),
short_channel_id: None,
+ retry: None,
error_code: None,
error_data: None,
});
/// given path is variable, keeping the length of any path to less than 20 should currently
/// ensure it is viable.
pub paths: Vec<Vec<RouteHop>>,
+ /// The `payee` parameter passed to [`find_route`].
+ /// This is used by `ChannelManager` to track information which may be required for retries,
+ /// provided back to you via [`Event::PaymentPathFailed`].
+ ///
+ /// [`Event::PaymentPathFailed`]: crate::util::events::Event::PaymentPathFailed
+ pub payee: Option<Payee>,
+}
+
+pub(crate) trait RoutePath {
+ /// Gets the fees for a given path, excluding any excess paid to the recipient.
+ fn get_path_fees(&self) -> u64;
+}
+impl RoutePath for Vec<RouteHop> {
+ fn get_path_fees(&self) -> u64 {
+ // Do not count last hop of each path since that's the full value of the payment
+ self.split_last().map(|(_, path_prefix)| path_prefix).unwrap_or(&[])
+ .iter().map(|hop| &hop.fee_msat)
+ .sum()
+ }
}
impl Route {
/// Returns the total amount of fees paid on this [`Route`].
///
/// This doesn't include any extra payment made to the recipient, which can happen in excess of
- /// the amount passed to [`get_route`]'s `final_value_msat`.
+ /// the amount passed to [`find_route`]'s `params.final_value_msat`.
pub fn get_total_fees(&self) -> u64 {
- // Do not count last hop of each path since that's the full value of the payment
- return self.paths.iter()
- .flat_map(|path| path.split_last().map(|(_, path_prefix)| path_prefix).unwrap_or(&[]))
- .map(|hop| &hop.fee_msat)
- .sum();
+ self.paths.iter().map(|path| path.get_path_fees()).sum()
}
/// Returns the total amount paid on this [`Route`], excluding the fees.
hop.write(writer)?;
}
}
- write_tlv_fields!(writer, {});
+ write_tlv_fields!(writer, {
+ (1, self.payee, option),
+ });
Ok(())
}
}
}
paths.push(hops);
}
- read_tlv_fields!(reader, {});
- Ok(Route { paths })
+ let mut payee = None;
+ read_tlv_fields!(reader, {
+ (1, payee, option),
+ });
+ Ok(Route { paths, payee })
+ }
+}
+
+/// Parameters needed to find a [`Route`] for paying a [`Payee`].
+///
+/// Passed to [`find_route`] and also provided in [`Event::PaymentPathFailed`] for retrying a failed
+/// payment path.
+///
+/// [`Event::PaymentPathFailed`]: crate::util::events::Event::PaymentPathFailed
+#[derive(Clone, Debug)]
+pub struct RouteParameters {
+ /// The recipient of the failed payment path.
+ pub payee: Payee,
+
+ /// The amount in msats sent on the failed payment path.
+ pub final_value_msat: u64,
+
+ /// The CLTV on the final hop of the failed payment path.
+ pub final_cltv_expiry_delta: u32,
+}
+
+impl_writeable_tlv_based!(RouteParameters, {
+ (0, payee, required),
+ (2, final_value_msat, required),
+ (4, final_cltv_expiry_delta, required),
+});
+
+/// The recipient of a payment.
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+pub struct Payee {
+ /// The node id of the payee.
+ pub pubkey: PublicKey,
+
+ /// Features supported by the payee.
+ ///
+ /// May be set from the payee's invoice or via [`for_keysend`]. May be `None` if the invoice
+ /// does not contain any features.
+ ///
+ /// [`for_keysend`]: Self::for_keysend
+ pub features: Option<InvoiceFeatures>,
+
+ /// Hints for routing to the payee, containing channels connecting the payee to public nodes.
+ pub route_hints: Vec<RouteHint>,
+
+ /// Expiration of a payment to the payee, in seconds relative to the UNIX epoch.
+ pub expiry_time: Option<u64>,
+}
+
+impl_writeable_tlv_based!(Payee, {
+ (0, pubkey, required),
+ (2, features, option),
+ (4, route_hints, vec_type),
+ (6, expiry_time, option),
+});
+
+impl Payee {
+ /// Creates a payee with the node id of the given `pubkey`.
+ pub fn new(pubkey: PublicKey) -> Self {
+ Self {
+ pubkey,
+ features: None,
+ route_hints: vec![],
+ expiry_time: None,
+ }
+ }
+
+ /// Creates a payee with the node id of the given `pubkey` to use for keysend payments.
+ pub fn for_keysend(pubkey: PublicKey) -> Self {
+ Self::new(pubkey).with_features(InvoiceFeatures::for_keysend())
+ }
+
+ /// Includes the payee's features.
+ ///
+ /// (C-not exported) since bindings don't support move semantics
+ pub fn with_features(self, features: InvoiceFeatures) -> Self {
+ Self { features: Some(features), ..self }
+ }
+
+ /// Includes hints for routing to the payee.
+ ///
+ /// (C-not exported) since bindings don't support move semantics
+ pub fn with_route_hints(self, route_hints: Vec<RouteHint>) -> Self {
+ Self { route_hints, ..self }
+ }
+
+ /// Includes a payment expiration in seconds relative to the UNIX epoch.
+ pub fn with_expiry_time(self, expiry_time: u64) -> Self {
+ Self { expiry_time: Some(expiry_time), ..self }
}
}
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct RouteHint(pub Vec<RouteHintHop>);
+
+impl Writeable for RouteHint {
+ fn write<W: ::util::ser::Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+ (self.0.len() as u64).write(writer)?;
+ for hop in self.0.iter() {
+ hop.write(writer)?;
+ }
+ Ok(())
+ }
+}
+
+impl Readable for RouteHint {
+ fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
+ let hop_count: u64 = Readable::read(reader)?;
+ let mut hops = Vec::with_capacity(cmp::min(hop_count, 16) as usize);
+ for _ in 0..hop_count {
+ hops.push(Readable::read(reader)?);
+ }
+ Ok(Self(hops))
+ }
+}
+
/// A channel descriptor for a hop along a payment path.
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct RouteHintHop {
pub htlc_maximum_msat: Option<u64>,
}
+impl_writeable_tlv_based!(RouteHintHop, {
+ (0, src_node_id, required),
+ (1, htlc_minimum_msat, option),
+ (2, short_channel_id, required),
+ (3, htlc_maximum_msat, option),
+ (4, fees, required),
+ (6, cltv_expiry_delta, required),
+});
+
#[derive(Eq, PartialEq)]
struct RouteGraphNode {
node_id: NodeId,
}
}
-/// Gets a keysend route from us (payer) to the given target node (payee). This is needed because
-/// keysend payments do not have an invoice from which to pull the payee's supported features, which
-/// makes it tricky to otherwise supply the `payee_features` parameter of `get_route`.
-pub fn get_keysend_route<L: Deref, S: routing::Score>(
- our_node_pubkey: &PublicKey, network: &NetworkGraph, payee: &PublicKey,
- first_hops: Option<&[&ChannelDetails]>, last_hops: &[&RouteHint], final_value_msat: u64,
- final_cltv: u32, logger: L, scorer: &S
-) -> Result<Route, LightningError>
-where L::Target: Logger {
- let invoice_features = InvoiceFeatures::for_keysend();
- get_route(
- our_node_pubkey, network, payee, Some(invoice_features), first_hops, last_hops,
- final_value_msat, final_cltv, logger, scorer
- )
-}
-
-/// Gets a route from us (payer) to the given target node (payee).
+/// Finds a route from us (payer) to the given target node (payee).
///
-/// If the payee provided features in their invoice, they should be provided via payee_features.
+/// If the payee provided features in their invoice, they should be provided via `params.payee`.
/// Without this, MPP will only be used if the payee's features are available in the network graph.
///
-/// Private routing paths between a public node and the target may be included in `last_hops`.
-/// Currently, only the last hop in each path is considered.
+/// Private routing paths between a public node and the target may be included in `params.payee`.
+///
+/// If some channels aren't announced, it may be useful to fill in `first_hops` with the results
+/// from [`ChannelManager::list_usable_channels`]. If it is filled in, the view of our local
+/// channels from [`NetworkGraph`] will be ignored, and only those in `first_hops` will be used.
+///
+/// The fees on channels from us to the next hop are ignored as they are assumed to all be equal.
+/// However, the enabled/disabled bit on such channels as well as the `htlc_minimum_msat` /
+/// `htlc_maximum_msat` *are* checked as they may change based on the receiving node.
+///
+/// # Note
///
-/// If some channels aren't announced, it may be useful to fill in a first_hops with the
-/// results from a local ChannelManager::list_usable_channels() call. If it is filled in, our
-/// view of our local channels (from net_graph_msg_handler) will be ignored, and only those
-/// in first_hops will be used.
+/// May be used to re-compute a [`Route`] when handling a [`Event::PaymentPathFailed`]. Any
+/// adjustments to the [`NetworkGraph`] and channel scores should be made prior to calling this
+/// function.
///
-/// Panics if first_hops contains channels without short_channel_ids
-/// (ChannelManager::list_usable_channels will never include such channels).
+/// # Panics
///
-/// The fees on channels from us to next-hops are ignored (as they are assumed to all be
-/// equal), however the enabled/disabled bit on such channels as well as the
-/// htlc_minimum_msat/htlc_maximum_msat *are* checked as they may change based on the receiving node.
-pub fn get_route<L: Deref, S: routing::Score>(
- our_node_pubkey: &PublicKey, network: &NetworkGraph, payee: &PublicKey,
- payee_features: Option<InvoiceFeatures>, first_hops: Option<&[&ChannelDetails]>,
- last_hops: &[&RouteHint], final_value_msat: u64, final_cltv: u32, logger: L, scorer: &S
+/// Panics if first_hops contains channels without short_channel_ids;
+/// [`ChannelManager::list_usable_channels`] will never include such channels.
+///
+/// [`ChannelManager::list_usable_channels`]: crate::ln::channelmanager::ChannelManager::list_usable_channels
+/// [`Event::PaymentPathFailed`]: crate::util::events::Event::PaymentPathFailed
+pub fn find_route<L: Deref, S: routing::Score>(
+ our_node_pubkey: &PublicKey, params: &RouteParameters, network: &NetworkGraph,
+ first_hops: Option<&[&ChannelDetails]>, logger: L, scorer: &S
+) -> Result<Route, LightningError>
+where L::Target: Logger {
+ get_route(
+ our_node_pubkey, ¶ms.payee, network, first_hops, params.final_value_msat,
+ params.final_cltv_expiry_delta, logger, scorer
+ )
+}
+
+pub(crate) fn get_route<L: Deref, S: routing::Score>(
+ our_node_pubkey: &PublicKey, payee: &Payee, network: &NetworkGraph,
+ first_hops: Option<&[&ChannelDetails]>, final_value_msat: u64, final_cltv_expiry_delta: u32,
+ logger: L, scorer: &S
) -> Result<Route, LightningError>
where L::Target: Logger {
- let payee_node_id = NodeId::from_pubkey(&payee);
+ let payee_node_id = NodeId::from_pubkey(&payee.pubkey);
let our_node_id = NodeId::from_pubkey(&our_node_pubkey);
if payee_node_id == our_node_id {
return Err(LightningError{err: "Cannot send a payment of 0 msat".to_owned(), action: ErrorAction::IgnoreError});
}
- for route in last_hops.iter() {
+ for route in payee.route_hints.iter() {
for hop in &route.0 {
- if hop.src_node_id == *payee {
- return Err(LightningError{err: "Last hop cannot have a payee as a source.".to_owned(), action: ErrorAction::IgnoreError});
+ if hop.src_node_id == payee.pubkey {
+ return Err(LightningError{err: "Route hint cannot have the payee as the source.".to_owned(), action: ErrorAction::IgnoreError});
}
}
}
// Allow MPP only if we have a features set from somewhere that indicates the payee supports
// it. If the payee supports it they're supposed to include it in the invoice, so that should
// work reliably.
- let allow_mpp = if let Some(features) = &payee_features {
+ let allow_mpp = if let Some(features) = &payee.features {
features.supports_basic_mpp()
} else if let Some(node) = network_nodes.get(&payee_node_id) {
if let Some(node_info) = node.announcement_info.as_ref() {
node_info.features.supports_basic_mpp()
} else { false }
} else { false };
- log_trace!(logger, "Searching for a route from payer {} to payee {} {} MPP", our_node_pubkey, payee,
- if allow_mpp { "with" } else { "without" });
+ log_trace!(logger, "Searching for a route from payer {} to payee {} {} MPP", our_node_pubkey,
+ payee.pubkey, if allow_mpp { "with" } else { "without" });
// Step (1).
// Prepare the data we'll use for payee-to-payer search by
// - when we want to stop looking for new paths.
let mut already_collected_value_msat = 0;
- log_trace!(logger, "Building path from {} (payee) to {} (us/payer) for value {} msat.", payee, our_node_pubkey, final_value_msat);
+ log_trace!(logger, "Building path from {} (payee) to {} (us/payer) for value {} msat.", payee.pubkey, our_node_pubkey, final_value_msat);
macro_rules! add_entry {
// Adds entry which goes from $src_node_id to $dest_node_id
}
let path_penalty_msat = $next_hops_path_penalty_msat
- .checked_add(scorer.channel_penalty_msat($chan_id.clone()))
+ .checked_add(scorer.channel_penalty_msat($chan_id.clone(), &$src_node_id, &$dest_node_id))
.unwrap_or_else(|| u64::max_value());
let new_graph_node = RouteGraphNode {
node_id: $src_node_id,
// If a caller provided us with last hops, add them to routing targets. Since this happens
// earlier than general path finding, they will be somewhat prioritized, although currently
// it matters only if the fees are exactly the same.
- for route in last_hops.iter().filter(|route| !route.0.is_empty()) {
+ for route in payee.route_hints.iter().filter(|route| !route.0.is_empty()) {
let first_hop_in_route = &(route.0)[0];
let have_hop_src_in_graph =
// Only add the hops in this route to our candidate set if either
// We start building the path from reverse, i.e., from payee
// to the first RouteHintHop in the path.
let hop_iter = route.0.iter().rev();
- let prev_hop_iter = core::iter::once(payee).chain(
+ let prev_hop_iter = core::iter::once(&payee.pubkey).chain(
route.0.iter().skip(1).rev().map(|hop| &hop.src_node_id));
let mut hop_used = true;
let mut aggregate_next_hops_fee_msat: u64 = 0;
_ => aggregate_next_hops_fee_msat.checked_add(999).unwrap_or(u64::max_value())
}) { Some( val / 1000 ) } else { break; }; // converting from msat or breaking if max ~ infinity
+ let src_node_id = NodeId::from_pubkey(&hop.src_node_id);
+ let dest_node_id = NodeId::from_pubkey(&prev_hop_id);
aggregate_next_hops_path_penalty_msat = aggregate_next_hops_path_penalty_msat
- .checked_add(scorer.channel_penalty_msat(hop.short_channel_id))
+ .checked_add(scorer.channel_penalty_msat(hop.short_channel_id, &src_node_id, &dest_node_id))
.unwrap_or_else(|| u64::max_value());
// We assume that the recipient only included route hints for routes which had
// sufficient value to route `final_value_msat`. Note that in the case of "0-value"
// invoices where the invoice does not specify value this may not be the case, but
// better to include the hints than not.
- if !add_entry!(hop.short_channel_id, NodeId::from_pubkey(&hop.src_node_id), NodeId::from_pubkey(&prev_hop_id), directional_info, reqd_channel_cap, &empty_channel_features, aggregate_next_hops_fee_msat, path_value_msat, aggregate_next_hops_path_htlc_minimum_msat, aggregate_next_hops_path_penalty_msat) {
+ if !add_entry!(hop.short_channel_id, src_node_id, dest_node_id, directional_info, reqd_channel_cap, &empty_channel_features, aggregate_next_hops_fee_msat, path_value_msat, aggregate_next_hops_path_htlc_minimum_msat, aggregate_next_hops_path_penalty_msat) {
// If this hop was not used then there is no use checking the preceding hops
// in the RouteHint. We can break by just searching for a direct channel between
// last checked hop and first_hop_targets
}
ordered_hops.last_mut().unwrap().0.fee_msat = value_contribution_msat;
ordered_hops.last_mut().unwrap().0.hop_use_fee_msat = 0;
- ordered_hops.last_mut().unwrap().0.cltv_expiry_delta = final_cltv;
+ ordered_hops.last_mut().unwrap().0.cltv_expiry_delta = final_cltv_expiry_delta;
log_trace!(logger, "Found a path back to us from the target with {} hops contributing up to {} msat: {:?}",
ordered_hops.len(), value_contribution_msat, ordered_hops);
}).collect());
}
- if let Some(features) = &payee_features {
+ if let Some(features) = &payee.features {
for path in selected_paths.iter_mut() {
if let Ok(route_hop) = path.last_mut().unwrap() {
route_hop.node_features = features.to_context();
}
}
- let route = Route { paths: selected_paths.into_iter().map(|path| path.into_iter().collect()).collect::<Result<Vec<_>, _>>()? };
- log_info!(logger, "Got route to {}: {}", payee, log_route!(route));
+ let route = Route {
+ paths: selected_paths.into_iter().map(|path| path.into_iter().collect()).collect::<Result<Vec<_>, _>>()?,
+ payee: Some(payee.clone()),
+ };
+ log_info!(logger, "Got route to {}: {}", payee.pubkey, log_route!(route));
Ok(route)
}
#[cfg(test)]
mod tests {
- use routing::network_graph::{NetworkGraph, NetGraphMsgHandler};
- use routing::router::{get_route, Route, RouteHint, RouteHintHop, RouteHop, RoutingFees};
+ use routing;
+ use routing::network_graph::{NetworkGraph, NetGraphMsgHandler, NodeId};
+ use routing::router::{get_route, Payee, Route, RouteHint, RouteHintHop, RouteHop, RoutingFees};
use routing::scorer::Scorer;
use chain::transaction::OutPoint;
use ln::features::{ChannelFeatures, InitFeatures, InvoiceFeatures, NodeFeatures};
fn simple_route_test() {
let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
+ let payee = Payee::new(nodes[2]);
let scorer = Scorer::new(0);
// Simple route to 2 via 1
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 0, 42, Arc::clone(&logger), &scorer) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 0, 42, Arc::clone(&logger), &scorer) {
assert_eq!(err, "Cannot send a payment of 0 msat");
} else { panic!(); }
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 100, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths[0].len(), 2);
assert_eq!(route.paths[0][0].pubkey, nodes[1]);
fn invalid_first_hop_test() {
let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
+ let payee = Payee::new(nodes[2]);
let scorer = Scorer::new(0);
// Simple route to 2 via 1
let our_chans = vec![get_channel_details(Some(2), our_id, InitFeatures::from_le_bytes(vec![0b11]), 100000)];
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, Some(&our_chans.iter().collect::<Vec<_>>()), &Vec::new(), 100, 42, Arc::clone(&logger), &scorer) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, Some(&our_chans.iter().collect::<Vec<_>>()), 100, 42, Arc::clone(&logger), &scorer) {
assert_eq!(err, "First hop cannot have our_node_pubkey as a destination.");
} else { panic!(); }
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 100, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths[0].len(), 2);
}
fn htlc_minimum_test() {
let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
+ let payee = Payee::new(nodes[2]);
let scorer = Scorer::new(0);
// Simple route to 2 via 1
});
// Not possible to send 199_999_999, because the minimum on channel=2 is 200_000_000.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 199_999_999, 42, Arc::clone(&logger), &scorer) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 199_999_999, 42, Arc::clone(&logger), &scorer) {
assert_eq!(err, "Failed to find a path to the given destination");
} else { panic!(); }
});
// A payment above the minimum should pass
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 199_999_999, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 199_999_999, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths[0].len(), 2);
}
fn htlc_minimum_overpay_test() {
let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
+ let payee = Payee::new(nodes[2]).with_features(InvoiceFeatures::known());
let scorer = Scorer::new(0);
// A route to node#2 via two paths.
excess_data: Vec::new()
});
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 60_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 60_000, 42, Arc::clone(&logger), &scorer).unwrap();
// Overpay fees to hit htlc_minimum_msat.
let overpaid_fees = route.paths[0][0].fee_msat + route.paths[1][0].fee_msat;
// TODO: this could be better balanced to overpay 10k and not 15k.
excess_data: Vec::new()
});
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 60_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 60_000, 42, Arc::clone(&logger), &scorer).unwrap();
// Fine to overpay for htlc_minimum_msat if it allows us to save fee.
assert_eq!(route.paths.len(), 1);
assert_eq!(route.paths[0][0].short_channel_id, 12);
let fees = route.paths[0][0].fee_msat;
assert_eq!(fees, 5_000);
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 50_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 50_000, 42, Arc::clone(&logger), &scorer).unwrap();
// Not fine to overpay for htlc_minimum_msat if it requires paying more than fee on
// the other channel.
assert_eq!(route.paths.len(), 1);
fn disable_channels_test() {
let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
+ let payee = Payee::new(nodes[2]);
let scorer = Scorer::new(0);
// // Disable channels 4 and 12 by flags=2
});
// If all the channels require some features we don't understand, route should fail
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 100, 42, Arc::clone(&logger), &scorer) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, Arc::clone(&logger), &scorer) {
assert_eq!(err, "Failed to find a path to the given destination");
} else { panic!(); }
// If we specify a channel to node7, that overrides our local channel view and that gets used
let our_chans = vec![get_channel_details(Some(42), nodes[7].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)];
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, Some(&our_chans.iter().collect::<Vec<_>>()), &Vec::new(), 100, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, Some(&our_chans.iter().collect::<Vec<_>>()), 100, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths[0].len(), 2);
assert_eq!(route.paths[0][0].pubkey, nodes[7]);
fn disable_node_test() {
let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
let (_, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
+ let payee = Payee::new(nodes[2]);
let scorer = Scorer::new(0);
// Disable nodes 1, 2, and 8 by requiring unknown feature bits
add_or_update_node(&net_graph_msg_handler, &secp_ctx, &privkeys[7], unknown_features.clone(), 1);
// If all nodes require some features we don't understand, route should fail
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 100, 42, Arc::clone(&logger), &scorer) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, Arc::clone(&logger), &scorer) {
assert_eq!(err, "Failed to find a path to the given destination");
} else { panic!(); }
// If we specify a channel to node7, that overrides our local channel view and that gets used
let our_chans = vec![get_channel_details(Some(42), nodes[7].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)];
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, Some(&our_chans.iter().collect::<Vec<_>>()), &Vec::new(), 100, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, Some(&our_chans.iter().collect::<Vec<_>>()), 100, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths[0].len(), 2);
assert_eq!(route.paths[0][0].pubkey, nodes[7]);
let scorer = Scorer::new(0);
// Route to 1 via 2 and 3 because our channel to 1 is disabled
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[0], None, None, &Vec::new(), 100, 42, Arc::clone(&logger), &scorer).unwrap();
+ let payee = Payee::new(nodes[0]);
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths[0].len(), 3);
assert_eq!(route.paths[0][0].pubkey, nodes[1]);
assert_eq!(route.paths[0][2].channel_features.le_flags(), &id_to_feature_flags(3));
// If we specify a channel to node7, that overrides our local channel view and that gets used
+ let payee = Payee::new(nodes[2]);
let our_chans = vec![get_channel_details(Some(42), nodes[7].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)];
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, Some(&our_chans.iter().collect::<Vec<_>>()), &Vec::new(), 100, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, Some(&our_chans.iter().collect::<Vec<_>>()), 100, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths[0].len(), 2);
assert_eq!(route.paths[0][0].pubkey, nodes[7]);
let mut invalid_last_hops = last_hops_multi_private_channels(&nodes);
invalid_last_hops.push(invalid_last_hop);
{
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &invalid_last_hops.iter().collect::<Vec<_>>(), 100, 42, Arc::clone(&logger), &scorer) {
- assert_eq!(err, "Last hop cannot have a payee as a source.");
+ let payee = Payee::new(nodes[6]).with_route_hints(invalid_last_hops);
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, Arc::clone(&logger), &scorer) {
+ assert_eq!(err, "Route hint cannot have the payee as the source.");
} else { panic!(); }
}
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &last_hops_multi_private_channels(&nodes).iter().collect::<Vec<_>>(), 100, 42, Arc::clone(&logger), &scorer).unwrap();
+ let payee = Payee::new(nodes[6]).with_route_hints(last_hops_multi_private_channels(&nodes));
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths[0].len(), 5);
assert_eq!(route.paths[0][0].pubkey, nodes[1]);
fn ignores_empty_last_hops_test() {
let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
+ let payee = Payee::new(nodes[6]).with_route_hints(empty_last_hop(&nodes));
let scorer = Scorer::new(0);
// Test handling of an empty RouteHint passed in Invoice.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &empty_last_hop(&nodes).iter().collect::<Vec<_>>(), 100, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths[0].len(), 5);
assert_eq!(route.paths[0][0].pubkey, nodes[1]);
fn multi_hint_last_hops_test() {
let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
let (_, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
+ let payee = Payee::new(nodes[6]).with_route_hints(multi_hint_last_hops(&nodes));
let scorer = Scorer::new(0);
// Test through channels 2, 3, 5, 8.
// Test shows that multiple hop hints are considered.
excess_data: Vec::new()
});
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &multi_hint_last_hops(&nodes).iter().collect::<Vec<_>>(), 100, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths[0].len(), 4);
assert_eq!(route.paths[0][0].pubkey, nodes[1]);
fn last_hops_with_public_channel_test() {
let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
+ let payee = Payee::new(nodes[6]).with_route_hints(last_hops_with_public_channel(&nodes));
let scorer = Scorer::new(0);
// This test shows that public routes can be present in the invoice
// which would be handled in the same manner.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &last_hops_with_public_channel(&nodes).iter().collect::<Vec<_>>(), 100, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths[0].len(), 5);
assert_eq!(route.paths[0][0].pubkey, nodes[1]);
// Simple test with outbound channel to 4 to test that last_hops and first_hops connect
let our_chans = vec![get_channel_details(Some(42), nodes[3].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)];
let mut last_hops = last_hops(&nodes);
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, Some(&our_chans.iter().collect::<Vec<_>>()), &last_hops.iter().collect::<Vec<_>>(), 100, 42, Arc::clone(&logger), &scorer).unwrap();
+ let payee = Payee::new(nodes[6]).with_route_hints(last_hops.clone());
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, Some(&our_chans.iter().collect::<Vec<_>>()), 100, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths[0].len(), 2);
assert_eq!(route.paths[0][0].pubkey, nodes[3]);
last_hops[0].0[0].fees.base_msat = 1000;
// Revert to via 6 as the fee on 8 goes up
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &last_hops.iter().collect::<Vec<_>>(), 100, 42, Arc::clone(&logger), &scorer).unwrap();
+ let payee = Payee::new(nodes[6]).with_route_hints(last_hops);
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths[0].len(), 4);
assert_eq!(route.paths[0][0].pubkey, nodes[1]);
assert_eq!(route.paths[0][3].channel_features.le_flags(), &Vec::<u8>::new()); // We can't learn any flags from invoices, sadly
// ...but still use 8 for larger payments as 6 has a variable feerate
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &last_hops.iter().collect::<Vec<_>>(), 2000, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 2000, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths[0].len(), 5);
assert_eq!(route.paths[0][0].pubkey, nodes[1]);
htlc_minimum_msat: None,
htlc_maximum_msat: last_hop_htlc_max,
}]);
+ let payee = Payee::new(target_node_id).with_route_hints(vec![last_hops]);
let our_chans = vec![get_channel_details(Some(42), middle_node_id, InitFeatures::from_le_bytes(vec![0b11]), outbound_capacity_msat)];
let scorer = Scorer::new(0);
- get_route(&source_node_id, &NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash()), &target_node_id, None, Some(&our_chans.iter().collect::<Vec<_>>()), &vec![&last_hops], route_val, 42, &test_utils::TestLogger::new(), &scorer)
+ get_route(&source_node_id, &payee, &NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash()), Some(&our_chans.iter().collect::<Vec<_>>()), route_val, 42, &test_utils::TestLogger::new(), &scorer)
}
#[test]
let (secp_ctx, mut net_graph_msg_handler, chain_monitor, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::new(0);
+ let payee = Payee::new(nodes[2]).with_features(InvoiceFeatures::known());
// We will use a simple single-path route from
// our node to node2 via node0: channels {1, 3}.
{
// Attempt to route more than available results in a failure.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 250_000_001, 42, Arc::clone(&logger), &scorer) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
+ &our_id, &payee, &net_graph_msg_handler.network_graph, None, 250_000_001, 42, Arc::clone(&logger), &scorer) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
}
{
// Now, attempt to route an exact amount we have should be fine.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 250_000_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 250_000_000, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths.len(), 1);
let path = route.paths.last().unwrap();
assert_eq!(path.len(), 2);
{
// Attempt to route more than available results in a failure.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
- Some(InvoiceFeatures::known()), Some(&our_chans.iter().collect::<Vec<_>>()), &Vec::new(), 200_000_001, 42, Arc::clone(&logger), &scorer) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
+ &our_id, &payee, &net_graph_msg_handler.network_graph, Some(&our_chans.iter().collect::<Vec<_>>()), 200_000_001, 42, Arc::clone(&logger), &scorer) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
}
{
// Now, attempt to route an exact amount we have should be fine.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
- Some(InvoiceFeatures::known()), Some(&our_chans.iter().collect::<Vec<_>>()), &Vec::new(), 200_000_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, Some(&our_chans.iter().collect::<Vec<_>>()), 200_000_000, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths.len(), 1);
let path = route.paths.last().unwrap();
assert_eq!(path.len(), 2);
{
// Attempt to route more than available results in a failure.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 15_001, 42, Arc::clone(&logger), &scorer) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
+ &our_id, &payee, &net_graph_msg_handler.network_graph, None, 15_001, 42, Arc::clone(&logger), &scorer) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
}
{
// Now, attempt to route an exact amount we have should be fine.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 15_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 15_000, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths.len(), 1);
let path = route.paths.last().unwrap();
assert_eq!(path.len(), 2);
{
// Attempt to route more than available results in a failure.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 15_001, 42, Arc::clone(&logger), &scorer) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
+ &our_id, &payee, &net_graph_msg_handler.network_graph, None, 15_001, 42, Arc::clone(&logger), &scorer) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
}
{
// Now, attempt to route an exact amount we have should be fine.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 15_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 15_000, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths.len(), 1);
let path = route.paths.last().unwrap();
assert_eq!(path.len(), 2);
{
// Attempt to route more than available results in a failure.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 10_001, 42, Arc::clone(&logger), &scorer) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
+ &our_id, &payee, &net_graph_msg_handler.network_graph, None, 10_001, 42, Arc::clone(&logger), &scorer) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
}
{
// Now, attempt to route an exact amount we have should be fine.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 10_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 10_000, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths.len(), 1);
let path = route.paths.last().unwrap();
assert_eq!(path.len(), 2);
let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::new(0);
+ let payee = Payee::new(nodes[3]).with_features(InvoiceFeatures::known());
// Path via {node7, node2, node4} is channels {12, 13, 6, 11}.
// {12, 13, 11} have the capacities of 100, {6} has a capacity of 50.
});
{
// Attempt to route more than available results in a failure.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 60_000, 42, Arc::clone(&logger), &scorer) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
+ &our_id, &payee, &net_graph_msg_handler.network_graph, None, 60_000, 42, Arc::clone(&logger), &scorer) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
}
{
// Now, attempt to route 49 sats (just a bit below the capacity).
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 49_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 49_000, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths.len(), 1);
let mut total_amount_paid_msat = 0;
for path in &route.paths {
{
// Attempt to route an exact amount is also fine
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 50_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 50_000, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths.len(), 1);
let mut total_amount_paid_msat = 0;
for path in &route.paths {
let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::new(0);
+ let payee = Payee::new(nodes[2]);
// Path via node0 is channels {1, 3}. Limit them to 100 and 50 sats (total limit 50).
update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
});
{
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 50_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 50_000, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths.len(), 1);
let mut total_amount_paid_msat = 0;
for path in &route.paths {
let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::new(0);
+ let payee = Payee::new(nodes[2]).with_features(InvoiceFeatures::known());
// We need a route consisting of 3 paths:
// From our node to node2 via node0, node7, node1 (three paths one hop each).
{
// Attempt to route more than available results in a failure.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph,
- &nodes[2], Some(InvoiceFeatures::known()), None, &Vec::new(), 300_000, 42, Arc::clone(&logger), &scorer) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
+ &our_id, &payee, &net_graph_msg_handler.network_graph, None, 300_000, 42, Arc::clone(&logger), &scorer) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
}
{
// Now, attempt to route 250 sats (just a bit below the capacity).
// Our algorithm should provide us with these 3 paths.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 250_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 250_000, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths.len(), 3);
let mut total_amount_paid_msat = 0;
for path in &route.paths {
{
// Attempt to route an exact amount is also fine
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 290_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 290_000, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths.len(), 3);
let mut total_amount_paid_msat = 0;
for path in &route.paths {
let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::new(0);
+ let payee = Payee::new(nodes[3]).with_features(InvoiceFeatures::known());
// We need a route consisting of 3 paths:
// From our node to node3 via {node0, node2}, {node7, node2, node4} and {node7, node2}.
{
// Attempt to route more than available results in a failure.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 350_000, 42, Arc::clone(&logger), &scorer) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
+ &our_id, &payee, &net_graph_msg_handler.network_graph, None, 350_000, 42, Arc::clone(&logger), &scorer) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
}
{
// Now, attempt to route 300 sats (exact amount we can route).
// Our algorithm should provide us with these 3 paths, 100 sats each.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 300_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 300_000, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths.len(), 3);
let mut total_amount_paid_msat = 0;
let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::new(0);
+ let payee = Payee::new(nodes[3]).with_features(InvoiceFeatures::known());
// This test checks that if we have two cheaper paths and one more expensive path,
// so that liquidity-wise any 2 of 3 combination is sufficient,
{
// Now, attempt to route 180 sats.
// Our algorithm should provide us with these 2 paths.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 180_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 180_000, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths.len(), 2);
let mut total_value_transferred_msat = 0;
let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::new(0);
+ let payee = Payee::new(nodes[3]).with_features(InvoiceFeatures::known());
// We need a route consisting of 2 paths:
// From our node to node3 via {node0, node2} and {node7, node2, node4}.
{
// Attempt to route more than available results in a failure.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 210_000, 42, Arc::clone(&logger), &scorer) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
+ &our_id, &payee, &net_graph_msg_handler.network_graph, None, 210_000, 42, Arc::clone(&logger), &scorer) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
}
{
// Now, attempt to route 200 sats (exact amount we can route).
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 200_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 200_000, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths.len(), 2);
let mut total_amount_paid_msat = 0;
let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::new(0);
+ let payee = Payee::new(nodes[2]).with_features(InvoiceFeatures::known());
// We need a route consisting of 3 paths:
// From our node to node2 via node0, node7, node1 (three paths one hop each).
{
// Attempt to route more than available results in a failure.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 150_000, 42, Arc::clone(&logger), &scorer) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
+ &our_id, &payee, &net_graph_msg_handler.network_graph, None, 150_000, 42, Arc::clone(&logger), &scorer) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
}
{
// Now, attempt to route 125 sats (just a bit below the capacity of 3 channels).
// Our algorithm should provide us with these 3 paths.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 125_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 125_000, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths.len(), 3);
let mut total_amount_paid_msat = 0;
for path in &route.paths {
{
// Attempt to route without the last small cheap channel
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
- Some(InvoiceFeatures::known()), None, &Vec::new(), 90_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 90_000, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths.len(), 2);
let mut total_amount_paid_msat = 0;
for path in &route.paths {
let net_graph_msg_handler = NetGraphMsgHandler::new(network_graph, None, Arc::clone(&logger));
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::new(0);
+ let payee = Payee::new(nodes[6]);
add_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, &privkeys[1], ChannelFeatures::from_le_bytes(id_to_feature_flags(6)), 6);
update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
{
// Now ensure the route flows simply over nodes 1 and 4 to 6.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &Vec::new(), 10_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 10_000, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths.len(), 1);
assert_eq!(route.paths[0].len(), 3);
let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, _, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::new(0);
+ let payee = Payee::new(nodes[2]);
// We modify the graph to set the htlc_maximum of channel 2 to below the value we wish to
// send.
{
// Now, attempt to route 90 sats, which is exactly 90 sats at the last hop, plus the
// 200% fee charged channel 13 in the 1-to-2 direction.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 90_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 90_000, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths.len(), 1);
assert_eq!(route.paths[0].len(), 2);
let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let scorer = Scorer::new(0);
+ let payee = Payee::new(nodes[2]).with_features(InvoiceFeatures::known());
// We modify the graph to set the htlc_minimum of channel 2 and 4 as needed - channel 2
// gets an htlc_maximum_msat of 80_000 and channel 4 an htlc_minimum_msat of 90_000. We
// Now, attempt to route 90 sats, hitting the htlc_minimum on channel 4, but
// overshooting the htlc_maximum on channel 2. Thus, we should pick the (absurdly
// expensive) channels 12-13 path.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], Some(InvoiceFeatures::known()), None, &Vec::new(), 90_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ let route = get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, 90_000, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths.len(), 1);
assert_eq!(route.paths[0].len(), 2);
let logger = Arc::new(test_utils::TestLogger::new());
let network_graph = NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash());
let scorer = Scorer::new(0);
+ let payee = Payee::new(nodes[0]).with_features(InvoiceFeatures::known());
{
- let route = get_route(&our_id, &network_graph, &nodes[0], Some(InvoiceFeatures::known()), Some(&[
+ let route = get_route(&our_id, &payee, &network_graph, Some(&[
&get_channel_details(Some(3), nodes[0], InitFeatures::known(), 200_000),
&get_channel_details(Some(2), nodes[0], InitFeatures::known(), 10_000),
- ]), &[], 100_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ ]), 100_000, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths.len(), 1);
assert_eq!(route.paths[0].len(), 1);
assert_eq!(route.paths[0][0].fee_msat, 100_000);
}
{
- let route = get_route(&our_id, &network_graph, &nodes[0], Some(InvoiceFeatures::known()), Some(&[
+ let route = get_route(&our_id, &payee, &network_graph, Some(&[
&get_channel_details(Some(3), nodes[0], InitFeatures::known(), 50_000),
&get_channel_details(Some(2), nodes[0], InitFeatures::known(), 50_000),
- ]), &[], 100_000, 42, Arc::clone(&logger), &scorer).unwrap();
+ ]), 100_000, 42, Arc::clone(&logger), &scorer).unwrap();
assert_eq!(route.paths.len(), 2);
assert_eq!(route.paths[0].len(), 1);
assert_eq!(route.paths[1].len(), 1);
fn prefers_shorter_route_with_higher_fees() {
let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
+ let payee = Payee::new(nodes[6]).with_route_hints(last_hops(&nodes));
+
+ // Without penalizing each hop 100 msats, a longer path with lower fees is chosen.
+ let scorer = Scorer::new(0);
+ let route = get_route(
+ &our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42,
+ Arc::clone(&logger), &scorer
+ ).unwrap();
+ let path = route.paths[0].iter().map(|hop| hop.short_channel_id).collect::<Vec<_>>();
+
+ assert_eq!(route.get_total_fees(), 100);
+ assert_eq!(route.get_total_amount(), 100);
+ assert_eq!(path, vec![2, 4, 6, 11, 8]);
// Applying a 100 msat penalty to each hop results in taking channels 7 and 10 to nodes[6]
// from nodes[2] rather than channel 6, 11, and 8, even though the longer path is cheaper.
let scorer = Scorer::new(100);
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &last_hops(&nodes).iter().collect::<Vec<_>>(), 100, 42, Arc::clone(&logger), &scorer).unwrap();
- assert_eq!(route.paths[0].len(), 4);
+ let route = get_route(
+ &our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42,
+ Arc::clone(&logger), &scorer
+ ).unwrap();
+ let path = route.paths[0].iter().map(|hop| hop.short_channel_id).collect::<Vec<_>>();
- assert_eq!(route.paths[0][0].pubkey, nodes[1]);
- assert_eq!(route.paths[0][0].short_channel_id, 2);
- assert_eq!(route.paths[0][0].fee_msat, 200);
- assert_eq!(route.paths[0][0].cltv_expiry_delta, (4 << 8) | 1);
- assert_eq!(route.paths[0][0].node_features.le_flags(), &id_to_feature_flags(2));
- assert_eq!(route.paths[0][0].channel_features.le_flags(), &id_to_feature_flags(2));
+ assert_eq!(route.get_total_fees(), 300);
+ assert_eq!(route.get_total_amount(), 100);
+ assert_eq!(path, vec![2, 4, 7, 10]);
+ }
- assert_eq!(route.paths[0][1].pubkey, nodes[2]);
- assert_eq!(route.paths[0][1].short_channel_id, 4);
- assert_eq!(route.paths[0][1].fee_msat, 100);
- assert_eq!(route.paths[0][1].cltv_expiry_delta, (7 << 8) | 1);
- assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags(3));
- assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags(4));
+ struct BadChannelScorer {
+ short_channel_id: u64,
+ }
- assert_eq!(route.paths[0][2].pubkey, nodes[5]);
- assert_eq!(route.paths[0][2].short_channel_id, 7);
- assert_eq!(route.paths[0][2].fee_msat, 0);
- assert_eq!(route.paths[0][2].cltv_expiry_delta, (10 << 8) | 1);
- assert_eq!(route.paths[0][2].node_features.le_flags(), &id_to_feature_flags(6));
- assert_eq!(route.paths[0][2].channel_features.le_flags(), &id_to_feature_flags(7));
+ impl routing::Score for BadChannelScorer {
+ fn channel_penalty_msat(&self, short_channel_id: u64, _source: &NodeId, _target: &NodeId) -> u64 {
+ if short_channel_id == self.short_channel_id { u64::max_value() } else { 0 }
+ }
+ }
- assert_eq!(route.paths[0][3].pubkey, nodes[6]);
- assert_eq!(route.paths[0][3].short_channel_id, 10);
- assert_eq!(route.paths[0][3].fee_msat, 100);
- assert_eq!(route.paths[0][3].cltv_expiry_delta, 42);
- assert_eq!(route.paths[0][3].node_features.le_flags(), &Vec::<u8>::new()); // We don't pass flags in from invoices yet
- assert_eq!(route.paths[0][3].channel_features.le_flags(), &Vec::<u8>::new()); // We can't learn any flags from invoices, sadly
+ struct BadNodeScorer {
+ node_id: NodeId,
+ }
+
+ impl routing::Score for BadNodeScorer {
+ fn channel_penalty_msat(&self, _short_channel_id: u64, _source: &NodeId, target: &NodeId) -> u64 {
+ if *target == self.node_id { u64::max_value() } else { 0 }
+ }
+ }
+
+ #[test]
+ fn avoids_routing_through_bad_channels_and_nodes() {
+ let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
+ let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
+ let payee = Payee::new(nodes[6]).with_route_hints(last_hops(&nodes));
+
+ // A path to nodes[6] exists when no penalties are applied to any channel.
+ let scorer = Scorer::new(0);
+ let route = get_route(
+ &our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42,
+ Arc::clone(&logger), &scorer
+ ).unwrap();
+ let path = route.paths[0].iter().map(|hop| hop.short_channel_id).collect::<Vec<_>>();
+
+ assert_eq!(route.get_total_fees(), 100);
+ assert_eq!(route.get_total_amount(), 100);
+ assert_eq!(path, vec![2, 4, 6, 11, 8]);
+
+ // A different path to nodes[6] exists if channel 6 cannot be routed over.
+ let scorer = BadChannelScorer { short_channel_id: 6 };
+ let route = get_route(
+ &our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42,
+ Arc::clone(&logger), &scorer
+ ).unwrap();
+ let path = route.paths[0].iter().map(|hop| hop.short_channel_id).collect::<Vec<_>>();
assert_eq!(route.get_total_fees(), 300);
assert_eq!(route.get_total_amount(), 100);
+ assert_eq!(path, vec![2, 4, 7, 10]);
+
+ // A path to nodes[6] does not exist if nodes[2] cannot be routed through.
+ let scorer = BadNodeScorer { node_id: NodeId::from_pubkey(&nodes[2]) };
+ match get_route(
+ &our_id, &payee, &net_graph_msg_handler.network_graph, None, 100, 42,
+ Arc::clone(&logger), &scorer
+ ) {
+ Err(LightningError { err, .. } ) => {
+ assert_eq!(err, "Failed to find a path to the given destination");
+ },
+ Ok(_) => panic!("Expected error"),
+ }
}
#[test]
short_channel_id: 0, fee_msat: 225, cltv_expiry_delta: 0
},
]],
+ payee: None,
};
assert_eq!(route.get_total_fees(), 250);
short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0
},
]],
+ payee: None,
};
assert_eq!(route.get_total_fees(), 200);
// In an earlier version of `Route::get_total_fees` and `Route::get_total_amount`, they
// would both panic if the route was completely empty. We test to ensure they return 0
// here, even though its somewhat nonsensical as a route.
- let route = Route { paths: Vec::new() };
+ let route = Route { paths: Vec::new(), payee: None };
assert_eq!(route.get_total_fees(), 0);
assert_eq!(route.get_total_amount(), 0);
seed = seed.overflowing_mul(0xdeadbeef).0;
let src = &PublicKey::from_slice(nodes.keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap();
seed = seed.overflowing_mul(0xdeadbeef).0;
- let dst = &PublicKey::from_slice(nodes.keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap();
+ let dst = PublicKey::from_slice(nodes.keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap();
+ let payee = Payee::new(dst);
let amt = seed as u64 % 200_000_000;
- if get_route(src, &graph, dst, None, None, &[], amt, 42, &test_utils::TestLogger::new(), &scorer).is_ok() {
+ if get_route(src, &payee, &graph, None, amt, 42, &test_utils::TestLogger::new(), &scorer).is_ok() {
continue 'load_endpoints;
}
}
seed = seed.overflowing_mul(0xdeadbeef).0;
let src = &PublicKey::from_slice(nodes.keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap();
seed = seed.overflowing_mul(0xdeadbeef).0;
- let dst = &PublicKey::from_slice(nodes.keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap();
+ let dst = PublicKey::from_slice(nodes.keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap();
+ let payee = Payee::new(dst).with_features(InvoiceFeatures::known());
let amt = seed as u64 % 200_000_000;
- if get_route(src, &graph, dst, Some(InvoiceFeatures::known()), None, &[], amt, 42, &test_utils::TestLogger::new(), &scorer).is_ok() {
+ if get_route(src, &payee, &graph, None, amt, 42, &test_utils::TestLogger::new(), &scorer).is_ok() {
continue 'load_endpoints;
}
}
let src = PublicKey::from_slice(nodes.keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap();
seed *= 0xdeadbeef;
let dst = PublicKey::from_slice(nodes.keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap();
+ let payee = Payee::new(dst);
let amt = seed as u64 % 1_000_000;
- if get_route(&src, &graph, &dst, None, None, &[], amt, 42, &DummyLogger{}, &scorer).is_ok() {
+ if get_route(&src, &payee, &graph, None, amt, 42, &DummyLogger{}, &scorer).is_ok() {
path_endpoints.push((src, dst, amt));
continue 'load_endpoints;
}
let mut idx = 0;
bench.iter(|| {
let (src, dst, amt) = path_endpoints[idx % path_endpoints.len()];
- assert!(get_route(&src, &graph, &dst, None, None, &[], amt, 42, &DummyLogger{}, &scorer).is_ok());
+ let payee = Payee::new(dst);
+ assert!(get_route(&src, &payee, &graph, None, amt, 42, &DummyLogger{}, &scorer).is_ok());
idx += 1;
});
}
let src = PublicKey::from_slice(nodes.keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap();
seed *= 0xdeadbeef;
let dst = PublicKey::from_slice(nodes.keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap();
+ let payee = Payee::new(dst).with_features(InvoiceFeatures::known());
let amt = seed as u64 % 1_000_000;
- if get_route(&src, &graph, &dst, Some(InvoiceFeatures::known()), None, &[], amt, 42, &DummyLogger{}, &scorer).is_ok() {
+ if get_route(&src, &payee, &graph, None, amt, 42, &DummyLogger{}, &scorer).is_ok() {
path_endpoints.push((src, dst, amt));
continue 'load_endpoints;
}
let mut idx = 0;
bench.iter(|| {
let (src, dst, amt) = path_endpoints[idx % path_endpoints.len()];
- assert!(get_route(&src, &graph, &dst, Some(InvoiceFeatures::known()), None, &[], amt, 42, &DummyLogger{}, &scorer).is_ok());
+ let payee = Payee::new(dst).with_features(InvoiceFeatures::known());
+ assert!(get_route(&src, &payee, &graph, None, amt, 42, &DummyLogger{}, &scorer).is_ok());
idx += 1;
});
}
//! Utilities for scoring payment channels.
//!
-//! [`Scorer`] may be given to [`get_route`] to score payment channels during path finding when a
+//! [`Scorer`] may be given to [`find_route`] to score payment channels during path finding when a
//! custom [`routing::Score`] implementation is not needed.
//!
//! # Example
//! # extern crate secp256k1;
//! #
//! # use lightning::routing::network_graph::NetworkGraph;
-//! # use lightning::routing::router::get_route;
+//! # use lightning::routing::router::{RouteParameters, find_route};
//! # use lightning::routing::scorer::Scorer;
//! # use lightning::util::logger::{Logger, Record};
//! # use secp256k1::key::PublicKey;
//! # impl Logger for FakeLogger {
//! # fn log(&self, record: &Record) { unimplemented!() }
//! # }
-//! # fn find_scored_route(payer: PublicKey, payee: PublicKey, network_graph: NetworkGraph) {
+//! # fn find_scored_route(payer: PublicKey, params: RouteParameters, network_graph: NetworkGraph) {
//! # let logger = FakeLogger {};
//! #
//! // Use the default channel penalty.
//! // Or use a custom channel penalty.
//! let scorer = Scorer::new(1_000);
//!
-//! let route = get_route(&payer, &network_graph, &payee, None, None, &vec![], 1_000, 42, &logger, &scorer);
+//! let route = find_route(&payer, ¶ms, &network_graph, None, &logger, &scorer);
//! # }
//! ```
//!
-//! [`get_route`]: crate::routing::router::get_route
+//! [`find_route`]: crate::routing::router::find_route
use routing;
+use routing::network_graph::NodeId;
+
/// [`routing::Score`] implementation that provides reasonable default behavior.
///
/// Used to apply a fixed penalty to each channel, thus avoiding long paths when shorter paths with
}
impl routing::Score for Scorer {
- fn channel_penalty_msat(&self, _short_channel_id: u64) -> u64 { self.base_penalty_msat }
+ fn channel_penalty_msat(
+ &self, _short_channel_id: u64, _source: &NodeId, _target: &NodeId
+ ) -> u64 {
+ self.base_penalty_msat
+ }
}
--- /dev/null
+//! A simple atomic counter that uses AtomicUsize to give a u64 counter.
+
+#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))]
+compile_error!("We need at least 32-bit pointers for atomic counter (and to have enough memory to run LDK)");
+
+use core::sync::atomic::{AtomicUsize, Ordering};
+
+pub(crate) struct AtomicCounter {
+ // Usize needs to be at least 32 bits to avoid overflowing both low and high. If usize is 64
+ // bits we will never realistically count into high:
+ counter_low: AtomicUsize,
+ counter_high: AtomicUsize,
+}
+
+impl AtomicCounter {
+ pub(crate) fn new() -> Self {
+ Self {
+ counter_low: AtomicUsize::new(0),
+ counter_high: AtomicUsize::new(0),
+ }
+ }
+ pub(crate) fn get_increment(&self) -> u64 {
+ let low = self.counter_low.fetch_add(1, Ordering::AcqRel) as u64;
+ let high = if low == 0 {
+ self.counter_high.fetch_add(1, Ordering::AcqRel) as u64
+ } else {
+ self.counter_high.load(Ordering::Acquire) as u64
+ };
+ (high << 32) | low
+ }
+}
v
}
-#[inline]
-pub fn le64_to_array(u: u64) -> [u8; 8] {
- let mut v = [0; 8];
- v[0] = ((u >> 8*0) & 0xff) as u8;
- v[1] = ((u >> 8*1) & 0xff) as u8;
- v[2] = ((u >> 8*2) & 0xff) as u8;
- v[3] = ((u >> 8*3) & 0xff) as u8;
- v[4] = ((u >> 8*4) & 0xff) as u8;
- v[5] = ((u >> 8*5) & 0xff) as u8;
- v[6] = ((u >> 8*6) & 0xff) as u8;
- v[7] = ((u >> 8*7) & 0xff) as u8;
- v
-}
-
#[cfg(test)]
mod tests {
use super::*;
assert_eq!(be32_to_array(0xdeadbeef), [0xde, 0xad, 0xbe, 0xef]);
assert_eq!(be48_to_array(0xdeadbeef1bad), [0xde, 0xad, 0xbe, 0xef, 0x1b, 0xad]);
assert_eq!(be64_to_array(0xdeadbeef1bad1dea), [0xde, 0xad, 0xbe, 0xef, 0x1b, 0xad, 0x1d, 0xea]);
- assert_eq!(le64_to_array(0xdeadbeef1bad1dea), [0xea, 0x1d, 0xad, 0x1b, 0xef, 0xbe, 0xad, 0xde]);
}
}
use util::poly1305::Poly1305;
use bitcoin::hashes::cmp::fixed_time_eq;
- use util::byte_utils;
-
#[derive(Clone, Copy)]
pub struct ChaCha20Poly1305RFC {
cipher: ChaCha20,
self.mac.input(output);
ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
self.finished = true;
- self.mac.input(&byte_utils::le64_to_array(self.aad_len));
- self.mac.input(&byte_utils::le64_to_array(self.data_len as u64));
+ self.mac.input(&self.aad_len.to_le_bytes());
+ self.mac.input(&(self.data_len as u64).to_le_bytes());
self.mac.raw_result(out_tag);
}
self.data_len += input.len();
ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
- self.mac.input(&byte_utils::le64_to_array(self.aad_len));
- self.mac.input(&byte_utils::le64_to_array(self.data_len as u64));
+ self.mac.input(&self.aad_len.to_le_bytes());
+ self.mac.input(&(self.data_len as u64).to_le_bytes());
let mut calc_tag = [0u8; 16];
self.mac.raw_result(&mut calc_tag);
//! few other things.
use chain::keysinterface::SpendableOutputDescriptor;
+use ln::channelmanager::PaymentId;
use ln::msgs;
use ln::msgs::DecodeError;
use ln::{PaymentPreimage, PaymentHash, PaymentSecret};
use routing::network_graph::NetworkUpdate;
use util::ser::{BigSize, FixedLengthReader, Writeable, Writer, MaybeReadable, Readable, VecReadWrapper, VecWriteWrapper};
-use routing::router::RouteHop;
+use routing::router::{RouteHop, RouteParameters};
+use bitcoin::Transaction;
use bitcoin::blockdata::script::Script;
use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256::Hash as Sha256;
use prelude::*;
use core::time::Duration;
use core::ops::Deref;
-use bitcoin::Transaction;
+use sync::Arc;
/// Some information provided on receipt of payment depends on whether the payment received is a
/// spontaneous payment or a "conventional" lightning payment that's paying an invoice.
/// Note for MPP payments: in rare cases, this event may be preceded by a `PaymentPathFailed`
/// event. In this situation, you SHOULD treat this payment as having succeeded.
PaymentSent {
+ /// The id returned by [`ChannelManager::send_payment`] and used with
+ /// [`ChannelManager::retry_payment`].
+ ///
+ /// [`ChannelManager::send_payment`]: crate::ln::channelmanager::ChannelManager::send_payment
+ /// [`ChannelManager::retry_payment`]: crate::ln::channelmanager::ChannelManager::retry_payment
+ payment_id: Option<PaymentId>,
/// The preimage to the hash given to ChannelManager::send_payment.
/// Note that this serves as a payment receipt, if you wish to have such a thing, you must
/// store it somehow!
///
/// [`ChannelManager::send_payment`]: crate::ln::channelmanager::ChannelManager::send_payment
payment_hash: PaymentHash,
+ /// The total fee which was spent at intermediate hops in this payment, across all paths.
+ ///
+ /// Note that, like [`Route::get_total_fees`] this does *not* include any potential
+ /// overpayment to the recipient node.
+ ///
+ /// If the recipient or an intermediate node misbehaves and gives us free money, this may
+ /// overstate the amount paid, though this is unlikely.
+ ///
+ /// [`Route::get_total_fees`]: crate::routing::router::Route::get_total_fees
+ fee_paid_msat: Option<u64>,
},
/// Indicates an outbound payment we made failed. Probably some intermediary node dropped
/// something. You may wish to retry with a different route.
PaymentPathFailed {
+ /// The id returned by [`ChannelManager::send_payment`] and used with
+ /// [`ChannelManager::retry_payment`].
+ ///
+ /// [`ChannelManager::send_payment`]: crate::ln::channelmanager::ChannelManager::send_payment
+ /// [`ChannelManager::retry_payment`]: crate::ln::channelmanager::ChannelManager::retry_payment
+ payment_id: Option<PaymentId>,
/// The hash which was given to ChannelManager::send_payment.
payment_hash: PaymentHash,
/// Indicates the payment was rejected for some reason by the recipient. This implies that
/// If this is `Some`, then the corresponding channel should be avoided when the payment is
/// retried. May be `None` for older [`Event`] serializations.
short_channel_id: Option<u64>,
+ /// Parameters needed to compute a new [`Route`] when retrying the failed payment path.
+ ///
+ /// See [`find_route`] for details.
+ ///
+ /// [`Route`]: crate::routing::router::Route
+ /// [`find_route`]: crate::routing::router::find_route
+ retry: Option<RouteParameters>,
#[cfg(test)]
error_code: Option<u16>,
#[cfg(test)]
(8, payment_preimage, option),
});
},
- &Event::PaymentSent { ref payment_preimage, ref payment_hash} => {
+ &Event::PaymentSent { ref payment_id, ref payment_preimage, ref payment_hash, ref fee_paid_msat } => {
2u8.write(writer)?;
write_tlv_fields!(writer, {
(0, payment_preimage, required),
(1, payment_hash, required),
+ (3, payment_id, option),
+ (5, fee_paid_msat, option),
});
},
- &Event::PaymentPathFailed { ref payment_hash, ref rejected_by_dest, ref network_update,
- ref all_paths_failed, ref path, ref short_channel_id,
+ &Event::PaymentPathFailed {
+ ref payment_id, ref payment_hash, ref rejected_by_dest, ref network_update,
+ ref all_paths_failed, ref path, ref short_channel_id, ref retry,
#[cfg(test)]
ref error_code,
#[cfg(test)]
(3, all_paths_failed, required),
(5, path, vec_type),
(7, short_channel_id, option),
+ (9, retry, option),
+ (11, payment_id, option),
});
},
&Event::PendingHTLCsForwardable { time_forwardable: _ } => {
let f = || {
let mut payment_preimage = PaymentPreimage([0; 32]);
let mut payment_hash = None;
+ let mut payment_id = None;
+ let mut fee_paid_msat = None;
read_tlv_fields!(reader, {
(0, payment_preimage, required),
(1, payment_hash, option),
+ (3, payment_id, option),
+ (5, fee_paid_msat, option),
});
if payment_hash.is_none() {
payment_hash = Some(PaymentHash(Sha256::hash(&payment_preimage.0[..]).into_inner()));
}
Ok(Some(Event::PaymentSent {
+ payment_id,
payment_preimage,
payment_hash: payment_hash.unwrap(),
+ fee_paid_msat,
}))
};
f()
let mut all_paths_failed = Some(true);
let mut path: Option<Vec<RouteHop>> = Some(vec![]);
let mut short_channel_id = None;
+ let mut retry = None;
+ let mut payment_id = None;
read_tlv_fields!(reader, {
(0, payment_hash, required),
(1, network_update, ignorable),
(2, rejected_by_dest, required),
(3, all_paths_failed, option),
(5, path, vec_type),
- (7, short_channel_id, ignorable),
+ (7, short_channel_id, option),
+ (9, retry, option),
+ (11, payment_id, option),
});
Ok(Some(Event::PaymentPathFailed {
+ payment_id,
payment_hash,
rejected_by_dest,
network_update,
all_paths_failed: all_paths_failed.unwrap(),
path: path.unwrap(),
short_channel_id,
+ retry,
#[cfg(test)]
error_code,
#[cfg(test)]
self(event)
}
}
+
+impl<T: EventHandler> EventHandler for Arc<T> {
+ fn handle_event(&self, event: &Event) {
+ self.deref().handle_event(event)
+ }
+}
fn log(&self, record: &Record);
}
+/// Wrapper for logging byte slices in hex format.
+#[doc(hidden)]
+pub struct DebugBytes<'a>(pub &'a [u8]);
+impl<'a> core::fmt::Display for DebugBytes<'a> {
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
+ for i in self.0 {
+ write!(f, "{:02x}", i)?;
+ }
+ Ok(())
+ }
+}
+
#[cfg(test)]
mod tests {
use util::logger::{Logger, Level};
use routing::router::Route;
use ln::chan_utils::HTLCType;
+use util::logger::DebugBytes;
pub(crate) struct DebugPubKey<'a>(pub &'a PublicKey);
impl<'a> core::fmt::Display for DebugPubKey<'a> {
}
}
-pub(crate) struct DebugBytes<'a>(pub &'a [u8]);
-impl<'a> core::fmt::Display for DebugBytes<'a> {
- fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
- for i in self.0 {
- write!(f, "{:02x}", i)?;
- }
- Ok(())
- }
-}
+/// Logs a byte slice in hex format.
+#[macro_export]
macro_rules! log_bytes {
($obj: expr) => {
- ::util::macro_logger::DebugBytes(&$obj)
+ $crate::util::logger::DebugBytes(&$obj)
}
}
/// Create a new Record and log it. You probably don't want to use this macro directly,
/// but it needs to be exported so `log_trace` etc can use it in external crates.
+#[doc(hidden)]
#[macro_export]
macro_rules! log_internal {
($logger: expr, $lvl:expr, $($arg:tt)+) => (
pub mod ser;
pub mod message_signing;
+pub(crate) mod atomic_counter;
pub(crate) mod byte_utils;
pub(crate) mod chacha20;
#[cfg(feature = "fuzztarget")]
impl<K, V> Readable for HashMap<K, V>
where K: Readable + Eq + Hash,
- V: Readable
+ V: MaybeReadable
{
#[inline]
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
let len: u16 = Readable::read(r)?;
let mut ret = HashMap::with_capacity(len as usize);
for _ in 0..len {
- ret.insert(K::read(r)?, V::read(r)?);
+ let k = K::read(r)?;
+ let v_opt = V::read(r)?;
+ if let Some(v) = v_opt {
+ if ret.insert(k, v).is_some() {
+ return Err(DecodeError::InvalidValue);
+ }
+ }
}
Ok(ret)
}
macro_rules! impl_writeable_tlv_based_enum_upgradable {
($st: ident, $(($variant_id: expr, $variant_name: ident) =>
{$(($type: expr, $field: ident, $fieldty: tt)),* $(,)*}
- ),* $(,)*) => {
+ ),* $(,)*
+ $(;
+ $(($tuple_variant_id: expr, $tuple_variant_name: ident)),* $(,)*)*) => {
_impl_writeable_tlv_based_enum_common!($st,
- $(($variant_id, $variant_name) => {$(($type, $field, $fieldty)),*}),*; );
+ $(($variant_id, $variant_name) => {$(($type, $field, $fieldty)),*}),*;
+ $($(($tuple_variant_id, $tuple_variant_name)),*)*);
impl ::util::ser::MaybeReadable for $st {
fn read<R: $crate::io::Read>(reader: &mut R) -> Result<Option<Self>, ::ln::msgs::DecodeError> {
};
f()
}),*
+ $($($tuple_variant_id => {
+ Ok(Some($st::$tuple_variant_name(Readable::read(reader)?)))
+ }),*)*
_ if id % 2 == 1 => Ok(None),
_ => Err(DecodeError::UnknownRequiredFeature),
}
use chain::chaininterface;
use chain::chaininterface::ConfirmationTarget;
use chain::chainmonitor;
+use chain::chainmonitor::MonitorUpdateId;
use chain::channelmonitor;
use chain::channelmonitor::MonitorEvent;
use chain::transaction::OutPoint;
pub struct TestChainMonitor<'a> {
pub added_monitors: Mutex<Vec<(OutPoint, channelmonitor::ChannelMonitor<EnforcingSigner>)>>,
- pub latest_monitor_update_id: Mutex<HashMap<[u8; 32], (OutPoint, u64)>>,
+ pub latest_monitor_update_id: Mutex<HashMap<[u8; 32], (OutPoint, u64, MonitorUpdateId)>>,
pub chain_monitor: chainmonitor::ChainMonitor<EnforcingSigner, &'a TestChainSource, &'a chaininterface::BroadcasterInterface, &'a TestFeeEstimator, &'a TestLogger, &'a chainmonitor::Persist<EnforcingSigner>>,
pub keys_manager: &'a TestKeysInterface,
/// If this is set to Some(), the next update_channel call (not watch_channel) must be a
let new_monitor = <(BlockHash, channelmonitor::ChannelMonitor<EnforcingSigner>)>::read(
&mut io::Cursor::new(&w.0), self.keys_manager).unwrap().1;
assert!(new_monitor == monitor);
- self.latest_monitor_update_id.lock().unwrap().insert(funding_txo.to_channel_id(), (funding_txo, monitor.get_latest_update_id()));
+ self.latest_monitor_update_id.lock().unwrap().insert(funding_txo.to_channel_id(),
+ (funding_txo, monitor.get_latest_update_id(), MonitorUpdateId::from_new_monitor(&monitor)));
self.added_monitors.lock().unwrap().push((funding_txo, monitor));
self.chain_monitor.watch_channel(funding_txo, new_monitor)
}
} else { panic!(); }
}
- self.latest_monitor_update_id.lock().unwrap().insert(funding_txo.to_channel_id(), (funding_txo, update.update_id));
+ self.latest_monitor_update_id.lock().unwrap().insert(funding_txo.to_channel_id(),
+ (funding_txo, update.update_id, MonitorUpdateId::from_monitor_update(&update)));
let update_res = self.chain_monitor.update_channel(funding_txo, update);
// At every point where we get a monitor update, we should be able to send a useful monitor
// to a watchtower and disk...
/// If this is set to Some(), after the next return, we'll always return this until update_ret
/// is changed:
pub next_update_ret: Mutex<Option<Result<(), chain::ChannelMonitorUpdateErr>>>,
-
+ /// When we get an update_persisted_channel call with no ChannelMonitorUpdate, we insert the
+ /// MonitorUpdateId here.
+ pub chain_sync_monitor_persistences: Mutex<HashMap<OutPoint, HashSet<MonitorUpdateId>>>,
}
impl TestPersister {
pub fn new() -> Self {
Self {
update_ret: Mutex::new(Ok(())),
next_update_ret: Mutex::new(None),
+ chain_sync_monitor_persistences: Mutex::new(HashMap::new()),
}
}
}
}
impl<Signer: keysinterface::Sign> chainmonitor::Persist<Signer> for TestPersister {
- fn persist_new_channel(&self, _funding_txo: OutPoint, _data: &channelmonitor::ChannelMonitor<Signer>) -> Result<(), chain::ChannelMonitorUpdateErr> {
+ fn persist_new_channel(&self, _funding_txo: OutPoint, _data: &channelmonitor::ChannelMonitor<Signer>, _id: MonitorUpdateId) -> Result<(), chain::ChannelMonitorUpdateErr> {
let ret = self.update_ret.lock().unwrap().clone();
if let Some(next_ret) = self.next_update_ret.lock().unwrap().take() {
*self.update_ret.lock().unwrap() = next_ret;
ret
}
- fn update_persisted_channel(&self, _funding_txo: OutPoint, _update: &channelmonitor::ChannelMonitorUpdate, _data: &channelmonitor::ChannelMonitor<Signer>) -> Result<(), chain::ChannelMonitorUpdateErr> {
+ fn update_persisted_channel(&self, funding_txo: OutPoint, update: &Option<channelmonitor::ChannelMonitorUpdate>, _data: &channelmonitor::ChannelMonitor<Signer>, update_id: MonitorUpdateId) -> Result<(), chain::ChannelMonitorUpdateErr> {
let ret = self.update_ret.lock().unwrap().clone();
if let Some(next_ret) = self.next_update_ret.lock().unwrap().take() {
*self.update_ret.lock().unwrap() = next_ret;
}
+ if update.is_none() {
+ self.chain_sync_monitor_persistences.lock().unwrap().entry(funding_txo).or_insert(HashSet::new()).insert(update_id);
+ }
ret
}
}