diff options
author | Gavin Andresen <gavinandresen@gmail.com> | 2013-11-10 18:09:58 -0800 |
---|---|---|
committer | Gavin Andresen <gavinandresen@gmail.com> | 2013-11-10 18:09:58 -0800 |
commit | 7d7df381f85112f5d00dc8f164451dea5baa48bb (patch) | |
tree | 624d282fc308688102fa17c1fd331e99e9ac0720 | |
parent | 16d5f2c5e0a7276fb2b09d8dd51b7bac2753c4cc (diff) | |
parent | a0fa20a12b69717636050dd8742713b1d82ae524 (diff) |
Merge pull request #3199 from sipa/mempoolmove
Cleanup refactoring of coins/mempool
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/coins.cpp | 180 | ||||
-rw-r--r-- | src/coins.h | 356 | ||||
-rw-r--r-- | src/core.cpp | 43 | ||||
-rw-r--r-- | src/core.h | 228 | ||||
-rw-r--r-- | src/main.cpp | 195 | ||||
-rw-r--r-- | src/main.h | 134 | ||||
-rw-r--r-- | src/net.h | 1 | ||||
-rw-r--r-- | src/rpcblockchain.cpp | 6 | ||||
-rw-r--r-- | src/txdb.cpp | 23 | ||||
-rw-r--r-- | src/txdb.h | 6 | ||||
-rw-r--r-- | src/txmempool.cpp | 22 | ||||
-rw-r--r-- | src/txmempool.h | 20 |
13 files changed, 613 insertions, 605 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 2571a6d755..d4d17b2a9b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,7 +14,7 @@ DIST_SUBDIRS = . qt test # bitcoin core # BITCOIN_CORE_H = addrman.h alert.h allocators.h base58.h bignum.h \ bitcoinrpc.h bloom.h chainparams.h checkpoints.h checkqueue.h \ - clientversion.h compat.h core.h crypter.h db.h hash.h init.h \ + clientversion.h compat.h core.h coins.h crypter.h db.h hash.h init.h \ key.h keystore.h leveldbwrapper.h limitedmap.h main.h miner.h mruset.h \ netbase.h net.h noui.h protocol.h script.h serialize.h sync.h threadsafety.h \ txdb.h txmempool.h ui_interface.h uint256.h util.h version.h walletdb.h wallet.h @@ -32,7 +32,7 @@ obj/build.h: FORCE version.o: obj/build.h libbitcoin_a_SOURCES = addrman.cpp alert.cpp allocators.cpp bitcoinrpc.cpp bloom.cpp \ - chainparams.cpp checkpoints.cpp core.cpp crypter.cpp db.cpp hash.cpp \ + chainparams.cpp checkpoints.cpp core.cpp coins.cpp crypter.cpp db.cpp hash.cpp \ init.cpp key.cpp keystore.cpp leveldbwrapper.cpp main.cpp miner.cpp \ netbase.cpp net.cpp noui.cpp protocol.cpp rpcblockchain.cpp rpcdump.cpp \ rpcmining.cpp rpcnet.cpp rpcrawtransaction.cpp rpcwallet.cpp script.cpp \ diff --git a/src/coins.cpp b/src/coins.cpp new file mode 100644 index 0000000000..ed82c9df8b --- /dev/null +++ b/src/coins.cpp @@ -0,0 +1,180 @@ +// Copyright (c) 2012-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "coins.h" + +#include <assert.h> + +// calculate number of bytes for the bitmask, and its number of non-zero bytes +// each bit in the bitmask represents the availability of one output, but the +// availabilities of the first two outputs are encoded separately +void CCoins::CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const { + unsigned int nLastUsedByte = 0; + for (unsigned int b = 0; 2+b*8 < vout.size(); b++) { + bool fZero = true; + for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) { + if (!vout[2+b*8+i].IsNull()) { + fZero = false; + continue; + } + } + if (!fZero) { + nLastUsedByte = b + 1; + nNonzeroBytes++; + } + } + nBytes += nLastUsedByte; +} + +bool CCoins::Spend(const COutPoint &out, CTxInUndo &undo) { + if (out.n >= vout.size()) + return false; + if (vout[out.n].IsNull()) + return false; + undo = CTxInUndo(vout[out.n]); + vout[out.n].SetNull(); + Cleanup(); + if (vout.size() == 0) { + undo.nHeight = nHeight; + undo.fCoinBase = fCoinBase; + undo.nVersion = this->nVersion; + } + return true; +} + +bool CCoins::Spend(int nPos) { + CTxInUndo undo; + COutPoint out(0, nPos); + return Spend(out, undo); +} + + +bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) { return false; } +bool CCoinsView::SetCoins(const uint256 &txid, const CCoins &coins) { return false; } +bool CCoinsView::HaveCoins(const uint256 &txid) { return false; } +uint256 CCoinsView::GetBestBlock() { return uint256(0); } +bool CCoinsView::SetBestBlock(const uint256 &hashBlock) { return false; } +bool CCoinsView::BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock) { return false; } +bool CCoinsView::GetStats(CCoinsStats &stats) { return false; } + + +CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { } +bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) { return base->GetCoins(txid, coins); } +bool CCoinsViewBacked::SetCoins(const uint256 &txid, const CCoins &coins) { return base->SetCoins(txid, coins); } +bool CCoinsViewBacked::HaveCoins(const uint256 &txid) { return base->HaveCoins(txid); } +uint256 CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); } +bool CCoinsViewBacked::SetBestBlock(const uint256 &hashBlock) { return base->SetBestBlock(hashBlock); } +void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } +bool CCoinsViewBacked::BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); } +bool CCoinsViewBacked::GetStats(CCoinsStats &stats) { return base->GetStats(stats); } + +CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), hashBlock(0) { } + +bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) { + if (cacheCoins.count(txid)) { + coins = cacheCoins[txid]; + return true; + } + if (base->GetCoins(txid, coins)) { + cacheCoins[txid] = coins; + return true; + } + return false; +} + +std::map<uint256,CCoins>::iterator CCoinsViewCache::FetchCoins(const uint256 &txid) { + std::map<uint256,CCoins>::iterator it = cacheCoins.lower_bound(txid); + if (it != cacheCoins.end() && it->first == txid) + return it; + CCoins tmp; + if (!base->GetCoins(txid,tmp)) + return cacheCoins.end(); + std::map<uint256,CCoins>::iterator ret = cacheCoins.insert(it, std::make_pair(txid, CCoins())); + tmp.swap(ret->second); + return ret; +} + +CCoins &CCoinsViewCache::GetCoins(const uint256 &txid) { + std::map<uint256,CCoins>::iterator it = FetchCoins(txid); + assert(it != cacheCoins.end()); + return it->second; +} + +bool CCoinsViewCache::SetCoins(const uint256 &txid, const CCoins &coins) { + cacheCoins[txid] = coins; + return true; +} + +bool CCoinsViewCache::HaveCoins(const uint256 &txid) { + return FetchCoins(txid) != cacheCoins.end(); +} + +uint256 CCoinsViewCache::GetBestBlock() { + if (hashBlock == uint256(0)) + hashBlock = base->GetBestBlock(); + return hashBlock; +} + +bool CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) { + hashBlock = hashBlockIn; + return true; +} + +bool CCoinsViewCache::BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlockIn) { + for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) + cacheCoins[it->first] = it->second; + hashBlock = hashBlockIn; + return true; +} + +bool CCoinsViewCache::Flush() { + bool fOk = base->BatchWrite(cacheCoins, hashBlock); + if (fOk) + cacheCoins.clear(); + return fOk; +} + +unsigned int CCoinsViewCache::GetCacheSize() { + return cacheCoins.size(); +} + +const CTxOut &CCoinsViewCache::GetOutputFor(const CTxIn& input) +{ + const CCoins &coins = GetCoins(input.prevout.hash); + assert(coins.IsAvailable(input.prevout.n)); + return coins.vout[input.prevout.n]; +} + +int64_t CCoinsViewCache::GetValueIn(const CTransaction& tx) +{ + if (tx.IsCoinBase()) + return 0; + + int64_t nResult = 0; + for (unsigned int i = 0; i < tx.vin.size(); i++) + nResult += GetOutputFor(tx.vin[i]).nValue; + + return nResult; +} + +bool CCoinsViewCache::HaveInputs(const CTransaction& tx) +{ + if (!tx.IsCoinBase()) { + // first check whether information about the prevout hash is available + for (unsigned int i = 0; i < tx.vin.size(); i++) { + const COutPoint &prevout = tx.vin[i].prevout; + if (!HaveCoins(prevout.hash)) + return false; + } + + // then check whether the actual outputs are available + for (unsigned int i = 0; i < tx.vin.size(); i++) { + const COutPoint &prevout = tx.vin[i].prevout; + const CCoins &coins = GetCoins(prevout.hash); + if (!coins.IsAvailable(prevout.n)) + return false; + } + } + return true; +} diff --git a/src/coins.h b/src/coins.h new file mode 100644 index 0000000000..2c72ee88e1 --- /dev/null +++ b/src/coins.h @@ -0,0 +1,356 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_COINS_H +#define BITCOIN_COINS_H + +#include "core.h" +#include "serialize.h" +#include "uint256.h" + +#include <assert.h> +#include <stdint.h> + +#include <boost/foreach.hpp> + +/** pruned version of CTransaction: only retains metadata and unspent transaction outputs + * + * Serialized format: + * - VARINT(nVersion) + * - VARINT(nCode) + * - unspentness bitvector, for vout[2] and further; least significant byte first + * - the non-spent CTxOuts (via CTxOutCompressor) + * - VARINT(nHeight) + * + * The nCode value consists of: + * - bit 1: IsCoinBase() + * - bit 2: vout[0] is not spent + * - bit 4: vout[1] is not spent + * - The higher bits encode N, the number of non-zero bytes in the following bitvector. + * - In case both bit 2 and bit 4 are unset, they encode N-1, as there must be at + * least one non-spent output). + * + * Example: 0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e + * <><><--------------------------------------------><----> + * | \ | / + * version code vout[1] height + * + * - version = 1 + * - code = 4 (vout[1] is not spent, and 0 non-zero bytes of bitvector follow) + * - unspentness bitvector: as 0 non-zero bytes follow, it has length 0 + * - vout[1]: 835800816115944e077fe7c803cfa57f29b36bf87c1d35 + * * 8358: compact amount representation for 60000000000 (600 BTC) + * * 00: special txout type pay-to-pubkey-hash + * * 816115944e077fe7c803cfa57f29b36bf87c1d35: address uint160 + * - height = 203998 + * + * + * Example: 0109044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b + * <><><--><--------------------------------------------------><----------------------------------------------><----> + * / \ \ | | / + * version code unspentness vout[4] vout[16] height + * + * - version = 1 + * - code = 9 (coinbase, neither vout[0] or vout[1] are unspent, + * 2 (1, +1 because both bit 2 and bit 4 are unset) non-zero bitvector bytes follow) + * - unspentness bitvector: bits 2 (0x04) and 14 (0x4000) are set, so vout[2+2] and vout[14+2] are unspent + * - vout[4]: 86ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4ee + * * 86ef97d579: compact amount representation for 234925952 (2.35 BTC) + * * 00: special txout type pay-to-pubkey-hash + * * 61b01caab50f1b8e9c50a5057eb43c2d9563a4ee: address uint160 + * - vout[16]: bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4 + * * bbd123: compact amount representation for 110397 (0.001 BTC) + * * 00: special txout type pay-to-pubkey-hash + * * 8c988f1a4a4de2161e0f50aac7f17e7f9555caa4: address uint160 + * - height = 120891 + */ +class CCoins +{ +public: + // whether transaction is a coinbase + bool fCoinBase; + + // unspent transaction outputs; spent outputs are .IsNull(); spent outputs at the end of the array are dropped + std::vector<CTxOut> vout; + + // at which height this transaction was included in the active block chain + int nHeight; + + // version of the CTransaction; accesses to this value should probably check for nHeight as well, + // as new tx version will probably only be introduced at certain heights + int nVersion; + + // construct a CCoins from a CTransaction, at a given height + CCoins(const CTransaction &tx, int nHeightIn) : fCoinBase(tx.IsCoinBase()), vout(tx.vout), nHeight(nHeightIn), nVersion(tx.nVersion) { + ClearUnspendable(); + } + + // empty constructor + CCoins() : fCoinBase(false), vout(0), nHeight(0), nVersion(0) { } + + // remove spent outputs at the end of vout + void Cleanup() { + while (vout.size() > 0 && vout.back().IsNull()) + vout.pop_back(); + if (vout.empty()) + std::vector<CTxOut>().swap(vout); + } + + void ClearUnspendable() { + BOOST_FOREACH(CTxOut &txout, vout) { + if (txout.scriptPubKey.IsUnspendable()) + txout.SetNull(); + } + Cleanup(); + } + + void swap(CCoins &to) { + std::swap(to.fCoinBase, fCoinBase); + to.vout.swap(vout); + std::swap(to.nHeight, nHeight); + std::swap(to.nVersion, nVersion); + } + + // equality test + friend bool operator==(const CCoins &a, const CCoins &b) { + // Empty CCoins objects are always equal. + if (a.IsPruned() && b.IsPruned()) + return true; + return a.fCoinBase == b.fCoinBase && + a.nHeight == b.nHeight && + a.nVersion == b.nVersion && + a.vout == b.vout; + } + friend bool operator!=(const CCoins &a, const CCoins &b) { + return !(a == b); + } + + void CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const; + + bool IsCoinBase() const { + return fCoinBase; + } + + unsigned int GetSerializeSize(int nType, int nVersion) const { + unsigned int nSize = 0; + unsigned int nMaskSize = 0, nMaskCode = 0; + CalcMaskSize(nMaskSize, nMaskCode); + bool fFirst = vout.size() > 0 && !vout[0].IsNull(); + bool fSecond = vout.size() > 1 && !vout[1].IsNull(); + assert(fFirst || fSecond || nMaskCode); + unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); + // version + nSize += ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion); + // size of header code + nSize += ::GetSerializeSize(VARINT(nCode), nType, nVersion); + // spentness bitmask + nSize += nMaskSize; + // txouts themself + for (unsigned int i = 0; i < vout.size(); i++) + if (!vout[i].IsNull()) + nSize += ::GetSerializeSize(CTxOutCompressor(REF(vout[i])), nType, nVersion); + // height + nSize += ::GetSerializeSize(VARINT(nHeight), nType, nVersion); + return nSize; + } + + template<typename Stream> + void Serialize(Stream &s, int nType, int nVersion) const { + unsigned int nMaskSize = 0, nMaskCode = 0; + CalcMaskSize(nMaskSize, nMaskCode); + bool fFirst = vout.size() > 0 && !vout[0].IsNull(); + bool fSecond = vout.size() > 1 && !vout[1].IsNull(); + assert(fFirst || fSecond || nMaskCode); + unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); + // version + ::Serialize(s, VARINT(this->nVersion), nType, nVersion); + // header code + ::Serialize(s, VARINT(nCode), nType, nVersion); + // spentness bitmask + for (unsigned int b = 0; b<nMaskSize; b++) { + unsigned char chAvail = 0; + for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) + if (!vout[2+b*8+i].IsNull()) + chAvail |= (1 << i); + ::Serialize(s, chAvail, nType, nVersion); + } + // txouts themself + for (unsigned int i = 0; i < vout.size(); i++) { + if (!vout[i].IsNull()) + ::Serialize(s, CTxOutCompressor(REF(vout[i])), nType, nVersion); + } + // coinbase height + ::Serialize(s, VARINT(nHeight), nType, nVersion); + } + + template<typename Stream> + void Unserialize(Stream &s, int nType, int nVersion) { + unsigned int nCode = 0; + // version + ::Unserialize(s, VARINT(this->nVersion), nType, nVersion); + // header code + ::Unserialize(s, VARINT(nCode), nType, nVersion); + fCoinBase = nCode & 1; + std::vector<bool> vAvail(2, false); + vAvail[0] = nCode & 2; + vAvail[1] = nCode & 4; + unsigned int nMaskCode = (nCode / 8) + ((nCode & 6) != 0 ? 0 : 1); + // spentness bitmask + while (nMaskCode > 0) { + unsigned char chAvail = 0; + ::Unserialize(s, chAvail, nType, nVersion); + for (unsigned int p = 0; p < 8; p++) { + bool f = (chAvail & (1 << p)) != 0; + vAvail.push_back(f); + } + if (chAvail != 0) + nMaskCode--; + } + // txouts themself + vout.assign(vAvail.size(), CTxOut()); + for (unsigned int i = 0; i < vAvail.size(); i++) { + if (vAvail[i]) + ::Unserialize(s, REF(CTxOutCompressor(vout[i])), nType, nVersion); + } + // coinbase height + ::Unserialize(s, VARINT(nHeight), nType, nVersion); + Cleanup(); + } + + // mark an outpoint spent, and construct undo information + bool Spend(const COutPoint &out, CTxInUndo &undo); + + // mark a vout spent + bool Spend(int nPos); + + // check whether a particular output is still available + bool IsAvailable(unsigned int nPos) const { + return (nPos < vout.size() && !vout[nPos].IsNull()); + } + + // check whether the entire CCoins is spent + // note that only !IsPruned() CCoins can be serialized + bool IsPruned() const { + BOOST_FOREACH(const CTxOut &out, vout) + if (!out.IsNull()) + return false; + return true; + } +}; + + +struct CCoinsStats +{ + int nHeight; + uint256 hashBlock; + uint64_t nTransactions; + uint64_t nTransactionOutputs; + uint64_t nSerializedSize; + uint256 hashSerialized; + int64_t nTotalAmount; + + CCoinsStats() : nHeight(0), hashBlock(0), nTransactions(0), nTransactionOutputs(0), nSerializedSize(0), hashSerialized(0), nTotalAmount(0) {} +}; + + +/** Abstract view on the open txout dataset. */ +class CCoinsView +{ +public: + // Retrieve the CCoins (unspent transaction outputs) for a given txid + virtual bool GetCoins(const uint256 &txid, CCoins &coins); + + // Modify the CCoins for a given txid + virtual bool SetCoins(const uint256 &txid, const CCoins &coins); + + // Just check whether we have data for a given txid. + // This may (but cannot always) return true for fully spent transactions + virtual bool HaveCoins(const uint256 &txid); + + // Retrieve the block hash whose state this CCoinsView currently represents + virtual uint256 GetBestBlock(); + + // Modify the currently active block hash + virtual bool SetBestBlock(const uint256 &hashBlock); + + // Do a bulk modification (multiple SetCoins + one SetBestBlock) + virtual bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock); + + // Calculate statistics about the unspent transaction output set + virtual bool GetStats(CCoinsStats &stats); + + // As we use CCoinsViews polymorphically, have a virtual destructor + virtual ~CCoinsView() {} +}; + + +/** CCoinsView backed by another CCoinsView */ +class CCoinsViewBacked : public CCoinsView +{ +protected: + CCoinsView *base; + +public: + CCoinsViewBacked(CCoinsView &viewIn); + bool GetCoins(const uint256 &txid, CCoins &coins); + bool SetCoins(const uint256 &txid, const CCoins &coins); + bool HaveCoins(const uint256 &txid); + uint256 GetBestBlock(); + bool SetBestBlock(const uint256 &hashBlock); + void SetBackend(CCoinsView &viewIn); + bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock); + bool GetStats(CCoinsStats &stats); +}; + + +/** CCoinsView that adds a memory cache for transactions to another CCoinsView */ +class CCoinsViewCache : public CCoinsViewBacked +{ +protected: + uint256 hashBlock; + std::map<uint256,CCoins> cacheCoins; + +public: + CCoinsViewCache(CCoinsView &baseIn, bool fDummy = false); + + // Standard CCoinsView methods + bool GetCoins(const uint256 &txid, CCoins &coins); + bool SetCoins(const uint256 &txid, const CCoins &coins); + bool HaveCoins(const uint256 &txid); + uint256 GetBestBlock(); + bool SetBestBlock(const uint256 &hashBlock); + bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock); + + // Return a modifiable reference to a CCoins. Check HaveCoins first. + // Many methods explicitly require a CCoinsViewCache because of this method, to reduce + // copying. + CCoins &GetCoins(const uint256 &txid); + + // Push the modifications applied to this cache to its base. + // Failure to call this method before destruction will cause the changes to be forgotten. + bool Flush(); + + // Calculate the size of the cache (in number of transactions) + unsigned int GetCacheSize(); + + /** Amount of bitcoins coming in to a transaction + Note that lightweight clients may not know anything besides the hash of previous transactions, + so may not be able to calculate this. + + @param[in] tx transaction for which we are checking input total + @return Sum of value of all inputs (scriptSigs) + @see CTransaction::FetchInputs + */ + int64_t GetValueIn(const CTransaction& tx); + + // Check whether all prevouts of the transaction are present in the UTXO set represented by this view + bool HaveInputs(const CTransaction& tx); + + const CTxOut &GetOutputFor(const CTxIn& input); + +private: + std::map<uint256,CCoins>::iterator FetchCoins(const uint256 &txid); +}; + +#endif diff --git a/src/core.cpp b/src/core.cpp index 26c2cfc5c5..80cdcb0849 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -183,49 +183,6 @@ uint64_t CTxOutCompressor::DecompressAmount(uint64_t x) return n; } -// calculate number of bytes for the bitmask, and its number of non-zero bytes -// each bit in the bitmask represents the availability of one output, but the -// availabilities of the first two outputs are encoded separately -void CCoins::CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const { - unsigned int nLastUsedByte = 0; - for (unsigned int b = 0; 2+b*8 < vout.size(); b++) { - bool fZero = true; - for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) { - if (!vout[2+b*8+i].IsNull()) { - fZero = false; - continue; - } - } - if (!fZero) { - nLastUsedByte = b + 1; - nNonzeroBytes++; - } - } - nBytes += nLastUsedByte; -} - -bool CCoins::Spend(const COutPoint &out, CTxInUndo &undo) { - if (out.n >= vout.size()) - return false; - if (vout[out.n].IsNull()) - return false; - undo = CTxInUndo(vout[out.n]); - vout[out.n].SetNull(); - Cleanup(); - if (vout.size() == 0) { - undo.nHeight = nHeight; - undo.fCoinBase = fCoinBase; - undo.nVersion = this->nVersion; - } - return true; -} - -bool CCoins::Spend(int nPos) { - CTxInUndo undo; - COutPoint out(0, nPos); - return Spend(out, undo); -} - uint256 CBlockHeader::GetHash() const { return Hash(BEGIN(nVersion), END(nNonce)); diff --git a/src/core.h b/src/core.h index c881a78f9d..e31a7e6582 100644 --- a/src/core.h +++ b/src/core.h @@ -12,8 +12,6 @@ #include <stdint.h> -#include <boost/foreach.hpp> - class CTransaction; /** An outpoint - a combination of a transaction hash and an index n into its vout */ @@ -324,232 +322,6 @@ public: }; -/** pruned version of CTransaction: only retains metadata and unspent transaction outputs - * - * Serialized format: - * - VARINT(nVersion) - * - VARINT(nCode) - * - unspentness bitvector, for vout[2] and further; least significant byte first - * - the non-spent CTxOuts (via CTxOutCompressor) - * - VARINT(nHeight) - * - * The nCode value consists of: - * - bit 1: IsCoinBase() - * - bit 2: vout[0] is not spent - * - bit 4: vout[1] is not spent - * - The higher bits encode N, the number of non-zero bytes in the following bitvector. - * - In case both bit 2 and bit 4 are unset, they encode N-1, as there must be at - * least one non-spent output). - * - * Example: 0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e - * <><><--------------------------------------------><----> - * | \ | / - * version code vout[1] height - * - * - version = 1 - * - code = 4 (vout[1] is not spent, and 0 non-zero bytes of bitvector follow) - * - unspentness bitvector: as 0 non-zero bytes follow, it has length 0 - * - vout[1]: 835800816115944e077fe7c803cfa57f29b36bf87c1d35 - * * 8358: compact amount representation for 60000000000 (600 BTC) - * * 00: special txout type pay-to-pubkey-hash - * * 816115944e077fe7c803cfa57f29b36bf87c1d35: address uint160 - * - height = 203998 - * - * - * Example: 0109044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b - * <><><--><--------------------------------------------------><----------------------------------------------><----> - * / \ \ | | / - * version code unspentness vout[4] vout[16] height - * - * - version = 1 - * - code = 9 (coinbase, neither vout[0] or vout[1] are unspent, - * 2 (1, +1 because both bit 2 and bit 4 are unset) non-zero bitvector bytes follow) - * - unspentness bitvector: bits 2 (0x04) and 14 (0x4000) are set, so vout[2+2] and vout[14+2] are unspent - * - vout[4]: 86ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4ee - * * 86ef97d579: compact amount representation for 234925952 (2.35 BTC) - * * 00: special txout type pay-to-pubkey-hash - * * 61b01caab50f1b8e9c50a5057eb43c2d9563a4ee: address uint160 - * - vout[16]: bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4 - * * bbd123: compact amount representation for 110397 (0.001 BTC) - * * 00: special txout type pay-to-pubkey-hash - * * 8c988f1a4a4de2161e0f50aac7f17e7f9555caa4: address uint160 - * - height = 120891 - */ -class CCoins -{ -public: - // whether transaction is a coinbase - bool fCoinBase; - - // unspent transaction outputs; spent outputs are .IsNull(); spent outputs at the end of the array are dropped - std::vector<CTxOut> vout; - - // at which height this transaction was included in the active block chain - int nHeight; - - // version of the CTransaction; accesses to this value should probably check for nHeight as well, - // as new tx version will probably only be introduced at certain heights - int nVersion; - - // construct a CCoins from a CTransaction, at a given height - CCoins(const CTransaction &tx, int nHeightIn) : fCoinBase(tx.IsCoinBase()), vout(tx.vout), nHeight(nHeightIn), nVersion(tx.nVersion) { - ClearUnspendable(); - } - - // empty constructor - CCoins() : fCoinBase(false), vout(0), nHeight(0), nVersion(0) { } - - // remove spent outputs at the end of vout - void Cleanup() { - while (vout.size() > 0 && vout.back().IsNull()) - vout.pop_back(); - if (vout.empty()) - std::vector<CTxOut>().swap(vout); - } - - void ClearUnspendable() { - BOOST_FOREACH(CTxOut &txout, vout) { - if (txout.scriptPubKey.IsUnspendable()) - txout.SetNull(); - } - Cleanup(); - } - - void swap(CCoins &to) { - std::swap(to.fCoinBase, fCoinBase); - to.vout.swap(vout); - std::swap(to.nHeight, nHeight); - std::swap(to.nVersion, nVersion); - } - - // equality test - friend bool operator==(const CCoins &a, const CCoins &b) { - // Empty CCoins objects are always equal. - if (a.IsPruned() && b.IsPruned()) - return true; - return a.fCoinBase == b.fCoinBase && - a.nHeight == b.nHeight && - a.nVersion == b.nVersion && - a.vout == b.vout; - } - friend bool operator!=(const CCoins &a, const CCoins &b) { - return !(a == b); - } - - void CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const; - - bool IsCoinBase() const { - return fCoinBase; - } - - unsigned int GetSerializeSize(int nType, int nVersion) const { - unsigned int nSize = 0; - unsigned int nMaskSize = 0, nMaskCode = 0; - CalcMaskSize(nMaskSize, nMaskCode); - bool fFirst = vout.size() > 0 && !vout[0].IsNull(); - bool fSecond = vout.size() > 1 && !vout[1].IsNull(); - assert(fFirst || fSecond || nMaskCode); - unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); - // version - nSize += ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion); - // size of header code - nSize += ::GetSerializeSize(VARINT(nCode), nType, nVersion); - // spentness bitmask - nSize += nMaskSize; - // txouts themself - for (unsigned int i = 0; i < vout.size(); i++) - if (!vout[i].IsNull()) - nSize += ::GetSerializeSize(CTxOutCompressor(REF(vout[i])), nType, nVersion); - // height - nSize += ::GetSerializeSize(VARINT(nHeight), nType, nVersion); - return nSize; - } - - template<typename Stream> - void Serialize(Stream &s, int nType, int nVersion) const { - unsigned int nMaskSize = 0, nMaskCode = 0; - CalcMaskSize(nMaskSize, nMaskCode); - bool fFirst = vout.size() > 0 && !vout[0].IsNull(); - bool fSecond = vout.size() > 1 && !vout[1].IsNull(); - assert(fFirst || fSecond || nMaskCode); - unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); - // version - ::Serialize(s, VARINT(this->nVersion), nType, nVersion); - // header code - ::Serialize(s, VARINT(nCode), nType, nVersion); - // spentness bitmask - for (unsigned int b = 0; b<nMaskSize; b++) { - unsigned char chAvail = 0; - for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) - if (!vout[2+b*8+i].IsNull()) - chAvail |= (1 << i); - ::Serialize(s, chAvail, nType, nVersion); - } - // txouts themself - for (unsigned int i = 0; i < vout.size(); i++) { - if (!vout[i].IsNull()) - ::Serialize(s, CTxOutCompressor(REF(vout[i])), nType, nVersion); - } - // coinbase height - ::Serialize(s, VARINT(nHeight), nType, nVersion); - } - - template<typename Stream> - void Unserialize(Stream &s, int nType, int nVersion) { - unsigned int nCode = 0; - // version - ::Unserialize(s, VARINT(this->nVersion), nType, nVersion); - // header code - ::Unserialize(s, VARINT(nCode), nType, nVersion); - fCoinBase = nCode & 1; - std::vector<bool> vAvail(2, false); - vAvail[0] = nCode & 2; - vAvail[1] = nCode & 4; - unsigned int nMaskCode = (nCode / 8) + ((nCode & 6) != 0 ? 0 : 1); - // spentness bitmask - while (nMaskCode > 0) { - unsigned char chAvail = 0; - ::Unserialize(s, chAvail, nType, nVersion); - for (unsigned int p = 0; p < 8; p++) { - bool f = (chAvail & (1 << p)) != 0; - vAvail.push_back(f); - } - if (chAvail != 0) - nMaskCode--; - } - // txouts themself - vout.assign(vAvail.size(), CTxOut()); - for (unsigned int i = 0; i < vAvail.size(); i++) { - if (vAvail[i]) - ::Unserialize(s, REF(CTxOutCompressor(vout[i])), nType, nVersion); - } - // coinbase height - ::Unserialize(s, VARINT(nHeight), nType, nVersion); - Cleanup(); - } - - // mark an outpoint spent, and construct undo information - bool Spend(const COutPoint &out, CTxInUndo &undo); - - // mark a vout spent - bool Spend(int nPos); - - // check whether a particular output is still available - bool IsAvailable(unsigned int nPos) const { - return (nPos < vout.size() && !vout[nPos].IsNull()); - } - - // check whether the entire CCoins is spent - // note that only !IsPruned() CCoins can be serialized - bool IsPruned() const { - BOOST_FOREACH(const CTxOut &out, vout) - if (!out.IsNull()) - return false; - return true; - } -}; - - /** Nodes collect new transactions into a block, hash them into a hash tree, * and scan through nonce values to make the block's hash satisfy proof-of-work * requirements. When they solve the proof-of-work, they broadcast the block diff --git a/src/main.cpp b/src/main.cpp index 11f262d9cb..8aef91d829 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -229,128 +229,6 @@ CBlockIndex *CChain::FindFork(const CBlockLocator &locator) const { return Genesis(); } -////////////////////////////////////////////////////////////////////////////// -// -// CCoinsView implementations -// - -bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) { return false; } -bool CCoinsView::SetCoins(const uint256 &txid, const CCoins &coins) { return false; } -bool CCoinsView::HaveCoins(const uint256 &txid) { return false; } -CBlockIndex *CCoinsView::GetBestBlock() { return NULL; } -bool CCoinsView::SetBestBlock(CBlockIndex *pindex) { return false; } -bool CCoinsView::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { return false; } -bool CCoinsView::GetStats(CCoinsStats &stats) { return false; } - - -CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { } -bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) { return base->GetCoins(txid, coins); } -bool CCoinsViewBacked::SetCoins(const uint256 &txid, const CCoins &coins) { return base->SetCoins(txid, coins); } -bool CCoinsViewBacked::HaveCoins(const uint256 &txid) { return base->HaveCoins(txid); } -CBlockIndex *CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); } -bool CCoinsViewBacked::SetBestBlock(CBlockIndex *pindex) { return base->SetBestBlock(pindex); } -void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } -bool CCoinsViewBacked::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { return base->BatchWrite(mapCoins, pindex); } -bool CCoinsViewBacked::GetStats(CCoinsStats &stats) { return base->GetStats(stats); } - -CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), pindexTip(NULL) { } - -bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) { - if (cacheCoins.count(txid)) { - coins = cacheCoins[txid]; - return true; - } - if (base->GetCoins(txid, coins)) { - cacheCoins[txid] = coins; - return true; - } - return false; -} - -std::map<uint256,CCoins>::iterator CCoinsViewCache::FetchCoins(const uint256 &txid) { - std::map<uint256,CCoins>::iterator it = cacheCoins.lower_bound(txid); - if (it != cacheCoins.end() && it->first == txid) - return it; - CCoins tmp; - if (!base->GetCoins(txid,tmp)) - return cacheCoins.end(); - std::map<uint256,CCoins>::iterator ret = cacheCoins.insert(it, std::make_pair(txid, CCoins())); - tmp.swap(ret->second); - return ret; -} - -CCoins &CCoinsViewCache::GetCoins(const uint256 &txid) { - std::map<uint256,CCoins>::iterator it = FetchCoins(txid); - assert(it != cacheCoins.end()); - return it->second; -} - -bool CCoinsViewCache::SetCoins(const uint256 &txid, const CCoins &coins) { - cacheCoins[txid] = coins; - return true; -} - -bool CCoinsViewCache::HaveCoins(const uint256 &txid) { - return FetchCoins(txid) != cacheCoins.end(); -} - -CBlockIndex *CCoinsViewCache::GetBestBlock() { - if (pindexTip == NULL) - pindexTip = base->GetBestBlock(); - return pindexTip; -} - -bool CCoinsViewCache::SetBestBlock(CBlockIndex *pindex) { - pindexTip = pindex; - return true; -} - -bool CCoinsViewCache::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { - for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) - cacheCoins[it->first] = it->second; - pindexTip = pindex; - return true; -} - -bool CCoinsViewCache::Flush() { - bool fOk = base->BatchWrite(cacheCoins, pindexTip); - if (fOk) - cacheCoins.clear(); - return fOk; -} - -unsigned int CCoinsViewCache::GetCacheSize() { - return cacheCoins.size(); -} - -/** Helper; lookup from tip (used calling mempool.check() - NOTE: code calling this MUST hold the cs_main lock so - another thread doesn't modify pcoinsTip. When we switch - to C++11 this should be replaced by lambda expressions... - **/ -static CCoins &LookupFromTip(const uint256& hash) { - return pcoinsTip->GetCoins(hash); -} - -/** CCoinsView that brings transactions from a memorypool into view. - It does not check for spendings by memory pool transactions. */ -CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } - -bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) { - if (base->GetCoins(txid, coins)) - return true; - CTransaction tx; - if (mempool.lookup(txid, tx)) { - coins = CCoins(tx, MEMPOOL_HEIGHT); - return true; - } - return false; -} - -bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) { - return mempool.exists(txid) || base->HaveCoins(txid); -} - CCoinsViewCache *pcoinsTip = NULL; CBlockTreeDB *pblocktree = NULL; @@ -1430,25 +1308,6 @@ void UpdateTime(CBlockHeader& block, const CBlockIndex* pindexPrev) -const CTxOut &CCoinsViewCache::GetOutputFor(const CTxIn& input) -{ - const CCoins &coins = GetCoins(input.prevout.hash); - assert(coins.IsAvailable(input.prevout.n)); - return coins.vout[input.prevout.n]; -} - -int64_t CCoinsViewCache::GetValueIn(const CTransaction& tx) -{ - if (tx.IsCoinBase()) - return 0; - - int64_t nResult = 0; - for (unsigned int i = 0; i < tx.vin.size(); i++) - nResult += GetOutputFor(tx.vin[i]).nValue; - - return nResult; -} - void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) { // mark inputs spent @@ -1465,27 +1324,6 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach assert(inputs.SetCoins(txhash, CCoins(tx, nHeight))); } -bool CCoinsViewCache::HaveInputs(const CTransaction& tx) -{ - if (!tx.IsCoinBase()) { - // first check whether information about the prevout hash is available - for (unsigned int i = 0; i < tx.vin.size(); i++) { - const COutPoint &prevout = tx.vin[i].prevout; - if (!HaveCoins(prevout.hash)) - return false; - } - - // then check whether the actual outputs are available - for (unsigned int i = 0; i < tx.vin.size(); i++) { - const COutPoint &prevout = tx.vin[i].prevout; - const CCoins &coins = GetCoins(prevout.hash); - if (!coins.IsAvailable(prevout.n)) - return false; - } - } - return true; -} - bool CScriptCheck::operator()() const { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; if (!VerifyScript(scriptSig, scriptPubKey, *ptxTo, nIn, nFlags, nHashType)) @@ -1512,7 +1350,8 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach // While checking, GetBestBlock() refers to the parent block. // This is also true for mempool checks. - int nSpendHeight = inputs.GetBestBlock()->nHeight + 1; + CBlockIndex *pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second; + int nSpendHeight = pindexPrev->nHeight + 1; int64_t nValueIn = 0; int64_t nFees = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) @@ -1588,7 +1427,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) { - assert(pindex == view.GetBestBlock()); + assert(pindex->GetBlockHash() == view.GetBestBlock()); if (pfClean) *pfClean = false; @@ -1664,7 +1503,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex } // move best block pointer to prevout block - view.SetBestBlock(pindex->pprev); + view.SetBestBlock(pindex->pprev->GetBlockHash()); if (pfClean) { *pfClean = fClean; @@ -1713,12 +1552,13 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C return false; // verify that the view's current state corresponds to the previous block - assert(pindex->pprev == view.GetBestBlock()); + uint256 hashPrevBlock = pindex->pprev == NULL ? uint256(0) : pindex->pprev->GetBlockHash(); + assert(hashPrevBlock == view.GetBestBlock()); // Special case for the genesis block, skipping connection of its transactions // (its coinbase is unspendable) if (block.GetHash() == Params().HashGenesisBlock()) { - view.SetBestBlock(pindex); + view.SetBestBlock(pindex->GetBlockHash()); return true; } @@ -1855,7 +1695,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C return state.Abort(_("Failed to write transaction index")); // add this block to the view's block chain - assert(view.SetBestBlock(pindex)); + assert(view.SetBestBlock(pindex->GetBlockHash())); // Watch for transactions paying to me for (unsigned int i = 0; i < block.vtx.size(); i++) @@ -1866,14 +1706,16 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) { - mempool.check(&LookupFromTip); + mempool.check(pcoinsTip); // All modifications to the coin state will be done in this cache. // Only when all have succeeded, we push it to pcoinsTip. CCoinsViewCache view(*pcoinsTip, true); // Find the fork (typically, there is none) - CBlockIndex* pfork = view.GetBestBlock(); + std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(view.GetBestBlock()); + CBlockIndex* ptip = (it != mapBlockIndex.end()) ? it->second : NULL; + CBlockIndex* pfork = ptip; CBlockIndex* plonger = pindexNew; while (pfork && pfork != plonger) { @@ -1889,7 +1731,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) // List of what to disconnect (typically nothing) vector<CBlockIndex*> vDisconnect; - for (CBlockIndex* pindex = view.GetBestBlock(); pindex != pfork; pindex = pindex->pprev) + for (CBlockIndex* pindex = ptip; pindex != pfork; pindex = pindex->pprev) vDisconnect.push_back(pindex); // List of what to connect (typically only pindexNew) @@ -1989,7 +1831,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) mempool.removeConflicts(tx); } - mempool.check(&LookupFromTip); + mempool.check(pcoinsTip); // Update best block in wallet (so we can detect restored wallets) if ((pindexNew->nHeight % 20160) == 0 || (!fIsInitialDownload && (pindexNew->nHeight % 144) == 0)) @@ -2730,9 +2572,10 @@ bool static LoadBlockIndexDB() LogPrintf("LoadBlockIndexDB(): transaction index %s\n", fTxIndex ? "enabled" : "disabled"); // Load pointer to end of best chain - chainActive.SetTip(pcoinsTip->GetBestBlock()); - if (chainActive.Tip() == NULL) + std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); + if (it == mapBlockIndex.end()) return true; + chainActive.SetTip(it->second); LogPrintf("LoadBlockIndexDB(): hashBestChain=%s height=%d date=%s\n", chainActive.Tip()->GetBlockHash().ToString().c_str(), chainActive.Height(), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()).c_str()); @@ -3580,7 +3423,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CValidationState state; if (AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) { - mempool.check(&LookupFromTip); + mempool.check(pcoinsTip); RelayTransaction(tx, inv.hash); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); @@ -3616,7 +3459,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vEraseQueue.push_back(orphanHash); LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString().c_str()); } - mempool.check(&LookupFromTip); + mempool.check(pcoinsTip); } } diff --git a/src/main.h b/src/main.h index 460929d624..b02aa60665 100644 --- a/src/main.h +++ b/src/main.h @@ -12,6 +12,7 @@ #include "bignum.h" #include "chainparams.h" +#include "coins.h" #include "core.h" #include "net.h" #include "script.h" @@ -48,8 +49,6 @@ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB /** The pre-allocation chunk size for rev?????.dat files (since 0.8) */ static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB -/** Fake height value used in CCoins to signify they are only in the memory pool (since 0.8) */ -static const unsigned int MEMPOOL_HEIGHT = 0x7FFFFFFF; /** No amount larger than this (in satoshi) is valid */ static const int64_t MAX_MONEY = 21000000 * COIN; inline bool MoneyRange(int64_t nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } @@ -111,8 +110,6 @@ class CCoinsDB; class CBlockTreeDB; struct CDiskBlockPos; class CTxUndo; -class CCoinsView; -class CCoinsViewCache; class CScriptCheck; class CValidationState; class CWalletInterface; @@ -1049,135 +1046,6 @@ public: extern CChain chainActive; - - - - - -struct CCoinsStats -{ - int nHeight; - uint256 hashBlock; - uint64_t nTransactions; - uint64_t nTransactionOutputs; - uint64_t nSerializedSize; - uint256 hashSerialized; - int64_t nTotalAmount; - - CCoinsStats() : nHeight(0), hashBlock(0), nTransactions(0), nTransactionOutputs(0), nSerializedSize(0), hashSerialized(0), nTotalAmount(0) {} -}; - -/** Abstract view on the open txout dataset. */ -class CCoinsView -{ -public: - // Retrieve the CCoins (unspent transaction outputs) for a given txid - virtual bool GetCoins(const uint256 &txid, CCoins &coins); - - // Modify the CCoins for a given txid - virtual bool SetCoins(const uint256 &txid, const CCoins &coins); - - // Just check whether we have data for a given txid. - // This may (but cannot always) return true for fully spent transactions - virtual bool HaveCoins(const uint256 &txid); - - // Retrieve the block index whose state this CCoinsView currently represents - virtual CBlockIndex *GetBestBlock(); - - // Modify the currently active block index - virtual bool SetBestBlock(CBlockIndex *pindex); - - // Do a bulk modification (multiple SetCoins + one SetBestBlock) - virtual bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex); - - // Calculate statistics about the unspent transaction output set - virtual bool GetStats(CCoinsStats &stats); - - // As we use CCoinsViews polymorphically, have a virtual destructor - virtual ~CCoinsView() {} -}; - -/** CCoinsView backed by another CCoinsView */ -class CCoinsViewBacked : public CCoinsView -{ -protected: - CCoinsView *base; - -public: - CCoinsViewBacked(CCoinsView &viewIn); - bool GetCoins(const uint256 &txid, CCoins &coins); - bool SetCoins(const uint256 &txid, const CCoins &coins); - bool HaveCoins(const uint256 &txid); - CBlockIndex *GetBestBlock(); - bool SetBestBlock(CBlockIndex *pindex); - void SetBackend(CCoinsView &viewIn); - bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex); - bool GetStats(CCoinsStats &stats); -}; - -/** CCoinsView that adds a memory cache for transactions to another CCoinsView */ -class CCoinsViewCache : public CCoinsViewBacked -{ -protected: - CBlockIndex *pindexTip; - std::map<uint256,CCoins> cacheCoins; - -public: - CCoinsViewCache(CCoinsView &baseIn, bool fDummy = false); - - // Standard CCoinsView methods - bool GetCoins(const uint256 &txid, CCoins &coins); - bool SetCoins(const uint256 &txid, const CCoins &coins); - bool HaveCoins(const uint256 &txid); - CBlockIndex *GetBestBlock(); - bool SetBestBlock(CBlockIndex *pindex); - bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex); - - // Return a modifiable reference to a CCoins. Check HaveCoins first. - // Many methods explicitly require a CCoinsViewCache because of this method, to reduce - // copying. - CCoins &GetCoins(const uint256 &txid); - - // Push the modifications applied to this cache to its base. - // Failure to call this method before destruction will cause the changes to be forgotten. - bool Flush(); - - // Calculate the size of the cache (in number of transactions) - unsigned int GetCacheSize(); - - /** Amount of bitcoins coming in to a transaction - Note that lightweight clients may not know anything besides the hash of previous transactions, - so may not be able to calculate this. - - @param[in] tx transaction for which we are checking input total - @return Sum of value of all inputs (scriptSigs) - @see CTransaction::FetchInputs - */ - int64_t GetValueIn(const CTransaction& tx); - - - // Check whether all prevouts of the transaction are present in the UTXO set represented by this view - bool HaveInputs(const CTransaction& tx); - - const CTxOut &GetOutputFor(const CTxIn& input); - -private: - std::map<uint256,CCoins>::iterator FetchCoins(const uint256 &txid); -}; - -/** CCoinsView that brings transactions from a memorypool into view. - It does not check for spendings by memory pool transactions. */ -class CCoinsViewMemPool : public CCoinsViewBacked -{ -protected: - CTxMemPool &mempool; - -public: - CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn); - bool GetCoins(const uint256 &txid, CCoins &coins); - bool HaveCoins(const uint256 &txid); -}; - /** Global variable that points to the active CCoinsView (protected by cs_main) */ extern CCoinsViewCache *pcoinsTip; @@ -32,7 +32,6 @@ class CAddrMan; class CBlockIndex; -class CNetAddr; class CNode; namespace boost { diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 18e213257e..483d8d2e8d 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -245,11 +245,13 @@ Value gettxout(const Array& params, bool fHelp) if (n<0 || (unsigned int)n>=coins.vout.size() || coins.vout[n].IsNull()) return Value::null; - ret.push_back(Pair("bestblock", pcoinsTip->GetBestBlock()->GetBlockHash().GetHex())); + std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); + CBlockIndex *pindex = it->second; + ret.push_back(Pair("bestblock", pindex->GetBlockHash().GetHex())); if ((unsigned int)coins.nHeight == MEMPOOL_HEIGHT) ret.push_back(Pair("confirmations", 0)); else - ret.push_back(Pair("confirmations", pcoinsTip->GetBestBlock()->nHeight - coins.nHeight + 1)); + ret.push_back(Pair("confirmations", pindex->nHeight - coins.nHeight + 1)); ret.push_back(Pair("value", ValueFromAmount(coins.vout[n].nValue))); Object o; ScriptPubKeyToJSON(coins.vout[n].scriptPubKey, o, true); diff --git a/src/txdb.cpp b/src/txdb.cpp index 3c8b0b7fe1..99deb31404 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -40,30 +40,27 @@ bool CCoinsViewDB::HaveCoins(const uint256 &txid) { return db.Exists(make_pair('c', txid)); } -CBlockIndex *CCoinsViewDB::GetBestBlock() { +uint256 CCoinsViewDB::GetBestBlock() { uint256 hashBestChain; if (!db.Read('B', hashBestChain)) - return NULL; - std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(hashBestChain); - if (it == mapBlockIndex.end()) - return NULL; - return it->second; + return uint256(0); + return hashBestChain; } -bool CCoinsViewDB::SetBestBlock(CBlockIndex *pindex) { +bool CCoinsViewDB::SetBestBlock(const uint256 &hashBlock) { CLevelDBBatch batch; - BatchWriteHashBestChain(batch, pindex->GetBlockHash()); + BatchWriteHashBestChain(batch, hashBlock); return db.WriteBatch(batch); } -bool CCoinsViewDB::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { +bool CCoinsViewDB::BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock) { LogPrint("coindb", "Committing %u changed transactions to coin database...\n", (unsigned int)mapCoins.size()); CLevelDBBatch batch; for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) BatchWriteCoins(batch, it->first, it->second); - if (pindex) - BatchWriteHashBestChain(batch, pindex->GetBlockHash()); + if (hashBlock != uint256(0)) + BatchWriteHashBestChain(batch, hashBlock); return db.WriteBatch(batch); } @@ -115,7 +112,7 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) { pcursor->SeekToFirst(); CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); - stats.hashBlock = GetBestBlock()->GetBlockHash(); + stats.hashBlock = GetBestBlock(); ss << stats.hashBlock; int64_t nTotalAmount = 0; while (pcursor->Valid()) { @@ -155,7 +152,7 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) { } } delete pcursor; - stats.nHeight = GetBestBlock()->nHeight; + stats.nHeight = mapBlockIndex.find(GetBestBlock())->second->nHeight; stats.hashSerialized = ss.GetHash(); stats.nTotalAmount = nTotalAmount; return true; diff --git a/src/txdb.h b/src/txdb.h index 8043a595a8..7ce6585d37 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -29,9 +29,9 @@ public: bool GetCoins(const uint256 &txid, CCoins &coins); bool SetCoins(const uint256 &txid, const CCoins &coins); bool HaveCoins(const uint256 &txid); - CBlockIndex *GetBestBlock(); - bool SetBestBlock(CBlockIndex *pindex); - bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex); + uint256 GetBestBlock(); + bool SetBestBlock(const uint256 &hashBlock); + bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock); bool GetStats(CCoinsStats &stats); }; diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 975955a458..d501b89ecf 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -105,7 +105,7 @@ void CTxMemPool::clear() ++nTransactionsUpdated; } -void CTxMemPool::check(CTxMemPool::CoinLookupFunc fnLookup) const +void CTxMemPool::check(CCoinsViewCache *pcoins) const { if (!fSanityCheck) return; @@ -121,7 +121,7 @@ void CTxMemPool::check(CTxMemPool::CoinLookupFunc fnLookup) const if (it2 != mapTx.end()) { assert(it2->second.vout.size() > txin.prevout.n && !it2->second.vout[txin.prevout.n].IsNull()); } else { - CCoins &coins = (*fnLookup)(txin.prevout.hash); + CCoins &coins = pcoins->GetCoins(txin.prevout.hash); assert(coins.IsAvailable(txin.prevout.n)); } // Check whether its inputs are marked in mapNextTx. @@ -160,3 +160,21 @@ bool CTxMemPool::lookup(uint256 hash, CTransaction& result) const result = i->second; return true; } + +CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } + +bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) { + if (base->GetCoins(txid, coins)) + return true; + CTransaction tx; + if (mempool.lookup(txid, tx)) { + coins = CCoins(tx, MEMPOOL_HEIGHT); + return true; + } + return false; +} + +bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) { + return mempool.exists(txid) || base->HaveCoins(txid); +} + diff --git a/src/txmempool.h b/src/txmempool.h index d5a7832ff7..57b92789fb 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -5,9 +5,13 @@ #ifndef BITCOIN_TXMEMPOOL_H #define BITCOIN_TXMEMPOOL_H +#include "coins.h" #include "core.h" #include "sync.h" +/** Fake height value used in CCoins to signify they are only in the memory pool (since 0.8) */ +static const unsigned int MEMPOOL_HEIGHT = 0x7FFFFFFF; + /* * CTxMemPool stores valid-according-to-the-current-best-chain * transactions that may be included in the next block. @@ -37,8 +41,7 @@ public: * all inputs are in the mapNextTx array). If sanity-checking is turned off, * check does nothing. */ - typedef CCoins& (*CoinLookupFunc)(const uint256&); - void check(CoinLookupFunc fnLookup) const; + void check(CCoinsViewCache *pcoins) const; void setSanityCheck(bool _fSanityCheck) { fSanityCheck = _fSanityCheck; } bool addUnchecked(const uint256& hash, const CTransaction &tx); @@ -65,4 +68,17 @@ public: bool lookup(uint256 hash, CTransaction& result) const; }; +/** CCoinsView that brings transactions from a memorypool into view. + It does not check for spendings by memory pool transactions. */ +class CCoinsViewMemPool : public CCoinsViewBacked +{ +protected: + CTxMemPool &mempool; + +public: + CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn); + bool GetCoins(const uint256 &txid, CCoins &coins); + bool HaveCoins(const uint256 &txid); +}; + #endif /* BITCOIN_TXMEMPOOL_H */ |