From 576fcf6d24bf11e82f552cb4a55aa9b8bd81b2d5 Mon Sep 17 00:00:00 2001 From: femi Date: Wed, 8 Jul 2026 11:55:21 +0100 Subject: [PATCH 1/2] feat(tests): derive cost model proposals from live protocol params Governance and upgrade tests hardcoded fixture files with fixed PlutusV2/V3 cost model lengths (185/297, 185). Mainnet governance updates cost models over time, so these fixtures drift out of sync and get rejected by the node. Add cost_model_utils.get_current_cost_models/write_cost_model_proposal to build a governance proposal from the cluster's own live protocol parameters instead. test_pparam_update.py and test_node_upgrade.py now use this for their happy-path proposals; test_incompatible_cost_models keeps its static fixture since it intentionally tests format rejection. Also derive LAST_KNOWN_PROTOCOL_VERSION from Versions.MAP instead of a hardcoded constant, so it no longer needs a manual bump on every new protocol version. --- cardano_node_tests/tests/test_node_upgrade.py | 8 +++-- .../tests/tests_conway/test_pparam_update.py | 12 +++++++- cardano_node_tests/utils/cost_model_utils.py | 29 +++++++++++++++++++ cardano_node_tests/utils/versions.py | 5 +++- 4 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 cardano_node_tests/utils/cost_model_utils.py diff --git a/cardano_node_tests/tests/test_node_upgrade.py b/cardano_node_tests/tests/test_node_upgrade.py index 96c10fc30..b992be942 100644 --- a/cardano_node_tests/tests/test_node_upgrade.py +++ b/cardano_node_tests/tests/test_node_upgrade.py @@ -15,6 +15,7 @@ from cardano_node_tests.tests.tests_conway import conway_common from cardano_node_tests.utils import cluster_nodes from cardano_node_tests.utils import clusterlib_utils +from cardano_node_tests.utils import cost_model_utils from cardano_node_tests.utils import governance_setup from cardano_node_tests.utils import governance_utils from cardano_node_tests.utils import helpers @@ -132,7 +133,7 @@ def test_update_cost_models( Test updating Plutus cost models after node upgrade. Runs only on step 2 of upgrade testing sequence. - * Load cost model proposal from JSON file (PlutusV2 and PlutusV3 models) + * Generate cost model proposal from the running cluster's current protocol parameters * Get default governance data (DReps, committee members, pools) * Submit cost model update governance action * Vote and ratify the cost model update @@ -141,7 +142,10 @@ def test_update_cost_models( """ cluster = cluster_singleton temp_template = common.get_test_id(cluster) - cost_proposal_file = DATA_DIR / "cost_models_list_185_297_v2_v3.json" + cost_proposal_file = cost_model_utils.write_cost_model_proposal( + cost_models=cost_model_utils.get_current_cost_models(cluster), + dest=temptools.get_basetemp() / f"{temp_template}_cost_models.json", + ) governance_data = governance_setup.get_default_governance( cluster_manager=cluster_manager, cluster_obj=cluster diff --git a/cardano_node_tests/tests/tests_conway/test_pparam_update.py b/cardano_node_tests/tests/tests_conway/test_pparam_update.py index 984e249e3..7388d2b82 100644 --- a/cardano_node_tests/tests/tests_conway/test_pparam_update.py +++ b/cardano_node_tests/tests/tests_conway/test_pparam_update.py @@ -17,11 +17,13 @@ from cardano_node_tests.tests.tests_conway import conway_common from cardano_node_tests.utils import clusterlib_utils from cardano_node_tests.utils import configuration +from cardano_node_tests.utils import cost_model_utils from cardano_node_tests.utils import dbsync_utils from cardano_node_tests.utils import governance_utils from cardano_node_tests.utils import helpers from cardano_node_tests.utils import submit_api from cardano_node_tests.utils import submit_utils +from cardano_node_tests.utils import temptools from cardano_node_tests.utils.versions import VERSIONS LOGGER = logging.getLogger(__name__) @@ -280,6 +282,11 @@ def test_pparam_update( # noqa: C901 * Vote to disapprove the actions * Submit a "protocol parameters update" action that will be enacted + + - Cost model proposal is generated dynamically from the running cluster's + current protocol parameters, so no static fixture file needs updating + when mainnet governance changes cost models. + * Check that SPOs cannot vote on a "protocol parameters update" action that doesn't change security parameters * Vote to approve the action @@ -293,7 +300,10 @@ def test_pparam_update( # noqa: C901 """ cluster, governance_data = cluster_lock_governance_plutus temp_template = common.get_test_id(cluster) - cost_proposal_file = DATA_DIR / "cost_models_list_185_v2_v3.json" + cost_proposal_file = cost_model_utils.write_cost_model_proposal( + cost_models=cost_model_utils.get_current_cost_models(cluster), + dest=temptools.get_basetemp() / f"{temp_template}_cost_models.json", + ) db_errors_final = [] is_in_bootstrap = conway_common.is_in_bootstrap(cluster_obj=cluster) diff --git a/cardano_node_tests/utils/cost_model_utils.py b/cardano_node_tests/utils/cost_model_utils.py new file mode 100644 index 000000000..bfc928edd --- /dev/null +++ b/cardano_node_tests/utils/cost_model_utils.py @@ -0,0 +1,29 @@ +"""Utilities for cost model proposal generation.""" + +import json +import pathlib as pl + +from cardano_clusterlib import clusterlib + + +def get_current_cost_models(cluster_obj: clusterlib.ClusterLib) -> dict[str, list[int]]: + """Return the current cost models from live protocol parameters.""" + return cluster_obj.g_query.get_protocol_params()["costModels"] + + +def write_cost_model_proposal( + cost_models: dict[str, list[int]], + dest: pl.Path, +) -> pl.Path: + """Write cost models to a JSON file suitable for --cost-model-file. + + Args: + cost_models: Dict mapping Plutus version names to their cost model arrays. + dest: Destination path for the JSON file. + + Returns: + The destination path. + """ + with open(dest, "w", encoding="utf-8") as fp: + json.dump(cost_models, fp, indent=2) + return dest diff --git a/cardano_node_tests/utils/versions.py b/cardano_node_tests/utils/versions.py index bc22d1a15..1a96027d1 100644 --- a/cardano_node_tests/utils/versions.py +++ b/cardano_node_tests/utils/versions.py @@ -32,7 +32,7 @@ class Versions: DEFAULT_CLUSTER_ERA: tp.Final[int] = CONWAY DEFAULT_TX_ERA: tp.Final[int] = DEFAULT_CLUSTER_ERA - # Map protocol versions to era names + # Map protocol versions to era names — add new entries here when a new era ships MAP: tp.ClassVar[dict[int, str]] = { 0: "byron", 1: "byron", @@ -49,6 +49,9 @@ class Versions: 12: "dijkstra", } + # Derived from MAP so it stays current when new eras are added above + LAST_KNOWN_PROTOCOL_VERSION: tp.Final[int] = max(MAP) + def __init__(self) -> None: protocol_version = helpers.get_env_int("PROTOCOL_VERSION", self.DEFAULT_CLUSTER_ERA) if protocol_version not in self.MAP: From 2610e08c8f33f1d2417a48933b5866cc2f35e79b Mon Sep 17 00:00:00 2001 From: femi Date: Thu, 9 Jul 2026 11:18:25 +0100 Subject: [PATCH 2/2] fix(tests): silence mypy no-any-return in cost_model_utils get_protocol_params() returns dict[str, Any], so subscripting it returned Any despite the function's declared return type. Assign through an explicitly annotated local so mypy narrows it to dict[str, list[int]]. --- cardano_node_tests/utils/cost_model_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cardano_node_tests/utils/cost_model_utils.py b/cardano_node_tests/utils/cost_model_utils.py index bfc928edd..23fa60182 100644 --- a/cardano_node_tests/utils/cost_model_utils.py +++ b/cardano_node_tests/utils/cost_model_utils.py @@ -8,7 +8,8 @@ def get_current_cost_models(cluster_obj: clusterlib.ClusterLib) -> dict[str, list[int]]: """Return the current cost models from live protocol parameters.""" - return cluster_obj.g_query.get_protocol_params()["costModels"] + cost_models: dict[str, list[int]] = cluster_obj.g_query.get_protocol_params()["costModels"] + return cost_models def write_cost_model_proposal(