结合:
Polygon zkEVM的虚拟机支持的opcode为:【基本与以太坊虚拟机opcode对应】
opcode | name | cnt_arith | cnt_binary | cnt_mem_align | cnt_keccak_f | cnt_padding_pg | cnt_poseidon_g | is_dynamic |
---|---|---|---|---|---|---|---|---|
0x00 | STOP | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x01 | ADD | 0 | 1 | 0 | 0 | 0 | 0 | false |
0x02 | MUL | 1 | 0 | 0 | 0 | 0 | 0 | false |
0x03 | SUB | 0 | 1 | 0 | 0 | 0 | 0 | false |
0x04 | DIV | 1 | 2 | 0 | 0 | 0 | 0 | false |
0x05 | SDIV | 1 | 8 | 0 | 0 | 0 | 0 | false |
0x06 | MOD | 1 | 2 | 0 | 0 | 0 | 0 | false |
0x07 | SMOD | 1 | 8 | 0 | 0 | 0 | 0 | false |
0x08 | ADDMOD | 1 | 3 | 0 | 0 | 0 | 0 | false |
0x09 | MULMOD | 2 | 2 | 0 | 0 | 0 | 0 | false |
0x0a | EXP | 512 | 1025 | 0 | 0 | 0 | 0 | true |
0x0b | SIGNEXTEND | 0 | 6 | 0 | 0 | 0 | 0 | false |
0x10 | LT | 0 | 1 | 0 | 0 | 0 | 0 | false |
0x11 | GT | 0 | 1 | 0 | 0 | 0 | 0 | false |
0x12 | SLT | 0 | 1 | 0 | 0 | 0 | 0 | false |
0x13 | SGT | 0 | 1 | 0 | 0 | 0 | 0 | false |
0x14 | EQ | 0 | 1 | 0 | 0 | 0 | 0 | false |
0x15 | ISZERO | 0 | 1 | 0 | 0 | 0 | 0 | false |
0x16 | AND | 0 | 1 | 0 | 0 | 0 | 0 | false |
0x17 | OR | 0 | 1 | 0 | 0 | 0 | 0 | false |
0x18 | XOR | 0 | 1 | 0 | 0 | 0 | 0 | false |
0x19 | NOT | 0 | 1 | 0 | 0 | 0 | 0 | false |
0x1a | BYTE | 2 | 4 | 0 | 0 | 0 | 0 | false |
0x1b | SHL | 1 | 2 | 0 | 0 | 0 | 0 | false |
0x1c | SHR | 1 | 3 | 0 | 0 | 0 | 0 | false |
0x1d | SAR | 2 | 10 | 0 | 0 | 0 | 0 | false |
0x20 | SHA3 | 192 | 193 | 2 | 2 | 0 | 10 | true |
0x30 | ADDRESS | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x31 | BALANCE | 0 | 0 | 0 | 0 | 0 | 9 | false |
0x32 | ORIGIN | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x33 | CALLER | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x34 | CALLVALUE | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x35 | CALLDATALOAD | 64 | 66 | 0 | 0 | 0 | 0 | true |
0x36 | CALLDATASIZE | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x37 | CALLDATACOPY | - | - | - | 0 | 0 | 0 | true |
0x38 | CODESIZE | 0 | 0 | 0 | 0 | 0 | 252 | true |
0x39 | CODECOPY | 0 | - | - | 0 | 0 | 255 | true |
0x3a | GASPRICE | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x3b | EXTCODESIZE | 0 | 0 | 0 | 0 | 0 | 255 | true |
0x3c | EXTCODECOPY | 0 | - | - | 0 | 11 | 510 | true |
0x3d | RETURNDATASIZE | 0 | 1 | 0 | 0 | 0 | 0 | false |
0x3e | RETURNDATACOPY | - | - | 2 | 0 | 0 | 0 | true |
0x3f | EXTCODEHASH | 0 | 0 | 0 | 0 | 0 | 255 | true |
0x40 | BLOCKHASH | 0 | 0 | 0 | 1 | 0 | 9 | false |
0x41 | COINBASE | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x42 | TIMESTAMP | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x43 | NUMBER | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x44 | DIFFICULTY | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x45 | GASLIMIT | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x46 | CHAINID | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x47 | SELFBALANCE | 0 | 0 | 0 | 0 | 0 | 255 | true |
0x50 | POP | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x51 | MLOAD | 32 | 32 | 1 | 0 | 0 | 255 | true |
0x52 | MSTORE | 32 | 32 | 1 | 0 | 0 | 255 | true |
0x53 | MSTORE8 | 32 | 1 | 1 | 0 | 0 | 255 | false |
0x54 | SLOAD | 0 | 0 | 0 | 0 | 0 | 255 | true |
0x55 | SSTORE | 0 | - | 0 | 0 | 0 | 255 | true |
0x56 | JUMP | 0 | - | 0 | 0 | 0 | 0 | true |
0x57 | JUMPI | 0 | - | 0 | 0 | 0 | 0 | true |
0x59 | MSIZE | 1 | 3 | 0 | 0 | 0 | 0 | false |
0x5a | GAS | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x5b | JUMPDEST | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x60 | PUSH1 | 0 | 3 | 0 | 0 | 0 | 0 | true |
0x61 | PUSH2 | 0 | 4 | 0 | 0 | 0 | 0 | true |
0x62 | PUSH3 | 0 | 5 | 0 | 0 | 0 | 0 | false |
0x63 | PUSH4 | 0 | 2 | 0 | 0 | 0 | 0 | false |
0x64 | PUSH5 | 0 | 4 | 0 | 0 | 0 | 0 | false |
0x65 | PUSH6 | 0 | 5 | 0 | 0 | 0 | 0 | false |
0x66 | PUSH7 | 0 | 6 | 0 | 0 | 0 | 0 | false |
0x67 | PUSH8 | 0 | 3 | 0 | 0 | 0 | 0 | false |
0x68 | PUSH9 | 0 | 5 | 0 | 0 | 0 | 0 | false |
0x69 | PUSH10 | 0 | 6 | 0 | 0 | 0 | 0 | false |
0x6a | PUSH11 | 0 | 7 | 0 | 0 | 0 | 0 | false |
0x6b | PUSH12 | 0 | 4 | 0 | 0 | 0 | 0 | false |
0x6c | PUSH13 | 0 | 6 | 0 | 0 | 0 | 0 | false |
0x6d | PUSH14 | 0 | 7 | 0 | 0 | 0 | 0 | false |
0x6e | PUSH15 | 0 | 8 | 0 | 0 | 0 | 0 | false |
0x6f | PUSH16 | 0 | 5 | 0 | 0 | 0 | 0 | false |
0x70 | PUSH17 | 0 | 7 | 0 | 0 | 0 | 0 | false |
0x71 | PUSH18 | 0 | 8 | 0 | 0 | 0 | 0 | false |
0x72 | PUSH19 | 0 | 9 | 0 | 0 | 0 | 0 | false |
0x73 | PUSH20 | 0 | 6 | 0 | 0 | 0 | 0 | false |
0x74 | PUSH21 | 0 | 8 | 0 | 0 | 0 | 0 | false |
0x75 | PUSH22 | 0 | 9 | 0 | 0 | 0 | 0 | false |
0x76 | PUSH23 | 0 | 10 | 0 | 0 | 0 | 0 | false |
0x77 | PUSH24 | 0 | 7 | 0 | 0 | 0 | 0 | false |
0x78 | PUSH25 | 0 | 9 | 0 | 0 | 0 | 0 | false |
0x79 | PUSH26 | 0 | 10 | 0 | 0 | 0 | 0 | false |
0x7a | PUSH27 | 0 | 11 | 0 | 0 | 0 | 0 | false |
0x7b | PUSH28 | 0 | 8 | 0 | 0 | 0 | 0 | false |
0x7c | PUSH29 | 0 | 10 | 0 | 0 | 0 | 0 | false |
0x7d | PUSH30 | 0 | 11 | 0 | 0 | 0 | 0 | false |
0x7e | PUSH31 | 0 | 12 | 0 | 0 | 0 | 0 | false |
0x7f | PUSH32 | 0 | 9 | 0 | 0 | 0 | 0 | false |
0x80 | DUP1 | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x81 | DUP2 | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x82 | DUP3 | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x83 | DUP4 | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x84 | DUP5 | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x85 | DUP6 | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x86 | DUP7 | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x87 | DUP8 | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x88 | DUP9 | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x90 | SWAP1 | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x91 | SWAP2 | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x92 | SWAP3 | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x93 | SWAP4 | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x94 | SWAP5 | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x95 | SWAP6 | 0 | 0 | 0 | 0 | 0 | 0 | false |
0x96 | SWAP7 | 0 | 0 | 0 | 0 | 0 | 0 | false |
0xa0 | LOG0 | 0 | - | 0 | 0 | 0 | 0 | true |
0xa1 | LOG1 | 0 | - | 0 | 0 | 0 | 0 | true |
0xa2 | LOG2 | 0 | - | 0 | 0 | 0 | 0 | true |
0xa3 | LOG3 | 0 | - | 0 | 0 | 0 | 0 | true |
0xa4 | LOG4 | 0 | - | 0 | 0 | 0 | 0 | true |
0xf0 | CREATE | - | - | 0 | - | 0 | - | true |
0xf1 | CALL | - | - | 0 | 0 | - | - | true |
0xf2 | CALLCODE | - | - | 0 | 0 | - | - | true |
0xf3 | RETURN | 0 | 0 | 0 | 0 | 0 | 0 | false |
0xf4 | DELEGATECALL | - | - | 0 | 0 | - | - | true |
0xf5 | CREATE2 | - | - | 0 | - | 0 | - | true |
0xfa | STATICCALL | - | - | 0 | 0 | - | - | true |
0xfd | REVERT | 0 | 0 | 0 | 0 | 0 | 0 | false |
0xfe | INVALID | 0 | 1 | 0 | 0 | 0 | 0 | false |
各opcode详细的zkASM实现参见zkevm-rom项目中的opcodes.zkasm
文件。
根据上图可知,0x01 ADD opcode所需最小gas为3。
在Polygon zkEVM中,以太坊虚拟机的0x01 ADD opcode对应的zkASM表示为:【分别调用了Memory二级状态机、Binary二级状态机。】
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
子程序负责执行加法运算,具体实现见:
; 其中tmpZkPC、storeTmp、arithA、arithB、arithRes1、loadTmp均为全局变量。
addARITH:
; 将调用addARITH子程序之前的RR值存入tmpZkPC临时全局变量中。
RR :MSTORE(tmpZkPC)
zkPC+1 => RR :JMP(storeTmp) ; 等效为 CALL(storeTmp),将A/B/C/D/E寄存器的值存储在临时全局变量tmpVarA/B/C/D/E中。
$ => A :MLOAD(arithA)
$ => B :MLOAD(arithB)
$ => E :ADD ; 对应Binary状态机,为byte-wise加法运算。
E :MSTORE(arithRes1)
zkPC+1 => RR :JMP(loadTmp) ; 等效为 CALL(loadTmp),将临时全局变量tmpVarA/B/C/D/E中的值加载到A/B/C/D/E寄存器中。
; 重置RR值 为 调用addARITH子程序之前的RR值(从tmpZkPC临时全局变量中取出)
$ => RR :MLOAD(tmpZkPC)
:JMP(RR) ; 等效为 RETURN
其中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
不过,实际实现时,Polygon zkEVM中设定了一些常量上限值:
; COUNTERS
CONST %MAX_CNT_STEPS = 2**21 ; 最大STEP数
CONST %MAX_CNT_ARITH = %MAX_CNT_STEPS / 32 ; 最多ARITH计算数
CONST %MAX_CNT_BINARY = %MAX_CNT_STEPS / 32 ; 最多BINARY计算数
CONST %MAX_CNT_MEM_ALIGN = %MAX_CNT_STEPS / 32 ; 最多MemAlign计算数
CONST %MAX_CNT_KECCAK_F = (%MAX_CNT_STEPS / 158418) * 9 ; 最多Keccakf计算数
CONST %MAX_CNT_PADDING_PG = (%MAX_CNT_STEPS / 56) ; 最多padding pg计算数,针对Poseidon哈希
CONST %MAX_CNT_POSEIDON_G = (%MAX_CNT_STEPS / 30) ; 最多PoseidonG计算数,针对Poseidon哈希
CONST %MIN_CNT_KECCAK_BATCH = 2 ; 最少Keccak_batch数
; ETHEREUM CONSTANTS
CONSTL %MAX_NONCE = 0xffffffffffffffffn ; 以太坊nonce最大值为2^{64}-1。
因此,实际在zkevm-rom的opcodes.zkasm中对0x01 ADD opcode的实际实现为:【借助了Binary状态机默认是对A和B寄存器进行运算的,进行了优化,使代码更简洁;同时考虑实际应用场景,对相关计数器进行了约束。】
opADD:
%MAX_CNT_BINARY - CNT_BINARY - 1 :JMPN(outOfCounters) ; 对相关计算计数器进行了约束。
%MAX_CNT_STEPS - STEP - 120 :JMPN(outOfCounters) ; 约束了计算复杂度。
SP - 2 :JMPN(stackUnderflow)
SP - 1 => SP
$ => A :MLOAD(SP--)
$ => B :MLOAD(SP)
; Add operation with Arith
$ => E :ADD
E :MSTORE(SP++)
1024 - SP :JMPN(stackOverflow)
GAS-3 => GAS :JMPN(outOfGas)
:JMP(readCode)
[1] zkASM示例
[2] 以太坊黄皮书 https://ethereum.github.io/yellowpaper/paper.pdf
[3] 理解以太坊黄皮书
[3] EVM.Codes