обновление статистики более часто
[novacoin.git] / src / wallet.cpp
index 477c4f3..f9ce658 100644 (file)
 #include "coincontrol.h"
 #include <boost/algorithm/string/replace.hpp>
 
+#include "main.h"
+
 using namespace std;
-extern int nStakeMaxAge;
+
 
 bool fCoinsDataActual;
 
@@ -391,6 +393,7 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx, bool fBlock)
                     wtx.MarkSpent(txin.prevout.n);
                     wtx.WriteToDisk();
                     NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED);
+                    vMintingWalletUpdated.push_back(txin.prevout.hash);
                 }
             }
         }
@@ -408,6 +411,7 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx, bool fBlock)
                     wtx.MarkUnspent(&txout - &tx.vout[0]);
                     wtx.WriteToDisk();
                     NotifyTransactionChanged(this, hash, CT_UPDATED);
+                    vMintingWalletUpdated.push_back(hash);
                 }
             }
         }
@@ -538,7 +542,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn)
 
         // Notify UI of new or updated transaction
         NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED);
-
+        vMintingWalletUpdated.push_back(hash);
         // notify an external script when a wallet transaction comes in or is updated
         std::string strCmd = GetArg("-walletnotify", "");
 
@@ -644,8 +648,7 @@ bool CWallet::IsChange(const CTxOut& txout) const
 
 int64 CWalletTx::GetTxTime() const
 {
-    int64 n = nTimeSmart;
-    return n ? n : nTimeReceived;
+    return nTime;
 }
 
 int CWalletTx::GetRequestCount() const
@@ -1153,7 +1156,7 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
     }
 }
 
-void CWallet::AvailableCoinsMinConf(vector<COutput>& vCoins, int nConf) const
+void CWallet::AvailableCoinsMinConf(vector<COutput>& vCoins, int nConf, int64 nMinValue, int64 nMaxValue) const
 {
     vCoins.clear();
 
@@ -1172,7 +1175,12 @@ void CWallet::AvailableCoinsMinConf(vector<COutput>& vCoins, int nConf) const
             for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
                 isminetype mine = IsMine(pcoin->vout[i]);
 
-                if (!(pcoin->IsSpent(i)) && mine != MINE_NO && pcoin->vout[i].nValue >= nMinimumInputValue)
+                // ignore coin if it was already spent or we don't own it
+                if (pcoin->IsSpent(i) || mine == MINE_NO)
+                    continue;
+
+                // if coin value is between required limits then add new item to vector
+                if (pcoin->vout[i].nValue >= nMinValue && pcoin->vout[i].nValue < nMaxValue)
                     vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain(), mine == MINE_SPENDABLE));
             }
         }
@@ -1402,10 +1410,10 @@ bool CWallet::SelectCoins(int64 nTargetValue, unsigned int nSpendTime, set<pair<
 }
 
 // Select some coins without random shuffle or best subset approximation
-bool CWallet::SelectCoinsSimple(int64 nTargetValue, unsigned int nSpendTime, int nMinConf, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
+bool CWallet::SelectCoinsSimple(int64 nTargetValue, int64 nMinValue, int64 nMaxValue, unsigned int nSpendTime, int nMinConf, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
 {
     vector<COutput> vCoins;
-    AvailableCoinsMinConf(vCoins, nMinConf);
+    AvailableCoinsMinConf(vCoins, nMinConf, nMinValue, nMaxValue);
 
     setCoinsRet.clear();
     nValueRet = 0;
@@ -1617,69 +1625,40 @@ void CWallet::GetStakeWeightFromValue(const int64& nTime, const int64& nValue, u
 }
 
 
-// NovaCoin: get current stake weight
-bool CWallet::GetStakeWeight(const CKeyStore& keystore, uint64& nMinWeight, uint64& nMaxWeight, uint64& nWeight)
+// NovaCoin: get current stake miner statistics
+void CWallet::GetStakeStats(float &nKernelsRate, float &nCoinDaysRate)
 {
-    // Choose coins to use
-    int64 nBalance = GetBalance();
-    int64 nReserveBalance = 0;
+    static uint64 nLastKernels = 0, nLastCoinDays = 0;
+    static float nLastKernelsRate = 0, nLastCoinDaysRate = 0;
+    static int64 nLastTime = GetTime();
 
-    if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
+    if (nKernelsTried < nLastKernels)
     {
-        error("GetStakeWeight : invalid reserve balance amount");
-        return false;
-    }
-
-    if (nBalance <= nReserveBalance)
-        return false;
+        nLastKernels = 0;
+        nLastCoinDays = 0;
 
-    vector<const CWalletTx*> vwtxPrev;
-
-    static set<pair<const CWalletTx*,unsigned int> > setCoins;
-    static uint256 hashPrevBlock;
+        nLastTime = GetTime();
+    }
 
-    // Cache outputs unless best block or wallet transaction set changed
-    if (!fCoinsDataActual || setCoins.size() == 0)
+    int64 nInterval = GetTime() - nLastTime;
+    //if (nKernelsTried > 1000 && nInterval > 5)
+    if (nInterval > 10)
     {
-        int64 nValueIn = 0;
-        if (!SelectCoinsSimple(nBalance - nReserveBalance, GetAdjustedTime(), nCoinbaseMaturity * 10, setCoins, nValueIn))
-            return false;
+        nKernelsRate = nLastKernelsRate = ( nKernelsTried - nLastKernels ) / (float) nInterval;
+        nCoinDaysRate = nLastCoinDaysRate = ( nCoinDaysTried - nLastCoinDays ) / (float) nInterval;
 
-        if (setCoins.empty())
-            return false;
-
-        fCoinsDataActual = true;
+        nLastKernels = nKernelsTried;
+        nLastCoinDays = nCoinDaysTried;
+        nLastTime = GetTime();
     }
-
-    CTxDB txdb("r");
-    BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
+    else
     {
-        int64 nTimeWeight = GetWeight((int64)pcoin.first->nTime, (int64)GetTime());
-        CBigNum bnCoinDayWeight = CBigNum(pcoin.first->vout[pcoin.second].nValue) * nTimeWeight / COIN / (24 * 60 * 60);
-
-        // Weight is greater than zero
-        if (nTimeWeight > 0)
-        {
-            nWeight += bnCoinDayWeight.getuint64();
-        }
-
-        // Weight is greater than zero, but the maximum value isn't reached yet
-        if (nTimeWeight > 0 && nTimeWeight < nStakeMaxAge)
-        {
-            nMinWeight += bnCoinDayWeight.getuint64();
-        }
-
-        // Maximum weight was reached
-        if (nTimeWeight == nStakeMaxAge)
-        {
-            nMaxWeight += bnCoinDayWeight.getuint64();
-        }
+        nKernelsRate = nLastKernelsRate;
+        nCoinDaysRate = nLastCoinDaysRate;
     }
-
-    return true;
 }
 
-bool CWallet::MergeCoins(const int64& nAmount, const int64& nMaxValue, const int64& nOutputValue, list<uint256>& listMerged)
+bool CWallet::MergeCoins(const int64& nAmount, const int64& nMinValue, const int64& nOutputValue, list<uint256>& listMerged)
 {
     int64 nBalance = GetBalance();
 
@@ -1691,7 +1670,7 @@ bool CWallet::MergeCoins(const int64& nAmount, const int64& nMaxValue, const int
     set<pair<const CWalletTx*,unsigned int> > setCoins;
 
     // Simple coins selection - no randomization
-    if (!SelectCoinsSimple(nAmount, GetTime(), 1, setCoins, nValueIn))
+    if (!SelectCoinsSimple(nAmount, nMinValue, nOutputValue, GetTime(), 1, setCoins, nValueIn))
         return false;
 
     if (setCoins.empty())
@@ -1716,10 +1695,6 @@ bool CWallet::MergeCoins(const int64& nAmount, const int64& nMaxValue, const int
     {
         int64 nCredit = pcoin.first->vout[pcoin.second].nValue;
 
-        // Ignore coin if credit is too high
-        if (nCredit >= nMaxValue)
-            continue;
-
         // Add current coin to inputs list and add its credit to transaction output
         wtxNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second));
         wtxNew.vout[0].nValue += nCredit;
@@ -1836,20 +1811,16 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int
 
     vector<const CWalletTx*> vwtxPrev;
 
-    static CoinsSet setCoins;
-    static MetaMap mapMeta;
-
     CTxDB txdb("r");
     {
         LOCK2(cs_main, cs_wallet);
         // Cache outputs unless best block or wallet transaction set changed
-        if (!fCoinsDataActual || setCoins.size() == 0)
+        if (!fCoinsDataActual)
         {
-            setCoins.clear();
             mapMeta.clear();
-
             int64 nValueIn = 0;
-            if (!SelectCoinsSimple(nBalance - nReserveBalance, txNew.nTime, nCoinbaseMaturity * 10, setCoins, nValueIn))
+            CoinsSet setCoins;
+            if (!SelectCoinsSimple(nBalance - nReserveBalance, MIN_TX_FEE, MAX_MONEY, txNew.nTime, nCoinbaseMaturity * 10, setCoins, nValueIn))
                 return false;
 
             if (setCoins.empty())
@@ -1869,12 +1840,12 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int
                         continue;
 
                     uint64 nStakeModifier = 0;
-
                     if (!GetKernelStakeModifier(block.GetHash(), nStakeModifier))
-                        return false;
+                        continue;
 
                     // Add meta record
-                    mapMeta[pcoin->first->GetHash()] = make_pair(txindex, make_pair(block, nStakeModifier));
+                    // txid => ((txindex, (tx, vout.n)), (block, modifier))
+                    mapMeta[make_pair(pcoin->first->GetHash(), pcoin->second)] = make_pair(make_pair(txindex, *pcoin), make_pair(block, nStakeModifier));
 
                     if (fDebug)
                         printf("Load coin: %s\n", pcoin->first->GetHash().GetHex().c_str());
@@ -1882,9 +1853,11 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int
             }
 
             if (fDebug)
-                printf("Stake miner: %zu meta items loaded for %zu coins\n", mapMeta.size(), setCoins.size());
+                printf("Stake miner: %"PRIszu" meta items loaded for %"PRIszu" coins\n", mapMeta.size(), setCoins.size());
 
             fCoinsDataActual = true;
+            nKernelsTried = 0;
+            nCoinDaysTried = 0;
         }
     }
 
@@ -1895,14 +1868,14 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int
     settings.nBits = nBits;
     settings.nTime = txNew.nTime;
     settings.nOffset = 0;
-    settings.nLimit = setCoins.size();
+    settings.nLimit = mapMeta.size();
     settings.nSearchInterval = nSearchInterval;
 
     unsigned int nTimeTx, nBlockTime;
     COutPoint prevoutStake;
     CoinsSet::value_type kernelcoin;
 
-    if (ScanForStakeKernelHash(setCoins, mapMeta, settings, kernelcoin, nTimeTx, nBlockTime))
+    if (ScanForStakeKernelHash(mapMeta, settings, kernelcoin, nTimeTx, nBlockTime, nKernelsTried, nCoinDaysTried))
     {
         // Found a kernel
         if (fDebug && GetBoolArg("-printcoinstake"))
@@ -1970,14 +1943,18 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int
     if (nCredit == 0 || nCredit > nBalance - nReserveBalance)
         return false;
 
-    for(CoinsSet::const_iterator pcoin = setCoins.begin(); pcoin != setCoins.end(); pcoin++)
+    // (txid, vout.n) => ((txindex, (tx, vout.n)), (block, modifier))
+    for(MetaMap::const_iterator meta_item = mapMeta.begin(); meta_item != mapMeta.end(); meta_item++)
     {
+        // Get coin
+        CoinsSet::value_type pcoin = meta_item->second.first.second;
+
         // Attempt to add more inputs
         // Only add coins of the same key/address as kernel
-        if (txNew.vout.size() == 2 && ((pcoin->first->vout[pcoin->second].scriptPubKey == scriptPubKeyKernel || pcoin->first->vout[pcoin->second].scriptPubKey == txNew.vout[1].scriptPubKey))
-            && pcoin->first->GetHash() != txNew.vin[0].prevout.hash)
+        if (txNew.vout.size() == 2 && ((pcoin.first->vout[pcoin.second].scriptPubKey == scriptPubKeyKernel || pcoin.first->vout[pcoin.second].scriptPubKey == txNew.vout[1].scriptPubKey))
+            && pcoin.first->GetHash() != txNew.vin[0].prevout.hash)
         {
-            int64 nTimeWeight = GetWeight((int64)pcoin->first->nTime, (int64)txNew.nTime);
+            int64 nTimeWeight = GetWeight((int64)pcoin.first->nTime, (int64)txNew.nTime);
 
             // Stop adding more inputs if already too many inputs
             if (txNew.vin.size() >= 100)
@@ -1986,18 +1963,18 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int
             if (nCredit > nCombineThreshold)
                 break;
             // Stop adding inputs if reached reserve limit
-            if (nCredit + pcoin->first->vout[pcoin->second].nValue > nBalance - nReserveBalance)
+            if (nCredit + pcoin.first->vout[pcoin.second].nValue > nBalance - nReserveBalance)
                 break;
             // Do not add additional significant input
-            if (pcoin->first->vout[pcoin->second].nValue > nCombineThreshold)
+            if (pcoin.first->vout[pcoin.second].nValue > nCombineThreshold)
                 continue;
             // Do not add input that is still too young
             if (nTimeWeight < nStakeMaxAge)
                 continue;
 
-            txNew.vin.push_back(CTxIn(pcoin->first->GetHash(), pcoin->second));
-            nCredit += pcoin->first->vout[pcoin->second].nValue;
-            vwtxPrev.push_back(pcoin->first);
+            txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second));
+            nCredit += pcoin.first->vout[pcoin.second].nValue;
+            vwtxPrev.push_back(pcoin.first);
         }
     }
 
@@ -2007,7 +1984,13 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int
         CTxDB txdb("r");
         if (!txNew.GetCoinAge(txdb, nCoinAge))
             return error("CreateCoinStake : failed to calculate coin age");
-        nCredit += GetProofOfStakeReward(nCoinAge, nBits, txNew.nTime);
+        
+        int64 nReward = GetProofOfStakeReward(nCoinAge, nBits, txNew.nTime);
+        // Refuse to create mint that has zero or negative reward
+        if(nReward <= 0)
+            return false;
+    
+        nCredit += nReward;
     }
 
     int64 nMinFee = 0;
@@ -2082,6 +2065,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
                 coin.MarkSpent(txin.prevout.n);
                 coin.WriteToDisk();
                 NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED);
+                vMintingWalletUpdated.push_back(coin.GetHash());
             }
 
             if (fFileBacked)
@@ -2687,7 +2671,10 @@ void CWallet::UpdatedTransaction(const uint256 &hashTx)
         // Only notify UI if this transaction is in this wallet
         map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(hashTx);
         if (mi != mapWallet.end())
+        {
             NotifyTransactionChanged(this, hashTx, CT_UPDATED);
+            vMintingWalletUpdated.push_back(hashTx);
+        }
     }
 }
 
@@ -2741,3 +2728,21 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64> &mapKeyBirth) const {
     for (std::map<CKeyID, CBlockIndex*>::const_iterator it = mapKeyFirstBlock.begin(); it != mapKeyFirstBlock.end(); it++)
         mapKeyBirth[it->first] = it->second->nTime - 7200; // block times can be 2h off
 }
+
+void CWallet::ClearOrphans()
+{
+    list<uint256> orphans;
+
+    LOCK(cs_wallet);
+    for(map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+    {
+        const CWalletTx *wtx = &(*it).second;
+        if((wtx->IsCoinBase() || wtx->IsCoinStake()) && !wtx->IsInMainChain())
+        {
+            orphans.push_back(wtx->GetHash());
+        }
+    }
+
+    for(list<uint256>::const_iterator it = orphans.begin(); it != orphans.end(); ++it)
+        EraseFromWallet(*it);
+}