Skip to content

Commit

Permalink
chore: Add tests for messages execution order
Browse files Browse the repository at this point in the history
  • Loading branch information
kulikthebird committed Dec 9, 2024
1 parent 0ef57e7 commit 91b7a1f
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 0 deletions.
138 changes: 138 additions & 0 deletions x/wasm/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ import (
//go:embed testdata/hackatom.wasm
var hackatomWasm []byte

//go:embed testdata/replier.wasm
var replierWasm []byte

var AvailableCapabilities = []string{
"iterator", "staking", "stargate", "cosmwasm_1_1", "cosmwasm_1_2", "cosmwasm_1_3",
"cosmwasm_1_4", "cosmwasm_2_0", "cosmwasm_2_1", "cosmwasm_2_2",
Expand Down Expand Up @@ -2174,6 +2177,141 @@ func TestReply(t *testing.T) {
}
}

type replierExecMsg struct {
MsgId byte `json:"msg_id"`
SetDataInExecAndReply bool `json:"set_data_in_exec_and_reply"`
ReturnOrderInReply bool `json:"return_order_in_reply"`
ExecError bool `json:"exec_error"`
ReplyOnNever bool `json:"reply_on_never"`
Messages []replierExecMsg `json:"messages"`
}

var repliesMsgTemplate = replierExecMsg{
MsgId: 1,
SetDataInExecAndReply: true,
ReturnOrderInReply: false,
ExecError: false,
ReplyOnNever: false,
Messages: []replierExecMsg{
replierExecMsg{
MsgId: 2,
SetDataInExecAndReply: true,
ReturnOrderInReply: false,
ExecError: false,
ReplyOnNever: false,
Messages: []replierExecMsg{
replierExecMsg{
MsgId: 3,
SetDataInExecAndReply: true,
ReturnOrderInReply: false,
ExecError: false,
ReplyOnNever: false,
Messages: []replierExecMsg{},
},
},
},
replierExecMsg{
MsgId: 4,
SetDataInExecAndReply: true,
ReturnOrderInReply: false,
ExecError: false,
ReplyOnNever: false,
Messages: []replierExecMsg{
replierExecMsg{
MsgId: 5,
SetDataInExecAndReply: true,
ReturnOrderInReply: false,
ExecError: false,
ReplyOnNever: false,
Messages: []replierExecMsg{},
},
},
},
},
}

func TestMultipleReplies(t *testing.T) {
ctx, keepers := CreateTestInput(t, false, AvailableCapabilities)
_, keeper, _ := keepers.AccountKeeper, keepers.ContractKeeper, keepers.BankKeeper

deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
creator := DeterministicAccountAddress(t, 1)
keepers.Faucet.Fund(ctx, creator, deposit.Add(deposit...)...)
creatorAddr := RandomAccountAddress(t)

contractID, _, err := keeper.Create(ctx, creator, replierWasm, nil)
require.NoError(t, err)

require.NoError(t, err)
addr, _, err := keepers.ContractKeeper.Instantiate(ctx, contractID, creator, nil, []byte("{}"), "demo contract replier", deposit)
require.NoError(t, err)

// Assert the depth-first order of message handling
repliesMsgTemplate.ReturnOrderInReply = true
execMsg, err := json.Marshal(repliesMsgTemplate)
require.NoError(t, err)
em := sdk.NewEventManager()
res, err := keepers.ContractKeeper.Execute(ctx.WithEventManager(em), addr, creatorAddr, execMsg, nil)
require.NoError(t, err)
require.NotNil(t, res)
assert.Equal(t, []byte{0xee, 0x1, 0xee, 0x2, 0xee, 0x3, 0xbb, 0x2, 0xbb, 0x1, 0xee, 0x4, 0xee, 0x5, 0xbb, 0x4, 0xbb, 0x1}, res)
repliesMsgTemplate.ReturnOrderInReply = false

// Assert that with a list of submessages the `data` field will be set by the
// last submessage.
execMsg, err = json.Marshal(repliesMsgTemplate)
require.NoError(t, err)
em = sdk.NewEventManager()
res, err = keepers.ContractKeeper.Execute(ctx.WithEventManager(em), addr, creatorAddr, execMsg, nil)
require.NoError(t, err)
require.NotNil(t, res)
assert.Equal(t, []byte{0xa, 0x6, 0xa, 0x2, 0xee, 0x5, 0xbb, 0x4, 0xbb, 0x1}, res)

// Assert that with a list of submessages the `data` field will be
// set by the last submessage that overrides it.
repliesMsgTemplate.Messages[1].ReplyOnNever = true
execMsg, err = json.Marshal(repliesMsgTemplate)
require.NoError(t, err)
em = sdk.NewEventManager()
res, err = keepers.ContractKeeper.Execute(ctx.WithEventManager(em), addr, creatorAddr, execMsg, nil)
require.NoError(t, err)
require.NotNil(t, res)
assert.Equal(t, []byte{0xa, 0x6, 0xa, 0x2, 0xee, 0x3, 0xbb, 0x2, 0xbb, 0x1}, res)
repliesMsgTemplate.Messages[1].ReplyOnNever = false

// Assert that in scenario C1 -> C4 -> C5 if C4 doesn't set `data`,
// the `data` set by C5 **is not forwarded** to the result of C1.
repliesMsgTemplate.Messages[1].SetDataInExecAndReply = false
execMsg, err = json.Marshal(repliesMsgTemplate)
require.NoError(t, err)
em = sdk.NewEventManager()
res, err = keepers.ContractKeeper.Execute(ctx.WithEventManager(em), addr, creatorAddr, execMsg, nil)
require.NoError(t, err)
require.NotNil(t, res)
assert.Equal(t, []byte{0xbb, 0x1}, res)
repliesMsgTemplate.Messages[1].SetDataInExecAndReply = true

// Assert that returning the error will cause the transaction
// rollback until the moment of handling the error by the
// `reply`` entry-point that returns non-error response. Everything
// In this example we have the following scenario:
// `C1 -> C2 -> C3 -> reply(C2) -> reply(C1) -> C4 -> C5 -> reply(C4) -> reply(C1)`.
// The `C2` contract call returns an error that is handled by reply entrypoint of `C2`.
// It means that the changes done by `C2` are reverted and `C3` is never called,
// but the rest of the changes are kept.
repliesMsgTemplate.Messages[0].ExecError = true
repliesMsgTemplate.ReturnOrderInReply = true
execMsg, err = json.Marshal(repliesMsgTemplate)
require.NoError(t, err)
em = sdk.NewEventManager()
res, err = keepers.ContractKeeper.Execute(ctx.WithEventManager(em), addr, creatorAddr, execMsg, nil)
require.NoError(t, err)
assert.Equal(t, []byte{0xee, 0x1, 0xbb, 0x1, 0xee, 0x4, 0xee, 0x5, 0xbb, 0x4, 0xbb, 0x1}, res)
repliesMsgTemplate.Messages[0].Messages[0].ExecError = false
repliesMsgTemplate.ReturnOrderInReply = false

}

func TestQueryIsolation(t *testing.T) {
ctx, keepers := CreateTestInput(t, false, AvailableCapabilities)
k := keepers.WasmKeeper
Expand Down
Binary file added x/wasm/keeper/testdata/replier.wasm
Binary file not shown.

0 comments on commit 91b7a1f

Please sign in to comment.