aboutsummaryrefslogtreecommitdiff
path: root/src/bitcoinrpc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bitcoinrpc.cpp')
-rw-r--r--src/bitcoinrpc.cpp308
1 files changed, 132 insertions, 176 deletions
diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp
index 93d4d8fd64..15d1a7450c 100644
--- a/src/bitcoinrpc.cpp
+++ b/src/bitcoinrpc.cpp
@@ -3,10 +3,15 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "headers.h"
+#include "main.h"
+#include "wallet.h"
#include "db.h"
+#include "walletdb.h"
#include "net.h"
#include "init.h"
+#include "ui_interface.h"
+#include "bitcoinrpc.h"
+
#undef printf
#include <boost/asio.hpp>
#include <boost/filesystem.hpp>
@@ -14,15 +19,10 @@
#include <boost/iostreams/stream.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
-#ifdef USE_SSL
#include <boost/asio/ssl.hpp>
-#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> SSLStream;
-#endif
-#include "json/json_spirit_reader_template.h"
-#include "json/json_spirit_writer_template.h"
-#include "json/json_spirit_utils.h"
+
#define printf OutputDebugStringF
// MinGW 3.4.5 gets "fatal error: had to relocate PCH" if the json headers are
// precompiled in headers.h. The problem might be when the pch file goes over
@@ -35,8 +35,6 @@ using namespace boost::asio;
using namespace json_spirit;
void ThreadRPCServer2(void* parg);
-typedef Value(*rpcfn_type)(const Array& params, bool fHelp);
-extern map<string, rpcfn_type> mapCallTable;
static std::string strRPCUserColonPass;
@@ -140,7 +138,7 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex)
{
Object result;
result.push_back(Pair("hash", block.GetHash().GetHex()));
- result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK)));
+ 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));
result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex()));
@@ -166,23 +164,14 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex)
/// Note: This interface may still be subject to change.
///
-
-Value help(const Array& params, bool fHelp)
+string CRPCTable::help(string strCommand) const
{
- if (fHelp || params.size() > 1)
- throw runtime_error(
- "help [command]\n"
- "List commands, or get help for a command.");
-
- string strCommand;
- if (params.size() > 0)
- strCommand = params[0].get_str();
-
string strRet;
set<rpcfn_type> setDone;
- for (map<string, rpcfn_type>::iterator mi = mapCallTable.begin(); mi != mapCallTable.end(); ++mi)
+ for (map<string, const CRPCCommand*>::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi)
{
- string strMethod = (*mi).first;
+ const CRPCCommand *pcmd = mi->second;
+ string strMethod = mi->first;
// We already filter duplicates, but these deprecated screw up the sort order
if (strMethod == "getamountreceived" ||
strMethod == "getallreceived" ||
@@ -194,7 +183,7 @@ Value help(const Array& params, bool fHelp)
try
{
Array params;
- rpcfn_type pfn = (*mi).second;
+ rpcfn_type pfn = pcmd->actor;
if (setDone.insert(pfn).second)
(*pfn)(params, true);
}
@@ -214,6 +203,20 @@ Value help(const Array& params, bool fHelp)
return strRet;
}
+Value help(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() > 1)
+ throw runtime_error(
+ "help [command]\n"
+ "List commands, or get help for a command.");
+
+ string strCommand;
+ if (params.size() > 0)
+ strCommand = params[0].get_str();
+
+ return tableRPC.help(strCommand);
+}
+
Value stop(const Array& params, bool fHelp)
{
@@ -221,14 +224,9 @@ Value stop(const Array& params, bool fHelp)
throw runtime_error(
"stop\n"
"Stop bitcoin server.");
-#ifndef QT_GUI
// Shutdown will take long enough that the response should get back
- // NOTE: This should actually work with Bitcoin-Qt too now, but 0.6.0 didn't allow it
StartShutdown();
return "bitcoin server stopping";
-#else
- throw runtime_error("NYI: cannot shut down GUI with RPC command");
-#endif
}
@@ -370,7 +368,7 @@ Value getmininginfo(const Array& params, bool fHelp)
obj.push_back(Pair("generate", GetBoolArg("-gen")));
obj.push_back(Pair("genproclimit", (int)GetArg("-genproclimit", -1)));
obj.push_back(Pair("hashespersec", gethashespersec(params, false)));
- obj.push_back(Pair("pooledtx", (uint64_t)nPooledTx));
+ obj.push_back(Pair("pooledtx", (uint64_t)mempool.size()));
obj.push_back(Pair("testnet", fTestNet));
return obj;
}
@@ -605,7 +603,7 @@ Value signmessage(const Array& params, bool fHelp)
if (!pwalletMain->GetKey(addr, key))
throw JSONRPCError(-4, "Private key not available");
- CDataStream ss(SER_GETHASH);
+ CDataStream ss(SER_GETHASH, 0);
ss << strMessageMagic;
ss << strMessage;
@@ -637,7 +635,7 @@ Value verifymessage(const Array& params, bool fHelp)
if (fInvalid)
throw JSONRPCError(-5, "Malformed base64 encoding");
- CDataStream ss(SER_GETHASH);
+ CDataStream ss(SER_GETHASH, 0);
ss << strMessageMagic;
ss << strMessage;
@@ -995,8 +993,6 @@ Value addmultisigaddress(const Array& params, bool fHelp)
"If [account] is specified, assign address to [account].";
throw runtime_error(msg);
}
- if (!fTestNet)
- throw runtime_error("addmultisigaddress available only when running -testnet\n");
int nRequired = params[0].get_int();
const Array& keys = params[1].get_array();
@@ -1007,7 +1003,7 @@ Value addmultisigaddress(const Array& params, bool fHelp)
// Gather public keys
if (nRequired < 1)
throw runtime_error("a multisignature address must require at least one key to redeem");
- if (keys.size() < nRequired)
+ if ((int)keys.size() < nRequired)
throw runtime_error(
strprintf("not enough keys supplied "
"(got %d keys, but need at least %d to redeem)", keys.size(), nRequired));
@@ -1339,8 +1335,10 @@ Value listtransactions(const Array& params, bool fHelp)
}
// ret is newest to oldest
- if (nFrom > ret.size()) nFrom = ret.size();
- if (nFrom+nCount > ret.size()) nCount = ret.size()-nFrom;
+ if (nFrom > (int)ret.size())
+ nFrom = ret.size();
+ if ((nFrom + nCount) > (int)ret.size())
+ nCount = ret.size() - nFrom;
Array::iterator first = ret.begin();
std::advance(first, nFrom);
Array::iterator last = ret.begin();
@@ -1671,8 +1669,8 @@ Value walletlock(const Array& params, bool fHelp)
if (!pwalletMain->IsCrypted())
throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletlock was called.");
- CRITICAL_BLOCK(cs_nWalletUnlockTime)
{
+ LOCK(cs_nWalletUnlockTime);
pwalletMain->Lock();
nWalletUnlockTime = 0;
}
@@ -1692,11 +1690,6 @@ Value encryptwallet(const Array& params, bool fHelp)
if (pwalletMain->IsCrypted())
throw JSONRPCError(-15, "Error: running with an encrypted wallet, but encryptwallet was called.");
-#ifdef QT_GUI
- // shutting down via RPC while the GUI is running does not work (yet):
- throw runtime_error("Not Yet Implemented: use GUI to encrypt wallet, not RPC command");
-#endif
-
// TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
// Alternately, find a way to make params[0] mlock()'d to begin with.
SecureString strWalletPass;
@@ -1959,7 +1952,7 @@ Value getmemorypool(const Array& params, bool fHelp)
if(tx.IsCoinBase())
continue;
- CDataStream ssTx;
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << tx;
transactions.push_back(HexStr(ssTx.begin(), ssTx.end()));
@@ -1982,7 +1975,7 @@ Value getmemorypool(const Array& params, bool fHelp)
else
{
// Parse parameters
- CDataStream ssBlock(ParseHex(params[0].get_str()));
+ CDataStream ssBlock(ParseHex(params[0].get_str()), SER_NETWORK, PROTOCOL_VERSION);
CBlock pblock;
ssBlock >> pblock;
@@ -2042,86 +2035,77 @@ Value getblock(const Array& params, bool fHelp)
// Call Table
//
-pair<string, rpcfn_type> pCallTable[] =
-{
- make_pair("help", &help),
- make_pair("stop", &stop),
- make_pair("getblockcount", &getblockcount),
- make_pair("getblocknumber", &getblocknumber),
- make_pair("getconnectioncount", &getconnectioncount),
- make_pair("getdifficulty", &getdifficulty),
- make_pair("getgenerate", &getgenerate),
- make_pair("setgenerate", &setgenerate),
- make_pair("gethashespersec", &gethashespersec),
- make_pair("getinfo", &getinfo),
- make_pair("getmininginfo", &getmininginfo),
- make_pair("getnewaddress", &getnewaddress),
- make_pair("getaccountaddress", &getaccountaddress),
- make_pair("setaccount", &setaccount),
- make_pair("getaccount", &getaccount),
- make_pair("getaddressesbyaccount", &getaddressesbyaccount),
- make_pair("sendtoaddress", &sendtoaddress),
- make_pair("getreceivedbyaddress", &getreceivedbyaddress),
- make_pair("getreceivedbyaccount", &getreceivedbyaccount),
- make_pair("listreceivedbyaddress", &listreceivedbyaddress),
- make_pair("listreceivedbyaccount", &listreceivedbyaccount),
- make_pair("backupwallet", &backupwallet),
- make_pair("keypoolrefill", &keypoolrefill),
- make_pair("walletpassphrase", &walletpassphrase),
- make_pair("walletpassphrasechange", &walletpassphrasechange),
- make_pair("walletlock", &walletlock),
- make_pair("encryptwallet", &encryptwallet),
- make_pair("validateaddress", &validateaddress),
- make_pair("getbalance", &getbalance),
- make_pair("move", &movecmd),
- make_pair("sendfrom", &sendfrom),
- make_pair("sendmany", &sendmany),
- make_pair("addmultisigaddress", &addmultisigaddress),
- make_pair("getblock", &getblock),
- make_pair("getblockhash", &getblockhash),
- make_pair("gettransaction", &gettransaction),
- make_pair("listtransactions", &listtransactions),
- make_pair("signmessage", &signmessage),
- make_pair("verifymessage", &verifymessage),
- make_pair("getwork", &getwork),
- make_pair("listaccounts", &listaccounts),
- make_pair("settxfee", &settxfee),
- make_pair("getmemorypool", &getmemorypool),
- make_pair("listsinceblock", &listsinceblock),
- make_pair("dumpprivkey", &dumpprivkey),
- make_pair("importprivkey", &importprivkey)
-};
-map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
-
-string pAllowInSafeMode[] =
-{
- "help",
- "stop",
- "getblockcount",
- "getblocknumber", // deprecated
- "getconnectioncount",
- "getdifficulty",
- "getgenerate",
- "setgenerate",
- "gethashespersec",
- "getinfo",
- "getmininginfo",
- "getnewaddress",
- "getaccountaddress",
- "getaccount",
- "getaddressesbyaccount",
- "backupwallet",
- "keypoolrefill",
- "walletpassphrase",
- "walletlock",
- "validateaddress",
- "getwork",
- "getmemorypool",
+
+static const CRPCCommand vRPCCommands[] =
+{ // name function safe mode?
+ // ------------------------ ----------------------- ----------
+ { "help", &help, true },
+ { "stop", &stop, true },
+ { "getblockcount", &getblockcount, true },
+ { "getblocknumber", &getblocknumber, true },
+ { "getconnectioncount", &getconnectioncount, true },
+ { "getdifficulty", &getdifficulty, true },
+ { "getgenerate", &getgenerate, true },
+ { "setgenerate", &setgenerate, true },
+ { "gethashespersec", &gethashespersec, true },
+ { "getinfo", &getinfo, true },
+ { "getmininginfo", &getmininginfo, true },
+ { "getnewaddress", &getnewaddress, true },
+ { "getaccountaddress", &getaccountaddress, true },
+ { "setaccount", &setaccount, true },
+ { "getaccount", &getaccount, false },
+ { "getaddressesbyaccount", &getaddressesbyaccount, true },
+ { "sendtoaddress", &sendtoaddress, false },
+ { "getreceivedbyaddress", &getreceivedbyaddress, false },
+ { "getreceivedbyaccount", &getreceivedbyaccount, false },
+ { "listreceivedbyaddress", &listreceivedbyaddress, false },
+ { "listreceivedbyaccount", &listreceivedbyaccount, false },
+ { "backupwallet", &backupwallet, true },
+ { "keypoolrefill", &keypoolrefill, true },
+ { "walletpassphrase", &walletpassphrase, true },
+ { "walletpassphrasechange", &walletpassphrasechange, false },
+ { "walletlock", &walletlock, true },
+ { "encryptwallet", &encryptwallet, false },
+ { "validateaddress", &validateaddress, true },
+ { "getbalance", &getbalance, false },
+ { "move", &movecmd, false },
+ { "sendfrom", &sendfrom, false },
+ { "sendmany", &sendmany, false },
+ { "addmultisigaddress", &addmultisigaddress, false },
+ { "getblock", &getblock, false },
+ { "getblockhash", &getblockhash, false },
+ { "gettransaction", &gettransaction, false },
+ { "listtransactions", &listtransactions, false },
+ { "signmessage", &signmessage, false },
+ { "verifymessage", &verifymessage, false },
+ { "getwork", &getwork, true },
+ { "listaccounts", &listaccounts, false },
+ { "settxfee", &settxfee, false },
+ { "getmemorypool", &getmemorypool, true },
+ { "listsinceblock", &listsinceblock, false },
+ { "dumpprivkey", &dumpprivkey, false },
+ { "importprivkey", &importprivkey, false },
};
-set<string> setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0]));
+CRPCTable::CRPCTable()
+{
+ unsigned int vcidx;
+ for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++)
+ {
+ const CRPCCommand *pcmd;
+ pcmd = &vRPCCommands[vcidx];
+ mapCommands[pcmd->name] = pcmd;
+ }
+}
+const CRPCCommand *CRPCTable::operator[](string name) const
+{
+ map<string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
+ if (it == mapCommands.end())
+ return NULL;
+ return (*it).second;
+}
//
// HTTP protocol
@@ -2249,7 +2233,7 @@ int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRe
// Read header
int nLen = ReadHTTPHeader(stream, mapHeadersRet);
- if (nLen < 0 || nLen > MAX_SIZE)
+ if (nLen < 0 || nLen > (int)MAX_SIZE)
return 500;
// Read message
@@ -2326,7 +2310,6 @@ bool ClientAllowed(const string& strAddress)
return false;
}
-#ifdef USE_SSL
//
// IOStream device that speaks SSL but can also speak non-SSL
//
@@ -2378,7 +2361,6 @@ private:
bool fUseSSL;
SSLStream& stream;
};
-#endif
void ThreadRPCServer(void* parg)
{
@@ -2421,12 +2403,10 @@ void ThreadRPCServer2(void* parg)
"(you do not need to remember this password)\n"
"If the file does not exist, create it with owner-readable-only file permissions.\n"),
strWhatAmI.c_str(),
- GetConfigFile().c_str(),
+ GetConfigFile().string().c_str(),
EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()),
_("Error"), wxOK | wxMODAL);
-#ifndef QT_GUI
StartShutdown();
-#endif
return;
}
@@ -2435,11 +2415,6 @@ void ThreadRPCServer2(void* parg)
asio::io_service io_service;
ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332));
-#ifndef QT_GUI
- ip::tcp::acceptor acceptor(io_service, endpoint);
-
- acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
-#else
ip::tcp::acceptor acceptor(io_service);
try
{
@@ -2455,49 +2430,36 @@ void ThreadRPCServer2(void* parg)
StartShutdown();
return;
}
-#endif
-#ifdef USE_SSL
ssl::context context(io_service, ssl::context::sslv23);
if (fUseSSL)
{
context.set_options(ssl::context::no_sslv2);
- filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert");
- if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile;
- if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str());
- else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str());
- filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem");
- if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile;
- if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem);
- else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str());
-
- string ciphers = GetArg("-rpcsslciphers",
- "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
- SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str());
- }
-#else
- if (fUseSSL)
- throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries.");
-#endif
+
+ filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert"));
+ if (!pathCertFile.is_complete()) pathCertFile = filesystem::path(GetDataDir()) / pathCertFile;
+ if (filesystem::exists(pathCertFile)) context.use_certificate_chain_file(pathCertFile.string());
+ else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string().c_str());
+
+ filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem"));
+ if (!pathPKFile.is_complete()) pathPKFile = filesystem::path(GetDataDir()) / pathPKFile;
+ if (filesystem::exists(pathPKFile)) context.use_private_key_file(pathPKFile.string(), ssl::context::pem);
+ else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string().c_str());
+
+ string strCiphers = GetArg("-rpcsslciphers", "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
+ SSL_CTX_set_cipher_list(context.impl(), strCiphers.c_str());
+ }
loop
{
// Accept connection
-#ifdef USE_SSL
SSLStream sslStream(io_service, context);
SSLIOStreamDevice d(sslStream, fUseSSL);
iostreams::stream<SSLIOStreamDevice> stream(d);
-#else
- ip::tcp::iostream stream;
-#endif
ip::tcp::endpoint peer;
vnThreadsRunning[THREAD_RPCSERVER]--;
-#ifdef USE_SSL
acceptor.accept(sslStream.lowest_layer(), peer);
-#else
- acceptor.accept(*stream.rdbuf(), peer);
-#endif
vnThreadsRunning[4]++;
if (fShutdown)
return;
@@ -2574,22 +2536,24 @@ void ThreadRPCServer2(void* parg)
throw JSONRPCError(-32600, "Params must be an array");
// Find method
- map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod);
- if (mi == mapCallTable.end())
+ const CRPCCommand *pcmd = tableRPC[strMethod];
+ if (!pcmd)
throw JSONRPCError(-32601, "Method not found");
// Observe safe mode
string strWarning = GetWarnings("rpc");
- if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod))
+ if (strWarning != "" && !GetBoolArg("-disablesafemode") &&
+ !pcmd->okSafeMode)
throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
try
{
// Execute
Value result;
- CRITICAL_BLOCK(cs_main)
- CRITICAL_BLOCK(pwalletMain->cs_wallet)
- result = (*(*mi).second)(params, false);
+ {
+ LOCK2(cs_main, pwalletMain->cs_wallet);
+ result = pcmd->actor(params, false);
+ }
// Send reply
string strReply = JSONRPCReply(result, Value::null, id);
@@ -2620,11 +2584,10 @@ Object CallRPC(const string& strMethod, const Array& params)
throw runtime_error(strprintf(
_("You must set rpcpassword=<password> in the configuration file:\n%s\n"
"If the file does not exist, create it with owner-readable-only file permissions."),
- GetConfigFile().c_str()));
+ GetConfigFile().string().c_str()));
// Connect to localhost
bool fUseSSL = GetBoolArg("-rpcssl");
-#ifdef USE_SSL
asio::io_service io_service;
ssl::context context(io_service, ssl::context::sslv23);
context.set_options(ssl::context::no_sslv2);
@@ -2633,15 +2596,6 @@ Object CallRPC(const string& strMethod, const Array& params)
iostreams::stream<SSLIOStreamDevice> stream(d);
if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332")))
throw runtime_error("couldn't connect to server");
-#else
- if (fUseSSL)
- throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries.");
-
- ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332"));
- if (stream.fail())
- throw runtime_error("couldn't connect to server");
-#endif
-
// HTTP basic authentication
string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
@@ -2839,3 +2793,5 @@ int main(int argc, char *argv[])
return 0;
}
#endif
+
+const CRPCTable tableRPC;