aboutsummaryrefslogtreecommitdiff
path: root/src/bitcoinrpc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bitcoinrpc.cpp')
-rw-r--r--src/bitcoinrpc.cpp466
1 files changed, 361 insertions, 105 deletions
diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp
index 15bcf1da3..a189b2b2b 100644
--- a/src/bitcoinrpc.cpp
+++ b/src/bitcoinrpc.cpp
@@ -44,6 +44,8 @@ static CCriticalSection cs_nWalletUnlockTime;
extern Value dumpprivkey(const Array& params, bool fHelp);
extern Value importprivkey(const Array& params, bool fHelp);
+const Object emptyobj;
+
Object JSONRPCError(int code, const string& message)
{
Object error;
@@ -111,6 +113,33 @@ HexBits(unsigned int nBits)
return HexStr(BEGIN(uBits.cBits), END(uBits.cBits));
}
+enum DecomposeMode {
+ DM_NONE = 0,
+ DM_HASH,
+ DM_HEX,
+ DM_ASM,
+ DM_OBJ,
+};
+
+enum DecomposeMode
+FindDecompose(const Object& decompositions, const char* pcType, const char* pcDefault)
+{
+ Value val = find_value(decompositions, pcType);
+ std::string strDecompose = (val.type() == null_type) ? pcDefault : val.get_str();
+
+ if (strDecompose == "no")
+ return DM_NONE;
+ if (strDecompose == "hash")
+ return DM_HASH;
+ if (strDecompose == "hex")
+ return DM_HEX;
+ if (strDecompose == "asm")
+ return DM_ASM;
+ if (strDecompose == "obj")
+ return DM_OBJ;
+ throw JSONRPCError(-18, "Invalid decomposition");
+}
+
void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
{
int confirms = wtx.GetDepthInMainChain();
@@ -126,6 +155,141 @@ void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
entry.push_back(Pair(item.first, item.second));
}
+void
+ScriptSigToJSON(const CTxIn& txin, Object& out)
+{
+ out.push_back(Pair("asm", txin.scriptSig.ToString()));
+ out.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
+
+ CTransaction txprev;
+ uint256 hashTxprevBlock;
+ if (!GetTransaction(txin.prevout.hash, txprev, hashTxprevBlock))
+ return;
+
+ txnouttype type;
+ vector<CBitcoinAddress> addresses;
+ int nRequired;
+
+ if (!ExtractAddresses(txprev.vout[txin.prevout.n].scriptPubKey, type,
+ addresses, nRequired))
+ {
+ out.push_back(Pair("type", GetTxnOutputType(TX_NONSTANDARD)));
+ return;
+ }
+
+ out.push_back(Pair("type", GetTxnOutputType(type)));
+ if (type == TX_MULTISIG)
+ {
+ // TODO: Need to handle this specially since not all input addresses are required...
+ return;
+ }
+
+ Array a;
+ BOOST_FOREACH(const CBitcoinAddress& addr, addresses)
+ a.push_back(addr.ToString());
+ out.push_back(Pair("addresses", a));
+}
+
+void
+ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out)
+{
+ txnouttype type;
+ vector<CBitcoinAddress> addresses;
+ int nRequired;
+
+ out.push_back(Pair("asm", scriptPubKey.ToString()));
+ out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
+
+ if (!ExtractAddresses(scriptPubKey, type, addresses, nRequired))
+ {
+ out.push_back(Pair("type", GetTxnOutputType(TX_NONSTANDARD)));
+ return;
+ }
+
+ out.push_back(Pair("reqSigs", nRequired));
+ out.push_back(Pair("type", GetTxnOutputType(type)));
+
+ Array a;
+ BOOST_FOREACH(const CBitcoinAddress& addr, addresses)
+ a.push_back(addr.ToString());
+ out.push_back(Pair("addresses", a));
+}
+
+void TxToJSON(const CTransaction &tx, Object& entry, const Object& decompositions)
+{
+ entry.push_back(Pair("version", tx.nVersion));
+ entry.push_back(Pair("locktime", (boost::int64_t)tx.nLockTime));
+ entry.push_back(Pair("size", (boost::int64_t)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION)));
+
+ enum DecomposeMode decomposeScript = FindDecompose(decompositions, "script", "asm");
+
+ Array vin;
+ BOOST_FOREACH(const CTxIn& txin, tx.vin)
+ {
+ Object in;
+ if (tx.IsCoinBase())
+ in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
+ else
+ {
+ Object prevout;
+ prevout.push_back(Pair("hash", txin.prevout.hash.GetHex()));
+ prevout.push_back(Pair("n", (boost::int64_t)txin.prevout.n));
+ in.push_back(Pair("prevout", prevout));
+ switch (decomposeScript) {
+ case DM_NONE:
+ break;
+ case DM_HEX:
+ in.push_back(Pair("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
+ break;
+ case DM_ASM:
+ in.push_back(Pair("scriptSig", txin.scriptSig.ToString()));
+ break;
+ case DM_OBJ:
+ {
+ Object o;
+ ScriptSigToJSON(txin, o);
+ in.push_back(Pair("scriptSig", o));
+ break;
+ }
+ default:
+ throw JSONRPCError(-18, "Invalid script decomposition");
+ }
+ }
+ in.push_back(Pair("sequence", (boost::int64_t)txin.nSequence));
+ vin.push_back(in);
+ }
+ entry.push_back(Pair("vin", vin));
+ Array vout;
+ BOOST_FOREACH(const CTxOut& txout, tx.vout)
+ {
+ Object out;
+ out.push_back(Pair("value", ValueFromAmount(txout.nValue)));
+ switch (decomposeScript) {
+ case DM_NONE:
+ break;
+ case DM_HEX:
+ out.push_back(Pair("scriptPubKey", HexStr(txout.scriptPubKey.begin(), txout.scriptPubKey.end())));
+ break;
+ case DM_ASM:
+ out.push_back(Pair("scriptPubKey", txout.scriptPubKey.ToString()));
+ break;
+ case DM_OBJ:
+ {
+ Object o;
+ ScriptPubKeyToJSON(txout.scriptPubKey, o);
+ out.push_back(Pair("scriptPubKey", o));
+ break;
+ }
+ default:
+ throw JSONRPCError(-18, "Invalid script decomposition");
+ }
+ vout.push_back(out);
+ }
+ entry.push_back(Pair("vout", vout));
+}
+
+void AnyTxToJSON(const uint256 hash, const CTransaction* ptx, Object& entry, const Object& decompositions);
+
string AccountFromValue(const Value& value)
{
string strAccount = value.get_str();
@@ -134,10 +298,13 @@ string AccountFromValue(const Value& value)
return strAccount;
}
-Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex)
+Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex, const Object& decompositions)
{
Object result;
result.push_back(Pair("hash", block.GetHash().GetHex()));
+ CMerkleTx txGen(block.vtx[0]);
+ txGen.SetMerkleBranch(&block);
+ result.push_back(Pair("confirmations", (int)txGen.GetDepthInMainChain()));
result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION)));
result.push_back(Pair("height", blockindex->nHeight));
result.push_back(Pair("version", block.nVersion));
@@ -146,10 +313,38 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex)
result.push_back(Pair("nonce", (boost::uint64_t)block.nNonce));
result.push_back(Pair("bits", HexBits(block.nBits)));
result.push_back(Pair("difficulty", GetDifficulty(blockindex)));
- Array txhashes;
- BOOST_FOREACH (const CTransaction&tx, block.vtx)
- txhashes.push_back(tx.GetHash().GetHex());
- result.push_back(Pair("tx", txhashes));
+
+ enum DecomposeMode decomposeTxn = FindDecompose(decompositions, "tx", "hash");
+ if (decomposeTxn)
+ {
+ Array txs;
+ switch (decomposeTxn) {
+ case DM_OBJ:
+ BOOST_FOREACH (const CTransaction&tx, block.vtx)
+ {
+ Object entry;
+ AnyTxToJSON(tx.GetHash(), &tx, entry, decompositions);
+ txs.push_back(entry);
+ }
+ break;
+ case DM_HEX:
+ BOOST_FOREACH (const CTransaction&tx, block.vtx)
+ {
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << tx;
+
+ txs.push_back(HexStr(ssTx.begin(), ssTx.end()));
+ }
+ break;
+ case DM_HASH:
+ BOOST_FOREACH (const CTransaction&tx, block.vtx)
+ txs.push_back(tx.GetHash().GetHex());
+ break;
+ default:
+ throw JSONRPCError(-18, "Invalid transaction decomposition");
+ }
+ result.push_back(Pair("tx", txs));
+ }
if (blockindex->pprev)
result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()));
@@ -160,6 +355,7 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex)
+
///
/// Note: This interface may still be subject to change.
///
@@ -999,10 +1195,12 @@ Value addmultisigaddress(const Array& params, bool fHelp)
strAccount = AccountFromValue(params[2]);
// Gather public keys
- if ((nRequired < 1) || ((int)keys.size() < nRequired))
+ if (nRequired < 1)
+ throw runtime_error("a multisignature address must require at least one key to redeem");
+ if ((int)keys.size() < nRequired)
throw runtime_error(
- strprintf("wrong number of keys"
- "(got %d, need at least %d)", keys.size(), nRequired));
+ strprintf("not enough keys supplied "
+ "(got %d keys, but need at least %d to redeem)", keys.size(), nRequired));
std::vector<CKey> pubkeys;
pubkeys.resize(keys.size());
for (unsigned int i = 0; i < keys.size(); i++)
@@ -1462,11 +1660,69 @@ Value listsinceblock(const Array& params, bool fHelp)
return ret;
}
+void
+AnyTxToJSON(const uint256 hash, const CTransaction* ptx, Object& entry, const Object& decompositions)
+{
+ if (pwalletMain->mapWallet.count(hash))
+ {
+ const CWalletTx& wtx = pwalletMain->mapWallet[hash];
+
+ TxToJSON(wtx, entry, decompositions);
+
+ int64 nCredit = wtx.GetCredit();
+ int64 nDebit = wtx.GetDebit();
+ int64 nNet = nCredit - nDebit;
+ int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
+
+ entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
+ if (wtx.IsFromMe())
+ entry.push_back(Pair("fee", ValueFromAmount(nFee)));
+
+ WalletTxToJSON(wtx, entry);
+
+ Array details;
+ ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
+ entry.push_back(Pair("details", details));
+ }
+ else
+ {
+ CTransaction tx;
+ uint256 hashBlock = 0;
+ if ((!ptx) && GetTransaction(hash, tx, hashBlock))
+ ptx = &tx;
+ if (ptx)
+ {
+ entry.push_back(Pair("txid", hash.GetHex()));
+ TxToJSON(*ptx, entry, decompositions);
+ if (hashBlock == 0)
+ entry.push_back(Pair("confirmations", 0));
+ else
+ {
+ entry.push_back(Pair("blockhash", hashBlock.GetHex()));
+ map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
+ if (mi != mapBlockIndex.end() && (*mi).second)
+ {
+ CBlockIndex* pindex = (*mi).second;
+ if (pindex->IsInMainChain())
+ {
+ entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
+ entry.push_back(Pair("time", (boost::int64_t)pindex->nTime));
+ }
+ else
+ entry.push_back(Pair("confirmations", 0));
+ }
+ }
+ }
+ else
+ throw JSONRPCError(-5, "No information available about transaction");
+ }
+}
+
Value gettransaction(const Array& params, bool fHelp)
{
- if (fHelp || params.size() != 1)
+ if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error(
- "gettransaction <txid>\n"
+ "gettransaction <txid> [decompositions]\n"
"Get detailed information about <txid>");
uint256 hash;
@@ -1474,24 +1730,8 @@ Value gettransaction(const Array& params, bool fHelp)
Object entry;
- if (!pwalletMain->mapWallet.count(hash))
- throw JSONRPCError(-5, "Invalid or non-wallet transaction id");
- const CWalletTx& wtx = pwalletMain->mapWallet[hash];
-
- int64 nCredit = wtx.GetCredit();
- int64 nDebit = wtx.GetDebit();
- int64 nNet = nCredit - nDebit;
- int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
-
- entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
- if (wtx.IsFromMe())
- entry.push_back(Pair("fee", ValueFromAmount(nFee)));
-
- WalletTxToJSON(pwalletMain->mapWallet[hash], entry);
-
- Array details;
- ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
- entry.push_back(Pair("details", details));
+ AnyTxToJSON(hash, NULL, entry,
+ (params.size() > 1) ? params[1].get_obj() : emptyobj);
return entry;
}
@@ -1973,9 +2213,9 @@ Value getblockhash(const Array& params, bool fHelp)
Value getblock(const Array& params, bool fHelp)
{
- if (fHelp || params.size() != 1)
+ if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error(
- "getblock <hash>\n"
+ "getblock <hash> [decompositions]\n"
"Returns details of a block with given block-hash.");
std::string strHash = params[0].get_str();
@@ -1988,7 +2228,8 @@ Value getblock(const Array& params, bool fHelp)
CBlockIndex* pblockindex = mapBlockIndex[hash];
block.ReadFromDisk(pblockindex, true);
- return blockToJSON(block, pblockindex);
+ return blockToJSON(block, pblockindex,
+ (params.size() > 1) ? params[1].get_obj() : emptyobj);
}
@@ -2505,34 +2746,11 @@ void ThreadRPCServer2(void* parg)
else
throw JSONRPCError(-32600, "Params must be an array");
- // Find method
- const CRPCCommand *pcmd = tableRPC[strMethod];
- if (!pcmd)
- throw JSONRPCError(-32601, "Method not found");
-
- // Observe safe mode
- string strWarning = GetWarnings("rpc");
- if (strWarning != "" && !GetBoolArg("-disablesafemode") &&
- !pcmd->okSafeMode)
- throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
+ Value result = tableRPC.execute(strMethod, params);
- try
- {
- // Execute
- Value result;
- {
- LOCK2(cs_main, pwalletMain->cs_wallet);
- result = pcmd->actor(params, false);
- }
-
- // Send reply
- string strReply = JSONRPCReply(result, Value::null, id);
- stream << HTTPReply(200, strReply) << std::flush;
- }
- catch (std::exception& e)
- {
- ErrorReply(stream, JSONRPCError(-1, e.what()), id);
- }
+ // Send reply
+ string strReply = JSONRPCReply(result, Value::null, id);
+ stream << HTTPReply(200, strReply) << std::flush;
}
catch (Object& objError)
{
@@ -2545,7 +2763,34 @@ void ThreadRPCServer2(void* parg)
}
}
+json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array &params) const
+{
+ // Find method
+ const CRPCCommand *pcmd = tableRPC[strMethod];
+ if (!pcmd)
+ throw JSONRPCError(-32601, "Method not found");
+ // Observe safe mode
+ string strWarning = GetWarnings("rpc");
+ if (strWarning != "" && !GetBoolArg("-disablesafemode") &&
+ !pcmd->okSafeMode)
+ throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
+
+ try
+ {
+ // Execute
+ Value result;
+ {
+ LOCK2(cs_main, pwalletMain->cs_wallet);
+ result = pcmd->actor(params, false);
+ }
+ return result;
+ }
+ catch (std::exception& e)
+ {
+ throw JSONRPCError(-1, e.what());
+ }
+}
Object CallRPC(const string& strMethod, const Array& params)
@@ -2619,6 +2864,62 @@ void ConvertTo(Value& value)
}
}
+// Convert strings to command-specific RPC representation
+Array RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams)
+{
+ Array params;
+ BOOST_FOREACH(const std::string &param, strParams)
+ params.push_back(param);
+
+ int n = params.size();
+
+ //
+ // Special case non-string parameter types
+ //
+ if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
+ if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
+ if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
+ if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
+ if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
+ if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
+ if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
+ if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
+ if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
+ if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
+ if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
+ if (strMethod == "getblock" && n > 1) ConvertTo<Object>(params[1]);
+ if (strMethod == "getblockhash" && n > 0) ConvertTo<boost::int64_t>(params[0]);
+ if (strMethod == "gettransaction" && n > 1) ConvertTo<Object>(params[1]);
+ if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
+ if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
+ if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
+ if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
+ if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
+ if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
+ if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
+ if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
+ if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
+ if (strMethod == "sendmany" && n > 1)
+ {
+ string s = params[1].get_str();
+ Value v;
+ if (!read_string(s, v) || v.type() != obj_type)
+ throw runtime_error("type mismatch");
+ params[1] = v.get_obj();
+ }
+ if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
+ if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
+ if (strMethod == "addmultisigaddress" && n > 1)
+ {
+ string s = params[1].get_str();
+ Value v;
+ if (!read_string(s, v) || v.type() != array_type)
+ throw runtime_error("type mismatch "+s);
+ params[1] = v.get_array();
+ }
+ return params;
+}
+
int CommandLineRPC(int argc, char *argv[])
{
string strPrint;
@@ -2638,53 +2939,8 @@ int CommandLineRPC(int argc, char *argv[])
string strMethod = argv[1];
// Parameters default to strings
- Array params;
- for (int i = 2; i < argc; i++)
- params.push_back(argv[i]);
- int n = params.size();
-
- //
- // Special case non-string parameter types
- //
- if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
- if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
- if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
- if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
- if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
- if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
- if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
- if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
- if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
- if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
- if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
- if (strMethod == "getblockhash" && n > 0) ConvertTo<boost::int64_t>(params[0]);
- if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
- if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
- if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
- if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
- if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
- if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
- if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
- if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
- if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
- if (strMethod == "sendmany" && n > 1)
- {
- string s = params[1].get_str();
- Value v;
- if (!read_string(s, v) || v.type() != obj_type)
- throw runtime_error("type mismatch");
- params[1] = v.get_obj();
- }
- if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
- if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
- if (strMethod == "addmultisigaddress" && n > 1)
- {
- string s = params[1].get_str();
- Value v;
- if (!read_string(s, v) || v.type() != array_type)
- throw runtime_error("type mismatch "+s);
- params[1] = v.get_array();
- }
+ std::vector<std::string> strParams(&argv[2], &argv[argc]);
+ Array params = RPCConvertValues(strMethod, strParams);
// Execute
Object reply = CallRPC(strMethod, params);