From faf82e8fc819b2f1f8b60983ac72cb111c47e8ba Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Fri, 3 Jun 2016 19:07:08 +0200 Subject: [PATCH 1/2] [rpc] fundrawtransaction: Fix help text and interface --- qa/rpc-tests/fundrawtransaction.py | 2 +- src/wallet/rpcwallet.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/qa/rpc-tests/fundrawtransaction.py b/qa/rpc-tests/fundrawtransaction.py index 5c11d3ab1..3040752dc 100755 --- a/qa/rpc-tests/fundrawtransaction.py +++ b/qa/rpc-tests/fundrawtransaction.py @@ -681,7 +681,7 @@ class RawTransactionsTest(BitcoinTestFramework): inputs = [] outputs = {self.nodes[2].getnewaddress() : 1} rawtx = self.nodes[3].createrawtransaction(inputs, outputs) - result = self.nodes[3].fundrawtransaction(rawtx, ) + result = self.nodes[3].fundrawtransaction(rawtx) # 1000 sat via settxfee result2 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2000}) result3 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 10000}) assert_equal(result['fee']*2, result2['fee']) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 5e6afcd7c..68895ac52 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2369,13 +2369,13 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp) " \"changePosition\" (numeric, optional, default random) The index of the change output\n" " \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n" " \"lockUnspents\" (boolean, optional, default false) Lock selected unspent outputs\n" - " \"feeRate\" (numeric, optional, default 0=estimate) Set a specific feerate (fee per KB)\n" + " \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific feerate (satoshis per KB)\n" " }\n" " for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}\n" "\nResult:\n" "{\n" " \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n" - " \"fee\": n, (numeric) Fee the resulting transaction pays\n" + " \"fee\": n, (numeric) Fee in " + CURRENCY_UNIT + " the resulting transaction pays\n" " \"changepos\": n (numeric) The position of the added change output, or -1\n" "}\n" "\"hex\" \n" From fa7f4f577cbab2b4bc03b5427704c2ec16680c34 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Mon, 6 Jun 2016 17:50:50 +0200 Subject: [PATCH 2/2] [rpc] fundrawtransaction feeRate: Use BTC/kB Also introduce UniValueType UniValueType is a wrapper for UniValue::VType which allows setting a typeAny flag. This flag indicates the type does not matter. (Used by RPCTypeCheckObj) --- qa/rpc-tests/fundrawtransaction.py | 6 +++--- src/rpc/rawtransaction.cpp | 15 +++++++++++++-- src/rpc/server.cpp | 14 ++++++-------- src/rpc/server.h | 15 ++++++++++++--- src/wallet/rpcwallet.cpp | 20 ++++++++++++++++---- 5 files changed, 50 insertions(+), 20 deletions(-) diff --git a/qa/rpc-tests/fundrawtransaction.py b/qa/rpc-tests/fundrawtransaction.py index 3040752dc..998f822af 100755 --- a/qa/rpc-tests/fundrawtransaction.py +++ b/qa/rpc-tests/fundrawtransaction.py @@ -681,9 +681,9 @@ class RawTransactionsTest(BitcoinTestFramework): inputs = [] outputs = {self.nodes[2].getnewaddress() : 1} rawtx = self.nodes[3].createrawtransaction(inputs, outputs) - result = self.nodes[3].fundrawtransaction(rawtx) # 1000 sat via settxfee - result2 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2000}) - result3 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 10000}) + result = self.nodes[3].fundrawtransaction(rawtx) # uses min_relay_tx_fee (set by settxfee) + result2 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2*min_relay_tx_fee}) + result3 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 10*min_relay_tx_fee}) assert_equal(result['fee']*2, result2['fee']) assert_equal(result['fee']*10, result3['fee']) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 483fe746c..f92ddb282 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -675,7 +675,12 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) UniValue prevOut = p.get_obj(); - RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR)); + RPCTypeCheckObj(prevOut, + { + {"txid", UniValueType(UniValue::VSTR)}, + {"vout", UniValueType(UniValue::VNUM)}, + {"scriptPubKey", UniValueType(UniValue::VSTR)}, + }); uint256 txid = ParseHashO(prevOut, "txid"); @@ -703,7 +708,13 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) // if redeemScript given and not using the local wallet (private keys // given), add redeemScript to the tempKeystore so it can be signed: if (fGivenKeys && scriptPubKey.IsPayToScriptHash()) { - RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR)("redeemScript",UniValue::VSTR)); + RPCTypeCheckObj(prevOut, + { + {"txid", UniValueType(UniValue::VSTR)}, + {"vout", UniValueType(UniValue::VNUM)}, + {"scriptPubKey", UniValueType(UniValue::VSTR)}, + {"redeemScript", UniValueType(UniValue::VSTR)}, + }); UniValue v = find_value(prevOut, "redeemScript"); if (!v.isNull()) { vector rsData(ParseHexV(v, "redeemScript")); diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index d06a9142b..23149baa6 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -88,20 +88,18 @@ void RPCTypeCheck(const UniValue& params, } void RPCTypeCheckObj(const UniValue& o, - const map& typesExpected, - bool fAllowNull, - bool fStrict) + const map& typesExpected, + bool fAllowNull, + bool fStrict) { - BOOST_FOREACH(const PAIRTYPE(string, UniValue::VType)& t, typesExpected) - { + for (const auto& t : typesExpected) { const UniValue& v = find_value(o, t.first); if (!fAllowNull && v.isNull()) throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first)); - if (!((v.type() == t.second) || (fAllowNull && (v.isNull())))) - { + if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull()))) { string err = strprintf("Expected type %s for %s, got %s", - uvTypeName(t.second), t.first, uvTypeName(v.type())); + uvTypeName(t.second.type), t.first, uvTypeName(v.type())); throw JSONRPCError(RPC_TYPE_ERROR, err); } } diff --git a/src/rpc/server.h b/src/rpc/server.h index b47133661..b5ccc153d 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -32,6 +32,15 @@ namespace RPCServer class CBlockIndex; class CNetAddr; +/** Wrapper for UniValue::VType, which includes typeAny: + * Used to denote don't care type. Only used by RPCTypeCheckObj */ +struct UniValueType { + UniValueType(UniValue::VType _type) : typeAny(false), type(_type) {} + UniValueType() : typeAny(true) {} + bool typeAny; + UniValue::VType type; +}; + class JSONRequest { public: @@ -60,17 +69,17 @@ bool RPCIsInWarmup(std::string *statusOut); /** * Type-check arguments; throws JSONRPCError if wrong type given. Does not check that * the right number of arguments are passed, just that any passed are the correct type. - * Use like: RPCTypeCheck(params, boost::assign::list_of(str_type)(int_type)(obj_type)); */ void RPCTypeCheck(const UniValue& params, const std::list& typesExpected, bool fAllowNull=false); /* Check for expected keys/value types in an Object. - Use like: RPCTypeCheckObj(object, boost::assign::map_list_of("name", str_type)("value", int_type)); */ void RPCTypeCheckObj(const UniValue& o, - const std::map& typesExpected, bool fAllowNull=false, bool fStrict=false); + const std::map& typesExpected, + bool fAllowNull = false, + bool fStrict = false); /** Opaque base class for timers returned by NewTimerFunc. * This provides no methods at the moment, but makes sure that delete diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 68895ac52..1300e39aa 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2069,7 +2069,11 @@ UniValue lockunspent(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object"); const UniValue& o = output.get_obj(); - RPCTypeCheckObj(o, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)); + RPCTypeCheckObj(o, + { + {"txid", UniValueType(UniValue::VSTR)}, + {"vout", UniValueType(UniValue::VNUM)}, + }); string txid = find_value(o, "txid").get_str(); if (!IsHex(txid)) @@ -2369,7 +2373,7 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp) " \"changePosition\" (numeric, optional, default random) The index of the change output\n" " \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n" " \"lockUnspents\" (boolean, optional, default false) Lock selected unspent outputs\n" - " \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific feerate (satoshis per KB)\n" + " \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific feerate (" + CURRENCY_UNIT + " per KB)\n" " }\n" " for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}\n" "\nResult:\n" @@ -2409,7 +2413,15 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp) UniValue options = params[1]; - RPCTypeCheckObj(options, boost::assign::map_list_of("changeAddress", UniValue::VSTR)("changePosition", UniValue::VNUM)("includeWatching", UniValue::VBOOL)("lockUnspents", UniValue::VBOOL)("feeRate", UniValue::VNUM), true, true); + RPCTypeCheckObj(options, + { + {"changeAddress", UniValueType(UniValue::VSTR)}, + {"changePosition", UniValueType(UniValue::VNUM)}, + {"includeWatching", UniValueType(UniValue::VBOOL)}, + {"lockUnspents", UniValueType(UniValue::VBOOL)}, + {"feeRate", UniValueType()}, // will be checked below + }, + true, true); if (options.exists("changeAddress")) { CBitcoinAddress address(options["changeAddress"].get_str()); @@ -2431,7 +2443,7 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp) if (options.exists("feeRate")) { - feeRate = CFeeRate(options["feeRate"].get_real()); + feeRate = CFeeRate(AmountFromValue(options["feeRate"])); overrideEstimatedFeerate = true; } }