Skip to content

Commit

Permalink
fix: wasm binding integrations (#1333)
Browse files Browse the repository at this point in the history
# Related Github tickets

- VolumeFi#2531

# Background

This change addresses a bug in the way custom `wasm` messages are parsed
and routed to target modules.

# Testing completed

- [x] test coverage exists or has been added/updated
- [x] tested in a private testnet

# Breaking changes

- [x] I have checked my code for breaking changes
- [x] If there are breaking changes, there is a supporting migration.
  • Loading branch information
byte-bandit authored Jan 24, 2025
1 parent 92b9420 commit d74570d
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 102 deletions.
2 changes: 1 addition & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -1342,7 +1342,7 @@ func buildWasmMessageDecorator(
srv := schedulermodulekeeper.NewMsgServerImpl(scheduler)
skwSrv := skywaymodulekeeper.NewMsgServerImpl(*skyway)

return libwasm.NewMessenger(
return libwasm.NewRouterMessageDecorator(
log,
schedulerbindings.NewLegacyMessenger(scheduler),
schedulerbindings.NewMessenger(scheduler, srv),
Expand Down
39 changes: 23 additions & 16 deletions util/libwasm/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,25 @@ import (
wasmvmtypes "github.com/CosmWasm/wasmvm/v2/types"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/palomachain/paloma/v2/util/liberr"
"github.com/palomachain/paloma/v2/util/liblog"
schedulerbindings "github.com/palomachain/paloma/v2/x/scheduler/bindings/types"
skywaybindings "github.com/palomachain/paloma/v2/x/skyway/bindings/types"
tfbindings "github.com/palomachain/paloma/v2/x/tokenfactory/bindings/types"
)

type Messenger struct {
legacyFallback wasmkeeper.Messenger
scheduler wasmkeeper.Messenger
skyway wasmkeeper.Messenger
tokenfactory wasmkeeper.Messenger
wrapped wasmkeeper.Messenger
const ErrUnrecognizedMessage = liberr.Error("unrecognized message type")

type Messenger[T any] interface {
DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, t T) (events []sdk.Event, data [][]byte, msgResponses [][]*codectypes.Any, err error)
}

type router struct {
legacyFallback Messenger[wasmvmtypes.CosmosMsg]
scheduler Messenger[schedulerbindings.Message]
skyway Messenger[skywaybindings.Message]
tokenfactory Messenger[tfbindings.Message]
wrapped Messenger[wasmvmtypes.CosmosMsg]
log log.Logger
}

Expand All @@ -43,15 +50,15 @@ type CustomQuery struct {
TokenFactory *tfbindings.Query `json:"token_factory_query,omitempty"`
}

func NewMessenger(
func NewRouterMessageDecorator(
log log.Logger,
legacyFallback wasmkeeper.Messenger,
scheduler wasmkeeper.Messenger,
skyway wasmkeeper.Messenger,
tokenfactory wasmkeeper.Messenger,
legacyFallback Messenger[wasmvmtypes.CosmosMsg],
scheduler Messenger[schedulerbindings.Message],
skyway Messenger[skywaybindings.Message],
tokenfactory Messenger[tfbindings.Message],
) func(old wasmkeeper.Messenger) wasmkeeper.Messenger {
return func(old wasmkeeper.Messenger) wasmkeeper.Messenger {
return &Messenger{
return &router{
log: log,
legacyFallback: legacyFallback,
scheduler: scheduler,
Expand All @@ -62,7 +69,7 @@ func NewMessenger(
}
}

func (h Messenger) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (a []sdk.Event, b [][]byte, c [][]*codectypes.Any, err error) {
func (h router) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (a []sdk.Event, b [][]byte, c [][]*codectypes.Any, err error) {
logger := liblog.FromSDKLogger(h.log).WithComponent("wasm-message-decorator")
logger.Debug("Dispatching message...", "contract", contractAddr, "msg", msg)

Expand All @@ -86,11 +93,11 @@ func (h Messenger) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, con
}
switch {
case contractMsg.Scheduler != nil:
return h.scheduler.DispatchMsg(ctx, contractAddr, contractIBCPortID, msg)
return h.scheduler.DispatchMsg(ctx, contractAddr, contractIBCPortID, *contractMsg.Scheduler)
case contractMsg.Skyway != nil:
return h.skyway.DispatchMsg(ctx, contractAddr, contractIBCPortID, msg)
return h.skyway.DispatchMsg(ctx, contractAddr, contractIBCPortID, *contractMsg.Skyway)
case contractMsg.TokenFactory != nil:
return h.tokenfactory.DispatchMsg(ctx, contractAddr, contractIBCPortID, msg)
return h.tokenfactory.DispatchMsg(ctx, contractAddr, contractIBCPortID, *contractMsg.TokenFactory)
}

logger.Debug("Calling legacy fallback message handler...")
Expand Down
17 changes: 8 additions & 9 deletions util/libwasm/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,28 @@ import (
)

// MockMessenger is a mock implementation of wasmkeeper.Messenger
type MockMessenger struct {
type MockMessenger[T any] struct {
mock.Mock
}

func (m *MockMessenger) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) ([]sdk.Event, [][]byte, [][]*codectypes.Any, error) {
func (m *MockMessenger[T]) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg T) ([]sdk.Event, [][]byte, [][]*codectypes.Any, error) {
args := m.Called(ctx, contractAddr, contractIBCPortID, msg)
return args.Get(0).([]sdk.Event), args.Get(1).([][]byte), args.Get(2).([][]*codectypes.Any), args.Error(3)
}

func TestDispatchMsg(t *testing.T) {
// Setup
ctx := sdk.Context{}
contractAddr := sdk.AccAddress([]byte("test_address"))
contractIBCPortID := "test_port"

logger := log.NewNopLogger()
mockScheduler := new(MockMessenger)
mockSkyway := new(MockMessenger)
mockTokenFactory := new(MockMessenger)
mockLegacyFallback := new(MockMessenger)
mockWrapped := new(MockMessenger)
mockScheduler := new(MockMessenger[schedulerbindings.Message])
mockSkyway := new(MockMessenger[skywaybindings.Message])
mockTokenFactory := new(MockMessenger[tfbindings.Message])
mockLegacyFallback := new(MockMessenger[wasmvmtypes.CosmosMsg])
mockWrapped := new(MockMessenger[wasmvmtypes.CosmosMsg])

h := Messenger{
h := router{
log: logger,
legacyFallback: mockLegacyFallback,
scheduler: mockScheduler,
Expand Down
37 changes: 19 additions & 18 deletions x/scheduler/bindings/msg_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ package bindings

import (
"context"
"encoding/json"

sdkerrors "cosmossdk.io/errors"
wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
wasmvmtypes "github.com/CosmWasm/wasmvm/v2/types"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/palomachain/paloma/v2/util/libwasm"
bindingstypes "github.com/palomachain/paloma/v2/x/scheduler/bindings/types"
schedulertypes "github.com/palomachain/paloma/v2/x/scheduler/types"
)
Expand All @@ -17,7 +16,10 @@ type SchedulerMsgServer interface {
CreateJob(context.Context, *schedulertypes.MsgCreateJob) (*schedulertypes.MsgCreateJobResponse, error)
}

func NewMessenger(k Schedulerkeeper, ms SchedulerMsgServer) wasmkeeper.Messenger {
func NewMessenger(
k Schedulerkeeper,
ms SchedulerMsgServer,
) libwasm.Messenger[bindingstypes.Message] {
return &customMessenger{
ms: ms,
k: k,
Expand All @@ -29,23 +31,22 @@ type customMessenger struct {
k Schedulerkeeper
}

var _ wasmkeeper.Messenger = (*customMessenger)(nil)

func (m *customMessenger) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, _ string, msg wasmvmtypes.CosmosMsg) ([]sdk.Event, [][]byte, [][]*codectypes.Any, error) {
if msg.Custom == nil {
var contractMsg bindingstypes.Message
if err := json.Unmarshal(msg.Custom, &contractMsg); err != nil {
return nil, nil, nil, sdkerrors.Wrap(err, "scheduler msg")
}
switch {
case contractMsg.CreateJob != nil:
return m.createJob(ctx, contractAddr, contractMsg.CreateJob)
case contractMsg.ExecuteJob != nil:
return m.executeJob(ctx, contractAddr, contractMsg.ExecuteJob)
}
var _ libwasm.Messenger[bindingstypes.Message] = (*customMessenger)(nil)

func (m *customMessenger) DispatchMsg(
ctx sdk.Context,
contractAddr sdk.AccAddress,
_ string,
contractMsg bindingstypes.Message,
) ([]sdk.Event, [][]byte, [][]*codectypes.Any, error) {
switch {
case contractMsg.CreateJob != nil:
return m.createJob(ctx, contractAddr, contractMsg.CreateJob)
case contractMsg.ExecuteJob != nil:
return m.executeJob(ctx, contractAddr, contractMsg.ExecuteJob)
}

return nil, nil, nil, nil
return nil, nil, nil, libwasm.ErrUnrecognizedMessage
}

func (m *customMessenger) createJob(ctx sdk.Context, contractAddr sdk.AccAddress, createJob *bindingstypes.CreateJob) ([]sdk.Event, [][]byte, [][]*codectypes.Any, error) {
Expand Down
67 changes: 33 additions & 34 deletions x/skyway/bindings/msg_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ package bindings

import (
"context"
"encoding/json"

sdkerrors "cosmossdk.io/errors"
wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
wasmvmtypes "github.com/CosmWasm/wasmvm/v2/types"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/palomachain/paloma/v2/util/libwasm"
bindingstypes "github.com/palomachain/paloma/v2/x/skyway/bindings/types"
skywaytypes "github.com/palomachain/paloma/v2/x/skyway/types"
"google.golang.org/protobuf/types/known/emptypb"
Expand All @@ -24,84 +22,85 @@ type customMessenger struct {
k SkywayMsgServer
}

var _ wasmkeeper.Messenger = (*customMessenger)(nil)
var _ libwasm.Messenger[bindingstypes.Message] = (*customMessenger)(nil)

func NewMessenger(k SkywayMsgServer) wasmkeeper.Messenger {
func NewMessenger(k SkywayMsgServer) libwasm.Messenger[bindingstypes.Message] {
return &customMessenger{
k: k,
}
}

func (m *customMessenger) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) ([]sdk.Event, [][]byte, [][]*codectypes.Any, error) {
if msg.Custom != nil {
var contractMsg bindingstypes.Message
if err := json.Unmarshal(msg.Custom, &contractMsg); err != nil {
return nil, nil, nil, sdkerrors.Wrap(err, "skyway msg")
}

switch {
case contractMsg.SetErc20ToDenom != nil:
case contractMsg.SendTx != nil:
case contractMsg.CancelTx != nil:
return nil, nil, nil, nil
}
func (m *customMessenger) DispatchMsg(
ctx sdk.Context,
contractAddr sdk.AccAddress,
contractIBCPortID string,
contractMsg bindingstypes.Message,
) ([]sdk.Event, [][]byte, [][]*codectypes.Any, error) {
switch {
case contractMsg.SetErc20ToDenom != nil:
return handleSetErc20ToDenom(ctx, m.k, contractAddr, contractMsg.SetErc20ToDenom)
case contractMsg.SendTx != nil:
return sendTx(ctx, m.k, contractAddr, contractMsg.SendTx)
case contractMsg.CancelTx != nil:
return cancelTx(ctx, m.k, contractAddr, contractMsg.CancelTx)
}
return nil, nil, nil, nil

return nil, nil, nil, libwasm.ErrUnrecognizedMessage
}

func sendTx(ctx sdk.Context, k SkywayMsgServer, sender sdk.AccAddress, msg bindingstypes.SendTx) error {
func sendTx(ctx sdk.Context, k SkywayMsgServer, sender sdk.AccAddress, msg *bindingstypes.SendTx) ([]sdk.Event, [][]byte, [][]*codectypes.Any, error) {
if err := msg.ValidateBasic(); err != nil {
return sdkerrors.Wrap(err, "validation failed")
return nil, nil, nil, sdkerrors.Wrap(err, "validation failed")
}

dest, err := skywaytypes.NewEthAddress(msg.RemoteChainDestinationAddress)
if err != nil {
return sdkerrors.Wrap(err, "invalid eth address")
return nil, nil, nil, sdkerrors.Wrap(err, "invalid eth address")
}

amount, err := sdk.ParseCoinsNormalized(msg.Amount)
if err != nil {
return sdkerrors.Wrap(err, "amount")
return nil, nil, nil, sdkerrors.Wrap(err, "amount")
}

req := skywaytypes.NewMsgSendToRemote(sender, *dest, amount[0], msg.ChainReferenceId)
_, err = k.SendToRemote(ctx, req)
if err != nil {
return sdkerrors.Wrap(err, "failed to dispatch message")
return nil, nil, nil, sdkerrors.Wrap(err, "failed to dispatch message")
}

return nil
return nil, nil, nil, nil
}

func cancelTx(ctx sdk.Context, k SkywayMsgServer, sender sdk.AccAddress, msg bindingstypes.CancelTx) error {
func cancelTx(ctx sdk.Context, k SkywayMsgServer, sender sdk.AccAddress, msg *bindingstypes.CancelTx) ([]sdk.Event, [][]byte, [][]*codectypes.Any, error) {
if err := msg.ValidateBasic(); err != nil {
return sdkerrors.Wrap(err, "validation failed")
return nil, nil, nil, sdkerrors.Wrap(err, "validation failed")
}

req := skywaytypes.NewMsgCancelSendToRemote(sender, msg.TransactionId)
_, err := k.CancelSendToRemote(ctx, req)
if err != nil {
return sdkerrors.Wrap(err, "failed to dispatch message")
return nil, nil, nil, sdkerrors.Wrap(err, "failed to dispatch message")
}

return nil
return nil, nil, nil, nil
}

func handleSetErc20ToDenom(ctx sdk.Context, k SkywayMsgServer, sender sdk.AccAddress, msg bindingstypes.SetErc20ToDenom) error {
func handleSetErc20ToDenom(ctx sdk.Context, k SkywayMsgServer, sender sdk.AccAddress, msg *bindingstypes.SetErc20ToDenom) ([]sdk.Event, [][]byte, [][]*codectypes.Any, error) {
if err := msg.ValidateBasic(); err != nil {
return sdkerrors.Wrap(err, "validation failed")
return nil, nil, nil, sdkerrors.Wrap(err, "validation failed")
}

erc20, err := skywaytypes.NewEthAddress(msg.Erc20Address)
if err != nil {
return sdkerrors.Wrap(err, "invalid eth address")
return nil, nil, nil, sdkerrors.Wrap(err, "invalid eth address")
}

req := skywaytypes.NewMsgSetERC20ToTokenDenom(sender, *erc20, msg.ChainReferenceId, msg.TokenDenom)
_, err = k.SetERC20ToTokenDenom(ctx, req)
if err != nil {
return sdkerrors.Wrap(err, "failed to dispatch message")
return nil, nil, nil, sdkerrors.Wrap(err, "failed to dispatch message")
}

return nil
return nil, nil, nil, nil
}
41 changes: 17 additions & 24 deletions x/tokenfactory/bindings/msg_plugin.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
package bindings

import (
"encoding/json"

sdkerrors "cosmossdk.io/errors"
wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
wasmvmtypes "github.com/CosmWasm/wasmvm/v2/types"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/palomachain/paloma/v2/util/libwasm"
bindingstypes "github.com/palomachain/paloma/v2/x/tokenfactory/bindings/types"
tokenfactorykeeper "github.com/palomachain/paloma/v2/x/tokenfactory/keeper"
tokenfactorytypes "github.com/palomachain/paloma/v2/x/tokenfactory/types"
)

func NewMessenger(bank *bankkeeper.BaseKeeper, tokenFactory *tokenfactorykeeper.Keeper) wasmkeeper.Messenger {
func NewMessenger(bank *bankkeeper.BaseKeeper, tokenFactory *tokenfactorykeeper.Keeper) libwasm.Messenger[bindingstypes.Message] {
return &customMessenger{
bank: bank,
tokenFactory: tokenFactory,
Expand All @@ -27,28 +25,23 @@ type customMessenger struct {
tokenFactory *tokenfactorykeeper.Keeper
}

var _ wasmkeeper.Messenger = (*customMessenger)(nil)
var _ libwasm.Messenger[bindingstypes.Message] = (*customMessenger)(nil)

func (m *customMessenger) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) ([]sdk.Event, [][]byte, [][]*codectypes.Any, error) {
if msg.Custom != nil {
var contractMsg bindingstypes.Message
if err := json.Unmarshal(msg.Custom, &contractMsg); err != nil {
return nil, nil, nil, sdkerrors.Wrap(err, "token factory msg")
}
switch {
case contractMsg.CreateDenom != nil:
return m.createDenom(ctx, contractAddr, contractMsg.CreateDenom)
case contractMsg.MintTokens != nil:
return m.mintTokens(ctx, contractAddr, contractMsg.MintTokens)
case contractMsg.ChangeAdmin != nil:
return m.changeAdmin(ctx, contractAddr, contractMsg.ChangeAdmin)
case contractMsg.BurnTokens != nil:
return m.burnTokens(ctx, contractAddr, contractMsg.BurnTokens)
case contractMsg.SetMetadata != nil:
return m.setMetadata(ctx, contractAddr, contractMsg.SetMetadata)
}
func (m *customMessenger) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, contractMsg bindingstypes.Message) ([]sdk.Event, [][]byte, [][]*codectypes.Any, error) {
switch {
case contractMsg.CreateDenom != nil:
return m.createDenom(ctx, contractAddr, contractMsg.CreateDenom)
case contractMsg.MintTokens != nil:
return m.mintTokens(ctx, contractAddr, contractMsg.MintTokens)
case contractMsg.ChangeAdmin != nil:
return m.changeAdmin(ctx, contractAddr, contractMsg.ChangeAdmin)
case contractMsg.BurnTokens != nil:
return m.burnTokens(ctx, contractAddr, contractMsg.BurnTokens)
case contractMsg.SetMetadata != nil:
return m.setMetadata(ctx, contractAddr, contractMsg.SetMetadata)
}
return nil, nil, nil, nil

return nil, nil, nil, libwasm.ErrUnrecognizedMessage
}

func (m *customMessenger) createDenom(ctx sdk.Context, contractAddr sdk.AccAddress, createDenom *bindingstypes.CreateDenom) ([]sdk.Event, [][]byte, [][]*codectypes.Any, error) {
Expand Down

0 comments on commit d74570d

Please sign in to comment.