如何读取智能合约存储

2018-08-10 18:20 区块链 技术 160765 收藏

智能合约是区块链技术的重要应用,所有部署在EVM上的合约都有专门用于存放状态的存储,本次区块链安全团队BUGX.IO给大家分享这个读取思路。

一、前言

所有部署在EVM上的合约都有专门用于存放状态(state)的存储(storage)。这里是一个示例如何使用web3 js库 `eth.getStorageAt()`方法读取这个存储。

二、示例代码

示例合约代码位于: ropsten. etherscan. io/ address/ 0xf1f5896ace3a78c347eb7eab503450bc93bd0c3b 

1.png

三、具体分析

从上面的示例代码里,我们可以看到有11个状态变量,现在我们逐个查看存储中的状态。

执行:

npm install web3@ 0.20. 1 

node storage. js 

// storage.js

const Web3 = require('web3');

const web3 = new Web3(new Web3.providers.HttpProvider("https://ropsten.infura.io/0x88f9c0F375FDF741b1E218aC66f30408F5a55527")); // 你的钱包账户地址

let contractAddress = '0xf1f5896ace3a78c347eb7eab503450bc93bd0c3b';

let storage = '';

for (index = 0; index < 10; index ){

 storage = web3.eth.getStorageAt(contractAddress, index)

 console.log(`[${index}]` storage)

}

// result:

// [0] 0x000000000000000000000000000000000000000000000000000000000000000f

// [1] 0x00000000000000000000000059b92d9a0000000000000000000000000000429f

// [2] 0x0000000000000000000000000000000074657374310000000000000000000000

// [3] 0x7465737431323336000000000000000000000000000000000000000000000000

// [4] 0x6c65747320737472696e6720736f6d657468696e67000000000000000000002a

// [5] 0x0000000000000000000000000000000000000000000000000000000000000000

// [6] 0x0000000000000000000000000000000000000000000000000000000000000000

// [7] 0x0000000000000000000000000000000000000000000000000000000000000002

// [8] 0x0000000000000000000000000000000000000000000000000000000000000002

// [9] 0x0000000000000000000000000000000000000000000000000000000000000000

(1) Index 0 — storeduint1

uint storeduint1 = 15;

解码代码如下:

bytecode = '0x000000000000000000000000000000000000000000000000000000000000000f';

console. log( 'DEC:'  web3. toDecimal( bytecode));

// DEC: 15 

(2)constuint

uint constant constuint = 16;

常值不存放在存储中,仅在代码中可用。

(3)index 1 — investmentsLimit, investmentsDeadlineTimeStamp

在 Index 1 中合并了这两个属性以优化存储的使用。

uint128 investmentsLimit = 17055;

uint32 investmentsDeadlineTimeStamp = uint32( now);

解码代码如下:

bytecode = '0x00000000000000000000000059b92d9a';

console. log( 'DEC:'  web3. toDecimal( bytecode));

bytecode = '0x0000000000000000000000000000429f';

console. log( 'DEC:'  web3. toDecimal( bytecode));

// DEC: 1505308058 and 17055 

(4) index 2 — string1

bytes16 string1 = 'test1';

解码代码如下:

bytecode = '0x0000000000000000000000000000000074657374310000000000000000000000';

console. log( 'ASCII:'  web3. toAscii( bytecode))

// ASCII: test1 

(5)index 3 — string2

bytes32 string2 = 'test1236';

解码代码如下:

bytecode = '0x7465737431323336000000000000000000000000000000000000000000000000';

console. log( 'ASCII:'  web3. toAscii( bytecode));

// ASCII: test1236 

(6) index 4 — string3

string string3 = 'lets string something';

解码代码如下:

bytecode = '0x6c65747320737472696e6720736f6d657468696e67000000000000000000002a';

console. log( 'ASCII:'  web3. toAscii( bytecode));

// ASCII:lets string something* 

字节码最后的符号 2a (dec 42) 用于表示存储的字符串长度。

(7)index 5 — uints1

mapping 类型的声明形式为: `mapping(_KeyType => _ValueType)` 

示例中的代码为:

uint[] uintarray;

address address1 = 0xbccc714d56bc0da0fd33d96d2a87b680dd6d0df6;

uints1[ address1] = 88;

mapping 类型有不同的索引形式,需要不同的读取方式。本例中,KeyType为address,ValueType为uint,要读取mapping值88,那你需要知道KeyType的值,即address1,否则是不可能读取得到的。

解码代码如下:

index = '0000000000000000000000000000000000000000000000000000000000000005' 

key = '00000000000000000000000xbccc714d56bc0da0fd33d96d2a87b680dd6d0df6' 

let newKey = web3. sha3( key  index, { "encoding" : "hex"})

console. log( web3. eth. getStorageAt( contractAddress, newKey))

console. log( 'DEC: '  web3. toDecimal( web3. eth. getStorageAt( contractAddress, newKey)))

// result: 

// 0x0000000000000000000000000000000000000000000000000000000000000058 

// DEC: 88 

(8) index6 — structs1

相关代码:

struct DeviceData {

string deviceBrand;

string deviceYear;

string batteryWearLevel;

}

address address1 = 0xbccc714d56bc0da0fd33d96d2a87b680dd6d0df6;

var dev1 = DeviceData( 'deviceBrand', 'deviceYear', 'wearLevel');

structs1[ address1] = dev1;

另一种形式的mapping类型,通过增加newkey的方式去读取里面的其它内容。

需要先安装bignumber模块: `npm install --save bignumber.js` 

2.png

(9)index 7— uintarray

uint[] uintarray;

uintarray. push( 8000);

uintarray. push( 9000);

解码代码如下:

const Web3 = require('web3');

const web3 = new Web3(new Web3.providers.HttpProvider("https://ropsten.infura.io/0x88f9c0F375FDF741b1E218aC66f30408F5a55527")); // 你的钱包账户地址

let contractAddress = '0xf1f5896ace3a78c347eb7eab503450bc93bd0c3b';

var BigNumber = require('bignumber.js');

function increaseHexByOne(hex) {

 let x = new BigNumber(hex)

 let sum = x.plus(1)

 let result = '0x' sum.toString(16)

 return result

}

// 先读取 item 数

index = "7"

console.log(web3.eth.getStorageAt(contractAddress, index))

// result:

// 0x0000000000000000000000000000000000000000000000000000000000000002

// This array has 2 items

// 将index转为sha3散列,可以读取数组的值

index = "0000000000000000000000000000000000000000000000000000000000000007"

let newKey = web3.sha3(index, {"encoding":"hex"})

console.log(web3.eth.getStorageAt(contractAddress, newKey))

console.log('DEC: '  web3.toDecimal(web3.eth.getStorageAt(contractAddress, newKey)))

// result:

// 0x0000000000000000000000000000000000000000000000000000000000001f40

// DEC: 8000

// 通过增加sha3散列值的形式读取其它位置的值

newKey = increaseHexByOne(web3.sha3(index, {"encoding":"hex"}))

console.log(web3.eth.getStorageAt(contractAddress, newKey))

console.log('DEC: ' web3.toDecimal(web3.eth.getStorageAt(contractAddress, newKey)))

// result:

// 0x0000000000000000000000000000000000000000000000000000000000002328

// DEC: 9000

(10)index 8 — deviceDataArray

DeviceData[] deviceDataArray;

var dev1 = DeviceData( 'deviceBrand', 'deviceYear', 'wearLevel');

var dev2 = DeviceData( 'deviceBrand2', 'deviceYear2', 'wearLevel2');

deviceDataArray. push( dev1);

deviceDataArray. push( dev2);

解码过程如下:

3.png

结果如下:

图片1.png

本文为作者“BUGX区块安全”,原创文章,转载时请保留本声明及附带文章链接。 内容仅供读者参考,并非投资建议,本网站将保留所有法律权益。

评论

共9条评论

查看更多

BUGX区块安全

BUGX.IO是一家致力于区块链领域的安全公司。

文章 粉丝


近期文章
7X24快讯 查看更多>
跳到顶
正在载入...