mirror of
https://github.com/kvazar-network/kevacoin.git
synced 2025-01-11 23:58:18 +00:00
Merge pull request #2224 from sipa/valstate
Improve error handling during validation
This commit is contained in:
commit
db3b4ade7b
@ -187,9 +187,9 @@ bool AppInit(int argc, char* argv[])
|
|||||||
fRet = AppInit2();
|
fRet = AppInit2();
|
||||||
}
|
}
|
||||||
catch (std::exception& e) {
|
catch (std::exception& e) {
|
||||||
PrintException(&e, "AppInit()");
|
PrintExceptionContinue(&e, "AppInit()");
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
PrintException(NULL, "AppInit()");
|
PrintExceptionContinue(NULL, "AppInit()");
|
||||||
}
|
}
|
||||||
if (!fRet)
|
if (!fRet)
|
||||||
Shutdown(NULL);
|
Shutdown(NULL);
|
||||||
@ -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
|
// 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..."));
|
uiInterface.InitMessage(_("Importing blocks from block database..."));
|
||||||
if (!ConnectBestBlock())
|
CValidationState state;
|
||||||
|
if (!ConnectBestBlock(state))
|
||||||
strErrors << "Failed to connect best block";
|
strErrors << "Failed to connect best block";
|
||||||
|
|
||||||
CImportData *pimport = new CImportData();
|
CImportData *pimport = new CImportData();
|
||||||
|
@ -12,6 +12,18 @@
|
|||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
|
void HandleError(const leveldb::Status &status) throw(leveldb_error) {
|
||||||
|
if (status.ok())
|
||||||
|
return;
|
||||||
|
if (status.IsCorruption())
|
||||||
|
throw leveldb_error("Database corrupted");
|
||||||
|
if (status.IsIOError())
|
||||||
|
throw leveldb_error("Database I/O error");
|
||||||
|
if (status.IsNotFound())
|
||||||
|
throw leveldb_error("Database entry missing");
|
||||||
|
throw leveldb_error("Unknown database error");
|
||||||
|
}
|
||||||
|
|
||||||
static leveldb::Options GetOptions(size_t nCacheSize) {
|
static leveldb::Options GetOptions(size_t nCacheSize) {
|
||||||
leveldb::Options options;
|
leveldb::Options options;
|
||||||
options.block_cache = leveldb::NewLRUCache(nCacheSize / 2);
|
options.block_cache = leveldb::NewLRUCache(nCacheSize / 2);
|
||||||
@ -57,12 +69,12 @@ CLevelDB::~CLevelDB() {
|
|||||||
options.env = NULL;
|
options.env = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CLevelDB::WriteBatch(CLevelDBBatch &batch, bool fSync) {
|
bool CLevelDB::WriteBatch(CLevelDBBatch &batch, bool fSync) throw(leveldb_error) {
|
||||||
leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch);
|
leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
printf("LevelDB write failure: %s\n", status.ToString().c_str());
|
printf("LevelDB write failure: %s\n", status.ToString().c_str());
|
||||||
|
HandleError(status);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,14 @@
|
|||||||
|
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
|
class leveldb_error : public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
leveldb_error(const std::string &msg) : std::runtime_error(msg) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
void HandleError(const leveldb::Status &status) throw(leveldb_error);
|
||||||
|
|
||||||
// Batch of changes queued to be written to a CLevelDB
|
// Batch of changes queued to be written to a CLevelDB
|
||||||
class CLevelDBBatch
|
class CLevelDBBatch
|
||||||
{
|
{
|
||||||
@ -72,7 +80,7 @@ public:
|
|||||||
CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory = false, bool fWipe = false);
|
CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory = false, bool fWipe = false);
|
||||||
~CLevelDB();
|
~CLevelDB();
|
||||||
|
|
||||||
template<typename K, typename V> bool Read(const K& key, V& value) {
|
template<typename K, typename V> bool Read(const K& key, V& value) throw(leveldb_error) {
|
||||||
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
||||||
ssKey.reserve(ssKey.GetSerializeSize(key));
|
ssKey.reserve(ssKey.GetSerializeSize(key));
|
||||||
ssKey << key;
|
ssKey << key;
|
||||||
@ -84,6 +92,7 @@ public:
|
|||||||
if (status.IsNotFound())
|
if (status.IsNotFound())
|
||||||
return false;
|
return false;
|
||||||
printf("LevelDB read failure: %s\n", status.ToString().c_str());
|
printf("LevelDB read failure: %s\n", status.ToString().c_str());
|
||||||
|
HandleError(status);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION);
|
CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION);
|
||||||
@ -94,13 +103,13 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename K, typename V> bool Write(const K& key, const V& value, bool fSync = false) {
|
template<typename K, typename V> bool Write(const K& key, const V& value, bool fSync = false) throw(leveldb_error) {
|
||||||
CLevelDBBatch batch;
|
CLevelDBBatch batch;
|
||||||
batch.Write(key, value);
|
batch.Write(key, value);
|
||||||
return WriteBatch(batch, fSync);
|
return WriteBatch(batch, fSync);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename K> bool Exists(const K& key) {
|
template<typename K> bool Exists(const K& key) throw(leveldb_error) {
|
||||||
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
||||||
ssKey.reserve(ssKey.GetSerializeSize(key));
|
ssKey.reserve(ssKey.GetSerializeSize(key));
|
||||||
ssKey << key;
|
ssKey << key;
|
||||||
@ -112,24 +121,25 @@ public:
|
|||||||
if (status.IsNotFound())
|
if (status.IsNotFound())
|
||||||
return false;
|
return false;
|
||||||
printf("LevelDB read failure: %s\n", status.ToString().c_str());
|
printf("LevelDB read failure: %s\n", status.ToString().c_str());
|
||||||
|
HandleError(status);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename K> bool Erase(const K& key, bool fSync = false) {
|
template<typename K> bool Erase(const K& key, bool fSync = false) throw(leveldb_error) {
|
||||||
CLevelDBBatch batch;
|
CLevelDBBatch batch;
|
||||||
batch.Erase(key);
|
batch.Erase(key);
|
||||||
return WriteBatch(batch, fSync);
|
return WriteBatch(batch, fSync);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WriteBatch(CLevelDBBatch &batch, bool fSync = false);
|
bool WriteBatch(CLevelDBBatch &batch, bool fSync = false) throw(leveldb_error);
|
||||||
|
|
||||||
// not available for LevelDB; provide for compatibility with BDB
|
// not available for LevelDB; provide for compatibility with BDB
|
||||||
bool Flush() {
|
bool Flush() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Sync() {
|
bool Sync() throw(leveldb_error) {
|
||||||
CLevelDBBatch batch;
|
CLevelDBBatch batch;
|
||||||
return WriteBatch(batch, true);
|
return WriteBatch(batch, true);
|
||||||
}
|
}
|
||||||
@ -141,4 +151,3 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
#endif // BITCOIN_LEVELDB_H
|
#endif // BITCOIN_LEVELDB_H
|
||||||
|
|
393
src/main.cpp
393
src/main.cpp
@ -515,28 +515,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
|
// Basic checks that don't depend on any context
|
||||||
if (vin.empty())
|
if (vin.empty())
|
||||||
return DoS(10, error("CTransaction::CheckTransaction() : vin empty"));
|
return state.DoS(10, error("CTransaction::CheckTransaction() : vin empty"));
|
||||||
if (vout.empty())
|
if (vout.empty())
|
||||||
return DoS(10, error("CTransaction::CheckTransaction() : vout empty"));
|
return state.DoS(10, error("CTransaction::CheckTransaction() : vout empty"));
|
||||||
// Size limits
|
// Size limits
|
||||||
if (::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
|
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
|
// Check for negative or overflow output values
|
||||||
int64 nValueOut = 0;
|
int64 nValueOut = 0;
|
||||||
BOOST_FOREACH(const CTxOut& txout, vout)
|
BOOST_FOREACH(const CTxOut& txout, vout)
|
||||||
{
|
{
|
||||||
if (txout.nValue < 0)
|
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)
|
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;
|
nValueOut += txout.nValue;
|
||||||
if (!MoneyRange(nValueOut))
|
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
|
// Check for duplicate inputs
|
||||||
@ -544,20 +544,20 @@ bool CTransaction::CheckTransaction() const
|
|||||||
BOOST_FOREACH(const CTxIn& txin, vin)
|
BOOST_FOREACH(const CTxIn& txin, vin)
|
||||||
{
|
{
|
||||||
if (vInOutPoints.count(txin.prevout))
|
if (vInOutPoints.count(txin.prevout))
|
||||||
return false;
|
return state.DoS(100, error("CTransaction::CheckTransaction() : duplicate inputs"));
|
||||||
vInOutPoints.insert(txin.prevout);
|
vInOutPoints.insert(txin.prevout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsCoinBase())
|
if (IsCoinBase())
|
||||||
{
|
{
|
||||||
if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100)
|
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
|
else
|
||||||
{
|
{
|
||||||
BOOST_FOREACH(const CTxIn& txin, vin)
|
BOOST_FOREACH(const CTxIn& txin, vin)
|
||||||
if (txin.prevout.IsNull())
|
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;
|
return true;
|
||||||
@ -624,18 +624,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)
|
bool* pfMissingInputs)
|
||||||
{
|
{
|
||||||
if (pfMissingInputs)
|
if (pfMissingInputs)
|
||||||
*pfMissingInputs = false;
|
*pfMissingInputs = false;
|
||||||
|
|
||||||
if (!tx.CheckTransaction())
|
if (!tx.CheckTransaction(state))
|
||||||
return error("CTxMemPool::accept() : CheckTransaction failed");
|
return error("CTxMemPool::accept() : CheckTransaction failed");
|
||||||
|
|
||||||
// Coinbase is only valid in a block, not as a loose transaction
|
// Coinbase is only valid in a block, not as a loose transaction
|
||||||
if (tx.IsCoinBase())
|
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
|
// To help v0.1.5 clients who would see it as a negative number
|
||||||
if ((int64)tx.nLockTime > std::numeric_limits<int>::max())
|
if ((int64)tx.nLockTime > std::numeric_limits<int>::max())
|
||||||
@ -708,7 +708,7 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool fLimitFree,
|
|||||||
|
|
||||||
// are the actual inputs available?
|
// are the actual inputs available?
|
||||||
if (!tx.HaveInputs(view))
|
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
|
// Bring the best block into scope
|
||||||
view.GetBestBlock();
|
view.GetBestBlock();
|
||||||
@ -760,7 +760,7 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool fLimitFree,
|
|||||||
|
|
||||||
// Check against previous transactions
|
// Check against previous transactions
|
||||||
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
// 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());
|
return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
|
||||||
}
|
}
|
||||||
@ -789,9 +789,13 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool fLimitFree,
|
|||||||
return true;
|
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);
|
try {
|
||||||
|
return mempool.accept(state, *this, fCheckInputs, fLimitFree, pfMissingInputs);
|
||||||
|
} catch(std::runtime_error &e) {
|
||||||
|
return state.Abort(_("System error: ") + e.what());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx)
|
bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx)
|
||||||
@ -904,7 +908,8 @@ int CMerkleTx::GetBlocksToMaturity() const
|
|||||||
|
|
||||||
bool CMerkleTx::AcceptToMemoryPool(bool fCheckInputs, bool fLimitFree)
|
bool CMerkleTx::AcceptToMemoryPool(bool fCheckInputs, bool fLimitFree)
|
||||||
{
|
{
|
||||||
return CTransaction::AcceptToMemoryPool(fCheckInputs, fLimitFree);
|
CValidationState state;
|
||||||
|
return CTransaction::AcceptToMemoryPool(state, fCheckInputs, fLimitFree);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1200,11 +1205,13 @@ void static InvalidBlockFound(CBlockIndex *pindex) {
|
|||||||
pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex));
|
pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex));
|
||||||
setBlockIndexValid.erase(pindex);
|
setBlockIndexValid.erase(pindex);
|
||||||
InvalidChainFound(pindex);
|
InvalidChainFound(pindex);
|
||||||
if (pindex->pnext)
|
if (pindex->pnext) {
|
||||||
ConnectBestBlock(); // reorganise away from the failed block
|
CValidationState stateDummy;
|
||||||
|
ConnectBestBlock(stateDummy); // reorganise away from the failed block
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConnectBestBlock() {
|
bool ConnectBestBlock(CValidationState &state) {
|
||||||
do {
|
do {
|
||||||
CBlockIndex *pindexNewBest;
|
CBlockIndex *pindexNewBest;
|
||||||
|
|
||||||
@ -1243,8 +1250,13 @@ bool ConnectBestBlock() {
|
|||||||
BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) {
|
BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) {
|
||||||
if (fRequestShutdown)
|
if (fRequestShutdown)
|
||||||
break;
|
break;
|
||||||
if (!SetBestChain(pindexSwitch))
|
CValidationState state;
|
||||||
return false;
|
try {
|
||||||
|
if (!SetBestChain(state, pindexSwitch))
|
||||||
|
return false;
|
||||||
|
} catch(std::runtime_error &e) {
|
||||||
|
return state.Abort(_("System error: ") + e.what());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1306,22 +1318,20 @@ unsigned int CTransaction::GetP2SHSigOpCount(CCoinsViewCache& inputs) const
|
|||||||
return nSigOps;
|
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
|
// mark inputs spent
|
||||||
if (!IsCoinBase()) {
|
if (!IsCoinBase()) {
|
||||||
BOOST_FOREACH(const CTxIn &txin, vin) {
|
BOOST_FOREACH(const CTxIn &txin, vin) {
|
||||||
CCoins &coins = inputs.GetCoins(txin.prevout.hash);
|
CCoins &coins = inputs.GetCoins(txin.prevout.hash);
|
||||||
CTxInUndo undo;
|
CTxInUndo undo;
|
||||||
if (!coins.Spend(txin.prevout, undo))
|
assert(coins.Spend(txin.prevout, undo));
|
||||||
return error("UpdateCoins() : cannot spend input");
|
|
||||||
txundo.vprevout.push_back(undo);
|
txundo.vprevout.push_back(undo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add outputs
|
// add outputs
|
||||||
if (!inputs.SetCoins(txhash, CCoins(*this, nHeight)))
|
assert(inputs.SetCoins(txhash, CCoins(*this, nHeight)));
|
||||||
return error("UpdateCoins() : cannot update output");
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1359,7 +1369,7 @@ bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned in
|
|||||||
return CScriptCheck(txFrom, txTo, nIn, flags, nHashType)();
|
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())
|
if (!IsCoinBase())
|
||||||
{
|
{
|
||||||
@ -1369,7 +1379,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
|
// 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.
|
// for an attacker to attempt to split the network.
|
||||||
if (!HaveInputs(inputs))
|
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.
|
// While checking, GetBestBlock() refers to the parent block.
|
||||||
// This is also true for mempool checks.
|
// This is also true for mempool checks.
|
||||||
@ -1384,26 +1394,26 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, bool fScriptChecks, unsi
|
|||||||
// If prev is coinbase, check that it's matured
|
// If prev is coinbase, check that it's matured
|
||||||
if (coins.IsCoinBase()) {
|
if (coins.IsCoinBase()) {
|
||||||
if (nSpendHeight - coins.nHeight < COINBASE_MATURITY)
|
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
|
// Check for negative or overflow input values
|
||||||
nValueIn += coins.vout[prevout.n].nValue;
|
nValueIn += coins.vout[prevout.n].nValue;
|
||||||
if (!MoneyRange(coins.vout[prevout.n].nValue) || !MoneyRange(nValueIn))
|
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())
|
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
|
// Tally transaction fees
|
||||||
int64 nTxFee = nValueIn - GetValueOut();
|
int64 nTxFee = nValueIn - GetValueOut();
|
||||||
if (nTxFee < 0)
|
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;
|
nFees += nTxFee;
|
||||||
if (!MoneyRange(nFees))
|
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.
|
// The first loop above does all the inexpensive checks.
|
||||||
// Only if ALL inputs pass do we perform expensive ECDSA signature checks.
|
// Only if ALL inputs pass do we perform expensive ECDSA signature checks.
|
||||||
@ -1423,7 +1433,7 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, bool fScriptChecks, unsi
|
|||||||
pvChecks->push_back(CScriptCheck());
|
pvChecks->push_back(CScriptCheck());
|
||||||
check.swap(pvChecks->back());
|
check.swap(pvChecks->back());
|
||||||
} else if (!check())
|
} else if (!check())
|
||||||
return DoS(100,false);
|
return state.DoS(100,false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1432,56 +1442,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(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &view, bool *pfClean)
|
||||||
|
|
||||||
bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view, bool *pfClean)
|
|
||||||
{
|
{
|
||||||
assert(pindex == view.GetBestBlock());
|
assert(pindex == view.GetBestBlock());
|
||||||
|
|
||||||
@ -1582,7 +1545,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);
|
static CCheckQueue<CScriptCheck> scriptcheckqueue(128);
|
||||||
|
|
||||||
@ -1597,10 +1560,10 @@ void ThreadScriptCheckQuit() {
|
|||||||
scriptcheckqueue.Quit();
|
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
|
// Check it again in case a previous version let a bad block in
|
||||||
if (!CheckBlock(!fJustCheck, !fJustCheck))
|
if (!CheckBlock(state, !fJustCheck, !fJustCheck))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// verify that the view's current state corresponds to the previous block
|
// verify that the view's current state corresponds to the previous block
|
||||||
@ -1665,12 +1628,12 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
|
|||||||
nInputs += tx.vin.size();
|
nInputs += tx.vin.size();
|
||||||
nSigOps += tx.GetLegacySigOpCount();
|
nSigOps += tx.GetLegacySigOpCount();
|
||||||
if (nSigOps > MAX_BLOCK_SIGOPS)
|
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.IsCoinBase())
|
||||||
{
|
{
|
||||||
if (!tx.HaveInputs(view))
|
if (!tx.HaveInputs(view))
|
||||||
return DoS(100, error("ConnectBlock() : inputs missing/spent"));
|
return state.DoS(100, error("ConnectBlock() : inputs missing/spent"));
|
||||||
|
|
||||||
if (fStrictPayToScriptHash)
|
if (fStrictPayToScriptHash)
|
||||||
{
|
{
|
||||||
@ -1679,19 +1642,19 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
|
|||||||
// an incredibly-expensive-to-validate block.
|
// an incredibly-expensive-to-validate block.
|
||||||
nSigOps += tx.GetP2SHSigOpCount(view);
|
nSigOps += tx.GetP2SHSigOpCount(view);
|
||||||
if (nSigOps > MAX_BLOCK_SIGOPS)
|
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();
|
nFees += tx.GetValueIn(view)-tx.GetValueOut();
|
||||||
|
|
||||||
std::vector<CScriptCheck> vChecks;
|
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;
|
return false;
|
||||||
control.Add(vChecks);
|
control.Add(vChecks);
|
||||||
}
|
}
|
||||||
|
|
||||||
CTxUndo txundo;
|
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");
|
return error("ConnectBlock() : UpdateInputs failed");
|
||||||
if (!tx.IsCoinBase())
|
if (!tx.IsCoinBase())
|
||||||
blockundo.vtxundo.push_back(txundo);
|
blockundo.vtxundo.push_back(txundo);
|
||||||
@ -1704,10 +1667,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));
|
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))
|
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.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees)));
|
||||||
|
|
||||||
if (!control.Wait())
|
if (!control.Wait())
|
||||||
return DoS(100, false);
|
return state.DoS(100, false);
|
||||||
int64 nTime2 = GetTimeMicros() - nStart;
|
int64 nTime2 = GetTimeMicros() - nStart;
|
||||||
if (fBenchmark)
|
if (fBenchmark)
|
||||||
printf("- Verify %u txins: %.2fms (%.3fms/txin)\n", nInputs - 1, 0.001 * nTime2, nInputs <= 1 ? 0 : 0.001 * nTime2 / (nInputs-1));
|
printf("- Verify %u txins: %.2fms (%.3fms/txin)\n", nInputs - 1, 0.001 * nTime2, nInputs <= 1 ? 0 : 0.001 * nTime2 / (nInputs-1));
|
||||||
@ -1720,10 +1683,10 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
|
|||||||
{
|
{
|
||||||
if (pindex->GetUndoPos().IsNull()) {
|
if (pindex->GetUndoPos().IsNull()) {
|
||||||
CDiskBlockPos pos;
|
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");
|
return error("ConnectBlock() : FindUndoPos failed");
|
||||||
if (!blockundo.WriteToDisk(pos, pindex->pprev->GetBlockHash()))
|
if (!blockundo.WriteToDisk(pos, pindex->pprev->GetBlockHash()))
|
||||||
return error("ConnectBlock() : CBlockUndo::WriteToDisk failed");
|
return state.Abort(_("Failed to write undo data"));
|
||||||
|
|
||||||
// update nUndoPos in block index
|
// update nUndoPos in block index
|
||||||
pindex->nUndoPos = pos.nPos;
|
pindex->nUndoPos = pos.nPos;
|
||||||
@ -1734,15 +1697,15 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
|
|||||||
|
|
||||||
CDiskBlockIndex blockindex(pindex);
|
CDiskBlockIndex blockindex(pindex);
|
||||||
if (!pblocktree->WriteBlockIndex(blockindex))
|
if (!pblocktree->WriteBlockIndex(blockindex))
|
||||||
return error("ConnectBlock() : WriteBlockIndex failed");
|
return state.Abort(_("Failed to write block index"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fTxIndex)
|
if (fTxIndex)
|
||||||
pblocktree->WriteTxIndex(vPos);
|
if (!pblocktree->WriteTxIndex(vPos))
|
||||||
|
return state.Abort(_("Failed to write transaction index"));
|
||||||
|
|
||||||
// add this block to the view's block chain
|
// add this block to the view's block chain
|
||||||
if (!view.SetBestBlock(pindex))
|
assert(view.SetBestBlock(pindex));
|
||||||
return false;
|
|
||||||
|
|
||||||
// Watch for transactions paying to me
|
// Watch for transactions paying to me
|
||||||
for (unsigned int i=0; i<vtx.size(); i++)
|
for (unsigned int i=0; i<vtx.size(); i++)
|
||||||
@ -1751,7 +1714,7 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SetBestChain(CBlockIndex* pindexNew)
|
bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
|
||||||
{
|
{
|
||||||
// All modifications to the coin state will be done in this cache.
|
// All modifications to the coin state will be done in this cache.
|
||||||
// Only when all have succeeded, we push it to pcoinsTip.
|
// Only when all have succeeded, we push it to pcoinsTip.
|
||||||
@ -1762,13 +1725,14 @@ bool SetBestChain(CBlockIndex* pindexNew)
|
|||||||
CBlockIndex* plonger = pindexNew;
|
CBlockIndex* plonger = pindexNew;
|
||||||
while (pfork && pfork != plonger)
|
while (pfork && pfork != plonger)
|
||||||
{
|
{
|
||||||
while (plonger->nHeight > pfork->nHeight)
|
while (plonger->nHeight > pfork->nHeight) {
|
||||||
if (!(plonger = plonger->pprev))
|
plonger = plonger->pprev;
|
||||||
return error("SetBestChain() : plonger->pprev is null");
|
assert(plonger != NULL);
|
||||||
|
}
|
||||||
if (pfork == plonger)
|
if (pfork == plonger)
|
||||||
break;
|
break;
|
||||||
if (!(pfork = pfork->pprev))
|
pfork = pfork->pprev;
|
||||||
return error("SetBestChain() : pfork->pprev is null");
|
assert(pfork != NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// List of what to disconnect (typically nothing)
|
// List of what to disconnect (typically nothing)
|
||||||
@ -1792,9 +1756,9 @@ bool SetBestChain(CBlockIndex* pindexNew)
|
|||||||
BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) {
|
BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) {
|
||||||
CBlock block;
|
CBlock block;
|
||||||
if (!block.ReadFromDisk(pindex))
|
if (!block.ReadFromDisk(pindex))
|
||||||
return error("SetBestBlock() : ReadFromDisk for disconnect failed");
|
return state.Abort(_("Failed to read block"));
|
||||||
int64 nStart = GetTimeMicros();
|
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());
|
return error("SetBestBlock() : DisconnectBlock %s failed", BlockHashStr(pindex->GetBlockHash()).c_str());
|
||||||
if (fBenchmark)
|
if (fBenchmark)
|
||||||
printf("- Disconnect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
|
printf("- Disconnect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
|
||||||
@ -1812,11 +1776,13 @@ bool SetBestChain(CBlockIndex* pindexNew)
|
|||||||
BOOST_FOREACH(CBlockIndex *pindex, vConnect) {
|
BOOST_FOREACH(CBlockIndex *pindex, vConnect) {
|
||||||
CBlock block;
|
CBlock block;
|
||||||
if (!block.ReadFromDisk(pindex))
|
if (!block.ReadFromDisk(pindex))
|
||||||
return error("SetBestBlock() : ReadFromDisk for connect failed");
|
return state.Abort(_("Failed to read block"));
|
||||||
int64 nStart = GetTimeMicros();
|
int64 nStart = GetTimeMicros();
|
||||||
if (!block.ConnectBlock(pindex, view)) {
|
if (!block.ConnectBlock(state, pindex, view)) {
|
||||||
InvalidChainFound(pindexNew);
|
if (state.IsInvalid()) {
|
||||||
InvalidBlockFound(pindex);
|
InvalidChainFound(pindexNew);
|
||||||
|
InvalidBlockFound(pindex);
|
||||||
|
}
|
||||||
return error("SetBestBlock() : ConnectBlock %s failed", BlockHashStr(pindex->GetBlockHash()).c_str());
|
return error("SetBestBlock() : ConnectBlock %s failed", BlockHashStr(pindex->GetBlockHash()).c_str());
|
||||||
}
|
}
|
||||||
if (fBenchmark)
|
if (fBenchmark)
|
||||||
@ -1830,8 +1796,7 @@ bool SetBestChain(CBlockIndex* pindexNew)
|
|||||||
// Flush changes to global coin state
|
// Flush changes to global coin state
|
||||||
int64 nStart = GetTimeMicros();
|
int64 nStart = GetTimeMicros();
|
||||||
int nModified = view.GetCacheSize();
|
int nModified = view.GetCacheSize();
|
||||||
if (!view.Flush())
|
assert(view.Flush());
|
||||||
return error("SetBestBlock() : unable to modify coin state");
|
|
||||||
int64 nTime = GetTimeMicros() - nStart;
|
int64 nTime = GetTimeMicros() - nStart;
|
||||||
if (fBenchmark)
|
if (fBenchmark)
|
||||||
printf("- Flush %i transactions: %.2fms (%.4fms/tx)\n", nModified, 0.001 * nTime, 0.001 * nTime / nModified);
|
printf("- Flush %i transactions: %.2fms (%.4fms/tx)\n", nModified, 0.001 * nTime, 0.001 * nTime / nModified);
|
||||||
@ -1839,10 +1804,17 @@ bool SetBestChain(CBlockIndex* pindexNew)
|
|||||||
// Make sure it's successfully written to disk before changing memory structure
|
// Make sure it's successfully written to disk before changing memory structure
|
||||||
bool fIsInitialDownload = IsInitialBlockDownload();
|
bool fIsInitialDownload = IsInitialBlockDownload();
|
||||||
if (!fIsInitialDownload || pcoinsTip->GetCacheSize() > nCoinCacheSize) {
|
if (!fIsInitialDownload || pcoinsTip->GetCacheSize() > nCoinCacheSize) {
|
||||||
|
// Typical CCoins structures on disk are around 100 bytes in size.
|
||||||
|
// Pushing a new one to the database can cause it to be written
|
||||||
|
// twice (once in the log, and once in the tables). This is already
|
||||||
|
// an overestimation, as most will delete an existing entry or
|
||||||
|
// overwrite one. Still, use a conservative safety factor of 2.
|
||||||
|
if (!CheckDiskSpace(100 * 2 * 2 * pcoinsTip->GetCacheSize()))
|
||||||
|
return state.Error();
|
||||||
FlushBlockFile();
|
FlushBlockFile();
|
||||||
pblocktree->Sync();
|
pblocktree->Sync();
|
||||||
if (!pcoinsTip->Flush())
|
if (!pcoinsTip->Flush())
|
||||||
return false;
|
return state.Abort(_("Failed to write to coin database"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point, all changes have been done to the database.
|
// At this point, all changes have been done to the database.
|
||||||
@ -1859,8 +1831,11 @@ bool SetBestChain(CBlockIndex* pindexNew)
|
|||||||
pindex->pprev->pnext = pindex;
|
pindex->pprev->pnext = pindex;
|
||||||
|
|
||||||
// Resurrect memory transactions that were in the disconnected branch
|
// Resurrect memory transactions that were in the disconnected branch
|
||||||
BOOST_FOREACH(CTransaction& tx, vResurrect)
|
BOOST_FOREACH(CTransaction& tx, vResurrect) {
|
||||||
tx.AcceptToMemoryPool(true, false);
|
// ignore validation errors in resurrected transactions
|
||||||
|
CValidationState stateDummy;
|
||||||
|
tx.AcceptToMemoryPool(stateDummy, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
// Delete redundant memory transactions that are in the connected branch
|
// Delete redundant memory transactions that are in the connected branch
|
||||||
BOOST_FOREACH(CTransaction& tx, vDelete) {
|
BOOST_FOREACH(CTransaction& tx, vDelete) {
|
||||||
@ -1917,17 +1892,16 @@ bool SetBestChain(CBlockIndex* pindexNew)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos)
|
bool CBlock::AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos)
|
||||||
{
|
{
|
||||||
// Check for duplicate
|
// Check for duplicate
|
||||||
uint256 hash = GetHash();
|
uint256 hash = GetHash();
|
||||||
if (mapBlockIndex.count(hash))
|
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
|
// Construct new block index object
|
||||||
CBlockIndex* pindexNew = new CBlockIndex(*this);
|
CBlockIndex* pindexNew = new CBlockIndex(*this);
|
||||||
if (!pindexNew)
|
assert(pindexNew);
|
||||||
return error("AddToBlockIndex() : new CBlockIndex failed");
|
|
||||||
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
|
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
|
||||||
pindexNew->phashBlock = &((*mi).first);
|
pindexNew->phashBlock = &((*mi).first);
|
||||||
map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(hashPrevBlock);
|
map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(hashPrevBlock);
|
||||||
@ -1945,10 +1919,11 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos)
|
|||||||
pindexNew->nStatus = BLOCK_VALID_TRANSACTIONS | BLOCK_HAVE_DATA;
|
pindexNew->nStatus = BLOCK_VALID_TRANSACTIONS | BLOCK_HAVE_DATA;
|
||||||
setBlockIndexValid.insert(pindexNew);
|
setBlockIndexValid.insert(pindexNew);
|
||||||
|
|
||||||
pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew));
|
if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew)))
|
||||||
|
return state.Abort(_("Failed to write block index"));
|
||||||
|
|
||||||
// New best?
|
// New best?
|
||||||
if (!ConnectBestBlock())
|
if (!ConnectBestBlock(state))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (pindexNew == pindexBest)
|
if (pindexNew == pindexBest)
|
||||||
@ -1959,14 +1934,15 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos)
|
|||||||
hashPrevBestCoinBase = GetTxHash(0);
|
hashPrevBestCoinBase = GetTxHash(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pblocktree->Flush();
|
if (!pblocktree->Flush())
|
||||||
|
return state.Abort(_("Failed to sync block index"));
|
||||||
|
|
||||||
uiInterface.NotifyBlocksChanged();
|
uiInterface.NotifyBlocksChanged();
|
||||||
return true;
|
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;
|
bool fUpdatedLast = false;
|
||||||
|
|
||||||
@ -2008,19 +1984,19 @@ bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeigh
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return error("FindBlockPos() : out of disk space");
|
return state.Error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
|
if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
|
||||||
return error("FindBlockPos() : cannot write updated block info");
|
return state.Abort(_("Failed to write file info"));
|
||||||
if (fUpdatedLast)
|
if (fUpdatedLast)
|
||||||
pblocktree->WriteLastBlockFile(nLastBlockFile);
|
pblocktree->WriteLastBlockFile(nLastBlockFile);
|
||||||
|
|
||||||
return true;
|
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;
|
pos.nFile = nFile;
|
||||||
|
|
||||||
@ -2031,15 +2007,15 @@ bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize)
|
|||||||
pos.nPos = infoLastBlockFile.nUndoSize;
|
pos.nPos = infoLastBlockFile.nUndoSize;
|
||||||
nNewSize = (infoLastBlockFile.nUndoSize += nAddSize);
|
nNewSize = (infoLastBlockFile.nUndoSize += nAddSize);
|
||||||
if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
|
if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
|
||||||
return error("FindUndoPos() : cannot write updated block info");
|
return state.Abort(_("Failed to write block info"));
|
||||||
} else {
|
} else {
|
||||||
CBlockFileInfo info;
|
CBlockFileInfo info;
|
||||||
if (!pblocktree->ReadBlockFileInfo(nFile, info))
|
if (!pblocktree->ReadBlockFileInfo(nFile, info))
|
||||||
return error("FindUndoPos() : cannot read block info");
|
return state.Abort(_("Failed to read block info"));
|
||||||
pos.nPos = info.nUndoSize;
|
pos.nPos = info.nUndoSize;
|
||||||
nNewSize = (info.nUndoSize += nAddSize);
|
nNewSize = (info.nUndoSize += nAddSize);
|
||||||
if (!pblocktree->WriteBlockFileInfo(nFile, info))
|
if (!pblocktree->WriteBlockFileInfo(nFile, info))
|
||||||
return error("FindUndoPos() : cannot write updated block info");
|
return state.Abort(_("Failed to write block info"));
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE;
|
unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE;
|
||||||
@ -2054,41 +2030,41 @@ bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return error("FindUndoPos() : out of disk space");
|
return state.Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
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
|
// These are checks that are independent of context
|
||||||
// that can be verified before saving an orphan block.
|
// that can be verified before saving an orphan block.
|
||||||
|
|
||||||
// Size limits
|
// Size limits
|
||||||
if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
|
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
|
// Check proof of work matches claimed amount
|
||||||
if (fCheckPOW && !CheckProofOfWork(GetHash(), nBits))
|
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
|
// Check timestamp
|
||||||
if (GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60)
|
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
|
// First transaction must be coinbase, the rest must not be
|
||||||
if (vtx.empty() || !vtx[0].IsCoinBase())
|
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++)
|
for (unsigned int i = 1; i < vtx.size(); i++)
|
||||||
if (vtx[i].IsCoinBase())
|
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
|
// Check transactions
|
||||||
BOOST_FOREACH(const CTransaction& tx, vtx)
|
BOOST_FOREACH(const CTransaction& tx, vtx)
|
||||||
if (!tx.CheckTransaction())
|
if (!tx.CheckTransaction(state))
|
||||||
return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed"));
|
return error("CheckBlock() : CheckTransaction failed");
|
||||||
|
|
||||||
// Build the merkle tree already. We need it anyway later, and it makes the
|
// 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
|
// block cache the transaction hashes, which means they don't need to be
|
||||||
@ -2102,7 +2078,7 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const
|
|||||||
uniqueTx.insert(GetTxHash(i));
|
uniqueTx.insert(GetTxHash(i));
|
||||||
}
|
}
|
||||||
if (uniqueTx.size() != vtx.size())
|
if (uniqueTx.size() != vtx.size())
|
||||||
return DoS(100, error("CheckBlock() : duplicate transaction"));
|
return state.DoS(100, error("CheckBlock() : duplicate transaction"));
|
||||||
|
|
||||||
unsigned int nSigOps = 0;
|
unsigned int nSigOps = 0;
|
||||||
BOOST_FOREACH(const CTransaction& tx, vtx)
|
BOOST_FOREACH(const CTransaction& tx, vtx)
|
||||||
@ -2110,21 +2086,21 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const
|
|||||||
nSigOps += tx.GetLegacySigOpCount();
|
nSigOps += tx.GetLegacySigOpCount();
|
||||||
}
|
}
|
||||||
if (nSigOps > MAX_BLOCK_SIGOPS)
|
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
|
// Check merkle root
|
||||||
if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree())
|
if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree())
|
||||||
return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"));
|
return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CBlock::AcceptBlock(CDiskBlockPos *dbp)
|
bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp)
|
||||||
{
|
{
|
||||||
// Check for duplicate
|
// Check for duplicate
|
||||||
uint256 hash = GetHash();
|
uint256 hash = GetHash();
|
||||||
if (mapBlockIndex.count(hash))
|
if (mapBlockIndex.count(hash))
|
||||||
return error("AcceptBlock() : block already in mapBlockIndex");
|
return state.Invalid(error("AcceptBlock() : block already in mapBlockIndex"));
|
||||||
|
|
||||||
// Get prev block index
|
// Get prev block index
|
||||||
CBlockIndex* pindexPrev = NULL;
|
CBlockIndex* pindexPrev = NULL;
|
||||||
@ -2132,26 +2108,26 @@ bool CBlock::AcceptBlock(CDiskBlockPos *dbp)
|
|||||||
if (hash != hashGenesisBlock) {
|
if (hash != hashGenesisBlock) {
|
||||||
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashPrevBlock);
|
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashPrevBlock);
|
||||||
if (mi == mapBlockIndex.end())
|
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;
|
pindexPrev = (*mi).second;
|
||||||
nHeight = pindexPrev->nHeight+1;
|
nHeight = pindexPrev->nHeight+1;
|
||||||
|
|
||||||
// Check proof of work
|
// Check proof of work
|
||||||
if (nBits != GetNextWorkRequired(pindexPrev, this))
|
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
|
// Check timestamp against prev
|
||||||
if (GetBlockTime() <= pindexPrev->GetMedianTimePast())
|
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
|
// Check that all transactions are finalized
|
||||||
BOOST_FOREACH(const CTransaction& tx, vtx)
|
BOOST_FOREACH(const CTransaction& tx, vtx)
|
||||||
if (!tx.IsFinal(nHeight, GetBlockTime()))
|
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
|
// Check that the block chain matches the known block chain up to a checkpoint
|
||||||
if (!Checkpoints::CheckBlock(nHeight, hash))
|
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:
|
// Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded:
|
||||||
if (nVersion < 2)
|
if (nVersion < 2)
|
||||||
@ -2159,7 +2135,7 @@ bool CBlock::AcceptBlock(CDiskBlockPos *dbp)
|
|||||||
if ((!fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) ||
|
if ((!fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) ||
|
||||||
(fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100)))
|
(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
|
// Enforce block.nVersion=2 rule that the coinbase starts with serialized block height
|
||||||
@ -2171,23 +2147,27 @@ bool CBlock::AcceptBlock(CDiskBlockPos *dbp)
|
|||||||
{
|
{
|
||||||
CScript expect = CScript() << nHeight;
|
CScript expect = CScript() << nHeight;
|
||||||
if (!std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin()))
|
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"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write block to history file
|
// Write block to history file
|
||||||
unsigned int nBlockSize = ::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION);
|
try {
|
||||||
CDiskBlockPos blockPos;
|
unsigned int nBlockSize = ::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION);
|
||||||
if (dbp != NULL)
|
CDiskBlockPos blockPos;
|
||||||
blockPos = *dbp;
|
if (dbp != NULL)
|
||||||
if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, nTime, dbp != NULL))
|
blockPos = *dbp;
|
||||||
return error("AcceptBlock() : FindBlockPos failed");
|
if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, nTime, dbp != NULL))
|
||||||
if (dbp == NULL)
|
return error("AcceptBlock() : FindBlockPos failed");
|
||||||
if (!WriteToDisk(blockPos))
|
if (dbp == NULL)
|
||||||
return error("AcceptBlock() : WriteToDisk failed");
|
if (!WriteToDisk(blockPos))
|
||||||
if (!AddToBlockIndex(blockPos))
|
return state.Abort(_("Failed to write block"));
|
||||||
return error("AcceptBlock() : AddToBlockIndex failed");
|
if (!AddToBlockIndex(state, blockPos))
|
||||||
|
return error("AcceptBlock() : AddToBlockIndex failed");
|
||||||
|
} catch(std::runtime_error &e) {
|
||||||
|
return state.Abort(_("System error: ") + e.what());
|
||||||
|
}
|
||||||
|
|
||||||
// Relay inventory, but don't relay old inventory during initial block download
|
// Relay inventory, but don't relay old inventory during initial block download
|
||||||
int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate();
|
int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate();
|
||||||
@ -2214,17 +2194,17 @@ bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, uns
|
|||||||
return (nFound >= nRequired);
|
return (nFound >= nRequired);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp)
|
bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp)
|
||||||
{
|
{
|
||||||
// Check for duplicate
|
// Check for duplicate
|
||||||
uint256 hash = pblock->GetHash();
|
uint256 hash = pblock->GetHash();
|
||||||
if (mapBlockIndex.count(hash))
|
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))
|
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
|
// Preliminary checks
|
||||||
if (!pblock->CheckBlock())
|
if (!pblock->CheckBlock(state))
|
||||||
return error("ProcessBlock() : CheckBlock FAILED");
|
return error("ProcessBlock() : CheckBlock FAILED");
|
||||||
|
|
||||||
CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex);
|
CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex);
|
||||||
@ -2234,9 +2214,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp)
|
|||||||
int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime;
|
int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime;
|
||||||
if (deltaTime < 0)
|
if (deltaTime < 0)
|
||||||
{
|
{
|
||||||
if (pfrom)
|
return state.DoS(100, error("ProcessBlock() : block with timestamp before last checkpoint"));
|
||||||
pfrom->Misbehaving(100);
|
|
||||||
return error("ProcessBlock() : block with timestamp before last checkpoint");
|
|
||||||
}
|
}
|
||||||
CBigNum bnNewBlock;
|
CBigNum bnNewBlock;
|
||||||
bnNewBlock.SetCompact(pblock->nBits);
|
bnNewBlock.SetCompact(pblock->nBits);
|
||||||
@ -2244,9 +2222,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp)
|
|||||||
bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime));
|
bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime));
|
||||||
if (bnNewBlock > bnRequired)
|
if (bnNewBlock > bnRequired)
|
||||||
{
|
{
|
||||||
if (pfrom)
|
return state.DoS(100, error("ProcessBlock() : block with too little proof-of-work"));
|
||||||
pfrom->Misbehaving(100);
|
|
||||||
return error("ProcessBlock() : block with too little proof-of-work");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2269,7 +2245,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Store to disk
|
// Store to disk
|
||||||
if (!pblock->AcceptBlock(dbp))
|
if (!pblock->AcceptBlock(state, dbp))
|
||||||
return error("ProcessBlock() : AcceptBlock FAILED");
|
return error("ProcessBlock() : AcceptBlock FAILED");
|
||||||
|
|
||||||
// Recursively process any orphan blocks that depended on this one
|
// Recursively process any orphan blocks that depended on this one
|
||||||
@ -2283,7 +2259,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp)
|
|||||||
++mi)
|
++mi)
|
||||||
{
|
{
|
||||||
CBlock* pblockOrphan = (*mi).second;
|
CBlock* pblockOrphan = (*mi).second;
|
||||||
if (pblockOrphan->AcceptBlock())
|
if (pblockOrphan->AcceptBlock(state))
|
||||||
vWorkQueue.push_back(pblockOrphan->GetHash());
|
vWorkQueue.push_back(pblockOrphan->GetHash());
|
||||||
mapOrphanBlocks.erase(pblockOrphan->GetHash());
|
mapOrphanBlocks.erase(pblockOrphan->GetHash());
|
||||||
delete pblockOrphan;
|
delete pblockOrphan;
|
||||||
@ -2455,6 +2431,14 @@ uint256 CPartialMerkleTree::ExtractMatches(std::vector<uint256> &vMatch) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool AbortNode(const std::string &strMessage) {
|
||||||
|
fRequestShutdown = true;
|
||||||
|
strMiscWarning = strMessage;
|
||||||
|
printf("*** %s\n", strMessage.c_str());
|
||||||
|
uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_ERROR | CClientUIInterface::MODAL);
|
||||||
|
StartShutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool CheckDiskSpace(uint64 nAdditionalBytes)
|
bool CheckDiskSpace(uint64 nAdditionalBytes)
|
||||||
{
|
{
|
||||||
@ -2462,15 +2446,8 @@ bool CheckDiskSpace(uint64 nAdditionalBytes)
|
|||||||
|
|
||||||
// Check for nMinDiskSpace bytes (currently 50MB)
|
// Check for nMinDiskSpace bytes (currently 50MB)
|
||||||
if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes)
|
if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes)
|
||||||
{
|
return AbortNode(_("Error: Disk space is low!"));
|
||||||
fShutdown = true;
|
|
||||||
string strMessage = _("Error: Disk space is low!");
|
|
||||||
strMiscWarning = strMessage;
|
|
||||||
printf("*** %s\n", strMessage.c_str());
|
|
||||||
uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_ERROR);
|
|
||||||
StartShutdown();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2612,6 +2589,7 @@ bool VerifyDB() {
|
|||||||
CBlockIndex* pindexState = pindexBest;
|
CBlockIndex* pindexState = pindexBest;
|
||||||
CBlockIndex* pindexFailure = NULL;
|
CBlockIndex* pindexFailure = NULL;
|
||||||
int nGoodTransactions = 0;
|
int nGoodTransactions = 0;
|
||||||
|
CValidationState state;
|
||||||
for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev)
|
for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev)
|
||||||
{
|
{
|
||||||
if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth)
|
if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth)
|
||||||
@ -2621,7 +2599,7 @@ bool VerifyDB() {
|
|||||||
if (!block.ReadFromDisk(pindex))
|
if (!block.ReadFromDisk(pindex))
|
||||||
return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
|
return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
|
||||||
// check level 1: verify block validity
|
// 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());
|
return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
|
||||||
// check level 2: verify undo validity
|
// check level 2: verify undo validity
|
||||||
if (nCheckLevel >= 2 && pindex) {
|
if (nCheckLevel >= 2 && pindex) {
|
||||||
@ -2635,7 +2613,7 @@ bool VerifyDB() {
|
|||||||
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
|
// 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) {
|
if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= 2*nCoinCacheSize + 32000) {
|
||||||
bool fClean = true;
|
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());
|
return error("VerifyDB() : *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
|
||||||
pindexState = pindex->pprev;
|
pindexState = pindex->pprev;
|
||||||
if (!fClean) {
|
if (!fClean) {
|
||||||
@ -2656,7 +2634,7 @@ bool VerifyDB() {
|
|||||||
CBlock block;
|
CBlock block;
|
||||||
if (!block.ReadFromDisk(pindex))
|
if (!block.ReadFromDisk(pindex))
|
||||||
return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
|
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());
|
return error("VerifyDB() : *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2737,11 +2715,12 @@ bool LoadBlockIndex()
|
|||||||
// Start new block file
|
// Start new block file
|
||||||
unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
|
unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
|
||||||
CDiskBlockPos blockPos;
|
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");
|
return error("AcceptBlock() : FindBlockPos failed");
|
||||||
if (!block.WriteToDisk(blockPos))
|
if (!block.WriteToDisk(blockPos))
|
||||||
return error("LoadBlockIndex() : writing genesis block to disk failed");
|
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");
|
return error("LoadBlockIndex() : genesis block not accepted");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2825,7 +2804,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
|
|||||||
int64 nStart = GetTimeMillis();
|
int64 nStart = GetTimeMillis();
|
||||||
|
|
||||||
int nLoaded = 0;
|
int nLoaded = 0;
|
||||||
{
|
try {
|
||||||
CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION);
|
CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION);
|
||||||
uint64 nStartByte = 0;
|
uint64 nStartByte = 0;
|
||||||
if (dbp) {
|
if (dbp) {
|
||||||
@ -2871,14 +2850,19 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
|
|||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
if (dbp)
|
if (dbp)
|
||||||
dbp->nPos = nBlockPos;
|
dbp->nPos = nBlockPos;
|
||||||
if (ProcessBlock(NULL, &block, dbp))
|
CValidationState state;
|
||||||
|
if (ProcessBlock(state, NULL, &block, dbp))
|
||||||
nLoaded++;
|
nLoaded++;
|
||||||
|
if (state.IsError())
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} catch (std::exception &e) {
|
} catch (std::exception &e) {
|
||||||
printf("%s() : Deserialize or I/O error caught during load\n", __PRETTY_FUNCTION__);
|
printf("%s() : Deserialize or I/O error caught during load\n", __PRETTY_FUNCTION__);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fclose(fileIn);
|
fclose(fileIn);
|
||||||
|
} catch(std::runtime_error &e) {
|
||||||
|
AbortNode(_("Error: system error: ") + e.what());
|
||||||
}
|
}
|
||||||
if (nLoaded > 0)
|
if (nLoaded > 0)
|
||||||
printf("Loaded %i blocks from external file in %"PRI64d"ms\n", nLoaded, GetTimeMillis() - nStart);
|
printf("Loaded %i blocks from external file in %"PRI64d"ms\n", nLoaded, GetTimeMillis() - nStart);
|
||||||
@ -3448,7 +3432,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
|||||||
pfrom->AddInventoryKnown(inv);
|
pfrom->AddInventoryKnown(inv);
|
||||||
|
|
||||||
bool fMissingInputs = false;
|
bool fMissingInputs = false;
|
||||||
if (tx.AcceptToMemoryPool(true, true, &fMissingInputs))
|
CValidationState state;
|
||||||
|
if (tx.AcceptToMemoryPool(state, true, true, &fMissingInputs))
|
||||||
{
|
{
|
||||||
RelayTransaction(tx, inv.hash, vMsg);
|
RelayTransaction(tx, inv.hash, vMsg);
|
||||||
mapAlreadyAskedFor.erase(inv);
|
mapAlreadyAskedFor.erase(inv);
|
||||||
@ -3469,7 +3454,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
|||||||
CInv inv(MSG_TX, tx.GetHash());
|
CInv inv(MSG_TX, tx.GetHash());
|
||||||
bool fMissingInputs2 = false;
|
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());
|
printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str());
|
||||||
RelayTransaction(tx, inv.hash, vMsg);
|
RelayTransaction(tx, inv.hash, vMsg);
|
||||||
@ -3498,7 +3483,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
|||||||
if (nEvicted > 0)
|
if (nEvicted > 0)
|
||||||
printf("mapOrphan overflow, removed %u tx\n", nEvicted);
|
printf("mapOrphan overflow, removed %u tx\n", nEvicted);
|
||||||
}
|
}
|
||||||
if (tx.nDoS) pfrom->Misbehaving(tx.nDoS);
|
int nDoS;
|
||||||
|
if (state.IsInvalid(nDoS))
|
||||||
|
pfrom->Misbehaving(nDoS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3513,9 +3500,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
|||||||
CInv inv(MSG_BLOCK, block.GetHash());
|
CInv inv(MSG_BLOCK, block.GetHash());
|
||||||
pfrom->AddInventoryKnown(inv);
|
pfrom->AddInventoryKnown(inv);
|
||||||
|
|
||||||
if (ProcessBlock(pfrom, &block))
|
CValidationState state;
|
||||||
|
if (ProcessBlock(state, pfrom, &block))
|
||||||
mapAlreadyAskedFor.erase(inv);
|
mapAlreadyAskedFor.erase(inv);
|
||||||
if (block.nDoS) pfrom->Misbehaving(block.nDoS);
|
int nDoS;
|
||||||
|
if (state.IsInvalid(nDoS))
|
||||||
|
pfrom->Misbehaving(nDoS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -4265,12 +4255,13 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey)
|
|||||||
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
|
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!tx.CheckInputs(viewTemp, true, SCRIPT_VERIFY_P2SH))
|
CValidationState state;
|
||||||
|
if (!tx.CheckInputs(state, viewTemp, true, SCRIPT_VERIFY_P2SH))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
CTxUndo txundo;
|
CTxUndo txundo;
|
||||||
uint256 hash = tx.GetHash();
|
uint256 hash = tx.GetHash();
|
||||||
if (!tx.UpdateCoins(viewTemp, txundo, pindexPrev->nHeight+1, hash))
|
if (!tx.UpdateCoins(state, viewTemp, txundo, pindexPrev->nHeight+1, hash))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// push changes from the second layer cache to the first one
|
// push changes from the second layer cache to the first one
|
||||||
@ -4328,7 +4319,8 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey)
|
|||||||
indexDummy.pprev = pindexPrev;
|
indexDummy.pprev = pindexPrev;
|
||||||
indexDummy.nHeight = pindexPrev->nHeight + 1;
|
indexDummy.nHeight = pindexPrev->nHeight + 1;
|
||||||
CCoinsViewCache viewNew(*pcoinsTip, true);
|
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");
|
throw std::runtime_error("CreateNewBlock() : ConnectBlock failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4430,7 +4422,8 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process this block the same as if we had received it from another node
|
// 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");
|
return error("BitcoinMiner : ProcessBlock, block not accepted");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
90
src/main.h
90
src/main.h
@ -112,9 +112,11 @@ class CTxUndo;
|
|||||||
class CCoinsView;
|
class CCoinsView;
|
||||||
class CCoinsViewCache;
|
class CCoinsViewCache;
|
||||||
class CScriptCheck;
|
class CScriptCheck;
|
||||||
|
class CValidationState;
|
||||||
|
|
||||||
struct CBlockTemplate;
|
struct CBlockTemplate;
|
||||||
|
|
||||||
|
|
||||||
/** Register a wallet to receive updates from core */
|
/** Register a wallet to receive updates from core */
|
||||||
void RegisterWallet(CWallet* pwalletIn);
|
void RegisterWallet(CWallet* pwalletIn);
|
||||||
/** Unregister a wallet from core */
|
/** Unregister a wallet from core */
|
||||||
@ -122,7 +124,7 @@ void UnregisterWallet(CWallet* pwalletIn);
|
|||||||
/** Push an updated transaction to all registered wallets */
|
/** Push an updated transaction to all registered wallets */
|
||||||
void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false);
|
void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false);
|
||||||
/** Process an incoming block */
|
/** 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 */
|
/** Check whether enough disk space is available for an incoming block */
|
||||||
bool CheckDiskSpace(uint64 nAdditionalBytes = 0);
|
bool CheckDiskSpace(uint64 nAdditionalBytes = 0);
|
||||||
/** Open a block file (blk?????.dat) */
|
/** Open a block file (blk?????.dat) */
|
||||||
@ -172,13 +174,15 @@ std::string GetWarnings(std::string strFor);
|
|||||||
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
|
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
|
||||||
bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false);
|
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 */
|
/** 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 */
|
/** 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 */
|
/** Create a new block index entry for a given block hash */
|
||||||
CBlockIndex * InsertBlockIndex(uint256 hash);
|
CBlockIndex * InsertBlockIndex(uint256 hash);
|
||||||
/** Verify a signature */
|
/** Verify a signature */
|
||||||
bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType);
|
bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType);
|
||||||
|
/** Abort with a message */
|
||||||
|
bool AbortNode(const std::string &msg);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -454,7 +458,6 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
enum GetMinFee_mode
|
enum GetMinFee_mode
|
||||||
{
|
{
|
||||||
GMF_BLOCK,
|
GMF_BLOCK,
|
||||||
@ -474,10 +477,6 @@ public:
|
|||||||
std::vector<CTxOut> vout;
|
std::vector<CTxOut> vout;
|
||||||
unsigned int nLockTime;
|
unsigned int nLockTime;
|
||||||
|
|
||||||
// Denial-of-service detection:
|
|
||||||
mutable int nDoS;
|
|
||||||
bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; }
|
|
||||||
|
|
||||||
CTransaction()
|
CTransaction()
|
||||||
{
|
{
|
||||||
SetNull();
|
SetNull();
|
||||||
@ -498,7 +497,6 @@ public:
|
|||||||
vin.clear();
|
vin.clear();
|
||||||
vout.clear();
|
vout.clear();
|
||||||
nLockTime = 0;
|
nLockTime = 0;
|
||||||
nDoS = 0; // Denial-of-service prevention
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsNull() const
|
bool IsNull() const
|
||||||
@ -654,27 +652,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
|
// Check whether all prevouts of this transaction are present in the UTXO set represented by view
|
||||||
bool HaveInputs(CCoinsViewCache &view) const;
|
bool HaveInputs(CCoinsViewCache &view) const;
|
||||||
|
|
||||||
// Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts)
|
// 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
|
// This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it
|
||||||
// instead of being performed inline.
|
// 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,
|
unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC,
|
||||||
std::vector<CScriptCheck> *pvChecks = NULL) const;
|
std::vector<CScriptCheck> *pvChecks = NULL) const;
|
||||||
|
|
||||||
// Apply the effects of this transaction on the UTXO set represented by view
|
// 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
|
// Context-independent validity checks
|
||||||
bool CheckTransaction() const;
|
bool CheckTransaction(CValidationState &state) const;
|
||||||
|
|
||||||
// Try to accept this transaction into the memory pool
|
// 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:
|
protected:
|
||||||
static const CTxOut &GetOutputFor(const CTxIn& input, CCoinsViewCache& mapInputs);
|
static const CTxOut &GetOutputFor(const CTxIn& input, CCoinsViewCache& mapInputs);
|
||||||
@ -1320,10 +1315,6 @@ public:
|
|||||||
// memory only
|
// memory only
|
||||||
mutable std::vector<uint256> vMerkleTree;
|
mutable std::vector<uint256> vMerkleTree;
|
||||||
|
|
||||||
// Denial-of-service detection:
|
|
||||||
mutable int nDoS;
|
|
||||||
bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; }
|
|
||||||
|
|
||||||
CBlock()
|
CBlock()
|
||||||
{
|
{
|
||||||
SetNull();
|
SetNull();
|
||||||
@ -1346,7 +1337,6 @@ public:
|
|||||||
CBlockHeader::SetNull();
|
CBlockHeader::SetNull();
|
||||||
vtx.clear();
|
vtx.clear();
|
||||||
vMerkleTree.clear();
|
vMerkleTree.clear();
|
||||||
nDoS = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CBlockHeader GetBlockHeader() const
|
CBlockHeader GetBlockHeader() const
|
||||||
@ -1494,23 +1484,23 @@ public:
|
|||||||
* In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean
|
* 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
|
* 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. */
|
* 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
|
// 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
|
// Read a block from disk
|
||||||
bool ReadFromDisk(const CBlockIndex* pindex);
|
bool ReadFromDisk(const CBlockIndex* pindex);
|
||||||
|
|
||||||
// Add this block to the block index, and if necessary, switch the active block chain to this
|
// 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
|
// 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
|
// Store block on disk
|
||||||
// if dbp is provided, the file is known to already reside 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 +1867,52 @@ 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() {
|
||||||
|
mode = MODE_ERROR;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool Abort(const std::string &msg) {
|
||||||
|
AbortNode(msg);
|
||||||
|
return Error();
|
||||||
|
}
|
||||||
|
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 +2061,7 @@ public:
|
|||||||
std::map<uint256, CTransaction> mapTx;
|
std::map<uint256, CTransaction> mapTx;
|
||||||
std::map<COutPoint, CInPoint> mapNextTx;
|
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 addUnchecked(const uint256& hash, CTransaction &tx);
|
||||||
bool remove(const CTransaction &tx, bool fRecursive = false);
|
bool remove(const CTransaction &tx, bool fRecursive = false);
|
||||||
bool removeConflicts(const CTransaction &tx);
|
bool removeConflicts(const CTransaction &tx);
|
||||||
|
@ -365,9 +365,10 @@ Value submitblock(const Array& params, bool fHelp)
|
|||||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
|
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fAccepted = ProcessBlock(NULL, &pblock);
|
CValidationState state;
|
||||||
|
bool fAccepted = ProcessBlock(state, NULL, &pblock);
|
||||||
if (!fAccepted)
|
if (!fAccepted)
|
||||||
return "rejected";
|
return "rejected"; // TODO: report validation state
|
||||||
|
|
||||||
return Value::null;
|
return Value::null;
|
||||||
}
|
}
|
||||||
|
@ -546,8 +546,9 @@ Value sendrawtransaction(const Array& params, bool fHelp)
|
|||||||
fHave = view.GetCoins(hashTx, existingCoins);
|
fHave = view.GetCoins(hashTx, existingCoins);
|
||||||
if (!fHave) {
|
if (!fHave) {
|
||||||
// push to local node
|
// push to local node
|
||||||
if (!tx.AcceptToMemoryPool(true, false))
|
CValidationState state;
|
||||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected");
|
if (!tx.AcceptToMemoryPool(state, true, false))
|
||||||
|
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); // TODO: report validation state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fHave) {
|
if (fHave) {
|
||||||
|
@ -73,7 +73,9 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||||||
txFirst.push_back(new CTransaction(pblock->vtx[0]));
|
txFirst.push_back(new CTransaction(pblock->vtx[0]));
|
||||||
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
|
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
|
||||||
pblock->nNonce = blockinfo[i].nonce;
|
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();
|
pblock->hashPrevBlock = pblock->GetHash();
|
||||||
}
|
}
|
||||||
delete pblocktemplate;
|
delete pblocktemplate;
|
||||||
|
@ -66,7 +66,9 @@ BOOST_AUTO_TEST_CASE(tx_valid)
|
|||||||
CTransaction tx;
|
CTransaction tx;
|
||||||
stream >> 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++)
|
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||||
{
|
{
|
||||||
@ -133,7 +135,8 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
|
|||||||
CTransaction tx;
|
CTransaction tx;
|
||||||
stream >> 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++)
|
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);
|
CDataStream stream(vch, SER_DISK, CLIENT_VERSION);
|
||||||
CTransaction tx;
|
CTransaction tx;
|
||||||
stream >> 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
|
// Check that duplicate txins fail
|
||||||
tx.vin.push_back(tx.vin[0]);
|
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.");
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -203,7 +203,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
|
|||||||
ssKey >> hash;
|
ssKey >> hash;
|
||||||
CWalletTx& wtx = pwallet->mapWallet[hash];
|
CWalletTx& wtx = pwallet->mapWallet[hash];
|
||||||
ssValue >> wtx;
|
ssValue >> wtx;
|
||||||
if (wtx.CheckTransaction() && (wtx.GetHash() == hash))
|
CValidationState state;
|
||||||
|
if (wtx.CheckTransaction(state) && (wtx.GetHash() == hash) && state.IsValid())
|
||||||
wtx.BindWallet(pwallet);
|
wtx.BindWallet(pwallet);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user