mirror of
https://github.com/LBRYFoundation/lbcd.git
synced 2025-08-23 17:47:24 +00:00
Merge pull request #1215 from davecgh/blockchain_generalize_reorganizechain
blockchain: Generalize chain reorg.
This commit is contained in:
commit
f673a4b563
1 changed files with 68 additions and 24 deletions
|
@ -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
|
// Use of this source code is governed by an ISC
|
||||||
// license that can be found in the LICENSE file.
|
// 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).
|
// This function MUST be called with the chain state lock held (for writes).
|
||||||
func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error {
|
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
|
// All of the blocks to detach and related spend journal entries needed
|
||||||
// to unspend transaction outputs in the blocks being disconnected must
|
// to unspend transaction outputs in the blocks being disconnected must
|
||||||
// be loaded from the database during the reorg check phase below and
|
// 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
|
// database and using that information to unspend all of the spent txos
|
||||||
// and remove the utxos created by the blocks.
|
// and remove the utxos created by the blocks.
|
||||||
view := NewUtxoViewpoint()
|
view := NewUtxoViewpoint()
|
||||||
view.SetBestHash(&b.bestChain.Tip().hash)
|
view.SetBestHash(&oldBest.hash)
|
||||||
for e := detachNodes.Front(); e != nil; e = e.Next() {
|
for e := detachNodes.Front(); e != nil; e = e.Next() {
|
||||||
n := e.Value.(*blockNode)
|
n := e.Value.(*blockNode)
|
||||||
var block *btcutil.Block
|
var block *btcutil.Block
|
||||||
|
@ -845,6 +878,11 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
// Load all of the utxos referenced by the block that aren't
|
||||||
// already in the view.
|
// already in the view.
|
||||||
|
@ -872,6 +910,15 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
// 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
|
// at least a couple of ways accomplish that rollback, but both involve
|
||||||
// tweaking the chain and/or database. This approach catches these
|
// tweaking the chain and/or database. This approach catches these
|
||||||
// issues before ever modifying the chain.
|
// issues before ever modifying the chain.
|
||||||
var validationError error
|
|
||||||
for e := attachNodes.Front(); e != nil; e = e.Next() {
|
for e := attachNodes.Front(); e != nil; e = e.Next() {
|
||||||
n := e.Value.(*blockNode)
|
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
|
var block *btcutil.Block
|
||||||
err := b.db.View(func(dbTx database.Tx) error {
|
err := b.db.View(func(dbTx database.Tx) error {
|
||||||
var err error
|
var err error
|
||||||
|
@ -922,6 +961,8 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newBest = n
|
||||||
continue
|
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
|
// thus will not be generated. This is done because the state
|
||||||
// is not being immediately written to the database, so it is
|
// is not being immediately written to the database, so it is
|
||||||
// not needed.
|
// 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)
|
err = b.checkConnectBlock(n, block, view, nil)
|
||||||
if err != 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 {
|
if _, ok := err.(RuleError); ok {
|
||||||
b.index.SetStatusFlags(n, statusValidateFailed)
|
b.index.SetStatusFlags(n, statusValidateFailed)
|
||||||
validationError = err
|
for de := e.Next(); de != nil; de = de.Next() {
|
||||||
continue
|
dn := de.Value.(*blockNode)
|
||||||
|
b.index.SetStatusFlags(dn, statusInvalidAncestor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
b.index.SetStatusFlags(n, statusValid)
|
b.index.SetStatusFlags(n, statusValid)
|
||||||
}
|
|
||||||
|
|
||||||
if validationError != nil {
|
newBest = n
|
||||||
return validationError
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the view for the actual connection code below. This is
|
// 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
|
// Log the point where the chain forked and old and new best chain
|
||||||
// heads.
|
// heads.
|
||||||
firstAttachNode := attachNodes.Front().Value.(*blockNode)
|
if forkNode != nil {
|
||||||
firstDetachNode := detachNodes.Front().Value.(*blockNode)
|
log.Infof("REORGANIZE: Chain forks at %v (height %v)", forkNode.hash,
|
||||||
lastAttachNode := attachNodes.Back().Value.(*blockNode)
|
forkNode.height)
|
||||||
log.Infof("REORGANIZE: Chain forks at %v", firstAttachNode.parent.hash)
|
}
|
||||||
log.Infof("REORGANIZE: Old best chain head was %v", firstDetachNode.hash)
|
log.Infof("REORGANIZE: Old best chain head was %v (height %v)",
|
||||||
log.Infof("REORGANIZE: New best chain head is %v", lastAttachNode.hash)
|
&oldBest.hash, oldBest.height)
|
||||||
|
log.Infof("REORGANIZE: New best chain head is %v (height %v)",
|
||||||
|
newBest.hash, newBest.height)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue