EVM Puzzle 5 & 6: Math Operations & Multiple Conditions
Introduction
In this post, we'll explore Puzzles 5 and 6, which focus on mathematical operations and combining multiple conditions in the EVM. Puzzle 5 introduces squaring operations while Puzzle 6 combines two separate conditions that must both be satisfied to solve the puzzle.
Understanding EVM Conditional Logic
The EVM provides several ways to implement conditional logic:
- JUMPI: Conditional jump that executes if the top stack value is non-zero
- EQ: Checks if two values are equal, pushing 1 (true) or 0 (false) to the stack
- LT/GT: Compares values for less-than or greater-than conditions
- AND/OR: Bitwise operations that can be used for logical conditions
Transaction Properties
EVM puzzles often make use of transaction properties that can be accessed via opcodes:
- CALLVALUE: The amount of ETH (in wei) sent with the transaction
- CALLDATASIZE: The size of input data in bytes
- CALLER: The address that initiated the call
- GASPRICE: The gas price specified by the transaction
Puzzle 5
JUMPI and EQ
Jumpi: Conditional jump that executes if the top stack value is non-zero
EQ: Checks if two values are equal, pushing 1 (true) or 0 (false) to the stackCopy and Paste → 34800261010014600C57FDFD5B00FDFD into the EVM Playground. Then click RUN.
# Puzzle 5 # Goal: # Successfully reach the JUMPDEST at location 0C (12) # The contract's code is: # # 00 34 CALLVALUE # 01 80 DUP1 # 02 02 MUL # 03 6101 PUSH2 0x0100 # 06 14 EQ # 07 600C PUSH1 0x0C # 09 57 JUMPI # 0A FD REVERT # 0B FD REVERT # 0C 5B JUMPDEST # 0D 00 STOP # 0E FD REVERT # 0F FD REVERT # # Bytecode: 34800261010014600C57FDFD5B00FDFD
Understanding the Puzzle
Let's trace the execution:
CALLVALUEpushes the amount of ETH sent with the transaction onto the stackDUP1duplicates this value on the stackMULmultiplies the top two values (CALLVALUE × CALLVALUE = CALLVALUE²)PUSH2 0x0100pushes the value 256 (0x0100 in hex) onto the stackEQchecks if CALLVALUE² equals 256PUSH1 0x0Cpushes the destination (12) for a potential jumpJUMPIjumps to position 0x0C if the EQ result is true (non-zero)- If the jump occurs, we reach the
JUMPDESTand theSTOPopcode - If not, we hit a
REVERTand fail
Find x:
| Location | Opcode | Meaning | Operation | Stack After |
|---|---|---|---|---|
| 00 | CALLVALUE | Pushes the amount of ETH sent (x) | Push x | [x] |
| 01 | DUP1 | Duplicates the top stack item | Duplicate x | [x, x] |
| 02 | MUL | Multiplies the top two stack items to get 256. (We find this out in the next 2 steps) | x² = 256 | [256] |
| 03 | PUSH2 0x0100 | Pushes 256 (0x0100) onto the stack. (This is where 256 comes from) | Push 256 | [256, 256] |
| 06 | EQ | Checks if top two stack items are equal. (This is how we know x² must = 256) | 256 == 256 → 1 | [1] |
| 07 | PUSH1 0x0C | Pushes jump destination (12) onto stack | Push 12 | [12, 1] |
| 09 | JUMPI | Conditional jump based on condition | Jump to 12 if 1 | [] |
| 0A | REVERT | Reverts the transaction if we reach here | FAIL | [] |
| 0B | REVERT | Reverts the transaction if we reach here | FAIL | [] |
| 0C | JUMPDEST | Valid jump destination marker | SUCCESS! | [] |
| 0D | STOP | Halts execution successfully | - | [] |
Solution:
x To solve this puzzle:
- We need CALLVALUE² = 256
- Therefore, CALLVALUE = √256 = 16
- Send exactly 16 wei with the transaction
This will make the JUMPI condition true, causing the execution to jump to the JUMPDEST at position 0x0C, followed by the STOP opcode which successfully ends the execution.
Puzzle 6
CALLDATALOAD
Puzzle 6 introduces us to the CALLDATALOAD opcode, which loads 32 bytes of calldata starting from a specified position. (This is very important, b/c it means our answer must be 32 bytes long to work.)
Copy and Paste → 60003556FDFDFDFDFDFD5B00 into the EVM Playground. Then click RUN.
# Puzzle 6 # Goal: # Successfully reach the JUMPDEST at location 0A (10) # # The contract's code is: # # [00] PUSH1 00 # Push 0 onto the stack # [02] CALLDATALOAD # Load first 32 bytes of calldata # [03] JUMP # Jump to the loaded value # [04] REVERT # [05] REVERT # [06] REVERT # [07] REVERT # [08] REVERT # [09] REVERT # [0A] JUMPDEST # We need to jump here (to position 10) # [0B] STOP # # Bytecode: 60003556FDFDFDFDFDFD5B00
Understanding CALLDATALOAD
CALLDATALOAD is an important opcode that reads 32 bytes from the calldata starting at a specified position:
- Input: Takes a position from the stack (where to start reading)
- Output: Pushes the 32 bytes read from that position onto the stack
- Behavior:
- Always reads exactly 32 bytes
- Pads with zeros if it reaches the end of calldata
- The value is interpreted as a big-endian number
Understanding the Puzzle
Let's trace the execution:
| Location | Opcode | Meaning | Operation | Stack After |
|---|---|---|---|---|
| 00 | PUSH1 00 | Push 0 onto the stack | Push starting position | [0] |
| 02 | CALLDATALOAD | Load 32 bytes from position 0 | Read calldata | [value_from_calldata] |
| 03 | JUMP | Jump to loaded value | Jump to position | [] |
| 0A | JUMPDEST | Valid jump destination | Success! | [] |
| 0B | STOP | Halt execution | - | [] |
Solution:
To solve this puzzle:
- We need to provide calldata that, when loaded from position 0, will give us the value 10 (0x0A)
- CALLDATALOAD reads 32 bytes, so our calldata needs to be padded appropriately
- The value 10 needs to be properly encoded as a 32-byte big-endian number
Example solution calldata:
0x000000000000000000000000000000000000000000000000000000000000000A
This provides exactly the value we need (10) when loaded from position 0, allowing us to jump to the JUMPDEST at position 0x0A.
Key Takeaways
- Puzzles can combine multiple conditions using JUMPI instructions
- The CALLDATASIZE opcode can be used with CALLVALUE for more complex challenges
- Conditional jumps (JUMPI) allow for branching execution paths
- Transaction properties like value and data size can be used together in conditions