Combine CCoinsViewCache's HaveCoins and const GetCoins into AccessCoins.

The efficient version of CCoinsViewCache::GetCoins only works for known-to-exist
cache entries, requiring a separate HaveCoins call beforehand. This is
inefficient as both perform a hashtable lookup.

Replace the non-mutable GetCoins with AccessCoins, which returns a potentially-NULL
pointer. This also decreases the overloading of GetCoins.

Also replace some copying (inefficient) GetCoins calls with equivalent AccessCoins,
decreasing the copying.
This commit is contained in:
Pieter Wuille 2014-09-02 21:21:15 +02:00
parent b8d92236f6
commit 629d75faac
7 changed files with 54 additions and 51 deletions

View File

@ -418,12 +418,12 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)
// Sign what we can:
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
CTxIn& txin = mergedTx.vin[i];
CCoins coins;
if (!view.GetCoins(txin.prevout.hash, coins) || !coins.IsAvailable(txin.prevout.n)) {
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
if (!coins || !coins->IsAvailable(txin.prevout.n)) {
fComplete = false;
continue;
}
const CScript& prevPubKey = coins.vout[txin.prevout.n].scriptPubKey;
const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey;
txin.scriptSig.clear();
// Only sign SIGHASH_SINGLE if there's a corresponding output:

View File

@ -110,9 +110,13 @@ CCoins &CCoinsViewCache::GetCoins(const uint256 &txid) {
return it->second;
}
const CCoins &CCoinsViewCache::GetCoins(const uint256 &txid) const {
/* Avoid redundant implementation with the const-cast. */
return const_cast<CCoinsViewCache*>(this)->GetCoins(txid);
const CCoins* CCoinsViewCache::AccessCoins(const uint256 &txid) const {
CCoinsMap::const_iterator it = FetchCoins(txid);
if (it == cacheCoins.end()) {
return NULL;
} else {
return &it->second;
}
}
bool CCoinsViewCache::SetCoins(const uint256 &txid, const CCoins &coins) {
@ -162,9 +166,9 @@ unsigned int CCoinsViewCache::GetCacheSize() const {
const CTxOut &CCoinsViewCache::GetOutputFor(const CTxIn& input) const
{
const CCoins &coins = GetCoins(input.prevout.hash);
assert(coins.IsAvailable(input.prevout.n));
return coins.vout[input.prevout.n];
const CCoins* coins = AccessCoins(input.prevout.hash);
assert(coins && coins->IsAvailable(input.prevout.n));
return coins->vout[input.prevout.n];
}
int64_t CCoinsViewCache::GetValueIn(const CTransaction& tx) const
@ -182,19 +186,12 @@ int64_t CCoinsViewCache::GetValueIn(const CTransaction& tx) const
bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
{
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))
const CCoins* coins = AccessCoins(prevout.hash);
if (!coins || !coins->IsAvailable(prevout.n)) {
return false;
}
}
}
return true;
@ -207,10 +204,11 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const
double dResult = 0.0;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
const CCoins &coins = GetCoins(txin.prevout.hash);
if (!coins.IsAvailable(txin.prevout.n)) continue;
if (coins.nHeight < nHeight) {
dResult += coins.vout[txin.prevout.n].nValue * (nHeight-coins.nHeight);
const CCoins* coins = AccessCoins(txin.prevout.hash);
assert(coins);
if (!coins->IsAvailable(txin.prevout.n)) continue;
if (coins->nHeight < nHeight) {
dResult += coins->vout[txin.prevout.n].nValue * (nHeight-coins->nHeight);
}
}
return tx.ComputePriority(dResult);

View File

@ -344,11 +344,13 @@ public:
bool SetBestBlock(const uint256 &hashBlock);
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock);
// Return a pointer to CCoins in the cache, or NULL if not found. This is
// more efficient than GetCoins. Modifications to other cache entries are
// allowed while accessing the returned pointer.
const CCoins* AccessCoins(const uint256 &txid) const;
// 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);
const CCoins &GetCoins(const uint256 &txid) const;
// Push the modifications applied to this cache to its base.
// Failure to call this method before destruction will cause the changes to be forgotten.

View File

@ -1017,9 +1017,9 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock
int nHeight = -1;
{
CCoinsViewCache &view = *pcoinsTip;
CCoins coins;
if (view.GetCoins(hash, coins))
nHeight = coins.nHeight;
const CCoins* coins = view.AccessCoins(hash);
if (coins)
nHeight = coins->nHeight;
}
if (nHeight > 0)
pindexSlow = chainActive[nHeight];
@ -1371,19 +1371,20 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
for (unsigned int i = 0; i < tx.vin.size(); i++)
{
const COutPoint &prevout = tx.vin[i].prevout;
const CCoins &coins = inputs.GetCoins(prevout.hash);
const CCoins *coins = inputs.AccessCoins(prevout.hash);
assert(coins);
// If prev is coinbase, check that it's matured
if (coins.IsCoinBase()) {
if (nSpendHeight - coins.nHeight < COINBASE_MATURITY)
if (coins->IsCoinBase()) {
if (nSpendHeight - coins->nHeight < COINBASE_MATURITY)
return state.Invalid(
error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight),
error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight),
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase");
}
// Check for negative or overflow input values
nValueIn += coins.vout[prevout.n].nValue;
if (!MoneyRange(coins.vout[prevout.n].nValue) || !MoneyRange(nValueIn))
nValueIn += coins->vout[prevout.n].nValue;
if (!MoneyRange(coins->vout[prevout.n].nValue) || !MoneyRange(nValueIn))
return state.DoS(100, error("CheckInputs() : txin values out of range"),
REJECT_INVALID, "bad-txns-inputvalues-outofrange");
@ -1413,10 +1414,11 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
if (fScriptChecks) {
for (unsigned int i = 0; i < tx.vin.size(); i++) {
const COutPoint &prevout = tx.vin[i].prevout;
const CCoins &coins = inputs.GetCoins(prevout.hash);
const CCoins* coins = inputs.AccessCoins(prevout.hash);
assert(coins);
// Verify signature
CScriptCheck check(coins, tx, i, flags, 0);
CScriptCheck check(*coins, tx, i, flags, 0);
if (pvChecks) {
pvChecks->push_back(CScriptCheck());
check.swap(pvChecks->back());
@ -1428,7 +1430,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
// arguments; if so, don't trigger DoS protection to
// avoid splitting the network between upgraded and
// non-upgraded nodes.
CScriptCheck check(coins, tx, i,
CScriptCheck check(*coins, tx, i,
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, 0);
if (check())
return state.Invalid(false, REJECT_NONSTANDARD, "non-mandatory-script-verify-flag");
@ -1614,8 +1616,8 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
(pindex->nHeight==91880 && pindex->GetBlockHash() == uint256("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721")));
if (fEnforceBIP30) {
BOOST_FOREACH(const CTransaction& tx, block.vtx) {
const uint256& hash = tx.GetHash();
if (view.HaveCoins(hash) && !view.GetCoins(hash).IsPruned())
const CCoins* coins = view.AccessCoins(tx.GetHash());
if (coins && !coins->IsPruned())
return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction"),
REJECT_INVALID, "bad-txns-BIP30");
}

View File

@ -167,12 +167,13 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
nTotalIn += mempool.mapTx[txin.prevout.hash].GetTx().vout[txin.prevout.n].nValue;
continue;
}
const CCoins &coins = view.GetCoins(txin.prevout.hash);
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
assert(coins);
int64_t nValueIn = coins.vout[txin.prevout.n].nValue;
int64_t nValueIn = coins->vout[txin.prevout.n].nValue;
nTotalIn += nValueIn;
int nConf = pindexPrev->nHeight - coins.nHeight + 1;
int nConf = pindexPrev->nHeight - coins->nHeight + 1;
dPriority += (double)nValueIn * nConf;
}

View File

@ -565,7 +565,7 @@ Value signrawtransaction(const Array& params, bool fHelp)
BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) {
const uint256& prevHash = txin.prevout.hash;
CCoins coins;
view.GetCoins(prevHash, coins); // this is certainly allowed to fail
view.AccessCoins(prevHash); // this is certainly allowed to fail
}
view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
@ -669,12 +669,12 @@ Value signrawtransaction(const Array& params, bool fHelp)
// Sign what we can:
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
CTxIn& txin = mergedTx.vin[i];
CCoins coins;
if (!view.GetCoins(txin.prevout.hash, coins) || !coins.IsAvailable(txin.prevout.n)) {
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
if (coins == NULL || !coins->IsAvailable(txin.prevout.n)) {
fComplete = false;
continue;
}
const CScript& prevPubKey = coins.vout[txin.prevout.n].scriptPubKey;
const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey;
txin.scriptSig.clear();
// Only sign SIGHASH_SINGLE if there's a corresponding output:
@ -732,9 +732,9 @@ Value sendrawtransaction(const Array& params, bool fHelp)
fOverrideFees = params[1].get_bool();
CCoinsViewCache &view = *pcoinsTip;
CCoins existingCoins;
const CCoins* existingCoins = view.AccessCoins(hashTx);
bool fHaveMempool = mempool.exists(hashTx);
bool fHaveChain = view.GetCoins(hashTx, existingCoins) && existingCoins.nHeight < 1000000000;
bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000;
if (!fHaveMempool && !fHaveChain) {
// push to local node and sync with wallets
CValidationState state;

View File

@ -509,8 +509,8 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
const CTransaction& tx2 = it2->second.GetTx();
assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull());
} else {
const CCoins &coins = pcoins->GetCoins(txin.prevout.hash);
assert(coins.IsAvailable(txin.prevout.n));
const CCoins* coins = pcoins->AccessCoins(txin.prevout.hash);
assert(coins && coins->IsAvailable(txin.prevout.n));
}
// Check whether its inputs are marked in mapNextTx.
std::map<COutPoint, CInPoint>::const_iterator it3 = mapNextTx.find(txin.prevout);