changeset 113:987d4e9225f9 draft

bugfix Db::open/close and zombie sockets bugs fix double-close of socket handle, keep databases open, close db cursors, initial block download in batches of 500 blocks, fix misc warnings, subver linux-test8
author s_nakamoto <s_nakamoto@1a98c847-1fd6-4fd8-948a-caf3550aa51b>
date Wed, 18 Nov 2009 19:19:41 +0000
parents 445b269c9f1f
children 837ab126734d
files build-unix.txt db.cpp db.h irc.cpp main.cpp main.h makefile.unix net.cpp net.h serialize.h ui.cpp util.cpp util.h xpm/addressbook16.xpm xpm/addressbook20.xpm xpm/bitcoin16.xpm xpm/bitcoin20.xpm xpm/bitcoin32.xpm xpm/bitcoin48.xpm xpm/check.xpm xpm/send16.xpm xpm/send16noshadow.xpm xpm/send20.xpm
diffstat 23 files changed, 305 insertions(+), 185 deletions(-) [+]
line wrap: on
line diff
--- a/build-unix.txt
+++ b/build-unix.txt
@@ -13,18 +13,18 @@
 
 Dependencies
 ------------
-Install the dev files for the shared libraries:
 apt-get install build-essential
 apt-get install libgtk2.0-dev
 apt-get install libssl-dev
+apt-get install libdb4.7-dev
+apt-get install libdb4.7++-dev
+apt-get install libboost-dev
 
 Libraries you need to obtain separately and build:
               default path   download
 wxWidgets      \wxwidgets     http://www.wxwidgets.org/downloads/
-Berkeley DB    \db            http://www.oracle.com/technology/software/products/berkeley-db/index.html
-Boost          \boost         http://www.boost.org/users/download/
 
-Their licenses:
+Licenses:
 wxWidgets      LGPL 2.1 with very liberal exceptions
 Berkeley DB    New BSD license with additional requirement that linked software must be free open source
 Boost          MIT-like license
@@ -59,15 +59,9 @@
 ldconfig
 
 
-Berkeley DB
------------
-cd /usr/local/db-4.7.25.NC/build_unix
-../dist/configure --enable-cxx
-make
-
-
 Boost
 -----
+If you download and build Boost yourself
 cd /usr/local/boost_1_40_0
 su
 ./bootstrap.sh
--- a/db.cpp
+++ b/db.cpp
@@ -20,6 +20,7 @@
 static bool fDbEnvInit = false;
 DbEnv dbenv(0);
 static map<string, int> mapFileUseCount;
+static map<string, Db*> mapDb;
 
 class CDBInit
 {
@@ -39,21 +40,17 @@
 instance_of_cdbinit;
 
 
-CDB::CDB(const char* pszFile, const char* pszMode, bool fTxn) : pdb(NULL)
+CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL)
 {
     int ret;
     if (pszFile == NULL)
         return;
 
+    fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
     bool fCreate = strchr(pszMode, 'c');
-    bool fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
     unsigned int nFlags = DB_THREAD;
     if (fCreate)
         nFlags |= DB_CREATE;
-    else if (fReadOnly)
-        nFlags |= DB_RDONLY;
-    if (!fReadOnly || fTxn)
-        nFlags |= DB_AUTO_COMMIT;
 
     CRITICAL_BLOCK(cs_db)
     {
@@ -72,7 +69,7 @@
             dbenv.set_lk_max_locks(10000);
             dbenv.set_lk_max_objects(10000);
             dbenv.set_errfile(fopen(strErrorFile.c_str(), "a")); /// debug
-            ///dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1); /// causes corruption
+            dbenv.set_flags(DB_AUTO_COMMIT, 1);
             ret = dbenv.open(strDataDir.c_str(),
                              DB_CREATE     |
                              DB_INIT_LOCK  |
@@ -90,31 +87,39 @@
 
         strFile = pszFile;
         ++mapFileUseCount[strFile];
-    }
-
-    pdb = new Db(&dbenv, 0);
+        pdb = mapDb[strFile];
+        if (pdb == NULL)
+        {
+            pdb = new Db(&dbenv, 0);
 
-    ret = pdb->open(NULL,      // Txn pointer
-                    pszFile,   // Filename
-                    "main",    // Logical db name
-                    DB_BTREE,  // Database type
-                    nFlags,    // Flags
-                    0);
+            ret = pdb->open(NULL,      // Txn pointer
+                            pszFile,   // Filename
+                            "main",    // Logical db name
+                            DB_BTREE,  // Database type
+                            nFlags,    // Flags
+                            0);
 
-    if (ret > 0)
-    {
-        delete pdb;
-        pdb = NULL;
-        CRITICAL_BLOCK(cs_db)
-            --mapFileUseCount[strFile];
-        strFile = "";
-        throw runtime_error(strprintf("CDB() : can't open database file %s, error %d\n", pszFile, ret));
+            if (ret > 0)
+            {
+                delete pdb;
+                pdb = NULL;
+                CRITICAL_BLOCK(cs_db)
+                    --mapFileUseCount[strFile];
+                strFile = "";
+                throw runtime_error(strprintf("CDB() : can't open database file %s, error %d\n", pszFile, ret));
+            }
+
+            if (fCreate && !Exists(string("version")))
+            {
+                bool fTmp = fReadOnly;
+                fReadOnly = false;
+                WriteVersion(VERSION);
+                fReadOnly = fTmp;
+            }
+
+            mapDb[strFile] = pdb;
+        }
     }
-
-    if (fCreate && !Exists(string("version")))
-        WriteVersion(VERSION);
-
-    RandAddSeed();
 }
 
 void CDB::Close()
@@ -124,8 +129,6 @@
     if (!vTxn.empty())
         vTxn.front()->abort();
     vTxn.clear();
-    pdb->close(0);
-    delete pdb;
     pdb = NULL;
     dbenv.txn_checkpoint(0, 0, 0);
 
@@ -135,6 +138,21 @@
     RandAddSeed();
 }
 
+void CloseDb(const string& strFile)
+{
+    CRITICAL_BLOCK(cs_db)
+    {
+        if (mapDb[strFile] != NULL)
+        {
+            // Close the database handle
+            Db* pdb = mapDb[strFile];
+            pdb->close(0);
+            delete pdb;
+            mapDb[strFile] = NULL;
+        }
+    }
+}
+
 void DBFlush(bool fShutdown)
 {
     // Flush log data to the actual data file
@@ -144,14 +162,18 @@
         return;
     CRITICAL_BLOCK(cs_db)
     {
-        dbenv.txn_checkpoint(0, 0, 0);
         map<string, int>::iterator mi = mapFileUseCount.begin();
         while (mi != mapFileUseCount.end())
         {
             string strFile = (*mi).first;
             int nRefCount = (*mi).second;
+            printf("%s refcount=%d\n", strFile.c_str(), nRefCount);
             if (nRefCount == 0)
             {
+                // Move log data to the dat file
+                CloseDb(strFile);
+                dbenv.txn_checkpoint(0, 0, 0);
+                printf("%s flush\n", strFile.c_str());
                 dbenv.lsn_reset(strFile.c_str(), 0);
                 mapFileUseCount.erase(mi++);
             }
@@ -238,7 +260,10 @@
         if (ret == DB_NOTFOUND)
             break;
         else if (ret != 0)
+        {
+            pcursor->close();
             return false;
+        }
 
         // Unserialize
         string strType;
@@ -255,9 +280,14 @@
         {
             vtx.resize(vtx.size()+1);
             if (!vtx.back().ReadFromDisk(pos))
+            {
+                pcursor->close();
                 return false;
+            }
         }
     }
+
+    pcursor->close();
     return true;
 }
 
@@ -379,6 +409,7 @@
             break;
         }
     }
+    pcursor->close();
 
     if (!ReadHashBestChain(hashBestChain))
     {
@@ -391,7 +422,7 @@
         return error("CTxDB::LoadBlockIndex() : blockindex for hashBestChain not found");
     pindexBest = mapBlockIndex[hashBestChain];
     nBestHeight = pindexBest->nHeight;
-    printf("LoadBlockIndex(): hashBestChain=%s  height=%d\n", hashBestChain.ToString().substr(0,14).c_str(), nBestHeight);
+    printf("LoadBlockIndex(): hashBestChain=%s  height=%d\n", hashBestChain.ToString().substr(0,16).c_str(), nBestHeight);
 
     return true;
 }
@@ -456,6 +487,7 @@
                 mapAddresses.insert(make_pair(addr.GetKey(), addr));
             }
         }
+        pcursor->close();
 
         printf("Loaded %d addresses\n", mapAddresses.size());
 
@@ -558,7 +590,7 @@
                 //printf(" %12I64d  %s  %s  %s\n",
                 //    wtx.vout[0].nValue,
                 //    DateTimeStrFormat("%x %H:%M:%S", wtx.nTime).c_str(),
-                //    wtx.hashBlock.ToString().substr(0,14).c_str(),
+                //    wtx.hashBlock.ToString().substr(0,16).c_str(),
                 //    wtx.mapValue["message"].c_str());
             }
             else if (strType == "key")
@@ -596,6 +628,7 @@
 
             }
         }
+        pcursor->close();
     }
 
     printf("fShowGenerated = %d\n", fShowGenerated);
@@ -655,6 +688,8 @@
     if (fOneThread)
         return;
     fOneThread = true;
+    if (mapArgs.count("-noflushwallet"))
+        return;
 
     unsigned int nLastSeen = nWalletDBUpdated;
     unsigned int nLastFlushed = nWalletDBUpdated;
@@ -669,24 +704,37 @@
             nLastWalletUpdate = GetTime();
         }
 
-        if (nLastFlushed != nWalletDBUpdated && nLastWalletUpdate < GetTime() - 1)
+        if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)
         {
             TRY_CRITICAL_BLOCK(cs_db)
             {
-                string strFile = "wallet.dat";
-                map<string, int>::iterator mi = mapFileUseCount.find(strFile);
-                if (mi != mapFileUseCount.end())
+                // Don't do this if any databases are in use
+                int nRefCount = 0;
+                map<string, int>::iterator mi = mapFileUseCount.begin();
+                while (mi != mapFileUseCount.end())
                 {
-                    int nRefCount = (*mi).second;
-                    if (nRefCount == 0 && !fShutdown)
+                    nRefCount += (*mi).second;
+                    mi++;
+                }
+
+                if (nRefCount == 0 && !fShutdown)
+                {
+                    string strFile = "wallet.dat";
+                    map<string, int>::iterator mi = mapFileUseCount.find(strFile);
+                    if (mi != mapFileUseCount.end())
                     {
-                        // Flush wallet.dat so it's self contained
+                        printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());
+                        printf("Flushing wallet.dat\n");
                         nLastFlushed = nWalletDBUpdated;
                         int64 nStart = GetTimeMillis();
+
+                        // Flush wallet.dat so it's self contained
+                        CloseDb(strFile);
                         dbenv.txn_checkpoint(0, 0, 0);
                         dbenv.lsn_reset(strFile.c_str(), 0);
+
+                        mapFileUseCount.erase(mi++);
                         printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);
-                        mapFileUseCount.erase(mi++);
                     }
                 }
             }
--- a/db.h
+++ b/db.h
@@ -32,8 +32,9 @@
     Db* pdb;
     string strFile;
     vector<DbTxn*> vTxn;
+    bool fReadOnly;
 
-    explicit CDB(const char* pszFile, const char* pszMode="r+", bool fTxn=false);
+    explicit CDB(const char* pszFile, const char* pszMode="r+");
     ~CDB() { Close(); }
 public:
     void Close();
@@ -77,6 +78,8 @@
     {
         if (!pdb)
             return false;
+        if (fReadOnly)
+            assert(("Write called on database in read-only mode", false));
 
         // Key
         CDataStream ssKey(SER_DISK);
@@ -104,6 +107,8 @@
     {
         if (!pdb)
             return false;
+        if (fReadOnly)
+            assert(("Erase called on database in read-only mode", false));
 
         // Key
         CDataStream ssKey(SER_DISK);
@@ -254,7 +259,7 @@
 class CTxDB : public CDB
 {
 public:
-    CTxDB(const char* pszMode="r+", bool fTxn=false) : CDB(!fClient ? "blkindex.dat" : NULL, pszMode, fTxn) { }
+    CTxDB(const char* pszMode="r+") : CDB(!fClient ? "blkindex.dat" : NULL, pszMode) { }
 private:
     CTxDB(const CTxDB&);
     void operator=(const CTxDB&);
@@ -283,7 +288,7 @@
 class CReviewDB : public CDB
 {
 public:
-    CReviewDB(const char* pszMode="r+", bool fTxn=false) : CDB("reviews.dat", pszMode, fTxn) { }
+    CReviewDB(const char* pszMode="r+") : CDB("reviews.dat", pszMode) { }
 private:
     CReviewDB(const CReviewDB&);
     void operator=(const CReviewDB&);
@@ -309,7 +314,7 @@
 class CMarketDB : public CDB
 {
 public:
-    CMarketDB(const char* pszMode="r+", bool fTxn=false) : CDB("market.dat", pszMode, fTxn) { }
+    CMarketDB(const char* pszMode="r+") : CDB("market.dat", pszMode) { }
 private:
     CMarketDB(const CMarketDB&);
     void operator=(const CMarketDB&);
@@ -322,7 +327,7 @@
 class CAddrDB : public CDB
 {
 public:
-    CAddrDB(const char* pszMode="r+", bool fTxn=false) : CDB("addr.dat", pszMode, fTxn) { }
+    CAddrDB(const char* pszMode="r+") : CDB("addr.dat", pszMode) { }
 private:
     CAddrDB(const CAddrDB&);
     void operator=(const CAddrDB&);
@@ -341,7 +346,7 @@
 class CWalletDB : public CDB
 {
 public:
-    CWalletDB(const char* pszMode="r+", bool fTxn=false) : CDB("wallet.dat", pszMode, fTxn) { }
+    CWalletDB(const char* pszMode="r+") : CDB("wallet.dat", pszMode) { }
 private:
     CWalletDB(const CWalletDB&);
     void operator=(const CWalletDB&);
--- a/irc.cpp
+++ b/irc.cpp
@@ -159,15 +159,12 @@
     SetThreadPriority(THREAD_PRIORITY_NORMAL);
     int nErrorWait = 10;
     int nRetryWait = 10;
-
-    // IRC server blocks TOR users
-    if (fUseProxy && addrProxy.port == htons(9050))
-        return;
+    bool fTOR = (fUseProxy && addrProxy.port == htons(9050));
 
     while (!fShutdown)
     {
         CAddress addrConnect("216.155.130.130:6667");
-        if (!(fUseProxy && addrProxy.port == htons(9050)))
+        if (!fTOR)
         {
             struct hostent* phostent = gethostbyname("chat.freenode.net");
             if (phostent && phostent->h_addr_list && phostent->h_addr_list[0])
@@ -188,6 +185,7 @@
         if (!RecvUntil(hSocket, "Found your hostname", "using your IP address instead", "Couldn't look up your hostname"))
         {
             closesocket(hSocket);
+            hSocket = INVALID_SOCKET;
             nErrorWait = nErrorWait * 11 / 10;
             if (Wait(nErrorWait += 60))
                 continue;
@@ -208,6 +206,7 @@
         if (!RecvUntil(hSocket, " 004 "))
         {
             closesocket(hSocket);
+            hSocket = INVALID_SOCKET;
             nErrorWait = nErrorWait * 11 / 10;
             if (Wait(nErrorWait += 60))
                 continue;
@@ -269,6 +268,11 @@
             }
         }
         closesocket(hSocket);
+        hSocket = INVALID_SOCKET;
+
+        // IRC usually blocks TOR, so only try once
+        if (fTOR)
+            return;
 
         if (GetTime() - nStart > 20 * 60)
         {
--- a/main.cpp
+++ b/main.cpp
@@ -760,7 +760,7 @@
         bnNew = bnProofOfWorkLimit;
 
     /// debug print
-    printf("\n\n\nGetNextWorkRequired RETARGET *****\n");
+    printf("GetNextWorkRequired RETARGET\n");
     printf("nTargetTimespan = %d    nActualTimespan = %d\n", nTargetTimespan, nActualTimespan);
     printf("Before: %08x  %s\n", pindexLast->nBits, CBigNum().SetCompact(pindexLast->nBits).getuint256().ToString().c_str());
     printf("After:  %08x  %s\n", bnNew.GetCompact(), bnNew.getuint256().ToString().c_str());
@@ -1013,7 +1013,7 @@
 
 bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
 {
-    printf("*** REORGANIZE ***\n");
+    printf("REORGANIZE\n");
 
     // Find the fork
     CBlockIndex* pfork = pindexBest;
@@ -1114,7 +1114,7 @@
     // Check for duplicate
     uint256 hash = GetHash();
     if (mapBlockIndex.count(hash))
-        return error("AddToBlockIndex() : %s already exists", hash.ToString().substr(0,14).c_str());
+        return error("AddToBlockIndex() : %s already exists", hash.ToString().substr(0,16).c_str());
 
     // Construct new block index object
     CBlockIndex* pindexNew = new CBlockIndex(nFile, nBlockPos, *this);
@@ -1174,7 +1174,7 @@
         pindexBest = pindexNew;
         nBestHeight = pindexBest->nHeight;
         nTransactionsUpdated++;
-        printf("AddToBlockIndex: new best=%s  height=%d\n", hashBestChain.ToString().substr(0,14).c_str(), nBestHeight);
+        printf("AddToBlockIndex: new best=%s  height=%d\n", hashBestChain.ToString().substr(0,16).c_str(), nBestHeight);
     }
 
     txdb.TxnCommit();
@@ -1294,9 +1294,9 @@
     // Check for duplicate
     uint256 hash = pblock->GetHash();
     if (mapBlockIndex.count(hash))
-        return error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString().substr(0,14).c_str());
+        return error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString().substr(0,16).c_str());
     if (mapOrphanBlocks.count(hash))
-        return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,14).c_str());
+        return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,16).c_str());
 
     // Preliminary checks
     if (!pblock->CheckBlock())
@@ -1308,7 +1308,7 @@
     // If don't already have its previous block, shunt it off to holding area until we get it
     if (!mapBlockIndex.count(pblock->hashPrevBlock))
     {
-        printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,14).c_str());
+        printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,16).c_str());
         mapOrphanBlocks.insert(make_pair(hash, pblock));
         mapOrphanBlocksByPrev.insert(make_pair(pblock->hashPrevBlock, pblock));
 
@@ -1503,11 +1503,11 @@
         //   vMerkleTree: 4a5e1e
 
         // Genesis block
-        char* pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks";
+        const char* pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks";
         CTransaction txNew;
         txNew.vin.resize(1);
         txNew.vout.resize(1);
-        txNew.vin[0].scriptSig     = CScript() << 486604799 << CBigNum(4) << vector<unsigned char>((unsigned char*)pszTimestamp, (unsigned char*)pszTimestamp + strlen(pszTimestamp));
+        txNew.vin[0].scriptSig     = CScript() << 486604799 << CBigNum(4) << vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
         txNew.vout[0].nValue       = 50 * COIN;
         txNew.vout[0].scriptPubKey = CScript() << CBigNum("0x5F1DF16B2B704C8A578D0BBAF74D385CDE12C11EE50455F3C438EF4C3FBCF649B6DE611FEAE06279A60939E028A8D65C10B73071A6F16719274855FEB0FD8A6704") << OP_CHECKSIG;
         CBlock block;
@@ -1519,7 +1519,7 @@
         block.nBits    = 0x1d00ffff;
         block.nNonce   = 2083236893;
 
-            //// debug print, delete this later
+            //// debug print
             printf("%s\n", block.GetHash().ToString().c_str());
             printf("%s\n", block.hashMerkleRoot.ToString().c_str());
             printf("%s\n", hashGenesisBlock.ToString().c_str());
@@ -1592,7 +1592,7 @@
             pindex->nHeight,
             pindex->nFile,
             pindex->nBlockPos,
-            block.GetHash().ToString().substr(0,14).c_str(),
+            block.GetHash().ToString().substr(0,16).c_str(),
             DateTimeStrFormat("%x %H:%M:%S", block.nTime).c_str(),
             block.vtx.size());
 
@@ -1912,6 +1912,18 @@
                     CBlock block;
                     block.ReadFromDisk((*mi).second, !pfrom->fClient);
                     pfrom->PushMessage("block", block);
+
+                    // Trigger them to send a getblocks request for the next batch of inventory
+                    if (inv.hash == pfrom->hashContinue)
+                    {
+                        // Bypass PushInventory, this must send even if redundant,
+                        // and we want it right after the last block so they don't
+                        // wait for other stuff first.
+                        vector<CInv> vInv;
+                        vInv.push_back(CInv(MSG_BLOCK, hashBestChain));
+                        pfrom->PushMessage("inv", vInv);
+                        pfrom->hashContinue = 0;
+                    }
                 }
             }
             else if (inv.IsKnownType())
@@ -1948,25 +1960,23 @@
         // Send the rest of the chain
         if (pindex)
             pindex = pindex->pnext;
-        printf("getblocks %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,14).c_str());
+        printf("getblocks %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,16).c_str());
+        int nLimit = 500;
         for (; pindex; pindex = pindex->pnext)
         {
             if (pindex->GetBlockHash() == hashStop)
             {
-                printf("  getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,14).c_str());
+                printf("  getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,16).c_str());
                 break;
             }
-
-            // Bypass setInventoryKnown in case an inventory message got lost
-            CRITICAL_BLOCK(pfrom->cs_inventory)
+            pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash()));
+            if (--nLimit <= 0)
             {
-                CInv inv(MSG_BLOCK, pindex->GetBlockHash());
-                // returns true if wasn't already contained in the set
-                if (pfrom->setInventoryKnown2.insert(inv).second)
-                {
-                    pfrom->setInventoryKnown.erase(inv);
-                    pfrom->vInventoryToSend.push_back(inv);
-                }
+                // When this block is requested, we'll send an inv that'll make them
+                // getblocks the next batch of inventory.
+                printf("  getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,16).c_str());
+                pfrom->hashContinue = pindex->GetBlockHash();
+                break;
             }
         }
     }
@@ -2049,7 +2059,13 @@
         vRecv >> *pblock;
 
         //// debug print
-        printf("received block:\n"); pblock->print();
+        if (false)
+        {
+            printf("received block:\n");
+            pblock->print();
+        }
+        else
+            printf("received block %s\n", pblock->GetHash().ToString().substr(0,16).c_str());
 
         CInv inv(MSG_BLOCK, pblock->GetHash());
         pfrom->AddInventoryKnown(inv);
@@ -2175,9 +2191,13 @@
         if (pto->nVersion == 0)
             return true;
 
+        // Keep-alive ping
+        if (pto->nLastSend && GetTime() - pto->nLastSend > 12 * 60 && pto->vSend.empty())
+            pto->PushMessage("ping");
+
         // Address refresh broadcast
         static int64 nLastRebroadcast;
-        if (nLastRebroadcast < GetTime() - 24 * 60 * 60) // every 24 hours
+        if (GetTime() - nLastRebroadcast > 24 * 60 * 60) // every 24 hours
         {
             nLastRebroadcast = GetTime();
             CRITICAL_BLOCK(cs_vNodes)
@@ -2194,9 +2214,16 @@
             }
         }
 
-        // Keep-alive ping
-        if (pto->nLastSend && GetTime() - pto->nLastSend > 12 * 60 && pto->vSend.empty())
-            pto->PushMessage("ping");
+        // Clear inventory known periodically in case an inv message was missed,
+        // although usually they would just get it from another node.
+        static int64 nLastInventoryKnownClear;
+        if (GetTime() - nLastInventoryKnownClear > 2 * 60 * 60) // every 2 hours
+        {
+            nLastInventoryKnownClear = GetTime();
+            CRITICAL_BLOCK(cs_vNodes)
+                foreach(CNode* pnode, vNodes)
+                    pnode->setInventoryKnown.clear();
+        }
 
 
         //
@@ -2243,7 +2270,6 @@
                 }
             }
             pto->vInventoryToSend.clear();
-            pto->setInventoryKnown2.clear();
         }
         if (!vInventoryToSend.empty())
             pto->PushMessage("inv", vInventoryToSend);
@@ -2817,8 +2843,7 @@
 
         // This is only to keep the database open to defeat the auto-flush for the
         // duration of this scope.  This is the only place where this optimization
-        // maybe makes sense; please don't do it anywhere else.  Keeping databases
-        // open longer than necessary can create deadlocks.
+        // maybe makes sense; please don't do it anywhere else.
         CWalletDB walletdb("r");
 
         // Add the change's private key to wallet
--- a/main.h
+++ b/main.h
@@ -1009,9 +1009,9 @@
     void print() const
     {
         printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%d)\n",
-            GetHash().ToString().substr(0,14).c_str(),
+            GetHash().ToString().substr(0,16).c_str(),
             nVersion,
-            hashPrevBlock.ToString().substr(0,14).c_str(),
+            hashPrevBlock.ToString().substr(0,16).c_str(),
             hashMerkleRoot.ToString().substr(0,6).c_str(),
             nTime, nBits, nNonce,
             vtx.size());
@@ -1159,7 +1159,7 @@
         return strprintf("CBlockIndex(nprev=%08x, pnext=%08x, nFile=%d, nBlockPos=%-6d nHeight=%d, merkle=%s, hashBlock=%s)",
             pprev, pnext, nFile, nBlockPos, nHeight,
             hashMerkleRoot.ToString().substr(0,6).c_str(),
-            GetBlockHash().ToString().substr(0,14).c_str());
+            GetBlockHash().ToString().substr(0,16).c_str());
     }
 
     void print() const
@@ -1229,8 +1229,8 @@
         str += CBlockIndex::ToString();
         str += strprintf("\n                hashBlock=%s, hashPrev=%s, hashNext=%s)",
             GetBlockHash().ToString().c_str(),
-            hashPrev.ToString().substr(0,14).c_str(),
-            hashNext.ToString().substr(0,14).c_str());
+            hashPrev.ToString().substr(0,16).c_str(),
+            hashNext.ToString().substr(0,16).c_str());
         return str;
     }
 
--- a/makefile.unix
+++ b/makefile.unix
@@ -17,24 +17,21 @@
 
 INCLUDEPATHS= \
  -I"/usr/include" \
- -I"/usr/local/boost_1_40_0" \
- -I"/usr/local/db-4.7.25.NC/build_unix" \
  -I"/usr/local/include/wx-2.8" \
  -I"/usr/local/lib/wx/include/gtk2-ansi-debug-static-2.8"
 
 LIBPATHS= \
  -L"/usr/lib" \
  -L"/usr/local/lib" \
- -L"/usr/local/db-4.7.25.NC/build_unix"
 
 LIBS= \
- -Wl,-Bstatic -l boost_thread -l boost_system -l boost_filesystem -Wl,-Bdynamic \
+ -Wl,-Bstatic -l boost_system -l boost_filesystem -Wl,-Bdynamic \
  -Wl,-Bstatic -l db_cxx -l wx_gtk2$(D)-2.8 -Wl,-Bdynamic \
  -l crypto \
  -l gtk-x11-2.0 -l gthread-2.0 -l SM
 
 WXDEFS=-D__WXGTK__ -DNOPCH
-CFLAGS=-O0 -w -Wno-invalid-offsetof -Wformat $(DEBUGFLAGS) $(WXDEFS) $(INCLUDEPATHS)
+CFLAGS=-O0 -Wno-invalid-offsetof -Wformat $(DEBUGFLAGS) $(WXDEFS) $(INCLUDEPATHS)
 HEADERS=headers.h util.h main.h serialize.h uint256.h key.h bignum.h script.h db.h base58.h
 
 
--- a/net.cpp
+++ b/net.cpp
@@ -148,8 +148,8 @@
 bool GetMyExternalIP(unsigned int& ipRet)
 {
     CAddress addrConnect;
-    char* pszGet;
-    char* pszKeyword;
+    const char* pszGet;
+    const char* pszKeyword;
 
     if (fUseProxy)
         return false;
@@ -463,14 +463,21 @@
     }
 }
 
-void CNode::DoDisconnect()
+void CNode::CloseSocketDisconnect()
 {
-    if (fDebug)
-        printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());
-    printf("disconnecting node %s\n", addr.ToStringLog().c_str());
+    fDisconnect = true;
+    if (hSocket != INVALID_SOCKET)
+    {
+        if (fDebug)
+            printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());
+        printf("disconnecting node %s\n", addr.ToStringLog().c_str());
+        closesocket(hSocket);
+        hSocket = INVALID_SOCKET;
+    }
+}
 
-    closesocket(hSocket);
-
+void CNode::Cleanup()
+{
     // All of a nodes broadcasts and subscriptions are automatically torn down
     // when it goes down, so a node has to stay up to keep its broadcast going.
 
@@ -540,11 +547,12 @@
                     // remove from vNodes
                     vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end());
 
-                    // close socket
-                    pnode->DoDisconnect();
+                    // close socket and cleanup
+                    pnode->CloseSocketDisconnect();
+                    pnode->Cleanup();
 
                     // hold in disconnected pool until all refs are released
-                    pnode->nReleaseTime = max(pnode->nReleaseTime, GetTime() + 5 * 60);
+                    pnode->nReleaseTime = max(pnode->nReleaseTime, GetTime() + 15 * 60);
                     if (pnode->fNetworkNode || pnode->fInbound)
                         pnode->Release();
                     vNodesDisconnected.push_back(pnode);
@@ -599,6 +607,8 @@
         {
             foreach(CNode* pnode, vNodes)
             {
+                if (pnode->hSocket == INVALID_SOCKET || pnode->hSocket < 0)
+                    continue;
                 FD_SET(pnode->hSocket, &fdsetRecv);
                 FD_SET(pnode->hSocket, &fdsetError);
                 hSocketMax = max(hSocketMax, pnode->hSocket);
@@ -659,17 +669,22 @@
         //
         vector<CNode*> vNodesCopy;
         CRITICAL_BLOCK(cs_vNodes)
+        {
             vNodesCopy = vNodes;
+            foreach(CNode* pnode, vNodesCopy)
+                pnode->AddRef();
+        }
         foreach(CNode* pnode, vNodesCopy)
         {
             if (fShutdown)
                 return;
-            SOCKET hSocket = pnode->hSocket;
 
             //
             // Receive
             //
-            if (FD_ISSET(hSocket, &fdsetRecv) || FD_ISSET(hSocket, &fdsetError))
+            if (pnode->hSocket == INVALID_SOCKET)
+                continue;
+            if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError))
             {
                 TRY_CRITICAL_BLOCK(pnode->cs_vRecv)
                 {
@@ -678,7 +693,7 @@
 
                     // typical socket buffer is 8K-64K
                     char pchBuf[0x10000];
-                    int nBytes = recv(hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
+                    int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
                     if (nBytes > 0)
                     {
                         vRecv.resize(nPos + nBytes);
@@ -690,7 +705,7 @@
                         // socket closed gracefully
                         if (!pnode->fDisconnect)
                             printf("socket closed\n");
-                        pnode->fDisconnect = true;
+                        pnode->CloseSocketDisconnect();
                     }
                     else if (nBytes < 0)
                     {
@@ -700,7 +715,7 @@
                         {
                             if (!pnode->fDisconnect)
                                 printf("socket recv error %d\n", nErr);
-                            pnode->fDisconnect = true;
+                            pnode->CloseSocketDisconnect();
                         }
                     }
                 }
@@ -709,14 +724,16 @@
             //
             // Send
             //
-            if (FD_ISSET(hSocket, &fdsetSend))
+            if (pnode->hSocket == INVALID_SOCKET)
+                continue;
+            if (FD_ISSET(pnode->hSocket, &fdsetSend))
             {
                 TRY_CRITICAL_BLOCK(pnode->cs_vSend)
                 {
                     CDataStream& vSend = pnode->vSend;
                     if (!vSend.empty())
                     {
-                        int nBytes = send(hSocket, &vSend[0], vSend.size(), MSG_NOSIGNAL | MSG_DONTWAIT);
+                        int nBytes = send(pnode->hSocket, &vSend[0], vSend.size(), MSG_NOSIGNAL | MSG_DONTWAIT);
                         if (nBytes > 0)
                         {
                             vSend.erase(vSend.begin(), vSend.begin() + nBytes);
@@ -729,7 +746,7 @@
                             if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS)
                             {
                                 printf("socket send error %d\n", nErr);
-                                pnode->fDisconnect = true;
+                                pnode->CloseSocketDisconnect();
                             }
                         }
                     }
@@ -760,18 +777,12 @@
                 }
             }
         }
-
-
-        //// debug heartbeat
-        static int64 nHeartbeat1;
-        if (GetTime() - nHeartbeat1 >= 5 * 60)
+        CRITICAL_BLOCK(cs_vNodes)
         {
-            printf("%s sendrecv\n", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());
-            nHeartbeat1 = GetTime();
-            fDebug = true;
+            foreach(CNode* pnode, vNodesCopy)
+                pnode->Release();
         }
 
-
         nThreadSocketHandlerHeartbeat = GetTime();
         Sleep(10);
     }
@@ -812,18 +823,21 @@
     printf("ThreadOpenConnections started\n");
 
     // Connect to specific addresses
-    while (mapArgs.count("-connect"))
+    if (mapArgs.count("-connect"))
     {
-        foreach(string strAddr, mapMultiArgs["-connect"])
+        for (int64 nLoop = 0;; nLoop++)
         {
-            CAddress addr(strAddr, NODE_NETWORK);
-            if (addr.IsValid())
-                OpenNetworkConnection(addr);
-            for (int i = 0; i < 10; i++)
+            foreach(string strAddr, mapMultiArgs["-connect"])
             {
-                Sleep(1000);
-                if (fShutdown)
-                    return;
+                CAddress addr(strAddr, NODE_NETWORK);
+                if (addr.IsValid())
+                    OpenNetworkConnection(addr);
+                for (int i = 0; i < 10 && i < nLoop; i++)
+                {
+                    Sleep(500);
+                    if (fShutdown)
+                        return;
+                }
             }
         }
     }
@@ -837,7 +851,7 @@
             if (addr.IsValid())
             {
                 OpenNetworkConnection(addr);
-                Sleep(1000);
+                Sleep(500);
                 if (fShutdown)
                     return;
             }
@@ -898,7 +912,7 @@
                 //   30 days   27 hours
                 //   90 days   46 hours
                 //  365 days   93 hours
-                int64 nDelay = 3600.0 * sqrt(fabs(nSinceLastSeen) / 3600.0) + nRandomizer;
+                int64 nDelay = (int64)(3600.0 * sqrt(fabs(nSinceLastSeen) / 3600.0) + nRandomizer);
 
                 // Fast reconnect for one hour after last seen
                 if (nSinceLastSeen < 60 * 60)
@@ -1013,11 +1027,13 @@
         // Poll the connected nodes for messages
         vector<CNode*> vNodesCopy;
         CRITICAL_BLOCK(cs_vNodes)
+        {
             vNodesCopy = vNodes;
+            foreach(CNode* pnode, vNodesCopy)
+                pnode->AddRef();
+        }
         foreach(CNode* pnode, vNodesCopy)
         {
-            pnode->AddRef();
-
             // Receive messages
             TRY_CRITICAL_BLOCK(pnode->cs_vRecv)
                 ProcessMessages(pnode);
@@ -1029,8 +1045,11 @@
                 SendMessages(pnode);
             if (fShutdown)
                 return;
-
-            pnode->Release();
+        }
+        CRITICAL_BLOCK(cs_vNodes)
+        {
+            foreach(CNode* pnode, vNodesCopy)
+                pnode->Release();
         }
 
         // Wait and allow messages to bunch up
@@ -1257,8 +1276,7 @@
                     if (!fGot)
                     {
                         printf("*** closing socket\n");
-                        closesocket(pnode->hSocket);
-                        pnode->fDisconnect = true;
+                        pnode->CloseSocketDisconnect();
                     }
                 }
             }
@@ -1292,7 +1310,7 @@
     int64 nStart = GetTime();
     while (vnThreadsRunning[0] > 0 || vnThreadsRunning[2] > 0 || vnThreadsRunning[3] > 0)
     {
-        if (GetTime() - nStart > 15)
+        if (GetTime() - nStart > 20)
             break;
         Sleep(20);
     }
--- a/net.h
+++ b/net.h
@@ -414,7 +414,7 @@
 
     string ToString() const
     {
-        return strprintf("%s %s", GetCommand(), hash.ToString().substr(0,14).c_str());
+        return strprintf("%s %s", GetCommand(), hash.ToString().substr(0,16).c_str());
     }
 
     void print() const
@@ -504,6 +504,7 @@
     int64 nReleaseTime;
     map<uint256, CRequestTracker> mapRequests;
     CCriticalSection cs_mapRequests;
+    uint256 hashContinue;
 
     // flood
     vector<CAddress> vAddrToSend;
@@ -512,7 +513,6 @@
 
     // inventory based relay
     set<CInv> setInventoryKnown;
-    set<CInv> setInventoryKnown2;
     vector<CInv> vInventoryToSend;
     CCriticalSection cs_inventory;
     multimap<int64, CInv> mapAskFor;
@@ -541,6 +541,7 @@
         fDisconnect = false;
         nRefCount = 0;
         nReleaseTime = 0;
+        hashContinue = 0;
         fGetAddr = false;
         vfSubscribe.assign(256, false);
 
@@ -550,13 +551,16 @@
         CAddress addrYou = (fUseProxy ? CAddress("0.0.0.0") : addr);
         CAddress addrMe = (fUseProxy ? CAddress("0.0.0.0") : addrLocalHost);
         RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce));
-        PushMessage("version", VERSION, nLocalServices, nTime, addrYou, addrMe, nLocalHostNonce, string("test5"));
+        PushMessage("version", VERSION, nLocalServices, nTime, addrYou, addrMe, nLocalHostNonce, string(pszSubVer));
     }
 
     ~CNode()
     {
         if (hSocket != INVALID_SOCKET)
+        {
             closesocket(hSocket);
+            hSocket = INVALID_SOCKET;
+        }
     }
 
 private:
@@ -570,12 +574,13 @@
         return max(nRefCount, 0) + (GetTime() < nReleaseTime ? 1 : 0);
     }
 
-    void AddRef(int64 nTimeout=0)
+    CNode* AddRef(int64 nTimeout=0)
     {
         if (nTimeout != 0)
             nReleaseTime = max(nReleaseTime, GetTime() + nTimeout);
         else
             nRefCount++;
+        return this;
     }
 
     void Release()
@@ -899,7 +904,8 @@
     bool IsSubscribed(unsigned int nChannel);
     void Subscribe(unsigned int nChannel, unsigned int nHops=0);
     void CancelSubscribe(unsigned int nChannel);
-    void DoDisconnect();
+    void CloseSocketDisconnect();
+    void Cleanup();
 };
 
 
--- a/serialize.h
+++ b/serialize.h
@@ -20,6 +20,7 @@
 class CAutoFile;
 
 static const int VERSION = 106;
+static const char* pszSubVer = " linux-test8";
 
 
 
--- a/ui.cpp
+++ b/ui.cpp
@@ -1664,7 +1664,7 @@
 
 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
 {
-    m_staticTextVersion->SetLabel(strprintf("version 0.%d.%d Beta", VERSION/100, VERSION%100));
+    m_staticTextVersion->SetLabel(strprintf("version 0.%d.%d beta", VERSION/100, VERSION%100));
 
     // Workaround until upgrade to wxWidgets supporting UTF-8
     wxString str = m_staticTextMain->GetLabel();
@@ -2030,7 +2030,7 @@
     // We may have connected already for product details
     if (!Status("Connecting..."))
         return;
-    CNode* pnode = ConnectNode(addr, 5 * 60);
+    CNode* pnode = ConnectNode(addr, 15 * 60);
     if (!pnode)
     {
         Error("Unable to connect");
@@ -2075,14 +2075,6 @@
         return;
     }
 
-    // Should already be connected
-    CNode* pnode = ConnectNode(addr, 5 * 60);
-    if (!pnode)
-    {
-        Error("Lost connection");
-        return;
-    }
-
     // Pause to give the user a chance to cancel
     while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
     {
@@ -2112,6 +2104,14 @@
             return;
         }
 
+        // Make sure we're still connected
+        CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
+        if (!pnode)
+        {
+            Error("Lost connection, transaction cancelled");
+            return;
+        }
+
         // Last chance to cancel
         Sleep(50);
         if (!Status())
@@ -3495,12 +3495,14 @@
 
     if (mapArgs.count("-debug"))
         fDebug = true;
+    if (strstr(pszSubVer, "test"))
+        fDebug = true;
 
     if (mapArgs.count("-printtodebugger"))
         fPrintToDebugger = true;
 
     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, OS version %s\n", VERSION, wxGetOsDescription().mb_str());
+    printf("Bitcoin version %d%s, OS version %s\n", VERSION, pszSubVer, wxGetOsDescription().mb_str());
 
     if (mapArgs.count("-loadblockindextest"))
     {
@@ -3843,9 +3845,8 @@
         CoInitialize(NULL);
 
         // Get a pointer to the IShellLink interface.
-        HRESULT hres = NULL;
         IShellLink* psl = NULL;
-        hres = CoCreateInstance(CLSID_ShellLink, NULL,
+        HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
                                 CLSCTX_INPROC_SERVER, IID_IShellLink,
                                 reinterpret_cast<void**>(&psl));
 
--- a/util.cpp
+++ b/util.cpp
@@ -56,9 +56,11 @@
 
         // Close sockets
         foreach(CNode* pnode, vNodes)
-            closesocket(pnode->hSocket);
-        if (closesocket(hListenSocket) == SOCKET_ERROR)
-            printf("closesocket(hListenSocket) failed with error %d\n", WSAGetLastError());
+            if (pnode->hSocket != INVALID_SOCKET)
+                closesocket(pnode->hSocket);
+        if (hListenSocket != INVALID_SOCKET)
+            if (closesocket(hListenSocket) == SOCKET_ERROR)
+                printf("closesocket(hListenSocket) failed with error %d\n", WSAGetLastError());
 
 #ifdef __WXMSW__
         // Shutdown Windows Sockets
@@ -348,7 +350,7 @@
     {
         char psz[10000];
         strlcpy(psz, argv[i], sizeof(psz));
-        char* pszValue = "";
+        char* pszValue = (char*)"";
         if (strchr(psz, '='))
         {
             pszValue = strchr(psz, '=');
--- a/util.h
+++ b/util.h
@@ -57,9 +57,11 @@
 #ifdef __WXMSW__
 #define MSG_NOSIGNAL        0
 #define MSG_DONTWAIT        0
+#ifndef UINT64_MAX
 #define UINT64_MAX          _UI64_MAX
 #define INT64_MAX           _I64_MAX
 #define INT64_MIN           _I64_MIN
+#endif
 #else
 #define WSAGetLastError()   errno
 #define WSAEWOULDBLOCK      EWOULDBLOCK
@@ -67,7 +69,7 @@
 #define WSAEINTR            EINTR
 #define WSAEINPROGRESS      EINPROGRESS
 #define WSAEADDRINUSE       EADDRINUSE
-#define closesocket(s)      close(s)
+#define WSAENOTSOCK         EBADF
 #define INVALID_SOCKET      (SOCKET)(~0)
 #define SOCKET_ERROR        -1
 typedef u_int SOCKET;
@@ -80,6 +82,23 @@
 #define Beep(n1,n2)         (0)
 #endif
 
+inline int myclosesocket(SOCKET& hSocket)
+{
+    if (hSocket == INVALID_SOCKET)
+        return WSAENOTSOCK;
+#ifdef __WXMSW__
+    int ret = closesocket(hSocket);
+#else
+    int ret = close(hSocket);
+#endif
+    hSocket = INVALID_SOCKET;
+    return ret;
+}
+#define closesocket(s)      myclosesocket(s)
+
+
+
+
 
 
 
@@ -149,7 +168,7 @@
     bool TryEnter() { return mutex.TryLock() == wxMUTEX_NO_ERROR; }
 #endif
 public:
-    char* pszFile;
+    const char* pszFile;
     int nLine;
 };
 
--- a/xpm/addressbook16.xpm
+++ b/xpm/addressbook16.xpm
@@ -1,5 +1,5 @@
 /* XPM */
-static char * addressbook16_xpm[] = {
+static const char * addressbook16_xpm[] = {
 /* columns rows colors chars-per-pixel */
 "16 16 256 2",
 "   c #FFFFFF",
--- a/xpm/addressbook20.xpm
+++ b/xpm/addressbook20.xpm
@@ -1,5 +1,5 @@
 /* XPM */
-static char * addressbook20_xpm[] = {
+static const char * addressbook20_xpm[] = {
 /* columns rows colors chars-per-pixel */
 "20 20 256 2",
 "   c #FFFFFF",
--- a/xpm/bitcoin16.xpm
+++ b/xpm/bitcoin16.xpm
@@ -1,5 +1,5 @@
 /* XPM */
-static char * bitcoin16_xpm[] = {
+static const char * bitcoin16_xpm[] = {
 /* columns rows colors chars-per-pixel */
 "16 16 181 2",
 "   c #775605",
--- a/xpm/bitcoin20.xpm
+++ b/xpm/bitcoin20.xpm
@@ -1,5 +1,5 @@
 /* XPM */
-static char * bitcoin20_xpm[] = {
+static const char * bitcoin20_xpm[] = {
 /* columns rows colors chars-per-pixel */
 "20 20 200 2",
 "   c #7B5500",
--- a/xpm/bitcoin32.xpm
+++ b/xpm/bitcoin32.xpm
@@ -1,5 +1,5 @@
 /* XPM */
-static char * bitcoin32_xpm[] = {
+static const char * bitcoin32_xpm[] = {
 /* columns rows colors chars-per-pixel */
 "32 32 185 2",
 "   c #715103",
--- a/xpm/bitcoin48.xpm
+++ b/xpm/bitcoin48.xpm
@@ -1,5 +1,5 @@
 /* XPM */
-static char * bitcoin48_xpm[] = {
+static const char * bitcoin48_xpm[] = {
 /* columns rows colors chars-per-pixel */
 "48 48 224 2",
 "   c #715103",
--- a/xpm/check.xpm
+++ b/xpm/check.xpm
@@ -1,5 +1,5 @@
 /* XPM */
-static char * check_xpm[] = {
+static const char * check_xpm[] = {
 /* columns rows colors chars-per-pixel */
 "32 32 3 1",
 "  c #008000",
--- a/xpm/send16.xpm
+++ b/xpm/send16.xpm
@@ -1,5 +1,5 @@
 /* XPM */
-static char * send16_xpm[] = {
+static const char * send16_xpm[] = {
 /* columns rows colors chars-per-pixel */
 "16 16 256 2",
 "   c #ADF7AD",
--- a/xpm/send16noshadow.xpm
+++ b/xpm/send16noshadow.xpm
@@ -1,5 +1,5 @@
 /* XPM */
-static char * send16noshadow_xpm[] = {
+static const char * send16noshadow_xpm[] = {
 /* columns rows colors chars-per-pixel */
 "16 16 256 2",
 "   c #ADF7AD",
--- a/xpm/send20.xpm
+++ b/xpm/send20.xpm
@@ -1,5 +1,5 @@
 /* XPM */
-static char * send20_xpm[] = {
+static const char * send20_xpm[] = {
 /* columns rows colors chars-per-pixel */
 "20 20 256 2",
 "   c #CEFFCE",