diff options
author | Gavin Andresen <gavinandresen@gmail.com> | 2014-02-15 16:38:28 -0500 |
---|---|---|
committer | Gavin Andresen <gavinandresen@gmail.com> | 2014-02-26 11:53:51 -0500 |
commit | 93a18a3650292afbb441a47d1fa1b94aeb0164e3 (patch) | |
tree | 36382e2077820d469fd1257ee35375a86206a878 /src | |
parent | a16ad1c0f465935d437bd9ae9875b28be49ec65b (diff) |
Remove CWalletTx::vfSpent
Use the spent outpoint multimap to figure out which wallet transaction
outputs are unspent, instead of a vfSpent array that is saved
to disk.
Diffstat (limited to 'src')
-rw-r--r-- | src/main.cpp | 25 | ||||
-rw-r--r-- | src/qt/coincontroldialog.cpp | 9 | ||||
-rw-r--r-- | src/qt/walletmodel.cpp | 6 | ||||
-rw-r--r-- | src/qt/walletmodel.h | 1 | ||||
-rw-r--r-- | src/rpcdump.cpp | 2 | ||||
-rw-r--r-- | src/rpcwallet.cpp | 7 | ||||
-rw-r--r-- | src/txmempool.cpp | 17 | ||||
-rw-r--r-- | src/txmempool.h | 6 | ||||
-rw-r--r-- | src/wallet.cpp | 270 | ||||
-rw-r--r-- | src/wallet.h | 125 |
10 files changed, 159 insertions, 309 deletions
diff --git a/src/main.cpp b/src/main.cpp index 53b99101d9..b38973b18e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1866,17 +1866,23 @@ bool static DisconnectTip(CValidationState &state) { // Write the chain state to disk, if necessary. if (!WriteChainState(state)) return false; - // Ressurect mempool transactions from the disconnected block. + // Resurrect mempool transactions from the disconnected block. BOOST_FOREACH(const CTransaction &tx, block.vtx) { // ignore validation errors in resurrected transactions + list<CTransaction> removed; CValidationState stateDummy; if (!tx.IsCoinBase()) if (!AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) - mempool.remove(tx, true); + mempool.remove(tx, removed, true); } mempool.check(pcoinsTip); // Update chainActive and related variables. UpdateTip(pindexDelete->pprev); + // Let wallets know transactions went from 1-confirmed to + // 0-confirmed or conflicted: + BOOST_FOREACH(const CTransaction &tx, block.vtx) { + SyncWithWallets(tx.GetHash(), tx, NULL); + } return true; } @@ -1907,13 +1913,24 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew) { if (!WriteChainState(state)) return false; // Remove conflicting transactions from the mempool. + list<CTransaction> txConflicted; BOOST_FOREACH(const CTransaction &tx, block.vtx) { - mempool.remove(tx); - mempool.removeConflicts(tx); + list<CTransaction> unused; + mempool.remove(tx, unused); + mempool.removeConflicts(tx, txConflicted); } mempool.check(pcoinsTip); // Update chainActive & related variables. UpdateTip(pindexNew); + // Tell wallet about transactions that went from mempool + // to conflicted: + BOOST_FOREACH(const CTransaction &tx, txConflicted) { + SyncWithWallets(tx.GetHash(), tx, NULL); + } + // ... and about transactions that got confirmed: + BOOST_FOREACH(const CTransaction &tx, block.vtx) { + SyncWithWallets(tx.GetHash(), tx, &block); + } return true; } diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index e1a9140f45..2d8fcd7a52 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -468,11 +468,12 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) BOOST_FOREACH(const COutput& out, vOutputs) { - // unselect already spent, very unlikely scenario, this could happen when selected are spent elsewhere, like rpc or another computer - if (out.tx->IsSpent(out.i)) + // unselect already spent, very unlikely scenario, this could happen + // when selected are spent elsewhere, like rpc or another computer + uint256 txhash = out.tx->GetHash(); + COutPoint outpt(txhash, out.i); + if (model->isSpent(outpt)) { - uint256 txhash = out.tx->GetHash(); - COutPoint outpt(txhash, out.i); coinControl->UnSelect(outpt); continue; } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 3549cd49f0..eae448fee4 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -501,6 +501,12 @@ void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vect } } +bool WalletModel::isSpent(const COutPoint& outpoint) const +{ + LOCK(wallet->cs_wallet); + return wallet->IsSpent(outpoint.hash, outpoint.n); +} + // AvailableCoins + LockedCoins grouped by wallet address (put change in one group with wallet address) void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const { diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 91a6fba222..28a9169e27 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -180,6 +180,7 @@ public: bool getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; void getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs); + bool isSpent(const COutPoint& outpoint) const; void listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const; bool isLockedCoin(uint256 hash, unsigned int n) const; diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp index 9e1d47846e..635d4ac19b 100644 --- a/src/rpcdump.cpp +++ b/src/rpcdump.cpp @@ -128,7 +128,6 @@ Value importprivkey(const Array& params, bool fHelp) if (fRescan) { pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true); - pwalletMain->ReacceptWalletTransactions(); } } @@ -216,7 +215,6 @@ Value importwallet(const Array& params, bool fHelp) LogPrintf("Rescanning last %i blocks\n", chainActive.Height() - pindex->nHeight + 1); pwalletMain->ScanForWalletTransactions(pindex); - pwalletMain->ReacceptWalletTransactions(); pwalletMain->MarkDirty(); if (!fGood) diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 7b605af589..d3b6c349a7 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -560,7 +560,7 @@ int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMi for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (!IsFinalTx(wtx)) + if (!IsFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) continue; int64_t nReceived, nSent, nFee; @@ -1324,13 +1324,14 @@ Value listaccounts(const Array& params, bool fHelp) string strSentAccount; list<pair<CTxDestination, int64_t> > listReceived; list<pair<CTxDestination, int64_t> > listSent; - if (wtx.GetBlocksToMaturity() > 0) + int nDepth = wtx.GetDepthInMainChain(); + if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0) continue; wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount); mapAccountBalances[strSentAccount] -= nFee; BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent) mapAccountBalances[strSentAccount] -= s.second; - if (wtx.GetDepthInMainChain() >= nMinDepth) + if (nDepth >= nMinDepth) { BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived) if (pwalletMain->mapAddressBook.count(r.first)) diff --git a/src/txmempool.cpp b/src/txmempool.cpp index be251d1d64..64c9eac73d 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -86,7 +86,7 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry) } -bool CTxMemPool::remove(const CTransaction &tx, bool fRecursive) +void CTxMemPool::remove(const CTransaction &tx, std::list<CTransaction>& removed, bool fRecursive) { // Remove transaction from memory pool { @@ -95,34 +95,37 @@ bool CTxMemPool::remove(const CTransaction &tx, bool fRecursive) if (fRecursive) { for (unsigned int i = 0; i < tx.vout.size(); i++) { std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(COutPoint(hash, i)); - if (it != mapNextTx.end()) - remove(*it->second.ptx, true); + if (it == mapNextTx.end()) + continue; + remove(*it->second.ptx, removed, true); } } if (mapTx.count(hash)) { + removed.push_front(tx); BOOST_FOREACH(const CTxIn& txin, tx.vin) mapNextTx.erase(txin.prevout); mapTx.erase(hash); nTransactionsUpdated++; } } - return true; } -bool CTxMemPool::removeConflicts(const CTransaction &tx) +void CTxMemPool::removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed) { // Remove transactions which depend on inputs of tx, recursively + list<CTransaction> result; LOCK(cs); BOOST_FOREACH(const CTxIn &txin, tx.vin) { std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(txin.prevout); if (it != mapNextTx.end()) { const CTransaction &txConflict = *it->second.ptx; if (txConflict != tx) - remove(txConflict, true); + { + remove(txConflict, removed, true); + } } } - return true; } void CTxMemPool::clear() diff --git a/src/txmempool.h b/src/txmempool.h index a652c424a4..4509e95778 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -5,6 +5,8 @@ #ifndef BITCOIN_TXMEMPOOL_H #define BITCOIN_TXMEMPOOL_H +#include <list> + #include "coins.h" #include "core.h" #include "sync.h" @@ -72,8 +74,8 @@ public: void setSanityCheck(bool _fSanityCheck) { fSanityCheck = _fSanityCheck; } bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry); - bool remove(const CTransaction &tx, bool fRecursive = false); - bool removeConflicts(const CTransaction &tx); + void remove(const CTransaction &tx, std::list<CTransaction>& removed, bool fRecursive = false); + void removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed); void clear(); void queryHashes(std::vector<uint256>& vtxid); void pruneSpent(const uint256& hash, CCoins &coins); diff --git a/src/wallet.cpp b/src/wallet.cpp index 5e24738b54..2514d24cc0 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -32,6 +32,15 @@ struct CompareValueOnly } }; +const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const +{ + LOCK(cs_wallet); + std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(hash); + if (it == mapWallet.end()) + return NULL; + return &(it->second); +} + CPubKey CWallet::GenerateNewKey() { AssertLockHeld(cs_wallet); // mapKeyMetadata @@ -239,18 +248,20 @@ set<uint256> CWallet::GetConflicts(const uint256& txid) const return result; const CWalletTx& wtx = it->second; - std::pair<TxConflicts::const_iterator, TxConflicts::const_iterator> range; + std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range; BOOST_FOREACH(const CTxIn& txin, wtx.vin) { - range = mapTxConflicts.equal_range(txin.prevout); - for (TxConflicts::const_iterator it = range.first; it != range.second; ++it) + if (mapTxSpends.count(txin.prevout) <= 1) + continue; // No conflict if zero or one spends + range = mapTxSpends.equal_range(txin.prevout); + for (TxSpends::const_iterator it = range.first; it != range.second; ++it) result.insert(it->second); } return result; } -void CWallet::SyncMetaData(pair<TxConflicts::iterator, TxConflicts::iterator> range) +void CWallet::SyncMetaData(pair<TxSpends::iterator, TxSpends::iterator> range) { // We want all the wallet transactions in range to have the same metadata as // the oldest (smallest nOrderPos). @@ -258,7 +269,7 @@ void CWallet::SyncMetaData(pair<TxConflicts::iterator, TxConflicts::iterator> ra int nMinOrderPos = std::numeric_limits<int>::max(); const CWalletTx* copyFrom = NULL; - for (TxConflicts::iterator it = range.first; it != range.second; ++it) + for (TxSpends::iterator it = range.first; it != range.second; ++it) { const uint256& hash = it->second; int n = mapWallet[hash].nOrderPos; @@ -269,7 +280,7 @@ void CWallet::SyncMetaData(pair<TxConflicts::iterator, TxConflicts::iterator> ra } } // Now copy data from copyFrom to rest: - for (TxConflicts::iterator it = range.first; it != range.second; ++it) + for (TxSpends::iterator it = range.first; it != range.second; ++it) { const uint256& hash = it->second; CWalletTx* copyTo = &mapWallet[hash]; @@ -281,28 +292,48 @@ void CWallet::SyncMetaData(pair<TxConflicts::iterator, TxConflicts::iterator> ra copyTo->nTimeSmart = copyFrom->nTimeSmart; copyTo->fFromMe = copyFrom->fFromMe; copyTo->strFromAccount = copyFrom->strFromAccount; - // vfSpent not copied on purpose // nOrderPos not copied on purpose // cached members not copied on purpose } } -void CWallet::AddToConflicts(const uint256& wtxhash) +// Outpoint is spent if any non-conflicted transaction +// spends it: +bool CWallet::IsSpent(const uint256& hash, unsigned int n) const { - assert(mapWallet.count(wtxhash)); - CWalletTx& thisTx = mapWallet[wtxhash]; - if (thisTx.IsCoinBase()) - return; + const COutPoint outpoint(hash, n); + pair<TxSpends::const_iterator, TxSpends::const_iterator> range; + range = mapTxSpends.equal_range(outpoint); - BOOST_FOREACH(const CTxIn& txin, thisTx.vin) + for (TxSpends::const_iterator it = range.first; it != range.second; ++it) { - mapTxConflicts.insert(make_pair(txin.prevout, wtxhash)); - - pair<TxConflicts::iterator, TxConflicts::iterator> range; - range = mapTxConflicts.equal_range(txin.prevout); - if (range.first != range.second) - SyncMetaData(range); + const uint256& wtxid = it->second; + std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid); + if (mit != mapWallet.end() && mit->second.GetDepthInMainChain() >= 0) + return true; // Spent } + return false; +} + +void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid) +{ + mapTxSpends.insert(make_pair(outpoint, wtxid)); + + pair<TxSpends::iterator, TxSpends::iterator> range; + range = mapTxSpends.equal_range(outpoint); + SyncMetaData(range); +} + + +void CWallet::AddToSpends(const uint256& wtxid) +{ + assert(mapWallet.count(wtxid)); + CWalletTx& thisTx = mapWallet[wtxid]; + if (thisTx.IsCoinBase()) // Coinbases don't spend anything! + return; + + BOOST_FOREACH(const CTxIn& txin, thisTx.vin) + AddToSpends(txin.prevout, wtxid); } bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) @@ -423,33 +454,6 @@ CWallet::TxItems CWallet::OrderedTxItems(std::list<CAccountingEntry>& acentries, return txOrdered; } -void CWallet::WalletUpdateSpent(const CTransaction &tx) -{ - // 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 - // restored from backup or the user making copies of wallet.dat. - { - LOCK(cs_wallet); - BOOST_FOREACH(const CTxIn& txin, tx.vin) - { - map<uint256, CWalletTx>::iterator mi = mapWallet.find(txin.prevout.hash); - if (mi != mapWallet.end()) - { - CWalletTx& wtx = (*mi).second; - if (txin.prevout.n >= wtx.vout.size()) - LogPrintf("WalletUpdateSpent: bad wtx %s\n", wtx.GetHash().ToString()); - else if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n])) - { - LogPrintf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()), wtx.GetHash().ToString()); - wtx.MarkSpent(txin.prevout.n); - wtx.WriteToDisk(); - NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED); - } - } - } - } -} - void CWallet::MarkDirty() { { @@ -466,7 +470,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet) if (fFromLoadWallet) { mapWallet[hash] = wtxIn; - AddToConflicts(hash); + AddToSpends(hash); } else { @@ -526,7 +530,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet) wtxIn.GetHash().ToString(), wtxIn.hashBlock.ToString()); } - AddToConflicts(hash); + AddToSpends(hash); } bool fUpdated = false; @@ -549,7 +553,6 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet) wtx.fFromMe = wtxIn.fFromMe; fUpdated = true; } - fUpdated |= wtx.UpdateSpent(wtxIn.vfSpent); } //// debug print @@ -560,8 +563,8 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet) if (!wtx.WriteToDisk()) return false; - // since AddToWallet is called directly for self-originating transactions, check for consumption of own coins - WalletUpdateSpent(wtx); + // Break debit/credit balance caches: + wtx.MarkDirty(); // Notify UI of new or updated transaction NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); @@ -596,14 +599,25 @@ bool CWallet::AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& wtx.SetMerkleBranch(pblock); return AddToWallet(wtx); } - else - WalletUpdateSpent(tx); } return false; } -void CWallet::SyncTransaction(const uint256 &hash, const CTransaction& tx, const CBlock* pblock) { +void CWallet::SyncTransaction(const uint256 &hash, const CTransaction& tx, const CBlock* pblock) +{ AddToWalletIfInvolvingMe(hash, tx, pblock, true); + + if (mapWallet.count(hash) == 0) + return; // Not one of ours + + // If a transaction changes 'conflicted' state, that changes the balance + // available of the outputs it spends. So force those to be + // recomputed, also: + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + if (mapWallet.count(txin.prevout.hash)) + mapWallet[txin.prevout.hash].MarkDirty(); + } } void CWallet::EraseFromWallet(const uint256 &hash) @@ -804,78 +818,6 @@ void CWalletTx::GetAccountAmounts(const string& strAccount, int64_t& nReceived, } } -void CWalletTx::AddSupportingTransactions() -{ - vtxPrev.clear(); - - const int COPY_DEPTH = 3; - if (SetMerkleBranch() < COPY_DEPTH) - { - vector<uint256> vWorkQueue; - BOOST_FOREACH(const CTxIn& txin, vin) - vWorkQueue.push_back(txin.prevout.hash); - - { - LOCK(pwallet->cs_wallet); - map<uint256, const CMerkleTx*> mapWalletPrev; - set<uint256> setAlreadyDone; - for (unsigned int i = 0; i < vWorkQueue.size(); i++) - { - uint256 hash = vWorkQueue[i]; - if (setAlreadyDone.count(hash)) - continue; - setAlreadyDone.insert(hash); - - CMerkleTx tx; - map<uint256, CWalletTx>::const_iterator mi = pwallet->mapWallet.find(hash); - if (mi != pwallet->mapWallet.end()) - { - tx = (*mi).second; - BOOST_FOREACH(const CMerkleTx& txWalletPrev, (*mi).second.vtxPrev) - mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev; - } - else if (mapWalletPrev.count(hash)) - { - tx = *mapWalletPrev[hash]; - } - else - { - continue; - } - - int nDepth = tx.SetMerkleBranch(); - vtxPrev.push_back(tx); - - if (nDepth < COPY_DEPTH) - { - BOOST_FOREACH(const CTxIn& txin, tx.vin) - vWorkQueue.push_back(txin.prevout.hash); - } - } - } - } - - reverse(vtxPrev.begin(), vtxPrev.end()); -} - -bool CWalletTx::AcceptWalletTransaction() -{ - { - LOCK(mempool.cs); - // Add previous supporting transactions first - BOOST_FOREACH(CMerkleTx& tx, vtxPrev) - { - if (!tx.IsCoinBase()) - { - uint256 hash = tx.GetHash(); - if (!mempool.exists(hash) && pcoinsTip->HaveCoins(hash)) - tx.AcceptToMemoryPool(false); - } - } - return AcceptToMemoryPool(false); - } - return false; -} bool CWalletTx::WriteToDisk() { @@ -916,69 +858,26 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) void CWallet::ReacceptWalletTransactions() { - bool fRepeat = true; - while (fRepeat) + LOCK(cs_wallet); + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) { - LOCK(cs_wallet); - fRepeat = false; - bool fMissing = false; - BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) - { - CWalletTx& wtx = item.second; - if (wtx.IsCoinBase() && wtx.IsSpent(0)) - continue; + const uint256& wtxid = item.first; + CWalletTx& wtx = item.second; + assert(wtx.GetHash() == wtxid); - CCoins coins; - bool fUpdated = false; - bool fFound = pcoinsTip->GetCoins(wtx.GetHash(), coins); - if (fFound || wtx.GetDepthInMainChain() > 0) - { - // Update fSpent if a tx got spent somewhere else by a copy of wallet.dat - for (unsigned int i = 0; i < wtx.vout.size(); i++) - { - if (wtx.IsSpent(i)) - continue; - if ((i >= coins.vout.size() || coins.vout[i].IsNull()) && IsMine(wtx.vout[i])) - { - wtx.MarkSpent(i); - fUpdated = true; - fMissing = true; - } - } - if (fUpdated) - { - LogPrintf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()), wtx.GetHash().ToString()); - wtx.MarkDirty(); - wtx.WriteToDisk(); - } - } - else - { - // Re-accept any txes of ours that aren't already in a block - if (!wtx.IsCoinBase()) - wtx.AcceptWalletTransaction(); - } - } - if (fMissing) + int nDepth = wtx.GetDepthInMainChain(); + + if (!wtx.IsCoinBase() && nDepth < 0) { - // TODO: optimize this to scan just part of the block chain? - if (ScanForWalletTransactions(chainActive.Genesis())) - fRepeat = true; // Found missing transactions: re-do re-accept. + // Try to add to memory pool + LOCK(mempool.cs); + wtx.AcceptToMemoryPool(false); } } } void CWalletTx::RelayWalletTransaction() { - BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) - { - // Important: versions of bitcoin before 0.8.6 had a bug that inserted - // empty transactions into the vtxPrev, which will cause the node to be - // banned when retransmitted, hence the check for !tx.vin.empty() - if (!tx.IsCoinBase() && !tx.vin.empty()) - if (tx.GetDepthInMainChain() == 0) - RelayTransaction((CTransaction)tx, tx.GetHash()); - } if (!IsCoinBase()) { if (GetDepthInMainChain() == 0) { @@ -1104,6 +1003,7 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const LOCK(cs_wallet); for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { + const uint256& wtxid = it->first; const CWalletTx* pcoin = &(*it).second; if (!IsFinalTx(*pcoin)) @@ -1120,7 +1020,7 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const continue; for (unsigned int i = 0; i < pcoin->vout.size(); i++) { - if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && + if (!(IsSpent(wtxid, i)) && IsMine(pcoin->vout[i]) && !IsLockedCoin((*it).first, i) && pcoin->vout[i].nValue > 0 && (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i))) vCoins.push_back(COutput(pcoin, i, nDepth)); @@ -1452,8 +1352,6 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend, continue; } - // Fill vtxPrev by copying from previous transactions vtxPrev - wtxNew.AddSupportingTransactions(); wtxNew.fTimeReceivedIsTxTime = true; break; @@ -1490,14 +1388,12 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) // otherwise just for transaction history. AddToWallet(wtxNew); - // Mark old coins as spent + // Notify that old coins are spent set<CWalletTx*> setCoins; BOOST_FOREACH(const CTxIn& txin, wtxNew.vin) { CWalletTx &coin = mapWallet[txin.prevout.hash]; coin.BindWallet(this); - coin.MarkSpent(txin.prevout.n); - coin.WriteToDisk(); NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED); } @@ -1855,7 +1751,7 @@ std::map<CTxDestination, int64_t> CWallet::GetAddressBalances() if(!ExtractDestination(pcoin->vout[i].scriptPubKey, addr)) continue; - int64_t n = pcoin->IsSpent(i) ? 0 : pcoin->vout[i].nValue; + int64_t n = IsSpent(walletEntry.first, i) ? 0 : pcoin->vout[i].nValue; if (!balances.count(addr)) balances[addr] = 0; diff --git a/src/wallet.h b/src/wallet.h index eb192f1ca6..7feb86d294 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -108,11 +108,15 @@ private: int64_t nNextResend; int64_t nLastResend; - // Used to detect and report conflicted transactions: - typedef std::multimap<COutPoint, uint256> TxConflicts; - TxConflicts mapTxConflicts; - void AddToConflicts(const uint256& wtxhash); - void SyncMetaData(std::pair<TxConflicts::iterator, TxConflicts::iterator>); + // Used to keep track of spent outpoints, and + // detect and report conflicts (double-spends or + // mutated transactions where the mutant gets mined). + typedef std::multimap<COutPoint, uint256> TxSpends; + TxSpends mapTxSpends; + void AddToSpends(const COutPoint& outpoint, const uint256& wtxid); + void AddToSpends(const uint256& wtxid); + + void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>); public: /// Main wallet lock. @@ -169,12 +173,16 @@ public: int64_t nTimeFirstKey; + const CWalletTx* GetWalletTx(const uint256& hash) const; + // check whether we are allowed to upgrade (or already support) to the named feature bool CanSupportFeature(enum WalletFeature wf) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; } void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl = NULL) const; bool SelectCoinsMinConf(int64_t nTargetValue, int nConfMine, int nConfTheirs, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64_t& nValueRet) const; + bool IsSpent(const uint256& hash, unsigned int n) const; + bool IsLockedCoin(uint256 hash, unsigned int n) const; void LockCoin(COutPoint& output); void UnlockCoin(COutPoint& output); @@ -234,7 +242,6 @@ public: void SyncTransaction(const uint256 &hash, const CTransaction& tx, const CBlock* pblock); bool AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate); void EraseFromWallet(const uint256 &hash); - void WalletUpdateSpent(const CTransaction& prevout); int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); void ReacceptWalletTransactions(); void ResendWalletTransactions(); @@ -439,7 +446,6 @@ private: const CWallet* pwallet; public: - std::vector<CMerkleTx> vtxPrev; mapValue_t mapValue; std::vector<std::pair<std::string, std::string> > vOrderForm; unsigned int fTimeReceivedIsTxTime; @@ -447,7 +453,6 @@ public: unsigned int nTimeSmart; char fFromMe; std::string strFromAccount; - std::vector<char> vfSpent; // which outputs are already spent int64_t nOrderPos; // position in ordered transaction list // memory only @@ -485,7 +490,6 @@ public: void Init(const CWallet* pwalletIn) { pwallet = pwalletIn; - vtxPrev.clear(); mapValue.clear(); vOrderForm.clear(); fTimeReceivedIsTxTime = false; @@ -493,7 +497,6 @@ public: nTimeSmart = 0; fFromMe = false; strFromAccount.clear(); - vfSpent.clear(); fDebitCached = false; fCreditCached = false; fImmatureCreditCached = false; @@ -518,15 +521,6 @@ public: { pthis->mapValue["fromaccount"] = pthis->strFromAccount; - std::string str; - BOOST_FOREACH(char f, vfSpent) - { - str += (f ? '1' : '0'); - if (f) - fSpent = true; - } - pthis->mapValue["spent"] = str; - WriteOrderPos(pthis->nOrderPos, pthis->mapValue); if (nTimeSmart) @@ -534,7 +528,8 @@ public: } nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion,ser_action); - READWRITE(vtxPrev); + std::vector<CMerkleTx> vUnused; // Used to be vtxPrev + READWRITE(vUnused); READWRITE(mapValue); READWRITE(vOrderForm); READWRITE(fTimeReceivedIsTxTime); @@ -546,12 +541,6 @@ public: { pthis->strFromAccount = pthis->mapValue["fromaccount"]; - if (mapValue.count("spent")) - BOOST_FOREACH(char c, pthis->mapValue["spent"]) - pthis->vfSpent.push_back(c != '0'); - else - pthis->vfSpent.assign(vout.size(), fSpent); - ReadOrderPos(pthis->nOrderPos, pthis->mapValue); pthis->nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(pthis->mapValue["timesmart"]) : 0; @@ -564,26 +553,6 @@ public: pthis->mapValue.erase("timesmart"); ) - // marks certain txout's as spent - // returns true if any update took place - bool UpdateSpent(const std::vector<char>& vfNewSpent) - { - bool fReturn = false; - for (unsigned int i = 0; i < vfNewSpent.size(); i++) - { - if (i == vfSpent.size()) - break; - - if (vfNewSpent[i] && !vfSpent[i]) - { - vfSpent[i] = true; - fReturn = true; - fAvailableCreditCached = false; - } - } - return fReturn; - } - // make sure balances are recalculated void MarkDirty() { @@ -599,27 +568,6 @@ public: MarkDirty(); } - void MarkSpent(unsigned int nOut) - { - if (nOut >= vout.size()) - throw std::runtime_error("CWalletTx::MarkSpent() : nOut out of range"); - vfSpent.resize(vout.size()); - if (!vfSpent[nOut]) - { - vfSpent[nOut] = true; - fAvailableCreditCached = false; - } - } - - bool IsSpent(unsigned int nOut) const - { - if (nOut >= vout.size()) - throw std::runtime_error("CWalletTx::IsSpent() : nOut out of range"); - if (nOut >= vfSpent.size()) - return false; - return (!!vfSpent[nOut]); - } - int64_t GetDebit() const { if (vin.empty()) @@ -661,6 +609,9 @@ public: int64_t GetAvailableCredit(bool fUseCache=true) const { + if (pwallet == 0) + return 0; + // Must wait until coinbase is safely deep enough in the chain before valuing it if (IsCoinBase() && GetBlocksToMaturity() > 0) return 0; @@ -671,7 +622,7 @@ public: int64_t nCredit = 0; for (unsigned int i = 0; i < vout.size(); i++) { - if (!IsSpent(i)) + if (!pwallet->IsSpent(GetHash(), i)) { const CTxOut &txout = vout[i]; nCredit += pwallet->GetCredit(txout); @@ -719,38 +670,14 @@ public: if (!bSpendZeroConfChange || !IsFromMe()) // using wtx's cached debit return false; - // If no confirmations but it's from us, we can still - // consider it confirmed if all dependencies are confirmed - std::map<uint256, const CMerkleTx*> mapPrev; - std::vector<const CMerkleTx*> vWorkQueue; - vWorkQueue.reserve(vtxPrev.size()+1); - vWorkQueue.push_back(this); - for (unsigned int i = 0; i < vWorkQueue.size(); i++) + // Trusted if all inputs are from us and are in the mempool: + BOOST_FOREACH(const CTxIn& txin, vin) { - const CMerkleTx* ptx = vWorkQueue[i]; - - if (!IsFinalTx(*ptx)) + // Transactions not sent by us: not trusted + const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash); + const CTxOut& parentOut = parent->vout[txin.prevout.n]; + if (parent == NULL || !pwallet->IsMine(parentOut)) return false; - int nPDepth = ptx->GetDepthInMainChain(); - if (nPDepth >= 1) - continue; - if (nPDepth < 0) - return false; - if (!pwallet->IsFromMe(*ptx)) - return false; - - if (mapPrev.empty()) - { - BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) - mapPrev[tx.GetHash()] = &tx; - } - - BOOST_FOREACH(const CTxIn& txin, ptx->vin) - { - if (!mapPrev.count(txin.prevout.hash)) - return false; - vWorkQueue.push_back(mapPrev[txin.prevout.hash]); - } } return true; } @@ -760,8 +687,6 @@ public: int64_t GetTxTime() const; int GetRequestCount() const; - void AddSupportingTransactions(); - bool AcceptWalletTransaction(); void RelayWalletTransaction(); std::set<uint256> GetConflicts() const; |