X-Git-Url: https://git.novaco.in/?p=novacoin.git;a=blobdiff_plain;f=src%2Fbitcoinrpc.cpp;h=a954050b31fe6072a8dcbce395e26353b3702cb6;hp=71725ac3350889096f6d480c8619a722da685315;hb=a49927a46d578d142411c52b17c6f11bb19da03d;hpb=d67b0434f24a629372000b0bd573ea9013ca3d90 diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 71725ac..a954050 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 #include @@ -14,15 +19,10 @@ #include #include #include -#ifdef USE_SSL #include -#include #include typedef boost::asio::ssl::stream 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 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 setDone; - for (map::iterator mi = mapCallTable.begin(); mi != mapCallTable.end(); ++mi) + for (map::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,13 +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 - CreateThread(Shutdown, NULL); + QueueShutdown(); return "bitcoin server stopping"; -#else - throw runtime_error("NYI: cannot shut down GUI with RPC command"); -#endif } @@ -369,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; } @@ -604,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; @@ -636,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; @@ -994,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(); @@ -1006,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)); @@ -1338,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(); @@ -1670,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; } @@ -1691,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; @@ -1713,7 +1707,7 @@ Value encryptwallet(const Array& params, bool fHelp) // BDB seems to have a bad habit of writing old data into // slack space in .dat files; that is bad if the old data is // unencrypted private keys. So: - CreateThread(Shutdown, NULL); + QueueShutdown(); return "wallet encrypted; bitcoin server stopping, restart to run with encrypted wallet"; } @@ -1933,7 +1927,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())); @@ -1955,7 +1949,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; @@ -2015,86 +2009,77 @@ Value getblock(const Array& params, bool fHelp) // Call Table // -pair 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 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 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::const_iterator it = mapCommands.find(name); + if (it == mapCommands.end()) + return NULL; + return (*it).second; +} // // HTTP protocol @@ -2222,7 +2207,7 @@ int ReadHTTP(std::basic_istream& stream, map& 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 @@ -2299,7 +2284,6 @@ bool ClientAllowed(const string& strAddress) return false; } -#ifdef USE_SSL // // IOStream device that speaks SSL but can also speak non-SSL // @@ -2351,7 +2335,6 @@ private: bool fUseSSL; SSLStream& stream; }; -#endif void ThreadRPCServer(void* parg) { @@ -2372,10 +2355,6 @@ void ThreadRPCServer(void* parg) printf("ThreadRPCServer exiting\n"); } -#ifdef QT_GUI -extern bool HACK_SHUTDOWN; -#endif - void ThreadRPCServer2(void* parg) { printf("ThreadRPCServer started\n"); @@ -2398,12 +2377,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 - CreateThread(Shutdown, NULL); -#endif + QueueShutdown(); return; } @@ -2412,11 +2389,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 { @@ -2427,54 +2399,41 @@ void ThreadRPCServer2(void* parg) } catch(boost::system::system_error &e) { - HACK_SHUTDOWN = true; ThreadSafeMessageBox(strprintf(_("An error occured while setting up the RPC port %i for listening: %s"), endpoint.port(), e.what()), _("Error"), wxOK | wxMODAL); + QueueShutdown(); 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 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; @@ -2551,22 +2510,24 @@ void ThreadRPCServer2(void* parg) throw JSONRPCError(-32600, "Params must be an array"); // Find method - map::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); @@ -2597,11 +2558,10 @@ Object CallRPC(const string& strMethod, const Array& params) throw runtime_error(strprintf( _("You must set rpcpassword= 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); @@ -2610,15 +2570,6 @@ Object CallRPC(const string& strMethod, const Array& params) iostreams::stream 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"]); @@ -2816,3 +2767,5 @@ int main(int argc, char *argv[]) return 0; } #endif + +const CRPCTable tableRPC;