diff --git a/util/bytes.go b/util/bytes.go new file mode 100644 index 0000000000..c2fe55a88e --- /dev/null +++ b/util/bytes.go @@ -0,0 +1,17 @@ +package util + +// ConcatBytes creates a new slice by merging list of bytes and leaving empty amount of margin +// bytes at the end +func ConcatBytes(margin int, bzs ...[]byte) []byte { + l := 0 + for _, bz := range bzs { + l += len(bz) + } + out := make([]byte, l+margin) + offset := 0 + for _, bz := range bzs { + copy(out[offset:], bz) + offset += len(bz) + } + return out +} diff --git a/util/bytes_test.go b/util/bytes_test.go new file mode 100644 index 0000000000..ab72668e7c --- /dev/null +++ b/util/bytes_test.go @@ -0,0 +1,24 @@ +package util + +import "testing" +import "github.com/stretchr/testify/require" + +func TestMergeBytes(t *testing.T) { + require := require.New(t) + tcs := []struct { + in [][]byte + inMargin int + out []byte + }{ + {[][]byte{}, 0, []byte{}}, + {[][]byte{}, 2, []byte{0, 0}}, + {[][]byte{{1}}, 0, []byte{1}}, + {[][]byte{{1}}, 1, []byte{1, 0}}, + {[][]byte{{1, 2}, {2}}, 0, []byte{1, 2, 2}}, + {[][]byte{{1, 2}, {2}}, 3, []byte{1, 2, 2, 0, 0, 0}}, + {[][]byte{{1, 2}, {2}, {3, 3}, {4}}, 1, []byte{1, 2, 2, 3, 3, 4, 0}}, + } + for i, tc := range tcs { + require.Equal(tc.out, ConcatBytes(tc.inMargin, tc.in...), i) + } +} diff --git a/x/oracle/keeper/historic_price.go b/x/oracle/keeper/historic_price.go index b7e5bf19b6..e504d31eb5 100644 --- a/x/oracle/keeper/historic_price.go +++ b/x/oracle/keeper/historic_price.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/umee-network/umee/v3/util" "github.com/umee-network/umee/v3/x/oracle/types" ) @@ -54,7 +55,7 @@ func (k Keeper) GetMedian( blockNum uint64, ) (sdk.Dec, error) { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.GetMedianKey(denom, blockNum)) + bz := store.Get(types.MedianKey(denom, blockNum)) if bz == nil { return sdk.ZeroDec(), sdkerrors.Wrap(types.ErrNoMedian, fmt.Sprintf("denom: %s block: %d", denom, blockNum)) } @@ -77,7 +78,7 @@ func (k Keeper) SetMedian( historicPrices := k.getHistoricPrices(ctx, denom) median := median(historicPrices) bz := k.cdc.MustMarshal(&sdk.DecProto{Dec: median}) - store.Set(types.GetMedianKey(denom, uint64(ctx.BlockHeight())), bz) + store.Set(types.MedianKey(denom, uint64(ctx.BlockHeight())), bz) k.setMedianDeviation(ctx, denom, median, historicPrices) } @@ -89,7 +90,7 @@ func (k Keeper) GetMedianDeviation( blockNum uint64, ) (sdk.Dec, error) { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.GetMedianDeviationKey(denom, blockNum)) + bz := store.Get(types.MedianDeviationKey(denom, blockNum)) if bz == nil { return sdk.ZeroDec(), sdkerrors.Wrap(types.ErrNoMedianDeviation, fmt.Sprintf("denom: %s block: %d", denom, blockNum)) } @@ -111,7 +112,7 @@ func (k Keeper) setMedianDeviation( store := ctx.KVStore(k.storeKey) medianDeviation := medianDeviation(median, prices) bz := k.cdc.MustMarshal(&sdk.DecProto{Dec: medianDeviation}) - store.Set(types.GetMedianDeviationKey(denom, uint64(ctx.BlockHeight())), bz) + store.Set(types.MedianDeviationKey(denom, uint64(ctx.BlockHeight())), bz) } // getHistoricPrices returns all the historic prices of a given denom. @@ -143,7 +144,9 @@ func (k Keeper) IterateHistoricPrices( ) { store := ctx.KVStore(k.storeKey) - iter := sdk.KVStorePrefixIterator(store, append(types.KeyPrefixHistoricPrice, []byte(denom)...)) + // make sure we have one zero byte to correctly separate denoms + prefix := util.ConcatBytes(1, types.KeyPrefixHistoricPrice, []byte(denom)) + iter := sdk.KVStorePrefixIterator(store, prefix) defer iter.Close() for ; iter.Valid(); iter.Next() { @@ -168,7 +171,7 @@ func (k Keeper) AddHistoricPrice( ExchangeRate: exchangeRate, BlockNum: block, }) - store.Set(types.GetHistoricPriceKey(denom, block), bz) + store.Set(types.HistoricPriceKey(denom, block), bz) } // DeleteHistoricPrice deletes the historic price of a denom at a @@ -179,7 +182,7 @@ func (k Keeper) DeleteHistoricPrice( blockNum uint64, ) { store := ctx.KVStore(k.storeKey) - store.Delete(types.GetHistoricPriceKey(denom, blockNum)) + store.Delete(types.HistoricPriceKey(denom, blockNum)) } // DeleteMedian deletes a given denom's median price in the last prune @@ -190,7 +193,7 @@ func (k Keeper) DeleteMedian( blockNum uint64, ) { store := ctx.KVStore(k.storeKey) - store.Delete(types.GetMedianKey(denom, blockNum)) + store.Delete(types.MedianKey(denom, blockNum)) } // DeleteMedianDeviation deletes a given denom's standard deviation around @@ -201,5 +204,5 @@ func (k Keeper) DeleteMedianDeviation( blockNum uint64, ) { store := ctx.KVStore(k.storeKey) - store.Delete(types.GetMedianDeviationKey(denom, blockNum)) + store.Delete(types.MedianDeviationKey(denom, blockNum)) } diff --git a/x/oracle/types/keys.go b/x/oracle/types/keys.go index 13bd80d4a6..c8cc82c978 100644 --- a/x/oracle/types/keys.go +++ b/x/oracle/types/keys.go @@ -61,33 +61,30 @@ func GetAggregateExchangeRateVoteKey(v sdk.ValAddress) (key []byte) { return append(key, address.MustLengthPrefix(v)...) } -// GetMedianKey - stored by *denom* and *block* -func GetMedianKey(denom string, blockNum uint64) (key []byte) { +// MedianKey - stored by *denom* and *block* +func MedianKey(denom string, blockNum uint64) (key []byte) { key = append(key, KeyPrefixMedian...) - key = appendDenomAndBlockToKey(key, denom, blockNum) - return append(key, 0) // append 0 for null-termination + return appendDenomAndBlock(key, denom, blockNum) } -// GetMedianDeviationKey - stored by *denom* and *block* -func GetMedianDeviationKey(denom string, blockNum uint64) (key []byte) { +// MedianDeviationKey - stored by *denom* and *block* +func MedianDeviationKey(denom string, blockNum uint64) (key []byte) { key = append(key, KeyPrefixMedianDeviation...) - key = appendDenomAndBlockToKey(key, denom, blockNum) - return append(key, 0) // append 0 for null-termination + return appendDenomAndBlock(key, denom, blockNum) } -// GetHistoricPriceKey - stored by *denom* and *block* -func GetHistoricPriceKey(denom string, blockNum uint64) (key []byte) { +// HistoricPriceKey - stored by *denom* and *block* +func HistoricPriceKey(denom string, blockNum uint64) (key []byte) { key = append(key, KeyPrefixHistoricPrice...) - key = appendDenomAndBlockToKey(key, denom, blockNum) - return append(key, 0) // append 0 for null-termination + return appendDenomAndBlock(key, denom, blockNum) } -func appendDenomAndBlockToKey(key []byte, denom string, blockNum uint64) []byte { +func appendDenomAndBlock(key []byte, denom string, blockNum uint64) []byte { key = append(key, []byte(denom)...) + key = append(key, 0) // null delimeter to avoid collision between different denoms block := make([]byte, 8) binary.LittleEndian.PutUint64(block, blockNum) - key = append(key, block...) - return key + return append(key, block...) } func ParseDemonFromHistoricPriceKey(key []byte) string {