Add text envelope serialisation for AnyPlutusScript#1156
Conversation
There was a problem hiding this comment.
Pull request overview
Adds text envelope serialisation/deserialisation helpers for AnyPlutusScript, enabling round-tripping via TextEnvelope despite the existential Plutus language parameter.
Changes:
- Introduce
serialiseAnyPlutusScriptToTextEnvelopedelegating to existingHasTextEnvelope (PlutusScriptInEra lang era)instances. - Introduce
deserialiseAnyPlutusScriptFromTextEnvelopeusingdeserialiseFromTextEnvelopeAnyOfwithFromSomeTypeover supported Plutus versions. - Re-export the new helpers from
Cardano.Api.Experimental.PlutusandCardano.Api.Experimental.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| cardano-api/src/Cardano/Api/Experimental/Plutus/Internal/Script.hs | Implements text envelope serialise/deserialise helpers for AnyPlutusScript. |
| cardano-api/src/Cardano/Api/Experimental/Plutus.hs | Re-exports the new API from the experimental Plutus module. |
| cardano-api/src/Cardano/Api/Experimental.hs | Re-exports the new API from the top-level experimental module. |
| serialiseAnyPlutusScriptToTextEnvelope | ||
| :: Maybe TextEnvelopeDescr -> AnyPlutusScript era -> TextEnvelope | ||
| serialiseAnyPlutusScriptToTextEnvelope mbDescr (AnyPlutusScript script) = | ||
| obtainLangConstraints (plutusScriptInEraSLanguage script) $ | ||
| serialiseToTextEnvelope mbDescr script | ||
|
|
||
| deserialiseAnyPlutusScriptFromTextEnvelope | ||
| :: forall era | ||
| . L.Era era | ||
| => TextEnvelope | ||
| -> Either TextEnvelopeError (AnyPlutusScript era) | ||
| deserialiseAnyPlutusScriptFromTextEnvelope = |
There was a problem hiding this comment.
These functions are part of the exported API (re-exported from Cardano.Api.Experimental.*) but currently have no Haddock explaining expected envelope types, version handling, or common failure modes (e.g., wrong TextEnvelopeType). Adding concise Haddock (including that deserialisation supports the enumerated Plutus versions and that the envelope type is determined by the underlying PlutusScriptInEra instance) will make the new API safer and easier to use.
| deserialiseAnyPlutusScriptFromTextEnvelope | ||
| :: forall era | ||
| . L.Era era | ||
| => TextEnvelope | ||
| -> Either TextEnvelopeError (AnyPlutusScript era) | ||
| deserialiseAnyPlutusScriptFromTextEnvelope = | ||
| deserialiseFromTextEnvelopeAnyOf textEnvTypes | ||
| where | ||
| textEnvTypes :: [FromSomeType HasTextEnvelope (AnyPlutusScript era)] | ||
| textEnvTypes = | ||
| [ FromSomeType (asType @(PlutusScriptInEra L.PlutusV1 era)) AnyPlutusScript | ||
| , FromSomeType (asType @(PlutusScriptInEra L.PlutusV2 era)) AnyPlutusScript | ||
| , FromSomeType (asType @(PlutusScriptInEra L.PlutusV3 era)) AnyPlutusScript | ||
| , FromSomeType (asType @(PlutusScriptInEra L.PlutusV4 era)) AnyPlutusScript | ||
| ] |
There was a problem hiding this comment.
The supported Plutus versions are hard-coded here; when a new Plutus language version is introduced, this list must be updated or deserialisation will silently not support the new envelope type. Consider centralising this list (e.g., a single top-level allAnyPlutusScriptTextEnvelopeTypes value reused anywhere similar logic exists) and/or adding an explicit comment that this must be kept in sync with the set of supported Plutus language versions.
| deserialiseAnyPlutusScriptFromTextEnvelope | |
| :: forall era | |
| . L.Era era | |
| => TextEnvelope | |
| -> Either TextEnvelopeError (AnyPlutusScript era) | |
| deserialiseAnyPlutusScriptFromTextEnvelope = | |
| deserialiseFromTextEnvelopeAnyOf textEnvTypes | |
| where | |
| textEnvTypes :: [FromSomeType HasTextEnvelope (AnyPlutusScript era)] | |
| textEnvTypes = | |
| [ FromSomeType (asType @(PlutusScriptInEra L.PlutusV1 era)) AnyPlutusScript | |
| , FromSomeType (asType @(PlutusScriptInEra L.PlutusV2 era)) AnyPlutusScript | |
| , FromSomeType (asType @(PlutusScriptInEra L.PlutusV3 era)) AnyPlutusScript | |
| , FromSomeType (asType @(PlutusScriptInEra L.PlutusV4 era)) AnyPlutusScript | |
| ] | |
| -- | List of all supported Plutus script text envelope types for 'AnyPlutusScript'. | |
| -- NOTE: This must be kept in sync with the set of supported 'Plutus.PlutusLanguage' | |
| -- / 'L.PlutusLanguage' versions in this module. | |
| allAnyPlutusScriptTextEnvelopeTypes | |
| :: forall era. L.Era era | |
| => [FromSomeType HasTextEnvelope (AnyPlutusScript era)] | |
| allAnyPlutusScriptTextEnvelopeTypes = | |
| [ FromSomeType (asType @(PlutusScriptInEra L.PlutusV1 era)) AnyPlutusScript | |
| , FromSomeType (asType @(PlutusScriptInEra L.PlutusV2 era)) AnyPlutusScript | |
| , FromSomeType (asType @(PlutusScriptInEra L.PlutusV3 era)) AnyPlutusScript | |
| , FromSomeType (asType @(PlutusScriptInEra L.PlutusV4 era)) AnyPlutusScript | |
| ] | |
| deserialiseAnyPlutusScriptFromTextEnvelope | |
| :: forall era | |
| . L.Era era | |
| => TextEnvelope | |
| -> Either TextEnvelopeError (AnyPlutusScript era) | |
| deserialiseAnyPlutusScriptFromTextEnvelope = | |
| deserialiseFromTextEnvelopeAnyOf allAnyPlutusScriptTextEnvelopeTypes |
There was a problem hiding this comment.
Looks good 👍 . There are two things that bother me, but I cannot think of any easy way of solving them:
- The fact that we are not using the
HasTextEnvelopeinstance - What copilot pointed out, that we won't get compiler errors when adding a new version of Plutus
Well and, yes, maybe it would also be good to add a roundtrip test and haddock
0b6b10b to
bf6c10d
Compare
Use nonNativeLanguages and withSLanguage to derive textEnvTypes so new Plutus versions are picked up automatically and GHC enforces handling in constraint witness functions.
bf6c10d to
ca16748
Compare
Fix serialiseToCBOR to extract raw script bytes from PlutusRunnable instead of using L.serialize' which adds CBOR framing that deserialiseFromCBOR does not expect. Add CBOR and TextEnvelope roundtrip tests for PlutusScriptInEra and AnyPlutusScript. Add haddocks for the text envelope functions.
ca16748 to
9239e07
Compare
Changelog
Context
AnyPlutusScriptexistentially quantifies the Plutus language, so it cannot have aHasTextEnvelopeinstance directly (sincetextEnvelopeType :: AsType a -> TextEnvelopeTypehas no value to pattern match on to recover the existentiallang). Instead, this adds standalone serialisation/deserialisation functions following the same pattern used byScriptInAnyLang(textEnvelopeToScriptinCardano.Api.Plutus.Internal.Script).Relates to: IntersectMBO/cardano-cli#1240
How to trust this PR
HasTextEnvelope (PlutusScriptInEra lang era)instance, preserving correct per-language envelope types (PlutusScriptV1,PlutusScriptV2, etc.)deserialiseFromTextEnvelopeAnyOfwithFromSomeTypeentries for all four Plutus language versionsCardano.Api.Experimental.PlutusandCardano.Api.ExperimentalChecklist