1. 引言

结合:

Polygon zkEVM的虚拟机支持的opcode为:【基本与以太坊虚拟机opcode对应】

opcodenamecnt_arithcnt_binarycnt_mem_aligncnt_keccak_fcnt_padding_pgcnt_poseidon_gis_dynamic
0x00STOP000000false
0x01ADD010000false
0x02MUL100000false
0x03SUB010000false
0x04DIV120000false
0x05SDIV180000false
0x06MOD120000false
0x07SMOD180000false
0x08ADDMOD130000false
0x09MULMOD220000false
0x0aEXP51210250000true
0x0bSIGNEXTEND060000false
0x10LT010000false
0x11GT010000false
0x12SLT010000false
0x13SGT010000false
0x14EQ010000false
0x15ISZERO010000false
0x16AND010000false
0x17OR010000false
0x18XOR010000false
0x19NOT010000false
0x1aBYTE240000false
0x1bSHL120000false
0x1cSHR130000false
0x1dSAR2100000false
0x20SHA319219322010true
0x30ADDRESS000000false
0x31BALANCE000009false
0x32ORIGIN000000false
0x33CALLER000000false
0x34CALLVALUE000000false
0x35CALLDATALOAD64660000true
0x36CALLDATASIZE000000false
0x37CALLDATACOPY---000true
0x38CODESIZE00000252true
0x39CODECOPY0--00255true
0x3aGASPRICE000000false
0x3bEXTCODESIZE00000255true
0x3cEXTCODECOPY0--011510true
0x3dRETURNDATASIZE010000false
0x3eRETURNDATACOPY--2000true
0x3fEXTCODEHASH00000255true
0x40BLOCKHASH000109false
0x41COINBASE000000false
0x42TIMESTAMP000000false
0x43NUMBER000000false
0x44DIFFICULTY000000false
0x45GASLIMIT000000false
0x46CHAINID000000false
0x47SELFBALANCE00000255true
0x50POP000000false
0x51MLOAD3232100255true
0x52MSTORE3232100255true
0x53MSTORE8321100255false
0x54SLOAD00000255true
0x55SSTORE0-000255true
0x56JUMP0-0000true
0x57JUMPI0-0000true
0x59MSIZE130000false
0x5aGAS000000false
0x5bJUMPDEST000000false
0x60PUSH1030000true
0x61PUSH2040000true
0x62PUSH3050000false
0x63PUSH4020000false
0x64PUSH5040000false
0x65PUSH6050000false
0x66PUSH7060000false
0x67PUSH8030000false
0x68PUSH9050000false
0x69PUSH10060000false
0x6aPUSH11070000false
0x6bPUSH12040000false
0x6cPUSH13060000false
0x6dPUSH14070000false
0x6ePUSH15080000false
0x6fPUSH16050000false
0x70PUSH17070000false
0x71PUSH18080000false
0x72PUSH19090000false
0x73PUSH20060000false
0x74PUSH21080000false
0x75PUSH22090000false
0x76PUSH230100000false
0x77PUSH24070000false
0x78PUSH25090000false
0x79PUSH260100000false
0x7aPUSH270110000false
0x7bPUSH28080000false
0x7cPUSH290100000false
0x7dPUSH300110000false
0x7ePUSH310120000false
0x7fPUSH32090000false
0x80DUP1000000false
0x81DUP2000000false
0x82DUP3000000false
0x83DUP4000000false
0x84DUP5000000false
0x85DUP6000000false
0x86DUP7000000false
0x87DUP8000000false
0x88DUP9000000false
0x90SWAP1000000false
0x91SWAP2000000false
0x92SWAP3000000false
0x93SWAP4000000false
0x94SWAP5000000false
0x95SWAP6000000false
0x96SWAP7000000false
0xa0LOG00-0000true
0xa1LOG10-0000true
0xa2LOG20-0000true
0xa3LOG30-0000true
0xa4LOG40-0000true
0xf0CREATE--0-0-true
0xf1CALL--00--true
0xf2CALLCODE--00--true
0xf3RETURN000000false
0xf4DELEGATECALL--00--true
0xf5CREATE2--0-0-true
0xfaSTATICCALL--00--true
0xfdREVERT000000false
0xfeINVALID010000false

各opcode详细的zkASM实现参见zkevm-rom项目中的opcodes.zkasm文件。

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表示为:【分别调用了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子程序负责执行加法运算,具体实现见:

  • 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/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

附录:Polygon Hermez 2.0 zkEVM系列博客

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐