changeset 103:52daef3d5aa1 draft

move debug.log and db.log to data dir, portable GetDataDir, optimize GetBalance, fix repaint bogdown, -addnode and -? switches
author s_nakamoto <s_nakamoto@1a98c847-1fd6-4fd8-948a-caf3550aa51b>
date Sun, 01 Nov 2009 01:16:51 +0000
parents 5b1ace67ce26
children 5cda4bd17fd4
files build.txt db.cpp irc.cpp irc.h main.cpp main.h net.cpp net.h ui.cpp ui.h util.cpp util.h
diffstat 12 files changed, 428 insertions(+), 394 deletions(-) [+]
line wrap: on
line diff
--- a/build.txt
+++ b/build.txt
@@ -10,7 +10,7 @@
 
 Compilers Supported
 -------------------
-MinGW GCC
+MinGW GCC (v3.4.5)
 Microsoft Visual C++ 6.0 SP6
 
 
--- a/db.cpp
+++ b/db.cpp
@@ -61,18 +61,19 @@
         {
             if (fShutdown)
                 return;
-            string strAppDir = GetAppDir();
-            string strLogDir = strAppDir + "\\database";
+            string strDataDir = GetDataDir();
+            string strLogDir = strDataDir + "\\database";
             _mkdir(strLogDir.c_str());
-            printf("dbenv.open strAppDir=%s\n", strAppDir.c_str());
+            string strErrorFile = strDataDir + "\\db.log";
+            printf("dbenv.open strLogDir=%s strErrorFile=%s\n", strLogDir.c_str(), strErrorFile.c_str());
 
             dbenv.set_lg_dir(strLogDir.c_str());
             dbenv.set_lg_max(10000000);
             dbenv.set_lk_max_locks(10000);
             dbenv.set_lk_max_objects(10000);
-            dbenv.set_errfile(fopen("db.log", "a")); /// debug
+            dbenv.set_errfile(fopen(strErrorFile.c_str(), "a")); /// debug
             ///dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1); /// causes corruption
-            ret = dbenv.open(strAppDir.c_str(),
+            ret = dbenv.open(strDataDir.c_str(),
                              DB_CREATE     |
                              DB_INIT_LOCK  |
                              DB_INIT_LOG   |
@@ -139,6 +140,8 @@
     // Flush log data to the actual data file
     //  on all files that are not in use
     printf("DBFlush(%s)\n", fShutdown ? "true" : "false");
+    if (!fDbEnvInit)
+        return;
     CRITICAL_BLOCK(cs_db)
     {
         dbenv.txn_checkpoint(0, 0, 0);
@@ -421,7 +424,7 @@
                 while (fgets(psz, sizeof(psz), filein))
                 {
                     CAddress addr(psz, NODE_NETWORK);
-                    if (addr.ip != 0)
+                    if (addr.IsValid())
                     {
                         AddAddress(*this, addr);
                         mapIRCAddresses.insert(make_pair(addr.GetKey(), addr));
@@ -676,10 +679,10 @@
                     {
                         // Flush wallet.dat so it's self contained
                         nLastFlushed == nWalletDBUpdated;
-                        int64 nStart = PerformanceCounter();
+                        int64 nStart = GetTimeMillis();
                         dbenv.txn_checkpoint(0, 0, 0);
                         dbenv.lsn_reset(strFile.c_str(), 0);
-                        printf("Flushed wallet.dat %15"PRI64d"\n", PerformanceCounter() - nStart);
+                        printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);
                         mapFileUseCount.erase(mi++);
                     }
                 }
--- a/irc.cpp
+++ b/irc.cpp
@@ -40,7 +40,7 @@
         return false;
     memcpy(&tmp, &vch[0], sizeof(tmp));
 
-    addr  = CAddress(tmp.ip, tmp.port);
+    addr = CAddress(tmp.ip, tmp.port, NODE_NETWORK);
     return true;
 }
 
@@ -163,6 +163,7 @@
     int nErrorWait = 10;
     int nRetryWait = 10;
 
+    // IRC server blocks TOR users
     if (fUseProxy && addrProxy.port == htons(9050))
         return;
 
@@ -237,14 +238,14 @@
             {
                 // index 7 is limited to 16 characters
                 // could get full length name at index 10, but would be different from join messages
-                strcpy(pszName, vWords[7].c_str());
+                strlcpy(pszName, vWords[7].c_str(), sizeof(pszName));
                 printf("IRC got who\n");
             }
 
             if (vWords[1] == "JOIN" && vWords[0].size() > 1)
             {
                 // :username!username@50000007.F000000B.90000002.IP JOIN :#channelname
-                strcpy(pszName, vWords[0].c_str() + 1);
+                strlcpy(pszName, vWords[0].c_str() + 1, sizeof(pszName));
                 if (strchr(pszName, '!'))
                     *strchr(pszName, '!') = '\0';
                 printf("IRC got join\n");
--- a/irc.h
+++ b/irc.h
@@ -1,11 +1,6 @@
 // Copyright (c) 2009 Satoshi Nakamoto
 // Distributed under the MIT/X11 software license, see the accompanying
 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
-
-#ifndef __WXMSW__
-#define closesocket(s) close(s)
-typedef u_int SOCKET;
-#endif
 
 extern bool RecvLine(SOCKET hSocket, string& strLine);
 extern void ThreadIRCSeed(void* parg);
--- a/main.cpp
+++ b/main.cpp
@@ -42,7 +42,6 @@
 CCriticalSection cs_mapKeys;
 CKey keyUser;
 
-string strSetDataDir;
 int nDropMessagesTest = 0;
 
 // Settings
@@ -1361,52 +1360,17 @@
     }
 }
 
-string GetAppDir()
-{
-    string strDir;
-    if (!strSetDataDir.empty())
-    {
-        strDir = strSetDataDir;
-    }
-    else if (getenv("APPDATA"))
-    {
-        strDir = strprintf("%s\\Bitcoin", getenv("APPDATA"));
-    }
-    else if (getenv("USERPROFILE"))
-    {
-        string strAppData = strprintf("%s\\Application Data", getenv("USERPROFILE"));
-        static bool fMkdirDone;
-        if (!fMkdirDone)
-        {
-            fMkdirDone = true;
-            _mkdir(strAppData.c_str());
-        }
-        strDir = strprintf("%s\\Bitcoin", strAppData.c_str());
-    }
-    else
-    {
-        return ".";
-    }
-    static bool fMkdirDone;
-    if (!fMkdirDone)
-    {
-        fMkdirDone = true;
-        _mkdir(strDir.c_str());
-    }
-    return strDir;
-}
-
 bool CheckDiskSpace(int64 nAdditionalBytes)
 {
     wxLongLong nFreeBytesAvailable = 0;
-    if (!wxGetDiskSpace(wxStandardPaths::Get().GetDataDir(), NULL, &nFreeBytesAvailable))
+    if (!wxGetDiskSpace(GetDataDir(), NULL, &nFreeBytesAvailable))
     {
         printf("ERROR: wxGetDiskSpace() failed\n");
         return true;
     }
 
     // Check for 15MB because database could create another 10MB log file at any time
-    if (nFreeBytesAvailable < (int64)15000000 + nAdditionalBytes)
+    if (nFreeBytesAvailable.GetValue() < (int64)15000000 + nAdditionalBytes)
     {
         fShutdown = true;
         wxMessageBox("Warning: Your disk space is low  ", "Bitcoin", wxICON_EXCLAMATION);
@@ -1420,7 +1384,7 @@
 {
     if (nFile == -1)
         return NULL;
-    FILE* file = fopen(strprintf("%s\\blk%04d.dat", GetAppDir().c_str(), nFile).c_str(), pszMode);
+    FILE* file = fopen(strprintf("%s\\blk%04d.dat", GetDataDir().c_str(), nFile).c_str(), pszMode);
     if (!file)
         return NULL;
     if (nBlockPos != 0 && !strchr(pszMode, 'a') && !strchr(pszMode, 'w'))
@@ -1719,7 +1683,7 @@
             if (strstr(e.what(), "CDataStream::read() : end of data"))
             {
                 // Allow exceptions from underlength message on vRecv
-                LogException(&e, "ProcessMessage()");
+                printf("ProcessMessage(%s, %d bytes) : Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand.c_str(), nMessageSize, e.what());
             }
             else
                 PrintException(&e, "ProcessMessage()");
@@ -2512,7 +2476,7 @@
 
 int64 GetBalance()
 {
-    int64 nStart = PerformanceCounter();
+    int64 nStart = GetTimeMillis();
 
     int64 nTotal = 0;
     CRITICAL_BLOCK(cs_mapWallet)
@@ -2522,11 +2486,11 @@
             CWalletTx* pcoin = &(*it).second;
             if (!pcoin->IsFinal() || pcoin->fSpent)
                 continue;
-            nTotal += pcoin->GetCredit();
+            nTotal += pcoin->GetCredit(true);
         }
     }
 
-    ///printf(" GetBalance() time = %15"PRI64d"\n", PerformanceCounter() - nStart);
+    //printf("GetBalance() %"PRI64d"ms\n", GetTimeMillis() - nStart);
     return nTotal;
 }
 
--- a/main.h
+++ b/main.h
@@ -34,7 +34,6 @@
 extern uint256 hashBestChain;
 extern CBlockIndex* pindexBest;
 extern unsigned int nTransactionsUpdated;
-extern string strSetDataDir;
 extern int nDropMessagesTest;
 
 // Settings
@@ -50,7 +49,6 @@
 
 
 
-string GetAppDir();
 bool CheckDiskSpace(int64 nAdditionalBytes=0);
 FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb");
 FILE* AppendBlockFile(unsigned int& nFileRet);
@@ -405,10 +403,10 @@
     {
         // Time based nLockTime implemented in 0.1.6,
         // do not use time based until most 0.1.5 nodes have upgraded.
+        if (nLockTime == 0)
+            return true;
         if (nBlockTime == 0)
             nBlockTime = GetAdjustedTime();
-        if (nLockTime == 0)
-            return true;
         if (nLockTime < (nLockTime < 500000000 ? nBestHeight : nBlockTime))
             return true;
         foreach(const CTxIn& txin, vin)
@@ -627,6 +625,8 @@
 
     // memory only
     mutable bool fMerkleVerified;
+    mutable bool fGetCreditCached;
+    mutable int64 nGetCreditCached;
 
 
     CMerkleTx()
@@ -644,14 +644,22 @@
         hashBlock = 0;
         nIndex = -1;
         fMerkleVerified = false;
+        fGetCreditCached = false;
+        nGetCreditCached = 0;
     }
 
-    int64 GetCredit() const
+    int64 GetCredit(bool fUseCache=false) const
     {
         // Must wait until coinbase is safely deep enough in the chain before valuing it
         if (IsCoinBase() && GetBlocksToMaturity() > 0)
             return 0;
-        return CTransaction::GetCredit();
+
+        // GetBalance can assume transactions in mapWallet won't change
+        if (fUseCache && fGetCreditCached)
+            return nGetCreditCached;
+        nGetCreditCached = CTransaction::GetCredit();
+        fGetCreditCached = true;
+        return nGetCreditCached;
     }
 
     IMPLEMENT_SERIALIZE
--- a/net.cpp
+++ b/net.cpp
@@ -21,8 +21,7 @@
 bool fClient = false;
 uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK);
 CAddress addrLocalHost(0, DEFAULT_PORT, nLocalServices);
-CNode nodeLocalHost(INVALID_SOCKET, CAddress("127.0.0.1", nLocalServices));
-CNode* pnodeLocalHost = &nodeLocalHost;
+CNode* pnodeLocalHost = NULL;
 uint64 nLocalHostNonce = 0;
 bool fShutdown = false;
 array<int, 10> vnThreadsRunning;
@@ -129,7 +128,7 @@
             strLine = wxString(strLine).Trim();
             CAddress addr(strLine.c_str());
             printf("GetMyExternalIP() received [%s] %s\n", strLine.c_str(), addr.ToString().c_str());
-            if (addr.ip == 0 || !addr.IsRoutable())
+            if (addr.ip == 0 || addr.ip == INADDR_NONE || !addr.IsRoutable())
                 return false;
             ipRet = addr.ip;
             return true;
@@ -740,10 +739,29 @@
     printf("ThreadOpenConnections started\n");
 
     // Connect to one specified address
-    while (mapArgs.count("/connect"))
+    while (mapArgs.count("-connect"))
     {
-        OpenNetworkConnection(CAddress(mapArgs["/connect"].c_str()));
-        Sleep(10000);
+        OpenNetworkConnection(CAddress(mapArgs["-connect"]));
+        for (int i = 0; i < 10; i++)
+        {
+            Sleep(1000);
+            CheckForShutdown(1);
+        }
+    }
+
+    // Connect to manually added nodes first
+    if (mapArgs.count("-addnode"))
+    {
+        foreach(string strAddr, mapMultiArgs["-addnode"])
+        {
+            CAddress addr(strAddr, NODE_NETWORK);
+            if (addr.IsValid())
+            {
+                OpenNetworkConnection(addr);
+                Sleep(1000);
+                CheckForShutdown(1);
+            }
+        }
     }
 
     // Initiate network connections
@@ -967,6 +985,8 @@
 
 bool StartNode(string& strError)
 {
+    if (pnodeLocalHost == NULL)
+        pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress("127.0.0.1", nLocalServices));
     strError = "";
 
     // Sockets startup
@@ -1031,7 +1051,7 @@
         printf("%s\n", strError.c_str());
         return false;
     }
-    printf("bound to addrLocalHost = %s\n\n", addrLocalHost.ToString().c_str());
+    printf("bound to addrLocalHost = %s\n", addrLocalHost.ToString().c_str());
 
     // Listen for incoming connections
     if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR)
--- a/net.h
+++ b/net.h
@@ -1,12 +1,6 @@
 // Copyright (c) 2009 Satoshi Nakamoto
 // Distributed under the MIT/X11 software license, see the accompanying
 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
-
-#ifndef __WXMSW__
-#define closesocket(s) close(s)
-#define INVALID_SOCKET (SOCKET)(~0)
-typedef u_int SOCKET;
-#endif
 
 class CMessageHeader;
 class CAddress;
@@ -148,61 +142,73 @@
 
     CAddress()
     {
-        nServices = 0;
+        Init();
+    }
+
+    CAddress(unsigned int ipIn, unsigned short portIn=DEFAULT_PORT, uint64 nServicesIn=NODE_NETWORK)
+    {
+        Init();
+        ip = ipIn;
+        port = portIn;
+        nServices = nServicesIn;
+    }
+
+    explicit CAddress(const struct sockaddr_in& sockaddr, uint64 nServicesIn=NODE_NETWORK)
+    {
+        Init();
+        ip = sockaddr.sin_addr.s_addr;
+        port = sockaddr.sin_port;
+        nServices = nServicesIn;
+    }
+
+    explicit CAddress(const char* pszIn, uint64 nServicesIn=NODE_NETWORK)
+    {
+        Init();
+        SetAddress(pszIn);
+        nServices = nServicesIn;
+    }
+
+    explicit CAddress(string strIn, uint64 nServicesIn=NODE_NETWORK)
+    {
+        Init();
+        SetAddress(strIn.c_str());
+        nServices = nServicesIn;
+    }
+
+    void Init()
+    {
+        nServices = NODE_NETWORK;
         memcpy(pchReserved, pchIPv4, sizeof(pchReserved));
-        ip = 0;
+        ip = INADDR_NONE;
         port = DEFAULT_PORT;
         nTime = GetAdjustedTime();
         nLastFailed = 0;
     }
 
-    CAddress(unsigned int ipIn, unsigned short portIn=DEFAULT_PORT, uint64 nServicesIn=0)
-    {
-        nServices = nServicesIn;
-        memcpy(pchReserved, pchIPv4, sizeof(pchReserved));
-        ip = ipIn;
-        port = portIn;
-        nTime = GetAdjustedTime();
-        nLastFailed = 0;
-    }
-
-    explicit CAddress(const struct sockaddr_in& sockaddr, uint64 nServicesIn=0)
+    bool SetAddress(const char* pszIn)
     {
-        nServices = nServicesIn;
-        memcpy(pchReserved, pchIPv4, sizeof(pchReserved));
-        ip = sockaddr.sin_addr.s_addr;
-        port = sockaddr.sin_port;
-        nTime = GetAdjustedTime();
-        nLastFailed = 0;
-    }
-
-    explicit CAddress(const char* pszIn, uint64 nServicesIn=0)
-    {
-        nServices = nServicesIn;
-        memcpy(pchReserved, pchIPv4, sizeof(pchReserved));
         ip = INADDR_NONE;
         port = DEFAULT_PORT;
-        nTime = GetAdjustedTime();
-        nLastFailed = 0;
-
         char psz[100];
-        if (strlen(pszIn) > ARRAYLEN(psz)-1)
-            return;
-        strcpy(psz, pszIn);
+        strlcpy(psz, pszIn, sizeof(psz));
         unsigned int a=0, b=0, c=0, d=0, e=0;
         if (sscanf(psz, "%u.%u.%u.%u:%u", &a, &b, &c, &d, &e) < 4)
-            return;
+            return false;
         char* pszPort = strchr(psz, ':');
         if (pszPort)
         {
             *pszPort++ = '\0';
             port = htons(atoi(pszPort));
-            if (atoi(pszPort) > USHRT_MAX)
+            if (atoi(pszPort) < 0 || atoi(pszPort) > USHRT_MAX)
                 port = htons(USHRT_MAX);
-            if (atoi(pszPort) < 0)
-                port = htons(0);
         }
         ip = inet_addr(psz);
+        return IsValid();
+    }
+
+    bool SetAddress(string strIn)
+    {
+        return SetAddress(strIn.c_str());
     }
 
     IMPLEMENT_SERIALIZE
@@ -274,7 +280,17 @@
 
     bool IsRoutable() const
     {
-        return !(GetByte(3) == 10 || (GetByte(3) == 192 && GetByte(2) == 168) || GetByte(3) == 127 || GetByte(3) == 0);
+        return !(GetByte(3) == 10 ||
+                 (GetByte(3) == 192 && GetByte(2) == 168) ||
+                 GetByte(3) == 127 ||
+                 GetByte(3) == 0 ||
+                 ip == 0 ||
+                 ip == INADDR_NONE);
+    }
+
+    bool IsValid() const
+    {
+        return (ip != 0 && ip != INADDR_NONE && port != htons(USHRT_MAX));
     }
 
     unsigned char GetByte(int n) const
--- a/ui.cpp
+++ b/ui.cpp
@@ -25,7 +25,6 @@
 CMainFrame* pframeMain = NULL;
 CMyTaskBarIcon* ptaskbaricon = NULL;
 map<string, string> mapAddressBook;
-map<string, string> mapArgs;
 bool fRandSendTest = false;
 void RandSend();
 extern int g_isPainting;
@@ -283,7 +282,6 @@
     fRefreshListCtrl = false;
     fRefreshListCtrlRunning = false;
     fOnSetFocusAddress = false;
-    pindexBestLast = NULL;
     m_choiceFilter->SetSelection(0);
     m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + "  ");
     m_listCtrl->SetFocus();
@@ -507,6 +505,7 @@
     string strStatus = FormatTxStatus(wtx);
     map<string, string> mapValue = wtx.mapValue;
     wtx.nLinesDisplayed = 1;
+    nListViewUpdated++;
 
     // Filter
     if (wtx.IsCoinBase())
@@ -712,48 +711,6 @@
     return true;
 }
 
-void CMainFrame::RefreshStatus()
-{
-    static int nLastTop;
-    int nTop = max((int)m_listCtrl->GetTopItem(), 0);
-    if (nTop == nLastTop && pindexBestLast == pindexBest)
-        return;
-
-    TRY_CRITICAL_BLOCK(cs_mapWallet)
-    {
-        int nStart = nTop;
-        int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
-        if (pindexBestLast == pindexBest)
-        {
-            if (nStart >= nLastTop && nStart < nLastTop + 100)
-                nStart = nLastTop + 100;
-            if (nEnd >= nLastTop && nEnd < nLastTop + 100)
-                nEnd = nLastTop;
-        }
-        nLastTop = nTop;
-        pindexBestLast = pindexBest;
-
-        for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
-        {
-            uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
-            map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
-            if (mi == mapWallet.end())
-            {
-                printf("CMainFrame::RefreshStatus() : tx not found in mapWallet\n");
-                continue;
-            }
-            CWalletTx& wtx = (*mi).second;
-            if (wtx.IsCoinBase() || wtx.GetTxTime() != wtx.nTimeDisplayed)
-            {
-                if (!InsertTransaction(wtx, false, nIndex))
-                    m_listCtrl->DeleteItem(nIndex--);
-            }
-            else
-                m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
-        }
-    }
-}
-
 void CMainFrame::RefreshListCtrl()
 {
     fRefreshListCtrl = true;
@@ -832,21 +789,104 @@
     }
 }
 
+void CMainFrame::RefreshStatusColumn()
+{
+    static int nLastTop;
+    static CBlockIndex* pindexLastBest;
+    static unsigned int nLastRefreshed;
+
+    int nTop = max((int)m_listCtrl->GetTopItem(), 0);
+    if (nTop == nLastTop && pindexLastBest == pindexBest)
+        return;
+
+    TRY_CRITICAL_BLOCK(cs_mapWallet)
+    {
+        int nStart = nTop;
+        int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
+
+        if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
+        {
+            // If no updates, only need to do the part that moved onto the screen
+            if (nStart >= nLastTop && nStart < nLastTop + 100)
+                nStart = nLastTop + 100;
+            if (nEnd >= nLastTop && nEnd < nLastTop + 100)
+                nEnd = nLastTop;
+        }
+        nLastTop = nTop;
+        pindexLastBest = pindexBest;
+        nLastRefreshed = nListViewUpdated;
+
+        for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
+        {
+            uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
+            map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
+            if (mi == mapWallet.end())
+            {
+                printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
+                continue;
+            }
+            CWalletTx& wtx = (*mi).second;
+            if (wtx.IsCoinBase() || wtx.GetTxTime() != wtx.nTimeDisplayed)
+            {
+                if (!InsertTransaction(wtx, false, nIndex))
+                    m_listCtrl->DeleteItem(nIndex--);
+            }
+            else
+                m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
+        }
+    }
+}
+
 void CMainFrame::OnPaint(wxPaintEvent& event)
 {
     event.Skip();
 }
 
-void DelayedRepaint(void* parg)
+
+unsigned int nNeedRepaint = 0;
+unsigned int nLastRepaint = 0;
+int64 nLastRepaintTime = 0;
+int64 nRepaintInterval = 500;
+
+void ThreadDelayedRepaint(void* parg)
 {
-    static bool fOneThread;
-    if (fOneThread)
-        return;
-    fOneThread = true;
-    Sleep(1000);
-    printf("DelayedRepaint()\n");
-    MainFrameRepaint();
-    fOneThread = false;
+    while (!fShutdown)
+    {
+        if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
+        {
+            nLastRepaint = nNeedRepaint;
+            if (pframeMain)
+            {
+                printf("DelayedRepaint\n");
+                wxPaintEvent event;
+                pframeMain->Refresh();
+                pframeMain->AddPendingEvent(event);
+            }
+        }
+        Sleep(nRepaintInterval);
+    }
+}
+
+void MainFrameRepaint()
+{
+    // This is called by network code that shouldn't access pframeMain
+    // directly because it could still be running after the UI is closed.
+    if (pframeMain)
+    {
+        // Don't repaint too often
+        static int64 nLastRepaintRequest;
+        if (GetTimeMillis() - nLastRepaintRequest < 100)
+        {
+            nNeedRepaint++;
+            return;
+        }
+        nLastRepaintRequest = GetTimeMillis();
+
+        printf("MainFrameRepaint\n");
+        wxPaintEvent event;
+        pframeMain->Refresh();
+        pframeMain->AddPendingEvent(event);
+    }
 }
 
 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
@@ -854,43 +894,54 @@
     if (ptaskbaricon)
         ptaskbaricon->UpdateTooltip();
 
-    // Update listctrl contents
-    if (!vWalletUpdated.empty())
+    //
+    // Slower stuff
+    //
+    static int nTransactionCount;
+    bool fPaintedBalance = false;
+    if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
     {
+        nLastRepaint = nNeedRepaint;
+        nLastRepaintTime = GetTimeMillis();
+
+        // Update listctrl contents
+        if (!vWalletUpdated.empty())
+        {
+            TRY_CRITICAL_BLOCK(cs_mapWallet)
+            {
+                bool fInserted = false;
+                foreach(uint256 hash, vWalletUpdated)
+                {
+                    map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
+                    if (mi != mapWallet.end())
+                        fInserted |= InsertTransaction((*mi).second, false);
+                }
+                vWalletUpdated.clear();
+                if (fInserted)
+                    m_listCtrl->ScrollList(0, INT_MAX);
+            }
+        }
+
+        // Balance total
         TRY_CRITICAL_BLOCK(cs_mapWallet)
         {
-            bool fInserted = false;
-            foreach(uint256 hash, vWalletUpdated)
+            fPaintedBalance = true;
+            m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + "  ");
+
+            // Count hidden and multi-line transactions
+            nTransactionCount = 0;
+            for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
             {
-                map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
-                if (mi != mapWallet.end())
-                    fInserted |= InsertTransaction((*mi).second, false);
+                CWalletTx& wtx = (*it).second;
+                nTransactionCount += wtx.nLinesDisplayed;
             }
-            vWalletUpdated.clear();
-            if (fInserted)
-                m_listCtrl->ScrollList(0, INT_MAX);
         }
     }
+    if (!vWalletUpdated.empty() || !fPaintedBalance)
+        nNeedRepaint++;
 
     // Update status column of visible items only
-    RefreshStatus();
-
-    // Balance total
-    bool fRefreshed = false;
-    static int nTransactionCount;
-    TRY_CRITICAL_BLOCK(cs_mapWallet)
-    {
-        fRefreshed = true;
-        m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + "  ");
-
-        // Count hidden and multi-line transactions
-        nTransactionCount = 0;
-        for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
-        {
-            CWalletTx& wtx = (*it).second;
-            nTransactionCount += wtx.nLinesDisplayed;
-        }
-    }
+    RefreshStatusColumn();
 
     // Update status bar
     string strGen = "";
@@ -903,13 +954,10 @@
     string strStatus = strprintf("     %d connections     %d blocks     %d transactions", vNodes.size(), nBestHeight + 1, nTransactionCount);
     m_statusBar->SetStatusText(strStatus, 2);
 
-    // mapWallet was locked, try again later
-    if (!vWalletUpdated.empty() || !fRefreshed)
-        _beginthread(DelayedRepaint, 0, NULL);
-
     m_listCtrl->OnPaint(event);
 }
 
+
 void CrossThreadCall(wxCommandEvent& event)
 {
     if (pframeMain)
@@ -994,13 +1042,6 @@
 
 void CMainFrame::OnButtonSend(wxCommandEvent& event)
 {
-    /// debug test
-    if (fRandSendTest)
-    {
-        RandSend();
-        return;
-    }
-
     // Toolbar: Send
     CSendDialog dialog(this);
     dialog.ShowModal();
@@ -1684,8 +1725,8 @@
     else
     {
         // Parse IP address
-        CAddress addr(strAddress.c_str());
-        if (addr.ip == 0)
+        CAddress addr(strAddress);
+        if (!addr.IsValid())
         {
             wxMessageBox("Invalid address  ", "Send Coins");
             return;
@@ -1818,14 +1859,6 @@
         wxMessageBox("Transfer cancelled  ", "Sending...", wxOK, this);
     }
     event.Skip();
-
-    /// debug test
-    if (fRandSendTest && fWorkDone && fSuccess)
-    {
-        Close();
-        Sleep(1000);
-        RandSend();
-    }
 }
 
 
@@ -3305,27 +3338,6 @@
     return false;
 }
 
-map<string, string> ParseParameters(int argc, char* argv[])
-{
-    map<string, string> mapArgs;
-    for (int i = 0; i < argc; i++)
-    {
-        char psz[10000];
-        strcpy(psz, argv[i]);
-        char* pszValue = "";
-        if (strchr(psz, '='))
-        {
-            pszValue = strchr(psz, '=');
-            *pszValue++ = '\0';
-        }
-        strlwr(psz);
-        if (psz[0] == '-')
-            psz[0] = '/';
-        mapArgs[psz] = pszValue;
-    }
-    return mapArgs;
-}
-
 bool CMyApp::OnInit2()
 {
 #ifdef _MSC_VER
@@ -3337,10 +3349,27 @@
     // Disable malfunctioning wxWidgets debug assertion
     g_isPainting = 10000;
 #endif
-
-    //// debug print
-    printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
-    printf("Bitcoin version %d, Windows version %08x\n", VERSION, GetVersion());
+    wxImage::AddHandler(new wxPNGHandler);
+    SetAppName("Bitcoin");
+
+    ParseParameters(argc, argv);
+    if (mapArgs.count("-?") || mapArgs.count("--help"))
+    {
+        string strUsage =
+            "Usage: bitcoin [options]\t\t\t\t\t\t\n"
+            "Options:\n"
+            "  -gen\t\t  Generate coins\n"
+            "  -gen=0\t\t  Don't generate coins\n"
+            "  -min\t\t  Start minimized\n"
+            "  -datadir=<dir>\t  Specify data directory\n"
+            "  -proxy=<ip:port>\t  Connect through socks4 proxy,\n"
+            "  \t\t     e.g. -proxy=127.0.0.1:9050 to use TOR\n"
+            "  -addnode=<ip>\t  Add a node to connect to\n"
+            "  -connect=<ip>\t  Connect only to the specified node\n"
+            "  -?\t\t  This help message\n";
+        wxMessageBox(strUsage, "Bitcoin", wxOK);
+        exit(0);
+    }
 
     //
     // Limit to single instance per user
@@ -3382,31 +3411,31 @@
     //
     // Parameters
     //
-    wxImage::AddHandler(new wxPNGHandler);
-    mapArgs = ParseParameters(argc, argv);
-
-    if (mapArgs.count("/datadir"))
-        strSetDataDir = mapArgs["/datadir"];
-
-    if (mapArgs.count("/debug"))
+    if (mapArgs.count("-datadir"))
+        strlcpy(pszSetDataDir, mapArgs["-datadir"].c_str(), sizeof(pszSetDataDir));
+
+    if (mapArgs.count("-debug"))
         fDebug = true;
 
-    if (mapArgs.count("/printtodebugger"))
+    if (mapArgs.count("-printtodebugger"))
         fPrintToDebugger = true;
 
-    if (mapArgs.count("/dropmessages"))
+    printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
+    printf("Bitcoin version %d, Windows version %08x\n", VERSION, GetVersion());
+
+    if (mapArgs.count("-dropmessages"))
     {
-        nDropMessagesTest = atoi(mapArgs["/dropmessages"]);
+        nDropMessagesTest = atoi(mapArgs["-dropmessages"]);
         if (nDropMessagesTest == 0)
             nDropMessagesTest = 20;
     }
 
-    if (mapArgs.count("/loadblockindextest"))
+    if (mapArgs.count("-loadblockindextest"))
     {
         CTxDB txdb("r");
         txdb.LoadBlockIndex();
         PrintBlockTree();
-        ExitProcess(0);
+        exit(0);
     }
 
     //
@@ -3417,22 +3446,22 @@
     int64 nStart;
 
     printf("Loading addresses...\n");
-    nStart = PerformanceCounter();
+    nStart = GetTimeMillis();
     if (!LoadAddresses())
         strErrors += "Error loading addr.dat      \n";
-    printf(" addresses   %15"PRI64d"\n", PerformanceCounter() - nStart);
+    printf(" addresses   %15"PRI64d"ms\n", GetTimeMillis() - nStart);
 
     printf("Loading block index...\n");
-    nStart = PerformanceCounter();
+    nStart = GetTimeMillis();
     if (!LoadBlockIndex())
         strErrors += "Error loading blkindex.dat      \n";
-    printf(" block index %15"PRI64d"\n", PerformanceCounter() - nStart);
+    printf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart);
 
     printf("Loading wallet...\n");
-    nStart = PerformanceCounter();
+    nStart = GetTimeMillis();
     if (!LoadWallet(fFirstRun))
         strErrors += "Error loading wallet.dat      \n";
-    printf(" wallet      %15"PRI64d"\n", PerformanceCounter() - nStart);
+    printf(" wallet      %15"PRI64d"ms\n", GetTimeMillis() - nStart);
 
     printf("Done loading\n");
 
@@ -3457,45 +3486,59 @@
     //
     // Parameters
     //
-    if (mapArgs.count("/printblockindex") || mapArgs.count("/printblocktree"))
+    if (mapArgs.count("-printblockindex") || mapArgs.count("-printblocktree"))
     {
         PrintBlockTree();
         OnExit();
         return false;
     }
 
-    if (mapArgs.count("/proxy"))
+    if (mapArgs.count("-gen"))
+    {
+        if (mapArgs["-gen"].empty())
+            fGenerateBitcoins = true;
+        else
+            fGenerateBitcoins = atoi(mapArgs["-gen"].c_str());
+    }
+
+    if (mapArgs.count("-proxy"))
     {
         fUseProxy = true;
-        addrProxy = CAddress(mapArgs["/proxy"].c_str());
-        if (addrProxy.ip == INADDR_NONE)
+        addrProxy = CAddress(mapArgs["-proxy"]);
+        if (!addrProxy.IsValid())
         {
-            wxMessageBox("Invalid /proxy address", "Bitcoin");
+            wxMessageBox("Invalid -proxy address", "Bitcoin");
             OnExit();
+            return false;
         }
         CWalletDB walletdb;
         walletdb.WriteSetting("fUseProxy", fUseProxy);
         walletdb.WriteSetting("addrProxy", addrProxy);
     }
 
-    if (mapArgs.count("/gen"))
+    if (mapArgs.count("-addnode"))
     {
-        if (mapArgs["/gen"].empty())
-            fGenerateBitcoins = true;
-        else
-            fGenerateBitcoins = atoi(mapArgs["/gen"].c_str());
+        CAddrDB addrdb;
+        foreach(string strAddr, mapMultiArgs["-addnode"])
+        {
+            CAddress addr(strAddr, NODE_NETWORK);
+            if (addr.IsValid())
+                AddAddress(addrdb, addr);
+        }
     }
 
     //
     // Create the main frame window
     //
     pframeMain = new CMainFrame(NULL);
-    if (mapArgs.count("/min"))
+    if (mapArgs.count("-min"))
         pframeMain->Iconize(true);
     pframeMain->Show(true);  // have to show first to get taskbar button to hide
     pframeMain->Show(!fMinimizeToTray || !pframeMain->IsIconized());
     ptaskbaricon->Show(fMinimizeToTray);
 
+    _beginthread(ThreadDelayedRepaint, 0, NULL);
+
     if (!CheckDiskSpace())
     {
         OnExit();
@@ -3516,7 +3559,7 @@
     //
     // Tests
     //
-    if (argc >= 2 && stricmp(argv[1], "/send") == 0)
+    if (argc >= 2 && stricmp(argv[1], "-send") == 0)
     {
         int64 nValue = 1;
         if (argc >= 3)
@@ -3525,7 +3568,7 @@
         string strAddress;
         if (argc >= 4)
             strAddress = argv[3];
-        CAddress addr(strAddress.c_str());
+        CAddress addr(strAddress);
 
         CWalletTx wtx;
         wtx.mapValue["to"] = strAddress;
@@ -3538,15 +3581,6 @@
             return false;
     }
 
-    if (mapArgs.count("/randsendtest"))
-    {
-        if (!mapArgs["/randsendtest"].empty())
-            _beginthread(ThreadRandSendTest, 0, new string(mapArgs["/randsendtest"]));
-        else
-            fRandSendTest = true;
-        fDebug = true;
-    }
-
     return true;
 }
 
@@ -3610,19 +3644,6 @@
 
 
 
-void MainFrameRepaint()
-{
-    // This is called by network code that shouldn't access pframeMain
-    // directly because it could still be running after the UI is closed.
-    if (pframeMain)
-    {
-        printf("MainFrameRepaint()\n");
-        wxPaintEvent event;
-        pframeMain->Refresh();
-        pframeMain->AddPendingEvent(event);
-    }
-}
-
 
 
 typedef WINSHELLAPI BOOL WINAPI (*PSHGETSPECIALFOLDERPATHA)(HWND hwndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate);
@@ -3666,7 +3687,7 @@
 
 bool GetStartOnSystemStartup()
 {
-    return FileExists(StartupShortcutPath().c_str());
+    return wxFileExists(StartupShortcutPath());
 }
 
 void SetStartOnSystemStartup(bool fAutoStart)
@@ -3727,79 +3748,3 @@
 
 
 
-
-// randsendtest to bitcoin address
-void ThreadRandSendTest(void* parg)
-{
-    string strAddress = *(string*)parg;
-    uint160 hash160;
-    if (!AddressToHash160(strAddress, hash160))
-    {
-        wxMessageBox(strprintf("ThreadRandSendTest: Bitcoin address '%s' not valid  ", strAddress.c_str()));
-        return;
-    }
-
-    while (!fShutdown)
-    {
-        Sleep(GetRand(30) * 1000 + 100);
-
-        // Message
-        CWalletTx wtx;
-        wtx.mapValue["to"] = strAddress;
-        wtx.mapValue["from"] = addrLocalHost.ToString();
-        static int nRep;
-        wtx.mapValue["message"] = strprintf("randsendtest %d\n", ++nRep);
-
-        // Value
-        int64 nValue = (GetRand(9) + 1) * 100 * CENT;
-        if (GetBalance() < nValue)
-        {
-            wxMessageBox("Out of money  ");
-            while (GetBalance() < 1000)
-                Sleep(1000);
-        }
-        nValue += (nRep % 100) * CENT;
-
-        // Send to bitcoin address
-        CScript scriptPubKey;
-        scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
-
-        if (fShutdown)
-            return;
-        if (!SendMoney(scriptPubKey, nValue, wtx))
-            return;
-    }
-}
-
-
-// randsendtest to any connected node
-void RandSend()
-{
-    while (vNodes.empty())
-        Sleep(1000);
-    CAddress addr;
-    CRITICAL_BLOCK(cs_vNodes)
-        addr = vNodes[GetRand(vNodes.size())]->addr;
-
-    // Message
-    CWalletTx wtx;
-    wtx.mapValue["to"] = addr.ToString();
-    wtx.mapValue["from"] = addrLocalHost.ToString();
-    static int nRep;
-    wtx.mapValue["message"] = strprintf("randsendtest %d\n", ++nRep);
-
-    // Value
-    int64 nValue = (GetRand(999) + 1) * CENT;
-    if (GetBalance() < nValue)
-    {
-        wxMessageBox("Out of money  ");
-        return;
-    }
-
-    // Send to IP address
-    if (fShutdown)
-        return;
-    CSendingDialog* pdialog = new CSendingDialog(pframeMain, addr, nValue, wtx);
-    if (!pdialog->Show())
-        wxMessageBox("ShowModal Failed  ");
-}
--- a/ui.h
+++ b/ui.h
@@ -83,15 +83,14 @@
     bool fRefreshListCtrl;
     bool fRefreshListCtrlRunning;
     bool fOnSetFocusAddress;
-    CBlockIndex* pindexBestLast;
-    set<uint256> setUnmaturedDisplayed;
+    unsigned int nListViewUpdated;
 
     void OnCrossThreadCall(wxCommandEvent& event);
     void InsertLine(bool fNew, int nIndex, uint256 hashKey, string strSort, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4, const wxString& str5);
     bool DeleteLine(uint256 hashKey);
     bool InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex=-1);
     void RefreshListCtrl();
-    void RefreshStatus();
+    void RefreshStatusColumn();
 };
 
 
--- a/util.cpp
+++ b/util.cpp
@@ -5,9 +5,13 @@
 #include "headers.h"
 
 
+map<string, string> mapArgs;
+map<string, vector<string> > mapMultiArgs;
 bool fDebug = false;
 bool fPrintToDebugger = false;
 bool fPrintToConsole = false;
+char pszSetDataDir[MAX_PATH] = "";
+
 
 
 
@@ -68,6 +72,8 @@
 
 void RandAddSeedPerfmon()
 {
+#ifdef __WXMSW__
+    // Don't need this on Linux, OpenSSL automatically uses /dev/urandom
     // This can take up to 2 seconds, so only do it every 10 minutes
     static int64 nLastPerfmon;
     if (GetTime() < nLastPerfmon + 10 * 60)
@@ -95,6 +101,7 @@
         strftime(pszTime, sizeof(pszTime), "%x %H:%M:%S", ptmTime);
         printf("%s RandAddSeed() %d bytes\n", pszTime, nSize);
     }
+#endif
 }
 
 
@@ -304,6 +311,32 @@
 }
 
 
+void ParseParameters(int argc, char* argv[])
+{
+    mapArgs.clear();
+    mapMultiArgs.clear();
+    for (int i = 0; i < argc; i++)
+    {
+        char psz[10000];
+        strlcpy(psz, argv[i], sizeof(psz));
+        char* pszValue = "";
+        if (strchr(psz, '='))
+        {
+            pszValue = strchr(psz, '=');
+            *pszValue++ = '\0';
+        }
+        strlwr(psz);
+        #ifdef __WXMSW__
+        if (psz[0] == '/')
+            psz[0] = '-';
+        #endif
+        mapArgs[psz] = pszValue;
+        mapMultiArgs[psz].push_back(pszValue);
+    }
+}
+
+
+
 
 
 
@@ -346,15 +379,6 @@
 
 
 
-bool FileExists(const char* psz)
-{
-#ifdef WIN32
-    return GetFileAttributes(psz) != -1;
-#else
-    return access(psz, 0) != -1;
-#endif
-}
-
 int GetFilesize(FILE* file)
 {
     int nSavePos = ftell(file);
@@ -365,6 +389,46 @@
     return nFilesize;
 }
 
+void GetDataDir(char* pszDir)
+{
+    // pszDir must be at least MAX_PATH length.
+    if (pszSetDataDir[0] != 0)
+    {
+        strlcpy(pszDir, pszSetDataDir, MAX_PATH);
+        static bool fMkdirDone;
+        if (!fMkdirDone)
+        {
+            fMkdirDone = true;
+            _mkdir(pszDir);
+        }
+    }
+    else
+    {
+        // This can be called during exceptions by printf, so we cache the
+        // value so we don't have to do memory allocations after that.
+        // wxStandardPaths::GetUserDataDir
+        //  Return the directory for the user-dependent application data files:
+        //  Unix: ~/.appname
+        //  Windows: C:\Documents and Settings\username\Application Data\appname
+        //  Mac: ~/Library/Application Support/appname
+        static char pszCachedDir[MAX_PATH];
+        if (pszCachedDir[0] == 0)
+        {
+            strlcpy(pszCachedDir, wxStandardPaths::Get().GetUserDataDir().c_str(), sizeof(pszCachedDir));
+            _mkdir(pszCachedDir);
+        }
+        strlcpy(pszDir, pszCachedDir, MAX_PATH);
+    }
+
+}
+
+string GetDataDir()
+{
+    char pszDir[MAX_PATH];
+    GetDataDir(pszDir);
+    return pszDir;
+}
+
 
 
 
--- a/util.h
+++ b/util.h
@@ -54,16 +54,23 @@
     return (T&)val;
 }
 
+#ifndef __WXMSW__
+#define closesocket(s)  close(s)
+#define INVALID_SOCKET  (SOCKET)(~0)
+typedef u_int SOCKET;
+#endif
 
 
 
 
 
 
+extern map<string, string> mapArgs;
+extern map<string, vector<string> > mapMultiArgs;
 extern bool fDebug;
 extern bool fPrintToDebugger;
 extern bool fPrintToConsole;
-extern map<string, string> mapArgs;
+extern char pszSetDataDir[MAX_PATH];
 
 void RandAddSeed();
 void RandAddSeedPerfmon();
@@ -77,8 +84,10 @@
 bool ParseMoney(const char* pszIn, int64& nRet);
 vector<unsigned char> ParseHex(const char* psz);
 vector<unsigned char> ParseHex(const std::string& str);
-bool FileExists(const char* psz);
+void ParseParameters(int argc, char* argv[]);
 int GetFilesize(FILE* file);
+void GetDataDir(char* pszDirRet);
+string GetDataDir();
 uint64 GetRand(uint64 nMax);
 int64 GetTime();
 int64 GetAdjustedTime();
@@ -172,9 +181,14 @@
     if (!fPrintToConsole)
     {
         // print to debug.log
-        FILE* fileout = fopen("debug.log", "a");
+        char pszFile[MAX_PATH+100];
+        GetDataDir(pszFile);
+        strlcat(pszFile, "\\debug.log", sizeof(pszFile));
+        FILE* fileout = fopen(pszFile, "a");
         if (fileout)
         {
+            //// Debug print useful for profiling
+            //fprintf(fileout, " %"PRI64d" ", wxGetLocalTimeMillis().GetValue());
             va_list arg_ptr;
             va_start(arg_ptr, pszFormat);
             ret = vfprintf(fileout, pszFormat, arg_ptr);
@@ -321,22 +335,25 @@
 {
     printf(pszFormat, HexStr(vch, fSpaces).c_str());
 }
-
 
 inline int64 PerformanceCounter()
 {
-    int64 nCounter = 0;
+    int64 nCounter = 0;
 #ifdef __WXMSW__
-    QueryPerformanceCounter((LARGE_INTEGER*)&nCounter);
+    QueryPerformanceCounter((LARGE_INTEGER*)&nCounter);
 #else
-	// this could be changed to reading /dev/urandom
-	timeval t;
-	gettimeofday(&t, NULL);
-	nCounter += t.tv_sec * 1000000 + t.tv_usec;
+    timeval t;
+    gettimeofday(&t, NULL);
+    nCounter = t.tv_sec * 1000000 + t.tv_usec;
 #endif
     return nCounter;
 }
 
+inline int64 GetTimeMillis()
+{
+    return wxGetLocalTimeMillis().GetValue();
+}
+
 #ifndef __WXMSW__
 inline void Sleep(unsigned int nMilliseconds)
 {
@@ -354,8 +371,10 @@
 
 inline void heapchk()
 {
+#ifdef __WXMSW__
     if (_heapchk() != _HEAPOK)
         DebugBreak();
+#endif
 }
 
 // Randomize the stack to help protect against buffer overrun exploits