Add support for Plutus scripts on compatible Tx - #1282
Conversation
75f0875 to
26aaf4a
Compare
There was a problem hiding this comment.
Pull request overview
Extends the Cardano.Api.Compatible.Tx builder to support Plutus spending scripts (and related era features) so callers can build pre-Conway Plutus script transactions without relying on deprecated APIs, while also fixing redeemer-pointer indexing behavior and adding regression/property coverage.
Changes:
- Add Plutus spending-witness support and additional threading (collateral, metadata, validity upper bound, script integrity hash) to
createCompatibleTx, with a newCompatibleTxBodyContentrecord andCompatibleTxError. - Fix witness/indexing behavior for proposals/certificates/votes by preserving insertion order (OSet/OMap) and ensuring unwitnessed items occupy redeemer index slots.
- Add/extend property tests to validate redeemer pointer indexing against ledger resolution and cover new compatible-builder behavior.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| cardano-api/test/cardano-api-test/Test/Cardano/Api/Transaction/Body/Plutus/RedeemerIndex.hs | New property suite asserting API redeemer indices match ledger Indexable/redeemerPointer resolution. |
| cardano-api/test/cardano-api-test/Test/Cardano/Api/Experimental/Fee.hs | Updates compatible-tx tests and adds new properties for spending redeemer order, witness set inclusion, pparams requirement, and extra content threading. |
| cardano-api/test/cardano-api-test/Test/Cardano/Api/Experimental.hs | Adds properties verifying proposal insertion-order indexing and certificate indexing that counts unwitnessed certs. |
| cardano-api/test/cardano-api-test/cardano-api-test.hs | Wires the new redeemer-index test group into the test runner. |
| cardano-api/src/Cardano/Api/Tx/Internal/Body.hs | Keeps unwitnessed certificates in witnessable extraction to preserve redeemer index slots; updates proposal-indexing docs. |
| cardano-api/src/Cardano/Api/Tx.hs | Reorders/clarifies export comment for witnessable extractors. |
| cardano-api/src/Cardano/Api/Internal/Orphans/Serialisation.hs | Adds ToJSON for DijkstraLedgerPredFailure. |
| cardano-api/src/Cardano/Api/Experimental/Tx/Internal/BodyContent/New.hs | Includes unwitnessed cert placeholders; makes vote extraction total to avoid index shifts on misses. |
| cardano-api/src/Cardano/Api/Experimental/Plutus/Internal/ScriptWitness.hs | Exports mkSpendingScriptDatum helper for spending-purpose datum construction. |
| cardano-api/src/Cardano/Api/Experimental/Plutus/Internal/IndexedPlutusScriptWitness.hs | Preserves insertion order for certificates/proposals during stable sort by returning EQ intra-category. |
| cardano-api/src/Cardano/Api/Experimental/Plutus.hs | Re-exports mkSpendingScriptDatum. |
| cardano-api/src/Cardano/Api/Experimental.hs | Re-exports mkSpendingScriptDatum. |
| cardano-api/src/Cardano/Api/Compatible/Tx.hs | Major rewrite: createCompatibleTx now takes CompatibleTxBodyContent, supports Plutus spending witnesses, computes script integrity hash, threads metadata/ttl/collateral, and fixes witness inclusion. |
| cardano-api/cardano-api.cabal | Adds cardano-data to test-suite dependencies and includes the new test module. |
| .changes/20260811_cardano_api_redeemer_pointer_indexing.yml | Changelog fragment for redeemer pointer indexing fixes (includes breaking + tests). |
| .changes/20260807_cardano_api_compatible_plutus.yml | Changelog fragment for compatible builder Plutus support and breaking API changes. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
palas
left a comment
There was a problem hiding this comment.
I think there are a couple of issues that should be resolved before merging, and I also included a couple of potential readability improvement opportunities that I saw.
I still haven't had time to look at the tests yet too.
But it is looking good 👍
| -- Order must stay OMap insertion order; the shared | ||
| -- Witnessable-based indexing (via 'extractWitnessableProposals') preserves it. | ||
| proposalWitnesses | ||
| :: [(Witnessable ProposalItem (ShelleyLedgerEra era), AnyWitness (ShelleyLedgerEra era))] | ||
| proposalWitnesses = | ||
| [ (ix, witness) | ||
| | (_, (ix, witness)) <- | ||
| indexWitnessedTxProposalProcedures conwayOnwards proposalProcedures | ||
| ] | ||
| Exp.obtainCommonConstraints (convert conwayOnwards) $ | ||
| Exp.extractWitnessableProposals $ | ||
| Just proposalProcedures |
There was a problem hiding this comment.
I think the function is getting too large (even ignoring the where bits).
And proposalWitnesses doesn't need to be in the case, it could be moved to the where (having its own small case), and that would allow moving allWitnesses and sData to the where too (txAuxData can moved to the where already), and I think that would make the body of the function much more manageable.
Actually, updateTxBody wouldn't need to be here either, because I think it doesn't make use of the Either monad, so it wouldn't need to be monadic, it could be moved to the where too, but that is out of scope of the PR
There was a problem hiding this comment.
Done in d2c152c. proposalWitnesses, updateTxBody, allWitnesses, sData and txAuxData moved to the where clause, so the Either block is now just the integrity hash step. One subtlety: the ProposalProcedures arm needed its own shelleyBasedEraConstraints wrap once hoisted, since it had been borrowing the constraint from the do block's wrapper.
| :: L.AlonzoEraScript ledgerera | ||
| => [(TxIn, AnyWitness ledgerera)] | ||
| -> [(Witnessable TxInItem ledgerera, AnyWitness ledgerera)] | ||
| witnessableTxIns txIns' = L.nub [(WitTxIn txIn, wit) | (txIn, wit) <- txIns'] |
There was a problem hiding this comment.
We should validate there are no repeated TxIns in the list. Or at least that there are no two TxIns with different witnesses, because that would mess up the redeemer pointers
There was a problem hiding this comment.
Good catch. I checked how this behaves across the builders: witnessableTxIns is a faithful copy of the experimental extractWitnessableTxIns, which is itself identical to the legacy one, so all three share this gap. Two entries with the same TxIn but different witnesses both survive the nub, and the pointer indices shift against the body's deduplicated input set. The result is rejected client-side by transaction build during execution-unit evaluation, or at submission with ExtraRedeemers for build-raw-style paths; in one narrow case it validates with the extra witness silently ignored, provably never misassigned. A proper fix touches the compatible, experimental and legacy APIs, plus arguably cardano-cli, which already dedupes collateral but not spending inputs, so I split it out into #1324. I also fixed the stale dedupInputsByTxIn reference in the comment here.
26aaf4a to
70d5345
Compare
createCompatibleTx now takes a CompatibleTxBodyContent record with per-input AnyWitness values, and computes spending redeemer pointers, witness datums, the script integrity hash, collateral inputs, transaction metadata and the validity upper bound. Witness indexing is shared with the experimental transaction builder. Also export the mkSpendingScriptDatum smart constructor from Cardano.Api.Experimental.
70d5345 to
4230761
Compare
- Include vote script witnesses in createCompatibleTx: voting redeemers, scripts, script integrity hash contribution and reference inputs, with a regression test - Remove the unreachable CompatibleTxProtocolParametersConversionError constructor - Simplify createCompatibleTx: hoist the pure bindings out of the Either block, collapse setRefInputs into a single comprehension, rewrite setScriptWitnesses without Endo - Correct the stale dedupInputsByTxIn reference in the redeemer pointer ordering comment
Description
Adds plutus spending script support to the compatible transaction API, so pre-Conway script transactions (PlutusV1/V2, Alonzo/Babbage) can be built without the deprecated API.
Motivated by the tx-generator migration in IntersectMBO/cardano-node#6602.
Changes:
createCompatibleTxsupports script-witnessed inputs: spending redeemer pointers, witness datums, script integrity hash, collateral inputs, transaction metadata and validity upper bound.CompatibleTxBodyContentrecord, and failures are reported via the newCompatibleTxError.mkSpendingScriptDatumis exported for building spending-purpose datums.Review follow-ups included:
CompatibleTxProtocolParametersConversionErrorconstructor.createCompatibleTx'sEitherblock,setRefInputscollapsed to a single comprehension, andsetScriptWitnessesrewritten withoutEndo.TxInvalidation is split out into Transaction builders accept duplicate TxIns with differing witnesses, producing transactions rejected with ExtraRedeemers #1324, since it equally affects the legacy and experimental builders.Checklist
Test.Cardano.Api.Experimental.Fee.changes/20260807_cardano_api_compatible_plutus.yml)