Implement SerialiseAsCBOR and Eq for AnyScript era#1122
Implement SerialiseAsCBOR and Eq for AnyScript era#1122
Conversation
f2fbb2e to
da77b52
Compare
There was a problem hiding this comment.
Pull request overview
Adds CBOR serialisation and equality support for AnyScript in the experimental API, plus generators and property tests to validate CBOR roundtripping for the Conway-era AnyScript wrapper.
Changes:
- Implement
Eq (AnyScript era)by resolving the existential Plutus language viaeqT. - Implement
SerialiseAsCBOR (AnyScript era)using the ledgerScriptencoding (native vs Plutus tagged form). - Add a Conway
AnyScriptgenerator and Hedgehog properties for CBOR roundtrip and failure cases.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
cardano-api/src/Cardano/Api/Experimental/AnyScript.hs |
Adds Eq and SerialiseAsCBOR instances for AnyScript and the AsAnyScript type proxy. |
cardano-api/gen/Test/Gen/Cardano/Api/Experimental.hs |
Adds generators for SimpleScript in Conway and AnyScript values used by tests. |
cardano-api/test/cardano-api-test/Test/Cardano/Api/Experimental.hs |
Adds a new test group with CBOR roundtrip and “garbage bytes” deserialisation properties for AnyScript. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| import Cardano.Api.Ledger.Internal.Reexport qualified as L | ||
| import Cardano.Api.Serialise.Cbor | ||
|
|
||
| import Cardano.Binary qualified as CBOR | ||
| import Cardano.Ledger.Binary qualified as CBOR | ||
| import Cardano.Ledger.Core qualified as L |
There was a problem hiding this comment.
The module imports two different modules qualified as L (Cardano.Api.Ledger.Internal.Reexport and Cardano.Ledger.Core), which will not compile due to the duplicate qualifier. Use distinct qualifiers (e.g. Ledger vs LedgerCore) or drop one import if it’s redundant, and update the references accordingly.
| prop_roundtrip_cbor_any_script :: Property | ||
| prop_roundtrip_cbor_any_script = H.property $ do | ||
| script <- H.forAll genAnyScript | ||
| H.tripping script Exp.serialiseToCBOR (Exp.deserialiseFromCBOR Exp.AsAnyScript) |
There was a problem hiding this comment.
serialiseToCBOR, deserialiseFromCBOR, and AsAnyScript are not exported from Cardano.Api.Experimental, so Exp.serialiseToCBOR / Exp.deserialiseFromCBOR Exp.AsAnyScript won’t typecheck from this import. Either import Cardano.Api.Serialise.Cbor (for the methods) and Cardano.Api.Experimental.AnyScript (for AsAnyScript), or switch to using Api.serialiseToCBOR / Api.deserialiseFromCBOR with AsAnyScript brought into scope.
| prop_deserialise_garbage_bytes_returns_left = H.property $ do | ||
| garbage <- H.forAll $ Gen.bytes (Range.linear 0 128) | ||
| case Exp.deserialiseFromCBOR Exp.AsAnyScript garbage of | ||
| Left _ -> H.success | ||
| Right _ -> H.annotate "Expected deserialisation failure but got Right" >> H.failure |
There was a problem hiding this comment.
This property is not guaranteed: arbitrary bytes can occasionally form a valid CBOR encoding of a script, so the test can be flaky (sometimes Right). Consider generating bytes that are guaranteed invalid for this decoder (e.g. take a valid serialiseToCBOR output and append extra trailing bytes, or otherwise ensure the bytes cannot match the tagged AlonzoScript encoding).
51d7d8c to
830c361
Compare
Add HasTypeProxy and SerialiseAsCBOR instances for AnyScript era, enabling CBOR serialisation/deserialisation of both simple and plutus scripts using the ledger's native Script era encoding format. Closes #1088
Uses eqT to handle the existential lang in AnyPlutusScript, delegating to the underlying Eq instances on SimpleScript and PlutusScriptInEra. Also adds Typeable lang constraint to the AnyPlutusScript constructor.
Adds genAnyScript and genSimpleScriptInEra generators, along with roundtrip and garbage-input property tests for SerialiseAsCBOR AnyScript.
830c361 to
13deb19
Compare
| CBOR.DecoderErrorCustom | ||
| ( mconcat | ||
| [ "AnyScript PlutusScript (" | ||
| , Text.pack (show (Plutus.plutusLanguage plutus)) |
There was a problem hiding this comment.
| , Text.pack (show (Plutus.plutusLanguage plutus)) | |
| , Text.show (Plutus.plutusLanguage plutus) |
| , ")" | ||
| ] | ||
| ) | ||
| (Text.pack . show $ pretty e) |
There was a problem hiding this comment.
| (Text.pack . show $ pretty e) | |
| (Text.show $ pretty e) |
| script <- decodeScript | ||
| case L.getNativeScript script of | ||
| Just ns -> Right $ AnySimpleScript (SimpleScript ns) | ||
| Nothing -> |
There was a problem hiding this comment.
@copilot Rewrite deserialiseFromCBOR using asum over a list, utilising Alternative Maybe to short-circuit on the first successful parse.
|
@carbolymer I've opened a new pull request, #1130, to work on those changes. Once the pull request is ready, I'll request review from you. |
|
|
||
| -- | Deserialising random garbage bytes should always return 'Left'. | ||
| prop_deserialise_garbage_bytes_returns_left :: Property | ||
| prop_deserialise_garbage_bytes_returns_left = H.property $ do |
There was a problem hiding this comment.
This negative case test is pointless - it does not give us any guarantees and just adds a maintenance burden.
Changelog
Context
Closes #1088
How to trust this PR
AnyScriptCBOR serialisation uses the ledger's taggedAlonzoScriptformat (tag 0 = native script, tags 1–4 = PlutusV1–V4), so round-tripping is unambiguous.Eq (AnyScript era)useseqTto resolve the existentiallanginAnyPlutusScript, delegating to the underlyingEqinstances.H.tripping, and garbage bytes always returningLeft.Checklist