Skip to content

Commit 47a2ae0

Browse files
authored
chore: cleanup and improve x/mint params validation and test in cosmos-sdk (backport #25562) (#1919)
* chore: cleanup and improve `x/mint` params validation and test in cosmos-sdk (backport #25562) (#1918) * update cosmos-sdk (backport #26652) - improve mint params validation * add integration tests * fix python lint * remove comment Signed-off-by: randy-cro <randy.ang@crypto.com> --------- Signed-off-by: randy-cro <randy.ang@crypto.com> * fix minor f print --------- Signed-off-by: randy-cro <randy.ang@crypto.com>
1 parent 984097c commit 47a2ae0

File tree

6 files changed

+263
-6
lines changed

6 files changed

+263
-6
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
timeout-minutes: 240
2020
strategy:
2121
matrix:
22-
tests: [unmarked, ibc, ibc_rly_evm, ibc_rly_gas, ibc_timeout, ibc_update_client, ica, gov, upgrade, slow, gas]
22+
tests: [unmarked, ibc, ibc_rly_evm, ibc_rly_gas, ibc_timeout, ibc_update_client, ica, gov, upgrade, slow, gas, mint]
2323
env:
2424
TESTS_TO_RUN: ${{ matrix.tests }}
2525
steps:

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ require (
281281
replace (
282282
cosmossdk.io/store => github.com/crypto-org-chain/cosmos-sdk/store v0.0.0-20241217090828-cfbca9fe8254
283283
cosmossdk.io/x/tx => github.com/crypto-org-chain/cosmos-sdk/x/tx v0.0.0-20241217090828-cfbca9fe8254
284-
github.com/cosmos/cosmos-sdk => github.com/crypto-org-chain/cosmos-sdk v0.50.6-0.20250424063720-28ea58ae20d8
284+
github.com/cosmos/cosmos-sdk => github.com/crypto-org-chain/cosmos-sdk v0.50.6-0.20251119062431-8d0a31ef043d
285285
)
286286

287287
replace (

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -904,8 +904,8 @@ github.com/crypto-org-chain/btree v0.0.0-20240406140148-2687063b042c h1:MOgfS4+F
904904
github.com/crypto-org-chain/btree v0.0.0-20240406140148-2687063b042c/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY=
905905
github.com/crypto-org-chain/cometbft v0.0.0-20251014161156-b0e778b18408 h1:7dfWkDRYCsguKrpd0t14nrZ3Xf/9aVHiQrWx5o0DCdo=
906906
github.com/crypto-org-chain/cometbft v0.0.0-20251014161156-b0e778b18408/go.mod h1:khbgmtxbgwJfMqDmnGY4rl2sQpTdzpPb1f9nqnfpy1o=
907-
github.com/crypto-org-chain/cosmos-sdk v0.50.6-0.20250424063720-28ea58ae20d8 h1:Sif0pGNc4C384OLucyQ7P/+KjYiJ6uDn8Cf8wR7MI+c=
908-
github.com/crypto-org-chain/cosmos-sdk v0.50.6-0.20250424063720-28ea58ae20d8/go.mod h1:JwwsMeZldLN20b72mmbWPY0EV9rs+v/12hRu1JFttvY=
907+
github.com/crypto-org-chain/cosmos-sdk v0.50.6-0.20251119062431-8d0a31ef043d h1:ffzsdKbhbSSBIMBAGJGjezjEr60A/JgpznOJhMUMbfE=
908+
github.com/crypto-org-chain/cosmos-sdk v0.50.6-0.20251119062431-8d0a31ef043d/go.mod h1:8/AdT5lF3ILCCl/sDQXyBgzWGtcmD1tInWyhYeREVPA=
909909
github.com/crypto-org-chain/cosmos-sdk/store v0.0.0-20241217090828-cfbca9fe8254 h1:NEgy0r3otU/O+0OAjMdEhbn4VotQlg+98hHbD7M23wU=
910910
github.com/crypto-org-chain/cosmos-sdk/store v0.0.0-20241217090828-cfbca9fe8254/go.mod h1:8DwVTz83/2PSI366FERGbWSH7hL6sB7HbYp8bqksNwM=
911911
github.com/crypto-org-chain/cosmos-sdk/x/tx v0.0.0-20241217090828-cfbca9fe8254 h1:JzLOFRiKsDtLJt5h0M0jkEIPDKvFFyja7VEp7gG6O9U=

gomod2nix.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,8 @@ schema = 3
213213
version = "v1.0.0-beta.5"
214214
hash = "sha256-Fy/PbsOsd6iq0Njy3DVWK6HqWsogI+MkE8QslHGWyVg="
215215
[mod."github.com/cosmos/cosmos-sdk"]
216-
version = "v0.50.6-0.20250424063720-28ea58ae20d8"
217-
hash = "sha256-UCynFh2IangiNqQsgux4dKCk8wuF1vgoINQGA8N59QY="
216+
version = "v0.50.6-0.20251119062431-8d0a31ef043d"
217+
hash = "sha256-VxQus9ynUK8nAZh3ubNXRcxJsITzgndjd7UYYgMt6C0="
218218
replaced = "github.com/crypto-org-chain/cosmos-sdk"
219219
[mod."github.com/cosmos/go-bip39"]
220220
version = "v1.0.0"

integration_tests/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def pytest_configure(config):
2323
config.addinivalue_line("markers", "ibc_update_client: marks ibc updateclient test")
2424
config.addinivalue_line("markers", "gov: marks gov related tests")
2525
config.addinivalue_line("markers", "gas: marks gas related tests")
26+
config.addinivalue_line("markers", "mint: marks mint module tests")
2627

2728

2829
def pytest_collection_modifyitems(items, config):
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
"""
2+
Integration tests for mint module BlocksPerYear parameter validation.
3+
4+
This test suite validates that the mint module properly handles different values
5+
of the blocks_per_year parameter, including edge cases that could cause overflow
6+
or panic conditions.
7+
8+
Test scenarios:
9+
1. blocks_per_year = 0 (invalid, should be rejected)
10+
2. blocks_per_year = 100 (valid, should succeed)
11+
3. blocks_per_year = MaxInt64 (valid, should succeed)
12+
4. blocks_per_year = MaxInt64 + 1 (overflow case, should be rejected)
13+
5. blocks_per_year = 2^64-1 (UINT64_MAX, overflow case, should be rejected)
14+
"""
15+
16+
import pytest
17+
18+
from .cosmoscli import module_address
19+
from .utils import submit_gov_proposal, wait_for_new_blocks
20+
21+
pytestmark = pytest.mark.mint
22+
23+
MAX_INT64 = (1 << 63) - 1 # 9223372036854775807
24+
UINT64_MAX = (1 << 64) - 1 # 18446744073709551615
25+
26+
27+
def normalize_legacy_dec(value: str) -> str:
28+
"""
29+
Ensure math.LegacyDec strings have an explicit decimal point (scale 18).
30+
This matches the Cosmos SDK's LegacyDec format.
31+
"""
32+
if not value:
33+
return "0.000000000000000000"
34+
if "." in value:
35+
return value
36+
sign = ""
37+
if value[0] == "-":
38+
sign = "-"
39+
value = value[1:]
40+
stripped = value.lstrip("0")
41+
if not stripped:
42+
return "0.000000000000000000"
43+
padded = stripped.rjust(19, "0")
44+
int_part = padded[:-18] or "0"
45+
frac_part = padded[-18:]
46+
return f"{sign}{int_part}.{frac_part}"
47+
48+
49+
def get_mint_params(cli):
50+
"""Query current mint module parameters."""
51+
params = cli.query_params("mint")
52+
return params
53+
54+
55+
def submit_mint_param_update(cronos, blocks_per_year_value):
56+
"""
57+
Submit a governance proposal to update mint module parameters.
58+
59+
Args:
60+
cronos: Cronos cluster instance
61+
blocks_per_year_value: Value to set for blocks_per_year parameter
62+
63+
Returns:
64+
True if proposal passes, False if it fails
65+
"""
66+
cli = cronos.cosmos_cli()
67+
68+
params = get_mint_params(cli)
69+
70+
# Normalize legacy decimal fields
71+
for dec_key in (
72+
"inflation_rate_change",
73+
"inflation_max",
74+
"inflation_min",
75+
"goal_bonded",
76+
):
77+
if dec_key in params and isinstance(params[dec_key], str):
78+
params[dec_key] = normalize_legacy_dec(params[dec_key])
79+
80+
# Update blocks_per_year to the test value
81+
params["blocks_per_year"] = str(blocks_per_year_value)
82+
83+
authority = module_address("gov")
84+
msg = "/cosmos.mint.v1beta1.MsgUpdateParams"
85+
86+
try:
87+
submit_gov_proposal(
88+
cronos,
89+
msg,
90+
messages=[
91+
{
92+
"@type": msg,
93+
"authority": authority,
94+
"params": params,
95+
}
96+
],
97+
)
98+
return True
99+
except (AssertionError, Exception) as e:
100+
print(f"Proposal failed as expected: {e}")
101+
return False
102+
103+
104+
def test_mint_blocks_per_year_zero(cronos):
105+
"""
106+
Test that blocks_per_year = 0 is properly rejected.
107+
108+
Zero is an invalid value because it would cause division by zero
109+
in the minting calculations.
110+
"""
111+
cli = cronos.cosmos_cli()
112+
113+
# Get initial params
114+
initial_params = get_mint_params(cli)
115+
initial_blocks_per_year = initial_params["blocks_per_year"]
116+
print(f"Initial blocks_per_year: {initial_blocks_per_year}")
117+
test_value = 0
118+
119+
# Attempt to update to 0 (should fail)
120+
print(f"Attempting to set blocks_per_year to {test_value}")
121+
success = submit_mint_param_update(cronos, test_value)
122+
123+
assert not success, f"Proposal should fail for blocks_per_year = {test_value}"
124+
wait_for_new_blocks(cli, 1)
125+
current_params = get_mint_params(cli)
126+
assert (
127+
current_params["blocks_per_year"] == initial_blocks_per_year
128+
), "Blocks per year should not change"
129+
130+
131+
def test_mint_blocks_per_year_valid(cronos):
132+
"""
133+
Test that a valid blocks_per_year value (100) works correctly.
134+
135+
This verifies that the parameter update mechanism works for valid values.
136+
"""
137+
cli = cronos.cosmos_cli()
138+
139+
# Get initial params
140+
initial_params = get_mint_params(cli)
141+
initial_blocks_per_year = initial_params["blocks_per_year"]
142+
print(f"Initial blocks_per_year: {initial_blocks_per_year}")
143+
144+
# Update to a valid value (100)
145+
test_value = 100
146+
print(f"Attempting to set blocks_per_year to {test_value}")
147+
success = submit_mint_param_update(cronos, test_value)
148+
149+
# Should succeed
150+
assert (
151+
success
152+
), f"Valid blocks_per_year proposal should succeed with value {test_value}"
153+
154+
# Wait for proposal execution
155+
wait_for_new_blocks(cli, 2)
156+
157+
# Verify the chain is still producing blocks (no panic)
158+
updated_params = get_mint_params(cli)
159+
updated_blocks_per_year = updated_params["blocks_per_year"]
160+
print(f"Updated blocks_per_year: {updated_blocks_per_year}")
161+
162+
# Verify the value was updated correctly
163+
assert updated_blocks_per_year == str(
164+
test_value
165+
), f"blocks_per_year should be {test_value}, got {updated_blocks_per_year}"
166+
167+
# Verify chain continues to produce blocks after the update
168+
# This ensures the BeginBlocker doesn't panic with the new value
169+
wait_for_new_blocks(cli, 3)
170+
print(f"Chain continues to produce blocks with valid blocks_per_year {test_value}")
171+
172+
173+
def test_mint_blocks_per_year_max_int64_boundary(cronos):
174+
"""
175+
Test the boundary case: blocks_per_year = MaxInt64.
176+
177+
This value should be valid as it's the maximum positive value
178+
that fits in an int64.
179+
"""
180+
cli = cronos.cosmos_cli()
181+
182+
# Get initial params
183+
initial_params = get_mint_params(cli)
184+
initial_blocks_per_year = initial_params["blocks_per_year"]
185+
print(f"Initial blocks_per_year: {initial_blocks_per_year}")
186+
187+
# Update to MaxInt64 (should be valid)
188+
test_value = MAX_INT64
189+
print(f"Attempting to set blocks_per_year to {test_value} (MaxInt64)")
190+
191+
success = submit_mint_param_update(cronos, test_value)
192+
assert success, f"Proposal should succeed with blocks_per_year = {test_value}"
193+
194+
wait_for_new_blocks(cli, 2)
195+
updated_params = get_mint_params(cli)
196+
updated_blocks_per_year = updated_params["blocks_per_year"]
197+
print(f"Updated blocks_per_year: {updated_blocks_per_year}")
198+
assert updated_blocks_per_year == str(
199+
test_value
200+
), f"blocks_per_year should be {test_value}, got {updated_blocks_per_year}"
201+
202+
wait_for_new_blocks(cli, 3)
203+
print(f"Chain continues to produce blocks with blocks_per_year = {test_value}")
204+
205+
206+
def test_mint_blocks_per_year_just_over_max_int64(cronos):
207+
"""
208+
Test the edge case: blocks_per_year = MaxInt64 + 1.
209+
210+
This is the smallest value that would overflow int64 and should be rejected.
211+
"""
212+
cli = cronos.cosmos_cli()
213+
214+
# Get initial params
215+
initial_params = get_mint_params(cli)
216+
initial_blocks_per_year = initial_params["blocks_per_year"]
217+
print(f"Initial blocks_per_year: {initial_blocks_per_year}")
218+
219+
# Attempt to update to MaxInt64 + 1 (should fail or be rejected)
220+
overflow_value = MAX_INT64 + 1
221+
print(f"Attempting to set blocks_per_year to {overflow_value}")
222+
223+
success = submit_mint_param_update(cronos, overflow_value)
224+
225+
assert not success, f"Proposal should fail for blocks_per_year = {overflow_value}"
226+
wait_for_new_blocks(cli, 1)
227+
current_params = get_mint_params(cli)
228+
assert (
229+
current_params["blocks_per_year"] == initial_blocks_per_year
230+
), "Blocks per year should not change"
231+
232+
233+
def test_mint_blocks_per_year_uint64_max(cronos):
234+
"""
235+
Test that blocks_per_year = 2^64-1 (UINT64_MAX) is properly rejected.
236+
"""
237+
cli = cronos.cosmos_cli()
238+
239+
# Get initial params
240+
initial_params = get_mint_params(cli)
241+
initial_blocks_per_year = initial_params["blocks_per_year"]
242+
print(f"Initial blocks_per_year: {initial_blocks_per_year}")
243+
244+
# Attempt to update to UINT64_MAX (2^64 - 1)
245+
# This value fits in uint64 but severely overflows int64
246+
overflow_value = UINT64_MAX
247+
print(f"Attempting to set blocks_per_year to {overflow_value} (2^64 - 1)")
248+
249+
success = submit_mint_param_update(cronos, overflow_value)
250+
251+
assert not success, f"Proposal should fail for blocks_per_year = {overflow_value}"
252+
wait_for_new_blocks(cli, 1)
253+
current_params = get_mint_params(cli)
254+
assert (
255+
current_params["blocks_per_year"] == initial_blocks_per_year
256+
), "Blocks per year should not change"

0 commit comments

Comments
 (0)