以太坊 stateObject中Storage存储内容
stateOject中有两个Storage类型的变量,是用来缓存智能合约中所有变量的值的。这个Storage类型的定义如下:
type Storage map[common.Hash]common.Hash
就是一个map,key跟value都是common.Hash类型,其实就是[]byte。这个map只有在EVM执行SSTORE指令时候会赋值,用于记录变量的值。那么这里面的key跟value具体是什么内容呢?经研究,结果如下:
- 对于基本数据类型,key = 合约中的变量声明位置(从0开始)
- 对于map类型,key = SHA3(map中的关键字,变量声明位置),也就是把map中的关键字和变量声明位置拼在一起成为一个64字节的[]byte,然后计算hash值
- value不管在哪种情况下都存储变量的实际值
可以使用browser-solidity查看编译出来的EVM指令:
https://ethereum.github.io/browser-solidity
在编辑器中写测试代码:
pragma solidity ^0.4.0; contract Demo { int a; int x; mapping(address => int) b; function myfunc() public { a = 8; x = 9; b[123] = 1; } }
点击右边的“Start to compile”,然后点击“Details”就可以查看编译出来的EVM指令。以map为例,逐条分析指令就可以看出SSTORE具体存储的key和value是如何生成的:
// 压栈返回值 PUSH 1 1 stack [1] // 压栈变量position PUSH 2 b stack [2 1] // 压栈内存存储起点 PUSH 0 b[123] stack [0 2 1] // 压栈map索引值(即123) PUSH 7B 123 stack [7B 0 2 1] // 压栈mask PUSH FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF b[123] stack [FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 7B 0 2 1] // 索引值 & mask,弹出2个值,压栈与的结果 AND b[123] stack [7B 0 2 1] // 复制第二个值并压栈 DUP2 b[123] stack [0 7B 0 2 1] // 在内存0位置存储7B(即map索引值),弹出2个值 MSTORE b[123] stack [0 2 1] store {7B} // 压栈内存偏移,0x20 = 32字节 PUSH 20 b[123] stack [20 0 2 1] store {7B} // 计算内存偏移,弹出2个值,压栈相加的结果 ADD b[123] stack [20 2 1] store {7B} // 交换栈顶2个值 SWAP1 b[123] stack [2 20 1] store {7B} // 复制第二个值并压栈 DUP2 b[123] stack [20 2 20 1] store {7B} // 在内存0x20位置存储2(即变量position) MSTORE b[123] stack [20 1] store {7B 2} // 压栈内存偏移,0x20 = 32字节 PUSH 20 b[123] stack [20 20 1] store {7B 2} // 计算内存偏移,弹出2个值,压栈相加的结果 ADD b[123] stack [40 1] store {7B 2} // 压栈数据读取offset PUSH 0 b[123] stack [0 40 1] store {7B 2} // offset=0, size=0x40,也就是从store中读取2个32字节的值, // 然后计算SHA3,结果压栈 KECCAK256 b[123] stack [sha3(0x0000007B00000002) 1] store {7B 2} // 复制第二个值(即要赋的值)并压栈 DUP2 b[123] = 1 stack [1 sha3 1] store {7B 2} // 交换栈顶2个值 SWAP1 b[123] = 1 stack [sha3 1 1] store {7B 2} // 以sha3为key,1为value,存入StateDB,同时弹栈 SSTORE b[123] = 1 stack [1] store {7B 2} // 弹出返回值 POP b[123] = 1 stack [] store {7B 2}
以太坊挖矿流程的基本框架参见下图:其中涉及到的组件之间的关系可以参见下面的UML图:1. Miner启动打包在eth Service初始化的时候,会创建一个Miner实例:eth.miner = miner.New(e ...