Skip to content

Commit

Permalink
Direct CCoins references
Browse files Browse the repository at this point in the history
Using the direct CCoins references to prevent excessive copying of CCoins in and out of the CCoinsView instances.
  • Loading branch information
alex authored and alex committed Jan 21, 2014
1 parent 8ed64a6 commit ec44459
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 61 deletions.
102 changes: 50 additions & 52 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,13 +237,30 @@ bool CCoinsViewCache::GetCoinsReadOnly(uint256 txid, CCoins &coins) {
return false;
}

std::map<uint256,CCoins>::iterator CCoinsViewCache::FetchCoins(uint256 txid) {
std::map<uint256,CCoins>::iterator it = cacheCoins.find(txid);
if (it != cacheCoins.end())
return it;
CCoins tmp;
if (!base->GetCoins(txid,tmp))
return it;
std::pair<std::map<uint256,CCoins>::iterator,bool> ret = cacheCoins.insert(std::make_pair(txid, tmp));
return ret.first;
}

CCoins &CCoinsViewCache::GetCoins(uint256 txid) {
std::map<uint256,CCoins>::iterator it = FetchCoins(txid);
assert(it != cacheCoins.end());
return it->second;
}

bool CCoinsViewCache::SetCoins(uint256 txid, const CCoins &coins) {
cacheCoins[txid] = coins;
return true;
}

bool CCoinsViewCache::HaveCoins(uint256 txid) {
return cacheCoins.count(txid) || base->HaveCoins(txid);
return FetchCoins(txid) != cacheCoins.end();
}

CBlockIndex *CCoinsViewCache::GetBestBlock() {
Expand Down Expand Up @@ -410,7 +427,7 @@ bool CTransaction::IsStandard() const
// expensive-to-check-upon-redemption script like:
// DUP CHECKSIG DROP ... repeated 100 times... OP_1
//
bool CTransaction::AreInputsStandard(CCoinsView& mapInputs) const
bool CTransaction::AreInputsStandard(CCoinsViewCache& mapInputs) const
{
if (IsCoinBase())
return true; // Coinbases don't use vin normally
Expand Down Expand Up @@ -708,6 +725,9 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool* pfMissingInpu
}
}

if (!tx.HaveInputs(view))
return error("CTxMemPool::accept() : inputs already spent");

// Check for non-standard pay-to-script-hash in inputs
if (!tx.AreInputsStandard(view) && !fTestNet)
return error("CTxMemPool::accept() : nonstandard transaction input");
Expand Down Expand Up @@ -1306,67 +1326,52 @@ void CBlock::UpdateTime(const CBlockIndex* pindexPrev)
nTime = max(GetBlockTime(), GetAdjustedTime());
}


CTxOut CTransaction::GetOutputFor(const CTxIn& input, CCoinsView& view)
const CTxOut &CTransaction::GetOutputFor(const CTxIn& input, CCoinsViewCache& view)
{
CCoins coins;
if (!view.GetCoins(input.prevout.hash, coins))
throw std::runtime_error("CTransaction::GetOutputFor() : prevout.hash not found");

if (input.prevout.n >= coins.vout.size())
throw std::runtime_error("CTransaction::GetOutputFor() : prevout.n out of range or already spent");

const CTxOut &out = coins.vout[input.prevout.n];
if (out.IsNull())
throw std::runtime_error("CTransaction::GetOutputFor() : already spent");

return out;
const CCoins &coins = view.GetCoins(input.prevout.hash);
assert(coins.IsAvailable(input.prevout.n));
return coins.vout[input.prevout.n];
}

int64 CTransaction::GetValueIn(CCoinsView& inputs) const
int64 CTransaction::GetValueIn(CCoinsViewCache& inputs) const
{
if (IsCoinBase())
return 0;

int64 nResult = 0;
for (unsigned int i = 0; i < vin.size(); i++)
{
nResult += GetOutputFor(vin[i], inputs).nValue;
}

return nResult;
}

unsigned int CTransaction::GetP2SHSigOpCount(CCoinsView& inputs) const
unsigned int CTransaction::GetP2SHSigOpCount(CCoinsViewCache& inputs) const
{
if (IsCoinBase())
return 0;

unsigned int nSigOps = 0;
for (unsigned int i = 0; i < vin.size(); i++)
{
CTxOut prevout = GetOutputFor(vin[i], inputs);
const CTxOut &prevout = GetOutputFor(vin[i], inputs);
if (prevout.scriptPubKey.IsPayToScriptHash())
nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig);
}
return nSigOps;
}

bool CTransaction::UpdateCoins(CCoinsView &inputs, CTxUndo &txundo, int nHeight, unsigned int nTimeStamp, const uint256 &txhash) const
bool CTransaction::UpdateCoins(CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, unsigned int nTimeStamp, const uint256 &txhash) const
{
// mark inputs spent
if (!IsCoinBase()) {
BOOST_FOREACH(const CTxIn &txin, vin) {
CCoins coins;
if (!inputs.GetCoins(txin.prevout.hash, coins))
return error("UpdateCoins() : cannot find prevtx");
CCoins &coins = inputs.GetCoins(txin.prevout.hash);
if (coins.nTime > nTimeStamp)
return error("UpdateCoins() : timestamp violation");
CTxInUndo undo;
if (!coins.Spend(txin.prevout, undo))
return error("UpdateCoins() : cannot spend input");
txundo.vprevout.push_back(undo);
if (!inputs.SetCoins(txin.prevout.hash, coins))
return error("UpdateCoins() : cannot update input");
}
}

Expand All @@ -1377,9 +1382,9 @@ bool CTransaction::UpdateCoins(CCoinsView &inputs, CTxUndo &txundo, int nHeight,
return true;
}

bool CTransaction::HaveInputs(CCoinsView &inputs) const
bool CTransaction::HaveInputs(CCoinsViewCache &inputs) const
{
if (!IsCoinBase()) {
if (!IsCoinBase()) {
// first check whether information about the prevout hash is available
for (unsigned int i = 0; i < vin.size(); i++) {
const COutPoint &prevout = vin[i].prevout;
Expand All @@ -1390,37 +1395,33 @@ bool CTransaction::HaveInputs(CCoinsView &inputs) const
// then check whether the actual outputs are available
for (unsigned int i = 0; i < vin.size(); i++) {
const COutPoint &prevout = vin[i].prevout;
CCoins coins;
inputs.GetCoins(prevout.hash, coins);
const CCoins &coins = inputs.GetCoins(prevout.hash);
if (!coins.IsAvailable(prevout.n))
return false;
}
}
return true;
}

bool CTransaction::CheckInputs(CCoinsView &inputs, enum CheckSig_mode csmode, bool fStrictPayToScriptHash, bool fStrictEncodings, CBlock *pblock) const
bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmode, bool fStrictPayToScriptHash, bool fStrictEncodings, CBlock *pblock) const
{
if (!IsCoinBase())
{
// This doesn't trigger the DoS code on purpose; if it did, it would make it easier
// for an attacker to attempt to split the network.
if (!HaveInputs(inputs))
return error("CheckInputs() : %s inputs unavailable", GetHash().ToString().substr(0,10).c_str());

CBlockIndex *pindexBlock = inputs.GetBestBlock();
int64 nValueIn = 0;
int64 nFees = 0;
for (unsigned int i = 0; i < vin.size(); i++)
{
const COutPoint &prevout = vin[i].prevout;
CCoins coins;
if (!inputs.GetCoins(prevout.hash, coins))
return error("CheckInputs() : cannot find prevout tx");

// Check for conflicts (double-spend)
// This doesn't trigger the DoS code on purpose; if it did, it would make it easier
// for an attacker to attempt to split the network.
if (!coins.IsAvailable(prevout.n))
return error("CheckInputs() : %s prev tx already used", GetHash().ToString().substr(0,10).c_str());
const CCoins &coins = inputs.GetCoins(prevout.hash);

// If prev is coinbase or coinstake, check that it's matured
if (coins.IsCoinBase() || coins.IsCoinStake()) {
CBlockIndex *pindexBlock = inputs.GetBestBlock();
if (pindexBlock->nHeight - coins.nHeight < nCoinbaseMaturity)
return error("CheckInputs() : tried to spend %s at depth %d", coins.IsCoinBase() ? "coinbase" : "coinstake", pindexBlock->nHeight - coins.nHeight);
}
Expand Down Expand Up @@ -1480,8 +1481,7 @@ bool CTransaction::CheckInputs(CCoinsView &inputs, enum CheckSig_mode csmode, bo
(csmode == CS_AFTER_CHECKPOINT && inputs.GetBestBlock()->nHeight >= Checkpoints::GetTotalBlocksEstimate())) {
for (unsigned int i = 0; i < vin.size(); i++) {
const COutPoint &prevout = vin[i].prevout;
CCoins coins;
inputs.GetCoins(prevout.hash, coins);
const CCoins &coins = inputs.GetCoins(prevout.hash);

// Verify signature
if (!VerifySignature(coins, *this, i, fStrictPayToScriptHash, fStrictEncodings, 0)) {
Expand Down Expand Up @@ -1546,7 +1546,7 @@ bool CTransaction::ClientCheckInputs() const
return true;
}

bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsView &view)
bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view)
{
assert(pindex == view.GetBestBlock());

Expand Down Expand Up @@ -1574,17 +1574,16 @@ bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsView &view)
continue;

// check that all outputs are available
CCoins outs;
if (!view.GetCoins(hash, outs))
if (!view.HaveCoins(hash))
return error("DisconnectBlock() : outputs still spent? database corrupted");
CCoins &outs = view.GetCoins(hash);

CCoins outsBlock = CCoins(tx, pindex->nHeight, pindex->nTime);
if (outs != outsBlock)
return error("DisconnectBlock() : added transaction mismatch? database corrupted");

// remove outputs
if (!view.SetCoins(hash, CCoins()))
return error("DisconnectBlock() : cannot delete coin outputs");
outs = CCoins();

// restore inputs
if (i > 0) { // not coinbases
Expand Down Expand Up @@ -1627,7 +1626,7 @@ bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsView &view)

bool FindUndoPos(CChainDB &chaindb, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);

bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsView &view, bool fJustCheck)
bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck)
{
// Check it again in case a previous version let a bad block in
if (!CheckBlock(!fJustCheck, !fJustCheck))
Expand All @@ -1653,8 +1652,7 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsView &view, bool fJustCheck
if (fEnforceBIP30) {
for (unsigned int i=0; i<vtx.size(); i++) {
uint256 hash = GetTxHash(i);
CCoins coins;
if (view.GetCoins(hash, coins) && !coins.IsPruned())
if (view.HaveCoins(hash) && !view.GetCoins(hash).IsPruned())
return error("ConnectBlock() : tried to overwrite transaction");
}
}
Expand Down
22 changes: 13 additions & 9 deletions src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ class CDiskBlockPos;
class CCoins;
class CTxUndo;
class CCoinsView;
class CCoinsViewCache;

void RegisterWallet(CWallet* pwalletIn);
void UnregisterWallet(CWallet* pwalletIn);
Expand Down Expand Up @@ -525,7 +526,7 @@ class CTransaction
@return True if all inputs (scriptSigs) use only standard transaction forms
@see CTransaction::FetchInputs
*/
bool AreInputsStandard(CCoinsView& mapInputs) const;
bool AreInputsStandard(CCoinsViewCache& mapInputs) const;

/** Count ECDSA signature operations the old-fashioned (pre-0.6) way
@return number of sigops this transaction's outputs will produce when spent
Expand All @@ -539,7 +540,7 @@ class CTransaction
@return maximum number of sigops required to validate this transaction's inputs
@see CTransaction::FetchInputs
*/
unsigned int GetP2SHSigOpCount(CCoinsView& mapInputs) const;
unsigned int GetP2SHSigOpCount(CCoinsViewCache& mapInputs) const;

/** Amount of bitcoins spent by this transaction.
@return sum of all outputs (note: does not include fees)
Expand All @@ -564,7 +565,7 @@ class CTransaction
@return Sum of value of all inputs (scriptSigs)
@see CTransaction::FetchInputs
*/
int64 GetValueIn(CCoinsView& mapInputs) const;
int64 GetValueIn(CCoinsViewCache& mapInputs) const;

static bool AllowFree(double dPriority)
{
Expand Down Expand Up @@ -624,14 +625,14 @@ class CTransaction
bool ClientCheckInputs() const;

// Check whether all prevouts of this transaction are present in the UTXO set represented by view
bool HaveInputs(CCoinsView &view) const;
bool HaveInputs(CCoinsViewCache &view) const;

// Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts)
// This does not modify the UTXO set
bool CheckInputs(CCoinsView &view, enum CheckSig_mode csmode, bool fStrictPayToScriptHash=true, bool fStrictEncodings=true, CBlock *pblock=NULL) const;
bool CheckInputs(CCoinsViewCache &view, enum CheckSig_mode csmode, bool fStrictPayToScriptHash=true, bool fStrictEncodings=true, CBlock *pblock=NULL) const;

// Apply the effects of this transaction on the UTXO set represented by view
bool UpdateCoins(CCoinsView &view, CTxUndo &txundo, int nHeight, unsigned int nBlockTime, const uint256 &txhash) const;
bool UpdateCoins(CCoinsViewCache &view, CTxUndo &txundo, int nHeight, unsigned int nBlockTime, const uint256 &txhash) const;

// Context-independent validity checks
bool CheckTransaction() const;
Expand All @@ -640,7 +641,7 @@ class CTransaction
bool AcceptToMemoryPool(bool fCheckInputs=true, bool* pfMissingInputs=NULL);
bool GetCoinAge(uint64& nCoinAge) const; // Get transaction coin age
protected:
static CTxOut GetOutputFor(const CTxIn& input, CCoinsView& mapInputs);
static const CTxOut &GetOutputFor(const CTxIn& input, CCoinsViewCache& mapInputs);
};


Expand Down Expand Up @@ -1376,10 +1377,10 @@ class CBlock
}

// Undo the effects of this block (with given index) on the UTXO set represented by coins
bool DisconnectBlock(CBlockIndex *pindex, CCoinsView &coins);
bool DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &coins);

// Apply the effects of this block (with given index) on the UTXO set represented by coins
bool ConnectBlock(CBlockIndex *pindex, CCoinsView &coins, bool fJustCheck=false);
bool ConnectBlock(CBlockIndex *pindex, CCoinsViewCache &coins, bool fJustCheck=false);

// Read a block from disk
bool ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions=true);
Expand Down Expand Up @@ -2023,11 +2024,14 @@ class CCoinsViewCache : public CCoinsViewBacked
bool GetCoinsReadOnly(uint256 txid, CCoins &coins);
bool SetCoins(uint256 txid, const CCoins &coins);
bool HaveCoins(uint256 txid);
CCoins &GetCoins(uint256 txid);
CBlockIndex *GetBestBlock();
bool SetBestBlock(CBlockIndex *pindex);
bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex);
bool Flush();
unsigned int GetCacheSize();
private:
std::map<uint256,CCoins>::iterator FetchCoins(uint256 txid);
};

/** CCoinsView that brings transactions from a memorypool into view.
Expand Down

0 comments on commit ec44459

Please sign in to comment.