IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 区块链 -> Polygon zkEVM zkROM代码解析(2) -> 正文阅读

[区块链]Polygon zkEVM zkROM代码解析(2)

1. 引言

前序博客为:

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 block
        A-1 => A        :MSTORE(pendingTxs)
        ; 若A为负数,则跳转到processTxsEnd
        A               :JMPN(processTxsEnd)
		; ctxTxToUse表示 First context to be used when processing transactions
        $ => A          :MLOAD(ctxTxToUse) ; Load first context used by transaction
        ; 更新ctxTxToUse加1
        A+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 => B
        A                               :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 increased
        SR                              :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 var
        A                               :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 => B
        0 => 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                          :EQ
        C - 1                           :JMPN(invalidIntrinsicTx) ; Compare nonce state tree with nonce transaction
        ; 断言 B==A
        B                               :ASSERT ; sanity check
        ; 将nonce值加1,再更新到Storage中相应key中。
        A + 1 => D
        ; 此时E中为交易签名账号
        E => A
        %SMT_KEY_NONCE => B
        0 => 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 = 32000
        E + %BASE_TX_DEPLOY_GAS => E ; Add 32000 if transaction is a create

getCalldataGasCost:
		; 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为0
        0 => C
                                        :JMP(loopBytes)
loopBytes:
		; 预留足够的step
        %MAX_CNT_STEPS - STEP - 20 :JMPN(outOfCounters)
        ; 逐个处理txCalldataLen
        A - C - 1                       :JMPN(endCalldataIntrinsicGas)
        ; 此时E寄存器中存储的为累加gas费,用B临时存储
        E => B
        HASHPOS => D
        ; 设D为1
        1 => D
        $ => E                          :MLOAD(batchHashDataId)
         ; 每次从ctx.hashK[batchHashDataId]的HASHPOS位置读取D(1)个字节到D寄存器中
        $ => D                          :HASHK(E)
        ; 再次用E寄出去存储累加gas费
        B => E
        ; C+1,便于下次循环,遍历calldata
        C + 1 => C
        ; 从ctx.hashK[batchHashDataId]的HASHPOS位置读取的1个字节值 小于 1,则加4Gas;否则加16Gas
        D - 1                           :JMPN(add4Gas)
                                        :JMP(add16Gas)

add4Gas:
		; E寄存器中累加gas加4
        E + 4 => E
        ; 继续循环,遍历calldata
                                        :JMP(loopBytes)

add16Gas:
		; E寄存器中累加gas加16
        E + 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 calldata
        E                               :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)
        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*txGasLimit
        D                               :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*txGasLimit
        D                               :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,表示为创建合约操作
        ; 跳转至getContractAddress
        0 - 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,则表示合约调用,跳转至callContract
        0 => B
        $                               :EQ, JMPC(callContract)
        ; 若交易中的to参数不为0且小于10,则表示为预编译合约,跳转至selectorPrecompiled
        10 => 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等非零值,则跳转到create2
        0 - 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为1
        1 => 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=0x94
        0x94                            :HASHK(E) ; 为address字符串的前缀值
        20 => D
        $ => B                          :MLOAD(txSrcAddr)
        B                               :HASHK(E) ; 只取txSrcAddr地址的20个字节附加到哈希输入中
        ; 设D为1
        1 => D
        ; 此时C中为lengthNonce值,即交易中的nonce字节数,不超过8个字节
        ; nonce字符串编码前缀值为0x80+C
        0x80 + C                        :HASHK(E) ; 为nonce字符串的前缀值
        ; 将交易中的nonce字节数给D
        C => D
        ; 此时A为交易中的nonce值
        A                               :HASHK(E) ; 只取交易中nonce值中的lengthNonce个字节附加到哈希输入中
        ; 跳转到endContractAddress
                                        :JMP(endContractAddress)

nonce1byte: ; 针对交易nonce参数只有1个字节的情况
		; 加载交易签名者账号
        $ => A                          :MLOAD(txSrcAddr)
        ; 加载交易中的nonce值
        $ => B                          :MLOAD(txNonce)
        ; 设置D为1
        1 => 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=0x94
        0x94                            :HASHK(E)
        20 => D
        A                               :HASHK(E) ; 只取txSrcAddr地址的20个字节附加到哈希输入中
        ; 设置D为1
        1 => D
        ; 若交易中的nonce值为0,则跳转到nonceIs0
        B - 1                           :JMPN(nonceIs0)
        ; 交易中的nonce值为非零值,且为1字节
        B                               :HASHK(E) ; 将1字节的nonce值直接附加到哈希输入中
        ; 跳转到endContractAddress
                                        :JMP(endContractAddress)

nonceIs0:
		; 若相应的nonce值为0,则RLP(0)=0x80
        0x80                            :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 contract
        A                               :MSTORE(createContractAddress)
        ; 将哈希截取的20字节地址存入txDestAddr中
        ; CTX变量txDestAddr表示 transaction parameter: 'to'
        A                               :MSTORE(txDestAddr)
        ; 将哈希截取的20字节地址存入storageAddr中
        ; CTX变量storageAddr表示 address which the storage will be modified
        A                               :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,则跳转至create2end
        C - 1                           :JMPN(create2end)
        ; 若C为1~31,则跳转至endloopCreate2,处理剩余的不足32字节的calldata内容
        C - 32                          :JMPN(endloopCreate2)
        ; 将argsOffsetCall给E寄存器,argsOffsetCall值应小于0x200000
        B => E
        ; 
                                        :CALL(MLOAD32)
        E => B
        32 => D
        $ => E                          :MLOAD(lastHashKIdUsed)
        A                               :HASHK(E)
        C - 32 => C
                                        :JMP(loopCreate2)

endloopCreate2:
        B => E
                                        :CALL(MLOADX)
        32 - C => D
                                        :CALL(SHRarith)
        C => D
        $ => E                          :MLOAD(lastHashKIdUsed)
        A                               :HASHK(E)

create2end:
        $ => CTX                        :MLOAD(currentCTX)
        HASHPOS                         :HASHKLEN(E)
        ; Check keccak counters
        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)
        $ => C                          :HASHKDIGEST(E)
        ; new hash with position 0 is started
        0 => HASHPOS
        $ => E                          :MLOAD(lastHashKIdUsed)
        E+1 => E                        :MSTORE(lastHashKIdUsed)
        1 => D
        0xff                            :HASHK(E)
        20 => D
        $ => A                          :MLOAD(txSrcAddr)
        A                               :HASHK(E)
        32 => D
        $ => B                          :MLOAD(salt)
        B                               :HASHK(E)
        32 => D
        C                               :HASHK(E)
        HASHPOS                         :HASHKLEN(E)

        ; Check keccak counters
        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)

        $ => A                          :HASHKDIGEST(E)
                                        :CALL(maskAddress) ; Mask address to 20 bytes
        A                               :MSTORE(createContractAddress)
        A                               :MSTORE(txDestAddr)
        A                               :MSTORE(storageAddr)

;; deploy contract in state-tree
deploy:
        ; add address to touched addresses
        ${touchedAddress(A)}

        ; check if address is deployable ( nonce == bytecode == 0)
        A => E

        ; read nonce
        0 => C
        %SMT_KEY_NONCE => B
        $ => B                      :SLOAD
        0 => A
        $                           :LT,JMPC(deployAddressCollision)

        ; read bytecode
        E => A
        %SMT_KEY_SC_CODE => B
        $ => B                      :SLOAD
        0 => A
        $                           :LT,JMPC(deployAddressCollision)

        ; set contract nonce to 1
        E => A
        1 => D
        %SMT_KEY_NONCE => B
        $ => SR                         :SSTORE
        ; Move balances if value > 0 just before deploy
        $ => B                          :MLOAD(txValue)
        0 => A
        zkPC+2 => RR
        $                               :LT, JMPC(moveBalances)
        0 => PC
        0 => SP
                                        :JMP(readCode)

;; read calldata bytes of a deploy transaction and process them
readDeployBytecode:
        ; check transaction is a deploy transaction
        $ => B                          :MLOAD(isCreate)
        0 - B                           :JMPN(readDeployBytecodeCreate)
        ; check enough bytes to read in calldata
        $ => B                          :MLOAD(txCalldataLen)
        B - PC - 1                      :JMPN(defaultOpCode)
        $ => HASHPOS                    :MLOAD(dataStarts)
        HASHPOS + PC => HASHPOS
        $ => E                          :MLOAD(batchHashDataId)
        1 => D
        $ => RR                         :HASHK(E)
        ${eventLog(onOpcode(RR))}
        PC + 1 => PC
                                        :JMP(@mapping_opcodes + RR)

;; read calldata bytes of a CREATE/CREATE2 call and process them
readDeployBytecodeCreate:
        $ => E                          :MLOAD(txCalldataLen)
        $ => CTX                        :MLOAD(originCTX)
        ; check enough bytes to read in memory
        E - PC - 1                      :JMPN(readDeployBytecodeCreateDefault)

        $ => E                          :MLOAD(argsOffsetCall)
        E + PC => E
        1 => C
                                        :CALL(MLOADX)
        $ => CTX                        :MLOAD(currentCTX)
        31 => D
                                        :CALL(SHRarith)
        A => RR
        ${eventLog(onOpcode(RR))}
        PC + 1 => PC
                                        :JMP(@mapping_opcodes + RR)

;; handle error no more bytecode to read when call CREATE/CREATE2
readDeployBytecodeCreateDefault:
        $ => CTX                        :MLOAD(currentCTX)
                                        :JMP(defaultOpCode)

;;;;;;;;;;;;;;;;;;
;; 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
        $ => B                          :MLOAD(txValue)
        0 => A
        zkPC+2 => RR
        $                               :LT, JMPC(moveBalances)
        0 => PC
        0 => SP

        $ => A                          :MLOAD(txDestAddr)
        ; get contract length
        %SMT_KEY_SC_LENGTH => B
        0 => C
        $ => B                          :SLOAD
        B                               :MSTORE(bytecodeLength)
        0 => A
        $                               :EQ, JMPC(defaultOpCode) ;no bytecode

        $ => A                          :MLOAD(txDestAddr)
        ; get hash contract
        %SMT_KEY_SC_CODE => B
        $ => A                          :SLOAD
        A                               :MSTORE(hashContractTxDestAddr)
        0 => HASHPOS
        1 => D
        $ => B                          :MLOAD(bytecodeLength)

        ; get a new hashPId
        $ => E                          :MLOAD(nextHashPId)
        E                               :MSTORE(contractHashId)
        E+1                             :MSTORE(nextHashPId)

checkHashBytecodeLoop:
        %MAX_CNT_STEPS - STEP - 10 :JMPN(outOfCounters)
        B - 1 - HASHPOS                         :JMPN(checkHashBytecodeEnd) ; finish reading bytecode
        ${getBytecode(A, HASHPOS, 1)}           :HASHP(E) ; hash contract bytecode
                                                :JMP(checkHashBytecodeLoop)

checkHashBytecodeEnd:
        HASHPOS                         :HASHPLEN(E)
        $ => E                          :HASHPDIGEST(E)
        ; check hash computed matches hash in the smt leaf
        $ => A                          :MLOAD(hashContractTxDestAddr)
        E                               :ASSERT
                                        :JMP(readCode)

readByteCode:
        $ => E                          :MLOAD(contractHashId) ; hash index
        $ => A                          :MLOAD(txDestAddr)
        ; check next byte exist on the bytecode
        $ => B                          :MLOAD(bytecodeLength)
        B - PC - 1                      :JMPN(defaultOpCode) ; no bytecode treated as 0x00
        PC => HASHPOS
        1 => D
        $ => RR                         :HASHP(E)
        ${eventLog(onOpcode(RR))}
        PC + 1 => PC
                                        :JMP(@mapping_opcodes + RR)

readCode:
        $ => A                          :MLOAD(isCreateContract)
        0 - 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                           :JMPN(handleGas)
        ; save offset memory and length to compute hash bytecode
        E                               :MSTORE(memOffsetLinearPoseidon)
        C                               :MSTORE(memSizeLinearPoseidon)
        ; set bytecode length
        $ => A                          :MLOAD(createContractAddress)
        %SMT_KEY_SC_LENGTH => B
        C => D
        0 => C
        $ => SR                         :SSTORE
        A                               :MSTORE(txDestAddr)
                                        :CALL(hashPoseidonLinearFromMemory)
        $ => A                          :MLOAD(createContractAddress)
        0 => C
        %SMT_KEY_SC_CODE => B
        $ => 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
        $ => B                          :MLOAD(gasRefund)
        B - 1                           :JMPN(refundGas)
        $ => A                          :MLOAD(txGasLimit)
        A - GAS => A
        ; Div operation with Arith
        A                               :MSTORE(arithA)
        2                               :MSTORE(arithB)
                                        :CALL(divARITH)
        $ => A                          :MLOAD(arithRes1)
        A - B                           :JMPN(refundGas)
        B => A

;; add remaining gas to transaction origin
refundGas:
        GAS + A => GAS
        GAS => A
        $ => B                          :MLOAD(txGasPrice)
        ;Mul operation with Arith
        A                               :MSTORE(arithA)
        B                               :MSTORE(arithB)
                                        :CALL(mulARITH)
        $ => D                          :MLOAD(arithRes1)

        $ => A                          :MLOAD(txSrcOriginAddr)
        0 => B,C ; balance key smt
        $ => A                          :SLOAD ; Original Balance in A

        ; Add operation with Arith
        A                               :MSTORE(arithA)
        D                               :MSTORE(arithB)
                                        :CALL(addARITH)
        $ => D                          :MLOAD(arithRes1)

        $ => A                          :MLOAD(txSrcOriginAddr)
        0 => B,C                        ; balance key smt
        $ => SR                         :SSTORE


;; Send gas spent to sequencer
sendGasSeq:
        $ => A          :MLOAD(txGasLimit)
        A - GAS => A

        $ => B          :MLOAD(txGasPrice)
        ; Mul operation with Arith
        A               :MSTORE(arithA)
        B               :MSTORE(arithB)
                        :CALL(mulARITH)
        $ => D          :MLOAD(arithRes1) ; value to pay the sequencer in D

        $ => A          :MLOAD(sequencerAddr)
        0 => B,C ; Balance key smt
        $ => A          :SLOAD ; Original Balance in A
        ; Add operation with Arith
        A               :MSTORE(arithA)
        D               :MSTORE(arithB)
                        :CALL(addARITH)
        $ => D          :MLOAD(arithRes1)
        $ => A          :MLOAD(sequencerAddr)
        0 => B,C ; balance key smt
        $ => SR         :SSTORE
                        :JMP(processTxEnd)


;; handle invalid transaction due to intrinsic checks
invalidIntrinsicTx:
        ${eventLog(onError, intrinsic_invalid)}
        $ => SR                         :MLOAD(originSR)
                                        :JMP(processTxEnd)

;; handle error no more bytecode to read
defaultOpCode:
        ${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} -> C
    0x0FFFF - E     :JMPN(stackUnderflow)
    31-C            :JMPN(stackUnderflow)
    E*32+C          :ASSERT
                    :RETURN
                    
VAR 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 it
    32 - 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 begins
    E => A ; argsOffsetCall的值必须小于0x200000,即最大为0x1fffff
    0x200000 => B
    $               :LT,JMPC(initMLOAD)
                    :JMP(errorMLOADMSTORE)

initMLOAD:
	; E寄存器中值为argsOffsetCall/32,C寄存器值为argsOffsetCall%32
    zkPC+1 => RR    :JMP(offsetUtil)
    ; 若argsOffsetCall不能被32整除,则调用memAlignOptionMLOAD
    -C              :JMPN(memAlignOptionMLOAD)
    $ => A          :MLOAD(MEM:E)
    $ => B          :MLOAD(isMLOADX)
    E*32 => E
    B - 1           :JMPN(offsetMLOAD32)
                    :JMP(sliceA)

memAlignOptionMLOAD:
    $ => A          :MLOAD(MEM:E)
    $ => B          :MLOAD(MEM:E+1)
    $ => A          :MEM_ALIGN_RD
    E*32 + C => E
    $ => B          :MLOAD(isMLOADX)
    B - 1           :JMPN(offsetMLOAD32)

sliceA:
    $ => C          :MLOAD(tmpVarC)
    32 - C => D
    zkPC+1 => RR    :JMP(SHRarith)
    zkPC+1 => RR    :JMP(SHLarith)
    0               :MSTORE(isMLOADX)
    E*32 + C => E
                    :JMP(endMLOAD)

offsetMLOAD32:
    E + 32 => E

endMLOAD:
    $ => B          :MLOAD(tmpVarB)
    $ => C          :MLOAD(tmpVarC)
    $ => D          :MLOAD(tmpVarD)
    $ => RR         :MLOAD(tmpZkPC)
                    :RETURN

附录:Polygon Hermez 2.0 zkEVM系列博客

  区块链 最新文章
盘点具备盈利潜力的几大加密板块,以及潜在
阅读笔记|让区块空间成为商品,打造Web3云
区块链1.0-比特币的数据结构
Team Finance被黑分析|黑客自建Token“瞒天
区块链≠绿色?波卡或成 Web3“生态环保”标
期货从入门到高深之手动交易系列D1课
以太坊基础---区块验证
进入以太坊合并的五个数字
经典同态加密算法Paillier解读 - 原理、实现
IPFS/Filecoin学习知识科普(四)
上一篇文章      下一篇文章      查看所有文章
加:2022-09-30 00:58:05  更:2022-09-30 00:59:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 20:30:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码