3. CEK Machine Tutorial : Plutus - wimsio/universities GitHub Wiki
Plutus CEK Machine Tutorial
Table of Contents
1. Introduction
The CEK Machine is the virtual machine used by Plutus to evaluate Untyped Plutus Core (UPLC) smart contracts on Cardano.
- It tracks execution costs (resource usage), supports various error types, and offers logging modes for debugging.
- The CEK API lets you run scripts, analyze execution, and safely interact with on-chain smart contracts.
2. Running the Machine
Basic Evaluation
The main entry point is runCek
:
runCek
:: ThrowableBuiltins uni fun
=> MachineParameters CekMachineCosts fun (CekValue uni fun ann)
-> ExBudgetMode cost uni fun
-> EmitterMode uni fun
-> Term Name uni fun ann
-> (Either (CekEvaluationException Name uni fun) (Term Name uni fun ()), cost, [Text])
- Purpose: Evaluates a Plutus Core term, tracks execution cost, and produces logs.
- Returns: A tuple of
(result, cost, logs)
.
Minimal Example
import UntypedPlutusCore.Evaluation.Machine.Cek
import UntypedPlutusCore.Core.Type (Term(..), Name(..))
-- ...other imports...
main = do
let params = defaultCekParameters
budgetMode = counting
emitter = logEmitter
term = ... -- your Plutus Core term here
let (result, cost, logs) = runCek params budgetMode emitter term
print result
print cost
mapM_ putStrLn logs
Logging and Emission
-
Emitter modes control what gets logged:
noEmitter
: No logging.logEmitter
: Basic logs.logWithTimeEmitter
,logWithBudgetEmitter
: Add timestamps/budget to logs.
Example:
runCek params counting logWithTimeEmitter myTerm
Costing
-
Costing modes track resource usage:
counting
: Total budget spent.tallying
: Budget per operation.restricting
: Enforce a budget cap (fail if over).restrictingLarge
/restrictingEnormous
: Pre-set very large budgets for benchmarks.
Example:
runCek params restrictingLarge noEmitter myTerm
Error Handling
Evaluation can fail for structural reasons (bad program) or operational reasons (runtime error, like division by zero):
case result of
Left err -> putStrLn $ "Evaluation failed: " ++ show err
Right val -> putStrLn $ "Success: " ++ show val
3. Advanced Topics
Costing Modes
- counting: Track total cost (for analysis).
- tallying: Track detailed per-operation cost.
- restricting: Limit execution to a maximum budget (
ExRestrictingBudget
).
Set the mode in runCek
:
runCek params tallying logEmitter term
Emitter Modes
- noEmitter: No log output.
- logEmitter: Basic logging.
- logWithTimeEmitter: Logs with timestamps.
- logWithBudgetEmitter: Logs with budget information.
Example:
runCek params counting logWithBudgetEmitter term
Working with DeBruijn Terms
For terms using DeBruijn indices (instead of named variables), use:
runCekDeBruijn :: ... -> NTerm uni fun ann -> (Either (CekEvaluationException NamedDeBruijn uni fun) (NTerm uni fun ()), cost, [Text])
This is useful for advanced scenarios, such as compiling or optimizing terms for on-chain deployment.
4. Exercises
Exercise 1:
Write a function to evaluate a simple Plutus Core integer constant using runCek
and print the result and cost.
Exercise 2:
Modify your function to log each evaluation step using a different emitter mode.
Exercise 3:
Evaluate an invalid term (such as applying a constant as a function) and print the resulting error.
5. Exercise Solutions
Solution 1:
import UntypedPlutusCore.Evaluation.Machine.Cek
import UntypedPlutusCore.Core.Type (Term(..), Name(..))
import PlutusCore.Default
main = do
let term = Constant () (Some (ValueOf DefaultUniInteger 42))
params = defaultCekParameters
(result, cost, logs) = runCek params counting noEmitter term
print result -- Should print: Right (Constant () ...)
print cost -- Shows execution cost
Solution 2:
main = do
let term = Constant () (Some (ValueOf DefaultUniInteger 42))
params = defaultCekParameters
(result, cost, logs) = runCek params counting logEmitter term
print result
print cost
mapM_ putStrLn logs -- Prints logs for each step
Solution 3:
main = do
let invalidTerm = Apply () (Constant () (Some (ValueOf DefaultUniInteger 1)))
(Constant () (Some (ValueOf DefaultUniInteger 2)))
params = defaultCekParameters
(result, cost, logs) = runCek params counting logEmitter invalidTerm
print result -- Should print: Left (CekEvaluationException ...)
print cost
mapM_ putStrLn logs
6. Glossary
Term | Meaning |
---|---|
CEK Machine | An abstract machine (evaluator) for Untyped Plutus Core; tracks costs and errors. |
Emitter Mode | Controls what gets logged during evaluation. |
Costing Mode | Controls how execution resource usage is tracked or restricted. |
Term | An Untyped Plutus Core expression to be evaluated. |
ExBudget | The resource budget (memory, CPU) for script execution. |
MachineParameters | The runtime parameters (cost models, builtins, etc) for the CEK machine. |
CekEvaluationException | An error thrown during evaluation (could be structural or operational). |
DeBruijn Index | A variable representation that avoids naming issues by using numeric indices. |
NTerm | A Plutus Core term in DeBruijn representation. |
Constant | A term representing a literal value (integer, bool, etc). |
Apply | Application of one term to another (function application). |
Counting / Tallying / Restricting | Different ways to measure or limit execution cost. |