Skip to content

Commit 764e81b

Browse files
committed
Merge pull request #825 from obscuren/develop
core: chain fork fix
2 parents bac4440 + 13364a2 commit 764e81b

File tree

6 files changed

+117
-35
lines changed

6 files changed

+117
-35
lines changed

cmd/geth/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ import _ "net/http/pprof"
4747

4848
const (
4949
ClientIdentifier = "Geth"
50-
Version = "0.9.12"
50+
Version = "0.9.13"
5151
)
5252

5353
var (

core/chain_manager.go

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,9 @@ type ChainManager struct {
7474
eventMux *event.TypeMux
7575
genesisBlock *types.Block
7676
// Last known total difficulty
77-
mu sync.RWMutex
78-
tsmu sync.RWMutex
77+
mu sync.RWMutex
78+
tsmu sync.RWMutex
79+
7980
td *big.Int
8081
currentBlock *types.Block
8182
lastBlockHash common.Hash
@@ -92,15 +93,14 @@ type ChainManager struct {
9293

9394
func NewChainManager(blockDb, stateDb common.Database, mux *event.TypeMux) *ChainManager {
9495
bc := &ChainManager{
95-
blockDb: blockDb,
96-
stateDb: stateDb,
97-
genesisBlock: GenesisBlock(stateDb),
98-
eventMux: mux,
99-
quit: make(chan struct{}),
100-
cache: NewBlockCache(blockCacheLimit),
101-
currentGasLimit: new(big.Int),
96+
blockDb: blockDb,
97+
stateDb: stateDb,
98+
genesisBlock: GenesisBlock(stateDb),
99+
eventMux: mux,
100+
quit: make(chan struct{}),
101+
cache: NewBlockCache(blockCacheLimit),
102102
}
103-
bc.setLastBlock()
103+
bc.setLastState()
104104

105105
// Check the current state of the block hashes and make sure that we do not have any of the bad blocks in our chain
106106
for _, hash := range badHashes {
@@ -145,7 +145,7 @@ func (bc *ChainManager) SetHead(head *types.Block) {
145145
bc.transState = statedb.Copy()
146146
bc.setTotalDifficulty(head.Td)
147147
bc.insert(head)
148-
bc.setLastBlock()
148+
bc.setLastState()
149149
}
150150

151151
func (self *ChainManager) Td() *big.Int {
@@ -212,7 +212,7 @@ func (self *ChainManager) setTransState(statedb *state.StateDB) {
212212
self.transState = statedb
213213
}
214214

215-
func (bc *ChainManager) setLastBlock() {
215+
func (bc *ChainManager) setLastState() {
216216
data, _ := bc.blockDb.Get([]byte("LastBlock"))
217217
if len(data) != 0 {
218218
block := bc.GetBlock(common.BytesToHash(data))
@@ -224,6 +224,7 @@ func (bc *ChainManager) setLastBlock() {
224224
} else {
225225
bc.Reset()
226226
}
227+
bc.currentGasLimit = CalcGasLimit(bc.currentBlock)
227228

228229
if glog.V(logger.Info) {
229230
glog.Infof("Last block (#%v) %x TD=%v\n", bc.currentBlock.Number(), bc.currentBlock.Hash(), bc.td)
@@ -319,6 +320,7 @@ func (bc *ChainManager) ResetWithGenesisBlock(gb *types.Block) {
319320
bc.insert(bc.genesisBlock)
320321
bc.currentBlock = bc.genesisBlock
321322
bc.makeCache()
323+
bc.td = gb.Difficulty()
322324
}
323325

324326
// Export writes the active chain to the given writer.
@@ -346,8 +348,6 @@ func (self *ChainManager) Export(w io.Writer) error {
346348
func (bc *ChainManager) insert(block *types.Block) {
347349
key := append(blockNumPre, block.Number().Bytes()...)
348350
bc.blockDb.Put(key, block.Hash().Bytes())
349-
// Push block to cache
350-
bc.cache.Push(block)
351351

352352
bc.blockDb.Put([]byte("LastBlock"), block.Hash().Bytes())
353353
bc.currentBlock = block
@@ -358,6 +358,8 @@ func (bc *ChainManager) write(block *types.Block) {
358358
enc, _ := rlp.EncodeToBytes((*types.StorageBlock)(block))
359359
key := append(blockHashPre, block.Hash().Bytes()...)
360360
bc.blockDb.Put(key, enc)
361+
// Push block to cache
362+
bc.cache.Push(block)
361363
}
362364

363365
// Accessors
@@ -552,16 +554,17 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
552554
// Compare the TD of the last known block in the canonical chain to make sure it's greater.
553555
// At this point it's possible that a different chain (fork) becomes the new canonical chain.
554556
if block.Td.Cmp(self.td) > 0 {
555-
//if block.Header().Number.Cmp(new(big.Int).Add(cblock.Header().Number, common.Big1)) < 0 {
556-
if block.Number().Cmp(cblock.Number()) <= 0 {
557+
// Check for chain forks. If H(block.num - 1) != block.parent, we're on a fork and need to do some merging
558+
if previous := self.getBlockByNumber(block.NumberU64() - 1); previous.Hash() != block.ParentHash() {
557559
chash := cblock.Hash()
558560
hash := block.Hash()
559561

560562
if glog.V(logger.Info) {
561563
glog.Infof("Split detected. New head #%v (%x) TD=%v, was #%v (%x) TD=%v\n", block.Header().Number, hash[:4], block.Td, cblock.Header().Number, chash[:4], self.td)
562564
}
565+
563566
// during split we merge two different chains and create the new canonical chain
564-
self.merge(self.getBlockByNumber(block.NumberU64()), block)
567+
self.merge(previous, block)
565568

566569
queue[i] = ChainSplitEvent{block, logs}
567570
queueEvent.splitCount++
@@ -587,16 +590,19 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
587590
glog.Infof("inserted block #%d (%d TXs %d UNCs) (%x...)\n", block.Number(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4])
588591
}
589592
} else {
593+
if glog.V(logger.Detail) {
594+
glog.Infof("inserted forked block #%d (%d TXs %d UNCs) (%x...)\n", block.Number(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4])
595+
}
596+
590597
queue[i] = ChainSideEvent{block, logs}
591598
queueEvent.sideCount++
592599
}
600+
self.futureBlocks.Delete(block.Hash())
593601
}
594602
self.mu.Unlock()
595603

596604
stats.processed++
597605

598-
self.futureBlocks.Delete(block.Hash())
599-
600606
}
601607

602608
if (stats.queued > 0 || stats.processed > 0) && bool(glog.V(logger.Info)) {
@@ -610,33 +616,38 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
610616
return nil
611617
}
612618

613-
// merge takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them
619+
// diff takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them
614620
// to be part of the new canonical chain.
615-
func (self *ChainManager) merge(oldBlock, newBlock *types.Block) {
621+
func (self *ChainManager) diff(oldBlock, newBlock *types.Block) types.Blocks {
616622
glog.V(logger.Debug).Infof("Applying diff to %x & %x\n", oldBlock.Hash().Bytes()[:4], newBlock.Hash().Bytes()[:4])
617623

618-
var oldChain, newChain types.Blocks
619-
// First find the split (common ancestor) so we can perform an adequate merge
624+
var newChain types.Blocks
625+
// first find common number
626+
for newBlock = newBlock; newBlock.NumberU64() != oldBlock.NumberU64(); newBlock = self.GetBlock(newBlock.ParentHash()) {
627+
newChain = append(newChain, newBlock)
628+
}
629+
630+
glog.V(logger.Debug).Infoln("Found common number", newBlock.Number())
620631
for {
621-
oldBlock, newBlock = self.GetBlock(oldBlock.ParentHash()), self.GetBlock(newBlock.ParentHash())
622632
if oldBlock.Hash() == newBlock.Hash() {
623633
break
624634
}
625-
oldChain = append(oldChain, oldBlock)
626635
newChain = append(newChain, newBlock)
636+
637+
oldBlock, newBlock = self.GetBlock(oldBlock.ParentHash()), self.GetBlock(newBlock.ParentHash())
627638
}
628639

640+
return newChain
641+
}
642+
643+
// merge merges two different chain to the new canonical chain
644+
func (self *ChainManager) merge(oldBlock, newBlock *types.Block) {
645+
newChain := self.diff(oldBlock, newBlock)
646+
629647
// insert blocks
630648
for _, block := range newChain {
631649
self.insert(block)
632650
}
633-
634-
if glog.V(logger.Detail) {
635-
for i, oldBlock := range oldChain {
636-
glog.Infof("- %.10v = %x\n", oldBlock.Number(), oldBlock.Hash())
637-
glog.Infof("+ %.10v = %x\n", newChain[i].Number(), newChain[i].Hash())
638-
}
639-
}
640651
}
641652

642653
func (self *ChainManager) update() {

core/chain_manager_test.go

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"strconv"
1010
"testing"
1111

12+
"github.com/ethereum/go-ethereum/core/state"
1213
"github.com/ethereum/go-ethereum/core/types"
1314
"github.com/ethereum/go-ethereum/ethdb"
1415
"github.com/ethereum/go-ethereum/event"
@@ -56,12 +57,14 @@ func testFork(t *testing.T, bman *BlockProcessor, i, N int, f func(td1, td2 *big
5657
}
5758
// Compare difficulties
5859
f(tdpre, td)
60+
61+
// Loop over parents making sure reconstruction is done properly
5962
}
6063

6164
func printChain(bc *ChainManager) {
6265
for i := bc.CurrentBlock().Number().Uint64(); i > 0; i-- {
6366
b := bc.GetBlockByNumber(uint64(i))
64-
fmt.Printf("\t%x\n", b.Hash())
67+
fmt.Printf("\t%x %v\n", b.Hash(), b.Difficulty())
6568
}
6669
}
6770

@@ -344,3 +347,50 @@ func TestGetAncestors(t *testing.T) {
344347
ancestors := chainMan.GetAncestors(chain[len(chain)-1], 4)
345348
fmt.Println(ancestors)
346349
}
350+
351+
type bproc struct{}
352+
353+
func (bproc) Process(*types.Block) (state.Logs, error) { return nil, nil }
354+
355+
func makeChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block {
356+
var chain []*types.Block
357+
for i, difficulty := range d {
358+
header := &types.Header{Number: big.NewInt(int64(i + 1)), Difficulty: big.NewInt(int64(difficulty))}
359+
block := types.NewBlockWithHeader(header)
360+
copy(block.HeaderHash[:2], []byte{byte(i + 1), seed})
361+
if i == 0 {
362+
block.ParentHeaderHash = genesis.Hash()
363+
} else {
364+
copy(block.ParentHeaderHash[:2], []byte{byte(i), seed})
365+
}
366+
367+
chain = append(chain, block)
368+
}
369+
return chain
370+
}
371+
372+
func TestReorg(t *testing.T) {
373+
db, _ := ethdb.NewMemDatabase()
374+
var eventMux event.TypeMux
375+
376+
genesis := GenesisBlock(db)
377+
bc := &ChainManager{blockDb: db, stateDb: db, genesisBlock: genesis, eventMux: &eventMux}
378+
bc.cache = NewBlockCache(100)
379+
bc.futureBlocks = NewBlockCache(100)
380+
bc.processor = bproc{}
381+
bc.ResetWithGenesisBlock(genesis)
382+
bc.txState = state.ManageState(bc.State())
383+
384+
chain1 := makeChainWithDiff(genesis, []int{1, 2, 4}, 10)
385+
chain2 := makeChainWithDiff(genesis, []int{1, 2, 3, 4}, 11)
386+
387+
bc.InsertChain(chain1)
388+
bc.InsertChain(chain2)
389+
390+
prev := bc.CurrentBlock()
391+
for block := bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 1); block.NumberU64() != 0; prev, block = block, bc.GetBlockByNumber(block.NumberU64()-1) {
392+
if prev.ParentHash() != block.Hash() {
393+
t.Errorf("parent hash mismatch %x - %x", prev.ParentHash(), block.Hash())
394+
}
395+
}
396+
}

core/types/block.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ func (self *Block) Copy() *Block {
351351
}
352352

353353
func (self *Block) String() string {
354-
return fmt.Sprintf(`Block(#%v): Size: %v TD: %v {
354+
str := fmt.Sprintf(`Block(#%v): Size: %v TD: %v {
355355
MinerHash: %x
356356
%v
357357
Transactions:
@@ -360,6 +360,16 @@ Uncles:
360360
%v
361361
}
362362
`, self.Number(), self.Size(), self.Td, self.header.HashNoNonce(), self.header, self.transactions, self.uncles)
363+
364+
if (self.HeaderHash != common.Hash{}) {
365+
str += fmt.Sprintf("\nFake hash = %x", self.HeaderHash)
366+
}
367+
368+
if (self.ParentHeaderHash != common.Hash{}) {
369+
str += fmt.Sprintf("\nFake parent hash = %x", self.ParentHeaderHash)
370+
}
371+
372+
return str
363373
}
364374

365375
func (self *Header) String() string {

eth/downloader/downloader.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,8 @@ func (d *Downloader) process(peer *peer) error {
436436
if err != nil && core.IsParentErr(err) {
437437
glog.V(logger.Debug).Infoln("Aborting process due to missing parent.")
438438

439+
// XXX this needs a lot of attention
440+
blocks = nil
439441
break
440442
} else if err != nil {
441443
// immediatly unregister the false peer but do not disconnect
@@ -472,3 +474,7 @@ func (d *Downloader) isProcessing() bool {
472474
func (d *Downloader) isBusy() bool {
473475
return d.isFetchingHashes() || d.isDownloadingBlocks() || d.isProcessing()
474476
}
477+
478+
func (d *Downloader) IsBusy() bool {
479+
return d.isBusy()
480+
}

eth/handler.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ func (pm *ProtocolManager) synchronise(peer *peer) {
163163
if peer.td.Cmp(pm.chainman.Td()) <= 0 {
164164
return
165165
}
166+
// Check downloader if it's busy so it doesn't show the sync message
167+
// for every attempty
168+
if pm.downloader.IsBusy() {
169+
return
170+
}
166171

167172
glog.V(logger.Info).Infof("Synchronisation attempt using %s TD=%v\n", peer.id, peer.td)
168173
// Get the hashes from the peer (synchronously)

0 commit comments

Comments
 (0)