Skip to content

Commit acf68ed

Browse files
committed
[fuzz] Test chanmon_consistency payment-send errors are sane
Instead of simply always considering a payment-send failure as acceptable (and aborting fuzzing), we check that a payment send failure is from a list of errors that we know we can hit, mostly around maxing out our channel balance. Critically, we keep going after hitting an error, as there's no reason channels should get out of sync even if a send fails.
1 parent 4e82003 commit acf68ed

File tree

1 file changed

+49
-10
lines changed

1 file changed

+49
-10
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,11 @@ use lightning::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdateErr,
3535
use lightning::chain::transaction::OutPoint;
3636
use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};
3737
use lightning::chain::keysinterface::{KeysInterface, InMemoryChannelKeys};
38-
use lightning::ln::channelmanager::{ChannelManager, PaymentHash, PaymentPreimage, PaymentSecret, ChannelManagerReadArgs};
38+
use lightning::ln::channelmanager::{ChannelManager, PaymentHash, PaymentPreimage, PaymentSecret, PaymentSendFailure, ChannelManagerReadArgs};
3939
use lightning::ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
4040
use lightning::ln::msgs::{CommitmentUpdate, ChannelMessageHandler, ErrorAction, UpdateAddHTLC, Init};
4141
use lightning::util::enforcing_trait_impls::EnforcingChannelKeys;
42+
use lightning::util::errors::APIError;
4243
use lightning::util::events;
4344
use lightning::util::logger::Logger;
4445
use lightning::util::config::UserConfig;
@@ -185,6 +186,47 @@ impl KeysInterface for KeyProvider {
185186
}
186187
}
187188

189+
#[inline]
190+
fn check_api_err(api_err: APIError) {
191+
match api_err {
192+
APIError::APIMisuseError { .. } => panic!("We can't misuse the API"),
193+
APIError::FeeRateTooHigh { .. } => panic!("We can't send too much fee?"),
194+
APIError::RouteError { .. } => panic!("Our routes should work"),
195+
APIError::ChannelUnavailable { err } => {
196+
// Test the error against a list of errors we can hit, and reject
197+
// all others. If you hit this panic, the list of acceptable errors
198+
// is probably just stale and you should add new messages here.
199+
match err.as_str() {
200+
"Peer for first hop currently disconnected/pending monitor update!" => {},
201+
_ if err.starts_with("Cannot push more than their max accepted HTLCs ") => {},
202+
_ if err.starts_with("Cannot send value that would put us over the max HTLC value in flight our peer will accept ") => {},
203+
_ if err.starts_with("Cannot send value that would put our balance under counterparty-announced channel reserve value") => {},
204+
_ if err.starts_with("Cannot send value that would overdraw remaining funds.") => {},
205+
_ if err.starts_with("Cannot send value that would not leave enough to pay for fees.") => {},
206+
_ => panic!(err),
207+
}
208+
},
209+
APIError::MonitorUpdateFailed => {
210+
// We can (obviously) temp-fail a monitor update
211+
},
212+
}
213+
}
214+
#[inline]
215+
fn check_payment_err(send_err: PaymentSendFailure) {
216+
match send_err {
217+
PaymentSendFailure::ParameterError(api_err) => check_api_err(api_err),
218+
PaymentSendFailure::PathParameterError(per_path_results) => {
219+
for res in per_path_results { if let Err(api_err) = res { check_api_err(api_err); } }
220+
},
221+
PaymentSendFailure::AllFailedRetrySafe(per_path_results) => {
222+
for api_err in per_path_results { check_api_err(api_err); }
223+
},
224+
PaymentSendFailure::PartialFailure(per_path_results) => {
225+
for res in per_path_results { if let Err(api_err) = res { check_api_err(api_err); } }
226+
},
227+
}
228+
}
229+
188230
#[inline]
189231
pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
190232
let fee_est = Arc::new(FuzzEstimator{});
@@ -407,7 +449,7 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
407449
($source: expr, $dest: expr, $amt: expr) => { {
408450
let payment_hash = Sha256::hash(&[payment_id; 1]);
409451
payment_id = payment_id.wrapping_add(1);
410-
if let Err(_) = $source.send_payment(&Route {
452+
if let Err(err) = $source.send_payment(&Route {
411453
paths: vec![vec![RouteHop {
412454
pubkey: $dest.0.get_our_node_id(),
413455
node_features: NodeFeatures::empty(),
@@ -417,14 +459,13 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
417459
cltv_expiry_delta: 200,
418460
}]],
419461
}, PaymentHash(payment_hash.into_inner()), &None) {
420-
// Probably ran out of funds
421-
test_return!();
462+
check_payment_err(err);
422463
}
423464
} };
424465
($source: expr, $middle: expr, $dest: expr, $amt: expr) => { {
425466
let payment_hash = Sha256::hash(&[payment_id; 1]);
426467
payment_id = payment_id.wrapping_add(1);
427-
if let Err(_) = $source.send_payment(&Route {
468+
if let Err(err) = $source.send_payment(&Route {
428469
paths: vec![vec![RouteHop {
429470
pubkey: $middle.0.get_our_node_id(),
430471
node_features: NodeFeatures::empty(),
@@ -441,8 +482,7 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
441482
cltv_expiry_delta: 200,
442483
}]],
443484
}, PaymentHash(payment_hash.into_inner()), &None) {
444-
// Probably ran out of funds
445-
test_return!();
485+
check_payment_err(err);
446486
}
447487
} }
448488
}
@@ -452,7 +492,7 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
452492
payment_id = payment_id.wrapping_add(1);
453493
let payment_secret = Sha256::hash(&[payment_id; 1]);
454494
payment_id = payment_id.wrapping_add(1);
455-
if let Err(_) = $source.send_payment(&Route {
495+
if let Err(err) = $source.send_payment(&Route {
456496
paths: vec![vec![RouteHop {
457497
pubkey: $middle.0.get_our_node_id(),
458498
node_features: NodeFeatures::empty(),
@@ -483,8 +523,7 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
483523
cltv_expiry_delta: 200,
484524
}]],
485525
}, PaymentHash(payment_hash.into_inner()), &Some(PaymentSecret(payment_secret.into_inner()))) {
486-
// Probably ran out of funds
487-
test_return!();
526+
check_payment_err(err);
488527
}
489528
} }
490529
}

0 commit comments

Comments
 (0)