Repository: decred/dcrd
Pull request #2957 (2022-05-28 03:23:29)

blockchain: Use new uint256 for work sums.
~**This requires PR #2952**~.

Live profiling data pulled from an instance of dcrd v1.8.0-pre on commit a4b20e9d performing an initial sync shows that roughly 36% of all in-use allocations are the result of the big integers used to store the cumulative work for each block. Further, around 12% of the entire CPU time is spent scaning the heap for garbage collection which is a direct result of the total number of inuse allocations.  Therefore, a reasonable expectation is that eliminating those heap objects should produce a speedup of around 4-5%.

CPU Profiling Data:

```
      flat  flat%   sum%        cum   cum%
  1456.78s 25.97% 25.97%   1459.30s 26.01%  runtime.cgocall
      668s 11.91% 37.87%   2176.40s 38.79%  runtime.scanobject
   609.77s 10.87% 48.74%    668.60s 11.92%  runtime.findObject
   231.85s  4.13% 52.88%    231.85s  4.13%  runtime.heapBits.bits
   226.34s  4.03% 56.91%    226.34s  4.03%  runtime.markBits.isMarked
   178.15s  3.18% 60.09%    178.15s  3.18%  cmpbody
   176.58s  3.15% 63.23%    510.10s  9.09%  runtime.greyobject
```

Memory Profiling Data:
```
      flat  flat%   sum%        cum   cum%
    720918 20.08% 20.08%    1308043 36.44%  github.com/decred/dcrd/blockchain/standalone/v2.CalcWork
    587125 16.36% 73.81%     587125 16.36%  math/big.nat.make (inline)
```

Consequently, this modifies the blockchain package to make use of the much more efficient zero-alloc uint256s and associated work calculation funcs in the new primitives package that is under development.

As can be seen in the following graphs, the result is about 100MiB less heap usage on average while the overall total number of used bytes is almost identical, which results in a reduction of about 5% to the initial sync time which is all in line with the expected results.

![dcrd_initial_sync_total_bytes_used_comparison_uint256_work_sums_vs_1_8_0_a4b20e9d](https://user-images.githubusercontent.com/2115102/170807885-02621a63-384d-4c21-bd34-339560027dfc.png)

![dcrd_initial_sync_mem_usage_comparison_uint256_work_sums_vs_1_8_0_a4b20e9d](https://user-images.githubusercontent.com/2115102/170807891-9f2f2017-8aa2-4dd8-a4fc-7d79879b49da.png)




Patch:
diff --git a/internal/blockchain/blockindex.go b/internal/blockchain/blockindex.go
index 9d7c76b5..17bb9341 100644
--- a/internal/blockchain/blockindex.go
+++ b/internal/blockchain/blockindex.go
@@ -8,15 +8,15 @@ package blockchain
 import (
 	"bytes"
 	"encoding/binary"
-	"math/big"
 	"sort"
 	"sync"
 	"time"
 
 	"github.com/decred/dcrd/blockchain/stake/v5"
-	"github.com/decred/dcrd/blockchain/standalone/v2"
 	"github.com/decred/dcrd/chaincfg/chainhash"
 	"github.com/decred/dcrd/database/v3"
+	"github.com/decred/dcrd/internal/staging/primitives"
+	"github.com/decred/dcrd/math/uint256"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -128,7 +128,7 @@ type blockNode struct {
 
 	// workSum is the total amount of work in the chain up to and including
 	// this node.
-	workSum *big.Int
+	workSum uint256.Uint256
 
 	// Some fields from block headers to aid in best chain selection and
 	// reconstructing headers from memory.  These must be treated as
@@ -233,7 +233,7 @@ func calcSkipListHeight(height int64) int64 {
 func initBlockNode(node *blockNode, blockHeader *wire.BlockHeader, parent *blockNode) {
 	*node = blockNode{
 		hash:         blockHeader.BlockHash(),
-		workSum:      standalone.CalcWork(blockHeader.Bits),
+		workSum:      primitives.CalcWork(blockHeader.Bits),
 		height:       int64(blockHeader.Height),
 		blockVersion: blockHeader.Version,
 		voteBits:     blockHeader.VoteBits,
@@ -256,7 +256,7 @@ func initBlockNode(node *blockNode, blockHeader *wire.BlockHeader, parent *block
 	if parent != nil {
 		node.parent = parent
 		node.skipToAncestor = parent.Ancestor(calcSkipListHeight(node.height))
-		node.workSum = node.workSum.Add(parent.workSum, node.workSum)
+		node.workSum.Add(&parent.workSum)
 	}
 }
 
@@ -448,7 +448,7 @@ func betterCandidate(a, b *blockNode) bool {
 	//
 	// Blocks with more cumulative work are better candidates for best chain
 	// selection.
-	if workCmp := a.workSum.Cmp(b.workSum); workCmp != 0 {
+	if workCmp := a.workSum.Cmp(&b.workSum); workCmp != 0 {
 		return workCmp > 0
 	}
 
@@ -1249,7 +1249,7 @@ func (bi *blockIndex) removeLessWorkCandidates(node *blockNode) {
 	// Remove all best chain candidates that have less work than the passed
 	// node.
 	for n := range bi.bestChainCandidates {
-		if n.workSum.Cmp(node.workSum) < 0 {
+		if n.workSum.Lt(&node.workSum) {
 			bi.removeBestChainCandidate(n)
 		}
 	}
@@ -1306,7 +1306,7 @@ func (bi *blockIndex) linkBlockData(node, tip *blockNode) []*blockNode {
 
 		// The block is now a candidate to potentially become the best chain if
 		// it has the same or more work than the current best chain tip.
-		if linkedNode.workSum.Cmp(tip.workSum) >= 0 {
+		if linkedNode.workSum.GtEq(&tip.workSum) {
 			bi.addBestChainCandidate(linkedNode)
 		}
 
diff --git a/internal/blockchain/chain.go b/internal/blockchain/chain.go
index 60988732..163047c3 100644
--- a/internal/blockchain/chain.go
+++ b/internal/blockchain/chain.go
@@ -26,6 +26,7 @@ import (
 	"github.com/decred/dcrd/internal/blockchain/indexers"
 	"github.com/decred/dcrd/internal/blockchain/spendpruner"
 	"github.com/decred/dcrd/lru"
+	"github.com/decred/dcrd/math/uint256"
 	"github.com/decred/dcrd/txscript/v4"
 	"github.com/decred/dcrd/wire"
 )
@@ -141,6 +142,7 @@ type BlockChain struct {
 	allowOldForks            bool
 	expectedBlocksInTwoWeeks int64
 	deploymentVers           map[string]uint32
+	minKnownWork             *uint256.Uint256
 	db                       database.DB
 	dbInfo                   *databaseInfo
 	chainParams              *chaincfg.Params
@@ -467,7 +469,7 @@ func (b *BlockChain) ChainWork(hash *chainhash.Hash) (*big.Int, error) {
 		return nil, unknownBlockError(hash)
 	}
 
-	return node.workSum, nil
+	return node.workSum.ToBig(), nil
 }
 
 // TipGeneration returns the entire generation of blocks stemming from the
@@ -710,7 +712,7 @@ func (b *BlockChain) connectBlock(node *blockNode, block, parent *dcrutil.Block,
 	// Atomically insert info into the database.
 	err = b.db.Update(func(dbTx database.Tx) error {
 		// Update best block state.
-		err := dbPutBestState(dbTx, state, node.workSum)
+		err := dbPutBestState(dbTx, state, &node.workSum)
 		if err != nil {
 			return err
 		}
@@ -922,7 +924,7 @@ func (b *BlockChain) disconnectBlock(node *blockNode, block, parent *dcrutil.Blo
 
 	err = b.db.Update(func(dbTx database.Tx) error {
 		// Update best block state.
-		err := dbPutBestState(dbTx, state, node.workSum)
+		err := dbPutBestState(dbTx, state, &node.workSum)
 		if err != nil {
 			return err
 		}
@@ -1593,8 +1595,7 @@ func (b *BlockChain) maybeUpdateIsCurrent(curBest *blockNode) {
 	if !b.isCurrentLatch {
 		// Not current if the latest best block has a cumulative work less than
 		// the minimum known work specified by the network parameters.
-		minKnownWork := b.chainParams.MinKnownChainWork
-		if minKnownWork != nil && curBest.workSum.Cmp(minKnownWork) < 0 {
+		if b.minKnownWork != nil && curBest.workSum.Lt(b.minKnownWork) {
 			return
 		}
 
@@ -2371,6 +2372,15 @@ func New(ctx context.Context, config *Config) (*BlockChain, error) {
 		return nil, err
 	}
 
+	// Convert the minimum known work to a uint256 when it exists.  Ideally, the
+	// chain params should be updated to use the new type, but that will be a
+	// major version bump, so a one-time conversion is a good tradeoff in the
+	// mean time.
+	var minKnownWork *uint256.Uint256
+	if params.MinKnownChainWork != nil {
+		minKnownWork = new(uint256.Uint256).SetBig(params.MinKnownChainWork)
+	}
+
 	// Either use the subsidy cache provided by the caller or create a new
 	// one when one was not provided.
 	subsidyCache := config.SubsidyCache
@@ -2396,6 +2406,7 @@ func New(ctx context.Context, config *Config) (*BlockChain, error) {
 		allowOldForks:                 allowOldForks,
 		expectedBlocksInTwoWeeks:      expectedBlksInTwoWeeks,
 		deploymentVers:                deploymentVers,
+		minKnownWork:                  minKnownWork,
 		db:                            config.DB,
 		chainParams:                   params,
 		timeSource:                    config.TimeSource,
diff --git a/internal/blockchain/chainio.go b/internal/blockchain/chainio.go
index 5d964f39..a31fbc81 100644
--- a/internal/blockchain/chainio.go
+++ b/internal/blockchain/chainio.go
@@ -11,7 +11,6 @@ import (
 	"encoding/binary"
 	"errors"
 	"fmt"
-	"math/big"
 	"time"
 
 	"github.com/decred/dcrd/blockchain/stake/v5"
@@ -21,6 +20,7 @@ import (
 	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/gcs/v4"
 	"github.com/decred/dcrd/gcs/v4/blockcf2"
+	"github.com/decred/dcrd/math/uint256"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -1120,7 +1120,7 @@ func dbFetchDatabaseInfo(dbTx database.Tx) *databaseInfo {
 //   total txns        uint64           8 bytes
 //   total subsidy     int64            8 bytes
 //   work sum length   uint32           4 bytes
-//   work sum          big.Int          work sum length
+//   work sum          uint256          work sum length
 // -----------------------------------------------------------------------------
 
 // bestChainState represents the data to be stored the database for the current
@@ -1130,14 +1130,30 @@ type bestChainState struct {
 	height       uint32
 	totalTxns    uint64
 	totalSubsidy int64
-	workSum      *big.Int
+	workSum      uint256.Uint256
 }
 
 // serializeBestChainState returns the serialization of the passed block best
 // chain state.  This is data to be stored in the chain state bucket.
 func serializeBestChainState(state bestChainState) []byte {
 	// Calculate the full size needed to serialize the chain state.
-	workSumBytes := state.workSum.Bytes()
+	//
+	// NOTE: The leading zero bytes are truncated in order to match the
+	// semantics of legacy code that made use of stdlib big.Ints which returns a
+	// variable number of bytes versus the new uint256 type that returns a
+	// fixed-size array.  This is needed because the bytes are stored in the
+	// database and changing the format would require a version bump and the
+	// associated migration which does not seem worth it given the difference is
+	// easily handled efficiently as implemented here.
+	workSumBytesArray := state.workSum.Bytes()
+	var firstNonzero int
+	for i, b := range workSumBytesArray {
+		if b != 0 {
+			firstNonzero = i
+			break
+		}
+	}
+	workSumBytes := workSumBytesArray[firstNonzero:]
 	workSumBytesLen := uint32(len(workSumBytes))
 	serializedLen := chainhash.HashSize + 4 + 8 + 8 + 4 + workSumBytesLen
 
@@ -1196,21 +1212,21 @@ func deserializeBestChainState(serializedData []byte) (bestChainState, error) {
 		return bestChainState{}, makeDbErr(database.ErrCorruption, str)
 	}
 	workSumBytes := serializedData[offset : offset+workSumBytesLen]
-	state.workSum = new(big.Int).SetBytes(workSumBytes)
+	state.workSum = *new(uint256.Uint256).SetByteSlice(workSumBytes)
 
 	return state, nil
 }
 
 // dbPutBestState uses an existing database transaction to update the best chain
 // state with the given parameters.
-func dbPutBestState(dbTx database.Tx, snapshot *BestState, workSum *big.Int) error {
+func dbPutBestState(dbTx database.Tx, snapshot *BestState, workSum *uint256.Uint256) error {
 	// Serialize the current best chain state.
 	serializedData := serializeBestChainState(bestChainState{
 		hash:         snapshot.Hash,
 		height:       uint32(snapshot.Height),
 		totalTxns:    snapshot.TotalTxns,
 		totalSubsidy: snapshot.TotalSubsidy,
-		workSum:      workSum,
+		workSum:      *workSum,
 	})
 
 	// Store the current best chain state into the database.
@@ -1308,7 +1324,7 @@ func (b *BlockChain) createChainState() error {
 		}
 
 		// Store the current best chain state into the database.
-		err = dbPutBestState(dbTx, stateSnapshot, node.workSum)
+		err = dbPutBestState(dbTx, stateSnapshot, &node.workSum)
 		if err != nil {
 			return err
 		}
diff --git a/internal/blockchain/process.go b/internal/blockchain/process.go
index c209e6c7..a0da54ae 100644
--- a/internal/blockchain/process.go
+++ b/internal/blockchain/process.go
@@ -736,7 +736,7 @@ func (b *BlockChain) InvalidateBlock(hash *chainhash.Hash) error {
 		// Chain tips that have less work than the new tip are not best chain
 		// candidates nor are any of their ancestors since they have even less
 		// work.
-		if tip.workSum.Cmp(newTip.workSum) < 0 {
+		if tip.workSum.Lt(&newTip.workSum) {
 			return nil
 		}
 
@@ -748,7 +748,7 @@ func (b *BlockChain) InvalidateBlock(hash *chainhash.Hash) error {
 		for n != nil && (n.status.KnownInvalid() || !b.index.canValidate(n)) {
 			n = n.parent
 		}
-		if n != nil && n != newTip && n.workSum.Cmp(newTip.workSum) >= 0 {
+		if n != nil && n != newTip && n.workSum.GtEq(&newTip.workSum) {
 			b.index.addBestChainCandidate(n)
 		}
 
@@ -826,7 +826,7 @@ func (b *BlockChain) ReconsiderBlock(hash *chainhash.Hash) error {
 			b.recentContextChecks.Delete(n.hash)
 		}
 
-		if b.index.canValidate(n) && n.workSum.Cmp(curBestTip.workSum) >= 0 {
+		if b.index.canValidate(n) && n.workSum.GtEq(&curBestTip.workSum) {
 			b.index.addBestChainCandidate(n)
 		}
 
@@ -875,7 +875,7 @@ func (b *BlockChain) ReconsiderBlock(hash *chainhash.Hash) error {
 		for n := finalNotKnownInvalidDescendant; n != vfNode; n = n.parent {
 			b.index.unsetStatusFlags(n, statusInvalidAncestor)
 			b.recentContextChecks.Delete(n.hash)
-			if b.index.canValidate(n) && n.workSum.Cmp(curBestTip.workSum) >= 0 {
+			if b.index.canValidate(n) && n.workSum.GtEq(&curBestTip.workSum) {
 				b.index.addBestChainCandidate(n)
 			}
 


Test patch:
diff --git a/internal/blockchain/blockindex_test.go b/internal/blockchain/blockindex_test.go
index 46ae770c..a10cb6e1 100644
--- a/internal/blockchain/blockindex_test.go
+++ b/internal/blockchain/blockindex_test.go
@@ -6,13 +6,13 @@ package blockchain
 
 import (
 	"fmt"
-	"math/big"
 	"math/rand"
 	"reflect"
 	"testing"
 	"time"
 
 	"github.com/decred/dcrd/chaincfg/v3"
+	"github.com/decred/dcrd/math/uint256"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -406,13 +406,13 @@ func TestBetterCandidate(t *testing.T) {
 		name: "exactly equal, both data",
 		nodeA: &blockNode{
 			hash:            *mustParseHash("0000000000000000000000000000000000000000000000000000000000000000"),
-			workSum:         big.NewInt(2),
+			workSum:         *new(uint256.Uint256).SetUint64(2),
 			status:          statusDataStored,
 			receivedOrderID: 0,
 		},
 		nodeB: &blockNode{
 			hash:            *mustParseHash("0000000000000000000000000000000000000000000000000000000000000000"),
-			workSum:         big.NewInt(2),
+			workSum:         *new(uint256.Uint256).SetUint64(2),
 			status:          statusDataStored,
 			receivedOrderID: 0,
 		},
@@ -422,12 +422,12 @@ func TestBetterCandidate(t *testing.T) {
 		name: "exactly equal, no data",
 		nodeA: &blockNode{
 			hash:            *mustParseHash("0000000000000000000000000000000000000000000000000000000000000000"),
-			workSum:         big.NewInt(2),
+			workSum:         *new(uint256.Uint256).SetUint64(2),
 			receivedOrderID: 0,
 		},
 		nodeB: &blockNode{
 			hash:            *mustParseHash("0000000000000000000000000000000000000000000000000000000000000000"),
-			workSum:         big.NewInt(2),
+			workSum:         *new(uint256.Uint256).SetUint64(2),
 			receivedOrderID: 0,
 		},
 		wantCmp:    0,
@@ -436,12 +436,12 @@ func TestBetterCandidate(t *testing.T) {
 		name: "a has more cumulative work, same order, higher hash, b has data",
 		nodeA: &blockNode{
 			hash:            *higherHash,
-			workSum:         big.NewInt(4),
+			workSum:         *new(uint256.Uint256).SetUint64(4),
 			receivedOrderID: 0,
 		},
 		nodeB: &blockNode{
 			hash:            *lowerHash,
-			workSum:         big.NewInt(2),
+			workSum:         *new(uint256.Uint256).SetUint64(2),
 			status:          statusDataStored,
 			receivedOrderID: 0,
 		},
@@ -451,13 +451,13 @@ func TestBetterCandidate(t *testing.T) {
 		name: "a has less cumulative work, same order, lower hash, a has data",
 		nodeA: &blockNode{
 			hash:            *lowerHash,
-			workSum:         big.NewInt(2),
+			workSum:         *new(uint256.Uint256).SetUint64(2),
 			status:          statusDataStored,
 			receivedOrderID: 0,
 		},
 		nodeB: &blockNode{
 			hash:            *higherHash,
-			workSum:         big.NewInt(4),
+			workSum:         *new(uint256.Uint256).SetUint64(4),
 			receivedOrderID: 0,
 		},
 		wantCmp:    -1,
@@ -466,12 +466,12 @@ func TestBetterCandidate(t *testing.T) {
 		name: "a has same cumulative work, same order, lower hash, b has data",
 		nodeA: &blockNode{
 			hash:            *lowerHash,
-			workSum:         big.NewInt(2),
+			workSum:         *new(uint256.Uint256).SetUint64(2),
 			receivedOrderID: 0,
 		},
 		nodeB: &blockNode{
 			hash:            *higherHash,
-			workSum:         big.NewInt(2),
+			workSum:         *new(uint256.Uint256).SetUint64(2),
 			status:          statusDataStored,
 			receivedOrderID: 0,
 		},
@@ -481,13 +481,13 @@ func TestBetterCandidate(t *testing.T) {
 		name: "a has same cumulative work, same order, higher hash, a has data",
 		nodeA: &blockNode{
 			hash:            *higherHash,
-			workSum:         big.NewInt(2),
+			workSum:         *new(uint256.Uint256).SetUint64(2),
 			status:          statusDataStored,
 			receivedOrderID: 0,
 		},
 		nodeB: &blockNode{
 			hash:            *lowerHash,
-			workSum:         big.NewInt(2),
+			workSum:         *new(uint256.Uint256).SetUint64(2),
 			receivedOrderID: 0,
 		},
 		wantCmp:    1,
@@ -496,13 +496,13 @@ func TestBetterCandidate(t *testing.T) {
 		name: "a has same cumulative work, higher order, lower hash, both data",
 		nodeA: &blockNode{
 			hash:            *lowerHash,
-			workSum:         big.NewInt(2),
+			workSum:         *new(uint256.Uint256).SetUint64(2),
 			status:          statusDataStored,
 			receivedOrderID: 1,
 		},
 		nodeB: &blockNode{
 			hash:            *higherHash,
-			workSum:         big.NewInt(2),
+			workSum:         *new(uint256.Uint256).SetUint64(2),
 			status:          statusDataStored,
 			receivedOrderID: 0,
 		},
@@ -512,13 +512,13 @@ func TestBetterCandidate(t *testing.T) {
 		name: "a has same cumulative work, lower order, lower hash, both data",
 		nodeA: &blockNode{
 			hash:            *lowerHash,
-			workSum:         big.NewInt(2),
+			workSum:         *new(uint256.Uint256).SetUint64(2),
 			status:          statusDataStored,
 			receivedOrderID: 1,
 		},
 		nodeB: &blockNode{
 			hash:            *higherHash,
-			workSum:         big.NewInt(2),
+			workSum:         *new(uint256.Uint256).SetUint64(2),
 			status:          statusDataStored,
 			receivedOrderID: 2,
 		},
@@ -528,12 +528,12 @@ func TestBetterCandidate(t *testing.T) {
 		name: "a has same cumulative work, same order, lower hash, no data",
 		nodeA: &blockNode{
 			hash:            *lowerHash,
-			workSum:         big.NewInt(2),
+			workSum:         *new(uint256.Uint256).SetUint64(2),
 			receivedOrderID: 0,
 		},
 		nodeB: &blockNode{
 			hash:            *higherHash,
-			workSum:         big.NewInt(2),
+			workSum:         *new(uint256.Uint256).SetUint64(2),
 			receivedOrderID: 0,
 		},
 		wantCmp:    -1,
@@ -542,13 +542,13 @@ func TestBetterCandidate(t *testing.T) {
 		name: "a has same cumulative work, same order, lower hash, both data",
 		nodeA: &blockNode{
 			hash:            *lowerHash,
-			workSum:         big.NewInt(2),
+			workSum:         *new(uint256.Uint256).SetUint64(2),
 			status:          statusDataStored,
 			receivedOrderID: 0,
 		},
 		nodeB: &blockNode{
 			hash:            *higherHash,
-			workSum:         big.NewInt(2),
+			workSum:         *new(uint256.Uint256).SetUint64(2),
 			status:          statusDataStored,
 			receivedOrderID: 0,
 		},
@@ -558,13 +558,13 @@ func TestBetterCandidate(t *testing.T) {
 		name: "a has same cumulative work, same order, higher hash, both data",
 		nodeA: &blockNode{
 			hash:            *higherHash,
-			workSum:         big.NewInt(2),
+			workSum:         *new(uint256.Uint256).SetUint64(2),
 			status:          statusDataStored,
 			receivedOrderID: 0,
 		},
 		nodeB: &blockNode{
 			hash:            *lowerHash,
-			workSum:         big.NewInt(2),
+			workSum:         *new(uint256.Uint256).SetUint64(2),
 			status:          statusDataStored,
 			receivedOrderID: 0,
 		},
diff --git a/internal/blockchain/chainio_test.go b/internal/blockchain/chainio_test.go
index 103755e4..8793f3f2 100644
--- a/internal/blockchain/chainio_test.go
+++ b/internal/blockchain/chainio_test.go
@@ -10,16 +10,16 @@ import (
 	"encoding/hex"
 	"errors"
 	"math"
-	"math/big"
 	"reflect"
 	"testing"
 	"time"
 
 	"github.com/decred/dcrd/blockchain/stake/v5"
-	"github.com/decred/dcrd/blockchain/standalone/v2"
 	"github.com/decred/dcrd/chaincfg/chainhash"
 	"github.com/decred/dcrd/chaincfg/v3"
 	"github.com/decred/dcrd/database/v3"
+	"github.com/decred/dcrd/internal/staging/primitives"
+	"github.com/decred/dcrd/math/uint256"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -892,7 +892,7 @@ func TestHeaderCommitmentDeserializeErrors(t *testing.T) {
 func TestBestChainStateSerialization(t *testing.T) {
 	t.Parallel()
 
-	workSum := new(big.Int)
+	var workSum uint256.Uint256
 	tests := []struct {
 		name       string
 		state      bestChainState
@@ -905,9 +905,10 @@ func TestBestChainStateSerialization(t *testing.T) {
 				height:       0,
 				totalTxns:    1,
 				totalSubsidy: 0,
-				workSum: func() *big.Int {
-					workSum.Add(workSum, standalone.CalcWork(486604799))
-					return new(big.Int).Set(workSum)
+				workSum: func() uint256.Uint256 {
+					work := primitives.CalcWork(486604799)
+					workSum.Add(&work)
+					return workSum
 				}(), // 0x0100010001
 			},
 			serialized: hexToBytes("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000001000000000000000000000000000000050000000100010001"),
@@ -919,9 +920,10 @@ func TestBestChainStateSerialization(t *testing.T) {
 				height:       1,
 				totalTxns:    2,
 				totalSubsidy: 123456789,
-				workSum: func() *big.Int {
-					workSum.Add(workSum, standalone.CalcWork(486604799))
-					return new(big.Int).Set(workSum)
+				workSum: func() uint256.Uint256 {
+					work := primitives.CalcWork(486604799)
+					workSum.Add(&work)
+					return workSum
 				}(), // 0x0200020002,
 			},
 			serialized: hexToBytes("4860eb18bf1b1620e37e9490fc8a427514416fd75159ab86688e9a830000000001000000020000000000000015cd5b0700000000050000000200020002"),