1. 引言
结合:
2. 0x01 ADD opcode
其中:
-
δ
\delta
δ为从stack中pop出来的value数,因ADD是对stack的top 2 values求和(除非明确说明,否则是对
2
256
2^{256}
2256取模)。
-
α
\alpha
α为向stack push进去的value数。ADD操作会将二值求和结果再push进stack中。
根据上图可知,0x01 ADD opcode所需最小gas为3。
在Polygon zkEVM中,以太坊虚拟机的0x01 ADD opcode对应的zkASM表示为:
opADD:
; 检查当前stack中确实有至少2个元素,否则跳转到stackUnderflow。
SP - 2 :JMPN(stackUnderflow)
; 将stack pointer值减一
SP - 1 => SP
; 将stack pointer值加载到A寄存器中;再将stack pointer值减一
$ => A :MLOAD(SP--)
; 将stack pointer值加载到C寄存器中
$ => C :MLOAD(SP)
; Add operation with Arith
; 将A寄存器的值存储在Memory状态机中的`arithA`变量中
A :MSTORE(arithA)
; 将C寄存器的值存储在Memory状态机中的`arithB`变量中
C :MSTORE(arithB)
; 调用`addARITH`子程序,负责执行加法运算
:CALL(addARITH)
; 从Memory状态机中的`arithRes1`变量中读取加法运算结果 存入在 E寄存器中
$ => E :MLOAD(arithRes1)
; 将E寄存器的值存储在stack pointer位置,将stack pointer值加一
E :MSTORE(SP++)
; stack空间为1024,若当前stack pointer值大于1024,则跳转到stackOverflow
1024 - SP :JMPN(stackOverflow)
; ADD opcode所需最低gas为3,若执行为ADD操作后GAS为负数,则跳转到outOfGas
GAS-3 => GAS :JMPN(outOfGas)
; 最后但同样重要的是,以下表示继续处理下一指令。
:JMP(readCode)
其中addARITH 子程序负责执行加法运算,具体实现见:
- zkEVM Rom项目中的utils.zkasm
; 其中tmpZkPC、storeTmp、arithA、arithB、arithRes1、loadTmp均为全局变量。
addARITH:
; 将调用addARITH子程序之前的RR值存入tmpZkPC临时全局变量中。
RR :MSTORE(tmpZkPC)
zkPC+1 => RR :JMP(storeTmp) ; 等效为 CALL(storeTmp)
$ => A :MLOAD(arithA)
$ => B :MLOAD(arithB)
$ => E :ADD
E :MSTORE(arithRes1)
zkPC+1 => RR :JMP(loadTmp) ; 等效为 CALL(loadTmp)
; 重置RR值 为 调用addARITH子程序之前的RR值(从tmpZkPC临时全局变量中取出)
$ => RR :MLOAD(tmpZkPC)
:JMP(RR)
其中storeTmp 子程序为将A/B/C/D/E寄存器的值存储在临时全局变量tmpVarA/B/C/D/E中,而loadTmp 为将临时全局变量tmpVarA/B/C/D/E中的值加载到A/B/C/D/E寄存器中:storeTmp:
A :MSTORE(tmpVarA)
B :MSTORE(tmpVarB)
C :MSTORE(tmpVarC)
D :MSTORE(tmpVarD)
E :MSTORE(tmpVarE)
:JMP(RR) ; 等效为 RETURN
loadTmp:
$ => A :MLOAD(tmpVarA)
$ => B :MLOAD(tmpVarB)
$ => C :MLOAD(tmpVarC)
$ => D :MLOAD(tmpVarD)
$ => E :MLOAD(tmpVarE)
:JMP(RR) ; 等效为 RETURN
参考资料
[1] zkASM示例 [2] 以太坊黄皮书 https://ethereum.github.io/yellowpaper/paper.pdf [3] 理解以太坊黄皮书 [3] EVM.Codes
附录:Polygon Hermez 2.0 zkEVM系列博客
|