Some checks failed
Build SimApp / build (amd64) (push) Waiting to run
Build SimApp / build (arm64) (push) Waiting to run
CodeQL / Analyze (push) Waiting to run
Build & Push / build (push) Waiting to run
Run Gosec / Gosec (push) Waiting to run
Lint / golangci-lint (push) Waiting to run
Checks dependencies and mocks generation / Check go mod tidy (push) Waiting to run
Checks dependencies and mocks generation / Check up to date mocks (push) Waiting to run
System Tests / setup (push) Waiting to run
System Tests / test-system (push) Blocked by required conditions
System Tests / test-system-legacy (push) Blocked by required conditions
Tests / Code Coverage / split-test-files (push) Waiting to run
Tests / Code Coverage / tests (00) (push) Blocked by required conditions
Tests / Code Coverage / tests (01) (push) Blocked by required conditions
Tests / Code Coverage / tests (02) (push) Blocked by required conditions
Tests / Code Coverage / tests (03) (push) Blocked by required conditions
Tests / Code Coverage / test-integration (push) Waiting to run
Tests / Code Coverage / test-e2e (push) Waiting to run
Tests / Code Coverage / repo-analysis (push) Blocked by required conditions
Tests / Code Coverage / test-sim-nondeterminism (push) Waiting to run
Tests / Code Coverage / test-clientv2 (push) Waiting to run
Tests / Code Coverage / test-core (push) Waiting to run
Tests / Code Coverage / test-depinject (push) Waiting to run
Tests / Code Coverage / test-errors (push) Waiting to run
Tests / Code Coverage / test-math (push) Waiting to run
Tests / Code Coverage / test-schema (push) Waiting to run
Tests / Code Coverage / test-collections (push) Waiting to run
Tests / Code Coverage / test-cosmovisor (push) Waiting to run
Tests / Code Coverage / test-confix (push) Waiting to run
Tests / Code Coverage / test-store (push) Waiting to run
Tests / Code Coverage / test-log (push) Waiting to run
Tests / Code Coverage / test-x-tx (push) Waiting to run
Tests / Code Coverage / test-x-nft (push) Waiting to run
Tests / Code Coverage / test-x-circuit (push) Waiting to run
Tests / Code Coverage / test-x-feegrant (push) Waiting to run
Tests / Code Coverage / test-x-evidence (push) Waiting to run
Tests / Code Coverage / test-x-upgrade (push) Waiting to run
Tests / Code Coverage / test-tools-benchmark (push) Waiting to run
Build & Push SDK Proto Builder / build (push) Has been cancelled
192 lines
9.4 KiB
Go
192 lines
9.4 KiB
Go
package keeper_test
|
|
|
|
import (
|
|
"maps"
|
|
"slices"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/cosmos/cosmos-sdk/x/epochs/types"
|
|
)
|
|
|
|
// This test is responsible for testing how epochs increment based off
|
|
// of their initial conditions, and subsequent block height / times.
|
|
func (suite *KeeperTestSuite) TestEpochInfoBeginBlockChanges() {
|
|
block1Time := time.Unix(1656907200, 0).UTC()
|
|
const (
|
|
defaultIdentifier = "hourly"
|
|
defaultDuration = time.Hour
|
|
// eps is short for epsilon - in this case a negligible amount of time.
|
|
eps = time.Nanosecond
|
|
)
|
|
|
|
tests := map[string]struct {
|
|
// if identifier, duration is not set, we make it defaultIdentifier and defaultDuration.
|
|
// EpochCountingStarted, if unspecified, is inferred by CurrentEpoch == 0
|
|
// StartTime is inferred to be block1Time if left blank.
|
|
initialEpochInfo types.EpochInfo
|
|
blockHeightTimePairs map[int]time.Time
|
|
expEpochInfo types.EpochInfo
|
|
}{
|
|
"First block running at exactly start time sets epoch tick": {
|
|
initialEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 0, CurrentEpochStartTime: time.Time{}},
|
|
expEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 1, CurrentEpochStartTime: block1Time, CurrentEpochStartHeight: 1},
|
|
},
|
|
"First block run sets start time, subsequent blocks within timer interval do not cause timer tick": {
|
|
initialEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 0, CurrentEpochStartTime: time.Time{}},
|
|
blockHeightTimePairs: map[int]time.Time{2: block1Time.Add(time.Second), 3: block1Time.Add(time.Minute), 4: block1Time.Add(30 * time.Minute)},
|
|
expEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 1, CurrentEpochStartTime: block1Time, CurrentEpochStartHeight: 1},
|
|
},
|
|
"Second block at exactly timer interval later does not tick": {
|
|
initialEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 0, CurrentEpochStartTime: time.Time{}},
|
|
blockHeightTimePairs: map[int]time.Time{2: block1Time.Add(defaultDuration)},
|
|
expEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 1, CurrentEpochStartTime: block1Time, CurrentEpochStartHeight: 1},
|
|
},
|
|
"Second block at timer interval + epsilon later does tick": {
|
|
initialEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 0, CurrentEpochStartTime: time.Time{}},
|
|
blockHeightTimePairs: map[int]time.Time{2: block1Time.Add(defaultDuration).Add(eps)},
|
|
expEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 2, CurrentEpochStartTime: block1Time.Add(time.Hour), CurrentEpochStartHeight: 2},
|
|
},
|
|
"Downtime recovery (many intervals), first block causes 1 tick and sets current start time 1 interval ahead": {
|
|
initialEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 0, CurrentEpochStartTime: time.Time{}},
|
|
blockHeightTimePairs: map[int]time.Time{2: block1Time.Add(24 * time.Hour)},
|
|
expEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 2, CurrentEpochStartTime: block1Time.Add(time.Hour), CurrentEpochStartHeight: 2},
|
|
},
|
|
"Downtime recovery (many intervals), second block is at tick 2, w/ start time 2 intervals ahead": {
|
|
initialEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 0, CurrentEpochStartTime: time.Time{}},
|
|
blockHeightTimePairs: map[int]time.Time{2: block1Time.Add(24 * time.Hour), 3: block1Time.Add(24 * time.Hour).Add(eps)},
|
|
expEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 3, CurrentEpochStartTime: block1Time.Add(2 * time.Hour), CurrentEpochStartHeight: 3},
|
|
},
|
|
"Many blocks between first and second tick": {
|
|
initialEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 1, CurrentEpochStartTime: block1Time},
|
|
blockHeightTimePairs: map[int]time.Time{2: block1Time.Add(time.Second), 3: block1Time.Add(2 * time.Second), 4: block1Time.Add(time.Hour).Add(eps)},
|
|
expEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 2, CurrentEpochStartTime: block1Time.Add(time.Hour), CurrentEpochStartHeight: 4},
|
|
},
|
|
"Distinct identifier and duration still works": {
|
|
initialEpochInfo: types.EpochInfo{Identifier: "hello", Duration: time.Minute, StartTime: block1Time, CurrentEpoch: 0, CurrentEpochStartTime: time.Time{}},
|
|
blockHeightTimePairs: map[int]time.Time{2: block1Time.Add(time.Second), 3: block1Time.Add(time.Minute).Add(eps)},
|
|
expEpochInfo: types.EpochInfo{Identifier: "hello", Duration: time.Minute, StartTime: block1Time, CurrentEpoch: 2, CurrentEpochStartTime: block1Time.Add(time.Minute), CurrentEpochStartHeight: 3},
|
|
},
|
|
"StartTime in future won't get ticked on first block": {
|
|
initialEpochInfo: types.EpochInfo{StartTime: block1Time.Add(time.Second), CurrentEpoch: 0, CurrentEpochStartTime: time.Time{}},
|
|
// currentEpochStartHeight is 0 since it hasn't started or been triggered
|
|
expEpochInfo: types.EpochInfo{StartTime: block1Time.Add(time.Second), CurrentEpoch: 0, CurrentEpochStartTime: time.Time{}, CurrentEpochStartHeight: 0},
|
|
},
|
|
"StartTime in past will get ticked on first block": {
|
|
initialEpochInfo: types.EpochInfo{StartTime: block1Time.Add(-time.Second), CurrentEpoch: 0, CurrentEpochStartTime: time.Time{}},
|
|
expEpochInfo: types.EpochInfo{StartTime: block1Time.Add(-time.Second), CurrentEpoch: 1, CurrentEpochStartTime: block1Time.Add(-time.Second), CurrentEpochStartHeight: 1},
|
|
},
|
|
}
|
|
for name, test := range tests {
|
|
suite.Run(name, func() {
|
|
suite.SetupTest()
|
|
suite.Ctx = suite.Ctx.WithBlockHeight(1).WithBlockTime(block1Time)
|
|
initialEpoch := initializeBlankEpochInfoFields(test.initialEpochInfo, defaultIdentifier, defaultDuration)
|
|
err := suite.EpochsKeeper.AddEpochInfo(suite.Ctx, initialEpoch)
|
|
suite.Require().NoError(err)
|
|
err = suite.EpochsKeeper.BeginBlocker(suite.Ctx)
|
|
suite.Require().NoError(err)
|
|
|
|
// get sorted heights
|
|
heights := slices.SortedFunc(maps.Keys(test.blockHeightTimePairs), func(i, j int) int {
|
|
if test.blockHeightTimePairs[i].Before(test.blockHeightTimePairs[j]) {
|
|
return -1
|
|
} else if test.blockHeightTimePairs[i].After(test.blockHeightTimePairs[j]) {
|
|
return 1
|
|
}
|
|
return 0
|
|
})
|
|
for _, h := range heights {
|
|
// for each height in order, run begin block
|
|
suite.Ctx = suite.Ctx.WithBlockHeight(int64(h)).WithBlockTime(test.blockHeightTimePairs[h])
|
|
err := suite.EpochsKeeper.BeginBlocker(suite.Ctx)
|
|
suite.Require().NoError(err)
|
|
}
|
|
expEpoch := initializeBlankEpochInfoFields(test.expEpochInfo, initialEpoch.Identifier, initialEpoch.Duration)
|
|
actEpoch, err := suite.EpochsKeeper.EpochInfo.Get(suite.Ctx, initialEpoch.Identifier)
|
|
suite.Require().NoError(err)
|
|
suite.Require().Equal(expEpoch, actEpoch)
|
|
})
|
|
}
|
|
}
|
|
|
|
// initializeBlankEpochInfoFields set identifier, duration and epochCountingStarted if blank in epoch
|
|
func initializeBlankEpochInfoFields(epoch types.EpochInfo, identifier string, duration time.Duration) types.EpochInfo {
|
|
if epoch.Identifier == "" {
|
|
epoch.Identifier = identifier
|
|
}
|
|
if epoch.Duration == time.Duration(0) {
|
|
epoch.Duration = duration
|
|
}
|
|
epoch.EpochCountingStarted = (epoch.CurrentEpoch != 0)
|
|
return epoch
|
|
}
|
|
|
|
func TestEpochStartingOneMonthAfterInitGenesis(t *testing.T) {
|
|
ctx, epochsKeeper := Setup(t)
|
|
// On init genesis, default epochs information is set
|
|
// To check init genesis again, should make it fresh status
|
|
epochInfos, err := epochsKeeper.AllEpochInfos(ctx)
|
|
require.NoError(t, err)
|
|
for _, epochInfo := range epochInfos {
|
|
err := epochsKeeper.EpochInfo.Remove(ctx, epochInfo.Identifier)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
now := time.Now()
|
|
week := time.Hour * 24 * 7
|
|
month := time.Hour * 24 * 30
|
|
initialBlockHeight := int64(1)
|
|
ctx = ctx.WithBlockHeight(initialBlockHeight).WithBlockTime(now)
|
|
|
|
err = epochsKeeper.InitGenesis(ctx, types.GenesisState{
|
|
Epochs: []types.EpochInfo{
|
|
{
|
|
Identifier: "monthly",
|
|
StartTime: now.Add(month),
|
|
Duration: time.Hour * 24 * 30,
|
|
CurrentEpoch: 0,
|
|
CurrentEpochStartHeight: ctx.BlockHeight(),
|
|
CurrentEpochStartTime: time.Time{},
|
|
EpochCountingStarted: false,
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// epoch not started yet
|
|
epochInfo, err := epochsKeeper.EpochInfo.Get(ctx, "monthly")
|
|
require.NoError(t, err)
|
|
require.Equal(t, epochInfo.CurrentEpoch, int64(0))
|
|
require.Equal(t, epochInfo.CurrentEpochStartHeight, initialBlockHeight)
|
|
require.Equal(t, epochInfo.CurrentEpochStartTime, time.Time{})
|
|
require.Equal(t, epochInfo.EpochCountingStarted, false)
|
|
|
|
// after 1 week
|
|
ctx = ctx.WithBlockHeight(2).WithBlockTime(now.Add(week))
|
|
err = epochsKeeper.BeginBlocker(ctx)
|
|
require.NoError(t, err)
|
|
|
|
// epoch not started yet
|
|
epochInfo, err = epochsKeeper.EpochInfo.Get(ctx, "monthly")
|
|
require.NoError(t, err)
|
|
require.Equal(t, epochInfo.CurrentEpoch, int64(0))
|
|
require.Equal(t, epochInfo.CurrentEpochStartHeight, initialBlockHeight)
|
|
require.Equal(t, epochInfo.CurrentEpochStartTime, time.Time{})
|
|
require.Equal(t, epochInfo.EpochCountingStarted, false)
|
|
|
|
// after 1 month
|
|
ctx = ctx.WithBlockHeight(3).WithBlockTime(now.Add(month))
|
|
err = epochsKeeper.BeginBlocker(ctx)
|
|
require.NoError(t, err)
|
|
|
|
// epoch started
|
|
epochInfo, err = epochsKeeper.EpochInfo.Get(ctx, "monthly")
|
|
require.NoError(t, err)
|
|
require.Equal(t, epochInfo.CurrentEpoch, int64(1))
|
|
require.Equal(t, epochInfo.CurrentEpochStartHeight, ctx.BlockHeight())
|
|
require.Equal(t, epochInfo.CurrentEpochStartTime.UTC().String(), now.Add(month).UTC().String())
|
|
require.Equal(t, epochInfo.EpochCountingStarted, true)
|
|
}
|