diff --git a/CHANGELOG.md b/CHANGELOG.md index 69338dc417..4cde95bf2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - [#1232](https://github.com/crypto-org-chain/cronos/pull/1232) Adjust require gas in relayer precompile to be closed with actual consumed. - [#1247](https://github.com/crypto-org-chain/cronos/pull/1247) Update ethermint to develop, go-ethereum to `v1.11.2`. +- [#1235](https://github.com/crypto-org-chain/cronos/pull/1235) Add channel detail in ica packet callback. ### Improvements diff --git a/app/app.go b/app/app.go index 5df8f4fda0..3a8a3f0a2e 100644 --- a/app/app.go +++ b/app/app.go @@ -642,15 +642,17 @@ func New( } app.StakingKeeper.SetHooks(stakingtypes.NewMultiStakingHooks(hooks...)) - app.ICAAuthKeeper = *icaauthkeeper.NewKeeper(appCodec, keys[icaauthtypes.StoreKey], keys[icaauthtypes.MemStoreKey], app.ICAControllerKeeper, scopedICAAuthKeeper) - icaAuthModule := icaauth.NewAppModule(appCodec, app.ICAAuthKeeper) + app.ICAAuthKeeper = *icaauthkeeper.NewKeeper(appCodec, keys[icaauthtypes.StoreKey], keys[icaauthtypes.MemStoreKey], app.ICAControllerKeeper, scopedICAAuthKeeper, scopedICAControllerKeeper) icaAuthIBCModule := icaauth.NewIBCModule(app.ICAAuthKeeper) var icaControllerStack porttypes.IBCModule icaControllerStack = icacontroller.NewIBCMiddleware(icaAuthIBCModule, app.ICAControllerKeeper) icaControllerStack = ibcfee.NewIBCMiddleware(icaControllerStack, app.IBCFeeKeeper) // Since the callbacks middleware itself is an ics4wrapper, it needs to be passed to the ica controller keeper - app.ICAControllerKeeper.WithICS4Wrapper(icaControllerStack.(porttypes.Middleware)) + ics4Wrapper := icaControllerStack.(porttypes.Middleware) + app.ICAControllerKeeper.WithICS4Wrapper(ics4Wrapper) + app.ICAAuthKeeper.WithICS4Wrapper(ics4Wrapper) + icaAuthModule := icaauth.NewAppModule(appCodec, app.ICAAuthKeeper, ics4Wrapper) // we don't limit gas usage here, because the cronos keeper will use network parameter to control it. icaControllerStack = ibccallbacks.NewIBCMiddleware(icaControllerStack, app.IBCFeeKeeper, app.CronosKeeper, math.MaxUint64) diff --git a/integration_tests/contracts/contracts/TestICA.sol b/integration_tests/contracts/contracts/TestICA.sol index a9006abcef..120e266879 100644 --- a/integration_tests/contracts/contracts/TestICA.sol +++ b/integration_tests/contracts/contracts/TestICA.sol @@ -15,8 +15,8 @@ contract TestICA { SUCCESS, FAIL } - mapping (uint64 => Status) public statusMap; - event OnPacketResult(uint64 seq, Status status); + mapping (string => mapping (uint64 => Status)) public statusMap; + event OnPacketResult(string indexed packetSrcChannel, uint64 seq, Status status); function encodeRegister(string memory connectionID, string memory version) internal view returns (bytes memory) { return abi.encodeWithSignature( @@ -102,15 +102,19 @@ contract TestICA { return lastSeq; } - function onPacketResultCallback(uint64 seq, bool ack) external payable returns (bool) { + function getStatus(string calldata packetSrcChannel, uint64 seq) public view returns (Status) { + return statusMap[packetSrcChannel][seq]; + } + + function onPacketResultCallback(string calldata packetSrcChannel, uint64 seq, bool ack) external payable returns (bool) { // To prevent called by arbitrary user require(msg.sender == module_address); Status status = Status.FAIL; if (ack) { status = Status.SUCCESS; } - statusMap[seq] = status; - emit OnPacketResult(seq, status); + statusMap[packetSrcChannel][seq] = status; + emit OnPacketResult(packetSrcChannel, seq, status); return true; } } diff --git a/integration_tests/test_ica_precompile.py b/integration_tests/test_ica_precompile.py index 67f6761206..4bdb127e69 100644 --- a/integration_tests/test_ica_precompile.py +++ b/integration_tests/test_ica_precompile.py @@ -3,6 +3,7 @@ from enum import IntEnum import pytest +from eth_utils import keccak from pystarport import cluster from web3.datastructures import AttributeDict @@ -79,6 +80,7 @@ def submit_msgs( amount=amt, need_wait=True, msg_num=2, + channel_id="", ): cli_host = ibc.chainmain.cosmos_cli() cli_controller = ibc.cronos.cosmos_cli() @@ -118,7 +120,12 @@ def submit_msgs( else: logs = event.getLogs() assert len(logs) > 0 - assert logs[0].args == AttributeDict({"seq": expected_seq}) + assert logs[0].args == AttributeDict( + { + "packetSrcChannel": keccak(text=channel_id), + "seq": expected_seq, + } + ) if need_wait: wait_for_check_tx(cli_host, ica_address, num_txs) return str, diff_amt @@ -133,6 +140,7 @@ def test_call(ibc): contract_info = json.loads(CONTRACT_ABIS["IICAModule"].read_text()) contract = w3.eth.contract(address=CONTRACT, abi=contract_info) data = {"from": ADDRS[name]} + channel_id = get_next_channel(cli_controller, connid) ica_address = register_acc( cli_controller, w3, @@ -140,7 +148,7 @@ def test_call(ibc): contract.functions.queryAccount, data, addr, - get_next_channel(cli_controller, connid), + channel_id, ) balance = funds_ica(cli_host, ica_address) expected_seq = 1 @@ -152,6 +160,7 @@ def test_call(ibc): False, expected_seq, contract.events.SubmitMsgsResult, + channel_id=channel_id, ) balance -= diff assert cli_host.balance(ica_address, denom=denom) == balance @@ -164,25 +173,31 @@ def test_call(ibc): True, expected_seq, contract.events.SubmitMsgsResult, + channel_id=channel_id, ) balance -= diff assert cli_host.balance(ica_address, denom=denom) == balance -def wait_for_status_change(tcontract, seq): +def wait_for_status_change(tcontract, channel_id, seq): print(f"wait for status change for {seq}") def check_status(): - status = tcontract.caller.statusMap(seq) - print("current", status) + status = tcontract.caller.getStatus(channel_id, seq) return status wait_for_fn("current status", check_status) -def wait_for_packet_log(start, event, seq, status): +def wait_for_packet_log(start, event, channel_id, seq, status): print("wait for log arrive", seq, status) - expected = AttributeDict({"seq": seq, "status": status}) + expected = AttributeDict( + { + "packetSrcChannel": keccak(text=channel_id), + "seq": seq, + "status": status, + } + ) def check_log(): logs = event.getLogs(fromBlock=start) @@ -245,6 +260,7 @@ def submit_msgs_ro(func, str): assert send_transaction(w3, tx, keys).status == 0 expected_seq = 1 + packet_event = tcontract.events.OnPacketResult start = w3.eth.get_block_number() str, diff = submit_msgs( ibc, @@ -254,15 +270,16 @@ def submit_msgs_ro(func, str): False, expected_seq, contract.events.SubmitMsgsResult, + channel_id=channel_id, ) submit_msgs_ro(tcontract.functions.delegateSubmitMsgs, str) submit_msgs_ro(tcontract.functions.staticSubmitMsgs, str) last_seq = tcontract.caller.getLastSeq() - wait_for_status_change(tcontract, last_seq) - status = tcontract.caller.statusMap(last_seq) + wait_for_status_change(tcontract, channel_id, last_seq) + status = tcontract.caller.getStatus(channel_id, last_seq) assert expected_seq == last_seq assert status == Status.SUCCESS - wait_for_packet_log(start, tcontract.events.OnPacketResult, last_seq, status) + wait_for_packet_log(start, packet_event, channel_id, last_seq, status) balance -= diff assert cli_host.balance(ica_address, denom=denom) == balance @@ -276,15 +293,16 @@ def submit_msgs_ro(func, str): True, expected_seq, contract.events.SubmitMsgsResult, + channel_id=channel_id, ) submit_msgs_ro(tcontract.functions.delegateSubmitMsgs, str) submit_msgs_ro(tcontract.functions.staticSubmitMsgs, str) last_seq = tcontract.caller.getLastSeq() - wait_for_status_change(tcontract, last_seq) - status = tcontract.caller.statusMap(last_seq) + wait_for_status_change(tcontract, channel_id, last_seq) + status = tcontract.caller.getStatus(channel_id, last_seq) assert expected_seq == last_seq assert status == Status.SUCCESS - wait_for_packet_log(start, tcontract.events.OnPacketResult, last_seq, status) + wait_for_packet_log(start, packet_event, channel_id, last_seq, status) balance -= diff assert cli_host.balance(ica_address, denom=denom) == balance @@ -301,13 +319,14 @@ def submit_msgs_ro(func, str): contract.events.SubmitMsgsResult, amount=100000001, need_wait=False, + channel_id=channel_id, ) last_seq = tcontract.caller.getLastSeq() - wait_for_status_change(tcontract, last_seq) - status = tcontract.caller.statusMap(last_seq) + wait_for_status_change(tcontract, channel_id, last_seq) + status = tcontract.caller.getStatus(channel_id, last_seq) assert expected_seq == last_seq assert status == Status.FAIL - wait_for_packet_log(start, tcontract.events.OnPacketResult, last_seq, status) + wait_for_packet_log(start, packet_event, channel_id, last_seq, status) assert cli_host.balance(ica_address, denom=denom) == balance # balance should not change on timeout @@ -325,16 +344,18 @@ def submit_msgs_ro(func, str): contract.events.SubmitMsgsResult, timeout, msg_num=100, + channel_id=channel_id, ) last_seq = tcontract.caller.getLastSeq() - wait_for_status_change(tcontract, last_seq) - status = tcontract.caller.statusMap(last_seq) + wait_for_status_change(tcontract, channel_id, last_seq) + status = tcontract.caller.getStatus(channel_id, last_seq) assert expected_seq == last_seq assert status == Status.FAIL - wait_for_packet_log(start, tcontract.events.OnPacketResult, last_seq, status) + wait_for_packet_log(start, packet_event, channel_id, last_seq, status) assert cli_host.balance(ica_address, denom=denom) == balance wait_for_check_channel_ready(cli_controller, connid, channel_id, "STATE_CLOSED") data["gas"] = default_gas + channel_id2 = get_next_channel(cli_controller, connid) ica_address2 = register_acc( cli_controller, w3, @@ -342,8 +363,9 @@ def submit_msgs_ro(func, str): contract.functions.queryAccount, data, addr, - get_next_channel(cli_controller, connid), + channel_id2, ) + assert channel_id2 != channel_id, channel_id2 assert ica_address2 == ica_address, ica_address2 expected_seq = 1 start = w3.eth.get_block_number() @@ -355,13 +377,14 @@ def submit_msgs_ro(func, str): False, expected_seq, contract.events.SubmitMsgsResult, + channel_id=channel_id2, ) last_seq = tcontract.caller.getLastSeq() - wait_for_status_change(tcontract, last_seq) - status = tcontract.caller.statusMap(last_seq) + wait_for_status_change(tcontract, channel_id2, last_seq) + status = tcontract.caller.getStatus(channel_id2, last_seq) assert expected_seq == last_seq assert status == Status.SUCCESS # wait for ack to add log from call evm - wait_for_packet_log(start, tcontract.events.OnPacketResult, last_seq, status) + wait_for_packet_log(start, packet_event, channel_id2, last_seq, status) balance -= diff assert cli_host.balance(ica_address, denom=denom) == balance diff --git a/x/cronos/events/bindings/cosmos/precompile/ica/i_ica_module.abigen.go b/x/cronos/events/bindings/cosmos/precompile/ica/i_ica_module.abigen.go index 7f05447446..63fe15aaa8 100644 --- a/x/cronos/events/bindings/cosmos/precompile/ica/i_ica_module.abigen.go +++ b/x/cronos/events/bindings/cosmos/precompile/ica/i_ica_module.abigen.go @@ -30,7 +30,7 @@ var ( // ICAModuleMetaData contains all meta data concerning the ICAModule contract. var ICAModuleMetaData = &bind.MetaData{ - ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"seq\",\"type\":\"uint64\"}],\"name\":\"SubmitMsgsResult\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"connectionID\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"queryAccount\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"connectionID\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"}],\"name\":\"registerAccount\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"connectionID\",\"type\":\"string\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"timeout\",\"type\":\"uint256\"}],\"name\":\"submitMsgs\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"payable\",\"type\":\"function\"}]", + ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"string\",\"name\":\"packetSrcChannel\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"seq\",\"type\":\"uint64\"}],\"name\":\"SubmitMsgsResult\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"connectionID\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"queryAccount\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"connectionID\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"}],\"name\":\"registerAccount\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"connectionID\",\"type\":\"string\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"timeout\",\"type\":\"uint256\"}],\"name\":\"submitMsgs\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"payable\",\"type\":\"function\"}]", } // ICAModuleABI is the input ABI used to generate the binding from. @@ -321,28 +321,39 @@ func (it *ICAModuleSubmitMsgsResultIterator) Close() error { // ICAModuleSubmitMsgsResult represents a SubmitMsgsResult event raised by the ICAModule contract. type ICAModuleSubmitMsgsResult struct { - Seq uint64 - Raw types.Log // Blockchain specific contextual infos + PacketSrcChannel common.Hash + Seq uint64 + Raw types.Log // Blockchain specific contextual infos } -// FilterSubmitMsgsResult is a free log retrieval operation binding the contract event 0x9445c42aa80c009b1b0204bc10c31084e1262133ee7f838b51280832874afc1c. +// FilterSubmitMsgsResult is a free log retrieval operation binding the contract event 0x3e695f8f5525f556604458a25db1d059820bd331e56bc17b0693b9d35e243eca. // -// Solidity: event SubmitMsgsResult(uint64 seq) -func (_ICAModule *ICAModuleFilterer) FilterSubmitMsgsResult(opts *bind.FilterOpts) (*ICAModuleSubmitMsgsResultIterator, error) { +// Solidity: event SubmitMsgsResult(string indexed packetSrcChannel, uint64 seq) +func (_ICAModule *ICAModuleFilterer) FilterSubmitMsgsResult(opts *bind.FilterOpts, packetSrcChannel []string) (*ICAModuleSubmitMsgsResultIterator, error) { - logs, sub, err := _ICAModule.contract.FilterLogs(opts, "SubmitMsgsResult") + var packetSrcChannelRule []interface{} + for _, packetSrcChannelItem := range packetSrcChannel { + packetSrcChannelRule = append(packetSrcChannelRule, packetSrcChannelItem) + } + + logs, sub, err := _ICAModule.contract.FilterLogs(opts, "SubmitMsgsResult", packetSrcChannelRule) if err != nil { return nil, err } return &ICAModuleSubmitMsgsResultIterator{contract: _ICAModule.contract, event: "SubmitMsgsResult", logs: logs, sub: sub}, nil } -// WatchSubmitMsgsResult is a free log subscription operation binding the contract event 0x9445c42aa80c009b1b0204bc10c31084e1262133ee7f838b51280832874afc1c. +// WatchSubmitMsgsResult is a free log subscription operation binding the contract event 0x3e695f8f5525f556604458a25db1d059820bd331e56bc17b0693b9d35e243eca. // -// Solidity: event SubmitMsgsResult(uint64 seq) -func (_ICAModule *ICAModuleFilterer) WatchSubmitMsgsResult(opts *bind.WatchOpts, sink chan<- *ICAModuleSubmitMsgsResult) (event.Subscription, error) { +// Solidity: event SubmitMsgsResult(string indexed packetSrcChannel, uint64 seq) +func (_ICAModule *ICAModuleFilterer) WatchSubmitMsgsResult(opts *bind.WatchOpts, sink chan<- *ICAModuleSubmitMsgsResult, packetSrcChannel []string) (event.Subscription, error) { + + var packetSrcChannelRule []interface{} + for _, packetSrcChannelItem := range packetSrcChannel { + packetSrcChannelRule = append(packetSrcChannelRule, packetSrcChannelItem) + } - logs, sub, err := _ICAModule.contract.WatchLogs(opts, "SubmitMsgsResult") + logs, sub, err := _ICAModule.contract.WatchLogs(opts, "SubmitMsgsResult", packetSrcChannelRule) if err != nil { return nil, err } @@ -374,9 +385,9 @@ func (_ICAModule *ICAModuleFilterer) WatchSubmitMsgsResult(opts *bind.WatchOpts, }), nil } -// ParseSubmitMsgsResult is a log parse operation binding the contract event 0x9445c42aa80c009b1b0204bc10c31084e1262133ee7f838b51280832874afc1c. +// ParseSubmitMsgsResult is a log parse operation binding the contract event 0x3e695f8f5525f556604458a25db1d059820bd331e56bc17b0693b9d35e243eca. // -// Solidity: event SubmitMsgsResult(uint64 seq) +// Solidity: event SubmitMsgsResult(string indexed packetSrcChannel, uint64 seq) func (_ICAModule *ICAModuleFilterer) ParseSubmitMsgsResult(log types.Log) (*ICAModuleSubmitMsgsResult, error) { event := new(ICAModuleSubmitMsgsResult) if err := _ICAModule.contract.UnpackLog(event, "SubmitMsgsResult", log); err != nil { diff --git a/x/cronos/events/bindings/cosmos/precompile/icacallback/i_ica_callback.abigen.go b/x/cronos/events/bindings/cosmos/precompile/icacallback/i_ica_callback.abigen.go index 239ef1503a..6b82977f80 100644 --- a/x/cronos/events/bindings/cosmos/precompile/icacallback/i_ica_callback.abigen.go +++ b/x/cronos/events/bindings/cosmos/precompile/icacallback/i_ica_callback.abigen.go @@ -30,7 +30,7 @@ var ( // ICACallbackMetaData contains all meta data concerning the ICACallback contract. var ICACallbackMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"seq\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"ack\",\"type\":\"bool\"}],\"name\":\"onPacketResultCallback\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"payable\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"packetSrcChannel\",\"type\":\"string\"},{\"internalType\":\"uint64\",\"name\":\"seq\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"ack\",\"type\":\"bool\"}],\"name\":\"onPacketResultCallback\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"payable\",\"type\":\"function\"}]", } // ICACallbackABI is the input ABI used to generate the binding from. @@ -179,23 +179,23 @@ func (_ICACallback *ICACallbackTransactorRaw) Transact(opts *bind.TransactOpts, return _ICACallback.Contract.contract.Transact(opts, method, params...) } -// OnPacketResultCallback is a paid mutator transaction binding the contract method 0x74fb6019. +// OnPacketResultCallback is a paid mutator transaction binding the contract method 0xd2712162. // -// Solidity: function onPacketResultCallback(uint64 seq, bool ack) payable returns(bool) -func (_ICACallback *ICACallbackTransactor) OnPacketResultCallback(opts *bind.TransactOpts, seq uint64, ack bool) (*types.Transaction, error) { - return _ICACallback.contract.Transact(opts, "onPacketResultCallback", seq, ack) +// Solidity: function onPacketResultCallback(string packetSrcChannel, uint64 seq, bool ack) payable returns(bool) +func (_ICACallback *ICACallbackTransactor) OnPacketResultCallback(opts *bind.TransactOpts, packetSrcChannel string, seq uint64, ack bool) (*types.Transaction, error) { + return _ICACallback.contract.Transact(opts, "onPacketResultCallback", packetSrcChannel, seq, ack) } -// OnPacketResultCallback is a paid mutator transaction binding the contract method 0x74fb6019. +// OnPacketResultCallback is a paid mutator transaction binding the contract method 0xd2712162. // -// Solidity: function onPacketResultCallback(uint64 seq, bool ack) payable returns(bool) -func (_ICACallback *ICACallbackSession) OnPacketResultCallback(seq uint64, ack bool) (*types.Transaction, error) { - return _ICACallback.Contract.OnPacketResultCallback(&_ICACallback.TransactOpts, seq, ack) +// Solidity: function onPacketResultCallback(string packetSrcChannel, uint64 seq, bool ack) payable returns(bool) +func (_ICACallback *ICACallbackSession) OnPacketResultCallback(packetSrcChannel string, seq uint64, ack bool) (*types.Transaction, error) { + return _ICACallback.Contract.OnPacketResultCallback(&_ICACallback.TransactOpts, packetSrcChannel, seq, ack) } -// OnPacketResultCallback is a paid mutator transaction binding the contract method 0x74fb6019. +// OnPacketResultCallback is a paid mutator transaction binding the contract method 0xd2712162. // -// Solidity: function onPacketResultCallback(uint64 seq, bool ack) payable returns(bool) -func (_ICACallback *ICACallbackTransactorSession) OnPacketResultCallback(seq uint64, ack bool) (*types.Transaction, error) { - return _ICACallback.Contract.OnPacketResultCallback(&_ICACallback.TransactOpts, seq, ack) +// Solidity: function onPacketResultCallback(string packetSrcChannel, uint64 seq, bool ack) payable returns(bool) +func (_ICACallback *ICACallbackTransactorSession) OnPacketResultCallback(packetSrcChannel string, seq uint64, ack bool) (*types.Transaction, error) { + return _ICACallback.Contract.OnPacketResultCallback(&_ICACallback.TransactOpts, packetSrcChannel, seq, ack) } diff --git a/x/cronos/events/bindings/src/ICA.sol b/x/cronos/events/bindings/src/ICA.sol index 31f10530a7..e3528255f3 100644 --- a/x/cronos/events/bindings/src/ICA.sol +++ b/x/cronos/events/bindings/src/ICA.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.4; interface IICAModule { - event SubmitMsgsResult(uint64 seq); + event SubmitMsgsResult(string indexed packetSrcChannel, uint64 seq); function registerAccount(string calldata connectionID, string calldata version) external payable returns (bool); function queryAccount(string calldata connectionID, address addr) external view returns (string memory); function submitMsgs(string calldata connectionID, bytes calldata data, uint256 timeout) external payable returns (uint64); diff --git a/x/cronos/events/bindings/src/ICACallback.sol b/x/cronos/events/bindings/src/ICACallback.sol index aa3a779513..89aea3ff7f 100644 --- a/x/cronos/events/bindings/src/ICACallback.sol +++ b/x/cronos/events/bindings/src/ICACallback.sol @@ -2,5 +2,5 @@ pragma solidity ^0.8.4; interface IICACallback { - function onPacketResultCallback(uint64 seq, bool ack) external payable returns (bool); + function onPacketResultCallback(string calldata packetSrcChannel, uint64 seq, bool ack) external payable returns (bool); } diff --git a/x/cronos/events/events.go b/x/cronos/events/events.go index d4b0a98be4..9bb3fca69b 100644 --- a/x/cronos/events/events.go +++ b/x/cronos/events/events.go @@ -35,7 +35,8 @@ var ( transfertypes.AttributeKeyDenom: ReturnStringAsIs, } IcaValueDecoders = ValueDecoders{ - cronoseventstypes.AttributeKeySeq: ConvertUint64, + cronoseventstypes.AttributeKeySeq: ConvertUint64, + channeltypes.AttributeKeySrcChannel: ReturnStringAsIs, } ) diff --git a/x/cronos/keeper/keeper.go b/x/cronos/keeper/keeper.go index 8a013756a4..08fd65b5db 100644 --- a/x/cronos/keeper/keeper.go +++ b/x/cronos/keeper/keeper.go @@ -292,8 +292,7 @@ func (k Keeper) onPacketResult( packetSenderAddress string, ) error { contractAddr := common.HexToAddress(contractAddress) - - data, err := cronosprecompiles.OnPacketResultCallback(packet.Sequence, acknowledgement) + data, err := cronosprecompiles.OnPacketResultCallback(packet.SourceChannel, packet.Sequence, acknowledgement) if err != nil { return err } diff --git a/x/cronos/keeper/precompiles/ica.go b/x/cronos/keeper/precompiles/ica.go index e003312a10..6016e64e4b 100644 --- a/x/cronos/keeper/precompiles/ica.go +++ b/x/cronos/keeper/precompiles/ica.go @@ -11,6 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" cronosevents "github.com/crypto-org-chain/cronos/v2/x/cronos/events" "github.com/crypto-org-chain/cronos/v2/x/cronos/events/bindings/cosmos/precompile/ica" "github.com/crypto-org-chain/cronos/v2/x/cronos/events/bindings/cosmos/precompile/icacallback" @@ -177,7 +178,7 @@ func (ic *IcaContract) Run(evm *vm.EVM, contract *vm.Contract, readonly bool) ([ timeoutDuration := time.Duration(timeout.Uint64()) seq := uint64(0) execErr = stateDB.ExecuteNativeAction(precompileAddr, converter, func(ctx sdk.Context) error { - response, err := ic.icaauthKeeper.SubmitTxWithArgs( + activeChannelID, response, err := ic.icaauthKeeper.SubmitTxWithArgs( ctx, owner, connectionID, @@ -189,6 +190,7 @@ func (ic *IcaContract) Run(evm *vm.EVM, contract *vm.Contract, readonly bool) ([ ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( cronoseventstypes.EventTypeSubmitMsgsResult, + sdk.NewAttribute(channeltypes.AttributeKeySrcChannel, activeChannelID), sdk.NewAttribute(cronoseventstypes.AttributeKeySeq, fmt.Sprintf("%d", response.Sequence)), ), }) diff --git a/x/cronos/types/interfaces.go b/x/cronos/types/interfaces.go index 24a5a7e7b5..61e8acaeb0 100644 --- a/x/cronos/types/interfaces.go +++ b/x/cronos/types/interfaces.go @@ -87,7 +87,7 @@ type EvmKeeper interface { type Icaauthkeeper interface { RegisterAccount(goCtx context.Context, msg *icaauthtypes.MsgRegisterAccount) (*icaauthtypes.MsgRegisterAccountResponse, error) InterchainAccountAddress(goCtx context.Context, req *icaauthtypes.QueryInterchainAccountAddressRequest) (*icaauthtypes.QueryInterchainAccountAddressResponse, error) - SubmitTxWithArgs(goCtx context.Context, owner, connectionId string, timeoutDuration time.Duration, packetData icatypes.InterchainAccountPacketData) (*icaauthtypes.MsgSubmitTxResponse, error) + SubmitTxWithArgs(goCtx context.Context, owner, connectionId string, timeoutDuration time.Duration, packetData icatypes.InterchainAccountPacketData) (string, *icaauthtypes.MsgSubmitTxResponse, error) } // CronosKeeper defines the interface for cronos keeper diff --git a/x/icaauth/keeper/keeper.go b/x/icaauth/keeper/keeper.go index 5137efd79e..cef768f6cb 100644 --- a/x/icaauth/keeper/keeper.go +++ b/x/icaauth/keeper/keeper.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + errorsmod "cosmossdk.io/errors" "github.com/cometbft/cometbft/libs/log" "github.com/cosmos/cosmos-sdk/codec" storetypes "github.com/cosmos/cosmos-sdk/store/types" @@ -13,6 +14,9 @@ import ( capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" icacontrollerkeeper "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/keeper" icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" "github.com/crypto-org-chain/cronos/v2/x/icaauth/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -24,8 +28,10 @@ type ( storeKey storetypes.StoreKey memKey storetypes.StoreKey - icaControllerKeeper icacontrollerkeeper.Keeper - scopedKeeper capabilitykeeper.ScopedKeeper + icaControllerKeeper icacontrollerkeeper.Keeper + ics4Wrapper porttypes.ICS4Wrapper + scopedKeeper capabilitykeeper.ScopedKeeper + controllerScopedKeeper capabilitykeeper.ScopedKeeper } ) @@ -35,17 +41,22 @@ func NewKeeper( memKey storetypes.StoreKey, icaControllerKeeper icacontrollerkeeper.Keeper, scopedKeeper capabilitykeeper.ScopedKeeper, + controllerScopedKeeper capabilitykeeper.ScopedKeeper, ) *Keeper { return &Keeper{ - cdc: cdc, - storeKey: storeKey, - memKey: memKey, - - icaControllerKeeper: icaControllerKeeper, - scopedKeeper: scopedKeeper, + cdc: cdc, + storeKey: storeKey, + memKey: memKey, + icaControllerKeeper: icaControllerKeeper, + scopedKeeper: scopedKeeper, + controllerScopedKeeper: controllerScopedKeeper, } } +func (k *Keeper) WithICS4Wrapper(ics4Wrapper porttypes.ICS4Wrapper) { + k.ics4Wrapper = ics4Wrapper +} + // SubmitTx submits a transaction to the host chain on behalf of interchain account func (k *Keeper) SubmitTx(goCtx context.Context, msg *types.MsgSubmitTx) (*types.MsgSubmitTxResponse, error) { msgs, err := msg.GetMessages() @@ -62,14 +73,41 @@ func (k *Keeper) SubmitTx(goCtx context.Context, msg *types.MsgSubmitTx) (*types Type: icatypes.EXECUTE_TX, Data: data, } - return k.SubmitTxWithArgs(goCtx, msg.Owner, msg.ConnectionId, *msg.TimeoutDuration, packetData) + _, rsp, err := k.SubmitTxWithArgs(goCtx, msg.Owner, msg.ConnectionId, *msg.TimeoutDuration, packetData) + return rsp, err } -func (k *Keeper) SubmitTxWithArgs(goCtx context.Context, owner, connectionId string, timeoutDuration time.Duration, packetData icatypes.InterchainAccountPacketData) (*types.MsgSubmitTxResponse, error) { +func (k *Keeper) sendTx(ctx sdk.Context, connectionID, portID string, icaPacketData icatypes.InterchainAccountPacketData, timeoutTimestamp uint64) (string, uint64, error) { + activeChannelID, found := k.icaControllerKeeper.GetOpenActiveChannel(ctx, connectionID, portID) + if !found { + return "", 0, errorsmod.Wrapf(icatypes.ErrActiveChannelNotFound, "failed to retrieve active channel on connection %s for port %s", connectionID, portID) + } + + chanCap, found := k.controllerScopedKeeper.GetCapability(ctx, host.ChannelCapabilityPath(portID, activeChannelID)) + if !found { + return "", 0, errorsmod.Wrapf(capabilitytypes.ErrCapabilityNotFound, "failed to find capability: %s", host.ChannelCapabilityPath(portID, activeChannelID)) + } + + if uint64(ctx.BlockTime().UnixNano()) >= timeoutTimestamp { + return "", 0, icatypes.ErrInvalidTimeoutTimestamp + } + + if err := icaPacketData.ValidateBasic(); err != nil { + return "", 0, errorsmod.Wrap(err, "invalid interchain account packet data") + } + + sequence, err := k.ics4Wrapper.SendPacket(ctx, chanCap, portID, activeChannelID, clienttypes.ZeroHeight(), timeoutTimestamp, icaPacketData.GetBytes()) + if err != nil { + return "", 0, err + } + return activeChannelID, sequence, nil +} + +func (k *Keeper) SubmitTxWithArgs(goCtx context.Context, owner, connectionId string, timeoutDuration time.Duration, packetData icatypes.InterchainAccountPacketData) (string, *types.MsgSubmitTxResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) portID, err := icatypes.NewControllerPortID(owner) if err != nil { - return nil, err + return "", nil, err } minTimeoutDuration := k.MinTimeoutDuration(ctx) // timeoutDuration should be constraited by MinTimeoutDuration parameter. @@ -77,11 +115,11 @@ func (k *Keeper) SubmitTxWithArgs(goCtx context.Context, owner, connectionId str types.MsgSubmitTx{ TimeoutDuration: &timeoutDuration, }.CalculateTimeoutDuration(minTimeoutDuration)).UnixNano() - res, err := k.icaControllerKeeper.SendTx(ctx, nil, connectionId, portID, packetData, uint64(timeoutTimestamp)) //nolint:staticcheck + activeChannelID, res, err := k.sendTx(ctx, connectionId, portID, packetData, uint64(timeoutTimestamp)) if err != nil { - return nil, err + return "", nil, err } - return &types.MsgSubmitTxResponse{ + return activeChannelID, &types.MsgSubmitTxResponse{ Sequence: res, }, nil } diff --git a/x/icaauth/module.go b/x/icaauth/module.go index b0b085e9d4..58e2802a15 100644 --- a/x/icaauth/module.go +++ b/x/icaauth/module.go @@ -11,6 +11,7 @@ import ( cdctypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" "github.com/crypto-org-chain/cronos/v2/x/icaauth/client/cli" "github.com/crypto-org-chain/cronos/v2/x/icaauth/keeper" "github.com/crypto-org-chain/cronos/v2/x/icaauth/types" @@ -104,7 +105,9 @@ type AppModule struct { func NewAppModule( cdc codec.BinaryCodec, keeper keeper.Keeper, + ics4Wrapper porttypes.ICS4Wrapper, ) AppModule { + keeper.WithICS4Wrapper(ics4Wrapper) return AppModule{ AppModuleBasic: NewAppModuleBasic(cdc), keeper: keeper,