@@ -12,14 +12,16 @@ import (
12
12
13
13
"github.com/ChainSafe/chaindb"
14
14
"github.com/ChainSafe/gossamer/dot/types"
15
+ "github.com/ChainSafe/gossamer/lib/blocktree"
15
16
"github.com/ChainSafe/gossamer/lib/common"
16
17
"github.com/ChainSafe/gossamer/pkg/scale"
17
18
)
18
19
19
20
var (
21
+ ErrConfigNotFound = errors .New ("config data not found" )
20
22
ErrEpochNotInMemory = errors .New ("epoch not found in memory map" )
21
23
errHashNotInMemory = errors .New ("hash not found in memory map" )
22
- errEpochDataNotFound = errors .New ("epoch data not found in the database" )
24
+ errEpochNotInDatabase = errors .New ("epoch data not found in the database" )
23
25
errHashNotPersisted = errors .New ("hash with next epoch not found in database" )
24
26
errNoPreRuntimeDigest = errors .New ("header does not contain pre-runtime digest" )
25
27
)
@@ -58,11 +60,11 @@ type EpochState struct {
58
60
59
61
nextEpochDataLock sync.RWMutex
60
62
// nextEpochData follows the format map[epoch]map[block hash]next epoch data
61
- nextEpochData map [ uint64 ] map [common. Hash ] types.NextEpochData
63
+ nextEpochData nextEpochMap [ types.NextEpochData ]
62
64
63
65
nextConfigDataLock sync.RWMutex
64
66
// nextConfigData follows the format map[epoch]map[block hash]next config data
65
- nextConfigData map [ uint64 ] map [common. Hash ] types.NextConfigData
67
+ nextConfigData nextEpochMap [ types.NextConfigData ]
66
68
}
67
69
68
70
// NewEpochStateFromGenesis returns a new EpochState given information for the first epoch, fetched from the runtime
@@ -90,8 +92,8 @@ func NewEpochStateFromGenesis(db chaindb.Database, blockState *BlockState,
90
92
blockState : blockState ,
91
93
db : epochDB ,
92
94
epochLength : genesisConfig .EpochLength ,
93
- nextEpochData : make (map [ uint64 ] map [common. Hash ] types.NextEpochData ),
94
- nextConfigData : make (map [ uint64 ] map [common. Hash ] types.NextConfigData ),
95
+ nextEpochData : make (nextEpochMap [ types.NextEpochData ] ),
96
+ nextConfigData : make (nextEpochMap [ types.NextConfigData ] ),
95
97
}
96
98
97
99
auths , err := types .BABEAuthorityRawToAuthority (genesisConfig .GenesisAuthorities )
@@ -151,8 +153,8 @@ func NewEpochState(db chaindb.Database, blockState *BlockState) (*EpochState, er
151
153
db : chaindb .NewTable (db , epochPrefix ),
152
154
epochLength : epochLength ,
153
155
skipToEpoch : skipToEpoch ,
154
- nextEpochData : make (map [ uint64 ] map [common. Hash ] types.NextEpochData ),
155
- nextConfigData : make (map [ uint64 ] map [common. Hash ] types.NextConfigData ),
156
+ nextEpochData : make (nextEpochMap [ types.NextEpochData ] ),
157
+ nextConfigData : make (nextEpochMap [ types.NextConfigData ] ),
156
158
}, nil
157
159
}
158
160
@@ -247,25 +249,29 @@ func (s *EpochState) SetEpochData(epoch uint64, info *types.EpochData) error {
247
249
// if the header params is nil then it will search only in database
248
250
func (s * EpochState ) GetEpochData (epoch uint64 , header * types.Header ) (* types.EpochData , error ) {
249
251
epochData , err := s .getEpochDataFromDatabase (epoch )
250
- if err == nil && epochData != nil {
252
+ if err != nil && ! errors .Is (err , chaindb .ErrKeyNotFound ) {
253
+ return nil , fmt .Errorf ("failed to retrieve epoch data from database: %w" , err )
254
+ }
255
+
256
+ if epochData != nil {
251
257
return epochData , nil
252
258
}
253
259
254
- if err != nil && ! errors . Is ( err , chaindb . ErrKeyNotFound ) {
255
- return nil , fmt . Errorf ( "failed to get epoch data from database: %w" , err )
260
+ if header == nil {
261
+ return nil , errEpochNotInDatabase
256
262
}
257
263
258
- // lookup in-memory only if header is given
259
- if header != nil && errors . Is ( err , chaindb . ErrKeyNotFound ) {
260
- epochData , err = s . getEpochDataFromMemory ( epoch , header )
261
- if err != nil {
262
- return nil , fmt . Errorf ( "failed to get epoch data from memory: %w" , err )
263
- }
264
+ s . nextEpochDataLock . RLock ()
265
+ defer s . nextEpochDataLock . RUnlock ()
266
+
267
+ inMemoryEpochData , err := s . nextEpochData . Retrieve ( s . blockState , epoch , header )
268
+ if err != nil {
269
+ return nil , fmt . Errorf ( "failed to get epoch data from memory: %w" , err )
264
270
}
265
271
266
- if epochData == nil {
267
- return nil , fmt . Errorf ( "%w: for epoch %d and header with hash %s" ,
268
- errEpochDataNotFound , epoch , header . Hash () )
272
+ epochData , err = inMemoryEpochData . ToEpochData ()
273
+ if err != nil {
274
+ return nil , fmt . Errorf ( "cannot transform into epoch data: %w" , err )
269
275
}
270
276
271
277
return epochData , nil
@@ -287,32 +293,6 @@ func (s *EpochState) getEpochDataFromDatabase(epoch uint64) (*types.EpochData, e
287
293
return raw .ToEpochData ()
288
294
}
289
295
290
- // getEpochDataFromMemory retrieves the right epoch data that belongs to the header parameter
291
- func (s * EpochState ) getEpochDataFromMemory (epoch uint64 , header * types.Header ) (* types.EpochData , error ) {
292
- s .nextEpochDataLock .RLock ()
293
- defer s .nextEpochDataLock .RUnlock ()
294
-
295
- atEpoch , has := s .nextEpochData [epoch ]
296
- if ! has {
297
- return nil , fmt .Errorf ("%w: %d" , ErrEpochNotInMemory , epoch )
298
- }
299
-
300
- headerHash := header .Hash ()
301
-
302
- for hash , value := range atEpoch {
303
- isDescendant , err := s .blockState .IsDescendantOf (hash , headerHash )
304
- if err != nil {
305
- return nil , fmt .Errorf ("cannot verify the ancestry: %w" , err )
306
- }
307
-
308
- if isDescendant {
309
- return value .ToEpochData ()
310
- }
311
- }
312
-
313
- return nil , fmt .Errorf ("%w: %s" , errHashNotInMemory , headerHash )
314
- }
315
-
316
296
// GetLatestEpochData returns the EpochData for the current epoch
317
297
func (s * EpochState ) GetLatestEpochData () (* types.EpochData , error ) {
318
298
curr , err := s .GetCurrentEpoch ()
@@ -323,26 +303,6 @@ func (s *EpochState) GetLatestEpochData() (*types.EpochData, error) {
323
303
return s .GetEpochData (curr , nil )
324
304
}
325
305
326
- // HasEpochData returns whether epoch data exists for a given epoch
327
- func (s * EpochState ) HasEpochData (epoch uint64 ) (bool , error ) {
328
- has , err := s .db .Has (epochDataKey (epoch ))
329
- if err == nil && has {
330
- return has , nil
331
- }
332
-
333
- // we can have `has == false` and `err == nil`
334
- // so ensure the error is not nil in the condition below.
335
- if err != nil && ! errors .Is (chaindb .ErrKeyNotFound , err ) {
336
- return false , fmt .Errorf ("cannot check database for epoch key %d: %w" , epoch , err )
337
- }
338
-
339
- s .nextEpochDataLock .Lock ()
340
- defer s .nextEpochDataLock .Unlock ()
341
-
342
- _ , has = s .nextEpochData [epoch ]
343
- return has , nil
344
- }
345
-
346
306
// SetConfigData sets the BABE config data for a given epoch
347
307
func (s * EpochState ) SetConfigData (epoch uint64 , info * types.ConfigData ) error {
348
308
enc , err := scale .Marshal (* info )
@@ -364,28 +324,44 @@ func (s *EpochState) setLatestConfigData(epoch uint64) error {
364
324
return s .db .Put (latestConfigDataKey , buf )
365
325
}
366
326
367
- // GetConfigData returns the config data for a given epoch persisted in database
368
- // otherwise tries to get the data from the in-memory map using the header.
369
- // If the header params is nil then it will search only in the database
370
- func (s * EpochState ) GetConfigData (epoch uint64 , header * types.Header ) (* types.ConfigData , error ) {
371
- configData , err := s .getConfigDataFromDatabase (epoch )
372
- if err == nil && configData != nil {
373
- return configData , nil
374
- }
327
+ // GetConfigData returns the newest config data for a given epoch persisted in database
328
+ // otherwise tries to get the data from the in-memory map using the header. If we don't
329
+ // find any config data for the current epoch we lookup in the previous epochs, as the spec says:
330
+ // - The supplied configuration data are intended to be used from the next epoch onwards.
331
+ // If the header params is nil then it will search only in the database.
332
+ func (s * EpochState ) GetConfigData (epoch uint64 , header * types.Header ) (configData * types.ConfigData , err error ) {
333
+ for tryEpoch := int (epoch ); tryEpoch >= 0 ; tryEpoch -- {
334
+ configData , err = s .getConfigDataFromDatabase (uint64 (tryEpoch ))
335
+ if err != nil && ! errors .Is (err , chaindb .ErrKeyNotFound ) {
336
+ return nil , fmt .Errorf ("failed to retrieve config epoch from database: %w" , err )
337
+ }
375
338
376
- if err != nil && ! errors .Is (err , chaindb .ErrKeyNotFound ) {
377
- return nil , fmt .Errorf ("failed to get config data from database: %w" , err )
378
- } else if header == nil {
379
- // if no header is given then skip the lookup in-memory
380
- return configData , nil
381
- }
339
+ if configData != nil {
340
+ return configData , nil
341
+ }
382
342
383
- configData , err = s .getConfigDataFromMemory (epoch , header )
384
- if err != nil {
385
- return nil , fmt .Errorf ("failed to get config data from memory: %w" , err )
343
+ // there is no config data for the `tryEpoch` on database and we don't have a
344
+ // header to lookup in the memory map, so let's go retrieve the previous epoch
345
+ if header == nil {
346
+ continue
347
+ }
348
+
349
+ // we will check in the memory map and if we don't find the data
350
+ // then we continue searching through the previous epoch
351
+ s .nextConfigDataLock .RLock ()
352
+ inMemoryConfigData , err := s .nextConfigData .Retrieve (s .blockState , uint64 (tryEpoch ), header )
353
+ s .nextConfigDataLock .RUnlock ()
354
+
355
+ if errors .Is (err , ErrEpochNotInMemory ) {
356
+ continue
357
+ } else if err != nil {
358
+ return nil , fmt .Errorf ("failed to get config data from memory: %w" , err )
359
+ }
360
+
361
+ return inMemoryConfigData .ToConfigData (), err
386
362
}
387
363
388
- return configData , nil
364
+ return nil , fmt . Errorf ( "%w: epoch %d" , ErrConfigNotFound , epoch )
389
365
}
390
366
391
367
// getConfigDataFromDatabase returns the BABE config data for a given epoch persisted in database
@@ -404,26 +380,36 @@ func (s *EpochState) getConfigDataFromDatabase(epoch uint64) (*types.ConfigData,
404
380
return info , nil
405
381
}
406
382
407
- // getConfigDataFromMemory retrieves the BABE config data for a given epoch that belongs to the header parameter
408
- func (s * EpochState ) getConfigDataFromMemory (epoch uint64 , header * types.Header ) (* types.ConfigData , error ) {
409
- s .nextConfigDataLock .RLock ()
410
- defer s .nextConfigDataLock .RUnlock ()
383
+ type nextEpochMap [T types.NextEpochData | types.NextConfigData ] map [uint64 ]map [common.Hash ]T
411
384
412
- atEpoch , has := s .nextConfigData [epoch ]
385
+ func (nem nextEpochMap [T ]) Retrieve (blockState * BlockState , epoch uint64 , header * types.Header ) (* T , error ) {
386
+ atEpoch , has := nem [epoch ]
413
387
if ! has {
414
388
return nil , fmt .Errorf ("%w: %d" , ErrEpochNotInMemory , epoch )
415
389
}
416
390
417
391
headerHash := header .Hash ()
418
-
419
392
for hash , value := range atEpoch {
420
- isDescendant , err := s .blockState .IsDescendantOf (hash , headerHash )
393
+ isDescendant , err := blockState .IsDescendantOf (hash , headerHash )
394
+
395
+ // sometimes while moving to the next epoch is possible the header
396
+ // is not fully imported by the blocktree, in this case we will use
397
+ // its parent header which migth be already imported.
398
+ if errors .Is (err , blocktree .ErrEndNodeNotFound ) {
399
+ parentHeader , err := blockState .GetHeader (header .ParentHash )
400
+ if err != nil {
401
+ return nil , fmt .Errorf ("cannot get parent header: %w" , err )
402
+ }
403
+
404
+ return nem .Retrieve (blockState , epoch , parentHeader )
405
+ }
406
+
421
407
if err != nil {
422
408
return nil , fmt .Errorf ("cannot verify the ancestry: %w" , err )
423
409
}
424
410
425
411
if isDescendant {
426
- return value . ToConfigData () , nil
412
+ return & value , nil
427
413
}
428
414
}
429
415
@@ -441,24 +427,6 @@ func (s *EpochState) GetLatestConfigData() (*types.ConfigData, error) {
441
427
return s .GetConfigData (epoch , nil )
442
428
}
443
429
444
- // HasConfigData returns whether config data exists for a given epoch
445
- func (s * EpochState ) HasConfigData (epoch uint64 ) (bool , error ) {
446
- has , err := s .db .Has (configDataKey (epoch ))
447
- if err == nil && has {
448
- return has , nil
449
- }
450
-
451
- if err != nil && ! errors .Is (chaindb .ErrKeyNotFound , err ) {
452
- return false , fmt .Errorf ("cannot check database for epoch key %d: %w" , epoch , err )
453
- }
454
-
455
- s .nextConfigDataLock .Lock ()
456
- defer s .nextConfigDataLock .Unlock ()
457
-
458
- _ , has = s .nextConfigData [epoch ]
459
- return has , nil
460
- }
461
-
462
430
// GetStartSlotForEpoch returns the first slot in the given epoch.
463
431
// If 0 is passed as the epoch, it returns the start slot for the current epoch.
464
432
func (s * EpochState ) GetStartSlotForEpoch (epoch uint64 ) (uint64 , error ) {
0 commit comments