Convert tree to using univalue. Eliminate all json_spirit uses.

This commit is contained in:
Jeff Garzik 2014-08-20 15:15:16 -04:00 committed by Jonas Schnelli
parent 5e3060c0d1
commit 15982a8b69
23 changed files with 321 additions and 205 deletions

View file

@ -342,6 +342,7 @@ endif
bitcoin_cli_LDADD = \
$(LIBBITCOIN_CLI) \
$(LIBBITCOIN_UNIVALUE) \
$(LIBBITCOIN_UTIL) \
$(LIBSECP256K1)

View file

@ -143,7 +143,7 @@ Object CallRPC(const string& strMethod, const Array& params)
// Parse reply
Value valReply;
if (!read_string(strReply, valReply))
if (!valReply.read(strReply))
throw runtime_error("couldn't parse reply from server");
const Object& reply = valReply.get_obj();
if (reply.empty())
@ -176,29 +176,27 @@ int CommandLineRPC(int argc, char *argv[])
const bool fWait = GetBoolArg("-rpcwait", false);
do {
try {
const Object reply = CallRPC(strMethod, params);
// Execute
Object reply = CallRPC(strMethod, params);
// Parse reply
const Value& result = find_value(reply, "result");
const Value& error = find_value(reply, "error");
if (error.type() != null_type) {
if (!error.isNull()) {
// Error
const int code = find_value(error.get_obj(), "code").get_int();
if (fWait && code == RPC_IN_WARMUP)
throw CConnectionFailed("server in warmup");
strPrint = "error: " + write_string(error, false);
strPrint = "error: " + error.write();
int code = error["code"].get_int();
nRet = abs(code);
} else {
// Result
if (result.type() == null_type)
if (result.isNull())
strPrint = "";
else if (result.type() == str_type)
else if (result.isStr())
strPrint = result.get_str();
else
strPrint = write_string(result, true);
strPrint = result.write(2);
}
// Connection succeeded, no need to retry.
break;
}

17
src/json_spirit_wrapper.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef __JSON_SPIRIT_WRAPPER_H__
#define __JSON_SPIRIT_WRAPPER_H__
#include "univalue/univalue.h"
namespace json_spirit {
typedef UniValue Value;
typedef UniValue Array;
typedef UniValue Object;
typedef UniValue::VType Value_type;
}
#define find_value(val,key) (val[key])
#endif // __JSON_SPIRIT_WRAPPER_H__

View file

@ -16,10 +16,10 @@
#include "rpcclient.h"
#include "util.h"
#include "json/json_spirit_value.h"
#include <openssl/crypto.h>
#include "univalue/univalue.h"
#ifdef ENABLE_WALLET
#include <db_cxx.h>
#endif
@ -167,21 +167,25 @@ void RPCExecutor::request(const QString &command)
std::string strPrint;
// Convert argument list to JSON objects in method-dependent way,
// and pass it along with the method name to the dispatcher.
json_spirit::Value result = tableRPC.execute(
UniValue result = tableRPC.execute(
args[0],
RPCConvertValues(args[0], std::vector<std::string>(args.begin() + 1, args.end())));
// Format result reply
if (result.type() == json_spirit::null_type)
if (result.isNull())
strPrint = "";
else if (result.type() == json_spirit::str_type)
else if (result.isStr())
strPrint = result.get_str();
else
strPrint = write_string(result, true);
strPrint = result.write(2);
emit reply(RPCConsole::CMD_REPLY, QString::fromStdString(strPrint));
}
<<<<<<< HEAD
catch (const json_spirit::Object& objError)
=======
catch (UniValue& objError)
>>>>>>> Convert tree to using univalue. Eliminate all json_spirit uses.
{
try // Nice formatting for standard-format error
{
@ -191,7 +195,7 @@ void RPCExecutor::request(const QString &command)
}
catch (const std::runtime_error&) // raised when converting to invalid type, i.e. missing code or message
{ // Show raw JSON object
emit reply(RPCConsole::CMD_ERROR, QString::fromStdString(write_string(json_spirit::Value(objError), false)));
emit reply(RPCConsole::CMD_ERROR, QString::fromStdString(objError.write()));
}
}
catch (const std::exception& e)

View file

@ -13,7 +13,7 @@
#include <stdint.h>
#include "json/json_spirit_value.h"
#include "json_spirit_wrapper.h"
using namespace json_spirit;
using namespace std;
@ -206,7 +206,13 @@ Value getrawmempool(const Array& params, bool fHelp)
if (mempool.exists(txin.prevout.hash))
setDepends.insert(txin.prevout.hash.ToString());
}
Array depends(setDepends.begin(), setDepends.end());
UniValue depends;
BOOST_FOREACH(const string& dep, setDepends)
{
depends.push_back(dep);
}
info.push_back(Pair("depends", depends));
o.push_back(Pair(hash.ToString(), info));
}
@ -412,14 +418,14 @@ Value gettxout(const Array& params, bool fHelp)
LOCK(mempool.cs);
CCoinsViewMemPool view(pcoinsTip, mempool);
if (!view.GetCoins(hash, coins))
return Value::null;
return NullUniValue;
mempool.pruneSpent(hash, coins); // TODO: this should be done by the CCoinsViewMemPool
} else {
if (!pcoinsTip->GetCoins(hash, coins))
return Value::null;
return NullUniValue;
}
if (n<0 || (unsigned int)n>=coins.vout.size() || coins.vout[n].IsNull())
return Value::null;
return NullUniValue;
BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock());
CBlockIndex *pindex = it->second;

View file

@ -135,7 +135,7 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri
// parse string as JSON, insert bool/number/object/etc. value
else {
Value jVal;
if (!read_string(strVal, jVal))
if (!jVal.read(strVal))
throw runtime_error(string("Error parsing JSON:")+strVal);
params.push_back(jVal);
}

View file

@ -6,9 +6,7 @@
#ifndef BITCOIN_RPCCLIENT_H
#define BITCOIN_RPCCLIENT_H
#include "json/json_spirit_reader_template.h"
#include "json/json_spirit_utils.h"
#include "json/json_spirit_writer_template.h"
#include "json_spirit_wrapper.h"
json_spirit::Array RPCConvertValues(const std::string& strMethod, const std::vector<std::string>& strParams);

View file

@ -24,8 +24,7 @@
#include <boost/assign/list_of.hpp>
#include "json/json_spirit_utils.h"
#include "json/json_spirit_value.h"
#include "json_spirit_wrapper.h"
using namespace json_spirit;
using namespace std;
@ -216,7 +215,7 @@ Value setgenerate(const Array& params, bool fHelp)
mapArgs ["-genproclimit"] = itostr(nGenProcLimit);
GenerateBitcoins(fGenerate, pwalletMain, nGenProcLimit);
return Value::null;
return NullUniValue;
}
#endif
@ -382,14 +381,14 @@ Value getblocktemplate(const Array& params, bool fHelp)
LOCK(cs_main);
std::string strMode = "template";
Value lpval = Value::null;
Value lpval = NullUniValue;
if (params.size() > 0)
{
const Object& oparam = params[0].get_obj();
const Value& modeval = find_value(oparam, "mode");
if (modeval.type() == str_type)
if (modeval.isStr())
strMode = modeval.get_str();
else if (modeval.type() == null_type)
else if (modeval.isNull())
{
/* Do nothing */
}
@ -439,14 +438,14 @@ Value getblocktemplate(const Array& params, bool fHelp)
static unsigned int nTransactionsUpdatedLast;
if (lpval.type() != null_type)
if (!lpval.isNull())
{
// Wait to respond until either the best block changes, OR a minute has passed and there are more transactions
uint256 hashWatchedChain;
boost::system_time checktxtime;
unsigned int nTransactionsUpdatedLastLP;
if (lpval.type() == str_type)
if (lpval.isStr())
{
// Format: <hashBestChain><nTransactionsUpdatedLast>
std::string lpstr = lpval.get_str();
@ -686,7 +685,7 @@ Value estimatefee(const Array& params, bool fHelp)
+ HelpExampleCli("estimatefee", "6")
);
RPCTypeCheck(params, boost::assign::list_of(int_type));
RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM));
int nBlocks = params[0].get_int();
if (nBlocks < 1)
@ -718,7 +717,7 @@ Value estimatepriority(const Array& params, bool fHelp)
+ HelpExampleCli("estimatepriority", "6")
);
RPCTypeCheck(params, boost::assign::list_of(int_type));
RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM));
int nBlocks = params[0].get_int();
if (nBlocks < 1)

View file

@ -20,8 +20,7 @@
#include <stdint.h>
#include <boost/assign/list_of.hpp>
#include "json/json_spirit_utils.h"
#include "json/json_spirit_value.h"
#include "json_spirit_wrapper.h"
using namespace json_spirit;
using namespace std;
@ -204,7 +203,7 @@ Value validateaddress(const Array& params, bool fHelp)
if (mine != ISMINE_NO) {
ret.push_back(Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true: false));
Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest);
ret.insert(ret.end(), detail.begin(), detail.end());
ret.pushKVs(detail);
}
if (pwalletMain && pwalletMain->mapAddressBook.count(dest))
ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest].name));

View file

@ -16,7 +16,7 @@
#include <boost/foreach.hpp>
#include "json/json_spirit_value.h"
#include "json_spirit_wrapper.h"
using namespace json_spirit;
using namespace std;
@ -59,7 +59,7 @@ Value ping(const Array& params, bool fHelp)
pNode->fPingQueued = true;
}
return Value::null;
return NullUniValue;
}
static void CopyNodeStats(std::vector<CNodeStats>& vstats)
@ -190,7 +190,7 @@ Value addnode(const Array& params, bool fHelp)
{
CAddress addr;
OpenNetworkConnection(addr, NULL, strNode.c_str());
return Value::null;
return NullUniValue;
}
LOCK(cs_vAddedNodes);
@ -212,7 +212,7 @@ Value addnode(const Array& params, bool fHelp)
vAddedNodes.erase(it);
}
return Value::null;
return NullUniValue;
}
Value getaddednodeinfo(const Array& params, bool fHelp)

View file

@ -23,7 +23,7 @@
#include <boost/iostreams/concepts.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/shared_ptr.hpp>
#include "json/json_spirit_writer_template.h"
#include "json_spirit_wrapper.h"
using namespace std;
using namespace json_spirit;
@ -260,14 +260,14 @@ string JSONRPCRequest(const string& strMethod, const Array& params, const Value&
request.push_back(Pair("method", strMethod));
request.push_back(Pair("params", params));
request.push_back(Pair("id", id));
return write_string(Value(request), false) + "\n";
return request.write() + "\n";
}
Object JSONRPCReplyObj(const Value& result, const Value& error, const Value& id)
{
Object reply;
if (error.type() != null_type)
reply.push_back(Pair("result", Value::null));
if (!error.isNull())
reply.push_back(Pair("result", NullUniValue));
else
reply.push_back(Pair("result", result));
reply.push_back(Pair("error", error));
@ -278,7 +278,7 @@ Object JSONRPCReplyObj(const Value& result, const Value& error, const Value& id)
string JSONRPCReply(const Value& result, const Value& error, const Value& id)
{
Object reply = JSONRPCReplyObj(result, error, id);
return write_string(Value(reply), false) + "\n";
return reply.write() + "\n";
}
Object JSONRPCError(int code, const string& message)

View file

@ -15,9 +15,7 @@
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include "json/json_spirit_reader_template.h"
#include "json/json_spirit_utils.h"
#include "json/json_spirit_writer_template.h"
#include "json_spirit_wrapper.h"
//! HTTP status codes
enum HTTPStatusCode

View file

@ -25,8 +25,7 @@
#include <stdint.h>
#include <boost/assign/list_of.hpp>
#include "json/json_spirit_utils.h"
#include "json/json_spirit_value.h"
#include "json_spirit_wrapper.h"
using namespace json_spirit;
using namespace std;
@ -343,20 +342,21 @@ Value createrawtransaction(const Array& params, bool fHelp)
);
LOCK(cs_main);
RPCTypeCheck(params, boost::assign::list_of(array_type)(obj_type));
RPCTypeCheck(params, boost::assign::list_of(UniValue::VARR)(UniValue::VOBJ));
Array inputs = params[0].get_array();
Object sendTo = params[1].get_obj();
CMutableTransaction rawTx;
BOOST_FOREACH(const Value& input, inputs) {
for (unsigned int idx = 0; idx < inputs.size(); idx++) {
const Value& input = inputs[idx];
const Object& o = input.get_obj();
uint256 txid = ParseHashO(o, "txid");
const Value& vout_v = find_value(o, "vout");
if (vout_v.type() != int_type)
if (!vout_v.isNum())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
int nOutput = vout_v.get_int();
if (nOutput < 0)
@ -367,17 +367,18 @@ Value createrawtransaction(const Array& params, bool fHelp)
}
set<CBitcoinAddress> setAddress;
BOOST_FOREACH(const Pair& s, sendTo) {
CBitcoinAddress address(s.name_);
vector<string> addrList = sendTo.getKeys();
BOOST_FOREACH(const string& name_, addrList) {
CBitcoinAddress address(name_);
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+s.name_);
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+name_);
if (setAddress.count(address))
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_);
setAddress.insert(address);
CScript scriptPubKey = GetScriptForDestination(address.Get());
CAmount nAmount = AmountFromValue(s.value_);
CAmount nAmount = AmountFromValue(sendTo[name_]);
CTxOut out(nAmount, scriptPubKey);
rawTx.vout.push_back(out);
@ -438,7 +439,7 @@ Value decoderawtransaction(const Array& params, bool fHelp)
);
LOCK(cs_main);
RPCTypeCheck(params, boost::assign::list_of(str_type));
RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR));
CTransaction tx;
@ -570,7 +571,7 @@ Value signrawtransaction(const Array& params, bool fHelp)
#else
LOCK(cs_main);
#endif
RPCTypeCheck(params, boost::assign::list_of(str_type)(array_type)(array_type)(str_type), true);
RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VARR)(UniValue::VARR)(UniValue::VSTR), true);
vector<unsigned char> txData(ParseHexV(params[0], "argument 1"));
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
@ -613,10 +614,11 @@ Value signrawtransaction(const Array& params, bool fHelp)
bool fGivenKeys = false;
CBasicKeyStore tempKeystore;
if (params.size() > 2 && params[2].type() != null_type) {
if (params.size() > 2 && !params[2].isNull()) {
fGivenKeys = true;
Array keys = params[2].get_array();
BOOST_FOREACH(Value k, keys) {
for (unsigned int idx = 0; idx < keys.size(); idx++) {
Value k = keys[idx];
CBitcoinSecret vchSecret;
bool fGood = vchSecret.SetString(k.get_str());
if (!fGood)
@ -633,15 +635,16 @@ Value signrawtransaction(const Array& params, bool fHelp)
#endif
// Add previous txouts given in the RPC call:
if (params.size() > 1 && params[1].type() != null_type) {
if (params.size() > 1 && !params[1].isNull()) {
Array prevTxs = params[1].get_array();
BOOST_FOREACH(Value& p, prevTxs) {
if (p.type() != obj_type)
for (unsigned int idx = 0; idx < prevTxs.size(); idx++) {
const Value& p = prevTxs[idx];
if (!p.isObject())
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
Object prevOut = p.get_obj();
RPCTypeCheck(prevOut, boost::assign::map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type));
RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR));
uint256 txid = ParseHashO(prevOut, "txid");
@ -669,9 +672,9 @@ Value signrawtransaction(const Array& 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()) {
RPCTypeCheck(prevOut, boost::assign::map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)("redeemScript",str_type));
RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR)("redeemScript",UniValue::VSTR));
Value v = find_value(prevOut, "redeemScript");
if (!(v == Value::null)) {
if (!v.isNull()) {
vector<unsigned char> rsData(ParseHexV(v, "redeemScript"));
CScript redeemScript(rsData.begin(), rsData.end());
tempKeystore.AddCScript(redeemScript);
@ -687,7 +690,7 @@ Value signrawtransaction(const Array& params, bool fHelp)
#endif
int nHashType = SIGHASH_ALL;
if (params.size() > 3 && params[3].type() != null_type) {
if (params.size() > 3 && !params[3].isNull()) {
static map<string, int> mapSigHashValues =
boost::assign::map_list_of
(string("ALL"), int(SIGHASH_ALL))
@ -769,7 +772,7 @@ Value sendrawtransaction(const Array& params, bool fHelp)
);
LOCK(cs_main);
RPCTypeCheck(params, boost::assign::list_of(str_type)(bool_type));
RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL));
// parse hex string from parameter
CTransaction tx;

View file

@ -27,7 +27,8 @@
#include <boost/shared_ptr.hpp>
#include <boost/signals2/signal.hpp>
#include <boost/thread.hpp>
#include "json/json_spirit_writer_template.h"
#include "json_spirit_wrapper.h"
using namespace boost::asio;
using namespace json_spirit;
@ -89,30 +90,30 @@ void RPCTypeCheck(const Array& params,
break;
const Value& v = params[i];
if (!((v.type() == t) || (fAllowNull && (v.type() == null_type))))
if (!((v.type() == t) || (fAllowNull && (v.isNull()))))
{
string err = strprintf("Expected type %s, got %s",
Value_type_name[t], Value_type_name[v.type()]);
uvTypeName(t), uvTypeName(v.type()));
throw JSONRPCError(RPC_TYPE_ERROR, err);
}
i++;
}
}
void RPCTypeCheck(const Object& o,
const map<string, Value_type>& typesExpected,
void RPCTypeCheckObj(const UniValue& o,
const map<string, UniValue::VType>& typesExpected,
bool fAllowNull)
{
BOOST_FOREACH(const PAIRTYPE(string, Value_type)& t, typesExpected)
{
const Value& v = find_value(o, t.first);
if (!fAllowNull && v.type() == null_type)
if (!fAllowNull && v.isNull())
throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first));
if (!((v.type() == t.second) || (fAllowNull && (v.type() == null_type))))
if (!((v.type() == t.second) || (fAllowNull && (v.isNull()))))
{
string err = strprintf("Expected type %s for %s, got %s",
Value_type_name[t.second], t.first, Value_type_name[v.type()]);
uvTypeName(t.second), t.first, uvTypeName(v.type()));
throw JSONRPCError(RPC_TYPE_ERROR, err);
}
}
@ -142,7 +143,7 @@ Value ValueFromAmount(const CAmount& amount)
uint256 ParseHashV(const Value& v, string strName)
{
string strHex;
if (v.type() == str_type)
if (v.isStr())
strHex = v.get_str();
if (!IsHex(strHex)) // Note: IsHex("") is false
throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
@ -157,7 +158,7 @@ uint256 ParseHashO(const Object& o, string strKey)
vector<unsigned char> ParseHexV(const Value& v, string strName)
{
string strHex;
if (v.type() == str_type)
if (v.isStr())
strHex = v.get_str();
if (!IsHex(strHex))
throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
@ -417,7 +418,7 @@ void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
int code = find_value(objError, "code").get_int();
if (code == RPC_INVALID_REQUEST) nStatus = HTTP_BAD_REQUEST;
else if (code == RPC_METHOD_NOT_FOUND) nStatus = HTTP_NOT_FOUND;
string strReply = JSONRPCReply(Value::null, objError, id);
string strReply = JSONRPCReply(NullUniValue, objError, id);
stream << HTTPReply(nStatus, strReply, false) << std::flush;
}
@ -828,14 +829,14 @@ public:
string strMethod;
Array params;
JSONRequest() { id = Value::null; }
JSONRequest() { id = NullUniValue; }
void parse(const Value& valRequest);
};
void JSONRequest::parse(const Value& valRequest)
{
// Parse request
if (valRequest.type() != obj_type)
if (!valRequest.isObject())
throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
const Object& request = valRequest.get_obj();
@ -844,9 +845,9 @@ void JSONRequest::parse(const Value& valRequest)
// Parse method
Value valMethod = find_value(request, "method");
if (valMethod.type() == null_type)
if (valMethod.isNull())
throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
if (valMethod.type() != str_type)
if (!valMethod.isStr())
throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
strMethod = valMethod.get_str();
if (strMethod != "getblocktemplate")
@ -854,9 +855,9 @@ void JSONRequest::parse(const Value& valRequest)
// Parse params
Value valParams = find_value(request, "params");
if (valParams.type() == array_type)
if (valParams.isArray())
params = valParams.get_array();
else if (valParams.type() == null_type)
else if (valParams.isNull())
params = Array();
else
throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array");
@ -872,15 +873,15 @@ static Object JSONRPCExecOne(const Value& req)
jreq.parse(req);
Value result = tableRPC.execute(jreq.strMethod, jreq.params);
rpc_result = JSONRPCReplyObj(result, Value::null, jreq.id);
rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id);
}
catch (const Object& objError)
{
rpc_result = JSONRPCReplyObj(Value::null, objError, jreq.id);
rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id);
}
catch (const std::exception& e)
{
rpc_result = JSONRPCReplyObj(Value::null,
rpc_result = JSONRPCReplyObj(NullUniValue,
JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
}
@ -893,7 +894,7 @@ static string JSONRPCExecBatch(const Array& vReq)
for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++)
ret.push_back(JSONRPCExecOne(vReq[reqIdx]));
return write_string(Value(ret), false) + "\n";
return ret.write() + "\n";
}
static bool HTTPReq_JSONRPC(AcceptedConnection *conn,
@ -925,7 +926,7 @@ static bool HTTPReq_JSONRPC(AcceptedConnection *conn,
{
// Parse request
Value valRequest;
if (!read_string(strRequest, valRequest))
if (!valRequest.read(strRequest))
throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
// Return immediately if in warmup
@ -938,16 +939,16 @@ static bool HTTPReq_JSONRPC(AcceptedConnection *conn,
string strReply;
// singleton request
if (valRequest.type() == obj_type) {
if (valRequest.isObject()) {
jreq.parse(valRequest);
Value result = tableRPC.execute(jreq.strMethod, jreq.params);
// Send reply
strReply = JSONRPCReply(result, Value::null, jreq.id);
strReply = JSONRPCReply(result, NullUniValue, jreq.id);
// array of requests
} else if (valRequest.type() == array_type)
} else if (valRequest.isArray())
strReply = JSONRPCExecBatch(valRequest.get_array());
else
throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");

View file

@ -15,9 +15,8 @@
#include <stdint.h>
#include <string>
#include "json/json_spirit_reader_template.h"
#include "json/json_spirit_utils.h"
#include "json/json_spirit_writer_template.h"
#include <boost/function.hpp>
#include "json_spirit_wrapper.h"
class CRPCCommand;
@ -73,12 +72,13 @@ bool RPCIsInWarmup(std::string *statusOut);
*/
void RPCTypeCheck(const json_spirit::Array& params,
const std::list<json_spirit::Value_type>& typesExpected, bool fAllowNull=false);
/**
* Check for expected keys/value types in an Object.
* Use like: RPCTypeCheck(object, boost::assign::map_list_of("name", str_type)("value", int_type));
*/
void RPCTypeCheck(const json_spirit::Object& o,
const std::map<std::string, json_spirit::Value_type>& 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<std::string, UniValue::VType>& typesExpected, bool fAllowNull=false);
/**
* Run func nSeconds from now. Uses boost deadline timers.

View file

@ -17,9 +17,7 @@
#include <boost/foreach.hpp>
#include <boost/test/unit_test.hpp>
#include "json/json_spirit_reader_template.h"
#include "json/json_spirit_utils.h"
#include "json/json_spirit_writer_template.h"
#include "json_spirit_wrapper.h"
using namespace json_spirit;
extern Array read_json(const std::string& jsondata);
@ -30,10 +28,9 @@ BOOST_FIXTURE_TEST_SUITE(base58_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(base58_EncodeBase58)
{
Array tests = read_json(std::string(json_tests::base58_encode_decode, json_tests::base58_encode_decode + sizeof(json_tests::base58_encode_decode)));
BOOST_FOREACH(Value& tv, tests)
{
Array test = tv.get_array();
std::string strTest = write_string(tv, false);
for (unsigned int idx = 0; idx < tests.size(); idx++) {
Array test = tests[idx];
std::string strTest = test.write();
if (test.size() < 2) // Allow for extra stuff (useful for comments)
{
BOOST_ERROR("Bad test: " << strTest);
@ -53,10 +50,9 @@ BOOST_AUTO_TEST_CASE(base58_DecodeBase58)
Array tests = read_json(std::string(json_tests::base58_encode_decode, json_tests::base58_encode_decode + sizeof(json_tests::base58_encode_decode)));
std::vector<unsigned char> result;
BOOST_FOREACH(Value& tv, tests)
{
Array test = tv.get_array();
std::string strTest = write_string(tv, false);
for (unsigned int idx = 0; idx < tests.size(); idx++) {
Array test = tests[idx];
std::string strTest = test.write();
if (test.size() < 2) // Allow for extra stuff (useful for comments)
{
BOOST_ERROR("Bad test: " << strTest);
@ -130,10 +126,9 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
CBitcoinAddress addr;
SelectParams(CBaseChainParams::MAIN);
BOOST_FOREACH(Value& tv, tests)
{
Array test = tv.get_array();
std::string strTest = write_string(tv, false);
for (unsigned int idx = 0; idx < tests.size(); idx++) {
Array test = tests[idx];
std::string strTest = test.write();
if (test.size() < 3) // Allow for extra stuff (useful for comments)
{
BOOST_ERROR("Bad test: " << strTest);
@ -185,10 +180,10 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
{
Array tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid)));
std::vector<unsigned char> result;
BOOST_FOREACH(Value& tv, tests)
{
Array test = tv.get_array();
std::string strTest = write_string(tv, false);
for (unsigned int idx = 0; idx < tests.size(); idx++) {
Array test = tests[idx];
std::string strTest = test.write();
if (test.size() < 3) // Allow for extra stuff (useful for comments)
{
BOOST_ERROR("Bad test: " << strTest);
@ -256,10 +251,9 @@ BOOST_AUTO_TEST_CASE(base58_keys_invalid)
CBitcoinSecret secret;
CBitcoinAddress addr;
BOOST_FOREACH(Value& tv, tests)
{
Array test = tv.get_array();
std::string strTest = write_string(tv, false);
for (unsigned int idx = 0; idx < tests.size(); idx++) {
Array test = tests[idx];
std::string strTest = test.write();
if (test.size() < 1) // Allow for extra stuff (useful for comments)
{
BOOST_ERROR("Bad test: " << strTest);

View file

@ -111,20 +111,20 @@ BOOST_AUTO_TEST_CASE(rpc_rawsign)
BOOST_AUTO_TEST_CASE(rpc_format_monetary_values)
{
BOOST_CHECK_EQUAL(write_string(ValueFromAmount(0LL), false), "0.00000000");
BOOST_CHECK_EQUAL(write_string(ValueFromAmount(1LL), false), "0.00000001");
BOOST_CHECK_EQUAL(write_string(ValueFromAmount(17622195LL), false), "0.17622195");
BOOST_CHECK_EQUAL(write_string(ValueFromAmount(50000000LL), false), "0.50000000");
BOOST_CHECK_EQUAL(write_string(ValueFromAmount(89898989LL), false), "0.89898989");
BOOST_CHECK_EQUAL(write_string(ValueFromAmount(100000000LL), false), "1.00000000");
BOOST_CHECK_EQUAL(write_string(ValueFromAmount(2099999999999990LL), false), "20999999.99999990");
BOOST_CHECK_EQUAL(write_string(ValueFromAmount(2099999999999999LL), false), "20999999.99999999");
BOOST_CHECK(ValueFromAmount(0LL).write() == "0.00000000");
BOOST_CHECK(ValueFromAmount(1LL).write() == "0.00000001");
BOOST_CHECK(ValueFromAmount(17622195LL).write() == "0.17622195");
BOOST_CHECK(ValueFromAmount(50000000LL).write() == "0.50000000");
BOOST_CHECK(ValueFromAmount(89898989LL).write() == "0.89898989");
BOOST_CHECK(ValueFromAmount(100000000LL).write() == "1.00000000");
BOOST_CHECK(ValueFromAmount(2099999999999990LL).write() == "20999999.99999990");
BOOST_CHECK(ValueFromAmount(2099999999999999LL).write() == "20999999.99999999");
}
static Value ValueFromString(const std::string &str)
{
Value value;
BOOST_CHECK(read_string(str, value));
BOOST_CHECK(value.read(str));
return value;
}

View file

@ -26,9 +26,7 @@
#include <boost/foreach.hpp>
#include <boost/test/unit_test.hpp>
#include "json/json_spirit_reader_template.h"
#include "json/json_spirit_utils.h"
#include "json/json_spirit_writer_template.h"
#include "json_spirit_wrapper.h"
using namespace std;
using namespace json_spirit;
@ -46,7 +44,7 @@ read_json(const std::string& jsondata)
{
Value v;
if (!read_string(jsondata, v) || v.type() != array_type)
if (!v.read(jsondata) || !v.isArray())
{
BOOST_ERROR("Parse error.");
return Array();
@ -636,10 +634,9 @@ BOOST_AUTO_TEST_CASE(script_valid)
// scripts.
Array tests = read_json(std::string(json_tests::script_valid, json_tests::script_valid + sizeof(json_tests::script_valid)));
BOOST_FOREACH(Value& tv, tests)
{
Array test = tv.get_array();
string strTest = write_string(tv, false);
for (unsigned int idx = 0; idx < tests.size(); idx++) {
Array test = tests[idx];
string strTest = test.write();
if (test.size() < 3) // Allow size > 3; extra stuff ignored (useful for comments)
{
if (test.size() != 1) {
@ -662,11 +659,10 @@ BOOST_AUTO_TEST_CASE(script_invalid)
// Scripts that should evaluate as invalid
Array tests = read_json(std::string(json_tests::script_invalid, json_tests::script_invalid + sizeof(json_tests::script_invalid)));
BOOST_FOREACH(Value& tv, tests)
{
Array test = tv.get_array();
string strTest = write_string(tv, false);
if (test.size() < 3) // Allow size > 3; extra stuff ignored (useful for comments)
for (unsigned int idx = 0; idx < tests.size(); idx++) {
Array test = tests[idx];
string strTest = test.write();
if (test.size() < 3) // Allow size > 2; extra stuff ignored (useful for comments)
{
if (test.size() != 1) {
BOOST_ERROR("Bad test: " << strTest);

View file

@ -16,9 +16,7 @@
#include <iostream>
#include <boost/test/unit_test.hpp>
#include "json/json_spirit_reader_template.h"
#include "json/json_spirit_utils.h"
#include "json/json_spirit_writer_template.h"
#include "json_spirit_wrapper.h"
using namespace json_spirit;
extern Array read_json(const std::string& jsondata);
@ -170,10 +168,9 @@ BOOST_AUTO_TEST_CASE(sighash_from_data)
{
Array tests = read_json(std::string(json_tests::sighash, json_tests::sighash + sizeof(json_tests::sighash)));
BOOST_FOREACH(Value& tv, tests)
{
Array test = tv.get_array();
std::string strTest = write_string(tv, false);
for (unsigned int idx = 0; idx < tests.size(); idx++) {
Array test = tests[idx];
std::string strTest = test.write();
if (test.size() < 1) // Allow for extra stuff (useful for comments)
{
BOOST_ERROR("Bad test: " << strTest);

View file

@ -22,7 +22,8 @@
#include <boost/algorithm/string/split.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/assign/list_of.hpp>
#include "json/json_spirit_writer_template.h"
#include "json_spirit_wrapper.h"
using namespace std;
using namespace json_spirit;
@ -90,14 +91,21 @@ BOOST_AUTO_TEST_CASE(tx_valid)
// verifyFlags is a comma separated list of script verification flags to apply, or "NONE"
Array tests = read_json(std::string(json_tests::tx_valid, json_tests::tx_valid + sizeof(json_tests::tx_valid)));
<<<<<<< HEAD
ScriptError err;
BOOST_FOREACH(Value& tv, tests)
{
Array test = tv.get_array();
string strTest = write_string(tv, false);
if (test[0].type() == array_type)
=======
for (unsigned int idx = 0; idx < tests.size(); idx++) {
Array test = tests[idx];
string strTest = test.write();
if (test[0].isArray())
>>>>>>> Convert tree to using univalue. Eliminate all json_spirit uses.
{
if (test.size() != 3 || test[1].type() != str_type || test[2].type() != str_type)
if (test.size() != 3 || !test[1].isStr() || !test[2].isStr())
{
BOOST_ERROR("Bad test: " << strTest);
continue;
@ -106,9 +114,9 @@ BOOST_AUTO_TEST_CASE(tx_valid)
map<COutPoint, CScript> mapprevOutScriptPubKeys;
Array inputs = test[0].get_array();
bool fValid = true;
BOOST_FOREACH(Value& input, inputs)
{
if (input.type() != array_type)
for (unsigned int inpIdx = 0; inpIdx < inputs.size(); inpIdx++) {
const Value& input = inputs[inpIdx];
if (!input.isArray())
{
fValid = false;
break;
@ -166,14 +174,21 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
// verifyFlags is a comma separated list of script verification flags to apply, or "NONE"
Array tests = read_json(std::string(json_tests::tx_invalid, json_tests::tx_invalid + sizeof(json_tests::tx_invalid)));
<<<<<<< HEAD
ScriptError err;
BOOST_FOREACH(Value& tv, tests)
{
Array test = tv.get_array();
string strTest = write_string(tv, false);
if (test[0].type() == array_type)
=======
for (unsigned int idx = 0; idx < tests.size(); idx++) {
Array test = tests[idx];
string strTest = test.write();
if (test[0].isArray())
>>>>>>> Convert tree to using univalue. Eliminate all json_spirit uses.
{
if (test.size() != 3 || test[1].type() != str_type || test[2].type() != str_type)
if (test.size() != 3 || !test[1].isStr() || !test[2].isStr())
{
BOOST_ERROR("Bad test: " << strTest);
continue;
@ -182,9 +197,9 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
map<COutPoint, CScript> mapprevOutScriptPubKeys;
Array inputs = test[0].get_array();
bool fValid = true;
BOOST_FOREACH(Value& input, inputs)
{
if (input.type() != array_type)
for (unsigned int inpIdx = 0; inpIdx < inputs.size(); inpIdx++) {
const Value& input = inputs[inpIdx];
if (!input.isArray())
{
fValid = false;
break;

View file

@ -11,6 +11,10 @@
#include <map>
#include <cassert>
#include <sstream> // .get_int64()
#include <utility> // std::pair
#include <stdlib.h> // atoi(), atof() TODO: remove
class UniValue {
public:
enum VType { VNULL, VOBJ, VARR, VSTR, VNUM, VBOOL, };
@ -130,8 +134,88 @@ private:
int findKey(const std::string& key) const;
void writeArray(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const;
void writeObject(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const;
public:
//
// The following were added for compatibility with json_spirit.
// Most duplicate other methods, and should be removed.
//
std::vector<std::string> getKeys() const { return keys; }
std::vector<UniValue> getValues() const { return values; }
bool get_bool() const { return getBool(); }
std::string get_str() const { return getValStr(); }
int get_int() const { return atoi(getValStr().c_str()); }
double get_real() const { return atof(getValStr().c_str()); }
const UniValue& get_obj() const { return *this; }
const UniValue& get_array() const { return *this; }
enum VType type() const { return getType(); }
bool push_back(std::pair<std::string,UniValue> pear) {
return pushKV(pear.first, pear.second);
}
int64_t get_int64() const {
int64_t ret;
std::istringstream(getValStr()) >> ret;
return ret;
}
};
//
// The following were added for compatibility with json_spirit.
// Most duplicate other methods, and should be removed.
//
static inline std::pair<std::string,UniValue> Pair(const char *cKey, const char *cVal)
{
std::string key(cKey);
UniValue uVal(cVal);
return std::make_pair(key, uVal);
}
static inline std::pair<std::string,UniValue> Pair(const char *cKey, std::string strVal)
{
std::string key(cKey);
UniValue uVal(strVal);
return std::make_pair(key, uVal);
}
static inline std::pair<std::string,UniValue> Pair(const char *cKey, uint64_t u64Val)
{
std::string key(cKey);
UniValue uVal(u64Val);
return std::make_pair(key, uVal);
}
static inline std::pair<std::string,UniValue> Pair(const char *cKey, int64_t i64Val)
{
std::string key(cKey);
UniValue uVal(i64Val);
return std::make_pair(key, uVal);
}
static inline std::pair<std::string,UniValue> Pair(const char *cKey, int iVal)
{
std::string key(cKey);
UniValue uVal(iVal);
return std::make_pair(key, uVal);
}
static inline std::pair<std::string,UniValue> Pair(const char *cKey, double dVal)
{
std::string key(cKey);
UniValue uVal(dVal);
return std::make_pair(key, uVal);
}
static inline std::pair<std::string,UniValue> Pair(const char *cKey, const UniValue& uVal)
{
std::string key(cKey);
return std::make_pair(key, uVal);
}
static inline std::pair<std::string,UniValue> Pair(std::string key, const UniValue& uVal)
{
return std::make_pair(key, uVal);
}
enum jtokentype {
JTOK_ERR = -1,
JTOK_NONE = 0, // eof

View file

@ -19,7 +19,7 @@
#include <boost/algorithm/string.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include "json/json_spirit_value.h"
#include "json_spirit_wrapper.h"
using namespace json_spirit;
using namespace std;
@ -126,7 +126,7 @@ Value importprivkey(const Array& params, bool fHelp)
// Don't throw error in case a key is already there
if (pwalletMain->HaveKey(vchAddress))
return Value::null;
return NullUniValue;
pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = 1;
@ -141,7 +141,7 @@ Value importprivkey(const Array& params, bool fHelp)
}
}
return Value::null;
return NullUniValue;
}
Value importaddress(const Array& params, bool fHelp)
@ -200,7 +200,7 @@ Value importaddress(const Array& params, bool fHelp)
// Don't throw error in case an address is already there
if (pwalletMain->HaveWatchOnly(script))
return Value::null;
return NullUniValue;
pwalletMain->MarkDirty();
@ -214,7 +214,7 @@ Value importaddress(const Array& params, bool fHelp)
}
}
return Value::null;
return NullUniValue;
}
Value importwallet(const Array& params, bool fHelp)
@ -318,7 +318,7 @@ Value importwallet(const Array& params, bool fHelp)
if (!fGood)
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys to wallet");
return Value::null;
return NullUniValue;
}
Value dumpprivkey(const Array& params, bool fHelp)
@ -421,5 +421,5 @@ Value dumpwallet(const Array& params, bool fHelp)
file << "\n";
file << "# End of dump\n";
file.close();
return Value::null;
return NullUniValue;
}

View file

@ -21,8 +21,7 @@
#include <boost/assign/list_of.hpp>
#include "json/json_spirit_utils.h"
#include "json/json_spirit_value.h"
#include "json_spirit_wrapper.h"
using namespace std;
using namespace json_spirit;
@ -275,7 +274,7 @@ Value setaccount(const Array& params, bool fHelp)
else
throw JSONRPCError(RPC_MISC_ERROR, "setaccount can only be used with own address");
return Value::null;
return NullUniValue;
}
@ -419,9 +418,9 @@ Value sendtoaddress(const Array& params, bool fHelp)
// Wallet comments
CWalletTx wtx;
if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
if (params.size() > 2 && !params[2].isNull() && !params[2].get_str().empty())
wtx.mapValue["comment"] = params[2].get_str();
if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
if (params.size() > 3 && !params[3].isNull() && !params[3].get_str().empty())
wtx.mapValue["to"] = params[3].get_str();
bool fSubtractFeeFromAmount = false;
@ -896,9 +895,9 @@ Value sendfrom(const Array& params, bool fHelp)
CWalletTx wtx;
wtx.strFromAccount = strAccount;
if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
if (params.size() > 4 && !params[4].isNull() && !params[4].get_str().empty())
wtx.mapValue["comment"] = params[4].get_str();
if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
if (params.size() > 5 && !params[5].isNull() && !params[5].get_str().empty())
wtx.mapValue["to"] = params[5].get_str();
EnsureWalletIsUnlocked();
@ -965,7 +964,7 @@ Value sendmany(const Array& params, bool fHelp)
CWalletTx wtx;
wtx.strFromAccount = strAccount;
if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
if (params.size() > 3 && !params[3].isNull() && !params[3].get_str().empty())
wtx.mapValue["comment"] = params[3].get_str();
Array subtractFeeFromAmount;
@ -976,18 +975,19 @@ Value sendmany(const Array& params, bool fHelp)
vector<CRecipient> vecSend;
CAmount totalAmount = 0;
BOOST_FOREACH(const Pair& s, sendTo)
vector<string> keys = sendTo.getKeys();
BOOST_FOREACH(const string& name_, keys)
{
CBitcoinAddress address(s.name_);
CBitcoinAddress address(name_);
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+s.name_);
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+name_);
if (setAddress.count(address))
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_);
setAddress.insert(address);
CScript scriptPubKey = GetScriptForDestination(address.Get());
CAmount nAmount = AmountFromValue(s.value_);
CAmount nAmount = AmountFromValue(sendTo[name_]);
totalAmount += nAmount;
bool fSubtractFeeFromAmount = false;
@ -1472,15 +1472,21 @@ Value listtransactions(const Array& params, bool fHelp)
nFrom = ret.size();
if ((nFrom + nCount) > (int)ret.size())
nCount = ret.size() - nFrom;
Array::iterator first = ret.begin();
vector<UniValue> arrTmp = ret.getValues();
vector<UniValue>::iterator first = arrTmp.begin();
std::advance(first, nFrom);
Array::iterator last = ret.begin();
vector<UniValue>::iterator last = arrTmp.begin();
std::advance(last, nFrom+nCount);
if (last != ret.end()) ret.erase(last, ret.end());
if (first != ret.begin()) ret.erase(ret.begin(), first);
if (last != arrTmp.end()) arrTmp.erase(last, arrTmp.end());
if (first != arrTmp.begin()) arrTmp.erase(arrTmp.begin(), first);
std::reverse(ret.begin(), ret.end()); // Return oldest to newest
std::reverse(arrTmp.begin(), arrTmp.end()); // Return oldest to newest
ret.clear();
ret.push_backV(arrTmp);
return ret;
}
@ -1756,7 +1762,7 @@ Value backupwallet(const Array& params, bool fHelp)
if (!BackupWallet(*pwalletMain, strDest))
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
return Value::null;
return NullUniValue;
}
@ -1793,7 +1799,7 @@ Value keypoolrefill(const Array& params, bool fHelp)
if (pwalletMain->GetKeyPoolSize() < kpSize)
throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
return Value::null;
return NullUniValue;
}
@ -1860,7 +1866,7 @@ Value walletpassphrase(const Array& params, bool fHelp)
nWalletUnlockTime = GetTime() + nSleepTime;
RPCRunLater("lockwallet", boost::bind(LockWallet, pwalletMain), nSleepTime);
return Value::null;
return NullUniValue;
}
@ -1906,7 +1912,7 @@ Value walletpassphrasechange(const Array& params, bool fHelp)
if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
return Value::null;
return NullUniValue;
}
@ -1945,7 +1951,7 @@ Value walletlock(const Array& params, bool fHelp)
nWalletUnlockTime = 0;
}
return Value::null;
return NullUniValue;
}
@ -2050,9 +2056,9 @@ Value lockunspent(const Array& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
if (params.size() == 1)
RPCTypeCheck(params, boost::assign::list_of(bool_type));
RPCTypeCheck(params, boost::assign::list_of(UniValue::VBOOL));
else
RPCTypeCheck(params, boost::assign::list_of(bool_type)(array_type));
RPCTypeCheck(params, boost::assign::list_of(UniValue::VBOOL)(UniValue::VARR));
bool fUnlock = params[0].get_bool();
@ -2063,13 +2069,13 @@ Value lockunspent(const Array& params, bool fHelp)
}
Array outputs = params[1].get_array();
BOOST_FOREACH(Value& output, outputs)
{
if (output.type() != obj_type)
for (unsigned int idx = 0; idx < outputs.size(); idx++) {
const UniValue& output = outputs[idx];
if (!output.isObject())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object");
const Object& o = output.get_obj();
RPCTypeCheck(o, boost::assign::map_list_of("txid", str_type)("vout", int_type));
RPCTypeCheckObj(o, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM));
string txid = find_value(o, "txid").get_str();
if (!IsHex(txid))