11package dbft
22
33import (
4- "errors"
54 "fmt"
6- "github.com/ethereum/go-ethereum/core/state"
7- "sync"
85
9- "github.com/ethereum/go-ethereum/common"
6+ "github.com/ethereum/go-ethereum/core"
7+ "github.com/ethereum/go-ethereum/core/state"
108 "github.com/ethereum/go-ethereum/core/types"
9+ "github.com/ethereum/go-ethereum/event"
1110 "github.com/ethereum/go-ethereum/log"
1211)
1312
14- const (
15- // blockQueueCap is the number of tasks blockQueue can fit at once. It's OK for
16- // the blockQueue not to have a proper task for the newly-created block, and
17- // normally a single task is expected to be present in blockQueue. But we still
18- // need blockQueueCap restriction for the case of endless change views.
19- blockQueueCap = 100
20-
21- clearAllMatchingTasks = - 1
22- )
23-
24- // blockQueue is an entity that collects sealed blocks from dBFT and routs these
25- // blocks to a proper place (either to miner or directly to chain).
13+ // blockQueue is an entity that collects sealed blocks from dBFT and stores these
14+ // blocks in the chain.
2615type blockQueue struct {
27- chain ChainHeaderWriter
28- tasksLock sync.RWMutex
29- tasks map [common.Hash ]task
30- }
31-
32- // task holds information about miner sealing task.
33- type task struct {
34- height uint64
35- resCh chan <- * types.Block
36- cancelCh <- chan struct {}
16+ chain ChainHeaderWriter
17+ mux * event.TypeMux
3718}
3819
3920// newBlockQueue creates an instance of blockQueue. It's not ready for usage until
4021// an instance of ChainHeaderWriter is properly set.
4122func newBlockQueue () * blockQueue {
42- return & blockQueue {
43- tasks : make (map [common.Hash ]task ),
44- }
23+ return & blockQueue {}
4524}
4625
4726// SetChain initializes ChainHeaderWriter instanse needed for proper blockQueue
@@ -50,54 +29,15 @@ func (bq *blockQueue) SetChain(chain ChainHeaderWriter) {
5029 bq .chain = chain
5130}
5231
32+ // SetMux initializes mux instanse needed for proper blockQueue functioning.
33+ func (bq * blockQueue ) SetMux (mux * event.TypeMux ) {
34+ bq .mux = mux
35+ }
36+
5337// PutBlock routs block either to miner or (if there's no suitable sealing task)
5438// directly to blockchain. No block verification is performed, it is assumed that
5539// provided block is sealed and valid.
5640func (bq * blockQueue ) PutBlock (b * types.Block , state * state.StateDB , receipts []* types.Receipt ) error {
57- h := WorkerSealHash (b .Header ())
58-
59- bq .tasksLock .Lock ()
60- task , ok := bq .tasks [h ]
61-
62- bq .clearStaleTasks (b .NumberU64 (), clearAllMatchingTasks )
63-
64- if ok {
65- var (
66- err error
67- readByMiner bool
68- )
69- select {
70- case <- task .cancelCh :
71- case task .resCh <- b :
72- readByMiner = true
73- default :
74- err = errors .New ("sealing result is not read by miner, trying to insert block in chain manually" )
75- }
76- delete (bq .tasks , h )
77-
78- if readByMiner {
79- bq .tasksLock .Unlock ()
80- return nil
81- }
82-
83- if err != nil {
84- log .Warn (err .Error (),
85- "number" , b .Number (),
86- "seal hash" , h .String (),
87- "hash" , b .Hash ().String (),
88- )
89- }
90- }
91- bq .tasksLock .Unlock ()
92-
93- // If we're here then we're OK with that, it just means that:
94- // 1) either dBFT received some extra commits and trying to
95- // send already constructed block one more time
96- // 2) or worker has received block with the same index via network. Then
97- // we still need to save the block in case it has different hash.
98- // 3) or we're not a primary node in this consensus round and thus,
99- // worker's task differs from the dBFT's proposal. In this case we
100- // need to try to insert block right into chain.
10141 hash := b .Hash ()
10242 if bq .chain .HasBlock (hash , b .NumberU64 ()) {
10343 return nil
@@ -110,9 +50,14 @@ func (bq *blockQueue) PutBlock(b *types.Block, state *state.StateDB, receipts []
11050 return fmt .Errorf ("failed to insert block into chain: %w" , err )
11151 }
11252 log .Info ("Successfully inserted new block" , "number" , b .Number (), "hash" , hash )
53+
54+ // Broadcast the block and announce chain insertion event
55+ bq .mux .Post (core.NewMinedBlockEvent {Block : b })
56+
11357 return nil
11458 }
11559
60+ currH := bq .chain .CurrentBlock ().Number
11661 // Insert state directly if we have one.
11762 var logs []* types.Log
11863 for i , receipt := range receipts {
@@ -129,7 +74,7 @@ func (bq *blockQueue) PutBlock(b *types.Block, state *state.StateDB, receipts []
12974 logs = append (logs , receipt .Logs ... )
13075 }
13176 // Commit block and state to database.
132- _ , err := bq .chain .WriteBlockAndSetHead (b , receipts , logs , state , true )
77+ _ , err := bq .chain .WriteBlockAndSetHead (b , receipts , logs , state , b . Number (). Cmp ( currH ) > 0 )
13378 if err != nil {
13479 log .Error ("Failed to write block to chain and set head" ,
13580 "number" , b .NumberU64 (),
@@ -138,51 +83,8 @@ func (bq *blockQueue) PutBlock(b *types.Block, state *state.StateDB, receipts []
13883 }
13984 log .Info ("Successfully wrote new block with state" , "number" , b .Number (), "hash" , hash )
14085
141- return nil
142- }
143-
144- // ClearStaleTasks removes all stale tasks up to the specified height (including
145- // the height itself).
146- func (bq * blockQueue ) ClearStaleTasks (till uint64 ) {
147- bq .tasksLock .Lock ()
148- defer bq .tasksLock .Unlock ()
149-
150- bq .clearStaleTasks (till , clearAllMatchingTasks )
151- }
152-
153- // clearStaleTasks removes all stale tasks up to the specified height (including
154- // the height itself). It doesn't hold tasksLock, so it's the caller's responsibility.
155- func (bq * blockQueue ) clearStaleTasks (till uint64 , count int ) {
156- for h , task := range bq .tasks {
157- if task .height <= till {
158- delete (bq .tasks , h )
159- if count != clearAllMatchingTasks {
160- count --
161- if count <= 0 {
162- break
163- }
164- }
165- }
166- }
167- }
168-
169- // SubmitTask adds subsequent miner task to the blockqueue instance.
170- func (bq * blockQueue ) SubmitTask (sealHash common.Hash , number uint64 , resCh chan <- * types.Block , cancelCh <- chan struct {}) {
171- bq .tasksLock .Lock ()
172- defer bq .tasksLock .Unlock ()
86+ // Broadcast the block and announce chain insertion event
87+ bq .mux .Post (core.NewMinedBlockEvent {Block : b })
17388
174- // We're OK with the fact that capacity is reached, remove random outdated seal
175- // task (it's likely won't be completed, and if it will, then the block will be
176- // inserted to the chain directly).
177- if len (bq .tasks ) == blockQueueCap {
178- bq .clearStaleTasks (number , 1 )
179- }
180-
181- // Do not check the existing task with the same hash. It could happen that new
182- // sealing task has the same hash after ChangeView sealing proposal initialisation.
183- bq .tasks [sealHash ] = task {
184- height : number ,
185- resCh : resCh ,
186- cancelCh : cancelCh ,
187- }
89+ return nil
18890}
0 commit comments