PPCoin: Separate newmint value from stake in RPC 'getinfo' output
[novacoin.git] / src / wallet.cpp
index 564362b..c245f84 100644 (file)
@@ -7,6 +7,7 @@
 #include "headers.h"
 #include "db.h"
 #include "crypter.h"
+#include "checkpoints.h"
 
 using namespace std;
 
@@ -771,13 +772,29 @@ int64 CWallet::GetStake() const
         for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
         {
             const CWalletTx* pcoin = &(*it).second;
-            if ((pcoin->IsCoinBase() || pcoin->IsCoinStake()) && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0)
+            if (pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0)
                 nTotal += CWallet::GetCredit(*pcoin);
         }
     }
     return nTotal;
 }
 
+int64 CWallet::GetNewMint() const
+{
+    int64 nTotal = 0;
+    CRITICAL_BLOCK(cs_wallet)
+    {
+        for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+        {
+            const CWalletTx* pcoin = &(*it).second;
+            if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0)
+                nTotal += CWallet::GetCredit(*pcoin);
+        }
+    }
+    return nTotal;
+}
+
+
 bool CWallet::SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
 {
     setCoinsRet.clear();
@@ -1061,8 +1078,11 @@ bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& w
 }
 
 // ppcoin: create coin stake transaction
-bool CWallet::CreateCoinStake(CScript scriptPubKey, CTransaction& txNew)
+bool CWallet::CreateCoinStake(unsigned int nBits, CTransaction& txNew)
 {
+    CBigNum bnTargetPerCoinDay;
+    bnTargetPerCoinDay.SetCompact(nBits);
+
     CRITICAL_BLOCK(cs_main)
     CRITICAL_BLOCK(cs_wallet)
     {
@@ -1077,6 +1097,7 @@ bool CWallet::CreateCoinStake(CScript scriptPubKey, CTransaction& txNew)
         if (nBalance <= nBalanceReserve)
             return false;
         set<pair<const CWalletTx*,unsigned int> > setCoins;
+        vector<const CWalletTx*> vwtxPrev;
         int64 nValueIn = 0;
         if (!SelectCoins(nBalance - nBalanceReserve, txNew.nTime, setCoins, nValueIn))
             return false;
@@ -1085,39 +1106,63 @@ bool CWallet::CreateCoinStake(CScript scriptPubKey, CTransaction& txNew)
         int64 nCredit = 0;
         BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
         {
-            nCredit += pcoin.first->vout[pcoin.second].nValue;
-            // Only spend one tx for now
-            break;
+            CTxDB txdb("r");
+            CTxIndex txindex;
+            if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex))
+                continue;
+
+            // Read block header
+            CBlock block;
+            if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
+                continue;
+            if (block.GetBlockTime() + AUTO_CHECKPOINT_TRUST_SPAN > txNew.nTime)
+                continue; // only count coins from at least one week ago
+
+            int64 nValueIn = pcoin.first->vout[pcoin.second].nValue;
+            CBigNum bnCoinDay = CBigNum(nValueIn) * (txNew.nTime-pcoin.first->nTime) / COIN / (24 * 60 * 60);
+            // Calculate hash
+            CDataStream ss(SER_GETHASH, VERSION);
+            ss << nBits << block.nTime << (txindex.pos.nTxPos - txindex.pos.nBlockPos) << pcoin.first->nTime << pcoin.second << txNew.nTime;
+            if (CBigNum(Hash(ss.begin(), ss.end())) <= bnCoinDay * bnTargetPerCoinDay)
+            {
+                txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second));
+                nCredit += pcoin.first->vout[pcoin.second].nValue;
+                vwtxPrev.push_back(pcoin.first);
+                // Set output scriptPubKey
+                txNew.vout.push_back(CTxOut(0, pcoin.first->vout[pcoin.second].scriptPubKey));
+                break;
+            }
         }
-        if (nCredit > nBalance - nBalanceReserve)
+        if (nCredit == 0 || nCredit > nBalance - nBalanceReserve)
             return false;
-        // Fill vin
-        BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
+        BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
         {
-            txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second));
-            // Only spend one tx for now
-            break;
+            if (pcoin.first->vout[pcoin.second].scriptPubKey == txNew.vout[1].scriptPubKey && pcoin.first->GetHash() != txNew.vin[0].prevout.hash)
+            {
+                if (nCredit + pcoin.first->vout[pcoin.second].nValue > nBalance - nBalanceReserve)
+                    break;
+                txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second));
+                nCredit += pcoin.first->vout[pcoin.second].nValue;
+                vwtxPrev.push_back(pcoin.first);
+            }
         }
         // Calculate coin age reward
         {
             uint64 nCoinAge;
             CTxDB txdb("r");
             if (!txNew.GetCoinAge(txdb, nCoinAge))
-                return false;
+                return error("CreateCoinStake : failed to calculate coin age");
             nCredit += GetProofOfStakeReward(nCoinAge);
         }
-        // Fill vout
-        txNew.vout.push_back(CTxOut(nCredit, scriptPubKey));
-
+        // Set output amount
+        txNew.vout[1].nValue = nCredit;
 
         // Sign
         int nIn = 0;
-        BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
+        BOOST_FOREACH(const CWalletTx* pcoin, vwtxPrev)
         {
-            if (!SignSignature(*this, *coin.first, txNew, nIn++))
-                return false;
-            // Only spend one tx for now
-            break;
+            if (!SignSignature(*this, *pcoin, txNew, nIn++))
+                return error("CreateCoinStake : failed to sign coinstake");
         }
     }
     return true;
@@ -1471,6 +1516,110 @@ int64 CWallet::GetOldestKeyPoolTime()
     return keypool.nTime;
 }
 
+// ppcoin: check 'spent' consistency between wallet and txindex
+bool CWallet::CheckSpentCoins(int& nMismatchFound, int64& nBalanceInQuestion)
+{
+    nMismatchFound = 0;
+    nBalanceInQuestion = 0;
+    CRITICAL_BLOCK(cs_wallet)
+    {
+       vector<const CWalletTx*> vCoins;
+       vCoins.reserve(mapWallet.size());
+       for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+           vCoins.push_back(&(*it).second);
+
+       CTxDB txdb("r");
+       BOOST_FOREACH(const CWalletTx* pcoin, vCoins)
+       {
+           // Find the corresponding transaction index
+           CTxIndex txindex;
+           if (!txdb.ReadTxIndex(pcoin->GetHash(), txindex))
+               continue;
+           for (int n=0; n < pcoin->vout.size(); n++)
+           {
+               if (pcoin->IsSpent(n) && (txindex.vSpent.size() <= n || txindex.vSpent[n].IsNull()))
+               {
+                   printf("CheckSpentCoins found lost coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n);
+                   nMismatchFound++;
+                   nBalanceInQuestion += pcoin->vout[n].nValue;
+               }
+               else if (!pcoin->IsSpent(n) && (txindex.vSpent.size() > n && !txindex.vSpent[n].IsNull()))
+               {
+                   printf("CheckSpentCoins found spent coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n);
+                   nMismatchFound++;
+                   nBalanceInQuestion += pcoin->vout[n].nValue;
+               }
+           }
+       }
+    }
+    return (nMismatchFound == 0);
+}
+
+// ppcoin: fix wallet spent state according to txindex
+void CWallet::FixSpentCoins(int& nMismatchFound, int64& nBalanceInQuestion)
+{
+    nMismatchFound = 0;
+    nBalanceInQuestion = 0;
+    CRITICAL_BLOCK(cs_wallet)
+    {
+       vector<CWalletTx*> vCoins;
+       vCoins.reserve(mapWallet.size());
+       for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+           vCoins.push_back(&(*it).second);
+
+       CTxDB txdb("r");
+       BOOST_FOREACH(CWalletTx* pcoin, vCoins)
+       {
+           // Find the corresponding transaction index
+           CTxIndex txindex;
+           if (!txdb.ReadTxIndex(pcoin->GetHash(), txindex))
+               continue;
+           for (int n=0; n < pcoin->vout.size(); n++)
+           {
+               if (pcoin->IsSpent(n) && (txindex.vSpent.size() <= n || txindex.vSpent[n].IsNull()))
+               {
+                   printf("FixSpentCoins found lost coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n);
+                   nMismatchFound++;
+                   nBalanceInQuestion += pcoin->vout[n].nValue;
+                   pcoin->MarkUnspent(n);
+                   pcoin->WriteToDisk();
+               }
+               else if (!pcoin->IsSpent(n) && (txindex.vSpent.size() > n && !txindex.vSpent[n].IsNull()))
+               {
+                   printf("FixSpentCoins found spent coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n);
+                   nMismatchFound++;
+                   nBalanceInQuestion += pcoin->vout[n].nValue;
+                   pcoin->MarkSpent(n);
+                   pcoin->WriteToDisk();
+               }
+           }
+       }
+    }
+}
+
+// ppcoin: disable transaction (only for coinstake)
+void CWallet::DisableTransaction(const CTransaction &tx)
+{
+    if (!tx.IsCoinStake() || !IsFromMe(tx))
+        return; // only disconnecting coinstake requires marking input unspent
+    CRITICAL_BLOCK(cs_wallet)
+    {
+        BOOST_FOREACH(const CTxIn& txin, tx.vin)
+        {
+            map<uint256, CWalletTx>::iterator mi = mapWallet.find(txin.prevout.hash);
+            if (mi != mapWallet.end())
+            {
+                CWalletTx& prev = (*mi).second;
+                if (txin.prevout.n < prev.vout.size() && IsMine(prev.vout[txin.prevout.n]))
+                {
+                    prev.MarkUnspent(txin.prevout.n);
+                    prev.WriteToDisk();
+                }
+            }
+        }
+    }
+}
+
 vector<unsigned char> CReserveKey::GetReservedKey()
 {
     if (nIndex == -1)