├── .gitignore ├── .gitmodules ├── foundry.toml ├── .github └── workflows │ └── test.yml ├── disassembly ├── opcode-stepping │ └── 2022-05-complected │ │ └── basic-set │ │ ├── DebuggerStepping.t.sol │ │ └── basic-set-report.md ├── deployed_bytecode.txt ├── README.md └── dmap-bytecode-disassembly.md ├── src └── test │ └── Dmap.t.sol └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | cache/ 2 | out/ 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | path = lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [default] 2 | src = 'src' 3 | out = 'out' 4 | libs = ['lib'] 5 | fuzz_runs = 250 6 | # See more config options https://github.com/foundry-rs/foundry/tree/master/config -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: workflow_dispatch 4 | 5 | env: 6 | FOUNDRY_PROFILE: ci 7 | 8 | jobs: 9 | check: 10 | strategy: 11 | fail-fast: true 12 | 13 | name: Foundry project 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | with: 18 | submodules: recursive 19 | 20 | - name: Install Foundry 21 | uses: foundry-rs/foundry-toolchain@v1 22 | with: 23 | version: nightly 24 | 25 | - name: Run Forge build 26 | run: | 27 | forge --version 28 | forge build --sizes 29 | id: build 30 | 31 | - name: Run Forge tests 32 | run: | 33 | forge test -vvv 34 | id: test 35 | -------------------------------------------------------------------------------- /disassembly/opcode-stepping/2022-05-complected/basic-set/DebuggerStepping.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.14; 3 | 4 | import "forge-std/Test.sol"; 5 | 6 | contract DebuggerStepping is Test { 7 | // Address of publicly deployed dmap 8 | address dmap_address = address(0x90949c9937A11BA943C7A72C3FA073a37E3FdD96); 9 | // selector is ignored by contract 10 | bytes4 SEL = bytes4(0x00000000); 11 | 12 | function testSetBasic() public { 13 | bytes32 name = bytes32("1"); 14 | bytes32 meta = bytes32("2"); 15 | bytes32 data = bytes32("3"); 16 | setBasic(name, meta, data); 17 | } 18 | 19 | function setBasic(bytes32 name, bytes32 meta, bytes32 data) public { 20 | bytes memory call_data = abi.encodePacked(SEL, name, meta, data); 21 | (bool ok1, ) = dmap_address.call(call_data); 22 | require(ok1); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/Dmap.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.14; 3 | 4 | import "forge-std/Test.sol"; 5 | 6 | contract ContractTest is Test { 7 | 8 | // Address of publicly deployed dmap 9 | address dmap_address = address(0x90949c9937A11BA943C7A72C3FA073a37E3FdD96); 10 | // selector is ignored by contract 11 | bytes4 SEL = bytes4(0x00000000); 12 | 13 | /// Tests set and get with fixed values (no storage inspection) 14 | function testBasicSetAndGet() public { 15 | bytes32 name = bytes32("name"); 16 | bytes32 meta = bytes32("meta"); 17 | bytes32 data = bytes32("data1"); 18 | setAndGetTestBasic(name, meta, data); 19 | } 20 | 21 | /// Test set and get with fuzzed values (no storage inspection) 22 | function testBasicSetAndGetFuzzed(bytes32 name, bytes32 meta, bytes32 data) public { 23 | setAndGetTestBasic(name, meta, data); 24 | } 25 | 26 | /// Test set and get with fixed values (storage inspection) 27 | function testSetAndGetStorageInspection() public { 28 | bytes32 name = bytes32("name"); 29 | bytes32 meta = bytes32("meta"); 30 | bytes32 data = bytes32("data1"); 31 | setAndGetTestStorageInspection(name, meta, data); 32 | } 33 | 34 | /// Test that locked meta cannot be written to twice, and that data doesn't change 35 | function testLockedSlot(bytes32 name, bytes32 meta, bytes32 data) public { 36 | bool locked; 37 | assembly { 38 | locked := and(meta, 0x1) 39 | } 40 | vm.assume(locked); 41 | 42 | // set once 43 | (bool ok1, ) = dmap_address.call(abi.encodePacked(SEL,name,meta,data)); 44 | require(ok1); 45 | 46 | // try to set again 47 | (bool ok2, ) = dmap_address.call(abi.encodePacked(SEL,name,meta,bytes32("other data"))); 48 | 49 | // expect a revert 50 | require(!ok2); 51 | 52 | // Check that data has not changed 53 | bytes32 data_slot; 54 | address testAddress = address(this); 55 | assembly { 56 | let ptr := mload(0x40) 57 | mstore(0x0, testAddress) 58 | mstore(0x20, name) 59 | mstore(ptr, keccak256(0x0, 0x40)) 60 | data_slot := add(1, mload(ptr)) 61 | } 62 | (bool ok3, bytes memory get_return) = dmap_address.call(abi.encodePacked(SEL, data_slot)); 63 | require(ok3); // should still be able to read 64 | bytes32 stored_data = abi.decode(get_return, (bytes32)); 65 | assertEq(data, stored_data); 66 | // TODO: Check meta value too 67 | 68 | } 69 | 70 | /// Test set and get with fuzzed values (storage inspection) 71 | function testSetAndGetStorageInspectionFuzzed(bytes32 name, bytes32 meta, bytes32 data) public { 72 | setAndGetTestStorageInspection(name, meta, data); 73 | } 74 | 75 | /// Tests the dmap object using it's 'get' and 'set' fallback impl 76 | function setAndGetTestBasic(bytes32 name, bytes32 meta, bytes32 data) public { 77 | bytes memory call_data = abi.encodePacked(SEL,name,meta,data); 78 | (bool ok1, ) = dmap_address.call(call_data); 79 | require(ok1); 80 | 81 | // test get meta 82 | bytes32 meta_slot = keccak256(abi.encode(address(this), name)); 83 | (bool ok2, bytes memory ret2) = dmap_address.call(abi.encodePacked(SEL, meta_slot)); 84 | require(ok2); 85 | (bytes32 returned_meta, bytes32 returned_data) = abi.decode(ret2, (bytes32, bytes32)); 86 | assertEq(meta, returned_meta); 87 | assertEq(data, returned_data); 88 | } 89 | 90 | // Tests the dmap object using it's fallback 'set' by using storage inspection 91 | function setAndGetTestStorageInspection(bytes32 name, bytes32 meta, bytes32 data) public { 92 | bytes memory call_data = abi.encodePacked(SEL,name,meta,data); 93 | (bool ok1, ) = dmap_address.call(call_data); 94 | require(ok1); 95 | 96 | bytes32 meta_slot = keccak256(abi.encode(address(this), name)); 97 | bytes32 stored_meta = vm.load(dmap_address, meta_slot); 98 | assertEq(meta, stored_meta); 99 | 100 | bytes32 data_slot; 101 | assembly { 102 | data_slot := add(1, meta_slot) 103 | } 104 | bytes32 stored_data = vm.load(dmap_address, data_slot); 105 | assertEq(data, stored_data); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /disassembly/deployed_bytecode.txt: -------------------------------------------------------------------------------- 1 | // SOURCE https://etherscan.io/address/0x90949c9937A11BA943C7A72C3FA073a37E3FdD96#code 2 | 3 | // Store 0x80 at the free memory pointer 4 | PUSH1 0x80 5 | PUSH1 0x40 6 | MSTORE 7 | 8 | // Push CALLDATASIZE to stack 9 | CALLDATASIZE 10 | // Push 36 to to stack 11 | PUSH1 0x24 12 | // Substract 36 - CALLDATASIZE [1] 13 | SUB 14 | // Push 22 on the stack 15 | PUSH1 0x22 16 | // IF [1] was not zero, Jump to [2] 17 | JUMPI 18 | // [1] was zero, enter the if statement 19 | // Load the word in calldata with an offset of 4. Push this to the stack. 20 | PUSH1 0x04 21 | CALLDATALOAD 22 | // Load the slot from storage identified by the value on the stack 23 | SLOAD 24 | // Store the value at the loaded slot at the first word in memory 25 | PUSH1 0x00 26 | MSTORE 27 | // Load the word in calldata with an offset of 4 (again). Push this to the stack 28 | PUSH1 0x04 29 | CALLDATALOAD 30 | // Push 1 to the stack 31 | PUSH1 0x01 32 | // Add the two values on the stack (CALLDATA[4:36) + 1) 33 | ADD 34 | // Load from storage slot at this value 35 | SLOAD 36 | // Store this value at the second word in memory 37 | PUSH1 0x20 38 | MSTORE 39 | // Return the first two words in memory (value at storage slot in calldata, and value at that slot + 1) 40 | PUSH1 0x40 41 | PUSH1 0x00 42 | RETURN 43 | 44 | // [2] We jump here if [1] is != 0 45 | JUMPDEST 46 | // Push the first word after the selector in the calldata (name) 47 | PUSH1 0x04 48 | CALLDATALOAD 49 | // Push the second word after the selector in the calldata (meta) 50 | PUSH1 0x24 51 | CALLDATALOAD 52 | // Push the third word after the selector in calldata (data) 53 | PUSH1 0x44 54 | CALLDATALOAD 55 | // Push the caller() address onto the stack and store it in the first word of memory 56 | CALLER 57 | PUSH1 0x00 58 | MSTORE 59 | // Duplicate the 3rd value on the stack (name) and store it at the second word of memory 60 | DUP3 61 | PUSH1 0x20 62 | // Compute the keccak256 of caller() . name (where . is concat) 63 | MSTORE 64 | PUSH1 0x40 65 | PUSH1 0x00 66 | SHA3 67 | // Duplicate the 2nd word on the stack (data) 68 | DUP2 69 | // Duplicate the 4th word on the stack (meta) 70 | DUP4 71 | // Duplicate the 6th word on the stack (name) 72 | DUP6 73 | // Store the caller on the stack 74 | CALLER 75 | // Store zero on the stack 76 | PUSH1 0x00 77 | // Duplicate the 1st word on the stack (0) 78 | DUP1 79 | // Emit log4 using first 6 words on stack (0,0,caller,name,meta,data) 80 | LOG4 81 | // Duplicate the 2nd word on the stack (data) 82 | DUP2 83 | // Push 1 to the stack. Duplicate the 3rd word (keccac256 of caller() . name). Add these two. 84 | PUSH1 0x01 85 | DUP3 86 | ADD 87 | // Store data at 1 + keccak256(caller() . name) 88 | SSTORE 89 | // Duplicate the first word on the stack (keccak256(caller() . name)) 90 | DUP1 91 | // Load the storage slot at this value 92 | SLOAD 93 | // Push 0x1 to the stack (constant, but represents LOCK). Do 0x1 & keccak256(caller() . name), and store value on stack. 94 | PUSH1 0x01 95 | AND 96 | // Push the size of the calldata onto the stack. Push 100 on to the stack. 97 | CALLDATASIZE 98 | PUSH1 0x64 99 | // XOR 100 and CALLDATASIZE. Store the value on the stack 100 | XOR 101 | // Check if either of the last two values pushed to stack are 1 (lock is set or calldata is not 100) 102 | OR 103 | // Push jump dest onto stack 104 | PUSH1 0x58 105 | // [3] Jump to [4] if OR statement is true (lock is set or wrong calldatasize) 106 | JUMPI 107 | // Store the meta at slot keccak256(caller() . name ) and return 0 (STOP) 108 | DUP3 109 | DUP2 110 | SSTORE 111 | STOP 112 | 113 | // [4] We jump here if the lock is set. Pop all variables from stack. 114 | JUMPDEST 115 | POP 116 | POP 117 | POP 118 | POP 119 | // Push (100 - CALLDATASIZE) to the stack 120 | CALLDATASIZE 121 | PUSH1 0x64 122 | SUB 123 | // [5] Push jump location [6] to stack and jump if (100 - calldatasize) != 0 124 | PUSH1 0x74 125 | JUMPI 126 | // Didn't jump. We attempted to write to a locked meta slot. Put LOCKED() error on stack and shift left 224 bits so 127 | // the 4byte error is left aligned 128 | PUSH4 0xa1422f69 129 | PUSH1 0xe0 130 | SHL 131 | // store the shifted error at memory position 0 132 | PUSH1 0x00 133 | MSTORE 134 | // Revert with the 4byte error data 135 | PUSH1 0x04 136 | PUSH1 0x00 137 | REVERT 138 | 139 | // [6] We jump here if [5] is equal to zero. This means a calldatasize was neither 36 nor 100. 140 | JUMPDEST 141 | PUSH1 0x00 142 | DUP1 143 | REVERT 144 | 145 | // remainder of init / metadata code 146 | 'fe'(Unknown Opcode) 147 | LOG2 148 | PUSH5 0x6970667358 149 | '22'(Unknown Opcode) 150 | SLT 151 | SHA3 152 | SELFBALANCE 153 | '5e'(Unknown Opcode) 154 | '23'(Unknown Opcode) 155 | DUP16 156 | MULMOD 157 | 'c0'(Unknown Opcode) 158 | PUSH28 0x2df011287cd0b887d9e0864657776ab6a3484c43f79237fefa64736f -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Assembly Walkthroughs and Fuzz Tests against deployed dmap object 2 | ### What does this repo do? 3 | **The more interesting part of this repo is a collection of various stepthroughs of the dmap object's deployed bytecode. Stepthroughs are from a number of contributors. See [Dissassembly](./disassembly)**. 4 | 5 | This less interesting part of this repo is designed to fork the mainnet deploy of [dmap](https://github.com/dapphub/dmap) and run various fuzzed tests against it. For example 6 | * It tests that the `get` and `set` functions of dmap work as expected 7 | * It further verifies the `set` function using storage inspection within our fork 8 | * It tests that entries with locked metadata (LSB of meta set to `0x1`) revert on 2nd write and data is not changed 9 | 10 | There are more tests to be written. 11 | 12 | ### How to run tests in this repo 13 | This repo uses [Foundry](https://github.com/foundry-rs/foundry). Look in that repo for install instructions. 14 | 15 | To run them, go to the root of the repo and do: 16 | ``` 17 | $> forge install 18 | ``` 19 | 20 | Then do 21 | ``` 22 | $> forge test -f test 23 | ``` 24 | 25 | optionally pass `-vvvv` to see fork and call details. 26 | 27 | If you don't have an ethereum node running for the rpc url and really don't want to run one, you can look at the Ethereum Foundation's [Node as a service](https://ethereum.org/en/developers/docs/nodes-and-clients/nodes-as-a-service/) article. 28 | 29 | ### Example Output 30 | Here's the example output with high verbosity. In this example we run each fuzzed test 10,000 times with different inputs. By default 256 runs are used as 10,000 takes some time. I do not recommend running it 10,000 times by default. 31 | 32 | For reference, running 256 iterations for all tests only takes about 10 seconds. 10,000 times takes 7+ minutes depending on your machine. 33 | 34 | ```sh 35 | $> FOUNDRY_FUZZ_RUNS=10000 forge test -f $ETH_RPC_URL -vvvv 36 | [⠑] Compiling... 37 | [⠰] Compiling 1 files with 0.8.14 38 | [⠒] Solc 0.8.14 finished in 1.72s 39 | Compiler run successful 40 | 41 | Running 5 tests for src/test/Dmap.t.sol:ContractTest 42 | [PASS] testBasicSetAndGet() (gas: 57097) 43 | Traces: 44 | [57097] ContractTest::testBasicSetAndGet() 45 | ├─ [46266] 0x9094…dd96::00000000(6e616d65000000000000000000000000000000000000000000000000000000006d657461000000000000000000000000000000000000000000000000000000006461746131000000000000000000000000000000000000000000000000000000) 46 | │ ├─ emit topic 0: 0x000000000000000000000000b4c79dab8f259c7aee6e5b2aa729821864227e84 47 | │ │ topic 1: 0x6e616d6500000000000000000000000000000000000000000000000000000000 48 | │ │ topic 2: 0x6d65746100000000000000000000000000000000000000000000000000000000 49 | │ │ topic 3: 0x6461746131000000000000000000000000000000000000000000000000000000 50 | │ │ data: 0x 51 | │ └─ ← () 52 | ├─ [275] 0x9094…dd96::00000000(e5ea0e2e475545ad8f17dc5c8715fbe19059cd94087d116068caa6cbaf092175) 53 | │ └─ ← 0x6d657461000000000000000000000000000000000000000000000000000000006461746131000000000000000000000000000000000000000000000000000000 54 | ├─ [2275] 0x9094…dd96::00000000(e5ea0e2e475545ad8f17dc5c8715fbe19059cd94087d116068caa6cbaf092176) 55 | │ └─ ← 0x64617461310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 56 | └─ ← () 57 | 58 | [PASS] testBasicSetAndGetFuzzed(bytes32,bytes32,bytes32) (runs: 10000, μ: 57128, ~: 57128) 59 | [PASS] testLockedSlot(bytes32,bytes32,bytes32) (runs: 10000, μ: 62143, ~: 62143) 60 | [PASS] testSetAndGetStorageInspection() (gas: 56207) 61 | Traces: 62 | [56207] ContractTest::testSetAndGetStorageInspection() 63 | ├─ [46266] 0x9094…dd96::00000000(6e616d65000000000000000000000000000000000000000000000000000000006d657461000000000000000000000000000000000000000000000000000000006461746131000000000000000000000000000000000000000000000000000000) 64 | │ ├─ emit topic 0: 0x000000000000000000000000b4c79dab8f259c7aee6e5b2aa729821864227e84 65 | │ │ topic 1: 0x6e616d6500000000000000000000000000000000000000000000000000000000 66 | │ │ topic 2: 0x6d65746100000000000000000000000000000000000000000000000000000000 67 | │ │ topic 3: 0x6461746131000000000000000000000000000000000000000000000000000000 68 | │ │ data: 0x 69 | │ └─ ← () 70 | ├─ [0] VM::load(0x90949c9937a11ba943c7a72c3fa073a37e3fdd96, 0xe5ea0e2e475545ad8f17dc5c8715fbe19059cd94087d116068caa6cbaf092175) 71 | │ └─ ← 0x6d65746100000000000000000000000000000000000000000000000000000000 72 | ├─ [0] VM::load(0x90949c9937a11ba943c7a72c3fa073a37e3fdd96, 0xe5ea0e2e475545ad8f17dc5c8715fbe19059cd94087d116068caa6cbaf092176) 73 | │ └─ ← 0x6461746131000000000000000000000000000000000000000000000000000000 74 | └─ ← () 75 | 76 | [PASS] testSetAndGetStorageInspectionFuzzed(bytes32,bytes32,bytes32) (runs: 10000, μ: 56368, ~: 56368) 77 | Test result: ok. 5 passed; 0 failed; finished in 487.97s 78 | ``` 79 | -------------------------------------------------------------------------------- /disassembly/README.md: -------------------------------------------------------------------------------- 1 | ## Disassemlbing the DMAP object 2 | For starters, know that the dmap object is deployed [here](https://etherscan.io/address/0x90949c9937A11BA943C7A72C3FA073a37E3FdD96). 3 | 4 | Please look at the various walkthroughs through this directory to see different approaches and styles. 5 | 6 | 7 | Let's start by going through the fallback function, line by line. 8 | 9 | Example Scenario: Calling `get(1)`, aka reading the `rootzone`'s `data`. 10 | We will be inspecting the assembly for the following section of the object 11 | 12 | (Alternatively, you can read 2 words from `0x0` for both `rootzone`'s `meta` and `data` i.e. `get(0)`) 13 | ``` 14 | if eq(36, calldatasize()) { 15 | mstore(0, sload(calldataload(4))) 16 | mstore(32, sload(add(1, calldataload(4)))) 17 | return(0, 64) 18 | } 19 | ``` 20 | 21 | 1. value of `calldatasize()` is pushed onto the stack 22 | ``` 23 | Stack: 24 | 0x0000000000000000000000000000000000000000000000000000000000000024 25 | 26 | Memory: 27 | 0x0000000000000000000000000000000000000000000000000000000000000000 28 | 0x0000000000000000000000000000000000000000000000000000000000000000 29 | 0x0000000000000000000000000000000000000000000000000000000000000080 30 | ``` 31 | 2. `36` is pushed onto the stack 32 | ``` 33 | Stack: 34 | 0x0000000000000000000000000000000000000000000000000000000000000024 35 | 0x0000000000000000000000000000000000000000000000000000000000000024 36 | 37 | Memory: 38 | 0x0000000000000000000000000000000000000000000000000000000000000000 39 | 0x0000000000000000000000000000000000000000000000000000000000000000 40 | 0x0000000000000000000000000000000000000000000000000000000000000080 41 | ``` 42 | 43 | 3. `SUB` is called 44 | ``` 45 | Stack: 46 | 0x0000000000000000000000000000000000000000000000000000000000000000 47 | 48 | Memory: 49 | 0x0000000000000000000000000000000000000000000000000000000000000000 50 | 0x0000000000000000000000000000000000000000000000000000000000000000 51 | 0x0000000000000000000000000000000000000000000000000000000000000080 52 | ``` 53 | 54 | 4. `34` is pushed to the stack 55 | ``` 56 | Stack: 57 | 0x0000000000000000000000000000000000000000000000000000000000000022 58 | 0x0000000000000000000000000000000000000000000000000000000000000000 59 | 60 | Memory: 61 | 0x0000000000000000000000000000000000000000000000000000000000000000 62 | 0x0000000000000000000000000000000000000000000000000000000000000000 63 | 0x0000000000000000000000000000000000000000000000000000000000000080 64 | ``` 65 | 66 | 5. `JUMPI` is called. It does not jump as the second value on the stack is zero. 67 | 68 | 6. The object enters the if condition 69 | ``` 70 | mstore(0, sload(calldataload(4))) 71 | ``` 72 | ``` 73 | Stack: 74 | 75 | Memory: 76 | 0x0000000000000000000000000000000000000000000000000000000000000000 77 | 0x0000000000000000000000000000000000000000000000000000000000000000 78 | 0x0000000000000000000000000000000000000000000000000000000000000080 79 | ``` 80 | 81 | 7. `4` is pushed onto the stack 82 | ``` 83 | Stack: 84 | 0x0000000000000000000000000000000000000000000000000000000000000004 85 | 86 | Memory: 87 | 0x0000000000000000000000000000000000000000000000000000000000000000 88 | 0x0000000000000000000000000000000000000000000000000000000000000000 89 | 0x0000000000000000000000000000000000000000000000000000000000000080 90 | ``` 91 | 92 | 8. `calldataload()` is called, using an offset of 4. This reads 32 bytes from the calldata starting with after the 4th byte. In this 93 | scenario, that value is 1. 94 | ``` 95 | Stack: 96 | 0x0000000000000000000000000000000000000000000000000000000000000001 97 | 98 | Memory: 99 | 0x0000000000000000000000000000000000000000000000000000000000000000 100 | 0x0000000000000000000000000000000000000000000000000000000000000000 101 | 0x0000000000000000000000000000000000000000000000000000000000000080 102 | ``` 103 | 104 | 9. `SLOAD` is called, loading data from the first storage slot 105 | ``` 106 | Stack: 107 | 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 108 | 109 | Memory: 110 | 0x0000000000000000000000000000000000000000000000000000000000000000 111 | 0x0000000000000000000000000000000000000000000000000000000000000000 112 | 0x0000000000000000000000000000000000000000000000000000000000000080 113 | ``` 114 | 115 | 10. `0` is pushed to the stack 116 | ``` 117 | Stack: 118 | 0x0000000000000000000000000000000000000000000000000000000000000000 119 | 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 120 | 121 | Memory: 122 | 0x0000000000000000000000000000000000000000000000000000000000000000 123 | 0x0000000000000000000000000000000000000000000000000000000000000000 124 | 0x0000000000000000000000000000000000000000000000000000000000000080 125 | ``` 126 | 127 | 11. `MSTORE` is called, storing the rootzone in the first word of memory (a word is 32 bytes in the evm) 128 | ``` 129 | Stack: 130 | 0x0000000000000000000000000000000000000000000000000000000000000000 131 | 0x0000000000000000000000000000000000000000000000000000000000000000 132 | 133 | Memory: 134 | 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 135 | 0x0000000000000000000000000000000000000000000000000000000000000000 136 | 0x0000000000000000000000000000000000000000000000000000000000000080 137 | ``` 138 | 139 | 12. We move to the next line 140 | ``` 141 | mstore(32, sload(add(1, calldataload(4)))) 142 | ``` 143 | 144 | 13. `4` is pushed to the stack 145 | ``` 146 | Stack: 147 | 0x0000000000000000000000000000000000000000000000000000000000000004 148 | 149 | Memory: 150 | 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 151 | 0x0000000000000000000000000000000000000000000000000000000000000000 152 | 0x0000000000000000000000000000000000000000000000000000000000000080 153 | ``` 154 | 14. `calldataload()` is called with an offset of 4, loading bytes [4,32] of calldata onto the stack 155 | ``` 156 | Stack: 157 | 0x0000000000000000000000000000000000000000000000000000000000000001 158 | 159 | Memory: 160 | 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 161 | 0x0000000000000000000000000000000000000000000000000000000000000000 162 | 0x0000000000000000000000000000000000000000000000000000000000000080 163 | ``` 164 | 165 | 15. `1` is pushed to the stack 166 | ``` 167 | Stack: 168 | 0x0000000000000000000000000000000000000000000000000000000000000001 169 | 0x0000000000000000000000000000000000000000000000000000000000000001 170 | 171 | Memory: 172 | 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 173 | 0x0000000000000000000000000000000000000000000000000000000000000000 174 | 0x0000000000000000000000000000000000000000000000000000000000000080 175 | ``` 176 | 177 | 16. `ADD` is called 178 | ``` 179 | Stack: 180 | 0x0000000000000000000000000000000000000000000000000000000000000002 181 | 182 | Memory: 183 | 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 184 | 0x0000000000000000000000000000000000000000000000000000000000000000 185 | 0x0000000000000000000000000000000000000000000000000000000000000080 186 | ``` 187 | 188 | 17. `SLOAD` is called. The slot is empty in this case as `get(1)` is reading the rootzone's data, where `get(0)` would be reading both `meta` and `data`. 189 | ``` 190 | Stack: 191 | 0x0000000000000000000000000000000000000000000000000000000000000000 192 | 193 | Memory: 194 | 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 195 | 0x0000000000000000000000000000000000000000000000000000000000000000 196 | 0x0000000000000000000000000000000000000000000000000000000000000080 197 | ``` 198 | 199 | 18. `32` is pushed onto the stack 200 | ``` 201 | Stack: 202 | 0x0000000000000000000000000000000000000000000000000000000000000020 203 | 0x0000000000000000000000000000000000000000000000000000000000000000 204 | 205 | Memory: 206 | 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 207 | 0x0000000000000000000000000000000000000000000000000000000000000000 208 | 0x0000000000000000000000000000000000000000000000000000000000000080 209 | ``` 210 | 211 | 212 | 19. `MSTORE` is called, storing the previously loaded slot in the 2nd word of memory 213 | ``` 214 | Stack: 215 | 216 | Memory: 217 | 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 218 | 0x0000000000000000000000000000000000000000000000000000000000000000 // THIS VALUE WAS JUST WRITTEN 219 | 0x0000000000000000000000000000000000000000000000000000000000000080 220 | ``` 221 | 222 | 223 | 19. We enter the return phase 224 | ``` 225 | return(0, 64) 226 | ``` 227 | 228 | 20. `0x40`, is pushed to the stack 229 | ``` 230 | Stack: 231 | 0x0000000000000000000000000000000000000000000000000000000000000040 232 | 233 | Memory: 234 | 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 235 | 0x0000000000000000000000000000000000000000000000000000000000000000 236 | 0x0000000000000000000000000000000000000000000000000000000000000080 237 | ``` 238 | 239 | 21. `0x00`, is pushed to the stack 240 | ``` 241 | Stack: 242 | 0x0000000000000000000000000000000000000000000000000000000000000000 243 | 0x0000000000000000000000000000000000000000000000000000000000000040 244 | 245 | Memory: 246 | 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 247 | 0x0000000000000000000000000000000000000000000000000000000000000000 248 | 0x0000000000000000000000000000000000000000000000000000000000000080 249 | ``` 250 | 251 | 22. `return` is called, returning the first two words in memory (bytes 0-64). In this case, the rootzone address and nothing. 252 | ``` 253 | Return Value: 0x022ea9ba506e38ef6093b6ab53e48bbd60f868320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 254 | ``` 255 | -------------------------------------------------------------------------------- /disassembly/dmap-bytecode-disassembly.md: -------------------------------------------------------------------------------- 1 | # DMAP 2 | 3 | `dmap` is basically a simple global registry that stores `(zone, name, meta, data)`. 4 | 5 | Usage is divided into mainly two functions: 6 | 7 | 1. `set(name, meta, data)`: sets `s=keccak256(msg.sender, name)` slot to `meta` and `s+1` slot to `data`. 8 | 2. `get(name)`: returns `s=keccak256(msg.sender, name)` slot as `meta` and `s+1` as `data`. 9 | 10 | Decompiled using [ethervm's decompiler](https://ethervm.io/decompile/0x90949c9937A11BA943C7A72C3FA073a37E3FdD96). 11 | 12 | ## Examples 13 | 14 | Let's manually step through `dmap` bytecode using set and get example. 15 | 16 | Create `set` calldata using `abi.encodePacked(SEL, name, meta, data)` where SEL is a `4byte` selector and others are `bytes32`. So, for `set` calls, calldata length should be **100** bytes. And, `get` calldata should be **36** bytes. using `abi.encodePacked(SEL, s)` where `s` is the storage slot from `keccak256(msg.sender, name)`. 17 | 18 | First start with a `get` example. 19 | 20 | ## GET 21 | 22 | 1. Set up the free-memory pointer. 23 | 24 | ```bash 25 | 0000 60 PUSH1 0x80 26 | 0002 60 PUSH1 0x40 27 | 0004 52 MSTORE 28 | 29 | MEMORY: 30 | 0040: 0x0000000000000000000000000000000000000000000000000000000000000080 31 | ``` 32 | 33 | 2. Check `calldatasize` to be equal to _36_ as `get` calldata = _36_ bytes. 34 | 35 | ```bash 36 | 0005 36 CALLDATASIZE 37 | STACK: 1 38 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000024 (calldatasize) 39 | 40 | 0006 60 PUSH1 0x24 41 | STACK: 2 42 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000024 (36 bytes) 43 | 0001: 0x0000000000000000000000000000000000000000000000000000000000000024 (calldatasize) 44 | 45 | 0008 03 SUB 46 | STACK: 1 47 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000000 (sub) 48 | 49 | 0009 60 PUSH1 0x22 50 | STACK: 2 51 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000022 (jump location) 52 | 0001: 0x0000000000000000000000000000000000000000000000000000000000000000 (jump condition) 53 | 54 | 000B 57 *JUMPI 55 | STACK: 0 56 | ``` 57 | 58 | 3. load slot to stack 59 | 60 | ```bash 61 | 000C 60 PUSH1 0x04 62 | STACK: 1 63 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000004 (calldata offset) 64 | 65 | 000E 35 CALLDATALOAD 66 | STACK: 1 67 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000001 (load calldata[0x04:0x24]) 68 | ``` 69 | 70 | 4. load storage slot to stack and store to `mem[0x00:0x20]` 71 | 72 | ```bash 73 | 000F 54 SLOAD 74 | STACK: 1 75 | 0000: 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 (slot value) 76 | 77 | 0010 60 PUSH1 0x00 78 | STACK: 2 79 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000000 80 | 0001: 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 81 | 82 | 0012 52 MSTORE 83 | STACK: 0 84 | MEMORY: 85 | 0000: 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 (slot value) 86 | 0040: 0x0000000000000000000000000000000000000000000000000000000000000080 87 | ``` 88 | 89 | 5. load s+1 slot to stack 90 | 91 | ```bash 92 | 0013 60 PUSH1 0x04 93 | STACK: 1 94 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000004 95 | MEMORY: 96 | 0000: 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 97 | 0040: 0x0000000000000000000000000000000000000000000000000000000000000080 98 | 99 | 0015 35 CALLDATALOAD 100 | STACK: 1 101 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000001 (slot to retrieve) 102 | MEMORY: 103 | 0000: 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 104 | 105 | 0016 60 PUSH1 0x01 106 | STACK: 2 107 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000001 108 | 0001: 0x0000000000000000000000000000000000000000000000000000000000000001 109 | MEMORY: 110 | 0000: 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 111 | 112 | 0018 01 ADD 113 | STACK: 1 114 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000002 115 | MEMORY: 116 | 0000: 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 117 | 118 | 0019 54 SLOAD 119 | STACK: 1 120 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000000 121 | MEMORY: 122 | 0000: 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 123 | ``` 124 | 125 | 6. store loaded slot to `mem[0x20:0x40]` 126 | 127 | ```bash 128 | 001A 60 PUSH1 0x20 129 | STACK: 2 130 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000020 131 | 0001: 0x0000000000000000000000000000000000000000000000000000000000000000 132 | MEMORY: 133 | 0000: 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 134 | 135 | 001C 52 MSTORE 136 | STACK: 0 137 | MEMORY: 138 | 0000: 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 139 | 0020: 0x0000000000000000000000000000000000000000000000000000000000000000 140 | ``` 141 | 142 | 7. return `mem[0x00:0x40]` 143 | 144 | ```bash 145 | 001D 60 PUSH1 0x40 146 | 001F 60 PUSH1 0x00 147 | STACK: 2 148 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000000 (memory offset) 149 | 0001: 0x0000000000000000000000000000000000000000000000000000000000000040 (memory length) 150 | MEMORY: 151 | 0000: 0x022ea9ba506e38ef6093b6ab53e48bbd60f86832000000000000000000000000 152 | 0020: 0x0000000000000000000000000000000000000000000000000000000000000000 153 | 154 | 0021 F3 *RETURN 155 | ``` 156 | 157 | ## SET 158 | 159 | Now, let's walkthrough a `set` case. 160 | 161 | ``` 162 | name: "dmap" = 0x6e616d6500000000000000000000000000000000000000000000000000000000 163 | meta: "meta" = 0x6d65746100000000000000000000000000000000000000000000000000000000 164 | data: "data" = 0x6461746100000000000000000000000000000000000000000000000000000000 165 | 166 | calldata: `abi.encodePacked(0x00000000,name,meta,data)` = 0x000000006e616d65000000000000000000000000000000000000000000000000000000006d657461000000000000000000000000000000000000000000000000000000006461746100000000000000000000000000000000000000000000000000000000 167 | ``` 168 | 169 | 1. Jump to `0x22` if `calldatasize > 36` 170 | 171 | ```bash 172 | 0022 5B JUMPDEST 173 | ``` 174 | 175 | 2. push `name=calldata[0x04:0x24]`, `meta=calldata[0x24:0x44]`, `calldata[0x44:0x64]` to stack 176 | 177 | ```bash 178 | 0023 60 PUSH1 0x04 179 | 0025 35 CALLDATALOAD 180 | 0026 60 PUSH1 0x24 181 | 0028 35 CALLDATALOAD 182 | 0029 60 PUSH1 0x44 183 | 002B 35 CALLDATALOAD 184 | 185 | STACK: 3 186 | 0000: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 187 | 0001: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 188 | 0002: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 189 | ``` 190 | 191 | 3. store msg.sender at mem[0x00:0x20] 192 | 193 | ```bash 194 | 002C 33 CALLER 195 | 002D 60 PUSH1 0x00 196 | 002F 52 MSTORE 197 | 198 | STACK: 3 199 | 0000: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 200 | 0001: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 201 | 0002: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 202 | MEMORY: 203 | 0000: 0x000000000000000000000000be862ad9abfe6f22bcb087716c7d89a26051f74c (caller) 204 | ``` 205 | 206 | 207 | 4. store `name` at mem[0x20:0x40] 208 | 209 | ```bash 210 | 0030 82 DUP3 211 | 0031 60 PUSH1 0x20 212 | 0033 52 MSTORE 213 | 214 | STACK: 3 215 | 0000: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 216 | 0001: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 217 | 0002: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 218 | MEMORY: 219 | 0000: 0x000000000000000000000000be862ad9abfe6f22bcb087716c7d89a26051f74c (caller) 220 | 0020: 0x6e616d6500000000000000000000000000000000000000000000000000000000 221 | ``` 222 | 223 | 5. calculate `keccak256(mem[0x00:0x40])` to find the storage slot 224 | 225 | ```bash 226 | 0034 60 PUSH1 0x40 227 | 0036 60 PUSH1 0x00 228 | 0038 20 SHA3 229 | 230 | STACK: 4 231 | 0000: 0xf7fbcbda33204447e5b6bf43e1ab2765746799462c92f997afb4e0d6a2fab922 (slot) 232 | 0000: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 233 | 0001: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 234 | 0002: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 235 | MEMORY: 236 | 0000: 0x000000000000000000000000be862ad9abfe6f22bcb087716c7d89a26051f74c (caller) 237 | 0020: 0x6e616d6500000000000000000000000000000000000000000000000000000000 238 | ``` 239 | 240 | 6. log the event `(0, 0, msg.sender, name, meta, data)` 241 | 242 | ```bash 243 | 0039 81 DUP2 244 | 003A 83 DUP4 245 | 003B 85 DUP6 246 | 003C 33 CALLER 247 | 003D 60 PUSH1 0x00 248 | 003F 80 DUP1 249 | 250 | STACK: 9 251 | 0000: 0 252 | 0001: 0 253 | 0002: 0x000000000000000000000000be862ad9abfe6f22bcb087716c7d89a26051f74c (caller) 254 | 0003: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 255 | 0004: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 256 | 0005: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 257 | 0000: 0xf7fbcbda33204447e5b6bf43e1ab2765746799462c92f997afb4e0d6a2fab922 (slot) 258 | 0000: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 259 | 0001: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 260 | 0002: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 261 | MEMORY: 262 | 0000: 0x000000000000000000000000be862ad9abfe6f22bcb087716c7d89a26051f74c (caller) 263 | 0020: 0x6e616d6500000000000000000000000000000000000000000000000000000000 264 | 265 | 0040 A4 LOG4 266 | STACK: 4 267 | 0000: 0xf7fbcbda33204447e5b6bf43e1ab2765746799462c92f997afb4e0d6a2fab922 (slot) 268 | 0000: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 269 | 0001: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 270 | 0002: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 271 | MEMORY: 272 | 0000: 0x000000000000000000000000be862ad9abfe6f22bcb087716c7d89a26051f74c (caller) 273 | 0020: 0x6e616d6500000000000000000000000000000000000000000000000000000000 274 | ``` 275 | 276 | > Store `data` at `slot + 1` 277 | 278 | 7. duplicate `data` to stack top and push `1` 279 | 280 | ```bash 281 | 0041 81 DUP2 282 | STACK: 5 283 | 0000: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 284 | 0000: 0xf7fbcbda33204447e5b6bf43e1ab2765746799462c92f997afb4e0d6a2fab922 (slot) 285 | 0000: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 286 | 0001: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 287 | 0002: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 288 | 289 | 0042 60 PUSH1 0x01 290 | STACK: 7 291 | 0000: 01 292 | 0000: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 293 | 0000: 0xf7fbcbda33204447e5b6bf43e1ab2765746799462c92f997afb4e0d6a2fab922 (slot) 294 | 0000: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 295 | 0001: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 296 | 0002: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 297 | ``` 298 | 299 | 8. duplicate slot and add 1. 300 | 301 | ```bash 302 | 0044 82 DUP3 303 | 0045 01 ADD 304 | STACK: 6 305 | 0000: 0xf7fbcbda33204447e5b6bf43e1ab2765746799462c92f997afb4e0d6a2fab923 (slot + 1) 306 | 0000: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 307 | 0000: 0xf7fbcbda33204447e5b6bf43e1ab2765746799462c92f997afb4e0d6a2fab922 (slot) 308 | 0000: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 309 | 0001: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 310 | 0002: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 311 | ``` 312 | 313 | 9. store at `storage[slot+1] = data` 314 | 315 | ```bash 316 | 0046 55 SSTORE 317 | STACK: 4 318 | 0000: 0xf7fbcbda33204447e5b6bf43e1ab2765746799462c92f997afb4e0d6a2fab922 (slot) 319 | 0000: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 320 | 0001: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 321 | 0002: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 322 | ``` 323 | 324 | > Next code block checks that `calldatasize=100` and storage slot should be empty. 325 | 326 | 10. load slot and perform and with 1 to check if slot is empty or not 327 | 328 | ```bash 329 | 0047 80 DUP1 330 | STACK: 5 331 | 0000: 0xf7fbcbda33204447e5b6bf43e1ab2765746799462c92f997afb4e0d6a2fab922 (slot) 332 | 0001: 0xf7fbcbda33204447e5b6bf43e1ab2765746799462c92f997afb4e0d6a2fab922 (slot) 333 | 0002: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 334 | 0003: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 335 | 0004: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 336 | 337 | 0048 54 SLOAD 338 | STACK: 5 339 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000000 (slot value) 340 | 0001: 0xf7fbcbda33204447e5b6bf43e1ab2765746799462c92f997afb4e0d6a2fab922 (slot) 341 | 0002: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 342 | 0003: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 343 | 0004: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 344 | 345 | 0049 60 PUSH1 0x01 346 | STACK: 6 347 | 0000: 0x01 348 | 0001: 0x0000000000000000000000000000000000000000000000000000000000000000 (slot value) 349 | 0002: 0xf7fbcbda33204447e5b6bf43e1ab2765746799462c92f997afb4e0d6a2fab922 (slot) 350 | 0003: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 351 | 0004: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 352 | 0005: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 353 | 354 | 004B 16 AND 355 | STACK: 5 356 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000000 357 | 0001: 0xf7fbcbda33204447e5b6bf43e1ab2765746799462c92f997afb4e0d6a2fab922 (slot) 358 | 0002: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 359 | 0003: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 360 | 0004: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 361 | ``` 362 | 363 | 11. check `calldatasize=100` 364 | 365 | ```bash 366 | 004C 36 CALLDATASIZE 367 | STACK: 6 368 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000064 (calldatasize) 369 | 0001: 0x0000000000000000000000000000000000000000000000000000000000000000 (slot filled or not) 370 | 0002: 0xf7fbcbda33204447e5b6bf43e1ab2765746799462c92f997afb4e0d6a2fab922 (slot) 371 | 0003: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 372 | 0004: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 373 | 0005: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 374 | 375 | 004D 60 PUSH1 0x64 376 | STACK: 7 377 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000064 (100 bytes) 378 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000064 (calldatasize) 379 | 0001: 0x0000000000000000000000000000000000000000000000000000000000000000 (slot filled or not) 380 | 0002: 0xf7fbcbda33204447e5b6bf43e1ab2765746799462c92f997afb4e0d6a2fab922 (slot) 381 | 0003: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 382 | 0004: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 383 | 0005: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 384 | 385 | 004F 18 XOR 386 | STACK: 6 387 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000000 (calldatasize=100 or not) 388 | 0001: 0x0000000000000000000000000000000000000000000000000000000000000000 (slot filled or not) 389 | 0002: 0xf7fbcbda33204447e5b6bf43e1ab2765746799462c92f997afb4e0d6a2fab922 (slot) 390 | 0003: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 391 | 0004: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 392 | 0005: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 393 | 394 | 0050 17 OR 395 | STACK: 5 396 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000000 397 | 0001: 0xf7fbcbda33204447e5b6bf43e1ab2765746799462c92f997afb4e0d6a2fab922 (slot) 398 | 0002: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 399 | 0003: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 400 | 0004: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 401 | ``` 402 | 403 | 12. if true, jump to `0x58` and revert according to calldatasize 404 | 405 | ```bash 406 | 0051 60 PUSH1 0x58 407 | 0053 57 *JUMPI 408 | ``` 409 | 410 | 13. conditions are fulfilled, store `meta` at storage slot and return 411 | 412 | ```bash 413 | 0054 82 DUP3 414 | STACK: 5 415 | 0000: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 416 | 0001: 0xf7fbcbda33204447e5b6bf43e1ab2765746799462c92f997afb4e0d6a2fab922 (slot) 417 | 0002: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 418 | 0003: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 419 | 0004: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 420 | 421 | 0055 81 DUP2 422 | STACK: 6 423 | 0000: 0xf7fbcbda33204447e5b6bf43e1ab2765746799462c92f997afb4e0d6a2fab922 (slot) 424 | 0001: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 425 | 0002: 0xf7fbcbda33204447e5b6bf43e1ab2765746799462c92f997afb4e0d6a2fab922 (slot) 426 | 0003: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 427 | 0004: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 428 | 0005: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 429 | 430 | 0056 55 SSTORE 431 | STACK: 4 432 | 0000: 0xf7fbcbda33204447e5b6bf43e1ab2765746799462c92f997afb4e0d6a2fab922 (slot) 433 | 0001: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 434 | 0002: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 435 | 0003: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 436 | 437 | 0057 00 *STOP 438 | ``` 439 | 440 | > Jump here, if conditions aren't fulfilled i.e. either lock is set or calldata is incorrect. 441 | 442 | 14. pop everything 443 | 444 | ```bash 445 | 0058 5B JUMPDEST 446 | STACK: 4 447 | 0000: 0xf7fbcbda33204447e5b6bf43e1ab2765746799462c92f997afb4e0d6a2fab922 (slot) 448 | 0001: 0x6461746100000000000000000000000000000000000000000000000000000000 (data) 449 | 0002: 0x6d65746100000000000000000000000000000000000000000000000000000000 (meta) 450 | 0003: 0x6e616d6500000000000000000000000000000000000000000000000000000000 (name) 451 | 452 | 0059 50 POP 453 | 005A 50 POP 454 | 005B 50 POP 455 | 005C 50 POP 456 | 457 | STACK: 0 458 | ``` 459 | 460 | 15. check if attempted to write at locked slot by checking `calldatasize=100` 461 | 462 | ```bash 463 | 005D 36 CALLDATASIZE 464 | STACK: 1 465 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000064 (calldatasize) 466 | 467 | 005E 60 PUSH1 0x64 468 | STACK: 2 469 | 0000: 0x64 470 | 0001: 0x0000000000000000000000000000000000000000000000000000000000000064 (calldatasize) 471 | 472 | 0060 03 SUB 473 | STACK: 1 474 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000000 (calldatasize - 100) 475 | ``` 476 | 477 | 17. jump to `0x74` if calldatasize was wrong 478 | ```bash 479 | 0061 60 PUSH1 0x74 480 | 0063 57 *JUMPI 481 | ``` 482 | 483 | > Revert if attempt to write to a locked slot. 484 | 485 | 18. push `LOCKED()` converted to 4bytes 486 | 487 | ```bash 488 | 0064 63 PUSH4 0xa1422f69 489 | STACK: 1 490 | 0000: 0x00000000000000000000000000000000000000000000000000000000a1422f69 (4byte error) 491 | MEMORY: 492 | 0000: 0x000000000000000000000000be862ad9abfe6f22bcb087716c7d89a26051f74c (caller) 493 | 0020: 0x6e616d6500000000000000000000000000000000000000000000000000000000 494 | ``` 495 | 496 | 19. shift left by 224 bytes to left align the 4byte error 497 | 498 | ```bash 499 | 0069 60 PUSH1 0xe0 500 | STACK: 2 501 | 0000: 0xe0 502 | 0001: 0x00000000000000000000000000000000000000000000000000000000a1422f69 (4byte error) 503 | MEMORY: 504 | 0000: 0x000000000000000000000000be862ad9abfe6f22bcb087716c7d89a26051f74c (caller) 505 | 0020: 0x6e616d6500000000000000000000000000000000000000000000000000000000 506 | 507 | 006B 1B SHL 508 | STACK: 1 509 | 0000: 0xa1422f6900000000000000000000000000000000000000000000000000000000 510 | MEMORY: 511 | 0000: 0x000000000000000000000000be862ad9abfe6f22bcb087716c7d89a26051f74c (caller) 512 | 0020: 0x6e616d6500000000000000000000000000000000000000000000000000000000 513 | ``` 514 | 515 | 20. store the error and revert 516 | 517 | ```bash 518 | 006C 60 PUSH1 0x00 519 | 006E 52 MSTORE 520 | 521 | STACK: 0 522 | MEMORY: 523 | 0000: 0xa1422f6900000000000000000000000000000000000000000000000000000000 (4byte error left aligned) 524 | 0020: 0x6e616d6500000000000000000000000000000000000000000000000000000000 525 | 526 | 006F 60 PUSH1 0x04 527 | 0071 60 PUSH1 0x00 528 | STACK: 2 529 | 0000: 0x0000000000000000000000000000000000000000000000000000000000000000 (memory offset) 530 | 0001: 0x0000000000000000000000000000000000000000000000000000000000000004 (memory length) 531 | MEMORY: 532 | 0000: 0xa1422f6900000000000000000000000000000000000000000000000000000000 (4byte error left aligned) 533 | 0020: 0x6e616d6500000000000000000000000000000000000000000000000000000000 534 | 535 | 0073 FD *REVERT 536 | ``` 537 | 538 | 21. revert, if no conditions fulfilled 539 | 540 | ```bash 541 | 0074 5B JUMPDEST 542 | STACK: 0 543 | 544 | 0075 60 PUSH1 0x00 545 | 0077 80 DUP1 546 | STACK: 2 547 | 0000: 0x00 (memory offset) 548 | 0001: 0x00 (memory length) 549 | 550 | 0078 FD *REVERT 551 | ``` 552 | 553 | 22. remaining init opcodes 554 | 555 | ```bash 556 | 0079 FE *ASSERT 557 | 007A A2 LOG2 558 | 007B 64 PUSH5 0x6970667358 559 | 0081 22 22 560 | 0082 12 SLT 561 | 0083 20 SHA3 562 | 0084 47 SELFBALANCE 563 | 0085 5E 5E 564 | 0086 23 23 565 | 0087 8F DUP16 566 | 0088 09 MULMOD 567 | 0089 C0 C0 568 | 008A 7B PUSH28 0x2df011287cd0b887d9e0864657776ab6a3484c43f79237fefa64736f 569 | 00A7 6C PUSH13 0x634300080d0033 570 | ``` -------------------------------------------------------------------------------- /disassembly/opcode-stepping/2022-05-complected/basic-set/basic-set-report.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | - The goal here is to analyze: 4 | - the `dmap.sol` contract's Solidity source, as verified on Etherscan 5 | - the corresponding opcodes, as decompiled from the contract object on Mainnet, using Foundry's debugger 6 | - the memory and stack state that the opcodes manipulate, in order to check if the contract object correctly implements the most basic set call 7 | - This document is structured in the order of the Solidity source 8 | - For each section, I will show: 9 | 1. the Solidity code being analyzed 10 | 1. the opcodes 11 | 1. then the stack and memory state before/after the important instructions 12 | 13 | # dmap.sol:L33 14 | 15 | This checks for a `get` call, by looking at whether `calldatasize()` is `36` byte = 4 (method selector) + 32 (the slot argument). 16 | 17 | ``` 18 | L33 if eq(36, calldatasize()) { 19 | ... 20 | } 21 | ``` 22 | 23 | ``` 24 | │00|▶PUSH1(0x80) // Bytecode 0x00-0x04 are boilerplate in most contracts for finding free memory pointer. FIXME: source. 25 | │02| PUSH1(0x40) 26 | │04| MSTORE 27 | │05| CALLDATASIZE // L33 starts here. 28 | │06| PUSH1(0x24) 29 | │08| SUB 30 | │09| PUSH1(0x22) 31 | │0b| JUMPI // Only jump when `CALLDATASIZE` is not 36 byte. 32 | 33 | ``` 34 | 35 | Before `0x00` 36 | ``` 37 | // Stack and memory begin empty in a contract call. 38 | ``` 39 | 40 | # dmp.sol:L38-L40 41 | 42 | This loads arguments from `message`. 43 | ``` 44 | L38 let name := calldataload(4) 45 | L39 let meta := calldataload(36) 46 | L40 let data := calldataload(68) 47 | ``` 48 | 49 | ``` 50 | │22|▶JUMPDEST // Just a jump label. 51 | │23| PUSH1(0x04) 52 | │25| CALLDATALOAD // Push calldata[4:36]. 53 | │26| PUSH1(0x24) 54 | │28| CALLDATALOAD // Push calldata[36:68]. 55 | │29| PUSH1(0x44) 56 | │2b| CALLDATALOAD // Push calldata[68:100]. 57 | ``` 58 | 59 | Before `0x22` 60 | ``` 61 | // Stack is empty. 62 | 63 | ┌Memory (max expansion: 96 bytes)─────────────────────────────────────────────────────────────────── 64 | │00| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 65 | │20| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 66 | │40| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 // memory[0x40] will stay the same through the life of the call 67 | ``` 68 | 69 | After `0x2b` 70 | ``` 71 | ┌Stack: 3─────────────────────────────────────────────────────────────────────────────────────────── 72 | │00| 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // correctly loads arugument `data` 73 | │01| 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // `meta` 74 | │02| 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // `name` 75 | 76 | ┌Memory (max expansion: 96 bytes)─────────────────────────────────────────────────────────────────── 77 | │00| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 78 | │20| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 79 | │40| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 80 | ``` 81 | 82 | # IMPORTANT: dmap.sol:L41-L43 83 | 84 | // FIXME: make it more clear. 85 | 86 | The goal for this block is to compute the slot number. And the `slot` number is **particularly important**. 87 | 88 | Since slot number is keccak256 of caller address and `name`. this in effect implements authorization for writing to `slot`s. For any given slot x (and x + 1) already set by EOA1, malicious EOA2 practically cannot set the x without breaking `keecak256`. 89 | 90 | Because setting x requires EOA2 to either: 91 | 92 | > 1. find `call2_name` such that `slot2 = keecak256(EOA2 + call2_name)` == `slot1 = keecak256(EOA1 + call1_name)` 93 | > 1. The attack works by simply writing over `slot2` and `slot2 + 1`. Note the attack requires `slot1` be unlocked (or not `0x1`) before `slot2` gets overwritten 94 | > 1. In other words, the attack works because the lock is not set and the only requirement is the attacker finds the right name to overwrite the slots 95 | > 1. find `call2_name ` such that `slot2 = keecak256(EOA2 + call2_name)` == `slot1 + 1 = keecak256(EOA1 + call1_name) + 1` 96 | > 1. This case is more subtle. For this attack to work, EOA1 has to first set `slot1 + 1`, i.e. the data slot for call1, to a non `0x1` 97 | > 1. `slot2` == `slot1 + 1` being unlocked implies both `slot2` (call2's meta) can now write over (call1's data). (`slot2 + 1` doesn't cause any harm in this case) 98 | 99 | For reference, these are known as preimage attacks - given y, find an x such that h(x) = y. 100 | 101 | `keecak256/sha3-256` are known to have 256-bit preimage resistance, meaning time complexity 2^256. Source: [wikipedia](https://en.wikipedia.org/wiki/SHA-3#Instances), [wikipedia](https://en.wikipedia.org/wiki/Preimage_attack). 102 | 103 | Finally, the actually intented effect of this auth system: 104 | 105 | > to set at e.g. dpath `.free`, an EOA must call the root zone contract object to `set`. since EOA needs the root zone contract object to set name under the root zone, and you need the free zone contract object to set name under the free zone etc. 106 | 107 | > The application of this effect is that a `get` user can rely on the locked values that are `walk`ed from root zone being immutable, provided all of these "work": 108 | > 1. Ethereum consensus 109 | > 1. user's Ethereum client 110 | > 1. every zone contract object involved in the dpath 111 | 112 | ``` 113 | L41: mstore(0, caller()) 114 | L42: mstore(32, name) 115 | L43: let slot := keccak256(0, 64) 116 | ``` 117 | 118 | ``` 119 | │2c|▶CALLER 120 | │2d| PUSH1(0x00) 121 | │2f| MSTORE // memory[0:32] = CALLER i.e. zone or end user data 122 | │30| DUP3 123 | │31| PUSH1(0x20) 124 | │33| MSTORE // memory[32:64] = 0x31... i.e. name 125 | │34| PUSH1(0x40) // Push 64. 126 | │36| PUSH1(0x00) // Push 0. 127 | │38| SHA3 // Push to stack, keccak256 of the byte seqence `zone` followed by `name`. 128 | ``` 129 | 130 | Before `0x2c` 131 | ``` 132 | ┌Stack: 3─────────────────────────────────────────────────────────────────────────────────────────── 133 | │00| 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 134 | │01| 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 135 | │02| 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 136 | 137 | ┌Memory (max expansion: 96 bytes)─────────────────────────────────────────────────────────────────── 138 | │00| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 139 | │20| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 140 | │40| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 141 | ``` 142 | 143 | Before `0x38` 144 | ``` 145 | ┌Stack: 5──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 146 | │00| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | offset │ 147 | │01| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 | size │ 148 | │02| 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | │ 149 | │03| 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | │ 150 | │04| 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | │ 151 | │ │ 152 | │ │ 153 | │ │ 154 | └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 155 | 156 | ┌Memory (max expansion: 96 bytes)──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 157 | │00| 00 00 00 00 00 00 00 00 00 00 00 00 b4 c7 9d ab 8f 25 9c 7a ee 6e 5b 2a a7 29 82 18 64 22 7e 84 │ 158 | │20| 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ 159 | │40| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 │ 160 | 161 | ``` 162 | 163 | # dmap.sol:L44 164 | 165 | ``` 166 | L44 log4(0, 0, caller(), name, meta, data) 167 | ``` 168 | 169 | ``` 170 | │39|▶DUP2 171 | │3a| DUP4 172 | │3b| DUP6 173 | │3c| CALLER 174 | │3d| PUSH1(0x00) 175 | │3f| DUP1 176 | │40| LOG4 177 | ``` 178 | 179 | Before `0x39` 180 | ``` 181 | ┌Stack: 4──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 182 | │00| 89 96 19 c3 e7 fd 33 9f 93 01 af 06 29 79 50 0f 51 ce d5 ed 61 f1 14 ff 8e 8a c9 98 d6 b2 de ef | │ 183 | │01| 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | dup_value │ 184 | │02| 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | │ 185 | │03| 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | │ 186 | │ │ 187 | │ │ 188 | │ │ 189 | │ │ 190 | └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 191 | 192 | ┌Memory (max expansion: 96 bytes)──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 193 | │00| 00 00 00 00 00 00 00 00 00 00 00 00 b4 c7 9d ab 8f 25 9c 7a ee 6e 5b 2a a7 29 82 18 64 22 7e 84 │ 194 | │20| 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ 195 | │40| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 │ 196 | 197 | ``` 198 | 199 | Before `0x40` 200 | ``` 201 | ┌Stack: 10─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 202 | │00| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | offset │ 203 | │01| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | size │ 204 | │02| 00 00 00 00 00 00 00 00 00 00 00 00 b4 c7 9d ab 8f 25 9c 7a ee 6e 5b 2a a7 29 82 18 64 22 7e 84 | topic1 │ 205 | │03| 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | topic2 │ 206 | │04| 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | topic3 │ 207 | │05| 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | topic4 │ 208 | │06| 89 96 19 c3 e7 fd 33 9f 93 01 af 06 29 79 50 0f 51 ce d5 ed 61 f1 14 ff 8e 8a c9 98 d6 b2 de ef | │ 209 | │07| 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | │ 210 | └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 211 | 212 | ┌Memory (max expansion: 96 bytes)──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 213 | │00| 00 00 00 00 00 00 00 00 00 00 00 00 b4 c7 9d ab 8f 25 9c 7a ee 6e 5b 2a a7 29 82 18 64 22 7e 84 │ 214 | │20| 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ 215 | │40| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 │ 216 | ``` 217 | 218 | 219 | 220 | # IMPORTANT: dmap.sol:L45 221 | 222 | Sstoring `data` at `slot + 1` 223 | ``` 224 | L45 sstore(add(slot, 1), data) 225 | ``` 226 | 227 | ``` 228 | │41|▶DUP2 // DUP 0x33... i.e. data 229 | │42| PUSH1(0x01) 230 | │44| DUP3 // DUP 0x8969...deef i.e. slot 231 | │45| ADD 232 | │46| SSTORE // offset: slot + 1, value: data 233 | ``` 234 | 235 | Before `0x41` 236 | ``` 237 | ┌Stack: 4──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 238 | │00| 89 96 19 c3 e7 fd 33 9f 93 01 af 06 29 79 50 0f 51 ce d5 ed 61 f1 14 ff 8e 8a c9 98 d6 b2 de ef | │ 239 | │01| 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | dup_value │ 240 | │02| 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | │ 241 | │03| 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | │ 242 | 243 | ┌Memory (max expansion: 96 bytes)──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 244 | │00| 00 00 00 00 00 00 00 00 00 00 00 00 b4 c7 9d ab 8f 25 9c 7a ee 6e 5b 2a a7 29 82 18 64 22 7e 84 │ 245 | │20| 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ 246 | │40| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 │ 247 | ``` 248 | 249 | Before `0x46` 250 | ``` 251 | ┌Stack: 6──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 252 | │00| 89 96 19 c3 e7 fd 33 9f 93 01 af 06 29 79 50 0f 51 ce d5 ed 61 f1 14 ff 8e 8a c9 98 d6 b2 de f0 | key │ 253 | │01| 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | value │ 254 | │02| 89 96 19 c3 e7 fd 33 9f 93 01 af 06 29 79 50 0f 51 ce d5 ed 61 f1 14 ff 8e 8a c9 98 d6 b2 de ef | │ 255 | │03| 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | │ 256 | │04| 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | │ 257 | │05| 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | │ 258 | 259 | ┌Memory (max expansion: 96 bytes)──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 260 | │00| 00 00 00 00 00 00 00 00 00 00 00 00 b4 c7 9d ab 8f 25 9c 7a ee 6e 5b 2a a7 29 82 18 64 22 7e 84 │ 261 | │20| 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ 262 | │40| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 │ 263 | ``` 264 | 265 | # IMPORTANT: dmap.sol:L46-L49 266 | 267 | Check if: 268 | 1. this call is a `set` call 269 | 1. `slot` is "locked" / non 0x1, 270 | 271 | and if so, set `meta` at `slot`. 272 | 273 | ``` 274 | L46 if iszero(or(xor(100, calldatasize()), and(LOCK, sload(slot)))) { 275 | L47 sstore(slot, meta) 276 | L48 return(0, 0) 277 | L49 } 278 | ``` 279 | 280 | ``` 281 | │47|▶DUP1 282 | │48| SLOAD // sload(slot) 283 | │49| PUSH1(0x01) 284 | │4b| AND // and(LOCK, sload(slot)) 285 | │4c| CALLDATASIZE 286 | │4d| PUSH1(0x64) 287 | │4f| XOR // 0 if CALLDATASIZE is 100 288 | │50| OR // 0 because both 0 289 | │51| PUSH1(0x58) 290 | │53| JUMPI // Doesn't jump because of 0 291 | │54| DUP3 // DUP 0x32... i.e. meta 292 | │55| DUP2 // DUP 0x8996...deef i.e. slot 293 | │56| SSTORE 294 | │57| STOP // contract finishes 295 | │END CALL 296 | ``` 297 | 298 | Before `0x47` 299 | ``` 300 | ┌Stack: 4──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 301 | │00| 89 96 19 c3 e7 fd 33 9f 93 01 af 06 29 79 50 0f 51 ce d5 ed 61 f1 14 ff 8e 8a c9 98 d6 b2 de ef | dup_value │ 302 | │01| 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | │ 303 | │02| 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | │ 304 | │03| 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | │ 305 | 306 | ┌Memory (max expansion: 96 bytes)──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 307 | │00| 00 00 00 00 00 00 00 00 00 00 00 00 b4 c7 9d ab 8f 25 9c 7a ee 6e 5b 2a a7 29 82 18 64 22 7e 84 │ 308 | │20| 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ 309 | │40| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 │ 310 | ``` 311 | 312 | Before `0x50` 313 | ``` 314 | ┌Stack: 6──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 315 | │00| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | a │ 316 | │01| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | b │ 317 | │02| 89 96 19 c3 e7 fd 33 9f 93 01 af 06 29 79 50 0f 51 ce d5 ed 61 f1 14 ff 8e 8a c9 98 d6 b2 de ef | │ 318 | │03| 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | │ 319 | │04| 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | │ 320 | │05| 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | │ 321 | 322 | ┌Memory (max expansion: 96 bytes)──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 323 | │00| 00 00 00 00 00 00 00 00 00 00 00 00 b4 c7 9d ab 8f 25 9c 7a ee 6e 5b 2a a7 29 82 18 64 22 7e 84 │ 324 | │20| 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ 325 | │40| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 │ 326 | ``` 327 | 328 | Before `0x56` / `SSTORE` 329 | ``` 330 | ┌Stack: 6──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 331 | │00| 89 96 19 c3 e7 fd 33 9f 93 01 af 06 29 79 50 0f 51 ce d5 ed 61 f1 14 ff 8e 8a c9 98 d6 b2 de ef | key │ 332 | │01| 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | value │ 333 | │02| 89 96 19 c3 e7 fd 33 9f 93 01 af 06 29 79 50 0f 51 ce d5 ed 61 f1 14 ff 8e 8a c9 98 d6 b2 de ef | │ 334 | │03| 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | │ 335 | │04| 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | │ 336 | │05| 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | │ 337 | 338 | ┌Memory (max expansion: 96 bytes)──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 339 | │00| 00 00 00 00 00 00 00 00 00 00 00 00 b4 c7 9d ab 8f 25 9c 7a ee 6e 5b 2a a7 29 82 18 64 22 7e 84 │ 340 | │20| 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ 341 | │40| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 │ 342 | ``` 343 | 344 | # Appendiex 345 | 346 | - Deployed `dmap` contract object: https://etherscan.io/address/0x90949c9937A11BA943C7A72C3FA073a37E3FdD96#code 347 | - Tools: 348 | - code stepper: https://book.getfoundry.sh/forge/debugger.html. 349 | - Opcode reference: https://ethervm.io/ 350 | - I find Geth implementation helpful for clarifying the opcode's semantics - https://github.com/ethereum/go-ethereum/blob/master/core/vm/instructions.go 351 | - Future reading: https://docs.soliditylang.org/en/v0.8.14/yul.html 352 | --------------------------------------------------------------------------------