Puzzle 3:
CALLDATASIZE
Puzzle 3 introduces us to the CALLDATASIZE opcode, which gets the size of the input data and demonstrates how we can use it to control program flow with JUMP operations.
Copy and Paste → 3656FDFD5B00 into the EVM Playground. Then click RUN.
# Puzzle 3 # Goal: # Find the right amount of calldata that will make execution reach JUMPDEST (04) 00 37 CALLDATASIZE 01 56 JUMP 02 FD REVERT 03 FD REVERT 04 5B JUMPDEST 05 00 STOP # Bytecode: 0x3656FDFD5B00
Understanding CALLDATASIZE
CALLDATASIZE is used to return the size (in bytes) of the data sent along with a transaction. It determines how much data was included in a transaction's calldata field.
Common uses:- Validating input data length before processing
- Determining if any function arguments were provided
- Detecting whether a contract was called directly or via another contract
- Used with CALLDATACOPY to read transaction input
** In hexadecimal notation (starting with 0x), each byte is represented by 2 hex digits.
For Example:
0x34 → 1 byte (CALLDATASIZE = 1)
0x3456 → 2 bytes (CALLDATASIZE = 2)
0x345678 → 3 bytes (CALLDATASIZE = 3)
0x12345678 → 4 bytes (CALLDATASIZE = 4) ← Solution for this puzzle!
0x1234567890 → 5 bytes (CALLDATASIZE = 5)
Understanding the Puzzle
In this puzzle, since our CALLVALUE = 4, we need to make our CALLVALUESIZE exactly 4 bytes of calldata. This will push 4 onto the stack and direct our JUMP instruction to our destination (04).
| Location | Opcode | Meaning | Operation | Stack After |
|---|---|---|---|---|
| 0 | CALLDATASIZE | Pushes the size of calldata onto the stack | Push 4 | [4] |
| 1 | JUMP | Jumps to the position specified by the top stack value | Jump to 4 | [] |
| 2 | REVERT | Reverts the transaction if we jump here | FAIL | [] |
| 3 | REVERT | Reverts the transaction if we jump here | FAIL | [] |
| 4 | JUMPDEST | Valid jump destination marker | SUCCESS! | [] |
| 5 | STOP | Halts execution successfully | - | [] |
Solution:
To solve this puzzle, we need to:
- Send exactly 4 bytes of calldata (this will make CALLDATASIZE push 4 onto the stack)
- This will make the JUMP instruction jump to position 4, where the JUMPDEST is located
This will work with any 4 byte number (0x11223344)
This puzzle teaches us that CALLDATASIZE can be used to control program flow just like CALLVALUE, but it's based on the size of the input data rather than the amount of ETH sent.
Puzzle 4:
XOR and CODESIZE
Puzzle 4 introduces the XOR operation, which performs a bitwise exclusive OR on stack values.
Copy and Paste → 34381856FDFDFDFDFDFD5B00 into the EVM Playground. Then click RUN.
# Puzzle 4 # Goal: # Make it to JUMPDEST at 0x0a 00 34 CALLVALUE 01 38 CODESIZE 02 18 XOR 03 56 JUMP 04 FD REVERT 05 FD REVERT 06 FD REVERT 07 FD REVERT 08 FD REVERT 09 FD REVERT 0A 5B JUMPDEST 0B 00 STOP # Bytecode: 0x34381856FDFDFDFDFDFD5B00
Understanding XOR
XOR (exclusive OR) is a bitwise operation that returns 1 only when exactly one of the compared bits is 1. It returns 0 if both bits are the same (both 0 or both 1).
XOR Truth Table:
0 XOR 0 = 0
0 XOR 1 = 1
1 XOR 0 = 1
1 XOR 1 = 0
Understanding CODESIZE
CODESIZE is an opcode that returns the size (in bytes) of the currently executing contract's bytecode. Here's what you need to know:
- Purpose: Determines the total size of the contract's bytecode
- How it's calculated: Each opcode is 1 byte (2 digits), and some opcodes (like PUSH1, PUSH2, etc.) have additional data bytes that count toward the total size
- Common uses:
- Contract size optimization (Ethereum has a maximum contract size limit)
- Security checks (contract size can help identify certain vulnerabilities)
- Creating mathematical relationships in code (as in this puzzle)
In this puzzle, the bytecode is 12 bytes long:
0x34 38 18 56 FD FD FD FD FD FD 5B 00
1. 0x34 - CALLVALUE
2. 0x38 - CODESIZE
3. 0x18 - XOR
4. 0x56 - JUMP
5-10. 0xFD - REVERT (6 times)
11. 0x5B - JUMPDEST
12. 0x00 - STOP
When the CODESIZE opcode executes at position 1, it counts all these bytes (including itself) and pushes 12 onto the stack.
Visual XOR Calculation
Let's visually walk through how we calculate 12 XOR x = 10:
Step 1: Convert to binary
Step 2: Solve for x using XOR properties
Step 3: Convert back to decimal
IF: 12 XOR x = 10
Then: 12 XOR 10 = 6
Therefore: x = 6
So we need to send 6 wei as CALLVALUE to make the JUMP go to position 10.
Understanding the Puzzle
Find X:
| Location | Opcode | Meaning | Operation | Stack After |
|---|---|---|---|---|
| 0 | CALLVALUE | Pushes the amount of ETH sent. We're going to call this 'x' b/c we don't know it yet. | x | [x] |
| 1 | CODESIZE | Pushes the size of the contract code | Push 12 | [12, x] |
| 2 | XOR | Performs bitwise XOR on top two stack items. (We need this to = 10 b/c our JUMPDEST is 10) | 12 XOR x = 10 | [10] |
| 3 | JUMP | Jumps to the position specified by the top stack value | Jump to 10 (0x0A) | [10] |
| 4 | REVERT | Reverts the transaction if we jump here | FAIL | [] |
| A | JUMPDEST | Our destination | SUCCESS! | [] |
| B | STOP | Halts execution successfully | - | [] |
Solution:
To solve this puzzle:
- We need CODESIZE XOR CALLVALUE = 10 (0x0A)
- CODESIZE is 12 bytes (0x0C)
- So we need: 12 XOR CALLVALUE = 10
- Using the properties of XOR: CALLVALUE = 12 XOR 10 = 6
- Send exactly 6 wei with the transaction
The key insight: For XOR operations, if A XOR B = C, then A XOR C = B. This property of XOR allows us to solve for CALLVALUE.