From d5ccebda102ef2bdf0901e4fe2641b2ce0ed4558 Mon Sep 17 00:00:00 2001
From: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com>
Date: Wed, 29 Mar 2023 07:32:24 -0500
Subject: [PATCH 01/35] log: add a log for invalid mnauth (#5271)

## Issue being fixed or feature implemented
We are seeing lots of "invalid mnauth" on testnet.. We should be logging
this anyhow

## What was done?
Add some logging the the mnauth sig isn't valid

## How Has This Been Tested?
make check

## Breaking Changes
None

## Checklist:
- [x] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have added or updated relevant unit/integration/functional/e2e
tests
- [ ] I have made corresponding changes to the documentation

**For repository code-owners and collaborators only**
- [x] I have assigned this pull request to a milestone
---
 src/evo/mnauth.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/evo/mnauth.cpp b/src/evo/mnauth.cpp
index d7527a55cb8d..d0122bf9fd43 100644
--- a/src/evo/mnauth.cpp
+++ b/src/evo/mnauth.cpp
@@ -87,6 +87,7 @@ void CMNAuth::ProcessMessage(CNode& peer, CConnman& connman, std::string_view ms
 
     if (!mnauth.sig.IsValid()) {
         Misbehaving(peer.GetId(), 100, "invalid mnauth signature");
+        LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- invalid mnauth for protx=%s with sig=%s\n", mnauth.proRegTxHash.ToString(), mnauth.sig.ToString());
         return;
     }
 

From 7f520f5c954fa91764592b36b1e3a472cd7dd8a5 Mon Sep 17 00:00:00 2001
From: Odysseas Gabrielides <odysseas.gabrielides@gmail.com>
Date: Wed, 29 Mar 2023 15:34:19 +0300
Subject: [PATCH 02/35] log: Add logs when send qgetdata (#5275)

## Issue being fixed or feature implemented


## What was done?
Added logs with requested parameters (`llmqType`, `quorumHash`,
`proRegTx`) when sending `qgetdata` for better troubleshooting.

## How Has This Been Tested?


## Breaking Changes


## Checklist:
- [x] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have added or updated relevant unit/integration/functional/e2e
tests
- [ ] I have made corresponding changes to the documentation

**For repository code-owners and collaborators only**
- [x] I have assigned this pull request to a milestone
---
 src/llmq/quorums.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/llmq/quorums.cpp b/src/llmq/quorums.cpp
index dedf7a4263dd..cfcf66366822 100644
--- a/src/llmq/quorums.cpp
+++ b/src/llmq/quorums.cpp
@@ -478,6 +478,8 @@ bool CQuorumManager::RequestQuorumData(CNode* pfrom, Consensus::LLMQType llmqTyp
         LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- Already requested\n", __func__);
         return false;
     }
+    LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- sending QGETDATA quorumHash[%s] llmqType[%d] proRegTx[%s]\n", __func__, key.quorumHash.ToString(),
+             ToUnderlying(key.llmqType), key.proRegTx.ToString());
 
     CNetMsgMaker msgMaker(pfrom->GetSendVersion());
     connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::QGETDATA, it.first->second));

From 5456be67800007cd2654ab3961cf506905317029 Mon Sep 17 00:00:00 2001
From: Odysseas Gabrielides <odysseas.gabrielides@gmail.com>
Date: Wed, 29 Mar 2023 19:23:45 +0300
Subject: [PATCH 03/35] feat(rpc): Added RPC cleardiscouraged (#5273)

## Issue being fixed or feature implemented


## What was done?
Added RPC `cleardiscouraged` which clears internally the list of
discouraged peers.

Note: Implementation of a `listdiscouraged` RPC is not possible because
the internal data structure used for discouraged peers is a Bloom
filter.

## How Has This Been Tested?

## Breaking Changes


## Checklist:
- [x] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have added or updated relevant unit/integration/functional/e2e
tests
- [x] I have made corresponding changes to the documentation

**For repository code-owners and collaborators only**
- [x] I have assigned this pull request to a milestone
---
 doc/release-notes-5273.md |  5 +++++
 src/banman.cpp            | 10 ++++++++++
 src/banman.h              |  1 +
 src/rpc/net.cpp           | 22 ++++++++++++++++++++++
 4 files changed, 38 insertions(+)
 create mode 100644 doc/release-notes-5273.md

diff --git a/doc/release-notes-5273.md b/doc/release-notes-5273.md
new file mode 100644
index 000000000000..16bd0c88dcef
--- /dev/null
+++ b/doc/release-notes-5273.md
@@ -0,0 +1,5 @@
+Added RPCs
+--------
+
+- `cleardiscouraged` clears all the already discouraged peers.
+
diff --git a/src/banman.cpp b/src/banman.cpp
index 48973e9c4a97..807ad19faa0a 100644
--- a/src/banman.cpp
+++ b/src/banman.cpp
@@ -68,6 +68,16 @@ void BanMan::ClearBanned()
     if (m_client_interface) m_client_interface->BannedListChanged();
 }
 
+void BanMan::ClearDiscouraged()
+{
+    {
+        LOCK(m_cs_banned);
+        m_discouraged.reset();
+        m_is_dirty = true;
+    }
+    if (m_client_interface) m_client_interface->BannedListChanged();
+}
+
 bool BanMan::IsDiscouraged(const CNetAddr& net_addr)
 {
     LOCK(m_cs_banned);
diff --git a/src/banman.h b/src/banman.h
index 6b972c84e28e..ca46677e3ee4 100644
--- a/src/banman.h
+++ b/src/banman.h
@@ -63,6 +63,7 @@ class BanMan
     void Ban(const CSubNet& sub_net, int64_t ban_time_offset = 0, bool since_unix_epoch = false);
     void Discourage(const CNetAddr& net_addr);
     void ClearBanned();
+    void ClearDiscouraged();
 
     //! Return whether net_addr is banned
     bool IsBanned(const CNetAddr& net_addr);
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index eca3d2179c6f..8db4721c1922 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -736,6 +736,27 @@ static UniValue clearbanned(const JSONRPCRequest& request)
     return NullUniValue;
 }
 
+static UniValue cleardiscouraged(const JSONRPCRequest& request)
+{
+    RPCHelpMan{"cleardiscouraged",
+               "\nClear all discouraged nodes.\n",
+               {},
+               RPCResult{RPCResult::Type::NONE, "", ""},
+               RPCExamples{
+                       HelpExampleCli("cleardiscouraged", "")
+                       + HelpExampleRpc("cleardiscouraged", "")
+               },
+    }.Check(request);
+    NodeContext& node = EnsureNodeContext(request.context);
+    if (!node.banman) {
+        throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
+    }
+
+    node.banman->ClearDiscouraged();
+
+    return NullUniValue;
+}
+
 static UniValue setnetworkactive(const JSONRPCRequest& request)
 {
     RPCHelpMan{"setnetworkactive",
@@ -825,6 +846,7 @@ static const CRPCCommand commands[] =
     { "network",            "setban",                 &setban,                 {"subnet", "command", "bantime", "absolute"} },
     { "network",            "listbanned",             &listbanned,             {} },
     { "network",            "clearbanned",            &clearbanned,            {} },
+    { "network",            "cleardiscouraged",       &cleardiscouraged,        {} },
     { "network",            "setnetworkactive",       &setnetworkactive,       {"state"} },
     { "network",            "getnodeaddresses",       &getnodeaddresses,       {"count"} },
 };

From 376f78d8aac121f5fcae976bbd7fefbe75d5dbe8 Mon Sep 17 00:00:00 2001
From: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com>
Date: Wed, 29 Mar 2023 11:24:16 -0500
Subject: [PATCH 04/35] format: fix indentation (#5277)

## Issue being fixed or feature implemented
Indentation was wrong

## What was done?


## How Has This Been Tested?


## Breaking Changes


## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have added or updated relevant unit/integration/functional/e2e
tests
- [ ] I have made corresponding changes to the documentation

**For repository code-owners and collaborators only**
- [x] I have assigned this pull request to a milestone
---
 src/net.cpp | 42 +++++++++++++++++++++---------------------
 1 file changed, 21 insertions(+), 21 deletions(-)

diff --git a/src/net.cpp b/src/net.cpp
index 1dd7e4495f80..51bf6c3c0f20 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -2534,31 +2534,31 @@ void CConnman::ThreadOpenMasternodeConnections()
                     }
                     const auto& addr2 = dmn->pdmnState->addr;
                     if (connectedNodes.count(addr2) && !connectedProRegTxHashes.count(proRegTxHash)) {
-                            // we probably connected to it before it became a masternode
-                            // or maybe we are still waiting for mnauth
-                            (void)ForNode(addr2, [&](CNode* pnode) {
-                                if (pnode->nTimeFirstMessageReceived != 0 && GetSystemTimeInSeconds() - pnode->nTimeFirstMessageReceived > 5) {
-                                    // clearly not expecting mnauth to take that long even if it wasn't the first message
-                                    // we received (as it should normally), disconnect
-                                    LogPrint(BCLog::NET_NETCONN, "CConnman::%s -- dropping non-mnauth connection to %s, service=%s\n", _func_, proRegTxHash.ToString(), addr2.ToString(false));
-                                    pnode->fDisconnect = true;
-                                    return true;
-                                }
-                                return false;
-                            });
-                            // either way - it's not ready, skip it for now
-                            continue;
-                        }
-                        if (!connectedNodes.count(addr2) && !IsMasternodeOrDisconnectRequested(addr2) && !connectedProRegTxHashes.count(proRegTxHash)) {
-                            int64_t lastAttempt = mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastOutboundAttempt();
-                            // back off trying connecting to an address if we already tried recently
-                            if (nANow - lastAttempt < chainParams.LLMQConnectionRetryTimeout()) {
-                                continue;
+                        // we probably connected to it before it became a masternode
+                        // or maybe we are still waiting for mnauth
+                        (void)ForNode(addr2, [&](CNode* pnode) {
+                            if (pnode->nTimeFirstMessageReceived != 0 && GetSystemTimeInSeconds() - pnode->nTimeFirstMessageReceived > 5) {
+                                // clearly not expecting mnauth to take that long even if it wasn't the first message
+                                // we received (as it should normally), disconnect
+                                LogPrint(BCLog::NET_NETCONN, "CConnman::%s -- dropping non-mnauth connection to %s, service=%s\n", _func_, proRegTxHash.ToString(), addr2.ToString(false));
+                                pnode->fDisconnect = true;
+                                return true;
                             }
-                            ret.emplace_back(dmn);
+                            return false;
+                        });
+                        // either way - it's not ready, skip it for now
+                        continue;
+                    }
+                    if (!connectedNodes.count(addr2) && !IsMasternodeOrDisconnectRequested(addr2) && !connectedProRegTxHashes.count(proRegTxHash)) {
+                        int64_t lastAttempt = mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastOutboundAttempt();
+                        // back off trying connecting to an address if we already tried recently
+                        if (nANow - lastAttempt < chainParams.LLMQConnectionRetryTimeout()) {
+                            continue;
                         }
+                        ret.emplace_back(dmn);
                     }
                 }
+            }
             return ret;
         };
 

From 238e585f9d7fe71d0f5b576758d8359806cbd7fa Mon Sep 17 00:00:00 2001
From: Konstantin Akimov <knstqq@gmail.com>
Date: Thu, 2 Feb 2023 16:25:52 +0700
Subject: [PATCH 05/35] Merge #15718: docs: Improve netaddress comments

303372c41a8d5c58a46cf9ed595e30e67bd0bc99 docs: Improve netaddress comments (Carl Dong)

Pull request description:

  Improves comments for `netaddress`, making them available to Doxygen.

  I think this is worthwhile because a lot of the code require some context (e.g., A lot of the things that we do to fit hostnames and tor addresses into `CNetAddr` is non-obvious, and documenting it is beneficial).

ACKs for commit 303372:

Tree-SHA512: 2a35784a01ed8ec5fdbe111a540192d31bde16afa96e4be97b0385daf290fc7469a66d7cb8905a70b920fad6a0e7400ca4e5da082d6e4af1d1aaccc0e8297720
---
 src/netaddress.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++--
 src/netaddress.h   | 12 ++++++---
 2 files changed, 68 insertions(+), 6 deletions(-)

diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index bb4ba3095c8f..ce3a3745d6be 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -420,6 +420,16 @@ bool CNetAddr::IsLocal() const
     return false;
 }
 
+/**
+ * @returns Whether or not this network address is a valid address that @a could
+ *          be used to refer to an actual host.
+ *
+ * @note A valid address may or may not be publicly routable on the global
+ *       internet. As in, the set of valid addresses is a superset of the set of
+ *       publicly routable addresses.
+ *
+ * @see CNetAddr::IsRoutable()
+ */
 bool CNetAddr::IsValid() const
 {
     // unspecified IPv6 address (::/128)
@@ -450,6 +460,15 @@ bool CNetAddr::IsValid() const
     return true;
 }
 
+/**
+ * @returns Whether or not this network address is publicly routable on the
+ *          global internet.
+ *
+ * @note A routable address is always valid. As in, the set of routable addresses
+ *       is a subset of the set of valid addresses.
+ *
+ * @see CNetAddr::IsValid()
+ */
 bool CNetAddr::IsRoutable() const
 {
     if (!IsValid())
@@ -588,6 +607,16 @@ bool operator<(const CNetAddr& a, const CNetAddr& b)
     return std::tie(a.m_net, a.m_addr) < std::tie(b.m_net, b.m_addr);
 }
 
+/**
+ * Try to get our IPv4 address.
+ *
+ * @param[out] pipv4Addr The in_addr struct to which to copy.
+ *
+ * @returns Whether or not the operation was successful, in particular, whether
+ *          or not our address was an IPv4 address.
+ *
+ * @see CNetAddr::IsIPv4()
+ */
 bool CNetAddr::GetInAddr(struct in_addr* pipv4Addr) const
 {
     if (!IsIPv4())
@@ -597,6 +626,16 @@ bool CNetAddr::GetInAddr(struct in_addr* pipv4Addr) const
     return true;
 }
 
+/**
+ * Try to get our IPv6 (or CJDNS) address.
+ *
+ * @param[out] pipv6Addr The in6_addr struct to which to copy.
+ *
+ * @returns Whether or not the operation was successful, in particular, whether
+ *          or not our address was an IPv6 address.
+ *
+ * @see CNetAddr::IsIPv6()
+ */
 bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const
 {
     if (!IsIPv6()) {
@@ -887,6 +926,18 @@ bool operator<(const CService& a, const CService& b)
     return static_cast<CNetAddr>(a) < static_cast<CNetAddr>(b) || (static_cast<CNetAddr>(a) == static_cast<CNetAddr>(b) && a.port < b.port);
 }
 
+/**
+ * Obtain the IPv4/6 socket address this represents.
+ *
+ * @param[out] paddr The obtained socket address.
+ * @param[in,out] addrlen The size, in bytes, of the address structure pointed
+ *                        to by paddr. The value that's pointed to by this
+ *                        parameter might change after calling this function if
+ *                        the size of the corresponding address structure
+ *                        changed.
+ *
+ * @returns Whether or not the operation was successful.
+ */
 bool CService::GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const
 {
     if (IsIPv4()) {
@@ -917,11 +968,14 @@ bool CService::GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const
     return false;
 }
 
+/**
+ * @returns An identifier unique to this service's address and port number.
+ */
 std::vector<unsigned char> CService::GetKey() const
 {
     auto key = GetAddrBytes();
-    key.push_back(port / 0x100);
-    key.push_back(port & 0x0FF);
+    key.push_back(port / 0x100); // most significant byte of our port
+    key.push_back(port & 0x0FF); // least significant byte of our port
     return key;
 }
 
@@ -1041,6 +1095,10 @@ CSubNet::CSubNet(const CNetAddr& addr) : CSubNet()
     network = addr;
 }
 
+/**
+ * @returns True if this subnet is valid, the specified address is valid, and
+ *          the specified address belongs in this subnet.
+ */
 bool CSubNet::Match(const CNetAddr &addr) const
 {
     if (!valid || !addr.IsValid() || network.m_net != addr.m_net)
diff --git a/src/netaddress.h b/src/netaddress.h
index d25d63f65666..c771f760e340 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -153,12 +153,16 @@ class CNetAddr
         void SetRaw(Network network, const uint8_t *data);
 
     public:
-        /**
-          * Transform an arbitrary string into a non-routable ipv6 address.
-          * Useful for mapping resolved addresses back to their source.
-         */
         bool SetInternal(const std::string& name);
 
+        /**
+         * Parse a Tor or I2P address and set this object to it.
+         * @param[in] addr Address to parse, for example
+         * pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion or
+         * ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p.
+         * @returns Whether the operation was successful.
+         * @see CNetAddr::IsTor(), CNetAddr::IsI2P()
+         */
         bool SetSpecial(const std::string &strName); // for Tor addresses
         bool IsBindAny() const; // INADDR_ANY equivalent
         bool IsIPv4() const;    // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0)

From 57e3060ec8d8c16786a1cbaa0521cc4ccfbb3930 Mon Sep 17 00:00:00 2001
From: MarcoFalke <falke.marco@gmail.com>
Date: Tue, 2 Jul 2019 17:20:58 -0400
Subject: [PATCH 06/35] Merge #16250: signrawtransactionwithkey: report error
 when missing redeemScript/witnessScript

01174596e69568c434198a86f54cb9ea6740e6c2 signrawtransactionwithkey: report error when missing redeemScript/witnessScript param (Anthony Towns)

Pull request description:

  Adding support for "witnessScript" as an alternative to "redeemScript" when using "signrawtransactionwithkey" meant that the `RPCTypeCheckObj()` call in `SignTransaction` can't error out just because either parameter is missing -- it's only a problem if both are missing, which isn't a state `RPCTypeCheckObj()` tests for. This results in the regression described in #16249. This patch adds some code to test for this case and give a similar error, namely:

      error code: -8
      error message:
      Missing redeemScript/witnessScript

  Fixes: #16249

ACKs for top commit:
  meshcollider:
    utACK https://github.com/bitcoin/bitcoin/pull/16250/commits/01174596e69568c434198a86f54cb9ea6740e6c2
  promag:
    ACK 01174596e. Could also write test without `dict`/`del`:

Tree-SHA512: cf51346b7dea551b7f18f2a93c2a336a293b2535c62c03a5263cd2be8c58cf0cc302891da659c167e88ad1a68a756472c3c07e99f71627c61d32886fc5a3a353
---
 src/rpc/rawtransaction_util.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index 9f22f0555a01..7c7823c5c119 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -207,6 +207,9 @@ void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keyst
                         throw JSONRPCError(RPC_INVALID_PARAMETER, "redeemScript does not match scriptPubKey");
                     }
                 }
+                if (rs.isNull()) {
+                    throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing redeemScript");
+                }
             }
         }
     }

From 914946301c504edf76eb54cfa1e0fb1bc6a1c71e Mon Sep 17 00:00:00 2001
From: "Wladimir J. van der Laan" <laanwj@gmail.com>
Date: Wed, 3 Jul 2019 14:06:45 +0200
Subject: [PATCH 07/35] Merge #14734: fix an undefined behavior in uint::SetHex

0f459d868d85053f1cc066ea9099793f88cbd655 fix an undefined behavior in uint::SetHex (Kaz Wesley)

Pull request description:

  Decrementing psz beyond the beginning of the string is UB, even though
  the out-of-bounds pointer is never dereferenced.

  I don't think any clang sanitizer covers this, so I don't see any way a test could catch the original behavior.

ACKs for top commit:
  promag:
    utACK 0f459d8.
  l2a5b1:
    utACK 0f459d868d85053f1cc066ea9099793f88cbd655

Tree-SHA512: 388223254ea6e955f643d2ebdf74d15a3d494e9f0597d9f05987ebb708d7a1cc06ce64bd25d447d75b5f5561bdae9630dcf25adb7bd75f7a382298b95d127162
---
 src/uint256.cpp | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/src/uint256.cpp b/src/uint256.cpp
index 696a7081dd8d..8c89e896824c 100644
--- a/src/uint256.cpp
+++ b/src/uint256.cpp
@@ -40,16 +40,15 @@ void base_blob<BITS>::SetHex(const char* psz)
         psz += 2;
 
     // hex string to uint
-    const char* pbegin = psz;
-    while (::HexDigit(*psz) != -1)
-        psz++;
-    psz--;
+    size_t digits = 0;
+    while (::HexDigit(psz[digits]) != -1)
+        digits++;
     unsigned char* p1 = (unsigned char*)m_data;
     unsigned char* pend = p1 + WIDTH;
-    while (psz >= pbegin && p1 < pend) {
-        *p1 = ::HexDigit(*psz--);
-        if (psz >= pbegin) {
-            *p1 |= ((unsigned char)::HexDigit(*psz--) << 4);
+    while (digits > 0 && p1 < pend) {
+        *p1 = ::HexDigit(psz[--digits]);
+        if (digits > 0) {
+            *p1 |= ((unsigned char)::HexDigit(psz[--digits]) << 4);
             p1++;
         }
     }

From 13ac152c9b13db0c61ad1ca291a3b7303ae18199 Mon Sep 17 00:00:00 2001
From: "Wladimir J. van der Laan" <laanwj@gmail.com>
Date: Wed, 3 Jul 2019 14:49:07 +0200
Subject: [PATCH 08/35] Merge #16325: rpc: Clarify that block count means
 height excl genesis

fab0c820fa4c0c3227eec85c64310a3bf938a149 rpc: Clarify that block count means height excl genesis (MarcoFalke)

Pull request description:

  There is a common misconception that the block count returned by the blockchain rpcs includes the genesis block. See for example the discussion in https://github.com/bitcoin/bitcoin/pull/16292#issuecomment-506303256.

  However, it really returns the height, which is `0` for the genesis block.

  So clarify that and also remove the misleading "longest blockchain" comment.

  Finally, fix the wallet test that incorrectly used this rpc.

ACKs for top commit:
  instagibbs:
    utACK https://github.com/bitcoin/bitcoin/pull/16325/commits/fab0c820fa4c0c3227eec85c64310a3bf938a149
  promag:
    ACK fab0c82, sorry for the misconception.

Tree-SHA512: 0d087cbb628d3866352bca6420402f392e6a997e579941701a408a7fca355d84645045661f39b022e4479cc07f85a6cddaa9095b6fd9911b245692482420a5e4
---
 src/rpc/blockchain.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 67a3ba564cc9..71ea21b39de1 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -15,7 +15,6 @@
 #include <consensus/validation.h>
 #include <index/blockfilterindex.h>
 #include <index/txindex.h>
-#include <key_io.h>
 #include <llmq/context.h>
 #include <node/coinstats.h>
 #include <node/context.h>
@@ -49,10 +48,11 @@
 
 #include <univalue.h>
 
-#include <mutex>
-#include <condition_variable>
 #include <merkleblock.h>
 
+#include <condition_variable>
+#include <mutex>
+
 struct CUpdatedBlock
 {
     uint256 hash;

From 4459e60acc868d3b04eef744c986449daaa00616 Mon Sep 17 00:00:00 2001
From: "Wladimir J. van der Laan" <laanwj@gmail.com>
Date: Mon, 8 Jul 2019 13:19:39 +0200
Subject: [PATCH 09/35] Merge #16352: build: prune dbus from depends

e8fabd9253400a7c3fe45b34bc572eb00ff5522d build: prune dbus from depends (fanquake)

Pull request description:

  Since #8210 (59d063d07660d6e8d53ed8aa1eb8b0747ea6767c), we've been passing `-dbus-runtime` when configuring Qt.

  ```
  qtbase-opensource-src-5.9.7 $ ./configure -h | grep -i dbus
    -no-dbus ............. Do not build the Qt D-Bus module
    -dbus-linked ......... Build Qt D-Bus and link to libdbus-1 [auto]
    -dbus-runtime ........ Build Qt D-Bus and dynamically load libdbus-1 [no]
  ```

  This means we don't actually seem to be using the `D-Bus` we build in depends. This was pointed out by theuni at the time, [here](https://github.com/bitcoin/bitcoin/pull/7993#issuecomment-223114395) and [here](https://github.com/bitcoin/bitcoin/pull/8210#issuecomment-226930545), but was never followed up. dongcarl also bought it up as part of #16150.

  I've tested building and running `bitcoin-qt` using depends on Debian. Needs further testing.

ACKs for top commit:
  laanwj:
    code review ACK e8fabd9253400a7c3fe45b34bc572eb00ff5522d

Tree-SHA512: 164e6e52b6f97c04aef42bd185e2a157bc1a42103840f9404c5a795749f45a8c2c35f35873395a3a56398b3cd5955496b90d9c885d929b434c9bc871695abe20
---
 depends/packages/dbus.mk     | 27 ---------------------------
 depends/packages/packages.mk |  2 +-
 doc/dependencies.md          |  1 -
 3 files changed, 1 insertion(+), 29 deletions(-)
 delete mode 100644 depends/packages/dbus.mk

diff --git a/depends/packages/dbus.mk b/depends/packages/dbus.mk
deleted file mode 100644
index ad10b0fdd7fb..000000000000
--- a/depends/packages/dbus.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-package=dbus
-$(package)_version=1.10.18
-$(package)_download_path=https://dbus.freedesktop.org/releases/dbus
-$(package)_file_name=$(package)-$($(package)_version).tar.gz
-$(package)_sha256_hash=6049ddd5f3f3e2618f615f1faeda0a115104423a7996b7aa73e2f36e38cc514a
-$(package)_dependencies=expat
-
-define $(package)_set_vars
-  $(package)_config_opts=--disable-tests --disable-doxygen-docs --disable-xml-docs --disable-shared --without-x
-endef
-
-define $(package)_config_cmds
-  $($(package)_autoconf)
-endef
-
-define $(package)_build_cmds
-  $(MAKE) -C dbus libdbus-1.la
-endef
-
-define $(package)_stage_cmds
-  $(MAKE) -C dbus DESTDIR=$($(package)_staging_dir) install-libLTLIBRARIES install-dbusincludeHEADERS install-nodist_dbusarchincludeHEADERS && \
-  $(MAKE) DESTDIR=$($(package)_staging_dir) install-pkgconfigDATA
-endef
-
-define $(package)_postprocess_cmds
-  rm lib/*.la
-endef
diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk
index 1b9ecd32cb6d..3452a9359c96 100644
--- a/depends/packages/packages.mk
+++ b/depends/packages/packages.mk
@@ -1,6 +1,6 @@
 packages:=boost libevent gmp backtrace
 
-qt_linux_packages:=qt expat dbus libxcb xcb_proto libXau xproto freetype fontconfig libxkbcommon
+qt_linux_packages:=qt expat libxcb xcb_proto libXau xproto freetype fontconfig libxkbcommon
 
 qrencode_linux_packages = qrencode
 qrencode_android_packages = qrencode
diff --git a/doc/dependencies.md b/doc/dependencies.md
index cc74b0f4edc7..8869b93fee97 100644
--- a/doc/dependencies.md
+++ b/doc/dependencies.md
@@ -8,7 +8,6 @@ These are the dependencies currently used by Dash Core. You can find instruction
 | Berkeley DB | [4.8.30](https://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html) | 4.8.x | No |  |  |
 | Boost | [1.73.0](https://www.boost.org/users/download/) | [1.64.0](https://github.com/bitcoin/bitcoin/pull/22320) | No |  |  |
 | Clang<sup>[ \* ](#note1)</sup> |  | [5.0+](https://releases.llvm.org/download.html) (C++17 support) |  |  |  |
-| D-Bus | [1.10.18](https://cgit.freedesktop.org/dbus/dbus/tree/NEWS?h=dbus-1.10) |  | No | Yes |  |
 | Expat | [2.2.7](https://libexpat.github.io/) |  | No | Yes |  |
 | fontconfig | [2.12.1](https://www.freedesktop.org/software/fontconfig/release/) |  | No | Yes |  |
 | FreeType | [2.7.1](https://download.savannah.gnu.org/releases/freetype) |  | No |  | [Yes](https://github.com/dashpay/dash/blob/develop/depends/packages/qt.mk) (Android only) |

From 906dd71de0ed6f74e88e719a52eeaa99f78f0883 Mon Sep 17 00:00:00 2001
From: "Wladimir J. van der Laan" <laanwj@gmail.com>
Date: Mon, 8 Jul 2019 20:03:25 +0200
Subject: [PATCH 10/35] Merge #16267: bench: Benchmark blockToJSON

91509ffe247b0eacbf84214c7c9c3f8a0012f2eb bench: Benchmark blockToJSON (Kirill Fomichev)

Pull request description:

  Related:
  - "getblock performance issue on verbosity" https://github.com/bitcoin/bitcoin/issues/15925
  - "refactor: Avoid UniValue copy constructor" #15974

ACKs for top commit:
  laanwj:
    ACK 91509ffe247b0eacbf84214c7c9c3f8a0012f2eb

Tree-SHA512: e70b12cb31921c7527bde334f52f39776da698b6bbdb196079a8b68478c67585a5bd7bed7403f65166bd604f7ed60778c53dc064d743bb8368318a1283d1073e
---
 src/Makefile.bench.include   |  1 +
 src/bench/rpc_blockchain.cpp | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 36 insertions(+)
 create mode 100644 src/bench/rpc_blockchain.cpp

diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index db47baaffaf5..6171a0381a54 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -39,6 +39,7 @@ bench_bench_dash_SOURCES = \
   bench/mempool_stress.cpp \
   bench/nanobench.h \
   bench/nanobench.cpp \
+  bench/rpc_blockchain.cpp \
   bench/rpc_mempool.cpp \
   bench/util_time.cpp \
   bench/base58.cpp \
diff --git a/src/bench/rpc_blockchain.cpp b/src/bench/rpc_blockchain.cpp
new file mode 100644
index 000000000000..3cf8947b9964
--- /dev/null
+++ b/src/bench/rpc_blockchain.cpp
@@ -0,0 +1,35 @@
+// Copyright (c) 2016-2019 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <bench/bench.h>
+#include <bench/data.h>
+
+#include <validation.h>
+#include <streams.h>
+#include <consensus/validation.h>
+#include <llmq/chainlocks.h>
+#include <llmq/instantsend.h>
+#include <rpc/blockchain.h>
+
+#include <univalue.h>
+
+static void BlockToJsonVerbose(benchmark::Bench& bench) {
+    CDataStream stream(benchmark::data::block813851, SER_NETWORK, PROTOCOL_VERSION);
+    char a = '\0';
+    stream.write(&a, 1); // Prevent compaction
+
+    CBlock block;
+    stream >> block;
+
+    CBlockIndex blockindex;
+    const uint256 blockHash = block.GetHash();
+    blockindex.phashBlock = &blockHash;
+    blockindex.nBits = 403014710;
+
+    bench.run([&] {
+        (void)blockToJSON(block, &blockindex, &blockindex, *llmq::chainLocksHandler, *llmq::quorumInstantSendManager, /*verbose*/ true);
+    });
+}
+
+BENCHMARK(BlockToJsonVerbose);

From ddd8e2f45bf19dbbb3169dcf419ab3bb7d626d55 Mon Sep 17 00:00:00 2001
From: "Wladimir J. van der Laan" <laanwj@gmail.com>
Date: Fri, 12 Jul 2019 21:17:01 +0200
Subject: [PATCH 11/35] Merge #16334: test: rpc_users: Also test rpcauth.py
 with password.

e263a343d4b6a2622df6bb734cd9d51a0d20a663 test: rpc_users: Make variable names more clear. (Carl Dong)
830dc2dd0fccb7f3ec49ff7233a188d92c541e7e test: rpc_users: Also test rpcauth.py with specified password. (Carl Dong)
c73d871799982ca29c29cef90e1a78814cf34019 test: rpc_users: Add function for testing auth params. (Carl Dong)
604e2a997ff26202dd0fa1932d60dc14cc53ac6d test: rpc_users: Add function for auth'd requests. (Carl Dong)

Pull request description:

  Fixes #14758

  First two commits are tidy-ups which I feel are worthwhile as they are very straightforward, cut down the file by 50%, and made the final diff more minimal. Happy to squash after review.

ACKs for top commit:
  laanwj:
    ACK e263a343d4b6a2622df6bb734cd9d51a0d20a663

Tree-SHA512: aa75c48570a87060238932d4c68e17234e158077f6195fb4917367e1ecc565e3cd8dd0ae51f9159ddd3d03742739680391bc1246454302db22d4a608c0633e80
---
 test/functional/rpc_users.py | 178 ++++++++---------------------------
 1 file changed, 39 insertions(+), 139 deletions(-)

diff --git a/test/functional/rpc_users.py b/test/functional/rpc_users.py
index 22c8885b8dc5..76d66b2a2a79 100755
--- a/test/functional/rpc_users.py
+++ b/test/functional/rpc_users.py
@@ -20,6 +20,17 @@
 import configparser
 import sys
 
+def call_with_auth(node, user, password):
+    url = urllib.parse.urlparse(node.url)
+    headers = {"Authorization": "Basic " + str_to_b64str('{}:{}'.format(user, password))}
+
+    conn = http.client.HTTPConnection(url.hostname, url.port)
+    conn.connect()
+    conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
+    resp = conn.getresponse()
+    conn.close()
+    return resp
+
 
 class HTTPBasicsTest(BitcoinTestFramework):
     def set_test_params(self):
@@ -29,15 +40,24 @@ def set_test_params(self):
     def setup_chain(self):
         super().setup_chain()
         #Append rpcauth to dash.conf before initialization
+        self.rtpassword = "cA773lm788buwYe4g4WT+05pKyNruVKjQ25x3n0DQcM="
         rpcauth = "rpcauth=rt:93648e835a54c573682c2eb19f882535$7681e9c5b74bdd85e78166031d2058e1069b3ed7ed967c93fc63abba06f31144"
-        rpcauth2 = "rpcauth=rt2:f8607b1a88861fac29dfccf9b52ff9f$ff36a0c23c8c62b4846112e50fa888416e94c17bfd4c42f88fd8f55ec6a3137e"
-        rpcuser = "rpcuser=rpcuser💻"
-        rpcpassword = "rpcpassword=rpcpassword🔑"
 
-        self.user = ''.join(SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(10))
+        self.rpcuser = "rpcuser💻"
+        self.rpcpassword = "rpcpassword🔑"
+
         config = configparser.ConfigParser()
         config.read_file(open(self.options.configfile))
         gen_rpcauth = config['environment']['RPCAUTH']
+
+        # Generate RPCAUTH with specified password
+        self.rt2password = "8/F3uMDw4KSEbw96U3CA1C4X05dkHDN2BPFjTgZW4KI="
+        p = subprocess.Popen([sys.executable, gen_rpcauth, 'rt2', self.rt2password], stdout=subprocess.PIPE, universal_newlines=True)
+        lines = p.stdout.read().splitlines()
+        rpcauth2 = lines[1]
+
+        # Generate RPCAUTH without specifying password
+        self.user = ''.join(SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(10))
         p = subprocess.Popen([sys.executable, gen_rpcauth, self.user], stdout=subprocess.PIPE, universal_newlines=True)
         lines = p.stdout.read().splitlines()
         rpcauth3 = lines[1]
@@ -48,155 +68,35 @@ def setup_chain(self):
             f.write(rpcauth2 + "\n")
             f.write(rpcauth3 + "\n")
         with open(os.path.join(get_datadir_path(self.options.tmpdir, 1), "dash.conf"), 'a', encoding='utf8') as f:
-            f.write(rpcuser + "\n")
-            f.write(rpcpassword + "\n")
+            f.write("rpcuser={}\n".format(self.rpcuser))
+            f.write("rpcpassword={}\n".format(self.rpcpassword))
 
-    def run_test(self):
-        self.log.info('Check correctness of the rpcauth config option')
-        url = urllib.parse.urlparse(self.nodes[0].url)
-
-        #Old authpair
-        authpair = url.username + ':' + url.password
-
-        #New authpair generated via share/rpcauth tool
-        password = "cA773lm788buwYe4g4WT+05pKyNruVKjQ25x3n0DQcM="
-
-        #Second authpair with different username
-        password2 = "8/F3uMDw4KSEbw96U3CA1C4X05dkHDN2BPFjTgZW4KI="
-        authpairnew = "rt:"+password
-
-        self.log.info('Correct...')
-        headers = {"Authorization": "Basic " + str_to_b64str(authpair)}
-
-        conn = http.client.HTTPConnection(url.hostname, url.port)
-        conn.connect()
-        conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
-        resp = conn.getresponse()
-        assert_equal(resp.status, 200)
-        conn.close()
-
-        #Use new authpair to confirm both work
+    def test_auth(self, node, user, password):
         self.log.info('Correct...')
-        headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
-
-        conn = http.client.HTTPConnection(url.hostname, url.port)
-        conn.connect()
-        conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
-        resp = conn.getresponse()
-        assert_equal(resp.status, 200)
-        conn.close()
+        assert_equal(200, call_with_auth(node, user, password).status)
 
-        #Wrong login name with rt's password
         self.log.info('Wrong...')
-        authpairnew = "rtwrong:"+password
-        headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
+        assert_equal(401, call_with_auth(node, user, password+'wrong').status)
 
-        conn = http.client.HTTPConnection(url.hostname, url.port)
-        conn.connect()
-        conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
-        resp = conn.getresponse()
-        assert_equal(resp.status, 401)
-        conn.close()
-
-        #Wrong password for rt
         self.log.info('Wrong...')
-        authpairnew = "rt:"+password+"wrong"
-        headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
-
-        conn = http.client.HTTPConnection(url.hostname, url.port)
-        conn.connect()
-        conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
-        resp = conn.getresponse()
-        assert_equal(resp.status, 401)
-        conn.close()
+        assert_equal(401, call_with_auth(node, user+'wrong', password).status)
 
-        #Correct for rt2
-        self.log.info('Correct...')
-        authpairnew = "rt2:"+password2
-        headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
-
-        conn = http.client.HTTPConnection(url.hostname, url.port)
-        conn.connect()
-        conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
-        resp = conn.getresponse()
-        assert_equal(resp.status, 200)
-        conn.close()
-
-        #Wrong password for rt2
         self.log.info('Wrong...')
-        authpairnew = "rt2:"+password2+"wrong"
-        headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
-
-        conn = http.client.HTTPConnection(url.hostname, url.port)
-        conn.connect()
-        conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
-        resp = conn.getresponse()
-        assert_equal(resp.status, 401)
-        conn.close()
-
-        #Correct for randomly generated user
-        self.log.info('Correct...')
-        authpairnew = self.user+":"+self.password
-        headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
-
-        conn = http.client.HTTPConnection(url.hostname, url.port)
-        conn.connect()
-        conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
-        resp = conn.getresponse()
-        assert_equal(resp.status, 200)
-        conn.close()
+        assert_equal(401, call_with_auth(node, user+'wrong', password+'wrong').status)
 
-        #Wrong password for randomly generated user
-        self.log.info('Wrong...')
-        authpairnew = self.user+":"+self.password+"Wrong"
-        headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
+    def run_test(self):
+        self.log.info('Check correctness of the rpcauth config option')
+        url = urllib.parse.urlparse(self.nodes[0].url)
 
-        conn = http.client.HTTPConnection(url.hostname, url.port)
-        conn.connect()
-        conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
-        resp = conn.getresponse()
-        assert_equal(resp.status, 401)
-        conn.close()
+        self.test_auth(self.nodes[0], url.username, url.password)
+        self.test_auth(self.nodes[0], 'rt', self.rtpassword)
+        self.test_auth(self.nodes[0], 'rt2', self.rt2password)
+        self.test_auth(self.nodes[0], self.user, self.password)
 
         self.log.info('Check correctness of the rpcuser/rpcpassword config options')
         url = urllib.parse.urlparse(self.nodes[1].url)
 
-        # rpcuser and rpcpassword authpair
-        self.log.info('Correct...')
-        rpcuserauthpair = "rpcuser💻:rpcpassword🔑"
-
-        headers = {"Authorization": "Basic " + str_to_b64str(rpcuserauthpair)}
-
-        conn = http.client.HTTPConnection(url.hostname, url.port)
-        conn.connect()
-        conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
-        resp = conn.getresponse()
-        assert_equal(resp.status, 200)
-        conn.close()
-
-        #Wrong login name with rpcuser's password
-        rpcuserauthpair = "rpcuserwrong:rpcpassword"
-        headers = {"Authorization": "Basic " + str_to_b64str(rpcuserauthpair)}
-
-        conn = http.client.HTTPConnection(url.hostname, url.port)
-        conn.connect()
-        conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
-        resp = conn.getresponse()
-        assert_equal(resp.status, 401)
-        conn.close()
-
-        #Wrong password for rpcuser
-        self.log.info('Wrong...')
-        rpcuserauthpair = "rpcuser:rpcpasswordwrong"
-        headers = {"Authorization": "Basic " + str_to_b64str(rpcuserauthpair)}
-
-        conn = http.client.HTTPConnection(url.hostname, url.port)
-        conn.connect()
-        conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
-        resp = conn.getresponse()
-        assert_equal(resp.status, 401)
-        conn.close()
-
+        self.test_auth(self.nodes[1], self.rpcuser, self.rpcpassword)
 
         init_error = 'Error: Unable to start HTTP server. See debug log for details.'
 

From e246892fd701dafa894ce76f49bb9092a53ac968 Mon Sep 17 00:00:00 2001
From: MarcoFalke <falke.marco@gmail.com>
Date: Sun, 28 Jul 2019 10:12:46 -0400
Subject: [PATCH 12/35] Merge #16464: [qa] Ensure we don't generate a too-big
 block in p2sh sigops test

bf3be5297a746982cf8e83f45d342121e5665f80 [qa] Ensure we don't generate a too-big block in p2sh sigops test (Suhas Daftuar)

Pull request description:

  There's a bug in the loop that is calculating the block size in the p2sh sigops test -- we start with the size of the block when it has no transactions, and then increment by the size of each transaction we add, without regard to the changing size of the encoding for the number of transactions in the block.

  This might be fine if the block construction were deterministic, but the first transaction in the block has an ECDSA signature which can be variable length, so we see intermittent failures of this test when the initial transaction has a 70-byte signature and the block ends up being one byte too big.

  Fix this by double-checking the block size after construction.

ACKs for top commit:
  jonasschnelli:
    utACK bf3be5297a746982cf8e83f45d342121e5665f80
  jnewbery:
    tested ACK bf3be5297a746982cf8e83f45d342121e5665f80

Tree-SHA512: f86385b96f7a6feafa4183727f5f2c9aae8ad70060b574aad13b150f174a17ce9a0040bc51ae7a04bd08f2a5298b983a84b0aed5e86a8440189ebc63b99e64dc
---
 test/functional/feature_block.py | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index 96f4d115dc9f..44dc80ccdb3c 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -506,6 +506,14 @@ def run_test(self):
             tx_last = tx_new
             b39_outputs += 1
 
+        # The accounting in the loop above can be off, because it misses the
+        # compact size encoding of the number of transactions in the block.
+        # Make sure we didn't accidentally make too big a block. Note that the
+        # size of the block has non-determinism due to the ECDSA signature in
+        # the first transaction.
+        while (len(b39.serialize()) >= MAX_BLOCK_SIZE):
+            del b39.vtx[-1]
+
         b39 = self.update_block(39, [])
         self.send_blocks([b39], True)
         self.save_spendable_output()

From 211faa9c2929d9835786b654501015cf36e69468 Mon Sep 17 00:00:00 2001
From: MeshCollider <dobsonsa68@gmail.com>
Date: Tue, 30 Jul 2019 20:31:12 +1200
Subject: [PATCH 13/35] Merge #15709: wallet: Do not add "setting" key as
 unknown

914923d125f5d17b39b4dc05f666d130e80a68b2 Add setting as known type (Peter Bushnell)

Pull request description:

  When loading old wallets I get "Unknown wallet records" showing up in the log file. The key that is adding to the unknown record count is "setting", this is a known key removed in the 0.6 release of Bitcoin in the commit linked below. The "setting" key is not known to the wallet anymore, like "acentry" which is not added as an unknown record, but the "setting" key was used in previous versions of Bitcoin.

  https://github.com/bitcoin/bitcoin/commit/972060ce0e9746c979ce0ddeeb997121414c1d58#diff-8094838580e1bb7a3bb8fc78dcebc733

ACKs for top commit:
  laanwj:
    ACK 914923d125f5d17b39b4dc05f666d130e80a68b2, this code change is straightforward enough and I don't think it makes sense to warn about this key being present.
  meshcollider:
    ACK 914923d125f5d17b39b4dc05f666d130e80a68b2

Tree-SHA512: 6346690c05cebae2dcd868512322bf5250f6fbd07abb5e747065444185d3f69e19e1a99e3f38d6e34535ffd6979b2297100ba9c7da8e45ca792598eded5ae0d3
---
 src/wallet/walletdb.cpp | 4 +++-
 src/wallet/walletdb.h   | 1 +
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 32ca41e963bc..b32544b28a2b 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -55,6 +55,7 @@ const std::string ORDERPOSNEXT{"orderposnext"};
 const std::string POOL{"pool"};
 const std::string PURPOSE{"purpose"};
 const std::string PRIVATESEND_SALT{"ps_salt"};
+const std::string SETTINGS{"settings"};
 const std::string TX{"tx"};
 const std::string VERSION{"version"};
 const std::string WATCHMETA{"watchmeta"};
@@ -487,7 +488,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
             strErr = "Found unsupported 'wkey' record, try loading with version 0.17";
             return false;
         } else if (strType != DBKeys::BESTBLOCK && strType != DBKeys::BESTBLOCK_NOMERKLE &&
-                   strType != DBKeys::MINVERSION && strType != DBKeys::ACENTRY && strType != DBKeys::VERSION) {
+                   strType != DBKeys::MINVERSION && strType != DBKeys::ACENTRY &&
+                   strType != DBKeys::VERSION && strType != DBKeys::SETTINGS) {
             wss.m_unknown_records++;
         }
     } catch (const std::exception& e) {
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 2ec827c89ff8..878ed78df2d5 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -76,6 +76,7 @@ extern const std::string ORDERPOSNEXT;
 extern const std::string POOL;
 extern const std::string PURPOSE;
 extern const std::string PRIVATESEND_SALT;
+extern const std::string SETTINGS;
 extern const std::string TX;
 extern const std::string VERSION;
 extern const std::string WATCHMETA;

From 08aa10c8cb935d813869366a9bc9317768a8a4a5 Mon Sep 17 00:00:00 2001
From: Konstantin Akimov <knstqq@gmail.com>
Date: Thu, 30 Mar 2023 21:49:22 +0700
Subject: [PATCH 14/35] fix: add a missing name for key `hdaccounts` for RPC
 help for `getwalletinfo` (#5280)

## Issue being fixed or feature implemented
RPC help has a wrongly generated RPC help output in CLI and online help:
https://dashcore.readme.io/docs/core-api-ref-remote-procedure-calls-wallet#getwalletinfo

New version:
```
...
"hdaccounts" : [                        (json array)
  {                                     (json object)
    "hdaccountindex" : n,               (numeric) the index of the account
    "hdexternalkeyindex" : n,           (numeric) current external childkey index
    "hdinternalkeyindex" : n            (numeric) current internal childkey index
  },
  ...
],
...
```

against old version:
```
...
"" : [                                  (json array)
  {                                     (json object)
    "hdaccountindex" : n,               (numeric) the index of the account
    "hdexternalkeyindex" : n,           (numeric) current external childkey index
    "hdinternalkeyindex" : n            (numeric) current internal childkey index
  },
  ...
],
...
```



## What was done?
Add a missing name `hdaccounts` for that key for `getwalletinfo` RPC


## How Has This Been Tested?
Run a command `help getwalletinfo` for old and for new versions.


## Breaking Changes
No breaking changes. It doesn't change rpc, only change text description
(help).


## Checklist:
- [x] I have performed a self-review of my own code
- [x] I have assigned this pull request to a milestone
---
 src/wallet/rpcwallet.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 1c92cac730e7..613b0b18d295 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -2474,7 +2474,7 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
                             {RPCResult::Type::STR_AMOUNT, "paytxfee", "the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB"},
                             {RPCResult::Type::STR_HEX, "hdchainid", "the ID of the HD chain"},
                             {RPCResult::Type::STR, "hdaccountcount", "how many accounts of the HD chain are in this wallet"},
-                            {RPCResult::Type::ARR, "", "",
+                            {RPCResult::Type::ARR, "hdaccounts", "",
                                 {
                                 {RPCResult::Type::OBJ, "", "",
                                     {

From f4b91c08a6ea0ed0447d0c30c41905f90d8626fb Mon Sep 17 00:00:00 2001
From: Odysseas Gabrielides <odysseas.gabrielides@gmail.com>
Date: Thu, 30 Mar 2023 18:05:15 +0300
Subject: [PATCH 15/35] fix(net): Do not punish nodes when Quorum data are
 missing. (#5272)

## Issue being fixed or feature implemented
Currently, we store internally the nodes that already requested
`QGETDATA` for the same Quorum.
If data for the same Quorum is requested twice from the same `proRegTx`,
then the requester is P2P misbehaved.

## What was done?
Some data like `VerificationVector` and `EncryptedContributions` are not
instantly available.
This PR does not misbehave nodes for requesting data that weren't
available when asked.

## How Has This Been Tested?


## Breaking Changes


## Checklist:
- [x] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have added or updated relevant unit/integration/functional/e2e
tests
- [ ] I have made corresponding changes to the documentation

**For repository code-owners and collaborators only**
- [x] I have assigned this pull request to a milestone
---
 src/llmq/quorums.cpp | 34 +++++++++++++++++++++++++---------
 1 file changed, 25 insertions(+), 9 deletions(-)

diff --git a/src/llmq/quorums.cpp b/src/llmq/quorums.cpp
index cfcf66366822..1304ee12872e 100644
--- a/src/llmq/quorums.cpp
+++ b/src/llmq/quorums.cpp
@@ -631,13 +631,29 @@ void CQuorumManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, C
         CQuorumDataRequest request;
         vRecv >> request;
 
-        auto sendQDATA = [&](CQuorumDataRequest::Errors nError = CQuorumDataRequest::Errors::UNDEFINED,
+        auto sendQDATA = [&](CQuorumDataRequest::Errors nError,
+                             bool request_limit_exceeded,
                              const CDataStream& body = CDataStream(SER_NETWORK, PROTOCOL_VERSION)) {
+            switch (nError) {
+                case (CQuorumDataRequest::Errors::NONE):
+                case (CQuorumDataRequest::Errors::QUORUM_TYPE_INVALID):
+                case (CQuorumDataRequest::Errors::QUORUM_BLOCK_NOT_FOUND):
+                case (CQuorumDataRequest::Errors::QUORUM_NOT_FOUND):
+                case (CQuorumDataRequest::Errors::MASTERNODE_IS_NO_MEMBER):
+                case (CQuorumDataRequest::Errors::UNDEFINED):
+                    if (request_limit_exceeded) errorHandler("Request limit exceeded", 25);
+                    break;
+                case (CQuorumDataRequest::Errors::QUORUM_VERIFICATION_VECTOR_MISSING):
+                case (CQuorumDataRequest::Errors::ENCRYPTED_CONTRIBUTIONS_MISSING):
+                    // Do not punish limit exceed if we don't have the requested data
+                    break;
+            }
             request.SetError(nError);
             CDataStream ssResponse(SER_NETWORK, pfrom.GetSendVersion(), request, body);
             connman.PushMessage(&pfrom, CNetMsgMaker(pfrom.GetSendVersion()).Make(NetMsgType::QDATA, ssResponse));
         };
 
+        bool request_limit_exceeded(false);
         {
             LOCK2(cs_main, cs_data_requests);
             CQuorumDataRequestKey key;
@@ -651,24 +667,24 @@ void CQuorumManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, C
             } else if (it->second.IsExpired()) {
                 it->second = request;
             } else {
-                errorHandler("Request limit exceeded", 25);
+                request_limit_exceeded = true;
             }
         }
 
         if (!GetLLMQParams(request.GetLLMQType()).has_value()) {
-            sendQDATA(CQuorumDataRequest::Errors::QUORUM_TYPE_INVALID);
+            sendQDATA(CQuorumDataRequest::Errors::QUORUM_TYPE_INVALID, request_limit_exceeded);
             return;
         }
 
         const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return LookupBlockIndex(request.GetQuorumHash()));
         if (pQuorumBaseBlockIndex == nullptr) {
-            sendQDATA(CQuorumDataRequest::Errors::QUORUM_BLOCK_NOT_FOUND);
+            sendQDATA(CQuorumDataRequest::Errors::QUORUM_BLOCK_NOT_FOUND, request_limit_exceeded);
             return;
         }
 
         const CQuorumCPtr pQuorum = GetQuorum(request.GetLLMQType(), pQuorumBaseBlockIndex);
         if (pQuorum == nullptr) {
-            sendQDATA(CQuorumDataRequest::Errors::QUORUM_NOT_FOUND);
+            sendQDATA(CQuorumDataRequest::Errors::QUORUM_NOT_FOUND, request_limit_exceeded);
             return;
         }
 
@@ -677,7 +693,7 @@ void CQuorumManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, C
         // Check if request wants QUORUM_VERIFICATION_VECTOR data
         if (request.GetDataMask() & CQuorumDataRequest::QUORUM_VERIFICATION_VECTOR) {
             if (!pQuorum->HasVerificationVector()) {
-                sendQDATA(CQuorumDataRequest::Errors::QUORUM_VERIFICATION_VECTOR_MISSING);
+                sendQDATA(CQuorumDataRequest::Errors::QUORUM_VERIFICATION_VECTOR_MISSING, request_limit_exceeded);
                 return;
             }
 
@@ -689,20 +705,20 @@ void CQuorumManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, C
 
             int memberIdx = pQuorum->GetMemberIndex(request.GetProTxHash());
             if (memberIdx == -1) {
-                sendQDATA(CQuorumDataRequest::Errors::MASTERNODE_IS_NO_MEMBER);
+                sendQDATA(CQuorumDataRequest::Errors::MASTERNODE_IS_NO_MEMBER, request_limit_exceeded);
                 return;
             }
 
             std::vector<CBLSIESEncryptedObject<CBLSSecretKey>> vecEncrypted;
             if (!dkgManager.GetEncryptedContributions(request.GetLLMQType(), pQuorumBaseBlockIndex, pQuorum->qc->validMembers, request.GetProTxHash(), vecEncrypted)) {
-                sendQDATA(CQuorumDataRequest::Errors::ENCRYPTED_CONTRIBUTIONS_MISSING);
+                sendQDATA(CQuorumDataRequest::Errors::ENCRYPTED_CONTRIBUTIONS_MISSING, request_limit_exceeded);
                 return;
             }
 
             ssResponseData << vecEncrypted;
         }
 
-        sendQDATA(CQuorumDataRequest::Errors::NONE, ssResponseData);
+        sendQDATA(CQuorumDataRequest::Errors::NONE, request_limit_exceeded, ssResponseData);
         return;
     }
 

From 2d89442d89fbfebb69ddb4ea35221f5305ef980e Mon Sep 17 00:00:00 2001
From: Odysseas Gabrielides <odysseas.gabrielides@gmail.com>
Date: Thu, 30 Mar 2023 18:06:47 +0300
Subject: [PATCH 16/35] fix: hpmn adjustements to getprojectedmnpayees (#5274)

## Issue being fixed or feature implemented
`GetProjectedMNPayees` wasn't projecting MN payees correctly.

## What was done?
HPMNs are now added 4 times before sorting the return list.
In addition, the case of last payee being HPMN and having still pending
payments is handled.


## How Has This Been Tested?
Tested on Masternodes / Next Payment tab on Qt client on Testnet.

## Breaking Changes


## Checklist:
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have added or updated relevant unit/integration/functional/e2e
tests
- [ ] I have made corresponding changes to the documentation

**For repository code-owners and collaborators only**
- [x] I have assigned this pull request to a milestone
---
 src/evo/deterministicmns.cpp | 26 +++++++++++++++++++++++---
 src/evo/deterministicmns.h   |  9 +++++++++
 2 files changed, 32 insertions(+), 3 deletions(-)

diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp
index 9905d351b542..fe266149ad9c 100644
--- a/src/evo/deterministicmns.cpp
+++ b/src/evo/deterministicmns.cpp
@@ -218,15 +218,35 @@ std::vector<CDeterministicMNCPtr> CDeterministicMNList::GetProjectedMNPayees(int
     if (nCount < 0 ) {
         return {};
     }
-    nCount = std::min(nCount, int(GetValidMNsCount()));
+    nCount = std::min(nCount, int(GetValidWeightedMNsCount()));
 
     std::vector<CDeterministicMNCPtr> result;
     result.reserve(nCount);
 
+    auto remaining_hpmn_payments = 0;
+    CDeterministicMNCPtr hpmn_to_be_skipped = nullptr;
     ForEachMNShared(true, [&](const CDeterministicMNCPtr& dmn) {
-        result.emplace_back(dmn);
+        if (dmn->pdmnState->nLastPaidHeight == nHeight) {
+            // We found the last MN Payee.
+            // If the last payee is a HPMN, we need to check its consecutive payments and pay him again if needed
+            if (dmn->nType == MnType::HighPerformance && dmn->pdmnState->nConsecutivePayments < dmn_types::HighPerformance.voting_weight) {
+                remaining_hpmn_payments = dmn_types::HighPerformance.voting_weight - dmn->pdmnState->nConsecutivePayments;
+                for ([[maybe_unused]] auto _ : irange::range(remaining_hpmn_payments)) {
+                    result.emplace_back(dmn);
+                    hpmn_to_be_skipped = dmn;
+                }
+            }
+        }
+        return;
+    });
+
+    ForEachMNShared(true, [&](const CDeterministicMNCPtr& dmn) {
+        if (dmn == hpmn_to_be_skipped) return;
+        for ([[maybe_unused]] auto _ : irange::range(GetMnType(dmn->nType).voting_weight)) {
+            result.emplace_back(dmn);
+        }
     });
-    std::sort(result.begin(), result.end(), [&](const CDeterministicMNCPtr& a, const CDeterministicMNCPtr& b) {
+    std::sort(result.begin() + remaining_hpmn_payments, result.end(), [&](const CDeterministicMNCPtr& a, const CDeterministicMNCPtr& b) {
         return CompareByLastPaid(a.get(), b.get());
     });
 
diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h
index bd7f74d15fa7..108cbbc75f7d 100644
--- a/src/evo/deterministicmns.h
+++ b/src/evo/deterministicmns.h
@@ -19,6 +19,7 @@
 
 #include <immer/map.hpp>
 
+#include <numeric>
 #include <unordered_map>
 #include <utility>
 
@@ -241,6 +242,14 @@ class CDeterministicMNList
         return ranges::count_if(mnMap, [](const auto& p) { return p.second->nType == MnType::HighPerformance && IsMNValid(*p.second); });
     }
 
+    [[nodiscard]] size_t GetValidWeightedMNsCount() const
+    {
+        return std::accumulate(mnMap.begin(), mnMap.end(), 0, [](auto res, const auto& p) {
+                                                                if (!IsMNValid(*p.second)) return res;
+                                                                return res + GetMnType(p.second->nType).voting_weight;
+                                                            });
+    }
+
     /**
      * Execute a callback on all masternodes in the mnList. This will pass a reference
      * of each masternode to the callback function. This should be preferred over ForEachMNShared.

From 1a96a98481b66a40443ceaeb7cc3578c55c4caba Mon Sep 17 00:00:00 2001
From: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com>
Date: Thu, 30 Mar 2023 10:24:44 -0500
Subject: [PATCH 17/35] fix: add a `bias` to IsExpired to avoid potential
 timing issues where nodeA thinks it's been 300 seconds but nodeB only thinks
 it's been 295 for some reason (#5276)

## Issue being fixed or feature implemented
add a bias to IsExpired to avoid potential timing issues where nodeA thinks it's been 300 seconds but nodeB only thinks it's been 295 for some reason

## What was done?


## How Has This Been Tested?


## Breaking Changes


## Checklist:
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have added or updated relevant unit/integration/functional/e2e
tests
- [ ] I have made corresponding changes to the documentation

**For repository code-owners and collaborators only**
- [x] I have assigned this pull request to a milestone
---
 src/llmq/quorums.cpp | 8 ++++----
 src/llmq/quorums.h   | 3 ++-
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/src/llmq/quorums.cpp b/src/llmq/quorums.cpp
index 1304ee12872e..522e60d0a1d9 100644
--- a/src/llmq/quorums.cpp
+++ b/src/llmq/quorums.cpp
@@ -283,7 +283,7 @@ void CQuorumManager::UpdatedBlockTip(const CBlockIndex* pindexNew, bool fInitial
         LOCK(cs_data_requests);
         auto it = mapQuorumDataRequests.begin();
         while (it != mapQuorumDataRequests.end()) {
-            if (it->second.IsExpired()) {
+            if (it->second.IsExpired(/*add_bias=*/true)) {
                 it = mapQuorumDataRequests.erase(it);
             } else {
                 ++it;
@@ -474,7 +474,7 @@ bool CQuorumManager::RequestQuorumData(CNode* pfrom, Consensus::LLMQType llmqTyp
     key.quorumHash = pQuorumBaseBlockIndex->GetBlockHash();
     key.llmqType = llmqType;
     auto it = mapQuorumDataRequests.emplace(key, CQuorumDataRequest(llmqType, pQuorumBaseBlockIndex->GetBlockHash(), nDataMask, proTxHash));
-    if (!it.second && !it.first->second.IsExpired()) {
+    if (!it.second && !it.first->second.IsExpired(/*add_bias=*/true)) {
         LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- Already requested\n", __func__);
         return false;
     }
@@ -664,7 +664,7 @@ void CQuorumManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, C
             auto it = mapQuorumDataRequests.find(key);
             if (it == mapQuorumDataRequests.end()) {
                 it = mapQuorumDataRequests.emplace(key, request).first;
-            } else if (it->second.IsExpired()) {
+            } else if (it->second.IsExpired(/*add_bias=*/false)) {
                 it->second = request;
             } else {
                 request_limit_exceeded = true;
@@ -919,7 +919,7 @@ void CQuorumManager::StartQuorumDataRecoveryThread(const CQuorumCPtr pQuorum, co
                     key.quorumHash = pQuorum->qc->quorumHash;
                     key.llmqType = pQuorum->qc->llmqType;
                     auto it = mapQuorumDataRequests.find(key);
-                    if (it != mapQuorumDataRequests.end() && !it->second.IsExpired()) {
+                    if (it != mapQuorumDataRequests.end() && !it->second.IsExpired(/*add_bias=*/true)) {
                         printLog("Already asked");
                         continue;
                     }
diff --git a/src/llmq/quorums.h b/src/llmq/quorums.h
index 5e6d12dd8c06..6e24d369d1fd 100644
--- a/src/llmq/quorums.h
+++ b/src/llmq/quorums.h
@@ -83,6 +83,7 @@ class CQuorumDataRequest
     bool fProcessed{false};
 
     static constexpr int64_t EXPIRATION_TIMEOUT{300};
+    static constexpr int64_t EXPIRATION_BIAS{60};
 
 public:
 
@@ -119,7 +120,7 @@ class CQuorumDataRequest
     Errors GetError() const { return nError; }
     std::string GetErrorString() const;
 
-    bool IsExpired() const { return (GetTime() - nTime) >= EXPIRATION_TIMEOUT; }
+    bool IsExpired(bool add_bias) const { return (GetTime() - nTime) >= (EXPIRATION_TIMEOUT + (add_bias ? EXPIRATION_BIAS : 0)); }
     bool IsProcessed() const { return fProcessed; }
     void SetProcessed() { fProcessed = true; }
 

From 6e00fd06059dfb40a3313dc06c62a26ae6beb1cf Mon Sep 17 00:00:00 2001
From: UdjinM6 <UdjinM6@users.noreply.github.com>
Date: Thu, 30 Mar 2023 19:45:02 +0300
Subject: [PATCH 18/35] fix(test): bump
 `quorum_data_request_expiration_timeout` to fix `p2p_quorum_data.py` (#5281)

## Issue being fixed or feature implemented
fix `p2p_quorum_data.py` test broken by #5276


## What was done?
adjust data request expiration timeout in tests

## How Has This Been Tested?
`./test/functional/test_runner.py p2p_quorum_data.py`

## Breaking Changes
n/a

## Checklist:
- [x] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [x] I have added or updated relevant unit/integration/functional/e2e
tests
- [ ] I have made corresponding changes to the documentation

**For repository code-owners and collaborators only**
- [x] I have assigned this pull request to a milestone
---
 test/functional/test_framework/test_framework.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 66d5745ab40c..abff8e31ee24 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -1000,8 +1000,8 @@ def set_dash_test_params(self, num_nodes, masterodes_count, extra_args=None, fas
 
         # This is nRequestTimeout in dash-q-recovery thread
         self.quorum_data_thread_request_timeout_seconds = 10
-        # This is EXPIRATION_TIMEOUT in CQuorumDataRequest
-        self.quorum_data_request_expiration_timeout = 300
+        # This is EXPIRATION_TIMEOUT + EXPIRATION_BIAS in CQuorumDataRequest
+        self.quorum_data_request_expiration_timeout = 360
 
     def set_dash_dip8_activation(self, activate_after_block):
         self.dip8_activation_height = activate_after_block

From 6947dfbda6a72a9b3d4bcb651f28452e75870f9d Mon Sep 17 00:00:00 2001
From: MarcoFalke <falke.marco@gmail.com>
Date: Sat, 29 Aug 2020 10:03:41 +0200
Subject: [PATCH 19/35] Merge #18817: doc: Document differences in bitcoind and
 bitcoin-qt locale handling

ca185cf5a14b16d61814d7172284bc8efcd28b69 doc: Document differences in bitcoind and bitcoin-qt locale handling (practicalswift)

Pull request description:

  Document differences in `bitcoind` and `bitcoin-qt` locale handling.

  Since this seems to be the root cause to the locale dependency issues we've seen over the years I thought it was worth documenting :)

  Note that 1.) `QLocale` (used by Qt), 2.) C locale (used by locale-sensitive C standard library functions/POSIX functions and some parts of the C++ standard library such as `std::to_string`) and 3.) C++ locale (used by the C++ input/output library) are three separate things. This comment is about the perhaps surprising interference with the C locale (2) that takes place as part of the Qt initialization.

ACKs for top commit:
  hebasto:
    re-ACK ca185cf5a14b16d61814d7172284bc8efcd28b69

Tree-SHA512: e51c32f3072c506b0029a001d8b108125e1acb4f2b6a48a6be721ddadda9da0ae77a9b39ff33f9d9eebabe2244c1db09e8502e3e7012d7a5d40d98e96da0dc44
---
 src/qt/bitcoin.cpp                  |  1 +
 test/lint/lint-locale-dependence.sh | 33 +++++++++++++++++++++++++++++
 2 files changed, 34 insertions(+)

diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 4ee2d11fb03d..fc3c246c2960 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -206,6 +206,7 @@ BitcoinApplication::BitcoinApplication(interfaces::Node& node):
     pollShutdownTimer(nullptr),
     returnValue(0)
 {
+    // Qt runs setlocale(LC_ALL, "") on initialization.
     setQuitOnLastWindowClosed(false);
 }
 
diff --git a/test/lint/lint-locale-dependence.sh b/test/lint/lint-locale-dependence.sh
index 7020c43917d7..3f1ef8daf1b5 100755
--- a/test/lint/lint-locale-dependence.sh
+++ b/test/lint/lint-locale-dependence.sh
@@ -8,6 +8,39 @@ export LC_ALL=C
 # TODO: Reduce KNOWN_VIOLATIONS by replacing uses of locale dependent stoul/strtol with locale
 #       independent ToIntegral<T>(...).
 # TODO: Reduce KNOWN_VIOLATIONS by replacing uses of locale dependent snprintf with strprintf.
+
+# Be aware that bitcoind and bitcoin-qt differ in terms of localization: Qt
+# opts in to POSIX localization by running setlocale(LC_ALL, "") on startup,
+# whereas no such call is made in bitcoind.
+#
+# Qt runs setlocale(LC_ALL, "") on initialization. This installs the locale
+# specified by the user's LC_ALL (or LC_*) environment variable as the new
+# C locale.
+#
+# In contrast, bitcoind does not opt in to localization -- no call to
+# setlocale(LC_ALL, "") is made and the environment variables LC_* are
+# thus ignored.
+#
+# This results in situations where bitcoind is guaranteed to be running
+# with the classic locale ("C") whereas the locale of bitcoin-qt will vary
+# depending on the user's environment variables.
+#
+# An example: Assuming the environment variable LC_ALL=de_DE then the
+# call std::to_string(1.23) will return "1.230000" in bitcoind but
+# "1,230000" in bitcoin-qt.
+#
+# From the Qt documentation:
+# "On Unix/Linux Qt is configured to use the system locale settings by default.
+#  This can cause a conflict when using POSIX functions, for instance, when
+#  converting between data types such as floats and strings, since the notation
+#  may differ between locales. To get around this problem, call the POSIX function
+#  setlocale(LC_NUMERIC,"C") right after initializing QApplication, QGuiApplication
+#  or QCoreApplication to reset the locale that is used for number formatting to
+#  "C"-locale."
+#
+# See https://doc.qt.io/qt-5/qcoreapplication.html#locale-settings and
+# https://stackoverflow.com/a/34878283 for more details.
+
 KNOWN_VIOLATIONS=(
     "src/bitcoin-tx.cpp.*stoul"
     "src/dbwrapper.cpp.*stoul"

From b35557be971ebcd45bf1941d1fe3f8c1d845ef8b Mon Sep 17 00:00:00 2001
From: MarcoFalke <falke.marco@gmail.com>
Date: Sat, 16 May 2020 06:16:45 -0400
Subject: [PATCH 20/35] Merge #18952: test: avoid os-dependant path

8a22fd01140bd957036fc00419b147e4268ae9b1 avoided os-dependant path (Ferdinando M. Ametrano)

Pull request description:

  The current code fails on windows because of the forward slashes; using os.path.join solves the problem and it is in general more robust

ACKs for top commit:
  MarcoFalke:
    ACK 8a22fd01140bd957036fc00419b147e4268ae9b1

Tree-SHA512: 813f27aea33f97c8afac52e716a55fc5d7fb69621023aba99d40df7e1d145e0ec8d1eee49ddd403b219bf0e0e168e0e987b05c78eaef611f744d99bf2fc8bc91
---
 test/functional/test_framework/test_framework.py | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index abff8e31ee24..799142a619fa 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -238,8 +238,18 @@ def setup(self):
 
         config = self.config
 
-        self.options.bitcoind = os.getenv("BITCOIND", default=config["environment"]["BUILDDIR"] + '/src/dashd' + config["environment"]["EXEEXT"])
-        self.options.bitcoincli = os.getenv("BITCOINCLI", default=config["environment"]["BUILDDIR"] + '/src/dash-cli' + config["environment"]["EXEEXT"])
+        fname_bitcoind = os.path.join(
+            config["environment"]["BUILDDIR"],
+            "src",
+            "dashd" + config["environment"]["EXEEXT"]
+        )
+        fname_bitcoincli = os.path.join(
+            config["environment"]["BUILDDIR"],
+            "src",
+            "dash-cli" + config["environment"]["EXEEXT"]
+        )
+        self.options.bitcoind = os.getenv("BITCOIND", default=fname_bitcoind)
+        self.options.bitcoincli = os.getenv("BITCOINCLI", default=fname_bitcoincli)
 
         self.extra_args_from_options = self.options.dashd_extra_args
 

From 79a5b197b0b3fa01c69d671e1d54fe824f7c20d7 Mon Sep 17 00:00:00 2001
From: UdjinM6 <UdjinM6@users.noreply.github.com>
Date: Tue, 4 Apr 2023 19:07:00 +0300
Subject: [PATCH 21/35] refactor/fix: replace expired requests with a new one
 in RequestQuorumData (#5286)

## Issue being fixed or feature implemented
should fix "qdata: Already received" discouraging issue

the root of the issue is that we remove expired requests on
UpdatedBlockTip which is too late sometimes.

## What was done?
replacing expired requests with a new one in RequestQuorumData kind of
does the same (drops the expired request) but without waiting for
UpdatedBlockTip

## How Has This Been Tested?


## Breaking Changes


## Checklist:
- [x] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have added or updated relevant unit/integration/functional/e2e
tests
- [ ] I have made corresponding changes to the documentation

**For repository code-owners and collaborators only**
- [ ] I have assigned this pull request to a milestone
---
 src/llmq/quorums.cpp | 45 +++++++++++++++-----------------------------
 src/llmq/quorums.h   | 14 ++++++++++----
 2 files changed, 25 insertions(+), 34 deletions(-)

diff --git a/src/llmq/quorums.cpp b/src/llmq/quorums.cpp
index 522e60d0a1d9..b3a8998258b7 100644
--- a/src/llmq/quorums.cpp
+++ b/src/llmq/quorums.cpp
@@ -468,21 +468,22 @@ bool CQuorumManager::RequestQuorumData(CNode* pfrom, Consensus::LLMQType llmqTyp
     }
 
     LOCK(cs_data_requests);
-    CQuorumDataRequestKey key;
-    key.proRegTx = pfrom->GetVerifiedProRegTxHash();
-    key.flag = true;
-    key.quorumHash = pQuorumBaseBlockIndex->GetBlockHash();
-    key.llmqType = llmqType;
-    auto it = mapQuorumDataRequests.emplace(key, CQuorumDataRequest(llmqType, pQuorumBaseBlockIndex->GetBlockHash(), nDataMask, proTxHash));
-    if (!it.second && !it.first->second.IsExpired(/*add_bias=*/true)) {
-        LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- Already requested\n", __func__);
-        return false;
+    const CQuorumDataRequestKey key(pfrom->GetVerifiedProRegTxHash(), true, pQuorumBaseBlockIndex->GetBlockHash(), llmqType);
+    const CQuorumDataRequest request(llmqType, pQuorumBaseBlockIndex->GetBlockHash(), nDataMask, proTxHash);
+    auto [old_pair, exists] = mapQuorumDataRequests.emplace(key, request);
+    if (!exists) {
+        if (old_pair->second.IsExpired(/*add_bias=*/true)) {
+            old_pair->second = request;
+        } else {
+            LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- Already requested\n", __func__);
+            return false;
+        }
     }
     LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- sending QGETDATA quorumHash[%s] llmqType[%d] proRegTx[%s]\n", __func__, key.quorumHash.ToString(),
              ToUnderlying(key.llmqType), key.proRegTx.ToString());
 
     CNetMsgMaker msgMaker(pfrom->GetSendVersion());
-    connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::QGETDATA, it.first->second));
+    connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::QGETDATA, request));
 
     return true;
 }
@@ -656,11 +657,7 @@ void CQuorumManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, C
         bool request_limit_exceeded(false);
         {
             LOCK2(cs_main, cs_data_requests);
-            CQuorumDataRequestKey key;
-            key.proRegTx = pfrom.GetVerifiedProRegTxHash();
-            key.flag = false;
-            key.quorumHash = request.GetQuorumHash();
-            key.llmqType = request.GetLLMQType();
+            const CQuorumDataRequestKey key(pfrom.GetVerifiedProRegTxHash(), false, request.GetQuorumHash(), request.GetLLMQType());
             auto it = mapQuorumDataRequests.find(key);
             if (it == mapQuorumDataRequests.end()) {
                 it = mapQuorumDataRequests.emplace(key, request).first;
@@ -733,11 +730,7 @@ void CQuorumManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, C
 
         {
             LOCK2(cs_main, cs_data_requests);
-            CQuorumDataRequestKey key;
-            key.proRegTx = pfrom.GetVerifiedProRegTxHash();
-            key.flag = true;
-            key.quorumHash = request.GetQuorumHash();
-            key.llmqType = request.GetLLMQType();
+            const CQuorumDataRequestKey key(pfrom.GetVerifiedProRegTxHash(), true, request.GetQuorumHash(), request.GetLLMQType());
             auto it = mapQuorumDataRequests.find(key);
             if (it == mapQuorumDataRequests.end()) {
                 errorHandler("Not requested");
@@ -913,11 +906,7 @@ void CQuorumManager::StartQuorumDataRecoveryThread(const CQuorumCPtr pQuorum, co
                 pCurrentMemberHash = &vecMemberHashes[(nMyStartOffset + nTries++) % vecMemberHashes.size()];
                 {
                     LOCK(cs_data_requests);
-                    CQuorumDataRequestKey key;
-                    key.proRegTx = *pCurrentMemberHash;
-                    key.flag = true;
-                    key.quorumHash = pQuorum->qc->quorumHash;
-                    key.llmqType = pQuorum->qc->llmqType;
+                    const CQuorumDataRequestKey key(*pCurrentMemberHash, true, pQuorum->qc->quorumHash, pQuorum->qc->llmqType);
                     auto it = mapQuorumDataRequests.find(key);
                     if (it != mapQuorumDataRequests.end() && !it->second.IsExpired(/*add_bias=*/true)) {
                         printLog("Already asked");
@@ -943,11 +932,7 @@ void CQuorumManager::StartQuorumDataRecoveryThread(const CQuorumCPtr pQuorum, co
                     printLog("Requested");
                 } else {
                     LOCK(cs_data_requests);
-                    CQuorumDataRequestKey key;
-                    key.proRegTx = *pCurrentMemberHash;
-                    key.flag = true;
-                    key.quorumHash = pQuorum->qc->quorumHash;
-                    key.llmqType = pQuorum->qc->llmqType;
+                    const CQuorumDataRequestKey key(*pCurrentMemberHash, true, pQuorum->qc->quorumHash, pQuorum->qc->llmqType);
                     auto it = mapQuorumDataRequests.find(key);
                     if (it == mapQuorumDataRequests.end()) {
                         printLog("Failed");
diff --git a/src/llmq/quorums.h b/src/llmq/quorums.h
index 6e24d369d1fd..c254393322a9 100644
--- a/src/llmq/quorums.h
+++ b/src/llmq/quorums.h
@@ -39,14 +39,20 @@ static constexpr bool DEFAULT_WATCH_QUORUMS{false};
 struct CQuorumDataRequestKey
 {
     uint256 proRegTx;
-    //TODO: Investigate purpose of this flag and rename accordingly
-    bool flag;
+    bool m_we_requested;
     uint256 quorumHash;
     Consensus::LLMQType llmqType;
 
+    CQuorumDataRequestKey(const uint256& proRegTxIn, const bool _m_we_requested, const uint256& quorumHashIn, const Consensus::LLMQType llmqTypeIn) :
+        proRegTx(proRegTxIn),
+        m_we_requested(_m_we_requested),
+        quorumHash(quorumHashIn),
+        llmqType(llmqTypeIn)
+        {}
+
     bool operator ==(const CQuorumDataRequestKey& obj) const
     {
-        return (proRegTx == obj.proRegTx && flag == obj.flag && quorumHash == obj.quorumHash && llmqType == obj.llmqType);
+        return (proRegTx == obj.proRegTx && m_we_requested == obj.m_we_requested && quorumHash == obj.quorumHash && llmqType == obj.llmqType);
     }
 };
 
@@ -275,7 +281,7 @@ struct SaltedHasherImpl<llmq::CQuorumDataRequestKey>
     {
         CSipHasher c(k0, k1);
         c.Write((unsigned char*)&(v.proRegTx), sizeof(v.proRegTx));
-        c.Write((unsigned char*)&(v.flag), sizeof(v.flag));
+        c.Write((unsigned char*)&(v.m_we_requested), sizeof(v.m_we_requested));
         c.Write((unsigned char*)&(v.quorumHash), sizeof(v.quorumHash));
         c.Write((unsigned char*)&(v.llmqType), sizeof(v.llmqType));
         return c.Finalize();

From 15a9783e52ec379be10439706cc2e8184b583745 Mon Sep 17 00:00:00 2001
From: UdjinM6 <UdjinM6@users.noreply.github.com>
Date: Tue, 4 Apr 2023 19:11:59 +0300
Subject: [PATCH 22/35] trivial(doc): fix typos in getrawtransaction and
 decoderawtransaction help texts (#5290)

## Issue being fixed or feature implemented
fix typos in getrawtransaction and decoderawtransaction help texts

## What was done?
tweak field name to match
https://github.com/dashpay/dash/blob/develop/src/core_write.cpp#L192

## How Has This Been Tested?

## Breaking Changes
n/a

## Checklist:
- [x] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have added or updated relevant unit/integration/functional/e2e
tests
- [ ] I have made corresponding changes to the documentation

**For repository code-owners and collaborators only**
- [x] I have assigned this pull request to a milestone
---
 src/rpc/rawtransaction.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 9f90b07b0d47..ff318b9344a8 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -143,7 +143,7 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
                             {RPCResult::Type::STR_HEX, "txid", "The transaction id (same as provided)"},
                             {RPCResult::Type::NUM, "size", "The serialized transaction size"},
                             {RPCResult::Type::NUM, "version", "The version"},
-                            {RPCResult::Type::NUM, "version", "The type"},
+                            {RPCResult::Type::NUM, "type", "The type"},
                             {RPCResult::Type::NUM_TIME, "locktime", "The lock time"},
                             {RPCResult::Type::ARR, "vin", "",
                             {
@@ -494,7 +494,7 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request)
                         {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
                         {RPCResult::Type::NUM, "size", "The transaction size"},
                         {RPCResult::Type::NUM, "version", "The version"},
-                        {RPCResult::Type::NUM, "version", "The type"},
+                        {RPCResult::Type::NUM, "type", "The type"},
                         {RPCResult::Type::NUM_TIME, "locktime", "The lock time"},
                         {RPCResult::Type::ARR, "vin", "",
                         {

From ce3e8072a74ab468e6ac988fff3f997ff9462c3c Mon Sep 17 00:00:00 2001
From: Konstantin Akimov <knstqq@gmail.com>
Date: Mon, 2 Sep 2019 15:36:57 +0800
Subject: [PATCH 23/35] merge #13868: Remove unused fScriptChecks parameter
 from CheckInputs

---
 src/test/txvalidationcache_tests.cpp |  16 +--
 src/validation.cpp                   | 169 +++++++++++++--------------
 2 files changed, 89 insertions(+), 96 deletions(-)

diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 85db73787b90..3c0747066319 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -12,7 +12,7 @@
 
 #include <boost/test/unit_test.hpp>
 
-bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks);
+bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks);
 
 BOOST_AUTO_TEST_SUITE(txvalidationcache_tests)
 
@@ -118,7 +118,7 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
             // script/interpreter.cpp
             test_flags |= SCRIPT_VERIFY_P2SH;
         }
-        bool ret = CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), true, test_flags, true, add_to_cache, txdata, nullptr);
+        bool ret = CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, nullptr);
         // CheckInputs should succeed iff test_flags doesn't intersect with
         // failing_flags
         bool expected_return_value = !(test_flags & failing_flags);
@@ -128,13 +128,13 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
         if (ret && add_to_cache) {
             // Check that we get a cache hit if the tx was valid
             std::vector<CScriptCheck> scriptchecks;
-            BOOST_CHECK(CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), true, test_flags, true, add_to_cache, txdata, &scriptchecks));
+            BOOST_CHECK(CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, &scriptchecks));
             BOOST_CHECK(scriptchecks.empty());
         } else {
             // Check that we get script executions to check, if the transaction
             // was invalid, or we didn't add to cache.
             std::vector<CScriptCheck> scriptchecks;
-            BOOST_CHECK(CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), true, test_flags, true, add_to_cache, txdata, &scriptchecks));
+            BOOST_CHECK(CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, &scriptchecks));
             BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size());
         }
     }
@@ -196,13 +196,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
         CValidationState state;
         PrecomputedTransactionData ptd_spend_tx;
 
-        BOOST_CHECK(!CheckInputs(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
+        BOOST_CHECK(!CheckInputs(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
 
         // If we call again asking for scriptchecks (as happens in
         // ConnectBlock), we should add a script check object for this -- we're
         // not caching invalidity (if that changes, delete this test case).
         std::vector<CScriptCheck> scriptchecks;
-        BOOST_CHECK(CheckInputs(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
+        BOOST_CHECK(CheckInputs(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
         BOOST_CHECK_EQUAL(scriptchecks.size(), 1U);
 
         // Test that CheckInputs returns true iff DERSIG-enforcing flags are
@@ -264,7 +264,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
         invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
         CValidationState state;
         PrecomputedTransactionData txdata;
-        BOOST_CHECK(CheckInputs(CTransaction(invalid_with_cltv_tx), state, &::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
+        BOOST_CHECK(CheckInputs(CTransaction(invalid_with_cltv_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
     }
 
     // TEST CHECKSEQUENCEVERIFY
@@ -292,7 +292,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
         invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
         CValidationState state;
         PrecomputedTransactionData txdata;
-        BOOST_CHECK(CheckInputs(CTransaction(invalid_with_csv_tx), state, &::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
+        BOOST_CHECK(CheckInputs(CTransaction(invalid_with_csv_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
     }
 
     // TODO: add tests for remaining script flags
diff --git a/src/validation.cpp b/src/validation.cpp
index 1d4fe13252fe..fc4405d72647 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -201,7 +201,7 @@ std::unique_ptr<CBlockTreeDB> pblocktree;
 // See definition for documentation
 static void FindFilesToPruneManual(ChainstateManager& chainman, std::set<int>& setFilesToPrune, int nManualPruneHeight);
 static void FindFilesToPrune(ChainstateManager& chainman, std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight);
-bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = nullptr);
+bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = nullptr);
 static FILE* OpenUndoFile(const FlatFilePos &pos, bool fReadOnly = false);
 static FlatFileSeq BlockFileSeq();
 static FlatFileSeq UndoFileSeq();
@@ -503,7 +503,7 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt
     }
 
     // Call CheckInputs() to cache signature and script validity against current tip consensus rules.
-    return CheckInputs(tx, state, view, true, flags, /* cacheSigStore = */ true, /* cacheFullSciptStore = */ true, txdata);
+    return CheckInputs(tx, state, view, flags, /* cacheSigStore = */ true, /* cacheFullSciptStore = */ true, txdata);
 }
 
 /**
@@ -731,7 +731,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
         // Check against previous transactions
         // This is done last to help prevent CPU exhaustion denial-of-service attacks.
         PrecomputedTransactionData txdata;
-        if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true, false, txdata)) {
+        if (!CheckInputs(tx, state, view, scriptVerifyFlags, true, false, txdata)) {
             assert(IsTransactionReason(state.GetReason()));
             return false; // state filled in by CheckInputs
         }
@@ -1415,100 +1415,88 @@ void InitScriptExecutionCache() {
  *
  * Non-static (and re-declared) in src/test/txvalidationcache_tests.cpp
  */
-bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
 {
     boost::posix_time::ptime start = boost::posix_time::microsec_clock::local_time();
-    if (!tx.IsCoinBase())
-    {
-        if (pvChecks)
-            pvChecks->reserve(tx.vin.size());
-
-        // The first loop above does all the inexpensive checks.
-        // Only if ALL inputs pass do we perform expensive ECDSA signature checks.
-        // Helps prevent CPU exhaustion attacks.
-
-        // Skip script verification when connecting blocks under the
-        // assumevalid block. Assuming the assumevalid block is valid this
-        // is safe because block merkle hashes are still computed and checked,
-        // Of course, if an assumed valid block is invalid due to false scriptSigs
-        // this optimization would allow an invalid chain to be accepted.
-        if (fScriptChecks) {
-            // First check if script executions have been cached with the same
-            // flags. Note that this assumes that the inputs provided are
-            // correct (ie that the transaction hash which is in tx's prevouts
-            // properly commits to the scriptPubKey in the inputs view of that
-            // transaction).
-            uint256 hashCacheEntry;
-            CSHA256 hasher = g_scriptExecutionCacheHasher;
-            hasher.Write(tx.GetHash().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)) {
-                return true;
-            }
-
-            if (!txdata.m_ready) {
-                txdata.Init(tx, {});
-            }
-
-            for (unsigned int i = 0; i < tx.vin.size(); i++) {
-                const COutPoint &prevout = tx.vin[i].prevout;
-                const Coin& coin = inputs.AccessCoin(prevout);
-                assert(!coin.IsSpent());
-
-                // We very carefully only pass in things to CScriptCheck which
-                // are clearly committed to by tx' witness hash. This provides
-                // a sanity check that our caching is not introducing consensus
-                // failures through additional data in, eg, the coins being
-                // spent being checked as a part of CScriptCheck.
-
-                // Verify signature
-                CScriptCheck check(coin.out, tx, i, flags, cacheSigStore, &txdata);
-                if (pvChecks) {
-                    pvChecks->push_back(CScriptCheck());
-                    check.swap(pvChecks->back());
-                } else if (!check()) {
-                    const bool hasNonMandatoryFlags = (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) != 0;
-                    const bool hasDIP0020Opcodes = (flags & SCRIPT_ENABLE_DIP0020_OPCODES) != 0;
-
-                    if (hasNonMandatoryFlags || !hasDIP0020Opcodes) {
-                        // Check whether the failure was caused by a
-                        // non-mandatory script verification check, such as
-                        // non-standard DER encodings or non-null dummy
-                        // arguments; if so, ensure we return NOT_STANDARD
-                        // instead of CONSENSUS to avoid downstream users
-                        // splitting the network between upgraded and
-                        // non-upgraded nodes by banning CONSENSUS-failing
-                        // data providers.
-                        CScriptCheck check2(coin.out, tx, i,
-                                (flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS) | SCRIPT_ENABLE_DIP0020_OPCODES, cacheSigStore, &txdata);
-                        if (check2())
-                            return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
-                    }
-                    // MANDATORY flag failures correspond to
-                    // ValidationInvalidReason::CONSENSUS. Because CONSENSUS
-                    // failures are the most serious case of validation
-                    // failures, we may need to consider using
-                    // RECENT_CONSENSUS_CHANGE for any script failure that
-                    // could be due to non-upgraded nodes which we may want to
-                    // support, to avoid splitting the network (but this
-                    // depends on the details of how net_processing handles
-                    // such errors).
-                    return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError())));
-                }
-            }
+    if (tx.IsCoinBase()) return true;
+
+    if (pvChecks) {
+        pvChecks->reserve(tx.vin.size());
+    }
+
+    // First check if script executions have been cached with the same
+    // flags. Note that this assumes that the inputs provided are
+    // correct (ie that the transaction hash which is in tx's prevouts
+    // properly commits to the scriptPubKey in the inputs view of that
+    // transaction).
+    uint256 hashCacheEntry;
+    CSHA256 hasher = g_scriptExecutionCacheHasher;
+    hasher.Write(tx.GetHash().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)) {
+        return true;
+    }
 
-            if (cacheFullScriptStore && !pvChecks) {
-                // We executed all of the provided scripts, and were told to
-                // cache the result. Do so now.
-                g_scriptExecutionCache.insert(hashCacheEntry);
+    if (!txdata.m_ready) {
+        txdata.Init(tx, {});
+    }
+
+    for (unsigned int i = 0; i < tx.vin.size(); i++) {
+        const COutPoint &prevout = tx.vin[i].prevout;
+        const Coin& coin = inputs.AccessCoin(prevout);
+        assert(!coin.IsSpent());
+
+        // We very carefully only pass in things to CScriptCheck which
+        // are clearly committed to by tx' witness hash. This provides
+        // a sanity check that our caching is not introducing consensus
+        // failures through additional data in, eg, the coins being
+        // spent being checked as a part of CScriptCheck.
+
+        // Verify signature
+        CScriptCheck check(coin.out, tx, i, flags, cacheSigStore, &txdata);
+        if (pvChecks) {
+            pvChecks->push_back(CScriptCheck());
+            check.swap(pvChecks->back());
+        } else if (!check()) {
+            const bool hasNonMandatoryFlags = (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) != 0;
+            const bool hasDIP0020Opcodes = (flags & SCRIPT_ENABLE_DIP0020_OPCODES) != 0;
+
+            if (hasNonMandatoryFlags || !hasDIP0020Opcodes) {
+                // Check whether the failure was caused by a
+                // non-mandatory script verification check, such as
+                // non-standard DER encodings or non-null dummy
+                // arguments; if so, ensure we return NOT_STANDARD
+                // instead of CONSENSUS to avoid downstream users
+                // splitting the network between upgraded and
+                // non-upgraded nodes by banning CONSENSUS-failing
+                // data providers.
+                CScriptCheck check2(coin.out, tx, i,
+                        (flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS) | SCRIPT_ENABLE_DIP0020_OPCODES, cacheSigStore, &txdata);
+                if (check2())
+                    return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
             }
+            // MANDATORY flag failures correspond to
+            // ValidationInvalidReason::CONSENSUS. Because CONSENSUS
+            // failures are the most serious case of validation
+            // failures, we may need to consider using
+            // RECENT_CONSENSUS_CHANGE for any script failure that
+            // could be due to non-upgraded nodes which we may want to
+            // support, to avoid splitting the network (but this
+            // depends on the details of how net_processing handles
+            // such errors).
+            return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError())));
         }
     }
 
+    if (cacheFullScriptStore && !pvChecks) {
+        // We executed all of the provided scripts, and were told to
+        // cache the result. Do so now.
+        g_scriptExecutionCache.insert(hashCacheEntry);
+    }
+
     boost::posix_time::ptime finish = boost::posix_time::microsec_clock::local_time();
     boost::posix_time::time_duration diff = finish - start;
     statsClient.timing("CheckInputs_ms", diff.total_milliseconds(), 1.0f);
-
     return true;
 }
 
@@ -2055,6 +2043,11 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
                 pindexBestHeader->GetAncestor(pindex->nHeight) == pindex &&
                 pindexBestHeader->nChainWork >= nMinimumChainWork) {
                 // This block is a member of the assumed verified chain and an ancestor of the best header.
+                // Script verification is skipped when connecting blocks under the
+                // assumevalid block. Assuming the assumevalid block is valid this
+                // is safe because block merkle hashes are still computed and checked,
+                // Of course, if an assumed valid block is invalid due to false scriptSigs
+                // this optimization would allow an invalid chain to be accepted.
                 // The equivalent time check discourages hash power from extorting the network via DOS attack
                 //  into accepting an invalid block through telling users they must manually set assumevalid.
                 //  Requiring a software change or burying the invalid block, regardless of the setting, makes
@@ -2252,7 +2245,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
 
             std::vector<CScriptCheck> vChecks;
             bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
-            if (fScriptChecks && !CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, fCacheResults, txsdata[i], g_parallel_script_checks ? &vChecks : nullptr)) {
+            if (fScriptChecks && !CheckInputs(tx, state, view, flags, fCacheResults, fCacheResults, txsdata[i], g_parallel_script_checks ? &vChecks : nullptr)) {
                 if (state.GetReason() == ValidationInvalidReason::TX_NOT_STANDARD) {
                     // CheckInputs may return NOT_STANDARD for extra flags we passed,
                     // but we can't return that, as it's not defined for a block, so

From bd00a6a2e829e9f46da3768abce122f4849ba69d Mon Sep 17 00:00:00 2001
From: Konstantin Akimov <knstqq@gmail.com>
Date: Thu, 16 Mar 2023 08:48:17 +0000
Subject: [PATCH 24/35] merge #16400: refactor: Rewrite
 AcceptToMemoryPoolWorker() using smaller parts

---
 src/validation.cpp | 541 +++++++++++++++++++++++++++++----------------
 1 file changed, 345 insertions(+), 196 deletions(-)

diff --git a/src/validation.cpp b/src/validation.cpp
index fc4405d72647..54470bf5e2bf 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -506,22 +506,124 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt
     return CheckInputs(tx, state, view, flags, /* cacheSigStore = */ true, /* cacheFullSciptStore = */ true, txdata);
 }
 
-/**
- * @param[out] coins_to_uncache   Return any outpoints which were not previously present in the
- *                                coins cache, but were added as a result of validating the tx
- *                                for mempool acceptance. This allows the caller to optionally
- *                                remove the cache additions if the associated transaction ends
- *                                up being rejected by the mempool.
- */
-static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx,
-                                     bool* pfMissingInputs, int64_t nAcceptTime, bool bypass_limits,
-                                     const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+namespace {
+
+class MemPoolAccept
 {
-    boost::posix_time::ptime start = boost::posix_time::microsec_clock::local_time();
-    const CTransaction& tx = *ptx;
-    const uint256 hash = tx.GetHash();
-    AssertLockHeld(cs_main);
-    LOCK(pool.cs); // mempool "read lock" (held through GetMainSignals().TransactionAddedToMempool())
+public:
+    MemPoolAccept(CTxMemPool& mempool) : m_pool(mempool), m_view(&m_dummy), m_viewmempool(&::ChainstateActive().CoinsTip(), m_pool),
+        m_limit_ancestors(gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT)),
+        m_limit_ancestor_size(gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000),
+        m_limit_descendants(gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)),
+        m_limit_descendant_size(gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000) {}
+
+    // We put the arguments we're handed into a struct, so we can pass them
+    // around easier.
+    struct ATMPArgs {
+        const CChainParams& m_chainparams;
+        CValidationState &m_state;
+        bool* m_missing_inputs;
+        const int64_t m_accept_time;
+        const bool m_bypass_limits;
+        const CAmount& m_absurd_fee;
+        /*
+         * Return any outpoints which were not previously present in the coins
+         * cache, but were added as a result of validating the tx for mempool
+         * acceptance. This allows the caller to optionally remove the cache
+         * additions if the associated transaction ends up being rejected by
+         * the mempool.
+         */
+        std::vector<COutPoint>& m_coins_to_uncache;
+        const bool m_test_accept;
+    };
+
+    // Single transaction acceptance
+    bool AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+private:
+    // All the intermediate state that gets passed between the various levels
+    // of checking a given transaction.
+    struct Workspace {
+        Workspace(const CTransactionRef& ptx) : m_ptx(ptx), m_hash(ptx->GetHash()) {}
+        CTxMemPool::setEntries m_ancestors;
+        std::unique_ptr<CTxMemPoolEntry> m_entry;
+
+        CAmount m_modified_fees;
+
+        const CTransactionRef& m_ptx;
+        const uint256& m_hash;
+    };
+
+    // Run the policy checks on a given transaction, excluding any script checks.
+    // Looks up inputs, calculates feerate, considers replacement, evaluates
+    // package limits, etc. As this function can be invoked for "free" by a peer,
+    // only tests that are fast should be done here (to avoid CPU DoS).
+    bool PreChecks(ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
+
+    // Run the script checks using our policy flags. As this can be slow, we should
+    // only invoke this on transactions that have otherwise passed policy checks.
+    bool PolicyScriptChecks(ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+    // Re-run the script checks, using consensus flags, and try to cache the
+    // result in the scriptcache. This should be done after
+    // PolicyScriptChecks(). This requires that all inputs either be in our
+    // utxo set or in the mempool.
+    bool ConsensusScriptChecks(ATMPArgs& args, Workspace& ws, PrecomputedTransactionData &txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+    // Try to add the transaction to the mempool, removing any conflicts first.
+    // Returns true if the transaction is in the mempool after any size
+    // limiting is performed, false otherwise.
+    bool Finalize(ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
+
+    // Compare a package's feerate against minimum allowed.
+    bool CheckFeeRate(size_t package_size, CAmount package_fee, CValidationState& state)
+    {
+        CAmount mempoolRejectFee = m_pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(package_size);
+        if (mempoolRejectFee > 0 && package_fee < mempoolRejectFee) {
+            return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", strprintf("%d < %d", package_fee, mempoolRejectFee));
+        }
+
+        if (package_fee < ::minRelayTxFee.GetFee(package_size)) {
+            return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "min relay fee not met", strprintf("%d < %d", package_fee, ::minRelayTxFee.GetFee(package_size)));
+        }
+        return true;
+    }
+
+private:
+    CTxMemPool& m_pool;
+    CCoinsViewCache m_view;
+    CCoinsViewMemPool m_viewmempool;
+    CCoinsView m_dummy;
+
+    // The package limits in effect at the time of invocation.
+    const size_t m_limit_ancestors;
+    const size_t m_limit_ancestor_size;
+    // These may be modified while evaluating a transaction (eg to account for
+    // in-mempool conflicts; see below).
+    size_t m_limit_descendants;
+    size_t m_limit_descendant_size;
+};
+
+bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
+{
+    const CTransactionRef& ptx = ws.m_ptx;
+    const CTransaction& tx = *ws.m_ptx;
+    const uint256& hash = ws.m_hash;
+
+    // Copy/alias what we need out of args
+    const CChainParams& chainparams = args.m_chainparams;
+    CValidationState &state = args.m_state;
+    bool* pfMissingInputs = args.m_missing_inputs;
+    const int64_t nAcceptTime = args.m_accept_time;
+    const bool bypass_limits = args.m_bypass_limits;
+    const CAmount& nAbsurdFee = args.m_absurd_fee;
+    std::vector<COutPoint>& coins_to_uncache = args.m_coins_to_uncache;
+
+    // Alias what we need out of ws
+    CTxMemPool::setEntries& setAncestors = ws.m_ancestors;
+    std::unique_ptr<CTxMemPoolEntry>& entry = ws.m_entry;
+    CAmount& nModifiedFees = ws.m_modified_fees;
+
     if (pfMissingInputs) {
         *pfMissingInputs = false;
     }
@@ -560,7 +662,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
         return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, REJECT_NONSTANDARD, "non-final");
 
     // is it already in the memory pool?
-    if (pool.exists(hash)) {
+    if (m_pool.exists(hash)) {
         statsClient.inc("transactions.duplicate", 1.0f);
         return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-already-in-mempool");
     }
@@ -568,7 +670,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
     llmq::CInstantSendLockPtr conflictLock = llmq::quorumInstantSendManager->GetConflictingLock(tx);
     if (conflictLock) {
         uint256 hashBlock;
-        CTransactionRef txConflict = GetTransaction(/* block_index */ nullptr, &pool, conflictLock->txid, chainparams.GetConsensus(), hashBlock);
+        CTransactionRef txConflict = GetTransaction(/* block_index */ nullptr, &m_pool, conflictLock->txid, chainparams.GetConsensus(), hashBlock);
         if (txConflict) {
             GetMainSignals().NotifyInstantSendDoubleSpendAttempt(ptx, txConflict);
         }
@@ -576,13 +678,13 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
     }
 
     if (llmq::quorumInstantSendManager->IsWaitingForTx(hash)) {
-        pool.removeConflicts(tx);
-        pool.removeProTxConflicts(tx);
+        m_pool.removeConflicts(tx);
+        m_pool.removeProTxConflicts(tx);
     } else {
         // Check for conflicts with in-memory transactions
         for (const CTxIn &txin : tx.vin)
         {
-            const CTransaction* ptxConflicting = pool.GetConflictTx(txin.prevout);
+            const CTransaction* ptxConflicting = m_pool.GetConflictTx(txin.prevout);
             if (ptxConflicting)
             {
                 // Transaction conflicts with mempool and RBF doesn't exist in Dash
@@ -591,212 +693,255 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
         }
     }
 
-    {
-        CCoinsView dummy;
-        CCoinsViewCache view(&dummy);
-
-        LockPoints lp;
-        CCoinsViewCache& coins_cache = ::ChainstateActive().CoinsTip();
-        CCoinsViewMemPool viewMemPool(&coins_cache, pool);
-        view.SetBackend(viewMemPool);
-
-        // do all inputs exist?
-        for (const CTxIn& txin : tx.vin) {
-            if (!coins_cache.HaveCoinInCache(txin.prevout)) {
-                coins_to_uncache.push_back(txin.prevout);
-            }
+    LockPoints lp;
+    m_view.SetBackend(m_viewmempool);
 
-            // Note: this call may add txin.prevout to the coins cache
-            // (pcoinsTip.cacheCoins) by way of FetchCoin(). It should be removed
-            // later (via coins_to_uncache) if this tx turns out to be invalid.
-            if (!view.HaveCoin(txin.prevout)) {
-                // Are inputs missing because we already have the tx?
-                for (size_t out = 0; out < tx.vout.size(); out++) {
-                    // Optimistically just do efficient check of cache for outputs
-                    if (coins_cache.HaveCoinInCache(COutPoint(hash, out))) {
-                        return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-already-known");
-                    }
-                }
-                // Otherwise assume this might be an orphan tx for which we just haven't seen parents yet
-                if (pfMissingInputs) {
-                    *pfMissingInputs = true;
+    CCoinsViewCache& coins_cache = ::ChainstateActive().CoinsTip();
+    // do all inputs exist?
+    for (const CTxIn& txin : tx.vin) {
+        if (!coins_cache.HaveCoinInCache(txin.prevout)) {
+            coins_to_uncache.push_back(txin.prevout);
+        }
+
+        // Note: this call may add txin.prevout to the coins cache
+        // (coins_cache.cacheCoins) by way of FetchCoin(). It should be removed
+        // later (via coins_to_uncache) if this tx turns out to be invalid.
+        if (!m_view.HaveCoin(txin.prevout)) {
+            // Are inputs missing because we already have the tx?
+            for (size_t out = 0; out < tx.vout.size(); out++) {
+                // Optimistically just do efficient check of cache for outputs
+                if (coins_cache.HaveCoinInCache(COutPoint(hash, out))) {
+                    return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-already-known");
                 }
-                return false; // fMissingInputs and !state.IsInvalid() is used to detect this condition, don't set state.Invalid()
             }
+            // Otherwise assume this might be an orphan tx for which we just haven't seen parents yet
+            if (pfMissingInputs) {
+                *pfMissingInputs = true;
+            }
+            return false; // fMissingInputs and !state.IsInvalid() is used to detect this condition, don't set state.Invalid()
         }
+    }
 
-        // Bring the best block into scope
-        view.GetBestBlock();
+    // Bring the best block into scope
+    m_view.GetBestBlock();
 
-        // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool
-        view.SetBackend(dummy);
+    // we have all inputs cached now, so switch back to dummy (to protect
+    // against bugs where we pull more inputs from disk that miss being added
+    // to coins_to_uncache)
+    m_view.SetBackend(m_dummy);
 
-        // Only accept BIP68 sequence locked transactions that can be mined in the next
-        // block; we don't want our mempool filled up with transactions that can't
-        // be mined yet.
-        // Must keep pool.cs for this unless we change CheckSequenceLocks to take a
-        // CoinsViewCache instead of create its own
-        if (!CheckSequenceLocks(pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
-            return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, REJECT_NONSTANDARD, "non-BIP68-final");
+    // Only accept BIP68 sequence locked transactions that can be mined in the next
+    // block; we don't want our mempool filled up with transactions that can't
+    // be mined yet.
+    // Must keep pool.cs for this unless we change CheckSequenceLocks to take a
+    // CoinsViewCache instead of create its own
+    if (!CheckSequenceLocks(m_pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
+        return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, REJECT_NONSTANDARD, "non-BIP68-final");
 
-        CAmount nFees = 0;
-        if (!Consensus::CheckTxInputs(tx, state, view, GetSpendHeight(view), nFees)) {
-            return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state));
-        }
+    CAmount nFees = 0;
+    if (!Consensus::CheckTxInputs(tx, state, m_view, GetSpendHeight(m_view), nFees)) {
+        return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state));
+    }
 
-        // Check for non-standard pay-to-script-hash in inputs
-        if (fRequireStandard && !AreInputsStandard(tx, view))
-            return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs");
+    // Check for non-standard pay-to-script-hash in inputs
+    if (fRequireStandard && !AreInputsStandard(tx, m_view))
+        return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs");
 
-        unsigned int nSigOps = GetTransactionSigOpCount(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS);
+    unsigned int nSigOps = GetTransactionSigOpCount(tx, m_view, STANDARD_SCRIPT_VERIFY_FLAGS);
 
-        // nModifiedFees includes any fee deltas from PrioritiseTransaction
-        CAmount nModifiedFees = nFees;
-        pool.ApplyDelta(hash, nModifiedFees);
+    // nModifiedFees includes any fee deltas from PrioritiseTransaction
+    nModifiedFees = nFees;
+    m_pool.ApplyDelta(hash, nModifiedFees);
 
-        // Keep track of transactions that spend a coinbase, which we re-scan
-        // during reorgs to ensure COINBASE_MATURITY is still met.
-        bool fSpendsCoinbase = false;
-        for (const CTxIn &txin : tx.vin) {
-            const Coin &coin = view.AccessCoin(txin.prevout);
-            if (coin.IsCoinBase()) {
-                fSpendsCoinbase = true;
-                break;
-            }
+    // Keep track of transactions that spend a coinbase, which we re-scan
+    // during reorgs to ensure COINBASE_MATURITY is still met.
+    bool fSpendsCoinbase = false;
+    for (const CTxIn &txin : tx.vin) {
+        const Coin &coin = m_view.AccessCoin(txin.prevout);
+        if (coin.IsCoinBase()) {
+            fSpendsCoinbase = true;
+            break;
         }
+    }
 
-        CTxMemPoolEntry entry(ptx, nFees, nAcceptTime, ::ChainActive().Height(),
-                              fSpendsCoinbase, nSigOps, lp);
-        unsigned int nSize = entry.GetTxSize();
+    entry.reset(new CTxMemPoolEntry(ptx, nFees, nAcceptTime, ::ChainActive().Height(),
+            fSpendsCoinbase, nSigOps, lp));
+    unsigned int nSize = entry->GetTxSize();
 
-        if (nSigOps > MAX_STANDARD_TX_SIGOPS)
-            return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops",
+    if (nSigOps > MAX_STANDARD_TX_SIGOPS)
+        return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops",
                 strprintf("%d", nSigOps));
 
-        CAmount mempoolRejectFee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize);
-        if (!bypass_limits && mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) {
-            return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", strprintf("%d < %d", nModifiedFees, mempoolRejectFee));
-        }
-
-        // No transactions are allowed below minRelayTxFee except from disconnected blocks
-        if (!bypass_limits && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) {
-            return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "min relay fee not met", strprintf("%d < %d", nModifiedFees, ::minRelayTxFee.GetFee(nSize)));
-        }
+    // No transactions are allowed below minRelayTxFee except from disconnected
+    // blocks
+    if (!bypass_limits && !CheckFeeRate(nSize, nModifiedFees, state)) return false;
 
-        if (nAbsurdFee && nFees > nAbsurdFee)
-            return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false,
+    if (nAbsurdFee && nFees > nAbsurdFee)
+        return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false,
                 REJECT_HIGHFEE, "absurdly-high-fee",
                 strprintf("%d > %d", nFees, nAbsurdFee));
 
-        // Calculate in-mempool ancestors, up to a limit.
-        CTxMemPool::setEntries setAncestors;
-        size_t nLimitAncestors = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
-        size_t nLimitAncestorSize = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000;
-        size_t nLimitDescendants = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
-        size_t nLimitDescendantSize = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000;
-        std::string errString;
-        if (!pool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) {
-            setAncestors.clear();
-            // If CalculateMemPoolAncestors fails second time, we want the original error string.
-            std::string dummy_err_string;
-            // If the new transaction is relatively small (up to 40k weight)
-            // and has at most one ancestor (ie ancestor limit of 2, including
-            // the new transaction), allow it if its parent has exactly the
-            // descendant limit descendants.
-            //
-            // This allows protocols which rely on distrusting counterparties
-            // being able to broadcast descendants of an unconfirmed transaction
-            // to be secure by simply only having two immediately-spendable
-            // outputs - one for each counterparty. For more info on the uses for
-            // this, see https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-November/016518.html
-            if (nSize >  EXTRA_DESCENDANT_TX_SIZE_LIMIT ||
-                    !pool.CalculateMemPoolAncestors(entry, setAncestors, 2, nLimitAncestorSize, nLimitDescendants + 1, nLimitDescendantSize + EXTRA_DESCENDANT_TX_SIZE_LIMIT, dummy_err_string)) {
-                return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "too-long-mempool-chain", errString);
-            }
-        }
+    // Calculate in-mempool ancestors, up to a limit.
+    std::string errString;
+    if (!m_pool.CalculateMemPoolAncestors(*entry, setAncestors, m_limit_ancestors, m_limit_ancestor_size, m_limit_descendants, m_limit_descendant_size, errString)) {
+        setAncestors.clear();
+        // If CalculateMemPoolAncestors fails second time, we want the original error string.
+        std::string dummy_err_string;
+        // If the new transaction is relatively small (up to 40k weight)
+        // and has at most one ancestor (ie ancestor limit of 2, including
+        // the new transaction), allow it if its parent has exactly the
+        // descendant limit descendants.
+        //
+        // This allows protocols which rely on distrusting counterparties
+        // being able to broadcast descendants of an unconfirmed transaction
+        // to be secure by simply only having two immediately-spendable
+        // outputs - one for each counterparty. For more info on the uses for
+        // this, see https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-November/016518.html
+        if (nSize >  EXTRA_DESCENDANT_TX_SIZE_LIMIT ||
+                !m_pool.CalculateMemPoolAncestors(*entry, setAncestors, 2, m_limit_ancestor_size, m_limit_descendants + 1, m_limit_descendant_size + EXTRA_DESCENDANT_TX_SIZE_LIMIT, dummy_err_string)) {
+            return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "too-long-mempool-chain", errString);
+        }
+    }
+
+    // check special TXs after all the other checks. If we'd do this before the other checks, we might end up
+    // DoS scoring a node for non-critical errors, e.g. duplicate keys because a TX is received that was already
+    // mined
+    // NOTE: we use UTXO here and do NOT allow mempool txes as masternode collaterals
+    if (!CheckSpecialTx(tx, ::ChainActive().Tip(), state, ::ChainstateActive().CoinsTip(), true))
+        return false;
 
-        // check special TXs after all the other checks. If we'd do this before the other checks, we might end up
-        // DoS scoring a node for non-critical errors, e.g. duplicate keys because a TX is received that was already
-        // mined
-        // NOTE: we use UTXO here and do NOT allow mempool txes as masternode collaterals
-        if (!CheckSpecialTx(tx, ::ChainActive().Tip(), state, ::ChainstateActive().CoinsTip(), true))
-            return false;
+    if (m_pool.existsProviderTxConflict(tx)) {
+        return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "protx-dup");
+    }
 
-        if (pool.existsProviderTxConflict(tx)) {
-            return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "protx-dup");
-        }
+    return true;
+}
 
-        constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
+bool MemPoolAccept::PolicyScriptChecks(ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata)
+{
+    const CTransaction& tx = *ws.m_ptx;
 
-        // Check against previous transactions
-        // This is done last to help prevent CPU exhaustion denial-of-service attacks.
-        PrecomputedTransactionData txdata;
-        if (!CheckInputs(tx, state, view, scriptVerifyFlags, true, false, txdata)) {
-            assert(IsTransactionReason(state.GetReason()));
-            return false; // state filled in by CheckInputs
-        }
+    CValidationState &state = args.m_state;
 
-        // Check again against the current block tip's script verification
-        // flags to cache our script execution flags. This is, of course,
-        // useless if the next block has different script flags from the
-        // previous one, but because the cache tracks script flags for us it
-        // will auto-invalidate and we'll just have a few blocks of extra
-        // misses on soft-fork activation.
-        //
-        // This is also useful in case of bugs in the standard flags that cause
-        // transactions to pass as valid when they're actually invalid. For
-        // instance the STRICTENC flag was incorrectly allowing certain
-        // CHECKSIG NOT scripts to pass, even though they were invalid.
-        //
-        // There is a similar check in CreateNewBlock() to prevent creating
-        // invalid blocks (using TestBlockValidity), however allowing such
-        // transactions into the mempool can be exploited as a DoS attack.
-        unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(::ChainActive().Tip(), chainparams.GetConsensus());
-        if (!CheckInputsFromMempoolAndCache(tx, state, view, pool, currentBlockScriptVerifyFlags, txdata)) {
-            return error("%s: BUG! PLEASE REPORT THIS! CheckInputs failed against latest-block but not STANDARD flags %s, %s",
-                    __func__, hash.ToString(), FormatStateMessage(state));
-        }
+    constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
 
-        if (test_accept) {
-            // Tx was accepted, but not added
-            return true;
-        }
+    // Check against previous transactions
+    // This is done last to help prevent CPU exhaustion denial-of-service attacks.
+    if (!CheckInputs(tx, state, m_view, scriptVerifyFlags, true, false, txdata)) {
+        assert(IsTransactionReason(state.GetReason()));
+        return false; // state filled in by CheckInputs
+    }
 
-        // This transaction should only count for fee estimation if:
-        // - it's not being re-added during a reorg which bypasses typical mempool fee limits
-        // - the node is not behind
-        // - the transaction is not dependent on any other transactions in the mempool
-        // - the transaction is not a zero fee transaction
-        bool validForFeeEstimation = (nFees !=0) && !bypass_limits && IsCurrentForFeeEstimation() && pool.HasNoInputsOf(tx);
+    return true;
+}
 
-        // Store transaction in memory
-        pool.addUnchecked(entry, setAncestors, validForFeeEstimation);
-        CAmount nValueOut = tx.GetValueOut();
-        statsClient.count("transactions.sizeBytes", nSize, 1.0f);
-        statsClient.count("transactions.fees", nFees, 1.0f);
-        statsClient.count("transactions.inputValue", nValueOut - nFees, 1.0f);
-        statsClient.count("transactions.outputValue", nValueOut, 1.0f);
-        statsClient.count("transactions.sigOps", nSigOps, 1.0f);
+bool MemPoolAccept::ConsensusScriptChecks(ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata)
+{
+    const CTransaction& tx = *ws.m_ptx;
+    const uint256& hash = ws.m_hash;
 
-        // Add memory address index
-        if (fAddressIndex) {
-            pool.addAddressIndex(entry, view);
-        }
+    CValidationState &state = args.m_state;
+    const CChainParams& chainparams = args.m_chainparams;
 
-        // Add memory spent index
-        if (fSpentIndex) {
-            pool.addSpentIndex(entry, view);
-        }
+    // Check again against the current block tip's script verification
+    // flags to cache our script execution flags. This is, of course,
+    // useless if the next block has different script flags from the
+    // previous one, but because the cache tracks script flags for us it
+    // will auto-invalidate and we'll just have a few blocks of extra
+    // misses on soft-fork activation.
+    //
+    // This is also useful in case of bugs in the standard flags that cause
+    // transactions to pass as valid when they're actually invalid. For
+    // instance the STRICTENC flag was incorrectly allowing certain
+    // CHECKSIG NOT scripts to pass, even though they were invalid.
+    //
+    // There is a similar check in CreateNewBlock() to prevent creating
+    // invalid blocks (using TestBlockValidity), however allowing such
+    // transactions into the mempool can be exploited as a DoS attack.
+    unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(::ChainActive().Tip(), chainparams.GetConsensus());
+    if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags, txdata)) {
+        return error("%s: BUG! PLEASE REPORT THIS! CheckInputs failed against latest-block but not STANDARD flags %s, %s",
+                __func__, hash.ToString(), FormatStateMessage(state));
+    }
 
-        if (!bypass_limits) {
-            LimitMempoolSize(pool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
-            if (!pool.exists(hash))
-                return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool full");
-        }
+    return true;
+}
+bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws)
+{
+    const CTransaction& tx = *ws.m_ptx;
+    const uint256& hash = ws.m_hash;
+    CValidationState &state = args.m_state;
+    const bool bypass_limits = args.m_bypass_limits;
+
+    CTxMemPool::setEntries& setAncestors = ws.m_ancestors;
+    const CAmount& nModifiedFees = ws.m_modified_fees;
+    std::unique_ptr<CTxMemPoolEntry>& entry = ws.m_entry;
+
+    // This transaction should only count for fee estimation if:
+    // - it's not being re-added during a reorg which bypasses typical mempool fee limits
+    // - the node is not behind
+    // - the transaction is not dependent on any other transactions in the mempool
+    // - the transaction is not a zero fee transaction
+    bool validForFeeEstimation = (nModifiedFees != 0) && !bypass_limits && IsCurrentForFeeEstimation() && m_pool.HasNoInputsOf(tx);
+
+    // Store transaction in memory
+    m_pool.addUnchecked(*entry, setAncestors, validForFeeEstimation);
+    CAmount nValueOut = tx.GetValueOut();
+    unsigned int nSigOps = GetTransactionSigOpCount(tx, m_view, STANDARD_SCRIPT_VERIFY_FLAGS);
+
+    statsClient.count("transactions.sizeBytes", entry->GetTxSize(), 1.0f);
+    statsClient.count("transactions.fees", nModifiedFees, 1.0f);
+    statsClient.count("transactions.inputValue", nValueOut - nModifiedFees, 1.0f);
+    statsClient.count("transactions.outputValue", nValueOut, 1.0f);
+    statsClient.count("transactions.sigOps", nSigOps, 1.0f);
+
+    // Add memory address index
+    if (fAddressIndex) {
+        m_pool.addAddressIndex(*entry, m_view);
+    }
+
+    // Add memory spent index
+    if (fSpentIndex) {
+        m_pool.addSpentIndex(*entry, m_view);
     }
 
+    if (!bypass_limits) {
+        LimitMempoolSize(m_pool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
+        if (!m_pool.exists(hash))
+            return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool full");
+    }
+    return true;
+}
+
+bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args)
+{
+    boost::posix_time::ptime start = boost::posix_time::microsec_clock::local_time();
+    AssertLockHeld(cs_main);
+    LOCK(m_pool.cs); // mempool "read lock" (held through GetMainSignals().TransactionAddedToMempool())
+
+    Workspace workspace(ptx);
+
+    if (!PreChecks(args, workspace)) return false;
+
+    // Only compute the precomputed transaction data if we need to verify
+    // scripts (ie, other policy checks pass). We perform the inexpensive
+    // checks first and avoid hashing and signature verification unless those
+    // checks pass, to mitigate CPU exhaustion denial-of-service attacks.
+    PrecomputedTransactionData txdata(*ptx);
+
+    if (!PolicyScriptChecks(args, workspace, txdata)) return false;
+
+    if (!ConsensusScriptChecks(args, workspace, txdata)) return false;
+
+    // Tx was accepted, but not added
+    if (args.m_test_accept) return true;
+
+    if (!Finalize(args, workspace)) return false;
+
+    const int64_t nAcceptTime = args.m_accept_time;
     GetMainSignals().TransactionAddedToMempool(ptx, nAcceptTime);
 
+    const CTransaction& tx = *ptx;
     boost::posix_time::ptime finish = boost::posix_time::microsec_clock::local_time();
     boost::posix_time::time_duration diff = finish - start;
     statsClient.timing("AcceptToMemoryPool_ms", diff.total_milliseconds(), 1.0f);
@@ -807,19 +952,23 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
     return true;
 }
 
+} // anon namespace
+
 /** (try to) add transaction to memory pool with a specified acceptance time **/
 static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx,
                         bool* pfMissingInputs, int64_t nAcceptTime, bool bypass_limits,
                         const CAmount nAbsurdFee, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
 {
     std::vector<COutPoint> coins_to_uncache;
-    bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, pfMissingInputs, nAcceptTime, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept);
+    MemPoolAccept::ATMPArgs args { chainparams, state, pfMissingInputs, nAcceptTime, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept };
+    bool res = MemPoolAccept(pool).AcceptSingleTransaction(tx, args);
     if (!res || test_accept) {
-        if(!res) LogPrint(BCLog::MEMPOOL, "%s: %s %s (%s)\n", __func__, tx->GetHash().ToString(), state.GetRejectReason(), state.GetDebugMessage());
-           // Remove coins that were not present in the coins cache before calling ATMPW;
-           // this is to prevent memory DoS in case we receive a large number of
-           // invalid transactions that attempt to overrun the in-memory coins cache
-           // (`CCoinsViewCache::cacheCoins`).
+        if (!res) LogPrint(BCLog::MEMPOOL, "%s: %s %s (%s)\n", __func__, tx->GetHash().ToString(), state.GetRejectReason(), state.GetDebugMessage());
+
+        // Remove coins that were not present in the coins cache before calling ATMPW;
+        // this is to prevent memory DoS in case we receive a large number of
+        // invalid transactions that attempt to overrun the in-memory coins cache
+        // (`CCoinsViewCache::cacheCoins`).
 
         for (const COutPoint& hashTx : coins_to_uncache)
             ::ChainstateActive().CoinsTip().Uncache(hashTx);

From bce9b6bc97538972fa8643ce5fb384e664f16912 Mon Sep 17 00:00:00 2001
From: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com>
Date: Mon, 7 Sep 2020 07:58:42 +0200
Subject: [PATCH 25/35] merge bitcoin#19905: Remove dead
 CheckForkWarningConditionsOnNewFork

---
 src/validation.cpp | 94 ++++++----------------------------------------
 src/warnings.cpp   | 18 +--------
 src/warnings.h     |  2 -
 3 files changed, 13 insertions(+), 101 deletions(-)

diff --git a/src/validation.cpp b/src/validation.cpp
index 54470bf5e2bf..cdf6ef08714f 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -1340,8 +1340,6 @@ bool CChainState::IsInitialBlockDownload() const
     return false;
 }
 
-static CBlockIndex *pindexBestForkTip = nullptr, *pindexBestForkBase = nullptr;
-
 static void AlertNotify(const std::string& strMessage)
 {
     uiInterface.NotifyAlertChanged();
@@ -1367,80 +1365,16 @@ static void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
     AssertLockHeld(cs_main);
     // Before we get past initial download, we cannot reliably alert about forks
     // (we assume we don't get stuck on a fork before finishing our initial sync)
-    if (::ChainstateActive().IsInitialBlockDownload())
+    if (::ChainstateActive().IsInitialBlockDownload()) {
         return;
-
-    // If our best fork is no longer within 72 blocks (+/- 3 hours if no one mines it)
-    // of our head, drop it
-    if (pindexBestForkTip && ::ChainActive().Height() - pindexBestForkTip->nHeight >= 72)
-        pindexBestForkTip = nullptr;
-
-    if (pindexBestForkTip || (pindexBestInvalid && pindexBestInvalid->nChainWork > ::ChainActive().Tip()->nChainWork + (GetBlockProof(*::ChainActive().Tip()) * 6)))
-    {
-        if (!GetfLargeWorkForkFound() && pindexBestForkBase)
-        {
-            if(pindexBestForkBase->phashBlock){
-                std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") +
-                    pindexBestForkBase->phashBlock->ToString() + std::string("'");
-                AlertNotify(warning);
-            }
-        }
-        if (pindexBestForkTip && pindexBestForkBase)
-        {
-            if(pindexBestForkBase->phashBlock){
-                LogPrintf("%s: Warning: Large valid fork found\n  forking the chain at height %d (%s)\n  lasting to height %d (%s).\nChain state database corruption likely.\n", __func__,
-                       pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString(),
-                       pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString());
-                SetfLargeWorkForkFound(true);
-            }
-        }
-        else
-        {
-            if(pindexBestInvalid->nHeight > ::ChainActive().Height() + 6)
-                LogPrintf("%s: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n", __func__);
-            else
-                LogPrintf("%s: Warning: Found invalid chain which has higher work (at least ~6 blocks worth of work) than our best chain.\nChain state database corruption likely.\n", __func__);
-            SetfLargeWorkInvalidChainFound(true);
-        }
     }
-    else
-    {
-        SetfLargeWorkForkFound(false);
-        SetfLargeWorkInvalidChainFound(false);
-    }
-}
 
-static void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
-{
-    AssertLockHeld(cs_main);
-    // If we are on a fork that is sufficiently large, set a warning flag
-    CBlockIndex* pfork = pindexNewForkTip;
-    CBlockIndex* plonger = ::ChainActive().Tip();
-    while (pfork && pfork != plonger)
-    {
-        while (plonger && plonger->nHeight > pfork->nHeight)
-            plonger = plonger->pprev;
-        if (pfork == plonger)
-            break;
-        pfork = pfork->pprev;
-    }
-
-    // We define a condition where we should warn the user about as a fork of at least 7 blocks
-    // with a tip within 72 blocks (+/- 3 hours if no one mines it) of ours
-    // or a chain that is entirely longer than ours and invalid (note that this should be detected by both)
-    // We use 7 blocks rather arbitrarily as it represents just under 10% of sustained network
-    // hash rate operating on the fork.
-    // We define it this way because it allows us to only store the highest fork tip (+ base) which meets
-    // the 7-block condition and from this always have the most-likely-to-cause-warning fork
-    if (pfork && (!pindexBestForkTip || pindexNewForkTip->nHeight > pindexBestForkTip->nHeight) &&
-            pindexNewForkTip->nChainWork - pfork->nChainWork > (GetBlockProof(*pfork) * 7) &&
-            ::ChainActive().Height() - pindexNewForkTip->nHeight < 72)
-    {
-        pindexBestForkTip = pindexNewForkTip;
-        pindexBestForkBase = pfork;
+    if (pindexBestInvalid && pindexBestInvalid->nChainWork > ::ChainActive().Tip()->nChainWork + (GetBlockProof(*::ChainActive().Tip()) * 6)) {
+        LogPrintf("%s: Warning: Found invalid chain which has higher work (at least ~6 blocks worth of work) than our best chain.\nChain state database corruption likely.\n", __func__);
+        SetfLargeWorkInvalidChainFound(true);
+    } else {
+        SetfLargeWorkInvalidChainFound(false);
     }
-
-    CheckForkWarningConditions();
 }
 
 // Called both upon regular invalid block discovery *and* InvalidateBlock
@@ -3094,8 +3028,8 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
     AssertLockHeld(cs_main);
     AssertLockHeld(m_mempool.cs);
 
-    const CBlockIndex *pindexOldTip = m_chain.Tip();
-    const CBlockIndex *pindexFork = m_chain.FindFork(pindexMostWork);
+    const CBlockIndex* pindexOldTip = m_chain.Tip();
+    const CBlockIndex* pindexFork = m_chain.FindFork(pindexMostWork);
 
     // Disconnect active blocks which are no longer in the best chain.
     bool fBlocksDisconnected = false;
@@ -3115,7 +3049,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
         fBlocksDisconnected = true;
     }
 
-    // Build list of new blocks to connect.
+    // Build list of new blocks to connect (in descending height order).
     std::vector<CBlockIndex*> vpindexToConnect;
     bool fContinue = true;
     int nHeight = pindexFork ? pindexFork->nHeight : -1;
@@ -3125,7 +3059,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
         int nTargetHeight = std::min(nHeight + 32, pindexMostWork->nHeight);
         vpindexToConnect.clear();
         vpindexToConnect.reserve(nTargetHeight - nHeight);
-        CBlockIndex *pindexIter = pindexMostWork->GetAncestor(nTargetHeight);
+        CBlockIndex* pindexIter = pindexMostWork->GetAncestor(nTargetHeight);
         while (pindexIter && pindexIter->nHeight != nHeight) {
             vpindexToConnect.push_back(pindexIter);
             pindexIter = pindexIter->pprev;
@@ -3133,7 +3067,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
         nHeight = nTargetHeight;
 
         // Connect new blocks.
-        for (CBlockIndex *pindexConnect : reverse_iterate(vpindexToConnect)) {
+        for (CBlockIndex* pindexConnect : reverse_iterate(vpindexToConnect)) {
             if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool)) {
                 if (state.IsInvalid()) {
                     // The block violates a consensus rule.
@@ -3169,11 +3103,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
     }
     m_mempool.check(&CoinsTip());
 
-    // Callbacks/notifications for a new best chain.
-    if (fInvalidFound)
-        CheckForkWarningConditionsOnNewFork(vpindexToConnect.back());
-    else
-        CheckForkWarningConditions();
+    CheckForkWarningConditions();
 
     return true;
 }
diff --git a/src/warnings.cpp b/src/warnings.cpp
index f50a997b2c8d..db0fcf0f1b0d 100644
--- a/src/warnings.cpp
+++ b/src/warnings.cpp
@@ -12,7 +12,6 @@
 
 static Mutex g_warnings_mutex;
 static std::string strMiscWarning GUARDED_BY(g_warnings_mutex);
-static bool fLargeWorkForkFound GUARDED_BY(g_warnings_mutex) = false;
 static bool fLargeWorkInvalidChainFound GUARDED_BY(g_warnings_mutex) = false;
 
 void SetMiscWarning(const std::string& strWarning)
@@ -21,18 +20,6 @@ void SetMiscWarning(const std::string& strWarning)
     strMiscWarning = strWarning;
 }
 
-void SetfLargeWorkForkFound(bool flag)
-{
-    LOCK(g_warnings_mutex);
-    fLargeWorkForkFound = flag;
-}
-
-bool GetfLargeWorkForkFound()
-{
-    LOCK(g_warnings_mutex);
-    return fLargeWorkForkFound;
-}
-
 void SetfLargeWorkInvalidChainFound(bool flag)
 {
     LOCK(g_warnings_mutex);
@@ -59,10 +46,7 @@ std::string GetWarnings(bool verbose)
         warnings_verbose += (warnings_verbose.empty() ? "" : warning_separator) + strMiscWarning;
     }
 
-    if (fLargeWorkForkFound) {
-        warnings_concise = "Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.";
-        warnings_verbose += (warnings_verbose.empty() ? "" : warning_separator) + _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.").translated;
-    } else if (fLargeWorkInvalidChainFound) {
+    if (fLargeWorkInvalidChainFound) {
         warnings_concise = "Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.";
         warnings_verbose += (warnings_verbose.empty() ? "" : warning_separator) + _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.").translated;
     }
diff --git a/src/warnings.h b/src/warnings.h
index 4bf7af53685d..b7906f877be1 100644
--- a/src/warnings.h
+++ b/src/warnings.h
@@ -9,8 +9,6 @@
 #include <string>
 
 void SetMiscWarning(const std::string& strWarning);
-void SetfLargeWorkForkFound(bool flag);
-bool GetfLargeWorkForkFound();
 void SetfLargeWorkInvalidChainFound(bool flag);
 /** Format a string that describes several potential problems detected by the core.
  * @param[in] verbose bool

From 0d7516e2c2991177f004f9a98896e30923933fb2 Mon Sep 17 00:00:00 2001
From: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com>
Date: Tue, 8 Sep 2020 14:36:31 -0400
Subject: [PATCH 26/35] merge bitcoin#19927: Reduce direct g_chainman usage

---
 src/init.cpp                     |  2 +-
 src/qt/test/apptests.cpp         |  7 ++-
 src/test/util/setup_common.cpp   |  2 +-
 src/validation.cpp               | 86 +++++++++++---------------------
 src/validation.h                 | 38 ++++++++++++--
 src/wallet/test/wallet_tests.cpp |  6 +--
 6 files changed, 72 insertions(+), 69 deletions(-)

diff --git a/src/init.cpp b/src/init.cpp
index 1a9dce565461..cb2614a2b835 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -2015,7 +2015,7 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc
                 chainman.m_total_coinstip_cache = nCoinCacheUsage;
                 chainman.m_total_coinsdb_cache = nCoinDBCache;
 
-                UnloadBlockIndex(node.mempool.get());
+                UnloadBlockIndex(node.mempool.get(), chainman);
 
                 // new CBlockTreeDB tries to delete the existing file, which
                 // fails if it's still open from the previous loop. Close it first:
diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp
index 4486464cb89d..d4c4f3874974 100644
--- a/src/qt/test/apptests.cpp
+++ b/src/qt/test/apptests.cpp
@@ -87,8 +87,11 @@ void AppTests::appTests()
     // Reset global state to avoid interfering with later tests.
     LogInstance().DisconnectTestLogger();
     AbortShutdown();
-    UnloadBlockIndex(/* mempool */ nullptr);
-    WITH_LOCK(::cs_main, g_chainman.Reset());
+    {
+        LOCK(cs_main);
+        UnloadBlockIndex(/* mempool */ nullptr, g_chainman);
+        g_chainman.Reset();
+    }
 }
 
 //! Entry point for BitcoinGUI tests.
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 8cad26fec4be..010dc93e85b4 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -216,7 +216,7 @@ ChainTestingSetup::~ChainTestingSetup()
     m_node.addrman.reset();
     m_node.args = nullptr;
     m_node.banman.reset();
-    UnloadBlockIndex(m_node.mempool.get());
+    UnloadBlockIndex(m_node.mempool.get(), *m_node.chainman);
     m_node.mempool.reset();
     m_node.scheduler.reset();
     m_node.llmq_ctx.reset();
diff --git a/src/validation.cpp b/src/validation.cpp
index cdf6ef08714f..6fb2215cf61c 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -198,9 +198,6 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc
 
 std::unique_ptr<CBlockTreeDB> pblocktree;
 
-// See definition for documentation
-static void FindFilesToPruneManual(ChainstateManager& chainman, std::set<int>& setFilesToPrune, int nManualPruneHeight);
-static void FindFilesToPrune(ChainstateManager& chainman, std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight);
 bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = nullptr);
 static FILE* OpenUndoFile(const FlatFilePos &pos, bool fReadOnly = false);
 static FlatFileSeq BlockFileSeq();
@@ -2566,11 +2563,11 @@ bool CChainState::FlushStateToDisk(
             if (nManualPruneHeight > 0) {
                 LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune (manual)", BCLog::BENCHMARK);
 
-                FindFilesToPruneManual(g_chainman, setFilesToPrune, nManualPruneHeight);
+                m_blockman.FindFilesToPruneManual(setFilesToPrune, nManualPruneHeight, m_chain.Height());
             } else {
                 LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune", BCLog::BENCHMARK);
 
-                FindFilesToPrune(g_chainman, setFilesToPrune, chainparams.PruneAfterHeight());
+                m_blockman.FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight(), m_chain.Height(), IsInitialBlockDownload());
                 fCheckForPruning = false;
             }
             if (!setFilesToPrune.empty()) {
@@ -4328,12 +4325,12 @@ uint64_t CalculateCurrentUsage()
     return retval;
 }
 
-void ChainstateManager::PruneOneBlockFile(const int fileNumber)
+void BlockManager::PruneOneBlockFile(const int fileNumber)
 {
     AssertLockHeld(cs_main);
     LOCK(cs_LastBlockFile);
 
-    for (const auto& entry : m_blockman.m_block_index) {
+    for (const auto& entry : m_block_index) {
         CBlockIndex* pindex = entry.second;
         if (pindex->nFile == fileNumber) {
             pindex->nStatus &= ~BLOCK_HAVE_DATA;
@@ -4347,12 +4344,12 @@ void ChainstateManager::PruneOneBlockFile(const int fileNumber)
             // to be downloaded again in order to consider its chain, at which
             // point it would be considered as a candidate for
             // m_blocks_unlinked or setBlockIndexCandidates.
-            auto range = m_blockman.m_blocks_unlinked.equal_range(pindex->pprev);
+            auto range = m_blocks_unlinked.equal_range(pindex->pprev);
             while (range.first != range.second) {
                 std::multimap<CBlockIndex *, CBlockIndex *>::iterator _it = range.first;
                 range.first++;
                 if (_it->second == pindex) {
-                    m_blockman.m_blocks_unlinked.erase(_it);
+                    m_blocks_unlinked.erase(_it);
                 }
             }
         }
@@ -4373,22 +4370,23 @@ void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune)
     }
 }
 
-/* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */
-static void FindFilesToPruneManual(ChainstateManager& chainman, std::set<int>& setFilesToPrune, int nManualPruneHeight)
+void BlockManager::FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight, int chain_tip_height)
 {
     assert(fPruneMode && nManualPruneHeight > 0);
 
     LOCK2(cs_main, cs_LastBlockFile);
-    if (::ChainActive().Tip() == nullptr)
+    if (chain_tip_height < 0) {
         return;
+    }
 
     // last block to prune is the lesser of (user-specified height, MIN_BLOCKS_TO_KEEP from the tip)
-    unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, ::ChainActive().Tip()->nHeight - MIN_BLOCKS_TO_KEEP);
-    int count=0;
+    unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, chain_tip_height - MIN_BLOCKS_TO_KEEP);
+    int count = 0;
     for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) {
-        if (vinfoBlockFile[fileNumber].nSize == 0 || vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune)
+        if (vinfoBlockFile[fileNumber].nSize == 0 || vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
             continue;
-        chainman.PruneOneBlockFile(fileNumber);
+        }
+        PruneOneBlockFile(fileNumber);
         setFilesToPrune.insert(fileNumber);
         count++;
     }
@@ -4406,46 +4404,31 @@ void PruneBlockFilesManual(int nManualPruneHeight)
     }
 }
 
-/**
- * Prune block and undo files (blk???.dat and undo???.dat) so that the disk space used is less than a user-defined target.
- * The user sets the target (in MB) on the command line or in config file.  This will be run on startup and whenever new
- * space is allocated in a block or undo file, staying below the target. Changing back to unpruned requires a reindex
- * (which in this case means the blockchain must be re-downloaded.)
- *
- * Pruning functions are called from FlushStateToDisk when the global fCheckForPruning flag has been set.
- * Block and undo files are deleted in lock-step (when blk00003.dat is deleted, so is rev00003.dat.)
- * Pruning cannot take place until the longest chain is at least a certain length (100000 on mainnet, 1000 on testnet, 1000 on regtest).
- * Pruning will never delete a block within a defined distance (currently 288) from the active chain's tip.
- * The block index is updated by unsetting HAVE_DATA and HAVE_UNDO for any blocks that were stored in the deleted files.
- * A db flag records the fact that at least some block files have been pruned.
- *
- * @param[out]   setFilesToPrune   The set of file indices that can be unlinked will be returned
- */
-static void FindFilesToPrune(ChainstateManager& chainman, std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight)
+void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, bool is_ibd)
 {
     LOCK2(cs_main, cs_LastBlockFile);
-    if (::ChainActive().Tip() == nullptr || nPruneTarget == 0) {
+    if (chain_tip_height < 0 || nPruneTarget == 0) {
         return;
     }
-    if ((uint64_t)::ChainActive().Tip()->nHeight <= nPruneAfterHeight) {
+    if ((uint64_t)chain_tip_height <= nPruneAfterHeight) {
         return;
     }
 
-    unsigned int nLastBlockWeCanPrune = ::ChainActive().Tip()->nHeight - MIN_BLOCKS_TO_KEEP;
+    unsigned int nLastBlockWeCanPrune = chain_tip_height - MIN_BLOCKS_TO_KEEP;
     uint64_t nCurrentUsage = CalculateCurrentUsage();
     // We don't check to prune until after we've allocated new space for files
     // So we should leave a buffer under our target to account for another allocation
     // before the next pruning.
     uint64_t nBuffer = BLOCKFILE_CHUNK_SIZE + UNDOFILE_CHUNK_SIZE;
     uint64_t nBytesToPrune;
-    int count=0;
+    int count = 0;
 
     if (nCurrentUsage + nBuffer >= nPruneTarget) {
         // On a prune event, the chainstate DB is flushed.
         // To avoid excessive prune events negating the benefit of high dbcache
         // values, we should not prune too rapidly.
         // So when pruning in IBD, increase the buffer a bit to avoid a re-prune too soon.
-        if (::ChainstateActive().IsInitialBlockDownload()) {
+        if (is_ibd) {
             // Since this is only relevant during IBD, we use a fixed 10%
             nBuffer += nPruneTarget / 10;
         }
@@ -4453,17 +4436,20 @@ static void FindFilesToPrune(ChainstateManager& chainman, std::set<int>& setFile
         for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) {
             nBytesToPrune = vinfoBlockFile[fileNumber].nSize + vinfoBlockFile[fileNumber].nUndoSize;
 
-            if (vinfoBlockFile[fileNumber].nSize == 0)
+            if (vinfoBlockFile[fileNumber].nSize == 0) {
                 continue;
+            }
 
-            if (nCurrentUsage + nBuffer < nPruneTarget)  // are we below our target?
+            if (nCurrentUsage + nBuffer < nPruneTarget) { // are we below our target?
                 break;
+            }
 
             // don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip but keep scanning
-            if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune)
+            if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
                 continue;
+            }
 
-            chainman.PruneOneBlockFile(fileNumber);
+            PruneOneBlockFile(fileNumber);
             // Queue up the files for removal
             setFilesToPrune.insert(fileNumber);
             nCurrentUsage -= nBytesToPrune;
@@ -4921,10 +4907,10 @@ void CChainState::UnloadBlockIndex() {
 // May NOT be used after any connections are up as much
 // of the peer-processing logic assumes a consistent
 // block index state
-void UnloadBlockIndex(CTxMemPool* mempool)
+void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman)
 {
     LOCK(cs_main);
-    g_chainman.Unload();
+    chainman.Unload();
     pindexBestInvalid = nullptr;
     pindexBestHeader = nullptr;
     if (mempool) mempool->clear();
@@ -5578,20 +5564,6 @@ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pin
     return std::min<double>(pindex->nChainTx / fTxTotal, 1.0);
 }
 
-class CMainCleanup
-{
-public:
-    CMainCleanup() {}
-    ~CMainCleanup() {
-        // block headers
-        BlockMap::iterator it1 = g_chainman.BlockIndex().begin();
-        for (; it1 != g_chainman.BlockIndex().end(); it1++)
-            delete (*it1).second;
-        g_chainman.BlockIndex().clear();
-    }
-};
-static CMainCleanup instance_of_cmaincleanup;
-
 std::optional<uint256> ChainstateManager::SnapshotBlockhash() const {
     if (m_active_chainstate != nullptr) {
         // If a snapshot chainstate exists, it will always be our active.
diff --git a/src/validation.h b/src/validation.h
index d79b6ad20fe5..532e22ed1b81 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -180,7 +180,7 @@ void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi
 /** Ensures we have a genesis block in the block tree, possibly writing one to disk. */
 bool LoadGenesisBlock(const CChainParams& chainparams);
 /** Unload database information */
-void UnloadBlockIndex(CTxMemPool* mempool);
+void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman);
 /** Run instances of script checking worker threads */
 void StartScriptCheckWorkerThreads(int threads_num);
 /** Stop all of the script checking worker threads */
@@ -375,7 +375,31 @@ struct CBlockIndexWorkComparator
  * This data is used mostly in `CChainState` - information about, e.g.,
  * candidate tips is not maintained here.
  */
-class BlockManager {
+class BlockManager
+{
+    friend CChainState;
+
+private:
+    /* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */
+    void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight, int chain_tip_height);
+
+    /**
+     * Prune block and undo files (blk???.dat and undo???.dat) so that the disk space used is less than a user-defined target.
+     * The user sets the target (in MB) on the command line or in config file.  This will be run on startup and whenever new
+     * space is allocated in a block or undo file, staying below the target. Changing back to unpruned requires a reindex
+     * (which in this case means the blockchain must be re-downloaded.)
+     *
+     * Pruning functions are called from FlushStateToDisk when the global fCheckForPruning flag has been set.
+     * Block and undo files are deleted in lock-step (when blk00003.dat is deleted, so is rev00003.dat.)
+     * Pruning cannot take place until the longest chain is at least a certain length (100000 on mainnet, 1000 on testnet, 1000 on regtest).
+     * Pruning will never delete a block within a defined distance (currently 288) from the active chain's tip.
+     * The block index is updated by unsetting HAVE_DATA and HAVE_UNDO for any blocks that were stored in the deleted files.
+     * A db flag records the fact that at least some block files have been pruned.
+     *
+     * @param[out]   setFilesToPrune   The set of file indices that can be unlinked will be returned
+     */
+    void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, bool is_ibd);
+
 public:
     BlockMap m_block_index GUARDED_BY(cs_main);
     PrevBlockMap m_prev_block_index GUARDED_BY(cs_main);
@@ -426,6 +450,9 @@ class BlockManager {
     /** Create a new block index entry for a given block hash */
     CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
 
+    //! Mark one block file as pruned (modify associated database entries)
+    void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
     /**
      * If a block header hasn't already been seen, call CheckBlockHeader on it, ensure
      * that it doesn't descend from an invalid block, and then add it to m_block_index.
@@ -435,6 +462,10 @@ class BlockManager {
         CValidationState& state,
         const CChainParams& chainparams,
         CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+    ~BlockManager() {
+        Unload();
+    }
 };
 
 /**
@@ -948,9 +979,6 @@ class ChainstateManager
      */
     bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr, CBlockHeader* first_invalid = nullptr) LOCKS_EXCLUDED(cs_main);
 
-    //! Mark one block file as pruned (modify associated database entries)
-    void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
     //! Load the block tree and coins database from disk, initializing state if we're running with -reindex
     bool LoadBlockIndex(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
 
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 6626347f0a1f..c1a94d383603 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -134,7 +134,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
     // Prune the older block file.
     {
         LOCK(cs_main);
-        Assert(m_node.chainman)->PruneOneBlockFile(oldTip->GetBlockPos().nFile);
+        Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(oldTip->GetBlockPos().nFile);
     }
     UnlinkPrunedFiles({oldTip->GetBlockPos().nFile});
 
@@ -160,7 +160,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
     // Prune the remaining block file.
     {
         LOCK(cs_main);
-        Assert(m_node.chainman)->PruneOneBlockFile(newTip->GetBlockPos().nFile);
+        Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(newTip->GetBlockPos().nFile);
     }
     UnlinkPrunedFiles({newTip->GetBlockPos().nFile});
 
@@ -199,7 +199,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
     // Prune the older block file.
     {
         LOCK(cs_main);
-        Assert(m_node.chainman)->PruneOneBlockFile(oldTip->GetBlockPos().nFile);
+        Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(oldTip->GetBlockPos().nFile);
     }
     UnlinkPrunedFiles({oldTip->GetBlockPos().nFile});
 

From 6648e9f199726adbd6233313f1a2d46fe61bc1da Mon Sep 17 00:00:00 2001
From: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com>
Date: Wed, 27 Jan 2021 16:20:59 -0500
Subject: [PATCH 27/35] merge bitcoin#21025: Guard all chainstates with cs_main

---
 src/validation.cpp | 6 ++++++
 src/validation.h   | 4 ++--
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/validation.cpp b/src/validation.cpp
index 6fb2215cf61c..4531f02be9a5 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -5565,6 +5565,7 @@ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pin
 }
 
 std::optional<uint256> ChainstateManager::SnapshotBlockhash() const {
+    LOCK(::cs_main);
     if (m_active_chainstate != nullptr) {
         // If a snapshot chainstate exists, it will always be our active.
         return m_active_chainstate->m_from_snapshot_blockhash;
@@ -5574,6 +5575,7 @@ std::optional<uint256> ChainstateManager::SnapshotBlockhash() const {
 
 std::vector<CChainState*> ChainstateManager::GetAll()
 {
+    LOCK(::cs_main);
     std::vector<CChainState*> out;
 
     if (!IsSnapshotValidated() && m_ibd_chainstate) {
@@ -5623,11 +5625,13 @@ CChainState& ChainstateManager::ActiveChainstate() const
 
 bool ChainstateManager::IsSnapshotActive() const
 {
+    LOCK(::cs_main);
     return m_snapshot_chainstate && m_active_chainstate == m_snapshot_chainstate.get();
 }
 
 CChainState& ChainstateManager::ValidatedChainstate() const
 {
+    LOCK(::cs_main);
     if (m_snapshot_chainstate && IsSnapshotValidated()) {
         return *m_snapshot_chainstate.get();
     }
@@ -5637,6 +5641,7 @@ CChainState& ChainstateManager::ValidatedChainstate() const
 
 bool ChainstateManager::IsBackgroundIBD(CChainState* chainstate) const
 {
+    LOCK(::cs_main);
     return (m_snapshot_chainstate && chainstate == m_ibd_chainstate.get());
 }
 
@@ -5652,6 +5657,7 @@ void ChainstateManager::Unload()
 
 void ChainstateManager::Reset()
 {
+    LOCK(::cs_main);
     m_ibd_chainstate.reset();
     m_snapshot_chainstate.reset();
     m_active_chainstate = nullptr;
diff --git a/src/validation.h b/src/validation.h
index 532e22ed1b81..292bb13fca79 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -841,7 +841,7 @@ class ChainstateManager
     //! This is especially important when, e.g., calling ActivateBestChain()
     //! on all chainstates because we are not able to hold ::cs_main going into
     //! that call.
-    std::unique_ptr<CChainState> m_ibd_chainstate;
+    std::unique_ptr<CChainState> m_ibd_chainstate GUARDED_BY(::cs_main);
 
     //! A chainstate initialized on the basis of a UTXO snapshot. If this is
     //! non-null, it is always our active chainstate.
@@ -854,7 +854,7 @@ class ChainstateManager
     //! This is especially important when, e.g., calling ActivateBestChain()
     //! on all chainstates because we are not able to hold ::cs_main going into
     //! that call.
-    std::unique_ptr<CChainState> m_snapshot_chainstate;
+    std::unique_ptr<CChainState> m_snapshot_chainstate GUARDED_BY(::cs_main);
 
     //! Points to either the ibd or snapshot chainstate; indicates our
     //! most-work chain.

From b0b80d0c9c0e8b5ecb9957e3e39ddc3a796e5af9 Mon Sep 17 00:00:00 2001
From: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com>
Date: Mon, 13 Mar 2023 15:35:09 +0000
Subject: [PATCH 28/35] partial bitcoin#19775: Activate segwit in
 TestChain100Setup

excludes:
- fad84b7e14ff92465bc17bfdaf1362bcffe092f6
---
 src/test/util/setup_common.cpp | 25 ++++++++++++++-----------
 src/test/util/setup_common.h   | 13 +++++++------
 2 files changed, 21 insertions(+), 17 deletions(-)

diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 010dc93e85b4..baf4f17665d9 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -269,8 +269,7 @@ TestChainSetup::TestChainSetup(int blockCount)
     // Generate a 100-block chain:
     coinbaseKey.MakeNewKey(true);
     CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
-    for (int i = 0; i < blockCount; i++)
-    {
+    for (int i = 0; i < blockCount; i++) {
         std::vector<CMutableTransaction> noTxns;
         CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey);
         m_coinbase_txns.push_back(b.vtx[0]);
@@ -288,8 +287,6 @@ TestChainSetup::TestChainSetup(int blockCount)
     }
 }
 
-// Create a new block with just given transactions, coinbase paying to
-// scriptPubKey, and try to add it to the current chain.
 CBlock TestChainSetup::CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns, const CScript& scriptPubKey)
 {
     const CChainParams& chainparams = Params();
@@ -298,8 +295,7 @@ CBlock TestChainSetup::CreateAndProcessBlock(const std::vector<CMutableTransacti
     std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
     Assert(m_node.chainman)->ProcessNewBlock(chainparams, shared_pblock, true, nullptr);
 
-    CBlock result = block;
-    return result;
+    return block;
 }
 
 CBlock TestChainSetup::CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns, const CKey& scriptKey)
@@ -311,8 +307,12 @@ CBlock TestChainSetup::CreateAndProcessBlock(const std::vector<CMutableTransacti
 CBlock TestChainSetup::CreateBlock(const std::vector<CMutableTransaction>& txns, const CScript& scriptPubKey)
 {
     const CChainParams& chainparams = Params();
-    std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, chainparams).CreateNewBlock(scriptPubKey);
-    CBlock& block = pblocktemplate->block;
+    CTxMemPool empty_pool;
+    CBlock block = BlockAssembler(
+            *sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor,
+            *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb,
+            empty_pool, chainparams
+        ).CreateNewBlock(scriptPubKey)->block;
 
     std::vector<CTransactionRef> llmqCommitments;
     for (const auto& tx : block.vtx) {
@@ -323,10 +323,13 @@ CBlock TestChainSetup::CreateBlock(const std::vector<CMutableTransaction>& txns,
 
     // Replace mempool-selected txns with just coinbase plus passed-in txns:
     block.vtx.resize(1);
+    Assert(block.vtx.size() == 1);
+
     // Re-add quorum commitments
     block.vtx.insert(block.vtx.end(), llmqCommitments.begin(), llmqCommitments.end());
-    for (const CMutableTransaction& tx : txns)
+    for (const CMutableTransaction& tx : txns) {
         block.vtx.push_back(MakeTransactionRef(tx));
+    }
 
     // Manually update CbTx as we modified the block here
     if (block.vtx[0]->nType == TRANSACTION_COINBASE) {
@@ -373,8 +376,8 @@ TestChainSetup::~TestChainSetup()
     g_txindex.reset();
 }
 
-
-CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx) {
+CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction& tx)
+{
     return FromTx(MakeTransactionRef(tx));
 }
 
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index a009dd857222..d69dd73dbd4d 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -115,8 +115,10 @@ struct TestChainSetup : public RegTestingSetup
     TestChainSetup(int blockCount);
     ~TestChainSetup();
 
-    // Create a new block with just given transactions, coinbase paying to
-    // scriptPubKey, and try to add it to the current chain.
+    /**
+     * Create a new block with just given transactions, coinbase paying to
+     * scriptPubKey, and try to add it to the current chain.
+     */
     CBlock CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns,
                                  const CScript& scriptPubKey);
     CBlock CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns,
@@ -130,10 +132,9 @@ struct TestChainSetup : public RegTestingSetup
     CKey coinbaseKey; // private/public key needed to spend coinbase transactions
 };
 
-//
-// Testing fixture that pre-creates a
-// 100-block REGTEST-mode block chain
-//
+/**
+ * Testing fixture that pre-creates a 100-block REGTEST-mode block chain
+ */
 struct TestChain100Setup : public TestChainSetup {
     TestChain100Setup() : TestChainSetup(100) {}
 };

From 431415a67918fde4c964ec63d0927b4d5981be8b Mon Sep 17 00:00:00 2001
From: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com>
Date: Thu, 16 Mar 2023 08:46:21 +0000
Subject: [PATCH 29/35] merge bitcoin#20828: Introduce CallOneOf helper to
 replace switch-case

---
 src/test/fuzz/addrman.cpp                     | 138 ++++----
 src/test/fuzz/autofile.cpp                    |  68 ++--
 src/test/fuzz/banman.cpp                      |  82 ++---
 src/test/fuzz/bloom_filter.cpp                |  78 ++--
 src/test/fuzz/buffered_file.cpp               |  65 ++--
 src/test/fuzz/coins_view.cpp                  | 332 +++++++++---------
 src/test/fuzz/connman.cpp                     | 165 +++++----
 src/test/fuzz/crypto.cpp                      | 163 ++++-----
 src/test/fuzz/crypto_chacha20.cpp             |  47 ++-
 .../fuzz/crypto_chacha20_poly1305_aead.cpp    |  71 ++--
 src/test/fuzz/merkleblock.cpp                 |  48 ++-
 src/test/fuzz/net.cpp                         | 173 +++++----
 src/test/fuzz/policy_estimator.cpp            |  68 ++--
 src/test/fuzz/rolling_bloom_filter.cpp        |  46 ++-
 src/test/fuzz/script_ops.cpp                  |  85 +++--
 src/test/fuzz/scriptnum_ops.cpp               | 192 +++++-----
 src/test/fuzz/strprintf.cpp                   | 121 +++----
 src/test/fuzz/system.cpp                      | 118 +++----
 src/test/fuzz/util.h                          | 164 ++++-----
 19 files changed, 1054 insertions(+), 1170 deletions(-)

diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index 14f133e40f20..b12f421bc833 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -35,83 +35,71 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
         }
     }
     while (fuzzed_data_provider.ConsumeBool()) {
-        switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 11)) {
-        case 0: {
-            addr_man.Clear();
-            break;
-        }
-        case 1: {
-            addr_man.ResolveCollisions();
-            break;
-        }
-        case 2: {
-            (void)addr_man.SelectTriedCollision();
-            break;
-        }
-        case 3: {
-            (void)addr_man.Select(fuzzed_data_provider.ConsumeBool());
-            break;
-        }
-        case 4: {
-            (void)addr_man.GetAddr();
-            break;
-        }
-        case 5: {
-            const std::optional<CAddress> opt_address = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
-            const std::optional<CNetAddr> opt_net_addr = ConsumeDeserializable<CNetAddr>(fuzzed_data_provider);
-            if (opt_address && opt_net_addr) {
-                addr_man.Add(*opt_address, *opt_net_addr, fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 100000000));
-            }
-            break;
-        }
-        case 6: {
-            std::vector<CAddress> addresses;
-            while (fuzzed_data_provider.ConsumeBool()) {
+        CallOneOf(
+            fuzzed_data_provider,
+            [&] {
+                addr_man.Clear();
+            },
+            [&] {
+                addr_man.ResolveCollisions();
+            },
+            [&] {
+                (void)addr_man.SelectTriedCollision();
+            },
+            [&] {
+                (void)addr_man.Select(fuzzed_data_provider.ConsumeBool());
+            },
+            [&] {
+                (void)addr_man.GetAddr();
+            },
+            [&] {
                 const std::optional<CAddress> opt_address = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
-                if (!opt_address) {
-                    break;
+                const std::optional<CNetAddr> opt_net_addr = ConsumeDeserializable<CNetAddr>(fuzzed_data_provider);
+                if (opt_address && opt_net_addr) {
+                    addr_man.Add(*opt_address, *opt_net_addr, fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 100000000));
                 }
-                addresses.push_back(*opt_address);
-            }
-            const std::optional<CNetAddr> opt_net_addr = ConsumeDeserializable<CNetAddr>(fuzzed_data_provider);
-            if (opt_net_addr) {
-                addr_man.Add(addresses, *opt_net_addr, fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 100000000));
-            }
-            break;
-        }
-        case 7: {
-            const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider);
-            if (opt_service) {
-                addr_man.Good(*opt_service, fuzzed_data_provider.ConsumeBool(), ConsumeTime(fuzzed_data_provider));
-            }
-            break;
-        }
-        case 8: {
-            const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider);
-            if (opt_service) {
-                addr_man.Attempt(*opt_service, fuzzed_data_provider.ConsumeBool(), ConsumeTime(fuzzed_data_provider));
-            }
-            break;
-        }
-        case 9: {
-            const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider);
-            if (opt_service) {
-                addr_man.Connected(*opt_service, ConsumeTime(fuzzed_data_provider));
-            }
-            break;
-        }
-        case 10: {
-            const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider);
-            if (opt_service) {
-                addr_man.SetServices(*opt_service, ServiceFlags{fuzzed_data_provider.ConsumeIntegral<uint64_t>()});
-            }
-            break;
-        }
-        case 11: {
-            (void)addr_man.Check();
-            break;
-        }
-        }
+            },
+            [&] {
+                std::vector<CAddress> addresses;
+                while (fuzzed_data_provider.ConsumeBool()) {
+                    const std::optional<CAddress> opt_address = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
+                    if (!opt_address) {
+                        break;
+                    }
+                    addresses.push_back(*opt_address);
+                }
+                const std::optional<CNetAddr> opt_net_addr = ConsumeDeserializable<CNetAddr>(fuzzed_data_provider);
+                if (opt_net_addr) {
+                    addr_man.Add(addresses, *opt_net_addr, fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 100000000));
+                }
+            },
+            [&] {
+                const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider);
+                if (opt_service) {
+                    addr_man.Good(*opt_service, fuzzed_data_provider.ConsumeBool(), ConsumeTime(fuzzed_data_provider));
+                }
+            },
+            [&] {
+                const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider);
+                if (opt_service) {
+                    addr_man.Attempt(*opt_service, fuzzed_data_provider.ConsumeBool(), ConsumeTime(fuzzed_data_provider));
+                }
+            },
+            [&] {
+                const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider);
+                if (opt_service) {
+                    addr_man.Connected(*opt_service, ConsumeTime(fuzzed_data_provider));
+                }
+            },
+            [&] {
+                const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider);
+                if (opt_service) {
+                    addr_man.SetServices(*opt_service, ServiceFlags{fuzzed_data_provider.ConsumeIntegral<uint64_t>()});
+                }
+            },
+            [&] {
+                (void)addr_man.Check();
+            });
     }
     (void)addr_man.size();
     CDataStream data_stream(SER_NETWORK, PROTOCOL_VERSION);
diff --git a/src/test/fuzz/autofile.cpp b/src/test/fuzz/autofile.cpp
index 95be582d6587..e3f1a8572a4c 100644
--- a/src/test/fuzz/autofile.cpp
+++ b/src/test/fuzz/autofile.cpp
@@ -20,43 +20,37 @@ FUZZ_TARGET(autofile)
     FuzzedAutoFileProvider fuzzed_auto_file_provider = ConsumeAutoFile(fuzzed_data_provider);
     CAutoFile auto_file = fuzzed_auto_file_provider.open();
     while (fuzzed_data_provider.ConsumeBool()) {
-        switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 5)) {
-        case 0: {
-            std::array<uint8_t, 4096> arr{};
-            try {
-                auto_file.read((char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
-            } catch (const std::ios_base::failure&) {
-            }
-            break;
-        }
-        case 1: {
-            const std::array<uint8_t, 4096> arr{};
-            try {
-                auto_file.write((const char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
-            } catch (const std::ios_base::failure&) {
-            }
-            break;
-        }
-        case 2: {
-            try {
-                auto_file.ignore(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
-            } catch (const std::ios_base::failure&) {
-            }
-            break;
-        }
-        case 3: {
-            auto_file.fclose();
-            break;
-        }
-        case 4: {
-            ReadFromStream(fuzzed_data_provider, auto_file);
-            break;
-        }
-        case 5: {
-            WriteToStream(fuzzed_data_provider, auto_file);
-            break;
-        }
-        }
+        CallOneOf(
+            fuzzed_data_provider,
+            [&] {
+                std::array<uint8_t, 4096> arr{};
+                try {
+                    auto_file.read((char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+                } catch (const std::ios_base::failure&) {
+                }
+            },
+            [&] {
+                const std::array<uint8_t, 4096> arr{};
+                try {
+                    auto_file.write((const char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+                } catch (const std::ios_base::failure&) {
+                }
+            },
+            [&] {
+                try {
+                    auto_file.ignore(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+                } catch (const std::ios_base::failure&) {
+                }
+            },
+            [&] {
+                auto_file.fclose();
+            },
+            [&] {
+                ReadFromStream(fuzzed_data_provider, auto_file);
+            },
+            [&] {
+                WriteToStream(fuzzed_data_provider, auto_file);
+            });
     }
     (void)auto_file.Get();
     (void)auto_file.GetType();
diff --git a/src/test/fuzz/banman.cpp b/src/test/fuzz/banman.cpp
index bc321932e633..17271ff2cb7f 100644
--- a/src/test/fuzz/banman.cpp
+++ b/src/test/fuzz/banman.cpp
@@ -37,51 +37,43 @@ FUZZ_TARGET_INIT(banman, initialize_banman)
     {
         BanMan ban_man{banlist_file, nullptr, ConsumeBanTimeOffset(fuzzed_data_provider)};
         while (fuzzed_data_provider.ConsumeBool()) {
-            switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 11)) {
-            case 0: {
-                ban_man.Ban(ConsumeNetAddr(fuzzed_data_provider),
-                    ConsumeBanTimeOffset(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool());
-                break;
-            }
-            case 1: {
-                ban_man.Ban(ConsumeSubNet(fuzzed_data_provider),
-                    ConsumeBanTimeOffset(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool());
-                break;
-            }
-            case 2: {
-                ban_man.ClearBanned();
-                break;
-            }
-            case 4: {
-                ban_man.IsBanned(ConsumeNetAddr(fuzzed_data_provider));
-                break;
-            }
-            case 5: {
-                ban_man.IsBanned(ConsumeSubNet(fuzzed_data_provider));
-                break;
-            }
-            case 6: {
-                ban_man.Unban(ConsumeNetAddr(fuzzed_data_provider));
-                break;
-            }
-            case 7: {
-                ban_man.Unban(ConsumeSubNet(fuzzed_data_provider));
-                break;
-            }
-            case 8: {
-                banmap_t banmap;
-                ban_man.GetBanned(banmap);
-                break;
-            }
-            case 9: {
-                ban_man.DumpBanlist();
-                break;
-            }
-            case 11: {
-                ban_man.Discourage(ConsumeNetAddr(fuzzed_data_provider));
-                break;
-            }
-            }
+            CallOneOf(
+                fuzzed_data_provider,
+                [&] {
+                    ban_man.Ban(ConsumeNetAddr(fuzzed_data_provider),
+                                ConsumeBanTimeOffset(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool());
+                },
+                [&] {
+                    ban_man.Ban(ConsumeSubNet(fuzzed_data_provider),
+                                ConsumeBanTimeOffset(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool());
+                },
+                [&] {
+                    ban_man.ClearBanned();
+                },
+                [] {},
+                [&] {
+                    ban_man.IsBanned(ConsumeNetAddr(fuzzed_data_provider));
+                },
+                [&] {
+                    ban_man.IsBanned(ConsumeSubNet(fuzzed_data_provider));
+                },
+                [&] {
+                    ban_man.Unban(ConsumeNetAddr(fuzzed_data_provider));
+                },
+                [&] {
+                    ban_man.Unban(ConsumeSubNet(fuzzed_data_provider));
+                },
+                [&] {
+                    banmap_t banmap;
+                    ban_man.GetBanned(banmap);
+                },
+                [&] {
+                    ban_man.DumpBanlist();
+                },
+                [] {},
+                [&] {
+                    ban_man.Discourage(ConsumeNetAddr(fuzzed_data_provider));
+                });
         }
     }
     fs::remove(banlist_file);
diff --git a/src/test/fuzz/bloom_filter.cpp b/src/test/fuzz/bloom_filter.cpp
index c0c66c564b2c..d43c182644bb 100644
--- a/src/test/fuzz/bloom_filter.cpp
+++ b/src/test/fuzz/bloom_filter.cpp
@@ -25,47 +25,43 @@ FUZZ_TARGET(bloom_filter)
         fuzzed_data_provider.ConsumeIntegral<unsigned int>(),
         static_cast<unsigned char>(fuzzed_data_provider.PickValueInArray({BLOOM_UPDATE_NONE, BLOOM_UPDATE_ALL, BLOOM_UPDATE_P2PUBKEY_ONLY, BLOOM_UPDATE_MASK}))};
     while (fuzzed_data_provider.remaining_bytes() > 0) {
-        switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 3)) {
-        case 0: {
-            const std::vector<unsigned char> b = ConsumeRandomLengthByteVector(fuzzed_data_provider);
-            (void)bloom_filter.contains(b);
-            bloom_filter.insert(b);
-            const bool present = bloom_filter.contains(b);
-            assert(present);
-            break;
-        }
-        case 1: {
-            const std::optional<COutPoint> out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
-            if (!out_point) {
-                break;
-            }
-            (void)bloom_filter.contains(*out_point);
-            bloom_filter.insert(*out_point);
-            const bool present = bloom_filter.contains(*out_point);
-            assert(present);
-            break;
-        }
-        case 2: {
-            const std::optional<uint256> u256 = ConsumeDeserializable<uint256>(fuzzed_data_provider);
-            if (!u256) {
-                break;
-            }
-            (void)bloom_filter.contains(*u256);
-            bloom_filter.insert(*u256);
-            const bool present = bloom_filter.contains(*u256);
-            assert(present);
-            break;
-        }
-        case 3: {
-            const std::optional<CMutableTransaction> mut_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
-            if (!mut_tx) {
-                break;
-            }
-            const CTransaction tx{*mut_tx};
-            (void)bloom_filter.IsRelevantAndUpdate(tx);
-            break;
-        }
-        }
+        CallOneOf(
+            fuzzed_data_provider,
+            [&] {
+                const std::vector<unsigned char> b = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+                (void)bloom_filter.contains(b);
+                bloom_filter.insert(b);
+                const bool present = bloom_filter.contains(b);
+                assert(present);
+            },
+            [&] {
+                const std::optional<COutPoint> out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
+                if (!out_point) {
+                    return;
+                }
+                (void)bloom_filter.contains(*out_point);
+                bloom_filter.insert(*out_point);
+                const bool present = bloom_filter.contains(*out_point);
+                assert(present);
+            },
+            [&] {
+                const std::optional<uint256> u256 = ConsumeDeserializable<uint256>(fuzzed_data_provider);
+                if (!u256) {
+                    return;
+                }
+                (void)bloom_filter.contains(*u256);
+                bloom_filter.insert(*u256);
+                const bool present = bloom_filter.contains(*u256);
+                assert(present);
+            },
+            [&] {
+                const std::optional<CMutableTransaction> mut_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+                if (!mut_tx) {
+                    return;
+                }
+                const CTransaction tx{*mut_tx};
+                (void)bloom_filter.IsRelevantAndUpdate(tx);
+            });
         (void)bloom_filter.IsWithinSizeConstraints();
     }
 }
diff --git a/src/test/fuzz/buffered_file.cpp b/src/test/fuzz/buffered_file.cpp
index e5a987783936..b32a59986aaa 100644
--- a/src/test/fuzz/buffered_file.cpp
+++ b/src/test/fuzz/buffered_file.cpp
@@ -30,41 +30,36 @@ FUZZ_TARGET(buffered_file)
     if (opt_buffered_file && fuzzed_file != nullptr) {
         bool setpos_fail = false;
         while (fuzzed_data_provider.ConsumeBool()) {
-            switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 4)) {
-            case 0: {
-                std::array<uint8_t, 4096> arr{};
-                try {
-                    opt_buffered_file->read((char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
-                } catch (const std::ios_base::failure&) {
-                }
-                break;
-            }
-            case 1: {
-                opt_buffered_file->SetLimit(fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(0, 4096));
-                break;
-            }
-            case 2: {
-                if (!opt_buffered_file->SetPos(fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(0, 4096))) {
-                    setpos_fail = true;
-                }
-                break;
-            }
-            case 3: {
-                if (setpos_fail) {
-                    // Calling FindByte(...) after a failed SetPos(...) call may result in an infinite loop.
-                    break;
-                }
-                try {
-                    opt_buffered_file->FindByte(fuzzed_data_provider.ConsumeIntegral<char>());
-                } catch (const std::ios_base::failure&) {
-                }
-                break;
-            }
-            case 4: {
-                ReadFromStream(fuzzed_data_provider, *opt_buffered_file);
-                break;
-            }
-            }
+            CallOneOf(
+                fuzzed_data_provider,
+                [&] {
+                    std::array<uint8_t, 4096> arr{};
+                    try {
+                        opt_buffered_file->read((char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+                    } catch (const std::ios_base::failure&) {
+                    }
+                },
+                [&] {
+                    opt_buffered_file->SetLimit(fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(0, 4096));
+                },
+                [&] {
+                    if (!opt_buffered_file->SetPos(fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(0, 4096))) {
+                        setpos_fail = true;
+                    }
+                },
+                [&] {
+                    if (setpos_fail) {
+                        // Calling FindByte(...) after a failed SetPos(...) call may result in an infinite loop.
+                        return;
+                    }
+                    try {
+                        opt_buffered_file->FindByte(fuzzed_data_provider.ConsumeIntegral<char>());
+                    } catch (const std::ios_base::failure&) {
+                    }
+                },
+                [&] {
+                    ReadFromStream(fuzzed_data_provider, *opt_buffered_file);
+                });
         }
         opt_buffered_file->GetPos();
         opt_buffered_file->GetType();
diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
index e3781d057a28..748c4c29a734 100644
--- a/src/test/fuzz/coins_view.cpp
+++ b/src/test/fuzz/coins_view.cpp
@@ -51,103 +51,93 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
     Coin random_coin;
     CMutableTransaction random_mutable_transaction;
     while (fuzzed_data_provider.ConsumeBool()) {
-        switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 9)) {
-        case 0: {
-            if (random_coin.IsSpent()) {
-                break;
-            }
-            Coin coin = random_coin;
-            bool expected_code_path = false;
-            const bool possible_overwrite = fuzzed_data_provider.ConsumeBool();
-            try {
-                coins_view_cache.AddCoin(random_out_point, std::move(coin), possible_overwrite);
-                expected_code_path = true;
-            } catch (const std::logic_error& e) {
-                if (e.what() == std::string{"Attempted to overwrite an unspent coin (when possible_overwrite is false)"}) {
-                    assert(!possible_overwrite);
+        CallOneOf(
+            fuzzed_data_provider,
+            [&] {
+                if (random_coin.IsSpent()) {
+                    return;
+                }
+                Coin coin = random_coin;
+                bool expected_code_path = false;
+                const bool possible_overwrite = fuzzed_data_provider.ConsumeBool();
+                try {
+                    coins_view_cache.AddCoin(random_out_point, std::move(coin), possible_overwrite);
                     expected_code_path = true;
+                } catch (const std::logic_error& e) {
+                    if (e.what() == std::string{"Attempted to overwrite an unspent coin (when possible_overwrite is false)"}) {
+                        assert(!possible_overwrite);
+                        expected_code_path = true;
+                    }
                 }
-            }
-            assert(expected_code_path);
-            break;
-        }
-        case 1: {
-            (void)coins_view_cache.Flush();
-            break;
-        }
-        case 2: {
-            coins_view_cache.SetBestBlock(ConsumeUInt256(fuzzed_data_provider));
-            break;
-        }
-        case 3: {
-            Coin move_to;
-            (void)coins_view_cache.SpendCoin(random_out_point, fuzzed_data_provider.ConsumeBool() ? &move_to : nullptr);
-            break;
-        }
-        case 4: {
-            coins_view_cache.Uncache(random_out_point);
-            break;
-        }
-        case 5: {
-            if (fuzzed_data_provider.ConsumeBool()) {
-                backend_coins_view = CCoinsView{};
-            }
-            coins_view_cache.SetBackend(backend_coins_view);
-            break;
-        }
-        case 6: {
-            const std::optional<COutPoint> opt_out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
-            if (!opt_out_point) {
-                break;
-            }
-            random_out_point = *opt_out_point;
-            break;
-        }
-        case 7: {
-            const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider);
-            if (!opt_coin) {
-                break;
-            }
-            random_coin = *opt_coin;
-            break;
-        }
-        case 8: {
-            const std::optional<CMutableTransaction> opt_mutable_transaction = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
-            if (!opt_mutable_transaction) {
-                break;
-            }
-            random_mutable_transaction = *opt_mutable_transaction;
-            break;
-        }
-        case 9: {
-            CCoinsMap coins_map;
-            while (fuzzed_data_provider.ConsumeBool()) {
-                CCoinsCacheEntry coins_cache_entry;
-                coins_cache_entry.flags = fuzzed_data_provider.ConsumeIntegral<unsigned char>();
+                assert(expected_code_path);
+            },
+            [&] {
+                (void)coins_view_cache.Flush();
+            },
+            [&] {
+                coins_view_cache.SetBestBlock(ConsumeUInt256(fuzzed_data_provider));
+            },
+            [&] {
+                Coin move_to;
+                (void)coins_view_cache.SpendCoin(random_out_point, fuzzed_data_provider.ConsumeBool() ? &move_to : nullptr);
+            },
+            [&] {
+                coins_view_cache.Uncache(random_out_point);
+            },
+            [&] {
                 if (fuzzed_data_provider.ConsumeBool()) {
-                    coins_cache_entry.coin = random_coin;
-                } else {
-                    const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider);
-                    if (!opt_coin) {
-                        break;
+                    backend_coins_view = CCoinsView{};
+                }
+                coins_view_cache.SetBackend(backend_coins_view);
+            },
+            [&] {
+                const std::optional<COutPoint> opt_out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
+                if (!opt_out_point) {
+                    return;
+                }
+                random_out_point = *opt_out_point;
+            },
+            [&] {
+                const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider);
+                if (!opt_coin) {
+                    return;
+                }
+                random_coin = *opt_coin;
+            },
+            [&] {
+                const std::optional<CMutableTransaction> opt_mutable_transaction = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+                if (!opt_mutable_transaction) {
+                    return;
+                }
+                random_mutable_transaction = *opt_mutable_transaction;
+            },
+            [&] {
+                CCoinsMap coins_map;
+                while (fuzzed_data_provider.ConsumeBool()) {
+                    CCoinsCacheEntry coins_cache_entry;
+                    coins_cache_entry.flags = fuzzed_data_provider.ConsumeIntegral<unsigned char>();
+                    if (fuzzed_data_provider.ConsumeBool()) {
+                        coins_cache_entry.coin = random_coin;
+                    } else {
+                        const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider);
+                        if (!opt_coin) {
+                            return;
+                        }
+                        coins_cache_entry.coin = *opt_coin;
                     }
-                    coins_cache_entry.coin = *opt_coin;
+                    coins_map.emplace(random_out_point, std::move(coins_cache_entry));
                 }
-                coins_map.emplace(random_out_point, std::move(coins_cache_entry));
-            }
-            bool expected_code_path = false;
-            try {
-                coins_view_cache.BatchWrite(coins_map, fuzzed_data_provider.ConsumeBool() ? ConsumeUInt256(fuzzed_data_provider) : coins_view_cache.GetBestBlock());
-                expected_code_path = true;
-            } catch (const std::logic_error& e) {
-                if (e.what() == std::string{"FRESH flag misapplied to coin that exists in parent cache"}) {
+                bool expected_code_path = false;
+                try {
+                    coins_view_cache.BatchWrite(coins_map, fuzzed_data_provider.ConsumeBool() ? ConsumeUInt256(fuzzed_data_provider) : coins_view_cache.GetBestBlock());
                     expected_code_path = true;
+                } catch (const std::logic_error& e) {
+                    if (e.what() == std::string{"FRESH flag misapplied to coin that exists in parent cache"}) {
+                        expected_code_path = true;
+                    }
                 }
-            }
-            assert(expected_code_path);
-            break;
-        }
-        }
+                assert(expected_code_path);
+            });
     }
 
     {
@@ -200,92 +190,86 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
     }
 
     if (fuzzed_data_provider.ConsumeBool()) {
-        switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 5)) {
-        case 0: {
-            const CTransaction transaction{random_mutable_transaction};
-            bool is_spent = false;
-            for (const CTxOut& tx_out : transaction.vout) {
-                if (Coin{tx_out, 0, transaction.IsCoinBase()}.IsSpent()) {
-                    is_spent = true;
+        CallOneOf(
+            fuzzed_data_provider,
+            [&] {
+                const CTransaction transaction{random_mutable_transaction};
+                bool is_spent = false;
+                for (const CTxOut& tx_out : transaction.vout) {
+                    if (Coin{tx_out, 0, transaction.IsCoinBase()}.IsSpent()) {
+                        is_spent = true;
+                    }
+                }
+                if (is_spent) {
+                    // Avoid:
+                    // coins.cpp:69: void CCoinsViewCache::AddCoin(const COutPoint &, Coin &&, bool): Assertion `!coin.IsSpent()' failed.
+                    return;
                 }
-            }
-            if (is_spent) {
-                // Avoid:
-                // coins.cpp:69: void CCoinsViewCache::AddCoin(const COutPoint &, Coin &&, bool): Assertion `!coin.IsSpent()' failed.
-                break;
-            }
-            bool expected_code_path = false;
-            const int height = fuzzed_data_provider.ConsumeIntegral<int>();
-            const bool possible_overwrite = fuzzed_data_provider.ConsumeBool();
-            try {
-                AddCoins(coins_view_cache, transaction, height, possible_overwrite);
-                expected_code_path = true;
-            } catch (const std::logic_error& e) {
-                if (e.what() == std::string{"Attempted to overwrite an unspent coin (when possible_overwrite is false)"}) {
-                    assert(!possible_overwrite);
+                bool expected_code_path = false;
+                const int height = fuzzed_data_provider.ConsumeIntegral<int>();
+                const bool possible_overwrite = fuzzed_data_provider.ConsumeBool();
+                try {
+                    AddCoins(coins_view_cache, transaction, height, possible_overwrite);
                     expected_code_path = true;
+                } catch (const std::logic_error& e) {
+                    if (e.what() == std::string{"Attempted to overwrite an unspent coin (when possible_overwrite is false)"}) {
+                        assert(!possible_overwrite);
+                        expected_code_path = true;
+                    }
                 }
-            }
-            assert(expected_code_path);
-            break;
-        }
-        case 1: {
-            (void)AreInputsStandard(CTransaction{random_mutable_transaction}, coins_view_cache);
-            break;
-        }
-        case 2: {
-            CValidationState state;
-            CAmount tx_fee_out;
-            const CTransaction transaction{random_mutable_transaction};
-            if (ContainsSpentInput(transaction, coins_view_cache)) {
-                // Avoid:
-                // consensus/tx_verify.cpp:171: bool Consensus::CheckTxInputs(const CTransaction &, CValidationState &, const CCoinsViewCache &, int, CAmount &): Assertion `!coin.IsSpent()' failed.
-                break;
-            }
-            try {
-                (void)Consensus::CheckTxInputs(transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, std::numeric_limits<int>::max()), tx_fee_out);
-                assert(MoneyRange(tx_fee_out));
-            } catch (const std::runtime_error&) {
-            }
-            break;
-        }
-        case 3: {
-            const CTransaction transaction{random_mutable_transaction};
-            if (ContainsSpentInput(transaction, coins_view_cache)) {
-                // Avoid:
-                // consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed.
-                break;
-            }
-            (void)GetP2SHSigOpCount(transaction, coins_view_cache);
-            break;
-        }
-        case 4: {
-            const CTransaction transaction{random_mutable_transaction};
-            if (ContainsSpentInput(transaction, coins_view_cache)) {
-                // Avoid:
-                // consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed.
-                break;
-            }
-            const int flags = fuzzed_data_provider.ConsumeIntegral<int>();
-            if (!transaction.vin.empty() && (flags & SCRIPT_VERIFY_P2SH) == 0) {
-                // Avoid:
-                // script/interpreter.cpp:1705: size_t CountWitnessSigOps(const CScript &, const CScript &, const CScriptWitness *, unsigned int): Assertion `(flags & SCRIPT_VERIFY_P2SH) != 0' failed.
-                break;
-            }
-            (void)GetTransactionSigOpCount(transaction, coins_view_cache, flags);
-            break;
-        }
-        case 5: {
-            CCoinsStats stats;
-            bool expected_code_path = false;
-            try {
-                (void)GetUTXOStats(&coins_view_cache, stats, CoinStatsHashType::HASH_SERIALIZED);
-            } catch (const std::logic_error&) {
-                expected_code_path = true;
-            }
-            assert(expected_code_path);
-            break;
-        }
-        }
+                assert(expected_code_path);
+            },
+            [&] {
+                (void)AreInputsStandard(CTransaction{random_mutable_transaction}, coins_view_cache);
+            },
+            [&] {
+                CValidationState state;
+                CAmount tx_fee_out;
+                const CTransaction transaction{random_mutable_transaction};
+                if (ContainsSpentInput(transaction, coins_view_cache)) {
+                    // Avoid:
+                    // consensus/tx_verify.cpp:171: bool Consensus::CheckTxInputs(const CTransaction &, CValidationState &, const CCoinsViewCache &, int, CAmount &): Assertion `!coin.IsSpent()' failed.
+                    return;
+                }
+                try {
+                    (void)Consensus::CheckTxInputs(transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, std::numeric_limits<int>::max()), tx_fee_out);
+                    assert(MoneyRange(tx_fee_out));
+                } catch (const std::runtime_error&) {
+                }
+            },
+            [&] {
+                const CTransaction transaction{random_mutable_transaction};
+                if (ContainsSpentInput(transaction, coins_view_cache)) {
+                    // Avoid:
+                    // consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed.
+                    return;
+                }
+                (void)GetP2SHSigOpCount(transaction, coins_view_cache);
+            },
+            [&] {
+                const CTransaction transaction{random_mutable_transaction};
+                if (ContainsSpentInput(transaction, coins_view_cache)) {
+                    // Avoid:
+                    // consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed.
+                    return;
+                }
+                const int flags = fuzzed_data_provider.ConsumeIntegral<int>();
+                if (!transaction.vin.empty() && (flags & SCRIPT_VERIFY_P2SH) == 0) {
+                    // Avoid:
+                    // script/interpreter.cpp:1705: size_t CountWitnessSigOps(const CScript &, const CScript &, const CScriptWitness *, unsigned int): Assertion `(flags & SCRIPT_VERIFY_P2SH) != 0' failed.
+                    return;
+                }
+                (void)GetTransactionSigOpCount(transaction, coins_view_cache, flags);
+            },
+            [&] {
+                CCoinsStats stats;
+                bool expected_code_path = false;
+                try {
+                    (void)GetUTXOStats(&coins_view_cache, stats, CoinStatsHashType::HASH_SERIALIZED);
+                } catch (const std::logic_error&) {
+                    expected_code_path = true;
+                }
+                assert(expected_code_path);
+            });
     }
 }
diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp
index f2c52f7a37b6..13de7604bd37 100644
--- a/src/test/fuzz/connman.cpp
+++ b/src/test/fuzz/connman.cpp
@@ -30,90 +30,87 @@ FUZZ_TARGET_INIT(connman, initialize_connman)
     CSubNet random_subnet;
     std::string random_string;
     while (fuzzed_data_provider.ConsumeBool()) {
-        switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 23)) {
-        case 0:
-            random_netaddr = ConsumeNetAddr(fuzzed_data_provider);
-            break;
-        case 1:
-            random_subnet = ConsumeSubNet(fuzzed_data_provider);
-            break;
-        case 2:
-            random_string = fuzzed_data_provider.ConsumeRandomLengthString(64);
-            break;
-        case 3:
-            connman.AddNode(random_string);
-            break;
-        case 4:
-            connman.CheckIncomingNonce(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
-            break;
-        case 5:
-            connman.DisconnectNode(fuzzed_data_provider.ConsumeIntegral<NodeId>());
-            break;
-        case 6:
-            connman.DisconnectNode(random_netaddr);
-            break;
-        case 7:
-            connman.DisconnectNode(random_string);
-            break;
-        case 8:
-            connman.DisconnectNode(random_subnet);
-            break;
-        case 9:
-            connman.ForEachNode([](auto) {});
-            break;
-        case 10:
-            connman.ForEachNodeThen([](auto) {}, []() {});
-            break;
-        case 11:
-            (void)connman.ForNode(fuzzed_data_provider.ConsumeIntegral<NodeId>(), [&](auto) { return fuzzed_data_provider.ConsumeBool(); });
-            break;
-        case 12:
-            (void)connman.GetAddresses();
-            break;
-        case 13: {
-            (void)connman.GetAddresses();
-            break;
-        }
-        case 14:
-            (void)connman.GetDeterministicRandomizer(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
-            break;
-        case 15:
-            (void)connman.GetNodeCount(fuzzed_data_provider.PickValueInArray({CConnman::CONNECTIONS_NONE, CConnman::CONNECTIONS_IN, CConnman::CONNECTIONS_OUT, CConnman::CONNECTIONS_ALL}));
-            break;
-        case 16:
-            (void)connman.OutboundTargetReached(fuzzed_data_provider.ConsumeBool());
-            break;
-        case 17:
-            // Limit now to int32_t to avoid signed integer overflow
-            (void)connman.PoissonNextSendInbound(fuzzed_data_provider.ConsumeIntegral<int32_t>(), fuzzed_data_provider.ConsumeIntegral<int>());
-            break;
-        case 18: {
-            CSerializedNetMsg serialized_net_msg;
-            serialized_net_msg.command = fuzzed_data_provider.ConsumeRandomLengthString(CMessageHeader::COMMAND_SIZE);
-            serialized_net_msg.data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
-            connman.PushMessage(&random_node, std::move(serialized_net_msg));
-            break;
-        }
-        case 19:
-            connman.RemoveAddedNode(random_string);
-            break;
-        case 20: {
-            const std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider);
-            if (SanityCheckASMap(asmap)) {
-                connman.SetAsmap(asmap);
-            }
-            break;
-        }
-        case 21:
-            connman.SetBestHeight(fuzzed_data_provider.ConsumeIntegral<int>());
-            break;
-        case 22:
-            connman.SetNetworkActive(fuzzed_data_provider.ConsumeBool());
-            break;
-        case 23:
-            connman.SetTryNewOutboundPeer(fuzzed_data_provider.ConsumeBool());
-            break;
-        }
+        CallOneOf(
+            fuzzed_data_provider,
+            [&] {
+                random_netaddr = ConsumeNetAddr(fuzzed_data_provider);
+            },
+            [&] {
+                random_subnet = ConsumeSubNet(fuzzed_data_provider);
+            },
+            [&] {
+                random_string = fuzzed_data_provider.ConsumeRandomLengthString(64);
+            },
+            [&] {
+                connman.AddNode(random_string);
+            },
+            [&] {
+                connman.CheckIncomingNonce(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
+            },
+            [&] {
+                connman.DisconnectNode(fuzzed_data_provider.ConsumeIntegral<NodeId>());
+            },
+            [&] {
+                connman.DisconnectNode(random_netaddr);
+            },
+            [&] {
+                connman.DisconnectNode(random_string);
+            },
+            [&] {
+                connman.DisconnectNode(random_subnet);
+            },
+            [&] {
+                connman.ForEachNode([](auto) {});
+            },
+            [&] {
+                connman.ForEachNodeThen([](auto) {}, []() {});
+            },
+            [&] {
+                (void)connman.ForNode(fuzzed_data_provider.ConsumeIntegral<NodeId>(), [&](auto) { return fuzzed_data_provider.ConsumeBool(); });
+            },
+            [&] {
+                (void)connman.GetAddresses();
+            },
+            [&] {
+                (void)connman.GetAddresses();
+            },
+            [&] {
+                (void)connman.GetDeterministicRandomizer(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
+            },
+            [&] {
+                (void)connman.GetNodeCount(fuzzed_data_provider.PickValueInArray({CConnman::CONNECTIONS_NONE, CConnman::CONNECTIONS_IN, CConnman::CONNECTIONS_OUT, CConnman::CONNECTIONS_ALL}));
+            },
+            [&] {
+                (void)connman.OutboundTargetReached(fuzzed_data_provider.ConsumeBool());
+            },
+            [&] {
+                // Limit now to int32_t to avoid signed integer overflow
+                (void)connman.PoissonNextSendInbound(fuzzed_data_provider.ConsumeIntegral<int32_t>(), fuzzed_data_provider.ConsumeIntegral<int>());
+            },
+            [&] {
+                CSerializedNetMsg serialized_net_msg;
+                serialized_net_msg.command = fuzzed_data_provider.ConsumeRandomLengthString(CMessageHeader::COMMAND_SIZE);
+                serialized_net_msg.data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+                connman.PushMessage(&random_node, std::move(serialized_net_msg));
+            },
+            [&] {
+                connman.RemoveAddedNode(random_string);
+            },
+            [&] {
+                const std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider);
+                if (SanityCheckASMap(asmap)) {
+                    connman.SetAsmap(asmap);
+                }
+            },
+            [&] {
+                connman.SetBestHeight(fuzzed_data_provider.ConsumeIntegral<int>());
+            },
+            [&] {
+                connman.SetNetworkActive(fuzzed_data_provider.ConsumeBool());
+            },
+            [&] {
+                connman.SetTryNewOutboundPeer(fuzzed_data_provider.ConsumeBool());
+            });
     }
     (void)connman.GetAddedNodeInfo();
     (void)connman.GetBestHeight();
diff --git a/src/test/fuzz/crypto.cpp b/src/test/fuzz/crypto.cpp
index 08cfa823a91c..901378d66079 100644
--- a/src/test/fuzz/crypto.cpp
+++ b/src/test/fuzz/crypto.cpp
@@ -37,97 +37,84 @@ FUZZ_TARGET(crypto)
     CSipHasher sip_hasher{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
 
     while (fuzzed_data_provider.ConsumeBool()) {
-        switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 2)) {
-        case 0: {
-            if (fuzzed_data_provider.ConsumeBool()) {
-                data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
-                if (data.empty()) {
-                    data.resize(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096), fuzzed_data_provider.ConsumeIntegral<uint8_t>());
+        CallOneOf(
+            fuzzed_data_provider,
+            [&] {
+                if (fuzzed_data_provider.ConsumeBool()) {
+                    data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+                    if (data.empty()) {
+                        data.resize(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096), fuzzed_data_provider.ConsumeIntegral<uint8_t>());
+                    }
                 }
-            }
 
-            (void)hash160.Write(data);
-            (void)hash256.Write(data);
-            (void)hmac_sha256.Write(data.data(), data.size());
-            (void)hmac_sha512.Write(data.data(), data.size());
-            (void)ripemd160.Write(data.data(), data.size());
-            (void)sha1.Write(data.data(), data.size());
-            (void)sha256.Write(data.data(), data.size());
-            (void)sha3.Write(data);
-            (void)sha512.Write(data.data(), data.size());
-            (void)sip_hasher.Write(data.data(), data.size());
+                (void)hash160.Write(data);
+                (void)hash256.Write(data);
+                (void)hmac_sha256.Write(data.data(), data.size());
+                (void)hmac_sha512.Write(data.data(), data.size());
+                (void)ripemd160.Write(data.data(), data.size());
+                (void)sha1.Write(data.data(), data.size());
+                (void)sha256.Write(data.data(), data.size());
+                (void)sha3.Write(data);
+                (void)sha512.Write(data.data(), data.size());
+                (void)sip_hasher.Write(data.data(), data.size());
 
-            (void)Hash160(data);
-            (void)Hash160(data.begin(), data.end());
-            (void)sha512.Size();
-            break;
-        }
-        case 1: {
-            (void)hash160.Reset();
-            (void)hash256.Reset();
-            (void)ripemd160.Reset();
-            (void)sha1.Reset();
-            (void)sha256.Reset();
-            (void)sha3.Reset();
-            (void)sha512.Reset();
-            break;
-        }
-        case 2: {
-            switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 9)) {
-            case 0: {
-                data.resize(CHash160::OUTPUT_SIZE);
-                hash160.Finalize(data);
-                break;
-            }
-            case 1: {
-                data.resize(CHash256::OUTPUT_SIZE);
-                hash256.Finalize(data);
-                break;
-            }
-            case 2: {
-                data.resize(CHMAC_SHA256::OUTPUT_SIZE);
-                hmac_sha256.Finalize(data.data());
-                break;
-            }
-            case 3: {
-                data.resize(CHMAC_SHA512::OUTPUT_SIZE);
-                hmac_sha512.Finalize(data.data());
-                break;
-            }
-            case 4: {
-                data.resize(CRIPEMD160::OUTPUT_SIZE);
-                ripemd160.Finalize(data.data());
-                break;
-            }
-            case 5: {
-                data.resize(CSHA1::OUTPUT_SIZE);
-                sha1.Finalize(data.data());
-                break;
-            }
-            case 6: {
-                data.resize(CSHA256::OUTPUT_SIZE);
-                sha256.Finalize(data.data());
-                break;
-            }
-            case 7: {
-                data.resize(CSHA512::OUTPUT_SIZE);
-                sha512.Finalize(data.data());
-                break;
-            }
-            case 8: {
-                data.resize(1);
-                data[0] = sip_hasher.Finalize() % 256;
-                break;
-            }
-            case 9: {
-                data.resize(SHA3_256::OUTPUT_SIZE);
-                sha3.Finalize(data);
-                break;
-            }
-            }
-            break;
-        }
-        }
+                (void)Hash160(data);
+                (void)Hash160(data.begin(), data.end());
+                (void)sha512.Size();
+            },
+            [&] {
+                (void)hash160.Reset();
+                (void)hash256.Reset();
+                (void)ripemd160.Reset();
+                (void)sha1.Reset();
+                (void)sha256.Reset();
+                (void)sha3.Reset();
+                (void)sha512.Reset();
+            },
+            [&] {
+                CallOneOf(
+                    fuzzed_data_provider,
+                    [&] {
+                        data.resize(CHash160::OUTPUT_SIZE);
+                        hash160.Finalize(data);
+                    },
+                    [&] {
+                        data.resize(CHash256::OUTPUT_SIZE);
+                        hash256.Finalize(data);
+                    },
+                    [&] {
+                        data.resize(CHMAC_SHA256::OUTPUT_SIZE);
+                        hmac_sha256.Finalize(data.data());
+                    },
+                    [&] {
+                        data.resize(CHMAC_SHA512::OUTPUT_SIZE);
+                        hmac_sha512.Finalize(data.data());
+                    },
+                    [&] {
+                        data.resize(CRIPEMD160::OUTPUT_SIZE);
+                        ripemd160.Finalize(data.data());
+                    },
+                    [&] {
+                        data.resize(CSHA1::OUTPUT_SIZE);
+                        sha1.Finalize(data.data());
+                    },
+                    [&] {
+                        data.resize(CSHA256::OUTPUT_SIZE);
+                        sha256.Finalize(data.data());
+                    },
+                    [&] {
+                        data.resize(CSHA512::OUTPUT_SIZE);
+                        sha512.Finalize(data.data());
+                    },
+                    [&] {
+                        data.resize(1);
+                        data[0] = sip_hasher.Finalize() % 256;
+                    },
+                    [&] {
+                        data.resize(SHA3_256::OUTPUT_SIZE);
+                        sha3.Finalize(data);
+                    });
+                });
     }
     if (fuzzed_data_provider.ConsumeBool()) {
         uint64_t state[25];
diff --git a/src/test/fuzz/crypto_chacha20.cpp b/src/test/fuzz/crypto_chacha20.cpp
index d751466f1157..bb8dd4594ff9 100644
--- a/src/test/fuzz/crypto_chacha20.cpp
+++ b/src/test/fuzz/crypto_chacha20.cpp
@@ -20,31 +20,26 @@ FUZZ_TARGET(crypto_chacha20)
         chacha20 = ChaCha20{key.data(), key.size()};
     }
     while (fuzzed_data_provider.ConsumeBool()) {
-        switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 4)) {
-        case 0: {
-            const std::vector<unsigned char> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, fuzzed_data_provider.ConsumeIntegralInRange<size_t>(16, 32));
-            chacha20.SetKey(key.data(), key.size());
-            break;
-        }
-        case 1: {
-            chacha20.SetIV(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
-            break;
-        }
-        case 2: {
-            chacha20.Seek(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
-            break;
-        }
-        case 3: {
-            std::vector<uint8_t> output(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
-            chacha20.Keystream(output.data(), output.size());
-            break;
-        }
-        case 4: {
-            std::vector<uint8_t> output(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
-            const std::vector<uint8_t> input = ConsumeFixedLengthByteVector(fuzzed_data_provider, output.size());
-            chacha20.Crypt(input.data(), output.data(), input.size());
-            break;
-        }
-        }
+        CallOneOf(
+            fuzzed_data_provider,
+            [&] {
+                const std::vector<unsigned char> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, fuzzed_data_provider.ConsumeIntegralInRange<size_t>(16, 32));
+                chacha20.SetKey(key.data(), key.size());
+            },
+            [&] {
+                chacha20.SetIV(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
+            },
+            [&] {
+                chacha20.Seek(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
+            },
+            [&] {
+                std::vector<uint8_t> output(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+                chacha20.Keystream(output.data(), output.size());
+            },
+            [&] {
+                std::vector<uint8_t> output(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+                const std::vector<uint8_t> input = ConsumeFixedLengthByteVector(fuzzed_data_provider, output.size());
+                chacha20.Crypt(input.data(), output.data(), input.size());
+            });
     }
 }
diff --git a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp
index 631af9c70dd0..1f122082b21d 100644
--- a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp
+++ b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp
@@ -29,44 +29,37 @@ FUZZ_TARGET(crypto_chacha20_poly1305_aead)
     std::vector<uint8_t> out(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
     bool is_encrypt = fuzzed_data_provider.ConsumeBool();
     while (fuzzed_data_provider.ConsumeBool()) {
-        switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 6)) {
-        case 0: {
-            buffer_size = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(64, 4096);
-            in = std::vector<uint8_t>(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
-            out = std::vector<uint8_t>(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
-            break;
-        }
-        case 1: {
-            (void)aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffer_size, is_encrypt);
-            break;
-        }
-        case 2: {
-            uint32_t len = 0;
-            const bool ok = aead.GetLength(&len, seqnr_aad, aad_pos, in.data());
-            assert(ok);
-            break;
-        }
-        case 3: {
-            seqnr_payload += 1;
-            aad_pos += CHACHA20_POLY1305_AEAD_AAD_LEN;
-            if (aad_pos + CHACHA20_POLY1305_AEAD_AAD_LEN > CHACHA20_ROUND_OUTPUT) {
-                aad_pos = 0;
-                seqnr_aad += 1;
-            }
-            break;
-        }
-        case 4: {
-            seqnr_payload = fuzzed_data_provider.ConsumeIntegral<int>();
-            break;
-        }
-        case 5: {
-            seqnr_aad = fuzzed_data_provider.ConsumeIntegral<int>();
-            break;
-        }
-        case 6: {
-            is_encrypt = fuzzed_data_provider.ConsumeBool();
-            break;
-        }
-        }
+        CallOneOf(
+            fuzzed_data_provider,
+            [&] {
+                buffer_size = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(64, 4096);
+                in = std::vector<uint8_t>(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
+                out = std::vector<uint8_t>(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
+            },
+            [&] {
+                (void)aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffer_size, is_encrypt);
+            },
+            [&] {
+                uint32_t len = 0;
+                const bool ok = aead.GetLength(&len, seqnr_aad, aad_pos, in.data());
+                assert(ok);
+            },
+            [&] {
+                seqnr_payload += 1;
+                aad_pos += CHACHA20_POLY1305_AEAD_AAD_LEN;
+                if (aad_pos + CHACHA20_POLY1305_AEAD_AAD_LEN > CHACHA20_ROUND_OUTPUT) {
+                    aad_pos = 0;
+                    seqnr_aad += 1;
+                }
+            },
+            [&] {
+                seqnr_payload = fuzzed_data_provider.ConsumeIntegral<int>();
+            },
+            [&] {
+                seqnr_aad = fuzzed_data_provider.ConsumeIntegral<int>();
+            },
+            [&] {
+                is_encrypt = fuzzed_data_provider.ConsumeBool();
+            });
     }
 }
diff --git a/src/test/fuzz/merkleblock.cpp b/src/test/fuzz/merkleblock.cpp
index 15bcfab3ad80..23e0baa56458 100644
--- a/src/test/fuzz/merkleblock.cpp
+++ b/src/test/fuzz/merkleblock.cpp
@@ -17,33 +17,31 @@ FUZZ_TARGET(merkleblock)
 {
     FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
     CPartialMerkleTree partial_merkle_tree;
-    switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 1)) {
-    case 0: {
-        const std::optional<CPartialMerkleTree> opt_partial_merkle_tree = ConsumeDeserializable<CPartialMerkleTree>(fuzzed_data_provider);
-        if (opt_partial_merkle_tree) {
-            partial_merkle_tree = *opt_partial_merkle_tree;
-        }
-        break;
-    }
-    case 1: {
-        CMerkleBlock merkle_block;
-        const std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider);
-        CBloomFilter bloom_filter;
-        std::set<uint256> txids;
-        if (opt_block && !opt_block->vtx.empty()) {
-            if (fuzzed_data_provider.ConsumeBool()) {
-                merkle_block = CMerkleBlock{*opt_block, bloom_filter};
-            } else if (fuzzed_data_provider.ConsumeBool()) {
-                while (fuzzed_data_provider.ConsumeBool()) {
-                    txids.insert(ConsumeUInt256(fuzzed_data_provider));
+    CallOneOf(
+        fuzzed_data_provider,
+        [&] {
+            const std::optional<CPartialMerkleTree> opt_partial_merkle_tree = ConsumeDeserializable<CPartialMerkleTree>(fuzzed_data_provider);
+            if (opt_partial_merkle_tree) {
+                partial_merkle_tree = *opt_partial_merkle_tree;
+            }
+        },
+        [&] {
+            CMerkleBlock merkle_block;
+            const std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider);
+            CBloomFilter bloom_filter;
+            std::set<uint256> txids;
+            if (opt_block && !opt_block->vtx.empty()) {
+                if (fuzzed_data_provider.ConsumeBool()) {
+                    merkle_block = CMerkleBlock{*opt_block, bloom_filter};
+                } else if (fuzzed_data_provider.ConsumeBool()) {
+                    while (fuzzed_data_provider.ConsumeBool()) {
+                        txids.insert(ConsumeUInt256(fuzzed_data_provider));
+                    }
+                    merkle_block = CMerkleBlock{*opt_block, txids};
                 }
-                merkle_block = CMerkleBlock{*opt_block, txids};
             }
-        }
-        partial_merkle_tree = merkle_block.txn;
-        break;
-    }
-    }
+            partial_merkle_tree = merkle_block.txn;
+        });
     (void)partial_merkle_tree.GetNumTransactions();
     std::vector<uint256> matches;
     std::vector<unsigned int> indices;
diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp
index 7a32948f8b3c..8f44042dd9e8 100644
--- a/src/test/fuzz/net.cpp
+++ b/src/test/fuzz/net.cpp
@@ -50,99 +50,86 @@ FUZZ_TARGET_INIT(net, initialize_net)
                fuzzed_data_provider.ConsumeBool()
            };
     while (fuzzed_data_provider.ConsumeBool()) {
-        switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 12)) {
-        case 0: {
-            CAddrMan addrman;
-            CConnman connman{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>(), addrman};
-            node.CloseSocketDisconnect(&connman);
-            break;
-        }
-        case 1: {
-            node.MaybeSetAddrName(fuzzed_data_provider.ConsumeRandomLengthString(32));
-            break;
-        }
-        case 2: {
-            node.SetSendVersion(fuzzed_data_provider.ConsumeIntegral<int>());
-            break;
-        }
-        case 3: {
-            const std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider);
-            if (!SanityCheckASMap(asmap)) {
-                break;
-            }
-            CNodeStats stats;
-            node.copyStats(stats, asmap);
-            break;
-        }
-        case 4: {
-            node.SetRecvVersion(fuzzed_data_provider.ConsumeIntegral<int>());
-            break;
-        }
-        case 5: {
-            const CNode* add_ref_node = node.AddRef();
-            assert(add_ref_node == &node);
-            break;
-        }
-        case 6: {
-            if (node.GetRefCount() > 0) {
-                node.Release();
-            }
-            break;
-        }
-        case 7: {
-            // if (node.m_addr_known == nullptr) {
-            //     break;
-            // }
-            const std::optional<CAddress> addr_opt = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
-            if (!addr_opt) {
-                break;
-            }
-            node.AddAddressKnown(*addr_opt);
-            break;
-        }
-        case 8: {
-            // if (node.m_addr_known == nullptr) {
-            //     break;
-            // }
-            const std::optional<CAddress> addr_opt = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
-            if (!addr_opt) {
-                break;
-            }
-            FastRandomContext fast_random_context{ConsumeUInt256(fuzzed_data_provider)};
-            node.PushAddress(*addr_opt, fast_random_context);
-            break;
-        }
-        case 9: {
-            const std::optional<CInv> inv_opt = ConsumeDeserializable<CInv>(fuzzed_data_provider);
-            if (!inv_opt) {
-                break;
-            }
-            // node.AddKnownTx(inv_opt->hash);
-            break;
-        }
-        case 10: {
-            const std::optional<CInv> inv_opt = ConsumeDeserializable<CInv>(fuzzed_data_provider);
-            if (!inv_opt) {
-                break;
-            }
-            node.PushInventory(*inv_opt);
-            break;
-        }
-        case 11: {
-            const std::optional<CService> service_opt = ConsumeDeserializable<CService>(fuzzed_data_provider);
-            if (!service_opt) {
-                break;
-            }
-            node.SetAddrLocal(*service_opt);
-            break;
-        }
-        case 12: {
-            const std::vector<uint8_t> b = ConsumeRandomLengthByteVector(fuzzed_data_provider);
-            bool complete;
-            node.ReceiveMsgBytes((const char*)b.data(), b.size(), complete);
-            break;
-        }
-        }
+        CallOneOf(
+            fuzzed_data_provider,
+            [&] {
+                CAddrMan addrman;
+                CConnman connman{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>(), addrman};
+                node.CloseSocketDisconnect(&connman);
+            },
+            [&] {
+                node.MaybeSetAddrName(fuzzed_data_provider.ConsumeRandomLengthString(32));
+            },
+            [&] {
+                node.SetSendVersion(fuzzed_data_provider.ConsumeIntegral<int>());
+            },
+            [&] {
+                const std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider);
+                if (!SanityCheckASMap(asmap)) {
+                    return;
+                }
+                CNodeStats stats;
+                node.copyStats(stats, asmap);
+            },
+            [&] {
+                node.SetRecvVersion(fuzzed_data_provider.ConsumeIntegral<int>());
+            },
+            [&] {
+                const CNode* add_ref_node = node.AddRef();
+                assert(add_ref_node == &node);
+            },
+            [&] {
+                if (node.GetRefCount() > 0) {
+                    node.Release();
+                }
+            },
+            [&] {
+                // if (node.m_addr_known == nullptr) {
+                //     return;
+                // }
+                const std::optional<CAddress> addr_opt = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
+                if (!addr_opt) {
+                    return;
+                }
+                node.AddAddressKnown(*addr_opt);
+            },
+            [&] {
+                // if (node.m_addr_known == nullptr) {
+                //     return;
+                // }
+                const std::optional<CAddress> addr_opt = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
+                if (!addr_opt) {
+                    return;
+                }
+                FastRandomContext fast_random_context{ConsumeUInt256(fuzzed_data_provider)};
+                node.PushAddress(*addr_opt, fast_random_context);
+            },
+            [&] {
+                const std::optional<CInv> inv_opt = ConsumeDeserializable<CInv>(fuzzed_data_provider);
+                if (!inv_opt) {
+                    return;
+                }
+                // node.AddKnownTx(inv_opt->hash);
+            },
+            [&] {
+                const std::optional<CInv> inv_opt = ConsumeDeserializable<CInv>(fuzzed_data_provider);
+                if (!inv_opt) {
+                    return;
+                }
+                node.PushInventory(*inv_opt);
+            },
+            [&] {
+                const std::optional<CService> service_opt = ConsumeDeserializable<CService>(fuzzed_data_provider);
+                if (!service_opt) {
+                    return;
+                }
+                node.SetAddrLocal(*service_opt);
+            },
+            [&] {
+                const std::vector<uint8_t> b = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+                bool complete;
+                node.ReceiveMsgBytes((const char*)b.data(), b.size(), complete);
+            });
     }
 
     (void)node.GetAddrLocal();
diff --git a/src/test/fuzz/policy_estimator.cpp b/src/test/fuzz/policy_estimator.cpp
index 451d4a3b5b60..8c77391e99f7 100644
--- a/src/test/fuzz/policy_estimator.cpp
+++ b/src/test/fuzz/policy_estimator.cpp
@@ -19,46 +19,42 @@ FUZZ_TARGET(policy_estimator)
     FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
     CBlockPolicyEstimator block_policy_estimator;
     while (fuzzed_data_provider.ConsumeBool()) {
-        switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 3)) {
-        case 0: {
-            const std::optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
-            if (!mtx) {
-                break;
-            }
-            const CTransaction tx{*mtx};
-            block_policy_estimator.processTransaction(ConsumeTxMemPoolEntry(fuzzed_data_provider, tx), fuzzed_data_provider.ConsumeBool());
-            if (fuzzed_data_provider.ConsumeBool()) {
-                (void)block_policy_estimator.removeTx(tx.GetHash(), /* inBlock */ fuzzed_data_provider.ConsumeBool());
-            }
-            break;
-        }
-        case 1: {
-            std::vector<CTxMemPoolEntry> mempool_entries;
-            while (fuzzed_data_provider.ConsumeBool()) {
+        CallOneOf(
+            fuzzed_data_provider,
+            [&] {
                 const std::optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
                 if (!mtx) {
-                    break;
+                    return;
                 }
                 const CTransaction tx{*mtx};
-                mempool_entries.push_back(ConsumeTxMemPoolEntry(fuzzed_data_provider, tx));
-            }
-            std::vector<const CTxMemPoolEntry*> ptrs;
-            ptrs.reserve(mempool_entries.size());
-            for (const CTxMemPoolEntry& mempool_entry : mempool_entries) {
-                ptrs.push_back(&mempool_entry);
-            }
-            block_policy_estimator.processBlock(fuzzed_data_provider.ConsumeIntegral<unsigned int>(), ptrs);
-            break;
-        }
-        case 2: {
-            (void)block_policy_estimator.removeTx(ConsumeUInt256(fuzzed_data_provider), /* inBlock */ fuzzed_data_provider.ConsumeBool());
-            break;
-        }
-        case 3: {
-            block_policy_estimator.FlushUnconfirmed();
-            break;
-        }
-        }
+                block_policy_estimator.processTransaction(ConsumeTxMemPoolEntry(fuzzed_data_provider, tx), fuzzed_data_provider.ConsumeBool());
+                if (fuzzed_data_provider.ConsumeBool()) {
+                    (void)block_policy_estimator.removeTx(tx.GetHash(), /* inBlock */ fuzzed_data_provider.ConsumeBool());
+                }
+            },
+            [&] {
+                std::vector<CTxMemPoolEntry> mempool_entries;
+                while (fuzzed_data_provider.ConsumeBool()) {
+                    const std::optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+                    if (!mtx) {
+                        break;
+                    }
+                    const CTransaction tx{*mtx};
+                    mempool_entries.push_back(ConsumeTxMemPoolEntry(fuzzed_data_provider, tx));
+                }
+                std::vector<const CTxMemPoolEntry*> ptrs;
+                ptrs.reserve(mempool_entries.size());
+                for (const CTxMemPoolEntry& mempool_entry : mempool_entries) {
+                    ptrs.push_back(&mempool_entry);
+                }
+                block_policy_estimator.processBlock(fuzzed_data_provider.ConsumeIntegral<unsigned int>(), ptrs);
+            },
+            [&] {
+                (void)block_policy_estimator.removeTx(ConsumeUInt256(fuzzed_data_provider), /* inBlock */ fuzzed_data_provider.ConsumeBool());
+            },
+            [&] {
+                block_policy_estimator.FlushUnconfirmed();
+            });
         (void)block_policy_estimator.estimateFee(fuzzed_data_provider.ConsumeIntegral<int>());
         EstimationResult result;
         (void)block_policy_estimator.estimateRawFee(fuzzed_data_provider.ConsumeIntegral<int>(), fuzzed_data_provider.ConsumeFloatingPoint<double>(), fuzzed_data_provider.PickValueInArray({FeeEstimateHorizon::SHORT_HALFLIFE, FeeEstimateHorizon::MED_HALFLIFE, FeeEstimateHorizon::LONG_HALFLIFE}), fuzzed_data_provider.ConsumeBool() ? &result : nullptr);
diff --git a/src/test/fuzz/rolling_bloom_filter.cpp b/src/test/fuzz/rolling_bloom_filter.cpp
index 6087ee964a99..2a08b45aa324 100644
--- a/src/test/fuzz/rolling_bloom_filter.cpp
+++ b/src/test/fuzz/rolling_bloom_filter.cpp
@@ -22,29 +22,27 @@ FUZZ_TARGET(rolling_bloom_filter)
         fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, 1000),
         0.999 / fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, std::numeric_limits<unsigned int>::max())};
     while (fuzzed_data_provider.remaining_bytes() > 0) {
-        switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 2)) {
-        case 0: {
-            const std::vector<unsigned char> b = ConsumeRandomLengthByteVector(fuzzed_data_provider);
-            (void)rolling_bloom_filter.contains(b);
-            rolling_bloom_filter.insert(b);
-            const bool present = rolling_bloom_filter.contains(b);
-            assert(present);
-            break;
-        }
-        case 1: {
-            const std::optional<uint256> u256 = ConsumeDeserializable<uint256>(fuzzed_data_provider);
-            if (!u256) {
-                break;
-            }
-            (void)rolling_bloom_filter.contains(*u256);
-            rolling_bloom_filter.insert(*u256);
-            const bool present = rolling_bloom_filter.contains(*u256);
-            assert(present);
-            break;
-        }
-        case 2:
-            rolling_bloom_filter.reset();
-            break;
-        }
+        CallOneOf(
+            fuzzed_data_provider,
+            [&] {
+                const std::vector<unsigned char> b = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+                (void)rolling_bloom_filter.contains(b);
+                rolling_bloom_filter.insert(b);
+                const bool present = rolling_bloom_filter.contains(b);
+                assert(present);
+            },
+            [&] {
+                const std::optional<uint256> u256 = ConsumeDeserializable<uint256>(fuzzed_data_provider);
+                if (!u256) {
+                    return;
+                }
+                (void)rolling_bloom_filter.contains(*u256);
+                rolling_bloom_filter.insert(*u256);
+                const bool present = rolling_bloom_filter.contains(*u256);
+                assert(present);
+            },
+            [&] {
+                rolling_bloom_filter.reset();
+            });
     }
 }
diff --git a/src/test/fuzz/script_ops.cpp b/src/test/fuzz/script_ops.cpp
index 501e0c8ced90..569e85332652 100644
--- a/src/test/fuzz/script_ops.cpp
+++ b/src/test/fuzz/script_ops.cpp
@@ -16,49 +16,46 @@ FUZZ_TARGET(script_ops)
     FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
     CScript script = ConsumeScript(fuzzed_data_provider);
     while (fuzzed_data_provider.remaining_bytes() > 0) {
-        switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 7)) {
-        case 0: {
-            CScript s = ConsumeScript(fuzzed_data_provider);
-            script = std::move(s);
-            break;
-        }
-        case 1: {
-            const CScript& s = ConsumeScript(fuzzed_data_provider);
-            script = s;
-            break;
-        }
-        case 2:
-            script << fuzzed_data_provider.ConsumeIntegral<int64_t>();
-            break;
-        case 3:
-            script << ConsumeOpcodeType(fuzzed_data_provider);
-            break;
-        case 4:
-            script << ConsumeScriptNum(fuzzed_data_provider);
-            break;
-        case 5:
-            script << ConsumeRandomLengthByteVector(fuzzed_data_provider);
-            break;
-        case 6:
-            script.clear();
-            break;
-        case 7: {
-            (void)script.GetSigOpCount(false);
-            (void)script.GetSigOpCount(true);
-            (void)script.GetSigOpCount(script);
-            (void)script.IsPayToScriptHash();
-            (void)script.IsPushOnly();
-            (void)script.IsUnspendable();
-            {
-                CScript::const_iterator pc = script.begin();
-                opcodetype opcode;
-                (void)script.GetOp(pc, opcode);
-                std::vector<uint8_t> data;
-                (void)script.GetOp(pc, opcode, data);
-                (void)script.IsPushOnly(pc);
-            }
-            break;
-        }
-        }
+        CallOneOf(
+            fuzzed_data_provider,
+            [&] {
+                CScript s = ConsumeScript(fuzzed_data_provider);
+                script = std::move(s);
+            },
+            [&] {
+                const CScript& s = ConsumeScript(fuzzed_data_provider);
+                script = s;
+            },
+            [&] {
+                script << fuzzed_data_provider.ConsumeIntegral<int64_t>();
+            },
+            [&] {
+                script << ConsumeOpcodeType(fuzzed_data_provider);
+            },
+            [&] {
+                script << ConsumeScriptNum(fuzzed_data_provider);
+            },
+            [&] {
+                script << ConsumeRandomLengthByteVector(fuzzed_data_provider);
+            },
+            [&] {
+                script.clear();
+            },
+            [&] {
+                (void)script.GetSigOpCount(false);
+                (void)script.GetSigOpCount(true);
+                (void)script.GetSigOpCount(script);
+                (void)script.IsPayToScriptHash();
+                (void)script.IsPushOnly();
+                (void)script.IsUnspendable();
+                {
+                    CScript::const_iterator pc = script.begin();
+                    opcodetype opcode;
+                    (void)script.GetOp(pc, opcode);
+                    std::vector<uint8_t> data;
+                    (void)script.GetOp(pc, opcode, data);
+                    (void)script.IsPushOnly(pc);
+                }
+            });
     }
 }
diff --git a/src/test/fuzz/scriptnum_ops.cpp b/src/test/fuzz/scriptnum_ops.cpp
index fa5605f7578e..e112c894b531 100644
--- a/src/test/fuzz/scriptnum_ops.cpp
+++ b/src/test/fuzz/scriptnum_ops.cpp
@@ -29,105 +29,99 @@ FUZZ_TARGET(scriptnum_ops)
     FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
     CScriptNum script_num = ConsumeScriptNum(fuzzed_data_provider);
     while (fuzzed_data_provider.remaining_bytes() > 0) {
-        switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 11)) {
-        case 0: {
-            const int64_t i = fuzzed_data_provider.ConsumeIntegral<int64_t>();
-            assert((script_num == i) != (script_num != i));
-            assert((script_num <= i) != (script_num > i));
-            assert((script_num >= i) != (script_num < i));
-            // Avoid signed integer overflow:
-            // script/script.h:264:93: runtime error: signed integer overflow: -2261405121394637306 + -9223372036854775802 cannot be represented in type 'long'
-            if (IsValidAddition(script_num, CScriptNum{i})) {
-                assert((script_num + i) - i == script_num);
-            }
-            // Avoid signed integer overflow:
-            // script/script.h:265:93: runtime error: signed integer overflow: 9223371895120855039 - -9223372036854710486 cannot be represented in type 'long'
-            if (IsValidSubtraction(script_num, CScriptNum{i})) {
-                assert((script_num - i) + i == script_num);
-            }
-            break;
-        }
-        case 1: {
-            const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider);
-            assert((script_num == random_script_num) != (script_num != random_script_num));
-            assert((script_num <= random_script_num) != (script_num > random_script_num));
-            assert((script_num >= random_script_num) != (script_num < random_script_num));
-            // Avoid signed integer overflow:
-            // script/script.h:264:93: runtime error: signed integer overflow: -9223126527765971126 + -9223372036854756825 cannot be represented in type 'long'
-            if (IsValidAddition(script_num, random_script_num)) {
-                assert((script_num + random_script_num) - random_script_num == script_num);
-            }
-            // Avoid signed integer overflow:
-            // script/script.h:265:93: runtime error: signed integer overflow: 6052837899185946624 - -9223372036854775808 cannot be represented in type 'long'
-            if (IsValidSubtraction(script_num, random_script_num)) {
-                assert((script_num - random_script_num) + random_script_num == script_num);
-            }
-            break;
-        }
-        case 2: {
-            const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider);
-            if (!IsValidAddition(script_num, random_script_num)) {
-                // Avoid assertion failure:
-                // ./script/script.h:292: CScriptNum &CScriptNum::operator+=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value <= std::numeric_limits<int64_t>::max() - rhs) || (rhs < 0 && m_value >= std::numeric_limits<int64_t>::min() - rhs)' failed.
-                break;
-            }
-            script_num += random_script_num;
-            break;
-        }
-        case 3: {
-            const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider);
-            if (!IsValidSubtraction(script_num, random_script_num)) {
-                // Avoid assertion failure:
-                // ./script/script.h:300: CScriptNum &CScriptNum::operator-=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value >= std::numeric_limits<int64_t>::min() + rhs) || (rhs < 0 && m_value <= std::numeric_limits<int64_t>::max() + rhs)' failed.
-                break;
-            }
-            script_num -= random_script_num;
-            break;
-        }
-        case 4:
-            script_num = script_num & fuzzed_data_provider.ConsumeIntegral<int64_t>();
-            break;
-        case 5:
-            script_num = script_num & ConsumeScriptNum(fuzzed_data_provider);
-            break;
-        case 6:
-            script_num &= ConsumeScriptNum(fuzzed_data_provider);
-            break;
-        case 7:
-            if (script_num == CScriptNum{std::numeric_limits<int64_t>::min()}) {
-                // Avoid assertion failure:
-                // ./script/script.h:279: CScriptNum CScriptNum::operator-() const: Assertion `m_value != std::numeric_limits<int64_t>::min()' failed.
-                break;
-            }
-            script_num = -script_num;
-            break;
-        case 8:
-            script_num = fuzzed_data_provider.ConsumeIntegral<int64_t>();
-            break;
-        case 9: {
-            const int64_t random_integer = fuzzed_data_provider.ConsumeIntegral<int64_t>();
-            if (!IsValidAddition(script_num, CScriptNum{random_integer})) {
-                // Avoid assertion failure:
-                // ./script/script.h:292: CScriptNum &CScriptNum::operator+=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value <= std::numeric_limits<int64_t>::max() - rhs) || (rhs < 0 && m_value >= std::numeric_limits<int64_t>::min() - rhs)' failed.
-                break;
-            }
-            script_num += random_integer;
-            break;
-        }
-        case 10: {
-            const int64_t random_integer = fuzzed_data_provider.ConsumeIntegral<int64_t>();
-            if (!IsValidSubtraction(script_num, CScriptNum{random_integer})) {
-                // Avoid assertion failure:
-                // ./script/script.h:300: CScriptNum &CScriptNum::operator-=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value >= std::numeric_limits<int64_t>::min() + rhs) || (rhs < 0 && m_value <= std::numeric_limits<int64_t>::max() + rhs)' failed.
-                break;
-            }
-            script_num -= random_integer;
-            break;
-        }
-        case 11:
-            script_num &= fuzzed_data_provider.ConsumeIntegral<int64_t>();
-            break;
-        }
+        CallOneOf(
+            fuzzed_data_provider,
+            [&] {
+                const int64_t i = fuzzed_data_provider.ConsumeIntegral<int64_t>();
+                assert((script_num == i) != (script_num != i));
+                assert((script_num <= i) != (script_num > i));
+                assert((script_num >= i) != (script_num < i));
+                // Avoid signed integer overflow:
+                // script/script.h:264:93: runtime error: signed integer overflow: -2261405121394637306 + -9223372036854775802 cannot be represented in type 'long'
+                if (IsValidAddition(script_num, CScriptNum{i})) {
+                    assert((script_num + i) - i == script_num);
+                }
+                // Avoid signed integer overflow:
+                // script/script.h:265:93: runtime error: signed integer overflow: 9223371895120855039 - -9223372036854710486 cannot be represented in type 'long'
+                if (IsValidSubtraction(script_num, CScriptNum{i})) {
+                    assert((script_num - i) + i == script_num);
+                }
+            },
+            [&] {
+                const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider);
+                assert((script_num == random_script_num) != (script_num != random_script_num));
+                assert((script_num <= random_script_num) != (script_num > random_script_num));
+                assert((script_num >= random_script_num) != (script_num < random_script_num));
+                // Avoid signed integer overflow:
+                // script/script.h:264:93: runtime error: signed integer overflow: -9223126527765971126 + -9223372036854756825 cannot be represented in type 'long'
+                if (IsValidAddition(script_num, random_script_num)) {
+                    assert((script_num + random_script_num) - random_script_num == script_num);
+                }
+                // Avoid signed integer overflow:
+                // script/script.h:265:93: runtime error: signed integer overflow: 6052837899185946624 - -9223372036854775808 cannot be represented in type 'long'
+                if (IsValidSubtraction(script_num, random_script_num)) {
+                    assert((script_num - random_script_num) + random_script_num == script_num);
+                }
+            },
+            [&] {
+                const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider);
+                if (!IsValidAddition(script_num, random_script_num)) {
+                    // Avoid assertion failure:
+                    // ./script/script.h:292: CScriptNum &CScriptNum::operator+=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value <= std::numeric_limits<int64_t>::max() - rhs) || (rhs < 0 && m_value >= std::numeric_limits<int64_t>::min() - rhs)' failed.
+                    return;
+                }
+                script_num += random_script_num;
+            },
+            [&] {
+                const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider);
+                if (!IsValidSubtraction(script_num, random_script_num)) {
+                    // Avoid assertion failure:
+                    // ./script/script.h:300: CScriptNum &CScriptNum::operator-=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value >= std::numeric_limits<int64_t>::min() + rhs) || (rhs < 0 && m_value <= std::numeric_limits<int64_t>::max() + rhs)' failed.
+                    return;
+                }
+                script_num -= random_script_num;
+            },
+            [&] {
+                script_num = script_num & fuzzed_data_provider.ConsumeIntegral<int64_t>();
+            },
+            [&] {
+                script_num = script_num & ConsumeScriptNum(fuzzed_data_provider);
+            },
+            [&] {
+                script_num &= ConsumeScriptNum(fuzzed_data_provider);
+            },
+            [&] {
+                if (script_num == CScriptNum{std::numeric_limits<int64_t>::min()}) {
+                    // Avoid assertion failure:
+                    // ./script/script.h:279: CScriptNum CScriptNum::operator-() const: Assertion `m_value != std::numeric_limits<int64_t>::min()' failed.
+                    return;
+                }
+                script_num = -script_num;
+            },
+            [&] {
+                script_num = fuzzed_data_provider.ConsumeIntegral<int64_t>();
+            },
+            [&] {
+                const int64_t random_integer = fuzzed_data_provider.ConsumeIntegral<int64_t>();
+                if (!IsValidAddition(script_num, CScriptNum{random_integer})) {
+                    // Avoid assertion failure:
+                    // ./script/script.h:292: CScriptNum &CScriptNum::operator+=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value <= std::numeric_limits<int64_t>::max() - rhs) || (rhs < 0 && m_value >= std::numeric_limits<int64_t>::min() - rhs)' failed.
+                    return;
+                }
+                script_num += random_integer;
+            },
+            [&] {
+                const int64_t random_integer = fuzzed_data_provider.ConsumeIntegral<int64_t>();
+                if (!IsValidSubtraction(script_num, CScriptNum{random_integer})) {
+                    // Avoid assertion failure:
+                    // ./script/script.h:300: CScriptNum &CScriptNum::operator-=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value >= std::numeric_limits<int64_t>::min() + rhs) || (rhs < 0 && m_value <= std::numeric_limits<int64_t>::max() + rhs)' failed.
+                    return;
+                }
+                script_num -= random_integer;
+            },
+            [&] {
+                script_num &= fuzzed_data_provider.ConsumeIntegral<int64_t>();
+            });
         (void)script_num.getint();
         // Avoid negation failure:
         // script/script.h:332:35: runtime error: negation of -9223372036854775808 cannot be represented in type 'int64_t' (aka 'long'); cast to an unsigned type to negate this value to itself
diff --git a/src/test/fuzz/strprintf.cpp b/src/test/fuzz/strprintf.cpp
index 4af0e750ceb0..b66a7abfb317 100644
--- a/src/test/fuzz/strprintf.cpp
+++ b/src/test/fuzz/strprintf.cpp
@@ -4,6 +4,7 @@
 
 #include <test/fuzz/FuzzedDataProvider.h>
 #include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
 #include <tinyformat.h>
 #include <util/strencodings.h>
 #include <util/translation.h>
@@ -109,32 +110,32 @@ FUZZ_TARGET(str_printf)
     }
 
     try {
-        switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 5)) {
-        case 0:
-            (void)strprintf(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32));
-            (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeRandomLengthString(32));
-            break;
-        case 1:
-            (void)strprintf(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32).c_str());
-            (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeRandomLengthString(32).c_str());
-            break;
-        case 2:
-            (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<signed char>());
-            (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<signed char>());
-            break;
-        case 3:
-            (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<unsigned char>());
-            (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<unsigned char>());
-            break;
-        case 4:
-            (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<char>());
-            (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<char>());
-            break;
-        case 5:
-            (void)strprintf(format_string, fuzzed_data_provider.ConsumeBool());
-            (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeBool());
-            break;
-        }
+        CallOneOf(
+            fuzzed_data_provider,
+            [&] {
+                (void)strprintf(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32));
+                (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeRandomLengthString(32));
+            },
+            [&] {
+                (void)strprintf(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32).c_str());
+                (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeRandomLengthString(32).c_str());
+            },
+            [&] {
+                (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<signed char>());
+                (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<signed char>());
+            },
+            [&] {
+                (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<unsigned char>());
+                (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<unsigned char>());
+            },
+            [&] {
+                (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<char>());
+                (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<char>());
+            },
+            [&] {
+                (void)strprintf(format_string, fuzzed_data_provider.ConsumeBool());
+                (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeBool());
+            });
     } catch (const tinyformat::format_error&) {
     }
 
@@ -155,40 +156,40 @@ FUZZ_TARGET(str_printf)
     }
 
     try {
-        switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 7)) {
-        case 0:
-            (void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint<float>());
-            (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeFloatingPoint<float>());
-            break;
-        case 1:
-            (void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint<double>());
-            (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeFloatingPoint<double>());
-            break;
-        case 2:
-            (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int16_t>());
-            (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<int16_t>());
-            break;
-        case 3:
-            (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint16_t>());
-            (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<uint16_t>());
-            break;
-        case 4:
-            (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int32_t>());
-            (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<int32_t>());
-            break;
-        case 5:
-            (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint32_t>());
-            (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<uint32_t>());
-            break;
-        case 6:
-            (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int64_t>());
-            (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<int64_t>());
-            break;
-        case 7:
-            (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint64_t>());
-            (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<uint64_t>());
-            break;
-        }
+        CallOneOf(
+            fuzzed_data_provider,
+            [&] {
+                (void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint<float>());
+                (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeFloatingPoint<float>());
+            },
+            [&] {
+                (void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint<double>());
+                (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeFloatingPoint<double>());
+            },
+            [&] {
+                (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int16_t>());
+                (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<int16_t>());
+            },
+            [&] {
+                (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint16_t>());
+                (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<uint16_t>());
+            },
+            [&] {
+                (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int32_t>());
+                (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<int32_t>());
+            },
+            [&] {
+                (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint32_t>());
+                (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<uint32_t>());
+            },
+            [&] {
+                (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int64_t>());
+                (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<int64_t>());
+            },
+            [&] {
+                (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint64_t>());
+                (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<uint64_t>());
+            });
     } catch (const tinyformat::format_error&) {
     }
 }
diff --git a/src/test/fuzz/system.cpp b/src/test/fuzz/system.cpp
index 70f616ed979f..274a71d8ab3e 100644
--- a/src/test/fuzz/system.cpp
+++ b/src/test/fuzz/system.cpp
@@ -32,71 +32,63 @@ FUZZ_TARGET(system)
     }
 
     while (fuzzed_data_provider.ConsumeBool()) {
-        switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 7)) {
-        case 0: {
-            args_manager.SelectConfigNetwork(fuzzed_data_provider.ConsumeRandomLengthString(16));
-            break;
-        }
-        case 1: {
-            args_manager.SoftSetArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeRandomLengthString(16));
-            break;
-        }
-        case 2: {
-            args_manager.ForceSetArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeRandomLengthString(16));
-            break;
-        }
-        case 3: {
-            args_manager.SoftSetBoolArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeBool());
-            break;
-        }
-        case 4: {
-            const OptionsCategory options_category = fuzzed_data_provider.PickValueInArray<OptionsCategory>({OptionsCategory::OPTIONS, OptionsCategory::CONNECTION, OptionsCategory::WALLET, OptionsCategory::WALLET_DEBUG_TEST, OptionsCategory::ZMQ, OptionsCategory::DEBUG_TEST, OptionsCategory::CHAINPARAMS, OptionsCategory::NODE_RELAY, OptionsCategory::BLOCK_CREATION, OptionsCategory::RPC, OptionsCategory::GUI, OptionsCategory::COMMANDS, OptionsCategory::REGISTER_COMMANDS, OptionsCategory::HIDDEN});
-            // Avoid hitting:
-            // util/system.cpp:425: void ArgsManager::AddArg(const std::string &, const std::string &, unsigned int, const OptionsCategory &): Assertion `ret.second' failed.
-            const std::string argument_name = GetArgumentName(fuzzed_data_provider.ConsumeRandomLengthString(16));
-            if (args_manager.GetArgFlags(argument_name)) {
-                break;
-            }
-            args_manager.AddArg(argument_name, fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeIntegral<unsigned int>(), options_category);
-            break;
-        }
-        case 5: {
-            // Avoid hitting:
-            // util/system.cpp:425: void ArgsManager::AddArg(const std::string &, const std::string &, unsigned int, const OptionsCategory &): Assertion `ret.second' failed.
-            const std::vector<std::string> names = ConsumeRandomLengthStringVector(fuzzed_data_provider);
-            std::vector<std::string> hidden_arguments;
-            for (const std::string& name : names) {
-                const std::string hidden_argument = GetArgumentName(name);
-                if (args_manager.GetArgFlags(hidden_argument)) {
-                    continue;
+        CallOneOf(
+            fuzzed_data_provider,
+            [&] {
+                args_manager.SelectConfigNetwork(fuzzed_data_provider.ConsumeRandomLengthString(16));
+            },
+            [&] {
+                args_manager.SoftSetArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeRandomLengthString(16));
+            },
+            [&] {
+                args_manager.ForceSetArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeRandomLengthString(16));
+            },
+            [&] {
+                args_manager.SoftSetBoolArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeBool());
+            },
+            [&] {
+                const OptionsCategory options_category = fuzzed_data_provider.PickValueInArray<OptionsCategory>({OptionsCategory::OPTIONS, OptionsCategory::CONNECTION, OptionsCategory::WALLET, OptionsCategory::WALLET_DEBUG_TEST, OptionsCategory::ZMQ, OptionsCategory::DEBUG_TEST, OptionsCategory::CHAINPARAMS, OptionsCategory::NODE_RELAY, OptionsCategory::BLOCK_CREATION, OptionsCategory::RPC, OptionsCategory::GUI, OptionsCategory::COMMANDS, OptionsCategory::REGISTER_COMMANDS, OptionsCategory::HIDDEN});
+                // Avoid hitting:
+                // util/system.cpp:425: void ArgsManager::AddArg(const std::string &, const std::string &, unsigned int, const OptionsCategory &): Assertion `ret.second' failed.
+                const std::string argument_name = GetArgumentName(fuzzed_data_provider.ConsumeRandomLengthString(16));
+                if (args_manager.GetArgFlags(argument_name) != std::nullopt) {
+                    return;
                 }
-                if (std::find(hidden_arguments.begin(), hidden_arguments.end(), hidden_argument) != hidden_arguments.end()) {
-                    continue;
+                args_manager.AddArg(argument_name, fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeIntegral<unsigned int>(), options_category);
+            },
+            [&] {
+                // Avoid hitting:
+                // util/system.cpp:425: void ArgsManager::AddArg(const std::string &, const std::string &, unsigned int, const OptionsCategory &): Assertion `ret.second' failed.
+                const std::vector<std::string> names = ConsumeRandomLengthStringVector(fuzzed_data_provider);
+                std::vector<std::string> hidden_arguments;
+                for (const std::string& name : names) {
+                    const std::string hidden_argument = GetArgumentName(name);
+                    if (args_manager.GetArgFlags(hidden_argument) != std::nullopt) {
+                        continue;
+                    }
+                    if (std::find(hidden_arguments.begin(), hidden_arguments.end(), hidden_argument) != hidden_arguments.end()) {
+                        continue;
+                    }
+                    hidden_arguments.push_back(hidden_argument);
                 }
-                hidden_arguments.push_back(hidden_argument);
-            }
-            args_manager.AddHiddenArgs(hidden_arguments);
-            break;
-        }
-        case 6: {
-            args_manager.ClearArgs();
-            break;
-        }
-        case 7: {
-            const std::vector<std::string> random_arguments = ConsumeRandomLengthStringVector(fuzzed_data_provider);
-            std::vector<const char*> argv;
-            argv.reserve(random_arguments.size());
-            for (const std::string& random_argument : random_arguments) {
-                argv.push_back(random_argument.c_str());
-            }
-            try {
-                std::string error;
-                (void)args_manager.ParseParameters(argv.size(), argv.data(), error);
-            } catch (const std::logic_error&) {
-            }
-            break;
-        }
-        }
+                args_manager.AddHiddenArgs(hidden_arguments);
+            },
+            [&] {
+                args_manager.ClearArgs();
+            },
+            [&] {
+                const std::vector<std::string> random_arguments = ConsumeRandomLengthStringVector(fuzzed_data_provider);
+                std::vector<const char*> argv;
+                argv.reserve(random_arguments.size());
+                for (const std::string& random_argument : random_arguments) {
+                    argv.push_back(random_argument.c_str());
+                }
+                try {
+                    std::string error;
+                    (void)args_manager.ParseParameters(argv.size(), argv.data(), error);
+                } catch (const std::logic_error&) {
+                }
+            });
     }
 
     const std::string s1 = fuzzed_data_provider.ConsumeRandomLengthString(16);
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index c5ddecddf64e..0038fd27e4f5 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -36,6 +36,17 @@
 #include <string>
 #include <vector>
 
+template <typename... Callables>
+void CallOneOf(FuzzedDataProvider& fuzzed_data_provider, Callables... callables)
+{
+    constexpr size_t call_size{sizeof...(callables)};
+    static_assert(call_size >= 1);
+    const size_t call_index{fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, call_size - 1)};
+
+    size_t i{0};
+    return ((i++ == call_index ? callables() : void()), ...);
+}
+
 [[ nodiscard ]] inline std::vector<uint8_t> ConsumeRandomLengthByteVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept
 {
     const std::string s = fuzzed_data_provider.ConsumeRandomLengthString(max_length);
@@ -157,20 +168,17 @@ template <typename T>
 [[ nodiscard ]] inline CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) noexcept
 {
     CTxDestination tx_destination;
-    switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 2)) {
-    case 0: {
-        tx_destination = CNoDestination{};
-        break;
-    }
-    case 1: {
-        tx_destination = PKHash{ConsumeUInt160(fuzzed_data_provider)};
-        break;
-    }
-    case 2: {
-        tx_destination = ScriptHash{ConsumeUInt160(fuzzed_data_provider)};
-        break;
-    }
-    }
+    CallOneOf(
+        fuzzed_data_provider,
+        [&] {
+            tx_destination = CNoDestination{};
+        },
+        [&] {
+            tx_destination = PKHash{ConsumeUInt160(fuzzed_data_provider)};
+        },
+        [&] {
+            tx_destination = ScriptHash{ConsumeUInt160(fuzzed_data_provider)};
+        });
     return tx_destination;
 }
 
@@ -316,32 +324,26 @@ class FuzzedFileProvider
             return nullptr;
         }
         std::string mode;
-        switch (m_fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 5)) {
-        case 0: {
-            mode = "r";
-            break;
-        }
-        case 1: {
-            mode = "r+";
-            break;
-        }
-        case 2: {
-            mode = "w";
-            break;
-        }
-        case 3: {
-            mode = "w+";
-            break;
-        }
-        case 4: {
-            mode = "a";
-            break;
-        }
-        case 5: {
-            mode = "a+";
-            break;
-        }
-        }
+        CallOneOf(
+            m_fuzzed_data_provider,
+            [&] {
+                mode = "r";
+            },
+            [&] {
+                mode = "r+";
+            },
+            [&] {
+                mode = "w";
+            },
+            [&] {
+                mode = "w+";
+            },
+            [&] {
+                mode = "a";
+            },
+            [&] {
+                mode = "a+";
+            });
 #ifdef _GNU_SOURCE
         const cookie_io_functions_t io_hooks = {
             FuzzedFileProvider::read,
@@ -439,66 +441,64 @@ class FuzzedAutoFileProvider
     return {fuzzed_data_provider};
 }
 
-#define WRITE_TO_STREAM_CASE(id, type, consume) \
-    case id: {                                  \
-        type o = consume;                       \
-        stream << o;                            \
-        break;                                  \
+#define WRITE_TO_STREAM_CASE(type, consume) \
+    [&] {                                   \
+        type o = consume;                   \
+        stream << o;                        \
     }
 template <typename Stream>
 void WriteToStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) noexcept
 {
     while (fuzzed_data_provider.ConsumeBool()) {
         try {
-            switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 13)) {
-                WRITE_TO_STREAM_CASE(0, bool, fuzzed_data_provider.ConsumeBool())
-                WRITE_TO_STREAM_CASE(1, char, fuzzed_data_provider.ConsumeIntegral<char>())
-                WRITE_TO_STREAM_CASE(2, int8_t, fuzzed_data_provider.ConsumeIntegral<int8_t>())
-                WRITE_TO_STREAM_CASE(3, uint8_t, fuzzed_data_provider.ConsumeIntegral<uint8_t>())
-                WRITE_TO_STREAM_CASE(4, int16_t, fuzzed_data_provider.ConsumeIntegral<int16_t>())
-                WRITE_TO_STREAM_CASE(5, uint16_t, fuzzed_data_provider.ConsumeIntegral<uint16_t>())
-                WRITE_TO_STREAM_CASE(6, int32_t, fuzzed_data_provider.ConsumeIntegral<int32_t>())
-                WRITE_TO_STREAM_CASE(7, uint32_t, fuzzed_data_provider.ConsumeIntegral<uint32_t>())
-                WRITE_TO_STREAM_CASE(8, int64_t, fuzzed_data_provider.ConsumeIntegral<int64_t>())
-                WRITE_TO_STREAM_CASE(9, uint64_t, fuzzed_data_provider.ConsumeIntegral<uint64_t>())
-                WRITE_TO_STREAM_CASE(10, float, fuzzed_data_provider.ConsumeFloatingPoint<float>())
-                WRITE_TO_STREAM_CASE(11, double, fuzzed_data_provider.ConsumeFloatingPoint<double>())
-                WRITE_TO_STREAM_CASE(12, std::string, fuzzed_data_provider.ConsumeRandomLengthString(32))
-                WRITE_TO_STREAM_CASE(13, std::vector<char>, ConsumeRandomLengthIntegralVector<char>(fuzzed_data_provider))
-            }
+            CallOneOf(
+                fuzzed_data_provider,
+                WRITE_TO_STREAM_CASE(bool, fuzzed_data_provider.ConsumeBool()),
+                WRITE_TO_STREAM_CASE(char, fuzzed_data_provider.ConsumeIntegral<char>()),
+                WRITE_TO_STREAM_CASE(int8_t, fuzzed_data_provider.ConsumeIntegral<int8_t>()),
+                WRITE_TO_STREAM_CASE(uint8_t, fuzzed_data_provider.ConsumeIntegral<uint8_t>()),
+                WRITE_TO_STREAM_CASE(int16_t, fuzzed_data_provider.ConsumeIntegral<int16_t>()),
+                WRITE_TO_STREAM_CASE(uint16_t, fuzzed_data_provider.ConsumeIntegral<uint16_t>()),
+                WRITE_TO_STREAM_CASE(int32_t, fuzzed_data_provider.ConsumeIntegral<int32_t>()),
+                WRITE_TO_STREAM_CASE(uint32_t, fuzzed_data_provider.ConsumeIntegral<uint32_t>()),
+                WRITE_TO_STREAM_CASE(int64_t, fuzzed_data_provider.ConsumeIntegral<int64_t>()),
+                WRITE_TO_STREAM_CASE(uint64_t, fuzzed_data_provider.ConsumeIntegral<uint64_t>()),
+                WRITE_TO_STREAM_CASE(float, fuzzed_data_provider.ConsumeFloatingPoint<float>()),
+                WRITE_TO_STREAM_CASE(double, fuzzed_data_provider.ConsumeFloatingPoint<double>()),
+                WRITE_TO_STREAM_CASE(std::string, fuzzed_data_provider.ConsumeRandomLengthString(32)),
+                WRITE_TO_STREAM_CASE(std::vector<char>, ConsumeRandomLengthIntegralVector<char>(fuzzed_data_provider)));
         } catch (const std::ios_base::failure&) {
             break;
         }
     }
 }
 
-#define READ_FROM_STREAM_CASE(id, type) \
-    case id: {                          \
-        type o;                         \
-        stream >> o;                    \
-        break;                          \
+#define READ_FROM_STREAM_CASE(type) \
+    [&] {                           \
+        type o;                     \
+        stream >> o;                \
     }
 template <typename Stream>
 void ReadFromStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) noexcept
 {
     while (fuzzed_data_provider.ConsumeBool()) {
         try {
-            switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 13)) {
-                READ_FROM_STREAM_CASE(0, bool)
-                READ_FROM_STREAM_CASE(1, char)
-                READ_FROM_STREAM_CASE(2, int8_t)
-                READ_FROM_STREAM_CASE(3, uint8_t)
-                READ_FROM_STREAM_CASE(4, int16_t)
-                READ_FROM_STREAM_CASE(5, uint16_t)
-                READ_FROM_STREAM_CASE(6, int32_t)
-                READ_FROM_STREAM_CASE(7, uint32_t)
-                READ_FROM_STREAM_CASE(8, int64_t)
-                READ_FROM_STREAM_CASE(9, uint64_t)
-                READ_FROM_STREAM_CASE(10, float)
-                READ_FROM_STREAM_CASE(11, double)
-                READ_FROM_STREAM_CASE(12, std::string)
-                READ_FROM_STREAM_CASE(13, std::vector<char>)
-            }
+            CallOneOf(
+                fuzzed_data_provider,
+                READ_FROM_STREAM_CASE(bool),
+                READ_FROM_STREAM_CASE(char),
+                READ_FROM_STREAM_CASE(int8_t),
+                READ_FROM_STREAM_CASE(uint8_t),
+                READ_FROM_STREAM_CASE(int16_t),
+                READ_FROM_STREAM_CASE(uint16_t),
+                READ_FROM_STREAM_CASE(int32_t),
+                READ_FROM_STREAM_CASE(uint32_t),
+                READ_FROM_STREAM_CASE(int64_t),
+                READ_FROM_STREAM_CASE(uint64_t),
+                READ_FROM_STREAM_CASE(float),
+                READ_FROM_STREAM_CASE(double),
+                READ_FROM_STREAM_CASE(std::string),
+                READ_FROM_STREAM_CASE(std::vector<char>));
         } catch (const std::ios_base::failure&) {
             break;
         }

From 3eb89314da1473ec5cd9cbcfe3f01572241eb7e5 Mon Sep 17 00:00:00 2001
From: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com>
Date: Thu, 16 Mar 2023 08:44:21 +0000
Subject: [PATCH 30/35] fuzz: remove unused variable in
 script_bitcoin_consensus

---
 src/test/fuzz/script_bitcoin_consensus.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/test/fuzz/script_bitcoin_consensus.cpp b/src/test/fuzz/script_bitcoin_consensus.cpp
index 94ee91d9da23..f2ab1a2ebb57 100644
--- a/src/test/fuzz/script_bitcoin_consensus.cpp
+++ b/src/test/fuzz/script_bitcoin_consensus.cpp
@@ -17,7 +17,6 @@ FUZZ_TARGET(script_bitcoin_consensus)
     FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
     const std::vector<uint8_t> random_bytes_1 = ConsumeRandomLengthByteVector(fuzzed_data_provider);
     const std::vector<uint8_t> random_bytes_2 = ConsumeRandomLengthByteVector(fuzzed_data_provider);
-    const CAmount money = ConsumeMoney(fuzzed_data_provider);
     dashconsensus_error err;
     dashconsensus_error* err_p = fuzzed_data_provider.ConsumeBool() ? &err : nullptr;
     const unsigned int n_in = fuzzed_data_provider.ConsumeIntegral<unsigned int>();

From 9d55bd8d1c3aa1f693a72240eca5d256c8376586 Mon Sep 17 00:00:00 2001
From: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com>
Date: Mon, 27 Mar 2023 14:51:49 +0000
Subject: [PATCH 31/35] merge bitcoin#20749: Prune g_chainman usage related to
 ::LookupBlockIndex

---
 src/evo/mnhftx.cpp                         |   2 +-
 src/evo/simplifiedmns.cpp                  |   4 +-
 src/governance/object.cpp                  |   4 +-
 src/index/base.cpp                         |   4 +-
 src/init.cpp                               |   8 +-
 src/interfaces/chain.cpp                   |  10 +-
 src/llmq/blockprocessor.cpp                |   6 +-
 src/llmq/chainlocks.cpp                    |  10 +-
 src/llmq/commitment.cpp                    |   2 +-
 src/llmq/debug.cpp                         |   2 +-
 src/llmq/dkgsessionhandler.cpp             |   2 +-
 src/llmq/dkgsessionmgr.cpp                 |   4 +-
 src/llmq/instantsend.cpp                   |  12 +--
 src/llmq/quorums.cpp                       |   4 +-
 src/llmq/snapshot.cpp                      |   4 +-
 src/miner.cpp                              |   2 +-
 src/net_processing.cpp                     |  36 +++----
 src/node/coinstats.cpp                     |   2 +-
 src/rest.cpp                               |   4 +-
 src/rpc/blockchain.cpp                     |  32 +++----
 src/rpc/masternode.cpp                     |   2 +-
 src/rpc/mining.cpp                         |  10 +-
 src/rpc/quorums.cpp                        |   6 +-
 src/rpc/rawtransaction.cpp                 |  10 +-
 src/test/blockfilter_index_tests.cpp       |  10 +-
 src/test/fuzz/load_external_block_file.cpp |   2 +-
 src/test/util/setup_common.cpp             |   2 +-
 src/txmempool.cpp                          |   2 +-
 src/validation.cpp                         | 103 +++++++++++++--------
 src/validation.h                           |  70 +++++++-------
 30 files changed, 199 insertions(+), 172 deletions(-)

diff --git a/src/evo/mnhftx.cpp b/src/evo/mnhftx.cpp
index d3b86dc4beed..81b931016730 100644
--- a/src/evo/mnhftx.cpp
+++ b/src/evo/mnhftx.cpp
@@ -45,7 +45,7 @@ bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidat
         return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-mnhf-version");
     }
 
-    const CBlockIndex* pindexQuorum = LookupBlockIndex(mnhfTx.signal.quorumHash);
+    const CBlockIndex* pindexQuorum = g_chainman.m_blockman.LookupBlockIndex(mnhfTx.signal.quorumHash);
     if (!pindexQuorum) {
         return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-mnhf-quorum-hash");
     }
diff --git a/src/evo/simplifiedmns.cpp b/src/evo/simplifiedmns.cpp
index 202396047e3f..f1f378e58f0b 100644
--- a/src/evo/simplifiedmns.cpp
+++ b/src/evo/simplifiedmns.cpp
@@ -243,14 +243,14 @@ bool BuildSimplifiedMNListDiff(const uint256& baseBlockHash, const uint256& bloc
 
     const CBlockIndex* baseBlockIndex = ::ChainActive().Genesis();
     if (!baseBlockHash.IsNull()) {
-        baseBlockIndex = LookupBlockIndex(baseBlockHash);
+        baseBlockIndex = g_chainman.m_blockman.LookupBlockIndex(baseBlockHash);
         if (!baseBlockIndex) {
             errorRet = strprintf("block %s not found", baseBlockHash.ToString());
             return false;
         }
     }
 
-    const CBlockIndex* blockIndex = LookupBlockIndex(blockHash);
+    const CBlockIndex* blockIndex = g_chainman.m_blockman.LookupBlockIndex(blockHash);
     if (!blockIndex) {
         errorRet = strprintf("block %s not found", blockHash.ToString());
         return false;
diff --git a/src/governance/object.cpp b/src/governance/object.cpp
index 61b104335592..b05ddd1446c6 100644
--- a/src/governance/object.cpp
+++ b/src/governance/object.cpp
@@ -563,7 +563,7 @@ bool CGovernanceObject::IsCollateralValid(std::string& strError, bool& fMissingC
     findScript << OP_RETURN << ToByteVector(nExpectedHash);
 
     AssertLockHeld(cs_main);
-    bool fork_active = VersionBitsState(LookupBlockIndex(nBlockHash), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE;
+    bool fork_active = VersionBitsState(g_chainman.m_blockman.LookupBlockIndex(nBlockHash), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE;
     CAmount nMinFee = GetMinCollateralFee(fork_active);
 
     LogPrint(BCLog::GOBJECT, "CGovernanceObject::IsCollateralValid -- txCollateral->vout.size() = %s, findScript = %s, nMinFee = %lld\n",
@@ -594,7 +594,7 @@ bool CGovernanceObject::IsCollateralValid(std::string& strError, bool& fMissingC
     AssertLockHeld(cs_main);
     int nConfirmationsIn = 0;
     if (nBlockHash != uint256()) {
-        const CBlockIndex* pindex = LookupBlockIndex(nBlockHash);
+        const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(nBlockHash);
         if (pindex && ::ChainActive().Contains(pindex)) {
             nConfirmationsIn += ::ChainActive().Height() - pindex->nHeight + 1;
         }
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 2eaacc1f6eeb..c8fd962a186c 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -61,7 +61,7 @@ bool BaseIndex::Init()
     if (locator.IsNull()) {
         m_best_block_index = nullptr;
     } else {
-        m_best_block_index = FindForkInGlobalIndex(::ChainActive(), locator);
+        m_best_block_index = g_chainman.m_blockman.FindForkInGlobalIndex(::ChainActive(), locator);
     }
     m_synced = m_best_block_index.load() == ::ChainActive().Tip();
     return true;
@@ -238,7 +238,7 @@ void BaseIndex::ChainStateFlushed(const CBlockLocator& locator)
     const CBlockIndex* locator_tip_index;
     {
         LOCK(cs_main);
-        locator_tip_index = LookupBlockIndex(locator_tip_hash);
+        locator_tip_index = g_chainman.m_blockman.LookupBlockIndex(locator_tip_hash);
     }
 
     if (!locator_tip_index) {
diff --git a/src/init.cpp b/src/init.cpp
index cb2614a2b835..c0ac35e21831 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -893,7 +893,7 @@ static void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImp
             if (!file)
                 break; // This error is logged in OpenBlockFile
             LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile);
-            LoadExternalBlockFile(chainparams, file, &pos);
+            ::ChainstateActive().LoadExternalBlockFile(chainparams, file, &pos);
             if (ShutdownRequested()) {
                 LogPrintf("Shutdown requested. Exit %s\n", __func__);
                 return;
@@ -912,7 +912,7 @@ static void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImp
         FILE *file = fsbridge::fopen(path, "rb");
         if (file) {
             LogPrintf("Importing blocks file %s...\n", path.string());
-            LoadExternalBlockFile(chainparams, file);
+            ::ChainstateActive().LoadExternalBlockFile(chainparams, file);
             if (ShutdownRequested()) {
                 LogPrintf("Shutdown requested. Exit %s\n", __func__);
                 return;
@@ -2056,12 +2056,12 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc
                 // If the loaded chain has a wrong genesis, bail out immediately
                 // (we're likely using a testnet datadir, or the other way around).
                 if (!chainman.BlockIndex().empty() &&
-                        !LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) {
+                        !g_chainman.m_blockman.LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) {
                     return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?"));
                 }
 
                 if (!chainparams.GetConsensus().hashDevnetGenesisBlock.IsNull() && !chainman.BlockIndex().empty() &&
-                        !LookupBlockIndex(chainparams.GetConsensus().hashDevnetGenesisBlock)) {
+                        !g_chainman.m_blockman.LookupBlockIndex(chainparams.GetConsensus().hashDevnetGenesisBlock)) {
                     return InitError(_("Incorrect or no devnet genesis block found. Wrong datadir for devnet specified?"));
                 }
 
diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp
index 3d745d4928c8..c8ae63cb2f89 100644
--- a/src/interfaces/chain.cpp
+++ b/src/interfaces/chain.cpp
@@ -149,7 +149,7 @@ class ChainImpl : public Chain
     std::optional<int> getBlockHeight(const uint256& hash) override
     {
         LOCK(::cs_main);
-        CBlockIndex* block = LookupBlockIndex(hash);
+        CBlockIndex* block = g_chainman.m_blockman.LookupBlockIndex(hash);
         if (block && ::ChainActive().Contains(block)) {
             return block->nHeight;
         }
@@ -209,7 +209,7 @@ class ChainImpl : public Chain
     std::optional<int> findFork(const uint256& hash, std::optional<int>* height) override
     {
         LOCK(cs_main);
-        const CBlockIndex* block = LookupBlockIndex(hash);
+        const CBlockIndex* block = g_chainman.m_blockman.LookupBlockIndex(hash);
         const CBlockIndex* fork = block ? ::ChainActive().FindFork(block) : nullptr;
         if (height) {
             if (block) {
@@ -231,7 +231,7 @@ class ChainImpl : public Chain
     std::optional<int> findLocatorFork(const CBlockLocator& locator) override
     {
         LOCK(cs_main);
-        if (CBlockIndex* fork = FindForkInGlobalIndex(::ChainActive(), locator)) {
+        if (CBlockIndex* fork = g_chainman.m_blockman.FindForkInGlobalIndex(::ChainActive(), locator)) {
             return fork->nHeight;
         }
         return std::nullopt;
@@ -246,7 +246,7 @@ class ChainImpl : public Chain
         CBlockIndex* index;
         {
             LOCK(cs_main);
-            index = LookupBlockIndex(hash);
+            index = g_chainman.m_blockman.LookupBlockIndex(hash);
             if (!index) {
                 return false;
             }
@@ -266,7 +266,7 @@ class ChainImpl : public Chain
     double guessVerificationProgress(const uint256& block_hash) override
     {
         LOCK(cs_main);
-        return GuessVerificationProgress(Params().TxData(), LookupBlockIndex(block_hash));
+        return GuessVerificationProgress(Params().TxData(), g_chainman.m_blockman.LookupBlockIndex(block_hash));
     }
     bool hasDescendantsInMempool(const uint256& txid) override
     {
diff --git a/src/llmq/blockprocessor.cpp b/src/llmq/blockprocessor.cpp
index 14580500ff7b..0e5f4943991e 100644
--- a/src/llmq/blockprocessor.cpp
+++ b/src/llmq/blockprocessor.cpp
@@ -72,7 +72,7 @@ void CQuorumBlockProcessor::ProcessMessage(const CNode& peer, std::string_view m
     const CBlockIndex* pQuorumBaseBlockIndex;
     {
         LOCK(cs_main);
-        pQuorumBaseBlockIndex = LookupBlockIndex(qc.quorumHash);
+        pQuorumBaseBlockIndex = g_chainman.m_blockman.LookupBlockIndex(qc.quorumHash);
         if (pQuorumBaseBlockIndex == nullptr) {
             LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- unknown block %s in commitment, peer=%d\n", __func__,
                      qc.quorumHash.ToString(), peer.GetId());
@@ -261,7 +261,7 @@ bool CQuorumBlockProcessor::ProcessCommitment(int nHeight, const uint256& blockH
         return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-height");
     }
 
-    const auto* pQuorumBaseBlockIndex = LookupBlockIndex(qc.quorumHash);
+    const auto* pQuorumBaseBlockIndex = g_chainman.m_blockman.LookupBlockIndex(qc.quorumHash);
 
     if (!qc.Verify(pQuorumBaseBlockIndex, fBLSChecks)) {
         LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s height=%d, type=%d, quorumIndex=%d, quorumHash=%s, signers=%s, validMembers=%d, quorumPublicKey=%s qc verify failed.\n", __func__,
@@ -386,7 +386,7 @@ bool CQuorumBlockProcessor::UpgradeDB()
                 if (qc.IsNull()) {
                     continue;
                 }
-                const auto* pQuorumBaseBlockIndex = LookupBlockIndex(qc.quorumHash);
+                const auto* pQuorumBaseBlockIndex = g_chainman.m_blockman.LookupBlockIndex(qc.quorumHash);
                 m_evoDb.GetRawDB().Write(std::make_pair(DB_MINED_COMMITMENT, std::make_pair(qc.llmqType, qc.quorumHash)), std::make_pair(qc, pindex->GetBlockHash()));
                 const auto& llmq_params_opt = GetLLMQParams(qc.llmqType);
                 assert(llmq_params_opt.has_value());
diff --git a/src/llmq/chainlocks.cpp b/src/llmq/chainlocks.cpp
index 3ba96ec118f7..bcdb89d34840 100644
--- a/src/llmq/chainlocks.cpp
+++ b/src/llmq/chainlocks.cpp
@@ -130,7 +130,7 @@ void CChainLocksHandler::ProcessNewChainLock(const NodeId from, const llmq::CCha
         return;
     }
 
-    CBlockIndex* pindex = WITH_LOCK(cs_main, return LookupBlockIndex(clsig.getBlockHash()));
+    CBlockIndex* pindex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(clsig.getBlockHash()));
 
     {
         LOCK(cs);
@@ -414,7 +414,7 @@ CChainLocksHandler::BlockTxs::mapped_type CChainLocksHandler::GetBlockTxs(const
         uint32_t blockTime;
         {
             LOCK(cs_main);
-            auto* pindex = LookupBlockIndex(blockHash);
+            auto* pindex = g_chainman.m_blockman.LookupBlockIndex(blockHash);
             CBlock block;
             if (!ReadBlockFromDisk(block, pindex, Params().GetConsensus())) {
                 return nullptr;
@@ -506,7 +506,7 @@ void CChainLocksHandler::EnforceBestChainLock()
     bool activateNeeded = WITH_LOCK(::cs_main, return ::ChainActive().Tip()->GetAncestor(currentBestChainLockBlockIndex->nHeight)) != currentBestChainLockBlockIndex;
 
     if (activateNeeded) {
-        if(!ActivateBestChain(state, params)) {
+        if (!::ChainstateActive().ActivateBestChain(state, params)) {
             LogPrintf("CChainLocksHandler::%s -- ActivateBestChain failed: %s\n", __func__, FormatStateMessage(state));
             return;
         }
@@ -638,7 +638,7 @@ void CChainLocksHandler::Cleanup()
     }
 
     for (auto it = blockTxs.begin(); it != blockTxs.end(); ) {
-        auto* pindex = LookupBlockIndex(it->first);
+        auto* pindex = g_chainman.m_blockman.LookupBlockIndex(it->first);
         if (InternalHasChainLock(pindex->nHeight, pindex->GetBlockHash())) {
             for (const auto& txid : *it->second) {
                 txFirstSeenTime.erase(txid);
@@ -657,7 +657,7 @@ void CChainLocksHandler::Cleanup()
             // tx has vanished, probably due to conflicts
             it = txFirstSeenTime.erase(it);
         } else if (!hashBlock.IsNull()) {
-            auto* pindex = LookupBlockIndex(hashBlock);
+            auto* pindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock);
             if (::ChainActive().Tip()->GetAncestor(pindex->nHeight) == pindex && ::ChainActive().Height() - pindex->nHeight >= 6) {
                 // tx got confirmed >= 6 times, so we can stop keeping track of it
                 it = txFirstSeenTime.erase(it);
diff --git a/src/llmq/commitment.cpp b/src/llmq/commitment.cpp
index 91c1bb2db993..7d4718b02424 100644
--- a/src/llmq/commitment.cpp
+++ b/src/llmq/commitment.cpp
@@ -209,7 +209,7 @@ bool CheckLLMQCommitment(const CTransaction& tx, const CBlockIndex* pindexPrev,
         return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-height");
     }
 
-    const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return LookupBlockIndex(qcTx.commitment.quorumHash));
+    const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(qcTx.commitment.quorumHash));
     if (pQuorumBaseBlockIndex == nullptr) {
         return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-quorum-hash");
     }
diff --git a/src/llmq/debug.cpp b/src/llmq/debug.cpp
index 003b977e5acf..8f02c3d8d867 100644
--- a/src/llmq/debug.cpp
+++ b/src/llmq/debug.cpp
@@ -25,7 +25,7 @@ UniValue CDKGDebugSessionStatus::ToJson(int quorumIndex, int detailLevel) const
 
     std::vector<CDeterministicMNCPtr> dmnMembers;
     if (detailLevel == 2) {
-        const CBlockIndex* pindex = WITH_LOCK(cs_main, return LookupBlockIndex(quorumHash));
+        const CBlockIndex* pindex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(quorumHash));
         if (pindex != nullptr) {
             dmnMembers = utils::GetAllQuorumMembers(llmqType, pindex);
         }
diff --git a/src/llmq/dkgsessionhandler.cpp b/src/llmq/dkgsessionhandler.cpp
index db14b8bde9c2..e29b88c44ec3 100644
--- a/src/llmq/dkgsessionhandler.cpp
+++ b/src/llmq/dkgsessionhandler.cpp
@@ -491,7 +491,7 @@ void CDKGSessionHandler::HandleDKGRound()
         curQuorumHash = quorumHash;
     }
 
-    const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return LookupBlockIndex(curQuorumHash));
+    const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(curQuorumHash));
 
     if (!InitNewQuorum(pQuorumBaseBlockIndex)) {
         // should actually never happen
diff --git a/src/llmq/dkgsessionmgr.cpp b/src/llmq/dkgsessionmgr.cpp
index 943168992071..8cea880fa0b2 100644
--- a/src/llmq/dkgsessionmgr.cpp
+++ b/src/llmq/dkgsessionmgr.cpp
@@ -217,7 +217,7 @@ void CDKGSessionManager::ProcessMessage(CNode& pfrom, const CQuorumManager& quor
 
     // No luck, try to compute
     if (quorumIndex == -1) {
-        CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return LookupBlockIndex(quorumHash));
+        CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(quorumHash));
         if (pQuorumBaseBlockIndex == nullptr) {
             LogPrintf("CDKGSessionManager -- unknown quorumHash %s\n", quorumHash.ToString());
             // NOTE: do not insta-ban for this, we might be lagging behind
@@ -476,7 +476,7 @@ void CDKGSessionManager::CleanupOldContributions() const
                     break;
                 }
                 cnt_all++;
-                const CBlockIndex* pindexQuorum = LookupBlockIndex(std::get<2>(k));
+                const CBlockIndex* pindexQuorum = g_chainman.m_blockman.LookupBlockIndex(std::get<2>(k));
                 if (pindexQuorum == nullptr || ::ChainActive().Tip()->nHeight - pindexQuorum->nHeight > MAX_STORE_DEPTH) {
                     // not found or too old
                     batch.Erase(k);
diff --git a/src/llmq/instantsend.cpp b/src/llmq/instantsend.cpp
index 4d2540faec98..7ee02db87cee 100644
--- a/src/llmq/instantsend.cpp
+++ b/src/llmq/instantsend.cpp
@@ -619,7 +619,7 @@ bool CInstantSendManager::CheckCanLock(const COutPoint& outpoint, bool printDebu
     int nTxAge;
     {
         LOCK(cs_main);
-        pindexMined = LookupBlockIndex(hashBlock);
+        pindexMined = g_chainman.m_blockman.LookupBlockIndex(hashBlock);
         nTxAge = ::ChainActive().Height() - pindexMined->nHeight + 1;
     }
 
@@ -796,7 +796,7 @@ void CInstantSendManager::ProcessMessageInstantSendLock(const CNode& pfrom, cons
 
     // Deterministic ISLocks are only produced by rotation quorums, if we don't see DIP24 as active, then we won't be able to validate it anyway
     if (islock->IsDeterministic() && fDIP0024IsActive) {
-        const auto blockIndex = WITH_LOCK(cs_main, return LookupBlockIndex(islock->cycleHash));
+        const auto blockIndex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(islock->cycleHash));
         if (blockIndex == nullptr) {
             // Maybe we don't have the block yet or maybe some peer spams invalid values for cycleHash
             Misbehaving(pfrom.GetId(), 1);
@@ -967,7 +967,7 @@ std::unordered_set<uint256, StaticSaltedHasher> CInstantSendManager::ProcessPend
         if (islock->IsDeterministic()) {
             LOCK(cs_main);
 
-            const auto blockIndex = LookupBlockIndex(islock->cycleHash);
+            const auto blockIndex = g_chainman.m_blockman.LookupBlockIndex(islock->cycleHash);
             if (blockIndex == nullptr) {
                 batchVerifier.badSources.emplace(nodeId);
                 continue;
@@ -1061,7 +1061,7 @@ void CInstantSendManager::ProcessInstantSendLock(NodeId from, const uint256& has
     const CBlockIndex* pindexMined{nullptr};
     // we ignore failure here as we must be able to propagate the lock even if we don't have the TX locally
     if (tx && !hashBlock.IsNull()) {
-        pindexMined = WITH_LOCK(cs_main, return LookupBlockIndex(hashBlock));
+        pindexMined = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(hashBlock));
 
         // Let's see if the TX that was locked by this islock is already mined in a ChainLocked block. If yes,
         // we can simply ignore the islock, as the ChainLock implies locking of all TXs in that chain
@@ -1479,7 +1479,7 @@ void CInstantSendManager::ResolveBlockConflicts(const uint256& islockHash, const
 
         CValidationState state;
         // need non-const pointer
-        auto pindex2 = WITH_LOCK(::cs_main, return LookupBlockIndex(pindex->GetBlockHash()));
+        auto pindex2 = WITH_LOCK(::cs_main, return g_chainman.m_blockman.LookupBlockIndex(pindex->GetBlockHash()));
         if (!InvalidateBlock(state, Params(), pindex2)) {
             LogPrintf("CInstantSendManager::%s -- InvalidateBlock failed: %s\n", __func__, FormatStateMessage(state));
             // This should not have happened and we are in a state were it's not safe to continue anymore
@@ -1496,7 +1496,7 @@ void CInstantSendManager::ResolveBlockConflicts(const uint256& islockHash, const
 
     if (activateBestChain) {
         CValidationState state;
-        if (!ActivateBestChain(state, Params())) {
+        if (!::ChainstateActive().ActivateBestChain(state, Params())) {
             LogPrintf("CChainLocksHandler::%s -- ActivateBestChain failed: %s\n", __func__, FormatStateMessage(state));
             // This should not have happened and we are in a state were it's not safe to continue anymore
             assert(false);
diff --git a/src/llmq/quorums.cpp b/src/llmq/quorums.cpp
index b3a8998258b7..37de78c4c66e 100644
--- a/src/llmq/quorums.cpp
+++ b/src/llmq/quorums.cpp
@@ -560,7 +560,7 @@ std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqTyp
 
 CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash) const
 {
-    const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return LookupBlockIndex(quorumHash));
+    const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(quorumHash));
     if (!pQuorumBaseBlockIndex) {
         LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- block %s not found\n", __func__, quorumHash.ToString());
         return nullptr;
@@ -673,7 +673,7 @@ void CQuorumManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, C
             return;
         }
 
-        const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return LookupBlockIndex(request.GetQuorumHash()));
+        const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(request.GetQuorumHash()));
         if (pQuorumBaseBlockIndex == nullptr) {
             sendQDATA(CQuorumDataRequest::Errors::QUORUM_BLOCK_NOT_FOUND, request_limit_exceeded);
             return;
diff --git a/src/llmq/snapshot.cpp b/src/llmq/snapshot.cpp
index 76bedfc7f3ff..1b46a033fe61 100644
--- a/src/llmq/snapshot.cpp
+++ b/src/llmq/snapshot.cpp
@@ -132,7 +132,7 @@ bool BuildQuorumRotationInfo(const CGetQuorumRotationInfo& request, CQuorumRotat
         baseBlockIndexes.push_back(blockIndex);
     } else {
         for (const auto& blockHash : request.baseBlockHashes) {
-            const CBlockIndex* blockIndex = LookupBlockIndex(blockHash);
+            const CBlockIndex* blockIndex = g_chainman.m_blockman.LookupBlockIndex(blockHash);
             if (!blockIndex) {
                 errorRet = strprintf("block %s not found", blockHash.ToString());
                 return false;
@@ -158,7 +158,7 @@ bool BuildQuorumRotationInfo(const CGetQuorumRotationInfo& request, CQuorumRotat
         return false;
     }
 
-    const CBlockIndex* blockIndex = LookupBlockIndex(request.blockRequestHash);
+    const CBlockIndex* blockIndex = g_chainman.m_blockman.LookupBlockIndex(request.blockRequestHash);
     if (!blockIndex) {
         errorRet = strprintf("block not found");
         return false;
diff --git a/src/miner.cpp b/src/miner.cpp
index 277f7155779f..92ed3c6a2b4a 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -234,7 +234,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
     pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(*pblock->vtx[0]);
 
     CValidationState state;
-    if (!TestBlockValidity(state, m_clhandler, m_evoDb, chainparams, *pblock, pindexPrev, false, false)) {
+    if (!TestBlockValidity(state, m_clhandler, m_evoDb, chainparams, ::ChainstateActive(), *pblock, pindexPrev, false, false)) {
         throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state)));
     }
     int64_t nTime2 = GetTimeMicros();
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 681ecf74dcfa..f8223a41d5d4 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -598,7 +598,7 @@ static void ProcessBlockAvailability(NodeId nodeid) EXCLUSIVE_LOCKS_REQUIRED(cs_
     assert(state != nullptr);
 
     if (!state->hashLastUnknownBlock.IsNull()) {
-        const CBlockIndex* pindex = LookupBlockIndex(state->hashLastUnknownBlock);
+        const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(state->hashLastUnknownBlock);
         if (pindex && pindex->nChainWork > 0) {
             if (state->pindexBestKnownBlock == nullptr || pindex->nChainWork >= state->pindexBestKnownBlock->nChainWork) {
                 state->pindexBestKnownBlock = pindex;
@@ -615,7 +615,7 @@ static void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) EXCLUSIV
 
     ProcessBlockAvailability(nodeid);
 
-    const CBlockIndex* pindex = LookupBlockIndex(hash);
+    const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash);
     if (pindex && pindex->nChainWork > 0) {
         // An actually better block was announced.
         if (state->pindexBestKnownBlock == nullptr || pindex->nChainWork >= state->pindexBestKnownBlock->nChainWork) {
@@ -1594,7 +1594,7 @@ bool static AlreadyHave(const CInv& inv, const CTxMemPool& mempool, const LLMQCo
         }
 
     case MSG_BLOCK:
-        return LookupBlockIndex(inv.hash) != nullptr;
+        return g_chainman.m_blockman.LookupBlockIndex(inv.hash) != nullptr;
 
     /*
         Dash Related Inventory Messages
@@ -1696,7 +1696,7 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
     bool need_activate_chain = false;
     {
         LOCK(cs_main);
-        const CBlockIndex* pindex = LookupBlockIndex(inv.hash);
+        const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(inv.hash);
         if (pindex) {
             if (pindex->HaveTxsDownloaded() && !pindex->IsValid(BLOCK_VALID_SCRIPTS) &&
                     pindex->IsValid(BLOCK_VALID_TREE)) {
@@ -1711,13 +1711,13 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
     } // release cs_main before calling ActivateBestChain
     if (need_activate_chain) {
         CValidationState state;
-        if (!ActivateBestChain(state, Params(), a_recent_block)) {
+        if (!::ChainstateActive().ActivateBestChain(state, Params(), a_recent_block)) {
             LogPrint(BCLog::NET, "failed to activate chain (%s)\n", FormatStateMessage(state));
         }
     }
 
     LOCK(cs_main);
-    const CBlockIndex* pindex = LookupBlockIndex(inv.hash);
+    const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(inv.hash);
     if (pindex) {
         send = BlockRequestAllowed(pindex, consensusParams);
         if (!send) {
@@ -2089,7 +2089,7 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan
         //   don't connect before giving DoS points
         // - Once a headers message is received that is valid and does connect,
         //   nUnconnectingHeaders gets reset back to 0.
-        if (!LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) {
+        if (!g_chainman.m_blockman.LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) {
             nodestate->nUnconnectingHeaders++;
             std::string msg_type = (pfrom.nServices & NODE_HEADERS_COMPRESSED) ? NetMsgType::GETHEADERS2 : NetMsgType::GETHEADERS;
             connman.PushMessage(&pfrom, msgMaker.Make(msg_type, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
@@ -2121,7 +2121,7 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan
 
         // If we don't have the last header, then they'll have given us
         // something new (if these headers are valid).
-        if (!LookupBlockIndex(hashLastBlock)) {
+        if (!g_chainman.m_blockman.LookupBlockIndex(hashLastBlock)) {
             received_new_header = true;
         }
     }
@@ -2340,7 +2340,7 @@ static bool PrepareBlockFilterRequest(CNode& peer, const CChainParams& chain_par
 
     {
         LOCK(cs_main);
-        stop_index = LookupBlockIndex(stop_hash);
+        stop_index = g_chainman.m_blockman.LookupBlockIndex(stop_hash);
 
         // Check that the stop block exists and the peer would be allowed to fetch it.
         if (!stop_index || !BlockRequestAllowed(stop_index, chain_params.GetConsensus())) {
@@ -3150,7 +3150,7 @@ void PeerLogicValidation::ProcessMessage(
                 a_recent_block = most_recent_block;
             }
             CValidationState state;
-            if (!ActivateBestChain(state, Params(), a_recent_block)) {
+            if (!::ChainstateActive().ActivateBestChain(state, Params(), a_recent_block)) {
                 LogPrint(BCLog::NET, "failed to activate chain (%s)\n", FormatStateMessage(state));
             }
         }
@@ -3158,7 +3158,7 @@ void PeerLogicValidation::ProcessMessage(
         LOCK(cs_main);
 
         // Find the last block the caller has in the main chain
-        const CBlockIndex* pindex = FindForkInGlobalIndex(::ChainActive(), locator);
+        const CBlockIndex* pindex = g_chainman.m_blockman.FindForkInGlobalIndex(::ChainActive(), locator);
 
         // Send the rest of the chain
         if (pindex)
@@ -3213,7 +3213,7 @@ void PeerLogicValidation::ProcessMessage(
 
         LOCK(cs_main);
 
-        const CBlockIndex* pindex = LookupBlockIndex(req.blockhash);
+        const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(req.blockhash);
         if (!pindex || !(pindex->nStatus & BLOCK_HAVE_DATA)) {
             LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have\n", pfrom.GetId());
             return;
@@ -3266,7 +3266,7 @@ void PeerLogicValidation::ProcessMessage(
         if (locator.IsNull())
         {
             // If locator is null, return the hashStop block
-            pindex = LookupBlockIndex(hashStop);
+            pindex = g_chainman.m_blockman.LookupBlockIndex(hashStop);
             if (!pindex) {
                 return;
             }
@@ -3279,7 +3279,7 @@ void PeerLogicValidation::ProcessMessage(
         else
         {
             // Find the last block the caller has in the main chain
-            pindex = FindForkInGlobalIndex(::ChainActive(), locator);
+            pindex = g_chainman.m_blockman.FindForkInGlobalIndex(::ChainActive(), locator);
             if (pindex)
                 pindex = ::ChainActive().Next(pindex);
         }
@@ -3501,14 +3501,14 @@ void PeerLogicValidation::ProcessMessage(
         {
         LOCK(cs_main);
 
-        if (!LookupBlockIndex(cmpctblock.header.hashPrevBlock)) {
+        if (!g_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.hashPrevBlock)) {
             // Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers
             if (!::ChainstateActive().IsInitialBlockDownload())
                 m_connman.PushMessage(&pfrom, msgMaker.Make((pfrom.nServices & NODE_HEADERS_COMPRESSED) ? NetMsgType::GETHEADERS2 : NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
             return;
         }
 
-        if (!LookupBlockIndex(cmpctblock.header.GetHash())) {
+        if (!g_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.GetHash())) {
             received_new_header = true;
         }
         }
@@ -4597,7 +4597,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
                 // then send all headers past that one.  If we come across any
                 // headers that aren't on ::ChainActive(), give up.
                 for (const uint256 &hash : pto->vBlockHashesToAnnounce) {
-                    const CBlockIndex* pindex = LookupBlockIndex(hash);
+                    const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash);
                     assert(pindex);
                     if (::ChainActive()[pindex->nHeight] != pindex) {
                         // Bail out if we reorged away from this block
@@ -4705,7 +4705,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
                 // in the past.
                 if (!pto->vBlockHashesToAnnounce.empty()) {
                     const uint256 &hashToAnnounce = pto->vBlockHashesToAnnounce.back();
-                    const CBlockIndex* pindex = LookupBlockIndex(hashToAnnounce);
+                    const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hashToAnnounce);
                     assert(pindex);
 
                     // Warn if we're announcing a block that is not on the main chain.
diff --git a/src/node/coinstats.cpp b/src/node/coinstats.cpp
index 782aa76fc92e..8f8a74fca99e 100644
--- a/src/node/coinstats.cpp
+++ b/src/node/coinstats.cpp
@@ -92,7 +92,7 @@ static bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, const
     stats.hashBlock = pcursor->GetBestBlock();
     {
         LOCK(cs_main);
-        stats.nHeight = LookupBlockIndex(stats.hashBlock)->nHeight;
+        stats.nHeight = g_chainman.m_blockman.LookupBlockIndex(stats.hashBlock)->nHeight;
     }
 
     PrepareHash(hash_obj, stats);
diff --git a/src/rest.cpp b/src/rest.cpp
index c561b1854358..2fa766b4763e 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -180,7 +180,7 @@ static bool rest_headers(const CoreContext& context,
     {
         LOCK(cs_main);
         tip = ::ChainActive().Tip();
-        const CBlockIndex* pindex = LookupBlockIndex(hash);
+        const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash);
         while (pindex != nullptr && ::ChainActive().Contains(pindex)) {
             headers.push_back(pindex);
             if (headers.size() == (unsigned long)count)
@@ -248,7 +248,7 @@ static bool rest_block(HTTPRequest* req,
     {
         LOCK(cs_main);
         tip = ::ChainActive().Tip();
-        pblockindex = LookupBlockIndex(hash);
+        pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash);
         if (!pblockindex) {
             return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
         }
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 71ea21b39de1..5c10e93cc0fa 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -299,7 +299,7 @@ static UniValue getbestchainlock(const JSONRPCRequest& request)
     result.pushKV("signature", clsig.getSig().ToString());
 
     LOCK(cs_main);
-    result.pushKV("known_block", LookupBlockIndex(clsig.getBlockHash()) != nullptr);
+    result.pushKV("known_block", g_chainman.m_blockman.LookupBlockIndex(clsig.getBlockHash()) != nullptr);
     return result;
 }
 
@@ -873,7 +873,7 @@ static UniValue getblockheader(const JSONRPCRequest& request)
     const CBlockIndex* tip;
     {
         LOCK(cs_main);
-        pblockindex = LookupBlockIndex(hash);
+        pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash);
         tip = ::ChainActive().Tip();
     }
 
@@ -943,7 +943,7 @@ static UniValue getblockheaders(const JSONRPCRequest& request)
     const CBlockIndex* tip;
     {
         LOCK(cs_main);
-        pblockindex = LookupBlockIndex(hash);
+        pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash);
         tip = ::ChainActive().Tip();
     }
 
@@ -1051,7 +1051,7 @@ static UniValue getmerkleblocks(const JSONRPCRequest& request)
     std::string strHash = request.params[1].get_str();
     uint256 hash(uint256S(strHash));
 
-    const CBlockIndex* pblockindex = LookupBlockIndex(hash);
+    const CBlockIndex* pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash);
     if (!pblockindex) {
         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
     }
@@ -1170,7 +1170,7 @@ static UniValue getblock(const JSONRPCRequest& request)
     const CBlockIndex* tip;
     {
         LOCK(cs_main);
-        pblockindex = LookupBlockIndex(hash);
+        pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash);
         tip = ::ChainActive().Tip();
 
         if (!pblockindex) {
@@ -1380,7 +1380,7 @@ static UniValue gettxout(const JSONRPCRequest& request)
         }
     }
 
-    const CBlockIndex* pindex = LookupBlockIndex(coins_view->GetBestBlock());
+    const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(coins_view->GetBestBlock());
     ret.pushKV("bestblock", pindex->GetBlockHash().GetHex());
     if (coin.nHeight == MEMPOOL_HEIGHT) {
         ret.pushKV("confirmations", 0);
@@ -1811,7 +1811,7 @@ static UniValue preciousblock(const JSONRPCRequest& request)
 
     {
         LOCK(cs_main);
-        pblockindex = LookupBlockIndex(hash);
+        pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash);
         if (!pblockindex) {
             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
         }
@@ -1848,7 +1848,7 @@ static UniValue invalidateblock(const JSONRPCRequest& request)
     CBlockIndex* pblockindex;
     {
         LOCK(cs_main);
-        pblockindex = LookupBlockIndex(hash);
+        pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash);
         if (!pblockindex) {
             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
         }
@@ -1856,7 +1856,7 @@ static UniValue invalidateblock(const JSONRPCRequest& request)
     InvalidateBlock(state, Params(), pblockindex);
 
     if (state.IsValid()) {
-        ActivateBestChain(state, Params());
+        ::ChainstateActive().ActivateBestChain(state, Params());
     }
 
     if (!state.IsValid()) {
@@ -1886,7 +1886,7 @@ static UniValue reconsiderblock(const JSONRPCRequest& request)
 
     {
         LOCK(cs_main);
-        CBlockIndex* pblockindex = LookupBlockIndex(hash);
+        CBlockIndex* pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash);
         if (!pblockindex) {
             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
         }
@@ -1895,7 +1895,7 @@ static UniValue reconsiderblock(const JSONRPCRequest& request)
     }
 
     CValidationState state;
-    ActivateBestChain(state, Params());
+    ::ChainstateActive().ActivateBestChain(state, Params());
 
     if (!state.IsValid()) {
         throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state));
@@ -1939,7 +1939,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
     } else {
         uint256 hash = uint256S(request.params[1].get_str());
         LOCK(cs_main);
-        pindex = LookupBlockIndex(hash);
+        pindex = g_chainman.m_blockman.LookupBlockIndex(hash);
         if (!pindex) {
             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
         }
@@ -2116,7 +2116,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
     } else {
         const uint256 hash = ParseHashV(request.params[0], "parameter 1");
 
-        pindex = LookupBlockIndex(hash);
+        pindex = g_chainman.m_blockman.LookupBlockIndex(hash);
         if (!pindex) {
             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
         }
@@ -2336,7 +2336,7 @@ static UniValue getspecialtxes(const JSONRPCRequest& request)
             throw JSONRPCError(RPC_INVALID_PARAMETER, "Verbosity must be in range 0..2");
         }
     }
-    const CBlockIndex* pblockindex = LookupBlockIndex(hash);
+    const CBlockIndex* pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash);
     if (!pblockindex) {
         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
     }
@@ -2657,7 +2657,7 @@ static UniValue getblockfilter(const JSONRPCRequest& request)
     bool block_was_connected;
     {
         LOCK(cs_main);
-        block_index = LookupBlockIndex(block_hash);
+        block_index = g_chainman.m_blockman.LookupBlockIndex(block_hash);
         if (!block_index) {
             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
         }
@@ -2761,7 +2761,7 @@ UniValue dumptxoutset(const JSONRPCRequest& request)
         }
 
         pcursor = std::unique_ptr<CCoinsViewCursor>(::ChainstateActive().CoinsDB().Cursor());
-        tip = LookupBlockIndex(stats.hashBlock);
+        tip = g_chainman.m_blockman.LookupBlockIndex(stats.hashBlock);
         CHECK_NONFATAL(tip);
     }
 
diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp
index 7d4fbb2473e8..82af40b21ca0 100644
--- a/src/rpc/masternode.cpp
+++ b/src/rpc/masternode.cpp
@@ -421,7 +421,7 @@ static UniValue masternode_payments(const JSONRPCRequest& request)
     } else {
         LOCK(cs_main);
         uint256 blockHash = ParseHashV(request.params[0], "blockhash");
-        pindex = LookupBlockIndex(blockHash);
+        pindex = g_chainman.m_blockman.LookupBlockIndex(blockHash);
         if (pindex == nullptr) {
             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
         }
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 10da6d07916e..cd862c836679 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -383,7 +383,7 @@ static UniValue generateblock(const JSONRPCRequest& request)
         LOCK(cs_main);
 
         CValidationState state;
-        if (!TestBlockValidity(state, *llmq_ctx.clhandler, *node_context.evodb, chainparams, block, LookupBlockIndex(block.hashPrevBlock), false, false)) {
+        if (!TestBlockValidity(state, *llmq_ctx.clhandler, *node_context.evodb, chainparams, ::ChainstateActive(), block, g_chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock), false, false)) {
             throw JSONRPCError(RPC_VERIFY_ERROR, strprintf("TestBlockValidity failed: %s", state.GetRejectReason()));
         }
     }
@@ -657,7 +657,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
 
             uint256 hash = block.GetHash();
-            const CBlockIndex* pindex = LookupBlockIndex(hash);
+            const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash);
             if (pindex) {
                 if (pindex->IsValid(BLOCK_VALID_SCRIPTS))
                     return "duplicate";
@@ -673,7 +673,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
             if (block.hashPrevBlock != pindexPrev->GetBlockHash())
                 return "inconclusive-not-best-prevblk";
             CValidationState state;
-            TestBlockValidity(state, *llmq_ctx.clhandler, *node_context.evodb, Params(), block, pindexPrev, false, true);
+            TestBlockValidity(state, *llmq_ctx.clhandler, *node_context.evodb, Params(), ::ChainstateActive(), block, pindexPrev, false, true);
             return BIP22ValidationResult(state);
         }
 
@@ -996,7 +996,7 @@ static UniValue submitblock(const JSONRPCRequest& request)
     uint256 hash = block.GetHash();
     {
         LOCK(cs_main);
-        const CBlockIndex* pindex = LookupBlockIndex(hash);
+        const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash);
         if (pindex) {
             if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) {
                 return "duplicate";
@@ -1043,7 +1043,7 @@ static UniValue submitheader(const JSONRPCRequest& request)
     }
     {
         LOCK(cs_main);
-        if (!LookupBlockIndex(h.hashPrevBlock)) {
+        if (!g_chainman.m_blockman.LookupBlockIndex(h.hashPrevBlock)) {
             throw JSONRPCError(RPC_VERIFY_ERROR, "Must submit previous header (" + h.hashPrevBlock.GetHex() + ") first");
         }
     }
diff --git a/src/rpc/quorums.cpp b/src/rpc/quorums.cpp
index a0a0839e0d1a..2dfeb42143ad 100644
--- a/src/rpc/quorums.cpp
+++ b/src/rpc/quorums.cpp
@@ -741,7 +741,7 @@ static UniValue quorum_getdata(const JSONRPCRequest& request)
 
     NodeContext& node = EnsureNodeContext(request.context);
     LLMQContext& llmq_ctx = EnsureLLMQContext(request.context);
-    const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return LookupBlockIndex(quorumHash));
+    const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(quorumHash));
     return node.connman->ForNode(nodeId, [&](CNode* pNode) {
         return llmq_ctx.qman->RequestQuorumData(pNode, llmqType, pQuorumBaseBlockIndex, nDataMask, proTxHash);
     });
@@ -880,7 +880,7 @@ static UniValue verifychainlock(const JSONRPCRequest& request)
 
     int nBlockHeight;
     if (request.params[2].isNull()) {
-        const CBlockIndex* pIndex = WITH_LOCK(cs_main, return LookupBlockIndex(nBlockHash));
+        const CBlockIndex* pIndex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(nBlockHash));
         if (pIndex == nullptr) {
             throw JSONRPCError(RPC_INTERNAL_ERROR, "blockHash not found");
         }
@@ -933,7 +933,7 @@ static UniValue verifyislock(const JSONRPCRequest& request)
         uint256 hash_block;
         CTransactionRef tx = GetTransaction(/* block_index */ nullptr,  /* mempool */ nullptr, txid, Params().GetConsensus(), hash_block);
         if (tx && !hash_block.IsNull()) {
-            pindexMined = LookupBlockIndex(hash_block);
+            pindexMined = g_chainman.m_blockman.LookupBlockIndex(hash_block);
         }
     }
 
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index ff318b9344a8..bfde57c3132b 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -92,7 +92,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, CTxMemPool& mempo
         LOCK(cs_main);
 
         entry.pushKV("blockhash", hashBlock.GetHex());
-        CBlockIndex* pindex = LookupBlockIndex(hashBlock);
+        CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock);
         if (pindex) {
             if (::ChainActive().Contains(pindex)) {
                 entry.pushKV("height", pindex->nHeight);
@@ -222,7 +222,7 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
         LOCK(cs_main);
 
         uint256 blockhash = ParseHashV(request.params[2], "parameter 3");
-        blockindex = LookupBlockIndex(blockhash);
+        blockindex = g_chainman.m_blockman.LookupBlockIndex(blockhash);
         if (!blockindex) {
             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block hash not found");
         }
@@ -313,7 +313,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
     if (!request.params[1].isNull()) {
         LOCK(cs_main);
         hashBlock = uint256S(request.params[1].get_str());
-        pblockindex = LookupBlockIndex(hashBlock);
+        pblockindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock);
         if (!pblockindex) {
             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
         }
@@ -343,7 +343,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
         if (!tx || hashBlock.IsNull()) {
             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block");
         }
-        pblockindex = LookupBlockIndex(hashBlock);
+        pblockindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock);
         if (!pblockindex) {
             throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt");
         }
@@ -404,7 +404,7 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
 
     LOCK(cs_main);
 
-    const CBlockIndex* pindex = LookupBlockIndex(merkleBlock.header.GetHash());
+    const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(merkleBlock.header.GetHash());
     if (!pindex || !::ChainActive().Contains(pindex) || pindex->nTx == 0) {
         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
     }
diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp
index df0ea6e2610f..3de6fbe2a2fd 100644
--- a/src/test/blockfilter_index_tests.cpp
+++ b/src/test/blockfilter_index_tests.cpp
@@ -185,7 +185,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
         const CBlockIndex* block_index;
         {
             LOCK(cs_main);
-            block_index = LookupBlockIndex(block->GetHash());
+            block_index = g_chainman.m_blockman.LookupBlockIndex(block->GetHash());
         }
 
         BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
@@ -203,7 +203,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
         const CBlockIndex* block_index;
         {
             LOCK(cs_main);
-            block_index = LookupBlockIndex(block->GetHash());
+            block_index = g_chainman.m_blockman.LookupBlockIndex(block->GetHash());
         }
 
         BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
@@ -217,7 +217,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
         const CBlockIndex* block_index;
         {
             LOCK(cs_main);
-            block_index = LookupBlockIndex(block->GetHash());
+            block_index = g_chainman.m_blockman.LookupBlockIndex(block->GetHash());
         }
 
         BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
@@ -238,14 +238,14 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
 
          {
              LOCK(cs_main);
-             block_index = LookupBlockIndex(chainA[i]->GetHash());
+             block_index = g_chainman.m_blockman.LookupBlockIndex(chainA[i]->GetHash());
          }
          BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
          CheckFilterLookups(filter_index, block_index, chainA_last_header);
 
          {
              LOCK(cs_main);
-             block_index = LookupBlockIndex(chainB[i]->GetHash());
+             block_index = g_chainman.m_blockman.LookupBlockIndex(chainB[i]->GetHash());
          }
          BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
          CheckFilterLookups(filter_index, block_index, chainB_last_header);
diff --git a/src/test/fuzz/load_external_block_file.cpp b/src/test/fuzz/load_external_block_file.cpp
index c428a866310f..bbff8962e107 100644
--- a/src/test/fuzz/load_external_block_file.cpp
+++ b/src/test/fuzz/load_external_block_file.cpp
@@ -27,5 +27,5 @@ FUZZ_TARGET_INIT(load_external_block_file, initialize_load_external_block_file)
         return;
     }
     FlatFilePos flat_file_pos;
-    LoadExternalBlockFile(Params(), fuzzed_block_file, fuzzed_data_provider.ConsumeBool() ? &flat_file_pos : nullptr);
+    ::ChainstateActive().LoadExternalBlockFile(Params(), fuzzed_block_file, fuzzed_data_provider.ConsumeBool() ? &flat_file_pos : nullptr);
 }
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index baf4f17665d9..8f38c9306e85 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -254,7 +254,7 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
     }
 
     CValidationState state;
-    if (!ActivateBestChain(state, chainparams)) {
+    if (!::ChainstateActive().ActivateBestChain(state, chainparams)) {
         throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", FormatStateMessage(state)));
     }
 }
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 77e5ef90f1b1..5413a27c7b2b 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -1041,7 +1041,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
     uint64_t innerUsage = 0;
 
     CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(pcoins));
-    const int64_t spendheight = GetSpendHeight(mempoolDuplicate);
+    const int64_t spendheight = g_chainman.m_blockman.GetSpendHeight(mempoolDuplicate);
 
     std::list<const CTxMemPoolEntry*> waitingOnDependants;
     for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
diff --git a/src/validation.cpp b/src/validation.cpp
index 4531f02be9a5..968018a23c4e 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -173,17 +173,19 @@ namespace {
     std::set<int> setDirtyFileInfo;
 } // anon namespace
 
-CBlockIndex* LookupBlockIndex(const uint256& hash)
+CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash)
 {
     AssertLockHeld(cs_main);
-    BlockMap::const_iterator it = g_chainman.BlockIndex().find(hash);
-    return it == g_chainman.BlockIndex().end() ? nullptr : it->second;
+    assert(std::addressof(g_chainman.BlockIndex()) == std::addressof(m_block_index));
+    BlockMap::const_iterator it = m_block_index.find(hash);
+    return it == m_block_index.end() ? nullptr : it->second;
 }
 
-CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator)
+CBlockIndex* BlockManager::FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator)
 {
     AssertLockHeld(cs_main);
 
+    assert(std::addressof(g_chainman.m_blockman) == std::addressof(*this));
     // Find the latest block common to locator and chain - we expect that
     // locator.vHave is sorted descending by height.
     for (const uint256& hash : locator.vHave) {
@@ -736,7 +738,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
         return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, REJECT_NONSTANDARD, "non-BIP68-final");
 
     CAmount nFees = 0;
-    if (!Consensus::CheckTxInputs(tx, state, m_view, GetSpendHeight(m_view), nFees)) {
+    if (!Consensus::CheckTxInputs(tx, state, m_view, g_chainman.m_blockman.GetSpendHeight(m_view), nFees)) {
         return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state));
     }
 
@@ -1281,12 +1283,12 @@ CChainState::CChainState(BlockManager& blockman,
                          std::unique_ptr<CEvoDB>& evoDb,
                          CTxMemPool& mempool,
                          uint256 from_snapshot_blockhash)
-    : m_blockman(blockman),
+    : m_mempool(mempool),
       m_clhandler(clhandler),
       m_isman(isman),
       m_quorum_block_processor(quorum_block_processor),
       m_evoDb(evoDb),
-      m_mempool(mempool),
+      m_blockman(blockman),
       m_from_snapshot_blockhash(from_snapshot_blockhash) {}
 
 void CChainState::InitCoinsDB(
@@ -1451,9 +1453,10 @@ bool CScriptCheck::operator()() {
     return VerifyScript(scriptSig, m_tx_out.scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, txdata, cacheStore), &error);
 }
 
-int GetSpendHeight(const CCoinsViewCache& inputs)
+int BlockManager::GetSpendHeight(const CCoinsViewCache& inputs)
 {
-    LOCK(cs_main);
+    AssertLockHeld(cs_main);
+    assert(std::addressof(g_chainman.m_blockman) == std::addressof(*this));
     CBlockIndex* pindexPrev = LookupBlockIndex(inputs.GetBestBlock());
     return pindexPrev->nHeight + 1;
 }
@@ -3105,7 +3108,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
     return true;
 }
 
-static bool NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) {
+static bool NotifyHeaderTip(CChainState& chainstate) LOCKS_EXCLUDED(cs_main) {
     bool fNotify = false;
     bool fInitialBlockDownload = false;
     static CBlockIndex* pindexHeaderOld = nullptr;
@@ -3116,7 +3119,8 @@ static bool NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) {
 
         if (pindexHeader != pindexHeaderOld) {
             fNotify = true;
-            fInitialBlockDownload = ::ChainstateActive().IsInitialBlockDownload();
+            assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate));
+            fInitialBlockDownload = chainstate.IsInitialBlockDownload();
             pindexHeaderOld = pindexHeader;
         }
     }
@@ -3241,10 +3245,6 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
     return true;
 }
 
-bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) {
-    return ::ChainstateActive().ActivateBestChain(state, chainparams, std::move(pblock));
-}
-
 bool CChainState::PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex)
 {
     {
@@ -3461,7 +3461,7 @@ void CChainState::EnforceBlock(CValidationState& state, const CChainParams& chai
     }
     // In case blocks from the enforced chain are invalid at the moment, reconsider them.
     if (!pindex->IsValid()) {
-        ResetBlockFailureFlags(LookupBlockIndex(pindex->GetBlockHash()));
+        ResetBlockFailureFlags(m_blockman.LookupBlockIndex(pindex->GetBlockHash()));
     }
 }
 
@@ -3857,14 +3857,14 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
     return true;
 }
 
-//! Returns last CBlockIndex* that is a checkpoint
-static CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
 {
     const MapCheckpoints& checkpoints = data.mapCheckpoints;
 
     for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints))
     {
         const uint256& hash = i.second;
+        assert(std::addressof(g_chainman.m_blockman) == std::addressof(*this));
         CBlockIndex* pindex = LookupBlockIndex(hash);
         if (pindex) {
             return pindex;
@@ -3882,7 +3882,7 @@ static CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOC
  *  in ConnectBlock().
  *  Note that -reindex-chainstate skips the validation that happens here!
  */
-static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& params, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, BlockManager& blockman, const CChainParams& params, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
 {
     assert(pindexPrev != nullptr);
     const int nHeight = pindexPrev->nHeight + 1;
@@ -3908,7 +3908,8 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta
         // Don't accept any forks from the main chain prior to last checkpoint.
         // GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our
         // BlockIndex().
-        CBlockIndex* pcheckpoint = GetLastCheckpoint(params.Checkpoints());
+        assert(std::addressof(g_chainman.m_blockman) == std::addressof(blockman));
+        CBlockIndex* pcheckpoint = blockman.GetLastCheckpoint(params.Checkpoints());
         if (pcheckpoint && nHeight < pcheckpoint->nHeight)
             return state.Invalid(ValidationInvalidReason::BLOCK_CHECKPOINT, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight), REJECT_CHECKPOINT, "bad-fork-prior-to-checkpoint");
     }
@@ -4038,7 +4039,7 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, CValidationState
             // it's ok-ish, the other node is probably missing the latest chainlock
             return state.Invalid(ValidationInvalidReason::BLOCK_CHAINLOCK, error("%s: prev block %s conflicts with chainlock", __func__, block.hashPrevBlock.ToString()), REJECT_INVALID, "bad-prevblk-chainlock");
 
-        if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime()))
+        if (!ContextualCheckBlockHeader(block, state, *this, chainparams, pindexPrev, GetAdjustedTime()))
             return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state));
 
         /* Determine if this block descends from any block which has been found
@@ -4101,6 +4102,7 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, CValidationState
 // Exposed wrapper for AcceptBlockHeader
 bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex, CBlockHeader *first_invalid)
 {
+    assert(std::addressof(::ChainstateActive()) == std::addressof(ActiveChainstate()));
     AssertLockNotHeld(cs_main);
     if (first_invalid != nullptr) first_invalid->SetNull();
     {
@@ -4109,7 +4111,7 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>&
             CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast
             bool accepted = m_blockman.AcceptBlockHeader(
                 header, state, chainparams, &pindex);
-            ::ChainstateActive().CheckBlockIndex(chainparams.GetConsensus());
+            ActiveChainstate().CheckBlockIndex(chainparams.GetConsensus());
 
             if (!accepted) {
                 if (first_invalid) *first_invalid = header;
@@ -4120,8 +4122,8 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>&
             }
         }
     }
-    if (NotifyHeaderTip()) {
-        if (::ChainstateActive().IsInitialBlockDownload() && ppindex && *ppindex) {
+    if (NotifyHeaderTip(ActiveChainstate())) {
+        if (ActiveChainstate().IsInitialBlockDownload() && ppindex && *ppindex) {
             LogPrintf("Synchronizing blockheaders, height: %d (~%.2f%%)\n", (*ppindex)->nHeight, 100.0/((*ppindex)->nHeight+(GetAdjustedTime() - (*ppindex)->GetBlockTime()) / Params().GetConsensus().nPowTargetSpacing) * (*ppindex)->nHeight);
         }
     }
@@ -4242,6 +4244,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CVali
 bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, bool* fNewBlock)
 {
     AssertLockNotHeld(cs_main);
+    assert(std::addressof(::ChainstateActive()) == std::addressof(ActiveChainstate()));
 
     {
         CBlockIndex *pindex = nullptr;
@@ -4257,7 +4260,7 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s
         bool ret = CheckBlock(*pblock, state, chainparams.GetConsensus());
         if (ret) {
             // Store to disk
-            ret = ::ChainstateActive().AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock);
+            ret = ActiveChainstate().AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock);
         }
         if (!ret) {
             GetMainSignals().BlockChecked(*pblock, state);
@@ -4265,27 +4268,36 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s
         }
     }
 
-    NotifyHeaderTip();
+    NotifyHeaderTip(ActiveChainstate());
 
     CValidationState state; // Only used to report errors, not invalidity - ignore it
-    if (!::ChainstateActive().ActivateBestChain(state, chainparams, pblock))
+    if (!ActiveChainstate().ActivateBestChain(state, chainparams, pblock))
         return error("%s: ActivateBestChain failed: %s", __func__, FormatStateMessage(state));
 
     LogPrintf("%s : ACCEPTED\n", __func__);
     return true;
 }
 
-bool TestBlockValidity(CValidationState& state, llmq::CChainLocksHandler& clhandler, CEvoDB& evoDb, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW, bool fCheckMerkleRoot)
+bool TestBlockValidity(CValidationState& state,
+                       llmq::CChainLocksHandler& clhandler,
+                       CEvoDB& evoDb,
+                       const CChainParams& chainparams,
+                       CChainState& chainstate,
+                       const CBlock& block,
+                       CBlockIndex* pindexPrev,
+                       bool fCheckPOW,
+                       bool fCheckMerkleRoot)
 {
     AssertLockHeld(cs_main);
-    assert(pindexPrev && pindexPrev == ::ChainActive().Tip());
+    assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate));
+    assert(pindexPrev && pindexPrev == chainstate.m_chain.Tip());
 
     uint256 hash = block.GetHash();
     if (clhandler.HasConflictingChainLock(pindexPrev->nHeight + 1, hash)) {
         return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_PREV, error("%s: conflicting with chainlock", __func__), REJECT_INVALID, "bad-chainlock");
     }
 
-    CCoinsViewCache viewNew(&::ChainstateActive().CoinsTip());
+    CCoinsViewCache viewNew(&chainstate.CoinsTip());
     uint256 block_hash(block.GetHash());
     CBlockIndex indexDummy(block);
     indexDummy.pprev = pindexPrev;
@@ -4296,13 +4308,14 @@ bool TestBlockValidity(CValidationState& state, llmq::CChainLocksHandler& clhand
     auto dbTx = evoDb.BeginTransaction();
 
     // NOTE: CheckBlockHeader is called by CheckBlock
-    if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime()))
+    assert(std::addressof(g_chainman.m_blockman) == std::addressof(chainstate.m_blockman));
+    if (!ContextualCheckBlockHeader(block, state, chainstate.m_blockman, chainparams, pindexPrev, GetAdjustedTime()))
         return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, FormatStateMessage(state));
     if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot))
         return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state));
     if (!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindexPrev))
         return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state));
-    if (!::ChainstateActive().ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true))
+    if (!chainstate.ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true))
         return false;
     assert(state.IsValid());
 
@@ -4666,7 +4679,7 @@ bool CChainState::LoadChainTip(const CChainParams& chainparams)
     }
 
     // Load pointer to end of best chain
-    CBlockIndex* pindex = LookupBlockIndex(coins_cache.GetBestBlock());
+    CBlockIndex* pindex = m_blockman.LookupBlockIndex(coins_cache.GetBestBlock());
     if (!pindex) {
         return false;
     }
@@ -5008,7 +5021,7 @@ bool LoadGenesisBlock(const CChainParams& chainparams)
     return ::ChainstateActive().LoadGenesisBlock(chainparams);
 }
 
-void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos* dbp)
+void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos* dbp)
 {
     // Map of disk positions for blocks with unknown parent (only used for reindex)
     static std::multimap<uint256, FlatFilePos> mapBlocksUnknownParent;
@@ -5058,7 +5071,8 @@ void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi
                 {
                     LOCK(cs_main);
                     // detect out of order blocks, and store them for later
-                    if (hash != chainparams.GetConsensus().hashGenesisBlock && !LookupBlockIndex(block.hashPrevBlock)) {
+                    assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_blockman));
+                    if (hash != chainparams.GetConsensus().hashGenesisBlock && !m_blockman.LookupBlockIndex(block.hashPrevBlock)) {
                         LogPrint(BCLog::REINDEX, "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(),
                                 block.hashPrevBlock.ToString());
                         if (dbp)
@@ -5067,10 +5081,12 @@ void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi
                     }
 
                     // process in case the block isn't known yet
-                    CBlockIndex* pindex = LookupBlockIndex(hash);
+                    assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_blockman));
+                    CBlockIndex* pindex = m_blockman.LookupBlockIndex(hash);
                     if (!pindex || (pindex->nStatus & BLOCK_HAVE_DATA) == 0) {
                       CValidationState state;
-                      if (::ChainstateActive().AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr)) {
+                      assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
+                      if (AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr)) {
                           nLoaded++;
                       }
                       if (state.IsError()) {
@@ -5084,12 +5100,14 @@ void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi
                 // Activate the genesis block so normal node progress can continue
                 if (hash == chainparams.GetConsensus().hashGenesisBlock) {
                     CValidationState state;
+                    assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
                     if (!ActivateBestChain(state, chainparams, nullptr)) {
                         break;
                     }
                 }
 
-                NotifyHeaderTip();
+                assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
+                NotifyHeaderTip(*this);
 
                 // Recursively process earlier encountered successors of this block
                 std::deque<uint256> queue;
@@ -5107,7 +5125,8 @@ void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi
                                     head.ToString());
                             LOCK(cs_main);
                             CValidationState dummy;
-                            if (::ChainstateActive().AcceptBlock(pblockrecursive, dummy, chainparams, nullptr, true, &it->second, nullptr))
+                            assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
+                            if (AcceptBlock(pblockrecursive, dummy, chainparams, nullptr, true, &it->second, nullptr))
                             {
                                 nLoaded++;
                                 queue.push_back(pblockrecursive->GetHash());
@@ -5115,7 +5134,8 @@ void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi
                         }
                         range.first++;
                         mapBlocksUnknownParent.erase(it);
-                        NotifyHeaderTip();
+                        assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
+                        NotifyHeaderTip(*this);
                     }
                 }
             } catch (const std::exception& e) {
@@ -5565,7 +5585,7 @@ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pin
 }
 
 std::optional<uint256> ChainstateManager::SnapshotBlockhash() const {
-    LOCK(::cs_main);
+    LOCK(::cs_main);  // for m_active_chainstate access
     if (m_active_chainstate != nullptr) {
         // If a snapshot chainstate exists, it will always be our active.
         return m_active_chainstate->m_from_snapshot_blockhash;
@@ -5619,6 +5639,7 @@ CChainState& ChainstateManager::InitializeChainstate(std::unique_ptr<llmq::CChai
 
 CChainState& ChainstateManager::ActiveChainstate() const
 {
+    LOCK(::cs_main);
     assert(m_active_chainstate);
     return *m_active_chainstate;
 }
diff --git a/src/validation.h b/src/validation.h
index 292bb13fca79..f6cb13cce079 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -47,6 +47,7 @@ class CBlockIndex;
 class CBlockTreeDB;
 class CBlockUndo;
 class CChainParams;
+class CCheckpointData;
 class CInv;
 class CConnman;
 class CScriptCheck;
@@ -175,8 +176,6 @@ extern uint64_t nPruneTarget;
 FILE* OpenBlockFile(const FlatFilePos &pos, bool fReadOnly = false);
 /** Translation to a filesystem path */
 fs::path GetBlockPosFilename(const FlatFilePos &pos);
-/** Import blocks from an external file */
-void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos* dbp = nullptr);
 /** Ensures we have a genesis block in the block tree, possibly writing one to disk. */
 bool LoadGenesisBlock(const CChainParams& chainparams);
 /** Unload database information */
@@ -198,13 +197,6 @@ void StopScriptCheckWorkerThreads();
  * @returns                    The tx if found, otherwise nullptr
  */
 CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock);
-/**
- * Find the best known block, and make it the tip of the block chain
- *
- * May not be called with cs_main held. May not be called in a
- * validationinterface callback.
- */
-bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock = std::shared_ptr<const CBlock>()) LOCKS_EXCLUDED(cs_main);
 
 double ConvertBitsToDouble(unsigned int nBits);
 CAmount GetBlockSubsidy(int nBits, int nHeight, const Consensus::Params& consensusParams, bool fSuperblockPartOnly = false);
@@ -332,7 +324,15 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex);
 bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
 
 /** Check a block is completely valid from start to finish (only works on top of our current best block) */
-bool TestBlockValidity(CValidationState& state, llmq::CChainLocksHandler& clhandler, CEvoDB& evoDb, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+bool TestBlockValidity(CValidationState& state,
+                       llmq::CChainLocksHandler& clhandler,
+                       CEvoDB& evoDb,
+                       const CChainParams& chainparams,
+                       CChainState& chainstate,
+                       const CBlock& block,
+                       CBlockIndex* pindexPrev,
+                       bool fCheckPOW = true,
+                       bool fCheckMerkleRoot = true) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
 
 /** RAII wrapper for VerifyDB: Verify consistency of the block and coin databases */
 class CVerifyDB {
@@ -342,11 +342,6 @@ class CVerifyDB {
     bool VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, CEvoDB& evoDb, int nCheckLevel, int nCheckDepth);
 };
 
-CBlockIndex* LookupBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
-/** Find the last common block between the parameter chain and a locator. */
-CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
 enum DisconnectResult {
     DISCONNECT_OK,      // All good.
     DISCONNECT_UNCLEAN, // Rolled back, but UTXO set was inconsistent with block.
@@ -463,6 +458,21 @@ class BlockManager
         const CChainParams& chainparams,
         CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
 
+    CBlockIndex* LookupBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+    /** Find the last common block between the parameter chain and a locator. */
+    CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+    //! Returns last CBlockIndex* that is a checkpoint
+    CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+    /**
+     * Return the spend height, which is one more than the inputs.GetBestBlock().
+     * While checking, GetBestBlock() refers to the parent block. (protected by cs_main)
+     * This is also true for mempool checks.
+     */
+    int GetSpendHeight(const CCoinsViewCache& inputs) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
     ~BlockManager() {
         Unload();
     }
@@ -556,11 +566,6 @@ class CChainState
      */
     mutable std::atomic<bool> m_cached_finished_ibd{false};
 
-    //! Reference to a BlockManager instance which itself is shared across all
-    //! CChainState instances. Keeping a local reference allows us to test more
-    //! easily as opposed to referencing a global.
-    BlockManager& m_blockman;
-
     //! mempool that is kept in sync with the chain
     CTxMemPool& m_mempool;
 
@@ -574,6 +579,10 @@ class CChainState
     std::unique_ptr<CEvoDB>& m_evoDb;
 
 public:
+    //! Reference to a BlockManager instance which itself is shared across all
+    //! CChainState instances.
+    BlockManager& m_blockman;
+
     explicit CChainState(BlockManager& blockman,
                          std::unique_ptr<llmq::CChainLocksHandler>& clhandler,
                          std::unique_ptr<llmq::CInstantSendManager>& isman,
@@ -656,6 +665,9 @@ class CChainState
     bool ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size)
         EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
 
+    /** Import blocks from an external file */
+    void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos* dbp = nullptr);
+
     /**
      * Update the on-disk chain state.
      * The caches and indexes are flushed depending on the mode we're called with
@@ -681,9 +693,10 @@ class CChainState
     void PruneAndFlush();
 
     /**
-     * Make the best chain active, in multiple steps. The result is either failure
-     * or an activated best chain. pblock is either nullptr or a pointer to a block
-     * that is already loaded (to avoid loading it again from disk).
+     * Find the best known block, and make it the tip of the block chain. The
+     * result is either failure or an activated best chain. pblock is either
+     * nullptr or a pointer to a block that is already loaded (to avoid loading
+     * it again from disk).
      *
      * ActivateBestChain is split into steps (see ActivateBestChainStep) so that
      * we avoid holding cs_main for an extended period of time; the length of this
@@ -697,7 +710,7 @@ class CChainState
     bool ActivateBestChain(
         CValidationState& state,
         const CChainParams& chainparams,
-        std::shared_ptr<const CBlock> pblock) LOCKS_EXCLUDED(cs_main);
+        std::shared_ptr<const CBlock> pblock = nullptr) LOCKS_EXCLUDED(cs_main);
 
     bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
 
@@ -867,7 +880,7 @@ class ChainstateManager
     //! This is especially important when, e.g., calling ActivateBestChain()
     //! on all chainstates because we are not able to hold ::cs_main going into
     //! that call.
-    CChainState* m_active_chainstate{nullptr};
+    CChainState* m_active_chainstate GUARDED_BY(::cs_main) {nullptr};
 
     //! If true, the assumed-valid chainstate has been fully validated
     //! by the background validation chainstate.
@@ -1005,13 +1018,6 @@ CChain& ChainActive();
 /** Global variable that points to the active block tree (protected by cs_main) */
 extern std::unique_ptr<CBlockTreeDB> pblocktree;
 
-/**
- * Return the spend height, which is one more than the inputs.GetBestBlock().
- * While checking, GetBestBlock() refers to the parent block. (protected by cs_main)
- * This is also true for mempool checks.
- */
-int GetSpendHeight(const CCoinsViewCache& inputs);
-
 extern VersionBitsCache versionbitscache;
 
 /**

From 228a7fb4a109fd30ed03341873a4f82694684bb7 Mon Sep 17 00:00:00 2001
From: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com>
Date: Wed, 20 Jan 2021 14:43:27 -0500
Subject: [PATCH 32/35] merge bitcoin#20972: Annotate CTxMemPool::check to
 require cs_main

---
 src/txmempool.cpp | 1 +
 src/txmempool.h   | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 5413a27c7b2b..fd4fc5bb5cea 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -1034,6 +1034,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
 
     if (GetRand(m_check_ratio) >= 1) return;
 
+    AssertLockHeld(::cs_main);
     LOCK(cs);
     LogPrint(BCLog::MEMPOOL, "Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size());
 
diff --git a/src/txmempool.h b/src/txmempool.h
index ce5b4b4f6211..87efdeab93e5 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -596,7 +596,7 @@ class CTxMemPool
      * all inputs are in the mapNextTx array). If sanity-checking is turned off,
      * check does nothing.
      */
-    void check(const CCoinsViewCache *pcoins) const;
+    void check(const CCoinsViewCache *pcoins) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
 
     // addUnchecked must updated state for all ancestors of a given transaction,
     // to track size/count of descendant transactions.  First version of

From 30191be0b10ff7ba4e7b926908b4e3f19be2f3f9 Mon Sep 17 00:00:00 2001
From: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com>
Date: Thu, 23 Mar 2023 07:47:33 +0000
Subject: [PATCH 33/35] merge bitcoin#20750: Prune g_chainman usage in
 mempool-related validation functions

---
 src/bench/block_assemble.cpp         |   2 +-
 src/coinjoin/coinjoin.cpp            |   2 +-
 src/coinjoin/server.cpp              |   4 +-
 src/interfaces/chain.cpp             |   2 +-
 src/net_processing.cpp               |   4 +-
 src/node/transaction.cpp             |   2 +-
 src/rpc/rawtransaction.cpp           |   2 +-
 src/test/miner_tests.cpp             |  12 +--
 src/test/txvalidation_tests.cpp      |   2 +-
 src/test/txvalidationcache_tests.cpp |   2 +-
 src/test/validation_block_tests.cpp  |   1 +
 src/txmempool.cpp                    |  10 +-
 src/txmempool.h                      |   3 +-
 src/validation.cpp                   | 138 +++++++++++++++++----------
 src/validation.h                     |  15 ++-
 15 files changed, 121 insertions(+), 80 deletions(-)

diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp
index 222f57c8c214..1afedc9963f7 100644
--- a/src/bench/block_assemble.cpp
+++ b/src/bench/block_assemble.cpp
@@ -41,7 +41,7 @@ static void AssembleBlock(benchmark::Bench& bench)
 
         for (const auto& txr : txs) {
             CValidationState state;
-            bool ret{::AcceptToMemoryPool(*test_setup.m_node.mempool, state, txr, nullptr /* pfMissingInputs */, false /* bypass_limits */, /* nAbsurdFee */ 0)};
+            bool ret{::AcceptToMemoryPool(::ChainstateActive(), *test_setup.m_node.mempool, state, txr, nullptr /* pfMissingInputs */, false /* bypass_limits */, /* nAbsurdFee */ 0)};
             assert(ret);
         }
     }
diff --git a/src/coinjoin/coinjoin.cpp b/src/coinjoin/coinjoin.cpp
index 92acfa664cab..c18c72d148a5 100644
--- a/src/coinjoin/coinjoin.cpp
+++ b/src/coinjoin/coinjoin.cpp
@@ -352,7 +352,7 @@ bool CCoinJoin::IsCollateralValid(CTxMemPool& mempool, const CTransaction& txCol
     {
         LOCK(cs_main);
         CValidationState validationState;
-        if (!AcceptToMemoryPool(mempool, validationState, MakeTransactionRef(txCollateral), /*pfMissingInputs=*/nullptr, /*bypass_limits=*/false, /*nAbsurdFee=*/DEFAULT_MAX_RAW_TX_FEE, /*test_accept=*/true)) {
+        if (!AcceptToMemoryPool(::ChainstateActive(), mempool, validationState, MakeTransactionRef(txCollateral), /*pfMissingInputs=*/nullptr, /*bypass_limits=*/false, /*nAbsurdFee=*/DEFAULT_MAX_RAW_TX_FEE, /*test_accept=*/true)) {
             LogPrint(BCLog::COINJOIN, "CCoinJoin::IsCollateralValid -- didn't pass AcceptToMemoryPool()\n");
             return false;
         }
diff --git a/src/coinjoin/server.cpp b/src/coinjoin/server.cpp
index 52dc6fea226c..1ebcc36b35bf 100644
--- a/src/coinjoin/server.cpp
+++ b/src/coinjoin/server.cpp
@@ -322,7 +322,7 @@ void CCoinJoinServer::CommitFinalTransaction()
         TRY_LOCK(cs_main, lockMain);
         CValidationState validationState;
         mempool.PrioritiseTransaction(hashTx, 0.1 * COIN);
-        if (!lockMain || !AcceptToMemoryPool(mempool, validationState, finalTransaction, nullptr /* pfMissingInputs */, false /* bypass_limits */, DEFAULT_MAX_RAW_TX_FEE /* nAbsurdFee */)) {
+        if (!lockMain || !AcceptToMemoryPool(::ChainstateActive(), mempool, validationState, finalTransaction, nullptr /* pfMissingInputs */, false /* bypass_limits */, DEFAULT_MAX_RAW_TX_FEE /* nAbsurdFee */)) {
             LogPrint(BCLog::COINJOIN, "CCoinJoinServer::CommitFinalTransaction -- AcceptToMemoryPool() error: Transaction not valid\n");
             WITH_LOCK(cs_coinjoin, SetNull());
             // not much we can do in this case, just notify clients
@@ -455,7 +455,7 @@ void CCoinJoinServer::ConsumeCollateral(const CTransactionRef& txref) const
 {
     LOCK(cs_main);
     CValidationState validationState;
-    if (!AcceptToMemoryPool(mempool, validationState, txref, nullptr /* pfMissingInputs */, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
+    if (!AcceptToMemoryPool(::ChainstateActive(), mempool, validationState, txref, nullptr /* pfMissingInputs */, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
         LogPrint(BCLog::COINJOIN, "%s -- AcceptToMemoryPool failed\n", __func__);
     } else {
         connman.RelayTransaction(*txref);
diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp
index c8ae63cb2f89..e5684485640c 100644
--- a/src/interfaces/chain.cpp
+++ b/src/interfaces/chain.cpp
@@ -239,7 +239,7 @@ class ChainImpl : public Chain
     bool checkFinalTx(const CTransaction& tx) override
     {
         LOCK(cs_main);
-        return CheckFinalTx(tx);
+        return CheckFinalTx(::ChainActive().Tip(), tx);
     }
     bool findBlock(const uint256& hash, CBlock* block, int64_t* time, int64_t* time_max) override
     {
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index f8223a41d5d4..5b9882a6dcb2 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -2272,7 +2272,7 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uin
         CValidationState orphan_state;
 
         if (setMisbehaving.count(fromPeer)) continue;
-        if (AcceptToMemoryPool(mempool, orphan_state, porphanTx, &fMissingInputs2 /* pfMissingInputs */,
+        if (AcceptToMemoryPool(::ChainstateActive(), mempool, orphan_state, porphanTx, &fMissingInputs2 /* pfMissingInputs */,
                 false /* bypass_limits */, 0 /* nAbsurdFee */)) {
             LogPrint(BCLog::MEMPOOL, "   accepted orphan tx %s\n", orphanHash.ToString());
             RelayTransaction(orphanTx.GetHash(), connman);
@@ -3368,7 +3368,7 @@ void PeerLogicValidation::ProcessMessage(
         bool fMissingInputs = false;
         CValidationState state;
 
-        if (!AlreadyHave(inv, m_mempool, *m_llmq_ctx) && AcceptToMemoryPool(m_mempool, state, ptx, &fMissingInputs /* pfMissingInputs */,
+        if (!AlreadyHave(inv, m_mempool, *m_llmq_ctx) && AcceptToMemoryPool(::ChainstateActive(), m_mempool, state, ptx, &fMissingInputs /* pfMissingInputs */,
                 false /* bypass_limits */, 0 /* nAbsurdFee */)) {
             // Process custom txes, this changes AlreadyHave to "true"
             if (nInvType == MSG_DSTX) {
diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
index df07816b6021..e44ae991977f 100644
--- a/src/node/transaction.cpp
+++ b/src/node/transaction.cpp
@@ -38,7 +38,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
         // Transaction is not already in the mempool. Submit it.
         CValidationState state;
         bool fMissingInputs;
-        if (!AcceptToMemoryPool(*node.mempool, state, std::move(tx), &fMissingInputs,
+        if (!AcceptToMemoryPool(::ChainstateActive(), *node.mempool, state, std::move(tx), &fMissingInputs,
                                 bypass_limits, max_tx_fee)) {
             if (state.IsInvalid()) {
                 err_string = FormatStateMessage(state);
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index bfde57c3132b..22e6002c8728 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -926,7 +926,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
     bool test_accept_res;
     {
         LOCK(cs_main);
-        test_accept_res = AcceptToMemoryPool(mempool, state, std::move(tx), &missing_inputs,
+        test_accept_res = AcceptToMemoryPool(::ChainstateActive(), mempool, state, std::move(tx), &missing_inputs,
             false /* bypass_limits */, max_raw_tx_fee, /* test_accept */ true);
     }
     result_0.pushKV("allowed", test_accept_res);
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 05c415f25689..8244b5d464e8 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -35,7 +35,7 @@ struct MinerTestingSetup : public TestingSetup {
     void TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs);
     bool TestSequenceLocks(const CTransaction& tx, int flags) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs)
     {
-        return CheckSequenceLocks(*m_node.mempool, tx, flags);
+        return CheckSequenceLocks(::ChainstateActive(), *m_node.mempool, tx, flags);
     }
     BlockAssembler AssemblerForTest(const CChainParams& params);
 };
@@ -456,7 +456,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
     tx.nLockTime = 0;
     hash = tx.GetHash();
     m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
-    BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes
+    BOOST_CHECK(CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime passes
     BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
     BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 2))); // Sequence locks pass on 2nd block
 
@@ -466,7 +466,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
     prevheights[0] = baseheight + 2;
     hash = tx.GetHash();
     m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
-    BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes
+    BOOST_CHECK(CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime passes
     BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
 
     for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
@@ -482,7 +482,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
     tx.nLockTime = ::ChainActive().Tip()->nHeight + 1;
     hash = tx.GetHash();
     m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
-    BOOST_CHECK(!CheckFinalTx(CTransaction(tx), flags)); // Locktime fails
+    BOOST_CHECK(!CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime fails
     BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
     BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
 
@@ -493,7 +493,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
     prevheights[0] = baseheight + 4;
     hash = tx.GetHash();
     m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
-    BOOST_CHECK(!CheckFinalTx(CTransaction(tx), flags)); // Locktime fails
+    BOOST_CHECK(!CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime fails
     BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
     BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
 
@@ -502,7 +502,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
     prevheights[0] = ::ChainActive().Tip()->nHeight + 1;
     tx.nLockTime = 0;
     tx.vin[0].nSequence = 0;
-    BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes
+    BOOST_CHECK(CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime passes
     BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
     tx.vin[0].nSequence = 1;
     BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp
index 7c597fe2b62c..fdb2b4a8c0f5 100644
--- a/src/test/txvalidation_tests.cpp
+++ b/src/test/txvalidation_tests.cpp
@@ -38,7 +38,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
 
     BOOST_CHECK_EQUAL(
             false,
-            AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(coinbaseTx),
+            AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool, state, MakeTransactionRef(coinbaseTx),
                 nullptr /* pfMissingInputs */,
                 true /* bypass_limits */,
                 0 /* nAbsurdFee */));
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 3c0747066319..39f74e4acfb4 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -28,7 +28,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
         LOCK(cs_main);
 
         CValidationState state;
-        return AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(tx), nullptr /* pfMissingInputs */,
+        return AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool, state, MakeTransactionRef(tx), nullptr /* pfMissingInputs */,
                                 true /* bypass_limits */, 0 /* nAbsurdFee */);
     };
 
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 01be24a24aae..b6cd6db433a5 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -299,6 +299,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
             CValidationState state;
             for (const auto& tx : txs) {
                 BOOST_REQUIRE(AcceptToMemoryPool(
+                    ::ChainstateActive(),
                     *m_node.mempool,
                     state,
                     tx,
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index fd4fc5bb5cea..a9a202991e34 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -752,7 +752,7 @@ void CTxMemPool::removeRecursive(const CTransaction &origTx, MemPoolRemovalReaso
         RemoveStaged(setAllRemoves, false, reason);
 }
 
-void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void CTxMemPool::removeForReorg(CChainState& active_chainstate, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
 {
     // Remove transactions spending a coinbase which are now immature and no-longer-final transactions
     AssertLockHeld(cs);
@@ -760,8 +760,9 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem
     for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
         const CTransaction& tx = it->GetTx();
         LockPoints lp = it->GetLockPoints();
-        bool validLP =  TestLockPointValidity(&lp);
-        if (!CheckFinalTx(tx, flags) || !CheckSequenceLocks(*this, tx, flags, &lp, validLP)) {
+        assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+        bool validLP =  TestLockPointValidity(active_chainstate.m_chain, &lp);
+        if (!CheckFinalTx(active_chainstate.m_chain.Tip(), tx, flags) || !CheckSequenceLocks(active_chainstate, *this, tx, flags, &lp, validLP)) {
             // Note if CheckSequenceLocks fails the LockPoints may still be invalid
             // So it's critical that we remove the tx and not depend on the LockPoints.
             txToRemove.insert(it);
@@ -770,8 +771,9 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem
                 indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash);
                 if (it2 != mapTx.end())
                     continue;
-                const Coin &coin = pcoins->AccessCoin(txin.prevout);
+                const Coin &coin = active_chainstate.CoinsTip().AccessCoin(txin.prevout);
                 if (m_check_ratio != 0) assert(!coin.IsSpent());
+                unsigned int nMemPoolHeight = active_chainstate.m_chain.Tip()->nHeight + 1;
                 if (coin.IsSpent() || (coin.IsCoinBase() && ((signed long)nMemPoolHeight) - coin.nHeight < COINBASE_MATURITY)) {
                     txToRemove.insert(it);
                     break;
diff --git a/src/txmempool.h b/src/txmempool.h
index 87efdeab93e5..e093a6733c73 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -36,6 +36,7 @@
 class CBLSPublicKey;
 
 class CBlockIndex;
+class CChainState;
 extern CCriticalSection cs_main;
 
 /** Fake height value used in Coin to signify they are only in the memory pool (since 0.8) */
@@ -618,7 +619,7 @@ class CTxMemPool
     bool removeSpentIndex(const uint256 txhash);
 
     void removeRecursive(const CTransaction& tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
-    void removeForReorg(const CCoinsViewCache* pcoins, unsigned int nMemPoolHeight, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
+    void removeForReorg(CChainState& active_chainstate, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
     void removeConflicts(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(cs);
     void removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID &keyId) EXCLUSIVE_LOCKS_REQUIRED(cs);
     void removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSPublicKey &pubKey) EXCLUSIVE_LOCKS_REQUIRED(cs);
diff --git a/src/validation.cpp b/src/validation.cpp
index 968018a23c4e..f3a590bd7cec 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -205,9 +205,10 @@ static FILE* OpenUndoFile(const FlatFilePos &pos, bool fReadOnly = false);
 static FlatFileSeq BlockFileSeq();
 static FlatFileSeq UndoFileSeq();
 
-bool CheckFinalTx(const CTransaction &tx, int flags)
+bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, int flags)
 {
     AssertLockHeld(cs_main);
+    assert(std::addressof(*::ChainActive().Tip()) == std::addressof(*active_chain_tip));
 
     // By convention a negative value for flags indicates that the
     // current network-enforced consensus rules should be used. In
@@ -223,7 +224,7 @@ bool CheckFinalTx(const CTransaction &tx, int flags)
     // evaluated is what is used. Thus if we want to know if a
     // transaction can be part of the *next* block, we need to call
     // IsFinalTx() with one more than ::ChainActive().Height().
-    const int nBlockHeight = ::ChainActive().Height() + 1;
+    const int nBlockHeight = active_chain_tip->nHeight + 1;
 
     // BIP113 requires that time-locked transactions have nLockTime set to
     // less than the median time of the previous block they're contained in.
@@ -231,13 +232,13 @@ bool CheckFinalTx(const CTransaction &tx, int flags)
     // chain tip, so we use that to calculate the median time passed to
     // IsFinalTx() if LOCKTIME_MEDIAN_TIME_PAST is set.
     const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST)
-                             ? ::ChainActive().Tip()->GetMedianTimePast()
+                             ? active_chain_tip->GetMedianTimePast()
                              : GetAdjustedTime();
 
     return IsFinalTx(tx, nBlockHeight, nBlockTime);
 }
 
-bool TestLockPointValidity(const LockPoints* lp)
+bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp)
 {
     AssertLockHeld(cs_main);
     assert(lp);
@@ -246,7 +247,8 @@ bool TestLockPointValidity(const LockPoints* lp)
     if (lp->maxInputBlock) {
         // Check whether ::ChainActive() is an extension of the block at which the LockPoints
         // calculation was valid.  If not LockPoints are no longer valid
-        if (!::ChainActive().Contains(lp->maxInputBlock)) {
+        assert(std::addressof(::ChainActive()) == std::addressof(active_chain));
+        if (!active_chain.Contains(lp->maxInputBlock)) {
             return false;
         }
     }
@@ -255,22 +257,28 @@ bool TestLockPointValidity(const LockPoints* lp)
     return true;
 }
 
-bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flags, LockPoints* lp, bool useExistingLockPoints)
+bool CheckSequenceLocks(CChainState& active_chainstate,
+                        const CTxMemPool& pool,
+                        const CTransaction& tx,
+                        int flags,
+                        LockPoints* lp,
+                        bool useExistingLockPoints)
 {
     AssertLockHeld(cs_main);
     AssertLockHeld(pool.cs);
+    assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
 
-    CBlockIndex* tip = ::ChainActive().Tip();
+    CBlockIndex* tip = active_chainstate.m_chain.Tip();
     assert(tip != nullptr);
 
     CBlockIndex index;
     index.pprev = tip;
-    // CheckSequenceLocks() uses ::ChainActive().Height()+1 to evaluate
+    // CheckSequenceLocks() uses active_chainstate.m_chain.Height()+1 to evaluate
     // height based locks because when SequenceLocks() is called within
     // ConnectBlock(), the height of the block *being*
     // evaluated is what is used.
     // Thus if we want to know if a transaction can be part of the
-    // *next* block, we need to use one more than ::ChainActive().Height()
+    // *next* block, we need to use one more than active_chainstate.m_chain.Height()
     index.nHeight = tip->nHeight + 1;
 
     std::pair<int, int64_t> lockPair;
@@ -280,8 +288,8 @@ bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flag
         lockPair.second = lp->time;
     }
     else {
-        // CoinsTip() contains the UTXO set for ::ChainActive().Tip()
-        CCoinsViewMemPool viewMemPool(&::ChainstateActive().CoinsTip(), pool);
+        // CoinsTip() contains the UTXO set for active_chainstate.m_chain.Tip()
+        CCoinsViewMemPool viewMemPool(&active_chainstate.CoinsTip(), pool);
         std::vector<int> prevheights;
         prevheights.resize(tx.vin.size());
         for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) {
@@ -389,7 +397,8 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state,
 // Returns the script flags which should be checked for a given block
 static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& chainparams);
 
-static void LimitMempoolSize(CTxMemPool& pool, size_t limit, std::chrono::seconds age) EXCLUSIVE_LOCKS_REQUIRED(pool.cs, ::cs_main)
+static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache, size_t limit, std::chrono::seconds age)
+    EXCLUSIVE_LOCKS_REQUIRED(pool.cs, ::cs_main)
 {
     int expired = pool.Expire(GetTime<std::chrono::seconds>() - age);
     if (expired != 0) {
@@ -398,18 +407,20 @@ static void LimitMempoolSize(CTxMemPool& pool, size_t limit, std::chrono::second
 
     std::vector<COutPoint> vNoSpendsRemaining;
     pool.TrimToSize(limit, &vNoSpendsRemaining);
+    assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(coins_cache));
     for (const COutPoint& removed : vNoSpendsRemaining)
-        ::ChainstateActive().CoinsTip().Uncache(removed);
+        coins_cache.Uncache(removed);
 }
 
-static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static bool IsCurrentForFeeEstimation(CChainState& active_chainstate) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
 {
     AssertLockHeld(cs_main);
-    if (::ChainstateActive().IsInitialBlockDownload())
+    assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+    if (active_chainstate.IsInitialBlockDownload())
         return false;
-    if (::ChainActive().Tip()->GetBlockTime() < count_seconds(GetTime<std::chrono::seconds>() - MAX_FEE_ESTIMATION_TIP_AGE))
+    if (active_chainstate.m_chain.Tip()->GetBlockTime() < count_seconds(GetTime<std::chrono::seconds>() - MAX_FEE_ESTIMATION_TIP_AGE))
         return false;
-    if (::ChainActive().Height() < pindexBestHeader->nHeight - 1)
+    if (active_chainstate.m_chain.Height() < pindexBestHeader->nHeight - 1)
         return false;
     return true;
 }
@@ -427,10 +438,11 @@ static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
  * and instead just erase from the mempool as needed.
  */
 
-static void UpdateMempoolForReorg(CTxMemPool& mempool, DisconnectedBlockTransactions& disconnectpool, bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, mempool.cs)
+static void UpdateMempoolForReorg(CChainState& active_chainstate, CTxMemPool& mempool, DisconnectedBlockTransactions& disconnectpool, bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, mempool.cs)
 {
     AssertLockHeld(cs_main);
     AssertLockHeld(mempool.cs);
+    assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
     std::vector<uint256> vHashUpdate;
     // disconnectpool's insertion_order index sorts the entries from
     // oldest to newest, but the oldest entry will be the last tx from the
@@ -443,7 +455,7 @@ static void UpdateMempoolForReorg(CTxMemPool& mempool, DisconnectedBlockTransact
         // ignore validation errors in resurrected transactions
         CValidationState stateDummy;
         if (!fAddToMempool || (*it)->IsCoinBase() ||
-            !AcceptToMemoryPool(mempool, stateDummy, *it, nullptr /* pfMissingInputs */,
+            !AcceptToMemoryPool(active_chainstate, mempool, stateDummy, *it, nullptr /* pfMissingInputs */,
                                 true /* bypass_limits */, 0 /* nAbsurdFee */)) {
             // If the transaction doesn't make it in to the mempool, remove any
             // transactions that depend on it (which would now be orphans).
@@ -462,16 +474,18 @@ static void UpdateMempoolForReorg(CTxMemPool& mempool, DisconnectedBlockTransact
     mempool.UpdateTransactionsFromBlock(vHashUpdate);
 
     // We also need to remove any now-immature transactions
-    mempool.removeForReorg(&::ChainstateActive().CoinsTip(), ::ChainActive().Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
-
+    mempool.removeForReorg(active_chainstate, STANDARD_LOCKTIME_VERIFY_FLAGS);
     // Re-limit mempool size, in case we added any transactions
-    LimitMempoolSize(mempool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
+    LimitMempoolSize(mempool, active_chainstate.CoinsTip(), gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
 }
 
 // Used to avoid mempool polluting consensus critical paths if CCoinsViewMempool
 // were somehow broken and returning the wrong scriptPubKeys
-static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& view, const CTxMemPool& pool,
-                 unsigned int flags, PrecomputedTransactionData& txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
+static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationState& state,
+                 const CCoinsViewCache& view, const CTxMemPool& pool,
+                 unsigned int flags, PrecomputedTransactionData& txdata, CCoinsViewCache& coins_tip)
+                 EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
     AssertLockHeld(cs_main);
 
     // pool.cs should be locked already, but go ahead and re-take the lock here
@@ -495,7 +509,8 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt
             assert(txFrom->vout.size() > txin.prevout.n);
             assert(txFrom->vout[txin.prevout.n] == coin.out);
         } else {
-            const Coin& coinFromDisk = ::ChainstateActive().CoinsTip().AccessCoin(txin.prevout);
+            assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(coins_tip));
+            const Coin& coinFromDisk = coins_tip.AccessCoin(txin.prevout);
             assert(!coinFromDisk.IsSpent());
             assert(coinFromDisk.out == coin.out);
         }
@@ -510,11 +525,13 @@ namespace {
 class MemPoolAccept
 {
 public:
-    MemPoolAccept(CTxMemPool& mempool) : m_pool(mempool), m_view(&m_dummy), m_viewmempool(&::ChainstateActive().CoinsTip(), m_pool),
+    explicit MemPoolAccept(CTxMemPool& mempool, CChainState& active_chainstate) : m_pool(mempool), m_view(&m_dummy), m_viewmempool(&active_chainstate.CoinsTip(), m_pool), m_active_chainstate(active_chainstate),
         m_limit_ancestors(gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT)),
         m_limit_ancestor_size(gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000),
         m_limit_descendants(gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)),
-        m_limit_descendant_size(gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000) {}
+        m_limit_descendant_size(gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000) {
+        assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate));
+    }
 
     // We put the arguments we're handed into a struct, so we can pass them
     // around easier.
@@ -593,6 +610,7 @@ class MemPoolAccept
     CCoinsViewCache m_view;
     CCoinsViewMemPool m_viewmempool;
     CCoinsView m_dummy;
+    CChainState& m_active_chainstate;
 
     // The package limits in effect at the time of invocation.
     const size_t m_limit_ancestors;
@@ -630,7 +648,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
     if (!CheckTransaction(tx, state))
         return false; // state filled in by CheckTransaction
 
-    if (!ContextualCheckTransaction(tx, state, chainparams.GetConsensus(), ::ChainActive().Tip()))
+    assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate));
+    if (!ContextualCheckTransaction(tx, state, chainparams.GetConsensus(), m_active_chainstate.m_chain.Tip()))
         return error("%s: ContextualCheckTransaction: %s, %s", __func__, hash.ToString(), FormatStateMessage(state));
 
     if (tx.nVersion == 3 && tx.nType == TRANSACTION_QUORUM_COMMITMENT) {
@@ -657,7 +676,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
     // Only accept nLockTime-using transactions that can be mined in the next
     // block; we don't want our mempool filled up with transactions that can't
     // be mined yet.
-    if (!CheckFinalTx(tx, STANDARD_LOCKTIME_VERIFY_FLAGS))
+    if (!CheckFinalTx(m_active_chainstate.m_chain.Tip(), tx, STANDARD_LOCKTIME_VERIFY_FLAGS))
         return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, REJECT_NONSTANDARD, "non-final");
 
     // is it already in the memory pool?
@@ -695,7 +714,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
     LockPoints lp;
     m_view.SetBackend(m_viewmempool);
 
-    CCoinsViewCache& coins_cache = ::ChainstateActive().CoinsTip();
+    assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(m_active_chainstate.CoinsTip()));
+    CCoinsViewCache& coins_cache = m_active_chainstate.CoinsTip();
     // do all inputs exist?
     for (const CTxIn& txin : tx.vin) {
         if (!coins_cache.HaveCoinInCache(txin.prevout)) {
@@ -734,11 +754,13 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
     // be mined yet.
     // Must keep pool.cs for this unless we change CheckSequenceLocks to take a
     // CoinsViewCache instead of create its own
-    if (!CheckSequenceLocks(m_pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
+    assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate));
+    if (!CheckSequenceLocks(m_active_chainstate, m_pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
         return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, REJECT_NONSTANDARD, "non-BIP68-final");
 
+    assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_active_chainstate.m_blockman));
     CAmount nFees = 0;
-    if (!Consensus::CheckTxInputs(tx, state, m_view, g_chainman.m_blockman.GetSpendHeight(m_view), nFees)) {
+    if (!Consensus::CheckTxInputs(tx, state, m_view, m_active_chainstate.m_blockman.GetSpendHeight(m_view), nFees)) {
         return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state));
     }
 
@@ -763,7 +785,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
         }
     }
 
-    entry.reset(new CTxMemPoolEntry(ptx, nFees, nAcceptTime, ::ChainActive().Height(),
+    assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain));
+    entry.reset(new CTxMemPoolEntry(ptx, nFees, nAcceptTime, m_active_chainstate.m_chain.Height(),
             fSpendsCoinbase, nSigOps, lp));
     unsigned int nSize = entry->GetTxSize();
 
@@ -806,7 +829,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
     // DoS scoring a node for non-critical errors, e.g. duplicate keys because a TX is received that was already
     // mined
     // NOTE: we use UTXO here and do NOT allow mempool txes as masternode collaterals
-    if (!CheckSpecialTx(tx, ::ChainActive().Tip(), state, ::ChainstateActive().CoinsTip(), true))
+    if (!CheckSpecialTx(tx, m_active_chainstate.m_chain.Tip(), state, m_active_chainstate.CoinsTip(), true))
         return false;
 
     if (m_pool.existsProviderTxConflict(tx)) {
@@ -857,8 +880,10 @@ bool MemPoolAccept::ConsensusScriptChecks(ATMPArgs& args, Workspace& ws, Precomp
     // There is a similar check in CreateNewBlock() to prevent creating
     // invalid blocks (using TestBlockValidity), however allowing such
     // transactions into the mempool can be exploited as a DoS attack.
-    unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(::ChainActive().Tip(), chainparams.GetConsensus());
-    if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags, txdata)) {
+    assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain));
+    unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(m_active_chainstate.m_chain.Tip(), chainparams.GetConsensus());
+    assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(m_active_chainstate.CoinsTip()));
+    if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags, txdata, m_active_chainstate.CoinsTip())) {
         return error("%s: BUG! PLEASE REPORT THIS! CheckInputs failed against latest-block but not STANDARD flags %s, %s",
                 __func__, hash.ToString(), FormatStateMessage(state));
     }
@@ -881,7 +906,8 @@ bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws)
     // - the node is not behind
     // - the transaction is not dependent on any other transactions in the mempool
     // - the transaction is not a zero fee transaction
-    bool validForFeeEstimation = (nModifiedFees != 0) && !bypass_limits && IsCurrentForFeeEstimation() && m_pool.HasNoInputsOf(tx);
+    assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate));
+    bool validForFeeEstimation = (nModifiedFees != 0) && !bypass_limits && IsCurrentForFeeEstimation(m_active_chainstate) && m_pool.HasNoInputsOf(tx);
 
     // Store transaction in memory
     m_pool.addUnchecked(*entry, setAncestors, validForFeeEstimation);
@@ -905,7 +931,8 @@ bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws)
     }
 
     if (!bypass_limits) {
-        LimitMempoolSize(m_pool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
+        assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(m_active_chainstate.CoinsTip()));
+        LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip(), gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
         if (!m_pool.exists(hash))
             return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool full");
     }
@@ -954,13 +981,15 @@ bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs
 } // anon namespace
 
 /** (try to) add transaction to memory pool with a specified acceptance time **/
-static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx,
+static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CChainState& active_chainstate, CValidationState &state, const CTransactionRef &tx,
                         bool* pfMissingInputs, int64_t nAcceptTime, bool bypass_limits,
                         const CAmount nAbsurdFee, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
 {
     std::vector<COutPoint> coins_to_uncache;
     MemPoolAccept::ATMPArgs args { chainparams, state, pfMissingInputs, nAcceptTime, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept };
-    bool res = MemPoolAccept(pool).AcceptSingleTransaction(tx, args);
+
+    assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+    bool res = MemPoolAccept(pool, active_chainstate).AcceptSingleTransaction(tx, args);
     if (!res || test_accept) {
         if (!res) LogPrint(BCLog::MEMPOOL, "%s: %s %s (%s)\n", __func__, tx->GetHash().ToString(), state.GetRejectReason(), state.GetDebugMessage());
 
@@ -970,19 +999,20 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo
         // (`CCoinsViewCache::cacheCoins`).
 
         for (const COutPoint& hashTx : coins_to_uncache)
-            ::ChainstateActive().CoinsTip().Uncache(hashTx);
+            active_chainstate.CoinsTip().Uncache(hashTx);
     }
     // After we've (potentially) uncached entries, ensure our coins cache is still within its size limits
     CValidationState stateDummy;
-    ::ChainstateActive().FlushStateToDisk(chainparams, stateDummy, FlushStateMode::PERIODIC);
+    active_chainstate.FlushStateToDisk(chainparams, stateDummy, FlushStateMode::PERIODIC);
     return res;
 }
 
-bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx,
+bool AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx,
                         bool* pfMissingInputs, bool bypass_limits, const CAmount nAbsurdFee, bool test_accept)
 {
     const CChainParams& chainparams = Params();
-    return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, pfMissingInputs, GetTime(), bypass_limits, nAbsurdFee, test_accept);
+    assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+    return AcceptToMemoryPoolWithTime(chainparams, pool, active_chainstate, state, tx, pfMissingInputs, GetTime(), bypass_limits, nAbsurdFee, test_accept);
 }
 
 bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, std::vector<uint256> &hashes)
@@ -3038,7 +3068,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
         if (!DisconnectTip(state, chainparams, &disconnectpool)) {
             // This is likely a fatal error, but keep the mempool consistent,
             // just in case. Only remove from the mempool in this case.
-            UpdateMempoolForReorg(m_mempool, disconnectpool, false);
+            UpdateMempoolForReorg(::ChainstateActive(), m_mempool, disconnectpool, false);
 
             // If we're unable to disconnect a block during normal operation,
             // then that is a failure of our local system -- we should abort
@@ -3082,7 +3112,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
                     // A system error occurred (disk space, database error, ...).
                     // Make the mempool consistent with the current tip, just in case
                     // any observers try to use it before shutdown.
-                    UpdateMempoolForReorg(m_mempool, disconnectpool, false);
+                    UpdateMempoolForReorg(::ChainstateActive(), m_mempool, disconnectpool, false);
                     return false;
                 }
             } else {
@@ -3099,7 +3129,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
     if (fBlocksDisconnected) {
         // If any blocks were disconnected, disconnectpool may be non empty.  Add
         // any disconnected transactions back to the mempool.
-        UpdateMempoolForReorg(m_mempool, disconnectpool, true);
+        UpdateMempoolForReorg(::ChainstateActive(), m_mempool, disconnectpool, true);
     }
     m_mempool.check(&CoinsTip());
 
@@ -3347,7 +3377,7 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
         // transactions back to the mempool if disconnecting was successful,
         // and we're not doing a very deep invalidation (in which case
         // keeping the mempool up to date is probably futile anyway).
-        UpdateMempoolForReorg(m_mempool, disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
+        UpdateMempoolForReorg(::ChainstateActive(), m_mempool, disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
         if (!ret) return false;
         assert(invalid_walk_tip->pprev == m_chain.Tip());
 
@@ -3493,7 +3523,7 @@ bool CChainState::MarkConflictingBlock(CValidationState& state, const CChainPara
         if (!DisconnectTip(state, chainparams, &disconnectpool)) {
             // It's probably hopeless to try to make the mempool consistent
             // here if DisconnectTip failed, but we can try.
-            UpdateMempoolForReorg(m_mempool, disconnectpool, false);
+            UpdateMempoolForReorg(::ChainstateActive(), m_mempool, disconnectpool, false);
             return false;
         }
         if (pindexOldTip == pindexBestHeader) {
@@ -3515,7 +3545,7 @@ bool CChainState::MarkConflictingBlock(CValidationState& state, const CChainPara
 
     // DisconnectTip will add transactions to disconnectpool; try to add these
     // back to the mempool.
-    UpdateMempoolForReorg(m_mempool, disconnectpool, true);
+    UpdateMempoolForReorg(::ChainstateActive(), m_mempool, disconnectpool, true);
     } // m_mempool.cs
 
     // The resulting new best tip may not be in setBlockIndexCandidates anymore, so
@@ -4662,7 +4692,8 @@ bool static LoadBlockIndexDB(ChainstateManager& chainman, const CChainParams& ch
 void CChainState::LoadMempool(const ArgsManager& args)
 {
     if (args.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
-        ::LoadMempool(m_mempool);
+        assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
+        ::LoadMempool(m_mempool, *this);
     }
     m_mempool.SetIsLoaded(!ShutdownRequested());
 }
@@ -5411,7 +5442,7 @@ int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::D
 
 static const uint64_t MEMPOOL_DUMP_VERSION = 1;
 
-bool LoadMempool(CTxMemPool& pool)
+bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate)
 {
     const CChainParams& chainparams = Params();
     int64_t nExpiryTimeout = gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60;
@@ -5453,7 +5484,8 @@ bool LoadMempool(CTxMemPool& pool)
             CValidationState state;
             if (nTime + nExpiryTimeout > nNow) {
                 LOCK(cs_main);
-                AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, nullptr /* pfMissingInputs */, nTime,
+                assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+                AcceptToMemoryPoolWithTime(chainparams, pool, active_chainstate, state, tx, nullptr /* pfMissingInputs */, nTime,
                                            false /* bypass_limits */, 0 /* nAbsurdFee */, false /* test_accept */);
                 if (state.IsValid()) {
                     ++count;
diff --git a/src/validation.h b/src/validation.h
index f6cb13cce079..6e79d4a829e9 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -217,7 +217,7 @@ void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
 void PruneBlockFilesManual(int nManualPruneHeight);
 
 /** (try to) add transaction to memory pool */
-bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx,
+bool AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx,
                         bool* pfMissingInputs, bool bypass_limits,
                         const CAmount nAbsurdFee, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
 
@@ -246,12 +246,12 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight);
  *
  * See consensus/consensus.h for flag definitions.
  */
-bool CheckFinalTx(const CTransaction &tx, int flags = -1) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, int flags = -1) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
 
 /**
  * Test whether the LockPoints height and time are still valid on the current chain
  */
-bool TestLockPointValidity(const LockPoints* lp) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
 
 /**
  * Check if transaction will be BIP 68 final in the next block to be created.
@@ -264,7 +264,12 @@ bool TestLockPointValidity(const LockPoints* lp) EXCLUSIVE_LOCKS_REQUIRED(cs_mai
  *
  * See consensus/consensus.h for flag definitions.
  */
-bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flags, LockPoints* lp = nullptr, bool useExistingLockPoints = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+bool CheckSequenceLocks(CChainState& active_chainstate,
+                        const CTxMemPool& pool,
+                        const CTransaction& tx,
+                        int flags,
+                        LockPoints* lp = nullptr,
+                        bool useExistingLockPoints = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
 
 /**
  * Closure representing one script verification
@@ -1046,7 +1051,7 @@ CBlockFileInfo* GetBlockFileInfo(size_t n);
 bool DumpMempool(const CTxMemPool& pool);
 
 /** Load the mempool from disk. */
-bool LoadMempool(CTxMemPool& pool);
+bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate);
 
 //! Check whether the block associated with this index entry is pruned or not.
 inline bool IsBlockPruned(const CBlockIndex* pblockindex)

From 548e8704c58c861e2e1ecee5d24167b29d5ba3ed Mon Sep 17 00:00:00 2001
From: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com>
Date: Mon, 13 Mar 2023 15:37:00 +0000
Subject: [PATCH 34/35] merge bitcoin#21055: Prune remaining g_chainman usage
 in validation functions

Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
---
 src/evo/specialtxman.cpp                      |   2 +-
 src/governance/classes.cpp                    |   2 +-
 src/governance/governance.cpp                 |   4 +-
 src/governance/object.cpp                     |   4 +-
 src/init.cpp                                  |   8 +-
 src/llmq/chainlocks.cpp                       |   2 +-
 src/llmq/instantsend.cpp                      |   4 +-
 src/net_processing.cpp                        |   4 +-
 src/rpc/blockchain.cpp                        |  16 +-
 src/rpc/governance.cpp                        |   8 +-
 src/test/block_reward_reallocation_tests.cpp  |  26 +--
 .../dynamic_activation_thresholds_tests.cpp   |  20 +--
 src/test/evo_deterministicmns_tests.cpp       |   2 +-
 src/test/miner_tests.cpp                      |   2 +-
 src/test/util/setup_common.cpp                |   2 +-
 src/txmempool.cpp                             |  11 +-
 src/txmempool.h                               |   2 +-
 src/validation.cpp                            | 166 ++++++++----------
 src/validation.h                              |  42 ++---
 src/versionbits.h                             |   3 +
 20 files changed, 153 insertions(+), 177 deletions(-)

diff --git a/src/evo/specialtxman.cpp b/src/evo/specialtxman.cpp
index 7a85c790bbbf..54dd69e63c89 100644
--- a/src/evo/specialtxman.cpp
+++ b/src/evo/specialtxman.cpp
@@ -42,7 +42,7 @@ bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVali
         case TRANSACTION_QUORUM_COMMITMENT:
             return llmq::CheckLLMQCommitment(tx, pindexPrev, state);
         case TRANSACTION_MNHF_SIGNAL:
-            return VersionBitsTipState(Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024) == ThresholdState::ACTIVE && CheckMNHFTx(tx, pindexPrev, state);
+            return VersionBitsState(::ChainActive().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE && CheckMNHFTx(tx, pindexPrev, state);
         }
     } catch (const std::exception& e) {
         LogPrintf("%s -- failed: %s\n", __func__, e.what());
diff --git a/src/governance/classes.cpp b/src/governance/classes.cpp
index eb0b75c4eedb..c4e305165cb1 100644
--- a/src/governance/classes.cpp
+++ b/src/governance/classes.cpp
@@ -525,7 +525,7 @@ void CSuperblock::ParsePaymentSchedule(const std::string& strPaymentAddresses, c
 
     // TODO: script addresses limit here and cs_main lock in
     // CGovernanceManager::InitOnLoad()once DIP0024 is active
-    bool fAllowScript = (VersionBitsTipState(Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024) == ThresholdState::ACTIVE);
+    bool fAllowScript = (VersionBitsState(::ChainActive().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE);
     for (int i = 0; i < (int)vecParsed1.size(); i++) {
         CTxDestination dest = DecodeDestination(vecParsed1[i]);
         if (!IsValidDestination(dest)) {
diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp
index b584f4a0de18..d4c0b37336fb 100644
--- a/src/governance/governance.cpp
+++ b/src/governance/governance.cpp
@@ -395,7 +395,7 @@ void CGovernanceManager::UpdateCachesAndClean()
         } else {
             // NOTE: triggers are handled via triggerman
             if (pObj->GetObjectType() == GOVERNANCE_OBJECT_PROPOSAL) {
-                bool fAllowScript = (VersionBitsTipState(Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024) == ThresholdState::ACTIVE);
+                bool fAllowScript = (VersionBitsState(::ChainActive().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE);
                 CProposalValidator validator(pObj->GetDataAsHexString(), fAllowScript);
                 if (!validator.Validate()) {
                     LogPrint(BCLog::GOBJECT, "CGovernanceManager::UpdateCachesAndClean -- set for deletion expired obj %s\n", strHash);
@@ -635,7 +635,7 @@ void CGovernanceManager::SyncObjects(CNode& peer, CConnman& connman) const
 
     LogPrint(BCLog::GOBJECT, "CGovernanceManager::%s -- syncing all objects to peer=%d\n", __func__, peer.GetId());
 
-    bool fAllowScript = WITH_LOCK(cs_main, return VersionBitsTipState(Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024) == ThresholdState::ACTIVE);
+    bool fAllowScript = WITH_LOCK(cs_main, return VersionBitsState(::ChainActive().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE);
 
     LOCK(cs);
 
diff --git a/src/governance/object.cpp b/src/governance/object.cpp
index b05ddd1446c6..8b267e924590 100644
--- a/src/governance/object.cpp
+++ b/src/governance/object.cpp
@@ -469,7 +469,7 @@ bool CGovernanceObject::IsValidLocally(std::string& strError, bool& fMissingConf
 
     switch (nObjectType) {
     case GOVERNANCE_OBJECT_PROPOSAL: {
-        bool fAllowScript = (VersionBitsTipState(Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024) == ThresholdState::ACTIVE);
+        bool fAllowScript = (VersionBitsState(::ChainActive().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE);
         CProposalValidator validator(GetDataAsHexString(), fAllowScript);
         // Note: It's ok to have expired proposals
         // they are going to be cleared by CGovernanceManager::UpdateCachesAndClean()
@@ -692,7 +692,7 @@ void CGovernanceObject::Relay(CConnman& connman) const
         // But we don't want to relay it to pre-GOVSCRIPT_PROTO_VERSION peers if payment_address is p2sh
         // because they won't accept it anyway and will simply ban us eventually.
         LOCK(cs_main);
-        bool fAllowScript = (VersionBitsTipState(Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024) == ThresholdState::ACTIVE);
+        bool fAllowScript = (VersionBitsState(::ChainActive().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE);
         if (fAllowScript) {
             CProposalValidator validator(GetDataAsHexString(), false /* no script */);
             if (!validator.Validate(false /* ignore expiration */)) {
diff --git a/src/init.cpp b/src/init.cpp
index c0ac35e21831..f2888ae7bbb9 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -904,7 +904,7 @@ static void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImp
         fReindex = false;
         LogPrintf("Reindexing finished\n");
         // To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked):
-        LoadGenesisBlock(chainparams);
+        ::ChainstateActive().LoadGenesisBlock(chainparams);
     }
 
     // -loadblock=
@@ -2094,7 +2094,7 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc
                 // If we're not mid-reindex (based on disk + args), add a genesis block on disk
                 // (otherwise we use the one already on disk).
                 // This is called again in ThreadImport after the reindex completes.
-                if (!fReindex && !LoadGenesisBlock(chainparams)) {
+                if (!fReindex && !::ChainstateActive().LoadGenesisBlock(chainparams)) {
                     strLoadError = _("Error initializing block database");
                     break;
                 }
@@ -2194,7 +2194,7 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc
                         // work when we allow VerifyDB to be parameterized by chainstate.
                         if (&::ChainstateActive() == chainstate &&
                             !CVerifyDB().VerifyDB(
-                                chainparams, &chainstate->CoinsDB(),
+                                chainparams, *chainstate, &chainstate->CoinsDB(),
                                 *node.evodb,
                                 args.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
                                 args.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
@@ -2215,7 +2215,7 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc
                     }
 
                     if (args.GetArg("-checklevel", DEFAULT_CHECKLEVEL) >= 3) {
-                        ResetBlockFailureFlags(nullptr);
+                        ::ChainstateActive().ResetBlockFailureFlags(nullptr);
                     }
                 }
             } catch (const std::exception& e) {
diff --git a/src/llmq/chainlocks.cpp b/src/llmq/chainlocks.cpp
index bcdb89d34840..746a36de4f80 100644
--- a/src/llmq/chainlocks.cpp
+++ b/src/llmq/chainlocks.cpp
@@ -501,7 +501,7 @@ void CChainLocksHandler::EnforceBestChainLock()
     // For each of these blocks, check if there are children that are NOT part of the chain referenced by clsig
     // and mark all of them as conflicting.
     LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- enforcing block %s via CLSIG (%s)\n", __func__, pindex->GetBlockHash().ToString(), clsig->ToString());
-    EnforceBlock(state, params, pindex);
+    ::ChainstateActive().EnforceBlock(state, params, pindex);
 
     bool activateNeeded = WITH_LOCK(::cs_main, return ::ChainActive().Tip()->GetAncestor(currentBestChainLockBlockIndex->nHeight)) != currentBestChainLockBlockIndex;
 
diff --git a/src/llmq/instantsend.cpp b/src/llmq/instantsend.cpp
index 7ee02db87cee..d39fb1db88ae 100644
--- a/src/llmq/instantsend.cpp
+++ b/src/llmq/instantsend.cpp
@@ -1480,7 +1480,7 @@ void CInstantSendManager::ResolveBlockConflicts(const uint256& islockHash, const
         CValidationState state;
         // need non-const pointer
         auto pindex2 = WITH_LOCK(::cs_main, return g_chainman.m_blockman.LookupBlockIndex(pindex->GetBlockHash()));
-        if (!InvalidateBlock(state, Params(), pindex2)) {
+        if (!::ChainstateActive().InvalidateBlock(state, Params(), pindex2)) {
             LogPrintf("CInstantSendManager::%s -- InvalidateBlock failed: %s\n", __func__, FormatStateMessage(state));
             // This should not have happened and we are in a state were it's not safe to continue anymore
             assert(false);
@@ -1490,7 +1490,7 @@ void CInstantSendManager::ResolveBlockConflicts(const uint256& islockHash, const
         } else {
             LogPrintf("CInstantSendManager::%s -- resetting block %s\n", __func__, pindex2->GetBlockHash().ToString());
             LOCK(cs_main);
-            ResetBlockFailureFlags(pindex2);
+            ::ChainstateActive().ResetBlockFailureFlags(pindex2);
         }
     }
 
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 5b9882a6dcb2..f6001796eb12 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -2303,7 +2303,7 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uin
             EraseOrphanTx(orphanHash);
             done = true;
         }
-        mempool.check(&::ChainstateActive().CoinsTip());
+        mempool.check(g_chainman.ActiveChainstate());
     }
 }
 
@@ -3377,7 +3377,7 @@ void PeerLogicValidation::ProcessMessage(
                 CCoinJoin::AddDSTX(dstx);
             }
 
-            m_mempool.check(&::ChainstateActive().CoinsTip());
+            m_mempool.check(m_chainman.ActiveChainstate());
             RelayTransaction(tx.GetHash(), m_connman);
 
             for (unsigned int i = 0; i < tx.vout.size(); i++) {
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 5c10e93cc0fa..d00e3773c7dc 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -1238,7 +1238,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
         height = chainHeight - MIN_BLOCKS_TO_KEEP;
     }
 
-    PruneBlockFilesManual(height);
+    PruneBlockFilesManual(::ChainstateActive(), height);
     const CBlockIndex* block = ::ChainActive().Tip();
     CHECK_NONFATAL(block);
     while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
@@ -1420,7 +1420,7 @@ static UniValue verifychain(const JSONRPCRequest& request)
     const NodeContext& node_context = EnsureNodeContext(request.context);
 
     return CVerifyDB().VerifyDB(
-        Params(), &::ChainstateActive().CoinsTip(), *node_context.evodb, check_level, check_depth);
+        Params(), ::ChainstateActive(), &::ChainstateActive().CoinsTip(), *node_context.evodb, check_level, check_depth);
 }
 
 /** Implementation of IsSuperMajority with better feedback */
@@ -1456,7 +1456,7 @@ static UniValue SoftForkDesc(const std::string &name, int version, const CBlockI
 static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Consensus::DeploymentPos id)
 {
     UniValue rv(UniValue::VOBJ);
-    const ThresholdState thresholdState = VersionBitsTipState(consensusParams, id);
+    const ThresholdState thresholdState = VersionBitsState(::ChainActive().Tip(), consensusParams, id, versionbitscache);
     switch (thresholdState) {
     case ThresholdState::DEFINED: rv.pushKV("status", "defined"); break;
     case ThresholdState::STARTED: rv.pushKV("status", "started"); break;
@@ -1470,11 +1470,11 @@ static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Conse
     }
     rv.pushKV("start_time", consensusParams.vDeployments[id].nStartTime);
     rv.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
-    rv.pushKV("since", VersionBitsTipStateSinceHeight(consensusParams, id));
+    rv.pushKV("since", VersionBitsStateSinceHeight(::ChainActive().Tip(), consensusParams, id, versionbitscache));
     if (ThresholdState::STARTED == thresholdState)
     {
         UniValue statsUV(UniValue::VOBJ);
-        BIP9Stats statsStruct = VersionBitsTipStatistics(consensusParams, id);
+        BIP9Stats statsStruct = VersionBitsStatistics(::ChainActive().Tip(), consensusParams, id, versionbitscache);
         statsUV.pushKV("period", statsStruct.period);
         statsUV.pushKV("threshold", statsStruct.threshold);
         statsUV.pushKV("elapsed", statsStruct.elapsed);
@@ -1818,7 +1818,7 @@ static UniValue preciousblock(const JSONRPCRequest& request)
     }
 
     CValidationState state;
-    PreciousBlock(state, Params(), pblockindex);
+    ::ChainstateActive().PreciousBlock(state, Params(), pblockindex);
 
     if (!state.IsValid()) {
         throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state));
@@ -1853,7 +1853,7 @@ static UniValue invalidateblock(const JSONRPCRequest& request)
             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
         }
     }
-    InvalidateBlock(state, Params(), pblockindex);
+    ::ChainstateActive().InvalidateBlock(state, Params(), pblockindex);
 
     if (state.IsValid()) {
         ::ChainstateActive().ActivateBestChain(state, Params());
@@ -1891,7 +1891,7 @@ static UniValue reconsiderblock(const JSONRPCRequest& request)
             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
         }
 
-        ResetBlockFailureFlags(pblockindex);
+        ::ChainstateActive().ResetBlockFailureFlags(pblockindex);
     }
 
     CValidationState state;
diff --git a/src/rpc/governance.cpp b/src/rpc/governance.cpp
index 585d79530eb8..e79eb3251b23 100644
--- a/src/rpc/governance.cpp
+++ b/src/rpc/governance.cpp
@@ -110,7 +110,7 @@ static UniValue gobject_check(const JSONRPCRequest& request)
 
     if (govobj.GetObjectType() == GOVERNANCE_OBJECT_PROPOSAL) {
         LOCK(cs_main);
-        bool fAllowScript = (VersionBitsTipState(Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024) == ThresholdState::ACTIVE);
+        bool fAllowScript = (VersionBitsState(::ChainActive().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE);
         CProposalValidator validator(strDataHex, fAllowScript);
         if (!validator.Validate())  {
             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid proposal data, error messages:" + validator.GetErrorMessages());
@@ -185,7 +185,7 @@ static UniValue gobject_prepare(const JSONRPCRequest& request)
 
     if (govobj.GetObjectType() == GOVERNANCE_OBJECT_PROPOSAL) {
         LOCK(cs_main);
-        bool fAllowScript = (VersionBitsTipState(Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024) == ThresholdState::ACTIVE);
+        bool fAllowScript = (VersionBitsState(::ChainActive().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE);
         CProposalValidator validator(strDataHex, fAllowScript);
         if (!validator.Validate()) {
             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid proposal data, error messages:" + validator.GetErrorMessages());
@@ -223,7 +223,7 @@ static UniValue gobject_prepare(const JSONRPCRequest& request)
 
     CTransactionRef tx;
 
-    bool fork_active = WITH_LOCK(cs_main, return VersionBitsTipState(Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024) == ThresholdState::ACTIVE);
+    bool fork_active = WITH_LOCK(cs_main, return VersionBitsState(::ChainActive().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE);
 
     if (!wallet->GetBudgetSystemCollateralTX(tx, govobj.GetHash(), govobj.GetMinCollateralFee(fork_active), outpoint)) {
         std::string err = "Error making collateral transaction for governance object. Please check your wallet balance and make sure your wallet is unlocked.";
@@ -350,7 +350,7 @@ static UniValue gobject_submit(const JSONRPCRequest& request)
 
     if (govobj.GetObjectType() == GOVERNANCE_OBJECT_PROPOSAL) {
         LOCK(cs_main);
-        bool fAllowScript = (VersionBitsTipState(Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024) == ThresholdState::ACTIVE);
+        bool fAllowScript = (VersionBitsState(::ChainActive().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE);
         CProposalValidator validator(strDataHex, fAllowScript);
         if (!validator.Validate()) {
             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid proposal data, error messages:" + validator.GetErrorMessages());
diff --git a/src/test/block_reward_reallocation_tests.cpp b/src/test/block_reward_reallocation_tests.cpp
index 8c457d79237e..81a093136fc1 100644
--- a/src/test/block_reward_reallocation_tests.cpp
+++ b/src/test/block_reward_reallocation_tests.cpp
@@ -182,9 +182,9 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
         }
         LOCK(cs_main);
         if (expected_lockin) {
-            BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::LOCKED_IN);
+            BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::LOCKED_IN);
         } else {
-            BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::STARTED);
+            BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::STARTED);
         }
     };
 
@@ -205,7 +205,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
         BOOST_ASSERT(deterministicMNManager->GetListAtChainTip().HasMN(tx.GetHash()));
 
         BOOST_CHECK_EQUAL(::ChainActive().Height(), 498);
-        BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::DEFINED);
+        BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::DEFINED);
     }
 
     CreateAndProcessBlock({}, coinbaseKey);
@@ -216,8 +216,8 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
         BOOST_CHECK_EQUAL(::ChainActive().Height(), 499);
         deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip());
         BOOST_ASSERT(deterministicMNManager->GetListAtChainTip().HasMN(tx.GetHash()));
-        BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::STARTED);
-        BOOST_CHECK_EQUAL(VersionBitsTipStatistics(consensus_params, deployment_id).threshold, threshold(0));
+        BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::STARTED);
+        BOOST_CHECK_EQUAL(VersionBitsStatistics(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache).threshold, threshold(0));
         // Next block should be signaling by default
         const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler,  *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey);
         const uint32_t bitmask = ((uint32_t)1) << consensus_params.vDeployments[deployment_id].bit;
@@ -233,8 +233,8 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
         BOOST_CHECK_EQUAL(::ChainActive().Height(), 999);
         deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip());
         BOOST_ASSERT(deterministicMNManager->GetListAtChainTip().HasMN(tx.GetHash()));
-        BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::STARTED);
-        BOOST_CHECK_EQUAL(VersionBitsTipStatistics(consensus_params, deployment_id).threshold, threshold(1));
+        BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::STARTED);
+        BOOST_CHECK_EQUAL(VersionBitsStatistics(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache).threshold, threshold(1));
     }
 
     signal(threshold(1) - 1, false); // 1 block short again
@@ -245,8 +245,8 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
         BOOST_CHECK_EQUAL(::ChainActive().Height(), 1499);
         deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip());
         BOOST_ASSERT(deterministicMNManager->GetListAtChainTip().HasMN(tx.GetHash()));
-        BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::STARTED);
-        BOOST_CHECK_EQUAL(VersionBitsTipStatistics(consensus_params, deployment_id).threshold, threshold(2));
+        BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::STARTED);
+        BOOST_CHECK_EQUAL(VersionBitsStatistics(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache).threshold, threshold(2));
     }
 
     signal(threshold(2), true); // just enough to lock in
@@ -257,14 +257,14 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
         BOOST_CHECK_EQUAL(::ChainActive().Height(), 1999);
         deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip());
         BOOST_ASSERT(deterministicMNManager->GetListAtChainTip().HasMN(tx.GetHash()));
-        BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::LOCKED_IN);
+        BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::LOCKED_IN);
     }
 
     for ([[maybe_unused]] auto _ : irange::range(499)) {
         // Still LOCKED_IN at height = 2498
         CreateAndProcessBlock({}, coinbaseKey);
         LOCK(cs_main);
-        BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::LOCKED_IN);
+        BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::LOCKED_IN);
     }
     CreateAndProcessBlock({}, coinbaseKey);
 
@@ -274,8 +274,8 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
         BOOST_CHECK_EQUAL(::ChainActive().Height(), 2499);
         deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip());
         BOOST_ASSERT(deterministicMNManager->GetListAtChainTip().HasMN(tx.GetHash()));
-        BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::ACTIVE);
-        BOOST_CHECK_EQUAL(VersionBitsTipStateSinceHeight(consensus_params, deployment_id), 2500);
+        BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::ACTIVE);
+        BOOST_CHECK_EQUAL(VersionBitsStateSinceHeight(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), 2500);
     }
 
     {
diff --git a/src/test/dynamic_activation_thresholds_tests.cpp b/src/test/dynamic_activation_thresholds_tests.cpp
index 1078c2c6f267..bf0ae6793111 100644
--- a/src/test/dynamic_activation_thresholds_tests.cpp
+++ b/src/test/dynamic_activation_thresholds_tests.cpp
@@ -53,9 +53,9 @@ struct TestChainDATSetup : public TestChainSetup
         }
         LOCK(cs_main);
         if (expected_lockin) {
-            BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::LOCKED_IN);
+            BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::LOCKED_IN);
         } else {
-            BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::STARTED);
+            BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::STARTED);
         }
     }
 
@@ -67,7 +67,7 @@ struct TestChainDATSetup : public TestChainSetup
         {
             LOCK(cs_main);
             BOOST_CHECK_EQUAL(::ChainActive().Height(), window - 2);
-            BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::DEFINED);
+            BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::DEFINED);
         }
 
         CreateAndProcessBlock({}, coinbaseKey);
@@ -76,8 +76,8 @@ struct TestChainDATSetup : public TestChainSetup
             LOCK(cs_main);
             // Advance from DEFINED to STARTED at height = window - 1
             BOOST_CHECK_EQUAL(::ChainActive().Height(), window - 1);
-            BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::STARTED);
-            BOOST_CHECK_EQUAL(VersionBitsTipStatistics(consensus_params, deployment_id).threshold, threshold(0));
+            BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::STARTED);
+            BOOST_CHECK_EQUAL(VersionBitsStatistics(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache).threshold, threshold(0));
             // Next block should be signaling by default
             const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey);
             const uint32_t bitmask = ((uint32_t)1) << consensus_params.vDeployments[deployment_id].bit;
@@ -93,17 +93,17 @@ struct TestChainDATSetup : public TestChainSetup
                 // Still STARTED but with a (potentially) new threshold
                 LOCK(cs_main);
                 BOOST_CHECK_EQUAL(::ChainActive().Height(), window * (i + 2) - 1);
-                BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::STARTED);
-                const auto vbts = VersionBitsTipStatistics(consensus_params, deployment_id);
+                BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::STARTED);
+                const auto vbts = VersionBitsStatistics(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache);
                 BOOST_CHECK_EQUAL(vbts.threshold, threshold(i + 1));
                 BOOST_CHECK(vbts.threshold <= th_start);
                 BOOST_CHECK(vbts.threshold >= th_end);
             }
         }
         if (LOCK(cs_main); check_activation_at_min) {
-            BOOST_CHECK_EQUAL(VersionBitsTipStatistics(consensus_params, deployment_id).threshold, th_end);
+            BOOST_CHECK_EQUAL(VersionBitsStatistics(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache).threshold, th_end);
         } else {
-            BOOST_CHECK(VersionBitsTipStatistics(consensus_params, deployment_id).threshold > th_end);
+            BOOST_CHECK(VersionBitsStatistics(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache).threshold > th_end);
         }
 
         // activate
@@ -113,7 +113,7 @@ struct TestChainDATSetup : public TestChainSetup
         }
         {
             LOCK(cs_main);
-            BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::ACTIVE);
+            BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::ACTIVE);
         }
 
     }
diff --git a/src/test/evo_deterministicmns_tests.cpp b/src/test/evo_deterministicmns_tests.cpp
index 20528bc7875c..da8fef5f36fe 100644
--- a/src/test/evo_deterministicmns_tests.cpp
+++ b/src/test/evo_deterministicmns_tests.cpp
@@ -655,7 +655,7 @@ void FuncVerifyDB(TestChainSetup& setup)
 
     // Verify db consistency
     LOCK(cs_main);
-    BOOST_ASSERT(CVerifyDB().VerifyDB(Params(), &::ChainstateActive().CoinsTip(), *(setup.m_node.evodb), 4, 2));
+    BOOST_ASSERT(CVerifyDB().VerifyDB(Params(), ::ChainstateActive(), &::ChainstateActive().CoinsTip(), *(setup.m_node.evodb), 4, 2));
 }
 
 BOOST_AUTO_TEST_SUITE(evo_dip3_activation_tests)
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 8244b5d464e8..c14f3f3d19bd 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -537,7 +537,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
     } // unlock cs_main while calling InvalidateBlock
 
     CValidationState state;
-    InvalidateBlock(state, chainparams, WITH_LOCK(cs_main, return ::ChainActive().Tip()));
+    ::ChainstateActive().InvalidateBlock(state, chainparams, WITH_LOCK(cs_main, return ::ChainActive().Tip()));
 
     SetMockTime(0);
     m_node.mempool->clear();
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 8f38c9306e85..9ff2a2badf75 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -239,7 +239,7 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
     assert(!::ChainstateActive().CanFlushToDisk());
     ::ChainstateActive().InitCoinsCache(1 << 23);
     assert(::ChainstateActive().CanFlushToDisk());
-    if (!LoadGenesisBlock(chainparams)) {
+    if (!::ChainstateActive().LoadGenesisBlock(chainparams)) {
         throw std::runtime_error("LoadGenesisBlock failed.");
     }
 
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index a9a202991e34..e033b595ff4b 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -1030,7 +1030,7 @@ static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& m
     UpdateCoins(tx, mempoolDuplicate, std::numeric_limits<int>::max());
 }
 
-void CTxMemPool::check(const CCoinsViewCache *pcoins) const
+void CTxMemPool::check(CChainState& active_chainstate) const
 {
     if (m_check_ratio == 0) return;
 
@@ -1043,8 +1043,11 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
     uint64_t checkTotal = 0;
     uint64_t innerUsage = 0;
 
-    CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(pcoins));
-    const int64_t spendheight = g_chainman.m_blockman.GetSpendHeight(mempoolDuplicate);
+    CCoinsViewCache& active_coins_tip = active_chainstate.CoinsTip();
+    assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(active_coins_tip)); // TODO: REVIEW-ONLY, REMOVE IN FUTURE COMMIT
+    CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(&active_coins_tip));
+    const int64_t spendheight = active_chainstate.m_chain.Height() + 1;
+    assert(g_chainman.m_blockman.GetSpendHeight(mempoolDuplicate) == spendheight); // TODO: REVIEW-ONLY, REMOVE IN FUTURE COMMIT
 
     std::list<const CTxMemPoolEntry*> waitingOnDependants;
     for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
@@ -1067,7 +1070,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
                 fDependsWait = true;
                 setParentCheck.insert(it2);
             } else {
-                assert(pcoins->HaveCoin(txin.prevout));
+                assert(active_coins_tip.HaveCoin(txin.prevout));
             }
             // Check whether its inputs are marked in mapNextTx.
             auto it3 = mapNextTx.find(txin.prevout);
diff --git a/src/txmempool.h b/src/txmempool.h
index e093a6733c73..6fa21023a867 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -597,7 +597,7 @@ class CTxMemPool
      * all inputs are in the mapNextTx array). If sanity-checking is turned off,
      * check does nothing.
      */
-    void check(const CCoinsViewCache *pcoins) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+    void check(CChainState& active_chainstate) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
 
     // addUnchecked must updated state for all ancestors of a given transaction,
     // to track size/count of descendant transactions.  First version of
diff --git a/src/validation.cpp b/src/validation.cpp
index f3a590bd7cec..b725cc1dd952 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -208,6 +208,7 @@ static FlatFileSeq UndoFileSeq();
 bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, int flags)
 {
     AssertLockHeld(cs_main);
+    assert(active_chain_tip); // TODO: Make active_chain_tip a reference
     assert(std::addressof(*::ChainActive().Tip()) == std::addressof(*active_chain_tip));
 
     // By convention a negative value for flags indicates that the
@@ -218,12 +219,12 @@ bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, i
     // scheduled, so no flags are set.
     flags = std::max(flags, 0);
 
-    // CheckFinalTx() uses ::ChainActive().Height()+1 to evaluate
+    // CheckFinalTx() uses active_chain_tip.Height()+1 to evaluate
     // nLockTime because when IsFinalTx() is called within
     // CBlock::AcceptBlock(), the height of the block *being*
     // evaluated is what is used. Thus if we want to know if a
     // transaction can be part of the *next* block, we need to call
-    // IsFinalTx() with one more than ::ChainActive().Height().
+    // IsFinalTx() with one more than active_chain_tip.Height().
     const int nBlockHeight = active_chain_tip->nHeight + 1;
 
     // BIP113 requires that time-locked transactions have nLockTime set to
@@ -1389,16 +1390,18 @@ static void AlertNotify(const std::string& strMessage)
 #endif
 }
 
-static void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void CChainState::CheckForkWarningConditions()
 {
     AssertLockHeld(cs_main);
+    assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
+
     // Before we get past initial download, we cannot reliably alert about forks
     // (we assume we don't get stuck on a fork before finishing our initial sync)
-    if (::ChainstateActive().IsInitialBlockDownload()) {
+    if (IsInitialBlockDownload()) {
         return;
     }
 
-    if (pindexBestInvalid && pindexBestInvalid->nChainWork > ::ChainActive().Tip()->nChainWork + (GetBlockProof(*::ChainActive().Tip()) * 6)) {
+    if (pindexBestInvalid && pindexBestInvalid->nChainWork > m_chain.Tip()->nChainWork + (GetBlockProof(*m_chain.Tip()) * 6)) {
         LogPrintf("%s: Warning: Found invalid chain which has higher work (at least ~6 blocks worth of work) than our best chain.\nChain state database corruption likely.\n", __func__);
         SetfLargeWorkInvalidChainFound(true);
     } else {
@@ -1407,38 +1410,42 @@ static void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
 }
 
 // Called both upon regular invalid block discovery *and* InvalidateBlock
-void static InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void CChainState::InvalidChainFound(CBlockIndex* pindexNew)
 {
+    assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
+
     statsClient.inc("warnings.InvalidChainFound", 1.0f);
 
     if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork)
         pindexBestInvalid = pindexNew;
     if (pindexBestHeader != nullptr && pindexBestHeader->GetAncestor(pindexNew->nHeight) == pindexNew) {
-        pindexBestHeader = ::ChainActive().Tip();
+        pindexBestHeader = m_chain.Tip();
     }
 
     LogPrintf("%s: invalid block=%s  height=%d  log2_work=%.8f  date=%s\n", __func__,
       pindexNew->GetBlockHash().ToString(), pindexNew->nHeight,
       log(pindexNew->nChainWork.getdouble())/log(2.0), FormatISO8601DateTime(pindexNew->GetBlockTime()));
-    CBlockIndex *tip = ::ChainActive().Tip();
+    CBlockIndex *tip = m_chain.Tip();
     assert (tip);
     LogPrintf("%s:  current best=%s  height=%d  log2_work=%.8f  date=%s\n", __func__,
-      tip->GetBlockHash().ToString(), ::ChainActive().Height(), log(tip->nChainWork.getdouble())/log(2.0),
+      tip->GetBlockHash().ToString(), m_chain.Height(), log(tip->nChainWork.getdouble())/log(2.0),
       FormatISO8601DateTime(tip->GetBlockTime()));
     CheckForkWarningConditions();
 }
 
-void static ConflictingChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void CChainState::ConflictingChainFound(CBlockIndex* pindexNew)
 {
+    assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
+
     statsClient.inc("warnings.ConflictingChainFound", 1.0f);
 
     LogPrintf("%s: conflicting block=%s  height=%d  log2_work=%.8f  date=%s\n", __func__,
       pindexNew->GetBlockHash().ToString(), pindexNew->nHeight,
       log(pindexNew->nChainWork.getdouble())/log(2.0), FormatISO8601DateTime(pindexNew->GetBlockTime()));
-    CBlockIndex *tip = ::ChainActive().Tip();
+    CBlockIndex *tip = m_chain.Tip();
     assert (tip);
     LogPrintf("%s:  current best=%s  height=%d  log2_work=%.8f  date=%s\n", __func__,
-      tip->GetBlockHash().ToString(), ::ChainActive().Height(), log(tip->nChainWork.getdouble())/log(2.0),
+      tip->GetBlockHash().ToString(), m_chain.Height(), log(tip->nChainWork.getdouble())/log(2.0),
       FormatISO8601DateTime(tip->GetBlockTime()));
     CheckForkWarningConditions();
 }
@@ -2742,7 +2749,7 @@ static void AppendWarning(std::string& res, const std::string& warn)
 }
 
 /** Check warning conditions and do some notifications on new chain tip set. */
-void static UpdateTip(CTxMemPool& mempool, const CBlockIndex *pindexNew, const CChainParams& chainParams, const CEvoDB& evoDb)
+static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const CChainParams& chainParams, CChainState& active_chainstate, const CEvoDB& evoDb)
     EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
 {
     // New best block
@@ -2755,7 +2762,8 @@ void static UpdateTip(CTxMemPool& mempool, const CBlockIndex *pindexNew, const C
     }
 
     std::string warningMessages;
-    if (!::ChainstateActive().IsInitialBlockDownload())
+    assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+    if (!active_chainstate.IsInitialBlockDownload())
     {
         int nUpgraded = 0;
         const CBlockIndex* pindex = pindexNew;
@@ -2782,11 +2790,12 @@ void static UpdateTip(CTxMemPool& mempool, const CBlockIndex *pindexNew, const C
         if (nUpgraded > 0)
             AppendWarning(warningMessages, strprintf(_("%d of last 100 blocks have unexpected version").translated, nUpgraded));
     }
+    assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
     LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo) evodb_cache=%.1fMiB%s\n", __func__,
       pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
       log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx,
       FormatISO8601DateTime(pindexNew->GetBlockTime()),
-      GuessVerificationProgress(chainParams.TxData(), pindexNew), ::ChainstateActive().CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), ::ChainstateActive().CoinsTip().GetCacheSize(),
+      GuessVerificationProgress(chainParams.TxData(), pindexNew), active_chainstate.CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), active_chainstate.CoinsTip().GetCacheSize(),
               evoDb.GetMemoryUsage() * (1.0 / (1<<20)),
       !warningMessages.empty() ? strprintf(" warning='%s'", warningMessages) : "");
 }
@@ -2846,7 +2855,7 @@ bool CChainState::DisconnectTip(CValidationState& state, const CChainParams& cha
 
     m_chain.SetTip(pindexDelete->pprev);
 
-    UpdateTip(m_mempool, pindexDelete->pprev, chainparams, *m_evoDb);
+    UpdateTip(m_mempool, pindexDelete->pprev, chainparams, *this, *m_evoDb);
     // Let wallets know transactions went from 1-confirmed to
     // 0-confirmed or conflicted:
     GetMainSignals().BlockDisconnected(pblock, pindexDelete);
@@ -2958,7 +2967,7 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
     disconnectpool.removeForBlock(blockConnecting.vtx);
     // Update m_chain & related variables.
     m_chain.SetTip(pindexNew);
-    UpdateTip(m_mempool, pindexNew, chainparams, *m_evoDb);
+    UpdateTip(m_mempool, pindexNew, chainparams, *this, *m_evoDb);
 
     int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
     LogPrint(BCLog::BENCHMARK, "  - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime5) * MILLI, nTimePostConnect * MICRO, nTimePostConnect * MILLI / nBlocksTotal);
@@ -3057,6 +3066,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
 {
     AssertLockHeld(cs_main);
     AssertLockHeld(m_mempool.cs);
+    assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
 
     const CBlockIndex* pindexOldTip = m_chain.Tip();
     const CBlockIndex* pindexFork = m_chain.FindFork(pindexMostWork);
@@ -3068,7 +3078,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
         if (!DisconnectTip(state, chainparams, &disconnectpool)) {
             // This is likely a fatal error, but keep the mempool consistent,
             // just in case. Only remove from the mempool in this case.
-            UpdateMempoolForReorg(::ChainstateActive(), m_mempool, disconnectpool, false);
+            UpdateMempoolForReorg(*this, m_mempool, disconnectpool, false);
 
             // If we're unable to disconnect a block during normal operation,
             // then that is a failure of our local system -- we should abort
@@ -3112,7 +3122,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
                     // A system error occurred (disk space, database error, ...).
                     // Make the mempool consistent with the current tip, just in case
                     // any observers try to use it before shutdown.
-                    UpdateMempoolForReorg(::ChainstateActive(), m_mempool, disconnectpool, false);
+                    UpdateMempoolForReorg(*this, m_mempool, disconnectpool, false);
                     return false;
                 }
             } else {
@@ -3129,9 +3139,9 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
     if (fBlocksDisconnected) {
         // If any blocks were disconnected, disconnectpool may be non empty.  Add
         // any disconnected transactions back to the mempool.
-        UpdateMempoolForReorg(::ChainstateActive(), m_mempool, disconnectpool, true);
+        UpdateMempoolForReorg(*this, m_mempool, disconnectpool, true);
     }
-    m_mempool.check(&CoinsTip());
+    m_mempool.check(*this);
 
     CheckForkWarningConditions();
 
@@ -3303,9 +3313,6 @@ bool CChainState::PreciousBlock(CValidationState& state, const CChainParams& par
 
     return ActivateBestChain(state, params, std::shared_ptr<const CBlock>());
 }
-bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex) {
-    return ::ChainstateActive().PreciousBlock(state, params, pindex);
-}
 
 bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex)
 {
@@ -3377,7 +3384,8 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
         // transactions back to the mempool if disconnecting was successful,
         // and we're not doing a very deep invalidation (in which case
         // keeping the mempool up to date is probably futile anyway).
-        UpdateMempoolForReorg(::ChainstateActive(), m_mempool, disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
+        assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
+        UpdateMempoolForReorg(*this, m_mempool, disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
         if (!ret) return false;
         assert(invalid_walk_tip->pprev == m_chain.Tip());
 
@@ -3460,19 +3468,16 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
     return true;
 }
 
-bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) {
-    return ::ChainstateActive().InvalidateBlock(state, chainparams, pindex);
-}
-
 void CChainState::EnforceBlock(CValidationState& state, const CChainParams& chainparams, const CBlockIndex *pindex)
 {
     AssertLockNotHeld(::cs_main);
 
     LOCK2(m_cs_chainstate, ::cs_main);
+    assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
 
     const CBlockIndex* pindex_walk = pindex;
 
-    while (pindex_walk && !::ChainActive().Contains(pindex_walk)) {
+    while (pindex_walk && !m_chain.Contains(pindex_walk)) {
         // Mark all blocks that have the same prevBlockHash but are not equal to blockHash as conflicting
         auto itp = g_chainman.PrevBlockIndex().equal_range(pindex_walk->pprev->GetBlockHash());
         for (auto jt = itp.first; jt != itp.second; ++jt) {
@@ -3495,13 +3500,10 @@ void CChainState::EnforceBlock(CValidationState& state, const CChainParams& chai
     }
 }
 
-void EnforceBlock(CValidationState& state, const CChainParams& chainparams, const CBlockIndex *pindex) {
-    return ::ChainstateActive().EnforceBlock(state, chainparams, pindex);
-}
-
 bool CChainState::MarkConflictingBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex)
 {
     AssertLockHeld(cs_main);
+    assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
 
     // We first disconnect backwards and then mark the blocks as conflicting.
 
@@ -3523,7 +3525,7 @@ bool CChainState::MarkConflictingBlock(CValidationState& state, const CChainPara
         if (!DisconnectTip(state, chainparams, &disconnectpool)) {
             // It's probably hopeless to try to make the mempool consistent
             // here if DisconnectTip failed, but we can try.
-            UpdateMempoolForReorg(::ChainstateActive(), m_mempool, disconnectpool, false);
+            UpdateMempoolForReorg(*this, m_mempool, disconnectpool, false);
             return false;
         }
         if (pindexOldTip == pindexBestHeader) {
@@ -3545,7 +3547,7 @@ bool CChainState::MarkConflictingBlock(CValidationState& state, const CChainPara
 
     // DisconnectTip will add transactions to disconnectpool; try to add these
     // back to the mempool.
-    UpdateMempoolForReorg(::ChainstateActive(), m_mempool, disconnectpool, true);
+    UpdateMempoolForReorg(*this, m_mempool, disconnectpool, true);
     } // m_mempool.cs
 
     // The resulting new best tip may not be in setBlockIndexCandidates anymore, so
@@ -3630,10 +3632,6 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
     }
 }
 
-void ResetBlockFailureFlags(CBlockIndex *pindex) {
-    return ::ChainstateActive().ResetBlockFailureFlags(pindex);
-}
-
 CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block, enum BlockStatus nStatus)
 {
     assert(!(nStatus & BLOCK_FAILED_MASK)); // no failed blocks allowed
@@ -3727,7 +3725,7 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi
     }
 }
 
-static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false)
+static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int nHeight, CChain& active_chain, uint64_t nTime, bool fKnown = false)
 {
     LOCK(cs_LastBlockFile);
 
@@ -3742,7 +3740,8 @@ static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int n
             // when the undo file is keeping up with the block file, we want to flush it explicitly
             // when it is lagging behind (more blocks arrive than are being connected), we let the
             // undo block write case handle it
-            finalize_undo = (vinfoBlockFile[nFile].nHeightLast == (unsigned int)::ChainActive().Tip()->nHeight);
+            assert(std::addressof(::ChainActive()) == std::addressof(active_chain));
+            finalize_undo = (vinfoBlockFile[nFile].nHeightLast == (unsigned int)active_chain.Tip()->nHeight);
             nFile++;
             if (vinfoBlockFile.size() <= nFile) {
                 vinfoBlockFile.resize(nFile + 1);
@@ -4161,12 +4160,12 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>&
 }
 
 /** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */
-static FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, const CChainParams& chainparams, const FlatFilePos* dbp) {
+static FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const CChainParams& chainparams, const FlatFilePos* dbp) {
     unsigned int nBlockSize = ::GetSerializeSize(block, CLIENT_VERSION);
     FlatFilePos blockPos;
     if (dbp != nullptr)
         blockPos = *dbp;
-    if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, block.GetBlockTime(), dbp != nullptr)) {
+    if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, active_chain, block.GetBlockTime(), dbp != nullptr)) {
         error("%s: FindBlockPos failed", __func__);
         return FlatFilePos();
     }
@@ -4247,8 +4246,9 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CVali
 
     // Write block to history file
     if (fNewBlock) *fNewBlock = true;
+    assert(std::addressof(::ChainActive()) == std::addressof(m_chain));
     try {
-        FlatFilePos blockPos = SaveBlockToDisk(block, pindex->nHeight, chainparams, dbp);
+        FlatFilePos blockPos = SaveBlockToDisk(block, pindex->nHeight, m_chain, chainparams, dbp);
         if (blockPos.IsNull()) {
             state.Error(strprintf("%s: Failed to find position to write new block to disk", __func__));
             return false;
@@ -4437,11 +4437,12 @@ void BlockManager::FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nM
 }
 
 /* This function is called from the RPC code for pruneblockchain */
-void PruneBlockFilesManual(int nManualPruneHeight)
+void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeight)
 {
     CValidationState state;
     const CChainParams& chainparams = Params();
-    if (!::ChainstateActive().FlushStateToDisk(
+    assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+    if (!active_chainstate.FlushStateToDisk(
             chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) {
         LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state));
     }
@@ -4622,11 +4623,12 @@ void BlockManager::Unload() {
     m_prev_block_index.clear();
 }
 
-bool static LoadBlockIndexDB(ChainstateManager& chainman, const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+bool CChainState::LoadBlockIndexDB(const CChainParams& chainparams)
 {
-    if (!chainman.m_blockman.LoadBlockIndex(
+    assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
+    if (!m_blockman.LoadBlockIndex(
             chainparams.GetConsensus(), *pblocktree,
-            ::ChainstateActive().setBlockIndexCandidates)) {
+            setBlockIndexCandidates)) {
         return false;
     }
 
@@ -4650,7 +4652,7 @@ bool static LoadBlockIndexDB(ChainstateManager& chainman, const CChainParams& ch
     // Check presence of blk files
     LogPrintf("Checking all blk files are present...\n");
     std::set<int> setBlkDataFiles;
-    for (const std::pair<const uint256, CBlockIndex*>& item : chainman.BlockIndex()) {
+    for (const std::pair<const uint256, CBlockIndex*>& item : m_blockman.m_block_index) {
         CBlockIndex* pindex = item.second;
         if (pindex->nStatus & BLOCK_HAVE_DATA) {
             setBlkDataFiles.insert(pindex->nFile);
@@ -4736,18 +4738,20 @@ CVerifyDB::~CVerifyDB()
     uiInterface.ShowProgress("", 100, false);
 }
 
-bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, CEvoDB& evoDb, int nCheckLevel, int nCheckDepth)
+bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CChainState& active_chainstate, CCoinsView *coinsview, CEvoDB& evoDb, int nCheckLevel, int nCheckDepth)
 {
-    LOCK(cs_main);
-    if (::ChainActive().Tip() == nullptr || ::ChainActive().Tip()->pprev == nullptr)
+    AssertLockHeld(cs_main);
+
+    assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+    if (active_chainstate.m_chain.Tip() == nullptr || active_chainstate.m_chain.Tip()->pprev == nullptr)
         return true;
 
     // begin tx and let it rollback
     auto dbTx = evoDb.BeginTransaction();
 
     // Verify blocks in the best chain
-    if (nCheckDepth <= 0 || nCheckDepth > ::ChainActive().Height())
-        nCheckDepth = ::ChainActive().Height();
+    if (nCheckDepth <= 0 || nCheckDepth > active_chainstate.m_chain.Height())
+        nCheckDepth = active_chainstate.m_chain.Height();
     nCheckLevel = std::max(0, std::min(4, nCheckLevel));
     LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel);
     CCoinsViewCache coins(coinsview);
@@ -4757,15 +4761,15 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
     CValidationState state;
     int reportDone = 0;
     LogPrintf("[0%%]..."); /* Continued */
-    for (pindex = ::ChainActive().Tip(); pindex && pindex->pprev; pindex = pindex->pprev) {
-        const int percentageDone = std::max(1, std::min(99, (int)(((double)(::ChainActive().Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))));
+    for (pindex = active_chainstate.m_chain.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) {
+        const int percentageDone = std::max(1, std::min(99, (int)(((double)(active_chainstate.m_chain.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))));
         if (reportDone < percentageDone/10) {
             // report every 10% step
             LogPrintf("[%d%%]...", percentageDone); /* Continued */
             reportDone = percentageDone/10;
         }
         uiInterface.ShowProgress(_("Verifying blocks...").translated, percentageDone, false);
-        if (pindex->nHeight <= ::ChainActive().Height()-nCheckDepth)
+        if (pindex->nHeight <= active_chainstate.m_chain.Height()-nCheckDepth)
             break;
         if (fPruneMode && !(pindex->nStatus & BLOCK_HAVE_DATA)) {
             // If pruning, only go back as far as we have data.
@@ -4790,9 +4794,9 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
             }
         }
         // check level 3: check for inconsistencies during memory-only disconnect of tip blocks
-        if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + ::ChainstateActive().CoinsTip().DynamicMemoryUsage()) <= ::ChainstateActive().m_coinstip_cache_size_bytes) {
+        if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + active_chainstate.CoinsTip().DynamicMemoryUsage()) <= active_chainstate.m_coinstip_cache_size_bytes) {
             assert(coins.GetBestBlock() == pindex->GetBlockHash());
-            DisconnectResult res = ::ChainstateActive().DisconnectBlock(block, pindex, coins);
+            DisconnectResult res = active_chainstate.DisconnectBlock(block, pindex, coins);
             if (res == DISCONNECT_FAILED) {
                 return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
             }
@@ -4806,26 +4810,26 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
         if (ShutdownRequested()) return true;
     }
     if (pindexFailure)
-        return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", ::ChainActive().Height() - pindexFailure->nHeight + 1, nGoodTransactions);
+        return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", active_chainstate.m_chain.Height() - pindexFailure->nHeight + 1, nGoodTransactions);
 
     // store block count as we move pindex at check level >= 4
-    int block_count = ::ChainActive().Height() - pindex->nHeight;
+    int block_count = active_chainstate.m_chain.Height() - pindex->nHeight;
 
     // check level 4: try reconnecting blocks
     if (nCheckLevel >= 4) {
-        while (pindex != ::ChainActive().Tip()) {
-            const int percentageDone = std::max(1, std::min(99, 100 - (int)(((double)(::ChainActive().Height() - pindex->nHeight)) / (double)nCheckDepth * 50)));
+        while (pindex != active_chainstate.m_chain.Tip()) {
+            const int percentageDone = std::max(1, std::min(99, 100 - (int)(((double)(active_chainstate.m_chain.Height() - pindex->nHeight)) / (double)nCheckDepth * 50)));
             if (reportDone < percentageDone/10) {
                 // report every 10% step
                 LogPrintf("[%d%%]...", percentageDone); /* Continued */
                 reportDone = percentageDone/10;
             }
             uiInterface.ShowProgress(_("Verifying blocks...").translated, percentageDone, false);
-            pindex = ::ChainActive().Next(pindex);
+            pindex = active_chainstate.m_chain.Next(pindex);
             CBlock block;
             if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()))
                 return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
-            if (!::ChainstateActive().ConnectBlock(block, state, pindex, coins, chainparams))
+            if (!active_chainstate.ConnectBlock(block, state, pindex, coins, chainparams))
                 return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state));
             if (ShutdownRequested()) return true;
         }
@@ -4975,7 +4979,7 @@ bool ChainstateManager::LoadBlockIndex(const CChainParams& chainparams)
     // Load block index from databases
     bool needs_init = fReindex;
     if (!fReindex) {
-        bool ret = LoadBlockIndexDB(*this, chainparams);
+        bool ret = ActiveChainstate().LoadBlockIndexDB(chainparams);
         if (!ret) return false;
         needs_init = m_blockman.m_block_index.empty();
     }
@@ -5006,7 +5010,8 @@ bool ChainstateManager::LoadBlockIndex(const CChainParams& chainparams)
 
 bool CChainState::AddGenesisBlock(const CChainParams& chainparams, const CBlock& block, CValidationState& state)
 {
-    FlatFilePos blockPos = SaveBlockToDisk(block, 0, chainparams, nullptr);
+    assert(std::addressof(::ChainActive()) == std::addressof(m_chain));
+    FlatFilePos blockPos = SaveBlockToDisk(block, 0, m_chain, chainparams, nullptr);
     if (blockPos.IsNull())
         return error("%s: writing genesis block to disk failed (%s)", __func__, FormatStateMessage(state));
     CBlockIndex* pindex = m_blockman.AddToBlockIndex(block);
@@ -5047,11 +5052,6 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
     return true;
 }
 
-bool LoadGenesisBlock(const CChainParams& chainparams)
-{
-    return ::ChainstateActive().LoadGenesisBlock(chainparams);
-}
-
 void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos* dbp)
 {
     // Map of disk positions for blocks with unknown parent (only used for reindex)
@@ -5422,24 +5422,6 @@ CBlockFileInfo* GetBlockFileInfo(size_t n)
     return &vinfoBlockFile.at(n);
 }
 
-ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos)
-{
-    AssertLockHeld(cs_main);
-    return VersionBitsState(::ChainActive().Tip(), params, pos, versionbitscache);
-}
-
-BIP9Stats VersionBitsTipStatistics(const Consensus::Params& params, Consensus::DeploymentPos pos)
-{
-    LOCK(cs_main);
-    return VersionBitsStatistics(::ChainActive().Tip(), params, pos, versionbitscache);
-}
-
-int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos)
-{
-    LOCK(cs_main);
-    return VersionBitsStateSinceHeight(::ChainActive().Tip(), params, pos, versionbitscache);
-}
-
 static const uint64_t MEMPOOL_DUMP_VERSION = 1;
 
 bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate)
diff --git a/src/validation.h b/src/validation.h
index 6e79d4a829e9..d3e94f155377 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -214,7 +214,7 @@ uint64_t CalculateCurrentUsage();
 void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
 
 /** Prune block files up to a given height */
-void PruneBlockFilesManual(int nManualPruneHeight);
+void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeight);
 
 /** (try to) add transaction to memory pool */
 bool AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx,
@@ -225,15 +225,6 @@ bool GetUTXOCoin(const COutPoint& outpoint, Coin& coin);
 int GetUTXOHeight(const COutPoint& outpoint);
 int GetUTXOConfirmations(const COutPoint& outpoint);
 
-/** Get the BIP9 state for a given deployment at the current tip. */
-ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos);
-
-/** Get the numerical statistics for the BIP9 state for a given deployment at the current tip. */
-BIP9Stats VersionBitsTipStatistics(const Consensus::Params& params, Consensus::DeploymentPos pos);
-
-/** Get the block height at which the BIP9 deployment switched into the state for the block building on the current tip. */
-int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos);
-
 /** Apply the effects of this transaction on the UTXO set represented by view */
 void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight);
 
@@ -344,7 +335,7 @@ class CVerifyDB {
 public:
     CVerifyDB();
     ~CVerifyDB();
-    bool VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, CEvoDB& evoDb, int nCheckLevel, int nCheckDepth);
+    bool VerifyDB(const CChainParams& chainparams, CChainState& active_chainstate, CCoinsView *coinsview, CEvoDB& evoDb, int nCheckLevel, int nCheckDepth) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
 };
 
 enum DisconnectResult {
@@ -727,9 +718,16 @@ class CChainState
     bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs);
 
     // Manual block validity manipulation:
+    /** Mark a block as precious and reorganize.
+     *
+     * May not be called in a validationinterface callback.
+     */
     bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
+    /** Mark a block as invalid. */
     bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
+    /** Enforce a block marking all the other chains as conflicting. */
     void EnforceBlock(CValidationState& state, const CChainParams& chainparams, const CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
+    /** Remove invalidity status from a block and its descendants. */
     void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
 
     /** Replay blocks that aren't fully applied to the database. */
@@ -787,24 +785,14 @@ class CChainState
     //! Mark a block as not having block data
     void EraseBlockData(CBlockIndex* index) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
 
-    friend ChainstateManager;
-};
+    void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+    void InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+    void ConflictingChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
 
-/** Mark a block as precious and reorganize.
- *
- * May not be called in a
- * validationinterface callback.
- */
-bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex) LOCKS_EXCLUDED(cs_main);
-
-/** Mark a block as invalid. */
-bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
+    bool LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
 
-/** Enforce a block marking all the other chains as conflicting. */
-void EnforceBlock(CValidationState& state, const CChainParams& chainparams, const CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
-
-/** Remove invalidity status from a block and its descendants. */
-void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+    friend ChainstateManager;
+};
 
 /**
  * Provides an interface for creating and interacting with one or two
diff --git a/src/versionbits.h b/src/versionbits.h
index cba2bae77703..cd34d2ecb5b4 100644
--- a/src/versionbits.h
+++ b/src/versionbits.h
@@ -79,8 +79,11 @@ struct VersionBitsCache
     void Clear();
 };
 
+/** Get the BIP9 state for a given deployment at the current tip. */
 ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache);
+/** Get the numerical statistics for the BIP9 state for a given deployment at the current tip. */
 BIP9Stats VersionBitsStatistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache);
+/** Get the block height at which the BIP9 deployment switched into the state for the block building on the current tip. */
 int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache);
 uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos);
 

From 24a5552918e434178e4cf608b73a9d224056b49a Mon Sep 17 00:00:00 2001
From: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com>
Date: Mon, 8 Mar 2021 15:48:42 -0500
Subject: [PATCH 35/35] partial bitcoin#21270: Prune g_chainman usage in
 validation-adjacent modules

contains:
- a04aac493fd564894166d58ed4cdfd9ad4f561cb
- d0de61b764fc7e9c670b69d8210705da296dd245
- 46b7f29340acb399fbd2378508a204d8d8ee8fca
- 2afcf24408b4453e4418ebfb326b141f6ea8647c
---
 src/miner.cpp                                 |  9 +++--
 src/miner.h                                   |  2 +-
 src/rpc/mining.cpp                            |  6 +--
 src/test/block_reward_reallocation_tests.cpp  | 14 +++----
 src/test/blockfilter_index_tests.cpp          |  2 +-
 .../dynamic_activation_thresholds_tests.cpp   |  2 +-
 src/test/miner_tests.cpp                      | 38 +++++++++----------
 src/test/util.cpp                             |  2 +-
 src/test/util/setup_common.cpp                |  2 +-
 src/test/validation_block_tests.cpp           |  2 +-
 src/validation.h                              |  3 +-
 11 files changed, 41 insertions(+), 41 deletions(-)

diff --git a/src/miner.cpp b/src/miner.cpp
index 92ed3c6a2b4a..3e4b4171ec52 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -108,7 +108,7 @@ void BlockAssembler::resetBlock()
     nFees = 0;
 }
 
-std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn)
+std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(CChainState& chainstate, const CScript& scriptPubKeyIn)
 {
     int64_t nTimeStart = GetTimeMicros();
 
@@ -126,8 +126,8 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
     pblocktemplate->vTxSigOps.push_back(-1); // updated at end
 
     LOCK2(cs_main, m_mempool.cs);
-
-    CBlockIndex* pindexPrev = ::ChainActive().Tip();
+    assert(std::addressof(*::ChainActive().Tip()) == std::addressof(*chainstate.m_chain.Tip()));
+    CBlockIndex* pindexPrev = chainstate.m_chain.Tip();
     assert(pindexPrev != nullptr);
     nHeight = pindexPrev->nHeight + 1;
 
@@ -234,7 +234,8 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
     pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(*pblock->vtx[0]);
 
     CValidationState state;
-    if (!TestBlockValidity(state, m_clhandler, m_evoDb, chainparams, ::ChainstateActive(), *pblock, pindexPrev, false, false)) {
+    assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate));
+    if (!TestBlockValidity(state, m_clhandler, m_evoDb, chainparams, chainstate, *pblock, pindexPrev, false, false)) {
         throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state)));
     }
     int64_t nTime2 = GetTimeMicros();
diff --git a/src/miner.h b/src/miner.h
index e44dbcdf0db7..097e1bb67b39 100644
--- a/src/miner.h
+++ b/src/miner.h
@@ -177,7 +177,7 @@ class BlockAssembler
                             llmq::CInstantSendManager& isman, CEvoDB& evoDb, const CTxMemPool& mempool, const CChainParams& params, const Options& options);
 
     /** Construct a new block template with coinbase to scriptPubKeyIn */
-    std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn);
+    std::unique_ptr<CBlockTemplate> CreateNewBlock(CChainState& chainstate, const CScript& scriptPubKeyIn);
 
     inline static std::optional<int64_t> m_last_block_num_txs{};
     inline static std::optional<int64_t> m_last_block_size{};
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index cd862c836679..c65a4512c265 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -161,7 +161,7 @@ static UniValue generateBlocks(ChainstateManager& chainman, const CTxMemPool& me
     UniValue blockHashes(UniValue::VARR);
     while (nHeight < nHeightEnd && !ShutdownRequested())
     {
-        std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(*sporkManager, *governance, quorum_block_processor, clhandler, isman, evodb, mempool, Params()).CreateNewBlock(coinbase_script));
+        std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(*sporkManager, *governance, quorum_block_processor, clhandler, isman, evodb, mempool, Params()).CreateNewBlock(::ChainstateActive(), coinbase_script));
         if (!pblocktemplate.get())
             throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
         CBlock *pblock = &pblocktemplate->block;
@@ -367,7 +367,7 @@ static UniValue generateblock(const JSONRPCRequest& request)
         LOCK(cs_main);
 
         CTxMemPool empty_mempool;
-        std::unique_ptr<CBlockTemplate> blocktemplate(BlockAssembler(*sporkManager, *governance, *llmq_ctx.quorum_block_processor, *llmq_ctx.clhandler, *llmq_ctx.isman, *node_context.evodb, empty_mempool, chainparams).CreateNewBlock(coinbase_script));
+        std::unique_ptr<CBlockTemplate> blocktemplate(BlockAssembler(*sporkManager, *governance, *llmq_ctx.quorum_block_processor, *llmq_ctx.clhandler, *llmq_ctx.isman, *node_context.evodb, empty_mempool, chainparams).CreateNewBlock(::ChainstateActive(), coinbase_script));
         if (!blocktemplate) {
             throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
         }
@@ -779,7 +779,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
         // Create new block
         CScript scriptDummy = CScript() << OP_TRUE;
         LLMQContext& llmq_ctx = EnsureLLMQContext(request.context);
-        pblocktemplate = BlockAssembler(*sporkManager, *governance, *llmq_ctx.quorum_block_processor, *llmq_ctx.clhandler, *llmq_ctx.isman, *node_context.evodb, mempool, Params()).CreateNewBlock(scriptDummy);
+        pblocktemplate = BlockAssembler(*sporkManager, *governance, *llmq_ctx.quorum_block_processor, *llmq_ctx.clhandler, *llmq_ctx.isman, *node_context.evodb, mempool, Params()).CreateNewBlock(::ChainstateActive(), scriptDummy);
         if (!pblocktemplate)
             throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
 
diff --git a/src/test/block_reward_reallocation_tests.cpp b/src/test/block_reward_reallocation_tests.cpp
index 81a093136fc1..9ea366407059 100644
--- a/src/test/block_reward_reallocation_tests.cpp
+++ b/src/test/block_reward_reallocation_tests.cpp
@@ -219,7 +219,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
         BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::STARTED);
         BOOST_CHECK_EQUAL(VersionBitsStatistics(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache).threshold, threshold(0));
         // Next block should be signaling by default
-        const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler,  *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey);
+        const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler,  *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), coinbasePubKey);
         const uint32_t bitmask = ((uint32_t)1) << consensus_params.vDeployments[deployment_id].bit;
         BOOST_CHECK_EQUAL(::ChainActive().Tip()->nVersion & bitmask, 0);
         BOOST_CHECK_EQUAL(pblocktemplate->block.nVersion & bitmask, bitmask);
@@ -286,7 +286,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
         deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip());
         BOOST_ASSERT(deterministicMNManager->GetListAtChainTip().HasMN(tx.GetHash()));
         auto masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidy(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
-        const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler,  *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey);
+        const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler,  *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), coinbasePubKey);
         BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment);
     }
 
@@ -297,7 +297,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
     {
         LOCK(cs_main);
         auto masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidy(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
-        const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler,  *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey);
+        const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler,  *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), coinbasePubKey);
         BOOST_CHECK_EQUAL(pblocktemplate->block.vtx[0]->GetValueOut(), 13748571607);
         BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment);
         BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, 6874285801); // 0.4999999998
@@ -312,7 +312,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
             }
             LOCK(cs_main);
             auto masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidy(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
-            const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler,  *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey);
+            const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler,  *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), coinbasePubKey);
             BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment);
         }
     }
@@ -321,7 +321,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
         // Reward split should reach ~60/40 after reallocation is done
         LOCK(cs_main);
         auto masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidy(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
-        const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler,  *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey);
+        const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler,  *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), coinbasePubKey);
         BOOST_CHECK_EQUAL(pblocktemplate->block.vtx[0]->GetValueOut(), 10221599170);
         BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment);
         BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, 6132959502); // 0.6
@@ -335,7 +335,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
         }
         LOCK(cs_main);
         auto masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidy(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
-        const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler,  *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey);
+        const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler,  *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), coinbasePubKey);
         BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment);
     }
 
@@ -343,7 +343,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
         // Reward split should reach ~60/40 after reallocation is done
         LOCK(cs_main);
         auto masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidy(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500);
-        const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler,  *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey);
+        const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler,  *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), coinbasePubKey);
         BOOST_CHECK_EQUAL(pblocktemplate->block.vtx[0]->GetValueOut(), 9491484944);
         BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment);
         BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, 5694890966); // 0.6
diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp
index 3de6fbe2a2fd..63fb2e39a2ff 100644
--- a/src/test/blockfilter_index_tests.cpp
+++ b/src/test/blockfilter_index_tests.cpp
@@ -69,7 +69,7 @@ CBlock BuildChainTestingSetup::CreateBlock(const CBlockIndex* prev,
     const CScript& scriptPubKey)
 {
     const CChainParams& chainparams = Params();
-    std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, chainparams).CreateNewBlock(scriptPubKey);
+    std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey);
     CBlock& block = pblocktemplate->block;
     block.hashPrevBlock = prev->GetBlockHash();
     block.nTime = prev->nTime + 1;
diff --git a/src/test/dynamic_activation_thresholds_tests.cpp b/src/test/dynamic_activation_thresholds_tests.cpp
index bf0ae6793111..fad2518b261f 100644
--- a/src/test/dynamic_activation_thresholds_tests.cpp
+++ b/src/test/dynamic_activation_thresholds_tests.cpp
@@ -79,7 +79,7 @@ struct TestChainDATSetup : public TestChainSetup
             BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::STARTED);
             BOOST_CHECK_EQUAL(VersionBitsStatistics(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache).threshold, threshold(0));
             // Next block should be signaling by default
-            const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey);
+            const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), coinbasePubKey);
             const uint32_t bitmask = ((uint32_t)1) << consensus_params.vDeployments[deployment_id].bit;
             BOOST_CHECK_EQUAL(::ChainActive().Tip()->nVersion & bitmask, 0);
             BOOST_CHECK_EQUAL(pblocktemplate->block.nVersion & bitmask, bitmask);
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index c14f3f3d19bd..91f3a1061371 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -135,7 +135,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
     uint256 hashHighFeeTx = tx.GetHash();
     m_node.mempool->addUnchecked(entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
 
-    std::unique_ptr<CBlockTemplate> pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+    std::unique_ptr<CBlockTemplate> pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey);
     BOOST_CHECK(pblocktemplate->block.vtx[1]->GetHash() == hashParentTx);
     BOOST_CHECK(pblocktemplate->block.vtx[2]->GetHash() == hashHighFeeTx);
     BOOST_CHECK(pblocktemplate->block.vtx[3]->GetHash() == hashMediumFeeTx);
@@ -155,7 +155,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
     tx.vout[0].nValue = 5000000000LL - 1000 - 50000 - feeToUse;
     uint256 hashLowFeeTx = tx.GetHash();
     m_node.mempool->addUnchecked(entry.Fee(feeToUse).FromTx(tx));
-    pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+    pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey);
     // Verify that the free tx and the low fee tx didn't get selected
     for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
         BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashFreeTx);
@@ -169,7 +169,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
     tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee
     hashLowFeeTx = tx.GetHash();
     m_node.mempool->addUnchecked(entry.Fee(feeToUse+2).FromTx(tx));
-    pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+    pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey);
     BOOST_CHECK(pblocktemplate->block.vtx[4]->GetHash() == hashFreeTx);
     BOOST_CHECK(pblocktemplate->block.vtx[5]->GetHash() == hashLowFeeTx);
 
@@ -190,7 +190,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
     tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse;
     uint256 hashLowFeeTx2 = tx.GetHash();
     m_node.mempool->addUnchecked(entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx));
-    pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+    pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey);
 
     // Verify that this tx isn't selected.
     for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
@@ -203,7 +203,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
     tx.vin[0].prevout.n = 1;
     tx.vout[0].nValue = 100000000 - 10000; // 10k satoshi fee
     m_node.mempool->addUnchecked(entry.Fee(10000).FromTx(tx));
-    pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
+    pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey);
     BOOST_CHECK(pblocktemplate->block.vtx[8]->GetHash() == hashLowFeeTx2);
 }
 
@@ -224,7 +224,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
     fCheckpointsEnabled = false;
 
     // Simple block creation, nothing special yet:
-    BOOST_CHECK(pemptyblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+    BOOST_CHECK(pemptyblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
 
     // We can't make transactions until we have inputs
     // Therefore, load 100 blocks :)
@@ -272,7 +272,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
     LOCK(m_node.mempool->cs);
 
     // Just to make sure we can still make simple blocks
-    BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+    BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
 
     const CAmount BLOCKSUBSIDY = 500*COIN;
     const CAmount LOWFEE = CENT;
@@ -297,7 +297,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
         tx.vin[0].prevout.hash = hash;
     }
 
-    BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-blk-sigops"));
+    BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("bad-blk-sigops"));
     m_node.mempool->clear();
 
     tx.vin[0].prevout.hash = txFirst[0]->GetHash();
@@ -311,7 +311,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
         m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOps(20).FromTx(tx));
         tx.vin[0].prevout.hash = hash;
     }
-    BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+    BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
     m_node.mempool->clear();
 
     // block size > limit
@@ -331,13 +331,13 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
         m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
         tx.vin[0].prevout.hash = hash;
     }
-    BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+    BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
     m_node.mempool->clear();
 
     // orphan in *m_node.mempool, template creation fails
     hash = tx.GetHash();
     m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx));
-    BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
+    BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
     m_node.mempool->clear();
 
     // child with higher feerate than parent
@@ -354,7 +354,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
     tx.vout[0].nValue = tx.vout[0].nValue+BLOCKSUBSIDY-HIGHERFEE; //First txn output + fresh coinbase - new txn fee
     hash = tx.GetHash();
     m_node.mempool->addUnchecked(entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
-    BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+    BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
     m_node.mempool->clear();
 
     // coinbase in *m_node.mempool, template creation fails
@@ -366,7 +366,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
     // give it a fee so it'll get mined
     m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
     // Should throw bad-cb-multiple
-    BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-cb-multiple"));
+    BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("bad-cb-multiple"));
     m_node.mempool->clear();
 
     // invalid (pre-p2sh) txn in *m_node.mempool, template creation fails
@@ -384,7 +384,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
     hash = tx.GetHash();
     m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
     // Should throw block-validation-failed
-    BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("block-validation-failed"));
+    BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("block-validation-failed"));
     m_node.mempool->clear();
 
     // double spend txn pair in *m_node.mempool, template creation fails
@@ -397,7 +397,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
     tx.vout[0].scriptPubKey = CScript() << OP_2;
     hash = tx.GetHash();
     m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
-    BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
+    BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
     m_node.mempool->clear();
 
     // subsidy changing
@@ -413,7 +413,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
     //     next->BuildSkip();
     //     ::ChainActive().SetTip(next);
     // }
-    //BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey));
+    //BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
     // // Extend to a 210000-long block chain.
     // while (::ChainActive().Tip()->nHeight < 210000) {
     //     CBlockIndex* prev = ::ChainActive().Tip();
@@ -425,7 +425,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
     //     next->BuildSkip();
     //     ::ChainActive().SetTip(next);
     // }
-    //BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey));
+    //BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
     // // Delete the dummy blocks again.
     // while (::ChainActive().Tip()->nHeight > nHeight) {
     //     CBlockIndex* del = ::ChainActive().Tip();
@@ -511,7 +511,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
     tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | 1;
     BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
 
-    BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+    BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
 
     // None of the of the absolute height/time locked tx should have made
     // it into the template because we still check IsFinalTx in CreateNewBlock,
@@ -532,7 +532,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
 
     SetMockTime(::ChainActive().Tip()->GetMedianTimePast() + 1);
 
-    BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
+    BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey));
     BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5U);
     } // unlock cs_main while calling InvalidateBlock
 
diff --git a/src/test/util.cpp b/src/test/util.cpp
index 4933f52426d1..7c265630248b 100644
--- a/src/test/util.cpp
+++ b/src/test/util.cpp
@@ -81,7 +81,7 @@ std::shared_ptr<CBlock> PrepareBlock(const NodeContext& node, const CScript& coi
     assert(node.mempool);
     auto block = std::make_shared<CBlock>(
         BlockAssembler{*sporkManager, *governance, *node.llmq_ctx->quorum_block_processor, *node.llmq_ctx->clhandler, *node.llmq_ctx->isman, *node.evodb, *node.mempool, Params()}
-            .CreateNewBlock(coinbase_scriptPubKey)
+            .CreateNewBlock(::ChainstateActive(), coinbase_scriptPubKey)
             ->block);
 
     block->nTime = ::ChainActive().Tip()->GetMedianTimePast() + 1;
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 9ff2a2badf75..36f7156f1250 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -312,7 +312,7 @@ CBlock TestChainSetup::CreateBlock(const std::vector<CMutableTransaction>& txns,
             *sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor,
             *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb,
             empty_pool, chainparams
-        ).CreateNewBlock(scriptPubKey)->block;
+        ).CreateNewBlock(::ChainstateActive(), scriptPubKey)->block;
 
     std::vector<CTransactionRef> llmqCommitments;
     for (const auto& tx : block.vtx) {
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index b6cd6db433a5..becce21a2bba 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -75,7 +75,7 @@ std::shared_ptr<CBlock> MinerTestingSetup::Block(const uint256& prev_hash)
     CScript pubKey;
     pubKey << i++ << OP_TRUE;
 
-    auto ptemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler,  *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(pubKey);
+    auto ptemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler,  *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), pubKey);
     auto pblock = std::make_shared<CBlock>(ptemplate->block);
     pblock->hashPrevBlock = prev_hash;
     pblock->nTime = ++time;
diff --git a/src/validation.h b/src/validation.h
index d3e94f155377..ccba0b5bec86 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -176,8 +176,6 @@ extern uint64_t nPruneTarget;
 FILE* OpenBlockFile(const FlatFilePos &pos, bool fReadOnly = false);
 /** Translation to a filesystem path */
 fs::path GetBlockPosFilename(const FlatFilePos &pos);
-/** Ensures we have a genesis block in the block tree, possibly writing one to disk. */
-bool LoadGenesisBlock(const CChainParams& chainparams);
 /** Unload database information */
 void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman);
 /** Run instances of script checking worker threads */
@@ -732,6 +730,7 @@ class CChainState
 
     /** Replay blocks that aren't fully applied to the database. */
     bool ReplayBlocks(const CChainParams& params);
+    /** Ensures we have a genesis block in the block tree, possibly writing one to disk. */
     bool LoadGenesisBlock(const CChainParams& chainparams);
     bool AddGenesisBlock(const CChainParams& chainparams, const CBlock& block, CValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
 
