Skip to content

add readTypedScriptWith #41

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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`.
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
3 changes: 2 additions & 1 deletion ply-core/src/Ply.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module Ply (
typeName,
PlyArg,
readTypedScript,
readTypedScriptWith,
getPlutusVersion,
(#),
(#$),
Expand All @@ -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 (..),
Expand Down
37 changes: 30 additions & 7 deletions ply-core/src/Ply/Core/TypedReader.hs
Original file line number Diff line number Diff line change
@@ -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)

Expand All @@ -15,6 +21,7 @@ import Ply.Core.Types (
ScriptRole (ValidatorRole),
TypedScript (TypedScriptConstr),
TypedScriptEnvelope (..),
Typename (Typename),
)
import Ply.Core.Unsafe (unsafeUnTypedScript)

Expand All @@ -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
Expand All @@ -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 ::
Expand Down
2 changes: 1 addition & 1 deletion ply-core/src/Ply/Core/Typename.hs
Original file line number Diff line number Diff line change
@@ -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))
Expand Down
4 changes: 2 additions & 2 deletions ply-core/src/Ply/Core/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module Ply.Core.Types (
ScriptRole (..),
ScriptReaderException (..),
TypedScriptEnvelope (..),
Typename,
Typename (Typename),
UPLCProgram,
) where

Expand Down Expand Up @@ -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 ()

Expand Down