diff --git a/blockdag/accept.go b/blockdag/accept.go index 3c9331a09..25eca7dc4 100644 --- a/blockdag/accept.go +++ b/blockdag/accept.go @@ -11,22 +11,21 @@ import ( "github.com/daglabs/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 -// several validation checks which depend on its position within the block chain -// before adding it. The block is expected to have already gone through -// ProcessBlock before calling this function with it. +// maybeAcceptBlock potentially accepts a block into the block DAG. It +// performs several validation checks which depend on its position within +// the block DAG before adding it. The block is expected to have already +// gone through ProcessBlock before calling this function with it. // -// The flags are also passed to checkBlockContext and connectBestChain. See +// The flags are also passed to checkBlockContext and connectToDAG. See // their documentation for how the flags modify their behavior. // // This function MUST be called with the chain state lock held (for writes). -func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) (bool, error) { +func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) error { // The height of this block is one more than the referenced previous // block. parents, err := lookupPreviousNodes(block, b) if err != nil { - return false, err + return err } selectedParent := parents.first() @@ -37,7 +36,7 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) // position of the block within the block chain. err = b.checkBlockContext(block, selectedParent, flags) if err != nil { - return false, err + return err } // Insert the block into the database if it's not already there. Even @@ -53,7 +52,7 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) return dbStoreBlock(dbTx, block) }) if err != nil { - return false, err + return err } // Create a new block node for the block and add it to the node index. Even @@ -66,15 +65,14 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) b.index.AddNode(newNode) err = b.index.flushToDB() if err != nil { - return false, err + return err } - // Connect the passed block to the chain while respecting proper chain - // selection according to the chain with the most proof of work. This - // also handles validation of the transaction scripts. - isMainChain, err := b.connectBestChain(newNode, parents, block, flags) + // Connect the passed block to the DAG. This also handles validation of the + // transaction scripts. + err = b.connectToDAG(newNode, parents, block, flags) if err != nil { - return false, err + return err } // Notify the caller that the new block was accepted into the block @@ -84,7 +82,7 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) b.sendNotification(NTBlockAccepted, block) b.chainLock.Lock() - return isMainChain, nil + return nil } func lookupPreviousNodes(block *btcutil.Block, blockChain *BlockChain) (blockSet, error) { diff --git a/blockdag/chain.go b/blockdag/chain.go index 4d5625228..9a58ec123 100644 --- a/blockdag/chain.go +++ b/blockdag/chain.go @@ -5,7 +5,6 @@ package blockdag import ( - "container/list" "fmt" "sync" "time" @@ -484,65 +483,6 @@ func LockTimeToSequence(isSeconds bool, locktime uint32) uint32 { locktime>>wire.SequenceLockTimeGranularity } -// getReorganizeNodes finds the fork point between the main chain and the passed -// node and returns a list of block nodes that would need to be detached from -// the main chain and a list of block nodes that would need to be attached to -// the fork point (which will be the end of the main chain after detaching the -// returned list of block nodes) in order to reorganize the chain such that the -// passed node is the new end of the main chain. The lists will be empty if the -// passed node is not on a side chain. -// -// This function may modify node statuses in the block index without flushing. -// -// This function MUST be called with the chain state lock held (for reads). -func (b *BlockChain) getReorganizeNodes(node *blockNode) (*list.List, *list.List) { - attachNodes := list.New() - detachNodes := list.New() - - // Do not reorganize to a known invalid chain. Ancestors deeper than the - // direct parent are checked below but this is a quick check before doing - // more unnecessary work. - if b.index.NodeStatus(node.selectedParent).KnownInvalid() { - b.index.SetStatusFlags(node, statusInvalidAncestor) - return detachNodes, attachNodes - } - - // Find the fork point (if any) adding each block to the list of nodes - // 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. - forkNode := b.bestChain.FindFork(node) - invalidChain := false - for n := node; n != nil && n != forkNode; n = n.selectedParent { - if b.index.NodeStatus(n).KnownInvalid() { - invalidChain = true - break - } - attachNodes.PushFront(n) - } - - // If any of the node's ancestors are invalid, unwind attachNodes, marking - // each one as invalid for future reference. - if invalidChain { - var next *list.Element - for e := attachNodes.Front(); e != nil; e = next { - next = e.Next() - n := attachNodes.Remove(e).(*blockNode) - b.index.SetStatusFlags(n, statusInvalidAncestor) - } - return detachNodes, attachNodes - } - - // 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.bestChain.SelectedTip(); n != nil && n != forkNode; n = n.selectedParent { - detachNodes.PushBack(n) - } - - return detachNodes, attachNodes -} - // connectBlock handles connecting the passed node/block to the end of the main // (best) chain. // @@ -793,345 +733,69 @@ func countSpentOutputs(block *btcutil.Block) int { return numSpent } -// reorganizeChain reorganizes the block chain by disconnecting the nodes in the -// detachNodes list and connecting the nodes in the attach list. It expects -// that the lists are already in the correct order and are in sync with the -// end of the current best chain. Specifically, nodes that are being -// disconnected must be in reverse order (think of popping them off the end of -// the chain) and nodes the are being attached must be in forwards order -// (think pushing them onto the end of the chain). -// -// This function may modify node statuses in the block index without flushing. -// -// This function MUST be called with the chain state lock held (for writes). -func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error { - // 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 - // then they are needed again when doing the actual database updates. - // 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 - // database and using that information to unspend all of the spent txos - // and remove the utxos created by the blocks. - view := NewUtxoViewpoint() - view.SetTips(b.bestChain.Tips()) - for element := detachNodes.Front(); element != nil; element = element.Next() { - node := element.Value.(*blockNode) - var block *btcutil.Block - err := b.db.View(func(dbTx database.Tx) error { - var err error - block, err = dbFetchBlockByNode(dbTx, node) - return err - }) - if err != nil { - return err - } - - // Load all of the utxos referenced by the block that aren't - // already in the view. - err = view.fetchInputUtxos(b.db, block) - if err != nil { - return err - } - - // Load all of the spent txos for the block from the spend - // journal. - var stxos []spentTxOut - err = b.db.View(func(dbTx database.Tx) error { - stxos, err = dbFetchSpendJournalEntry(dbTx, block) - return err - }) - if err != nil { - return err - } - - // Store the loaded block and spend journal entry for later. - detachBlocks = append(detachBlocks, block) - detachSpentTxOuts = append(detachSpentTxOuts, stxos) - - err = view.disconnectTransactions(b.db, node.parents, block, stxos) - if err != nil { - return err - } - } - - // Perform several checks to verify each block that needs to be attached - // to the main chain can be connected without violating any rules and - // without actually connecting the block. - // - // NOTE: These checks could be done directly when connecting a block, - // however the downside to that approach is that if any of these checks - // fail after disconnecting some blocks or attaching others, all of the - // operations have to be rolled back to get the chain back into the - // state it was before the rule violation (or other failure). There are - // at least a couple of ways accomplish that rollback, but both involve - // tweaking the chain and/or database. This approach catches these - // issues before ever modifying the chain. - var validationError error - for element := attachNodes.Front(); element != nil; element = element.Next() { - node := element.Value.(*blockNode) - - // If any previous nodes in attachNodes failed validation, - // mark this one as having an invalid ancestor. - if validationError != nil { - b.index.SetStatusFlags(node, statusInvalidAncestor) - continue - } - - var block *btcutil.Block - err := b.db.View(func(dbTx database.Tx) error { - var err error - block, err = dbFetchBlockByNode(dbTx, node) - return err - }) - if err != nil { - return err - } - - // Store the loaded block for later. - attachBlocks = append(attachBlocks, block) - - // Skip checks if node has already been fully validated. Although - // checkConnectBlock gets skipped, we still need to update the UTXO - // view. - if b.index.NodeStatus(node).KnownValid() { - err = view.fetchInputUtxos(b.db, block) - if err != nil { - return err - } - err = view.connectTransactions(node, block.Transactions(), nil) - if err != nil { - return err - } - continue - } - - // 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(node, block, view, nil) - if err != nil { - // If the block failed validation mark it as invalid, then - // continue to loop through remaining nodes, marking them as - // having an invalid ancestor. - if _, ok := err.(RuleError); ok { - b.index.SetStatusFlags(node, statusValidateFailed) - validationError = err - continue - } - return err - } - b.index.SetStatusFlags(node, statusValid) - } - - if validationError != nil { - return validationError - } - - // Reset the view for the actual connection code below. This is - // required because the view was previously modified when checking if - // the reorg would be successful and the connection code requires the - // view to be valid from the viewpoint of each block being connected or - // disconnected. - view = NewUtxoViewpoint() - view.SetTips(b.bestChain.Tips()) - - // Disconnect blocks from the main chain. - for i, element := 0, detachNodes.Front(); element != nil; i, element = i+1, element.Next() { - node := element.Value.(*blockNode) - block := detachBlocks[i] - - // Load all of the utxos referenced by the block that aren't - // already in the view. - err := view.fetchInputUtxos(b.db, block) - if err != nil { - return err - } - - // Update the view to unspend all of the spent txos and remove - // the utxos created by the block. - err = view.disconnectTransactions(b.db, node.parents, block, - detachSpentTxOuts[i]) - if err != nil { - return err - } - - // Update the database and chain state. - err = b.disconnectBlock(node, block, view) - if err != nil { - return err - } - } - - // Connect the new best chain blocks. - for i, element := 0, attachNodes.Front(); element != nil; i, element = i+1, element.Next() { - node := element.Value.(*blockNode) - block := attachBlocks[i] - - // Load all of the utxos referenced by the block that aren't - // already in the view. - err := view.fetchInputUtxos(b.db, block) - if err != nil { - return err - } - - // Update the view to mark all utxos referenced by the block - // as spent and add all transactions being created by this block - // to it. Also, provide an stxo slice so the spent txout - // details are generated. - stxos := make([]spentTxOut, 0, countSpentOutputs(block)) - err = view.connectTransactions(node, block.Transactions(), &stxos) - if err != nil { - return err - } - - // Update the database and chain state. - err = b.connectBlock(node, block, view, stxos) - if err != nil { - return err - } - } - - // Log the point where the chain forked and old and new best chain - // heads. - firstAttachNode := attachNodes.Front().Value.(*blockNode) - firstDetachNode := detachNodes.Front().Value.(*blockNode) - lastAttachNode := attachNodes.Back().Value.(*blockNode) - log.Infof("REORGANIZE: Chain forks at %v", firstAttachNode.selectedParent.hash) - log.Infof("REORGANIZE: Old best chain head was %v", firstDetachNode.hash) - log.Infof("REORGANIZE: New best chain head is %v", lastAttachNode.hash) - - return nil -} - -// connectBestChain handles connecting the passed block to the chain while -// respecting proper chain selection according to the chain with the most -// proof of work. In the typical case, the new block simply extends the main -// chain. However, it may also be extending (or creating) a side chain (fork) -// which may or may not end up becoming the main chain depending on which fork -// cumulatively has the most proof of work. It returns whether or not the block -// ended up on the main chain (either due to extending the main chain or causing -// a reorganization to become the main chain). +// connectToDAG handles connecting the passed block to the DAG. // // The flags modify the behavior of this function as follows: // - BFFastAdd: Avoids several expensive transaction validation operations. // This is useful when using checkpoints. // // This function MUST be called with the chain state lock held (for writes). -func (b *BlockChain) connectBestChain(node *blockNode, parentNodes blockSet, block *btcutil.Block, flags BehaviorFlags) (bool, error) { - fastAdd := flags&BFFastAdd == BFFastAdd +func (b *BlockChain) connectToDAG(node *blockNode, parentNodes blockSet, block *btcutil.Block, flags BehaviorFlags) error { + // Skip checks if node has already been fully validated. + fastAdd := flags&BFFastAdd == BFFastAdd || b.index.NodeStatus(node).KnownValid() - // We are extending the main (best) chain with a new block. This is the - // most common case. - parentHashes := block.MsgBlock().Header.PrevBlocks - if b.bestChain.Tips().hashesEqual(parentHashes) { - // Skip checks if node has already been fully validated. - fastAdd = fastAdd || b.index.NodeStatus(node).KnownValid() - - // Perform several checks to verify the block can be connected - // to the main chain without violating any rules and without - // actually connecting the block. - view := NewUtxoViewpoint() - view.SetTips(parentNodes) - stxos := make([]spentTxOut, 0, countSpentOutputs(block)) - if !fastAdd { - err := b.checkConnectBlock(node, block, view, &stxos) - if err == nil { - b.index.SetStatusFlags(node, statusValid) - } else if _, ok := err.(RuleError); ok { - b.index.SetStatusFlags(node, statusValidateFailed) - } else { - return false, err - } - - // Intentionally ignore errors writing updated node status to DB. If - // it fails to write, it's not the end of the world. If the block is - // valid, we flush in connectBlock and if the block is invalid, the - // worst that can happen is we revalidate the block after a restart. - if writeErr := b.index.flushToDB(); writeErr != nil { - log.Warnf("Error flushing block index changes to disk: %v", - writeErr) - } - - if err != nil { - return false, err - } - } - - // In the fast add case the code to check the block connection - // was skipped, so the utxo view needs to load the referenced - // utxos, spend them, and add the new utxos being created by - // this block. - if fastAdd { - err := view.fetchInputUtxos(b.db, block) - if err != nil { - return false, err - } - err = view.connectTransactions(node, block.Transactions(), &stxos) - if err != nil { - return false, err - } - } - - // Connect the block to the main chain. - err := b.connectBlock(node, block, view, stxos) - if err != nil { - return false, err - } - - return true, nil - } - if fastAdd { - log.Warnf("fastAdd set in the side chain case? %v\n", - block.Hash()) - } - - // 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.bestChain.SelectedTip().workSum) <= 0 { - // Log information about how the block is forking the chain. - fork := b.bestChain.FindFork(node) - if fork.hash.IsEqual(block.MsgBlock().Header.SelectedPrevBlock()) { - log.Infof("FORK: Block %v forks the chain at height %d"+ - "/block %v, but does not cause a reorganize", - node.hash, fork.height, fork.hash) + // Perform several checks to verify the block can be connected + // to the DAG without violating any rules and without actually + // connecting the block. + view := NewUtxoViewpoint() + view.SetTips(parentNodes) + stxos := make([]spentTxOut, 0, countSpentOutputs(block)) + if !fastAdd { + err := b.checkConnectBlock(node, block, view, &stxos) + if err == nil { + b.index.SetStatusFlags(node, statusValid) + } else if _, ok := err.(RuleError); ok { + b.index.SetStatusFlags(node, statusValidateFailed) } else { - log.Infof("EXTEND FORK: Block %v extends a side chain "+ - "which forks the chain at height %d/block %v", - node.hash, fork.height, fork.hash) + return err } - return false, nil + // Intentionally ignore errors writing updated node status to DB. If + // it fails to write, it's not the end of the world. If the block is + // valid, we flush in connectBlock and if the block is invalid, the + // worst that can happen is we revalidate the block after a restart. + if writeErr := b.index.flushToDB(); writeErr != nil { + log.Warnf("Error flushing block index changes to disk: %v", + writeErr) + } + + if err != nil { + return err + } } - // We're extending (or creating) a side chain and the cumulative work - // for this new side chain is more than the old best chain, so this side - // chain needs to become the main chain. In order to accomplish that, - // find the common ancestor of both sides of the fork, disconnect the - // blocks that form the (now) old fork from the main chain, and attach - // the blocks that form the new chain to the main chain starting at the - // common ancenstor (the point where the chain forked). - detachNodes, attachNodes := b.getReorganizeNodes(node) - - // Reorganize the chain. - log.Infof("REORGANIZE: Block %v is causing a reorganize.", node.hash) - err := b.reorganizeChain(detachNodes, attachNodes) - - // Either getReorganizeNodes or reorganizeChain could have made unsaved - // changes to the block index, so flush regardless of whether there was an - // error. The index would only be dirty if the block failed to connect, so - // we can ignore any errors writing. - if writeErr := b.index.flushToDB(); writeErr != nil { - log.Warnf("Error flushing block index changes to disk: %v", writeErr) + // In the fast add case the code to check the block connection + // was skipped, so the utxo view needs to load the referenced + // utxos, spend them, and add the new utxos being created by + // this block. + if fastAdd { + err := view.fetchInputUtxos(b.db, block) + if err != nil { + return err + } + err = view.connectTransactions(node, block.Transactions(), &stxos) + if err != nil { + return err + } } - return err == nil, err + // Connect the block to the DAG. + err := b.connectBlock(node, block, view, stxos) + if err != nil { + return err + } + + return nil } // isCurrent returns whether or not the chain believes it is current. Several diff --git a/blockdag/chain_test.go b/blockdag/chain_test.go index 2c246a4b6..0fb75b7ed 100644 --- a/blockdag/chain_test.go +++ b/blockdag/chain_test.go @@ -49,7 +49,7 @@ func TestHaveBlock(t *testing.T) { chain.TstSetCoinbaseMaturity(1) for i := 1; i < len(blocks); i++ { - _, isOrphan, err := chain.ProcessBlock(blocks[i], BFNone) + isOrphan, err := chain.ProcessBlock(blocks[i], BFNone) if err != nil { t.Errorf("ProcessBlock fail on block %v: %v\n", i, err) return @@ -62,7 +62,7 @@ func TestHaveBlock(t *testing.T) { } // Insert an orphan block. - _, isOrphan, err := chain.ProcessBlock(btcutil.NewBlock(&Block100000), + isOrphan, err := chain.ProcessBlock(btcutil.NewBlock(&Block100000), BFNone) if err != nil { t.Errorf("Unable to process block: %v", err) diff --git a/blockdag/example_test.go b/blockdag/example_test.go index d90db1461..48c2e0efe 100644 --- a/blockdag/example_test.go +++ b/blockdag/example_test.go @@ -59,13 +59,12 @@ func ExampleBlockChain_ProcessBlock() { // cause an error by trying to process the genesis block which already // exists. genesisBlock := btcutil.NewBlock(dagconfig.MainNetParams.GenesisBlock) - isMainChain, isOrphan, err := chain.ProcessBlock(genesisBlock, + isOrphan, err := chain.ProcessBlock(genesisBlock, blockdag.BFNone) if err != nil { fmt.Printf("Failed to process block: %v\n", err) return } - fmt.Printf("Block accepted. Is it on the main chain?: %v", isMainChain) fmt.Printf("Block accepted. Is it an orphan?: %v", isOrphan) // Output: diff --git a/blockdag/fullblocks_test.go b/blockdag/fullblocks_test.go index 2962ce054..003fe1642 100644 --- a/blockdag/fullblocks_test.go +++ b/blockdag/fullblocks_test.go @@ -156,7 +156,7 @@ func TestFullBlocks(t *testing.T) { t.Logf("Testing block %s (hash %s, height %d)", item.Name, block.Hash(), blockHeight) - isMainChain, isOrphan, err := chain.ProcessBlock(block, + isOrphan, err := chain.ProcessBlock(block, blockdag.BFNone) if err != nil { t.Fatalf("block %q (hash %s, height %d) should "+ @@ -164,14 +164,6 @@ func TestFullBlocks(t *testing.T) { block.Hash(), blockHeight, err) } - // Ensure the main chain and orphan flags match the values - // specified in the test. - if isMainChain != item.IsMainChain { - t.Fatalf("block %q (hash %s, height %d) unexpected main "+ - "chain flag -- got %v, want %v", item.Name, - block.Hash(), blockHeight, isMainChain, - item.IsMainChain) - } if isOrphan != item.IsOrphan { t.Fatalf("block %q (hash %s, height %d) unexpected "+ "orphan flag -- got %v, want %v", item.Name, @@ -190,7 +182,7 @@ func TestFullBlocks(t *testing.T) { t.Logf("Testing block %s (hash %s, height %d)", item.Name, block.Hash(), blockHeight) - _, _, err := chain.ProcessBlock(block, blockdag.BFNone) + _, err := chain.ProcessBlock(block, blockdag.BFNone) if err == nil { t.Fatalf("block %q (hash %s, height %d) should not "+ "have been accepted", item.Name, block.Hash(), @@ -247,7 +239,7 @@ func TestFullBlocks(t *testing.T) { t.Logf("Testing block %s (hash %s, height %d)", item.Name, block.Hash(), blockHeight) - _, isOrphan, err := chain.ProcessBlock(block, blockdag.BFNone) + isOrphan, err := chain.ProcessBlock(block, blockdag.BFNone) if err != nil { // Ensure the error code is of the expected type. if _, ok := err.(blockdag.RuleError); !ok { diff --git a/blockdag/fullblocktests/generate.go b/blockdag/fullblocktests/generate.go index 3af092335..e98b9d618 100644 --- a/blockdag/fullblocktests/generate.go +++ b/blockdag/fullblocktests/generate.go @@ -64,11 +64,10 @@ type TestInstance interface { // the blockchain either by extending the main chain, on a side chain, or as an // orphan. type AcceptedBlock struct { - Name string - Block *wire.MsgBlock - Height int32 - IsMainChain bool - IsOrphan bool + Name string + Block *wire.MsgBlock + Height int32 + IsOrphan bool } // Ensure AcceptedBlock implements the TestInstance interface. @@ -836,10 +835,9 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) { // // expectTipBlock creates a test instance that expects the provided // block to be the current tip of the block chain. - acceptBlock := func(blockName string, block *wire.MsgBlock, isMainChain, isOrphan bool) TestInstance { + acceptBlock := func(blockName string, block *wire.MsgBlock, isOrphan bool) TestInstance { blockHeight := g.blockHeights[blockName] - return AcceptedBlock{blockName, block, blockHeight, isMainChain, - isOrphan} + return AcceptedBlock{blockName, block, blockHeight, isOrphan} } rejectBlock := func(blockName string, block *wire.MsgBlock, code blockdag.ErrorCode) TestInstance { blockHeight := g.blockHeights[blockName] @@ -882,12 +880,12 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) { // test instance for the current tip. accepted := func() { tests = append(tests, []TestInstance{ - acceptBlock(g.tipName, g.tip, true, false), + acceptBlock(g.tipName, g.tip, false), }) } acceptedToSideChainWithExpectedTip := func(tipName string) { tests = append(tests, []TestInstance{ - acceptBlock(g.tipName, g.tip, false, false), + acceptBlock(g.tipName, g.tip, false), expectTipBlock(tipName, g.blocksByName[tipName]), }) } @@ -920,7 +918,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) { g.nextBlock(blockName, nil) g.saveTipCoinbaseOut() testInstances = append(testInstances, acceptBlock(g.tipName, - g.tip, true, false)) + g.tip, false)) } tests = append(tests, testInstances) @@ -1037,8 +1035,8 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) { b13 := g.nextBlock("b13", outs[4]) b14 := g.nextBlock("b14", outs[5], additionalCoinbase(1)) tests = append(tests, []TestInstance{ - acceptBlock("b13", b13, false, true), - acceptBlock("b14", b14, false, true), + acceptBlock("b13", b13, true), + acceptBlock("b14", b14, true), rejectBlock("b12", b12, blockdag.ErrBadCoinbaseValue), expectTipBlock("b13", b13), }) @@ -2081,7 +2079,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) { g.assertTipBlockSize(maxBlockSize) g.saveTipCoinbaseOut() testInstances = append(testInstances, acceptBlock(g.tipName, - g.tip, true, false)) + g.tip, false)) // Use the next available spendable output. First use up any // remaining spendable outputs that were already popped into the @@ -2105,7 +2103,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) { chain2TipName = fmt.Sprintf("bralt%d", i) g.nextBlock(chain2TipName, nil) testInstances = append(testInstances, acceptBlock(g.tipName, - g.tip, false, false)) + g.tip, false)) } testInstances = append(testInstances, expectTipBlock(chain1TipName, g.blocksByName[chain1TipName])) diff --git a/blockdag/notifications_test.go b/blockdag/notifications_test.go index eb72fe458..fceb63683 100644 --- a/blockdag/notifications_test.go +++ b/blockdag/notifications_test.go @@ -39,7 +39,7 @@ func TestNotifications(t *testing.T) { chain.Subscribe(callback) } - _, _, err = chain.ProcessBlock(blocks[1], BFNone) + _, err = chain.ProcessBlock(blocks[1], BFNone) if err != nil { t.Fatalf("ProcessBlock fail on block 1: %v\n", err) } diff --git a/blockdag/process.go b/blockdag/process.go index 6b361cf9f..a8fe3a407 100644 --- a/blockdag/process.go +++ b/blockdag/process.go @@ -115,7 +115,7 @@ func (b *BlockChain) processOrphans(hash *daghash.Hash, flags BehaviorFlags) err i-- // Potentially accept the block into the block chain. - _, err := b.maybeAcceptBlock(orphan.block, flags) + err := b.maybeAcceptBlock(orphan.block, flags) if err != nil { return err } @@ -132,14 +132,13 @@ func (b *BlockChain) processOrphans(hash *daghash.Hash, flags BehaviorFlags) err // ProcessBlock is the main workhorse for handling insertion of new blocks into // the block chain. It includes functionality such as rejecting duplicate // blocks, ensuring blocks follow all rules, orphan handling, and insertion into -// the block chain along with best chain selection and reorganization. +// the block DAG. // // When no errors occurred during processing, the first return value indicates -// whether or not the block is on the main chain and the second indicates // whether or not the block is an orphan. // // This function is safe for concurrent access. -func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bool, bool, error) { +func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bool, error) { b.chainLock.Lock() defer b.chainLock.Unlock() @@ -151,23 +150,23 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bo // The block must not already exist in the main chain or side chains. exists, err := b.blockExists(blockHash) if err != nil { - return false, false, err + return false, err } if exists { str := fmt.Sprintf("already have block %v", blockHash) - return false, false, ruleError(ErrDuplicateBlock, str) + return false, ruleError(ErrDuplicateBlock, str) } // The block must not already exist as an orphan. if _, exists := b.orphans[*blockHash]; exists { str := fmt.Sprintf("already have block (orphan) %v", blockHash) - return false, false, ruleError(ErrDuplicateBlock, str) + return false, ruleError(ErrDuplicateBlock, str) } // Perform preliminary sanity checks on the block and its transactions. err = checkBlockSanity(block, b.chainParams.PowLimit, b.timeSource, flags) if err != nil { - return false, false, err + return false, err } // Find the previous checkpoint and perform some additional checks based @@ -179,7 +178,7 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bo blockHeader := &block.MsgBlock().Header checkpointNode, err := b.findPreviousCheckpoint() if err != nil { - return false, false, err + return false, err } if checkpointNode != nil { // Ensure the block timestamp is after the checkpoint timestamp. @@ -188,7 +187,7 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bo str := fmt.Sprintf("block %v has timestamp %v before "+ "last checkpoint timestamp %v", blockHash, blockHeader.Timestamp, checkpointTime) - return false, false, ruleError(ErrCheckpointTimeTooOld, str) + return false, ruleError(ErrCheckpointTimeTooOld, str) } if !fastAdd { // Even though the checks prior to now have already ensured the @@ -205,7 +204,7 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bo str := fmt.Sprintf("block target difficulty of %064x "+ "is too low when compared to the previous "+ "checkpoint", currentTarget) - return false, false, ruleError(ErrDifficultyTooLow, str) + return false, ruleError(ErrDifficultyTooLow, str) } } } @@ -215,7 +214,7 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bo for _, prevBlock := range blockHeader.PrevBlocks { prevBlockExists, err := b.blockExists(&prevBlock) if err != nil { - return false, false, err + return false, err } if !prevBlockExists { @@ -227,14 +226,14 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bo } if !allPrevBlocksExist { - return false, true, nil + return true, nil } // The block has passed all context independent checks and appears sane - // enough to potentially accept it into the block chain. - isMainChain, err := b.maybeAcceptBlock(block, flags) + // enough to potentially accept it into the block DAG. + err = b.maybeAcceptBlock(block, flags) if err != nil { - return false, false, err + return false, err } // Accept any orphan blocks that depend on this block (they are @@ -242,10 +241,10 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bo // there are no more. err = b.processOrphans(blockHash, flags) if err != nil { - return false, false, err + return false, err } log.Debugf("Accepted block %v", blockHash) - return isMainChain, false, nil + return false, nil } diff --git a/blockdag/validate_test.go b/blockdag/validate_test.go index 66e7f57cf..30cbeaef2 100644 --- a/blockdag/validate_test.go +++ b/blockdag/validate_test.go @@ -97,15 +97,11 @@ func TestCheckConnectBlockTemplate(t *testing.T) { } for i := 1; i <= 3; i++ { - isMainChain, _, err := chain.ProcessBlock(blocks[i], BFNone) + _, err := chain.ProcessBlock(blocks[i], BFNone) if err != nil { t.Fatalf("CheckConnectBlockTemplate: Received unexpected error "+ "processing block %d: %v", i, err) } - if !isMainChain { - t.Fatalf("CheckConnectBlockTemplate: Expected block %d to connect "+ - "to main chain", i) - } } // Block 3 should fail to connect since it's already inserted. diff --git a/cmd/addblock/import.go b/cmd/addblock/import.go index 6ff296745..f3db36f1a 100644 --- a/cmd/addblock/import.go +++ b/cmd/addblock/import.go @@ -129,15 +129,11 @@ func (bi *blockImporter) processBlock(serializedBlock []byte) (bool, error) { // Ensure the blocks follows all of the chain rules and match up to the // known checkpoints. - isMainChain, isOrphan, err := bi.chain.ProcessBlock(block, + isOrphan, err := bi.chain.ProcessBlock(block, blockdag.BFFastAdd) if err != nil { return false, err } - if !isMainChain { - return false, fmt.Errorf("import file contains an block that "+ - "does not extend the main chain: %v", blockHash) - } if isOrphan { return false, fmt.Errorf("import file contains an orphan "+ "block: %v", blockHash) diff --git a/netsync/manager.go b/netsync/manager.go index c7990d9da..527884bfc 100644 --- a/netsync/manager.go +++ b/netsync/manager.go @@ -543,9 +543,8 @@ func (sm *SyncManager) handleBlockMsg(bmsg *blockMsg) { delete(state.requestedBlocks, *blockHash) delete(sm.requestedBlocks, *blockHash) - // Process the block to include validation, best chain selection, orphan - // handling, etc. - _, isOrphan, err := sm.chain.ProcessBlock(bmsg.block, behaviorFlags) + // Process the block to include validation, orphan handling, etc. + isOrphan, err := sm.chain.ProcessBlock(bmsg.block, behaviorFlags) if err != nil { // When the error is a rule error, it means the block was simply // rejected as opposed to something actually going wrong, so log @@ -1127,7 +1126,7 @@ out: msg.reply <- peerID case processBlockMsg: - _, isOrphan, err := sm.chain.ProcessBlock( + isOrphan, err := sm.chain.ProcessBlock( msg.block, msg.flags) if err != nil { msg.reply <- processBlockResponse{