changeset 150:5e816eaf006d draft

better prevention of inventory relaying during initial download, message checksum between nodes with 0.2.9 or higher, optimization level up from -O0 to -O2, rpc functions: setlabel, getlabel, getaddressesbylabel, getreceivedbyaddress, getreceivedbylabel, listreceivedbyaddress, listreceivedbylabel -- version 0.2.9
author s_nakamoto <s_nakamoto@1a98c847-1fd6-4fd8-948a-caf3550aa51b>
date Wed, 26 May 2010 00:05:26 +0000
parents 59e41d9d065a
children 5b17b74a1db5
files db.h init.cpp main.cpp makefile.mingw makefile.unix net.h rpc.cpp serialize.h ui.cpp ui.h uibase.h uiproject.fbp util.cpp
diffstat 13 files changed, 364 insertions(+), 101 deletions(-) [+]
line wrap: on
line diff
--- a/db.h
+++ b/db.h
@@ -328,6 +328,8 @@
 
     bool EraseName(const string& strAddress)
     {
+        // This should only be used for sending addresses, never for receiving addresses,
+        // receiving addresses must always have an address book entry if they're not change return.
         CRITICAL_BLOCK(cs_mapAddressBook)
             mapAddressBook.erase(strAddress);
         nWalletDBUpdated++;
--- a/init.cpp
+++ b/init.cpp
@@ -224,9 +224,6 @@
         }
     }
 
-    if (fDaemon)
-        fprintf(stdout, "bitcoin server starting\n");
-
 #ifdef __WXGTK__
     if (fDaemon || fCommandLine)
     {
@@ -447,7 +444,8 @@
     //
     // Load data files
     //
-    bool fFirstRun;
+    if (fDaemon)
+        fprintf(stdout, "bitcoin server starting\n");
     strErrors = "";
     int64 nStart;
 
@@ -465,6 +463,7 @@
 
     printf("Loading wallet...\n");
     nStart = GetTimeMillis();
+    bool fFirstRun;
     if (!LoadWallet(fFirstRun))
         strErrors += _("Error loading wallet.dat      \n");
     printf(" wallet      %15"PRI64d"ms\n", GetTimeMillis() - nStart);
--- a/main.cpp
+++ b/main.cpp
@@ -1336,19 +1336,12 @@
     if (!AddToBlockIndex(nFile, nBlockPos))
         return error("AcceptBlock() : AddToBlockIndex failed");
 
-    // Don't relay old inventory during initial block download.
-    // Please keep this number updated to a few thousand below current block count.
-    if (hashBestChain == hash && nBestHeight > 55000)
-        RelayInventory(CInv(MSG_BLOCK, hash));
-
-    // // Add atoms to user reviews for coins created
-    // vector<unsigned char> vchPubKey;
-    // if (ExtractPubKey(vtx[0].vout[0].scriptPubKey, false, vchPubKey))
-    // {
-    //     unsigned short nAtom = GetRand(USHRT_MAX - 100) + 100;
-    //     vector<unsigned short> vAtoms(1, nAtom);
-    //     AddAtomsAndPropagate(Hash(vchPubKey.begin(), vchPubKey.end()), vAtoms, true);
-    // }
+    // Relay inventory, but don't relay old inventory during initial block download
+    if (hashBestChain == hash)
+        CRITICAL_BLOCK(cs_vNodes)
+            foreach(CNode* pnode, vNodes)
+                if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 55000))
+                    pnode->PushInventory(CInv(MSG_BLOCK, hash));
 
     return true;
 }
@@ -1721,6 +1714,7 @@
     //  (4) message start
     //  (12) command
     //  (4) size
+    //  (4) checksum
     //  (x) data
     //
 
@@ -1728,12 +1722,13 @@
     {
         // Scan for message start
         CDataStream::iterator pstart = search(vRecv.begin(), vRecv.end(), BEGIN(pchMessageStart), END(pchMessageStart));
-        if (vRecv.end() - pstart < sizeof(CMessageHeader))
+        int nHeaderSize = vRecv.GetSerializeSize(CMessageHeader());
+        if (vRecv.end() - pstart < nHeaderSize)
         {
-            if (vRecv.size() > sizeof(CMessageHeader))
+            if (vRecv.size() > nHeaderSize)
             {
                 printf("\n\nPROCESSMESSAGE MESSAGESTART NOT FOUND\n\n");
-                vRecv.erase(vRecv.begin(), vRecv.end() - sizeof(CMessageHeader));
+                vRecv.erase(vRecv.begin(), vRecv.end() - nHeaderSize);
             }
             break;
         }
@@ -1742,6 +1737,7 @@
         vRecv.erase(vRecv.begin(), pstart);
 
         // Read header
+        vector<char> vHeaderSave(vRecv.begin(), vRecv.begin() + nHeaderSize);
         CMessageHeader hdr;
         vRecv >> hdr;
         if (!hdr.IsValid())
@@ -1757,10 +1753,9 @@
         {
             // Rewind and wait for rest of message
             ///// need a mechanism to give up waiting for overlong message size error
-            //if (fDebug)
-            //    printf("message-break\n");
-            vRecv.insert(vRecv.begin(), BEGIN(hdr), END(hdr));
-            Sleep(100);
+            if (fDebug)
+                printf("message-break\n");
+            vRecv.insert(vRecv.begin(), vHeaderSave.begin(), vHeaderSave.end());
             break;
         }
 
@@ -1768,6 +1763,20 @@
         CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize, vRecv.nType, vRecv.nVersion);
         vRecv.ignore(nMessageSize);
 
+        // Checksum
+        if (vRecv.GetVersion() >= 209)
+        {
+            uint256 hash = Hash(vMsg.begin(), vMsg.end());
+            unsigned int nChecksum = 0;
+            memcpy(&nChecksum, &hash, sizeof(nChecksum));
+            if (nChecksum != hdr.nChecksum)
+            {
+                printf("ProcessMessage(%s, %d bytes) : CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n",
+                       strCommand.c_str(), nMessageSize, nChecksum, hdr.nChecksum);
+                continue;
+            }
+        }
+
         // Process message
         bool fRet = false;
         try
@@ -1844,6 +1853,9 @@
             vRecv >> addrFrom >> nNonce;
         if (pfrom->nVersion >= 106 && !vRecv.empty())
             vRecv >> strSubVer;
+        if (pfrom->nVersion >= 209 && !vRecv.empty())
+            vRecv >> pfrom->nStartingHeight;
+
         if (pfrom->nVersion == 0)
             return false;
 
@@ -1854,9 +1866,6 @@
             return true;
         }
 
-        pfrom->vSend.SetVersion(min(pfrom->nVersion, VERSION));
-        pfrom->vRecv.SetVersion(min(pfrom->nVersion, VERSION));
-
         pfrom->fClient = !(pfrom->nServices & NODE_NETWORK);
         if (pfrom->fClient)
         {
@@ -1866,6 +1875,13 @@
 
         AddTimeData(pfrom->addr.ip, nTime);
 
+        // Change version
+        if (pfrom->nVersion >= 209)
+            pfrom->PushMessage("verack");
+        pfrom->vSend.SetVersion(min(pfrom->nVersion, VERSION));
+        if (pfrom->nVersion < 209)
+            pfrom->vRecv.SetVersion(min(pfrom->nVersion, VERSION));
+
         // Ask the first connected node for block updates
         static bool fAskedForBlocks;
         if (!fAskedForBlocks && !pfrom->fClient)
@@ -1876,7 +1892,7 @@
 
         pfrom->fSuccessfullyConnected = true;
 
-        printf("version message: version %d\n", pfrom->nVersion);
+        printf("version message: version %d, blocks=%d\n", pfrom->nVersion, pfrom->nStartingHeight);
     }
 
 
@@ -1887,6 +1903,12 @@
     }
 
 
+    else if (strCommand == "verack")
+    {
+        pfrom->vRecv.SetVersion(min(pfrom->nVersion, VERSION));
+    }
+
+
     else if (strCommand == "addr")
     {
         vector<CAddress> vAddr;
@@ -2101,9 +2123,8 @@
         vRecv >> *pblock;
 
         //// debug print
-        // printf("received block:\n");
+        printf("received block %s\n", pblock->GetHash().ToString().substr(0,16).c_str());
         // pblock->print();
-        printf("received block %s\n", pblock->GetHash().ToString().substr(0,16).c_str());
 
         CInv inv(MSG_BLOCK, pblock->GetHash());
         pfrom->AddInventoryKnown(inv);
@@ -2388,8 +2409,11 @@
         int nAddThreads = nProcessors - vnThreadsRunning[3];
         printf("Starting %d BitcoinMiner threads\n", nAddThreads);
         for (int i = 0; i < nAddThreads; i++)
+        {
             if (!CreateThread(ThreadBitcoinMiner, NULL))
                 printf("Error: CreateThread(ThreadBitcoinMiner) failed\n");
+            Sleep(10);
+        }
     }
 }
 
--- a/makefile.mingw
+++ b/makefile.mingw
@@ -29,7 +29,7 @@
 
 WXDEFS=-DWIN32 -D__WXMSW__ -D_WINDOWS -DNOPCH
 DEBUGFLAGS=-g -D__WXDEBUG__
-CFLAGS=-mthreads -O0 -w -Wno-invalid-offsetof -Wformat $(DEBUGFLAGS) $(WXDEFS) $(INCLUDEPATHS)
+CFLAGS=-mthreads -O2 -w -Wno-invalid-offsetof -Wformat $(DEBUGFLAGS) $(WXDEFS) $(INCLUDEPATHS)
 HEADERS=headers.h strlcpy.h serialize.h uint256.h util.h key.h bignum.h base58.h \
     script.h db.h net.h irc.h main.h rpc.h uibase.h ui.h init.h sha.h
 
--- a/makefile.unix
+++ b/makefile.unix
@@ -29,7 +29,7 @@
 
 WXDEFS=-D__WXGTK__ -DNOPCH
 DEBUGFLAGS=-g -D__WXDEBUG__
-CFLAGS=-O0 -Wno-invalid-offsetof -Wformat $(DEBUGFLAGS) $(WXDEFS) $(INCLUDEPATHS)
+CFLAGS=-O2 -Wno-invalid-offsetof -Wformat $(DEBUGFLAGS) $(WXDEFS) $(INCLUDEPATHS)
 HEADERS=headers.h strlcpy.h serialize.h uint256.h util.h key.h bignum.h base58.h \
     script.h db.h net.h irc.h main.h rpc.h uibase.h ui.h init.h sha.h
 
--- a/net.h
+++ b/net.h
@@ -8,6 +8,7 @@
 class CRequestTracker;
 class CNode;
 class CBlockIndex;
+extern int nBestHeight;
 
 
 
@@ -59,7 +60,7 @@
     char pchMessageStart[sizeof(::pchMessageStart)];
     char pchCommand[COMMAND_SIZE];
     unsigned int nMessageSize;
-    //unsigned int nChecksum;
+    unsigned int nChecksum;
 
     CMessageHeader()
     {
@@ -67,7 +68,7 @@
         memset(pchCommand, 0, sizeof(pchCommand));
         pchCommand[1] = 1;
         nMessageSize = -1;
-        //nChecksum = 0;
+        nChecksum = 0;
     }
 
     CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn)
@@ -75,6 +76,7 @@
         memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart));
         strncpy(pchCommand, pszCommand, COMMAND_SIZE);
         nMessageSize = nMessageSizeIn;
+        nChecksum = 0;
     }
 
     IMPLEMENT_SERIALIZE
@@ -82,8 +84,8 @@
         READWRITE(FLATDATA(pchMessageStart));
         READWRITE(FLATDATA(pchCommand));
         READWRITE(nMessageSize);
-        //if (nVersion >= 209 && GetCommand() != "version")
-        //    READWRITE(nChecksum);
+        if (nVersion >= 209)
+            READWRITE(nChecksum);
     )
 
     string GetCommand()
@@ -475,6 +477,7 @@
 
 
 
+
 class CNode
 {
 public:
@@ -507,6 +510,7 @@
     uint256 hashContinue;
     CBlockIndex* pindexLastGetBlocksBegin;
     uint256 hashLastGetBlocksEnd;
+    int nStartingHeight;
 
     // flood
     vector<CAddress> vAddrToSend;
@@ -529,7 +533,9 @@
         nServices = 0;
         hSocket = hSocketIn;
         vSend.SetType(SER_NETWORK);
+        vSend.SetVersion(0);
         vRecv.SetType(SER_NETWORK);
+        vRecv.SetVersion(0);
         nLastSend = 0;
         nLastRecv = 0;
         nLastSendEmpty = GetTime();
@@ -548,6 +554,7 @@
         hashContinue = 0;
         pindexLastGetBlocksBegin = 0;
         hashLastGetBlocksEnd = 0;
+        nStartingHeight = -1;
         fGetAddr = false;
         nNextSendTxInv = 0;
         vfSubscribe.assign(256, false);
@@ -558,7 +565,8 @@
         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(pszSubVer));
+        PushMessage("version", VERSION, nLocalServices, nTime, addrYou, addrMe,
+                    nLocalHostNonce, string(pszSubVer), nBestHeight);
     }
 
     ~CNode()
@@ -680,10 +688,20 @@
         if (nHeaderStart == -1)
             return;
 
-        // Patch in the size
+        // Set the size
         unsigned int nSize = vSend.size() - nMessageStart;
         memcpy((char*)&vSend[nHeaderStart] + offsetof(CMessageHeader, nMessageSize), &nSize, sizeof(nSize));
 
+        // Set the checksum
+        if (vSend.GetVersion() >= 209)
+        {
+            uint256 hash = Hash(vSend.begin() + nMessageStart, vSend.end());
+            unsigned int nChecksum = 0;
+            memcpy(&nChecksum, &hash, sizeof(nChecksum));
+            assert(nMessageStart - nHeaderStart >= offsetof(CMessageHeader, nChecksum) + sizeof(nChecksum));
+            memcpy((char*)&vSend[nHeaderStart] + offsetof(CMessageHeader, nChecksum), &nChecksum, sizeof(nChecksum));
+        }
+
         printf("(%d bytes) ", nSize);
         printf("\n");
 
--- a/rpc.cpp
+++ b/rpc.cpp
@@ -26,7 +26,7 @@
 
 
 ///
-/// Note: I'm not finished designing this interface, it's still subject to change.
+/// Note: This interface may still be subject to change.
 ///
 
 
@@ -188,6 +188,73 @@
 }
 
 
+Value setlabel(const Array& params)
+{
+    if (params.size() < 1 || params.size() > 2)
+        throw runtime_error(
+            "setlabel <bitcoinaddress> <label>\n"
+            "Sets the label associated with the given address.");
+
+    string strAddress = params[0].get_str();
+    string strLabel;
+    if (params.size() > 1)
+        strLabel = params[1].get_str();
+
+    SetAddressBookName(strAddress, strLabel);
+    return Value::null;
+}
+
+
+Value getlabel(const Array& params)
+{
+    if (params.size() != 1)
+        throw runtime_error(
+            "getlabel <bitcoinaddress>\n"
+            "Returns the label associated with the given address.");
+
+    string strAddress = params[0].get_str();
+
+    string strLabel;
+    CRITICAL_BLOCK(cs_mapAddressBook)
+    {
+        map<string, string>::iterator mi = mapAddressBook.find(strAddress);
+        if (mi != mapAddressBook.end() && !(*mi).second.empty())
+            strLabel = (*mi).second;
+    }
+    return strLabel;
+}
+
+
+Value getaddressesbylabel(const Array& params)
+{
+    if (params.size() != 1)
+        throw runtime_error(
+            "getaddressesbylabel <label>\n"
+            "Returns the list of addresses with the given label.");
+
+    string strLabel = params[0].get_str();
+
+    // Find all addresses that have the given label
+    Array ret;
+    CRITICAL_BLOCK(cs_mapAddressBook)
+    {
+        foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
+        {
+            const string& strAddress = item.first;
+            const string& strName = item.second;
+            if (strName == strLabel)
+            {
+                // We're only adding valid bitcoin addresses and not ip addresses
+                CScript scriptPubKey;
+                if (scriptPubKey.SetBitcoinAddress(strAddress))
+                    ret.push_back(strAddress);
+            }
+        }
+    }
+    return ret;
+}
+
+
 Value sendtoaddress(const Array& params)
 {
     if (params.size() < 2 || params.size() > 4)
@@ -237,11 +304,11 @@
 }
 
 
-Value getamountreceived(const Array& params)
+Value getreceivedbyaddress(const Array& params)
 {
     if (params.size() < 1 || params.size() > 2)
         throw runtime_error(
-            "getamountreceived <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
@@ -249,6 +316,8 @@
     CScript scriptPubKey;
     if (!scriptPubKey.SetBitcoinAddress(strAddress))
         throw runtime_error("Invalid bitcoin address");
+    if (!IsMine(scriptPubKey))
+        return (double)0.0;
 
     // Minimum confirmations
     int nMinDepth = 1;
@@ -276,6 +345,59 @@
 }
 
 
+Value getreceivedbylabel(const Array& params)
+{
+    if (params.size() < 1 || params.size() > 2)
+        throw runtime_error(
+            "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
+    string strLabel = params[0].get_str();
+    set<CScript> setPubKey;
+    CRITICAL_BLOCK(cs_mapAddressBook)
+    {
+        foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
+        {
+            const string& strAddress = item.first;
+            const string& strName = item.second;
+            if (strName == strLabel)
+            {
+                // We're only counting our own valid bitcoin addresses and not ip addresses
+                CScript scriptPubKey;
+                if (scriptPubKey.SetBitcoinAddress(strAddress))
+                    if (IsMine(scriptPubKey))
+                        setPubKey.insert(scriptPubKey);
+            }
+        }
+    }
+
+    // Minimum confirmations
+    int nMinDepth = 1;
+    if (params.size() > 1)
+        nMinDepth = params[1].get_int();
+
+    // Tally
+    int64 nAmount = 0;
+    CRITICAL_BLOCK(cs_mapWallet)
+    {
+        for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+        {
+            const CWalletTx& wtx = (*it).second;
+            if (wtx.IsCoinBase() || !wtx.IsFinal())
+                continue;
+
+            foreach(const CTxOut& txout, wtx.vout)
+                if (setPubKey.count(txout.scriptPubKey))
+                    if (wtx.GetDepthInMainChain() >= nMinDepth)
+                        nAmount += txout.nValue;
+        }
+    }
+
+    return (double)nAmount / (double)COIN;
+}
+
+
 struct tallyitem
 {
     int64 nAmount;
@@ -287,23 +409,18 @@
     }
 };
 
-Value getallreceived(const Array& params)
+Value ListReceived(const Array& params, bool fByLabels)
 {
-    if (params.size() > 1)
-        throw runtime_error(
-            "getallreceived [minconf=1]\n"
-            "[minconf] is the minimum number of confirmations before payments are included.\n"
-            "Returns an array of objects containing:\n"
-            "  \"address\" : receiving address\n"
-            "  \"amount\" : total amount received by the address\n"
-            "  \"confirmations\" : number of confirmations of the most recent transaction included\n"
-            "  \"label\" : the label of the receiving address");
-
     // Minimum confirmations
     int nMinDepth = 1;
     if (params.size() > 0)
         nMinDepth = params[0].get_int();
 
+    // Whether to include empty accounts
+    bool fIncludeEmpty = false;
+    if (params.size() > 1)
+        fIncludeEmpty = params[1].get_bool();
+
     // Tally
     map<uint160, tallyitem> mapTally;
     CRITICAL_BLOCK(cs_mapWallet)
@@ -318,18 +435,11 @@
             if (nDepth < nMinDepth)
                 continue;
 
-            // Filter out debits and payments to self, which may have change return
-            // we don't want to count.
-            int64 nCredit = wtx.GetCredit(true);
-            int64 nDebit = wtx.GetDebit();
-            int64 nNet = nCredit - nDebit;
-            if (nNet <= 0)
-                continue;
-
             foreach(const CTxOut& txout, wtx.vout)
             {
+                // Only counting our own bitcoin addresses and not ip addresses
                 uint160 hash160 = txout.scriptPubKey.GetBitcoinAddressHash160();
-                if (hash160 == 0 || !mapPubKeys.count(hash160))
+                if (hash160 == 0 || !mapPubKeys.count(hash160)) // IsMine
                     continue;
 
                 tallyitem& item = mapTally[hash160];
@@ -341,27 +451,100 @@
 
     // Reply
     Array ret;
+    map<string, tallyitem> mapLabelTally;
     CRITICAL_BLOCK(cs_mapAddressBook)
     {
-        for (map<uint160, tallyitem>::iterator it = mapTally.begin(); it != mapTally.end(); ++it)
+        foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
         {
-            string strAddress = Hash160ToAddress((*it).first);
-            string strLabel;
-            map<string, string>::iterator mi = mapAddressBook.find(strAddress);
-            if (mi != mapAddressBook.end())
-                strLabel = (*mi).second;
+            const string& strAddress = item.first;
+            const string& strLabel = item.second;
+            uint160 hash160;
+            if (!AddressToHash160(strAddress, hash160))
+                continue;
+            map<uint160, tallyitem>::iterator it = mapTally.find(hash160);
+            if (it == mapTally.end() && !fIncludeEmpty)
+                continue;
+
+            int64 nAmount = 0;
+            int nConf = INT_MAX;
+            if (it != mapTally.end())
+            {
+                nAmount = (*it).second.nAmount;
+                nConf = (*it).second.nConf;
+            }
 
+            if (fByLabels)
+            {
+                tallyitem& item = mapLabelTally[strLabel];
+                item.nAmount += nAmount;
+                item.nConf = min(item.nConf, nConf);
+            }
+            else
+            {
+                Object obj;
+                obj.push_back(Pair("address",       strAddress));
+                obj.push_back(Pair("label",         strLabel));
+                obj.push_back(Pair("amount",        (double)nAmount / (double)COIN));
+                obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
+                ret.push_back(obj);
+            }
+        }
+    }
+
+    if (fByLabels)
+    {
+        for (map<string, tallyitem>::iterator it = mapLabelTally.begin(); it != mapLabelTally.end(); ++it)
+        {
+            int64 nAmount = (*it).second.nAmount;
+            int nConf = (*it).second.nConf;
             Object obj;
-            obj.push_back(Pair("address",       strAddress));
-            obj.push_back(Pair("amount",        (double)(*it).second.nAmount / (double)COIN));
-            obj.push_back(Pair("confirmations", (*it).second.nConf));
-            obj.push_back(Pair("label",         strLabel));
+            obj.push_back(Pair("label",         (*it).first));
+            obj.push_back(Pair("amount",        (double)nAmount / (double)COIN));
+            obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
             ret.push_back(obj);
         }
     }
+
     return ret;
 }
 
+Value listreceivedbyaddress(const Array& params)
+{
+    if (params.size() > 2)
+        throw runtime_error(
+            "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"
+            "  \"address\" : receiving address\n"
+            "  \"label\" : the label of the receiving address\n"
+            "  \"amount\" : total amount received by the address\n"
+            "  \"confirmations\" : number of confirmations of the most recent transaction included");
+
+    return ListReceived(params, false);
+}
+
+Value listreceivedbylabel(const Array& params)
+{
+    if (params.size() > 2)
+        throw runtime_error(
+            "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"
+            "  \"label\" : the label of the receiving addresses\n"
+            "  \"amount\" : total amount received by addresses with this label\n"
+            "  \"confirmations\" : number of confirmations of the most recent transaction included");
+
+    return ListReceived(params, true);
+}
+
+
+
+
+
+
+
 
 
 
@@ -375,20 +558,27 @@
 typedef Value(*rpcfn_type)(const Array& params);
 pair<string, rpcfn_type> pCallTable[] =
 {
-    make_pair("stop",               &stop),
-    make_pair("getblockcount",      &getblockcount),
-    make_pair("getblocknumber",     &getblocknumber),
-    make_pair("getconnectioncount", &getconnectioncount),
-    make_pair("getdifficulty",      &getdifficulty),
-    make_pair("getbalance",         &getbalance),
-    make_pair("getgenerate",        &getgenerate),
-    make_pair("setgenerate",        &setgenerate),
-    make_pair("getinfo",            &getinfo),
-    make_pair("getnewaddress",      &getnewaddress),
-    make_pair("sendtoaddress",      &sendtoaddress),
-    make_pair("listtransactions",   &listtransactions),
-    make_pair("getamountreceived",  &getamountreceived),
-    make_pair("getallreceived",     &getallreceived),
+    make_pair("stop",                  &stop),
+    make_pair("getblockcount",         &getblockcount),
+    make_pair("getblocknumber",        &getblocknumber),
+    make_pair("getconnectioncount",    &getconnectioncount),
+    make_pair("getdifficulty",         &getdifficulty),
+    make_pair("getbalance",            &getbalance),
+    make_pair("getgenerate",           &getgenerate),
+    make_pair("setgenerate",           &setgenerate),
+    make_pair("getinfo",               &getinfo),
+    make_pair("getnewaddress",         &getnewaddress),
+    make_pair("setlabel",              &setlabel),
+    make_pair("getlabel",              &getlabel),
+    make_pair("getaddressesbylabel",   &getaddressesbylabel),
+    make_pair("sendtoaddress",         &sendtoaddress),
+    make_pair("listtransactions",      &listtransactions),
+    make_pair("getamountreceived",     &getreceivedbyaddress), // deprecated, renamed to getreceivedbyaddress
+    make_pair("getallreceived",        &listreceivedbyaddress), // deprecated, renamed to listreceivedbyaddress
+    make_pair("getreceivedbyaddress",  &getreceivedbyaddress),
+    make_pair("getreceivedbylabel",    &getreceivedbylabel),
+    make_pair("listreceivedbyaddress", &listreceivedbyaddress),
+    make_pair("listreceivedbylabel",   &listreceivedbylabel),
 };
 map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
 
@@ -666,13 +856,20 @@
         //
         // Special case other types
         //
-        if (strMethod == "setgenerate"       && n > 0) ConvertTo<bool>(params[0]);
-        if (strMethod == "setgenerate"       && n > 1) ConvertTo<boost::int64_t>(params[1]);
-        if (strMethod == "sendtoaddress"     && n > 1) ConvertTo<double>(params[1]);
-        if (strMethod == "listtransactions"  && n > 0) ConvertTo<boost::int64_t>(params[0]);
-        if (strMethod == "listtransactions"  && n > 1) ConvertTo<bool>(params[1]);
-        if (strMethod == "getamountreceived" && n > 1) ConvertTo<boost::int64_t>(params[1]);
-        if (strMethod == "getallreceived"    && n > 0) ConvertTo<boost::int64_t>(params[0]);
+        if (strMethod == "setgenerate"            && n > 0) ConvertTo<bool>(params[0]);
+        if (strMethod == "setgenerate"            && n > 1) ConvertTo<boost::int64_t>(params[1]);
+        if (strMethod == "sendtoaddress"          && n > 1) ConvertTo<double>(params[1]);
+        if (strMethod == "listtransactions"       && n > 0) ConvertTo<boost::int64_t>(params[0]);
+        if (strMethod == "listtransactions"       && n > 1) ConvertTo<bool>(params[1]);
+        if (strMethod == "getamountreceived"      && n > 1) ConvertTo<boost::int64_t>(params[1]); // deprecated
+        if (strMethod == "getreceivedbyaddress"   && n > 1) ConvertTo<boost::int64_t>(params[1]);
+        if (strMethod == "getreceivedbylabel"     && n > 1) ConvertTo<boost::int64_t>(params[1]);
+        if (strMethod == "getallreceived"         && n > 0) ConvertTo<boost::int64_t>(params[0]); // deprecated
+        if (strMethod == "getallreceived"         && n > 1) ConvertTo<bool>(params[1]);
+        if (strMethod == "listreceivedbyaddress"  && n > 0) ConvertTo<boost::int64_t>(params[0]);
+        if (strMethod == "listreceivedbyaddress"  && n > 1) ConvertTo<bool>(params[1]);
+        if (strMethod == "listreceivedbylabel"    && n > 0) ConvertTo<boost::int64_t>(params[0]);
+        if (strMethod == "listreceivedbylabel"    && n > 1) ConvertTo<bool>(params[1]);
 
         // Execute
         Value result = CallRPC(strMethod, params);
--- a/serialize.h
+++ b/serialize.h
@@ -19,7 +19,7 @@
 class CDataStream;
 class CAutoFile;
 
-static const int VERSION = 208;
+static const int VERSION = 209;
 static const char* pszSubVer = ".0";
 
 
@@ -809,6 +809,18 @@
             vch.insert(it, first, last);
     }
 
+    void insert(iterator it, vector<char>::const_iterator first, vector<char>::const_iterator last)
+    {
+        if (it == vch.begin() + nReadPos && last - first <= nReadPos)
+        {
+            // special case for inserting at the front when there's room
+            nReadPos -= (last - first);
+            memcpy(&vch[nReadPos], &first[0], last - first);
+        }
+        else
+            vch.insert(it, first, last);
+    }
+
 #if !defined(_MSC_VER) || _MSC_VER >= 1300
     void insert(iterator it, const char* first, const char* last)
     {
--- a/ui.cpp
+++ b/ui.cpp
@@ -193,6 +193,12 @@
     return (ThreadSafeMessageBox(strMessage, strCaption, wxYES_NO, parent) == wxYES);
 }
 
+void CalledSetStatusBar(const string& strText, int nField)
+{
+    if (pframeMain && pframeMain->m_statusBar)
+        pframeMain->m_statusBar->SetStatusText(strText, nField);
+}
+
 void SetDefaultReceivingAddress(const string& strAddress)
 {
     // Update main window address and database
@@ -268,7 +274,8 @@
     if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
         nDateWidth += 12;
 #ifdef __WXMAC__
-    nDateWidth += 2;
+    nDateWidth += 5;
+    dResize -= 0.01;
 #endif
     wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
     foreach(wxListCtrl* p, pplistCtrl)
--- a/ui.h
+++ b/ui.h
@@ -30,6 +30,7 @@
 void UIThreadCall(boost::function0<void>);
 int ThreadSafeMessageBox(const string& message, const string& caption="Message", int style=wxOK, wxWindow* parent=NULL, int x=-1, int y=-1);
 bool ThreadSafeAskFee(int64 nFeeRequired, const string& strCaption, wxWindow* parent);
+void CalledSetStatusBar(const string& strText, int nField);
 void MainFrameRepaint();
 void CreateMainWindow();
 
@@ -48,6 +49,14 @@
     return true;
 }
 
+inline void CalledSetStatusBar(const string& strText, int nField)
+{
+}
+
+inline void UIThreadCall(boost::function0<void> fn)
+{
+}
+
 inline void MainFrameRepaint()
 {
 }
--- a/uibase.h
+++ b/uibase.h
@@ -77,7 +77,6 @@
 		wxMenu* m_menuFile;
 		wxMenu* m_menuHelp;
 		wxToolBar* m_toolBar;
-		wxStatusBar* m_statusBar;
 		
 		wxStaticText* m_staticText32;
 		wxButton* m_buttonNew;
@@ -121,6 +120,7 @@
 	
 	public:
 		wxMenu* m_menuOptions;
+		wxStatusBar* m_statusBar;
 		wxTextCtrl* m_textCtrlAddress;
 		wxListCtrl* m_listCtrlAll;
 		wxListCtrl* m_listCtrlSentReceived;
--- a/uiproject.fbp
+++ b/uiproject.fbp
@@ -293,7 +293,7 @@
                 <property name="maximum_size"></property>
                 <property name="minimum_size"></property>
                 <property name="name">m_statusBar</property>
-                <property name="permission">protected</property>
+                <property name="permission">public</property>
                 <property name="pos"></property>
                 <property name="size"></property>
                 <property name="style">wxST_SIZEGRIP</property>
--- a/util.cpp
+++ b/util.cpp
@@ -250,11 +250,6 @@
         if (p == NULL)
             throw std::bad_alloc();
     }
-#ifdef _MSC_VER
-    // msvc optimisation
-    if (p == buffer)
-        return string(p, p+ret);
-#endif
     string str(p, p+ret);
     if (p != buffer)
         delete p;