Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ENG-214] Add new epoch types: year, hour #559

Closed
wants to merge 11 commits into from
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

### API Breaking

- (epochs) [\#553](https://github.com/tharsis/evmos/pull/553) Store epoch information by duration and by identifier.
- (inflation) [\#536](https://github.com/tharsis/evmos/pull/536) Rename inflation endpoint `/evmos/inflation/v1/total_supply` -> `/evmos/inflation/v1/circulating_supply`
- (erc20) [\#544](https://github.com/tharsis/evmos/pull/544) Remove `updateTokenPairERC20Proposal` functionality rename `relay` to `conversion`
- (upgrade) [\#557](https://github.com/tharsis/evmos/pull/557) Update Evmos go.mod version `v3` -> `v4`
Expand Down
64 changes: 60 additions & 4 deletions x/epochs/keeper/epoch_infos.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package keeper

import (
"fmt"
"time"

"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"

Expand All @@ -9,9 +12,28 @@ import (

// GetEpochInfo returns epoch info by identifier
func (k Keeper) GetEpochInfo(ctx sdk.Context, identifier string) (types.EpochInfo, bool) {
duration, found := k.GetEpochDuration(ctx, identifier)
if !found {
return types.EpochInfo{}, false
}
return k.GetEpoch(ctx, duration)
}

// GetEpochDuration returns epoch duration by identifier
func (k Keeper) GetEpochDuration(ctx sdk.Context, identifier string) ([]byte, bool) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEpochDuration)
bz := store.Get([]byte(identifier))
if len(bz) == 0 {
return make([]byte, 0), false
}
return bz, true
}

// GetEpochInfo returns epoch info by duration
func (k Keeper) GetEpoch(ctx sdk.Context, duration []byte) (types.EpochInfo, bool) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would merge GetEpoch with GetEpochInfo, as right now it's confusing to have both and GetEpoch is only called by GetEpochInfo

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@loredanacirstea what do you think of renaming EpochInfo to Epoch in the proto files? This will make the code less wordy and I don't see the benefit in have Info added.

epoch := types.EpochInfo{}
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEpoch)
bz := store.Get([]byte(identifier))
bz := store.Get(duration)
if len(bz) == 0 {
return epoch, false
}
Expand All @@ -22,18 +44,45 @@ func (k Keeper) GetEpochInfo(ctx sdk.Context, identifier string) (types.EpochInf

// SetEpochInfo set epoch info
func (k Keeper) SetEpochInfo(ctx sdk.Context, epoch types.EpochInfo) {
k.setEpochDuration(ctx, epoch)
k.setEpoch(ctx, epoch)
}

// SetEpochDuration set epoch duration by identifier
func (k Keeper) setEpochDuration(ctx sdk.Context, epoch types.EpochInfo) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEpochDuration)
store.Set([]byte(epoch.Identifier), durationToBz(epoch.Duration))
}

// SetEpochInfo set epoch duration by identifier
func (k Keeper) setEpoch(ctx sdk.Context, epoch types.EpochInfo) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here Epoch and EpochInfo seem mixed

store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEpoch)
bz := k.cdc.MustMarshal(&epoch)
store.Set([]byte(epoch.Identifier), bz)
store.Set(durationToBz(epoch.Duration), bz)
}

// DeleteEpochInfo delete epoch info
func (k Keeper) DeleteEpochInfo(ctx sdk.Context, identifier string) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEpoch)
duration, found := k.GetEpochDuration(ctx, identifier)
if found {
k.deleteEpochDuration(ctx, identifier)
k.deleteEpoch(ctx, duration)
}
}

// DeleteEpochDuration delete epoch duration by identifier
func (k Keeper) deleteEpochDuration(ctx sdk.Context, identifier string) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEpochDuration)
store.Delete([]byte(identifier))
}

// IterateEpochInfo iterate through epochs
// DeleteEpoch delete epoch info
func (k Keeper) deleteEpoch(ctx sdk.Context, duration []byte) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEpoch)
store.Delete(duration)
}

// IterateEpochInfo iterate through epochs in ascending numerical order, by duration
func (k Keeper) IterateEpochInfo(ctx sdk.Context, fn func(index int64, epochInfo types.EpochInfo) (stop bool)) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEpoch)

Expand Down Expand Up @@ -64,3 +113,10 @@ func (k Keeper) AllEpochInfos(ctx sdk.Context) []types.EpochInfo {
})
return epochs
}

// durationToBz parses time duration to maintain number-compatible ordering
func durationToBz(duration time.Duration) []byte {
// 13 digits left padded with zero, allows for 300 year durations
s := fmt.Sprintf("%013d", duration.Milliseconds())
return []byte(s)
}
128 changes: 125 additions & 3 deletions x/epochs/keeper/epoch_infos_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
package keeper_test

import (
"sort"
"time"

"github.com/tharsis/evmos/v4/x/epochs/types"
)

type sortEpochInfos []types.EpochInfo

func (s sortEpochInfos) Len() int { return len(s) }
func (s sortEpochInfos) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s sortEpochInfos) Less(i, j int) bool {
return s[i].Duration < s[j].Duration
}

func (suite *KeeperTestSuite) TestEpochLifeCycle() {
suite.SetupTest()

Expand All @@ -24,7 +35,118 @@ func (suite *KeeperTestSuite) TestEpochLifeCycle() {

allEpochs := suite.app.EpochsKeeper.AllEpochInfos(suite.ctx)
suite.Require().Len(allEpochs, 3)
suite.Require().Equal(allEpochs[0].Identifier, types.DayEpochID) // alphabetical order
suite.Require().Equal(allEpochs[1].Identifier, "monthly")
suite.Require().Equal(allEpochs[2].Identifier, types.WeekEpochID)

// ascending numerical order
suite.Require().Equal(allEpochs[0].Identifier, types.DayEpochID)
suite.Require().Equal(allEpochs[1].Identifier, types.WeekEpochID)
suite.Require().Equal(allEpochs[2].Identifier, "monthly")
}

func (suite *KeeperTestSuite) TestIterateEpochInfo() {
suite.SetupTest()

epochInfos := sortEpochInfos{
{
Identifier: "day",
StartTime: time.Time{},
Duration: time.Hour * 24,
CurrentEpoch: 0,
CurrentEpochStartHeight: 0,
CurrentEpochStartTime: time.Time{},
EpochCountingStarted: false,
},
{
Identifier: "hour",
StartTime: time.Time{},
Duration: time.Hour,
CurrentEpoch: 0,
CurrentEpochStartHeight: 0,
CurrentEpochStartTime: time.Time{},
EpochCountingStarted: false,
},
{
Identifier: "monthly",
StartTime: time.Time{},
Duration: time.Hour * 24 * 30,
CurrentEpoch: 0,
CurrentEpochStartTime: time.Time{},
EpochCountingStarted: false,
},
{
Identifier: "week",
StartTime: time.Time{},
Duration: time.Hour * 24 * 7,
CurrentEpoch: 0,
CurrentEpochStartHeight: 0,
CurrentEpochStartTime: time.Time{},
EpochCountingStarted: false,
},
}

for _, epochInfo := range epochInfos {
suite.app.EpochsKeeper.SetEpochInfo(suite.ctx, epochInfo)
}

sort.Sort(epochInfos)
suite.app.EpochsKeeper.IterateEpochInfo(suite.ctx, func(index int64, epochInfo types.EpochInfo) bool {
expectedEpoch := epochInfos[index]
suite.Require().Equal(expectedEpoch.Identifier, epochInfo.Identifier)
suite.Require().Equal(expectedEpoch.Duration, epochInfo.Duration)
return false
})
}

func (suite *KeeperTestSuite) TestAllEpochInfos() {
suite.SetupTest()

epochInfos := sortEpochInfos{
{
Identifier: "day",
StartTime: time.Time{},
Duration: time.Hour * 24,
CurrentEpoch: 0,
CurrentEpochStartHeight: 0,
CurrentEpochStartTime: time.Time{},
EpochCountingStarted: false,
},
{
Identifier: "hour",
StartTime: time.Time{},
Duration: time.Hour,
CurrentEpoch: 0,
CurrentEpochStartHeight: 0,
CurrentEpochStartTime: time.Time{},
EpochCountingStarted: false,
},
{
Identifier: "monthly",
StartTime: time.Time{},
Duration: time.Hour * 24 * 30,
CurrentEpoch: 0,
CurrentEpochStartTime: time.Time{},
EpochCountingStarted: false,
},
{
Identifier: "week",
StartTime: time.Time{},
Duration: time.Hour * 24 * 7,
CurrentEpoch: 0,
CurrentEpochStartHeight: 0,
CurrentEpochStartTime: time.Time{},
EpochCountingStarted: false,
},
}

for _, epochInfo := range epochInfos {
suite.app.EpochsKeeper.SetEpochInfo(suite.ctx, epochInfo)
}

// sorts epochs by ascending duration
sort.Sort(epochInfos)
storedEpochInfos := suite.app.EpochsKeeper.AllEpochInfos(suite.ctx)
for i, epochInfo := range storedEpochInfos {
expectedEpoch := epochInfos[i]
suite.Require().Equal(expectedEpoch.Identifier, epochInfo.Identifier)
suite.Require().Equal(expectedEpoch.Duration, epochInfo.Duration)
}
}
2 changes: 1 addition & 1 deletion x/epochs/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func (suite *KeeperTestSuite) TestEpochInfo() {

req = &types.QueryEpochsInfoRequest{}
expRes = &types.QueryEpochsInfoResponse{
Epochs: []types.EpochInfo{day, quarter, week},
Epochs: []types.EpochInfo{day, week, quarter},
Pagination: &query.PageResponse{
NextKey: nil,
Total: uint64(3),
Expand Down
27 changes: 27 additions & 0 deletions x/epochs/keeper/migrations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package keeper

import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"

v2 "github.com/tharsis/evmos/v4/x/epochs/migrations/v2"
)

var _ module.MigrationHandler = Migrator{}.Migrate1to2

// Migrator is a struct for handling in-place store migrations.
type Migrator struct {
keeper Keeper
}

// NewMigrator returns a new Migrator.
func NewMigrator(keeper Keeper) Migrator {
return Migrator{
keeper: keeper,
}
}

// Migrate1to2 migrates from consensus version 1 to 2.
func (m Migrator) Migrate1to2(ctx sdk.Context) error {
return v2.MigrateStore(ctx, m.keeper.storeKey, m.keeper.cdc)
}
12 changes: 12 additions & 0 deletions x/epochs/migrations/v2/identifier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package v2

const (
// YearEpochID defines the identifier for yealy epochs
YearEpochID = "year"
// WeekEpochID defines the identifier for weekly epochs
WeekEpochID = "week"
// DayEpochID defines the identifier for daily epochs
DayEpochID = "day"
// HourEpochID defines the identifier for hourly epochs
HourEpochID = "hour"
)
35 changes: 35 additions & 0 deletions x/epochs/migrations/v2/json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package v2

import (
"time"

"github.com/tharsis/evmos/v4/x/epochs/types"
)

var NewEpochs = []types.EpochInfo{
{
Identifier: YearEpochID,
StartTime: time.Time{},
Duration: time.Hour * 24 * 365,
CurrentEpoch: 0,
CurrentEpochStartHeight: 0,
CurrentEpochStartTime: time.Time{},
EpochCountingStarted: false,
},
{
Identifier: HourEpochID,
StartTime: time.Time{},
Duration: time.Hour,
CurrentEpoch: 0,
CurrentEpochStartHeight: 0,
CurrentEpochStartTime: time.Time{},
EpochCountingStarted: false,
},
}

// MigrateJSON accepts exported 1 x/epochs genesis state and migrates it
// to 2 x/epochs genesis state. Hourly and yearly epochs are added.
func MigrateJSON(state types.GenesisState) types.GenesisState {
state.Epochs = append(state.Epochs, NewEpochs[:]...)
return state
}
13 changes: 13 additions & 0 deletions x/epochs/migrations/v2/keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package v2

// prefix bytes for the epochs persistent store
const (
prefixEpoch = iota + 1
prefixEpochDuration
)

// KeyPrefixEpoch defines prefix key for storing epochs
var KeyPrefixEpoch = []byte{prefixEpoch}

// KeyPrefixEpochDuration defines prefix key for storing epochs durations
var KeyPrefixEpochDuration = []byte{prefixEpochDuration}
Loading