利用web3读取storage 参考文章 参考 wiki部分
合约 看完wiki在网上找了个简单例子。
合约如下,地址为0xB70D357314CA537E275E1F5891C35026Bf61ad25
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 40 41 42 43 44 45 pragma solidity ^0.4.0; contract testStorage { uint storeduint1 = 15; uint constant constuint = 16; uint128 investmentsLimit = 17055; uint32 investmentsDeadlineTimeStamp = uint32(now); bytes16 string1 = 'test1'; bytes32 string2 = 'test1236'; string string3 = 'lets string something'; mapping (address => uint) uints1; mapping (address => DeviceData) structs1; uint[] uintarray; DeviceData[] deviceDataArray; struct DeviceData { string deviceBrand; string deviceYear; string batteryWearLevel; } function testStorage() { address address1 = 0xbccc714d56bc0da0fd33d96d2a87b680dd6d0df6; address address2 = 0xaee905fdd3ed851e48d22059575b9f4245a82b04; uints1[address1] = 88; uints1[address2] = 99; var dev1 = DeviceData('deviceBrand', 'deviceYear', 'wearLevel'); var dev2 = DeviceData('deviceBrand2', 'deviceYear2', 'wearLevel2'); structs1[address1] = dev1; structs1[address2] = dev2; uintarray.push(8000); uintarray.push(9000); deviceDataArray.push(dev1); deviceDataArray.push(dev2); } }
这份代码里一共有10个变量,首先要注意的是,被标记为constant的变量是不会存在于storage的,他在代码中出现的地方会在编译时被编译器替换上。
分析 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 第一个变量为uint 单独占用一个slot slot 0 0x000000000000000000000000000000000000000000000000000000000000000f 第二变量为constant 不会放在storage,可参考上述文章。 第三个变量uint128和第四个变量uint32会放在一个slot里面,因为如果 bytes 和 string 的数据很短,那么它们的长度也会和数据一起存储到同一个插槽。(如果一个slot就能装下的话) slot 1 0x00000000000000000000000062f1f5880000000000000000000000000000429f 后面两个变量是bytes16和bytes32,都是直接占用一个slot(高位对齐) slot 2 0x0000000000000000000000000000000074657374310000000000000000000000 slot 3 0x7465737431323336000000000000000000000000000000000000000000000000 然后是string变量,数据长度小于等于 31 字节, 则它存储在高位字节(左对齐),最低位字节存储 length * 2 slot 4 0x6c65747320737472696e6720736f6d657468696e67000000000000000000002a 然后是两个映射类型变量,都会占据一个slot但是真正的起始位置为keccak256(k . p),这里k,p就是键和键值。 slot 5 0x0000000000000000000000000000000000000000000000000000000000000000 slot 6 0x0000000000000000000000000000000000000000000000000000000000000000 然后是一个uint256数组,对于形如 uint[] b; 的动态数组,其同样会占用对应位置 p 处的插槽,用以储存数组的长度,而数组真正的起始点会位于 keccak256(p) 处. 这里由于在构造函数中先push了两个数所以长度初始就是2. slot 7 0x0000000000000000000000000000000000000000000000000000000000000002 最后是一个结构体数组,存储方式和动态数组是一样的,无非就是每个结构体含有多个变量,所以数组里的一个索引的值会占用多个slot。 在构造函数中也给结构体数组push了两个元素,所以初始长度为2。 slot 8 0x0000000000000000000000000000000000000000000000000000000000000002
计算 所以如何计算数组或者映射的起始位置呢。这里可以使用合约也可以使用web3.js或者python下的web3.
使用如下合约1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 pragma solidity ^0.4.24; contract calc{ function read_slot(uint k) public view returns (bytes32 res) { assembly { res := sload(k) } } function cal_addr(uint k, uint p) public pure returns(bytes32 res) { res = keccak256(keccak256(abi.encodePacked(k, p))); } function calc_1(uint k, uint p) public pure returns(bytes32 res) { res = keccak256(abi.encodePacked(k, p)); } function cal_addr(uint p) public pure returns(bytes32 res) { res = keccak256(abi.encodePacked(p)); } function cal_uint(uint p) public pure returns(bytes32 res) { res = keccak256(p); } }
调用函数可以获取uints1[address1] = 0xafef6be2b419f4d69d56fe34788202bf06650015554457a2470181981bcce7ef
var Web3 = require ('web3' );var web3 = new Web3(HTTPProvider(your rpc)); index = '0000000000000000000000000000000000000000000000000000000000000005' key = '000000000000000000000000bccc714d56bc0da0fd33d96d2a87b680dd6d0df6' newKey = web3.utils.sha3(key + index, {"encoding" :"hex" })console .log(newKey);
也能得到相同的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from Crypto.Util.number import bytes_to_longfrom web3 import Web3,HTTPProviderfrom eth_abi import encode_abifrom eth_utils import keccakdef bytesTohex (data ): return hex (bytes_to_long(data)) index = '0000000000000000000000000000000000000000000000000000000000000005' key = '000000000000000000000000bccc714d56bc0da0fd33d96d2a87b680dd6d0df6' contract_address = "0xB70D357314CA537E275E1F5891C35026Bf61ad25" slot5 = Web3.keccak(encode_abi(['address' ,'uint' ], ['0xbccc714d56bc0da0fd33d96d2a87b680dd6d0df6' ,0x05 ])) slot7 = keccak(encode_abi(['uint' ], [0x07 ])) slot8 = keccak(encode_abi(['uint' ], [0x08 ]))print (bytesTohex(slot5))print (bytesTohex(slot7))print (bytesTohex(slot8))
所以可以计算得到
对于uints1[address1]位置在0xafef6be2b419f4d69d56fe34788202bf06650015554457a2470181981bcce7ef uints1[address2]位置在0xb01ced36af2255129ceb004046ab791f2c883dd74d458026d7b81c7aaff3566b structs1[address1]位置在0x720c187f2880b2567f9fccc279625ea13024b8b82a6f73e26d9ca6d82ede1cc5 structs1[address2]位置在0x0dd425230ba48d6e0641f6db78455be91ddb278d66cd6b80d80fdf73dde88f45 uintarray的第一个元素的位置在0xa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688 deviceDataArray的第一个位置在0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3
读取storage 利用代码,getStorageAt第二个参数就是上面计算得到的slot索引值
var Web3 = require ('web3' );var web3 = new Web3(); contract_address = "0xB70D357314CA537E275E1F5891C35026Bf61ad25" const getStorage = (async ()=>{ for (var i = 0 ; i<10 ;i++){ await web3.eth.getStorageAt(contract_address, i).then(console .log) } }) getStorage();
或者用python都行。
data1 = w3.eth.getStorageAt(contract_address ,slot5 )