Skip to content

fix: Scalus 0.17 upgrade + correct chain cost-model application in JulcTransactionEvaluator#39

Merged
satran004 merged 2 commits into
mainfrom
fix/scalus_vm_0.17
May 30, 2026
Merged

fix: Scalus 0.17 upgrade + correct chain cost-model application in JulcTransactionEvaluator#39
satran004 merged 2 commits into
mainfrom
fix/scalus_vm_0.17

Conversation

@satran004

Copy link
Copy Markdown
Member

Summary

Two related fixes that make JuLC apply the chain's Plutus V3 cost model correctly when evaluating transactions, rather than silently falling back to built-in default. Together they correct script-budget (ExUnits) over-estimation on Plutus V11 (vanRossem) and any chain with custom cost-model params.

  • Upgrade Scalus to 0.17.0 and adapt the Scalus VM integration to its API.
  • Fix JulcTransactionEvaluator so cost-model setup and evaluation use the same VM provider instance.

Why

On a PV11 devnet, JulcTransactionEvaluator reported 65,563,707 CPU for a script, while the node's evaluateTx and JuLC's own VMs (invoked correctly) reported 65,559,542 CPU — a constant +4,165 gap (memory identical). Since QuickTx.buildAndSign(...) bakes the evaluator's ExUnits into the transaction, every transaction built through this evaluator carried a wrong-for-the-protocol budget. It was masked on PV10 because the built-in default happened to match. Two independent root causes had to be fixed for the chain cost model to reach the evaluating VM:

1. Scalus 0.17 integration

ScalusVmProvider mishandled custom V3 cost models and protocol versions:

  • CostModels map key: keyed by the Language.PlutusV3 enum object, but MachineParams.fromCostModels() looks the model up by integer language id (languageId() == ordinal() == 2) → runtime key not found: 2. Now keyed by Language.PlutusV3.languageId().
  • Protocol version mapping: protocolMajorVersion >= 10 ? plominPV : changPV collapsed PV11 → PV10. PlutusVM gates PV11 semantics (e.g. case-on-builtins) on protocolVersion >= vanRossemPV, so this silently disabled them. Now maps directly: new MajorProtocolVersion(protocolMajorVersion).
  • Scalus 0.17 API: evaluateDeBruijnedTerm gained a 4th tracing boolean; pass false to preserve prior non-tracing behaviour.
  • Bump scalus_3 and scalus-bloxbean-cardano-client-lib_3 to 0.17.0, and cardano-client-lib to 0.7.2.

2. Single VM provider instance

JulcTransactionEvaluator configured the cost model on one provider instance but evaluated on another:

  • Setup: getOrCreateVm()JulcVm.create() did its own ServiceLoader lookup → instance A; setCostModelParams(...) ran on A.
  • Eval: JulcVm.withProvider(getProvider(), lang) — a separate ServiceLoader lookup → instance B; evaluation ran on B.

Cost-model params are stored per provider instance, so B never received the chain cost model and fell back to its built-in (PV10-shaped) default. Affected all backends (Java, Truffle, Scalus).

Fix: resolve the provider once via getProvider() and use that same instance for both setCostModelParams and evaluateWithArgs, passing the resolved script's language explicitly. Remove the now-unused vm field, getOrCreateVm(), and the JulcVm import. Document the evaluator's (unchanged) non-thread-safe contract.

Behavioral change

Transactions built via this evaluator now use the chain's cost model for ExUnits on non-PV10 protocol versions (and any custom cost models), instead of the built-in default. Budgets/fees will change accordingly on such chains
(correctly).

Tests

  • New regression test evaluateTx_usesSuppliedCostModel_notBuiltInDefault: supplies two differing PlutusV3 cost models (one with every cost doubled) and asserts the evaluated CPU budget tracks the supplied model — pre-fix both runs produced the same built-in-default budget and the test fails.
  • JulcTransactionEvaluatorTest: 19/19 pass.
  • Integration (EscrowBudgetComparisonTest, julc-examples, Yaci DevKit PV11): JuLC Java VM, JuLC Scalus VM, and the DevKit evaluateTx endpoint now all report 65,559,542 / 208,684; strict-equality and 3-way comparison pass.

Out of scope (tracked separately)

  • JuLC's Java/Truffle VMs don't yet gate PV11-introduced features (case-on-builtins, new builtins) by protocol version.
  • JulcTransactionEvaluator thread-safety is documented, not enforced; making per-call cost-model config atomic (or per-call rather than instance state) is a follow-up.

satran004 and others added 2 commits May 29, 2026 18:23
Upgrade Scalus to 0.17.0 and adapt the Scalus VM integration to its updated API.

Key PlutusV3 custom cost models by the integer language id expected by
 MachineParams.fromCostModels, fixing the runtime "key not found: 2" failure when using chain-supplied V3 cost model params.

  Pass the new evaluateDeBruijnedTerm tracing argument explicitly as false to
  preserve the previous non-tracing evaluation behavior
…t-model setup and evaluation

JulcTransactionEvaluator configured the chain cost model on one provider
instance but evaluated on another. setCostModelParams ran on the instance
created by getOrCreateVm()/JulcVm.create() (its own ServiceLoader lookup),
while evaluation ran on JulcVm.withProvider(getProvider(), ...) — a separate
ServiceLoader lookup yielding a different instance. Cost-model params are
stored per provider instance, so the evaluating VM never received the chain
cost model and silently fell back to its built-in default.

This over-estimated the script budget on any protocol version whose cost
model differs from the built-in default (e.g. PV11: 65,563,707 vs the correct
65,559,542 CPU). It was masked on PV10 because the default happened to match.
Since QuickTx bakes the evaluator's ExUnits into the transaction, every tx
built through this evaluator carried the wrong budget. Affected all backends
(Java, Truffle, Scalus).

Resolve the VM provider once via getProvider() and use that same instance for
both setCostModelParams and evaluateWithArgs, passing the resolved script's
language explicitly. Remove the now-unused vm field, getOrCreateVm(), and the
JulcVm import. Document the evaluator's (unchanged) non-thread-safe contract.

Add a regression test that supplies two differing PlutusV3 cost models and
asserts the evaluated CPU budget tracks the supplied model rather than the
built-in default.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@satran004 satran004 merged commit b226b02 into main May 30, 2026
1 check passed
@satran004 satran004 deleted the fix/scalus_vm_0.17 branch May 30, 2026 11:56
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