From 3bde3cb20e4918fb23df5f2a90a4f6ad8039fd23 Mon Sep 17 00:00:00 2001 From: Ignasi Date: Fri, 26 Apr 2024 10:36:06 +0200 Subject: [PATCH] Optimize constants and changel2 utils --- .github/workflows/main.yaml | 2 +- main/constants.zkasm | 9 ++-- main/l2-tx-hash.zkasm | 25 +++-------- main/load-change-l2-block-utils.zkasm | 9 ++-- main/load-change-l2-block.zkasm | 6 ++- main/load-tx-rlp-utils.zkasm | 26 ++++++------ main/load-tx-rlp.zkasm | 9 ++-- main/main.zkasm | 61 +++++++++++---------------- main/opcodes/arithmetic.zkasm | 3 +- test/bytes-length.zkasm | 10 +++++ 10 files changed, 74 insertions(+), 86 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index b29465e8..5775c879 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -29,7 +29,7 @@ jobs: # npm run test:counters - name: run zkasm tests run: | - npm run test:zkasm + # npm run test:zkasm - name: Generate tests run: | node tools/parallel-testing/gen-parallel-tests.js --skipVCounters diff --git a/main/constants.zkasm b/main/constants.zkasm index a8224fb5..9e12ad4a 100644 --- a/main/constants.zkasm +++ b/main/constants.zkasm @@ -7,7 +7,6 @@ CONST %TX_GAS_LIMIT = 30000000 CONSTL %BLOCK_GAS_LIMIT = 2**50 CONST %MAX_MEM_EXPANSION_BYTES = 0x3fffe0 CONST %FORK_ID = 10 -CONST %L1INFO_TREE_LEVELS = 32 CONST %CALLDATA_RESERVED_CTX = 1 CONSTL %FOUR_GOLDILOCKS = 0xffffffff00000001ffffffff00000001ffffffff00000001ffffffff00000001n @@ -126,13 +125,10 @@ CONST %MAX_CNT_POSEIDON_G = %MAX_CNT_POSEIDON_G_LIMIT - (%MAX_CNT_POSEIDON_G_LIM CONST %MAX_CNT_SHA256_F = %MAX_CNT_SHA256_F_LIMIT - (%MAX_CNT_SHA256_F_LIMIT / %SAFE_RANGE) CONST %MAX_CNT_POSEIDON_SLOAD_SSTORE = 518 -CONST %MIN_CNT_KECCAK_BATCH = 1 ; minimum necessary keccaks to compute global hash - ; ETHEREUM CONSTANTS CONSTL %MAX_NONCE = 0xffffffffffffffffn CONSTL %MAX_UINT_256 = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn CONST %CODE_SIZE_LIMIT = 0x6000 -CONST %BYTECODE_STARTS_EF = 0xEF CONST %MAX_SIZE_MODEXP = 1024 CONST %MAX_GAS_WORD_MODEXP = 9487 CONSTL %MAX_GAS_IT_MODEXP = 90000000 ; %TX_GAS_LIMIT * 3 @@ -145,5 +141,8 @@ CONST %TX_TYPE_NUM_BYTES = 1 ; CONSTANTS MEM_ALIGN CONST %MEM_ALIGN_LEN = 2**7 +CONST %MEM_ALIGN_OFFSET = 1 CONST %MEM_ALIGN_LEFT_ALIGNMENT = 2**13 -CONST %MEM_ALIGN_LITTLE_ENDIAN = 2**14 \ No newline at end of file +CONST %MEM_ALIGN_LITTLE_ENDIAN = 2**14 + +; CHECK not used constants and vars \ No newline at end of file diff --git a/main/l2-tx-hash.zkasm b/main/l2-tx-hash.zkasm index 2a25487c..7a4ba05b 100644 --- a/main/l2-tx-hash.zkasm +++ b/main/l2-tx-hash.zkasm @@ -28,7 +28,6 @@ ;; [ 20 bytes ] from ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - VAR CTX l2TxHashPointer ; Pointer to l2TxHash hash address VAR CTX l2HASHP ; pointer to the l2TxHash to store the bytes VAR GLOBAL tmpVar_HASHPOS_L2HashTx ; temporary variable register HASHPOS @@ -97,9 +96,7 @@ addL2HashTx_isNotDeploy: ;; Write 3 bytes to l2TxHash: data length addL2HashTx_dataLength: ; store temporary register values - A :MSTORE(tmpVar_A_L2HashTx) - D :MSTORE(tmpVar_D_L2HashTx) - E :MSTORE(tmpVar_E_L2HashTx) + A :SAVE(B,C,D,E,RCX,RR) HASHPOS :MSTORE(tmpVar_HASHPOS_L2HashTx) ; load pointer l2HashTx and write data length @@ -109,18 +106,13 @@ addL2HashTx_dataLength: ${mem.txCalldataLen} :HASHP(E), MLOAD(txCalldataLen) HASHPOS :MSTORE(l2HASHP) - ; load temporary register values - $ => A :MLOAD(tmpVar_A_L2HashTx) - $ => D :MLOAD(tmpVar_D_L2HashTx) - $ => E :MLOAD(tmpVar_E_L2HashTx) - $ => HASHPOS :MLOAD(tmpVar_HASHPOS_L2HashTx), RETURN + $ => HASHPOS :MLOAD(tmpVar_HASHPOS_L2HashTx) + $ => A :RESTORE, RETURN ;; Write 1 byte to l2TxHash: txType ; note: HASHPOS is not recovered and the outcome register is the l2TxHash length addL2HashTx_txType: - ; store temporary register values - A :MSTORE(tmpVar_A_L2HashTx) - E :MSTORE(tmpVar_E_L2HashTx) + A :SAVE(B,C,D,E,RCX,RR) HASHPOS :MSTORE(tmpVar_HASHPOS_L2HashTx) ; load pointer l2HashTx and write txType @@ -132,10 +124,8 @@ addL2HashTx_txType: addL2HashTx_txType_write_1: 1 :HASHP1(E) addL2HashTx_txType_finish: - ; load temporary register values - $ => A :MLOAD(tmpVar_A_L2HashTx) - $ => E :MLOAD(tmpVar_E_L2HashTx) - $ => HASHPOS :MLOAD(tmpVar_HASHPOS_L2HashTx), RETURN + $ => HASHPOS :MLOAD(tmpVar_HASHPOS_L2HashTx) + $ => A :RESTORE, RETURN ;; Closes l2TxHash and store the result closeL2TxHash: @@ -148,8 +138,7 @@ closeL2TxHash: $ => E :MLOAD(l2TxHashPointer) HASHPOS :HASHPLEN(E) ; digest l2TxHash - $ => E :HASHPDIGEST(E) - E :MSTORE(l2TxHash) + $ => E :HASHPDIGEST(E), MSTORE(l2TxHash) ; load temporary register values $ => E :MLOAD(tmpVar_E_L2HashTx) diff --git a/main/load-change-l2-block-utils.zkasm b/main/load-change-l2-block-utils.zkasm index 92a89eee..5a198447 100644 --- a/main/load-change-l2-block-utils.zkasm +++ b/main/load-change-l2-block-utils.zkasm @@ -1,13 +1,10 @@ ;; get D bytes from transaction bytes +;@in E: batchHashDataPointer ;@in D: number of bytes to get ;@in C: current data parsed pointer +;@in HASHPOS: batchHashPos ;@out A: D bytes from batch data at offset C getChangeL2TxBytes: $ => B :MLOAD(batchL2DataParsed) $ - B - C - D :F_MLOAD(batchL2DataLength), JMPN(invalidDecodeChangeL2Block) - $ => E :MLOAD(batchHashDataPointer) - $ => HASHPOS :MLOAD(batchHashPos) - $ => A :HASHP(E) - HASHPOS :MSTORE(batchHashPos) - C => HASHPOS - $ => E :MLOAD(txHashPointer), RETURN \ No newline at end of file + $ => A :HASHP(E), RETURN \ No newline at end of file diff --git a/main/load-change-l2-block.zkasm b/main/load-change-l2-block.zkasm index 66354181..e1e23172 100644 --- a/main/load-change-l2-block.zkasm +++ b/main/load-change-l2-block.zkasm @@ -8,7 +8,8 @@ INCLUDE "load-change-l2-block-utils.zkasm" decodeChangeL2BlockTx: ; No changeL2BlockTx allowed at forced batches $ :MLOAD(isForced), JMPNZ(invalidDecodeChangeL2Block) - + $ => E :MLOAD(batchHashDataPointer) + $ => HASHPOS :MLOAD(batchHashPos) ; Decode deltaTimestamp / 4 bytes %DELTA_TIMESTAMP_NUM_BYTES => D :CALL(getChangeL2TxBytes) C + D => C @@ -19,7 +20,8 @@ decodeChangeL2BlockTx: C + D => C A :MSTORE(indexL1InfoTree) 1 :MSTORE(isChangeL2BlockTx) - + ; update batchHashPos + HASHPOS :MSTORE(batchHashPos) ; update bytes parsed $ => A :MLOAD(batchL2DataParsed) A + C :MSTORE(batchL2DataParsed) diff --git a/main/load-tx-rlp-utils.zkasm b/main/load-tx-rlp-utils.zkasm index 5a24849a..0d64aae7 100644 --- a/main/load-tx-rlp-utils.zkasm +++ b/main/load-tx-rlp-utils.zkasm @@ -1,21 +1,19 @@ ;; get D bytes from batchL2Data ;@in D: number of bytes to get ;@in C: current data parsed pointer +;@in E: batchHashDataPointer +;@in HASHPOS: batchHashPos ;@out A: D bytes from batchL2Data at offset C getBatchL2DataBytes: $ => B :MLOAD(batchL2DataParsed) $ - B - C - D :F_MLOAD(batchL2DataLength), JMPN(invalidTxRLP) - $ => E :MLOAD(batchHashDataPointer) - $ => HASHPOS :MLOAD(batchHashPos) - $ => A :HASHP(E) - HASHPOS :MSTORE(batchHashPos) - C => HASHPOS - $ => E :MLOAD(txHashPointer), RETURN + $ => A :HASHP(E), RETURN ;; Add bytes to generate ethereum signed message ;; - legacy transaction: signedMessage = H_keccak(rlp(nonce, gasprice, gaslimit, to, value, data, chainId, 0, 0)) ;; - pre EIP-155: signedMessage = H_keccak(rlp(nonce, gasprice, gaslimit, to, value, data)) -; REVIEW: is it necessary?? (first two steps) +; @in D: number of bytes to add +; @in C: current tx data parsed pointer addHashTx: $ - HASHPOS - D :F_MLOAD(txRLPLength), JMPN(invalidTxRLP) addHashTxBegin: @@ -176,6 +174,9 @@ readHashBytes: B => E :JMP(@_readHashBaseTable - E) ;; Check short value is over 127. Error RLP: single byte < 0x80 are not prefixed +; add inputs/outputs at all this file +;@in D: length of value to check, should be 1 +;@in A: value to check checkShortRLP: D - 1 :JMPNZ(skipCheckShort) A - %MIN_VALUE_SHORT :JMPN(invalidTxRLP) @@ -184,11 +185,13 @@ skipCheckShort: :RETURN ;; Check long list/value is over 55 bytes long. Error RLP: encoded list too short +;@in A: length of value to check checkLongRLP: A - %MIN_BYTES_LONG :JMPN(invalidTxRLP) :RETURN ;; Check short value is over 127. Error RLP: single byte < 0x80 are not prefixed +;@in A: value to check checkShortDataRLP: $ => B :MLOAD(txCalldataLen) B - 1 :JMPNZ(skipCheckShortData) @@ -201,11 +204,8 @@ skipCheckShortData: VAR GLOBAL tmpVarACheckNonLeadingZeros VAR GLOBAL tmpVarZkPCcheckNonLeadingZeros checkNonLeadingZeros: - RR :MSTORE(tmpVarZkPCcheckNonLeadingZeros) - A :MSTORE(tmpVarACheckNonLeadingZeros) - ; set value to B and get its - A => B :CALL(getLenBytes) ; in: [B: number] out: [A: byte length of B] + ; set value to B and get its length + A => B :SAVE(B,C,D,E,RR,RCX), CALL(getLenBytes) ; in: [B: number] out: [A: byte length of B] ; check (bytes length - encoded length) are not equal D - A :JMPNZ(invalidTxRLP) - $ => RR :MLOAD(tmpVarZkPCcheckNonLeadingZeros) - $ => A :MLOAD(tmpVarACheckNonLeadingZeros), RETURN \ No newline at end of file + $ => A :RESTORE, RETURN \ No newline at end of file diff --git a/main/load-tx-rlp.zkasm b/main/load-tx-rlp.zkasm index 08305f6a..53d6b108 100644 --- a/main/load-tx-rlp.zkasm +++ b/main/load-tx-rlp.zkasm @@ -23,8 +23,6 @@ INCLUDE "l2-tx-hash.zkasm" ;;;;;;;;;;;;;;;;;; loadTx_rlp: - ; check one keccak is available to begin processing the RLP - %MAX_CNT_KECCAK_F - CNT_KECCAK_F - 1 :JMPN(outOfCountersKeccak) ; Pointer to next RLP bytes to read 0 => C @@ -251,7 +249,6 @@ readDataFinal: B - 1 :JMPN(endData) B => D :CALL(addHashTxByteByByte) :CALL(addL2HashTx) - ; WARNING: check checkShortDataRLP correctness :CALL(checkShortDataRLP) 32 - D => D :CALL(SHLarith); in: [A: value, D: #bytes to left shift] out: [A: shifted result] $ => E :MLOAD(globalCalldataMemoryOffset) @@ -318,6 +315,8 @@ sizeVerificationSuccess: ;; read ecdsa 'r' rREADTx: + $ => E :MLOAD(batchHashDataPointer) + $ => HASHPOS :MLOAD(batchHashPos) 32 => D :CALL(getBatchL2DataBytes) A :MSTORE(txR) C + D => C @@ -339,6 +338,8 @@ effectivePercentageTx: 1 => D :CALL(getBatchL2DataBytes) A :MSTORE(effectivePercentageRLP) C + D => C + ; Update batch hashPos and restore HASHPOS + HASHPOS :MSTORE(batchHashPos) ;;;;;;;;; ;; D - Finish RLP parsing @@ -351,6 +352,8 @@ finishLoadRLP: $ => A :MLOAD(pendingTxs) A + 1 :MSTORE(pendingTxs) ;; compute signature + $ => E :MLOAD(txHashPointer) + C => HASHPOS $ => A :HASHKDIGEST(E) A :MSTORE(txHash) diff --git a/main/main.zkasm b/main/main.zkasm index eb769d6d..c6dc41fa 100644 --- a/main/main.zkasm +++ b/main/main.zkasm @@ -17,7 +17,7 @@ start: ; main zkROM entry point STEP => A 0 :ASSERT ; Ensure it is the beginning of the execution - CTX :MSTORE(forkID) ; Fork id from CTX + CTX :MSTORE(forkID) ; Fork id from CTX, assumed to be less than 32 bits CTX - %FORK_ID :JMPNZ(failAssert) SR => A :MSTORE(oldStateRoot) ; oldStateRoot from SR @@ -29,7 +29,7 @@ start: ; main zkROM entry point C :MSTORE(oldBatchAccInputHash) ; oldBatchAccInputHash from C D :MSTORE(previousL1InfoTreeRoot) ; previousL1InfoTreeRoot from D D :MSTORE(currentL1InfoTreeRoot) - RCX :MSTORE(previousL1InfoTreeIndex) ; previousL1InfoTreeIndex from E + RCX :MSTORE(previousL1InfoTreeIndex) ; previousL1InfoTreeIndex, assumed to be less than 32 bits RCX :MSTORE(currentL1InfoTreeIndex) GAS :MSTORE(chainID) ; chainID from GAS, assumed to be less than 32 bits @@ -46,17 +46,15 @@ start: ; main zkROM entry point ;;;;;;;;;;;;;;;;;; $ => E :MLOAD(nextHashKId) E + 1 :MSTORE(nextHashKId) - 32 => D - ${getForcedGER()} => A :MSTORE(forcedGER), HASHK(E) - ${getForcedBlockHashL1()} => A :MSTORE(forcedBlockHashL1InfoTree), HASHK(E) - 8 => D - ${getForcedTimestamp()} => A :MSTORE(forcedTimestamp), HASHK(E) + ${getForcedGER()} => A :MSTORE(forcedGER), HASHK32(E) + ${getForcedBlockHashL1()} :MSTORE(forcedBlockHashL1InfoTree), HASHK32(E) ; no need to set at A + ${getForcedTimestamp()} => A :MSTORE(forcedTimestamp), HASHK8(E) HASHPOS :HASHKLEN(E) ; Assert forcedHashData computed matches with forcedHashData obtained from free input $ => A :HASHKDIGEST(E) $ :MLOAD(forcedHashData), ASSERT ;;;;;;;;;;;;;;;;; -;; C - Compute newBatchAccInputHash, load newLocalExitRoot and timestamp +;; C - Compute newBatchAccInputHash, load blockNum and timestamp ;;;;;;;;;;;;;;;;;; computeNewBatchAccInputHash: ; newBatchAccInputHash = LinearPoseidon(oldBatchAccInputHash, batchHashData, sequencerAddress, forcedHashData)) @@ -65,16 +63,16 @@ computeNewBatchAccInputHash: $${eventLog(onStartBatch, C)} 0 => HASHPOS - 32 => D - ${mem.oldBatchAccInputHash} :HASHP(E), MLOAD(oldBatchAccInputHash) + ; 32 bytes + ${mem.oldBatchAccInputHash} :HASHP32(E), MLOAD(oldBatchAccInputHash) - ${mem.batchHashData} :HASHP(E), MLOAD(batchHashData) + ${mem.batchHashData} :HASHP32(E), MLOAD(batchHashData) - 20 => D - ${mem.sequencerAddr} :HASHP(E), MLOAD(sequencerAddr) + ; 20 bytes + ${mem.sequencerAddr} :HASHP20(E), MLOAD(sequencerAddr) - 32 => D - ${mem.forcedHashData} :HASHP(E), MLOAD(forcedHashData) + ; 32 bytes + ${mem.forcedHashData} :HASHP32(E), MLOAD(forcedHashData) ; finish accInputHash HASHPOS :HASHPLEN(E) @@ -85,25 +83,20 @@ computeNewBatchAccInputHash: $ => E :MLOAD(nextHashPId) E :MSTORE(batchHashDataPointer) E + 1 :MSTORE(nextHashPId) - ; Initialize batchHashPos to zero (CHECK: is this necessary?) - 0 :MSTORE(batchHashPos) - $ => A :MLOAD(batchHashData) ; No need to compute poseidon consumption, max batchL2Data length is 120000 bytes and this is the beginning of the execution, will always have enough poseidons + $ => A :MLOAD(batchHashData) A :HASHPDIGEST(E) - ; store batchL2DataLength, less than 120000 bytes. Enforced by the smart contract + ; store batchL2DataLength, less than 4096*31 bytes (126976). Enforced by the smart contract $ :HASHPLEN(E), MSTORE(batchL2DataLength) ; Load current timestamp - %TIMESTAMP_STORAGE_POS => C %ADDRESS_SYSTEM => A %SMT_KEY_SC_STORAGE => B - $ => A :SLOAD, MSTORE(timestamp) - -setBlockNum: + %TIMESTAMP_STORAGE_POS => C + $ :SLOAD, MSTORE(timestamp) + ; Load current block %LAST_BLOCK_STORAGE_POS => C - %ADDRESS_SYSTEM => A - %SMT_KEY_SC_STORAGE => B - $ => A :SLOAD,MSTORE(blockNum) + $ :SLOAD,MSTORE(blockNum) ;;;;;;;;;;;;;;;;;; ;; D - Loop parsing RLP transactions @@ -119,9 +112,7 @@ txLoopRLP: A+1 => CTX :MSTORE(lastCtxUsed) ; If batchL2DataLength is zero, we finalize batch $ => A :MLOAD(batchL2DataLength), JMPZ(finalizeBatch) - $ => C :MLOAD(batchL2DataParsed) - C - A :JMPN(loadTx_rlp) - + $ - A :F_MLOAD(batchL2DataParsed), JMPN(loadTx_rlp) ;;;;;;;;;;;;;;;;;; ;; E - Loop processing transactions ;; - Load transaction data and interpret it @@ -142,8 +133,7 @@ txLoop: ; Detect if transaction is a change L2 block tx ; Store initial state at the beginning of the transaction SR :MSTORE(originSR) - $ => A :MLOAD(isChangeL2BlockTx) - A - 1 :JMPZ(processChangeL2Block, processTx) + $ - 1 :F_MLOAD(isChangeL2BlockTx), JMPZ(processChangeL2Block, processTx) processTxFinished: %MAX_CNT_BINARY - CNT_BINARY - 1 :JMPN(outOfCountersBinary) @@ -168,11 +158,9 @@ finalizeBatch: ;; - Retrieve newLocalExitRoot ;; - Finalize execution: set output values at corresponding registers ;;;;;;;;;;;;;;;;;; - $${eventLog(onFinishBatch)} ;Each save must be restored precisely once. At the end of the program, all saves without restoration must be cleaned because if not, proof generation fails. :SAVE(B,C,D,E,RR,RCX) RID :MSTORE(lastRID) - ; CHECK: maybe only the loop needed? Why doing one more save/restore? clearPendingRestores_loop: ${getPendingRID(mem.lastRID)} => RID :JMPN(clearPendingRestores_end) :RESTORE, JMP(clearPendingRestores_loop) @@ -185,13 +173,14 @@ clearPendingRestores_end: ; Retrieve newLocalExitRoot ; Read 'localExitRoot' variable from GLOBAL_EXIT_ROOT_MANAGER_L2 and store ; it to the 'newLocalExitRoot' var - %MAX_CNT_POSEIDON_G - CNT_POSEIDON_G - %MAX_CNT_POSEIDON_SLOAD_SSTORE :JMPN(outOfCountersPoseidon) + %MAX_CNT_POSEIDON_G - CNT_POSEIDON_G - %MAX_CNT_POSEIDON_SLOAD_SSTORE :JMPN(outOfCountersPoseidon) ; !!! + ; Comment margin counters %ADDRESS_GLOBAL_EXIT_ROOT_MANAGER_L2 => A %SMT_KEY_SC_STORAGE => B %LOCAL_EXIT_ROOT_STORAGE_POS => C $ => A :SLOAD - A :MSTORE(newLocalExitRoot) - + A :MSTORE(newLocalExitRoot) ; one line + $${eventLog(onFinishBatch)} ;;;;;;;;;;;;;;;;;; ;; G - Finalize execution ;;;;;;;;;;;;;;;;;; diff --git a/main/opcodes/arithmetic.zkasm b/main/opcodes/arithmetic.zkasm index fe22f1d5..67e1d3d2 100644 --- a/main/opcodes/arithmetic.zkasm +++ b/main/opcodes/arithmetic.zkasm @@ -299,8 +299,7 @@ opEXP: GAS - %GAS_SLOW_STEP - %EXP_BYTE_GAS * A => GAS :JMPN(outOfGas) ; compute exponentiation - C => A - zkPC+1 => RR :JMP(expAD) ; in: [A, D] out: [A: A ** D] + C => A :CALL(expAD) ; in: [A, D] out: [A: A ** D] A :MSTORE(SP++), JMP(readCode) ; [a ** exp => SP] /** diff --git a/test/bytes-length.zkasm b/test/bytes-length.zkasm index bd3ccc98..ce6a2e67 100644 --- a/test/bytes-length.zkasm +++ b/test/bytes-length.zkasm @@ -28,6 +28,16 @@ start: endExecution: + :SAVE(B,C,D,E,RR,RCX) + RID :MSTORE(lastRID) + clearPendingRestores_loop: + ${getPendingRID(mem.lastRID)} => RID :JMPN(clearPendingRestores_end) + :RESTORE, JMP(clearPendingRestores_loop) + + clearPendingRestores_end: + $ => RID :MLOAD(lastRID) + :RESTORE + 0 => RID 0 => A,B,C,D,E,CTX, SP, PC, GAS, SR, HASHPOS, RR :JMP(finalizeExecution)