Merge pull request #3199 from sipa/mempoolmove
Cleanup refactoring of coins/mempool
This commit is contained in:
commit
7d7df381f8
13 changed files with 613 additions and 605 deletions
|
@ -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 \
|
||||
|
|
180
src/coins.cpp
Normal file
180
src/coins.cpp
Normal file
|
@ -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;
|
||||
}
|
356
src/coins.h
Normal file
356
src/coins.h
Normal file
|
@ -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
|
43
src/core.cpp
43
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));
|
||||
|
|
228
src/core.h
228
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
|
||||
|
|
195
src/main.cpp
195
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
134
src/main.h
134
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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
23
src/txdb.cpp
23
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;
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in a new issue