Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion agent_docs/fixtures_caching.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Use `cluster_manager.cache_fixture()` to cache fixture values:
Without a `key`, `cache_fixture()` uses the file name and line number of the fixture that calls it, so every fixture gets its own cache entry. Pass an explicit `key` only when the default doesn't fit:

- Inside a helper called from several fixtures, the line number of the helper would be shared by all callers. The helper must take the key from the caller - this is why the address helpers in `cardano_node_tests/tests/addrs_common.py` take `caching_key`, and fixtures pass `caching_key=helpers.get_current_line_str()`.
- When one fixture caches different values for different parameters, include the parameter in the key (e.g. `caching_key=f"plutusv3_builtins_batch_testing_{variant}"`), otherwise all variants get the value cached for the first one.
- When one fixture caches different values for different parameters, include the parameter in the key (e.g. `caching_key=f"plutus_builtins_batch_testing_{variant}"`), otherwise all variants get the value cached for the first one.

Prefer the `addrs_common` helpers (`get_payment_addrs`, `get_payment_addr`, etc.) over calling `cache_fixture` directly for funded addresses. With `caching_key`, they create the addresses once and re-fund them on later calls when the balance drops below `min_amount`. When only `amount` is passed, the addresses are funded once and never re-funded, so a cached address can run out of funds over a long run.

Expand Down
54 changes: 44 additions & 10 deletions agent_docs/multi_index_array.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,22 +85,56 @@ the same names and order as the PlutusV3 ones, and both languages use the same
builtin semantics variant. The genesis from cardonnay gives PlutusV4 only 251
parameters. Plutus sets the missing parameters to `maxBound`, so a PlutusV4
policy that uses `multiIndexArray` exceeds any budget without this update. The
fixture proposes both languages even when the PlutusV4 scenarios are skipped,
and then checks only the cost models of the tested languages.
shared setup now uses `cost_models_list_369_v3_v4.json`, which preserves those
355 parameters and adds Value builtin costing. It proposes and checks both languages
at PV12+, or V3 alone at PV10/PV11, once for all builtin groups in the same test.
At PV10/PV11 the test installs all 369 V3 parameters, then requires batch7
protocol rejection: updated costing must not activate the builtins. PV9 keeps
its existing costing and still runs pre-activation rejection checks; the shared
update helper's DRep votes are forbidden during Conway bootstrap.

The CLI must also decode PV12 scripts in `transaction policyid` and transaction
witnesses. Some development builds accept `dijkstra` commands but still decode
policies as Conway/PV11. The fixture checks policy decoding before governance setup
so this incompatibility fails immediately rather than after several epochs.
policies as Conway/PV11. For activated builtin groups, the shared test checks each
ledger/Core group's policy decoding independently before running its scenarios.
A known incompatibility skips only that variant; a V3 decoding failure cannot
suppress V4 coverage (or vice versa). Other groups still use the shared governance
setup.

With a running PV12 cluster whose node and CLI support batch7:
Before activation, an availability error is an expected rejection. An unknown
builtin tag instead skips the affected subtest: that older CLI decoder cannot
establish protocol rejection, so its incompatibility is not counted as a pass.

Use a `local_fast` PV12 cluster whose node and CLI support batch7. These builtin
tests do not depend on Leios features such as endorsement blocks.

```sh
PROTOCOL_VERSION=12 COMMAND_ERA=dijkstra pytest -v \
cardano_node_tests/tests/tests_plutus_v3/test_multi_index_array.py
TESTNET_VARIANT=local_fast PROTOCOL_VERSION=12 COMMAND_ERA=dijkstra pytest -v \
cardano_node_tests/tests/tests_plutus/test_builtins.py \
-k test_builtins_new_cost
```

Add `TESTNET_VARIANT=leios_fast` if that is the variant of the running cluster.
Pytest discovers this module automatically; the existing `plutus` and `team_plutus`
CI selections include it. It skips before PV12. Collection or local UPLC evaluation
Run the same updated-cost selection with `PROTOCOL_VERSION=11 COMMAND_ERA=conway`
on a `local_fast` PV11 cluster for batch7 V3 pre-activation coverage. V4 groups
skip because the ledger language itself is unavailable before PV12. If an
upgrade step prevents the cost-model update, pre-activation groups still run
with the existing model and must reject the scripts.

For negative coverage without updating cost models, select
`-k 'test_builtins_old and batch7'` instead. This covers multiIndexArray and Value
policies for each applicable ledger language and both PLC versions, using the
actual protocol version and each language's cost model (355 and 369 parameters
respectively). Fully enabled groups skip; pre-activation rejection and missing
costing remain separate cases. V4 groups require the Dijkstra transaction era
and PV12; V3 groups can also exercise builtin rejection before PV12. Use `-k v3`
or `-k v4` with the old-state test selection to select a particular language.

When a cost-model update is needed, the shared setup checks a worst-case wait of
five epochs against the local 90-minute limit. Long-epoch variants such as
`leios_fast` can therefore skip the updated-cost test. This guard does not apply
when the cost models are already current or to the old-state negative tests.

Pytest discovers the shared test automatically; the existing `plutus` and `team_plutus`
CI selections include it. Batch7 V3 groups also run before PV12 to check rejection.
Collection or local UPLC evaluation
does not establish that the E2E tests pass or that budgets were verified on a node.
2 changes: 2 additions & 0 deletions cardano_node_tests/tests/plutus_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,7 @@ def _get_multi_index_array_script(name: str, ledger: str, core_version: str) ->
class ValueBuiltinScripts(tp.NamedTuple):
"""CIP-0168 minting policies for one ledger/Core combination."""

cost_model: str
succeeding: tuple[PlutusScriptData, ...]
failing: tuple[PlutusScriptData, ...]

Expand Down Expand Up @@ -873,6 +874,7 @@ def _get_value_builtin_script(name: str, ledger: str, core_version: str) -> Plut

VALUE_BUILTIN_SCRIPTS = {
f"{ledger}_{core_version.replace('.', '')}": ValueBuiltinScripts(
cost_model=f"Plutus{ledger.upper()}",
succeeding=tuple(
_get_value_builtin_script(name=n, ledger=ledger, core_version=core_version)
for n in _SUCCEEDING_VALUE_BUILTIN_NAMES
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
"""Shared minting scenarios for PlutusV3+ builtin E2E tests."""
"""Shared minting scenarios for Plutus builtin E2E tests across ledger languages."""

import enum
import logging
import math
import pathlib as pl
import typing as tp

import pytest
import pytest_subtests
from cardano_clusterlib import clusterlib

Expand Down Expand Up @@ -102,6 +103,27 @@ class PendingMint(tp.NamedTuple):
plutus_costs: list[dict]


def _get_policyid(
cluster_obj: clusterlib.ClusterLib, script_file: pl.Path, is_prot_version_ok: bool
) -> str:
"""Return a policy ID, or an empty string for expected pre-activation rejection.

Skip unknown builtin tags separately: an older CLI decoder cannot establish
protocol rejection. Capable decoders must still report the availability error.
"""
try:
return cluster_obj.g_transaction.get_policyid(script_file)
except clusterlib.CLIError as exc:
if is_prot_version_ok:
raise
exc_value = str(exc)
if "Failed to decode builtin tag" in exc_value:
pytest.skip(f"CLI cannot decode builtin tag in {script_file.name}: {exc_value}")
with common.allow_unstable_error_messages():
assert "not available" in exc_value and "protocol version" in exc_value, exc_value
return ""


def run_scenario(
cluster_obj: clusterlib.ClusterLib,
temp_template: str,
Expand All @@ -126,10 +148,12 @@ def run_scenario(
A script expected to fail, or a script submitted with a protocol version that doesn't
support it, must not be accepted. A script expected to succeed can be accepted with
an old cost model, e.g. casing on constants doesn't use any new cost model parameters.
Before protocol activation, rejection can happen already during policy decoding.
Unknown builtin tags skip the scenario as a CLI incompatibility, not a pass.

Returns:
PendingMint | None: The submitted minting transaction, or None when the minting
failed as expected.
failed as expected.
"""
lovelace_amount = 2_000_000
token_amount = 5
Expand All @@ -138,7 +162,16 @@ def run_scenario(

# Mint the "qacoin"

policyid = cluster_obj.g_transaction.get_policyid(plutus_v_record.script_file)
# New builtins can be rejected during policy decoding, before transaction
# construction. This is expected only before their protocol activation.
policyid = _get_policyid(
cluster_obj=cluster_obj,
script_file=plutus_v_record.script_file,
is_prot_version_ok=is_prot_version_ok,
)
if not policyid:
return None

asset_name = f"qacoin{clusterlib.get_rand_str(4)}".encode().hex()
token = f"{policyid}.{asset_name}"
mint_txouts = [clusterlib.TxOut(address=issuer_addr.address, amount=token_amount, coin=token)]
Expand Down Expand Up @@ -198,7 +231,7 @@ def _dump_cost() -> list[dict]:
except clusterlib.CLIError as excp:
str_excp = str(excp)
if not is_prot_version_ok and (
"not available in language PlutusV3 at and protocol version" in str_excp
("not available" in str_excp and "protocol version" in str_excp)
or "Script evaluation error" in str_excp
):
return None
Expand Down Expand Up @@ -290,7 +323,7 @@ def confirm_mints(
return unconfirmed


def run_plutusv3_builtins_test(
def run_builtins_test(
cluster_manager: cluster_management.ClusterManager,
cluster_obj: clusterlib.ClusterLib,
temp_template: str,
Expand Down Expand Up @@ -343,7 +376,7 @@ def run_plutusv3_builtins_test(
cluster_obj=cluster_obj,
num=2,
fund_idx=[0],
caching_key=f"plutusv3_builtins_batch_testing_{variant}",
caching_key=f"plutus_builtins_batch_testing_{variant}",
amount=max(1_000_000_000, total_amount),
min_amount=total_amount,
)
Expand Down
15 changes: 15 additions & 0 deletions cardano_node_tests/tests/tests_plutus/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,18 @@ def cluster(
) -> clusterlib.ClusterLib:
"""Return instance of `clusterlib.ClusterLib`."""
return cluster_manager.get(use_resources=[cluster_management.Resources.PLUTUS])


@pytest.fixture
def cluster_plutus(
cluster_manager: cluster_management.ClusterManager,
) -> clusterlib.ClusterLib:
"""Lock governance and Plutus while keeping cluster pools available for voting."""
return cluster_manager.get(
use_resources=cluster_management.Resources.ALL_POOLS,
lock_resources=[
cluster_management.Resources.COMMITTEE,
cluster_management.Resources.DREPS,
cluster_management.Resources.PLUTUS,
],
)
Loading
Loading