aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGavin Andresen <gavinandresen@gmail.com>2013-11-10 18:09:58 -0800
committerGavin Andresen <gavinandresen@gmail.com>2013-11-10 18:09:58 -0800
commit7d7df381f85112f5d00dc8f164451dea5baa48bb (patch)
tree624d282fc308688102fa17c1fd331e99e9ac0720
parent16d5f2c5e0a7276fb2b09d8dd51b7bac2753c4cc (diff)
parenta0fa20a12b69717636050dd8742713b1d82ae524 (diff)
Merge pull request #3199 from sipa/mempoolmove
Cleanup refactoring of coins/mempool
-rw-r--r--src/Makefile.am4
-rw-r--r--src/coins.cpp180
-rw-r--r--src/coins.h356
-rw-r--r--src/core.cpp43
-rw-r--r--src/core.h228
-rw-r--r--src/main.cpp195
-rw-r--r--src/main.h134
-rw-r--r--src/net.h1
-rw-r--r--src/rpcblockchain.cpp6
-rw-r--r--src/txdb.cpp23
-rw-r--r--src/txdb.h6
-rw-r--r--src/txmempool.cpp22
-rw-r--r--src/txmempool.h20
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;
diff --git a/src/net.h b/src/net.h
index b07c10610d..4f3d400058 100644
--- a/src/net.h
+++ b/src/net.h
@@ -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 */