From 934908c79e998685fe8aef7922977f4edc684b44 Mon Sep 17 00:00:00 2001
From: Anthony Fieroni <bvbfan@abv.bg>
Date: Tue, 9 Jul 2019 18:02:54 +0300
Subject: [PATCH] Fix expiration fork usage

Signed-off-by: Anthony Fieroni <bvbfan@abv.bg>
---
 src/claimscriptop.cpp                 | 87 ++++++++++++++++----------
 src/claimscriptop.h                   | 25 +++-----
 src/claimtrie.cpp                     | 30 ++++-----
 src/claimtrie.h                       | 29 ++++++---
 src/claimtrieforks.cpp                | 48 +++++++++++++-
 src/init.cpp                          |  3 -
 src/miner.cpp                         | 90 +++++++++++----------------
 src/miner.h                           |  2 +-
 src/test/claimtriebranching_tests.cpp | 20 +++---
 src/validation.cpp                    | 53 +++-------------
 10 files changed, 196 insertions(+), 191 deletions(-)

diff --git a/src/claimscriptop.cpp b/src/claimscriptop.cpp
index 3af804fe2..837ea23f3 100644
--- a/src/claimscriptop.cpp
+++ b/src/claimscriptop.cpp
@@ -2,8 +2,9 @@
 // Distributed under the MIT software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
-#include "claimscriptop.h"
-#include "nameclaim.h"
+#include <coins.h>
+#include <claimscriptop.h>
+#include <nameclaim.h>
 
 CClaimScriptAddOp::CClaimScriptAddOp(const COutPoint& point, CAmount nValue, int nHeight)
     : point(point), nValue(nValue), nHeight(nHeight)
@@ -153,59 +154,81 @@ bool ProcessClaim(CClaimScriptOp& claimOp, CClaimTrieCache& trieCache, const CSc
     throw std::runtime_error("Unimplemented OP handler.");
 }
 
-bool SpendClaim(CClaimTrieCache& trieCache, const CScript& scriptPubKey, const COutPoint& point, int nHeight, int& nValidHeight, spentClaimsType& spentClaims)
+void UpdateCache(const CTransaction& tx, CClaimTrieCache& trieCache, const CCoinsViewCache& view, int nHeight, const CUpdateCacheCallbacks& callbacks)
 {
     class CSpendClaimHistory : public CClaimScriptSpendOp
     {
     public:
-        CSpendClaimHistory(spentClaimsType& spentClaims, const COutPoint& point, int nHeight, int& nValidHeight)
-            : CClaimScriptSpendOp(point, nHeight, nValidHeight), spentClaims(spentClaims)
-        {
-        }
+        using CClaimScriptSpendOp::CClaimScriptSpendOp;
 
         bool spendClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId) override
         {
             if (CClaimScriptSpendOp::spendClaim(trieCache, name, claimId)) {
-                spentClaims.emplace_back(name, claimId);
+                callback(name, claimId);
                 return true;
             }
             return false;
         }
-
-    private:
-        spentClaimsType& spentClaims;
+        std::function<void(const std::string& name, const uint160& claimId)> callback;
     };
 
-    CSpendClaimHistory spendClaim(spentClaims, point, nHeight, nValidHeight);
-    return ProcessClaim(spendClaim, trieCache, scriptPubKey);
-}
+    spentClaimsType spentClaims;
+
+    for (std::size_t j = 0; j < tx.vin.size(); j++) {
+        const CTxIn& txin = tx.vin[j];
+        const Coin& coin = view.AccessCoin(txin.prevout);
+
+        CScript scriptPubKey;
+        int scriptHeight = nHeight;
+        if (coin.out.IsNull() && callbacks.findScriptKey) {
+            scriptPubKey = callbacks.findScriptKey(txin.prevout);
+        } else {
+            scriptHeight = coin.nHeight;
+            scriptPubKey = coin.out.scriptPubKey;
+        }
+
+        if (scriptPubKey.empty())
+            continue;
+
+        int nValidAtHeight;
+        CSpendClaimHistory spendClaim(COutPoint(txin.prevout.hash, txin.prevout.n), scriptHeight, nValidAtHeight);
+        spendClaim.callback = [&spentClaims](const std::string& name, const uint160& claimId) {
+            spentClaims.emplace_back(name, claimId);
+        };
+        if (ProcessClaim(spendClaim, trieCache, scriptPubKey) && callbacks.claimUndoHeights)
+            callbacks.claimUndoHeights(j, nValidAtHeight);
+    }
 
-bool AddSpendClaim(CClaimTrieCache& trieCache, const CScript& scriptPubKey, const COutPoint& point, CAmount nValue, int nHeight, spentClaimsType& spentClaims)
-{
     class CAddSpendClaim : public CClaimScriptAddOp
     {
     public:
-        CAddSpendClaim(spentClaimsType& spentClaims, const COutPoint& point, CAmount nValue, int nHeight)
-            : CClaimScriptAddOp(point, nValue, nHeight), spentClaims(spentClaims)
-        {
-        }
+        using CClaimScriptAddOp::CClaimScriptAddOp;
 
         bool updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId) override
         {
-            spentClaimsType::iterator itSpent = spentClaims.begin();
-            for (; itSpent != spentClaims.end(); ++itSpent) {
+            if (callback(name, claimId))
+                return CClaimScriptAddOp::updateClaim(trieCache, name, claimId);
+            return false;
+        }
+        std::function<bool(const std::string& name, const uint160& claimId)> callback;
+    };
+
+    for (std::size_t j = 0; j < tx.vout.size(); j++) {
+        const CTxOut& txout = tx.vout[j];
+
+        if (txout.scriptPubKey.empty())
+            continue;
+
+        CAddSpendClaim addClaim(COutPoint(tx.GetHash(), j), txout.nValue, nHeight);
+        addClaim.callback = [&trieCache, &spentClaims](const std::string& name, const uint160& claimId) -> bool {
+            for (auto itSpent = spentClaims.begin(); itSpent != spentClaims.end(); ++itSpent) {
                 if (itSpent->second == claimId && trieCache.normalizeClaimName(name) == trieCache.normalizeClaimName(itSpent->first)) {
                     spentClaims.erase(itSpent);
-                    return CClaimScriptAddOp::updateClaim(trieCache, name, claimId);
+                    return true;
                 }
             }
             return false;
-        }
-
-    private:
-        spentClaimsType& spentClaims;
-    };
-
-    CAddSpendClaim addClaim(spentClaims, point, nValue, nHeight);
-    return ProcessClaim(addClaim, trieCache, scriptPubKey);
-}
\ No newline at end of file
+        };
+        ProcessClaim(addClaim, trieCache, txout.scriptPubKey);
+    }
+}
diff --git a/src/claimscriptop.h b/src/claimscriptop.h
index 0502093dc..7fc93048a 100644
--- a/src/claimscriptop.h
+++ b/src/claimscriptop.h
@@ -225,26 +225,21 @@ typedef std::pair<std::string, uint160> spentClaimType;
 
 typedef std::vector<spentClaimType> spentClaimsType;
 
+struct CUpdateCacheCallbacks
+{
+    std::function<CScript(const COutPoint& point)> findScriptKey;
+    std::function<void(int, int)> claimUndoHeights;
+};
+
 /**
  * Function to spend claim from tie, keeping the successful list on
+ * @param[in]  tx               transaction inputs/outputs
  * @param[in]  trieCache        trie to operate on
- * @param[in]  scriptPubKey     claim script to be decoded
+ * @param[in]  view             coins cache
  * @param[in]  point            pair of transaction hash and its index
  * @param[in]  nHeight          entry height of the claim
- * @param[out] nValidHeight     valid height of the claim
- * @param[out] spentClaims      inserts successfully spent claim
+ * @param[out] fallback         optional callbacks
  */
-bool SpendClaim(CClaimTrieCache& trieCache, const CScript& scriptPubKey, const COutPoint& point, int nHeight, int& nValidHeight, spentClaimsType& spentClaims);
-
-/**
- * Function to add / update (that present in spent list) claim in trie
- * @param[in]  trieCache        trie to operate on
- * @param[in]  scriptPubKey     claim script to be decoded
- * @param[in]  point            pair of transaction hash and its index
- * @param[in]  nValue        `  value of the claim
- * @param[in]  nHeight          entry height of the claim
- * @param[out] spentClaims      erases successfully added claim
- */
-bool AddSpendClaim(CClaimTrieCache& trieCache, const CScript& scriptPubKey, const COutPoint& point, CAmount nValue, int nHeight, spentClaimsType& spentClaims);
+void UpdateCache(const CTransaction& tx, CClaimTrieCache& trieCache, const CCoinsViewCache& view, int nHeight, const CUpdateCacheCallbacks& callbacks = {});
 
 #endif // CLAIMSCRIPTOP_H
diff --git a/src/claimtrie.cpp b/src/claimtrie.cpp
index db6c53377..dc072c6c1 100644
--- a/src/claimtrie.cpp
+++ b/src/claimtrie.cpp
@@ -92,7 +92,6 @@ void CClaimTrieData::reorderClaims(const supportEntryType& supports)
 CClaimTrie::CClaimTrie(bool fMemory, bool fWipe, int proportionalDelayFactor)
 {
     nProportionalDelayFactor = proportionalDelayFactor;
-    nExpirationTime = Params().GetConsensus().nOriginalClaimExpirationTime;
     db.reset(new CDBWrapper(GetDataDir() / "claimtrie", 100 * 1024 * 1024, fMemory, fWipe, false));
 }
 
@@ -469,9 +468,7 @@ bool CClaimTrieCacheBase::ReadFromDisk(const CBlockIndex* tip)
 {
     LogPrintf("Loading the claim trie from disk...\n");
 
-    nNextHeight = tip ? tip->nHeight + 1 : 0;
-    base->nNextHeight = nNextHeight;
-    base->nExpirationTime = Params().GetConsensus().GetExpirationTime(nNextHeight - 1); // -1 okay
+    base->nNextHeight = nNextHeight = tip ? tip->nHeight + 1 : 0;
 
     clear();
     base->clear();
@@ -532,14 +529,9 @@ CClaimTrieCacheBase::CClaimTrieCacheBase(CClaimTrie* base, bool fRequireTakeover
     nNextHeight = base->nNextHeight;
 }
 
-void CClaimTrieCacheBase::setExpirationTime(int time)
+int CClaimTrieCacheBase::expirationTime() const
 {
-    base->nExpirationTime = time;
-}
-
-int CClaimTrieCacheBase::expirationTime()
-{
-    return base->nExpirationTime;
+    return Params().GetConsensus().nOriginalClaimExpirationTime;
 }
 
 uint256 CClaimTrieCacheBase::recursiveComputeMerkleHash(CClaimTrie::iterator& it)
@@ -726,7 +718,7 @@ bool CClaimTrieCacheBase::undoSpendClaim(const std::string& name, const COutPoin
     CClaimValue claim(outPoint, claimId, nAmount, nHeight, nValidAtHeight);
     if (nValidAtHeight < nNextHeight) {
         CNameOutPointType entry(adjustNameForValidHeight(name, nValidAtHeight), claim.outPoint);
-        addToExpirationQueue(claim.nHeight + base->nExpirationTime, entry);
+        addToExpirationQueue(claim.nHeight + expirationTime(), entry);
         CClaimIndexElement element = {name, claim};
         claimsToAdd.push_back(element);
         return insertClaimIntoTrie(name, claim, false);
@@ -745,7 +737,7 @@ bool CClaimTrieCacheBase::addClaimToQueues(const std::string& name, const CClaim
     itQueueRow->second.push_back(entry);
     itQueueNameRow->second.emplace_back(claim.outPoint, claim.nValidAtHeight);
     CNameOutPointType expireEntry(name, claim.outPoint);
-    addToExpirationQueue(claim.nHeight + base->nExpirationTime, expireEntry);
+    addToExpirationQueue(claim.nHeight + expirationTime(), expireEntry);
     return true;
 }
 
@@ -798,7 +790,7 @@ bool CClaimTrieCacheBase::removeClaim(const std::string& name, const COutPoint&
     std::string adjusted = adjustNameForValidHeight(name, nValidAtHeight);
 
     if (removeClaimFromQueue(adjusted, outPoint, claim) || removeClaimFromTrie(name, outPoint, claim, fCheckTakeover)) {
-        int expirationHeight = claim.nHeight + base->nExpirationTime;
+        int expirationHeight = claim.nHeight + expirationTime();
         removeFromExpirationQueue(adjusted, outPoint, expirationHeight);
         claimsToDelete.insert(claim);
         nValidAtHeight = claim.nValidAtHeight;
@@ -880,7 +872,7 @@ bool CClaimTrieCacheBase::addSupportToQueues(const std::string& name, const CSup
     itQueueRow->second.push_back(entry);
     itQueueNameRow->second.emplace_back(support.outPoint, support.nValidAtHeight);
     CNameOutPointType expireEntry(name, support.outPoint);
-    addSupportToExpirationQueue(support.nHeight + base->nExpirationTime, expireEntry);
+    addSupportToExpirationQueue(support.nHeight + expirationTime(), expireEntry);
     return true;
 }
 
@@ -929,7 +921,7 @@ bool CClaimTrieCacheBase::undoSpendSupport(const std::string& name, const COutPo
     CSupportValue support(outPoint, supportedClaimId, nAmount, nHeight, nValidAtHeight);
     if (nValidAtHeight < nNextHeight) {
         CNameOutPointType entry(adjustNameForValidHeight(name, nValidAtHeight), support.outPoint);
-        addSupportToExpirationQueue(support.nHeight + base->nExpirationTime, entry);
+        addSupportToExpirationQueue(support.nHeight + expirationTime(), entry);
         return insertSupportIntoMap(name, support, false);
     } else {
         return addSupportToQueues(name, support);
@@ -943,7 +935,7 @@ bool CClaimTrieCacheBase::removeSupport(const std::string& name, const COutPoint
     std::string adjusted = adjustNameForValidHeight(name, nValidAtHeight);
 
     if (removeSupportFromQueue(adjusted, outPoint, support) || removeSupportFromMap(name, outPoint, support, fCheckTakeover)) {
-        int expirationHeight = support.nHeight + base->nExpirationTime;
+        int expirationHeight = support.nHeight + expirationTime();
         removeSupportFromExpirationQueue(adjusted, outPoint, expirationHeight);
         nValidAtHeight = support.nValidAtHeight;
         return true;
@@ -1244,7 +1236,7 @@ bool CClaimTrieCacheBase::decrementBlock(insertUndoType& insertUndo, claimQueueR
     if (!expireSupportUndo.empty()) {
         for (auto itSupportExpireUndo = expireSupportUndo.crbegin(); itSupportExpireUndo != expireSupportUndo.crend(); ++itSupportExpireUndo) {
             insertSupportIntoMap(itSupportExpireUndo->first, itSupportExpireUndo->second, false);
-            if (nNextHeight == itSupportExpireUndo->second.nHeight + base->nExpirationTime) {
+            if (nNextHeight == itSupportExpireUndo->second.nHeight + expirationTime()) {
                 auto itSupportExpireRow = getSupportExpirationQueueCacheRow(nNextHeight, true);
                 itSupportExpireRow->second.emplace_back(itSupportExpireUndo->first, itSupportExpireUndo->second.outPoint);
             }
@@ -1270,7 +1262,7 @@ bool CClaimTrieCacheBase::decrementBlock(insertUndoType& insertUndo, claimQueueR
             insertClaimIntoTrie(itExpireUndo->first, itExpireUndo->second, false);
             CClaimIndexElement element = {itExpireUndo->first, itExpireUndo->second};
             claimsToAdd.push_back(element);
-            if (nNextHeight == itExpireUndo->second.nHeight + base->nExpirationTime) {
+            if (nNextHeight == itExpireUndo->second.nHeight + expirationTime()) {
                 auto itExpireRow = getExpirationQueueCacheRow(nNextHeight, true);
                 itExpireRow->second.emplace_back(itExpireUndo->first, itExpireUndo->second.outPoint);
             }
diff --git a/src/claimtrie.h b/src/claimtrie.h
index 5c5af60fd..8805d1db9 100644
--- a/src/claimtrie.h
+++ b/src/claimtrie.h
@@ -302,7 +302,6 @@ struct CClaimsForNameType
 class CClaimTrie : public CPrefixTrie<std::string, CClaimTrieData>
 {
     int nNextHeight = 0;
-    int nExpirationTime = 0;
     int nProportionalDelayFactor = 0;
     std::unique_ptr<CDBWrapper> db;
 
@@ -434,6 +433,8 @@ public:
     virtual bool getProofForName(const std::string& name, CClaimTrieProof& proof);
     virtual bool getInfoForName(const std::string& name, CClaimValue& claim) const;
 
+    virtual int expirationTime() const;
+
     bool finalizeDecrement(std::vector<std::pair<std::string, int>>& takeoverHeightUndo);
 
     virtual CClaimsForNameType getClaimsForName(const std::string& name) const;
@@ -441,9 +442,6 @@ public:
     CAmount getEffectiveAmountForClaim(const std::string& name, const uint160& claimId, std::vector<CSupportValue>* supports = nullptr) const;
     CAmount getEffectiveAmountForClaim(const CClaimsForNameType& claims, const uint160& claimId, std::vector<CSupportValue>* supports = nullptr) const;
 
-    void setExpirationTime(int time);
-    int expirationTime();
-
     CClaimTrie::const_iterator begin() const;
     CClaimTrie::const_iterator end() const;
     CClaimTrie::const_iterator find(const std::string& name) const;
@@ -539,16 +537,27 @@ private:
 class CClaimTrieCacheExpirationFork : public CClaimTrieCacheBase
 {
 public:
-    explicit CClaimTrieCacheExpirationFork(CClaimTrie* base, bool fRequireTakeoverHeights = true)
-        : CClaimTrieCacheBase(base, fRequireTakeoverHeights)
-    {
-    }
+    explicit CClaimTrieCacheExpirationFork(CClaimTrie* base, bool fRequireTakeoverHeights = true);
 
-    bool forkForExpirationChange(bool increment);
+    void setExpirationTime(int time);
+    int expirationTime() const override;
 
-    // TODO: move the expiration fork code from main.cpp to overrides of increment/decrement block
+    void expirationForkActive(int height, bool increment);
+
+    bool incrementBlock(insertUndoType& insertUndo,
+        claimQueueRowType& expireUndo,
+        insertUndoType& insertSupportUndo,
+        supportQueueRowType& expireSupportUndo,
+        std::vector<std::pair<std::string, int>>& takeoverHeightUndo) override;
+
+    bool decrementBlock(insertUndoType& insertUndo,
+        claimQueueRowType& expireUndo,
+        insertUndoType& insertSupportUndo,
+        supportQueueRowType& expireSupportUndo) override;
 
 private:
+    int nExpirationTime;
+    bool forkForExpirationChange(bool increment);
     void removeAndAddSupportToExpirationQueue(expirationQueueRowType& row, int height, bool increment);
     void removeAndAddToExpirationQueue(expirationQueueRowType& row, int height, bool increment);
 };
diff --git a/src/claimtrieforks.cpp b/src/claimtrieforks.cpp
index d8cec6198..b3d4cb58b 100644
--- a/src/claimtrieforks.cpp
+++ b/src/claimtrieforks.cpp
@@ -1,4 +1,6 @@
-#include "claimtrie.h"
+
+#include <chainparams.h>
+#include <claimtrie.h>
 
 #include <boost/algorithm/string.hpp>
 #include <boost/foreach.hpp>
@@ -7,6 +9,46 @@
 #include <boost/locale/localization_backend.hpp>
 #include <boost/scope_exit.hpp>
 
+CClaimTrieCacheExpirationFork::CClaimTrieCacheExpirationFork(CClaimTrie* base, bool fRequireTakeoverHeights)
+    : CClaimTrieCacheBase(base, fRequireTakeoverHeights)
+{
+    setExpirationTime(Params().GetConsensus().GetExpirationTime(nNextHeight));
+}
+
+void CClaimTrieCacheExpirationFork::setExpirationTime(int time)
+{
+    nExpirationTime = time;
+}
+
+int CClaimTrieCacheExpirationFork::expirationTime() const
+{
+    return nExpirationTime;
+}
+
+bool CClaimTrieCacheExpirationFork::incrementBlock(insertUndoType& insertUndo, claimQueueRowType& expireUndo, insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo, std::vector<std::pair<std::string, int>>& takeoverHeightUndo)
+{
+    if (CClaimTrieCacheBase::incrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo, takeoverHeightUndo)) {
+        setExpirationTime(Params().GetConsensus().GetExpirationTime(nNextHeight));
+        return true;
+    }
+    return false;
+}
+
+bool CClaimTrieCacheExpirationFork::decrementBlock(insertUndoType& insertUndo, claimQueueRowType& expireUndo, insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo)
+{
+    if (CClaimTrieCacheBase::decrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo)) {
+        setExpirationTime(Params().GetConsensus().GetExpirationTime(nNextHeight));
+        return true;
+    }
+    return false;
+}
+
+void CClaimTrieCacheExpirationFork::expirationForkActive(int nHeight, bool increment)
+{
+    if (nHeight == Params().GetConsensus().nExtendedClaimExpirationForkHeight)
+        forkForExpirationChange(increment);
+}
+
 void CClaimTrieCacheExpirationFork::removeAndAddToExpirationQueue(expirationQueueRowType& row, int height, bool increment)
 {
     for (auto e = row.begin(); e != row.end(); ++e) {
@@ -150,7 +192,7 @@ bool CClaimTrieCacheNormalizationFork::normalizeAllNamesInTrieIfNecessary(insert
         auto supports = getSupportsForName(it.key());
         for (auto& support : supports) {
             // if it's already going to expire just skip it
-            if (support.nHeight + base->nExpirationTime <= nNextHeight)
+            if (support.nHeight + expirationTime() <= nNextHeight)
                 continue;
 
             CSupportValue removed;
@@ -167,7 +209,7 @@ bool CClaimTrieCacheNormalizationFork::normalizeAllNamesInTrieIfNecessary(insert
             continue;
 
         for (auto& claim : it->claims) {
-            if (claim.nHeight + base->nExpirationTime <= nNextHeight)
+            if (claim.nHeight + expirationTime() <= nNextHeight)
                 continue;
 
             CClaimValue removed;
diff --git a/src/init.cpp b/src/init.cpp
index 768bffdfa..c61b56a92 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1765,9 +1765,6 @@ bool AppInitMain(InitInterfaces& interfaces)
     }
     LogPrintf("nBestHeight = %d\n", chain_active_height);
 
-    const Consensus::Params& consensusParams = Params().GetConsensus();
-    CClaimTrieCache(pclaimTrie).setExpirationTime(consensusParams.GetExpirationTime(chain_active_height));
-
     if (gArgs.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION))
         StartTorControl();
 
diff --git a/src/miner.cpp b/src/miner.cpp
index 0b32cdade..6ef0f6090 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -43,6 +43,35 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam
     return nNewTime - nOldTime;
 }
 
+void blockToCache(const CBlock* pblock, CClaimTrieCache& trieCache, int nHeight)
+{
+    insertUndoType dummyInsertUndo;
+    claimQueueRowType dummyExpireUndo;
+    insertUndoType dummyInsertSupportUndo;
+    supportQueueRowType dummyExpireSupportUndo;
+    std::vector<std::pair<std::string, int> > dummyTakeoverHeightUndo;
+
+    CUpdateCacheCallbacks callbacks = {
+        .findScriptKey = [&pblock](const COutPoint& point) {
+            for (auto& tx : pblock->vtx)
+                if (tx->GetHash() == point.hash && point.n < tx->vout.size())
+                    return tx->vout[point.n].scriptPubKey;
+            return CScript{};
+        },
+        .claimUndoHeights = {}
+    };
+
+    trieCache.expirationForkActive(nHeight, true);
+
+    CCoinsViewCache view(pcoinsTip.get());
+
+    for (auto& tx : pblock->vtx)
+        if (!tx->IsCoinBase())
+            UpdateCache(*tx, trieCache, view, nHeight, callbacks);
+
+    trieCache.incrementBlock(dummyInsertUndo, dummyExpireUndo, dummyInsertSupportUndo, dummyExpireSupportUndo, dummyTakeoverHeightUndo);
+}
+
 BlockAssembler::Options::Options() {
     blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE);
     nBlockMaxWeight = DEFAULT_BLOCK_MAX_WEIGHT;
@@ -110,12 +139,6 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
     CBlockIndex* pindexPrev = ::ChainActive().Tip();
     assert(pindexPrev != nullptr);
     nHeight = pindexPrev->nHeight + 1;
-    if (!pclaimTrie)
-    {
-        LogPrintf("CreateNewBlock(): pclaimTrie is invalid");
-        return NULL;
-    }
-    CClaimTrieCache trieCache(pclaimTrie);
     pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus());
     // -regtest only: allow overriding block.nVersion with
     // -blockversion=N to test forking scenarios
@@ -142,7 +165,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
 
     int nPackagesSelected = 0;
     int nDescendantsUpdated = 0;
-    addPackageTxs(nPackagesSelected, nDescendantsUpdated, trieCache);
+    addPackageTxs(nPackagesSelected, nDescendantsUpdated);
 
     int64_t nTime1 = GetTimeMicros();
 
@@ -170,20 +193,14 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
     pblock->nNonce         = 0;
     pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]);
 
-    insertUndoType dummyInsertUndo;
-    claimQueueRowType dummyExpireUndo;
-    insertUndoType dummyInsertSupportUndo;
-    supportQueueRowType dummyExpireSupportUndo;
-    std::vector<std::pair<std::string, int> > dummyTakeoverHeightUndo;
-
-    trieCache.incrementBlock(dummyInsertUndo, dummyExpireUndo, dummyInsertSupportUndo, dummyExpireSupportUndo, dummyTakeoverHeightUndo);
-
+    CClaimTrieCache trieCache(pclaimTrie);
+    blockToCache(pblock, trieCache, nHeight);
     pblock->hashClaimTrie = trieCache.getMerkleHash();
 
     CValidationState state;
     if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) {
-        // if (trieCache.checkConsistency()) // TODO: bring back after prefixtrie merge
-        //     trieCache.dumpToLog(trieCache.begin());
+        if (!trieCache.empty() && trieCache.checkConsistency())
+            trieCache.dumpToLog(trieCache.begin());
         throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state)));
     }
     int64_t nTime2 = GetTimeMicros();
@@ -303,42 +320,6 @@ void BlockAssembler::SortForBlock(const CTxMemPool::setEntries& package, std::ve
     std::sort(sortedEntries.begin(), sortedEntries.end(), CompareTxIterByAncestorCount());
 }
 
-void iterToTrieCache(CTxMemPool::txiter iter, CClaimTrieCache& trieCache, const CTxMemPool::setEntries& entries, int nHeight)
-{
-    spentClaimsType spentClaims;
-    auto& tx = iter->GetTx();
-
-    CCoinsViewCache view(pcoinsTip.get());
-    for (const CTxIn& txin: tx.vin) {
-        const Coin& coin = view.AccessCoin(txin.prevout);
-        CScript scriptPubKey;
-        int scriptHeight = nHeight;
-        if (coin.out.IsNull()) {
-            for (auto entry : entries) {
-                auto& e = entry->GetTx();
-                if (e.GetHash() != txin.prevout.hash || txin.prevout.n >= e.vout.size())
-                    continue;
-                scriptPubKey = e.vout[txin.prevout.n].scriptPubKey;
-                break;
-            }
-        } else {
-            scriptPubKey = coin.out.scriptPubKey;
-            scriptHeight = coin.nHeight;
-        }
-
-        if (!scriptPubKey.empty()) {
-            int throwaway;
-            SpendClaim(trieCache, scriptPubKey, COutPoint(txin.prevout.hash, txin.prevout.n), scriptHeight, throwaway, spentClaims);
-        }
-    }
-
-    for (unsigned int i = 0; i < tx.vout.size(); ++i) {
-        const CTxOut& txout = tx.vout[i];
-        if (!txout.scriptPubKey.empty())
-            AddSpendClaim(trieCache, txout.scriptPubKey, COutPoint(tx.GetHash(), i), txout.nValue, nHeight, spentClaims);
-    }
-}
-
 // This transaction selection algorithm orders the mempool based
 // on feerate of a transaction including all unconfirmed ancestors.
 // Since we don't remove transactions from the mempool as we select them
@@ -349,7 +330,7 @@ void iterToTrieCache(CTxMemPool::txiter iter, CClaimTrieCache& trieCache, const
 // Each time through the loop, we compare the best transaction in
 // mapModifiedTxs with the next transaction in the mempool to decide what
 // transaction package to work on next.
-void BlockAssembler::addPackageTxs(int &nPackagesSelected, int &nDescendantsUpdated, CClaimTrieCache& trieCache)
+void BlockAssembler::addPackageTxs(int &nPackagesSelected, int &nDescendantsUpdated)
 {
     // mapModifiedTx will store sorted packages after they are modified
     // because some of their txs are already in the block
@@ -466,7 +447,6 @@ void BlockAssembler::addPackageTxs(int &nPackagesSelected, int &nDescendantsUpda
         SortForBlock(ancestors, sortedEntries);
 
         for (size_t i=0; i<sortedEntries.size(); ++i) {
-            iterToTrieCache(sortedEntries[i], trieCache, inBlock, nHeight);
             AddToBlock(sortedEntries[i]);
             // Erase from the modified set, if present
             mapModifiedTx.erase(sortedEntries[i]);
diff --git a/src/miner.h b/src/miner.h
index 1222f6ef5..7c4c45507 100644
--- a/src/miner.h
+++ b/src/miner.h
@@ -175,7 +175,7 @@ private:
     /** Add transactions based on feerate including unconfirmed ancestors
       * Increments nPackagesSelected / nDescendantsUpdated with corresponding
       * statistics from the package selection (for logging statistics). */
-    void addPackageTxs(int &nPackagesSelected, int &nDescendantsUpdated, CClaimTrieCache& trieCache) EXCLUSIVE_LOCKS_REQUIRED(mempool.cs);
+    void addPackageTxs(int &nPackagesSelected, int &nDescendantsUpdated) EXCLUSIVE_LOCKS_REQUIRED(mempool.cs);
 
     // helper functions for addPackageTxs()
     /** Remove confirmed (inBlock) entries from given set */
diff --git a/src/test/claimtriebranching_tests.cpp b/src/test/claimtriebranching_tests.cpp
index ed3faf7fa..b391b3c24 100644
--- a/src/test/claimtriebranching_tests.cpp
+++ b/src/test/claimtriebranching_tests.cpp
@@ -72,7 +72,7 @@ static BlockAssembler AssemblerForTest()
 }
 
 // Test Fixtures
-struct ClaimTrieChainFixture: public CClaimTrieCacheBase
+struct ClaimTrieChainFixture: public CClaimTrieCacheExpirationFork
 {
     std::vector<CTransaction> coinbase_txs;
     std::vector<int> marks;
@@ -86,9 +86,9 @@ struct ClaimTrieChainFixture: public CClaimTrieCacheBase
     int64_t originalExpiration;
     int64_t extendedExpiration;
 
-    using CClaimTrieCacheBase::getSupportsForName;
+    using CClaimTrieCacheExpirationFork::getSupportsForName;
 
-    ClaimTrieChainFixture(): CClaimTrieCacheBase(pclaimTrie),
+    ClaimTrieChainFixture(): CClaimTrieCacheExpirationFork(pclaimTrie),
         unique_block_counter(0), normalization_original(-1), expirationForkHeight(-1)
     {
         fRequireStandard = false;
@@ -114,14 +114,12 @@ struct ClaimTrieChainFixture: public CClaimTrieCacheBase
         DecrementBlocks(chainActive.Height());
         auto& consensus = const_cast<Consensus::Params&>(Params().GetConsensus());
         if (normalization_original >= 0)
-        {
             consensus.nNormalizedNameForkHeight = normalization_original;
-        }
+
         if (expirationForkHeight >= 0) {
             consensus.nExtendedClaimExpirationForkHeight = expirationForkHeight;
             consensus.nExtendedClaimExpirationTime = extendedExpiration;
             consensus.nOriginalClaimExpirationTime = originalExpiration;
-            base->nExpirationTime = originalExpiration;
         }
     }
 
@@ -136,7 +134,7 @@ struct ClaimTrieChainFixture: public CClaimTrieCacheBase
         consensus.nExtendedClaimExpirationForkHeight = target;
         consensus.nExtendedClaimExpirationTime = postForkExpirationTime;
         consensus.nOriginalClaimExpirationTime = preForkExpirationTime;
-        base->nExpirationTime = targetMinusCurrent >= 0 ? preForkExpirationTime : postForkExpirationTime;
+        setExpirationTime(targetMinusCurrent >= 0 ? preForkExpirationTime : postForkExpirationTime);
     }
 
     void setNormalizationForkHeight(int targetMinusCurrent) {
@@ -292,6 +290,7 @@ struct ClaimTrieChainFixture: public CClaimTrieCacheBase
             num_txs_for_next_block = 0;
             nNextHeight = chainActive.Height() + 1;
         }
+        setExpirationTime(Params().GetConsensus().GetExpirationTime(nNextHeight - 1));
     }
 
     //disconnect i blocks from tip
@@ -306,9 +305,10 @@ struct ClaimTrieChainFixture: public CClaimTrieCacheBase
         }
         BOOST_CHECK_EQUAL(state.IsValid(), true);
         BOOST_CHECK_EQUAL(ActivateBestChain(state, Params()), true);
-        nNextHeight = chainActive.Height() + 1;
         mempool.clear();
         num_txs_for_next_block = 0;
+        nNextHeight = chainActive.Height() + 1;
+        setExpirationTime(Params().GetConsensus().GetExpirationTime(nNextHeight - 1));
     }
 
     // decrement back to last mark
@@ -1157,7 +1157,7 @@ BOOST_AUTO_TEST_CASE(hardfork_claim_test)
     // make sure decrementing to before the fork height will apppropriately set back the
     // expiration time to the original expiraiton time
     fixture.DecrementBlocks(1);
-    BOOST_CHECK_NE(fixture.expirationTime(), 6);
+    BOOST_CHECK_EQUAL(fixture.expirationTime(), 3);
     fixture.IncrementBlocks(1);
     BOOST_CHECK_EQUAL(fixture.expirationTime(), 6);
 
@@ -1723,7 +1723,7 @@ BOOST_AUTO_TEST_CASE(normalization_does_not_kill_sort_order)
 BOOST_AUTO_TEST_CASE(normalization_does_not_kill_expirations)
 {
     ClaimTrieChainFixture fixture;
-    fixture.setExpirationTime(3);
+    fixture.setExpirationForkHeight(800, 3, 800);
     fixture.setNormalizationForkHeight(4);
     // need to see that claims expiring on the frame when we normalize aren't kept
     // need to see that supports expiring on the frame when we normalize aren't kept
diff --git a/src/validation.cpp b/src/validation.cpp
index 938dc2b9b..51377a0b3 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -1840,12 +1840,7 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
         assert(merkleHash == pindex->pprev->hashClaimTrie);
     }
 
-    if (pindex->nHeight == Params().GetConsensus().nExtendedClaimExpirationForkHeight)
-    {
-        LogPrintf("Decremented past the extended claim expiration hard fork height\n");
-        trieCache.setExpirationTime(Params().GetConsensus().GetExpirationTime(pindex->nHeight-1));
-        trieCache.forkForExpirationChange(false);
-    }
+    trieCache.expirationForkActive(pindex->nHeight, false);
 
     return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
 }
@@ -2196,13 +2191,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
     // Get the script flags for this block
     unsigned int flags = GetBlockScriptFlags(pindex, chainparams.GetConsensus());
 
-    // v 13 LBRYcrd hard fork to extend expiration time
-    if (pindex->nHeight == Params().GetConsensus().nExtendedClaimExpirationForkHeight)
-    {
-        LogPrintf("Incremented past the extended claim expiration hard fork height\n");
-        trieCache.setExpirationTime(chainparams.GetConsensus().GetExpirationTime(pindex->nHeight));
-        trieCache.forkForExpirationChange(true);
-    }
+    trieCache.expirationForkActive(pindex->nHeight, true);
 
     int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1;
     LogPrint(BCLog::BENCH, "    - Fork checks: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime2 - nTime1), nTimeForks * MICRO, nTimeForks * MILLI / nBlocksTotal);
@@ -2291,35 +2280,13 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
                              tx.GetHash().ToString(), FormatStateMessage(state));
             }
             control.Add(vChecks);
-
-            // To handle claim updates, stick all claims found in the inputs into a map of
-            // name: (txhash, nOut). When running through the outputs, if any claim's
-            // name is found in the map, send the name's txhash and nOut to the trie cache,
-            // and then remove the name: (txhash, nOut) mapping from the map.
-            // If there are two or more claims in the inputs with the same name, only
-            // use the first.
-
-            spentClaimsType spentClaims;
-
-            for (unsigned int j = 0; j < tx.vin.size(); j++)
-            {
-                const CTxIn& txin = tx.vin[j];
-                const Coin& coin = view.AccessCoin(txin.prevout);
-
-                if (coin.out.scriptPubKey.empty())
-                    continue;
-
-                int nValidAtHeight;
-                if (SpendClaim(trieCache, coin.out.scriptPubKey, COutPoint(txin.prevout.hash, txin.prevout.n), coin.nHeight, nValidAtHeight, spentClaims))
-                    mClaimUndoHeights[j] = nValidAtHeight;
-            }
-
-            for (unsigned int j = 0; j < tx.vout.size(); j++) {
-                const CTxOut& txout = tx.vout[j];
-
-                if (!txout.scriptPubKey.empty())
-                    AddSpendClaim(trieCache, txout.scriptPubKey, COutPoint(tx.GetHash(), j), txout.nValue, pindex->nHeight, spentClaims);
-            }
+            CUpdateCacheCallbacks callbacks = {
+                .findScriptKey = {},
+                .claimUndoHeights = [&mClaimUndoHeights](int index, int nValidAtHeight) {
+                    mClaimUndoHeights.emplace(index, nValidAtHeight);
+                }
+            };
+            UpdateCache(tx, trieCache, view, pindex->nHeight, callbacks);
         }
 
         CTxUndo undoDummy;
@@ -2358,7 +2325,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
 
     if (trieCache.getMerkleHash() != block.hashClaimTrie)
     {
-        if (trieCache.checkConsistency())
+        if (!trieCache.empty() && trieCache.checkConsistency())
             trieCache.dumpToLog(trieCache.begin());
         return state.DoS(100, error("ConnectBlock() : the merkle root of the claim trie does not match "
                                "(actual=%s vs block=%s on height=%d)", trieCache.getMerkleHash().GetHex(),