mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-19 13:27:35 +02:00
Merge 94d56b9def into merged_master (Bitcoin PR bitcoin/bitcoin#30141)
Added FIXMEs to blind_tests. TODO: de-globalise range/surjection proof sig cache logic.
This commit is contained in:
commit
aacf403349
19 changed files with 278 additions and 222 deletions
|
|
@ -141,6 +141,7 @@ const CBlockIndex* Chainstate::FindForkInGlobalIndex(const CBlockLocator& locato
|
|||
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
|
||||
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
|
||||
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
|
||||
ValidationCache& validation_cache,
|
||||
std::vector<CCheck*>* pvChecks = nullptr)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
||||
|
|
@ -406,7 +407,8 @@ void Chainstate::MaybeUpdateMempoolForReorg(
|
|||
* */
|
||||
static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationState& state,
|
||||
const CCoinsViewCache& view, const CTxMemPool& pool,
|
||||
unsigned int flags, PrecomputedTransactionData& txdata, CCoinsViewCache& coins_tip)
|
||||
unsigned int flags, PrecomputedTransactionData& txdata, CCoinsViewCache& coins_tip,
|
||||
ValidationCache& validation_cache)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
|
|
@ -442,7 +444,7 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationS
|
|||
}
|
||||
|
||||
// Call CheckInputScripts() to cache signature and script validity against current tip consensus rules.
|
||||
return CheckInputScripts(tx, state, view, flags, /* cacheSigStore= */ true, /* cacheFullScriptStore= */ true, txdata);
|
||||
return CheckInputScripts(tx, state, view, flags, /* cacheSigStore= */ true, /* cacheFullScriptStore= */ true, txdata, validation_cache);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
|
@ -863,6 +865,11 @@ private:
|
|||
// or the peg-in paid enough subsidy
|
||||
return true;
|
||||
}
|
||||
|
||||
ValidationCache& GetValidationCache()
|
||||
{
|
||||
return m_active_chainstate.m_chainman.m_validation_cache;
|
||||
}
|
||||
|
||||
private:
|
||||
CTxMemPool& m_pool;
|
||||
|
|
@ -1465,13 +1472,13 @@ bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws)
|
|||
|
||||
// Check input scripts and signatures.
|
||||
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
||||
if (!CheckInputScripts(tx, state, m_view, scriptVerifyFlags, true, false, ws.m_precomputed_txdata)) {
|
||||
if (!CheckInputScripts(tx, state, m_view, scriptVerifyFlags, true, false, ws.m_precomputed_txdata, GetValidationCache())) {
|
||||
// SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we
|
||||
// need to turn both off, and compare against just turning off CLEANSTACK
|
||||
// to see if the failure is specifically due to witness validation.
|
||||
TxValidationState state_dummy; // Want reported failures to be from first CheckInputScripts
|
||||
if (!tx.HasWitness() && CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, ws.m_precomputed_txdata) &&
|
||||
!CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, ws.m_precomputed_txdata)) {
|
||||
if (!tx.HasWitness() && CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, ws.m_precomputed_txdata, GetValidationCache()) &&
|
||||
!CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, ws.m_precomputed_txdata, GetValidationCache())) {
|
||||
// Only the witness is missing, so the transaction itself may be fine.
|
||||
state.Invalid(TxValidationResult::TX_WITNESS_STRIPPED,
|
||||
state.GetRejectReason(), state.GetDebugMessage());
|
||||
|
|
@ -1507,7 +1514,7 @@ bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws)
|
|||
// transactions into the mempool can be exploited as a DoS attack.
|
||||
unsigned int currentBlockScriptVerifyFlags{GetBlockScriptFlags(*m_active_chainstate.m_chain.Tip(), m_active_chainstate.m_chainman)};
|
||||
if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags,
|
||||
ws.m_precomputed_txdata, m_active_chainstate.CoinsTip())) {
|
||||
ws.m_precomputed_txdata, m_active_chainstate.CoinsTip(), GetValidationCache())) {
|
||||
LogPrintf("BUG! PLEASE REPORT THIS! CheckInputScripts failed against latest-block but not STANDARD flags %s, %s\n", hash.ToString(), state.ToString());
|
||||
return Assume(false);
|
||||
}
|
||||
|
|
@ -2327,29 +2334,23 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund
|
|||
bool CScriptCheck::operator()() {
|
||||
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
|
||||
const CScriptWitness *witness = ptxTo->witness.vtxinwit.size() > nIn ? &ptxTo->witness.vtxinwit[nIn].scriptWitness : nullptr;
|
||||
return VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *txdata), &error);
|
||||
return VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *m_signature_cache, *txdata), &error);
|
||||
}
|
||||
|
||||
static CuckooCache::cache<uint256, SignatureCacheHasher> g_scriptExecutionCache;
|
||||
static CSHA256 g_scriptExecutionCacheHasher;
|
||||
|
||||
bool InitScriptExecutionCache(size_t max_size_bytes)
|
||||
ValidationCache::ValidationCache(const size_t script_execution_cache_bytes, const size_t signature_cache_bytes)
|
||||
: m_signature_cache{signature_cache_bytes}
|
||||
{
|
||||
// Setup the salted hasher
|
||||
uint256 nonce = GetRandHash();
|
||||
// We want the nonce to be 64 bytes long to force the hasher to process
|
||||
// this chunk, which makes later hash computations more efficient. We
|
||||
// just write our 32-byte entropy twice to fill the 64 bytes.
|
||||
g_scriptExecutionCacheHasher.Write(nonce.begin(), 32);
|
||||
g_scriptExecutionCacheHasher.Write(nonce.begin(), 32);
|
||||
m_script_execution_cache_hasher.Write(nonce.begin(), 32);
|
||||
m_script_execution_cache_hasher.Write(nonce.begin(), 32);
|
||||
|
||||
auto setup_results = g_scriptExecutionCache.setup_bytes(max_size_bytes);
|
||||
if (!setup_results) return false;
|
||||
|
||||
const auto [num_elems, approx_size_bytes] = *setup_results;
|
||||
const auto [num_elems, approx_size_bytes] = m_script_execution_cache.setup_bytes(script_execution_cache_bytes);
|
||||
LogPrintf("Using %zu MiB out of %zu MiB requested for script execution cache, able to store %zu elements\n",
|
||||
approx_size_bytes >> 20, max_size_bytes >> 20, num_elems);
|
||||
return true;
|
||||
approx_size_bytes >> 20, script_execution_cache_bytes >> 20, num_elems);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2374,6 +2375,7 @@ bool InitScriptExecutionCache(size_t max_size_bytes)
|
|||
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
|
||||
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
|
||||
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
|
||||
ValidationCache& validation_cache,
|
||||
std::vector<CCheck*>* pvChecks)
|
||||
{
|
||||
if (tx.IsCoinBase()) return true;
|
||||
|
|
@ -2388,10 +2390,10 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
|
|||
// properly commits to the scriptPubKey in the inputs view of that
|
||||
// transaction).
|
||||
uint256 hashCacheEntry;
|
||||
CSHA256 hasher = g_scriptExecutionCacheHasher;
|
||||
CSHA256 hasher = validation_cache.ScriptExecutionCacheHasher();
|
||||
hasher.Write(UCharCast(tx.GetWitnessHash().begin()), 32).Write((unsigned char*)&flags, sizeof(flags)).Finalize(hashCacheEntry.begin());
|
||||
AssertLockHeld(cs_main); //TODO: Remove this requirement by making CuckooCache not require external locks
|
||||
if (g_scriptExecutionCache.contains(hashCacheEntry, !cacheFullScriptStore)) {
|
||||
if (validation_cache.m_script_execution_cache.contains(hashCacheEntry, !cacheFullScriptStore)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -2425,7 +2427,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
|
|||
// spent being checked as a part of CScriptCheck.
|
||||
|
||||
// Verify signature
|
||||
CCheck* check = new CScriptCheck(txdata.m_spent_outputs[i], tx, i, flags, cacheSigStore, &txdata);
|
||||
CCheck* check = new CScriptCheck(txdata.m_spent_outputs[i], tx, validation_cache.m_signature_cache, i, flags, cacheSigStore, &txdata);
|
||||
ScriptError serror = QueueCheck(pvChecks, check);
|
||||
if (serror != SCRIPT_ERR_OK) {
|
||||
if (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) {
|
||||
|
|
@ -2437,7 +2439,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
|
|||
// splitting the network between upgraded and
|
||||
// non-upgraded nodes by banning CONSENSUS-failing
|
||||
// data providers.
|
||||
CScriptCheck check2(txdata.m_spent_outputs[i], tx, i,
|
||||
CScriptCheck check2(txdata.m_spent_outputs[i], tx, validation_cache.m_signature_cache, i,
|
||||
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata);
|
||||
if (check2()) {
|
||||
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(serror)));
|
||||
|
|
@ -2459,7 +2461,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
|
|||
if (cacheFullScriptStore && !pvChecks) {
|
||||
// We executed all of the provided scripts, and were told to
|
||||
// cache the result. Do so now.
|
||||
g_scriptExecutionCache.insert(hashCacheEntry);
|
||||
validation_cache.m_script_execution_cache.insert(hashCacheEntry);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -3035,7 +3037,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
std::vector<CCheck*> vChecks;
|
||||
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
|
||||
TxValidationState tx_state;
|
||||
if (fScriptChecks && !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], parallel_script_checks ? &vChecks : nullptr)) {
|
||||
if (fScriptChecks && !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], m_chainman.m_validation_cache, parallel_script_checks ? &vChecks : nullptr)) {
|
||||
// Any transaction validation failure in ConnectBlock is a block consensus failure
|
||||
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
|
||||
tx_state.GetRejectReason(), tx_state.GetDebugMessage());
|
||||
|
|
@ -6857,7 +6859,8 @@ ChainstateManager::ChainstateManager(const util::SignalInterrupt& interrupt, Opt
|
|||
: m_script_check_queue{/*batch_size=*/128, options.worker_threads_num},
|
||||
m_interrupt{interrupt},
|
||||
m_options{Flatten(std::move(options))},
|
||||
m_blockman{interrupt, std::move(blockman_options)}
|
||||
m_blockman{interrupt, std::move(blockman_options)},
|
||||
m_validation_cache{m_options.script_execution_cache_bytes, m_options.signature_cache_bytes}
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue