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

Update record sanity check #2741

Merged
merged 27 commits into from
May 6, 2023
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
29539a1
add new error type
hieuvubk Sep 14, 2022
7b87cd8
add condition to ensure ctx time > record time
hieuvubk Sep 14, 2022
0cbfc04
format
hieuvubk Sep 14, 2022
b09e65f
replace
hieuvubk Sep 14, 2022
dfc2307
fix unused import
hieuvubk Sep 14, 2022
cd78749
update co conditions, bypass the case when creating pool
hieuvubk Sep 23, 2022
5e9e64b
add cmt
hieuvubk Sep 23, 2022
a7b94af
resolved conflict
hieuvubk Sep 23, 2022
17618aa
Merge branch 'main' into update_record_sanity_check
hieuvubk Sep 26, 2022
5c3e98a
update test for checking block height
hieuvubk Sep 26, 2022
f7fcdc3
format
hieuvubk Sep 26, 2022
c43885d
Merge branch 'main' into update_record_sanity_check
hieuvubk Sep 27, 2022
acf03e3
Merge branch 'main' into update_record_sanity_check
hieuvubk Sep 27, 2022
0409367
update err message
hieuvubk Sep 27, 2022
77502d9
Merge branch 'main' into update_record_sanity_check
alexanderbez Sep 27, 2022
fa28336
Merge branch 'main' into update_record_sanity_check
mattverse Sep 28, 2022
95713c2
Merge branch 'main' into update_record_sanity_check
hieuvubk Oct 2, 2022
24da39c
update ctx & err
hieuvubk Oct 11, 2022
ad6bd30
Merge branch 'update_record_sanity_check' of https://github.com/osmos…
hieuvubk Oct 11, 2022
71991e9
rebase
hieuvubk Apr 6, 2023
bc79f48
resolve conflict
hieuvubk Apr 6, 2023
c6f1b8f
split condition
hieuvubk Apr 7, 2023
277d7a7
update condition & add more test
hieuvubk Apr 7, 2023
2f5103d
lint
hieuvubk Apr 7, 2023
6574fa6
update return err
hieuvubk Apr 11, 2023
a00dbcf
update changelog
hieuvubk May 5, 2023
d9f3a6a
merge main
hieuvubk May 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion x/twap/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func (k Keeper) GetChangedPools(ctx sdk.Context) []uint64 {
return k.getChangedPools(ctx)
}

func (k Keeper) UpdateRecord(ctx sdk.Context, record types.TwapRecord) types.TwapRecord {
func (k Keeper) UpdateRecord(ctx sdk.Context, record types.TwapRecord) (types.TwapRecord, error) {
return k.updateRecord(ctx, record)
}

Expand Down
22 changes: 19 additions & 3 deletions x/twap/logic.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,31 @@ func (k Keeper) updateRecords(ctx sdk.Context, poolId uint64) error {
}

for _, record := range records {
newRecord := k.updateRecord(ctx, record)
newRecord, err := k.updateRecord(ctx, record)
if err != nil {
return err
}
k.storeNewRecord(ctx, newRecord)
}
return nil
}

// updateRecord returns a new record with updated accumulators and block time
// for the current block time.
func (k Keeper) updateRecord(ctx sdk.Context, record types.TwapRecord) types.TwapRecord {
func (k Keeper) updateRecord(ctx sdk.Context, record types.TwapRecord) (types.TwapRecord, error) {
// Returns error for last updated records in the same block.
// Exception: record is initialized when creating a pool,
// then the TwapAccumulator variables are zero.
if (record.Height >= ctx.BlockHeight() || !record.Time.Before(ctx.BlockTime())) &&
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (record.Height >= ctx.BlockHeight() || !record.Time.Before(ctx.BlockTime())) &&
if (record.Height > ctx.BlockHeight() || !record.Time.Before(ctx.BlockTime())) &&

Why is it not > but >=? Can the record not have same height as blockheight by design?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Its a case when creating new pool. New twap records will be created and updated immediately. I limit this by adding the condition ArithmeticTwapAccumulator must be zero (init twap record). But I should split it up

Copy link
Member

Choose a reason for hiding this comment

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

sweet, can we add this context as inline comment?

!record.P1ArithmeticTwapAccumulator.Equal(sdk.ZeroDec()) &&
mattverse marked this conversation as resolved.
Show resolved Hide resolved
!record.P0ArithmeticTwapAccumulator.Equal(sdk.ZeroDec()) {
return types.TwapRecord{}, types.InvalidRecordTimeError{
RecordBlockHeight: record.Height,
RecordTime: record.Time,
ActualBlockHeight: ctx.BlockHeight(),
ActualTime: ctx.BlockTime(),
}
}
newRecord := recordWithUpdatedAccumulators(record, ctx.BlockTime())
newRecord.Height = ctx.BlockHeight()

Expand All @@ -152,7 +168,7 @@ func (k Keeper) updateRecord(ctx sdk.Context, record types.TwapRecord) types.Twa
newRecord.P1LastSpotPrice = newSp1
newRecord.LastErrorTime = lastErrorTime

return newRecord
return newRecord, nil
}

// pruneRecords prunes twap records that happened earlier than recordHistoryKeepPeriod
Expand Down
92 changes: 57 additions & 35 deletions x/twap/logic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"

tmproto "github.com/tendermint/tendermint/proto/tendermint/types"

"github.com/osmosis-labs/osmosis/v12/app/apptesting/osmoassert"
gammtypes "github.com/osmosis-labs/osmosis/v12/x/gamm/types"
"github.com/osmosis-labs/osmosis/v12/x/twap"
Expand Down Expand Up @@ -227,7 +229,8 @@ func (s *TestSuite) TestUpdateRecord() {
defaultTwoAssetCoins[1].Denom, defaultTwoAssetCoins[0].Denom,
test.spotPriceResult1.Sp, test.spotPriceResult1.Err)

newRecord := s.twapkeeper.UpdateRecord(s.Ctx, test.record)
newRecord, err := s.twapkeeper.UpdateRecord(s.Ctx, test.record)
s.Require().NoError(err)
s.Equal(test.expRecord, newRecord)
})
}
Expand Down Expand Up @@ -756,6 +759,7 @@ func (s *TestSuite) TestUpdateRecords() {
ammMock twapmock.ProgrammedAmmInterface
spOverrides []spOverride
blockTime time.Time
blockHeight int64

expectedHistoricalRecords []expectedResults
expectError error
Expand Down Expand Up @@ -1005,9 +1009,8 @@ func (s *TestSuite) TestUpdateRecords() {
},
},
},
// This case should never happen in-practice since ctx.BlockTime
// should always be greater than the last record's time.
"two-asset; pre-set at t and t + 1, new record inserted between existing": {
"new record can't be inserted before the last record's update time": {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
"new record can't be inserted before the last record's update time": {
"new record can't be inserted prior to the last record's update time": {

preSetRecords: []types.TwapRecord{baseRecord, tPlus10sp5Record},
poolId: baseRecord.PoolId,

Expand All @@ -1026,31 +1029,47 @@ func (s *TestSuite) TestUpdateRecords() {
},
},

expectedHistoricalRecords: []expectedResults{
// The original record at t.
{
spotPriceA: baseRecord.P0LastSpotPrice,
spotPriceB: baseRecord.P1LastSpotPrice,
},
// The new record added.
// TODO: it should not be possible to add a record between existing records.
// https://github.com/osmosis-labs/osmosis/issues/2686
expectError: types.InvalidRecordTimeError{
Copy link
Member

Choose a reason for hiding this comment

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

Can we also add a test case for having block height greater than the last record's time and checking the error?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes of course

RecordBlockHeight: tPlus10sp5Record.Height,
RecordTime: tPlus10sp5Record.Time,
ActualBlockHeight: (baseRecord.Height + 1),
ActualTime: baseRecord.Time.Add(time.Second * 5),
},
},
// should always be greater than the last record's block.
"new record can't be inserted before the last record's update block": {
preSetRecords: []types.TwapRecord{mostRecentRecordPoolOne},
poolId: baseRecord.PoolId,

// Even if lastRecord.Time < ctx.Time,
// lastRecord.Height >= ctx.BlockHeight also throws error
blockTime: mostRecentRecordPoolOne.Time.Add(time.Second),
blockHeight: mostRecentRecordPoolOne.Height - 1,

spOverrides: []spOverride{
{
spotPriceA: sdk.OneDec(),
spotPriceB: sdk.OneDec().Add(sdk.OneDec()),
isMostRecent: true,
baseDenom: mostRecentRecordPoolOne.Asset0Denom,
quoteDenom: mostRecentRecordPoolOne.Asset1Denom,
overrideSp: sdk.OneDec(),
},
// The original record at t + 1.
{
spotPriceA: tPlus10sp5Record.P0LastSpotPrice,
spotPriceB: tPlus10sp5Record.P1LastSpotPrice,
baseDenom: mostRecentRecordPoolOne.Asset1Denom,
quoteDenom: mostRecentRecordPoolOne.Asset0Denom,
overrideSp: sdk.OneDec().Add(sdk.OneDec()),
},
},

expectError: types.InvalidRecordTimeError{
RecordBlockHeight: mostRecentRecordPoolOne.Height,
RecordTime: mostRecentRecordPoolOne.Time,
ActualBlockHeight: mostRecentRecordPoolOne.Height - 1,
ActualTime: mostRecentRecordPoolOne.Time.Add(time.Second),
},
},
"multi-asset pool; pre-set at t and t + 1; creates new records": {
preSetRecords: []types.TwapRecord{threeAssetRecordAB, threeAssetRecordAC, threeAssetRecordBC, tPlus10sp5ThreeAssetRecordAB, tPlus10sp5ThreeAssetRecordAC, tPlus10sp5ThreeAssetRecordBC},
poolId: threeAssetRecordAB.PoolId,
blockTime: threeAssetRecordAB.Time.Add(time.Second * 11),
blockTime: threeAssetRecordAB.Time.Add(time.Second * 11),
spOverrides: []spOverride{
{
baseDenom: threeAssetRecordAB.Asset0Denom,
Expand Down Expand Up @@ -1138,18 +1157,18 @@ func (s *TestSuite) TestUpdateRecords() {
"multi-asset pool; pre-set at t and t + 1 with err, large spot price, overwrites error time": {
preSetRecords: []types.TwapRecord{threeAssetRecordAB, threeAssetRecordAC, threeAssetRecordBC, withLastErrTime(tPlus10sp5ThreeAssetRecordAB, tPlus10sp5ThreeAssetRecordAB.Time), tPlus10sp5ThreeAssetRecordAC, tPlus10sp5ThreeAssetRecordBC},
poolId: threeAssetRecordAB.PoolId,
blockTime: threeAssetRecordAB.Time.Add(time.Second * 11),
blockTime: threeAssetRecordAB.Time.Add(time.Second * 11),
spOverrides: []spOverride{
{
baseDenom: threeAssetRecordAB.Asset0Denom,
quoteDenom: threeAssetRecordAB.Asset1Denom,
overrideSp: sdk.OneDec(),
},
{
baseDenom: threeAssetRecordAB.Asset1Denom,
quoteDenom: threeAssetRecordAB.Asset0Denom,
overrideSp: sdk.OneDec().Add(sdk.OneDec()),
},
baseDenom: threeAssetRecordAB.Asset1Denom,
quoteDenom: threeAssetRecordAB.Asset0Denom,
overrideSp: sdk.OneDec().Add(sdk.OneDec()),
},
{
baseDenom: threeAssetRecordAC.Asset0Denom,
quoteDenom: threeAssetRecordAC.Asset1Denom,
Expand All @@ -1167,9 +1186,9 @@ func (s *TestSuite) TestUpdateRecords() {
overrideSp: sdk.OneDec(),
},
{
baseDenom: threeAssetRecordBC.Asset1Denom,
quoteDenom: threeAssetRecordBC.Asset0Denom,
overrideSp: sdk.OneDec().Add(sdk.OneDec()),
baseDenom: threeAssetRecordBC.Asset1Denom,
quoteDenom: threeAssetRecordBC.Asset0Denom,
overrideSp: sdk.OneDec().Add(sdk.OneDec()),
},
},

Expand All @@ -1188,7 +1207,7 @@ func (s *TestSuite) TestUpdateRecords() {
// The new record AB added.
{
spotPriceA: sdk.OneDec(),
spotPriceB: sdk.OneDec().Add(sdk.OneDec()),
spotPriceB: sdk.OneDec().Add(sdk.OneDec()),
lastErrorTime: tPlus10sp5ThreeAssetRecordAB.Time,
isMostRecent: true,
},
Expand All @@ -1199,8 +1218,8 @@ func (s *TestSuite) TestUpdateRecords() {
},
// The original record AC at t + 1.
{
spotPriceA: tPlus10sp5ThreeAssetRecordAC.P0LastSpotPrice,
spotPriceB: tPlus10sp5ThreeAssetRecordAC.P1LastSpotPrice,
spotPriceA: tPlus10sp5ThreeAssetRecordAC.P0LastSpotPrice,
spotPriceB: tPlus10sp5ThreeAssetRecordAC.P1LastSpotPrice,
},
// The new record AC added.
{
Expand All @@ -1216,14 +1235,14 @@ func (s *TestSuite) TestUpdateRecords() {
},
// The original record BC at t + 1.
{
spotPriceA: tPlus10sp5ThreeAssetRecordBC.P0LastSpotPrice,
spotPriceB: tPlus10sp5ThreeAssetRecordBC.P1LastSpotPrice,
spotPriceA: tPlus10sp5ThreeAssetRecordBC.P0LastSpotPrice,
spotPriceB: tPlus10sp5ThreeAssetRecordBC.P1LastSpotPrice,
},
// The new record BC added.
{
spotPriceA: sdk.OneDec(),
spotPriceB: sdk.OneDec().Add(sdk.OneDec()),
isMostRecent: true,
spotPriceA: sdk.OneDec(),
spotPriceB: sdk.OneDec().Add(sdk.OneDec()),
isMostRecent: true,
},
},
},
Expand All @@ -1234,6 +1253,9 @@ func (s *TestSuite) TestUpdateRecords() {
s.SetupTest()
twapKeeper := s.App.TwapKeeper
ctx := s.Ctx.WithBlockTime(tc.blockTime)
if tc.blockHeight != 0 {
ctx = s.Ctx.WithBlockHeader(tmproto.Header{Time: tc.blockTime, Height: tc.blockHeight})
Copy link
Member

Choose a reason for hiding this comment

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

Can we use the WithBlockTime and WithBlockHeight API instead?

}

if len(tc.spOverrides) > 0 {
ammMock := twapmock.NewProgrammedAmmInterface(s.App.GAMMKeeper)
Expand Down
11 changes: 11 additions & 0 deletions x/twap/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,14 @@ type InvalidRecordCountError struct {
func (e InvalidRecordCountError) Error() string {
return fmt.Sprintf("The number of records do not match, expected: %d\n got: %d", e.Expected, e.Actual)
}

type InvalidRecordTimeError struct {
Copy link
Member

Choose a reason for hiding this comment

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

I'm wondering if we should change this to InvalidUpdateRecordError or something similar, since this error does not only happen when it has a invalid record time but it also includes invlaid record height as well. Lmk what you think

RecordBlockHeight int64
RecordTime time.Time
ActualBlockHeight int64
ActualTime time.Time
}

func (e InvalidRecordTimeError) Error() string {
return fmt.Sprintf("failed to update the record, the context time must be greater than record time; record: block %d at %s, actual: block %d at %s", e.RecordBlockHeight, e.RecordTime, e.ActualBlockHeight, e.ActualTime)
}