ImportDescriptor currently accepts invalid ranges.
<details>
<summary>suggestion</summary>
diff --git a/src/wallet/imports.cpp b/src/wallet/imports.cpp
index 992de81f0b..5bd31403dd 100644
--- a/src/wallet/imports.cpp
+++ b/src/wallet/imports.cpp
@@ -5,6 +5,8 @@
#include <chain.h>
#include <wallet/imports.h>
+#include <limits>
+
namespace wallet {
ImportResult ImportDescriptor(CWallet& wallet, const ImportDescriptorRequest& request) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
@@ -47,8 +49,34 @@ ImportResult ImportDescriptor(CWallet& wallet, const ImportDescriptorRequest& re
);
} else if (parsed_descs.at(0)->IsRange()) {
if (request.range.has_value()) {
- range_start = request.range->first;
- range_end = request.range->second + 1; // Specified range end is inclusive, but we need range end as exclusive
+ const auto [range_begin, range_last] = request.range.value();
+ if (range_begin > range_last) {
+ return make_error(
+ WalletErrorCode::InvalidParameter,
+ "Range specified as [begin,end] must not have begin after end"
+ );
+ }
+ if (range_begin < 0) {
+ return make_error(
+ WalletErrorCode::InvalidParameter,
+ "Range should be greater or equal than 0"
+ );
+ }
+ // WalletDescriptor stores an exclusive range end in an int32_t.
+ if (range_last >= std::numeric_limits<int32_t>::max()) {
+ return make_error(
+ WalletErrorCode::InvalidParameter,
+ "End of range is too high"
+ );
+ }
+ if (range_last - range_begin >= 1000000) {
+ return make_error(
+ WalletErrorCode::InvalidParameter,
+ "Range is too large"
+ );
+ }
+ range_start = range_begin;
+ range_end = range_last + 1; // Specified range end is inclusive, but we need range end as exclusive
} else {
result.warnings.emplace_back("Range not given, using default keypool range");
range_start = 0;
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 1c3047727d..be2618f635 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -4,9 +4,15 @@
#include <wallet/wallet.h>
+#include <array>
+#include <cstddef>
#include <cstdint>
#include <future>
+#include <limits>
#include <memory>
+#include <optional>
+#include <string>
+#include <utility>
#include <vector>
#include <addresstype.h>
@@ -16,6 +22,7 @@
#include <node/types.h>
#include <policy/policy.h>
#include <rpc/server.h>
+#include <script/descriptor.h>
#include <script/solver.h>
#include <test/util/common.h>
#include <test/util/logging.h>
@@ -26,6 +33,7 @@
#include <validationinterface.h>
#include <wallet/coincontrol.h>
#include <wallet/context.h>
+#include <wallet/imports.h>
#include <wallet/receive.h>
#include <wallet/spend.h>
#include <wallet/test/util.h>
@@ -71,6 +79,47 @@ static void AddKey(CWallet& wallet, const CKey& key)
Assert(wallet.AddWalletDescriptor(w_desc, provider, "", false));
}
+BOOST_AUTO_TEST_CASE(reject_invalid_descriptor_ranges)
+{
+ const int height{*Assert(m_node.chain->getHeight())};
+ {
+ LOCK(m_wallet.cs_wallet);
+ m_wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ m_wallet.SetLastBlockProcessed(height, m_node.chain->getBlockHash(height));
+ }
+
+ CExtKey ext_key;
+ ext_key.SetSeed(std::array<std::byte, 32>{});
+ const std::string descriptor_without_checksum{"wpkh(" + EncodeExtPubKey(ext_key.Neuter()) + "/*)"};
+ const std::string descriptor{descriptor_without_checksum + "#" + GetDescriptorChecksum(descriptor_without_checksum)};
+
+ const std::array invalid_ranges{
+ std::pair{std::pair<int64_t, int64_t>{2, 1}, "Range specified as [begin,end] must not have begin after end"},
+ std::pair{std::pair<int64_t, int64_t>{-1, 10}, "Range should be greater or equal than 0"},
+ std::pair{std::pair<int64_t, int64_t>{0, 1'000'000}, "Range is too large"},
+ std::pair{std::pair<int64_t, int64_t>{0, std::numeric_limits<int64_t>::max()}, "End of range is too high"},
+ std::pair{std::pair<int64_t, int64_t>{std::numeric_limits<int32_t>::max(), std::numeric_limits<int32_t>::max()}, "End of range is too high"},
+ };
+
+ for (const auto& [range, expected_error] : invalid_ranges) {
+ std::vector requests{ImportDescriptorRequest{
+ .descriptor = descriptor,
+ .label = {},
+ .timestamp = 0,
+ .active = false,
+ .internal = std::nullopt,
+ .range = range,
+ .next_index = std::nullopt,
+ }};
+ const auto results{ProcessDescriptorsImport(m_wallet, requests)};
+ BOOST_REQUIRE_EQUAL(results.size(), 1U);
+ BOOST_REQUIRE(results.front().error.has_value());
+ BOOST_CHECK(results.front().error->wallet_error.code == WalletErrorCode::InvalidParameter);
+ BOOST_CHECK_EQUAL(results.front().error->wallet_error.message.original, expected_error);
+ BOOST_CHECK(!results.front().error->is_general_error);
+ }
+}
+
BOOST_FIXTURE_TEST_CASE(update_non_range_descriptor, TestingSetup)
{
CWallet wallet(m_node.chain.get(), "", CreateMockableWalletDatabase());
</details>