diff --git a/CHANGELOG.md b/CHANGELOG.md index 1167976ffb2..ddbaa5bda53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [#7360](https://github.com/osmosis-labs/osmosis/pull/7360) Bump cometbft-db from 0.8.0 to 0.10.0 * [#7385](https://github.com/osmosis-labs/osmosis/pull/7385) Add missing protobuf interface +* [#7427](https://github.com/osmosis-labs/osmosis/pull/7427) Prune TWAP records over multiple blocks, instead of all at once at epoch ## v23.0.0 diff --git a/proto/osmosis/twap/v1beta1/twap_record.proto b/proto/osmosis/twap/v1beta1/twap_record.proto index ee5a0ab0d8f..89d2f527ca9 100644 --- a/proto/osmosis/twap/v1beta1/twap_record.proto +++ b/proto/osmosis/twap/v1beta1/twap_record.proto @@ -74,3 +74,23 @@ message TwapRecord { (gogoproto.moretags) = "yaml:\"last_error_time\"" ]; } + +// PruningState allows us to spread out the pruning of TWAP records over time, +// instead of pruning all at once at the end of the epoch. +message PruningState { + // is_pruning is true if the pruning process is ongoing. + // This tells the module to continue pruning the TWAP records + // at the EndBlock. + bool is_pruning = 1; + // last_kept_time is the time of the last kept TWAP record. + // This is used to determine all TWAP records that are older than + // last_kept_time and should be pruned. + google.protobuf.Timestamp last_kept_time = 2 [ + (gogoproto.nullable) = false, + (gogoproto.stdtime) = true, + (gogoproto.moretags) = "yaml:\"last_kept_time\"" + ]; + // last_key_seen is the last key of the TWAP records that were pruned + // before reaching the block's prune limit + bytes last_key_seen = 3; +} diff --git a/x/twap/export_test.go b/x/twap/export_test.go index e51abb3e26b..f0818072018 100644 --- a/x/twap/export_test.go +++ b/x/twap/export_test.go @@ -40,12 +40,8 @@ func (k Keeper) UpdateRecords(ctx sdk.Context, poolId uint64) error { return k.updateRecords(ctx, poolId) } -func (k Keeper) PruneRecordsBeforeTimeButNewest(ctx sdk.Context, lastKeptTime time.Time) error { - return k.pruneRecordsBeforeTimeButNewest(ctx, lastKeptTime) -} - -func (k Keeper) PruneRecords(ctx sdk.Context) error { - return k.pruneRecords(ctx) +func (k Keeper) PruneRecordsBeforeTimeButNewest(ctx sdk.Context, state types.PruningState) error { + return k.pruneRecordsBeforeTimeButNewest(ctx, state) } func (k Keeper) GetInterpolatedRecord(ctx sdk.Context, poolId uint64, asset0Denom string, asset1Denom string, t time.Time) (types.TwapRecord, error) { diff --git a/x/twap/keeper.go b/x/twap/keeper.go index 6bd5f9b761e..5f7f29f8b3e 100644 --- a/x/twap/keeper.go +++ b/x/twap/keeper.go @@ -5,6 +5,7 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/gogoproto/proto" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" @@ -100,3 +101,31 @@ func (k Keeper) GetGeometricStrategy() *geometric { func (k Keeper) GetArithmeticStrategy() *arithmetic { return &arithmetic{k} } + +// GetPruningState gets the current pruning state, which is used to determine +// whether to prune historical records in the EndBlock. This allows us to spread +// out the computational cost of pruning over time rather than all at once at epoch. +func (k Keeper) GetPruningState(ctx sdk.Context) types.PruningState { + store := ctx.KVStore(k.storeKey) + state := types.PruningState{} + + bz := store.Get(types.PruningStateKey) + if bz == nil { + return state + } + err := proto.Unmarshal(bz, &state) + if err != nil { + panic(err) + } + return state +} + +func (k Keeper) SetPruningState(ctx sdk.Context, state types.PruningState) { + store := ctx.KVStore(k.storeKey) + + bz, err := proto.Marshal(&state) + if err != nil { + panic(err) + } + store.Set(types.PruningStateKey, bz) +} diff --git a/x/twap/listeners.go b/x/twap/listeners.go index 2db483653a3..81efcc7ddf2 100644 --- a/x/twap/listeners.go +++ b/x/twap/listeners.go @@ -6,6 +6,7 @@ import ( "github.com/osmosis-labs/osmosis/osmomath" concentratedliquiditytypes "github.com/osmosis-labs/osmosis/v23/x/concentrated-liquidity/types" gammtypes "github.com/osmosis-labs/osmosis/v23/x/gamm/types" + "github.com/osmosis-labs/osmosis/v23/x/twap/types" epochtypes "github.com/osmosis-labs/osmosis/x/epochs/types" ) @@ -24,9 +25,12 @@ func (k Keeper) EpochHooks() epochtypes.EpochHooks { func (hook *epochhook) AfterEpochEnd(ctx sdk.Context, epochIdentifier string, epochNumber int64) error { if epochIdentifier == hook.k.PruneEpochIdentifier(ctx) { - if err := hook.k.pruneRecords(ctx); err != nil { - ctx.Logger().Error("Error pruning old twaps at the epoch end", err) - } + lastKeptTime := ctx.BlockTime().Add(-hook.k.RecordHistoryKeepPeriod(ctx)) + hook.k.SetPruningState(ctx, types.PruningState{ + IsPruning: true, + LastKeptTime: lastKeptTime, + LastKeySeen: types.FormatHistoricalTimeIndexTWAPKey(lastKeptTime, 0, "", ""), + }) } return nil } diff --git a/x/twap/listeners_test.go b/x/twap/listeners_test.go index 54b2f93020e..3d5c470bb09 100644 --- a/x/twap/listeners_test.go +++ b/x/twap/listeners_test.go @@ -275,20 +275,20 @@ func (s *TestSuite) TestAfterEpochEnd() { err = s.App.TwapKeeper.EpochHooks().AfterEpochEnd(s.Ctx, allEpochs[i].Identifier, int64(1)) s.Require().NoError(err) - recordsAfterEpoch, err := s.twapkeeper.GetAllHistoricalTimeIndexedTWAPs(s.Ctx) + lastKeptTime := s.Ctx.BlockTime().Add(-s.twapkeeper.RecordHistoryKeepPeriod(s.Ctx)) + pruneState := s.twapkeeper.GetPruningState(s.Ctx) - // old record should have been pruned here - // however, the newest younger than the prune threshold - // is kept. + // state entry should be set for pruning state if allEpochs[i].Identifier == pruneEpochIdentifier { - s.Require().Equal(1, len(recordsAfterEpoch)) - s.Require().Equal(newestRecord, recordsAfterEpoch[0]) + s.Require().Equal(true, pruneState.IsPruning) + s.Require().Equal(lastKeptTime, pruneState.LastKeptTime) - // quit test once the record has been pruned - return + // reset pruning state to make sure other epochs do not modify it + s.twapkeeper.SetPruningState(s.Ctx, types.PruningState{}) } else { // pruning should not be triggered at first, not pruning epoch s.Require().NoError(err) - s.Require().Equal(twapsBeforeEpoch, recordsAfterEpoch) + s.Require().Equal(false, pruneState.IsPruning) + s.Require().Equal(time.Time{}, pruneState.LastKeptTime) } } } diff --git a/x/twap/logic.go b/x/twap/logic.go index 3c7f9d3dd73..771feefffd6 100644 --- a/x/twap/logic.go +++ b/x/twap/logic.go @@ -115,6 +115,14 @@ func (k Keeper) EndBlock(ctx sdk.Context) { " Skipping record update. Underlying err: %w", id, err).Error()) } } + + state := k.GetPruningState(ctx) + if state.IsPruning { + err := k.pruneRecordsBeforeTimeButNewest(ctx, state) + if err != nil { + ctx.Logger().Error("Error pruning old twaps at the end block", err) + } + } } // updateRecords updates all records for a given pool id. @@ -195,18 +203,6 @@ func (k Keeper) updateRecord(ctx sdk.Context, record types.TwapRecord) (types.Tw return newRecord, nil } -// pruneRecords prunes twap records that happened earlier than recordHistoryKeepPeriod -// before current block time while preserving the most recent record before the threshold. -// Such record is preserved for each pool. -// See TWAP keeper's `pruneRecordsBeforeTimeButNewest(...)` for more details about the reasons for -// keeping this record. -func (k Keeper) pruneRecords(ctx sdk.Context) error { - recordHistoryKeepPeriod := k.RecordHistoryKeepPeriod(ctx) - - lastKeptTime := ctx.BlockTime().Add(-recordHistoryKeepPeriod) - return k.pruneRecordsBeforeTimeButNewest(ctx, lastKeptTime) -} - // recordWithUpdatedAccumulators returns a record, with updated accumulator values and time for provided newTime, // otherwise referred to as "interpolating the record" to the target time. // This does not mutate the passed in record. diff --git a/x/twap/logic_test.go b/x/twap/logic_test.go index b756fb7d9fa..b00cd6cbd94 100644 --- a/x/twap/logic_test.go +++ b/x/twap/logic_test.go @@ -572,62 +572,6 @@ type computeThreeAssetArithmeticTwapTestCase struct { expErr bool } -// TestPruneRecords tests that twap records earlier than -// current block time - RecordHistoryKeepPeriod are pruned from the store -// while keeping the newest record before the above time threshold. -// Such record is kept for each pool. -func (s *TestSuite) TestPruneRecords() { - recordHistoryKeepPeriod := s.twapkeeper.RecordHistoryKeepPeriod(s.Ctx) - - pool1OlderMin2MsRecord, // deleted - pool2OlderMin1MsRecordAB, pool2OlderMin1MsRecordAC, pool2OlderMin1MsRecordBC, // deleted - pool3OlderBaseRecord, // kept as newest under keep period - pool4OlderPlus1Record := // kept as newest under keep period - s.createTestRecordsFromTime(baseTime.Add(2 * -recordHistoryKeepPeriod)) - - pool1Min2MsRecord, // kept as newest under keep period - pool2Min1MsRecordAB, pool2Min1MsRecordAC, pool2Min1MsRecordBC, // kept as newest under keep period - pool3BaseRecord, // kept as it is at the keep period boundary - pool4Plus1Record := // kept as it is above the keep period boundary - s.createTestRecordsFromTime(baseTime.Add(-recordHistoryKeepPeriod)) - - // non-ascending insertion order. - recordsToPreSet := []types.TwapRecord{ - pool2OlderMin1MsRecordAB, pool2OlderMin1MsRecordAC, pool2OlderMin1MsRecordBC, - pool4Plus1Record, - pool4OlderPlus1Record, - pool3OlderBaseRecord, - pool2Min1MsRecordAB, pool2Min1MsRecordAC, pool2Min1MsRecordBC, - pool3BaseRecord, - pool1Min2MsRecord, - pool1OlderMin2MsRecord, - } - - // tMin2Record is before the threshold and is pruned away. - // tmin1Record is the newest record before current block time - record history keep period. - // All other records happen after the threshold and are kept. - expectedKeptRecords := []types.TwapRecord{ - pool3OlderBaseRecord, - pool4OlderPlus1Record, - pool1Min2MsRecord, - pool2Min1MsRecordAB, pool2Min1MsRecordAC, pool2Min1MsRecordBC, - pool3BaseRecord, - pool4Plus1Record, - } - s.SetupTest() - s.preSetRecords(recordsToPreSet) - - ctx := s.Ctx - twapKeeper := s.twapkeeper - - ctx = ctx.WithBlockTime(baseTime) - - err := twapKeeper.PruneRecords(ctx) - s.Require().NoError(err) - - s.validateExpectedRecords(expectedKeptRecords) -} - // TestUpdateRecords tests that the records are updated correctly. // It tests the following: // - two-asset pools diff --git a/x/twap/store.go b/x/twap/store.go index c6e9fdc85ad..72a3f139d29 100644 --- a/x/twap/store.go +++ b/x/twap/store.go @@ -11,6 +11,13 @@ import ( "github.com/osmosis-labs/osmosis/v23/x/twap/types" ) +// NumRecordsToPrunePerBlock is the number of records to prune per block. +// Two records are deleted per incentive record: +// 1. by time index +// 2. by pool index +// Therefore, setting this to 1000 means 500 complete incentive records are deleted per block. +var NumRecordsToPrunePerBlock uint16 = 1000 + type timeTooOldError struct { Time time.Time } @@ -73,7 +80,14 @@ func (k Keeper) StoreHistoricalTWAP(ctx sdk.Context, twap types.TwapRecord) { // So, in order to have correct behavior for the desired guarantee, // we keep the newest record that is older than the pruning time. // This is why we would keep the -50 hour and -1hour twaps despite a 48hr pruning period -func (k Keeper) pruneRecordsBeforeTimeButNewest(ctx sdk.Context, lastKeptTime time.Time) error { +// +// If we reach the per block pruning limit, we store the last key seen in the pruning state. +// This is so that we can continue pruning from where we left off in the next block. +// If we have pruned all records, we set the pruning state to not pruning. +// There is a small bug here where we store more seenPoolAssetTriplets than we need to. +// Issue added here: https://github.com/osmosis-labs/osmosis/issues/7435 +// The bloat is minimal though, and is not at risk of getting out of hand. +func (k Keeper) pruneRecordsBeforeTimeButNewest(ctx sdk.Context, state types.PruningState) error { store := ctx.KVStore(k.storeKey) // Reverse iterator guarantees that we iterate through the newest per pool first. @@ -81,7 +95,7 @@ func (k Keeper) pruneRecordsBeforeTimeButNewest(ctx sdk.Context, lastKeptTime ti // lastKeptTime exclusively down to the oldest record. iter := store.ReverseIterator( []byte(types.HistoricalTWAPTimeIndexPrefix), - types.FormatHistoricalTimeIndexTWAPKey(lastKeptTime, 0, "", "")) + state.LastKeySeen) defer iter.Close() // We mark what (pool id, asset 0, asset 1) triplets we've seen. @@ -93,6 +107,8 @@ func (k Keeper) pruneRecordsBeforeTimeButNewest(ctx sdk.Context, lastKeptTime ti } seenPoolAssetTriplets := map[uniqueTriplet]struct{}{} + var numPruned uint16 + for ; iter.Valid(); iter.Next() { timeIndexKey := iter.Key() timeS, poolId, asset0, asset1, err := types.ParseFieldsFromHistoricalTimeKey(timeIndexKey) @@ -117,6 +133,24 @@ func (k Keeper) pruneRecordsBeforeTimeButNewest(ctx sdk.Context, lastKeptTime ti store.Delete(timeIndexKey) poolIndexKey := types.FormatHistoricalPoolIndexTWAPKeyFromStrTime(poolId, asset0, asset1, timeS) store.Delete(poolIndexKey) + + // Increment the number of records pruned by 2, since we delete two records per iteration. + numPruned += 2 + + if numPruned >= NumRecordsToPrunePerBlock { + // We have hit the limit, so we stop pruning. + break + } + } + + if !iter.Valid() { + // The iterator is exhausted, so we have pruned all records. + state.IsPruning = false + k.SetPruningState(ctx, state) + } else { + // We have not pruned all records, so we update the last key seen. + state.LastKeySeen = iter.Key() + k.SetPruningState(ctx, state) } return nil } diff --git a/x/twap/store_test.go b/x/twap/store_test.go index c55562db34a..51450b8f2be 100644 --- a/x/twap/store_test.go +++ b/x/twap/store_test.go @@ -359,6 +359,8 @@ func (s *TestSuite) TestPruneRecordsBeforeTimeButNewest() { lastKeptTime time.Time expectedKeptRecords []types.TwapRecord + + overwriteLimit uint16 }{ "base time; across pool 3; 4 records; 3 before lastKeptTime; 2 deleted and newest kept": { recordsToPreSet: []types.TwapRecord{ @@ -507,6 +509,20 @@ func (s *TestSuite) TestPruneRecordsBeforeTimeButNewest() { expectedKeptRecords: []types.TwapRecord{}, }, + "base time; across pool 3; 4 records; 3 before lastKeptTime; only 1 deleted due to limit set to 1": { + recordsToPreSet: []types.TwapRecord{ + pool3BaseSecMin1Ms, // base time - 1ms; kept since newest before lastKeptTime + pool3BaseSecBaseMs, // base time; kept since at lastKeptTime + pool3BaseSecMin3Ms, // base time - 3ms; in queue for deletion + pool3BaseSecMin2Ms, // base time - 2ms; deleted + }, + + lastKeptTime: baseTime, + + expectedKeptRecords: []types.TwapRecord{pool3BaseSecMin3Ms, pool3BaseSecMin1Ms, pool3BaseSecBaseMs}, + + overwriteLimit: 1, + }, } for name, tc := range tests { s.Run(name, func() { @@ -516,7 +532,21 @@ func (s *TestSuite) TestPruneRecordsBeforeTimeButNewest() { ctx := s.Ctx twapKeeper := s.twapkeeper - err := twapKeeper.PruneRecordsBeforeTimeButNewest(ctx, tc.lastKeptTime) + if tc.overwriteLimit != 0 { + originalLimit := twap.NumRecordsToPrunePerBlock + defer func() { + twap.NumRecordsToPrunePerBlock = originalLimit + }() + twap.NumRecordsToPrunePerBlock = tc.overwriteLimit + } + + state := types.PruningState{ + IsPruning: true, + LastKeptTime: tc.lastKeptTime, + LastKeySeen: types.FormatHistoricalTimeIndexTWAPKey(tc.lastKeptTime, 0, "", ""), + } + + err := twapKeeper.PruneRecordsBeforeTimeButNewest(ctx, state) s.Require().NoError(err) s.validateExpectedRecords(tc.expectedKeptRecords) @@ -524,6 +554,160 @@ func (s *TestSuite) TestPruneRecordsBeforeTimeButNewest() { } } +// TestPruneRecordsBeforeTimeButNewestPerBlock tests TWAP record pruning logic over multiple blocks. +func (s *TestSuite) TestPruneRecordsBeforeTimeButNewestPerBlock() { + // N.B.: the records follow the following naming convention: + // + // These are manually created to be able to refer to them by name + // for convenience. + + // Create 6 records of 4 pools from base time, each in different pool with the difference of 1 second between them. Pool 2 is a 3 asset pool. + pool1Min2SBaseMs, pool2Min1SBaseMsAB, pool2Min1SBaseMsAC, pool2Min1SBaseMsBC, pool3BaseSecBaseMs, pool4Plus1SBaseMs := s.createTestRecordsFromTime(baseTime) + + // Create 6 records of 4 pools from base time - 1 ms, each in different pool with the difference of 1 second between them. Pool 2 is a 3 asset pool. + pool1Min2SMin1Ms, pool2Min1SMin1MsAB, pool2Min1SMin1MsAC, pool2Min1SMin1MsBC, pool3BaseSecMin1Ms, pool4Plus1SMin1Ms := s.createTestRecordsFromTime(baseTime.Add(-time.Millisecond)) + + // Create 6 records of 4 pools from base time - 2 ms, each in different pool with the difference of 1 second between them. Pool 2 is a 3 asset pool. + pool1Min2SMin2Ms, pool2Min1SMin2MsAB, pool2Min1SMin2MsAC, pool2Min1SMin2MsBC, pool3BaseSecMin2Ms, pool4Plus1SMin2Ms := s.createTestRecordsFromTime(baseTime.Add(2 * -time.Millisecond)) + + // Create 6 records of 4 pools from base time - 3 ms, each in different pool with the difference of 1 second between them. Pool 2 is a 3 asset pool. + pool1Min2SMin3Ms, pool2Min1SMin3MsAB, pool2Min1SMin3MsAC, pool2Min1SMin3MsBC, pool3BaseSecMin3Ms, pool4Plus1SMin3Ms := s.createTestRecordsFromTime(baseTime.Add(3 * -time.Millisecond)) + + // Create 12 records in the same pool from base time , each record with the difference of 1 second between them. + pool5Min2SBaseMsAB, pool5Min2SBaseMsAC, pool5Min2SBaseMsBC, + pool5Min1SBaseMsAB, pool5Min1SBaseMsAC, pool5Min1SBaseMsBC, + pool5BaseSecBaseMsAB, pool5BaseSecBaseMsAC, pool5BaseSecBaseMsBC, + pool5Plus1SBaseMsAB, pool5Plus1SBaseMsAC, pool5Plus1SBaseMsBC := s.CreateTestRecordsFromTimeInPool(baseTime, 5) + + // Create 12 records in the same pool from base time - 1 ms, each record with the difference of 1 second between them + pool5Min2SMin1MsAB, pool5Min2SMin1MsAC, pool5Min2SMin1MsBC, + pool5Min1SMin1MsAB, pool5Min1SMin1MsAC, pool5Min1SMin1MsBC, + pool5BaseSecMin1MsAB, pool5BaseSecMin1MsAC, pool5BaseSecMin1MsBC, + pool5Plus1SMin1MsAB, pool5Plus1SMin1MsAC, pool5Plus1SMin1MsBC := s.CreateTestRecordsFromTimeInPool(baseTime.Add(-time.Millisecond), 5) + + s.SetupTest() + + recordsToPreSet := []types.TwapRecord{ + pool3BaseSecMin3Ms, // base time - 3ms; kept since older + pool3BaseSecMin2Ms, // base time - 2ms; kept since older + pool3BaseSecMin1Ms, // base time - 1ms; kept since older + pool3BaseSecBaseMs, // base time; kept since older + + pool2Min1SMin3MsAB, pool2Min1SMin3MsAC, pool2Min1SMin3MsBC, // base time - 1s - 3ms; kept since newest before lastKeptTime + pool2Min1SMin2MsAB, pool2Min1SMin2MsAC, pool2Min1SMin2MsBC, // base time - 1s - 2ms; kept since at lastKeptTime + pool2Min1SMin1MsAB, pool2Min1SMin1MsAC, pool2Min1SMin1MsBC, // base time - 1s - 1ms; kept since older + pool2Min1SBaseMsAB, pool2Min1SBaseMsAC, pool2Min1SBaseMsBC, // base time - 1s; kept since older + + pool1Min2SMin3Ms, // base time - 2s - 3ms; will be deleted in block 2 + pool1Min2SMin2Ms, // base time - 2s - 2ms; will be deleted in block 2 + pool1Min2SMin1Ms, // base time - 2s - 1ms; should be deleted, but will be kept due to bug + pool1Min2SBaseMs, // base time - 2s; kept since newest before lastKeptTime + + pool4Plus1SMin3Ms, // base time + 1s - 3ms; kept since older + pool4Plus1SMin2Ms, // base time + 1s - 2ms; kept since older + pool4Plus1SMin1Ms, // base time + 1s -1ms; kept since older + pool4Plus1SBaseMs, // base time + 1s; kept since older + + pool5Min2SBaseMsAB, pool5Min2SBaseMsAC, pool5Min2SBaseMsBC, // base time - 2s; kept since newest before lastKeptTime + pool5Min1SBaseMsAB, pool5Min1SBaseMsAC, pool5Min1SBaseMsBC, // base time - 1s; kept since older + pool5BaseSecBaseMsAB, pool5BaseSecBaseMsAC, pool5BaseSecBaseMsBC, // base time; kept since older + pool5Plus1SBaseMsAB, pool5Plus1SBaseMsAC, pool5Plus1SBaseMsBC, // base time + 1s; kept since older + + pool5Min2SMin1MsAB, pool5Min2SMin1MsAC, pool5Min2SMin1MsBC, // base time - 2s - 1ms; will be deleted in block 1 + pool5Min1SMin1MsAB, pool5Min1SMin1MsAC, pool5Min1SMin1MsBC, // base time - 1s - 1ms; kept since older + pool5BaseSecMin1MsAB, pool5BaseSecMin1MsAC, pool5BaseSecMin1MsBC, // base time - 1ms; kept since older + pool5Plus1SMin1MsAB, pool5Plus1SMin1MsAC, pool5Plus1SMin1MsBC, // base time + 1s - 1ms; kept since older + } + s.preSetRecords(recordsToPreSet) + + twap.NumRecordsToPrunePerBlock = 6 // 3 records max will be pruned per block + lastKeptTime := baseTime.Add(-time.Second).Add(2 * -time.Millisecond) + + state := types.PruningState{ + IsPruning: true, + LastKeptTime: lastKeptTime, + LastKeySeen: types.FormatHistoricalTimeIndexTWAPKey(lastKeptTime, 0, "", ""), + } + + // Block 1 + err := s.twapkeeper.PruneRecordsBeforeTimeButNewest(s.Ctx, state) + s.Require().NoError(err) + + // Pruning state should show pruning is still true, lastKeptTime is the same, and the last key seen is the last key we deleted. + newPruningState := s.twapkeeper.GetPruningState(s.Ctx) + s.Require().Equal(true, newPruningState.IsPruning) + s.Require().Equal(lastKeptTime, newPruningState.LastKeptTime) + timeS, poolId, asset0, asset1, err := types.ParseFieldsFromHistoricalTimeKey(newPruningState.LastKeySeen) + s.Require().NoError(err) + + // The last key seen is the third record we delete, since we prune 3 records per block. + s.Require().Equal(pool5Min2SMin1MsAB.Time.Format("2006-01-02T15:04:05.000000000"), timeS) + s.Require().Equal(pool5Min2SMin1MsAB.PoolId, poolId) + s.Require().Equal(pool5Min2SMin1MsAB.Asset0Denom, asset0) + s.Require().Equal(pool5Min2SMin1MsAB.Asset1Denom, asset1) + + expectedKeptRecords := []types.TwapRecord{ + pool1Min2SMin3Ms, // base time - 2s - 3ms; in queue to be deleted + pool1Min2SMin2Ms, // base time - 2s - 2ms; in queue to be deleted + pool1Min2SMin1Ms, // base time - 2s - 1ms; in queue to be deleted + pool1Min2SBaseMs, // base time - 2s; kept since newest before lastKeptTime + pool5Min2SBaseMsAB, pool5Min2SBaseMsAC, pool5Min2SBaseMsBC, // base time - 2s; kept since newest before lastKeptTime + pool2Min1SMin3MsAB, pool2Min1SMin3MsAC, pool2Min1SMin3MsBC, // base time - 1s - 3ms; kept since newest before lastKeptTime + pool2Min1SMin2MsAB, pool2Min1SMin2MsAC, pool2Min1SMin2MsBC, // base time - 1s - 2ms; kept since at lastKeptTime + pool2Min1SMin1MsAB, pool2Min1SMin1MsAC, pool2Min1SMin1MsBC, // base time - 1s - 1ms; kept since older + pool5Min1SMin1MsAB, pool5Min1SMin1MsAC, pool5Min1SMin1MsBC, // base time - 1s - 1ms; kept since older + pool2Min1SBaseMsAB, pool2Min1SBaseMsAC, pool2Min1SBaseMsBC, // base time - 1s; kept since older + pool5Min1SBaseMsAB, pool5Min1SBaseMsAC, pool5Min1SBaseMsBC, // base time - 1s; kept since older + pool3BaseSecMin3Ms, // base time - 3ms; kept since older + pool3BaseSecMin2Ms, // base time - 2ms; kept since older + pool3BaseSecMin1Ms, // base time - 1ms; kept since older + pool5BaseSecMin1MsAB, pool5BaseSecMin1MsAC, pool5BaseSecMin1MsBC, // base time - 1ms; kept since older + pool3BaseSecBaseMs, // base time; kept since older + pool5BaseSecBaseMsAB, pool5BaseSecBaseMsAC, pool5BaseSecBaseMsBC, // base time; kept since older + pool4Plus1SMin3Ms, // base time + 1s - 3ms; kept since older + pool4Plus1SMin2Ms, // base time + 1s - 2ms; kept since older + pool4Plus1SMin1Ms, // base time + 1s -1ms; kept since older + pool5Plus1SMin1MsAB, pool5Plus1SMin1MsAC, pool5Plus1SMin1MsBC, // base time + 1s - 1ms; kept since older + pool4Plus1SBaseMs, // base time + 1s; kept since older + pool5Plus1SBaseMsAB, pool5Plus1SBaseMsAC, pool5Plus1SBaseMsBC, // base time + 1s; kept since older + } + s.validateExpectedRecords(expectedKeptRecords) + + // Block 2 + err = s.twapkeeper.PruneRecordsBeforeTimeButNewest(s.Ctx, newPruningState) + s.Require().NoError(err) + + // Pruning state should show now show pruning is false + newPruningState = s.twapkeeper.GetPruningState(s.Ctx) + s.Require().Equal(false, newPruningState.IsPruning) + + expectedKeptRecords = []types.TwapRecord{ + pool1Min2SMin1Ms, // mistakenly kept, is fine though + pool1Min2SBaseMs, // base time - 2s; kept since newest before lastKeptTime + pool5Min2SBaseMsAB, pool5Min2SBaseMsAC, pool5Min2SBaseMsBC, // base time - 2s; kept since newest before lastKeptTime + pool2Min1SMin3MsAB, pool2Min1SMin3MsAC, pool2Min1SMin3MsBC, // base time - 1s - 3ms; kept since newest before lastKeptTime + pool2Min1SMin2MsAB, pool2Min1SMin2MsAC, pool2Min1SMin2MsBC, // base time - 1s - 2ms; kept since at lastKeptTime + pool2Min1SMin1MsAB, pool2Min1SMin1MsAC, pool2Min1SMin1MsBC, // base time - 1s - 1ms; kept since older + pool5Min1SMin1MsAB, pool5Min1SMin1MsAC, pool5Min1SMin1MsBC, // base time - 1s - 1ms; kept since older + pool2Min1SBaseMsAB, pool2Min1SBaseMsAC, pool2Min1SBaseMsBC, // base time - 1s; kept since older + pool5Min1SBaseMsAB, pool5Min1SBaseMsAC, pool5Min1SBaseMsBC, // base time - 1s; kept since older + pool3BaseSecMin3Ms, // base time - 3ms; kept since older + pool3BaseSecMin2Ms, // base time - 2ms; kept since older + pool3BaseSecMin1Ms, // base time - 1ms; kept since older + pool5BaseSecMin1MsAB, pool5BaseSecMin1MsAC, pool5BaseSecMin1MsBC, // base time - 1ms; kept since older + pool3BaseSecBaseMs, // base time; kept since older + pool5BaseSecBaseMsAB, pool5BaseSecBaseMsAC, pool5BaseSecBaseMsBC, // base time; kept since older + pool4Plus1SMin3Ms, // base time + 1s - 3ms; kept since older + pool4Plus1SMin2Ms, // base time + 1s - 2ms; kept since older + pool4Plus1SMin1Ms, // base time + 1s -1ms; kept since older + pool5Plus1SMin1MsAB, pool5Plus1SMin1MsAC, pool5Plus1SMin1MsBC, // base time + 1s - 1ms; kept since older + pool4Plus1SBaseMs, // base time + 1s; kept since older + pool5Plus1SBaseMsAB, pool5Plus1SBaseMsAC, pool5Plus1SBaseMsBC, // base time + 1s; kept since older + } + + s.validateExpectedRecords(expectedKeptRecords) +} + func (s *TestSuite) TestGetAllHistoricalTimeIndexedTWAPs() { tests := map[string]struct { expectedRecords []types.TwapRecord diff --git a/x/twap/types/keys.go b/x/twap/types/keys.go index fe69bcba2b3..5a3d00af499 100644 --- a/x/twap/types/keys.go +++ b/x/twap/types/keys.go @@ -27,6 +27,7 @@ const ( ) var ( + PruningStateKey = []byte{0x01} mostRecentTWAPsNoSeparator = "recent_twap" historicalTWAPTimeIndexNoSeparator = "historical_time_index" historicalTWAPPoolIndexNoSeparator = "historical_pool_index" diff --git a/x/twap/types/twap_record.pb.go b/x/twap/types/twap_record.pb.go index 31054df23f2..b3f4b9499d5 100644 --- a/x/twap/types/twap_record.pb.go +++ b/x/twap/types/twap_record.pb.go @@ -137,8 +137,79 @@ func (m *TwapRecord) GetLastErrorTime() time.Time { return time.Time{} } +// PruningState allows us to spread out the pruning of TWAP records over time, +// instead of pruning all at once at the end of the epoch. +type PruningState struct { + // is_pruning is true if the pruning process is ongoing. + // This tells the module to continue pruning the TWAP records + // at the EndBlock. + IsPruning bool `protobuf:"varint,1,opt,name=is_pruning,json=isPruning,proto3" json:"is_pruning,omitempty"` + // last_kept_time is the time of the last kept TWAP record. + // This is used to determine all TWAP records that are older than + // last_kept_time and should be pruned. + LastKeptTime time.Time `protobuf:"bytes,2,opt,name=last_kept_time,json=lastKeptTime,proto3,stdtime" json:"last_kept_time" yaml:"last_kept_time"` + // last_key_seen is the last key of the TWAP records that were pruned + // before reaching the block's prune limit + LastKeySeen []byte `protobuf:"bytes,3,opt,name=last_key_seen,json=lastKeySeen,proto3" json:"last_key_seen,omitempty"` +} + +func (m *PruningState) Reset() { *m = PruningState{} } +func (m *PruningState) String() string { return proto.CompactTextString(m) } +func (*PruningState) ProtoMessage() {} +func (*PruningState) Descriptor() ([]byte, []int) { + return fileDescriptor_dbf5c78678e601aa, []int{1} +} +func (m *PruningState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PruningState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PruningState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PruningState) XXX_Merge(src proto.Message) { + xxx_messageInfo_PruningState.Merge(m, src) +} +func (m *PruningState) XXX_Size() int { + return m.Size() +} +func (m *PruningState) XXX_DiscardUnknown() { + xxx_messageInfo_PruningState.DiscardUnknown(m) +} + +var xxx_messageInfo_PruningState proto.InternalMessageInfo + +func (m *PruningState) GetIsPruning() bool { + if m != nil { + return m.IsPruning + } + return false +} + +func (m *PruningState) GetLastKeptTime() time.Time { + if m != nil { + return m.LastKeptTime + } + return time.Time{} +} + +func (m *PruningState) GetLastKeySeen() []byte { + if m != nil { + return m.LastKeySeen + } + return nil +} + func init() { proto.RegisterType((*TwapRecord)(nil), "osmosis.twap.v1beta1.TwapRecord") + proto.RegisterType((*PruningState)(nil), "osmosis.twap.v1beta1.PruningState") } func init() { @@ -146,42 +217,46 @@ func init() { } var fileDescriptor_dbf5c78678e601aa = []byte{ - // 545 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x94, 0x4d, 0x6f, 0xd3, 0x4c, - 0x10, 0xc7, 0xe3, 0xa7, 0x79, 0x52, 0xba, 0xa1, 0xaa, 0x64, 0x45, 0x60, 0x52, 0xc9, 0x0e, 0x41, - 0x42, 0xe1, 0x80, 0x5f, 0xda, 0x1b, 0xb7, 0x44, 0xe5, 0x00, 0xaa, 0x50, 0x65, 0x7a, 0xe2, 0x62, - 0xad, 0x9d, 0xad, 0x6d, 0x61, 0x67, 0x56, 0xbb, 0x9b, 0x96, 0x7c, 0x8b, 0x7e, 0x25, 0x6e, 0x3d, - 0xf6, 0x88, 0x38, 0x18, 0x94, 0xdc, 0x38, 0xf6, 0x13, 0xa0, 0xdd, 0x75, 0x02, 0x09, 0x6f, 0xed, - 0xcd, 0x33, 0xf3, 0x9f, 0xdf, 0x7f, 0xc7, 0x3b, 0x5a, 0xf4, 0x14, 0x78, 0x09, 0x3c, 0xe7, 0x9e, - 0xb8, 0xc0, 0xd4, 0x3b, 0x0f, 0x62, 0x22, 0x70, 0xa0, 0x82, 0x88, 0x91, 0x04, 0xd8, 0xd8, 0xa5, - 0x0c, 0x04, 0x98, 0x9d, 0x5a, 0xe7, 0xca, 0x92, 0x5b, 0xeb, 0xba, 0x9d, 0x14, 0x52, 0x50, 0x02, - 0x4f, 0x7e, 0x69, 0x6d, 0xf7, 0x51, 0x0a, 0x90, 0x16, 0xc4, 0x53, 0x51, 0x3c, 0x3d, 0xf3, 0xf0, - 0x64, 0xb6, 0x2c, 0x25, 0x8a, 0x13, 0xe9, 0x1e, 0x1d, 0xd4, 0x25, 0x5b, 0x47, 0x5e, 0x8c, 0x39, - 0x59, 0x1d, 0x24, 0x81, 0x7c, 0x52, 0xd7, 0x9d, 0x4d, 0xaa, 0xc8, 0x4b, 0xc2, 0x05, 0x2e, 0xa9, - 0x16, 0xf4, 0x3f, 0xb6, 0x10, 0x3a, 0xbd, 0xc0, 0x34, 0x54, 0xe7, 0x36, 0x1f, 0xa2, 0x6d, 0x0a, - 0x50, 0x44, 0xf9, 0xd8, 0x32, 0x7a, 0xc6, 0xa0, 0x19, 0xb6, 0x64, 0xf8, 0x6a, 0x6c, 0x3e, 0x46, - 0xf7, 0x31, 0xe7, 0x44, 0xf8, 0xd1, 0x98, 0x4c, 0xa0, 0xb4, 0xfe, 0xeb, 0x19, 0x83, 0x9d, 0xb0, - 0xad, 0x73, 0x47, 0x32, 0xb5, 0x92, 0x04, 0xb5, 0x64, 0xeb, 0x27, 0x49, 0xa0, 0x25, 0x43, 0xd4, - 0xca, 0x48, 0x9e, 0x66, 0xc2, 0x6a, 0xf6, 0x8c, 0xc1, 0xd6, 0xe8, 0xd9, 0xb7, 0xca, 0xd9, 0xd5, - 0xbf, 0x2c, 0xd2, 0x85, 0x9b, 0xca, 0xe9, 0xcc, 0x70, 0x59, 0xbc, 0xe8, 0xaf, 0xa5, 0xfb, 0x61, - 0xdd, 0x68, 0xbe, 0x41, 0x4d, 0x39, 0x83, 0xf5, 0x7f, 0xcf, 0x18, 0xb4, 0x0f, 0xba, 0xae, 0x1e, - 0xd0, 0x5d, 0x0e, 0xe8, 0x9e, 0x2e, 0x07, 0x1c, 0xd9, 0x57, 0x95, 0xd3, 0xb8, 0xa9, 0x1c, 0x73, - 0x8d, 0x27, 0x9b, 0xfb, 0x97, 0x5f, 0x1c, 0x23, 0x54, 0x1c, 0xf3, 0x04, 0x99, 0xd4, 0x8f, 0x0a, - 0xcc, 0x45, 0xc4, 0x29, 0x88, 0x88, 0xb2, 0x3c, 0x21, 0x56, 0x4b, 0x9e, 0x7d, 0xf4, 0x44, 0x12, - 0x3e, 0x57, 0xce, 0xbe, 0xfe, 0xcb, 0x7c, 0xfc, 0xde, 0xcd, 0xc1, 0x2b, 0xb1, 0xc8, 0xdc, 0x63, - 0x92, 0xe2, 0x64, 0x76, 0x44, 0x92, 0x70, 0x8f, 0xfa, 0xc7, 0x98, 0x8b, 0xb7, 0x14, 0xc4, 0x89, - 0xec, 0x55, 0xc4, 0xe0, 0x17, 0xe2, 0xf6, 0x5d, 0x88, 0xc1, 0x3a, 0x31, 0x43, 0x36, 0xf5, 0x23, - 0xcc, 0x72, 0x91, 0x95, 0x44, 0xe4, 0x49, 0xa4, 0x56, 0x0d, 0x27, 0xc9, 0xb4, 0x9c, 0x16, 0x58, - 0x00, 0xb3, 0xee, 0xdd, 0x9e, 0xbe, 0x4f, 0xfd, 0xe1, 0x8a, 0x24, 0xaf, 0x7e, 0xf8, 0x83, 0xa3, - 0x9c, 0x82, 0xbf, 0x3a, 0xed, 0xdc, 0xc5, 0x29, 0xf8, 0xb3, 0x13, 0x46, 0xdd, 0x94, 0x40, 0x49, - 0x04, 0xfb, 0x9d, 0x0b, 0xba, 0xbd, 0x8b, 0xb5, 0xc2, 0x6c, 0x5a, 0x9c, 0xa1, 0x3d, 0x75, 0x0b, - 0x84, 0x31, 0x60, 0xea, 0xe2, 0xad, 0xf6, 0x3f, 0xb7, 0xa6, 0x5f, 0x6f, 0xcd, 0x03, 0xbd, 0x35, - 0x1b, 0x00, 0xbd, 0x39, 0xbb, 0x32, 0xfb, 0x52, 0x26, 0x65, 0xdf, 0xe8, 0xf5, 0xd5, 0xdc, 0x36, - 0xae, 0xe7, 0xb6, 0xf1, 0x75, 0x6e, 0x1b, 0x97, 0x0b, 0xbb, 0x71, 0xbd, 0xb0, 0x1b, 0x9f, 0x16, - 0x76, 0xe3, 0x9d, 0x9f, 0xe6, 0x22, 0x9b, 0xc6, 0x6e, 0x02, 0xa5, 0x57, 0xbf, 0x05, 0xcf, 0x0b, - 0x1c, 0xf3, 0x65, 0xe0, 0x9d, 0x1f, 0x1c, 0x7a, 0x1f, 0xf4, 0x33, 0x22, 0x66, 0x94, 0xf0, 0xb8, - 0xa5, 0x8e, 0x74, 0xf8, 0x3d, 0x00, 0x00, 0xff, 0xff, 0x43, 0x12, 0x21, 0x98, 0x63, 0x04, 0x00, - 0x00, + // 624 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0x4d, 0x4f, 0xdb, 0x40, + 0x10, 0x8d, 0x81, 0x06, 0xd8, 0x84, 0x22, 0x59, 0xb4, 0x75, 0x83, 0x6a, 0x07, 0x57, 0xaa, 0xd2, + 0x43, 0xfd, 0x01, 0xb7, 0xde, 0x88, 0xe8, 0xa1, 0x2d, 0xaa, 0x90, 0xe1, 0xd4, 0x8b, 0xb5, 0x76, + 0x16, 0x7b, 0x85, 0xed, 0x5d, 0x79, 0x37, 0x50, 0xff, 0x0b, 0x7e, 0x4d, 0xef, 0xbd, 0x71, 0xe4, + 0x58, 0xf5, 0x90, 0x56, 0xe4, 0xd6, 0x23, 0xbf, 0xa0, 0xda, 0x5d, 0x27, 0x25, 0xe9, 0x07, 0x70, + 0xf3, 0xcc, 0xbc, 0x79, 0xef, 0x8d, 0x77, 0x76, 0xc1, 0x0b, 0xc2, 0x72, 0xc2, 0x30, 0x73, 0xf9, + 0x19, 0xa4, 0xee, 0xa9, 0x1f, 0x21, 0x0e, 0x7d, 0x19, 0x84, 0x25, 0x8a, 0x49, 0x39, 0x70, 0x68, + 0x49, 0x38, 0xd1, 0x37, 0x6a, 0x9c, 0x23, 0x4a, 0x4e, 0x8d, 0xeb, 0x6c, 0x24, 0x24, 0x21, 0x12, + 0xe0, 0x8a, 0x2f, 0x85, 0xed, 0x3c, 0x4d, 0x08, 0x49, 0x32, 0xe4, 0xca, 0x28, 0x1a, 0x1e, 0xbb, + 0xb0, 0xa8, 0x26, 0xa5, 0x58, 0xf2, 0x84, 0xaa, 0x47, 0x05, 0x75, 0xc9, 0x54, 0x91, 0x1b, 0x41, + 0x86, 0xa6, 0x46, 0x62, 0x82, 0x8b, 0xba, 0x6e, 0xcd, 0xb3, 0x72, 0x9c, 0x23, 0xc6, 0x61, 0x4e, + 0x15, 0xc0, 0xfe, 0xd2, 0x04, 0xe0, 0xe8, 0x0c, 0xd2, 0x40, 0xfa, 0xd6, 0x9f, 0x80, 0x65, 0x4a, + 0x48, 0x16, 0xe2, 0x81, 0xa1, 0x75, 0xb5, 0xde, 0x52, 0xd0, 0x14, 0xe1, 0xdb, 0x81, 0xbe, 0x05, + 0xda, 0x90, 0x31, 0xc4, 0xbd, 0x70, 0x80, 0x0a, 0x92, 0x1b, 0x0b, 0x5d, 0xad, 0xb7, 0x1a, 0xb4, + 0x54, 0x6e, 0x4f, 0xa4, 0xa6, 0x10, 0xbf, 0x86, 0x2c, 0xde, 0x80, 0xf8, 0x0a, 0xb2, 0x0b, 0x9a, + 0x29, 0xc2, 0x49, 0xca, 0x8d, 0xa5, 0xae, 0xd6, 0x5b, 0xec, 0xbf, 0xfc, 0x39, 0xb2, 0xd6, 0xd4, + 0x2f, 0x0b, 0x55, 0xe1, 0x7a, 0x64, 0x6d, 0x54, 0x30, 0xcf, 0x5e, 0xdb, 0x33, 0x69, 0x3b, 0xa8, + 0x1b, 0xf5, 0x0f, 0x60, 0x49, 0xcc, 0x60, 0x3c, 0xe8, 0x6a, 0xbd, 0xd6, 0x76, 0xc7, 0x51, 0x03, + 0x3a, 0x93, 0x01, 0x9d, 0xa3, 0xc9, 0x80, 0x7d, 0xf3, 0x62, 0x64, 0x35, 0xae, 0x47, 0x96, 0x3e, + 0xc3, 0x27, 0x9a, 0xed, 0xf3, 0xef, 0x96, 0x16, 0x48, 0x1e, 0xfd, 0x00, 0xe8, 0xd4, 0x0b, 0x33, + 0xc8, 0x78, 0xc8, 0x28, 0xe1, 0x21, 0x2d, 0x71, 0x8c, 0x8c, 0xa6, 0xf0, 0xde, 0x7f, 0x2e, 0x18, + 0xbe, 0x8d, 0xac, 0x4d, 0xf5, 0x97, 0xd9, 0xe0, 0xc4, 0xc1, 0xc4, 0xcd, 0x21, 0x4f, 0x9d, 0x7d, + 0x94, 0xc0, 0xb8, 0xda, 0x43, 0x71, 0xb0, 0x4e, 0xbd, 0x7d, 0xc8, 0xf8, 0x21, 0x25, 0xfc, 0x40, + 0xf4, 0x4a, 0x46, 0xff, 0x0f, 0xc6, 0xe5, 0xfb, 0x30, 0xfa, 0xb3, 0x8c, 0x29, 0x30, 0xa9, 0x17, + 0xc2, 0x12, 0xf3, 0x34, 0x47, 0x1c, 0xc7, 0xa1, 0x5c, 0x35, 0x18, 0xc7, 0xc3, 0x7c, 0x98, 0x41, + 0x4e, 0x4a, 0x63, 0xe5, 0xee, 0xec, 0x9b, 0xd4, 0xdb, 0x9d, 0x32, 0x89, 0xa3, 0xdf, 0xfd, 0xcd, + 0x23, 0x95, 0xfc, 0xff, 0x2a, 0xad, 0xde, 0x47, 0xc9, 0xff, 0xb7, 0x12, 0x04, 0x9d, 0x04, 0x91, + 0x1c, 0xf1, 0xf2, 0x6f, 0x2a, 0xe0, 0xee, 0x2a, 0xc6, 0x94, 0x66, 0x5e, 0xe2, 0x18, 0xac, 0xcb, + 0x53, 0x40, 0x65, 0x49, 0x4a, 0x79, 0xf0, 0x46, 0xeb, 0xd6, 0xad, 0xb1, 0xeb, 0xad, 0x79, 0xac, + 0xb6, 0x66, 0x8e, 0x40, 0x6d, 0xce, 0x9a, 0xc8, 0xbe, 0x11, 0x49, 0xd1, 0x67, 0x7f, 0xd6, 0x40, + 0xfb, 0xa0, 0x1c, 0x16, 0xb8, 0x48, 0x0e, 0x39, 0xe4, 0x48, 0x7f, 0x06, 0x00, 0x16, 0xd7, 0x55, + 0xa6, 0xe4, 0x45, 0x5a, 0x09, 0x56, 0x31, 0xab, 0x31, 0x7a, 0x0c, 0x1e, 0x4a, 0xda, 0x13, 0x44, + 0xb9, 0xb2, 0xb5, 0x70, 0xab, 0xad, 0xad, 0xda, 0xd6, 0xa3, 0x1b, 0xb6, 0xa6, 0xfd, 0xca, 0x55, + 0x5b, 0x24, 0xdf, 0x23, 0xca, 0x45, 0x97, 0x6e, 0x83, 0xb5, 0x1a, 0x54, 0x85, 0x0c, 0xa1, 0x42, + 0x5e, 0xc7, 0x76, 0xd0, 0x52, 0xa0, 0xea, 0x10, 0xa1, 0xa2, 0xff, 0xee, 0xe2, 0xca, 0xd4, 0x2e, + 0xaf, 0x4c, 0xed, 0xc7, 0x95, 0xa9, 0x9d, 0x8f, 0xcd, 0xc6, 0xe5, 0xd8, 0x6c, 0x7c, 0x1d, 0x9b, + 0x8d, 0x8f, 0x5e, 0x82, 0x79, 0x3a, 0x8c, 0x9c, 0x98, 0xe4, 0x6e, 0xfd, 0x88, 0xbd, 0xca, 0x60, + 0xc4, 0x26, 0x81, 0x7b, 0xba, 0xbd, 0xe3, 0x7e, 0x52, 0xef, 0x1f, 0xaf, 0x28, 0x62, 0x51, 0x53, + 0x9a, 0xde, 0xf9, 0x15, 0x00, 0x00, 0xff, 0xff, 0x59, 0x03, 0x4c, 0x93, 0x1c, 0x05, 0x00, 0x00, } func (m *TwapRecord) Marshal() (dAtA []byte, err error) { @@ -297,6 +372,54 @@ func (m *TwapRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *PruningState) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PruningState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PruningState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.LastKeySeen) > 0 { + i -= len(m.LastKeySeen) + copy(dAtA[i:], m.LastKeySeen) + i = encodeVarintTwapRecord(dAtA, i, uint64(len(m.LastKeySeen))) + i-- + dAtA[i] = 0x1a + } + n3, err3 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.LastKeptTime, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.LastKeptTime):]) + if err3 != nil { + return 0, err3 + } + i -= n3 + i = encodeVarintTwapRecord(dAtA, i, uint64(n3)) + i-- + dAtA[i] = 0x12 + if m.IsPruning { + i-- + if m.IsPruning { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func encodeVarintTwapRecord(dAtA []byte, offset int, v uint64) int { offset -= sovTwapRecord(v) base := offset @@ -345,6 +468,24 @@ func (m *TwapRecord) Size() (n int) { return n } +func (m *PruningState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.IsPruning { + n += 2 + } + l = github_com_cosmos_gogoproto_types.SizeOfStdTime(m.LastKeptTime) + n += 1 + l + sovTwapRecord(uint64(l)) + l = len(m.LastKeySeen) + if l > 0 { + n += 1 + l + sovTwapRecord(uint64(l)) + } + return n +} + func sovTwapRecord(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -739,6 +880,143 @@ func (m *TwapRecord) Unmarshal(dAtA []byte) error { } return nil } +func (m *PruningState) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTwapRecord + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PruningState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PruningState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsPruning", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTwapRecord + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IsPruning = bool(v != 0) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LastKeptTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTwapRecord + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTwapRecord + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTwapRecord + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_cosmos_gogoproto_types.StdTimeUnmarshal(&m.LastKeptTime, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LastKeySeen", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTwapRecord + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTwapRecord + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTwapRecord + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LastKeySeen = append(m.LastKeySeen[:0], dAtA[iNdEx:postIndex]...) + if m.LastKeySeen == nil { + m.LastKeySeen = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTwapRecord(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTwapRecord + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTwapRecord(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0