diff --git a/Cargo.lock b/Cargo.lock index 8e3f874888d..97edb8937e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4434,6 +4434,15 @@ dependencies = [ "zstd", ] +[[package]] +name = "near-dump-test-contract" +version = "0.0.0" +dependencies = [ + "anyhow", + "clap", + "near-test-contracts", +] + [[package]] name = "near-dyn-configs" version = "0.0.0" @@ -5526,6 +5535,7 @@ dependencies = [ "near-config-utils", "near-crypto", "near-database-tool", + "near-dump-test-contract", "near-dyn-configs", "near-flat-storage", "near-fork-network", diff --git a/Cargo.toml b/Cargo.toml index 05664f2df5c..86b83be2e98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -274,6 +274,7 @@ near-telemetry = { path = "chain/telemetry" } near-test-contracts = { path = "runtime/near-test-contracts" } near-time = { path = "core/time" } near-undo-block = { path = "tools/undo-block" } +near-dump-test-contract = { path = "tools/dump-test-contract" } near-vm-test-api = { path = "runtime/near-vm/test-api" } near-vm-compiler = { path = "runtime/near-vm/compiler" } near-vm-compiler-singlepass = { path = "runtime/near-vm/compiler-singlepass" } diff --git a/neard/Cargo.toml b/neard/Cargo.toml index ae1ef6c1c70..e5ec2dcf9ee 100644 --- a/neard/Cargo.toml +++ b/neard/Cargo.toml @@ -56,6 +56,7 @@ near-state-parts-dump-check.workspace = true near-state-viewer.workspace = true near-store.workspace = true near-undo-block.workspace = true +near-dump-test-contract.workspace = true [build-dependencies] anyhow.workspace = true @@ -66,7 +67,10 @@ default = ["json_rpc", "rosetta_rpc"] performance_stats = ["nearcore/performance_stats"] c_memory_stats = ["nearcore/c_memory_stats"] -test_features = ["nearcore/test_features"] +test_features = [ + "nearcore/test_features", + "near-dump-test-contract/test_features", +] expensive_tests = ["nearcore/expensive_tests"] rosetta_rpc = ["nearcore/rosetta_rpc"] json_rpc = ["nearcore/json_rpc"] diff --git a/neard/src/cli.rs b/neard/src/cli.rs index fbef7799e0b..3463a5209b3 100644 --- a/neard/src/cli.rs +++ b/neard/src/cli.rs @@ -6,6 +6,7 @@ use near_client::ConfigUpdater; use near_cold_store_tool::ColdStoreCommand; use near_config_utils::DownloadConfigType; use near_database_tool::commands::DatabaseCommand; +use near_dump_test_contract::DumpTestContractCommand; use near_dyn_configs::{UpdatableConfigLoader, UpdatableConfigLoaderError, UpdatableConfigs}; use near_flat_storage::commands::FlatStorageCommand; use near_fork_network::cli::ForkNetworkCommand; @@ -147,6 +148,9 @@ impl NeardCmd { NeardSubCommand::ReplayArchive(cmd) => { cmd.run(&home_dir, genesis_validation)?; } + NeardSubCommand::DumpTestContracts(cmd) => { + cmd.run()?; + } }; Ok(()) } @@ -253,6 +257,9 @@ pub(super) enum NeardSubCommand { /// Replays the blocks in the chain from an archival node. ReplayArchive(ReplayArchiveCommand), + + /// Placeholder for test contracts subcommand + DumpTestContracts(DumpTestContractCommand), } #[allow(unused)] diff --git a/pytest/lib/cluster.py b/pytest/lib/cluster.py index 13377e081ab..ae549ddcba8 100644 --- a/pytest/lib/cluster.py +++ b/pytest/lib/cluster.py @@ -875,6 +875,7 @@ def init_cluster( is_local = config['local'] near_root = config['near_root'] binary_name = config.get('binary_name', 'neard') + binary_path = os.path.join(near_root, binary_name) if extra_state_dumper: num_observers += 1 @@ -882,7 +883,6 @@ def init_cluster( logger.info("Creating %s cluster configuration with %s nodes" % ("LOCAL" if is_local else "REMOTE", num_nodes + num_observers)) - binary_path = os.path.join(near_root, binary_name) process = subprocess.Popen( [ binary_path, diff --git a/pytest/lib/utils.py b/pytest/lib/utils.py index ccf6660c0da..7b78a36ec6b 100644 --- a/pytest/lib/utils.py +++ b/pytest/lib/utils.py @@ -11,12 +11,15 @@ import time import typing import requests +import subprocess from prometheus_client.parser import text_string_to_metric_families from retrying import retry from rc import gcloud import cluster import transaction +from branches import _REPO_DIR + from configured_logger import logger @@ -125,7 +128,7 @@ def count(self, pattern: str) -> int: class MetricsTracker: """Helper class to collect prometheus metrics from the node. - + Usage: tracker = MetricsTracker(node) assert tracker.get_int_metric_value("near-connections") == 2 @@ -242,16 +245,19 @@ def load_binary_file(filepath): def load_test_contract( - filename: str = 'backwards_compatible_rs_contract.wasm') -> bytearray: - """Loads a WASM file from near-test-contracts package. - - This is just a convenience function around load_binary_file which loads - files from ../runtime/near-test-contracts/res directory. By default - test_contract_rs.wasm is loaded. - """ - repo_dir = pathlib.Path(__file__).resolve().parents[2] - path = repo_dir / 'runtime/near-test-contracts/res' / filename - return load_binary_file(path) + filename: str = 'backwards_compatible_rs_contract.wasm', + config: cluster.Config = cluster.DEFAULT_CONFIG, +) -> bytearray: + """Loads a WASM file from neard.""" + + near_root = config['near_root'] + binary_name = config.get('binary_name', 'neard') + binary_path = os.path.join(near_root, binary_name) + + logger.info(f'Loading test contract {filename}') + cmd = [binary_path, 'dump-test-contracts', '--contract-name', filename] + output = subprocess.check_output(cmd) + return output def user_name(): diff --git a/pytest/tests/sanity/backward_compatible.py b/pytest/tests/sanity/backward_compatible.py index 49e6623a791..018a3359e7b 100755 --- a/pytest/tests/sanity/backward_compatible.py +++ b/pytest/tests/sanity/backward_compatible.py @@ -65,13 +65,15 @@ def main(): block_height = stable_node.get_latest_block().height nonce = block_height * 1_000_000 - 1 - tx = sign_deploy_contract_tx(new_signer_key, utils.load_test_contract(), - nonce, block_hash) + test_contract = utils.load_test_contract( + config=executables.current.node_config()) + tx = sign_deploy_contract_tx(new_signer_key, test_contract, nonce, + block_hash) res = stable_node.send_tx_and_wait(tx, timeout=20) assert 'error' not in res, res - tx = sign_deploy_contract_tx(stable_node.signer_key, - utils.load_test_contract(), 3, block_hash) + tx = sign_deploy_contract_tx(stable_node.signer_key, test_contract, 3, + block_hash) res = stable_node.send_tx_and_wait(tx, timeout=20) assert 'error' not in res, res diff --git a/pytest/tests/sanity/congestion_control.py b/pytest/tests/sanity/congestion_control.py index ec598393b59..fd0f754c84e 100644 --- a/pytest/tests/sanity/congestion_control.py +++ b/pytest/tests/sanity/congestion_control.py @@ -52,6 +52,7 @@ class CongestionControlTest(unittest.TestCase): def setUp(self): + self.config = load_config() self.threads = [] def tearDown(self): @@ -265,7 +266,6 @@ def __load(self, node: BaseNode, sender_account: Key, target_account: Key): def __setup_node(self) -> BaseNode: logger.info("Setting up the node") epoch_length = 100 - config = load_config() genesis_config_changes = [ ("epoch_length", epoch_length), ("shard_layout", SHARD_LAYOUT), @@ -285,12 +285,12 @@ def __setup_node(self) -> BaseNode: num_nodes=1, num_observers=0, num_shards=NUM_SHARDS, - config=config, + config=self.config, genesis_config_changes=genesis_config_changes, client_config_changes=client_config_changes, ) - node = spin_up_node(config, near_root, node_dir, 0) + node = spin_up_node(self.config, near_root, node_dir, 0) # Save a block hash to use for creating transactions. Querying it every # time when creating a new transaction is really slow. @@ -325,7 +325,7 @@ def __create_accounts(self, node: BaseNode, accounts: list[Key]): def __deploy_contracts(self, node: BaseNode, accounts: list[Key]): logger.info("Deploying contracts") - contract = load_test_contract('test_contract_rs.wasm') + contract = load_test_contract('rs_contract.wasm', self.config) deploy_contract_tx_list = list() for account in accounts: tx_hash = self.__deploy_contract(node, account, contract) diff --git a/pytest/tests/sanity/db_migration.py b/pytest/tests/sanity/db_migration.py index 47a6d5dfaf2..91394391d20 100644 --- a/pytest/tests/sanity/db_migration.py +++ b/pytest/tests/sanity/db_migration.py @@ -27,10 +27,10 @@ } -def deploy_contract(node): +def deploy_contract(node, config): hash_ = node.get_latest_block().hash_bytes - tx = sign_deploy_contract_tx(node.signer_key, utils.load_test_contract(), - 10, hash_) + test_contract = utils.load_test_contract(config=config) + tx = sign_deploy_contract_tx(node.signer_key, test_contract, 10, hash_) node.send_tx_and_wait(tx, timeout=15) utils.wait_for_blocks(node, count=3) @@ -113,7 +113,7 @@ def main(): logging.info("Running the stable node...") utils.wait_for_blocks(node, count=EPOCH_LENGTH) logging.info("Blocks are being produced, sending some tx...") - deploy_contract(node) + deploy_contract(node, executables.current.node_config()) send_some_tx(node) unstake_and_stake(nodes[1], node) diff --git a/pytest/tests/sanity/slow_chunk.py b/pytest/tests/sanity/slow_chunk.py index b20cef96c90..5e0e3bdfc32 100644 --- a/pytest/tests/sanity/slow_chunk.py +++ b/pytest/tests/sanity/slow_chunk.py @@ -75,7 +75,7 @@ def __deploy_contract(self, node): logger.info("Deploying contract.") block_hash = node.get_latest_block().hash_bytes - contract = load_test_contract('test_contract_rs.wasm') + contract = load_test_contract('rs_contract.wasm') tx = sign_deploy_contract_tx(node.signer_key, contract, 10, block_hash) node.send_tx(tx) diff --git a/pytest/tests/sanity/upgradable.py b/pytest/tests/sanity/upgradable.py index 6861650a9d4..79febc20d3d 100755 --- a/pytest/tests/sanity/upgradable.py +++ b/pytest/tests/sanity/upgradable.py @@ -166,8 +166,8 @@ def test_upgrade() -> None: # deploy a contract hash = nodes[0].get_latest_block().hash_bytes - tx = sign_deploy_contract_tx(nodes[0].signer_key, - utils.load_test_contract(), 1, hash) + test_contract = utils.load_test_contract(config=config) + tx = sign_deploy_contract_tx(nodes[0].signer_key, test_contract, 1, hash) res = nodes[0].send_tx_and_wait(tx, timeout=20) assert 'error' not in res, res diff --git a/tools/dump-test-contract/Cargo.toml b/tools/dump-test-contract/Cargo.toml new file mode 100644 index 00000000000..07669df3456 --- /dev/null +++ b/tools/dump-test-contract/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "near-dump-test-contract" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +anyhow.workspace = true +clap.workspace = true + +near-test-contracts.workspace = true + +[features] +nightly_protocol = [] +nightly = [ + "near-test-contracts/nightly", + "nightly_protocol", +] +test_features = [ + "near-test-contracts/test_features", +] diff --git a/tools/dump-test-contract/src/lib.rs b/tools/dump-test-contract/src/lib.rs new file mode 100644 index 00000000000..9647bb2bc36 --- /dev/null +++ b/tools/dump-test-contract/src/lib.rs @@ -0,0 +1,44 @@ +use std::io::Write; + +use clap::Parser; +use near_test_contracts::{ + backwards_compatible_rs_contract, congestion_control_test_contract, estimator_contract, + ft_contract, fuzzing_contract, nightly_rs_contract, rs_contract, smallest_rs_contract, + trivial_contract, ts_contract, +}; + +#[derive(Parser)] +pub struct DumpTestContractCommand { + #[arg(short, long)] + contract_name: String, +} + +impl DumpTestContractCommand { + pub fn run(&self) -> anyhow::Result<()> { + let Some((name, extension)) = self.contract_name.rsplit_once(".") else { + panic!("argument expected in `filename.wasm` form"); + }; + + if extension != "wasm" { + panic!("unsupported filetype `{extension}`"); + } + + let code = match &*name { + "trivial" => trivial_contract(), + "rs_contract" => rs_contract(), + "nightly_rs_contract" => nightly_rs_contract(), + "backwards_compatible_rs_contract" => backwards_compatible_rs_contract(), + "ts_contract" => ts_contract(), + "fuzzing_contract" => fuzzing_contract(), + "ft_contract" => ft_contract(), + "smallest_rs_contract" => smallest_rs_contract(), + "estimator_contract" => estimator_contract(), + "congestion_control_test_contract" => congestion_control_test_contract(), + _ => panic!("unknown contract {name}"), + }; + + std::io::stdout().write_all(&code).expect("while writing code to stdout"); + + Ok(()) + } +}