1. 引言
首先需了解以下基本概念:
- 1)区块链
- 2)世界状态
- 3)账号
- 4)交易
- 5)消息
- 6)去中心化账本
- 7)原子性和顺序性
1.1 何为以太坊区块链?
以太坊可看成是基于交易的状态机,交易代表了2个状态间的valid arc:
将多笔交易整理打包为区块,区块为package of data:
从状态的角度来看,可将以太坊看成是状态链(chain of states):
从实现的角度来看,还可将以太坊看成是“BLOCKCHAIN”(chain of blocks):
从账本的角度来看,还可将以太坊看成是“stack of transactions”:
1.2 何为世界状态?
世界状态(World state)为address与account state之间的mapping:
可从多个角度来看待world state:
- mapping角度
- table表格角度
- object对象角度
1.3 何为账号?
账号(Account)为world state的object对象表示:
账号状态中可包含EVM code和Account storage:
以太坊中主要有2种账号状态:
- Externally owned account(EOA):由某个私钥控制。不允许包含EVM code。
- Contract account(CA,合约账号):包含了EVM code。由EVM code控制。
账号地址以160-bit code来表示:
1.4 何为交易?
交易为经密码学签名的单个指令:
交易由external actor来提交:
有两种实用的交易类型:
- contract creation交易
- message call交易
交易中包含的字段有:
- nonce
- gasPrice
- gasLimit
- to:若为contract creation交易,该值为0;否则为160 bit address。
- value:为tranasferred wei(ether)
- v, r, s:交易签名
- init or data:contract creation 或 message call
1.4.1 contract creation交易
contract creation交易示意为:
1.4.2 message call交易
message call交易示意为:
1.5 何为message?
message的来源主要有:
- 由交易触发的关联消息
- 由EVM code触发的消息
消息之间的发送和接收方可分为以下四种场景:
- 1)由EOA通过交易发送消息给EOA
- 2)由EOA通过交易发送消息给CA
- 3)由CA通过EVM code发送消息给EOA
- 4)由CA通过EVM code发送消息给CA
1.6 何为去中心化账本?
区块链不仅为a globally shared, transactional database:
区块链还是a globally shared, 去中心化, transactional database:
由去中心化节点构成以太坊P2P网络:
external actors通过以太坊节点来访问以太坊世界:
不过,实际以太坊客户端是通过Web3 API来访问以太坊网络:
1.7 何为原子性和顺序性?
交易的原子性是指:
- 交易为原子式操作,无法分解或中断。
- 交易要么完全执行成功,要么完全失败。(All(complete done)or Nothing(zero effect))
交易的顺序性是指:
- 交易是无法重叠的
- 交易必须按顺序执行
不过,交易的顺序不是按交易发起时间来确定的,交易的顺序是无法保证的,由矿工来决定区块内的交易顺序:
同时,区块间的顺序则由共识算法(如PoW)来决定:
2. 虚拟机
以太坊虚拟机为stack-based, big-endian VM with a word size of 256-bits,并用于运行智能合约。智能合约与普通账号类似,只是当收到某交易时,智能合约会运行EVM bytecode,处理相应的计算以及后续交易。
EVM中的opcode有:
Contract execution starts at the beginning of the bytecode.
Each opcode is encoded as one byte, except for the PUSH opcodes, which take a immediate value.
All opcodes pop their operands from the top of the stack and push their result.
交易的payload可为0或一定字节数的data,用于定义与合约交互的类型即其它额外信息:
- 1)合约创建交易:
交易的data payload为所创建合约自身的bytecode,会运行合约的构造函数、设置合约初始状态、并返回最终的合约bytecode。即,一旦合约部署完毕,构造函数将不展示在合约中。 - 2)合约交互交易:
通常合约会暴露一个public API——为支持用户与合约交互的一组方法。
为与合约交互,用户将提交交易,交易内可包含任意wei数量(包括0),以及某格式与ABI匹配的data payload,定义了交互类型以及其他额外参数。
合约通常由4种方式来处理data:- 1)Call Data:通常为:4字节的method identifier + 序列化的参数。相关指令有:CALLDATALOAD/CALLDATASIZE/CALLDATACOPY。
- 2)Stack:EVM会维护a stack of uint256s来保存本地变量、函数调用参数以及返回地址。区分返回地址与其它变量是复杂的。相关指令有:PUSH1/DUP1/SWAP1/POP等。
- 3)Memory:为uint8数组,用于保存合约执行过程中的临时数据。memory中的数据无法跨交易保存。相关指令有:MLOAD/MOSTROE/MSTORE8。
- 4)Storage:为持久关联map,以uint256为key,uint256为value。所有的合约元素以及mappings都存储在storage中。Storage中的元素可通过
web3.eth.getStorageAt(address, key)
接口来访问获得。相关指令有:SLOAD/SSTORE。
2.1 以太坊虚拟机EVM(Ethereum Virtual Machine)
go-ethereum提供了evm命令行工具对汇编代码进行编译和反汇编,以及执行调试汇编代码:
可使用evm工具直接执行调试汇编代码:
对于message call交易,以太坊虚拟机输入有:
- 相应合约账号的code
- 相应合约账号的storage
- message call交易中的input data
相应的EVM输出会更新到合约账号的stoarge中。
EVM为以太坊合约的运行时环境:
EVM采用简单的栈架构,其中:
- EVM code为Virtual ROM:不变的
- machine state中包含:PC(Program counter)、Gas(Gas available)、Stack、Memory。这些均是可变的。
- 合约账号Storage为World state:是持久保存的。
EVM code可由各种高级语言经相应的编译器编译而来,如:
- Solidity合约源码:对应Solidity编译器
- Viper合约源码:对应Viper编译器
- LLL合约源码:对应LLL编译器
EVM code有2种表示方式:
- 汇编表示
- bytecode表示:EVM本地执行的EVM code为bytecode表示。
EVM的执行模式为:
EVM的machine space中没有寄存器,主要有以下3种资源:
- Stack:为stack memory,最多可容纳 256 bits x 1024个元素。
- Memory:为volatile memory,为byte addressing linear memory。
- (Account)storage:为persistent memory,为256 bits -> 256 bits key-value store。
2.1.1 EVM的stack
stack中最多可存储1024个元素,每个元素为256bits。
所有操作都是基于stack进行的,访问stack的指令有:
- PUSH
- POP
- COPY
- SWAP等等
2.1.2 EVM中的memory
memory为线性的,可按字节寻址。访问memory的指令有:
- MSTORE:256-bit store
- MSTORE8:8-bit store
- MLOAD:256-bit load
memory中所有的位置都是以0为初始值,定义明确的。
2.1.3 EVM中的(account)storage
storage为key-value store,将256-bit words映射到256-bit words。
访问storage的指令有:
- SSTORE:256-bit store
- SLOAD:256-bit load
storage中所有的位置都是以0为初始值,定义明确的。
2.2 Message call
EVM可向其它账号发送消息,message call的深度限制为不超过1024层。
message call由CALL
指令触发,参数和返回值均通过memory来传输:
2.3 异常情况
EVM执行EVM code时存在多种异常情况,如:
- 1)无效的jump目标
- 2)out-of-gas
- 3)无效的指令
- 4)stack underflow等
2.4 Gas and fee
以太坊中所有的程序计算都需要付费(以gas来表示):
如EVM中的操作需要消耗gas、message call需要更多的gas、操作storage需要更多的gas。
2.5 EVM的输入和输出
外部数据通过message call输入到EVM中,EVM可输出log,同时也可返回值给Caller EVM。
input data的指令有:
- CALLDATALOAD:指向Stack
- CALLDATACOPY:指向Memory
2.6 字节顺序
EVM中的字节采用big endian顺序(network byte order)。
memory、input data、以及BYTE指令等都采用big endian顺序,而SIGNEXTEND指令则有所不同。
2.6.1 memory的字节顺序
MSTORE8指令是指将LSB字节存入到相应的位置:
2.6.2 input data的字节顺序
2.6.3 BYTE和SIGNEXTEND指令的字节顺序
2.6.4 PUSH指令的字节顺序
PUSH系列指令:PUSH1/PUSH4/PUSH32均采用right-aligned,big endian顺序。
2.7 EVM指令集
EVM指令集通常是256-bit operation。
合约创建和destruct的指令有:
- CREATE
- DELEGTECALL
- CREATE2
Hash指令有:
- SHA3
Shift指令有:
- MUL:如
MUL m (2^n) == m << n
,左移表示乘法指令 - DIV:表示logical right shift
- SDIV:表示arithmetic right shift
其中DIV/SDIV等除法指令,无zero divisional exception。
在EVM内部,存在多个copy指令:
- CALLDATALOAD:将Input data复制到Stack
- CALLDATACOPY:将Input data复制到Memory
- CODECOPY:将EVM code复制到Memory
- EXTCODECOPY:将另一合约的EVM code复制到本合约运行时的Memory中
2.8 Solidity ABI(Application Binary Interface)
参考资料
[1] Ethereum EVM illustrated,可参看github https://github.com/takenobu-hs/ethereum-evm-illustrated
[2] Ethereum Virtual Machine Opcodes
[3] Learning Ethereum Virtual Machine Opcodes With EVM Puzzles