diff --git a/download.py b/download.py index b95cfac..d3a494f 100644 --- a/download.py +++ b/download.py @@ -11,8 +11,7 @@ import requests from tqdm import tqdm -versions = ['0.109.0', '0.110.2', '0.111.0', '0.112.1', '0.113.1', "0.114.0", "0.115.0"] # Replace with your versions - +versions = ['0.109.0', '0.110.2', '0.111.0', '0.112.1', '0.113.1', "0.114.0", "0.115.0", "0.116.1"] # Replace with your versions DOWNLOAD_DIR = "download" SYSTEMS = { diff --git a/framework/helper/tx.py b/framework/helper/tx.py index 7e5b98b..cbcb9c1 100644 --- a/framework/helper/tx.py +++ b/framework/helper/tx.py @@ -95,4 +95,108 @@ def build_tx_info(tmp_tx_file): witness = "0x5500000010000000550000005500000041000000" + tx['signatures'][sign_keys][0][2:] tx_msg = tx['transaction'] tx_msg['witnesses'] = [witness] - return tx_msg \ No newline at end of file + return tx_msg + +def build_tx_info_err(tmp_tx_file): + with open(tmp_tx_file, "r") as file: + tx_info_str = file.read() + tx = json.loads(tx_info_str) + sign_keys = list(tx['signatures'].keys())[0] + witness = "0x0500000010000000550000005500000041000000" + tx['signatures'][sign_keys][0][2:] + tx_msg = tx['transaction'] + tx_msg['witnesses'] = [witness] + return tx_msg + +def build_tx_info_err2(tmp_tx_file): + with open(tmp_tx_file, "r") as file: + tx_info_str = file.read() + tx = json.loads(tx_info_str) + tx['transaction']['cell_deps'][0]['out_point']['index'] = '0x11' + sign_keys = list(tx['signatures'].keys())[0] + witness = "0x5500000010000000550000005500000041000000" + tx['signatures'][sign_keys][0][2:] + tx_msg = tx['transaction'] + tx_msg['witnesses'] = [witness] + return tx_msg + +def build_send_transfer_self_tx_with_input_err(input_tx_hash_list, input_tx_index_list, sign_private, data="0x", + + fee=5000, output_count=1, + api_url="http://127.0.0.1:8114", dep_cells=[]): + # tx file init + + tmp_tx_file = f"/tmp/demo{time.time()}-{random.randint(0, 100000000)}.json" + tx_init(tmp_tx_file, api_url) + account = util_key_info_by_private_key(sign_private) + account_address = account["address"]["testnet"] + tx_add_multisig_config(account_address, tmp_tx_file, api_url) + # add input + output_cell_capacity_total = 0 + input_cell_template: any + for i in range(len(input_tx_hash_list)): + input_tx_index = input_tx_index_list[i] + input_tx_hash = input_tx_hash_list[i] + print(f"input_tx_index:{input_tx_index}") + tx_add_input(input_tx_hash, int(input_tx_index, 16), tmp_tx_file, api_url) + # add output + input_cell = RPCClient(api_url).get_transaction(input_tx_hash)["transaction"]["outputs"][ + int(input_tx_index, 16)] + output_cell_capacity = int(int(input_cell["capacity"], 16) - fee) + output_cell_capacity_total += output_cell_capacity + input_cell_template = input_cell + min_output_count = min(int(output_cell_capacity_total / (100 * 100000000)), output_count) + min_output_count = max(min_output_count, 1) + output_cell_capacity = int(output_cell_capacity_total / min_output_count) + for i in range(min_output_count): + tx_add_type_out_put(input_cell_template["lock"]["code_hash"], input_cell_template["lock"]["hash_type"], + input_cell_template["lock"]["args"], + hex(output_cell_capacity), data, tmp_tx_file, False) + for i in range(len(dep_cells)): + tx_add_cell_dep(dep_cells[i]['tx_hash'],dep_cells[i]['index_hex'],tmp_tx_file) + + # sign + sign_data = tx_sign_inputs(sign_private, tmp_tx_file, api_url) + tx_add_signature(sign_data[0]['lock-arg'], sign_data[0]['signature'], tmp_tx_file, api_url) + tx_info(tmp_tx_file, api_url) + # send tx return hash + return build_tx_info_err(tmp_tx_file) + +def build_send_transfer_self_tx_with_input_err2(input_tx_hash_list, input_tx_index_list, sign_private, data="0x", + fee=5000, output_count=1, + api_url="http://127.0.0.1:8114", dep_cells=[]): + # tx file init + + tmp_tx_file = f"/tmp/demo{time.time()}-{random.randint(0, 100000000)}.json" + tx_init(tmp_tx_file, api_url) + account = util_key_info_by_private_key(sign_private) + account_address = account["address"]["testnet"] + tx_add_multisig_config(account_address, tmp_tx_file, api_url) + # add input + output_cell_capacity_total = 0 + input_cell_template: any + for i in range(len(input_tx_hash_list)): + input_tx_index = input_tx_index_list[i] + input_tx_hash = input_tx_hash_list[i] + print(f"input_tx_index:{input_tx_index}") + tx_add_input(input_tx_hash, int(input_tx_index, 16), tmp_tx_file, api_url) + # add output + input_cell = RPCClient(api_url).get_transaction(input_tx_hash)["transaction"]["outputs"][ + int(input_tx_index, 16)] + output_cell_capacity = int(int(input_cell["capacity"], 16) - fee) + output_cell_capacity_total += output_cell_capacity + input_cell_template = input_cell + min_output_count = min(int(output_cell_capacity_total / (100 * 100000000)), output_count) + min_output_count = max(min_output_count, 1) + output_cell_capacity = int(output_cell_capacity_total / min_output_count) + for i in range(min_output_count): + tx_add_type_out_put(input_cell_template["lock"]["code_hash"], input_cell_template["lock"]["hash_type"], + input_cell_template["lock"]["args"], + hex(output_cell_capacity), data, tmp_tx_file, False) + for i in range(len(dep_cells)): + tx_add_cell_dep(dep_cells[i]['tx_hash'],dep_cells[i]['index_hex'],tmp_tx_file) + + # sign + sign_data = tx_sign_inputs(sign_private, tmp_tx_file, api_url) + tx_add_signature(sign_data[0]['lock-arg'], sign_data[0]['signature'], tmp_tx_file, api_url) + tx_info(tmp_tx_file, api_url) + # send tx return hash + return build_tx_info_err2(tmp_tx_file) \ No newline at end of file diff --git a/framework/rpc.py b/framework/rpc.py index 304b9b9..17a7442 100644 --- a/framework/rpc.py +++ b/framework/rpc.py @@ -1,4 +1,5 @@ import time +from typing import Union import requests @@ -177,7 +178,33 @@ def set_network_active(self, state): def remove_transaction(self, tx_hash): return self.call("remove_transaction", [tx_hash]) + def get_live_cell_with_include_tx_pool(self, index, tx_hash, with_data=True, include_tx_pool: Union[bool, None] = None): + """ + over ckb v116.1 version + https://github.com/nervosnetwork/ckb/blob/bb677558efdc3e5f0759556720b62169469b555d/rpc/README.md#chain-get_live_cell + Args: + index: + tx_hash: + with_data:boolean + include_tx_pool:boolean | null + + Returns:CellWithStatus + + """ + return self.call("get_live_cell", [{"index": index, "tx_hash": tx_hash}, with_data, include_tx_pool]) + def get_live_cell(self, index, tx_hash, with_data=True): + """ + under ckb v116.1 version + https://github.com/nervosnetwork/ckb/blob/bb677558efdc3e5f0759556720b62169469b555d/rpc/README.md#chain-get_live_cell + Args: + index: + tx_hash: + with_data:boolean + + Returns:CellWithStatus + + """ return self.call("get_live_cell", [{"index": index, "tx_hash": tx_hash}, with_data]) def submit_block(self, work_id, block): @@ -192,6 +219,9 @@ def get_cells_capacity(self, script): def get_current_epoch(self): return self.call("get_current_epoch", []) + def test_tx_pool_accept(self,tx, outputs_validator): + return self.call("test_tx_pool_accept",[tx, outputs_validator]) + def call(self, method, params, try_count=15): headers = {'content-type': 'application/json'} diff --git a/framework/test_node.py b/framework/test_node.py index 71cbc4c..7405e65 100644 --- a/framework/test_node.py +++ b/framework/test_node.py @@ -11,17 +11,22 @@ class CkbNodeConfigPath(Enum): CURRENT_TEST = ( - "source/template/ckb/v115/ckb.toml.j2", - "source/template/ckb/v115/ckb-miner.toml.j2", - "source/template/ckb/v115/specs/dev.toml", - "download/0.115.0" + "source/template/ckb/v116/ckb.toml.j2", + "source/template/ckb/v116/ckb-miner.toml.j2", + "source/template/ckb/v116/specs/dev.toml", + "download/0.116.1" ) - CURRENT_MAIN = ("source/template/ckb/v115/ckb.toml.j2", - "source/template/ckb/v115/ckb-miner.toml.j2", + CURRENT_MAIN = ("source/template/ckb/v116/ckb.toml.j2", + "source/template/ckb/v116/ckb-miner.toml.j2", "source/template/specs/mainnet.toml.j2", - "download/0.115.0") + "download/0.116.1") + v116 = ( "source/template/ckb/v116/ckb.toml.j2", + "source/template/ckb/v116/ckb-miner.toml.j2", + "source/template/ckb/v116/specs/dev.toml", + "download/0.116.1" + ) v115 = ( "source/template/ckb/v115/ckb.toml.j2", "source/template/ckb/v115/ckb-miner.toml.j2", diff --git a/source/template/ckb/v114/ckb.toml.j2 b/source/template/ckb/v114/ckb.toml.j2 index bf863e1..bd52354 100644 --- a/source/template/ckb/v114/ckb.toml.j2 +++ b/source/template/ckb/v114/ckb.toml.j2 @@ -187,7 +187,7 @@ block_uncles_cache_size = {{ ckb_store_block_uncles_cache_size | default(" # init_tip_hash = "0x8fbd0ec887159d2814cee475911600e3589849670f5ee1ed9798b38fdeef4e44" # # # CKB rich-indexer has its unique configuration. -[indexer_v2.rich_indexer] +#[indexer_v2.rich_indexer] # # By default, it uses an embedded SQLite database. # # Alternatively, you can set up a PostgreSQL database service and provide the connection parameters. # db_type = "postgres" diff --git a/source/template/ckb/v116/ckb-miner.toml.j2 b/source/template/ckb/v116/ckb-miner.toml.j2 new file mode 100644 index 0000000..6c29b12 --- /dev/null +++ b/source/template/ckb/v116/ckb-miner.toml.j2 @@ -0,0 +1,45 @@ +# Config generated by `ckb init --chain dev` + +data_dir = "{{ ckb_miner_data_dir | default(ckb_data_dir) }}" + +[chain] +{# Choose the kind of chains to run, possible values: #} +{# - { file = "specs/dev.toml" } #} +{# - { bundled = "specs/testnet.toml" } #} +{# - { bundled = "specs/mainnet.toml" } #} +spec = {{ ckb_chain_spec }} + + +[logger] +filter = "{{ ckb_miner_logger_filter | default("info") }}" +color = {{ ckb_miner_logger_color | default("true") }} +log_to_file = {{ ckb_miner_logger_log_to_file | default("true") }} +log_to_stdout = {{ ckb_miner_logger_log_to_stdout | default("true") }} + +[sentry] +# set to blank to disable sentry error collection +dsn = "{{ ckb_miner_sentry_dsn | default("") }}" +# if you are willing to help us to improve, +# please leave a way to contact you when we have troubles to reproduce the errors. +# org_contact = "{{ ckb_miner_sentry_org_contact | default() }}" + +[miner.client] +rpc_url = "http://{{ ckb_miner_rpc_url | default("127.0.0.1:8114") }}" +block_on_submit = {{ ckb_miner_block_on_submit | default("true") }} + +# block template polling interval in milliseconds +poll_interval = {{ ckb_miner_poll_interval | default("1000") }} + +#{% if ckb_miner_workers is defined %} +# {% for worker in ckb_miner_workers %} +# [[miner.workers]] +# worker_type = "{{ worker.worker_type }}" +# delay_type = "{{ worker.delay_type }}" +# value = {{ worker.value }} +# {% endfor %} +#{% else %} +[[miner.workers]] +worker_type = "Dummy" +delay_type = "Constant" +value = 1000 +#{% endif %} diff --git a/source/template/ckb/v116/ckb.toml.j2 b/source/template/ckb/v116/ckb.toml.j2 new file mode 100644 index 0000000..968f3ee --- /dev/null +++ b/source/template/ckb/v116/ckb.toml.j2 @@ -0,0 +1,206 @@ +# Config generated by `ckb init --chain dev` + +data_dir = "{{ ckb_data_dir | default("data") }}" + + +[chain] +# Choose the kind of chains to run, possible values: +# - { file = "specs/dev.toml" } +# - { bundled = "specs/testnet.toml" } +# - { bundled = "specs/mainnet.toml" } +spec = {{ ckb_chain_spec }} + + +[logger] +filter = "{{ ckb_logger_filter | default("info") }}" +color = {{ ckb_logger_color | default("true") }} +log_to_file = {{ ckb_logger_log_to_file | default("true") }} +log_to_stdout = {{ ckb_logger_log_to_stdout | default("true") }} + + +[sentry] +# set to blank to disable sentry error collection +dsn = "{{ ckb_sentry_dsn | default("") }}" +# if you are willing to help us to improve, +# please leave a way to contact you when we have troubles to reproduce the errors. +org_contact = "{{ ckb_sentry_org_contact | default("") }}" + + +# # **Experimental** Monitor memory changes. +# [memory_tracker] +# # Seconds between checking the process, 0 is disable, default is 0. +# interval = 600 + +[db] +# The capacity of RocksDB cache, which caches uncompressed data blocks, indexes and filters, default is 128MB. +# Rocksdb will automatically create and use an 8MB internal cache if you set this value to 0. +# To turning off cache, you need to set this value to 0 and set `no_block_cache = true` in the options_file, +# however, we strongly discourage this setting, it may lead to severe performance degradation. +cache_size = {{ ckb_db_cache_size | default("134217728") }} + +# Provide an options file to tune RocksDB for your workload and your system configuration. +# More details can be found in [the official tuning guide](https://github.com/facebook/rocksdb/wiki/RocksDB-Tuning-Guide). +options_file = "{{ ckb_db_options_file | default("default.db-options") }}" + +[network] +listen_addresses = {{ ckb_network_listen_addresses | default(["/ip4/0.0.0.0/tcp/8115"]) | to_json }} +### Specify the public and routable network addresses +public_addresses = {{ ckb_network_public_addresses | default([]) | to_json }} + +# Node connects to nodes listed here to discovery other peers when there's no local stored peers. +# When chain.spec is changed, this usually should also be changed to the bootnodes in the new chain. +bootnodes = {{ ckb_network_bootnodes | default([]) | to_json }} + +### Whitelist-only mode +whitelist_only = {{ ckb_network_whitelist_only | default("false") }} +### Whitelist peers connecting from the given IP addresses +whitelist_peers = {{ ckb_network_whitelist_peers | default([]) | to_json }} +### Enable `SO_REUSEPORT` feature to reuse port on Linux, not supported on other OS yet +# reuse_port_on_linux = true + +max_peers = {{ ckb_network_max_peers | default(125) }} +max_outbound_peers = {{ ckb_network_max_outbound_peers | default(8) }} +# 2 minutes +ping_interval_secs = {{ ckb_network_ping_interval_secs | default(120) }} +# 20 minutes +ping_timeout_secs = {{ ckb_network_ping_timeout_secs | default(1200) }} +connect_outbound_interval_secs = 15 +# If set to true, try to register upnp +upnp = {{ ckb_network_upnp | default("false") }} +# If set to true, network service will add discovered local address to peer store, it's helpful for private net development +discovery_local_address = {{ ckb_network_discovery_local_address | default("true") }} +# If set to true, random cleanup when there are too many inbound nodes +# Ensure that itself can continue to serve as a bootnode node +bootnode_mode = {{ ckb_network_bootnode_mode | default("false") }} + +# Supported protocols list, only "Sync" and "Identify" are mandatory, others are optional +support_protocols = ["Ping", "Discovery", "Identify", "Feeler", "DisconnectMessage", "Sync", "Relay", "Time", "Alert", "LightClient", "Filter"] + +# [network.sync.header_map] +# memory_limit = "600MB" + +[rpc] +# By default RPC only binds to localhost, thus it only allows accessing from the same machine. +# +# Allowing arbitrary machines to access the JSON-RPC port is dangerous and strongly discouraged. +# Please strictly limit the access to only trusted machines. +listen_address = "{{ ckb_rpc_listen_address | default("127.0.0.1:8114") }}" + +# Default is 10MiB = 10 * 1024 * 1024 +max_request_body_size = {{ ckb_rpc_max_request_body_size | default(10485760) }} + +# List of API modules: ["Net", "Pool", "Miner", "Chain", "Stats", "Subscription", "Experiment", "Debug", "Indexer"] +#modules = ["Net", "Pool", "Miner", "Chain", "Stats", "Subscription", "Experiment", "Debug"] +modules = {{ ckb_rpc_modules | to_json }} + +# By default RPC only binds to HTTP service, you can bind it to TCP and WebSocket. +#{% if ckb_tcp_listen_address is defined %} +tcp_listen_address = "{{ ckb_tcp_listen_address }}" +#{% endif %} + +#{% if ckb_ws_listen_address is defined %} +ws_listen_address = "{{ ckb_ws_listen_address }}" +#{% endif %} + +reject_ill_transactions = {{ ckb_rpc_reject_ill_transactions | default("true") }} + +# By default deprecated rpc methods are disabled. +enable_deprecated_rpc = {{ ckb_rpc_enable_deprecated_rpc | default("false") }} + + +[tx_pool] +max_tx_pool_size = {{ ckb_tx_pool_max_tx_pool_size | default("180_000_000") }} +min_fee_rate = {{ ckb_tx_pool_min_fee_rate | default("1_000") }} +max_tx_verify_cycles = {{ ckb_tx_pool_max_tx_verify_cycles | default("70_000_000") }} +max_ancestors_count = {{ ckb_tx_pool_max_ancestors_count | default("25") }} +min_rbf_rate = {{ ckb_tx_pool_min_rbf_rate | default("1_500") }} + + +[store] +header_cache_size = {{ ckb_store_header_cache_size | default("4096")}} +cell_data_cache_size = {{ ckb_store_cell_data_cache_size | default("128")}} +block_proposals_cache_size = {{ ckb_store_block_proposals_cache_size | default("30")}} +block_tx_hashes_cache_size = {{ ckb_store_block_tx_hashes_cache_size | default("30")}} +block_uncles_cache_size = {{ ckb_store_block_uncles_cache_size | default("30")}} + + +# [notify] +# # Execute command when the new tip block changes, first arg is block hash. +# new_block_notify_script = "your_new_block_notify_script.sh" +# # Execute command when node received an network alert, first arg is alert message string. +# network_alert_notify_script = "your_network_alert_notify_script.sh" + + +# Set the lock script to protect mined CKB. +# +# CKB uses CS architecture for miner. Miner process (ckb miner) gets block +# template from the Node process (ckb run) via RPC. Thus the lock script is +# configured in ckb.toml instead of ckb-miner.toml, and the config takes effect +# after restarting Node process. +# +# The `code_hash` identifies different cryptography algorithm. Read the manual +# of the lock script provider about how to generate this config. +# +# CKB provides an secp256k1 implementation, it requires a hash on the +# compressed public key. The hash algorithm is blake2b, with personal +# "ckb-default-hash". The first 160 bits (20 bytes) are used as the only arg. +# +# You can use any tool you trust to generate a Bitcoin private key and public +# key pair, which can be used in CKB as well. CKB CLI provides the function for +# you to convert the public key into block assembler configuration parameters. +# +# Here is an example using ckb-cli to generate an account, this command will +# print the block assembler args(lock_arg) to screen: +# +# ckb-cli account new +# +# If you already have a raw secp256k1 private key, you can get the lock_arg by: +# +# ckb-cli util key-info --privkey-path +# +# The command `ckb init` also accepts options to generate the block assembler +# directly. See `ckb init --help` for details. +# +# ckb init +# +# secp256k1_blake160_sighash_all example: +# [block_assembler] +# code_hash = "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8" +# args = "ckb-cli util blake2b --prefix-160 " +# hash_type = "type" +# message = "A 0x-prefixed hex string" +# # +# # CKB will prepend the binary version to message, to identify the block miner client. (default true, false to disable it) +# use_binary_version_as_message_prefix = true +# # +# # Block assembler will notify new block template through http post to specified endpoints when update +# notify = ["http://127.0.0.1:8888"] +# # Or you may want use more flexible scripts, block template as arg. +# notify_scripts = ["{cmd} {blocktemplate}"] +# +# [indexer_v2] +# # Indexing the pending txs in the ckb tx-pool +# index_tx_pool = false +# # Customize block filtering rules to index only retained blocks +#block_filter = "block.header.number.to_uint() >= \"0x0\".to_uint()" +# # Customize cell filtering rules to index only retained cells +#cell_filter = "let script = output.type;script!=() && script.code_hash == \"0x00000000000000000000000000000000000000000000000000545950455f4944\"" +# # The initial tip can be set higher than the current indexer tip as the starting height for indexing. +# init_tip_hash = "0x8fbd0ec887159d2814cee475911600e3589849670f5ee1ed9798b38fdeef4e44" +# +# # CKB rich-indexer has its unique configuration. +#[indexer_v2.rich_indexer] +# # By default, it uses an embedded SQLite database. +# # Alternatively, you can set up a PostgreSQL database service and provide the connection parameters. +# db_type = "postgres" +# db_name = "ckb-rich-indexer" +# db_host = "127.0.0.1" +# db_port = 5432 +# db_user = "postgres" +# db_password = "123456" + +[block_assembler] +code_hash = "{{ ckb_block_assembler_code_hash }}" +args = "{{ ckb_block_assembler_args }}" +hash_type = "{{ ckb_block_assembler_hash_type }}" +message = "{{ ckb_block_assembler_message }}" diff --git a/source/template/ckb/v116/default.db-options b/source/template/ckb/v116/default.db-options new file mode 100644 index 0000000..bffbdc0 --- /dev/null +++ b/source/template/ckb/v116/default.db-options @@ -0,0 +1,22 @@ +# This is a RocksDB option file. +# +# For detailed file format spec, please refer to the official documents +# in https://rocksdb.org/docs/ +# + +[DBOptions] +bytes_per_sync=1048576 +max_background_jobs=6 +max_total_wal_size=134217728 +keep_log_file_num=32 + +[CFOptions "default"] +level_compaction_dynamic_level_bytes=true +write_buffer_size=8388608 +min_write_buffer_number_to_merge=1 +max_write_buffer_number=2 +max_write_buffer_size_to_maintain=-1 + +[TableOptions/BlockBasedTable "default"] +cache_index_and_filter_blocks=true +pin_l0_filter_and_index_blocks_in_cache=true diff --git a/source/template/ckb/v116/specs/benchmark-spec.toml b/source/template/ckb/v116/specs/benchmark-spec.toml new file mode 100644 index 0000000..e69de29 diff --git a/source/template/ckb/v116/specs/dev.toml b/source/template/ckb/v116/specs/dev.toml new file mode 100644 index 0000000..f25a259 --- /dev/null +++ b/source/template/ckb/v116/specs/dev.toml @@ -0,0 +1,100 @@ +name = "ckb_dev" + +[genesis] +version = 0 +parent_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +timestamp = 0 +compact_target = 0x20010000 +uncles_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +nonce = "0x0" + +[genesis.genesis_cell] +message = "1688032132025" + +[genesis.genesis_cell.lock] +code_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +args = "0x" +hash_type = "data" + +# An array list paths to system cell files, which is absolute or relative to +# the directory containing this config file. +[[genesis.system_cells]] +file = { bundled = "specs/cells/secp256k1_blake160_sighash_all" } +create_type_id = true +capacity = 100_000_0000_0000 +[[genesis.system_cells]] +file = { bundled = "specs/cells/dao" } +create_type_id = true +capacity = 16_000_0000_0000 +[[genesis.system_cells]] +file = { bundled = "specs/cells/secp256k1_data" } +create_type_id = false +capacity = 1_048_617_0000_0000 +[[genesis.system_cells]] +file = { bundled = "specs/cells/secp256k1_blake160_multisig_all" } +create_type_id = true +capacity = 100_000_0000_0000 + +[genesis.system_cells_lock] +code_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +args = "0x" +hash_type = "data" + +# Dep group cells +[[genesis.dep_groups]] +name = "secp256k1_blake160_sighash_all" +files = [ + { bundled = "specs/cells/secp256k1_data" }, + { bundled = "specs/cells/secp256k1_blake160_sighash_all" }, +] +[[genesis.dep_groups]] +name = "secp256k1_blake160_multisig_all" +files = [ + { bundled = "specs/cells/secp256k1_data" }, + { bundled = "specs/cells/secp256k1_blake160_multisig_all" }, +] + +# For first 11 block +[genesis.bootstrap_lock] +code_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +args = "0x" +hash_type = "type" + +# Burn +[[genesis.issued_cells]] +capacity = 8_400_000_000_00000000 +lock.code_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +lock.args = "0x62e907b15cbf27d5425399ebf6f0fb50ebb88f18" +lock.hash_type = "data" + +# issue for random generated private key: d00c06bfd800d27397002dca6fb0993d5ba6399b4238b2f29ee9deb97593d2bc +[[genesis.issued_cells]] +capacity = 20_000_000_000_00000000 +lock.code_hash = "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8" +lock.args = "0xc8328aabcd9b9e8e64fbc566c4385c3bdeb219d7" +lock.hash_type = "type" + +# issue for random generated private key: 63d86723e08f0f813a36ce6aa123bb2289d90680ae1e99d4de8cdb334553f24d +[[genesis.issued_cells]] +capacity = 5_198_735_037_00000000 +lock.code_hash = "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8" +lock.args = "0x470dcdc5e44064909650113a274b3b36aecb6dc7" +lock.hash_type = "type" + +[params] +initial_primary_epoch_reward = 1_917_808_21917808 +secondary_epoch_reward = 613_698_63013698 +max_block_cycles = 10_000_000_000 +cellbase_maturity = 0 +primary_epoch_reward_halving_interval = 8760 +epoch_duration_target = 14400 +genesis_epoch_length = 1000 +# For development and testing purposes only. +# Keep difficulty be permanent if the pow is Dummy. (default: false) +permanent_difficulty_in_dummy = true + +[params.hardfork] +ckb2023 = 1 + +[pow] +func = "Dummy" diff --git a/source/template/ckb/v116/specs/mainnet.toml b/source/template/ckb/v116/specs/mainnet.toml new file mode 100644 index 0000000..daf7edf --- /dev/null +++ b/source/template/ckb/v116/specs/mainnet.toml @@ -0,0 +1,69 @@ +name = "ckb" + +[genesis] +version = 0 +parent_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +timestamp = 1573852190812 +compact_target = 0x1a08a97e +uncles_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +nonce = "0x0" + +[genesis.genesis_cell] +message = "lina 0x18e020f6b1237a3d06b75121f25a7efa0550e4b3f44f974822f471902424c104" + +[genesis.genesis_cell.lock] +code_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +args = "0x" +hash_type = "data" + +# An array list paths to system cell files, which is absolute or relative to +# the directory containing this config file. +[[genesis.system_cells]] +file = { bundled = "specs/cells/secp256k1_blake160_sighash_all" } +create_type_id = true +capacity = 100_000_0000_0000 +[[genesis.system_cells]] +file = { bundled = "specs/cells/dao" } +create_type_id = true +capacity = 16_000_0000_0000 +[[genesis.system_cells]] +file = { bundled = "specs/cells/secp256k1_data" } +create_type_id = false +capacity = 1_048_617_0000_0000 +[[genesis.system_cells]] +file = { bundled = "specs/cells/secp256k1_blake160_multisig_all" } +create_type_id = true +capacity = 100_000_0000_0000 + +[genesis.system_cells_lock] +code_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +args = "0x" +hash_type = "data" + +# Dep group cells +[[genesis.dep_groups]] +name = "secp256k1_blake160_sighash_all" +files = [ + { bundled = "specs/cells/secp256k1_data" }, + { bundled = "specs/cells/secp256k1_blake160_sighash_all" }, +] +[[genesis.dep_groups]] +name = "secp256k1_blake160_multisig_all" +files = [ + { bundled = "specs/cells/secp256k1_data" }, + { bundled = "specs/cells/secp256k1_blake160_multisig_all" }, +] + +# For first 11 block +[genesis.bootstrap_lock] +code_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +args = "0x" +hash_type = "data" + + + +[params] +genesis_epoch_length = 1743 + +[pow] +func = "Eaglesong" \ No newline at end of file diff --git a/source/template/ckb/v116/specs/testnet.toml b/source/template/ckb/v116/specs/testnet.toml new file mode 100644 index 0000000..2461e53 --- /dev/null +++ b/source/template/ckb/v116/specs/testnet.toml @@ -0,0 +1,90 @@ +name = "ckb_testnet" + +[genesis] +version = 0 +parent_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +timestamp = 1589276230000 +compact_target = 0x1e015555 +uncles_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +nonce = "0x0" +# run `cargo run list-hashes -b` to get the genesis hash +hash = "0x10639e0895502b5688a6be8cf69460d76541bfa4821629d86d62ba0aae3f9606" + +[genesis.genesis_cell] +message = "aggron-v4" + +[genesis.genesis_cell.lock] +code_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +args = "0x" +hash_type = "data" + +# An array list paths to system cell files, which is absolute or relative to +# the directory containing this config file. +[[genesis.system_cells]] +file = { bundled = "specs/cells/secp256k1_blake160_sighash_all" } +create_type_id = true +capacity = 100_000_0000_0000 +[[genesis.system_cells]] +file = { bundled = "specs/cells/dao" } +create_type_id = true +capacity = 16_000_0000_0000 +[[genesis.system_cells]] +file = { bundled = "specs/cells/secp256k1_data" } +create_type_id = false +capacity = 1_048_617_0000_0000 +[[genesis.system_cells]] +file = { bundled = "specs/cells/secp256k1_blake160_multisig_all" } +create_type_id = true +capacity = 100_000_0000_0000 + +[genesis.system_cells_lock] +code_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +args = "0x" +hash_type = "data" + +# Dep group cells +[[genesis.dep_groups]] +name = "secp256k1_blake160_sighash_all" +files = [ + { bundled = "specs/cells/secp256k1_data" }, + { bundled = "specs/cells/secp256k1_blake160_sighash_all" }, +] +[[genesis.dep_groups]] +name = "secp256k1_blake160_multisig_all" +files = [ + { bundled = "specs/cells/secp256k1_data" }, + { bundled = "specs/cells/secp256k1_blake160_multisig_all" }, +] + +# For first 11 block +[genesis.bootstrap_lock] +code_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +args = "0x" +hash_type = "type" + +# Burn +[[genesis.issued_cells]] +capacity = 8_400_000_000_00000000 +lock.code_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +lock.args = "0x62e907b15cbf27d5425399ebf6f0fb50ebb88f18" +lock.hash_type = "data" + +# Locks for developers to run tests +[[genesis.issued_cells]] +capacity = 8_399_578_345_00000000 +lock.code_hash = "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8" +lock.args = "0x64257f00b6b63e987609fa9be2d0c86d351020fb" +lock.hash_type = "type" +[[genesis.issued_cells]] +capacity = 8_399_578_345_00000000 +lock.code_hash = "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8" +lock.args = "0x3f1573b44218d4c12a91919a58a863be415a2bc3" +lock.hash_type = "type" +[[genesis.issued_cells]] +capacity = 8_399_578_347_00000000 +lock.code_hash = "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8" +lock.args = "0x57ccb07be6875f61d93636b0ee11b675494627d2" +lock.hash_type = "type" + +[pow] +func = "EaglesongBlake2b" \ No newline at end of file diff --git a/test_cases/feature/test_get_live_cell.py b/test_cases/feature/test_get_live_cell.py new file mode 100644 index 0000000..a6f0ab1 --- /dev/null +++ b/test_cases/feature/test_get_live_cell.py @@ -0,0 +1,55 @@ +from framework.basic import CkbTest +import pytest + + +class TestGetLiveCell(CkbTest): + + @classmethod + def setup_class(cls): + cls.node = cls.CkbNode.init_dev_by_port(cls.CkbNodeConfigPath.CURRENT_TEST, "livecell/node1", 8120, + 8225) + cls.node.prepare(other_ckb_config={"ckb_tx_pool_max_tx_pool_size": "180_000"}) + cls.node.start() + cls.Miner.make_tip_height_number(cls.node, 100) + + @classmethod + def teardown_class(cls): + cls.node.stop() + cls.node.clean() + + @pytest.mark.skip("wait v117.0 release") + def test_get_live_cell_with_unspend(self): + account = self.Ckb_cli.util_key_info_by_private_key(self.Config.MINER_PRIVATE_1) + tx_hash = self.Ckb_cli.wallet_transfer_by_private_key(self.Config.MINER_PRIVATE_1, + account["address"]["testnet"], 100, + self.node.getClient().url, "1500") + print(f"txHash:{tx_hash}") + transaction = self.node.getClient().get_transaction(tx_hash) + result = self.node.getClient().get_live_cell_with_include_tx_pool( + transaction["transaction"]["inputs"][0]["previous_output"]["index"], + transaction["transaction"]["inputs"][0]["previous_output"]["tx_hash"]) + assert result['status'] == 'live' + + @pytest.mark.skip("wait v117.0 release") + def test_get_live_cell_with_spend(self): + account = self.Ckb_cli.util_key_info_by_private_key(self.Config.MINER_PRIVATE_1) + father_tx_hash = self.Ckb_cli.wallet_transfer_by_private_key(self.Config.MINER_PRIVATE_1, + account["address"]["testnet"], 100, + self.node.getClient().url, "1500") + print(f"txHash:{father_tx_hash}") + transaction = self.node.getClient().get_transaction(father_tx_hash) + result = self.node.getClient().get_live_cell_with_include_tx_pool( + transaction["transaction"]["inputs"][0]["previous_output"]["index"], + transaction["transaction"]["inputs"][0]["previous_output"]["tx_hash"], include_tx_pool=True) + assert result['status'] == 'live' + + tx = self.Tx.build_send_transfer_self_tx_with_input([father_tx_hash], ['0x0'], self.Config.MINER_PRIVATE_1, + output_count=15, + fee=15000, + api_url=self.node.getClient().url) + tx_hash = self.node.getClient().send_transaction(tx) + transaction = self.node.getClient().get_transaction(tx_hash) + result = self.node.getClient().get_live_cell_with_include_tx_pool( + transaction["transaction"]["inputs"][0]["previous_output"]["index"], + transaction["transaction"]["inputs"][0]["previous_output"]["tx_hash"], include_tx_pool=True) + assert result['status'] == 'unknown' diff --git a/test_cases/feature/test_test_tx_pool_accept.py b/test_cases/feature/test_test_tx_pool_accept.py new file mode 100644 index 0000000..5ebb600 --- /dev/null +++ b/test_cases/feature/test_test_tx_pool_accept.py @@ -0,0 +1,251 @@ +import pytest + +from framework.basic import CkbTest + + +class TestTestTxPoolAccept(CkbTest): + + @classmethod + def setup_class(cls): + cls.node = cls.CkbNode.init_dev_by_port(cls.CkbNodeConfigPath.CURRENT_TEST, + "feature/TestTxPoolAccept/node1", 8114, + 8225) + cls.node.prepare(other_ckb_config={"ckb_tx_pool_max_tx_pool_size": "4640"}) + cls.node.start() + cls.Miner.make_tip_height_number(cls.node, 200) + + @classmethod + def teardown_class(cls): + print("\nTeardown TestClass1") + cls.node.stop() + cls.node.clean() + + def setup_method(self, method): + self.node.getClient().clear_tx_pool() + + def test_normal_tx(self): + account = self.Ckb_cli.util_key_info_by_private_key(self.Config.ACCOUNT_PRIVATE_1) + father_tx_hash = self.Ckb_cli.wallet_transfer_by_private_key(self.Config.ACCOUNT_PRIVATE_1, + account["address"]["testnet"], 100000, + self.node.getClient().url, "1500000") + + tx = self.Tx.build_send_transfer_self_tx_with_input([father_tx_hash], ['0x0'], self.Config.ACCOUNT_PRIVATE_1, + output_count=15, + fee=15000, + api_url=self.node.getClient().url) + response = self.node.getClient().test_tx_pool_accept(tx, "passthrough") + tx_hash = self.node.getClient().send_transaction(tx) + tx_pool = self.node.getClient().get_raw_tx_pool(True) + assert tx_pool['pending'][tx_hash]['fee'] == response['fee'] + assert tx_pool['pending'][tx_hash]['cycles'] == response['cycles'] + + def test_min_fee_rejected(self): + account = self.Ckb_cli.util_key_info_by_private_key(self.Config.ACCOUNT_PRIVATE_1) + father_tx_hash = self.Ckb_cli.wallet_transfer_by_private_key(self.Config.ACCOUNT_PRIVATE_1, + account["address"]["testnet"], 100000, + self.node.getClient().url, "1500000") + + tx = self.Tx.build_send_transfer_self_tx_with_input([father_tx_hash], ['0x0'], self.Config.ACCOUNT_PRIVATE_1, + output_count=15, + fee=15, + api_url=self.node.getClient().url) + + with pytest.raises(Exception) as exc_info: + response = self.node.getClient().test_tx_pool_accept(tx, "passthrough") + expected_error_message = "PoolRejectedTransactionByMinFeeRate" + print("exc_info.value.args[0]:", exc_info.value.args[0]) + 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]}'" + + def test_err_outputs_validator(self): + account = self.Ckb_cli.util_key_info_by_private_key(self.Config.ACCOUNT_PRIVATE_1) + father_tx_hash = self.Ckb_cli.wallet_transfer_by_private_key(self.Config.ACCOUNT_PRIVATE_1, + account["address"]["testnet"], 100000, + self.node.getClient().url, "1500000") + + tx = self.Tx.build_send_transfer_self_tx_with_input([father_tx_hash], ['0x0'], self.Config.ACCOUNT_PRIVATE_1, + output_count=15, + fee=15000, + api_url=self.node.getClient().url) + + with pytest.raises(Exception) as exc_info: + response = self.node.getClient().test_tx_pool_accept(tx, "other") + + expected_error_message = "Invalid parameter for `outputs_validator`" + print("exc_info.value.args[0]:", exc_info.value.args[0]) + 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]}'" + + def test_dup_cell_tx(self): + account = self.Ckb_cli.util_key_info_by_private_key(self.Config.ACCOUNT_PRIVATE_1) + father_tx_hash = self.Ckb_cli.wallet_transfer_by_private_key(self.Config.ACCOUNT_PRIVATE_1, + account["address"]["testnet"], 100000, + self.node.getClient().url, "1500000") + + tx = self.Tx.build_send_transfer_self_tx_with_input([father_tx_hash], ['0x0'], self.Config.ACCOUNT_PRIVATE_1, + output_count=15, + fee=15000, + api_url=self.node.getClient().url) + + self.node.getClient().send_transaction(tx) + with pytest.raises(Exception) as exc_info: + response = self.node.getClient().test_tx_pool_accept(tx, "passthrough") + + expected_error_message = "PoolRejectedDuplicatedTransaction" + print("exc_info.value.args[0]:", exc_info.value.args[0]) + 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]}'" + + def test_send_link_tx(self): + account = self.Ckb_cli.util_key_info_by_private_key(self.Config.ACCOUNT_PRIVATE_1) + father_tx_hash = self.Ckb_cli.wallet_transfer_by_private_key(self.Config.ACCOUNT_PRIVATE_1, + account["address"]["testnet"], 100000, + self.node.getClient().url, "1500000") + tx_hash = father_tx_hash + for i in range(3): + tx = self.Tx.build_send_transfer_self_tx_with_input([tx_hash], [hex(0)], self.Config.ACCOUNT_PRIVATE_1, + output_count=1, + fee=1100090 + i * 1000, + api_url=self.node.getClient().url) + test_tx_pool_accept_response = self.node.getClient().test_tx_pool_accept(tx, "passthrough") + tx_hash = self.node.getClient().send_transaction(tx) + + def test_PoolRejectedTransactionByMinFeeRate(self): + """ + PoolRejectedTransactionByMinFeeRate + Returns: + + """ + account = self.Ckb_cli.util_key_info_by_private_key(self.Config.ACCOUNT_PRIVATE_1) + father_tx_hash = self.Ckb_cli.wallet_transfer_by_private_key(self.Config.ACCOUNT_PRIVATE_1, + account["address"]["testnet"], 100000, + self.node.getClient().url, "1500000") + with pytest.raises(Exception) as exc_info: + tx = self.Tx.build_send_transfer_self_tx_with_input([father_tx_hash], [hex(0)], self.Config.ACCOUNT_PRIVATE_1, + output_count=1, + fee=100, + api_url=self.node.getClient().url) + self.node.getClient().test_tx_pool_accept(tx,"passthrough") + expected_error_message = "PoolRejectedTransactionByMinFeeRate" + 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]}'" + + @pytest.mark.skip + def test_pool_full(self): + account = self.Ckb_cli.util_key_info_by_private_key(self.Config.ACCOUNT_PRIVATE_1) + father_tx_hash = self.Ckb_cli.wallet_transfer_by_private_key(self.Config.ACCOUNT_PRIVATE_1, + account["address"]["testnet"], 1500000, + self.node.getClient().url, "1500000") + tx_hash = father_tx_hash + send_tx_list = [] + for i in range(10): + print("current idx:", i) + tx = self.Tx.build_send_transfer_self_tx_with_input([tx_hash], [hex(0)], self.Config.ACCOUNT_PRIVATE_1, + output_count=1, + fee=1000000 - i * 1000, + api_url=self.node.getClient().url) + response = self.node.getClient().test_tx_pool_accept(tx, "passthrough") + + tx_hash = self.node.getClient().send_transaction(tx) + send_tx_list.append(tx_hash) + + with pytest.raises(Exception) as exc_info: + tx = self.Tx.build_send_transfer_self_tx_with_input([tx_hash], [hex(0)], self.Config.ACCOUNT_PRIVATE_1, + output_count=1, + fee=1000000 - i * 1000, + api_url=self.node.getClient().url) + response = self.node.getClient().test_tx_pool_accept(tx, "passthrough") + expected_error_message = "PoolIsFull" + 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]}'" + with pytest.raises(Exception) as exc_info: + self.node.getClient().send_transaction(tx) + + + def test_TransactionFailedToVerify(self): + account = self.Ckb_cli.util_key_info_by_private_key(self.Config.ACCOUNT_PRIVATE_1) + father_tx_hash = self.Ckb_cli.wallet_transfer_by_private_key(self.Config.ACCOUNT_PRIVATE_1, + account["address"]["testnet"], 1500000, + self.node.getClient().url, "1500000") + print(f"father_tx_hash:", father_tx_hash) + tx_hash = father_tx_hash + send_tx_list = [] + for i in range(10): + print("current idx:", i) + tx = self.Tx.build_send_transfer_self_tx_with_input([tx_hash], [hex(0)], self.Config.ACCOUNT_PRIVATE_1, + output_count=1, + fee=1000000 - i * 1000, + api_url=self.node.getClient().url) + response = self.node.getClient().test_tx_pool_accept(tx, "passthrough") + + tx_hash = self.node.getClient().send_transaction(tx) + send_tx_list.append(tx_hash) + + with pytest.raises(Exception) as exc_info: + tx = self.Tx.build_send_transfer_self_tx_with_input_err([tx_hash], [hex(0)], self.Config.ACCOUNT_PRIVATE_1, + output_count=1, + fee=1000000 - i * 1000, + api_url=self.node.getClient().url) + response = self.node.getClient().test_tx_pool_accept(tx, "passthrough") + expected_error_message = "TransactionFailedToVerify" + 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]}'" + with pytest.raises(Exception) as exc_info: + self.node.getClient().send_transaction(tx) + + def test_TransactionFailedToResolve(self): + account = self.Ckb_cli.util_key_info_by_private_key(self.Config.ACCOUNT_PRIVATE_1) + father_tx_hash = self.Ckb_cli.wallet_transfer_by_private_key(self.Config.ACCOUNT_PRIVATE_1, + account["address"]["testnet"], 1500000, + self.node.getClient().url, "1500000") + print(f"father_tx_hash:", father_tx_hash) + tx_hash = father_tx_hash + send_tx_list = [] + for i in range(10): + print("current idx:", i) + tx = self.Tx.build_send_transfer_self_tx_with_input([tx_hash], [hex(0)], self.Config.ACCOUNT_PRIVATE_1, + output_count=1, + fee=1000000 - i * 1000, + api_url=self.node.getClient().url) + response = self.node.getClient().test_tx_pool_accept(tx, "passthrough") + + tx_hash = self.node.getClient().send_transaction(tx) + send_tx_list.append(tx_hash) + + with pytest.raises(Exception) as exc_info: + tx = self.Tx.build_send_transfer_self_tx_with_input_err2([tx_hash], [hex(0)], self.Config.ACCOUNT_PRIVATE_1, + output_count=1, + fee=1000000 - i * 1000, + api_url=self.node.getClient().url) + response = self.node.getClient().test_tx_pool_accept(tx, "passthrough") + expected_error_message = "TransactionFailedToResolve" + 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]}'" + with pytest.raises(Exception) as exc_info: + self.node.getClient().send_transaction(tx) + + def test_change_cache_tx(self): + account = self.Ckb_cli.util_key_info_by_private_key(self.Config.ACCOUNT_PRIVATE_1) + father_tx_hash = self.Ckb_cli.wallet_transfer_by_private_key(self.Config.ACCOUNT_PRIVATE_1, + account["address"]["testnet"], 100000, + self.node.getClient().url, "1500000") + + tx = self.Tx.build_send_transfer_self_tx_with_input([father_tx_hash], ['0x0'], self.Config.ACCOUNT_PRIVATE_1, + output_count=15, + fee=15000, + api_url=self.node.getClient().url) + tx_hash = self.node.getClient().send_transaction(tx) + self.node.getClient().remove_transaction(tx_hash) + tx['witnesses'][0] = "0x00" + with pytest.raises(Exception) as exc_info: + tx_hash = self.node.getClient().send_transaction(tx) + expected_error_message = "ValidationFailure" + 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]}'" + with pytest.raises(Exception) as exc_info: + tx_hash = self.node.getClient().test_tx_pool_accept(tx,"passthrough") + expected_error_message = "ValidationFailure" + 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/soft_fork/test_mainnet_soft_fork_failed.py b/test_cases/soft_fork/test_mainnet_soft_fork_failed.py index 7f1ecdb..50be1ed 100644 --- a/test_cases/soft_fork/test_mainnet_soft_fork_failed.py +++ b/test_cases/soft_fork/test_mainnet_soft_fork_failed.py @@ -1,3 +1,5 @@ +import pytest + from framework.basic import CkbTest @@ -25,6 +27,7 @@ def teardown_class(cls): cls.cluster.stop_all_nodes() cls.cluster.clean_all_nodes() + @pytest.mark.skip def test_01_get_consensus_in_ge_110(self): """ > 110 node softforks:light_client min_activation_epoch == 0x21c8 @@ -46,6 +49,7 @@ def test_02_get_consensus_in_lt_110(self): consensus = self.node109.getClient().get_consensus() assert consensus['softforks'] == {} + @pytest.mark.skip def test_03_get_deployments_in_gt_110(self): """ > 110 node deployments:light_client min_activation_epoch == 0x21c8 @@ -59,6 +63,7 @@ def test_03_get_deployments_in_gt_110(self): assert deployments['deployments']['light_client']['period'] == '0x2a' assert deployments['deployments']['light_client']['timeout'] == '0x2168' + @pytest.mark.skip def test_04_miner_use_109(self): """ use 109 miner block,will cause softfork failed diff --git a/test_cases/soft_fork/test_mainnet_soft_fork_successful.py b/test_cases/soft_fork/test_mainnet_soft_fork_successful.py index bd4e920..3b2b30d 100644 --- a/test_cases/soft_fork/test_mainnet_soft_fork_successful.py +++ b/test_cases/soft_fork/test_mainnet_soft_fork_successful.py @@ -1,3 +1,5 @@ +import pytest + from framework.basic import CkbTest @@ -27,6 +29,7 @@ def teardown_class(cls): cls.cluster.stop_all_nodes() cls.cluster.clean_all_nodes() + @pytest.mark.skip def test_01_miner_use_110(self): """ 110 node miner will cause softfork active diff --git a/test_cases/tx_pool_refactor/test_03_tx_pool_limit.py b/test_cases/tx_pool_refactor/test_03_tx_pool_limit.py index 2053e4f..0389915 100644 --- a/test_cases/tx_pool_refactor/test_03_tx_pool_limit.py +++ b/test_cases/tx_pool_refactor/test_03_tx_pool_limit.py @@ -7,7 +7,7 @@ class TestTxPoolLimit(CkbTest): def setup_class(cls): cls.node = cls.CkbNode.init_dev_by_port(cls.CkbNodeConfigPath.CURRENT_TEST, "tx_pool/node1", 8120, 8225) - cls.node.prepare(other_ckb_config={"ckb_tx_pool_max_tx_pool_size": "4640"}) + cls.node.prepare(other_ckb_config={"ckb_tx_pool_max_tx_pool_size": "46400000"}) cls.node.start() cls.Miner.make_tip_height_number(cls.node, 30) @@ -24,50 +24,6 @@ def teardown_class(cls): cls.node.stop() cls.node.clean() - def test_remove_tx_when_ckb_tx_pool_full(self): - """ - 1. make tx pool full - tx pool size > ckb_tx_pool_max_tx_pool_size - 2. start miner - clean tx ,make tx pool size < ckb_tx_pool_max_tx_pool_size - 3. query tx pool size - tx pool size < ckb_tx_pool_max_tx_pool_size - :return: - """ - tip_number = self.node.getClient().get_tip_block_number() - account = self.Ckb_cli.util_key_info_by_private_key(self.Config.ACCOUNT_PRIVATE_1) - tx_hash = self.Ckb_cli.wallet_transfer_by_private_key(self.Config.ACCOUNT_PRIVATE_1, - account["address"]["testnet"], 1000000, - self.node.getClient().url, "1500000") - tx_hash = self.Tx.send_transfer_self_tx_with_input([tx_hash], ['0x0'], self.Config.ACCOUNT_PRIVATE_1, - output_count=10, - fee=1500000, - api_url=self.node.getClient().url) - self.Miner.miner_until_tx_committed(self.node, tx_hash) - for i in range(0, 10): - tx_hash1 = self.Tx.send_transfer_self_tx_with_input([tx_hash], [hex(i)], self.Config.ACCOUNT_PRIVATE_1, - output_count=1, - fee=1090, - api_url=self.node.getClient().url) - for i in range(10): - tx_hash1 = self.Tx.send_transfer_self_tx_with_input([tx_hash1], ['0x0'], self.Config.ACCOUNT_PRIVATE_1, - output_count=1, - fee=1090, - api_url=self.node.getClient().url) - tx_pool = self.node.getClient().get_raw_tx_pool(True) - tx_poll_list = list(tx_pool['pending'].keys()) - before_miner_tx_pool = self.node.getClient().tx_pool_info() - print(before_miner_tx_pool) - for i in range(1): - self.Miner.miner_with_version(self.node, "0x0") - self.node.getClient().get_raw_tx_pool() - after_miner_tx_pool = self.node.getClient().tx_pool_info() - print(tx_pool) - assert int(after_miner_tx_pool['total_tx_size'], 16) < int(after_miner_tx_pool['max_tx_pool_size'], 16) - for tx_hash in tx_poll_list: - tx_response = self.node.getClient().get_transaction(tx_hash) - assert tx_response['tx_status']['status'] == 'pending' or tx_response['tx_status']['status'] == 'unknown' - def test_max_linked_transactions(self): """ test linked tx limit diff --git a/test_cases/tx_pool_refactor/test_12_send_tx_when_tx_pool_limit.py b/test_cases/tx_pool_refactor/test_12_send_tx_when_tx_pool_limit.py new file mode 100644 index 0000000..c548266 --- /dev/null +++ b/test_cases/tx_pool_refactor/test_12_send_tx_when_tx_pool_limit.py @@ -0,0 +1,196 @@ +import pytest +from framework.basic import CkbTest + + +class TestSendTxWhenPoolLimit(CkbTest): + @classmethod + def setup_class(cls): + cls.node = cls.CkbNode.init_dev_by_port(cls.CkbNodeConfigPath.CURRENT_TEST, "tx_pool/node1", 8120, + 8225) + cls.node.prepare(other_ckb_config={"ckb_tx_pool_max_tx_pool_size": "4640"}) + cls.node.start() + cls.Miner.make_tip_height_number(cls.node, 30) + + def setup_method(self, method): + """ + clean tx pool + :param method: + :return: + """ + # self.node.getClient().clear_tx_pool() + for i in range(5): + self.Miner.miner_with_version(self.node, "0x0") + pool = self.node.getClient().tx_pool_info() + print("pool:", pool) + + @classmethod + def teardown_class(cls): + cls.node.stop() + cls.node.clean() + + def test_replace_same_father_link_tx_when_tx_pool_full(self): + """ + 能够替换父交易其他output产生的子交易 + cellDep的交易手续费很低,子交易只会替换其他交易不会替换被其他高fee交易当cellDep的交易 + 父交易手续费最低,子交易只会替换其他交易不会替换父交易 + + Returns: + + """ + account = self.Ckb_cli.util_key_info_by_private_key(self.Config.ACCOUNT_PRIVATE_1) + father_tx_hash = self.Ckb_cli.wallet_transfer_by_private_key(self.Config.ACCOUNT_PRIVATE_1, + account["address"]["testnet"], 100000, + self.node.getClient().url, "1500000") + + tx_hash_2 = self.Ckb_cli.wallet_transfer_by_private_key(self.Config.ACCOUNT_PRIVATE_2, + account["address"]["testnet"], 100000, + self.node.getClient().url, "1000") + + tx_list = [father_tx_hash, tx_hash_2] + tx_hash = self.Tx.send_transfer_self_tx_with_input([father_tx_hash], ['0x0'], self.Config.ACCOUNT_PRIVATE_1, + output_count=15, + fee=15000, + api_url=self.node.getClient().url) + tx_list.append(tx_hash) + for i in range(0, 15): + print("current i:", i) + tx_hash1 = self.Tx.send_transfer_self_tx_with_input([tx_hash], [hex(i)], self.Config.ACCOUNT_PRIVATE_1, + output_count=1, + fee=10000 * 2 ** i, + dep_cells=[{"tx_hash": tx_hash_2, "index_hex": "0x0"}], + api_url=self.node.getClient().url) + tx_list.append(tx_hash1) + response = self.node.getClient().get_transaction(tx_hash_2) + assert response['tx_status']['status'] != "unknown" + response = self.node.getClient().get_transaction(father_tx_hash) + assert response['tx_status']['status'] != "unknown" + unknown = 0 + for tx_hash in tx_list: + response = self.node.getClient().get_transaction(tx_hash) + print(response) + if response['tx_status']['status'] == "unknown": + unknown += 1 + assert unknown == 11 + + def test_replace_normal_tx_when_tx_pool_full(self): + """ + 交易池都是普通交易, 交易池满时,发送一笔手续费比较高的交易,发送成功,手续费比较低的交易会被移除 + + 1. send 10 normal tx + 2. send 12 tx that fee > 10 normal tx + 3. 12 tx status == pending + 4. 8 normal tx status == unknown + Returns: + + """ + account = self.Ckb_cli.util_key_info_by_private_key(self.Config.ACCOUNT_PRIVATE_1) + tx_hash = self.Ckb_cli.wallet_transfer_by_private_key(self.Config.ACCOUNT_PRIVATE_1, + account["address"]["testnet"], 1000000, + self.node.getClient().url, "1500000") + tx_hash = self.Tx.send_transfer_self_tx_with_input([tx_hash], ['0x0'], self.Config.ACCOUNT_PRIVATE_1, + output_count=10, + fee=1500000, + api_url=self.node.getClient().url) + self.Miner.miner_until_tx_committed(self.node, tx_hash) + tx_list = [] + for i in range(0, 10): + print("current i:", i) + tx_hash1 = self.Tx.send_transfer_self_tx_with_input([tx_hash], [hex(i)], self.Config.ACCOUNT_PRIVATE_1, + output_count=1, + fee=1090 + i, + api_url=self.node.getClient().url) + tx_list.append(tx_hash1) + + tx_hash_a = tx_hash + tx_hash_a_list = [] + for i in range(12): + print("tx_hash_a_list current i:", i) + tx_hash_a = self.Tx.send_transfer_self_tx_with_input([tx_hash_a], [hex(0)], self.Config.ACCOUNT_PRIVATE_1, + output_count=1, + fee=11090 + i * 1000, + api_url=self.node.getClient().url) + tx_hash_a_list.append(tx_hash_a) + unknown_status_size = 0 + for tx_hash in tx_list: + tx = self.node.getClient().get_transaction(tx_hash) + if tx['tx_status']['status'] == "unknown": + unknown_status_size += 1 + for tx_hash_a in tx_hash_a_list: + tx = self.node.getClient().get_transaction(tx_hash_a) + assert tx['tx_status']['status'] != "unknown" + assert unknown_status_size == 8 + + def test_replace_link_tx_when_ckb_tx_pool_full(self): + """ + xxxx 交易(fee = 500000000) + 链式交易:a(fee=1000)-> b(fee=2000)- ->c(fee=3000)- -> d(fee=4000) -> e(fee=5000) + 插入tx(fee=500000),那会导致a,b,c,d,e 交易都被移除 + Returns: + """ + account = self.Ckb_cli.util_key_info_by_private_key(self.Config.ACCOUNT_PRIVATE_1) + tx_hash = self.Ckb_cli.wallet_transfer_by_private_key(self.Config.ACCOUNT_PRIVATE_1, + account["address"]["testnet"], 1000000, + self.node.getClient().url, "1500000") + tx_hash = self.Tx.send_transfer_self_tx_with_input([tx_hash], ['0x0'], self.Config.ACCOUNT_PRIVATE_1, + output_count=10, + fee=1500000, + api_url=self.node.getClient().url) + self.Miner.miner_until_tx_committed(self.node, tx_hash) + tx_hash_a = tx_hash + tx_hash_a_list = [] + for i in range(12): + print("tx_hash_a_list current i:", i) + tx_hash_a = self.Tx.send_transfer_self_tx_with_input([tx_hash_a], [hex(0)], self.Config.ACCOUNT_PRIVATE_1, + output_count=1, + fee=11090 + i * 1000, + api_url=self.node.getClient().url) + tx_hash_a_list.append(tx_hash_a) + + tx_hash_b = tx_hash + tx_hash_b_list = [] + tx_hash_b = self.Tx.send_transfer_self_tx_with_input([tx_hash_b], [hex(1)], self.Config.ACCOUNT_PRIVATE_1, + output_count=1, + fee=1100090 + i * 1000, + api_url=self.node.getClient().url) + tx_hash_b_list.append(tx_hash_b) + for i in range(3): + print("tx_hash_b_list current i:", i) + + tx_hash_b = self.Tx.send_transfer_self_tx_with_input([tx_hash_b], [hex(0)], self.Config.ACCOUNT_PRIVATE_1, + output_count=1, + fee=110000000 + i * 10000000, + api_url=self.node.getClient().url) + tx_hash_b_list.append(tx_hash_b) + for tx_hash_a in tx_hash_a_list: + tx = self.node.getClient().get_transaction(tx_hash_a) + assert tx['tx_status']['status'] == 'unknown' + for tx_hash_b in tx_hash_b_list: + self.node.getClient().get_transaction(tx_hash_b) + + def test_cant_replace_father_tx_when_ckb_tx_pool_full(self): + """ + 交易池全是父交易,子交易手续费给再高,都是报pool is full + + Returns: + + """ + account = self.Ckb_cli.util_key_info_by_private_key(self.Config.ACCOUNT_PRIVATE_1) + tx_hash = self.Ckb_cli.wallet_transfer_by_private_key(self.Config.ACCOUNT_PRIVATE_1, + account["address"]["testnet"], 1000000, + self.node.getClient().url, "1500000") + tx_hash = self.Tx.send_transfer_self_tx_with_input([tx_hash], ['0x0'], self.Config.ACCOUNT_PRIVATE_1, + output_count=10, + fee=1500000, + api_url=self.node.getClient().url) + self.Miner.miner_until_tx_committed(self.node, tx_hash) + with pytest.raises(Exception) as exc_info: + for i in range(22): + tx_hash = self.Tx.send_transfer_self_tx_with_input([tx_hash], [hex(0)], self.Config.ACCOUNT_PRIVATE_1, + output_count=1, + fee=11090 + i * 1000, + api_url=self.node.getClient().url) + + expected_error_message = "PoolIsFull" + print("exc_info.value.args[0]:", exc_info.value.args[0]) + 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]}'"