{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StrictData #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
module PlutusLedgerApi.Common.Eval where
import Control.Lens
import PlutusCore
import PlutusCore as ScriptPlutus (Version, defaultVersion)
import PlutusCore.Data as Plutus
import PlutusCore.Default
import PlutusCore.Evaluation.Machine.CostModelInterface as Plutus
import PlutusCore.Evaluation.Machine.ExBudget as Plutus
import PlutusCore.Evaluation.Machine.ExBudgetingDefaults qualified as Plutus
import PlutusCore.Evaluation.Machine.MachineParameters.Default
import PlutusCore.Evaluation.Machine.MachineParameters.DeferredMachineParameters
import PlutusCore.Evaluation.Machine.MachineParameters.ImmediateMachineParameters
import PlutusCore.MkPlc qualified as UPLC
import PlutusCore.Pretty
import PlutusLedgerApi.Common.SerialisedScript
import PlutusLedgerApi.Common.Versions
import PlutusPrelude
import UntypedPlutusCore qualified as UPLC
import UntypedPlutusCore.Evaluation.Machine.Cek qualified as UPLC
import Control.Monad.Except
import Control.Monad.Writer
import Data.Text as Text
import Data.Tuple
import NoThunks.Class
import Prettyprinter
data EvaluationError =
CekError (UPLC.CekEvaluationException NamedDeBruijn DefaultUni DefaultFun)
| DeBruijnError FreeVariableError
| CodecError ScriptDecodeError
| IncompatibleVersionError (ScriptPlutus.Version ())
| CostModelParameterMismatch
deriving stock (Int -> EvaluationError -> ShowS
[EvaluationError] -> ShowS
EvaluationError -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [EvaluationError] -> ShowS
$cshowList :: [EvaluationError] -> ShowS
show :: EvaluationError -> String
$cshow :: EvaluationError -> String
showsPrec :: Int -> EvaluationError -> ShowS
$cshowsPrec :: Int -> EvaluationError -> ShowS
Show, EvaluationError -> EvaluationError -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: EvaluationError -> EvaluationError -> Bool
$c/= :: EvaluationError -> EvaluationError -> Bool
== :: EvaluationError -> EvaluationError -> Bool
$c== :: EvaluationError -> EvaluationError -> Bool
Eq)
makeClassyPrisms ''EvaluationError
instance AsScriptDecodeError EvaluationError where
_ScriptDecodeError :: Prism' EvaluationError ScriptDecodeError
_ScriptDecodeError = forall r. AsEvaluationError r => Prism' r ScriptDecodeError
_CodecError
instance Pretty EvaluationError where
pretty :: forall ann. EvaluationError -> Doc ann
pretty (CekError CekEvaluationException NamedDeBruijn DefaultUni DefaultFun
e) = forall a ann. PrettyClassic a => a -> Doc ann
prettyClassicDef CekEvaluationException NamedDeBruijn DefaultUni DefaultFun
e
pretty (DeBruijnError FreeVariableError
e) = forall a ann. Pretty a => a -> Doc ann
pretty FreeVariableError
e
pretty (CodecError ScriptDecodeError
e) = forall a ann. Show a => a -> Doc ann
viaShow ScriptDecodeError
e
pretty (IncompatibleVersionError Version ()
actual) = Doc ann
"This version of the Plutus Core interface does not support the version indicated by the AST:" forall ann. Doc ann -> Doc ann -> Doc ann
<+> forall a ann. Pretty a => a -> Doc ann
pretty Version ()
actual
pretty EvaluationError
CostModelParameterMismatch = Doc ann
"Cost model parameters were not as we expected"
type LogOutput = [Text.Text]
data VerboseMode = Verbose | Quiet
deriving stock (VerboseMode -> VerboseMode -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: VerboseMode -> VerboseMode -> Bool
$c/= :: VerboseMode -> VerboseMode -> Bool
== :: VerboseMode -> VerboseMode -> Bool
$c== :: VerboseMode -> VerboseMode -> Bool
Eq)
mkTermToEvaluate
:: (MonadError EvaluationError m)
=> LedgerPlutusVersion
-> ProtocolVersion
-> SerialisedScript
-> [Plutus.Data]
-> m (UPLC.Term UPLC.NamedDeBruijn DefaultUni DefaultFun ())
mkTermToEvaluate :: forall (m :: * -> *).
MonadError EvaluationError m =>
LedgerPlutusVersion
-> ProtocolVersion
-> SerialisedScript
-> [Data]
-> m (Term NamedDeBruijn DefaultUni DefaultFun ())
mkTermToEvaluate LedgerPlutusVersion
lv ProtocolVersion
pv SerialisedScript
bs [Data]
args = do
ScriptForExecution (UPLC.Program ()
_ Version ()
v Term NamedDeBruijn DefaultUni DefaultFun ()
t) <- forall e (m :: * -> *).
(AsScriptDecodeError e, MonadError e m) =>
LedgerPlutusVersion
-> ProtocolVersion -> SerialisedScript -> m ScriptForExecution
fromSerialisedScript LedgerPlutusVersion
lv ProtocolVersion
pv SerialisedScript
bs
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Version ()
v forall a. Eq a => a -> a -> Bool
== forall ann. ann -> Version ann
ScriptPlutus.defaultVersion ()) forall a b. (a -> b) -> a -> b
$ forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError forall a b. (a -> b) -> a -> b
$ Version () -> EvaluationError
IncompatibleVersionError Version ()
v
let termArgs :: [Term NamedDeBruijn DefaultUni DefaultFun ()]
termArgs = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (forall a (uni :: * -> *) fun (term :: * -> *) tyname name ann.
(TermLike term tyname name uni fun, Includes uni a) =>
ann -> a -> term ann
UPLC.mkConstant ()) [Data]
args
appliedT :: Term NamedDeBruijn DefaultUni DefaultFun ()
appliedT = forall (term :: * -> *) tyname name (uni :: * -> *) fun ann.
TermLike term tyname name uni fun =>
ann -> term ann -> [term ann] -> term ann
UPLC.mkIterApp () Term NamedDeBruijn DefaultUni DefaultFun ()
t [Term NamedDeBruijn DefaultUni DefaultFun ()]
termArgs
forall (f :: * -> *) a b. Functor f => (a -> f b) -> a -> f a
through (forall e (m :: * -> *) a. MonadError e m => Either e a -> m a
liftEither forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first FreeVariableError -> EvaluationError
DeBruijnError forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall e (m :: * -> *) name (uni :: * -> *) fun a.
(HasIndex name, MonadError e m, AsFreeVariableError e) =>
Term name uni fun a -> m ()
UPLC.checkScope) Term NamedDeBruijn DefaultUni DefaultFun ()
appliedT
unliftingModeIn :: ProtocolVersion -> UnliftingMode
unliftingModeIn :: ProtocolVersion -> UnliftingMode
unliftingModeIn ProtocolVersion
pv =
if ProtocolVersion
pv forall a. Ord a => a -> a -> Bool
>= ProtocolVersion
vasilPV then UnliftingMode
UnliftingDeferred else UnliftingMode
UnliftingImmediate
toMachineParameters :: ProtocolVersion -> EvaluationContext -> DefaultMachineParameters
toMachineParameters :: ProtocolVersion -> EvaluationContext -> DefaultMachineParameters
toMachineParameters ProtocolVersion
pv = case ProtocolVersion -> UnliftingMode
unliftingModeIn ProtocolVersion
pv of
UnliftingMode
UnliftingImmediate -> EvaluationContext -> DefaultMachineParameters
machineParametersImmediate
UnliftingMode
UnliftingDeferred -> EvaluationContext -> DefaultMachineParameters
machineParametersDeferred
data EvaluationContext = EvaluationContext
{ EvaluationContext -> DefaultMachineParameters
machineParametersImmediate :: DefaultMachineParameters
, EvaluationContext -> DefaultMachineParameters
machineParametersDeferred :: DefaultMachineParameters
}
deriving stock forall x. Rep EvaluationContext x -> EvaluationContext
forall x. EvaluationContext -> Rep EvaluationContext x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep EvaluationContext x -> EvaluationContext
$cfrom :: forall x. EvaluationContext -> Rep EvaluationContext x
Generic
deriving anyclass (EvaluationContext -> ()
forall a. (a -> ()) -> NFData a
rnf :: EvaluationContext -> ()
$crnf :: EvaluationContext -> ()
NFData, Context -> EvaluationContext -> IO (Maybe ThunkInfo)
Proxy EvaluationContext -> String
forall a.
(Context -> a -> IO (Maybe ThunkInfo))
-> (Context -> a -> IO (Maybe ThunkInfo))
-> (Proxy a -> String)
-> NoThunks a
showTypeOf :: Proxy EvaluationContext -> String
$cshowTypeOf :: Proxy EvaluationContext -> String
wNoThunks :: Context -> EvaluationContext -> IO (Maybe ThunkInfo)
$cwNoThunks :: Context -> EvaluationContext -> IO (Maybe ThunkInfo)
noThunks :: Context -> EvaluationContext -> IO (Maybe ThunkInfo)
$cnoThunks :: Context -> EvaluationContext -> IO (Maybe ThunkInfo)
NoThunks)
mkDynEvaluationContext :: MonadError CostModelApplyError m => BuiltinVersion DefaultFun -> Plutus.CostModelParams -> m EvaluationContext
mkDynEvaluationContext :: forall (m :: * -> *).
MonadError CostModelApplyError m =>
BuiltinVersion DefaultFun -> CostModelParams -> m EvaluationContext
mkDynEvaluationContext BuiltinVersion DefaultFun
ver CostModelParams
newCMP =
DefaultMachineParameters
-> DefaultMachineParameters -> EvaluationContext
EvaluationContext
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *).
MonadError CostModelApplyError m =>
BuiltinVersion DefaultFun
-> CostModelParams -> m DefaultMachineParameters
immediateMachineParameters BuiltinVersion DefaultFun
ver CostModelParams
newCMP
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall (m :: * -> *).
MonadError CostModelApplyError m =>
BuiltinVersion DefaultFun
-> CostModelParams -> m DefaultMachineParameters
deferredMachineParameters BuiltinVersion DefaultFun
ver CostModelParams
newCMP
assertWellFormedCostModelParams :: MonadError CostModelApplyError m => Plutus.CostModelParams -> m ()
assertWellFormedCostModelParams :: forall (m :: * -> *).
MonadError CostModelApplyError m =>
CostModelParams -> m ()
assertWellFormedCostModelParams = forall (f :: * -> *) a. Functor f => f a -> f ()
void forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall evaluatorcosts builtincosts (m :: * -> *).
(FromJSON evaluatorcosts, FromJSON builtincosts,
ToJSON evaluatorcosts, ToJSON builtincosts,
MonadError CostModelApplyError m) =>
CostModel evaluatorcosts builtincosts
-> CostModelParams -> m (CostModel evaluatorcosts builtincosts)
Plutus.applyCostModelParams CostModel CekMachineCosts BuiltinCostModel
Plutus.defaultCekCostModel
evaluateScriptRestricting
:: LedgerPlutusVersion
-> ProtocolVersion
-> VerboseMode
-> EvaluationContext
-> ExBudget
-> SerialisedScript
-> [Plutus.Data]
-> (LogOutput, Either EvaluationError ExBudget)
evaluateScriptRestricting :: LedgerPlutusVersion
-> ProtocolVersion
-> VerboseMode
-> EvaluationContext
-> ExBudget
-> SerialisedScript
-> [Data]
-> (LogOutput, Either EvaluationError ExBudget)
evaluateScriptRestricting LedgerPlutusVersion
lv ProtocolVersion
pv VerboseMode
verbose EvaluationContext
ectx ExBudget
budget SerialisedScript
p [Data]
args = forall a b. (a, b) -> (b, a)
swap forall a b. (a -> b) -> a -> b
$ forall w a. Writer w a -> (a, w)
runWriter @LogOutput forall a b. (a -> b) -> a -> b
$ forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
runExceptT forall a b. (a -> b) -> a -> b
$ do
Term NamedDeBruijn DefaultUni DefaultFun ()
appliedTerm <- forall (m :: * -> *).
MonadError EvaluationError m =>
LedgerPlutusVersion
-> ProtocolVersion
-> SerialisedScript
-> [Data]
-> m (Term NamedDeBruijn DefaultUni DefaultFun ())
mkTermToEvaluate LedgerPlutusVersion
lv ProtocolVersion
pv SerialisedScript
p [Data]
args
let (Either
(CekEvaluationException NamedDeBruijn DefaultUni DefaultFun)
(Term NamedDeBruijn DefaultUni DefaultFun ())
res, UPLC.RestrictingSt (ExRestrictingBudget ExBudget
final), LogOutput
logs) =
forall fun (uni :: * -> *) cost.
(Ix fun, PrettyUni uni fun) =>
MachineParameters CekMachineCosts CekValue uni fun
-> ExBudgetMode cost uni fun
-> EmitterMode uni fun
-> Term NamedDeBruijn uni fun ()
-> (Either
(CekEvaluationException NamedDeBruijn uni fun)
(Term NamedDeBruijn uni fun ()),
cost, LogOutput)
UPLC.runCekDeBruijn
(ProtocolVersion -> EvaluationContext -> DefaultMachineParameters
toMachineParameters ProtocolVersion
pv EvaluationContext
ectx)
(forall (uni :: * -> *) fun.
PrettyUni uni fun =>
ExRestrictingBudget -> ExBudgetMode RestrictingSt uni fun
UPLC.restricting forall a b. (a -> b) -> a -> b
$ ExBudget -> ExRestrictingBudget
ExRestrictingBudget ExBudget
budget)
(if VerboseMode
verbose forall a. Eq a => a -> a -> Bool
== VerboseMode
Verbose then forall (uni :: * -> *) fun. EmitterMode uni fun
UPLC.logEmitter else forall (uni :: * -> *) fun. EmitterMode uni fun
UPLC.noEmitter)
Term NamedDeBruijn DefaultUni DefaultFun ()
appliedTerm
forall w (m :: * -> *). MonadWriter w m => w -> m ()
tell LogOutput
logs
forall e (m :: * -> *) a. MonadError e m => Either e a -> m a
liftEither forall a b. (a -> b) -> a -> b
$ forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first CekEvaluationException NamedDeBruijn DefaultUni DefaultFun
-> EvaluationError
CekError forall a b. (a -> b) -> a -> b
$ forall (f :: * -> *) a. Functor f => f a -> f ()
void Either
(CekEvaluationException NamedDeBruijn DefaultUni DefaultFun)
(Term NamedDeBruijn DefaultUni DefaultFun ())
res
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ExBudget
budget ExBudget -> ExBudget -> ExBudget
`minusExBudget` ExBudget
final)
evaluateScriptCounting
:: LedgerPlutusVersion
-> ProtocolVersion
-> VerboseMode
-> EvaluationContext
-> SerialisedScript
-> [Plutus.Data]
-> (LogOutput, Either EvaluationError ExBudget)
evaluateScriptCounting :: LedgerPlutusVersion
-> ProtocolVersion
-> VerboseMode
-> EvaluationContext
-> SerialisedScript
-> [Data]
-> (LogOutput, Either EvaluationError ExBudget)
evaluateScriptCounting LedgerPlutusVersion
lv ProtocolVersion
pv VerboseMode
verbose EvaluationContext
ectx SerialisedScript
p [Data]
args = forall a b. (a, b) -> (b, a)
swap forall a b. (a -> b) -> a -> b
$ forall w a. Writer w a -> (a, w)
runWriter @LogOutput forall a b. (a -> b) -> a -> b
$ forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
runExceptT forall a b. (a -> b) -> a -> b
$ do
Term NamedDeBruijn DefaultUni DefaultFun ()
appliedTerm <- forall (m :: * -> *).
MonadError EvaluationError m =>
LedgerPlutusVersion
-> ProtocolVersion
-> SerialisedScript
-> [Data]
-> m (Term NamedDeBruijn DefaultUni DefaultFun ())
mkTermToEvaluate LedgerPlutusVersion
lv ProtocolVersion
pv SerialisedScript
p [Data]
args
let (Either
(CekEvaluationException NamedDeBruijn DefaultUni DefaultFun)
(Term NamedDeBruijn DefaultUni DefaultFun ())
res, UPLC.CountingSt ExBudget
final, LogOutput
logs) =
forall fun (uni :: * -> *) cost.
(Ix fun, PrettyUni uni fun) =>
MachineParameters CekMachineCosts CekValue uni fun
-> ExBudgetMode cost uni fun
-> EmitterMode uni fun
-> Term NamedDeBruijn uni fun ()
-> (Either
(CekEvaluationException NamedDeBruijn uni fun)
(Term NamedDeBruijn uni fun ()),
cost, LogOutput)
UPLC.runCekDeBruijn
(ProtocolVersion -> EvaluationContext -> DefaultMachineParameters
toMachineParameters ProtocolVersion
pv EvaluationContext
ectx)
forall (uni :: * -> *) fun. ExBudgetMode CountingSt uni fun
UPLC.counting
(if VerboseMode
verbose forall a. Eq a => a -> a -> Bool
== VerboseMode
Verbose then forall (uni :: * -> *) fun. EmitterMode uni fun
UPLC.logEmitter else forall (uni :: * -> *) fun. EmitterMode uni fun
UPLC.noEmitter)
Term NamedDeBruijn DefaultUni DefaultFun ()
appliedTerm
forall w (m :: * -> *). MonadWriter w m => w -> m ()
tell LogOutput
logs
forall e (m :: * -> *) a. MonadError e m => Either e a -> m a
liftEither forall a b. (a -> b) -> a -> b
$ forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first CekEvaluationException NamedDeBruijn DefaultUni DefaultFun
-> EvaluationError
CekError forall a b. (a -> b) -> a -> b
$ forall (f :: * -> *) a. Functor f => f a -> f ()
void Either
(CekEvaluationException NamedDeBruijn DefaultUni DefaultFun)
(Term NamedDeBruijn DefaultUni DefaultFun ())
res
forall (f :: * -> *) a. Applicative f => a -> f a
pure ExBudget
final