diff --git a/docs/running_tests/execute/remote.md b/docs/running_tests/execute/remote.md index 74839ead71..77c3231316 100644 --- a/docs/running_tests/execute/remote.md +++ b/docs/running_tests/execute/remote.md @@ -18,10 +18,10 @@ uv run execute remote --fork=Prague --rpc-endpoint=https://rpc.endpoint.io --rpc The value needs to be a private key that is used to sign the transactions that deploy the contracts and fund the accounts. -One last requirement is that the `--rpc-chain-id` flag is set to the chain id of the network that is being tested: +One last requirement is that the `--chain-id` flag is set to the chain id of the network that is being tested: ```bash -uv run execute remote --fork=Prague --rpc-endpoint=https://rpc.endpoint.io --rpc-seed-key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f --rpc-chain-id 12345 +uv run execute remote --fork=Prague --rpc-endpoint=https://rpc.endpoint.io --rpc-seed-key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f --chain-id 12345 ``` ## Engine RPC Endpoint (Optional) @@ -31,13 +31,13 @@ By default, the `execute remote` command assumes that the execution client is co To use this feature, you need to provide both the `--engine-endpoint` and JWT authentication: ```bash -uv run execute remote --fork=Prague --rpc-endpoint=https://rpc.endpoint.io --rpc-seed-key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f --rpc-chain-id 12345 --engine-endpoint=https://engine.endpoint.io --engine-jwt-secret "your-jwt-secret-here" +uv run execute remote --fork=Prague --rpc-endpoint=https://rpc.endpoint.io --rpc-seed-key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f --chain-id 12345 --engine-endpoint=https://engine.endpoint.io --engine-jwt-secret "your-jwt-secret-here" ``` Alternatively, you can provide the JWT secret from a file: ```bash -uv run execute remote --fork=Prague --rpc-endpoint=https://rpc.endpoint.io --rpc-seed-key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f --rpc-chain-id 12345 --engine-endpoint=https://engine.endpoint.io --engine-jwt-secret-file /path/to/jwt-secret.txt +uv run execute remote --fork=Prague --rpc-endpoint=https://rpc.endpoint.io --rpc-seed-key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f --chain-id 12345 --engine-endpoint=https://engine.endpoint.io --engine-jwt-secret-file /path/to/jwt-secret.txt ``` The JWT secret file must contain only the JWT secret as a hex string. @@ -49,7 +49,7 @@ The `execute remote` command will connect to the client via the RPC endpoint and It is recommended to only run a subset of the tests when executing on a live network. To do so, a path to a specific test can be provided to the command: ```bash -uv run execute remote --fork=Prague --rpc-endpoint=https://rpc.endpoint.io --rpc-seed-key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f --rpc-chain-id 12345 ./tests/prague/eip7702_set_code_tx/test_set_code_txs.py::test_set_code_to_sstore +uv run execute remote --fork=Prague --rpc-endpoint=https://rpc.endpoint.io --rpc-seed-key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f --chain-id 12345 ./tests/prague/eip7702_set_code_tx/test_set_code_txs.py::test_set_code_to_sstore ``` ## Address Stubs for Pre-deployed Contracts @@ -70,19 +70,19 @@ You can provide address stubs in several formats: **JSON string:** ```bash -uv run execute remote --fork=Prague --rpc-endpoint=https://rpc.endpoint.io --rpc-seed-key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f --rpc-chain-id 12345 --address-stubs '{"DEPOSIT_CONTRACT": "0x00000000219ab540356cbb839cbe05303d7705fa", "UNISWAP_V3_FACTORY": "0x1F98431c8aD98523631AE4a59f267346ea31F984"}' +uv run execute remote --fork=Prague --rpc-endpoint=https://rpc.endpoint.io --rpc-seed-key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f --chain-id 12345 --address-stubs '{"DEPOSIT_CONTRACT": "0x00000000219ab540356cbb839cbe05303d7705fa", "UNISWAP_V3_FACTORY": "0x1F98431c8aD98523631AE4a59f267346ea31F984"}' ``` **JSON file:** ```bash -uv run execute remote --fork=Prague --rpc-endpoint=https://rpc.endpoint.io --rpc-seed-key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f --rpc-chain-id 12345 --address-stubs ./contracts.json +uv run execute remote --fork=Prague --rpc-endpoint=https://rpc.endpoint.io --rpc-seed-key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f --chain-id 12345 --address-stubs ./contracts.json ``` **YAML file:** ```bash -uv run execute remote --fork=Prague --rpc-endpoint=https://rpc.endpoint.io --rpc-seed-key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f --rpc-chain-id 12345 --address-stubs ./contracts.yaml +uv run execute remote --fork=Prague --rpc-endpoint=https://rpc.endpoint.io --rpc-seed-key 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f --chain-id 12345 --address-stubs ./contracts.yaml ``` ### Address Stubs File Format diff --git a/packages/testing/src/execution_testing/cli/pytest_commands/execute.py b/packages/testing/src/execution_testing/cli/pytest_commands/execute.py index 7bfa8ea684..303ff5fcdd 100644 --- a/packages/testing/src/execution_testing/cli/pytest_commands/execute.py +++ b/packages/testing/src/execution_testing/cli/pytest_commands/execute.py @@ -61,7 +61,7 @@ def command(pytest_args: List[str], **_kwargs: Any) -> None: "Execute tests using a remote RPC endpoint.", required_args=[ "--rpc-endpoint=http://localhost:8545", - "--rpc-chain-id=1", + "--chain-id=1", "--rpc-seed-key=1", ], ) diff --git a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/execute.py b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/execute.py index 64092f78a6..452938291b 100644 --- a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/execute.py +++ b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/execute.py @@ -15,7 +15,6 @@ from execution_testing.rpc import EngineRPC, EthRPC from execution_testing.specs import BaseTest from execution_testing.test_types import ( - ChainConfigDefaults, EnvironmentDefaults, TransactionDefaults, ) @@ -106,15 +105,6 @@ def pytest_addoption(parser: pytest.Parser) -> None: "Time to wait after sending a forkchoice_updated before getting the payload." ), ) - execute_group.addoption( - "--chain-id", - action="store", - dest="chain_id", - required=False, - type=int, - default=None, - help="ID of the chain where the tests will be executed.", - ) report_group = parser.getgroup( "tests", "Arguments defining html report behavior" @@ -172,22 +162,6 @@ def pytest_configure(config: pytest.Config) -> None: config.skip_transition_forks = True # type: ignore[attr-defined] config.single_fork_mode = True # type: ignore[attr-defined] - # Configure the chain ID for the tests. - rpc_chain_id = config.getoption("rpc_chain_id", None) - chain_id = config.getoption("chain_id", None) - if rpc_chain_id is not None or chain_id is not None: - if rpc_chain_id is not None and chain_id is not None: - if chain_id != rpc_chain_id: - pytest.exit( - "Conflicting chain ID configuration. " - "The --rpc-chain-id flag is deprecated and will be removed in a future " - "release. Use --chain-id instead." - ) - if rpc_chain_id is not None: - ChainConfigDefaults.chain_id = rpc_chain_id - if chain_id is not None: - ChainConfigDefaults.chain_id = chain_id - def pytest_metadata(metadata: dict[str, Any]) -> None: """Add or remove metadata to/from the pytest report.""" diff --git a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/execute_flags/execute_flags.py b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/execute_flags/execute_flags.py new file mode 100644 index 0000000000..7e7696f3e6 --- /dev/null +++ b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/execute_flags/execute_flags.py @@ -0,0 +1,63 @@ +"""Plugin that handles correct setting of chain-id and rpc-chain-id.""" + +import pytest + +from execution_testing.test_types.chain_config_types import ChainConfigDefaults + + +def pytest_addoption(parser: pytest.Parser) -> None: + """Add command-line options to pytest.""" + execute_commands_group = parser.getgroup( + "execute", + "Arguments defining chain configuration for execute commands", + ) + execute_commands_group.addoption( + "--chain-id", + action="store", + dest="chain_id", + required=False, + type=int, + default=None, + help="ID of the chain where the tests will be executed.", + ) + execute_commands_group.addoption( + "--rpc-chain-id", + action="store", + dest="rpc_chain_id", + required=False, + type=int, + default=None, + help=( + "ID of the chain where the tests will be executed. This flag " + "is deprecated and will be removed in a future release. " + "Use --chain-id instead." + ), + ) + + +@pytest.hookimpl(tryfirst=True) +def pytest_configure(config: pytest.Config) -> None: + """ + Set the provided command-line arguments. + """ + # Skip validation if we're just showing help + if config.option.help: + return + + chain_id = config.getoption("chain_id") + rpc_chain_id = config.getoption("rpc_chain_id") + + if not ((chain_id is None) ^ (rpc_chain_id is None)): # XOR + pytest.exit( + "ERROR: you must either pass --chain-id or --rpc-chain-id, " + "but not both!\n" + f"You passed: chain-id={chain_id}, rpc-chain-id={rpc_chain_id}", + returncode=4, + ) + + # Use rpc_chain_id if chain_id is not provided (for backwards compatibility) + if not chain_id: + chain_id = rpc_chain_id + + # write to config + ChainConfigDefaults.chain_id = chain_id diff --git a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/rpc/remote.py b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/rpc/remote.py index 81e736a6ff..423697aa65 100644 --- a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/rpc/remote.py +++ b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/rpc/remote.py @@ -26,26 +26,6 @@ def pytest_addoption(parser: pytest.Parser) -> None: dest="rpc_endpoint", help="RPC endpoint to an execution client", ) - remote_rpc_group.addoption( - "--chain-id", - action="store", - dest="chain_id", - required=False, - type=int, - default=None, - help="ID of the chain where the tests will be executed.", - ) - remote_rpc_group.addoption( - "--rpc-chain-id", - action="store", - dest="rpc_chain_id", - required=False, - type=int, - default=None, - help="DEPRECATED: ID of the chain where the tests will be executed. " - "This flag is deprecated and will be removed in a future release." - "Use --chain-id instead.", - ) remote_rpc_group.addoption( "--tx-wait-timeout", action="store", @@ -100,27 +80,6 @@ def pytest_addoption(parser: pytest.Parser) -> None: def pytest_configure(config: pytest.Config) -> None: """Check if a chain ID configuration is provided.""" - rpc_chain_id = config.getoption("rpc_chain_id", None) - chain_id = config.getoption("chain_id", None) - - if rpc_chain_id is None and chain_id is None: - pytest.exit("No chain ID configuration found. Please use --chain-id.") - - # Handle both --chain-id and deprecated --rpc-chain-id - if rpc_chain_id is not None and chain_id is not None: - if chain_id != rpc_chain_id: - pytest.exit( - "Conflicting chain ID configuration. " - "The --rpc-chain-id flag is deprecated and will be removed in a future " - "release. Use --chain-id instead." - ) - - # Set the chain ID - if chain_id is not None: - ChainConfigDefaults.chain_id = chain_id - elif rpc_chain_id is not None: - ChainConfigDefaults.chain_id = rpc_chain_id - # Verify the chain ID configuration is consistent with the remote RPC endpoint rpc_endpoint = config.getoption("rpc_endpoint") eth_rpc = EthRPC(rpc_endpoint) diff --git a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/help/help.py b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/help/help.py index 42f7059dcd..7fd815f3da 100644 --- a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/help/help.py +++ b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/help/help.py @@ -112,6 +112,7 @@ def pytest_configure(config: pytest.Config) -> None: "pre-allocation behavior during test execution", "sender key fixtures", "remote seed sender", + "chain configuration", ], ) elif config.getoption("show_execute_hive_help"): @@ -124,6 +125,7 @@ def pytest_configure(config: pytest.Config) -> None: "pre-allocation behavior during test execution", "sender key fixtures", "remote seed sender", + "chain configuration", ], ) elif config.getoption("show_execute_recover_help"): @@ -134,6 +136,7 @@ def pytest_configure(config: pytest.Config) -> None: "fund recovery", "remote RPC configuration", "remote seed sender", + "chain configuration", ], ) elif config.getoption("show_execute_eth_config_help"): @@ -142,6 +145,7 @@ def pytest_configure(config: pytest.Config) -> None: "pytest-execute-eth-config.ini", [ "eth_config", + "chain configuration", ], ) diff --git a/packages/testing/src/execution_testing/cli/pytest_commands/pytest_ini_files/pytest-execute-eth-config.ini b/packages/testing/src/execution_testing/cli/pytest_commands/pytest_ini_files/pytest-execute-eth-config.ini index cd676d1485..1cb505ad13 100644 --- a/packages/testing/src/execution_testing/cli/pytest_commands/pytest_ini_files/pytest-execute-eth-config.ini +++ b/packages/testing/src/execution_testing/cli/pytest_commands/pytest_ini_files/pytest-execute-eth-config.ini @@ -4,6 +4,7 @@ minversion = 7.0 python_files = *.py # Note: register new markers via src/execution_testing/cli/pytest_commands/plugins/shared/execute_fill.py addopts = + -p execution_testing.cli.pytest_commands.plugins.execute.execute_flags.execute_flags -p execution_testing.cli.pytest_commands.plugins.execute.eth_config.eth_config -p execution_testing.cli.pytest_commands.plugins.help.help -p execution_testing.cli.pytest_commands.plugins.custom_logging.plugin_logging diff --git a/packages/testing/src/execution_testing/cli/pytest_commands/pytest_ini_files/pytest-execute-recover.ini b/packages/testing/src/execution_testing/cli/pytest_commands/pytest_ini_files/pytest-execute-recover.ini index 744b272610..3f0afb5fe7 100644 --- a/packages/testing/src/execution_testing/cli/pytest_commands/pytest_ini_files/pytest-execute-recover.ini +++ b/packages/testing/src/execution_testing/cli/pytest_commands/pytest_ini_files/pytest-execute-recover.ini @@ -3,6 +3,7 @@ console_output_style = count minversion = 7.0 python_files = *.py addopts = + -p execution_testing.cli.pytest_commands.plugins.execute.execute_flags.execute_flags -p execution_testing.cli.pytest_commands.plugins.execute.rpc.remote -p execution_testing.cli.pytest_commands.plugins.execute.recover -p execution_testing.cli.pytest_commands.plugins.help.help diff --git a/packages/testing/src/execution_testing/cli/pytest_commands/pytest_ini_files/pytest-execute.ini b/packages/testing/src/execution_testing/cli/pytest_commands/pytest_ini_files/pytest-execute.ini index 29a8f52767..51ba2052d4 100644 --- a/packages/testing/src/execution_testing/cli/pytest_commands/pytest_ini_files/pytest-execute.ini +++ b/packages/testing/src/execution_testing/cli/pytest_commands/pytest_ini_files/pytest-execute.ini @@ -5,6 +5,7 @@ python_files = test_*.py testpaths = tests/ # Note: register new markers via src/execution_testing/cli/pytest_commands/plugins/shared/execute_fill.py addopts = + -p execution_testing.cli.pytest_commands.plugins.execute.execute_flags.execute_flags -p execution_testing.cli.pytest_commands.plugins.concurrency -p execution_testing.cli.pytest_commands.plugins.execute.sender -p execution_testing.cli.pytest_commands.plugins.execute.pre_alloc diff --git a/packages/testing/src/execution_testing/cli/tests/test_pytest_execute_command.py b/packages/testing/src/execution_testing/cli/tests/test_pytest_execute_command.py index 576a1256b9..1f8179ec0f 100644 --- a/packages/testing/src/execution_testing/cli/tests/test_pytest_execute_command.py +++ b/packages/testing/src/execution_testing/cli/tests/test_pytest_execute_command.py @@ -41,3 +41,69 @@ def test_execute_subcommands_have_help_text() -> None: assert recover.__doc__ is not None assert "recover" in recover.__doc__.lower() + + +def test_execute_main_help(runner: CliRunner) -> None: + """Test that execute --help works without errors.""" + result = runner.invoke(execute, ["--help"]) + assert result.exit_code == 0 + assert "Execute command to run tests" in result.output + + +def test_execute_remote_help(runner: CliRunner) -> None: + """Test that execute remote --help works without argument conflicts.""" + result = runner.invoke(execute, ["remote", "--help"]) + assert result.exit_code == 0 + assert "After displaying help" in result.output + # Verify no argparse conflicts with --chain-id + assert "conflicting option string" not in result.output + + +def test_execute_recover_help(runner: CliRunner) -> None: + """Test that execute recover --help works without argument conflicts.""" + result = runner.invoke(execute, ["recover", "--help"]) + assert result.exit_code == 0 + assert "After displaying help" in result.output + # Verify --chain-id is available + assert "--chain-id" in result.output + # Verify no argparse conflicts + assert "conflicting option string" not in result.output + + +def test_execute_hive_help(runner: CliRunner) -> None: + """Test that execute hive --help works without errors.""" + result = runner.invoke(execute, ["hive", "--help"]) + assert result.exit_code == 0 + assert "After displaying help" in result.output + + +def test_execute_eth_config_help(runner: CliRunner) -> None: + """Test that execute eth-config --help works without errors.""" + result = runner.invoke(execute, ["eth-config", "--help"]) + assert result.exit_code == 0 + assert "After displaying help" in result.output + + +def test_all_execute_subcommands_help_no_conflicts(runner: CliRunner) -> None: + """Test that all execute subcommands --help work without argument conflicts. + + This is a regression test for issue where --chain-id was defined in multiple + plugins, causing argparse.ArgumentError conflicts. + """ + subcommands = ["remote", "recover", "hive", "eth-config"] + + for subcommand in subcommands: + result = runner.invoke(execute, [subcommand, "--help"]) + assert result.exit_code == 0, ( + f"execute {subcommand} --help failed with exit code {result.exit_code}\n" + f"Output: {result.output}" + ) + # Ensure no argparse conflicts + assert "ArgumentError" not in result.output, ( + f"execute {subcommand} --help has ArgumentError\n" + f"Output: {result.output}" + ) + assert "conflicting option string" not in result.output, ( + f"execute {subcommand} --help has conflicting option string\n" + f"Output: {result.output}" + ) diff --git a/tests/benchmark/stateful/bloatnet/README.md b/tests/benchmark/stateful/bloatnet/README.md index e72213b55a..c3bcf9cfc7 100644 --- a/tests/benchmark/stateful/bloatnet/README.md +++ b/tests/benchmark/stateful/bloatnet/README.md @@ -50,7 +50,7 @@ Create a JSON file (`stubs.json`) mapping test-specific stub names to deployed c uv run execute remote \ --rpc-endpoint http://localhost:8545 \ --rpc-seed-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ - --rpc-chain-id 1337 \ + --chain-id 1337 \ --address-stubs geth_stubs.json \ --fork Prague \ tests/benchmark/stateful/bloatnet/test_single_opcode.py::test_sload_empty_erc20_balanceof \