EVM架构
EVM具有基于堆栈的体系结构,它将内存中的值都存储在堆栈上,然后使用PC读取堆栈的操作码去执行合约代码。
EVM可以分为三个可寻址组件:
Virtual ROM:用于存放代码的虚拟只读存储器,在ROM中的code(智能合约)不能更改。
Machine state:相当于RAM,一种易失性的存储器。这里的易失的是指当合约代码执行完毕后,内存的变量会被清除。
World state:所有合约的状态变量存储的地方,这部分数据是永久存储在区块链上的数据。
EVM的字长为256位,这样方便本地哈希和椭圆曲线操作
Machine state
堆栈(stack)
EVM堆栈是用于存储字节码执行过程的中间数据和指令,所有的操作都是在堆栈上执行的。
EVM堆栈的大小是256bit * 1024=28K(字位宽为256,栈深度为1024),堆栈的读写都是以256bit为单位进行的。内联汇编可以使用PUSH、POP、SWAP、DUP指令操作stack。
内存(memory)
EVM内存是线性存储的,可以实现字节级别的寻址。它用于存储临时变量和一些动态大小的数组。solidity可以使用memory来声明内存变量。
内存的字宽是8位。内存一次能读取为256位,而内存一次写入可以为8位或256位。内联汇编能够使用 MSTORE、MSTORE8、MLOAD指令操作memory。
程序计数器(PC)
PC类似于汇编中的寄存器,它指向EVM将要执行的下一条指令。在执行一条指令后,PC通常会增加1 byte(32bit)。其中的例外情况包括JUMP操作码变体,它们将PC重新定位到堆栈顶部指定的位置。
World state
存储(storage)
在以太坊中,每个特定地址的智能合约都有自己的 “存储”,由一个键值存储组成,将 256 位Key映射到 256 位Value。可以通过solidity中定义storage和状态变量去指定变量存储位置。
存储的每次读写都是256bit。内联汇编可以使用SSTORE、SLOAD指令操作storage。
Virtual ROM
合约代码(EVM code)
合约代码是指EVM在本机执行的字节码。在EVM的ROM中只能对合约代码进行读操作。
字节码包含了很多关于合约的信息和逻辑,包括调度器,以及合约元数据。
Calldata
根据以太坊黄皮书描述,calldata
是一个不限制大小的字节数组,用来指定消息调用的输入数据(这个输入数据是指Massage Call输入的data)。calldata与内存不同,是一段只读的可寻址的保存函数调用参数的空间。
一个消息调用交易包括:
data: 一个不限制大小的字节数组,用来指定消息调 用的输入数据,由 Td 表示。
—节选以太坊黄皮书
当一个以太坊合约被外部或EOA调用时,调用者将数据传递给合约,这些数据被存储在calldata区域。在内存或者栈需要使用到该数据时,会通过calldata相关指令集取操作
对calldata操作的指令有三个:
calldatasize
:返回calldata的大小。calldataload
:从calldata中加载32bytes到stack中。calldatacopy
:拷贝一些字节到内存中。
Calldata组成
calldata组成由两部分:
- 前四字节是函数选择器。
- 其余字节是函数输入参数, 每个输入参数长度为32字节。小于32字节的参数会被填充到32字节长度。
1 | // a = 1, b = 2 |
Calldata特性
calldata
区域是只读的,合约不能在执行期间修改它。
读取calldata其实是从calldata区域复制其中的值到堆栈中。
比如:声明一个calldata数组,如果试图修改calldata的参数就会报错。
Messgae Call
在调用者(EOA或合约)发送一个Messgae Call时,会有一个calldata被放入交易的data字段。EVM会为data分配一个只读的空间(大小没有限制) 。EVM能够通过读取calldata中的函数选择器知晓合约中那一个函数的代码能执行。
代码示例
代码逻辑
- msg.data:可以获得整个calldata数据
- event Log(bytes data):返回当前交易的calldata
- 逻辑:用户地址调用合约参数A.example1,输入合约B地址和a,b的值,在example1中会出发Log事件。然后example1会使用call调用B中的add函数,并触发Log事件
- 预测:根据calldata的原理,第一个触发事件会返回example1的函数选择器和a,b两个参数;第二个触发的事件会返回add的函数选择器和a,b两个参数;其中result和第二个Log中的data值相同
1 | // SPDX-License-Identifier: MIT |
事件和结果
输入参数:crt=b_addres,a=1,b=5
第一个Log的data前四位字节为:0xebe5f989
第二个Log的data前四位字节为:0x771602f7
返回的result前四位字节为:0x771602f7,且result的值也与data完成相同
引用
1.ethereumbook/13evm.asciidoc at develop · ethereumbook/ethereumbook (github.com)