Skip to content

Commit 1495975

Browse files
author
MarcoFalke
committed
Merge #15744: refactor: Extract ParseDescriptorRange
510c653 Extract ParseDescriptorRange (Ben Woosley) Pull request description: So as to be consistently informative when the checks fail, and to protect against unintentional divergence among the checks. ACKs for commit 510c65: meshcollider: Oh apologies, yes. Thanks :) utACK bitcoin/bitcoin@510c653 MarcoFalke: utACK 510c653 sipa: utACK 510c653 Tree-SHA512: b1f0792bfaa163890a20654a0fc2c4c4a996659916bf5f4a495662436b39326692a1a0c825caafd859e48c05f5dd1865c4f7c28092be5074edda3c94f94f9f8b
2 parents 87dbf89 + 510c653 commit 1495975

File tree

7 files changed

+48
-23
lines changed

7 files changed

+48
-23
lines changed

src/rpc/blockchain.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2244,8 +2244,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
22442244
desc_str = desc_uni.get_str();
22452245
UniValue range_uni = find_value(scanobject, "range");
22462246
if (!range_uni.isNull()) {
2247-
range = ParseRange(range_uni);
2248-
if (range.first < 0 || (range.second >> 31) != 0 || range.second >= range.first + 1000000) throw JSONRPCError(RPC_INVALID_PARAMETER, "range out of range");
2247+
range = ParseDescriptorRange(range_uni);
22492248
}
22502249
} else {
22512250
throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan object needs to be either a string or an object");

src/rpc/misc.cpp

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <warnings.h>
2525

2626
#include <stdint.h>
27+
#include <tuple>
2728
#ifdef HAVE_MALLOC_INFO
2829
#include <malloc.h>
2930
#endif
@@ -215,18 +216,7 @@ UniValue deriveaddresses(const JSONRPCRequest& request)
215216
int64_t range_end = 0;
216217

217218
if (request.params.size() >= 2 && !request.params[1].isNull()) {
218-
auto range = ParseRange(request.params[1]);
219-
if (range.first < 0) {
220-
throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should be greater or equal than 0");
221-
}
222-
if ((range.second >> 31) != 0) {
223-
throw JSONRPCError(RPC_INVALID_PARAMETER, "End of range is too high");
224-
}
225-
if (range.second >= range.first + 1000000) {
226-
throw JSONRPCError(RPC_INVALID_PARAMETER, "Range is too large");
227-
}
228-
range_begin = range.first;
229-
range_end = range.second;
219+
std::tie(range_begin, range_end) = ParseDescriptorRange(request.params[1]);
230220
}
231221

232222
FlatSigningProvider key_provider;

src/rpc/util.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include <tinyformat.h>
99
#include <util/strencodings.h>
1010

11+
#include <tuple>
12+
1113
InitInterfaces* g_rpc_interfaces = nullptr;
1214

1315
void RPCTypeCheck(const UniValue& params,
@@ -654,7 +656,7 @@ std::string RPCArg::ToString(const bool oneline) const
654656
assert(false);
655657
}
656658

657-
std::pair<int64_t, int64_t> ParseRange(const UniValue& value)
659+
static std::pair<int64_t, int64_t> ParseRange(const UniValue& value)
658660
{
659661
if (value.isNum()) {
660662
return {0, value.get_int64()};
@@ -667,3 +669,19 @@ std::pair<int64_t, int64_t> ParseRange(const UniValue& value)
667669
}
668670
throw JSONRPCError(RPC_INVALID_PARAMETER, "Range must be specified as end or as [begin,end]");
669671
}
672+
673+
std::pair<int64_t, int64_t> ParseDescriptorRange(const UniValue& value)
674+
{
675+
int64_t low, high;
676+
std::tie(low, high) = ParseRange(value);
677+
if (low < 0) {
678+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should be greater or equal than 0");
679+
}
680+
if ((high >> 31) != 0) {
681+
throw JSONRPCError(RPC_INVALID_PARAMETER, "End of range is too high");
682+
}
683+
if (high >= low + 1000000) {
684+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Range is too large");
685+
}
686+
return {low, high};
687+
}

src/rpc/util.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ RPCErrorCode RPCErrorFromTransactionError(TransactionError terr);
8181
UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_string = "");
8282

8383
//! Parse a JSON range specified as int64, or [int64, int64]
84-
std::pair<int64_t, int64_t> ParseRange(const UniValue& value);
84+
std::pair<int64_t, int64_t> ParseDescriptorRange(const UniValue& value);
8585

8686
struct RPCArg {
8787
enum class Type {

src/wallet/rpcdump.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <wallet/rpcwallet.h>
2323

2424
#include <stdint.h>
25+
#include <tuple>
2526

2627
#include <boost/algorithm/string.hpp>
2728
#include <boost/date_time/posix_time/posix_time.hpp>
@@ -1141,12 +1142,7 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID
11411142
if (!data.exists("range")) {
11421143
throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor is ranged, please specify the range");
11431144
}
1144-
auto range = ParseRange(data["range"]);
1145-
range_start = range.first;
1146-
range_end = range.second;
1147-
if (range_start < 0 || (range_end >> 31) != 0 || range_end - range_start >= 1000000) {
1148-
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid descriptor range specified");
1149-
}
1145+
std::tie(range_start, range_end) = ParseDescriptorRange(data["range"]);
11501146
}
11511147

11521148
const UniValue& priv_keys = data.exists("keys") ? data["keys"].get_array() : UniValue();

test/functional/rpc_scantxoutset.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55
"""Test the scantxoutset rpc call."""
66
from test_framework.test_framework import BitcoinTestFramework
7-
from test_framework.util import assert_equal
7+
from test_framework.util import assert_equal, assert_raises_rpc_error
88

99
from decimal import Decimal
1010
import shutil
@@ -67,6 +67,13 @@ def run_test(self):
6767
assert_equal(self.nodes[0].scantxoutset("start", [ "addr(" + addr_P2SH_SEGWIT + ")", "addr(" + addr_LEGACY + ")", "addr(" + addr_BECH32 + ")"])['total_amount'], Decimal("0.007"))
6868
assert_equal(self.nodes[0].scantxoutset("start", [ "addr(" + addr_P2SH_SEGWIT + ")", "addr(" + addr_LEGACY + ")", "combo(" + pubk3 + ")"])['total_amount'], Decimal("0.007"))
6969

70+
self.log.info("Test range validation.")
71+
assert_raises_rpc_error(-8, "End of range is too high", self.nodes[0].scantxoutset, "start", [ {"desc": "desc", "range": -1}])
72+
assert_raises_rpc_error(-8, "Range should be greater or equal than 0", self.nodes[0].scantxoutset, "start", [ {"desc": "desc", "range": [-1, 10]}])
73+
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)]}])
74+
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]}])
75+
assert_raises_rpc_error(-8, "Range is too large", self.nodes[0].scantxoutset, "start", [ {"desc": "desc", "range": [0, 1000001]}])
76+
7077
self.log.info("Test extended key derivation.")
7178
# Run various scans, and verify that the sum of the amounts of the matches corresponds to the expected subset.
7279
# Note that all amounts in the UTXO set are powers of 2 multiplied by 0.001 BTC, so each amounts uniquely identifies a subset.

test/functional/wallet_importmulti.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,21 @@ def run_test(self):
591591
key.p2sh_p2wpkh_addr,
592592
solvable=True)
593593

594+
self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": -1},
595+
success=False, error_code=-8, error_message='End of range is too high')
596+
597+
self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": [-1, 10]},
598+
success=False, error_code=-8, error_message='Range should be greater or equal than 0')
599+
600+
self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": [(2 << 31 + 1) - 1000000, (2 << 31 + 1)]},
601+
success=False, error_code=-8, error_message='End of range is too high')
602+
603+
self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": [2, 1]},
604+
success=False, error_code=-8, error_message='Range specified as [begin,end] must not have begin after end')
605+
606+
self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": [0, 1000001]},
607+
success=False, error_code=-8, error_message='Range is too large')
608+
594609
# Test importing of a P2PKH address via descriptor
595610
key = get_key(self.nodes[0])
596611
self.log.info("Should import a p2pkh address from descriptor")

0 commit comments

Comments
 (0)