From 461764cbbea5965ebbd248f221876743d7a9ccd4 Mon Sep 17 00:00:00 2001
From: s_nakamoto <s_nakamoto@1a98c847-1fd6-4fd8-948a-caf3550aa51b>
Date: Mon, 8 Nov 2010 22:06:07 +0000
Subject: [PATCH] -paytxfee is now per KB, BitcoinMiner prioritise transactions
 by how old their dependencies are

git-svn-id: https://bitcoin.svn.sourceforge.net/svnroot/bitcoin/trunk@176 1a98c847-1fd6-4fd8-948a-caf3550aa51b
---
 build-msw.txt |   5 +--
 init.cpp      |   3 +-
 main.cpp      | 119 ++++++++++++++++++++++++++++++++++++++++++--------
 main.h        |   8 ++--
 serialize.h   |   2 +-
 ui.cpp        |   9 +++-
 6 files changed, 119 insertions(+), 27 deletions(-)

diff --git a/build-msw.txt b/build-msw.txt
index 7a58aaa6e..3082f6941 100644
--- a/build-msw.txt
+++ b/build-msw.txt
@@ -63,8 +63,7 @@ nmake -f makefile.vc
 
 OpenSSL
 -------
-Bitcoin does not use any encryption.  If you want to do a no-everything
-build of OpenSSL to exclude encryption routines, a few patches are required.
+If you want to exclude unused optional algorithms, a few patches are required.
 (instructions for OpenSSL v0.9.8k)
 
 Edit engines\e_gmp.c and engines\e_capi.c and add this #ifndef around
@@ -88,7 +87,7 @@ Build
   cd \openssl
   ms\mingw32.bat
 
-If you want to use it with MSVC, generate the .lib file
+If you're using MSVC, generate the .lib file
   lib /machine:i386 /def:ms\libeay32.def /out:out\libeay32.lib
 
 
diff --git a/init.cpp b/init.cpp
index b10e22a32..78e842b5a 100644
--- a/init.cpp
+++ b/init.cpp
@@ -173,6 +173,7 @@ bool AppInit2(int argc, char* argv[])
             "  -proxy=<ip:port> \t  "   + _("Connect through socks4 proxy\n") +
             "  -addnode=<ip>    \t  "   + _("Add a node to connect to\n") +
             "  -connect=<ip>    \t\t  " + _("Connect only to the specified node\n") +
+            "  -paytxfee=<amt>  \t  "   + _("Fee per KB to add to transactions you send\n") +
             "  -server          \t\t  " + _("Accept command line and JSON-RPC commands\n") +
             "  -daemon          \t\t  " + _("Run in the background as a daemon and accept commands\n") +
             "  -testnet         \t\t  " + _("Use the test network\n") +
@@ -413,7 +414,7 @@ bool AppInit2(int argc, char* argv[])
             wxMessageBox(_("Invalid amount for -paytxfee=<amount>"), "Bitcoin");
             return false;
         }
-        if (nTransactionFee > 1 * COIN)
+        if (nTransactionFee > 0.25 * COIN)
             wxMessageBox(_("Warning: -paytxfee is set very high.  This is the transaction fee you will pay if you send a transaction."), "Bitcoin", wxOK | wxICON_EXCLAMATION);
     }
 
diff --git a/main.cpp b/main.cpp
index 2cb418aa4..904c2b8f7 100644
--- a/main.cpp
+++ b/main.cpp
@@ -277,6 +277,34 @@ void EraseOrphanTx(uint256 hash)
 // CTransaction
 //
 
+bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet)
+{
+    SetNull();
+    if (!txdb.ReadTxIndex(prevout.hash, txindexRet))
+        return false;
+    if (!ReadFromDisk(txindexRet.pos))
+        return false;
+    if (prevout.n >= vout.size())
+    {
+        SetNull();
+        return false;
+    }
+    return true;
+}
+
+bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout)
+{
+    CTxIndex txindex;
+    return ReadFromDisk(txdb, prevout, txindex);
+}
+
+bool CTransaction::ReadFromDisk(COutPoint prevout)
+{
+    CTxDB txdb("r");
+    CTxIndex txindex;
+    return ReadFromDisk(txdb, prevout, txindex);
+}
+
 bool CTxIn::IsMine() const
 {
     CRITICAL_BLOCK(cs_mapWallet)
@@ -2882,7 +2910,7 @@ void CallCPUID(int in, int& aret, int& cret)
         "mov %2, %%eax; " // in into eax
         "cpuid;"
         "mov %%eax, %0;" // eax into a
-        "mov %%ecx, %1;" // eax into c
+        "mov %%ecx, %1;" // ecx into c
         :"=r"(a),"=r"(c) /* output */
         :"r"(in) /* input */
         :"%eax","%ecx" /* clobbered register */
@@ -3068,42 +3096,97 @@ void BitcoinMiner()
         CRITICAL_BLOCK(cs_mapTransactions)
         {
             CTxDB txdb("r");
+
+            // Priority order to process transactions
+            multimap<double, CTransaction*> mapPriority;
+            for (map<uint256, CTransaction>::iterator mi = mapTransactions.begin(); mi != mapTransactions.end(); ++mi)
+            {
+                CTransaction& tx = (*mi).second;
+                if (tx.IsCoinBase() || !tx.IsFinal())
+                    continue;
+
+                double dPriority = 0;
+                foreach(const CTxIn& txin, tx.vin)
+                {
+                    // Read prev transaction
+                    CTransaction txPrev;
+                    CTxIndex txindex;
+                    if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex))
+                        continue;
+                    int64 nValueIn = txPrev.vout[txin.prevout.n].nValue;
+
+                    // Read block header
+                    int nConf = 0;
+                    CBlock block;
+                    if (block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
+                    {
+                        map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(block.GetHash());
+                        if (it != mapBlockIndex.end())
+                        {
+                            CBlockIndex* pindex = (*it).second;
+                            if (pindex->IsInMainChain())
+                                nConf = 1 + nBestHeight - pindex->nHeight;
+                        }
+                    }
+
+                    dPriority += (double)nValueIn * nConf;
+
+                    if (fDebug && mapArgs.count("-printpriority"))
+                        printf("priority     nValueIn=%-12I64d nConf=%-5d dPriority=%-20.1f\n", nValueIn, nConf, dPriority);
+                }
+
+                // Priority is sum(valuein * age) / txsize
+                dPriority /= ::GetSerializeSize(tx, SER_NETWORK);
+
+                mapPriority.insert(make_pair(-dPriority, &(*mi).second));
+
+                if (fDebug && mapArgs.count("-printpriority"))
+                    printf("priority %-20.1f %s\n%s\n", dPriority, tx.GetHash().ToString().substr(0,10).c_str(), tx.ToString().c_str());
+            }
+
+            // Collect transactions into block
             map<uint256, CTxIndex> mapTestPool;
-            vector<char> vfAlreadyAdded(mapTransactions.size());
             uint64 nBlockSize = 1000;
             int nBlockSigOps = 100;
             bool fFoundSomething = true;
             while (fFoundSomething)
             {
                 fFoundSomething = false;
-                unsigned int n = 0;
-                for (map<uint256, CTransaction>::iterator mi = mapTransactions.begin(); mi != mapTransactions.end(); ++mi, ++n)
+                for (multimap<double, CTransaction*>::iterator mi = mapPriority.begin(); mi != mapPriority.end();)
                 {
-                    if (vfAlreadyAdded[n])
-                        continue;
-                    CTransaction& tx = (*mi).second;
-                    if (tx.IsCoinBase() || !tx.IsFinal())
-                        continue;
+                    CTransaction& tx = *(*mi).second;
                     unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK);
                     if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN)
+                    {
+                        mapPriority.erase(mi++);
                         continue;
+                    }
                     int nTxSigOps = tx.GetSigOpCount();
                     if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
+                    {
+                        mapPriority.erase(mi++);
                         continue;
+                    }
 
                     // Transaction fee based on block size
                     int64 nMinFee = tx.GetMinFee(nBlockSize);
 
+                    // Connecting can fail due to dependency on other memory pool transactions
+                    // that aren't in the block yet, so keep trying in later passes
                     map<uint256, CTxIndex> mapTestPoolTmp(mapTestPool);
                     if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nMinFee))
+                    {
+                        mi++;
                         continue;
+                    }
                     swap(mapTestPool, mapTestPoolTmp);
 
+                    // Added
                     pblock->vtx.push_back(tx);
                     nBlockSize += nTxSize;
                     nBlockSigOps += nTxSigOps;
-                    vfAlreadyAdded[n] = true;
                     fFoundSomething = true;
+                    mapPriority.erase(mi++);
                 }
             }
         }
@@ -3426,16 +3509,15 @@ bool SelectCoins(int64 nTargetValue, set<CWalletTx*>& setCoinsRet)
 
 
 
-bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRequiredRet)
+bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet)
 {
-    nFeeRequiredRet = 0;
     CRITICAL_BLOCK(cs_main)
     {
         // txdb must be opened before the mapWallet lock
         CTxDB txdb("r");
         CRITICAL_BLOCK(cs_mapWallet)
         {
-            int64 nFee = nTransactionFee;
+            nFeeRet = nTransactionFee;
             loop
             {
                 wtxNew.vin.clear();
@@ -3444,7 +3526,7 @@ bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CR
                 if (nValue < 0)
                     return false;
                 int64 nValueOut = nValue;
-                int64 nTotalValue = nValue + nFee;
+                int64 nTotalValue = nValue + nFeeRet;
 
                 // Choose coins to use
                 set<CWalletTx*> setCoins;
@@ -3504,13 +3586,16 @@ bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CR
                                 return false;
 
                 // Limit size
-                if (::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK) >= MAX_BLOCK_SIZE_GEN/5)
+                unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK);
+                if (nBytes >= MAX_BLOCK_SIZE_GEN/5)
                     return false;
 
                 // Check that enough fee is included
-                if (nFee < wtxNew.GetMinFee())
+                int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000);
+                int64 nMinFee = wtxNew.GetMinFee();
+                if (nFeeRet < max(nPayFee, nMinFee))
                 {
-                    nFee = nFeeRequiredRet = wtxNew.GetMinFee();
+                    nFeeRet = max(nPayFee, nMinFee);
                     continue;
                 }
 
diff --git a/main.h b/main.h
index af552a5ba..5176d90ce 100644
--- a/main.h
+++ b/main.h
@@ -76,7 +76,7 @@ bool ProcessMessages(CNode* pfrom);
 bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv);
 bool SendMessages(CNode* pto, bool fSendTrickle);
 int64 GetBalance();
-bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRequiredRet);
+bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet);
 bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
 bool BroadcastTransaction(CWalletTx& wtxNew);
 string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false);
@@ -580,7 +580,6 @@ public:
         return true;
     }
 
-
     friend bool operator==(const CTransaction& a, const CTransaction& b)
     {
         return (a.nVersion  == b.nVersion &&
@@ -617,6 +616,9 @@ public:
     }
 
 
+    bool ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet);
+    bool ReadFromDisk(CTxDB& txdb, COutPoint prevout);
+    bool ReadFromDisk(COutPoint prevout);
     bool DisconnectInputs(CTxDB& txdb);
     bool ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
                        CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee=0);
@@ -1654,7 +1656,7 @@ public:
     bool Cancels(const CAlert& alert) const
     {
         if (!IsInEffect())
-            false;
+            return false; // this was a no-op before 31403
         return (alert.nID <= nCancel || setCancel.count(alert.nID));
     }
 
diff --git a/serialize.h b/serialize.h
index af45d898e..7b7ab840d 100644
--- a/serialize.h
+++ b/serialize.h
@@ -22,7 +22,7 @@ class CDataStream;
 class CAutoFile;
 static const unsigned int MAX_SIZE = 0x02000000;
 
-static const int VERSION = 31402;
+static const int VERSION = 31403;
 static const char* pszSubVer = "";
 
 
diff --git a/ui.cpp b/ui.cpp
index a6f550054..b4eec0a55 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -196,7 +196,7 @@ int ThreadSafeMessageBox(const string& message, const string& caption, int style
 
 bool ThreadSafeAskFee(int64 nFeeRequired, const string& strCaption, wxWindow* parent)
 {
-    if (nFeeRequired < CENT || fDaemon)
+    if (nFeeRequired < CENT || nFeeRequired <= nTransactionFee || fDaemon)
         return true;
     string strMessage = strprintf(
         _("This transaction is over the size limit.  You can still send it for a fee of %s, "
@@ -1966,8 +1966,13 @@ void CSendDialog::OnButtonSend(wxCommandEvent& event)
             string strError = SendMoney(scriptPubKey, nValue, wtx, true);
             if (strError == "")
                 wxMessageBox(_("Payment sent  "), _("Sending..."));
-            else if (strError != "ABORTED")
+            else if (strError == "ABORTED")
+                return; // leave send dialog open
+            else
+            {
                 wxMessageBox(strError + "  ", _("Sending..."));
+                EndModal(false);
+            }
         }
         else
         {