Capture the Ether - Mapping
从一道题入门 blockchain

# EVM 存储简介

solidity 编译后的数据
是放在 slot 的结构中
slot 有 2 ^ 256 个
每一个有 256 位

对于 solidity 中的可以在编译期间确定的静态元素
solc 会将其依次放入内存插槽中(slot)

这里 bcd 都是 256 位
所以放在 slot [0…2]
但如果是可变长数据结构如果是 array 或者 map
值会被放在其他地方 为了性能 这里是直接对当前的 idx 取 keccak
比如 keccak(0) = 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563
如果 slot[0] 是 array 的话 那么数据就会被放在
slot[0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563]

注意这里 keccak 是对补齐后的字符串进行 hash 的 而不是对数字
所以需要 padding 一下

1
2
3
4
5
6
7
8
9
function padding(x) {
x = x.toString()
var s = "0x"
for(var i = 0; i < (64 - x.length); i++) {
s += "0"
}
s += x
return s
}

solidity 同样存在和 c 一样内存对齐的概念 不再赘述

# cte-mapping

首先看合约 map 的 kv 可控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
pragma solidity ^0.4.21;

contract MappingChallenge {
bool public isComplete;
uint256[] map;

function set(uint256 key, uint256 value) public {
// Expand dynamic array as needed
if (map.length <= key) {
map.length = key + 1;
}

map[key] = value;
}

function get(uint256 key) public view returns (uint256) {
return map[key];
}
}

连接到合约

set 是可以控制下标 那么给 isComplete 覆盖掉就行了 十分清楚
map 是可变结构 每个成员是存放在 keccak(padding(1)) 依次往上的地方
可以打印看到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
const ethers = require('ethers')
const fs = require('fs-extra')
require('dotenv').config()

var Web3 = require('web3')
var web3 = new Web3("your_rpc_url")
contractAddr = "your_account_addr"

const cl = x => console.log(x)
function padding(x) {
x = x.toString()
var s = "0x"
for(var i = 0; i %3C (64 - x.length); i++) {
s += "0"
}
s += x
return s
}
async function getSlot(idx){
let ret = await web3.eth.getStorageAt(contractAddr, idx)
return ret
}
async function getShaIdx(idx){
let ret = await web3.utils.sha3(padding(idx))
return ret
}

async function main(){
cl(await getSlot(0))
cl(await getSlot(1))
cl(await getShaIdx(1))
}

main()
.then(() => process.exit(0))
.catch((err) => {
console.log(err),
process.exit(1)
})>)
1
2
3
0x0000000000000000000000000000000000000000000000000000000000000000
0x0000000000000000000000000000000000000000000000000000000000000000
0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6

第三个就是我们 map 第一个元素存放的位置
2**256 减一下就是偏移了