diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -2499,12 +2499,7 @@ desc_str = desc_uni.get_str(); UniValue range_uni = find_value(scanobject, "range"); if (!range_uni.isNull()) { - range = ParseRange(range_uni); - if (range.first < 0 || (range.second >> 31) != 0 || - range.second >= range.first + 1000000) { - throw JSONRPCError(RPC_INVALID_PARAMETER, - "range out of range"); - } + range = ParseDescriptorRange(range_uni); } } else { throw JSONRPCError( diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -23,6 +23,7 @@ #include #include +#include #ifdef HAVE_MALLOC_INFO #include #endif @@ -243,20 +244,8 @@ int64_t range_end = 0; if (request.params.size() >= 2 && !request.params[1].isNull()) { - auto range = ParseRange(request.params[1]); - if (range.first < 0) { - throw JSONRPCError(RPC_INVALID_PARAMETER, - "Range should be greater or equal than 0"); - } - if ((range.second >> 31) != 0) { - throw JSONRPCError(RPC_INVALID_PARAMETER, - "End of range is too high"); - } - if (range.second >= range.first + 1000000) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Range is too large"); - } - range_begin = range.first; - range_end = range.second; + std::tie(range_begin, range_end) = + ParseDescriptorRange(request.params[1]); } FlatSigningProvider provider; diff --git a/src/rpc/util.h b/src/rpc/util.h --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -88,7 +88,7 @@ const std::string &err_string = ""); //! Parse a JSON range specified as int64, or [int64, int64] -std::pair ParseRange(const UniValue &value); +std::pair ParseDescriptorRange(const UniValue &value); struct RPCArg { enum class Type { diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -13,6 +13,8 @@ #include +#include + #include void RPCTypeCheck(const UniValue ¶ms, @@ -692,7 +694,7 @@ assert(false); } -std::pair ParseRange(const UniValue &value) { +static std::pair ParseRange(const UniValue &value) { if (value.isNum()) { return {0, value.get_int64()}; } @@ -709,3 +711,19 @@ throw JSONRPCError(RPC_INVALID_PARAMETER, "Range must be specified as end or as [begin,end]"); } + +std::pair ParseDescriptorRange(const UniValue &value) { + int64_t low, high; + std::tie(low, high) = ParseRange(value); + if (low < 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + "Range should be greater or equal than 0"); + } + if ((high >> 31) != 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "End of range is too high"); + } + if (high >= low + 1000000) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Range is too large"); + } + return {low, high}; +} diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -26,6 +26,7 @@ #include #include +#include static std::string EncodeDumpString(const std::string &str) { std::stringstream ret; @@ -1287,14 +1288,7 @@ RPC_INVALID_PARAMETER, "Descriptor is ranged, please specify the range"); } - auto range = ParseRange(data["range"]); - range_start = range.first; - range_end = range.second; - if (range_start < 0 || (range_end >> 31) != 0 || - range_end - range_start >= 1000000) { - throw JSONRPCError(RPC_INVALID_PARAMETER, - "Invalid descriptor range specified"); - } + std::tie(range_start, range_end) = ParseDescriptorRange(data["range"]); } const UniValue &priv_keys = diff --git a/test/functional/rpc_scantxoutset.py b/test/functional/rpc_scantxoutset.py --- a/test/functional/rpc_scantxoutset.py +++ b/test/functional/rpc_scantxoutset.py @@ -8,7 +8,7 @@ from decimal import Decimal from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal +from test_framework.util import assert_equal, assert_raises_rpc_error def descriptors(out): @@ -89,6 +89,42 @@ assert_equal(self.nodes[0].scantxoutset( "start", ["addr(" + addr + ")"])['total_amount'], Decimal("0.002")) + self.log.info("Test range validation.") + assert_raises_rpc_error(-8, + "End of range is too high", + self.nodes[0].scantxoutset, + "start", + [{"desc": "desc", + "range": -1}]) + assert_raises_rpc_error(-8, + "Range should be greater or equal than 0", + self.nodes[0].scantxoutset, + "start", + [{"desc": "desc", + "range": [-1, + 10]}]) + assert_raises_rpc_error(-8, + "End of range is too high", + self.nodes[0].scantxoutset, + "start", + [{"desc": "desc", + "range": [(2 << 31 + 1) - 1000000, + (2 << 31 + 1)]}]) + assert_raises_rpc_error(-8, + "Range specified as [begin,end] must not have begin after end", + self.nodes[0].scantxoutset, + "start", + [{"desc": "desc", + "range": [2, + 1]}]) + assert_raises_rpc_error(-8, + "Range is too large", + self.nodes[0].scantxoutset, + "start", + [{"desc": "desc", + "range": [0, + 1000001]}]) + self.log.info("Test extended key derivation.") # Run various scans, and verify that the sum of the amounts of the matches corresponds to the expected subset. # Note that all amounts in the UTXO set are powers of 2 multiplied by diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py --- a/test/functional/wallet_importmulti.py +++ b/test/functional/wallet_importmulti.py @@ -479,6 +479,27 @@ error_code=-5, error_message='Descriptor is invalid') + xpriv = "tprv8ZgxMBicQKsPeuVhWwi6wuMQGfPKi9Li5GtX35jVNknACgqe3CY4g5xgkfDDJcmtF7o1QnxWDRYw4H5P26PXq7sbcUkEqeR4fg3Kxp2tigg" + # hdkeypath=m/0'/0'/0' and 1' + addresses = [ + "2N7yv4p8G8yEaPddJxY41kPihnWvs39qCMf", + "2MsHxyb2JS3pAySeNUsJ7mNnurtpeenDzLA"] + desc = "sh(pkh(" + xpriv + "/0'/0'/*'" + "))" + self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": -1}, + success=False, error_code=-8, error_message='End of range is too high') + + self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": [-1, 10]}, + success=False, error_code=-8, error_message='Range should be greater or equal than 0') + + self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": [(2 << 31 + 1) - 1000000, (2 << 31 + 1)]}, + success=False, error_code=-8, error_message='End of range is too high') + + self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": [2, 1]}, + success=False, error_code=-8, error_message='Range specified as [begin,end] must not have begin after end') + + self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": [0, 1000001]}, + success=False, error_code=-8, error_message='Range is too large') + # Test importing of a P2PKH address via descriptor key = get_key(self.nodes[0]) self.log.info("Should import a p2pkh address from descriptor")