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

Forward slashing events to the contract for processing #187

Merged
merged 10 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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 demo/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.20

require (
github.com/CosmWasm/wasmd v0.45.0
github.com/CosmWasm/wasmvm v1.5.0 // indirect
github.com/CosmWasm/wasmvm v1.5.0
github.com/cosmos/cosmos-proto v1.0.0-beta.2 // indirect
github.com/cosmos/cosmos-sdk v0.47.5
github.com/cosmos/gogogateway v1.2.0 // indirect
Expand Down
39 changes: 39 additions & 0 deletions demo/wasmbinding/message_plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package contract

import (
"encoding/json"

wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"

errorsmod "cosmossdk.io/errors"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
sdk "github.com/cosmos/cosmos-sdk/types"
consumermsg "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity/contract"
)

type ConsumerKeeper interface {
HandleBondMsg(ctx sdk.Context, actor sdk.AccAddress, bondMsg *consumermsg.BondMsg) ([]sdk.Event, [][]byte, error)
HandleUnbondMsg(ctx sdk.Context, actor sdk.AccAddress, unbondMsg *consumermsg.UnbondMsg) ([]sdk.Event, [][]byte, error)
}

type CustomMessenger struct {
consKeeper ConsumerKeeper
}

// DispatchMsg executes on the contractMsg.
func (h CustomMessenger) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, _ string, msg wasmvmtypes.CosmosMsg) ([]sdk.Event, [][]byte, error) {
if msg.Custom != nil {
var contractMsg CustomMsg
if err := json.Unmarshal(msg.Custom, &contractMsg); err != nil {
return nil, nil, errorsmod.Wrap(err, "mesh security msg")
}

if contractMsg.Bond != nil {
return h.consKeeper.HandleBondMsg(ctx, contractAddr, contractMsg.Bond)
}
if contractMsg.Unbond != nil {
return h.consKeeper.HandleUnbondMsg(ctx, contractAddr, contractMsg.Unbond)
}
}
return nil, nil, wasmtypes.ErrUnknownMsg
}
12 changes: 12 additions & 0 deletions demo/wasmbinding/msg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package contract

import (
consumermsg "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity/contract"
)

type (
CustomMsg struct {
Bond *consumermsg.BondMsg `json:"bond,omitempty"`
Unbond *consumermsg.UnbondMsg `json:"unbond,omitempty"`
}
)
1 change: 0 additions & 1 deletion x/meshsecurityprovider/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ func GetQueryCmd() *cobra.Command {
return meshsecurityproviderQueryCmd
}


// GetCmdQueryParams implements a command to return the current parameters.
func GetCmdQueryParams() *cobra.Command {
cmd := &cobra.Command{
Expand Down
2 changes: 1 addition & 1 deletion x/meshsecurityprovider/contract/in_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ type (
Amount wasmvmtypes.Coin `json:"amount"`
Delegator string `json:"delegator"`
}
)
)
14 changes: 14 additions & 0 deletions x/meshsecurityprovider/contract/out_message.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package contract

type (
SudoMsg struct {
Jailing *ValidatorSlash `json:"jailing,omitempty"`
}
// ValidatorAddr alias for the Bech32 address string of sdk.ValAddress
ValidatorAddr = string

ValidatorSlash struct {
Jailed []ValidatorAddr `json:"jailed"`
Tombstoned []ValidatorAddr `json:"tombstoned"`
}
)
143 changes: 143 additions & 0 deletions x/meshsecurityprovider/keeper/adapter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package keeper

import (
"cosmossdk.io/math"

sdk "github.com/cosmos/cosmos-sdk/types"
slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"

meshsecuritykeeper "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity/keeper"
"github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity/types"
)

var _ types.XStakingKeeper = &StakingKeeperAdapter{}

// StakingKeeperAdapter is an adapter to enhance the vanilla sdk staking keeper with additional functionality
// required for MS. The methods match Osmosis SDK fork.
type StakingKeeperAdapter struct {
types.SDKStakingKeeper
bank types.SDKBankKeeper
}

// NewStakingKeeperAdapter constructor
func NewStakingKeeperAdapter(k types.SDKStakingKeeper, b types.SDKBankKeeper) *StakingKeeperAdapter {
return &StakingKeeperAdapter{SDKStakingKeeper: k, bank: b}
}

// InstantUndelegate allows another module account to undelegate while bypassing unbonding time.
// This function is a combination of Undelegate and CompleteUnbonding,
// but skips the creation and deletion of UnbondingDelegationEntry
//
// The code is copied from the Osmosis SDK fork https://github.com/osmosis-labs/cosmos-sdk/blob/v0.45.0x-osmo-v9.3/x/staking/keeper/delegation.go#L757
func (s StakingKeeperAdapter) InstantUndelegate(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec) (sdk.Coins, error) {
validator, found := s.GetValidator(ctx, valAddr)
if !found {
return nil, stakingtypes.ErrNoDelegatorForAddress
}

returnAmount, err := s.Unbond(ctx, delAddr, valAddr, sharesAmount)
if err != nil {
return nil, err
}

bondDenom := s.BondDenom(ctx)

amt := sdk.NewCoin(bondDenom, returnAmount)
res := sdk.NewCoins(amt)

moduleName := stakingtypes.NotBondedPoolName
if validator.IsBonded() {
moduleName = stakingtypes.BondedPoolName
}
err = s.bank.UndelegateCoinsFromModuleToAccount(ctx, moduleName, delAddr, res)
if err != nil {
return nil, err
}
return res, nil
}

// StakingDecorator decorate vanilla staking keeper to capture the jail and unjail events
type StakingDecorator struct {
slashingtypes.StakingKeeper
k *Keeper
}

// NewStakingDecorator constructor
func NewStakingDecorator(stakingKeeper slashingtypes.StakingKeeper, k *Keeper) *StakingDecorator {
return &StakingDecorator{StakingKeeper: stakingKeeper, k: k}
}

// Slash captures the slash event and calls the decorated staking keeper slash method
func (s StakingDecorator) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, power int64, height int64, slashRatio sdk.Dec) math.Int {
if s.k.meshConsumer == nil {
return s.StakingKeeper.Slash(ctx, consAddr, power, height, slashRatio)
}

val := s.StakingKeeper.ValidatorByConsAddr(ctx, consAddr)
totalSlashAmount := s.StakingKeeper.Slash(ctx, consAddr, power, height, slashRatio)
if val == nil {
meshsecuritykeeper.ModuleLogger(ctx).
Error("can not propagate slash: validator not found", "validator", consAddr.String())
} else if err := s.k.meshConsumer.ScheduleSlashed(ctx, val.GetOperator(), power, height, totalSlashAmount, slashRatio); err != nil {
meshsecuritykeeper.ModuleLogger(ctx).
Error("can not propagate slash: schedule event",
"cause", err,
"validator", consAddr.String())
}
return totalSlashAmount
}

// SlashWithInfractionReason implementation doesn't require the infraction (types.Infraction) to work but is required by Interchain Security.
func (s StakingDecorator) SlashWithInfractionReason(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeight int64, power int64, slashFactor sdk.Dec, infraction stakingtypes.Infraction) math.Int {
// foward it to native-staking contract
params := s.k.GetParams(ctx)
nativeStakingAddr := sdk.MustAccAddressFromBech32(params.NativeStakingAddress)

if infraction == stakingtypes.Infraction_INFRACTION_DOUBLE_SIGN {
s.k.SendJailHandlingMsg(ctx, nativeStakingAddr, nil, []string{consAddr.String()})
}

if infraction == stakingtypes.Infraction_INFRACTION_DOWNTIME {
s.k.SendJailHandlingMsg(ctx, nativeStakingAddr, []string{consAddr.String()}, nil)
}
return s.Slash(ctx, consAddr, infractionHeight, power, slashFactor)
}

// Jail captures the jail event and calls the decorated staking keeper jail method
func (s StakingDecorator) Jail(ctx sdk.Context, consAddr sdk.ConsAddress) {
if s.k.meshConsumer == nil {
s.StakingKeeper.Jail(ctx, consAddr)
}

val := s.StakingKeeper.ValidatorByConsAddr(ctx, consAddr)
if val == nil {
meshsecuritykeeper.ModuleLogger(ctx).
Error("can not propagate jail: validator not found", "validator", consAddr.String())
} else if err := s.k.meshConsumer.ScheduleJailed(ctx, val.GetOperator()); err != nil {
meshsecuritykeeper.ModuleLogger(ctx).
Error("can not propagate jail: schedule event",
"cause", err,
"validator", consAddr.String())
}
s.StakingKeeper.Jail(ctx, consAddr)
}

// Unjail captures the unjail event and calls the decorated staking keeper unjail method
func (s StakingDecorator) Unjail(ctx sdk.Context, consAddr sdk.ConsAddress) {
if s.k.meshConsumer == nil {
s.StakingKeeper.Unjail(ctx, consAddr)
}

val := s.StakingKeeper.ValidatorByConsAddr(ctx, consAddr)
if val == nil {
meshsecuritykeeper.ModuleLogger(ctx).
Error("can not propagate unjail: validator not found", "validator", consAddr.String())
} else if err := s.k.meshConsumer.ScheduleUnjailed(ctx, val.GetOperator()); err != nil {
meshsecuritykeeper.ModuleLogger(ctx).
Error("can not propagate unjail: schedule event",
"cause", err,
"validator", consAddr.String())
}
s.StakingKeeper.Unjail(ctx, consAddr)
}
1 change: 1 addition & 0 deletions x/meshsecurityprovider/keeper/handler_plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package keeper
1 change: 1 addition & 0 deletions x/meshsecurityprovider/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Keeper struct {
bankKeeper types.BankKeeper
wasmKeeper types.WasmKeeper
stakingKeeper types.StakingKeeper
meshConsumer types.MeshSecurityConsumer
}

func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey,
Expand Down
2 changes: 1 addition & 1 deletion x/meshsecurityprovider/keeper/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ func (k Keeper) VaultAddress(ctx sdk.Context) string {
// NativeStakingAddress - Address of native staking contract
func (k Keeper) NativeStakingAddress(ctx sdk.Context) string {
return k.GetParams(ctx).NativeStakingAddress
}
}
1 change: 1 addition & 0 deletions x/meshsecurityprovider/keeper/slash.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package keeper
31 changes: 31 additions & 0 deletions x/meshsecurityprovider/keeper/wasm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package keeper

import (
"encoding/json"

errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/osmosis-labs/mesh-security-sdk/x/meshsecurityprovider/contract"
)

// caller must ensure gas limits are set proper and handle panics
func (k Keeper) doSudoCall(ctx sdk.Context, contractAddr sdk.AccAddress, msg contract.SudoMsg) error {
bz, err := json.Marshal(msg)
if err != nil {
return errorsmod.Wrap(err, "marshal sudo msg")
}
_, err = k.wasmKeeper.Sudo(ctx, contractAddr, bz)
return err
}

// SendJailHandlingMsg send jail handling message to contract via sudo
func (k Keeper) SendJailHandlingMsg(ctx sdk.Context, contractAddr sdk.AccAddress, jailed []contract.ValidatorAddr, tombstoned []contract.ValidatorAddr) error {
msg := contract.SudoMsg{
Jailing: &contract.ValidatorSlash{
Jailed: jailed,
Tombstoned: tombstoned,
},
}
return k.doSudoCall(ctx, contractAddr, msg)
}
10 changes: 10 additions & 0 deletions x/meshsecurityprovider/types/expected_keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ type BankKeeper interface {
}

type WasmKeeper interface {
Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte) ([]byte, error)
HasContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) bool
QuerySmart(ctx sdk.Context, contractAddress sdk.AccAddress, queryMsg []byte) ([]byte, error)
}

Expand All @@ -27,3 +29,11 @@ type StakingKeeper interface {
Unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, shares sdk.Dec) (amount math.Int, err error)
Undelegate(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec) (time.Time, error)
}

type MeshSecurityConsumer interface {
ScheduleUnjailed(ctx sdk.Context, addr sdk.ValAddress) error
ScheduleJailed(ctx sdk.Context, addr sdk.ValAddress) error
ScheduleTombstoned(ctx sdk.Context, addr sdk.ValAddress) error
ScheduleModified(ctx sdk.Context, addr sdk.ValAddress) error
ScheduleSlashed(ctx sdk.Context, addr sdk.ValAddress, power int64, height int64, totalSlashAmount math.Int, slashRatio sdk.Dec) error
}
Loading