@@ -11,6 +11,8 @@ import (
11
11
sealing "github.com/onflow/flow-go/engine/consensus"
12
12
"github.com/onflow/flow-go/model/flow"
13
13
"github.com/onflow/flow-go/module"
14
+ "github.com/onflow/flow-go/module/component"
15
+ "github.com/onflow/flow-go/module/irrecoverable"
14
16
"github.com/onflow/flow-go/module/metrics"
15
17
"github.com/onflow/flow-go/network"
16
18
"github.com/onflow/flow-go/network/channels"
@@ -27,7 +29,7 @@ const defaultIncorporatedBlockQueueCapacity = 10
27
29
// Engine is a wrapper struct for `Core` which implements consensus algorithm.
28
30
// Engine is responsible for handling incoming messages, queueing for processing, broadcasting proposals.
29
31
type Engine struct {
30
- unit * engine. Unit
32
+ component. Component
31
33
log zerolog.Logger
32
34
me module.Local
33
35
core sealing.MatchingCore
@@ -69,7 +71,6 @@ func NewEngine(
69
71
70
72
e := & Engine {
71
73
log : log .With ().Str ("engine" , "matching.Engine" ).Logger (),
72
- unit : engine .NewUnit (),
73
74
me : me ,
74
75
core : core ,
75
76
state : state ,
@@ -83,6 +84,12 @@ func NewEngine(
83
84
pendingIncorporatedBlocks : pendingIncorporatedBlocks ,
84
85
}
85
86
87
+ e .Component = component .NewComponentManagerBuilder ().
88
+ AddWorker (e .inboundEventsProcessingLoop ).
89
+ AddWorker (e .finalizationProcessingLoop ).
90
+ AddWorker (e .blockIncorporatedEventsProcessingLoop ).
91
+ Build ()
92
+
86
93
// register engine with the receipt provider
87
94
_ , err = net .Register (channels .ReceiveReceipts , e )
88
95
if err != nil {
@@ -92,79 +99,34 @@ func NewEngine(
92
99
return e , nil
93
100
}
94
101
95
- // Ready returns a ready channel that is closed once the engine has fully
96
- // started. For consensus engine, this is true once the underlying consensus
97
- // algorithm has started.
98
- func (e * Engine ) Ready () <- chan struct {} {
99
- e .unit .Launch (e .inboundEventsProcessingLoop )
100
- e .unit .Launch (e .finalizationProcessingLoop )
101
- e .unit .Launch (e .blockIncorporatedEventsProcessingLoop )
102
- return e .unit .Ready ()
103
- }
104
-
105
- // Done returns a done channel that is closed once the engine has fully stopped.
106
- // For the consensus engine, we wait for hotstuff to finish.
107
- func (e * Engine ) Done () <- chan struct {} {
108
- return e .unit .Done ()
109
- }
110
-
111
- // SubmitLocal submits an event originating on the local node.
112
- func (e * Engine ) SubmitLocal (event interface {}) {
113
- err := e .ProcessLocal (event )
114
- if err != nil {
115
- e .log .Fatal ().Err (err ).Msg ("internal error processing event" )
116
- }
117
- }
118
-
119
- // Submit submits the given event from the node with the given origin ID
120
- // for processing in a non-blocking manner. It returns instantly and logs
121
- // a potential processing error internally when done.
122
- func (e * Engine ) Submit (channel channels.Channel , originID flow.Identifier , event interface {}) {
123
- err := e .Process (channel , originID , event )
124
- if err != nil {
125
- e .log .Fatal ().Err (err ).Msg ("internal error processing event" )
126
- }
127
- }
128
-
129
- // ProcessLocal processes an event originating on the local node.
130
- func (e * Engine ) ProcessLocal (event interface {}) error {
131
- return e .process (e .me .NodeID (), event )
132
- }
133
-
134
- // Process processes the given event from the node with the given origin ID in
135
- // a blocking manner. It returns the potential processing error when done.
102
+ // Process receives events from the network and checks their type,
103
+ // before enqueuing them to be processed by a worker in a non-blocking manner.
104
+ // No errors expected during normal operation (errors are logged instead).
136
105
func (e * Engine ) Process (channel channels.Channel , originID flow.Identifier , event interface {}) error {
137
- err := e .process (originID , event )
138
- if err != nil {
139
- if engine .IsIncompatibleInputTypeError (err ) {
140
- e .log .Warn ().Msgf ("%v delivered unsupported message %T through %v" , originID , event , channel )
141
- return nil
142
- }
143
- return fmt .Errorf ("unexpected error while processing engine message: %w" , err )
106
+ receipt , ok := event .(* flow.ExecutionReceipt )
107
+ if ! ok {
108
+ e .log .Warn ().Msgf ("%v delivered unsupported message %T through %v" , originID , event , channel )
109
+ return nil
144
110
}
111
+ e .addReceiptToQueue (receipt )
145
112
return nil
146
113
}
147
114
148
- // process events for the matching engine on the consensus node.
149
- func (e * Engine ) process (originID flow.Identifier , event interface {}) error {
150
- receipt , ok := event .(* flow.ExecutionReceipt )
151
- if ! ok {
152
- return fmt .Errorf ("no matching processor for message of type %T from origin %x: %w" , event , originID [:],
153
- engine .IncompatibleInputTypeError )
154
- }
115
+ // addReceiptToQueue adds an execution receipt to the queue of the matching engine, to be processed by a worker
116
+ func (e * Engine ) addReceiptToQueue (receipt * flow.ExecutionReceipt ) {
155
117
e .metrics .MessageReceived (metrics .EngineSealing , metrics .MessageExecutionReceipt )
156
118
e .pendingReceipts .Push (receipt )
157
119
e .inboundEventsNotifier .Notify ()
158
- return nil
159
120
}
160
121
161
- // HandleReceipt ingests receipts from the Requester module.
122
+ // HandleReceipt ingests receipts from the Requester module, adding them to the queue .
162
123
func (e * Engine ) HandleReceipt (originID flow.Identifier , receipt flow.Entity ) {
163
124
e .log .Debug ().Msg ("received receipt from requester engine" )
164
- err := e . process ( originID , receipt )
165
- if err != nil {
166
- e .log .Fatal ().Err (err ).Msg ("internal error processing event from requester module" )
125
+ r , ok := receipt .( * flow. ExecutionReceipt )
126
+ if ! ok {
127
+ e .log .Fatal ().Err (engine . IncompatibleInputTypeError ).Msg ("internal error processing event from requester module" )
167
128
}
129
+ e .addReceiptToQueue (r )
168
130
}
169
131
170
132
// OnFinalizedBlock implements the `OnFinalizedBlock` callback from the `hotstuff.FinalizationConsumer`
@@ -183,10 +145,10 @@ func (e *Engine) OnBlockIncorporated(incorporatedBlock *model.Block) {
183
145
}
184
146
185
147
// processIncorporatedBlock selects receipts that were included into incorporated block and submits them
186
- // for further processing by matching core.
148
+ // to the matching core for further processing .
187
149
// Without the logic below, the sealing engine would produce IncorporatedResults
188
150
// only from receipts received directly from ENs. sealing Core would not know about
189
- // Receipts that are incorporated by other nodes in their blocks blocks (but never
151
+ // Receipts that are incorporated by other nodes in their blocks (but never
190
152
// received directly from the EN).
191
153
// No errors expected during normal operations.
192
154
func (e * Engine ) processIncorporatedBlock (blockID flow.Identifier ) error {
@@ -205,61 +167,67 @@ func (e *Engine) processIncorporatedBlock(blockID flow.Identifier) error {
205
167
return nil
206
168
}
207
169
208
- // finalizationProcessingLoop is a separate goroutine that performs processing of finalization events
209
- func (e * Engine ) finalizationProcessingLoop () {
170
+ // finalizationProcessingLoop contains the logic for processing of finalization events.
171
+ // This method is intended to be executed by a dedicated worker / goroutine.
172
+ func (e * Engine ) finalizationProcessingLoop (ctx irrecoverable.SignalerContext , ready component.ReadyFunc ) {
210
173
finalizationNotifier := e .finalizationEventsNotifier .Channel ()
174
+ ready ()
211
175
for {
212
176
select {
213
- case <- e . unit . Quit ():
177
+ case <- ctx . Done ():
214
178
return
215
179
case <- finalizationNotifier :
216
180
err := e .core .OnBlockFinalization ()
217
181
if err != nil {
218
- e . log . Fatal (). Err ( err ). Msg ( "could not process last finalized event" )
182
+ ctx . Throw ( fmt . Errorf ( "could not process last finalized event: %w" , err ) )
219
183
}
220
184
}
221
185
}
222
186
}
223
187
224
- // blockIncorporatedEventsProcessingLoop is a separate goroutine for processing block incorporated events.
225
- func (e * Engine ) blockIncorporatedEventsProcessingLoop () {
188
+ // blockIncorporatedEventsProcessingLoop contains the logic for processing block incorporated events.
189
+ // This method is intended to be executed by a dedicated worker / goroutine.
190
+ func (e * Engine ) blockIncorporatedEventsProcessingLoop (ctx irrecoverable.SignalerContext , ready component.ReadyFunc ) {
226
191
c := e .blockIncorporatedNotifier .Channel ()
227
-
192
+ ready ()
228
193
for {
229
194
select {
230
- case <- e . unit . Quit ():
195
+ case <- ctx . Done ():
231
196
return
232
197
case <- c :
233
- err := e .processBlockIncorporatedEvents ()
198
+ err := e .processBlockIncorporatedEvents (ctx )
234
199
if err != nil {
235
- e . log . Fatal (). Err ( err ). Msg ( "internal error processing block incorporated queued message" )
200
+ ctx . Throw ( fmt . Errorf ( "internal error processing block incorporated queued message: %w" , err ) )
236
201
}
237
202
}
238
203
}
239
204
}
240
205
241
- func (e * Engine ) inboundEventsProcessingLoop () {
206
+ // inboundEventsProcessingLoop contains the logic for processing execution receipts, received
207
+ // from the network via Process, from the Requester module via HandleReceipt, or from incorporated blocks.
208
+ // This method is intended to be executed by a dedicated worker / goroutine.
209
+ func (e * Engine ) inboundEventsProcessingLoop (ctx irrecoverable.SignalerContext , ready component.ReadyFunc ) {
242
210
c := e .inboundEventsNotifier .Channel ()
243
-
211
+ ready ()
244
212
for {
245
213
select {
246
- case <- e . unit . Quit ():
214
+ case <- ctx . Done ():
247
215
return
248
216
case <- c :
249
- err := e .processAvailableEvents ( )
217
+ err := e .processExecutionReceipts ( ctx )
250
218
if err != nil {
251
- e . log . Fatal (). Err ( err ). Msg ( "internal error processing queued message" )
219
+ ctx . Throw ( fmt . Errorf ( "internal error processing queued execution receipt: %w" , err ) )
252
220
}
253
221
}
254
222
}
255
223
}
256
224
257
225
// processBlockIncorporatedEvents performs processing of block incorporated hot stuff events.
258
226
// No errors expected during normal operations.
259
- func (e * Engine ) processBlockIncorporatedEvents () error {
227
+ func (e * Engine ) processBlockIncorporatedEvents (ctx irrecoverable. SignalerContext ) error {
260
228
for {
261
229
select {
262
- case <- e . unit . Quit ():
230
+ case <- ctx . Done ():
263
231
return nil
264
232
default :
265
233
}
@@ -279,27 +247,18 @@ func (e *Engine) processBlockIncorporatedEvents() error {
279
247
}
280
248
}
281
249
282
- // processAvailableEvents processes _all_ available events (untrusted messages
250
+ // processExecutionReceipts processes execution receipts
283
251
// from other nodes as well as internally trusted.
284
252
// No errors expected during normal operations.
285
- func (e * Engine ) processAvailableEvents ( ) error {
253
+ func (e * Engine ) processExecutionReceipts ( ctx irrecoverable. SignalerContext ) error {
286
254
for {
287
255
select {
288
- case <- e . unit . Quit ():
256
+ case <- ctx . Done ():
289
257
return nil
290
258
default :
291
259
}
292
260
293
- msg , ok := e .pendingIncorporatedBlocks .Pop ()
294
- if ok {
295
- err := e .processIncorporatedBlock (msg .(flow.Identifier ))
296
- if err != nil {
297
- return fmt .Errorf ("could not process incorporated block: %w" , err )
298
- }
299
- continue
300
- }
301
-
302
- msg , ok = e .pendingReceipts .Pop ()
261
+ msg , ok := e .pendingReceipts .Pop ()
303
262
if ok {
304
263
err := e .core .ProcessReceipt (msg .(* flow.ExecutionReceipt ))
305
264
if err != nil {
0 commit comments