diff --git a/blockchain/accept.go b/blockchain/accept.go index 2ecd5bc0..08828aae 100644 --- a/blockchain/accept.go +++ b/blockchain/accept.go @@ -4,7 +4,10 @@ package blockchain -import "github.com/btcsuite/btcutil" +import ( + "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcutil" +) // maybeAcceptBlock potentially accepts a block into the block chain and, if // accepted, returns whether or not it is on the main chain. It performs @@ -46,6 +49,22 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) return false, err } + // Insert the block into the database if it's not already there. Even + // though it is possible the block will ultimately fail to connect, it + // has already passed all proof-of-work and validity tests which means + // it would be prohibitively expensive for an attacker to fill up the + // disk with a bunch of blocks that fail to connect. This is necessary + // since it allows block download to be decoupled from the much more + // expensive connection logic. It also has some other nice properties + // such as making blocks that never become part of the main chain or + // blocks that fail to connect available for further analysis. + err = b.db.Update(func(dbTx database.Tx) error { + return dbMaybeStoreBlock(dbTx, block) + }) + if err != nil { + return false, err + } + // Create a new block node for the block and add it to the in-memory // block chain (could be either a side chain or the main chain). blockHeader := &block.MsgBlock().Header diff --git a/blockchain/chain.go b/blockchain/chain.go index 2ecee077..2b286f7a 100644 --- a/blockchain/chain.go +++ b/blockchain/chain.go @@ -203,7 +203,6 @@ type BlockChain struct { orphans map[chainhash.Hash]*orphanBlock prevOrphans map[chainhash.Hash][]*orphanBlock oldestOrphan *orphanBlock - blockCache map[chainhash.Hash]*btcutil.Block // These fields are related to checkpoint handling. They are protected // by the chain lock. @@ -1027,12 +1026,6 @@ func (b *BlockChain) connectBlock(node *blockNode, block *btcutil.Block, view *U return err } - // Insert the block into the database if it's not already there. - err = dbMaybeStoreBlock(dbTx, block) - if err != nil { - return err - } - // Allow the index manager to call each of the currently active // optional indexes with the block being connected so they can // update themselves accordingly. @@ -1183,9 +1176,8 @@ func (b *BlockChain) disconnectBlock(node *blockNode, block *btcutil.Block, view // now that the modifications have been committed to the database. view.commit() - // Put block in the side chain cache. + // Mark block as being in a side chain. node.inMainChain = false - b.blockCache[*node.hash] = block // This node's parent is now the end of the best chain. b.bestNode = node.parent @@ -1233,15 +1225,6 @@ func countSpentOutputs(block *btcutil.Block) int { // // This function MUST be called with the chain state lock held (for writes). func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List, flags BehaviorFlags) error { - // Ensure all of the needed side chain blocks are in the cache. - for e := attachNodes.Front(); e != nil; e = e.Next() { - n := e.Value.(*blockNode) - if _, exists := b.blockCache[*n.hash]; !exists { - return AssertError(fmt.Sprintf("block %v is missing "+ - "from the side chain block cache", n.hash)) - } - } - // All of the blocks to detach and related spend journal entries needed // to unspend transaction outputs in the blocks being disconnected must // be loaded from the database during the reorg check phase below and @@ -1249,6 +1232,7 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List, flags // Rather than doing two loads, cache the loaded data into these slices. detachBlocks := make([]*btcutil.Block, 0, detachNodes.Len()) detachSpentTxOuts := make([][]spentTxOut, 0, detachNodes.Len()) + attachBlocks := make([]*btcutil.Block, 0, attachNodes.Len()) // Disconnect all of the blocks back to the point of the fork. This // entails loading the blocks and their associated spent txos from the @@ -1264,6 +1248,9 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List, flags block, err = dbFetchBlockByHash(dbTx, n.hash) return err }) + if err != nil { + return err + } // Load all of the utxos referenced by the block that aren't // already in the view. @@ -1307,13 +1294,35 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List, flags // issues before ever modifying the chain. for e := attachNodes.Front(); e != nil; e = e.Next() { n := e.Value.(*blockNode) - block := b.blockCache[*n.hash] + var block *btcutil.Block + err := b.db.View(func(dbTx database.Tx) error { + // NOTE: This block is not in the main chain, so the + // block has to be loaded directly from the database + // instead of using the dbFetchBlockByHash function. + blockBytes, err := dbTx.FetchBlock(n.hash) + if err != nil { + return err + } + + block, err = btcutil.NewBlockFromBytes(blockBytes) + if err != nil { + return err + } + block.SetHeight(n.height) + return nil + }) + if err != nil { + return err + } + + // Store the loaded block for later. + attachBlocks = append(attachBlocks, block) // Notice the spent txout details are not requested here and // thus will not be generated. This is done because the state // is not being immediately written to the database, so it is // not needed. - err := b.checkConnectBlock(n, block, view, nil) + err = b.checkConnectBlock(n, block, view, nil) if err != nil { return err } @@ -1360,9 +1369,9 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List, flags } // Connect the new best chain blocks. - for e := attachNodes.Front(); e != nil; e = e.Next() { + for i, e := 0, attachNodes.Front(); e != nil; i, e = i+1, e.Next() { n := e.Value.(*blockNode) - block := b.blockCache[*n.hash] + block := attachBlocks[i] // Load all of the utxos referenced by the block that aren't // already in the view. @@ -1386,7 +1395,6 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List, flags if err != nil { return err } - delete(b.blockCache, *n.hash) } // Log the point where the chain forked. @@ -1481,21 +1489,16 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla } // We're extending (or creating) a side chain which may or may not - // become the main chain, but in either case we need the block stored - // for future processing, so add the block to the side chain holding - // cache. - if !dryRun { - log.Debugf("Adding block %v to side chain cache", node.hash) - } - b.blockCache[*node.hash] = block + // become the main chain, but in either case the entry is needed in the + // index for future processing. b.index[*node.hash] = node // Connect the parent node to this node. node.inMainChain = false node.parent.children = append(node.parent.children, node) - // Remove the block from the side chain cache and disconnect it from the - // parent node when the function returns when running in dry run mode. + // Disconnect it from the parent node when the function returns when + // running in dry run mode. if dryRun { defer func() { children := node.parent.children @@ -1503,7 +1506,6 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla node.parent.children = children delete(b.index, *node.hash) - delete(b.blockCache, *node.hash) }() } @@ -1735,7 +1737,6 @@ func New(config *Config) (*BlockChain, error) { depNodes: make(map[chainhash.Hash][]*blockNode), orphans: make(map[chainhash.Hash]*orphanBlock), prevOrphans: make(map[chainhash.Hash][]*orphanBlock), - blockCache: make(map[chainhash.Hash]*btcutil.Block), warningCaches: newThresholdCaches(vbNumBits), deploymentCaches: newThresholdCaches(chaincfg.DefinedDeployments), }