@@ -37,6 +37,9 @@ import (
37
37
// stateSyncStage is a type of state synchronisation stage.
38
38
type stateSyncStage uint8
39
39
40
+ // OnStageChanged is a callback function that is called when state sync stage is changed.
41
+ type OnStageChanged func ()
42
+
40
43
const (
41
44
// inactive means that state exchange is disabled by the protocol configuration.
42
45
// Can't be combined with other states.
@@ -93,6 +96,10 @@ type Module struct {
93
96
billet * mpt.Billet
94
97
95
98
jumpCallback func (p uint32 ) error
99
+
100
+ // stageCB is an optional callback that is triggered whenever
101
+ // the sync stage changes.
102
+ stageCB func ()
96
103
}
97
104
98
105
// NewModule returns new instance of statesync module.
@@ -120,6 +127,7 @@ func NewModule(bc Ledger, stateMod *stateroot.Module, log *zap.Logger, s *dao.Si
120
127
// Init initializes state sync module for the current chain's height with given
121
128
// callback for MPT nodes requests.
122
129
func (s * Module ) Init (currChainHeight uint32 ) error {
130
+ oldStage := s .syncStage
123
131
s .lock .Lock ()
124
132
defer s .lock .Unlock ()
125
133
@@ -131,6 +139,9 @@ func (s *Module) Init(currChainHeight uint32) error {
131
139
if p < 2 * s .syncInterval {
132
140
// chain is too low to start state exchange process, use the standard sync mechanism
133
141
s .syncStage = inactive
142
+ if s .syncStage != oldStage {
143
+ s .notifyStageChanged ()
144
+ }
134
145
return nil
135
146
}
136
147
pOld , err := s .dao .GetStateSyncPoint ()
@@ -142,6 +153,9 @@ func (s *Module) Init(currChainHeight uint32) error {
142
153
// chain has already been synchronised up to old state sync point and regular blocks processing was started.
143
154
// Current block height is enough to start regular blocks processing.
144
155
s .syncStage = inactive
156
+ if s .syncStage != oldStage {
157
+ s .notifyStageChanged ()
158
+ }
145
159
return nil
146
160
}
147
161
if err == nil {
@@ -172,10 +186,26 @@ func (s *Module) Init(currChainHeight uint32) error {
172
186
s .log .Info ("try to sync state for the latest state synchronisation point" ,
173
187
zap .Uint32 ("point" , p ),
174
188
zap .Uint32 ("evaluated chain's blockHeight" , currChainHeight ))
175
-
189
+ if s .syncStage != oldStage {
190
+ s .notifyStageChanged ()
191
+ }
176
192
return s .defineSyncStage ()
177
193
}
178
194
195
+ // SetOnStageChanged sets callback that is triggered whenever the sync stage changes.
196
+ func (s * Module ) SetOnStageChanged (cb func ()) {
197
+ s .lock .Lock ()
198
+ defer s .lock .Unlock ()
199
+ s .stageCB = cb
200
+ }
201
+
202
+ // notifyStageChanged triggers stage callback if it's set.
203
+ func (s * Module ) notifyStageChanged () {
204
+ if s .stageCB != nil {
205
+ go s .stageCB ()
206
+ }
207
+ }
208
+
179
209
// TemporaryPrefix accepts current storage prefix and returns prefix
180
210
// to use for storing intermediate items during synchronization.
181
211
func TemporaryPrefix (currPrefix storage.KeyPrefix ) storage.KeyPrefix {
@@ -192,12 +222,17 @@ func TemporaryPrefix(currPrefix storage.KeyPrefix) storage.KeyPrefix {
192
222
// defineSyncStage sequentially checks and sets sync state process stage after Module
193
223
// initialization. It also performs initialization of MPT Billet if necessary.
194
224
func (s * Module ) defineSyncStage () error {
225
+ oldStage := s .syncStage
195
226
// check headers sync stage first
196
227
ltstHeaderHeight := s .bc .HeaderHeight ()
197
228
if ltstHeaderHeight > s .syncPoint {
198
229
s .syncStage = headersSynced
199
230
s .log .Info ("headers are in sync" ,
200
231
zap .Uint32 ("headerHeight" , s .bc .HeaderHeight ()))
232
+ if s .syncStage != oldStage {
233
+ s .notifyStageChanged ()
234
+ oldStage = s .syncStage
235
+ }
201
236
}
202
237
203
238
// check blocks sync stage
@@ -206,13 +241,21 @@ func (s *Module) defineSyncStage() error {
206
241
s .syncStage |= blocksSynced
207
242
s .log .Info ("blocks are in sync" ,
208
243
zap .Uint32 ("blockHeight" , s .blockHeight ))
244
+ if s .syncStage != oldStage {
245
+ s .notifyStageChanged ()
246
+ oldStage = s .syncStage
247
+ }
209
248
}
210
249
211
250
// check MPT sync stage
212
251
if s .blockHeight > s .syncPoint {
213
252
s .syncStage |= mptSynced
214
253
s .log .Info ("MPT is in sync" ,
215
254
zap .Uint32 ("stateroot height" , s .stateMod .CurrentLocalHeight ()))
255
+ if s .syncStage != oldStage {
256
+ s .notifyStageChanged ()
257
+ oldStage = s .syncStage
258
+ }
216
259
} else if s .syncStage & headersSynced != 0 {
217
260
header , err := s .bc .GetHeader (s .bc .GetHeaderHash (s .syncPoint + 1 ))
218
261
if err != nil {
@@ -255,12 +298,19 @@ func (s *Module) defineSyncStage() error {
255
298
s .syncStage |= mptSynced
256
299
s .log .Info ("MPT is in sync" ,
257
300
zap .Uint32 ("stateroot height" , s .syncPoint ))
301
+ if s .syncStage != oldStage {
302
+ s .notifyStageChanged ()
303
+ oldStage = s .syncStage
304
+ }
258
305
}
259
306
}
260
307
261
308
if s .syncStage == headersSynced | blocksSynced | mptSynced {
262
309
s .log .Info ("state is in sync, starting regular blocks processing" )
263
310
s .syncStage = inactive
311
+ if s .syncStage != oldStage {
312
+ s .notifyStageChanged ()
313
+ }
264
314
}
265
315
return nil
266
316
}
@@ -306,6 +356,7 @@ func (s *Module) AddHeaders(hdrs ...*block.Header) error {
306
356
307
357
// AddBlock verifies and saves block skipping executable scripts.
308
358
func (s * Module ) AddBlock (block * block.Block ) error {
359
+ oldStage := s .syncStage
309
360
s .lock .Lock ()
310
361
defer s .lock .Unlock ()
311
362
@@ -351,6 +402,9 @@ func (s *Module) AddBlock(block *block.Block) error {
351
402
s .syncStage |= blocksSynced
352
403
s .log .Info ("blocks are in sync" ,
353
404
zap .Uint32 ("blockHeight" , s .blockHeight ))
405
+ if s .syncStage != oldStage {
406
+ s .notifyStageChanged ()
407
+ }
354
408
s .checkSyncIsCompleted ()
355
409
}
356
410
return nil
@@ -425,6 +479,7 @@ func (s *Module) restoreNode(n mpt.Node) error {
425
479
// If so, then jumping to P state sync point occurs. It is not protected by lock, thus caller
426
480
// should take care of it.
427
481
func (s * Module ) checkSyncIsCompleted () {
482
+ oldStage := s .syncStage
428
483
if s .syncStage != headersSynced | mptSynced | blocksSynced {
429
484
return
430
485
}
@@ -436,6 +491,10 @@ func (s *Module) checkSyncIsCompleted() {
436
491
}
437
492
s .syncStage = inactive
438
493
s .dispose ()
494
+
495
+ if s .syncStage != oldStage {
496
+ s .notifyStageChanged ()
497
+ }
439
498
}
440
499
441
500
func (s * Module ) dispose () {
@@ -492,6 +551,14 @@ func (s *Module) NeedMPTNodes() bool {
492
551
return s .syncStage & headersSynced != 0 && s .syncStage & mptSynced == 0
493
552
}
494
553
554
+ // NeedBlocks returns whether the module hasn't completed blocks synchronisation.
555
+ func (s * Module ) NeedBlocks () bool {
556
+ s .lock .RLock ()
557
+ defer s .lock .RUnlock ()
558
+
559
+ return s .syncStage & headersSynced != 0 && s .syncStage & blocksSynced == 0
560
+ }
561
+
495
562
// Traverse traverses local MPT nodes starting from the specified root down to its
496
563
// children calling `process` for each serialised node until stop condition is satisfied.
497
564
func (s * Module ) Traverse (root util.Uint256 , process func (node mpt.Node , nodeBytes []byte ) bool ) error {
@@ -516,3 +583,8 @@ func (s *Module) GetUnknownMPTNodesBatch(limit int) []util.Uint256 {
516
583
517
584
return s .mptpool .GetBatch (limit )
518
585
}
586
+
587
+ // GetConfig returns current blockchain configuration.
588
+ func (s * Module ) GetConfig () config.Blockchain {
589
+ return s .bc .GetConfig ()
590
+ }
0 commit comments