Skip to content

Commit 31a0456

Browse files
authored
Merge pull request #2395 from wpaulino/phantom-deduped-forward-event
Force enqueue second forward event for phantom receives
2 parents 4c342bd + 81722ca commit 31a0456

File tree

3 files changed

+53
-30
lines changed

3 files changed

+53
-30
lines changed

lightning-invoice/src/utils.rs

+14-6
Original file line numberDiff line numberDiff line change
@@ -792,12 +792,13 @@ fn prefer_current_channel(min_inbound_capacity_msat: Option<u64>, current_channe
792792

793793
#[cfg(test)]
794794
mod test {
795+
use core::cell::RefCell;
795796
use core::time::Duration;
796797
use crate::{Currency, Description, InvoiceDescription, SignOrCreationError, CreationError};
797798
use bitcoin_hashes::{Hash, sha256};
798799
use bitcoin_hashes::sha256::Hash as Sha256;
799800
use lightning::sign::PhantomKeysManager;
800-
use lightning::events::{MessageSendEvent, MessageSendEventsProvider, Event};
801+
use lightning::events::{MessageSendEvent, MessageSendEventsProvider, Event, EventsProvider};
801802
use lightning::ln::{PaymentPreimage, PaymentHash};
802803
use lightning::ln::channelmanager::{PhantomRouteHints, MIN_FINAL_CLTV_EXPIRY_DELTA, PaymentId, RecipientOnionFields, Retry};
803804
use lightning::ln::functional_test_utils::*;
@@ -1357,13 +1358,20 @@ mod test {
13571358
// Note that we have to "forward pending HTLCs" twice before we see the PaymentClaimable as
13581359
// this "emulates" the payment taking two hops, providing some privacy to make phantom node
13591360
// payments "look real" by taking more time.
1360-
expect_pending_htlcs_forwardable_ignore!(nodes[fwd_idx]);
1361-
nodes[fwd_idx].node.process_pending_htlc_forwards();
1362-
expect_pending_htlcs_forwardable_ignore!(nodes[fwd_idx]);
1363-
nodes[fwd_idx].node.process_pending_htlc_forwards();
1361+
let other_events = RefCell::new(Vec::new());
1362+
let forward_event_handler = |event: Event| {
1363+
if let Event::PendingHTLCsForwardable { .. } = event {
1364+
nodes[fwd_idx].node.process_pending_htlc_forwards();
1365+
} else {
1366+
other_events.borrow_mut().push(event);
1367+
}
1368+
};
1369+
nodes[fwd_idx].node.process_pending_events(&forward_event_handler);
1370+
nodes[fwd_idx].node.process_pending_events(&forward_event_handler);
13641371

13651372
let payment_preimage_opt = if user_generated_pmt_hash { None } else { Some(payment_preimage) };
1366-
expect_payment_claimable!(&nodes[fwd_idx], payment_hash, payment_secret, payment_amt, payment_preimage_opt, invoice.recover_payee_pub_key());
1373+
assert_eq!(other_events.borrow().len(), 1);
1374+
check_payment_claimable(&other_events.borrow()[0], payment_hash, payment_secret, payment_amt, payment_preimage_opt, invoice.recover_payee_pub_key());
13671375
do_claim_payment_along_route(&nodes[0], &[&vec!(&nodes[fwd_idx])[..]], false, payment_preimage);
13681376
let events = nodes[0].node.get_and_clear_pending_events();
13691377
assert_eq!(events.len(), 2);

lightning/src/ln/channelmanager.rs

+15-8
Original file line numberDiff line numberDiff line change
@@ -2020,6 +2020,8 @@ macro_rules! process_events_body {
20202020
let mut pending_events = $self.pending_events.lock().unwrap();
20212021
pending_events.drain(..num_events);
20222022
processed_all_events = pending_events.is_empty();
2023+
// Note that `push_pending_forwards_ev` relies on `pending_events_processor` being
2024+
// updated here with the `pending_events` lock acquired.
20232025
$self.pending_events_processor.store(false, Ordering::Release);
20242026
}
20252027

@@ -5765,16 +5767,21 @@ where
57655767
}
57665768
}
57675769

5768-
// We only want to push a PendingHTLCsForwardable event if no others are queued.
57695770
fn push_pending_forwards_ev(&self) {
57705771
let mut pending_events = self.pending_events.lock().unwrap();
5771-
let forward_ev_exists = pending_events.iter()
5772-
.find(|(ev, _)| if let events::Event::PendingHTLCsForwardable { .. } = ev { true } else { false })
5773-
.is_some();
5774-
if !forward_ev_exists {
5775-
pending_events.push_back((events::Event::PendingHTLCsForwardable {
5776-
time_forwardable:
5777-
Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS),
5772+
let is_processing_events = self.pending_events_processor.load(Ordering::Acquire);
5773+
let num_forward_events = pending_events.iter().filter(|(ev, _)|
5774+
if let events::Event::PendingHTLCsForwardable { .. } = ev { true } else { false }
5775+
).count();
5776+
// We only want to push a PendingHTLCsForwardable event if no others are queued. Processing
5777+
// events is done in batches and they are not removed until we're done processing each
5778+
// batch. Since handling a `PendingHTLCsForwardable` event will call back into the
5779+
// `ChannelManager`, we'll still see the original forwarding event not removed. Phantom
5780+
// payments will need an additional forwarding event before being claimed to make them look
5781+
// real by taking more time.
5782+
if (is_processing_events && num_forward_events <= 1) || num_forward_events < 1 {
5783+
pending_events.push_back((Event::PendingHTLCsForwardable {
5784+
time_forwardable: Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS),
57785785
}, None));
57795786
}
57805787
}

lightning/src/ln/functional_test_utils.rs

+24-16
Original file line numberDiff line numberDiff line change
@@ -1806,6 +1806,28 @@ macro_rules! get_route_and_payment_hash {
18061806
}}
18071807
}
18081808

1809+
pub fn check_payment_claimable(
1810+
event: &Event, expected_payment_hash: PaymentHash, expected_payment_secret: PaymentSecret,
1811+
expected_recv_value: u64, expected_payment_preimage: Option<PaymentPreimage>,
1812+
expected_receiver_node_id: PublicKey,
1813+
) {
1814+
match event {
1815+
Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, .. } => {
1816+
assert_eq!(expected_payment_hash, *payment_hash);
1817+
assert_eq!(expected_recv_value, *amount_msat);
1818+
assert_eq!(expected_receiver_node_id, receiver_node_id.unwrap());
1819+
match purpose {
1820+
PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
1821+
assert_eq!(&expected_payment_preimage, payment_preimage);
1822+
assert_eq!(expected_payment_secret, *payment_secret);
1823+
},
1824+
_ => {},
1825+
}
1826+
},
1827+
_ => panic!("Unexpected event"),
1828+
}
1829+
}
1830+
18091831
#[macro_export]
18101832
#[cfg(any(test, ldk_bench, feature = "_test_utils"))]
18111833
macro_rules! expect_payment_claimable {
@@ -1815,22 +1837,8 @@ macro_rules! expect_payment_claimable {
18151837
($node: expr, $expected_payment_hash: expr, $expected_payment_secret: expr, $expected_recv_value: expr, $expected_payment_preimage: expr, $expected_receiver_node_id: expr) => {
18161838
let events = $node.node.get_and_clear_pending_events();
18171839
assert_eq!(events.len(), 1);
1818-
match events[0] {
1819-
$crate::events::Event::PaymentClaimable { ref payment_hash, ref purpose, amount_msat, receiver_node_id, .. } => {
1820-
assert_eq!($expected_payment_hash, *payment_hash);
1821-
assert_eq!($expected_recv_value, amount_msat);
1822-
assert_eq!($expected_receiver_node_id, receiver_node_id.unwrap());
1823-
match purpose {
1824-
$crate::events::PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
1825-
assert_eq!(&$expected_payment_preimage, payment_preimage);
1826-
assert_eq!($expected_payment_secret, *payment_secret);
1827-
},
1828-
_ => {},
1829-
}
1830-
},
1831-
_ => panic!("Unexpected event"),
1832-
}
1833-
}
1840+
$crate::ln::functional_test_utils::check_payment_claimable(&events[0], $expected_payment_hash, $expected_payment_secret, $expected_recv_value, $expected_payment_preimage, $expected_receiver_node_id)
1841+
};
18341842
}
18351843

18361844
#[macro_export]

0 commit comments

Comments
 (0)