From 9ef7db9b23e0a789f2cf9445d91badbbaf2cda2a Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Mon, 9 Jul 2018 15:00:15 -0500 Subject: [PATCH] blockchain: Refactor to use new chain view. This refactors and simplifies the code in blockchain to use the new more efficient chain views. An overview of the logic changes are as follows: - Remove inMainChain from block nodes since that can now be efficiently determined by using the chain view - Track the best chain via a chain view instead of a single block node - Use the tip of the best chain view everywhere bestNode was used - Update chain view tip instead of updating best node - Remove height map and associated lock in favor of chain view - Use chain view NodeByHeight everywhere height map was used - Change reorg logic to use more efficient chain view fork finding logic - Change block locator code over to use more efficient chain view logic - Remove now unused block-index-based block locator code - Move BlockLocator definition to chain.go - Move BlockLocatorFromHash and LatestBlockLocator to chain.go - Update both to use more efficient chain view logic - Rework several functions to use chain view for main chain detection - fetchMainChainBlockByNode - BlockByHeight - MainChainHasBlock - findPreviousCheckpoint - IsCheckpointCandidate --- blockchain/accept.go | 4 +- blockchain/agendas_test.go | 4 +- blockchain/blockindex.go | 5 - blockchain/blockindex_test.go | 11 +- blockchain/blocklocator.go | 137 ----------------- blockchain/chain.go | 253 ++++++++++++++++---------------- blockchain/chain_test.go | 57 ++++--- blockchain/chainio.go | 10 +- blockchain/chainquery.go | 12 +- blockchain/chainview.go | 19 +++ blockchain/checkpoints.go | 17 +-- blockchain/common_test.go | 6 +- blockchain/difficulty.go | 8 +- blockchain/difficulty_test.go | 22 +-- blockchain/sequencelock.go | 2 +- blockchain/sequencelock_test.go | 4 +- blockchain/stakeext.go | 26 ++-- blockchain/stakenode.go | 2 +- blockchain/stakeversion_test.go | 30 ++-- blockchain/thresholdstate.go | 6 +- blockchain/utxoviewpoint.go | 2 +- blockchain/validate.go | 2 +- blockchain/validate_test.go | 6 +- blockchain/votebits_test.go | 32 ++-- 24 files changed, 262 insertions(+), 415 deletions(-) delete mode 100644 blockchain/blocklocator.go diff --git a/blockchain/accept.go b/blockchain/accept.go index 79adac65..c228e4d7 100644 --- a/blockchain/accept.go +++ b/blockchain/accept.go @@ -179,7 +179,7 @@ func (b *BlockChain) maybeAcceptBlock(block *dcrutil.Block, flags BehaviorFlags) // Fetching a stake node could enable a new DoS vector, so restrict // this only to blocks that are recent in history. - if newNode.height < b.bestNode.height-minMemoryNodes { + if newNode.height < b.bestChain.Tip().height-minMemoryNodes { newNode.stakeNode, err = b.fetchStakeNode(newNode) if err != nil { return 0, err @@ -205,7 +205,7 @@ func (b *BlockChain) maybeAcceptBlock(block *dcrutil.Block, flags BehaviorFlags) // Notify the caller that the new block was accepted into the block // chain. The caller would typically want to react by relaying the // inventory to other peers. - bestHeight := b.bestNode.height + bestHeight := b.bestChain.Tip().height b.chainLock.Unlock() b.sendNotification(NTBlockAccepted, &BlockAcceptedNtfnsData{ BestHeight: bestHeight, diff --git a/blockchain/agendas_test.go b/blockchain/agendas_test.go index 272cf2b1..4b410933 100644 --- a/blockchain/agendas_test.go +++ b/blockchain/agendas_test.go @@ -108,7 +108,7 @@ func testLNFeaturesDeployment(t *testing.T, params *chaincfg.Params, deploymentV curTimestamp := time.Now() bc := newFakeChain(params) - node := bc.bestNode + node := bc.bestChain.Tip() for _, test := range tests { for i := uint32(0); i < test.numNodes; i++ { node = newFakeNode(node, int32(deploymentVer), @@ -122,7 +122,7 @@ func testLNFeaturesDeployment(t *testing.T, params *chaincfg.Params, deploymentV Bits: yesChoice.Bits | 0x01, }) } - bc.bestNode = node + bc.bestChain.SetTip(node) curTimestamp = curTimestamp.Add(time.Second) } diff --git a/blockchain/blockindex.go b/blockchain/blockindex.go index 79500d51..3f2239ca 100644 --- a/blockchain/blockindex.go +++ b/blockchain/blockindex.go @@ -113,11 +113,6 @@ type blockNode struct { // methods on blockIndex once the node has been added to the index. status blockStatus - // inMainChain denotes whether the block node is currently on the - // the main chain or not. This is used to help find the common - // ancestor when switching chains. - inMainChain bool - // stakeNode contains all the consensus information required for the // staking system. The node also caches information required to add or // remove stake nodes, so that the stake node itself may be pruneable diff --git a/blockchain/blockindex_test.go b/blockchain/blockindex_test.go index 57b080ea..8387bce0 100644 --- a/blockchain/blockindex_test.go +++ b/blockchain/blockindex_test.go @@ -34,9 +34,10 @@ func TestBlockNodeHeader(t *testing.T) { // values. params := &chaincfg.SimNetParams bc := newFakeChain(params) + tip := bc.bestChain.Tip() testHeader := wire.BlockHeader{ Version: 1, - PrevBlock: bc.bestNode.hash, + PrevBlock: tip.hash, MerkleRoot: *mustParseHash("09876543210987654321"), StakeRoot: *mustParseHash("43210987654321098765"), VoteBits: 0x03, @@ -54,7 +55,7 @@ func TestBlockNodeHeader(t *testing.T) { ExtraData: [32]byte{0xbb}, StakeVersion: 5, } - node := newBlockNode(&testHeader, bc.bestNode) + node := newBlockNode(&testHeader, tip) bc.index.AddNode(node) // Ensure reconstructing the header for the node produces the same header @@ -154,11 +155,11 @@ func TestCalcPastMedianTime(t *testing.T) { // Create a synthetic chain with the correct number of nodes and the // timestamps as specified by the test. bc := newFakeChain(params) - node := bc.bestNode + node := bc.bestChain.Tip() for _, timestamp := range test.timestamps { node = newFakeNode(node, 0, 0, 0, time.Unix(timestamp, 0)) bc.index.AddNode(node) - bc.bestNode = node + bc.bestChain.SetTip(node) } // Ensure the median time is the expected value. @@ -177,7 +178,7 @@ func TestCalcPastMedianTime(t *testing.T) { func TestChainTips(t *testing.T) { params := &chaincfg.SimNetParams bc := newFakeChain(params) - genesis := bc.bestNode + genesis := bc.bestChain.NodeByHeight(0) // Construct a synthetic simnet chain consisting of the following structure. // 0 -> 1 -> 2 -> 3 -> 4 diff --git a/blockchain/blocklocator.go b/blockchain/blocklocator.go deleted file mode 100644 index f58ae9e7..00000000 --- a/blockchain/blocklocator.go +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (c) 2013-2016 The btcsuite developers -// Copyright (c) 2015-2018 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package blockchain - -import ( - "github.com/decred/dcrd/chaincfg/chainhash" -) - -// log2FloorMasks defines the masks to use when quickly calculating -// floor(log2(x)) in a constant log2(32) = 5 steps, where x is a uint32, using -// shifts. They are derived from (2^(2^x) - 1) * (2^(2^x)), for x in 4..0. -var log2FloorMasks = []uint32{0xffff0000, 0xff00, 0xf0, 0xc, 0x2} - -// fastLog2Floor calculates and returns floor(log2(x)) in a constant 5 steps. -func fastLog2Floor(n uint32) uint8 { - rv := uint8(0) - exponent := uint8(16) - for i := 0; i < 5; i++ { - if n&log2FloorMasks[i] != 0 { - rv += exponent - n >>= exponent - } - exponent >>= 1 - } - return rv -} - -// BlockLocator is used to help locate a specific block. The algorithm for -// building the block locator is to add the hashes in reverse order until -// the genesis block is reached. In order to keep the list of locator hashes -// to a reasonable number of entries, first the most recent 12 block hashes are -// added, then the step is doubled each loop iteration to exponentially decrease -// the number of hashes as a function of the distance from the block being -// located. -// -// For example, assume you have a block chain with a side chain as depicted -// below: -// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18 -// \-> 16a -> 17a -// -// The block locator for block 17a would be the hashes of blocks: -// [17a 16a 15 14 13 12 11 10 9 8 7 6 4 genesis] -type BlockLocator []*chainhash.Hash - -// blockLocator returns a block locator for the passed block node. -// -// See BlockLocator for details on the algorithm used to create a block locator. -// -// This function MUST be called with the block index lock held (for reads). -func blockLocator(node *blockNode) BlockLocator { - if node == nil { - return nil - } - - // Calculate the max number of entries that will ultimately be in the - // block locator. See the description of the algorithm for how these - // numbers are derived. - var maxEntries uint8 - if node.height <= 12 { - maxEntries = uint8(node.height) + 1 - } else { - // Requested hash itself + previous 10 entries + genesis block. - // Then floor(log2(height-10)) entries for the skip portion. - adjustedHeight := uint32(node.height) - 10 - maxEntries = 12 + fastLog2Floor(adjustedHeight) - } - locator := make(BlockLocator, 0, maxEntries) - - step := int64(1) - for node != nil { - locator = append(locator, &node.hash) - - // Nothing more to add once the genesis block has been added. - if node.height == 0 { - break - } - - // Calculate height of previous node to include ensuring the - // final node is the genesis block. - height := node.height - step - if height < 0 { - height = 0 - } - - // Walk backwards through the nodes to the correct ancestor. - node = node.Ancestor(height) - - // Once 11 entries have been included, start doubling the - // distance between included hashes. - if len(locator) > 10 { - step *= 2 - } - } - - return locator -} - -// BlockLocatorFromHash returns a block locator for the passed block hash. -// See BlockLocator for details on the algorithm used to create a block locator. -// -// In addition to the general algorithm referenced above, there are a couple of -// special cases which are handled: -// -// - If the genesis hash is passed, there are no previous hashes to add and -// therefore the block locator will only consist of the genesis hash -// - If the passed hash is not currently known, the block locator will be for -// the latest known tip of the main (best) chain. -// -// This function is safe for concurrent access. -func (b *BlockChain) BlockLocatorFromHash(hash *chainhash.Hash) BlockLocator { - b.chainLock.RLock() - b.index.RLock() - node, exists := b.index.index[*hash] - if !exists { - node = b.bestNode - } - locator := blockLocator(node) - b.index.RUnlock() - b.chainLock.RUnlock() - return locator -} - -// LatestBlockLocator returns a block locator for the latest known tip of the -// main (best) chain. -// -// This function is safe for concurrent access. -func (b *BlockChain) LatestBlockLocator() (BlockLocator, error) { - b.chainLock.RLock() - b.index.RLock() - locator := blockLocator(b.bestNode) - b.index.RUnlock() - b.chainLock.RUnlock() - return locator, nil -} diff --git a/blockchain/chain.go b/blockchain/chain.go index cc8e0795..798ead83 100644 --- a/blockchain/chain.go +++ b/blockchain/chain.go @@ -52,6 +52,22 @@ func panicf(format string, args ...interface{}) { panic(str) } +// BlockLocator is used to help locate a specific block. The algorithm for +// building the block locator is to add the hashes in reverse order until +// the genesis block is reached. In order to keep the list of locator hashes +// to a reasonable number of entries, first the most recent previous 12 block +// hashes are added, then the step is doubled each loop iteration to +// exponentially decrease the number of hashes as a function of the distance +// from the block being located. +// +// For example, assume a block chain with a side chain as depicted below: +// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18 +// \-> 16a -> 17a +// +// The block locator for block 17a would be the hashes of blocks: +// [17a 16a 15 14 13 12 11 10 9 8 7 6 4 genesis] +type BlockLocator []*chainhash.Hash + // orphanBlock represents a block that we don't yet have the parent for. It // is a normal block plus an expiration time to prevent caching the orphan // forever. @@ -124,15 +140,17 @@ type BlockChain struct { noVerify bool noCheckpoints bool - // These fields are related to the memory block index. They are - // protected by the chain lock. - bestNode *blockNode - index *blockIndex - - // This field allows efficient lookup of nodes in the main chain by - // height. It is protected by the height lock. - heightLock sync.RWMutex - mainNodesByHeight map[int64]*blockNode + // These fields are related to the memory block index. They both have + // their own locks, however they are often also protected by the chain + // lock to help prevent logic races when blocks are being processed. + // + // index houses the entire block index in memory. The block index is + // a tree-shaped structure. + // + // bestChain tracks the current active chain by making use of an + // efficient chain view into the block index. + index *blockIndex + bestChain *chainView // These fields are related to handling of orphan blocks. They are // protected by a combination of the chain lock and the orphan lock. @@ -462,7 +480,7 @@ func (b *BlockChain) addOrphanBlock(block *dcrutil.Block) { func (b *BlockChain) TipGeneration() ([]chainhash.Hash, error) { b.chainLock.Lock() b.index.RLock() - nodes := b.index.chainTips[b.bestNode.height] + nodes := b.index.chainTips[b.bestChain.Tip().height] nodeHashes := make([]chainhash.Hash, len(nodes)) for i, n := range nodes { nodeHashes[i] = n.hash @@ -481,6 +499,12 @@ func (b *BlockChain) TipGeneration() ([]chainhash.Hash, error) { // // This function MUST be called with the chain lock held (for reads). func (b *BlockChain) fetchMainChainBlockByNode(node *blockNode) (*dcrutil.Block, error) { + // Ensure the block is in the main chain. + if !b.bestChain.Contains(node) { + str := fmt.Sprintf("block %s is not in the main chain", node.hash) + return nil, errNotInMainChain(str) + } + b.mainchainBlockCacheLock.RLock() block, ok := b.mainchainBlockCache[node.hash] b.mainchainBlockCacheLock.RUnlock() @@ -488,12 +512,6 @@ func (b *BlockChain) fetchMainChainBlockByNode(node *blockNode) (*dcrutil.Block, return block, nil } - // Ensure the block in the main chain. - if !node.inMainChain { - str := fmt.Sprintf("block %s is not in the main chain", node.hash) - return nil, errNotInMainChain(str) - } - // Load the block from the database. err := b.db.View(func(dbTx database.Tx) error { var err error @@ -543,7 +561,7 @@ func (b *BlockChain) fetchBlockByNode(node *blockNode) (*dcrutil.Block, error) { // This function MUST be called with the chain state lock held (for writes). func (b *BlockChain) pruneStakeNodes() { // Find the height to prune to. - pruneToNode := b.bestNode + pruneToNode := b.bestChain.Tip() for i := int64(0); i < minMemoryStakeNodes-1 && pruneToNode != nil; i++ { pruneToNode = pruneToNode.parent } @@ -570,7 +588,7 @@ func (b *BlockChain) pruneStakeNodes() { node := e.Value.(*blockNode) // Do not attempt to prune if the node should already have been pruned, // for example if you're adding an old side chain block. - if node.height > b.bestNode.height-minMemoryNodes { + if node.height > b.bestChain.Tip().height-minMemoryNodes { node.stakeNode = nil node.stakeUndoData = nil node.newTickets = nil @@ -588,8 +606,9 @@ func (b *BlockChain) BestPrevHash() chainhash.Hash { defer b.chainLock.Unlock() var prevHash chainhash.Hash - if b.bestNode.parent != nil { - prevHash = b.bestNode.parent.hash + tip := b.bestChain.Tip() + if tip.parent != nil { + prevHash = tip.parent.hash } return prevHash } @@ -642,21 +661,15 @@ func (b *BlockChain) getReorganizeNodes(node *blockNode) (*list.List, *list.List // to attach to the main tree. Push them onto the list in reverse order // so they are attached in the appropriate order when iterating the list // later. - ancestor := node - for ; ancestor.parent != nil; ancestor = ancestor.parent { - if ancestor.inMainChain { - break - } - attachNodes.PushFront(ancestor) + forkNode := b.bestChain.FindFork(node) + for n := node; n != nil && n != forkNode; n = n.parent { + attachNodes.PushFront(n) } // Start from the end of the main chain and work backwards until the // common ancestor adding each block to the list of nodes to detach from // the main chain. - for n := b.bestNode; n != nil; n = n.parent { - if n.hash == ancestor.hash { - break - } + for n := b.bestChain.Tip(); n != nil && n != forkNode; n = n.parent { detachNodes.PushBack(n) } @@ -692,10 +705,11 @@ func (b *BlockChain) pushMainChainBlockCache(block *dcrutil.Block) { func (b *BlockChain) connectBlock(node *blockNode, block, parent *dcrutil.Block, view *UtxoViewpoint, stxos []spentTxOut) error { // Make sure it's extending the end of the best chain. prevHash := block.MsgBlock().Header.PrevBlock - if prevHash != b.bestNode.hash { + tip := b.bestChain.Tip() + if prevHash != tip.hash { panicf("block %v (height %v) connects to block %v instead of "+ "extending the best chain (hash %v, height %v)", node.hash, - node.height, prevHash, b.bestNode.hash, b.bestNode.height) + node.height, prevHash, tip.hash, tip.height) } // Sanity check the correct number of stxos are provided. @@ -791,14 +805,8 @@ func (b *BlockChain) connectBlock(node *blockNode, block, parent *dcrutil.Block, // now that the modifications have been committed to the database. view.commit() - // Mark block as being in the main chain. - node.inMainChain = true - b.heightLock.Lock() - b.mainNodesByHeight[node.height] = node - b.heightLock.Unlock() - // This node is now the end of the best chain. - b.bestNode = node + b.bestChain.SetTip(node) // Update the state for the best block. Notice how this replaces the // entire struct instead of updating the existing one. This effectively @@ -851,11 +859,12 @@ func (b *BlockChain) connectBlock(node *blockNode, block, parent *dcrutil.Block, // Optimization: Before checkpoints, immediately dump the parent's stake // node because we no longer need it. if node.height < b.chainParams.LatestCheckpointHeight() { - b.bestNode.parent.stakeNode = nil - b.bestNode.parent.stakeUndoData = nil - b.bestNode.parent.newTickets = nil - b.bestNode.parent.ticketsVoted = nil - b.bestNode.parent.ticketsRevoked = nil + parent := b.bestChain.Tip().parent + parent.stakeNode = nil + parent.stakeUndoData = nil + parent.newTickets = nil + parent.ticketsVoted = nil + parent.ticketsRevoked = nil } b.pushMainChainBlockCache(block) @@ -877,10 +886,11 @@ func (b *BlockChain) dropMainChainBlockCache(block *dcrutil.Block) { // This function MUST be called with the chain state lock held (for writes). func (b *BlockChain) disconnectBlock(node *blockNode, block, parent *dcrutil.Block, view *UtxoViewpoint) error { // Make sure the node being disconnected is the end of the best chain. - if node.hash != b.bestNode.hash { + tip := b.bestChain.Tip() + if node.hash != tip.hash { panicf("block %v (height %v) is not the end of the best chain "+ - "(hash %v, height %v)", node.hash, node.height, b.bestNode.hash, - b.bestNode.height) + "(hash %v, height %v)", node.hash, node.height, tip.hash, + tip.height) } // Generate a new best state snapshot that will be used to update the @@ -963,14 +973,8 @@ func (b *BlockChain) disconnectBlock(node *blockNode, block, parent *dcrutil.Blo // now that the modifications have been committed to the database. view.commit() - // Mark block as being in a side chain. - node.inMainChain = false - b.heightLock.Lock() - delete(b.mainNodesByHeight, node.height) - b.heightLock.Unlock() - // This node's parent is now the end of the best chain. - b.bestNode = node.parent + b.bestChain.SetTip(node.parent) // Update the state for the best block. Notice how this replaces the // entire struct instead of updating the existing one. This effectively @@ -1047,12 +1051,13 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error } // Ensure the provided nodes match the current best chain. + tip := b.bestChain.Tip() if detachNodes.Len() != 0 { firstDetachNode := detachNodes.Front().Value.(*blockNode) - if firstDetachNode.hash != b.bestNode.hash { + if firstDetachNode.hash != tip.hash { panicf("reorganize nodes to detach are not for the current best "+ "chain -- first detach node %v, current chain %v", - &firstDetachNode.hash, &b.bestNode.hash) + &firstDetachNode.hash, &tip.hash) } } @@ -1068,8 +1073,8 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error } // Track the old and new best chains heads. - oldBest := b.bestNode - newBest := b.bestNode + oldBest := tip + newBest := tip // All of the blocks to detach and related spend journal entries needed // to unspend transaction outputs in the blocks being disconnected must @@ -1328,7 +1333,7 @@ func (b *BlockChain) forceHeadReorganization(formerBest chainhash.Hash, newBest if formerBest.IsEqual(&newBest) { return fmt.Errorf("can't reorganize to the same block") } - formerBestNode := b.bestNode + formerBestNode := b.bestChain.Tip() // We can't reorganize the chain unless our head block matches up with // b.bestChain. @@ -1351,7 +1356,7 @@ func (b *BlockChain) forceHeadReorganization(formerBest chainhash.Hash, newBest // Check to make sure our forced-in node validates correctly. view := NewUtxoViewpoint() - view.SetBestHash(&b.bestNode.parent.hash) + view.SetBestHash(&formerBestNode.parent.hash) view.SetStakeViewpoint(ViewpointPrevValidInitial) formerBestBlock, err := b.fetchBlockByNode(formerBestNode) @@ -1443,7 +1448,8 @@ func (b *BlockChain) connectBestChain(node *blockNode, block, parent *dcrutil.Bl // We are extending the main (best) chain with a new block. This is the // most common case. parentHash := &block.MsgBlock().Header.PrevBlock - if *parentHash == b.bestNode.hash { + tip := b.bestChain.Tip() + if *parentHash == tip.hash { // Skip expensive checks if the block has already been fully // validated. fastAdd = fastAdd || b.index.NodeStatus(node).KnownValid() @@ -1506,30 +1512,19 @@ func (b *BlockChain) connectBestChain(node *blockNode, block, parent *dcrutil.Bl block.Hash()) } - // We're extending (or creating) a side chain which may or may not - // become the main chain. - node.inMainChain = false - // We're extending (or creating) a side chain, but the cumulative // work for this new side chain is not enough to make it the new chain. - if node.workSum.Cmp(b.bestNode.workSum) <= 0 { - // Find the fork point. - fork := node - for ; fork.parent != nil; fork = fork.parent { - if fork.inMainChain { - break - } - } - + if node.workSum.Cmp(tip.workSum) <= 0 { // Log information about how the block is forking the chain. + fork := b.bestChain.FindFork(node) if fork.hash == *parentHash { log.Infof("FORK: Block %v (height %v) forks the chain at height "+ "%d/block %v, but does not cause a reorganize", node.hash, node.height, fork.height, fork.hash) } else { log.Infof("EXTEND FORK: Block %v (height %v) extends a side chain "+ - "which forks the chain at height %d/block %v", - node.hash, node.height, fork.height, fork.hash) + "which forks the chain at height %d/block %v", node.hash, + node.height, fork.height, fork.hash) } forkLen := node.height - fork.height @@ -1567,8 +1562,9 @@ func (b *BlockChain) connectBestChain(node *blockNode, block, parent *dcrutil.Bl func (b *BlockChain) isCurrent() bool { // Not current if the latest main (best) chain height is before the // latest known good checkpoint (when checkpoints are enabled). + tip := b.bestChain.Tip() checkpoint := b.latestCheckpoint() - if checkpoint != nil && b.bestNode.height < checkpoint.Height { + if checkpoint != nil && tip.height < checkpoint.Height { return false } @@ -1578,7 +1574,7 @@ func (b *BlockChain) isCurrent() bool { // The chain appears to be current if none of the checks reported // otherwise. minus24Hours := b.timeSource.AdjustedTime().Add(-24 * time.Hour).Unix() - return b.bestNode.timestamp >= minus24Hours + return tip.timestamp >= minus24Hours } // IsCurrent returns whether or not the chain believes it is current. Several @@ -1643,7 +1639,7 @@ func (b *BlockChain) maxBlockSize(prevNode *blockNode) (int64, error) { // This function is safe for concurrent access. func (b *BlockChain) MaxBlockSize() (int64, error) { b.chainLock.Lock() - maxSize, err := b.maxBlockSize(b.bestNode) + maxSize, err := b.maxBlockSize(b.bestChain.Tip()) b.chainLock.Unlock() return maxSize, err } @@ -1667,9 +1663,7 @@ func (b *BlockChain) HeaderByHash(hash *chainhash.Hash) (wire.BlockHeader, error // // This function is safe for concurrent access. func (b *BlockChain) HeaderByHeight(height int64) (wire.BlockHeader, error) { - b.heightLock.RLock() - node := b.mainNodesByHeight[height] - b.heightLock.RUnlock() + node := b.bestChain.NodeByHeight(height) if node == nil { str := fmt.Sprintf("no block at height %d exists", height) return wire.BlockHeader{}, errNotInMainChain(str) @@ -1697,9 +1691,8 @@ func (b *BlockChain) BlockByHash(hash *chainhash.Hash) (*dcrutil.Block, error) { // // This function is safe for concurrent access. func (b *BlockChain) BlockByHeight(height int64) (*dcrutil.Block, error) { - b.heightLock.RLock() - node := b.mainNodesByHeight[height] - b.heightLock.RUnlock() + // Lookup the block height in the best chain. + node := b.bestChain.NodeByHeight(height) if node == nil { str := fmt.Sprintf("no block at height %d exists", height) return nil, errNotInMainChain(str) @@ -1717,10 +1710,7 @@ func (b *BlockChain) BlockByHeight(height int64) (*dcrutil.Block, error) { // This function is safe for concurrent access. func (b *BlockChain) MainChainHasBlock(hash *chainhash.Hash) bool { node := b.index.LookupNode(hash) - b.chainLock.RLock() - hasBlock := node != nil && node.inMainChain - b.chainLock.RUnlock() - return hasBlock + return node != nil && b.bestChain.Contains(node) } // BlockHeightByHash returns the height of the block with the given hash in the @@ -1729,13 +1719,10 @@ func (b *BlockChain) MainChainHasBlock(hash *chainhash.Hash) bool { // This function is safe for concurrent access. func (b *BlockChain) BlockHeightByHash(hash *chainhash.Hash) (int64, error) { node := b.index.LookupNode(hash) - b.chainLock.RLock() - if node == nil || !node.inMainChain { - b.chainLock.RUnlock() + if node == nil || !b.bestChain.Contains(node) { str := fmt.Sprintf("block %s is not in the main chain", hash) return 0, errNotInMainChain(str) } - b.chainLock.RUnlock() return node.height, nil } @@ -1745,9 +1732,7 @@ func (b *BlockChain) BlockHeightByHash(hash *chainhash.Hash) (int64, error) { // // This function is safe for concurrent access. func (b *BlockChain) BlockHashByHeight(height int64) (*chainhash.Hash, error) { - b.heightLock.RLock() - node := b.mainNodesByHeight[height] - b.heightLock.RUnlock() + node := b.bestChain.NodeByHeight(height) if node == nil { str := fmt.Sprintf("no block at height %d exists", height) return nil, errNotInMainChain(str) @@ -1776,17 +1761,14 @@ func (b *BlockChain) HeightRange(startHeight, endHeight int64) ([]chainhash.Hash } // There is nothing to do when the start and end heights are the same, - // so return now to avoid the chain lock. + // so return now to avoid extra work. if startHeight == endHeight { return nil, nil } // When the requested start height is after the most recent best chain // height, there is nothing to do. - b.chainLock.RLock() - tip := b.bestNode - b.chainLock.RUnlock() - latestHeight := tip.height + latestHeight := b.bestChain.Tip().height if startHeight > latestHeight { return nil, nil } @@ -1796,11 +1778,9 @@ func (b *BlockChain) HeightRange(startHeight, endHeight int64) ([]chainhash.Hash endHeight = latestHeight + 1 } - // Fetch requested hashes. + // Fetch as many as are available within the specified range. hashes := make([]chainhash.Hash, endHeight-startHeight) - b.heightLock.RLock() - iterNode := b.mainNodesByHeight[endHeight-1] - b.heightLock.RUnlock() + iterNode := b.bestChain.NodeByHeight(endHeight - 1) for i := startHeight; i < endHeight; i++ { // Since the desired result is from the starting node to the // ending node in forward order, but they are iterated in @@ -1843,12 +1823,10 @@ func (b *BlockChain) locateInventory(locator BlockLocator, hashStop *chainhash.H // Find the most recent locator block hash in the main chain. In the // case none of the hashes in the locator are in the main chain, fall // back to the genesis block. - b.heightLock.RLock() - startNode := b.mainNodesByHeight[0] - b.heightLock.RUnlock() + startNode := b.bestChain.Genesis() for _, hash := range locator { node := b.index.LookupNode(hash) - if node != nil && node.inMainChain { + if node != nil && b.bestChain.Contains(node) { startNode = node break } @@ -1857,19 +1835,15 @@ func (b *BlockChain) locateInventory(locator BlockLocator, hashStop *chainhash.H // Start at the block after the most recently known block. When there // is no next block it means the most recently known block is the tip of // the best chain, so there is nothing more to do. - if startNode != nil { - b.heightLock.RLock() - startNode = b.mainNodesByHeight[startNode.height+1] - b.heightLock.RUnlock() - } + startNode = b.bestChain.Next(startNode) if startNode == nil { return nil, 0 } // Calculate how many entries are needed. - total := uint32((b.bestNode.height - startNode.height) + 1) - if stopNode != nil && stopNode.inMainChain && stopNode.height >= - startNode.height { + total := uint32((b.bestChain.Tip().height - startNode.height) + 1) + if stopNode != nil && b.bestChain.Contains(stopNode) && + stopNode.height >= startNode.height { total = uint32((stopNode.height - startNode.height) + 1) } @@ -1898,12 +1872,10 @@ func (b *BlockChain) locateBlocks(locator BlockLocator, hashStop *chainhash.Hash // Populate and return the found hashes. hashes := make([]chainhash.Hash, 0, total) - b.heightLock.RLock() for i := uint32(0); i < total; i++ { hashes = append(hashes, node.hash) - node = b.mainNodesByHeight[node.height+1] + node = b.bestChain.Next(node) } - b.heightLock.RUnlock() return hashes } @@ -1947,9 +1919,7 @@ func (b *BlockChain) locateHeaders(locator BlockLocator, hashStop *chainhash.Has headers := make([]wire.BlockHeader, 0, total) for i := uint32(0); i < total; i++ { headers = append(headers, node.Header()) - b.heightLock.RLock() - node = b.mainNodesByHeight[node.height+1] - b.heightLock.RUnlock() + node = b.bestChain.Next(node) } return headers } @@ -1974,6 +1944,33 @@ func (b *BlockChain) LocateHeaders(locator BlockLocator, hashStop *chainhash.Has return headers } +// BlockLocatorFromHash returns a block locator for the passed block hash. +// See BlockLocator for details on the algorithm used to create a block locator. +// +// In addition to the general algorithm referenced above, this function will +// return the block locator for the latest known tip of the main (best) chain if +// the passed hash is not currently known. +// +// This function is safe for concurrent access. +func (b *BlockChain) BlockLocatorFromHash(hash *chainhash.Hash) BlockLocator { + b.chainLock.RLock() + node := b.index.LookupNode(hash) + locator := b.bestChain.BlockLocator(node) + b.chainLock.RUnlock() + return locator +} + +// LatestBlockLocator returns a block locator for the latest known tip of the +// main (best) chain. +// +// This function is safe for concurrent access. +func (b *BlockChain) LatestBlockLocator() (BlockLocator, error) { + b.chainLock.RLock() + locator := b.bestChain.BlockLocator(nil) + b.chainLock.RUnlock() + return locator, nil +} + // IndexManager provides a generic interface that the is called when blocks are // connected and disconnected to and from the tip of the main chain for the // purpose of supporting optional indexes. @@ -2079,7 +2076,7 @@ func New(config *Config) (*BlockChain, error) { sigCache: config.SigCache, indexManager: config.IndexManager, index: newBlockIndex(config.DB, params), - mainNodesByHeight: make(map[int64]*blockNode), + bestChain: newChainView(nil), orphans: make(map[chainhash.Hash]*orphanBlock), prevOrphans: make(map[chainhash.Hash][]*orphanBlock), mainchainBlockCache: make(map[chainhash.Hash]*dcrutil.Block), @@ -2108,7 +2105,8 @@ func New(config *Config) (*BlockChain, error) { } } - b.subsidyCache = NewSubsidyCache(b.bestNode.height, b.chainParams) + tip := b.bestChain.Tip() + b.subsidyCache = NewSubsidyCache(tip.height, b.chainParams) b.pruner = newChainPruner(&b) log.Infof("Blockchain database version info: chain: %d, compression: "+ @@ -2116,9 +2114,8 @@ func New(config *Config) (*BlockChain, error) { b.dbInfo.bidxVer) log.Infof("Chain state: height %d, hash %v, total transactions %d, "+ - "work %v, stake version %v", b.bestNode.height, b.bestNode.hash, - b.stateSnapshot.TotalTxns, b.bestNode.workSum, - 0) + "work %v, stake version %v", tip.height, tip.hash, + b.stateSnapshot.TotalTxns, tip.workSum, 0) return &b, nil } diff --git a/blockchain/chain_test.go b/blockchain/chain_test.go index 6b7269fe..dd0b9090 100644 --- a/blockchain/chain_test.go +++ b/blockchain/chain_test.go @@ -643,32 +643,25 @@ func TestLocateInventory(t *testing.T) { // \-> 16a -> 17a tip := branchTip chain := newFakeChain(&chaincfg.MainNetParams) - branch0Nodes := chainedFakeNodes(chain.bestNode, 18) + branch0Nodes := chainedFakeNodes(chain.bestChain.Genesis(), 18) branch1Nodes := chainedFakeNodes(branch0Nodes[14], 2) for _, node := range branch0Nodes { chain.index.AddNode(node) - node.inMainChain = true - chain.mainNodesByHeight[node.height] = node } for _, node := range branch1Nodes { chain.index.AddNode(node) - node.inMainChain = false } - chain.bestNode = tip(branch0Nodes) + chain.bestChain.SetTip(tip(branch0Nodes)) - // NOTE: These tests simulate a local and remote node on different parts of - // the chain by treating the branch0Nodes as the local node and the - // branch1Nodes as the remote node. + // Create chain views for different branches of the overall chain to + // simulate a local and remote node on different parts of the chain. + localView := newChainView(tip(branch0Nodes)) + remoteView := newChainView(tip(branch1Nodes)) - // Create a completely unrelated block chain to simulate a remote node on a - // totally different chain. - unrelatedChain := newFakeChain(&chaincfg.MainNetParams) - unrelatedBranchNodes := chainedFakeNodes(unrelatedChain.bestNode, 5) - for _, node := range unrelatedBranchNodes { - unrelatedChain.index.AddNode(node) - node.inMainChain = true - unrelatedChain.mainNodesByHeight[node.height] = node - } + // Create a chain view for a completely unrelated block chain to + // simulate a remote node on a totally different chain. + unrelatedBranchNodes := chainedFakeNodes(nil, 5) + unrelatedView := newChainView(tip(unrelatedBranchNodes)) tests := []struct { name string @@ -711,7 +704,7 @@ func TestLocateInventory(t *testing.T) { // expected result is the blocks after the fork point in // the main chain and the stop hash has no effect. name: "remote side chain, unknown stop", - locator: blockLocator(tip(branch1Nodes)), + locator: remoteView.BlockLocator(nil), hashStop: chainhash.Hash{0x01}, headers: nodeHeaders(branch0Nodes, 15, 16, 17), hashes: nodeHashes(branch0Nodes, 15, 16, 17), @@ -722,7 +715,7 @@ func TestLocateInventory(t *testing.T) { // blocks after the fork point in the main chain and the // stop hash has no effect. name: "remote side chain, stop in side", - locator: blockLocator(tip(branch1Nodes)), + locator: remoteView.BlockLocator(nil), hashStop: tip(branch1Nodes).hash, headers: nodeHeaders(branch0Nodes, 15, 16, 17), hashes: nodeHashes(branch0Nodes, 15, 16, 17), @@ -733,7 +726,7 @@ func TestLocateInventory(t *testing.T) { // expected result is the blocks after the fork point in // the main chain and the stop hash has no effect. name: "remote side chain, stop in main before", - locator: blockLocator(tip(branch1Nodes)), + locator: remoteView.BlockLocator(nil), hashStop: branch0Nodes[13].hash, headers: nodeHeaders(branch0Nodes, 15, 16, 17), hashes: nodeHashes(branch0Nodes, 15, 16, 17), @@ -745,7 +738,7 @@ func TestLocateInventory(t *testing.T) { // fork point in the main chain and the stop hash has no // effect. name: "remote side chain, stop in main exact", - locator: blockLocator(tip(branch1Nodes)), + locator: remoteView.BlockLocator(nil), hashStop: branch0Nodes[14].hash, headers: nodeHeaders(branch0Nodes, 15, 16, 17), hashes: nodeHashes(branch0Nodes, 15, 16, 17), @@ -757,7 +750,7 @@ func TestLocateInventory(t *testing.T) { // point in the main chain up to and including the stop // hash. name: "remote side chain, stop in main after", - locator: blockLocator(tip(branch1Nodes)), + locator: remoteView.BlockLocator(nil), hashStop: branch0Nodes[15].hash, headers: nodeHeaders(branch0Nodes, 15), hashes: nodeHashes(branch0Nodes, 15), @@ -769,7 +762,7 @@ func TestLocateInventory(t *testing.T) { // fork point in the main chain up to and including the // stop hash. name: "remote side chain, stop in main after more", - locator: blockLocator(tip(branch1Nodes)), + locator: remoteView.BlockLocator(nil), hashStop: branch0Nodes[16].hash, headers: nodeHeaders(branch0Nodes, 15, 16), hashes: nodeHashes(branch0Nodes, 15, 16), @@ -781,7 +774,7 @@ func TestLocateInventory(t *testing.T) { // point in the main chain and the stop hash has no // effect. name: "remote main chain past, unknown stop", - locator: blockLocator(branch0Nodes[12]), + locator: localView.BlockLocator(branch0Nodes[12]), hashStop: chainhash.Hash{0x01}, headers: nodeHeaders(branch0Nodes, 13, 14, 15, 16, 17), hashes: nodeHashes(branch0Nodes, 13, 14, 15, 16, 17), @@ -792,7 +785,7 @@ func TestLocateInventory(t *testing.T) { // result is the blocks after the known point in the // main chain and the stop hash has no effect. name: "remote main chain past, stop in side", - locator: blockLocator(branch0Nodes[12]), + locator: localView.BlockLocator(branch0Nodes[12]), hashStop: tip(branch1Nodes).hash, headers: nodeHeaders(branch0Nodes, 13, 14, 15, 16, 17), hashes: nodeHashes(branch0Nodes, 13, 14, 15, 16, 17), @@ -804,7 +797,7 @@ func TestLocateInventory(t *testing.T) { // known point in the main chain and the stop hash has // no effect. name: "remote main chain past, stop in main before", - locator: blockLocator(branch0Nodes[12]), + locator: localView.BlockLocator(branch0Nodes[12]), hashStop: branch0Nodes[11].hash, headers: nodeHeaders(branch0Nodes, 13, 14, 15, 16, 17), hashes: nodeHashes(branch0Nodes, 13, 14, 15, 16, 17), @@ -816,7 +809,7 @@ func TestLocateInventory(t *testing.T) { // known point in the main chain and the stop hash has // no effect. name: "remote main chain past, stop in main exact", - locator: blockLocator(branch0Nodes[12]), + locator: localView.BlockLocator(branch0Nodes[12]), hashStop: branch0Nodes[12].hash, headers: nodeHeaders(branch0Nodes, 13, 14, 15, 16, 17), hashes: nodeHashes(branch0Nodes, 13, 14, 15, 16, 17), @@ -828,7 +821,7 @@ func TestLocateInventory(t *testing.T) { // the known point in the main chain and the stop hash // has no effect. name: "remote main chain past, stop in main after", - locator: blockLocator(branch0Nodes[12]), + locator: localView.BlockLocator(branch0Nodes[12]), hashStop: branch0Nodes[13].hash, headers: nodeHeaders(branch0Nodes, 13), hashes: nodeHashes(branch0Nodes, 13), @@ -840,7 +833,7 @@ func TestLocateInventory(t *testing.T) { // after the known point in the main chain and the stop // hash has no effect. name: "remote main chain past, stop in main after more", - locator: blockLocator(branch0Nodes[12]), + locator: localView.BlockLocator(branch0Nodes[12]), hashStop: branch0Nodes[15].hash, headers: nodeHeaders(branch0Nodes, 13, 14, 15), hashes: nodeHashes(branch0Nodes, 13, 14, 15), @@ -851,7 +844,7 @@ func TestLocateInventory(t *testing.T) { // doesn't know about. The expected result is no // located inventory. name: "remote main chain same, unknown stop", - locator: blockLocator(tip(branch0Nodes)), + locator: localView.BlockLocator(nil), hashStop: chainhash.Hash{0x01}, headers: nil, hashes: nil, @@ -862,7 +855,7 @@ func TestLocateInventory(t *testing.T) { // the same point. The expected result is no located // inventory. name: "remote main chain same, stop same point", - locator: blockLocator(tip(branch0Nodes)), + locator: localView.BlockLocator(nil), hashStop: tip(branch0Nodes).hash, headers: nil, hashes: nil, @@ -875,7 +868,7 @@ func TestLocateInventory(t *testing.T) { // expected result is the blocks after the genesis // block. name: "remote unrelated chain", - locator: blockLocator(tip(unrelatedBranchNodes)), + locator: unrelatedView.BlockLocator(nil), hashStop: chainhash.Hash{}, headers: nodeHeaders(branch0Nodes, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17), diff --git a/blockchain/chainio.go b/blockchain/chainio.go index b27348f0..a1183d75 100644 --- a/blockchain/chainio.go +++ b/blockchain/chainio.go @@ -1458,7 +1458,6 @@ func (b *BlockChain) createChainState() error { header := &genesisBlock.MsgBlock().Header node := newBlockNode(header, nil) node.status = statusDataStored | statusValid - node.inMainChain = true // Initialize the state related to the best block. Since it is the // genesis block, use its timestamp for the median time. @@ -1721,14 +1720,7 @@ func (b *BlockChain) initChainState(interrupt <-chan struct{}) error { return AssertError(fmt.Sprintf("initChainState: cannot find "+ "chain tip %s in block index", state.hash)) } - b.bestNode = tip - - // Mark all of the nodes from the tip back to the genesis block - // as part of the main chain and build the by height map. - for n := tip; n != nil; n = n.parent { - n.inMainChain = true - b.mainNodesByHeight[n.height] = n - } + b.bestChain.SetTip(tip) log.Debugf("Block index loaded in %v", time.Since(bidxStart)) diff --git a/blockchain/chainquery.go b/blockchain/chainquery.go index 709fc71e..335155da 100644 --- a/blockchain/chainquery.go +++ b/blockchain/chainquery.go @@ -51,19 +51,12 @@ func (b *BlockChain) ChainTips() []dcrjson.GetChainTipsResult { // Generate the results sorted by descending height. sort.Sort(sort.Reverse(nodeHeightSorter(chainTips))) results := make([]dcrjson.GetChainTipsResult, len(chainTips)) - b.chainLock.RLock() - bestTip := b.bestNode + bestTip := b.bestChain.Tip() for i, tip := range chainTips { - // Find the fork point in order calculate the branch length later. - fork := tip - for fork != nil && !fork.inMainChain { - fork = fork.parent - } - result := &results[i] result.Height = tip.height result.Hash = tip.hash.String() - result.BranchLen = tip.height - fork.height + result.BranchLen = tip.height - b.bestChain.FindFork(tip).height // Determine the status of the chain tip. // @@ -99,6 +92,5 @@ func (b *BlockChain) ChainTips() []dcrjson.GetChainTipsResult { result.Status = "valid-headers" } } - b.chainLock.RUnlock() return results } diff --git a/blockchain/chainview.go b/blockchain/chainview.go index 2ae006be..ce730ba0 100644 --- a/blockchain/chainview.go +++ b/blockchain/chainview.go @@ -13,6 +13,25 @@ import ( // in a week on average. const approxNodesPerWeek = 12 * 24 * 7 +// log2FloorMasks defines the masks to use when quickly calculating +// floor(log2(x)) in a constant log2(32) = 5 steps, where x is a uint32, using +// shifts. They are derived from (2^(2^x) - 1) * (2^(2^x)), for x in 4..0. +var log2FloorMasks = []uint32{0xffff0000, 0xff00, 0xf0, 0xc, 0x2} + +// fastLog2Floor calculates and returns floor(log2(x)) in a constant 5 steps. +func fastLog2Floor(n uint32) uint8 { + rv := uint8(0) + exponent := uint8(16) + for i := 0; i < 5; i++ { + if n&log2FloorMasks[i] != 0 { + rv += exponent + n >>= exponent + } + exponent >>= 1 + } + return rv +} + // chainView provides a flat view of a specific branch of the block chain from // its tip back to the genesis block and provides various convenience functions // for comparing chains. diff --git a/blockchain/checkpoints.go b/blockchain/checkpoints.go index ed10fec3..97cce475 100644 --- a/blockchain/checkpoints.go +++ b/blockchain/checkpoints.go @@ -118,7 +118,7 @@ func (b *BlockChain) findPreviousCheckpoint() (*blockNode, error) { // that is already available. for i := numCheckpoints - 1; i >= 0; i-- { node := b.index.LookupNode(checkpoints[i].Hash) - if node == nil || !node.inMainChain { + if node == nil || !b.bestChain.Contains(node) { continue } @@ -149,7 +149,7 @@ func (b *BlockChain) findPreviousCheckpoint() (*blockNode, error) { // When there is a next checkpoint and the height of the current best // chain does not exceed it, the current checkpoint lockin is still // the latest known checkpoint. - if b.bestNode.height < b.nextCheckpoint.Height { + if b.bestChain.Tip().height < b.nextCheckpoint.Height { return b.checkpointNode, nil } @@ -227,7 +227,7 @@ func (b *BlockChain) IsCheckpointCandidate(block *dcrutil.Block) (bool, error) { // A checkpoint must be in the main chain. node := b.index.LookupNode(block.Hash()) - if node == nil || !node.inMainChain { + if node == nil || !b.bestChain.Contains(node) { return false, nil } @@ -242,8 +242,7 @@ func (b *BlockChain) IsCheckpointCandidate(block *dcrutil.Block) (bool, error) { // A checkpoint must be at least CheckpointConfirmations blocks before // the end of the main chain. - tip := b.bestNode - if node.height > (tip.height - CheckpointConfirmations) { + if node.height > (b.bestChain.Tip().height - CheckpointConfirmations) { return false, nil } @@ -252,9 +251,7 @@ func (b *BlockChain) IsCheckpointCandidate(block *dcrutil.Block) (bool, error) { // This should always succeed since the check above already made sure it // is CheckpointConfirmations back, but be safe in case the constant // changes. - b.heightLock.RLock() - nextNode := b.mainNodesByHeight[node.height+1] - b.heightLock.RUnlock() + nextNode := b.bestChain.Next(node) if nextNode == nil { return false, nil } @@ -274,8 +271,8 @@ func (b *BlockChain) IsCheckpointCandidate(block *dcrutil.Block) (bool, error) { return false, nil } - // A checkpoint must have transactions that only contain - // standard scripts. + // A checkpoint must have transactions that only contain standard + // scripts. for _, tx := range block.Transactions() { if isNonstandardTransaction(tx) { return false, nil diff --git a/blockchain/common_test.go b/blockchain/common_test.go index 636ac812..acc4e67c 100644 --- a/blockchain/common_test.go +++ b/blockchain/common_test.go @@ -136,18 +136,14 @@ func newFakeChain(params *chaincfg.Params) *BlockChain { // Create a genesis block node and block index populated with it for use // when creating the fake chain below. node := newBlockNode(¶ms.GenesisBlock.Header, nil) - node.inMainChain = true index := newBlockIndex(nil, params) index.AddNode(node) - mainNodesByHeight := make(map[int64]*blockNode) - mainNodesByHeight[node.height] = node return &BlockChain{ chainParams: params, deploymentCaches: newThresholdCaches(params), - bestNode: node, index: index, - mainNodesByHeight: mainNodesByHeight, + bestChain: newChainView(node), isVoterMajorityVersionCache: make(map[[stakeMajorityCacheKeySize]byte]bool), isStakeMajorityVersionCache: make(map[[stakeMajorityCacheKeySize]byte]bool), calcPriorStakeVersionCache: make(map[[chainhash.HashSize]byte]uint32), diff --git a/blockchain/difficulty.go b/blockchain/difficulty.go index 71134940..ecc2ae0e 100644 --- a/blockchain/difficulty.go +++ b/blockchain/difficulty.go @@ -426,7 +426,7 @@ func (b *BlockChain) CalcNextRequiredDiffFromNode(hash *chainhash.Hash, timestam // This function is safe for concurrent access. func (b *BlockChain) CalcNextRequiredDifficulty(timestamp time.Time) (uint32, error) { b.chainLock.Lock() - difficulty, err := b.calcNextRequiredDifficulty(b.bestNode, timestamp) + difficulty, err := b.calcNextRequiredDifficulty(b.bestChain.Tip(), timestamp) b.chainLock.Unlock() return difficulty, err } @@ -936,7 +936,7 @@ func (b *BlockChain) calcNextRequiredStakeDifficulty(curNode *blockNode) (int64, // This function is safe for concurrent access. func (b *BlockChain) CalcNextRequiredStakeDifficulty() (int64, error) { b.chainLock.Lock() - nextDiff, err := b.calcNextRequiredStakeDifficulty(b.bestNode) + nextDiff, err := b.calcNextRequiredStakeDifficulty(b.bestChain.Tip()) b.chainLock.Unlock() return nextDiff, err } @@ -1423,8 +1423,8 @@ func (b *BlockChain) estimateNextStakeDifficulty(curNode *blockNode, newTickets // This function is safe for concurrent access. func (b *BlockChain) EstimateNextStakeDifficulty(newTickets int64, useMaxTickets bool) (int64, error) { b.chainLock.Lock() - estimate, err := b.estimateNextStakeDifficulty(b.bestNode, newTickets, - useMaxTickets) + estimate, err := b.estimateNextStakeDifficulty(b.bestChain.Tip(), + newTickets, useMaxTickets) b.chainLock.Unlock() return estimate, err } diff --git a/blockchain/difficulty_test.go b/blockchain/difficulty_test.go index ca664ae2..a272c624 100644 --- a/blockchain/difficulty_test.go +++ b/blockchain/difficulty_test.go @@ -435,7 +435,8 @@ nextTest: for _, ticketInfo := range test.ticketInfo { // Ensure the test data isn't faking ticket purchases at // an incorrect difficulty. - gotDiff, err := bc.calcNextRequiredStakeDifficultyV2(bc.bestNode) + tip := bc.bestChain.Tip() + gotDiff, err := bc.calcNextRequiredStakeDifficultyV2(tip) if err != nil { t.Errorf("calcNextRequiredStakeDifficultyV2 (%s): "+ "unexpected error: %v", test.name, err) @@ -451,7 +452,7 @@ nextTest: for i := uint32(0); i < ticketInfo.numNodes; i++ { // Make up a header. - nextHeight := uint32(bc.bestNode.height) + 1 + nextHeight := uint32(tip.height) + 1 header := &wire.BlockHeader{ Version: 4, SBits: ticketInfo.stakeDiff, @@ -459,7 +460,7 @@ nextTest: FreshStake: ticketInfo.newTickets, PoolSize: poolSize, } - node := newBlockNode(header, bc.bestNode) + tip = newBlockNode(header, tip) // Update the pool size for the next header. // Notice how tickets that mature for this block @@ -478,12 +479,12 @@ nextTest: // Update the chain to use the new fake node as // the new best node. - bc.bestNode = node + bc.bestChain.SetTip(tip) } } // Ensure the calculated difficulty matches the expected value. - gotDiff, err := bc.calcNextRequiredStakeDifficultyV2(bc.bestNode) + gotDiff, err := bc.calcNextRequiredStakeDifficultyV2(bc.bestChain.Tip()) if err != nil { t.Errorf("calcNextRequiredStakeDifficultyV2 (%s): "+ "unexpected error: %v", test.name, err) @@ -985,7 +986,8 @@ nextTest: for _, ticketInfo := range test.ticketInfo { // Ensure the test data isn't faking ticket purchases at // an incorrect difficulty. - reqDiff, err := bc.calcNextRequiredStakeDifficultyV2(bc.bestNode) + tip := bc.bestChain.Tip() + reqDiff, err := bc.calcNextRequiredStakeDifficultyV2(tip) if err != nil { t.Errorf("calcNextRequiredStakeDifficultyV2 (%s): "+ "unexpected error: %v", test.name, err) @@ -1001,7 +1003,7 @@ nextTest: for i := uint32(0); i < ticketInfo.numNodes; i++ { // Make up a header. - nextHeight := uint32(bc.bestNode.height) + 1 + nextHeight := uint32(tip.height) + 1 header := &wire.BlockHeader{ Version: 4, SBits: ticketInfo.stakeDiff, @@ -1009,7 +1011,7 @@ nextTest: FreshStake: ticketInfo.newTickets, PoolSize: poolSize, } - node := newBlockNode(header, bc.bestNode) + tip = newBlockNode(header, tip) // Update the pool size for the next header. // Notice how tickets that mature for this block @@ -1028,12 +1030,12 @@ nextTest: // Update the chain to use the new fake node as // the new best node. - bc.bestNode = node + bc.bestChain.SetTip(tip) } } // Ensure the calculated difficulty matches the expected value. - gotDiff, err := bc.estimateNextStakeDifficultyV2(bc.bestNode, + gotDiff, err := bc.estimateNextStakeDifficultyV2(bc.bestChain.Tip(), test.newTickets, test.useMaxTickets) if err != nil { t.Errorf("estimateNextStakeDifficultyV2 (%s): "+ diff --git a/blockchain/sequencelock.go b/blockchain/sequencelock.go index 3a2ffa62..a62deaa9 100644 --- a/blockchain/sequencelock.go +++ b/blockchain/sequencelock.go @@ -150,7 +150,7 @@ func (b *BlockChain) calcSequenceLock(node *blockNode, tx *dcrutil.Tx, view *Utx // This function is safe for concurrent access. func (b *BlockChain) CalcSequenceLock(tx *dcrutil.Tx, view *UtxoViewpoint) (*SequenceLock, error) { b.chainLock.Lock() - seqLock, err := b.calcSequenceLock(b.bestNode, tx, view, true) + seqLock, err := b.calcSequenceLock(b.bestChain.Tip(), tx, view, true) b.chainLock.Unlock() return seqLock, err } diff --git a/blockchain/sequencelock_test.go b/blockchain/sequencelock_test.go index b3887e9b..56d0e356 100644 --- a/blockchain/sequencelock_test.go +++ b/blockchain/sequencelock_test.go @@ -36,13 +36,13 @@ func TestCalcSequenceLock(t *testing.T) { numBlocks := uint32(20) params := &chaincfg.SimNetParams bc := newFakeChain(params) - node := bc.bestNode + node := bc.bestChain.Tip() blockTime := time.Unix(node.timestamp, 0) for i := uint32(0); i < numBlocks; i++ { blockTime = blockTime.Add(time.Second) node = newFakeNode(node, 1, 1, 0, blockTime) bc.index.AddNode(node) - bc.bestNode = node + bc.bestChain.SetTip(node) } // Create a utxo view with a fake utxo for the inputs used in the diff --git a/blockchain/stakeext.go b/blockchain/stakeext.go index 167f9c99..769a84eb 100644 --- a/blockchain/stakeext.go +++ b/blockchain/stakeext.go @@ -23,8 +23,9 @@ func (b *BlockChain) NextLotteryData() ([]chainhash.Hash, int, [6]byte, error) { b.chainLock.RLock() defer b.chainLock.RUnlock() - return b.bestNode.stakeNode.Winners(), b.bestNode.stakeNode.PoolSize(), - b.bestNode.stakeNode.FinalState(), nil + tipStakeNode := b.bestChain.Tip().stakeNode + return tipStakeNode.Winners(), tipStakeNode.PoolSize(), + tipStakeNode.FinalState(), nil } // lotteryDataForNode is a helper function that returns winning tickets @@ -41,8 +42,7 @@ func (b *BlockChain) lotteryDataForNode(node *blockNode) ([]chainhash.Hash, int, return []chainhash.Hash{}, 0, [6]byte{}, err } - return stakeNode.Winners(), b.bestNode.stakeNode.PoolSize(), - b.bestNode.stakeNode.FinalState(), nil + return stakeNode.Winners(), stakeNode.PoolSize(), stakeNode.FinalState(), nil } // lotteryDataForBlock takes a node block hash and returns the next tickets @@ -85,7 +85,7 @@ func (b *BlockChain) LotteryDataForBlock(hash *chainhash.Hash) ([]chainhash.Hash // This function is NOT safe for concurrent access. func (b *BlockChain) LiveTickets() ([]chainhash.Hash, error) { b.chainLock.RLock() - sn := b.bestNode.stakeNode + sn := b.bestChain.Tip().stakeNode b.chainLock.RUnlock() return sn.LiveTickets(), nil @@ -96,7 +96,7 @@ func (b *BlockChain) LiveTickets() ([]chainhash.Hash, error) { // This function is NOT safe for concurrent access. func (b *BlockChain) MissedTickets() ([]chainhash.Hash, error) { b.chainLock.RLock() - sn := b.bestNode.stakeNode + sn := b.bestChain.Tip().stakeNode b.chainLock.RUnlock() return sn.MissedTickets(), nil @@ -108,7 +108,7 @@ func (b *BlockChain) MissedTickets() ([]chainhash.Hash, error) { // This function is safe for concurrent access. func (b *BlockChain) TicketsWithAddress(address dcrutil.Address) ([]chainhash.Hash, error) { b.chainLock.RLock() - sn := b.bestNode.stakeNode + sn := b.bestChain.Tip().stakeNode b.chainLock.RUnlock() tickets := sn.LiveTickets() @@ -146,7 +146,7 @@ func (b *BlockChain) TicketsWithAddress(address dcrutil.Address) ([]chainhash.Ha // This function is safe for concurrent access. func (b *BlockChain) CheckLiveTicket(hash chainhash.Hash) bool { b.chainLock.RLock() - sn := b.bestNode.stakeNode + sn := b.bestChain.Tip().stakeNode b.chainLock.RUnlock() return sn.ExistsLiveTicket(hash) @@ -158,7 +158,7 @@ func (b *BlockChain) CheckLiveTicket(hash chainhash.Hash) bool { // This function is safe for concurrent access. func (b *BlockChain) CheckLiveTickets(hashes []chainhash.Hash) []bool { b.chainLock.RLock() - sn := b.bestNode.stakeNode + sn := b.bestChain.Tip().stakeNode b.chainLock.RUnlock() existsSlice := make([]bool, len(hashes)) @@ -175,7 +175,7 @@ func (b *BlockChain) CheckLiveTickets(hashes []chainhash.Hash) []bool { // This function is safe for concurrent access. func (b *BlockChain) CheckMissedTickets(hashes []chainhash.Hash) []bool { b.chainLock.RLock() - sn := b.bestNode.stakeNode + sn := b.bestChain.Tip().stakeNode b.chainLock.RUnlock() existsSlice := make([]bool, len(hashes)) @@ -191,7 +191,7 @@ func (b *BlockChain) CheckMissedTickets(hashes []chainhash.Hash) []bool { // This function is safe for concurrent access. func (b *BlockChain) CheckExpiredTicket(hash chainhash.Hash) bool { b.chainLock.RLock() - sn := b.bestNode.stakeNode + sn := b.bestChain.Tip().stakeNode b.chainLock.RUnlock() return sn.ExistsExpiredTicket(hash) @@ -203,7 +203,7 @@ func (b *BlockChain) CheckExpiredTicket(hash chainhash.Hash) bool { // This function is safe for concurrent access. func (b *BlockChain) CheckExpiredTickets(hashes []chainhash.Hash) []bool { b.chainLock.RLock() - sn := b.bestNode.stakeNode + sn := b.bestChain.Tip().stakeNode b.chainLock.RUnlock() existsSlice := make([]bool, len(hashes)) @@ -222,7 +222,7 @@ func (b *BlockChain) CheckExpiredTickets(hashes []chainhash.Hash) []bool { // the asked for transactions. func (b *BlockChain) TicketPoolValue() (dcrutil.Amount, error) { b.chainLock.RLock() - sn := b.bestNode.stakeNode + sn := b.bestChain.Tip().stakeNode b.chainLock.RUnlock() var amt int64 diff --git a/blockchain/stakenode.go b/blockchain/stakenode.go index 1703b2fc..6640d248 100644 --- a/blockchain/stakenode.go +++ b/blockchain/stakenode.go @@ -106,7 +106,7 @@ func (b *BlockChain) fetchStakeNode(node *blockNode) (*stake.Node, error) { // always be filled in, so assume it is safe to begin working // backwards from there. detachNodes, attachNodes := b.getReorganizeNodes(node) - current := b.bestNode + current := b.bestChain.Tip() // Move backwards through the main chain, undoing the ticket // treaps for each block. The database is passed because the diff --git a/blockchain/stakeversion_test.go b/blockchain/stakeversion_test.go index 776838d1..02f95698 100644 --- a/blockchain/stakeversion_test.go +++ b/blockchain/stakeversion_test.go @@ -99,10 +99,10 @@ func TestCalcStakeVersionCorners(t *testing.T) { // Generate enough nodes to reach stake validation height with stake // versions set to 0. bc := newFakeChain(params) - node := bc.bestNode + node := bc.bestChain.Tip() for i := int64(1); i <= svh; i++ { node = newFakeNode(node, 0, 0, 0, time.Now()) - bc.bestNode = node + bc.bestChain.SetTip(node) } if node.height != svh { t.Fatalf("invalid height got %v expected %v", node.height, svh) @@ -115,7 +115,7 @@ func TestCalcStakeVersionCorners(t *testing.T) { // Set vote and stake versions. node = newFakeNode(node, 3, sv, 0, time.Now()) appendFakeVotes(node, params.TicketsPerBlock, 2, 0) - bc.bestNode = node + bc.bestChain.SetTip(node) } // Versions 0 and 2 should now be considered the majority version, but @@ -137,7 +137,7 @@ func TestCalcStakeVersionCorners(t *testing.T) { // Set vote and stake versions. node = newFakeNode(node, 3, sv, 0, time.Now()) appendFakeVotes(node, params.TicketsPerBlock, 4, 0) - bc.bestNode = node + bc.bestChain.SetTip(node) } // Versions up to and including v4 should now be considered the majority @@ -160,7 +160,7 @@ func TestCalcStakeVersionCorners(t *testing.T) { // Set vote and stake versions. node = newFakeNode(node, 3, sv, 0, time.Now()) appendFakeVotes(node, params.TicketsPerBlock, 2, 0) - bc.bestNode = node + bc.bestChain.SetTip(node) } // Versions up to and including v4 should still be considered the @@ -185,7 +185,7 @@ func TestCalcStakeVersionCorners(t *testing.T) { // Set vote and stake versions. node = newFakeNode(node, 3, sv, 0, time.Now()) appendFakeVotes(node, params.TicketsPerBlock, 5, 0) - bc.bestNode = node + bc.bestChain.SetTip(node) } // Versions up to and including v5 should now be considered the majority @@ -208,7 +208,7 @@ func TestCalcStakeVersionCorners(t *testing.T) { // Set vote and stake versions. node = newFakeNode(node, 3, sv, 0, time.Now()) appendFakeVotes(node, params.TicketsPerBlock, 4, 0) - bc.bestNode = node + bc.bestChain.SetTip(node) } @@ -234,7 +234,7 @@ func TestCalcStakeVersionCorners(t *testing.T) { // Set stake versions. node = newFakeNode(node, 3, sv, 0, time.Now()) appendFakeVotes(node, params.TicketsPerBlock, 4, 0) - bc.bestNode = node + bc.bestChain.SetTip(node) } @@ -296,15 +296,15 @@ func TestCalcStakeVersion(t *testing.T) { for _, test := range tests { bc := newFakeChain(params) - node := bc.bestNode + node := bc.bestChain.Tip() for i := int64(1); i <= test.numNodes; i++ { node = newFakeNode(node, 1, 0, 0, time.Now()) test.set(node) - bc.bestNode = node + bc.bestChain.SetTip(node) } - version := bc.calcStakeVersion(bc.bestNode) + version := bc.calcStakeVersion(bc.bestChain.Tip()) if version != test.expectVersion { t.Fatalf("version mismatch: got %v expected %v", version, test.expectVersion) @@ -625,7 +625,7 @@ func TestIsStakeMajorityVersion(t *testing.T) { for _, test := range tests { // Create new BlockChain in order to blow away cache. bc := newFakeChain(params) - node := bc.bestNode + node := bc.bestChain.Tip() node.stakeVersion = test.startStakeVersion ticketCount = 0 @@ -642,7 +642,7 @@ func TestIsStakeMajorityVersion(t *testing.T) { test.startStakeVersion, 0) } - bc.bestNode = node + bc.bestChain.SetTip(node) } res := bc.isVoterMajorityVersion(test.expectedStakeVersion, node) @@ -695,7 +695,7 @@ func TestLarge(t *testing.T) { for _, test := range tests { // Create new BlockChain in order to blow away cache. bc := newFakeChain(params) - node := bc.bestNode + node := bc.bestChain.Tip() node.stakeVersion = test.startStakeVersion for i := int64(1); i <= test.numNodes; i++ { @@ -705,7 +705,7 @@ func TestLarge(t *testing.T) { // Override version. appendFakeVotes(node, params.TicketsPerBlock, test.startStakeVersion, 0) - bc.bestNode = node + bc.bestChain.SetTip(node) } for x := 0; x < numRuns; x++ { diff --git a/blockchain/thresholdstate.go b/blockchain/thresholdstate.go index 9657d504..965abb4f 100644 --- a/blockchain/thresholdstate.go +++ b/blockchain/thresholdstate.go @@ -523,7 +523,7 @@ func (b *BlockChain) isLNFeaturesAgendaActive(prevNode *blockNode) (bool, error) // This function is safe for concurrent access. func (b *BlockChain) IsLNFeaturesAgendaActive() (bool, error) { b.chainLock.Lock() - isActive, err := b.isLNFeaturesAgendaActive(b.bestNode) + isActive, err := b.isLNFeaturesAgendaActive(b.bestChain.Tip()) b.chainLock.Unlock() return isActive, err } @@ -593,7 +593,7 @@ func (b *BlockChain) GetVoteCounts(version uint32, deploymentID string) (VoteCou deployment := &b.chainParams.Deployments[version][k] if deployment.Vote.Id == deploymentID { b.chainLock.Lock() - counts, err := b.getVoteCounts(b.bestNode, version, deployment) + counts, err := b.getVoteCounts(b.bestChain.Tip(), version, deployment) b.chainLock.Unlock() return counts, err } @@ -608,7 +608,7 @@ func (b *BlockChain) GetVoteCounts(version uint32, deploymentID string) (VoteCou func (b *BlockChain) CountVoteVersion(version uint32) (uint32, error) { b.chainLock.Lock() defer b.chainLock.Unlock() - countNode := b.bestNode + countNode := b.bestChain.Tip() // Don't try to count votes before the stake validation height since there // could not possibly have been any. diff --git a/blockchain/utxoviewpoint.go b/blockchain/utxoviewpoint.go index 88549c0f..25360ed9 100644 --- a/blockchain/utxoviewpoint.go +++ b/blockchain/utxoviewpoint.go @@ -1084,7 +1084,7 @@ func (b *BlockChain) FetchUtxoView(tx *dcrutil.Tx, treeValid bool) (*UtxoViewpoi // can't possibly be any details about it. This is also necessary // because the code below requires the parent block and the genesis // block doesn't have one. - tip := b.bestNode + tip := b.bestChain.Tip() view := NewUtxoViewpoint() if tip.height == 0 { view.SetBestHash(&tip.hash) diff --git a/blockchain/validate.go b/blockchain/validate.go index 41e50880..e34fc23f 100644 --- a/blockchain/validate.go +++ b/blockchain/validate.go @@ -2564,7 +2564,7 @@ func (b *BlockChain) CheckConnectBlockTemplate(block *dcrutil.Block) error { // The block template must build off the current tip of the main chain // or its parent. - tip := b.bestNode + tip := b.bestChain.Tip() var prevNode *blockNode parentHash := block.MsgBlock().Header.PrevBlock if parentHash == tip.hash { diff --git a/blockchain/validate_test.go b/blockchain/validate_test.go index daf54e11..af7544e4 100644 --- a/blockchain/validate_test.go +++ b/blockchain/validate_test.go @@ -87,7 +87,7 @@ func TestBlockchainSpendJournal(t *testing.T) { // Loop through all of the blocks and ensure the number of spent outputs // matches up with the information loaded from the spend journal. err = chain.db.View(func(dbTx database.Tx) error { - parentNode := chain.bestNode.parent + parentNode := chain.bestChain.NodeByHeight(1) if parentNode == nil { str := fmt.Sprintf("no block at height %d exists", 1) return errNotInMainChain(str) @@ -97,8 +97,8 @@ func TestBlockchainSpendJournal(t *testing.T) { return err } - for i := int64(2); i <= chain.bestNode.height; i++ { - node := chain.bestNode.Ancestor(i) + for i := int64(2); i <= chain.bestChain.Tip().height; i++ { + node := chain.bestChain.NodeByHeight(i) if node == nil { str := fmt.Sprintf("no block at height %d exists", i) return errNotInMainChain(str) diff --git a/blockchain/votebits_test.go b/blockchain/votebits_test.go index 0c8a0c1c..7d6ff6d4 100644 --- a/blockchain/votebits_test.go +++ b/blockchain/votebits_test.go @@ -115,14 +115,14 @@ func defaultParams(vote chaincfg.Vote) chaincfg.Params { func TestNoQuorum(t *testing.T) { params := defaultParams(pedro) bc := newFakeChain(¶ms) - node := bc.bestNode + node := bc.bestChain.Tip() node.stakeVersion = posVersion // get to svi curTimestamp := time.Now() for i := uint32(0); i < uint32(params.StakeValidationHeight); i++ { node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp) - bc.bestNode = node + bc.bestChain.SetTip(node) bc.index.AddNode(node) curTimestamp = curTimestamp.Add(time.Second) } @@ -143,7 +143,7 @@ func TestNoQuorum(t *testing.T) { // Set stake versions and vote bits. node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp) appendFakeVotes(node, params.TicketsPerBlock, posVersion, 0x01) - bc.bestNode = node + bc.bestChain.SetTip(node) bc.index.AddNode(node) curTimestamp = curTimestamp.Add(time.Second) } @@ -174,7 +174,7 @@ func TestNoQuorum(t *testing.T) { voteCount++ } - bc.bestNode = node + bc.bestChain.SetTip(node) bc.index.AddNode(node) curTimestamp = curTimestamp.Add(time.Second) } @@ -211,7 +211,7 @@ func TestNoQuorum(t *testing.T) { voteCount++ } - bc.bestNode = node + bc.bestChain.SetTip(node) bc.index.AddNode(node) curTimestamp = curTimestamp.Add(time.Second) } @@ -248,7 +248,7 @@ func TestNoQuorum(t *testing.T) { voteCount++ } - bc.bestNode = node + bc.bestChain.SetTip(node) bc.index.AddNode(node) curTimestamp = curTimestamp.Add(time.Second) } @@ -271,7 +271,7 @@ func TestNoQuorum(t *testing.T) { func TestYesQuorum(t *testing.T) { params := defaultParams(pedro) bc := newFakeChain(¶ms) - node := bc.bestNode + node := bc.bestChain.Tip() node.stakeVersion = posVersion // get to svi @@ -279,7 +279,7 @@ func TestYesQuorum(t *testing.T) { for i := uint32(0); i < uint32(params.StakeValidationHeight); i++ { node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp) - bc.bestNode = node + bc.bestChain.SetTip(node) bc.index.AddNode(node) curTimestamp = curTimestamp.Add(time.Second) } @@ -300,7 +300,7 @@ func TestYesQuorum(t *testing.T) { // Set stake versions and vote bits. node = newFakeNode(node, powVersion, posVersion, 0, curTimestamp) appendFakeVotes(node, params.TicketsPerBlock, posVersion, 0x01) - bc.bestNode = node + bc.bestChain.SetTip(node) bc.index.AddNode(node) curTimestamp = curTimestamp.Add(time.Second) } @@ -331,7 +331,7 @@ func TestYesQuorum(t *testing.T) { voteCount++ } - bc.bestNode = node + bc.bestChain.SetTip(node) bc.index.AddNode(node) curTimestamp = curTimestamp.Add(time.Second) } @@ -368,7 +368,7 @@ func TestYesQuorum(t *testing.T) { voteCount++ } - bc.bestNode = node + bc.bestChain.SetTip(node) bc.index.AddNode(node) curTimestamp = curTimestamp.Add(time.Second) } @@ -405,7 +405,7 @@ func TestYesQuorum(t *testing.T) { voteCount++ } - bc.bestNode = node + bc.bestChain.SetTip(node) bc.index.AddNode(node) curTimestamp = curTimestamp.Add(time.Second) } @@ -1446,7 +1446,7 @@ func TestVoting(t *testing.T) { params = defaultParams(test.vote) // We have to reset the cache for every test. bc := newFakeChain(¶ms) - node := bc.bestNode + node := bc.bestChain.Tip() node.stakeVersion = test.startStakeVersion t.Logf("running: %v", test.name) @@ -1474,7 +1474,7 @@ func TestVoting(t *testing.T) { appendFakeVotes(node, params.TicketsPerBlock, vote.Version, vote.Bits) - bc.bestNode = node + bc.bestChain.SetTip(node) bc.index.AddNode(node) curTimestamp = curTimestamp.Add(time.Second) } @@ -1627,7 +1627,7 @@ func TestParallelVoting(t *testing.T) { params = defaultParallelParams() // We have to reset the cache for every test. bc := newFakeChain(¶ms) - node := bc.bestNode + node := bc.bestChain.Tip() node.stakeVersion = test.startStakeVersion curTimestamp := time.Now() @@ -1640,7 +1640,7 @@ func TestParallelVoting(t *testing.T) { appendFakeVotes(node, params.TicketsPerBlock, vote.Version, vote.Bits) - bc.bestNode = node + bc.bestChain.SetTip(node) bc.index.AddNode(node) curTimestamp = curTimestamp.Add(time.Second) }