Skip to content

Commit 8938768

Browse files
karalabeobscuren
authored andcommitted
core, eth/downloader: ensure state presence in ancestor lookup
1 parent 5490437 commit 8938768

File tree

6 files changed

+85
-61
lines changed

6 files changed

+85
-61
lines changed

core/blockchain.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,19 @@ func (bc *BlockChain) HasBlock(hash common.Hash) bool {
591591
return bc.GetBlock(hash) != nil
592592
}
593593

594+
// HasBlockAndState checks if a block and associated state trie is fully present
595+
// in the database or not, caching it if present.
596+
func (bc *BlockChain) HasBlockAndState(hash common.Hash) bool {
597+
// Check first that the block itself is known
598+
block := bc.GetBlock(hash)
599+
if block == nil {
600+
return false
601+
}
602+
// Ensure the associated state is also present
603+
_, err := state.New(block.Root(), bc.chainDb)
604+
return err == nil
605+
}
606+
594607
// GetBlock retrieves a block from the database by hash, caching it if found.
595608
func (self *BlockChain) GetBlock(hash common.Hash) *types.Block {
596609
// Short circuit if the block's already in the cache, retrieve otherwise

core/state/statedb.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ type StateDB struct {
5555
func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
5656
tr, err := trie.NewSecure(root, db)
5757
if err != nil {
58-
glog.Errorf("can't create state trie with root %x: %v", root[:], err)
5958
return nil, err
6059
}
6160
return &StateDB{

eth/downloader/downloader.go

Lines changed: 52 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -112,20 +112,20 @@ type Downloader struct {
112112
syncStatsLock sync.RWMutex // Lock protecting the sync stats fields
113113

114114
// Callbacks
115-
hasHeader headerCheckFn // Checks if a header is present in the chain
116-
hasBlock blockCheckFn // Checks if a block is present in the chain
117-
getHeader headerRetrievalFn // Retrieves a header from the chain
118-
getBlock blockRetrievalFn // Retrieves a block from the chain
119-
headHeader headHeaderRetrievalFn // Retrieves the head header from the chain
120-
headBlock headBlockRetrievalFn // Retrieves the head block from the chain
121-
headFastBlock headFastBlockRetrievalFn // Retrieves the head fast-sync block from the chain
122-
commitHeadBlock headBlockCommitterFn // Commits a manually assembled block as the chain head
123-
getTd tdRetrievalFn // Retrieves the TD of a block from the chain
124-
insertHeaders headerChainInsertFn // Injects a batch of headers into the chain
125-
insertBlocks blockChainInsertFn // Injects a batch of blocks into the chain
126-
insertReceipts receiptChainInsertFn // Injects a batch of blocks and their receipts into the chain
127-
rollback chainRollbackFn // Removes a batch of recently added chain links
128-
dropPeer peerDropFn // Drops a peer for misbehaving
115+
hasHeader headerCheckFn // Checks if a header is present in the chain
116+
hasBlockAndState blockAndStateCheckFn // Checks if a block and associated state is present in the chain
117+
getHeader headerRetrievalFn // Retrieves a header from the chain
118+
getBlock blockRetrievalFn // Retrieves a block from the chain
119+
headHeader headHeaderRetrievalFn // Retrieves the head header from the chain
120+
headBlock headBlockRetrievalFn // Retrieves the head block from the chain
121+
headFastBlock headFastBlockRetrievalFn // Retrieves the head fast-sync block from the chain
122+
commitHeadBlock headBlockCommitterFn // Commits a manually assembled block as the chain head
123+
getTd tdRetrievalFn // Retrieves the TD of a block from the chain
124+
insertHeaders headerChainInsertFn // Injects a batch of headers into the chain
125+
insertBlocks blockChainInsertFn // Injects a batch of blocks into the chain
126+
insertReceipts receiptChainInsertFn // Injects a batch of blocks and their receipts into the chain
127+
rollback chainRollbackFn // Removes a batch of recently added chain links
128+
dropPeer peerDropFn // Drops a peer for misbehaving
129129

130130
// Status
131131
synchroniseMock func(id string, hash common.Hash) error // Replacement for synchronise during testing
@@ -156,41 +156,41 @@ type Downloader struct {
156156
}
157157

158158
// New creates a new downloader to fetch hashes and blocks from remote peers.
159-
func New(stateDb ethdb.Database, mux *event.TypeMux, hasHeader headerCheckFn, hasBlock blockCheckFn, getHeader headerRetrievalFn,
160-
getBlock blockRetrievalFn, headHeader headHeaderRetrievalFn, headBlock headBlockRetrievalFn, headFastBlock headFastBlockRetrievalFn,
161-
commitHeadBlock headBlockCommitterFn, getTd tdRetrievalFn, insertHeaders headerChainInsertFn, insertBlocks blockChainInsertFn,
162-
insertReceipts receiptChainInsertFn, rollback chainRollbackFn, dropPeer peerDropFn) *Downloader {
159+
func New(stateDb ethdb.Database, mux *event.TypeMux, hasHeader headerCheckFn, hasBlockAndState blockAndStateCheckFn,
160+
getHeader headerRetrievalFn, getBlock blockRetrievalFn, headHeader headHeaderRetrievalFn, headBlock headBlockRetrievalFn,
161+
headFastBlock headFastBlockRetrievalFn, commitHeadBlock headBlockCommitterFn, getTd tdRetrievalFn, insertHeaders headerChainInsertFn,
162+
insertBlocks blockChainInsertFn, insertReceipts receiptChainInsertFn, rollback chainRollbackFn, dropPeer peerDropFn) *Downloader {
163163

164164
return &Downloader{
165-
mode: FullSync,
166-
mux: mux,
167-
queue: newQueue(stateDb),
168-
peers: newPeerSet(),
169-
hasHeader: hasHeader,
170-
hasBlock: hasBlock,
171-
getHeader: getHeader,
172-
getBlock: getBlock,
173-
headHeader: headHeader,
174-
headBlock: headBlock,
175-
headFastBlock: headFastBlock,
176-
commitHeadBlock: commitHeadBlock,
177-
getTd: getTd,
178-
insertHeaders: insertHeaders,
179-
insertBlocks: insertBlocks,
180-
insertReceipts: insertReceipts,
181-
rollback: rollback,
182-
dropPeer: dropPeer,
183-
newPeerCh: make(chan *peer, 1),
184-
hashCh: make(chan dataPack, 1),
185-
blockCh: make(chan dataPack, 1),
186-
headerCh: make(chan dataPack, 1),
187-
bodyCh: make(chan dataPack, 1),
188-
receiptCh: make(chan dataPack, 1),
189-
stateCh: make(chan dataPack, 1),
190-
blockWakeCh: make(chan bool, 1),
191-
bodyWakeCh: make(chan bool, 1),
192-
receiptWakeCh: make(chan bool, 1),
193-
stateWakeCh: make(chan bool, 1),
165+
mode: FullSync,
166+
mux: mux,
167+
queue: newQueue(stateDb),
168+
peers: newPeerSet(),
169+
hasHeader: hasHeader,
170+
hasBlockAndState: hasBlockAndState,
171+
getHeader: getHeader,
172+
getBlock: getBlock,
173+
headHeader: headHeader,
174+
headBlock: headBlock,
175+
headFastBlock: headFastBlock,
176+
commitHeadBlock: commitHeadBlock,
177+
getTd: getTd,
178+
insertHeaders: insertHeaders,
179+
insertBlocks: insertBlocks,
180+
insertReceipts: insertReceipts,
181+
rollback: rollback,
182+
dropPeer: dropPeer,
183+
newPeerCh: make(chan *peer, 1),
184+
hashCh: make(chan dataPack, 1),
185+
blockCh: make(chan dataPack, 1),
186+
headerCh: make(chan dataPack, 1),
187+
bodyCh: make(chan dataPack, 1),
188+
receiptCh: make(chan dataPack, 1),
189+
stateCh: make(chan dataPack, 1),
190+
blockWakeCh: make(chan bool, 1),
191+
bodyWakeCh: make(chan bool, 1),
192+
receiptWakeCh: make(chan bool, 1),
193+
stateWakeCh: make(chan bool, 1),
194194
}
195195
}
196196

@@ -564,7 +564,7 @@ func (d *Downloader) findAncestor61(p *peer) (uint64, error) {
564564
// Check if a common ancestor was found
565565
finished = true
566566
for i := len(hashes) - 1; i >= 0; i-- {
567-
if d.hasBlock(hashes[i]) {
567+
if d.hasBlockAndState(hashes[i]) {
568568
number, hash = uint64(from)+uint64(i), hashes[i]
569569
break
570570
}
@@ -620,11 +620,11 @@ func (d *Downloader) findAncestor61(p *peer) (uint64, error) {
620620
arrived = true
621621

622622
// Modify the search interval based on the response
623-
block := d.getBlock(hashes[0])
624-
if block == nil {
623+
if !d.hasBlockAndState(hashes[0]) {
625624
end = check
626625
break
627626
}
627+
block := d.getBlock(hashes[0]) // this doesn't check state, hence the above explicit check
628628
if block.NumberU64() != check {
629629
glog.V(logger.Debug).Infof("%v: non requested hash #%d [%x…], instead of #%d", p, block.NumberU64(), block.Hash().Bytes()[:4], check)
630630
return 0, errBadPeer
@@ -989,7 +989,7 @@ func (d *Downloader) findAncestor(p *peer) (uint64, error) {
989989
// Check if a common ancestor was found
990990
finished = true
991991
for i := len(headers) - 1; i >= 0; i-- {
992-
if (d.mode != LightSync && d.hasBlock(headers[i].Hash())) || (d.mode == LightSync && d.hasHeader(headers[i].Hash())) {
992+
if (d.mode != LightSync && d.hasBlockAndState(headers[i].Hash())) || (d.mode == LightSync && d.hasHeader(headers[i].Hash())) {
993993
number, hash = headers[i].Number.Uint64(), headers[i].Hash()
994994
break
995995
}
@@ -1045,7 +1045,7 @@ func (d *Downloader) findAncestor(p *peer) (uint64, error) {
10451045
arrived = true
10461046

10471047
// Modify the search interval based on the response
1048-
if (d.mode == FullSync && !d.hasBlock(headers[0].Hash())) || (d.mode != FullSync && !d.hasHeader(headers[0].Hash())) {
1048+
if (d.mode == FullSync && !d.hasBlockAndState(headers[0].Hash())) || (d.mode != FullSync && !d.hasHeader(headers[0].Hash())) {
10491049
end = check
10501050
break
10511051
}

eth/downloader/downloader_test.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ func newTester() *downloadTester {
150150
peerChainTds: make(map[string]map[common.Hash]*big.Int),
151151
}
152152
tester.stateDb, _ = ethdb.NewMemDatabase()
153+
tester.stateDb.Put(genesis.Root().Bytes(), []byte{0x00})
154+
153155
tester.downloader = New(tester.stateDb, new(event.TypeMux), tester.hasHeader, tester.hasBlock, tester.getHeader,
154156
tester.getBlock, tester.headHeader, tester.headBlock, tester.headFastBlock, tester.commitHeadBlock, tester.getTd,
155157
tester.insertHeaders, tester.insertBlocks, tester.insertReceipts, tester.rollback, tester.dropPeer)
@@ -177,9 +179,14 @@ func (dl *downloadTester) hasHeader(hash common.Hash) bool {
177179
return dl.getHeader(hash) != nil
178180
}
179181

180-
// hasBlock checks if a block is present in the testers canonical chain.
182+
// hasBlock checks if a block and associated state is present in the testers canonical chain.
181183
func (dl *downloadTester) hasBlock(hash common.Hash) bool {
182-
return dl.getBlock(hash) != nil
184+
block := dl.getBlock(hash)
185+
if block == nil {
186+
return false
187+
}
188+
_, err := dl.stateDb.Get(block.Root().Bytes())
189+
return err == nil
183190
}
184191

185192
// getHeader retrieves a header from the testers canonical chain.
@@ -292,8 +299,10 @@ func (dl *downloadTester) insertBlocks(blocks types.Blocks) (int, error) {
292299
defer dl.lock.Unlock()
293300

294301
for i, block := range blocks {
295-
if _, ok := dl.ownBlocks[block.ParentHash()]; !ok {
302+
if parent, ok := dl.ownBlocks[block.ParentHash()]; !ok {
296303
return i, errors.New("unknown parent")
304+
} else if _, err := dl.stateDb.Get(parent.Root().Bytes()); err != nil {
305+
return i, fmt.Errorf("unknown parent state %x: %v", parent.Root(), err)
297306
}
298307
if _, ok := dl.ownHeaders[block.Hash()]; !ok {
299308
dl.ownHashes = append(dl.ownHashes, block.Hash())
@@ -1102,6 +1111,8 @@ func testShiftedHeaderAttack(t *testing.T, protocol int, mode SyncMode) {
11021111
}
11031112

11041113
// Tests that upon detecting an invalid header, the recent ones are rolled back
1114+
// for various failure scenarios. Afterwards a full sync is attempted to make
1115+
// sure no state was corrupted.
11051116
func TestInvalidHeaderRollback63Fast(t *testing.T) { testInvalidHeaderRollback(t, 63, FastSync) }
11061117
func TestInvalidHeaderRollback64Fast(t *testing.T) { testInvalidHeaderRollback(t, 64, FastSync) }
11071118
func TestInvalidHeaderRollback64Light(t *testing.T) { testInvalidHeaderRollback(t, 64, LightSync) }

eth/downloader/types.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ import (
2727
// headerCheckFn is a callback type for verifying a header's presence in the local chain.
2828
type headerCheckFn func(common.Hash) bool
2929

30-
// blockCheckFn is a callback type for verifying a block's presence in the local chain.
31-
type blockCheckFn func(common.Hash) bool
30+
// blockAndStateCheckFn is a callback type for verifying block and associated states' presence in the local chain.
31+
type blockAndStateCheckFn func(common.Hash) bool
3232

3333
// headerRetrievalFn is a callback type for retrieving a header from the local chain.
3434
type headerRetrievalFn func(common.Hash) *types.Header

eth/handler.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,10 @@ func NewProtocolManager(fastSync bool, networkId int, mux *event.TypeMux, txpool
138138
return nil, errIncompatibleConfig
139139
}
140140
// Construct the different synchronisation mechanisms
141-
manager.downloader = downloader.New(chaindb, manager.eventMux, blockchain.HasHeader, blockchain.HasBlock, blockchain.GetHeader, blockchain.GetBlock,
142-
blockchain.CurrentHeader, blockchain.CurrentBlock, blockchain.CurrentFastBlock, blockchain.FastSyncCommitHead, blockchain.GetTd,
143-
blockchain.InsertHeaderChain, blockchain.InsertChain, blockchain.InsertReceiptChain, blockchain.Rollback, manager.removePeer)
141+
manager.downloader = downloader.New(chaindb, manager.eventMux, blockchain.HasHeader, blockchain.HasBlockAndState, blockchain.GetHeader,
142+
blockchain.GetBlock, blockchain.CurrentHeader, blockchain.CurrentBlock, blockchain.CurrentFastBlock, blockchain.FastSyncCommitHead,
143+
blockchain.GetTd, blockchain.InsertHeaderChain, blockchain.InsertChain, blockchain.InsertReceiptChain, blockchain.Rollback,
144+
manager.removePeer)
144145

145146
validator := func(block *types.Block, parent *types.Block) error {
146147
return core.ValidateHeader(pow, block.Header(), parent.Header(), true, false)

0 commit comments

Comments
 (0)