Skip to content

Fix OgmiosV6 backend truncating/reordering Plutus cost models (wrong script integrity hash)#497

Open
theeldermillenial wants to merge 1 commit into
Python-Cardano:mainfrom
theeldermillenial:fix/ogmios-v6-cost-model-order
Open

Fix OgmiosV6 backend truncating/reordering Plutus cost models (wrong script integrity hash)#497
theeldermillenial wants to merge 1 commit into
Python-Cardano:mainfrom
theeldermillenial:fix/ogmios-v6-cost-model-order

Conversation

@theeldermillenial

Copy link
Copy Markdown
Contributor

Problem

OgmiosV6ChainContext._parse_cost_models builds the PlutusV1/V2 cost models by zipping the operation-cost array Ogmios returns against sorted(PLUTUS_V{1,2}_COST_MODEL.keys()):

cost_models["PlutusV2"] = dict(zip(
    sorted(PLUTUS_V2_COST_MODEL.keys()),
    ogmios_cost_models["plutus:v2"].copy(),
))

This corrupts the cost model two ways:

  1. Truncation. The hardcoded PLUTUS_V*_COST_MODEL name lists are a fixed length (PLUTUS_V2_COST_MODEL = 175), but the ledger's cost models grow across protocol eras — mainnet PlutusV2 now has 332 operation costs. zip stops at the shorter iterable, so every parameter past the hardcoded count is silently dropped.
  2. Ordering. sorted(...keys()) is alphabetical, not the ledger's canonical parameter order — which Ogmios already returns in the array.

The cost model is the language view that goes into the script integrity hash (script_data_hash). A truncated/reordered cost model produces a script integrity hash the node rejects on submit, even though evaluateTransaction — which executes the scripts but does not verify the integrity hash — passes. This makes any Plutus transaction built through the OgmiosV6 backend fail submission with a script-integrity-hash mismatch.

The PlutusV3 branch a few lines below is already correct — it keys each cost by its position over the full array.

Fix

Key every language's cost model by its zero-padded position over the full Ogmios array, matching the existing PlutusV3 handling. This preserves both the canonical order and the full length for all three languages, and removes the now-unused PLUTUS_V1/V2_COST_MODEL imports.

The zero-padded keys also keep the V1 language-view path correct: CostModels.to_shallow_primitive does sorted(cost_model.keys()) for language 0, and zero-padded numeric strings sort in canonical (numeric) order, whereas parameter names sort alphabetically.

Verification

  • Added test/pycardano/backend/test_ogmios_v6.py covering order preservation, no truncation when the model is longer than any historical hardcoded list, zero-padded canonical-sorting keys, and empty input. The new test fails on the old code (truncation + reorder) and passes on the fix.
  • Confirmed against a live mainnet node: with the corrected cost model, the recomputed script_data_hash for a real PlutusV2 transaction exactly matches the computedScriptIntegrity the node reports — where the old code produced the rejected hash.

…kend

`OgmiosV6ChainContext._parse_cost_models` keyed the PlutusV1/V2 cost models by
zipping the operation-cost array against `sorted(PLUTUS_V{1,2}_COST_MODEL.keys())`,
which corrupts the cost model two ways:

- Truncation: the hardcoded `PLUTUS_V*_COST_MODEL` name lists are a fixed length
  (V2 = 175), but the ledger's cost models grow across protocol eras — mainnet
  PlutusV2 is now 332 operation costs. `zip` silently truncates to the shorter
  name list, dropping every parameter past the hardcoded count.
- Ordering: `sorted(...keys())` is alphabetical, not the ledger's canonical
  parameter order, which Ogmios already returns.

The cost model feeds the language view in the script integrity (script-data)
hash, so either defect yields a hash the node rejects on submit — even though
`evaluateTransaction`, which does not verify that hash, still passes.

Key each cost model by its zero-padded position over the full Ogmios array — the
same approach the PlutusV3 branch already used — so all three languages preserve
the canonical order and full length. Drops the now-unused PLUTUS_V1/V2_COST_MODEL
imports. Adds a regression test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Jun 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.94%. Comparing base (3bc5677) to head (64f928f).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #497      +/-   ##
==========================================
- Coverage   90.73%   87.94%   -2.80%     
==========================================
  Files          37       36       -1     
  Lines        5356     5274      -82     
  Branches      808      790      -18     
==========================================
- Hits         4860     4638     -222     
- Misses        308      463     +155     
+ Partials      188      173      -15     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant