Skip to content

Commit

Permalink
Merge pull request #84 from osmosis-labs/queryratio
Browse files Browse the repository at this point in the history
Add custom query for slash ratios
  • Loading branch information
alpe authored Oct 11, 2023
2 parents 67511af + b50be3a commit e753038
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 42 deletions.
2 changes: 1 addition & 1 deletion demo/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,7 @@ func NewMeshApp(
)
})
wasmOpts = append(wasmOpts, meshMessageHandler,
wasmkeeper.WithQueryHandlerDecorator(meshseckeeper.NewQueryDecorator(app.MeshSecKeeper)),
wasmkeeper.WithQueryHandlerDecorator(meshseckeeper.NewQueryDecorator(app.MeshSecKeeper, app.SlashingKeeper)),
)
// The last arguments can contain custom message handlers, and custom query handlers,
// if we want to allow any custom callbacks
Expand Down
6 changes: 6 additions & 0 deletions x/meshsecurity/contract/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type CustomQuery struct {

type VirtualStakeQuery struct {
BondStatus *BondStatusQuery `json:"bond_status,omitempty"`
SlashRatio *struct{} `json:"slash_ratio,omitempty"`
}

type BondStatusQuery struct {
Expand All @@ -20,3 +21,8 @@ type BondStatusResponse struct {
// Delegated is the used amount of the max cap
Delegated wasmvmtypes.Coin `json:"delegated"`
}

type SlashRatioResponse struct {
SlashFractionDowntime string `json:"slash_fraction_downtime"`
SlashFractionDoubleSign string `json:"slash_fraction_double_sign"`
}
12 changes: 12 additions & 0 deletions x/meshsecurity/keeper/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import (
paramsclient "github.com/cosmos/cosmos-sdk/x/params/client"
paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper"
paramstypes "github.com/cosmos/cosmos-sdk/x/params/types"
slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper"
slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
"github.com/cosmos/cosmos-sdk/x/staking"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
Expand Down Expand Up @@ -99,6 +100,7 @@ func makeEncodingConfig(_ testing.TB) encodingConfig {

type TestKeepers struct {
StakingKeeper *stakingkeeper.Keeper
SlashingKeeper slashingkeeper.Keeper
BankKeeper bankkeeper.Keeper
StoreKey *storetypes.KVStoreKey
EncodingConfig encodingConfig
Expand Down Expand Up @@ -181,6 +183,15 @@ func CreateDefaultTestInput(t testing.TB) (sdk.Context, TestKeepers) {
)
require.NoError(t, stakingKeeper.SetParams(ctx, stakingtypes.DefaultParams()))

slashingKeeper := slashingkeeper.NewKeeper(
appCodec,
encConfig.Amino,
keys[slashingtypes.StoreKey],
stakingKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)
require.NoError(t, slashingKeeper.SetParams(ctx, slashingtypes.DefaultParams()))

distKeeper := distributionkeeper.NewKeeper(
appCodec,
keys[distributiontypes.StoreKey],
Expand Down Expand Up @@ -268,6 +279,7 @@ func CreateDefaultTestInput(t testing.TB) (sdk.Context, TestKeepers) {
return ctx, TestKeepers{
AccountKeeper: accountKeeper,
StakingKeeper: stakingKeeper,
SlashingKeeper: slashingKeeper,
BankKeeper: bankKeeper,
StoreKey: keys[types.StoreKey],
EncodingConfig: encConfig,
Expand Down
62 changes: 40 additions & 22 deletions x/meshsecurity/keeper/query_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,27 @@ import (
"github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity/contract"
)

// abstract query keeper
type viewKeeper interface {
GetMaxCapLimit(ctx sdk.Context, actor sdk.AccAddress) sdk.Coin
GetTotalDelegated(ctx sdk.Context, actor sdk.AccAddress) sdk.Coin
}
type (
// abstract query keeper
viewKeeper interface {
GetMaxCapLimit(ctx sdk.Context, actor sdk.AccAddress) sdk.Coin
GetTotalDelegated(ctx sdk.Context, actor sdk.AccAddress) sdk.Coin
}
slashingKeeper interface {
SlashFractionDoubleSign(ctx sdk.Context) (res sdk.Dec)
SlashFractionDowntime(ctx sdk.Context) (res sdk.Dec)
}
)

// NewQueryDecorator constructor to build a chained custom querier.
// The mesh-security custom query handler is placed at the first position
// and delegates to the next in chain for any queries that do not match
// the mesh-security custom query namespace.
//
// To be used with `wasmkeeper.WithQueryHandlerDecorator(meshseckeeper.NewQueryDecorator(app.MeshSecKeeper)))`
func NewQueryDecorator(k viewKeeper) func(wasmkeeper.WasmVMQueryHandler) wasmkeeper.WasmVMQueryHandler {
func NewQueryDecorator(k viewKeeper, sk slashingKeeper) func(wasmkeeper.WasmVMQueryHandler) wasmkeeper.WasmVMQueryHandler {
return func(next wasmkeeper.WasmVMQueryHandler) wasmkeeper.WasmVMQueryHandler {
return ChainedCustomQuerier(k, next)
return ChainedCustomQuerier(k, sk, next)
}
}

Expand All @@ -38,9 +44,12 @@ func NewQueryDecorator(k viewKeeper) func(wasmkeeper.WasmVMQueryHandler) wasmkee
//
// This CustomQuerier is designed as an extension point. See the NewQueryDecorator impl how to
// set this up for wasmd.
func ChainedCustomQuerier(k viewKeeper, next wasmkeeper.WasmVMQueryHandler) wasmkeeper.WasmVMQueryHandler {
func ChainedCustomQuerier(k viewKeeper, sk slashingKeeper, next wasmkeeper.WasmVMQueryHandler) wasmkeeper.WasmVMQueryHandler {
if k == nil {
panic("keeper must not be nil")
panic("ms keeper must not be nil")
}
if sk == nil {
panic("slashing Keeper must not be nil")
}
if next == nil {
panic("next handler must not be nil")
Expand All @@ -53,22 +62,31 @@ func ChainedCustomQuerier(k viewKeeper, next wasmkeeper.WasmVMQueryHandler) wasm
if err := json.Unmarshal(request.Custom, &contractQuery); err != nil {
return nil, errorsmod.Wrap(err, "mesh-security query")
}
if contractQuery.VirtualStake == nil || contractQuery.VirtualStake.BondStatus == nil {
query := contractQuery.VirtualStake
if query == nil {
return next.HandleQuery(ctx, caller, request)
}
contractAddr, err := sdk.AccAddressFromBech32(contractQuery.VirtualStake.BondStatus.Contract)
if err != nil {
return nil, sdkerrors.ErrInvalidAddress.Wrap(contractQuery.VirtualStake.BondStatus.Contract)
}
res := contract.BondStatusResponse{
MaxCap: wasmkeeper.ConvertSdkCoinToWasmCoin(k.GetMaxCapLimit(ctx, contractAddr)),
Delegated: wasmkeeper.ConvertSdkCoinToWasmCoin(k.GetTotalDelegated(ctx, contractAddr)),
}
bz, err := json.Marshal(res)
if err != nil {
return nil, errorsmod.Wrap(err, "mesh-security max cap query response")

var res any
switch {
case query.BondStatus != nil:
contractAddr, err := sdk.AccAddressFromBech32(query.BondStatus.Contract)
if err != nil {
return nil, sdkerrors.ErrInvalidAddress.Wrap(query.BondStatus.Contract)
}
res = contract.BondStatusResponse{
MaxCap: wasmkeeper.ConvertSdkCoinToWasmCoin(k.GetMaxCapLimit(ctx, contractAddr)),
Delegated: wasmkeeper.ConvertSdkCoinToWasmCoin(k.GetTotalDelegated(ctx, contractAddr)),
}
case query.SlashRatio != nil:
res = contract.SlashRatioResponse{
SlashFractionDowntime: sk.SlashFractionDowntime(ctx).String(),
SlashFractionDoubleSign: sk.SlashFractionDoubleSign(ctx).String(),
}
default:
return nil, wasmvmtypes.UnsupportedRequest{Kind: "unknown virtual_stake query variant"}
}
return bz, nil
return json.Marshal(res)
})
}

Expand Down
50 changes: 31 additions & 19 deletions x/meshsecurity/keeper/query_plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,49 @@ import (

func TestChainedCustomQuerier(t *testing.T) {
myContractAddr := sdk.AccAddress(rand.Bytes(32))
pCtx, keepers := CreateDefaultTestInput(t)

specs := map[string]struct {
src wasmvmtypes.QueryRequest
mockMaxCapLimit func(ctx sdk.Context, actor sdk.AccAddress) sdk.Coin
mockTotalDelegated func(ctx sdk.Context, actor sdk.AccAddress) sdk.Coin
expData []byte
expErr bool
expMocNextCalled bool
src wasmvmtypes.QueryRequest
viewKeeper viewKeeper
expData []byte
expErr bool
expNextCalled bool
}{
"all good": {
"bond status query": {
src: wasmvmtypes.QueryRequest{
Custom: []byte(fmt.Sprintf(`{"virtual_stake":{"bond_status":{"contract":%q}}}`, myContractAddr.String())),
},
mockMaxCapLimit: func(ctx sdk.Context, actor sdk.AccAddress) sdk.Coin {
return sdk.NewCoin("ALX", math.NewInt(123))
},
mockTotalDelegated: func(ctx sdk.Context, actor sdk.AccAddress) sdk.Coin {
return sdk.NewCoin("ALX", math.NewInt(456))
viewKeeper: &MockViewKeeper{
GetMaxCapLimitFn: func(ctx sdk.Context, actor sdk.AccAddress) sdk.Coin {
return sdk.NewCoin("ALX", math.NewInt(123))
},
GetTotalDelegatedFn: func(ctx sdk.Context, actor sdk.AccAddress) sdk.Coin {
return sdk.NewCoin("ALX", math.NewInt(456))
},
},
expData: []byte(`{"cap":{"denom":"ALX","amount":"123"},"delegated":{"denom":"ALX","amount":"456"}}`),
},
"slash ratio query": {
src: wasmvmtypes.QueryRequest{
Custom: []byte(`{"virtual_stake":{"slash_ratio":{}}}`),
},
viewKeeper: keepers.MeshKeeper,
expData: []byte(`{"slash_fraction_downtime":"0.010000000000000000","slash_fraction_double_sign":"0.050000000000000000"}`),
},
"non custom query": {
src: wasmvmtypes.QueryRequest{
Bank: &wasmvmtypes.BankQuery{},
},
expMocNextCalled: true,
viewKeeper: keepers.MeshKeeper,
expNextCalled: true,
},
"custom non mesh query": {
src: wasmvmtypes.QueryRequest{
Custom: []byte(`{"foo":{}}`),
},
expMocNextCalled: true,
viewKeeper: keepers.MeshKeeper,
expNextCalled: true,
},
}
for name, spec := range specs {
Expand All @@ -56,16 +68,16 @@ func TestChainedCustomQuerier(t *testing.T) {
nextCalled = true
return nil, nil
})
mock := &MockViewKeeper{GetMaxCapLimitFn: spec.mockMaxCapLimit, GetTotalDelegatedFn: spec.mockTotalDelegated}
ctx := sdk.Context{}
gotData, gotErr := ChainedCustomQuerier(mock, next).HandleQuery(ctx, myContractAddr, spec.src)

ctx, _ := pCtx.CacheContext()
gotData, gotErr := ChainedCustomQuerier(spec.viewKeeper, keepers.SlashingKeeper, next).HandleQuery(ctx, myContractAddr, spec.src)
if spec.expErr {
require.Error(t, gotErr)
return
}
require.NoError(t, gotErr)
assert.Equal(t, spec.expData, gotData)
assert.Equal(t, spec.expMocNextCalled, nextCalled)
assert.Equal(t, spec.expData, gotData, string(gotData))
assert.Equal(t, spec.expNextCalled, nextCalled)
})
}
}
Expand Down

0 comments on commit e753038

Please sign in to comment.