Skip to content
This repository has been archived by the owner on Dec 15, 2022. It is now read-only.

Commit

Permalink
Merge bitcoin#11050: Avoid treating null RPC arguments different from…
Browse files Browse the repository at this point in the history
… missing arguments

745d2e3 Clean up getbalance RPC parameter handling (Russell Yanofsky)
fd5d71e Update developer notes after params.size() cleanup (Russell Yanofsky)
e067673 Avoid treating null RPC arguments different from missing arguments (Russell Yanofsky)
e666efc Get rid of redundant RPC params.size() checks (Russell Yanofsky)

Pull request description:

  This is a followup to bitcoin#10783.

  - The first commit doesn't change behavior at all, just simplifies code.
  - The second commit just changes RPC methods to treat null arguments the same as missing arguments instead of throwing type errors.
  - The third commit updates developer notes after the cleanup.
  - The forth commit does some additional code cleanup in `getbalance`.

  Followup changes that should happen in future PRs:

  - [ ] Replace uses of `.isTrue()` with calls to `.get_bool()` so numbers, objects, and strings cause type errors instead of being interpreted as false. bitcoin#11050 (comment)
  - [ ] Add braces around if statements. bitcoin#11050 (comment)
  - [ ] Maybe improve UniValue type error exceptions and eliminate RPCTypeCheck and RPCTypeCheckArgument functions. bitcoin#11050 (comment)

Tree-SHA512: e72f696011d20acc0778e996659e41f9426bffce387b29ff63bf59ad1163d5146761e4445b2b9b9e069a80596a57c7f4402b75a15d5d20f69f775ae558cf67e9
  • Loading branch information
laanwj committed Aug 22, 2017
2 parents ea3ac59 + 745d2e3 commit 7ed57d3
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 61 deletions.
10 changes: 4 additions & 6 deletions doc/developer-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -572,16 +572,14 @@ A few guidelines for introducing and reviewing new RPC interfaces:
is specified as-is in BIP22.

- Missing arguments and 'null' should be treated the same: as default values. If there is no
default value, both cases should fail in the same way.
default value, both cases should fail in the same way. The easiest way to follow this
guideline is detect unspecified arguments with `params[x].isNull()` instead of
`params.size() <= x`. The former returns true if the argument is either null or missing,
while the latter returns true if is missing, and false if it is null.

- *Rationale*: Avoids surprises when switching to name-based arguments. Missing name-based arguments
are passed as 'null'.

- *Exception*: Many legacy exceptions to this exist, one of the worst ones is
`getbalance` which follows a completely different code path based on the
number of arguments. We are still in the process of cleaning these up. Do not introduce
new ones.

- Try not to overload methods on argument type. E.g. don't make `getblock(true)` and `getblock("hash")`
do different things.

Expand Down
4 changes: 2 additions & 2 deletions src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1489,11 +1489,11 @@ UniValue getchaintxstats(const JSONRPCRequest& request)
const CBlockIndex* pindex;
int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month

if (request.params.size() > 0 && !request.params[0].isNull()) {
if (!request.params[0].isNull()) {
blockcount = request.params[0].get_int();
}

bool havehash = request.params.size() > 1 && !request.params[1].isNull();
bool havehash = !request.params[1].isNull();
uint256 hash;
if (havehash) {
hash = uint256S(request.params[1].get_str());
Expand Down
2 changes: 1 addition & 1 deletion src/rpc/mining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -842,7 +842,7 @@ UniValue estimatesmartfee(const JSONRPCRequest& request)
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
unsigned int conf_target = ParseConfirmTarget(request.params[0]);
bool conservative = true;
if (request.params.size() > 1 && !request.params[1].isNull()) {
if (!request.params[1].isNull()) {
FeeEstimateMode fee_mode;
if (!FeeModeFromString(request.params[1].get_str(), fee_mode)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
Expand Down
6 changes: 3 additions & 3 deletions src/rpc/misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@ UniValue getmemoryinfo(const JSONRPCRequest& request)
+ HelpExampleRpc("getmemoryinfo", "")
);

std::string mode = (request.params.size() < 1 || request.params[0].isNull()) ? "stats" : request.params[0].get_str();
std::string mode = request.params[0].isNull() ? "stats" : request.params[0].get_str();
if (mode == "stats") {
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("locked", RPCLockedMemoryInfo()));
Expand Down Expand Up @@ -603,11 +603,11 @@ UniValue logging(const JSONRPCRequest& request)
}

uint32_t originalLogCategories = logCategories;
if (request.params.size() > 0 && request.params[0].isArray()) {
if (request.params[0].isArray()) {
logCategories |= getCategoryMask(request.params[0]);
}

if (request.params.size() > 1 && request.params[1].isArray()) {
if (request.params[1].isArray()) {
logCategories &= ~getCategoryMask(request.params[1]);
}

Expand Down
12 changes: 6 additions & 6 deletions src/rpc/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request)
UniValue addnode(const JSONRPCRequest& request)
{
std::string strCommand;
if (request.params.size() == 2)
if (!request.params[1].isNull())
strCommand = request.params[1].get_str();
if (request.fHelp || request.params.size() != 2 ||
(strCommand != "onetry" && strCommand != "add" && strCommand != "remove"))
Expand Down Expand Up @@ -258,7 +258,7 @@ UniValue disconnectnode(const JSONRPCRequest& request)

bool success;
const UniValue &address_arg = request.params[0];
const UniValue &id_arg = request.params.size() < 2 ? NullUniValue : request.params[1];
const UniValue &id_arg = request.params[1];

if (!address_arg.isNull() && id_arg.isNull()) {
/* handle disconnect-by-address */
Expand Down Expand Up @@ -311,7 +311,7 @@ UniValue getaddednodeinfo(const JSONRPCRequest& request)

std::vector<AddedNodeInfo> vInfo = g_connman->GetAddedNodeInfo();

if (request.params.size() == 1 && !request.params[0].isNull()) {
if (!request.params[0].isNull()) {
bool found = false;
for (const AddedNodeInfo& info : vInfo) {
if (info.strAddedNode == request.params[0].get_str()) {
Expand Down Expand Up @@ -490,7 +490,7 @@ UniValue getnetworkinfo(const JSONRPCRequest& request)
UniValue setban(const JSONRPCRequest& request)
{
std::string strCommand;
if (request.params.size() >= 2)
if (!request.params[1].isNull())
strCommand = request.params[1].get_str();
if (request.fHelp || request.params.size() < 2 ||
(strCommand != "add" && strCommand != "remove"))
Expand Down Expand Up @@ -534,11 +534,11 @@ UniValue setban(const JSONRPCRequest& request)
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned");

int64_t banTime = 0; //use standard bantime if not specified
if (request.params.size() >= 3 && !request.params[2].isNull())
if (!request.params[2].isNull())
banTime = request.params[2].get_int64();

bool absolute = false;
if (request.params.size() == 4 && request.params[3].isTrue())
if (request.params[3].isTrue())
absolute = true;

isSubnet ? g_connman->Ban(subNet, BanReasonManuallyAdded, banTime, absolute) : g_connman->Ban(netAddr, BanReasonManuallyAdded, banTime, absolute);
Expand Down
12 changes: 6 additions & 6 deletions src/rpc/rawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -336,14 +336,14 @@ UniValue createrawtransaction(const JSONRPCRequest& request)

CMutableTransaction rawTx;

if (request.params.size() > 2 && !request.params[2].isNull()) {
if (!request.params[2].isNull()) {
int64_t nLockTime = request.params[2].get_int64();
if (nLockTime < 0 || nLockTime > std::numeric_limits<uint32_t>::max())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
rawTx.nLockTime = nLockTime;
}

bool rbfOptIn = request.params.size() > 3 ? request.params[3].isTrue() : false;
bool rbfOptIn = request.params[3].isTrue();

for (unsigned int idx = 0; idx < inputs.size(); idx++) {
const UniValue& input = inputs[idx];
Expand Down Expand Up @@ -732,7 +732,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)

bool fGivenKeys = false;
CBasicKeyStore tempKeystore;
if (request.params.size() > 2 && !request.params[2].isNull()) {
if (!request.params[2].isNull()) {
fGivenKeys = true;
UniValue keys = request.params[2].get_array();
for (unsigned int idx = 0; idx < keys.size(); idx++) {
Expand All @@ -754,7 +754,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
#endif

// Add previous txouts given in the RPC call:
if (request.params.size() > 1 && !request.params[1].isNull()) {
if (!request.params[1].isNull()) {
UniValue prevTxs = request.params[1].get_array();
for (unsigned int idx = 0; idx < prevTxs.size(); idx++) {
const UniValue& p = prevTxs[idx];
Expand Down Expand Up @@ -825,7 +825,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
#endif

int nHashType = SIGHASH_ALL;
if (request.params.size() > 3 && !request.params[3].isNull()) {
if (!request.params[3].isNull()) {
static std::map<std::string, int> mapSigHashValues = {
{std::string("ALL"), int(SIGHASH_ALL)},
{std::string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)},
Expand Down Expand Up @@ -919,7 +919,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
const uint256& hashTx = tx->GetHash();

CAmount nMaxRawTxFee = maxTxFee;
if (request.params.size() > 1 && request.params[1].get_bool())
if (!request.params[1].isNull() && request.params[1].get_bool())
nMaxRawTxFee = 0;

CCoinsViewCache &view = *pcoinsTip;
Expand Down
86 changes: 49 additions & 37 deletions src/wallet/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ UniValue setaccount(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");

std::string strAccount;
if (request.params.size() > 1)
if (!request.params[1].isNull())
strAccount = AccountFromValue(request.params[1]);

// Only add the account if the address is yours.
Expand Down Expand Up @@ -462,26 +462,26 @@ UniValue sendtoaddress(const JSONRPCRequest& request)

// Wallet comments
CWalletTx wtx;
if (request.params.size() > 2 && !request.params[2].isNull() && !request.params[2].get_str().empty())
if (!request.params[2].isNull() && !request.params[2].get_str().empty())
wtx.mapValue["comment"] = request.params[2].get_str();
if (request.params.size() > 3 && !request.params[3].isNull() && !request.params[3].get_str().empty())
if (!request.params[3].isNull() && !request.params[3].get_str().empty())
wtx.mapValue["to"] = request.params[3].get_str();

bool fSubtractFeeFromAmount = false;
if (request.params.size() > 4 && !request.params[4].isNull()) {
if (!request.params[4].isNull()) {
fSubtractFeeFromAmount = request.params[4].get_bool();
}

CCoinControl coin_control;
if (request.params.size() > 5 && !request.params[5].isNull()) {
if (!request.params[5].isNull()) {
coin_control.signalRbf = request.params[5].get_bool();
}

if (request.params.size() > 6 && !request.params[6].isNull()) {
if (!request.params[6].isNull()) {
coin_control.m_confirm_target = ParseConfirmTarget(request.params[6]);
}

if (request.params.size() > 7 && !request.params[7].isNull()) {
if (!request.params[7].isNull()) {
if (!FeeModeFromString(request.params[7].get_str(), coin_control.m_fee_mode)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
}
Expand Down Expand Up @@ -768,18 +768,31 @@ UniValue getbalance(const JSONRPCRequest& request)

LOCK2(cs_main, pwallet->cs_wallet);

if (request.params.size() == 0)
return ValueFromAmount(pwallet->GetBalance());
const UniValue& account_value = request.params[0];
const UniValue& minconf = request.params[1];
const UniValue& include_watchonly = request.params[2];

if (account_value.isNull()) {
if (!minconf.isNull()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"getbalance minconf option is only currently supported if an account is specified");
}
if (!include_watchonly.isNull()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"getbalance include_watchonly option is only currently supported if an account is specified");
}
return ValueFromAmount(pwallet->GetBalance());
}

const std::string& account_param = request.params[0].get_str();
const std::string& account_param = account_value.get_str();
const std::string* account = account_param != "*" ? &account_param : nullptr;

int nMinDepth = 1;
if (!request.params[1].isNull())
nMinDepth = request.params[1].get_int();
if (!minconf.isNull())
nMinDepth = minconf.get_int();
isminefilter filter = ISMINE_SPENDABLE;
if(!request.params[2].isNull())
if(request.params[2].get_bool())
if(!include_watchonly.isNull())
if(include_watchonly.get_bool())
filter = filter | ISMINE_WATCH_ONLY;

return ValueFromAmount(pwallet->GetLegacyBalance(filter, nMinDepth, account));
Expand Down Expand Up @@ -838,11 +851,11 @@ UniValue movecmd(const JSONRPCRequest& request)
CAmount nAmount = AmountFromValue(request.params[2]);
if (nAmount <= 0)
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
if (request.params.size() > 3)
if (!request.params[3].isNull())
// unused parameter, used to be nMinDepth, keep type-checking it though
(void)request.params[3].get_int();
std::string strComment;
if (request.params.size() > 4)
if (!request.params[4].isNull())
strComment = request.params[4].get_str();

if (!pwallet->AccountMove(strFrom, strTo, nAmount, strComment)) {
Expand Down Expand Up @@ -899,14 +912,14 @@ UniValue sendfrom(const JSONRPCRequest& request)
if (nAmount <= 0)
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
int nMinDepth = 1;
if (request.params.size() > 3)
if (!request.params[3].isNull())
nMinDepth = request.params[3].get_int();

CWalletTx wtx;
wtx.strFromAccount = strAccount;
if (request.params.size() > 4 && !request.params[4].isNull() && !request.params[4].get_str().empty())
if (!request.params[4].isNull() && !request.params[4].get_str().empty())
wtx.mapValue["comment"] = request.params[4].get_str();
if (request.params.size() > 5 && !request.params[5].isNull() && !request.params[5].get_str().empty())
if (!request.params[5].isNull() && !request.params[5].get_str().empty())
wtx.mapValue["to"] = request.params[5].get_str();

EnsureWalletIsUnlocked(pwallet);
Expand Down Expand Up @@ -986,23 +999,23 @@ UniValue sendmany(const JSONRPCRequest& request)

CWalletTx wtx;
wtx.strFromAccount = strAccount;
if (request.params.size() > 3 && !request.params[3].isNull() && !request.params[3].get_str().empty())
if (!request.params[3].isNull() && !request.params[3].get_str().empty())
wtx.mapValue["comment"] = request.params[3].get_str();

UniValue subtractFeeFromAmount(UniValue::VARR);
if (request.params.size() > 4 && !request.params[4].isNull())
if (!request.params[4].isNull())
subtractFeeFromAmount = request.params[4].get_array();

CCoinControl coin_control;
if (request.params.size() > 5 && !request.params[5].isNull()) {
if (!request.params[5].isNull()) {
coin_control.signalRbf = request.params[5].get_bool();
}

if (request.params.size() > 6 && !request.params[6].isNull()) {
if (!request.params[6].isNull()) {
coin_control.m_confirm_target = ParseConfirmTarget(request.params[6]);
}

if (request.params.size() > 7 && !request.params[7].isNull()) {
if (!request.params[7].isNull()) {
if (!FeeModeFromString(request.params[7].get_str(), coin_control.m_fee_mode)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
}
Expand Down Expand Up @@ -1105,7 +1118,7 @@ UniValue addmultisigaddress(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet);

std::string strAccount;
if (request.params.size() > 2)
if (!request.params[2].isNull())
strAccount = AccountFromValue(request.params[2]);

// Construct using pay-to-script-hash:
Expand Down Expand Up @@ -1711,10 +1724,10 @@ UniValue listaccounts(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet);

int nMinDepth = 1;
if (request.params.size() > 0)
if (!request.params[0].isNull())
nMinDepth = request.params[0].get_int();
isminefilter includeWatchonly = ISMINE_SPENDABLE;
if(request.params.size() > 1)
if(!request.params[1].isNull())
if(request.params[1].get_bool())
includeWatchonly = includeWatchonly | ISMINE_WATCH_ONLY;

Expand Down Expand Up @@ -2363,19 +2376,18 @@ UniValue lockunspent(const JSONRPCRequest& request)

LOCK2(cs_main, pwallet->cs_wallet);

if (request.params.size() == 1)
RPCTypeCheck(request.params, {UniValue::VBOOL});
else
RPCTypeCheck(request.params, {UniValue::VBOOL, UniValue::VARR});
RPCTypeCheckArgument(request.params[0], UniValue::VBOOL);

bool fUnlock = request.params[0].get_bool();

if (request.params.size() == 1) {
if (request.params[1].isNull()) {
if (fUnlock)
pwallet->UnlockAllCoins();
return true;
}

RPCTypeCheckArgument(request.params[1], UniValue::VARR);

UniValue outputs = request.params[1].get_array();
for (unsigned int idx = 0; idx < outputs.size(); idx++) {
const UniValue& output = outputs[idx];
Expand Down Expand Up @@ -2672,19 +2684,19 @@ UniValue listunspent(const JSONRPCRequest& request)
);

int nMinDepth = 1;
if (request.params.size() > 0 && !request.params[0].isNull()) {
if (!request.params[0].isNull()) {
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
nMinDepth = request.params[0].get_int();
}

int nMaxDepth = 9999999;
if (request.params.size() > 1 && !request.params[1].isNull()) {
if (!request.params[1].isNull()) {
RPCTypeCheckArgument(request.params[1], UniValue::VNUM);
nMaxDepth = request.params[1].get_int();
}

std::set<CBitcoinAddress> setAddress;
if (request.params.size() > 2 && !request.params[2].isNull()) {
if (!request.params[2].isNull()) {
RPCTypeCheckArgument(request.params[2], UniValue::VARR);
UniValue inputs = request.params[2].get_array();
for (unsigned int idx = 0; idx < inputs.size(); idx++) {
Expand All @@ -2699,7 +2711,7 @@ UniValue listunspent(const JSONRPCRequest& request)
}

bool include_unsafe = true;
if (request.params.size() > 3 && !request.params[3].isNull()) {
if (!request.params[3].isNull()) {
RPCTypeCheckArgument(request.params[3], UniValue::VBOOL);
include_unsafe = request.params[3].get_bool();
}
Expand Down Expand Up @@ -3114,7 +3126,7 @@ UniValue generate(const JSONRPCRequest& request)

int num_generate = request.params[0].get_int();
uint64_t max_tries = 1000000;
if (request.params.size() > 1 && !request.params[1].isNull()) {
if (!request.params[1].isNull()) {
max_tries = request.params[1].get_int();
}

Expand Down

0 comments on commit 7ed57d3

Please sign in to comment.