refactor: Remove special treatment for single threaded script checking #32575

pull fjahr wants to merge 4 commits into bitcoin:master from fjahr:2025-05-CheckInputScript-split changing 3 files +106 −81
  1. fjahr commented at 8:52 pm on May 20, 2025: contributor

    This topic has been motivated by my work on batch validation and a related conversation just happened here: #32467 (review)

    CheckInputScripts currently only does what its name implies if there is no multithreading usage for validation. If there are worker threads available the function instead only creates the checks and puts them into a vector. Aside from some shared pre-checks the multithreaded code path is much simpler compared to the rest. This dual use makes the function hard to grasp and its naming confusing.

    This PR refactors this code to be more readable and improves documentation aside from unifying the single threaded and the multithreaded cases by letting checkqueue handle single threaded script checking as well. The code is already suited for this and there seems to be not noticable performance overhead from this.

  2. DrahtBot commented at 8:52 pm on May 20, 2025: contributor

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/32575.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    Concept ACK theuni, stickies-v
    Approach ACK sedited
    Approach NACK l0rinc

    If your review is incorrectly listed, please copy-paste <!–meta-tag:bot-skip–> into the comment that the bot should ignore.

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #32317 (kernel: Separate UTXO set access from validation functions by sedited)
    • #29843 (policy: Allow non-standard scripts with -acceptnonstdtxn=1 (test nets only) by ajtowns)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

  3. theuni commented at 8:57 pm on May 20, 2025: member

    Concept ACK. Anything to clean this up :)

    See also #32317 which moves some of this functionality around similarly.

  4. fjahr force-pushed on May 20, 2025
  5. fjahr marked this as ready for review on May 20, 2025
  6. fjahr commented at 9:49 pm on May 20, 2025: contributor
    Cleaned up the comments so this should be ready for more detailed feedback, but expect a rebase once #32467 is merged.
  7. in src/validation.cpp:2207 in 4262b75726 outdated
    2202+std::vector<CScriptCheck> PrepareInputScriptChecks(const CTransaction& tx,
    2203+                       const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
    2204+                       bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
    2205+                       ValidationCache& validation_cache) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
    2206+{
    2207+    if (tx.IsCoinBase()) return {};
    


    fjahr commented at 9:54 pm on May 20, 2025:
    I have been contemplating kicking this out (here and in CheckInputScripts) or even turning this into an assert because the callers already handle this everywhere. On the other hand it conceptually makes sense to return early if we know there is nothing to check because we don’t expect any inputs. So I am still undecided on this one and happy to hear opinions.
  8. sedited commented at 10:12 pm on May 20, 2025: contributor
    Concept ACK
  9. fjahr renamed this:
    RFC: refactor: Split multithreaded case out of CheckInputScripts
    refactor: Split multithreaded case out of CheckInputScripts
    on May 20, 2025
  10. DrahtBot added the label Refactoring on May 20, 2025
  11. stickies-v commented at 10:28 am on May 21, 2025: contributor
    Concept ACK
  12. DrahtBot added the label Needs rebase on May 22, 2025
  13. theuni commented at 6:29 pm on May 22, 2025: member
    Ready for rebase.
  14. fjahr force-pushed on May 22, 2025
  15. fjahr commented at 8:21 pm on May 22, 2025: contributor

    Ready for rebase.

    Done, ready for review :)

  16. DrahtBot removed the label Needs rebase on May 22, 2025
  17. in src/validation.cpp:2733 in 5dfd10c725 outdated
    2741-            if (!tx_ok) {
    2742-                // Any transaction validation failure in ConnectBlock is a block consensus failure
    2743-                state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
    2744-                              tx_state.GetRejectReason(), tx_state.GetDebugMessage());
    2745-                break;
    2746+                if (!CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], m_chainman.m_validation_cache)) {
    


    theuni commented at 9:26 pm on May 22, 2025:

    I think this should go a little further. At this point, CheckInputScripts is essentially meant to be a mempool function and PrepareInputScriptChecks is for block validation, except for the weird edge-case when we’re single-threaded.

    I think I’d rather just see this go all the way and make the split purposeful. Something like:

     0diff --git a/src/validation.cpp b/src/validation.cpp
     1index 1c81620889f..d9a447b460f 100644
     2--- a/src/validation.cpp
     3+++ b/src/validation.cpp
     4@@ -2723,19 +2723,18 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
     5         {
     6             bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
     7             TxValidationState tx_state;
     8-            // If parallel script checking is possible (worker threads are available) the checks are appended to a
     9-            // vector without running them. The vector is then added to control which runs the checks asynchronously.
    10-            // Otherwise, CheckInputScripts runs the checks on a single thread before returning.
    11-            if (control) {
    12-                std::vector<CScriptCheck> vChecks = PrepareInputScriptChecks(tx, view, flags, fCacheResults, fCacheResults, txsdata[i], m_chainman.m_validation_cache);
    13-                control->Add(std::move(vChecks));
    14-            } else {
    15-                if (!CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], m_chainman.m_validation_cache)) {
    16-                    // Any transaction validation failure in ConnectBlock is a block consensus failure
    17-                    state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
    18-                                  tx_state.GetRejectReason(), tx_state.GetDebugMessage());
    19-                    break;
    20+            // If parallel script checking is possible (worker threads are available), they are added to control which
    21+            // which runs the checks asynchronously. Otherwise they are run here directly.
    22+            std::vector<CScriptCheck> vChecks = PrepareInputScriptChecks(tx, view, flags, fCacheResults, fCacheResults, txsdata[i], m_chainman.m_validation_cache);
    23+            if (control) control->Add(std::move(vChecks));
    24+            else {
    25+                for (auto& check : vChecks) {
    26+                    if (const auto& check_result = check()) {
    27+                        state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check_result->first)), check_result->second);
    28+                        break;
    29+                    }
    30                 }
    31+                if (!state.IsValid()) break;
    32             }
    33         }
    

    An alternative would be to make CCheckQueueControl work with 0 worker threads (maybe it already does?) and just always use it. That would completely unify the approach here.


    mzumsande commented at 10:54 pm on May 22, 2025:

    I like that idea.

    I think it would be a small change in behaviour, because if I read this line right it will add entries to the cache if 1)pvChecks is not set (single-threaded) 2) cacheFullScriptStore is set (i.e. if ConnectBlock() was called with fJustCheck=True, so currently only from TestBlockValidity). I wonder if the current behavior is intended - it seems very strange to me that mining rpcs would add entries to the script cache, but only in the -par=1 case.


    theuni commented at 6:01 pm on June 17, 2025:

    @mzumsande I think it’s very unlikely that that behavior is intended :)

    Edit: likely -> unlikely.


    theuni commented at 6:02 pm on June 17, 2025:
    Whooops, *Very unlikely.

    fjahr commented at 10:04 pm on June 19, 2025:
    Took the suggestion here.
  18. in src/validation.cpp:2216 in 5dfd10c725 outdated
    2211+        return {};
    2212+    }
    2213+
    2214+    EnsureTxData(tx, inputs, txdata);
    2215+
    2216+    std::vector<CScriptCheck> pvChecks{};
    


    mzumsande commented at 9:38 pm on May 22, 2025:
    Just a rough idea, but did you consider the alternative approach of doing the pvChecks population not here (but in ConnectBlock() or a separate function), so that CheckInputScripts could also call PrepareInputScriptChecks and there would be no duplication of the coinbase check, cache check and TxData init?

    fjahr commented at 10:03 pm on June 19, 2025:
    I have implemented this at the cost of duplicating the cache entry calculation. I think it should be fine but I will try to run some benchmarks.

    l0rinc commented at 12:03 pm on March 5, 2026:
    This isn’t a minor issue; this should be mentioned in the commit message and PR description.
  19. fjahr force-pushed on Jun 19, 2025
  20. fjahr commented at 10:00 pm on June 19, 2025: contributor

    An alternative would be to make CCheckQueueControl work with 0 worker threads (maybe it already does?) and just always use it. That would completely unify the approach here.

    Yeah, that does work basically out of the box. I tested it with this minimal change on top of the changes here: https://github.com/fjahr/bitcoin/commit/320b9a967f88ece5b98779948c47493ad7717da2 However I don’t favor that approach at the moment since there seems to be a lot of overhead when using checkqueue with a single thread and a lot of docs would need to be changed accordingly as well. Just logically you wouldn’t think about using a queue when you just have a single thread, so while it would definitely simplify the code in ConnectBlock, I am not sure it’s a bigger win than the other approaches discussed here, given that it may cause irritation in other places and a performance penalty. I am happy to be convinced of this approach though if I overlooked something.

    I have now pushed here an updated version that addresses both the suggestions from @theuni and @mzumsande .

  21. in src/validation.cpp:2712 in edcc004c40 outdated
    2716-                tx_ok = CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], m_chainman.m_validation_cache, &vChecks);
    2717-                if (tx_ok) control->Add(std::move(vChecks));
    2718-            } else {
    2719-                tx_ok = CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], m_chainman.m_validation_cache);
    2720+            // If parallel script checking is possible (worker threads are available), they are added to control which
    2721+            // which runs the checks asynchronously. Otherwise they are run here directly.
    


    maflcko commented at 9:34 am on June 20, 2025:
    “this transactions’s input scripts” -> “this transaction’s input scripts” [incorrect possessive form]
    duplicate “which” in “control which which runs the checks asynchronously” -> remove one “which” [typo duplication]
    

    fjahr commented at 12:59 pm on June 20, 2025:
    both typos fixed
  22. fjahr force-pushed on Jun 20, 2025
  23. DrahtBot added the label Needs rebase on Aug 12, 2025
  24. fjahr force-pushed on Aug 13, 2025
  25. DrahtBot removed the label Needs rebase on Aug 13, 2025
  26. DrahtBot added the label Needs rebase on Oct 8, 2025
  27. fjahr force-pushed on Oct 9, 2025
  28. DrahtBot removed the label Needs rebase on Oct 9, 2025
  29. fjahr force-pushed on Oct 11, 2025
  30. fjahr commented at 8:44 pm on October 11, 2025: contributor
    Silent merged conflict was resolved with the last push but the CI still fails with some problem fetching the qa-assets repo, seems to be a github issue since I also see it in master, retrying one more time with close/re-open to see if it works again now.
  31. fjahr closed this on Oct 11, 2025

  32. fjahr reopened this on Oct 11, 2025

  33. maflcko closed this on Oct 13, 2025

  34. maflcko reopened this on Oct 13, 2025

  35. fjahr commented at 5:41 pm on October 13, 2025: contributor
    Turning it off and on again finally worked :p
  36. in src/validation.cpp:2122 in 413f56423a outdated
    2118@@ -2119,6 +2119,32 @@ ValidationCache::ValidationCache(const size_t script_execution_cache_bytes, cons
    2119               approx_size_bytes >> 20, script_execution_cache_bytes >> 20, num_elems);
    2120 }
    2121 
    2122+uint256 GetHashCacheEntry(ValidationCache& validation_cache, const CTransaction& tx, script_verify_flags flags)
    


    sedited commented at 9:19 pm on November 30, 2025:
    Nit: Could make these functions static.

    fjahr commented at 11:03 pm on February 27, 2026:
    Done
  37. in src/validation.cpp:2137 in 413f56423a outdated
    2132+ * flags. Note that this assumes that the inputs provided are
    2133+ * correct (ie that the transaction hash which is in tx's prevouts
    2134+ * properly commits to the scriptPubKey in the inputs view of that
    2135+ * transaction).
    2136+ */
    2137+bool IsScriptCached(ValidationCache& validation_cache, bool cacheFullScriptStore, uint256 hashCacheEntry)
    


    sedited commented at 9:37 pm on November 30, 2025:
    Why is this made a separate function?

    fjahr commented at 11:03 pm on February 27, 2026:
    I just found it nicer in terms of documentation and readability to extract this into a function. It isn’t strictly necessary but it should make the actual behavior change easier to review IMO.

    l0rinc commented at 10:15 am on March 5, 2026:

    I also don’t see the advantage of simply extracting it locally (incorrectly, since we need AssertLockHeld for the insertion as well), but I could get behind properly encapsulating the cache and exposing only the contains/insert methods, something like:

     0diff --git a/src/validation.cpp b/src/validation.cpp
     1index 67130a31cf..3947d0dfde 100644
     2--- a/src/validation.cpp
     3+++ b/src/validation.cpp
     4@@ -2036,6 +2036,18 @@ ValidationCache::ValidationCache(const size_t script_execution_cache_bytes, cons
     5               approx_size_bytes >> 20, script_execution_cache_bytes >> 20, num_elems);
     6 }
     7 
     8+bool ValidationCache::IsScriptValidated(const uint256& entry, bool erase) const
     9+{
    10+    AssertLockHeld(cs_main); //TODO: Remove this requirement by making CuckooCache not require external locks
    11+    return m_script_execution_cache.contains(entry, erase);
    12+}
    13+
    14+void ValidationCache::CacheScriptValidation(const uint256& entry)
    15+{
    16+    AssertLockHeld(cs_main); //TODO: Remove this requirement by making CuckooCache not require external locks
    17+    m_script_execution_cache.insert(entry);
    18+}
    19+
    20 /**
    21  * Check whether all of this transaction's input scripts succeed.
    22  *
    23@@ -2075,8 +2087,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
    24     uint256 hashCacheEntry;
    25     CSHA256 hasher = validation_cache.ScriptExecutionCacheHasher();
    26     hasher.Write(UCharCast(tx.GetWitnessHash().begin()), 32).Write((unsigned char*)&flags, sizeof(flags)).Finalize(hashCacheEntry.begin());
    27-    AssertLockHeld(cs_main); //TODO: Remove this requirement by making CuckooCache not require external locks
    28-    if (validation_cache.m_script_execution_cache.contains(hashCacheEntry, !cacheFullScriptStore)) {
    29+    if (validation_cache.IsScriptValidated(hashCacheEntry, !cacheFullScriptStore)) {
    30         return true;
    31     }
    32 
    33@@ -2124,7 +2135,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
    34     if (cacheFullScriptStore && !pvChecks) {
    35         // We executed all of the provided scripts, and were told to
    36         // cache the result. Do so now.
    37-        validation_cache.m_script_execution_cache.insert(hashCacheEntry);
    38+        validation_cache.CacheScriptValidation(hashCacheEntry);
    39     }
    40 
    41     return true;
    42diff --git a/src/validation.h b/src/validation.h
    43index 482772c0d6..2bec47aecc 100644
    44--- a/src/validation.h
    45+++ b/src/validation.h
    46@@ -373,8 +373,10 @@ private:
    47     //! Pre-initialized hasher to avoid having to recreate it for every hash calculation.
    48     CSHA256 m_script_execution_cache_hasher;
    49 
    50-public:
    51+    //! Cache for script verification results to avoid re-validation.
    52     CuckooCache::cache<uint256, SignatureCacheHasher> m_script_execution_cache;
    53+
    54+public:
    55     SignatureCache m_signature_cache;
    56 
    57     ValidationCache(size_t script_execution_cache_bytes, size_t signature_cache_bytes);
    58@@ -384,6 +386,9 @@ public:
    59 
    60     //! Return a copy of the pre-initialized hasher.
    61     CSHA256 ScriptExecutionCacheHasher() const { return m_script_execution_cache_hasher; }
    62+
    63+    bool IsScriptValidated(const uint256& entry, bool erase) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
    64+    void CacheScriptValidation(const uint256& entry) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
    65 };
    66 
    67 /** Functions for validating blocks and updating the block tree */
    

    Given the sensitive nature of this area, I’d do it in a separate commit.

    [!NOTE] This change alone would have likely prevented the consensus bug introduced in https://github.com/bitcoinknots/bitcoin/pull/238#issuecomment-3896624715.

  38. in src/validation.cpp:2690 in 02e1c01ed4
    2699-                // Any transaction validation failure in ConnectBlock is a block consensus failure
    2700-                state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
    2701-                              tx_state.GetRejectReason(), tx_state.GetDebugMessage());
    2702-                break;
    2703+
    2704+            if (control) control->Add(std::move(vChecks));
    


    sedited commented at 9:46 pm on November 30, 2025:
    Can you add braces here? I find different inlining styles for the same if-else block slightly confusing to read.

    fjahr commented at 11:03 pm on February 27, 2026:
    This line is removed in the final commit. I have squashed that commit now as promised.
  39. sedited commented at 10:04 pm on November 30, 2025: contributor
    Looking at this patch, I am asking myself why we’re keeping the single threaded / no checkqueue case in the first place. This seems to introduce a vector of jobs for both cases now, so maybe we could just create a queue without any workers?
  40. fjahr force-pushed on Feb 1, 2026
  41. fjahr commented at 7:08 pm on February 1, 2026: contributor

    Finally getting around to picking this up again.

    I am asking myself why we’re keeping the single threaded / no checkqueue case in the first place. This seems to introduce a vector of jobs for both cases now, so maybe we could just create a queue without any workers?

    Checkqueue can run without worker threads as well but I still expected that some changes would be needed. That doesn’t seem to be the case, it seems to work well as is. I also expected that there would be some performance penalty due to the overhead within checkqueue. This also doesn’t seem to be the case, at least not to a degree that is measurable by our connectblock benchmark. So I pushed this as an additional refactoring commit, which of course get’s rid of a few lines of extra code. Let me know what you all think.

    EDIT: The last commit should probably be squashed into the one before it but I am leaving it as standalone for now until I have gotten some feedback on the new approach. Unless there are issues noted I will probably squash it with the next push.

  42. fjahr renamed this:
    refactor: Split multithreaded case out of CheckInputScripts
    refactor: Remove special treatment for single threaded script checks
    on Feb 1, 2026
  43. fjahr renamed this:
    refactor: Remove special treatment for single threaded script checks
    refactor: Remove special treatment for single threaded script checking
    on Feb 1, 2026
  44. sedited commented at 5:24 pm on February 13, 2026: contributor

    Approach ACK

    This seems like a nice cleanup, will look at it in detail later.

  45. sedited requested review from theuni on Feb 13, 2026
  46. refactor: Extract cache check from CheckInputScripts 043301a537
  47. refactor: Extract TxData initialization from CheckInputScripts 6b37953134
  48. refactor: Extract PreCheckInputScripts 4e851e1745
  49. validation, refactor: Remove single threaded special case
    Instead use checkqueue just like in the multiprocess case.
    7726a6a6c3
  50. fjahr force-pushed on Feb 27, 2026
  51. DrahtBot added the label CI failed on Feb 28, 2026
  52. DrahtBot removed the label CI failed on Mar 2, 2026
  53. sedited requested review from l0rinc on Mar 3, 2026
  54. in src/validation.cpp:2064 in 043301a537
    2059+ * transaction).
    2060+ */
    2061+bool IsScriptCached(ValidationCache& validation_cache, bool cacheFullScriptStore, uint256 hashCacheEntry)
    2062+                    EXCLUSIVE_LOCKS_REQUIRED(cs_main)
    2063+{
    2064+    AssertLockHeld(cs_main); //TODO: Remove this requirement by making CuckooCache not require external locks
    


    l0rinc commented at 10:49 am on March 5, 2026:

    043301a refactor: Extract cache check from CheckInputScripts:

    The comment says this is about the CuckooCache, which we are inserting into later, so we cannot just move the lock assertion to the contains method only.

  55. in src/validation.cpp:2071 in 6b37953134
    2067@@ -2068,6 +2068,22 @@ bool IsScriptCached(ValidationCache& validation_cache, bool cacheFullScriptStore
    2068     return false;
    2069 }
    2070 
    2071+void EnsureTxData(const CTransaction& tx, const CCoinsViewCache& inputs, PrecomputedTransactionData& txdata)
    


    l0rinc commented at 11:54 am on March 5, 2026:

    6b37953 refactor: Extract TxData initialization from CheckInputScripts:

    This new abstraction is doing multiple things (which makes it a bit hard to name):

    • asking txdata whether the payload will be accepted, to avoid calculating GetSpentOutputs needlessly
    • calculating it if it will be accepted
    • passing it to txdata.Init
    • asserting that everything makes sense regardless of previous actions

    Most of these make more sense at the call site. The only excessive calculation is the to-be-spent outputs calculation. We could extract only that (making sure no extra copies happen, of course), something like:

     0diff --git a/src/validation.cpp b/src/validation.cpp
     1--- a/src/validation.cpp	(revision d26ff1ff03da3531d1a6a28466784911ece28e0a)
     2+++ b/src/validation.cpp	(date 1772711510934)
     3@@ -2048,6 +2048,18 @@
     4     m_script_execution_cache.insert(entry);
     5 }
     6 
     7+static std::vector<CTxOut> GetSpentOutputs(const CTransaction& tx, const CCoinsViewCache& inputs)
     8+{
     9+    std::vector<CTxOut> spent_outputs;
    10+    spent_outputs.reserve(tx.vin.size());
    11+    for (const auto& txin : tx.vin) {
    12+        const Coin& coin = inputs.AccessCoin(txin.prevout);
    13+        assert(!coin.IsSpent());
    14+        spent_outputs.emplace_back(coin.out);
    15+    }
    16+    return spent_outputs;
    17+}
    18+
    19 /**
    20  * Check whether all of this transaction's input scripts succeed.
    21  *
    22@@ -2091,18 +2103,7 @@
    23         return true;
    24     }
    25 
    26-    if (!txdata.m_spent_outputs_ready) {
    27-        std::vector<CTxOut> spent_outputs;
    28-        spent_outputs.reserve(tx.vin.size());
    29-
    30-        for (const auto& txin : tx.vin) {
    31-            const COutPoint& prevout = txin.prevout;
    32-            const Coin& coin = inputs.AccessCoin(prevout);
    33-            assert(!coin.IsSpent());
    34-            spent_outputs.emplace_back(coin.out);
    35-        }
    36-        txdata.Init(tx, std::move(spent_outputs));
    37-    }
    38+    if (!txdata.m_spent_outputs_ready) txdata.Init(tx, GetSpentOutputs(tx, inputs));
    39     assert(txdata.m_spent_outputs.size() == tx.vin.size());
    40 
    41     for (unsigned int i = 0; i < tx.vin.size(); i++) {
    

    https://godbolt.org/z/jj3eGq8eY indicates that GetSpentOutputs allocates once via reserve, and Init’s && parameter steals the buffer pointer directly, so it’s a pure refactor.

  56. in src/validation.cpp:145 in 4e851e1745
    138@@ -139,6 +139,11 @@ const CBlockIndex* Chainstate::FindForkInGlobalIndex(const CBlockLocator& locato
    139     return m_chain.Genesis();
    140 }
    141 
    142+bool PreCheckInputScripts(const CTransaction& tx,
    143+                       const CCoinsViewCache& inputs, script_verify_flags flags,
    144+                       bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
    145+                       ValidationCache& validation_cache) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
    


    l0rinc commented at 11:58 am on March 5, 2026:

    4e851e1 refactor: Extract PreCheckInputScripts:

    The commit message does not explain the motivation behind this change, or the extra work that we are doing now.

    nit: formatting of the new code is off

  57. in src/validation.cpp:2176 in 4e851e1745
    2172@@ -2153,6 +2173,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
    2173     if (cacheFullScriptStore && !pvChecks) {
    2174         // We executed all of the provided scripts, and were told to
    2175         // cache the result. Do so now.
    2176+        uint256 hashCacheEntry = GetHashCacheEntry(validation_cache, tx, flags);
    


    l0rinc commented at 12:00 pm on March 5, 2026:

    4e851e1 refactor: Extract PreCheckInputScripts:

    The commit forgot to mention that we’re recalculating the hash now, instead of only doing it once. I don’t consider that a refactor, like the commit title suggests.

  58. in src/validation.cpp:2100 in 4e851e1745
    2095+ * this is not a coinbase transaction. Returning false indicates that checks are not possible
    2096+ * or necessary. Also ensures txdata is complete.
    2097+ *
    2098+ * Non-static (and redeclared) in src/test/txvalidationcache_tests.cpp
    2099+ */
    2100+bool PreCheckInputScripts(const CTransaction& tx,
    


    l0rinc commented at 12:17 pm on March 5, 2026:

    4e851e1 refactor: Extract PreCheckInputScripts:

    The method name describes when this is executed, not what it does. It returns a bool, which forces us to recompute the cache key. What if we adjust it to return the cache key instead, something like:

     0diff --git a/src/validation.cpp b/src/validation.cpp
     1--- a/src/validation.cpp	(revision 4b2f347fc0495a9e4ddb9e47ec066f3b82a20d30)
     2+++ b/src/validation.cpp	(date 1772727309644)
     3@@ -2060,6 +2060,36 @@
     4     return spent_outputs;
     5 }
     6 
     7+/** Perform pre-checks for CheckInputScripts: skip coinbase, check the script
     8+ *  execution cache, reserve space for script checks, and initialize txdata.
     9+ *  Returns nullopt if the transaction is already validated (caller should
    10+ *  return true), or the cache entry hash needed to store results later. */
    11+static std::optional<uint256> LookupScriptValidation(
    12+    const CTransaction& tx,
    13+    const CCoinsViewCache& inputs, script_verify_flags flags,
    14+    bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
    15+    const ValidationCache& validation_cache) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
    16+{
    17+    if (tx.IsCoinBase()) return std::nullopt;
    18+
    19+    // First check if script executions have been cached with the same
    20+    // flags. Note that this assumes that the inputs provided are
    21+    // correct (ie that the transaction hash which is in tx's prevouts
    22+    // properly commits to the scriptPubKey in the inputs view of that
    23+    // transaction).
    24+    uint256 hashCacheEntry;
    25+    CSHA256 hasher = validation_cache.ScriptExecutionCacheHasher();
    26+    hasher.Write(UCharCast(tx.GetWitnessHash().begin()), 32).Write((unsigned char*)&flags, sizeof(flags)).Finalize(hashCacheEntry.begin());
    27+    if (validation_cache.IsScriptValidated(hashCacheEntry, !cacheFullScriptStore)) {
    28+        return std::nullopt;
    29+    }
    30+
    31+    if (!txdata.m_spent_outputs_ready) txdata.Init(tx, GetSpentOutputs(tx, inputs));
    32+    assert(txdata.m_spent_outputs.size() == tx.vin.size());
    33+
    34+    return hashCacheEntry;
    35+}
    36+
    37 /**
    38  * Check whether all of this transaction's input scripts succeed.
    39  *
    40@@ -2085,22 +2115,8 @@
    41                        ValidationCache& validation_cache,
    42                        std::vector<CScriptCheck>* pvChecks)
    43 {
    44-    if (tx.IsCoinBase()) return true;
    45-
    46-    // First check if script executions have been cached with the same
    47-    // flags. Note that this assumes that the inputs provided are
    48-    // correct (ie that the transaction hash which is in tx's prevouts
    49-    // properly commits to the scriptPubKey in the inputs view of that
    50-    // transaction).
    51-    uint256 hashCacheEntry;
    52-    CSHA256 hasher = validation_cache.ScriptExecutionCacheHasher();
    53-    hasher.Write(UCharCast(tx.GetWitnessHash().begin()), 32).Write((unsigned char*)&flags, sizeof(flags)).Finalize(hashCacheEntry.begin());
    54-    if (validation_cache.IsScriptValidated(hashCacheEntry, !cacheFullScriptStore)) {
    55-        return true;
    56-    }
    57-
    58-    if (!txdata.m_spent_outputs_ready) txdata.Init(tx, GetSpentOutputs(tx, inputs));
    59-    assert(txdata.m_spent_outputs.size() == tx.vin.size());
    60+    const auto hash_cache_entry{LookupScriptValidation(tx, inputs, flags, cacheFullScriptStore, txdata, validation_cache)};
    61+    if (!hash_cache_entry) return true;
    62 
    63     for (unsigned int i = 0; i < tx.vin.size(); i++) {
    64 
    65@@ -2133,7 +2149,7 @@
    66     if (cacheFullScriptStore && !pvChecks) {
    67         // We executed all of the provided scripts, and were told to
    68         // cache the result. Do so now.
    69-        validation_cache.CacheScriptValidation(hashCacheEntry);
    70+        validation_cache.CacheScriptValidation(*hash_cache_entry);
    71     }
    72 
    73     return true;
    
  59. in src/validation.cpp:2142 in 4e851e1745
    2138@@ -2110,19 +2139,10 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
    2139                        ValidationCache& validation_cache,
    2140                        std::vector<CScriptCheck>* pvChecks)
    2141 {
    2142-    if (tx.IsCoinBase()) return true;
    2143-
    2144-    if (pvChecks) {
    2145-        pvChecks->reserve(tx.vin.size());
    2146+    if (!PreCheckInputScripts(tx, inputs, flags, cacheFullScriptStore, txdata, validation_cache)) {
    


    l0rinc commented at 1:42 pm on March 5, 2026:

    4e851e1 refactor: Extract PreCheckInputScripts:

    We could avoid recomputing the the hash:

    0    const auto hash_cache_entry{LookupScriptValidation(tx, inputs, flags, cacheFullScriptStore, txdata, validation_cache, pvChecks)};
    1    if (!hash_cache_entry) return true;
    
  60. in src/test/txvalidationcache_tests.cpp:31 in 4e851e1745
    26                        const CCoinsViewCache& inputs, script_verify_flags flags, bool cacheSigStore,
    27                        bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
    28                        ValidationCache& validation_cache,
    29                        std::vector<CScriptCheck>* pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
    30 
    31+bool PreCheckInputScripts(const CTransaction& tx,
    


    l0rinc commented at 1:45 pm on March 5, 2026:

    4e851e1 refactor: Extract PreCheckInputScripts:

    Could we do the test updates in a separate change? It doesn’t seem strictly related to the refactor in src/validation.cpp (which is risky enough).

  61. in src/validation.cpp:2631 in 7726a6a6c3
    2621+            ValidationCache& validation_cache{m_chainman.m_validation_cache};
    2622+            if (PreCheckInputScripts(tx, view, flags, fCacheResults, txsdata[i], validation_cache)) {
    2623+                // Turn all of this transaction's input scripts into script checks and add them to the queue
    2624                 std::vector<CScriptCheck> vChecks;
    2625-                tx_ok = CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], m_chainman.m_validation_cache, &vChecks);
    2626-                if (tx_ok) control->Add(std::move(vChecks));
    


    l0rinc commented at 3:08 pm on March 5, 2026:

    7726a6a validation, refactor: Remove single threaded special case:

    This change is too big and contains too many subtle assumptions. For example, this seems to be dead code: it will always return true, since the only invalid path is when pvChecks is not provided. We should clarify that in smaller steps and iterate toward the unification in smaller, trivial steps, e.g. something like:

     0diff --git a/src/validation.cpp b/src/validation.cpp
     1--- a/src/validation.cpp	(revision c009adccee7a268ec844e29bf7070b543a26ae90)
     2+++ b/src/validation.cpp	(date 1772723088401)
     3@@ -2600,18 +2600,14 @@
     4         if (!tx.IsCoinBase() && fScriptChecks)
     5         {
     6             bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
     7-            bool tx_ok;
     8             TxValidationState tx_state;
     9-            // If CheckInputScripts is called with a pointer to a checks vector, the resulting checks are appended to it. In that case
    10-            // they need to be added to control which runs them asynchronously. Otherwise, CheckInputScripts runs the checks before returning.
    11             if (control) {
    12+                // CheckInputScripts always returns true when pvChecks is provided
    13+                // (checks are collected for deferred execution, not executed inline).
    14                 std::vector<CScriptCheck> vChecks;
    15-                tx_ok = CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], m_chainman.m_validation_cache, &vChecks);
    16-                if (tx_ok) control->Add(std::move(vChecks));
    17-            } else {
    18-                tx_ok = CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], m_chainman.m_validation_cache);
    19-            }
    20-            if (!tx_ok) {
    21+                Assume(CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], m_chainman.m_validation_cache, &vChecks));
    22+                control->Add(std::move(vChecks));
    23+            } else if (!CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], m_chainman.m_validation_cache)) {
    24                 // Any transaction validation failure in ConnectBlock is a block consensus failure
    25                 state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
    26                               tx_state.GetRejectReason(), tx_state.GetDebugMessage());
    
  62. in src/validation.cpp:2117 in 4e851e1745
    2143-
    2144-    if (pvChecks) {
    2145-        pvChecks->reserve(tx.vin.size());
    2146+    if (!PreCheckInputScripts(tx, inputs, flags, cacheFullScriptStore, txdata, validation_cache)) {
    2147+         return true;
    2148     }
    


    l0rinc commented at 4:12 pm on March 5, 2026:

    4e851e1 refactor: Extract PreCheckInputScripts:

    What’s the explanation for this removal? There are too many changes in this commit. If you want to move it closer to usage, we could do:

     0diff --git a/src/validation.cpp b/src/validation.cpp
     1--- a/src/validation.cpp	(revision b3b6dc9bb7165bbfd21cb1af636cdfc467de7f01)
     2+++ b/src/validation.cpp	(revision 4b2f347fc0495a9e4ddb9e47ec066f3b82a20d30)
     3@@ -2087,10 +2087,6 @@
     4 {
     5     if (tx.IsCoinBase()) return true;
     6 
     7-    if (pvChecks) {
     8-        pvChecks->reserve(tx.vin.size());
     9-    }
    10-
    11     // First check if script executions have been cached with the same
    12     // flags. Note that this assumes that the inputs provided are
    13     // correct (ie that the transaction hash which is in tx's prevouts
    14@@ -2117,6 +2113,7 @@
    15         // Verify signature
    16         CScriptCheck check(txdata.m_spent_outputs[i], tx, validation_cache.m_signature_cache, i, flags, cacheSigStore, &txdata);
    17         if (pvChecks) {
    18+            pvChecks->reserve(tx.vin.size());
    19             pvChecks->emplace_back(std::move(check));
    20         } else if (auto result = check(); result.has_value()) {
    21             // Tx failures never trigger disconnections/bans.
    

    in a separate commit

  63. in src/validation.cpp:2165 in 7726a6a6c3
    2161@@ -2170,7 +2162,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
    2162         }
    2163     }
    2164 
    2165-    if (cacheFullScriptStore && !pvChecks) {
    2166+    if (cacheFullScriptStore) {
    


    l0rinc commented at 4:52 pm on March 5, 2026:

    7726a6a validation, refactor: Remove single threaded special case:

    We’re mixing refactoring (separation of the two intertwined use cases) with a behavior change in a consensus area. I have a really hard time trusting this completely.

    It would help if we could separate “validation” from “refactor” here. We could split the collection use case from validation by duplicating CheckInputScripts into a CollectScriptChecks, which creates the vector, fills it, and returns the values, while making input script validation ignore the vector. The two algorithms would already be very different (probably already achieving the main goal of this change without introducing any potentially dangerous unification, which we can still do in a follow-up if needed). This is what that would look like:

      0diff --git a/src/validation.cpp b/src/validation.cpp
      1--- a/src/validation.cpp	(revision 87fcd645598d186bd1e4f68d5aca275dc7815fda)
      2+++ b/src/validation.cpp	(date 1772729378036)
      3@@ -137,11 +137,17 @@
      4     return m_chain.Genesis();
      5 }
      6 
      7-bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
      8-                       const CCoinsViewCache& inputs, script_verify_flags flags, bool cacheSigStore,
      9-                       bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
     10-                       ValidationCache& validation_cache,
     11-                       std::vector<CScriptCheck>* pvChecks = nullptr)
     12+void CollectScriptChecks(const CTransaction& tx,
     13+                         const CCoinsViewCache& inputs, script_verify_flags flags, bool cacheSigStore,
     14+                         bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
     15+                         ValidationCache& validation_cache,
     16+                         std::vector<CScriptCheck>& pvChecks)
     17+                         EXCLUSIVE_LOCKS_REQUIRED(cs_main);
     18+
     19+bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
     20+                       const CCoinsViewCache& inputs, script_verify_flags flags, bool cacheSigStore,
     21+                       bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
     22+                       ValidationCache& validation_cache)
     23                        EXCLUSIVE_LOCKS_REQUIRED(cs_main);
     24 
     25 bool CheckFinalTxAtTip(const CBlockIndex& active_chain_tip, const CTransaction& tx)
     26@@ -2060,8 +2066,8 @@
     27     return spent_outputs;
     28 }
     29 
     30-/** Perform pre-checks for CheckInputScripts: skip coinbase, check the script
     31- *  execution cache, reserve space for script checks, and initialize txdata.
     32+/** Perform pre-checks for script validation: skip coinbase, check the script
     33+ *  execution cache, and initialize txdata.
     34  *  Returns nullopt if the transaction is already validated (caller should
     35  *  return true), or the cache entry hash needed to store results later. */
     36 static std::optional<uint256> LookupScriptValidation(
     37@@ -2090,16 +2096,28 @@
     38     return hashCacheEntry;
     39 }
     40 
     41+/** Collect script checks for all inputs without executing them.
     42+ *  Non-static (and redeclared) in src/test/txvalidationcache_tests.cpp */
     43+void CollectScriptChecks(const CTransaction& tx,
     44+                         const CCoinsViewCache& inputs, script_verify_flags flags, bool cacheSigStore,
     45+                         bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
     46+                         ValidationCache& validation_cache,
     47+                         std::vector<CScriptCheck>& checks) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
     48+{
     49+    if (LookupScriptValidation(tx, inputs, flags, cacheFullScriptStore, txdata, validation_cache)) {
     50+        checks.reserve(tx.vin.size());
     51+        for (unsigned int i = 0; i < tx.vin.size(); i++) {
     52+            checks.emplace_back(txdata.m_spent_outputs[i], tx, validation_cache.m_signature_cache, i, flags, cacheSigStore, &txdata);
     53+        }
     54+    }
     55+}
     56+
     57 /**
     58  * Check whether all of this transaction's input scripts succeed.
     59  *
     60  * This involves ECDSA signature checks so can be computationally intensive. This function should
     61  * only be called after the cheap sanity checks in CheckTxInputs passed.
     62  *
     63- * If pvChecks is not nullptr, script checks are pushed onto it instead of being performed inline. Any
     64- * script checks which are not necessary (eg due to script execution cache hits) are, obviously,
     65- * not pushed onto pvChecks/run.
     66- *
     67  * Setting cacheSigStore/cacheFullScriptStore to false will remove elements from the corresponding cache
     68  * which are matched. This is useful for checking blocks where we will likely never need the cache
     69  * entry again.
     70@@ -2112,8 +2130,7 @@
     71 bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
     72                        const CCoinsViewCache& inputs, script_verify_flags flags, bool cacheSigStore,
     73                        bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
     74-                       ValidationCache& validation_cache,
     75-                       std::vector<CScriptCheck>* pvChecks)
     76+                       ValidationCache& validation_cache)
     77 {
     78     const auto hash_cache_entry{LookupScriptValidation(tx, inputs, flags, cacheFullScriptStore, txdata, validation_cache)};
     79     if (!hash_cache_entry) return true;
     80@@ -2128,10 +2145,7 @@
     81 
     82         // Verify signature
     83         CScriptCheck check(txdata.m_spent_outputs[i], tx, validation_cache.m_signature_cache, i, flags, cacheSigStore, &txdata);
     84-        if (pvChecks) {
     85-            pvChecks->reserve(tx.vin.size());
     86-            pvChecks->emplace_back(std::move(check));
     87-        } else if (auto result = check(); result.has_value()) {
     88+        if (auto result = check()) {
     89             // Tx failures never trigger disconnections/bans.
     90             // This is so that network splits aren't triggered
     91             // either due to non-consensus relay policies (such as
     92@@ -2146,7 +2160,7 @@
     93         }
     94     }
     95 
     96-    if (cacheFullScriptStore && !pvChecks) {
     97+    if (cacheFullScriptStore) {
     98         // We executed all of the provided scripts, and were told to
     99         // cache the result. Do so now.
    100         validation_cache.CacheScriptValidation(*hash_cache_entry);
    101@@ -2596,18 +2610,20 @@
    102         if (!tx.IsCoinBase() && fScriptChecks)
    103         {
    104             bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
    105-            TxValidationState tx_state;
    106             if (control) {
    107                 // CheckInputScripts always returns true when pvChecks is provided
    108                 // (checks are collected for deferred execution, not executed inline).
    109                 std::vector<CScriptCheck> vChecks;
    110-                Assume(CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], m_chainman.m_validation_cache, &vChecks));
    111+                CollectScriptChecks(tx, view, flags, fCacheResults, fCacheResults, txsdata[i], m_chainman.m_validation_cache, vChecks);
    112                 control->Add(std::move(vChecks));
    113-            } else if (!CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], m_chainman.m_validation_cache)) {
    114-                // Any transaction validation failure in ConnectBlock is a block consensus failure
    115-                state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
    116-                              tx_state.GetRejectReason(), tx_state.GetDebugMessage());
    117-                break;
    118+            } else {
    119+                TxValidationState tx_state;
    120+                if (!CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], m_chainman.m_validation_cache)) {
    121+                    // Any transaction validation failure in ConnectBlock is a block consensus failure
    122+                    state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
    123+                                  tx_state.GetRejectReason(), tx_state.GetDebugMessage());
    124+                    break;
    125+                }
    126             }
    127         }
    
  64. l0rinc changes_requested
  65. l0rinc commented at 5:06 pm on March 5, 2026: contributor

    Concept ACK. The recent consensus bug in the BIP110 soft fork proposal in this exact area indicates the current code is hard to understand.
    Approach NACK. The recent consensus bug in the BIP110 soft fork proposal in this exact area indicates we need to be extra careful, which isn’t the case in this PR yet.

    I left a ton of suggestions as I recreated the change locally, experimenting with a few different implementations. We should definitely split the two intertwined functionalities, but not in the same PR that also modifies the behavior. There are too many unexplained changes here that I strongly object to.

    Let’s untangle the two functionalities first. That refactor is verbose but quite safe. If it gains momentum and gets merged, we can build on it to unify the two even more, if needed.

    Edit: I measured multithreaded script verification, and I can confirm that it doesn’t introduce a slowdown

     0for DBCACHE in 5000; do \
     1    COMMITS="043301a537fdad469ef83ee3d1033404a0cefe16 7726a6a6c3e378e538ecb4b5bfd82c2880cc0688"; \
     2    STOP=936639; CC=gcc; CXX=g++; \
     3    BASE_DIR="/mnt/my_storage"; DATA_DIR="$BASE_DIR/BitcoinData"; LOG_DIR="$BASE_DIR/logs"; \
     4    (echo ""; for c in $COMMITS; do git fetch -q origin $c && git log -1 --pretty='%h %s' $c || exit 1; done) && \
     5    (echo "" && echo "$(date -I) | reindex-chainstate | ${STOP} blocks | dbcache ${DBCACHE} | $(hostname) | $(uname -m) | $(lscpu | grep 'Model name' | head -1 | cut -d: -f2 | xargs) | $(nproc) cores | $(free -h | awk '/^Mem:/{print $2}') RAM | $(lsblk -no ROTA $(df --output=source $BASE_DIR | tail -1) | grep -q 1 && echo HDD || echo SSD)"; echo "") && \
     6    hyperfine \
     7    --sort command \
     8    --runs 1 \
     9    --export-json "$BASE_DIR/rdx-$(sed -E 's/(\w{8})\w+ ?/\1-/g;s/-$//'<<<"$COMMITS")-$STOP-$DBCACHE-$CC.json" \
    10    --parameter-list COMMIT ${COMMITS// /,} \
    11    --prepare "killall -9 bitcoind 2>/dev/null; rm -f ./build/bin/bitcoind; git clean -fxd; git reset --hard {COMMIT} && \
    12      cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DENABLE_IPC=OFF && ninja -C build bitcoind -j1 && \
    13      ./build/bin/bitcoind -datadir=$DATA_DIR -stopatheight=$STOP -dbcache=1000 -printtoconsole=0; sleep 20; rm -f $DATA_DIR/debug.log" \
    14    --conclude "killall bitcoind || true; sleep 5; grep -q 'height=0' $DATA_DIR/debug.log && grep -q 'Disabling script verification at block [#1](/bitcoin-bitcoin/1/)' $DATA_DIR/debug.log && grep -q 'height=$STOP' $DATA_DIR/debug.log && grep 'Bitcoin Core version' $DATA_DIR/debug.log | grep -q "$(printf %.12s {COMMIT})"; \
    15                cp $DATA_DIR/debug.log $LOG_DIR/debug-{COMMIT}-$(date +%s).log" \
    16    "COMPILER=$CC ./build/bin/bitcoind -datadir=$DATA_DIR -stopatheight=$STOP -dbcache=$DBCACHE -reindex-chainstate -blocksonly -connect=0 -printtoconsole=0 -assumevalid=0";
    17done
    18
    19043301a537 refactor: Extract cache check from CheckInputScripts
    207726a6a6c3 validation, refactor: Remove single threaded special case
    21
    222026-03-05 | reindex-chainstate | 936639 blocks | dbcache 5000 | i9-ssd | x86_64 | Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz | 16 cores | 62Gi RAM | SSD
    23
    24Benchmark 1: COMPILER=gcc ./build/bin/bitcoind -datadir=/mnt/my_storage/BitcoinData -stopatheight=936639 -dbcache=5000 -reindex-chainstate -blocksonly -connect=0 -printtoconsole=0 -assumevalid=0 (COMMIT = 043301a537fdad469ef83ee3d1033404a0cefe16)
    25  Time (abs ):        37023.077 s               [User: 389375.279 s, System: 1125.389 s]
    26 
    27Benchmark 2: COMPILER=gcc ./build/bin/bitcoind -datadir=/mnt/my_storage/BitcoinData -stopatheight=936639 -dbcache=5000 -reindex-chainstate -blocksonly -connect=0 -printtoconsole=0 -assumevalid=0 (COMMIT = 7726a6a6c3e378e538ecb4b5bfd82c2880cc0688)
    28  Time (abs ):        37195.482 s               [User: 388819.276 s, System: 1134.977 s]
    29 
    30Relative speed comparison
    31        1.00          COMPILER=gcc ./build/bin/bitcoind -datadir=/mnt/my_storage/BitcoinData -stopatheight=936639 -dbcache=5000 -reindex-chainstate -blocksonly -connect=0 -printtoconsole=0 -assumevalid=0 (COMMIT = 043301a537fdad469ef83ee3d1033404a0cefe16)
    32        1.00          COMPILER=gcc ./build/bin/bitcoind -datadir=/mnt/my_storage/BitcoinData -stopatheight=936639 -dbcache=5000 -reindex-chainstate -blocksonly -connect=0 -printtoconsole=0 -assumevalid=0 (COMMIT = 7726a6a6c3e378e538ecb4b5bfd82c2880cc0688)
    

    I will measure single-threaded version next.


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-03-09 21:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me