diff --git a/Dockerfile b/Dockerfile
index ebaeefd76..a5b5cd466 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -30,7 +30,7 @@ RUN GOOS=linux go build -mod=mod -a -installsuffix cgo -o bin/core main.go
 # create tiny image
 FROM alpine:edge
 
-RUN apk add --update vips-dev
+RUN apk add --update vips
 
 COPY --from=core /deso/src/core/bin/core /deso/bin/core
 
diff --git a/lib/pos_fee_estimator.go b/lib/pos_fee_estimator.go
index 2462428e3..90aa0fcff 100644
--- a/lib/pos_fee_estimator.go
+++ b/lib/pos_fee_estimator.go
@@ -83,7 +83,7 @@ func (posFeeEstimator *PoSFeeEstimator) Init(
 	posFeeEstimator.pastBlocksTransactionRegister.Init(globalParams.Copy())
 
 	// Sort the past blocks by height just to be safe.
-	sortedPastBlocks := posFeeEstimator.cleanUpPastBlocks(pastBlocks)
+	sortedPastBlocks, _ := posFeeEstimator.cleanUpPastBlocks(pastBlocks)
 
 	// Add all the txns from the past blocks to the new pastBlocksTransactionRegister.
 	for _, block := range sortedPastBlocks {
@@ -116,22 +116,36 @@ func (posFeeEstimator *PoSFeeEstimator) AddBlock(block *MsgDeSoBlock) error {
 func (posFeeEstimator *PoSFeeEstimator) addBlockNoLock(block *MsgDeSoBlock) error {
 	// Create a new slice to house the new past blocks and add the new block to it.
 	newPastBlocks := append(posFeeEstimator.cachedBlocks, block)
-	newPastBlocks = posFeeEstimator.cleanUpPastBlocks(newPastBlocks)
+	var removedBlocks []*MsgDeSoBlock
+	newPastBlocks, removedBlocks = posFeeEstimator.cleanUpPastBlocks(newPastBlocks)
 
-	// Create a clean transaction register to add the blocks' transactions.
-	newTransactionRegister := NewTransactionRegister()
-	newTransactionRegister.Init(posFeeEstimator.globalParams.Copy())
+	incomingBlockHash, err := block.Hash()
+	if err != nil {
+		return errors.Wrap(err, "PoSFeeEstimator.addBlockNoLock: error computing blockHash")
+	}
+
+	// Remove all blocks that were pruned are no longer in the cached blocks.
+	for _, removedBlock := range removedBlocks {
+		if err = posFeeEstimator.removeBlockNoLock(removedBlock); err != nil {
+			return errors.Wrap(err, "PoSFeeEstimator.addBlockNoLock: error removing block from PoSFeeEstimator")
+		}
+	}
+
+	incomingBlockRemoved := collections.Any(removedBlocks, func(removedBlock *MsgDeSoBlock) bool {
+		// It's not possible for us to have added a block that can't be hashed.
+		removedBlockHash, _ := removedBlock.Hash()
+		return removedBlockHash.IsEqual(incomingBlockHash)
+	})
 
-	// Add all transactions from the block to the pastBlocksTransactionRegister.
-	for _, pastBlock := range newPastBlocks {
-		if err := addBlockToTransactionRegister(newTransactionRegister, pastBlock); err != nil {
+	if !incomingBlockRemoved {
+		// Add all transactions from the block to the pastBlocksTransactionRegister.
+		if err := addBlockToTransactionRegister(posFeeEstimator.pastBlocksTransactionRegister, block); err != nil {
 			return errors.Wrap(err, "PoSFeeEstimator.addBlockNoLock: error adding block to pastBlocksTransactionRegister")
 		}
 	}
 
-	// Update the cached blocks and pastBlocksTransactionRegister.
+	// Update the cached blocks
 	posFeeEstimator.cachedBlocks = newPastBlocks
-	posFeeEstimator.pastBlocksTransactionRegister = newTransactionRegister
 
 	return nil
 }
@@ -157,6 +171,25 @@ func addBlockToTransactionRegister(txnRegister *TransactionRegister, block *MsgD
 	return nil
 }
 
+func removeBlockFromTransactionRegister(txnRegister *TransactionRegister, block *MsgDeSoBlock) error {
+	for _, txn := range block.Txns {
+		// We explicitly exclude block reward transactions as they do not have fees and were never
+		// added in the first place.
+		if txn.TxnMeta.GetTxnType() == TxnTypeBlockReward {
+			continue
+		}
+		mtxn, err := NewMempoolTx(txn, NanoSecondsToTime(block.Header.TstampNanoSecs), block.Header.Height)
+		if err != nil {
+			return errors.Wrap(err, "PoSFeeEstimator.removeBlockFromTransactionRegister: error creating MempoolTx")
+		}
+		if err = txnRegister.RemoveTransaction(mtxn); err != nil {
+			return errors.Wrap(err,
+				"PoSFeeEstimator.removeBlockFromTransactionRegister: error removing txn from pastBlocksTransactionRegister")
+		}
+	}
+	return nil
+}
+
 // RemoveBlock removes a block from the PoSFeeEstimator. This will remove all the transactions from the block
 // from the pastBlocksTransactionRegister and remove the block from the cache.
 func (posFeeEstimator *PoSFeeEstimator) RemoveBlock(block *MsgDeSoBlock) error {
@@ -186,20 +219,14 @@ func (posFeeEstimator *PoSFeeEstimator) removeBlockNoLock(block *MsgDeSoBlock) e
 		return !blockHash.IsEqual(cachedBlockHash)
 	})
 
-	// Create a clean transaction register to add the blocks' transactions.
-	newTransactionRegister := NewTransactionRegister()
-	newTransactionRegister.Init(posFeeEstimator.globalParams.Copy())
-
-	// Add all transactions from the past blocks to the transaction register.
-	for _, pastBlock := range newPastBlocks {
-		if err := addBlockToTransactionRegister(newTransactionRegister, pastBlock); err != nil {
-			return errors.Wrap(err, "PoSFeeEstimator.removeBlockNoLock: error adding block to transaction register")
-		}
+	// Remove the block from the transaction register
+	if err = removeBlockFromTransactionRegister(posFeeEstimator.pastBlocksTransactionRegister, block); err != nil {
+		return errors.Wrap(err,
+			"PoSFeeEstimator.removeBlockNoLock: error removing block from pastBlocksTransactionRegister")
 	}
 
 	// Update the cached blocks and pastBlocksTransactionRegister.
 	posFeeEstimator.cachedBlocks = newPastBlocks
-	posFeeEstimator.pastBlocksTransactionRegister = newTransactionRegister
 
 	return nil
 }
@@ -226,23 +253,28 @@ func (posFeeEstimator *PoSFeeEstimator) UpdateGlobalParams(globalParams *GlobalP
 }
 
 // cleanUpPastBlocks cleans up the input blocks slice, deduping, sorting, and pruning the blocks by height.
-func (posFeeEstimator *PoSFeeEstimator) cleanUpPastBlocks(blocks []*MsgDeSoBlock) []*MsgDeSoBlock {
-	dedupedBlocks := posFeeEstimator.dedupeBlocksByBlockHeight(blocks)
+func (posFeeEstimator *PoSFeeEstimator) cleanUpPastBlocks(blocks []*MsgDeSoBlock) ([]*MsgDeSoBlock, []*MsgDeSoBlock) {
+	dedupedBlocks, dupeBlocksRemoved := posFeeEstimator.dedupeBlocksByBlockHeight(blocks)
 	sortedBlocks := posFeeEstimator.sortBlocksByBlockHeight(dedupedBlocks)
-	return posFeeEstimator.pruneBlocksToMaxNumPastBlocks(sortedBlocks)
+	cleanedUpPastBlocks, prunedBlocks := posFeeEstimator.pruneBlocksToMaxNumPastBlocks(sortedBlocks)
+	return cleanedUpPastBlocks, append(dupeBlocksRemoved, prunedBlocks...)
 }
 
 // dedupeBlocksByBlockHeight deduplicates the blocks by block height. If multiple blocks have the same
 // height, it keeps the one with the highest view.
-func (posFeeEstimator *PoSFeeEstimator) dedupeBlocksByBlockHeight(blocks []*MsgDeSoBlock) []*MsgDeSoBlock {
+func (posFeeEstimator *PoSFeeEstimator) dedupeBlocksByBlockHeight(blocks []*MsgDeSoBlock) (
+	_dedupedBlocks []*MsgDeSoBlock, _prunedBlocks []*MsgDeSoBlock) {
 	blocksByBlockHeight := make(map[uint64]*MsgDeSoBlock)
+	removedBlocks := make([]*MsgDeSoBlock, 0)
 	for _, block := range blocks {
 		existingBlock, hasExistingBlock := blocksByBlockHeight[block.Header.Height]
 		if !hasExistingBlock || existingBlock.Header.GetView() < block.Header.GetView() {
 			blocksByBlockHeight[block.Header.Height] = block
+		} else {
+			removedBlocks = append(removedBlocks, block)
 		}
 	}
-	return collections.MapValues(blocksByBlockHeight)
+	return collections.MapValues(blocksByBlockHeight), removedBlocks
 }
 
 // sortBlocksByBlockHeightAndTstamp sorts the blocks by height.
@@ -268,15 +300,17 @@ func (posFeeEstimator *PoSFeeEstimator) sortBlocksByBlockHeight(blocks []*MsgDeS
 }
 
 // pruneBlocksToMaxNumPastBlocks reduces the number of blocks to the numPastBlocks param
-func (posFeeEstimator *PoSFeeEstimator) pruneBlocksToMaxNumPastBlocks(blocks []*MsgDeSoBlock) []*MsgDeSoBlock {
+func (posFeeEstimator *PoSFeeEstimator) pruneBlocksToMaxNumPastBlocks(blocks []*MsgDeSoBlock) (
+	_cachedBlocks []*MsgDeSoBlock, _prunedBlocks []*MsgDeSoBlock) {
 	numCachedBlocks := uint64(len(blocks))
 	if numCachedBlocks <= posFeeEstimator.numPastBlocks {
-		return blocks
+		return blocks, nil
 	}
 
 	// Prune the blocks with the lowest block heights. We do this by removing the
 	// first len(blocks) - numPastBlocks blocks from the blocks slice.
-	return blocks[numCachedBlocks-posFeeEstimator.numPastBlocks:]
+	return blocks[numCachedBlocks-posFeeEstimator.numPastBlocks:],
+		blocks[:numCachedBlocks-posFeeEstimator.numPastBlocks]
 }
 
 // EstimateFeeRateNanosPerKB estimates the fee rate in nanos per KB for the current mempool