Merge #15744: refactor: Extract ParseDescriptorRange
510c6532ba
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 :) utACK510c6532ba
MarcoFalke: utACK510c6532ba
sipa: utACK510c6532ba
Tree-SHA512: b1f0792bfaa163890a20654a0fc2c4c4a996659916bf5f4a495662436b39326692a1a0c825caafd859e48c05f5dd1865c4f7c28092be5074edda3c94f94f9f8b
This commit is contained in:
commit
14959753a4
7 changed files with 48 additions and 23 deletions
|
@ -2244,8 +2244,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
|
||||||
desc_str = desc_uni.get_str();
|
desc_str = desc_uni.get_str();
|
||||||
UniValue range_uni = find_value(scanobject, "range");
|
UniValue range_uni = find_value(scanobject, "range");
|
||||||
if (!range_uni.isNull()) {
|
if (!range_uni.isNull()) {
|
||||||
range = ParseRange(range_uni);
|
range = ParseDescriptorRange(range_uni);
|
||||||
if (range.first < 0 || (range.second >> 31) != 0 || range.second >= range.first + 1000000) throw JSONRPCError(RPC_INVALID_PARAMETER, "range out of range");
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan object needs to be either a string or an object");
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan object needs to be either a string or an object");
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <warnings.h>
|
#include <warnings.h>
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <tuple>
|
||||||
#ifdef HAVE_MALLOC_INFO
|
#ifdef HAVE_MALLOC_INFO
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -215,18 +216,7 @@ UniValue deriveaddresses(const JSONRPCRequest& request)
|
||||||
int64_t range_end = 0;
|
int64_t range_end = 0;
|
||||||
|
|
||||||
if (request.params.size() >= 2 && !request.params[1].isNull()) {
|
if (request.params.size() >= 2 && !request.params[1].isNull()) {
|
||||||
auto range = ParseRange(request.params[1]);
|
std::tie(range_begin, range_end) = ParseDescriptorRange(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatSigningProvider key_provider;
|
FlatSigningProvider key_provider;
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
#include <tinyformat.h>
|
#include <tinyformat.h>
|
||||||
#include <util/strencodings.h>
|
#include <util/strencodings.h>
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
InitInterfaces* g_rpc_interfaces = nullptr;
|
InitInterfaces* g_rpc_interfaces = nullptr;
|
||||||
|
|
||||||
void RPCTypeCheck(const UniValue& params,
|
void RPCTypeCheck(const UniValue& params,
|
||||||
|
@ -654,7 +656,7 @@ std::string RPCArg::ToString(const bool oneline) const
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<int64_t, int64_t> ParseRange(const UniValue& value)
|
static std::pair<int64_t, int64_t> ParseRange(const UniValue& value)
|
||||||
{
|
{
|
||||||
if (value.isNum()) {
|
if (value.isNum()) {
|
||||||
return {0, value.get_int64()};
|
return {0, value.get_int64()};
|
||||||
|
@ -667,3 +669,19 @@ std::pair<int64_t, int64_t> ParseRange(const UniValue& value)
|
||||||
}
|
}
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Range must be specified as end or as [begin,end]");
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Range must be specified as end or as [begin,end]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<int64_t, int64_t> 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};
|
||||||
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ RPCErrorCode RPCErrorFromTransactionError(TransactionError terr);
|
||||||
UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_string = "");
|
UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_string = "");
|
||||||
|
|
||||||
//! Parse a JSON range specified as int64, or [int64, int64]
|
//! Parse a JSON range specified as int64, or [int64, int64]
|
||||||
std::pair<int64_t, int64_t> ParseRange(const UniValue& value);
|
std::pair<int64_t, int64_t> ParseDescriptorRange(const UniValue& value);
|
||||||
|
|
||||||
struct RPCArg {
|
struct RPCArg {
|
||||||
enum class Type {
|
enum class Type {
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include <wallet/rpcwallet.h>
|
#include <wallet/rpcwallet.h>
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||||
|
@ -1141,12 +1142,7 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID
|
||||||
if (!data.exists("range")) {
|
if (!data.exists("range")) {
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor is ranged, please specify the range");
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor is ranged, please specify the range");
|
||||||
}
|
}
|
||||||
auto range = ParseRange(data["range"]);
|
std::tie(range_start, range_end) = ParseDescriptorRange(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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const UniValue& priv_keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
|
const UniValue& priv_keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
"""Test the scantxoutset rpc call."""
|
"""Test the scantxoutset rpc call."""
|
||||||
from test_framework.test_framework import BitcoinTestFramework
|
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
|
||||||
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
import shutil
|
import shutil
|
||||||
|
@ -67,6 +67,13 @@ class ScantxoutsetTest(BitcoinTestFramework):
|
||||||
assert_equal(self.nodes[0].scantxoutset("start", [ "addr(" + addr_P2SH_SEGWIT + ")", "addr(" + addr_LEGACY + ")", "addr(" + addr_BECH32 + ")"])['total_amount'], Decimal("0.007"))
|
assert_equal(self.nodes[0].scantxoutset("start", [ "addr(" + addr_P2SH_SEGWIT + ")", "addr(" + addr_LEGACY + ")", "addr(" + addr_BECH32 + ")"])['total_amount'], Decimal("0.007"))
|
||||||
assert_equal(self.nodes[0].scantxoutset("start", [ "addr(" + addr_P2SH_SEGWIT + ")", "addr(" + addr_LEGACY + ")", "combo(" + pubk3 + ")"])['total_amount'], Decimal("0.007"))
|
assert_equal(self.nodes[0].scantxoutset("start", [ "addr(" + addr_P2SH_SEGWIT + ")", "addr(" + addr_LEGACY + ")", "combo(" + pubk3 + ")"])['total_amount'], Decimal("0.007"))
|
||||||
|
|
||||||
|
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.")
|
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.
|
# 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 0.001 BTC, so each amounts uniquely identifies a subset.
|
# Note that all amounts in the UTXO set are powers of 2 multiplied by 0.001 BTC, so each amounts uniquely identifies a subset.
|
||||||
|
|
|
@ -591,6 +591,21 @@ class ImportMultiTest(BitcoinTestFramework):
|
||||||
key.p2sh_p2wpkh_addr,
|
key.p2sh_p2wpkh_addr,
|
||||||
solvable=True)
|
solvable=True)
|
||||||
|
|
||||||
|
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
|
# Test importing of a P2PKH address via descriptor
|
||||||
key = get_key(self.nodes[0])
|
key = get_key(self.nodes[0])
|
||||||
self.log.info("Should import a p2pkh address from descriptor")
|
self.log.info("Should import a p2pkh address from descriptor")
|
||||||
|
|
Loading…
Reference in a new issue