Lazy Evaluation Traps
A language-level footgun in Plutus Core evaluation; see the Aiken language tour on control flow.
Identifier: evaluation-order
Property statement: Every check that must run to keep the validator safe is actually forced, not placed on a branch that can be skipped.
Test: A transaction passes validation because a required check sat on a short-circuited or untaken branch and never executed.
Impact:
- Required checks silently skipped
- A validator that succeeds when it should have failed
Further explanation:
Plutus Core, which Aiken compiles to, is lazy in a few places that matter for validators. The boolean operators && and || short-circuit: the second operand runs only if the first did not already decide the result. if/else and when evaluate only the branch that is taken. And an error or fail fires only when it is actually forced. Aiken is otherwise strict, but these control-flow constructs keep the lazy behavior.
The trap is a required check placed where it can be skipped. A necessary assertion in the right operand of || never runs when the left operand is already True. A guard inside a branch that is not taken never fires. A fail you expected to stop a bad transaction sits on a side that is never forced. In each case the validator returns success while a check you thought was protecting it did nothing.
Idiomatic Aiken avoids most of this: the and { ... } and or { ... } blocks list all conditions explicitly, and expect and fail are strict where the code path is taken. The exposure is real for hand-written Plutus, Plutarch, or UPLC, and for misjudged operand order in any language. Prevent it by never putting a required predicate or a security-relevant fail on a branch that can be short-circuited away, forcing every check that must run (a statement-level expect/fail, or listing conditions explicitly), and ordering operands so cheap, non-security guards go first and the checks that must always run are never the ones skipped.