mirror of
https://github.com/btcsuite/btcd.git
synced 2024-11-20 10:11:49 +01:00
Merge pull request #1215 from davecgh/blockchain_generalize_reorganizechain
blockchain: Generalize chain reorg.
This commit is contained in:
commit
f673a4b563
@ -1,4 +1,5 @@
|
||||
// Copyright (c) 2013-2017 The btcsuite developers
|
||||
// Copyright (c) 2013-2018 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.
|
||||
|
||||
@ -819,6 +820,38 @@ 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) error {
|
||||
// Nothing to do if no reorganize nodes were provided.
|
||||
if detachNodes.Len() == 0 && attachNodes.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 != tip.hash {
|
||||
return AssertError(fmt.Sprintf("reorganize nodes to detach are "+
|
||||
"not for the current best chain -- first detach node %v, "+
|
||||
"current chain %v", &firstDetachNode.hash, &tip.hash))
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the provided nodes are for the same fork point.
|
||||
if attachNodes.Len() != 0 && detachNodes.Len() != 0 {
|
||||
firstAttachNode := attachNodes.Front().Value.(*blockNode)
|
||||
lastDetachNode := detachNodes.Back().Value.(*blockNode)
|
||||
if firstAttachNode.parent.hash != lastDetachNode.parent.hash {
|
||||
return AssertError(fmt.Sprintf("reorganize nodes do not have the "+
|
||||
"same fork point -- first attach parent %v, last detach "+
|
||||
"parent %v", &firstAttachNode.parent.hash,
|
||||
&lastDetachNode.parent.hash))
|
||||
}
|
||||
}
|
||||
|
||||
// Track the old and new best chains heads.
|
||||
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
|
||||
// be loaded from the database during the reorg check phase below and
|
||||
@ -833,7 +866,7 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
||||
// database and using that information to unspend all of the spent txos
|
||||
// and remove the utxos created by the blocks.
|
||||
view := NewUtxoViewpoint()
|
||||
view.SetBestHash(&b.bestChain.Tip().hash)
|
||||
view.SetBestHash(&oldBest.hash)
|
||||
for e := detachNodes.Front(); e != nil; e = e.Next() {
|
||||
n := e.Value.(*blockNode)
|
||||
var block *btcutil.Block
|
||||
@ -845,6 +878,11 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n.hash != *block.Hash() {
|
||||
return AssertError(fmt.Sprintf("detach block node hash %v (height "+
|
||||
"%v) does not match previous parent block hash %v", &n.hash,
|
||||
n.height, block.Hash()))
|
||||
}
|
||||
|
||||
// Load all of the utxos referenced by the block that aren't
|
||||
// already in the view.
|
||||
@ -872,6 +910,15 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newBest = n.parent
|
||||
}
|
||||
|
||||
// Set the fork point only if there are nodes to attach since otherwise
|
||||
// blocks are only being disconnected and thus there is no fork point.
|
||||
var forkNode *blockNode
|
||||
if attachNodes.Len() > 0 {
|
||||
forkNode = newBest
|
||||
}
|
||||
|
||||
// Perform several checks to verify each block that needs to be attached
|
||||
@ -886,17 +933,9 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
||||
// 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 e := attachNodes.Front(); e != nil; e = e.Next() {
|
||||
n := e.Value.(*blockNode)
|
||||
|
||||
// If any previous nodes in attachNodes failed validation,
|
||||
// mark this one as having an invalid ancestor.
|
||||
if validationError != nil {
|
||||
b.index.SetStatusFlags(n, statusInvalidAncestor)
|
||||
continue
|
||||
}
|
||||
|
||||
var block *btcutil.Block
|
||||
err := b.db.View(func(dbTx database.Tx) error {
|
||||
var err error
|
||||
@ -922,6 +961,8 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newBest = n
|
||||
continue
|
||||
}
|
||||
|
||||
@ -929,23 +970,24 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
||||
// thus will not be generated. This is done because the state
|
||||
// is not being immediately written to the database, so it is
|
||||
// not needed.
|
||||
//
|
||||
// In the case the block is determined to be invalid due to a
|
||||
// rule violation, mark it as invalid and mark all of its
|
||||
// descendants as having an invalid ancestor.
|
||||
err = b.checkConnectBlock(n, 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(n, statusValidateFailed)
|
||||
validationError = err
|
||||
continue
|
||||
for de := e.Next(); de != nil; de = de.Next() {
|
||||
dn := de.Value.(*blockNode)
|
||||
b.index.SetStatusFlags(dn, statusInvalidAncestor)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
b.index.SetStatusFlags(n, statusValid)
|
||||
}
|
||||
|
||||
if validationError != nil {
|
||||
return validationError
|
||||
newBest = n
|
||||
}
|
||||
|
||||
// Reset the view for the actual connection code below. This is
|
||||
@ -1014,12 +1056,14 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
||||
|
||||
// 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.parent.hash)
|
||||
log.Infof("REORGANIZE: Old best chain head was %v", firstDetachNode.hash)
|
||||
log.Infof("REORGANIZE: New best chain head is %v", lastAttachNode.hash)
|
||||
if forkNode != nil {
|
||||
log.Infof("REORGANIZE: Chain forks at %v (height %v)", forkNode.hash,
|
||||
forkNode.height)
|
||||
}
|
||||
log.Infof("REORGANIZE: Old best chain head was %v (height %v)",
|
||||
&oldBest.hash, oldBest.height)
|
||||
log.Infof("REORGANIZE: New best chain head is %v (height %v)",
|
||||
newBest.hash, newBest.height)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user