-
Notifications
You must be signed in to change notification settings - Fork 4
4. CEK Machine Tutorial : Plutus Evaluating Plutus Core Lambda Expressions & Applications with the CEK Machine
This guide introduces how to evaluate lambda expressions and applications in Plutus Core using the CEK machine, with a special focus on costing and emitter modes. Understanding these modes is crucial for debugging, optimizing, and ensuring Cardano smart contracts stay within resource limits.
Costing modes determine how resource usage is measured and enforced during script execution:
| Mode | Purpose |
|---|---|
counting |
Tally total resource usage (CPU, memory) |
tallying |
Tally detailed costs for each operation |
restricting |
Fail evaluation if a maximum budget is exceeded |
restrictingLarge / restrictingEnormous
|
Allow almost any budget for stress-testing |
Emitter modes specify what logging information is produced during execution:
| Mode | What gets logged |
|---|---|
noEmitter |
No logs |
logEmitter |
Basic step logs |
logWithTimeEmitter |
Step logs with timestamps |
logWithBudgetEmitter |
Step logs plus budget consumption info |
Below is a demonstration using the expression (λx. x + 1) 3, showcasing different costing and emitter modes:
import UntypedPlutusCore.Evaluation.Machine.Cek
import UntypedPlutusCore.Core.Type (Term(..), Name(..))
import PlutusCore.Default
main = do
let x = Name () "x" 0
add = Builtin () (DynBuiltinName "addInteger")
body = Apply () (Apply () add (Var () x)) (Constant () (Some (ValueOf DefaultUniInteger 1)))
fun = LamAbs () x body
arg = Constant () (Some (ValueOf DefaultUniInteger 3))
applied = Apply () fun arg
params = defaultCekParameters
putStrLn "== Counting, No Logging =="
let (result1, cost1, logs1) = runCek params counting noEmitter applied
print result1
print cost1
print logs1
putStrLn "\n== Counting, logEmitter =="
let (result2, cost2, logs2) = runCek params counting logEmitter applied
print result2
print cost2
mapM_ putStrLn logs2
putStrLn "\n== Counting, logWithBudgetEmitter =="
let (result3, cost3, logs3) = runCek params counting logWithBudgetEmitter applied
print result3
print cost3
mapM_ putStrLn logs3
putStrLn "\n== Tallying, logEmitter =="
let (result4, cost4, logs4) = runCek params tallying logEmitter applied
print result4
print cost4
mapM_ putStrLn logs4
putStrLn "\n== Restricting with tiny budget (expect failure) =="
let restrictParams = restricting (ExRestrictingBudget 1 1)
(result5, cost5, logs5) = runCek params restrictParams logEmitter applied
print result5
print cost5
mapM_ putStrLn logs5Below are typical outcomes for various mode combinations:
- Counting + No Logging: Only result and total resource usage (CPU/memory) are shown. No logs.
- Counting + logEmitter: Result, cost, and a step-by-step textual log of the evaluator's process.
- Counting + logWithBudgetEmitter: Same as above, but each log entry includes budget usage so far.
- Tallying + logEmitter: Logs as above, but the cost is a detailed breakdown per operation.
-
Restricting (with small budget): Evaluation fails if resource use exceeds the limit (
CekEvaluationException).
== Counting, No Logging ==
Right (Constant () (Some (ValueOf DefaultUniInteger 4)))
ExBudget { cpu = ..., memory = ... }
[]
== Counting, logEmitter ==
Right (Constant () (Some (ValueOf DefaultUniInteger 4)))
ExBudget { cpu = ..., memory = ... }
AppStep: evaluating application ...
BuiltinStep: evaluating builtin addInteger ...
...
== Counting, logWithBudgetEmitter ==
Right (Constant () (Some (ValueOf DefaultUniInteger 4)))
ExBudget { cpu = ..., memory = ... }
AppStep [budget: cpu=5, mem=3]: evaluating application ...
...
== Tallying, logEmitter ==
Right (Constant () (Some (ValueOf DefaultUniInteger 4)))
TallyingSt { addInteger = ExBudget ..., ... }
...
== Restricting with tiny budget (expect failure) ==
Left (CekEvaluationException ...)
ExBudget { cpu = 1, memory = 1 }
...
-
Change the emitter mode: Try
noEmitter,logEmitter,logWithTimeEmitter, orlogWithBudgetEmitter. -
Change the costing mode: Use
counting,tallying, orrestricting (ExRestrictingBudget cpu mem). -
Change the term: Try more complex terms such as
(λx. λy. x * y) 4 5.
| Costing Mode | Emitter Mode | What you'll see |
|---|---|---|
| counting | noEmitter | Result and total cost only |
| counting | logEmitter | Result, cost, plain step logs |
| counting | logWithBudgetEmitter | Result, cost, logs with current budget after each step |
| tallying | logEmitter | Result, detailed cost breakdown, logs |
| restricting (small) | logEmitter | Likely failure (budget exceeded), logs |
-
Adjust the budget: Change the budget in
restrictingto a larger value so the evaluation succeeds. -
Try other emitters: Use
logWithTimeEmitterfor timestamped logs. - Observe inefficient scripts: Use terms with repeated applications and see how resource usage grows.
A lambda function in Plutus Core is structured as follows:
LamAbs ann (Name ann Text Unique) (Term Name uni fun ann)-
ann: Annotation (can be()) -
Name: Variable name -
Term: Function body
Example:
Identity function (\x -> x) in Plutus Core:
LamAbs () (Name () "x" 0) (Var () (Name () "x" 0))Function application in Plutus Core applies a function to an argument:
Apply ann (Term Name uni fun ann) (Term Name uni fun ann)- First term: function
- Second term: argument
Evaluating (λx. x) 42:
import UntypedPlutusCore.Evaluation.Machine.Cek
import UntypedPlutusCore.Core.Type (Term(..), Name(..))
import PlutusCore.Default
main = do
let x = Name () "x" 0
identity = LamAbs () x (Var () x)
arg = Constant () (Some (ValueOf DefaultUniInteger 42))
applied = Apply () identity arg
params = defaultCekParameters
(result, cost, logs) = runCek params counting logEmitter applied
print result -- Right (Constant () (Some (ValueOf DefaultUniInteger 42)))
print cost
putStrLn "Logs:"
mapM_ putStrLn logsEvaluating (λx. x + 1) 10:
import UntypedPlutusCore.Evaluation.Machine.Cek
import UntypedPlutusCore.Core.Type (Term(..), Name(..))
import PlutusCore.Default
main = do
let x = Name () "x" 0
add = Builtin () (DynBuiltinName "addInteger")
body = Apply () (Apply () add (Var () x)) (Constant () (Some (ValueOf DefaultUniInteger 1)))
fun = LamAbs () x body
arg = Constant () (Some (ValueOf DefaultUniInteger 10))
applied = Apply () fun arg
params = defaultCekParameters
(result, cost, logs) = runCek params counting logEmitter applied
print result -- Right (Constant () (Some (ValueOf DefaultUniInteger 11)))
print cost
putStrLn "Logs:"
mapM_ putStrLn logsTask: Write and evaluate (λx. x * 2) 7.
Solution:
import UntypedPlutusCore.Evaluation.Machine.Cek
import UntypedPlutusCore.Core.Type (Term(..), Name(..))
import PlutusCore.Default
main = do
let x = Name () "x" 0
mul = Builtin () (DynBuiltinName "multiplyInteger")
body = Apply () (Apply () mul (Var () x)) (Constant () (Some (ValueOf DefaultUniInteger 2)))
fun = LamAbs () x body
arg = Constant () (Some (ValueOf DefaultUniInteger 7))
applied = Apply () fun arg
params = defaultCekParameters
(result, cost, logs) = runCek params counting logEmitter applied
print result -- Right (Constant () (Some (ValueOf DefaultUniInteger 14)))
print cost
putStrLn "Logs:"
mapM_ putStrLn logs-
Nested Applications: Build up terms incrementally using
Apply. -
Multiple Arguments: Use nested lambdas, e.g.,
(\x y -> x + y) 2 3asApply (Apply (LamAbs x (LamAbs y body)) arg1) arg2 -
Built-in Functions: Use
Builtin () (DynBuiltinName "builtinName")for supported built-ins (e.g.,addInteger,multiplyInteger).
- Evaluate
(λx. λy. x + y) 5 8and print the result (should yield 13). - Evaluate
(λx. if x then 1 else 0) Trueand explore conditional logic. - Try applying
(λx. x)to a boolean value and observe the result.
| Plutus Term | Meaning |
|---|---|
LamAbs () x (Var () x) |
λx. x (identity) |
Apply () fun arg |
fun applied to arg |
Builtin () (DynBuiltinName "addInteger") |
Integer addition |
Constant () (Some (ValueOf ... value)) |
Literal constant |
Experimenting with different costing and emitter modes in the CEK machine enables a deep understanding of how Plutus Core terms are evaluated, resource usage is tracked, and logs are generated. This is invaluable for developing, debugging, and optimizing Cardano smart contracts to ensure they perform reliably within resource limits.
| Term | Definition |
|---|---|
| CEK Machine | An abstract machine used to evaluate Plutus Core terms, tracking cost (CPU, memory) and producing logs. |
| Costing Mode | Determines how resources are accounted for during script evaluation (e.g., counting, tallying, restricting). |
| Emitter Mode | Defines the granularity and style of logs emitted during evaluation (e.g., none, simple, with timestamps, with budget info). |
| Lambda (LamAbs) | A function abstraction in Plutus Core. |
| Apply | Application of a function to an argument in Plutus Core. |
| Builtin | Predefined function available in Plutus Core, such as addInteger or multiplyInteger. |
| Constant | A literal value (e.g., integer, boolean) in Plutus Core. |
| ExBudget | Data structure representing resource usage (CPU, memory) in script evaluation. |
| CekEvaluationException | Error thrown when evaluation exceeds a specified resource budget. |
| DynBuiltinName | Dynamic name referring to a specific built-in function in Plutus Core. |
| defaultCekParameters | Default configuration parameters for running the CEK machine. |
Bernard Sibanda is a global Technology Entrepreneur, Web3 and Software Consultant with a deep focus on Cardano Blockchain, Midnight and Community building.
Key Positions:
- Founder, CTO, Developer Advocate cohort #1, Fullstake Developer, Cardano Ambassador, Catalyst Project Manager, DREP-WIMS:
- Co-founder of ABL Tech and Cardano Africa Live
- EBU-certified Plutus Pioneer (Plutus/Haskell)
- Cohort #1 Plutus Pioneer Developer
- Catalyst Community Reviewer & Funded Projects Manager
-
DRep for WIMS-Cardano (ID:
drep1yguj8zu48n99pv70yl6ckzt9hdgjy8yjnlqs2uyzcpafnjgu4vkul) - Intersect Developer Advocate
- Intersect Committe Member 2025-2026
- Cardano Marketer,Promoter and blogger
- Cardano Open Source Contributor
- Cardano communities and events organizer and builder
- Cardano Ambassador for South Africa
Official links:
- Stablecoins Dex
- Coxygen Global Universities
- WIMS Cardano Global
- Cardano Africa Live
- WIMS Cardano Videos
- Cardano Smart Contract Videos
- Fullstack IT Consulting
Social links: