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