Merge pull request #2035 from TheBlueMatt/2023-02-fix-no-con-discon
authorMatt Corallo <649246+TheBlueMatt@users.noreply.github.com>
Tue, 21 Feb 2023 21:28:05 +0000 (21:28 +0000)
committerGitHub <noreply@github.com>
Tue, 21 Feb 2023 21:28:05 +0000 (21:28 +0000)
Fix (and DRY) the conditionals before calling peer_disconnected

32 files changed:
.github/workflows/build.yml
.gitignore
Cargo.toml
fuzz/src/chanmon_consistency.rs
fuzz/src/indexedmap.rs
lightning-background-processor/src/lib.rs
lightning-custom-message/Cargo.toml [new file with mode: 0644]
lightning-custom-message/src/lib.rs [new file with mode: 0644]
lightning-rapid-gossip-sync/src/lib.rs
lightning-rapid-gossip-sync/src/processing.rs
lightning/src/ln/chanmon_update_fail_tests.rs
lightning/src/ln/channelmanager.rs
lightning/src/ln/functional_test_utils.rs
lightning/src/ln/functional_tests.rs
lightning/src/ln/monitor_tests.rs
lightning/src/ln/onion_route_tests.rs
lightning/src/ln/outbound_payment.rs
lightning/src/ln/payment_tests.rs
lightning/src/ln/peer_handler.rs
lightning/src/onion_message/messenger.rs
lightning/src/routing/gossip.rs
lightning/src/routing/router.rs
lightning/src/sync/debug_sync.rs
lightning/src/sync/fairrwlock.rs [new file with mode: 0644]
lightning/src/sync/mod.rs
lightning/src/sync/nostd_sync.rs
lightning/src/sync/test_lockorder_checks.rs
lightning/src/util/events.rs
lightning/src/util/fairrwlock.rs [deleted file]
lightning/src/util/indexed_map.rs
lightning/src/util/mod.rs
lightning/src/util/test_utils.rs

index f729cecc01218fb54839c555c18c64ce36d36395..a8f0aa69d94502caaf8e2e420c85a07821d6555b 100644 (file)
@@ -31,6 +31,8 @@ jobs:
             build-no-std: true
             build-futures: true
             build-tx-sync: true
+          - toolchain: stable
+            test-custom-message: true
           - toolchain: beta
             platform: macos-latest
             build-net-tokio: true
@@ -54,6 +56,8 @@ jobs:
             build-no-std: true
             build-futures: true
             build-tx-sync: true
+          - toolchain: beta
+            test-custom-message: true
           - toolchain: 1.41.1
             build-no-std: false
             test-log-variants: true
@@ -226,6 +230,11 @@ jobs:
           RUSTFLAGS="-C link-dead-code" cargo test --verbose --color always --features rpc-client
           RUSTFLAGS="-C link-dead-code" cargo test --verbose --color always --features rpc-client,rest-client
           RUSTFLAGS="-C link-dead-code" cargo test --verbose --color always --features rpc-client,rest-client,tokio
+      - name: Test Custom Message Macros on Rust ${{ matrix.toolchain }}
+        if: "matrix.test-custom-message"
+        run: |
+          cd lightning-custom-message
+          cargo test --verbose --color always
       - name: Install deps for kcov
         if: matrix.coverage
         run: |
index d99c5a4ddf6603b32fb97d666d49c3ebb631df79..7a889b5b8230af929cd28134454d2f93502531bd 100644 (file)
@@ -9,5 +9,5 @@ Cargo.lock
 .idea
 lightning/target
 lightning/ldk-net_graph-*.bin
+lightning-custom-message/target
 no-std-check/target
-
index e8565e7ac0945d6a189079f40b5c91c96326c1c1..be76477f4c0f0548121cc8f5770053e798bbd3d0 100644 (file)
@@ -12,6 +12,7 @@ members = [
 ]
 
 exclude = [
+    "lightning-custom-message",
     "no-std-check",
 ]
 
index 457e1e45b4f9cbbe6c8e67a54ee7e48891c74db1..154f198c3286b0d297750f924650d177fa4617cd 100644 (file)
@@ -914,6 +914,7 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out) {
                                                events::Event::PaymentClaimed { .. } => {},
                                                events::Event::PaymentPathSuccessful { .. } => {},
                                                events::Event::PaymentPathFailed { .. } => {},
+                                               events::Event::PaymentFailed { .. } => {},
                                                events::Event::ProbeSuccessful { .. } | events::Event::ProbeFailed { .. } => {
                                                        // Even though we don't explicitly send probes, because probes are
                                                        // detected based on hashing the payment hash+preimage, its rather
index 795d6175bb5dc2dd8aee2b0cdce76ccc6a95c12b..7cbb8957ad247f807ff91f8851af44f0b8a546a8 100644 (file)
@@ -13,14 +13,27 @@ use hashbrown::HashSet;
 
 use crate::utils::test_logger;
 
-fn check_eq(btree: &BTreeMap<u8, u8>, indexed: &IndexedMap<u8, u8>) {
+use std::ops::{RangeBounds, Bound};
+
+struct ExclLowerInclUpper(u8, u8);
+impl RangeBounds<u8> for ExclLowerInclUpper {
+       fn start_bound(&self) -> Bound<&u8> { Bound::Excluded(&self.0) }
+       fn end_bound(&self) -> Bound<&u8> { Bound::Included(&self.1) }
+}
+struct ExclLowerExclUpper(u8, u8);
+impl RangeBounds<u8> for ExclLowerExclUpper {
+       fn start_bound(&self) -> Bound<&u8> { Bound::Excluded(&self.0) }
+       fn end_bound(&self) -> Bound<&u8> { Bound::Excluded(&self.1) }
+}
+
+fn check_eq(btree: &BTreeMap<u8, u8>, mut indexed: IndexedMap<u8, u8>) {
        assert_eq!(btree.len(), indexed.len());
        assert_eq!(btree.is_empty(), indexed.is_empty());
 
        let mut btree_clone = btree.clone();
        assert!(btree_clone == *btree);
        let mut indexed_clone = indexed.clone();
-       assert!(indexed_clone == *indexed);
+       assert!(indexed_clone == indexed);
 
        for k in 0..=255 {
                assert_eq!(btree.contains_key(&k), indexed.contains_key(&k));
@@ -43,16 +56,27 @@ fn check_eq(btree: &BTreeMap<u8, u8>, indexed: &IndexedMap<u8, u8>) {
        }
 
        const STRIDE: u8 = 16;
-       for k in 0..=255/STRIDE {
-               let lower_bound = k * STRIDE;
-               let upper_bound = lower_bound + (STRIDE - 1);
-               let mut btree_iter = btree.range(lower_bound..=upper_bound);
-               let mut indexed_iter = indexed.range(lower_bound..=upper_bound);
-               loop {
-                       let b_v = btree_iter.next();
-                       let i_v = indexed_iter.next();
-                       assert_eq!(b_v, i_v);
-                       if b_v.is_none() { break; }
+       for range_type in 0..4 {
+               for k in 0..=255/STRIDE {
+                       let lower_bound = k * STRIDE;
+                       let upper_bound = lower_bound + (STRIDE - 1);
+                       macro_rules! range { ($map: expr) => {
+                               match range_type {
+                                       0 => $map.range(lower_bound..upper_bound),
+                                       1 => $map.range(lower_bound..=upper_bound),
+                                       2 => $map.range(ExclLowerInclUpper(lower_bound, upper_bound)),
+                                       3 => $map.range(ExclLowerExclUpper(lower_bound, upper_bound)),
+                                       _ => unreachable!(),
+                               }
+                       } }
+                       let mut btree_iter = range!(btree);
+                       let mut indexed_iter = range!(indexed);
+                       loop {
+                               let b_v = btree_iter.next();
+                               let i_v = indexed_iter.next();
+                               assert_eq!(b_v, i_v);
+                               if b_v.is_none() { break; }
+                       }
                }
        }
 
@@ -91,7 +115,7 @@ pub fn do_test(data: &[u8]) {
                let prev_value_i = indexed.insert(tuple[0], tuple[1]);
                assert_eq!(prev_value_b, prev_value_i);
        }
-       check_eq(&btree, &indexed);
+       check_eq(&btree, indexed.clone());
 
        // Now, modify the maps in all the ways we have to do so, checking that the maps remain
        // equivalent as we go.
@@ -99,7 +123,7 @@ pub fn do_test(data: &[u8]) {
                *v = *k;
                *btree.get_mut(k).unwrap() = *k;
        }
-       check_eq(&btree, &indexed);
+       check_eq(&btree, indexed.clone());
 
        for k in 0..=255 {
                match btree.entry(k) {
@@ -124,7 +148,7 @@ pub fn do_test(data: &[u8]) {
                        },
                }
        }
-       check_eq(&btree, &indexed);
+       check_eq(&btree, indexed);
 }
 
 pub fn indexedmap_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
index 9b1df7522718759df41ce56f91365b080253f81a..a21f00f867cb36c1dc4d5c596f29dc2af551c5de 100644 (file)
@@ -1311,7 +1311,7 @@ mod tests {
                        0, 0, 0, 1, 0, 0, 0, 0, 58, 85, 116, 216, 255, 8, 153, 192, 0, 2, 27, 0, 0, 25, 0, 0,
                        0, 1, 0, 0, 0, 125, 255, 2, 68, 226, 0, 6, 11, 0, 1, 5, 0, 0, 0, 0, 29, 129, 25, 192,
                ];
-               nodes[0].rapid_gossip_sync.update_network_graph(&initialization_input[..]).unwrap();
+               nodes[0].rapid_gossip_sync.update_network_graph_no_std(&initialization_input[..], Some(1642291930)).unwrap();
 
                // this should have added two channels
                assert_eq!(network_graph.read_only().channels().len(), 3);
diff --git a/lightning-custom-message/Cargo.toml b/lightning-custom-message/Cargo.toml
new file mode 100644 (file)
index 0000000..657844c
--- /dev/null
@@ -0,0 +1,18 @@
+[package]
+name = "lightning-custom-message"
+version = "0.0.113"
+authors = ["Jeffrey Czyz"]
+license = "MIT OR Apache-2.0"
+repository = "http://github.com/lightningdevkit/rust-lightning"
+description = """
+Utilities for supporting custom peer-to-peer messages in LDK.
+"""
+edition = "2021"
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
+
+[dependencies]
+bitcoin = "0.29.0"
+lightning = { version = "0.0.113", path = "../lightning" }
diff --git a/lightning-custom-message/src/lib.rs b/lightning-custom-message/src/lib.rs
new file mode 100644 (file)
index 0000000..a6e4397
--- /dev/null
@@ -0,0 +1,310 @@
+//! Utilities for supporting custom peer-to-peer messages in LDK.
+//!
+//! [BOLT 1] specifies a custom message type range for use with experimental or application-specific
+//! messages. While a [`CustomMessageHandler`] can be defined to support more than one message type,
+//! defining such a handler requires a significant amount of boilerplate and can be error prone.
+//!
+//! This crate provides the [`composite_custom_message_handler`] macro for easily composing
+//! pre-defined custom message handlers into one handler. The resulting handler can be further
+//! composed with other custom message handlers using the same macro.
+//!
+//! The following example demonstrates defining a `FooBarHandler` to compose separate handlers for
+//! `Foo` and `Bar` messages, and further composing it with a handler for `Baz` messages.
+//!
+//!```
+//! # extern crate bitcoin;
+//! extern crate lightning;
+//! #[macro_use]
+//! extern crate lightning_custom_message;
+//!
+//! # use bitcoin::secp256k1::PublicKey;
+//! # use lightning::io;
+//! # use lightning::ln::msgs::{DecodeError, LightningError};
+//! use lightning::ln::peer_handler::CustomMessageHandler;
+//! use lightning::ln::wire::{CustomMessageReader, self};
+//! use lightning::util::ser::Writeable;
+//! # use lightning::util::ser::Writer;
+//!
+//! // Assume that `FooHandler` and `BarHandler` are defined in one crate and `BazHandler` is
+//! // defined in another crate, handling messages `Foo`, `Bar`, and `Baz`, respectively.
+//!
+//! #[derive(Debug)]
+//! pub struct Foo;
+//!
+//! macro_rules! foo_type_id {
+//!     () => { 32768 }
+//! }
+//!
+//! impl wire::Type for Foo {
+//!     fn type_id(&self) -> u16 { foo_type_id!() }
+//! }
+//! impl Writeable for Foo {
+//!     // ...
+//! #     fn write<W: Writer>(&self, _: &mut W) -> Result<(), io::Error> {
+//! #         unimplemented!()
+//! #     }
+//! }
+//!
+//! pub struct FooHandler;
+//!
+//! impl CustomMessageReader for FooHandler {
+//!     // ...
+//! #     type CustomMessage = Foo;
+//! #     fn read<R: io::Read>(
+//! #         &self, _message_type: u16, _buffer: &mut R
+//! #     ) -> Result<Option<Self::CustomMessage>, DecodeError> {
+//! #         unimplemented!()
+//! #     }
+//! }
+//! impl CustomMessageHandler for FooHandler {
+//!     // ...
+//! #     fn handle_custom_message(
+//! #         &self, _msg: Self::CustomMessage, _sender_node_id: &PublicKey
+//! #     ) -> Result<(), LightningError> {
+//! #         unimplemented!()
+//! #     }
+//! #     fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)> {
+//! #         unimplemented!()
+//! #     }
+//! }
+//!
+//! #[derive(Debug)]
+//! pub struct Bar;
+//!
+//! macro_rules! bar_type_id {
+//!     () => { 32769 }
+//! }
+//!
+//! impl wire::Type for Bar {
+//!     fn type_id(&self) -> u16 { bar_type_id!() }
+//! }
+//! impl Writeable for Bar {
+//!     // ...
+//! #     fn write<W: Writer>(&self, _: &mut W) -> Result<(), io::Error> {
+//! #         unimplemented!()
+//! #     }
+//! }
+//!
+//! pub struct BarHandler;
+//!
+//! impl CustomMessageReader for BarHandler {
+//!     // ...
+//! #     type CustomMessage = Bar;
+//! #     fn read<R: io::Read>(
+//! #         &self, _message_type: u16, _buffer: &mut R
+//! #     ) -> Result<Option<Self::CustomMessage>, DecodeError> {
+//! #         unimplemented!()
+//! #     }
+//! }
+//! impl CustomMessageHandler for BarHandler {
+//!     // ...
+//! #     fn handle_custom_message(
+//! #         &self, _msg: Self::CustomMessage, _sender_node_id: &PublicKey
+//! #     ) -> Result<(), LightningError> {
+//! #         unimplemented!()
+//! #     }
+//! #     fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)> {
+//! #         unimplemented!()
+//! #     }
+//! }
+//!
+//! #[derive(Debug)]
+//! pub struct Baz;
+//!
+//! macro_rules! baz_type_id {
+//!     () => { 32770 }
+//! }
+//!
+//! impl wire::Type for Baz {
+//!     fn type_id(&self) -> u16 { baz_type_id!() }
+//! }
+//! impl Writeable for Baz {
+//!     // ...
+//! #     fn write<W: Writer>(&self, _: &mut W) -> Result<(), io::Error> {
+//! #         unimplemented!()
+//! #     }
+//! }
+//!
+//! pub struct BazHandler;
+//!
+//! impl CustomMessageReader for BazHandler {
+//!     // ...
+//! #     type CustomMessage = Baz;
+//! #     fn read<R: io::Read>(
+//! #         &self, _message_type: u16, _buffer: &mut R
+//! #     ) -> Result<Option<Self::CustomMessage>, DecodeError> {
+//! #         unimplemented!()
+//! #     }
+//! }
+//! impl CustomMessageHandler for BazHandler {
+//!     // ...
+//! #     fn handle_custom_message(
+//! #         &self, _msg: Self::CustomMessage, _sender_node_id: &PublicKey
+//! #     ) -> Result<(), LightningError> {
+//! #         unimplemented!()
+//! #     }
+//! #     fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)> {
+//! #         unimplemented!()
+//! #     }
+//! }
+//!
+//! # fn main() {
+//! // The first crate may define a handler composing `FooHandler` and `BarHandler` and export the
+//! // corresponding message type ids as a macro to use in further composition.
+//!
+//! composite_custom_message_handler!(
+//!     pub struct FooBarHandler {
+//!         foo: FooHandler,
+//!         bar: BarHandler,
+//!     }
+//!
+//!     pub enum FooBarMessage {
+//!         Foo(foo_type_id!()),
+//!         Bar(bar_type_id!()),
+//!     }
+//! );
+//!
+//! #[macro_export]
+//! macro_rules! foo_bar_type_ids {
+//!     () => { foo_type_id!() | bar_type_id!() }
+//! }
+//!
+//! // Another crate can then define a handler further composing `FooBarHandler` with `BazHandler`
+//! // and similarly export the composition of message type ids as a macro.
+//!
+//! composite_custom_message_handler!(
+//!     pub struct FooBarBazHandler {
+//!         foo_bar: FooBarHandler,
+//!         baz: BazHandler,
+//!     }
+//!
+//!     pub enum FooBarBazMessage {
+//!         FooBar(foo_bar_type_ids!()),
+//!         Baz(baz_type_id!()),
+//!     }
+//! );
+//!
+//! #[macro_export]
+//! macro_rules! foo_bar_baz_type_ids {
+//!     () => { foo_bar_type_ids!() | baz_type_id!() }
+//! }
+//! # }
+//!```
+//!
+//! [BOLT 1]: https://github.com/lightning/bolts/blob/master/01-messaging.md
+//! [`CustomMessageHandler`]: crate::lightning::ln::peer_handler::CustomMessageHandler
+
+#![doc(test(no_crate_inject, attr(deny(warnings))))]
+
+pub extern crate bitcoin;
+pub extern crate lightning;
+
+/// Defines a composite type implementing [`CustomMessageHandler`] (and therefore also implementing
+/// [`CustomMessageReader`]), along with a corresponding enumerated custom message [`Type`], from
+/// one or more previously defined custom message handlers.
+///
+/// Useful for parameterizing [`PeerManager`] with custom message handling for one or more sets of
+/// custom messages. Message type ids may be given as a valid `match` pattern, including ranges,
+/// though using OR-ed literal patterns is preferred in order to catch unreachable code for
+/// conflicting handlers.
+///
+/// See [crate documentation] for example usage.
+///
+/// [`CustomMessageHandler`]: crate::lightning::ln::peer_handler::CustomMessageHandler
+/// [`CustomMessageReader`]: crate::lightning::ln::wire::CustomMessageReader
+/// [`Type`]: crate::lightning::ln::wire::Type
+/// [`PeerManager`]: crate::lightning::ln::peer_handler::PeerManager
+/// [crate documentation]: self
+#[macro_export]
+macro_rules! composite_custom_message_handler {
+       (
+               $handler_visibility:vis struct $handler:ident {
+                       $($field_visibility:vis $field:ident: $type:ty),* $(,)*
+               }
+
+               $message_visibility:vis enum $message:ident {
+                       $($variant:ident($pattern:pat)),* $(,)*
+               }
+       ) => {
+               #[allow(missing_docs)]
+               $handler_visibility struct $handler {
+                       $(
+                               $field_visibility $field: $type,
+                       )*
+               }
+
+               #[allow(missing_docs)]
+               #[derive(Debug)]
+               $message_visibility enum $message {
+                       $(
+                               $variant(<$type as $crate::lightning::ln::wire::CustomMessageReader>::CustomMessage),
+                       )*
+               }
+
+               impl $crate::lightning::ln::peer_handler::CustomMessageHandler for $handler {
+                       fn handle_custom_message(
+                               &self, msg: Self::CustomMessage, sender_node_id: &$crate::bitcoin::secp256k1::PublicKey
+                       ) -> Result<(), $crate::lightning::ln::msgs::LightningError> {
+                               match msg {
+                                       $(
+                                               $message::$variant(message) => {
+                                                       $crate::lightning::ln::peer_handler::CustomMessageHandler::handle_custom_message(
+                                                               &self.$field, message, sender_node_id
+                                                       )
+                                               },
+                                       )*
+                               }
+                       }
+
+                       fn get_and_clear_pending_msg(&self) -> Vec<($crate::bitcoin::secp256k1::PublicKey, Self::CustomMessage)> {
+                               vec![].into_iter()
+                                       $(
+                                               .chain(
+                                                       self.$field
+                                                               .get_and_clear_pending_msg()
+                                                               .into_iter()
+                                                               .map(|(pubkey, message)| (pubkey, $message::$variant(message)))
+                                               )
+                                       )*
+                                       .collect()
+                       }
+               }
+
+               impl $crate::lightning::ln::wire::CustomMessageReader for $handler {
+                       type CustomMessage = $message;
+                       fn read<R: $crate::lightning::io::Read>(
+                               &self, message_type: u16, buffer: &mut R
+                       ) -> Result<Option<Self::CustomMessage>, $crate::lightning::ln::msgs::DecodeError> {
+                               match message_type {
+                                       $(
+                                               $pattern => match <$type>::read(&self.$field, message_type, buffer)? {
+                                                       None => unreachable!(),
+                                                       Some(message) => Ok(Some($message::$variant(message))),
+                                               },
+                                       )*
+                                       _ => Ok(None),
+                               }
+                       }
+               }
+
+               impl $crate::lightning::ln::wire::Type for $message {
+                       fn type_id(&self) -> u16 {
+                               match self {
+                                       $(
+                                               Self::$variant(message) => message.type_id(),
+                                       )*
+                               }
+                       }
+               }
+
+               impl $crate::lightning::util::ser::Writeable for $message {
+                       fn write<W: $crate::lightning::util::ser::Writer>(&self, writer: &mut W) -> Result<(), $crate::lightning::io::Error> {
+                               match self {
+                                       $(
+                                               Self::$variant(message) => message.write(writer),
+                                       )*
+                               }
+                       }
+               }
+       }
+}
index 06ce7eb2075574df38c2b9cbf44813235e1fba1c..ff0e481b39711cdb41166f2627d23d322838064a 100644 (file)
@@ -126,14 +126,22 @@ impl<NG: Deref<Target=NetworkGraph<L>>, L: Deref> RapidGossipSync<NG, L> where L
        /// Update network graph from binary data.
        /// Returns the last sync timestamp to be used the next time rapid sync data is queried.
        ///
-       /// `network_graph`: network graph to be updated
-       ///
        /// `update_data`: `&[u8]` binary stream that comprises the update data
        pub fn update_network_graph(&self, update_data: &[u8]) -> Result<u32, GraphSyncError> {
                let mut read_cursor = io::Cursor::new(update_data);
                self.update_network_graph_from_byte_stream(&mut read_cursor)
        }
 
+       /// Update network graph from binary data.
+       /// Returns the last sync timestamp to be used the next time rapid sync data is queried.
+       ///
+       /// `update_data`: `&[u8]` binary stream that comprises the update data
+       /// `current_time_unix`: `Option<u64>` optional current timestamp to verify data age
+       pub fn update_network_graph_no_std(&self, update_data: &[u8], current_time_unix: Option<u64>) -> Result<u32, GraphSyncError> {
+               let mut read_cursor = io::Cursor::new(update_data);
+               self.update_network_graph_from_byte_stream_no_std(&mut read_cursor, current_time_unix)
+       }
+
        /// Gets a reference to the underlying [`NetworkGraph`] which was provided in
        /// [`RapidGossipSync::new`].
        ///
index 3e001ec45c4be1aa7becbaa6da9f9158f3d217fa..f84f205c9b56f324838240a1be7d07162655f32d 100644 (file)
@@ -16,6 +16,9 @@ use lightning::io;
 use crate::error::GraphSyncError;
 use crate::RapidGossipSync;
 
+#[cfg(all(feature = "std", not(test)))]
+use std::time::{SystemTime, UNIX_EPOCH};
+
 #[cfg(not(feature = "std"))]
 use alloc::{vec::Vec, borrow::ToOwned};
 
@@ -29,10 +32,30 @@ const GOSSIP_PREFIX: [u8; 4] = [76, 68, 75, 1];
 /// avoid malicious updates being able to trigger excessive memory allocation.
 const MAX_INITIAL_NODE_ID_VECTOR_CAPACITY: u32 = 50_000;
 
+/// We disallow gossip data that's more than two weeks old, per BOLT 7's
+/// suggestion.
+const STALE_RGS_UPDATE_AGE_LIMIT_SECS: u64 = 60 * 60 * 24 * 14;
+
 impl<NG: Deref<Target=NetworkGraph<L>>, L: Deref> RapidGossipSync<NG, L> where L::Target: Logger {
        pub(crate) fn update_network_graph_from_byte_stream<R: io::Read>(
+               &self,
+               read_cursor: &mut R,
+       ) -> Result<u32, GraphSyncError> {
+               #[allow(unused_mut)]
+               let mut current_time_unix = None;
+               #[cfg(all(feature = "std", not(test)))]
+               {
+                       // Note that many tests rely on being able to set arbitrarily old timestamps, thus we
+                       // disable this check during tests!
+                       current_time_unix = Some(SystemTime::now().duration_since(UNIX_EPOCH).expect("Time must be > 1970").as_secs());
+               }
+               self.update_network_graph_from_byte_stream_no_std(read_cursor, current_time_unix)
+       }
+
+       pub(crate) fn update_network_graph_from_byte_stream_no_std<R: io::Read>(
                &self,
                mut read_cursor: &mut R,
+               current_time_unix: Option<u64>
        ) -> Result<u32, GraphSyncError> {
                let mut prefix = [0u8; 4];
                read_cursor.read_exact(&mut prefix)?;
@@ -43,6 +66,13 @@ impl<NG: Deref<Target=NetworkGraph<L>>, L: Deref> RapidGossipSync<NG, L> where L
 
                let chain_hash: BlockHash = Readable::read(read_cursor)?;
                let latest_seen_timestamp: u32 = Readable::read(read_cursor)?;
+
+               if let Some(time) = current_time_unix {
+                       if (latest_seen_timestamp as u64) < time.saturating_sub(STALE_RGS_UPDATE_AGE_LIMIT_SECS) {
+                               return Err(LightningError{err: "Rapid Gossip Sync data is more than two weeks old".to_owned(), action: ErrorAction::IgnoreError}.into());
+                       }
+               }
+
                // backdate the applied timestamp by a week
                let backdated_timestamp = latest_seen_timestamp.saturating_sub(24 * 3600 * 7);
 
@@ -215,8 +245,28 @@ mod tests {
        use lightning::util::test_utils::TestLogger;
 
        use crate::error::GraphSyncError;
+       use crate::processing::STALE_RGS_UPDATE_AGE_LIMIT_SECS;
        use crate::RapidGossipSync;
 
+       const VALID_RGS_BINARY: [u8; 300] = [
+               76, 68, 75, 1, 111, 226, 140, 10, 182, 241, 179, 114, 193, 166, 162, 70, 174, 99, 247,
+               79, 147, 30, 131, 101, 225, 90, 8, 156, 104, 214, 25, 0, 0, 0, 0, 0, 97, 227, 98, 218,
+               0, 0, 0, 4, 2, 22, 7, 207, 206, 25, 164, 197, 231, 230, 231, 56, 102, 61, 250, 251,
+               187, 172, 38, 46, 79, 247, 108, 44, 155, 48, 219, 238, 252, 53, 192, 6, 67, 2, 36, 125,
+               157, 176, 223, 175, 234, 116, 94, 248, 201, 225, 97, 235, 50, 47, 115, 172, 63, 136,
+               88, 216, 115, 11, 111, 217, 114, 84, 116, 124, 231, 107, 2, 158, 1, 242, 121, 152, 106,
+               204, 131, 186, 35, 93, 70, 216, 10, 237, 224, 183, 89, 95, 65, 3, 83, 185, 58, 138,
+               181, 64, 187, 103, 127, 68, 50, 2, 201, 19, 17, 138, 136, 149, 185, 226, 156, 137, 175,
+               110, 32, 237, 0, 217, 90, 31, 100, 228, 149, 46, 219, 175, 168, 77, 4, 143, 38, 128,
+               76, 97, 0, 0, 0, 2, 0, 0, 255, 8, 153, 192, 0, 2, 27, 0, 0, 0, 1, 0, 0, 255, 2, 68,
+               226, 0, 6, 11, 0, 1, 2, 3, 0, 0, 0, 4, 0, 40, 0, 0, 0, 0, 0, 0, 3, 232, 0, 0, 3, 232,
+               0, 0, 0, 1, 0, 0, 0, 0, 29, 129, 25, 192, 255, 8, 153, 192, 0, 2, 27, 0, 0, 60, 0, 0,
+               0, 0, 0, 0, 0, 1, 0, 0, 0, 100, 0, 0, 2, 224, 0, 0, 0, 0, 58, 85, 116, 216, 0, 29, 0,
+               0, 0, 1, 0, 0, 0, 125, 0, 0, 0, 0, 58, 85, 116, 216, 255, 2, 68, 226, 0, 6, 11, 0, 1,
+               0, 0, 1,
+       ];
+       const VALID_BINARY_TIMESTAMP: u64 = 1642291930;
+
        #[test]
        fn network_graph_fails_to_update_from_clipped_input() {
                let block_hash = genesis_block(Network::Bitcoin).block_hash();
@@ -478,24 +528,6 @@ mod tests {
 
        #[test]
        fn full_update_succeeds() {
-               let valid_input = vec![
-                       76, 68, 75, 1, 111, 226, 140, 10, 182, 241, 179, 114, 193, 166, 162, 70, 174, 99, 247,
-                       79, 147, 30, 131, 101, 225, 90, 8, 156, 104, 214, 25, 0, 0, 0, 0, 0, 97, 227, 98, 218,
-                       0, 0, 0, 4, 2, 22, 7, 207, 206, 25, 164, 197, 231, 230, 231, 56, 102, 61, 250, 251,
-                       187, 172, 38, 46, 79, 247, 108, 44, 155, 48, 219, 238, 252, 53, 192, 6, 67, 2, 36, 125,
-                       157, 176, 223, 175, 234, 116, 94, 248, 201, 225, 97, 235, 50, 47, 115, 172, 63, 136,
-                       88, 216, 115, 11, 111, 217, 114, 84, 116, 124, 231, 107, 2, 158, 1, 242, 121, 152, 106,
-                       204, 131, 186, 35, 93, 70, 216, 10, 237, 224, 183, 89, 95, 65, 3, 83, 185, 58, 138,
-                       181, 64, 187, 103, 127, 68, 50, 2, 201, 19, 17, 138, 136, 149, 185, 226, 156, 137, 175,
-                       110, 32, 237, 0, 217, 90, 31, 100, 228, 149, 46, 219, 175, 168, 77, 4, 143, 38, 128,
-                       76, 97, 0, 0, 0, 2, 0, 0, 255, 8, 153, 192, 0, 2, 27, 0, 0, 0, 1, 0, 0, 255, 2, 68,
-                       226, 0, 6, 11, 0, 1, 2, 3, 0, 0, 0, 4, 0, 40, 0, 0, 0, 0, 0, 0, 3, 232, 0, 0, 3, 232,
-                       0, 0, 0, 1, 0, 0, 0, 0, 29, 129, 25, 192, 255, 8, 153, 192, 0, 2, 27, 0, 0, 60, 0, 0,
-                       0, 0, 0, 0, 0, 1, 0, 0, 0, 100, 0, 0, 2, 224, 0, 0, 0, 0, 58, 85, 116, 216, 0, 29, 0,
-                       0, 0, 1, 0, 0, 0, 125, 0, 0, 0, 0, 58, 85, 116, 216, 255, 2, 68, 226, 0, 6, 11, 0, 1,
-                       0, 0, 1,
-               ];
-
                let block_hash = genesis_block(Network::Bitcoin).block_hash();
                let logger = TestLogger::new();
                let network_graph = NetworkGraph::new(block_hash, &logger);
@@ -503,7 +535,7 @@ mod tests {
                assert_eq!(network_graph.read_only().channels().len(), 0);
 
                let rapid_sync = RapidGossipSync::new(&network_graph);
-               let update_result = rapid_sync.update_network_graph(&valid_input[..]);
+               let update_result = rapid_sync.update_network_graph(&VALID_RGS_BINARY);
                if update_result.is_err() {
                        panic!("Unexpected update result: {:?}", update_result)
                }
@@ -526,6 +558,58 @@ mod tests {
                assert!(after.contains("783241506229452801"));
        }
 
+       #[test]
+       fn full_update_succeeds_at_the_beginning_of_the_unix_era() {
+               let block_hash = genesis_block(Network::Bitcoin).block_hash();
+               let logger = TestLogger::new();
+               let network_graph = NetworkGraph::new(block_hash, &logger);
+
+               assert_eq!(network_graph.read_only().channels().len(), 0);
+
+               let rapid_sync = RapidGossipSync::new(&network_graph);
+               // this is mostly for checking uint underflow issues before the fuzzer does
+               let update_result = rapid_sync.update_network_graph_no_std(&VALID_RGS_BINARY, Some(0));
+               assert!(update_result.is_ok());
+               assert_eq!(network_graph.read_only().channels().len(), 2);
+       }
+
+       #[test]
+       fn timestamp_edge_cases_are_handled_correctly() {
+               // this is the timestamp encoded in the binary data of valid_input below
+               let block_hash = genesis_block(Network::Bitcoin).block_hash();
+               let logger = TestLogger::new();
+
+               let latest_succeeding_time = VALID_BINARY_TIMESTAMP + STALE_RGS_UPDATE_AGE_LIMIT_SECS;
+               let earliest_failing_time = latest_succeeding_time + 1;
+
+               {
+                       let network_graph = NetworkGraph::new(block_hash, &logger);
+                       assert_eq!(network_graph.read_only().channels().len(), 0);
+
+                       let rapid_sync = RapidGossipSync::new(&network_graph);
+                       let update_result = rapid_sync.update_network_graph_no_std(&VALID_RGS_BINARY, Some(latest_succeeding_time));
+                       assert!(update_result.is_ok());
+                       assert_eq!(network_graph.read_only().channels().len(), 2);
+               }
+
+               {
+                       let network_graph = NetworkGraph::new(block_hash, &logger);
+                       assert_eq!(network_graph.read_only().channels().len(), 0);
+
+                       let rapid_sync = RapidGossipSync::new(&network_graph);
+                       let update_result = rapid_sync.update_network_graph_no_std(&VALID_RGS_BINARY, Some(earliest_failing_time));
+                       assert!(update_result.is_err());
+                       if let Err(GraphSyncError::LightningError(lightning_error)) = update_result {
+                               assert_eq!(
+                                       lightning_error.err,
+                                       "Rapid Gossip Sync data is more than two weeks old"
+                               );
+                       } else {
+                               panic!("Unexpected update result: {:?}", update_result)
+                       }
+               }
+       }
+
        #[test]
        pub fn update_fails_with_unknown_version() {
                let unknown_version_input = vec![
index 4385fb718791b2a5aa1de9a747959c00b21b671c..6a626ee61d5a7ee30a1e56555499809086668bf6 100644 (file)
@@ -1752,12 +1752,18 @@ fn test_monitor_update_on_pending_forwards() {
        commitment_signed_dance!(nodes[0], nodes[1], bs_updates.commitment_signed, false, true);
 
        let events = nodes[0].node.get_and_clear_pending_events();
-       assert_eq!(events.len(), 2);
+       assert_eq!(events.len(), 3);
        if let Event::PaymentPathFailed { payment_hash, payment_failed_permanently, .. } = events[0] {
                assert_eq!(payment_hash, payment_hash_1);
                assert!(payment_failed_permanently);
        } else { panic!("Unexpected event!"); }
        match events[1] {
+               Event::PaymentFailed { payment_hash, .. } => {
+                       assert_eq!(payment_hash, payment_hash_1);
+               },
+               _ => panic!("Unexpected event"),
+       }
+       match events[2] {
                Event::PendingHTLCsForwardable { .. } => { },
                _ => panic!("Unexpected event"),
        };
index 097312dd1a4803253f6c5a24750c3969729d1c4a..ea5966c722e4a4d3df9ba5541a04111d446b6a87 100644 (file)
@@ -70,7 +70,7 @@ use crate::prelude::*;
 use core::{cmp, mem};
 use core::cell::RefCell;
 use crate::io::Read;
-use crate::sync::{Arc, Mutex, RwLock, RwLockReadGuard, FairRwLock};
+use crate::sync::{Arc, Mutex, RwLock, RwLockReadGuard, FairRwLock, LockTestExt, LockHeldState};
 use core::sync::atomic::{AtomicUsize, Ordering};
 use core::time::Duration;
 use core::ops::Deref;
@@ -1190,9 +1190,9 @@ pub enum RecentPaymentDetails {
                /// made before LDK version 0.0.104.
                payment_hash: Option<PaymentHash>,
        },
-       /// After a payment is explicitly abandoned by calling [`ChannelManager::abandon_payment`], it
-       /// is marked as abandoned until an [`Event::PaymentFailed`] is generated. A payment could also
-       /// be marked as abandoned if pathfinding fails repeatedly or retries have been exhausted.
+       /// After a payment's retries are exhausted per the provided [`Retry`], or it is explicitly
+       /// abandoned via [`ChannelManager::abandon_payment`], it is marked as abandoned until all
+       /// pending HTLCs for this payment resolve and an [`Event::PaymentFailed`] is generated.
        Abandoned {
                /// Hash of the payment that we have given up trying to send.
                payment_hash: PaymentHash,
@@ -1218,13 +1218,10 @@ macro_rules! handle_error {
                match $internal {
                        Ok(msg) => Ok(msg),
                        Err(MsgHandleErrInternal { err, chan_id, shutdown_finish }) => {
-                               #[cfg(any(feature = "_test_utils", test))]
-                               {
-                                       // In testing, ensure there are no deadlocks where the lock is already held upon
-                                       // entering the macro.
-                                       debug_assert!($self.pending_events.try_lock().is_ok());
-                                       debug_assert!($self.per_peer_state.try_write().is_ok());
-                               }
+                               // In testing, ensure there are no deadlocks where the lock is already held upon
+                               // entering the macro.
+                               debug_assert_ne!($self.pending_events.held_by_thread(), LockHeldState::HeldByThread);
+                               debug_assert_ne!($self.per_peer_state.held_by_thread(), LockHeldState::HeldByThread);
 
                                let mut msg_events = Vec::with_capacity(2);
 
@@ -1718,7 +1715,7 @@ where
        ///
        /// This can be useful for payments that may have been prepared, but ultimately not sent, as a
        /// result of a crash. If such a payment exists, is not listed here, and an
-       /// [`Event::PaymentSent`] has not been received, you may consider retrying the payment.
+       /// [`Event::PaymentSent`] has not been received, you may consider resending the payment.
        ///
        /// [`Event::PaymentSent`]: events::Event::PaymentSent
        pub fn list_recent_payments(&self) -> Vec<RecentPaymentDetails> {
@@ -2475,8 +2472,8 @@ where
        /// If a pending payment is currently in-flight with the same [`PaymentId`] provided, this
        /// method will error with an [`APIError::InvalidRoute`]. Note, however, that once a payment
        /// is no longer pending (either via [`ChannelManager::abandon_payment`], or handling of an
-       /// [`Event::PaymentSent`]) LDK will not stop you from sending a second payment with the same
-       /// [`PaymentId`].
+       /// [`Event::PaymentSent`] or [`Event::PaymentFailed`]) LDK will not stop you from sending a
+       /// second payment with the same [`PaymentId`].
        ///
        /// Thus, in order to ensure duplicate payments are not sent, you should implement your own
        /// tracking of payments, including state to indicate once a payment has completed. Because you
@@ -2521,6 +2518,7 @@ where
        /// [`Route`], we assume the invoice had the basic_mpp feature set.
        ///
        /// [`Event::PaymentSent`]: events::Event::PaymentSent
+       /// [`Event::PaymentFailed`]: events::Event::PaymentFailed
        /// [`PeerManager::process_events`]: crate::ln::peer_handler::PeerManager::process_events
        /// [`ChannelMonitorUpdateStatus::InProgress`]: crate::chain::ChannelMonitorUpdateStatus::InProgress
        pub fn send_payment(&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>, payment_id: PaymentId) -> Result<(), PaymentSendFailure> {
@@ -2558,48 +2556,25 @@ where
        }
 
 
-       /// Retries a payment along the given [`Route`].
-       ///
-       /// Errors returned are a superset of those returned from [`send_payment`], so see
-       /// [`send_payment`] documentation for more details on errors. This method will also error if the
-       /// retry amount puts the payment more than 10% over the payment's total amount, if the payment
-       /// for the given `payment_id` cannot be found (likely due to timeout or success), or if
-       /// further retries have been disabled with [`abandon_payment`].
-       ///
-       /// [`send_payment`]: [`ChannelManager::send_payment`]
-       /// [`abandon_payment`]: [`ChannelManager::abandon_payment`]
-       pub fn retry_payment(&self, route: &Route, payment_id: PaymentId) -> Result<(), PaymentSendFailure> {
-               let best_block_height = self.best_block.read().unwrap().height();
-               self.pending_outbound_payments.retry_payment_with_route(route, payment_id, &self.entropy_source, &self.node_signer, best_block_height,
-                       |path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv|
-                       self.send_payment_along_path(path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv))
-       }
-
-       /// Signals that no further retries for the given payment will occur.
+       /// Signals that no further retries for the given payment should occur. Useful if you have a
+       /// pending outbound payment with retries remaining, but wish to stop retrying the payment before
+       /// retries are exhausted.
        ///
-       /// After this method returns, no future calls to [`retry_payment`] for the given `payment_id`
-       /// are allowed. If no [`Event::PaymentFailed`] event had been generated before, one will be
-       /// generated as soon as there are no remaining pending HTLCs for this payment.
+       /// If no [`Event::PaymentFailed`] event had been generated before, one will be generated as soon
+       /// as there are no remaining pending HTLCs for this payment.
        ///
        /// Note that calling this method does *not* prevent a payment from succeeding. You must still
        /// wait until you receive either a [`Event::PaymentFailed`] or [`Event::PaymentSent`] event to
        /// determine the ultimate status of a payment.
        ///
        /// If an [`Event::PaymentFailed`] event is generated and we restart without this
-       /// [`ChannelManager`] having been persisted, the payment may still be in the pending state
-       /// upon restart. This allows further calls to [`retry_payment`] (and requiring a second call
-       /// to [`abandon_payment`] to mark the payment as failed again). Otherwise, future calls to
-       /// [`retry_payment`] will fail with [`PaymentSendFailure::ParameterError`].
+       /// [`ChannelManager`] having been persisted, another [`Event::PaymentFailed`] may be generated.
        ///
-       /// [`abandon_payment`]: Self::abandon_payment
-       /// [`retry_payment`]: Self::retry_payment
        /// [`Event::PaymentFailed`]: events::Event::PaymentFailed
        /// [`Event::PaymentSent`]: events::Event::PaymentSent
        pub fn abandon_payment(&self, payment_id: PaymentId) {
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
-               if let Some(payment_failed_ev) = self.pending_outbound_payments.abandon_payment(payment_id) {
-                       self.pending_events.lock().unwrap().push(payment_failed_ev);
-               }
+               self.pending_outbound_payments.abandon_payment(payment_id, &self.pending_events);
        }
 
        /// Send a spontaneous payment, which is a payment that does not require the recipient to have
@@ -3372,7 +3347,8 @@ where
 
                let best_block_height = self.best_block.read().unwrap().height();
                self.pending_outbound_payments.check_retry_payments(&self.router, || self.list_usable_channels(),
-                       || self.compute_inflight_htlcs(), &self.entropy_source, &self.node_signer, best_block_height, &self.logger,
+                       || self.compute_inflight_htlcs(), &self.entropy_source, &self.node_signer, best_block_height,
+                       &self.pending_events, &self.logger,
                        |path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv|
                        self.send_payment_along_path(path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv));
 
@@ -3743,17 +3719,12 @@ where
        /// Fails an HTLC backwards to the sender of it to us.
        /// Note that we do not assume that channels corresponding to failed HTLCs are still available.
        fn fail_htlc_backwards_internal(&self, source: &HTLCSource, payment_hash: &PaymentHash, onion_error: &HTLCFailReason, destination: HTLCDestination) {
-               #[cfg(any(feature = "_test_utils", test))]
-               {
-                       // Ensure that the peer state channel storage lock is not held when calling this
-                       // function.
-                       // This ensures that future code doesn't introduce a lock_order requirement for
-                       // `forward_htlcs` to be locked after the `per_peer_state` peer locks, which calling
-                       // this function with any `per_peer_state` peer lock aquired would.
-                       let per_peer_state = self.per_peer_state.read().unwrap();
-                       for (_, peer) in per_peer_state.iter() {
-                               debug_assert!(peer.try_lock().is_ok());
-                       }
+               // Ensure that no peer state channel storage lock is held when calling this function.
+               // This ensures that future code doesn't introduce a lock-order requirement for
+               // `forward_htlcs` to be locked after the `per_peer_state` peer locks, which calling
+               // this function with any `per_peer_state` peer lock acquired would.
+               for (_, peer) in self.per_peer_state.read().unwrap().iter() {
+                       debug_assert_ne!(peer.held_by_thread(), LockHeldState::HeldByThread);
                }
 
                //TODO: There is a timing attack here where if a node fails an HTLC back to us they can
@@ -3766,16 +3737,19 @@ where
                // being fully configured. See the docs for `ChannelManagerReadArgs` for more.
                match source {
                        HTLCSource::OutboundRoute { ref path, ref session_priv, ref payment_id, ref payment_params, .. } => {
-                               self.pending_outbound_payments.fail_htlc(source, payment_hash, onion_error, path, session_priv, payment_id, payment_params, self.probing_cookie_secret, &self.secp_ctx, &self.pending_events, &self.logger);
+                               if self.pending_outbound_payments.fail_htlc(source, payment_hash, onion_error, path,
+                                       session_priv, payment_id, payment_params, self.probing_cookie_secret, &self.secp_ctx,
+                                       &self.pending_events, &self.logger)
+                               { self.push_pending_forwards_ev(); }
                        },
                        HTLCSource::PreviousHopData(HTLCPreviousHopData { ref short_channel_id, ref htlc_id, ref incoming_packet_shared_secret, ref phantom_shared_secret, ref outpoint }) => {
                                log_trace!(self.logger, "Failing HTLC with payment_hash {} backwards from us with {:?}", log_bytes!(payment_hash.0), onion_error);
                                let err_packet = onion_error.get_encrypted_failure_packet(incoming_packet_shared_secret, phantom_shared_secret);
 
-                               let mut forward_event = None;
+                               let mut push_forward_ev = false;
                                let mut forward_htlcs = self.forward_htlcs.lock().unwrap();
                                if forward_htlcs.is_empty() {
-                                       forward_event = Some(Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS));
+                                       push_forward_ev = true;
                                }
                                match forward_htlcs.entry(*short_channel_id) {
                                        hash_map::Entry::Occupied(mut entry) => {
@@ -3786,12 +3760,8 @@ where
                                        }
                                }
                                mem::drop(forward_htlcs);
+                               if push_forward_ev { self.push_pending_forwards_ev(); }
                                let mut pending_events = self.pending_events.lock().unwrap();
-                               if let Some(time) = forward_event {
-                                       pending_events.push(events::Event::PendingHTLCsForwardable {
-                                               time_forwardable: time
-                                       });
-                               }
                                pending_events.push(events::Event::HTLCHandlingFailed {
                                        prev_channel_id: outpoint.to_channel_id(),
                                        failed_next_destination: destination,
@@ -4868,7 +4838,7 @@ where
        #[inline]
        fn forward_htlcs(&self, per_source_pending_forwards: &mut [(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)]) {
                for &mut (prev_short_channel_id, prev_funding_outpoint, prev_user_channel_id, ref mut pending_forwards) in per_source_pending_forwards {
-                       let mut forward_event = None;
+                       let mut push_forward_event = false;
                        let mut new_intercept_events = Vec::new();
                        let mut failed_intercept_forwards = Vec::new();
                        if !pending_forwards.is_empty() {
@@ -4926,7 +4896,7 @@ where
                                                                // We don't want to generate a PendingHTLCsForwardable event if only intercepted
                                                                // payments are being processed.
                                                                if forward_htlcs_empty {
-                                                                       forward_event = Some(Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS));
+                                                                       push_forward_event = true;
                                                                }
                                                                entry.insert(vec!(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
                                                                        prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info })));
@@ -4944,16 +4914,21 @@ where
                                let mut events = self.pending_events.lock().unwrap();
                                events.append(&mut new_intercept_events);
                        }
+                       if push_forward_event { self.push_pending_forwards_ev() }
+               }
+       }
 
-                       match forward_event {
-                               Some(time) => {
-                                       let mut pending_events = self.pending_events.lock().unwrap();
-                                       pending_events.push(events::Event::PendingHTLCsForwardable {
-                                               time_forwardable: time
-                                       });
-                               }
-                               None => {},
-                       }
+       // We only want to push a PendingHTLCsForwardable event if no others are queued.
+       fn push_pending_forwards_ev(&self) {
+               let mut pending_events = self.pending_events.lock().unwrap();
+               let forward_ev_exists = pending_events.iter()
+                       .find(|ev| if let events::Event::PendingHTLCsForwardable { .. } = ev { true } else { false })
+                       .is_some();
+               if !forward_ev_exists {
+                       pending_events.push(events::Event::PendingHTLCsForwardable {
+                               time_forwardable:
+                                       Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS),
+                       });
                }
        }
 
@@ -7555,7 +7530,8 @@ where
                        }
                }
 
-               if !forward_htlcs.is_empty() {
+               let pending_outbounds = OutboundPayments { pending_outbound_payments: Mutex::new(pending_outbound_payments.unwrap()), retry_lock: Mutex::new(()) };
+               if !forward_htlcs.is_empty() || pending_outbounds.needs_abandon() {
                        // If we have pending HTLCs to forward, assume we either dropped a
                        // `PendingHTLCsForwardable` or the user received it but never processed it as they
                        // shut down before the timer hit. Either way, set the time_forwardable to a small
@@ -7723,7 +7699,7 @@ where
 
                        inbound_payment_key: expanded_inbound_key,
                        pending_inbound_payments: Mutex::new(pending_inbound_payments),
-                       pending_outbound_payments: OutboundPayments { pending_outbound_payments: Mutex::new(pending_outbound_payments.unwrap()) },
+                       pending_outbound_payments: pending_outbounds,
                        pending_intercepted_htlcs: Mutex::new(pending_intercepted_htlcs.unwrap()),
 
                        forward_htlcs: Mutex::new(forward_htlcs),
index 9f888848fe91db7d2e158ca1bdf922af2dfa729d..0c21588fb717af4aee2ef93a52f9da16abaaaaa3 100644 (file)
@@ -351,6 +351,19 @@ impl<'a, 'b, 'c> Node<'a, 'b, 'c> {
        }
 }
 
+/// If we need an unsafe pointer to a `Node` (ie to reference it in a thread
+/// pre-std::thread::scope), this provides that with `Sync`. Note that accessing some of the fields
+/// in the `Node` are not safe to use (i.e. the ones behind an `Rc`), but that's left to the caller
+/// to figure out.
+pub struct NodePtr(pub *const Node<'static, 'static, 'static>);
+impl NodePtr {
+       pub fn from_node<'a, 'b: 'a, 'c: 'b>(node: &Node<'a, 'b, 'c>) -> Self {
+               Self((node as *const Node<'a, 'b, 'c>).cast())
+       }
+}
+unsafe impl Send for NodePtr {}
+unsafe impl Sync for NodePtr {}
+
 impl<'a, 'b, 'c> Drop for Node<'a, 'b, 'c> {
        fn drop(&mut self) {
                if !panicking() {
@@ -1800,17 +1813,18 @@ macro_rules! expect_payment_failed {
 }
 
 pub fn expect_payment_failed_conditions_event<'a, 'b, 'c, 'd, 'e>(
-       node: &'a Node<'b, 'c, 'd>, payment_failed_event: Event, expected_payment_hash: PaymentHash,
+       payment_failed_events: Vec<Event>, expected_payment_hash: PaymentHash,
        expected_payment_failed_permanently: bool, conditions: PaymentFailedConditions<'e>
 ) {
-       let expected_payment_id = match payment_failed_event {
+       if conditions.expected_mpp_parts_remain { assert_eq!(payment_failed_events.len(), 1); } else { assert_eq!(payment_failed_events.len(), 2); }
+       let expected_payment_id = match &payment_failed_events[0] {
                Event::PaymentPathFailed { payment_hash, payment_failed_permanently, path, retry, payment_id, network_update, short_channel_id,
                        #[cfg(test)]
                        error_code,
                        #[cfg(test)]
                        error_data, .. } => {
-                       assert_eq!(payment_hash, expected_payment_hash, "unexpected payment_hash");
-                       assert_eq!(payment_failed_permanently, expected_payment_failed_permanently, "unexpected payment_failed_permanently value");
+                       assert_eq!(*payment_hash, expected_payment_hash, "unexpected payment_hash");
+                       assert_eq!(*payment_failed_permanently, expected_payment_failed_permanently, "unexpected payment_failed_permanently 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().payment_params.payee_pubkey, path.last().unwrap().pubkey, "Retry payee node_id should match last hop in path");
@@ -1839,7 +1853,7 @@ pub fn expect_payment_failed_conditions_event<'a, 'b, 'c, 'd, 'e>(
                                        },
                                        Some(NetworkUpdate::ChannelFailure { short_channel_id, is_permanent }) if chan_closed => {
                                                if let Some(scid) = conditions.expected_blamed_scid {
-                                                       assert_eq!(short_channel_id, scid);
+                                                       assert_eq!(*short_channel_id, scid);
                                                }
                                                assert!(is_permanent);
                                        },
@@ -1853,10 +1867,7 @@ pub fn expect_payment_failed_conditions_event<'a, 'b, 'c, 'd, 'e>(
                _ => panic!("Unexpected event"),
        };
        if !conditions.expected_mpp_parts_remain {
-               node.node.abandon_payment(expected_payment_id);
-               let events = node.node.get_and_clear_pending_events();
-               assert_eq!(events.len(), 1);
-               match events[0] {
+               match &payment_failed_events[1] {
                        Event::PaymentFailed { ref payment_hash, ref payment_id } => {
                                assert_eq!(*payment_hash, expected_payment_hash, "unexpected second payment_hash");
                                assert_eq!(*payment_id, expected_payment_id);
@@ -1870,9 +1881,8 @@ pub fn expect_payment_failed_conditions<'a, 'b, 'c, 'd, 'e>(
        node: &'a Node<'b, 'c, 'd>, expected_payment_hash: PaymentHash, expected_payment_failed_permanently: bool,
        conditions: PaymentFailedConditions<'e>
 ) {
-       let mut events = node.node.get_and_clear_pending_events();
-       assert_eq!(events.len(), 1);
-       expect_payment_failed_conditions_event(node, events.pop().unwrap(), expected_payment_hash, expected_payment_failed_permanently, conditions);
+       let events = node.node.get_and_clear_pending_events();
+       expect_payment_failed_conditions_event(events, expected_payment_hash, expected_payment_failed_permanently, conditions);
 }
 
 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 {
@@ -2157,22 +2167,6 @@ pub fn fail_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe
 }
 
 pub fn pass_failed_payment_back<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_paths_slice: &[&[&Node<'a, 'b, 'c>]], skip_last: bool, our_payment_hash: PaymentHash) {
-       let expected_payment_id = pass_failed_payment_back_no_abandon(origin_node, expected_paths_slice, skip_last, our_payment_hash);
-       if !skip_last {
-               origin_node.node.abandon_payment(expected_payment_id.unwrap());
-               let events = origin_node.node.get_and_clear_pending_events();
-               assert_eq!(events.len(), 1);
-               match events[0] {
-                       Event::PaymentFailed { ref payment_hash, ref payment_id } => {
-                               assert_eq!(*payment_hash, our_payment_hash, "unexpected second payment_hash");
-                               assert_eq!(*payment_id, expected_payment_id.unwrap());
-                       }
-                       _ => panic!("Unexpected second event"),
-               }
-       }
-}
-
-pub fn pass_failed_payment_back_no_abandon<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_paths_slice: &[&[&Node<'a, 'b, 'c>]], skip_last: bool, our_payment_hash: PaymentHash) -> Option<PaymentId> {
        let mut expected_paths: Vec<_> = expected_paths_slice.iter().collect();
        check_added_monitors!(expected_paths[0].last().unwrap(), expected_paths.len());
 
@@ -2196,8 +2190,6 @@ pub fn pass_failed_payment_back_no_abandon<'a, 'b, 'c>(origin_node: &Node<'a, 'b
        per_path_msgs.sort_unstable_by(|(_, node_id_a), (_, node_id_b)| node_id_a.cmp(node_id_b));
        expected_paths.sort_unstable_by(|path_a, path_b| path_a[path_a.len() - 2].node.get_our_node_id().cmp(&path_b[path_b.len() - 2].node.get_our_node_id()));
 
-       let mut expected_payment_id = None;
-
        for (i, (expected_route, (path_msgs, next_hop))) in expected_paths.iter().zip(per_path_msgs.drain(..)).enumerate() {
                let mut next_msgs = Some(path_msgs);
                let mut expected_next_node = next_hop;
@@ -2245,8 +2237,9 @@ pub fn pass_failed_payment_back_no_abandon<'a, 'b, 'c>(origin_node: &Node<'a, 'b
                        assert!(origin_node.node.get_and_clear_pending_msg_events().is_empty());
                        commitment_signed_dance!(origin_node, prev_node, next_msgs.as_ref().unwrap().1, false);
                        let events = origin_node.node.get_and_clear_pending_events();
-                       assert_eq!(events.len(), 1);
-                       expected_payment_id = Some(match events[0] {
+                       if i == expected_paths.len() - 1 { assert_eq!(events.len(), 2); } else { assert_eq!(events.len(), 1); }
+
+                       let expected_payment_id = match events[0] {
                                Event::PaymentPathFailed { payment_hash, payment_failed_permanently, all_paths_failed, ref path, ref payment_id, .. } => {
                                        assert_eq!(payment_hash, our_payment_hash);
                                        assert!(payment_failed_permanently);
@@ -2257,7 +2250,16 @@ pub fn pass_failed_payment_back_no_abandon<'a, 'b, 'c>(origin_node: &Node<'a, 'b
                                        payment_id.unwrap()
                                },
                                _ => panic!("Unexpected event"),
-                       });
+                       };
+                       if i == expected_paths.len() - 1 {
+                               match events[1] {
+                                       Event::PaymentFailed { ref payment_hash, ref payment_id } => {
+                                               assert_eq!(*payment_hash, our_payment_hash, "unexpected second payment_hash");
+                                               assert_eq!(*payment_id, expected_payment_id);
+                                       }
+                                       _ => panic!("Unexpected second event"),
+                               }
+                       }
                }
        }
 
@@ -2266,8 +2268,6 @@ pub fn pass_failed_payment_back_no_abandon<'a, 'b, 'c>(origin_node: &Node<'a, 'b
        assert!(expected_paths[0].last().unwrap().node.get_and_clear_pending_events().is_empty());
        assert!(expected_paths[0].last().unwrap().node.get_and_clear_pending_msg_events().is_empty());
        check_added_monitors!(expected_paths[0].last().unwrap(), 0);
-
-       expected_payment_id
 }
 
 pub fn fail_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_path: &[&Node<'a, 'b, 'c>], our_payment_hash: PaymentHash)  {
index 7e2532f1f4f6774774dfca88914e38cf0e4e6fd6..8a5a77b90c373ccb6ff71019b15e8bfda1e45b8c 100644 (file)
@@ -3170,7 +3170,7 @@ fn do_test_commitment_revoked_fail_backward_exhaustive(deliver_bs_raa: bool, use
        connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
 
        let events = nodes[1].node.get_and_clear_pending_events();
-       assert_eq!(events.len(), if deliver_bs_raa { 2 + nodes.len() - 1 } else { 3 + nodes.len() });
+       assert_eq!(events.len(), if deliver_bs_raa { 3 + nodes.len() - 1 } else { 4 + nodes.len() });
        match events[0] {
                Event::ChannelClosed { reason: ClosureReason::CommitmentTxConfirmed, .. } => { },
                _ => panic!("Unexepected event"),
@@ -3181,20 +3181,11 @@ fn do_test_commitment_revoked_fail_backward_exhaustive(deliver_bs_raa: bool, use
                },
                _ => panic!("Unexpected event"),
        }
-       if !deliver_bs_raa {
-               match events[2] {
-                       Event::PendingHTLCsForwardable { .. } => { },
-                       _ => panic!("Unexpected event"),
-               };
-               nodes[1].node.abandon_payment(PaymentId(fourth_payment_hash.0));
-               let payment_failed_events = nodes[1].node.get_and_clear_pending_events();
-               assert_eq!(payment_failed_events.len(), 1);
-               match payment_failed_events[0] {
-                       Event::PaymentFailed { ref payment_hash, .. } => {
-                               assert_eq!(*payment_hash, fourth_payment_hash);
-                       },
-                       _ => panic!("Unexpected event"),
-               }
+       match events[2] {
+               Event::PaymentFailed { ref payment_hash, .. } => {
+                       assert_eq!(*payment_hash, fourth_payment_hash);
+               },
+               _ => panic!("Unexpected event"),
        }
 
        nodes[1].node.process_pending_htlc_forwards();
@@ -3242,7 +3233,7 @@ fn do_test_commitment_revoked_fail_backward_exhaustive(deliver_bs_raa: bool, use
                        commitment_signed_dance!(nodes[0], nodes[1], commitment_signed, false, true);
 
                        let events = nodes[0].node.get_and_clear_pending_events();
-                       assert_eq!(events.len(), 3);
+                       assert_eq!(events.len(), 6);
                        match events[0] {
                                Event::PaymentPathFailed { ref payment_hash, ref network_update, .. } => {
                                        assert!(failed_htlcs.insert(payment_hash.0));
@@ -3255,19 +3246,37 @@ fn do_test_commitment_revoked_fail_backward_exhaustive(deliver_bs_raa: bool, use
                                _ => panic!("Unexpected event"),
                        }
                        match events[1] {
+                               Event::PaymentFailed { ref payment_hash, .. } => {
+                                       assert_eq!(*payment_hash, first_payment_hash);
+                               },
+                               _ => panic!("Unexpected event"),
+                       }
+                       match events[2] {
                                Event::PaymentPathFailed { ref payment_hash, ref network_update, .. } => {
                                        assert!(failed_htlcs.insert(payment_hash.0));
                                        assert!(network_update.is_some());
                                },
                                _ => panic!("Unexpected event"),
                        }
-                       match events[2] {
+                       match events[3] {
+                               Event::PaymentFailed { ref payment_hash, .. } => {
+                                       assert_eq!(*payment_hash, second_payment_hash);
+                               },
+                               _ => panic!("Unexpected event"),
+                       }
+                       match events[4] {
                                Event::PaymentPathFailed { ref payment_hash, ref network_update, .. } => {
                                        assert!(failed_htlcs.insert(payment_hash.0));
                                        assert!(network_update.is_some());
                                },
                                _ => panic!("Unexpected event"),
                        }
+                       match events[5] {
+                               Event::PaymentFailed { ref payment_hash, .. } => {
+                                       assert_eq!(*payment_hash, third_payment_hash);
+                               },
+                               _ => panic!("Unexpected event"),
+                       }
                },
                _ => panic!("Unexpected event"),
        }
@@ -3354,7 +3363,7 @@ fn fail_backward_pending_htlc_upon_channel_failure() {
                nodes[0].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &update_add_htlc);
        }
        let events = nodes[0].node.get_and_clear_pending_events();
-       assert_eq!(events.len(), 2);
+       assert_eq!(events.len(), 3);
        // Check that Alice fails backward the pending HTLC from the second payment.
        match events[0] {
                Event::PaymentPathFailed { payment_hash, .. } => {
@@ -3363,6 +3372,12 @@ fn fail_backward_pending_htlc_upon_channel_failure() {
                _ => panic!("Unexpected event"),
        }
        match events[1] {
+               Event::PaymentFailed { payment_hash, .. } => {
+                       assert_eq!(payment_hash, failed_payment_hash);
+               },
+               _ => panic!("Unexpected event"),
+       }
+       match events[2] {
                Event::ChannelClosed { reason: ClosureReason::ProcessingError { ref err }, .. } => {
                        assert_eq!(err, "Remote side tried to send a 0-msat HTLC");
                },
@@ -3594,7 +3609,7 @@ fn test_simple_peer_disconnect() {
        reconnect_nodes(&nodes[0], &nodes[1], (false, false), (0, 0), (0, 0), (0, 0), (1, 0), (1, 0), (false, false));
        {
                let events = nodes[0].node.get_and_clear_pending_events();
-               assert_eq!(events.len(), 3);
+               assert_eq!(events.len(), 4);
                match events[0] {
                        Event::PaymentSent { payment_preimage, payment_hash, .. } => {
                                assert_eq!(payment_preimage, payment_preimage_3);
@@ -3610,6 +3625,12 @@ fn test_simple_peer_disconnect() {
                        _ => panic!("Unexpected event"),
                }
                match events[2] {
+                       Event::PaymentFailed { payment_hash, .. } => {
+                               assert_eq!(payment_hash, payment_hash_5);
+                       },
+                       _ => panic!("Unexpected event"),
+               }
+               match events[3] {
                        Event::PaymentPathSuccessful { .. } => {},
                        _ => panic!("Unexpected event"),
                }
@@ -5123,7 +5144,7 @@ fn do_test_fail_backwards_unrevoked_remote_announce(deliver_last_raa: bool, anno
        }
 
        let as_events = nodes[0].node.get_and_clear_pending_events();
-       assert_eq!(as_events.len(), if announce_latest { 5 } else { 3 });
+       assert_eq!(as_events.len(), if announce_latest { 10 } else { 6 });
        let mut as_failds = HashSet::new();
        let mut as_updates = 0;
        for event in as_events.iter() {
@@ -5137,6 +5158,7 @@ fn do_test_fail_backwards_unrevoked_remote_announce(deliver_last_raa: bool, anno
                        if network_update.is_some() {
                                as_updates += 1;
                        }
+               } else if let &Event::PaymentFailed { .. } = event {
                } else { panic!("Unexpected event"); }
        }
        assert!(as_failds.contains(&payment_hash_1));
@@ -5148,7 +5170,7 @@ fn do_test_fail_backwards_unrevoked_remote_announce(deliver_last_raa: bool, anno
        assert!(as_failds.contains(&payment_hash_6));
 
        let bs_events = nodes[1].node.get_and_clear_pending_events();
-       assert_eq!(bs_events.len(), if announce_latest { 4 } else { 3 });
+       assert_eq!(bs_events.len(), if announce_latest { 8 } else { 6 });
        let mut bs_failds = HashSet::new();
        let mut bs_updates = 0;
        for event in bs_events.iter() {
@@ -5162,6 +5184,7 @@ fn do_test_fail_backwards_unrevoked_remote_announce(deliver_last_raa: bool, anno
                        if network_update.is_some() {
                                bs_updates += 1;
                        }
+               } else if let &Event::PaymentFailed { .. } = event {
                } else { panic!("Unexpected event"); }
        }
        assert!(bs_failds.contains(&payment_hash_1));
@@ -5670,7 +5693,7 @@ fn test_fail_holding_cell_htlc_upon_free() {
 
        // Check that the payment failed to be sent out.
        let events = nodes[0].node.get_and_clear_pending_events();
-       assert_eq!(events.len(), 1);
+       assert_eq!(events.len(), 2);
        match &events[0] {
                &Event::PaymentPathFailed { ref payment_id, ref payment_hash, ref payment_failed_permanently, ref network_update, ref all_paths_failed, ref short_channel_id, .. } => {
                        assert_eq!(PaymentId(our_payment_hash.0), *payment_id.as_ref().unwrap());
@@ -5682,6 +5705,12 @@ fn test_fail_holding_cell_htlc_upon_free() {
                },
                _ => panic!("Unexpected event"),
        }
+       match &events[1] {
+               &Event::PaymentFailed { ref payment_hash, .. } => {
+                       assert_eq!(our_payment_hash.clone(), *payment_hash);
+               },
+               _ => panic!("Unexpected event"),
+       }
 }
 
 // Test that if multiple HTLCs are released from the holding cell and one is
@@ -5755,7 +5784,7 @@ fn test_free_and_fail_holding_cell_htlcs() {
 
        // Check that the second payment failed to be sent out.
        let events = nodes[0].node.get_and_clear_pending_events();
-       assert_eq!(events.len(), 1);
+       assert_eq!(events.len(), 2);
        match &events[0] {
                &Event::PaymentPathFailed { ref payment_id, ref payment_hash, ref payment_failed_permanently, ref network_update, ref all_paths_failed, ref short_channel_id, .. } => {
                        assert_eq!(payment_id_2, *payment_id.as_ref().unwrap());
@@ -5767,6 +5796,12 @@ fn test_free_and_fail_holding_cell_htlcs() {
                },
                _ => panic!("Unexpected event"),
        }
+       match &events[1] {
+               &Event::PaymentFailed { ref payment_hash, .. } => {
+                       assert_eq!(payment_hash_2.clone(), *payment_hash);
+               },
+               _ => panic!("Unexpected event"),
+       }
 
        // Complete the first payment and the RAA from the fee update.
        let (payment_event, send_raa_event) = {
@@ -6649,7 +6684,7 @@ fn test_channel_failed_after_message_with_badonion_node_perm_bits_set() {
        }
 
        let events_5 = nodes[0].node.get_and_clear_pending_events();
-       assert_eq!(events_5.len(), 1);
+       assert_eq!(events_5.len(), 2);
 
        // Expect a PaymentPathFailed event with a ChannelFailure network update for the channel between
        // the node originating the error to its next hop.
@@ -6663,6 +6698,12 @@ fn test_channel_failed_after_message_with_badonion_node_perm_bits_set() {
                },
                _ => panic!("Unexpected event"),
        }
+       match events_5[1] {
+               Event::PaymentFailed { payment_hash, .. } => {
+                       assert_eq!(payment_hash, our_payment_hash);
+               },
+               _ => panic!("Unexpected event"),
+       }
 
        // TODO: Test actual removal of channel from NetworkGraph when it's implemented.
 }
@@ -6734,7 +6775,7 @@ fn do_test_failure_delay_dust_htlc_local_commitment(announce_latest: bool) {
        connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
        let events = nodes[0].node.get_and_clear_pending_events();
        // Only 2 PaymentPathFailed events should show up, over-dust HTLC has to be failed by timeout tx
-       assert_eq!(events.len(), 2);
+       assert_eq!(events.len(), 4);
        let mut first_failed = false;
        for event in events {
                match event {
@@ -6745,7 +6786,8 @@ fn do_test_failure_delay_dust_htlc_local_commitment(announce_latest: bool) {
                                } else {
                                        assert_eq!(payment_hash, payment_hash_2);
                                }
-                       }
+                       },
+                       Event::PaymentFailed { .. } => {}
                        _ => panic!("Unexpected event"),
                }
        }
@@ -9032,9 +9074,11 @@ fn do_test_dup_htlc_second_rejected(test_for_second_fail_panic: bool) {
                commitment_signed_dance!(nodes[0], nodes[1], fail_updates_1.commitment_signed, false);
 
                let failure_events = nodes[0].node.get_and_clear_pending_events();
-               assert_eq!(failure_events.len(), 2);
+               assert_eq!(failure_events.len(), 4);
                if let Event::PaymentPathFailed { .. } = failure_events[0] {} else { panic!(); }
-               if let Event::PaymentPathFailed { .. } = failure_events[1] {} else { panic!(); }
+               if let Event::PaymentFailed { .. } = failure_events[1] {} else { panic!(); }
+               if let Event::PaymentPathFailed { .. } = failure_events[2] {} else { panic!(); }
+               if let Event::PaymentFailed { .. } = failure_events[3] {} else { panic!(); }
        } else {
                // Let the second HTLC fail and claim the first
                expect_pending_htlcs_forwardable_and_htlc_handling_failed_ignore!(nodes[1], vec![HTLCDestination::FailedPayment { payment_hash: our_payment_hash }]);
@@ -9045,7 +9089,7 @@ fn do_test_dup_htlc_second_rejected(test_for_second_fail_panic: bool) {
                nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &fail_updates_1.update_fail_htlcs[0]);
                commitment_signed_dance!(nodes[0], nodes[1], fail_updates_1.commitment_signed, false);
 
-               expect_payment_failed_conditions(&nodes[0], our_payment_hash, true, PaymentFailedConditions::new().mpp_parts_remain());
+               expect_payment_failed_conditions(&nodes[0], our_payment_hash, true, PaymentFailedConditions::new());
 
                claim_payment(&nodes[0], &[&nodes[1]], our_payment_preimage);
        }
@@ -9167,7 +9211,27 @@ fn test_inconsistent_mpp_params() {
        assert_eq!(events.len(), 1);
        pass_along_path(&nodes[0], &[&nodes[2], &nodes[3]], 15_000_000, our_payment_hash, Some(our_payment_secret), events.pop().unwrap(), true, None);
 
-       claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, our_payment_preimage);
+       do_claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, our_payment_preimage);
+       let events = nodes[0].node.get_and_clear_pending_events();
+       assert_eq!(events.len(), 3);
+       match events[0] {
+               Event::PaymentSent { payment_hash, .. } => { // The payment was abandoned earlier, so the fee paid will be None
+                       assert_eq!(payment_hash, our_payment_hash);
+               },
+               _ => panic!("Unexpected event")
+       }
+       match events[1] {
+               Event::PaymentPathSuccessful { payment_hash, .. } => {
+                       assert_eq!(payment_hash.unwrap(), our_payment_hash);
+               },
+               _ => panic!("Unexpected event")
+       }
+       match events[2] {
+               Event::PaymentPathSuccessful { payment_hash, .. } => {
+                       assert_eq!(payment_hash.unwrap(), our_payment_hash);
+               },
+               _ => panic!("Unexpected event")
+       }
 }
 
 #[test]
index 088cf8661f8487b577a2a71e2a4b68f77236f7ea..c4435b470ca8c6deb8618fada682e9cd063fcc17 100644 (file)
@@ -1241,11 +1241,10 @@ fn do_test_revoked_counterparty_commitment_balances(confirm_htlc_spend_first: bo
        test_spendable_output(&nodes[1], &as_revoked_txn[0]);
 
        let mut payment_failed_events = nodes[1].node.get_and_clear_pending_events();
-       expect_payment_failed_conditions_event(&nodes[1], payment_failed_events.pop().unwrap(),
-               dust_payment_hash, false, PaymentFailedConditions::new());
-       expect_payment_failed_conditions_event(&nodes[1], payment_failed_events.pop().unwrap(),
+       expect_payment_failed_conditions_event(payment_failed_events[..2].to_vec(),
                missing_htlc_payment_hash, false, PaymentFailedConditions::new());
-       assert!(payment_failed_events.is_empty());
+       expect_payment_failed_conditions_event(payment_failed_events[2..].to_vec(),
+               dust_payment_hash, false, PaymentFailedConditions::new());
 
        connect_blocks(&nodes[1], 1);
        test_spendable_output(&nodes[1], &claim_txn[if confirm_htlc_spend_first { 2 } else { 3 }]);
index 8f266bc176e5105a054511458b4dd2359ab74666..da5aadb83065af7d6de274c30a021e4b9b05fc23 100644 (file)
@@ -166,7 +166,7 @@ fn run_onion_failure_test_with_fail_intercept<F1,F2,F3>(_name: &str, test_case:
        commitment_signed_dance!(nodes[0], nodes[1], update_1_0.commitment_signed, false, true);
 
        let events = nodes[0].node.get_and_clear_pending_events();
-       assert_eq!(events.len(), 1);
+       assert_eq!(events.len(), 2);
        if let &Event::PaymentPathFailed { ref payment_failed_permanently, ref network_update, ref all_paths_failed, ref short_channel_id, ref error_code, .. } = &events[0] {
                assert_eq!(*payment_failed_permanently, !expected_retryable);
                assert_eq!(*all_paths_failed, true);
@@ -212,10 +212,7 @@ fn run_onion_failure_test_with_fail_intercept<F1,F2,F3>(_name: &str, test_case:
        } else {
                panic!("Unexpected event");
        }
-       nodes[0].node.abandon_payment(payment_id);
-       let events = nodes[0].node.get_and_clear_pending_events();
-       assert_eq!(events.len(), 1);
-       match events[0] {
+       match events[1] {
                Event::PaymentFailed { payment_hash: ev_payment_hash, payment_id: ev_payment_id } => {
                        assert_eq!(*payment_hash, ev_payment_hash);
                        assert_eq!(payment_id, ev_payment_id);
index 31ba9cb371f9c2c3036a5f4779cc9f24c429467e..f252d88ac364e87947ed42c31bef96b5c276375f 100644 (file)
@@ -15,7 +15,7 @@ use bitcoin::secp256k1::{self, Secp256k1, SecretKey};
 
 use crate::chain::keysinterface::{EntropySource, NodeSigner, Recipient};
 use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
-use crate::ln::channelmanager::{ChannelDetails, HTLCSource, IDEMPOTENCY_TIMEOUT_TICKS, MIN_HTLC_RELAY_HOLDING_CELL_MILLIS, PaymentId};
+use crate::ln::channelmanager::{ChannelDetails, HTLCSource, IDEMPOTENCY_TIMEOUT_TICKS, PaymentId};
 use crate::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA as LDK_DEFAULT_MIN_FINAL_CLTV_EXPIRY_DELTA;
 use crate::ln::msgs::DecodeError;
 use crate::ln::onion_utils::HTLCFailReason;
@@ -30,7 +30,6 @@ use crate::util::time::tests::SinceEpoch;
 use core::cmp;
 use core::fmt::{self, Display, Formatter};
 use core::ops::Deref;
-use core::time::Duration;
 
 use crate::prelude::*;
 use crate::sync::Mutex;
@@ -163,13 +162,13 @@ impl PendingOutboundPayment {
                let our_payment_hash;
                core::mem::swap(&mut session_privs, match self {
                        PendingOutboundPayment::Legacy { .. } |
-                               PendingOutboundPayment::Fulfilled { .. } =>
+                       PendingOutboundPayment::Fulfilled { .. } =>
                                return Err(()),
-                               PendingOutboundPayment::Retryable { session_privs, payment_hash, .. } |
-                                       PendingOutboundPayment::Abandoned { session_privs, payment_hash, .. } => {
-                                               our_payment_hash = *payment_hash;
-                                               session_privs
-                                       },
+                       PendingOutboundPayment::Retryable { session_privs, payment_hash, .. } |
+                       PendingOutboundPayment::Abandoned { session_privs, payment_hash, .. } => {
+                               our_payment_hash = *payment_hash;
+                               session_privs
+                       },
                });
                *self = PendingOutboundPayment::Abandoned { session_privs, payment_hash: our_payment_hash };
                Ok(())
@@ -323,68 +322,58 @@ pub enum PaymentSendFailure {
        ///
        /// You can freely resend the payment in full (with the parameter error fixed).
        ///
-       /// Because the payment failed outright, no payment tracking is done, you do not need to call
-       /// [`ChannelManager::abandon_payment`] and [`ChannelManager::retry_payment`] will *not* work
-       /// for this payment.
+       /// Because the payment failed outright, no payment tracking is done and no
+       /// [`Event::PaymentPathFailed`] or [`Event::PaymentFailed`] events will be generated.
        ///
-       /// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment
-       /// [`ChannelManager::retry_payment`]: crate::ln::channelmanager::ChannelManager::retry_payment
+       /// [`Event::PaymentPathFailed`]: crate::util::events::Event::PaymentPathFailed
+       /// [`Event::PaymentFailed`]: crate::util::events::Event::PaymentFailed
        ParameterError(APIError),
        /// A parameter in a single path which was passed to send_payment was invalid, preventing us
        /// from attempting to send the payment at all.
        ///
        /// You can freely resend the payment in full (with the parameter error fixed).
        ///
+       /// Because the payment failed outright, no payment tracking is done and no
+       /// [`Event::PaymentPathFailed`] or [`Event::PaymentFailed`] events will be generated.
+       ///
        /// The results here are ordered the same as the paths in the route object which was passed to
        /// send_payment.
        ///
-       /// Because the payment failed outright, no payment tracking is done, you do not need to call
-       /// [`ChannelManager::abandon_payment`] and [`ChannelManager::retry_payment`] will *not* work
-       /// for this payment.
-       ///
-       /// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment
-       /// [`ChannelManager::retry_payment`]: crate::ln::channelmanager::ChannelManager::retry_payment
+       /// [`Event::PaymentPathFailed`]: crate::util::events::Event::PaymentPathFailed
+       /// [`Event::PaymentFailed`]: crate::util::events::Event::PaymentFailed
        PathParameterError(Vec<Result<(), APIError>>),
        /// All paths which were attempted failed to send, with no channel state change taking place.
        /// You can freely resend the payment in full (though you probably want to do so over different
        /// paths than the ones selected).
        ///
-       /// Because the payment failed outright, no payment tracking is done, you do not need to call
-       /// [`ChannelManager::abandon_payment`] and [`ChannelManager::retry_payment`] will *not* work
-       /// for this payment.
+       /// Because the payment failed outright, no payment tracking is done and no
+       /// [`Event::PaymentPathFailed`] or [`Event::PaymentFailed`] events will be generated.
        ///
-       /// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment
-       /// [`ChannelManager::retry_payment`]: crate::ln::channelmanager::ChannelManager::retry_payment
+       /// [`Event::PaymentPathFailed`]: crate::util::events::Event::PaymentPathFailed
+       /// [`Event::PaymentFailed`]: crate::util::events::Event::PaymentFailed
        AllFailedResendSafe(Vec<APIError>),
        /// Indicates that a payment for the provided [`PaymentId`] is already in-flight and has not
-       /// yet completed (i.e. generated an [`Event::PaymentSent`]) or been abandoned (via
-       /// [`ChannelManager::abandon_payment`]).
+       /// yet completed (i.e. generated an [`Event::PaymentSent`] or [`Event::PaymentFailed`]).
        ///
        /// [`PaymentId`]: crate::ln::channelmanager::PaymentId
        /// [`Event::PaymentSent`]: crate::util::events::Event::PaymentSent
-       /// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment
+       /// [`Event::PaymentFailed`]: crate::util::events::Event::PaymentFailed
        DuplicatePayment,
-       /// Some paths which were attempted failed to send, though possibly not all. At least some
-       /// paths have irrevocably committed to the HTLC and retrying the payment in full would result
-       /// in over-/re-payment.
+       /// Some paths that were attempted failed to send, though some paths may have succeeded. At least
+       /// some paths have irrevocably committed to the HTLC.
        ///
-       /// The results here are ordered the same as the paths in the route object which was passed to
-       /// send_payment, and any `Err`s which are not [`APIError::MonitorUpdateInProgress`] can be
-       /// safely retried via [`ChannelManager::retry_payment`].
+       /// The results here are ordered the same as the paths in the route object that was passed to
+       /// send_payment.
        ///
-       /// Any entries which contain `Err(APIError::MonitorUpdateInprogress)` or `Ok(())` MUST NOT be
-       /// retried as they will result in over-/re-payment. These HTLCs all either successfully sent
-       /// (in the case of `Ok(())`) or will send once a [`MonitorEvent::Completed`] is provided for
-       /// the next-hop channel with the latest update_id.
+       /// Any entries that contain `Err(APIError::MonitorUpdateInprogress)` will send once a
+       /// [`MonitorEvent::Completed`] is provided for the next-hop channel with the latest update_id.
        ///
-       /// [`ChannelManager::retry_payment`]: crate::ln::channelmanager::ChannelManager::retry_payment
        /// [`MonitorEvent::Completed`]: crate::chain::channelmonitor::MonitorEvent::Completed
        PartialFailure {
-               /// The errors themselves, in the same order as the route hops.
+               /// The errors themselves, in the same order as the paths from the route.
                results: Vec<Result<(), APIError>>,
                /// If some paths failed without irrevocably committing to the new HTLC(s), this will
-               /// contain a [`RouteParameters`] object which can be used to calculate a new route that
-               /// will pay all remaining unpaid balance.
+               /// contain a [`RouteParameters`] object for the failing paths.
                failed_paths_retry: Option<RouteParameters>,
                /// The payment id for the payment, which is now at least partially pending.
                payment_id: PaymentId,
@@ -393,12 +382,14 @@ pub enum PaymentSendFailure {
 
 pub(super) struct OutboundPayments {
        pub(super) pending_outbound_payments: Mutex<HashMap<PaymentId, PendingOutboundPayment>>,
+       pub(super) retry_lock: Mutex<()>,
 }
 
 impl OutboundPayments {
        pub(super) fn new() -> Self {
                Self {
-                       pending_outbound_payments: Mutex::new(HashMap::new())
+                       pending_outbound_payments: Mutex::new(HashMap::new()),
+                       retry_lock: Mutex::new(()),
                }
        }
 
@@ -491,7 +482,8 @@ impl OutboundPayments {
 
        pub(super) fn check_retry_payments<R: Deref, ES: Deref, NS: Deref, SP, IH, FH, L: Deref>(
                &self, router: &R, first_hops: FH, inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS,
-               best_block_height: u32, logger: &L, send_payment_along_path: SP,
+               best_block_height: u32, pending_events: &Mutex<Vec<events::Event>>, logger: &L,
+               send_payment_along_path: SP,
        )
        where
                R::Target: Router,
@@ -503,6 +495,7 @@ impl OutboundPayments {
                FH: Fn() -> Vec<ChannelDetails>,
                L::Target: Logger,
        {
+               let _single_thread = self.retry_lock.lock().unwrap();
                loop {
                        let mut outbounds = self.pending_outbound_payments.lock().unwrap();
                        let mut retry_id_route_params = None;
@@ -525,15 +518,40 @@ impl OutboundPayments {
                                        }
                                }
                        }
+                       core::mem::drop(outbounds);
                        if let Some((payment_id, route_params)) = retry_id_route_params {
-                               core::mem::drop(outbounds);
                                if let Err(e) = self.pay_internal(payment_id, None, route_params, router, first_hops(), &inflight_htlcs, entropy_source, node_signer, best_block_height, logger, &send_payment_along_path) {
                                        log_info!(logger, "Errored retrying payment: {:?}", e);
+                                       // If we error on retry, there is no chance of the payment succeeding and no HTLCs have
+                                       // been irrevocably committed to, so we can safely abandon.
+                                       self.abandon_payment(payment_id, pending_events);
                                }
                        } else { break }
                }
+
+               let mut outbounds = self.pending_outbound_payments.lock().unwrap();
+               outbounds.retain(|pmt_id, pmt| {
+                       let mut retain = true;
+                       if !pmt.is_auto_retryable_now() && pmt.remaining_parts() == 0 {
+                               if pmt.mark_abandoned().is_ok() {
+                                       pending_events.lock().unwrap().push(events::Event::PaymentFailed {
+                                               payment_id: *pmt_id,
+                                               payment_hash: pmt.payment_hash().expect("PendingOutboundPayments::Retryable always has a payment hash set"),
+                                       });
+                                       retain = false;
+                               }
+                       }
+                       retain
+               });
+       }
+
+       pub(super) fn needs_abandon(&self) -> bool {
+               let outbounds = self.pending_outbound_payments.lock().unwrap();
+               outbounds.iter().any(|(_, pmt)|
+                       !pmt.is_auto_retryable_now() && pmt.remaining_parts() == 0 && !pmt.is_fulfilled())
        }
 
+       /// Will return `Ok(())` iff at least one HTLC is sent for the payment.
        fn pay_internal<R: Deref, NS: Deref, ES: Deref, IH, SP, L: Deref>(
                &self, payment_id: PaymentId,
                initial_send_info: Option<(PaymentHash, &Option<PaymentSecret>, Option<PaymentPreimage>, Retry)>,
@@ -876,8 +894,8 @@ impl OutboundPayments {
                        .map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e })
        }
 
-       // If we failed to send any paths, we should remove the new PaymentId from the
-       // `pending_outbound_payments` map, as the user isn't expected to `abandon_payment`.
+       // If we failed to send any paths, remove the new PaymentId from the `pending_outbound_payments`
+       // map as the payment is free to be resent.
        fn remove_outbound_if_all_failed(&self, payment_id: PaymentId, err: &PaymentSendFailure) {
                if let &PaymentSendFailure::AllFailedResendSafe(_) = err {
                        let removed = self.pending_outbound_payments.lock().unwrap().remove(&payment_id).is_some();
@@ -993,34 +1011,51 @@ impl OutboundPayments {
                });
        }
 
+       // Returns a bool indicating whether a PendingHTLCsForwardable event should be generated.
        pub(super) fn fail_htlc<L: Deref>(
                &self, source: &HTLCSource, payment_hash: &PaymentHash, onion_error: &HTLCFailReason,
                path: &Vec<RouteHop>, session_priv: &SecretKey, payment_id: &PaymentId,
                payment_params: &Option<PaymentParameters>, probing_cookie_secret: [u8; 32],
                secp_ctx: &Secp256k1<secp256k1::All>, pending_events: &Mutex<Vec<events::Event>>, logger: &L
-       ) where L::Target: Logger {
+       ) -> bool where L::Target: Logger {
                #[cfg(test)]
                let (network_update, short_channel_id, payment_retryable, onion_error_code, onion_error_data) = onion_error.decode_onion_failure(secp_ctx, logger, &source);
                #[cfg(not(test))]
                let (network_update, short_channel_id, payment_retryable, _, _) = onion_error.decode_onion_failure(secp_ctx, logger, &source);
 
+               let payment_is_probe = payment_is_probe(payment_hash, &payment_id, probing_cookie_secret);
                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 any payments already need retry, there's no need to generate a redundant
+               // `PendingHTLCsForwardable`.
+               let already_awaiting_retry = outbounds.iter().any(|(_, pmt)| {
+                       let mut awaiting_retry = false;
+                       if pmt.is_auto_retryable_now() {
+                               if let PendingOutboundPayment::Retryable { pending_amt_msat, total_msat, .. } = pmt {
+                                       if pending_amt_msat < total_msat {
+                                               awaiting_retry = true;
+                                       }
+                               }
+                       }
+                       awaiting_retry
+               });
+
                let mut all_paths_failed = false;
                let mut full_failure_ev = None;
-               let mut pending_retry_ev = None;
+               let mut pending_retry_ev = false;
                let mut retry = None;
                let attempts_remaining = if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(*payment_id) {
                        if !payment.get_mut().remove(&session_priv_bytes, Some(&path)) {
                                log_trace!(logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
-                               return
+                               return false
                        }
                        if payment.get().is_fulfilled() {
                                log_trace!(logger, "Received failure of HTLC with payment_hash {} after payment completion", log_bytes!(payment_hash.0));
-                               return
+                               return false
                        }
-                       let is_retryable_now = payment.get().is_auto_retryable_now();
+                       let mut is_retryable_now = payment.get().is_auto_retryable_now();
                        if let Some(scid) = short_channel_id {
                                payment.get_mut().insert_previously_failed_scid(scid);
                        }
@@ -1051,26 +1086,32 @@ impl OutboundPayments {
                                });
                        }
 
+                       if payment_is_probe || !is_retryable_now || !payment_retryable || retry.is_none() {
+                               let _ = payment.get_mut().mark_abandoned(); // we'll only Err if it's a legacy payment
+                               is_retryable_now = false;
+                       }
                        if payment.get().remaining_parts() == 0 {
                                all_paths_failed = true;
                                if payment.get().abandoned() {
-                                       full_failure_ev = Some(events::Event::PaymentFailed {
-                                               payment_id: *payment_id,
-                                               payment_hash: payment.get().payment_hash().expect("PendingOutboundPayments::RetriesExceeded always has a payment hash set"),
-                                       });
+                                       if !payment_is_probe {
+                                               full_failure_ev = Some(events::Event::PaymentFailed {
+                                                       payment_id: *payment_id,
+                                                       payment_hash: payment.get().payment_hash().expect("PendingOutboundPayments::RetriesExceeded always has a payment hash set"),
+                                               });
+                                       }
                                        payment.remove();
                                }
                        }
                        is_retryable_now
                } else {
                        log_trace!(logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
-                       return
+                       return false
                };
                core::mem::drop(outbounds);
                log_trace!(logger, "Failing outbound payment HTLC with payment_hash {}", log_bytes!(payment_hash.0));
 
                let path_failure = {
-                       if payment_is_probe(payment_hash, &payment_id, probing_cookie_secret) {
+                       if payment_is_probe {
                                if !payment_retryable {
                                        events::Event::ProbeSuccessful {
                                                payment_id: *payment_id,
@@ -1092,11 +1133,11 @@ impl OutboundPayments {
                                if let Some(scid) = short_channel_id {
                                        retry.as_mut().map(|r| r.payment_params.previously_failed_channels.push(scid));
                                }
-                               if payment_retryable && attempts_remaining && retry.is_some() {
+                               // If we miss abandoning the payment above, we *must* generate an event here or else the
+                               // payment will sit in our outbounds forever.
+                               if attempts_remaining && !already_awaiting_retry {
                                        debug_assert!(full_failure_ev.is_none());
-                                       pending_retry_ev = Some(events::Event::PendingHTLCsForwardable {
-                                               time_forwardable: Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS),
-                                       });
+                                       pending_retry_ev = true;
                                }
                                events::Event::PaymentPathFailed {
                                        payment_id: Some(*payment_id),
@@ -1117,16 +1158,17 @@ impl OutboundPayments {
                let mut pending_events = pending_events.lock().unwrap();
                pending_events.push(path_failure);
                if let Some(ev) = full_failure_ev { pending_events.push(ev); }
-               if let Some(ev) = pending_retry_ev { pending_events.push(ev); }
+               pending_retry_ev
        }
 
-       pub(super) fn abandon_payment(&self, payment_id: PaymentId) -> Option<events::Event> {
-               let mut failed_ev = None;
+       pub(super) fn abandon_payment(
+               &self, payment_id: PaymentId, pending_events: &Mutex<Vec<events::Event>>
+       ) {
                let mut outbounds = self.pending_outbound_payments.lock().unwrap();
                if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
                        if let Ok(()) = payment.get_mut().mark_abandoned() {
                                if payment.get().remaining_parts() == 0 {
-                                       failed_ev = Some(events::Event::PaymentFailed {
+                                       pending_events.lock().unwrap().push(events::Event::PaymentFailed {
                                                payment_id,
                                                payment_hash: payment.get().payment_hash().expect("PendingOutboundPayments::RetriesExceeded always has a payment hash set"),
                                        });
@@ -1134,7 +1176,6 @@ impl OutboundPayments {
                                }
                        }
                }
-               failed_ev
        }
 
        #[cfg(test)]
index 4f51d2f8114118328400dca853207669dafd6b94..9a957e37203799014c6853b6c9ffad93f360e37d 100644 (file)
@@ -40,63 +40,9 @@ use crate::routing::gossip::NodeId;
 #[cfg(feature = "std")]
 use {
        crate::util::time::tests::SinceEpoch,
-       std::time::{SystemTime, Duration}
+       std::time::{SystemTime, Instant, Duration}
 };
 
-#[test]
-fn retry_single_path_payment() {
-       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 mut nodes = create_network(3, &node_cfgs, &node_chanmgrs);
-
-       let _chan_0 = create_announced_chan_between_nodes(&nodes, 0, 1);
-       let chan_1 = create_announced_chan_between_nodes(&nodes, 2, 1);
-       // Rebalance to find a route
-       send_payment(&nodes[2], &vec!(&nodes[1])[..], 3_000_000);
-
-       let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], 100_000);
-
-       // Rebalance so that the first hop fails.
-       send_payment(&nodes[1], &vec!(&nodes[2])[..], 2_000_000);
-
-       // Make sure the payment fails on the first hop.
-       let payment_id = PaymentId(payment_hash.0);
-       nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret), 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);
-       let mut payment_event = SendEvent::from_event(events.pop().unwrap());
-       nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]);
-       check_added_monitors!(nodes[1], 0);
-       commitment_signed_dance!(nodes[1], nodes[0], payment_event.commitment_msg, false);
-       expect_pending_htlcs_forwardable!(nodes[1]);
-       expect_pending_htlcs_forwardable_and_htlc_handling_failed!(&nodes[1], vec![HTLCDestination::NextHopChannel { node_id: Some(nodes[2].node.get_our_node_id()), channel_id: chan_1.2 }]);
-       let htlc_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
-       assert!(htlc_updates.update_add_htlcs.is_empty());
-       assert_eq!(htlc_updates.update_fail_htlcs.len(), 1);
-       assert!(htlc_updates.update_fulfill_htlcs.is_empty());
-       assert!(htlc_updates.update_fail_malformed_htlcs.is_empty());
-       check_added_monitors!(nodes[1], 1);
-       nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &htlc_updates.update_fail_htlcs[0]);
-       commitment_signed_dance!(nodes[0], nodes[1], htlc_updates.commitment_signed, false);
-       expect_payment_failed_conditions(&nodes[0], payment_hash, false, PaymentFailedConditions::new().mpp_parts_remain());
-
-       // Rebalance the channel so the retry succeeds.
-       send_payment(&nodes[2], &vec!(&nodes[1])[..], 3_000_000);
-
-       // Mine two blocks (we expire retries after 3, so this will check that we don't expire early)
-       connect_blocks(&nodes[0], 2);
-
-       // Retry the payment and make sure it succeeds.
-       nodes[0].node.retry_payment(&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]], 100_000, payment_hash, Some(payment_secret), events.pop().unwrap(), true, None);
-       claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[2]]], false, payment_preimage);
-}
-
 #[test]
 fn mpp_failure() {
        let chanmon_cfgs = create_chanmon_cfgs(4);
@@ -136,7 +82,8 @@ fn mpp_retry() {
        // Rebalance
        send_payment(&nodes[3], &vec!(&nodes[2])[..], 1_500_000);
 
-       let (mut route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[3], 1_000_000);
+       let amt_msat = 1_000_000;
+       let (mut route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[3], amt_msat);
        let path = route.paths[0].clone();
        route.paths.push(path);
        route.paths[0][0].pubkey = nodes[1].node.get_our_node_id();
@@ -148,7 +95,14 @@ fn mpp_retry() {
 
        // Initiate the MPP payment.
        let payment_id = PaymentId(payment_hash.0);
-       nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret), payment_id).unwrap();
+       let mut route_params = RouteParameters {
+               payment_params: route.payment_params.clone().unwrap(),
+               final_value_msat: amt_msat,
+               final_cltv_expiry_delta: TEST_FINAL_CLTV,
+       };
+
+       nodes[0].router.expect_find_route(route_params.clone(), Ok(route.clone()));
+       nodes[0].node.send_payment_with_retry(payment_hash, &Some(payment_secret), payment_id, route_params.clone(), Retry::Attempts(1)).unwrap();
        check_added_monitors!(nodes[0], 2); // one monitor per path
        let mut events = nodes[0].node.get_and_clear_pending_msg_events();
        assert_eq!(events.len(), 2);
@@ -184,25 +138,23 @@ fn mpp_retry() {
        check_added_monitors!(nodes[2], 1);
        nodes[0].node.handle_update_fail_htlc(&nodes[2].node.get_our_node_id(), &htlc_updates.update_fail_htlcs[0]);
        commitment_signed_dance!(nodes[0], nodes[2], htlc_updates.commitment_signed, false);
-       expect_payment_failed_conditions(&nodes[0], payment_hash, false, PaymentFailedConditions::new().mpp_parts_remain());
+       let mut events = nodes[0].node.get_and_clear_pending_events();
+       match events[1] {
+               Event::PendingHTLCsForwardable { .. } => {},
+               _ => panic!("Unexpected event")
+       }
+       events.remove(1);
+       expect_payment_failed_conditions_event(events, payment_hash, false, PaymentFailedConditions::new().mpp_parts_remain());
 
        // Rebalance the channel so the second half of the payment can succeed.
        send_payment(&nodes[3], &vec!(&nodes[2])[..], 1_500_000);
 
-       // Make sure it errors as expected given a too-large amount.
-       if let Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError { err })) = nodes[0].node.retry_payment(&route, payment_id) {
-               assert!(err.contains("over total_payment_amt_msat"));
-       } else { panic!("Unexpected error"); }
-
-       // Make sure it errors as expected given the wrong payment_id.
-       if let Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError { err })) = nodes[0].node.retry_payment(&route, PaymentId([0; 32])) {
-               assert!(err.contains("not found"));
-       } else { panic!("Unexpected error"); }
-
        // Retry the second half of the payment and make sure it succeeds.
-       let mut path = route.clone();
-       path.paths.remove(0);
-       nodes[0].node.retry_payment(&path, payment_id).unwrap();
+       route.paths.remove(0);
+       route_params.final_value_msat = 1_000_000;
+       route_params.payment_params.previously_failed_channels.push(chan_4_update.contents.short_channel_id);
+       nodes[0].router.expect_find_route(route_params, Ok(route));
+       nodes[0].node.process_pending_htlc_forwards();
        check_added_monitors!(nodes[0], 1);
        let mut events = nodes[0].node.get_and_clear_pending_msg_events();
        assert_eq!(events.len(), 1);
@@ -284,55 +236,6 @@ fn mpp_receive_timeout() {
        do_mpp_receive_timeout(false);
 }
 
-#[test]
-fn retry_expired_payment() {
-       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 mut nodes = create_network(3, &node_cfgs, &node_chanmgrs);
-
-       let _chan_0 = create_announced_chan_between_nodes(&nodes, 0, 1);
-       let chan_1 = create_announced_chan_between_nodes(&nodes, 2, 1);
-       // Rebalance to find a route
-       send_payment(&nodes[2], &vec!(&nodes[1])[..], 3_000_000);
-
-       let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], 100_000);
-
-       // Rebalance so that the first hop fails.
-       send_payment(&nodes[1], &vec!(&nodes[2])[..], 2_000_000);
-
-       // Make sure the payment fails on the first hop.
-       nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret), PaymentId(payment_hash.0)).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 mut payment_event = SendEvent::from_event(events.pop().unwrap());
-       nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]);
-       check_added_monitors!(nodes[1], 0);
-       commitment_signed_dance!(nodes[1], nodes[0], payment_event.commitment_msg, false);
-       expect_pending_htlcs_forwardable!(nodes[1]);
-       expect_pending_htlcs_forwardable_and_htlc_handling_failed!(&nodes[1], vec![HTLCDestination::NextHopChannel { node_id: Some(nodes[2].node.get_our_node_id()), channel_id: chan_1.2 }]);
-       let htlc_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
-       assert!(htlc_updates.update_add_htlcs.is_empty());
-       assert_eq!(htlc_updates.update_fail_htlcs.len(), 1);
-       assert!(htlc_updates.update_fulfill_htlcs.is_empty());
-       assert!(htlc_updates.update_fail_malformed_htlcs.is_empty());
-       check_added_monitors!(nodes[1], 1);
-       nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &htlc_updates.update_fail_htlcs[0]);
-       commitment_signed_dance!(nodes[0], nodes[1], htlc_updates.commitment_signed, false);
-       expect_payment_failed!(nodes[0], payment_hash, false);
-
-       // Mine blocks so the payment will have expired.
-       connect_blocks(&nodes[0], 3);
-
-       // Retry the payment and make sure it errors as expected.
-       if let Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError { err })) = nodes[0].node.retry_payment(&route, PaymentId(payment_hash.0)) {
-               assert!(err.contains("not found"));
-       } else {
-               panic!("Unexpected error");
-       }
-}
-
 #[test]
 fn no_pending_leak_on_initial_send_failure() {
        // In an earlier version of our payment tracking, we'd have a retry entry even when the initial
@@ -388,9 +291,15 @@ fn do_retry_with_no_persist(confirm_before_reload: bool) {
 
        // 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 amt_msat = 1_000_000;
+       let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], amt_msat);
        let (payment_preimage_1, payment_hash_1, _, payment_id_1) = send_along_route(&nodes[0], route.clone(), &[&nodes[1], &nodes[2]], 1_000_000);
-       nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret), PaymentId(payment_hash.0)).unwrap();
+       let route_params = RouteParameters {
+               payment_params: route.payment_params.clone().unwrap(),
+               final_value_msat: amt_msat,
+               final_cltv_expiry_delta: TEST_FINAL_CLTV,
+       };
+       nodes[0].node.send_payment_with_retry(payment_hash, &Some(payment_secret), PaymentId(payment_hash.0), route_params, Retry::Attempts(1)).unwrap();
        check_added_monitors!(nodes[0], 1);
 
        let mut events = nodes[0].node.get_and_clear_pending_msg_events();
@@ -499,7 +408,7 @@ fn do_retry_with_no_persist(confirm_before_reload: bool) {
                confirm_transaction(&nodes[0], &first_htlc_timeout_tx);
        }
        nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().clear();
-       expect_payment_failed_conditions(&nodes[0], payment_hash, false, PaymentFailedConditions::new().mpp_parts_remain());
+       expect_payment_failed_conditions(&nodes[0], payment_hash, false, PaymentFailedConditions::new());
 
        // 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
@@ -525,8 +434,8 @@ fn do_retry_with_no_persist(confirm_before_reload: bool) {
                nodes[1].node.timer_tick_occurred();
        }
 
-       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, PaymentId(payment_hash.0)).unwrap();
+       assert!(nodes[0].node.send_payment(&new_route, payment_hash, &Some(payment_secret), payment_id_1).is_err()); // Shouldn't be allowed to retry a fulfilled payment
+       nodes[0].node.send_payment(&new_route, payment_hash, &Some(payment_secret), PaymentId(payment_hash.0)).unwrap();
        check_added_monitors!(nodes[0], 1);
        let mut events = nodes[0].node.get_and_clear_pending_msg_events();
        assert_eq!(events.len(), 1);
@@ -660,7 +569,10 @@ fn do_test_completed_payment_not_retryable_on_reload(use_dust: bool) {
        // If we attempt to retry prior to the HTLC-Timeout (or commitment transaction, for dust HTLCs)
        // confirming, we will fail as it's considered still-pending...
        let (new_route, _, _, _) = get_route_and_payment_hash!(nodes[0], nodes[2], if use_dust { 1_000 } else { 1_000_000 });
-       assert!(nodes[0].node.retry_payment(&new_route, payment_id).is_err());
+       match nodes[0].node.send_payment(&new_route, payment_hash, &Some(payment_secret), payment_id) {
+               Err(PaymentSendFailure::DuplicatePayment) => {},
+               _ => panic!("Unexpected error")
+       }
        assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
 
        // After ANTI_REORG_DELAY confirmations, the HTLC should be failed and we can try the payment
@@ -668,14 +580,14 @@ fn do_test_completed_payment_not_retryable_on_reload(use_dust: bool) {
        // (which should also still work).
        connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
        connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
-       // We set mpp_parts_remain to avoid having abandon_payment called
-       expect_payment_failed_conditions(&nodes[0], payment_hash, false, PaymentFailedConditions::new().mpp_parts_remain());
+       expect_payment_failed_conditions(&nodes[0], payment_hash, false, PaymentFailedConditions::new());
 
        let chan_0_monitor_serialized = get_monitor!(nodes[0], chan_id).encode();
        let chan_1_monitor_serialized = get_monitor!(nodes[0], chan_id_3).encode();
        nodes_0_serialized = nodes[0].node.encode();
 
-       assert!(nodes[0].node.retry_payment(&new_route, payment_id).is_ok());
+       // After the payment failed, we're free to send it again.
+       assert!(nodes[0].node.send_payment(&new_route, payment_hash, &Some(payment_secret), payment_id).is_ok());
        assert!(!nodes[0].node.get_and_clear_pending_msg_events().is_empty());
 
        reload_node!(nodes[0], test_default_channel_config(), nodes_0_serialized, &[&chan_0_monitor_serialized, &chan_1_monitor_serialized], second_persister, second_new_chain_monitor, second_nodes_0_deserialized);
@@ -685,25 +597,32 @@ fn do_test_completed_payment_not_retryable_on_reload(use_dust: bool) {
 
        // Now resend the payment, delivering the HTLC and actually claiming it this time. This ensures
        // the payment is not (spuriously) listed as still pending.
-       assert!(nodes[0].node.retry_payment(&new_route, payment_id).is_ok());
+       assert!(nodes[0].node.send_payment(&new_route, payment_hash, &Some(payment_secret), payment_id).is_ok());
        check_added_monitors!(nodes[0], 1);
        pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[2]]], if use_dust { 1_000 } else { 1_000_000 }, payment_hash, payment_secret);
        claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage);
 
-       assert!(nodes[0].node.retry_payment(&new_route, payment_id).is_err());
+       match nodes[0].node.send_payment(&new_route, payment_hash, &Some(payment_secret), payment_id) {
+               Err(PaymentSendFailure::DuplicatePayment) => {},
+               _ => panic!("Unexpected error")
+       }
        assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
 
        let chan_0_monitor_serialized = get_monitor!(nodes[0], chan_id).encode();
        let chan_1_monitor_serialized = get_monitor!(nodes[0], chan_id_3).encode();
        nodes_0_serialized = nodes[0].node.encode();
 
-       // Ensure that after reload we cannot retry the payment.
+       // Check that after reload we can send the payment again (though we shouldn't, since it was
+       // claimed previously).
        reload_node!(nodes[0], test_default_channel_config(), nodes_0_serialized, &[&chan_0_monitor_serialized, &chan_1_monitor_serialized], third_persister, third_new_chain_monitor, third_nodes_0_deserialized);
        nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id());
 
        reconnect_nodes(&nodes[0], &nodes[1], (false, false), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (false, false));
 
-       assert!(nodes[0].node.retry_payment(&new_route, payment_id).is_err());
+       match nodes[0].node.send_payment(&new_route, payment_hash, &Some(payment_secret), payment_id) {
+               Err(PaymentSendFailure::DuplicatePayment) => {},
+               _ => panic!("Unexpected error")
+       }
        assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
 }
 
@@ -1022,6 +941,7 @@ fn successful_probe_yields_event() {
                },
                _ => panic!(),
        };
+       assert!(!nodes[0].node.has_pending_payments());
 }
 
 #[test]
@@ -1067,6 +987,7 @@ fn failed_probe_yields_event() {
                },
                _ => panic!(),
        };
+       assert!(!nodes[0].node.has_pending_payments());
 }
 
 #[test]
@@ -1121,6 +1042,7 @@ fn onchain_failed_probe_yields_event() {
                }
        }
        assert!(found_probe_failed);
+       assert!(!nodes[0].node.has_pending_payments());
 }
 
 #[test]
@@ -1233,20 +1155,17 @@ fn abandoned_send_payment_idempotent() {
        nodes[1].node.fail_htlc_backwards(&first_payment_hash);
        expect_pending_htlcs_forwardable_and_htlc_handling_failed!(nodes[1], [HTLCDestination::FailedPayment { payment_hash: first_payment_hash }]);
 
-       pass_failed_payment_back_no_abandon(&nodes[0], &[&[&nodes[1]]], false, first_payment_hash);
-       check_send_rejected!();
-
-       // Until we abandon the payment, no matter how many timer ticks pass, we still cannot reuse the
+       // Until we abandon the payment upon path failure, no matter how many timer ticks pass, we still cannot reuse the
        // PaymentId.
        for _ in 0..=IDEMPOTENCY_TIMEOUT_TICKS {
                nodes[0].node.timer_tick_occurred();
        }
        check_send_rejected!();
 
-       nodes[0].node.abandon_payment(payment_id);
-       get_event!(nodes[0], Event::PaymentFailed);
+       pass_failed_payment_back(&nodes[0], &[&[&nodes[1]]], false, first_payment_hash);
 
-       // However, we can reuse the PaymentId immediately after we `abandon_payment`.
+       // However, we can reuse the PaymentId immediately after we `abandon_payment` upon passing the
+       // failed payment back.
        nodes[0].node.send_payment(&route, second_payment_hash, &Some(second_payment_secret), payment_id).unwrap();
        check_added_monitors!(nodes[0], 1);
        pass_along_route(&nodes[0], &[&[&nodes[1]]], 100_000, second_payment_hash, second_payment_secret);
@@ -1615,6 +1534,7 @@ enum AutoRetry {
        FailAttempts,
        FailTimeout,
        FailOnRestart,
+       FailOnRetry,
 }
 
 #[test]
@@ -1624,6 +1544,7 @@ fn automatic_retries() {
        do_automatic_retries(AutoRetry::FailAttempts);
        do_automatic_retries(AutoRetry::FailTimeout);
        do_automatic_retries(AutoRetry::FailOnRestart);
+       do_automatic_retries(AutoRetry::FailOnRetry);
 }
 fn do_automatic_retries(test: AutoRetry) {
        // Test basic automatic payment retries in ChannelManager. See individual `test` variant comments
@@ -1680,12 +1601,12 @@ fn do_automatic_retries(test: AutoRetry) {
                        check_added_monitors!(&nodes[1], 1);
                        assert!(update_1.update_fail_htlcs.len() == 1);
                        let fail_msg = update_1.update_fail_htlcs[0].clone();
-
                        nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &fail_msg);
                        commitment_signed_dance!(nodes[0], nodes[1], update_1.commitment_signed, false);
 
                        // Ensure the attempt fails and a new PendingHTLCsForwardable event is generated for the retry
                        let mut events = nodes[0].node.get_and_clear_pending_events();
+                       assert_eq!(events.len(), 2);
                        match events[0] {
                                Event::PaymentPathFailed { payment_hash: ev_payment_hash, payment_failed_permanently, ..  } => {
                                        assert_eq!(payment_hash, ev_payment_hash);
@@ -1694,12 +1615,18 @@ fn do_automatic_retries(test: AutoRetry) {
                                _ => panic!("Unexpected event"),
                        }
                        if $expect_pending_htlcs_forwardable {
-                               assert_eq!(events.len(), 2);
                                match events[1] {
                                        Event::PendingHTLCsForwardable { .. } => {},
                                        _ => panic!("Unexpected event"),
                                }
-                       } else { assert_eq!(events.len(), 1) }
+                       } else {
+                               match events[1] {
+                                       Event::PaymentFailed { payment_hash: ev_payment_hash, .. } => {
+                                               assert_eq!(payment_hash, ev_payment_hash);
+                                       },
+                                       _ => panic!("Unexpected event"),
+                               }
+                       }
                }
        }
 
@@ -1751,17 +1678,6 @@ fn do_automatic_retries(test: AutoRetry) {
                nodes[0].node.process_pending_htlc_forwards();
                let mut msg_events = nodes[0].node.get_and_clear_pending_msg_events();
                assert_eq!(msg_events.len(), 0);
-
-               nodes[0].node.abandon_payment(PaymentId(payment_hash.0));
-               let events = nodes[0].node.get_and_clear_pending_events();
-               assert_eq!(events.len(), 1);
-               match events[0] {
-                       Event::PaymentFailed { payment_hash: ref ev_payment_hash, payment_id: ref ev_payment_id } => {
-                               assert_eq!(payment_hash, *ev_payment_hash);
-                               assert_eq!(PaymentId(payment_hash.0), *ev_payment_id);
-                       },
-                       _ => panic!("Unexpected event"),
-               }
        } else if test == AutoRetry::FailTimeout {
                #[cfg(not(feature = "no-std"))] {
                        // Ensure ChannelManager will not retry a payment if it times out due to Retry::Timeout.
@@ -1776,7 +1692,6 @@ fn do_automatic_retries(test: AutoRetry) {
                        let mut msg_events = nodes[0].node.get_and_clear_pending_msg_events();
                        assert_eq!(msg_events.len(), 0);
 
-                       nodes[0].node.abandon_payment(PaymentId(payment_hash.0));
                        let mut events = nodes[0].node.get_and_clear_pending_events();
                        assert_eq!(events.len(), 1);
                        match events[0] {
@@ -1806,12 +1721,31 @@ fn do_automatic_retries(test: AutoRetry) {
                let chan_1_monitor_serialized = get_monitor!(nodes[0], channel_id_1).encode();
                reload_node!(nodes[0], node_encoded, &[&chan_1_monitor_serialized], persister, new_chain_monitor, node_0_deserialized);
 
+               let mut events = nodes[0].node.get_and_clear_pending_events();
+               expect_pending_htlcs_forwardable_from_events!(nodes[0], events, true);
                // Make sure we don't retry again.
+               let mut msg_events = nodes[0].node.get_and_clear_pending_msg_events();
+               assert_eq!(msg_events.len(), 0);
+
+               let mut events = nodes[0].node.get_and_clear_pending_events();
+               assert_eq!(events.len(), 1);
+               match events[0] {
+                       Event::PaymentFailed { payment_hash: ref ev_payment_hash, payment_id: ref ev_payment_id } => {
+                               assert_eq!(payment_hash, *ev_payment_hash);
+                               assert_eq!(PaymentId(payment_hash.0), *ev_payment_id);
+                       },
+                       _ => panic!("Unexpected event"),
+               }
+       } else if test == AutoRetry::FailOnRetry {
+               nodes[0].node.send_payment_with_retry(payment_hash, &Some(payment_secret), PaymentId(payment_hash.0), route_params, Retry::Attempts(1)).unwrap();
+               pass_failed_attempt_with_retry_along_path!(channel_id_2, true);
+
+               // We retry payments in `process_pending_htlc_forwards`. Since our channel closed, we should
+               // fail to find a route.
                nodes[0].node.process_pending_htlc_forwards();
                let mut msg_events = nodes[0].node.get_and_clear_pending_msg_events();
                assert_eq!(msg_events.len(), 0);
 
-               nodes[0].node.abandon_payment(PaymentId(payment_hash.0));
                let mut events = nodes[0].node.get_and_clear_pending_events();
                assert_eq!(events.len(), 1);
                match events[0] {
@@ -2413,9 +2347,9 @@ fn no_extra_retries_on_back_to_back_fail() {
        // by adding the `PaymentFailed` event.
        //
        // Because we now retry payments as a batch, we simply return a single-path route in the
-       // second, batched, request, have that fail, then complete the payment via `abandon_payment`.
+       // second, batched, request, have that fail, ensure the payment was abandoned.
        let mut events = nodes[0].node.get_and_clear_pending_events();
-       assert_eq!(events.len(), 4);
+       assert_eq!(events.len(), 3);
        match events[0] {
                Event::PaymentPathFailed { payment_hash: ev_payment_hash, payment_failed_permanently, ..  } => {
                        assert_eq!(payment_hash, ev_payment_hash);
@@ -2434,10 +2368,6 @@ fn no_extra_retries_on_back_to_back_fail() {
                },
                _ => panic!("Unexpected event"),
        }
-       match events[3] {
-               Event::PendingHTLCsForwardable { .. } => {},
-               _ => panic!("Unexpected event"),
-       }
 
        nodes[0].node.process_pending_htlc_forwards();
        let retry_htlc_updates = SendEvent::from_node(&nodes[0]);
@@ -2450,7 +2380,7 @@ fn no_extra_retries_on_back_to_back_fail() {
        commitment_signed_dance!(nodes[0], nodes[1], &bs_fail_update.commitment_signed, false, true);
 
        let mut events = nodes[0].node.get_and_clear_pending_events();
-       assert_eq!(events.len(), 1);
+       assert_eq!(events.len(), 2);
        match events[0] {
                Event::PaymentPathFailed { payment_hash: ev_payment_hash, payment_failed_permanently, ..  } => {
                        assert_eq!(payment_hash, ev_payment_hash);
@@ -2458,10 +2388,7 @@ fn no_extra_retries_on_back_to_back_fail() {
                },
                _ => panic!("Unexpected event"),
        }
-       nodes[0].node.abandon_payment(PaymentId(payment_hash.0));
-       events = nodes[0].node.get_and_clear_pending_events();
-       assert_eq!(events.len(), 1);
-       match events[0] {
+       match events[1] {
                Event::PaymentFailed { payment_hash: ref ev_payment_hash, payment_id: ref ev_payment_id } => {
                        assert_eq!(payment_hash, *ev_payment_hash);
                        assert_eq!(PaymentId(payment_hash.0), *ev_payment_id);
@@ -2627,3 +2554,165 @@ fn test_simple_partial_retry() {
        expect_pending_htlcs_forwardable!(nodes[2]);
        expect_payment_claimable!(nodes[2], payment_hash, payment_secret, amt_msat);
 }
+
+#[test]
+#[cfg(feature = "std")]
+fn test_threaded_payment_retries() {
+       // In the first version of the in-`ChannelManager` payment retries, retries weren't limited to
+       // a single thread and would happily let multiple threads run retries at the same time. Because
+       // retries are done by first calculating the amount we need to retry, then dropping the
+       // relevant lock, then actually sending, we would happily let multiple threads retry the same
+       // amount at the same time, overpaying our original HTLC!
+       let chanmon_cfgs = create_chanmon_cfgs(4);
+       let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
+       let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]);
+       let nodes = create_network(4, &node_cfgs, &node_chanmgrs);
+
+       // There is one mitigating guardrail when retrying payments - we can never over-pay by more
+       // than 10% of the original value. Thus, we want all our retries to be below that. In order to
+       // keep things simple, we route one HTLC for 0.1% of the payment over channel 1 and the rest
+       // out over channel 3+4. This will let us ignore 99% of the payment value and deal with only
+       // our channel.
+       let chan_1_scid = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 0).0.contents.short_channel_id;
+       create_announced_chan_between_nodes_with_value(&nodes, 1, 3, 10_000_000, 0);
+       let chan_3_scid = create_announced_chan_between_nodes_with_value(&nodes, 0, 2, 10_000_000, 0).0.contents.short_channel_id;
+       let chan_4_scid = create_announced_chan_between_nodes_with_value(&nodes, 2, 3, 10_000_000, 0).0.contents.short_channel_id;
+
+       let amt_msat = 100_000_000;
+       let (_, payment_hash, _, payment_secret) = get_route_and_payment_hash!(&nodes[0], nodes[2], amt_msat);
+       #[cfg(feature = "std")]
+       let payment_expiry_secs = SystemTime::UNIX_EPOCH.elapsed().unwrap().as_secs() + 60 * 60;
+       #[cfg(not(feature = "std"))]
+       let payment_expiry_secs = 60 * 60;
+       let mut invoice_features = InvoiceFeatures::empty();
+       invoice_features.set_variable_length_onion_required();
+       invoice_features.set_payment_secret_required();
+       invoice_features.set_basic_mpp_optional();
+       let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
+               .with_expiry_time(payment_expiry_secs as u64)
+               .with_features(invoice_features);
+       let mut route_params = RouteParameters {
+               payment_params,
+               final_value_msat: amt_msat,
+               final_cltv_expiry_delta: TEST_FINAL_CLTV,
+       };
+
+       let mut route = Route {
+               paths: vec![
+                       vec![RouteHop {
+                               pubkey: nodes[1].node.get_our_node_id(),
+                               node_features: nodes[1].node.node_features(),
+                               short_channel_id: chan_1_scid,
+                               channel_features: nodes[1].node.channel_features(),
+                               fee_msat: 0,
+                               cltv_expiry_delta: 100,
+                       }, RouteHop {
+                               pubkey: nodes[3].node.get_our_node_id(),
+                               node_features: nodes[2].node.node_features(),
+                               short_channel_id: 42, // Set a random SCID which nodes[1] will fail as unknown
+                               channel_features: nodes[2].node.channel_features(),
+                               fee_msat: amt_msat / 1000,
+                               cltv_expiry_delta: 100,
+                       }],
+                       vec![RouteHop {
+                               pubkey: nodes[2].node.get_our_node_id(),
+                               node_features: nodes[2].node.node_features(),
+                               short_channel_id: chan_3_scid,
+                               channel_features: nodes[2].node.channel_features(),
+                               fee_msat: 100_000,
+                               cltv_expiry_delta: 100,
+                       }, RouteHop {
+                               pubkey: nodes[3].node.get_our_node_id(),
+                               node_features: nodes[3].node.node_features(),
+                               short_channel_id: chan_4_scid,
+                               channel_features: nodes[3].node.channel_features(),
+                               fee_msat: amt_msat - amt_msat / 1000,
+                               cltv_expiry_delta: 100,
+                       }]
+               ],
+               payment_params: Some(PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)),
+       };
+       nodes[0].router.expect_find_route(route_params.clone(), Ok(route.clone()));
+
+       nodes[0].node.send_payment_with_retry(payment_hash, &Some(payment_secret), PaymentId(payment_hash.0), route_params.clone(), Retry::Attempts(0xdeadbeef)).unwrap();
+       check_added_monitors!(nodes[0], 2);
+       let mut send_msg_events = nodes[0].node.get_and_clear_pending_msg_events();
+       assert_eq!(send_msg_events.len(), 2);
+       send_msg_events.retain(|msg|
+               if let MessageSendEvent::UpdateHTLCs { node_id, .. } = msg {
+                       // Drop the commitment update for nodes[2], we can just let that one sit pending
+                       // forever.
+                       *node_id == nodes[1].node.get_our_node_id()
+               } else { panic!(); }
+       );
+
+       // from here on out, the retry `RouteParameters` amount will be amt/1000
+       route_params.final_value_msat /= 1000;
+       route.paths.pop();
+
+       let end_time = Instant::now() + Duration::from_secs(1);
+       macro_rules! thread_body { () => { {
+               // We really want std::thread::scope, but its not stable until 1.63. Until then, we get unsafe.
+               let node_ref = NodePtr::from_node(&nodes[0]);
+               move || {
+                       let node_a = unsafe { &*node_ref.0 };
+                       while Instant::now() < end_time {
+                               node_a.node.get_and_clear_pending_events(); // wipe the PendingHTLCsForwardable
+                               // Ignore if we have any pending events, just always pretend we just got a
+                               // PendingHTLCsForwardable
+                               node_a.node.process_pending_htlc_forwards();
+                       }
+               }
+       } } }
+       let mut threads = Vec::new();
+       for _ in 0..16 { threads.push(std::thread::spawn(thread_body!())); }
+
+       // Back in the main thread, poll pending messages and make sure that we never have more than
+       // one HTLC pending at a time. Note that the commitment_signed_dance will fail horribly if
+       // there are HTLC messages shoved in while its running. This allows us to test that we never
+       // generate an additional update_add_htlc until we've fully failed the first.
+       let mut previously_failed_channels = Vec::new();
+       loop {
+               assert_eq!(send_msg_events.len(), 1);
+               let send_event = SendEvent::from_event(send_msg_events.pop().unwrap());
+               assert_eq!(send_event.msgs.len(), 1);
+
+               nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &send_event.msgs[0]);
+               commitment_signed_dance!(nodes[1], nodes[0], send_event.commitment_msg, false, true);
+
+               // Note that we only push one route into `expect_find_route` at a time, because that's all
+               // the retries (should) need. If the bug is reintroduced "real" routes may be selected, but
+               // we should still ultimately fail for the same reason - because we're trying to send too
+               // many HTLCs at once.
+               let mut new_route_params = route_params.clone();
+               previously_failed_channels.push(route.paths[0][1].short_channel_id);
+               new_route_params.payment_params.previously_failed_channels = previously_failed_channels.clone();
+               route.paths[0][1].short_channel_id += 1;
+               nodes[0].router.expect_find_route(new_route_params, Ok(route.clone()));
+
+               let bs_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(), &bs_fail_updates.update_fail_htlcs[0]);
+               // The "normal" commitment_signed_dance delivers the final RAA and then calls
+               // `check_added_monitors` to ensure only the one RAA-generated monitor update was created.
+               // This races with our other threads which may generate an add-HTLCs commitment update via
+               // `process_pending_htlc_forwards`. Instead, we defer the monitor update check until after
+               // *we've* called `process_pending_htlc_forwards` when its guaranteed to have two updates.
+               let last_raa = commitment_signed_dance!(nodes[0], nodes[1], bs_fail_updates.commitment_signed, false, true, false, true);
+               nodes[0].node.handle_revoke_and_ack(&nodes[1].node.get_our_node_id(), &last_raa);
+
+               let cur_time = Instant::now();
+               if cur_time > end_time {
+                       for thread in threads.drain(..) { thread.join().unwrap(); }
+               }
+
+               // Make sure we have some events to handle when we go around...
+               nodes[0].node.get_and_clear_pending_events(); // wipe the PendingHTLCsForwardable
+               nodes[0].node.process_pending_htlc_forwards();
+               send_msg_events = nodes[0].node.get_and_clear_pending_msg_events();
+               check_added_monitors!(nodes[0], 2);
+
+               if cur_time > end_time {
+                       break;
+               }
+       }
+}
index 995901a3b54f7355bf50d430992b06da904f70c8..814bf304d8d4eb0e6aebd67f08ac9b7015b931ae 100644 (file)
@@ -46,16 +46,23 @@ use bitcoin::hashes::sha256::Hash as Sha256;
 use bitcoin::hashes::sha256::HashEngine as Sha256Engine;
 use bitcoin::hashes::{HashEngine, Hash};
 
-/// Handler for BOLT1-compliant messages.
+/// A handler provided to [`PeerManager`] for reading and handling custom messages.
+///
+/// [BOLT 1] specifies a custom message type range for use with experimental or application-specific
+/// messages. `CustomMessageHandler` allows for user-defined handling of such types. See the
+/// [`lightning_custom_message`] crate for tools useful in composing more than one custom handler.
+///
+/// [BOLT 1]: https://github.com/lightning/bolts/blob/master/01-messaging.md
+/// [`lightning_custom_message`]: https://docs.rs/lightning_custom_message/latest/lightning_custom_message
 pub trait CustomMessageHandler: wire::CustomMessageReader {
-       /// Called with the message type that was received and the buffer to be read.
-       /// Can return a `MessageHandlingError` if the message could not be handled.
+       /// Handles the given message sent from `sender_node_id`, possibly producing messages for
+       /// [`CustomMessageHandler::get_and_clear_pending_msg`] to return and thus for [`PeerManager`]
+       /// to send.
        fn handle_custom_message(&self, msg: Self::CustomMessage, sender_node_id: &PublicKey) -> Result<(), LightningError>;
 
-       /// Gets the list of pending messages which were generated by the custom message
-       /// handler, clearing the list in the process. The first tuple element must
-       /// correspond to the intended recipients node ids. If no connection to one of the
-       /// specified node does not exist, the message is simply not sent to it.
+       /// Returns the list of pending messages that were generated by the handler, clearing the list
+       /// in the process. Each message is paired with the node id of the intended recipient. If no
+       /// connection to the node exists, then the message is simply not sent.
        fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)>;
 }
 
index b174e52290b77c8abf221244c64f791809908820..2c71e9c3fb34e2d15d13933dbf0e6b37a17f570a 100644 (file)
@@ -43,15 +43,14 @@ use crate::prelude::*;
 /// # extern crate bitcoin;
 /// # use bitcoin::hashes::_export::_core::time::Duration;
 /// # use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
-/// # use lightning::chain::keysinterface::{InMemorySigner, KeysManager};
-/// # use lightning::ln::msgs::DecodeError;
+/// # use lightning::chain::keysinterface::KeysManager;
 /// # use lightning::ln::peer_handler::IgnoringMessageHandler;
 /// # use lightning::onion_message::{BlindedPath, CustomOnionMessageContents, Destination, OnionMessageContents, OnionMessenger};
 /// # use lightning::util::logger::{Logger, Record};
 /// # use lightning::util::ser::{Writeable, Writer};
 /// # use lightning::io;
 /// # use std::sync::Arc;
-/// # struct FakeLogger {};
+/// # struct FakeLogger;
 /// # impl Logger for FakeLogger {
 /// #     fn log(&self, record: &Record) { unimplemented!() }
 /// # }
@@ -67,7 +66,7 @@ use crate::prelude::*;
 /// # let your_custom_message_handler = IgnoringMessageHandler {};
 /// // Create the onion messenger. This must use the same `keys_manager` as is passed to your
 /// // ChannelManager.
-/// let onion_messenger = OnionMessenger::new(&keys_manager, &keys_manager, logger, your_custom_message_handler);
+/// let onion_messenger = OnionMessenger::new(&keys_manager, &keys_manager, logger, &your_custom_message_handler);
 ///
 /// # struct YourCustomMessage {}
 /// impl Writeable for YourCustomMessage {
index 6c2d70bd6f309ed429af45876666673fc6421a35..82d2cd4cb981fa4e8d532fabdbc8c0b60ad62897 100644 (file)
@@ -390,7 +390,7 @@ where U::Target: UtxoLookup, L::Target: Logger
        }
 
        fn get_next_channel_announcement(&self, starting_point: u64) -> Option<(ChannelAnnouncement, Option<ChannelUpdate>, Option<ChannelUpdate>)> {
-               let channels = self.network_graph.channels.read().unwrap();
+               let mut channels = self.network_graph.channels.write().unwrap();
                for (_, ref chan) in channels.range(starting_point..) {
                        if chan.announcement_message.is_some() {
                                let chan_announcement = chan.announcement_message.clone().unwrap();
@@ -412,7 +412,7 @@ where U::Target: UtxoLookup, L::Target: Logger
        }
 
        fn get_next_node_announcement(&self, starting_point: Option<&NodeId>) -> Option<NodeAnnouncement> {
-               let nodes = self.network_graph.nodes.read().unwrap();
+               let mut nodes = self.network_graph.nodes.write().unwrap();
                let iter = if let Some(node_id) = starting_point {
                                nodes.range((Bound::Excluded(node_id), Bound::Unbounded))
                        } else {
@@ -572,7 +572,7 @@ where U::Target: UtxoLookup, L::Target: Logger
                // (has at least one update). A peer may still want to know the channel
                // exists even if its not yet routable.
                let mut batches: Vec<Vec<u64>> = vec![Vec::with_capacity(MAX_SCIDS_PER_REPLY)];
-               let channels = self.network_graph.channels.read().unwrap();
+               let mut channels = self.network_graph.channels.write().unwrap();
                for (_, ref chan) in channels.range(inclusive_start_scid.unwrap()..exclusive_end_scid.unwrap()) {
                        if let Some(chan_announcement) = &chan.announcement_message {
                                // Construct a new batch if last one is full
index cfee77a67a2e89ece33616bc5a285a721182bdae..b4e2b138433648d9218ce0e8977cb85f4d9197a8 100644 (file)
@@ -333,7 +333,7 @@ impl Readable for Route {
 /// Parameters needed to find a [`Route`].
 ///
 /// Passed to [`find_route`] and [`build_route_from_hops`], but also provided in
-/// [`Event::PaymentPathFailed`] for retrying a failed payment path.
+/// [`Event::PaymentPathFailed`].
 ///
 /// [`Event::PaymentPathFailed`]: crate::util::events::Event::PaymentPathFailed
 #[derive(Clone, Debug, PartialEq, Eq)]
index 9f7caa2c1804350818a5804f49fb0896c6d33f0f..5631093723733f16f4d7447c0865f58219d1cf40 100644 (file)
@@ -14,6 +14,8 @@ use std::sync::Condvar as StdCondvar;
 
 use crate::prelude::HashMap;
 
+use super::{LockTestExt, LockHeldState};
+
 #[cfg(feature = "backtrace")]
 use {crate::prelude::hash_map, backtrace::Backtrace, std::sync::Once};
 
@@ -168,6 +170,18 @@ impl LockMetadata {
        fn pre_lock(this: &Arc<LockMetadata>) { Self::_pre_lock(this, false); }
        fn pre_read_lock(this: &Arc<LockMetadata>) -> bool { Self::_pre_lock(this, true) }
 
+       fn held_by_thread(this: &Arc<LockMetadata>) -> LockHeldState {
+               let mut res = LockHeldState::NotHeldByThread;
+               LOCKS_HELD.with(|held| {
+                       for (locked_idx, _locked) in held.borrow().iter() {
+                               if *locked_idx == this.lock_idx {
+                                       res = LockHeldState::HeldByThread;
+                               }
+                       }
+               });
+               res
+       }
+
        fn try_locked(this: &Arc<LockMetadata>) {
                LOCKS_HELD.with(|held| {
                        // Since a try-lock will simply fail if the lock is held already, we do not
@@ -248,6 +262,13 @@ impl<T> Mutex<T> {
        }
 }
 
+impl <T> LockTestExt for Mutex<T> {
+       #[inline]
+       fn held_by_thread(&self) -> LockHeldState {
+               LockMetadata::held_by_thread(&self.deps)
+       }
+}
+
 pub struct RwLock<T: Sized> {
        inner: StdRwLock<T>,
        deps: Arc<LockMetadata>,
@@ -332,4 +353,11 @@ impl<T> RwLock<T> {
        }
 }
 
+impl <T> LockTestExt for RwLock<T> {
+       #[inline]
+       fn held_by_thread(&self) -> LockHeldState {
+               LockMetadata::held_by_thread(&self.deps)
+       }
+}
+
 pub type FairRwLock<T> = RwLock<T>;
diff --git a/lightning/src/sync/fairrwlock.rs b/lightning/src/sync/fairrwlock.rs
new file mode 100644 (file)
index 0000000..a9519ac
--- /dev/null
@@ -0,0 +1,59 @@
+use std::sync::{LockResult, RwLock, RwLockReadGuard, RwLockWriteGuard, TryLockResult};
+use std::sync::atomic::{AtomicUsize, Ordering};
+use super::{LockHeldState, LockTestExt};
+
+/// Rust libstd's RwLock does not provide any fairness guarantees (and, in fact, when used on
+/// Linux with pthreads under the hood, readers trivially and completely starve writers).
+/// Because we often hold read locks while doing message processing in multiple threads which
+/// can use significant CPU time, with write locks being time-sensitive but relatively small in
+/// CPU time, we can end up with starvation completely blocking incoming connections or pings,
+/// especially during initial graph sync.
+///
+/// Thus, we need to block readers when a writer is pending, which we do with a trivial RwLock
+/// wrapper here. Its not particularly optimized, but provides some reasonable fairness by
+/// blocking readers (by taking the write lock) if there are writers pending when we go to take
+/// a read lock.
+pub struct FairRwLock<T> {
+       lock: RwLock<T>,
+       waiting_writers: AtomicUsize,
+}
+
+impl<T> FairRwLock<T> {
+       pub fn new(t: T) -> Self {
+               Self { lock: RwLock::new(t), waiting_writers: AtomicUsize::new(0) }
+       }
+
+       // Note that all atomic accesses are relaxed, as we do not rely on the atomics here for any
+       // ordering at all, instead relying on the underlying RwLock to provide ordering of unrelated
+       // memory.
+       pub fn write(&self) -> LockResult<RwLockWriteGuard<T>> {
+               self.waiting_writers.fetch_add(1, Ordering::Relaxed);
+               let res = self.lock.write();
+               self.waiting_writers.fetch_sub(1, Ordering::Relaxed);
+               res
+       }
+
+       pub fn read(&self) -> LockResult<RwLockReadGuard<T>> {
+               if self.waiting_writers.load(Ordering::Relaxed) != 0 {
+                       let _write_queue_lock = self.lock.write();
+               }
+               // Note that we don't consider ensuring that an underlying RwLock allowing writers to
+               // starve readers doesn't exhibit the same behavior here. I'm not aware of any
+               // libstd-backing RwLock which exhibits this behavior, and as documented in the
+               // struct-level documentation, it shouldn't pose a significant issue for our current
+               // codebase.
+               self.lock.read()
+       }
+
+       pub fn try_write(&self) -> TryLockResult<RwLockWriteGuard<'_, T>> {
+               self.lock.try_write()
+       }
+}
+
+impl<T> LockTestExt for FairRwLock<T> {
+       #[inline]
+       fn held_by_thread(&self) -> LockHeldState {
+               // fairrwlock is only built in non-test modes, so we should never support tests.
+               LockHeldState::Unsupported
+       }
+}
index f7226a5fa34eef114d27c6569207dbc68354b950..50ef40e295f50d0a0d9095a0f3dfe4c73916a340 100644 (file)
@@ -1,3 +1,16 @@
+#[allow(dead_code)] // Depending on the compilation flags some variants are never used
+#[derive(Debug, PartialEq, Eq)]
+pub(crate) enum LockHeldState {
+       HeldByThread,
+       NotHeldByThread,
+       #[cfg(any(feature = "_bench_unstable", not(test)))]
+       Unsupported,
+}
+
+pub(crate) trait LockTestExt {
+       fn held_by_thread(&self) -> LockHeldState;
+}
+
 #[cfg(all(feature = "std", not(feature = "_bench_unstable"), test))]
 mod debug_sync;
 #[cfg(all(feature = "std", not(feature = "_bench_unstable"), test))]
@@ -7,9 +20,22 @@ pub use debug_sync::*;
 mod test_lockorder_checks;
 
 #[cfg(all(feature = "std", any(feature = "_bench_unstable", not(test))))]
-pub use ::std::sync::{Arc, Mutex, Condvar, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};
+pub(crate) mod fairrwlock;
+#[cfg(all(feature = "std", any(feature = "_bench_unstable", not(test))))]
+pub use {std::sync::{Arc, Mutex, Condvar, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard}, fairrwlock::FairRwLock};
+
 #[cfg(all(feature = "std", any(feature = "_bench_unstable", not(test))))]
-pub use crate::util::fairrwlock::FairRwLock;
+mod ext_impl {
+       use super::*;
+       impl<T> LockTestExt for Mutex<T> {
+               #[inline]
+               fn held_by_thread(&self) -> LockHeldState { LockHeldState::Unsupported }
+       }
+       impl<T> LockTestExt for RwLock<T> {
+               #[inline]
+               fn held_by_thread(&self) -> LockHeldState { LockHeldState::Unsupported }
+       }
+}
 
 #[cfg(not(feature = "std"))]
 mod nostd_sync;
index caf88a7cc04a8617a86e63988a7cc7a291fe96bc..e17aa6ab15faa5dc33b66dbc1b80ab79fd36cb18 100644 (file)
@@ -2,6 +2,7 @@ pub use ::alloc::sync::Arc;
 use core::ops::{Deref, DerefMut};
 use core::time::Duration;
 use core::cell::{RefCell, Ref, RefMut};
+use super::{LockTestExt, LockHeldState};
 
 pub type LockResult<Guard> = Result<Guard, ()>;
 
@@ -61,6 +62,14 @@ impl<T> Mutex<T> {
        }
 }
 
+impl<T> LockTestExt for Mutex<T> {
+       #[inline]
+       fn held_by_thread(&self) -> LockHeldState {
+               if self.lock().is_err() { return LockHeldState::HeldByThread; }
+               else { return LockHeldState::NotHeldByThread; }
+       }
+}
+
 pub struct RwLock<T: ?Sized> {
        inner: RefCell<T>
 }
@@ -116,4 +125,12 @@ impl<T> RwLock<T> {
        }
 }
 
+impl<T> LockTestExt for RwLock<T> {
+       #[inline]
+       fn held_by_thread(&self) -> LockHeldState {
+               if self.write().is_err() { return LockHeldState::HeldByThread; }
+               else { return LockHeldState::NotHeldByThread; }
+       }
+}
+
 pub type FairRwLock<T> = RwLock<T>;
index f9f30e2cfa28efb4d42960d3fb4c1afc713b8933..a3f746b11dc80dd32bd7badf5f9786acf6426a89 100644 (file)
@@ -1,5 +1,10 @@
 use crate::sync::debug_sync::{RwLock, Mutex};
 
+use super::{LockHeldState, LockTestExt};
+
+use std::sync::Arc;
+use std::thread;
+
 #[test]
 #[should_panic]
 #[cfg(not(feature = "backtrace"))]
@@ -92,3 +97,22 @@ fn read_write_lockorder_fail() {
                let _a = a.write().unwrap();
        }
 }
+
+#[test]
+fn test_thread_locked_state() {
+       let mtx = Arc::new(Mutex::new(()));
+       let mtx_ref = Arc::clone(&mtx);
+       assert_eq!(mtx.held_by_thread(), LockHeldState::NotHeldByThread);
+
+       let lck = mtx.lock().unwrap();
+       assert_eq!(mtx.held_by_thread(), LockHeldState::HeldByThread);
+
+       let thrd = std::thread::spawn(move || {
+               assert_eq!(mtx_ref.held_by_thread(), LockHeldState::NotHeldByThread);
+       });
+       thrd.join().unwrap();
+       assert_eq!(mtx.held_by_thread(), LockHeldState::HeldByThread);
+
+       std::mem::drop(lck);
+       assert_eq!(mtx.held_by_thread(), LockHeldState::NotHeldByThread);
+}
index 265e918661168c8d3c9ab24372439471597849e9..4d68d01f6ed9301661145b222cf5b0aea2dba870 100644 (file)
@@ -565,11 +565,9 @@ pub enum Event {
        /// 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`].
+               /// The id returned by [`ChannelManager::send_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
@@ -594,16 +592,16 @@ pub enum Event {
        /// provide failure information for each MPP part in the payment.
        ///
        /// This event is provided once there are no further pending HTLCs for the payment and the
-       /// payment is no longer retryable due to [`ChannelManager::abandon_payment`] having been
-       /// called for the corresponding payment.
+       /// payment is no longer retryable, due either to the [`Retry`] provided or
+       /// [`ChannelManager::abandon_payment`] having been called for the corresponding payment.
        ///
+       /// [`Retry`]: crate::ln::channelmanager::Retry
        /// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment
        PaymentFailed {
                /// The id returned by [`ChannelManager::send_payment`] and used with
-               /// [`ChannelManager::retry_payment`] and [`ChannelManager::abandon_payment`].
+               /// [`ChannelManager::abandon_payment`].
                ///
                /// [`ChannelManager::send_payment`]: crate::ln::channelmanager::ChannelManager::send_payment
-               /// [`ChannelManager::retry_payment`]: crate::ln::channelmanager::ChannelManager::retry_payment
                /// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment
                payment_id: PaymentId,
                /// The hash that was given to [`ChannelManager::send_payment`].
@@ -616,11 +614,9 @@ pub enum Event {
        /// Always generated after [`Event::PaymentSent`] and thus useful for scoring channels. See
        /// [`Event::PaymentSent`] for obtaining the payment preimage.
        PaymentPathSuccessful {
-               /// The id returned by [`ChannelManager::send_payment`] and used with
-               /// [`ChannelManager::retry_payment`].
+               /// The id returned by [`ChannelManager::send_payment`].
                ///
                /// [`ChannelManager::send_payment`]: crate::ln::channelmanager::ChannelManager::send_payment
-               /// [`ChannelManager::retry_payment`]: crate::ln::channelmanager::ChannelManager::retry_payment
                payment_id: PaymentId,
                /// The hash that was given to [`ChannelManager::send_payment`].
                ///
@@ -631,24 +627,22 @@ pub enum Event {
                /// May contain a closed channel if the HTLC sent along the path was fulfilled on chain.
                path: Vec<RouteHop>,
        },
-       /// Indicates an outbound HTLC we sent failed. Probably some intermediary node dropped
-       /// something. You may wish to retry with a different route.
-       ///
-       /// If you have given up retrying this payment and wish to fail it, you MUST call
-       /// [`ChannelManager::abandon_payment`] at least once for a given [`PaymentId`] or memory
-       /// related to payment tracking will leak.
+       /// Indicates an outbound HTLC we sent failed, likely due to an intermediary node being unable to
+       /// handle the HTLC.
        ///
        /// Note that this does *not* indicate that all paths for an MPP payment have failed, see
        /// [`Event::PaymentFailed`] and [`all_paths_failed`].
        ///
+       /// See [`ChannelManager::abandon_payment`] for giving up on this payment before its retries have
+       /// been exhausted.
+       ///
        /// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment
        /// [`all_paths_failed`]: Self::PaymentPathFailed::all_paths_failed
        PaymentPathFailed {
                /// The id returned by [`ChannelManager::send_payment`] and used with
-               /// [`ChannelManager::retry_payment`] and [`ChannelManager::abandon_payment`].
+               /// [`ChannelManager::abandon_payment`].
                ///
                /// [`ChannelManager::send_payment`]: crate::ln::channelmanager::ChannelManager::send_payment
-               /// [`ChannelManager::retry_payment`]: crate::ln::channelmanager::ChannelManager::retry_payment
                /// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment
                payment_id: Option<PaymentId>,
                /// The hash that was given to [`ChannelManager::send_payment`].
@@ -656,8 +650,8 @@ pub enum Event {
                /// [`ChannelManager::send_payment`]: crate::ln::channelmanager::ChannelManager::send_payment
                payment_hash: PaymentHash,
                /// Indicates the payment was rejected for some reason by the recipient. This implies that
-               /// the payment has failed, not just the route in question. If this is not set, you may
-               /// retry the payment via a different route.
+               /// the payment has failed, not just the route in question. If this is not set, the payment may
+               /// be retried via a different route.
                payment_failed_permanently: bool,
                /// Any failure information conveyed via the Onion return packet by a node along the failed
                /// payment route.
@@ -670,20 +664,6 @@ pub enum Event {
                /// For both single-path and multi-path payments, this is set if all paths of the payment have
                /// failed. This will be set to false if (1) this is an MPP payment and (2) other parts of the
                /// larger MPP payment were still in flight when this event was generated.
-               ///
-               /// Note that if you are retrying individual MPP parts, using this value to determine if a
-               /// payment has fully failed is race-y. Because multiple failures can happen prior to events
-               /// being processed, you may retry in response to a first failure, with a second failure
-               /// (with `all_paths_failed` set) still pending. Then, when the second failure is processed
-               /// you will see `all_paths_failed` set even though the retry of the first failure still
-               /// has an associated in-flight HTLC. See (1) for an example of such a failure.
-               ///
-               /// If you wish to retry individual MPP parts and learn when a payment has failed, you must
-               /// call [`ChannelManager::abandon_payment`] and wait for a [`Event::PaymentFailed`] event.
-               ///
-               /// (1) <https://github.com/lightningdevkit/rust-lightning/issues/1164>
-               ///
-               /// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment
                all_paths_failed: bool,
                /// The payment path that failed.
                path: Vec<RouteHop>,
@@ -696,12 +676,9 @@ pub enum Event {
                /// 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.
+               /// Parameters used by LDK to compute a new [`Route`] when retrying the failed payment path.
                ///
                /// [`Route`]: crate::routing::router::Route
-               /// [`find_route`]: crate::routing::router::find_route
                retry: Option<RouteParameters>,
 #[cfg(test)]
                error_code: Option<u16>,
diff --git a/lightning/src/util/fairrwlock.rs b/lightning/src/util/fairrwlock.rs
deleted file mode 100644 (file)
index 5715a8c..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-use std::sync::{LockResult, RwLock, RwLockReadGuard, RwLockWriteGuard, TryLockResult};
-use std::sync::atomic::{AtomicUsize, Ordering};
-
-/// Rust libstd's RwLock does not provide any fairness guarantees (and, in fact, when used on
-/// Linux with pthreads under the hood, readers trivially and completely starve writers).
-/// Because we often hold read locks while doing message processing in multiple threads which
-/// can use significant CPU time, with write locks being time-sensitive but relatively small in
-/// CPU time, we can end up with starvation completely blocking incoming connections or pings,
-/// especially during initial graph sync.
-///
-/// Thus, we need to block readers when a writer is pending, which we do with a trivial RwLock
-/// wrapper here. Its not particularly optimized, but provides some reasonable fairness by
-/// blocking readers (by taking the write lock) if there are writers pending when we go to take
-/// a read lock.
-pub struct FairRwLock<T> {
-       lock: RwLock<T>,
-       waiting_writers: AtomicUsize,
-}
-
-impl<T> FairRwLock<T> {
-       pub fn new(t: T) -> Self {
-               Self { lock: RwLock::new(t), waiting_writers: AtomicUsize::new(0) }
-       }
-
-       // Note that all atomic accesses are relaxed, as we do not rely on the atomics here for any
-       // ordering at all, instead relying on the underlying RwLock to provide ordering of unrelated
-       // memory.
-       pub fn write(&self) -> LockResult<RwLockWriteGuard<T>> {
-               self.waiting_writers.fetch_add(1, Ordering::Relaxed);
-               let res = self.lock.write();
-               self.waiting_writers.fetch_sub(1, Ordering::Relaxed);
-               res
-       }
-
-       pub fn read(&self) -> LockResult<RwLockReadGuard<T>> {
-               if self.waiting_writers.load(Ordering::Relaxed) != 0 {
-                       let _write_queue_lock = self.lock.write();
-               }
-               // Note that we don't consider ensuring that an underlying RwLock allowing writers to
-               // starve readers doesn't exhibit the same behavior here. I'm not aware of any
-               // libstd-backing RwLock which exhibits this behavior, and as documented in the
-               // struct-level documentation, it shouldn't pose a significant issue for our current
-               // codebase.
-               self.lock.read()
-       }
-
-       pub fn try_write(&self) -> TryLockResult<RwLockWriteGuard<'_, T>> {
-               self.lock.try_write()
-       }
-}
index cccbfe7bc7a43434a12b8c7608dff2d935e5c1dc..3d45172517db3e706dcbf4884dd75923c872c8a5 100644 (file)
@@ -1,10 +1,11 @@
 //! This module has a map which can be iterated in a deterministic order. See the [`IndexedMap`].
 
 use crate::prelude::{HashMap, hash_map};
-use alloc::collections::{BTreeSet, btree_set};
+use alloc::vec::Vec;
+use alloc::slice::Iter;
 use core::hash::Hash;
 use core::cmp::Ord;
-use core::ops::RangeBounds;
+use core::ops::{Bound, RangeBounds};
 
 /// A map which can be iterated in a deterministic order.
 ///
@@ -21,11 +22,10 @@ use core::ops::RangeBounds;
 /// keys in the order defined by [`Ord`].
 ///
 /// [`BTreeMap`]: alloc::collections::BTreeMap
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Debug, Eq)]
 pub struct IndexedMap<K: Hash + Ord, V> {
        map: HashMap<K, V>,
-       // TODO: Explore swapping this for a sorted vec (that is only sorted on first range() call)
-       keys: BTreeSet<K>,
+       keys: Vec<K>,
 }
 
 impl<K: Clone + Hash + Ord, V> IndexedMap<K, V> {
@@ -33,7 +33,7 @@ impl<K: Clone + Hash + Ord, V> IndexedMap<K, V> {
        pub fn new() -> Self {
                Self {
                        map: HashMap::new(),
-                       keys: BTreeSet::new(),
+                       keys: Vec::new(),
                }
        }
 
@@ -58,7 +58,8 @@ impl<K: Clone + Hash + Ord, V> IndexedMap<K, V> {
        pub fn remove(&mut self, key: &K) -> Option<V> {
                let ret = self.map.remove(key);
                if let Some(_) = ret {
-                       assert!(self.keys.remove(key), "map and keys must be consistent");
+                       let idx = self.keys.iter().position(|k| k == key).expect("map and keys must be consistent");
+                       self.keys.remove(idx);
                }
                ret
        }
@@ -68,7 +69,7 @@ impl<K: Clone + Hash + Ord, V> IndexedMap<K, V> {
        pub fn insert(&mut self, key: K, value: V) -> Option<V> {
                let ret = self.map.insert(key.clone(), value);
                if ret.is_none() {
-                       assert!(self.keys.insert(key), "map and keys must be consistent");
+                       self.keys.push(key);
                }
                ret
        }
@@ -109,9 +110,21 @@ impl<K: Clone + Hash + Ord, V> IndexedMap<K, V> {
        }
 
        /// Returns an iterator which iterates over the `key`/`value` pairs in a given range.
-       pub fn range<R: RangeBounds<K>>(&self, range: R) -> Range<K, V> {
+       pub fn range<R: RangeBounds<K>>(&mut self, range: R) -> Range<K, V> {
+               self.keys.sort_unstable();
+               let start = match range.start_bound() {
+                       Bound::Unbounded => 0,
+                       Bound::Included(key) => self.keys.binary_search(key).unwrap_or_else(|index| index),
+                       Bound::Excluded(key) => self.keys.binary_search(key).and_then(|index| Ok(index + 1)).unwrap_or_else(|index| index),
+               };
+               let end = match range.end_bound() {
+                       Bound::Unbounded => self.keys.len(),
+                       Bound::Included(key) => self.keys.binary_search(key).and_then(|index| Ok(index + 1)).unwrap_or_else(|index| index),
+                       Bound::Excluded(key) => self.keys.binary_search(key).unwrap_or_else(|index| index),
+               };
+
                Range {
-                       inner_range: self.keys.range(range),
+                       inner_range: self.keys[start..end].iter(),
                        map: &self.map,
                }
        }
@@ -127,9 +140,15 @@ impl<K: Clone + Hash + Ord, V> IndexedMap<K, V> {
        }
 }
 
+impl<K: Hash + Ord + PartialEq, V: PartialEq> PartialEq for IndexedMap<K, V> {
+       fn eq(&self, other: &Self) -> bool {
+               self.map == other.map
+       }
+}
+
 /// An iterator over a range of values in an [`IndexedMap`]
 pub struct Range<'a, K: Hash + Ord, V> {
-       inner_range: btree_set::Range<'a, K>,
+       inner_range: Iter<'a, K>,
        map: &'a HashMap<K, V>,
 }
 impl<'a, K: Hash + Ord, V: 'a> Iterator for Range<'a, K, V> {
@@ -148,7 +167,7 @@ pub struct VacantEntry<'a, K: Hash + Ord, V> {
        #[cfg(not(feature = "hashbrown"))]
        underlying_entry: hash_map::VacantEntry<'a, K, V>,
        key: K,
-       keys: &'a mut BTreeSet<K>,
+       keys: &'a mut Vec<K>,
 }
 
 /// An [`Entry`] for an existing key-value pair
@@ -157,7 +176,7 @@ pub struct OccupiedEntry<'a, K: Hash + Ord, V> {
        underlying_entry: hash_map::OccupiedEntry<'a, K, V, hash_map::DefaultHashBuilder>,
        #[cfg(not(feature = "hashbrown"))]
        underlying_entry: hash_map::OccupiedEntry<'a, K, V>,
-       keys: &'a mut BTreeSet<K>,
+       keys: &'a mut Vec<K>,
 }
 
 /// A mutable reference to a position in the map. This can be used to reference, add, or update the
@@ -172,7 +191,7 @@ pub enum Entry<'a, K: Hash + Ord, V> {
 impl<'a, K: Hash + Ord, V> VacantEntry<'a, K, V> {
        /// Insert a value into the position described by this entry.
        pub fn insert(self, value: V) -> &'a mut V {
-               assert!(self.keys.insert(self.key), "map and keys must be consistent");
+               self.keys.push(self.key);
                self.underlying_entry.insert(value)
        }
 }
@@ -181,7 +200,8 @@ impl<'a, K: Hash + Ord, V> OccupiedEntry<'a, K, V> {
        /// Remove the value at the position described by this entry.
        pub fn remove_entry(self) -> (K, V) {
                let res = self.underlying_entry.remove_entry();
-               assert!(self.keys.remove(&res.0), "map and keys must be consistent");
+               let idx = self.keys.iter().position(|k| k == &res.0).expect("map and keys must be consistent");
+               self.keys.remove(idx);
                res
        }
 
index 1673bd07f69b24d7af49f7d9c3604ea33f000422..7bcbc5a41fbf72c346f6e2ed81ddebfad43ce9f1 100644 (file)
@@ -27,8 +27,6 @@ pub mod wakers;
 pub(crate) mod atomic_counter;
 pub(crate) mod byte_utils;
 pub(crate) mod chacha20;
-#[cfg(all(any(feature = "_bench_unstable", not(test)), feature = "std"))]
-pub(crate) mod fairrwlock;
 #[cfg(fuzzing)]
 pub mod zbase32;
 #[cfg(not(fuzzing))]
index 55abf68181ee47d48f32ccbfda609b3ee274b51f..8dc98ee43d7cb3ef384b86a349f9f2d10ea4f714 100644 (file)
@@ -137,11 +137,12 @@ impl<'a> Router for TestRouter<'a> {
        }
 }
 
-#[cfg(feature = "std")] // If we put this on the `if`, we get "attributes are not yet allowed on `if` expressions" on 1.41.1
 impl<'a> Drop for TestRouter<'a> {
        fn drop(&mut self) {
-               if std::thread::panicking() {
-                       return;
+               #[cfg(feature = "std")] {
+                       if std::thread::panicking() {
+                               return;
+                       }
                }
                assert!(self.next_routes.lock().unwrap().is_empty());
        }