Mercurial > hg > bitcoin
changeset 204:80b92dbd504a draft
Gavin Andresen's JSON-RPC HTTP authentication,
faster initial block download
-- version 0.3.3
git-svn-id: https://bitcoin.svn.sourceforge.net/svnroot/bitcoin/trunk@109 1a98c847-1fd6-4fd8-948a-caf3550aa51b
author | s_nakamoto <s_nakamoto@1a98c847-1fd6-4fd8-948a-caf3550aa51b> |
---|---|
date | Sun, 25 Jul 2010 16:45:21 +0000 |
parents | 091b5b2c8349 |
children | 7cbbf84c588e |
files | db.cpp db.h headers.h init.cpp main.cpp main.h makefile.mingw makefile.osx makefile.unix makefile.vc rpc.cpp serialize.h setup.nsi ui.cpp uibase.h uiproject.fbp util.cpp util.h |
diffstat | 18 files changed, 405 insertions(+), 137 deletions(-) [+] |
line wrap: on
line diff
--- a/db.cpp +++ b/db.cpp @@ -130,7 +130,14 @@ vTxn.front()->abort(); vTxn.clear(); pdb = NULL; - dbenv.txn_checkpoint(0, 0, 0); + + // Flush database activity from memory pool to disk log + unsigned int nMinutes = 0; + if (strFile == "addr.dat") + nMinutes = 2; + if (strFile == "blkindex.dat" && IsInitialBlockDownload() && nBestHeight % 500 != 0) + nMinutes = 1; + dbenv.txn_checkpoint(0, nMinutes, 0); CRITICAL_BLOCK(cs_db) --mapFileUseCount[strFile]; @@ -357,11 +364,12 @@ bool CTxDB::LoadBlockIndex() { - // Get cursor + // Get database cursor Dbc* pcursor = GetCursor(); if (!pcursor) return false; + // Load mapBlockIndex unsigned int fFlags = DB_SET_RANGE; loop { @@ -398,7 +406,7 @@ pindexNew->nBits = diskindex.nBits; pindexNew->nNonce = diskindex.nNonce; - // Watch for genesis block and best block + // Watch for genesis block if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock) pindexGenesisBlock = pindexNew; } @@ -409,17 +417,33 @@ } pcursor->close(); + // Calculate bnChainWork + vector<pair<int, CBlockIndex*> > vSortedByHeight; + vSortedByHeight.reserve(mapBlockIndex.size()); + foreach(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) + { + CBlockIndex* pindex = item.second; + vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex)); + } + sort(vSortedByHeight.begin(), vSortedByHeight.end()); + foreach(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight) + { + CBlockIndex* pindex = item.second; + pindex->bnChainWork = (pindex->pprev ? pindex->pprev->bnChainWork : 0) + pindex->GetBlockWork(); + } + + // Load hashBestChain pointer to end of best chain if (!ReadHashBestChain(hashBestChain)) { if (pindexGenesisBlock == NULL) return true; - return error("CTxDB::LoadBlockIndex() : hashBestChain not found"); + return error("CTxDB::LoadBlockIndex() : hashBestChain not loaded"); } - if (!mapBlockIndex.count(hashBestChain)) - return error("CTxDB::LoadBlockIndex() : blockindex for hashBestChain not found"); + return error("CTxDB::LoadBlockIndex() : hashBestChain not found in the block index"); pindexBest = mapBlockIndex[hashBestChain]; nBestHeight = pindexBest->nHeight; + bnBestChainWork = pindexBest->bnChainWork; printf("LoadBlockIndex(): hashBestChain=%s height=%d\n", hashBestChain.ToString().substr(0,16).c_str(), nBestHeight); return true;
--- a/db.h +++ b/db.h @@ -16,7 +16,7 @@ extern CCriticalSection cs_mapAddressBook; extern vector<unsigned char> vchDefaultKey; extern bool fClient; - +extern int nBestHeight; extern unsigned int nWalletDBUpdated; @@ -210,7 +210,7 @@ if (!pdb) return false; DbTxn* ptxn = NULL; - int ret = dbenv.txn_begin(GetTxn(), &ptxn, 0); + int ret = dbenv.txn_begin(GetTxn(), &ptxn, DB_TXN_NOSYNC); if (!ptxn || ret != 0) return false; vTxn.push_back(ptxn);
--- a/headers.h +++ b/headers.h @@ -26,6 +26,7 @@ #include <wx/clipbrd.h> #include <wx/taskbar.h> #endif +#include <openssl/buffer.h> #include <openssl/ecdsa.h> #include <openssl/evp.h> #include <openssl/rand.h> @@ -64,6 +65,9 @@ #include <boost/interprocess/sync/interprocess_recursive_mutex.hpp> #include <boost/date_time/gregorian/gregorian_types.hpp> #include <boost/date_time/posix_time/posix_time_types.hpp> +#include <boost/config.hpp> +#include <boost/program_options/detail/config_file.hpp> +#include <boost/program_options/parsers.hpp> #ifdef __WXMSW__ #include <windows.h>
--- a/init.cpp +++ b/init.cpp @@ -240,33 +240,34 @@ bool CMyApp::Initialize(int& argc, wxChar** argv) { - if (argc > 1 && argv[1][0] != '-' && (!fWindows || argv[1][0] != '/') && - wxString(argv[1]) != "start") - { - fCommandLine = true; - } - else if (!fGUI) - { - fDaemon = true; - } - else + for (int i = 1; i < argc; i++) + if (!IsSwitchChar(argv[i][0])) + fCommandLine = true; + + if (!fCommandLine) { - // wxApp::Initialize will remove environment-specific parameters, - // so it's too early to call ParseParameters yet - for (int i = 1; i < argc; i++) + if (!fGUI) + { + fDaemon = true; + } + else { - wxString str = argv[i]; - #ifdef __WXMSW__ - if (str.size() >= 1 && str[0] == '/') - str[0] = '-'; - char pszLower[MAX_PATH]; - strlcpy(pszLower, str.c_str(), sizeof(pszLower)); - strlwr(pszLower); - str = pszLower; - #endif - // haven't decided which argument to use for this yet - if (str == "-daemon" || str == "-d" || str == "start") - fDaemon = true; + // wxApp::Initialize will remove environment-specific parameters, + // so it's too early to call ParseParameters yet + for (int i = 1; i < argc; i++) + { + wxString str = argv[i]; + #ifdef __WXMSW__ + if (str.size() >= 1 && str[0] == '/') + str[0] = '-'; + char pszLower[MAX_PATH]; + strlcpy(pszLower, str.c_str(), sizeof(pszLower)); + strlwr(pszLower); + str = pszLower; + #endif + if (str == "-daemon") + fDaemon = true; + } } } @@ -375,22 +376,23 @@ // // Parameters // - if (fCommandLine) - { - int ret = CommandLineRPC(argc, argv); - exit(ret); - } + ParseParameters(argc, argv); - ParseParameters(argc, argv); + if (mapArgs.count("-datadir")) + strlcpy(pszSetDataDir, mapArgs["-datadir"].c_str(), sizeof(pszSetDataDir)); + + ReadConfigFile(mapArgs, mapMultiArgs); // Must be done after processing datadir + if (mapArgs.count("-?") || mapArgs.count("--help")) { wxString strUsage = string() + _("Usage:") + "\t\t\t\t\t\t\t\t\t\t\n" + - " bitcoin [options] \t" + "\n" + - " bitcoin [command] \t" + _("Send command to bitcoin running with -server or -daemon\n") + - " bitcoin [command] -? \t" + _("Get help for a command\n") + - " bitcoin help <pw> \t" + _("List commands\n") + + " bitcoin [options] \t " + "\n" + + " bitcoin [options] <command> [params]\t " + _("Send command to -server or bitcoind\n") + + " bitcoin [options] <command> -? \t\t " + _("Get help for a command\n") + + " bitcoin help \t\t\t " + _("List commands\n") + _("Options:\n") + + " -conf=<file> \t " + _("Specify configuration file (default: bitcoin.conf)\n") + " -gen \t " + _("Generate coins\n") + " -gen=0 \t " + _("Don't generate coins\n") + " -min \t " + _("Start minimized\n") + @@ -398,7 +400,7 @@ " -proxy=<ip:port>\t " + _("Connect through socks4 proxy\n") + " -addnode=<ip> \t " + _("Add a node to connect to\n") + " -connect=<ip> \t " + _("Connect only to the specified node\n") + - " -rpcpw=<pw> \t " + _("Accept command line and JSON-RPC commands with the given password\n") + + " -server \t " + _("Accept command line and JSON-RPC commands\n") + " -daemon \t " + _("Run in the background as a daemon and accept commands\n") + " -? \t " + _("This help message\n"); @@ -413,15 +415,18 @@ return false; } - if (mapArgs.count("-datadir")) - strlcpy(pszSetDataDir, mapArgs["-datadir"].c_str(), sizeof(pszSetDataDir)); - if (mapArgs.count("-debug")) fDebug = true; if (mapArgs.count("-printtodebugger")) fPrintToDebugger = true; + if (fCommandLine) + { + int ret = CommandLineRPC(argc, argv); + exit(ret); + } + if (!fDebug && !pszSetDataDir[0]) ShrinkDebugFile(); printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); @@ -611,7 +616,7 @@ if (!CreateThread(StartNode, NULL)) wxMessageBox("Error: CreateThread(StartNode) failed", "Bitcoin"); - if (mapArgs.count("-server") || mapArgs.count("-rpcpw") || fDaemon) + if (mapArgs.count("-server") || fDaemon) CreateThread(ThreadRPCServer, NULL); if (fFirstRun)
--- a/main.cpp +++ b/main.cpp @@ -24,6 +24,7 @@ const uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); CBlockIndex* pindexGenesisBlock = NULL; int nBestHeight = -1; +CBigNum bnBestChainWork = 0; uint256 hashBestChain = 0; CBlockIndex* pindexBest = NULL; int64 nTimeBestReceived = 0; @@ -848,6 +849,23 @@ return bnNew.GetCompact(); } +vector<int> vStartingHeight; +void AddStartingHeight(int nStartingHeight) +{ + if (nStartingHeight != -1) + { + vStartingHeight.push_back(nStartingHeight); + sort(vStartingHeight.begin(), vStartingHeight.end()); + } +} + +bool IsInitialBlockDownload() +{ + int nMedian = 69000; + if (vStartingHeight.size() >= 5) + nMedian = vStartingHeight[vStartingHeight.size()/2]; + return nBestHeight < nMedian-1000; +} @@ -1208,13 +1226,14 @@ pindexNew->pprev = (*miPrev).second; pindexNew->nHeight = pindexNew->pprev->nHeight + 1; } + pindexNew->bnChainWork = (pindexNew->pprev ? pindexNew->pprev->bnChainWork : 0) + pindexNew->GetBlockWork(); CTxDB txdb; txdb.TxnBegin(); txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew)); // New best - if (pindexNew->nHeight > nBestHeight) + if (pindexNew->bnChainWork > bnBestChainWork) { if (pindexGenesisBlock == NULL && hash == hashGenesisBlock) { @@ -1253,6 +1272,7 @@ hashBestChain = hash; pindexBest = pindexNew; nBestHeight = pindexBest->nHeight; + bnBestChainWork = pindexNew->bnChainWork; nTimeBestReceived = GetTime(); nTransactionsUpdated++; printf("AddToBlockIndex: new best=%s height=%d\n", hashBestChain.ToString().substr(0,16).c_str(), nBestHeight); @@ -1900,6 +1920,7 @@ } AddTimeData(pfrom->addr.ip, nTime); + AddStartingHeight(pfrom->nStartingHeight); // Change version if (pfrom->nVersion >= 209) @@ -2845,6 +2866,10 @@ } +int GetRandInt(int nMax) +{ + return GetRand(nMax); +} bool SelectCoins(int64 nTargetValue, set<CWalletTx*>& setCoinsRet) { @@ -2858,9 +2883,14 @@ CRITICAL_BLOCK(cs_mapWallet) { - for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - CWalletTx* pcoin = &(*it).second; + vector<CWalletTx*> vCoins; + vCoins.reserve(mapWallet.size()); + for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + vCoins.push_back(&(*it).second); + random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); + + foreach(CWalletTx* pcoin, vCoins) + { if (!pcoin->IsFinal() || pcoin->fSpent) continue; int64 n = pcoin->GetCredit();
--- a/main.h +++ b/main.h @@ -32,6 +32,7 @@ extern const uint256 hashGenesisBlock; extern CBlockIndex* pindexGenesisBlock; extern int nBestHeight; +extern CBigNum bnBestChainWork; extern uint256 hashBestChain; extern CBlockIndex* pindexBest; extern unsigned int nTransactionsUpdated; @@ -78,6 +79,7 @@ void GenerateBitcoins(bool fGenerate); void ThreadBitcoinMiner(void* parg); void BitcoinMiner(); +bool IsInitialBlockDownload(); @@ -986,11 +988,14 @@ // Flush stdio buffers and commit to disk before returning fflush(fileout); + if (!IsInitialBlockDownload() || (nBestHeight+1) % 500 == 0) + { #ifdef __WXMSW__ - _commit(_fileno(fileout)); + _commit(_fileno(fileout)); #else - fsync(fileno(fileout)); + fsync(fileno(fileout)); #endif + } return true; } @@ -1072,6 +1077,7 @@ unsigned int nFile; unsigned int nBlockPos; int nHeight; + CBigNum bnChainWork; // block header int nVersion; @@ -1089,6 +1095,7 @@ nFile = 0; nBlockPos = 0; nHeight = 0; + bnChainWork = 0; nVersion = 0; hashMerkleRoot = 0; @@ -1105,6 +1112,7 @@ nFile = nFileIn; nBlockPos = nBlockPosIn; nHeight = 0; + bnChainWork = 0; nVersion = block.nVersion; hashMerkleRoot = block.hashMerkleRoot; @@ -1118,6 +1126,11 @@ return *phashBlock; } + CBigNum GetBlockWork() const + { + return (CBigNum(1)<<256) / (CBigNum().SetCompact(nBits)+1); + } + bool IsInMainChain() const { return (pnext || this == pindexBest);
--- a/makefile.mingw +++ b/makefile.mingw @@ -22,7 +22,7 @@ -l wxmsw29ud_html -l wxmsw29ud_core -l wxmsw29ud_adv -l wxbase29ud -l wxtiffd -l wxjpegd -l wxpngd -l wxzlibd LIBS= \ - -l libboost_system-mgw34-mt-d -l libboost_filesystem-mgw34-mt-d \ + -l libboost_system-mgw34-mt-d -l libboost_filesystem-mgw34-mt-d -l libboost_program_options-mgw34-mt-d \ -l db_cxx \ -l eay32 \ -l kernel32 -l user32 -l gdi32 -l comdlg32 -l winspool -l winmm -l shell32 -l comctl32 -l ole32 -l oleaut32 -l uuid -l rpcrt4 -l advapi32 -l ws2_32 -l shlwapi
--- a/makefile.osx +++ b/makefile.osx @@ -19,6 +19,7 @@ $(DEPSDIR)/lib/libdb_cxx-4.8.a \ $(DEPSDIR)/lib/libboost_system.a \ $(DEPSDIR)/lib/libboost_filesystem.a \ + $(DEPSDIR)/lib/libboost_program_options.a \ $(DEPSDIR)/lib/libcrypto.a WXDEFS=$(shell $(DEPSDIR)/bin/wx-config --cxxflags) -DNOPCH -DMSG_NOSIGNAL=0
--- a/makefile.unix +++ b/makefile.unix @@ -21,7 +21,7 @@ LIBS= \ -Wl,-Bstatic \ - -l boost_system -l boost_filesystem \ + -l boost_system -l boost_filesystem -l boost_program_options \ -l db_cxx \ -l crypto \ -Wl,-Bdynamic \
--- a/makefile.vc +++ b/makefile.vc @@ -19,7 +19,7 @@ /LIBPATH:"/wxwidgets/lib/vc_lib" LIBS= \ - libboost_system-vc80-mt-gd.lib libboost_filesystem-vc80-mt-gd.lib \ + libboost_system-vc80-mt-gd.lib libboost_filesystem-vc80-mt-gd.lib libboost_program_options-vc80-mt-gd.lib \ libdb47sd.lib \ libeay32.lib \ wxmsw29ud_html.lib wxmsw29ud_core.lib wxmsw29ud_adv.lib wxbase29ud.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib \
--- a/rpc.cpp +++ b/rpc.cpp @@ -21,7 +21,27 @@ typedef Value(*rpcfn_type)(const Array& params, bool fHelp); extern map<string, rpcfn_type> mapCallTable; -static string strRPCPassword; + + +void PrintConsole(const char* format, ...) +{ + char buffer[50000]; + int limit = sizeof(buffer); + va_list arg_ptr; + va_start(arg_ptr, format); + int ret = _vsnprintf(buffer, limit, format, arg_ptr); + va_end(arg_ptr); + if (ret < 0 || ret >= limit) + { + ret = limit - 1; + buffer[limit-1] = 0; + } +#if defined(__WXMSW__) && wxUSE_GUI + MyMessageBox(buffer, "Bitcoin", wxOK | wxICON_EXCLAMATION); +#else + fprintf(stdout, "%s", buffer); +#endif +} @@ -34,12 +54,11 @@ /// - Value help(const Array& params, bool fHelp) { if (fHelp || params.size() != 0) throw runtime_error( - "help <pw>\n" + "help\n" "List commands."); string strRet; @@ -76,7 +95,7 @@ { if (fHelp || params.size() != 0) throw runtime_error( - "stop <pw>\n" + "stop\n" "Stop bitcoin server."); // Shutdown will take long enough that the response should get back @@ -89,7 +108,7 @@ { if (fHelp || params.size() != 0) throw runtime_error( - "getblockcount <pw>\n" + "getblockcount\n" "Returns the number of blocks in the longest block chain."); return nBestHeight + 1; @@ -100,7 +119,7 @@ { if (fHelp || params.size() != 0) throw runtime_error( - "getblocknumber <pw>\n" + "getblocknumber\n" "Returns the block number of the latest block in the longest block chain."); return nBestHeight; @@ -111,7 +130,7 @@ { if (fHelp || params.size() != 0) throw runtime_error( - "getconnectioncount <pw>\n" + "getconnectioncount\n" "Returns the number of connections to other nodes."); return (int)vNodes.size(); @@ -134,7 +153,7 @@ { if (fHelp || params.size() != 0) throw runtime_error( - "getdifficulty <pw>\n" + "getdifficulty\n" "Returns the proof-of-work difficulty as a multiple of the minimum difficulty."); return GetDifficulty(); @@ -145,7 +164,7 @@ { if (fHelp || params.size() != 0) throw runtime_error( - "getbalance <pw>\n" + "getbalance\n" "Returns the server's available balance."); return ((double)GetBalance() / (double)COIN); @@ -156,7 +175,7 @@ { if (fHelp || params.size() != 0) throw runtime_error( - "getgenerate <pw>\n" + "getgenerate\n" "Returns true or false."); return (bool)fGenerateBitcoins; @@ -167,7 +186,7 @@ { if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( - "setgenerate <pw> <generate> [genproclimit]\n" + "setgenerate <generate> [genproclimit]\n" "<generate> is true or false to turn generation on or off.\n" "Generation is limited to [genproclimit] processors, -1 is unlimited."); @@ -193,7 +212,7 @@ { if (fHelp || params.size() != 0) throw runtime_error( - "getinfo <pw>"); + "getinfo"); Object obj; obj.push_back(Pair("balance", (double)GetBalance() / (double)COIN)); @@ -211,7 +230,7 @@ { if (fHelp || params.size() > 1) throw runtime_error( - "getnewaddress <pw> [label]\n" + "getnewaddress [label]\n" "Returns a new bitcoin address for receiving payments. " "If [label] is specified (recommended), it is added to the address book " "so payments received with the address will be labeled."); @@ -233,7 +252,7 @@ { if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( - "setlabel <pw> <bitcoinaddress> <label>\n" + "setlabel <bitcoinaddress> <label>\n" "Sets the label associated with the given address."); string strAddress = params[0].get_str(); @@ -250,7 +269,7 @@ { if (fHelp || params.size() != 1) throw runtime_error( - "getlabel <pw> <bitcoinaddress>\n" + "getlabel <bitcoinaddress>\n" "Returns the label associated with the given address."); string strAddress = params[0].get_str(); @@ -270,7 +289,7 @@ { if (fHelp || params.size() != 1) throw runtime_error( - "getaddressesbylabel <pw> <label>\n" + "getaddressesbylabel <label>\n" "Returns the list of addresses with the given label."); string strLabel = params[0].get_str(); @@ -300,7 +319,7 @@ { if (fHelp || params.size() < 2 || params.size() > 4) throw runtime_error( - "sendtoaddress <pw> <bitcoinaddress> <amount> [comment] [comment-to]\n" + "sendtoaddress <bitcoinaddress> <amount> [comment] [comment-to]\n" "<amount> is a real and is rounded to the nearest 0.01"); string strAddress = params[0].get_str(); @@ -328,7 +347,7 @@ { if (fHelp || params.size() > 2) throw runtime_error( - "listtransactions <pw> [count=10] [includegenerated=false]\n" + "listtransactions [count=10] [includegenerated=false]\n" "Returns up to [count] most recent transactions."); int64 nCount = 10; @@ -349,7 +368,7 @@ { if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( - "getreceivedbyaddress <pw> <bitcoinaddress> [minconf=1]\n" + "getreceivedbyaddress <bitcoinaddress> [minconf=1]\n" "Returns the total amount received by <bitcoinaddress> in transactions with at least [minconf] confirmations."); // Bitcoin address @@ -390,7 +409,7 @@ { if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( - "getreceivedbylabel <pw> <label> [minconf=1]\n" + "getreceivedbylabel <label> [minconf=1]\n" "Returns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations."); // Get the set of pub keys that have the label @@ -553,7 +572,7 @@ { if (fHelp || params.size() > 2) throw runtime_error( - "listreceivedbyaddress <pw> [minconf=1] [includeempty=false]\n" + "listreceivedbyaddress [minconf=1] [includeempty=false]\n" "[minconf] is the minimum number of confirmations before payments are included.\n" "[includeempty] whether to include addresses that haven't received any payments.\n" "Returns an array of objects containing:\n" @@ -569,7 +588,7 @@ { if (fHelp || params.size() > 2) throw runtime_error( - "listreceivedbylabel <pw> [minconf=1] [includeempty=false]\n" + "listreceivedbylabel [minconf=1] [includeempty=false]\n" "[minconf] is the minimum number of confirmations before payments are included.\n" "[includeempty] whether to include labels that haven't received any payments.\n" "Returns an array of objects containing:\n" @@ -632,23 +651,41 @@ // and to be compatible with other JSON-RPC implementations. // -string HTTPPost(const string& strMsg) +string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders) { - return strprintf( - "POST / HTTP/1.1\r\n" - "User-Agent: json-rpc/1.0\r\n" - "Host: 127.0.0.1\r\n" - "Content-Type: application/json\r\n" - "Content-Length: %d\r\n" - "Accept: application/json\r\n" - "\r\n" - "%s", - strMsg.size(), - strMsg.c_str()); + ostringstream s; + s << "POST / HTTP/1.1\r\n" + << "User-Agent: json-rpc/1.0\r\n" + << "Host: 127.0.0.1\r\n" + << "Content-Type: application/json\r\n" + << "Content-Length: " << strMsg.size() << "\r\n" + << "Accept: application/json\r\n"; + for (map<string,string>::const_iterator it = mapRequestHeaders.begin(); it != mapRequestHeaders.end(); ++it) + s << it->first << ": " << it->second << "\r\n"; + s << "\r\n" << strMsg; + + return s.str(); } string HTTPReply(const string& strMsg, int nStatus=200) { + if (nStatus == 401) + return "HTTP/1.0 401 Authorization Required\r\n" + "Server: HTTPd/1.0\r\n" + "Date: Sat, 08 Jul 2006 12:04:08 GMT\r\n" + "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 311\r\n" + "\r\n" + "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n" + "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n" + "<HTML>\r\n" + "<HEAD>\r\n" + "<TITLE>Error</TITLE>\r\n" + "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n" + "</HEAD>\r\n" + "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n" + "</HTML>\r\n"; string strStatus; if (nStatus == 200) strStatus = "OK"; if (nStatus == 500) strStatus = "Internal Server Error"; @@ -667,7 +704,17 @@ strMsg.c_str()); } -int ReadHTTPHeader(tcp::iostream& stream) +int ReadHTTPStatus(tcp::iostream& stream) +{ + string str; + getline(stream, str); + vector<string> vWords; + boost::split(vWords, str, boost::is_any_of(" ")); + int nStatus = atoi(vWords[1].c_str()); + return nStatus; +} + +int ReadHTTPHeader(tcp::iostream& stream, map<string, string>& mapHeadersRet) { int nLen = 0; loop @@ -676,26 +723,92 @@ std::getline(stream, str); if (str.empty() || str == "\r") break; - if (str.substr(0,15) == "Content-Length:") - nLen = atoi(str.substr(15)); + string::size_type nColon = str.find(":"); + if (nColon != string::npos) + { + string strHeader = str.substr(0, nColon); + boost::trim(strHeader); + string strValue = str.substr(nColon+1); + boost::trim(strValue); + mapHeadersRet[strHeader] = strValue; + if (strHeader == "Content-Length") + nLen = atoi(strValue.c_str()); + } } return nLen; } -inline string ReadHTTP(tcp::iostream& stream) +int ReadHTTP(tcp::iostream& stream, map<string, string>& mapHeadersRet, string& strMessageRet) { + mapHeadersRet.clear(); + strMessageRet = ""; + + // Read status + int nStatus = ReadHTTPStatus(stream); + // Read header - int nLen = ReadHTTPHeader(stream); + int nLen = ReadHTTPHeader(stream, mapHeadersRet); if (nLen <= 0) - return string(); + return 500; // Read message vector<char> vch(nLen); stream.read(&vch[0], nLen); - return string(vch.begin(), vch.end()); + strMessageRet = string(vch.begin(), vch.end()); + + return nStatus; +} + +string EncodeBase64(string s) +{ + BIO *b64, *bmem; + BUF_MEM *bptr; + + b64 = BIO_new(BIO_f_base64()); + bmem = BIO_new(BIO_s_mem()); + b64 = BIO_push(b64, bmem); + BIO_write(b64, s.c_str(), s.size()); + BIO_flush(b64); + BIO_get_mem_ptr(b64, &bptr); + + string result(bptr->data, bptr->length-1); + BIO_free_all(b64); + + return result; } +string DecodeBase64(string s) +{ + BIO *b64, *bmem; + char* buffer = static_cast<char*>(calloc(s.size(), sizeof(char))); + + b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + bmem = BIO_new_mem_buf(const_cast<char*>(s.c_str()), s.size()); + bmem = BIO_push(b64, bmem); + BIO_read(bmem, buffer, s.size()); + BIO_free_all(bmem); + + string result(buffer); + free(buffer); + return result; +} + +bool HTTPAuthorized(map<string, string>& mapHeaders) +{ + string strAuth = mapHeaders["Authorization"]; + if (strAuth.substr(0,6) != "Basic ") + return false; + string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64); + string strUserPass = DecodeBase64(strUserPass64); + string::size_type nColon = strUserPass.find(":"); + if (nColon == string::npos) + return false; + string strUser = strUserPass.substr(0, nColon); + string strPassword = strUserPass.substr(nColon+1); + return (strUser == mapArgs["-rpcuser"] && strPassword == mapArgs["-rpcpassword"]); +} // // JSON-RPC protocol @@ -751,15 +864,20 @@ { printf("ThreadRPCServer started\n"); - if (mapArgs.count("-rpcpw")) - strRPCPassword = mapArgs["-rpcpw"]; - if (strRPCPassword == "") + if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") { -#if defined(__WXMSW__) && wxUSE_GUI - MyMessageBox("Warning: rpc password is blank, use -rpcpw=<password>\n", "Bitcoin", wxOK | wxICON_EXCLAMATION); -#else - fprintf(stdout, "Warning: rpc password is blank, use -rpcpw=<password>\n"); -#endif + string strWhatAmI = "To use bitcoind"; + if (mapArgs.count("-server")) + strWhatAmI = strprintf(_("To use the %s option"), "\"-server\""); + else if (mapArgs.count("-daemon")) + strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\""); + PrintConsole( + _("Warning: %s, you must set rpcpassword=<password>\nin the configuration file: %s\n" + "If the file does not exist, create it with owner-readable-only file permissions.\n"), + strWhatAmI.c_str(), + GetConfigFile().c_str()); + CreateThread(Shutdown, NULL); + return; } // Bind to loopback 127.0.0.1 so the socket can only be accessed locally @@ -783,7 +901,26 @@ continue; // Receive request - string strRequest = ReadHTTP(stream); + map<string, string> mapHeaders; + string strRequest; + ReadHTTP(stream, mapHeaders, strRequest); + + // Check authorization + if (mapHeaders.count("Authorization") == 0) + { + stream << HTTPReply("", 401) << std::flush; + continue; + } + if (!HTTPAuthorized(mapHeaders)) + { + // Deter brute-forcing short passwords + if (mapArgs["-rpcpassword"].size() < 15) + Sleep(50); + + stream << HTTPReply("", 401) << std::flush; + printf("ThreadRPCServer incorrect password attempt\n"); + continue; + } // Handle multiple invocations per request string::iterator begin = strRequest.begin(); @@ -808,23 +945,11 @@ printf("ThreadRPCServer method=%s\n", strMethod.c_str()); - // Check password - if (params.size() < 1 || params[0].type() != str_type) - throw runtime_error("First parameter must be the password."); - if (params[0].get_str() != strRPCPassword) - { - if (strRPCPassword.size() < 15) - Sleep(50); - begin = strRequest.end(); - printf("ThreadRPCServer incorrect password attempt\n"); - throw runtime_error("Incorrect password."); - } - // Execute map<string, rpcfn_type>::iterator mi = mapCallTable.find(strMethod); if (mi == mapCallTable.end()) throw runtime_error("Method not found."); - Value result = (*(*mi).second)(Array(params.begin()+1, params.end()), false); + Value result = (*(*mi).second)(params, false); // Send reply string strReply = JSONRPCReply(result, Value::null, id); @@ -847,18 +972,36 @@ Value CallRPC(const string& strMethod, const Array& params) { + if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") + 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())); + // Connect to localhost tcp::iostream stream("127.0.0.1", "8332"); if (stream.fail()) throw runtime_error("couldn't connect to server"); + // HTTP basic authentication + string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); + map<string, string> mapRequestHeaders; + mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64; + // Send request string strRequest = JSONRPCRequest(strMethod, params, 1); - stream << HTTPPost(strRequest) << std::flush; + string strPost = HTTPPost(strRequest, mapRequestHeaders); + stream << strPost << std::flush; // Receive reply - string strReply = ReadHTTP(stream); - if (strReply.empty()) + map<string, string> mapHeaders; + string strReply; + int nStatus = ReadHTTP(stream, mapHeaders, strReply); + if (nStatus == 401) + throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); + else if (nStatus >= 400 && nStatus != 500) + throw runtime_error(strprintf("server returned HTTP error %d", nStatus)); + else if (strReply.empty()) throw runtime_error("no response from server"); // Parse reply @@ -904,7 +1047,14 @@ { try { - // Check that method exists + // Skip switches + while (argc > 1 && IsSwitchChar(argv[1][0])) + { + argc--; + argv++; + } + + // Check that the method exists if (argc < 2) throw runtime_error("too few parameters"); string strMethod = argv[1];
--- a/serialize.h +++ b/serialize.h @@ -19,8 +19,8 @@ class CDataStream; class CAutoFile; -static const int VERSION = 302; -static const char* pszSubVer = ".2"; +static const int VERSION = 303; +static const char* pszSubVer = "";
--- a/setup.nsi +++ b/setup.nsi @@ -7,7 +7,7 @@ # General Symbol Definitions !define REGKEY "SOFTWARE\$(^Name)" -!define VERSION 0.3.2 +!define VERSION 0.3.3 !define COMPANY "Bitcoin project" !define URL http://www.bitcoin.org/ @@ -42,12 +42,12 @@ !insertmacro MUI_LANGUAGE English # Installer attributes -OutFile bitcoin-0.3.2-win32-setup.exe +OutFile bitcoin-0.3.3-win32-setup.exe InstallDir $PROGRAMFILES\Bitcoin CRCCheck on XPStyle on ShowInstDetails show -VIProductVersion 0.3.2.0 +VIProductVersion 0.3.3.0 VIAddVersionKey ProductName Bitcoin VIAddVersionKey ProductVersion "${VERSION}" VIAddVersionKey CompanyName "${COMPANY}"
--- a/ui.cpp +++ b/ui.cpp @@ -1614,6 +1614,7 @@ CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent) { m_staticTextVersion->SetLabel(strprintf(_("version %d.%d.%d beta"), VERSION/10000, (VERSION/100)%100, VERSION%100)); + //m_staticTextVersion->SetLabel(strprintf(_("version %d.%d.%d%s beta"), VERSION/10000, (VERSION/100)%100, VERSION%100, pszSubVer)); // Change (c) into UTF-8 or ANSI copyright symbol wxString str = m_staticTextMain->GetLabel();
--- a/uibase.h +++ b/uibase.h @@ -227,7 +227,7 @@ public: wxStaticText* m_staticTextVersion; - CAboutDialogBase( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("About Bitcoin"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 532,329 ), long style = wxDEFAULT_DIALOG_STYLE ); + CAboutDialogBase( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("About Bitcoin"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 532,333 ), long style = wxDEFAULT_DIALOG_STYLE ); ~CAboutDialogBase(); };
--- a/uiproject.fbp +++ b/uiproject.fbp @@ -2866,7 +2866,7 @@ <property name="minimum_size"></property> <property name="name">CAboutDialogBase</property> <property name="pos"></property> - <property name="size">532,329</property> + <property name="size">532,333</property> <property name="style">wxDEFAULT_DIALOG_STYLE</property> <property name="subclass"></property> <property name="title">About Bitcoin</property>
--- a/util.cpp +++ b/util.cpp @@ -416,7 +416,7 @@ { mapArgs.clear(); mapMultiArgs.clear(); - for (int i = 0; i < argc; i++) + for (int i = 1; i < argc; i++) { char psz[10000]; strlcpy(psz, argv[i], sizeof(psz)); @@ -431,6 +431,8 @@ if (psz[0] == '/') psz[0] = '-'; #endif + if (psz[0] != '-') + break; mapArgs[psz] = pszValue; mapMultiArgs[psz].push_back(pszValue); } @@ -619,6 +621,38 @@ return pszDir; } +string GetConfigFile() +{ + namespace fs = boost::filesystem; + fs::path pathConfig(mapArgs.count("-conf") ? mapArgs["-conf"] : string("bitcoin.conf")); + if (!pathConfig.is_complete()) + pathConfig = fs::path(GetDataDir()) / pathConfig; + return pathConfig.string(); +} + +void ReadConfigFile(map<string, string>& mapSettingsRet, + map<string, vector<string> >& mapMultiSettingsRet) +{ + namespace fs = boost::filesystem; + namespace pod = boost::program_options::detail; + + fs::ifstream streamConfig(GetConfigFile()); + if (!streamConfig.good()) + return; + + set<string> setOptions; + setOptions.insert("*"); + + for (pod::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it) + { + // Don't overwrite existing settings so command line settings override bitcoin.conf + string strKey = string("-") + it->string_key; + if (mapSettingsRet.count(strKey) == 0) + mapSettingsRet[strKey] = it->value[0]; + mapMultiSettingsRet[strKey].push_back(it->value[0]); + } +} + int GetFilesize(FILE* file) { int nSavePos = ftell(file); @@ -656,9 +690,6 @@ - - - // // "Never go to sea with two chronometers; take one or three." // Our three chronometers are: @@ -701,7 +732,7 @@ sort(vTimeOffsets.begin(), vTimeOffsets.end()); int64 nMedian = vTimeOffsets[vTimeOffsets.size()/2]; nTimeOffset = nMedian; - if ((nMedian > 0 ? nMedian : -nMedian) > 36 * 60 * 60) + if ((nMedian > 0 ? nMedian : -nMedian) > 70 * 60) { // Only let other nodes change our clock so far before we // go to the NTP servers
--- a/util.h +++ b/util.h @@ -141,6 +141,8 @@ const char* wxGetTranslation(const char* psz); int GetFilesize(FILE* file); void GetDataDir(char* pszDirRet); +string GetConfigFile(); +void ReadConfigFile(map<string, string>& mapSettingsRet, map<string, vector<string> >& mapMultiSettingsRet); #ifdef __WXMSW__ string MyGetSpecialFolderPath(int nFolder, bool fCreate); #endif @@ -348,7 +350,14 @@ ++it; } - +inline bool IsSwitchChar(char c) +{ +#ifdef __WXMSW__ + return c == '-' || c == '/'; +#else + return c == '-'; +#endif +}