diff --git a/cardano_node_tests/tests/plutus_common.py b/cardano_node_tests/tests/plutus_common.py index b7280b6a4..6f628d71e 100644 --- a/cardano_node_tests/tests/plutus_common.py +++ b/cardano_node_tests/tests/plutus_common.py @@ -304,6 +304,33 @@ class PlutusScriptData: ) +# Slots between the tip and the end of the validity interval of a transaction that runs +# a "time range" script. Long enough for the transaction to get submitted and land in a +# block, and capped by `get_timerange_slots_offset`. +TIMERANGE_SLOTS_OFFSET = 300 + + +def get_timerange_slots_offset(*, cluster_obj: clusterlib.ClusterLib) -> int: + """Return the slot offset to use for the validity interval of a "time range" script. + + A script that looks at the transaction validity interval makes the ledger translate + the end of the interval to a time, which it can do only up to its forecast horizon: + the end of the first epoch that starts at or after the stability window from the tip. + An interval reaching past that fails the script with `TimeTranslationPastHorizon`, so + `TIMERANGE_SLOTS_OFFSET` is capped with the stability window, which is by definition + always inside the horizon. + + Args: + cluster_obj: An instance of `clusterlib.ClusterLib`. + + Returns: + int: The number of slots to set the validity interval to, counted from the tip. + """ + return min( + TIMERANGE_SLOTS_OFFSET, clusterlib_utils.get_stability_window(cluster_obj=cluster_obj) + ) + + def rotate_shift_bitwise_fails(protocol_version: int) -> bool: """Check if the rotate/shift bitwise builtin scripts fail on the given protocol version. diff --git a/cardano_node_tests/tests/tests_plutus/test_mint_build.py b/cardano_node_tests/tests/tests_plutus/test_mint_build.py index 61249be88..2735407ab 100644 --- a/cardano_node_tests/tests/tests_plutus/test_mint_build.py +++ b/cardano_node_tests/tests/tests_plutus/test_mint_build.py @@ -405,7 +405,7 @@ def test_time_range_minting( # Step 2: mint the "qacoin" slot_step2 = cluster.g_query.get_slot_no() - slots_offset = 300 + slots_offset = plutus_common.get_timerange_slots_offset(cluster_obj=cluster) timestamp_offset_ms = int(slots_offset * cluster.slot_length + 5) * 1_000 # POSIX timestamp + offset @@ -653,7 +653,7 @@ def test_two_scripts_minting( ] # "time range" qacoin - slots_offset = 300 + slots_offset = plutus_common.get_timerange_slots_offset(cluster_obj=cluster) timestamp_offset_ms = int(slots_offset * cluster.slot_length + 5) * 1_000 # POSIX timestamp + offset @@ -997,10 +997,7 @@ def test_ttl_horizon( *mint_txouts, ] - # Calculate 3k/f - offset_3kf = round( - 3 * cluster.genesis["securityParam"] / cluster.genesis["activeSlotsCoeff"] - ) + offset_3kf = clusterlib_utils.get_stability_window(cluster_obj=cluster) # Use 3k/f + `epoch_length` slots for ttl - this will not meet the `expect_pass` condition if ttl_offset == -1: diff --git a/cardano_node_tests/tests/tests_plutus/test_mint_raw.py b/cardano_node_tests/tests/tests_plutus/test_mint_raw.py index f31475b38..f13a39871 100644 --- a/cardano_node_tests/tests/tests_plutus/test_mint_raw.py +++ b/cardano_node_tests/tests/tests_plutus/test_mint_raw.py @@ -14,6 +14,7 @@ from cardano_node_tests.tests import common from cardano_node_tests.tests import plutus_common from cardano_node_tests.tests.tests_plutus import mint_raw +from cardano_node_tests.utils import clusterlib_utils from cardano_node_tests.utils import dbsync_utils from cardano_node_tests.utils import helpers from cardano_node_tests.utils import tx_view @@ -422,7 +423,7 @@ def test_time_range_minting( # Step 2: mint the "qacoin" slot_step2 = cluster.g_query.get_slot_no() - slots_offset = 300 + slots_offset = plutus_common.get_timerange_slots_offset(cluster_obj=cluster) timestamp_offset_ms = int(slots_offset * cluster.slot_length + 5) * 1_000 # POSIX timestamp + offset @@ -625,7 +626,7 @@ def test_two_scripts_minting( ] # "timerange" qacoin - slots_offset = 300 + slots_offset = plutus_common.get_timerange_slots_offset(cluster_obj=cluster) timestamp_offset_ms = int(slots_offset * cluster.slot_length + 5) * 1_000 # POSIX timestamp + offset @@ -1117,10 +1118,7 @@ def test_ttl_horizon( *mint_txouts, ] - # Calculate 3k/f - offset_3kf = round( - 3 * cluster.genesis["securityParam"] / cluster.genesis["activeSlotsCoeff"] - ) + offset_3kf = clusterlib_utils.get_stability_window(cluster_obj=cluster) # Use 3k/f + `epoch_length` slots for ttl - this will not meet the `expect_pass` condition if ttl_offset == -1: diff --git a/cardano_node_tests/utils/clusterlib_utils.py b/cardano_node_tests/utils/clusterlib_utils.py index 5f5c40e5d..4a7a60795 100644 --- a/cardano_node_tests/utils/clusterlib_utils.py +++ b/cardano_node_tests/utils/clusterlib_utils.py @@ -1159,6 +1159,25 @@ def save_ledger_state( return json_file +def get_stability_window(*, cluster_obj: clusterlib.ClusterLib) -> int: + """Return the stability window (`3k/f`) in slots. + + Rounded down, so that the result is never above the window the ledger itself uses. A + caller that uses it as a limit therefore stays inside the window. The division is + exact on every testnet variant, so the rounding only guards against a variant that + picks a `securityParam` and `activeSlotsCoeff` that don't divide. + + Args: + cluster_obj: An instance of `clusterlib.ClusterLib`. + + Returns: + int: The number of slots in the stability window. + """ + security_param = int(cluster_obj.genesis["securityParam"]) + active_slots_coeff = float(cluster_obj.genesis["activeSlotsCoeff"]) + return math.floor(3 * security_param / active_slots_coeff) + + def wait_for_epoch_interval( *, cluster_obj: clusterlib.ClusterLib,