Skip to content

Commit c4ed34f

Browse files
Bas van Kervelobscuren
authored andcommitted
[release/1.4.11] core: ensure the canonical block is written before the canonical hash is set
(cherry picked from commit bb8059f) Conflicts: core/blockchain.go core/database_util.go core/headerchain.go eth/filters/filter.go
1 parent 0ab7e90 commit c4ed34f

File tree

5 files changed

+70
-22
lines changed

5 files changed

+70
-22
lines changed

core/blockchain.go

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,14 @@ func (self *BlockChain) WriteBlock(block *types.Block) (status WriteStatus, err
770770
localTd := self.GetTd(self.currentBlock.Hash())
771771
externTd := new(big.Int).Add(block.Difficulty(), ptd)
772772

773+
// Irrelevant of the canonical status, write the block itself to the database
774+
if err := self.hc.WriteTd(block.Hash(), externTd); err != nil {
775+
glog.Fatalf("failed to write block total difficulty: %v", err)
776+
}
777+
if err := WriteBlock(self.chainDb, block); err != nil {
778+
glog.Fatalf("failed to write block contents: %v", err)
779+
}
780+
773781
// If the total difficulty is higher than our known, add it to the canonical chain
774782
// Second clause in the if statement reduces the vulnerability to selfish mining.
775783
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
@@ -780,20 +788,11 @@ func (self *BlockChain) WriteBlock(block *types.Block) (status WriteStatus, err
780788
return NonStatTy, err
781789
}
782790
}
783-
// Insert the block as the new head of the chain
784-
self.insert(block)
791+
self.insert(block) // Insert the block as the new head of the chain
785792
status = CanonStatTy
786793
} else {
787794
status = SideStatTy
788795
}
789-
// Irrelevant of the canonical status, write the block itself to the database
790-
if err := self.hc.WriteTd(block.Hash(), externTd); err != nil {
791-
glog.Fatalf("failed to write block total difficulty: %v", err)
792-
}
793-
if err := WriteBlock(self.chainDb, block); err != nil {
794-
glog.Fatalf("failed to write block contents: %v", err)
795-
}
796-
797796
self.futureBlocks.Remove(block.Hash())
798797

799798
return

core/blockchain_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,3 +1090,41 @@ done:
10901090
}
10911091

10921092
}
1093+
1094+
// Tests if the canonical block can be fetched from the database during chain insertion.
1095+
func TestCanonicalBlockRetrieval(t *testing.T) {
1096+
var (
1097+
db, _ = ethdb.NewMemDatabase()
1098+
genesis = WriteGenesisBlockForTesting(db)
1099+
)
1100+
1101+
evmux := &event.TypeMux{}
1102+
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux)
1103+
1104+
chain, _ := GenerateChain(nil, genesis, db, 10, func(i int, gen *BlockGen) {})
1105+
1106+
for i, _ := range chain {
1107+
go func(block *types.Block) {
1108+
// try to retrieve a block by its canonical hash and see if the block data can be retrieved.
1109+
for {
1110+
ch := GetCanonicalHash(db, block.NumberU64())
1111+
if ch == (common.Hash{}) {
1112+
continue // busy wait for canonical hash to be written
1113+
}
1114+
if ch != block.Hash() {
1115+
t.Fatalf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex())
1116+
}
1117+
fb := GetBlock(db, ch)
1118+
if fb == nil {
1119+
t.Fatalf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex())
1120+
}
1121+
if fb.Hash() != block.Hash() {
1122+
t.Fatalf("invalid block hash for block %d, want %s, got %s", block.NumberU64(), block.Hash().Hex(), fb.Hash().Hex())
1123+
}
1124+
return
1125+
}
1126+
}(chain[i])
1127+
1128+
blockchain.InsertChain(types.Blocks{chain[i]})
1129+
}
1130+
}

core/database_util.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,11 @@ func GetTd(db ethdb.Database, hash common.Hash) *big.Int {
157157
}
158158

159159
// GetBlock retrieves an entire block corresponding to the hash, assembling it
160-
// back from the stored header and body.
160+
// back from the stored header and body. If either the header or body could not
161+
// be retrieved nil is returned.
162+
//
163+
// Note, due to concurrent download of header and block body the header and thus
164+
// canonical hash can be stored in the database but the body data not (yet).
161165
func GetBlock(db ethdb.Database, hash common.Hash) *types.Block {
162166
// Retrieve the block header and body contents
163167
header := GetHeader(db, hash)

core/headerchain.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,14 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
129129
localTd := hc.GetTd(hc.currentHeaderHash)
130130
externTd := new(big.Int).Add(header.Difficulty, ptd)
131131

132+
// Irrelevant of the canonical status, write the td and header to the database
133+
if err := hc.WriteTd(hash, externTd); err != nil {
134+
glog.Fatalf("failed to write header total difficulty: %v", err)
135+
}
136+
if err := WriteHeader(hc.chainDb, header); err != nil {
137+
glog.Fatalf("failed to write header contents: %v", err)
138+
}
139+
132140
// If the total difficulty is higher than our known, add it to the canonical chain
133141
// Second clause in the if statement reduces the vulnerability to selfish mining.
134142
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
@@ -150,26 +158,21 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
150158
headHeader = hc.GetHeader(headHash)
151159
headNumber = headHeader.Number.Uint64()
152160
}
161+
153162
// Extend the canonical chain with the new header
154163
if err := WriteCanonicalHash(hc.chainDb, hash, number); err != nil {
155164
glog.Fatalf("failed to insert header number: %v", err)
156165
}
157166
if err := WriteHeadHeaderHash(hc.chainDb, hash); err != nil {
158167
glog.Fatalf("failed to insert head header hash: %v", err)
159168
}
169+
160170
hc.currentHeaderHash, hc.currentHeader = hash, types.CopyHeader(header)
161171

162172
status = CanonStatTy
163173
} else {
164174
status = SideStatTy
165175
}
166-
// Irrelevant of the canonical status, write the header itself to the database
167-
if err := hc.WriteTd(hash, externTd); err != nil {
168-
glog.Fatalf("failed to write header total difficulty: %v", err)
169-
}
170-
if err := WriteHeader(hc.chainDb, header); err != nil {
171-
glog.Fatalf("failed to write header contents: %v", err)
172-
}
173176
hc.headerCache.Add(hash, header)
174177

175178
return

eth/filters/filter.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,11 @@ func (self *Filter) SetTopics(topics [][]common.Hash) {
7272

7373
// Run filters logs with the current parameters set
7474
func (self *Filter) Find() vm.Logs {
75-
latestBlock := core.GetBlock(self.db, core.GetHeadBlockHash(self.db))
75+
latestHash := core.GetHeadBlockHash(self.db)
76+
latestBlock := core.GetBlock(self.db, latestHash)
77+
if latestBlock == nil {
78+
return vm.Logs{}
79+
}
7680
var beginBlockNo uint64 = uint64(self.begin)
7781
if self.begin == -1 {
7882
beginBlockNo = latestBlock.NumberU64()
@@ -122,13 +126,13 @@ func (self *Filter) mipFind(start, end uint64, depth int) (logs vm.Logs) {
122126
}
123127

124128
func (self *Filter) getLogs(start, end uint64) (logs vm.Logs) {
125-
var block *types.Block
126-
127129
for i := start; i <= end; i++ {
130+
var block *types.Block
128131
hash := core.GetCanonicalHash(self.db, i)
129132
if hash != (common.Hash{}) {
130133
block = core.GetBlock(self.db, hash)
131-
} else { // block not found
134+
}
135+
if block == nil { // block not found/written
132136
return logs
133137
}
134138

0 commit comments

Comments
 (0)