diff --git a/.github/workflows/ci_integration_tests.yml b/.github/workflows/ci_integration_tests.yml index b541ed96..d7d4114e 100644 --- a/.github/workflows/ci_integration_tests.yml +++ b/.github/workflows/ci_integration_tests.yml @@ -15,7 +15,13 @@ permissions: jobs: build: - runs-on: ubuntu-22.04 + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-22.04] + + # os: [ubuntu-22.04, macos-latest] + steps: - uses: actions/checkout@v3 diff --git a/Makefile b/Makefile index 72da8435..4b8bd38f 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,8 @@ test_cases := \ test_cases/feature \ test_cases/config \ test_cases/miner \ - test_cases/get_fee_rate_statistics + test_cases/get_fee_rate_statistics \ + test_cases/ws test: diff --git a/download.py b/download.py index 0d4ef524..374542d5 100644 --- a/download.py +++ b/download.py @@ -23,6 +23,7 @@ "0.117.0", "0.118.0", "0.119.0", + "0.120.0", ] # Replace with your versions DOWNLOAD_DIR = "download" diff --git a/download_ckb_light_client.py b/download_ckb_light_client.py index 27500342..4c7ca3d2 100644 --- a/download_ckb_light_client.py +++ b/download_ckb_light_client.py @@ -21,6 +21,7 @@ "0.3.4", "0.3.5", "0.3.6", + "0.4.1", ] # Replace with your versions DOWNLOAD_DIR = "download" diff --git a/framework/test_light_client.py b/framework/test_light_client.py index 921d70b9..9c3e46c8 100644 --- a/framework/test_light_client.py +++ b/framework/test_light_client.py @@ -39,9 +39,14 @@ class CkbLightClientConfigPath(Enum): "download/0.3.6/ckb-light-client", ) + V0_4_1 = ( + "source/template/ckb_light_client/0.3.0/testnet.toml.j2", + "download/0.4.2/ckb-light-client", + ) + CURRENT_TEST = ( "source/template/ckb_light_client/0.3.0/testnet.toml.j2", - "download/0.3.6/ckb-light-client", + "download/0.4.1/ckb-light-client", ) def __init__(self, ckb_light_client_config_path, ckb_light_bin_path): diff --git a/framework/test_node.py b/framework/test_node.py index d34daf25..c1f3f088 100644 --- a/framework/test_node.py +++ b/framework/test_node.py @@ -15,30 +15,37 @@ class CkbNodeConfigPath(Enum): CURRENT_TEST = ( - "source/template/ckb/v118/ckb.toml.j2", - "source/template/ckb/v118/ckb-miner.toml.j2", - "source/template/ckb/v118/specs/dev.toml", - "download/0.119.0", + "source/template/ckb/v120/ckb.toml.j2", + "source/template/ckb/v120/ckb-miner.toml.j2", + "source/template/ckb/v120/specs/dev.toml", + "download/0.120.0", ) TESTNET = ( - "source/template/ckb/v118/ckb.toml.j2", - "source/template/ckb/v118/ckb-miner.toml.j2", + "source/template/ckb/v120/ckb.toml.j2", + "source/template/ckb/v120/ckb-miner.toml.j2", "source/template/specs/testnet.toml.j2", - "download/0.119.0", + "download/0.120.0", ) CURRENT_MAIN = ( - "source/template/ckb/v118/ckb.toml.j2", - "source/template/ckb/v118/ckb-miner.toml.j2", + "source/template/ckb/v120/ckb.toml.j2", + "source/template/ckb/v120/ckb-miner.toml.j2", "source/template/specs/mainnet.toml.j2", - "download/0.119.0", + "download/0.120.0", ) PREVIEW_DUMMY = ( - "source/template/ckb/v118/ckb.toml.j2", - "source/template/ckb/v118/ckb-miner.toml.j2", + "source/template/ckb/v120/ckb.toml.j2", + "source/template/ckb/v120/ckb-miner.toml.j2", "source/template/specs/preview_dev.toml", - "download/0.119.0", + "download/0.120.0", + ) + + v120 = ( + "source/template/ckb/v120/ckb.toml.j2", + "source/template/ckb/v120/ckb-miner.toml.j2", + "source/template/ckb/v120/specs/dev.toml", + "download/0.120.0", ) v119 = ( @@ -211,6 +218,22 @@ def connected(self, node): peer_address = node.get_peer_address() print("add node response:", self.getClient().add_node(peer_id, peer_address)) + def connected_ws(self, node): + peer_id = node.get_peer_id() + peer_address = node.get_peer_address() + if "ws" not in peer_address: + peer_address = peer_address + "/ws" + print("add node response:", self.getClient().add_node(peer_id, peer_address)) + + def connected_all_address(self, node): + peer_id = node.get_peer_id() + node_info = node.client.local_node_info() + for address in node_info["addresses"]: + peer_address = address["address"].replace("0.0.0.0", "127.0.0.1") + print( + "add node response:", self.getClient().add_node(peer_id, peer_address) + ) + def getClient(self) -> RPCClient: return self.client @@ -262,6 +285,9 @@ def stop(self): self.ckb_pid = -1 time.sleep(3) + def rmLockFile(self): + run_command(f"cd {self.ckb_dir} && rm -rf data/db/LOCK") + def prepare( self, other_ckb_config={}, @@ -365,6 +391,7 @@ def subscribe_telnet(self, topic, other_url=None) -> telnetlib.Telnet: + topic + '"]}' ) + print(f"host:{host},port:{port},topic_str:{topic_str}") tn.write(topic_str.encode("utf-8") + b"\n") data = tn.read_until(b"}\n") if data: diff --git a/source/contract/test_cases/exec_with_exec b/source/contract/test_cases/exec_with_exec index ca81436b..e60e3a5d 100755 Binary files a/source/contract/test_cases/exec_with_exec and b/source/contract/test_cases/exec_with_exec differ diff --git a/source/template/ckb/v120/ckb-miner.toml.j2 b/source/template/ckb/v120/ckb-miner.toml.j2 new file mode 100644 index 00000000..6c29b129 --- /dev/null +++ b/source/template/ckb/v120/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/v120/ckb.toml.j2 b/source/template/ckb/v120/ckb.toml.j2 new file mode 100644 index 00000000..d2c8dfd0 --- /dev/null +++ b/source/template/ckb/v120/ckb.toml.j2 @@ -0,0 +1,220 @@ +# 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"] +# reuse_tcp_with_ws = true +reuse_tcp_with_ws = {{ ckb_network_reuse_tcp_with_ws | default("false") }} +# [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") }} + + +{% if ckb_rpc_batch_limit is defined %} +rpc_batch_limit = {{ ckb_rpc_batch_limit | default("2000") }} +{% endif %} + + +[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}"] + + +{% if ckb_request_limit is defined %} +[indexer_v2] +request_limit = {{ ckb_request_limit | default("400") }} +{% endif %} + + + +# # 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/v120/default.db-options b/source/template/ckb/v120/default.db-options new file mode 100644 index 00000000..bffbdc09 --- /dev/null +++ b/source/template/ckb/v120/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/v120/specs/benchmark-spec.toml b/source/template/ckb/v120/specs/benchmark-spec.toml new file mode 100644 index 00000000..e69de29b diff --git a/source/template/ckb/v120/specs/dev.toml b/source/template/ckb/v120/specs/dev.toml new file mode 100644 index 00000000..f25a259a --- /dev/null +++ b/source/template/ckb/v120/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/v120/specs/mainnet.toml b/source/template/ckb/v120/specs/mainnet.toml new file mode 100644 index 00000000..daf7edfb --- /dev/null +++ b/source/template/ckb/v120/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/v120/specs/testnet.toml b/source/template/ckb/v120/specs/testnet.toml new file mode 100644 index 00000000..2461e535 --- /dev/null +++ b/source/template/ckb/v120/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/ckb2023/test_03_ckb_light_client_after_hardfork.py b/test_cases/ckb2023/test_03_ckb_light_client_after_hardfork.py index 6a1139b1..e9add02a 100644 --- a/test_cases/ckb2023/test_03_ckb_light_client_after_hardfork.py +++ b/test_cases/ckb2023/test_03_ckb_light_client_after_hardfork.py @@ -261,7 +261,6 @@ def test_04_ckb_light_client_current_transfer_data2_tx(self): ) assert tx_hash == light_tx_hash - @pytest.mark.skip def test_05_ckb_light_client_current_spawn_contract_use_data2(self): """ send spawn tx ( hash type : data2), on the ckb light client @@ -318,7 +317,6 @@ def test_05_ckb_light_client_current_spawn_contract_use_data2(self): assert tx_hash == light_tx_hash - @pytest.mark.skip def test_05_ckb_light_client_current_spawn_contract_use_type(self): """ 1. send spawn tx ( hash type : type), on the ckb light client diff --git a/test_cases/issue/test_4698.py b/test_cases/issue/test_4698.py new file mode 100644 index 00000000..e4401e16 --- /dev/null +++ b/test_cases/issue/test_4698.py @@ -0,0 +1,84 @@ +import pytest + +from framework.basic import CkbTest + + +class Test4698(CkbTest): + + @classmethod + def setup_class(cls): + """ + 1. start 1 ckb node in tmp/feature/TestTxPoolAccept/node1 dir + 2. miner 200 block + Returns: + + """ + 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): + """ + 1. stop ckb node + 2. clean ckb node tmp dir + Returns: + + """ + print("\nTeardown TestClass1") + cls.node.stop() + cls.node.clean() + + def setup_method(self, method): + """ + 1. clear tx pool setup for testcases + Args: + method: + + Returns: + + """ + self.node.getClient().clear_tx_pool() + + def test_4698(self): + """ + 1. RPC send_transaction should not include stack trace in the RPC response + 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 = 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) + # 1. remove tx from tx pool + 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) + # 2. use error witness check transaction can not send success,and RPC send_transaction should not include stack trace in the RPC response + expected_error_message = "Stack backtrace" + assert ( + expected_error_message not 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/issue/test_4702.py b/test_cases/issue/test_4702.py new file mode 100644 index 00000000..837c1a22 --- /dev/null +++ b/test_cases/issue/test_4702.py @@ -0,0 +1,120 @@ +import os +import time +from parameterized import parameterized + +from framework.basic import CkbTest +from framework.util import get_project_root + + +def get_all_files(directory): + file_list = [] + for root, directories, files in os.walk(directory): + for file_name in files: + file_path = os.path.join(root, file_name) + file_list.append(file_path) + return file_list + + +def get_failed_files(): + project_root = get_project_root() + files = get_all_files(f"{get_project_root()}/source/contract/test_cases") + + files_list = [ + "exec_with_exec", + ] + # 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] + + +class Test4702(CkbTest): + failed_files = get_failed_files() + + @classmethod + def setup_class(cls): + """ + 1.启动node1和node2两个节点 + """ + cls.local_node = cls.CkbNode.init_dev_by_port( + cls.CkbNodeConfigPath.CURRENT_TEST, "tx_pool/node1", 8120, 8225 + ) + cls.remote_node = cls.CkbNode.init_dev_by_port( + cls.CkbNodeConfigPath.CURRENT_TEST, "tx_pool/node2", 8121, 8226 + ) + cls.cluster = cls.Cluster([cls.local_node, cls.remote_node]) + cls.cluster.prepare_all_nodes() + cls.cluster.start_all_nodes() + cls.Miner.make_tip_height_number(cls.local_node, 200) + + @classmethod + def teardown_class(cls): + cls.cluster.stop_all_nodes() + cls.cluster.clean_all_nodes() + + @parameterized.expand(failed_files) + def test_4702(self, path): + """ + 2.首先node1和node2两个节点建立p2p连接 + 3.然后通过node1将包含这个大交易的区块上链,node2 同步到这个区块这个大交易 p2p 广播给 node2 + 4.接着node2的ckb-vm执行这个交易的过程中,发送 ctrl-c 给node2. 然后 node2 执行交易就会报错 + 5.预期的行为是node2不能ban掉remote的node1节点以及node2重启后日志里不会提示vm internal error的错误 + """ + + # 通过node1将包含这个大交易的区块上链,node2 同步到这个区块这个大交易 p2p 广播给 node2 + invoke_hash = self.deploy_and_invoke( + self.Config.MINER_PRIVATE_1, path, self.local_node + ) + self.Miner.miner_until_tx_committed(self.local_node, invoke_hash) + for i in range(0, 20): + self.Miner.miner_with_version(self.local_node, "0x0") + tx = self.local_node.getClient().get_transaction(invoke_hash) + block_number = tx["tx_status"]["block_number"] + # node1和node2两个节点建立p2p连接 + self.cluster.connected_all_nodes() + self.Node.wait_cluster_height(self.cluster, int(block_number, 16) - 1, 200) + time.sleep(2) + tip = self.remote_node.getClient().get_tip_block_number() + assert tip == int(block_number, 16) - 1 + # 接着node2的ckb-vm执行这个交易的过程中,发送 ctrl-c 给node2. 然后 node2 执行交易就会报错 + self.remote_node.stop() + self.remote_node.rmLockFile() + self.remote_node.start() + print(f"ban info:{self.remote_node.getClient().get_banned_addresses()}") + # 预期的行为是node2不能ban掉remote的node1节点以及node2重启后日志里不会提示vm internal error的错误 + assert self.remote_node.getClient().get_banned_addresses() == [] + + 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) + 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, + ) + + return invoke_hash + + except Exception as e: + print(e) + if "Resolve failed Dead" in str(e): + try_count -= 1 + for i in range(2): + self.Miner.miner_with_version(node, "0x0") + time.sleep(3) + return self.deploy_and_invoke(account, path, node, try_count) + if "PoolRejectedRBF" in str(e): + try_count -= 1 + for i in range(2): + self.Miner.miner_with_version(node, "0x0") + time.sleep(3) + return self.deploy_and_invoke(account, path, node, try_count) + raise e diff --git a/test_cases/replace_rpc/test_rpc.py b/test_cases/replace_rpc/test_rpc.py index 924bda49..f43a64d6 100644 --- a/test_cases/replace_rpc/test_rpc.py +++ b/test_cases/replace_rpc/test_rpc.py @@ -16,7 +16,7 @@ def setup_class(cls): """ cls.node113 = cls.CkbNode.init_dev_by_port( - cls.CkbNodeConfigPath.CURRENT_TEST, "telnet/node", 8114, 8115 + cls.CkbNodeConfigPath.v120, "telnet/node", 8114, 8115 ) cls.node113.prepare( other_ckb_config={ @@ -249,12 +249,8 @@ def test_telnet(self): 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]}'" - socket = self.node113.subscribe_telnet( - "new_tip_header", self.node113.rpcUrl.replace("http://", "") - ) with pytest.raises(Exception) as exc_info: + socket = self.node113.subscribe_telnet( + "new_tip_header", self.node113.rpcUrl.replace("http://", "") + ) socket.read_very_eager() - expected_error_message = "telnet connection closed" - 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/ws/test_ws_port.py b/test_cases/ws/test_ws_port.py new file mode 100644 index 00000000..c04b065c --- /dev/null +++ b/test_cases/ws/test_ws_port.py @@ -0,0 +1,278 @@ +import time + + +from framework.basic import CkbTest + + +class TestWs(CkbTest): + + @classmethod + def setup_class(cls): + nodes = [ + cls.CkbNode.init_dev_by_port( + cls.CkbNodeConfigPath.CURRENT_TEST, + "cluster1/node{i}".format(i=i), + 8114 + i, + 8225 + i, + ) + for i in range(0, 3) + ] + + cls.cluster = cls.Cluster(nodes) + for i in range(0, 3): + cls.cluster.ckb_nodes[i].prepare( + other_ckb_config={ + "ckb_network_listen_addresses": [ + f"/ip4/0.0.0.0/tcp/{8225 + i}/ws", + f"/ip4/0.0.0.0/tcp/{8225 + i}", + ], + "ckb_logger_filter": "debug", + } + ) + + cls.cluster.start_all_nodes() + cls.Miner.make_tip_height_number(cls.cluster.ckb_nodes[0], 10) + cls.cluster.ckb_nodes[0].start_miner() + for i in range(len(cls.cluster.ckb_nodes)): + cls.cluster.ckb_nodes[i].connected_ws(cls.cluster.ckb_nodes[0]) + cls.Node.wait_cluster_height(cls.cluster, 10, 1000) + + @classmethod + def teardown_class(cls): + print("\nTeardown TestClass1") + cls.cluster.stop_all_nodes() + cls.cluster.clean_all_nodes() + + def test_node_info(self): + """ + get node info + - ws address is in addresses + Returns: + + """ + node_info = self.cluster.ckb_nodes[0].getClient().local_node_info() + print("node_info:", node_info) + assert "ws" in node_info["addresses"][1]["address"] + + def test_get_peers(self): + """ + get peers + - ws address is in addresses + Returns: + + """ + peers = self.cluster.ckb_nodes[0].getClient().get_peers() + assert "ws" in str(peers) + + peers = self.cluster.ckb_nodes[1].getClient().get_peers() + assert "ws" in str(peers) + + def test_send_tx(self): + """ + 1. node2 send tx + 2. node0 miner tx + 3. node0 send tx + 4. node2 miner tx + Returns: + """ + account = self.Ckb_cli.util_key_info_by_private_key( + account_private=self.Config.ACCOUNT_PRIVATE_2 + ) + # 1. node2 send tx + tx_hash = self.Ckb_cli.wallet_transfer_by_private_key( + self.Config.ACCOUNT_PRIVATE_1, + account["address"]["testnet"], + 140, + self.cluster.ckb_nodes[2].getClient().url, + ) + # 2. node0 miner tx + self.Miner.miner_until_tx_committed(self.cluster.ckb_nodes[0], tx_hash, True) + self.cluster.ckb_nodes[0].stop_miner() + + # 3. node0 send tx + tx_hash = self.Ckb_cli.wallet_transfer_by_private_key( + self.Config.ACCOUNT_PRIVATE_1, + account["address"]["testnet"], + 140, + self.cluster.ckb_nodes[0].getClient().url, + ) + # 4. node2 miner tx + self.Miner.miner_until_tx_committed(self.cluster.ckb_nodes[2], tx_hash, True) + tip_number = self.cluster.ckb_nodes[2].getClient().get_tip_block_number() + self.Node.wait_node_height(self.cluster.ckb_nodes[0], tip_number, 1000) + self.cluster.ckb_nodes[0].start_miner() + + def test_link_tcp(self): + """ + 1. new_node start with tcp + 2. new_node link node0 tcp + 3. new_node send tx + 4. node0 miner tx + 5. node0 send tx + 6. new_node miner tx + Returns: + + """ + + # 1. new_node start with tcp + new_node = self.CkbNode.init_dev_by_port( + self.CkbNodeConfigPath.CURRENT_TEST, "cluster1/node_new1", 8134, 8236 + ) + new_node.prepare( + other_ckb_config={ + "ckb_network_listen_addresses": [ + f"/ip4/0.0.0.0/tcp/8229", + f"/ip4/0.0.0.0/tcp/8299/ws", + ], + "ckb_logger_filter": "debug", + } + ) + new_node.start() + self.cluster.ckb_nodes.append(new_node) + + # 2. new_node link node0 tcp + peer_id = self.cluster.ckb_nodes[0].get_peer_id() + node_info = self.cluster.ckb_nodes[0].client.local_node_info() + peer_address = node_info["addresses"][-1]["address"].replace( + "0.0.0.0", "127.0.0.1" + ) + print( + "add node response:", new_node.getClient().add_node(peer_id, peer_address) + ) + tip_number = self.cluster.ckb_nodes[0].getClient().get_tip_block_number() + self.Node.wait_node_height(new_node, tip_number, 1000) + + # 3. new_node send tx + account = self.Ckb_cli.util_key_info_by_private_key( + account_private=self.Config.ACCOUNT_PRIVATE_2 + ) + tx_hash = self.Ckb_cli.wallet_transfer_by_private_key( + self.Config.ACCOUNT_PRIVATE_1, + account["address"]["testnet"], + 140, + new_node.getClient().url, + ) + + # 4. node0 miner tx + self.Miner.miner_until_tx_committed(self.cluster.ckb_nodes[0], tx_hash, True) + self.cluster.ckb_nodes[0].stop_miner() + + # 5. node0 send tx + tx_hash = self.Ckb_cli.wallet_transfer_by_private_key( + self.Config.ACCOUNT_PRIVATE_1, + account["address"]["testnet"], + 140, + self.cluster.ckb_nodes[0].getClient().url, + ) + + # 6. new_node miner tx + self.Miner.miner_until_tx_committed(new_node, tx_hash, True) + tip_number = new_node.getClient().get_tip_block_number() + self.Node.wait_node_height(self.cluster.ckb_nodes[0], tip_number, 1000) + self.cluster.ckb_nodes[0].start_miner() + + def test_remove_node(self): + """ + 1. node1 remove node0 + remove success + 2. get_peers + return [] + Returns: + + """ + # 1. node1 remove node0 + self.cluster.ckb_nodes[1].getClient().remove_node( + self.cluster.ckb_nodes[0].get_peer_id() + ) + time.sleep(3) + tip_number1 = self.cluster.ckb_nodes[1].getClient().get_tip_block_number() + time.sleep(5) + tip_number2 = self.cluster.ckb_nodes[1].getClient().get_tip_block_number() + assert tip_number1 == tip_number2 + + # 2. get_peers + peers = self.cluster.ckb_nodes[1].getClient().get_peers() + assert peers == [] + + def test_119_add_node(self): + """ + 1. start 119 node + 2. connnet node0 ws port + linked failed + 3. connnet node0 ws port and tcp + linked success + 4. 119 send tx + 5. node0 miner tx + 6. node0 send tx + 7. 119 miner tx + Returns: + + """ + # 1. start 119 node + node119 = self.CkbNode.init_dev_by_port( + self.CkbNodeConfigPath.v119, "cluster1/node119", 8124, 8235 + ) + node119.prepare(other_ckb_config={"ckb_logger_filter": "debug"}) + node119.start() + + self.cluster.ckb_nodes.append(node119) + + # 2. connnet node0 ws port + node119.connected_ws(self.cluster.ckb_nodes[0]) + time.sleep(10) + assert node119.getClient().get_tip_block_number() == 0 + peers = node119.getClient().get_peers() + assert peers == [] + + # 3. connnet node0 ws port and tcp + node119.restart() + node119.connected_all_address(self.cluster.ckb_nodes[0]) + self.Node.wait_node_height(node119, 10, 1000) + + # 4. 119 send tx + account = self.Ckb_cli.util_key_info_by_private_key( + account_private=self.Config.ACCOUNT_PRIVATE_2 + ) + tx_hash = self.Ckb_cli.wallet_transfer_by_private_key( + self.Config.ACCOUNT_PRIVATE_1, + account["address"]["testnet"], + 140, + node119.getClient().url, + ) + + # 5. node0 miner tx + self.Miner.miner_until_tx_committed(self.cluster.ckb_nodes[0], tx_hash, True) + self.cluster.ckb_nodes[0].stop_miner() + + # 6. node0 send tx + tx_hash = self.Ckb_cli.wallet_transfer_by_private_key( + self.Config.ACCOUNT_PRIVATE_1, + account["address"]["testnet"], + 140, + self.cluster.ckb_nodes[0].getClient().url, + ) + + # 7. 119 miner tx + self.Miner.miner_until_tx_committed(node119, tx_hash, True) + tip_number = node119.getClient().get_tip_block_number() + self.Node.wait_node_height(self.cluster.ckb_nodes[0], tip_number, 1000) + self.cluster.ckb_nodes[0].start_miner() + + def test_ckb_network_reuse_tcp_with_ws(self): + # 1. new_node start with tcp + new_node = self.CkbNode.init_dev_by_port( + self.CkbNodeConfigPath.CURRENT_TEST, "cluster1/node_new", 8134, 8236 + ) + new_node.prepare( + other_ckb_config={ + "ckb_network_listen_addresses": [ + f"/ip4/0.0.0.0/tcp/8299/ws", + ], + "ckb_network_reuse_tcp_with_ws": "true", + } + ) + new_node.start() + self.cluster.ckb_nodes.append(new_node) + local_node_info = new_node.getClient().local_node_info() + assert "/ip4/0.0.0.0/tcp/8299/ws" in local_node_info["addresses"][0]["address"] + assert len(local_node_info["addresses"]) == 1