Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 643 Lines • ▼ Show 20 Lines | for (const CTxIn &txin : tx.vin) { | ||||
// At this point we haven't actually checked if the coins are all | // At this point we haven't actually checked if the coins are all | ||||
// available (or shouldn't assume we have, since CheckInputs does). So | // available (or shouldn't assume we have, since CheckInputs does). So | ||||
// we just return failure if the inputs are not available here, and then | // we just return failure if the inputs are not available here, and then | ||||
// only have to check equivalence for available inputs. | // only have to check equivalence for available inputs. | ||||
if (coin.IsSpent()) { | if (coin.IsSpent()) { | ||||
return false; | return false; | ||||
} | } | ||||
const CTransactionRef &txFrom = pool.get(txin.prevout.hash); | const CTransactionRef &txFrom = pool.get(txin.prevout.unspentid); | ||||
if (txFrom) { | if (txFrom) { | ||||
assert(txFrom->GetHash() == txin.prevout.hash); | assert(txFrom->GetUnspentid() == txin.prevout.unspentid); | ||||
assert(txFrom->vout.size() > txin.prevout.n); | assert(txFrom->vout.size() > txin.prevout.n); | ||||
assert(txFrom->vout[txin.prevout.n] == coin.GetTxOut()); | assert(txFrom->vout[txin.prevout.n] == coin.GetTxOut()); | ||||
} else { | } else { | ||||
const Coin &coinFromDisk = pcoinsTip->AccessCoin(txin.prevout); | const Coin &coinFromDisk = pcoinsTip->AccessCoin(txin.prevout); | ||||
assert(!coinFromDisk.IsSpent()); | assert(!coinFromDisk.IsSpent()); | ||||
assert(coinFromDisk.GetTxOut() == coin.GetTxOut()); | assert(coinFromDisk.GetTxOut() == coin.GetTxOut()); | ||||
} | } | ||||
} | } | ||||
return CheckInputs(tx, state, view, true, flags, cacheSigStore, true, | return CheckInputs(tx, state, view, true, flags, cacheSigStore, true, | ||||
txdata); | txdata); | ||||
} | } | ||||
static bool AcceptToMemoryPoolWorker( | 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 CAmount &nAbsurdFee, | bool fOverrideMempoolLimit, const CAmount &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.GetId(); | const txhash_t txhash = tx.GetHash(); | ||||
const unspentid_t unspentid = tx.GetUnspentid(); | |||||
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(unspentid, 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 | ||||
CAmount nModifiedFees = nFees.GetSatoshis(); | CAmount nModifiedFees = nFees.GetSatoshis(); | ||||
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 CAmount nAbsurdFee) { | bool fOverrideMempoolLimit, const CAmount 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 txhash_t &txhash, | ||||
CTransactionRef &txOut, uint256 &hashBlock) { | CTransactionRef &txOut, uint256 &hashBlock) { | ||||
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->GetId() != txid) | if (txOut->GetHash() != txhash) | ||||
return error("%s: txid mismatch", __func__); | return error("%s: txhash mismatch", __func__); | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
////////////////////////////////////////////////////////////////////////////// | ////////////////////////////////////////////////////////////////////////////// | ||||
▲ Show 20 Lines • Show All 497 Lines • ▼ Show 20 Lines | if (view.HaveCoin(out)) { | ||||
fClean = false; | fClean = false; | ||||
} | } | ||||
if (undo.GetHeight() == 0) { | if (undo.GetHeight() == 0) { | ||||
// Missing undo metadata (height and coinbase). Older versions included | // Missing undo metadata (height and coinbase). Older versions included | ||||
// this information only in undo records for the last spend of a | // this information only in undo records for the last spend of a | ||||
// transactions' outputs. This implies that it must be present for some | // transactions' outputs. This implies that it must be present for some | ||||
// other output of the same tx. | // other output of the same tx. | ||||
const Coin &alternate = AccessByTxid(view, out.hash); | COutPoint iter(out.unspentid, 0); | ||||
if (alternate.IsSpent()) { | while (iter.n < MAX_OUTPUTS_PER_TX) { | ||||
const Coin &alternate = view.AccessCoin(iter); | |||||
if (!alternate.IsSpent()) { | |||||
// This is somewhat ugly, but hopefully utility is limited. This | |||||
// is only | |||||
// useful when working from legacy on disck data. In any case, | |||||
// putting | |||||
// the correct information in there doesn't hurt. | |||||
const_cast<Coin &>(undo) = | |||||
Coin(undo.GetTxOut(), alternate.GetHeight(), | |||||
alternate.IsCoinBase()); | |||||
break; | |||||
} | |||||
++iter.n; | |||||
} | |||||
if (iter.n == MAX_OUTPUTS_PER_TX) { | |||||
// Adding output for transaction without known metadata | // Adding output for transaction without known metadata | ||||
return DISCONNECT_FAILED; | return DISCONNECT_FAILED; | ||||
} | } | ||||
// This is somewhat ugly, but hopefully utility is limited. This is only | |||||
// useful when working from legacy on disck data. In any case, putting | |||||
// the correct information in there doesn't hurt. | |||||
const_cast<Coin &>(undo) = Coin(undo.GetTxOut(), alternate.GetHeight(), | |||||
alternate.IsCoinBase()); | |||||
} | } | ||||
view.AddCoin(out, undo, undo.IsCoinBase()); | view.AddCoin(out, undo, undo.IsCoinBase()); | ||||
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN; | return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN; | ||||
} | } | ||||
/** | /** | ||||
* Undo the effects of this block (with given index) on the UTXO set represented | * Undo the effects of this block (with given index) on the UTXO set represented | ||||
Show All 29 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.GetId(); | unspentid_t unspentid = tx.GetUnspentid(); | ||||
// 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(unspentid, 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 277 Lines • ▼ Show 20 Lines | static bool ConnectBlock(const Config &config, const CBlock &block, | ||||
// block hash at that height doesn't correspond. | // block hash at that height doesn't correspond. | ||||
fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || | fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || | ||||
!(pindexBIP34height->GetBlockHash() == | !(pindexBIP34height->GetBlockHash() == | ||||
chainparams.GetConsensus().BIP34Hash)); | chainparams.GetConsensus().BIP34Hash)); | ||||
if (fEnforceBIP30) { | if (fEnforceBIP30) { | ||||
for (const auto &tx : block.vtx) { | for (const auto &tx : block.vtx) { | ||||
for (size_t o = 0; o < tx->vout.size(); o++) { | for (size_t o = 0; o < tx->vout.size(); o++) { | ||||
if (view.HaveCoin(COutPoint(tx->GetHash(), o))) { | if (view.HaveCoin(COutPoint(tx->GetUnspentid(), o))) { | ||||
return state.DoS( | return state.DoS( | ||||
100, | 100, | ||||
error("ConnectBlock(): tried to overwrite transaction"), | error("ConnectBlock(): tried to overwrite transaction"), | ||||
REJECT_INVALID, "bad-txns-BIP30"); | REJECT_INVALID, "bad-txns-BIP30"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
Show All 25 Lines | static bool ConnectBlock(const Config &config, const CBlock &block, | ||||
// Sigops counting. We need to do it again because of P2SH. | // Sigops counting. We need to do it again because of P2SH. | ||||
uint64_t nSigOpsCount = 0; | uint64_t nSigOpsCount = 0; | ||||
const uint64_t currentBlockSize = | const uint64_t currentBlockSize = | ||||
::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION); | ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION); | ||||
const uint64_t nMaxSigOpsCount = GetMaxBlockSigOpsCount(currentBlockSize); | const uint64_t nMaxSigOpsCount = GetMaxBlockSigOpsCount(currentBlockSize); | ||||
CDiskTxPos pos(pindex->GetBlockPos(), | CDiskTxPos pos(pindex->GetBlockPos(), | ||||
GetSizeOfCompactSize(block.vtx.size())); | GetSizeOfCompactSize(block.vtx.size())); | ||||
std::vector<std::pair<uint256, CDiskTxPos>> vPos; | std::vector<std::pair<txhash_t, CDiskTxPos>> vPos; | ||||
vPos.reserve(block.vtx.size()); | vPos.reserve(block.vtx.size()); | ||||
blockundo.vtxundo.reserve(block.vtx.size() - 1); | blockundo.vtxundo.reserve(block.vtx.size() - 1); | ||||
for (size_t i = 0; i < block.vtx.size(); i++) { | for (size_t i = 0; i < block.vtx.size(); i++) { | ||||
const CTransaction &tx = *(block.vtx[i]); | const CTransaction &tx = *(block.vtx[i]); | ||||
nInputs += tx.vin.size(); | nInputs += tx.vin.size(); | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | for (size_t i = 0; i < block.vtx.size(); i++) { | ||||
// consult the cache, though). | // consult the cache, though). | ||||
bool fCacheResults = fJustCheck; | bool fCacheResults = fJustCheck; | ||||
std::vector<CScriptCheck> vChecks; | std::vector<CScriptCheck> vChecks; | ||||
if (!CheckInputs(tx, state, view, fScriptChecks, flags, | if (!CheckInputs(tx, state, view, fScriptChecks, flags, | ||||
fCacheResults, fCacheResults, | fCacheResults, fCacheResults, | ||||
PrecomputedTransactionData(tx), &vChecks)) { | PrecomputedTransactionData(tx), &vChecks)) { | ||||
return error("ConnectBlock(): CheckInputs on %s failed with %s", | return error("ConnectBlock(): CheckInputs on %s failed with %s", | ||||
tx.GetId().ToString(), FormatStateMessage(state)); | tx.GetHash().ToString(), | ||||
FormatStateMessage(state)); | |||||
} | } | ||||
control.Add(vChecks); | control.Add(vChecks); | ||||
} | } | ||||
CTxUndo undoDummy; | CTxUndo undoDummy; | ||||
if (i > 0) { | if (i > 0) { | ||||
blockundo.vtxundo.push_back(CTxUndo()); | blockundo.vtxundo.push_back(CTxUndo()); | ||||
} | } | ||||
UpdateCoins(tx, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), | UpdateCoins(tx, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), | ||||
pindex->nHeight); | pindex->nHeight); | ||||
vPos.push_back(std::make_pair(tx.GetId(), pos)); | vPos.push_back(std::make_pair(tx.GetHash(), pos)); | ||||
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); | pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); | ||||
} | } | ||||
int64_t nTime3 = GetTimeMicros(); | int64_t nTime3 = GetTimeMicros(); | ||||
nTimeConnect += nTime3 - nTime2; | nTimeConnect += nTime3 - nTime2; | ||||
LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, " | LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, " | ||||
"%.3fms/txin) [%.2fs]\n", | "%.3fms/txin) [%.2fs]\n", | ||||
(unsigned)block.vtx.size(), 0.001 * (nTime3 - nTime2), | (unsigned)block.vtx.size(), 0.001 * (nTime3 - nTime2), | ||||
▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | static bool ConnectBlock(const Config &config, const CBlock &block, | ||||
view.SetBestBlock(pindex->GetBlockHash()); | view.SetBestBlock(pindex->GetBlockHash()); | ||||
int64_t nTime5 = GetTimeMicros(); | int64_t nTime5 = GetTimeMicros(); | ||||
nTimeIndex += nTime5 - nTime4; | nTimeIndex += nTime5 - nTime4; | ||||
LogPrint("bench", " - Index writing: %.2fms [%.2fs]\n", | LogPrint("bench", " - Index writing: %.2fms [%.2fs]\n", | ||||
0.001 * (nTime5 - nTime4), nTimeIndex * 0.000001); | 0.001 * (nTime5 - nTime4), nTimeIndex * 0.000001); | ||||
// Watch for changes to the previous coinbase transaction. | // Watch for changes to the previous coinbase transaction. | ||||
static uint256 hashPrevBestCoinBase; | static txhash_t hashPrevBestCoinBase; | ||||
GetMainSignals().UpdatedTransaction(hashPrevBestCoinBase); | GetMainSignals().UpdatedTransaction(hashPrevBestCoinBase); | ||||
hashPrevBestCoinBase = block.vtx[0]->GetId(); | hashPrevBestCoinBase = block.vtx[0]->GetHash(); | ||||
int64_t nTime6 = GetTimeMicros(); | int64_t nTime6 = GetTimeMicros(); | ||||
nTimeCallbacks += nTime6 - nTime5; | nTimeCallbacks += nTime6 - nTime5; | ||||
LogPrint("bench", " - Callbacks: %.2fms [%.2fs]\n", | LogPrint("bench", " - Callbacks: %.2fms [%.2fs]\n", | ||||
0.001 * (nTime6 - nTime5), nTimeCallbacks * 0.000001); | 0.001 * (nTime6 - nTime5), nTimeCallbacks * 0.000001); | ||||
return true; | return true; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 273 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_t> 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); | ||||
} else if (mempool.exists(tx.GetId())) { | } else if (mempool.exists(tx.GetHash())) { | ||||
vHashUpdate.push_back(tx.GetId()); | vHashUpdate.push_back(tx.GetHash()); | ||||
} | } | ||||
} | } | ||||
// AcceptToMemoryPool/addUnchecked all assume that new mempool entries | // AcceptToMemoryPool/addUnchecked all assume that new mempool entries | ||||
// have no in-mempool children, which is generally not true when adding | // have no in-mempool children, which is generally not true when adding | ||||
// previously-confirmed transactions back to the mempool. | // previously-confirmed transactions back to the mempool. | ||||
// UpdateTransactionsFromBlock finds descendants of any transactions in | // UpdateTransactionsFromBlock finds descendants of any transactions in | ||||
// this block that were added back and cleans up the mempool state. | // this block that were added back and cleans up the mempool state. | ||||
mempool.UpdateTransactionsFromBlock(vHashUpdate); | mempool.UpdateTransactionsFromBlock(vHashUpdate); | ||||
▲ Show 20 Lines • Show All 778 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]->GetId().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); | ||||
// Check transactions | // Check transactions | ||||
Show All 20 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->GetId().ToString(), state.GetDebugMessage())); | tx->GetHash().ToString(), state.GetDebugMessage())); | ||||
} | } | ||||
} | } | ||||
if (fCheckPOW && fCheckMerkleRoot) { | if (fCheckPOW && fCheckMerkleRoot) { | ||||
block.fChecked = true; | block.fChecked = true; | ||||
} | } | ||||
return true; | return true; | ||||
▲ Show 20 Lines • Show All 1,624 Lines • ▼ Show 20 Lines | try { | ||||
int64_t nTime; | int64_t nTime; | ||||
int64_t nFeeDelta; | int64_t nFeeDelta; | ||||
file >> tx; | file >> tx; | ||||
file >> nTime; | file >> nTime; | ||||
file >> nFeeDelta; | file >> nFeeDelta; | ||||
CAmount amountdelta = nFeeDelta; | CAmount amountdelta = nFeeDelta; | ||||
if (amountdelta) { | if (amountdelta) { | ||||
mempool.PrioritiseTransaction(tx->GetId(), | mempool.PrioritiseTransaction(tx->GetHash(), | ||||
tx->GetId().ToString(), | tx->GetHash().ToString(), | ||||
prioritydummy, amountdelta); | prioritydummy, amountdelta); | ||||
} | } | ||||
CValidationState state; | CValidationState state; | ||||
if (nTime + nExpiryTimeout > nNow) { | if (nTime + nExpiryTimeout > nNow) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
AcceptToMemoryPoolWithTime(config, mempool, state, tx, true, | AcceptToMemoryPoolWithTime(config, mempool, state, tx, true, | ||||
nullptr, nTime); | nullptr, nTime); | ||||
if (state.IsValid()) { | if (state.IsValid()) { | ||||
++count; | ++count; | ||||
} else { | } else { | ||||
++failed; | ++failed; | ||||
} | } | ||||
} else { | } else { | ||||
++skipped; | ++skipped; | ||||
} | } | ||||
if (ShutdownRequested()) return false; | if (ShutdownRequested()) return false; | ||||
} | } | ||||
std::map<uint256, CAmount> mapDeltas; | std::map<txhash_t, CAmount> 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 " | ||||
Show All 36 Lines | try { | ||||
uint64_t version = MEMPOOL_DUMP_VERSION; | uint64_t version = MEMPOOL_DUMP_VERSION; | ||||
file << version; | file << version; | ||||
file << (uint64_t)vinfo.size(); | file << (uint64_t)vinfo.size(); | ||||
for (const auto &i : vinfo) { | for (const auto &i : vinfo) { | ||||
file << *(i.tx); | file << *(i.tx); | ||||
file << (int64_t)i.nTime; | file << (int64_t)i.nTime; | ||||
file << (int64_t)i.nFeeDelta.GetSatoshis(); | file << (int64_t)i.nFeeDelta.GetSatoshis(); | ||||
mapDeltas.erase(i.tx->GetId()); | mapDeltas.erase(i.tx->GetHash()); | ||||
} | } | ||||
file << mapDeltas; | file << mapDeltas; | ||||
FileCommit(file.Get()); | FileCommit(file.Get()); | ||||
file.fclose(); | file.fclose(); | ||||
RenameOver(GetDataDir() / "mempool.dat.new", | RenameOver(GetDataDir() / "mempool.dat.new", | ||||
GetDataDir() / "mempool.dat"); | GetDataDir() / "mempool.dat"); | ||||
int64_t last = GetTimeMicros(); | int64_t last = GetTimeMicros(); | ||||
Show All 36 Lines |