March 25, 2024

EVM Puzzle 7 & 8: Advanced Memory Operations

15 min readDifficulty: Expert

Introduction

In this post, we'll tackle Puzzles 7 and 8, which introduce more advanced EVM concepts including memory operations and complex data manipulation. These puzzles will test your understanding of how the EVM handles data storage and retrieval.

Puzzle 7

Copy and Paste → 36600080373660006000F03B600114601357FD5B00 into the EVM Playground. Then click RUN.

# Puzzle 7
# Goal:
# Make it to the JUMPDEST at position 13
#
# The contract's code is:
#
# [00]    CALLDATASIZE    # Get size of calldata
# [01]    PUSH1 00        # Push 0 onto stack
# [03]    DUP1            # Duplicate 0
# [04]    CALLDATACOPY    # Copy calldata to memory
# [05]    CALLDATASIZE    # Get size of calldata again
# [06]    PUSH1 00        # Push 0 onto stack
# [08]    PUSH1 00        # Push 0 onto stack
# [0A]    CREATE          # Create new contract
# [0B]    EXTCODESIZE     # Get size of created contract
# [0C]    PUSH1 01        # Push 1 onto stack
# [0E]    EQ              # Check if sizes are equal
# [0F]    PUSH1 13        # Push 19 (0x13) onto stack
# [11]    JUMPI           # Conditional jump
# [12]    REVERT          # Revert if jump fails
# [13]    JUMPDEST        # Success destination
# [14]    STOP            # Halt execution
#
# Bytecode: 0x36600080373660006000F03B600114601357FD5B00

Understanding the Puzzle

This puzzle introduces contract creation and code size comparison. Let's break down what's happening:

  1. The code first gets the size of our calldata
  2. It copies our calldata to memory starting at position 0
  3. It then tries to create a new contract using that calldata as the contract's code
  4. Finally, it checks if the new contract's code size equals 1
LocationOpcodeMeaningStack After
00CALLDATASIZEGet size of calldata[size]
01PUSH1 00Push 0 onto stack[0, size]
03DUP1Duplicate top stack item[0, 0, size]
04CALLDATACOPYCopy calldata to memory[]
05CALLDATASIZEGet size of calldata[size]
06PUSH1 00Push 0 onto stack[0, size]
08PUSH1 00Push 0 onto stack[0, 0, size]
0ACREATECreate new contract[addr]
0BEXTCODESIZEGet contract size[size]
0CPUSH1 01Push 1 onto stack[1, size]
0EEQCheck equality[result]
0FPUSH1 13Push jump dest[0x13, result]
11JUMPIConditional jump[]

Puzzle 8:

CREATE and CALL

Puzzle 8 combines contract creation with contract interaction, requiring us to create a contract and then call it successfully.

Copy and Paste → 36600080373660006000F0600080808080945AF1600014601B57FD5B00 into the EVM Playground. Then click RUN.

# Puzzle 8
# Goal:
# Create a contract and call it successfully

00      36      CALLDATASIZE
01      60      00      PUSH1 00
03      80      DUP1
04      37      CALLDATACOPY
05      36      CALLDATASIZE
06      60      00      PUSH1 00
08      60      00      PUSH1 00
0A      F0      CREATE
0B      60      00      PUSH1 00
0D      80      DUP1
0E      80      DUP1
0F      80      DUP1
10      80      DUP1
11      86      SWAP5
12      5A      GAS
13      F1      CALL
14      60      00      PUSH1 00
16      14      EQ
17      60      1B      PUSH1 1B
19      57      JUMPI
1A      FD      REVERT
1B      5B      JUMPDEST
1C      00      STOP

# Bytecode: 36600080373660006000F0600080808080945AF1600014601B57FD5B00

Understanding the Puzzle

This puzzle introduces two new opcodes: CREATE and CALL. Let's break down what each does:

LocationOpcodeMeaningOperationStack After
00CALLDATASIZEGet size of calldataPush size of calldata[size]
01PUSH1 00Push 0 onto stackPush 0[0, size]
03DUP1Duplicate the top stack itemDup size[size, size]
04CALLDATACOPYCopy calldata to memoryCopy size bytes from calldata to memory[]
05CALLDATASIZEGet size of calldataPush size[size]
06PUSH1 00Push 0 onto stackPush 0[0, size]
08PUSH1 00Push 0 onto stackPush 0[0, 0, size]
0ACREATECreate new contractCreate contract with size bytes from memory[address]
0BPUSH1 00Push 0 onto stackPush 0[0, address]
0DDUP1Duplicate top stack itemDup 0[0, 0, address]
0EDUP1Duplicate top stack itemDup 0[0, 0, 0, address]
0FDUP1Duplicate top stack itemDup 0[0, 0, 0, 0, address]
10DUP1Duplicate top stack itemDup 0[0, 0, 0, 0, 0, address]
11SWAP5Swap top with 6th itemSwap address to top[address, 0, 0, 0, 0, 0]
12GASGet remaining gasPush gas[gas, address, 0, 0, 0, 0]
13CALLCall contractCall contract with 0 value[success]
14PUSH1 00Push 0 onto stackPush 0[0, success]
16EQCheck if equalCheck if success = 0[result]
17PUSH1 1BPush 27 onto stackPush 27[27, result]
19JUMPIConditional jumpJump if result is 1[]
1AREVERTRevert transactionFAIL[]
1BJUMPDESTValid jump destinationSUCCESS![]
1CSTOPStop execution-[]

Key Takeaways

  • Contract creation can be done dynamically using calldata
  • The CREATE opcode uses memory contents as the new contract's code
  • Contract calls can be used to test contract behavior
  • Return values from contract calls can be checked

Common Pitfalls

  • Forgetting that created contract code must be valid EVM bytecode
  • Not accounting for gas costs in contract creation and calls
  • Creating contracts that revert instead of returning successfully
  • Misunderstanding how the CALL opcode affects the stack

Practice Exercises

  1. Can you create a contract that returns a specific non-zero value?
  2. Try creating a contract that performs a calculation before returning
  3. Experiment with different ways to make contract calls fail or succeed