diff --git a/Makefile b/Makefile index 4b8bd38f..f9355b86 100644 --- a/Makefile +++ b/Makefile @@ -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/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/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_node.py b/framework/test_node.py index 03fa87cb..7780dc79 100644 --- a/framework/test_node.py +++ b/framework/test_node.py @@ -18,27 +18,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 = ( 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/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