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

Add async support to wasm hooks #5072

Merged
merged 37 commits into from
Aug 7, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
dc47387
added initial async ack support for wasm hooks
nicolaslara May 3, 2023
45833e8
added initial working implementation of async acks
nicolaslara May 5, 2023
447d8f1
removed debug prints
nicolaslara May 5, 2023
b872a7d
cleanner acks
nicolaslara May 26, 2023
62c6097
Merge branch 'main' into nicolas/async-hooks
nicolaslara May 26, 2023
cd1e9ea
Update x/ibc-hooks/README.md
nicolaslara May 31, 2023
67b59a4
added use case to readme
nicolaslara May 31, 2023
07446e7
clippy
nicolaslara May 31, 2023
415a40c
clippy
nicolaslara May 31, 2023
fa9d044
updated hooks to latest
nicolaslara May 31, 2023
25cf51a
added changelog
nicolaslara May 31, 2023
9be127c
updated deps
nicolaslara May 31, 2023
d3f7b1e
added proto fix and todo
nicolaslara May 31, 2023
5aebc19
added initial implementation of ack errors
nicolaslara Jun 2, 2023
338a15b
Merge branch 'main' into nicolas/async-hooks
nicolaslara Jun 7, 2023
80e698f
make protos
nicolaslara Jun 7, 2023
ab35895
better readme
nicolaslara Jun 7, 2023
bfc1da3
cleanup rust structs
nicolaslara Jun 30, 2023
e19b66d
initial params
nicolaslara Jun 30, 2023
17a2daa
fixed test
nicolaslara Jun 30, 2023
fbb90dc
updated async acks based on feedback. Safer this way
nicolaslara Jul 4, 2023
ef9bba1
add error ack test
nicolaslara Jul 6, 2023
eea8083
Merge branch 'main' into nicolas/async-hooks
nicolaslara Jul 7, 2023
b6208c3
added missing types
nicolaslara Jul 7, 2023
0ef4921
updated ibc-hooks
nicolaslara Jul 17, 2023
1c7208d
tidy
nicolaslara Jul 17, 2023
f056cfc
updated osmoutils
nicolaslara Jul 17, 2023
c28fbec
Merge branch 'main' into nicolas/async-hooks
nicolaslara Jul 17, 2023
36ab0d6
updated osmoutils and ibc-hooks after merge
nicolaslara Jul 17, 2023
e3b3537
Merge branch 'main' into nicolas/async-hooks
czarcas7ic Aug 7, 2023
325906e
merge main
czarcas7ic Aug 7, 2023
01fa42d
regen proto
czarcas7ic Aug 7, 2023
0ae9d16
update osmoutils
czarcas7ic Aug 7, 2023
16c217d
update ibc hooks
czarcas7ic Aug 7, 2023
bd56132
run go get
czarcas7ic Aug 7, 2023
8b472a9
Merge branch 'main' into nicolas/async-hooks
nicolaslara Aug 7, 2023
8f54f66
rl bytecode
nicolaslara Aug 7, 2023
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
3 changes: 3 additions & 0 deletions app/keepers/keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ func (appKeepers *AppKeepers) InitNormalKeepers(
// Configure the hooks keeper
hooksKeeper := ibchookskeeper.NewKeeper(
appKeepers.keys[ibchookstypes.StoreKey],
appKeepers.IBCKeeper.ChannelKeeper,
nil,
)
appKeepers.IBCHooksKeeper = &hooksKeeper

Expand Down Expand Up @@ -456,6 +458,7 @@ func (appKeepers *AppKeepers) InitNormalKeepers(
appKeepers.ContractKeeper = wasmkeeper.NewDefaultPermissionKeeper(appKeepers.WasmKeeper)
appKeepers.RateLimitingICS4Wrapper.ContractKeeper = appKeepers.ContractKeeper
appKeepers.Ics20WasmHooks.ContractKeeper = appKeepers.ContractKeeper
appKeepers.IBCHooksKeeper.ContractKeeper = appKeepers.ContractKeeper

// wire up x/wasm to IBC
ibcRouter.AddRoute(wasm.ModuleName, wasm.NewIBCHandler(appKeepers.WasmKeeper, appKeepers.IBCKeeper.ChannelKeeper, appKeepers.IBCKeeper.ChannelKeeper))
Expand Down
2 changes: 1 addition & 1 deletion app/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ func appModules(
tokenfactory.NewAppModule(*app.TokenFactoryKeeper, app.AccountKeeper, app.BankKeeper),
valsetprefmodule.NewAppModule(appCodec, *app.ValidatorSetPreferenceKeeper),
ibcratelimitmodule.NewAppModule(*app.RateLimitingICS4Wrapper),
ibc_hooks.NewAppModule(app.AccountKeeper),
ibc_hooks.NewAppModule(app.AccountKeeper, *app.IBCHooksKeeper),
icq.NewAppModule(*app.AppKeepers.ICQKeeper),
packetforward.NewAppModule(app.PacketForwardKeeper),
}
Expand Down
2 changes: 1 addition & 1 deletion cosmwasm/contracts/crosschain-swaps/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use swaprouter::msg::ExecuteMsg as SwapRouterExecute;

use crate::checks::{check_is_contract_governor, ensure_key_missing, validate_receiver};
use crate::consts::{MsgReplyID, CALLBACK_KEY};
use crate::msg::{CrosschainSwapResponse, FailedDeliveryAction};
use crate::msg::{AsyncResponse, CrosschainSwapResponse, FailedDeliveryAction};
use registry::proto::MsgTransferResponse;

use crate::state::{
Expand Down
7 changes: 6 additions & 1 deletion osmoutils/ibc.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package osmoutils

import (
"encoding/json"
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
transfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
Expand All @@ -11,15 +12,19 @@ import (
// NewEmitErrorAcknowledgement creates a new error acknowledgement after having emitted an event with the
// details of the error.
func NewEmitErrorAcknowledgement(ctx sdk.Context, err error, errorContexts ...string) channeltypes.Acknowledgement {
errorType := "ibc-acknowledgement-error"
logger := ctx.Logger().With("module", errorType)

attributes := make([]sdk.Attribute, len(errorContexts)+1)
attributes[0] = sdk.NewAttribute("error", err.Error())
for i, s := range errorContexts {
attributes[i+1] = sdk.NewAttribute("error-context", s)
logger.Error(fmt.Sprintf("error-context: %v", s))
}

ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
"ibc-acknowledgement-error",
errorType,
attributes...,
),
})
Expand Down
25 changes: 25 additions & 0 deletions proto/osmosis/ibc-hooks/tx.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
syntax = "proto3";
package osmosis.ibchooks;

import "gogoproto/gogo.proto";

option go_package = "github.com/osmosis-labs/osmosis/v15/x/ibc-hooks/types";

// Msg defines the Msg service.
service Msg {
// EmitIBCAck checks the sender can emit the ack and writes the IBC
// acknowledgement
rpc EmitIBCAck(MsgEmitIBCAck) returns (MsgEmitIBCAckResponse);
}

message MsgEmitIBCAck {
string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
uint64 packet_sequence = 2
[ (gogoproto.moretags) = "yaml:\"packet_sequence\"" ];
string channel = 3 [ (gogoproto.moretags) = "yaml:\"channel\"" ];
}
message MsgEmitIBCAckResponse {
string contract_result = 1
[ (gogoproto.moretags) = "yaml:\"contract_result\"" ];
string ibc_ack = 2 [ (gogoproto.moretags) = "yaml:\"ibc_ack\"" ];
}
90 changes: 90 additions & 0 deletions tests/ibc-hooks/async_acks_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package ibc_hooks_test

import (
"encoding/json"
"fmt"
wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
sdk "github.com/cosmos/cosmos-sdk/types"
channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
ibctesting "github.com/cosmos/ibc-go/v4/testing"
"github.com/osmosis-labs/osmosis/v15/app"
)

func (suite *HooksTestSuite) forceContractToEmitAckForPacket(osmosisApp *app.OsmosisApp, contractAddr sdk.AccAddress, packet channeltypes.Packet) ([]byte, error) {
packetJson, err := json.Marshal(packet)
suite.Require().NoError(err)

msg := fmt.Sprintf(`{"force_emit_ibc_ack": {"packet": %s, "channel": "channel-0"}}`, packetJson)
contractKeeper := wasmkeeper.NewDefaultPermissionKeeper(osmosisApp.WasmKeeper)
return contractKeeper.Execute(suite.chainA.GetContext(), contractAddr, suite.chainA.SenderAccount.GetAddress(), []byte(msg), sdk.NewCoins())

}

func (suite *HooksTestSuite) TestWasmHooksAsyncAcks() {
sender := suite.chainB.SenderAccount.GetAddress()
osmosisApp := suite.chainA.GetOsmosisApp()

// Instantiate a contract that knows how to send async Acks
suite.chainA.StoreContractCode(&suite.Suite, "./bytecode/echo.wasm")
contractAddr := suite.chainA.InstantiateContract(&suite.Suite, "{}", 1)

// Calls that don't specify async acks work as expected
memo := fmt.Sprintf(`{"wasm": {"contract": "%s", "msg": {"async": {"use_async": false}}}}`, contractAddr)
suite.fundAccount(suite.chainB, sender)
transferMsg := NewMsgTransfer(sdk.NewCoin("token0", sdk.NewInt(2000)), sender.String(), contractAddr.String(), "channel-0", memo)
sendResult, receiveResult, ack, err := suite.FullSend(transferMsg, BtoA)
suite.Require().NoError(err)
suite.Require().NotNil(sendResult)
suite.Require().NotNil(receiveResult)
suite.Require().NotNil(ack)

// the ack has been written
allAcks := osmosisApp.IBCKeeper.ChannelKeeper.GetAllPacketAcks(suite.chainA.GetContext())
suite.Require().Equal(1, len(allAcks))

// Try to emit an ack for a packet that already has been acked. This should fail

// we extract the packet that has been acked here to test later that our contract can't emit an ack for it
alreadyAckedPacket, err := ibctesting.ParsePacketFromEvents(sendResult.GetEvents())
suite.Require().NoError(err)

_, err = suite.forceContractToEmitAckForPacket(osmosisApp, contractAddr, alreadyAckedPacket)
suite.Require().Error(err)
suite.Require().Contains(err.Error(), "no ack actor set for channel channel-0 packet 1")

// Calls that specify async Acks work and no Acks are sent
memo = fmt.Sprintf(`{"wasm": {"contract": "%s", "msg": {"async": {"use_async": true}}}}`, contractAddr)
suite.fundAccount(suite.chainB, sender)
transferMsg = NewMsgTransfer(sdk.NewCoin("token0", sdk.NewInt(2000)), sender.String(), contractAddr.String(), "channel-0", memo)

sendResult, err = suite.chainB.SendMsgsNoCheck(transferMsg)
suite.Require().NoError(err)

packet, err := ibctesting.ParsePacketFromEvents(sendResult.GetEvents())
suite.Require().NoError(err)

receiveResult = suite.RelayPacketNoAck(packet, BtoA)
newAck, err := ibctesting.ParseAckFromEvents(receiveResult.GetEvents())
suite.Require().Error(err) // No ack!
suite.Require().Nil(newAck)

// No new ack has been written
allAcks = osmosisApp.IBCKeeper.ChannelKeeper.GetAllPacketAcks(suite.chainA.GetContext())
suite.Require().Equal(1, len(allAcks))

// Store a second contract and ask that one to emit an ack for the packet that the first contract sent
contractAddr2 := suite.chainA.InstantiateContract(&suite.Suite, "{}", 1)
_, err = suite.forceContractToEmitAckForPacket(osmosisApp, contractAddr2, packet)
// This should fail because the new contract is not authorized to emit acks for that packet
suite.Require().Error(err)
suite.Require().Contains(err.Error(), "is not allowed to send an ack for channel channel-0 packet 2")

// only the contract that sent the packet can send an ack for that packet sequence
_, err = suite.forceContractToEmitAckForPacket(osmosisApp, contractAddr, packet)
suite.Require().NoError(err)

allAcks = osmosisApp.IBCKeeper.ChannelKeeper.GetAllPacketAcks(suite.chainA.GetContext())
fmt.Println(allAcks)
suite.Require().Equal(2, len(allAcks))

}
Binary file modified tests/ibc-hooks/bytecode/echo.wasm
Binary file not shown.
Loading