Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 676 Lines • ▼ Show 20 Lines | static bool AcceptToMemoryPoolWorker( | ||||
const Config &config, CTxMemPool &pool, CValidationState &state, | const Config &config, CTxMemPool &pool, CValidationState &state, | ||||
const CTransactionRef &ptx, bool fLimitFree, bool *pfMissingInputs, | const CTransactionRef &ptx, bool fLimitFree, bool *pfMissingInputs, | ||||
int64_t nAcceptTime, std::list<CTransactionRef> *plTxnReplaced, | int64_t nAcceptTime, std::list<CTransactionRef> *plTxnReplaced, | ||||
bool fOverrideMempoolLimit, const Amount nAbsurdFee, | bool fOverrideMempoolLimit, const Amount nAbsurdFee, | ||||
std::vector<COutPoint> &coins_to_uncache) { | std::vector<COutPoint> &coins_to_uncache) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
const CTransaction &tx = *ptx; | const CTransaction &tx = *ptx; | ||||
const uint256 txid = tx.GetHash(); | const TxHash txhash = tx.GetHash(); | ||||
if (pfMissingInputs) { | if (pfMissingInputs) { | ||||
*pfMissingInputs = false; | *pfMissingInputs = false; | ||||
} | } | ||||
// 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 (!CheckRegularTransaction(tx, state, true)) { | if (!CheckRegularTransaction(tx, state, true)) { | ||||
// state filled in by CheckRegularTransaction. | // state filled in by CheckRegularTransaction. | ||||
return false; | return false; | ||||
Show All 15 Lines | if (!ContextualCheckTransactionForCurrentBlock( | ||||
// We copy the state from a dummy to ensure we don't increase the | // We copy the state from a dummy to ensure we don't increase the | ||||
// ban score of peer for transaction that could be valid in the future. | // ban score of peer for transaction that could be valid in the future. | ||||
return state.DoS( | return state.DoS( | ||||
0, false, REJECT_NONSTANDARD, ctxState.GetRejectReason(), | 0, false, REJECT_NONSTANDARD, ctxState.GetRejectReason(), | ||||
ctxState.CorruptionPossible(), ctxState.GetDebugMessage()); | ctxState.CorruptionPossible(), ctxState.GetDebugMessage()); | ||||
} | } | ||||
// Is it already in the memory pool? | // Is it already in the memory pool? | ||||
if (pool.exists(txid)) { | if (pool.exists(txhash)) { | ||||
return state.Invalid(false, REJECT_ALREADY_KNOWN, | return state.Invalid(false, REJECT_ALREADY_KNOWN, | ||||
"txn-already-in-mempool"); | "txn-already-in-mempool"); | ||||
} | } | ||||
// Check for conflicts with in-memory transactions | // Check for conflicts with in-memory transactions | ||||
{ | { | ||||
// Protect pool.mapNextTx | // Protect pool.mapNextTx | ||||
LOCK(pool.cs); | LOCK(pool.cs); | ||||
Show All 15 Lines | // Check for conflicts with in-memory transactions | ||||
LockPoints lp; | LockPoints lp; | ||||
{ | { | ||||
LOCK(pool.cs); | LOCK(pool.cs); | ||||
CCoinsViewMemPool viewMemPool(pcoinsTip, pool); | CCoinsViewMemPool viewMemPool(pcoinsTip, pool); | ||||
view.SetBackend(viewMemPool); | view.SetBackend(viewMemPool); | ||||
// Do we already have it? | // Do we already have it? | ||||
for (size_t out = 0; out < tx.vout.size(); out++) { | for (size_t out = 0; out < tx.vout.size(); out++) { | ||||
COutPoint outpoint(txid, out); | COutPoint outpoint(txhash, out); | ||||
bool had_coin_in_cache = pcoinsTip->HaveCoinInCache(outpoint); | bool had_coin_in_cache = pcoinsTip->HaveCoinInCache(outpoint); | ||||
if (view.HaveCoin(outpoint)) { | if (view.HaveCoin(outpoint)) { | ||||
if (!had_coin_in_cache) { | if (!had_coin_in_cache) { | ||||
coins_to_uncache.push_back(outpoint); | coins_to_uncache.push_back(outpoint); | ||||
} | } | ||||
return state.Invalid(false, REJECT_ALREADY_KNOWN, | return state.Invalid(false, REJECT_ALREADY_KNOWN, | ||||
"txn-already-known"); | "txn-already-known"); | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | // Check for conflicts with in-memory transactions | ||||
int64_t nSigOpsCount = | int64_t nSigOpsCount = | ||||
GetTransactionSigOpCount(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS); | GetTransactionSigOpCount(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS); | ||||
Amount nValueOut = tx.GetValueOut(); | Amount nValueOut = tx.GetValueOut(); | ||||
Amount nFees = nValueIn - nValueOut; | Amount nFees = nValueIn - nValueOut; | ||||
// nModifiedFees includes any fee deltas from PrioritiseTransaction | // nModifiedFees includes any fee deltas from PrioritiseTransaction | ||||
Amount nModifiedFees = nFees; | Amount nModifiedFees = nFees; | ||||
double nPriorityDummy = 0; | double nPriorityDummy = 0; | ||||
pool.ApplyDeltas(txid, nPriorityDummy, nModifiedFees); | pool.ApplyDeltas(txhash, nPriorityDummy, nModifiedFees); | ||||
Amount inChainInputValue; | Amount inChainInputValue; | ||||
double dPriority = | double dPriority = | ||||
view.GetPriority(tx, chainActive.Height(), inChainInputValue); | view.GetPriority(tx, chainActive.Height(), inChainInputValue); | ||||
// Keep track of transactions that spend a coinbase, which we re-scan | // Keep track of transactions that spend a coinbase, which we re-scan | ||||
// during reorgs to ensure COINBASE_MATURITY is still met. | // during reorgs to ensure COINBASE_MATURITY is still met. | ||||
bool fSpendsCoinbase = false; | bool fSpendsCoinbase = false; | ||||
▲ Show 20 Lines • Show All 131 Lines • ▼ Show 20 Lines | // Check for conflicts with in-memory transactions | ||||
txdata)) { | txdata)) { | ||||
// If we're using promiscuousmempoolflags, we may hit this normally. | // If we're using promiscuousmempoolflags, we may hit this normally. | ||||
// Check if current block has some flags that scriptVerifyFlags does | // Check if current block has some flags that scriptVerifyFlags does | ||||
// not before printing an ominous warning. | // not before printing an ominous warning. | ||||
if (!(~scriptVerifyFlags & currentBlockScriptVerifyFlags)) { | if (!(~scriptVerifyFlags & currentBlockScriptVerifyFlags)) { | ||||
return error( | return error( | ||||
"%s: BUG! PLEASE REPORT THIS! ConnectInputs failed against " | "%s: BUG! PLEASE REPORT THIS! ConnectInputs failed against " | ||||
"MANDATORY but not STANDARD flags %s, %s", | "MANDATORY but not STANDARD flags %s, %s", | ||||
__func__, txid.ToString(), FormatStateMessage(state)); | __func__, txhash.ToString(), FormatStateMessage(state)); | ||||
} | } | ||||
if (!CheckInputs(tx, state, view, true, | if (!CheckInputs(tx, state, view, true, | ||||
MANDATORY_SCRIPT_VERIFY_FLAGS, true, false, | MANDATORY_SCRIPT_VERIFY_FLAGS, true, false, | ||||
txdata)) { | txdata)) { | ||||
return error( | return error( | ||||
"%s: ConnectInputs failed against MANDATORY but not " | "%s: ConnectInputs failed against MANDATORY but not " | ||||
"STANDARD flags due to promiscuous mempool %s, %s", | "STANDARD flags due to promiscuous mempool %s, %s", | ||||
__func__, txid.ToString(), FormatStateMessage(state)); | __func__, txhash.ToString(), FormatStateMessage(state)); | ||||
} | } | ||||
LogPrintf("Warning: -promiscuousmempool flags set to not include " | LogPrintf("Warning: -promiscuousmempool flags set to not include " | ||||
"currently enforced soft forks, this may break mining or " | "currently enforced soft forks, this may break mining or " | ||||
"otherwise cause instability!\n"); | "otherwise cause instability!\n"); | ||||
} | } | ||||
// This transaction should only count for fee estimation if | // This transaction should only count for fee estimation if | ||||
// the node is not behind and it is not dependent on any other | // the node is not behind and it is not dependent on any other | ||||
// transactions in the mempool. | // transactions in the mempool. | ||||
bool validForFeeEstimation = | bool validForFeeEstimation = | ||||
IsCurrentForFeeEstimation() && pool.HasNoInputsOf(tx); | IsCurrentForFeeEstimation() && pool.HasNoInputsOf(tx); | ||||
// Store transaction in memory. | // Store transaction in memory. | ||||
pool.addUnchecked(txid, entry, setAncestors, validForFeeEstimation); | pool.addUnchecked(txhash, entry, setAncestors, validForFeeEstimation); | ||||
// Trim mempool and check if tx was trimmed. | // Trim mempool and check if tx was trimmed. | ||||
if (!fOverrideMempoolLimit) { | if (!fOverrideMempoolLimit) { | ||||
LimitMempoolSize( | LimitMempoolSize( | ||||
pool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, | pool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, | ||||
GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); | GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); | ||||
if (!pool.exists(txid)) { | if (!pool.exists(txhash)) { | ||||
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, | return state.DoS(0, false, REJECT_INSUFFICIENTFEE, | ||||
"mempool full"); | "mempool full"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
GetMainSignals().SyncTransaction( | GetMainSignals().SyncTransaction( | ||||
tx, nullptr, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK); | tx, nullptr, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK); | ||||
Show All 30 Lines | bool AcceptToMemoryPool(const Config &config, CTxMemPool &pool, | ||||
bool fOverrideMempoolLimit, const Amount nAbsurdFee) { | bool fOverrideMempoolLimit, const Amount nAbsurdFee) { | ||||
return AcceptToMemoryPoolWithTime(config, pool, state, tx, fLimitFree, | return AcceptToMemoryPoolWithTime(config, pool, state, tx, fLimitFree, | ||||
pfMissingInputs, GetTime(), plTxnReplaced, | pfMissingInputs, GetTime(), plTxnReplaced, | ||||
fOverrideMempoolLimit, nAbsurdFee); | fOverrideMempoolLimit, nAbsurdFee); | ||||
} | } | ||||
/** Return transaction in txOut, and if it was found inside a block, its hash is | /** Return transaction in txOut, and if it was found inside a block, its hash is | ||||
* placed in hashBlock */ | * placed in hashBlock */ | ||||
bool GetTransaction(const Config &config, const uint256 &txid, | bool GetTransaction(const Config &config, const uint256 &txhash, | ||||
CTransactionRef &txOut, uint256 &hashBlock, | CTransactionRef &txOut, uint256 &hashBlock, | ||||
bool fAllowSlow) { | bool fAllowSlow) { | ||||
CBlockIndex *pindexSlow = nullptr; | CBlockIndex *pindexSlow = nullptr; | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
CTransactionRef ptx = mempool.get(txid); | CTransactionRef ptx = mempool.get(txhash); | ||||
if (ptx) { | if (ptx) { | ||||
txOut = ptx; | txOut = ptx; | ||||
return true; | return true; | ||||
} | } | ||||
if (fTxIndex) { | if (fTxIndex) { | ||||
CDiskTxPos postx; | CDiskTxPos postx; | ||||
if (pblocktree->ReadTxIndex(txid, postx)) { | if (pblocktree->ReadTxIndex(txhash, postx)) { | ||||
CAutoFile file(OpenBlockFile(postx, true), SER_DISK, | CAutoFile file(OpenBlockFile(postx, true), SER_DISK, | ||||
CLIENT_VERSION); | CLIENT_VERSION); | ||||
if (file.IsNull()) | if (file.IsNull()) | ||||
return error("%s: OpenBlockFile failed", __func__); | return error("%s: OpenBlockFile failed", __func__); | ||||
CBlockHeader header; | CBlockHeader header; | ||||
try { | try { | ||||
file >> header; | file >> header; | ||||
fseek(file.Get(), postx.nTxOffset, SEEK_CUR); | fseek(file.Get(), postx.nTxOffset, SEEK_CUR); | ||||
file >> txOut; | file >> txOut; | ||||
} catch (const std::exception &e) { | } catch (const std::exception &e) { | ||||
return error("%s: Deserialize or I/O error - %s", __func__, | return error("%s: Deserialize or I/O error - %s", __func__, | ||||
e.what()); | e.what()); | ||||
} | } | ||||
hashBlock = header.GetHash(); | hashBlock = header.GetHash(); | ||||
if (txOut->GetHash() != txid) | if (txOut->GetHash() != txhash) | ||||
return error("%s: txid mismatch", __func__); | return error("%s: txhash mismatch", __func__); | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
// use coin database to locate block that contains transaction, and scan it | // use coin database to locate block that contains transaction, and scan it | ||||
if (fAllowSlow) { | if (fAllowSlow) { | ||||
const Coin &coin = AccessByTxid(*pcoinsTip, txid); | const Coin &coin = AccessByTxid(*pcoinsTip, txhash); | ||||
if (!coin.IsSpent()) { | if (!coin.IsSpent()) { | ||||
pindexSlow = chainActive[coin.GetHeight()]; | pindexSlow = chainActive[coin.GetHeight()]; | ||||
} | } | ||||
} | } | ||||
if (pindexSlow) { | if (pindexSlow) { | ||||
auto ¶ms = config.GetChainParams().GetConsensus(); | auto ¶ms = config.GetChainParams().GetConsensus(); | ||||
CBlock block; | CBlock block; | ||||
if (ReadBlockFromDisk(block, pindexSlow, params)) { | if (ReadBlockFromDisk(block, pindexSlow, params)) { | ||||
for (const auto &tx : block.vtx) { | for (const auto &tx : block.vtx) { | ||||
if (tx->GetHash() == txid) { | if (tx->GetHash() == txhash) { | ||||
txOut = tx; | txOut = tx; | ||||
hashBlock = pindexSlow->GetBlockHash(); | hashBlock = pindexSlow->GetBlockHash(); | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 556 Lines • ▼ Show 20 Lines | if (blockUndo.vtxundo.size() + 1 != block.vtx.size()) { | ||||
error("DisconnectBlock(): block and undo data inconsistent"); | error("DisconnectBlock(): block and undo data inconsistent"); | ||||
return DISCONNECT_FAILED; | return DISCONNECT_FAILED; | ||||
} | } | ||||
// Undo transactions in reverse order. | // Undo transactions in reverse order. | ||||
size_t i = block.vtx.size(); | size_t i = block.vtx.size(); | ||||
while (i-- > 0) { | while (i-- > 0) { | ||||
const CTransaction &tx = *(block.vtx[i]); | const CTransaction &tx = *(block.vtx[i]); | ||||
uint256 txid = tx.GetHash(); | TxHash txhash = tx.GetHash(); | ||||
// Check that all outputs are available and match the outputs in the | // Check that all outputs are available and match the outputs in the | ||||
// block itself exactly. | // block itself exactly. | ||||
for (size_t o = 0; o < tx.vout.size(); o++) { | for (size_t o = 0; o < tx.vout.size(); o++) { | ||||
if (tx.vout[o].scriptPubKey.IsUnspendable()) { | if (tx.vout[o].scriptPubKey.IsUnspendable()) { | ||||
continue; | continue; | ||||
} | } | ||||
COutPoint out(txid, o); | COutPoint out(txhash, o); | ||||
Coin coin; | Coin coin; | ||||
bool is_spent = view.SpendCoin(out, &coin); | bool is_spent = view.SpendCoin(out, &coin); | ||||
if (!is_spent || tx.vout[o] != coin.GetTxOut()) { | if (!is_spent || tx.vout[o] != coin.GetTxOut()) { | ||||
// transaction output mismatch | // transaction output mismatch | ||||
fClean = false; | fClean = false; | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 769 Lines • ▼ Show 20 Lines | static bool DisconnectTip(const Config &config, CValidationState &state, | ||||
// Write the chain state to disk, if necessary. | // Write the chain state to disk, if necessary. | ||||
if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED)) { | if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED)) { | ||||
return false; | return false; | ||||
} | } | ||||
if (!fBare) { | if (!fBare) { | ||||
// Resurrect mempool transactions from the disconnected block. | // Resurrect mempool transactions from the disconnected block. | ||||
std::vector<uint256> vHashUpdate; | std::vector<TxHash> vHashUpdate; | ||||
for (const auto &it : block.vtx) { | for (const auto &it : block.vtx) { | ||||
const CTransaction &tx = *it; | const CTransaction &tx = *it; | ||||
// ignore validation errors in resurrected transactions | // ignore validation errors in resurrected transactions | ||||
CValidationState stateDummy; | CValidationState stateDummy; | ||||
if (tx.IsCoinBase() || | if (tx.IsCoinBase() || | ||||
!AcceptToMemoryPool(config, mempool, stateDummy, it, false, | !AcceptToMemoryPool(config, mempool, stateDummy, it, false, | ||||
nullptr, nullptr, true)) { | nullptr, nullptr, true)) { | ||||
mempool.removeRecursive(tx, MemPoolRemovalReason::REORG); | mempool.removeRecursive(tx, MemPoolRemovalReason::REORG); | ||||
▲ Show 20 Lines • Show All 788 Lines • ▼ Show 20 Lines | if (currentBlockSize > nMaxBlockSize) { | ||||
return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, | return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, | ||||
"size limits failed"); | "size limits failed"); | ||||
} | } | ||||
// And a valid coinbase. | // And a valid coinbase. | ||||
if (!CheckCoinbase(*block.vtx[0], state, false)) { | if (!CheckCoinbase(*block.vtx[0], state, false)) { | ||||
return state.Invalid(false, state.GetRejectCode(), | return state.Invalid(false, state.GetRejectCode(), | ||||
state.GetRejectReason(), | state.GetRejectReason(), | ||||
strprintf("Coinbase check failed (txid %s) %s", | strprintf("Coinbase check failed (txhash %s) %s", | ||||
block.vtx[0]->GetHash().ToString(), | block.vtx[0]->GetHash().ToString(), | ||||
state.GetDebugMessage())); | state.GetDebugMessage())); | ||||
} | } | ||||
// Keep track of the sigops count. | // Keep track of the sigops count. | ||||
uint64_t nSigOps = 0; | uint64_t nSigOps = 0; | ||||
auto nMaxSigOpsCount = GetMaxBlockSigOpsCount(currentBlockSize); | auto nMaxSigOpsCount = GetMaxBlockSigOpsCount(currentBlockSize); | ||||
Show All 21 Lines | while (true) { | ||||
// Check that the transaction is valid. because this check differs for | // Check that the transaction is valid. because this check differs for | ||||
// the coinbase, the loos is arranged such as this only runs after at | // the coinbase, the loos is arranged such as this only runs after at | ||||
// least one increment. | // least one increment. | ||||
tx = block.vtx[i].get(); | tx = block.vtx[i].get(); | ||||
if (!CheckRegularTransaction(*tx, state, false)) { | if (!CheckRegularTransaction(*tx, state, false)) { | ||||
return state.Invalid( | return state.Invalid( | ||||
false, state.GetRejectCode(), state.GetRejectReason(), | false, state.GetRejectCode(), state.GetRejectReason(), | ||||
strprintf("Transaction check failed (txid %s) %s", | strprintf("Transaction check failed (txhash %s) %s", | ||||
tx->GetHash().ToString(), state.GetDebugMessage())); | tx->GetHash().ToString(), state.GetDebugMessage())); | ||||
} | } | ||||
} | } | ||||
if (fCheckPOW && fCheckMerkleRoot) { | if (fCheckPOW && fCheckMerkleRoot) { | ||||
block.fChecked = true; | block.fChecked = true; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 1,644 Lines • ▼ Show 20 Lines | try { | ||||
} else { | } else { | ||||
++failed; | ++failed; | ||||
} | } | ||||
} else { | } else { | ||||
++skipped; | ++skipped; | ||||
} | } | ||||
if (ShutdownRequested()) return false; | if (ShutdownRequested()) return false; | ||||
} | } | ||||
std::map<uint256, Amount> mapDeltas; | std::map<TxHash, Amount> mapDeltas; | ||||
file >> mapDeltas; | file >> mapDeltas; | ||||
for (const auto &i : mapDeltas) { | for (const auto &i : mapDeltas) { | ||||
mempool.PrioritiseTransaction(i.first, i.first.ToString(), | mempool.PrioritiseTransaction(i.first, i.first.ToString(), | ||||
prioritydummy, i.second); | prioritydummy, i.second); | ||||
} | } | ||||
} catch (const std::exception &e) { | } catch (const std::exception &e) { | ||||
LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing " | LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing " | ||||
"anyway.\n", | "anyway.\n", | ||||
e.what()); | e.what()); | ||||
return false; | return false; | ||||
} | } | ||||
LogPrintf("Imported mempool transactions from disk: %i successes, %i " | LogPrintf("Imported mempool transactions from disk: %i successes, %i " | ||||
"failed, %i expired\n", | "failed, %i expired\n", | ||||
count, failed, skipped); | count, failed, skipped); | ||||
return true; | return true; | ||||
} | } | ||||
void DumpMempool(void) { | void DumpMempool(void) { | ||||
int64_t start = GetTimeMicros(); | int64_t start = GetTimeMicros(); | ||||
std::map<uint256, Amount> mapDeltas; | std::map<TxHash, Amount> mapDeltas; | ||||
std::vector<TxMempoolInfo> vinfo; | std::vector<TxMempoolInfo> vinfo; | ||||
{ | { | ||||
LOCK(mempool.cs); | LOCK(mempool.cs); | ||||
for (const auto &i : mempool.mapDeltas) { | for (const auto &i : mempool.mapDeltas) { | ||||
mapDeltas[i.first] = i.second.second.GetSatoshis(); | mapDeltas[i.first] = i.second.second.GetSatoshis(); | ||||
} | } | ||||
vinfo = mempool.infoAll(); | vinfo = mempool.infoAll(); | ||||
▲ Show 20 Lines • Show All 66 Lines • Show Last 20 Lines |