diff --git a/kaiax/valset/README.md b/kaiax/valset/README.md index 3f6f01c95..8cba0e4df 100644 --- a/kaiax/valset/README.md +++ b/kaiax/valset/README.md @@ -412,9 +412,9 @@ proposer x 1 3 2 0 3 2 3 1 2 3 ``` "council" || Uint64BE(num) => JSON.Marshal([addr1, addr2, ...]) ``` -- `lowestCheckpointScannedBlockNum`: The lowest block number whose vote data and council is calculated from the legacy istanbul snapshot schema. That is, only vote block numbers are greater than or equal to this value are stored in `validatorVoteBlockNums`. It grows downwards by `istanbulCheckpointInterval` blocks. +- `lowestScannedValidatorVoteNum`: The lowest block number whose vote data and council is calculated and stored. That is, only vote block numbers are greater than or equal to this value are stored in `validatorVoteBlockNums`. It grows downwards by `istanbulCheckpointInterval` blocks. ``` - "lowestCheckpointScannedBlockNum" => Uint64BE(num) + "lowestScannedValidatorVoteNum" => Uint64BE(num) ``` - `istanbulSnapshot`: The legacy schema that periodically (every `istanbulCheckpointInterval` block) commits the council and other fields. Council at an arbitrary block can be recovered by accumulating the validator votes from the nearest istanbul snapshot. ``` @@ -480,7 +480,7 @@ type committeeContext struct { ### Start and stop -This module maintains a background thread that migrates `istanbulCheckpoint` schema into the new `valsetVoteBlockNums` and `council` schemas. +This module maintains a background thread that migrates `istanbulSnapshot` schema into the new `validatorVoteBlockNums` and `council` schemas. The progress is stored in `lowestScannedValidatorVoteNum`. ## Block processing @@ -506,7 +506,7 @@ This module does not expose APIs. ``` GetCouncil(num) -> []common.Address ``` -- `GetValidators(num)`: Returns the demoted validators at block `num`. Note that you can calculate the qualified validators as Council.Subtract(DemotedValidators). +- `GetDemotedValidators(num)`: Returns the demoted validators at block `num`. Note that you can calculate the qualified validators as Council.Subtract(DemotedValidators). ``` GetDemotedValidators(num) -> []common.Address ``` @@ -518,42 +518,3 @@ This module does not expose APIs. ``` GetProposer(num, round) -> common.Address ``` - -### Implementation outline - -``` -# getters -GetCouncil - getCouncilGenesis - getCouncilLegacyDB - getCouncilDB -GetValidators - GetCouncil - getStakingDemoted -GetCommittee - GetValidators - selectRandomCommittee - selectRandaoCommittee -GetProposer - GetValidators - selectRoundRobinProposer - selectStickyProposer - selectWeightedRandomProposer - selectUniformRandomProposer - selectRandaoProposer - -# types -AddressSet - Add - Remove - Has - ToSortedList - -AddressList - At - IndexOf - Swap - Sort - ShuffleLegacy - ShuffleRandao -``` \ No newline at end of file diff --git a/kaiax/valset/impl/getter.go b/kaiax/valset/impl/getter.go index 77412fa88..3e2b960b3 100644 --- a/kaiax/valset/impl/getter.go +++ b/kaiax/valset/impl/getter.go @@ -27,7 +27,7 @@ func (v *ValsetModule) getCouncil(num uint64) (*valset.AddressSet, error) { return v.getCouncilGenesis() } - pBorder := ReadLowestScannedSnapshotNum(v.ChainKv) + pBorder := ReadLowestScannedVoteNum(v.ChainKv) if pBorder == nil || *pBorder > 0 { // migration not started or migration not completed. council, _, err := v.getCouncilFromIstanbulSnapshot(num, false) return council, err @@ -57,14 +57,15 @@ func (v *ValsetModule) getCouncilDB(num uint64) (*valset.AddressSet, error) { if nums == nil { return nil, errEmptyVoteBlock } - voteNum := lastVoteBlockNum(nums, num) + voteNum := lastNumLessThan(nums, num) council := valset.NewAddressSet(ReadCouncil(v.ChainKv, voteNum)) return council, nil } -// lastVoteBlockNum returns the last block number in the list that is less than the given block number. -// For instance, if nums = [0, 10, 20, 30] and num = 25, the result is 20. -func lastVoteBlockNum(nums []uint64, num uint64) uint64 { +// lastNumLessThan returns the last (rightmost) number in the list that is less than the given number. +// If no such number exists, it returns 0. +// Suppose nums = [10, 20, 30]. If num = 25, the result is 20. If num = 7, the result is 0. +func lastNumLessThan(nums []uint64, num uint64) uint64 { // idx is the smallest index that is greater than or equal to `num`. // idx-1 is the largest index that is less than `num`. idx := sort.Search(len(nums), func(i int) bool { diff --git a/kaiax/valset/impl/getter_test.go b/kaiax/valset/impl/getter_test.go index 3d1cf27c5..db3b03f32 100644 --- a/kaiax/valset/impl/getter_test.go +++ b/kaiax/valset/impl/getter_test.go @@ -99,7 +99,7 @@ func TestGetCouncilDB(t *testing.T) { {8, 6, council6}, } for _, tc := range testcases { - voteNum := lastVoteBlockNum(voteNums, tc.num) + voteNum := lastNumLessThan(voteNums, tc.num) assert.Equal(t, tc.voteNum, voteNum, tc.num) v := &ValsetModule{InitOpts: InitOpts{ChainKv: db}} diff --git a/kaiax/valset/impl/init.go b/kaiax/valset/impl/init.go index d0bea7557..8044dc489 100644 --- a/kaiax/valset/impl/init.go +++ b/kaiax/valset/impl/init.go @@ -99,14 +99,14 @@ func (v *ValsetModule) initSchema() error { } // Ensure mandatory schema lowestScannedCheckpointInterval - if pBorder := ReadLowestScannedSnapshotNum(v.ChainKv); pBorder == nil { + if pMinVoteNum := ReadLowestScannedVoteNum(v.ChainKv); pMinVoteNum == nil { // migration not started. Migrating the last interval and leave the rest to be migrated by background thread. currentNum := v.Chain.CurrentBlock().NumberU64() _, snapshotNum, err := v.getCouncilFromIstanbulSnapshot(currentNum, true) if err != nil { return err } - writeLowestScannedSnapshotNum(v.ChainKv, snapshotNum) + writeLowestScannedVoteNum(v.ChainKv, snapshotNum) } return nil @@ -135,24 +135,24 @@ func (v *ValsetModule) Stop() { func (v *ValsetModule) migrate() { defer v.wg.Done() - pBorder := ReadLowestScannedSnapshotNum(v.ChainKv) - if pBorder == nil { + pMinVoteNum := ReadLowestScannedVoteNum(v.ChainKv) + if pMinVoteNum == nil { logger.Error("No lowest scanned snapshot num") return } - border := *pBorder - for border > 0 { + targetNum := *pMinVoteNum + for targetNum > 0 { if v.quit.Load() == 1 { break } - _, snapshotNum, err := v.getCouncilFromIstanbulSnapshot(border, true) + _, snapshotNum, err := v.getCouncilFromIstanbulSnapshot(targetNum, true) if err != nil { - logger.Error("Failed to migrate", "targetNum", border, "err", err) + logger.Error("Failed to migrate", "targetNum", targetNum, "err", err) break } - border = snapshotNum - writeLowestScannedSnapshotNum(v.ChainKv, border) + targetNum = snapshotNum + writeLowestScannedVoteNum(v.ChainKv, targetNum) } } diff --git a/kaiax/valset/impl/init_test.go b/kaiax/valset/impl/init_test.go index 63c7f23a9..27d390169 100644 --- a/kaiax/valset/impl/init_test.go +++ b/kaiax/valset/impl/init_test.go @@ -170,7 +170,7 @@ func TestMigration(t *testing.T) { })) // After initSchema: DB has mandatory schema + last istanbul snapshot interval (2048..2050) assert.Equal(t, []uint64{0, 2049}, ReadValidatorVoteBlockNums(db)) - assert.Equal(t, uint64(2048), *ReadLowestScannedSnapshotNum(db)) + assert.Equal(t, uint64(2048), *ReadLowestScannedVoteNum(db)) assert.Equal(t, numsToAddrs(2, 3, 5), ReadCouncil(db, 0)) assert.Equal(t, numsToAddrs(1, 2, 3, 4, 5), ReadCouncil(db, 2049)) @@ -186,7 +186,7 @@ func TestMigration(t *testing.T) { v.wg.Add(1) v.migrate() assert.Equal(t, []uint64{0, 1024, 1025, 1027, 2049}, ReadValidatorVoteBlockNums(db)) // valid votes - assert.Equal(t, uint64(0), *ReadLowestScannedSnapshotNum(db)) + assert.Equal(t, uint64(0), *ReadLowestScannedVoteNum(db)) assert.Equal(t, numsToAddrs(2, 3, 5), ReadCouncil(db, 0)) // genesis council assert.Equal(t, numsToAddrs(1, 2, 3, 5), ReadCouncil(db, 1024)) // after vote at 1024 (+1) assert.Equal(t, numsToAddrs(1, 3, 5), ReadCouncil(db, 1025)) // after vote at 1025 (-2) diff --git a/kaiax/valset/impl/schema.go b/kaiax/valset/impl/schema.go index 8e6b733a2..b2cf22e4e 100644 --- a/kaiax/valset/impl/schema.go +++ b/kaiax/valset/impl/schema.go @@ -11,10 +11,10 @@ import ( ) var ( - validatorVoteBlockNums = []byte("validatorVoteBlockNums") - lowestScannedSnapshotNumKey = []byte("lowestScannedSnapshotNum") - councilPrefix = []byte("council") - istanbulSnapshotKeyPrefix = []byte("snapshot") + validatorVoteBlockNums = []byte("validatorVoteBlockNums") + lowestScannedValidatorVoteNumKey = []byte("lowestScannedValidatorVoteNum") + councilPrefix = []byte("council") + istanbulSnapshotKeyPrefix = []byte("snapshot") mu = &sync.RWMutex{} ) @@ -114,8 +114,8 @@ func deleteCouncil(db database.Database, num uint64) { } } -func ReadLowestScannedSnapshotNum(db database.Database) *uint64 { - b, err := db.Get(lowestScannedSnapshotNumKey) +func ReadLowestScannedVoteNum(db database.Database) *uint64 { + b, err := db.Get(lowestScannedValidatorVoteNumKey) if err != nil || len(b) == 0 { return nil } @@ -127,10 +127,10 @@ func ReadLowestScannedSnapshotNum(db database.Database) *uint64 { return &ret } -func writeLowestScannedSnapshotNum(db database.Database, num uint64) { +func writeLowestScannedVoteNum(db database.Database, num uint64) { b := make([]byte, 8) binary.BigEndian.PutUint64(b, num) - if err := db.Put(lowestScannedSnapshotNumKey, b); err != nil { + if err := db.Put(lowestScannedValidatorVoteNumKey, b); err != nil { logger.Crit("Failed to write lowest scanned snapshot num", "num", num, "err", err) } } diff --git a/kaiax/valset/impl/schema_test.go b/kaiax/valset/impl/schema_test.go index 3ec866f96..99b1abd26 100644 --- a/kaiax/valset/impl/schema_test.go +++ b/kaiax/valset/impl/schema_test.go @@ -71,14 +71,14 @@ func TestSchema_IstanbulSnapshot(t *testing.T) { ), addrs) } -func TestSchema_LowestScannedSnapshotNum(t *testing.T) { +func TestSchema_LowestScannedVoteNum(t *testing.T) { db := database.NewMemDB() - assert.Nil(t, ReadLowestScannedSnapshotNum(db)) + assert.Nil(t, ReadLowestScannedVoteNum(db)) num := uint64(10) - writeLowestScannedSnapshotNum(db, num) - pNum := ReadLowestScannedSnapshotNum(db) + writeLowestScannedVoteNum(db, num) + pNum := ReadLowestScannedVoteNum(db) require.NotNil(t, pNum) assert.Equal(t, num, *pNum) }