diff --git a/.github/workflows/ci_integration_tests_docker.yml b/.github/workflows/ci_integration_tests_docker.yml new file mode 100644 index 00000000..962908d1 --- /dev/null +++ b/.github/workflows/ci_integration_tests_docker.yml @@ -0,0 +1,59 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python + +name: ci_integration_tests_docker + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-22.04] + + # os: [ubuntu-22.04, macos-latest] + + + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + + - name: Install dependencies + run: make prepare + + - name: Run tests + run: make test + env: + DOCKER: true + DOCKER_CKB_VERSION: "nervos/ckb:v0.202.0-rc1" + +# +#DOCKER = os.getenv('DOCKER',False) +#DOCKER_CKB_VERSION = os.getenv('DOCKER_CKB_VERSION',"nervos/ckb:v0.202.0-rc1") +# - name: Setup upterm session +# if: always() +# uses: lhotari/action-upterm@v1 + + - name: Publish reports + if: failure() + uses: actions/upload-artifact@v4 + with: + name: jfoa-build-reports-${{ runner.os }} + path: ./report diff --git a/Makefile b/Makefile index 4b8bd38f..76fc53ef 100644 --- a/Makefile +++ b/Makefile @@ -22,11 +22,11 @@ develop_prepare: python3 -m download_ckb_light_client echo "install ckb cli" bash develop_prepare.sh +# test_cases/ckb2023 \ test_cases := \ test_cases/replace_rpc \ test_cases/ckb_cli \ - test_cases/ckb2023 \ test_cases/contracts \ test_cases/example \ test_cases/framework \ @@ -41,7 +41,8 @@ test_cases := \ test_cases/config \ test_cases/miner \ test_cases/get_fee_rate_statistics \ - test_cases/ws + test_cases/ws \ + test_cases/memory test: diff --git a/download.py b/download.py index 923ba134..d0ec8a51 100644 --- a/download.py +++ b/download.py @@ -27,6 +27,7 @@ "0.121.0", "0.200.0", "0.201.0", + "0.202.0", ] # Replace with your versions DOWNLOAD_DIR = "download" diff --git a/download_ckb_light_client.py b/download_ckb_light_client.py index 4c7ca3d2..6463d8a0 100644 --- a/download_ckb_light_client.py +++ b/download_ckb_light_client.py @@ -22,6 +22,7 @@ "0.3.5", "0.3.6", "0.4.1", + "0.5.3", ] # Replace with your versions DOWNLOAD_DIR = "download" @@ -34,7 +35,7 @@ "Linux": { "x86_64": { "url": "https://github.com/nervosnetwork/ckb-light-client/releases/download/v{version}/ckb-light-client_v{" - "version}-x86_64-linux.tar.gz", + "version}-x86_64-linux-portable.tar.gz", "ext": ".tar.gz", }, }, diff --git a/framework/basic.py b/framework/basic.py index 80f7fd4d..f47c4d4f 100644 --- a/framework/basic.py +++ b/framework/basic.py @@ -12,6 +12,8 @@ import framework.test_cluster import framework.config import shutil + +from framework.test_node import DOCKER from framework.util import get_project_root @@ -56,3 +58,7 @@ def teardown_method(self, method): f"{get_project_root()}/tmp", f"{get_project_root()}/report/{method.__name__}", ) + + @staticmethod + def skip_docker(): + return DOCKER diff --git a/framework/config.py b/framework/config.py index c22803c4..bfdbb802 100644 --- a/framework/config.py +++ b/framework/config.py @@ -49,6 +49,8 @@ ALWAYS_SUCCESS_CONTRACT_PATH = f"{get_project_root()}/source/contract/always_success" SPAWN_CONTRACT_PATH = f"{get_project_root()}/source/contract/test_cases/spawn_demo" UDT_CONTRACT_PATH = f"{get_project_root()}/source/contract/XUDTType" +SPAWN_ARG_PATH = f"{get_project_root()}/source/contract/spawn_arg_length" +EXEC_ARG_PATH = f"{get_project_root()}/source/contract/exec_arg_length" def get_tmp_path(): diff --git a/framework/helper/ckb_cli.py b/framework/helper/ckb_cli.py index 70971293..7ec52622 100644 --- a/framework/helper/ckb_cli.py +++ b/framework/helper/ckb_cli.py @@ -388,7 +388,7 @@ def tx_add_input(tx_hash, index, tx_file, api_url="http://127.0.0.1:8114"): def tx_add_multisig_config(ckb_address, tx_file, api_url="http://127.0.0.1:8114"): """ - ./ckb-cli tx add-multisig-config --sighash-address ckt1qyqdfjzl8ju2vfwjtl4mttx6me09hayzfldq8m3a0y --tx-file tx.txt + ./ckb-cli tx add-multisig-config --multisig-code-hash legacy --sighash-address ckt1qyqdfjzl8ju2vfwjtl4mttx6me09hayzfldq8m3a0y --tx-file tx.txt status: success MacBook-Pro-4 0.111.0 % cat tx.txt { @@ -421,7 +421,7 @@ def tx_add_multisig_config(ckb_address, tx_file, api_url="http://127.0.0.1:8114" """ cmd = ( - f"export API_URL={api_url} && {cli_path} tx add-multisig-config --sighash-address {ckb_address} " + f"export API_URL={api_url} && {cli_path} tx add-multisig-config --multisig-code-hash legacy --sighash-address {ckb_address} " f"--tx-file {tx_file}" ) return run_command(cmd) diff --git a/framework/helper/contract.py b/framework/helper/contract.py index 5989f1c7..0362184c 100644 --- a/framework/helper/contract.py +++ b/framework/helper/contract.py @@ -302,7 +302,8 @@ def build_invoke_ckb_contract( ) # get input_cell account = util_key_info_by_private_key(account_private) - account_address = account["address"]["testnet"] + net = "testnet" if RPCClient(api_url).get_consensus()["id"] != "ckb" else "mainnet" + account_address = account["address"][net] account_live_cells = wallet_get_live_cells(account_address, api_url=api_url) assert len(account_live_cells["live_cells"]) > 0 input_cell_out_points = [] diff --git a/framework/helper/exec_arg.py b/framework/helper/exec_arg.py new file mode 100644 index 00000000..b3be7f7b --- /dev/null +++ b/framework/helper/exec_arg.py @@ -0,0 +1,61 @@ +from framework.config import EXEC_ARG_PATH +from framework.helper.contract import deploy_ckb_contract, CkbContract +from framework.helper.miner import miner_until_tx_committed +from framework.helper.ckb_cli import util_key_info_by_private_key +from framework.helper.contract import invoke_ckb_contract + +from framework.test_node import CkbNode +from framework.util import ( + ckb_hash_script, + to_big_uint128_le_compatible, + to_int_from_big_uint128_le, +) +from framework.helper.contract import get_ckb_contract_codehash + + +class ExecArgContract(CkbContract): + + def __init__(self, contract_hash=None, contract_tx_index=None): + self.contract_hash = contract_hash + self.contract_tx_index = contract_tx_index + if contract_hash is None: + self.deployed = False + else: + self.deployed = True + self.contract_path = EXEC_ARG_PATH + self.method = {"demo": {"args": "0x", "data": "0x"}} + + def deploy(self, account_private, node: CkbNode): + if self.deployed: + return + self.contract_hash = deploy_ckb_contract( + account_private, self.contract_path, api_url=node.getClient().url + ) + self.contract_tx_index = 0 + miner_until_tx_committed(node, self.contract_hash) + self.deployed = True + + def get_deploy_hash_and_index(self) -> (str, int): + if not self.deployed: + raise Exception("pls deploy first") + return self.contract_hash, self.contract_tx_index + + def get_code_hash(self, type_id, api): + return get_ckb_contract_codehash( + self.contract_hash, self.contract_tx_index, type_id, api + ) + + def get_owner_arg_by_lock_arg(self, lock_arg): + return ckb_hash_script(lock_arg) + + @classmethod + def get_test_data(cls, mb_size, kb_size, byte_size) -> (str, str): + return "0x1234", to_big_uint128_le_compatible( + mb_size * 100000000 + kb_size * 10000 + byte_size + ) + + def get_arg_and_data(self, key) -> (str, str): + if key not in self.method.keys(): + # return "0x0","0x0" + raise Exception("key not exist in method list") + return self.method[key]["args"], self.method[key]["data"] diff --git a/framework/helper/spawn_arg.py b/framework/helper/spawn_arg.py new file mode 100644 index 00000000..2ef556da --- /dev/null +++ b/framework/helper/spawn_arg.py @@ -0,0 +1,61 @@ +from framework.config import SPAWN_ARG_PATH +from framework.helper.contract import deploy_ckb_contract, CkbContract +from framework.helper.miner import miner_until_tx_committed +from framework.helper.ckb_cli import util_key_info_by_private_key +from framework.helper.contract import invoke_ckb_contract + +from framework.test_node import CkbNode +from framework.util import ( + ckb_hash_script, + to_big_uint128_le_compatible, + to_int_from_big_uint128_le, +) +from framework.helper.contract import get_ckb_contract_codehash + + +class SpawnArgContract(CkbContract): + + def __init__(self, contract_hash=None, contract_tx_index=None): + self.contract_hash = contract_hash + self.contract_tx_index = contract_tx_index + if contract_hash is None: + self.deployed = False + else: + self.deployed = True + self.contract_path = SPAWN_ARG_PATH + self.method = {"demo": {"args": "0x", "data": "0x"}} + + def deploy(self, account_private, node: CkbNode): + if self.deployed: + return + self.contract_hash = deploy_ckb_contract( + account_private, self.contract_path, api_url=node.getClient().url + ) + self.contract_tx_index = 0 + miner_until_tx_committed(node, self.contract_hash) + self.deployed = True + + def get_deploy_hash_and_index(self) -> (str, int): + if not self.deployed: + raise Exception("pls deploy first") + return self.contract_hash, self.contract_tx_index + + def get_code_hash(self, type_id, api): + return get_ckb_contract_codehash( + self.contract_hash, self.contract_tx_index, type_id, api + ) + + def get_owner_arg_by_lock_arg(self, lock_arg): + return ckb_hash_script(lock_arg) + + @classmethod + def get_test_data(cls, mb_size, kb_size, byte_size) -> (str, str): + return "0x1234", to_big_uint128_le_compatible( + mb_size * 100000000 + kb_size * 10000 + byte_size + ) + + def get_arg_and_data(self, key) -> (str, str): + if key not in self.method.keys(): + # return "0x0","0x0" + raise Exception("key not exist in method list") + return self.method[key]["args"], self.method[key]["data"] diff --git a/framework/rpc.py b/framework/rpc.py index de2c90e1..8c9a50a9 100644 --- a/framework/rpc.py +++ b/framework/rpc.py @@ -168,6 +168,9 @@ def get_transaction_proof(self, tx_hash, block_hash): def send_transaction(self, tx, outputs_validator="passthrough"): return self.call("send_transaction", [tx, outputs_validator]) + def send_test_transaction(self, tx, outputs_validator="passthrough"): + return self.call("send_test_transaction", [tx, outputs_validator]) + def get_raw_tx_pool(self, verbose=None): return self.call("get_raw_tx_pool", [verbose]) diff --git a/framework/test_light_client.py b/framework/test_light_client.py index 9c3e46c8..b124b5b0 100644 --- a/framework/test_light_client.py +++ b/framework/test_light_client.py @@ -44,9 +44,14 @@ class CkbLightClientConfigPath(Enum): "download/0.4.2/ckb-light-client", ) + V0_5_0 = ( + "source/template/ckb_light_client/0.3.0/testnet.toml.j2", + "download/0.5.0/ckb-light-client", + ) + CURRENT_TEST = ( "source/template/ckb_light_client/0.3.0/testnet.toml.j2", - "download/0.4.1/ckb-light-client", + "download/0.5.3/ckb-light-client", ) def __init__(self, ckb_light_client_config_path, ckb_light_bin_path): diff --git a/framework/test_node.py b/framework/test_node.py index 03fa87cb..9fc83014 100644 --- a/framework/test_node.py +++ b/framework/test_node.py @@ -11,6 +11,11 @@ import shutil import telnetlib from websocket import create_connection, WebSocket +import os + + +DOCKER = os.getenv("DOCKER", False) +DOCKER_CKB_VERSION = os.getenv("DOCKER_CKB_VERSION", "nervos/ckb:v0.202.0-rc1") class CkbNodeConfigPath(Enum): @@ -18,27 +23,34 @@ class CkbNodeConfigPath(Enum): "source/template/ckb/v200/ckb.toml.j2", "source/template/ckb/v200/ckb-miner.toml.j2", "source/template/ckb/v200/specs/dev.toml", - "download/0.201.0", + "download/0.202.0", ) TESTNET = ( "source/template/ckb/v200/ckb.toml.j2", "source/template/ckb/v200/ckb-miner.toml.j2", "source/template/specs/testnet.toml.j2", - "download/0.201.0", + "download/0.202.0", ) CURRENT_MAIN = ( "source/template/ckb/v200/ckb.toml.j2", "source/template/ckb/v200/ckb-miner.toml.j2", "source/template/specs/mainnet.toml.j2", - "download/0.201.0", + "download/0.202.0", ) PREVIEW_DUMMY = ( "source/template/ckb/v200/ckb.toml.j2", "source/template/ckb/v200/ckb-miner.toml.j2", "source/template/specs/preview_dev.toml", - "download/0.201.0", + "download/0.202.0", + ) + + v202 = ( + "source/template/ckb/v200/ckb.toml.j2", + "source/template/ckb/v200/ckb-miner.toml.j2", + "source/template/ckb/v200/specs/dev.toml", + "download/0.202.0", ) v201 = ( @@ -217,8 +229,12 @@ def __init__( self.ckb_pid = -1 self.ckb_miner_pid = -1 self.rpcUrl = "http://{url}".format( - url=self.ckb_config.get("ckb_rpc_listen_address", "127.0.0.1:8114") + url=self.ckb_config.get("ckb_rpc_listen_address", "127.0.0.1:8114").replace( + "0.0.0.0", "127.0.0.1" + ) ) + print("rpcUrl:", self.rpcUrl) + self.client = RPCClient(self.rpcUrl) def __str__(self): @@ -237,6 +253,14 @@ def get_connected_count(self): def connected(self, node): peer_id = node.get_peer_id() peer_address = node.get_peer_address() + peer_address = peer_address.replace( + "127.0.0.1", node.client.url.split(":")[1].replace("//", "") + ) + if DOCKER and self.ckb_config_path == CkbNodeConfigPath.CURRENT_TEST: + peer_address = peer_address.replace("127.0.0.1", "172.17.0.1") + print( + f"add node peer_address:{peer_address} self.ckb_config_path:{self.ckb_config_path}" + ) print("add node response:", self.getClient().add_node(peer_id, peer_address)) def connected_ws(self, node): @@ -275,13 +299,36 @@ def restart(self, config={}, clean_data=False): self.start() def start(self): + time.sleep(1) + if DOCKER and self.ckb_config_path == CkbNodeConfigPath.CURRENT_TEST: + p2p_port = self.ckb_config["ckb_network_listen_addresses"][0].split("/")[-1] + rpc_port = self.ckb_config["ckb_rpc_listen_address"].split(":")[-1] + # --network host + # self.ckb_pid = run_command( + # f"docker run -p {p2p_port}:{p2p_port} -p {rpc_port}:{rpc_port} --add-host=host.docker.internal:host-gateway -v {self.ckb_dir}:/var/lib/ckb nervos/ckb:v0.202.0-rc1 run -C /var/lib/ckb " + # f"--indexer --skip-spec-check > {self.ckb_dir}/node.log 2>&1 &" + # ) + if self.ckb_config.get("ckb_ws_listen_address") != None: + ws_port = self.ckb_config["ckb_ws_listen_address"].split(":")[-1] + tcp_port = self.ckb_config["ckb_tcp_listen_address"].split(":")[-1] + self.ckb_pid = run_command( + f"docker run --name {self.ckb_dir.split('/')[-1]} -p {p2p_port}:{p2p_port} -p {rpc_port}:{rpc_port} -p {ws_port}:{ws_port} -p {tcp_port}:{tcp_port} --network my-network -v {self.ckb_dir}:/var/lib/ckb {DOCKER_CKB_VERSION} run -C /var/lib/ckb " + f"--indexer --skip-spec-check > {self.ckb_dir}/node.log 2>&1 &" + ) + time.sleep(3) + return + self.ckb_pid = run_command( + f"docker run --name {self.ckb_dir.split('/')[-1]} -p {p2p_port}:{p2p_port} -p {rpc_port}:{rpc_port} --network my-network -v {self.ckb_dir}:/var/lib/ckb {DOCKER_CKB_VERSION} run -C /var/lib/ckb " + f"--indexer --skip-spec-check > {self.ckb_dir}/node.log 2>&1 &" + ) + time.sleep(3) + return version = run_command( "cd {ckb_dir} && ./ckb --version".format(ckb_dir=self.ckb_dir) ) print("\n================= CKB Version =================") print(version.strip()) print("===============================================\n") - self.ckb_pid = run_command( "cd {ckb_dir} && ./ckb run --indexer --skip-spec-check > node.log 2>&1 &".format( ckb_dir=self.ckb_dir @@ -307,9 +354,21 @@ def stop(self): self.stop_miner() # run_command("kill {pid}".format(pid=self.ckb_pid)) # self.ckb_pid = -1 + if DOCKER and self.ckb_config_path == CkbNodeConfigPath.CURRENT_TEST: + run_command( + f"docker stop {self.ckb_dir.split('/')[-1]}", check_exit_code=False + ) + run_command( + f"docker rm {self.ckb_dir.split('/')[-1]}", check_exit_code=False + ) + time.sleep(3) + return port = self.rpcUrl.split(":")[-1] - run_command(f"kill $(lsof -t -i:{port})", check_exit_code=False) + run_command( + f"kill $(lsof -i:{port} | grep LISTEN | awk '{{print $2}}')", + check_exit_code=False, + ) self.ckb_pid = -1 time.sleep(3) @@ -405,6 +464,7 @@ def subscribe_telnet(self, topic, other_url=None) -> telnetlib.Telnet: if "ckb_tcp_listen_address" not in self.ckb_config.keys(): raise Exception("not set ckb_ws_listen_address") ckb_tcp_listen_address = self.ckb_config["ckb_tcp_listen_address"] + ckb_tcp_listen_address = ckb_tcp_listen_address.replace("0.0.0.0", "127.0.0.1") if other_url is not None: ckb_tcp_listen_address = other_url # get host @@ -412,6 +472,7 @@ def subscribe_telnet(self, topic, other_url=None) -> telnetlib.Telnet: # get port port = ckb_tcp_listen_address.split(":")[1] # new telnet + print(f"host:{host},port:{port}") tn = telnetlib.Telnet(host, int(port)) print("----") topic_str = ( @@ -436,6 +497,8 @@ def subscribe_websocket(self, topic, other_url=None) -> WebSocket: else: ckb_ws_listen_address = self.ckb_config["ckb_ws_listen_address"] print(ckb_ws_listen_address) + ckb_ws_listen_address = ckb_ws_listen_address.replace("0.0.0.0", "127.0.0.1") + ws = create_connection(f"ws://{ckb_ws_listen_address}") topic_str = ( '{"id": 2, "jsonrpc": "2.0", "method": "subscribe", "params": ["' diff --git a/framework/util.py b/framework/util.py index 8044f953..7b390077 100644 --- a/framework/util.py +++ b/framework/util.py @@ -32,7 +32,7 @@ def get_ckb_configs(p2p_port, rpc_port, spec='{ file = "dev.toml" }'): "ckb_network_listen_addresses": [ "/ip4/0.0.0.0/tcp/{p2p_port}".format(p2p_port=p2p_port) ], - "ckb_rpc_listen_address": "127.0.0.1:{rpc_port}".format(rpc_port=rpc_port), + "ckb_rpc_listen_address": "0.0.0.0:{rpc_port}".format(rpc_port=rpc_port), "ckb_rpc_modules": [ "Net", "Pool", diff --git a/source/contract/exec_arg_length b/source/contract/exec_arg_length new file mode 100755 index 00000000..df30d126 Binary files /dev/null and b/source/contract/exec_arg_length differ diff --git a/source/contract/spawn_arg_length b/source/contract/spawn_arg_length new file mode 100755 index 00000000..b12519c7 Binary files /dev/null and b/source/contract/spawn_arg_length differ diff --git a/source/contract/test_cases/exec_oom b/source/contract/test_cases/exec_oom new file mode 100755 index 00000000..945bc7ef Binary files /dev/null and b/source/contract/test_cases/exec_oom differ diff --git a/source/contract/test_cases/spawn_oom b/source/contract/test_cases/spawn_oom new file mode 100755 index 00000000..76ed6567 Binary files /dev/null and b/source/contract/test_cases/spawn_oom differ diff --git a/test_cases/ckb2023/test_09_ckb_light_client_mainnet.py b/test_cases/ckb2023/test_09_ckb_light_client_mainnet.py new file mode 100644 index 00000000..41e4fc3c --- /dev/null +++ b/test_cases/ckb2023/test_09_ckb_light_client_mainnet.py @@ -0,0 +1,157 @@ +import time + +from framework.basic import CkbTest +from framework.helper.spawn_contract import SpawnContract + +# https://github.com/nervosnetwork/ckb/pull/4807 +# pub const CKB2023_START_EPOCH: u64 = 12_293; + +main_number = 12_293 + + +class CKBTestnet(CkbTest): + + @classmethod + def setup_class(cls): + """ + 1. star 4 node in tmp/cluster/hardFork dir + 2. link ckb node each other + Returns: + + """ + + # 1. star 4 node in tmp/cluster/hardFork dir + nodes = [ + cls.CkbNode.init_dev_by_port( + cls.CkbNodeConfigPath.CURRENT_MAIN, + "cluster/hardFork/node{i}".format(i=i), + 8114 + i, + 8225 + i, + ) + for i in range(1, 5) + ] + cls.cluster = cls.Cluster(nodes) + cls.cluster.prepare_all_nodes( + other_ckb_spec_config={ + "ckb_params_genesis_epoch_length": "1", + "ckb_name": "ckb", + } + ) + cls.cluster.start_all_nodes() + + # 2. link ckb node each other + cls.cluster.connected_all_nodes() + cls.Miner.make_tip_height_number(cls.cluster.ckb_nodes[0], 2000) + + # todo start 2 light client nodes + cls.ckb_light_node_current = cls.CkbLightClientNode.init_by_nodes( + cls.CkbLightClientConfigPath.CURRENT_TEST, + cls.cluster.ckb_nodes, + "tx_pool_light/node1", + 8001, + ) + + cls.ckb_light_node_current.prepare() + cls.ckb_light_node_current.start() + + # 6. wait light sync 2000 block + account = cls.Ckb_cli.util_key_info_by_private_key(cls.Config.MINER_PRIVATE_1) + cls.ckb_light_node_current.getClient().set_scripts( + [ + { + "script": { + "code_hash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", + "hash_type": "type", + "args": account["lock_arg"], + }, + "script_type": "lock", + "block_number": "0x0", + } + ] + ) + + cls.cluster.ckb_nodes[0].start_miner() + cls.Node.wait_light_sync_height(cls.ckb_light_node_current, 2000, 200) + cls.cluster.ckb_nodes[0].stop_miner() + + @classmethod + def teardown_class(cls): + print("\nTeardown TestClass1") + cls.cluster.stop_all_nodes() + cls.cluster.clean_all_nodes() + cls.ckb_light_node_current.stop() + cls.ckb_light_node_current.clean() + + def test_before_and_after_fork(self): + """ + 1. generate_epochs(hex(12_293)) + 2. get_consensus(0048) == 12_293 + 3. get_consensus(0049) == 12_293 + 4. miner with 0x1 + 6. light client invoke spawn + Returns: + + """ + self.cluster.ckb_nodes[0].getClient().get_consensus() + self.Miner.make_tip_height_number(self.cluster.ckb_nodes[0], main_number) + # generate_epochs will cause HeadersIsInvalid + # self.cluster.ckb_nodes[0].client.generate_epochs(hex(12_293)) + + tip_number = self.cluster.ckb_nodes[0].client.get_tip_block_number() + print("tip number:", tip_number) + consensus = self.cluster.ckb_nodes[0].getClient().get_consensus() + res = get_epoch_number_by_consensus_response(consensus, "0048") + assert res == main_number + res = get_epoch_number_by_consensus_response(consensus, "0049") + assert res == main_number + time.sleep(5) + # 0048 miner with other version block + for i in range(20): + self.Miner.miner_with_version(self.cluster.ckb_nodes[0], "0x1") + + # spawn + spawn = SpawnContract() + spawn.deploy(self.Config.MINER_PRIVATE_1, self.cluster.ckb_nodes[0]) + code_tx_hash, code_tx_index = spawn.get_deploy_hash_and_index() + invoke_arg, invoke_data = spawn.get_arg_and_data("demo") + + tip_number = self.cluster.ckb_nodes[0].client.get_tip_block_number() + self.cluster.ckb_nodes[0].start_miner() + self.Node.wait_light_sync_height(self.ckb_light_node_current, tip_number, 200) + self.cluster.ckb_nodes[0].stop_miner() + + tx = self.Contract.build_invoke_ckb_contract( + self.Config.MINER_PRIVATE_1, + code_tx_hash, + code_tx_index, + invoke_arg, + "data2", + invoke_data, + api_url=self.cluster.ckb_nodes[0].getClient().url, + ) + tx_hash = self.ckb_light_node_current.getClient().send_transaction(tx) + self.Miner.miner_until_tx_committed(self.cluster.ckb_nodes[0], tx_hash, True) + tip_number = self.cluster.ckb_nodes[0].getClient().get_tip_block_number() + self.Node.wait_cluster_height(self.cluster, tip_number, 250) + + +def get_epoch_number_by_consensus_response(consensus_response, rfc_name): + """ + get ckb epoch number + "hardfork_features": [ + { "rfc": "0028", "epoch_number": "0x1526" }, + ] + Example: + get_epoch_number_by_consensus_response(consensus_response,"0028") + return int(0x1526,16) + :param consensus_response: rpc get_consensus response + :param rfc_name: example : 0048 + :return: + """ + hardfork_features = consensus_response["hardfork_features"] + return int( + list(filter(lambda obj: rfc_name in obj["rfc"], hardfork_features))[0][ + "epoch_number" + ].replace("0x", ""), + 16, + ) diff --git a/test_cases/config/test_listen_address.py b/test_cases/config/test_listen_address.py new file mode 100644 index 00000000..7cad86a8 --- /dev/null +++ b/test_cases/config/test_listen_address.py @@ -0,0 +1,29 @@ +from framework.basic import CkbTest + + +class TestListenAddress(CkbTest): + + def test_listen_address(self): + """ + fix: fix multi-listen with tcp bind #4889 + Returns: + + """ + self.node = self.CkbNode.init_dev_by_port( + self.CkbNodeConfigPath.CURRENT_TEST, "node/node", 8118, 8119 + ) + self.node.prepare() + self.node.prepare( + other_ckb_config={ + "ckb_network_listen_addresses": [ + "/ip4/0.0.0.0/tcp/8115", + "/ip4/0.0.0.0/tcp/8116", + ], + "ckb_network_reuse_tcp_with_ws": "true", + } + ) + self.node.start() + local_node_info = self.node.getClient().local_node_info() + assert len(local_node_info["addresses"]) == 4 + self.node.stop() + self.node.clean() diff --git a/test_cases/contracts/test_01_contract.py b/test_cases/contracts/test_01_contract.py index 478b0efe..11f83bd2 100644 --- a/test_cases/contracts/test_01_contract.py +++ b/test_cases/contracts/test_01_contract.py @@ -30,6 +30,8 @@ def get_successful_files(): "loop_contract", "exec_with_block_opcode", "rfc49_atomic", + "exec_oom", + "spawn_oom", ] return [s for s in files if not any(s.endswith(suffix) for suffix in files_list)] @@ -50,6 +52,8 @@ def get_failed_files(): "loop_contract", "exec_with_block_opcode", "rfc49_atomic", + "exec_oom", + "spawn_oom", ] # return [s for s in files if not any(s.endswith(suffix) for suffix in files_list)] return [f"{project_root}/source/contract/test_cases/{x}" for x in files_list] @@ -67,11 +71,20 @@ def setup_class(cls): cls.node.prepare() cls.node.start() cls.Miner.make_tip_height_number(cls.node, 2000) + cls.node1 = cls.CkbNode.init_dev_by_port( + cls.CkbNodeConfigPath.v200, "contract/node1", 8116, 8117 + ) + cls.node1.prepare() + cls.node1.start() + cls.node1.connected(cls.node) + cls.Node.wait_node_height(cls.node1, 2000, 200) @classmethod def teardown_class(cls): cls.node.stop() cls.node.clean() + cls.node1.stop() + cls.node1.clean() @parameterized.expand(success_files) # @pytest.mark.skip @@ -80,7 +93,9 @@ def test_01_deploy_and_invoke_demo(self, path): 1. Retrieve the paths of successful files from `project_root/source/contract/test_cases` by excluding the files specified in `files_list`. 2. deploy and invoke contract """ - return self.deploy_and_invoke(self.Config.MINER_PRIVATE_1, path, self.node) + self.deploy_and_invoke(self.Config.MINER_PRIVATE_1, path, self.node) + tip_number = self.node.getClient().get_tip_block_number() + self.Node.wait_node_height(self.node1, tip_number, 200) @parameterized.expand(failed_files) def test_02_deploy_and_invoke_demo_failed(self, path): @@ -94,6 +109,8 @@ def test_02_deploy_and_invoke_demo_failed(self, path): self.fail("Did not raise an exception as expected!") except Exception as e: print(e) + tip_number = self.node.getClient().get_tip_block_number() + self.Node.wait_node_height(self.node1, tip_number, 200) def deploy_and_invoke(self, account, path, node, try_count=5): if try_count < 0: diff --git a/test_cases/contracts/test_exceeded_maximum_cycles.py b/test_cases/contracts/test_exceeded_maximum_cycles.py new file mode 100644 index 00000000..1f5d2f07 --- /dev/null +++ b/test_cases/contracts/test_exceeded_maximum_cycles.py @@ -0,0 +1,52 @@ +import time + +import pytest + +from framework.basic import CkbTest +from framework.util import get_project_root + + +class TestExceededMaximumCycles(CkbTest): + @classmethod + def setup_class(cls): + cls.node = cls.CkbNode.init_dev_by_port( + cls.CkbNodeConfigPath.CURRENT_TEST, "contract/node", 8114, 8115 + ) + cls.node.prepare() + cls.node.start() + cls.Miner.make_tip_height_number(cls.node, 2000) + + @classmethod + def teardown_class(cls): + cls.node.stop() + cls.node.clean() + + def test_01(self): + path = f"{get_project_root()}/source/contract/test_cases/spawn_loop_times" + account = self.Config.MINER_PRIVATE_1 + node = self.node + deploy_hash = self.Contract.deploy_ckb_contract( + account, path, enable_type_id=True, api_url=node.getClient().url + ) + self.Miner.miner_until_tx_committed(node, deploy_hash) + time.sleep(1) + tx = self.Contract.build_invoke_ckb_contract( + account_private=account, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=0, + type_script_arg="0x02", + data="0x1234", + hash_type="type", + api_url=node.getClient().url, + ) + invoke_hash = self.node.getClient().send_test_transaction(tx, "passthrough") + self.node.getClient().get_transaction(invoke_hash) + self.Node.wait_get_transaction(self.node, invoke_hash, "rejected") + + with pytest.raises(Exception) as exc_info: + invoke_hash = self.node.getClient().send_transaction(tx, "passthrough") + + expected_error_message = "ExceededMaximumCycles" + assert ( + expected_error_message in exc_info.value.args[0] + ), f"Expected substring '{expected_error_message}' not found in actual string '{exc_info.value.args[0]}'" diff --git a/test_cases/memory/test_exec_memory_limit.py b/test_cases/memory/test_exec_memory_limit.py new file mode 100644 index 00000000..1f0baed2 --- /dev/null +++ b/test_cases/memory/test_exec_memory_limit.py @@ -0,0 +1,552 @@ +import time + +import pytest + +from framework.basic import CkbTest +from framework.helper.exec_arg import ExecArgContract + + +class MemoryLimitTest(CkbTest): + + @classmethod + def setup_class(cls): + cls.node = cls.CkbNode.init_dev_by_port( + cls.CkbNodeConfigPath.CURRENT_TEST, "contract/node1", 8116, 8115 + ) + cls.node119 = cls.CkbNode.init_dev_by_port( + cls.CkbNodeConfigPath.v120, "contract/node2", 8117, 8118 + ) + cls.node119.prepare() + cls.node119.start() + cls.node.prepare() + cls.node.start() + + cls.node.getClient().generate_epochs("0x2") + cls.node119.connected(cls.node) + cls.execArgContract = ExecArgContract() + cls.execArgContract.deploy(cls.Config.ACCOUNT_PRIVATE_1, cls.node) + deploy_hash, deploy_index = cls.execArgContract.get_deploy_hash_and_index() + print("deploy_hash:", deploy_hash) + print("deploy_index:", deploy_index) + # cls.execArgContract = ExecArgContract("0xafa84968c99c22c6cb6a117f34a012773721e08f5b6c69bdf984b3de6a7efc63", 0) + tip_number = cls.node.getClient().get_tip_block_number() + cls.Node.wait_node_height(cls.node119, tip_number, 100000) + + @classmethod + def teardown_class(cls): + cls.node.stop() + cls.node.clean() + cls.node119.stop() + cls.node119.clean() + # pass + + # @pytest.mark.skip( + # "4. 旧节点出块包含大于1m的交易, 新节点不会同步该block, 也不会ban旧节点 ,等新节点出新块后,旧节点会遵循最长链原则进行同步") + def test_block_err(self): + account2 = self.Ckb_cli.util_key_info_by_private_key( + self.Config.MINER_PRIVATE_1 + ) + deploy_hash, deploy_index = self.execArgContract.get_deploy_hash_and_index() + invoke_arg, invoke_data = self.execArgContract.get_test_data(0, 2013, 111) + with pytest.raises(Exception) as exc_info: + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.ACCOUNT_PRIVATE_2, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="data1", + data=invoke_data, + fee=1000, + api_url=self.node.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + expected_error_message = "@@@VM@@@UNEXPECTED@@@ARGV@@@TOOLONG@@@" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.ACCOUNT_PRIVATE_2, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="data1", + data=invoke_data, + fee=1000, + api_url=self.node119.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + self.Miner.miner_until_tx_committed(self.node119, tx_hash) + tip_number = self.node119.getClient().get_tip_block_number() + self.Node.wait_node_height(self.node, tip_number - 1, 1000) + for i in range(15): + self.Miner.miner_with_version(self.node, "0x0") + tip_number = self.node.getClient().get_tip_block_number() + self.Node.wait_node_height(self.node119, tip_number, 1000) + self.node119.getClient().clear_tx_pool() + for i in range(15): + self.Miner.miner_with_version(self.node, "0x0") + response = self.node119.getClient().get_transaction(tx_hash) + assert response["tx_status"]["status"] == "unknown" + + def test_block_err_type(self): + account2 = self.Ckb_cli.util_key_info_by_private_key( + self.Config.MINER_PRIVATE_1 + ) + deploy_hash, deploy_index = self.execArgContract.get_deploy_hash_and_index() + invoke_arg, invoke_data = self.execArgContract.get_test_data(0, 2013, 111) + with pytest.raises(Exception) as exc_info: + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.ACCOUNT_PRIVATE_2, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + # expected_error_message = "@@@VM@@@UNEXPECTED@@@ARGV@@@TOOLONG@@@" + expected_error_message = "MemOutOfStack" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + def test_tx_pool_err(self): + account2 = self.Ckb_cli.util_key_info_by_private_key( + self.Config.MINER_PRIVATE_1 + ) + deploy_hash, deploy_index = self.execArgContract.get_deploy_hash_and_index() + invoke_arg, invoke_data = self.execArgContract.get_test_data(0, 1013, 111) + with pytest.raises(Exception) as exc_info: + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.MINER_PRIVATE_1, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + # expected_error_message = "@@@VM@@@UNEXPECTED@@@ARGV@@@TOOLONG@@@" + expected_error_message = "MemOutOfStack" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + def test_oom(self): + account2 = self.Ckb_cli.util_key_info_by_private_key( + self.Config.MINER_PRIVATE_1 + ) + deploy_hash, deploy_index = self.execArgContract.get_deploy_hash_and_index() + print("deploy_hash:", deploy_hash) + print("deploy_index:", deploy_index) + invoke_arg, invoke_data = self.execArgContract.get_test_data( + 1024 * 8 * 2, 0, 111 + ) + with pytest.raises(Exception) as exc_info: + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.MINER_PRIVATE_1, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="data1", + data=invoke_data, + fee=1000, + api_url=self.node.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + expected_error_message = "@@@VM@@@UNEXPECTED@@@ARGV@@@TOOLONG@@@" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + def test_oom_type(self): + account2 = self.Ckb_cli.util_key_info_by_private_key( + self.Config.MINER_PRIVATE_1 + ) + deploy_hash, deploy_index = self.execArgContract.get_deploy_hash_and_index() + print("deploy_hash:", deploy_hash) + print("deploy_index:", deploy_index) + invoke_arg, invoke_data = self.execArgContract.get_test_data( + 1024 * 8 * 2, 0, 111 + ) + with pytest.raises(Exception) as exc_info: + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.MINER_PRIVATE_1, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + expected_error_message = "MemOutOfStack" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + def test_MemWriteOnExecutablePage(self): + self.Miner.miner_with_version(self.node, "0x0") + account2 = self.Ckb_cli.util_key_info_by_private_key( + self.Config.MINER_PRIVATE_1 + ) + deploy_hash, deploy_index = self.execArgContract.get_deploy_hash_and_index() + # 合法交易边界 + with pytest.raises(Exception) as exc_info: + invoke_arg, invoke_data = self.execArgContract.get_test_data( + 3, 895 + 32, 93 + ) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.ACCOUNT_PRIVATE_2, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="data1", + data=invoke_data, + fee=1000, + api_url=self.node119.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + # self.node.getClient().test_tx_pool_accept(tx,"passthrough") + self.Miner.miner_until_tx_committed(self.node119, tx_hash) + expected_error_message = "MemWriteOnExecutablePage" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + with pytest.raises(Exception) as exc_info: + invoke_arg, invoke_data = self.execArgContract.get_test_data( + 3, 895 + 32, 93 + ) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.ACCOUNT_PRIVATE_2, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="data1", + data=invoke_data, + fee=1000, + api_url=self.node.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + # self.node.getClient().test_tx_pool_accept(tx,"passthrough") + self.Miner.miner_until_tx_committed(self.node119, tx_hash) + expected_error_message = "@@@VM@@@UNEXPECTED@@@ARGV@@@TOOLONG@@@" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + # + # with pytest.raises(Exception) as exc_info: + # invoke_arg, invoke_data = self.execArgContract.get_test_data(3, 9949, 2300 + 36) + # tx_hash = self.Contract.invoke_ckb_contract( + # account_private=self.Config.ACCOUNT_PRIVATE_2, + # contract_out_point_tx_hash=deploy_hash, + # contract_out_point_tx_index=deploy_index, + # type_script_arg=invoke_arg, + # hash_type="type", + # data=invoke_data, + # fee=1000, + # api_url=self.node119.getClient().url, + # cell_deps=[], + # input_cells=[], + # output_lock_arg=account2["lock_arg"], + # ) + # # self.node.getClient().test_tx_pool_accept(tx,"passthrough") + # self.Miner.miner_until_tx_committed(self.node119, tx_hash) + # expected_error_message = "MemWriteOnExecutablePage" + # assert expected_error_message in exc_info.value.args[0], ( + # f"Expected substring '{expected_error_message}' " + # f"not found in actual string '{exc_info.value.args[0]}'" + # ) + + with pytest.raises(Exception) as exc_info: + invoke_arg, invoke_data = self.execArgContract.get_test_data( + 3, 9949, 2300 + 36 + ) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.ACCOUNT_PRIVATE_2, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="data1", + data=invoke_data, + fee=1000, + api_url=self.node.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + expected_error_message = "@@@VM@@@UNEXPECTED@@@ARGV@@@TOOLONG@@@" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + def test_MemWriteOnExecutablePage_type(self): + self.Miner.miner_with_version(self.node, "0x0") + account2 = self.Ckb_cli.util_key_info_by_private_key( + self.Config.MINER_PRIVATE_1 + ) + deploy_hash, deploy_index = self.execArgContract.get_deploy_hash_and_index() + # 合法交易边界 + with pytest.raises(Exception) as exc_info: + invoke_arg, invoke_data = self.execArgContract.get_test_data( + 3, 895 + 32, 93 + ) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.ACCOUNT_PRIVATE_2, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node119.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + # self.node.getClient().test_tx_pool_accept(tx,"passthrough") + self.Miner.miner_until_tx_committed(self.node119, tx_hash) + expected_error_message = "MemWriteOnExecutablePage" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + with pytest.raises(Exception) as exc_info: + invoke_arg, invoke_data = self.execArgContract.get_test_data( + 3, 895 + 32, 93 + ) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.ACCOUNT_PRIVATE_2, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + # self.node.getClient().test_tx_pool_accept(tx,"passthrough") + self.Miner.miner_until_tx_committed(self.node119, tx_hash) + # expected_error_message = "@@@VM@@@UNEXPECTED@@@ARGV@@@TOOLONG@@@" + expected_error_message = "MemOutOfStack" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + # + # with pytest.raises(Exception) as exc_info: + # invoke_arg, invoke_data = self.execArgContract.get_test_data(3, 9949, 2300 + 36) + # tx_hash = self.Contract.invoke_ckb_contract( + # account_private=self.Config.ACCOUNT_PRIVATE_2, + # contract_out_point_tx_hash=deploy_hash, + # contract_out_point_tx_index=deploy_index, + # type_script_arg=invoke_arg, + # hash_type="type", + # data=invoke_data, + # fee=1000, + # api_url=self.node119.getClient().url, + # cell_deps=[], + # input_cells=[], + # output_lock_arg=account2["lock_arg"], + # ) + # # self.node.getClient().test_tx_pool_accept(tx,"passthrough") + # self.Miner.miner_until_tx_committed(self.node119, tx_hash) + # expected_error_message = "MemWriteOnExecutablePage" + # assert expected_error_message in exc_info.value.args[0], ( + # f"Expected substring '{expected_error_message}' " + # f"not found in actual string '{exc_info.value.args[0]}'" + # ) + + with pytest.raises(Exception) as exc_info: + invoke_arg, invoke_data = self.execArgContract.get_test_data( + 3, 9949, 2300 + 36 + ) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.ACCOUNT_PRIVATE_2, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + # expected_error_message = "@@@VM@@@UNEXPECTED@@@ARGV@@@TOOLONG@@@" + expected_error_message = "MemOutOfStack" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + def test_exec_limit(self): + account2 = self.Ckb_cli.util_key_info_by_private_key( + self.Config.MINER_PRIVATE_1 + ) + deploy_hash, deploy_index = self.execArgContract.get_deploy_hash_and_index() + invoke_arg, invoke_data = self.execArgContract.get_test_data(0, 1012, 12) + print("deploy_hash:", deploy_hash) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.MINER_PRIVATE_1, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="data1", + data=invoke_data, + fee=1000, + api_url=self.node.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + self.Miner.miner_until_tx_committed(self.node, tx_hash) + + with pytest.raises(Exception) as exc_info: + invoke_arg, invoke_data = self.execArgContract.get_test_data(0, 1012, 13) + print("deploy_hash:", deploy_hash) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.MINER_PRIVATE_1, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="data1", + data=invoke_data, + fee=1000, + api_url=self.node.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + self.Miner.miner_until_tx_committed(self.node, tx_hash) + expected_error_message = "@@@VM@@@UNEXPECTED@@@ARGV@@@TOOLONG@@@" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + def test_exec_limit_type(self): + account2 = self.Ckb_cli.util_key_info_by_private_key( + self.Config.MINER_PRIVATE_1 + ) + deploy_hash, deploy_index = self.execArgContract.get_deploy_hash_and_index() + invoke_arg, invoke_data = self.execArgContract.get_test_data(0, 1010, 91) + print("deploy_hash:", deploy_hash) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.MINER_PRIVATE_1, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + self.Miner.miner_until_tx_committed(self.node, tx_hash) + + with pytest.raises(Exception) as exc_info: + invoke_arg, invoke_data = self.execArgContract.get_test_data(0, 1010, 92) + print("deploy_hash:", deploy_hash) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.MINER_PRIVATE_1, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + self.Miner.miner_until_tx_committed(self.node, tx_hash) + # expected_error_message = "@@@VM@@@UNEXPECTED@@@ARGV@@@TOOLONG@@@" + expected_error_message = "MemOutOfStack" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + def deploy_and_invoke(self, account, path, node, try_count=5): + if try_count < 0: + raise Exception("try out of times") + try: + deploy_hash = self.Contract.deploy_ckb_contract( + account, path, enable_type_id=True, api_url=node.getClient().url + ) + self.Miner.miner_until_tx_committed(node, deploy_hash) + time.sleep(1) + beginTime = time.time() + invoke_hash = self.Contract.invoke_ckb_contract( + account_private=account, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=0, + type_script_arg="0x02", + data="0x1234", + hash_type="type", + api_url=node.getClient().url, + ) + cost_time = time.time() - beginTime + print("cost_time:", cost_time) + tx = node.getClient().get_transaction(invoke_hash) + del tx["transaction"]["hash"] + self.node.getClient().clear_tx_pool() + beginTime = time.time() + self.node.getClient().test_tx_pool_accept(tx["transaction"], "passthrough") + cost_time = time.time() - beginTime + print("test_tx_pool_accept cost_time:", cost_time) + return invoke_hash + except Exception as e: + print(e) + if "Resolve failed Dead" in str(e): + try_count -= 1 + for i in range(2): + self.Miner.miner_with_version(node, "0x0") + time.sleep(3) + return self.deploy_and_invoke(account, path, node, try_count) + if "PoolRejectedRBF" in str(e): + try_count -= 1 + for i in range(2): + self.Miner.miner_with_version(node, "0x0") + time.sleep(3) + return self.deploy_and_invoke(account, path, node, try_count) + raise e diff --git a/test_cases/memory/test_oom.py b/test_cases/memory/test_oom.py new file mode 100644 index 00000000..199c9394 --- /dev/null +++ b/test_cases/memory/test_oom.py @@ -0,0 +1,111 @@ +import time + +import pytest + +from framework.basic import CkbTest +from framework.util import get_project_root + + +class TestOom(CkbTest): + + @classmethod + def setup_class(cls): + cls.node = cls.CkbNode.init_dev_by_port( + cls.CkbNodeConfigPath.CURRENT_TEST, "contract/node1", 8116, 8115 + ) + cls.node119 = cls.CkbNode.init_dev_by_port( + cls.CkbNodeConfigPath.v120, "contract/node2", 8117, 8118 + ) + cls.node119.prepare( + # {"ckb_logger_filter": "debug"} + ) + cls.node119.start() + cls.node.prepare( + # {"ckb_logger_filter": "debug"} + ) + cls.node.start() + cls.Miner.make_tip_height_number(cls.node, 2000) + cls.node.getClient().generate_epochs("0x2") + cls.node119.connected(cls.node) + tip_number = cls.node.getClient().get_tip_block_number() + cls.Node.wait_node_height(cls.node119, tip_number, 1000) + + @classmethod + def teardown_class(cls): + cls.node.stop() + cls.node.clean() + cls.node119.stop() + cls.node119.clean() + + def test_spawn(self): + with pytest.raises(Exception) as exc_info: + self.deploy_and_invoke( + self.Config.ACCOUNT_PRIVATE_1, + f"{get_project_root()}/source/contract/test_cases/spawn_oom", + self.node, + "type", + ) + expected_error_message = "MemOutOfStack" + # expected_error_message = "@@@VM@@@UNEXPECTED@@@ARGV@@@TOOLONG@@@" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + def test_exec(self): + with pytest.raises(Exception) as exc_info: + self.deploy_and_invoke( + self.Config.ACCOUNT_PRIVATE_1, + f"{get_project_root()}/source/contract/test_cases/exec_oom", + self.node, + ) + expected_error_message = "@@@VM@@@UNEXPECTED@@@ARGV@@@TOOLONG@@@" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + def deploy_and_invoke(self, account, path, node, hash_type="data1", try_count=5): + if try_count < 0: + raise Exception("try out of times") + try: + deploy_hash = self.Contract.deploy_ckb_contract( + account, path, enable_type_id=True, api_url=node.getClient().url + ) + self.Miner.miner_until_tx_committed(node, deploy_hash) + time.sleep(1) + beginTime = time.time() + invoke_hash = self.Contract.invoke_ckb_contract( + account_private=account, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=0, + type_script_arg="0x02", + data="0x1234", + hash_type=hash_type, + api_url=node.getClient().url, + ) + cost_time = time.time() - beginTime + print("cost_time:", cost_time) + tx = node.getClient().get_transaction(invoke_hash) + del tx["transaction"]["hash"] + self.node.getClient().clear_tx_pool() + beginTime = time.time() + self.node.getClient().test_tx_pool_accept(tx["transaction"], "passthrough") + cost_time = time.time() - beginTime + print("test_tx_pool_accept cost_time:", cost_time) + return invoke_hash + except Exception as e: + print(e) + if "Resolve failed Dead" in str(e): + try_count -= 1 + for i in range(2): + self.Miner.miner_with_version(node, "0x0") + time.sleep(3) + return self.deploy_and_invoke(account, path, node, try_count) + if "PoolRejectedRBF" in str(e): + try_count -= 1 + for i in range(2): + self.Miner.miner_with_version(node, "0x0") + time.sleep(3) + return self.deploy_and_invoke(account, path, node, try_count) + raise e diff --git a/test_cases/memory/test_spawn_memory_limit.py b/test_cases/memory/test_spawn_memory_limit.py new file mode 100644 index 00000000..08f1f885 --- /dev/null +++ b/test_cases/memory/test_spawn_memory_limit.py @@ -0,0 +1,496 @@ +import time + +import pytest + +from framework.basic import CkbTest +from framework.helper.spawn_arg import SpawnArgContract +from framework.util import get_project_root + + +class MemoryLimitTest(CkbTest): + + @classmethod + def setup_class(cls): + cls.node = cls.CkbNode.init_dev_by_port( + cls.CkbNodeConfigPath.CURRENT_TEST, "contract/node1", 8116, 8115 + ) + cls.node119 = cls.CkbNode.init_dev_by_port( + cls.CkbNodeConfigPath.v120, "contract/node2", 8117, 8118 + ) + + cls.node119.prepare() + cls.node119.start() + cls.node.prepare() + cls.node.start() + + cls.Miner.make_tip_height_number(cls.node, 2000) + cls.node.getClient().generate_epochs("0x2") + cls.node119.connected(cls.node) + cls.spawnArgContract = SpawnArgContract() + cls.spawnArgContract.deploy(cls.Config.ACCOUNT_PRIVATE_1, cls.node) + deploy_hash, deploy_index = cls.spawnArgContract.get_deploy_hash_and_index() + print("deploy_hash:", deploy_hash) + print("deploy_index:", deploy_index) + tip_number = cls.node.getClient().get_tip_block_number() + cls.Node.wait_node_height(cls.node119, tip_number, 100000) + # cls.spawnArgContract = SpawnArgContract("0x202222df23f05c3c1d11b24b1e4757877480a1b2c1631c4e0fdc5f38d4625de9", 0) + + @classmethod + def teardown_class(cls): + cls.node.stop() + cls.node.clean() + cls.node119.stop() + cls.node119.clean() + # pass + + def test_spawn(self): + with pytest.raises(Exception) as exc_info: + self.deploy_and_invoke( + self.Config.ACCOUNT_PRIVATE_1, + f"{get_project_root()}/source/contract/test_cases/spawn_oom", + self.node, + ) + # expected_error_message = "@@@VM@@@UNEXPECTED@@@ARGV@@@TOOLONG@@@" + expected_error_message = "MemOutOfStack" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + def test_oom(self): + account2 = self.Ckb_cli.util_key_info_by_private_key( + self.Config.MINER_PRIVATE_1 + ) + deploy_hash, deploy_index = self.spawnArgContract.get_deploy_hash_and_index() + + # oom + with pytest.raises(Exception) as exc_info: + invoke_arg, invoke_data = self.spawnArgContract.get_test_data( + 1024 * 8 * 2, 0, 0 + ) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.MINER_PRIVATE_1, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + # expected_error_message = "@@@VM@@@UNEXPECTED@@@ARGV@@@TOOLONG@@@" + expected_error_message = "MemOutOfStack" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + def test_MemOutOfStack(self): + account2 = self.Ckb_cli.util_key_info_by_private_key( + self.Config.MINER_PRIVATE_1 + ) + deploy_hash, deploy_index = self.spawnArgContract.get_deploy_hash_and_index() + + # 合法交易边界 + invoke_arg, invoke_data = self.spawnArgContract.get_test_data(0, 1013, 111) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.MINER_PRIVATE_1, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node119.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + print(tx_hash) + self.Miner.miner_until_tx_committed(self.node119, tx_hash) + tip_number = self.node.getClient().get_tip_block_number() + self.Node.wait_node_height(self.node, tip_number, 100000) + + # MemOutOfStack 边界 119 + invoke_arg, invoke_data = self.spawnArgContract.get_test_data(0, 1013, 112) + with pytest.raises(Exception) as exc_info: + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.MINER_PRIVATE_1, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node119.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + expected_error_message = "MemOutOfStack" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + # MemOutOfStack 120 + with pytest.raises(Exception) as exc_info: + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.MINER_PRIVATE_1, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + expected_error_message = "MemOutOfStack" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + with pytest.raises(Exception) as exc_info: + invoke_arg, invoke_data = self.spawnArgContract.get_test_data( + 3, 895 + 32, 92 + ) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.MINER_PRIVATE_1, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node119.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + # self.node.getClient().test_tx_pool_accept(tx,"passthrough") + self.Miner.miner_until_tx_committed(self.node119, tx_hash) + expected_error_message = "MemOutOfStack" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + with pytest.raises(Exception) as exc_info: + invoke_arg, invoke_data = self.spawnArgContract.get_test_data( + 3, 895 + 32, 92 + ) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.MINER_PRIVATE_1, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + # self.node.getClient().test_tx_pool_accept(tx,"passthrough") + self.Miner.miner_until_tx_committed(self.node119, tx_hash) + # expected_error_message = "@@@VM@@@UNEXPECTED@@@ARGV@@@TOOLONG@@@" + expected_error_message = "MemOutOfStack" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + def test_bug(self): + """ + 不一致bug + mb_size:3 + kb_size:9949 + byte_size:2336 + 改动分支执行 执行成功 + 120分支:MemWriteOnExecutablePage + Returns: + """ + account2 = self.Ckb_cli.util_key_info_by_private_key( + self.Config.MINER_PRIVATE_1 + ) + deploy_hash, deploy_index = self.spawnArgContract.get_deploy_hash_and_index() + with pytest.raises(Exception) as exc_info: + invoke_arg, invoke_data = self.spawnArgContract.get_test_data( + 3, 9949, 2300 + 36 + ) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.MINER_PRIVATE_1, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + expected_error_message = "MemOutOfStack" + # expected_error_message = "@@@VM@@@UNEXPECTED@@@ARGV@@@TOOLONG@@@" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + def test_MemWriteOnExecutablePage(self): + self.Miner.miner_with_version(self.node, "0x0") + account2 = self.Ckb_cli.util_key_info_by_private_key( + self.Config.MINER_PRIVATE_1 + ) + deploy_hash, deploy_index = self.spawnArgContract.get_deploy_hash_and_index() + + with pytest.raises(Exception) as exc_info: + invoke_arg, invoke_data = self.spawnArgContract.get_test_data( + 3, 895 + 32, 93 + ) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.MINER_PRIVATE_1, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node119.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + # self.node.getClient().test_tx_pool_accept(tx,"passthrough") + self.Miner.miner_until_tx_committed(self.node119, tx_hash) + expected_error_message = "MemWriteOnExecutablePage" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + with pytest.raises(Exception) as exc_info: + invoke_arg, invoke_data = self.spawnArgContract.get_test_data( + 3, 895 + 32, 93 + ) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.MINER_PRIVATE_1, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + # self.node.getClient().test_tx_pool_accept(tx,"passthrough") + self.Miner.miner_until_tx_committed(self.node119, tx_hash) + # expected_error_message = "@@@VM@@@UNEXPECTED@@@ARGV@@@TOOLONG@@@" + expected_error_message = "MemOutOfStack" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + with pytest.raises(Exception) as exc_info: + invoke_arg, invoke_data = self.spawnArgContract.get_test_data( + 3, 9949, 2300 + 36 + ) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.MINER_PRIVATE_1, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node119.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + # self.node.getClient().test_tx_pool_accept(tx,"passthrough") + self.Miner.miner_until_tx_committed(self.node119, tx_hash) + expected_error_message = "MemWriteOnExecutablePage" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + with pytest.raises(Exception) as exc_info: + invoke_arg, invoke_data = self.spawnArgContract.get_test_data( + 3, 9949, 2300 + 36 + ) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.MINER_PRIVATE_1, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + # expected_error_message = "@@@VM@@@UNEXPECTED@@@ARGV@@@TOOLONG@@@" + expected_error_message = "MemOutOfStack" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + def test_MemOutOfBound(self): + account2 = self.Ckb_cli.util_key_info_by_private_key( + self.Config.MINER_PRIVATE_1 + ) + deploy_hash, deploy_index = self.spawnArgContract.get_deploy_hash_and_index() + + with pytest.raises(Exception) as exc_info: + invoke_arg, invoke_data = self.spawnArgContract.get_test_data(4, 0, 0) + print("deploy_hash:", deploy_hash) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.MINER_PRIVATE_1, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + expected_error_message = "MemOutOfStack" + # expected_error_message = "@@@VM@@@UNEXPECTED@@@ARGV@@@TOOLONG@@@" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + with pytest.raises(Exception) as exc_info: + invoke_arg, invoke_data = self.spawnArgContract.get_test_data(4, 0, 0) + print("deploy_hash:", deploy_hash) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.MINER_PRIVATE_1, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node119.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + expected_error_message = "MemOutOfBound" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + def test_argv_limit(self): + account2 = self.Ckb_cli.util_key_info_by_private_key( + self.Config.ACCOUNT_PRIVATE_2 + ) + deploy_hash, deploy_index = self.spawnArgContract.get_deploy_hash_and_index() + invoke_arg, invoke_data = self.spawnArgContract.get_test_data(0, 1014, 8) + print("deploy_hash:", deploy_hash) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.MINER_PRIVATE_1, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + self.Miner.miner_until_tx_committed(self.node, tx_hash) + + with pytest.raises(Exception) as exc_info: + invoke_arg, invoke_data = self.spawnArgContract.get_test_data(0, 1014, 9) + print("deploy_hash:", deploy_hash) + tx_hash = self.Contract.invoke_ckb_contract( + account_private=self.Config.MINER_PRIVATE_1, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=deploy_index, + type_script_arg=invoke_arg, + hash_type="type", + data=invoke_data, + fee=1000, + api_url=self.node.getClient().url, + cell_deps=[], + input_cells=[], + output_lock_arg=account2["lock_arg"], + ) + self.Miner.miner_until_tx_committed(self.node, tx_hash) + + expected_error_message = "MemOutOfStack" + assert expected_error_message in exc_info.value.args[0], ( + f"Expected substring '{expected_error_message}' " + f"not found in actual string '{exc_info.value.args[0]}'" + ) + + def deploy_and_invoke(self, account, path, node, try_count=5): + if try_count < 0: + raise Exception("try out of times") + try: + deploy_hash = self.Contract.deploy_ckb_contract( + account, path, enable_type_id=True, api_url=node.getClient().url + ) + self.Miner.miner_until_tx_committed(node, deploy_hash) + time.sleep(1) + beginTime = time.time() + invoke_hash = self.Contract.invoke_ckb_contract( + account_private=account, + contract_out_point_tx_hash=deploy_hash, + contract_out_point_tx_index=0, + type_script_arg="0x02", + data="0x1234", + hash_type="type", + api_url=node.getClient().url, + ) + cost_time = time.time() - beginTime + print("cost_time:", cost_time) + tx = node.getClient().get_transaction(invoke_hash) + del tx["transaction"]["hash"] + self.node.getClient().clear_tx_pool() + beginTime = time.time() + self.node.getClient().test_tx_pool_accept(tx["transaction"], "passthrough") + cost_time = time.time() - beginTime + print("test_tx_pool_accept cost_time:", cost_time) + return invoke_hash + except Exception as e: + print(e) + if "Resolve failed Dead" in str(e): + try_count -= 1 + for i in range(2): + self.Miner.miner_with_version(node, "0x0") + time.sleep(3) + return self.deploy_and_invoke(account, path, node, try_count) + if "PoolRejectedRBF" in str(e): + try_count -= 1 + for i in range(2): + self.Miner.miner_with_version(node, "0x0") + time.sleep(3) + return self.deploy_and_invoke(account, path, node, try_count) + raise e diff --git a/test_cases/replace_rpc/test_sub_telnet_with_websocket.py b/test_cases/replace_rpc/test_sub_telnet_with_websocket.py index ed1d3348..3a620f92 100644 --- a/test_cases/replace_rpc/test_sub_telnet_with_websocket.py +++ b/test_cases/replace_rpc/test_sub_telnet_with_websocket.py @@ -2,6 +2,7 @@ import time from framework.basic import CkbTest +from framework.test_node import DOCKER class TestTelnetAndWebsocket(CkbTest): @@ -21,8 +22,8 @@ def setup_class(cls): cls.node113.prepare( other_ckb_config={ "ckb_logger_filter": "debug", - "ckb_tcp_listen_address": "127.0.0.1:18116", - "ckb_ws_listen_address": "127.0.0.1:18124", + "ckb_tcp_listen_address": "0.0.0.0:18116", + "ckb_ws_listen_address": "0.0.0.0:18124", } ) cls.node112 = cls.CkbNode.init_dev_by_port( diff --git a/test_cases/replace_rpc/test_telnet.py b/test_cases/replace_rpc/test_telnet.py index 0ae20521..4afb97ef 100644 --- a/test_cases/replace_rpc/test_telnet.py +++ b/test_cases/replace_rpc/test_telnet.py @@ -24,8 +24,8 @@ def setup_class(cls): cls.node113.prepare( other_ckb_config={ "ckb_logger_filter": "debug", - "ckb_tcp_listen_address": "127.0.0.1:18115", - "ckb_ws_listen_address": "127.0.0.1:18124", + "ckb_tcp_listen_address": "0.0.0.0:18115", + "ckb_ws_listen_address": "0.0.0.0:18124", } ) cls.node112 = cls.CkbNode.init_dev_by_port( @@ -62,7 +62,7 @@ def test_link_count_max(self): 4.test 113 max link count """ telnets = [] - for i in range(1000): + for i in range(100): print(i) telnet = self.node112.subscribe_telnet("new_tip_header") telnets.append(telnet) @@ -79,7 +79,7 @@ def test_link_count_max(self): # 1.test 113 max link count telnets = [] - for i in range(10000): + for i in range(100): print(i) telnet = self.node113.subscribe_telnet("new_tip_header") telnets.append(telnet) @@ -101,7 +101,7 @@ def test_link_time_max(self): telnet113 = self.node113.subscribe_telnet("new_tip_header") telnet112 = self.node112.subscribe_telnet("new_tip_header") - for i in range(300): + for i in range(30): self.Miner.miner_with_version(self.node113, "0x0") print("current idx:", i) ret113 = telnet113.read_very_eager() @@ -130,7 +130,10 @@ def test_link_websocket(self): with pytest.raises(Exception) as exc_info: socket = self.node113.subscribe_websocket( - "new_tip_header", self.node113.ckb_config["ckb_tcp_listen_address"] + "new_tip_header", + self.node113.ckb_config["ckb_tcp_listen_address"].replace( + "0.0.0.0", "127.0.0.1" + ), ) expected_error_message = "invalid literal for int() with base 10" assert ( @@ -155,7 +158,7 @@ def test_rpc(self): ), f"Expected substring '{expected_error_message}' not found in actual string '{exc_info.value.args[0]}'" client = self.node113.getClient() - client.url = f"http://{self.node113.ckb_config['ckb_tcp_listen_address']}" + client.url = f"http://{self.node113.ckb_config['ckb_tcp_listen_address'].replace('0.0.0.0','127.0.0.1')}" with pytest.raises(Exception) as exc_info: response = client.call("get_tip_block_number", [], 1) @@ -171,6 +174,8 @@ def test_stop_node_when_link_telnet(self): 2. 113: stop successful 3. assert "ckb" not in ret """ + if self.skip_docker(): + return self.node112.restart() socket = self.node112.subscribe_telnet("new_tip_header") self.node112.stop() @@ -202,7 +207,7 @@ def test_unsubscribe(self): """ client = self.node113.getClient() - client.url = f"http://{self.node113.ckb_config['ckb_rpc_listen_address']}" + client.url = f"http://{self.node113.ckb_config['ckb_rpc_listen_address'].replace('0.0.0.0','127.0.0.1')}" socket = self.node113.subscribe_telnet("new_tip_header") self.Miner.miner_with_version(self.node113, "0x0") ret = socket.read_very_eager() diff --git a/test_cases/replace_rpc/test_websocket.py b/test_cases/replace_rpc/test_websocket.py index c08881fc..b92b2cec 100644 --- a/test_cases/replace_rpc/test_websocket.py +++ b/test_cases/replace_rpc/test_websocket.py @@ -18,17 +18,17 @@ def setup_class(cls): """ cls.node113 = cls.CkbNode.init_dev_by_port( - cls.CkbNodeConfigPath.CURRENT_TEST, "telnet/node", 8114, 8115 + cls.CkbNodeConfigPath.CURRENT_TEST, "telnet2/node", 8114, 8115 ) cls.node113.prepare( other_ckb_config={ "ckb_logger_filter": "debug", - "ckb_tcp_listen_address": "127.0.0.1:18114", - "ckb_ws_listen_address": "127.0.0.1:18124", + "ckb_tcp_listen_address": "0.0.0.0:18114", + "ckb_ws_listen_address": "0.0.0.0:18124", } ) cls.node112 = cls.CkbNode.init_dev_by_port( - cls.CkbNodeConfigPath.V112, "telnet/node2", 8116, 8117 + cls.CkbNodeConfigPath.V112, "telnet2/node2", 8116, 8117 ) cls.node112.prepare( other_ckb_config={ @@ -131,7 +131,7 @@ def test_rpc(self): ), f"Expected substring '{expected_error_message}' not found in actual string '{exc_info.value.args[0]}'" client = self.node113.getClient() - client.url = f"http://{self.node113.ckb_config['ckb_ws_listen_address']}" + client.url = f"http://{self.node113.ckb_config['ckb_ws_listen_address'].replace('0.0.0.0','127.0.0.1')}" response = client.call("get_tip_block_number", [], 1) @@ -174,6 +174,9 @@ def test_stop_node_when_link_websocket(self): 2. 112: stop successful 3. 113: stop failed """ + if self.skip_docker(): + pytest.skip("docker not support lsof") + return self.node112.restart() socket = self.node112.subscribe_websocket("new_tip_header") self.node112.stop()