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() {
// 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
}
}
+ 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");
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");
}
}
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;
// 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);
}
(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)) {
return true;
}
-bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsView &view)
+bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view)
{
assert(pindex == view.GetBestBlock());
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
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))
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");
}
}
class CCoins;
class CTxUndo;
class CCoinsView;
+class CCoinsViewCache;
void RegisterWallet(CWallet* pwalletIn);
void UnregisterWallet(CWallet* pwalletIn);
@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
@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)
@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)
{
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;
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);
};
}
// 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);
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.