From 36fb764fc4496c8b61e23790c9c8c436aa3b6a06 Mon Sep 17 00:00:00 2001 From: Ramiro Castillo <43893061+ramiroJCB@users.noreply.github.com> Date: Fri, 26 Jul 2024 20:09:20 -0500 Subject: [PATCH 01/13] fix(epoch):Save Next Epoch data and config and disk --- dot/state/epoch.go | 182 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 175 insertions(+), 7 deletions(-) diff --git a/dot/state/epoch.go b/dot/state/epoch.go index 4e472d17cc..a688b42ade 100644 --- a/dot/state/epoch.go +++ b/dot/state/epoch.go @@ -8,6 +8,8 @@ import ( "encoding/binary" "errors" "fmt" + "strconv" + "strings" "sync" "time" @@ -28,11 +30,13 @@ var ( ) var ( - epochPrefix = "epoch" - currentEpochKey = []byte("current") - epochDataPrefix = []byte("epochinfo") - configDataPrefix = []byte("configinfo") - skipToKey = []byte("skipto") + epochPrefix = "epoch" + currentEpochKey = []byte("current") + epochDataPrefix = []byte("epochinfo") + configDataPrefix = []byte("configinfo") + skipToKey = []byte("skipto") + nextEpochDataPrefix = []byte("nextepochdata") + nextConfigDataPrefix = []byte("nextconfigdata") ) func epochDataKey(epoch uint64) []byte { @@ -47,6 +51,18 @@ func configDataKey(epoch uint64) []byte { return append(configDataPrefix, buf...) } +func nextEpochDataKey(epoch uint64, hash common.Hash) []byte { + // we add a "-" to the key to avoid conflicts with composite keys + key := fmt.Sprintf("-%d:%s", epoch, hash) + return append(nextEpochDataPrefix, []byte(key)...) +} + +func nextConfigDataKey(epoch uint64, hash common.Hash) []byte { + // we add a "-" to the key to avoid conflicts with composite keys + key := fmt.Sprintf("-%d:%s", epoch, hash) + return append(nextConfigDataPrefix, []byte(key)...) +} + // GenesisEpochDescriptor is the informations provided by calling // the genesis WASM runtime exported function `BabeAPIConfiguration` type GenesisEpochDescriptor struct { @@ -115,6 +131,88 @@ func NewEpochStateFromGenesis(db database.Database, blockState *BlockState, return s, nil } +func getNextEpochDataFromDisk(db database.Database) (nextEpochMap[types.NextEpochData], error) { + nextEpochData := make(nextEpochMap[types.NextEpochData]) + + iter, err := db.NewPrefixIterator(nextEpochDataPrefix) + if err != nil { + return nil, err + } + defer iter.Release() + + for iter.First(); iter.Valid(); iter.Next() { + key := string(iter.Key()) + value := iter.Value() + + // Remove the prefix + keyWithoutPrefix := strings.TrimPrefix(key, string(nextEpochDataPrefix)+"-") + + // Split the key into epoch and fork + parts := strings.Split(keyWithoutPrefix, ":") + + epoch, err := strconv.ParseUint(parts[0], 10, 64) + if err != nil { + return nil, err + } + + var fork common.Hash + part1 := []byte(parts[1]) + // Copy the hash to the fork + copy(fork[:], part1) + + nexEpochvalue := new(types.NextEpochData) + if err = scale.Unmarshal(value, nexEpochvalue); err != nil { + return nil, err + } + + // Add data to the map + nextEpochData[epoch][fork] = *nexEpochvalue + } + + return nextEpochData, nil +} + +func getNextConfigDataFromDisk(db database.Database) (nextEpochMap[types.NextConfigDataV1], error) { + nextConfigData := make(nextEpochMap[types.NextConfigDataV1]) + + iter, err := db.NewPrefixIterator(nextConfigDataPrefix) + if err != nil { + return nil, err + } + defer iter.Release() + + for iter.First(); iter.Valid(); iter.Next() { + key := string(iter.Key()) + value := iter.Value() + + keyWithoutPrefix := strings.TrimPrefix(key, string(nextConfigDataPrefix)+"-") + + // Split the key into epoch and fork + parts := strings.Split(keyWithoutPrefix, ":") + if len(parts) != 2 { + return nil, fmt.Errorf("invalid key format: %s", key) + } + epoch, err := strconv.ParseUint(parts[0], 10, 64) + if err != nil { + return nil, err + } + + var fork common.Hash + part1 := []byte(parts[1]) + + copy(fork[:], part1) + + nexEpochvalue := new(types.NextConfigDataV1) + if err = scale.Unmarshal(value, nexEpochvalue); err != nil { + return nil, err + } + + nextConfigData[epoch][fork] = *nexEpochvalue + } + + return nextConfigData, nil +} + // NewEpochState returns a new EpochState func NewEpochState(db database.Database, blockState *BlockState, genesisConfig *types.BabeConfiguration) (*EpochState, error) { @@ -128,6 +226,16 @@ func NewEpochState(db database.Database, blockState *BlockState, return nil, err } + nextEpochData, err := getNextEpochDataFromDisk(db) + if err != nil { + return nil, err + } + + nextConfigData, err := getNextConfigDataFromDisk(db) + if err != nil { + return nil, err + } + return &EpochState{ baseState: baseState, blockState: blockState, @@ -135,8 +243,8 @@ func NewEpochState(db database.Database, blockState *BlockState, epochLength: genesisConfig.EpochLength, slotDuration: genesisConfig.SlotDuration, skipToEpoch: skipToEpoch, - nextEpochData: make(nextEpochMap[types.NextEpochData]), - nextConfigData: make(nextEpochMap[types.NextConfigDataV1]), + nextEpochData: nextEpochData, + nextConfigData: nextConfigData, genesisEpochDescriptor: &GenesisEpochDescriptor{ EpochData: &types.EpochDataRaw{ Authorities: genesisConfig.GenesisAuthorities, @@ -579,6 +687,13 @@ func getEpochDefinitionFromDatabase[T types.ConfigData | types.EpochDataRaw]( return info, nil } +// Case A) where we store the epoc info here +// 0->b1(epoch 1)->b2(epoch 1)->->->b15(epoch 1)->b16(epoch 2)->b17(epoch 2) +// nextEpoch +// asdas-epoch3:fork1 +// asdas-epoch3:fork2 +// asdas-epoch3:fork3 +// remove prefix and split : func (s *EpochState) HandleBABEDigest(header *types.Header, digest types.BabeConsensusDigest) error { headerHash := header.Hash() @@ -596,6 +711,9 @@ func (s *EpochState) HandleBABEDigest(header *types.Header, digest types.BabeCon nextEpoch := currEpoch + 1 s.storeBABENextEpochData(nextEpoch, headerHash, val) + + s.setBABENextEpochDataInDB(nextEpoch, headerHash, val) + logger.Debugf("stored BABENextEpochData data: %v for hash: %s to epoch: %d", digest, headerHash, nextEpoch) return nil @@ -616,6 +734,9 @@ func (s *EpochState) HandleBABEDigest(header *types.Header, digest types.BabeCon } nextEpoch := currEpoch + 1 s.storeBABENextConfigData(nextEpoch, headerHash, nextConfigData) + + s.setBABENextConfigData(nextEpoch, headerHash, nextConfigData) + logger.Debugf("stored BABENextConfigData data: %v for hash: %s to epoch: %d", digest, headerHash, nextEpoch) return nil default: @@ -823,6 +944,18 @@ func (s *EpochState) storeBABENextEpochData(epoch uint64, hash common.Hash, next s.nextEpochData[epoch][hash] = nextEpochData } +// setBABENextEpochDataInDB stores the types.NextEpochData under epoch and hash keys +func (s *EpochState) setBABENextEpochDataInDB(epoch uint64, forkHash common.Hash, nextEpochData types.NextEpochData) error { + encodedEpochData, err := scale.Marshal(nextEpochData) + if err != nil { + return err + } + + key := nextEpochDataKey(epoch, forkHash) + + return s.db.Put([]byte(key), encodedEpochData) +} + // StoreBABENextConfigData stores the types.NextConfigData under epoch and hash keys func (s *EpochState) storeBABENextConfigData(epoch uint64, hash common.Hash, nextConfigData types.NextConfigDataV1) { s.nextConfigDataLock.Lock() @@ -837,6 +970,17 @@ func (s *EpochState) storeBABENextConfigData(epoch uint64, hash common.Hash, nex s.nextConfigData[epoch][hash] = nextConfigData } +// setBABENextConfigData stores the types.NextConfigData under epoch and hash keys +func (s *EpochState) setBABENextConfigData(epoch uint64, forkHash common.Hash, nextConfigData types.NextConfigDataV1) error { + encodedConfigData, err := scale.Marshal(nextConfigData) + if err != nil { + return err + } + + key := nextConfigDataKey(epoch, forkHash) + return s.db.Put([]byte(key), encodedConfigData) +} + // FinalizeBABENextEpochData stores the right types.NextEpochData by // getting the set of hashes from the received epoch and for each hash // check if the header is in the database then it's been finalized and @@ -888,12 +1032,24 @@ func (s *EpochState) FinalizeBABENextEpochData(finalizedHeader *types.Header) er for e := range s.nextEpochData { if e <= nextEpoch { delete(s.nextEpochData, e) + // remove the epoch data from the database + s.deleteEpochDataFromDisk(e, finalizedHeader.Hash()) } } return nil } +func (s *EpochState) deleteEpochDataFromDisk(epoch uint64, hash common.Hash) error { + key := append(nextEpochDataPrefix, []byte(fmt.Sprintf("-%d:%s", epoch, hash))...) + err := s.db.NewBatch().Del(key) + if err != nil { + return fmt.Errorf("cannot delete next epoch data from the database: %w", err) + } + return nil + +} + // FinalizeBABENextConfigData stores the right types.NextConfigData by // getting the set of hashes from the received epoch and for each hash // check if the header is in the database then it's been finalized and @@ -950,12 +1106,24 @@ func (s *EpochState) FinalizeBABENextConfigData(finalizedHeader *types.Header) e for e := range s.nextConfigData { if e <= nextEpoch { delete(s.nextConfigData, e) + // remove the config data from the database + s.deleteNextConfigDataFromDisk(e, finalizedHeader.Hash()) } } return nil } +func (s *EpochState) deleteNextConfigDataFromDisk(epoch uint64, hash common.Hash) error { + key := append(nextConfigDataPrefix, []byte(fmt.Sprintf("-%d:%s", epoch, hash))...) + err := s.db.NewBatch().Del(key) + if err != nil { + return fmt.Errorf("cannot delete next config data from the database: %w", err) + } + + return nil +} + // findFinalizedHeaderForEpoch given a specific epoch (the key) will go through the hashes looking // for a database persisted hash (belonging to the finalized chain) // which contains the right configuration or data to be persisted and safely used From 0901a408bb6b4aac91dda2d8d31943ad6c26ad38 Mon Sep 17 00:00:00 2001 From: Ramiro Castillo <43893061+ramiroJCB@users.noreply.github.com> Date: Fri, 26 Jul 2024 22:16:20 -0500 Subject: [PATCH 02/13] chore(epoch): Resolving err values --- dot/state/epoch.go | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/dot/state/epoch.go b/dot/state/epoch.go index a688b42ade..34bdf52cc7 100644 --- a/dot/state/epoch.go +++ b/dot/state/epoch.go @@ -712,7 +712,9 @@ func (s *EpochState) HandleBABEDigest(header *types.Header, digest types.BabeCon nextEpoch := currEpoch + 1 s.storeBABENextEpochData(nextEpoch, headerHash, val) - s.setBABENextEpochDataInDB(nextEpoch, headerHash, val) + if err = s.setBABENextEpochDataInDB(nextEpoch, headerHash, val); err != nil { + return fmt.Errorf("setting next epoch data in db: %w", err) + } logger.Debugf("stored BABENextEpochData data: %v for hash: %s to epoch: %d", digest, headerHash, nextEpoch) return nil @@ -735,7 +737,9 @@ func (s *EpochState) HandleBABEDigest(header *types.Header, digest types.BabeCon nextEpoch := currEpoch + 1 s.storeBABENextConfigData(nextEpoch, headerHash, nextConfigData) - s.setBABENextConfigData(nextEpoch, headerHash, nextConfigData) + if err := s.setBABENextConfigData(nextEpoch, headerHash, nextConfigData); err != nil { + return fmt.Errorf("setting next config data in db: %w", err) + } logger.Debugf("stored BABENextConfigData data: %v for hash: %s to epoch: %d", digest, headerHash, nextEpoch) return nil @@ -953,7 +957,7 @@ func (s *EpochState) setBABENextEpochDataInDB(epoch uint64, forkHash common.Hash key := nextEpochDataKey(epoch, forkHash) - return s.db.Put([]byte(key), encodedEpochData) + return s.db.Put(key, encodedEpochData) } // StoreBABENextConfigData stores the types.NextConfigData under epoch and hash keys @@ -978,7 +982,7 @@ func (s *EpochState) setBABENextConfigData(epoch uint64, forkHash common.Hash, n } key := nextConfigDataKey(epoch, forkHash) - return s.db.Put([]byte(key), encodedConfigData) + return s.db.Put(key, encodedConfigData) } // FinalizeBABENextEpochData stores the right types.NextEpochData by @@ -1033,7 +1037,9 @@ func (s *EpochState) FinalizeBABENextEpochData(finalizedHeader *types.Header) er if e <= nextEpoch { delete(s.nextEpochData, e) // remove the epoch data from the database - s.deleteEpochDataFromDisk(e, finalizedHeader.Hash()) + if err = s.deleteEpochDataFromDisk(e, finalizedHeader.Hash()); err != nil { + return fmt.Errorf("cannot delete next epoch data from the database: %w", err) + } } } @@ -1107,7 +1113,9 @@ func (s *EpochState) FinalizeBABENextConfigData(finalizedHeader *types.Header) e if e <= nextEpoch { delete(s.nextConfigData, e) // remove the config data from the database - s.deleteNextConfigDataFromDisk(e, finalizedHeader.Hash()) + if err = s.deleteNextConfigDataFromDisk(e, finalizedHeader.Hash()); err != nil { + return fmt.Errorf("cannot delete next config data from the database: %w", err) + } } } From 69a878ae8b51c35b3f29d76ad7dc5b0ed592e550 Mon Sep 17 00:00:00 2001 From: Ramiro Castillo <43893061+ramiroJCB@users.noreply.github.com> Date: Fri, 16 Aug 2024 16:39:36 -0500 Subject: [PATCH 03/13] chore(epoch): Mergin config and epoch retrieval functions --- dot/state/epoch.go | 178 +++++++++++++++++++++------------------------ 1 file changed, 81 insertions(+), 97 deletions(-) diff --git a/dot/state/epoch.go b/dot/state/epoch.go index 34bdf52cc7..9137f0e7ec 100644 --- a/dot/state/epoch.go +++ b/dot/state/epoch.go @@ -53,13 +53,13 @@ func configDataKey(epoch uint64) []byte { func nextEpochDataKey(epoch uint64, hash common.Hash) []byte { // we add a "-" to the key to avoid conflicts with composite keys - key := fmt.Sprintf("-%d:%s", epoch, hash) + key := fmt.Sprintf("-%d:%v", epoch, hash) return append(nextEpochDataPrefix, []byte(key)...) } func nextConfigDataKey(epoch uint64, hash common.Hash) []byte { // we add a "-" to the key to avoid conflicts with composite keys - key := fmt.Sprintf("-%d:%s", epoch, hash) + key := fmt.Sprintf("-%d:%v", epoch, hash) return append(nextConfigDataPrefix, []byte(key)...) } @@ -131,88 +131,6 @@ func NewEpochStateFromGenesis(db database.Database, blockState *BlockState, return s, nil } -func getNextEpochDataFromDisk(db database.Database) (nextEpochMap[types.NextEpochData], error) { - nextEpochData := make(nextEpochMap[types.NextEpochData]) - - iter, err := db.NewPrefixIterator(nextEpochDataPrefix) - if err != nil { - return nil, err - } - defer iter.Release() - - for iter.First(); iter.Valid(); iter.Next() { - key := string(iter.Key()) - value := iter.Value() - - // Remove the prefix - keyWithoutPrefix := strings.TrimPrefix(key, string(nextEpochDataPrefix)+"-") - - // Split the key into epoch and fork - parts := strings.Split(keyWithoutPrefix, ":") - - epoch, err := strconv.ParseUint(parts[0], 10, 64) - if err != nil { - return nil, err - } - - var fork common.Hash - part1 := []byte(parts[1]) - // Copy the hash to the fork - copy(fork[:], part1) - - nexEpochvalue := new(types.NextEpochData) - if err = scale.Unmarshal(value, nexEpochvalue); err != nil { - return nil, err - } - - // Add data to the map - nextEpochData[epoch][fork] = *nexEpochvalue - } - - return nextEpochData, nil -} - -func getNextConfigDataFromDisk(db database.Database) (nextEpochMap[types.NextConfigDataV1], error) { - nextConfigData := make(nextEpochMap[types.NextConfigDataV1]) - - iter, err := db.NewPrefixIterator(nextConfigDataPrefix) - if err != nil { - return nil, err - } - defer iter.Release() - - for iter.First(); iter.Valid(); iter.Next() { - key := string(iter.Key()) - value := iter.Value() - - keyWithoutPrefix := strings.TrimPrefix(key, string(nextConfigDataPrefix)+"-") - - // Split the key into epoch and fork - parts := strings.Split(keyWithoutPrefix, ":") - if len(parts) != 2 { - return nil, fmt.Errorf("invalid key format: %s", key) - } - epoch, err := strconv.ParseUint(parts[0], 10, 64) - if err != nil { - return nil, err - } - - var fork common.Hash - part1 := []byte(parts[1]) - - copy(fork[:], part1) - - nexEpochvalue := new(types.NextConfigDataV1) - if err = scale.Unmarshal(value, nexEpochvalue); err != nil { - return nil, err - } - - nextConfigData[epoch][fork] = *nexEpochvalue - } - - return nextConfigData, nil -} - // NewEpochState returns a new EpochState func NewEpochState(db database.Database, blockState *BlockState, genesisConfig *types.BabeConfiguration) (*EpochState, error) { @@ -226,12 +144,7 @@ func NewEpochState(db database.Database, blockState *BlockState, return nil, err } - nextEpochData, err := getNextEpochDataFromDisk(db) - if err != nil { - return nil, err - } - - nextConfigData, err := getNextConfigDataFromDisk(db) + nextEpochData, nextConfigData, err := getNextEpochAndConfigDataFromDisk(db) if err != nil { return nil, err } @@ -259,6 +172,84 @@ func NewEpochState(db database.Database, blockState *BlockState, }, nil } +// getNextEpochAndConfigDataFromDisk retrieves the next epoch and config data maps from the database +func getNextEpochAndConfigDataFromDisk(db database.Database) (nextEpochMap[types.NextEpochData], nextEpochMap[types.NextConfigDataV1], error) { + nextConfigData := make(nextEpochMap[types.NextConfigDataV1]) + nextEpochData := make(nextEpochMap[types.NextEpochData]) + + configIter, err := db.NewPrefixIterator(nextConfigDataPrefix) + if err != nil { + return nextEpochMap[types.NextEpochData]{}, nextEpochMap[types.NextConfigDataV1]{}, err + } + + defer configIter.Release() + + for configIter.First(); configIter.Valid(); configIter.Next() { + nexEpochvalue := new(types.NextConfigDataV1) + + nexEpochvalue, epoch, fork, err := getNextEpochOrConfigData(nexEpochvalue, nextConfigDataPrefix, configIter) + if err != nil { + return nextEpochMap[types.NextEpochData]{}, nextEpochMap[types.NextConfigDataV1]{}, err + } + + // Add data to the map + nextConfigData[epoch][fork] = *nexEpochvalue + } + + configIter.Close() + + epochIter, err := db.NewPrefixIterator(nextEpochDataPrefix) + if err != nil { + return nextEpochMap[types.NextEpochData]{}, nextEpochMap[types.NextConfigDataV1]{}, err + } + + defer epochIter.Release() + + for epochIter.First(); epochIter.Valid(); epochIter.Next() { + + nexEpochvalue := new(types.NextEpochData) + nexEpochvalue, epoch, fork, err := getNextEpochOrConfigData(nexEpochvalue, nextEpochDataPrefix, epochIter) + if err != nil { + return nextEpochMap[types.NextEpochData]{}, nextEpochMap[types.NextConfigDataV1]{}, err + } + // Add data to the map + nextEpochData[epoch][fork] = *nexEpochvalue + } + + epochIter.Close() + + return nextEpochData, nextConfigData, nil +} + +// getNextEpochOrConfigData retrieves the next epoch or config data from the iterator +func getNextEpochOrConfigData[T *types.NextConfigDataV1 | *types.NextEpochData](NextData T, nextDataPrefix []byte, iter database.Iterator) (T, uint64, common.Hash, error) { + key := string(iter.Key()) + value := iter.Value() + + keyWithoutPrefix := strings.TrimPrefix(key, string(nextDataPrefix)+"-") + + // Split the key into epoch and fork + parts := strings.Split(keyWithoutPrefix, ":") + if len(parts) != 2 { + return nil, 0, common.Hash{}, fmt.Errorf("invalid key format: %s", key) + } + epoch, err := strconv.ParseUint(parts[0], 10, 64) + if err != nil { + return nil, 0, common.Hash{}, err + } + + var fork common.Hash + part1 := []byte(parts[1]) + + copy(fork[:], part1) + + if err = scale.Unmarshal(value, NextData); err != nil { + return nil, 0, common.Hash{}, err + } + + return NextData, epoch, fork, nil +} + // GetEpochLength returns the length of an epoch in slots func (s *EpochState) GetEpochLength() uint64 { return s.epochLength @@ -687,13 +678,6 @@ func getEpochDefinitionFromDatabase[T types.ConfigData | types.EpochDataRaw]( return info, nil } -// Case A) where we store the epoc info here -// 0->b1(epoch 1)->b2(epoch 1)->->->b15(epoch 1)->b16(epoch 2)->b17(epoch 2) -// nextEpoch -// asdas-epoch3:fork1 -// asdas-epoch3:fork2 -// asdas-epoch3:fork3 -// remove prefix and split : func (s *EpochState) HandleBABEDigest(header *types.Header, digest types.BabeConsensusDigest) error { headerHash := header.Hash() From 7e0d55a615846d4583c85e8be7c97a4ab5949142 Mon Sep 17 00:00:00 2001 From: Ramiro Castillo <43893061+ramiroJCB@users.noreply.github.com> Date: Mon, 19 Aug 2024 23:00:55 -0500 Subject: [PATCH 04/13] chore(epoch): Adding test suite to content --- dot/state/epoch.go | 76 +++++++-- dot/state/epoch_test.go | 291 ++++++++++++++++++++++++++++++++++ dot/state/inmemory_storage.go | 2 +- dot/state/interfaces.go | 7 +- internal/database/database.go | 1 + internal/database/table.go | 4 + 6 files changed, 366 insertions(+), 15 deletions(-) diff --git a/dot/state/epoch.go b/dot/state/epoch.go index 9137f0e7ec..6cc108e2d4 100644 --- a/dot/state/epoch.go +++ b/dot/state/epoch.go @@ -72,7 +72,7 @@ type GenesisEpochDescriptor struct { // EpochState tracks information related to each epoch type EpochState struct { - db GetterPutterNewBatcher + db GetterPutterNewBatcherPrefixIter baseState *BaseState blockState *BlockState epochLength uint64 // measured in slots @@ -192,6 +192,9 @@ func getNextEpochAndConfigDataFromDisk(db database.Database) (nextEpochMap[types return nextEpochMap[types.NextEpochData]{}, nextEpochMap[types.NextConfigDataV1]{}, err } + if _, ok := nextConfigData[epoch]; !ok { + nextConfigData[epoch] = make(map[common.Hash]types.NextConfigDataV1) + } // Add data to the map nextConfigData[epoch][fork] = *nexEpochvalue } @@ -213,7 +216,12 @@ func getNextEpochAndConfigDataFromDisk(db database.Database) (nextEpochMap[types return nextEpochMap[types.NextEpochData]{}, nextEpochMap[types.NextConfigDataV1]{}, err } // Add data to the map + if _, ok := nextEpochData[epoch]; !ok { + nextEpochData[epoch] = make(map[common.Hash]types.NextEpochData) + } + nextEpochData[epoch][fork] = *nexEpochvalue + } epochIter.Close() @@ -615,7 +623,7 @@ type prefixedKeyBuilder func(epoch uint64) []byte // updateEpochDefinitionKey updates the informations from database // by querying the raw bytes from prefix + oldEpoch and inserting // at prefix + newEpoch and return the values stored at prefix + oldEpoch -func updateEpochDefinitionKey(db GetterPutterNewBatcher, +func updateEpochDefinitionKey(db GetterPutterNewBatcherPrefixIter, oldEpoch, newEpoch uint64, usePrefix prefixedKeyBuilder) ([]byte, error) { rawBytes, err := db.Get(usePrefix(oldEpoch)) if err != nil { @@ -647,7 +655,7 @@ func updateEpochDefinitionKey(db GetterPutterNewBatcher, } func getAndUpdateEpochDefinitionKey[T types.ConfigData | types.EpochDataRaw]( - db GetterPutterNewBatcher, oldEpoch, newEpoch uint64, usePrefix prefixedKeyBuilder) (*T, error) { + db GetterPutterNewBatcherPrefixIter, oldEpoch, newEpoch uint64, usePrefix prefixedKeyBuilder) (*T, error) { rawBytes, err := updateEpochDefinitionKey(db, oldEpoch, newEpoch, usePrefix) if err != nil { return nil, fmt.Errorf("updating epoch key definition: %w", err) @@ -1021,7 +1029,7 @@ func (s *EpochState) FinalizeBABENextEpochData(finalizedHeader *types.Header) er if e <= nextEpoch { delete(s.nextEpochData, e) // remove the epoch data from the database - if err = s.deleteEpochDataFromDisk(e, finalizedHeader.Hash()); err != nil { + if err = s.deleteEpochDataFromDisk(e); err != nil { return fmt.Errorf("cannot delete next epoch data from the database: %w", err) } } @@ -1030,14 +1038,50 @@ func (s *EpochState) FinalizeBABENextEpochData(finalizedHeader *types.Header) er return nil } -func (s *EpochState) deleteEpochDataFromDisk(epoch uint64, hash common.Hash) error { - key := append(nextEpochDataPrefix, []byte(fmt.Sprintf("-%d:%s", epoch, hash))...) - err := s.db.NewBatch().Del(key) +func (s *EpochState) deleteEpochDataFromDisk(epoch uint64) error { + nexEpochvalue := new(types.NextEpochData) + configKeysToDelete, err := getNextEpochOrConfigDataKeysFromDisk(s.db, nexEpochvalue, nextEpochDataPrefix, epoch) if err != nil { - return fmt.Errorf("cannot delete next epoch data from the database: %w", err) + return fmt.Errorf("cannot get next config data keys from disk: %w", err) + } + + for _, key := range configKeysToDelete { + err = s.db.NewBatch().Del([]byte(key)) + if err != nil { + return fmt.Errorf("cannot delete next config data from the database: %w", err) + } } return nil +} + +// getNextEpochOrConfigDataKeysFromDisk is a generic function that returns all the nextEpochData or nextConfigData keys +// for a given epoch from the database +func getNextEpochOrConfigDataKeysFromDisk[T *types.NextEpochData | *types.NextConfigDataV1](db GetterPutterNewBatcherPrefixIter, value T, prefix []byte, currentEpoch uint64) ([]string, error) { + dataKeys := []string{} + + iter, err := db.NewPrefixIterator(prefix) + if err != nil { + return dataKeys, err + } + + defer iter.Release() + + for iter.First(); iter.Valid(); iter.Next() { + _, epoch, fork, err := getNextEpochOrConfigData(value, prefix, iter) + if err != nil { + return dataKeys, err + } + // if the epoch is the current epoch, then we append the key to the dataKeys + if epoch == currentEpoch { + key := append(prefix, []byte(fmt.Sprintf("-%d:%s", epoch, fork))...) + dataKeys = append(dataKeys, string(key)) + } + } + + iter.Close() + + return dataKeys, nil } // FinalizeBABENextConfigData stores the right types.NextConfigData by @@ -1097,7 +1141,7 @@ func (s *EpochState) FinalizeBABENextConfigData(finalizedHeader *types.Header) e if e <= nextEpoch { delete(s.nextConfigData, e) // remove the config data from the database - if err = s.deleteNextConfigDataFromDisk(e, finalizedHeader.Hash()); err != nil { + if err = s.deleteNextConfigDataFromDisk(e); err != nil { return fmt.Errorf("cannot delete next config data from the database: %w", err) } } @@ -1106,13 +1150,19 @@ func (s *EpochState) FinalizeBABENextConfigData(finalizedHeader *types.Header) e return nil } -func (s *EpochState) deleteNextConfigDataFromDisk(epoch uint64, hash common.Hash) error { - key := append(nextConfigDataPrefix, []byte(fmt.Sprintf("-%d:%s", epoch, hash))...) - err := s.db.NewBatch().Del(key) +func (s *EpochState) deleteNextConfigDataFromDisk(epoch uint64) error { + nextConfigValue := new(types.NextConfigDataV1) + configKeysToDelete, err := getNextEpochOrConfigDataKeysFromDisk(s.db, nextConfigValue, nextConfigDataPrefix, epoch) if err != nil { - return fmt.Errorf("cannot delete next config data from the database: %w", err) + return fmt.Errorf("cannot get next config data keys from disk: %w", err) } + for _, key := range configKeysToDelete { + err = s.db.NewBatch().Del([]byte(key)) + if err != nil { + return fmt.Errorf("cannot delete next config data from the database: %w", err) + } + } return nil } diff --git a/dot/state/epoch_test.go b/dot/state/epoch_test.go index ac65ccd4cd..35b057fd33 100644 --- a/dot/state/epoch_test.go +++ b/dot/state/epoch_test.go @@ -855,3 +855,294 @@ func TestFirstSlotNumberFromDb(t *testing.T) { require.EqualValuesf(t, predefinedSlotNumber, firstSlotNumber, "expected: %d, got: %d", predefinedSlotNumber, firstSlotNumber) } + +func TestNextEpochDataAndConfigInDisk(t *testing.T) { + epochState := newEpochStateFromGenesis(t) + db := NewInMemoryDB(t) + epochState.db = db + slotDuration, err := epochState.GetSlotDuration() + require.NoError(t, err) + + genesisHash := epochState.blockState.genesisHash + // setting a predefined slot number + predefinedSlotNumber := uint64(1000) + buf := make([]byte, 8) + binary.LittleEndian.PutUint64(buf, predefinedSlotNumber) + err = epochState.blockState.db.Put(firstSlotNumberKey, buf) + require.NoError(t, err) + + slotNumber := currentSlot(uint64(time.Now().UnixNano()), + uint64(slotDuration.Nanoseconds())) + + firstNonOrirginBlock := types.NewEmptyHeader() + firstNonOrirginBlock.ParentHash = genesisHash + firstNonOrirginBlock.Number = 1 + firstNonOrirginBlock.Digest = buildBlockPrimaryDigest(t, + types.BabePrimaryPreDigest{AuthorityIndex: 0, SlotNumber: slotNumber}) + + err = epochState.blockState.AddBlock( + &types.Block{Header: *firstNonOrirginBlock, Body: *types.NewBody([]types.Extrinsic{})}) + require.NoError(t, err) + + digest := types.NewDigest() + preRuntimeDigest := types.PreRuntimeDigest{ + ConsensusEngineID: types.BabeEngineID, + // bytes for PreRuntimeDigest that was created in setupHeaderFile function + Data: []byte{1, 60, 0, 0, 0, 150, 89, 189, 15, 0, 0, 0, 0, 112, 237, 173, 28, 144, 100, 255, + 247, 140, 177, 132, 53, 34, 61, 138, 218, 245, 234, 4, 194, 75, 26, 135, 102, 227, 220, 1, 235, 3, 204, + 106, 12, 17, 183, 151, 147, 212, 227, 28, 192, 153, 8, 56, 34, 156, 68, 254, 209, 102, 154, 124, 124, + 121, 225, 230, 208, 169, 99, 116, 214, 73, 103, 40, 6, 157, 30, 247, 57, 226, 144, 73, 122, 14, 59, 114, + 143, 168, 143, 203, 221, 58, 85, 4, 224, 239, 222, 2, 66, 231, 168, 6, 221, 79, 169, 38, 12}, + } + + preRuntimeDigestItem := types.NewDigestItem() + err = preRuntimeDigestItem.SetValue(preRuntimeDigest) + require.NoError(t, err) + preRuntimeDigestItemValue, err := preRuntimeDigestItem.Value() + require.NoError(t, err) + digest.Add(preRuntimeDigestItemValue) + + sealDigest := types.SealDigest{ + ConsensusEngineID: types.BabeEngineID, + // bytes for SealDigest that was created in setupHeaderFile function + Data: []byte{158, 127, 40, 221, 220, 242, 124, 30, 107, 50, 141, 86, 148, 195, 104, 213, 178, 236, 93, 190, + 14, 65, 42, 225, 201, 143, 136, 213, 59, 228, 216, 80, 47, 172, 87, 31, 63, 25, 201, 202, 175, 40, 26, + 103, 51, 25, 36, 30, 12, 80, 149, 166, 131, 173, 52, 49, 98, 4, 8, 138, 54, 164, 189, 134}, + } + + sealDigestItem := types.NewDigestItem() + err = sealDigestItem.SetValue(sealDigest) + require.NoError(t, err) + + sealDigestItemValue, err := sealDigestItem.Value() + require.NoError(t, err) + digest.Add(sealDigestItemValue) + + expectedHeader := &types.Header{ + ParentHash: common.MustHexToHash("0x3b45c9c22dcece75a30acc9c2968cb311e6b0557350f83b430f47559db786975"), + Number: 1482002, + StateRoot: common.MustHexToHash("0x09f9ca28df0560c2291aa16b56e15e07d1e1927088f51356d522722aa90ca7cb"), + ExtrinsicsRoot: common.MustHexToHash("0xda26dc8c1455f8f81cae12e4fc59e23ce961b2c837f6d3f664283af906d344e0"), + Digest: digest, + } + + keyring, _ := keystore.NewSr25519Keyring() + + keyPairs := []*sr25519.Keypair{ + keyring.KeyAlice, keyring.KeyBob, keyring.KeyCharlie, + } + + authorities := make([]types.AuthorityRaw, len(keyPairs)) + for i, keyPair := range keyPairs { + authorities[i] = types.AuthorityRaw{ + Key: keyPair.Public().(*sr25519.PublicKey).AsBytes(), + } + } + + genericNextEpochDigest := createBABEConsensusDigest(t, types.NextEpochData{ + Authorities: authorities, + Randomness: [32]byte{0, 1, 2, 3, 4, 5, 6, 7, 8}, + }) + + versionedNextConfigData := types.NewVersionedNextConfigData() + versionedNextConfigData.SetValue(types.NextConfigDataV1{ + C1: 9, + C2: 10, + SecondarySlots: 1, + }) + genericNextConfigDataDigest := createBABEConsensusDigest(t, versionedNextConfigData) + + consensusDigests := []types.ConsensusDigest{ + genericNextEpochDigest, genericNextConfigDataDigest, + } + + nextEpochData := types.NewBabeConsensusDigest() + err = scale.Unmarshal(consensusDigests[0].Data, &nextEpochData) + if err != nil { + t.Errorf("error unmarshalling next epoch data: %s", err) + } + + // Handle config and epoch data digests + err = epochState.HandleBABEDigest(expectedHeader, nextEpochData) + + require.NoError(t, err) + + nextConfigData := types.NewBabeConsensusDigest() + err = scale.Unmarshal(consensusDigests[1].Data, &nextConfigData) + if err != nil { + t.Errorf("error unmarshalling next config data: %s", err) + } + + err = epochState.HandleBABEDigest(expectedHeader, nextConfigData) + require.NoError(t, err) + + // Making sure that we have available storeSkipToEpoch prop on disk + epochState.baseState.db = db + err = epochState.baseState.storeSkipToEpoch(0) + + require.NoError(t, err) + + // Check if the next epoch data and config data are stored in the database + epochState, err = NewEpochState(db, epochState.blockState, config.BABEConfigurationTestDefault) + require.NoError(t, err) + require.Equal(t, len(epochState.nextEpochData), 1) + require.Equal(t, len(epochState.nextConfigData), 1) + +} + +func createBABEConsensusDigest(t *testing.T, digestData any) types.ConsensusDigest { + t.Helper() + + babeConsensusDigest := types.NewBabeConsensusDigest() + require.NoError(t, babeConsensusDigest.SetValue(digestData)) + + marshaledData, err := scale.Marshal(babeConsensusDigest) + require.NoError(t, err) + + return types.ConsensusDigest{ + ConsensusEngineID: types.BabeEngineID, + Data: marshaledData, + } +} + +func TestDeleteNextEpochDataAndConfig(t *testing.T) { + epochState := newEpochStateFromGenesis(t) + db := NewInMemoryDB(t) + // defining the db in the right context + epochState.db = db + epochState.baseState.db = db + + genesisHash := epochState.blockState.genesisHash + // setting a predefined slot number + predefinedSlotNumber := uint64(5) + buf := make([]byte, 8) + binary.LittleEndian.PutUint64(buf, predefinedSlotNumber) + err := epochState.blockState.db.Put(firstSlotNumberKey, buf) + require.NoError(t, err) + + slotNumber := 0 + + firstNonOrirginBlock := types.NewEmptyHeader() + firstNonOrirginBlock.ParentHash = genesisHash + firstNonOrirginBlock.Number = 1 + firstNonOrirginBlock.Digest = buildBlockPrimaryDigest(t, + types.BabePrimaryPreDigest{AuthorityIndex: 0, SlotNumber: uint64(slotNumber)}) + + err = epochState.blockState.AddBlock( + &types.Block{Header: *firstNonOrirginBlock, Body: *types.NewBody([]types.Extrinsic{})}) + require.NoError(t, err) + + babeHeader := types.NewBabeDigest() + err = babeHeader.SetValue(*types.NewBabePrimaryPreDigest(0, 5, [32]byte{}, [64]byte{})) + require.NoError(t, err) + + enc, err := scale.Marshal(babeHeader) + require.NoError(t, err) + digest := types.NewDigest() + preRuntimeDigest := types.PreRuntimeDigest{ + ConsensusEngineID: types.BabeEngineID, + Data: enc, + } + + preRuntimeDigestItem := types.NewDigestItem() + err = preRuntimeDigestItem.SetValue(preRuntimeDigest) + require.NoError(t, err) + preRuntimeDigestItemValue, err := preRuntimeDigestItem.Value() + require.NoError(t, err) + digest.Add(preRuntimeDigestItemValue) + + sealDigest := types.SealDigest{ + ConsensusEngineID: types.BabeEngineID, + Data: []byte{158, 127, 40, 221, 220, 242, 124, 30, 107, 50, 141, 86, 148, 195, 104, 213, 178, 236, 93, 190, + 14, 65, 42, 225, 201, 143, 136, 213, 59, 228, 216, 80, 47, 172, 87, 31, 63, 25, 201, 202, 175, 40, 26, + 103, 51, 25, 36, 30, 12, 80, 149, 166, 131, 173, 52, 49, 98, 4, 8, 138, 54, 164, 189, 134}, + } + + sealDigestItem := types.NewDigestItem() + err = sealDigestItem.SetValue(sealDigest) + require.NoError(t, err) + + sealDigestItemValue, err := sealDigestItem.Value() + require.NoError(t, err) + digest.Add(sealDigestItemValue) + + expectedHeader := &types.Header{ + ParentHash: common.MustHexToHash("0x3b45c9c22dcece75a30acc9c2968cb311e6b0557350f83b430f47559db786975"), + Number: 5, + StateRoot: common.MustHexToHash("0x09f9ca28df0560c2291aa16b56e15e07d1e1927088f51356d522722aa90ca7cb"), + ExtrinsicsRoot: common.MustHexToHash("0xda26dc8c1455f8f81cae12e4fc59e23ce961b2c837f6d3f664283af906d344e0"), + Digest: digest, + } + + keyring, _ := keystore.NewSr25519Keyring() + + keyPairs := []*sr25519.Keypair{ + keyring.KeyAlice, keyring.KeyBob, keyring.KeyCharlie, + } + + authorities := make([]types.AuthorityRaw, len(keyPairs)) + for i, keyPair := range keyPairs { + authorities[i] = types.AuthorityRaw{ + Key: keyPair.Public().(*sr25519.PublicKey).AsBytes(), + } + } + + genericNextEpochDigest := createBABEConsensusDigest(t, types.NextEpochData{ + Authorities: authorities, + Randomness: [32]byte{0, 1, 2, 3, 4, 5, 6, 7, 8}, + }) + + versionedNextConfigData := types.NewVersionedNextConfigData() + versionedNextConfigData.SetValue(types.NextConfigDataV1{ + C1: 9, + C2: 10, + SecondarySlots: 1, + }) + genericNextConfigDataDigest := createBABEConsensusDigest(t, versionedNextConfigData) + + consensusDigests := []types.ConsensusDigest{ + genericNextEpochDigest, genericNextConfigDataDigest, + } + + nextEpochData := types.NewBabeConsensusDigest() + err = scale.Unmarshal(consensusDigests[0].Data, &nextEpochData) + if err != nil { + t.Errorf("error unmarshalling next epoch data: %s", err) + } + + // Handle config and epoch data digests + err = epochState.HandleBABEDigest(expectedHeader, nextEpochData) + + require.NoError(t, err) + + nextConfigData := types.NewBabeConsensusDigest() + err = scale.Unmarshal(consensusDigests[1].Data, &nextConfigData) + if err != nil { + t.Errorf("error unmarshalling next config data: %s", err) + } + + err = epochState.HandleBABEDigest(expectedHeader, nextConfigData) + require.NoError(t, err) + + // Making sure that we have available storeSkipToEpoch prop on disk + err = epochState.baseState.storeSkipToEpoch(0) + require.NoError(t, err) + + epochState.blockState.SetHeader(expectedHeader) + require.NoError(t, err) + + // Finalize the next epoch data and config data + err = epochState.FinalizeBABENextConfigData(expectedHeader) + require.NoError(t, err) + + err = epochState.FinalizeBABENextEpochData(expectedHeader) + require.NoError(t, err) + + // Check if iterators are invalid, making sure that the data was deleted + iter, err := epochState.db.NewPrefixIterator(nextConfigDataPrefix) + require.NoError(t, err) + require.Equal(t, iter.Valid(), false) + epochState.db.NewPrefixIterator(nextEpochDataPrefix) + require.NoError(t, err) + require.Equal(t, iter.Valid(), false) +} diff --git a/dot/state/inmemory_storage.go b/dot/state/inmemory_storage.go index 592852836d..6e821a670e 100644 --- a/dot/state/inmemory_storage.go +++ b/dot/state/inmemory_storage.go @@ -34,7 +34,7 @@ type InmemoryStorageState struct { blockState *BlockState tries *Tries - db GetterPutterNewBatcher + db GetterPutterNewBatcherPrefixIter sync.RWMutex // change notifiers diff --git a/dot/state/interfaces.go b/dot/state/interfaces.go index 55b3f426fe..1a77516906 100644 --- a/dot/state/interfaces.go +++ b/dot/state/interfaces.go @@ -31,10 +31,11 @@ type GetPutter interface { // GetterPutterNewBatcher has methods to get values and create a // new batch. -type GetterPutterNewBatcher interface { +type GetterPutterNewBatcherPrefixIter interface { Getter Putter NewBatcher + NewPrefixIterator } // Getter gets a value corresponding to the given key. @@ -71,3 +72,7 @@ type BabeConfigurer interface { type Telemetry interface { SendMessage(msg json.Marshaler) } + +type NewPrefixIterator interface { + NewPrefixIterator(prefix []byte) (database.Iterator, error) +} diff --git a/internal/database/database.go b/internal/database/database.go index b3140a4623..b827c82d2b 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -60,6 +60,7 @@ type Table interface { Path() string NewBatch() Batch NewIterator() (Iterator, error) + NewPrefixIterator(prefix []byte) (Iterator, error) } const DefaultDatabaseDir = "db" diff --git a/internal/database/table.go b/internal/database/table.go index 3c6eb74793..4e03cb393e 100644 --- a/internal/database/table.go +++ b/internal/database/table.go @@ -59,3 +59,7 @@ func (t *table) NewBatch() Batch { func (t *table) NewIterator() (Iterator, error) { return t.db.NewPrefixIterator(t.prefix) } + +func (t *table) NewPrefixIterator(prefix []byte) (Iterator, error) { + return t.db.NewPrefixIterator(append(t.prefix, prefix...)) +} From 2503cf48e581d5f71f1a360bbfc4c69acd1cbd5a Mon Sep 17 00:00:00 2001 From: Ramiro Castillo <43893061+ramiroJCB@users.noreply.github.com> Date: Mon, 19 Aug 2024 23:11:14 -0500 Subject: [PATCH 05/13] fix(epoch): Fixing linting --- dot/state/epoch.go | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/dot/state/epoch.go b/dot/state/epoch.go index 6cc108e2d4..d4948c9110 100644 --- a/dot/state/epoch.go +++ b/dot/state/epoch.go @@ -173,7 +173,9 @@ func NewEpochState(db database.Database, blockState *BlockState, } // getNextEpochAndConfigDataFromDisk retrieves the next epoch and config data maps from the database -func getNextEpochAndConfigDataFromDisk(db database.Database) (nextEpochMap[types.NextEpochData], nextEpochMap[types.NextConfigDataV1], error) { +func getNextEpochAndConfigDataFromDisk(db database.Database) ( + nextEpochMap[types.NextEpochData], nextEpochMap[types.NextConfigDataV1], error) { + nextConfigData := make(nextEpochMap[types.NextConfigDataV1]) nextEpochData := make(nextEpochMap[types.NextEpochData]) @@ -199,8 +201,6 @@ func getNextEpochAndConfigDataFromDisk(db database.Database) (nextEpochMap[types nextConfigData[epoch][fork] = *nexEpochvalue } - configIter.Close() - epochIter, err := db.NewPrefixIterator(nextEpochDataPrefix) if err != nil { return nextEpochMap[types.NextEpochData]{}, nextEpochMap[types.NextConfigDataV1]{}, err @@ -224,13 +224,12 @@ func getNextEpochAndConfigDataFromDisk(db database.Database) (nextEpochMap[types } - epochIter.Close() - return nextEpochData, nextConfigData, nil } // getNextEpochOrConfigData retrieves the next epoch or config data from the iterator -func getNextEpochOrConfigData[T *types.NextConfigDataV1 | *types.NextEpochData](NextData T, nextDataPrefix []byte, iter database.Iterator) (T, uint64, common.Hash, error) { +func getNextEpochOrConfigData[T *types.NextConfigDataV1 | *types.NextEpochData]( + NextData T, nextDataPrefix []byte, iter database.Iterator) (T, uint64, common.Hash, error) { key := string(iter.Key()) value := iter.Value() @@ -941,7 +940,8 @@ func (s *EpochState) storeBABENextEpochData(epoch uint64, hash common.Hash, next } // setBABENextEpochDataInDB stores the types.NextEpochData under epoch and hash keys -func (s *EpochState) setBABENextEpochDataInDB(epoch uint64, forkHash common.Hash, nextEpochData types.NextEpochData) error { +func (s *EpochState) setBABENextEpochDataInDB(epoch uint64, forkHash common.Hash, + nextEpochData types.NextEpochData) error { encodedEpochData, err := scale.Marshal(nextEpochData) if err != nil { return err @@ -967,7 +967,8 @@ func (s *EpochState) storeBABENextConfigData(epoch uint64, hash common.Hash, nex } // setBABENextConfigData stores the types.NextConfigData under epoch and hash keys -func (s *EpochState) setBABENextConfigData(epoch uint64, forkHash common.Hash, nextConfigData types.NextConfigDataV1) error { +func (s *EpochState) setBABENextConfigData(epoch uint64, + forkHash common.Hash, nextConfigData types.NextConfigDataV1) error { encodedConfigData, err := scale.Marshal(nextConfigData) if err != nil { return err @@ -1056,7 +1057,10 @@ func (s *EpochState) deleteEpochDataFromDisk(epoch uint64) error { // getNextEpochOrConfigDataKeysFromDisk is a generic function that returns all the nextEpochData or nextConfigData keys // for a given epoch from the database -func getNextEpochOrConfigDataKeysFromDisk[T *types.NextEpochData | *types.NextConfigDataV1](db GetterPutterNewBatcherPrefixIter, value T, prefix []byte, currentEpoch uint64) ([]string, error) { +func getNextEpochOrConfigDataKeysFromDisk[T *types.NextEpochData | *types.NextConfigDataV1]( + db GetterPutterNewBatcherPrefixIter, value T, prefix []byte, currentEpoch uint64) ( + []string, error) { + dataKeys := []string{} iter, err := db.NewPrefixIterator(prefix) @@ -1079,8 +1083,6 @@ func getNextEpochOrConfigDataKeysFromDisk[T *types.NextEpochData | *types.NextCo } - iter.Close() - return dataKeys, nil } From db7747eff4322d3f056c230c48030823a3af418f Mon Sep 17 00:00:00 2001 From: Ramiro Castillo <43893061+ramiroJCB@users.noreply.github.com> Date: Tue, 20 Aug 2024 12:31:12 -0500 Subject: [PATCH 06/13] chore(epoch): Changing to more generic functions and approaches --- dot/state/epoch.go | 96 ++++++++++++++++++---------------------------- 1 file changed, 37 insertions(+), 59 deletions(-) diff --git a/dot/state/epoch.go b/dot/state/epoch.go index d4948c9110..ec18e554ab 100644 --- a/dot/state/epoch.go +++ b/dot/state/epoch.go @@ -52,15 +52,13 @@ func configDataKey(epoch uint64) []byte { } func nextEpochDataKey(epoch uint64, hash common.Hash) []byte { - // we add a "-" to the key to avoid conflicts with composite keys - key := fmt.Sprintf("-%d:%v", epoch, hash) - return append(nextEpochDataPrefix, []byte(key)...) + partialKey := fmt.Sprintf("-%d:%v", epoch, hash) + return append(nextEpochDataPrefix, []byte(partialKey)...) } func nextConfigDataKey(epoch uint64, hash common.Hash) []byte { - // we add a "-" to the key to avoid conflicts with composite keys - key := fmt.Sprintf("-%d:%v", epoch, hash) - return append(nextConfigDataPrefix, []byte(key)...) + partialKey := fmt.Sprintf("-%d:%v", epoch, hash) + return append(nextConfigDataPrefix, []byte(partialKey)...) } // GenesisEpochDescriptor is the informations provided by calling @@ -144,7 +142,12 @@ func NewEpochState(db database.Database, blockState *BlockState, return nil, err } - nextEpochData, nextConfigData, err := getNextEpochAndConfigDataFromDisk(db) + nextEpochData, err := restoreMapFromDisk[types.NextEpochData](db, nextEpochDataPrefix) + if err != nil { + return nil, err + } + + nextConfigData, err := restoreMapFromDisk[types.NextConfigDataV1](db, nextConfigDataPrefix) if err != nil { return nil, err } @@ -172,68 +175,45 @@ func NewEpochState(db database.Database, blockState *BlockState, }, nil } -// getNextEpochAndConfigDataFromDisk retrieves the next epoch and config data maps from the database -func getNextEpochAndConfigDataFromDisk(db database.Database) ( - nextEpochMap[types.NextEpochData], nextEpochMap[types.NextConfigDataV1], error) { - - nextConfigData := make(nextEpochMap[types.NextConfigDataV1]) - nextEpochData := make(nextEpochMap[types.NextEpochData]) +// restoreMapFromDisk retrieves the next epoch and config data maps from the database +func restoreMapFromDisk[T types.NextConfigDataV1 | types.NextEpochData](db database.Database, prefix []byte) ( + nextEpochMap[T], error) { - configIter, err := db.NewPrefixIterator(nextConfigDataPrefix) + resMap := make(nextEpochMap[T]) + iter, err := db.NewPrefixIterator(prefix) if err != nil { - return nextEpochMap[types.NextEpochData]{}, nextEpochMap[types.NextConfigDataV1]{}, err + return resMap, err } - defer configIter.Release() + defer iter.Release() - for configIter.First(); configIter.Valid(); configIter.Next() { - nexEpochvalue := new(types.NextConfigDataV1) + for iter.First(); iter.Valid(); iter.Next() { + mapValue, epoch, fork, err := getNextEpochOrConfigData[T](iter) - nexEpochvalue, epoch, fork, err := getNextEpochOrConfigData(nexEpochvalue, nextConfigDataPrefix, configIter) if err != nil { - return nextEpochMap[types.NextEpochData]{}, nextEpochMap[types.NextConfigDataV1]{}, err + return resMap, err } - if _, ok := nextConfigData[epoch]; !ok { - nextConfigData[epoch] = make(map[common.Hash]types.NextConfigDataV1) + if _, ok := resMap[epoch]; !ok { + resMap[epoch] = make(map[common.Hash]T) } - // Add data to the map - nextConfigData[epoch][fork] = *nexEpochvalue - } - epochIter, err := db.NewPrefixIterator(nextEpochDataPrefix) - if err != nil { - return nextEpochMap[types.NextEpochData]{}, nextEpochMap[types.NextConfigDataV1]{}, err + resMap[epoch][fork] = *mapValue } - defer epochIter.Release() - - for epochIter.First(); epochIter.Valid(); epochIter.Next() { - - nexEpochvalue := new(types.NextEpochData) - nexEpochvalue, epoch, fork, err := getNextEpochOrConfigData(nexEpochvalue, nextEpochDataPrefix, epochIter) - if err != nil { - return nextEpochMap[types.NextEpochData]{}, nextEpochMap[types.NextConfigDataV1]{}, err - } - // Add data to the map - if _, ok := nextEpochData[epoch]; !ok { - nextEpochData[epoch] = make(map[common.Hash]types.NextEpochData) - } - - nextEpochData[epoch][fork] = *nexEpochvalue - + if err = iter.Close(); err != nil { + return resMap, err } - - return nextEpochData, nextConfigData, nil + return resMap, nil } // getNextEpochOrConfigData retrieves the next epoch or config data from the iterator -func getNextEpochOrConfigData[T *types.NextConfigDataV1 | *types.NextEpochData]( - NextData T, nextDataPrefix []byte, iter database.Iterator) (T, uint64, common.Hash, error) { +func getNextEpochOrConfigData[T types.NextConfigDataV1 | types.NextEpochData](iter database.Iterator) (*T, uint64, common.Hash, error) { + nextData := new(T) key := string(iter.Key()) value := iter.Value() - keyWithoutPrefix := strings.TrimPrefix(key, string(nextDataPrefix)+"-") + keyWithoutPrefix := strings.Split(key, "-")[1] // Split the key into epoch and fork parts := strings.Split(keyWithoutPrefix, ":") @@ -250,11 +230,11 @@ func getNextEpochOrConfigData[T *types.NextConfigDataV1 | *types.NextEpochData]( copy(fork[:], part1) - if err = scale.Unmarshal(value, NextData); err != nil { + if err = scale.Unmarshal(value, nextData); err != nil { return nil, 0, common.Hash{}, err } - return NextData, epoch, fork, nil + return nextData, epoch, fork, nil } // GetEpochLength returns the length of an epoch in slots @@ -1040,8 +1020,7 @@ func (s *EpochState) FinalizeBABENextEpochData(finalizedHeader *types.Header) er } func (s *EpochState) deleteEpochDataFromDisk(epoch uint64) error { - nexEpochvalue := new(types.NextEpochData) - configKeysToDelete, err := getNextEpochOrConfigDataKeysFromDisk(s.db, nexEpochvalue, nextEpochDataPrefix, epoch) + configKeysToDelete, err := getDataKeysFromDisk[types.NextEpochData](s.db, nextEpochDataPrefix, epoch) if err != nil { return fmt.Errorf("cannot get next config data keys from disk: %w", err) } @@ -1055,10 +1034,10 @@ func (s *EpochState) deleteEpochDataFromDisk(epoch uint64) error { return nil } -// getNextEpochOrConfigDataKeysFromDisk is a generic function that returns all the nextEpochData or nextConfigData keys +// getDataKeysFromDisk is a generic function that returns all the nextEpochData or nextConfigData keys // for a given epoch from the database -func getNextEpochOrConfigDataKeysFromDisk[T *types.NextEpochData | *types.NextConfigDataV1]( - db GetterPutterNewBatcherPrefixIter, value T, prefix []byte, currentEpoch uint64) ( +func getDataKeysFromDisk[T types.NextEpochData | types.NextConfigDataV1]( + db GetterPutterNewBatcherPrefixIter, prefix []byte, currentEpoch uint64) ( []string, error) { dataKeys := []string{} @@ -1071,7 +1050,7 @@ func getNextEpochOrConfigDataKeysFromDisk[T *types.NextEpochData | *types.NextCo defer iter.Release() for iter.First(); iter.Valid(); iter.Next() { - _, epoch, fork, err := getNextEpochOrConfigData(value, prefix, iter) + _, epoch, fork, err := getNextEpochOrConfigData[T](iter) if err != nil { return dataKeys, err } @@ -1153,8 +1132,7 @@ func (s *EpochState) FinalizeBABENextConfigData(finalizedHeader *types.Header) e } func (s *EpochState) deleteNextConfigDataFromDisk(epoch uint64) error { - nextConfigValue := new(types.NextConfigDataV1) - configKeysToDelete, err := getNextEpochOrConfigDataKeysFromDisk(s.db, nextConfigValue, nextConfigDataPrefix, epoch) + configKeysToDelete, err := getDataKeysFromDisk[types.NextConfigDataV1](s.db, nextConfigDataPrefix, epoch) if err != nil { return fmt.Errorf("cannot get next config data keys from disk: %w", err) } From 94c41611719b7c680f250f86c4fedfd6f6020e27 Mon Sep 17 00:00:00 2001 From: Ramiro Castillo <43893061+ramiroJCB@users.noreply.github.com> Date: Tue, 20 Aug 2024 12:45:18 -0500 Subject: [PATCH 07/13] fix(epoch): Linting issue --- dot/state/epoch.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dot/state/epoch.go b/dot/state/epoch.go index ec18e554ab..e46f2c0539 100644 --- a/dot/state/epoch.go +++ b/dot/state/epoch.go @@ -208,7 +208,8 @@ func restoreMapFromDisk[T types.NextConfigDataV1 | types.NextEpochData](db datab } // getNextEpochOrConfigData retrieves the next epoch or config data from the iterator -func getNextEpochOrConfigData[T types.NextConfigDataV1 | types.NextEpochData](iter database.Iterator) (*T, uint64, common.Hash, error) { +func getNextEpochOrConfigData[T types.NextConfigDataV1 | types.NextEpochData](iter database.Iterator) ( + *T, uint64, common.Hash, error) { nextData := new(T) key := string(iter.Key()) value := iter.Value() From 078649c0e82cf8ac17213b4a6f84faf5eb1cb6db Mon Sep 17 00:00:00 2001 From: Ramiro Castillo <43893061+ramiroJCB@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:05:37 -0500 Subject: [PATCH 08/13] chore(epoch): Adding correct use of batch and updating deletion test --- dot/state/epoch.go | 34 +++++++++++++++++++++++----------- dot/state/epoch_test.go | 11 +++++------ dot/state/inmemory_storage.go | 2 +- dot/state/interfaces.go | 7 +------ 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/dot/state/epoch.go b/dot/state/epoch.go index e46f2c0539..4162fa5bd7 100644 --- a/dot/state/epoch.go +++ b/dot/state/epoch.go @@ -70,7 +70,7 @@ type GenesisEpochDescriptor struct { // EpochState tracks information related to each epoch type EpochState struct { - db GetterPutterNewBatcherPrefixIter + db database.Table baseState *BaseState blockState *BlockState epochLength uint64 // measured in slots @@ -603,7 +603,7 @@ type prefixedKeyBuilder func(epoch uint64) []byte // updateEpochDefinitionKey updates the informations from database // by querying the raw bytes from prefix + oldEpoch and inserting // at prefix + newEpoch and return the values stored at prefix + oldEpoch -func updateEpochDefinitionKey(db GetterPutterNewBatcherPrefixIter, +func updateEpochDefinitionKey(db GetterPutterNewBatcher, oldEpoch, newEpoch uint64, usePrefix prefixedKeyBuilder) ([]byte, error) { rawBytes, err := db.Get(usePrefix(oldEpoch)) if err != nil { @@ -635,7 +635,7 @@ func updateEpochDefinitionKey(db GetterPutterNewBatcherPrefixIter, } func getAndUpdateEpochDefinitionKey[T types.ConfigData | types.EpochDataRaw]( - db GetterPutterNewBatcherPrefixIter, oldEpoch, newEpoch uint64, usePrefix prefixedKeyBuilder) (*T, error) { + db GetterPutterNewBatcher, oldEpoch, newEpoch uint64, usePrefix prefixedKeyBuilder) (*T, error) { rawBytes, err := updateEpochDefinitionKey(db, oldEpoch, newEpoch, usePrefix) if err != nil { return nil, fmt.Errorf("updating epoch key definition: %w", err) @@ -1025,20 +1025,23 @@ func (s *EpochState) deleteEpochDataFromDisk(epoch uint64) error { if err != nil { return fmt.Errorf("cannot get next config data keys from disk: %w", err) } - + batch := s.db.NewBatch() for _, key := range configKeysToDelete { - err = s.db.NewBatch().Del([]byte(key)) + err = batch.Del([]byte(key)) if err != nil { return fmt.Errorf("cannot delete next config data from the database: %w", err) } } + if err := batch.Flush(); err != nil { + return fmt.Errorf("cannot flush deletion batch: %w", err) + } return nil } // getDataKeysFromDisk is a generic function that returns all the nextEpochData or nextConfigData keys // for a given epoch from the database func getDataKeysFromDisk[T types.NextEpochData | types.NextConfigDataV1]( - db GetterPutterNewBatcherPrefixIter, prefix []byte, currentEpoch uint64) ( + db database.Table, prefix []byte, currentEpoch uint64) ( []string, error) { dataKeys := []string{} @@ -1051,14 +1054,17 @@ func getDataKeysFromDisk[T types.NextEpochData | types.NextConfigDataV1]( defer iter.Release() for iter.First(); iter.Valid(); iter.Next() { - _, epoch, fork, err := getNextEpochOrConfigData[T](iter) + key := string(iter.Key()) + + keyWithoutPrefix := strings.Split(key, "-")[1] + epochPart := strings.Split(keyWithoutPrefix, ":")[0] + epoch, err := strconv.ParseUint(epochPart, 10, 64) if err != nil { return dataKeys, err } - // if the epoch is the current epoch, then we append the key to the dataKeys + if epoch == currentEpoch { - key := append(prefix, []byte(fmt.Sprintf("-%d:%s", epoch, fork))...) - dataKeys = append(dataKeys, string(key)) + dataKeys = append(dataKeys, key) } } @@ -1138,12 +1144,18 @@ func (s *EpochState) deleteNextConfigDataFromDisk(epoch uint64) error { return fmt.Errorf("cannot get next config data keys from disk: %w", err) } + batch := s.db.NewBatch() for _, key := range configKeysToDelete { - err = s.db.NewBatch().Del([]byte(key)) + err = batch.Del([]byte(key)) if err != nil { return fmt.Errorf("cannot delete next config data from the database: %w", err) } } + + if err := batch.Flush(); err != nil { + return fmt.Errorf("cannot flush deletion batch: %w", err) + } + return nil } diff --git a/dot/state/epoch_test.go b/dot/state/epoch_test.go index 35b057fd33..87baeea83e 100644 --- a/dot/state/epoch_test.go +++ b/dot/state/epoch_test.go @@ -1138,11 +1138,10 @@ func TestDeleteNextEpochDataAndConfig(t *testing.T) { err = epochState.FinalizeBABENextEpochData(expectedHeader) require.NoError(t, err) - // Check if iterators are invalid, making sure that the data was deleted - iter, err := epochState.db.NewPrefixIterator(nextConfigDataPrefix) - require.NoError(t, err) - require.Equal(t, iter.Valid(), false) - epochState.db.NewPrefixIterator(nextEpochDataPrefix) + // Check if the next epoch data and config data are not stored in the database + // after finalization + epochState, err = NewEpochState(db, epochState.blockState, config.BABEConfigurationTestDefault) require.NoError(t, err) - require.Equal(t, iter.Valid(), false) + require.Equal(t, 0, len(epochState.nextEpochData)) + require.Equal(t, 0, len(epochState.nextConfigData)) } diff --git a/dot/state/inmemory_storage.go b/dot/state/inmemory_storage.go index 6e821a670e..592852836d 100644 --- a/dot/state/inmemory_storage.go +++ b/dot/state/inmemory_storage.go @@ -34,7 +34,7 @@ type InmemoryStorageState struct { blockState *BlockState tries *Tries - db GetterPutterNewBatcherPrefixIter + db GetterPutterNewBatcher sync.RWMutex // change notifiers diff --git a/dot/state/interfaces.go b/dot/state/interfaces.go index 1a77516906..55b3f426fe 100644 --- a/dot/state/interfaces.go +++ b/dot/state/interfaces.go @@ -31,11 +31,10 @@ type GetPutter interface { // GetterPutterNewBatcher has methods to get values and create a // new batch. -type GetterPutterNewBatcherPrefixIter interface { +type GetterPutterNewBatcher interface { Getter Putter NewBatcher - NewPrefixIterator } // Getter gets a value corresponding to the given key. @@ -72,7 +71,3 @@ type BabeConfigurer interface { type Telemetry interface { SendMessage(msg json.Marshaler) } - -type NewPrefixIterator interface { - NewPrefixIterator(prefix []byte) (database.Iterator, error) -} From 809587d66f47df123d7f78a0f0a338800449b150 Mon Sep 17 00:00:00 2001 From: Ramiro Castillo <43893061+ramiroJCB@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:12:08 -0500 Subject: [PATCH 09/13] chore(epoch): Grammar error --- dot/state/epoch_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dot/state/epoch_test.go b/dot/state/epoch_test.go index 87baeea83e..a8ade945db 100644 --- a/dot/state/epoch_test.go +++ b/dot/state/epoch_test.go @@ -1139,7 +1139,7 @@ func TestDeleteNextEpochDataAndConfig(t *testing.T) { require.NoError(t, err) // Check if the next epoch data and config data are not stored in the database - // after finalization + // after finalisation epochState, err = NewEpochState(db, epochState.blockState, config.BABEConfigurationTestDefault) require.NoError(t, err) require.Equal(t, 0, len(epochState.nextEpochData)) From 49f10d15c58404c58b2dcbaac8515ddd19cac8f0 Mon Sep 17 00:00:00 2001 From: Ramiro Castillo <43893061+ramiroJCB@users.noreply.github.com> Date: Wed, 4 Sep 2024 09:20:06 -0500 Subject: [PATCH 10/13] chore(epoch): Mergin two functions into one generic --- dot/state/epoch.go | 35 ++++++++--------------------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/dot/state/epoch.go b/dot/state/epoch.go index 4162fa5bd7..97deef7299 100644 --- a/dot/state/epoch.go +++ b/dot/state/epoch.go @@ -1011,7 +1011,7 @@ func (s *EpochState) FinalizeBABENextEpochData(finalizedHeader *types.Header) er if e <= nextEpoch { delete(s.nextEpochData, e) // remove the epoch data from the database - if err = s.deleteEpochDataFromDisk(e); err != nil { + if err = deleteDataFromDisk[types.NextEpochData](s.db, e, nextEpochDataPrefix); err != nil { return fmt.Errorf("cannot delete next epoch data from the database: %w", err) } } @@ -1020,13 +1020,15 @@ func (s *EpochState) FinalizeBABENextEpochData(finalizedHeader *types.Header) er return nil } -func (s *EpochState) deleteEpochDataFromDisk(epoch uint64) error { - configKeysToDelete, err := getDataKeysFromDisk[types.NextEpochData](s.db, nextEpochDataPrefix, epoch) +// deleteDataFromDisk is a generic function that deletes all the nextEpochData or nextConfigData +// for a given epoch from the database +func deleteDataFromDisk[T types.NextEpochData | types.NextConfigDataV1](db database.Table, epoch uint64, prefix []byte) error { + keysToDelete, err := getDataKeysFromDisk[T](db, prefix, epoch) if err != nil { return fmt.Errorf("cannot get next config data keys from disk: %w", err) } - batch := s.db.NewBatch() - for _, key := range configKeysToDelete { + batch := db.NewBatch() + for _, key := range keysToDelete { err = batch.Del([]byte(key)) if err != nil { return fmt.Errorf("cannot delete next config data from the database: %w", err) @@ -1129,7 +1131,7 @@ func (s *EpochState) FinalizeBABENextConfigData(finalizedHeader *types.Header) e if e <= nextEpoch { delete(s.nextConfigData, e) // remove the config data from the database - if err = s.deleteNextConfigDataFromDisk(e); err != nil { + if err = deleteDataFromDisk[types.NextConfigDataV1](s.db, e, nextConfigDataPrefix); err != nil { return fmt.Errorf("cannot delete next config data from the database: %w", err) } } @@ -1138,27 +1140,6 @@ func (s *EpochState) FinalizeBABENextConfigData(finalizedHeader *types.Header) e return nil } -func (s *EpochState) deleteNextConfigDataFromDisk(epoch uint64) error { - configKeysToDelete, err := getDataKeysFromDisk[types.NextConfigDataV1](s.db, nextConfigDataPrefix, epoch) - if err != nil { - return fmt.Errorf("cannot get next config data keys from disk: %w", err) - } - - batch := s.db.NewBatch() - for _, key := range configKeysToDelete { - err = batch.Del([]byte(key)) - if err != nil { - return fmt.Errorf("cannot delete next config data from the database: %w", err) - } - } - - if err := batch.Flush(); err != nil { - return fmt.Errorf("cannot flush deletion batch: %w", err) - } - - return nil -} - // findFinalizedHeaderForEpoch given a specific epoch (the key) will go through the hashes looking // for a database persisted hash (belonging to the finalized chain) // which contains the right configuration or data to be persisted and safely used From a79153001eb9cf84412a89275cd69a53baf9b257 Mon Sep 17 00:00:00 2001 From: Ramiro Castillo <43893061+ramiroJCB@users.noreply.github.com> Date: Wed, 4 Sep 2024 09:29:32 -0500 Subject: [PATCH 11/13] fix(epoch): Linting line 1021 --- dot/state/epoch.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dot/state/epoch.go b/dot/state/epoch.go index 97deef7299..3026a32d52 100644 --- a/dot/state/epoch.go +++ b/dot/state/epoch.go @@ -1022,7 +1022,8 @@ func (s *EpochState) FinalizeBABENextEpochData(finalizedHeader *types.Header) er // deleteDataFromDisk is a generic function that deletes all the nextEpochData or nextConfigData // for a given epoch from the database -func deleteDataFromDisk[T types.NextEpochData | types.NextConfigDataV1](db database.Table, epoch uint64, prefix []byte) error { +func deleteDataFromDisk[T types.NextEpochData | types.NextConfigDataV1]( + db database.Table, epoch uint64, prefix []byte) error { keysToDelete, err := getDataKeysFromDisk[T](db, prefix, epoch) if err != nil { return fmt.Errorf("cannot get next config data keys from disk: %w", err) From 6588c9dad956fb58ec2c5191a2240ba27acb8e3c Mon Sep 17 00:00:00 2001 From: Ramiro Castillo <43893061+ramiroJCB@users.noreply.github.com> Date: Fri, 13 Sep 2024 11:56:26 -0500 Subject: [PATCH 12/13] chore(epoch): Fixing local issues and adjust the db context to be right when running gossamer --- dot/state/epoch.go | 40 +++++++++++++++++++--------------------- dot/state/epoch_test.go | 16 ++++++++++++---- lib/babe/babe.go | 8 ++++++-- 3 files changed, 37 insertions(+), 27 deletions(-) diff --git a/dot/state/epoch.go b/dot/state/epoch.go index 3026a32d52..45a5267e3f 100644 --- a/dot/state/epoch.go +++ b/dot/state/epoch.go @@ -52,12 +52,12 @@ func configDataKey(epoch uint64) []byte { } func nextEpochDataKey(epoch uint64, hash common.Hash) []byte { - partialKey := fmt.Sprintf("-%d:%v", epoch, hash) + partialKey := fmt.Sprintf("-%d:%s", epoch, hash.String()) return append(nextEpochDataPrefix, []byte(partialKey)...) } func nextConfigDataKey(epoch uint64, hash common.Hash) []byte { - partialKey := fmt.Sprintf("-%d:%v", epoch, hash) + partialKey := fmt.Sprintf("-%d:%s", epoch, hash.String()) return append(nextConfigDataPrefix, []byte(partialKey)...) } @@ -141,13 +141,13 @@ func NewEpochState(db database.Database, blockState *BlockState, if err != nil { return nil, err } - - nextEpochData, err := restoreMapFromDisk[types.NextEpochData](db, nextEpochDataPrefix) + epochTable := database.NewTable(db, epochPrefix) + nextEpochData, err := restoreMapFromDisk[types.NextEpochData](epochTable, nextEpochDataPrefix) if err != nil { return nil, err } - nextConfigData, err := restoreMapFromDisk[types.NextConfigDataV1](db, nextConfigDataPrefix) + nextConfigData, err := restoreMapFromDisk[types.NextConfigDataV1](epochTable, nextConfigDataPrefix) if err != nil { return nil, err } @@ -155,7 +155,7 @@ func NewEpochState(db database.Database, blockState *BlockState, return &EpochState{ baseState: baseState, blockState: blockState, - db: database.NewTable(db, epochPrefix), + db: epochTable, epochLength: genesisConfig.EpochLength, slotDuration: genesisConfig.SlotDuration, skipToEpoch: skipToEpoch, @@ -176,7 +176,7 @@ func NewEpochState(db database.Database, blockState *BlockState, } // restoreMapFromDisk retrieves the next epoch and config data maps from the database -func restoreMapFromDisk[T types.NextConfigDataV1 | types.NextEpochData](db database.Database, prefix []byte) ( +func restoreMapFromDisk[T types.NextConfigDataV1 | types.NextEpochData](db database.Table, prefix []byte) ( nextEpochMap[T], error) { resMap := make(nextEpochMap[T]) @@ -227,7 +227,10 @@ func getNextEpochOrConfigData[T types.NextConfigDataV1 | types.NextEpochData](it } var fork common.Hash - part1 := []byte(parts[1]) + part1, err := common.HexToBytes(parts[1]) + if err != nil { + return nil, 0, common.Hash{}, fmt.Errorf("while converting bytes to hash: %w", err) + } copy(fork[:], part1) @@ -274,7 +277,7 @@ func (s *EpochState) GetEpochForBlock(header *types.Header) (uint64, error) { // actually the epoch number for block number #1 is epoch 0, // epochs start from 0 and are incremented (almost, given that epochs might be skipped) // sequentially 0...1...2, so the block number #1 belongs to epoch 0 - if header.Number == 1 { + if header.Number == 0 || header.Number == 1 { return 0, nil } @@ -1035,9 +1038,11 @@ func deleteDataFromDisk[T types.NextEpochData | types.NextConfigDataV1]( return fmt.Errorf("cannot delete next config data from the database: %w", err) } } + if err := batch.Flush(); err != nil { return fmt.Errorf("cannot flush deletion batch: %w", err) } + return nil } @@ -1048,8 +1053,9 @@ func getDataKeysFromDisk[T types.NextEpochData | types.NextConfigDataV1]( []string, error) { dataKeys := []string{} + currentEpochPrefix := fmt.Sprintf("%s-%d", prefix, currentEpoch) - iter, err := db.NewPrefixIterator(prefix) + iter, err := db.NewPrefixIterator([]byte(currentEpochPrefix)) if err != nil { return dataKeys, err } @@ -1058,17 +1064,9 @@ func getDataKeysFromDisk[T types.NextEpochData | types.NextConfigDataV1]( for iter.First(); iter.Valid(); iter.Next() { key := string(iter.Key()) - - keyWithoutPrefix := strings.Split(key, "-")[1] - epochPart := strings.Split(keyWithoutPrefix, ":")[0] - epoch, err := strconv.ParseUint(epochPart, 10, 64) - if err != nil { - return dataKeys, err - } - - if epoch == currentEpoch { - dataKeys = append(dataKeys, key) - } + index := strings.Index(key, epochPrefix) + secondPart := key[index+len(epochPrefix):] + dataKeys = append(dataKeys, secondPart) } diff --git a/dot/state/epoch_test.go b/dot/state/epoch_test.go index a8ade945db..960d971af4 100644 --- a/dot/state/epoch_test.go +++ b/dot/state/epoch_test.go @@ -9,6 +9,7 @@ import ( "time" "github.com/ChainSafe/gossamer/dot/types" + "github.com/ChainSafe/gossamer/internal/database" "github.com/ChainSafe/gossamer/lib/common" "github.com/ChainSafe/gossamer/lib/crypto/sr25519" "github.com/ChainSafe/gossamer/lib/keystore" @@ -859,7 +860,8 @@ func TestFirstSlotNumberFromDb(t *testing.T) { func TestNextEpochDataAndConfigInDisk(t *testing.T) { epochState := newEpochStateFromGenesis(t) db := NewInMemoryDB(t) - epochState.db = db + dbTable := database.NewTable(db, epochPrefix) + epochState.db = dbTable slotDuration, err := epochState.GetSlotDuration() require.NoError(t, err) @@ -1009,8 +1011,8 @@ func TestDeleteNextEpochDataAndConfig(t *testing.T) { epochState := newEpochStateFromGenesis(t) db := NewInMemoryDB(t) // defining the db in the right context - epochState.db = db - epochState.baseState.db = db + dbTable := database.NewTable(db, epochPrefix) + epochState.db = dbTable genesisHash := epochState.blockState.genesisHash // setting a predefined slot number @@ -1125,9 +1127,16 @@ func TestDeleteNextEpochDataAndConfig(t *testing.T) { require.NoError(t, err) // Making sure that we have available storeSkipToEpoch prop on disk + epochState.baseState.db = db err = epochState.baseState.storeSkipToEpoch(0) require.NoError(t, err) + // Check if the next epoch data and config data are stored in the database + epochState, err = NewEpochState(db, epochState.blockState, config.BABEConfigurationTestDefault) + require.NoError(t, err) + require.Equal(t, 1, len(epochState.nextEpochData)) + require.Equal(t, 1, len(epochState.nextConfigData)) + epochState.blockState.SetHeader(expectedHeader) require.NoError(t, err) @@ -1137,7 +1146,6 @@ func TestDeleteNextEpochDataAndConfig(t *testing.T) { err = epochState.FinalizeBABENextEpochData(expectedHeader) require.NoError(t, err) - // Check if the next epoch data and config data are not stored in the database // after finalisation epochState, err = NewEpochState(db, epochState.blockState, config.BABEConfigurationTestDefault) diff --git a/lib/babe/babe.go b/lib/babe/babe.go index 0b6707c417..06ed8d25ed 100644 --- a/lib/babe/babe.go +++ b/lib/babe/babe.go @@ -300,11 +300,15 @@ func (b *Service) initiateAndGetEpochHandler(epoch uint64) (*epochHandler, error } func (b *Service) runEngine() error { - epoch, err := b.epochState.GetCurrentEpoch() + bestBlock, err := b.blockState.BestBlockHeader() if err != nil { - return fmt.Errorf("failed to get current epoch: %s", err) + return fmt.Errorf("getting best block: %w", err) } + epoch, err := b.epochState.GetEpochForBlock(bestBlock) + if err != nil { + return fmt.Errorf("failed to get current epoch: %s", err) + } for { next, err := b.handleEpoch(epoch) if errors.Is(err, errServicePaused) || errors.Is(err, context.Canceled) { From 4e635883bed56ba831ea1573cee105cbd6735231 Mon Sep 17 00:00:00 2001 From: Ramiro Castillo <43893061+ramiroJCB@users.noreply.github.com> Date: Mon, 30 Sep 2024 20:02:57 -0500 Subject: [PATCH 13/13] chore(epoch): Solving deepsource issues and removing hyphen --- dot/state/epoch.go | 14 +++++++------- dot/state/epoch_test.go | 30 +++++++++++++++--------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/dot/state/epoch.go b/dot/state/epoch.go index 45a5267e3f..390818eef4 100644 --- a/dot/state/epoch.go +++ b/dot/state/epoch.go @@ -52,12 +52,12 @@ func configDataKey(epoch uint64) []byte { } func nextEpochDataKey(epoch uint64, hash common.Hash) []byte { - partialKey := fmt.Sprintf("-%d:%s", epoch, hash.String()) + partialKey := fmt.Sprintf("%d:%s", epoch, hash.String()) return append(nextEpochDataPrefix, []byte(partialKey)...) } func nextConfigDataKey(epoch uint64, hash common.Hash) []byte { - partialKey := fmt.Sprintf("-%d:%s", epoch, hash.String()) + partialKey := fmt.Sprintf("%d:%s", epoch, hash.String()) return append(nextConfigDataPrefix, []byte(partialKey)...) } @@ -188,7 +188,7 @@ func restoreMapFromDisk[T types.NextConfigDataV1 | types.NextEpochData](db datab defer iter.Release() for iter.First(); iter.Valid(); iter.Next() { - mapValue, epoch, fork, err := getNextEpochOrConfigData[T](iter) + mapValue, epoch, fork, err := getNextEpochOrConfigData[T](iter, prefix) if err != nil { return resMap, err @@ -208,13 +208,13 @@ func restoreMapFromDisk[T types.NextConfigDataV1 | types.NextEpochData](db datab } // getNextEpochOrConfigData retrieves the next epoch or config data from the iterator -func getNextEpochOrConfigData[T types.NextConfigDataV1 | types.NextEpochData](iter database.Iterator) ( +func getNextEpochOrConfigData[T types.NextConfigDataV1 | types.NextEpochData](iter database.Iterator, prefix []byte) ( *T, uint64, common.Hash, error) { nextData := new(T) key := string(iter.Key()) value := iter.Value() - keyWithoutPrefix := strings.Split(key, "-")[1] + keyWithoutPrefix := strings.Split(key, string(prefix))[1] // Split the key into epoch and fork parts := strings.Split(keyWithoutPrefix, ":") @@ -1052,8 +1052,8 @@ func getDataKeysFromDisk[T types.NextEpochData | types.NextConfigDataV1]( db database.Table, prefix []byte, currentEpoch uint64) ( []string, error) { - dataKeys := []string{} - currentEpochPrefix := fmt.Sprintf("%s-%d", prefix, currentEpoch) + var dataKeys []string + currentEpochPrefix := fmt.Sprintf("%s%d", prefix, currentEpoch) iter, err := db.NewPrefixIterator([]byte(currentEpochPrefix)) if err != nil { diff --git a/dot/state/epoch_test.go b/dot/state/epoch_test.go index 960d971af4..1cffed5684 100644 --- a/dot/state/epoch_test.go +++ b/dot/state/epoch_test.go @@ -19,7 +19,7 @@ import ( "github.com/stretchr/testify/require" ) -func newEpochStateFromGenesis(t *testing.T) *EpochState { +func newTestEpochStateFromGenesis(t *testing.T) *EpochState { db := NewInMemoryDB(t) blockState := newTestBlockState(t, newTriesEmpty()) s, err := NewEpochStateFromGenesis(db, blockState, config.BABEConfigurationTestDefault) @@ -28,11 +28,11 @@ func newEpochStateFromGenesis(t *testing.T) *EpochState { } func TestNewEpochStateFromGenesis(t *testing.T) { - _ = newEpochStateFromGenesis(t) + _ = newTestEpochStateFromGenesis(t) } func TestEpochState_CurrentEpoch(t *testing.T) { - s := newEpochStateFromGenesis(t) + s := newTestEpochStateFromGenesis(t) epoch, err := s.GetCurrentEpoch() require.NoError(t, err) require.Equal(t, uint64(0), epoch) @@ -45,7 +45,7 @@ func TestEpochState_CurrentEpoch(t *testing.T) { } func TestEpochState_EpochData(t *testing.T) { - s := newEpochStateFromGenesis(t) + s := newTestEpochStateFromGenesis(t) keyring, err := keystore.NewSr25519Keyring() require.NoError(t, err) @@ -72,7 +72,7 @@ func TestEpochState_EpochData(t *testing.T) { } func TestEpochState_GetStartSlotForEpoch(t *testing.T) { - s := newEpochStateFromGenesis(t) + s := newTestEpochStateFromGenesis(t) // let's say first slot is 1 second after January 1, 1970 UTC startAtTime := time.Unix(1, 0) @@ -112,7 +112,7 @@ func TestEpochState_GetStartSlotForEpoch(t *testing.T) { } func TestEpochState_ConfigData(t *testing.T) { - s := newEpochStateFromGenesis(t) + s := newTestEpochStateFromGenesis(t) data := &types.ConfigData{ C1: 1, @@ -154,7 +154,7 @@ func createAndImportBlockOne(t *testing.T, slotNumber uint64, blockState *BlockS } func TestEpochState_GetEpochForBlock(t *testing.T) { - s := newEpochStateFromGenesis(t) + s := newTestEpochStateFromGenesis(t) firstSlot := uint64(1) blockOneHeader := createAndImportBlockOne(t, firstSlot, s.blockState) @@ -211,7 +211,7 @@ func TestEpochState_GetEpochForBlock(t *testing.T) { } func TestEpochState_SetAndGetSlotDuration(t *testing.T) { - s := newEpochStateFromGenesis(t) + s := newTestEpochStateFromGenesis(t) expected := time.Millisecond * time.Duration(config.BABEConfigurationTestDefault.SlotDuration) ret, err := s.GetSlotDuration() @@ -378,7 +378,7 @@ func TestStoreAndFinalizeBabeNextEpochData(t *testing.T) { for testName, tt := range tests { t.Run(testName, func(t *testing.T) { - epochState := newEpochStateFromGenesis(t) + epochState := newTestEpochStateFromGenesis(t) for _, e := range tt.inMemoryEpoch { for i, hash := range e.hashes { @@ -561,7 +561,7 @@ func TestStoreAndFinalizeBabeNextConfigData(t *testing.T) { for testName, tt := range tests { t.Run(testName, func(t *testing.T) { - epochState := newEpochStateFromGenesis(t) + epochState := newTestEpochStateFromGenesis(t) for _, finalized := range finalizedHeaders { // mapping number #1 to the block hash @@ -642,7 +642,7 @@ func TestRetrieveChainFirstSlot(t *testing.T) { // epoch calculation, same for blocks on X // when finalisation happens Gossamer should retrieve the chain first // slot for the finalized chain, given that the other chain will be pruned - singleEpochState := newEpochStateFromGenesis(t) + singleEpochState := newTestEpochStateFromGenesis(t) // calling without any block it must return error _, err := singleEpochState.retrieveFirstNonOriginBlockSlot(common.Hash{}) @@ -734,7 +734,7 @@ func TestRetrieveChainFirstSlot(t *testing.T) { } func TestRetrieveAndUpdate(t *testing.T) { - epochState := newEpochStateFromGenesis(t) + epochState := newTestEpochStateFromGenesis(t) blockState := epochState.blockState nem := nextEpochMap[types.NextEpochData]{} @@ -824,7 +824,7 @@ func TestRetrieveAndUpdate(t *testing.T) { func TestFirstSlotNumberFromDb(t *testing.T) { // test case to check whether we have the correct first slot number in the database - epochState := newEpochStateFromGenesis(t) + epochState := newTestEpochStateFromGenesis(t) slotDuration, err := epochState.GetSlotDuration() require.NoError(t, err) @@ -858,7 +858,7 @@ func TestFirstSlotNumberFromDb(t *testing.T) { } func TestNextEpochDataAndConfigInDisk(t *testing.T) { - epochState := newEpochStateFromGenesis(t) + epochState := newTestEpochStateFromGenesis(t) db := NewInMemoryDB(t) dbTable := database.NewTable(db, epochPrefix) epochState.db = dbTable @@ -1008,7 +1008,7 @@ func createBABEConsensusDigest(t *testing.T, digestData any) types.ConsensusDige } func TestDeleteNextEpochDataAndConfig(t *testing.T) { - epochState := newEpochStateFromGenesis(t) + epochState := newTestEpochStateFromGenesis(t) db := NewInMemoryDB(t) // defining the db in the right context dbTable := database.NewTable(db, epochPrefix)