// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "txdb.h"
+#include "db.h"
#include "wallet.h"
#include "walletdb.h"
#include "crypter.h"
using namespace std;
extern int nStakeMaxAge;
-
//////////////////////////////////////////////////////////////////////////////
//
// mapWallet
return txOrdered;
}
-void CWallet::WalletUpdateSpent(const CTransaction &tx)
+void CWallet::WalletUpdateSpent(const CTransaction &tx, bool fBlock)
{
// Anytime a signature is successfully verified, it's proof the outpoint is spent.
// Update the wallet spent flag if it doesn't know due to wallet.dat being
}
}
}
+
+ if (fBlock)
+ {
+ uint256 hash = tx.GetHash();
+ map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
+ CWalletTx& wtx = (*mi).second;
+
+ BOOST_FOREACH(const CTxOut& txout, tx.vout)
+ {
+ if (IsMine(txout))
+ {
+ wtx.MarkUnspent(&txout - &tx.vout[0]);
+ wtx.WriteToDisk();
+ NotifyTransactionChanged(this, hash, CT_UPDATED);
+ }
+ }
+ }
}
}
}
else
printf("AddToWallet() : found %s in block %s not in index\n",
- wtxIn.GetHash().ToString().substr(0,10).c_str(),
+ hash.ToString().substr(0,10).c_str(),
wtxIn.hashBlock.ToString().c_str());
}
}
}
//// debug print
- printf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString().substr(0,10).c_str(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : ""));
+ printf("AddToWallet %s %s%s\n", hash.ToString().substr(0,10).c_str(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : ""));
// Write to disk
if (fInsertedNew || fUpdated)
}
#endif
// since AddToWallet is called directly for self-originating transactions, check for consumption of own coins
- WalletUpdateSpent(wtx);
+ WalletUpdateSpent(wtx, (wtxIn.hashBlock != 0));
// Notify UI of new or updated transaction
NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED);
if ( !strCmd.empty())
{
- boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex());
+ boost::replace_all(strCmd, "%s", hash.GetHex());
boost::thread t(runCommand, strCmd); // thread runs free
}
// Add a transaction to the wallet, or update it.
// pblock is optional, but should be provided if the transaction is known to be in a block.
// If fUpdate is true, existing transactions will be updated.
-bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fFindBlock)
+bool CWallet::AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fFindBlock)
{
- uint256 hash = tx.GetHash();
{
LOCK(cs_wallet);
bool fExisted = mapWallet.count(hash);
}
}
-void CWalletTx::AddSupportingTransactions(CTxDB& txdb)
+void CWalletTx::AddSupportingTransactions()
{
vtxPrev.clear();
BOOST_FOREACH(const CTxIn& txin, vin)
vWorkQueue.push_back(txin.prevout.hash);
- // This critsect is OK because txdb is already open
{
LOCK(pwallet->cs_wallet);
map<uint256, const CMerkleTx*> mapWalletPrev;
{
tx = *mapWalletPrev[hash];
}
- else if (!fClient && txdb.ReadDiskTx(hash, tx))
- {
- ;
- }
- else
- {
- printf("ERROR: AddSupportingTransactions() : unsupported transaction\n");
- continue;
- }
int nDepth = tx.SetMerkleBranch();
vtxPrev.push_back(tx);
block.ReadFromDisk(pindex, true);
BOOST_FOREACH(CTransaction& tx, block.vtx)
{
- if (AddToWalletIfInvolvingMe(tx, &block, fUpdate))
+ if (AddToWalletIfInvolvingMe(tx.GetHash(), tx, &block, fUpdate))
ret++;
}
pindex = pindex->pnext;
return ret;
}
-int CWallet::ScanForWalletTransaction(const uint256& hashTx)
-{
- CTransaction tx;
- tx.ReadFromDisk(COutPoint(hashTx, 0));
- if (AddToWalletIfInvolvingMe(tx, NULL, true, true))
- return 1;
- return 0;
-}
-
void CWallet::ReacceptWalletTransactions()
{
- CTxDB txdb("r");
bool fRepeat = true;
while (fRepeat)
{
LOCK(cs_wallet);
fRepeat = false;
- vector<CDiskTxPos> vMissingTx;
+ bool fMissing = false;
BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
{
CWalletTx& wtx = item.second;
if ((wtx.IsCoinBase() && wtx.IsSpent(0)) || (wtx.IsCoinStake() && wtx.IsSpent(1)))
continue;
- CTxIndex txindex;
+ CCoins coins;
bool fUpdated = false;
- if (txdb.ReadTxIndex(wtx.GetHash(), txindex))
+ bool fNotFound = pcoinsTip->GetCoins(wtx.GetHash(), coins);
+ if (!fNotFound || wtx.GetDepthInMainChain() > 0)
{
// Update fSpent if a tx got spent somewhere else by a copy of wallet.dat
- if (txindex.vSpent.size() != wtx.vout.size())
- {
- printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %"PRIszu" != wtx.vout.size() %"PRIszu"\n", txindex.vSpent.size(), wtx.vout.size());
- continue;
- }
- for (unsigned int i = 0; i < txindex.vSpent.size(); i++)
+ for (unsigned int i = 0; i < wtx.vout.size(); i++)
{
if (wtx.IsSpent(i))
continue;
- if (!txindex.vSpent[i].IsNull() && IsMine(wtx.vout[i]))
+ if ((i >= coins.vout.size() || coins.vout[i].IsNull()) && IsMine(wtx.vout[i]))
{
wtx.MarkSpent(i);
fUpdated = true;
- vMissingTx.push_back(txindex.vSpent[i]);
+ fMissing = true;
}
}
if (fUpdated)
{
// Re-accept any txes of ours that aren't already in a block
if (!(wtx.IsCoinBase() || wtx.IsCoinStake()))
- wtx.AcceptWalletTransaction(txdb, false);
+ wtx.AcceptWalletTransaction(false);
}
}
- if (!vMissingTx.empty())
+ if (fMissing)
{
// TODO: optimize this to scan just part of the block chain?
if (ScanForWalletTransactions(pindexGenesisBlock))
}
}
-void CWalletTx::RelayWalletTransaction(CTxDB& txdb)
+void CWalletTx::RelayWalletTransaction()
{
+ CCoinsViewCache& coins = *pcoinsTip;
BOOST_FOREACH(const CMerkleTx& tx, vtxPrev)
{
if (!(tx.IsCoinBase() || tx.IsCoinStake()))
{
uint256 hash = tx.GetHash();
- if (!txdb.ContainsTx(hash))
+ if (!coins.HaveCoins(hash))
RelayTransaction((CTransaction)tx, hash);
}
}
if (!(IsCoinBase() || IsCoinStake()))
{
uint256 hash = GetHash();
- if (!txdb.ContainsTx(hash))
+ if (!coins.HaveCoins(hash))
{
printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str());
RelayTransaction((CTransaction)*this, hash);
}
}
-void CWalletTx::RelayWalletTransaction()
-{
- CTxDB txdb("r");
- RelayWalletTransaction(txdb);
-}
-
void CWallet::ResendWalletTransactions()
{
// Do this infrequently and randomly to avoid giving away
// Rebroadcast any of our txes that aren't in a block yet
printf("ResendWalletTransactions()\n");
- CTxDB txdb("r");
{
LOCK(cs_wallet);
// Sort them in chronological order
BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted)
{
CWalletTx& wtx = *item.second;
- if (wtx.CheckTransaction())
- wtx.RelayWalletTransaction(txdb);
- else
- printf("ResendWalletTransactions() : CheckTransaction failed for transaction %s\n", wtx.GetHash().ToString().c_str());
+ wtx.RelayWalletTransaction();
}
}
}
{
LOCK2(cs_main, cs_wallet);
- // txdb must be opened before the mapWallet lock
- CTxDB txdb("r");
{
nFeeRet = nTransactionFee;
while (true)
nFeeRet += nMoveToFee;
}
- // ppcoin: sub-cent change is moved to fee
+ // sub-cent change is moved to fee
if (nChange > 0 && nChange < MIN_TXOUT_AMOUNT)
{
nFeeRet += nChange;
// Check that enough fee is included
int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000);
- int64 nMinFee = wtxNew.GetMinFee(1, false, GMF_SEND, nBytes);
-
+ bool fAllowFree = CTransaction::AllowFree(dPriority);
+ int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree, GMF_SEND);
if (nFeeRet < max(nPayFee, nMinFee))
{
nFeeRet = max(nPayFee, nMinFee);
}
// Fill vtxPrev by copying from previous transactions vtxPrev
- wtxNew.AddSupportingTransactions(txdb);
+ wtxNew.AddSupportingTransactions();
wtxNew.fTimeReceivedIsTxTime = true;
break;
return false;
vector<const CWalletTx*> vwtxPrev;
-
-/*
- * TODO: performance comparison
-
- static set<pair<const CWalletTx*,unsigned int> > setCoins;
- static uint256 hashPrevBlock;
- static int64 nValueIn = 0;
-
- // Cache outputs unless best block changed
- if (hashPrevBlock != pindexBest->GetBlockHash())
- {
- if (!SelectCoinsSimple(nBalance - nReserveBalance, GetAdjustedTime(), nCoinbaseMaturity * 10, setCoins, nValueIn))
- return false;
-
- if (setCoins.empty())
- return false;
-
- hashPrevBlock == pindexBest->GetBlockHash();
- }
-*/
-
set<pair<const CWalletTx*,unsigned int> > setCoins;
int64 nValueIn = 0;
if (setCoins.empty())
return false;
- CTxDB txdb("r");
+ CCoinsViewCache &view = *pcoinsTip;
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
{
- CTxIndex txindex;
+ CCoins coins;
{
LOCK2(cs_main, cs_wallet);
- if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex))
+ if (!view.GetCoinsReadOnly(pcoin.first->GetHash(), coins))
continue;
}
// The following combine threshold is important to security
// Should not be adjusted if you don't understand the consequences
int64 nCombineThreshold = GetProofOfWorkReward(GetLastBlockIndex(pindexBest, false)->nBits) / 3;
-
+ CBlockIndex* pindexPrev = pindexBest;
CBigNum bnTargetPerCoinDay;
bnTargetPerCoinDay.SetCompact(nBits);
return false;
vector<const CWalletTx*> vwtxPrev;
-
-/*
- * TODO: performance comparison
-
- static set<pair<const CWalletTx*,unsigned int> > setCoins;
- static uint256 hashPrevBlock;
- static int64 nValueIn = 0;
-
- // Cache outputs unless best block changed
- if (hashPrevBlock != pindexBest->GetBlockHash())
- {
- if (!SelectCoinsSimple(nBalance - nReserveBalance, txNew.nTime, nCoinbaseMaturity * 10, setCoins, nValueIn))
- return false;
-
- if (setCoins.empty())
- return false;
-
- hashPrevBlock == pindexBest->GetBlockHash();
- }
-*/
-
set<pair<const CWalletTx*,unsigned int> > setCoins;
int64 nValueIn = 0;
int64 nCredit = 0;
CScript scriptPubKeyKernel;
- CTxDB txdb("r");
+
+ CCoinsViewCache &view = *pcoinsTip;
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
{
- CTxIndex txindex;
+ CCoins coins;
{
LOCK2(cs_main, cs_wallet);
- if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex))
+ if (!view.GetCoinsReadOnly(pcoin.first->GetHash(), coins))
continue;
}
+ static int nMaxStakeSearchInterval = 60;
+ if (coins.nBlockTime + nStakeMinAge > txNew.nTime - nMaxStakeSearchInterval)
+ continue; // only count coins meeting min age requirement
+
// Read block header
CBlock block;
+ unsigned int nTxPos = 0;
{
LOCK2(cs_main, cs_wallet);
- if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
+ CBlockIndex *pindex = FindBlockByHeight(coins.nHeight);
+
+ if (!block.ReadFromDisk(pindex))
continue;
+
+ BOOST_FOREACH(const CTransaction &tx, block.vtx) {
+ if (tx.GetHash() == pcoin.first->GetHash()) {
+ break;
+ }
+ nTxPos += tx.GetSerializeSize(SER_DISK, CLIENT_VERSION);
+ }
+ nTxPos += GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(block.vtx.size());
}
- static int nMaxStakeSearchInterval = 60;
- if (block.GetBlockTime() + nStakeMinAge > txNew.nTime - nMaxStakeSearchInterval)
- continue; // only count coins meeting min age requirement
+ bool fFatal = false;
bool fKernelFound = false;
- for (unsigned int n=0; n<min(nSearchInterval,(int64)nMaxStakeSearchInterval) && !fKernelFound && !fShutdown; n++)
+ for (unsigned int n=0; n<min(nSearchInterval,(int64)nMaxStakeSearchInterval) && !fKernelFound && !fShutdown && pindexPrev == pindexBest; n++)
{
// Search backward in time from the given txNew timestamp
// Search nSearchInterval seconds back up to nMaxStakeSearchInterval
uint256 hashProofOfStake = 0, targetProofOfStake = 0;
COutPoint prevoutStake = COutPoint(pcoin.first->GetHash(), pcoin.second);
- if (CheckStakeKernelHash(nBits, block, txindex.pos.nTxPos - txindex.pos.nBlockPos, *pcoin.first, prevoutStake, txNew.nTime - n, hashProofOfStake, targetProofOfStake))
+ if (CheckStakeKernelHash(nBits, block, nTxPos, *pcoin.first, prevoutStake, txNew.nTime - n, hashProofOfStake, targetProofOfStake, fFatal, true))
{
// Found a kernel
if (fDebug && GetBoolArg("-printcoinstake"))
// Calculate coin age reward
{
uint64 nCoinAge;
- CTxDB txdb("r");
- if (!txNew.GetCoinAge(txdb, nCoinAge))
+
+ if (!txNew.GetCoinAge(nCoinAge))
return error("CreateCoinStake : failed to calculate coin age");
nCredit += GetProofOfStakeReward(nCoinAge, nBits, txNew.nTime);
}
return ret;
}
-// ppcoin: check 'spent' consistency between wallet and txindex
-// ppcoin: fix wallet spent state according to txindex
+// 1. check 'spent' consistency between wallet and coins database
+// 2. fix wallet spent state according to coins database
+// 3. remove orphaned coinstakes and coinbases from wallet
void CWallet::FixSpentCoins(int& nMismatchFound, int64& nBalanceInQuestion, bool fCheckOnly)
{
nMismatchFound = 0;
for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
vCoins.push_back(&(*it).second);
- CTxDB txdb("r");
+ CCoinsViewCache &view = *pcoinsTip;
BOOST_FOREACH(CWalletTx* pcoin, vCoins)
{
- // Find the corresponding transaction index
- CTxIndex txindex;
- if (!txdb.ReadTxIndex(pcoin->GetHash(), txindex))
+ uint256 hash = pcoin->GetHash();
+ if(!view.HaveCoins(hash))
continue;
+
+ // Find the corresponding transaction index
+ CCoins &coins = view.GetCoins(hash);
+
for (unsigned int n=0; n < pcoin->vout.size(); n++)
{
- if (IsMine(pcoin->vout[n]) && pcoin->IsSpent(n) && (txindex.vSpent.size() <= n || txindex.vSpent[n].IsNull()))
+ bool fUpdated = false;
+ if (IsMine(pcoin->vout[n]))
{
- printf("FixSpentCoins found lost coin %sppc %s[%d], %s\n",
- FormatMoney(pcoin->vout[n].nValue).c_str(), pcoin->GetHash().ToString().c_str(), n, fCheckOnly? "repair not attempted" : "repairing");
- nMismatchFound++;
- nBalanceInQuestion += pcoin->vout[n].nValue;
- if (!fCheckOnly)
+ if (pcoin->IsSpent(n) && coins.IsAvailable(n))
{
- pcoin->MarkUnspent(n);
- pcoin->WriteToDisk();
+ printf("FixSpentCoins found lost coin %snvc %s[%d], %s\n",
+ FormatMoney(pcoin->vout[n].nValue).c_str(), hash.ToString().c_str(), n, fCheckOnly? "repair not attempted" : "repairing");
+ nMismatchFound++;
+ nBalanceInQuestion += pcoin->vout[n].nValue;
+ if (!fCheckOnly)
+ {
+ fUpdated = true;
+ pcoin->MarkUnspent(n);
+ pcoin->WriteToDisk();
+ }
}
- }
- else if (IsMine(pcoin->vout[n]) && !pcoin->IsSpent(n) && (txindex.vSpent.size() > n && !txindex.vSpent[n].IsNull()))
- {
- printf("FixSpentCoins found spent coin %sppc %s[%d], %s\n",
- FormatMoney(pcoin->vout[n].nValue).c_str(), pcoin->GetHash().ToString().c_str(), n, fCheckOnly? "repair not attempted" : "repairing");
- nMismatchFound++;
- nBalanceInQuestion += pcoin->vout[n].nValue;
- if (!fCheckOnly)
+ else if (!pcoin->IsSpent(n) && !coins.IsAvailable(n))
{
- pcoin->MarkSpent(n);
- pcoin->WriteToDisk();
+ printf("FixSpentCoins found spent coin %snvc %s[%d], %s\n",
+ FormatMoney(pcoin->vout[n].nValue).c_str(), hash.ToString().c_str(), n, fCheckOnly? "repair not attempted" : "repairing");
+ nMismatchFound++;
+ nBalanceInQuestion += pcoin->vout[n].nValue;
+ if (!fCheckOnly)
+ {
+ fUpdated = true;
+ pcoin->MarkSpent(n);
+ pcoin->WriteToDisk();
+ }
}
+
+ if (fUpdated)
+ NotifyTransactionChanged(this, hash, CT_UPDATED);
+ }
+ }
+
+ if((pcoin->IsCoinBase() || pcoin->IsCoinStake()) && pcoin->GetDepthInMainChain() == 0)
+ {
+ if (!fCheckOnly)
+ {
+ EraseFromWallet(hash);
+ NotifyTransactionChanged(this, hash, CT_DELETED);
}
+
+ printf("FixSpentCoins %s orphaned generation tx %s\n", fCheckOnly ? "found" : "removed", hash.ToString().c_str());
}
}
}