diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7886a368ce..a976472c23 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,7 +19,7 @@ jobs: timeout-minutes: 240 strategy: matrix: - tests: [unmarked, ibc, ibc_rly_evm, ibc_rly_gas, ibc_timeout, ibc_update_client, ica, gov, upgrade, slow, gas, mint] + tests: [unmarked, ibc, ibc_rly_evm, ibc_rly_gas, ibc_timeout, ibc_update_client, ica, gov, upgrade, slow, gas, mint, evm] env: TESTS_TO_RUN: ${{ matrix.tests }} steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index f6e05c87d9..91b91debab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * [#1875](https://github.com/crypto-org-chain/cronos/pull/1875) Support for preinstalls * [#1882](https://github.com/crypto-org-chain/cronos/pull/1882) Support for eip2935 * [#1880](https://github.com/crypto-org-chain/cronos/pull/1880) Move module from v2 to v1 to follow semver convention +* [#1933](https://github.com/crypto-org-chain/cronos/pull/1933) Chore: add validation for HeaderHashNum and HistoryServeWindow in evm params *Dec 4, 2025* diff --git a/go.mod b/go.mod index efea65c6f6..7926e40459 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/hashicorp/go-metrics v0.5.4 github.com/linxGnu/grocksdb v1.9.10-0.20250331012329-9d5f074653d1 github.com/spf13/cast v1.10.0 - github.com/spf13/cobra v1.10.1 + github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.11.1 @@ -303,7 +303,7 @@ replace ( // release/v1.15 github.com/ethereum/go-ethereum => github.com/crypto-org-chain/go-ethereum v1.10.20-0.20250815065500-a4fbafcae0dd // develop - github.com/evmos/ethermint => github.com/crypto-org-chain/ethermint v0.22.1-0.20251204034614-3b1542d755b6 + github.com/evmos/ethermint => github.com/crypto-org-chain/ethermint v0.22.1-0.20251208051817-9f5d1db3246d // Fix upstream GHSA-h395-qcrw-5vmq and GHSA-3vp4-m3rf-835h vulnerabilities. // TODO Remove it: https://github.com/cosmos/cosmos-sdk/issues/10409 github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.9.0 diff --git a/go.sum b/go.sum index 3a373a8a6e..01b7fdecd1 100644 --- a/go.sum +++ b/go.sum @@ -911,8 +911,8 @@ github.com/crypto-org-chain/cosmos-sdk/store v0.0.0-20241217090828-cfbca9fe8254 github.com/crypto-org-chain/cosmos-sdk/store v0.0.0-20241217090828-cfbca9fe8254/go.mod h1:8DwVTz83/2PSI366FERGbWSH7hL6sB7HbYp8bqksNwM= github.com/crypto-org-chain/cosmos-sdk/x/tx v0.0.0-20241217090828-cfbca9fe8254 h1:JzLOFRiKsDtLJt5h0M0jkEIPDKvFFyja7VEp7gG6O9U= github.com/crypto-org-chain/cosmos-sdk/x/tx v0.0.0-20241217090828-cfbca9fe8254/go.mod h1:V6DImnwJMTq5qFjeGWpXNiT/fjgE4HtmclRmTqRVM3w= -github.com/crypto-org-chain/ethermint v0.22.1-0.20251204034614-3b1542d755b6 h1:PGpc3xvGh+vCwi+0MVgdLlMWYPfR+ATD46QHcIqfoAI= -github.com/crypto-org-chain/ethermint v0.22.1-0.20251204034614-3b1542d755b6/go.mod h1:avrVK14+Kwx8rn4B2uzkOVc/TLYPeLcf19dbAII+7lM= +github.com/crypto-org-chain/ethermint v0.22.1-0.20251208051817-9f5d1db3246d h1:x57dT8gpztM9OEx2DhhQYxZ0RGyeHXzPOPHrxRyMJzc= +github.com/crypto-org-chain/ethermint v0.22.1-0.20251208051817-9f5d1db3246d/go.mod h1:bfcBQITxYzsbXuF+VWOtL3EhBi9mEIOzsFT0UvB+mso= github.com/crypto-org-chain/go-block-stm v0.0.0-20241213061541-7afe924fb4a6 h1:6KPEi8dWkDSBddQb4NAvEXmNnTXymF3yVeTaT4Hz1iU= github.com/crypto-org-chain/go-block-stm v0.0.0-20241213061541-7afe924fb4a6/go.mod h1:iwQTX9xMX8NV9k3o2BiWXA0SswpsZrDk5q3gA7nWYiE= github.com/crypto-org-chain/go-ethereum v1.10.20-0.20250815065500-a4fbafcae0dd h1:ebSnzvM9yKVGFjvoGly7LFQQCS2HuOWMCvQyByJ52Gs= @@ -1666,8 +1666,8 @@ github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8 github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= -github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= diff --git a/gomod2nix.toml b/gomod2nix.toml index e9727a7e27..b3e5a5bb70 100644 --- a/gomod2nix.toml +++ b/gomod2nix.toml @@ -312,8 +312,8 @@ schema = 3 version = "v0.2.2" hash = "sha256-0MLfSJKdeK3Z7tWAXTdzwB4091dmyxIX38S5SKH5QAw=" [mod."github.com/evmos/ethermint"] - version = "v0.22.1-0.20251204034614-3b1542d755b6" - hash = "sha256-POcEnzlcWj4YdwdbekPlFzC9FehRJ4nYo4kyvqsUJDA=" + version = "v0.22.1-0.20251208051817-9f5d1db3246d" + hash = "sha256-hB9qvklny56AP8MltiAQGhSx79SI68dj3l0ITXulSt0=" replaced = "github.com/crypto-org-chain/ethermint" [mod."github.com/fatih/color"] version = "v1.17.0" @@ -625,8 +625,8 @@ schema = 3 version = "v1.10.0" hash = "sha256-dQ6Qqf26IZsa6XsGKP7GDuCj+WmSsBmkBwGTDfue/rk=" [mod."github.com/spf13/cobra"] - version = "v1.10.1" - hash = "sha256-OP6wdqk4dvBD8U5aicTkySHZ2s0LWnBo2TST2SmgcpM=" + version = "v1.10.2" + hash = "sha256-nbRCTFiDCC2jKK7AHi79n7urYCMP5yDZnWtNVJrDi+k=" [mod."github.com/spf13/pflag"] version = "v1.0.10" hash = "sha256-uDPnWjHpSrzXr17KEYEA1yAbizfcsfo5AyztY2tS6ZU=" diff --git a/integration_tests/test_evm_params_int_64_overflow.py b/integration_tests/test_evm_params_int_64_overflow.py new file mode 100644 index 0000000000..1b285b7b21 --- /dev/null +++ b/integration_tests/test_evm_params_int_64_overflow.py @@ -0,0 +1,262 @@ +""" +Integration tests for EVM module int64 overflow parameter validation. + +This test suite validates that the EVM module properly handles different values +of the header_hash_num and history_serve_window parameters, including edge cases +that could cause int64 overflow. + +Test scenarios (both parameters tested together): +1. value = 0 (valid, should succeed - no minimum check in ValidateInt64Overflow) +2. value = 100 (valid, should succeed) +3. value = MaxInt64 (valid, should succeed) +4. value = MaxInt64 + 1 (overflow case, should be rejected) +5. value = 2^64-1 (UINT64_MAX, overflow case, should be rejected) +""" + +import pytest + +from .cosmoscli import module_address +from .utils import submit_gov_proposal, wait_for_new_blocks + +pytestmark = pytest.mark.evm + +MAX_INT64 = (1 << 63) - 1 # 9223372036854775807 +UINT64_MAX = (1 << 64) - 1 # 18446744073709551615 + + +def get_evm_params(cli): + """Query current EVM module parameters.""" + params = cli.query_params("evm") + return params + + +def submit_evm_param_update(cronos, params): + """ + Submit a governance proposal to update EVM module parameters. + + Args: + cronos: Cronos cluster instance + params: Complete params dict with updated values + + Returns: + True if proposal passes, False if it fails + """ + authority = module_address("gov") + msg = "/ethermint.evm.v1.MsgUpdateParams" + + try: + submit_gov_proposal( + cronos, + msg, + messages=[ + { + "@type": msg, + "authority": authority, + "params": params, + } + ], + ) + return True + except (AssertionError, Exception) as e: + print(f"Proposal failed as expected: {e}") + return False + + +def prepare_evm_params(cli, header_hash_num, history_serve_window): + """ + Prepare EVM params for update by querying current params and applying updates. + + Args: + cli: Cosmos CLI instance + header_hash_num: Value to set for header_hash_num + history_serve_window: Value to set for history_serve_window + + Returns: + Updated params dict + """ + params = get_evm_params(cli) + params["header_hash_num"] = str(header_hash_num) + params["history_serve_window"] = str(history_serve_window) + return params + + +def test_evm_params_zero(cronos): + """ + Test that header_hash_num = 0 and history_serve_window = 0 are valid. + + Zero is a valid value as ValidateInt64Overflow only checks for values + exceeding MaxInt64. + """ + cli = cronos.cosmos_cli() + + # Get initial params + initial_params = get_evm_params(cli) + initial_header_hash_num = initial_params["header_hash_num"] + initial_history_serve_window = initial_params["history_serve_window"] + print(f"Initial header_hash_num: {initial_header_hash_num}") + print(f"Initial history_serve_window: {initial_history_serve_window}") + + test_value = 0 + print(f"Attempting to set both params to {test_value}") + + params = prepare_evm_params(cli, test_value, test_value) + success = submit_evm_param_update(cronos, params) + + # Should succeed (0 is valid) + assert success, f"Proposal should succeed for value = {test_value}" + + wait_for_new_blocks(cli, 2) + updated_params = get_evm_params(cli) + print(f"Updated header_hash_num: {updated_params['header_hash_num']}") + print(f"Updated history_serve_window: {updated_params['history_serve_window']}") + + assert updated_params["header_hash_num"] == str( + test_value + ), f"header_hash_num should be {test_value}" + assert updated_params["history_serve_window"] == str( + test_value + ), f"history_serve_window should be {test_value}" + + # Verify chain continues to produce blocks + wait_for_new_blocks(cli, 3) + print(f"Chain continues to produce blocks with both params = {test_value}") + + +def test_evm_params_valid(cronos): + """ + Test that valid values (100) work correctly for both parameters. + + This verifies that the parameter update mechanism works for valid values. + """ + cli = cronos.cosmos_cli() + + # Get initial params + initial_params = get_evm_params(cli) + print(f"Initial header_hash_num: {initial_params['header_hash_num']}") + print(f"Initial history_serve_window: {initial_params['history_serve_window']}") + + test_value = 100 + print(f"Attempting to set both params to {test_value}") + + params = prepare_evm_params(cli, test_value, test_value) + success = submit_evm_param_update(cronos, params) + + assert success, f"Valid proposal should succeed with value {test_value}" + + wait_for_new_blocks(cli, 2) + updated_params = get_evm_params(cli) + print(f"Updated header_hash_num: {updated_params['header_hash_num']}") + print(f"Updated history_serve_window: {updated_params['history_serve_window']}") + + assert updated_params["header_hash_num"] == str( + test_value + ), f"header_hash_num should be {test_value}" + assert updated_params["history_serve_window"] == str( + test_value + ), f"history_serve_window should be {test_value}" + + # Verify chain continues to produce blocks + wait_for_new_blocks(cli, 3) + print(f"Chain continues to produce blocks with both params = {test_value}") + + +def test_evm_params_max_int64_boundary(cronos): + """ + Test the boundary case: both params = MaxInt64. + + This value should be valid as it's the maximum positive value + that fits in an int64. + """ + cli = cronos.cosmos_cli() + + # Get initial params + initial_params = get_evm_params(cli) + print(f"Initial header_hash_num: {initial_params['header_hash_num']}") + print(f"Initial history_serve_window: {initial_params['history_serve_window']}") + + test_value = MAX_INT64 + print(f"Attempting to set both params to {test_value} (MaxInt64)") + + params = prepare_evm_params(cli, test_value, test_value) + success = submit_evm_param_update(cronos, params) + + assert success, f"Proposal should succeed with value = {test_value}" + + wait_for_new_blocks(cli, 2) + updated_params = get_evm_params(cli) + print(f"Updated header_hash_num: {updated_params['header_hash_num']}") + print(f"Updated history_serve_window: {updated_params['history_serve_window']}") + + assert updated_params["header_hash_num"] == str( + test_value + ), f"header_hash_num should be {test_value}" + assert updated_params["history_serve_window"] == str( + test_value + ), f"history_serve_window should be {test_value}" + + wait_for_new_blocks(cli, 3) + print(f"Chain continues to produce blocks with both params = {test_value}") + + +def test_evm_params_just_over_max_int64(cronos): + """ + Test the edge case: both params = MaxInt64 + 1. + + This is the smallest value that would overflow int64 and should be rejected. + """ + cli = cronos.cosmos_cli() + + # Get initial params + initial_params = get_evm_params(cli) + initial_header_hash_num = initial_params["header_hash_num"] + initial_history_serve_window = initial_params["history_serve_window"] + print(f"Initial header_hash_num: {initial_header_hash_num}") + print(f"Initial history_serve_window: {initial_history_serve_window}") + + overflow_value = MAX_INT64 + 1 + print(f"Attempting to set both params to {overflow_value}") + + params = prepare_evm_params(cli, overflow_value, overflow_value) + success = submit_evm_param_update(cronos, params) + + assert not success, f"Proposal should fail for value = {overflow_value}" + + wait_for_new_blocks(cli, 1) + current_params = get_evm_params(cli) + assert ( + current_params["header_hash_num"] == initial_header_hash_num + ), "header_hash_num should not change" + assert ( + current_params["history_serve_window"] == initial_history_serve_window + ), "history_serve_window should not change" + + +def test_evm_params_uint64_max(cronos): + """ + Test that both params = 2^64-1 (UINT64_MAX) is properly rejected. + """ + cli = cronos.cosmos_cli() + + # Get initial params + initial_params = get_evm_params(cli) + initial_header_hash_num = initial_params["header_hash_num"] + initial_history_serve_window = initial_params["history_serve_window"] + print(f"Initial header_hash_num: {initial_header_hash_num}") + print(f"Initial history_serve_window: {initial_history_serve_window}") + + overflow_value = UINT64_MAX + print(f"Attempting to set both params to {overflow_value} (2^64 - 1)") + + params = prepare_evm_params(cli, overflow_value, overflow_value) + success = submit_evm_param_update(cronos, params) + + assert not success, f"Proposal should fail for value = {overflow_value}" + + wait_for_new_blocks(cli, 1) + current_params = get_evm_params(cli) + assert ( + current_params["header_hash_num"] == initial_header_hash_num + ), "header_hash_num should not change" + assert ( + current_params["history_serve_window"] == initial_history_serve_window + ), "history_serve_window should not change"