Skip to content

Commit 3fef601

Browse files
committed
Merge pull request #830 from obscuren/downloader-missing-parent
eth/downloader: missing parent improvement
2 parents 764e81b + 30b921e commit 3fef601

File tree

15 files changed

+117
-33
lines changed

15 files changed

+117
-33
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.13"
50+
Version = "0.9.14"
5151
)
5252

5353
var (

cmd/utils/cmd.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ func ImportChain(chainmgr *core.ChainManager, fn string) error {
172172
n++
173173

174174
if n == batchSize {
175-
if err := chainmgr.InsertChain(blocks); err != nil {
175+
if _, err := chainmgr.InsertChain(blocks); err != nil {
176176
return fmt.Errorf("invalid block %v", err)
177177
}
178178
n = 0
@@ -181,7 +181,7 @@ func ImportChain(chainmgr *core.ChainManager, fn string) error {
181181
}
182182

183183
if n > 0 {
184-
if err := chainmgr.InsertChain(blocks[:n]); err != nil {
184+
if _, err := chainmgr.InsertChain(blocks[:n]); err != nil {
185185
return fmt.Errorf("invalid block %v", err)
186186
}
187187
}

core/chain_makers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,6 @@ func newCanonical(n int, db common.Database) (*BlockProcessor, error) {
141141
return bman, nil
142142
}
143143
lchain := makeChain(bman, parent, n, db, CanonicalSeed)
144-
err := bman.bc.InsertChain(lchain)
144+
_, err := bman.bc.InsertChain(lchain)
145145
return bman, err
146146
}

core/chain_manager.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,9 @@ func (self *ChainManager) procFutureBlocks() {
497497
self.InsertChain(blocks)
498498
}
499499

500-
func (self *ChainManager) InsertChain(chain types.Blocks) error {
500+
// InsertChain will attempt to insert the given chain in to the canonical chain or, otherwise, create a fork. It an error is returned
501+
// it will return the index number of the failing block as well an error describing what went wrong (for possible errors see core/errors.go).
502+
func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
501503
// A queued approach to delivering events. This is generally faster than direct delivery and requires much less mutex acquiring.
502504
var (
503505
queue = make([]interface{}, len(chain))
@@ -540,7 +542,7 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
540542
glog.V(logger.Error).Infoln(err)
541543
glog.V(logger.Debug).Infoln(block)
542544

543-
return err
545+
return i, err
544546
}
545547

546548
block.Td = new(big.Int).Set(CalculateTD(block, self.GetBlock(block.ParentHash())))
@@ -591,7 +593,7 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
591593
}
592594
} else {
593595
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])
596+
glog.Infof("inserted forked block #%d (TD=%v) (%d TXs %d UNCs) (%x...)\n", block.Number(), block.Difficulty(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4])
595597
}
596598

597599
queue[i] = ChainSideEvent{block, logs}
@@ -613,7 +615,7 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
613615

614616
go self.eventMux.Post(queueEvent)
615617

616-
return nil
618+
return 0, nil
617619
}
618620

619621
// diff takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them

core/chain_manager_test.go

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

12+
"github.com/ethereum/go-ethereum/common"
1213
"github.com/ethereum/go-ethereum/core/state"
1314
"github.com/ethereum/go-ethereum/core/types"
1415
"github.com/ethereum/go-ethereum/ethdb"
@@ -44,7 +45,7 @@ func testFork(t *testing.T, bman *BlockProcessor, i, N int, f func(td1, td2 *big
4445
// extend the fork
4546
parent := bman2.bc.CurrentBlock()
4647
chainB := makeChain(bman2, parent, N, db, ForkSeed)
47-
err = bman2.bc.InsertChain(chainB)
48+
_, err = bman2.bc.InsertChain(chainB)
4849
if err != nil {
4950
t.Fatal("Insert chain error for fork:", err)
5051
}
@@ -108,7 +109,7 @@ func loadChain(fn string, t *testing.T) (types.Blocks, error) {
108109
}
109110

110111
func insertChain(done chan bool, chainMan *ChainManager, chain types.Blocks, t *testing.T) {
111-
err := chainMan.InsertChain(chain)
112+
_, err := chainMan.InsertChain(chain)
112113
if err != nil {
113114
fmt.Println(err)
114115
t.FailNow()
@@ -369,18 +370,23 @@ func makeChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block
369370
return chain
370371
}
371372

372-
func TestReorg(t *testing.T) {
373-
db, _ := ethdb.NewMemDatabase()
373+
func chm(genesis *types.Block, db common.Database) *ChainManager {
374374
var eventMux event.TypeMux
375-
376-
genesis := GenesisBlock(db)
377375
bc := &ChainManager{blockDb: db, stateDb: db, genesisBlock: genesis, eventMux: &eventMux}
378376
bc.cache = NewBlockCache(100)
379377
bc.futureBlocks = NewBlockCache(100)
380378
bc.processor = bproc{}
381379
bc.ResetWithGenesisBlock(genesis)
382380
bc.txState = state.ManageState(bc.State())
383381

382+
return bc
383+
}
384+
385+
func TestReorgLongest(t *testing.T) {
386+
db, _ := ethdb.NewMemDatabase()
387+
genesis := GenesisBlock(db)
388+
bc := chm(genesis, db)
389+
384390
chain1 := makeChainWithDiff(genesis, []int{1, 2, 4}, 10)
385391
chain2 := makeChainWithDiff(genesis, []int{1, 2, 3, 4}, 11)
386392

@@ -394,3 +400,22 @@ func TestReorg(t *testing.T) {
394400
}
395401
}
396402
}
403+
404+
func TestReorgShortest(t *testing.T) {
405+
db, _ := ethdb.NewMemDatabase()
406+
genesis := GenesisBlock(db)
407+
bc := chm(genesis, db)
408+
409+
chain1 := makeChainWithDiff(genesis, []int{1, 2, 3, 4}, 10)
410+
chain2 := makeChainWithDiff(genesis, []int{1, 10}, 11)
411+
412+
bc.InsertChain(chain1)
413+
bc.InsertChain(chain2)
414+
415+
prev := bc.CurrentBlock()
416+
for block := bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 1); block.NumberU64() != 0; prev, block = block, bc.GetBlockByNumber(block.NumberU64()-1) {
417+
if prev.ParentHash() != block.Hash() {
418+
t.Errorf("parent hash mismatch %x - %x", prev.ParentHash(), block.Hash())
419+
}
420+
}
421+
}

core/transaction_pool.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,27 @@ func (pool *TxPool) checkQueue() {
306306
}
307307
}
308308

309+
func (pool *TxPool) removeTx(hash common.Hash) {
310+
// delete from pending pool
311+
delete(pool.txs, hash)
312+
313+
// delete from queue
314+
out:
315+
for address, txs := range pool.queue {
316+
for i, tx := range txs {
317+
if tx.Hash() == hash {
318+
if len(txs) == 1 {
319+
// if only one tx, remove entire address entry
320+
delete(pool.queue, address)
321+
} else {
322+
pool.queue[address][len(txs)-1], pool.queue[address] = nil, append(txs[:i], txs[i+1:]...)
323+
}
324+
break out
325+
}
326+
}
327+
}
328+
}
329+
309330
func (pool *TxPool) validatePool() {
310331
pool.mu.Lock()
311332
defer pool.mu.Unlock()
@@ -316,7 +337,7 @@ func (pool *TxPool) validatePool() {
316337
glog.Infof("removed tx (%x) from pool: %v\n", hash[:4], err)
317338
}
318339

319-
delete(pool.txs, hash)
340+
pool.removeTx(hash)
320341
}
321342
}
322343
}

core/transaction_pool_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,30 @@ func TestTransactionQueue(t *testing.T) {
111111
t.Error("expected transaction queue to be empty. is", len(pool.queue[from]))
112112
}
113113
}
114+
115+
func TestRemoveTx(t *testing.T) {
116+
pool, key := setupTxPool()
117+
tx := transaction()
118+
tx.SignECDSA(key)
119+
from, _ := tx.From()
120+
pool.currentState().AddBalance(from, big.NewInt(1))
121+
pool.queueTx(tx)
122+
pool.addTx(tx)
123+
if len(pool.queue) != 1 {
124+
t.Error("expected queue to be 1, got", len(pool.queue))
125+
}
126+
127+
if len(pool.txs) != 1 {
128+
t.Error("expected txs to be 1, got", len(pool.txs))
129+
}
130+
131+
pool.removeTx(tx.Hash())
132+
133+
if len(pool.queue) > 0 {
134+
t.Error("expected queue to be 0, got", len(pool.queue))
135+
}
136+
137+
if len(pool.txs) > 0 {
138+
t.Error("expected txs to be 0, got", len(pool.txs))
139+
}
140+
}

core/types/block.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ type Block struct {
9999
Td *big.Int
100100
queued bool // flag for blockpool to skip TD check
101101

102+
ReceivedAt time.Time
103+
102104
receipts Receipts
103105
}
104106

eth/downloader/downloader.go

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ var (
3737
)
3838

3939
type hashCheckFn func(common.Hash) bool
40-
type chainInsertFn func(types.Blocks) error
40+
type chainInsertFn func(types.Blocks) (int, error)
4141
type hashIterFn func() (common.Hash, error)
4242

4343
type blockPack struct {
@@ -418,27 +418,30 @@ func (d *Downloader) process(peer *peer) error {
418418
// link). We should at least check whihc queue match. This code could move
419419
// to a seperate goroutine where it periodically checks for linked pieces.
420420
types.BlockBy(types.Number).Sort(d.queue.blocks)
421-
blocks := d.queue.blocks
422-
if len(blocks) == 0 {
421+
if len(d.queue.blocks) == 0 {
423422
return nil
424423
}
425424

425+
var (
426+
blocks = d.queue.blocks
427+
err error
428+
)
426429
glog.V(logger.Debug).Infof("Inserting chain with %d blocks (#%v - #%v)\n", len(blocks), blocks[0].Number(), blocks[len(blocks)-1].Number())
427430

428-
var err error
429431
// Loop untill we're out of blocks
430432
for len(blocks) != 0 {
431433
max := int(math.Min(float64(len(blocks)), 256))
432434
// TODO check for parent error. When there's a parent error we should stop
433435
// processing and start requesting the `block.hash` so that it's parent and
434436
// grandparents can be requested and queued.
435-
err = d.insertChain(blocks[:max])
437+
var i int
438+
i, err = d.insertChain(blocks[:max])
436439
if err != nil && core.IsParentErr(err) {
437-
glog.V(logger.Debug).Infoln("Aborting process due to missing parent.")
440+
// Ignore the missing blocks. Handler should take care of anything that's missing.
441+
glog.V(logger.Debug).Infof("Ignored block with missing parent (%d)\n", i)
442+
blocks = blocks[i+1:]
438443

439-
// XXX this needs a lot of attention
440-
blocks = nil
441-
break
444+
continue
442445
} else if err != nil {
443446
// immediatly unregister the false peer but do not disconnect
444447
d.UnregisterPeer(d.activePeer)

eth/downloader/downloader_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ func (dl *downloadTester) hasBlock(hash common.Hash) bool {
6262
return false
6363
}
6464

65-
func (dl *downloadTester) insertChain(blocks types.Blocks) error {
65+
func (dl *downloadTester) insertChain(blocks types.Blocks) (int, error) {
6666
dl.insertedBlocks += len(blocks)
6767

68-
return nil
68+
return 0, nil
6969
}
7070

7171
func (dl *downloadTester) getHashes(hash common.Hash) error {

0 commit comments

Comments
 (0)