diff --git a/.eslintrc.js b/.eslintrc.js index ed33e20f..adf9abfd 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -21,10 +21,9 @@ module.exports = { 'class-methods-use-this': [0], 'no-bitwise': [0], 'no-param-reassign': 'off', - 'no-console': [2, { allow: ['warn', 'error'] }], + 'no-console': 'off', 'import/prefer-default-export': [0], 'lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: true }], - 'multiline-comment-style': 'error', 'no-await-in-loop': 'off', 'newline-before-return': 'error', }, diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..53b3f9ef --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,14 @@ +# global owner +* @krlosMata @laisolizq @invocamanman @ignasirv + +# ecrecover owner +/main/ecrecover @zkronos73 @krlosMata + +# counters folder owner +/counters/ @ignasirv @krlosMata + +# tests owner +/test/ @laisolizq @krlosMata + +# tools owner +/tools/ @laisolizq @krlosMata \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 00000000..7526eda0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,38 @@ +name: Bug Report +description: File a bug report +title: "[Bug]: " +labels: ["bug"] +assignees: [krlosMata, laisolizq, invocamanman, ignasirv] +body: + - type: markdown + attributes: + value: | + Fill in all necessary information to report a bug + - type: input + id: version + attributes: + label: Version + description: What version are you using ? + placeholder: 'ex: v1.0.0' + validations: + required: true + - type: textarea + id: description + attributes: + label: Description + description: Explain the bug found and the expected result + validations: + required: true + - type: textarea + id: steps + attributes: + label: Steps to reproduce + description: Please share detailed information about ho to reproduce the bug + validations: + required: true + - type: textarea + id: logs + attributes: + label: Relevant log + description: Please copy and paste any relevant log output + render: shell \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml new file mode 100644 index 00000000..e1d4c0f9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -0,0 +1,26 @@ +name: Feature Request +description: File a bug report +title: "[Feature]: " +labels: ["feature"] +assignees: + - krlosMata +body: + - type: markdown + attributes: + value: | + Fill in all necessary information to request a feature + - type: input + id: version + attributes: + label: Version + description: What version the features should be added ? + placeholder: 'ex: v1.0.0' + validations: + required: true + - type: textarea + id: description + attributes: + label: Description + description: Explain the feature + validations: + required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/question.yml b/.github/ISSUE_TEMPLATE/question.yml new file mode 100644 index 00000000..406c3703 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.yml @@ -0,0 +1,26 @@ +name: Question +description: Submit a question +title: "[Question]: " +labels: ["question"] +assignees: + - krlosMata +body: + - type: markdown + attributes: + value: | + Fill in all necessary information to submit a question + - type: input + id: version + attributes: + label: Version + description: What version the question is refering to ? + placeholder: 'ex: v1.0.0' + validations: + required: true + - type: textarea + id: question + attributes: + label: Description + description: Explain the question + validations: + required: true \ No newline at end of file diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 82f24a60..6cdb3199 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -27,6 +27,9 @@ jobs: - name: run counters tests run: | npm run test:counters + - name: run zkasm tests + run: | + npm run test:zkasm - name: Generate tests run: | npm run test:gen diff --git a/.gitignore b/.gitignore index 6262be5f..6b7d518d 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,5 @@ build # testvectors files tools/parallel-tests + +.vscode/launch.json diff --git a/audits/zkEVM-ROM-upgrade-1-Spearbit-30-May.pdf b/audits/zkEVM-ROM-upgrade-1-Spearbit-30-May.pdf new file mode 100644 index 00000000..f3b2cd8d Binary files /dev/null and b/audits/zkEVM-ROM-upgrade-1-Spearbit-30-May.pdf differ diff --git a/audits/zkEVM-ROM-upgrade-2-Spearbit-21-August.pdf b/audits/zkEVM-ROM-upgrade-2-Spearbit-21-August.pdf new file mode 100644 index 00000000..d9dfc7bc Binary files /dev/null and b/audits/zkEVM-ROM-upgrade-2-Spearbit-21-August.pdf differ diff --git a/audits/zkEVM-engagement-3-Spearbit-6-April.pdf b/audits/zkEVM-engagement-3-Spearbit-6-April.pdf new file mode 100644 index 00000000..c5c98f2a Binary files /dev/null and b/audits/zkEVM-engagement-3-Spearbit-6-April.pdf differ diff --git a/counters/countersConstants.zkasm b/counters/countersConstants.zkasm index 677d2be6..5712a24c 100644 --- a/counters/countersConstants.zkasm +++ b/counters/countersConstants.zkasm @@ -70,14 +70,7 @@ CONST %ABS_CNT_KECCAK_F = 0 CONST %ABS_CNT_MEM_ALIGN = 0 CONST %ABS_CNT_PADDING_PG = 0 CONST %ABS_CNT_POSEIDON_G = 0 -; copySP -CONST %COPYSP_STEP = 20 + %MLOADX_STEP -CONST %COPYSP_CNT_BINARY = 0 + %MLOADX_CNT_BINARY -CONST %COPYSP_CNT_ARITH = 0 + %MLOADX_CNT_ARITH -CONST %COPYSP_CNT_KECCAK_F = 0 + %MLOADX_CNT_KECCAK_F -CONST %COPYSP_CNT_MEM_ALIGN = 0 + %MLOADX_CNT_MEM_ALIGN -CONST %COPYSP_CNT_PADDING_PG = 0 + %MLOADX_CNT_PADDING_PG -CONST %COPYSP_CNT_POSEIDON_G = 0 + %MLOADX_CNT_POSEIDON_G + ; mulARITH CONST %MULARITH_STEP = 50 CONST %MULARITH_CNT_BINARY = 1 @@ -208,7 +201,7 @@ CONST %OPBLOCKHASH_CNT_PADDING_PG = 0 CONST %OPBLOCKHASH_CNT_POSEIDON_G = 2 ; opCALLDATALOAD CONST %OPCALLDATALOAD_STEP = 100 + %SHLARITH_STEP + %SHRARITH_STEP + %DIVARITH_STEP -CONST %OPCALLDATALOAD_CNT_BINARY = 2 +CONST %OPCALLDATALOAD_CNT_BINARY = 3 CONST %OPCALLDATALOAD_CNT_ARITH = 0 CONST %OPCALLDATALOAD_CNT_KECCAK_F = 0 + %SHLARITH_CNT_KECCAK_F + %SHRARITH_CNT_KECCAK_F + %DIVARITH_CNT_KECCAK_F CONST %OPCALLDATALOAD_CNT_MEM_ALIGN = 1 @@ -248,34 +241,34 @@ CONST %OPEXTCODECOPY_CNT_PADDING_PG = 0 CONST %OPEXTCODECOPY_CNT_POSEIDON_G = 8 ; opCREATE - COMPLEX - hardcoded values at test CONST %OPCREATE_STEP = 400 -CONST %OPCREATE_CNT_BINARY = 23 -CONST %OPCREATE_CNT_ARITH = 4 +CONST %OPCREATE_CNT_BINARY = 16 +CONST %OPCREATE_CNT_ARITH = 1 CONST %OPCREATE_CNT_KECCAK_F = 0 -CONST %OPCREATE_CNT_MEM_ALIGN = 2 +CONST %OPCREATE_CNT_MEM_ALIGN = 0 CONST %OPCREATE_CNT_PADDING_PG = 0 CONST %OPCREATE_CNT_POSEIDON_G = 23 ; opCREATE2 - COMPLEX - hardcoded values at test CONST %OPCREATE2_STEP = 500 -CONST %OPCREATE2_CNT_BINARY = 29 -CONST %OPCREATE2_CNT_ARITH = 5 +CONST %OPCREATE2_CNT_BINARY = 22 +CONST %OPCREATE2_CNT_ARITH = 2 CONST %OPCREATE2_CNT_KECCAK_F = 0 -CONST %OPCREATE2_CNT_MEM_ALIGN = 2 +CONST %OPCREATE2_CNT_MEM_ALIGN = 0 CONST %OPCREATE2_CNT_PADDING_PG = 0 CONST %OPCREATE2_CNT_POSEIDON_G = 27 ; opCALL - COMPLEX - hardcoded values at test CONST %OPCALL_STEP = 600 -CONST %OPCALL_CNT_BINARY = 34 -CONST %OPCALL_CNT_ARITH = 6 +CONST %OPCALL_CNT_BINARY = 27 +CONST %OPCALL_CNT_ARITH = 3 CONST %OPCALL_CNT_KECCAK_F = 0 -CONST %OPCALL_CNT_MEM_ALIGN = 2 +CONST %OPCALL_CNT_MEM_ALIGN = 0 CONST %OPCALL_CNT_PADDING_PG = 0 CONST %OPCALL_CNT_POSEIDON_G = 14 ; opCALLCODE - COMPLEX - hardcoded values at test CONST %OPCALLCODE_STEP = 600 -CONST %OPCALLCODE_CNT_BINARY = 33 -CONST %OPCALLCODE_CNT_ARITH = 6 +CONST %OPCALLCODE_CNT_BINARY = 26 +CONST %OPCALLCODE_CNT_ARITH = 3 CONST %OPCALLCODE_CNT_KECCAK_F = 0 -CONST %OPCALLCODE_CNT_MEM_ALIGN = 2 +CONST %OPCALLCODE_CNT_MEM_ALIGN = 0 CONST %OPCALLCODE_CNT_PADDING_PG = 0 CONST %OPCALLCODE_CNT_POSEIDON_G = 10 ; opRETURN - COMPLEX - hardcoded values at test @@ -296,18 +289,18 @@ CONST %OPREVERT_CNT_PADDING_PG = 0 CONST %OPREVERT_CNT_POSEIDON_G = 0 ; opDELEGATECALL - COMPLEX - hardcoded values at test CONST %OPDELEGATECALL_STEP = 600 -CONST %OPDELEGATECALL_CNT_BINARY = 30 -CONST %OPDELEGATECALL_CNT_ARITH = 6 +CONST %OPDELEGATECALL_CNT_BINARY = 23 +CONST %OPDELEGATECALL_CNT_ARITH = 3 CONST %OPDELEGATECALL_CNT_KECCAK_F = 0 -CONST %OPDELEGATECALL_CNT_MEM_ALIGN = 2 +CONST %OPDELEGATECALL_CNT_MEM_ALIGN = 0 CONST %OPDELEGATECALL_CNT_PADDING_PG = 0 CONST %OPDELEGATECALL_CNT_POSEIDON_G = 6 ; opSTATICCALL - COMPLEX - hardcoded values at test CONST %OPSTATICCALL_STEP = 600 -CONST %OPSTATICCALL_CNT_BINARY = 30 -CONST %OPSTATICCALL_CNT_ARITH = 6 +CONST %OPSTATICCALL_CNT_BINARY = 23 +CONST %OPSTATICCALL_CNT_ARITH = 3 CONST %OPSTATICCALL_CNT_KECCAK_F = 0 -CONST %OPSTATICCALL_CNT_MEM_ALIGN = 2 +CONST %OPSTATICCALL_CNT_MEM_ALIGN = 0 CONST %OPSTATICCALL_CNT_PADDING_PG = 0 CONST %OPSTATICCALL_CNT_POSEIDON_G = 6 ; opSHA3 - COMPLEX - hardcoded values at test diff --git a/counters/tests/copySP.zkasm b/counters/tests/copySP.zkasm deleted file mode 100644 index e89327a7..00000000 --- a/counters/tests/copySP.zkasm +++ /dev/null @@ -1,38 +0,0 @@ -INCLUDE "../initIncludes.zkasm" - -start: - 1000000 => GAS - 1 => CTX -; TODO -operation: - 10 :MSTORE(argsOffsetCall) - 0x1005 :MSTORE(argsLengthCall) - CTX + 1 => CTX - 1 :MSTORE(originCTX) - :CALL(copySP) - - ; loop in SP - $ => C :MLOAD(argsLengthCall) - ${C/32} => C - ; copysp + stepsCopySPLoop + mload32*loop + mloadx - %COPYSP_STEP + 10*C + %MLOAD32_STEP*C - STEP :JMPN(failedCounters) - 261 - CNT_BINARY :JMPNZ(failedCounters) - ;%COPYSP_CNT_BINARY + %MLOAD32_CNT_BINARY*C - CNT_BINARY :JMPNZ(failedCounters) - 3 - CNT_ARITH :JMPNZ(failedCounters) - ;%COPYSP_CNT_ARITH + %MLOAD32_CNT_ARITH*C - CNT_ARITH :JMPNZ(failedCounters) - %COPYSP_CNT_KECCAK_F + %MLOAD32_CNT_KECCAK_F*C - CNT_KECCAK_F :JMPNZ(failedCounters) - 129 - CNT_MEM_ALIGN :JMPNZ(failedCounters) - ;%COPYSP_CNT_MEM_ALIGN + %MLOAD32_CNT_MEM_ALIGN*C - CNT_MEM_ALIGN :JMPNZ(failedCounters) - %COPYSP_CNT_PADDING_PG + %MLOAD32_CNT_PADDING_PG*C - CNT_PADDING_PG :JMPNZ(failedCounters) - %COPYSP_CNT_POSEIDON_G + %MLOAD32_CNT_POSEIDON_G*C - CNT_POSEIDON_G :JMPNZ(failedCounters) - -0 => A,B,C,D,E,CTX, SP, PC, GAS, SR, HASHPOS, RR ; Set all registers to 0 -finalizeExecution: - :JMP(finalWait) -readCode: - :RETURN -failedCounters: -2 => A -1 :ASSERT - -INCLUDE "../endIncludes.zkasm" \ No newline at end of file diff --git a/main/constants.zkasm b/main/constants.zkasm index 62ca8b1d..61692fd1 100644 --- a/main/constants.zkasm +++ b/main/constants.zkasm @@ -1,7 +1,7 @@ ; ZK_EVM CONSTL %ADDRESS_GLOBAL_EXIT_ROOT_MANAGER_L2 = 0xa40D5f56745a118D0906a34E69aeC8C0Db1cB8fAn CONSTL %ADDRESS_SYSTEM = 0x000000000000000000000000000000005ca1ab1en -CONST %CALLDATA_OFFSET = 1024 +CONST %MAX_STACK_SIZE = 1024 CONST %BATCH_DIFFICULTY = 0 CONST %TX_GAS_LIMIT = 30000000 CONST %GLOBAL_EXIT_ROOT_STORAGE_POS = 0 @@ -9,7 +9,8 @@ CONST %LOCAL_EXIT_ROOT_STORAGE_POS = 1 CONST %LAST_TX_STORAGE_POS = 0 CONST %STATE_ROOT_STORAGE_POS = 1 CONST %MAX_MEM_EXPANSION_BYTES = 0x3fffe0 -CONST %FORK_ID = 4 +CONST %FORK_ID = 5 +CONST %CALLDATA_RESERVED_CTX = 1 ; RLP CONST %MIN_VALUE_SHORT = 128 diff --git a/main/ecrecover/ecrecover.zkasm b/main/ecrecover/ecrecover.zkasm index 98436c4a..e77e0b60 100644 --- a/main/ecrecover/ecrecover.zkasm +++ b/main/ecrecover/ecrecover.zkasm @@ -72,7 +72,7 @@ ecrecover_store_args: 0n => A $ :EQ,JMPC(ecrecover_s_is_zero) - ; r and s in [1, FNEC-1] + ; compute r inverse $ => A :MLOAD(ecrecover_r),CALL(invFnEc) B :MSTORE(ecrecover_r_inv) diff --git a/main/load-tx-rlp.zkasm b/main/load-tx-rlp.zkasm index b58fd9db..ca6f7c91 100644 --- a/main/load-tx-rlp.zkasm +++ b/main/load-tx-rlp.zkasm @@ -17,7 +17,7 @@ INCLUDE "load-tx-rlp-utils.zkasm" loadTx_rlp: ; check one keccak is available to begin processing the RLP $ => D :MLOAD(cntKeccakPreProcess) - %MAX_CNT_KECCAK_F - CNT_KECCAK_F - 1 - D :JMPN(handleOOCKatRLP) + %MAX_CNT_KECCAK_F - CNT_KECCAK_F - 1 - D :JMPN(outOfCountersKeccak) ; A new hash with position 0 is started 0 => HASHPOS @@ -59,7 +59,7 @@ endList: 136 :MSTORE(arithB), CALL(divARITH); in: [arithA, arithB] out: [arithRes1: arithA/arithB, arithRes2: arithA%arithB] $ => B :MLOAD(arithRes1) $ => D :MLOAD(cntKeccakPreProcess) - %MAX_CNT_KECCAK_F - CNT_KECCAK_F - B - D - 1:JMPN(handleOOCKatRLP) + %MAX_CNT_KECCAK_F - CNT_KECCAK_F - B - D - 1:JMPN(outOfCountersKeccak) ;; Read RLP 'nonce' ; 64 bits max @@ -171,10 +171,13 @@ endValue: ;; Read RLP 'data' ; should not be a list dataREAD: + ; Set calldata offset and CTX + $ => D :MLOAD(globalCalldataMemoryOffset) + %CALLDATA_RESERVED_CTX :MSTORE(calldataCTX) + D * 32 :MSTORE(calldataOffset) $ => D :MLOAD(batchHashPos) D :MSTORE(dataStarts) - 1 => D - %CALLDATA_OFFSET => SP :CALL(addHashTx) + 1 => D :CALL(addHashTx) :CALL(addBatchHashData) A - 0x80 :JMPN(veryShortData) A - 0x81 :JMPN(endData) @@ -186,8 +189,17 @@ dataREAD: veryShortData: 1 :MSTORE(txCalldataLen) - 31 => D :CALL(SHLarith) - A :MSTORE(SP++), JMP(endData) + 31 => D :CALL(SHLarith) ; in: [A: value, D: #bytes to left shift] out: [A: shifted result] + ; Store current CTX + CTX => B + ; Store calldata to calldata CTX's memory + %CALLDATA_RESERVED_CTX => CTX + $ => E :MLOAD(globalCalldataMemoryOffset) + A :MSTORE(MEM:E) + E + 1 :MSTORE(globalCalldataMemoryOffset) + $ => E :MLOAD(lastHashKIdUsed) + ; Restore current CTX + B => CTX :JMP(endData) shortData: $ => D :MLOAD(batchHashPos) @@ -207,15 +219,31 @@ readData: 32 => D B - D :JMPN(readDataFinal) B - D :MSTORE(txDataRead), CALL(addHashTx) - A :MSTORE(SP++), CALL(addBatchHashByteByByte) + $ => E :MLOAD(globalCalldataMemoryOffset) + ; Store current CTX + CTX => B + ; Store calldata to calldata CTX's memory + %CALLDATA_RESERVED_CTX => CTX + A :MSTORE(MEM:E) + ; Restore current CTX + B => CTX + E + 1 :MSTORE(globalCalldataMemoryOffset), CALL(addBatchHashByteByByte) ; in: [A: bytes to add, D: bytes length] out: [E: lastHashKIdUsed, A: shifted bytes to add] $ => B :MLOAD(txDataRead), JMP(readData) readDataFinal: B - 1 :JMPN(endData) B => D :CALL(addHashTx) - 32 - D => D :CALL(SHLarith) - A :MSTORE(SP) - 32 - D => D :CALL(addBatchHashByteByByte) + 32 - D => D :CALL(SHLarith); in: [A: value, D: #bytes to left shift] out: [A: shifted result] + $ => E :MLOAD(globalCalldataMemoryOffset) + ; Store current CTX + CTX => B + ; Store calldata to calldata CTX's memory + %CALLDATA_RESERVED_CTX => CTX + A :MSTORE(MEM:E) + ; Restore current CTX + B => CTX + E + 1 :MSTORE(globalCalldataMemoryOffset) + 32 - D => D :CALL(addBatchHashByteByByte); in: [A: bytes to add, D: bytes length] out: [E: lastHashKIdUsed, A: shifted bytes to add] :CALL(checkShortDataRLP) endData: @@ -285,15 +313,20 @@ vREADTx: A :MSTORE(txV) C + D => C :CALL(addBatchHashData) +;; read effective percentage +effectivePercentageTx: + 1 => D :CALL(getTxBytes) + A :MSTORE(effectivePercentageRLP) + C + D => C :CALL(addBatchHashData) ;;;;;;;;; ;; D - Finish RLP parsing ;;;;;;;;; ;; update bytes parsed $ => A :MLOAD(batchL2DataParsed) - A + C => A :MSTORE(batchL2DataParsed) + A + C :MSTORE(batchL2DataParsed) ;; increase number of transaction to process $ => A :MLOAD(pendingTxs) - A + 1 => A :MSTORE(pendingTxs) + A + 1 :MSTORE(pendingTxs) ;; compute signature $ => A :HASHKDIGEST(E) A :MSTORE(txHash), JMP(txLoopRLP) @@ -301,19 +334,14 @@ vREADTx: ;;;;;;;;; ;; E - Handler error RLP fields ;;;;;;;;; -handleOOCBatRLP: - $${eventLog(onError, OOCB)} :JMP(handleOOCatRLP) -handleOOCKatRLP: - $${eventLog(onError, OOCK)} :JMP(handleOOCatRLP) -handleOOCSatRLP: - $${eventLog(onError, OOCS)} :JMP(handleOOCatRLP) -handleOOCatRLP: - $ => SR :MLOAD(batchSR) - invalidTxRLP: + $${eventLog(onError, invalidRLP)} :JMP(appendTxsInit) + +appendTxsInit: ;; Append all missing 'batchL2Data' to 'batchDataHash' bytes $ => B :MLOAD(batchL2DataLength) $ => C :MLOAD(batchHashPos) + $${p = C} $ => HASHPOS :MLOAD(batchHashPos) $ => E :MLOAD(batchHashDataId) diff --git a/main/main.zkasm b/main/main.zkasm index ec4b62c4..fecd9f08 100644 --- a/main/main.zkasm +++ b/main/main.zkasm @@ -87,7 +87,7 @@ skipSetGlobalExitRoot: $ => B :MLOAD(arithRes1) ; Compute minimum necessary keccaks to finish the batch B + 1 + %MIN_CNT_KECCAK_BATCH => B :MSTORE(cntKeccakPreProcess) - %MAX_CNT_KECCAK_F - CNT_KECCAK_F - B :JMPN(handleOOCKatRLP) + %MAX_CNT_KECCAK_F - CNT_KECCAK_F - B :JMPN(outOfCountersKeccak) ;;;;;;;;;;;;;;;;;; ;; C - Loop parsing RLP transactions @@ -99,9 +99,11 @@ skipSetGlobalExitRoot: 0 :MSTORE(batchHashPos) E :MSTORE(batchHashDataId) $ => A :MLOAD(lastCtxUsed) - A :MSTORE(ctxTxToUse) ; Points at first context to be used when processing transactions - + A + %CALLDATA_RESERVED_CTX => A :MSTORE(ctxTxToUse) ; Points at first context to be used when processing transactions. We reserve ctx = 1 for calldata + A :MSTORE(lastCtxUsed) $${var p = 0} + ; set flag isLoadingRLP to 1 + 1 :MSTORE(isLoadingRLP) txLoopRLP: $ => A :MLOAD(lastCtxUsed) @@ -110,7 +112,10 @@ txLoopRLP: $ => A :MLOAD(batchL2DataLength) $ => C :MLOAD(batchL2DataParsed) C - A :JMPN(loadTx_rlp, endCheckRLP) + endCheckRLP: + ; set flag isLoadingRLP to 0 + 0 :MSTORE(isLoadingRLP) :JMP(txLoop) ;;;;;;;;;;;;;;;;;; @@ -120,10 +125,10 @@ endCheckRLP: txLoop: $ => A :MLOAD(pendingTxs) - A-1 => A :MSTORE(pendingTxs), JMPN(processTxsEnd) + A - 1 :MSTORE(pendingTxs), JMPN(processTxsEnd) $ => A :MLOAD(ctxTxToUse) ; Load first context used by transaction - A+1 => CTX :MSTORE(ctxTxToUse),JMP(processTx) + A + 1 => CTX :MSTORE(ctxTxToUse),JMP(processTx) processTxEnd: :CALL(updateSystemData) diff --git a/main/map-opcodes.zkasm b/main/map-opcodes.zkasm index f0f754ad..720c41fa 100644 --- a/main/map-opcodes.zkasm +++ b/main/map-opcodes.zkasm @@ -111,7 +111,7 @@ mapping_opcodes: :JMP(opINVALID) ; 0x5C :JMP(opINVALID) ; 0x5D :JMP(opINVALID) ; 0x5E - :JMP(opINVALID) ; 0x5F + :JMP(opPUSH0) ; 0x5F :JMP(opPUSH1) ; 0x60 :JMP(opPUSH2) ; 0x61 :JMP(opPUSH3) ; 0x62 diff --git a/main/opcodes/block.zkasm b/main/opcodes/block.zkasm index 83e52e42..174478df 100644 --- a/main/opcodes/block.zkasm +++ b/main/opcodes/block.zkasm @@ -62,7 +62,7 @@ opCOINBASE: $ => A :MLOAD(sequencerAddr) A :MSTORE(SP++); [coinbase address => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) /** * @link [https://www.evm.codes/#42?fork=berlin] @@ -81,7 +81,7 @@ opTIMESTAMP: $ => A :MLOAD(timestamp) A :MSTORE(SP++); [timestamp => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) /** * @link [https://www.evm.codes/#43?fork=berlin] @@ -105,7 +105,7 @@ opNUMBER: ; call binary:add state machine and push to the stack $ :ADD, MSTORE(SP++); [blockNumber => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) /** * @link [https://www.evm.codes/#44?fork=berlin] @@ -125,7 +125,7 @@ opDIFFICULTY: %BATCH_DIFFICULTY => A A :MSTORE(SP++); [difficulty => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) /** * @link [https://www.evm.codes/#45?fork=berlin] @@ -145,7 +145,7 @@ opGASLIMIT: %TX_GAS_LIMIT => A A :MSTORE(SP++); [gasLimit => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) /** * @link [https://www.evm.codes/#46?fork=berlin] @@ -164,4 +164,4 @@ opCHAINID: $ => A :MLOAD(chainID) A :MSTORE(SP++); [chainId => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) \ No newline at end of file + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) \ No newline at end of file diff --git a/main/opcodes/calldata-returndata-code.zkasm b/main/opcodes/calldata-returndata-code.zkasm index 377ce6ea..fd85e440 100644 --- a/main/opcodes/calldata-returndata-code.zkasm +++ b/main/opcodes/calldata-returndata-code.zkasm @@ -12,37 +12,22 @@ ; checks zk-counters %MAX_CNT_STEPS - STEP - 100 :JMPN(outOfCountersStep) %MAX_CNT_BINARY - CNT_BINARY - 1 :JMPN(outOfCountersBinary) - %MAX_CNT_MEM_ALIGN - CNT_MEM_ALIGN - 1 :JMPN(outOfCountersMemalign) ; check stack underflow SP - 1 => SP :JMPN(stackUnderflow) ; check out-of-gas GAS - %GAS_FASTEST_STEP => GAS :JMPN(outOfGas) + ; return 0 for contract creation + $ :MLOAD(isCreateContract), JMPNZ(CALLDATALOADreturn0) $ => A :MLOAD(SP); [offset => A] $ => B :MLOAD(txCalldataLen) - ; if offset is not lower than calldata length, return 0 + ; if offset is not lower than calldata length, return 0. + ; This check is also done at `readFromCalldataOffset` but we do it here with a binary to be sure offset is lower than 32 bits $ :LT,JMPNC(CALLDATALOADreturn0) - :CALL(offsetUtil); in: [A: offset] out: [E: offset/32, C: offset%32] - ; store SP at D - SP => D - ; add %CALLDATA_OFFSET to point calldata in the memory - %CALLDATA_OFFSET + E => SP - C :JMPNZ(opCALLDATALOAD2) ; data not aligned - $ => B :MLOAD(SP); [stack => B] - ; recover previous stack pointer - D => SP - B :MSTORE(SP++), JMP(readCode); [data(offset) => SP] - -opCALLDATALOAD2: - $ => A :MLOAD(SP++) - ; reading beyond the calldata length will result in reading 0 - ; calldata memory reservation in zkEVM (2**16 * 32 bytes) is larger than maximum bytes allowed as calldata fixed in the smart contract (300.000 bytes) - ; therefore it is assured that remaining memory space will be always 0 and never writen or over exceed - $ => B :MLOAD(SP) - $ => A :MEM_ALIGN_RD - ; recover SP - D => SP - A :MSTORE(SP++), JMP(readCode); [data(offset) => SP] + 32 :MSTORE(readXFromCalldataLength) + A :MSTORE(readXFromCalldataOffset), CALL(readFromCalldataOffset); in: [readXFromCalldataOffset: offset value, readXFromCalldataLength: length value], out: [readXFromCalldataOffset: result value] + $ => A :MLOAD(readXFromCalldataResult) + A :MSTORE(SP++), JMP(readCode); [data(offset) => SP] CALLDATALOADreturn0: 0 :MSTORE(SP++), JMP(readCode); [0 => SP] @@ -61,20 +46,20 @@ opCALLDATASIZE: ; check out-of-gas GAS-%GAS_QUICK_STEP => GAS :JMPN(outOfGas) ; return 0 for contract creation - $ => A :MLOAD(isCreateContract), JMPNZ(opCALLDATASIZEdep) + $ :MLOAD(isCreateContract), JMPNZ(opCALLDATASIZEdep) $ => B :MLOAD(txCalldataLen) B :MSTORE(SP++); [size => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) opCALLDATASIZEdep: 0 :MSTORE(SP++); [0 => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) VAR GLOBAL auxDestOffset -VAR GLOBAL calldataOffset -VAR GLOBAL auxOffset +VAR GLOBAL calldataCopyOffset ; current calldatacopy offset +VAR GLOBAL auxOffset ; original calldatacopy offset VAR GLOBAL numLastBytes /** * @link [https://www.evm.codes/#37?fork=berlin] @@ -89,12 +74,12 @@ opCALLDATACOPY: ; checks zk-counters %MAX_CNT_STEPS - STEP - 100 :JMPN(outOfCountersStep) %MAX_CNT_BINARY - CNT_BINARY - 2 :JMPN(outOfCountersBinary) - %MAX_CNT_MEM_ALIGN - CNT_MEM_ALIGN - 2 :JMPN(outOfCountersMemalign) ; check stack underflow SP - 3 => SP :JMPN(stackUnderflow) $ => E :MLOAD(SP+2); [destOffset => E] $ => B :MLOAD(SP+1); [offset => B] B :MSTORE(auxOffset) + B :MSTORE(calldataCopyOffset) $ => C :MLOAD(SP); [size => C] C :MSTORE(numLastBytes) ; store lastMemOffset for memory expansion gas cost @@ -108,19 +93,25 @@ opCALLDATACOPY: C+31 => A :CALL(offsetUtil); in: [A: offset] out: [E: offset/32, C: offset%32] GAS - 3*E => GAS :JMPN(outOfGas) - ; save current stack pointer - SP :MSTORE(SPw) ; Recover destOffset at E $ => E :MLOAD(lastMemOffset) ; Recover size at C $ => C :MLOAD(lastMemLength) + ; If is a calldataCopy called from codeCopy, copy code from calldata alloccation, else if is a create contract, return 0 + $ :MLOAD(isCalldataCopyFromCodeCopy), JMPNZ(continueOpCalldatacopyFromCodeCopy) + $ :MLOAD(isCreateContract), JMPNZ(opCALLDATACOPY0, continueOpCalldatacopy) + continueOpCalldatacopyFromCodeCopy: + 0 :MSTORE(isCalldataCopyFromCodeCopy) + continueOpCalldatacopy: B => A $ => B :MLOAD(txCalldataLen); less than 2**32 bytes (calldata). Enforced by memory expansion gas cost & smart contract batchL2DataHash ; if offset is not lower than calldata length, return 0 + ; This check is also done at `readFromCalldataOffset` but we do it here with a binary to be sure offset is lower than 32 bits $ :LT,JMPNC(opCALLDATACOPY0) B => A $ => B :MLOAD(auxOffset) B + C => B + 32 :MSTORE(readXFromCalldataLength) ; if txCalldataLen < (offset + size) --> opCALLDATACOPYX0 $ :LT,JMPC(opCALLDATACOPYX0) $ => B :MLOAD(auxOffset),JMP(opCALLDATACOPYloop) @@ -137,17 +128,13 @@ opCALLDATACOPYloop: C :JMPZ(opCALLDATACOPYcheckLen) ; copy last bytes C - 32 :JMPN(opCALLDATACOPYfinal) - B => A :MSTORE(calldataOffset) - E => B - :CALL(offsetUtil); in: [A: offset] out: [E: offset/32, C: offset%32] - ; add %CALLDATA_OFFSET to offset to reach calldata in memory - ; set stack pointer to first byte to read - %CALLDATA_OFFSET + E => SP - B => E - $ => A :MLOAD(SP++) - $ => B :MLOAD(SP) - $ => A :MEM_ALIGN_RD, MSTORE(bytesToStore) - E => A + B + 32 :MSTORE(calldataCopyOffset) + B :MSTORE(readXFromCalldataOffset), CALL(readFromCalldataOffset); in: [readXFromCalldataOffset: offset value, readXFromCalldataLength: length value], out: [readXFromCalldataResult: result value] + $ => A :MLOAD(readXFromCalldataResult) + A :MSTORE(bytesToStore) + $ => A :MLOAD(lastMemOffset) + ; checks zk-counters + %MAX_CNT_MEM_ALIGN - CNT_MEM_ALIGN - 1 :JMPN(outOfCountersMemalign) ; Store 32 bytes from calldata to memory :CALL(offsetUtil); in: [A: offset] out: [E: offset/32, C: offset%32] E :MSTORE(auxDestOffset) @@ -165,31 +152,15 @@ opCALLDATACOPYloop: C - 32 => C :MSTORE(numLastBytes) $ => E :MLOAD(lastMemOffset) E + 32 => E :MSTORE(lastMemOffset) - $ => B :MLOAD(calldataOffset) - B + 32 => B :JMP(opCALLDATACOPYloop) + $ => B :MLOAD(calldataCopyOffset), JMP(opCALLDATACOPYloop) opCALLDATACOPYfinal: ; copy last bytes - C :MSTORE(numLastBytes) - B => A - E => D - :CALL(offsetUtil); in: [A: offset] out: [E: offset/32, C: offset%32] - ; add %CALLDATA_OFFSET to offset to reach calldata in memory - ; set SP to calldata - %CALLDATA_OFFSET + E => SP - D => E - C => D - $ => A :MLOAD(SP++), CALL(SHLarith); [calldata => A]; in: [A: value, D: #bytes to left shift] out: [A: shifted result] - ; Recover init SP to recover input size - $ => C :MLOAD(numLastBytes); [size => C] - ; point to next memory slot - C - 32 + D => D :JMPN(opCALLDATACOPYxor) - D :JMPZ(opCALLDATACOPYxor) - A => B - $ => A :MLOAD(SP); [calldata => C] - 32 - D => D :CALL(SHRarith); in: [A: value, D: #bytes to right shift] out: [A: shifted result] - 32 - C => D :CALL(SHLarith); in: [A: value, D: #bytes to left shift] out: [A: shifted result] - B + A => A + C :MSTORE(readXFromCalldataLength) + $ => D :MLOAD(calldataCopyOffset) + D :MSTORE(readXFromCalldataOffset), CALL(readFromCalldataOffset); in: [readXFromCalldataOffset: offset value, readXFromCalldataLength: length value], out: [readXFromCalldataResult: result value] + $ => A :MLOAD(readXFromCalldataResult) + ; set bytesToStore with value to use in MSTORE A :MSTORE(bytesToStore), CALL(MSTOREX); in: [bytesToStore, E: offset, C: length] out: [E: new offset] :JMP(opCALLDATACOPYcheckLen) @@ -206,13 +177,6 @@ opCALLDATACOPY320: 0 :MSTORE(bytesToStore), CALL(MSTORE32); in: [bytesToStore, E: offset] out: [E: new offset] C - 32 => C :JMP(opCALLDATACOPY0) - -opCALLDATACOPYxor: - 32 - C => D :CALL(SHRarith); in: [A: value, D: #bytes to right shift] out: [A: shifted result] - :CALL(SHLarith); in: [A: value, D: #bytes to left shift] out: [A: shifted result] - ; set bytesToStore with value to use in MSTORE - A :MSTORE(bytesToStore), CALL(MSTOREX); in: [bytesToStore, E: offset, C: length] out: [E: new offset] - opCALLDATACOPYcheckLen: ; fill missing values with 0 (size > calldata) $ => C :MLOAD(lastMemLength) @@ -222,8 +186,7 @@ opCALLDATACOPYcheckLen: opCALLDATACOPYend: - ; retrieve SP - $ => SP :MLOAD(SPw), JMP(readCode) + :JMP(readCode) /** * @link [https://www.evm.codes/#38?fork=berlin] @@ -250,13 +213,13 @@ opCODESIZE: $ => A :SLOAD A :MSTORE(SP++); [size => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) opCODESIZEdep: $ => B :MLOAD(txCalldataLen) B :MSTORE(SP++); [size => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) VAR GLOBAL memOffset VAR GLOBAL remainingBytes @@ -264,6 +227,8 @@ VAR GLOBAL previousValue VAR GLOBAL codecopyHashId VAR GLOBAL codecopyBytecodeLength VAR GLOBAL memInteger +; Flag to check if the calldataCopy function is being called from the codecopy function +VAR CTX isCalldataCopyFromCodeCopy /** * @link [https://www.evm.codes/#39?fork=berlin] * @zk-counters @@ -279,7 +244,9 @@ opCODECOPY: ;Check counters %MAX_CNT_STEPS - STEP - 100 :JMPN(outOfCountersStep) ; if is a create, copy from calldata - $ => A :MLOAD(isCreateContract), JMPNZ(opCALLDATACOPY) + $ => A :MLOAD(isCreateContract), JMPZ(continueOpCODECOPY) + 1 :MSTORE(isCalldataCopyFromCodeCopy), JMP(opCALLDATACOPY) +continueOpCODECOPY: ; check stack underflow SP - 3 => SP :JMPN(stackUnderflow) $ => C :MLOAD(SP+2); [destOffset => C] @@ -531,7 +498,7 @@ opRETURNDATASIZE: endOpRETURNDATASIZE: A :MSTORE(SP++); [size => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) /** * @link [https://www.evm.codes/#3E?fork=berlin] diff --git a/main/opcodes/context-information.zkasm b/main/opcodes/context-information.zkasm index c2d7d005..2c105471 100644 --- a/main/opcodes/context-information.zkasm +++ b/main/opcodes/context-information.zkasm @@ -23,7 +23,7 @@ opADDRESSdeploy: opADDRESSend: A :MSTORE(SP++); [address(A) => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) /** * @link [https://www.evm.codes/#47?fork=berlin] @@ -48,7 +48,7 @@ opSELFBALANCE: $ => D :SLOAD D :MSTORE(SP++); [balance(D) => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) /** * @link [https://www.evm.codes/#31?fork=berlin] @@ -92,7 +92,7 @@ opORIGIN: $ => A :MLOAD(txSrcOriginAddr) A :MSTORE(SP++); [address(A) => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) /** * @link [https://www.evm.codes/#33?fork=berlin] @@ -111,7 +111,7 @@ opCALLER: $ => A :MLOAD(txSrcAddr) A :MSTORE(SP++); [address(A) => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) /** * @link [https://www.evm.codes/#34?fork=berlin] @@ -130,7 +130,7 @@ opCALLVALUE: $ => A :MLOAD(txValue) A :MSTORE(SP++); [value(A) => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) /** * @link [https://www.evm.codes/#3A?fork=berlin] @@ -149,7 +149,7 @@ opGASPRICE: $ => A :MLOAD(txGasPrice) A :MSTORE(SP++); [price(A) => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) /** * @link [https://www.evm.codes/#5A?fork=berlin] @@ -166,4 +166,4 @@ opGAS: GAS - %GAS_QUICK_STEP => GAS :JMPN(outOfGas) GAS :MSTORE(SP++); [gas(GAS) => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) diff --git a/main/opcodes/create-terminate-context.zkasm b/main/opcodes/create-terminate-context.zkasm index eab7fafc..febcae8e 100644 --- a/main/opcodes/create-terminate-context.zkasm +++ b/main/opcodes/create-terminate-context.zkasm @@ -143,11 +143,11 @@ opCREATE: ; update GAS to computed gas send call A => GAS B :MSTORE(txSrcAddr) - ; copy calldata from origin CTX to current CTX - C :MSTORE(gasRefund), CALL(copySP) + C :MSTORE(gasRefund) ; set CTX var values $ => A :MLOAD(argsLengthCall) - A :MSTORE(txCalldataLen) + ; set calldata pointers to current CTX + A :MSTORE(txCalldataLen), CALL(saveCalldataPointer) $ => A :MLOAD(valueCall) ; Save touched root when a new context is created A :MSTORE(txValue), CALL(checkpointTouched) @@ -277,8 +277,8 @@ opCALLend: ; update GAS to computed gas send call A => GAS $ => A :MLOAD(argsLengthCall) - ; copy calldata from origin CTX to current CTX - A :MSTORE(txCalldataLen), CALL(copySP) + ; set calldata pointers to current CTX + A :MSTORE(txCalldataLen), CALL(saveCalldataPointer) ; save touched root when a new context is created :CALL(checkpointTouched) :JMP(txType) @@ -385,8 +385,8 @@ opCALLCODEend: A => GAS D :MSTORE(isStaticCall) $ => A :MLOAD(argsLengthCall) - ; copy calldata from origin CTX to current CTX - A :MSTORE(txCalldataLen), CALL(copySP) + ; set calldata pointers to current CTX + A :MSTORE(txCalldataLen), CALL(saveCalldataPointer) ; Save touched root when a new context is created :CALL(checkpointTouched) :JMP(txType) @@ -500,40 +500,30 @@ opRETURNcreate: ; save offset memory and length to compute contract hash E :MSTORE(memOffsetLinearPoseidon) - C :MSTORE(memSizeLinearPoseidon) - ; decrease context and set values - CTX :MSTORE(currentCTX) - $ => B :MLOAD(gasRefund) - $ => CTX :MLOAD(originCTX) - B :MSTORE(gasRefund) - $ => B :MLOAD(gasCTX) - B + GAS => GAS - $ => CTX :MLOAD(currentCTX) - $ => A :MLOAD(txSrcAddr) - $ => D :MLOAD(txNonce) - - ; set bytecode length and read bytecode first byte - $ => A :MLOAD(createContractAddress), CALL(checkBytecodeStartsEF) ; in: [memOffset], out: [startsWithEFn - + C :MSTORE(memSizeLinearPoseidon), CALL(checkBytecodeStartsEF) ; in: [memOffset], out: [startsWithEF] ; check bytecode first byte != 0xEF $ :MLOAD(startsWithEF), JMPNZ(invalidCodeStartsEF) - + ; set bytecode length ; set key for smt smart contract length query + $ => A :MLOAD(createContractAddress) %SMT_KEY_SC_LENGTH => B C => D 0 => C ; get bytecode and hash it $ => SR :SSTORE, CALL(hashPoseidonLinearFromMemory); in: [memOffsetLinearPoseidon, memSizeLinearPoseidon], out: [D: resulting linear poseidon] - $ => A :MLOAD(createContractAddress) - 0 => C ; set key for smt smart contract bytecode query %SMT_KEY_SC_CODE => B $ => SR :SSTORE opRETURNcreateEnd: + ; return to origin CTX - $ => A :MLOAD(createContractAddress) + $ => B :MLOAD(gasRefund) $ => CTX :MLOAD(originCTX) + ; Update gasRefund at origin CTX + B :MSTORE(gasRefund) + $ => B :MLOAD(gasCTX) + B + GAS => GAS CTX :MSTORE(currentCTX) 0 :MSTORE(retDataCTX) ; set SP and PC @@ -632,8 +622,8 @@ opDELEGATECALL: opDELEGATECALLend: $ => A :MLOAD(argsLengthCall) - ; copy calldata from origin CTX to current CTX - A :MSTORE(txCalldataLen), CALL(copySP) + ; set calldata pointers to current CTX + A :MSTORE(txCalldataLen), CALL(saveCalldataPointer) ; Save touched root when a new context is created :CALL(checkpointTouched) :JMP(txType) @@ -751,11 +741,11 @@ opCREATE2: $ => CTX :MLOAD(currentCTX) C :MSTORE(gasRefund) ; update GAS to computed gas send call - ; copy calldata from origin CTX to current CTX - A => GAS :CALL(copySP) + A => GAS ; set CTX var values $ => A :MLOAD(argsLengthCall) - A :MSTORE(txCalldataLen) + ; set calldata pointers to current CTX + A :MSTORE(txCalldataLen), CALL(saveCalldataPointer) $ => A :MLOAD(valueCall) ; Save touched root when a new context is created A :MSTORE(txValue), CALL(checkpointTouched) @@ -837,8 +827,8 @@ opSTATICCALL: ; update GAS to computed gas send call A => GAS $ => A :MLOAD(argsLengthCall) - ; copy calldata from origin CTX to current CTX - A :MSTORE(txCalldataLen), CALL(copySP) + ; set calldata pointers to current CTX + A :MSTORE(txCalldataLen), CALL(saveCalldataPointer) ; save touched root when a new context is created :CALL(checkpointTouched) :JMP(txType) diff --git a/main/opcodes/flow-control.zkasm b/main/opcodes/flow-control.zkasm index b7f86999..9a090add 100644 --- a/main/opcodes/flow-control.zkasm +++ b/main/opcodes/flow-control.zkasm @@ -107,7 +107,7 @@ opPC: ; store current PC PC - 1 :MSTORE(SP++); [PC => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) /** diff --git a/main/opcodes/stack-operations.zkasm b/main/opcodes/stack-operations.zkasm index cb1dc3be..b19dbc50 100644 --- a/main/opcodes/stack-operations.zkasm +++ b/main/opcodes/stack-operations.zkasm @@ -1,3 +1,22 @@ +/** + * @link [https://www.evm.codes/#5f?fork=shanghai] + * @zk-counters + * - 100 steps + * @process-opcode + * - stack input: none + * - stack output: [0] + */ +opPUSH0: + ; checks zk-counters + %MAX_CNT_STEPS - STEP - 100 :JMPN(outOfCountersStep) + + ; check out-of-gas + GAS - %GAS_QUICK_STEP => GAS :JMPN(outOfGas) + ; store stack output + 0 :MSTORE(SP++); [0 => SP] + ; check stack overflow + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) + /** * @link [https://www.evm.codes/#60?fork=berlin] * @zk-counters @@ -154,7 +173,7 @@ opAuxPUSHA2: ; store stack output E :MSTORE(SP++); [pushed_value(E) => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow) ; rectify PC PC + D => PC :JMP(readCode) @@ -197,7 +216,7 @@ opAuxPUSHBend: ; store stack output (value) A :MSTORE(SP++); [pushed_value(E) => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) opAuxPUSHBcreate: ; get bytes length @@ -250,7 +269,7 @@ opDUP1: A :MSTORE(SP++); [duplicated_value(A) => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) opDUP2: @@ -262,7 +281,7 @@ opDUP2: $ => A :MLOAD(SP-2) A :MSTORE(SP++) - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) opDUP3: @@ -274,7 +293,7 @@ opDUP3: $ => A :MLOAD(SP-3) A :MSTORE(SP++) - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) opDUP4: @@ -286,7 +305,7 @@ opDUP4: $ => A :MLOAD(SP-4) A :MSTORE(SP++) - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) opDUP5: @@ -298,7 +317,7 @@ opDUP5: $ => A :MLOAD(SP-5) A :MSTORE(SP++) - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) opDUP6: @@ -310,7 +329,7 @@ opDUP6: $ => A :MLOAD(SP-6) A :MSTORE(SP++) - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) opDUP7: @@ -322,7 +341,7 @@ opDUP7: $ => A :MLOAD(SP-7) A :MSTORE(SP++) - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) opDUP8: @@ -334,7 +353,7 @@ opDUP8: $ => A :MLOAD(SP-8) A :MSTORE(SP++) - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) opDUP9: @@ -346,7 +365,7 @@ opDUP9: $ => A :MLOAD(SP-9) A :MSTORE(SP++) - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) opDUP10: @@ -358,7 +377,7 @@ opDUP10: $ => A :MLOAD(SP-10) A :MSTORE(SP++) - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) opDUP11: @@ -370,7 +389,7 @@ opDUP11: $ => A :MLOAD(SP-11) A :MSTORE(SP++) - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) opDUP12: @@ -382,7 +401,7 @@ opDUP12: $ => A :MLOAD(SP-12) A :MSTORE(SP++) - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) opDUP13: @@ -394,7 +413,7 @@ opDUP13: $ => A :MLOAD(SP-13) A :MSTORE(SP++) - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) opDUP14: @@ -406,7 +425,7 @@ opDUP14: $ => A :MLOAD(SP-14) A :MSTORE(SP++) - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) opDUP15: @@ -418,7 +437,7 @@ opDUP15: $ => A :MLOAD(SP-15) A :MSTORE(SP++) - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) opDUP16: @@ -430,7 +449,7 @@ opDUP16: $ => A :MLOAD(SP-16) A :MSTORE(SP++) - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) /** * @link [https://www.evm.codes/#90?fork=berlin] diff --git a/main/opcodes/storage-memory.zkasm b/main/opcodes/storage-memory.zkasm index ce5c0342..dd545665 100644 --- a/main/opcodes/storage-memory.zkasm +++ b/main/opcodes/storage-memory.zkasm @@ -138,7 +138,7 @@ opMSIZE: MSIZEend: E :MSTORE(SP++); [size(E) => SP] ; check stack overflow - %CALLDATA_OFFSET - SP :JMPN(stackOverflow, readCode) + %MAX_STACK_SIZE - SP :JMPN(stackOverflow, readCode) /** * @link [https://www.evm.codes/#54?fork=berlin] * @zk-counters @@ -187,14 +187,17 @@ opSSTORE: ; check stack underflow SP - 2 => SP :JMPN(stackUnderflow) - ; check out-of-gas - GAS - %SSTORE_ENTRY_EIP_2200_GAS - 1 :JMPN(outOfGas) - ; check is static call - $ => A :MLOAD(isStaticCall), JMPNZ(invalidStaticTx) $ => C :MLOAD(SP+1) ; [key => C] C :MSTORE(tmpVarCsstore) $ => D :MLOAD(SP) ; [value => D] + + $${eventLog(onUpdateStorage(C, D))} + + ; check out-of-gas + GAS - %SSTORE_ENTRY_EIP_2200_GAS - 1 :JMPN(outOfGas) + ; check is static call + $ => A :MLOAD(isStaticCall), JMPNZ(invalidStaticTx) ; check if is a create call $ => A :MLOAD(isCreateContract), JMPNZ(deploymentSSTORE) ; load current storage address @@ -307,5 +310,4 @@ opSSTOREsr: ; set key for smt storage query %SMT_KEY_SC_STORAGE => B $ => C :MLOAD(tmpVarCsstore); key => C - $${eventLog(onUpdateStorage(C, D))} $ => SR :SSTORE, JMP(readCode) diff --git a/main/precompiled/identity.zkasm b/main/precompiled/identity.zkasm index 314ba29c..e353a665 100644 --- a/main/precompiled/identity.zkasm +++ b/main/precompiled/identity.zkasm @@ -16,21 +16,25 @@ IDENTITY: $ => A :MLOAD(arithRes1) GAS - %IDENTITY_WORD_GAS*A => GAS :JMPN(outOfGas) - 0 => B, E, A :MSTORE(retDataOffset) + 0 => E, D :MSTORE(retDataOffset) C :MSTORE(retDataLength) - %CALLDATA_OFFSET => SP + 32 :MSTORE(readXFromCalldataLength) IDENTITYloop: %MAX_CNT_STEPS - STEP - 100 :JMPN(outOfCountersStep) ; Copy from calldata to memory C :JMPZ(IDENTITYreturn) C - 32 :JMPN(IDENTITYfinal) - $ => B :MLOAD(SP++) - B :MSTORE(bytesToStore), CALL(MSTORE32); in: [bytesToStore, E: offset] out: [E: new offset] + D :MSTORE(readXFromCalldataOffset), CALL(readFromCalldataOffset); in: [readXFromCalldataOffset: offset value, readXFromCalldataLength: length value], out: [readXFromCalldataResult: result value] + $ => A :MLOAD(readXFromCalldataResult) + D + 32 => D + A :MSTORE(bytesToStore), CALL(MSTORE32); in: [bytesToStore, E: offset] out: [E: new offset] C - 32 => C :JMP(IDENTITYloop) IDENTITYfinal: - $ => A :MLOAD(SP) + C :MSTORE(readXFromCalldataLength) + D :MSTORE(readXFromCalldataOffset), CALL(readFromCalldataOffset); in: [readXFromCalldataOffset: offset value, readXFromCalldataLength: length value], out: [readXFromCalldataResult: result value] + $ => A :MLOAD(readXFromCalldataResult) A :MSTORE(bytesToStore), CALL(MSTOREX); in: [bytesToStore, E: offset, C: length] out: [E: new offset] IDENTITYreturn: diff --git a/main/precompiled/pre-ecrecover.zkasm b/main/precompiled/pre-ecrecover.zkasm index bd019869..6458f96c 100644 --- a/main/precompiled/pre-ecrecover.zkasm +++ b/main/precompiled/pre-ecrecover.zkasm @@ -1,19 +1,25 @@ funcECRECOVER: ; Move balances if value > 0 just before executing the contract CALL - $ => B :MLOAD(txValue) + $ => B :MLOAD(txValue) 0 => A zkPC+2 => RR - $ :LT, JMPC(moveBalances) - - GAS - %ECRECOVER_GAS => GAS :JMPN(outOfGas) ; gas static = 3000 - - ; read data stored in SP + %CALLDATA_OFFSET - ; hash [32 bytes], v [32 bytes], r [32 bytes], s [32 bytes] - %CALLDATA_OFFSET => SP - $ => A :MLOAD(SP++) ; hash - $ => D :MLOAD(SP++) ; v - $ => B :MLOAD(SP++) ; r - $ => C :MLOAD(SP) ,CALL(ecrecover_precompiled) ; in: [A: hash, B: r, C: s, D: v], out: [A: result_ecrecover, B: result_code] + $ :LT, JMPC(moveBalances) + + GAS - %ECRECOVER_GAS => GAS :JMPN(outOfGas) ; gas static = 3000 + ; read data stored in calldata + ; read hash [32 bytes] + 32 :MSTORE(readXFromCalldataLength) + 0 => E :MSTORE(readXFromCalldataOffset), CALL(readFromCalldataOffset); in: [readXFromCalldataOffset: offset value, readXFromCalldataLength: length value], out: [readXFromCalldataResult: result value] + $ => A :MLOAD(readXFromCalldataResult) + ; read v [32 bytes] + E + 32 => E :MSTORE(readXFromCalldataOffset), CALL(readFromCalldataOffset); in: [readXFromCalldataOffset: offset value, readXFromCalldataLength: length value], out: [readXFromCalldataResult: result value] + $ => D :MLOAD(readXFromCalldataResult) + ; read r [32 bytes] + E + 32 => E :MSTORE(readXFromCalldataOffset), CALL(readFromCalldataOffset); in: [readXFromCalldataOffset: offset value, readXFromCalldataLength: length value], out: [readXFromCalldataResult: result value] + $ => B :MLOAD(readXFromCalldataResult) + ; read s [32 bytes] + E + 32 => E :MSTORE(readXFromCalldataOffset), CALL(readFromCalldataOffset); in: [readXFromCalldataOffset: offset value, readXFromCalldataLength: length value], out: [readXFromCalldataResult: result value] + $ => C :MLOAD(readXFromCalldataResult),CALL(ecrecover_precompiled) ; in: [A: hash, B: r, C: s, D: v], out: [A: result_ecrecover, B: result_code] B :JMPNZ(endECRECOVERFail) ; write ecrecover data into memory diff --git a/main/precompiled/revert-precompiled.zkasm b/main/precompiled/revert-precompiled.zkasm index d271ab9f..cfcef93d 100644 --- a/main/precompiled/revert-precompiled.zkasm +++ b/main/precompiled/revert-precompiled.zkasm @@ -10,6 +10,9 @@ revertPrecompiled: $ => A :MLOAD(originCTX), JMPZ(handleGas) ; first context A => CTX :MSTORE(currentCTX) + ; clear retDataCTX, no return data in revert precompiled + 0 :MSTORE(retDataCTX) + ; return gas not used to previous context $ => B :MLOAD(gasCTX) GAS + B => GAS diff --git a/main/process-tx.zkasm b/main/process-tx.zkasm index 68ea07de..0fa24e22 100644 --- a/main/process-tx.zkasm +++ b/main/process-tx.zkasm @@ -29,9 +29,12 @@ processTx: ; Store initial state at the beginning of the transaction SR :MSTORE(originSR) SR :MSTORE(initSR) + CTX :MSTORE(currentCTX) ; Minimum of 100000 steps left to process a tx %MAX_CNT_STEPS - STEP - 100000 :JMPN(outOfCountersStep) %MAX_CNT_BINARY - CNT_BINARY - 100 :JMPN(outOfCountersBinary) + %MAX_CNT_ARITH - CNT_ARITH - 2 :JMPN(outOfCountersArith) + $ => A :MLOAD(txHash) ; Check the signature $ => B :MLOAD(txR) @@ -68,9 +71,33 @@ endCheckChainId: :CALL(isColdAddress) ; add tx.origin to touched addresses 0 :MSTORE(depth) ; Initial depth is 0 -;; Set gasPrice global var +;; Set gasPrice global var depending on effectivePercentage [0-255] -> txGasPrice = Floor((gasPrice * (effectivePercentage + 1)) / 256) + ; gasPrice => A $ => A :MLOAD(txGasPriceRLP) - A :MSTORE(txGasPrice) + ; effectivePercentage => B + $ => B :MLOAD(effectivePercentageRLP) + ; B -> [1, 256] + B + 1 => B + ; A*B + C = D * 2**256 + op(E) + 0 => C + ; _effGasPriceShifted = gasPrice * (effectivePercentage + 1) + $${var _effGasPriceShifted = A * B} + ; get value above 256 bits + ${_effGasPriceShifted >> 256} => D + ; compute ARITH + ${_effGasPriceShifted} => E :ARITH + + ; txGasPrice = _effGasPriceShifted / 256 + 256 => B + ; (_effGasPriceShifted / 256)(A) * 256(B) + (_effGasPriceShifted % 256)(C) = D * 2**256 + op(E) + ${_effGasPriceShifted / 256} => A :MSTORE(txGasPrice) + ${_effGasPriceShifted % 256} => C + ; compute ARITH + E :ARITH + ; check divisor > remainder + C => A + 1 :LT + ;;;;;;;;;;;;;;;;;; ;; D - Verify and increase nonce ;;;;;;;;;;;;;;;;;; @@ -436,6 +463,10 @@ readCode: ;; Compute and save hash bytecode and bytecode length in the state-tree endDeploy: + ; checks zk-counters + %MAX_CNT_STEPS - STEP - 400 :JMPN(outOfCountersStep) + %MAX_CNT_POSEIDON_G - CNT_POSEIDON_G - %MAX_CNT_POSEIDON_SLOAD_SSTORE*2 :JMPN(outOfCountersPoseidon) + ; 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 diff --git a/main/utils.zkasm b/main/utils.zkasm index 6e6d9aab..d7314dcd 100644 --- a/main/utils.zkasm +++ b/main/utils.zkasm @@ -18,52 +18,6 @@ absIsNeg: $ => A :SUB 1 => B :RETURN -VAR GLOBAL tmpZkPCcopy -; @info copy calldata from previous context to current context -; @in argsOffsetCall: offset to copy from te calldata -; @in argsLengthCall: length to copy from the calldata -copySP: - %MAX_CNT_STEPS - STEP - 20 :JMPN(outOfCountersStep) - RR :MSTORE(tmpZkPCcopy) - ; store current ctx - CTX :MSTORE(currentCTX) - ; set SP at the beginning of calldata memory allocation - %CALLDATA_OFFSET => SP - ; retrieve previous context - $ => CTX :MLOAD(originCTX) - $ => E :MLOAD(argsOffsetCall) - $ => C :MLOAD(argsLengthCall) - -copySPloop: - ; checks zk-counters - %MAX_CNT_STEPS - STEP - 10 :JMPN(outOfCountersStep) - C :JMPZ(copySPEnd) - C - 32 :JMPN(copySPFinal) - ; load 32 bytes from previous context - zkPC+1 => RR :JMP(MLOAD32); in: [E: offset] out: [A: value, E: new offset] - ; restore current context - $ => CTX :MLOAD(currentCTX) - ; insert 32 bytes from previous context's calldata to current context's calldata - A :MSTORE(SP++) - ; load previous context for next iteration - $ => CTX :MLOAD(originCTX) - C - 32 => C :JMP(copySPloop) - -copySPFinal: - ; update RR to call a function from a call and load X bytes from previous context - zkPC+1 => RR :JMP(MLOADX); in: [E: offset, C: length] out: [A: value, E: new offset] - ; restore current context - $ => CTX :MLOAD(currentCTX) - ; insert X bytes from previous context's calldata to current context's calldata - A :MSTORE(SP++) - ; load previous context - $ => CTX :MLOAD(originCTX) - -copySPEnd: - ; restore RR to return to initial call correctly - $ => RR :MLOAD(tmpZkPCcopy) - $ => CTX :MLOAD(currentCTX), RETURN - VAR GLOBAL tmpVarBgetLen VAR GLOBAL tmpVarCgetLen VAR GLOBAL tmpVarDgetLen @@ -928,8 +882,10 @@ handleError: handleBatchError: ; restore init state root and finish batch $ => SR :MLOAD(batchSR) - $${eventLog(onFinishTx)} - $${eventLog(onFinishBatch)} :JMP(processTxsEnd) + ; if batch error is triggered while parsing the RLP, it jumps to 'appendTxsInit' + ; to fill the missing bytes to complete 'batchDataHash' + $ :MLOAD(isLoadingRLP),JMPNZ(appendTxsInit) + $${eventLog(onFinishTx)} :JMP(processTxsEnd) firstContextInvalid: ;save Root and jump to send gas to sequencer @@ -1151,21 +1107,24 @@ finalPush: $ => D :MLOAD(pushBytes), RETURN VAR GLOBAL tmpVarDaddB +VAR GLOBAL tmpVarBaddB VAR GLOBAL tmpZkPCaddB VAR GLOBAL auxBytes + ;@info: adds data to batchHashdata byte by byte ;@in: A: bytes to add ;@in D: bytes length addBatchHashByteByByte: - %MAX_CNT_STEPS - STEP - 10 :JMPN(handleOOCSatRLP) + %MAX_CNT_STEPS - STEP - 10 :JMPN(outOfCountersStep) RR :MSTORE(tmpZkPCaddB) A :MSTORE(auxBytes) D :MSTORE(tmpVarDaddB) + B :MSTORE(tmpVarBaddB) 1 => D utilsAddBatchHashBytebyByteLoop: - %MAX_CNT_STEPS - STEP - 50 :JMPN(handleOOCSatRLP) - %MAX_CNT_BINARY - CNT_BINARY - 1 :JMPN(handleOOCBatRLP) + %MAX_CNT_STEPS - STEP - 50 :JMPN(outOfCountersStep) + %MAX_CNT_BINARY - CNT_BINARY - 1 :JMPN(outOfCountersBinary) 32 - D => D $ => A :MLOAD(auxBytes), CALL(SHRarith); in: [A: value, D: #bytes to right shift] out: [A: shifted result] ; get last byte in A @@ -1180,6 +1139,7 @@ utilsAddBatchHashBytebyByteLoop: 33 - D => D $ => B :MLOAD(tmpVarDaddB) D - B - 1 :JMPN(utilsAddBatchHashBytebyByteLoop) + $ => B :MLOAD(tmpVarBaddB) $ => RR :MLOAD(tmpZkPCaddB) :RETURN @@ -1554,4 +1514,61 @@ expADend: ;@info function to force a failed assert failAssert: 1 => A - 2 :ASSERT \ No newline at end of file + 2 :ASSERT + + +VAR GLOBAL tmpZkPCreadXFromOffset +VAR GLOBAL readXFromCalldataOffset +VAR GLOBAL readXFromCalldataLength +VAR GLOBAL readXFromCalldataResult +VAR GLOBAL tmpVarAReadXFromOffset +VAR GLOBAL tmpVarBReadXFromOffset +VAR GLOBAL tmpVarCReadXFromOffset +VAR GLOBAL tmpVarDReadXFromOffset +VAR GLOBAL tmpVarEReadXFromOffset +; @info Reads {readXFromCalldataOffset} bytes (max 32) from a given offset in calldata memory. If offset or offset + length exceeds txCalldataLen, zeros are added +; @internalParam {readXFromCalldataOffset} offset to read from calldata +; @internalParam {readXFromCalldataLength} length to read from calldata +; @returns {readXFromCalldataResult} bytes read from calldata +readFromCalldataOffset: + %MAX_CNT_STEPS - STEP - 100 :JMPN(outOfCountersStep) + RR :MSTORE(tmpZkPCreadXFromOffset) + A :MSTORE(tmpVarAReadXFromOffset) + B :MSTORE(tmpVarBReadXFromOffset) + C :MSTORE(tmpVarCReadXFromOffset) + E :MSTORE(tmpVarEReadXFromOffset) + $ => A :MLOAD(txCalldataLen), JMPZ(return0FromCalldata) + $ => B :MLOAD(calldataOffset) + $ => C :MLOAD(readXFromCalldataLength) + $ => E :MLOAD(readXFromCalldataOffset) + A - E - 1 :JMPN(return0FromCalldata) ; If offset >= txCalldataLen, return 0 + ; if readOffset + readLength > txCalldataLen, readLength = txCalldataLen - readOffset + E + C - A :JMPN(dontLimitLength) + A - E => C + +dontLimitLength: + $ => CTX :MLOAD(calldataCTX) ; get calldata context + B + E => E :CALL(MLOADX); in: [E: offset, C: length] out: [A: value, E: new offset] + $ => CTX :MLOAD(currentCTX) + A :MSTORE(readXFromCalldataResult), JMP(readFromCalldataOffsetEnd) + +return0FromCalldata: + 0 :MSTORE(readXFromCalldataResult) + +readFromCalldataOffsetEnd: + $ => RR :MLOAD(tmpZkPCreadXFromOffset) + $ => A :MLOAD(tmpVarAReadXFromOffset) + $ => B :MLOAD(tmpVarBReadXFromOffset) + $ => C :MLOAD(tmpVarCReadXFromOffset) + $ => E :MLOAD(tmpVarEReadXFromOffset), RETURN + +; @info store calldata pointer to read calldata +saveCalldataPointer: + %MAX_CNT_STEPS - STEP - 10 :JMPN(outOfCountersStep) + ; load data: previous CTX & argsOffsetCall + $ => A, CTX :MLOAD(originCTX) + $ => B :MLOAD(argsOffsetCall) + ; move to current CTX and store data + $ => CTX :MLOAD(currentCTX) + A :MSTORE(calldataCTX) + B :MSTORE(calldataOffset), RETURN \ No newline at end of file diff --git a/main/vars.zkasm b/main/vars.zkasm index 96e7b6a4..a14069d6 100644 --- a/main/vars.zkasm +++ b/main/vars.zkasm @@ -44,10 +44,11 @@ VAR GLOBAL txCount ; Current transaction count VAR GLOBAL touchedSR ; touched tree root VAR GLOBAL numTopics ; number of topics depending on LOG opcode call -VAR GLOBAL SPw ; aux variable to store Stack pointer 'SP' VAR GLOBAL auxSR ; auxiliary variable. Temporary state root VAR GLOBAL txRLPLength ; transaction RLP list length VAR GLOBAL txDataRead ; aux variable to check transaction 'data' left that needs to be read +VAR GLOBAL isLoadingRLP ; flag to determine if the function is called from RLP loop +VAR GLOBAL globalCalldataMemoryOffset ; Aux variable to store current calldata memory offset at calldata CTX's memory VAR CTX txGasLimit ; transaction parameter: 'gas limit' VAR CTX txDestAddr ; transaction parameter: 'to' @@ -55,6 +56,7 @@ VAR CTX storageAddr ; address which the storage will be modified VAR CTX txValue ; transaction parameter: 'value' VAR CTX txNonce ; transaction parameter: nonce VAR CTX txGasPriceRLP ; transaction parameter: 'gasPrice' decoded from the RLP +VAR CTX effectivePercentageRLP ; transaction parameter: 'effectivePercentage' decoded from the RLP VAR CTX txChainId ; transaction parameter: 'chainId' VAR CTX txS ; transaction parameter: ecdsa signature S VAR CTX txR ; transaction parameter: ecdsa signature R @@ -76,6 +78,8 @@ VAR CTX retCallLength ; size of the return data VAR CTX retDataOffset ; pointer to previous context return data offset VAR CTX retDataLength ; pointer to previous context return data length VAR CTX retDataCTX ; pointer to context where the return data is stored +VAR CTX calldataOffset ; pointer to previous context calldata offset +VAR CTX calldataCTX ; pointer to context where the calldata is stored VAR CTX argsOffsetCall ; pointer to the init slot where the calldata begins VAR CTX bytecodeLength ; state-tree length bytecode leaf value of the 'to' address VAR CTX contractHashId ; hashP address used to store contract bytecode @@ -88,6 +92,6 @@ VAR CTX isDelegateCall ; flag to determine if a new context comes from a DELEGAT VAR CTX isCreate2 ; flag to determine if a new context comes from a CREATE2 opcode VAR CTX salt ; CREATE2 parameter 'salt' used to compute new contract address VAR CTX gasCTX ; remaining gas in the origin CTX when a new context is created -VAR CTX dataStarts; hash position where de transaction 'data' starts in the batchHashData +VAR CTX dataStarts; hash position where the transaction 'data' starts in the batchHashData VAR CTX isPreEIP155 ; flag to check if the current tx is legacy, previous to Spurious Dragon (EIP-155) VAR CTX initTouchedSR ; touched root once a new context begins \ No newline at end of file diff --git a/package.json b/package.json index 0ee8483a..e3f9aff6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "@0xpolygonhermez/zkrom", - "version": "1.1.0", + "name": "@0xpolygonhermez/zkevm-rom", + "version": "2.0.0", "description": "zkROM source code", "main": "index.js", "scripts": { @@ -18,8 +18,9 @@ "test:counters": "node counters/counters-executor.js", "test:calldatacopy": "mkdir -p build && npx zkasm test/opcalldatacopy.zkasm -o build/opcalldatacopy.test.json", "test:mstorex": "mkdir -p build && npx zkasm test/mstorex.zkasm -o build/mstorex.test.json", - "eslint": "npx eslint tools/**.js", - "eslint:fix": "npx eslint tools/**.js --fix", + "test:zkasm": "node tools/run-tests-zkasm.js ./test", + "eslint": "npx eslint tools/**.js && npx eslint counters/counters-executor.js", + "eslint:fix": "npx eslint tools/**.js --fix && npx eslint counters/counters-executor.js --fix", "test:gen": "node tools/gen-parallel-tests.js", "test:start": "npx mocha --jobs $NUM_CPUS --timeout 0 --max-old-space-size=8192 --parallel tools/parallel-tests/*.test.js" }, @@ -30,7 +31,7 @@ "polygon" ], "author": "Jordi Baylina", - "license": "pending", + "license": "AGPL", "repository": { "type": "git", "url": "https://github.com/0xPolygonHermez/zkevm-rom.git" @@ -40,11 +41,14 @@ "yargs": "^17.5.1" }, "devDependencies": { - "@0xpolygonhermez/zkevm-proverjs": "github:0xPolygonHermez/zkevm-proverjs#95ca45eec5ae0a276c8f8feae7c89d8b06f2a385", - "@0xpolygonhermez/zkevm-testvectors": "github:0xPolygonHermez/zkevm-testvectors#v1.1.0-fork.4", + "@0xpolygonhermez/zkevm-proverjs": "github:0xPolygonHermez/zkevm-proverjs#5f0d122fdfb5e3e5b17f45976e08fd26c9f248ba", + "@0xpolygonhermez/zkevm-testvectors": "github:0xPolygonHermez/zkevm-testvectors#v2.0.0-fork.5", + "@0xpolygonhermez/zkevm-commonjs": "github:0xPolygonHermez/zkevm-commonjs#v2.0.0-fork.5", + "mocha": "^9.1.3", "chai": "^4.3.6", "chalk": "^3.0.0", "eslint": "^8.25.0", - "eslint-config-airbnb-base": "^15.0.0" + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-mocha": "^10.1.0" } } diff --git a/test/bitwise.zkasm b/test/bitwise.zkasm deleted file mode 100644 index bb9cf106..00000000 --- a/test/bitwise.zkasm +++ /dev/null @@ -1,73 +0,0 @@ -start: - - 1 => B - 1 => C - 1 => A - ${bitwise_and(B, C)} :ASSERT - - ${loadScalar(115792089237316195423570985008687907853269984665640564039457584007913129639935)} => B - ${loadScalar(255)} => A - ${bitwise_and(B, 255)} :ASSERT - - 2**32 + 5 => B - 2**8 - 1 => C - 5 => A - ${bitwise_and(B, C)} :ASSERT - - 2**32 + 5 => B - 2**8 - 1 => C - 5 => A - ${bitwise_and(B, C)} :ASSERT - - 10 => B - 5 => C - 15 => A - ${bitwise_or(B, C)} :ASSERT - - 2**16 => B - 2**8 => C - 2**16 + 2**8 => A - ${bitwise_or(B, C)} :ASSERT - - ${loadScalar(57896044618658097711785492504343953926634992332820282019728792003956564819968)} => B - ${loadScalar(1)} => C - ${loadScalar(57896044618658097711785492504343953926634992332820282019728792003956564819969)} => A - ${bitwise_or(B, C)} :ASSERT - - 0 => B - 7 => C - 7 => A - ${bitwise_xor(B, C)} :ASSERT - - 2**32 - 1 => B - 2**32 - 1 => C - 0 => A - ${bitwise_xor(B, C)} :ASSERT - - 2**32 - 1 => B - 2**32 - 1 => C - 0 => A - ${bitwise_xor(B, C)} :ASSERT - - 10 => B - 5 => C - 15 => A - ${bitwise_xor(B, C)} :ASSERT - - 0 => B - ${loadScalar(115792089237316195423570985008687907853269984665640564039457584007913129639935)} => A - ${bitwise_not(B)} :ASSERT - - ${loadScalar(115792089237316195423570985008687907853269984665640564039457584007913129639935)} => B - 0 => A - ${bitwise_not(B)} :ASSERT - - ${loadScalar(115792089237316195423570985008687907853269984665640564039457584007913129639929)} => B - 0x06 => A - ${bitwise_not(B)} :ASSERT - - - 0 => A,B,C,D,E,CTX, SP, PC, GAS, SR, HASHPOS, RR ; Set all registers to 0 - :JMP(finalWait) - -INCLUDE "../main/end.zkasm" \ No newline at end of file diff --git a/test/comp.zkasm b/test/comp.zkasm deleted file mode 100644 index f09b6eeb..00000000 --- a/test/comp.zkasm +++ /dev/null @@ -1,71 +0,0 @@ -start: - - 6 => B - 2 => C - 1 => A - ${comp_gt(B, C)} :ASSERT - - 2**250 => B - 2**249 => C - 1 => A - ${comp_gt(B, C)} :ASSERT - - 0 => B - 0 => C - 0 => A - ${comp_gt(B, C)} :ASSERT - - 2**52 => B - 2**52 => C - 0 => A - ${comp_gt(B, C)} :ASSERT - - 1 => B - 2 => C - 1 => A - ${comp_lt(B, C)} :ASSERT - - 2 => B - 1 => C - 0 => A - ${comp_lt(B, C)} :ASSERT - - 2**255 - 1 => B - 2**255 => C - 1 => A - ${comp_lt(B, C)} :ASSERT - - 2**255 => B - 2**255 => C - 0 => A - ${comp_lt(B, C)} :ASSERT - - 2**255 => B - 2**255 - 1 => C - 0 => A - ${comp_lt(B, C)} :ASSERT - - 0 => B - 0 => C - 1 => A - ${comp_eq(B, C)} :ASSERT - - 2**256 - 1 => B - 2**256 - 1 => C - 1 => A - ${comp_eq(B, C)} :ASSERT - - 2**256 - 1 => B - 2**256 - 2 => C - 0 => A - ${comp_eq(B, C)} :ASSERT - - 1 => B - 2 => C - 0 => A - ${comp_eq(B, C)} :ASSERT - - 0 => A,B,C,D,E,CTX, SP, PC, GAS, SR, HASHPOS, RR ; Set all registers to 0 - :JMP(finalWait) - -INCLUDE "../main/end.zkasm" \ No newline at end of file diff --git a/test/opcalldatacopy.zkasm b/test/opcalldatacopy.ignore.zkasm similarity index 69% rename from test/opcalldatacopy.zkasm rename to test/opcalldatacopy.ignore.zkasm index 1986935e..275dfb3c 100644 --- a/test/opcalldatacopy.zkasm +++ b/test/opcalldatacopy.ignore.zkasm @@ -13,6 +13,7 @@ start: 20 :MSTORE(SP++) ;size 0 :MSTORE(SP++) ;offset 32 :MSTORE(SP) ;destOffset + 100000000 => GAS :CALL(fillCalldataPattern) :CALL(fillMemPattern0) @@ -310,10 +311,6 @@ fillMemPattern: INCLUDE "../main/main.zkasm" -VAR GLOBAL auxDestOffset2 -VAR GLOBAL calldataOffset2 -VAR GLOBAL auxOffset2 -VAR GLOBAL numLastBytes2 VAR GLOBAL tmpSHXZkPCtest /** * @link [https://www.evm.codes/#37?fork=berlin] @@ -325,144 +322,10 @@ VAR GLOBAL tmpSHXZkPCtest * - stack output: [] */ opCALLDATACOPY2: - RR :MSTORE(tmpSHXZkPCtest) - ; checks zk-counters - ;%MAX_CNT_STEPS - STEP - 100 :JMPN(outOfCountersStep) - ;%MAX_CNT_BINARY - CNT_BINARY - 2 :JMPN(outOfCountersBinary) - ;%MAX_CNT_MEM_ALIGN - CNT_MEM_ALIGN - 2 :JMPN(outOfCountersMemalign) - ; check stack underflow - SP - 3 => SP :JMPN(stackUnderflow) - $ => E :MLOAD(SP+2); [destOffset => E] - $ => B :MLOAD(SP+1); [offset => B] - B :MSTORE(auxOffset2) - $ => C :MLOAD(SP); [size => C] - C :MSTORE(numLastBytes2) - ; store lastMemOffset for memory expansion gas cost - E :MSTORE(lastMemOffset) - ; store lastMemLength for memory expansion gas cost - C :MSTORE(lastMemLength);, CALL(saveMem); in: [lastMemOffset, lastMemLength] - ; check out-of-gas - ;GAS - %GAS_FASTEST_STEP => GAS :JMPN(outOfGas) - ;${3*((C+31)/32)} - ;(C+31)/32 => A - C+31 => A - :CALL(offsetUtil); in: [A: offset] out: [E: offset/32, C: offset%32] - ;GAS - 3*E => GAS :JMPN(outOfGas) - ; save current stack pointer - SP :MSTORE(SPw) - ; Recover destOffset at E - $ => E :MLOAD(lastMemOffset) - ; Recover size at C - $ => C :MLOAD(lastMemLength) - B => A - $ => B :MLOAD(txCalldataLen); less than 2**32 bytes (calldata). Enforced by memory expansion gas cost & smart contract batchL2DataHash - ; if offset is not lower than calldata length, return 0 - $ :LT,JMPNC(opCALLDATACOPY02) - B => A - $ => B :MLOAD(auxOffset2) - B + C => B - ; if txCalldataLen < (offset + size) --> opCALLDATACOPYX0 - $ :LT,JMPC(opCALLDATACOPYX02) - $ => B :MLOAD(auxOffset2),JMP(opCALLDATACOPYloop2) - -opCALLDATACOPYX02: - $ => C :MLOAD(txCalldataLen) - $ => B :MLOAD(auxOffset2) - C - B => C :MSTORE(numLastBytes2) - -opCALLDATACOPYloop2: - ; checks zk-counters - ;%MAX_CNT_STEPS - STEP - 300 :JMPN(outOfCountersStep) - ; finish loop - C :JMPZ(opCALLDATACOPYcheckLen2) - ; copy last bytes - C - 32 :JMPN(opCALLDATACOPYfinal2) - B => A :MSTORE(calldataOffset2) - E => B - :CALL(offsetUtil); in: [A: offset] out: [E: offset/32, C: offset%32] - ; add %CALLDATA_OFFSET to offset to reach calldata in memory - ; set stack pointer to first byte to read - %CALLDATA_OFFSET + E => SP - B => E - $ => A :MLOAD(SP++) - $ => B :MLOAD(SP) - $ => A :MEM_ALIGN_RD, MSTORE(bytesToStore) - E => A - ; Store 32 bytes from calldata to memory - :CALL(offsetUtil); in: [A: offset] out: [E: offset/32, C: offset%32] - E :MSTORE(auxDestOffset2) - $ => A :MLOAD(MEM:E) - $ => B :MLOAD(MEM:E+1) - ${memAlignWR_W0(A,mem.bytesToStore,C)} => D ; no trust calculate W0 - ${memAlignWR_W1(B,mem.bytesToStore,C)} => E ; no trust calculate W1 - $ :MEM_ALIGN_WR,MLOAD(bytesToStore) - E => A - $ => E :MLOAD(auxDestOffset2) - D :MSTORE(MEM:E) ; write W0 - A :MSTORE(MEM:E+1) ; write W1 - ; recover stack pointer - $ => C :MLOAD(numLastBytes2) - C - 32 => C :MSTORE(numLastBytes2) - $ => E :MLOAD(lastMemOffset) - E + 32 => E :MSTORE(lastMemOffset) - $ => B :MLOAD(calldataOffset2) - B + 32 => B :JMP(opCALLDATACOPYloop2) - -opCALLDATACOPYfinal2: - ; copy last bytes - C :MSTORE(numLastBytes2) - B => A - E => D - :CALL(offsetUtil); in: [A: offset] out: [E: offset/32, C: offset%32] - ; add %CALLDATA_OFFSET to offset to reach calldata in memory - ; set SP to calldata - %CALLDATA_OFFSET + E => SP - D => E - C => D - $ => A :MLOAD(SP++), CALL(SHLarith); [calldata => A]; in: [A: value, D: #bytes to left shift] out: [A: shifted result] - ; Recover init SP to recover input size - $ => C :MLOAD(numLastBytes2); [size => C] - ; point to next memory slot - C - 32 + D => D :JMPN(opCALLDATACOPYxor2) - D :JMPZ(opCALLDATACOPYxor2) - A => B - $ => A :MLOAD(SP); [calldata => C] - 32 - D => D :CALL(SHRarith); in: [A: value, D: #bytes to right shift] out: [A: shifted result] - 32 - C => D :CALL(SHLarith); in: [A: value, D: #bytes to left shift] out: [A: shifted result] - B + A => A - ; set bytesToStore with value to use in MSTORE - A :MSTORE(bytesToStore), CALL(MSTOREX); in: [bytesToStore, E: offset, C: length] out: [E: new offset] - :JMP(opCALLDATACOPYcheckLen2) - -opCALLDATACOPY02: - C :JMPZ(opCALLDATACOPYend2) - 32 - C :JMPN(opCALLDATACOPY3202) - ; set bytesToStore with value to use in MSTORE - 0 :MSTORE(bytesToStore), CALL(MSTOREX); in: [bytesToStore, E: offset, C: length] out: [E: new offset] - :JMP(opCALLDATACOPYend2) - -opCALLDATACOPY3202: - ; set bytesToStore with value to use in MSTORE - 0 :MSTORE(bytesToStore), CALL(MSTORE32); in: [bytesToStore, E: offset] out: [E: new offset] - C - 32 => C :JMP(opCALLDATACOPY02) - - -opCALLDATACOPYxor2: - 32 - C => D :CALL(SHRarith); in: [A: value, D: #bytes to right shift] out: [A: shifted result] - :CALL(SHLarith); in: [A: value, D: #bytes to left shift] out: [A: shifted result] - ; set bytesToStore with value to use in MSTORE - A :MSTORE(bytesToStore), CALL(MSTOREX); in: [bytesToStore, E: offset, C: length] out: [E: new offset] - -opCALLDATACOPYcheckLen2: - ; fill missing values with 0 (size > calldata) - $ => C :MLOAD(lastMemLength) - $ => A :MLOAD(txCalldataLen) - $ => B :MLOAD(auxOffset2) - C - A + B => C :JMPN(opCALLDATACOPYend2, opCALLDATACOPY02) - - -opCALLDATACOPYend2: - ; retrieve SP - $ => SP :MLOAD(SPw) + RR :MSTORE(tmpSHXZkPCtest) + :CALL(opCALLDATACOPY) + +opCALLDATACOPYend: + $ => RR :MLOAD(tmpSHXZkPCtest) :RETURN \ No newline at end of file diff --git a/test/performance/read-push.zkasm b/test/performance/read-push.zkasm new file mode 100644 index 00000000..81d57f44 --- /dev/null +++ b/test/performance/read-push.zkasm @@ -0,0 +1,71 @@ +VAR GLOBAL test + +;; Comments 'readPush' function +; @info Read bytes opcode PUSHX +; @internalParam {numBlocks} 4 bytes blocks to read +; @internalParam {leftBytes} remaining bytes +; @in D => bytes to read +; @out E => value read + +start: + ; init vars + 0 => HASHPOS + 1 => D + $${var rounds} + + ; add bytes one by one + 0x0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20n => A :CALL(initLoop) + 0x2122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40n => A :CALL(initLoop) + 0x4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60n => A :CALL(initLoop) + + ; close hash + HASHPOS :HASHPLEN(0) + $ :HASHPDIGEST(0) + 0 :MSTORE(contractHashId) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;;;;; INIT TESTS ;;;;; + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ; log init counters + CNT_BINARY => A + ; ${log(A, binaryCouners)} ; result: 1 + STEP => A + ; ${log(A, stepCounter)} ; result: 202 + + ; Start test PUSH1 + + ; PUSH1 reading 1 byte at position 0 in the bytecode + 0 => PC + 1 => D :CALL(readPush) + 0x01 => A + E :ASSERT + + ; PUSH1 reading 1 byte at position 1 in the bytecode + 20 => PC + 1 => D :CALL(readPush) + 0x02 => A + E :ASSERT + + ; PUSH1 reading 1 byte at position 2 in the bytecode + 2 => PC + 1 => D :CALL(readPush) + 0x03 => A + E :ASSERT + + + 0 => A,B,C,D,E,CTX, SP, PC, GAS, SR, HASHPOS, RR ; Set all registers to 0 + :JMP(finalizeExecution) + +initLoop: + $${rounds = 31} + 0 => B ; to allow $$ + +loopAdd32Byte: + ${(A >> (rounds * 8)) & 0xFF} :HASHP1(0) + $${rounds = rounds - 1} + ${rounds} :JMPZ(endLoop, loopAdd32Byte) + +endLoop: + :RETURN + +INCLUDE "../../main/main.zkasm" diff --git a/test/read-push.zkasm b/test/read-push.zkasm new file mode 100644 index 00000000..2dd66de7 --- /dev/null +++ b/test/read-push.zkasm @@ -0,0 +1,304 @@ +VAR GLOBAL test + +;; Comments 'readPush' function +; @info Read bytes opcode PUSHX +; @internalParam {numBlocks} 4 bytes blocks to read +; @internalParam {leftBytes} remaining bytes +; @in D => bytes to read +; @out E => value read + +start: + ; init vars + 0 => HASHPOS + 1 => D + $${var rounds} + + ; add bytes one by one + 0x0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20n => A :CALL(initLoop) + 0x2122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40n => A :CALL(initLoop) + 0x4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60n => A :CALL(initLoop) + + ; close hash + HASHPOS :HASHPLEN(0) + $ :HASHPDIGEST(0) + 0 :MSTORE(contractHashId) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;;;;; INIT TESTS ;;;;; + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ; log init counters + CNT_BINARY => A + ; ${log(A, binaryCouners)} ; result: 1 + STEP => A + ; ${log(A, stepCounter)} ; result: 202 + + ;; Start test PUSH1 + ; PUSH1 reading 1 byte at position 0 in the bytecode + 0 => PC + 1 => D :CALL(readPush) + 0x01 => A + E :ASSERT + + ; PUSH1 reading 1 byte at position 20 in the bytecode + 20 => PC + 1 => D :CALL(readPush) + 0x15 => A + E :ASSERT + + ; PUSH1 reading 1 byte at position 31 in the bytecode + 31 => PC + 1 => D :CALL(readPush) + 0x20 => A + E :ASSERT + + ; PUSH1 reading 1 byte at position 32 in the bytecode + 32 => PC + 1 => D :CALL(readPush) + 0x21 => A + E :ASSERT + + ; PUSH1 reading 1 byte at position 33 in the bytecode + 33 => PC + 1 => D :CALL(readPush) + 0x22 => A + E :ASSERT + + ;; Start test PUSH2 + ; PUSH2 reading 2 byte at position 4 in the bytecode + 4 => PC + 2 => D :CALL(readPush) + 0x0506 => A + E :ASSERT + + ; PUSH2 reading 2 byte at position 34 in the bytecode + 34 => PC + 2 => D :CALL(readPush) + 0x2324 => A + E :ASSERT + + ;; Start test PUSH3 + ; PUSH3 reading 3 byte at position 60 in the bytecode + 60 => PC + 3 => D :CALL(readPush) + 0x3d3e3f => A + E :ASSERT + + ;; Start test PUSH4 + ; PUSH4 reading 4 byte at position 72 in the bytecode + 72 => PC + 4 => D :CALL(readPush) + 0x494a4b4c => A + E :ASSERT + + ;; Start test PUSH5 + ; PUSH5 reading 5 byte at position 42 in the bytecode + 42 => PC + 5 => D :CALL(readPush) + 0x2B2C2D2E2Fn => A + E :ASSERT + + ;; Start test PUSH6 + ; PUSH6 reading 6 byte at position 42 in the bytecode + 42 => PC + 6 => D :CALL(readPush) + 0x2B2C2D2E2F30n => A + E :ASSERT + + ;; Start test PUSH7 + ; PUSH7 reading 7 byte at position 42 in the bytecode + 42 => PC + 7 => D :CALL(readPush) + 0x2B2C2D2E2F3031n => A + E :ASSERT + + ;; Start test PUSH8 + ; PUSH8 reading 8 byte at position 42 in the bytecode + 42 => PC + 8 => D :CALL(readPush) + 0x2B2C2D2E2F303132n => A + E :ASSERT + + ;; Start test PUSH9 + ; PUSH9 reading 9 byte at position 42 in the bytecode + 42 => PC + 9 => D :CALL(readPush) + 0x2B2C2D2E2F30313233n => A + E :ASSERT + + ;; Start test PUSH10 + ; PUSH10 reading 10 byte at position 42 in the bytecode + 42 => PC + 10 => D :CALL(readPush) + 0x2B2C2D2E2F3031323334n => A + E :ASSERT + + ;; Start test PUSH11 + ; PUSH11 reading 11 byte at position 42 in the bytecode + 42 => PC + 11 => D :CALL(readPush) + 0x2B2C2D2E2F303132333435n => A + E :ASSERT + + ;; Start test PUSH12 + ; PUSH12 reading 12 byte at position 42 in the bytecode + 42 => PC + 12 => D :CALL(readPush) + 0x2B2C2D2E2F30313233343536n => A + E :ASSERT + + ;; Start test PUSH13 + ; PUSH13 reading 13 byte at position 42 in the bytecode + 42 => PC + 13 => D :CALL(readPush) + 0x2B2C2D2E2F3031323334353637n => A + E :ASSERT + + ;; Start test PUSH14 + ; PUSH14 reading 14 byte at position 42 in the bytecode + 42 => PC + 14 => D :CALL(readPush) + 0x2B2C2D2E2F303132333435363738n => A + E :ASSERT + + ;; Start test PUSH15 + ; PUSH15 reading 15 byte at position 42 in the bytecode + 42 => PC + 15 => D :CALL(readPush) + 0x2B2C2D2E2F30313233343536373839n => A + E :ASSERT + + ;; Start test PUSH16 + ; PUSH16 reading 16 byte at position 42 in the bytecode + 42 => PC + 16 => D :CALL(readPush) + 0x2B2C2D2E2F303132333435363738393An => A + E :ASSERT + + ;; Start test PUSH17 + ; PUSH17 reading 17 byte at position 42 in the bytecode + 42 => PC + 17 => D :CALL(readPush) + 0x2B2C2D2E2F303132333435363738393A3Bn => A + E :ASSERT + + ;; Start test PUSH18 + ; PUSH18 reading 18 byte at position 42 in the bytecode + 42 => PC + 18 => D :CALL(readPush) + 0x2B2C2D2E2F303132333435363738393A3B3Cn => A + E :ASSERT + + ;; Start test PUSH19 + ; PUSH19 reading 19 byte at position 42 in the bytecode + 42 => PC + 19 => D :CALL(readPush) + 0x2B2C2D2E2F303132333435363738393A3B3C3Dn => A + E :ASSERT + + ;; Start test PUSH20 + ; PUSH20 reading 20 byte at position 42 in the bytecode + 42 => PC + 20 => D :CALL(readPush) + 0x2B2C2D2E2F303132333435363738393A3B3C3D3En => A + E :ASSERT + + ;; Start test PUSH21 + ; PUSH21 reading 21 byte at position 42 in the bytecode + 42 => PC + 21 => D :CALL(readPush) + 0x2B2C2D2E2F303132333435363738393A3B3C3D3E3Fn => A + E :ASSERT + + ;; Start test PUSH22 + ; PUSH22 reading 22 byte at position 42 in the bytecode + 42 => PC + 22 => D :CALL(readPush) + 0x2B2C2D2E2F303132333435363738393A3B3C3D3E3F40n => A + E :ASSERT + + ;; Start test PUSH23 + ; PUSH23 reading 23 byte at position 42 in the bytecode + 42 => PC + 23 => D :CALL(readPush) + 0x2B2C2D2E2F303132333435363738393A3B3C3D3E3F4041n => A + E :ASSERT + + ;; Start test PUSH24 + ; PUSH24 reading 24 byte at position 42 in the bytecode + 42 => PC + 24 => D :CALL(readPush) + 0x2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142n => A + E :ASSERT + + ;; Start test PUSH25 + ; PUSH25 reading 25 byte at position 42 in the bytecode + 42 => PC + 25 => D :CALL(readPush) + 0x2B2C2D2E2F303132333435363738393A3B3C3D3E3F40414243n => A + E :ASSERT + + ;; Start test PUSH26 + ; PUSH26 reading 26 byte at position 42 in the bytecode + 42 => PC + 26 => D :CALL(readPush) + 0x2B2C2D2E2F303132333435363738393A3B3C3D3E3F4041424344n => A + E :ASSERT + + ;; Start test PUSH27 + ; PUSH27 reading 27 byte at position 42 in the bytecode + 42 => PC + 27 => D :CALL(readPush) + 0x2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445n => A + E :ASSERT + + ;; Start test PUSH28 + ; PUSH28 reading 28 byte at position 42 in the bytecode + 42 => PC + 28 => D :CALL(readPush) + 0x2B2C2D2E2F303132333435363738393A3B3C3D3E3F40414243444546n => A + E :ASSERT + + ;; Start test PUSH29 + ; PUSH29 reading 29 byte at position 42 in the bytecode + 42 => PC + 29 => D :CALL(readPush) + 0x2B2C2D2E2F303132333435363738393A3B3C3D3E3F4041424344454647n => A + E :ASSERT + + ;; Start test PUSH30 + ; PUSH30 reading 30 byte at position 42 in the bytecode + 42 => PC + 30 => D :CALL(readPush) + 0x2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748n => A + E :ASSERT + + ;; Start test PUSH31 + ; PUSH31 reading 31 byte at position 42 in the bytecode + 42 => PC + 31 => D :CALL(readPush) + 0x2B2C2D2E2F303132333435363738393A3B3C3D3E3F40414243444546474849n => A + E :ASSERT + + ;; Start test PUSH32 + ; PUSH32 reading 32 byte at position 42 in the bytecode + 42 => PC + 32 => D :CALL(readPush) + 0x2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494An => A + E :ASSERT + + 0 => A,B,C,D,E,CTX, SP, PC, GAS, SR, HASHPOS, RR ; Set all registers to 0 + :JMP(finalizeExecution) + +initLoop: + $${rounds = 32} + 0 => B ; to allow $$ + +loopAdd32Byte: + $${rounds = rounds - 1} + ${(A >> (rounds * 8)) & 0xFF} => E :HASHP1(0) + ${rounds} :JMPZ(endLoop, loopAdd32Byte) + +endLoop: + :RETURN + +INCLUDE "../main/main.zkasm" diff --git a/test/rotate.zkasm b/test/rotate.zkasm deleted file mode 100644 index 9ec7ca49..00000000 --- a/test/rotate.zkasm +++ /dev/null @@ -1,120 +0,0 @@ -VAR GLOBAL test -; test valid for op-arith-final_0.json input -start: - 0x270b206cd7f7637a125ddc55aef59e54ef3ea0898dbc9b9094e5665fcd937bedn => A - 399n => PC - 8 => D ; read 20 bytes - ${getBytecode(A,PC,D)} => B - - -VAR GLOBAL leftBytes -VAR GLOBAL numBlock -; Params ==> bytes to read (readBytes) (PUSHX) -; compute ==> num_blocks --> readBytes / 4 -; compute ==> contador de 4 en 4 -; dos contadores: 0,1,2,3 0,1,2,3 0,1,2,3 0, 1 -----> insideBlock -; ------- ------- ------- ------ -; 0 1 2 -----> nums de ROTL_C -; alreadyReadBytes = numBlock * 4 + insideBlock; -readPush: - D => A - 0 => E - PC + D - 1 => PC - ${A >> 2} => B - ${A & 0x03} => D - B * 4 + D :ASSERT - D => A - 0x04 => B - $ :LT,JMPNC(failAssert) - D :MSTORE(leftBytes) - B :MSTORE(numBlock) - 0 => B - :JMP(readPushBlock) - -readPushBlock: - $ => A :MLOAD(numBlock) - $ :EQ, JMPC(endPushInit) - - 0x270b206cd7f7637a125ddc55aef59e54ef3ea0898dbc9b9094e5665fcd937bedn => A ; load address - - ${getBytecode(A,PC,1)} => C - PC - 1 => PC - - ${getBytecode(A,PC,1)} => D - PC - 1 => PC - D*256 + C => C - ${getBytecode(A,PC,1)} => D - PC - 1 => PC - D*65536 + C => C - ${getBytecode(A,PC,1)} => D - PC - 1 => PC - D*16777216 + C => C - :JMP(doRotate) - -doRotate: - B - 1 => A - - :JMP(doRotateLoop) - -doRotateLoop: - A :JMPN(endRotate) - ROTL_C => C - A - 1 => A - :JMP(doRotateLoop) - -endRotate: - C + E => E - B + 1 => B - :JMP(readPushBlock) - -doRotate2: - B - 1 => A - :JMP(doRotateLoop2) - -doRotateLoop2: - A :JMPN(endRotate2) - ROTL_C => C - A - 1 => A - :JMP(doRotateLoop2) - -endRotate2: - C + E => E - :JMP(finalPush) - -endPushInit: - $ => A :MLOAD(leftBytes) - A - 1 :JMPN(finalPush) - 0 => C - 0 => B - :JMP(endPushLoop) - -endPushLoop: - $ => A :MLOAD(leftBytes) - $ :EQ, JMPC(endPushFinal) - 0x270b206cd7f7637a125ddc55aef59e54ef3ea0898dbc9b9094e5665fcd937bedn => A ; load address - ${getBytecode(A,PC,1)} => D - PC - 1 => PC - B - 1 => A - :JMP(computeFactorLoop) - -computeFactorLoop: - A :JMPN(computeFactorEnd) - 256*D => D - A - 1 => A - :JMP(computeFactorLoop) - -computeFactorEnd: - B + 1 => B - D + C => C - :JMP(endPushLoop) - -endPushFinal: - $ => B :MLOAD(numBlock) - :JMP(doRotate2) - -finalPush: - -0 => A,B,C,D,E,CTX, SP, PC, GAS, SR, HASHPOS, RR ; Set all registers to 0 - :JMP(finalWait) - -INCLUDE "../main/end.zkasm" diff --git a/test/touched-assert.zkasm b/test/touched-assert.zkasm index bcfe29ce..de628243 100644 --- a/test/touched-assert.zkasm +++ b/test/touched-assert.zkasm @@ -1,6 +1,3 @@ -INCLUDE "../main/constants.zkasm" -INCLUDE "../main/vars.zkasm" - start: ; set root state & init root touched 0xABCDEFn => SR @@ -12,51 +9,51 @@ start: ; set address 0x0123456789012345678901234567890123456789n => A - ; cold address and mark it as warm - :CALL(isWarmedAddress) + ; if the address is cold [0 if warm, 1 if cold] + ; cold address + :CALL(isColdAddress) D => A 1 :ASSERT ; warm address 0x0123456789012345678901234567890123456789n => A - :CALL(isWarmedAddress) + :CALL(isColdAddress) D => A 0 :ASSERT ; set address & storage position 0x0123456789012345678901234567890123456789n => A 0xAn => C + ; if the storage slot is cold [0 if warm, 1 if cold] ; cold slot and mark it as warm - :CALL(isWarmedSlot) + :CALL(isColdSlot) 1 :ASSERT ; warm slot 0x0123456789012345678901234567890123456789n => A 0xAn => C - :CALL(isWarmedSlot) + :CALL(isColdSlot) 0 :ASSERT ; warm precompiled 0x3n => A - :CALL(isWarmedAddress) + :CALL(isColdAddress) D => A 0 :ASSERT ; cold zero address 0x0n => A - :CALL(isWarmedAddress) + :CALL(isColdAddress) D => A 1 :ASSERT ; warm zero address 0x0n => A - :CALL(isWarmedAddress) + :CALL(isColdAddress) D => A 0 :ASSERT 0 => A,B,C,D,E,CTX, SP, PC, GAS, SR, HASHPOS, RR ; Set all registers to 0 - finalizeExecution: - :JMP(finalWait) + :JMP(finalizeExecution) -INCLUDE "../main/end.zkasm" -INCLUDE "../main/touched.zkasm" \ No newline at end of file +INCLUDE "../main/main.zkasm" \ No newline at end of file diff --git a/test/utils-expAD.zkasm b/test/utils-expAD.zkasm index e0cf9052..c2b3bd8c 100644 --- a/test/utils-expAD.zkasm +++ b/test/utils-expAD.zkasm @@ -42,7 +42,7 @@ start: 0xf2eda75a1e9624437a4f18c1316372866f14b6bf3f7ff7441996f65b747a0001n => A $ :MLOAD(test),ASSERT -0 => A,B,C,D,E,CTX, SP, PC, GAS, SR, HASHPOS, RR ; Set all registers to 0 - :JMP(finalWait) + 0 => A,B,C,D,E,CTX, SP, PC, GAS, SR, HASHPOS, RR ; Set all registers to 0 + :JMP(finalizeExecution) INCLUDE "../main/main.zkasm" \ No newline at end of file diff --git a/test/utils-getLenBytes.zkasm b/test/utils-getLenBytes.zkasm index 8d8089f6..9ec9023a 100644 --- a/test/utils-getLenBytes.zkasm +++ b/test/utils-getLenBytes.zkasm @@ -24,13 +24,13 @@ start: 4 => A $ :MLOAD(test),ASSERT - 0x666666666666 => B + 0x666666666666n => B :CALL(getLenBytes) A :MSTORE(test) 6 => A $ :MLOAD(test),ASSERT -0 => A,B,C,D,E,CTX, SP, PC, GAS, SR, HASHPOS, RR ; Set all registers to 0 - :JMP(finalWait) + 0 => A,B,C,D,E,CTX, SP, PC, GAS, SR, HASHPOS, RR ; Set all registers to 0 + :JMP(finalizeExecution) INCLUDE "../main/main.zkasm" \ No newline at end of file diff --git a/tools/gen-parallel-tests.js b/tools/gen-parallel-tests.js index 8617622e..edcb8672 100644 --- a/tools/gen-parallel-tests.js +++ b/tools/gen-parallel-tests.js @@ -59,7 +59,7 @@ async function main() { const pilConfig = { defines: { N: 4096 }, namespaces: ['Main', 'Global'], - disableUnusedError: true + disableUnusedError: true, }; const pil = await compile(F, pathMainPil, null, pilConfig); diff --git a/tools/helpers/helpers.js b/tools/helpers/helpers.js new file mode 100644 index 00000000..3a14df2e --- /dev/null +++ b/tools/helpers/helpers.js @@ -0,0 +1,47 @@ +/* eslint-disable import/no-extraneous-dependencies */ +const path = require('path'); +const fs = require('fs'); +const { compile, newCommitPolsArray } = require('pilcom'); + +const buildPoseidon = require('@0xpolygonhermez/zkevm-commonjs').getPoseidon; + +// Global paths to build Main PIL to fill polynomials in tests +const pathMainPil = path.join(__dirname, '../../node_modules/@0xpolygonhermez/zkevm-proverjs/pil/main.pil'); +const fileCachePil = path.join(__dirname, '../../node_modules/@0xpolygonhermez/zkevm-proverjs/cache-main-pil.json'); + +async function compilePil() { + if (!fs.existsSync(fileCachePil)) { + const poseidon = await buildPoseidon(); + const { F } = poseidon; + const pilConfig = { + defines: { N: 4096 }, + namespaces: ['Main', 'Global'], + disableUnusedError: true, + }; + const p = await compile(F, pathMainPil, null, pilConfig); + fs.writeFileSync(fileCachePil, `${JSON.stringify(p, null, 1)}\n`, 'utf8'); + } + + const pil = JSON.parse(fs.readFileSync(fileCachePil)); + + return newCommitPolsArray(pil); +} + +// Get all zkasm test files +function getTestFiles(pathZkasm) { + // check if path provided is a file or a directory + const stats = fs.statSync(pathZkasm); + + if (!stats.isDirectory()) { + return [pathZkasm]; + } + + const filesNames = fs.readdirSync(pathZkasm).filter((name) => name.endsWith('.zkasm')); + + return filesNames.map((fileName) => path.join(pathZkasm, fileName)); +} + +module.exports = { + compilePil, + getTestFiles, +}; diff --git a/tools/parallel-tests-sample/sample.test.js b/tools/parallel-tests-sample/sample.test.js index f29a9cc2..b3b2b992 100644 --- a/tools/parallel-tests-sample/sample.test.js +++ b/tools/parallel-tests-sample/sample.test.js @@ -41,6 +41,7 @@ it(`${nameFile}`, async () => { }, stepsN: stepsN, counters, + assertOutputs: true, }; await smMain.execute(cmPols.Main, input, rom, config); } catch (err) { diff --git a/tools/run-tests-zkasm.js b/tools/run-tests-zkasm.js new file mode 100644 index 00000000..904c1f10 --- /dev/null +++ b/tools/run-tests-zkasm.js @@ -0,0 +1,69 @@ +/* eslint-disable no-restricted-syntax */ +/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable no-use-before-define */ +const path = require('path'); +const chalk = require('chalk'); +const zkasm = require('@0xpolygonhermez/zkasmcom'); +const smMain = require('@0xpolygonhermez/zkevm-proverjs/src/sm/sm_main/sm_main'); + +const emptyInput = require('@0xpolygonhermez/zkevm-proverjs/test/inputs/empty_input.json'); + +const { argv } = require('yargs') + .alias('v', 'verbose'); + +const { compilePil, getTestFiles } = require('./helpers/helpers'); + +async function main() { + // Compile pil + console.log(chalk.yellow('--> Compile PIL')); + const cmPols = await compilePil(); + + // Get all zkasm files + const pathZkasm = path.join(process.cwd(), process.argv[2]); + const files = await getTestFiles(pathZkasm); + + // Run all zkasm files + // eslint-disable-next-line no-restricted-syntax + console.log(chalk.yellow('--> Start running zkasm files')); + for (const file of files) { + if (file.includes('ignore')) + continue; + await runTest(file, cmPols); + } +} + +async function runTest(pathTest, cmPols) { + // Compile rom + const configZkasm = { + defines: [], + allowUndefinedLabels: true, + allowOverwriteLabels: true, + }; + + const rom = await zkasm.compile(pathTest, null, configZkasm); + const config = { + debug: true, + stepsN: 8388608, + assertOutputs: false, + }; + + // execute zkasm tests + try { + const result = await smMain.execute(cmPols.Main, emptyInput, rom, config); + console.log(chalk.green(' --> pass'), pathTest); + if (argv.verbose) { + console.log(chalk.blue(' --> verbose')); + console.log(chalk.blue(' --> counters')); + console.log(result.counters); + console.log(chalk.blue(' --> outputs')); + console.log(result.output); + console.log(chalk.blue(' --> logs')); + console.log(result.logs); + } + } catch (e) { + console.log(chalk.red(' --> fail'), pathTest); + throw new Error(e); + } +} + +main();