diff --git a/CHANGELOG.md b/CHANGELOG.md index 66adc00c5f..b5ab07dbd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - [cronos#1069](https://github.com/crypto-org-chain/cronos/pull/1069) Update ethermint to develop, go-ethereum to `v1.10.26` and ibc-go to `v6.2.0`. - [cronos#1147](https://github.com/crypto-org-chain/cronos/pull/1147) integrate ica module. - (deps) [#1121](https://github.com/crypto-org-chain/cronos/pull/1121) Bump Cosmos-SDK to v0.47.5 and ibc-go to v7.2.0. +- [cronos#1014](https://github.com/crypto-org-chain/cronos/pull/1014) Support stateful precompiled contract for relayer. ### Bug Fixes diff --git a/app/app.go b/app/app.go index cd4a1cfe52..9a50d43973 100644 --- a/app/app.go +++ b/app/app.go @@ -129,8 +129,8 @@ import ( ethermint "github.com/evmos/ethermint/types" "github.com/evmos/ethermint/x/evm" evmkeeper "github.com/evmos/ethermint/x/evm/keeper" + "github.com/evmos/ethermint/x/evm/keeper/precompiles" evmtypes "github.com/evmos/ethermint/x/evm/types" - "github.com/evmos/ethermint/x/evm/vm/geth" "github.com/evmos/ethermint/x/feemarket" feemarketkeeper "github.com/evmos/ethermint/x/feemarket/keeper" feemarkettypes "github.com/evmos/ethermint/x/feemarket/types" @@ -149,6 +149,7 @@ import ( cronosclient "github.com/crypto-org-chain/cronos/v2/x/cronos/client" cronoskeeper "github.com/crypto-org-chain/cronos/v2/x/cronos/keeper" evmhandlers "github.com/crypto-org-chain/cronos/v2/x/cronos/keeper/evmhandlers" + cronosprecompiles "github.com/crypto-org-chain/cronos/v2/x/cronos/keeper/precompiles" cronostypes "github.com/crypto-org-chain/cronos/v2/x/cronos/types" "github.com/crypto-org-chain/cronos/v2/client/docs" @@ -529,11 +530,26 @@ func New( ) // Set authority to x/gov module account to only expect the module account to update params evmS := app.GetSubspace(evmtypes.ModuleName) + allKeys := make(map[string]storetypes.StoreKey, len(keys)+len(tkeys)+len(memKeys)) + for k, v := range keys { + allKeys[k] = v + } + for k, v := range tkeys { + allKeys[k] = v + } + for k, v := range memKeys { + allKeys[k] = v + } app.EvmKeeper = evmkeeper.NewKeeper( appCodec, keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey], authtypes.NewModuleAddress(govtypes.ModuleName), app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.FeeMarketKeeper, - nil, geth.NewEVM, tracer, evmS, + tracer, + evmS, + []precompiles.StatefulPrecompiledContract{ + cronosprecompiles.NewRelayerContract(app.IBCKeeper, appCodec), + }, + allKeys, ) var gravityKeeper gravitykeeper.Keeper diff --git a/go.mod b/go.mod index 4e66e02a83..15aa45ef5d 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( github.com/crypto-org-chain/cronos/versiondb v0.0.0-00010101000000-000000000000 github.com/ethereum/go-ethereum v1.10.26 github.com/evmos/ethermint v0.0.0-00010101000000-000000000000 + github.com/gogo/protobuf v1.3.2 github.com/golang/protobuf v1.5.3 github.com/gorilla/mux v1.8.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 @@ -107,7 +108,6 @@ require ( github.com/go-stack/stack v1.8.1 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gogo/googleapis v1.4.1 // indirect - github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/glog v1.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/mock v1.6.0 // indirect @@ -186,7 +186,7 @@ require ( github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tendermint/go-amino v0.16.0 // indirect github.com/tendermint/tendermint v0.35.9 // indirect - github.com/tidwall/btree v1.6.0 // indirect + github.com/tidwall/btree v1.7.0 // indirect github.com/tidwall/gjson v1.14.4 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect @@ -237,7 +237,8 @@ replace ( // dgrijalva/jwt-go is deprecated and doesn't receive security updates. // TODO: remove it: https://github.com/cosmos/cosmos-sdk/issues/13134 github.com/dgrijalva/jwt-go => github.com/golang-jwt/jwt/v4 v4.4.2 - github.com/evmos/ethermint => github.com/crypto-org-chain/ethermint v0.6.1-0.20230907031841-3eb35238cb95 + github.com/ethereum/go-ethereum => github.com/evmos/go-ethereum v1.10.26-evmos-rc1 + github.com/evmos/ethermint => github.com/crypto-org-chain/ethermint v0.6.1-0.20230909034938-7f03e19e2b3a // Fix upstream GHSA-h395-qcrw-5vmq and GHSA-3vp4-m3rf-835h vulnerabilities. // TODO Remove it: https://github.com/cosmos/cosmos-sdk/issues/10409 github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.9.0 diff --git a/go.sum b/go.sum index d7cf9b0607..7585fd3af5 100644 --- a/go.sum +++ b/go.sum @@ -497,8 +497,8 @@ github.com/crypto-org-chain/cometbft-db v0.0.0-20230412133340-ac70df4b45f6 h1:d4 github.com/crypto-org-chain/cometbft-db v0.0.0-20230412133340-ac70df4b45f6/go.mod h1:hF5aclS++7WrW8USOA3zPeKI0CuzwUD2TPYug25ANlQ= github.com/crypto-org-chain/cosmos-sdk v0.46.0-beta2.0.20230905040840-b3af5590283b h1:d2GOFR3i3BjDlPsmJkp8Gsrt9LK2nq2IVEnE/rMv1Fo= github.com/crypto-org-chain/cosmos-sdk v0.46.0-beta2.0.20230905040840-b3af5590283b/go.mod h1:EHwCeN9IXonsjKcjpS12MqeStdZvIdxt3VYXhus3G3c= -github.com/crypto-org-chain/ethermint v0.6.1-0.20230907031841-3eb35238cb95 h1:rQzF790iJJ9s6WDWfCnwKyjiwe1/x2hFgECEPFCc5U4= -github.com/crypto-org-chain/ethermint v0.6.1-0.20230907031841-3eb35238cb95/go.mod h1:mRvceZZ4cEJwG3km3uhHZ5PejeBsYujI5lhtSD0WToE= +github.com/crypto-org-chain/ethermint v0.6.1-0.20230909034938-7f03e19e2b3a h1:K3ALTHFQ1YpU3cv3tydQK+cNY4IolzPQLJo7bXtEzNs= +github.com/crypto-org-chain/ethermint v0.6.1-0.20230909034938-7f03e19e2b3a/go.mod h1:xT6yzwilauRVhkvhjj7lDzazfgG4ZeUNSoQsuKImitY= github.com/crypto-org-chain/gravity-bridge/module/v2 v2.0.1-0.20230825054824-75403cd90c6e h1:rSTc35OBjjCBx47rHPWBCIHNGPbMnEj8f7fNcK2TjVI= github.com/crypto-org-chain/gravity-bridge/module/v2 v2.0.1-0.20230825054824-75403cd90c6e/go.mod h1:HBaDqlFjlaXJwVQtA7jHejyaA7xwjXI2o6pU/ccP3tE= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= @@ -576,9 +576,9 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= github.com/esimonov/ifshort v1.0.4/go.mod h1:Pe8zjlRrJ80+q2CxHLfEOfTwxCZ4O+MuhcHcfgNWTk0= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/ethereum/go-ethereum v1.10.26 h1:i/7d9RBBwiXCEuyduBQzJw/mKmnvzsN14jqBmytw72s= -github.com/ethereum/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY= +github.com/evmos/go-ethereum v1.10.26-evmos-rc1 h1:8+jrotZVyO0eIJGRGa1Kga3Fo7DjgFUE9rd6A8Yrbcg= +github.com/evmos/go-ethereum v1.10.26-evmos-rc1/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= @@ -1561,8 +1561,8 @@ github.com/tendermint/tm-db v0.6.6/go.mod h1:wP8d49A85B7/erz/r4YbKssKw6ylsO/hKtF github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= github.com/tetafro/godot v1.4.11/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8= -github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg= -github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= +github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= diff --git a/gomod2nix.toml b/gomod2nix.toml index 614a8b69ea..a8bfe93cdd 100644 --- a/gomod2nix.toml +++ b/gomod2nix.toml @@ -215,11 +215,12 @@ schema = 3 version = "v1.1.0" hash = "sha256-LFcJue98awAFkSPRc93tVvon3kWS7AvumrluxxRzt4c=" [mod."github.com/ethereum/go-ethereum"] - version = "v1.10.26" - hash = "sha256-gkMEwJ4rOgn12amD4QpZ4th/10uyTTeoFmpseuKDQPs=" + version = "v1.10.26-evmos-rc1" + hash = "sha256-GgcReGsIIuBE2TabDYqDO9sBGogdVr9RSh4arQzdPnE=" + replaced = "github.com/evmos/go-ethereum" [mod."github.com/evmos/ethermint"] - version = "v0.6.1-0.20230907031841-3eb35238cb95" - hash = "sha256-Y98oAuDhU2OcfL8ZdsSrZO4NynZXWajdTjDlX1zkHfs=" + version = "v0.6.1-0.20230909034938-7f03e19e2b3a" + hash = "sha256-OHmpXopLE+tbuB6C51c9+BpeDQ6bdriGCvRpyopvdrk=" replaced = "github.com/crypto-org-chain/ethermint" [mod."github.com/felixge/httpsnoop"] version = "v1.0.2" @@ -527,8 +528,8 @@ schema = 3 version = "v0.35.9" hash = "sha256-zaBXzkOd+KVMmb1iG4gg1JPeDXlGm8lvRDjN6ojt5ag=" [mod."github.com/tidwall/btree"] - version = "v1.6.0" - hash = "sha256-H4S46Yk3tVfOtrEhVWUrF4S1yWYmzU43W80HlzS9rcY=" + version = "v1.7.0" + hash = "sha256-bnr6c7a0nqo2HyGqxHk0kEZCEsjLYkPbAVY9WzaZ30o=" [mod."github.com/tidwall/gjson"] version = "v1.14.4" hash = "sha256-3DS2YNL95wG0qSajgRtIABD32J+oblaKVk8LIw+KSOc=" diff --git a/integration_tests/configs/ibc.jsonnet b/integration_tests/configs/ibc.jsonnet index ba9496ac23..f6e8b98a7c 100644 --- a/integration_tests/configs/ibc.jsonnet +++ b/integration_tests/configs/ibc.jsonnet @@ -4,6 +4,7 @@ config { 'cronos_777-1'+: { 'account-prefix': 'crc', 'coin-type': 60, + 'precompiled-contract-address': '0x0000000000000000000000000000000000000065', key_name: 'signer1', accounts: super.accounts[:std.length(super.accounts) - 1] + [super.accounts[std.length(super.accounts) - 1] { coins: super.coins + ',100000000000ibcfee', @@ -26,6 +27,7 @@ config { cmd: 'chain-maind', 'start-flags': '--trace', 'account-prefix': 'cro', + 'coin-type': 394, 'app-config': { 'minimum-gas-prices': '500basecro', }, diff --git a/integration_tests/ibc_utils.py b/integration_tests/ibc_utils.py index aa85b6e92d..86595fce1a 100644 --- a/integration_tests/ibc_utils.py +++ b/integration_tests/ibc_utils.py @@ -1,12 +1,25 @@ +import hashlib import json +import os import subprocess from pathlib import Path from typing import NamedTuple -from pystarport import ports +from pystarport import cluster, ports from .network import Chainmain, Cronos, Hermes, setup_custom_cronos -from .utils import ADDRS, eth_to_bech32, wait_for_port +from .utils import ( + ADDRS, + CONTRACTS, + deploy_contract, + eth_to_bech32, + parse_events, + send_transaction, + setup_token_mapping, + wait_for_fn, + wait_for_new_blocks, + wait_for_port, +) RATIO = 10**10 @@ -14,16 +27,29 @@ class IBCNetwork(NamedTuple): cronos: Cronos chainmain: Chainmain - hermes: Hermes + hermes: Hermes | None incentivized: bool + proc: subprocess.Popen[bytes] | None -def prepare_network(tmp_path, file, incentivized=True, start_relay=True): +def prepare_network( + tmp_path, + file, + incentivized=True, + is_relay=True, + relayer=cluster.Relayer.HERMES.value, +): + is_hermes = relayer == cluster.Relayer.HERMES.value + hermes = None file = f"configs/{file}.jsonnet" - gen = setup_custom_cronos(tmp_path, 26700, Path(__file__).parent / file) + gen = setup_custom_cronos( + tmp_path, + 26700, + Path(__file__).parent / file, + relayer=relayer, + ) cronos = next(gen) chainmain = Chainmain(cronos.base_dir.parent / "chainmain-1") - hermes = Hermes(cronos.base_dir.parent / "relayer.toml") # wait for grpc ready wait_for_port(ports.grpc_port(chainmain.base_port(0))) # chainmain grpc wait_for_port(ports.grpc_port(cronos.base_port(0))) # cronos grpc @@ -37,28 +63,59 @@ def prepare_network(tmp_path, file, incentivized=True, start_relay=True): if incentivized else [] ) - - subprocess.check_call( - [ - "hermes", - "--config", - hermes.configpath, - "create", - "channel", - "--a-port", + path = cronos.base_dir.parent / "relayer" + if is_hermes: + hermes = Hermes(cronos.base_dir.parent / "relayer.toml") + subprocess.check_call( + [ + "hermes", + "--config", + hermes.configpath, + "create", + "channel", + "--a-port", + "transfer", + "--b-port", + "transfer", + "--a-chain", + "cronos_777-1", + "--b-chain", + "chainmain-1", + "--new-client-connection", + "--yes", + ] + + incentivized_args + ) + else: + cmd = [ + "rly", + "pth", + "new", + "chainmain-1", + "cronos_777-1", + "chainmain-cronos", + "--home", + str(path), + ] + subprocess.check_call(cmd) + cmd = [ + "rly", + "tx", + "connect", + "chainmain-cronos", + "--src-port", "transfer", - "--b-port", + "--dst-port", "transfer", - "--a-chain", - "cronos_777-1", - "--b-chain", - "chainmain-1", - "--new-client-connection", - "--yes", + "--order", + "unordered", + "--version", + json.dumps(version), + "--home", + str(path), ] - + incentivized_args - ) - + subprocess.check_call(cmd) + proc = None if incentivized: # register fee payee src_chain = cronos.cosmos_cli() @@ -72,10 +129,27 @@ def prepare_network(tmp_path, file, incentivized=True, start_relay=True): fees="100000000basecro", ) assert rsp["code"] == 0, rsp["raw_log"] - if start_relay: - cronos.supervisorctl("start", "relayer-demo") - wait_for_port(hermes.port) - yield IBCNetwork(cronos, chainmain, hermes, incentivized) + + port = None + if is_relay: + if is_hermes: + cronos.supervisorctl("start", "relayer-demo") + port = hermes.port + else: + proc = subprocess.Popen( + [ + "rly", + "start", + "chainmain-cronos", + "--home", + str(path), + ], + preexec_fn=os.setsid, + ) + port = 5183 + yield IBCNetwork(cronos, chainmain, hermes, incentivized, proc) + if port: + wait_for_port(port) def assert_ready(ibc): @@ -111,3 +185,285 @@ def get_balance(chain, addr, denom): balance = chain.cosmos_cli().balance(addr, denom) print("balance", balance, addr, denom) return balance + + +def get_balances(chain, addr): + return chain.cosmos_cli().balances(addr) + + +def ibc_incentivized_transfer(ibc): + chains = [ibc.cronos.cosmos_cli(), ibc.chainmain.cosmos_cli()] + receiver = chains[1].address("signer2") + sender = chains[0].address("signer2") + relayer = chains[0].address("signer1") + amount = 1000 + fee_denom = "ibcfee" + base_denom = "basetcro" + old_amt_fee = chains[0].balance(relayer, fee_denom) + old_amt_sender_fee = chains[0].balance(sender, fee_denom) + old_amt_sender_base = chains[0].balance(sender, base_denom) + old_amt_receiver_base = chains[1].balance(receiver, "basecro") + assert old_amt_sender_base == 30000000000100000000000 + assert old_amt_receiver_base == 1000000000000000000000 + src_channel = "channel-0" + dst_channel = "channel-0" + rsp = chains[0].ibc_transfer( + sender, + receiver, + f"{amount}{base_denom}", + src_channel, + 1, + "0basecro", + ) + assert rsp["code"] == 0, rsp["raw_log"] + src_chain = ibc.cronos.cosmos_cli() + rsp = src_chain.event_query_tx_for(rsp["txhash"]) + evt = parse_events(rsp["logs"])["send_packet"] + print("packet event", evt) + packet_seq = int(evt["packet_sequence"]) + fee = f"10{fee_denom}" + rsp = chains[0].pay_packet_fee( + "transfer", + src_channel, + packet_seq, + recv_fee=fee, + ack_fee=fee, + timeout_fee=fee, + from_=sender, + ) + assert rsp["code"] == 0, rsp["raw_log"] + rsp = src_chain.event_query_tx_for(rsp["txhash"]) + # fee is locked + assert chains[0].balance(sender, fee_denom) == old_amt_sender_fee - 30 + + # wait for relayer receive the fee + def check_fee(): + amount = chains[0].balance(relayer, fee_denom) + if amount > old_amt_fee: + assert amount == old_amt_fee + 20 + return True + else: + return False + + wait_for_fn("wait for relayer to receive the fee", check_fee) + + # timeout fee is refunded + assert get_balances(ibc.cronos, sender) == [ + {"denom": base_denom, "amount": f"{old_amt_sender_base - amount}"}, + {"denom": fee_denom, "amount": f"{old_amt_sender_fee - 20}"}, + ] + path = f"transfer/{dst_channel}/{base_denom}" + denom_hash = hashlib.sha256(path.encode()).hexdigest().upper() + assert json.loads( + chains[0].raw( + "query", + "ibc-transfer", + "denom-trace", + denom_hash, + node=ibc.chainmain.node_rpc(0), + output="json", + ) + )["denom_trace"] == {"path": f"transfer/{dst_channel}", "base_denom": base_denom} + assert get_balances(ibc.chainmain, receiver) == [ + {"denom": "basecro", "amount": f"{old_amt_receiver_base}"}, + {"denom": f"ibc/{denom_hash}", "amount": f"{amount}"}, + ] + # transfer back + fee_amount = 100000000 + rsp = chains[1].ibc_transfer( + receiver, + sender, + f"{amount}ibc/{denom_hash}", + dst_channel, + 1, + f"{fee_amount}basecro", + ) + assert rsp["code"] == 0, rsp["raw_log"] + + def check_balance_change(): + return chains[0].balance(sender, base_denom) != old_amt_sender_base - amount + + wait_for_fn("balance change", check_balance_change) + assert chains[0].balance(sender, base_denom) == old_amt_sender_base + assert chains[1].balance(receiver, "basecro") == old_amt_receiver_base - fee_amount + return amount + + +def ibc_denom(channel, denom): + h = hashlib.sha256(f"transfer/{channel}/{denom}".encode()).hexdigest().upper() + return f"ibc/{h}" + + +def cronos_transfer_source_tokens(ibc): + # deploy crc21 contract + w3 = ibc.cronos.w3 + contract, denom = setup_token_mapping(ibc.cronos, "TestERC21Source", "DOG") + # send token to crypto.org + print("send to crypto.org") + chainmain_receiver = ibc.chainmain.cosmos_cli().address("signer2") + dest_denom = ibc_denom("channel-0", denom) + amount = 1000 + + # check and record receiver balance + chainmain_receiver_balance = get_balance( + ibc.chainmain, chainmain_receiver, dest_denom + ) + assert chainmain_receiver_balance == 0 + + # send to ibc + tx = contract.functions.send_to_ibc_v2( + chainmain_receiver, amount, 0, b"" + ).build_transaction({"from": ADDRS["validator"]}) + txreceipt = send_transaction(w3, tx) + assert txreceipt.status == 1, "should success" + + # check balance + chainmain_receiver_new_balance = 0 + + def check_chainmain_balance_change(): + nonlocal chainmain_receiver_new_balance + chainmain_receiver_new_balance = get_balance( + ibc.chainmain, chainmain_receiver, dest_denom + ) + chainmain_receiver_all_balance = get_balances(ibc.chainmain, chainmain_receiver) + print("receiver all balance:", chainmain_receiver_all_balance) + return chainmain_receiver_balance != chainmain_receiver_new_balance + + wait_for_fn("check balance change", check_chainmain_balance_change) + assert chainmain_receiver_new_balance == amount + + # check legacy send to ibc + tx = contract.functions.send_to_ibc(chainmain_receiver, 1).build_transaction( + {"from": ADDRS["validator"]} + ) + txreceipt = send_transaction(w3, tx) + assert txreceipt.status == 0, "should fail" + + # send back the token to cronos + # check receiver balance + cronos_balance_before_send = contract.caller.balanceOf(ADDRS["signer2"]) + assert cronos_balance_before_send == 0 + + # send back token through ibc + print("Send back token through ibc") + chainmain_cli = ibc.chainmain.cosmos_cli() + cronos_receiver = eth_to_bech32(ADDRS["signer2"]) + + coin = "1000" + dest_denom + rsp = chainmain_cli.ibc_transfer( + chainmain_receiver, cronos_receiver, coin, "channel-0", 1, "100000000basecro" + ) + assert rsp["code"] == 0, rsp["raw_log"] + + # check contract balance + cronos_balance_after_send = 0 + + def check_contract_balance_change(): + nonlocal cronos_balance_after_send + cronos_balance_after_send = contract.caller.balanceOf(ADDRS["signer2"]) + return cronos_balance_after_send != cronos_balance_before_send + + wait_for_fn("check contract balance change", check_contract_balance_change) + assert cronos_balance_after_send == amount + return amount, contract.address + + +def cronos_transfer_source_tokens_with_proxy(ibc): + w3 = ibc.cronos.w3 + symbol = "TEST" + contract, denom = setup_token_mapping(ibc.cronos, "TestCRC20", symbol) + + # deploy crc20 proxy contract + proxycrc20 = deploy_contract( + w3, + CONTRACTS["TestCRC20Proxy"], + (contract.address, True), + ) + + print("proxycrc20 contract deployed at address: ", proxycrc20.address) + assert proxycrc20.caller.is_source() + assert proxycrc20.caller.crc20() == contract.address + + cronos_cli = ibc.cronos.cosmos_cli() + # change token mapping + rsp = cronos_cli.update_token_mapping( + denom, proxycrc20.address, symbol, 6, from_="validator" + ) + assert rsp["code"] == 0, rsp["raw_log"] + wait_for_new_blocks(cronos_cli, 1) + + print("check the contract mapping exists now") + rsp = cronos_cli.query_denom_by_contract(proxycrc20.address) + assert rsp["denom"] == denom + + # send token to crypto.org + print("send to crypto.org") + chainmain_receiver = ibc.chainmain.cosmos_cli().address("signer2") + dest_denom = ibc_denom("channel-0", denom) + amount = 1000 + sender = ADDRS["validator"] + + # First we need to approve the proxy contract to move asset + tx = contract.functions.approve(proxycrc20.address, amount).build_transaction( + {"from": sender} + ) + txreceipt = send_transaction(w3, tx) + assert txreceipt.status == 1, "should success" + assert contract.caller.allowance(ADDRS["validator"], proxycrc20.address) == amount + + # check and record receiver balance + chainmain_receiver_balance = get_balance( + ibc.chainmain, chainmain_receiver, dest_denom + ) + assert chainmain_receiver_balance == 0 + + # send to ibc + tx = proxycrc20.functions.send_to_ibc( + chainmain_receiver, amount, 0, b"" + ).build_transaction({"from": sender}) + txreceipt = send_transaction(w3, tx) + print(txreceipt) + assert txreceipt.status == 1, "should success" + + # check balance + chainmain_receiver_new_balance = 0 + + def check_chainmain_balance_change(): + nonlocal chainmain_receiver_new_balance + chainmain_receiver_new_balance = get_balance( + ibc.chainmain, chainmain_receiver, dest_denom + ) + chainmain_receiver_all_balance = get_balances(ibc.chainmain, chainmain_receiver) + print("receiver all balance:", chainmain_receiver_all_balance) + return chainmain_receiver_balance != chainmain_receiver_new_balance + + wait_for_fn("check balance change", check_chainmain_balance_change) + assert chainmain_receiver_new_balance == amount + + # send back the token to cronos + # check receiver balance + cronos_balance_before_send = contract.caller.balanceOf(ADDRS["signer2"]) + assert cronos_balance_before_send == 0 + + # send back token through ibc + print("Send back token through ibc") + chainmain_cli = ibc.chainmain.cosmos_cli() + cronos_receiver = eth_to_bech32(ADDRS["signer2"]) + + coin = f"{amount}{dest_denom}" + rsp = chainmain_cli.ibc_transfer( + chainmain_receiver, cronos_receiver, coin, "channel-0", 1, "100000000basecro" + ) + assert rsp["code"] == 0, rsp["raw_log"] + + # check contract balance + cronos_balance_after_send = 0 + + def check_contract_balance_change(): + nonlocal cronos_balance_after_send + cronos_balance_after_send = contract.caller.balanceOf(ADDRS["signer2"]) + return cronos_balance_after_send != cronos_balance_before_send + + wait_for_fn("check contract balance change", check_contract_balance_change) + assert cronos_balance_after_send == amount + return amount, contract.address diff --git a/integration_tests/network.py b/integration_tests/network.py index fb714487f6..893321d72f 100644 --- a/integration_tests/network.py +++ b/integration_tests/network.py @@ -6,7 +6,7 @@ import tomlkit import web3 -from pystarport import ports +from pystarport import cluster, ports from web3.middleware import geth_poa_middleware from .cosmoscli import CosmosCLI @@ -149,7 +149,13 @@ def __init__(self, cronos, geth, contract): def setup_custom_cronos( - path, base_port, config, post_init=None, chain_binary=None, wait_port=True + path, + base_port, + config, + post_init=None, + chain_binary=None, + wait_port=True, + relayer=cluster.Relayer.HERMES.value, ): cmd = [ "pystarport", @@ -162,6 +168,8 @@ def setup_custom_cronos( str(base_port), "--no_remove", ] + if relayer == cluster.Relayer.RLY.value: + cmd = cmd + ["--relayer", str(relayer)] if chain_binary is not None: cmd = cmd[:1] + ["--cmd", chain_binary] + cmd[1:] print(*cmd) diff --git a/integration_tests/poetry.lock b/integration_tests/poetry.lock index df8cb88182..e3220ba463 100644 --- a/integration_tests/poetry.lock +++ b/integration_tests/poetry.lock @@ -1,9 +1,10 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry and should not be changed by hand. [[package]] name = "aiohttp" version = "3.8.5" description = "Async http client/server framework (asyncio)" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -112,6 +113,7 @@ speedups = ["Brotli", "aiodns", "cchardet"] name = "aiosignal" version = "1.2.0" description = "aiosignal: a list of registered asynchronous callbacks" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -126,6 +128,7 @@ frozenlist = ">=1.1.0" name = "async-timeout" version = "4.0.2" description = "Timeout context manager for asyncio programs" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -137,6 +140,7 @@ files = [ name = "atomicwrites" version = "1.4.0" description = "Atomic file writes." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -148,6 +152,7 @@ files = [ name = "attrs" version = "21.4.0" description = "Classes Without Boilerplate" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -165,6 +170,7 @@ tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy" name = "base58" version = "2.1.1" description = "Base58 and Base58Check implementation." +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -179,6 +185,7 @@ tests = ["PyHamcrest (>=2.0.2)", "mypy", "pytest (>=4.6)", "pytest-benchmark", " name = "bech32" version = "1.2.0" description = "Reference implementation for Bech32 and segwit addresses." +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -190,6 +197,7 @@ files = [ name = "bitarray" version = "2.6.0" description = "efficient arrays of booleans -- C extension" +category = "main" optional = false python-versions = "*" files = [ @@ -271,6 +279,7 @@ files = [ name = "black" version = "22.6.0" description = "The uncompromising code formatter." +category = "main" optional = false python-versions = ">=3.6.2" files = [ @@ -317,6 +326,7 @@ uvloop = ["uvloop (>=0.15.2)"] name = "certifi" version = "2023.7.22" description = "Python package for providing Mozilla's CA Bundle." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -328,6 +338,7 @@ files = [ name = "charset-normalizer" version = "2.0.12" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" optional = false python-versions = ">=3.5.0" files = [ @@ -342,6 +353,7 @@ unicode-backport = ["unicodedata2"] name = "click" version = "8.1.2" description = "Composable command line interface toolkit" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -356,6 +368,7 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "colorama" version = "0.4.4" description = "Cross-platform colored terminal text." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -367,6 +380,7 @@ files = [ name = "cprotobuf" version = "0.1.11" description = "pythonic and high performance protocol buffer implementation." +category = "main" optional = false python-versions = "*" files = [ @@ -377,6 +391,7 @@ files = [ name = "cytoolz" version = "0.11.2" description = "Cython implementation of Toolz: High performance functional utilities" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -393,6 +408,7 @@ cython = ["cython"] name = "docker" version = "5.0.3" description = "A Python library for the Docker Engine API." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -413,6 +429,7 @@ tls = ["cryptography (>=3.4.7)", "idna (>=2.0.0)", "pyOpenSSL (>=17.5.0)"] name = "durations" version = "0.3.3" description = "UNKNOWN" +category = "main" optional = false python-versions = "*" files = [ @@ -423,6 +440,7 @@ files = [ name = "eth-abi" version = "3.0.1" description = "eth_abi: Python utilities for working with Ethereum ABI definitions, especially encoding and decoding" +category = "main" optional = false python-versions = ">=3.7, <4" files = [ @@ -446,6 +464,7 @@ tools = ["hypothesis (>=4.18.2,<5.0.0)"] name = "eth-account" version = "0.7.0" description = "eth-account: Sign Ethereum transactions and messages with local private keys" +category = "main" optional = false python-versions = ">=3.6, <4" files = [ @@ -473,6 +492,7 @@ test = ["hypothesis (>=4.18.0,<5)", "pytest (>=6.2.5,<7)", "pytest-xdist", "tox name = "eth-bloom" version = "1.0.4" description = "Python implementation of the Ethereum Trie structure" +category = "main" optional = false python-versions = ">=3.6, <4" files = [ @@ -493,6 +513,7 @@ test = ["hypothesis (==3.7.0)", "pytest (==3.0.7)", "tox (==2.6.0)"] name = "eth-hash" version = "0.3.2" description = "eth-hash: The Ethereum hashing function, keccak256, sometimes (erroneously) called sha3" +category = "main" optional = false python-versions = ">=3.5, <4" files = [ @@ -515,6 +536,7 @@ test = ["pytest (==5.4.1)", "pytest-xdist", "tox (==3.14.6)"] name = "eth-keyfile" version = "0.6.0" description = "A library for handling the encrypted keyfiles used to store ethereum private keys." +category = "main" optional = false python-versions = "*" files = [ @@ -537,6 +559,7 @@ test = ["pytest (>=6.2.5,<7)"] name = "eth-keys" version = "0.4.0" description = "Common API for Ethereum key operations." +category = "main" optional = false python-versions = "*" files = [ @@ -559,6 +582,7 @@ test = ["asn1tools (>=0.146.2,<0.147)", "eth-hash[pycryptodome]", "eth-hash[pysh name = "eth-rlp" version = "0.3.0" description = "eth-rlp: RLP definitions for common Ethereum objects in Python" +category = "main" optional = false python-versions = ">=3.7, <4" files = [ @@ -581,6 +605,7 @@ test = ["eth-hash[pycryptodome]", "pytest (>=6.2.5,<7)", "pytest-xdist", "tox (= name = "eth-typing" version = "3.2.0" description = "eth-typing: Common type annotations for ethereum python packages" +category = "main" optional = false python-versions = ">=3.6, <4" files = [ @@ -598,6 +623,7 @@ test = ["pytest (>=6.2.5,<7)", "pytest-xdist", "tox (>=2.9.1,<3)"] name = "eth-utils" version = "2.0.0" description = "eth-utils: Common utility functions for python code that interacts with Ethereum" +category = "main" optional = false python-versions = ">=3.6,<4" files = [ @@ -621,6 +647,7 @@ test = ["hypothesis (>=4.43.0,<5.0.0)", "pytest (>=6.2.5,<7)", "pytest-xdist", " name = "fire" version = "0.4.0" description = "A library for automatically generating command line interfaces." +category = "main" optional = false python-versions = "*" files = [ @@ -635,6 +662,7 @@ termcolor = "*" name = "flake8" version = "4.0.1" description = "the modular source code checker: pep8 pyflakes and co" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -651,6 +679,7 @@ pyflakes = ">=2.4.0,<2.5.0" name = "flake8-black" version = "0.3.2" description = "flake8 plugin to call black as a code style validator" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -667,6 +696,7 @@ tomli = "*" name = "flake8-isort" version = "4.1.1" description = "flake8 plugin that integrates isort ." +category = "main" optional = false python-versions = "*" files = [ @@ -686,6 +716,7 @@ test = ["pytest-cov"] name = "flake8-polyfill" version = "1.0.2" description = "Polyfill package for Flake8 plugins" +category = "main" optional = false python-versions = "*" files = [ @@ -700,6 +731,7 @@ flake8 = "*" name = "flaky" version = "3.7.0" description = "Plugin for nose or pytest that automatically reruns flaky tests." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -711,6 +743,7 @@ files = [ name = "frozenlist" version = "1.3.0" description = "A list-like structure which implements collections.abc.MutableSequence" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -779,6 +812,7 @@ files = [ name = "grpcio" version = "1.53.0" description = "HTTP/2-based RPC framework" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -836,6 +870,7 @@ protobuf = ["grpcio-tools (>=1.53.0)"] name = "hexbytes" version = "0.2.2" description = "hexbytes: Python `bytes` subclass that decodes hex, with a readable console output" +category = "main" optional = false python-versions = ">=3.6, <4" files = [ @@ -853,6 +888,7 @@ test = ["eth-utils (>=1.0.1,<2)", "hypothesis (>=3.44.24,<4)", "pytest (==5.4.1) name = "idna" version = "3.3" description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -864,6 +900,7 @@ files = [ name = "importlib-resources" version = "5.7.1" description = "Read resources from Python packages" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -882,6 +919,7 @@ testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", name = "iniconfig" version = "1.1.1" description = "iniconfig: brain-dead simple config-ini parsing" +category = "main" optional = false python-versions = "*" files = [ @@ -893,6 +931,7 @@ files = [ name = "ipfshttpclient" version = "0.8.0a2" description = "Python IPFS HTTP CLIENT library" +category = "main" optional = false python-versions = ">=3.6.2,!=3.7.0,!=3.7.1" files = [ @@ -908,6 +947,7 @@ requests = ">=2.11" name = "isort" version = "5.10.1" description = "A Python utility / library to sort Python imports." +category = "main" optional = false python-versions = ">=3.6.1,<4.0" files = [ @@ -925,6 +965,7 @@ requirements-deprecated-finder = ["pip-api", "pipreqs"] name = "jsonmerge" version = "1.8.0" description = "Merge a series of JSON documents." +category = "main" optional = false python-versions = "*" files = [ @@ -938,6 +979,7 @@ jsonschema = "*" name = "jsonnet" version = "0.18.0" description = "Python bindings for Jsonnet - The data templating language" +category = "main" optional = false python-versions = "*" files = [ @@ -948,6 +990,7 @@ files = [ name = "jsonschema" version = "4.4.0" description = "An implementation of JSON Schema validation for Python" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -968,6 +1011,7 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- name = "lru-dict" version = "1.1.7" description = "An Dict like LRU container." +category = "main" optional = false python-versions = "*" files = [ @@ -978,6 +1022,7 @@ files = [ name = "mccabe" version = "0.6.1" description = "McCabe checker, plugin for flake8" +category = "main" optional = false python-versions = "*" files = [ @@ -989,6 +1034,7 @@ files = [ name = "multiaddr" version = "0.0.9" description = "Python implementation of jbenet's multiaddr" +category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" files = [ @@ -1006,6 +1052,7 @@ varint = "*" name = "multidict" version = "6.0.2" description = "multidict implementation" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1074,6 +1121,7 @@ files = [ name = "multitail2" version = "1.5.2" description = "Enables following multiple files for new lines at once, automatically handling file creation/deletion/rotation" +category = "main" optional = false python-versions = "*" files = [ @@ -1087,6 +1135,7 @@ six = "*" name = "mypy-extensions" version = "0.4.3" description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "main" optional = false python-versions = "*" files = [ @@ -1098,6 +1147,7 @@ files = [ name = "netaddr" version = "0.8.0" description = "A network address manipulation library for Python" +category = "main" optional = false python-versions = "*" files = [ @@ -1109,6 +1159,7 @@ files = [ name = "packaging" version = "21.3" description = "Core utilities for Python packages" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1123,6 +1174,7 @@ pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" name = "parsimonious" version = "0.8.1" description = "(Soon to be) the fastest pure-Python PEG parser I could muster" +category = "main" optional = false python-versions = "*" files = [ @@ -1136,6 +1188,7 @@ six = ">=1.9.0" name = "pathspec" version = "0.10.1" description = "Utility library for gitignore style pattern matching of file paths." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1147,6 +1200,7 @@ files = [ name = "pep8-naming" version = "0.11.1" description = "Check PEP-8 naming conventions, plugin for flake8" +category = "main" optional = false python-versions = "*" files = [ @@ -1161,6 +1215,7 @@ flake8-polyfill = ">=1.0.2,<2" name = "platformdirs" version = "2.5.2" description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1176,6 +1231,7 @@ test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock name = "pluggy" version = "1.0.0" description = "plugin and hook calling mechanisms for python" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1191,6 +1247,7 @@ testing = ["pytest", "pytest-benchmark"] name = "protobuf" version = "3.20.1" description = "Protocol Buffers" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1224,6 +1281,7 @@ files = [ name = "py" version = "1.11.0" description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -1235,6 +1293,7 @@ files = [ name = "pycodestyle" version = "2.8.0" description = "Python style guide checker" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -1246,6 +1305,7 @@ files = [ name = "pycryptodome" version = "3.14.1" description = "Cryptographic library for Python" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -1285,6 +1345,7 @@ files = [ name = "pyflakes" version = "2.4.0" description = "passive checker of Python programs" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1296,6 +1357,7 @@ files = [ name = "pyparsing" version = "3.0.8" description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "main" optional = false python-versions = ">=3.6.8" files = [ @@ -1310,6 +1372,7 @@ diagrams = ["jinja2", "railroad-diagrams"] name = "pyrsistent" version = "0.18.1" description = "Persistent/Functional/Immutable data structures" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1340,6 +1403,7 @@ files = [ name = "pysha3" version = "1.0.2" description = "SHA-3 (Keccak) for Python 2.7 - 3.5" +category = "main" optional = false python-versions = "*" files = [ @@ -1370,6 +1434,7 @@ files = [ name = "pystarport" version = "0.2.5" description = "Spawn local devnets for cosmos-sdk chains" +category = "main" optional = false python-versions = "^3.8" files = [] @@ -1393,13 +1458,14 @@ tomlkit = "^0.7.0" [package.source] type = "git" url = "https://github.com/crypto-com/pystarport.git" -reference = "main" -resolved_reference = "b2aa98544958811429948aff3ab4bb3881f56a1f" +reference = "b91b1a5a4e1a3d823542cd5291c4b679b45848c4" +resolved_reference = "b91b1a5a4e1a3d823542cd5291c4b679b45848c4" [[package]] name = "pytest" version = "7.1.1" description = "pytest: simple powerful testing with Python" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1424,6 +1490,7 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2. name = "pytest-github-actions-annotate-failures" version = "0.1.6" description = "pytest plugin to annotate failed tests with a workflow command for GitHub Actions" +category = "main" optional = false python-versions = "*" files = [ @@ -1438,6 +1505,7 @@ pytest = ">=4.0.0" name = "python-dateutil" version = "2.8.2" description = "Extensions to the standard Python datetime module" +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -1452,6 +1520,7 @@ six = ">=1.5" name = "python-dotenv" version = "0.19.2" description = "Read key-value pairs from a .env file and set them as environment variables" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1466,6 +1535,7 @@ cli = ["click (>=5.0)"] name = "pywin32" version = "227" description = "Python for Window Extensions" +category = "main" optional = false python-versions = "*" files = [ @@ -1487,6 +1557,7 @@ files = [ name = "pyyaml" version = "6.0.1" description = "YAML parser and emitter for Python" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1536,6 +1607,7 @@ files = [ name = "pyyaml-include" version = "1.3" description = "Extending PyYAML with a custom constructor for including YAML files within YAML files" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1554,6 +1626,7 @@ toml = ["toml"] name = "requests" version = "2.31.0" description = "Python HTTP for Humans." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1575,6 +1648,7 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "rlp" version = "3.0.0" description = "A package for Recursive Length Prefix encoding and decoding" +category = "main" optional = false python-versions = "*" files = [ @@ -1596,6 +1670,7 @@ test = ["hypothesis (==5.19.0)", "pytest (>=6.2.5,<7)", "tox (>=2.9.1,<3)"] name = "setuptools" version = "65.5.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1612,6 +1687,7 @@ testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs ( name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1623,6 +1699,7 @@ files = [ name = "supervisor" version = "4.2.4" description = "A system for controlling process state under UNIX" +category = "main" optional = false python-versions = "*" files = [ @@ -1640,6 +1717,7 @@ testing = ["pytest", "pytest-cov"] name = "termcolor" version = "1.1.0" description = "ANSII Color formatting for output in terminal." +category = "main" optional = false python-versions = "*" files = [ @@ -1650,6 +1728,7 @@ files = [ name = "testfixtures" version = "6.18.5" description = "A collection of helpers and mock objects for unit tests and doc tests." +category = "main" optional = false python-versions = "*" files = [ @@ -1666,6 +1745,7 @@ test = ["django", "django (<2)", "mock", "pytest (>=3.6)", "pytest-cov", "pytest name = "toml" version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" +category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1677,6 +1757,7 @@ files = [ name = "tomli" version = "2.0.1" description = "A lil' TOML parser" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1688,6 +1769,7 @@ files = [ name = "tomlkit" version = "0.7.2" description = "Style preserving TOML library" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -1699,6 +1781,7 @@ files = [ name = "toolz" version = "0.11.2" description = "List processing tools and functional utilities" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1710,6 +1793,7 @@ files = [ name = "typing-extensions" version = "4.2.0" description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1721,6 +1805,7 @@ files = [ name = "urllib3" version = "1.26.9" description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" files = [ @@ -1737,6 +1822,7 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] name = "varint" version = "1.0.2" description = "Simple python varint implementation" +category = "main" optional = false python-versions = "*" files = [ @@ -1747,6 +1833,7 @@ files = [ name = "web3" version = "6.0.0b6" description = "Web3.py" +category = "main" optional = false python-versions = ">=3.7.2" files = [ @@ -1780,6 +1867,7 @@ tester = ["eth-tester[py-evm] (==v0.7.0-beta.1)", "py-geth (>=3.9.1,<4)"] name = "websocket-client" version = "1.3.2" description = "WebSocket client for Python with low level API options" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1796,6 +1884,7 @@ test = ["websockets"] name = "websockets" version = "10.3" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1853,6 +1942,7 @@ files = [ name = "yarl" version = "1.7.2" description = "Yet another URL library" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1938,6 +2028,7 @@ multidict = ">=4.0" name = "zipp" version = "3.8.0" description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1952,4 +2043,4 @@ testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>= [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "36b192c8992d53d4912762d0a0d319e5f3a31fd341f46ac71ba42612542c6d40" +content-hash = "15ebf191dc54d6bd84cf1e9901491f1d0ad7c105865663cec6b5a1d5abeedcac" diff --git a/integration_tests/pyproject.toml b/integration_tests/pyproject.toml index f3a605e73d..4ca637f39e 100644 --- a/integration_tests/pyproject.toml +++ b/integration_tests/pyproject.toml @@ -20,7 +20,7 @@ python-dateutil = "^2.8.1" web3 = "^6.0.0b6" eth-bloom = "^1.0.4" python-dotenv = "^0.19.2" -pystarport = { git = "https://github.com/crypto-com/pystarport.git", branch = "main" } +pystarport = { git = "https://github.com/crypto-com/pystarport.git", branch = "main", rev = "b91b1a5a4e1a3d823542cd5291c4b679b45848c4" } websockets = "^10.3" toml = "^0.10.2" pysha3 = "^1.0.2" diff --git a/integration_tests/shell.nix b/integration_tests/shell.nix index e7f198dd8d..764bf35e50 100644 --- a/integration_tests/shell.nix +++ b/integration_tests/shell.nix @@ -19,6 +19,7 @@ pkgs.mkShell { pkgs.rocksdb pkgs.chain-maind pkgs.hermes + pkgs.rly ]; shellHook = '' mkdir ./coverage diff --git a/integration_tests/test_ibc.py b/integration_tests/test_ibc.py index e9f3d5e8df..5c8f6e6d1a 100644 --- a/integration_tests/test_ibc.py +++ b/integration_tests/test_ibc.py @@ -1,4 +1,3 @@ -import hashlib import json import pytest @@ -6,8 +5,11 @@ from .ibc_utils import ( RATIO, assert_ready, + cronos_transfer_source_tokens, + cronos_transfer_source_tokens_with_proxy, get_balance, hermes_transfer, + ibc_incentivized_transfer, prepare_network, ) from .utils import ( @@ -15,10 +17,8 @@ CONTRACTS, deploy_contract, eth_to_bech32, - parse_events, parse_events_rpc, send_transaction, - setup_token_mapping, wait_for_fn, wait_for_new_blocks, ) @@ -97,55 +97,7 @@ def test_ibc_incentivized_transfer(ibc): if not ibc.incentivized: # this test case only works for incentivized channel. return - src_chain = ibc.cronos.cosmos_cli() - dst_chain = ibc.chainmain.cosmos_cli() - receiver = dst_chain.address("signer2") - sender = src_chain.address("signer2") - relayer = src_chain.address("signer1") - original_amount = src_chain.balance(relayer, denom="ibcfee") - original_amount_sender = src_chain.balance(sender, denom="ibcfee") - - rsp = src_chain.ibc_transfer( - sender, - receiver, - "1000basetcro", - "channel-0", - 1, - "100000000basecro", - ) - assert rsp["code"] == 0, rsp["raw_log"] - rsp = src_chain.event_query_tx_for(rsp["txhash"]) - evt = parse_events(rsp["logs"])["send_packet"] - print("packet event", evt) - packet_seq = int(evt["packet_sequence"]) - - rsp = src_chain.pay_packet_fee( - "transfer", - "channel-0", - packet_seq, - recv_fee="10ibcfee", - ack_fee="10ibcfee", - timeout_fee="10ibcfee", - from_=sender, - ) - assert rsp["code"] == 0, rsp["raw_log"] - rsp = src_chain.event_query_tx_for(rsp["txhash"]) - # fee is locked - assert src_chain.balance(sender, denom="ibcfee") == original_amount_sender - 30 - - # wait for relayer receive the fee - def check_fee(): - amount = src_chain.balance(relayer, denom="ibcfee") - if amount > original_amount: - assert amount == original_amount + 20 - return True - else: - return False - - wait_for_fn("wait for relayer to receive the fee", check_fee) - - # timeout fee is refunded - assert src_chain.balance(sender, denom="ibcfee") == original_amount_sender - 20 + ibc_incentivized_transfer(ibc) def test_cronos_transfer_tokens(ibc): @@ -254,76 +206,7 @@ def test_cronos_transfer_source_tokens(ibc): test sending crc20 tokens originated from cronos to crypto-org-chain """ assert_ready(ibc) - # deploy crc21 contract - w3 = ibc.cronos.w3 - contract, denom = setup_token_mapping(ibc.cronos, "TestERC21Source", "DOG") - # send token to crypto.org - print("send to crypto.org") - chainmain_receiver = ibc.chainmain.cosmos_cli().address("signer2") - dest_denom = ibc_denom("channel-0", denom) - amount = 1000 - - # check and record receiver balance - chainmain_receiver_balance = get_balance( - ibc.chainmain, chainmain_receiver, dest_denom - ) - assert chainmain_receiver_balance == 0 - - # send to ibc - tx = contract.functions.send_to_ibc_v2( - chainmain_receiver, amount, 0, b"" - ).build_transaction({"from": ADDRS["validator"]}) - txreceipt = send_transaction(w3, tx) - assert txreceipt.status == 1, "should success" - - # check balance - chainmain_receiver_new_balance = 0 - - def check_chainmain_balance_change(): - nonlocal chainmain_receiver_new_balance - chainmain_receiver_new_balance = get_balance( - ibc.chainmain, chainmain_receiver, dest_denom - ) - chainmain_receiver_all_balance = get_balances(ibc.chainmain, chainmain_receiver) - print("receiver all balance:", chainmain_receiver_all_balance) - return chainmain_receiver_balance != chainmain_receiver_new_balance - - wait_for_fn("check balance change", check_chainmain_balance_change) - assert chainmain_receiver_new_balance == amount - - # check legacy send to ibc - tx = contract.functions.send_to_ibc(chainmain_receiver, 1).build_transaction( - {"from": ADDRS["validator"]} - ) - txreceipt = send_transaction(w3, tx) - assert txreceipt.status == 0, "should fail" - - # send back the token to cronos - # check receiver balance - cronos_balance_before_send = contract.caller.balanceOf(ADDRS["signer2"]) - assert cronos_balance_before_send == 0 - - # send back token through ibc - print("Send back token through ibc") - chainmain_cli = ibc.chainmain.cosmos_cli() - cronos_receiver = eth_to_bech32(ADDRS["signer2"]) - - coin = "1000" + dest_denom - rsp = chainmain_cli.ibc_transfer( - chainmain_receiver, cronos_receiver, coin, "channel-0", 1, "100000000basecro" - ) - assert rsp["code"] == 0, rsp["raw_log"] - - # check contract balance - cronos_balance_after_send = 0 - - def check_contract_balance_change(): - nonlocal cronos_balance_after_send - cronos_balance_after_send = contract.caller.balanceOf(ADDRS["signer2"]) - return cronos_balance_after_send != cronos_balance_before_send - - wait_for_fn("check contract balance change", check_contract_balance_change) - assert cronos_balance_after_send == amount + cronos_transfer_source_tokens(ibc) def test_cronos_transfer_source_tokens_with_proxy(ibc): @@ -331,109 +214,7 @@ def test_cronos_transfer_source_tokens_with_proxy(ibc): test sending crc20 tokens originated from cronos to crypto-org-chain """ assert_ready(ibc) - # deploy crc20 contract - w3 = ibc.cronos.w3 - symbol = "TEST" - contract, denom = setup_token_mapping(ibc.cronos, "TestCRC20", symbol) - - # deploy crc20 proxy contract - proxycrc20 = deploy_contract( - w3, - CONTRACTS["TestCRC20Proxy"], - (contract.address, True), - ) - - print("proxycrc20 contract deployed at address: ", proxycrc20.address) - assert proxycrc20.caller.is_source() - assert proxycrc20.caller.crc20() == contract.address - - cronos_cli = ibc.cronos.cosmos_cli() - # change token mapping - rsp = cronos_cli.update_token_mapping( - denom, proxycrc20.address, symbol, 6, from_="validator" - ) - assert rsp["code"] == 0, rsp["raw_log"] - wait_for_new_blocks(cronos_cli, 1) - - print("check the contract mapping exists now") - rsp = cronos_cli.query_denom_by_contract(proxycrc20.address) - assert rsp["denom"] == denom - - # send token to crypto.org - print("send to crypto.org") - chainmain_receiver = ibc.chainmain.cosmos_cli().address("signer2") - dest_denom = ibc_denom("channel-0", denom) - amount = 1000 - sender = ADDRS["validator"] - - # First we need to approve the proxy contract to move asset - tx = contract.functions.approve(proxycrc20.address, amount).build_transaction( - {"from": sender} - ) - txreceipt = send_transaction(w3, tx) - assert txreceipt.status == 1, "should success" - assert contract.caller.allowance(ADDRS["validator"], proxycrc20.address) == amount - - # check and record receiver balance - chainmain_receiver_balance = get_balance( - ibc.chainmain, chainmain_receiver, dest_denom - ) - assert chainmain_receiver_balance == 0 - - # send to ibc - tx = proxycrc20.functions.send_to_ibc( - chainmain_receiver, amount, 0, b"" - ).build_transaction({"from": sender}) - txreceipt = send_transaction(w3, tx) - print(txreceipt) - assert txreceipt.status == 1, "should success" - - # check balance - chainmain_receiver_new_balance = 0 - - def check_chainmain_balance_change(): - nonlocal chainmain_receiver_new_balance - chainmain_receiver_new_balance = get_balance( - ibc.chainmain, chainmain_receiver, dest_denom - ) - chainmain_receiver_all_balance = get_balances(ibc.chainmain, chainmain_receiver) - print("receiver all balance:", chainmain_receiver_all_balance) - return chainmain_receiver_balance != chainmain_receiver_new_balance - - wait_for_fn("check balance change", check_chainmain_balance_change) - assert chainmain_receiver_new_balance == amount - - # send back the token to cronos - # check receiver balance - cronos_balance_before_send = contract.caller.balanceOf(ADDRS["signer2"]) - assert cronos_balance_before_send == 0 - - # send back token through ibc - print("Send back token through ibc") - chainmain_cli = ibc.chainmain.cosmos_cli() - cronos_receiver = eth_to_bech32(ADDRS["signer2"]) - - coin = "1000" + dest_denom - rsp = chainmain_cli.ibc_transfer( - chainmain_receiver, cronos_receiver, coin, "channel-0", 1, "100000000basecro" - ) - assert rsp["code"] == 0, rsp["raw_log"] - - # check contract balance - cronos_balance_after_send = 0 - - def check_contract_balance_change(): - nonlocal cronos_balance_after_send - cronos_balance_after_send = contract.caller.balanceOf(ADDRS["signer2"]) - return cronos_balance_after_send != cronos_balance_before_send - - wait_for_fn("check contract balance change", check_contract_balance_change) - assert cronos_balance_after_send == amount - - -def ibc_denom(channel, denom): - h = hashlib.sha256(f"transfer/{channel}/{denom}".encode()).hexdigest().upper() - return f"ibc/{h}" + cronos_transfer_source_tokens_with_proxy(ibc) def test_ica(ibc, tmp_path): diff --git a/integration_tests/test_ibc_rly.py b/integration_tests/test_ibc_rly.py new file mode 100644 index 0000000000..1a297dd455 --- /dev/null +++ b/integration_tests/test_ibc_rly.py @@ -0,0 +1,101 @@ +import os +import signal +import subprocess + +import pytest +from pystarport import cluster + +from .ibc_utils import ( + RATIO, + cronos_transfer_source_tokens, + cronos_transfer_source_tokens_with_proxy, + get_balance, + ibc_incentivized_transfer, + prepare_network, +) +from .utils import ADDRS, eth_to_bech32, wait_for_fn, wait_for_new_blocks + +cronos_signer2 = ADDRS["signer2"] +src_amount = 10 +src_denom = "basecro" +dst_amount = src_amount * RATIO # the decimal places difference +dst_denom = "basetcro" +channel = "channel-0" + + +@pytest.fixture(scope="module") +def ibc(request, tmp_path_factory): + "prepare-network" + path = tmp_path_factory.mktemp("ibc_rly") + procs = [] + try: + for network in prepare_network( + path, + "ibc", + True, + True, + cluster.Relayer.RLY.value, + ): + if network.proc: + procs.append(network.proc) + print("append:", network.proc) + yield network + finally: + print("finally:", procs) + for proc in procs: + try: + os.killpg(os.getpgid(proc.pid), signal.SIGTERM) + except ProcessLookupError: + pass + # proc.terminate() + proc.wait() + + +def rly_transfer(ibc): + # chainmain-1 -> cronos_777-1 + my_ibc0 = "chainmain-1" + my_ibc1 = "cronos_777-1" + path = ibc.cronos.base_dir.parent / "relayer" + # srcchainid dstchainid amount dst_addr srchannelid + cmd = ( + f"rly tx transfer {my_ibc0} {my_ibc1} {src_amount}{src_denom} " + f"{eth_to_bech32(cronos_signer2)} {channel} " + f"--path chainmain-cronos " + f"--home {str(path)}" + ) + subprocess.run(cmd, check=True, shell=True) + + +def test_ibc(ibc): + # chainmain-1 relayer -> cronos_777-1 signer2 + wait_for_new_blocks(ibc.cronos.cosmos_cli(), 1) + rly_transfer(ibc) + dst_addr = eth_to_bech32(cronos_signer2) + old_dst_balance = get_balance(ibc.cronos, dst_addr, dst_denom) + new_dst_balance = 0 + + def check_balance_change(): + nonlocal new_dst_balance + new_dst_balance = get_balance(ibc.cronos, dst_addr, dst_denom) + return new_dst_balance != old_dst_balance + + wait_for_fn("balance change", check_balance_change) + assert old_dst_balance + dst_amount == new_dst_balance + + +def test_ibc_incentivized_transfer(ibc): + cli = ibc.cronos.cosmos_cli() + wait_for_new_blocks(cli, 1) + ibc_incentivized_transfer(ibc) + + +def test_cronos_transfer_source_tokens(ibc): + cli = ibc.cronos.cosmos_cli() + wait_for_new_blocks(cli, 1) + cronos_transfer_source_tokens(ibc) + + +def test_cronos_transfer_source_tokens_with_proxy(ibc): + cli = ibc.cronos.cosmos_cli() + wait_for_new_blocks(cli, 1) + cronos_transfer_source_tokens_with_proxy(ibc) diff --git a/nix/default.nix b/nix/default.nix index 78e3aa157c..b06c55d6cb 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -61,6 +61,19 @@ import sources.nixpkgs { doCheck = false; }; }) + (_: pkgs: { + rly = pkgs.buildGo120Module rec { + name = "rly"; + src = sources.relayer; + subPackages = [ "." ]; + vendorSha256 = "sha256-Fd1vVVHEeVabsWpfI7yQfmC8T1z+dSDavxwmrKh9MmU="; + doCheck = false; + GOWORK = "off"; + postInstall = '' + mv $out/bin/relayer $out/bin/rly + ''; + }; + }) ]; config = { }; inherit system; diff --git a/nix/sources.json b/nix/sources.json index 37191c00aa..39fe42a303 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -84,6 +84,18 @@ "url": "https://github.com/informalsystems/ibc-rs/archive/daad02843a091bbdb3dd608e5f4ce790895c8845.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, + "relayer": { + "branch": "master", + "description": "An IBC relayer for ibc-go", + "homepage": "https://github.com/crypto-org-chain/relayer", + "owner": "crypto-org-chain", + "repo": "relayer", + "rev": "6be13d112138244fc9dd4f80facd81baa2dac629", + "sha256": "sha256:02pbp0jksh3fvk6g4fqyqp56l15giajf2477898vjcaliqpvzrnr", + "type": "tarball", + "url": "https://github.com/crypto-org-chain/relayer/archive/6be13d112138244fc9dd4f80facd81baa2dac629.tar.gz", + "url_template": "https://github.com///archive/.tar.gz" + }, "niv": { "branch": "master", "description": "Easy dependency management for Nix projects", diff --git a/x/cronos/keeper/precompiles/base_contract.go b/x/cronos/keeper/precompiles/base_contract.go new file mode 100644 index 0000000000..a50e1a2a38 --- /dev/null +++ b/x/cronos/keeper/precompiles/base_contract.go @@ -0,0 +1,27 @@ +package precompiles + +import ( + "github.com/ethereum/go-ethereum/common" +) + +type Registrable interface { + RegistryKey() common.Address +} + +type BaseContract interface { + Registrable +} + +type baseContract struct { + address common.Address +} + +func NewBaseContract(address common.Address) BaseContract { + return &baseContract{ + address: address, + } +} + +func (c *baseContract) RegistryKey() common.Address { + return c.address +} diff --git a/x/cronos/keeper/precompiles/relayer.go b/x/cronos/keeper/precompiles/relayer.go new file mode 100644 index 0000000000..5e28d6ad32 --- /dev/null +++ b/x/cronos/keeper/precompiles/relayer.go @@ -0,0 +1,157 @@ +package precompiles + +import ( + "context" + "encoding/binary" + "errors" + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/evmos/ethermint/x/evm/keeper/precompiles" + + sdk "github.com/cosmos/cosmos-sdk/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + conntypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + chantypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" +) + +// TODO adjust the gas cost +const RelayerContractRequiredGas = 10000 + +var RelayerContractAddress = common.BytesToAddress([]byte{101}) + +type RelayerContract struct { + BaseContract + + cdc codec.Codec + ibcKeeper *ibckeeper.Keeper +} + +func NewRelayerContract(ibcKeeper *ibckeeper.Keeper, cdc codec.Codec) precompiles.StatefulPrecompiledContract { + return &RelayerContract{ + BaseContract: NewBaseContract(RelayerContractAddress), + ibcKeeper: ibcKeeper, + cdc: cdc, + } +} + +func (bc *RelayerContract) Address() common.Address { + return RelayerContractAddress +} + +// RequiredGas calculates the contract gas use +func (bc *RelayerContract) RequiredGas(input []byte) uint64 { + return RelayerContractRequiredGas +} + +func (bc *RelayerContract) IsStateful() bool { + return true +} + +// prefix bytes for the relayer msg type +const ( + prefixSize4Bytes = 4 + // Client + prefixCreateClient = iota + 1 + prefixUpdateClient + prefixUpgradeClient + prefixSubmitMisbehaviour + // Connection + prefixConnectionOpenInit + prefixConnectionOpenTry + prefixConnectionOpenAck + prefixConnectionOpenConfirm + // Channel + prefixChannelOpenInit + prefixChannelOpenTry + prefixChannelOpenAck + prefixChannelOpenConfirm + prefixChannelCloseInit + prefixChannelCloseConfirm + prefixRecvPacket + prefixAcknowledgement + prefixTimeout + prefixTimeoutOnClose +) + +// exec is a generic function that executes the given action in statedb, and marshal/unmarshal the input/output +func exec[Req codec.ProtoMarshaler, Resp codec.ProtoMarshaler]( + bc *RelayerContract, + stateDB precompiles.ExtStateDB, + input []byte, + msg Req, + action func(context.Context, Req) (Resp, error), +) ([]byte, error) { + if err := bc.cdc.Unmarshal(input, msg); err != nil { + return nil, fmt.Errorf("fail to Unmarshal %T", msg) + } + + var res Resp + if err := stateDB.ExecuteNativeAction(func(ctx sdk.Context) error { + var err error + res, err = action(ctx, msg) + return err + }); err != nil { + return nil, err + } + + return bc.cdc.Marshal(res) +} + +func (bc *RelayerContract) Run(evm *vm.EVM, contract *vm.Contract, readonly bool) ([]byte, error) { + if readonly { + return nil, errors.New("the method is not readonly") + } + // parse input + if len(contract.Input) < int(prefixSize4Bytes) { + return nil, errors.New("data too short to contain prefix") + } + prefix := int(binary.LittleEndian.Uint32(contract.Input[:prefixSize4Bytes])) + input := contract.Input[prefixSize4Bytes:] + stateDB := evm.StateDB.(precompiles.ExtStateDB) + + var ( + err error + res []byte + ) + switch prefix { + case prefixCreateClient: + res, err = exec(bc, stateDB, input, new(clienttypes.MsgCreateClient), bc.ibcKeeper.CreateClient) + case prefixUpdateClient: + res, err = exec(bc, stateDB, input, new(clienttypes.MsgUpdateClient), bc.ibcKeeper.UpdateClient) + case prefixUpgradeClient: + res, err = exec(bc, stateDB, input, new(clienttypes.MsgUpgradeClient), bc.ibcKeeper.UpgradeClient) + case prefixSubmitMisbehaviour: + res, err = exec(bc, stateDB, input, new(clienttypes.MsgSubmitMisbehaviour), bc.ibcKeeper.SubmitMisbehaviour) + case prefixConnectionOpenInit: + res, err = exec(bc, stateDB, input, new(conntypes.MsgConnectionOpenInit), bc.ibcKeeper.ConnectionOpenInit) + case prefixConnectionOpenTry: + res, err = exec(bc, stateDB, input, new(conntypes.MsgConnectionOpenTry), bc.ibcKeeper.ConnectionOpenTry) + case prefixConnectionOpenAck: + res, err = exec(bc, stateDB, input, new(conntypes.MsgConnectionOpenAck), bc.ibcKeeper.ConnectionOpenAck) + case prefixConnectionOpenConfirm: + res, err = exec(bc, stateDB, input, new(conntypes.MsgConnectionOpenConfirm), bc.ibcKeeper.ConnectionOpenConfirm) + case prefixChannelOpenInit: + res, err = exec(bc, stateDB, input, new(chantypes.MsgChannelOpenInit), bc.ibcKeeper.ChannelOpenInit) + case prefixChannelOpenTry: + res, err = exec(bc, stateDB, input, new(chantypes.MsgChannelOpenTry), bc.ibcKeeper.ChannelOpenTry) + case prefixChannelOpenAck: + res, err = exec(bc, stateDB, input, new(chantypes.MsgChannelOpenAck), bc.ibcKeeper.ChannelOpenAck) + case prefixChannelOpenConfirm: + res, err = exec(bc, stateDB, input, new(chantypes.MsgChannelOpenConfirm), bc.ibcKeeper.ChannelOpenConfirm) + case prefixRecvPacket: + res, err = exec(bc, stateDB, input, new(chantypes.MsgRecvPacket), bc.ibcKeeper.RecvPacket) + case prefixAcknowledgement: + res, err = exec(bc, stateDB, input, new(chantypes.MsgAcknowledgement), bc.ibcKeeper.Acknowledgement) + case prefixTimeout: + res, err = exec(bc, stateDB, input, new(chantypes.MsgTimeout), bc.ibcKeeper.Timeout) + case prefixTimeoutOnClose: + res, err = exec(bc, stateDB, input, new(chantypes.MsgTimeoutOnClose), bc.ibcKeeper.TimeoutOnClose) + default: + return nil, errors.New("unknown method") + } + return res, err +}