Skip to content

Commit 0e6f47e

Browse files
Merge pull request #3490 from TheBlueMatt/2024-12-nits
Fix fuzz deadlock
2 parents dcc531e + a91bf02 commit 0e6f47e

File tree

3 files changed

+81
-98
lines changed

3 files changed

+81
-98
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 54 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ use lightning::events::MessageSendEventsProvider;
4747
use lightning::ln::channel::FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE;
4848
use lightning::ln::channel_state::ChannelDetails;
4949
use lightning::ln::channelmanager::{
50-
ChainParameters, ChannelManager, ChannelManagerReadArgs, PaymentId, RecipientOnionFields, Retry,
50+
ChainParameters, ChannelManager, ChannelManagerReadArgs, PaymentId, RecentPaymentDetails,
51+
RecipientOnionFields, Retry,
5152
};
5253
use lightning::ln::functional_test_utils::*;
5354
use lightning::ln::inbound_payment::ExpandedKey;
@@ -64,7 +65,6 @@ use lightning::routing::router::{
6465
use lightning::sign::{EntropySource, InMemorySigner, NodeSigner, Recipient, SignerProvider};
6566
use lightning::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
6667
use lightning::util::config::UserConfig;
67-
use lightning::util::errors::APIError;
6868
use lightning::util::hash_tables::*;
6969
use lightning::util::logger::Logger;
7070
use lightning::util::ser::{Readable, ReadableArgs, Writeable, Writer};
@@ -442,57 +442,19 @@ impl KeyProvider {
442442

443443
// Returns a bool indicating whether the payment failed.
444444
#[inline]
445-
fn check_payment_send_events(
446-
source: &ChanMan, amt: u64, min_sendable: u64, max_sendable: u64,
447-
) -> bool {
448-
let mut payment_failed = false;
449-
let events = source.get_and_clear_pending_events();
450-
assert!(events.len() == 2 || events.len() == 0);
451-
for ev in events {
452-
match ev {
453-
events::Event::PaymentPathFailed {
454-
failure: events::PathFailure::InitialSend { err },
455-
..
456-
} => {
457-
check_api_err(err, amt > max_sendable || amt < min_sendable);
445+
fn check_payment_send_events(source: &ChanMan, sent_payment_id: PaymentId) -> bool {
446+
for payment in source.list_recent_payments() {
447+
match payment {
448+
RecentPaymentDetails::Pending { payment_id, .. } if payment_id == sent_payment_id => {
449+
return true;
458450
},
459-
events::Event::PaymentFailed { .. } => {},
460-
_ => panic!(),
461-
};
462-
payment_failed = true;
463-
}
464-
// Note that while the max is a strict upper-bound, we can occasionally send substantially
465-
// below the minimum, with some gap which is unusable immediately below the minimum. Thus,
466-
// we don't check against min_value_sendable here.
467-
assert!(payment_failed || (amt <= max_sendable));
468-
payment_failed
469-
}
470-
471-
#[inline]
472-
fn check_api_err(api_err: APIError, sendable_bounds_violated: bool) {
473-
match api_err {
474-
APIError::APIMisuseError { .. } => panic!("We can't misuse the API"),
475-
APIError::FeeRateTooHigh { .. } => panic!("We can't send too much fee?"),
476-
APIError::InvalidRoute { .. } => panic!("Our routes should work"),
477-
APIError::ChannelUnavailable { err } => {
478-
// Test the error against a list of errors we can hit, and reject
479-
// all others. If you hit this panic, the list of acceptable errors
480-
// is probably just stale and you should add new messages here.
481-
match err.as_str() {
482-
"Peer for first hop currently disconnected" => {},
483-
_ if err.starts_with("Cannot send less than our next-HTLC minimum - ") => {},
484-
_ if err.starts_with("Cannot send more than our next-HTLC maximum - ") => {},
485-
_ => panic!("{}", err),
486-
}
487-
assert!(sendable_bounds_violated);
488-
},
489-
APIError::MonitorUpdateInProgress => {
490-
// We can (obviously) temp-fail a monitor update
491-
},
492-
APIError::IncompatibleShutdownScript { .. } => {
493-
panic!("Cannot send an incompatible shutdown script")
494-
},
451+
RecentPaymentDetails::Abandoned { payment_id, .. } if payment_id == sent_payment_id => {
452+
return false;
453+
},
454+
_ => {},
455+
}
495456
}
457+
return false;
496458
}
497459

498460
type ChanMan<'a> = ChannelManager<
@@ -552,8 +514,11 @@ fn send_payment(
552514
.find(|chan| chan.short_channel_id == Some(dest_chan_id))
553515
.map(|chan| (chan.next_outbound_htlc_minimum_msat, chan.next_outbound_htlc_limit_msat))
554516
.unwrap_or((0, 0));
555-
let mut next_routes = source.router.next_routes.lock().unwrap();
556-
next_routes.push_back(Route {
517+
let route_params = RouteParameters::from_payment_params_and_value(
518+
PaymentParameters::from_node_id(source.get_our_node_id(), TEST_FINAL_CLTV),
519+
amt,
520+
);
521+
source.router.next_routes.lock().unwrap().push_back(Route {
557522
paths: vec![Path {
558523
hops: vec![RouteHop {
559524
pubkey: dest.get_our_node_id(),
@@ -566,22 +531,22 @@ fn send_payment(
566531
}],
567532
blinded_tail: None,
568533
}],
569-
route_params: None,
534+
route_params: Some(route_params.clone()),
570535
});
571-
let route_params = RouteParameters::from_payment_params_and_value(
572-
PaymentParameters::from_node_id(source.get_our_node_id(), TEST_FINAL_CLTV),
573-
amt,
574-
);
575-
if let Err(err) = source.send_payment(
576-
payment_hash,
577-
RecipientOnionFields::secret_only(payment_secret),
578-
PaymentId(payment_id),
579-
route_params,
580-
Retry::Attempts(0),
581-
) {
582-
panic!("Errored with {:?} on initial payment send", err);
583-
} else {
584-
check_payment_send_events(source, amt, min_value_sendable, max_value_sendable)
536+
let onion = RecipientOnionFields::secret_only(payment_secret);
537+
let payment_id = PaymentId(payment_id);
538+
let res =
539+
source.send_payment(payment_hash, onion, payment_id, route_params, Retry::Attempts(0));
540+
match res {
541+
Err(err) => {
542+
panic!("Errored with {:?} on initial payment send", err);
543+
},
544+
Ok(()) => {
545+
let expect_failure = amt < min_value_sendable || amt > max_value_sendable;
546+
let succeeded = check_payment_send_events(source, payment_id);
547+
assert_eq!(succeeded, !expect_failure);
548+
succeeded
549+
},
585550
}
586551
}
587552

@@ -623,8 +588,11 @@ fn send_hop_payment(
623588
.map(|chan| (chan.next_outbound_htlc_minimum_msat, chan.next_outbound_htlc_limit_msat))
624589
.unwrap_or((0, 0));
625590
let first_hop_fee = 50_000;
626-
let mut next_routes = source.router.next_routes.lock().unwrap();
627-
next_routes.push_back(Route {
591+
let route_params = RouteParameters::from_payment_params_and_value(
592+
PaymentParameters::from_node_id(source.get_our_node_id(), TEST_FINAL_CLTV),
593+
amt,
594+
);
595+
source.router.next_routes.lock().unwrap().push_back(Route {
628596
paths: vec![Path {
629597
hops: vec![
630598
RouteHop {
@@ -648,23 +616,23 @@ fn send_hop_payment(
648616
],
649617
blinded_tail: None,
650618
}],
651-
route_params: None,
619+
route_params: Some(route_params.clone()),
652620
});
653-
let route_params = RouteParameters::from_payment_params_and_value(
654-
PaymentParameters::from_node_id(source.get_our_node_id(), TEST_FINAL_CLTV),
655-
amt,
656-
);
657-
if let Err(err) = source.send_payment(
658-
payment_hash,
659-
RecipientOnionFields::secret_only(payment_secret),
660-
PaymentId(payment_id),
661-
route_params,
662-
Retry::Attempts(0),
663-
) {
664-
panic!("Errored with {:?} on initial payment send", err);
665-
} else {
666-
let sent_amt = amt + first_hop_fee;
667-
check_payment_send_events(source, sent_amt, min_value_sendable, max_value_sendable)
621+
let onion = RecipientOnionFields::secret_only(payment_secret);
622+
let payment_id = PaymentId(payment_id);
623+
let res =
624+
source.send_payment(payment_hash, onion, payment_id, route_params, Retry::Attempts(0));
625+
match res {
626+
Err(err) => {
627+
panic!("Errored with {:?} on initial payment send", err);
628+
},
629+
Ok(()) => {
630+
let sent_amt = amt + first_hop_fee;
631+
let expect_failure = sent_amt < min_value_sendable || sent_amt > max_value_sendable;
632+
let succeeded = check_payment_send_events(source, payment_id);
633+
assert_eq!(succeeded, !expect_failure);
634+
succeeded
635+
},
668636
}
669637
}
670638

lightning-persister/src/test_utils.rs

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,32 +67,46 @@ pub(crate) fn do_test_data_migration<S: MigratableKVStore, T: MigratableKVStore>
6767
) {
6868
// We fill the source with some bogus keys.
6969
let dummy_data = [42u8; 32];
70-
let num_primary_namespaces = 2;
71-
let num_secondary_namespaces = 2;
70+
let num_primary_namespaces = 3;
71+
let num_secondary_namespaces = 3;
7272
let num_keys = 3;
73+
let mut expected_keys = Vec::new();
7374
for i in 0..num_primary_namespaces {
74-
let primary_namespace =
75-
format!("testspace{}", KVSTORE_NAMESPACE_KEY_ALPHABET.chars().nth(i).unwrap());
75+
let primary_namespace = if i == 0 {
76+
String::new()
77+
} else {
78+
format!("testspace{}", KVSTORE_NAMESPACE_KEY_ALPHABET.chars().nth(i).unwrap())
79+
};
7680
for j in 0..num_secondary_namespaces {
77-
let secondary_namespace =
78-
format!("testsubspace{}", KVSTORE_NAMESPACE_KEY_ALPHABET.chars().nth(j).unwrap());
81+
let secondary_namespace = if i == 0 || j == 0 {
82+
String::new()
83+
} else {
84+
format!("testsubspace{}", KVSTORE_NAMESPACE_KEY_ALPHABET.chars().nth(j).unwrap())
85+
};
7986
for k in 0..num_keys {
8087
let key =
8188
format!("testkey{}", KVSTORE_NAMESPACE_KEY_ALPHABET.chars().nth(k).unwrap());
8289
source_store
8390
.write(&primary_namespace, &secondary_namespace, &key, &dummy_data)
8491
.unwrap();
92+
expected_keys.push((primary_namespace.clone(), secondary_namespace.clone(), key));
8593
}
8694
}
8795
}
88-
let total_num_entries = num_primary_namespaces * num_secondary_namespaces * num_keys;
89-
let all_keys = source_store.list_all_keys().unwrap();
90-
assert_eq!(all_keys.len(), total_num_entries);
96+
expected_keys.sort();
97+
expected_keys.dedup();
98+
99+
let mut source_list = source_store.list_all_keys().unwrap();
100+
source_list.sort();
101+
assert_eq!(source_list, expected_keys);
91102

92103
migrate_kv_store_data(source_store, target_store).unwrap();
93104

94-
assert_eq!(target_store.list_all_keys().unwrap().len(), total_num_entries);
95-
for (p, s, k) in &all_keys {
105+
let mut target_list = target_store.list_all_keys().unwrap();
106+
target_list.sort();
107+
assert_eq!(target_list, expected_keys);
108+
109+
for (p, s, k) in expected_keys.iter() {
96110
assert_eq!(target_store.read(p, s, k).unwrap(), dummy_data);
97111
}
98112
}

lightning/src/ln/outbound_payment.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1226,7 +1226,8 @@ impl OutboundPayments {
12261226

12271227
if route.route_params.as_ref() != Some(route_params) {
12281228
debug_assert!(false,
1229-
"Routers are expected to return a Route which includes the requested RouteParameters");
1229+
"Routers are expected to return a Route which includes the requested RouteParameters. Got {:?}, expected {:?}",
1230+
route.route_params, route_params);
12301231
route.route_params = Some(route_params.clone());
12311232
}
12321233

0 commit comments

Comments
 (0)