Skip to content

Commit a91bf02

Browse files
committed
Fix validation of payment success in chanmon_consistency fuzzer
Prior to bcaba29, the `chanmon_consistency` fuzzer checked that payments sent either succeeded or failed as expected by looking at the `APIError` which we received as a result of calling the send method. bcaba29 removed the legacy send method during fuzzing so attempted to replicate the old logic by checking for events which contained the legacy `APIError` value. While this was plenty thorough, it was somewhat brittle in that it made expectations about the event state of a `ChannelManager` which turned out to not be true. Instead, here, we validate the send correctness by examining the `RecentPaymentDetails` list from a `ChannelManager` immediately after sending.
1 parent 177a612 commit a91bf02

File tree

1 file changed

+42
-72
lines changed

1 file changed

+42
-72
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 42 additions & 72 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<
@@ -571,16 +533,20 @@ fn send_payment(
571533
}],
572534
route_params: Some(route_params.clone()),
573535
});
574-
if let Err(err) = source.send_payment(
575-
payment_hash,
576-
RecipientOnionFields::secret_only(payment_secret),
577-
PaymentId(payment_id),
578-
route_params,
579-
Retry::Attempts(0),
580-
) {
581-
panic!("Errored with {:?} on initial payment send", err);
582-
} else {
583-
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+
},
584550
}
585551
}
586552

@@ -652,17 +618,21 @@ fn send_hop_payment(
652618
}],
653619
route_params: Some(route_params.clone()),
654620
});
655-
if let Err(err) = source.send_payment(
656-
payment_hash,
657-
RecipientOnionFields::secret_only(payment_secret),
658-
PaymentId(payment_id),
659-
route_params,
660-
Retry::Attempts(0),
661-
) {
662-
panic!("Errored with {:?} on initial payment send", err);
663-
} else {
664-
let sent_amt = amt + first_hop_fee;
665-
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+
},
666636
}
667637
}
668638

0 commit comments

Comments
 (0)