1. 引言

Polygon zkEVM zkROM代码库为:

  • https://github.com/0xPolygonHermez/zkevm-rom

zkROM的基本流程为:

  • 1)A:加载输入变量;
  • 2)B:设置batch storage state-tree:batchHash (oldStateRoot) & globalExitRoot;
  • 3)C:循环解析RLP交易;
  • 4)D:循环处理交易;
  • 5)E:batch asserts:localExitRoot、transactions size、batchHashData & globalHash;
  • 6)F:finalize execution

前序博客为:

  • Polygon zkEVM zkROM代码解析(1)(包含A/B/C步骤)

本文重点包含D步骤。

2. D:循环处理交易

zkROM第四步为循环处理交易:

;;;;;;;;;;;;;;;;;;
;; D - Loop processing transactions
;;      - Load transaction data and interpret it
;;;;;;;;;;;;;;;;;;txLoop:$ => A          :MLOAD(pendingTxs); 一次处理一笔交易,更新pendingTxs减1; pendingTxs表示 Number of transactions decoded in RLP blockA-1 => A        :MSTORE(pendingTxs); 若A为负数,则跳转到processTxsEndA               :JMPN(processTxsEnd); ctxTxToUse表示 First context to be used when processing transactions$ => A          :MLOAD(ctxTxToUse) ; Load first context used by transaction; 更新ctxTxToUse加1A+1 => CTX      :MSTORE(ctxTxToUse); 跳转到processTx:JMP(processTx)processTxEnd:; 打印该笔交易处理完毕日志,继续处理下一笔交易${eventLog(onFinishTx)}:JMP(txLoop)processTxsEnd:

其中processTx表示单笔交易处理流程。

2.1 processTx单笔交易处理

processTx表示单笔交易处理流程为:

  • 1)A:验证ECDSA签名
  • 2)B:验证chainID
  • 3)C:验证nonce并递增nonce
  • 4)D:检查预付cost
  • 5)E:检查交易类型:
    • 5.1)E.1:合约调用交易

      • a)检查待处理的bytecode 与 state-tree hash bytecode是否一致;
      • b)处理bytecode;
      • c)。。。。
    • 5.2)E.2:部署合约交易(交易中的to参数为空)
      • a)计算新的合约地址;
      • b)处理bytecode;
      • c)部署完成:添加state-tree hash byte code和bytecode length。
  • 6)F:处理Gas

若交易中的to参数不为0且小于10,则表示为预编译合约,跳转至selectorPrecompiled,当前Polygon zkEVM仅支持:

  • ECRECOVER
  • IDENTITY
  • MODEXP

这3种预编译合约。

/*** Selector precompiled contract to run* Current precompiled supported: ECRECOVER, IDENTITY & MODEXP* @param {A} - Precompiled address*/
selectorPrecompiled: ; 此时A中为交易中的to参数值,即为预编译地址A - 2               :JMPN(funcECRECOVER)A - 3               :JMPN(callContract)  ;:JMPN(SHA256)A - 4               :JMPN(callContract)  ;:JMPN(RIPEMD160)A - 5               :JMPN(IDENTITY)A - 6               :JMPN(MODEXP)A - 7               :JMPN(callContract) ;:JMPN(ECADD)A - 8               :JMPN(callContract) ;:JMPN(ECMUL)A - 9               :JMPN(callContract) ;:JMPN(ECPAIRING)A - 10              :JMPN(callContract) ;:JMPN(BLAKE2F)
processTx:
;;;;;;;;;;;;;;;;;;
;; A - Verify ecdsa signature
;;;;;;;;;;;;;;;;;;; 打印日志${eventLog(onProcessTx)}; 预留足够的STEP以确保能处理单笔交易; Minimum of 100000 steps left to process a tx%MAX_CNT_STEPS - STEP - 100000 :JMPN(outOfCounters); Get sigDataSize; sigDataSize表示 hash position for the ethereum transaction hash$ => HASHPOS                     :MLOAD(sigDataSize); Check keccak counters; HASHKDIGEST操作符为对136取模,确保CNT_KECCAK_F计数器符合上限要求HASHPOS                          :MSTORE(arithA)136                              :MSTORE(arithB):CALL(divARITH)$ => B                           :MLOAD(arithRes1)%MAX_CNT_KECCAK_F - CNT_KECCAK_F - %MIN_CNT_KECCAK_BATCH => A$                                :LT, JMPC(outOfCounters); Get hash address previously stored in RLP parsing; lastTxHashId表示First hash address to be used when processing transactions; 更新lastTxHashId加1$ => E                          :MLOAD(lastTxHashId)E+1 => E                        :MSTORE(lastTxHashId); Check the signature; lastHashKIdUsed表示Last hash address used$ => A                          :MLOAD(lastHashKIdUsed)A + 1                           :MSTORE(lastHashKIdUsed)A + 1                           :MSTORE(ecrecover_lastHashIdUsed)$ => A                          :HASHKDIGEST(E)$ => B                          :MLOAD(txR)$ => C                          :MLOAD(txS)$ => D                          :MLOAD(txV); 调用ecrecover获得签名地址 存在 A寄存器中:CALL(ecrecover); Check result is non-zero
checkAndSaveFrom:; 要求ecrecover获得的签名地址不为0,否则为无效交易。; 同时将签名地址存入txSrcAddr和txSrcOriginAddr全局变量中。; txSrcOriginAddr表示origin address of a tx; txSrcAddr表示 address that sends a transaction 'message.sender'0 => BA                               :MSTORE(txSrcAddr)A                               :MSTORE(txSrcOriginAddr)$                               :EQ,JMPC(invalidIntrinsicTx);;;;;;;;;
;; Store init state
;;;;;;;;;; Store initial state at the beginning of the transaction; originSR表示State root before processing each transaction; initSR表示state-tree once the initial upfront cost is substracted and nonce is increasedSR                              :MSTORE(originSR)SR                              :MSTORE(initSR);;;;;;;;;;;;;;;;;;
;; B - Verify chainID,验证chainID
;;;;;;;;;;;;;;;;;;; txChainId表示 transaction parameter: 'chainId'$ => A                          :MLOAD(txChainId)                                       ; A: chainId tx; CONST %ZKEVM_CHAINID = 1000%ZKEVM_CHAINID => B                                                               ; B: defaultChainId, A: chainId tx$                               :EQ,JMPC(endCheckChainId)                               ; If A == B --> endCheckChainId:JMP(invalidIntrinsicTx)                                         ; If A != B --> invalidIntrinsicTx
endCheckChainId:;; Reset warm/cold information,更新信息$ => A                          :MLOAD(txSrcOriginAddr); 将ctx.input.touchedAddress置空${resetTouchedAddress()} ; clean touchedAddresses since there is a new transaction; 将ctx.input.touchedStorageSlots置空${resetStorageSlots()} ; clean storageSlots since there is a new transaction; 更新ctx.input.touchedAddress为签名者地址${touchedAddress(A)} ; add tx.origin to touched addresses;; Set gasPrice global var; txGasPriceRLP表示 transaction parameter: 'gasPrice' decoded from the RLP$ => A                          :MLOAD(txGasPriceRLP); txGasPrice表示 transaction parameter: 'gasPrice' global varA                               :MSTORE(txGasPrice)
;;;;;;;;;;;;;;;;;;
;; C - Verify and increase nonce
;;;;;;;;;;;;;;;;;;; 将交易签名者地址存入A和E中$ => A, E                       :MLOAD(txSrcOriginAddr) ; Address of the origin to A and E; CONST %SMT_KEY_NONCE = 1,为SMT CONSTANT KEY%SMT_KEY_NONCE => B0 => C; 从Storage中读取以签名者地址(A)和SMT_KEY_NONCE(B); 以及C为key 的Value值,存入A寄存器中$ => A                          :SLOAD; txNonce表示 transaction parameter: nonce$ => B                          :MLOAD(txNonce); 若从Storage中读的nonce值 与 交易中的nonce值 相等,则C=1;否则C=0,为无效交易。$ => C                          :EQC - 1                           :JMPN(invalidIntrinsicTx) ; Compare nonce state tree with nonce transaction; 断言 B==AB                               :ASSERT ; sanity check; 将nonce值加1,再更新到Storage中相应key中。A + 1 => D; 此时E中为交易签名账号E => A%SMT_KEY_NONCE => B0 => C; 更新storage中交易签名账号的nonce值,并将更新后的smt root给SR$ => SR                         :SSTORE ; Store the nonce plus one;;;;;;;;;;;;;;;;;;
;; D - Verify upfront cost,检查预付cost
;;;;;;;;;;;;;;;;;;; Verify batch gas limit; txGasLimit表示transaction parameter: 'gas limit'$ => B                          :MLOAD(txGasLimit); Check batch gas limit is not exceeded by transaction; CONST %BATCH_GAS_LIMIT = 30000000%BATCH_GAS_LIMIT => A; txGasLimit应大于等于BATCH_GAS_LIMIT ,否则为无效交易$                               :LT,JMPC(invalidIntrinsicTx); Intrinsic gas --> gas Limit >= 21000 + calldata cost + deployment cost; CONST %BASE_TX_GAS = 21000%BASE_TX_GAS => E ; Store init intrinsic gas at E; 当交易中to参数为空时,设置了isCreateContract为1;否则为0$ => A                          :MLOAD(isCreateContract); 若交易中to参数为空,则调用addDeploymentGasCost;否则调用getCalldataGasCost-A                              :JMPN(addDeploymentGasCost):JMP(getCalldataGasCost)addDeploymentGasCost:; CONST %BASE_TX_DEPLOY_GAS = 32000E + %BASE_TX_DEPLOY_GAS => E ; Add 32000 if transaction is a creategetCalldataGasCost:; txCalldataLen表示 calldata length$ => A                          :MLOAD(txCalldataLen)0 => B; 若txCalldataLen为0值,则调用endCalldataIntrinsicGas$                               :EQ,JMPC(endCalldataIntrinsicGas)addGas:; dataStarts表示 hash position where de transaction 'data' starts in the batchHashData$ => HASHPOS                    :MLOAD(dataStarts); 调用loopBytes之前,初始化C为00 => C:JMP(loopBytes)
loopBytes:; 预留足够的step%MAX_CNT_STEPS - STEP - 20 :JMPN(outOfCounters); 逐个处理txCalldataLenA - C - 1                       :JMPN(endCalldataIntrinsicGas); 此时E寄存器中存储的为累加gas费,用B临时存储E => BHASHPOS => D; 设D为11 => D$ => E                          :MLOAD(batchHashDataId); 每次从ctx.hashK[batchHashDataId]的HASHPOS位置读取D(1)个字节到D寄存器中$ => D                          :HASHK(E); 再次用E寄存器存储累加gas费B => E; C+1,便于下次循环,遍历calldataC + 1 => C; 从ctx.hashK[batchHashDataId]的HASHPOS位置读取的1个字节值 小于 1,则加4Gas;否则加16GasD - 1                           :JMPN(add4Gas):JMP(add16Gas)add4Gas:; E寄存器中累加gas加4E + 4 => E; 继续循环,遍历calldata:JMP(loopBytes)add16Gas:; E寄存器中累加gas加16E + 16 => E; 继续循环,遍历calldata:JMP(loopBytes)endCalldataIntrinsicGas:; txGasLimit表示 transaction parameter: 'gas limit'; 交易参数中附带的gaslimit应足够,应大于上述计算的gas值; Compare gas limit >= intrinsic gas$ => A                          :MLOAD(txGasLimit)E => B; 若A小于B,则交易无效$                               :LT, JMPC(invalidIntrinsicTx); Store calculated gas for later usage; 将calldata遍历完后累加的Gas值 存储在 gasCalldata全局变量中; gasCalldata表示 gas spent by the calldataE                               :MSTORE(gasCalldata); 检查账号余额应足够,大于等于txGasPrice*txGasLimit+转账金额txValue; Check upfront cost: balance >= gas price * gas limit + value; gas price * gas limit; 全局变量txGasPrice表示transaction parameter: 'gasPrice' global var$ => B                          :MLOAD(txGasPrice); 此时A为txGasLimit,transaction parameter: 'gas limit'A                               :MSTORE(arithA)B                               :MSTORE(arithB):CALL(mulARITH); 将A*B,即txGasPrice*txGasLimit结果给D$ => D                          :MLOAD(arithRes1); Get caller balance; 全局变量txSrcOriginAddr表示 origin address of a tx$ => A                          :MLOAD(txSrcOriginAddr); 用 %SMT_KEY_BALANCE => B 表示可读性更好0 => B, C; 以A/B/C寄存器为Key,读取storage smt中相应的值(为相应账号的balance值)$ => C                          :SLOAD; (gas price * gas limit) + value; CTX变量txValue表示 transaction parameter: 'value'$ => B                          :MLOAD(txValue); 此时D寄存器值为txGasPrice*txGasLimitD                               :MSTORE(arithA)B                               :MSTORE(arithB):CALL(addARITH);将txGasPrice*txGasLimit+txValue 值给B$ => B                          :MLOAD(arithRes1); Comparison; 此时C寄存器中为storage中存储的相应账号的balance值C => A; 比较若账号balance值 小于 txGasPrice*txGasLimit+txValue,则为无效交易$                               :LT, JMPC(invalidIntrinsicTx); Substract (gas price * gas limit) from caller balance; 此时C寄存器中为storage中存储的相应账号的balance值C                               :MSTORE(arithA); 此时D寄存器值为txGasPrice*txGasLimitD                               :MSTORE(arithB):CALL(subARITH); Substracted balance result in D; 账号的balance值 - txGasPrice*txGasLimit,结果存入D$ => D                          :MLOAD(arithRes1); 全局变量txSrcOriginAddr 表示origin address of a tx$ => A                          :MLOAD(txSrcOriginAddr)0 => B,C; 更新storage中相应账号的balance值为 减去txGasPrice*txGasLimit后的相应账号的balance值,并将更新后的smt root值给SR$ => SR                         :SSTORE; Store state root with upfront cost substracted and nonce increased; 更新了nonce值 和 balance值(减去了txGasPrice*txGasLimit) 之后的storage smt root值,存入initSR中SR                              :MSTORE(initSR); Substract intrinsic gas; CTX变量txGasLimit表示 transaction parameter: 'gas limit'$ => GAS                        :MLOAD(txGasLimit); gasCalldata中存储的为 将calldata遍历完后累加的Gas值 ; 全局变量gasCalldata表示 gas spent by the calldata$ => A                          :MLOAD(gasCalldata); txGasLimit - gasCalldata,结果存入GAS寄存器中GAS - A => GAS;;;;;;;;;;;;;;;;;;
;; E - Check transaction type
;;;;;;;;;;;;;;;;;;
txType:; Compute deployment address if create contract operation; 当交易中to参数为空时,设置了isCreateContract为1;否则为0$ => A                          :MLOAD(isCreateContract); 若to参数为空,则跳转至getContractAddress,表示为创建合约操作; 跳转至getContractAddress0 - A                           :JMPN(getContractAddress); 若to参数不为空; CTX变量txDestAddr表示 transaction parameter: 'to'$ => A                          :MLOAD(txDestAddr); Add 'to' to touched addresses ; 交易中的to参数追加到ctx.input.touchedAddress中${touchedAddress(A)}; Check 'to' is zero or precompiled contract; Check zero address since zero address is not a precompiled contract; 若交易中的to参数为0,则表示合约调用,跳转至callContract0 => B$                               :EQ, JMPC(callContract); 若交易中的to参数不为0且小于10,则表示为预编译合约,跳转至selectorPrecompiled10 => B$                               :LT,JMPC(selectorPrecompiled); 若交易中的to参数既不是0,也不小于10,则跳转至callContract:JMP(callContract);;;;;;;;;;;;;;;;;;
;; E.2 - Deploy contract
;;     - Compute new contract address
;;     - Process bytecode
;;     - End deploy: add state-tree hash bytecode and bytecode length
;;;;;;;;;;;;;;;;;;;; compute new create address
getContractAddress:; A new hash with position 0 is started; 设置HASHPOS为0,表示将启动新的哈希计算0 => HASHPOS; We get a new hashId; 获取新的lastHashKIdUsed到E,并加1后更新lastHashKIdUsed全局变量值; 全局变量lastHashKIdUsed表示Last hash address used$ => E                          :MLOAD(lastHashKIdUsed)E+1 => E                        :MSTORE(lastHashKIdUsed); Check if create is with CREATE2 opcode; CTX变量isCreate2表示 flag to determine if a new context comes from a CREATE2 opcode$ => A                          :MLOAD(isCreate2); 若isCreate2为1等非零值,则跳转到create20 - A                           :JMPN(create2); isCreate2为0。; 加载txNonce给A; CTX变量txNonce表示 transaction parameter: nonce$ => A                          :MLOAD(txNonce)0x80 => B; 若交易参数nonce值小于0x80,则跳转至nonce1byte$                               :LT,JMPC(nonce1byte); 若交易参数nonce值大于等于0x80; 加载lengthNonce值给C; lengthNonce为签名交易解析时获得的交易nonce值。; CTX变量lengthNonce表示 'nonce' length used when computing a new contract address$ => C                          :MLOAD(lengthNonce); 设D为11 => D; 1 byte length address + 20 bytes address + 1 byte length nonce + C bytes nonce; RLP数组编码,起始范围为0xc0。; 编码的数组结构为[address, nonce]; nonce最大值为64bit,即最多8个字节就够了,; 因此此时RLP数组编码的长度不会大于55个。0xc0 + 22 + C                   :HASHK(E) ; 附加RLP数组编码前缀值; 数组中address前缀值,address为20字节长字符串; address字符串长度20小于55,因此前缀值为0x80+0x14=0x940x94                            :HASHK(E) ; 为address字符串的前缀值20 => D$ => B                          :MLOAD(txSrcAddr)B                               :HASHK(E) ; 只取txSrcAddr地址的20个字节附加到哈希输入中; 设D为11 => D; 此时C中为lengthNonce值,即交易中的nonce字节数,不超过8个字节; nonce字符串编码前缀值为0x80+C0x80 + C                        :HASHK(E) ; 为nonce字符串的前缀值; 将交易中的nonce字节数给DC => D; 此时A为交易中的nonce值A                               :HASHK(E) ; 只取交易中nonce值中的lengthNonce个字节附加到哈希输入中; 跳转到endContractAddress:JMP(endContractAddress)nonce1byte: ; 针对交易nonce参数只有1个字节的情况; 加载交易签名者账号$ => A                          :MLOAD(txSrcAddr); 加载交易中的nonce值$ => B                          :MLOAD(txNonce); 设置D为11 => D; 1 byte length address + 20 bytes address + 1 byte nonce ; RLP数组编码,起始范围为0xc0。; 编码的数组结构为[address, nonce]; 此时nonce值为1个字节; 因此此时RLP数组编码的长度不会大于55个。0xc0 + 22                       :HASHK(E) ; 附加RLP数组编码前缀值; 数组中address前缀值,address为20字节长字符串; address字符串长度20小于55,因此前缀值为0x80+0x14=0x940x94                            :HASHK(E)20 => DA                               :HASHK(E) ; 只取txSrcAddr地址的20个字节附加到哈希输入中; 设置D为11 => D; 若交易中的nonce值为0,则跳转到nonceIs0B - 1                           :JMPN(nonceIs0); 交易中的nonce值为非零值,且为1字节B                               :HASHK(E) ; 将1字节的nonce值直接附加到哈希输入中; 跳转到endContractAddress:JMP(endContractAddress)nonceIs0:; 若相应的nonce值为0,则RLP(0)=0x800x80                            :HASHK(E) ; 将nonce为0的RLP值附加到哈希输入中endContractAddress:; end contract address hash and get the 20 first bytes; HASHPOS存储的为当前哈希输入的长度; HASHKLEN为对ctx.hashK[E(0)].data(内容为rlp([address,nonce]))进行Keccak256哈希运算HASHPOS                         :HASHKLEN(E); Keccak哈希运算计数器未超标%MAX_CNT_KECCAK_F - CNT_KECCAK_F - %MIN_CNT_KECCAK_BATCH - 1 :JMPN(outOfCounters); 取上面的哈希结果给A$ => A                          :HASHKDIGEST(E); 调用maskAdress。哈希结果为32字节,地址为20字节; maskAddress为将A寄存器中的值与0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFn做AND binary运算; 20字节的地址存入A寄存器中:CALL(maskAddress) ; Mask address to 20 bytes; 将哈希截取的20字节地址存入createContractAddress中; CTX变量createContractAddress表示 address computed of a new contractA                               :MSTORE(createContractAddress); 将哈希截取的20字节地址存入txDestAddr中; CTX变量txDestAddr表示 transaction parameter: 'to'A                               :MSTORE(txDestAddr); 将哈希截取的20字节地址存入storageAddr中; CTX变量storageAddr表示 address which the storage will be modifiedA                               :MSTORE(storageAddr); TODO: Add check CREATE or deployment with constructor reverted; 跳转到deploy:JMP(deploy);; compute new contract address as CREATE2 spec: keccak256(0xff ++ address ++ salt ++ keccak256(init_code))[12:] (https://eips.ethereum.org/EIPS/eip-1014)
create2:; CTX变量txCalldataLen表示 calldata length; 将txCalldataLen给C寄存器$ => C                          :MLOAD(txCalldataLen); CTX变量originCTX表示 The source context of the current context; 将originCTX给CTX寄存器$ => CTX                        :MLOAD(originCTX); CTX变量argsOffsetCall表示 pointer to the init slot where the calldata begins; 将argsOffsetCall给B寄存器$ => B                          :MLOAD(argsOffsetCall)loopCreate2: ; 以C为计数器,每次处理32个,遍历整个calldata; 预留足够的STEP和binary操作计数器%MAX_CNT_STEPS - STEP - 100 :JMPN(outOfCounters)%MAX_CNT_BINARY - CNT_BINARY - 4 :JMPN(outOfCounters); 若C小于1,则跳转至create2endC - 1                           :JMPN(create2end); 若C为1~31,则跳转至endloopCreate2,处理剩余的不足32字节的calldata内容C - 32                          :JMPN(endloopCreate2); 将argsOffsetCall给E寄存器,argsOffsetCall值应小于0x200000B => E; MLOAD32表示 从内存中读取32字节值,其中输入E为offset; 输出E为new offset,A为所读取的32字节值:CALL(MLOAD32); 调用MLOAD32之后获得的new offsetE => B; 设D为3232 => D$ => E                          :MLOAD(lastHashKIdUsed); 此时A为MLOAD32从内存中读取的32字节; 更新ctx.hashK[lastHashKIdUsed].data,Keccak(...|A|,附加MLOAD32从内存中读取的32字节A                               :HASHK(E); 每次处理32字节,循环处理。C - 32 => C:JMP(loopCreate2)endloopCreate2: ; 此时C值为1~31; 此时B为调用MLOAD32之后获得的new offsetB => E; MLOADX表示从内存中读取<32字节值; 输入E为offset,C为length,其中C小于32; 输出A为读取的C字节值,E为new offset:CALL(MLOADX); 更新D为32-C32 - C => D; 此时A为MLOADX从内存中读取的C字节值; SHRarith表示输入为A和D(字节),输出为A>>D => A:CALL(SHRarith); 此时C值为1~31C => D$ => E                          :MLOAD(lastHashKIdUsed); 更新ctx.hashK[lastHashKIdUsed].data,Keccak(...|A|,附加MLOADX从内存中读取的C个字节A                               :HASHK(E)create2end: ; 所有的txCalldataLen calldata length均已处理完毕; 全局变量currentCTX表示 keeps track of the context used$ => CTX                        :MLOAD(currentCTX); 对ctx.hashK[lastHashKIdUsed].data进行Keccak哈希运算; HASHPOS为Keccak函数的输入长度HASHPOS                         :HASHKLEN(E); Check keccak counters; 对于Keccak:incCounter = Math.ceil((ctx.hashK[addr].data.length + 1) / 136)HASHPOS                         :MSTORE(arithA)136                             :MSTORE(arithB):CALL(divARITH); B为本次Keccak哈希运算对应的incCounter$ => B                          :MLOAD(arithRes1); A为剩余的可用Keccak哈希计数值%MAX_CNT_KECCAK_F - CNT_KECCAK_F - %MIN_CNT_KECCAK_BATCH => A; 若A小于B,则计数器溢出,跳转至outOfCounters$                               :LT, JMPC(outOfCounters); 取ctx.hashK[lastHashKIdUsed].data的Keccak结果给C$ => C                          :HASHKDIGEST(E); new hash with position 0 is started; HASHPOS置0,表示新的哈希运算0 => HASHPOS; 更新lastHashKIdUsed和E值,加1$ => E                          :MLOAD(lastHashKIdUsed)E+1 => E                        :MSTORE(lastHashKIdUsed); 令D为1,表示Keccak输入将附加1个字节1 => D; 对ctx.hashK[lastHashKIdUsed].data,Keccak(0xff|0xff                            :HASHK(E); 令D为20,表示Keccak输入将附加20个字节20 => D; 对ctx.hashK[lastHashKIdUsed].data,Keccak(0xff | 交易签名者地址 |$ => A                          :MLOAD(txSrcAddr)A                               :HASHK(E); 令D为32,表示Keccak输入将附加32个字节32 => D; CTX变量salt为CREATE2参数:CREATE2 parameter 'salt' used to compute new contract address$ => B                          :MLOAD(salt); 对ctx.hashK[lastHashKIdUsed].data,Keccak(0xff | 交易签名者地址 | salt |B                               :HASHK(E); 令D为32,表示Keccak输入将附加32个字节32 => D; 此时C表示之前calldata的哈希值; 对ctx.hashK[lastHashKIdUsed].data,Keccak(0xff | 交易签名者地址 | salt | Keccak(calldata) |C                               :HASHK(E); 对ctx.hashK[lastHashKIdUsed].data,Keccak(0xff | 交易签名者地址 | salt | Keccak(calldata))做Keccak哈希运算HASHPOS                         :HASHKLEN(E); Check keccak counters; 对于Keccak:incCounter = Math.ceil((ctx.hashK[addr].data.length + 1) / 136)HASHPOS                         :MSTORE(arithA)136                             :MSTORE(arithB):CALL(divARITH); B为本次Keccak哈希运算对应的incCounter$ => B                          :MLOAD(arithRes1); A为剩余的可用Keccak哈希计数值%MAX_CNT_KECCAK_F - CNT_KECCAK_F - %MIN_CNT_KECCAK_BATCH => A; 若A小于B,则计数器溢出,跳转至outOfCounters$                               :LT, JMPC(outOfCounters); 取Keccak(0xff | 交易签名者地址 | salt | Keccak(calldata))哈希结果给A$ => A                          :HASHKDIGEST(E); 调用maskAdress。哈希结果为32字节,地址为20字节; maskAddress为将A寄存器中的值与0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFn做AND binary运算; 20字节的地址存入A寄存器中:CALL(maskAddress) ; Mask address to 20 bytes; 将哈希截取的20字节地址存入createContractAddress中; CTX变量createContractAddress表示 address computed of a new contractA                               :MSTORE(createContractAddress); 将哈希截取的20字节地址存入txDestAddr中; CTX变量txDestAddr表示 transaction parameter: 'to'A                               :MSTORE(txDestAddr); 将哈希截取的20字节地址存入storageAddr中; CTX变量storageAddr表示 address which the storage will be modifiedA                               :MSTORE(storageAddr);; deploy contract in state-tree
deploy:; add address to touched addresses; 此时A为经哈希运算获得的20字节合约地址; 将合约地址追加到ctx.input.touchedAddress中${touchedAddress(A)}; check if address is deployable ( nonce == bytecode == 0)A => E; read nonce0 => C; CONST %SMT_KEY_NONCE = 1%SMT_KEY_NONCE => B; 以A/B/C寄存器为Key,读取storage smt中相应的值(为nonce值)$ => B                      :SLOAD0 => A; 若storage中nonce值大于0,则说明该地址已被使用,跳转到deployAddressCollision$                           :LT,JMPC(deployAddressCollision); read bytecode; 此时E为经哈希运算获得的20字节合约地址E => A; CONST %SMT_KEY_SC_CODE = 2%SMT_KEY_SC_CODE => B; 以A/B/C寄存器为Key,读取storage smt中相应的值(为合约bytecode值)$ => B                      :SLOAD0 => A; 若storage中bytecode值大于0,则说明该地址已被使用,跳转到deployAddressCollision$                           :LT,JMPC(deployAddressCollision); set contract nonce to 1; 此时E为经哈希运算获得的20字节合约地址E => A1 => D%SMT_KEY_NONCE => B; 以A/B/C为key(对应nonce),设置storage相应key(nonce)的value值为D(1)$ => SR                         :SSTORE; Move balances if value > 0 just before deploy; CTX变量txValue表示 transaction parameter: 'value'$ => B                          :MLOAD(txValue)0 => AzkPC+2 => RR; 若交易参数`value`值大于0,则在部署之前先move balance,跳转到moveBalances$                               :LT, JMPC(moveBalances); 设置PC和SP为00 => PC0 => SP; 跳转到readCode:JMP(readCode);; read calldata bytes of a deploy transaction and process them
readDeployBytecode:; check transaction is a deploy transaction; CTX变量isCreate表示 flag to determine if a new context comes from a CREATE opcode$ => B                          :MLOAD(isCreate); 若交易中使用CREATE opcode创建合约,则跳转至readDeployBytecodeCreate0 - B                           :JMPN(readDeployBytecodeCreate); 若交易中使用CREATE2 opcode创建合约,则继续; check enough bytes to read in calldata; CTX变量txCalldataLen表示 calldata length$ => B                          :MLOAD(txCalldataLen); 若calldata length-PC-1 为负数,则跳转到defaultOpCodeB - PC - 1                      :JMPN(defaultOpCode); 加载dataStarts值给HASHPOS。; CTX变量dataStarts表示 hash position where de transaction 'data' starts in the batchHashData$ => HASHPOS                    :MLOAD(dataStarts)HASHPOS + PC => HASHPOS$ => E                          :MLOAD(batchHashDataId)1 => D ; 读取calldata第一个字节,调用JMP(@mapping_opcodes + RR)处理; 返回ctx.hashK[batchHashDataId].data中HASHPOS开始的D个字节,给RR$ => RR                         :HASHK(E)${eventLog(onOpcode(RR))} ; 打印读取的D个字节的bytecode信息PC + 1 => PC; 跳转到calldata中指定的bytecode操作符进行处理:JMP(@mapping_opcodes + RR); 读取完CREATE2/CREATE操作符之后,进一步读取和处理CREATE/CREATE2 call 的calldata bytes。
;; read calldata bytes of a CREATE/CREATE2 call and process them
readDeployBytecodeCreate:$ => E                          :MLOAD(txCalldataLen); JMP(@mapping_opcodes + RR)中会切换上下文; CTX变量 originCTX表示 The source context of the current context$ => CTX                        :MLOAD(originCTX); check enough bytes to read in memory; 若CREATE2/CREATE操作符之后无内容,则跳转到readDeployBytecodeCreateDefaultE - PC - 1                      :JMPN(readDeployBytecodeCreateDefault); CTX变量argsOffsetCall表示 pointer to the init slot where the calldata begins$ => E                          :MLOAD(argsOffsetCall)E + PC => E1 => C; 从内存中,以E为offset,读取C个字节;返回的A为读取的内容,E为新的offset。:CALL(MLOADX); 全局变量currentCTX表示 keeps track of the context used$ => CTX                        :MLOAD(currentCTX)31 => D; A>>D => A:CALL(SHRarith)A => RR  ; 从内存中,以E为offset,读取的C个字节内容在A>>31,赋给RR${eventLog(onOpcode(RR))}PC + 1 => PC; 跳转到RR指定的bytecode操作符进行处理; 除opCREATE/opCREATE2等特殊操作符之外,其它操作符(中有JMP(readCode))都会循环每次读取一个字节:JMP(@mapping_opcodes + RR);; handle error no more bytecode to read when call CREATE/CREATE2
readDeployBytecodeCreateDefault:; 全局变量currentCTX表示 keeps track of the context used$ => CTX                        :MLOAD(currentCTX):JMP(defaultOpCode) ; no bytecode treated as 0x00;;;;;;;;;;;;;;;;;;
;; E.1 - Call contract
;;     - Check bytecode to process against state-tree hash bytecode
;;     - Process bytecode
;;     - End deploy: add state-tree hash bytecode and bytecode length
;;;;;;;;;;;;;;;;;;
callContract:; Move balances if value > 0 just before executing the contract CALL; CTX变量txValue表示 transaction parameter: 'value'$ => B                          :MLOAD(txValue)0 => AzkPC+2 => RR; 若0<B,则跳转到moveBalances$                               :LT, JMPC(moveBalances)0 => PC0 => SP; CTX变量txDestAddr表示 transaction parameter: 'to'$ => A                          :MLOAD(txDestAddr); get contract length%SMT_KEY_SC_LENGTH => B0 => C; 从Storage中读取以A/B/C为key的Value值,存入B寄存器中$ => B                          :SLOAD; 将 从Storage中读取以A/B/C为key的Value值 存入 bytecodeLength变量中; CTX变量 bytecodeLength表示 state-tree length bytecode leaf value of the 'to' addressB                               :MSTORE(bytecodeLength)0 => A; 若B==0,则表示to地址不存在的bytecodeLength为0,不存在bytecode,跳转到defaultOpCode$                               :EQ, JMPC(defaultOpCode) ;no bytecode; CTX变量txDestAddr表示 transaction parameter: 'to'$ => A                          :MLOAD(txDestAddr); get hash contract%SMT_KEY_SC_CODE => B; 从Storage中读取以A/B/C为key的Value值,存入A寄存器中$ => A                          :SLOAD; 将 从Storage中读取以A/B/C为key的Value值 存入 hashContractTxDestAddr变量中; CTX变量 hashContractTxDestAddr表示 state-tree hash bytecode leaf value of the 'to' addressA                               :MSTORE(hashContractTxDestAddr)0 => HASHPOS1 => D; CTX变量 bytecodeLength表示 state-tree length bytecode leaf value of the 'to' address$ => B                          :MLOAD(bytecodeLength); get a new hashPId; 全局变量nextHashPId 表示 Next posidon hash address available$ => E                          :MLOAD(nextHashPId); CTX变量contractHashId表示 hashP address used to store contract bytecodeE                               :MSTORE(contractHashId); 更新全局变量nextHashPId, + 1E+1                             :MSTORE(nextHashPId)checkHashBytecodeLoop:; 预留足够的STEP%MAX_CNT_STEPS - STEP - 10 :JMPN(outOfCounters); 此时B为bytecodeLength; 若B - 1 - HASHPOS为负数,则跳转到checkHashBytecodeEndB - 1 - HASHPOS                         :JMPN(checkHashBytecodeEnd) ; finish reading bytecode; 此时A为从Storage中读取的hashContractTxDestAddr值; CTX变量 hashContractTxDestAddr表示 state-tree hash bytecode leaf value of the 'to' address; getBytecode(A, HASHPOS, 1)为:根据A获取hashContract,读取ctx.input.contractsBytecode[hashcontract]中的bytecode,; 以HASHPOS为offset,1为len,获取`d = "0x" + bytecode.slice(2+offset*2, 2+offset*2 + len*2); `,将d给A寄存器; 以contractHashId为addr,将A寄存器中D个字节附加到 ctx.hashP[addr].data 中ctx.HASHPOS位置之后${getBytecode(A, HASHPOS, 1)}           :HASHP(E) ; hash contract bytecode; 循环调用checkHashBytecodeLoop:JMP(checkHashBytecodeLoop)checkHashBytecodeEnd:; 此时E为contractHashId,以E为addr,对ctx.hashP[addr].data进行Poseidon哈希运算,结果存在ctx.hashP[addr].digest中HASHPOS                         :HASHPLEN(E); 读取ctx.hashP[addr].digest值给E寄存器$ => E                          :HASHPDIGEST(E); check hash computed matches hash in the smt leaf$ => A                          :MLOAD(hashContractTxDestAddr); 断言 ctx.input.contractsBytecode[hashcontract]中的bytecode哈希结果必须与hashcontract值相等E                               :ASSERT; 跳转到readCode:JMP(readCode)readByteCode: ; 当交易中to参数不为空时,isCreateContract为0,进入本代码段内; CTX变量contractHashId表示 hashP address used to store contract bytecode$ => E                          :MLOAD(contractHashId) ; hash index; CTX变量txDestAddr表示 transaction parameter: 'to'$ => A                          :MLOAD(txDestAddr); check next byte exist on the bytecode; CTX变量 bytecodeLength表示 state-tree length bytecode leaf value of the 'to' address$ => B                          :MLOAD(bytecodeLength); 若B - PC - 1为负数,则跳转到defaultOpCodeB - PC - 1                      :JMPN(defaultOpCode) ; no bytecode treated as 0x00; 以PC为HASHPOSPC => HASHPOS1 => D; 取ctx.hashP[contractHashId].data中HASHPOS往后的D个字节,给RR$ => RR                         :HASHP(E); 打印RR opcode${eventLog(onOpcode(RR))}; program counter + 1PC + 1 => PC; 跳转到RR指定的bytecode操作符进行处理:JMP(@mapping_opcodes + RR)readCode:; 当交易中to参数为空时,设置了isCreateContract为1;否则为0$ => A                          :MLOAD(isCreateContract); 若isCreateContract为1时,跳转到readDeployBytecode;否则跳转到readByteCode0 - A                           :JMPN(readDeployBytecode):JMP(readByteCode);; Compute and save hash bytecode and bytecode length in the state-tree
endDeploy:; called from `opRETURNDeploy` which has: C --> length, E --> offset; only when first context ends on deploy; If length is 0 do not modify state-tree; 若C<1,则跳转到handleGas,不修改state-treeC - 1                           :JMPN(handleGas); save offset memory and length to compute hash bytecode; 全局变量memOffsetLinearPoseidon 表示 memory offset to read bytes fromE                               :MSTORE(memOffsetLinearPoseidon); 全局变量memSizeLinearPoseidon 表示 memory size to read bytes fromC                               :MSTORE(memSizeLinearPoseidon); set bytecode length; CTX变量 createContractAddress表示 address computed of a new contract$ => A                          :MLOAD(createContractAddress)%SMT_KEY_SC_LENGTH => BC => D0 => C; 更新Storage中以A/B/C为Key,以D(为bytecode length)为value的值; 将更新后的smt new root返回给SR$ => SR                         :SSTORE ; CTX变量 createContractAddress表示 address computed of a new contract; CTX变量txDestAddr表示 transaction parameter: 'to'; 将txDestAddr更新为createContractAddress值A                               :MSTORE(txDestAddr); hashPoseidonLinearFromMemory表示,以memOffsetLinearPoseidon为offset,从内存中加载memSizeLinearPoseidon个字节进行Poseidon哈希运算,; 将Poseidon哈希运算的结果给D寄存器,; 同时将所加载的memSizeLinearPoseidon个字节存入ctx.input.contractsBytecode[ctx.hashP[addr].digest]中。:CALL(hashPoseidonLinearFromMemory); CTX变量 createContractAddress表示 address computed of a new contract$ => A                          :MLOAD(createContractAddress)0 => C%SMT_KEY_SC_CODE => B; 更新 以A/B/C为key,上面的Poseidon哈希结果(D寄存器)为value; 将更新后的smt new root返回给SR$ => SR                         :SSTORE;;;;;;;;;;;;;;;;;;
;; F - Handle GAS
;;   - Check refund gas
;;   - Return gas not used to caller
;;   - Pay gas to sequencer
;;;;;;;;;;;;;;;;;;;; compute maximum gas to refund
handleGas:0 => A; CTX变量gasRefund 表示keeps track of the transaction gas refund$ => B                          :MLOAD(gasRefund); 若gasRefund小于1,则跳转到refundGasB - 1                           :JMPN(refundGas); 若gasRefund大于等于1; CTX变量txGasLimit 表示 transaction parameter: 'gas limit'$ => A                          :MLOAD(txGasLimit); 令A = txGasLimit - gasRefundA - GAS => A; Div operation with ArithA                               :MSTORE(arithA)2                               :MSTORE(arithB):CALL(divARITH); 令 A = (txGasLimit - gasRefund)/2$ => A                          :MLOAD(arithRes1); 若(txGasLimit - gasRefund)/2-gasRefund 为负数,则跳转到refundGasA - B                           :JMPN(refundGas); 令A为gasRefundB => A;; add remaining gas to transaction origin
refundGas:; 令GAS=GAS+gasRefundGAS + A => GAS; 令A为GASGAS => A; 全局变量txGasPrice表示 transaction parameter: 'gasPrice' global var$ => B                          :MLOAD(txGasPrice);Mul operation with ArithA                               :MSTORE(arithA)B                               :MSTORE(arithB):CALL(mulARITH); 令D为GAS*txGasPrice$ => D                          :MLOAD(arithRes1); 全局变量txSrcOriginAddr表示 origin address of a tx$ => A                          :MLOAD(txSrcOriginAddr); 用 %SMT_KEY_BALANCE => B 表示可读性更好0 => B,C ; balance key smt; 从Storage中加载交易origin address的balance值,给A寄存器$ => A                          :SLOAD ; Original Balance in A; Add operation with ArithA                               :MSTORE(arithA)D                               :MSTORE(arithB):CALL(addARITH); 令D为GAS*txGasPrice+交易origin address在Storage中的balance值$ => D                          :MLOAD(arithRes1); 全局变量txSrcOriginAddr表示 origin address of a tx$ => A                          :MLOAD(txSrcOriginAddr); 用 %SMT_KEY_BALANCE => B 表示可读性更好0 => B,C                        ; balance key smt; 将Stoarge中交易origin address的balance值更新为“GAS*txGasPrice+交易origin address在Storage中的balance值”$ => SR                         :SSTORE;; Send gas spent to sequencer
sendGasSeq: ;将花费的gas发送给sequencer; CTX变量txGasLimit表示 transaction parameter: 'gas limit'$ => A          :MLOAD(txGasLimit); 令A为txGasLimit-GASA - GAS => A; 全局变量txGasPrice表示transaction parameter: 'gasPrice' global var$ => B          :MLOAD(txGasPrice); Mul operation with ArithA               :MSTORE(arithA)B               :MSTORE(arithB):CALL(mulARITH); 令D为 (txGasLimit-GAS)*txGasPrice$ => D          :MLOAD(arithRes1) ; value to pay the sequencer in D; 全局变量sequencerAddr表示 Coinbase address which will receive the fees$ => A          :MLOAD(sequencerAddr); 用 %SMT_KEY_BALANCE => B 表示可读性更好0 => B,C ; Balance key smt; 从Storage中加载sequencerAddr的balance给A寄存器$ => A          :SLOAD ; Original Balance in A; Add operation with ArithA               :MSTORE(arithA)D               :MSTORE(arithB):CALL(addARITH); 令D为 (txGasLimit-GAS)*txGasPrice+Storage中sequencerAddr的balance$ => D          :MLOAD(arithRes1)$ => A          :MLOAD(sequencerAddr); 用 %SMT_KEY_BALANCE => B 表示可读性更好0 => B,C ; balance key smt; 更新Storage中sequencerAddr的balance值为“(txGasLimit-GAS)*txGasPrice+Storage中sequencerAddr的balance”$ => SR         :SSTORE; 跳转到processTxEnd,表示当前交易处理完毕。继续处理后续交易:JMP(processTxEnd);; handle invalid transaction due to intrinsic checks
invalidIntrinsicTx:; 打印错误日志${eventLog(onError, intrinsic_invalid)}; 全局变量originSR表示 State root before processing each transaction$ => SR                         :MLOAD(originSR); 跳转到processTxEnd,表示当前交易处理完毕。继续处理后续交易:JMP(processTxEnd);; handle error no more bytecode to read
defaultOpCode: ; no bytecode treated as 0x00,即执行opSTOP${eventLog(onOpcode(0))}:JMP(opSTOP)

其中MLOAD32为:

;Get offset/32 & offset%32
;@in A offset
;@out E offset/32
;@out C offset%32
offsetUtil:${A >> 5} => E          ; ${A >> 5} -> E (*)${A & 0x1F} => C        ; ${A & 0x1F} -> C0x0FFFF - E     :JMPN(stackUnderflow)31-C            :JMPN(stackUnderflow)E*32+C          :ASSERT:RETURNVAR GLOBAL isMLOADX
; @info get value from memory (< 32 bytes)
; @in E => offset
; @in C => length
; @out A => value
; @out E => new offset
MLOADX:32 - C          :JMPN(errorMLOADMSTORE) ; TDDO Should be unreachable! check it32 - C - 1      :JMPN(MLOAD32)1               :MSTORE(isMLOADX); @info get value from memory (32 bytes)
; @in E => offset
; @out A => value
; @out E => new offset
MLOAD32:; 调用之前,将某些会复用的寄存器值存入临时变量中RR              :MSTORE(tmpZkPC)B               :MSTORE(tmpVarB)C               :MSTORE(tmpVarC)D               :MSTORE(tmpVarD); 此时E和A寄存器中值均为argsOffsetCall; CTX变量argsOffsetCall表示 pointer to the init slot where the calldata beginsE => A ; argsOffsetCall的值必须小于0x200000,即最大为0x1fffff0x200000 => B$               :LT,JMPC(initMLOAD):JMP(errorMLOADMSTORE)initMLOAD:; E寄存器中值为argsOffsetCall/32,C寄存器值为argsOffsetCall%32zkPC+1 => RR    :JMP(offsetUtil); 若argsOffsetCall不能被32整除,则调用memAlignOptionMLOAD-C              :JMPN(memAlignOptionMLOAD)$ => A          :MLOAD(MEM:E)$ => B          :MLOAD(isMLOADX)E*32 => EB - 1           :JMPN(offsetMLOAD32):JMP(sliceA)memAlignOptionMLOAD:; 加载内存中 地址为 E寄存器内值 的值,给A寄存器$ => A          :MLOAD(MEM:E); 加载内存中 地址为 E寄存器内值+1 的值,给A寄存器$ => B          :MLOAD(MEM:E+1); 从A、B寄存器中,以C(argsOffsetCall%32)为偏移量,读取32个字节$ => A          :MEM_ALIGN_RD; 此时E*32 + C 结果为argsOffsetCall,存入E寄存器中。E*32 + C => E; 读取isMLOADX全局变量值$ => B          :MLOAD(isMLOADX); 若isMLOADX全局变量值为0,则跳转至offsetMLOAD32,; 即取E=E+32=argsOffsetCall+32; 否则,若isMLOADX全局变量值为1,则继续执行下面的sliceAB - 1           :JMPN(offsetMLOAD32)sliceA:; 加载临时变量值给C,此时C为txCalldataLen (-32*i) calldata length$ => C          :MLOAD(tmpVarC); 将32-C给D32 - C => D; 此时A寄存器中值,为从MEM:E和MEM:E+1内存地址中,以argsOffsetCall%32为偏移量读取的32字节。; SHRarith表示 A>>D => AzkPC+1 => RR    :JMP(SHRarith); SHLarith表示 A<<D => AzkPC+1 => RR    :JMP(SHLarith); 将isMLOADX置零0               :MSTORE(isMLOADX); E*32 + C => E:JMP(endMLOAD)offsetMLOAD32:E + 32 => EendMLOAD: ; 调用结束,恢复调用开始前的各寄存器值。$ => B          :MLOAD(tmpVarB)$ => C          :MLOAD(tmpVarC)$ => D          :MLOAD(tmpVarD)$ => RR         :MLOAD(tmpZkPC):RETURN

其中moveBalances为:

moveBalances:;;;;;;;;
; evmCALL (Move Balances)
;;;;;;;;;Check if is a delegate call; CTX变量isDelegateCall表示 flag to determine if a new context comes from a DELEGATECALL opcode$ => A                           :MLOAD(isDelegateCall); 若isDelegateCall为1,则跳转至endMoveBalances,直接返回-A                               :JMPN(endMoveBalances); isDelegateCall为0; Decrement original balance$ => A                          :MLOAD(txSrcAddr); 用 %SMT_KEY_BALANCE => B 表示可读性更好0 => B,C                                                                                ; balance key smt; 以A/B/C为key,从storage中读取签名者地址对应的balance$ => A                          :SLOAD                                                  ; Original Balance in E; CTX变量txValue表示 transaction parameter: 'value'$ => B                          :MLOAD(txValue)                                         ; A = E - C; Check has enough balance to pay the value. In case not, means we are in a CALL/CALLCODE; 若交易message.sender账号balance值 小于 交易中的value参数值,则为无效交易,调用invalidCall$                               :LT,JMPC(invalidCall); A-B => A,A=>D,即将交易message.sender账号balance值 减去 交易中value值,结果存入D$ => D                          :SUB                                                    ; originalBalance -value in D; 将交易message.sender账号地址 加载到 A$ => A                          :MLOAD(txSrcAddr); 用 %SMT_KEY_BALANCE => B 表示可读性更好0 => B                                                                                  ; balance key smt; 更新storage中message.sender账号 的balance值为减去的结果。; 以A/B/C为key,将D值更新到storage相应的key中。$ => SR                         :SSTORE; Increment destination balance; CTX变量storageAddr表示 address which the storage will be modified; 将storageAddr加载到A$ => A                          :MLOAD(storageAddr); 用 %SMT_KEY_BALANCE => B 表示可读性更好0 => B                                                                                  ; balance key smt; 从stoarge中读取storageAddr账号的balance值$ => A                          :SLOAD                                                  ; Original Balance in E; 加载交易中的value参数值 到 B$ => B                          :MLOAD(txValue)                                         ; E = A + C; A+B => A, A => D$ => D                          :ADD$ => A                          :MLOAD(storageAddr); 用 %SMT_KEY_BALANCE => B 表示可读性更好0 => B,C                        ; balance key smt; 更新storage中storageAddr账号的balance为加法后的结果值,; 同时更新 storage smt root给SR$ => SR                         :SSTORE
endMoveBalances::RETURN

其中mapping_opcodes为:

/*** Map all Ethereum opcodes to its rom address* empty opcodes are set to INVALID opcode*/
mapping_opcodes::JMP(opSTOP)            ; 0x00:JMP(opADD)             ; 0x01:JMP(opMUL)             ; 0x02:JMP(opSUB)             ; 0x03:JMP(opDIV)             ; 0x04:JMP(opSDIV)            ; 0x05:JMP(opMOD)             ; 0x06:JMP(opSMOD)            ; 0x07:JMP(opADDMOD)          ; 0x08:JMP(opMULMOD)          ; 0x09:JMP(opEXP)             ; 0x0a:JMP(opSIGNEXTEND)      ; 0x0b:JMP(opINVALID)         ; 0x0c:JMP(opINVALID)         ; 0x0d:JMP(opINVALID)         ; 0x0e:JMP(opINVALID)         ; 0x0f:JMP(opLT)              ; 0x10:JMP(opGT)              ; 0x11:JMP(opSLT)             ; 0x12:JMP(opSGT)             ; 0x13:JMP(opEQ)              ; 0x14:JMP(opISZERO)          ; 0x15:JMP(opAND)             ; 0x16:JMP(opOR)              ; 0x17:JMP(opXOR)             ; 0x18:JMP(opNOT)             ; 0x19:JMP(opBYTE)            ; 0x1a:JMP(opSHL)             ; 0x1b:JMP(opSHR)             ; 0x1c:JMP(opSAR)             ; 0x1d:JMP(opINVALID)         ; 0x1e:JMP(opINVALID)         ; 0x1f:JMP(opSHA3)            ; 0x20:JMP(opINVALID)         ; 0x21:JMP(opINVALID)         ; 0x22:JMP(opINVALID)         ; 0x23:JMP(opINVALID)         ; 0x24:JMP(opINVALID)         ; 0x25:JMP(opINVALID)         ; 0x26:JMP(opINVALID)         ; 0x27:JMP(opINVALID)         ; 0x28:JMP(opINVALID)         ; 0x29:JMP(opINVALID)         ; 0x2a:JMP(opINVALID)         ; 0x2b:JMP(opINVALID)         ; 0x2c:JMP(opINVALID)         ; 0x2d:JMP(opINVALID)         ; 0x2e:JMP(opINVALID)         ; 0x2f:JMP(opADDRESS)         ; 0x30:JMP(opBALANCE)         ; 0x31:JMP(opORIGIN)          ; 0x32:JMP(opCALLER)          ; 0x33:JMP(opCALLVALUE)       ; 0x34:JMP(opCALLDATALOAD)    ; 0x35:JMP(opCALLDATASIZE)    ; 0x36:JMP(opCALLDATACOPY)    ; 0x37:JMP(opCODESIZE)        ; 0x38:JMP(opCODECOPY)        ; 0x39:JMP(opGASPRICE)        ; 0x3a:JMP(opEXTCODESIZE)     ; 0x3b:JMP(opEXTCODECOPY)     ; 0x3c:JMP(opRETURNDATASIZE)  ; 0x3d:JMP(opRETURNDATACOPY)  ; 0x3e:JMP(opEXTCODEHASH)     ; 0x3f:JMP(opBLOCKHASH)       ; 0x40:JMP(opCOINBASE)        ; 0x41:JMP(opTIMESTAMP)       ; 0x42:JMP(opNUMBER)          ; 0x43:JMP(opDIFFICULTY)      ; 0x44:JMP(opGASLIMIT)        ; 0x45:JMP(opCHAINID)         ; 0x46:JMP(opSELFBALANCE)     ; 0x47:JMP(opINVALID)         ; 0x48:JMP(opINVALID)         ; 0x49:JMP(opINVALID)         ; 0x4A:JMP(opINVALID)         ; 0x4B:JMP(opINVALID)         ; 0x4C:JMP(opINVALID)         ; 0x4D:JMP(opINVALID)         ; 0x4E:JMP(opINVALID)         ; 0x4F:JMP(opPOP)             ; 0x50:JMP(opMLOAD)           ; 0x51:JMP(opMSTORE)          ; 0x52:JMP(opMSTORE8)         ; 0x53:JMP(opSLOAD)           ; 0x54:JMP(opSSTORE)          ; 0x55:JMP(opJUMP)            ; 0x56:JMP(opJUMPI)           ; 0x57:JMP(opPC)              ; 0x58:JMP(opMSIZE)           ; 0x59:JMP(opGAS)             ; 0x5a:JMP(opJUMPDEST)        ; 0x5b:JMP(opINVALID)         ; 0x5C:JMP(opINVALID)         ; 0x5D:JMP(opINVALID)         ; 0x5E:JMP(opINVALID)         ; 0x5F:JMP(opPUSH1)           ; 0x60:JMP(opPUSH2)           ; 0x61:JMP(opPUSH3)           ; 0x62:JMP(opPUSH4)           ; 0x63:JMP(opPUSH5)           ; 0x64:JMP(opPUSH6)           ; 0x65:JMP(opPUSH7)           ; 0x66:JMP(opPUSH8)           ; 0x67:JMP(opPUSH9)           ; 0x68:JMP(opPUSH10)          ; 0x69:JMP(opPUSH11)          ; 0x6a:JMP(opPUSH12)          ; 0x6b:JMP(opPUSH13)          ; 0x6c:JMP(opPUSH14)          ; 0x6d:JMP(opPUSH15)          ; 0x6e:JMP(opPUSH16)          ; 0x6f:JMP(opPUSH17)          ; 0x70:JMP(opPUSH18)          ; 0x71:JMP(opPUSH19)          ; 0x72:JMP(opPUSH20)          ; 0x73:JMP(opPUSH21)          ; 0x74:JMP(opPUSH22)          ; 0x75:JMP(opPUSH23)          ; 0x76:JMP(opPUSH24)          ; 0x77:JMP(opPUSH25)          ; 0x78:JMP(opPUSH26)          ; 0x79:JMP(opPUSH27)          ; 0x7a:JMP(opPUSH28)          ; 0x7b:JMP(opPUSH29)          ; 0x7c:JMP(opPUSH30)          ; 0x7d:JMP(opPUSH31)          ; 0x7e:JMP(opPUSH32)          ; 0x7f:JMP(opDUP1)            ; 0x80:JMP(opDUP2)            ; 0x81:JMP(opDUP3)            ; 0x82:JMP(opDUP4)            ; 0x83:JMP(opDUP5)            ; 0x84:JMP(opDUP6)            ; 0x85:JMP(opDUP7)            ; 0x86:JMP(opDUP8)            ; 0x87:JMP(opDUP9)            ; 0x88:JMP(opDUP10)           ; 0x89:JMP(opDUP11)           ; 0x8a:JMP(opDUP12)           ; 0x8b:JMP(opDUP13)           ; 0x8c:JMP(opDUP14)           ; 0x8d:JMP(opDUP15)           ; 0x8e:JMP(opDUP16)           ; 0x8f:JMP(opSWAP1)           ; 0x90:JMP(opSWAP2)           ; 0x91:JMP(opSWAP3)           ; 0x92:JMP(opSWAP4)           ; 0x93:JMP(opSWAP5)           ; 0x94:JMP(opSWAP6)           ; 0x95:JMP(opSWAP7)           ; 0x96:JMP(opSWAP8)           ; 0x97:JMP(opSWAP9)           ; 0x98:JMP(opSWAP10)          ; 0x99:JMP(opSWAP11)          ; 0x9a:JMP(opSWAP12)          ; 0x9b:JMP(opSWAP13)          ; 0x9c:JMP(opSWAP14)          ; 0x9d:JMP(opSWAP15)          ; 0x9e:JMP(opSWAP16)          ; 0x9f:JMP(opLOG0)            ; 0xa0:JMP(opLOG1)            ; 0xa1:JMP(opLOG2)            ; 0xa2:JMP(opLOG3)            ; 0xa3:JMP(opLOG4)            ; 0xa4:JMP(opINVALID)         ; 0xA5:JMP(opINVALID)         ; 0xA6:JMP(opINVALID)         ; 0xA7:JMP(opINVALID)         ; 0xA8:JMP(opINVALID)         ; 0xA9:JMP(opINVALID)         ; 0xAA:JMP(opINVALID)         ; 0xAB:JMP(opINVALID)         ; 0xAC:JMP(opINVALID)         ; 0xAD:JMP(opINVALID)         ; 0xAE:JMP(opINVALID)         ; 0xAF:JMP(opINVALID)         ; 0xB0:JMP(opINVALID)         ; 0xB1:JMP(opINVALID)         ; 0xB2:JMP(opINVALID)         ; 0xB3:JMP(opINVALID)         ; 0xB4:JMP(opINVALID)         ; 0xB5:JMP(opINVALID)         ; 0xB6:JMP(opINVALID)         ; 0xB7:JMP(opINVALID)         ; 0xB8:JMP(opINVALID)         ; 0xB9:JMP(opINVALID)         ; 0xBA:JMP(opINVALID)         ; 0xBB:JMP(opINVALID)         ; 0xBC:JMP(opINVALID)         ; 0xBD:JMP(opINVALID)         ; 0xBE:JMP(opINVALID)         ; 0xBF:JMP(opINVALID)         ; 0xC0:JMP(opINVALID)         ; 0xC1:JMP(opINVALID)         ; 0xC2:JMP(opINVALID)         ; 0xC3:JMP(opINVALID)         ; 0xC4:JMP(opINVALID)         ; 0xC5:JMP(opINVALID)         ; 0xC6:JMP(opINVALID)         ; 0xC7:JMP(opINVALID)         ; 0xC8:JMP(opINVALID)         ; 0xC9:JMP(opINVALID)         ; 0xCA:JMP(opINVALID)         ; 0xCB:JMP(opINVALID)         ; 0xCC:JMP(opINVALID)         ; 0xCD:JMP(opINVALID)         ; 0xCE:JMP(opINVALID)         ; 0xCF:JMP(opINVALID)         ; 0xD0:JMP(opINVALID)         ; 0xD1:JMP(opINVALID)         ; 0xD2:JMP(opINVALID)         ; 0xD3:JMP(opINVALID)         ; 0xD4:JMP(opINVALID)         ; 0xD5:JMP(opINVALID)         ; 0xD6:JMP(opINVALID)         ; 0xD7:JMP(opINVALID)         ; 0xD8:JMP(opINVALID)         ; 0xD9:JMP(opINVALID)         ; 0xDA:JMP(opINVALID)         ; 0xDB:JMP(opINVALID)         ; 0xDC:JMP(opINVALID)         ; 0xDD:JMP(opINVALID)         ; 0xDE:JMP(opINVALID)         ; 0xDF:JMP(opINVALID)         ; 0xE0:JMP(opINVALID)         ; 0xE1:JMP(opINVALID)         ; 0xE2:JMP(opINVALID)         ; 0xE3:JMP(opINVALID)         ; 0xE4:JMP(opINVALID)         ; 0xE5:JMP(opINVALID)         ; 0xE6:JMP(opINVALID)         ; 0xE7:JMP(opINVALID)         ; 0xE8:JMP(opINVALID)         ; 0xE9:JMP(opINVALID)         ; 0xEA:JMP(opINVALID)         ; 0xEB:JMP(opINVALID)         ; 0xEC:JMP(opINVALID)         ; 0xED:JMP(opINVALID)         ; 0xEE:JMP(opINVALID)         ; 0xEF:JMP(opCREATE)          ; 0xf0:JMP(opCALL)            ; 0xf1:JMP(opCALLCODE)        ; 0xf2:JMP(opRETURN)          ; 0xf3:JMP(opDELEGATECALL)    ; 0xf4:JMP(opCREATE2)         ; 0xf5:JMP(opINVALID)         ; 0xf6:JMP(opINVALID)         ; 0xf7:JMP(opINVALID)         ; 0xf8:JMP(opINVALID)         ; 0xf9:JMP(opSTATICCALL)      ; 0xfa:JMP(opINVALID)         ; 0xfb:JMP(opINVALID)         ; 0xfc:JMP(opREVERT)          ; 0xfd:JMP(opINVALID)         ; 0xfe:JMP(opSELFDESTRUCT)    ; 0xff

附录:Polygon Hermez 2.0 zkEVM系列博客

  • ZK-Rollups工作原理
  • Polygon zkEVM——Hermez 2.0简介
  • Polygon zkEVM网络节点
  • Polygon zkEVM 基本概念
  • Polygon zkEVM Prover
  • Polygon zkEVM工具——PIL和CIRCOM
  • Polygon zkEVM节点代码解析
  • Polygon zkEVM的pil-stark Fibonacci状态机初体验
  • Polygon zkEVM的pil-stark Fibonacci状态机代码解析
  • Polygon zkEVM PIL编译器——pilcom 代码解析
  • Polygon zkEVM Arithmetic状态机
  • Polygon zkEVM中的常量多项式
  • Polygon zkEVM Binary状态机
  • Polygon zkEVM Memory状态机
  • Polygon zkEVM Memory Align状态机
  • Polygon zkEVM zkASM编译器——zkasmcom
  • Polygon zkEVM哈希状态机——Keccak-256和Poseidon
  • Polygon zkEVM zkASM语法
  • Polygon zkEVM可验证计算简单状态机示例
  • Polygon zkEVM zkASM 与 以太坊虚拟机opcode 对应集合
  • Polygon zkEVM zkROM代码解析(1)
  • Polygon zkEVM zkASM中的函数集合

Polygon zkEVM zkROM代码解析(2)相关推荐

  1. Polygon zkEVM交易解析

    1. 引言 前序博客有: Ethereum EVM简介 揭秘EVM Opcodes 剖析Solidity合约创建EVM bytecode Polygon zkEVM zkASM 与 以太坊虚拟机opc ...

  2. Polygon zkEVM中Goldilocks域元素circom约束

    1. 引言 前序博客有: Goldilocks域 Goldilocks域 p=264−232+1p= 2^{64} - 2^{32} + 1p=264−232+1. Polygon zkEVM中Gol ...

  3. Polygon zkEVM中的Merkle tree

    1. 引言 前序博客有: Merkle tree及其在区块链等领域的应用 以https://github.com/0xPolygonHermez/pil-stark为例,Polygon zkEVM中实 ...

  4. Polygon zkEVM zkProver基本设计原则 以及 Storage状态机

    1. zkProver基本设计原则 Polygon zkEVM采用状态机模型来模拟EVM(Ethereum Virtual Machine),从而提供与以太坊相同的用户体验,并支持部署运行相同的以太坊 ...

  5. Polygon zkEVM的pil-stark Fibonacci状态机代码解析

    1. 引言 前序博客有: Polygon zkEVM的pil-stark Fibonacci状态机初体验 STARKs and STARK VM: Proofs of Computational In ...

  6. Polygon zkEVM的pil-stark Fibonacci状态机初体验

    1. 引言 前序博客: Polygon zkEVM 基本概念 第5章 "一个例子--Fibonacci state machine" 实操主要见: 2022年8月8日Jordi B ...

  7. Polygon zkEVM zkASM中的函数集合

    1. 引言 前序博客有: Polygon zkEVM zkASM语法 zkASM程序的基本结构为: start(或其它任意label名,序号为0.为主业务流程):assignment : opcode ...

  8. Polygon zkEVM——Hermez 2.0简介

    1. 引言 前序博客有: ZK-Rollups工作原理 近期,Polygon团队开源了其Hermez 2.0 zkEVM代码,公开测试网即将上线: https://github.com/0xpolyg ...

  9. OVIS数据集代码解析

    OVIS数据集代码解析 OVIS数据集格式整体和COCO类似,但是是以video的形式存储的,对应的解析代码见:https://github.com/qjy981010/cocoapi/blob/ma ...

最新文章

  1. 机器人最大的人类士人禾力积木_开化县华埠镇中心小学:积木机器人好玩儿~~...
  2. C C++输出语句printf()输出保留小数点后保留1,2,3,4,5,6,7..n位(默认四舍五入)
  3. 【Java学习笔记之二十九】Java中的equals和==的用法及区别
  4. [业界资讯]腾讯QQ同时在线用户数突破8000万
  5. netty时间轮HashedWheelTimer文档翻译及简单说明
  6. Oracle数据库性能问题分析的一种常规思路
  7. 华为推出鸿蒙超级系统,华为鸿蒙系统正式发布!十个人里竟然只有两个人支持?...
  8. 凭据分配没有加密oracle_远程连接身份验证错误,又找不到加密Oracle修正
  9. python多线程队列两组数据_python 多线程2-队列同步
  10. 计算机网络性能(2)
  11. python实现局域网内传输文件
  12. 织梦dedecms蓝色培训机构模板教育学校学院整站php网站源码,织梦学校教育网 DEDECMS蓝色教育培训企业网站模板(成品整站源码)...
  13. 错误java.lang.NoClassDefFoundError: org/jaxen/VariableContext
  14. cc2430的ram和rom的划分
  15. 腾讯云搭建 CentOS 可视化界面startx无效解决方法
  16. 使用MV制作最简单的游戏:我要做游戏(4)
  17. SpringMVC源码学习
  18. 医保卡和社保卡的区别
  19. 解决ORA-00054错误
  20. babel7 + corejs3升级

热门文章

  1. 魔力宝贝高清单机计划(二) 地图转为tiled map
  2. 机器学习当道,还在使用基于词典的文本挖掘方法么,过时啦! 1
  3. 惠普dl20g9服务器系统分区,HPE DL20 Gen10 服务器
  4. 更改powshell默认路径,快速定制个性化的powshell启动方式
  5. C#ActiveX插件制作
  6. 神舟笔记本重装系统教程
  7. STM32F工程移植注意事项
  8. (附源码)计算机毕业设计ssm高校勤工助学管理系统
  9. 东华oj系统 52 k倍区间
  10. 大智慧专业财务PFFIN(N,M)函数N的取值一览表