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

feat(nexus)!: lock/unlock tokens from/to amplifier in core #2188

Merged
Show file tree
Hide file tree
Changes from all 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: 2 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,8 @@ func initMessageRouter(keepers *KeeperCache) nexusTypes.MessageRouter {
if IsWasmEnabled() {
messageRouter.AddRoute(wasm.ModuleName, nexusKeeper.NewMessageRoute(
GetKeeper[nexusKeeper.Keeper](keepers),
GetKeeper[axelarnetKeeper.IBCKeeper](keepers),
GetKeeper[bankkeeper.BaseKeeper](keepers),
GetKeeper[authkeeper.AccountKeeper](keepers),
GetKeeper[wasmkeeper.PermissionedKeeper](keepers),
))
Expand Down
2 changes: 1 addition & 1 deletion app/keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ func initWasmKeeper(encodingConfig axelarParams.EncodingConfig, keys map[string]
encoders,
initMessageAnteDecorators(encodingConfig, keepers),
// for security reasons we disallow some msg types that can be used for arbitrary calls
wasmkeeper.NewMessageHandlerChain(NewMsgTypeBlacklistMessenger(), old, nexusKeeper.NewMessenger(nexusK)))
wasmkeeper.NewMessageHandlerChain(NewMsgTypeBlacklistMessenger(), old, nexusKeeper.NewMessenger(nexusK, GetKeeper[axelarnetKeeper.IBCKeeper](keepers), GetKeeper[bankkeeper.BaseKeeper](keepers), GetKeeper[authkeeper.AccountKeeper](keepers))))
}),
wasmkeeper.WithWasmEngineDecorator(func(old wasmtypes.WasmerEngine) wasmtypes.WasmerEngine {
return nexusKeeper.NewWasmerEngine(old, nexusK)
Expand Down
5 changes: 5 additions & 0 deletions x/nexus/exported/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@
SourceTxID: msg.SourceTxID,
SourceTxIndex: msg.SourceTxIndex,
ID: msg.ID,
Asset: msg.Asset,

Check warning on line 365 in x/nexus/exported/types.go

View check run for this annotation

Codecov / codecov/patch

x/nexus/exported/types.go#L365

Added line #L365 was not covered by tests
}
}

haiyizxx marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -398,6 +399,10 @@
return fmt.Errorf("invalid wasm message source tx id")
}

if m.Asset != nil {
return m.Asset.Validate()

Check warning on line 403 in x/nexus/exported/types.go

View check run for this annotation

Codecov / codecov/patch

x/nexus/exported/types.go#L402-L403

Added lines #L402 - L403 were not covered by tests
}

return nil
}

Expand Down
File renamed without changes.
31 changes: 27 additions & 4 deletions x/nexus/keeper/msg_dispatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@

type Messenger struct {
types.Nexus
ibc types.IBCKeeper
bank types.BankKeeper
account types.AccountKeeper
}

// NewMessenger returns a new Messenger
func NewMessenger(nexus types.Nexus) Messenger {
return Messenger{nexus}
func NewMessenger(nexus types.Nexus, ibc types.IBCKeeper, bank types.BankKeeper, account types.AccountKeeper) Messenger {
return Messenger{nexus, ibc, bank, account}
}

// DispatchMsg decodes the messages from the cosmowasm gateway and routes them to the nexus module if possible
Expand Down Expand Up @@ -73,8 +76,8 @@
return nil
}

if msg.Asset != nil && !m.IsAssetRegistered(ctx, destinationChain, msg.Asset.Denom) {
return fmt.Errorf("asset %s is not registered on chain %s", msg.Asset.Denom, destinationChain.Name)
if err := m.lockCoinIfAny(ctx, msg); err != nil {
return err
}

sourceChain := exported.Chain{Name: msg.SourceChain, SupportsForeignAssets: false, KeyType: tss.None, Module: wasmtypes.ModuleName}
Expand All @@ -94,6 +97,26 @@
return nil
}

func (m Messenger) lockCoinIfAny(ctx sdk.Context, msg exported.WasmMessage) error {
if msg.Asset == nil {
return nil
}

// destination chain existence is already checked in routeMsg
destinationChain := funcs.MustOk(m.GetChain(ctx, msg.DestinationChain))

if !m.IsAssetRegistered(ctx, destinationChain, msg.Asset.Denom) {
return fmt.Errorf("asset %s is not registered on chain %s", msg.Asset.Denom, destinationChain.Name)
}

lockableAsset, err := m.NewLockableAsset(ctx, m.ibc, m.bank, *msg.Asset)
if err != nil {
return err

Check warning on line 114 in x/nexus/keeper/msg_dispatcher.go

View check run for this annotation

Codecov / codecov/patch

x/nexus/keeper/msg_dispatcher.go#L114

Added line #L114 was not covered by tests
}

return lockableAsset.LockFrom(ctx, m.account.GetModuleAddress(types.ModuleName))
}

// EncodeRoutingMessage encodes the message from the wasm contract into a sdk.Msg
func EncodeRoutingMessage(sender sdk.AccAddress, msg json.RawMessage) ([]sdk.Msg, error) {
req, err := encodeRoutingMessage(sender, msg)
Expand Down
22 changes: 19 additions & 3 deletions x/nexus/keeper/msg_dispatcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
axelarnet "github.com/axelarnetwork/axelar-core/x/axelarnet/exported"
evm "github.com/axelarnetwork/axelar-core/x/evm/exported"
"github.com/axelarnetwork/axelar-core/x/nexus/exported"
exportedmock "github.com/axelarnetwork/axelar-core/x/nexus/exported/mock"
"github.com/axelarnetwork/axelar-core/x/nexus/keeper"
"github.com/axelarnetwork/axelar-core/x/nexus/types"
"github.com/axelarnetwork/axelar-core/x/nexus/types/mock"
Expand All @@ -28,6 +29,9 @@ func TestMessenger_DispatchMsg(t *testing.T) {
ctx sdk.Context
messenger keeper.Messenger
nexus *mock.NexusMock
ibc *mock.IBCKeeperMock
bank *mock.BankKeeperMock
account *mock.AccountKeeperMock
msg wasmvmtypes.CosmosMsg
)

Expand All @@ -39,8 +43,11 @@ func TestMessenger_DispatchMsg(t *testing.T) {
LoggerFunc: func(ctx sdk.Context) log.Logger { return ctx.Logger() },
IsWasmConnectionActivatedFunc: func(sdk.Context) bool { return true },
}
ibc = &mock.IBCKeeperMock{}
bank = &mock.BankKeeperMock{}
account = &mock.AccountKeeperMock{}

messenger = keeper.NewMessenger(nexus)
messenger = keeper.NewMessenger(nexus, ibc, bank, account)
})

givenMessenger.
Expand Down Expand Up @@ -133,7 +140,14 @@ func TestMessenger_DispatchMsg(t *testing.T) {
When("the asset is registered for the destination chain", func() {
nexus.IsAssetRegisteredFunc = func(_ sdk.Context, _ exported.Chain, _ string) bool { return true }
}).
Then("should set new message", func(t *testing.T) {
Then("should lock the coin and set new message", func(t *testing.T) {
moduleAccount := rand.AccAddr()
account.GetModuleAddressFunc = func(_ string) sdk.AccAddress { return moduleAccount }
lockableAsset := &exportedmock.LockableAssetMock{}
lockableAsset.LockFromFunc = func(ctx sdk.Context, fromAddr sdk.AccAddress) error { return nil }
nexus.NewLockableAssetFunc = func(ctx sdk.Context, ibc types.IBCKeeper, bank types.BankKeeper, coin sdk.Coin) (exported.LockableAsset, error) {
return lockableAsset, nil
}
nexus.SetNewMessageFunc = func(_ sdk.Context, msg exported.GeneralMessage) error {
return msg.ValidateBasic()
}
Expand All @@ -142,6 +156,8 @@ func TestMessenger_DispatchMsg(t *testing.T) {
_, _, err := messenger.DispatchMsg(ctx, contractAddr, "", msg)

assert.NoError(t, err)
assert.Len(t, lockableAsset.LockFromCalls(), 1)
assert.Equal(t, lockableAsset.LockFromCalls()[0].FromAddr, moduleAccount)
assert.Len(t, nexus.SetNewMessageCalls(), 1)
assert.Len(t, nexus.RouteMessageCalls(), 1)
assert.NotNil(t, nexus.SetNewMessageCalls()[0].Msg.Asset)
Expand Down Expand Up @@ -298,7 +314,7 @@ func TestMessenger_DispatchMsg_WasmConnectionNotActivated(t *testing.T) {
nexus = &mock.NexusMock{
LoggerFunc: func(ctx sdk.Context) log.Logger { return ctx.Logger() },
}
messenger = keeper.NewMessenger(nexus)
messenger = keeper.NewMessenger(nexus, &mock.IBCKeeperMock{}, &mock.BankKeeperMock{}, &mock.AccountKeeperMock{})
}).
When("wasm connection is not activated", func() {
nexus.IsWasmConnectionActivatedFunc = func(_ sdk.Context) bool { return false }
Expand Down
26 changes: 20 additions & 6 deletions x/nexus/keeper/wasm_message_route.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,8 @@
}

// NewMessageRoute creates a new message route
func NewMessageRoute(nexus types.Nexus, account types.AccountKeeper, wasm types.WasmKeeper) exported.MessageRoute {
func NewMessageRoute(nexus types.Nexus, ibc types.IBCKeeper, bank types.BankKeeper, account types.AccountKeeper, wasm types.WasmKeeper) exported.MessageRoute {
return func(ctx sdk.Context, _ exported.RoutingContext, msg exported.GeneralMessage) error {
if msg.Asset != nil {
return fmt.Errorf("asset transfer is not supported")
}

if !nexus.IsWasmConnectionActivated(ctx) {
return fmt.Errorf("wasm connection is not activated")
}
Expand All @@ -35,12 +31,17 @@
return err
}

coins, err := unlockCoinIfAny(ctx, nexus, ibc, bank, account, wasmMsg)
if err != nil {
return err

Check warning on line 36 in x/nexus/keeper/wasm_message_route.go

View check run for this annotation

Codecov / codecov/patch

x/nexus/keeper/wasm_message_route.go#L36

Added line #L36 was not covered by tests
}

bz, err := json.Marshal(request{RouteMessagesFromNexus: []exported.WasmMessage{wasmMsg}})
if err != nil {
return nil
}

if _, err := wasm.Execute(ctx, gateway, account.GetModuleAddress(types.ModuleName), bz, sdk.NewCoins()); err != nil {
if _, err := wasm.Execute(ctx, gateway, account.GetModuleAddress(types.ModuleName), bz, coins); err != nil {
return err
}

Expand All @@ -51,3 +52,16 @@
return nil
}
}

func unlockCoinIfAny(ctx sdk.Context, nexus types.Nexus, ibc types.IBCKeeper, bank types.BankKeeper, account types.AccountKeeper, msg exported.WasmMessage) (sdk.Coins, error) {
if msg.Asset == nil {
return sdk.NewCoins(), nil
}

lockableAsset, err := nexus.NewLockableAsset(ctx, ibc, bank, *msg.Asset)
if err != nil {
return sdk.NewCoins(), err

Check warning on line 63 in x/nexus/keeper/wasm_message_route.go

View check run for this annotation

Codecov / codecov/patch

x/nexus/keeper/wasm_message_route.go#L63

Added line #L63 was not covered by tests
}

return sdk.NewCoins(*msg.Asset), lockableAsset.UnlockTo(ctx, account.GetModuleAddress(types.ModuleName))
}
46 changes: 41 additions & 5 deletions x/nexus/keeper/wasm_message_route_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/axelarnetwork/axelar-core/testutils/fake"
"github.com/axelarnetwork/axelar-core/testutils/rand"
"github.com/axelarnetwork/axelar-core/x/nexus/exported"
exportedmock "github.com/axelarnetwork/axelar-core/x/nexus/exported/mock"
"github.com/axelarnetwork/axelar-core/x/nexus/exported/testutils"
"github.com/axelarnetwork/axelar-core/x/nexus/keeper"
"github.com/axelarnetwork/axelar-core/x/nexus/types"
Expand All @@ -31,6 +32,8 @@ func TestNewMessageRoute(t *testing.T) {
msg exported.GeneralMessage

nexusK *mock.NexusMock
ibcK *mock.IBCKeeperMock
bankK *mock.BankKeeperMock
accountK *mock.AccountKeeperMock
wasmK *mock.WasmKeeperMock
gateway sdk.AccAddress
Expand All @@ -41,10 +44,12 @@ func TestNewMessageRoute(t *testing.T) {

nexusK = &mock.NexusMock{}
nexusK.IsWasmConnectionActivatedFunc = func(_ sdk.Context) bool { return true }
ibcK = &mock.IBCKeeperMock{}
bankK = &mock.BankKeeperMock{}
accountK = &mock.AccountKeeperMock{}
wasmK = &mock.WasmKeeperMock{}

route = keeper.NewMessageRoute(nexusK, accountK, wasmK)
route = keeper.NewMessageRoute(nexusK, ibcK, bankK, accountK, wasmK)
})

givenMessageRoute.
Expand Down Expand Up @@ -72,8 +77,36 @@ func TestNewMessageRoute(t *testing.T) {
When("the message has an asset", func() {
msg = randMsg(exported.Processing, true)
}).
Then("should return error", func(t *testing.T) {
assert.ErrorContains(t, route(ctx, exported.RoutingContext{}, msg), "asset transfer is not supported")
Then("should execute the wasm message with token sent to the gateway", func(t *testing.T) {
moduleAddr := rand.AccAddr()
accountK.GetModuleAddressFunc = func(_ string) sdk.AccAddress { return moduleAddr }

lockableAsset := &exportedmock.LockableAssetMock{}
nexusK.NewLockableAssetFunc = func(ctx sdk.Context, ibc types.IBCKeeper, bank types.BankKeeper, coin sdk.Coin) (exported.LockableAsset, error) {
return lockableAsset, nil
}
lockableAsset.UnlockToFunc = func(ctx sdk.Context, toAddr sdk.AccAddress) error { return nil }

wasmK.ExecuteFunc = func(_ sdk.Context, _, _ sdk.AccAddress, _ []byte, _ sdk.Coins) ([]byte, error) {
return nil, nil
}

assert.NoError(t, route(ctx, exported.RoutingContext{}, msg))

assert.Len(t, lockableAsset.UnlockToCalls(), 1)
assert.Equal(t, moduleAddr, lockableAsset.UnlockToCalls()[0].ToAddr)

assert.Len(t, wasmK.ExecuteCalls(), 1)
assert.Equal(t, gateway, wasmK.ExecuteCalls()[0].ContractAddress)
assert.Equal(t, moduleAddr, wasmK.ExecuteCalls()[0].Caller)
assert.Equal(t, sdk.NewCoins(*msg.Asset), wasmK.ExecuteCalls()[0].Coins)

var actual req
assert.NoError(t, json.Unmarshal(wasmK.ExecuteCalls()[0].Msg, &actual))
assert.Len(t, actual.RouteMessages, 1)
assert.Equal(t, exported.FromGeneralMessage(msg), actual.RouteMessages[0])

assert.Equal(t, len(nexusK.SetMessageExecutedCalls()), 1)
}),

When("the message has no asset", func() {
Expand Down Expand Up @@ -110,6 +143,8 @@ func TestMessageRoute_WasmConnectionNotActivated(t *testing.T) {
ctx sdk.Context
route exported.MessageRoute
nexusK *mock.NexusMock
ibcK *mock.IBCKeeperMock
bankK *mock.BankKeeperMock
accountK *mock.AccountKeeperMock
wasmK *mock.WasmKeeperMock
)
Expand All @@ -126,13 +161,14 @@ func TestMessageRoute_WasmConnectionNotActivated(t *testing.T) {
return params
}
nexusK.SetMessageExecutedFunc = func(_ sdk.Context, _ string) error { return nil }

ibcK = &mock.IBCKeeperMock{}
bankK = &mock.BankKeeperMock{}
accountK = &mock.AccountKeeperMock{}
accountK.GetModuleAddressFunc = func(_ string) sdk.AccAddress { return rand.AccAddr() }
wasmK = &mock.WasmKeeperMock{}
wasmK.ExecuteFunc = func(_ sdk.Context, _, _ sdk.AccAddress, _ []byte, _ sdk.Coins) ([]byte, error) { return nil, nil }

route = keeper.NewMessageRoute(nexusK, accountK, wasmK)
route = keeper.NewMessageRoute(nexusK, ibcK, bankK, accountK, wasmK)
}).
When("the wasm connection is not activated", func() {
nexusK.IsWasmConnectionActivatedFunc = func(_ sdk.Context) bool { return false }
Expand Down
1 change: 1 addition & 0 deletions x/nexus/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type Nexus interface {
IsAssetRegistered(ctx sdk.Context, chain exported.Chain, denom string) bool
GetChainByNativeAsset(ctx sdk.Context, asset string) (chain exported.Chain, ok bool)
CurrID(ctx sdk.Context) ([32]byte, uint64)
NewLockableAsset(ctx sdk.Context, ibc IBCKeeper, bank BankKeeper, coin sdk.Coin) (exported.LockableAsset, error)
}

// MsgIDGenerator provides functionality to generate msg IDs
Expand Down
Loading
Loading