avatarSimon Busch

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

10254

Abstract

</div><div id="d45d"><pre><span class="hljs-symbol">121 </span>PUSH2 <span class="hljs-number">004</span>e</pre></div><div id="2e68"><pre><span class="hljs-symbol">124 </span>JUMP</pre></div><div id="baa0"><pre><span class="hljs-symbol">125 </span>JUMPDEST</pre></div><div id="c12a"><pre><span class="hljs-symbol">126 </span>PUSH1 <span class="hljs-number">40</span></pre></div><div id="4c39"><pre><span class="hljs-symbol">128 </span>MLOAD</pre></div><div id="3c11"><pre><span class="hljs-symbol">129 </span>PUSH2 <span class="hljs-number">0045</span></pre></div><div id="6b7b"><pre><span class="hljs-symbol">132 </span>SWAP2</pre></div><div id="0db8"><pre><span class="hljs-symbol">133 </span>SWAP1</pre></div><div id="eee2"><pre><span class="hljs-symbol">134 </span>PUSH2 <span class="hljs-number">0074</span></pre></div><div id="e940"><pre><span class="hljs-symbol">137 </span>JUMP</pre></div><div id="e23a"><pre><span class="hljs-symbol">138 </span>JUMPDEST</pre></div><div id="0ece"><pre><span class="hljs-symbol">139 </span>PUSH1 <span class="hljs-number">40</span></pre></div><div id="e3f4"><pre><span class="hljs-symbol">141 </span>MLOAD</pre></div><div id="a773"><pre><span class="hljs-symbol">142 </span>DUP1</pre></div><div id="2d73"><pre><span class="hljs-symbol">143 </span>SWAP2</pre></div><div id="69a9"><pre><span class="hljs-symbol">144 </span>SUB</pre></div><div id="a884"><pre><span class="hljs-symbol">145 </span>SWAP1</pre></div><div id="6781"><pre><span class="hljs-symbol">146 </span><span class="hljs-keyword">RETURN</span></pre></div><div id="694f"><pre><span class="hljs-symbol">147 </span>JUMPDEST</pre></div><h1 id="d10e">How does it work?</h1><p id="51ef">Each of these instructions is readable and the full list can be found here: <a href="https://www.evm.codes/">https://www.evm.codes/</a>. The first instructions can be read in the execution order. First, we push a value, then store it … It will be way clearer in the puzzles :-)</p><p id="1928">Important, these opcodes are compiled into <code>bytecode</code> that looks like this :</p><div id="86a9"><pre><span class="hljs-number">60806040526005600055600</span>a<span class="hljs-number">6001556001600260006101000</span>a<span class="hljs-number">81548160</span>ff<span class="hljs-number">02191690831515021790555034801561003557600080</span>fd<span class="hljs-number">5</span>b<span class="hljs-number">50610154806100456000396000</span>f<span class="hljs-number">3</span>fe<span class="hljs-number">608060405234801561001057600080</span>fd<span class="hljs-number">5</span>b<span class="hljs-number">506004361061002</span>b<span class="hljs-number">5760003560e01</span><span class="hljs-keyword">c</span><span class="hljs-number">80633</span>a<span class="hljs-number">2</span>f<span class="hljs-number">9</span>eb<span class="hljs-number">714610030575</span>b<span class="hljs-number">600080</span>fd<span class="hljs-number">5</span>b<span class="hljs-number">61003861004e565</span>b<span class="hljs-number">6040516100459190610074565</span>b<span class="hljs-number">60405180910390</span>f<span class="hljs-number">35</span>b<span class="hljs-number">6000600154600054610060919061008</span>f<span class="hljs-number">565</span>b<span class="hljs-number">905090565</span>b<span class="hljs-number">61006e816100</span>e<span class="hljs-number">5565</span>b<span class="hljs-number">82525050565</span>b<span class="hljs-number">60006020820190506100896000830184610065565</span>b<span class="hljs-number">92915050565</span>b<span class="hljs-number">600061009</span>a<span class="hljs-number">826100e5565</span>b<span class="hljs-number">91506100</span>a<span class="hljs-number">5836100e5565</span>b<span class="hljs-number">9250827</span>fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff<span class="hljs-number">038211156100</span>da<span class="hljs-number">576100</span>d<span class="hljs-number">96100</span>ef<span class="hljs-number">565</span>b<span class="hljs-number">5</span>b<span class="hljs-number">828201905092915050565</span>b<span class="hljs-number">6000819050919050565</span>b<span class="hljs-number">7</span>f<span class="hljs-number">4e487</span>b<span class="hljs-number">7100000000000000000000000000000000000000000000000000000000600052601160045260246000</span>fdfea<span class="hljs-number">26469706673582212203</span>fed<span class="hljs-number">942253</span>f<span class="hljs-number">5</span>a<span class="hljs-number">012</span><span class="hljs-keyword">c</span><span class="hljs-number">3422013</span><span class="hljs-keyword">c</span><span class="hljs-number">4601688071</span>a<span class="hljs-number">02</span>a<span class="hljs-number">565</span>ae<span class="hljs-number">8</span>eda<span class="hljs-number">725</span>cf<span class="hljs-number">0409</span>a<span class="hljs-number">454</span>bf<span class="hljs-number">364736</span>f<span class="hljs-number">6</span><span class="hljs-keyword">c</span><span class="hljs-number">63430008070033</span></pre></div><p id="8375">That you can read by decomposing it such as :</p><p id="4af4">6080 =&gt; <b>PUSH1 80</b></p><p id="969f">6040 =&gt; <b>PUSH1 40</b></p><p id="8361">…</p><h1 id="11b8">How to use EVM opcode playground</h1><figure id="3a6d"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*B8C6_mIorb9EirnZNdr56Q.png"><figcaption></figcaption></figure><p id="d5d2">To use the playground, you can go to this address:<a href="https://www.evm.codes/playground">https://www.evm.codes/playground</a></p><p id="fd2e">There are a few things to understand here :</p><ul><li>As you as see, I pasted the Bytecode of the above contract, but you can set it to Yul, Solidity, and Mnemonic.</li><li>You can pass it a <b>calldata</b> or a <b>value</b>.</li><li>Use the blue arrow on the top right to go through all opcodes, step by step, see the stack, memory, storage, and return value evolving as you go through the code.</li></ul><h1 id="98ab">Detailed solution</h1><p id="0a5c">Let’s dive deeper into all the puzzles. We are presented with a set of 10 puzzles and the goal of each of them is to reach the <b>JUMPDEST</b>.</p><p id="6e09">On <a href="https://www.evm.codes/playground">https://www.evm.codes/</a> we can reproduce these puzzles and place them around. The goal is to force us to make some research on the opcodes and understand what’s happening under the hood.</p><figure id="b081"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*B5EshtFeZdxOgoEhcsvWBg.png"><figcaption></figcaption></figure><h1 id="5d9c">Puzzle 1</h1><figure id="2045"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*7oSkXjs5yWPR14P_A-9BWw.png"><figcaption></figcaption></figure><p id="26bb">Let’s decompose it:</p><ul><li><b>CALLVALUE </b>opcode that gets the deposited value by the instruction/transaction [value we pass in wei in this case].</li><li>The <b>JUMP</b> opcode will grab the last value of the stack and jump to this destination on the code.</li></ul><p id="ff82">Once this is understood we quickly realize that if we want to reach <b>JUMPDEST</b>, position 08, the <b>CALLVALUE</b> has to be 8.</p><p id="28cf">NB: you can make your experimentation on the playground. The solution is <b>8 </b>wei<b>.</b></p><h1 id="7442">Puzzle 2</h1><figure id="c8ef"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*ProajDJR8PhMaifSfAddTA.png"><figcaption></figcaption></figure><p id="e10c">Let’s decompose this second puzzle :</p><ul><li><b>CALLVALUE</b> opcode again, so we know we will have to pass a value and it will be added on top of the stack.</li><li><b>CODESIZE</b> this opcode represents the size of the code we have here, so basically 10 ( 00–01–02–03 … 09 ).</li><li><b>SUB</b>, if we check on the documentation, the <b>SUB</b> opcode takes the value on top of the stack ( <b>CODESIZE</b> ) and subtracts the value right after it ( <b>CALLVALUE</b>).</li></ul><p id="16b7">Knowing that we want to reach <b>JUMPDEST</b> again,</p><p id="8bda">We simply need to solve the following: 10-X = <b>6</b></p><h1 id="78dd">Puzzle 3</h1><figure id="cb60"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*1PKID1XuWaesPrEHszeA3Q.png"><figcaption></figcaption></figure><p id="b57a">New puzzle, new opcodes! :)</p><p id="bed5">Moreover, if we pay attention to the prompt, we need to enter the <b>calldata</b> and not a value in this case!</p><ul><li><b>CALLDATASIZE</b> opcode takes a value in bytes size.</li></ul><p id="0dd0">To reach our <b>JUMPDEST</b> ( 04 )</p><p id="8c36">We need to convert 04 to bytes which are <b>0xFFFFFFFF</b></p><p id="f3db"><b>0x</b> — define bytes</p><p id="2ebd"><b>4 x FF</b> — represent 4 bytes</p><h1 id="d65e">Puzzle 4</h1><figure id="f190"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*iNqfjeoJuYboMHBOeu6tPw.png"><figcaption></figcaption></figure><ul><li><b>CALLVALUE</b> — we know we will have to pass a value here</li><li><b>CODESIZE</b> — so here we have numeric until 9 then it goes 0A 0B so the total code size is <b>0C &lt;-&gt; 12 bytes</b></li><li><b>XOR — </b>This one is a bit tricky, so keep in mind that in the stack we need have “x” ( To be found).</li></ul><p id="736f">The Stack is a LIFO queue, so when the <b>XOR </b>will be applied it would be like this: <b>XOR</b>(CODESIZE, CALLVALUE)</p><p id="7aab">We know that the result of XOR(12, CALLVALUE ) must be equal to 10 — JUMPDEST</p><p id="e3f0">Therefore, the result is <b>6</b> :-)</p><h1 id="9a8a">Puzzle 5</h1><figure id="43ff"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*vzkDNlXwM3KFFdw_hw7Kjg.png"><figcaption></figcaption></figure><ul><li>Again we have to pass a <b>CALLVALUE</b></li><li><b>DUP1</b> — Duplicate the first item in the stack ( here, CALLVALUE )</li><li><b>MUL</b> — multiply the last 2 items on the stack → CALLVALUE * CALLVALUE</li><li><b>PUSH2</b> <b>0100</b> — Place 2 bytes item on the stack</li><li><b>EQ</b> — will check the equality of the last 2 items in the stack (the result of MUL == 100 ? ) return 1 if true, 0 if false</li><li><b>PUSH1</b> <b>0C</b> — Place 1byte item on the stack which is 0C in this case</li></ul><p id="6cdf">Therefore, we need to find a value * value == 0x0100</p><p id="6c64">! 0x0100 in decim # Options al == 256</p><p id="8ad7">So 16 * 16 = <b>256</b> :-)</p><h1 id="3eda">Puzzle 6</h1><figure id="0928"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*twO4XCroGY88HmLXrlJP-g.png"><figcaption></figcaption></figure><p id="de26">In this puzzle, the first instruction is a <b>PUSH1</b> with a 0 value.</p><p id="a0ea">Then we have a new opcode: <b>CALLDATALOAD</b> which will take the last value in the stack ( 0 ) as an index. So we are reading the calldata with an offset of 0, so an entire 32 bytes of data.</p><p id="f5c6">So we know the offset index is a value of 0 that we need inside of calldata that will jump us a position 0A ( <b>JUMPDEST</b>)</p><p id="0a53">The solution is: <b>0x000000000000000000000000000000000000000000000000000000000000000A</b></p><h1 id="3e80">Puzzle 7</h1><figure id="55de"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*gEP4pw6M6a-_uueP4nwalg.png"><figcaption></figcaption></figure><p id="0617">See the breakdown:</p><ul><li><b>CALLDATASIZE</b>: Get the size of input data in the current environment</li><li><b>PUSH1 00</b>: Push 00 in the first position of the stack</li><li><b>DUP1</b>: will duplicate the 1st stack item ( 00 )</li><li><b>CALLDATACOPY</b>: pops 3 values from the stack and copies the calldata value from the transaction data to memory:</li></ul><p id="9d9b"><i>destOffset</i> ( 0 ) <i>offset</i> ( 0 ) <i>size</i> (CALLDATASIZE)</p><p id="5a03">Will return bytes in memory</p><ul><li><b>CALLDATASIZE</b>: same as above</li><li><b>PUSH1</b> 00</li><li><b>PUSH1</b> 00</li><li><b>CREATE</b>: pops 3 values from the stack. This will actually deploy a new contract and returns the address of the deployed contract that is pushed to the stack.</li></ul><ol><li><i>value — </i>value in wei to send to the new contract address</li></ol><p id="07ee"><i>2. offset — </i>byte offset from where you want to start to copy the new contract’s code from the memory</p><p id="9238"><i>3. size — </i>byte size to copy ( size of the initialization code )</p><p id="fe49">Returns the address of the deployed contract, if 0, failed</p><ul><li><b>EXTCODESIZE</b>: get the size in. bytes of the deployed contract</li></ul><p id="33e2">Here we need to contract size code to be 1.</p><ul><li><b>EQ</b>: equality comparison, return 1 if true, 0 if false.</li><li><b>PUSH1 13</b></li><li><b>JUMPI</b></li></ul><p id="a778">Note on Create:</p><p id="e747">The create opcode gets executed in the transaction, returning a copy of the runtime code.</p><p id="393a">⚠️ constructor is part of the creation code, but NOT the runtime code</p><p id="55b3">The <b>runtime byte code</b> is the part of the code saved in the blockchain as the contract <b>runtime code</b>.</p><p id="ab76">When the <b>CREATE</b> opcode is executed, only the code returned by the <b>RETURN</b> opcode will be the runtime code that will be executed in the future once the contract is called.</p><p id="253f">Here is what we want <b>600060005360016000F3</b></p><p id="2a37">Will return 1-byte code.</p><h1 id="1154">Puzzle 8</h1><figure id="57a9"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*1-wbpNRM1DhoXvxsCWynTg.png"><figcaption></figcaption></figure><p id="70f1">We can see that the first chunk of code is the same as puzzle 7 so I won’t go through it.</p><figure id="44ed"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*pNThha8ZzF2h8lFwEl1wPg.png"><figcaption></figcaption></figure><p id="4107">Let’s look a the second big chunk:</p><figure id="b153"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*_rLgIlTdLzy7wmBua_KjUw.png"><figcaption></figcaption></figure><ul><li><b>CALL</b> opcode creates a new sub-context and executes the code present in the external account. It will also pop <b>7 </b>elements from the stack.</li></ul><ol><li><i>Gas</i>: the amount of gas to send to the sub-context to execute</li><li><i>Address</i>: an account in which context to execute</li><li><i>Value</i>: value in wei to send</li><li><i>ArgsOffset</i>: byte offset in the memory in bytes [Call data of the sub context]</li><li><i>ArgSize</i>: byte size to copy [Byte size of the call data]</li><li><i>RetOffset</i>: byte offset in the memory in bytes, where to store the return data of the subcontext.</li><li><i>RetSize</i>: byte size to copy</li></ol><ul><li><i>SWAP5</i>: this opcode will swap the opcode in position 0 with the one in position 5</li></ul><p id="f185">At this moment here is where we stand:</p><p id="e735"><code>CALL(ALL_THE_GAS_AVAILABLE,ADDRESS_FROM_CREATE,0,0,0,0,0)</code></p><p id="472e">Will return 0 if the call is reverted, otherwise 1.</p><p id="6155">Then there is the last chunk:</p><figure id="b7c2"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*L8z4bsv9vA2aChwHmTR2Mw.png"><figcaption></figcaption></figure><p id="fd6a">So basically, we need to make the call revert to having 0.</p><p id="b495">Then <b>PUSH1 00</b></p><div id="92e5"><pre>EQ <span class="hljs-number">0</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span> <span class="hljs-operator">=</span>&gt; <span class="hljs-number">1</span></pre></div><p id="a14e">Here is an example that would revert: <b>0x60FD60005360016000F3</b></p><h1 id="240d">Puzzle 9</h1><figure id="3e71"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*nO1bEZ-ye_MBAorQv3RVyg.png"><figcaption></figcaption></figure><p id="8627">So for this puzzle, the interesting point is that we will have to pass a <b>value in wei </b>and a <b>calldata</b> for this level.</p><p id="2a0b">Here, we have a new opcode: <b>LT </b>it pops 2 values (a,b) from the stack and pushes the results.</p><div id="72ba"><pre>a &lt; <span class="hljs-function"><span class="hljs-params">b</span> =&gt;</span> <span class="hljs-number">1</span> </pre></div><div id="20a9"><pre>a &gt; <span class="hljs-function"><span class="hljs-params">b</span> =&gt;</span> <span class="hljs-number">0</span></pre></div><figure id="6088"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*woa6Fu_h63zfGUlvlSyUlw.png"><figcaption></figcaption></figure><p id="9e88">In the first chunk of code, we need to make the <b>CALLDATASIZE</b> greater or equal to 3.</p><figure id="19f4"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*8s7IqZgXpN0hoBGB-qCY-Q.png"><figcaption></figcaption></figure><p id="4838">Then here, we have the <b>MUL</b>(<b>CALLDATASIZE</b>, <b>CALLVALUE</b>) = 8</p><p id="249f">So that could be either:</p><div id="594a"><pre><span class="hljs-attribute">CALLDATASIZE</span> <span class="hljs-operator">=</span> <span class="hljs-number">4</span> ( <span class="hljs-number">0</span>xFFFFFFFF )</pre></div><div id="2a28"><pre><span class="hljs-attribute">CALLVALUE</span> <span class="hljs-operator">=</span> <span class="hljs-number">2</span></pre></div><p id="b151">OR</p><div id="2df3"><pre><span class="hljs-attribute">CALLDATASIZE</span> <span class="hljs-operator">=</span> <span class="hljs-number">8</span> ( <span class="hljs-number">0</span>xFFFFFFFFFFFFFFFF )</pre></div><div id="319b"><pre><span class="hljs-attribute">CALLVALUE</span><span class="hljs-operator">=</span><span class="hljs-number">1</span> </pre></div><h1 id="76f2">Puzzle 10</h1><figure id="54c0"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*VIjlNCGIYyowE9WZXZ5XKQ.png"><figcaption></figcaption></figure><p id="be11">Here once again, we will have to pass a value &amp;&amp; calldata.</p><p id="fa79">Let’s break it down:</p><figure id="87cc"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*fVJY6skdXCFD5tdXekdE_Q.png"><figcaption></figcaption></figure><p id="4cab">There is a new opcode here, GT, pop 2 values from the stack and push the result of a &gt; b.</p><p id="82ad">The code size in our case is <b>0x1b</b> ( 27 in decimal )</p><div id="6ef1"><pre>a &gt; <span class="hljs-function"><span class="hljs-params">b</span> =&gt;</span> <span class="hljs-number">1</span></pre></div><div id="bfe9"><pre>a &lt; <span class="hljs-function"><span class="hljs-params">b</span> =&gt;</span> <span class="hljs-number">0</span></pre></div><p id="577c">So:</p><div id="33d0"><pre><span class="hljs-function"><span class="hljs-title">GT</span><span class="hljs-params">(CODESIZE,CALLVALUE)</span></span> </pre></div><div id="056c"><pre><span class="hljs-function"><span class="hljs-title">GT</span><span class="hljs-params">(<span class="hljs-number">27</span>,CALLVALUE)</span></span></pre></div><div id="974b"><pre><span class="hljs-attribute">CALLVALUE</span> must be ≤ <span class="hljs-number">27</span> ( <span class="hljs-number">1</span>b in hex )</pre></div><figure id="c861"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*u27wFoAW7zUkUm78VSIqig.png"><figcaption></figcaption></figure><p id="5c8b">Here we have some new opcodes as well:</p><ul><li><b>MOD</b>: pop2 values from the stack and push back to the stack the result of a % b</li><li><b>ISZERO</b>: pop a value A from the stack and push the result of a === 0 to the stack</li></ul><p id="1e11"><b>ISZERO</b>( CALLDATA SIZE % 3 ) ( 3 from PUSH2 0003 )</p><p id="b001"><b>ADD</b>(CALLVALUE, 0A) = 0x19 ( 25 in decimal) <b>the destination</b></p><p id="8bd7">Based on this, ISZERO results must be equal to 1</p><p id="f45a">This is a possible answer:</p><p id="bccd"><b>CALLVALUE</b>: 15</p><p id="1786"><b>CALLDATA</b>: 0xFFFFFFFFFFFF</p><p id="b208"><b>NB</b>: You can find my solutions here: <a href="https://github.com/Simon-Busch/evm-puzzles">https://github.com/Simon-Busch/evm-puzzles</a></p><h1 id="9f28">References:</h1><p id="d721"><a href="https://www.evm.codes/playground">https://www.evm.codes/playground</a></p><p id="e514"><a href="https://blog.openzeppelin.com/deconstructing-a-solidity-contract-part-i-introduction-832efd2d7737/">https://blog.openzeppelin.com/deconstructing-a-solidity-contract-part-i-introduction-832efd2d7737/</a></p><p id="d042"><a href="https://blog.openzeppelin.com/deconstructing-a-solidity-contract-part-ii-creation-vs-runtime-6b9d60ecb44c/">https://blog.openzeppelin.com/deconstructing-a-solidity-contract-part-ii-creation-vs-runtime-6b9d60ecb44c/</a></p></article></body>

EVM Puzzle full solution

https://unsplash.com/es/@karlahrnndz

As you dive into Solidity, it’s quite important to understand what opcodes are and how to use them. I strongly recommend checking this link :

https://www.evm.codes/?fork=grayGlacier

What are the EVM puzzles?

This is a set of 10 puzzles created by Franco Victorio (https://github.com/fvictorio) to get us more comfortable with opcodes. You can find it in the below link. Follow the instruction to get started: https://github.com/fvictorio/evm-puzzles

What are opcodes?

You can understand “opcode” as low-level human-readable instructions to program. Let’s take a look at the following contract:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract Sum {
  uint256 a = 5;
  uint256 b = 10;
  bool isItTrue = true;
  function addNumbers() public view returns (uint256) {
    return a+b;
  }
}

Here would be the resulting opcode:

000 PUSH1 80
002 PUSH1 40
004 MSTORE
005 PUSH1 05
007 PUSH1 00
009 SSTORE
010 PUSH1 0a
012 PUSH1 01
014 SSTORE
015 PUSH1 01
017 PUSH1 02
019 PUSH1 00
021 PUSH2 0100
024 EXP
025 DUP2
026 SLOAD
027 DUP2
028 PUSH1 ff
030 MUL
031 NOT
032 AND
033 SWAP1
034 DUP4
035 ISZERO
036 ISZERO
037 MUL
038 OR
039 SWAP1
040 SSTORE
041 POP
042 CALLVALUE
043 DUP1
044 ISZERO
045 PUSH2 0035
048 JUMPI
049 PUSH1 00
051 DUP1
052 REVERT
053 JUMPDEST
054 POP
055 PUSH2 0154
058 DUP1
059 PUSH2 0045
062 PUSH1 00
064 CODECOPY
065 PUSH1 00
067 RETURN
068 INVALID
069 PUSH1 80
071 PUSH1 40
073 MSTORE
074 CALLVALUE
075 DUP1
076 ISZERO
077 PUSH2 0010
080 JUMPI
081 PUSH1 00
083 DUP1
084 REVERT
085 JUMPDEST
086 POP
087 PUSH1 04
089 CALLDATASIZE
090 LT
091 PUSH2 002b
094 JUMPI
095 PUSH1 00
097 CALLDATALOAD
098 PUSH1 e0
100 SHR
101 DUP1
102 PUSH4 3a2f9eb7
107 EQ
108 PUSH2 0030
111 JUMPI
112 JUMPDEST
113 PUSH1 00
115 DUP1
116 REVERT
117 JUMPDEST
118 PUSH2 0038
121 PUSH2 004e
124 JUMP
125 JUMPDEST
126 PUSH1 40
128 MLOAD
129 PUSH2 0045
132 SWAP2
133 SWAP1
134 PUSH2 0074
137 JUMP
138 JUMPDEST
139 PUSH1 40
141 MLOAD
142 DUP1
143 SWAP2
144 SUB
145 SWAP1
146 RETURN
147 JUMPDEST

How does it work?

Each of these instructions is readable and the full list can be found here: https://www.evm.codes/. The first instructions can be read in the execution order. First, we push a value, then store it … It will be way clearer in the puzzles :-)

Important, these opcodes are compiled into bytecode that looks like this :

60806040526005600055600a6001556001600260006101000a81548160ff02191690831515021790555034801561003557600080fd5b50610154806100456000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80633a2f9eb714610030575b600080fd5b61003861004e565b6040516100459190610074565b60405180910390f35b6000600154600054610060919061008f565b905090565b61006e816100e5565b82525050565b60006020820190506100896000830184610065565b92915050565b600061009a826100e5565b91506100a5836100e5565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156100da576100d96100ef565b5b828201905092915050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fdfea26469706673582212203fed942253f5a012c3422013c4601688071a02a565ae8eda725cf0409a454bf364736f6c63430008070033

That you can read by decomposing it such as :

6080 => PUSH1 80

6040 => PUSH1 40

How to use EVM opcode playground

To use the playground, you can go to this address:https://www.evm.codes/playground

There are a few things to understand here :

  • As you as see, I pasted the Bytecode of the above contract, but you can set it to Yul, Solidity, and Mnemonic.
  • You can pass it a calldata or a value.
  • Use the blue arrow on the top right to go through all opcodes, step by step, see the stack, memory, storage, and return value evolving as you go through the code.

Detailed solution

Let’s dive deeper into all the puzzles. We are presented with a set of 10 puzzles and the goal of each of them is to reach the JUMPDEST.

On https://www.evm.codes/ we can reproduce these puzzles and place them around. The goal is to force us to make some research on the opcodes and understand what’s happening under the hood.

Puzzle 1

Let’s decompose it:

  • CALLVALUE opcode that gets the deposited value by the instruction/transaction [value we pass in wei in this case].
  • The JUMP opcode will grab the last value of the stack and jump to this destination on the code.

Once this is understood we quickly realize that if we want to reach JUMPDEST, position 08, the CALLVALUE has to be 8.

NB: you can make your experimentation on the playground. The solution is 8 wei.

Puzzle 2

Let’s decompose this second puzzle :

  • CALLVALUE opcode again, so we know we will have to pass a value and it will be added on top of the stack.
  • CODESIZE this opcode represents the size of the code we have here, so basically 10 ( 00–01–02–03 … 09 ).
  • SUB, if we check on the documentation, the SUB opcode takes the value on top of the stack ( CODESIZE ) and subtracts the value right after it ( CALLVALUE).

Knowing that we want to reach JUMPDEST again,

We simply need to solve the following: 10-X = 6

Puzzle 3

New puzzle, new opcodes! :)

Moreover, if we pay attention to the prompt, we need to enter the calldata and not a value in this case!

  • CALLDATASIZE opcode takes a value in bytes size.

To reach our JUMPDEST ( 04 )

We need to convert 04 to bytes which are 0xFFFFFFFF

0x — define bytes

4 x FF — represent 4 bytes

Puzzle 4

  • CALLVALUE — we know we will have to pass a value here
  • CODESIZE — so here we have numeric until 9 then it goes 0A 0B so the total code size is 0C <-> 12 bytes
  • XOR — This one is a bit tricky, so keep in mind that in the stack we need have “x” ( To be found).

The Stack is a LIFO queue, so when the XOR will be applied it would be like this: XOR(CODESIZE, CALLVALUE)

We know that the result of XOR(12, CALLVALUE ) must be equal to 10 — JUMPDEST

Therefore, the result is 6 :-)

Puzzle 5

  • Again we have to pass a CALLVALUE
  • DUP1 — Duplicate the first item in the stack ( here, CALLVALUE )
  • MUL — multiply the last 2 items on the stack → CALLVALUE * CALLVALUE
  • PUSH2 0100 — Place 2 bytes item on the stack
  • EQ — will check the equality of the last 2 items in the stack (the result of MUL == 100 ? ) return 1 if true, 0 if false
  • PUSH1 0C — Place 1byte item on the stack which is 0C in this case

Therefore, we need to find a value * value == 0x0100

! 0x0100 in decimal == 256

So 16 * 16 = 256 :-)

Puzzle 6

In this puzzle, the first instruction is a PUSH1 with a 0 value.

Then we have a new opcode: CALLDATALOAD which will take the last value in the stack ( 0 ) as an index. So we are reading the calldata with an offset of 0, so an entire 32 bytes of data.

So we know the offset index is a value of 0 that we need inside of calldata that will jump us a position 0A ( JUMPDEST)

The solution is: 0x000000000000000000000000000000000000000000000000000000000000000A

Puzzle 7

See the breakdown:

  • CALLDATASIZE: Get the size of input data in the current environment
  • PUSH1 00: Push 00 in the first position of the stack
  • DUP1: will duplicate the 1st stack item ( 00 )
  • CALLDATACOPY: pops 3 values from the stack and copies the calldata value from the transaction data to memory:

destOffset ( 0 ) offset ( 0 ) size (CALLDATASIZE)

Will return bytes in memory

  • CALLDATASIZE: same as above
  • PUSH1 00
  • PUSH1 00
  • CREATE: pops 3 values from the stack. This will actually deploy a new contract and returns the address of the deployed contract that is pushed to the stack.
  1. value — value in wei to send to the new contract address

2. offset — byte offset from where you want to start to copy the new contract’s code from the memory

3. size — byte size to copy ( size of the initialization code )

Returns the address of the deployed contract, if 0, failed

  • EXTCODESIZE: get the size in. bytes of the deployed contract

Here we need to contract size code to be 1.

  • EQ: equality comparison, return 1 if true, 0 if false.
  • PUSH1 13
  • JUMPI

Note on Create:

The create opcode gets executed in the transaction, returning a copy of the runtime code.

⚠️ constructor is part of the creation code, but NOT the runtime code

The runtime byte code is the part of the code saved in the blockchain as the contract runtime code.

When the CREATE opcode is executed, only the code returned by the RETURN opcode will be the runtime code that will be executed in the future once the contract is called.

Here is what we want 600060005360016000F3

Will return 1-byte code.

Puzzle 8

We can see that the first chunk of code is the same as puzzle 7 so I won’t go through it.

Let’s look a the second big chunk:

  • CALL opcode creates a new sub-context and executes the code present in the external account. It will also pop 7 elements from the stack.
  1. Gas: the amount of gas to send to the sub-context to execute
  2. Address: an account in which context to execute
  3. Value: value in wei to send
  4. ArgsOffset: byte offset in the memory in bytes [Call data of the sub context]
  5. ArgSize: byte size to copy [Byte size of the call data]
  6. RetOffset: byte offset in the memory in bytes, where to store the return data of the subcontext.
  7. RetSize: byte size to copy
  • SWAP5: this opcode will swap the opcode in position 0 with the one in position 5

At this moment here is where we stand:

CALL(ALL_THE_GAS_AVAILABLE,ADDRESS_FROM_CREATE,0,0,0,0,0)

Will return 0 if the call is reverted, otherwise 1.

Then there is the last chunk:

So basically, we need to make the call revert to having 0.

Then PUSH1 00

EQ 0 == 0 => 1

Here is an example that would revert: 0x60FD60005360016000F3

Puzzle 9

So for this puzzle, the interesting point is that we will have to pass a value in wei and a calldata for this level.

Here, we have a new opcode: LT it pops 2 values (a,b) from the stack and pushes the results.

a < b => 1 
a > b => 0

In the first chunk of code, we need to make the CALLDATASIZE greater or equal to 3.

Then here, we have the MUL(CALLDATASIZE, CALLVALUE) = 8

So that could be either:

CALLDATASIZE = 4 ( 0xFFFFFFFF )
CALLVALUE = 2

OR

CALLDATASIZE = 8 ( 0xFFFFFFFFFFFFFFFF )
CALLVALUE=1 

Puzzle 10

Here once again, we will have to pass a value && calldata.

Let’s break it down:

There is a new opcode here, GT, pop 2 values from the stack and push the result of a > b.

The code size in our case is 0x1b ( 27 in decimal )

a > b => 1
a < b => 0

So:

GT(CODESIZE,CALLVALUE) 
GT(27,CALLVALUE)
CALLVALUE must be ≤ 27 ( 1b in hex )

Here we have some new opcodes as well:

  • MOD: pop2 values from the stack and push back to the stack the result of a % b
  • ISZERO: pop a value A from the stack and push the result of a === 0 to the stack

ISZERO( CALLDATA SIZE % 3 ) ( 3 from PUSH2 0003 )

ADD(CALLVALUE, 0A) = 0x19 ( 25 in decimal) the destination

Based on this, ISZERO results must be equal to 1

This is a possible answer:

CALLVALUE: 15

CALLDATA: 0xFFFFFFFFFFFF

NB: You can find my solutions here: https://github.com/Simon-Busch/evm-puzzles

References:

https://www.evm.codes/playground

https://blog.openzeppelin.com/deconstructing-a-solidity-contract-part-i-introduction-832efd2d7737/

https://blog.openzeppelin.com/deconstructing-a-solidity-contract-part-ii-creation-vs-runtime-6b9d60ecb44c/

Solidity
Evm
Bytecode
Solidity Tutorial
Puzzle Game
Recommended from ReadMedium