Browse Source

CValidationState framework

tags/v0.15.1
Pieter Wuille 8 years ago
parent
commit
ef3988ca36
8 changed files with 226 additions and 213 deletions
  1. 2
    1
      src/init.cpp
  2. 148
    175
      src/main.cpp
  3. 57
    27
      src/main.h
  4. 3
    2
      src/rpcmining.cpp
  5. 3
    2
      src/rpcrawtransaction.cpp
  6. 3
    1
      src/test/miner_tests.cpp
  7. 8
    4
      src/test/transaction_tests.cpp
  8. 2
    1
      src/walletdb.cpp

+ 2
- 1
src/init.cpp View File

@@ -936,7 +936,8 @@ bool AppInit2()

// scan for better chains in the block chain database, that are not yet connected in the active best chain
uiInterface.InitMessage(_("Importing blocks from block database..."));
if (!ConnectBestBlock())
CValidationState state;
if (!ConnectBestBlock(state))
strErrors << "Failed to connect best block";

CImportData *pimport = new CImportData();

+ 148
- 175
src/main.cpp View File

@@ -524,28 +524,28 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock)



bool CTransaction::CheckTransaction() const
bool CTransaction::CheckTransaction(CValidationState &state) const
{
// Basic checks that don't depend on any context
if (vin.empty())
return DoS(10, error("CTransaction::CheckTransaction() : vin empty"));
return state.DoS(10, error("CTransaction::CheckTransaction() : vin empty"));
if (vout.empty())
return DoS(10, error("CTransaction::CheckTransaction() : vout empty"));
return state.DoS(10, error("CTransaction::CheckTransaction() : vout empty"));
// Size limits
if (::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
return DoS(100, error("CTransaction::CheckTransaction() : size limits failed"));
return state.DoS(100, error("CTransaction::CheckTransaction() : size limits failed"));

// Check for negative or overflow output values
int64 nValueOut = 0;
BOOST_FOREACH(const CTxOut& txout, vout)
{
if (txout.nValue < 0)
return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative"));
return state.DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative"));
if (txout.nValue > MAX_MONEY)
return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high"));
return state.DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high"));
nValueOut += txout.nValue;
if (!MoneyRange(nValueOut))
return DoS(100, error("CTransaction::CheckTransaction() : txout total out of range"));
return state.DoS(100, error("CTransaction::CheckTransaction() : txout total out of range"));
}

// Check for duplicate inputs
@@ -553,20 +553,20 @@ bool CTransaction::CheckTransaction() const
BOOST_FOREACH(const CTxIn& txin, vin)
{
if (vInOutPoints.count(txin.prevout))
return false;
return state.DoS(100, error("CTransaction::CheckTransaction() : duplicate inputs"));
vInOutPoints.insert(txin.prevout);
}

if (IsCoinBase())
{
if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100)
return DoS(100, error("CTransaction::CheckTransaction() : coinbase script size"));
return state.DoS(100, error("CTransaction::CheckTransaction() : coinbase script size"));
}
else
{
BOOST_FOREACH(const CTxIn& txin, vin)
if (txin.prevout.IsNull())
return DoS(10, error("CTransaction::CheckTransaction() : prevout is null"));
return state.DoS(10, error("CTransaction::CheckTransaction() : prevout is null"));
}

return true;
@@ -633,18 +633,18 @@ void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins)
}
}

bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool fLimitFree,
bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckInputs, bool fLimitFree,
bool* pfMissingInputs)
{
if (pfMissingInputs)
*pfMissingInputs = false;

if (!tx.CheckTransaction())
if (!tx.CheckTransaction(state))
return error("CTxMemPool::accept() : CheckTransaction failed");

// Coinbase is only valid in a block, not as a loose transaction
if (tx.IsCoinBase())
return tx.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx"));
return state.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx"));

// To help v0.1.5 clients who would see it as a negative number
if ((int64)tx.nLockTime > std::numeric_limits<int>::max())
@@ -717,7 +717,7 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool fLimitFree,

// are the actual inputs available?
if (!tx.HaveInputs(view))
return error("CTxMemPool::accept() : inputs already spent");
return state.Invalid(error("CTxMemPool::accept() : inputs already spent"));

// Bring the best block into scope
view.GetBestBlock();
@@ -769,7 +769,7 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool fLimitFree,

// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
if (!tx.CheckInputs(view, true, SCRIPT_VERIFY_P2SH))
if (!tx.CheckInputs(state, view, true, SCRIPT_VERIFY_P2SH))
{
return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
}
@@ -798,9 +798,9 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool fLimitFree,
return true;
}

bool CTransaction::AcceptToMemoryPool(bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs)
bool CTransaction::AcceptToMemoryPool(CValidationState &state, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs)
{
return mempool.accept(*this, fCheckInputs, fLimitFree, pfMissingInputs);
return mempool.accept(state, *this, fCheckInputs, fLimitFree, pfMissingInputs);
}

bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx)
@@ -913,7 +913,8 @@ int CMerkleTx::GetBlocksToMaturity() const

bool CMerkleTx::AcceptToMemoryPool(bool fCheckInputs, bool fLimitFree)
{
return CTransaction::AcceptToMemoryPool(fCheckInputs, fLimitFree);
CValidationState state;
return CTransaction::AcceptToMemoryPool(state, fCheckInputs, fLimitFree);
}


@@ -1209,11 +1210,13 @@ void static InvalidBlockFound(CBlockIndex *pindex) {
pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex));
setBlockIndexValid.erase(pindex);
InvalidChainFound(pindex);
if (pindex->pnext)
ConnectBestBlock(); // reorganise away from the failed block
if (pindex->pnext) {
CValidationState stateDummy;
ConnectBestBlock(stateDummy); // reorganise away from the failed block
}
}

bool ConnectBestBlock() {
bool ConnectBestBlock(CValidationState &state) {
do {
CBlockIndex *pindexNewBest;

@@ -1252,7 +1255,8 @@ bool ConnectBestBlock() {
BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) {
if (fRequestShutdown)
break;
if (!SetBestChain(pindexSwitch))
CValidationState state;
if (!SetBestChain(state, pindexSwitch))
return false;
}
return true;
@@ -1315,22 +1319,20 @@ unsigned int CTransaction::GetP2SHSigOpCount(CCoinsViewCache& inputs) const
return nSigOps;
}

bool CTransaction::UpdateCoins(CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) const
bool CTransaction::UpdateCoins(CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) const
{
// mark inputs spent
if (!IsCoinBase()) {
BOOST_FOREACH(const CTxIn &txin, vin) {
CCoins &coins = inputs.GetCoins(txin.prevout.hash);
CTxInUndo undo;
if (!coins.Spend(txin.prevout, undo))
return error("UpdateCoins() : cannot spend input");
assert(coins.Spend(txin.prevout, undo));
txundo.vprevout.push_back(undo);
}
}

// add outputs
if (!inputs.SetCoins(txhash, CCoins(*this, nHeight)))
return error("UpdateCoins() : cannot update output");
assert(inputs.SetCoins(txhash, CCoins(*this, nHeight)));

return true;
}
@@ -1368,7 +1370,7 @@ bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned in
return CScriptCheck(txFrom, txTo, nIn, flags, nHashType)();
}

bool CTransaction::CheckInputs(CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, std::vector<CScriptCheck> *pvChecks) const
bool CTransaction::CheckInputs(CValidationState &state, CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, std::vector<CScriptCheck> *pvChecks) const
{
if (!IsCoinBase())
{
@@ -1378,7 +1380,7 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, bool fScriptChecks, unsi
// 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());
return state.Invalid(error("CheckInputs() : %s inputs unavailable", GetHash().ToString().substr(0,10).c_str()));

// While checking, GetBestBlock() refers to the parent block.
// This is also true for mempool checks.
@@ -1393,26 +1395,26 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, bool fScriptChecks, unsi
// If prev is coinbase, check that it's matured
if (coins.IsCoinBase()) {
if (nSpendHeight - coins.nHeight < COINBASE_MATURITY)
return error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight);
return state.Invalid(error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight));
}

// Check for negative or overflow input values
nValueIn += coins.vout[prevout.n].nValue;
if (!MoneyRange(coins.vout[prevout.n].nValue) || !MoneyRange(nValueIn))
return DoS(100, error("CheckInputs() : txin values out of range"));
return state.DoS(100, error("CheckInputs() : txin values out of range"));

}

if (nValueIn < GetValueOut())
return DoS(100, error("ChecktInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str()));
return state.DoS(100, error("ChecktInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str()));

// Tally transaction fees
int64 nTxFee = nValueIn - GetValueOut();
if (nTxFee < 0)
return DoS(100, error("CheckInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str()));
return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str()));
nFees += nTxFee;
if (!MoneyRange(nFees))
return DoS(100, error("CheckInputs() : nFees out of range"));
return state.DoS(100, error("CheckInputs() : nFees out of range"));

// The first loop above does all the inexpensive checks.
// Only if ALL inputs pass do we perform expensive ECDSA signature checks.
@@ -1432,7 +1434,7 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, bool fScriptChecks, unsi
pvChecks->push_back(CScriptCheck());
check.swap(pvChecks->back());
} else if (!check())
return DoS(100,false);
return state.DoS(100,false);
}
}
}
@@ -1441,56 +1443,9 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, bool fScriptChecks, unsi
}


bool CTransaction::ClientCheckInputs() const
{
if (IsCoinBase())
return false;

// Take over previous transactions' spent pointers
{
LOCK(mempool.cs);
int64 nValueIn = 0;
for (unsigned int i = 0; i < vin.size(); i++)
{
// Get prev tx from single transactions in memory
COutPoint prevout = vin[i].prevout;
if (!mempool.exists(prevout.hash))
return false;
CTransaction& txPrev = mempool.lookup(prevout.hash);

if (prevout.n >= txPrev.vout.size())
return false;

// Verify signature
if (!VerifySignature(CCoins(txPrev, -1), *this, i, SCRIPT_VERIFY_P2SH, 0))
return error("ConnectInputs() : VerifySignature failed");

///// this is redundant with the mempool.mapNextTx stuff,
///// not sure which I want to get rid of
///// this has to go away now that posNext is gone
// // Check for conflicts
// if (!txPrev.vout[prevout.n].posNext.IsNull())
// return error("ConnectInputs() : prev tx already used");
//
// // Flag outpoints as used
// txPrev.vout[prevout.n].posNext = posThisTx;

nValueIn += txPrev.vout[prevout.n].nValue;

if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn))
return error("ClientConnectInputs() : txin values out of range");
}
if (GetValueOut() > nValueIn)
return false;
}

return true;
}




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

@@ -1591,7 +1546,7 @@ void static FlushBlockFile()
}
}

bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);

static CCheckQueue<CScriptCheck> scriptcheckqueue(128);

@@ -1606,10 +1561,10 @@ void ThreadScriptCheckQuit() {
scriptcheckqueue.Quit();
}

bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck)
bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck)
{
// Check it again in case a previous version let a bad block in
if (!CheckBlock(!fJustCheck, !fJustCheck))
if (!CheckBlock(state, !fJustCheck, !fJustCheck))
return false;

// verify that the view's current state corresponds to the previous block
@@ -1674,12 +1629,12 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
nInputs += tx.vin.size();
nSigOps += tx.GetLegacySigOpCount();
if (nSigOps > MAX_BLOCK_SIGOPS)
return DoS(100, error("ConnectBlock() : too many sigops"));
return state.DoS(100, error("ConnectBlock() : too many sigops"));

if (!tx.IsCoinBase())
{
if (!tx.HaveInputs(view))
return DoS(100, error("ConnectBlock() : inputs missing/spent"));
return state.DoS(100, error("ConnectBlock() : inputs missing/spent"));

if (fStrictPayToScriptHash)
{
@@ -1688,19 +1643,19 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
// an incredibly-expensive-to-validate block.
nSigOps += tx.GetP2SHSigOpCount(view);
if (nSigOps > MAX_BLOCK_SIGOPS)
return DoS(100, error("ConnectBlock() : too many sigops"));
return state.DoS(100, error("ConnectBlock() : too many sigops"));
}

nFees += tx.GetValueIn(view)-tx.GetValueOut();

std::vector<CScriptCheck> vChecks;
if (!tx.CheckInputs(view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL))
if (!tx.CheckInputs(state, view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL))
return false;
control.Add(vChecks);
}

CTxUndo txundo;
if (!tx.UpdateCoins(view, txundo, pindex->nHeight, GetTxHash(i)))
if (!tx.UpdateCoins(state, view, txundo, pindex->nHeight, GetTxHash(i)))
return error("ConnectBlock() : UpdateInputs failed");
if (!tx.IsCoinBase())
blockundo.vtxundo.push_back(txundo);
@@ -1713,10 +1668,10 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
printf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)vtx.size(), 0.001 * nTime, 0.001 * nTime / vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1));

if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees))
return error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees));
return state.Invalid(error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees)));

if (!control.Wait())
return DoS(100, false);
return state.DoS(100, false);
int64 nTime2 = GetTimeMicros() - nStart;
if (fBenchmark)
printf("- Verify %u txins: %.2fms (%.3fms/txin)\n", nInputs - 1, 0.001 * nTime2, nInputs <= 1 ? 0 : 0.001 * nTime2 / (nInputs-1));
@@ -1729,10 +1684,10 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
{
if (pindex->GetUndoPos().IsNull()) {
CDiskBlockPos pos;
if (!FindUndoPos(pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40))
if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40))
return error("ConnectBlock() : FindUndoPos failed");
if (!blockundo.WriteToDisk(pos, pindex->pprev->GetBlockHash()))
return error("ConnectBlock() : CBlockUndo::WriteToDisk failed");
return state.Error(error("ConnectBlock() : CBlockUndo::WriteToDisk failed"));

// update nUndoPos in block index
pindex->nUndoPos = pos.nPos;
@@ -1743,15 +1698,16 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust

CDiskBlockIndex blockindex(pindex);
if (!pblocktree->WriteBlockIndex(blockindex))
return error("ConnectBlock() : WriteBlockIndex failed");
return state.Error(error("ConnectBlock() : WriteBlockIndex failed"));
}

if (fTxIndex)
pblocktree->WriteTxIndex(vPos);
if (!pblocktree->WriteTxIndex(vPos))
return state.Error(error("ConnectBlock() : WriteTxIndex failed"));

// add this block to the view's block chain
if (!view.SetBestBlock(pindex))
return false;
return state.Error();

// Watch for transactions paying to me
for (unsigned int i=0; i<vtx.size(); i++)
@@ -1760,7 +1716,7 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
return true;
}

bool SetBestChain(CBlockIndex* pindexNew)
bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
{
// All modifications to the coin state will be done in this cache.
// Only when all have succeeded, we push it to pcoinsTip.
@@ -1773,11 +1729,11 @@ bool SetBestChain(CBlockIndex* pindexNew)
{
while (plonger->nHeight > pfork->nHeight)
if (!(plonger = plonger->pprev))
return error("SetBestChain() : plonger->pprev is null");
return state.Error(error("SetBestChain() : plonger->pprev is null"));
if (pfork == plonger)
break;
if (!(pfork = pfork->pprev))
return error("SetBestChain() : pfork->pprev is null");
return state.Error(error("SetBestChain() : pfork->pprev is null"));
}

// List of what to disconnect (typically nothing)
@@ -1801,9 +1757,9 @@ bool SetBestChain(CBlockIndex* pindexNew)
BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) {
CBlock block;
if (!block.ReadFromDisk(pindex))
return error("SetBestBlock() : ReadFromDisk for disconnect failed");
return state.Error(error("SetBestBlock() : ReadFromDisk for disconnect failed"));
int64 nStart = GetTimeMicros();
if (!block.DisconnectBlock(pindex, view))
if (!block.DisconnectBlock(state, pindex, view))
return error("SetBestBlock() : DisconnectBlock %s failed", BlockHashStr(pindex->GetBlockHash()).c_str());
if (fBenchmark)
printf("- Disconnect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
@@ -1821,11 +1777,13 @@ bool SetBestChain(CBlockIndex* pindexNew)
BOOST_FOREACH(CBlockIndex *pindex, vConnect) {
CBlock block;
if (!block.ReadFromDisk(pindex))
return error("SetBestBlock() : ReadFromDisk for connect failed");
return state.Error(error("SetBestBlock() : ReadFromDisk for connect failed"));
int64 nStart = GetTimeMicros();
if (!block.ConnectBlock(pindex, view)) {
InvalidChainFound(pindexNew);
InvalidBlockFound(pindex);
if (!block.ConnectBlock(state, pindex, view)) {
if (state.IsInvalid()) {
InvalidChainFound(pindexNew);
InvalidBlockFound(pindex);
}
return error("SetBestBlock() : ConnectBlock %s failed", BlockHashStr(pindex->GetBlockHash()).c_str());
}
if (fBenchmark)
@@ -1840,7 +1798,7 @@ bool SetBestChain(CBlockIndex* pindexNew)
int64 nStart = GetTimeMicros();
int nModified = view.GetCacheSize();
if (!view.Flush())
return error("SetBestBlock() : unable to modify coin state");
return state.Error(error("SetBestBlock() : unable to modify coin state"));
int64 nTime = GetTimeMicros() - nStart;
if (fBenchmark)
printf("- Flush %i transactions: %.2fms (%.4fms/tx)\n", nModified, 0.001 * nTime, 0.001 * nTime / nModified);
@@ -1851,7 +1809,7 @@ bool SetBestChain(CBlockIndex* pindexNew)
FlushBlockFile();
pblocktree->Sync();
if (!pcoinsTip->Flush())
return false;
return state.Error();
}

// At this point, all changes have been done to the database.
@@ -1868,8 +1826,11 @@ bool SetBestChain(CBlockIndex* pindexNew)
pindex->pprev->pnext = pindex;

// Resurrect memory transactions that were in the disconnected branch
BOOST_FOREACH(CTransaction& tx, vResurrect)
tx.AcceptToMemoryPool(true, false);
BOOST_FOREACH(CTransaction& tx, vResurrect) {
// ignore validation errors in resurrected transactions
CValidationState stateDummy;
tx.AcceptToMemoryPool(stateDummy, true, false);
}

// Delete redundant memory transactions that are in the connected branch
BOOST_FOREACH(CTransaction& tx, vDelete) {
@@ -1926,17 +1887,17 @@ bool SetBestChain(CBlockIndex* pindexNew)
}


bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos)
bool CBlock::AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos)
{
// Check for duplicate
uint256 hash = GetHash();
if (mapBlockIndex.count(hash))
return error("AddToBlockIndex() : %s already exists", BlockHashStr(hash).c_str());
return state.Invalid(error("AddToBlockIndex() : %s already exists", BlockHashStr(hash).c_str()));

// Construct new block index object
CBlockIndex* pindexNew = new CBlockIndex(*this);
if (!pindexNew)
return error("AddToBlockIndex() : new CBlockIndex failed");
return state.Error(error("AddToBlockIndex() : new CBlockIndex failed"));
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
pindexNew->phashBlock = &((*mi).first);
map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(hashPrevBlock);
@@ -1954,10 +1915,11 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos)
pindexNew->nStatus = BLOCK_VALID_TRANSACTIONS | BLOCK_HAVE_DATA;
setBlockIndexValid.insert(pindexNew);

pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew));
if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew)))
return state.Error(error("AddToBlockIndex() : writing block index failed"));

// New best?
if (!ConnectBestBlock())
if (!ConnectBestBlock(state))
return false;

if (pindexNew == pindexBest)
@@ -1968,14 +1930,15 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos)
hashPrevBestCoinBase = GetTxHash(0);
}

pblocktree->Flush();
if (!pblocktree->Flush())
return state.Error("AddToBlockIndex() : failed to sync block tree");

uiInterface.NotifyBlocksChanged();
return true;
}


bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime, bool fKnown = false)
bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime, bool fKnown = false)
{
bool fUpdatedLast = false;

@@ -2017,19 +1980,19 @@ bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeigh
}
}
else
return error("FindBlockPos() : out of disk space");
return state.Error(error("FindBlockPos() : out of disk space"));
}
}

if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
return error("FindBlockPos() : cannot write updated block info");
return state.Error(error("FindBlockPos() : cannot write updated block info"));
if (fUpdatedLast)
pblocktree->WriteLastBlockFile(nLastBlockFile);

return true;
}

bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize)
bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize)
{
pos.nFile = nFile;

@@ -2040,15 +2003,15 @@ bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize)
pos.nPos = infoLastBlockFile.nUndoSize;
nNewSize = (infoLastBlockFile.nUndoSize += nAddSize);
if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
return error("FindUndoPos() : cannot write updated block info");
return state.Error(error("FindUndoPos() : cannot write updated block info"));
} else {
CBlockFileInfo info;
if (!pblocktree->ReadBlockFileInfo(nFile, info))
return error("FindUndoPos() : cannot read block info");
return state.Error(error("FindUndoPos() : cannot read block info"));
pos.nPos = info.nUndoSize;
nNewSize = (info.nUndoSize += nAddSize);
if (!pblocktree->WriteBlockFileInfo(nFile, info))
return error("FindUndoPos() : cannot write updated block info");
return state.Error(error("FindUndoPos() : cannot write updated block info"));
}

unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE;
@@ -2063,41 +2026,41 @@ bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize)
}
}
else
return error("FindUndoPos() : out of disk space");
return state.Error(error("FindUndoPos() : out of disk space"));
}

return true;
}


bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const
bool CBlock::CheckBlock(CValidationState &state, bool fCheckPOW, bool fCheckMerkleRoot) const
{
// These are checks that are independent of context
// that can be verified before saving an orphan block.

// Size limits
if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
return DoS(100, error("CheckBlock() : size limits failed"));
return state.DoS(100, error("CheckBlock() : size limits failed"));

// Check proof of work matches claimed amount
if (fCheckPOW && !CheckProofOfWork(GetHash(), nBits))
return DoS(50, error("CheckBlock() : proof of work failed"));
return state.DoS(50, error("CheckBlock() : proof of work failed"));

// Check timestamp
if (GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60)
return error("CheckBlock() : block timestamp too far in the future");
return state.Invalid(error("CheckBlock() : block timestamp too far in the future"));

// First transaction must be coinbase, the rest must not be
if (vtx.empty() || !vtx[0].IsCoinBase())
return DoS(100, error("CheckBlock() : first tx is not coinbase"));
return state.DoS(100, error("CheckBlock() : first tx is not coinbase"));
for (unsigned int i = 1; i < vtx.size(); i++)
if (vtx[i].IsCoinBase())
return DoS(100, error("CheckBlock() : more than one coinbase"));
return state.DoS(100, error("CheckBlock() : more than one coinbase"));

// Check transactions
BOOST_FOREACH(const CTransaction& tx, vtx)
if (!tx.CheckTransaction())
return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed"));
if (!tx.CheckTransaction(state))
return error("CheckBlock() : CheckTransaction failed");

// Build the merkle tree already. We need it anyway later, and it makes the
// block cache the transaction hashes, which means they don't need to be
@@ -2111,7 +2074,7 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const
uniqueTx.insert(GetTxHash(i));
}
if (uniqueTx.size() != vtx.size())
return DoS(100, error("CheckBlock() : duplicate transaction"));
return state.DoS(100, error("CheckBlock() : duplicate transaction"));

unsigned int nSigOps = 0;
BOOST_FOREACH(const CTransaction& tx, vtx)
@@ -2119,21 +2082,21 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const
nSigOps += tx.GetLegacySigOpCount();
}
if (nSigOps > MAX_BLOCK_SIGOPS)
return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"));
return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"));

// Check merkle root
if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree())
return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"));
return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"));

return true;
}

bool CBlock::AcceptBlock(CDiskBlockPos *dbp)
bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp)
{
// Check for duplicate
uint256 hash = GetHash();
if (mapBlockIndex.count(hash))
return error("AcceptBlock() : block already in mapBlockIndex");
return state.Invalid(error("AcceptBlock() : block already in mapBlockIndex"));

// Get prev block index
CBlockIndex* pindexPrev = NULL;
@@ -2141,26 +2104,26 @@ bool CBlock::AcceptBlock(CDiskBlockPos *dbp)
if (hash != hashGenesisBlock) {
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashPrevBlock);
if (mi == mapBlockIndex.end())
return DoS(10, error("AcceptBlock() : prev block not found"));
return state.DoS(10, error("AcceptBlock() : prev block not found"));
pindexPrev = (*mi).second;
nHeight = pindexPrev->nHeight+1;

// Check proof of work
if (nBits != GetNextWorkRequired(pindexPrev, this))
return DoS(100, error("AcceptBlock() : incorrect proof of work"));
return state.DoS(100, error("AcceptBlock() : incorrect proof of work"));

// Check timestamp against prev
if (GetBlockTime() <= pindexPrev->GetMedianTimePast())
return error("AcceptBlock() : block's timestamp is too early");
return state.Invalid(error("AcceptBlock() : block's timestamp is too early"));

// Check that all transactions are finalized
BOOST_FOREACH(const CTransaction& tx, vtx)
if (!tx.IsFinal(nHeight, GetBlockTime()))
return DoS(10, error("AcceptBlock() : contains a non-final transaction"));
return state.DoS(10, error("AcceptBlock() : contains a non-final transaction"));

// Check that the block chain matches the known block chain up to a checkpoint
if (!Checkpoints::CheckBlock(nHeight, hash))
return DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight));
return state.DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight));

// Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded:
if (nVersion < 2)
@@ -2168,7 +2131,7 @@ bool CBlock::AcceptBlock(CDiskBlockPos *dbp)
if ((!fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) ||
(fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100)))
{
return error("AcceptBlock() : rejected nVersion=1 block");
return state.Invalid(error("AcceptBlock() : rejected nVersion=1 block"));
}
}
// Enforce block.nVersion=2 rule that the coinbase starts with serialized block height
@@ -2180,7 +2143,7 @@ bool CBlock::AcceptBlock(CDiskBlockPos *dbp)
{
CScript expect = CScript() << nHeight;
if (!std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin()))
return DoS(100, error("AcceptBlock() : block height mismatch in coinbase"));
return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase"));
}
}
}
@@ -2190,12 +2153,12 @@ bool CBlock::AcceptBlock(CDiskBlockPos *dbp)
CDiskBlockPos blockPos;
if (dbp != NULL)
blockPos = *dbp;
if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, nTime, dbp != NULL))
if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, nTime, dbp != NULL))
return error("AcceptBlock() : FindBlockPos failed");
if (dbp == NULL)
if (!WriteToDisk(blockPos))
return error("AcceptBlock() : WriteToDisk failed");
if (!AddToBlockIndex(blockPos))
return state.Error(error("AcceptBlock() : WriteToDisk failed"));
if (!AddToBlockIndex(state, blockPos))
return error("AcceptBlock() : AddToBlockIndex failed");

// Relay inventory, but don't relay old inventory during initial block download
@@ -2223,17 +2186,17 @@ bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, uns
return (nFound >= nRequired);
}

bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp)
bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp)
{
// Check for duplicate
uint256 hash = pblock->GetHash();
if (mapBlockIndex.count(hash))
return error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, BlockHashStr(hash).c_str());
return state.Invalid(error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, BlockHashStr(hash).c_str()));
if (mapOrphanBlocks.count(hash))
return error("ProcessBlock() : already have block (orphan) %s", BlockHashStr(hash).c_str());
return state.Invalid(error("ProcessBlock() : already have block (orphan) %s", BlockHashStr(hash).c_str()));

// Preliminary checks
if (!pblock->CheckBlock())
if (!pblock->CheckBlock(state))
return error("ProcessBlock() : CheckBlock FAILED");

CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex);
@@ -2243,9 +2206,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp)
int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime;
if (deltaTime < 0)
{
if (pfrom)
pfrom->Misbehaving(100);
return error("ProcessBlock() : block with timestamp before last checkpoint");
return state.DoS(100, error("ProcessBlock() : block with timestamp before last checkpoint"));
}
CBigNum bnNewBlock;
bnNewBlock.SetCompact(pblock->nBits);
@@ -2253,9 +2214,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp)
bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime));
if (bnNewBlock > bnRequired)
{
if (pfrom)
pfrom->Misbehaving(100);
return error("ProcessBlock() : block with too little proof-of-work");
return state.DoS(100, error("ProcessBlock() : block with too little proof-of-work"));
}
}

@@ -2278,7 +2237,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp)
}

// Store to disk
if (!pblock->AcceptBlock(dbp))
if (!pblock->AcceptBlock(state, dbp))
return error("ProcessBlock() : AcceptBlock FAILED");

// Recursively process any orphan blocks that depended on this one
@@ -2292,7 +2251,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp)
++mi)
{
CBlock* pblockOrphan = (*mi).second;
if (pblockOrphan->AcceptBlock())
if (pblockOrphan->AcceptBlock(state))
vWorkQueue.push_back(pblockOrphan->GetHash());
mapOrphanBlocks.erase(pblockOrphan->GetHash());
delete pblockOrphan;
@@ -2621,6 +2580,7 @@ bool VerifyDB() {
CBlockIndex* pindexState = pindexBest;
CBlockIndex* pindexFailure = NULL;
int nGoodTransactions = 0;
CValidationState state;
for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev)
{
if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth)
@@ -2630,7 +2590,7 @@ bool VerifyDB() {
if (!block.ReadFromDisk(pindex))
return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
// check level 1: verify block validity
if (nCheckLevel >= 1 && !block.CheckBlock())
if (nCheckLevel >= 1 && !block.CheckBlock(state))
return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
// check level 2: verify undo validity
if (nCheckLevel >= 2 && pindex) {
@@ -2644,7 +2604,7 @@ bool VerifyDB() {
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= 2*nCoinCacheSize + 32000) {
bool fClean = true;
if (!block.DisconnectBlock(pindex, coins, &fClean))
if (!block.DisconnectBlock(state, pindex, coins, &fClean))
return error("VerifyDB() : *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
pindexState = pindex->pprev;
if (!fClean) {
@@ -2665,7 +2625,7 @@ bool VerifyDB() {
CBlock block;
if (!block.ReadFromDisk(pindex))
return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
if (!block.ConnectBlock(pindex, coins))
if (!block.ConnectBlock(state, pindex, coins))
return error("VerifyDB() : *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
}
}
@@ -2746,11 +2706,12 @@ bool LoadBlockIndex()
// Start new block file
unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
CDiskBlockPos blockPos;
if (!FindBlockPos(blockPos, nBlockSize+8, 0, block.nTime))
CValidationState state;
if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.nTime))
return error("AcceptBlock() : FindBlockPos failed");
if (!block.WriteToDisk(blockPos))
return error("LoadBlockIndex() : writing genesis block to disk failed");
if (!block.AddToBlockIndex(blockPos))
if (!block.AddToBlockIndex(state, blockPos))
return error("LoadBlockIndex() : genesis block not accepted");
}

@@ -2880,8 +2841,11 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
LOCK(cs_main);
if (dbp)
dbp->nPos = nBlockPos;
if (ProcessBlock(NULL, &block, dbp))
CValidationState state;
if (ProcessBlock(state, NULL, &block, dbp))
nLoaded++;
if (state.IsError())
break;
}
} catch (std::exception &e) {
printf("%s() : Deserialize or I/O error caught during load\n", __PRETTY_FUNCTION__);
@@ -3457,7 +3421,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
pfrom->AddInventoryKnown(inv);

bool fMissingInputs = false;
if (tx.AcceptToMemoryPool(true, true, &fMissingInputs))
CValidationState state;
if (tx.AcceptToMemoryPool(state, true, true, &fMissingInputs))
{
RelayTransaction(tx, inv.hash, vMsg);
mapAlreadyAskedFor.erase(inv);
@@ -3478,7 +3443,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
CInv inv(MSG_TX, tx.GetHash());
bool fMissingInputs2 = false;

if (tx.AcceptToMemoryPool(true, true, &fMissingInputs2))
if (tx.AcceptToMemoryPool(state, true, true, &fMissingInputs2))
{
printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str());
RelayTransaction(tx, inv.hash, vMsg);
@@ -3507,7 +3472,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
if (nEvicted > 0)
printf("mapOrphan overflow, removed %u tx\n", nEvicted);
}
if (tx.nDoS) pfrom->Misbehaving(tx.nDoS);
int nDoS;
if (state.IsInvalid(nDoS))
pfrom->Misbehaving(nDoS);
}


@@ -3522,9 +3489,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
CInv inv(MSG_BLOCK, block.GetHash());
pfrom->AddInventoryKnown(inv);

if (ProcessBlock(pfrom, &block))
CValidationState state;
if (ProcessBlock(state, pfrom, &block))
mapAlreadyAskedFor.erase(inv);
if (block.nDoS) pfrom->Misbehaving(block.nDoS);
int nDoS;
if (state.IsInvalid(nDoS))
pfrom->Misbehaving(nDoS);
}


@@ -4274,12 +4244,13 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey)
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
continue;

if (!tx.CheckInputs(viewTemp, true, SCRIPT_VERIFY_P2SH))
CValidationState state;
if (!tx.CheckInputs(state, viewTemp, true, SCRIPT_VERIFY_P2SH))
continue;

CTxUndo txundo;
uint256 hash = tx.GetHash();
if (!tx.UpdateCoins(viewTemp, txundo, pindexPrev->nHeight+1, hash))
if (!tx.UpdateCoins(state, viewTemp, txundo, pindexPrev->nHeight+1, hash))
continue;

// push changes from the second layer cache to the first one
@@ -4337,7 +4308,8 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey)
indexDummy.pprev = pindexPrev;
indexDummy.nHeight = pindexPrev->nHeight + 1;
CCoinsViewCache viewNew(*pcoinsTip, true);
if (!pblock->ConnectBlock(&indexDummy, viewNew, true))
CValidationState state;
if (!pblock->ConnectBlock(state, &indexDummy, viewNew, true))
throw std::runtime_error("CreateNewBlock() : ConnectBlock failed");
}

@@ -4439,7 +4411,8 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey)
}

// Process this block the same as if we had received it from another node
if (!ProcessBlock(NULL, pblock))
CValidationState state;
if (!ProcessBlock(state, NULL, pblock))
return error("BitcoinMiner : ProcessBlock, block not accepted");
}


+ 57
- 27
src/main.h View File

@@ -112,9 +112,11 @@ class CTxUndo;
class CCoinsView;
class CCoinsViewCache;
class CScriptCheck;
class CValidationState;

struct CBlockTemplate;


/** Register a wallet to receive updates from core */
void RegisterWallet(CWallet* pwalletIn);
/** Unregister a wallet from core */
@@ -122,7 +124,7 @@ void UnregisterWallet(CWallet* pwalletIn);
/** Push an updated transaction to all registered wallets */
void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false);
/** Process an incoming block */
bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp = NULL);
bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp = NULL);
/** Check whether enough disk space is available for an incoming block */
bool CheckDiskSpace(uint64 nAdditionalBytes = 0);
/** Open a block file (blk?????.dat) */
@@ -172,9 +174,9 @@ std::string GetWarnings(std::string strFor);
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false);
/** Connect/disconnect blocks until pindexNew is the new tip of the active block chain */
bool SetBestChain(CBlockIndex* pindexNew);
bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew);
/** Find the best known block, and make it the tip of the block chain */
bool ConnectBestBlock();
bool ConnectBestBlock(CValidationState &state);
/** Create a new block index entry for a given block hash */
CBlockIndex * InsertBlockIndex(uint256 hash);
/** Verify a signature */
@@ -454,7 +456,6 @@ public:




enum GetMinFee_mode
{
GMF_BLOCK,
@@ -474,10 +475,6 @@ public:
std::vector<CTxOut> vout;
unsigned int nLockTime;

// Denial-of-service detection:
mutable int nDoS;
bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; }

CTransaction()
{
SetNull();
@@ -498,7 +495,6 @@ public:
vin.clear();
vout.clear();
nLockTime = 0;
nDoS = 0; // Denial-of-service prevention
}

bool IsNull() const
@@ -654,27 +650,24 @@ public:
}


// Do all possible client-mode checks
bool ClientCheckInputs() const;

// Check whether all prevouts of this transaction are present in the UTXO set represented by view
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. If pvChecks is not NULL, script checks are pushed onto it
// instead of being performed inline.
bool CheckInputs(CCoinsViewCache &view, bool fScriptChecks = true,
bool CheckInputs(CValidationState &state, CCoinsViewCache &view, bool fScriptChecks = true,
unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC,
std::vector<CScriptCheck> *pvChecks = NULL) const;

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

// Context-independent validity checks
bool CheckTransaction() const;
bool CheckTransaction(CValidationState &state) const;

// Try to accept this transaction into the memory pool
bool AcceptToMemoryPool(bool fCheckInputs=true, bool fLimitFree = true, bool* pfMissingInputs=NULL);
bool AcceptToMemoryPool(CValidationState &state, bool fCheckInputs=true, bool fLimitFree = true, bool* pfMissingInputs=NULL);

protected:
static const CTxOut &GetOutputFor(const CTxIn& input, CCoinsViewCache& mapInputs);
@@ -1320,10 +1313,6 @@ public:
// memory only
mutable std::vector<uint256> vMerkleTree;

// Denial-of-service detection:
mutable int nDoS;
bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; }

CBlock()
{
SetNull();
@@ -1346,7 +1335,6 @@ public:
CBlockHeader::SetNull();
vtx.clear();
vMerkleTree.clear();
nDoS = 0;
}

CBlockHeader GetBlockHeader() const
@@ -1494,23 +1482,23 @@ public:
* In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean
* will be true if no problems were found. Otherwise, the return value will be false in case
* of problems. Note that in any case, coins may be modified. */
bool DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &coins, bool *pfClean = NULL);
bool DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &coins, bool *pfClean = NULL);

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

// Read a block from disk
bool ReadFromDisk(const CBlockIndex* pindex);

// Add this block to the block index, and if necessary, switch the active block chain to this
bool AddToBlockIndex(const CDiskBlockPos &pos);
bool AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos);

// Context-independent validity checks
bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true) const;
bool CheckBlock(CValidationState &state, bool fCheckPOW=true, bool fCheckMerkleRoot=true) const;

// Store block on disk
// if dbp is provided, the file is known to already reside on disk
bool AcceptBlock(CDiskBlockPos *dbp = NULL);
bool AcceptBlock(CValidationState &state, CDiskBlockPos *dbp = NULL);
};


@@ -1877,6 +1865,48 @@ public:
}
};

/** Capture information about block/transaction validation */
class CValidationState {
private:
enum mode_state {
MODE_VALID, // everything ok
MODE_INVALID, // network rule violation (DoS value may be set)
MODE_ERROR, // run-time error
} mode;
int nDoS;
public:
CValidationState() : mode(MODE_VALID), nDoS(0) {}
bool DoS(int level, bool ret = false) {
if (mode == MODE_ERROR)
return ret;
nDoS += level;
mode = MODE_INVALID;
return ret;
}
bool Invalid(bool ret = false) {
return DoS(0, ret);
}
bool Error(bool ret = false) {
mode = MODE_ERROR;
return ret;
}
bool IsValid() {
return mode == MODE_VALID;
}
bool IsInvalid() {
return mode == MODE_INVALID;
}
bool IsError() {
return mode == MODE_ERROR;
}
bool IsInvalid(int &nDoSOut) {
if (IsInvalid()) {
nDoSOut = nDoS;
return true;
}
return false;
}
};



@@ -2025,7 +2055,7 @@ public:
std::map<uint256, CTransaction> mapTx;
std::map<COutPoint, CInPoint> mapNextTx;

bool accept(CTransaction &tx, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs);
bool accept(CValidationState &state, CTransaction &tx, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs);
bool addUnchecked(const uint256& hash, CTransaction &tx);
bool remove(const CTransaction &tx, bool fRecursive = false);
bool removeConflicts(const CTransaction &tx);

+ 3
- 2
src/rpcmining.cpp View File

@@ -365,9 +365,10 @@ Value submitblock(const Array& params, bool fHelp)
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
}

bool fAccepted = ProcessBlock(NULL, &pblock);
CValidationState state;
bool fAccepted = ProcessBlock(state, NULL, &pblock);
if (!fAccepted)
return "rejected";
return "rejected"; // TODO: report validation state

return Value::null;
}

+ 3
- 2
src/rpcrawtransaction.cpp View File

@@ -546,8 +546,9 @@ Value sendrawtransaction(const Array& params, bool fHelp)
fHave = view.GetCoins(hashTx, existingCoins);
if (!fHave) {
// push to local node
if (!tx.AcceptToMemoryPool(true, false))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected");
CValidationState state;
if (!tx.AcceptToMemoryPool(state, true, false))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); // TODO: report validation state
}
}
if (fHave) {

+ 3
- 1
src/test/miner_tests.cpp View File

@@ -73,7 +73,9 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
txFirst.push_back(new CTransaction(pblock->vtx[0]));
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
pblock->nNonce = blockinfo[i].nonce;
assert(ProcessBlock(NULL, pblock));
CValidationState state;
BOOST_CHECK(ProcessBlock(state, NULL, pblock));
BOOST_CHECK(state.IsValid());
pblock->hashPrevBlock = pblock->GetHash();
}
delete pblocktemplate;

+ 8
- 4
src/test/transaction_tests.cpp View File

@@ -66,7 +66,9 @@ BOOST_AUTO_TEST_CASE(tx_valid)
CTransaction tx;
stream >> tx;

BOOST_CHECK_MESSAGE(tx.CheckTransaction(), strTest);
CValidationState state;
BOOST_CHECK_MESSAGE(tx.CheckTransaction(state), strTest);
BOOST_CHECK(state.IsValid());

for (unsigned int i = 0; i < tx.vin.size(); i++)
{
@@ -133,7 +135,8 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
CTransaction tx;
stream >> tx;

fValid = tx.CheckTransaction();
CValidationState state;
fValid = tx.CheckTransaction(state) && state.IsValid();

for (unsigned int i = 0; i < tx.vin.size() && fValid; i++)
{
@@ -159,11 +162,12 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests)
CDataStream stream(vch, SER_DISK, CLIENT_VERSION);
CTransaction tx;
stream >> tx;
BOOST_CHECK_MESSAGE(tx.CheckTransaction(), "Simple deserialized transaction should be valid.");
CValidationState state;
BOOST_CHECK_MESSAGE(tx.CheckTransaction(state) && state.IsValid(), "Simple deserialized transaction should be valid.");

// Check that duplicate txins fail
tx.vin.push_back(tx.vin[0]);
BOOST_CHECK_MESSAGE(!tx.CheckTransaction(), "Transaction with duplicate txins should be invalid.");
BOOST_CHECK_MESSAGE(!tx.CheckTransaction(state) || !state.IsValid(), "Transaction with duplicate txins should be invalid.");
}

//

+ 2
- 1
src/walletdb.cpp View File

@@ -203,7 +203,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
ssKey >> hash;
CWalletTx& wtx = pwallet->mapWallet[hash];
ssValue >> wtx;
if (wtx.CheckTransaction() && (wtx.GetHash() == hash))
CValidationState state;
if (wtx.CheckTransaction(state) && (wtx.GetHash() == hash) && state.IsValid())
wtx.BindWallet(pwallet);
else
{

Loading…
Cancel
Save