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

compress vote extension data #74

Merged
merged 9 commits into from
Jul 17, 2024
2 changes: 2 additions & 0 deletions app/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
cmtjson "github.com/cometbft/cometbft/libs/json"
cmttypes "github.com/cometbft/cometbft/types"
dbm "github.com/cosmos/cosmos-db"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/cosmos-sdk/testutil/mock"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
Expand All @@ -26,6 +27,7 @@ func Setup(t *testing.T, isCheckTx bool) *App {
db := dbm.NewMemDB()
var wasmOpts []wasmkeeper.Option
appOptions := make(simtestutil.AppOptionsMap, 0)
appOptions[flags.FlagHome] = "/tmp/" + t.Name()

app := New(
log.NewNopLogger(),
Expand Down
36 changes: 13 additions & 23 deletions proto/kujira/oracle/oracle.proto
Original file line number Diff line number Diff line change
Expand Up @@ -22,40 +22,32 @@ message Params {
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
repeated string required_denoms = 4 [
(gogoproto.moretags) = "yaml:\"required_denoms\""
repeated Symbol required_symbols = 4 [
(gogoproto.moretags) = "yaml:\"required_symbols\"",
(gogoproto.nullable) = false
];
string slash_fraction = 5 [
uint32 last_symbol_id = 5;
string slash_fraction = 6 [
(gogoproto.moretags) = "yaml:\"slash_fraction\"",
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
uint64 slash_window = 6 [(gogoproto.moretags) = "yaml:\"slash_window\""];
string min_valid_per_window = 7 [
uint64 slash_window = 7 [(gogoproto.moretags) = "yaml:\"slash_window\""];
string min_valid_per_window = 8 [
(gogoproto.moretags) = "yaml:\"min_valid_per_window\"",
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
// Deprecated
string reward_band = 8 [
(gogoproto.moretags) = "yaml:\"reward_band\"",
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
repeated Denom whitelist = 9 [
(gogoproto.moretags) = "yaml:\"whitelist\"",
(gogoproto.castrepeated) = "DenomList",
(gogoproto.nullable) = false
];
}

// Denom - the object to hold configurations of each denom
message Denom {
// Symbol - the object to hold configurations of each symbol
message Symbol {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
option (gogoproto.goproto_stringer) = false;

string name = 1 [(gogoproto.moretags) = "yaml:\"name\""];
string symbol = 1;
uint32 id = 2;
}

// ExchangeRateTuple - struct to store interpreted exchange rates data to store
Expand All @@ -64,7 +56,7 @@ message ExchangeRateTuple {
option (gogoproto.goproto_getters) = false;
option (gogoproto.goproto_stringer) = false;

string denom = 1 [(gogoproto.moretags) = "yaml:\"denom\""];
string symbol = 1 [ (gogoproto.moretags) = "yaml:\"symbol\"" ];
string exchange_rate = 2 [
(gogoproto.moretags) = "yaml:\"exchange_rate\"",
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
Expand All @@ -74,7 +66,5 @@ message ExchangeRateTuple {

message VoteExtension {
int64 height = 1;
repeated ExchangeRateTuple prices = 2 [
(gogoproto.nullable) = false
];
map<uint32, bytes> prices = 2;
}
22 changes: 11 additions & 11 deletions proto/kujira/oracle/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@ option go_package = "github.com/Team-Kujira/core/x/oracle/types";

// Query defines the gRPC querier service.
service Query {
// ExchangeRate returns exchange rate of a denom
// ExchangeRate returns exchange rate of a symbol
rpc ExchangeRate(QueryExchangeRateRequest) returns (QueryExchangeRateResponse) {
option (google.api.http).get = "/oracle/denoms/{denom}/exchange_rate";
option (google.api.http).get = "/oracle/symbols/{symbol}/exchange_rate";
}

// ExchangeRates returns exchange rates of all denoms
// ExchangeRates returns exchange rates of all symbols
rpc ExchangeRates(QueryExchangeRatesRequest) returns (QueryExchangeRatesResponse) {
option (google.api.http).get = "/oracle/denoms/exchange_rates";
option (google.api.http).get = "/oracle/symbols/exchange_rates";
}

// Actives returns all active denoms
// Actives returns all active symbols
rpc Actives(QueryActivesRequest) returns (QueryActivesResponse) {
option (google.api.http).get = "/oracle/denoms/actives";
option (google.api.http).get = "/oracle/symbols/actives";
}

// MissCounter returns oracle miss counter of a validator
Expand All @@ -41,8 +41,8 @@ message QueryExchangeRateRequest {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;

// denom defines the denomination to query for.
string denom = 1;
// symbol defines the symbol to query for.
string symbol = 1;
}

// QueryExchangeRateResponse is response type for the
Expand All @@ -59,7 +59,7 @@ message QueryExchangeRatesRequest {}
// QueryExchangeRatesResponse is response type for the
// Query/ExchangeRates RPC method.
message QueryExchangeRatesResponse {
// exchange_rates defines a list of the exchange rate for all whitelisted denoms.
// exchange_rates defines a list of the exchange rate for all whitelisted symbols.
repeated cosmos.base.v1beta1.DecCoin exchange_rates = 1
[(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins", (gogoproto.nullable) = false];
}
Expand All @@ -70,7 +70,7 @@ message QueryActivesRequest {}
// QueryActivesResponse is response type for the
// Query/Actives RPC method.
message QueryActivesResponse {
// actives defines a list of the denomination which oracle prices aggreed upon.
// actives defines a list of the symbols which oracle prices aggreed upon.
repeated string actives = 1;
}

Expand All @@ -80,7 +80,7 @@ message QueryVoteTargetsRequest {}
// QueryVoteTargetsResponse is response type for the
// Query/VoteTargets RPC method.
message QueryVoteTargetsResponse {
// vote_targets defines a list of the denomination in which everyone
// vote_targets defines a list of the symbols in which everyone
// should vote in the current vote period.
repeated string vote_targets = 1;
}
Expand Down
30 changes: 15 additions & 15 deletions proto/kujira/oracle/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -9,37 +9,37 @@ option go_package = "github.com/Team-Kujira/core/x/oracle/types";

// Msg defines the oracle Msg service.
service Msg {
// AddRequiredDenom adds a new price to the required list of prices
rpc AddRequiredDenom(MsgAddRequiredDenom) returns (MsgAddRequiredDenomResponse);
// AddRequiredSymbol adds a new price to the required list of prices
rpc AddRequiredSymbols(MsgAddRequiredSymbols) returns (MsgAddRequiredSymbolsResponse);

// RemoveRequiredDenom removes a price from the required list of prices
rpc RemoveRequiredDenom(MsgRemoveRequiredDenom) returns (MsgRemoveRequiredDenomResponse);
// RemoveRequiredSymbol removes a price from the required list of prices
rpc RemoveRequiredSymbols(MsgRemoveRequiredSymbols) returns (MsgRemoveRequiredSymbolsResponse);

// UpdateParams sets new module params
rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse);
}

// MsgAddRequiredDenom represents a message to add a denom to the whitelist
message MsgAddRequiredDenom {
// MsgAddRequiredSymbol represents a message to add a symbol to the whitelist
message MsgAddRequiredSymbols {
option (cosmos.msg.v1.signer) = "authority";

string authority = 1 [(gogoproto.moretags) = "yaml:\"authority\""];
string symbol = 2 [(gogoproto.moretags) = "yaml:\"symbol\""];
repeated string symbols = 2 [ (gogoproto.moretags) = "yaml:\"symbols\"" ];
}

// MsgAddRequiredDenomResponse defines the Msg/AddRequiredDenom response type.
message MsgAddRequiredDenomResponse {}
// MsgAddRequiredSymbolResponse defines the Msg/AddRequiredSymbol response type.
message MsgAddRequiredSymbolsResponse {}

// MsgRemoveRequiredDenom represents a message to remove a denom from the whitelist
message MsgRemoveRequiredDenom {
// MsgRemoveRequiredSymbol represents a message to remove a symbol from the whitelist
message MsgRemoveRequiredSymbols {
option (cosmos.msg.v1.signer) = "authority";

string authority = 1 [(gogoproto.moretags) = "yaml:\"authority\""];
string symbol = 2 [(gogoproto.moretags) = "yaml:\"symbol\""];
string authority = 1 [ (gogoproto.moretags) = "yaml:\"authority\"" ];
repeated string symbols = 2 [ (gogoproto.moretags) = "yaml:\"symbols\"" ];
}

// MsgRemoveRequiredDenomResponse defines the Msg/RemoveRequiredDenom response type.
message MsgRemoveRequiredDenomResponse {}
// MsgRemoveRequiredSymbolResponse defines the Msg/RemoveRequiredSymbol response type.
message MsgRemoveRequiredSymbolsResponse {}

message MsgUpdateParams {
option (cosmos.msg.v1.signer) = "authority";
Expand Down
75 changes: 75 additions & 0 deletions x/oracle/abci/compress.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package abci

import (
"math/big"

"cosmossdk.io/math"
"github.com/Team-Kujira/core/x/oracle/keeper"
"github.com/Team-Kujira/core/x/oracle/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)

func CompressDecimal(dec math.LegacyDec, desiredValidDigit int64) []byte {
validDigits := int64(0)
newAmount := dec
for !newAmount.IsZero() {
newAmount = newAmount.QuoInt64(10)
validDigits++
}
cuttingDigits := validDigits - desiredValidDigit
if cuttingDigits < 0 {
cuttingDigits = 0
}
cutAmount := dec.Quo(math.LegacyNewDec(10).Power(uint64(cuttingDigits)))
return append(cutAmount.BigInt().Bytes(), byte(cuttingDigits))
}

func DecompressDecimal(bz []byte) math.LegacyDec {
if len(bz) == 0 {
return math.LegacyZeroDec()
}
cuttingDigits := int(bz[len(bz)-1])
amountInt := new(big.Int).SetBytes(bz[:len(bz)-1])
amountDec := math.LegacyNewDecFromBigIntWithPrec(amountInt, math.LegacyPrecision)
amountDec = amountDec.Mul(math.LegacyNewDec(10).Power(uint64(cuttingDigits)))
return amountDec
}

func ComposeVoteExtension(k keeper.Keeper, ctx sdk.Context, height int64, exchangeRates sdk.DecCoins) types.VoteExtension {
params := k.GetParams(ctx)
symbolIDs := make(map[string]uint32)
for _, symbol := range params.RequiredSymbols {
symbolIDs[symbol.Symbol] = symbol.Id
}
prices := make(map[uint32][]byte)
for _, rate := range exchangeRates {
id, ok := symbolIDs[rate.Denom]
if !ok {
continue
}
prices[id] = CompressDecimal(rate.Amount, 8)
}
return types.VoteExtension{
Height: height,
Prices: prices,
}
}

func ExchangeRatesFromVoteExtension(k keeper.Keeper, ctx sdk.Context, voteExt types.VoteExtension) sdk.DecCoins {
params := k.GetParams(ctx)
idToSymbol := make(map[uint32]string)
for _, symbol := range params.RequiredSymbols {
idToSymbol[symbol.Id] = symbol.Symbol
}

exchangeRates := sdk.DecCoins{}
for id, priceBytes := range voteExt.Prices {
symbol, ok := idToSymbol[id]
if !ok {
continue
}

exchangeRates = exchangeRates.Add(sdk.NewDecCoinFromDec(symbol, DecompressDecimal(priceBytes)))
}
return exchangeRates
}
62 changes: 62 additions & 0 deletions x/oracle/abci/compress_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package abci_test

import (
"testing"

"cosmossdk.io/math"
"github.com/Team-Kujira/core/x/oracle/abci"
"github.com/stretchr/testify/require"
)

func TestCompressDecimal(t *testing.T) {
// compress 0 decimal
bz := abci.CompressDecimal(math.LegacyZeroDec(), 8)
require.Len(t, bz, 1)

// compress negative decimal - only abs value's encoded/decoded
bz = abci.CompressDecimal(math.LegacyNewDec(-1), 8)
require.Len(t, bz, 4)

// compress low number of valid digits decimal
bz = abci.CompressDecimal(math.LegacyNewDecWithPrec(123, 18), 8)
require.Len(t, bz, 2)

// compress high number of valid digits decimal
bz = abci.CompressDecimal(math.LegacyNewDecWithPrec(123456123456, 18), 8)
require.Len(t, bz, 4)

// compress big number
bz = abci.CompressDecimal(math.LegacyNewDec(123456123456123456), 8)
require.Len(t, bz, 4)
}

func TestDecompressDecimal(t *testing.T) {
// empty bytes
decoded := abci.DecompressDecimal([]byte{})
require.Equal(t, decoded, math.LegacyZeroDec())

// decompress 0 decimal bytes
bz := abci.CompressDecimal(math.LegacyZeroDec(), 8)
decoded = abci.DecompressDecimal(bz)
require.Equal(t, decoded, math.LegacyZeroDec())

// decompress negative decimal bytes - only abs value's encoded/decoded
bz = abci.CompressDecimal(math.LegacyNewDec(-1), 8)
decoded = abci.DecompressDecimal(bz)
require.Equal(t, decoded, math.LegacyNewDec(1))

// decompress low number of valid digits decimal bytes
bz = abci.CompressDecimal(math.LegacyNewDecWithPrec(123, 18), 8)
decoded = abci.DecompressDecimal(bz)
require.Equal(t, decoded, math.LegacyNewDecWithPrec(123, 18))

// decompress high number of valid digits decimal bytes
bz = abci.CompressDecimal(math.LegacyNewDecWithPrec(123456123456, 18), 8)
decoded = abci.DecompressDecimal(bz)
require.Equal(t, decoded, math.LegacyNewDecWithPrec(123456120000, 18))

// decompress big number bytes
bz = abci.CompressDecimal(math.LegacyNewDec(123456123456123456), 8)
decoded = abci.DecompressDecimal(bz)
require.Equal(t, decoded, math.LegacyNewDec(123456120000000000))
}
Loading
Loading