Skip to content

Commit 17835d8

Browse files
committed
errors calling last_used_index without wildcard
1 parent 65c6e34 commit 17835d8

File tree

3 files changed

+18
-24
lines changed

3 files changed

+18
-24
lines changed

src/server/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ pub enum Error {
270270
NotYetImplemented,
271271
AddressCannotBeBlinded,
272272
TooManyAddresses,
273+
DescriptorMustHaveWildcard,
273274
}
274275

275276
impl std::fmt::Display for Error {

src/server/route.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,10 @@ fn parse_descriptor_query(
438438

439439
let descriptor = be::Descriptor::from_str(&desc_str, network)?;
440440

441+
if !descriptor.has_wildcard() {
442+
return Err(Error::DescriptorMustHaveWildcard);
443+
}
444+
441445
if is_testnet_or_regtest == descriptor.is_mainnet() {
442446
return Err(Error::WrongNetwork);
443447
}
@@ -720,11 +724,11 @@ async fn handle_last_used_index(
720724
let mut internal_last_used: Option<u32> = None;
721725

722726
for desc in descriptor.into_single_descriptors().unwrap().iter() {
723-
let is_single_address = !desc.has_wildcard();
724-
725727
// Determine if this is external (0/*) or internal (1/*) chain
726728
let is_internal = desc.to_string().contains("/1/*");
727729

730+
// we don't need to check for no wildcard, since parse_descriptor_query guarantees we have wildcard
731+
728732
let mut last_used_for_chain: Option<u32> = None;
729733

730734
for batch in 0..MAX_BATCH {
@@ -746,7 +750,7 @@ async fn handle_last_used_index(
746750
}
747751

748752
// If no activity in this batch and we've checked at least GAP_LIMIT addresses, stop
749-
if !batch_has_activity || is_single_address {
753+
if !batch_has_activity {
750754
break;
751755
}
752756
}
@@ -909,7 +913,13 @@ pub async fn infallible_route(
909913
.status(StatusCode::UNPROCESSABLE_ENTITY)
910914
.body(Full::new(e.to_string().into()))
911915
.unwrap()
916+
} else if matches!(e, Error::DescriptorMustHaveWildcard) {
917+
Response::builder()
918+
.status(StatusCode::BAD_REQUEST)
919+
.body(Full::new(e.to_string().into()))
920+
.unwrap()
912921
} else {
922+
// TODO map many errors to specific status codes
913923
Response::builder()
914924
.status(StatusCode::INTERNAL_SERVER_ERROR)
915925
.body(Full::new(e.to_string().into()))

tests/integration.rs

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -552,29 +552,12 @@ async fn do_test_last_used_index(test_env: waterfalls::test_env::TestEnv) {
552552
);
553553

554554
// Test descriptor without wildcard (single address at index 0)
555-
// This should not cause infinite loops and should return external=0 since that address was used
555+
// This must error
556556
let no_wildcard_desc = format!("{prefix}wpkh({tpub}/0/0)");
557-
let result_no_wildcard = client.last_used_index(&no_wildcard_desc).await.unwrap();
557+
let result_no_wildcard = client.last_used_index(&no_wildcard_desc).await.unwrap_err();
558558
assert_eq!(
559-
result_no_wildcard.external,
560-
Some(0),
561-
"Descriptor without wildcard (index 0) should find activity"
562-
);
563-
assert_eq!(
564-
result_no_wildcard.internal, None,
565-
"Descriptor without wildcard has no internal chain"
566-
);
567-
568-
// Test descriptor without wildcard at an unused index (index 1)
569-
// This should return None for external since no tx at index 1
570-
let no_wildcard_unused_desc = format!("{prefix}wpkh({tpub}/0/1)");
571-
let result_no_wildcard_unused = client
572-
.last_used_index(&no_wildcard_unused_desc)
573-
.await
574-
.unwrap();
575-
assert_eq!(
576-
result_no_wildcard_unused.external, None,
577-
"Descriptor without wildcard at unused index should return None"
559+
result_no_wildcard.to_string(),
560+
"last_used_index response is not 200 but: 400 body is: DescriptorMustHaveWildcard"
578561
);
579562

580563
println!(

0 commit comments

Comments
 (0)