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
+}