diff --git a/CHANGELOG.md b/CHANGELOG.md index 84f4d88..2019314 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.4.1 - Mapping for Typename on loading + +Add `readTypedScriptWith`, which allows to load scripts having been written with an older version of `ply`, see _Mapping for Typename_ section in README.md for example. + # 0.4.0 - Direct 'TypedScript' access and usage, and add Rational instances - Add `Ply.Core.Unsafe` to the public API, which allows direct access to `TypedScript`. diff --git a/README.md b/README.md index 7b44b11..8f947f0 100644 --- a/README.md +++ b/README.md @@ -157,6 +157,22 @@ main = do > Aside: Notice how I didn't use type applications, it got inferred from the surrounding context! +### Mapping for Typename + +The way `ply` stores scripts parameters type names has changed at some point. Previously `Typename` was stored without package qualifiers, so if you need to load scripts serialized quite a while ago, you might need a way to convert type names upon doing that. Use `readTypedScriptWith`, which takes an additional mapping like as following: + +```haskell +paramMapping :: Text -> Text +paramMapping = \case + "Address" -> "PlutusLedgerApi.V1.Address:Address" + "Credential" -> "PlutusLedgerApi.V1.Credential:Credential" + "CurrencySymbol" -> "PlutusLedgerApi.V1.Value:CurrencySymbol" + "ValidatorHash" -> "PlutusLedgerApi.V1.Scripts:ScriptHash" + "TokenName" -> "PlutusLedgerApi.V1.Value:TokenName" + "Integer" -> "GHC.Num.Integer:Integer" + others -> others +``` + ## Building a TypedScript directly from Plutarch `Ply.Plutarch.writeTypedScript` is essentially a wrapper around `Ply.Plutarch.mkEnvelope`, which is then a wrapper around `Ply.Plutarch.toTypedScript`. diff --git a/ply-core/src/Ply.hs b/ply-core/src/Ply.hs index d74622e..5e452e6 100644 --- a/ply-core/src/Ply.hs +++ b/ply-core/src/Ply.hs @@ -8,6 +8,7 @@ module Ply ( typeName, PlyArg, readTypedScript, + readTypedScriptWith, getPlutusVersion, (#), (#$), @@ -17,7 +18,7 @@ module Ply ( import Ply.Core.Apply ((#), (#!), (#$), (#$!)) import Ply.Core.Class (PlyArg) -import Ply.Core.TypedReader (readTypedScript) +import Ply.Core.TypedReader (readTypedScript, readTypedScriptWith) import Ply.Core.Typename (typeName) import Ply.Core.Types ( ScriptReaderException (..), diff --git a/ply-core/src/Ply/Core/TypedReader.hs b/ply-core/src/Ply/Core/TypedReader.hs index 6fa7cdc..21c07f8 100644 --- a/ply-core/src/Ply/Core/TypedReader.hs +++ b/ply-core/src/Ply/Core/TypedReader.hs @@ -1,10 +1,16 @@ {-# LANGUAGE UnboxedTuples #-} {-# LANGUAGE UndecidableInstances #-} -module Ply.Core.TypedReader (TypedReader, readTypedScript, mkTypedScript, typedScriptToEnvelope) where +module Ply.Core.TypedReader ( + TypedReader, + readTypedScript, + readTypedScriptWith, + typedScriptToEnvelope, +) where import Control.Exception (throwIO) import Control.Monad (unless) +import Data.Coerce (coerce) import Data.Proxy (Proxy (Proxy)) import Data.Text (Text) @@ -15,6 +21,7 @@ import Ply.Core.Types ( ScriptRole (ValidatorRole), TypedScript (TypedScriptConstr), TypedScriptEnvelope (..), + Typename (Typename), ) import Ply.Core.Unsafe (unsafeUnTypedScript) @@ -27,19 +34,21 @@ type TypedReader rl params = , ReifyTypenames params ) --- | Pure function to parse a 'TypedScript' from a 'TypedScriptEnvelope'. -mkTypedScript :: +mkTypedScriptWith :: forall rl params. TypedReader rl params => + (Text -> Text) -> TypedScriptEnvelope -> Either ScriptReaderException (TypedScript rl params) -mkTypedScript (TypedScriptEnvelope ver rol params _ prog) = do +mkTypedScriptWith mapping (TypedScriptEnvelope ver rol params _ prog) = do unless (rol == reifyRole (Proxy @rl)) . Left $ ScriptRoleError ValidatorRole rol let expectedParams = reifyTypenames $ Proxy @params - unless (expectedParams == params) . Left $ ScriptTypeError expectedParams params + let actualParams = coerce mapping <$> params + unless (expectedParams == actualParams) . Left $ ScriptTypeError expectedParams actualParams pure $ TypedScriptConstr ver prog -{- | Read and verify a 'TypedScript' from given filepath. +{- | Read and verify a 'TypedScript' from given filepath. If you need to adjust type names use +'readTypedScriptWith' instead. The user is responsible for choosing the "correct" 'ScriptRole' and script parameters, probably with type applications. The reader will then use this information to parse the file and verify @@ -50,7 +59,21 @@ readTypedScript :: -- | File path where the typed script file is located. FilePath -> IO (TypedScript r params) -readTypedScript p = readEnvelope p >>= either throwIO pure . mkTypedScript +readTypedScript = readTypedScriptWith id + +{- | Like 'readTypedScript', but takes a mapper @(Text -> Text) @ for type names of a script +parameters. It might be useful if your script have been serialized with an older version of +@ply@, which would write type constructor name, or using old-style ledger types. Mapping +applies to @Typename@'s coming in the envelop. +-} +readTypedScriptWith :: + TypedReader r params => + -- | Mapping for type names of script parameters. + (Text -> Text) -> + -- | File path where the typed script file is located. + FilePath -> + IO (TypedScript r params) +readTypedScriptWith mapping p = readEnvelope p >>= either throwIO pure . mkTypedScriptWith mapping -- | Converting a 'TypedScript' into a 'TypedScriptEnvelope', given the description and the script. typedScriptToEnvelope :: diff --git a/ply-core/src/Ply/Core/Typename.hs b/ply-core/src/Ply/Core/Typename.hs index b1a5f29..6a5da3e 100644 --- a/ply-core/src/Ply/Core/Typename.hs +++ b/ply-core/src/Ply/Core/Typename.hs @@ -1,7 +1,7 @@ {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE BangPatterns #-} -module Ply.Core.Typename (Typename, typeName) where +module Ply.Core.Typename (Typename (Typename), typeName) where import Control.Monad (when) import Data.Aeson (FromJSON (parseJSON), ToJSON, Value (String)) diff --git a/ply-core/src/Ply/Core/Types.hs b/ply-core/src/Ply/Core/Types.hs index 489b044..7110621 100644 --- a/ply-core/src/Ply/Core/Types.hs +++ b/ply-core/src/Ply/Core/Types.hs @@ -6,7 +6,7 @@ module Ply.Core.Types ( ScriptRole (..), ScriptReaderException (..), TypedScriptEnvelope (..), - Typename, + Typename (Typename), UPLCProgram, ) where @@ -36,7 +36,7 @@ import PlutusLedgerApi.Common (deserialiseUPLC, serialiseUPLC) import UntypedPlutusCore (DeBruijn, DefaultFun, DefaultUni, Program) import Ply.Core.Serialize.Script (serializeScriptCbor) -import Ply.Core.Typename (Typename) +import Ply.Core.Typename (Typename (Typename)) type UPLCProgram = Program DeBruijn DefaultUni DefaultFun ()