From f339d0af4539bea71469cef3c884fca8f3fc04bf Mon Sep 17 00:00:00 2001 From: koloz193 Date: Tue, 28 Nov 2023 14:16:40 -0500 Subject: [PATCH 001/268] chore: Testnet Boojum Upgrade (#514) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Preparing the upgrade for testnet to support boojum ## Why ❔ ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zk fmt` and `zk lint`. --------- Co-authored-by: Stanislav Breadless --- contracts | 2 +- .../1699353977-boojum/testnet2/crypto.json | 11 + .../1699353977-boojum/testnet2/facetCuts.json | 177 ++++++++++ .../1699353977-boojum/testnet2/facets.json | 18 + .../1699353977-boojum/testnet2/l2Upgrade.json | 323 ++++++++++++++++++ .../testnet2/transactions.json | 235 +++++++++++++ 6 files changed, 765 insertions(+), 1 deletion(-) create mode 100644 etc/upgrades/1699353977-boojum/testnet2/crypto.json create mode 100644 etc/upgrades/1699353977-boojum/testnet2/facetCuts.json create mode 100644 etc/upgrades/1699353977-boojum/testnet2/facets.json create mode 100644 etc/upgrades/1699353977-boojum/testnet2/l2Upgrade.json create mode 100644 etc/upgrades/1699353977-boojum/testnet2/transactions.json diff --git a/contracts b/contracts index ff7452884558..3e2bee96e412 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit ff74528845586bd175d74edc45dca1f1ae2ea454 +Subproject commit 3e2bee96e412bac7c0a58c4b919837b59e9af36e diff --git a/etc/upgrades/1699353977-boojum/testnet2/crypto.json b/etc/upgrades/1699353977-boojum/testnet2/crypto.json new file mode 100644 index 000000000000..a78fec325b04 --- /dev/null +++ b/etc/upgrades/1699353977-boojum/testnet2/crypto.json @@ -0,0 +1,11 @@ +{ + "verifier": { + "address": "0xB465882F67d236DcC0D090F78ebb0d838e9719D8", + "txHash": "0x72973954279049aa8f9e04f4fb61e628248cd9ccebe51ae93851aaecb0689979" + }, + "keys": { + "recursionNodeLevelVkHash": "0x5a3ef282b21e12fe1f4438e5bb158fc5060b160559c5158c6389d62d9fe3d080", + "recursionLeafLevelVkHash": "0x14628525c227822148e718ca1138acfc6d25e759e19452455d89f7f610c3dcb8", + "recursionCircuitsSetVksHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } +} \ No newline at end of file diff --git a/etc/upgrades/1699353977-boojum/testnet2/facetCuts.json b/etc/upgrades/1699353977-boojum/testnet2/facetCuts.json new file mode 100644 index 000000000000..f2f6d4affa0d --- /dev/null +++ b/etc/upgrades/1699353977-boojum/testnet2/facetCuts.json @@ -0,0 +1,177 @@ +[ + { + "facet": "0x0000000000000000000000000000000000000000", + "selectors": [ + "0x73fb9297", + "0x36d4eb84", + "0x27ae4c16", + "0x0551448c", + "0x8043760a", + "0xbeda4b12", + "0x17338945", + "0x587809c7" + ], + "action": 2, + "isFreezable": false + }, + { + "facet": "0x0000000000000000000000000000000000000000", + "selectors": [ + "0xcdffacc6", + "0x52ef6b2c", + "0xadfca15e", + "0x7a0ed627", + "0xa7cd63b7", + "0xfe10226d", + "0x79823c9a", + "0x4fc07d75", + "0xd86970d8", + "0xfd791f3c", + "0x9d1b5a81", + "0x7b30c8da", + "0x8665b150", + "0x631f4bac", + "0x0ec6b0b7", + "0x1b60e626", + "0xe39d3bff", + "0x33ce93fe", + "0x0ef240a0", + "0xfe26699e", + "0x39607382", + "0xaf6a2dcd", + "0xa1954fc5", + "0xa39980a0", + "0x46657fe9", + "0x18e3a941", + "0x3db920ce", + "0x29b98c67", + "0xbd7c5412", + "0xc3bbd2d7", + "0xe81e0ba1", + "0xfacd743b", + "0x9cd939e4", + "0x56142d7a", + "0x74f4d30d" + ], + "action": 2, + "isFreezable": false + }, + { + "facet": "0x0000000000000000000000000000000000000000", + "selectors": [ + "0x6c0960f9", + "0xb473318e", + "0x042901c7", + "0x263b7f8e", + "0xe4948f43", + "0xeb672419" + ], + "action": 2, + "isFreezable": false + }, + { + "facet": "0x0000000000000000000000000000000000000000", + "selectors": [ + "0x0c4dd810", + "0xce9dcf16", + "0x7739cbe7", + "0xa9a2d18a" + ], + "action": 2, + "isFreezable": false + }, + { + "facet": "0x0000000000000000000000000000000000000000", + "selectors": [ + "0xe58bb639", + "0xf235757f", + "0x1cc5d103", + "0xbe6f11cf", + "0x4623c91d" + ], + "action": 2, + "isFreezable": false + }, + { + "facet": "0x409560DE546e057ce5bD5dB487EdF2bB5E785baB", + "selectors": [ + "0x0e18b681", + "0xe58bb639", + "0xa9f6d941", + "0x27ae4c16", + "0x4dd18bf5", + "0xf235757f", + "0x1cc5d103", + "0xbe6f11cf", + "0x4623c91d", + "0x17338945" + ], + "action": 0, + "isFreezable": false + }, + { + "facet": "0xF3ACF6a03ea4a914B78Ec788624B25ceC37c14A4", + "selectors": [ + "0xcdffacc6", + "0x52ef6b2c", + "0xadfca15e", + "0x7a0ed627", + "0xa7cd63b7", + "0x79823c9a", + "0x4fc07d75", + "0xd86970d8", + "0xfd791f3c", + "0xe5355c75", + "0x9d1b5a81", + "0x7b30c8da", + "0x8665b150", + "0x631f4bac", + "0x0ec6b0b7", + "0x33ce93fe", + "0xdb1f0bf9", + "0xb8c2f66f", + "0xef3f0bae", + "0xfe26699e", + "0x39607382", + "0xaf6a2dcd", + "0xa1954fc5", + "0x46657fe9", + "0x18e3a941", + "0x29b98c67", + "0xbd7c5412", + "0xc3bbd2d7", + "0xe81e0ba1", + "0xfacd743b", + "0x9cd939e4", + "0x56142d7a", + "0xb22dd78e", + "0x74f4d30d" + ], + "action": 0, + "isFreezable": false + }, + { + "facet": "0x63b5EC36B09384fFA7106A80Ec7cfdFCa521fD08", + "selectors": [ + "0x6c0960f9", + "0xb473318e", + "0x042901c7", + "0x263b7f8e", + "0xe4948f43", + "0xeb672419" + ], + "action": 0, + "isFreezable": true + }, + { + "facet": "0xD059478a564dF1353A54AC0D0e7Fc55A90b92246", + "selectors": [ + "0x701f58c5", + "0xc3d93e7c", + "0x7f61885c", + "0x97c09d34" + ], + "action": 0, + "isFreezable": true + } +] \ No newline at end of file diff --git a/etc/upgrades/1699353977-boojum/testnet2/facets.json b/etc/upgrades/1699353977-boojum/testnet2/facets.json new file mode 100644 index 000000000000..c21934fa6ae7 --- /dev/null +++ b/etc/upgrades/1699353977-boojum/testnet2/facets.json @@ -0,0 +1,18 @@ +{ + "ExecutorFacet": { + "address": "0xD059478a564dF1353A54AC0D0e7Fc55A90b92246", + "txHash": "0xe57894bba732935fcbdd52f373532f60e91c4f79157afc69a082d561ff6f2cbb" + }, + "AdminFacet": { + "address": "0x409560DE546e057ce5bD5dB487EdF2bB5E785baB", + "txHash": "0xce0c1b354d1eb7d3abecadc5d70b091ab1775e66f9b87ca7553b6d718cec4704" + }, + "GettersFacet": { + "address": "0xF3ACF6a03ea4a914B78Ec788624B25ceC37c14A4", + "txHash": "0xedea3c9fa4bb30115401e8f16477f712af8b0065e2682a3fcaf6249058b1442e" + }, + "MailboxFacet": { + "address": "0x63b5EC36B09384fFA7106A80Ec7cfdFCa521fD08", + "txHash": "0xd44c9cc34e5bbb112f8df1d3d60f82257bb2e44aa652dda0c9c82f9cb1a2df00" + } +} \ No newline at end of file diff --git a/etc/upgrades/1699353977-boojum/testnet2/l2Upgrade.json b/etc/upgrades/1699353977-boojum/testnet2/l2Upgrade.json new file mode 100644 index 000000000000..19977b5cc2a7 --- /dev/null +++ b/etc/upgrades/1699353977-boojum/testnet2/l2Upgrade.json @@ -0,0 +1,323 @@ +{ + "systemContracts": [ + { + "name": "EmptyContract", + "bytecodeHashes": [ + "0x01000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c7" + ], + "address": "0x0000000000000000000000000000000000000000" + }, + { + "name": "Ecrecover", + "bytecodeHashes": [ + "0x010000114daca2ff44f27d543b8ef67d885bfed09a74ba9cb25f5912dd3d739c" + ], + "address": "0x0000000000000000000000000000000000000001" + }, + { + "name": "SHA256", + "bytecodeHashes": [ + "0x010000178d93b2d7d6448866009892223caf018a8e8dbcf090c2b9053a285f8d" + ], + "address": "0x0000000000000000000000000000000000000002" + }, + { + "name": "EcAdd", + "bytecodeHashes": [ + "0x010000c5a85a372f441ac693210a18e683b530bed875fdcab2f7e101b057d433" + ], + "address": "0x0000000000000000000000000000000000000006" + }, + { + "name": "EcMul", + "bytecodeHashes": [ + "0x0100013759b40792c2c3d033990e992e5508263c15252eb2d9bfbba571350675" + ], + "address": "0x0000000000000000000000000000000000000007" + }, + { + "name": "EmptyContract", + "bytecodeHashes": [ + "0x01000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c7" + ], + "address": "0x0000000000000000000000000000000000008001" + }, + { + "name": "AccountCodeStorage", + "bytecodeHashes": [ + "0x0100009bc0511159b5ec703d0c56f87615964017739def4ab1ee606b8ec6458c" + ], + "address": "0x0000000000000000000000000000000000008002" + }, + { + "name": "NonceHolder", + "bytecodeHashes": [ + "0x0100012fa73fa922dd9fabb40d3275ce80396eff6ccf1b452c928c17d98bd470" + ], + "address": "0x0000000000000000000000000000000000008003" + }, + { + "name": "KnownCodesStorage", + "bytecodeHashes": [ + "0x0100008b0ca6c6f277035366e99407fbb4b01e743e80b7d24dea5a3d647b423e" + ], + "address": "0x0000000000000000000000000000000000008004" + }, + { + "name": "ImmutableSimulator", + "bytecodeHashes": [ + "0x01000047a3c40e3f4eb98f14967f141452ae602d8723a10975dc33960911d8c5" + ], + "address": "0x0000000000000000000000000000000000008005" + }, + { + "name": "ContractDeployer", + "bytecodeHashes": [ + "0x010006091341955c8f76409de00549fb00b275166b5a0d0d7b82cbd629bb4212" + ], + "address": "0x0000000000000000000000000000000000008006" + }, + { + "name": "L1Messenger", + "bytecodeHashes": [ + "0x01000301c943edb65f5a0b8cdd806218b8ecf25c022720fe3afe6951f202f3fa" + ], + "address": "0x0000000000000000000000000000000000008008" + }, + { + "name": "MsgValueSimulator", + "bytecodeHashes": [ + "0x0100006fa1591d93fcc4a25e9340ad11d0e825904cd1842b8f7255701e1aacbb" + ], + "address": "0x0000000000000000000000000000000000008009" + }, + { + "name": "L2EthToken", + "bytecodeHashes": [ + "0x01000139b506af2b02225838c5a33e30ace701b44b210a422eedab7dd31c28a3" + ], + "address": "0x000000000000000000000000000000000000800a" + }, + { + "name": "SystemContext", + "bytecodeHashes": [ + "0x0100023ba65021e4689dd1755f82108214a1f25150d439fe58c55cdb1f376436" + ], + "address": "0x000000000000000000000000000000000000800b" + }, + { + "name": "BootloaderUtilities", + "bytecodeHashes": [ + "0x010009759cab4fa9e6ca0784746e1df600ff523f0f90c1e94191755cab4b2ed0" + ], + "address": "0x000000000000000000000000000000000000800c" + }, + { + "name": "EventWriter", + "bytecodeHashes": [ + "0x01000019642d87621fdd82cf65aa9146486c9256d5f8849af9a37c78ef519339" + ], + "address": "0x000000000000000000000000000000000000800d" + }, + { + "name": "Compressor", + "bytecodeHashes": [ + "0x010001b72874590239af612f65d50a35975299f88de022493fe7f0a190e79496" + ], + "address": "0x000000000000000000000000000000000000800e" + }, + { + "name": "ComplexUpgrader", + "bytecodeHashes": [ + "0x0100005bfc0443349233459892b51e9f67e27ac828d44d9c7cba8c8285fd66bc" + ], + "address": "0x000000000000000000000000000000000000800f" + }, + { + "name": "Keccak256", + "bytecodeHashes": [ + "0x0100001fb52ca33668d01c230a1c3b13ede90fe2e37d77222410e9f183cb7a89" + ], + "address": "0x0000000000000000000000000000000000008010" + } + ], + "defaultAA": { + "name": "DefaultAccount", + "bytecodeHashes": [ + "0x01000651c5ae96f2aab07d720439e42491bb44c6384015e3a08e32620a4d582d" + ] + }, + "bootloader": { + "name": "Bootloader", + "bytecodeHashes": [ + "0x01000983d4ac4f797cf5c077e022f72284969b13248c2a8e9846f574bdeb5b88" + ] + }, + "forcedDeployments": [ + { + "bytecodeHash": "0x01000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c7", + "newAddress": "0x0000000000000000000000000000000000000000", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x010000114daca2ff44f27d543b8ef67d885bfed09a74ba9cb25f5912dd3d739c", + "newAddress": "0x0000000000000000000000000000000000000001", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x010000178d93b2d7d6448866009892223caf018a8e8dbcf090c2b9053a285f8d", + "newAddress": "0x0000000000000000000000000000000000000002", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x010000c5a85a372f441ac693210a18e683b530bed875fdcab2f7e101b057d433", + "newAddress": "0x0000000000000000000000000000000000000006", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x0100013759b40792c2c3d033990e992e5508263c15252eb2d9bfbba571350675", + "newAddress": "0x0000000000000000000000000000000000000007", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x01000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c7", + "newAddress": "0x0000000000000000000000000000000000008001", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x0100009bc0511159b5ec703d0c56f87615964017739def4ab1ee606b8ec6458c", + "newAddress": "0x0000000000000000000000000000000000008002", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x0100012fa73fa922dd9fabb40d3275ce80396eff6ccf1b452c928c17d98bd470", + "newAddress": "0x0000000000000000000000000000000000008003", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x0100008b0ca6c6f277035366e99407fbb4b01e743e80b7d24dea5a3d647b423e", + "newAddress": "0x0000000000000000000000000000000000008004", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x01000047a3c40e3f4eb98f14967f141452ae602d8723a10975dc33960911d8c5", + "newAddress": "0x0000000000000000000000000000000000008005", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x010006091341955c8f76409de00549fb00b275166b5a0d0d7b82cbd629bb4212", + "newAddress": "0x0000000000000000000000000000000000008006", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x01000301c943edb65f5a0b8cdd806218b8ecf25c022720fe3afe6951f202f3fa", + "newAddress": "0x0000000000000000000000000000000000008008", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x0100006fa1591d93fcc4a25e9340ad11d0e825904cd1842b8f7255701e1aacbb", + "newAddress": "0x0000000000000000000000000000000000008009", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x01000139b506af2b02225838c5a33e30ace701b44b210a422eedab7dd31c28a3", + "newAddress": "0x000000000000000000000000000000000000800a", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x0100023ba65021e4689dd1755f82108214a1f25150d439fe58c55cdb1f376436", + "newAddress": "0x000000000000000000000000000000000000800b", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x010009759cab4fa9e6ca0784746e1df600ff523f0f90c1e94191755cab4b2ed0", + "newAddress": "0x000000000000000000000000000000000000800c", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x01000019642d87621fdd82cf65aa9146486c9256d5f8849af9a37c78ef519339", + "newAddress": "0x000000000000000000000000000000000000800d", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x010001b72874590239af612f65d50a35975299f88de022493fe7f0a190e79496", + "newAddress": "0x000000000000000000000000000000000000800e", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x0100005bfc0443349233459892b51e9f67e27ac828d44d9c7cba8c8285fd66bc", + "newAddress": "0x000000000000000000000000000000000000800f", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x0100001fb52ca33668d01c230a1c3b13ede90fe2e37d77222410e9f183cb7a89", + "newAddress": "0x0000000000000000000000000000000000008010", + "value": 0, + "input": "0x", + "callConstructor": false + } + ], + "forcedDeploymentCalldata": "0xe9f18c170000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000005800000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000007c0000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000ac00000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000dc00000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000f40000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010c001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000114daca2ff44f27d543b8ef67d885bfed09a74ba9cb25f5912dd3d739c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000178d93b2d7d6448866009892223caf018a8e8dbcf090c2b9053a285f8d00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000c5a85a372f441ac693210a18e683b530bed875fdcab2f7e101b057d43300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100013759b40792c2c3d033990e992e5508263c15252eb2d9bfbba57135067500000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000080010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100009bc0511159b5ec703d0c56f87615964017739def4ab1ee606b8ec6458c00000000000000000000000000000000000000000000000000000000000080020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100012fa73fa922dd9fabb40d3275ce80396eff6ccf1b452c928c17d98bd47000000000000000000000000000000000000000000000000000000000000080030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100008b0ca6c6f277035366e99407fbb4b01e743e80b7d24dea5a3d647b423e00000000000000000000000000000000000000000000000000000000000080040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000047a3c40e3f4eb98f14967f141452ae602d8723a10975dc33960911d8c500000000000000000000000000000000000000000000000000000000000080050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010006091341955c8f76409de00549fb00b275166b5a0d0d7b82cbd629bb421200000000000000000000000000000000000000000000000000000000000080060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000301c943edb65f5a0b8cdd806218b8ecf25c022720fe3afe6951f202f3fa00000000000000000000000000000000000000000000000000000000000080080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100006fa1591d93fcc4a25e9340ad11d0e825904cd1842b8f7255701e1aacbb00000000000000000000000000000000000000000000000000000000000080090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000139b506af2b02225838c5a33e30ace701b44b210a422eedab7dd31c28a3000000000000000000000000000000000000000000000000000000000000800a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100023ba65021e4689dd1755f82108214a1f25150d439fe58c55cdb1f376436000000000000000000000000000000000000000000000000000000000000800b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010009759cab4fa9e6ca0784746e1df600ff523f0f90c1e94191755cab4b2ed0000000000000000000000000000000000000000000000000000000000000800c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000019642d87621fdd82cf65aa9146486c9256d5f8849af9a37c78ef519339000000000000000000000000000000000000000000000000000000000000800d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010001b72874590239af612f65d50a35975299f88de022493fe7f0a190e79496000000000000000000000000000000000000000000000000000000000000800e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100005bfc0443349233459892b51e9f67e27ac828d44d9c7cba8c8285fd66bc000000000000000000000000000000000000000000000000000000000000800f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100001fb52ca33668d01c230a1c3b13ede90fe2e37d77222410e9f183cb7a8900000000000000000000000000000000000000000000000000000000000080100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000", + "calldata": "0xe9f18c170000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000005800000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000007c0000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000ac00000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000dc00000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000f40000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010c001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000114daca2ff44f27d543b8ef67d885bfed09a74ba9cb25f5912dd3d739c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000178d93b2d7d6448866009892223caf018a8e8dbcf090c2b9053a285f8d00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000c5a85a372f441ac693210a18e683b530bed875fdcab2f7e101b057d43300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100013759b40792c2c3d033990e992e5508263c15252eb2d9bfbba57135067500000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000080010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100009bc0511159b5ec703d0c56f87615964017739def4ab1ee606b8ec6458c00000000000000000000000000000000000000000000000000000000000080020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100012fa73fa922dd9fabb40d3275ce80396eff6ccf1b452c928c17d98bd47000000000000000000000000000000000000000000000000000000000000080030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100008b0ca6c6f277035366e99407fbb4b01e743e80b7d24dea5a3d647b423e00000000000000000000000000000000000000000000000000000000000080040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000047a3c40e3f4eb98f14967f141452ae602d8723a10975dc33960911d8c500000000000000000000000000000000000000000000000000000000000080050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010006091341955c8f76409de00549fb00b275166b5a0d0d7b82cbd629bb421200000000000000000000000000000000000000000000000000000000000080060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000301c943edb65f5a0b8cdd806218b8ecf25c022720fe3afe6951f202f3fa00000000000000000000000000000000000000000000000000000000000080080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100006fa1591d93fcc4a25e9340ad11d0e825904cd1842b8f7255701e1aacbb00000000000000000000000000000000000000000000000000000000000080090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000139b506af2b02225838c5a33e30ace701b44b210a422eedab7dd31c28a3000000000000000000000000000000000000000000000000000000000000800a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100023ba65021e4689dd1755f82108214a1f25150d439fe58c55cdb1f376436000000000000000000000000000000000000000000000000000000000000800b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010009759cab4fa9e6ca0784746e1df600ff523f0f90c1e94191755cab4b2ed0000000000000000000000000000000000000000000000000000000000000800c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000019642d87621fdd82cf65aa9146486c9256d5f8849af9a37c78ef519339000000000000000000000000000000000000000000000000000000000000800d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010001b72874590239af612f65d50a35975299f88de022493fe7f0a190e79496000000000000000000000000000000000000000000000000000000000000800e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100005bfc0443349233459892b51e9f67e27ac828d44d9c7cba8c8285fd66bc000000000000000000000000000000000000000000000000000000000000800f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100001fb52ca33668d01c230a1c3b13ede90fe2e37d77222410e9f183cb7a8900000000000000000000000000000000000000000000000000000000000080100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000", + "tx": { + "txType": 254, + "from": "0x0000000000000000000000000000000000008007", + "to": "0x0000000000000000000000000000000000008006", + "gasLimit": 72000000, + "gasPerPubdataByteLimit": 800, + "maxFeePerGas": 0, + "maxPriorityFeePerGas": 0, + "paymaster": 0, + "nonce": "18", + "value": 0, + "reserved": [ + 0, + 0, + 0, + 0 + ], + "data": "0xe9f18c170000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000005800000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000007c0000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000ac00000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000dc00000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000f40000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010c001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000114daca2ff44f27d543b8ef67d885bfed09a74ba9cb25f5912dd3d739c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000178d93b2d7d6448866009892223caf018a8e8dbcf090c2b9053a285f8d00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000c5a85a372f441ac693210a18e683b530bed875fdcab2f7e101b057d43300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100013759b40792c2c3d033990e992e5508263c15252eb2d9bfbba57135067500000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000080010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100009bc0511159b5ec703d0c56f87615964017739def4ab1ee606b8ec6458c00000000000000000000000000000000000000000000000000000000000080020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100012fa73fa922dd9fabb40d3275ce80396eff6ccf1b452c928c17d98bd47000000000000000000000000000000000000000000000000000000000000080030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100008b0ca6c6f277035366e99407fbb4b01e743e80b7d24dea5a3d647b423e00000000000000000000000000000000000000000000000000000000000080040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000047a3c40e3f4eb98f14967f141452ae602d8723a10975dc33960911d8c500000000000000000000000000000000000000000000000000000000000080050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010006091341955c8f76409de00549fb00b275166b5a0d0d7b82cbd629bb421200000000000000000000000000000000000000000000000000000000000080060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000301c943edb65f5a0b8cdd806218b8ecf25c022720fe3afe6951f202f3fa00000000000000000000000000000000000000000000000000000000000080080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100006fa1591d93fcc4a25e9340ad11d0e825904cd1842b8f7255701e1aacbb00000000000000000000000000000000000000000000000000000000000080090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000139b506af2b02225838c5a33e30ace701b44b210a422eedab7dd31c28a3000000000000000000000000000000000000000000000000000000000000800a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100023ba65021e4689dd1755f82108214a1f25150d439fe58c55cdb1f376436000000000000000000000000000000000000000000000000000000000000800b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010009759cab4fa9e6ca0784746e1df600ff523f0f90c1e94191755cab4b2ed0000000000000000000000000000000000000000000000000000000000000800c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000019642d87621fdd82cf65aa9146486c9256d5f8849af9a37c78ef519339000000000000000000000000000000000000000000000000000000000000800d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010001b72874590239af612f65d50a35975299f88de022493fe7f0a190e79496000000000000000000000000000000000000000000000000000000000000800e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100005bfc0443349233459892b51e9f67e27ac828d44d9c7cba8c8285fd66bc000000000000000000000000000000000000000000000000000000000000800f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100001fb52ca33668d01c230a1c3b13ede90fe2e37d77222410e9f183cb7a8900000000000000000000000000000000000000000000000000000000000080100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000", + "signature": "0x", + "factoryDeps": [], + "paymasterInput": "0x", + "reservedDynamic": "0x" + } +} \ No newline at end of file diff --git a/etc/upgrades/1699353977-boojum/testnet2/transactions.json b/etc/upgrades/1699353977-boojum/testnet2/transactions.json new file mode 100644 index 000000000000..8abc06de518c --- /dev/null +++ b/etc/upgrades/1699353977-boojum/testnet2/transactions.json @@ -0,0 +1,235 @@ +{ + "proposeUpgradeTx": { + "l2ProtocolUpgradeTx": { + "txType": 254, + "from": "0x0000000000000000000000000000000000008007", + "to": "0x0000000000000000000000000000000000008006", + "gasLimit": 72000000, + "gasPerPubdataByteLimit": 800, + "maxFeePerGas": 0, + "maxPriorityFeePerGas": 0, + "paymaster": 0, + "nonce": "18", + "value": 0, + "reserved": [ + 0, + 0, + 0, + 0 + ], + "data": "0xe9f18c170000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000005800000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000007c0000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000ac00000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000dc00000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000f40000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010c001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000114daca2ff44f27d543b8ef67d885bfed09a74ba9cb25f5912dd3d739c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000178d93b2d7d6448866009892223caf018a8e8dbcf090c2b9053a285f8d00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000c5a85a372f441ac693210a18e683b530bed875fdcab2f7e101b057d43300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100013759b40792c2c3d033990e992e5508263c15252eb2d9bfbba57135067500000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000080010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100009bc0511159b5ec703d0c56f87615964017739def4ab1ee606b8ec6458c00000000000000000000000000000000000000000000000000000000000080020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100012fa73fa922dd9fabb40d3275ce80396eff6ccf1b452c928c17d98bd47000000000000000000000000000000000000000000000000000000000000080030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100008b0ca6c6f277035366e99407fbb4b01e743e80b7d24dea5a3d647b423e00000000000000000000000000000000000000000000000000000000000080040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000047a3c40e3f4eb98f14967f141452ae602d8723a10975dc33960911d8c500000000000000000000000000000000000000000000000000000000000080050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010006091341955c8f76409de00549fb00b275166b5a0d0d7b82cbd629bb421200000000000000000000000000000000000000000000000000000000000080060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000301c943edb65f5a0b8cdd806218b8ecf25c022720fe3afe6951f202f3fa00000000000000000000000000000000000000000000000000000000000080080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100006fa1591d93fcc4a25e9340ad11d0e825904cd1842b8f7255701e1aacbb00000000000000000000000000000000000000000000000000000000000080090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000139b506af2b02225838c5a33e30ace701b44b210a422eedab7dd31c28a3000000000000000000000000000000000000000000000000000000000000800a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100023ba65021e4689dd1755f82108214a1f25150d439fe58c55cdb1f376436000000000000000000000000000000000000000000000000000000000000800b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010009759cab4fa9e6ca0784746e1df600ff523f0f90c1e94191755cab4b2ed0000000000000000000000000000000000000000000000000000000000000800c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000019642d87621fdd82cf65aa9146486c9256d5f8849af9a37c78ef519339000000000000000000000000000000000000000000000000000000000000800d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010001b72874590239af612f65d50a35975299f88de022493fe7f0a190e79496000000000000000000000000000000000000000000000000000000000000800e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100005bfc0443349233459892b51e9f67e27ac828d44d9c7cba8c8285fd66bc000000000000000000000000000000000000000000000000000000000000800f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100001fb52ca33668d01c230a1c3b13ede90fe2e37d77222410e9f183cb7a8900000000000000000000000000000000000000000000000000000000000080100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000", + "signature": "0x", + "factoryDeps": [], + "paymasterInput": "0x", + "reservedDynamic": "0x" + }, + "bootloaderHash": "0x01000983d4ac4f797cf5c077e022f72284969b13248c2a8e9846f574bdeb5b88", + "defaultAccountHash": "0x01000651c5ae96f2aab07d720439e42491bb44c6384015e3a08e32620a4d582d", + "verifier": "0xB465882F67d236DcC0D090F78ebb0d838e9719D8", + "verifierParams": { + "recursionNodeLevelVkHash": "0x5a3ef282b21e12fe1f4438e5bb158fc5060b160559c5158c6389d62d9fe3d080", + "recursionLeafLevelVkHash": "0x14628525c227822148e718ca1138acfc6d25e759e19452455d89f7f610c3dcb8", + "recursionCircuitsSetVksHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + "l1ContractsUpgradeCalldata": "0x", + "postUpgradeCalldata": "0x", + "upgradeTimestamp": { + "type": "BigNumber", + "hex": "0x656491a9" + }, + "factoryDeps": [], + "newProtocolVersion": "18", + "newAllowList": "0x1ad02481F1F9E779Ec0C229799B05365E453Ce30" + }, + "l1upgradeCalldata": "0x1ed824a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000168001000983d4ac4f797cf5c077e022f72284969b13248c2a8e9846f574bdeb5b8801000651c5ae96f2aab07d720439e42491bb44c6384015e3a08e32620a4d582d000000000000000000000000b465882f67d236dcc0d090f78ebb0d838e9719d85a3ef282b21e12fe1f4438e5bb158fc5060b160559c5158c6389d62d9fe3d08014628525c227822148e718ca1138acfc6d25e759e19452455d89f7f610c3dcb8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016a000000000000000000000000000000000000000000000000000000000000016c000000000000000000000000000000000000000000000000000000000656491a900000000000000000000000000000000000000000000000000000000000000120000000000000000000000001ad02481f1f9e779ec0c229799b05365e453ce3000000000000000000000000000000000000000000000000000000000000000fe0000000000000000000000000000000000000000000000000000000000008007000000000000000000000000000000000000000000000000000000000000800600000000000000000000000000000000000000000000000000000000044aa200000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000001460000000000000000000000000000000000000000000000000000000000000148000000000000000000000000000000000000000000000000000000000000014a000000000000000000000000000000000000000000000000000000000000014c000000000000000000000000000000000000000000000000000000000000011c4e9f18c170000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000005800000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000007c0000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000ac00000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000dc00000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000f40000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010c001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000114daca2ff44f27d543b8ef67d885bfed09a74ba9cb25f5912dd3d739c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000178d93b2d7d6448866009892223caf018a8e8dbcf090c2b9053a285f8d00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000c5a85a372f441ac693210a18e683b530bed875fdcab2f7e101b057d43300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100013759b40792c2c3d033990e992e5508263c15252eb2d9bfbba57135067500000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000080010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100009bc0511159b5ec703d0c56f87615964017739def4ab1ee606b8ec6458c00000000000000000000000000000000000000000000000000000000000080020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100012fa73fa922dd9fabb40d3275ce80396eff6ccf1b452c928c17d98bd47000000000000000000000000000000000000000000000000000000000000080030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100008b0ca6c6f277035366e99407fbb4b01e743e80b7d24dea5a3d647b423e00000000000000000000000000000000000000000000000000000000000080040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000047a3c40e3f4eb98f14967f141452ae602d8723a10975dc33960911d8c500000000000000000000000000000000000000000000000000000000000080050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010006091341955c8f76409de00549fb00b275166b5a0d0d7b82cbd629bb421200000000000000000000000000000000000000000000000000000000000080060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000301c943edb65f5a0b8cdd806218b8ecf25c022720fe3afe6951f202f3fa00000000000000000000000000000000000000000000000000000000000080080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100006fa1591d93fcc4a25e9340ad11d0e825904cd1842b8f7255701e1aacbb00000000000000000000000000000000000000000000000000000000000080090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000139b506af2b02225838c5a33e30ace701b44b210a422eedab7dd31c28a3000000000000000000000000000000000000000000000000000000000000800a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100023ba65021e4689dd1755f82108214a1f25150d439fe58c55cdb1f376436000000000000000000000000000000000000000000000000000000000000800b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010009759cab4fa9e6ca0784746e1df600ff523f0f90c1e94191755cab4b2ed0000000000000000000000000000000000000000000000000000000000000800c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000019642d87621fdd82cf65aa9146486c9256d5f8849af9a37c78ef519339000000000000000000000000000000000000000000000000000000000000800d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010001b72874590239af612f65d50a35975299f88de022493fe7f0a190e79496000000000000000000000000000000000000000000000000000000000000800e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100005bfc0443349233459892b51e9f67e27ac828d44d9c7cba8c8285fd66bc000000000000000000000000000000000000000000000000000000000000800f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100001fb52ca33668d01c230a1c3b13ede90fe2e37d77222410e9f183cb7a8900000000000000000000000000000000000000000000000000000000000080100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "upgradeAddress": "0x02B24CAabB9f1337a48A482BF5296449dDAAdA52", + "protocolVersion": "18", + "diamondUpgradeProposalId": { + "type": "BigNumber", + "hex": "0x0e" + }, + "upgradeTimestamp": "1701089705", + "transparentUpgrade": { + "facetCuts": [ + { + "facet": "0x0000000000000000000000000000000000000000", + "selectors": [ + "0x73fb9297", + "0x36d4eb84", + "0x27ae4c16", + "0x0551448c", + "0x8043760a", + "0xbeda4b12", + "0x17338945", + "0x587809c7" + ], + "action": 2, + "isFreezable": false + }, + { + "facet": "0x0000000000000000000000000000000000000000", + "selectors": [ + "0xcdffacc6", + "0x52ef6b2c", + "0xadfca15e", + "0x7a0ed627", + "0xa7cd63b7", + "0xfe10226d", + "0x79823c9a", + "0x4fc07d75", + "0xd86970d8", + "0xfd791f3c", + "0x9d1b5a81", + "0x7b30c8da", + "0x8665b150", + "0x631f4bac", + "0x0ec6b0b7", + "0x1b60e626", + "0xe39d3bff", + "0x33ce93fe", + "0x0ef240a0", + "0xfe26699e", + "0x39607382", + "0xaf6a2dcd", + "0xa1954fc5", + "0xa39980a0", + "0x46657fe9", + "0x18e3a941", + "0x3db920ce", + "0x29b98c67", + "0xbd7c5412", + "0xc3bbd2d7", + "0xe81e0ba1", + "0xfacd743b", + "0x9cd939e4", + "0x56142d7a", + "0x74f4d30d" + ], + "action": 2, + "isFreezable": false + }, + { + "facet": "0x0000000000000000000000000000000000000000", + "selectors": [ + "0x6c0960f9", + "0xb473318e", + "0x042901c7", + "0x263b7f8e", + "0xe4948f43", + "0xeb672419" + ], + "action": 2, + "isFreezable": false + }, + { + "facet": "0x0000000000000000000000000000000000000000", + "selectors": [ + "0x0c4dd810", + "0xce9dcf16", + "0x7739cbe7", + "0xa9a2d18a" + ], + "action": 2, + "isFreezable": false + }, + { + "facet": "0x0000000000000000000000000000000000000000", + "selectors": [ + "0xe58bb639", + "0xf235757f", + "0x1cc5d103", + "0xbe6f11cf", + "0x4623c91d" + ], + "action": 2, + "isFreezable": false + }, + { + "facet": "0x409560DE546e057ce5bD5dB487EdF2bB5E785baB", + "selectors": [ + "0x0e18b681", + "0xe58bb639", + "0xa9f6d941", + "0x27ae4c16", + "0x4dd18bf5", + "0xf235757f", + "0x1cc5d103", + "0xbe6f11cf", + "0x4623c91d", + "0x17338945" + ], + "action": 0, + "isFreezable": false + }, + { + "facet": "0xF3ACF6a03ea4a914B78Ec788624B25ceC37c14A4", + "selectors": [ + "0xcdffacc6", + "0x52ef6b2c", + "0xadfca15e", + "0x7a0ed627", + "0xa7cd63b7", + "0x79823c9a", + "0x4fc07d75", + "0xd86970d8", + "0xfd791f3c", + "0xe5355c75", + "0x9d1b5a81", + "0x7b30c8da", + "0x8665b150", + "0x631f4bac", + "0x0ec6b0b7", + "0x33ce93fe", + "0xdb1f0bf9", + "0xb8c2f66f", + "0xef3f0bae", + "0xfe26699e", + "0x39607382", + "0xaf6a2dcd", + "0xa1954fc5", + "0x46657fe9", + "0x18e3a941", + "0x29b98c67", + "0xbd7c5412", + "0xc3bbd2d7", + "0xe81e0ba1", + "0xfacd743b", + "0x9cd939e4", + "0x56142d7a", + "0xb22dd78e", + "0x74f4d30d" + ], + "action": 0, + "isFreezable": false + }, + { + "facet": "0x63b5EC36B09384fFA7106A80Ec7cfdFCa521fD08", + "selectors": [ + "0x6c0960f9", + "0xb473318e", + "0x042901c7", + "0x263b7f8e", + "0xe4948f43", + "0xeb672419" + ], + "action": 0, + "isFreezable": true + }, + { + "facet": "0xD059478a564dF1353A54AC0D0e7Fc55A90b92246", + "selectors": [ + "0x701f58c5", + "0xc3d93e7c", + "0x7f61885c", + "0x97c09d34" + ], + "action": 0, + "isFreezable": true + } + ], + "initAddress": "0x02B24CAabB9f1337a48A482BF5296449dDAAdA52", + "initCalldata": "0x1ed824a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000168001000983d4ac4f797cf5c077e022f72284969b13248c2a8e9846f574bdeb5b8801000651c5ae96f2aab07d720439e42491bb44c6384015e3a08e32620a4d582d000000000000000000000000b465882f67d236dcc0d090f78ebb0d838e9719d85a3ef282b21e12fe1f4438e5bb158fc5060b160559c5158c6389d62d9fe3d08014628525c227822148e718ca1138acfc6d25e759e19452455d89f7f610c3dcb8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016a000000000000000000000000000000000000000000000000000000000000016c000000000000000000000000000000000000000000000000000000000656491a900000000000000000000000000000000000000000000000000000000000000120000000000000000000000001ad02481f1f9e779ec0c229799b05365e453ce3000000000000000000000000000000000000000000000000000000000000000fe0000000000000000000000000000000000000000000000000000000000008007000000000000000000000000000000000000000000000000000000000000800600000000000000000000000000000000000000000000000000000000044aa200000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000001460000000000000000000000000000000000000000000000000000000000000148000000000000000000000000000000000000000000000000000000000000014a000000000000000000000000000000000000000000000000000000000000014c000000000000000000000000000000000000000000000000000000000000011c4e9f18c170000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000005800000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000007c0000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000ac00000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000dc00000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000f40000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010c001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000114daca2ff44f27d543b8ef67d885bfed09a74ba9cb25f5912dd3d739c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000178d93b2d7d6448866009892223caf018a8e8dbcf090c2b9053a285f8d00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000c5a85a372f441ac693210a18e683b530bed875fdcab2f7e101b057d43300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100013759b40792c2c3d033990e992e5508263c15252eb2d9bfbba57135067500000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000080010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100009bc0511159b5ec703d0c56f87615964017739def4ab1ee606b8ec6458c00000000000000000000000000000000000000000000000000000000000080020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100012fa73fa922dd9fabb40d3275ce80396eff6ccf1b452c928c17d98bd47000000000000000000000000000000000000000000000000000000000000080030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100008b0ca6c6f277035366e99407fbb4b01e743e80b7d24dea5a3d647b423e00000000000000000000000000000000000000000000000000000000000080040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000047a3c40e3f4eb98f14967f141452ae602d8723a10975dc33960911d8c500000000000000000000000000000000000000000000000000000000000080050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010006091341955c8f76409de00549fb00b275166b5a0d0d7b82cbd629bb421200000000000000000000000000000000000000000000000000000000000080060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000301c943edb65f5a0b8cdd806218b8ecf25c022720fe3afe6951f202f3fa00000000000000000000000000000000000000000000000000000000000080080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100006fa1591d93fcc4a25e9340ad11d0e825904cd1842b8f7255701e1aacbb00000000000000000000000000000000000000000000000000000000000080090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000139b506af2b02225838c5a33e30ace701b44b210a422eedab7dd31c28a3000000000000000000000000000000000000000000000000000000000000800a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100023ba65021e4689dd1755f82108214a1f25150d439fe58c55cdb1f376436000000000000000000000000000000000000000000000000000000000000800b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010009759cab4fa9e6ca0784746e1df600ff523f0f90c1e94191755cab4b2ed0000000000000000000000000000000000000000000000000000000000000800c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000019642d87621fdd82cf65aa9146486c9256d5f8849af9a37c78ef519339000000000000000000000000000000000000000000000000000000000000800d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010001b72874590239af612f65d50a35975299f88de022493fe7f0a190e79496000000000000000000000000000000000000000000000000000000000000800e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100005bfc0443349233459892b51e9f67e27ac828d44d9c7cba8c8285fd66bc000000000000000000000000000000000000000000000000000000000000800f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100001fb52ca33668d01c230a1c3b13ede90fe2e37d77222410e9f183cb7a8900000000000000000000000000000000000000000000000000000000000080100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + "proposeTransparentUpgradeCalldata": "0x8043760a0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000006000000000000000000000000002b24caabb9f1337a48a482bf5296449ddaada5200000000000000000000000000000000000000000000000000000000000015400000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000007c000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000d60000000000000000000000000000000000000000000000000000000000000124000000000000000000000000000000000000000000000000000000000000013a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000873fb92970000000000000000000000000000000000000000000000000000000036d4eb840000000000000000000000000000000000000000000000000000000027ae4c16000000000000000000000000000000000000000000000000000000000551448c000000000000000000000000000000000000000000000000000000008043760a00000000000000000000000000000000000000000000000000000000beda4b12000000000000000000000000000000000000000000000000000000001733894500000000000000000000000000000000000000000000000000000000587809c70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000023cdffacc60000000000000000000000000000000000000000000000000000000052ef6b2c00000000000000000000000000000000000000000000000000000000adfca15e000000000000000000000000000000000000000000000000000000007a0ed62700000000000000000000000000000000000000000000000000000000a7cd63b700000000000000000000000000000000000000000000000000000000fe10226d0000000000000000000000000000000000000000000000000000000079823c9a000000000000000000000000000000000000000000000000000000004fc07d7500000000000000000000000000000000000000000000000000000000d86970d800000000000000000000000000000000000000000000000000000000fd791f3c000000000000000000000000000000000000000000000000000000009d1b5a81000000000000000000000000000000000000000000000000000000007b30c8da000000000000000000000000000000000000000000000000000000008665b15000000000000000000000000000000000000000000000000000000000631f4bac000000000000000000000000000000000000000000000000000000000ec6b0b7000000000000000000000000000000000000000000000000000000001b60e62600000000000000000000000000000000000000000000000000000000e39d3bff0000000000000000000000000000000000000000000000000000000033ce93fe000000000000000000000000000000000000000000000000000000000ef240a000000000000000000000000000000000000000000000000000000000fe26699e000000000000000000000000000000000000000000000000000000003960738200000000000000000000000000000000000000000000000000000000af6a2dcd00000000000000000000000000000000000000000000000000000000a1954fc500000000000000000000000000000000000000000000000000000000a39980a00000000000000000000000000000000000000000000000000000000046657fe90000000000000000000000000000000000000000000000000000000018e3a941000000000000000000000000000000000000000000000000000000003db920ce0000000000000000000000000000000000000000000000000000000029b98c6700000000000000000000000000000000000000000000000000000000bd7c541200000000000000000000000000000000000000000000000000000000c3bbd2d700000000000000000000000000000000000000000000000000000000e81e0ba100000000000000000000000000000000000000000000000000000000facd743b000000000000000000000000000000000000000000000000000000009cd939e40000000000000000000000000000000000000000000000000000000056142d7a0000000000000000000000000000000000000000000000000000000074f4d30d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000066c0960f900000000000000000000000000000000000000000000000000000000b473318e00000000000000000000000000000000000000000000000000000000042901c700000000000000000000000000000000000000000000000000000000263b7f8e00000000000000000000000000000000000000000000000000000000e4948f4300000000000000000000000000000000000000000000000000000000eb67241900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000040c4dd81000000000000000000000000000000000000000000000000000000000ce9dcf16000000000000000000000000000000000000000000000000000000007739cbe700000000000000000000000000000000000000000000000000000000a9a2d18a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000005e58bb63900000000000000000000000000000000000000000000000000000000f235757f000000000000000000000000000000000000000000000000000000001cc5d10300000000000000000000000000000000000000000000000000000000be6f11cf000000000000000000000000000000000000000000000000000000004623c91d00000000000000000000000000000000000000000000000000000000000000000000000000000000409560de546e057ce5bd5db487edf2bb5e785bab000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000a0e18b68100000000000000000000000000000000000000000000000000000000e58bb63900000000000000000000000000000000000000000000000000000000a9f6d9410000000000000000000000000000000000000000000000000000000027ae4c16000000000000000000000000000000000000000000000000000000004dd18bf500000000000000000000000000000000000000000000000000000000f235757f000000000000000000000000000000000000000000000000000000001cc5d10300000000000000000000000000000000000000000000000000000000be6f11cf000000000000000000000000000000000000000000000000000000004623c91d000000000000000000000000000000000000000000000000000000001733894500000000000000000000000000000000000000000000000000000000000000000000000000000000f3acf6a03ea4a914b78ec788624b25cec37c14a40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000022cdffacc60000000000000000000000000000000000000000000000000000000052ef6b2c00000000000000000000000000000000000000000000000000000000adfca15e000000000000000000000000000000000000000000000000000000007a0ed62700000000000000000000000000000000000000000000000000000000a7cd63b70000000000000000000000000000000000000000000000000000000079823c9a000000000000000000000000000000000000000000000000000000004fc07d7500000000000000000000000000000000000000000000000000000000d86970d800000000000000000000000000000000000000000000000000000000fd791f3c00000000000000000000000000000000000000000000000000000000e5355c75000000000000000000000000000000000000000000000000000000009d1b5a81000000000000000000000000000000000000000000000000000000007b30c8da000000000000000000000000000000000000000000000000000000008665b15000000000000000000000000000000000000000000000000000000000631f4bac000000000000000000000000000000000000000000000000000000000ec6b0b70000000000000000000000000000000000000000000000000000000033ce93fe00000000000000000000000000000000000000000000000000000000db1f0bf900000000000000000000000000000000000000000000000000000000b8c2f66f00000000000000000000000000000000000000000000000000000000ef3f0bae00000000000000000000000000000000000000000000000000000000fe26699e000000000000000000000000000000000000000000000000000000003960738200000000000000000000000000000000000000000000000000000000af6a2dcd00000000000000000000000000000000000000000000000000000000a1954fc50000000000000000000000000000000000000000000000000000000046657fe90000000000000000000000000000000000000000000000000000000018e3a9410000000000000000000000000000000000000000000000000000000029b98c6700000000000000000000000000000000000000000000000000000000bd7c541200000000000000000000000000000000000000000000000000000000c3bbd2d700000000000000000000000000000000000000000000000000000000e81e0ba100000000000000000000000000000000000000000000000000000000facd743b000000000000000000000000000000000000000000000000000000009cd939e40000000000000000000000000000000000000000000000000000000056142d7a00000000000000000000000000000000000000000000000000000000b22dd78e0000000000000000000000000000000000000000000000000000000074f4d30d0000000000000000000000000000000000000000000000000000000000000000000000000000000063b5ec36b09384ffa7106a80ec7cfdfca521fd0800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000066c0960f900000000000000000000000000000000000000000000000000000000b473318e00000000000000000000000000000000000000000000000000000000042901c700000000000000000000000000000000000000000000000000000000263b7f8e00000000000000000000000000000000000000000000000000000000e4948f4300000000000000000000000000000000000000000000000000000000eb67241900000000000000000000000000000000000000000000000000000000000000000000000000000000d059478a564df1353a54ac0d0e7fc55a90b922460000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004701f58c500000000000000000000000000000000000000000000000000000000c3d93e7c000000000000000000000000000000000000000000000000000000007f61885c0000000000000000000000000000000000000000000000000000000097c09d340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017041ed824a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000168001000983d4ac4f797cf5c077e022f72284969b13248c2a8e9846f574bdeb5b8801000651c5ae96f2aab07d720439e42491bb44c6384015e3a08e32620a4d582d000000000000000000000000b465882f67d236dcc0d090f78ebb0d838e9719d85a3ef282b21e12fe1f4438e5bb158fc5060b160559c5158c6389d62d9fe3d08014628525c227822148e718ca1138acfc6d25e759e19452455d89f7f610c3dcb8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016a000000000000000000000000000000000000000000000000000000000000016c000000000000000000000000000000000000000000000000000000000656491a900000000000000000000000000000000000000000000000000000000000000120000000000000000000000001ad02481f1f9e779ec0c229799b05365e453ce3000000000000000000000000000000000000000000000000000000000000000fe0000000000000000000000000000000000000000000000000000000000008007000000000000000000000000000000000000000000000000000000000000800600000000000000000000000000000000000000000000000000000000044aa200000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000001460000000000000000000000000000000000000000000000000000000000000148000000000000000000000000000000000000000000000000000000000000014a000000000000000000000000000000000000000000000000000000000000014c000000000000000000000000000000000000000000000000000000000000011c4e9f18c170000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000005800000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000007c0000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000ac00000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000dc00000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000f40000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010c001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000114daca2ff44f27d543b8ef67d885bfed09a74ba9cb25f5912dd3d739c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000178d93b2d7d6448866009892223caf018a8e8dbcf090c2b9053a285f8d00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000c5a85a372f441ac693210a18e683b530bed875fdcab2f7e101b057d43300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100013759b40792c2c3d033990e992e5508263c15252eb2d9bfbba57135067500000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000080010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100009bc0511159b5ec703d0c56f87615964017739def4ab1ee606b8ec6458c00000000000000000000000000000000000000000000000000000000000080020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100012fa73fa922dd9fabb40d3275ce80396eff6ccf1b452c928c17d98bd47000000000000000000000000000000000000000000000000000000000000080030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100008b0ca6c6f277035366e99407fbb4b01e743e80b7d24dea5a3d647b423e00000000000000000000000000000000000000000000000000000000000080040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000047a3c40e3f4eb98f14967f141452ae602d8723a10975dc33960911d8c500000000000000000000000000000000000000000000000000000000000080050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010006091341955c8f76409de00549fb00b275166b5a0d0d7b82cbd629bb421200000000000000000000000000000000000000000000000000000000000080060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000301c943edb65f5a0b8cdd806218b8ecf25c022720fe3afe6951f202f3fa00000000000000000000000000000000000000000000000000000000000080080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100006fa1591d93fcc4a25e9340ad11d0e825904cd1842b8f7255701e1aacbb00000000000000000000000000000000000000000000000000000000000080090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000139b506af2b02225838c5a33e30ace701b44b210a422eedab7dd31c28a3000000000000000000000000000000000000000000000000000000000000800a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100023ba65021e4689dd1755f82108214a1f25150d439fe58c55cdb1f376436000000000000000000000000000000000000000000000000000000000000800b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010009759cab4fa9e6ca0784746e1df600ff523f0f90c1e94191755cab4b2ed0000000000000000000000000000000000000000000000000000000000000800c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000019642d87621fdd82cf65aa9146486c9256d5f8849af9a37c78ef519339000000000000000000000000000000000000000000000000000000000000800d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010001b72874590239af612f65d50a35975299f88de022493fe7f0a190e79496000000000000000000000000000000000000000000000000000000000000800e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100005bfc0443349233459892b51e9f67e27ac828d44d9c7cba8c8285fd66bc000000000000000000000000000000000000000000000000000000000000800f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100001fb52ca33668d01c230a1c3b13ede90fe2e37d77222410e9f183cb7a8900000000000000000000000000000000000000000000000000000000000080100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "executeUpgradeCalldata": "0x36d4eb8400000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000002b24caabb9f1337a48a482bf5296449ddaada5200000000000000000000000000000000000000000000000000000000000015400000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000007c000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000d60000000000000000000000000000000000000000000000000000000000000124000000000000000000000000000000000000000000000000000000000000013a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000873fb92970000000000000000000000000000000000000000000000000000000036d4eb840000000000000000000000000000000000000000000000000000000027ae4c16000000000000000000000000000000000000000000000000000000000551448c000000000000000000000000000000000000000000000000000000008043760a00000000000000000000000000000000000000000000000000000000beda4b12000000000000000000000000000000000000000000000000000000001733894500000000000000000000000000000000000000000000000000000000587809c70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000023cdffacc60000000000000000000000000000000000000000000000000000000052ef6b2c00000000000000000000000000000000000000000000000000000000adfca15e000000000000000000000000000000000000000000000000000000007a0ed62700000000000000000000000000000000000000000000000000000000a7cd63b700000000000000000000000000000000000000000000000000000000fe10226d0000000000000000000000000000000000000000000000000000000079823c9a000000000000000000000000000000000000000000000000000000004fc07d7500000000000000000000000000000000000000000000000000000000d86970d800000000000000000000000000000000000000000000000000000000fd791f3c000000000000000000000000000000000000000000000000000000009d1b5a81000000000000000000000000000000000000000000000000000000007b30c8da000000000000000000000000000000000000000000000000000000008665b15000000000000000000000000000000000000000000000000000000000631f4bac000000000000000000000000000000000000000000000000000000000ec6b0b7000000000000000000000000000000000000000000000000000000001b60e62600000000000000000000000000000000000000000000000000000000e39d3bff0000000000000000000000000000000000000000000000000000000033ce93fe000000000000000000000000000000000000000000000000000000000ef240a000000000000000000000000000000000000000000000000000000000fe26699e000000000000000000000000000000000000000000000000000000003960738200000000000000000000000000000000000000000000000000000000af6a2dcd00000000000000000000000000000000000000000000000000000000a1954fc500000000000000000000000000000000000000000000000000000000a39980a00000000000000000000000000000000000000000000000000000000046657fe90000000000000000000000000000000000000000000000000000000018e3a941000000000000000000000000000000000000000000000000000000003db920ce0000000000000000000000000000000000000000000000000000000029b98c6700000000000000000000000000000000000000000000000000000000bd7c541200000000000000000000000000000000000000000000000000000000c3bbd2d700000000000000000000000000000000000000000000000000000000e81e0ba100000000000000000000000000000000000000000000000000000000facd743b000000000000000000000000000000000000000000000000000000009cd939e40000000000000000000000000000000000000000000000000000000056142d7a0000000000000000000000000000000000000000000000000000000074f4d30d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000066c0960f900000000000000000000000000000000000000000000000000000000b473318e00000000000000000000000000000000000000000000000000000000042901c700000000000000000000000000000000000000000000000000000000263b7f8e00000000000000000000000000000000000000000000000000000000e4948f4300000000000000000000000000000000000000000000000000000000eb67241900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000040c4dd81000000000000000000000000000000000000000000000000000000000ce9dcf16000000000000000000000000000000000000000000000000000000007739cbe700000000000000000000000000000000000000000000000000000000a9a2d18a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000005e58bb63900000000000000000000000000000000000000000000000000000000f235757f000000000000000000000000000000000000000000000000000000001cc5d10300000000000000000000000000000000000000000000000000000000be6f11cf000000000000000000000000000000000000000000000000000000004623c91d00000000000000000000000000000000000000000000000000000000000000000000000000000000409560de546e057ce5bd5db487edf2bb5e785bab000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000a0e18b68100000000000000000000000000000000000000000000000000000000e58bb63900000000000000000000000000000000000000000000000000000000a9f6d9410000000000000000000000000000000000000000000000000000000027ae4c16000000000000000000000000000000000000000000000000000000004dd18bf500000000000000000000000000000000000000000000000000000000f235757f000000000000000000000000000000000000000000000000000000001cc5d10300000000000000000000000000000000000000000000000000000000be6f11cf000000000000000000000000000000000000000000000000000000004623c91d000000000000000000000000000000000000000000000000000000001733894500000000000000000000000000000000000000000000000000000000000000000000000000000000f3acf6a03ea4a914b78ec788624b25cec37c14a40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000022cdffacc60000000000000000000000000000000000000000000000000000000052ef6b2c00000000000000000000000000000000000000000000000000000000adfca15e000000000000000000000000000000000000000000000000000000007a0ed62700000000000000000000000000000000000000000000000000000000a7cd63b70000000000000000000000000000000000000000000000000000000079823c9a000000000000000000000000000000000000000000000000000000004fc07d7500000000000000000000000000000000000000000000000000000000d86970d800000000000000000000000000000000000000000000000000000000fd791f3c00000000000000000000000000000000000000000000000000000000e5355c75000000000000000000000000000000000000000000000000000000009d1b5a81000000000000000000000000000000000000000000000000000000007b30c8da000000000000000000000000000000000000000000000000000000008665b15000000000000000000000000000000000000000000000000000000000631f4bac000000000000000000000000000000000000000000000000000000000ec6b0b70000000000000000000000000000000000000000000000000000000033ce93fe00000000000000000000000000000000000000000000000000000000db1f0bf900000000000000000000000000000000000000000000000000000000b8c2f66f00000000000000000000000000000000000000000000000000000000ef3f0bae00000000000000000000000000000000000000000000000000000000fe26699e000000000000000000000000000000000000000000000000000000003960738200000000000000000000000000000000000000000000000000000000af6a2dcd00000000000000000000000000000000000000000000000000000000a1954fc50000000000000000000000000000000000000000000000000000000046657fe90000000000000000000000000000000000000000000000000000000018e3a9410000000000000000000000000000000000000000000000000000000029b98c6700000000000000000000000000000000000000000000000000000000bd7c541200000000000000000000000000000000000000000000000000000000c3bbd2d700000000000000000000000000000000000000000000000000000000e81e0ba100000000000000000000000000000000000000000000000000000000facd743b000000000000000000000000000000000000000000000000000000009cd939e40000000000000000000000000000000000000000000000000000000056142d7a00000000000000000000000000000000000000000000000000000000b22dd78e0000000000000000000000000000000000000000000000000000000074f4d30d0000000000000000000000000000000000000000000000000000000000000000000000000000000063b5ec36b09384ffa7106a80ec7cfdfca521fd0800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000066c0960f900000000000000000000000000000000000000000000000000000000b473318e00000000000000000000000000000000000000000000000000000000042901c700000000000000000000000000000000000000000000000000000000263b7f8e00000000000000000000000000000000000000000000000000000000e4948f4300000000000000000000000000000000000000000000000000000000eb67241900000000000000000000000000000000000000000000000000000000000000000000000000000000d059478a564df1353a54ac0d0e7fc55a90b922460000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004701f58c500000000000000000000000000000000000000000000000000000000c3d93e7c000000000000000000000000000000000000000000000000000000007f61885c0000000000000000000000000000000000000000000000000000000097c09d340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017041ed824a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000168001000983d4ac4f797cf5c077e022f72284969b13248c2a8e9846f574bdeb5b8801000651c5ae96f2aab07d720439e42491bb44c6384015e3a08e32620a4d582d000000000000000000000000b465882f67d236dcc0d090f78ebb0d838e9719d85a3ef282b21e12fe1f4438e5bb158fc5060b160559c5158c6389d62d9fe3d08014628525c227822148e718ca1138acfc6d25e759e19452455d89f7f610c3dcb8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016a000000000000000000000000000000000000000000000000000000000000016c000000000000000000000000000000000000000000000000000000000656491a900000000000000000000000000000000000000000000000000000000000000120000000000000000000000001ad02481f1f9e779ec0c229799b05365e453ce3000000000000000000000000000000000000000000000000000000000000000fe0000000000000000000000000000000000000000000000000000000000008007000000000000000000000000000000000000000000000000000000000000800600000000000000000000000000000000000000000000000000000000044aa200000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000001460000000000000000000000000000000000000000000000000000000000000148000000000000000000000000000000000000000000000000000000000000014a000000000000000000000000000000000000000000000000000000000000014c000000000000000000000000000000000000000000000000000000000000011c4e9f18c170000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000005800000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000007c0000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000ac00000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000dc00000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000f40000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010c001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000114daca2ff44f27d543b8ef67d885bfed09a74ba9cb25f5912dd3d739c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000178d93b2d7d6448866009892223caf018a8e8dbcf090c2b9053a285f8d00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000c5a85a372f441ac693210a18e683b530bed875fdcab2f7e101b057d43300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100013759b40792c2c3d033990e992e5508263c15252eb2d9bfbba57135067500000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000080010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100009bc0511159b5ec703d0c56f87615964017739def4ab1ee606b8ec6458c00000000000000000000000000000000000000000000000000000000000080020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100012fa73fa922dd9fabb40d3275ce80396eff6ccf1b452c928c17d98bd47000000000000000000000000000000000000000000000000000000000000080030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100008b0ca6c6f277035366e99407fbb4b01e743e80b7d24dea5a3d647b423e00000000000000000000000000000000000000000000000000000000000080040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000047a3c40e3f4eb98f14967f141452ae602d8723a10975dc33960911d8c500000000000000000000000000000000000000000000000000000000000080050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010006091341955c8f76409de00549fb00b275166b5a0d0d7b82cbd629bb421200000000000000000000000000000000000000000000000000000000000080060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000301c943edb65f5a0b8cdd806218b8ecf25c022720fe3afe6951f202f3fa00000000000000000000000000000000000000000000000000000000000080080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100006fa1591d93fcc4a25e9340ad11d0e825904cd1842b8f7255701e1aacbb00000000000000000000000000000000000000000000000000000000000080090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000139b506af2b02225838c5a33e30ace701b44b210a422eedab7dd31c28a3000000000000000000000000000000000000000000000000000000000000800a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100023ba65021e4689dd1755f82108214a1f25150d439fe58c55cdb1f376436000000000000000000000000000000000000000000000000000000000000800b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010009759cab4fa9e6ca0784746e1df600ff523f0f90c1e94191755cab4b2ed0000000000000000000000000000000000000000000000000000000000000800c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000019642d87621fdd82cf65aa9146486c9256d5f8849af9a37c78ef519339000000000000000000000000000000000000000000000000000000000000800d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010001b72874590239af612f65d50a35975299f88de022493fe7f0a190e79496000000000000000000000000000000000000000000000000000000000000800e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100005bfc0443349233459892b51e9f67e27ac828d44d9c7cba8c8285fd66bc000000000000000000000000000000000000000000000000000000000000800f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100001fb52ca33668d01c230a1c3b13ede90fe2e37d77222410e9f183cb7a8900000000000000000000000000000000000000000000000000000000000080100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file From bd60f1cb8ae5a4a7545a5478fd2accd71e144b62 Mon Sep 17 00:00:00 2001 From: zksync-era-bot <147085853+zksync-era-bot@users.noreply.github.com> Date: Tue, 28 Nov 2023 20:34:43 +0100 Subject: [PATCH 002/268] chore(main): release core 18.3.1 (#558) :robot: I have created a release *beep* *boop* --- ## [18.3.1](https://github.com/matter-labs/zksync-era/compare/core-v18.3.0...core-v18.3.1) (2023-11-28) ### Bug Fixes * **external-node:** Check txs at insert time instead of read time ([#555](https://github.com/matter-labs/zksync-era/issues/555)) ([9ea02a1](https://github.com/matter-labs/zksync-era/commit/9ea02a1b2e7c861882f10c8cbe1997f6bb96d9cf)) * Update comments post-hotfix ([#556](https://github.com/matter-labs/zksync-era/issues/556)) ([339e450](https://github.com/matter-labs/zksync-era/commit/339e45035e85eba7d60b533221be92ce78643705)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --- .github/release-please/manifest.json | 2 +- core/CHANGELOG.md | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/release-please/manifest.json b/.github/release-please/manifest.json index 7deaec6a597d..f5774af59440 100644 --- a/.github/release-please/manifest.json +++ b/.github/release-please/manifest.json @@ -1,5 +1,5 @@ { "sdk/zksync-rs": "0.4.0", - "core": "18.3.0", + "core": "18.3.1", "prover": "9.0.0" } diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 1b182d12dfc4..6999f1448d6b 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [18.3.1](https://github.com/matter-labs/zksync-era/compare/core-v18.3.0...core-v18.3.1) (2023-11-28) + + +### Bug Fixes + +* **external-node:** Check txs at insert time instead of read time ([#555](https://github.com/matter-labs/zksync-era/issues/555)) ([9ea02a1](https://github.com/matter-labs/zksync-era/commit/9ea02a1b2e7c861882f10c8cbe1997f6bb96d9cf)) +* Update comments post-hotfix ([#556](https://github.com/matter-labs/zksync-era/issues/556)) ([339e450](https://github.com/matter-labs/zksync-era/commit/339e45035e85eba7d60b533221be92ce78643705)) + ## [18.3.0](https://github.com/matter-labs/zksync-era/compare/core-v18.2.0...core-v18.3.0) (2023-11-28) From e8fd805c8be7980de7676bca87cfc2d445aab9e1 Mon Sep 17 00:00:00 2001 From: igamigo Date: Wed, 29 Nov 2023 02:36:00 -0300 Subject: [PATCH 003/268] fix: Change no pending batches 404 error into a success response (#279) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # What ❔ When no pending batches were found on the server API, the response was a 404 which was not handled on the client (gateway) side and was treated as an HTTP client error, causing a misleading log line: ```2023-10-19T18:30:46.726413Z ERROR zksync_prover_fri_gateway::api_data_fetcher: HTTP request failed due to error: HTTP status client error (404 Not Found) for url (http://127.0.0.1:3320/proof_generation_data)``` When testing out the gpu proving pipeline for the ZK stack effort, the error was hit soon after starting all services. This PR changes this so in info trace is printed stating that there are currently no pending batches to be proven. ## Why ❔ There is some semantic dissonance on receiving an HTTP client error and a log line when calling the API, since it's possible and reasonable that there are no pending batches at some points. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --- core/lib/types/src/prover_server_api/mod.rs | 2 +- .../proof_data_handler/request_processor.rs | 19 ++++++++++--------- .../src/api_data_fetcher.rs | 1 + .../src/proof_gen_data_fetcher.rs | 6 +++++- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/core/lib/types/src/prover_server_api/mod.rs b/core/lib/types/src/prover_server_api/mod.rs index 84262b182c60..dc226f11d265 100644 --- a/core/lib/types/src/prover_server_api/mod.rs +++ b/core/lib/types/src/prover_server_api/mod.rs @@ -19,7 +19,7 @@ pub struct ProofGenerationDataRequest {} #[derive(Debug, Serialize, Deserialize)] pub enum ProofGenerationDataResponse { - Success(ProofGenerationData), + Success(Option), Error(String), } diff --git a/core/lib/zksync_core/src/proof_data_handler/request_processor.rs b/core/lib/zksync_core/src/proof_data_handler/request_processor.rs index f091993812b9..866990b31c92 100644 --- a/core/lib/zksync_core/src/proof_data_handler/request_processor.rs +++ b/core/lib/zksync_core/src/proof_data_handler/request_processor.rs @@ -31,7 +31,6 @@ pub(crate) struct RequestProcessor { } pub(crate) enum RequestProcessorError { - NoPendingBatches, ObjectStore(ObjectStoreError), Sqlx(SqlxError), } @@ -39,10 +38,6 @@ pub(crate) enum RequestProcessorError { impl IntoResponse for RequestProcessorError { fn into_response(self) -> Response { let (status_code, message) = match self { - Self::NoPendingBatches => ( - StatusCode::NOT_FOUND, - "No pending batches to process".to_owned(), - ), RequestProcessorError::ObjectStore(err) => { tracing::error!("GCS error: {:?}", err); ( @@ -88,15 +83,19 @@ impl RequestProcessor { ) -> Result, RequestProcessorError> { tracing::info!("Received request for proof generation data: {:?}", request); - let l1_batch_number = self + let l1_batch_number_result = self .pool .access_storage() .await .unwrap() .proof_generation_dal() .get_next_block_to_be_proven(self.config.proof_generation_timeout()) - .await - .ok_or(RequestProcessorError::NoPendingBatches)?; + .await; + + let l1_batch_number = match l1_batch_number_result { + Some(number) => number, + None => return Ok(Json(ProofGenerationDataResponse::Success(None))), // no batches pending to be proven + }; let blob = self .blob_store @@ -125,7 +124,9 @@ impl RequestProcessor { l1_verifier_config, }; - Ok(Json(ProofGenerationDataResponse::Success(proof_gen_data))) + Ok(Json(ProofGenerationDataResponse::Success(Some( + proof_gen_data, + )))) } pub(crate) async fn submit_proof( diff --git a/prover/prover_fri_gateway/src/api_data_fetcher.rs b/prover/prover_fri_gateway/src/api_data_fetcher.rs index 7b3a814837cf..a009f1783f21 100644 --- a/prover/prover_fri_gateway/src/api_data_fetcher.rs +++ b/prover/prover_fri_gateway/src/api_data_fetcher.rs @@ -33,6 +33,7 @@ impl PeriodicApiStruct { Resp: DeserializeOwned, { tracing::info!("Sending request to {}", endpoint); + self.client .post(endpoint) .json(&request) diff --git a/prover/prover_fri_gateway/src/proof_gen_data_fetcher.rs b/prover/prover_fri_gateway/src/proof_gen_data_fetcher.rs index e2ac2e42dd93..1f00c7f74299 100644 --- a/prover/prover_fri_gateway/src/proof_gen_data_fetcher.rs +++ b/prover/prover_fri_gateway/src/proof_gen_data_fetcher.rs @@ -33,6 +33,7 @@ impl PeriodicApiStruct { impl PeriodicApi for PeriodicApiStruct { type JobId = (); type Response = ProofGenerationDataResponse; + const SERVICE_NAME: &'static str = "ProofGenDataFetcher"; async fn get_next_request(&self) -> Option<(Self::JobId, ProofGenerationDataRequest)> { @@ -49,7 +50,10 @@ impl PeriodicApi for PeriodicApiStruct { async fn handle_response(&self, _: (), response: Self::Response) { match response { - ProofGenerationDataResponse::Success(data) => { + ProofGenerationDataResponse::Success(None) => { + tracing::info!("There are currently no pending batches to be proven"); + } + ProofGenerationDataResponse::Success(Some(data)) => { tracing::info!("Received proof gen data for: {:?}", data.l1_batch_number); self.save_proof_gen_data(data).await; } From beac0a85bb1535b05c395057171f197cd976bf82 Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Wed, 29 Nov 2023 10:00:27 -0600 Subject: [PATCH 004/268] feat: adds spellchecker workflow, and corrects misspelled words (#559) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ - Finishes the work started by @Deniallugo in https://github.com/matter-labs/zksync-era/pull/437 - Adds spellchecker workflow to prevent further misspellings - Corrects existing misspelled words ## Why ❔ - Ensures comments and inline documentation does not contain misspelled words for improved readability ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --------- Signed-off-by: Danil Co-authored-by: Danil --- .github/pull_request_template.md | 1 + .github/workflows/check-spelling.yml | 24 + core/CHANGELOG.md | 2 +- core/bin/block_reverter/src/main.rs | 2 +- core/bin/external_node/src/config/mod.rs | 2 +- core/lib/basic_types/src/lib.rs | 2 +- core/lib/config/src/configs/api.rs | 2 +- core/lib/config/src/configs/chain.rs | 2 +- core/lib/constants/src/crypto.rs | 2 +- core/lib/constants/src/ethereum.rs | 2 +- core/lib/contracts/src/lib.rs | 6 +- core/lib/dal/src/connection/mod.rs | 8 +- core/lib/dal/src/contract_verification_dal.rs | 2 +- core/lib/dal/src/lib.rs | 2 +- core/lib/dal/src/storage_dal.rs | 2 +- core/lib/dal/src/transactions_dal.rs | 2 +- core/lib/dal/src/witness_generator_dal.rs | 2 +- core/lib/eth_client/src/lib.rs | 10 +- core/lib/eth_signer/src/json_rpc_signer.rs | 6 +- core/lib/eth_signer/src/pk_signer.rs | 2 +- core/lib/mempool/src/mempool_store.rs | 2 +- core/lib/merkle_tree/src/pruning.rs | 2 +- core/lib/merkle_tree/src/recovery.rs | 2 +- core/lib/merkle_tree/src/storage/patch.rs | 4 +- core/lib/merkle_tree/src/types/mod.rs | 6 +- core/lib/multivm/src/glue/mod.rs | 2 +- core/lib/multivm/src/glue/tracers/mod.rs | 20 +- .../types/errors/vm_revert_reason.rs | 2 +- core/lib/multivm/src/lib.rs | 2 +- .../src/versions/vm_1_3_2/bootloader_state.rs | 2 +- .../vm_1_3_2/errors/vm_revert_reason.rs | 2 +- .../src/versions/vm_1_3_2/oracle_tools.rs | 2 +- .../versions/vm_1_3_2/oracles/decommitter.rs | 4 +- .../vm_1_3_2/oracles/tracer/bootloader.rs | 2 +- .../versions/vm_1_3_2/oracles/tracer/call.rs | 2 +- .../versions/vm_1_3_2/oracles/tracer/utils.rs | 2 +- .../src/versions/vm_1_3_2/test_utils.rs | 4 +- .../src/versions/vm_1_3_2/transaction_data.rs | 56 +- .../src/versions/vm_1_3_2/vm_instance.rs | 2 +- .../vm_latest/bootloader_state/snapshot.rs | 6 +- .../versions/vm_latest/bootloader_state/tx.rs | 2 +- .../src/versions/vm_latest/constants.rs | 6 +- .../vm_latest/implementation/statistics.rs | 2 +- .../vm_latest/old_vm/oracles/decommitter.rs | 4 +- .../vm_latest/tests/tester/inner_state.rs | 4 +- .../src/versions/vm_latest/tracers/utils.rs | 2 +- .../types/internals/transaction_data.rs | 6 +- .../src/versions/vm_latest/utils/fee.rs | 2 +- .../src/versions/vm_latest/utils/overhead.rs | 54 +- core/lib/multivm/src/versions/vm_latest/vm.rs | 2 +- .../src/versions/vm_m5/bootloader_state.rs | 2 +- .../versions/vm_m5/errors/vm_revert_reason.rs | 2 +- .../src/versions/vm_m5/oracles/tracer.rs | 4 +- .../multivm/src/versions/vm_m5/test_utils.rs | 4 +- .../multivm/src/versions/vm_m5/vm_instance.rs | 2 +- .../src/versions/vm_m6/bootloader_state.rs | 2 +- .../versions/vm_m6/errors/vm_revert_reason.rs | 2 +- .../src/versions/vm_m6/oracle_tools.rs | 2 +- .../src/versions/vm_m6/oracles/decommitter.rs | 4 +- .../vm_m6/oracles/tracer/bootloader.rs | 2 +- .../src/versions/vm_m6/oracles/tracer/call.rs | 2 +- .../versions/vm_m6/oracles/tracer/utils.rs | 2 +- .../multivm/src/versions/vm_m6/test_utils.rs | 4 +- .../src/versions/vm_m6/transaction_data.rs | 56 +- .../multivm/src/versions/vm_m6/vm_instance.rs | 2 +- .../bootloader_state/snapshot.rs | 6 +- .../bootloader_state/tx.rs | 2 +- .../vm_refunds_enhancement/constants.rs | 4 +- .../implementation/statistics.rs | 2 +- .../old_vm/oracles/decommitter.rs | 4 +- .../tests/tester/inner_state.rs | 4 +- .../vm_refunds_enhancement/tracers/utils.rs | 2 +- .../types/internals/transaction_data.rs | 8 +- .../vm_refunds_enhancement/utils/fee.rs | 2 +- .../vm_refunds_enhancement/utils/overhead.rs | 54 +- .../src/versions/vm_refunds_enhancement/vm.rs | 2 +- .../bootloader_state/snapshot.rs | 6 +- .../vm_virtual_blocks/bootloader_state/tx.rs | 2 +- .../versions/vm_virtual_blocks/constants.rs | 4 +- .../implementation/statistics.rs | 2 +- .../old_vm/oracles/decommitter.rs | 4 +- .../tests/tester/inner_state.rs | 4 +- .../vm_virtual_blocks/tracers/utils.rs | 2 +- .../types/internals/transaction_data.rs | 6 +- .../versions/vm_virtual_blocks/utils/fee.rs | 2 +- .../vm_virtual_blocks/utils/overhead.rs | 54 +- .../src/versions/vm_virtual_blocks/vm.rs | 2 +- core/lib/state/src/cache/metrics.rs | 2 +- core/lib/state/src/in_memory.rs | 2 +- core/lib/state/src/lib.rs | 2 +- core/lib/storage/src/db.rs | 4 +- core/lib/storage/src/metrics.rs | 2 +- core/lib/types/src/api/mod.rs | 6 +- core/lib/types/src/block.rs | 2 +- core/lib/types/src/storage/writes/mod.rs | 4 +- core/lib/types/src/transaction_request.rs | 8 +- .../eip712_signature/typed_structure.rs | 2 +- .../src/tx/primitives/packed_eth_signature.rs | 8 +- core/lib/utils/src/bytecode.rs | 2 +- core/lib/utils/src/convert.rs | 2 +- core/lib/vlog/src/lib.rs | 2 +- .../contract_verification/api_decl.rs | 2 +- .../api_server/execution_sandbox/execute.rs | 2 +- .../api_server/execution_sandbox/tracers.rs | 4 +- .../api_server/execution_sandbox/validate.rs | 2 +- .../src/api_server/tx_sender/mod.rs | 8 +- .../src/eth_sender/eth_tx_manager.rs | 2 +- .../lib/zksync_core/src/eth_sender/metrics.rs | 2 +- .../house_keeper/fri_prover_queue_monitor.rs | 2 +- core/lib/zksync_core/src/lib.rs | 2 +- .../src/metadata_calculator/helpers.rs | 2 +- core/lib/zksync_core/src/metrics.rs | 4 +- .../lib/zksync_core/src/reorg_detector/mod.rs | 10 +- .../src/state_keeper/batch_executor/mod.rs | 2 +- .../zksync_core/src/state_keeper/io/mod.rs | 2 +- .../zksync_core/src/state_keeper/metrics.rs | 6 +- .../zksync_core/src/witness_generator/mod.rs | 8 +- core/tests/loadnext/src/account/mod.rs | 2 +- core/tests/loadnext/src/account_pool.rs | 2 +- core/tests/loadnext/src/command/api.rs | 2 +- docs/advanced/pubdata.md | 2 +- prover/witness_generator/README.md | 4 +- spellcheck/era.cfg | 69 ++ spellcheck/era.dic | 605 ++++++++++++++++++ 124 files changed, 1029 insertions(+), 328 deletions(-) create mode 100644 .github/workflows/check-spelling.yml create mode 100644 spellcheck/era.cfg create mode 100644 spellcheck/era.dic diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index dba6efd2fdff..7b828d1ca895 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -18,3 +18,4 @@ - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zk fmt` and `zk lint`. +- [ ] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. diff --git a/.github/workflows/check-spelling.yml b/.github/workflows/check-spelling.yml new file mode 100644 index 000000000000..76fd6352c8e7 --- /dev/null +++ b/.github/workflows/check-spelling.yml @@ -0,0 +1,24 @@ +name: Check Spelling + +on: + push: + branches: + - main + pull_request: + +env: + CARGO_TERM_COLOR: always + +jobs: + spellcheck: + runs-on: ubuntu-latest + steps: + - name: Install cargo-spellcheck + uses: taiki-e/install-action@v2 + with: + tool: cargo-spellcheck + + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4 + + - name: Run cargo-spellcheck + run: cargo spellcheck --cfg=./spellcheck/era.cfg --code 1 diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 6999f1448d6b..d34f9d4faf58 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -244,7 +244,7 @@ * **prover-fri:** added picked-by column in prover fri related tables ([#2600](https://github.com/matter-labs/zksync-2-dev/issues/2600)) ([9e604ab](https://github.com/matter-labs/zksync-2-dev/commit/9e604abf3bae11b6f583f2abd39c07a85dc20f0a)) * update verification keys, protocol version 15 ([#2602](https://github.com/matter-labs/zksync-2-dev/issues/2602)) ([2fff59b](https://github.com/matter-labs/zksync-2-dev/commit/2fff59bab00849996864b68e932739135337ebd7)) * **vlog:** Rework the observability configuration subsystem ([#2608](https://github.com/matter-labs/zksync-2-dev/issues/2608)) ([377f0c5](https://github.com/matter-labs/zksync-2-dev/commit/377f0c5f734c979bc990b429dff0971466872e71)) -* **vm:** Multivm tracer support ([#2601](https://github.com/matter-labs/zksync-2-dev/issues/2601)) ([4a7467b](https://github.com/matter-labs/zksync-2-dev/commit/4a7467b1b1556bfd795792dbe280bcf28c93a58f)) +* **vm:** MultiVM tracer support ([#2601](https://github.com/matter-labs/zksync-2-dev/issues/2601)) ([4a7467b](https://github.com/matter-labs/zksync-2-dev/commit/4a7467b1b1556bfd795792dbe280bcf28c93a58f)) ## [8.7.0](https://github.com/matter-labs/zksync-2-dev/compare/core-v8.6.0...core-v8.7.0) (2023-09-19) diff --git a/core/bin/block_reverter/src/main.rs b/core/bin/block_reverter/src/main.rs index 3958f4dec11b..bc49b731d149 100644 --- a/core/bin/block_reverter/src/main.rs +++ b/core/bin/block_reverter/src/main.rs @@ -33,7 +33,7 @@ enum Command { /// L1 batch number used to rollback to. #[arg(long)] l1_batch_number: u32, - /// Priority fee used for rollback ethereum transaction. + /// Priority fee used for rollback Ethereum transaction. // We operate only by priority fee because we want to use base fee from ethereum // and send transaction as soon as possible without any resend logic #[arg(long)] diff --git a/core/bin/external_node/src/config/mod.rs b/core/bin/external_node/src/config/mod.rs index 00ae9d1da1b5..3f26a334ea3c 100644 --- a/core/bin/external_node/src/config/mod.rs +++ b/core/bin/external_node/src/config/mod.rs @@ -105,7 +105,7 @@ pub struct OptionalENConfig { /// Max possible size of an ABI encoded tx (in bytes). #[serde(default = "OptionalENConfig::default_max_tx_size")] pub max_tx_size: usize, - /// Max number of cache misses during one VM execution. If the number of cache misses exceeds this value, the api server panics. + /// Max number of cache misses during one VM execution. If the number of cache misses exceeds this value, the API server panics. /// This is a temporary solution to mitigate API request resulting in thousands of DB queries. pub vm_execution_cache_misses_limit: Option, /// Inbound transaction limit used for throttling. diff --git a/core/lib/basic_types/src/lib.rs b/core/lib/basic_types/src/lib.rs index 86cc8c592212..6c6223fbb177 100644 --- a/core/lib/basic_types/src/lib.rs +++ b/core/lib/basic_types/src/lib.rs @@ -77,7 +77,7 @@ impl TryFrom for AccountTreeId { } } -/// ChainId in the ZkSync network. +/// ChainId in the zkSync network. #[derive(Copy, Clone, Debug, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct L2ChainId(u64); diff --git a/core/lib/config/src/configs/api.rs b/core/lib/config/src/configs/api.rs index 3b23abea43c5..14b3d81520c1 100644 --- a/core/lib/config/src/configs/api.rs +++ b/core/lib/config/src/configs/api.rs @@ -57,7 +57,7 @@ pub struct Web3JsonRpcConfig { pub estimate_gas_acceptable_overestimation: u32, /// Max possible size of an ABI encoded tx (in bytes). pub max_tx_size: usize, - /// Max number of cache misses during one VM execution. If the number of cache misses exceeds this value, the api server panics. + /// Max number of cache misses during one VM execution. If the number of cache misses exceeds this value, the API server panics. /// This is a temporary solution to mitigate API request resulting in thousands of DB queries. pub vm_execution_cache_misses_limit: Option, /// Max number of VM instances to be concurrently spawned by the API server. diff --git a/core/lib/config/src/configs/chain.rs b/core/lib/config/src/configs/chain.rs index 95392c8df839..f09b5bb292c5 100644 --- a/core/lib/config/src/configs/chain.rs +++ b/core/lib/config/src/configs/chain.rs @@ -76,7 +76,7 @@ pub struct StateKeeperConfig { pub close_block_at_geometry_percentage: f64, /// Denotes the percentage of L1 params used in L2 block that triggers L2 block seal. pub close_block_at_eth_params_percentage: f64, - /// Denotes the percentage of L1 gas used in l2 block that triggers L2 block seal. + /// Denotes the percentage of L1 gas used in L2 block that triggers L2 block seal. pub close_block_at_gas_percentage: f64, pub fee_account_addr: Address, diff --git a/core/lib/constants/src/crypto.rs b/core/lib/constants/src/crypto.rs index e9ed44ff3081..53a5bb98b796 100644 --- a/core/lib/constants/src/crypto.rs +++ b/core/lib/constants/src/crypto.rs @@ -26,7 +26,7 @@ pub const MAX_NEW_FACTORY_DEPS: usize = 32; pub const PAD_MSG_BEFORE_HASH_BITS_LEN: usize = 736; /// The size of the bootloader memory in bytes which is used by the protocol. -/// While the maximal possible size is a lot higher, we restric ourselves to a certain limit to reduce +/// While the maximal possible size is a lot higher, we restrict ourselves to a certain limit to reduce /// the requirements on RAM. pub const USED_BOOTLOADER_MEMORY_BYTES: usize = 1 << 24; pub const USED_BOOTLOADER_MEMORY_WORDS: usize = USED_BOOTLOADER_MEMORY_BYTES / 32; diff --git a/core/lib/constants/src/ethereum.rs b/core/lib/constants/src/ethereum.rs index 13cdd32d5c1a..299b08e3d0d2 100644 --- a/core/lib/constants/src/ethereum.rs +++ b/core/lib/constants/src/ethereum.rs @@ -12,7 +12,7 @@ pub const GUARANTEED_PUBDATA_PER_L1_BATCH: u64 = 4000; /// The maximum number of pubdata per L1 batch. This limit is due to the fact that the Ethereum /// nodes do not accept transactions that have more than 128kb of pubdata. -/// The 18kb margin is left in case of any inpreciseness of the pubdata calculation. +/// The 18kb margin is left in case of any impreciseness of the pubdata calculation. pub const MAX_PUBDATA_PER_L1_BATCH: u64 = 110000; // TODO: import from zkevm_opcode_defs once VM1.3 is supported diff --git a/core/lib/contracts/src/lib.rs b/core/lib/contracts/src/lib.rs index 01dce6a98f9f..766d2464d343 100644 --- a/core/lib/contracts/src/lib.rs +++ b/core/lib/contracts/src/lib.rs @@ -194,7 +194,7 @@ pub struct SystemContractsRepo { } impl SystemContractsRepo { - /// Returns the default system contracts repo with directory based on the ZKSYNC_HOME environment variable. + /// Returns the default system contracts repository with directory based on the ZKSYNC_HOME environment variable. pub fn from_env() -> Self { let zksync_home = std::env::var("ZKSYNC_HOME").unwrap_or_else(|_| ".".into()); let zksync_home = PathBuf::from(zksync_home); @@ -336,7 +336,7 @@ impl BaseSystemContracts { BaseSystemContracts::load_with_bootloader(bootloader_bytecode) } - /// BaseSystemContracts with playground bootloader - used for handling 'eth_calls'. + /// BaseSystemContracts with playground bootloader - used for handling eth_calls. pub fn playground() -> Self { let bootloader_bytecode = read_playground_batch_bootloader_bytecode(); BaseSystemContracts::load_with_bootloader(bootloader_bytecode) @@ -364,7 +364,7 @@ impl BaseSystemContracts { BaseSystemContracts::load_with_bootloader(bootloader_bytecode) } - /// BaseSystemContracts with playground bootloader - used for handling 'eth_calls'. + /// BaseSystemContracts with playground bootloader - used for handling eth_calls. pub fn estimate_gas() -> Self { let bootloader_bytecode = read_bootloader_code("fee_estimate"); BaseSystemContracts::load_with_bootloader(bootloader_bytecode) diff --git a/core/lib/dal/src/connection/mod.rs b/core/lib/dal/src/connection/mod.rs index ad761a7edf01..845dbc64dc4c 100644 --- a/core/lib/dal/src/connection/mod.rs +++ b/core/lib/dal/src/connection/mod.rs @@ -72,13 +72,13 @@ impl<'a> ConnectionPoolBuilder<'a> { } } -/// Constructucts a new temporary database (with a randomized name) +/// Constructs a new temporary database (with a randomized name) /// by cloning the database template pointed by TEST_DATABASE_URL env var. /// The template is expected to have all migrations from dal/migrations applied. -/// For efficiency, the postgres container of TEST_DATABASE_URL should be +/// For efficiency, the Postgres container of TEST_DATABASE_URL should be /// configured with option "fsync=off" - it disables waiting for disk synchronization -/// whenever you write to the DBs, therefore making it as fast as an inmem postgres instance. -/// The database is not cleaned up automatically, but rather the whole postgres +/// whenever you write to the DBs, therefore making it as fast as an in-memory Postgres instance. +/// The database is not cleaned up automatically, but rather the whole Postgres /// container is recreated whenever you call "zk test rust". pub(super) async fn create_test_db() -> anyhow::Result { use rand::Rng as _; diff --git a/core/lib/dal/src/contract_verification_dal.rs b/core/lib/dal/src/contract_verification_dal.rs index 59e0c6996f96..a6c549f482bc 100644 --- a/core/lib/dal/src/contract_verification_dal.rs +++ b/core/lib/dal/src/contract_verification_dal.rs @@ -91,7 +91,7 @@ impl ContractVerificationDal<'_, '_> { /// Returns the next verification request for processing. /// Considering the situation where processing of some request /// can be interrupted (panic, pod restart, etc..), - /// `processing_timeout` parameter is added to avoid stucking of requests. + /// `processing_timeout` parameter is added to avoid stuck requests. pub async fn get_next_queued_verification_request( &mut self, processing_timeout: Duration, diff --git a/core/lib/dal/src/lib.rs b/core/lib/dal/src/lib.rs index 788ce2d98bd1..9dfc9458202d 100644 --- a/core/lib/dal/src/lib.rs +++ b/core/lib/dal/src/lib.rs @@ -85,7 +85,7 @@ mod tests; /// Storage processor is the main storage interaction point. /// It holds down the connection (either direct or pooled) to the database -/// and provide methods to obtain different storage schemas. +/// and provide methods to obtain different storage schema. #[derive(Debug)] pub struct StorageProcessor<'a> { conn: ConnectionHolder<'a>, diff --git a/core/lib/dal/src/storage_dal.rs b/core/lib/dal/src/storage_dal.rs index fdaaea386171..8ec6d9164938 100644 --- a/core/lib/dal/src/storage_dal.rs +++ b/core/lib/dal/src/storage_dal.rs @@ -43,7 +43,7 @@ impl StorageDal<'_, '_> { .unwrap(); } - /// Returns bytecode for a factory dep with the specified bytecode `hash`. + /// Returns bytecode for a factory dependency with the specified bytecode `hash`. pub async fn get_factory_dep(&mut self, hash: H256) -> Option> { sqlx::query!( "SELECT bytecode FROM factory_deps WHERE bytecode_hash = $1", diff --git a/core/lib/dal/src/transactions_dal.rs b/core/lib/dal/src/transactions_dal.rs index 5b2356899619..cbca986b16c0 100644 --- a/core/lib/dal/src/transactions_dal.rs +++ b/core/lib/dal/src/transactions_dal.rs @@ -969,7 +969,7 @@ impl TransactionsDal<'_, '_> { } } - /// Returns miniblocks with their transactions that state_keeper needs to reexecute on restart. + /// Returns miniblocks with their transactions that state_keeper needs to re-execute on restart. /// These are the transactions that are included to some miniblock, /// but not included to L1 batch. The order of the transactions is the same as it was /// during the previous execution. diff --git a/core/lib/dal/src/witness_generator_dal.rs b/core/lib/dal/src/witness_generator_dal.rs index ab73c525c767..983112ab35cc 100644 --- a/core/lib/dal/src/witness_generator_dal.rs +++ b/core/lib/dal/src/witness_generator_dal.rs @@ -527,7 +527,7 @@ impl WitnessGeneratorDal<'_, '_> { /// Saves artifacts in node_aggregation_job /// and advances it to `waiting_for_proofs` status /// it will be advanced to `queued` by the prover when all the dependency proofs are computed. - /// If the node aggregation job was already `queued` in case of connrecunt run of same leaf aggregation job + /// If the node aggregation job was already `queued` in case of connector run of same leaf aggregation job /// we keep the status as is to prevent data race. pub async fn save_leaf_aggregation_artifacts( &mut self, diff --git a/core/lib/eth_client/src/lib.rs b/core/lib/eth_client/src/lib.rs index 2291f7214706..a03503683255 100644 --- a/core/lib/eth_client/src/lib.rs +++ b/core/lib/eth_client/src/lib.rs @@ -21,7 +21,7 @@ use zksync_types::{ }; /// Common Web3 interface, as seen by the core applications. -/// Encapsulates the raw Web3 interction, providing a high-level interface. +/// Encapsulates the raw Web3 interaction, providing a high-level interface. /// /// ## Trait contents /// @@ -34,7 +34,7 @@ use zksync_types::{ /// /// Most of the trait methods support the `component` parameter. This parameter is used to /// describe the caller of the method. It may be useful to find the component that makes an -/// unnecessary high amount of Web3 calls. Implementations are advices to count invocations +/// unnecessary high amount of Web3 calls. Implementations are advice to count invocations /// per component and expose them to Prometheus. #[async_trait] pub trait EthInterface: Sync + Send { @@ -139,7 +139,7 @@ pub trait EthInterface: Sync + Send { /// An extension of `EthInterface` trait, which is used to perform queries that are bound to /// a certain contract and account. /// -/// THe example use cases for this trait would be: +/// The example use cases for this trait would be: /// - An operator that sends transactions and interacts with zkSync contract. /// - A wallet implementation in the SDK that is tied to a user's account. /// @@ -149,10 +149,10 @@ pub trait EthInterface: Sync + Send { /// implementation that invokes `contract` / `contract_addr` / `sender_account` methods. #[async_trait] pub trait BoundEthInterface: EthInterface { - /// ABI of the contract that is used by the implementor. + /// ABI of the contract that is used by the implementer. fn contract(&self) -> ðabi::Contract; - /// Address of the contract that is used by the implementor. + /// Address of the contract that is used by the implementer. fn contract_addr(&self) -> H160; /// Chain ID of the L1 network the client is *configured* to connected to. diff --git a/core/lib/eth_signer/src/json_rpc_signer.rs b/core/lib/eth_signer/src/json_rpc_signer.rs index da81ff51dba7..b6619f5e831f 100644 --- a/core/lib/eth_signer/src/json_rpc_signer.rs +++ b/core/lib/eth_signer/src/json_rpc_signer.rs @@ -85,7 +85,7 @@ impl EthereumSigner for JsonRpcSigner { } } - /// Signs typed struct using ethereum private key by EIP-712 signature standard. + /// Signs typed struct using Ethereum private key by EIP-712 signature standard. /// Result of this function is the equivalent of RPC calling `eth_signTypedData`. async fn sign_typed_data( &self, @@ -192,7 +192,7 @@ impl JsonRpcSigner { self.address.ok_or(SignerError::DefineAddress) } - /// Specifies the Ethreum address which sets the address for which all other requests will be processed. + /// Specifies the Ethereum address which sets the address for which all other requests will be processed. /// If the address has already been set, then it will all the same change to a new one. pub async fn detect_address( &mut self, @@ -376,7 +376,7 @@ mod messages { Self::create("eth_sign", params) } - /// Signs typed struct using ethereum private key by EIP-712 signature standard. + /// Signs typed struct using Ethereum private key by EIP-712 signature standard. /// The address to sign with must be unlocked. pub fn sign_typed_data( address: Address, diff --git a/core/lib/eth_signer/src/pk_signer.rs b/core/lib/eth_signer/src/pk_signer.rs index 4a5bfb838def..680d87d62d0e 100644 --- a/core/lib/eth_signer/src/pk_signer.rs +++ b/core/lib/eth_signer/src/pk_signer.rs @@ -41,7 +41,7 @@ impl EthereumSigner for PrivateKeySigner { Ok(signature) } - /// Signs typed struct using ethereum private key by EIP-712 signature standard. + /// Signs typed struct using Ethereum private key by EIP-712 signature standard. /// Result of this function is the equivalent of RPC calling `eth_signTypedData`. async fn sign_typed_data( &self, diff --git a/core/lib/mempool/src/mempool_store.rs b/core/lib/mempool/src/mempool_store.rs index f900523517cc..a8b02bee0cb8 100644 --- a/core/lib/mempool/src/mempool_store.rs +++ b/core/lib/mempool/src/mempool_store.rs @@ -29,7 +29,7 @@ pub struct MempoolStore { /// Next priority operation next_priority_id: PriorityOpId, stashed_accounts: Vec
, - /// Number of l2 transactions in the mempool. + /// Number of L2 transactions in the mempool. size: u64, capacity: u64, } diff --git a/core/lib/merkle_tree/src/pruning.rs b/core/lib/merkle_tree/src/pruning.rs index bf60b8cf956b..21a3e8712fd7 100644 --- a/core/lib/merkle_tree/src/pruning.rs +++ b/core/lib/merkle_tree/src/pruning.rs @@ -89,7 +89,7 @@ impl MerkleTreePruner { /// Sets the sleep duration when the pruner cannot progress. This time should be enough /// for the tree to produce enough stale keys. /// - /// The default value is 60s. + /// The default value is 60 seconds. pub fn set_poll_interval(&mut self, poll_interval: Duration) { self.poll_interval = poll_interval; } diff --git a/core/lib/merkle_tree/src/recovery.rs b/core/lib/merkle_tree/src/recovery.rs index 6f57b64ee81f..85ac578cc0a1 100644 --- a/core/lib/merkle_tree/src/recovery.rs +++ b/core/lib/merkle_tree/src/recovery.rs @@ -8,7 +8,7 @@ //! afterwards will have the same outcome as if they were applied to the original tree. //! //! Importantly, a recovered tree is only *observably* identical to the original tree; it differs -//! in (currently unobservable) node versions. In a recovered tree, all nodes will initially have +//! in (currently un-observable) node versions. In a recovered tree, all nodes will initially have //! the same version (the snapshot version), while in the original tree, node versions are distributed //! from 0 to the snapshot version (both inclusive). //! diff --git a/core/lib/merkle_tree/src/storage/patch.rs b/core/lib/merkle_tree/src/storage/patch.rs index 9e251bf01782..6d0c38d6c9fb 100644 --- a/core/lib/merkle_tree/src/storage/patch.rs +++ b/core/lib/merkle_tree/src/storage/patch.rs @@ -344,7 +344,7 @@ impl WorkingPatchSet { } } - /// Computes hashes and serializes this changeset. + /// Computes hashes and serializes this change set. pub(super) fn finalize( self, manifest: Manifest, @@ -597,7 +597,7 @@ impl WorkingPatchSet { Some(Node::Internal(node)) => { let (next_nibble, child_ref) = node.last_child_ref(); nibbles = nibbles.push(next_nibble).unwrap(); - // ^ `unwrap()` is safe; there can be no internal nodes on the bottommost tree level + // ^ `unwrap()` is safe; there can be no internal nodes on the bottom-most tree level let child_key = nibbles.with_version(child_ref.version); let child_node = db.tree_node(&child_key, child_ref.is_leaf).unwrap(); // ^ `unwrap()` is safe by construction diff --git a/core/lib/merkle_tree/src/types/mod.rs b/core/lib/merkle_tree/src/types/mod.rs index 6988735ec021..de35d9024b7b 100644 --- a/core/lib/merkle_tree/src/types/mod.rs +++ b/core/lib/merkle_tree/src/types/mod.rs @@ -42,7 +42,7 @@ impl TreeEntry { } } - /// Returns `true` iff this entry encodes lack of a value. + /// Returns `true` if and only if this entry encodes lack of a value. pub fn is_empty(&self) -> bool { self.leaf_index == 0 && self.value_hash.is_zero() } @@ -63,7 +63,7 @@ pub struct TreeEntryWithProof { /// Proof of the value authenticity. /// /// If specified, a proof is the Merkle path consisting of up to 256 hashes - /// ordered starting the bottommost level of the tree (one with leaves) and ending before + /// ordered starting the bottom-most level of the tree (one with leaves) and ending before /// the root level. /// /// If the path is not full (contains <256 hashes), it means that the hashes at the beginning @@ -152,7 +152,7 @@ pub struct TreeLogEntryWithProof

> { /// Log entry about an atomic operation on the tree. pub base: TreeLogEntry, /// Merkle path to prove log authenticity. The path consists of up to 256 hashes - /// ordered starting the bottommost level of the tree (one with leaves) and ending before + /// ordered starting the bottom-most level of the tree (one with leaves) and ending before /// the root level. /// /// If the path is not full (contains <256 hashes), it means that the hashes at the beginning diff --git a/core/lib/multivm/src/glue/mod.rs b/core/lib/multivm/src/glue/mod.rs index 0904661a73cf..299093532bd4 100644 --- a/core/lib/multivm/src/glue/mod.rs +++ b/core/lib/multivm/src/glue/mod.rs @@ -11,7 +11,7 @@ pub(crate) mod history_mode; pub mod tracers; mod types; -/// This trait is a workaround on the Rust'c [orphan rule](orphan_rule). +/// This trait is a workaround on the Rust's [orphan rule](orphan_rule). /// We need to convert a lot of types that come from two different versions of some crate, /// and `From`/`Into` traits are natural way of doing so. Unfortunately, we can't implement an /// external trait on a pair of external types, so we're unable to use these traits. diff --git a/core/lib/multivm/src/glue/tracers/mod.rs b/core/lib/multivm/src/glue/tracers/mod.rs index b9c0e083b84c..a504d5d2c8ff 100644 --- a/core/lib/multivm/src/glue/tracers/mod.rs +++ b/core/lib/multivm/src/glue/tracers/mod.rs @@ -1,4 +1,4 @@ -//! # Multivm Tracing +//! # MultiVM Tracing //! //! The MultiVM tracing module enables support for Tracers in different versions of virtual machines. //! @@ -7,7 +7,7 @@ //! Different VM versions may have distinct requirements and types for Tracers. To accommodate these differences, //! this module defines one primary trait: //! -//! - `MultivmTracer`: This trait represents a tracer that can be converted into a tracer for +//! - `MultiVMTracer`: This trait represents a tracer that can be converted into a tracer for //! a specific VM version. //! //! Specific traits for each VM version, which support Custom Tracers: @@ -19,23 +19,23 @@ //! into a form compatible with the vm_virtual_blocks version. //! It defines a method `vm_virtual_blocks` for obtaining a boxed tracer. //! -//! For `MultivmTracer` to be implemented, the Tracer must implement all N currently +//! For `MultiVMTracer` to be implemented, the Tracer must implement all N currently //! existing sub-traits. //! //! ## Adding a new VM version //! -//! To add support for one more VM version to MultivmTracer, one needs to: +//! To add support for one more VM version to MultiVMTracer, one needs to: //! - Create a new trait performing conversion to the specified VM tracer, e.g., `IntoTracer`. -//! - Add this trait as a trait bound to the `MultivmTracer`. -//! - Add this trait as a trait bound for `T` in `MultivmTracer` implementation. -//! — Implement the trait for `T` with a bound to `VmTracer` for a specific version. +//! - Add this trait as a trait bound to the `MultiVMTracer`. +//! - Add this trait as a trait bound for `T` in `MultiVMTracer` implementation. +//! - Implement the trait for `T` with a bound to `VmTracer` for a specific version. //! use crate::HistoryMode; use zksync_state::WriteStorage; -pub type MultiVmTracerPointer = Box>; +pub type MultiVmTracerPointer = Box>; -pub trait MultivmTracer: +pub trait MultiVMTracer: IntoLatestTracer + IntoVmVirtualBlocksTracer + IntoVmRefundsEnhancementTracer { fn into_tracer_pointer(self) -> MultiVmTracerPointer @@ -102,7 +102,7 @@ where } } -impl MultivmTracer for T +impl MultiVMTracer for T where S: WriteStorage, H: HistoryMode, diff --git a/core/lib/multivm/src/interface/types/errors/vm_revert_reason.rs b/core/lib/multivm/src/interface/types/errors/vm_revert_reason.rs index 531d8b5507f6..4a6457491264 100644 --- a/core/lib/multivm/src/interface/types/errors/vm_revert_reason.rs +++ b/core/lib/multivm/src/interface/types/errors/vm_revert_reason.rs @@ -12,7 +12,7 @@ pub enum VmRevertReasonParsingError { IncorrectStringLength(Vec), } -/// Rich Revert Reasons https://github.com/0xProject/ZEIPs/issues/32 +/// Rich Revert Reasons `https://github.com/0xProject/ZEIPs/issues/32` #[derive(Debug, Clone, PartialEq)] pub enum VmRevertReason { General { diff --git a/core/lib/multivm/src/lib.rs b/core/lib/multivm/src/lib.rs index 1e45443c0f27..adb9358980fd 100644 --- a/core/lib/multivm/src/lib.rs +++ b/core/lib/multivm/src/lib.rs @@ -6,7 +6,7 @@ pub use crate::{ glue::{ history_mode::HistoryMode, - tracers::{MultiVmTracerPointer, MultivmTracer}, + tracers::{MultiVMTracer, MultiVmTracerPointer}, }, vm_instance::VmInstance, }; diff --git a/core/lib/multivm/src/versions/vm_1_3_2/bootloader_state.rs b/core/lib/multivm/src/versions/vm_1_3_2/bootloader_state.rs index a55846623239..f0324137bdcb 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/bootloader_state.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/bootloader_state.rs @@ -5,7 +5,7 @@ use crate::vm_1_3_2::vm_with_bootloader::TX_DESCRIPTION_OFFSET; /// Required to process transactions one by one (since we intercept the VM execution to execute /// transactions and add new ones to the memory on the fly). /// Think about it like a two-pointer scheme: one pointer (`free_tx_index`) tracks the end of the -/// initialized memory; while another (`tx_to_execute`) tracks our progess in this initialized memory. +/// initialized memory; while another (`tx_to_execute`) tracks our progress in this initialized memory. /// This is required since it's possible to push several transactions to the bootloader memory and then /// execute it one by one. /// diff --git a/core/lib/multivm/src/versions/vm_1_3_2/errors/vm_revert_reason.rs b/core/lib/multivm/src/versions/vm_1_3_2/errors/vm_revert_reason.rs index e1d50f724481..c127a9e6f2df 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/errors/vm_revert_reason.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/errors/vm_revert_reason.rs @@ -15,7 +15,7 @@ pub enum VmRevertReasonParsingError { IncorrectStringLength(Vec), } -/// Rich Revert Reasons https://github.com/0xProject/ZEIPs/issues/32 +/// Rich Revert Reasons `https://github.com/0xProject/ZEIPs/issues/32` #[derive(Debug, Clone, PartialEq)] pub enum VmRevertReason { General { diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracle_tools.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracle_tools.rs index 0f1feef4f94c..9f0f2600c5bb 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracle_tools.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracle_tools.rs @@ -12,7 +12,7 @@ use zksync_state::{StoragePtr, WriteStorage}; /// zkEVM requires a bunch of objects implementing given traits to work. /// For example: Storage, Memory, PrecompilerProcessor etc -/// (you can find all these traites in zk_evm crate -> src/abstractions/mod.rs) +/// (you can find all these traits in zk_evm crate -> src/abstractions/mod.rs) /// For each of these traits, we have a local implementation (for example StorageOracle) /// that also support additional features (like rollbacks & history). /// The OracleTools struct, holds all these things together in one place. diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/decommitter.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/decommitter.rs index e5c30c305bc8..17583b70dc9f 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/decommitter.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/decommitter.rs @@ -68,7 +68,7 @@ impl DecommitterOracle } } - /// Adds additional bytecodes. They will take precendent over the bytecodes from storage. + /// Adds additional bytecodes. They will take precedent over the bytecodes from storage. pub fn populate(&mut self, bytecodes: Vec<(U256, Vec)>, timestamp: Timestamp) { for (hash, bytecode) in bytecodes { self.known_bytecodes.insert(hash, bytecode, timestamp); @@ -178,7 +178,7 @@ impl DecommittmentProcessor > { self.decommitment_requests.push((), partial_query.timestamp); // First - check if we didn't fetch this bytecode in the past. - // If we did - we can just return the page that we used before (as the memory is read only). + // If we did - we can just return the page that we used before (as the memory is readonly). if let Some(memory_page) = self .decommitted_code_hashes .inner() diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/bootloader.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/bootloader.rs index 20d8621e8291..16b1efdff54b 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/bootloader.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/bootloader.rs @@ -16,7 +16,7 @@ use zk_evm_1_3_3::{ zkevm_opcode_defs::{Opcode, RetOpcode}, }; -/// Tells the VM to end the execution before `ret` from the booloader if there is no panic or revert. +/// Tells the VM to end the execution before `ret` from the bootloader if there is no panic or revert. /// Also, saves the information if this `ret` was caused by "out of gas" panic. #[derive(Debug, Clone, Default)] pub struct BootloaderTracer { diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/call.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/call.rs index b50ee5f925cf..72701f6e0f29 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/call.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/call.rs @@ -94,7 +94,7 @@ impl Tracer for CallTracer { } impl CallTracer { - /// We use parent gas for propery calculation of gas used in the trace. + /// We use parent gas for property calculation of gas used in the trace. /// This method updates parent gas for the current call. fn update_parent_gas(&mut self, state: &VmLocalStateData<'_>, current_call: &mut Call) { let current = state.vm_local_state.callstack.current; diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/utils.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/utils.rs index 2914faf5120d..9c9e87c065d0 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/utils.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/utils.rs @@ -99,7 +99,7 @@ pub(crate) fn get_debug_log( } /// Reads the memory slice represented by the fat pointer. -/// Note, that the fat pointer must point to the accesible memory (i.e. not cleared up yet). +/// Note, that the fat pointer must point to the accessible memory (i.e. not cleared up yet). pub(crate) fn read_pointer( memory: &SimpleMemory, pointer: FatPointer, diff --git a/core/lib/multivm/src/versions/vm_1_3_2/test_utils.rs b/core/lib/multivm/src/versions/vm_1_3_2/test_utils.rs index 6738b0704825..e697e3b310d8 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/test_utils.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/test_utils.rs @@ -59,7 +59,7 @@ impl PartialEq for ModifiedKeysMap { #[derive(Clone, PartialEq, Debug)] pub struct DecommitterTestInnerState { - /// There is no way to "trully" compare the storage pointer, + /// There is no way to "truly" compare the storage pointer, /// so we just compare the modified keys. This is reasonable enough. pub modified_storage_keys: ModifiedKeysMap, pub known_bytecodes: HistoryRecorder>, H>, @@ -68,7 +68,7 @@ pub struct DecommitterTestInnerState { #[derive(Clone, PartialEq, Debug)] pub struct StorageOracleInnerState { - /// There is no way to "trully" compare the storage pointer, + /// There is no way to "truly" compare the storage pointer, /// so we just compare the modified keys. This is reasonable enough. pub modified_storage_keys: ModifiedKeysMap, diff --git a/core/lib/multivm/src/versions/vm_1_3_2/transaction_data.rs b/core/lib/multivm/src/versions/vm_1_3_2/transaction_data.rs index fc931f2ad9af..2d9dd1cb7aa1 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/transaction_data.rs @@ -212,12 +212,12 @@ impl TransactionData { self.reserved_dynamic.len() as u64, ); - let coeficients = OverheadCoeficients::from_tx_type(self.tx_type); + let coefficients = OverheadCoefficients::from_tx_type(self.tx_type); get_amortized_overhead( total_gas_limit, gas_price_per_pubdata, encoded_len, - coeficients, + coefficients, ) } @@ -231,7 +231,7 @@ pub fn derive_overhead( gas_limit: u32, gas_price_per_pubdata: u32, encoded_len: usize, - coeficients: OverheadCoeficients, + coefficients: OverheadCoefficients, ) -> u32 { // Even if the gas limit is greater than the MAX_TX_ERGS_LIMIT, we assume that everything beyond MAX_TX_ERGS_LIMIT // will be spent entirely on publishing bytecodes and so we derive the overhead solely based on the capped value @@ -268,31 +268,31 @@ pub fn derive_overhead( // ); vec![ - (coeficients.ergs_limit_overhead_coeficient + (coefficients.ergs_limit_overhead_coeficient * overhead_for_single_instance_circuits.as_u32() as f64) .floor() as u32, - (coeficients.bootloader_memory_overhead_coeficient * overhead_for_length.as_u32() as f64) + (coefficients.bootloader_memory_overhead_coeficient * overhead_for_length.as_u32() as f64) .floor() as u32, - (coeficients.slot_overhead_coeficient * tx_slot_overhead.as_u32() as f64) as u32, + (coefficients.slot_overhead_coeficient * tx_slot_overhead.as_u32() as f64) as u32, ] .into_iter() .max() .unwrap() } -/// Contains the coeficients with which the overhead for transactions will be calculated. -/// All of the coeficients should be <= 1. There are here to provide a certain "discount" for normal transactions +/// Contains the coefficients with which the overhead for transactions will be calculated. +/// All of the coefficients should be <= 1. There are here to provide a certain "discount" for normal transactions /// at the risk of malicious transactions that may close the block prematurely. -/// IMPORTANT: to perform correct computations, `MAX_TX_ERGS_LIMIT / coeficients.ergs_limit_overhead_coeficient` MUST +/// IMPORTANT: to perform correct computations, `MAX_TX_ERGS_LIMIT / coefficients.ergs_limit_overhead_coeficient` MUST /// result in an integer number #[derive(Debug, Clone, Copy)] -pub struct OverheadCoeficients { +pub struct OverheadCoefficients { slot_overhead_coeficient: f64, bootloader_memory_overhead_coeficient: f64, ergs_limit_overhead_coeficient: f64, } -impl OverheadCoeficients { +impl OverheadCoefficients { // This method ensures that the parameters keep the required invariants fn new_checked( slot_overhead_coeficient: f64, @@ -314,11 +314,11 @@ impl OverheadCoeficients { // L1->L2 do not receive any discounts fn new_l1() -> Self { - OverheadCoeficients::new_checked(1.0, 1.0, 1.0) + OverheadCoefficients::new_checked(1.0, 1.0, 1.0) } fn new_l2() -> Self { - OverheadCoeficients::new_checked( + OverheadCoefficients::new_checked( 1.0, 1.0, // For L2 transactions we allow a certain default discount with regard to the number of ergs. // Multiinstance circuits can in theory be spawned infinite times, while projected future limitations @@ -342,7 +342,7 @@ pub fn get_amortized_overhead( total_gas_limit: u32, gas_per_pubdata_byte_limit: u32, encoded_len: usize, - coeficients: OverheadCoeficients, + coefficients: OverheadCoefficients, ) -> u32 { // Using large U256 type to prevent overflows. let overhead_for_block_gas = U256::from(block_overhead_gas(gas_per_pubdata_byte_limit)); @@ -379,7 +379,7 @@ pub fn get_amortized_overhead( let tx_slot_overhead = { let tx_slot_overhead = ceil_div_u256(overhead_for_block_gas, MAX_TXS_IN_BLOCK.into()).as_u32(); - (coeficients.slot_overhead_coeficient * tx_slot_overhead as f64).floor() as u32 + (coefficients.slot_overhead_coeficient * tx_slot_overhead as f64).floor() as u32 }; // 2. The overhead for occupying the bootloader memory can be derived from encoded_len @@ -390,7 +390,7 @@ pub fn get_amortized_overhead( ) .as_u32(); - (coeficients.bootloader_memory_overhead_coeficient * overhead_for_length as f64).floor() + (coefficients.bootloader_memory_overhead_coeficient * overhead_for_length as f64).floor() as u32 }; @@ -435,7 +435,7 @@ pub fn get_amortized_overhead( let overhead_for_gas = { let numerator = overhead_for_block_gas * total_gas_limit + U256::from(MAX_TX_ERGS_LIMIT); let denominator: U256 = U256::from( - (MAX_TX_ERGS_LIMIT as f64 / coeficients.ergs_limit_overhead_coeficient) as u64, + (MAX_TX_ERGS_LIMIT as f64 / coefficients.ergs_limit_overhead_coeficient) as u64, ) + overhead_for_block_gas; let overhead_for_gas = (numerator - 1) / denominator; @@ -460,7 +460,7 @@ pub fn get_amortized_overhead( MAX_L2_TX_GAS_LIMIT as u32, gas_per_pubdata_byte_limit, encoded_len.as_usize(), - coeficients, + coefficients, ) } else { overhead @@ -483,7 +483,7 @@ mod tests { total_gas_limit: u32, gas_per_pubdata_byte_limit: u32, encoded_len: usize, - coeficients: OverheadCoeficients, + coefficients: OverheadCoefficients, ) -> u32 { let mut left_bound = if MAX_TX_ERGS_LIMIT < total_gas_limit { total_gas_limit - MAX_TX_ERGS_LIMIT @@ -501,7 +501,7 @@ mod tests { total_gas_limit - suggested_overhead, gas_per_pubdata_byte_limit, encoded_len, - coeficients, + coefficients, ); derived_overhead >= suggested_overhead @@ -530,41 +530,41 @@ mod tests { let test_params = |total_gas_limit: u32, gas_per_pubdata: u32, encoded_len: usize, - coeficients: OverheadCoeficients| { + coefficients: OverheadCoefficients| { let result_by_efficient_search = - get_amortized_overhead(total_gas_limit, gas_per_pubdata, encoded_len, coeficients); + get_amortized_overhead(total_gas_limit, gas_per_pubdata, encoded_len, coefficients); let result_by_binary_search = get_maximal_allowed_overhead_bin_search( total_gas_limit, gas_per_pubdata, encoded_len, - coeficients, + coefficients, ); assert_eq!(result_by_efficient_search, result_by_binary_search); }; // Some arbitrary test - test_params(60_000_000, 800, 2900, OverheadCoeficients::new_l2()); + test_params(60_000_000, 800, 2900, OverheadCoefficients::new_l2()); // Very small parameters - test_params(0, 1, 12, OverheadCoeficients::new_l2()); + test_params(0, 1, 12, OverheadCoefficients::new_l2()); // Relatively big parameters let max_tx_overhead = derive_overhead( MAX_TX_ERGS_LIMIT, 5000, 10000, - OverheadCoeficients::new_l2(), + OverheadCoefficients::new_l2(), ); test_params( MAX_TX_ERGS_LIMIT + max_tx_overhead, 5000, 10000, - OverheadCoeficients::new_l2(), + OverheadCoefficients::new_l2(), ); - test_params(115432560, 800, 2900, OverheadCoeficients::new_l1()); + test_params(115432560, 800, 2900, OverheadCoefficients::new_l1()); } #[test] diff --git a/core/lib/multivm/src/versions/vm_1_3_2/vm_instance.rs b/core/lib/multivm/src/versions/vm_1_3_2/vm_instance.rs index d84cd1cc7b6e..3e157e74c02f 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/vm_instance.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/vm_instance.rs @@ -364,7 +364,7 @@ impl VmInstance { } } - /// Removes the latest snapshot without rollbacking to it. + /// Removes the latest snapshot without rolling it back. /// This function expects that there is at least one snapshot present. pub fn pop_snapshot_no_rollback(&mut self) { self.snapshots.pop().unwrap(); diff --git a/core/lib/multivm/src/versions/vm_latest/bootloader_state/snapshot.rs b/core/lib/multivm/src/versions/vm_latest/bootloader_state/snapshot.rs index 683fc28a69e9..8f1cec3cb7f1 100644 --- a/core/lib/multivm/src/versions/vm_latest/bootloader_state/snapshot.rs +++ b/core/lib/multivm/src/versions/vm_latest/bootloader_state/snapshot.rs @@ -4,9 +4,9 @@ use zksync_types::H256; pub(crate) struct BootloaderStateSnapshot { /// ID of the next transaction to be executed. pub(crate) tx_to_execute: usize, - /// Stored l2 blocks in bootloader memory + /// Stored L2 blocks in bootloader memory pub(crate) l2_blocks_len: usize, - /// Snapshot of the last l2 block. Only this block could be changed during the rollback + /// Snapshot of the last L2 block. Only this block could be changed during the rollback pub(crate) last_l2_block: L2BlockSnapshot, /// The number of 32-byte words spent on the already included compressed bytecodes. pub(crate) compressed_bytecodes_encoding: usize, @@ -20,6 +20,6 @@ pub(crate) struct BootloaderStateSnapshot { pub(crate) struct L2BlockSnapshot { /// The rolling hash of all the transactions in the miniblock pub(crate) txs_rolling_hash: H256, - /// The number of transactions in the last l2 block + /// The number of transactions in the last L2 block pub(crate) txs_len: usize, } diff --git a/core/lib/multivm/src/versions/vm_latest/bootloader_state/tx.rs b/core/lib/multivm/src/versions/vm_latest/bootloader_state/tx.rs index 6d322e5877d6..dce0ecce3fbc 100644 --- a/core/lib/multivm/src/versions/vm_latest/bootloader_state/tx.rs +++ b/core/lib/multivm/src/versions/vm_latest/bootloader_state/tx.rs @@ -14,7 +14,7 @@ pub(super) struct BootloaderTx { pub(super) refund: u32, /// Gas overhead pub(super) gas_overhead: u32, - /// Gas Limit for this transaction. It can be different from the gaslimit inside the transaction + /// Gas Limit for this transaction. It can be different from the gas limit inside the transaction pub(super) trusted_gas_limit: U256, /// Offset of the tx in bootloader memory pub(super) offset: usize, diff --git a/core/lib/multivm/src/versions/vm_latest/constants.rs b/core/lib/multivm/src/versions/vm_latest/constants.rs index c67156681a0b..4d1c77054237 100644 --- a/core/lib/multivm/src/versions/vm_latest/constants.rs +++ b/core/lib/multivm/src/versions/vm_latest/constants.rs @@ -60,7 +60,7 @@ pub const OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_OFFSET: usize = /// One of "worst case" scenarios for the number of state diffs in a batch is when 120kb of pubdata is spent /// on repeated writes, that are all zeroed out. In this case, the number of diffs is 120k / 5 = 24k. This means that they will have /// accommodate 6528000 bytes of calldata for the uncompressed state diffs. Adding 120k on top leaves us with -/// roughly 6650000 bytes needed for calldata. 207813 slots are needed to accomodate this amount of data. +/// roughly 6650000 bytes needed for calldata. 207813 slots are needed to accommodate this amount of data. /// We round up to 208000 slots just in case. /// /// In theory though much more calldata could be used (if for instance 1 byte is used for enum index). It is the responsibility of the @@ -92,10 +92,10 @@ pub const BLOCK_OVERHEAD_L1_GAS: u32 = 1000000; pub const BLOCK_OVERHEAD_PUBDATA: u32 = BLOCK_OVERHEAD_L1_GAS / L1_GAS_PER_PUBDATA_BYTE; /// VM Hooks are used for communication between bootloader and tracers. -/// The 'type'/'opcode' is put into VM_HOOK_POSITION slot, +/// The 'type' / 'opcode' is put into VM_HOOK_POSITION slot, /// and VM_HOOKS_PARAMS_COUNT parameters (each 32 bytes) are put in the slots before. /// So the layout looks like this: -/// [param 0][param 1][vmhook opcode] +/// `[param 0][param 1][vmhook opcode]` pub const VM_HOOK_POSITION: u32 = RESULT_SUCCESS_FIRST_SLOT - 1; pub const VM_HOOK_PARAMS_COUNT: u32 = 2; pub const VM_HOOK_PARAMS_START_POSITION: u32 = VM_HOOK_POSITION - VM_HOOK_PARAMS_COUNT; diff --git a/core/lib/multivm/src/versions/vm_latest/implementation/statistics.rs b/core/lib/multivm/src/versions/vm_latest/implementation/statistics.rs index ddbc7aec2f73..92604479a88e 100644 --- a/core/lib/multivm/src/versions/vm_latest/implementation/statistics.rs +++ b/core/lib/multivm/src/versions/vm_latest/implementation/statistics.rs @@ -43,7 +43,7 @@ impl Vm { } } - /// Returns the hashes the bytecodes that have been decommitted by the decomittment processor. + /// Returns the hashes the bytecodes that have been decommitted by the decommitment processor. pub(crate) fn get_used_contracts(&self) -> Vec { self.state .decommittment_processor diff --git a/core/lib/multivm/src/versions/vm_latest/old_vm/oracles/decommitter.rs b/core/lib/multivm/src/versions/vm_latest/old_vm/oracles/decommitter.rs index fe5416cd1201..c679532fa763 100644 --- a/core/lib/multivm/src/versions/vm_latest/old_vm/oracles/decommitter.rs +++ b/core/lib/multivm/src/versions/vm_latest/old_vm/oracles/decommitter.rs @@ -70,7 +70,7 @@ impl DecommitterOracle { } } - /// Adds additional bytecodes. They will take precendent over the bytecodes from storage. + /// Adds additional bytecodes. They will take precedent over the bytecodes from storage. pub fn populate(&mut self, bytecodes: Vec<(U256, Vec)>, timestamp: Timestamp) { for (hash, bytecode) in bytecodes { self.known_bytecodes.insert(hash, bytecode, timestamp); @@ -180,7 +180,7 @@ impl DecommittmentProcess > { self.decommitment_requests.push((), partial_query.timestamp); // First - check if we didn't fetch this bytecode in the past. - // If we did - we can just return the page that we used before (as the memory is read only). + // If we did - we can just return the page that we used before (as the memory is readonly). if let Some(memory_page) = self .decommitted_code_hashes .inner() diff --git a/core/lib/multivm/src/versions/vm_latest/tests/tester/inner_state.rs b/core/lib/multivm/src/versions/vm_latest/tests/tester/inner_state.rs index ec9ffe785f95..4767f9344795 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/tester/inner_state.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/tester/inner_state.rs @@ -34,7 +34,7 @@ impl PartialEq for ModifiedKeysMap { #[derive(Clone, PartialEq, Debug)] pub(crate) struct DecommitterTestInnerState { - /// There is no way to "trully" compare the storage pointer, + /// There is no way to "truly" compare the storage pointer, /// so we just compare the modified keys. This is reasonable enough. pub(crate) modified_storage_keys: ModifiedKeysMap, pub(crate) known_bytecodes: HistoryRecorder>, H>, @@ -43,7 +43,7 @@ pub(crate) struct DecommitterTestInnerState { #[derive(Clone, PartialEq, Debug)] pub(crate) struct StorageOracleInnerState { - /// There is no way to "trully" compare the storage pointer, + /// There is no way to "truly" compare the storage pointer, /// so we just compare the modified keys. This is reasonable enough. pub(crate) modified_storage_keys: ModifiedKeysMap, diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/utils.rs b/core/lib/multivm/src/versions/vm_latest/tracers/utils.rs index 5b34eee4742a..c91d2f3ce0c1 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/utils.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/utils.rs @@ -109,7 +109,7 @@ pub(crate) fn get_debug_log( } /// Reads the memory slice represented by the fat pointer. -/// Note, that the fat pointer must point to the accesible memory (i.e. not cleared up yet). +/// Note, that the fat pointer must point to the accessible memory (i.e. not cleared up yet). pub(crate) fn read_pointer( memory: &SimpleMemory, pointer: FatPointer, diff --git a/core/lib/multivm/src/versions/vm_latest/types/internals/transaction_data.rs b/core/lib/multivm/src/versions/vm_latest/types/internals/transaction_data.rs index f81741d2a431..3c7b9bcac035 100644 --- a/core/lib/multivm/src/versions/vm_latest/types/internals/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_latest/types/internals/transaction_data.rs @@ -11,7 +11,7 @@ use zksync_types::{ use zksync_utils::address_to_h256; use zksync_utils::{bytecode::hash_bytecode, bytes_to_be_words, h256_to_u256}; -use crate::vm_latest::utils::overhead::{get_amortized_overhead, OverheadCoeficients}; +use crate::vm_latest::utils::overhead::{get_amortized_overhead, OverheadCoefficients}; /// This structure represents the data that is used by /// the Bootloader to describe the transaction. @@ -212,12 +212,12 @@ impl TransactionData { self.reserved_dynamic.len() as u64, ); - let coeficients = OverheadCoeficients::from_tx_type(self.tx_type); + let coefficients = OverheadCoefficients::from_tx_type(self.tx_type); get_amortized_overhead( total_gas_limit, gas_price_per_pubdata, encoded_len, - coeficients, + coefficients, ) } diff --git a/core/lib/multivm/src/versions/vm_latest/utils/fee.rs b/core/lib/multivm/src/versions/vm_latest/utils/fee.rs index bbf09a75f3fc..23b744a348f7 100644 --- a/core/lib/multivm/src/versions/vm_latest/utils/fee.rs +++ b/core/lib/multivm/src/versions/vm_latest/utils/fee.rs @@ -4,7 +4,7 @@ use zksync_utils::ceil_div; use crate::vm_latest::old_vm::utils::eth_price_per_pubdata_byte; -/// Calcluates the amount of gas required to publish one byte of pubdata +/// Calculates the amount of gas required to publish one byte of pubdata pub fn base_fee_to_gas_per_pubdata(l1_gas_price: u64, base_fee: u64) -> u64 { let eth_price_per_pubdata_byte = eth_price_per_pubdata_byte(l1_gas_price); diff --git a/core/lib/multivm/src/versions/vm_latest/utils/overhead.rs b/core/lib/multivm/src/versions/vm_latest/utils/overhead.rs index 2541c7d7037c..a4012e540ed1 100644 --- a/core/lib/multivm/src/versions/vm_latest/utils/overhead.rs +++ b/core/lib/multivm/src/versions/vm_latest/utils/overhead.rs @@ -12,7 +12,7 @@ pub fn derive_overhead( gas_limit: u32, gas_price_per_pubdata: u32, encoded_len: usize, - coeficients: OverheadCoeficients, + coefficients: OverheadCoefficients, ) -> u32 { // Even if the gas limit is greater than the MAX_TX_ERGS_LIMIT, we assume that everything beyond MAX_TX_ERGS_LIMIT // will be spent entirely on publishing bytecodes and so we derive the overhead solely based on the capped value @@ -49,31 +49,31 @@ pub fn derive_overhead( // ); vec![ - (coeficients.ergs_limit_overhead_coeficient + (coefficients.ergs_limit_overhead_coeficient * overhead_for_single_instance_circuits.as_u32() as f64) .floor() as u32, - (coeficients.bootloader_memory_overhead_coeficient * overhead_for_length.as_u32() as f64) + (coefficients.bootloader_memory_overhead_coeficient * overhead_for_length.as_u32() as f64) .floor() as u32, - (coeficients.slot_overhead_coeficient * tx_slot_overhead.as_u32() as f64) as u32, + (coefficients.slot_overhead_coeficient * tx_slot_overhead.as_u32() as f64) as u32, ] .into_iter() .max() .unwrap() } -/// Contains the coeficients with which the overhead for transactions will be calculated. -/// All of the coeficients should be <= 1. There are here to provide a certain "discount" for normal transactions +/// Contains the coefficients with which the overhead for transactions will be calculated. +/// All of the coefficients should be <= 1. There are here to provide a certain "discount" for normal transactions /// at the risk of malicious transactions that may close the block prematurely. -/// IMPORTANT: to perform correct computations, `MAX_TX_ERGS_LIMIT / coeficients.ergs_limit_overhead_coeficient` MUST +/// IMPORTANT: to perform correct computations, `MAX_TX_ERGS_LIMIT / coefficients.ergs_limit_overhead_coeficient` MUST /// result in an integer number #[derive(Debug, Clone, Copy)] -pub struct OverheadCoeficients { +pub struct OverheadCoefficients { slot_overhead_coeficient: f64, bootloader_memory_overhead_coeficient: f64, ergs_limit_overhead_coeficient: f64, } -impl OverheadCoeficients { +impl OverheadCoefficients { // This method ensures that the parameters keep the required invariants fn new_checked( slot_overhead_coeficient: f64, @@ -95,11 +95,11 @@ impl OverheadCoeficients { // L1->L2 do not receive any discounts fn new_l1() -> Self { - OverheadCoeficients::new_checked(1.0, 1.0, 1.0) + OverheadCoefficients::new_checked(1.0, 1.0, 1.0) } fn new_l2() -> Self { - OverheadCoeficients::new_checked( + OverheadCoefficients::new_checked( 1.0, 1.0, // For L2 transactions we allow a certain default discount with regard to the number of ergs. // Multiinstance circuits can in theory be spawned infinite times, while projected future limitations @@ -109,7 +109,7 @@ impl OverheadCoeficients { ) } - /// Return the coeficients for the given transaction type + /// Return the coefficients for the given transaction type pub fn from_tx_type(tx_type: u8) -> Self { if is_l1_tx_type(tx_type) { Self::new_l1() @@ -124,7 +124,7 @@ pub(crate) fn get_amortized_overhead( total_gas_limit: u32, gas_per_pubdata_byte_limit: u32, encoded_len: usize, - coeficients: OverheadCoeficients, + coefficients: OverheadCoefficients, ) -> u32 { // Using large U256 type to prevent overflows. let overhead_for_block_gas = U256::from(block_overhead_gas(gas_per_pubdata_byte_limit)); @@ -161,7 +161,7 @@ pub(crate) fn get_amortized_overhead( let tx_slot_overhead = { let tx_slot_overhead = ceil_div_u256(overhead_for_block_gas, MAX_TXS_IN_BLOCK.into()).as_u32(); - (coeficients.slot_overhead_coeficient * tx_slot_overhead as f64).floor() as u32 + (coefficients.slot_overhead_coeficient * tx_slot_overhead as f64).floor() as u32 }; // 2. The overhead for occupying the bootloader memory can be derived from encoded_len @@ -172,7 +172,7 @@ pub(crate) fn get_amortized_overhead( ) .as_u32(); - (coeficients.bootloader_memory_overhead_coeficient * overhead_for_length as f64).floor() + (coefficients.bootloader_memory_overhead_coeficient * overhead_for_length as f64).floor() as u32 }; @@ -217,7 +217,7 @@ pub(crate) fn get_amortized_overhead( let overhead_for_gas = { let numerator = overhead_for_block_gas * total_gas_limit + U256::from(MAX_TX_ERGS_LIMIT); let denominator: U256 = U256::from( - (MAX_TX_ERGS_LIMIT as f64 / coeficients.ergs_limit_overhead_coeficient) as u64, + (MAX_TX_ERGS_LIMIT as f64 / coefficients.ergs_limit_overhead_coeficient) as u64, ) + overhead_for_block_gas; let overhead_for_gas = (numerator - 1) / denominator; @@ -242,7 +242,7 @@ pub(crate) fn get_amortized_overhead( MAX_L2_TX_GAS_LIMIT as u32, gas_per_pubdata_byte_limit, encoded_len.as_usize(), - coeficients, + coefficients, ) } else { overhead @@ -263,7 +263,7 @@ mod tests { total_gas_limit: u32, gas_per_pubdata_byte_limit: u32, encoded_len: usize, - coeficients: OverheadCoeficients, + coefficients: OverheadCoefficients, ) -> u32 { let mut left_bound = if MAX_TX_ERGS_LIMIT < total_gas_limit { total_gas_limit - MAX_TX_ERGS_LIMIT @@ -281,7 +281,7 @@ mod tests { total_gas_limit - suggested_overhead, gas_per_pubdata_byte_limit, encoded_len, - coeficients, + coefficients, ); derived_overhead >= suggested_overhead @@ -310,40 +310,40 @@ mod tests { let test_params = |total_gas_limit: u32, gas_per_pubdata: u32, encoded_len: usize, - coeficients: OverheadCoeficients| { + coefficients: OverheadCoefficients| { let result_by_efficient_search = - get_amortized_overhead(total_gas_limit, gas_per_pubdata, encoded_len, coeficients); + get_amortized_overhead(total_gas_limit, gas_per_pubdata, encoded_len, coefficients); let result_by_binary_search = get_maximal_allowed_overhead_bin_search( total_gas_limit, gas_per_pubdata, encoded_len, - coeficients, + coefficients, ); assert_eq!(result_by_efficient_search, result_by_binary_search); }; // Some arbitrary test - test_params(60_000_000, 800, 2900, OverheadCoeficients::new_l2()); + test_params(60_000_000, 800, 2900, OverheadCoefficients::new_l2()); // Very small parameters - test_params(0, 1, 12, OverheadCoeficients::new_l2()); + test_params(0, 1, 12, OverheadCoefficients::new_l2()); // Relatively big parameters let max_tx_overhead = derive_overhead( MAX_TX_ERGS_LIMIT, 5000, 10000, - OverheadCoeficients::new_l2(), + OverheadCoefficients::new_l2(), ); test_params( MAX_TX_ERGS_LIMIT + max_tx_overhead, 5000, 10000, - OverheadCoeficients::new_l2(), + OverheadCoefficients::new_l2(), ); - test_params(115432560, 800, 2900, OverheadCoeficients::new_l1()); + test_params(115432560, 800, 2900, OverheadCoefficients::new_l1()); } } diff --git a/core/lib/multivm/src/versions/vm_latest/vm.rs b/core/lib/multivm/src/versions/vm_latest/vm.rs index e63b6438dc9d..20d74e39093f 100644 --- a/core/lib/multivm/src/versions/vm_latest/vm.rs +++ b/core/lib/multivm/src/versions/vm_latest/vm.rs @@ -138,7 +138,7 @@ impl VmInterface for Vm { } } -/// Methods of vm, which required some history manipullations +/// Methods of vm, which required some history manipulations impl VmInterfaceHistoryEnabled for Vm { /// Create snapshot of current vm state and push it into the memory fn make_snapshot(&mut self) { diff --git a/core/lib/multivm/src/versions/vm_m5/bootloader_state.rs b/core/lib/multivm/src/versions/vm_m5/bootloader_state.rs index 518d999b6ea5..4bb51c7a8395 100644 --- a/core/lib/multivm/src/versions/vm_m5/bootloader_state.rs +++ b/core/lib/multivm/src/versions/vm_m5/bootloader_state.rs @@ -5,7 +5,7 @@ use crate::vm_m5::vm_with_bootloader::TX_DESCRIPTION_OFFSET; /// Required to process transactions one by one (since we intercept the VM execution to execute /// transactions and add new ones to the memory on the fly). /// Think about it like a two-pointer scheme: one pointer (`free_tx_index`) tracks the end of the -/// initialized memory; while another (`tx_to_execute`) tracks our progess in this initialized memory. +/// initialized memory; while another (`tx_to_execute`) tracks our progress in this initialized memory. /// This is required since it's possible to push several transactions to the bootloader memory and then /// execute it one by one. /// diff --git a/core/lib/multivm/src/versions/vm_m5/errors/vm_revert_reason.rs b/core/lib/multivm/src/versions/vm_m5/errors/vm_revert_reason.rs index 1997336c3a4e..5d1a075f6a53 100644 --- a/core/lib/multivm/src/versions/vm_m5/errors/vm_revert_reason.rs +++ b/core/lib/multivm/src/versions/vm_m5/errors/vm_revert_reason.rs @@ -15,7 +15,7 @@ pub enum VmRevertReasonParsingError { IncorrectStringLength(Vec), } -/// Rich Revert Reasons https://github.com/0xProject/ZEIPs/issues/32 +/// Rich Revert Reasons `https://github.com/0xProject/ZEIPs/issues/32` #[derive(Debug, Clone, PartialEq)] pub enum VmRevertReason { General { diff --git a/core/lib/multivm/src/versions/vm_m5/oracles/tracer.rs b/core/lib/multivm/src/versions/vm_m5/oracles/tracer.rs index d8a70bdaf644..96ba04e85aa5 100644 --- a/core/lib/multivm/src/versions/vm_m5/oracles/tracer.rs +++ b/core/lib/multivm/src/versions/vm_m5/oracles/tracer.rs @@ -651,7 +651,7 @@ impl OneTxTracer { } } -/// Tells the VM to end the execution before `ret` from the booloader if there is no panic or revert. +/// Tells the VM to end the execution before `ret` from the bootloader if there is no panic or revert. /// Also, saves the information if this `ret` was caused by "out of gas" panic. #[derive(Debug, Clone, Default)] pub struct BootloaderTracer { @@ -816,7 +816,7 @@ fn get_debug_log(state: &VmLocalStateData<'_>, memory: &SimpleMemory) -> String } /// Reads the memory slice represented by the fat pointer. -/// Note, that the fat pointer must point to the accesible memory (i.e. not cleared up yet). +/// Note, that the fat pointer must point to the accessible memory (i.e. not cleared up yet). pub(crate) fn read_pointer(memory: &SimpleMemory, pointer: FatPointer) -> Vec { let FatPointer { offset, diff --git a/core/lib/multivm/src/versions/vm_m5/test_utils.rs b/core/lib/multivm/src/versions/vm_m5/test_utils.rs index 590579be6d8a..36c1d60dfdac 100644 --- a/core/lib/multivm/src/versions/vm_m5/test_utils.rs +++ b/core/lib/multivm/src/versions/vm_m5/test_utils.rs @@ -58,7 +58,7 @@ impl PartialEq for ModifiedKeysMap { #[derive(Clone, PartialEq, Debug)] pub struct DecommitterTestInnerState { - /// There is no way to "trully" compare the storage pointer, + /// There is no way to "truly" compare the storage pointer, /// so we just compare the modified keys. This is reasonable enough. pub modified_storage_keys: ModifiedKeysMap, pub known_bytecodes: HistoryRecorder>>, @@ -67,7 +67,7 @@ pub struct DecommitterTestInnerState { #[derive(Clone, PartialEq, Debug)] pub struct StorageOracleInnerState { - /// There is no way to "trully" compare the storage pointer, + /// There is no way to "truly" compare the storage pointer, /// so we just compare the modified keys. This is reasonable enough. pub modified_storage_keys: ModifiedKeysMap, diff --git a/core/lib/multivm/src/versions/vm_m5/vm_instance.rs b/core/lib/multivm/src/versions/vm_m5/vm_instance.rs index 1df40c8a0b86..e92305003c72 100644 --- a/core/lib/multivm/src/versions/vm_m5/vm_instance.rs +++ b/core/lib/multivm/src/versions/vm_m5/vm_instance.rs @@ -108,7 +108,7 @@ pub struct VmExecutionResult { /// available to VM before and after execution. /// /// It means, that depending on the context, `gas_used` may represent different things. - /// If VM is continously invoked and interrupted after each tx, this field may represent the + /// If VM is continuously invoked and interrupted after each tx, this field may represent the /// amount of gas spent by a single transaction. /// /// To understand, which value does `gas_used` represent, see the documentation for the method diff --git a/core/lib/multivm/src/versions/vm_m6/bootloader_state.rs b/core/lib/multivm/src/versions/vm_m6/bootloader_state.rs index 5dce7e1c6a96..1328d0dd7017 100644 --- a/core/lib/multivm/src/versions/vm_m6/bootloader_state.rs +++ b/core/lib/multivm/src/versions/vm_m6/bootloader_state.rs @@ -5,7 +5,7 @@ use crate::vm_m6::vm_with_bootloader::TX_DESCRIPTION_OFFSET; /// Required to process transactions one by one (since we intercept the VM execution to execute /// transactions and add new ones to the memory on the fly). /// Think about it like a two-pointer scheme: one pointer (`free_tx_index`) tracks the end of the -/// initialized memory; while another (`tx_to_execute`) tracks our progess in this initialized memory. +/// initialized memory; while another (`tx_to_execute`) tracks our progress in this initialized memory. /// This is required since it's possible to push several transactions to the bootloader memory and then /// execute it one by one. /// diff --git a/core/lib/multivm/src/versions/vm_m6/errors/vm_revert_reason.rs b/core/lib/multivm/src/versions/vm_m6/errors/vm_revert_reason.rs index d954f0779535..9025ee9f3781 100644 --- a/core/lib/multivm/src/versions/vm_m6/errors/vm_revert_reason.rs +++ b/core/lib/multivm/src/versions/vm_m6/errors/vm_revert_reason.rs @@ -15,7 +15,7 @@ pub enum VmRevertReasonParsingError { IncorrectStringLength(Vec), } -/// Rich Revert Reasons https://github.com/0xProject/ZEIPs/issues/32 +/// Rich Revert Reasons `https://github.com/0xProject/ZEIPs/issues/32` #[derive(Debug, Clone, PartialEq)] pub enum VmRevertReason { General { diff --git a/core/lib/multivm/src/versions/vm_m6/oracle_tools.rs b/core/lib/multivm/src/versions/vm_m6/oracle_tools.rs index 6650752da27b..4acc2fe68e57 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracle_tools.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracle_tools.rs @@ -13,7 +13,7 @@ use zk_evm_1_3_1::witness_trace::DummyTracer; /// zkEVM requires a bunch of objects implementing given traits to work. /// For example: Storage, Memory, PrecompilerProcessor etc -/// (you can find all these traites in zk_evm crate -> src/abstractions/mod.rs) +/// (you can find all these traits in zk_evm crate -> src/abstractions/mod.rs) /// For each of these traits, we have a local implementation (for example StorageOracle) /// that also support additional features (like rollbacks & history). /// The OracleTools struct, holds all these things together in one place. diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/decommitter.rs b/core/lib/multivm/src/versions/vm_m6/oracles/decommitter.rs index 3917063422aa..48948827c3dd 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/decommitter.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/decommitter.rs @@ -66,7 +66,7 @@ impl DecommitterOracle { } } - /// Adds additional bytecodes. They will take precendent over the bytecodes from storage. + /// Adds additional bytecodes. They will take precedent over the bytecodes from storage. pub fn populate(&mut self, bytecodes: Vec<(U256, Vec)>, timestamp: Timestamp) { for (hash, bytecode) in bytecodes { self.known_bytecodes.insert(hash, bytecode, timestamp); @@ -170,7 +170,7 @@ impl DecommittmentProcessor ) -> (DecommittmentQuery, Option>) { self.decommitment_requests.push((), partial_query.timestamp); // First - check if we didn't fetch this bytecode in the past. - // If we did - we can just return the page that we used before (as the memory is read only). + // If we did - we can just return the page that we used before (as the memory is readonly). if let Some(memory_page) = self .decommitted_code_hashes .inner() diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/bootloader.rs b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/bootloader.rs index fc2a62374dbf..81902f330a55 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/bootloader.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/bootloader.rs @@ -16,7 +16,7 @@ use zk_evm_1_3_1::{ zkevm_opcode_defs::{Opcode, RetOpcode}, }; -/// Tells the VM to end the execution before `ret` from the booloader if there is no panic or revert. +/// Tells the VM to end the execution before `ret` from the bootloader if there is no panic or revert. /// Also, saves the information if this `ret` was caused by "out of gas" panic. #[derive(Debug, Clone, Default)] pub struct BootloaderTracer { diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/call.rs b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/call.rs index 4b61c9fcc15a..f2ddd2762adb 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/call.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/call.rs @@ -95,7 +95,7 @@ impl Tracer for CallTracer { } impl CallTracer { - /// We use parent gas for propery calculation of gas used in the trace. + /// We use parent gas for property calculation of gas used in the trace. /// This method updates parent gas for the current call. fn update_parent_gas(&mut self, state: &VmLocalStateData<'_>, current_call: &mut Call) { let current = state.vm_local_state.callstack.current; diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/utils.rs b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/utils.rs index e2f1652e9b76..87aa81d69db5 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/utils.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/utils.rs @@ -99,7 +99,7 @@ pub(crate) fn get_debug_log( } /// Reads the memory slice represented by the fat pointer. -/// Note, that the fat pointer must point to the accesible memory (i.e. not cleared up yet). +/// Note, that the fat pointer must point to the accessible memory (i.e. not cleared up yet). pub(crate) fn read_pointer( memory: &SimpleMemory, pointer: FatPointer, diff --git a/core/lib/multivm/src/versions/vm_m6/test_utils.rs b/core/lib/multivm/src/versions/vm_m6/test_utils.rs index 8b022c008a78..6cce779362da 100644 --- a/core/lib/multivm/src/versions/vm_m6/test_utils.rs +++ b/core/lib/multivm/src/versions/vm_m6/test_utils.rs @@ -58,7 +58,7 @@ impl PartialEq for ModifiedKeysMap { #[derive(Clone, PartialEq, Debug)] pub struct DecommitterTestInnerState { - /// There is no way to "trully" compare the storage pointer, + /// There is no way to "truly" compare the storage pointer, /// so we just compare the modified keys. This is reasonable enough. pub modified_storage_keys: ModifiedKeysMap, pub known_bytecodes: HistoryRecorder>, H>, @@ -67,7 +67,7 @@ pub struct DecommitterTestInnerState { #[derive(Clone, PartialEq, Debug)] pub struct StorageOracleInnerState { - /// There is no way to "trully" compare the storage pointer, + /// There is no way to "truly" compare the storage pointer, /// so we just compare the modified keys. This is reasonable enough. pub modified_storage_keys: ModifiedKeysMap, diff --git a/core/lib/multivm/src/versions/vm_m6/transaction_data.rs b/core/lib/multivm/src/versions/vm_m6/transaction_data.rs index f41afee3a40f..bdecb9bf4547 100644 --- a/core/lib/multivm/src/versions/vm_m6/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_m6/transaction_data.rs @@ -213,12 +213,12 @@ impl TransactionData { self.reserved_dynamic.len() as u64, ); - let coeficients = OverheadCoeficients::from_tx_type(self.tx_type); + let coefficients = OverheadCoefficients::from_tx_type(self.tx_type); get_amortized_overhead( total_gas_limit, gas_price_per_pubdata, encoded_len, - coeficients, + coefficients, ) } @@ -232,7 +232,7 @@ pub fn derive_overhead( gas_limit: u32, gas_price_per_pubdata: u32, encoded_len: usize, - coeficients: OverheadCoeficients, + coefficients: OverheadCoefficients, ) -> u32 { // Even if the gas limit is greater than the MAX_TX_ERGS_LIMIT, we assume that everything beyond MAX_TX_ERGS_LIMIT // will be spent entirely on publishing bytecodes and so we derive the overhead solely based on the capped value @@ -269,31 +269,31 @@ pub fn derive_overhead( // ); vec![ - (coeficients.ergs_limit_overhead_coeficient + (coefficients.ergs_limit_overhead_coeficient * overhead_for_single_instance_circuits.as_u32() as f64) .floor() as u32, - (coeficients.bootloader_memory_overhead_coeficient * overhead_for_length.as_u32() as f64) + (coefficients.bootloader_memory_overhead_coeficient * overhead_for_length.as_u32() as f64) .floor() as u32, - (coeficients.slot_overhead_coeficient * tx_slot_overhead.as_u32() as f64) as u32, + (coefficients.slot_overhead_coeficient * tx_slot_overhead.as_u32() as f64) as u32, ] .into_iter() .max() .unwrap() } -/// Contains the coeficients with which the overhead for transactions will be calculated. -/// All of the coeficients should be <= 1. There are here to provide a certain "discount" for normal transactions +/// Contains the coefficients with which the overhead for transactions will be calculated. +/// All of the coefficients should be <= 1. There are here to provide a certain "discount" for normal transactions /// at the risk of malicious transactions that may close the block prematurely. -/// IMPORTANT: to perform correct computations, `MAX_TX_ERGS_LIMIT / coeficients.ergs_limit_overhead_coeficient` MUST +/// IMPORTANT: to perform correct computations, `MAX_TX_ERGS_LIMIT / coefficients.ergs_limit_overhead_coeficient` MUST /// result in an integer number #[derive(Debug, Clone, Copy)] -pub struct OverheadCoeficients { +pub struct OverheadCoefficients { slot_overhead_coeficient: f64, bootloader_memory_overhead_coeficient: f64, ergs_limit_overhead_coeficient: f64, } -impl OverheadCoeficients { +impl OverheadCoefficients { // This method ensures that the parameters keep the required invariants fn new_checked( slot_overhead_coeficient: f64, @@ -315,11 +315,11 @@ impl OverheadCoeficients { // L1->L2 do not receive any discounts fn new_l1() -> Self { - OverheadCoeficients::new_checked(1.0, 1.0, 1.0) + OverheadCoefficients::new_checked(1.0, 1.0, 1.0) } fn new_l2() -> Self { - OverheadCoeficients::new_checked( + OverheadCoefficients::new_checked( 1.0, 1.0, // For L2 transactions we allow a certain default discount with regard to the number of ergs. // Multiinstance circuits can in theory be spawned infinite times, while projected future limitations @@ -343,7 +343,7 @@ pub fn get_amortized_overhead( total_gas_limit: u32, gas_per_pubdata_byte_limit: u32, encoded_len: usize, - coeficients: OverheadCoeficients, + coefficients: OverheadCoefficients, ) -> u32 { // Using large U256 type to prevent overflows. let overhead_for_block_gas = U256::from(block_overhead_gas(gas_per_pubdata_byte_limit)); @@ -380,7 +380,7 @@ pub fn get_amortized_overhead( let tx_slot_overhead = { let tx_slot_overhead = ceil_div_u256(overhead_for_block_gas, MAX_TXS_IN_BLOCK.into()).as_u32(); - (coeficients.slot_overhead_coeficient * tx_slot_overhead as f64).floor() as u32 + (coefficients.slot_overhead_coeficient * tx_slot_overhead as f64).floor() as u32 }; // 2. The overhead for occupying the bootloader memory can be derived from encoded_len @@ -391,7 +391,7 @@ pub fn get_amortized_overhead( ) .as_u32(); - (coeficients.bootloader_memory_overhead_coeficient * overhead_for_length as f64).floor() + (coefficients.bootloader_memory_overhead_coeficient * overhead_for_length as f64).floor() as u32 }; @@ -436,7 +436,7 @@ pub fn get_amortized_overhead( let overhead_for_gas = { let numerator = overhead_for_block_gas * total_gas_limit + U256::from(MAX_TX_ERGS_LIMIT); let denominator: U256 = U256::from( - (MAX_TX_ERGS_LIMIT as f64 / coeficients.ergs_limit_overhead_coeficient) as u64, + (MAX_TX_ERGS_LIMIT as f64 / coefficients.ergs_limit_overhead_coeficient) as u64, ) + overhead_for_block_gas; let overhead_for_gas = (numerator - 1) / denominator; @@ -461,7 +461,7 @@ pub fn get_amortized_overhead( MAX_L2_TX_GAS_LIMIT as u32, gas_per_pubdata_byte_limit, encoded_len.as_usize(), - coeficients, + coefficients, ) } else { overhead @@ -484,7 +484,7 @@ mod tests { total_gas_limit: u32, gas_per_pubdata_byte_limit: u32, encoded_len: usize, - coeficients: OverheadCoeficients, + coefficients: OverheadCoefficients, ) -> u32 { let mut left_bound = if MAX_TX_ERGS_LIMIT < total_gas_limit { total_gas_limit - MAX_TX_ERGS_LIMIT @@ -502,7 +502,7 @@ mod tests { total_gas_limit - suggested_overhead, gas_per_pubdata_byte_limit, encoded_len, - coeficients, + coefficients, ); derived_overhead >= suggested_overhead @@ -531,41 +531,41 @@ mod tests { let test_params = |total_gas_limit: u32, gas_per_pubdata: u32, encoded_len: usize, - coeficients: OverheadCoeficients| { + coefficients: OverheadCoefficients| { let result_by_efficient_search = - get_amortized_overhead(total_gas_limit, gas_per_pubdata, encoded_len, coeficients); + get_amortized_overhead(total_gas_limit, gas_per_pubdata, encoded_len, coefficients); let result_by_binary_search = get_maximal_allowed_overhead_bin_search( total_gas_limit, gas_per_pubdata, encoded_len, - coeficients, + coefficients, ); assert_eq!(result_by_efficient_search, result_by_binary_search); }; // Some arbitrary test - test_params(60_000_000, 800, 2900, OverheadCoeficients::new_l2()); + test_params(60_000_000, 800, 2900, OverheadCoefficients::new_l2()); // Very small parameters - test_params(0, 1, 12, OverheadCoeficients::new_l2()); + test_params(0, 1, 12, OverheadCoefficients::new_l2()); // Relatively big parameters let max_tx_overhead = derive_overhead( MAX_TX_ERGS_LIMIT, 5000, 10000, - OverheadCoeficients::new_l2(), + OverheadCoefficients::new_l2(), ); test_params( MAX_TX_ERGS_LIMIT + max_tx_overhead, 5000, 10000, - OverheadCoeficients::new_l2(), + OverheadCoefficients::new_l2(), ); - test_params(115432560, 800, 2900, OverheadCoeficients::new_l1()); + test_params(115432560, 800, 2900, OverheadCoefficients::new_l1()); } #[test] diff --git a/core/lib/multivm/src/versions/vm_m6/vm_instance.rs b/core/lib/multivm/src/versions/vm_m6/vm_instance.rs index cfb5bac806d9..468dd3fc72da 100644 --- a/core/lib/multivm/src/versions/vm_m6/vm_instance.rs +++ b/core/lib/multivm/src/versions/vm_m6/vm_instance.rs @@ -383,7 +383,7 @@ impl VmInstance { } } - /// Removes the latest snapshot without rollbacking to it. + /// Removes the latest snapshot without rolling back to it. /// This function expects that there is at least one snapshot present. pub fn pop_snapshot_no_rollback(&mut self) { self.snapshots.pop().unwrap(); diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/snapshot.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/snapshot.rs index e417a3b9ee69..2c5990928699 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/snapshot.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/snapshot.rs @@ -4,9 +4,9 @@ use zksync_types::H256; pub(crate) struct BootloaderStateSnapshot { /// ID of the next transaction to be executed. pub(crate) tx_to_execute: usize, - /// Stored l2 blocks in bootloader memory + /// Stored L2 blocks in bootloader memory pub(crate) l2_blocks_len: usize, - /// Snapshot of the last l2 block. Only this block could be changed during the rollback + /// Snapshot of the last L2 block. Only this block could be changed during the rollback pub(crate) last_l2_block: L2BlockSnapshot, /// The number of 32-byte words spent on the already included compressed bytecodes. pub(crate) compressed_bytecodes_encoding: usize, @@ -18,6 +18,6 @@ pub(crate) struct BootloaderStateSnapshot { pub(crate) struct L2BlockSnapshot { /// The rolling hash of all the transactions in the miniblock pub(crate) txs_rolling_hash: H256, - /// The number of transactions in the last l2 block + /// The number of transactions in the last L2 block pub(crate) txs_len: usize, } diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/tx.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/tx.rs index c1551dcf6cd4..3bd10e9374b0 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/tx.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/tx.rs @@ -14,7 +14,7 @@ pub(super) struct BootloaderTx { pub(super) refund: u32, /// Gas overhead pub(super) gas_overhead: u32, - /// Gas Limit for this transaction. It can be different from the gaslimit inside the transaction + /// Gas Limit for this transaction. It can be different from the gas limit inside the transaction pub(super) trusted_gas_limit: U256, /// Offset of the tx in bootloader memory pub(super) offset: usize, diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/constants.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/constants.rs index ef3b09299fd5..0dca7a6ce265 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/constants.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/constants.rs @@ -75,10 +75,10 @@ pub const BLOCK_OVERHEAD_L1_GAS: u32 = 1000000; pub const BLOCK_OVERHEAD_PUBDATA: u32 = BLOCK_OVERHEAD_L1_GAS / L1_GAS_PER_PUBDATA_BYTE; /// VM Hooks are used for communication between bootloader and tracers. -/// The 'type'/'opcode' is put into VM_HOOK_POSITION slot, +/// The 'type' / 'opcode' is put into VM_HOOK_POSITION slot, /// and VM_HOOKS_PARAMS_COUNT parameters (each 32 bytes) are put in the slots before. /// So the layout looks like this: -/// [param 0][param 1][vmhook opcode] +/// `[param 0][param 1][vmhook opcode]` pub const VM_HOOK_POSITION: u32 = RESULT_SUCCESS_FIRST_SLOT - 1; pub const VM_HOOK_PARAMS_COUNT: u32 = 2; pub const VM_HOOK_PARAMS_START_POSITION: u32 = VM_HOOK_POSITION - VM_HOOK_PARAMS_COUNT; diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/statistics.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/statistics.rs index 48bbd64ecf2e..a49ce2a67464 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/statistics.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/statistics.rs @@ -43,7 +43,7 @@ impl Vm { } } - /// Returns the hashes the bytecodes that have been decommitted by the decomittment processor. + /// Returns the hashes the bytecodes that have been decommitted by the decommitment processor. pub(crate) fn get_used_contracts(&self) -> Vec { self.state .decommittment_processor diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/oracles/decommitter.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/oracles/decommitter.rs index 0f335cabf398..a39be0ba93b9 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/oracles/decommitter.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/oracles/decommitter.rs @@ -70,7 +70,7 @@ impl DecommitterOracle { } } - /// Adds additional bytecodes. They will take precendent over the bytecodes from storage. + /// Adds additional bytecodes. They will take precedent over the bytecodes from storage. pub fn populate(&mut self, bytecodes: Vec<(U256, Vec)>, timestamp: Timestamp) { for (hash, bytecode) in bytecodes { self.known_bytecodes.insert(hash, bytecode, timestamp); @@ -180,7 +180,7 @@ impl DecommittmentProcess > { self.decommitment_requests.push((), partial_query.timestamp); // First - check if we didn't fetch this bytecode in the past. - // If we did - we can just return the page that we used before (as the memory is read only). + // If we did - we can just return the page that we used before (as the memory is readonly). if let Some(memory_page) = self .decommitted_code_hashes .inner() diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tests/tester/inner_state.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/tests/tester/inner_state.rs index c4c6ec05bd7f..5af50ee0d91f 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/tests/tester/inner_state.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/tests/tester/inner_state.rs @@ -35,7 +35,7 @@ impl PartialEq for ModifiedKeysMap { #[derive(Clone, PartialEq, Debug)] pub(crate) struct DecommitterTestInnerState { - /// There is no way to "trully" compare the storage pointer, + /// There is no way to "truly" compare the storage pointer, /// so we just compare the modified keys. This is reasonable enough. pub(crate) modified_storage_keys: ModifiedKeysMap, pub(crate) known_bytecodes: HistoryRecorder>, H>, @@ -44,7 +44,7 @@ pub(crate) struct DecommitterTestInnerState { #[derive(Clone, PartialEq, Debug)] pub(crate) struct StorageOracleInnerState { - /// There is no way to "trully" compare the storage pointer, + /// There is no way to "truly" compare the storage pointer, /// so we just compare the modified keys. This is reasonable enough. pub(crate) modified_storage_keys: ModifiedKeysMap, diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/utils.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/utils.rs index 654c7300e4ad..a9170c5a442a 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/utils.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/utils.rs @@ -109,7 +109,7 @@ pub(crate) fn get_debug_log( } /// Reads the memory slice represented by the fat pointer. -/// Note, that the fat pointer must point to the accesible memory (i.e. not cleared up yet). +/// Note, that the fat pointer must point to the accessible memory (i.e. not cleared up yet). pub(crate) fn read_pointer( memory: &SimpleMemory, pointer: FatPointer, diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/types/internals/transaction_data.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/types/internals/transaction_data.rs index 1b589146a298..1ad2ce0f9774 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/types/internals/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/types/internals/transaction_data.rs @@ -11,7 +11,9 @@ use zksync_types::{ use zksync_utils::address_to_h256; use zksync_utils::{bytecode::hash_bytecode, bytes_to_be_words, h256_to_u256}; -use crate::vm_refunds_enhancement::utils::overhead::{get_amortized_overhead, OverheadCoeficients}; +use crate::vm_refunds_enhancement::utils::overhead::{ + get_amortized_overhead, OverheadCoefficients, +}; /// This structure represents the data that is used by /// the Bootloader to describe the transaction. @@ -212,12 +214,12 @@ impl TransactionData { self.reserved_dynamic.len() as u64, ); - let coeficients = OverheadCoeficients::from_tx_type(self.tx_type); + let coefficients = OverheadCoefficients::from_tx_type(self.tx_type); get_amortized_overhead( total_gas_limit, gas_price_per_pubdata, encoded_len, - coeficients, + coefficients, ) } diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/fee.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/fee.rs index 02ea1c4a5618..cc6081d7a229 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/fee.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/fee.rs @@ -4,7 +4,7 @@ use zksync_utils::ceil_div; use crate::vm_refunds_enhancement::old_vm::utils::eth_price_per_pubdata_byte; -/// Calcluates the amount of gas required to publish one byte of pubdata +/// Calculates the amount of gas required to publish one byte of pubdata pub fn base_fee_to_gas_per_pubdata(l1_gas_price: u64, base_fee: u64) -> u64 { let eth_price_per_pubdata_byte = eth_price_per_pubdata_byte(l1_gas_price); diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/overhead.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/overhead.rs index 671ff0e05722..cce2f2914e33 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/overhead.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/overhead.rs @@ -12,7 +12,7 @@ pub fn derive_overhead( gas_limit: u32, gas_price_per_pubdata: u32, encoded_len: usize, - coeficients: OverheadCoeficients, + coefficients: OverheadCoefficients, ) -> u32 { // Even if the gas limit is greater than the MAX_TX_ERGS_LIMIT, we assume that everything beyond MAX_TX_ERGS_LIMIT // will be spent entirely on publishing bytecodes and so we derive the overhead solely based on the capped value @@ -49,31 +49,31 @@ pub fn derive_overhead( // ); vec![ - (coeficients.ergs_limit_overhead_coeficient + (coefficients.ergs_limit_overhead_coeficient * overhead_for_single_instance_circuits.as_u32() as f64) .floor() as u32, - (coeficients.bootloader_memory_overhead_coeficient * overhead_for_length.as_u32() as f64) + (coefficients.bootloader_memory_overhead_coeficient * overhead_for_length.as_u32() as f64) .floor() as u32, - (coeficients.slot_overhead_coeficient * tx_slot_overhead.as_u32() as f64) as u32, + (coefficients.slot_overhead_coeficient * tx_slot_overhead.as_u32() as f64) as u32, ] .into_iter() .max() .unwrap() } -/// Contains the coeficients with which the overhead for transactions will be calculated. -/// All of the coeficients should be <= 1. There are here to provide a certain "discount" for normal transactions +/// Contains the coefficients with which the overhead for transactions will be calculated. +/// All of the coefficients should be <= 1. There are here to provide a certain "discount" for normal transactions /// at the risk of malicious transactions that may close the block prematurely. -/// IMPORTANT: to perform correct computations, `MAX_TX_ERGS_LIMIT / coeficients.ergs_limit_overhead_coeficient` MUST +/// IMPORTANT: to perform correct computations, `MAX_TX_ERGS_LIMIT / coefficients.ergs_limit_overhead_coeficient` MUST /// result in an integer number #[derive(Debug, Clone, Copy)] -pub struct OverheadCoeficients { +pub struct OverheadCoefficients { slot_overhead_coeficient: f64, bootloader_memory_overhead_coeficient: f64, ergs_limit_overhead_coeficient: f64, } -impl OverheadCoeficients { +impl OverheadCoefficients { // This method ensures that the parameters keep the required invariants fn new_checked( slot_overhead_coeficient: f64, @@ -95,11 +95,11 @@ impl OverheadCoeficients { // L1->L2 do not receive any discounts fn new_l1() -> Self { - OverheadCoeficients::new_checked(1.0, 1.0, 1.0) + OverheadCoefficients::new_checked(1.0, 1.0, 1.0) } fn new_l2() -> Self { - OverheadCoeficients::new_checked( + OverheadCoefficients::new_checked( 1.0, 1.0, // For L2 transactions we allow a certain default discount with regard to the number of ergs. // Multiinstance circuits can in theory be spawned infinite times, while projected future limitations @@ -109,7 +109,7 @@ impl OverheadCoeficients { ) } - /// Return the coeficients for the given transaction type + /// Return the coefficients for the given transaction type pub fn from_tx_type(tx_type: u8) -> Self { if is_l1_tx_type(tx_type) { Self::new_l1() @@ -124,7 +124,7 @@ pub(crate) fn get_amortized_overhead( total_gas_limit: u32, gas_per_pubdata_byte_limit: u32, encoded_len: usize, - coeficients: OverheadCoeficients, + coefficients: OverheadCoefficients, ) -> u32 { // Using large U256 type to prevent overflows. let overhead_for_block_gas = U256::from(block_overhead_gas(gas_per_pubdata_byte_limit)); @@ -161,7 +161,7 @@ pub(crate) fn get_amortized_overhead( let tx_slot_overhead = { let tx_slot_overhead = ceil_div_u256(overhead_for_block_gas, MAX_TXS_IN_BLOCK.into()).as_u32(); - (coeficients.slot_overhead_coeficient * tx_slot_overhead as f64).floor() as u32 + (coefficients.slot_overhead_coeficient * tx_slot_overhead as f64).floor() as u32 }; // 2. The overhead for occupying the bootloader memory can be derived from encoded_len @@ -172,7 +172,7 @@ pub(crate) fn get_amortized_overhead( ) .as_u32(); - (coeficients.bootloader_memory_overhead_coeficient * overhead_for_length as f64).floor() + (coefficients.bootloader_memory_overhead_coeficient * overhead_for_length as f64).floor() as u32 }; @@ -217,7 +217,7 @@ pub(crate) fn get_amortized_overhead( let overhead_for_gas = { let numerator = overhead_for_block_gas * total_gas_limit + U256::from(MAX_TX_ERGS_LIMIT); let denominator: U256 = U256::from( - (MAX_TX_ERGS_LIMIT as f64 / coeficients.ergs_limit_overhead_coeficient) as u64, + (MAX_TX_ERGS_LIMIT as f64 / coefficients.ergs_limit_overhead_coeficient) as u64, ) + overhead_for_block_gas; let overhead_for_gas = (numerator - 1) / denominator; @@ -242,7 +242,7 @@ pub(crate) fn get_amortized_overhead( MAX_L2_TX_GAS_LIMIT as u32, gas_per_pubdata_byte_limit, encoded_len.as_usize(), - coeficients, + coefficients, ) } else { overhead @@ -263,7 +263,7 @@ mod tests { total_gas_limit: u32, gas_per_pubdata_byte_limit: u32, encoded_len: usize, - coeficients: OverheadCoeficients, + coefficients: OverheadCoefficients, ) -> u32 { let mut left_bound = if MAX_TX_ERGS_LIMIT < total_gas_limit { total_gas_limit - MAX_TX_ERGS_LIMIT @@ -281,7 +281,7 @@ mod tests { total_gas_limit - suggested_overhead, gas_per_pubdata_byte_limit, encoded_len, - coeficients, + coefficients, ); derived_overhead >= suggested_overhead @@ -310,40 +310,40 @@ mod tests { let test_params = |total_gas_limit: u32, gas_per_pubdata: u32, encoded_len: usize, - coeficients: OverheadCoeficients| { + coefficients: OverheadCoefficients| { let result_by_efficient_search = - get_amortized_overhead(total_gas_limit, gas_per_pubdata, encoded_len, coeficients); + get_amortized_overhead(total_gas_limit, gas_per_pubdata, encoded_len, coefficients); let result_by_binary_search = get_maximal_allowed_overhead_bin_search( total_gas_limit, gas_per_pubdata, encoded_len, - coeficients, + coefficients, ); assert_eq!(result_by_efficient_search, result_by_binary_search); }; // Some arbitrary test - test_params(60_000_000, 800, 2900, OverheadCoeficients::new_l2()); + test_params(60_000_000, 800, 2900, OverheadCoefficients::new_l2()); // Very small parameters - test_params(0, 1, 12, OverheadCoeficients::new_l2()); + test_params(0, 1, 12, OverheadCoefficients::new_l2()); // Relatively big parameters let max_tx_overhead = derive_overhead( MAX_TX_ERGS_LIMIT, 5000, 10000, - OverheadCoeficients::new_l2(), + OverheadCoefficients::new_l2(), ); test_params( MAX_TX_ERGS_LIMIT + max_tx_overhead, 5000, 10000, - OverheadCoeficients::new_l2(), + OverheadCoefficients::new_l2(), ); - test_params(115432560, 800, 2900, OverheadCoeficients::new_l1()); + test_params(115432560, 800, 2900, OverheadCoefficients::new_l1()); } } diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/vm.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/vm.rs index 4056d709a9bb..11eea1206a81 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/vm.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/vm.rs @@ -131,7 +131,7 @@ impl VmInterface for Vm { } } -/// Methods of vm, which required some history manipullations +/// Methods of vm, which required some history manipulations impl VmInterfaceHistoryEnabled for Vm { /// Create snapshot of current vm state and push it into the memory fn make_snapshot(&mut self) { diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/snapshot.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/snapshot.rs index e417a3b9ee69..2c5990928699 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/snapshot.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/snapshot.rs @@ -4,9 +4,9 @@ use zksync_types::H256; pub(crate) struct BootloaderStateSnapshot { /// ID of the next transaction to be executed. pub(crate) tx_to_execute: usize, - /// Stored l2 blocks in bootloader memory + /// Stored L2 blocks in bootloader memory pub(crate) l2_blocks_len: usize, - /// Snapshot of the last l2 block. Only this block could be changed during the rollback + /// Snapshot of the last L2 block. Only this block could be changed during the rollback pub(crate) last_l2_block: L2BlockSnapshot, /// The number of 32-byte words spent on the already included compressed bytecodes. pub(crate) compressed_bytecodes_encoding: usize, @@ -18,6 +18,6 @@ pub(crate) struct BootloaderStateSnapshot { pub(crate) struct L2BlockSnapshot { /// The rolling hash of all the transactions in the miniblock pub(crate) txs_rolling_hash: H256, - /// The number of transactions in the last l2 block + /// The number of transactions in the last L2 block pub(crate) txs_len: usize, } diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/tx.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/tx.rs index 73825312b5e1..3b53c918fda6 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/tx.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/tx.rs @@ -14,7 +14,7 @@ pub(super) struct BootloaderTx { pub(super) refund: u32, /// Gas overhead pub(super) gas_overhead: u32, - /// Gas Limit for this transaction. It can be different from the gaslimit inside the transaction + /// Gas Limit for this transaction. It can be different from the gas limit inside the transaction pub(super) trusted_gas_limit: U256, /// Offset of the tx in bootloader memory pub(super) offset: usize, diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/constants.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/constants.rs index ed462581cb79..5535be903812 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/constants.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/constants.rs @@ -75,10 +75,10 @@ pub(crate) const BLOCK_OVERHEAD_L1_GAS: u32 = 1000000; pub const BLOCK_OVERHEAD_PUBDATA: u32 = BLOCK_OVERHEAD_L1_GAS / L1_GAS_PER_PUBDATA_BYTE; /// VM Hooks are used for communication between bootloader and tracers. -/// The 'type'/'opcode' is put into VM_HOOK_POSITION slot, +/// The 'type' / 'opcode' is put into VM_HOOK_POSITION slot, /// and VM_HOOKS_PARAMS_COUNT parameters (each 32 bytes) are put in the slots before. /// So the layout looks like this: -/// [param 0][param 1][vmhook opcode] +/// `[param 0][param 1][vmhook opcode]` pub const VM_HOOK_POSITION: u32 = RESULT_SUCCESS_FIRST_SLOT - 1; pub const VM_HOOK_PARAMS_COUNT: u32 = 2; pub const VM_HOOK_PARAMS_START_POSITION: u32 = VM_HOOK_POSITION - VM_HOOK_PARAMS_COUNT; diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/statistics.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/statistics.rs index 14570f154539..dd4a5ad55b21 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/statistics.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/statistics.rs @@ -43,7 +43,7 @@ impl Vm { } } - /// Returns the hashes the bytecodes that have been decommitted by the decomittment processor. + /// Returns the hashes the bytecodes that have been decommitted by the decommitment processor. pub(crate) fn get_used_contracts(&self) -> Vec { self.state .decommittment_processor diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/oracles/decommitter.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/oracles/decommitter.rs index 050b244736ff..12c3ffd403d0 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/oracles/decommitter.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/oracles/decommitter.rs @@ -70,7 +70,7 @@ impl DecommitterOracle { } } - /// Adds additional bytecodes. They will take precendent over the bytecodes from storage. + /// Adds additional bytecodes. They will take precedent over the bytecodes from storage. pub fn populate(&mut self, bytecodes: Vec<(U256, Vec)>, timestamp: Timestamp) { for (hash, bytecode) in bytecodes { self.known_bytecodes.insert(hash, bytecode, timestamp); @@ -180,7 +180,7 @@ impl DecommittmentProcess > { self.decommitment_requests.push((), partial_query.timestamp); // First - check if we didn't fetch this bytecode in the past. - // If we did - we can just return the page that we used before (as the memory is read only). + // If we did - we can just return the page that we used before (as the memory is readonly). if let Some(memory_page) = self .decommitted_code_hashes .inner() diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/tester/inner_state.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/tester/inner_state.rs index 8105ca244d34..83ad0b9044b5 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/tester/inner_state.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/tester/inner_state.rs @@ -36,7 +36,7 @@ impl PartialEq for ModifiedKeysMap { #[derive(Clone, PartialEq, Debug)] pub(crate) struct DecommitterTestInnerState { - /// There is no way to "trully" compare the storage pointer, + /// There is no way to "truly" compare the storage pointer, /// so we just compare the modified keys. This is reasonable enough. pub(crate) modified_storage_keys: ModifiedKeysMap, pub(crate) known_bytecodes: HistoryRecorder>, H>, @@ -45,7 +45,7 @@ pub(crate) struct DecommitterTestInnerState { #[derive(Clone, PartialEq, Debug)] pub(crate) struct StorageOracleInnerState { - /// There is no way to "trully" compare the storage pointer, + /// There is no way to "truly" compare the storage pointer, /// so we just compare the modified keys. This is reasonable enough. pub(crate) modified_storage_keys: ModifiedKeysMap, diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/utils.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/utils.rs index abf8714bbe9a..0b6c7ebcfa8a 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/utils.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/utils.rs @@ -106,7 +106,7 @@ pub(crate) fn get_debug_log( } /// Reads the memory slice represented by the fat pointer. -/// Note, that the fat pointer must point to the accesible memory (i.e. not cleared up yet). +/// Note, that the fat pointer must point to the accessible memory (i.e. not cleared up yet). pub(crate) fn read_pointer( memory: &SimpleMemory, pointer: FatPointer, diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/types/internals/transaction_data.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/types/internals/transaction_data.rs index 55f942d99283..add3d829d800 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/types/internals/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/types/internals/transaction_data.rs @@ -11,7 +11,7 @@ use zksync_types::{ use zksync_utils::address_to_h256; use zksync_utils::{bytecode::hash_bytecode, bytes_to_be_words, h256_to_u256}; -use crate::vm_virtual_blocks::utils::overhead::{get_amortized_overhead, OverheadCoeficients}; +use crate::vm_virtual_blocks::utils::overhead::{get_amortized_overhead, OverheadCoefficients}; /// This structure represents the data that is used by /// the Bootloader to describe the transaction. @@ -212,12 +212,12 @@ impl TransactionData { self.reserved_dynamic.len() as u64, ); - let coeficients = OverheadCoeficients::from_tx_type(self.tx_type); + let coefficients = OverheadCoefficients::from_tx_type(self.tx_type); get_amortized_overhead( total_gas_limit, gas_price_per_pubdata, encoded_len, - coeficients, + coefficients, ) } diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/utils/fee.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/utils/fee.rs index d4808e91bf4f..6753e8197813 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/utils/fee.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/utils/fee.rs @@ -4,7 +4,7 @@ use zksync_utils::ceil_div; use crate::vm_virtual_blocks::old_vm::utils::eth_price_per_pubdata_byte; -/// Calcluates the amount of gas required to publish one byte of pubdata +/// Calculates the amount of gas required to publish one byte of pubdata pub fn base_fee_to_gas_per_pubdata(l1_gas_price: u64, base_fee: u64) -> u64 { let eth_price_per_pubdata_byte = eth_price_per_pubdata_byte(l1_gas_price); diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/utils/overhead.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/utils/overhead.rs index 85446534a2ec..59b54888ee18 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/utils/overhead.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/utils/overhead.rs @@ -12,7 +12,7 @@ pub fn derive_overhead( gas_limit: u32, gas_price_per_pubdata: u32, encoded_len: usize, - coeficients: OverheadCoeficients, + coefficients: OverheadCoefficients, ) -> u32 { // Even if the gas limit is greater than the MAX_TX_ERGS_LIMIT, we assume that everything beyond MAX_TX_ERGS_LIMIT // will be spent entirely on publishing bytecodes and so we derive the overhead solely based on the capped value @@ -49,31 +49,31 @@ pub fn derive_overhead( // ); vec![ - (coeficients.ergs_limit_overhead_coeficient + (coefficients.ergs_limit_overhead_coeficient * overhead_for_single_instance_circuits.as_u32() as f64) .floor() as u32, - (coeficients.bootloader_memory_overhead_coeficient * overhead_for_length.as_u32() as f64) + (coefficients.bootloader_memory_overhead_coeficient * overhead_for_length.as_u32() as f64) .floor() as u32, - (coeficients.slot_overhead_coeficient * tx_slot_overhead.as_u32() as f64) as u32, + (coefficients.slot_overhead_coeficient * tx_slot_overhead.as_u32() as f64) as u32, ] .into_iter() .max() .unwrap() } -/// Contains the coeficients with which the overhead for transactions will be calculated. -/// All of the coeficients should be <= 1. There are here to provide a certain "discount" for normal transactions +/// Contains the coefficients with which the overhead for transactions will be calculated. +/// All of the coefficients should be <= 1. There are here to provide a certain "discount" for normal transactions /// at the risk of malicious transactions that may close the block prematurely. -/// IMPORTANT: to perform correct computations, `MAX_TX_ERGS_LIMIT / coeficients.ergs_limit_overhead_coeficient` MUST +/// IMPORTANT: to perform correct computations, `MAX_TX_ERGS_LIMIT / coefficients.ergs_limit_overhead_coeficient` MUST /// result in an integer number #[derive(Debug, Clone, Copy)] -pub struct OverheadCoeficients { +pub struct OverheadCoefficients { slot_overhead_coeficient: f64, bootloader_memory_overhead_coeficient: f64, ergs_limit_overhead_coeficient: f64, } -impl OverheadCoeficients { +impl OverheadCoefficients { // This method ensures that the parameters keep the required invariants fn new_checked( slot_overhead_coeficient: f64, @@ -95,11 +95,11 @@ impl OverheadCoeficients { // L1->L2 do not receive any discounts fn new_l1() -> Self { - OverheadCoeficients::new_checked(1.0, 1.0, 1.0) + OverheadCoefficients::new_checked(1.0, 1.0, 1.0) } fn new_l2() -> Self { - OverheadCoeficients::new_checked( + OverheadCoefficients::new_checked( 1.0, 1.0, // For L2 transactions we allow a certain default discount with regard to the number of ergs. // Multiinstance circuits can in theory be spawned infinite times, while projected future limitations @@ -109,7 +109,7 @@ impl OverheadCoeficients { ) } - /// Return the coeficients for the given transaction type + /// Return the coefficients for the given transaction type pub fn from_tx_type(tx_type: u8) -> Self { if is_l1_tx_type(tx_type) { Self::new_l1() @@ -124,7 +124,7 @@ pub(crate) fn get_amortized_overhead( total_gas_limit: u32, gas_per_pubdata_byte_limit: u32, encoded_len: usize, - coeficients: OverheadCoeficients, + coefficients: OverheadCoefficients, ) -> u32 { // Using large U256 type to prevent overflows. let overhead_for_block_gas = U256::from(block_overhead_gas(gas_per_pubdata_byte_limit)); @@ -161,7 +161,7 @@ pub(crate) fn get_amortized_overhead( let tx_slot_overhead = { let tx_slot_overhead = ceil_div_u256(overhead_for_block_gas, MAX_TXS_IN_BLOCK.into()).as_u32(); - (coeficients.slot_overhead_coeficient * tx_slot_overhead as f64).floor() as u32 + (coefficients.slot_overhead_coeficient * tx_slot_overhead as f64).floor() as u32 }; // 2. The overhead for occupying the bootloader memory can be derived from encoded_len @@ -172,7 +172,7 @@ pub(crate) fn get_amortized_overhead( ) .as_u32(); - (coeficients.bootloader_memory_overhead_coeficient * overhead_for_length as f64).floor() + (coefficients.bootloader_memory_overhead_coeficient * overhead_for_length as f64).floor() as u32 }; @@ -217,7 +217,7 @@ pub(crate) fn get_amortized_overhead( let overhead_for_gas = { let numerator = overhead_for_block_gas * total_gas_limit + U256::from(MAX_TX_ERGS_LIMIT); let denominator: U256 = U256::from( - (MAX_TX_ERGS_LIMIT as f64 / coeficients.ergs_limit_overhead_coeficient) as u64, + (MAX_TX_ERGS_LIMIT as f64 / coefficients.ergs_limit_overhead_coeficient) as u64, ) + overhead_for_block_gas; let overhead_for_gas = (numerator - 1) / denominator; @@ -242,7 +242,7 @@ pub(crate) fn get_amortized_overhead( MAX_L2_TX_GAS_LIMIT as u32, gas_per_pubdata_byte_limit, encoded_len.as_usize(), - coeficients, + coefficients, ) } else { overhead @@ -263,7 +263,7 @@ mod tests { total_gas_limit: u32, gas_per_pubdata_byte_limit: u32, encoded_len: usize, - coeficients: OverheadCoeficients, + coefficients: OverheadCoefficients, ) -> u32 { let mut left_bound = if MAX_TX_ERGS_LIMIT < total_gas_limit { total_gas_limit - MAX_TX_ERGS_LIMIT @@ -281,7 +281,7 @@ mod tests { total_gas_limit - suggested_overhead, gas_per_pubdata_byte_limit, encoded_len, - coeficients, + coefficients, ); derived_overhead >= suggested_overhead @@ -310,40 +310,40 @@ mod tests { let test_params = |total_gas_limit: u32, gas_per_pubdata: u32, encoded_len: usize, - coeficients: OverheadCoeficients| { + coefficients: OverheadCoefficients| { let result_by_efficient_search = - get_amortized_overhead(total_gas_limit, gas_per_pubdata, encoded_len, coeficients); + get_amortized_overhead(total_gas_limit, gas_per_pubdata, encoded_len, coefficients); let result_by_binary_search = get_maximal_allowed_overhead_bin_search( total_gas_limit, gas_per_pubdata, encoded_len, - coeficients, + coefficients, ); assert_eq!(result_by_efficient_search, result_by_binary_search); }; // Some arbitrary test - test_params(60_000_000, 800, 2900, OverheadCoeficients::new_l2()); + test_params(60_000_000, 800, 2900, OverheadCoefficients::new_l2()); // Very small parameters - test_params(0, 1, 12, OverheadCoeficients::new_l2()); + test_params(0, 1, 12, OverheadCoefficients::new_l2()); // Relatively big parameters let max_tx_overhead = derive_overhead( MAX_TX_ERGS_LIMIT, 5000, 10000, - OverheadCoeficients::new_l2(), + OverheadCoefficients::new_l2(), ); test_params( MAX_TX_ERGS_LIMIT + max_tx_overhead, 5000, 10000, - OverheadCoeficients::new_l2(), + OverheadCoefficients::new_l2(), ); - test_params(115432560, 800, 2900, OverheadCoeficients::new_l1()); + test_params(115432560, 800, 2900, OverheadCoefficients::new_l1()); } } diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/vm.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/vm.rs index e96c326b2191..4110825a260e 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/vm.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/vm.rs @@ -132,7 +132,7 @@ impl VmInterface for Vm { } } -/// Methods of vm, which required some history manipullations +/// Methods of vm, which required some history manipulations impl VmInterfaceHistoryEnabled for Vm { /// Create snapshot of current vm state and push it into the memory fn make_snapshot(&mut self) { diff --git a/core/lib/state/src/cache/metrics.rs b/core/lib/state/src/cache/metrics.rs index 7198d4339475..13bc8c94aa97 100644 --- a/core/lib/state/src/cache/metrics.rs +++ b/core/lib/state/src/cache/metrics.rs @@ -18,7 +18,7 @@ pub(super) enum RequestOutcome { Miss, } -/// Buckets for small latencies: from 10ns to 1ms. +/// Buckets for small latencies: from 10 ns to 1 ms. const SMALL_LATENCIES: Buckets = Buckets::values(&[ 1e-8, 2.5e-8, 5e-8, 1e-7, 2.5e-7, 5e-7, 1e-6, 2.5e-6, 5e-6, 1e-5, 2.5e-5, 5e-5, 1e-4, 1e-3, ]); diff --git a/core/lib/state/src/in_memory.rs b/core/lib/state/src/in_memory.rs index 87a26b238f23..fcb69affea85 100644 --- a/core/lib/state/src/in_memory.rs +++ b/core/lib/state/src/in_memory.rs @@ -8,7 +8,7 @@ use zksync_types::{ }; use zksync_utils::u256_to_h256; -/// Network ID we use by defailt for in memory storage. +/// Network ID we use by default for in memory storage. pub const IN_MEMORY_STORAGE_DEFAULT_NETWORK_ID: u32 = 270; /// In-memory storage. diff --git a/core/lib/state/src/lib.rs b/core/lib/state/src/lib.rs index c943e48dbc1d..3d54967c9ad6 100644 --- a/core/lib/state/src/lib.rs +++ b/core/lib/state/src/lib.rs @@ -43,7 +43,7 @@ pub trait ReadStorage: fmt::Debug { /// Checks whether a write to this storage at the specified `key` would be an initial write. /// Roughly speaking, this is the case when the storage doesn't contain `key`, although - /// in case of mutable storages, the caveats apply (a write to a key that is present + /// in case of mutable storage, the caveats apply (a write to a key that is present /// in the storage but was not committed is still an initial write). fn is_write_initial(&mut self, key: &StorageKey) -> bool; diff --git a/core/lib/storage/src/db.rs b/core/lib/storage/src/db.rs index 3280183abf99..617d14d272d0 100644 --- a/core/lib/storage/src/db.rs +++ b/core/lib/storage/src/db.rs @@ -553,7 +553,7 @@ impl RocksDB { } impl RocksDB<()> { - /// Awaits termination of all running rocksdb instances. + /// Awaits termination of all running RocksDB instances. /// /// This method is blocking and should be wrapped in `spawn_blocking(_)` if run in the async context. pub fn await_rocksdb_termination() { @@ -570,7 +570,7 @@ impl RocksDB<()> { } } -/// Empty struct used to register rocksdb instance +/// Empty struct used to register RocksDB instance #[derive(Debug)] struct RegistryEntry; diff --git a/core/lib/storage/src/metrics.rs b/core/lib/storage/src/metrics.rs index 0c26bd749d5a..928e735a30ca 100644 --- a/core/lib/storage/src/metrics.rs +++ b/core/lib/storage/src/metrics.rs @@ -96,7 +96,7 @@ pub(crate) struct RocksdbSizeMetrics { pub live_data_size: Family>, /// Total size of all SST files in the column family of a RocksDB instance. pub total_sst_size: Family>, - /// Total size of all mem tables in the column family of a RocksDB instance. + /// Total size of all memory tables in the column family of a RocksDB instance. pub total_mem_table_size: Family>, /// Total size of block cache in the column family of a RocksDB instance. pub block_cache_size: Family>, diff --git a/core/lib/types/src/api/mod.rs b/core/lib/types/src/api/mod.rs index f0cc71328313..24ac74ab335e 100644 --- a/core/lib/types/src/api/mod.rs +++ b/core/lib/types/src/api/mod.rs @@ -89,7 +89,7 @@ impl<'de> Deserialize<'de> for BlockNumber { } } -/// Block unified identifier in terms of ZKSync +/// Block unified identifier in terms of zkSync /// /// This is an utility structure that cannot be (de)serialized, it has to be created manually. /// The reason is because Web3 API provides multiple methods for referring block either by hash or number, @@ -271,7 +271,7 @@ pub struct Block { /// Hash of the uncles #[serde(rename = "sha3Uncles")] pub uncles_hash: H256, - /// Miner/author's address + /// Miner / author's address #[serde(rename = "miner", default, deserialize_with = "null_to_default")] pub author: H160, /// State root hash @@ -463,7 +463,7 @@ pub struct Transaction { pub from: Option

, /// Recipient (None when contract creation) pub to: Option
, - /// Transfered value + /// Transferred value pub value: U256, /// Gas Price #[serde(rename = "gasPrice")] diff --git a/core/lib/types/src/block.rs b/core/lib/types/src/block.rs index 762733f8e214..80a4d131e21b 100644 --- a/core/lib/types/src/block.rs +++ b/core/lib/types/src/block.rs @@ -64,7 +64,7 @@ pub struct L1BatchHeader { /// The L2 gas price that the operator agrees on. pub l2_fair_gas_price: u64, pub base_system_contracts_hashes: BaseSystemContractsHashes, - /// System logs are those emitted as part of the Vm excecution. + /// System logs are those emitted as part of the Vm execution. pub system_logs: Vec, /// Version of protocol used for the L1 batch. pub protocol_version: Option, diff --git a/core/lib/types/src/storage/writes/mod.rs b/core/lib/types/src/storage/writes/mod.rs index 6a17afb7d15f..54393f417856 100644 --- a/core/lib/types/src/storage/writes/mod.rs +++ b/core/lib/types/src/storage/writes/mod.rs @@ -41,7 +41,7 @@ pub struct RepeatedStorageWrite { #[derive(Clone, Debug, Deserialize, Serialize, Default, Eq, PartialEq)] pub struct StateDiffRecord { - /// address state diff occured at + /// address state diff occurred at pub address: Address, /// storage slot key updated pub key: U256, @@ -115,7 +115,7 @@ impl StateDiffRecord { } } - /// compression follows the following algo: + /// compression follows the following algorithm: /// 1. if repeated write: /// entry <- enumeration_index || compressed value /// 2. if initial write: diff --git a/core/lib/types/src/transaction_request.rs b/core/lib/types/src/transaction_request.rs index 3c450e77c89b..85194902e323 100644 --- a/core/lib/types/src/transaction_request.rs +++ b/core/lib/types/src/transaction_request.rs @@ -60,7 +60,7 @@ pub struct CallRequest { /// Access list #[serde(default, skip_serializing_if = "Option::is_none")] pub access_list: Option, - /// Eip712 meta + /// EIP712 meta #[serde(default, skip_serializing_if = "Option::is_none")] pub eip712_meta: Option, } @@ -97,7 +97,7 @@ impl CallRequestBuilder { self } - /// Set transfered value (None for no transfer) + /// Set transferred, value (None for no transfer) pub fn gas_price(mut self, gas_price: U256) -> Self { self.call_request.gas_price = Some(gas_price); self @@ -113,7 +113,7 @@ impl CallRequestBuilder { self } - /// Set transfered value (None for no transfer) + /// Set transferred, value (None for no transfer) pub fn value(mut self, value: U256) -> Self { self.call_request.value = Some(value); self @@ -177,7 +177,7 @@ pub enum SerializationTransactionError { AccessListsNotSupported, #[error("nonce has max value")] TooBigNonce, - /// TooHighGas is a sanity error to avoid extremely big numbers specified + /// Sanity check error to avoid extremely big numbers specified /// to gas and pubdata price. #[error("{0}")] TooHighGas(String), diff --git a/core/lib/types/src/tx/primitives/eip712_signature/typed_structure.rs b/core/lib/types/src/tx/primitives/eip712_signature/typed_structure.rs index 999afbbe604d..28e9d27f0a68 100644 --- a/core/lib/types/src/tx/primitives/eip712_signature/typed_structure.rs +++ b/core/lib/types/src/tx/primitives/eip712_signature/typed_structure.rs @@ -29,7 +29,7 @@ impl EncodedStructureMember { } } - /// Encodes the structure as `name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")". + /// Encodes the structure as `name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")"`. pub fn get_encoded_type(&self) -> String { let mut encoded_type = String::new(); encoded_type.push_str(&self.member_type); diff --git a/core/lib/types/src/tx/primitives/packed_eth_signature.rs b/core/lib/types/src/tx/primitives/packed_eth_signature.rs index b249d151ef56..32564829ad88 100644 --- a/core/lib/types/src/tx/primitives/packed_eth_signature.rs +++ b/core/lib/types/src/tx/primitives/packed_eth_signature.rs @@ -14,7 +14,7 @@ use thiserror::Error; use zksync_basic_types::{Address, H256}; use zksync_utils::ZeroPrefixHexSerde; -/// Struct used for working with ethereum signatures created using eth_sign (using geth, ethers.js, etc) +/// Struct used for working with Ethereum signatures created using eth_sign (using geth, ethers.js, etc) /// message is serialized as 65 bytes long `0x` prefixed string. /// /// Some notes on implementation of methods of this structure: @@ -66,7 +66,7 @@ impl PackedEthSignature { Ok(PackedEthSignature(ETHSignature::from(signature))) } - /// Signs message using ethereum private key, results are identical to signature created + /// Signs message using Ethereum private key, results are identical to signature created /// using `geth`, `ethers.js`, etc. No hashing and prefixes required. pub fn sign(private_key: &H256, msg: &[u8]) -> Result { let signed_bytes = Self::message_to_signed_bytes(msg); @@ -85,7 +85,7 @@ impl PackedEthSignature { Ok(PackedEthSignature(signature)) } - /// Signs typed struct using ethereum private key by EIP-712 signature standard. + /// Signs typed struct using Ethereum private key by EIP-712 signature standard. /// Result of this function is the equivalent of RPC calling `eth_signTypedData`. pub fn sign_typed_data( private_key: &H256, @@ -115,7 +115,7 @@ impl PackedEthSignature { msg.keccak256().into() } - /// Checks signature and returns ethereum address of the signer. + /// Checks signature and returns Ethereum address of the signer. /// message should be the same message that was passed to `eth.sign`(or similar) method /// as argument. No hashing and prefixes required. pub fn signature_recover_signer( diff --git a/core/lib/utils/src/bytecode.rs b/core/lib/utils/src/bytecode.rs index 66101da4f5bc..4a9d1cd2475b 100644 --- a/core/lib/utils/src/bytecode.rs +++ b/core/lib/utils/src/bytecode.rs @@ -27,7 +27,7 @@ pub enum FailedToCompressBytecodeError { InvalidBytecode(#[from] InvalidBytecodeError), } -/// Implelements a simple compression algorithm for the bytecode. +/// Implements, a simple compression algorithm for the bytecode. pub fn compress_bytecode(code: &[u8]) -> Result, FailedToCompressBytecodeError> { validate_bytecode(code)?; diff --git a/core/lib/utils/src/convert.rs b/core/lib/utils/src/convert.rs index bcaa6c68f1ff..973b28cc613a 100644 --- a/core/lib/utils/src/convert.rs +++ b/core/lib/utils/src/convert.rs @@ -154,7 +154,7 @@ pub fn h256_to_u32(value: H256) -> u32 { u32::from_be_bytes(be_u32_bytes) } -/// Converts u32 into the h256 as BE bytes +/// Converts u32 into the H256 as BE bytes pub fn u32_to_h256(value: u32) -> H256 { let mut result = [0u8; 32]; result[28..].copy_from_slice(&value.to_be_bytes()); diff --git a/core/lib/vlog/src/lib.rs b/core/lib/vlog/src/lib.rs index 173770beecea..2c6702ede960 100644 --- a/core/lib/vlog/src/lib.rs +++ b/core/lib/vlog/src/lib.rs @@ -153,7 +153,7 @@ pub fn log_format_from_env() -> LogFormat { } /// Loads the Sentry URL from the environment variable according to the existing zkSync configuration scheme. -/// If the environemnt value is present but the value is `unset`, `None` will be returned for compatibility with the +/// If the environment value is present but the value is `unset`, `None` will be returned for compatibility with the /// existing configuration setup. /// /// This is a deprecated function existing for compatibility with the old configuration scheme. diff --git a/core/lib/zksync_core/src/api_server/contract_verification/api_decl.rs b/core/lib/zksync_core/src/api_server/contract_verification/api_decl.rs index 1b7d07b42767..5f9730d458e1 100644 --- a/core/lib/zksync_core/src/api_server/contract_verification/api_decl.rs +++ b/core/lib/zksync_core/src/api_server/contract_verification/api_decl.rs @@ -19,7 +19,7 @@ impl RestApi { } } - /// Creates an actix-web `Scope`, which can be mounted to the Http server. + /// Creates an actix-web `Scope`, which can be mounted to the HTTP server. pub fn into_scope(self) -> actix_web::Scope { web::scope("") .app_data(web::Data::new(self)) diff --git a/core/lib/zksync_core/src/api_server/execution_sandbox/execute.rs b/core/lib/zksync_core/src/api_server/execution_sandbox/execute.rs index d1ac41553df2..9621adae2d87 100644 --- a/core/lib/zksync_core/src/api_server/execution_sandbox/execute.rs +++ b/core/lib/zksync_core/src/api_server/execution_sandbox/execute.rs @@ -5,7 +5,7 @@ use tracing::{span, Level}; use multivm::interface::{TxExecutionMode, VmExecutionMode, VmExecutionResultAndLogs, VmInterface}; use multivm::tracers::StorageInvocations; use multivm::vm_latest::constants::ETH_CALL_GAS_LIMIT; -use multivm::MultivmTracer; +use multivm::MultiVMTracer; use zksync_dal::ConnectionPool; use zksync_types::{ diff --git a/core/lib/zksync_core/src/api_server/execution_sandbox/tracers.rs b/core/lib/zksync_core/src/api_server/execution_sandbox/tracers.rs index ac675eee707f..9f987a150da8 100644 --- a/core/lib/zksync_core/src/api_server/execution_sandbox/tracers.rs +++ b/core/lib/zksync_core/src/api_server/execution_sandbox/tracers.rs @@ -1,13 +1,13 @@ use multivm::tracers::CallTracer; use multivm::vm_latest::HistoryMode; -use multivm::{MultiVmTracerPointer, MultivmTracer}; +use multivm::{MultiVMTracer, MultiVmTracerPointer}; use once_cell::sync::OnceCell; use std::sync::Arc; use zksync_state::WriteStorage; use zksync_types::vm_trace::Call; -/// Custom tracers supported by our api +/// Custom tracers supported by our API #[derive(Debug)] pub(crate) enum ApiTracer { CallTracer(Arc>>), diff --git a/core/lib/zksync_core/src/api_server/execution_sandbox/validate.rs b/core/lib/zksync_core/src/api_server/execution_sandbox/validate.rs index 119d6423ba21..4b9e13084efd 100644 --- a/core/lib/zksync_core/src/api_server/execution_sandbox/validate.rs +++ b/core/lib/zksync_core/src/api_server/execution_sandbox/validate.rs @@ -1,5 +1,5 @@ use multivm::interface::{ExecutionResult, VmExecutionMode, VmInterface}; -use multivm::MultivmTracer; +use multivm::MultiVMTracer; use std::collections::HashSet; use multivm::tracers::{ diff --git a/core/lib/zksync_core/src/api_server/tx_sender/mod.rs b/core/lib/zksync_core/src/api_server/tx_sender/mod.rs index b994fbff8824..12c738004151 100644 --- a/core/lib/zksync_core/src/api_server/tx_sender/mod.rs +++ b/core/lib/zksync_core/src/api_server/tx_sender/mod.rs @@ -18,7 +18,7 @@ use multivm::vm_latest::{ constants::{BLOCK_GAS_LIMIT, MAX_PUBDATA_PER_BLOCK}, utils::{ fee::derive_base_fee_and_gas_per_pubdata, - overhead::{derive_overhead, OverheadCoeficients}, + overhead::{derive_overhead, OverheadCoefficients}, }, }; @@ -111,7 +111,7 @@ pub struct ApiContracts { pub(crate) estimate_gas: MultiVMBaseSystemContracts, /// Contracts to be used when performing `eth_call` requests. /// These contracts (mainly, bootloader) normally should be tuned to provide better UX - /// exeprience (e.g. revert messages). + /// experience (e.g. revert messages). pub(crate) eth_call: MultiVMBaseSystemContracts, } @@ -600,7 +600,7 @@ impl TxSender { tx_gas_limit, gas_per_pubdata_byte as u32, tx.encoding_len(), - OverheadCoeficients::from_tx_type(tx.tx_format() as u8), + OverheadCoefficients::from_tx_type(tx.tx_format() as u8), ); match &mut tx.common_data { @@ -832,7 +832,7 @@ impl TxSender { suggested_gas_limit, gas_per_pubdata_byte as u32, tx.encoding_len(), - OverheadCoeficients::from_tx_type(tx.tx_format() as u8), + OverheadCoefficients::from_tx_type(tx.tx_format() as u8), ); let full_gas_limit = diff --git a/core/lib/zksync_core/src/eth_sender/eth_tx_manager.rs b/core/lib/zksync_core/src/eth_sender/eth_tx_manager.rs index da11e9056474..98e75702a4c8 100644 --- a/core/lib/zksync_core/src/eth_sender/eth_tx_manager.rs +++ b/core/lib/zksync_core/src/eth_sender/eth_tx_manager.rs @@ -43,7 +43,7 @@ pub(super) struct L1BlockNumbers { /// The component is responsible for managing sending eth_txs attempts: /// Based on eth_tx queue the component generates new attempt with the minimum possible fee, -/// save it to the database, and send it to ethereum. +/// save it to the database, and send it to Ethereum. /// Based on eth_tx_history queue the component can mark txs as stuck and create the new attempt /// with higher gas price #[derive(Debug)] diff --git a/core/lib/zksync_core/src/eth_sender/metrics.rs b/core/lib/zksync_core/src/eth_sender/metrics.rs index 2f4a225e570e..950ff8bf6f79 100644 --- a/core/lib/zksync_core/src/eth_sender/metrics.rs +++ b/core/lib/zksync_core/src/eth_sender/metrics.rs @@ -78,7 +78,7 @@ pub(super) struct EthSenderMetrics { pub used_priority_fee_per_gas: Histogram, /// Last L1 block observed by the Ethereum sender. pub last_known_l1_block: Gauge, - /// Number of inflight txs produced by the Ethereum sender. + /// Number of in-flight txs produced by the Ethereum sender. pub number_of_inflight_txs: Gauge, #[metrics(buckets = GAS_BUCKETS)] pub l1_gas_used: Family>, diff --git a/core/lib/zksync_core/src/house_keeper/fri_prover_queue_monitor.rs b/core/lib/zksync_core/src/house_keeper/fri_prover_queue_monitor.rs index ba731ede944f..9d3264b679ec 100644 --- a/core/lib/zksync_core/src/house_keeper/fri_prover_queue_monitor.rs +++ b/core/lib/zksync_core/src/house_keeper/fri_prover_queue_monitor.rs @@ -24,7 +24,7 @@ impl FriProverStatsReporter { } } -/// Invoked periodically to push prover queued/inprogress job statistics +/// Invoked periodically to push prover queued/in-progress job statistics #[async_trait] impl PeriodicJob for FriProverStatsReporter { const SERVICE_NAME: &'static str = "FriProverStatsReporter"; diff --git a/core/lib/zksync_core/src/lib.rs b/core/lib/zksync_core/src/lib.rs index d52fd76661f2..3b00d7cffda1 100644 --- a/core/lib/zksync_core/src/lib.rs +++ b/core/lib/zksync_core/src/lib.rs @@ -251,7 +251,7 @@ pub enum Component { WitnessGenerator(Option, AggregationRound), /// Component for housekeeping task such as cleaning blobs from GCS, reporting metrics etc. Housekeeper, - /// Component for exposing API's to prover for providing proof generation data and accepting proofs. + /// Component for exposing APIs to prover for providing proof generation data and accepting proofs. ProofDataHandler, } diff --git a/core/lib/zksync_core/src/metadata_calculator/helpers.rs b/core/lib/zksync_core/src/metadata_calculator/helpers.rs index ffd87b92d164..32f39276a1e5 100644 --- a/core/lib/zksync_core/src/metadata_calculator/helpers.rs +++ b/core/lib/zksync_core/src/metadata_calculator/helpers.rs @@ -42,7 +42,7 @@ impl From for Health { /// /// Async methods provided by this wrapper are not cancel-safe! This is probably not an issue; /// `ZkSyncTree` is only indirectly available via `MetadataCalculator::run()` entrypoint -/// which consumes `self`. That is, if `MetadataCalculator::run()` is cancelled (which we don't currently do, +/// which consumes `self`. That is, if `MetadataCalculator::run()` is canceled (which we don't currently do, /// at least not explicitly), all `MetadataCalculator` data including `ZkSyncTree` is discarded. /// In the unlikely case you get a "`ZkSyncTree` is in inconsistent state" panic, /// cancellation is most probably the reason. diff --git a/core/lib/zksync_core/src/metrics.rs b/core/lib/zksync_core/src/metrics.rs index fdb043e211fc..539cbbbb2fbb 100644 --- a/core/lib/zksync_core/src/metrics.rs +++ b/core/lib/zksync_core/src/metrics.rs @@ -180,9 +180,9 @@ pub(crate) struct ExternalNodeMetrics { pub synced: Gauge, /// Current sync lag of the external node. pub sync_lag: Gauge, - /// Number of the last L1 batch checked by the reorg detector or consistency checker. + /// Number of the last L1 batch checked by the re-org detector or consistency checker. pub last_correct_batch: Family>, - /// Number of the last miniblock checked by the reorg detector or consistency checker. + /// Number of the last miniblock checked by the re-org detector or consistency checker. pub last_correct_miniblock: Family>, } diff --git a/core/lib/zksync_core/src/reorg_detector/mod.rs b/core/lib/zksync_core/src/reorg_detector/mod.rs index 4f7b8eb55035..16137d40af67 100644 --- a/core/lib/zksync_core/src/reorg_detector/mod.rs +++ b/core/lib/zksync_core/src/reorg_detector/mod.rs @@ -14,19 +14,19 @@ use crate::metrics::{CheckerComponent, EN_METRICS}; const SLEEP_INTERVAL: Duration = Duration::from_secs(5); -/// This is a component that is responsible for detecting the batch reorgs. -/// Batch reorg is a rare event of manual intervention, when the node operator +/// This is a component that is responsible for detecting the batch re-orgs. +/// Batch re-org is a rare event of manual intervention, when the node operator /// decides to revert some of the not yet finalized batches for some reason /// (e.g. inability to generate a proof), and then potentially /// re-organize transactions in them to fix the problem. /// /// To detect them, we constantly check the latest sealed batch root hash, -/// and in the event of mismatch, we know that there has been a reorg. +/// and in the event of mismatch, we know that there has been a re-org. /// We then perform a binary search to find the latest correct block /// and revert all batches after it, to keep being consistent with the main node. /// /// This is the only component that is expected to finish its execution -/// in the even of reorg, since we have to restart the node after a rollback is performed, +/// in the even of re-org, since we have to restart the node after a rollback is performed, /// and is special-cased in the `zksync_external_node` crate. #[derive(Debug)] pub struct ReorgDetector { @@ -113,7 +113,7 @@ impl ReorgDetector { Ok(hash == local_hash) } - /// Localizes a reorg: performs binary search to determine the last non-diverged block. + /// Localizes a re-org: performs binary search to determine the last non-diverged block. async fn detect_reorg(&self, diverged_l1_batch: L1BatchNumber) -> RpcResult { // TODO (BFT-176, BFT-181): We have to look through the whole history, since batch status updater may mark // a block as executed even if the state diverges for it. diff --git a/core/lib/zksync_core/src/state_keeper/batch_executor/mod.rs b/core/lib/zksync_core/src/state_keeper/batch_executor/mod.rs index fffabe0b7e2e..389677b04397 100644 --- a/core/lib/zksync_core/src/state_keeper/batch_executor/mod.rs +++ b/core/lib/zksync_core/src/state_keeper/batch_executor/mod.rs @@ -5,7 +5,7 @@ use tokio::{ task::JoinHandle, }; -use multivm::MultivmTracer; +use multivm::MultiVMTracer; use std::{fmt, sync::Arc}; use multivm::{ diff --git a/core/lib/zksync_core/src/state_keeper/io/mod.rs b/core/lib/zksync_core/src/state_keeper/io/mod.rs index 06397c3cd17e..858c46b2e704 100644 --- a/core/lib/zksync_core/src/state_keeper/io/mod.rs +++ b/core/lib/zksync_core/src/state_keeper/io/mod.rs @@ -143,7 +143,7 @@ impl MiniblockSealerHandle { /// Submits a new sealing `command` to the sealer that this handle is attached to. /// /// If there are currently too many unprocessed commands, this method will wait until - /// enough of them are processed (i.e., there is backpressure). + /// enough of them are processed (i.e., there is back pressure). pub async fn submit(&mut self, command: MiniblockSealCommand) { let miniblock_number = command.miniblock_number; tracing::debug!( diff --git a/core/lib/zksync_core/src/state_keeper/metrics.rs b/core/lib/zksync_core/src/state_keeper/metrics.rs index f16b311e8056..f3f433243209 100644 --- a/core/lib/zksync_core/src/state_keeper/metrics.rs +++ b/core/lib/zksync_core/src/state_keeper/metrics.rs @@ -175,7 +175,7 @@ pub(super) enum L1BatchSealStage { const COUNT_BUCKETS: Buckets = Buckets::values(&[ 10.0, 20.0, 50.0, 100.0, 200.0, 500.0, 1_000.0, 2_000.0, 5_000.0, 10_000.0, 20_000.0, 50_000.0, ]); -/// Buckets for sealing deltas for L1 batches (in seconds). The expected delta is ~1 minute. +/// Buckets for sealing deltas for L1 batches (in seconds). The expected delta is approximately 1 minute. const L1_BATCH_SEAL_DELTA_BUCKETS: Buckets = Buckets::values(&[ 0.1, 0.5, 1.0, 5.0, 10.0, 20.0, 30.0, 40.0, 60.0, 90.0, 120.0, 180.0, 240.0, 300.0, ]); @@ -205,7 +205,7 @@ pub(crate) struct L1BatchMetrics { /// Number of entities stored in Postgres during a specific stage of sealing an L1 batch. #[metrics(buckets = COUNT_BUCKETS)] sealed_entity_count: Family>, - /// Latency of sealing an L1 batch split by the stage and divided by the number of entiries + /// Latency of sealing an L1 batch split by the stage and divided by the number of entries /// stored in the stage. #[metrics(buckets = Buckets::LATENCIES)] sealed_entity_per_unit: Family>, @@ -285,7 +285,7 @@ pub(super) struct MiniblockMetrics { /// Number of entities stored in Postgres during a specific stage of sealing a miniblock. #[metrics(buckets = COUNT_BUCKETS)] sealed_entity_count: Family>, - /// Latency of sealing a miniblock split by the stage and divided by the number of entiries + /// Latency of sealing a miniblock split by the stage and divided by the number of entries /// stored in the stage. #[metrics(buckets = Buckets::LATENCIES)] sealed_entity_per_unit: Family>, diff --git a/core/lib/zksync_core/src/witness_generator/mod.rs b/core/lib/zksync_core/src/witness_generator/mod.rs index 10a7ff861bd8..5dcebadf6a81 100644 --- a/core/lib/zksync_core/src/witness_generator/mod.rs +++ b/core/lib/zksync_core/src/witness_generator/mod.rs @@ -9,7 +9,7 @@ //! each of them starts with an invocation of `WitnessGenerator` with a corresponding `WitnessGeneratorJobType`: //! * `WitnessGeneratorJobType::BasicCircuits`: //! generates basic circuits (circuits like `Main VM` - up to 50 * 48 = 2400 circuits): -//! input table: `basic_circuit_witness_jobs` (todo SMA-1362: will be renamed from `witness_inputs`) +//! input table: `basic_circuit_witness_jobs` (TODO SMA-1362: will be renamed from `witness_inputs`) //! artifact/output table: `leaf_aggregation_jobs` (also creates job stubs in `node_aggregation_jobs` and `scheduler_aggregation_jobs`) //! value in `aggregation_round` field of `prover_jobs` table: 0 //! * `WitnessGeneratorJobType::LeafAggregation`: @@ -28,14 +28,14 @@ //! //! One round of prover generation consists of: //! * `WitnessGenerator` picks up the next `queued` job in its input table and processes it -//! (invoking the corresponding helper function in `zkevm_test_harness` repo) -//! * it saves the generated circuis to `prover_jobs` table and the other artifacts to its output table +//! (invoking the corresponding helper function in `zkevm_test_harness` repository) +//! * it saves the generated circuits to `prover_jobs` table and the other artifacts to its output table //! * the individual proofs are picked up by the provers, processed, and marked as complete. //! * when the last proof for this round is computed, the prover updates the row in the output table //! setting its status to `queued` //! * `WitnessGenerator` picks up such job and proceeds to the next round //! -//! Note that the very first input table (`basic_circuit_witness_jobs` (todo SMA-1362: will be renamed from `witness_inputs`)) +//! Note that the very first input table (`basic_circuit_witness_jobs` (TODO SMA-1362: will be renamed from `witness_inputs`)) //! is populated by the tree (as the input artifact for the `WitnessGeneratorJobType::BasicCircuits` is the merkle proofs) use vise::{Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics}; diff --git a/core/tests/loadnext/src/account/mod.rs b/core/tests/loadnext/src/account/mod.rs index bead354cdeaa..14aa23b50315 100644 --- a/core/tests/loadnext/src/account/mod.rs +++ b/core/tests/loadnext/src/account/mod.rs @@ -59,7 +59,7 @@ pub struct AccountLifespan { contract_execution_params: LoadnextContractExecutionParams, /// Pool of account addresses, used to generate commands. addresses: AddressPool, - /// Successful transactions, required for requesting api + /// Successful transactions, required for requesting API successfully_sent_txs: Arc>>, /// L1 ERC-20 token used in the test. main_l1_token: Address, diff --git a/core/tests/loadnext/src/account_pool.rs b/core/tests/loadnext/src/account_pool.rs index 556bee7f402f..e4ded62dcf14 100644 --- a/core/tests/loadnext/src/account_pool.rs +++ b/core/tests/loadnext/src/account_pool.rs @@ -76,7 +76,7 @@ pub struct TestWallet { } /// Pool of accounts to be used in the test. -/// Each account is represented as `zksync::Wallet` in order to provide convenient interface of interation with zkSync. +/// Each account is represented as `zksync::Wallet` in order to provide convenient interface of interaction with zkSync. #[derive(Debug)] pub struct AccountPool { /// Main wallet that will be used to initialize all the test wallets. diff --git a/core/tests/loadnext/src/command/api.rs b/core/tests/loadnext/src/command/api.rs index e865ab000318..1e520d7c195a 100644 --- a/core/tests/loadnext/src/command/api.rs +++ b/core/tests/loadnext/src/command/api.rs @@ -54,7 +54,7 @@ impl AllWeighted for ApiRequestType { pub struct ApiRequest { /// Type of the request to be performed. pub request_type: ApiRequestType, - /// ZkSync block number, generated randomly. + /// zkSync block number, generated randomly. pub block_number: api::BlockNumber, } diff --git a/docs/advanced/pubdata.md b/docs/advanced/pubdata.md index 6bab6a85a46d..d34033d0a5ef 100644 --- a/docs/advanced/pubdata.md +++ b/docs/advanced/pubdata.md @@ -53,7 +53,7 @@ In pre-boojum era the superset of pubdata fields and input to the `commitBlocks` /// @param repeatedStorageChanges Storage write access as a concatenation index-value /// @param l2Logs concatenation of all L2 -> L1 logs in the block /// @param l2ArbitraryLengthMessages array of hash preimages that were sent as value of L2 logs by special system L2 contract -/// @param factoryDeps (contract bytecodes) array of l2 bytecodes that were deployed +/// @param factoryDeps (contract bytecodes) array of L2 bytecodes that were deployed struct CommitBlockInfo { uint64 blockNumber; uint64 timestamp; diff --git a/prover/witness_generator/README.md b/prover/witness_generator/README.md index 9d35fe7e054a..dc476ca44fc3 100644 --- a/prover/witness_generator/README.md +++ b/prover/witness_generator/README.md @@ -15,7 +15,7 @@ aggregation. That is, every aggregation round needs two sets of input: ## BasicCircuitsWitnessGenerator - generates basic circuits (circuits like `Main VM` - up to 50 \* 48 = 2400 circuits): -- input table: `basic_circuit_witness_jobs` (todo SMA-1362: will be renamed from `witness_inputs`) +- input table: `basic_circuit_witness_jobs` (TODO SMA-1362: will be renamed from `witness_inputs`) - artifact/output table: `leaf_aggregation_jobs` (also creates job stubs in `node_aggregation_jobs` and `scheduler_aggregation_jobs`) value in `aggregation_round` field of `prover_jobs` table: 0 @@ -42,7 +42,7 @@ One round of prover generation consists of: - `WitnessGenerator` picks up the next `queued` job in its input table and processes it (invoking the corresponding helper function in `zkevm_test_harness` repo) -- it saves the generated circuis to `prover_jobs` table and the other artifacts to its output table +- it saves the generated circuits to `prover_jobs` table and the other artifacts to its output table - the individual proofs are picked up by the provers, processed, and marked as complete. - when the last proof for this round is computed, the prover updates the row in the output table setting its status to `queued` diff --git a/spellcheck/era.cfg b/spellcheck/era.cfg new file mode 100644 index 000000000000..c00c2d7a64ce --- /dev/null +++ b/spellcheck/era.cfg @@ -0,0 +1,69 @@ +# Project settings where a Cargo.toml exists and is passed +# ${CARGO_MANIFEST_DIR}/.config/spellcheck.toml + +# Also take into account developer comments +dev_comments = false + +# Skip the README.md file as defined in the cargo manifest +skip_readme = false + +[Hunspell] +# lang and name of `.dic` file +lang = "en_US" +# OS specific additives +# Linux: [ /usr/share/myspell ] +# Windows: [] +# macOS [ /home/alice/Libraries/hunspell, /Libraries/hunspell ] + +# Additional search paths, which take presedence over the default +# os specific search dirs, searched in order, defaults last +search_dirs = ["."] + +# Adds additional dictionaries, can be specified as +# absolute paths or relative in the search dirs (in this order). +# Relative paths are resolved relative to the configuration file +# which is used. +# Refer to `man 5 hunspell` +# or https://www.systutorials.com/docs/linux/man/4-hunspell/#lbAE +# on how to define a custom dictionary file. +extra_dictionaries = ["era.dic"] + +# If set to `true`, the OS specific default search paths +# are skipped and only explicitly specified ones are used. +skip_os_lookups = false + +# Use the builtin dictionaries if none were found in +# in the configured lookup paths. +# Usually combined with `skip_os_lookups=true` +# to enforce the `builtin` usage for consistent +# results across distributions and CI runs. +# Setting this will still use the dictionaries +# specified in `extra_dictionaries = [..]` +# for topic specific lingo. +use_builtin = true + + +[Hunspell.quirks] +# Transforms words that are provided by the tokenizer +# into word fragments based on the capture groups which are to +# be checked. +# If no capture groups are present, the matched word is whitelisted. +transform_regex = ["^'([^\\s])'$", "^[0-9]+x$"] +# Accepts `alphabeta` variants if the checker provides a replacement suggestion +# of `alpha-beta`. +allow_concatenation = true +# And the counterpart, which accepts words with dashes, when the suggestion has +# recommendations without the dashes. This is less common. +allow_dashed = false + +[NlpRules] +# Allows the user to override the default included +# exports of LanguageTool, with other custom +# languages + +# override_rules = "/path/to/rules_binencoded.bin" +# override_tokenizer = "/path/to/tokenizer_binencoded.bin" + +[Reflow] +# Reflows doc comments to adhere to a given maximum line width limit. +max_line_length = 80 diff --git a/spellcheck/era.dic b/spellcheck/era.dic new file mode 100644 index 000000000000..214efbcd595f --- /dev/null +++ b/spellcheck/era.dic @@ -0,0 +1,605 @@ +42 +<= +=> +== +-> +<- ++ +- +* +\ += +/ +|| +< +> +% +0x00 +0x01 +0x02 +~10x +u32 +u64 +H256 +10e18 +10^9 +U256 +k +M +kb +50M +– +18kb +128kb +10k +100k +120k +24k +500k +120kb +500B +100M +~100us +10ms +1us +~100 + +ABI +vlog +L2 +L1 +json +l1 +SystemConfig +TODO +se +ZKSYNC_HOME +MultiVMTracer +vm_virtual_blocks +eth_node +BaseSystemContracts +eth_calls +refactor +WS +env +url +GasAdjuster +base_fee +base_fee_per_gas +ERC20 +Finalizer +Backoff +middleware +parallelization +precompute +precomputed +Postgres +parallelized +parallelize +job_id +API +APIs +async +pointwise +observability +atomics +integrations +stdout +GCS +websocket +struct +localhost +TOML +config +finalizer +boolean +prover +timestamp +H160 +zkSync +AccessList +miniblock +member₁ +member₂ +memberₙ +merkle +eth +Ethereum +deployer +RPC +tx +txs +subtrees +subtree +unfinalizable +meterer +Timedout +bootloader +testkit +Sepolia +Goerli +miniblock +miniblocks +MempoolIO +mempool +latencies +OracleTools +StorageOracle +zk_evm +zkEVM +src +utils +ptr +RefCell +Rc +StorageView +VM_HOOK_POSITION +VM_HOOKS_PARAMS_COUNT +PAYMASTER_CONTEXT_SLOTS +PrecompilerProcessor +MAX_POSTOP_SLOTS +postOp +type +opcode +KnownCodesStorage +param +HistoryDisabled +HistoryEnabled +sorted_timestamps +DecommiterOracle +DecommittmentProcessor +encodings +DecommittmentProcessor +decommitment +known_bytecodes +returndata +namespaces +StateDiffRecord +BYTES_PER_ENUMERATION_INDEX +derived_key + +// zkSync-related words +matterlabs +zkweb +zksync +blockchain +zkscan +zkscrypto +PubSub +loadtest +BigUint +radix +state_keeper +MIN_PAYMASTER_BALANCE +PrometheusCollector +RetryCollector +ScriptCollector +MetricsCollector +OperationResultsCollector +ReportCollector +filesystem +hasher +Hasher +grafana +prometheus +serializer +serializable +deserializer +Deserializes +serializing +deserializing +deserialization +configs +operation_number +hashed_key +deduplication +mutexes +mutex +Blake2s +Blake2 +web3 +Testnets +miniblock_number +hashed_key +tuples +\x19Ethereum +libzkscrypto +EOA +MultiVM +nonces +fri +rollup +pubkey +JSON +keccak256 +pubdata +timestamps +keccak +musig +len +calldata +DApp +metadata +boojum +deps +Precalculated +decommitted +WASM +DefaultPrecompilesProcessor +LSB +DDoS +refactored +tuple +HistoryMode +vm +VM +VMs +VM's +MSB +Enum +PublishProof +jsrpc +backends +ethsig +ethop +decentralization +rollups +zkrollup +unencrypted +permissionless +trustlessness +IERC +Schnorr +MuSig +Merkle +decentralised +mainchain +offchain +processed +zcli +blockchains +sidechain +sidechains +tokenomics +validator's +CHAINID +PREVRANDAO +ECDSA +EIP712 +EIP1559 +EIPs +eth_estimateGas +eth_call +versa +blake2 +AR16MT +Preimages + +// Names +Vyper +stimate +samount +Stichting +Kingsfordweg +RSIN +ABDK +Alef +Zcon +Paypal +Numio +MLTT +USDCs +dapi +validiums +validium +Validium +sharded +pepe +Arweave +Streamr +dutterbutter + +// Used libraries +numberish +arrayify +hexlify +markdownlint +ethersproject +nomicfoundation +nomiclabs +Consensys +zkforge +zkcast +Eigen +IPFS + +// Used programming language words +printf +charsets +println +fatalf +allowfullscreen +inttypes +zbin +Panicf +Deri +DERI +Furucombo +kwargs +scaleb +isinstance +RocksDB +mload +secp +porco +rosso +insize +MLOAD + +// ETC +gitter +signup +signups +precompiled +checkmark +Vitalik +Buterin +roadmap +majeure +conveniens +reimplementing +subsecond +supermajority +gemeente +unauthorised +Ethereum's +SDKs +EVM's +EVM +Göerli +ETHUSDC +USDCUSD +ETHUS +USDCUS +ETHUSD +Arbitrum +Adamantium +Immunefi +Winternitz +ewasm +Evmla +UUPS +Uups +TLDR +BLAKE2s +bytes32 +enumeration_index +backend +enum +num_initial +to_check_storage +source_storage +prepend +deduplicated +user_l2_to_l1_logs +L1Messeger +params +provers +zk +substring +reverter +wei +deduplicate +testnet +mainnet +performant +opcodes +USDC +USD +DBs +unexecutable +RLP +DAL +zkSync's +l2_to_l1 + +// crypto events +Edcon + +// Famous crypto people +Gluchowski +Vitalik's +Buterin's +multisignature +onchain +convertion +Keyhash +Armeabi +scijava +gluk +@Deniallugo's + +// Programming related words +bytecode +bytecodes +timeframe +mkdir +zksolc +zksyncrobot +precompiles +vyper +zkvyper +undol +applyl +Upgradability +Initializable +Hola +mundo +ISTN +Zerion +Maverik +zk_evm_1_3_3 +vk +verifier +crypto +callee +Subcalls +Vec +vecs +L1Messenger +SystemL2ToL1Log +witness_inputs +StateKeeper +enum_index +virtual_block_start_batch +virtual_block_finish_l2_block +maxFeePerGas +maxPriorityFeePerGas +structs +all_circuit +OversizedData +M5 +eth_sign +geth +ethers +js +recovery_id +&self +ETHSignature +recover_signer +BlockNumber +(de) +{result +DebugCall} +CREATE2 +memtables +memtable +PostgreSQL +OneTx +DefaultTracer +Tx1 +Tx2 +TxN +VmStopped +Unversioned +versioned +l2_block +submodule +enums +deserialized +hashmap +vm_m5 +SDK +1M +dir +SSD +getter +Getters +WebSocket +gasLimit +MiBs +MiB +GiB +GiBs +pubsub +\x19Ethereum +nibbles–node +ZkSyncTree +invariants +LEB128 +workflow +L1Batch +runtime +Tokio +Blobstore +S3 +AWS +ExternalIO +ClosedFormInputWrapper +AggregationWrapper +(de)serializer +typesafe +LRU +ns +Q3 +loadnext +args +with_arg +node_aggregation_job +scheduler_job +leaf_aggregation_job +MAX_ATTEMPTs +fsync +TEST_DATABASE_URL +newest_block +block_count +contracts_verification_info +RNG +jsonrpsee +l1_batch +Namespace +ExecutionStatus +VmStarted +reproducibility +CFs +key–value +enum_index_migration_cursor +block_number +initial_writes +errored +FactoryDeps +de +StorageView's +Yul +eth_txs +eth_tx +ExecuteBlock +PublishProofBlocksOnchain +CommitBlocks +entrypoint +gas_limit +TxSender +UX +BasicWitnessInputProducer +eth_tx_history +PENDING_BLOCK +from_block +namespace +PriorityQueue +Görli +Ropsten +Rinkeby +tokio +threadpool +IntrinsicGas +InsufficientFundsForTransfer +ChainId +eth_getLogs +façade +virtual_blocks_per_miniblock +virtual_block_interval +cloneable +timestamped +healthcheck +readonly +upgrader +startup + +AUTOGENERATED +x19Ethereum +block_timestamp +SYSTEM_BLOCK_INFO_BLOCK_NUMBER_MULTIPLIER +OneTxTracer +multicall +Multicall3 +updatable +instantiation +unexecuted +transactional +benchmarking +virtual_blocks_interval +dal +codebase +compactions +M6 +compiler_common \ No newline at end of file From bd268ac02bc3530c1d3247cb9496c3e13c2e52d9 Mon Sep 17 00:00:00 2001 From: Danil Date: Wed, 29 Nov 2023 17:51:47 +0100 Subject: [PATCH 005/268] fix(vm): Expose additional types and traits (#563) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Add more pubs for previous vm ## Why ❔ For updating era testnode we need to expose some additional data ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zk fmt` and `zk lint`. --------- Signed-off-by: Danil --- core/lib/multivm/src/lib.rs | 3 +++ core/lib/multivm/src/versions/vm_refunds_enhancement/mod.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/core/lib/multivm/src/lib.rs b/core/lib/multivm/src/lib.rs index adb9358980fd..06d7a4291300 100644 --- a/core/lib/multivm/src/lib.rs +++ b/core/lib/multivm/src/lib.rs @@ -26,3 +26,6 @@ pub use versions::vm_m5; pub use versions::vm_m6; pub use versions::vm_refunds_enhancement; pub use versions::vm_virtual_blocks; +pub use zk_evm_1_3_1; +pub use zk_evm_1_3_3; +pub use zk_evm_1_4_0; diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/mod.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/mod.rs index 89e7b21b984b..28a681e5e604 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/mod.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/mod.rs @@ -6,7 +6,7 @@ pub use old_vm::{ pub use oracles::storage::StorageOracle; pub use tracers::dispatcher::TracerDispatcher; -pub use tracers::traits::{TracerPointer, VmTracer}; +pub use tracers::traits::{ToTracerPointer, TracerPointer, VmTracer}; pub use utils::transaction_encoding::TransactionVmExt; From 8b34ab9d35b678da6df7c7f9eee0652bc8522d56 Mon Sep 17 00:00:00 2001 From: Alex Ostrovski Date: Wed, 29 Nov 2023 19:53:22 +0200 Subject: [PATCH 006/268] test(api): Test coverage for WebSocket server (#525) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Cover basic WS-specific JSON-RPC functionality (subscriptions, `getFilterChanges`) with tests. ## Why ❔ Will allow to set the expectations the current server behavior, so that it could be evolved in the future w/o unexpected / breaking changes (e.g., when implementing `jsonrpsee`-based subscriptions). ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --- core/lib/types/src/tx/mod.rs | 2 +- core/lib/web3_decl/src/types.rs | 61 ++- .../web3/backend_jsonrpc/pub_sub.rs | 3 +- .../zksync_core/src/api_server/web3/mod.rs | 48 +- .../web3/namespaces/eth_subscribe.rs | 147 ----- .../src/api_server/web3/namespaces/mod.rs | 10 +- .../zksync_core/src/api_server/web3/pubsub.rs | 391 ++++++++++++++ .../src/api_server/web3/pubsub_notifier.rs | 191 ------- .../zksync_core/src/api_server/web3/tests.rs | 145 ----- .../src/api_server/web3/tests/mod.rs | 503 ++++++++++++++++++ .../src/api_server/web3/tests/ws.rs | 466 ++++++++++++++++ 11 files changed, 1433 insertions(+), 534 deletions(-) delete mode 100644 core/lib/zksync_core/src/api_server/web3/namespaces/eth_subscribe.rs create mode 100644 core/lib/zksync_core/src/api_server/web3/pubsub.rs delete mode 100644 core/lib/zksync_core/src/api_server/web3/pubsub_notifier.rs delete mode 100644 core/lib/zksync_core/src/api_server/web3/tests.rs create mode 100644 core/lib/zksync_core/src/api_server/web3/tests/mod.rs create mode 100644 core/lib/zksync_core/src/api_server/web3/tests/ws.rs diff --git a/core/lib/types/src/tx/mod.rs b/core/lib/types/src/tx/mod.rs index bd2e6e46694d..71f188f3217e 100644 --- a/core/lib/types/src/tx/mod.rs +++ b/core/lib/types/src/tx/mod.rs @@ -49,7 +49,7 @@ impl TransactionExecutionResult { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct IncludedTxLocation { pub tx_hash: H256, pub tx_index_in_miniblock: u32, diff --git a/core/lib/web3_decl/src/types.rs b/core/lib/web3_decl/src/types.rs index 46033bc41180..7abe34637d67 100644 --- a/core/lib/web3_decl/src/types.rs +++ b/core/lib/web3_decl/src/types.rs @@ -5,13 +5,15 @@ //! //! These "extensions" are required to provide more zkSync-specific information while remaining Web3-compilant. -use core::convert::{TryFrom, TryInto}; -use core::fmt; -use core::marker::PhantomData; - use itertools::unfold; use rlp::Rlp; -use serde::{de, Deserialize, Serialize, Serializer}; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + +use core::{ + convert::{TryFrom, TryInto}, + fmt, + marker::PhantomData, +}; pub use zksync_types::{ api::{Block, BlockNumber, Log, TransactionReceipt, TransactionRequest}, @@ -105,13 +107,18 @@ pub enum FilterChanges { } /// Either value or array of values. +/// +/// A value must serialize into a string. #[derive(Default, Debug, PartialEq, Clone)] pub struct ValueOrArray(pub Vec); -impl Serialize for ValueOrArray -where - T: Serialize, -{ +impl From for ValueOrArray { + fn from(value: T) -> Self { + Self(vec![value]) + } +} + +impl Serialize for ValueOrArray { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -124,18 +131,18 @@ where } } -impl<'de, T: std::fmt::Debug + Deserialize<'de>> ::serde::Deserialize<'de> for ValueOrArray { +impl<'de, T: Deserialize<'de>> Deserialize<'de> for ValueOrArray { fn deserialize(deserializer: D) -> Result where - D: ::serde::Deserializer<'de>, + D: Deserializer<'de>, { struct Visitor(PhantomData); - impl<'de, T: std::fmt::Debug + Deserialize<'de>> de::Visitor<'de> for Visitor { + impl<'de, T: Deserialize<'de>> de::Visitor<'de> for Visitor { type Value = ValueOrArray; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("Expected value or sequence") + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("string value or sequence of values") } fn visit_str(self, value: &str) -> Result @@ -408,4 +415,30 @@ mod tests { assert_eq!(&actual_block_id, expected_block_id); } } + + #[test] + fn serializing_value_or_array() { + let value = ValueOrArray::from(Address::repeat_byte(0x1f)); + let json = serde_json::to_value(value.clone()).unwrap(); + assert_eq!( + json, + serde_json::json!("0x1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f") + ); + + let restored_value: ValueOrArray
= serde_json::from_value(json).unwrap(); + assert_eq!(restored_value, value); + + let value = ValueOrArray(vec![Address::repeat_byte(0x1f), Address::repeat_byte(0x23)]); + let json = serde_json::to_value(value.clone()).unwrap(); + assert_eq!( + json, + serde_json::json!([ + "0x1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f", + "0x2323232323232323232323232323232323232323", + ]) + ); + + let restored_value: ValueOrArray
= serde_json::from_value(json).unwrap(); + assert_eq!(restored_value, value); + } } diff --git a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/pub_sub.rs b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/pub_sub.rs index 4a28a17b4e36..1b11919abde4 100644 --- a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/pub_sub.rs +++ b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/pub_sub.rs @@ -7,8 +7,7 @@ use jsonrpc_pubsub::{Session, SubscriptionId}; use zksync_web3_decl::types::PubSubResult; -use super::super::namespaces::EthSubscribe; -use super::batch_limiter_middleware::RateLimitMetadata; +use super::{super::EthSubscribe, batch_limiter_middleware::RateLimitMetadata}; #[rpc] pub trait Web3PubSub { diff --git a/core/lib/zksync_core/src/api_server/web3/mod.rs b/core/lib/zksync_core/src/api_server/web3/mod.rs index 918bf5f67dc1..2904d5af79dc 100644 --- a/core/lib/zksync_core/src/api_server/web3/mod.rs +++ b/core/lib/zksync_core/src/api_server/web3/mod.rs @@ -4,7 +4,7 @@ use jsonrpc_core::MetaIoHandler; use jsonrpc_http_server::hyper; use jsonrpc_pubsub::PubSubHandler; use serde::Deserialize; -use tokio::sync::{oneshot, watch, Mutex}; +use tokio::sync::{mpsc, oneshot, watch, Mutex}; use tower_http::{cors::CorsLayer, metrics::InFlightRequestsLayer}; use chrono::NaiveDateTime; @@ -40,7 +40,7 @@ pub mod backend_jsonrpc; pub mod backend_jsonrpsee; mod metrics; pub mod namespaces; -mod pubsub_notifier; +mod pubsub; pub mod state; #[cfg(test)] pub(crate) mod tests; @@ -56,10 +56,9 @@ use self::backend_jsonrpc::{ }; use self::metrics::API_METRICS; use self::namespaces::{ - DebugNamespace, EnNamespace, EthNamespace, EthSubscribe, NetNamespace, Web3Namespace, - ZksNamespace, + DebugNamespace, EnNamespace, EthNamespace, NetNamespace, Web3Namespace, ZksNamespace, }; -use self::pubsub_notifier::{notify_blocks, notify_logs, notify_txs}; +use self::pubsub::{EthSubscribe, PubSubEvent}; use self::state::{Filters, InternalApiConfig, RpcState, SealedMiniblockNumber}; /// Timeout for graceful shutdown logic within API servers. @@ -150,6 +149,7 @@ pub struct ApiBuilder { namespaces: Option>, logs_translator_enabled: bool, tree_api_url: Option, + pub_sub_events_sender: Option>, } impl ApiBuilder { @@ -174,6 +174,7 @@ impl ApiBuilder { config, logs_translator_enabled: false, tree_api_url: None, + pub_sub_events_sender: None, } } @@ -275,6 +276,12 @@ impl ApiBuilder { self.tree_api_url = tree_api_url; self } + + #[cfg(test)] + fn with_pub_sub_events(mut self, sender: mpsc::UnboundedSender) -> Self { + self.pub_sub_events_sender = Some(sender); + self + } } impl ApiBuilder { @@ -541,30 +548,19 @@ impl ApiBuilder { .unwrap() .contains(&Namespace::Pubsub) { - let pub_sub = EthSubscribe::new(runtime.handle().clone()); + let mut pub_sub = EthSubscribe::new(runtime.handle().clone()); + if let Some(sender) = self.pub_sub_events_sender.take() { + pub_sub.set_events_sender(sender); + } let polling_interval = self .polling_interval .context("Polling interval is not set")?; - tasks.extend([ - tokio::spawn(notify_blocks( - pub_sub.active_block_subs.clone(), - self.pool.clone(), - polling_interval, - stop_receiver.clone(), - )), - tokio::spawn(notify_txs( - pub_sub.active_tx_subs.clone(), - self.pool.clone(), - polling_interval, - stop_receiver.clone(), - )), - tokio::spawn(notify_logs( - pub_sub.active_log_subs.clone(), - self.pool.clone(), - polling_interval, - stop_receiver.clone(), - )), - ]); + + tasks.extend(pub_sub.spawn_notifiers( + self.pool.clone(), + polling_interval, + stop_receiver.clone(), + )); io_handler.extend_with(pub_sub.to_delegate()); } self.extend_jsonrpc_methods(&mut io_handler).await; diff --git a/core/lib/zksync_core/src/api_server/web3/namespaces/eth_subscribe.rs b/core/lib/zksync_core/src/api_server/web3/namespaces/eth_subscribe.rs deleted file mode 100644 index 208318ec3e12..000000000000 --- a/core/lib/zksync_core/src/api_server/web3/namespaces/eth_subscribe.rs +++ /dev/null @@ -1,147 +0,0 @@ -use jsonrpc_core::error::{Error, ErrorCode}; -use jsonrpc_pubsub::{typed, SubscriptionId}; -use tokio::sync::RwLock; - -use std::{collections::HashMap, sync::Arc}; - -use zksync_types::web3::types::H128; -use zksync_web3_decl::types::{PubSubFilter, PubSubResult}; - -use super::eth::EVENT_TOPIC_NUMBER_LIMIT; -use crate::api_server::web3::metrics::{SubscriptionType, PUB_SUB_METRICS}; - -pub type SubscriptionMap = Arc>>; - -#[derive(Debug, Clone)] -pub struct EthSubscribe { - // `jsonrpc` backend executes task subscription on a separate thread that has no tokio context. - pub runtime_handle: tokio::runtime::Handle, - pub active_block_subs: SubscriptionMap>, - pub active_tx_subs: SubscriptionMap>, - pub active_log_subs: SubscriptionMap<(typed::Sink, PubSubFilter)>, -} - -impl EthSubscribe { - pub fn new(runtime_handle: tokio::runtime::Handle) -> Self { - Self { - runtime_handle, - active_block_subs: SubscriptionMap::default(), - active_tx_subs: SubscriptionMap::default(), - active_log_subs: SubscriptionMap::default(), - } - } - - /// Assigns ID for the subscriber if the connection is open, returns error otherwise. - fn assign_id( - subscriber: typed::Subscriber, - ) -> Result<(typed::Sink, SubscriptionId), ()> { - let id = H128::random(); - let sub_id = SubscriptionId::String(format!("0x{}", hex::encode(id.0))); - let sink = subscriber.assign_id(sub_id.clone())?; - Ok((sink, sub_id)) - } - - fn reject(subscriber: typed::Subscriber) { - subscriber - .reject(Error { - code: ErrorCode::InvalidParams, - message: "Rejecting subscription - invalid parameters provided.".into(), - data: None, - }) - .unwrap(); - } - - #[tracing::instrument(skip(self, subscriber, params))] - pub async fn sub( - &self, - subscriber: typed::Subscriber, - sub_type: String, - params: Option, - ) { - let sub_type = match sub_type.as_str() { - "newHeads" => { - let mut block_subs = self.active_block_subs.write().await; - let Ok((sink, id)) = Self::assign_id(subscriber) else { - return; - }; - block_subs.insert(id, sink); - Some(SubscriptionType::Blocks) - } - "newPendingTransactions" => { - let mut tx_subs = self.active_tx_subs.write().await; - let Ok((sink, id)) = Self::assign_id(subscriber) else { - return; - }; - tx_subs.insert(id, sink); - Some(SubscriptionType::Txs) - } - "logs" => { - let filter = params.map(serde_json::from_value).transpose(); - match filter { - Ok(filter) => { - let filter: PubSubFilter = filter.unwrap_or_default(); - if filter - .topics - .as_ref() - .map(|topics| topics.len()) - .unwrap_or(0) - > EVENT_TOPIC_NUMBER_LIMIT - { - Self::reject(subscriber); - None - } else { - let mut log_subs = self.active_log_subs.write().await; - let Ok((sink, id)) = Self::assign_id(subscriber) else { - return; - }; - log_subs.insert(id, (sink, filter)); - Some(SubscriptionType::Logs) - } - } - Err(_) => { - Self::reject(subscriber); - None - } - } - } - "syncing" => { - let Ok((sink, _id)) = Self::assign_id(subscriber) else { - return; - }; - let _ = sink.notify(Ok(PubSubResult::Syncing(false))); - None - } - _ => { - Self::reject(subscriber); - None - } - }; - - if let Some(sub_type) = sub_type { - PUB_SUB_METRICS.active_subscribers[&sub_type].inc_by(1); - } - } - - #[tracing::instrument(skip(self))] - pub async fn unsub(&self, id: SubscriptionId) -> Result { - let removed = if self.active_block_subs.write().await.remove(&id).is_some() { - Some(SubscriptionType::Blocks) - } else if self.active_tx_subs.write().await.remove(&id).is_some() { - Some(SubscriptionType::Txs) - } else if self.active_log_subs.write().await.remove(&id).is_some() { - Some(SubscriptionType::Logs) - } else { - None - }; - if let Some(sub_type) = removed { - PUB_SUB_METRICS.active_subscribers[&sub_type].dec_by(1); - Ok(true) - } else { - Err(Error { - code: ErrorCode::InvalidParams, - message: "Invalid subscription.".into(), - data: None, - }) - } - } -} diff --git a/core/lib/zksync_core/src/api_server/web3/namespaces/mod.rs b/core/lib/zksync_core/src/api_server/web3/namespaces/mod.rs index 9792fed5edc0..8504717f3b9e 100644 --- a/core/lib/zksync_core/src/api_server/web3/namespaces/mod.rs +++ b/core/lib/zksync_core/src/api_server/web3/namespaces/mod.rs @@ -4,17 +4,11 @@ mod debug; mod en; pub(crate) mod eth; -mod eth_subscribe; mod net; mod web3; mod zks; pub use self::{ - debug::DebugNamespace, - en::EnNamespace, - eth::EthNamespace, - eth_subscribe::{EthSubscribe, SubscriptionMap}, - net::NetNamespace, - web3::Web3Namespace, - zks::ZksNamespace, + debug::DebugNamespace, en::EnNamespace, eth::EthNamespace, net::NetNamespace, + web3::Web3Namespace, zks::ZksNamespace, }; diff --git a/core/lib/zksync_core/src/api_server/web3/pubsub.rs b/core/lib/zksync_core/src/api_server/web3/pubsub.rs new file mode 100644 index 000000000000..946c0744ba4d --- /dev/null +++ b/core/lib/zksync_core/src/api_server/web3/pubsub.rs @@ -0,0 +1,391 @@ +//! (Largely) backend-agnostic logic for dealing with Web3 subscriptions. + +use anyhow::Context as _; +use jsonrpc_core::error::{Error, ErrorCode}; +use jsonrpc_pubsub::{typed, SubscriptionId}; +use tokio::{ + sync::{mpsc, watch, RwLock}, + task::JoinHandle, + time::{interval, Duration}, +}; + +use std::{collections::HashMap, sync::Arc}; + +use zksync_dal::ConnectionPool; +use zksync_types::{MiniblockNumber, H128, H256}; +use zksync_web3_decl::types::{BlockHeader, Log, PubSubFilter, PubSubResult}; + +use super::{ + metrics::{SubscriptionType, PUB_SUB_METRICS}, + namespaces::eth::EVENT_TOPIC_NUMBER_LIMIT, +}; + +pub(super) type SubscriptionMap = Arc>>; + +/// Events emitted by the subscription logic. Only used in WebSocket server tests so far. +#[derive(Debug)] +pub(super) enum PubSubEvent { + Subscribed(SubscriptionType), + NotifyIterationFinished(SubscriptionType), +} + +/// Manager of notifications for a certain type of subscriptions. +#[derive(Debug)] +struct PubSubNotifier { + subscribers: SubscriptionMap, + connection_pool: ConnectionPool, + polling_interval: Duration, + events_sender: Option>, +} + +impl PubSubNotifier { + async fn sealed_miniblock_number(&self) -> anyhow::Result { + self.connection_pool + .access_storage_tagged("api") + .await + .context("access_storage_tagged")? + .blocks_web3_dal() + .get_sealed_miniblock_number() + .await + .context("get_sealed_miniblock_number()") + } + + async fn current_subscribers(&self) -> Vec { + self.subscribers.read().await.values().cloned().collect() + } + + fn emit_event(&self, event: PubSubEvent) { + if let Some(sender) = &self.events_sender { + sender.send(event).ok(); + } + } +} + +impl PubSubNotifier> { + async fn notify_blocks(self, stop_receiver: watch::Receiver) -> anyhow::Result<()> { + let mut last_block_number = self.sealed_miniblock_number().await?; + let mut timer = interval(self.polling_interval); + loop { + if *stop_receiver.borrow() { + tracing::info!("Stop signal received, pubsub_block_notifier is shutting down"); + break; + } + timer.tick().await; + + let db_latency = PUB_SUB_METRICS.db_poll_latency[&SubscriptionType::Blocks].start(); + let new_blocks = self.new_blocks(last_block_number).await?; + db_latency.observe(); + + if let Some(last_block) = new_blocks.last() { + last_block_number = MiniblockNumber(last_block.number.unwrap().as_u32()); + + let notify_latency = + PUB_SUB_METRICS.notify_subscribers_latency[&SubscriptionType::Blocks].start(); + for sink in self.current_subscribers().await { + for block in new_blocks.iter().cloned() { + if sink.notify(Ok(PubSubResult::Header(block))).is_err() { + // Subscriber disconnected. + break; + } + PUB_SUB_METRICS.notify[&SubscriptionType::Blocks].inc(); + } + } + notify_latency.observe(); + } + self.emit_event(PubSubEvent::NotifyIterationFinished( + SubscriptionType::Blocks, + )); + } + Ok(()) + } + + async fn new_blocks( + &self, + last_block_number: MiniblockNumber, + ) -> anyhow::Result> { + self.connection_pool + .access_storage_tagged("api") + .await + .context("access_storage_tagged")? + .blocks_web3_dal() + .get_block_headers_after(last_block_number) + .await + .with_context(|| format!("get_block_headers_after({last_block_number})")) + } + + async fn notify_txs(self, stop_receiver: watch::Receiver) -> anyhow::Result<()> { + let mut last_time = chrono::Utc::now().naive_utc(); + let mut timer = interval(self.polling_interval); + loop { + if *stop_receiver.borrow() { + tracing::info!("Stop signal received, pubsub_tx_notifier is shutting down"); + break; + } + timer.tick().await; + + let db_latency = PUB_SUB_METRICS.db_poll_latency[&SubscriptionType::Txs].start(); + let (new_txs, new_last_time) = self.new_txs(last_time).await?; + db_latency.observe(); + + if let Some(new_last_time) = new_last_time { + last_time = new_last_time; + let notify_latency = + PUB_SUB_METRICS.notify_subscribers_latency[&SubscriptionType::Txs].start(); + + for sink in self.current_subscribers().await { + for tx_hash in new_txs.iter().cloned() { + if sink.notify(Ok(PubSubResult::TxHash(tx_hash))).is_err() { + // Subscriber disconnected. + break; + } + PUB_SUB_METRICS.notify[&SubscriptionType::Txs].inc(); + } + } + notify_latency.observe(); + } + self.emit_event(PubSubEvent::NotifyIterationFinished(SubscriptionType::Txs)); + } + Ok(()) + } + + async fn new_txs( + &self, + last_time: chrono::NaiveDateTime, + ) -> anyhow::Result<(Vec, Option)> { + self.connection_pool + .access_storage_tagged("api") + .await + .context("access_storage_tagged")? + .transactions_web3_dal() + .get_pending_txs_hashes_after(last_time, None) + .await + .context("get_pending_txs_hashes_after()") + } +} + +impl PubSubNotifier<(typed::Sink, PubSubFilter)> { + async fn notify_logs(self, stop_receiver: watch::Receiver) -> anyhow::Result<()> { + let mut last_block_number = self.sealed_miniblock_number().await?; + let mut timer = interval(self.polling_interval); + loop { + if *stop_receiver.borrow() { + tracing::info!("Stop signal received, pubsub_logs_notifier is shutting down"); + break; + } + timer.tick().await; + + let db_latency = PUB_SUB_METRICS.db_poll_latency[&SubscriptionType::Logs].start(); + let new_logs = self.new_logs(last_block_number).await?; + db_latency.observe(); + + if let Some(last_log) = new_logs.last() { + last_block_number = MiniblockNumber(last_log.block_number.unwrap().as_u32()); + let notify_latency = + PUB_SUB_METRICS.notify_subscribers_latency[&SubscriptionType::Logs].start(); + + for (sink, filter) in self.current_subscribers().await { + for log in &new_logs { + if filter.matches(log) { + if sink.notify(Ok(PubSubResult::Log(log.clone()))).is_err() { + // Subscriber disconnected. + break; + } + PUB_SUB_METRICS.notify[&SubscriptionType::Logs].inc(); + } + } + } + notify_latency.observe(); + } + self.emit_event(PubSubEvent::NotifyIterationFinished(SubscriptionType::Logs)); + } + Ok(()) + } + + async fn new_logs(&self, last_block_number: MiniblockNumber) -> anyhow::Result> { + self.connection_pool + .access_storage_tagged("api") + .await + .context("access_storage_tagged")? + .events_web3_dal() + .get_all_logs(last_block_number) + .await + .context("events_web3_dal().get_all_logs()") + } +} + +/// Subscription support for Web3 APIs. +#[derive(Debug, Clone)] +pub(super) struct EthSubscribe { + // `jsonrpc` backend executes task subscription on a separate thread that has no tokio context. + pub runtime_handle: tokio::runtime::Handle, + active_block_subs: SubscriptionMap>, + active_tx_subs: SubscriptionMap>, + active_log_subs: SubscriptionMap<(typed::Sink, PubSubFilter)>, + events_sender: Option>, +} + +impl EthSubscribe { + pub fn new(runtime_handle: tokio::runtime::Handle) -> Self { + Self { + runtime_handle, + active_block_subs: SubscriptionMap::default(), + active_tx_subs: SubscriptionMap::default(), + active_log_subs: SubscriptionMap::default(), + events_sender: None, + } + } + + pub fn set_events_sender(&mut self, sender: mpsc::UnboundedSender) { + self.events_sender = Some(sender); + } + + /// Assigns ID for the subscriber if the connection is open, returns error otherwise. + fn assign_id( + subscriber: typed::Subscriber, + ) -> Result<(typed::Sink, SubscriptionId), ()> { + let id = H128::random(); + let sub_id = SubscriptionId::String(format!("0x{}", hex::encode(id.0))); + let sink = subscriber.assign_id(sub_id.clone())?; + Ok((sink, sub_id)) + } + + fn reject(subscriber: typed::Subscriber) { + subscriber + .reject(Error { + code: ErrorCode::InvalidParams, + message: "Rejecting subscription - invalid parameters provided.".into(), + data: None, + }) + .unwrap(); + } + + #[tracing::instrument(skip(self, subscriber, params))] + pub async fn sub( + &self, + subscriber: typed::Subscriber, + sub_type: String, + params: Option, + ) { + let sub_type = match sub_type.as_str() { + "newHeads" => { + let mut block_subs = self.active_block_subs.write().await; + let Ok((sink, id)) = Self::assign_id(subscriber) else { + return; + }; + block_subs.insert(id, sink); + Some(SubscriptionType::Blocks) + } + "newPendingTransactions" => { + let mut tx_subs = self.active_tx_subs.write().await; + let Ok((sink, id)) = Self::assign_id(subscriber) else { + return; + }; + tx_subs.insert(id, sink); + Some(SubscriptionType::Txs) + } + "logs" => { + let filter = params.map(serde_json::from_value).transpose(); + match filter { + Ok(filter) => { + let filter: PubSubFilter = filter.unwrap_or_default(); + let topic_count = filter.topics.as_ref().map_or(0, Vec::len); + if topic_count > EVENT_TOPIC_NUMBER_LIMIT { + Self::reject(subscriber); + None + } else { + let mut log_subs = self.active_log_subs.write().await; + let Ok((sink, id)) = Self::assign_id(subscriber) else { + return; + }; + log_subs.insert(id, (sink, filter)); + Some(SubscriptionType::Logs) + } + } + Err(_) => { + Self::reject(subscriber); + None + } + } + } + "syncing" => { + let Ok((sink, _id)) = Self::assign_id(subscriber) else { + return; + }; + let _ = sink.notify(Ok(PubSubResult::Syncing(false))); + None + } + _ => { + Self::reject(subscriber); + None + } + }; + + if let Some(sub_type) = sub_type { + PUB_SUB_METRICS.active_subscribers[&sub_type].inc_by(1); + if let Some(sender) = &self.events_sender { + sender.send(PubSubEvent::Subscribed(sub_type)).ok(); + } + } + } + + #[tracing::instrument(skip(self))] + pub async fn unsub(&self, id: SubscriptionId) -> Result { + let removed = if self.active_block_subs.write().await.remove(&id).is_some() { + Some(SubscriptionType::Blocks) + } else if self.active_tx_subs.write().await.remove(&id).is_some() { + Some(SubscriptionType::Txs) + } else if self.active_log_subs.write().await.remove(&id).is_some() { + Some(SubscriptionType::Logs) + } else { + None + }; + if let Some(sub_type) = removed { + PUB_SUB_METRICS.active_subscribers[&sub_type].dec_by(1); + Ok(true) + } else { + Err(Error { + code: ErrorCode::InvalidParams, + message: "Invalid subscription.".into(), + data: None, + }) + } + } + + /// Spawns notifier tasks. This should be called once per instance. + pub fn spawn_notifiers( + &self, + connection_pool: ConnectionPool, + polling_interval: Duration, + stop_receiver: watch::Receiver, + ) -> Vec>> { + let mut notifier_tasks = Vec::with_capacity(3); + let notifier = PubSubNotifier { + subscribers: self.active_block_subs.clone(), + connection_pool: connection_pool.clone(), + polling_interval, + events_sender: self.events_sender.clone(), + }; + let notifier_task = tokio::spawn(notifier.notify_blocks(stop_receiver.clone())); + notifier_tasks.push(notifier_task); + + let notifier = PubSubNotifier { + subscribers: self.active_tx_subs.clone(), + connection_pool: connection_pool.clone(), + polling_interval, + events_sender: self.events_sender.clone(), + }; + let notifier_task = tokio::spawn(notifier.notify_txs(stop_receiver.clone())); + notifier_tasks.push(notifier_task); + + let notifier = PubSubNotifier { + subscribers: self.active_log_subs.clone(), + connection_pool, + polling_interval, + events_sender: self.events_sender.clone(), + }; + let notifier_task = tokio::spawn(notifier.notify_logs(stop_receiver)); + + notifier_tasks.push(notifier_task); + notifier_tasks + } +} diff --git a/core/lib/zksync_core/src/api_server/web3/pubsub_notifier.rs b/core/lib/zksync_core/src/api_server/web3/pubsub_notifier.rs deleted file mode 100644 index 0d1008e77e0c..000000000000 --- a/core/lib/zksync_core/src/api_server/web3/pubsub_notifier.rs +++ /dev/null @@ -1,191 +0,0 @@ -use anyhow::Context as _; -use jsonrpc_pubsub::typed; -use tokio::sync::watch; -use tokio::time::{interval, Duration}; - -use zksync_dal::ConnectionPool; -use zksync_types::MiniblockNumber; -use zksync_web3_decl::types::{PubSubFilter, PubSubResult}; - -use super::{ - metrics::{SubscriptionType, PUB_SUB_METRICS}, - namespaces::SubscriptionMap, -}; - -pub async fn notify_blocks( - subscribers: SubscriptionMap>, - connection_pool: ConnectionPool, - polling_interval: Duration, - stop_receiver: watch::Receiver, -) -> anyhow::Result<()> { - let mut last_block_number = connection_pool - .access_storage_tagged("api") - .await - .unwrap() - .blocks_web3_dal() - .get_sealed_miniblock_number() - .await - .context("get_sealed_miniblock_number()")?; - let mut timer = interval(polling_interval); - loop { - if *stop_receiver.borrow() { - tracing::info!("Stop signal received, pubsub_block_notifier is shutting down"); - break; - } - - timer.tick().await; - - let db_latency = PUB_SUB_METRICS.db_poll_latency[&SubscriptionType::Blocks].start(); - let new_blocks = connection_pool - .access_storage_tagged("api") - .await - .unwrap() - .blocks_web3_dal() - .get_block_headers_after(last_block_number) - .await - .with_context(|| format!("get_block_headers_after({last_block_number})"))?; - db_latency.observe(); - - if !new_blocks.is_empty() { - last_block_number = - MiniblockNumber(new_blocks.last().unwrap().number.unwrap().as_u32()); - - let notify_latency = - PUB_SUB_METRICS.notify_subscribers_latency[&SubscriptionType::Blocks].start(); - let subscribers = subscribers - .read() - .await - .values() - .cloned() - .collect::>(); - for sink in subscribers { - for block in new_blocks.iter().cloned() { - if sink.notify(Ok(PubSubResult::Header(block))).is_err() { - // Subscriber disconnected. - break; - } - PUB_SUB_METRICS.notify[&SubscriptionType::Blocks].inc(); - } - } - notify_latency.observe(); - } - } - Ok(()) -} - -pub async fn notify_txs( - subscribers: SubscriptionMap>, - connection_pool: ConnectionPool, - polling_interval: Duration, - stop_receiver: watch::Receiver, -) -> anyhow::Result<()> { - let mut last_time = chrono::Utc::now().naive_utc(); - let mut timer = interval(polling_interval); - loop { - if *stop_receiver.borrow() { - tracing::info!("Stop signal received, pubsub_tx_notifier is shutting down"); - break; - } - - timer.tick().await; - - let db_latency = PUB_SUB_METRICS.db_poll_latency[&SubscriptionType::Txs].start(); - let (new_txs, new_last_time) = connection_pool - .access_storage_tagged("api") - .await - .unwrap() - .transactions_web3_dal() - .get_pending_txs_hashes_after(last_time, None) - .await - .context("get_pending_txs_hashes_after()")?; - db_latency.observe(); - - if let Some(new_last_time) = new_last_time { - last_time = new_last_time; - let notify_latency = - PUB_SUB_METRICS.notify_subscribers_latency[&SubscriptionType::Txs].start(); - - let subscribers = subscribers - .read() - .await - .values() - .cloned() - .collect::>(); - for sink in subscribers { - for tx_hash in new_txs.iter().cloned() { - if sink.notify(Ok(PubSubResult::TxHash(tx_hash))).is_err() { - // Subscriber disconnected. - break; - } - PUB_SUB_METRICS.notify[&SubscriptionType::Txs].inc(); - } - } - notify_latency.observe(); - } - } - Ok(()) -} - -pub async fn notify_logs( - subscribers: SubscriptionMap<(typed::Sink, PubSubFilter)>, - connection_pool: ConnectionPool, - polling_interval: Duration, - stop_receiver: watch::Receiver, -) -> anyhow::Result<()> { - let mut last_block_number = connection_pool - .access_storage_tagged("api") - .await - .unwrap() - .blocks_web3_dal() - .get_sealed_miniblock_number() - .await - .context("get_sealed_miniblock_number()")?; - let mut timer = interval(polling_interval); - loop { - if *stop_receiver.borrow() { - tracing::info!("Stop signal received, pubsub_logs_notifier is shutting down"); - break; - } - - timer.tick().await; - - let db_latency = PUB_SUB_METRICS.db_poll_latency[&SubscriptionType::Logs].start(); - let new_logs = connection_pool - .access_storage_tagged("api") - .await - .unwrap() - .events_web3_dal() - .get_all_logs(last_block_number) - .await - .context("events_web3_dal().get_all_logs()")?; - db_latency.observe(); - - if !new_logs.is_empty() { - last_block_number = - MiniblockNumber(new_logs.last().unwrap().block_number.unwrap().as_u32()); - let notify_latency = - PUB_SUB_METRICS.notify_subscribers_latency[&SubscriptionType::Logs].start(); - - let subscribers = subscribers - .read() - .await - .values() - .cloned() - .collect::>(); - - for (sink, filter) in subscribers { - for log in new_logs.iter().cloned() { - if filter.matches(&log) { - if sink.notify(Ok(PubSubResult::Log(log))).is_err() { - // Subscriber disconnected. - break; - } - PUB_SUB_METRICS.notify[&SubscriptionType::Logs].inc(); - } - } - } - notify_latency.observe(); - } - } - Ok(()) -} diff --git a/core/lib/zksync_core/src/api_server/web3/tests.rs b/core/lib/zksync_core/src/api_server/web3/tests.rs deleted file mode 100644 index 0d595830d34f..000000000000 --- a/core/lib/zksync_core/src/api_server/web3/tests.rs +++ /dev/null @@ -1,145 +0,0 @@ -use tokio::sync::watch; - -use std::{sync::Arc, time::Instant}; - -use zksync_config::configs::{ - api::Web3JsonRpcConfig, - chain::{NetworkConfig, StateKeeperConfig}, - ContractsConfig, -}; -use zksync_dal::ConnectionPool; -use zksync_health_check::CheckHealth; -use zksync_state::PostgresStorageCaches; -use zksync_types::{L1BatchNumber, U64}; -use zksync_web3_decl::{ - jsonrpsee::http_client::HttpClient, - namespaces::{EthNamespaceClient, ZksNamespaceClient}, -}; - -use super::*; -use crate::{ - api_server::tx_sender::TxSenderConfig, - genesis::{ensure_genesis_state, GenesisParams}, -}; - -const TEST_TIMEOUT: Duration = Duration::from_secs(5); -const POLL_INTERVAL: Duration = Duration::from_millis(50); - -/// Mock [`L1GasPriceProvider`] that returns a constant value. -struct MockL1GasPriceProvider(u64); - -impl L1GasPriceProvider for MockL1GasPriceProvider { - fn estimate_effective_gas_price(&self) -> u64 { - self.0 - } -} - -impl ApiServerHandles { - /// Waits until the server health check reports the ready state. - pub(crate) async fn wait_until_ready(&self) { - let started_at = Instant::now(); - loop { - assert!( - started_at.elapsed() <= TEST_TIMEOUT, - "Timed out waiting for API server" - ); - let health = self.health_check.check_health().await; - if health.status().is_ready() { - break; - } - tokio::time::sleep(POLL_INTERVAL).await; - } - } - - pub(crate) async fn shutdown(self) { - let stop_server = async { - for task in self.tasks { - task.await - .expect("Server panicked") - .expect("Server terminated with error"); - } - }; - tokio::time::timeout(TEST_TIMEOUT, stop_server) - .await - .unwrap(); - } -} - -pub(crate) async fn spawn_http_server( - network_config: &NetworkConfig, - pool: ConnectionPool, - stop_receiver: watch::Receiver, -) -> ApiServerHandles { - let contracts_config = ContractsConfig::for_tests(); - let web3_config = Web3JsonRpcConfig::for_tests(); - let state_keeper_config = StateKeeperConfig::for_tests(); - let api_config = InternalApiConfig::new(network_config, &web3_config, &contracts_config); - let tx_sender_config = - TxSenderConfig::new(&state_keeper_config, &web3_config, api_config.l2_chain_id); - - let storage_caches = PostgresStorageCaches::new(1, 1); - let gas_adjuster = Arc::new(MockL1GasPriceProvider(1)); - let (tx_sender, vm_barrier) = crate::build_tx_sender( - &tx_sender_config, - &web3_config, - &state_keeper_config, - pool.clone(), - pool.clone(), - gas_adjuster, - storage_caches, - ) - .await; - - ApiBuilder::jsonrpsee_backend(api_config, pool) - .http(0) // Assign random port - .with_threads(1) - .with_tx_sender(tx_sender, vm_barrier) - .enable_api_namespaces(Namespace::NON_DEBUG.to_vec()) - .build(stop_receiver) - .await - .expect("Failed spawning JSON-RPC server") -} - -#[tokio::test] -async fn http_server_can_start() { - let pool = ConnectionPool::test_pool().await; - let network_config = NetworkConfig::for_tests(); - let mut storage = pool.access_storage().await.unwrap(); - if storage.blocks_dal().is_genesis_needed().await.unwrap() { - ensure_genesis_state( - &mut storage, - network_config.zksync_network_id, - &GenesisParams::mock(), - ) - .await - .unwrap(); - } - drop(storage); - - let (stop_sender, stop_receiver) = watch::channel(false); - let server_handles = spawn_http_server(&network_config, pool, stop_receiver).await; - server_handles.wait_until_ready().await; - - test_http_server_methods(server_handles.local_addr).await; - - stop_sender.send_replace(true); - server_handles.shutdown().await; -} - -async fn test_http_server_methods(local_addr: SocketAddr) { - let client = ::builder() - .build(format!("http://{local_addr}/")) - .unwrap(); - let block_number = client.get_block_number().await.unwrap(); - assert_eq!(block_number, U64::from(0)); - - let l1_batch_number = client.get_l1_batch_number().await.unwrap(); - assert_eq!(l1_batch_number, U64::from(0)); - - let genesis_l1_batch = client - .get_l1_batch_details(L1BatchNumber(0)) - .await - .unwrap() - .unwrap(); - assert!(genesis_l1_batch.base.root_hash.is_some()); -} diff --git a/core/lib/zksync_core/src/api_server/web3/tests/mod.rs b/core/lib/zksync_core/src/api_server/web3/tests/mod.rs new file mode 100644 index 000000000000..12bb6481213d --- /dev/null +++ b/core/lib/zksync_core/src/api_server/web3/tests/mod.rs @@ -0,0 +1,503 @@ +use assert_matches::assert_matches; +use async_trait::async_trait; +use tokio::sync::watch; + +use std::{sync::Arc, time::Instant}; + +use zksync_config::configs::{ + api::Web3JsonRpcConfig, + chain::{NetworkConfig, StateKeeperConfig}, + ContractsConfig, +}; +use zksync_contracts::BaseSystemContractsHashes; +use zksync_dal::{transactions_dal::L2TxSubmissionResult, ConnectionPool}; +use zksync_health_check::CheckHealth; +use zksync_state::PostgresStorageCaches; +use zksync_types::{ + block::MiniblockHeader, fee::TransactionExecutionMetrics, tx::IncludedTxLocation, Address, + L1BatchNumber, ProtocolVersionId, VmEvent, H256, U64, +}; +use zksync_web3_decl::{ + jsonrpsee::{core::Error as RpcError, http_client::HttpClient, types::error::ErrorCode}, + namespaces::{EthNamespaceClient, ZksNamespaceClient}, + types::FilterChanges, +}; + +mod ws; + +use super::{metrics::ApiTransportLabel, *}; +use crate::{ + api_server::tx_sender::TxSenderConfig, + genesis::{ensure_genesis_state, GenesisParams}, + state_keeper::tests::create_l2_transaction, +}; + +const TEST_TIMEOUT: Duration = Duration::from_secs(10); +const POLL_INTERVAL: Duration = Duration::from_millis(50); + +/// Mock [`L1GasPriceProvider`] that returns a constant value. +struct MockL1GasPriceProvider(u64); + +impl L1GasPriceProvider for MockL1GasPriceProvider { + fn estimate_effective_gas_price(&self) -> u64 { + self.0 + } +} + +impl ApiServerHandles { + /// Waits until the server health check reports the ready state. + pub(crate) async fn wait_until_ready(&self) { + let started_at = Instant::now(); + loop { + assert!( + started_at.elapsed() <= TEST_TIMEOUT, + "Timed out waiting for API server" + ); + let health = self.health_check.check_health().await; + if health.status().is_ready() { + break; + } + tokio::time::sleep(POLL_INTERVAL).await; + } + } + + pub(crate) async fn shutdown(self) { + let stop_server = async { + for task in self.tasks { + // FIXME(PLA-481): avoid these errors (by spawning notifier tasks on server runtime?) + if let Err(err) = task.await.expect("Server panicked") { + let err = err.root_cause().to_string(); + assert!(err.contains("Tokio 1.x context was found")); + } + } + }; + tokio::time::timeout(TEST_TIMEOUT, stop_server) + .await + .unwrap(); + } +} + +pub(crate) async fn spawn_http_server( + network_config: &NetworkConfig, + pool: ConnectionPool, + stop_receiver: watch::Receiver, +) -> ApiServerHandles { + spawn_server(ApiTransportLabel::Http, network_config, pool, stop_receiver) + .await + .0 +} + +async fn spawn_ws_server( + network_config: &NetworkConfig, + pool: ConnectionPool, + stop_receiver: watch::Receiver, +) -> (ApiServerHandles, mpsc::UnboundedReceiver) { + spawn_server(ApiTransportLabel::Ws, network_config, pool, stop_receiver).await +} + +async fn spawn_server( + transport: ApiTransportLabel, + network_config: &NetworkConfig, + pool: ConnectionPool, + stop_receiver: watch::Receiver, +) -> (ApiServerHandles, mpsc::UnboundedReceiver) { + let contracts_config = ContractsConfig::for_tests(); + let web3_config = Web3JsonRpcConfig::for_tests(); + let state_keeper_config = StateKeeperConfig::for_tests(); + let api_config = InternalApiConfig::new(network_config, &web3_config, &contracts_config); + let tx_sender_config = + TxSenderConfig::new(&state_keeper_config, &web3_config, api_config.l2_chain_id); + + let storage_caches = PostgresStorageCaches::new(1, 1); + let gas_adjuster = Arc::new(MockL1GasPriceProvider(1)); + let (tx_sender, vm_barrier) = crate::build_tx_sender( + &tx_sender_config, + &web3_config, + &state_keeper_config, + pool.clone(), + pool.clone(), + gas_adjuster, + storage_caches, + ) + .await; + let (pub_sub_events_sender, pub_sub_events_receiver) = mpsc::unbounded_channel(); + + let server_builder = match transport { + ApiTransportLabel::Http => ApiBuilder::jsonrpsee_backend(api_config, pool).http(0), + ApiTransportLabel::Ws => ApiBuilder::jsonrpc_backend(api_config, pool) + .ws(0) + .with_polling_interval(POLL_INTERVAL) + .with_subscriptions_limit(100), + }; + let server_handles = server_builder + .with_threads(1) + .with_tx_sender(tx_sender, vm_barrier) + .with_pub_sub_events(pub_sub_events_sender) + .enable_api_namespaces(Namespace::NON_DEBUG.to_vec()) + .build(stop_receiver) + .await + .expect("Failed spawning JSON-RPC server"); + (server_handles, pub_sub_events_receiver) +} + +#[async_trait] +trait HttpTest { + async fn test(&self, client: &HttpClient, pool: &ConnectionPool) -> anyhow::Result<()>; +} + +async fn test_http_server(test: impl HttpTest) { + let pool = ConnectionPool::test_pool().await; + let network_config = NetworkConfig::for_tests(); + let mut storage = pool.access_storage().await.unwrap(); + if storage.blocks_dal().is_genesis_needed().await.unwrap() { + ensure_genesis_state( + &mut storage, + network_config.zksync_network_id, + &GenesisParams::mock(), + ) + .await + .unwrap(); + } + drop(storage); + + let (stop_sender, stop_receiver) = watch::channel(false); + let server_handles = spawn_http_server(&network_config, pool.clone(), stop_receiver).await; + server_handles.wait_until_ready().await; + + let client = ::builder() + .build(format!("http://{}/", server_handles.local_addr)) + .unwrap(); + test.test(&client, &pool).await.unwrap(); + + stop_sender.send_replace(true); + server_handles.shutdown().await; +} + +fn assert_logs_match(actual_logs: &[api::Log], expected_logs: &[&VmEvent]) { + assert_eq!(actual_logs.len(), expected_logs.len()); + for (actual_log, &expected_log) in actual_logs.iter().zip(expected_logs) { + assert_eq!(actual_log.address, expected_log.address); + assert_eq!(actual_log.topics, expected_log.indexed_topics); + assert_eq!(actual_log.data.0, expected_log.value); + } +} + +fn create_miniblock(number: u32) -> MiniblockHeader { + MiniblockHeader { + number: MiniblockNumber(number), + timestamp: number.into(), + hash: H256::from_low_u64_be(number.into()), + l1_tx_count: 0, + l2_tx_count: 0, + base_fee_per_gas: 100, + l1_gas_price: 100, + l2_fair_gas_price: 100, + base_system_contracts_hashes: BaseSystemContractsHashes::default(), + protocol_version: Some(ProtocolVersionId::latest()), + virtual_blocks: 1, + } +} + +async fn store_block(pool: &ConnectionPool) -> anyhow::Result<(MiniblockHeader, H256)> { + let mut storage = pool.access_storage().await?; + let new_tx = create_l2_transaction(1, 2); + let new_tx_hash = new_tx.hash(); + let tx_submission_result = storage + .transactions_dal() + .insert_transaction_l2(new_tx, TransactionExecutionMetrics::default()) + .await; + assert_matches!(tx_submission_result, L2TxSubmissionResult::Added); + + let new_miniblock = create_miniblock(1); + storage + .blocks_dal() + .insert_miniblock(&new_miniblock) + .await?; + Ok((new_miniblock, new_tx_hash)) +} + +async fn store_events( + storage: &mut StorageProcessor<'_>, + miniblock_number: u32, + start_idx: u32, +) -> anyhow::Result<(IncludedTxLocation, Vec)> { + let new_miniblock = create_miniblock(miniblock_number); + storage + .blocks_dal() + .insert_miniblock(&new_miniblock) + .await?; + let tx_location = IncludedTxLocation { + tx_hash: H256::repeat_byte(1), + tx_index_in_miniblock: 0, + tx_initiator_address: Address::repeat_byte(2), + }; + let events = vec![ + // Matches address, doesn't match topics + VmEvent { + location: (L1BatchNumber(1), start_idx), + address: Address::repeat_byte(23), + indexed_topics: vec![], + value: start_idx.to_le_bytes().to_vec(), + }, + // Doesn't match address, matches topics + VmEvent { + location: (L1BatchNumber(1), start_idx + 1), + address: Address::zero(), + indexed_topics: vec![H256::repeat_byte(42)], + value: (start_idx + 1).to_le_bytes().to_vec(), + }, + // Doesn't match address or topics + VmEvent { + location: (L1BatchNumber(1), start_idx + 2), + address: Address::zero(), + indexed_topics: vec![H256::repeat_byte(1), H256::repeat_byte(42)], + value: (start_idx + 2).to_le_bytes().to_vec(), + }, + // Matches both address and topics + VmEvent { + location: (L1BatchNumber(1), start_idx + 3), + address: Address::repeat_byte(23), + indexed_topics: vec![H256::repeat_byte(42), H256::repeat_byte(111)], + value: (start_idx + 3).to_le_bytes().to_vec(), + }, + ]; + storage + .events_dal() + .save_events( + MiniblockNumber(miniblock_number), + &[(tx_location, events.iter().collect())], + ) + .await; + Ok((tx_location, events)) +} + +#[derive(Debug)] +struct HttpServerBasics; + +#[async_trait] +impl HttpTest for HttpServerBasics { + async fn test(&self, client: &HttpClient, _pool: &ConnectionPool) -> anyhow::Result<()> { + let block_number = client.get_block_number().await?; + assert_eq!(block_number, U64::from(0)); + + let l1_batch_number = client.get_l1_batch_number().await?; + assert_eq!(l1_batch_number, U64::from(0)); + + let genesis_l1_batch = client + .get_l1_batch_details(L1BatchNumber(0)) + .await? + .context("No genesis L1 batch")?; + assert!(genesis_l1_batch.base.root_hash.is_some()); + Ok(()) + } +} + +#[tokio::test] +async fn http_server_basics() { + test_http_server(HttpServerBasics).await; +} + +#[derive(Debug)] +struct BasicFilterChanges; + +#[async_trait] +impl HttpTest for BasicFilterChanges { + async fn test(&self, client: &HttpClient, pool: &ConnectionPool) -> anyhow::Result<()> { + let block_filter_id = client.new_block_filter().await?; + let tx_filter_id = client.new_pending_transaction_filter().await?; + + let (new_miniblock, new_tx_hash) = store_block(pool).await?; + + let block_filter_changes = client.get_filter_changes(block_filter_id).await?; + assert_matches!( + block_filter_changes, + FilterChanges::Hashes(hashes) if hashes == [new_miniblock.hash] + ); + let block_filter_changes = client.get_filter_changes(block_filter_id).await?; + assert_matches!(block_filter_changes, FilterChanges::Hashes(hashes) if hashes.is_empty()); + + let tx_filter_changes = client.get_filter_changes(tx_filter_id).await?; + assert_matches!( + tx_filter_changes, + FilterChanges::Hashes(hashes) if hashes == [new_tx_hash] + ); + let tx_filter_changes = client.get_filter_changes(tx_filter_id).await?; + assert_matches!(tx_filter_changes, FilterChanges::Hashes(hashes) if hashes.is_empty()); + + // Check uninstalling the filter. + let removed = client.uninstall_filter(block_filter_id).await?; + assert!(removed); + let removed = client.uninstall_filter(block_filter_id).await?; + assert!(!removed); + + let err = client + .get_filter_changes(block_filter_id) + .await + .unwrap_err(); + assert_matches!(err, RpcError::Call(err) if err.code() == ErrorCode::InvalidParams.code()); + Ok(()) + } +} + +#[tokio::test] +async fn basic_filter_changes() { + test_http_server(BasicFilterChanges).await; +} + +#[derive(Debug)] +struct LogFilterChanges; + +#[async_trait] +impl HttpTest for LogFilterChanges { + async fn test(&self, client: &HttpClient, pool: &ConnectionPool) -> anyhow::Result<()> { + let all_logs_filter_id = client.new_filter(Filter::default()).await?; + let address_filter = Filter { + address: Some(Address::repeat_byte(23).into()), + ..Filter::default() + }; + let address_filter_id = client.new_filter(address_filter).await?; + let topics_filter = Filter { + topics: Some(vec![Some(H256::repeat_byte(42).into())]), + ..Filter::default() + }; + let topics_filter_id = client.new_filter(topics_filter).await?; + + let mut storage = pool.access_storage().await?; + let (_, events) = store_events(&mut storage, 1, 0).await?; + drop(storage); + let events: Vec<_> = events.iter().collect(); + + let all_logs = client.get_filter_changes(all_logs_filter_id).await?; + let FilterChanges::Logs(all_logs) = all_logs else { + panic!("Unexpected getFilterChanges output: {:?}", all_logs); + }; + assert_logs_match(&all_logs, &events); + + let address_logs = client.get_filter_changes(address_filter_id).await?; + let FilterChanges::Logs(address_logs) = address_logs else { + panic!("Unexpected getFilterChanges output: {:?}", address_logs); + }; + assert_logs_match(&address_logs, &[events[0], events[3]]); + + let topics_logs = client.get_filter_changes(topics_filter_id).await?; + let FilterChanges::Logs(topics_logs) = topics_logs else { + panic!("Unexpected getFilterChanges output: {:?}", topics_logs); + }; + assert_logs_match(&topics_logs, &[events[1], events[3]]); + + let new_all_logs = client.get_filter_changes(all_logs_filter_id).await?; + let FilterChanges::Logs(new_all_logs) = new_all_logs else { + panic!("Unexpected getFilterChanges output: {:?}", new_all_logs); + }; + assert_eq!(new_all_logs, all_logs); // FIXME(#546): update test after behavior is fixed + Ok(()) + } +} + +#[tokio::test] +async fn log_filter_changes() { + test_http_server(LogFilterChanges).await; +} + +#[derive(Debug)] +struct LogFilterChangesWithBlockBoundaries; + +#[async_trait] +impl HttpTest for LogFilterChangesWithBlockBoundaries { + async fn test(&self, client: &HttpClient, pool: &ConnectionPool) -> anyhow::Result<()> { + let lower_bound_filter = Filter { + from_block: Some(api::BlockNumber::Number(2.into())), + ..Filter::default() + }; + let lower_bound_filter_id = client.new_filter(lower_bound_filter).await?; + let upper_bound_filter = Filter { + to_block: Some(api::BlockNumber::Number(1.into())), + ..Filter::default() + }; + let upper_bound_filter_id = client.new_filter(upper_bound_filter).await?; + let bounded_filter = Filter { + from_block: Some(api::BlockNumber::Number(1.into())), + to_block: Some(api::BlockNumber::Number(1.into())), + ..Filter::default() + }; + let bounded_filter_id = client.new_filter(bounded_filter).await?; + + let mut storage = pool.access_storage().await?; + let (_, events) = store_events(&mut storage, 1, 0).await?; + drop(storage); + let events: Vec<_> = events.iter().collect(); + + let lower_bound_logs = client.get_filter_changes(lower_bound_filter_id).await?; + assert_matches!( + lower_bound_logs, + FilterChanges::Hashes(hashes) if hashes.is_empty() + ); + // ^ Since `FilterChanges` is serialized w/o a tag, an empty array will be deserialized + // as `Hashes(_)` (the first declared variant). + + let upper_bound_logs = client.get_filter_changes(upper_bound_filter_id).await?; + let FilterChanges::Logs(upper_bound_logs) = upper_bound_logs else { + panic!("Unexpected getFilterChanges output: {:?}", upper_bound_logs); + }; + assert_logs_match(&upper_bound_logs, &events); + let bounded_logs = client.get_filter_changes(bounded_filter_id).await?; + let FilterChanges::Logs(bounded_logs) = bounded_logs else { + panic!("Unexpected getFilterChanges output: {:?}", bounded_logs); + }; + assert_eq!(bounded_logs, upper_bound_logs); + + // Add another miniblock with events to the storage. + let mut storage = pool.access_storage().await?; + let (_, new_events) = store_events(&mut storage, 2, 4).await?; + drop(storage); + let new_events: Vec<_> = new_events.iter().collect(); + + let lower_bound_logs = client.get_filter_changes(lower_bound_filter_id).await?; + let FilterChanges::Logs(lower_bound_logs) = lower_bound_logs else { + panic!("Unexpected getFilterChanges output: {:?}", lower_bound_logs); + }; + assert_logs_match(&lower_bound_logs, &new_events); + + // FIXME(#546): update test after behavior is fixed + let new_upper_bound_logs = client.get_filter_changes(upper_bound_filter_id).await?; + assert_eq!(new_upper_bound_logs, FilterChanges::Logs(upper_bound_logs)); + let new_bounded_logs = client.get_filter_changes(bounded_filter_id).await?; + assert_eq!(new_bounded_logs, FilterChanges::Logs(bounded_logs)); + + // Add miniblock #3. It should not be picked up by the bounded and upper bound filters, + // and should be picked up by the lower bound filter. + let mut storage = pool.access_storage().await?; + let (_, new_events) = store_events(&mut storage, 3, 8).await?; + drop(storage); + let new_events: Vec<_> = new_events.iter().collect(); + + let bounded_logs = client.get_filter_changes(bounded_filter_id).await?; + let FilterChanges::Logs(bounded_logs) = bounded_logs else { + panic!("Unexpected getFilterChanges output: {:?}", bounded_logs); + }; + assert!(bounded_logs + .iter() + .all(|log| log.block_number.unwrap() < 3.into())); + + let upper_bound_logs = client.get_filter_changes(upper_bound_filter_id).await?; + let FilterChanges::Logs(upper_bound_logs) = upper_bound_logs else { + panic!("Unexpected getFilterChanges output: {:?}", upper_bound_logs); + }; + assert!(upper_bound_logs + .iter() + .all(|log| log.block_number.unwrap() < 3.into())); + + let lower_bound_logs = client.get_filter_changes(lower_bound_filter_id).await?; + let FilterChanges::Logs(lower_bound_logs) = lower_bound_logs else { + panic!("Unexpected getFilterChanges output: {:?}", lower_bound_logs); + }; + let start_idx = lower_bound_logs.len() - 4; + assert_logs_match(&lower_bound_logs[start_idx..], &new_events); + Ok(()) + } +} + +#[tokio::test] +async fn log_filter_changes_with_block_boundaries() { + test_http_server(LogFilterChangesWithBlockBoundaries).await; +} diff --git a/core/lib/zksync_core/src/api_server/web3/tests/ws.rs b/core/lib/zksync_core/src/api_server/web3/tests/ws.rs new file mode 100644 index 000000000000..704dfef67005 --- /dev/null +++ b/core/lib/zksync_core/src/api_server/web3/tests/ws.rs @@ -0,0 +1,466 @@ +//! WS-related tests. + +use async_trait::async_trait; +use tokio::sync::watch; + +use zksync_config::configs::chain::NetworkConfig; +use zksync_dal::ConnectionPool; +use zksync_types::{api, Address, L1BatchNumber, H256, U64}; +use zksync_web3_decl::{ + jsonrpsee::{ + core::client::{Subscription, SubscriptionClientT}, + rpc_params, + ws_client::{WsClient, WsClientBuilder}, + }, + namespaces::{EthNamespaceClient, ZksNamespaceClient}, + types::{BlockHeader, PubSubFilter}, +}; + +use super::*; +use crate::api_server::web3::metrics::SubscriptionType; + +#[allow(clippy::needless_pass_by_ref_mut)] // false positive +async fn wait_for_subscription( + events: &mut mpsc::UnboundedReceiver, + sub_type: SubscriptionType, +) { + let wait_future = tokio::time::timeout(TEST_TIMEOUT, async { + loop { + let event = events + .recv() + .await + .expect("Events emitter unexpectedly dropped"); + if matches!(event, PubSubEvent::Subscribed(ty) if ty == sub_type) { + break; + } else { + tracing::trace!(?event, "Skipping event"); + } + } + }); + wait_future + .await + .expect("Timed out waiting for subscription") +} + +#[allow(clippy::needless_pass_by_ref_mut)] // false positive +async fn wait_for_notifier( + events: &mut mpsc::UnboundedReceiver, + sub_type: SubscriptionType, +) { + let wait_future = tokio::time::timeout(TEST_TIMEOUT, async { + loop { + let event = events + .recv() + .await + .expect("Events emitter unexpectedly dropped"); + if matches!(event, PubSubEvent::NotifyIterationFinished(ty) if ty == sub_type) { + break; + } else { + tracing::trace!(?event, "Skipping event"); + } + } + }); + wait_future.await.expect("Timed out waiting for notifier") +} + +#[async_trait] +trait WsTest { + async fn test( + &self, + client: &WsClient, + pool: &ConnectionPool, + pub_sub_events: mpsc::UnboundedReceiver, + ) -> anyhow::Result<()>; +} + +async fn test_ws_server(test: impl WsTest) { + let pool = ConnectionPool::test_pool().await; + let network_config = NetworkConfig::for_tests(); + let mut storage = pool.access_storage().await.unwrap(); + if storage.blocks_dal().is_genesis_needed().await.unwrap() { + ensure_genesis_state( + &mut storage, + network_config.zksync_network_id, + &GenesisParams::mock(), + ) + .await + .unwrap(); + } + drop(storage); + + let (stop_sender, stop_receiver) = watch::channel(false); + let (server_handles, pub_sub_events) = + spawn_ws_server(&network_config, pool.clone(), stop_receiver).await; + server_handles.wait_until_ready().await; + + let client = WsClientBuilder::default() + .build(format!("ws://{}", server_handles.local_addr)) + .await + .unwrap(); + test.test(&client, &pool, pub_sub_events).await.unwrap(); + + stop_sender.send_replace(true); + server_handles.shutdown().await; +} + +#[derive(Debug)] +struct WsServerCanStart; + +#[async_trait] +impl WsTest for WsServerCanStart { + async fn test( + &self, + client: &WsClient, + _pool: &ConnectionPool, + _pub_sub_events: mpsc::UnboundedReceiver, + ) -> anyhow::Result<()> { + let block_number = client.get_block_number().await?; + assert_eq!(block_number, U64::from(0)); + + let l1_batch_number = client.get_l1_batch_number().await?; + assert_eq!(l1_batch_number, U64::from(0)); + + let genesis_l1_batch = client + .get_l1_batch_details(L1BatchNumber(0)) + .await? + .context("missing genesis L1 batch")?; + assert!(genesis_l1_batch.base.root_hash.is_some()); + Ok(()) + } +} + +#[tokio::test] +async fn ws_server_can_start() { + test_ws_server(WsServerCanStart).await; +} + +#[derive(Debug)] +struct BasicSubscriptions; + +#[async_trait] +impl WsTest for BasicSubscriptions { + async fn test( + &self, + client: &WsClient, + pool: &ConnectionPool, + mut pub_sub_events: mpsc::UnboundedReceiver, + ) -> anyhow::Result<()> { + // Wait for the notifiers to get initialized so that they don't skip notifications + // for the created subscriptions. + wait_for_notifier(&mut pub_sub_events, SubscriptionType::Blocks).await; + wait_for_notifier(&mut pub_sub_events, SubscriptionType::Txs).await; + + let params = rpc_params!["newHeads"]; + let mut blocks_subscription = client + .subscribe::("eth_subscribe", params, "eth_unsubscribe") + .await?; + wait_for_subscription(&mut pub_sub_events, SubscriptionType::Blocks).await; + + let params = rpc_params!["newPendingTransactions"]; + let mut txs_subscription = client + .subscribe::("eth_subscribe", params, "eth_unsubscribe") + .await?; + wait_for_subscription(&mut pub_sub_events, SubscriptionType::Txs).await; + + let (new_miniblock, new_tx_hash) = store_block(pool).await?; + + let received_tx_hash = tokio::time::timeout(TEST_TIMEOUT, txs_subscription.next()) + .await + .context("Timed out waiting for new tx hash")? + .context("Pending txs subscription terminated")??; + assert_eq!(received_tx_hash, new_tx_hash); + let received_block_header = tokio::time::timeout(TEST_TIMEOUT, blocks_subscription.next()) + .await + .context("Timed out waiting for new block hash")? + .context("New blocks subscription terminated")??; + assert_eq!(received_block_header.number, Some(1.into())); + assert_eq!(received_block_header.hash, Some(new_miniblock.hash)); + assert_eq!(received_block_header.timestamp, 1.into()); + blocks_subscription.unsubscribe().await?; + Ok(()) + } +} + +#[tokio::test] +async fn basic_subscriptions() { + test_ws_server(BasicSubscriptions).await; +} + +#[derive(Debug)] +struct LogSubscriptions; + +#[derive(Debug)] +struct Subscriptions { + all_logs_subscription: Subscription, + address_subscription: Subscription, + topic_subscription: Subscription, +} + +impl Subscriptions { + async fn new( + client: &WsClient, + pub_sub_events: &mut mpsc::UnboundedReceiver, + ) -> anyhow::Result { + // Wait for the notifier to get initialized so that it doesn't skip notifications + // for the created subscriptions. + wait_for_notifier(pub_sub_events, SubscriptionType::Logs).await; + + let params = rpc_params!["logs"]; + let all_logs_subscription = client + .subscribe::("eth_subscribe", params, "eth_unsubscribe") + .await?; + let address_filter = PubSubFilter { + address: Some(Address::repeat_byte(23).into()), + topics: None, + }; + let params = rpc_params!["logs", address_filter]; + let address_subscription = client + .subscribe::("eth_subscribe", params, "eth_unsubscribe") + .await?; + let topic_filter = PubSubFilter { + address: None, + topics: Some(vec![Some(H256::repeat_byte(42).into())]), + }; + let params = rpc_params!["logs", topic_filter]; + let topic_subscription = client + .subscribe::("eth_subscribe", params, "eth_unsubscribe") + .await?; + for _ in 0..3 { + wait_for_subscription(pub_sub_events, SubscriptionType::Logs).await; + } + + Ok(Self { + all_logs_subscription, + address_subscription, + topic_subscription, + }) + } +} + +#[async_trait] +impl WsTest for LogSubscriptions { + async fn test( + &self, + client: &WsClient, + pool: &ConnectionPool, + mut pub_sub_events: mpsc::UnboundedReceiver, + ) -> anyhow::Result<()> { + let Subscriptions { + mut all_logs_subscription, + mut address_subscription, + mut topic_subscription, + } = Subscriptions::new(client, &mut pub_sub_events).await?; + + let mut storage = pool.access_storage().await?; + let (tx_location, events) = store_events(&mut storage, 1, 0).await?; + drop(storage); + let events: Vec<_> = events.iter().collect(); + + let all_logs = collect_logs(&mut all_logs_subscription, 4).await?; + for (i, log) in all_logs.iter().enumerate() { + assert_eq!(log.transaction_index, Some(0.into())); + assert_eq!(log.log_index, Some(i.into())); + assert_eq!(log.transaction_hash, Some(tx_location.tx_hash)); + assert_eq!(log.block_number, Some(1.into())); + } + assert_logs_match(&all_logs, &events); + + let address_logs = collect_logs(&mut address_subscription, 2).await?; + assert_logs_match(&address_logs, &[events[0], events[3]]); + + let topic_logs = collect_logs(&mut topic_subscription, 2).await?; + assert_logs_match(&topic_logs, &[events[1], events[3]]); + + wait_for_notifier(&mut pub_sub_events, SubscriptionType::Logs).await; + + // Check that no new notifications were sent to subscribers. + tokio::time::timeout(POLL_INTERVAL, all_logs_subscription.next()) + .await + .unwrap_err(); + tokio::time::timeout(POLL_INTERVAL, address_subscription.next()) + .await + .unwrap_err(); + tokio::time::timeout(POLL_INTERVAL, topic_subscription.next()) + .await + .unwrap_err(); + Ok(()) + } +} + +async fn collect_logs( + sub: &mut Subscription, + expected_count: usize, +) -> anyhow::Result> { + let mut logs = Vec::with_capacity(expected_count); + for _ in 0..expected_count { + let log = tokio::time::timeout(TEST_TIMEOUT, sub.next()) + .await + .context("Timed out waiting for new log")? + .context("Logs subscription terminated")??; + logs.push(log); + } + Ok(logs) +} + +#[tokio::test] +async fn log_subscriptions() { + test_ws_server(LogSubscriptions).await; +} + +#[derive(Debug)] +struct LogSubscriptionsWithNewBlock; + +#[async_trait] +impl WsTest for LogSubscriptionsWithNewBlock { + async fn test( + &self, + client: &WsClient, + pool: &ConnectionPool, + mut pub_sub_events: mpsc::UnboundedReceiver, + ) -> anyhow::Result<()> { + let Subscriptions { + mut all_logs_subscription, + mut address_subscription, + .. + } = Subscriptions::new(client, &mut pub_sub_events).await?; + + let mut storage = pool.access_storage().await?; + let (_, events) = store_events(&mut storage, 1, 0).await?; + drop(storage); + let events: Vec<_> = events.iter().collect(); + + let all_logs = collect_logs(&mut all_logs_subscription, 4).await?; + assert_logs_match(&all_logs, &events); + + // Create a new block and wait for the pub-sub notifier to run. + let mut storage = pool.access_storage().await?; + let (_, new_events) = store_events(&mut storage, 2, 4).await?; + drop(storage); + let new_events: Vec<_> = new_events.iter().collect(); + + let all_new_logs = collect_logs(&mut all_logs_subscription, 4).await?; + assert_logs_match(&all_new_logs, &new_events); + + let address_logs = collect_logs(&mut address_subscription, 4).await?; + assert_logs_match( + &address_logs, + &[events[0], events[3], new_events[0], new_events[3]], + ); + Ok(()) + } +} + +#[tokio::test] +async fn log_subscriptions_with_new_block() { + test_ws_server(LogSubscriptionsWithNewBlock).await; +} + +#[derive(Debug)] +struct LogSubscriptionsWithManyBlocks; + +#[async_trait] +impl WsTest for LogSubscriptionsWithManyBlocks { + async fn test( + &self, + client: &WsClient, + pool: &ConnectionPool, + mut pub_sub_events: mpsc::UnboundedReceiver, + ) -> anyhow::Result<()> { + let Subscriptions { + mut all_logs_subscription, + mut address_subscription, + .. + } = Subscriptions::new(client, &mut pub_sub_events).await?; + + // Add two blocks in the storage atomically. + let mut storage = pool.access_storage().await?; + let mut transaction = storage.start_transaction().await?; + let (_, events) = store_events(&mut transaction, 1, 0).await?; + let events: Vec<_> = events.iter().collect(); + let (_, new_events) = store_events(&mut transaction, 2, 4).await?; + let new_events: Vec<_> = new_events.iter().collect(); + transaction.commit().await?; + drop(storage); + + let all_logs = collect_logs(&mut all_logs_subscription, 4).await?; + assert_logs_match(&all_logs, &events); + let all_new_logs = collect_logs(&mut all_logs_subscription, 4).await?; + assert_logs_match(&all_new_logs, &new_events); + + let address_logs = collect_logs(&mut address_subscription, 4).await?; + assert_logs_match( + &address_logs, + &[events[0], events[3], new_events[0], new_events[3]], + ); + Ok(()) + } +} + +#[tokio::test] +async fn log_subscriptions_with_many_new_blocks_at_once() { + test_ws_server(LogSubscriptionsWithManyBlocks).await; +} + +#[derive(Debug)] +struct LogSubscriptionsWithDelay; + +#[async_trait] +impl WsTest for LogSubscriptionsWithDelay { + async fn test( + &self, + client: &WsClient, + pool: &ConnectionPool, + mut pub_sub_events: mpsc::UnboundedReceiver, + ) -> anyhow::Result<()> { + // Store a miniblock w/o subscriptions being present. + let mut storage = pool.access_storage().await?; + store_events(&mut storage, 1, 0).await?; + drop(storage); + + while pub_sub_events.try_recv().is_ok() { + // Drain all existing pub-sub events. + } + wait_for_notifier(&mut pub_sub_events, SubscriptionType::Logs).await; + + let params = rpc_params!["logs"]; + let mut all_logs_subscription = client + .subscribe::("eth_subscribe", params, "eth_unsubscribe") + .await?; + let address_and_topic_filter = PubSubFilter { + address: Some(Address::repeat_byte(23).into()), + topics: Some(vec![Some(H256::repeat_byte(42).into())]), + }; + let params = rpc_params!["logs", address_and_topic_filter]; + let mut address_and_topic_subscription = client + .subscribe::("eth_subscribe", params, "eth_unsubscribe") + .await?; + for _ in 0..2 { + wait_for_subscription(&mut pub_sub_events, SubscriptionType::Logs).await; + } + + let mut storage = pool.access_storage().await?; + let (_, new_events) = store_events(&mut storage, 2, 4).await?; + drop(storage); + let new_events: Vec<_> = new_events.iter().collect(); + + let all_logs = collect_logs(&mut all_logs_subscription, 4).await?; + assert_logs_match(&all_logs, &new_events); + let address_and_topic_logs = collect_logs(&mut address_and_topic_subscription, 1).await?; + assert_logs_match(&address_and_topic_logs, &[new_events[3]]); + + // Check the behavior of remaining subscriptions if a subscription is dropped. + all_logs_subscription.unsubscribe().await?; + let mut storage = pool.access_storage().await?; + let (_, new_events) = store_events(&mut storage, 3, 8).await?; + drop(storage); + + let address_and_topic_logs = collect_logs(&mut address_and_topic_subscription, 1).await?; + assert_logs_match(&address_and_topic_logs, &[&new_events[3]]); + Ok(()) + } +} + +#[tokio::test] +async fn log_subscriptions_with_delay() { + test_ws_server(LogSubscriptionsWithDelay).await; +} From 06f510d00f855ddafaebb504f7ea799700221072 Mon Sep 17 00:00:00 2001 From: Lech <88630083+Artemka374@users.noreply.github.com> Date: Wed, 29 Nov 2023 21:03:37 +0200 Subject: [PATCH 007/268] feat: Restore commitment test in Boojum integration (#539) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Restored commitmenet test ## Why ❔ During Boojum integration the commitment_test was commented out, since the commitment schema has changed. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --- core/lib/types/src/commitment.rs | 2 -- .../zksync_testharness_test.json | 20 +++++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/core/lib/types/src/commitment.rs b/core/lib/types/src/commitment.rs index 29750a5c77be..7925a37d92fb 100644 --- a/core/lib/types/src/commitment.rs +++ b/core/lib/types/src/commitment.rs @@ -719,8 +719,6 @@ mod tests { expected_outputs: ExpectedOutput, } - // TODO(PLA-568): restore this test - #[ignore] #[test] fn commitment_test() { let zksync_home = std::env::var("ZKSYNC_HOME").unwrap_or_else(|_| ".".into()); diff --git a/etc/commitment_tests/zksync_testharness_test.json b/etc/commitment_tests/zksync_testharness_test.json index 3240c3b4d9e7..6f5730144a02 100644 --- a/etc/commitment_tests/zksync_testharness_test.json +++ b/etc/commitment_tests/zksync_testharness_test.json @@ -56,19 +56,19 @@ ] }, "expected_outputs": { - "l2_l1_bytes": "0000000200000000000000000000000000000000000000000000800b000000000000000000000000000000000000000000000000000000006349f8b557a1099a71e51ea9cce8d89b5a9f1741d3a704e5258077c811223a8b604cdc8a000100000000000000000000000000000000000000008001cb99d29a1b4ffeaefdbf74b8b8b07c78e5e02b3100946f8d0463b79789086aff0000000000000000000000000000000000000000000000000000000000000001", - "l2_l1_linear_hash": "0x680f578a7b39e9f74385a3aabfb4cf054917f23aea9ae165d2afaac02fc9f3b8", - "l2_l1_root_hash": "0xcb5f7b72ab30095b81e2cd35c308a7a752fe59213475339b8a833e91bf731837", - "initial_writes_bytes": "00000002db1231bec2de6342908165662c0d968bb89db1e63211d92fa9547a7efb81499457a1099a71e51ea9cce8d89b5a9f1741d3a704e5258077c811223a8b604cdc8a098c669256db6fe36d87834ceae9c8161af6b72f8b1543b8a3ffacca5b9206af0000000000000000000000000000000000000000000000000000000000000064", - "repeated_writes_bytes": "0000000600000000000002920000000000000000000000000000000c0000000000000000000000000000001f000000000000003d000000000000000000000000000000000000000000000003aec912ce8057d164000000000000003e0000000000000000000000000000000000000000000000000003acf87e3e2200000000000000029400000000000000000000000000000000000000000000000001962afc49a1eb2e0000000000000028000000000000000000000000000000690000000000000000000000006349f8b5000000000000029800000000000000000000000000000000000000000000000000d3abb1bfb7be70", - "repeated_writes_hash": "0xdc5a883793479c779f5c99b0fca910deb20195d8ccf430afad05a9c2bd9f81bd", - "initial_writes_hash": "0xdcc4877ab0c07a79a16ae34de6fb7971a54128db0d11791fd5064bd6d03076c1", + "l2_l1_bytes": "00000000000000000000000000000000000000000000800b000000000000000000000000000000000000000000000000000000006349f8b557a1099a71e51ea9cce8d89b5a9f1741d3a704e5258077c811223a8b604cdc8a000100000000000000000000000000000000000000008001cb99d29a1b4ffeaefdbf74b8b8b07c78e5e02b3100946f8d0463b79789086aff0000000000000000000000000000000000000000000000000000000000000001", + "l2_l1_linear_hash": "0x3735b5ef78a8c9b9a88397148dc68860f183e75ff4e09a2b922554843dff6bde", + "l2_l1_root_hash": "0xf769ef2c8211398f31675bce8797ddc52dac8f4d22606e941a7d7561e1227dd1", + "initial_writes_bytes": "db1231bec2de6342908165662c0d968bb89db1e63211d92fa9547a7efb81499457a1099a71e51ea9cce8d89b5a9f1741d3a704e5258077c811223a8b604cdc8a098c669256db6fe36d87834ceae9c8161af6b72f8b1543b8a3ffacca5b9206af0000000000000000000000000000000000000000000000000000000000000064", + "repeated_writes_bytes": "00000000000002920000000000000000000000000000000c0000000000000000000000000000001f000000000000003d000000000000000000000000000000000000000000000003aec912ce8057d164000000000000003e0000000000000000000000000000000000000000000000000003acf87e3e2200000000000000029400000000000000000000000000000000000000000000000001962afc49a1eb2e0000000000000028000000000000000000000000000000690000000000000000000000006349f8b5000000000000029800000000000000000000000000000000000000000000000000d3abb1bfb7be70", + "repeated_writes_hash": "0xe67f7b247bf84fdbd8a65ab9181343bdc741abac85d783ac9278cc812a76334e", + "initial_writes_hash": "0xa4f52787f13aec574d566ef0d9d8c440b895fb70b74df3758c63da0ff5b47741", "pass_through_bytes": "000000000000012bbf08d89aaedde3696967d5ac74d2733f10ace64c3a492f503f23b7566b37ab1700000000000000000000000000000000000000000000000000000000000000000000000000000000", "pass_through_hash": "0x1c695ec7d7944f720a2c0fc6b5651cbd3178967407bc4df579a15985652350e9", "meta_params_bytes": "000100037723960c07cda7251089daffbdd567476a7e31971ff801568a3856e8e8010006699c833b654b365f0e3ce866c394626d5e40461a6868809d452738606f", "meta_params_hash": "0x57404e50342edcd09180fb27fa49634676f71a3ce1a76e9b3edf6185bf164082", - "auxiliary_bytes": "cb5f7b72ab30095b81e2cd35c308a7a752fe59213475339b8a833e91bf731837680f578a7b39e9f74385a3aabfb4cf054917f23aea9ae165d2afaac02fc9f3b8dcc4877ab0c07a79a16ae34de6fb7971a54128db0d11791fd5064bd6d03076c1dc5a883793479c779f5c99b0fca910deb20195d8ccf430afad05a9c2bd9f81bd", - "auxiliary_hash": "0x31fff4dc27ee5cbba99aac88a1fd05be00133398b9b7679774663f56b3775dd1", - "commitment_hash": "0x3af4672cd1362badfc0cbc47a7e8b3fbcd3c947055af041b4481bb15009c41a8" + "auxiliary_bytes": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "auxiliary_hash": "0x7c613c82ec911cf56dd6241854dd87bd538e0201f4ff0735f56a1a013db6466a", + "commitment_hash": "0xea55acb8903f82e4cfedd2041ce2db2f3b77741b6e35bc90a4f0a11e9526bfc2" } } From b74a0f09c8634ebc9f55d319c90c6da42cf2a94c Mon Sep 17 00:00:00 2001 From: Todd <148772493+toddfil@users.noreply.github.com> Date: Thu, 30 Nov 2023 14:13:09 +0800 Subject: [PATCH 008/268] chore: update docs (#465) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ - update docs ## Why ❔ - incorrect link reference in zk_intuition.md ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --------- Co-authored-by: Igor Aleksanov --- docs/advanced/zk_intuition.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/advanced/zk_intuition.md b/docs/advanced/zk_intuition.md index 7ea7dad0a449..4a8996ff8dbe 100644 --- a/docs/advanced/zk_intuition.md +++ b/docs/advanced/zk_intuition.md @@ -139,8 +139,7 @@ version 1.4.0. [witness_example]: https://github.com/matter-labs/era-zkevm_test_harness/tree/main/src/witness/individual_circuits/decommit_code.rs#L24 -[verifier]: - https://github.com/matter-labs/zksync-2-contracts/blob/d9785355518edc7f686fb2c91ff7d1caced9f9b8/ethereum/contracts/zksync/Plonk4VerifierWithAccessToDNext.sol#L284 +[verifier]: https://github.com/matter-labs/era-contracts/blob/main/ethereum/contracts/zksync/Verifier.sol [bellman repo]: https://github.com/matter-labs/bellman [bellman cuda repo]: https://github.com/matter-labs/era-bellman-cuda [example ecrecover circuit]: From 0d55d6df980b70a060712ca4beb80d72d704b9d4 Mon Sep 17 00:00:00 2001 From: Jean <148654781+oxJean@users.noreply.github.com> Date: Thu, 30 Nov 2023 14:14:45 +0800 Subject: [PATCH 009/268] chore: fixed typo in code notes (#497) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ fixed typo in code notes ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --------- Co-authored-by: Igor Aleksanov --- infrastructure/protocol-upgrade/src/transaction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/protocol-upgrade/src/transaction.ts b/infrastructure/protocol-upgrade/src/transaction.ts index 9162da1c46f8..f4fd30dcde1b 100644 --- a/infrastructure/protocol-upgrade/src/transaction.ts +++ b/infrastructure/protocol-upgrade/src/transaction.ts @@ -60,7 +60,7 @@ export interface L2CanonicalTransaction { // is to be passed to account and any changes to its structure // would mean a breaking change to these accounts. In order to prevent this, // we should keep some fields as "reserved". - // It is also recommneded that their length is fixed, since + // It is also recommended that their length is fixed, since // it would allow easier proof integration (in case we will need // some special circuit for preprocessing transactions). reserved: [BigNumberish, BigNumberish, BigNumberish, BigNumberish]; From c067007a3e9bc41db35d25f1beb79eb5d2dc5bb2 Mon Sep 17 00:00:00 2001 From: Karma <148863819+0xKarm@users.noreply.github.com> Date: Thu, 30 Nov 2023 14:15:15 +0800 Subject: [PATCH 010/268] chore(docs): fixed typos in documentation (#479) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ - fix typo in README.md ## Why ❔ - fix typo ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --------- Co-authored-by: Igor Aleksanov --- core/tests/loadnext/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/tests/loadnext/README.md b/core/tests/loadnext/README.md index 99c0a47d5b7c..397226f66896 100644 --- a/core/tests/loadnext/README.md +++ b/core/tests/loadnext/README.md @@ -8,7 +8,7 @@ The general flow is as follows: - The master account performs an initial deposit to L2 - Paymaster on L2 is funded if necessary - The L2 master account distributes funds to the participating accounts (`accounts_amount` configuration option) -- Each account continiously sends L2 transactions as configured in `contract_execution_params` configuration option. At +- Each account continuously sends L2 transactions as configured in `contract_execution_params` configuration option. At any given time there are no more than `max_inflight_txs` transactions in flight for each account. - Once each account is done with the initial deposit, the test is run for `duration_sec` seconds. - After the test is finished, the master account withdraws all the remaining funds from L2. From f4f322ae03f08abe8c2ae391798297f345ee8da7 Mon Sep 17 00:00:00 2001 From: Doll <148654386+Dollyerls@users.noreply.github.com> Date: Thu, 30 Nov 2023 14:16:35 +0800 Subject: [PATCH 011/268] chore: fixed typo (#486) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Fixed typo ## Why ❔ Affect reading ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --------- Co-authored-by: Igor Aleksanov --- core/tests/ts-integration/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/tests/ts-integration/README.md b/core/tests/ts-integration/README.md index b3707cac664f..5b34bd7ac02e 100644 --- a/core/tests/ts-integration/README.md +++ b/core/tests/ts-integration/README.md @@ -134,7 +134,7 @@ finalization: it make take several hours to generate a proof and send it onchain Because of that, framework supports "fast" and "long" modes. `TestMaster` objects have `isFastMode` method to determine which mode is currently being used. -If you're going to write a test that can make test run duration longer, it is adviced to guard the "long" part with the +If you're going to write a test that can make test run duration longer, it is advised to guard the "long" part with the corresponding check. By default, "long" mode is assumed, and to enable the "fast" mode one must set the `ZK_INTEGRATION_TESTS_FAST_MODE` From dbe89e2dd802dcf4a4bbe23b05770a24d27b390a Mon Sep 17 00:00:00 2001 From: Salad <148864073+Saladerl@users.noreply.github.com> Date: Thu, 30 Nov 2023 14:17:35 +0800 Subject: [PATCH 012/268] chore(docs): fix docs (#487) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ fixed docs ## Why ❔ fixed typo ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --------- Co-authored-by: Igor Aleksanov --- docs/advanced/02_deposits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/02_deposits.md b/docs/advanced/02_deposits.md index 9d9ac4d526b8..a7b729495932 100644 --- a/docs/advanced/02_deposits.md +++ b/docs/advanced/02_deposits.md @@ -162,7 +162,7 @@ The zk server (that you started with `zk server` command) is listening on events ) and adds them to the postgres database (into `transactions` table). You can actually check it - by running the psql and looking at the contents of the table - then you'll notice that -transaction was succesfully inserted, and it was also marked as 'priority' (as it came from L1) - as regular +transaction was successfully inserted, and it was also marked as 'priority' (as it came from L1) - as regular transactions that are received by the server directly are not marked as priority. You can verify that this is your transaction, by looking at the `l1_block_number` column (it should match the From 4b00ee01b5d07f829ff78d7ab05231f8854fee08 Mon Sep 17 00:00:00 2001 From: momodaka <463435681@qq.com> Date: Thu, 30 Nov 2023 14:17:37 +0800 Subject: [PATCH 013/268] docs: fix typo (#488) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ fix typo issue `succesfully` -> `successfully` `occurence` -> `occurence` ## Why ❔ ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --------- Co-authored-by: jiachen Co-authored-by: Igor Aleksanov --- docs/advanced/bytecode_compression.md | 2 +- docs/advanced/zk_intuition.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/advanced/bytecode_compression.md b/docs/advanced/bytecode_compression.md index 6f94277f8017..2c9e42acd5d6 100644 --- a/docs/advanced/bytecode_compression.md +++ b/docs/advanced/bytecode_compression.md @@ -31,7 +31,7 @@ Dictionary would be: 3 -> 0xC (count: 1) ``` -Note that '1' maps to '0xD', as it occurs twice, and first occurrence is earlier than first occurence of 0xB, that also +Note that '1' maps to '0xD', as it occurs twice, and first occurrence is earlier than first occurrence of 0xB, that also occurs twice. Compressed bytecode: diff --git a/docs/advanced/zk_intuition.md b/docs/advanced/zk_intuition.md index 4a8996ff8dbe..58777b264fc1 100644 --- a/docs/advanced/zk_intuition.md +++ b/docs/advanced/zk_intuition.md @@ -7,7 +7,7 @@ understanding. We're leaving out a lot of details to keep things brief. In our case, the prover takes public input and witness (which is huge - you'll see below), and produces a proof, but the verifier takes (public input, proof) only, without witness. This means that the huge witness doesn't have to be -submitted to L1. This property can be used for many things, like privacy, but here we use it to ipmlement an efficient +submitted to L1. This property can be used for many things, like privacy, but here we use it to implement an efficient rollup that publishes the least required amount of data to L1. ## Basic overview @@ -85,7 +85,7 @@ located in a module [zksync core witness]. However, for the new proof system, th new location called [separate witness binary]. Inside this new location, after the necessary data is fetched from storage, the witness generator calls another piece of -code from [zkevm_test_harness witness] named `run_with_fixed_params`. This code is responsible for createing the +code from [zkevm_test_harness witness] named `run_with_fixed_params`. This code is responsible for creating the witnesses themselves (which can get really HUGE). ## Generating the Proof From dddb797818661ba966e1a6202d340bc28ccaa971 Mon Sep 17 00:00:00 2001 From: buldazer <93915704+buldazer23@users.noreply.github.com> Date: Thu, 30 Nov 2023 09:19:14 +0300 Subject: [PATCH 014/268] chore: fix minor typo (#518) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ fix minor typo ## Why ❔ minor typo succesfully---->successfully were----->where occurence---->occurrence separte---->separate constrain system---->constraint system constraing system---->constraint system Co-authored-by: Igor Aleksanov --- docs/advanced/03_withdrawals.md | 2 +- docs/advanced/contracts.md | 2 +- docs/advanced/prover.md | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/advanced/03_withdrawals.md b/docs/advanced/03_withdrawals.md index 003121d86467..46833809e0ac 100644 --- a/docs/advanced/03_withdrawals.md +++ b/docs/advanced/03_withdrawals.md @@ -81,7 +81,7 @@ This is a good opportunity to talk about system contracts that are automatically list here [in github](https://github.com/matter-labs/era-system-contracts/blob/436d57da2fb35c40e38bcb6637c3a090ddf60701/scripts/constants.ts#L29) -This is the place were we specify that `bootloader` is at address 0x8001, `NonceHolder` at 0x8003 etc. +This is the place where we specify that `bootloader` is at address 0x8001, `NonceHolder` at 0x8003 etc. This brings us to [L2EthToken.sol](https://github.com/matter-labs/era-system-contracts/blob/main/contracts/L2EthToken.sol) that has the diff --git a/docs/advanced/contracts.md b/docs/advanced/contracts.md index 9b44268827c0..e32992fb79bd 100644 --- a/docs/advanced/contracts.md +++ b/docs/advanced/contracts.md @@ -32,7 +32,7 @@ a bunch of registers. More details on this will be written in the future article Having a different VM means that we must have a separate compiler [zk-solc](https://github.com/matter-labs/zksolc-bin) - as the bytecode that is produced by this compiler has to use the zkEVM specific opcodes. -While having a separte compiler introduces a bunch of challenges (for example, we need a custom +While having a separate compiler introduces a bunch of challenges (for example, we need a custom [hardhat plugins](https://github.com/matter-labs/hardhat-zksync) ), it brings a bunch of benefits too: for example it allows us to move some of the VM logic (like new contract deployment) into System contracts - which allows faster & cheaper modifications and increased flexibility. diff --git a/docs/advanced/prover.md b/docs/advanced/prover.md index 02e69c4d38e2..6211f00dea78 100644 --- a/docs/advanced/prover.md +++ b/docs/advanced/prover.md @@ -86,7 +86,7 @@ pub fn select>( ``` And then there is a block of code for witness evaluation (let's skip it for now), and the final block that adds the gate -to the constrain system `cs`: +to the constraint system `cs`: ```rust if ::SetupConfig::KEEP_SETUP { @@ -204,7 +204,7 @@ filled with concrete values. ### CsAllocatable -Implements CsAllocatable - which allows you to directly 'allocate' this struct within constraing system (similarly to +Implements CsAllocatable - which allows you to directly 'allocate' this struct within constraint system (similarly to how we were operating on regular 'Variables' above). ### CSSelectable From 6095d690154cd16d6e6688ec4830f5e8f226e2ac Mon Sep 17 00:00:00 2001 From: Mc Kenna <150222622+McKenna8989@users.noreply.github.com> Date: Thu, 30 Nov 2023 14:26:21 +0800 Subject: [PATCH 015/268] chore(docs): the typos have been corrected (#441) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ - the typos have been corrected ## Why ❔ - the typos have been corrected ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --------- Co-authored-by: Igor Aleksanov --- .../contracts/custom-account/interfaces/IPaymaster.sol | 2 +- core/tests/ts-integration/src/test-master.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/tests/ts-integration/contracts/custom-account/interfaces/IPaymaster.sol b/core/tests/ts-integration/contracts/custom-account/interfaces/IPaymaster.sol index cf5ced948782..1bd5b81f32b4 100644 --- a/core/tests/ts-integration/contracts/custom-account/interfaces/IPaymaster.sol +++ b/core/tests/ts-integration/contracts/custom-account/interfaces/IPaymaster.sol @@ -37,7 +37,7 @@ interface IPaymaster { /// @param _context, the context of the execution, returned by the "validateAndPayForPaymasterTransaction" method. /// @param _transaction, the users' transaction. /// @param _txResult, the result of the transaction execution (success or failure). - /// @param _maxRefundedGas, the upper bound on the amout of gas that could be refunded to the paymaster. + /// @param _maxRefundedGas, the upper bound on the amount of gas that could be refunded to the paymaster. /// @dev The exact amount refunded depends on the gas spent by the "postOp" itself and so the developers should /// take that into account. function postTransaction( diff --git a/core/tests/ts-integration/src/test-master.ts b/core/tests/ts-integration/src/test-master.ts index 8f59288ba5c3..8919bbffd1e9 100644 --- a/core/tests/ts-integration/src/test-master.ts +++ b/core/tests/ts-integration/src/test-master.ts @@ -30,7 +30,7 @@ export class TestMaster { const contextStr = process.env.ZKSYNC_JEST_TEST_CONTEXT; if (!contextStr) { - throw new Error('Test context was not initalized; unable to load context environment variable'); + throw new Error('Test context was not initialized; unable to load context environment variable'); } const context = JSON.parse(contextStr) as TestContext; From f15885e4850a70f321da0c8d4b4d2b48df686df4 Mon Sep 17 00:00:00 2001 From: 0xmbcode <152050562+0xmbcode@users.noreply.github.com> Date: Thu, 30 Nov 2023 09:35:28 +0300 Subject: [PATCH 016/268] chore: Fix broken link in repositories.md (#565) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hello,
 ## Description This change is made to ensure that developers, contributors can access the correct documentation.
 ## Changes Made This pull request fixes a broken link. 

 ## Why The broken link was causing confusion and potentially preventing developers, contributors from accessing the zksync-cli. Correct link:
https://github.com/matter-labs/zksync-cli ## Testing Done - Manually reviewed the documentation and verified the correct link.
 - No additional dependencies or changes are required. - This fix is straightforward and does not impact any other functionalities. Thank you. ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zk fmt` and `zk lint`. - [ ] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --- docs/repositories.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/repositories.md b/docs/repositories.md index 7250c5aef221..0902f38dcd80 100644 --- a/docs/repositories.md +++ b/docs/repositories.md @@ -51,7 +51,7 @@ | [local-setup](https://github.com/matter-labs/local-setup) | Docker-based zk server (together with L1), that can be used for local testing | | [zksolc-bin](https://github.com/matter-labs/zksolc-bin) | repository with solc compiler binaries | | [zkvyper-bin](https://github.com/matter-labs/zkvyper-bin) | repository with vyper compiler binaries | -| [zksync-cli](<(https://github.com/matter-labs/zksync-cli)>) | Command line tool to interact with zksync | +| [zksync-cli](https://github.com/matter-labs/zksync-cli) | Command line tool to interact with zksync | | [hardhat-zksync](https://github.com/matter-labs/hardhat-zksync) | Plugins for hardhat | ### Examples & documentation From cae51479570060fe9e78816714f738cd6dff22d0 Mon Sep 17 00:00:00 2001 From: RakeshXBT <85406290+Rakesh-lab-stack@users.noreply.github.com> Date: Thu, 30 Nov 2023 12:06:27 +0530 Subject: [PATCH 017/268] chore: fix minor typo in rust code in docs (#566) Co-authored-by: Igor Aleksanov --- docs/advanced/gas_and_fees.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/gas_and_fees.md b/docs/advanced/gas_and_fees.md index 800d27299c2a..b8f0e531e98f 100644 --- a/docs/advanced/gas_and_fees.md +++ b/docs/advanced/gas_and_fees.md @@ -86,7 +86,7 @@ transaction. ```rust let gas_remaining_before = vm.gas_remaining(); execute_tx(); -let gas_used = gas_remainig_before = vm.gas_remaining(); +let gas_used = gas_remaining_before - vm.gas_remaining(); ``` ## Gas estimation From 26767b6952c3588e7bc1c9dfe6c3261931cf78d2 Mon Sep 17 00:00:00 2001 From: Tudor <32748771+RedaOps@users.noreply.github.com> Date: Thu, 30 Nov 2023 09:40:24 +0200 Subject: [PATCH 018/268] refactor(eth_client): Use `BlockId` for `block_id` instead of String in `eth_client` (#499) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ The `block` function within `eth_client` now uses the type `BlockId` instead of `String` for its `block_id` parameter. ## Why ❔ TODO comment: https://github.com/matter-labs/zksync-era/blob/cb873bd0da6b421160ce96b8d578f1351861f376/core/lib/eth_client/src/clients/http/query.rs#L289-L308 This PR fixes the TODO. The `web3` crate being used inside the project was updated to version `0.19.0` inside `core/lib/basic_types/Cargo.toml` in commit 829ef5085f938ddda1f2a695930c6b4308e1643a. The `web3` crate now supports `BlockNumber::Finalized`. Source: https://docs.rs/web3/latest/web3/types/enum.BlockNumber.html Source: https://github.com/tomusdrw/rust-web3/releases/tag/v0.19.0 ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Code has been formatted via `zk fmt` and `zk lint`. Co-authored-by: Igor Aleksanov --- core/lib/eth_client/src/clients/http/query.rs | 16 +++------------- core/lib/eth_client/src/clients/http/signing.rs | 2 +- core/lib/eth_client/src/clients/mock.rs | 4 ++-- core/lib/eth_client/src/lib.rs | 2 +- .../zksync_core/src/eth_sender/eth_tx_manager.rs | 8 ++++++-- core/lib/zksync_core/src/eth_watch/client.rs | 4 ++-- 6 files changed, 15 insertions(+), 21 deletions(-) diff --git a/core/lib/eth_client/src/clients/http/query.rs b/core/lib/eth_client/src/clients/http/query.rs index 198f5fc45af0..0094c76f88a6 100644 --- a/core/lib/eth_client/src/clients/http/query.rs +++ b/core/lib/eth_client/src/clients/http/query.rs @@ -14,13 +14,12 @@ use zksync_types::web3::{ Contract, Options, }, ethabi, - helpers::CallFuture, transports::Http, types::{ Address, Block, BlockId, BlockNumber, Bytes, Filter, Log, Transaction, TransactionId, TransactionReceipt, H256, U256, U64, }, - Transport, Web3, + Web3, }; /// An "anonymous" Ethereum client that can invoke read-only methods that aren't @@ -286,23 +285,14 @@ impl EthInterface for QueryClient { Ok(logs) } - // TODO (PLA-333): at the moment the latest version of `web3` crate doesn't have `Finalized` variant in `BlockNumber`. - // However, it's already added in github repo and probably will be included in the next released version. - // Scope of PLA-333 includes forking/using crate directly from github, after that we will be able to change - // type of `block_id` from `String` to `BlockId` and use `self.web3.eth().block(block_id)`. async fn block( &self, - block_id: String, + block_id: BlockId, component: &'static str, ) -> Result>, Error> { COUNTERS.call[&(Method::Block, component)].inc(); let latency = LATENCIES.direct[&Method::Block].start(); - let block = CallFuture::new( - self.web3 - .transport() - .execute("eth_getBlockByNumber", vec![block_id.into(), false.into()]), - ) - .await?; + let block = self.web3.eth().block(block_id).await?; latency.observe(); Ok(block) } diff --git a/core/lib/eth_client/src/clients/http/signing.rs b/core/lib/eth_client/src/clients/http/signing.rs index fcc38efb4cc8..a0a6647db5fd 100644 --- a/core/lib/eth_client/src/clients/http/signing.rs +++ b/core/lib/eth_client/src/clients/http/signing.rs @@ -213,7 +213,7 @@ impl EthInterface for SigningClient { async fn block( &self, - block_id: String, + block_id: BlockId, component: &'static str, ) -> Result>, Error> { self.query_client.block(block_id, component).await diff --git a/core/lib/eth_client/src/clients/mock.rs b/core/lib/eth_client/src/clients/mock.rs index 07297a3645fb..576fbac21a70 100644 --- a/core/lib/eth_client/src/clients/mock.rs +++ b/core/lib/eth_client/src/clients/mock.rs @@ -342,7 +342,7 @@ impl EthInterface for MockEthereum { async fn block( &self, - _block_id: String, + _block_id: BlockId, _component: &'static str, ) -> Result>, Error> { unimplemented!("Not needed right now") @@ -524,7 +524,7 @@ impl + Send + Sync> EthInterface for T { async fn block( &self, - block_id: String, + block_id: BlockId, component: &'static str, ) -> Result>, Error> { self.as_ref().block(block_id, component).await diff --git a/core/lib/eth_client/src/lib.rs b/core/lib/eth_client/src/lib.rs index a03503683255..f61814893bb4 100644 --- a/core/lib/eth_client/src/lib.rs +++ b/core/lib/eth_client/src/lib.rs @@ -131,7 +131,7 @@ pub trait EthInterface: Sync + Send { /// Returns the block header for the specified block number or hash. async fn block( &self, - block_id: String, + block_id: BlockId, component: &'static str, ) -> Result>, Error>; } diff --git a/core/lib/zksync_core/src/eth_sender/eth_tx_manager.rs b/core/lib/zksync_core/src/eth_sender/eth_tx_manager.rs index 98e75702a4c8..5aab4a2903c8 100644 --- a/core/lib/zksync_core/src/eth_sender/eth_tx_manager.rs +++ b/core/lib/zksync_core/src/eth_sender/eth_tx_manager.rs @@ -12,7 +12,11 @@ use zksync_eth_client::{ }; use zksync_types::{ eth_sender::EthTx, - web3::{contract::Options, error::Error as Web3Error}, + web3::{ + contract::Options, + error::Error as Web3Error, + types::{BlockId, BlockNumber}, + }, L1BlockNumber, Nonce, H256, U256, }; use zksync_utils::time::seconds_since_epoch; @@ -285,7 +289,7 @@ where (latest_block_number.saturating_sub(confirmations) as u32).into() } else { self.ethereum_gateway - .block("finalized".to_string(), "eth_tx_manager") + .block(BlockId::Number(BlockNumber::Finalized), "eth_tx_manager") .await? .expect("Finalized block must be present on L1") .number diff --git a/core/lib/zksync_core/src/eth_watch/client.rs b/core/lib/zksync_core/src/eth_watch/client.rs index af38ac79ae7d..cbd3785640e2 100644 --- a/core/lib/zksync_core/src/eth_watch/client.rs +++ b/core/lib/zksync_core/src/eth_watch/client.rs @@ -5,7 +5,7 @@ use zksync_types::{ vk_transform::l1_vk_commitment, web3::{ self, - types::{BlockNumber, FilterBuilder, Log}, + types::{BlockId, BlockNumber, FilterBuilder, Log}, }, Address, H256, }; @@ -225,7 +225,7 @@ impl EthClient for EthHttpQueryClient Date: Thu, 30 Nov 2023 11:11:21 +0100 Subject: [PATCH 019/268] ci: Remove leftovers of explicit publishing to public Docker registries (#496) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Subj ## Why ❔ All DockerHub registries of FOSS repos are now public ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --- .github/workflows/build-core-template.yml | 2 +- infrastructure/zk/src/docker.ts | 24 ++++++++++------------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build-core-template.yml b/.github/workflows/build-core-template.yml index c4ae27faf9c3..95bb28947953 100644 --- a/.github/workflows/build-core-template.yml +++ b/.github/workflows/build-core-template.yml @@ -83,7 +83,7 @@ jobs: COMPONENT: ${{ matrix.component }} run: | ci_run rustup default nightly-2023-08-21 - ci_run zk docker $DOCKER_ACTION $COMPONENT -- --public + ci_run zk docker $DOCKER_ACTION $COMPONENT - name: Show sccache stats if: always() run: | diff --git a/infrastructure/zk/src/docker.ts b/infrastructure/zk/src/docker.ts index 3db9a69313d5..9a4bf6e291a4 100644 --- a/infrastructure/zk/src/docker.ts +++ b/infrastructure/zk/src/docker.ts @@ -25,19 +25,19 @@ async function dockerCommand( command: 'push' | 'build', image: string, customTag?: string, - publishPublic: boolean = false, dockerOrg: string = 'matterlabs' ) { // Generating all tags for containers. We need 2 tags here: SHA and SHA+TS const { stdout: COMMIT_SHORT_SHA }: { stdout: string } = await utils.exec('git rev-parse --short HEAD'); + // COMMIT_SHORT_SHA returns with newline, so we need to trim it const imageTagShaTS: string = process.env.IMAGE_TAG_SUFFIX ? process.env.IMAGE_TAG_SUFFIX : `${COMMIT_SHORT_SHA.trim()}-${UNIX_TIMESTAMP}`; - // we want alternative flow for rust image + // We want an alternative flow for Rust image if (image == 'rust') { - await dockerCommand(command, 'server-v2', customTag, publishPublic); - await dockerCommand(command, 'prover', customTag, publishPublic); + await dockerCommand(command, 'server-v2', customTag, dockerOrg); + await dockerCommand(command, 'prover', customTag, dockerOrg); return; } if (!IMAGES.includes(image)) { @@ -49,14 +49,14 @@ async function dockerCommand( } const tagList = customTag ? [customTag] : defaultTagList(image, COMMIT_SHORT_SHA.trim(), imageTagShaTS); + // Main build\push flow - // COMMIT_SHORT_SHA returns with newline, so we need to trim it switch (command) { case 'build': await _build(image, tagList, dockerOrg); break; case 'push': - await _push(image, tagList, publishPublic); + await _push(image, tagList); break; default: console.log(`Unknown command for docker ${command}.`); @@ -114,7 +114,7 @@ async function _build(image: string, tagList: string[], dockerOrg: string) { await utils.spawn(buildCommand); } -async function _push(image: string, tagList: string[], publishPublic: boolean = false) { +async function _push(image: string, tagList: string[]) { // For development purposes, we want to use `2.0` tags for 2.0 images, just to not interfere with 1.x for (const tag of tagList) { @@ -134,9 +134,6 @@ async function _push(image: string, tagList: string[], publishPublic: boolean = await utils.spawn(`docker push asia-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${image}:${tag}`); await utils.spawn(`docker push europe-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${image}:${tag}`); } - if (image == 'external-node' && publishPublic) { - await utils.spawn(`docker push matterlabs/${image}-public:${tag}`); - } } } @@ -145,12 +142,12 @@ export async function build(image: string, cmd: Command) { } export async function customBuildForHyperchain(image: string, dockerOrg: string) { - await dockerCommand('build', image, '', false, dockerOrg); + await dockerCommand('build', image, '', dockerOrg); } export async function push(image: string, cmd: Command) { - await dockerCommand('build', image, cmd.customTag, cmd.public); - await dockerCommand('push', image, cmd.customTag, cmd.public); + await dockerCommand('build', image, cmd.customTag); + await dockerCommand('push', image, cmd.customTag); } export async function restart(container: string) { @@ -171,7 +168,6 @@ command command .command('push ') .option('--custom-tag ', 'Custom tag for image') - .option('--public', 'Publish image to the public repo') .description('build and push docker image') .action(push); command.command('pull').description('pull all containers').action(pull); From 83791aa6704221755674dd5b1eb428e286f791da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Grze=C5=9Bkiewicz?= Date: Thu, 30 Nov 2023 11:49:23 +0100 Subject: [PATCH 020/268] feat: zk db check-sqlx-check on pre-push (#548) --- .githooks/pre-push | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.githooks/pre-push b/.githooks/pre-push index eb1acbb693c1..94cc937c9b1d 100755 --- a/.githooks/pre-push +++ b/.githooks/pre-push @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # # Pre-push hook verifying that inappropriate code will not be pushed. @@ -8,7 +8,13 @@ NC='\033[0m' # No Color # Check that prettier formatting rules are not violated. if ! zk fmt --check; then - echo -e "${RED}Commit error!${NC}" + echo -e "${RED}Push error!${NC}" echo "Please format the code via 'zk fmt', cannot push unformatted code" exit 1 fi + +if ! zk db check-sqlx-data; then + echo -e "${RED}Push error!${NC}" + echo "Please update sqlx-data.json via 'zk db setup', cannot push invalid sqlx-data.json file" + exit 1 +fi From e2c1b20e361e6ee2f5ac69cefe75d9c5575eb2f7 Mon Sep 17 00:00:00 2001 From: Alex Ostrovski Date: Thu, 30 Nov 2023 14:34:55 +0200 Subject: [PATCH 021/268] feat(merkle tree): Remove enumeration index assignment from Merkle tree (#551) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Since enumeration indices are now fully stored in Postgres, it makes sense to not duplicate their assignment in the Merkle tree. Instead, the tree could take enum indices as inputs. ## Why ❔ This allows simplifying tree logic and unify "normal" L1 batch processing and tree recovery. (This unification is not a part of this PR; it'll be implemented separately.) ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --- .../lib/merkle_tree/examples/loadtest/main.rs | 24 +- core/lib/merkle_tree/examples/recovery.rs | 10 +- core/lib/merkle_tree/src/consistency.rs | 35 ++- core/lib/merkle_tree/src/domain.rs | 145 ++++++------ core/lib/merkle_tree/src/getters.rs | 17 +- core/lib/merkle_tree/src/hasher/mod.rs | 24 +- core/lib/merkle_tree/src/hasher/proofs.rs | 62 +++-- core/lib/merkle_tree/src/lib.rs | 13 +- core/lib/merkle_tree/src/pruning.rs | 28 +-- core/lib/merkle_tree/src/recovery.rs | 35 +-- core/lib/merkle_tree/src/storage/mod.rs | 62 ++--- core/lib/merkle_tree/src/storage/patch.rs | 21 +- core/lib/merkle_tree/src/storage/proofs.rs | 224 ++---------------- .../merkle_tree/src/storage/serialization.rs | 11 +- core/lib/merkle_tree/src/storage/tests.rs | 128 +++++----- core/lib/merkle_tree/src/types/internal.rs | 22 +- core/lib/merkle_tree/src/types/mod.rs | 93 ++++++-- core/lib/merkle_tree/src/utils.rs | 5 - .../merkle_tree/tests/integration/common.rs | 50 ++-- .../tests/integration/consistency.rs | 6 +- .../merkle_tree/tests/integration/domain.rs | 53 +++-- .../tests/integration/merkle_tree.rs | 116 +++++---- .../merkle_tree/tests/integration/recovery.rs | 43 +--- .../zksync_core/src/api_server/tree/mod.rs | 2 +- .../src/metadata_calculator/helpers.rs | 91 +++---- .../src/metadata_calculator/metrics.rs | 2 +- 26 files changed, 591 insertions(+), 731 deletions(-) diff --git a/core/lib/merkle_tree/examples/loadtest/main.rs b/core/lib/merkle_tree/examples/loadtest/main.rs index b598a579f6b4..527daa87b37a 100644 --- a/core/lib/merkle_tree/examples/loadtest/main.rs +++ b/core/lib/merkle_tree/examples/loadtest/main.rs @@ -15,7 +15,8 @@ use std::{ use zksync_crypto::hasher::blake2::Blake2Hasher; use zksync_merkle_tree::{ - Database, HashTree, MerkleTree, MerkleTreePruner, PatchSet, RocksDBWrapper, TreeInstruction, + Database, HashTree, MerkleTree, MerkleTreePruner, PatchSet, RocksDBWrapper, TreeEntry, + TreeInstruction, }; use zksync_storage::{RocksDB, RocksDBOptions}; use zksync_types::{AccountTreeId, Address, StorageKey, H256, U256}; @@ -135,19 +136,22 @@ impl Cli { next_key_idx += new_keys.len() as u64; next_value_idx += (new_keys.len() + updated_indices.len()) as u64; - let values = (next_value_idx..).map(H256::from_low_u64_be); let updated_keys = Self::generate_keys(updated_indices.into_iter()); - let kvs = new_keys.into_iter().chain(updated_keys).zip(values); + let kvs = new_keys + .into_iter() + .chain(updated_keys) + .zip(next_value_idx..); + let kvs = kvs.map(|(key, idx)| { + // The assigned leaf indices here are not always correct, but it's OK for load test purposes. + TreeEntry::new(key, idx, H256::from_low_u64_be(idx)) + }); tracing::info!("Processing block #{version}"); let start = Instant::now(); let root_hash = if self.proofs { - let reads = Self::generate_keys(read_indices.into_iter()) - .map(|key| (key, TreeInstruction::Read)); - let instructions = kvs - .map(|(key, hash)| (key, TreeInstruction::Write(hash))) - .chain(reads) - .collect(); + let reads = + Self::generate_keys(read_indices.into_iter()).map(TreeInstruction::Read); + let instructions = kvs.map(TreeInstruction::Write).chain(reads).collect(); let output = tree.extend_with_proofs(instructions); output.root_hash().unwrap() } else { @@ -160,7 +164,7 @@ impl Cli { tracing::info!("Verifying tree consistency..."); let start = Instant::now(); - tree.verify_consistency(self.commit_count - 1) + tree.verify_consistency(self.commit_count - 1, false) .expect("tree consistency check failed"); let elapsed = start.elapsed(); tracing::info!("Verified tree consistency in {elapsed:?}"); diff --git a/core/lib/merkle_tree/examples/recovery.rs b/core/lib/merkle_tree/examples/recovery.rs index af16ed05baf3..1a2aae236ea5 100644 --- a/core/lib/merkle_tree/examples/recovery.rs +++ b/core/lib/merkle_tree/examples/recovery.rs @@ -9,8 +9,8 @@ use std::time::Instant; use zksync_crypto::hasher::blake2::Blake2Hasher; use zksync_merkle_tree::{ - recovery::{MerkleTreeRecovery, RecoveryEntry}, - HashTree, Key, PatchSet, PruneDatabase, RocksDBWrapper, ValueHash, + recovery::MerkleTreeRecovery, HashTree, Key, PatchSet, PruneDatabase, RocksDBWrapper, + TreeEntry, ValueHash, }; use zksync_storage::{RocksDB, RocksDBOptions}; @@ -94,7 +94,7 @@ impl Cli { .map(|_| { last_leaf_index += 1; if self.random { - RecoveryEntry { + TreeEntry { key: Key::from(rng.gen::<[u8; 32]>()), value: ValueHash::zero(), leaf_index: last_leaf_index, @@ -102,7 +102,7 @@ impl Cli { } else { last_key += key_step - Key::from(rng.gen::()); // ^ Increases the key by a random increment close to `key` step with some randomness. - RecoveryEntry { + TreeEntry { key: last_key, value: ValueHash::zero(), leaf_index: last_leaf_index, @@ -127,7 +127,7 @@ impl Cli { recovery_started_at.elapsed() ); let started_at = Instant::now(); - tree.verify_consistency(recovered_version).unwrap(); + tree.verify_consistency(recovered_version, true).unwrap(); tracing::info!("Verified consistency in {:?}", started_at.elapsed()); } } diff --git a/core/lib/merkle_tree/src/consistency.rs b/core/lib/merkle_tree/src/consistency.rs index 85896bad1ae1..2cc8996e64e9 100644 --- a/core/lib/merkle_tree/src/consistency.rs +++ b/core/lib/merkle_tree/src/consistency.rs @@ -69,10 +69,17 @@ pub enum ConsistencyError { impl MerkleTree { /// Verifies the internal tree consistency as stored in the database. /// + /// If `validate_indices` flag is set, it will be checked that indices for all tree leaves are unique + /// and are sequentially assigned starting from 1. + /// /// # Errors /// /// Returns an error (the first encountered one if there are multiple). - pub fn verify_consistency(&self, version: u64) -> Result<(), ConsistencyError> { + pub fn verify_consistency( + &self, + version: u64, + validate_indices: bool, + ) -> Result<(), ConsistencyError> { let manifest = self.db.try_manifest()?; let manifest = manifest.ok_or(ConsistencyError::MissingVersion(version))?; if version >= manifest.version_count { @@ -91,16 +98,19 @@ impl MerkleTree { // We want to perform a depth-first walk of the tree in order to not keep // much in memory. let root_key = Nibbles::EMPTY.with_version(version); - let leaf_data = LeafConsistencyData::new(leaf_count); - self.validate_node(&root_node, root_key, &leaf_data)?; - leaf_data.validate_count() + let leaf_data = validate_indices.then(|| LeafConsistencyData::new(leaf_count)); + self.validate_node(&root_node, root_key, leaf_data.as_ref())?; + if let Some(leaf_data) = leaf_data { + leaf_data.validate_count()?; + } + Ok(()) } fn validate_node( &self, node: &Node, key: NodeKey, - leaf_data: &LeafConsistencyData, + leaf_data: Option<&LeafConsistencyData>, ) -> Result { match node { Node::Leaf(leaf) => { @@ -111,7 +121,9 @@ impl MerkleTree { full_key: leaf.full_key, }); } - leaf_data.insert_leaf(leaf)?; + if let Some(leaf_data) = leaf_data { + leaf_data.insert_leaf(leaf)?; + } } Node::Internal(node) => { @@ -261,7 +273,10 @@ mod tests { use std::num::NonZeroU64; use super::*; - use crate::{types::InternalNode, PatchSet}; + use crate::{ + types::{InternalNode, TreeEntry}, + PatchSet, + }; use zksync_types::{H256, U256}; const FIRST_KEY: Key = U256([0, 0, 0, 0x_dead_beef_0000_0000]); @@ -270,8 +285,8 @@ mod tests { fn prepare_database() -> PatchSet { let mut tree = MerkleTree::new(PatchSet::default()); tree.extend(vec![ - (FIRST_KEY, H256([1; 32])), - (SECOND_KEY, H256([2; 32])), + TreeEntry::new(FIRST_KEY, 1, H256([1; 32])), + TreeEntry::new(SECOND_KEY, 2, H256([2; 32])), ]); tree.db } @@ -300,7 +315,7 @@ mod tests { .num_threads(1) .build() .expect("failed initializing `rayon` thread pool"); - thread_pool.install(|| MerkleTree::new(db).verify_consistency(0)) + thread_pool.install(|| MerkleTree::new(db).verify_consistency(0, true)) } #[test] diff --git a/core/lib/merkle_tree/src/domain.rs b/core/lib/merkle_tree/src/domain.rs index bb82233aec28..0cd9a56a4866 100644 --- a/core/lib/merkle_tree/src/domain.rs +++ b/core/lib/merkle_tree/src/domain.rs @@ -5,7 +5,10 @@ use zksync_utils::h256_to_u256; use crate::{ storage::{MerkleTreeColumnFamily, PatchSet, Patched, RocksDBWrapper}, - types::{Key, Root, TreeEntryWithProof, TreeInstruction, TreeLogEntry, ValueHash, TREE_DEPTH}, + types::{ + Key, Root, TreeEntry, TreeEntryWithProof, TreeInstruction, TreeLogEntry, ValueHash, + TREE_DEPTH, + }, BlockOutput, HashTree, MerkleTree, NoVersionError, }; use zksync_crypto::hasher::blake2::Blake2Hasher; @@ -13,7 +16,7 @@ use zksync_storage::RocksDB; use zksync_types::{ proofs::{PrepareBasicCircuitsJob, StorageLogMetadata}, writes::{InitialStorageWrite, RepeatedStorageWrite, StateDiffRecord}, - L1BatchNumber, StorageKey, StorageLog, StorageLogKind, U256, + L1BatchNumber, StorageKey, U256, }; /// Metadata for the current tree state. @@ -65,17 +68,17 @@ impl ZkSyncTree { /// Returns metadata based on `storage_logs` generated by the genesis L1 batch. This does not /// create a persistent tree. - pub fn process_genesis_batch(storage_logs: &[StorageLog]) -> BlockOutput { - let kvs = Self::filter_write_logs(storage_logs); + pub fn process_genesis_batch(storage_logs: &[TreeInstruction]) -> BlockOutput { + let kvs = Self::filter_write_instructions(storage_logs); tracing::info!( "Creating Merkle tree for genesis batch with {instr_count} writes", instr_count = kvs.len() ); - let kvs = kvs + let kvs: Vec<_> = kvs .iter() - .map(|(k, v)| (k.hashed_key_u256(), *v)) - .collect::>(); + .map(|instr| instr.map_key(StorageKey::hashed_key_u256)) + .collect(); let mut in_memory_tree = MerkleTree::new(PatchSet::default()); let output = in_memory_tree.extend(kvs); @@ -170,29 +173,36 @@ impl ZkSyncTree { /// Panics if an inconsistency is detected. pub fn verify_consistency(&self, l1_batch_number: L1BatchNumber) { let version = u64::from(l1_batch_number.0); - self.tree.verify_consistency(version).unwrap_or_else(|err| { - panic!("Tree at version {version} is inconsistent: {err}"); - }); + self.tree + .verify_consistency(version, true) + .unwrap_or_else(|err| { + panic!("Tree at version {version} is inconsistent: {err}"); + }); } /// Processes an iterator of storage logs comprising a single L1 batch. - pub fn process_l1_batch(&mut self, storage_logs: &[StorageLog]) -> TreeMetadata { + pub fn process_l1_batch( + &mut self, + storage_logs: &[TreeInstruction], + ) -> TreeMetadata { match self.mode { TreeMode::Full => self.process_l1_batch_full(storage_logs), TreeMode::Lightweight => self.process_l1_batch_lightweight(storage_logs), } } - fn process_l1_batch_full(&mut self, storage_logs: &[StorageLog]) -> TreeMetadata { + fn process_l1_batch_full( + &mut self, + instructions: &[TreeInstruction], + ) -> TreeMetadata { let l1_batch_number = self.next_l1_batch_number(); - let instructions = Self::transform_logs(storage_logs); let starting_leaf_count = self.tree.latest_root().leaf_count(); let starting_root_hash = self.tree.latest_root_hash(); - let instructions_with_hashed_keys = instructions + let instructions_with_hashed_keys: Vec<_> = instructions .iter() - .map(|(k, instr)| (k.hashed_key_u256(), *instr)) - .collect::>(); + .map(|instr| instr.map_key(StorageKey::hashed_key_u256)) + .collect(); tracing::info!( "Extending Merkle tree with batch #{l1_batch_number} with {instr_count} ops in full mode", @@ -207,7 +217,7 @@ impl ZkSyncTree { let mut witness = PrepareBasicCircuitsJob::new(starting_leaf_count + 1); witness.reserve(output.logs.len()); - for (log, (key, instruction)) in output.logs.iter().zip(&instructions) { + for (log, instruction) in output.logs.iter().zip(instructions) { let empty_levels_end = TREE_DEPTH - log.merkle_path.len(); let empty_subtree_hashes = (0..empty_levels_end).map(|i| Blake2Hasher.empty_subtree_hash(i)); @@ -218,20 +228,22 @@ impl ZkSyncTree { .collect(); let value_written = match instruction { - TreeInstruction::Write(value) => value.0, - TreeInstruction::Read => [0_u8; 32], + TreeInstruction::Write(entry) => entry.value.0, + TreeInstruction::Read(_) => [0_u8; 32], }; let log = StorageLogMetadata { root_hash: log.root_hash.0, is_write: !log.base.is_read(), - first_write: matches!(log.base, TreeLogEntry::Inserted { .. }), + first_write: matches!(log.base, TreeLogEntry::Inserted), merkle_paths, - leaf_hashed_key: key.hashed_key_u256(), - leaf_enumeration_index: match log.base { - TreeLogEntry::Updated { leaf_index, .. } - | TreeLogEntry::Inserted { leaf_index } - | TreeLogEntry::Read { leaf_index, .. } => leaf_index, - TreeLogEntry::ReadMissingKey => 0, + leaf_hashed_key: instruction.key().hashed_key_u256(), + leaf_enumeration_index: match instruction { + TreeInstruction::Write(entry) => entry.leaf_index, + TreeInstruction::Read(_) => match log.base { + TreeLogEntry::Read { leaf_index, .. } => leaf_index, + TreeLogEntry::ReadMissingKey => 0, + _ => unreachable!("Read instructions always transform to Read / ReadMissingKey log entries"), + } }, value_written, value_read: match log.base { @@ -243,7 +255,7 @@ impl ZkSyncTree { previous_value.0 } TreeLogEntry::Read { value, .. } => value.0, - TreeLogEntry::Inserted { .. } | TreeLogEntry::ReadMissingKey => [0_u8; 32], + TreeLogEntry::Inserted | TreeLogEntry::ReadMissingKey => [0_u8; 32], }, }; witness.push_merkle_path(log); @@ -254,12 +266,12 @@ impl ZkSyncTree { .logs .into_iter() .filter_map(|log| (!log.base.is_read()).then_some(log.base)); - let kvs = instructions.into_iter().filter_map(|(key, instruction)| { - let TreeInstruction::Write(value) = instruction else { - return None; - }; - Some((key, value)) - }); + let kvs = instructions + .iter() + .filter_map(|instruction| match instruction { + TreeInstruction::Write(entry) => Some(*entry), + TreeInstruction::Read(_) => None, + }); let (initial_writes, repeated_writes, state_diffs) = Self::extract_writes(logs, kvs); tracing::info!( @@ -281,21 +293,9 @@ impl ZkSyncTree { } } - fn transform_logs(storage_logs: &[StorageLog]) -> Vec<(StorageKey, TreeInstruction)> { - let instructions = storage_logs.iter().map(|log| { - let key = log.key; - let instruction = match log.kind { - StorageLogKind::Write => TreeInstruction::Write(log.value), - StorageLogKind::Read => TreeInstruction::Read, - }; - (key, instruction) - }); - instructions.collect() - } - fn extract_writes( logs: impl Iterator, - kvs: impl Iterator, + entries: impl Iterator>, ) -> ( Vec, Vec, @@ -304,13 +304,14 @@ impl ZkSyncTree { let mut initial_writes = vec![]; let mut repeated_writes = vec![]; let mut state_diffs = vec![]; - for (log_entry, (key, value)) in logs.zip(kvs) { + for (log_entry, input_entry) in logs.zip(entries) { + let key = &input_entry.key; match log_entry { - TreeLogEntry::Inserted { leaf_index } => { + TreeLogEntry::Inserted => { initial_writes.push(InitialStorageWrite { - index: leaf_index, + index: input_entry.leaf_index, key: key.hashed_key_u256(), - value, + value: input_entry.value, }); state_diffs.push(StateDiffRecord { address: *key.address(), @@ -318,25 +319,25 @@ impl ZkSyncTree { derived_key: StorageKey::raw_hashed_key(key.address(), key.key()), enumeration_index: 0u64, initial_value: U256::default(), - final_value: h256_to_u256(value), + final_value: h256_to_u256(input_entry.value), }); } TreeLogEntry::Updated { + previous_value: prev_value_hash, leaf_index, - previous_value, } => { - if previous_value != value { + if prev_value_hash != input_entry.value { repeated_writes.push(RepeatedStorageWrite { - index: leaf_index, - value, + index: input_entry.leaf_index, + value: input_entry.value, }); state_diffs.push(StateDiffRecord { address: *key.address(), key: h256_to_u256(*key.key()), derived_key: StorageKey::raw_hashed_key(key.address(), key.key()), enumeration_index: leaf_index, - initial_value: h256_to_u256(previous_value), - final_value: h256_to_u256(value), + initial_value: h256_to_u256(prev_value_hash), + final_value: h256_to_u256(input_entry.value), }); } // Else we have a no-op update that must be omitted from `repeated_writes`. @@ -348,8 +349,11 @@ impl ZkSyncTree { (initial_writes, repeated_writes, state_diffs) } - fn process_l1_batch_lightweight(&mut self, storage_logs: &[StorageLog]) -> TreeMetadata { - let kvs = Self::filter_write_logs(storage_logs); + fn process_l1_batch_lightweight( + &mut self, + instructions: &[TreeInstruction], + ) -> TreeMetadata { + let kvs = Self::filter_write_instructions(instructions); let l1_batch_number = self.next_l1_batch_number(); tracing::info!( "Extending Merkle tree with batch #{l1_batch_number} with {kv_count} writes \ @@ -357,10 +361,10 @@ impl ZkSyncTree { kv_count = kvs.len() ); - let kvs_with_derived_key = kvs + let kvs_with_derived_key: Vec<_> = kvs .iter() - .map(|(k, v)| (k.hashed_key_u256(), *v)) - .collect::>(); + .map(|entry| entry.map_key(StorageKey::hashed_key_u256)) + .collect(); let output = if let Some(thread_pool) = &self.thread_pool { thread_pool.install(|| self.tree.extend(kvs_with_derived_key.clone())) @@ -390,14 +394,15 @@ impl ZkSyncTree { } } - fn filter_write_logs(storage_logs: &[StorageLog]) -> Vec<(StorageKey, ValueHash)> { - let kvs = storage_logs.iter().filter_map(|log| match log.kind { - StorageLogKind::Write => { - let key = log.key; - Some((key, log.value)) - } - StorageLogKind::Read => None, - }); + fn filter_write_instructions( + instructions: &[TreeInstruction], + ) -> Vec> { + let kvs = instructions + .iter() + .filter_map(|instruction| match instruction { + TreeInstruction::Write(entry) => Some(*entry), + TreeInstruction::Read(_) => None, + }); kvs.collect() } diff --git a/core/lib/merkle_tree/src/getters.rs b/core/lib/merkle_tree/src/getters.rs index 67ce2aa98773..7fd6bfc96ed0 100644 --- a/core/lib/merkle_tree/src/getters.rs +++ b/core/lib/merkle_tree/src/getters.rs @@ -26,7 +26,7 @@ impl MerkleTree { let node = patch_set.get(longest_prefix); match node { Some(Node::Leaf(leaf)) if &leaf.full_key == leaf_key => (*leaf).into(), - _ => TreeEntry::empty(), + _ => TreeEntry::empty(*leaf_key), } }, ) @@ -76,11 +76,12 @@ impl MerkleTree { |patch_set, &leaf_key, longest_prefix| { let (leaf, merkle_path) = patch_set.create_proof(&mut hasher, leaf_key, longest_prefix, 0); - let value_hash = leaf + let value = leaf .as_ref() .map_or_else(ValueHash::zero, |leaf| leaf.value_hash); TreeEntry { - value_hash, + key: leaf_key, + value, leaf_index: leaf.map_or(0, |leaf| leaf.leaf_index), } .with_merkle_path(merkle_path.into_inner()) @@ -107,26 +108,26 @@ mod tests { let entries = tree.entries_with_proofs(0, &[missing_key]).unwrap(); assert_eq!(entries.len(), 1); assert!(entries[0].base.is_empty()); - entries[0].verify(&tree.hasher, missing_key, tree.hasher.empty_tree_hash()); + entries[0].verify(&tree.hasher, tree.hasher.empty_tree_hash()); } #[test] fn entries_in_single_node_tree() { let mut tree = MerkleTree::new(PatchSet::default()); let key = Key::from(987_654); - let output = tree.extend(vec![(key, ValueHash::repeat_byte(1))]); + let output = tree.extend(vec![TreeEntry::new(key, 1, ValueHash::repeat_byte(1))]); let missing_key = Key::from(123); let entries = tree.entries(0, &[key, missing_key]).unwrap(); assert_eq!(entries.len(), 2); - assert_eq!(entries[0].value_hash, ValueHash::repeat_byte(1)); + assert_eq!(entries[0].value, ValueHash::repeat_byte(1)); assert_eq!(entries[0].leaf_index, 1); let entries = tree.entries_with_proofs(0, &[key, missing_key]).unwrap(); assert_eq!(entries.len(), 2); assert!(!entries[0].base.is_empty()); - entries[0].verify(&tree.hasher, key, output.root_hash); + entries[0].verify(&tree.hasher, output.root_hash); assert!(entries[1].base.is_empty()); - entries[1].verify(&tree.hasher, missing_key, output.root_hash); + entries[1].verify(&tree.hasher, output.root_hash); } } diff --git a/core/lib/merkle_tree/src/hasher/mod.rs b/core/lib/merkle_tree/src/hasher/mod.rs index 8b2478c43d34..9425a5836f02 100644 --- a/core/lib/merkle_tree/src/hasher/mod.rs +++ b/core/lib/merkle_tree/src/hasher/mod.rs @@ -11,7 +11,7 @@ pub(crate) use self::nodes::{InternalNodeCache, MerklePath}; pub use self::proofs::TreeRangeDigest; use crate::{ metrics::HashingStats, - types::{Key, ValueHash, TREE_DEPTH}, + types::{TreeEntry, ValueHash, TREE_DEPTH}, }; use zksync_crypto::hasher::{blake2::Blake2Hasher, Hasher}; @@ -65,17 +65,11 @@ impl dyn HashTree + '_ { empty_hashes.chain(path.iter().copied()) } - fn fold_merkle_path( - &self, - path: &[ValueHash], - key: Key, - value_hash: ValueHash, - leaf_index: u64, - ) -> ValueHash { - let mut hash = self.hash_leaf(&value_hash, leaf_index); + fn fold_merkle_path(&self, path: &[ValueHash], entry: TreeEntry) -> ValueHash { + let mut hash = self.hash_leaf(&entry.value, entry.leaf_index); let full_path = self.extend_merkle_path(path); for (depth, adjacent_hash) in full_path.enumerate() { - hash = if key.bit(depth) { + hash = if entry.key.bit(depth) { self.hash_branch(&adjacent_hash, &hash) } else { self.hash_branch(&hash, &adjacent_hash) @@ -254,7 +248,7 @@ mod tests { let address: Address = "4b3af74f66ab1f0da3f2e4ec7a3cb99baf1af7b2".parse().unwrap(); let key = StorageKey::new(AccountTreeId::new(address), H256::zero()); let key = key.hashed_key_u256(); - let leaf = LeafNode::new(key, H256([1; 32]), 1); + let leaf = LeafNode::new(TreeEntry::new(key, 1, H256([1; 32]))); let stats = HashingStats::default(); let mut hasher = (&Blake2Hasher as &dyn HashTree).with_stats(&stats); @@ -265,7 +259,7 @@ mod tests { assert!(stats.hashed_bytes.into_inner() > 100); let hasher: &dyn HashTree = &Blake2Hasher; - let folded_hash = hasher.fold_merkle_path(&[], key, H256([1; 32]), 1); + let folded_hash = hasher.fold_merkle_path(&[], leaf.into()); assert_eq!(folded_hash, EXPECTED_HASH); } @@ -274,7 +268,7 @@ mod tests { let address: Address = "4b3af74f66ab1f0da3f2e4ec7a3cb99baf1af7b2".parse().unwrap(); let key = StorageKey::new(AccountTreeId::new(address), H256::zero()); let key = key.hashed_key_u256(); - let leaf = LeafNode::new(key, H256([1; 32]), 1); + let leaf = LeafNode::new(TreeEntry::new(key, 1, H256([1; 32]))); let mut hasher = HasherWithStats::new(&Blake2Hasher); let leaf_hash = leaf.hash(&mut hasher, 2); @@ -283,9 +277,7 @@ mod tests { let expected_hash = hasher.hash_branch(&merkle_path[0], &leaf_hash); let expected_hash = hasher.hash_branch(&expected_hash, &merkle_path[1]); - let folded_hash = hasher - .inner - .fold_merkle_path(&merkle_path, key, H256([1; 32]), 1); + let folded_hash = hasher.inner.fold_merkle_path(&merkle_path, leaf.into()); assert_eq!(folded_hash, expected_hash); } } diff --git a/core/lib/merkle_tree/src/hasher/proofs.rs b/core/lib/merkle_tree/src/hasher/proofs.rs index d97df0ad97d0..49d4bfe92958 100644 --- a/core/lib/merkle_tree/src/hasher/proofs.rs +++ b/core/lib/merkle_tree/src/hasher/proofs.rs @@ -22,36 +22,37 @@ impl BlockOutputWithProofs { &self, hasher: &dyn HashTree, old_root_hash: ValueHash, - instructions: &[(Key, TreeInstruction)], + instructions: &[TreeInstruction], ) { assert_eq!(instructions.len(), self.logs.len()); let mut root_hash = old_root_hash; - for (op, &(key, instruction)) in self.logs.iter().zip(instructions) { + for (op, &instruction) in self.logs.iter().zip(instructions) { assert!(op.merkle_path.len() <= TREE_DEPTH); - if matches!(instruction, TreeInstruction::Read) { + if matches!(instruction, TreeInstruction::Read(_)) { assert_eq!(op.root_hash, root_hash); assert!(op.base.is_read()); } else { assert!(!op.base.is_read()); } - let (prev_leaf_index, leaf_index, prev_value) = match op.base { - TreeLogEntry::Inserted { leaf_index } => (0, leaf_index, ValueHash::zero()), + let prev_entry = match op.base { + TreeLogEntry::Inserted | TreeLogEntry::ReadMissingKey => { + TreeEntry::empty(instruction.key()) + } TreeLogEntry::Updated { leaf_index, - previous_value, - } => (leaf_index, leaf_index, previous_value), - - TreeLogEntry::Read { leaf_index, value } => (leaf_index, leaf_index, value), - TreeLogEntry::ReadMissingKey => (0, 0, ValueHash::zero()), + previous_value: value, + } + | TreeLogEntry::Read { leaf_index, value } => { + TreeEntry::new(instruction.key(), leaf_index, value) + } }; - let prev_hash = - hasher.fold_merkle_path(&op.merkle_path, key, prev_value, prev_leaf_index); + let prev_hash = hasher.fold_merkle_path(&op.merkle_path, prev_entry); assert_eq!(prev_hash, root_hash); - if let TreeInstruction::Write(value) = instruction { - let next_hash = hasher.fold_merkle_path(&op.merkle_path, key, value, leaf_index); + if let TreeInstruction::Write(new_entry) = instruction { + let next_hash = hasher.fold_merkle_path(&op.merkle_path, new_entry); assert_eq!(next_hash, op.root_hash); } root_hash = op.root_hash; @@ -65,19 +66,14 @@ impl TreeEntryWithProof { /// # Panics /// /// Panics if the proof doesn't verify. - pub fn verify(&self, hasher: &dyn HashTree, key: Key, trusted_root_hash: ValueHash) { + pub fn verify(&self, hasher: &dyn HashTree, trusted_root_hash: ValueHash) { if self.base.leaf_index == 0 { assert!( - self.base.value_hash.is_zero(), + self.base.value.is_zero(), "Invalid missing value specification: leaf index is zero, but value is non-default" ); } - let root_hash = hasher.fold_merkle_path( - &self.merkle_path, - key, - self.base.value_hash, - self.base.leaf_index, - ); + let root_hash = hasher.fold_merkle_path(&self.merkle_path, self.base); assert_eq!(root_hash, trusted_root_hash, "Root hash mismatch"); } } @@ -146,11 +142,7 @@ impl<'a> TreeRangeDigest<'a> { let left_contour: Vec<_> = left_contour.collect(); Self { hasher: HasherWithStats::new(hasher), - current_leaf: LeafNode::new( - start_key, - start_entry.base.value_hash, - start_entry.base.leaf_index, - ), + current_leaf: LeafNode::new(start_entry.base), left_contour: left_contour.try_into().unwrap(), // ^ `unwrap()` is safe by construction; `left_contour` will always have necessary length } @@ -161,13 +153,13 @@ impl<'a> TreeRangeDigest<'a> { /// # Panics /// /// Panics if the provided `key` is not greater than the previous key provided to this digest. - pub fn update(&mut self, key: Key, entry: TreeEntry) { + pub fn update(&mut self, entry: TreeEntry) { assert!( - key > self.current_leaf.full_key, + entry.key > self.current_leaf.full_key, "Keys provided to a digest must be monotonically increasing" ); - let diverging_level = utils::find_diverging_bit(self.current_leaf.full_key, key) + 1; + let diverging_level = utils::find_diverging_bit(self.current_leaf.full_key, entry.key) + 1; // Hash the current leaf up to the `diverging_level`, taking current `left_contour` into account. let mut hash = self @@ -188,7 +180,7 @@ impl<'a> TreeRangeDigest<'a> { } // Record the computed hash. self.left_contour[TREE_DEPTH - diverging_level] = hash; - self.current_leaf = LeafNode::new(key, entry.value_hash, entry.leaf_index); + self.current_leaf = LeafNode::new(entry); } /// Finalizes this digest and returns the root hash of the tree. @@ -196,8 +188,8 @@ impl<'a> TreeRangeDigest<'a> { /// # Panics /// /// Panics if the provided `final_key` is not greater than the previous key provided to this digest. - pub fn finalize(mut self, final_key: Key, final_entry: &TreeEntryWithProof) -> ValueHash { - self.update(final_key, final_entry.base); + pub fn finalize(mut self, final_entry: &TreeEntryWithProof) -> ValueHash { + self.update(final_entry.base); let full_path = self .hasher @@ -206,9 +198,9 @@ impl<'a> TreeRangeDigest<'a> { let zipped_paths = self.left_contour.into_iter().zip(full_path); let mut hash = self .hasher - .hash_leaf(&final_entry.base.value_hash, final_entry.base.leaf_index); + .hash_leaf(&final_entry.base.value, final_entry.base.leaf_index); for (depth, (left, right)) in zipped_paths.enumerate() { - hash = if final_key.bit(depth) { + hash = if final_entry.base.key.bit(depth) { self.hasher.hash_branch(&left, &hash) } else { self.hasher.hash_branch(&hash, &right) diff --git a/core/lib/merkle_tree/src/lib.rs b/core/lib/merkle_tree/src/lib.rs index 166400cbb640..85ace50aada5 100644 --- a/core/lib/merkle_tree/src/lib.rs +++ b/core/lib/merkle_tree/src/lib.rs @@ -26,10 +26,15 @@ //! - Hash of a vacant leaf is `hash([0_u8; 40])`, where `hash` is the hash function used //! (Blake2s-256). //! - Hash of an occupied leaf is `hash(u64::to_be_bytes(leaf_index) ++ value_hash)`, -//! where `leaf_index` is the 1-based index of the leaf key in the order of insertion, +//! where `leaf_index` is a 1-based index of the leaf key provided when the leaf is inserted / updated, //! `++` is byte concatenation. //! - Hash of an internal node is `hash(left_child_hash ++ right_child_hash)`. //! +//! Currently in zksync, leaf indices enumerate leaves in the order of their insertion into the tree. +//! Indices are computed externally and are provided to the tree as inputs; the tree doesn't verify +//! index assignment and doesn't rely on particular index assignment assumptions (other than when +//! [verifying tree consistency](MerkleTree::verify_consistency())). +//! //! [Jellyfish Merkle tree]: https://developers.diem.com/papers/jellyfish-merkle-tree/2021-01-14.pdf // Linter settings. @@ -209,10 +214,10 @@ impl MerkleTree { /// # Return value /// /// Returns information about the update such as the final tree hash. - pub fn extend(&mut self, key_value_pairs: Vec<(Key, ValueHash)>) -> BlockOutput { + pub fn extend(&mut self, entries: Vec) -> BlockOutput { let next_version = self.db.manifest().unwrap_or_default().version_count; let storage = Storage::new(&self.db, &self.hasher, next_version, true); - let (output, patch) = storage.extend(key_value_pairs); + let (output, patch) = storage.extend(entries); self.db.apply_patch(patch); output } @@ -226,7 +231,7 @@ impl MerkleTree { /// instruction. pub fn extend_with_proofs( &mut self, - instructions: Vec<(Key, TreeInstruction)>, + instructions: Vec, ) -> BlockOutputWithProofs { let next_version = self.db.manifest().unwrap_or_default().version_count; let storage = Storage::new(&self.db, &self.hasher, next_version, true); diff --git a/core/lib/merkle_tree/src/pruning.rs b/core/lib/merkle_tree/src/pruning.rs index 21a3e8712fd7..5b1911ca6005 100644 --- a/core/lib/merkle_tree/src/pruning.rs +++ b/core/lib/merkle_tree/src/pruning.rs @@ -187,7 +187,7 @@ mod tests { use super::*; use crate::{ types::{Node, NodeKey}, - Database, Key, MerkleTree, PatchSet, ValueHash, + Database, Key, MerkleTree, PatchSet, TreeEntry, ValueHash, }; fn create_db() -> PatchSet { @@ -195,7 +195,7 @@ mod tests { for i in 0..5 { let key = Key::from(i); let value = ValueHash::from_low_u64_be(i); - MerkleTree::new(&mut db).extend(vec![(key, value)]); + MerkleTree::new(&mut db).extend(vec![TreeEntry::new(key, i + 1, value)]); } db } @@ -245,9 +245,9 @@ mod tests { assert!(start.elapsed() < Duration::from_secs(10)); } - fn generate_key_value_pairs(indexes: impl Iterator) -> Vec<(Key, ValueHash)> { + fn generate_key_value_pairs(indexes: impl Iterator) -> Vec { indexes - .map(|i| (Key::from(i), ValueHash::from_low_u64_be(i))) + .map(|i| TreeEntry::new(Key::from(i), i + 1, ValueHash::from_low_u64_be(i))) .collect() } @@ -273,7 +273,7 @@ mod tests { let mut tree = MerkleTree::new(&mut db); for version in first_retained_version..=latest_version { - tree.verify_consistency(version).unwrap(); + tree.verify_consistency(version, true).unwrap(); } let kvs = generate_key_value_pairs(100..200); @@ -290,7 +290,7 @@ mod tests { let tree = MerkleTree::new(&mut db); for version in first_retained_version..=latest_version { - tree.verify_consistency(version).unwrap(); + tree.verify_consistency(version, true).unwrap(); } assert_no_stale_keys(&db, first_retained_version); } @@ -318,8 +318,8 @@ mod tests { const ITERATIVE_BATCH_COUNT: usize = 10; let mut db = PatchSet::default(); - let kvs: Vec<_> = (0_u32..100) - .map(|i| (Key::from(i), ValueHash::zero())) + let kvs: Vec<_> = (0_u64..100) + .map(|i| TreeEntry::new(Key::from(i), i + 1, ValueHash::zero())) .collect(); let batch_count = if initialize_iteratively { @@ -335,8 +335,8 @@ mod tests { // Completely overwrite all keys. let new_value_hash = ValueHash::from_low_u64_be(1_000); - let new_kvs = (0_u32..100) - .map(|i| (Key::from(i), new_value_hash)) + let new_kvs = (0_u64..100) + .map(|i| TreeEntry::new(Key::from(i), i + 1, new_value_hash)) .collect(); MerkleTree::new(&mut db).extend(new_kvs); @@ -364,16 +364,16 @@ mod tests { prune_iteratively: bool, ) { let mut db = PatchSet::default(); - let kvs: Vec<_> = (0_u32..100) - .map(|i| (Key::from(i), ValueHash::zero())) + let kvs: Vec<_> = (0_u64..100) + .map(|i| TreeEntry::new(Key::from(i), i + 1, ValueHash::zero())) .collect(); MerkleTree::new(&mut db).extend(kvs); let leaf_keys_in_db = leaf_keys(&mut db); // Completely overwrite all keys in several batches. let new_value_hash = ValueHash::from_low_u64_be(1_000); - let new_kvs: Vec<_> = (0_u32..100) - .map(|i| (Key::from(i), new_value_hash)) + let new_kvs: Vec<_> = (0_u64..100) + .map(|i| TreeEntry::new(Key::from(i), i + 1, new_value_hash)) .collect(); for chunk in new_kvs.chunks(20) { MerkleTree::new(&mut db).extend(chunk.to_vec()); diff --git a/core/lib/merkle_tree/src/recovery.rs b/core/lib/merkle_tree/src/recovery.rs index 85ac578cc0a1..d1f2618a5cdd 100644 --- a/core/lib/merkle_tree/src/recovery.rs +++ b/core/lib/merkle_tree/src/recovery.rs @@ -40,23 +40,11 @@ use std::time::Instant; use crate::{ hasher::{HashTree, HasherWithStats}, storage::{PatchSet, PruneDatabase, PrunePatchSet, Storage}, - types::{Key, Manifest, Root, TreeTags, ValueHash}, + types::{Key, Manifest, Root, TreeEntry, TreeTags, ValueHash}, MerkleTree, }; use zksync_crypto::hasher::blake2::Blake2Hasher; -/// Entry in a Merkle tree used during recovery. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct RecoveryEntry { - /// Entry key. - pub key: Key, - /// Entry value. - pub value: ValueHash, - /// Leaf index associated with the entry. It is **not** checked whether leaf indices are well-formed - /// during recovery (e.g., that they are unique). - pub leaf_index: u64, -} - /// Handle to a Merkle tree during its recovery. #[derive(Debug)] pub struct MerkleTreeRecovery { @@ -154,7 +142,7 @@ impl MerkleTreeRecovery { %entries.key_range = entries_key_range(&entries), ), )] - pub fn extend_linear(&mut self, entries: Vec) { + pub fn extend_linear(&mut self, entries: Vec) { tracing::debug!("Started extending tree"); let started_at = Instant::now(); @@ -177,7 +165,7 @@ impl MerkleTreeRecovery { entries.len = entries.len(), ), )] - pub fn extend_random(&mut self, entries: Vec) { + pub fn extend_random(&mut self, entries: Vec) { tracing::debug!("Started extending tree"); let started_at = Instant::now(); @@ -242,7 +230,7 @@ impl MerkleTreeRecovery { } } -fn entries_key_range(entries: &[RecoveryEntry]) -> String { +fn entries_key_range(entries: &[TreeEntry]) -> String { let (Some(first), Some(last)) = (entries.first(), entries.last()) else { return "(empty)".to_owned(); }; @@ -280,11 +268,7 @@ mod tests { #[test] fn recovering_tree_with_single_node() { let mut recovery = MerkleTreeRecovery::new(PatchSet::default(), 42); - let recovery_entry = RecoveryEntry { - key: Key::from(123), - value: ValueHash::repeat_byte(1), - leaf_index: 1, - }; + let recovery_entry = TreeEntry::new(Key::from(123), 1, ValueHash::repeat_byte(1)); recovery.extend_linear(vec![recovery_entry]); let tree = recovery.finalize(); @@ -292,13 +276,8 @@ mod tests { let mut hasher = HasherWithStats::new(&Blake2Hasher); assert_eq!( tree.latest_root_hash(), - LeafNode::new( - recovery_entry.key, - recovery_entry.value, - recovery_entry.leaf_index - ) - .hash(&mut hasher, 0) + LeafNode::new(recovery_entry).hash(&mut hasher, 0) ); - tree.verify_consistency(42).unwrap(); + tree.verify_consistency(42, true).unwrap(); } } diff --git a/core/lib/merkle_tree/src/storage/mod.rs b/core/lib/merkle_tree/src/storage/mod.rs index c5a56abfca90..ae273d22f323 100644 --- a/core/lib/merkle_tree/src/storage/mod.rs +++ b/core/lib/merkle_tree/src/storage/mod.rs @@ -18,12 +18,10 @@ pub use self::{ use crate::{ hasher::HashTree, metrics::{TreeUpdaterStats, BLOCK_TIMINGS, GENERAL_METRICS}, - recovery::RecoveryEntry, types::{ BlockOutput, ChildRef, InternalNode, Key, LeafNode, Manifest, Nibbles, Node, Root, - TreeLogEntry, TreeTags, ValueHash, + TreeEntry, TreeLogEntry, TreeTags, ValueHash, }, - utils::increment_counter, }; /// Tree operation: either inserting a new version or updating an existing one (the latter is only @@ -132,17 +130,17 @@ impl TreeUpdater { /// hashes for all updated nodes in [`Self::finalize()`]. fn insert( &mut self, - key: Key, - value_hash: ValueHash, + entry: TreeEntry, parent_nibbles: &Nibbles, - leaf_index_fn: impl FnOnce() -> u64, ) -> (TreeLogEntry, NewLeafData) { let version = self.patch_set.root_version(); + let key = entry.key; + let traverse_outcome = self.patch_set.traverse(key, parent_nibbles); let (log, leaf_data) = match traverse_outcome { TraverseOutcome::LeafMatch(nibbles, mut leaf) => { - let log = TreeLogEntry::update(leaf.value_hash, leaf.leaf_index); - leaf.value_hash = value_hash; + let log = TreeLogEntry::update(leaf.leaf_index, leaf.value_hash); + leaf.update_from(entry); self.patch_set.insert(nibbles, leaf.into()); self.metrics.updated_leaves += 1; (log, NewLeafData::new(nibbles, leaf)) @@ -173,23 +171,20 @@ impl TreeUpdater { nibble_idx += 1; } - let leaf_index = leaf_index_fn(); - let new_leaf = LeafNode::new(key, value_hash, leaf_index); + let new_leaf = LeafNode::new(entry); let new_leaf_nibbles = Nibbles::new(&key, nibble_idx + 1); let leaf_data = NewLeafData::new(new_leaf_nibbles, new_leaf); let moved_leaf_nibbles = Nibbles::new(&leaf.full_key, nibble_idx + 1); let leaf_data = leaf_data.with_adjacent_leaf(moved_leaf_nibbles, leaf); - (TreeLogEntry::insert(leaf_index), leaf_data) + (TreeLogEntry::Inserted, leaf_data) } TraverseOutcome::MissingChild(nibbles) if nibbles.nibble_count() == 0 => { // The root is currently empty; we replace it with a leaf. - let leaf_index = leaf_index_fn(); - debug_assert_eq!(leaf_index, 1); - let root_leaf = LeafNode::new(key, value_hash, leaf_index); + let root_leaf = LeafNode::new(entry); self.set_root_node(root_leaf.into()); let leaf_data = NewLeafData::new(Nibbles::EMPTY, root_leaf); - (TreeLogEntry::insert(1), leaf_data) + (TreeLogEntry::Inserted, leaf_data) } TraverseOutcome::MissingChild(nibbles) => { @@ -198,10 +193,9 @@ impl TreeUpdater { unreachable!("Node parent must be an internal node"); }; parent.insert_child_ref(last_nibble, ChildRef::leaf(version)); - let leaf_index = leaf_index_fn(); - let new_leaf = LeafNode::new(key, value_hash, leaf_index); + let new_leaf = LeafNode::new(entry); let leaf_data = NewLeafData::new(nibbles, new_leaf); - (TreeLogEntry::insert(leaf_index), leaf_data) + (TreeLogEntry::Inserted, leaf_data) } }; @@ -289,19 +283,20 @@ impl<'a, DB: Database + ?Sized> Storage<'a, DB> { /// Extends the Merkle tree in the lightweight operation mode, without intermediate hash /// computations. - pub fn extend(mut self, key_value_pairs: Vec<(Key, ValueHash)>) -> (BlockOutput, PatchSet) { + pub fn extend(mut self, entries: Vec) -> (BlockOutput, PatchSet) { let load_nodes_latency = BLOCK_TIMINGS.load_nodes.start(); - let sorted_keys = SortedKeys::new(key_value_pairs.iter().map(|(key, _)| *key)); + let sorted_keys = SortedKeys::new(entries.iter().map(|entry| entry.key)); let parent_nibbles = self.updater.load_ancestors(&sorted_keys, self.db); let load_nodes_latency = load_nodes_latency.observe(); tracing::debug!("Load stage took {load_nodes_latency:?}"); let extend_patch_latency = BLOCK_TIMINGS.extend_patch.start(); - let mut logs = Vec::with_capacity(key_value_pairs.len()); - for ((key, value_hash), parent_nibbles) in key_value_pairs.into_iter().zip(parent_nibbles) { - let (log, _) = self.updater.insert(key, value_hash, &parent_nibbles, || { - increment_counter(&mut self.leaf_count) - }); + let mut logs = Vec::with_capacity(entries.len()); + for (entry, parent_nibbles) in entries.into_iter().zip(parent_nibbles) { + let (log, _) = self.updater.insert(entry, &parent_nibbles); + if matches!(log, TreeLogEntry::Inserted) { + self.leaf_count += 1; + } logs.push(log); } let extend_patch_latency = extend_patch_latency.observe(); @@ -321,10 +316,7 @@ impl<'a, DB: Database + ?Sized> Storage<'a, DB> { Some(self.updater.load_greatest_key(self.db)?.0.full_key) } - pub fn extend_during_linear_recovery( - mut self, - recovery_entries: Vec, - ) -> PatchSet { + pub fn extend_during_linear_recovery(mut self, recovery_entries: Vec) -> PatchSet { let (mut prev_key, mut prev_nibbles) = match self.updater.load_greatest_key(self.db) { Some((leaf, nibbles)) => (Some(leaf.full_key), nibbles), None => (None, Nibbles::EMPTY), @@ -343,9 +335,7 @@ impl<'a, DB: Database + ?Sized> Storage<'a, DB> { let key_nibbles = Nibbles::new(&entry.key, prev_nibbles.nibble_count()); let parent_nibbles = prev_nibbles.common_prefix(&key_nibbles); - let (_, new_leaf) = - self.updater - .insert(entry.key, entry.value, &parent_nibbles, || entry.leaf_index); + let (_, new_leaf) = self.updater.insert(entry, &parent_nibbles); prev_nibbles = new_leaf.nibbles; self.leaf_count += 1; } @@ -356,10 +346,7 @@ impl<'a, DB: Database + ?Sized> Storage<'a, DB> { patch } - pub fn extend_during_random_recovery( - mut self, - recovery_entries: Vec, - ) -> PatchSet { + pub fn extend_during_random_recovery(mut self, recovery_entries: Vec) -> PatchSet { let load_nodes_latency = BLOCK_TIMINGS.load_nodes.start(); let sorted_keys = SortedKeys::new(recovery_entries.iter().map(|entry| entry.key)); let parent_nibbles = self.updater.load_ancestors(&sorted_keys, self.db); @@ -368,8 +355,7 @@ impl<'a, DB: Database + ?Sized> Storage<'a, DB> { let extend_patch_latency = BLOCK_TIMINGS.extend_patch.start(); for (entry, parent_nibbles) in recovery_entries.into_iter().zip(parent_nibbles) { - self.updater - .insert(entry.key, entry.value, &parent_nibbles, || entry.leaf_index); + self.updater.insert(entry, &parent_nibbles); self.leaf_count += 1; } let extend_patch_latency = extend_patch_latency.observe(); diff --git a/core/lib/merkle_tree/src/storage/patch.rs b/core/lib/merkle_tree/src/storage/patch.rs index 6d0c38d6c9fb..ff41fb2f6bf3 100644 --- a/core/lib/merkle_tree/src/storage/patch.rs +++ b/core/lib/merkle_tree/src/storage/patch.rs @@ -680,7 +680,7 @@ mod tests { use super::*; use crate::{ storage::Storage, - types::{Key, LeafNode}, + types::{Key, LeafNode, TreeEntry}, }; fn patch_len(patch: &WorkingPatchSet) -> usize { @@ -697,7 +697,7 @@ mod tests { let key = Key::from_little_endian(&[i; 32]); let nibbles = Nibbles::new(&key, 2 + usize::from(i) % 4); // ^ We need nibble count at least 2 for all `nibbles` to be distinct. - let leaf = LeafNode::new(key, ValueHash::zero(), i.into()); + let leaf = LeafNode::new(TreeEntry::new(key, i.into(), ValueHash::zero())); patch.insert(nibbles, leaf.into()); nibbles }); @@ -742,7 +742,8 @@ mod tests { // Test DB with a single entry. let mut db = PatchSet::default(); let key = Key::from(1234_u64); - let (_, patch) = Storage::new(&db, &(), 0, true).extend(vec![(key, ValueHash::zero())]); + let (_, patch) = + Storage::new(&db, &(), 0, true).extend(vec![TreeEntry::new(key, 1, ValueHash::zero())]); db.apply_patch(patch); let mut patch = WorkingPatchSet::new(1, db.root(0).unwrap()); @@ -754,8 +755,11 @@ mod tests { // Test DB with multiple entries. let other_key = Key::from_little_endian(&[0xa0; 32]); - let (_, patch) = - Storage::new(&db, &(), 1, true).extend(vec![(other_key, ValueHash::zero())]); + let (_, patch) = Storage::new(&db, &(), 1, true).extend(vec![TreeEntry::new( + other_key, + 2, + ValueHash::zero(), + )]); db.apply_patch(patch); let mut patch = WorkingPatchSet::new(2, db.root(1).unwrap()); @@ -766,8 +770,11 @@ mod tests { assert_eq!(load_result.db_reads, 1); let greater_key = Key::from_little_endian(&[0xaf; 32]); - let (_, patch) = - Storage::new(&db, &(), 2, true).extend(vec![(greater_key, ValueHash::zero())]); + let (_, patch) = Storage::new(&db, &(), 2, true).extend(vec![TreeEntry::new( + greater_key, + 3, + ValueHash::zero(), + )]); db.apply_patch(patch); let mut patch = WorkingPatchSet::new(3, db.root(2).unwrap()); diff --git a/core/lib/merkle_tree/src/storage/proofs.rs b/core/lib/merkle_tree/src/storage/proofs.rs index 9e2d172bd6bd..81f140088d37 100644 --- a/core/lib/merkle_tree/src/storage/proofs.rs +++ b/core/lib/merkle_tree/src/storage/proofs.rs @@ -15,26 +15,6 @@ //! with root at level 4 (= 1 nibble). Thus, the patch sets and Merkle proofs //! produced by each group are mostly disjoint; they intersect only at the root node level. //! -//! ## Computing leaf indices -//! -//! We need to determine leaf indices for all write instructions. Indices potentially depend -//! on the entire list of `instructions`, so we should determine leaf indices before -//! parallelization. Otherwise, we'd need to sync between parallelized tasks, which defeats -//! the purpose of parallelization. -//! -//! We precompute indices as a separate step using the following observations: -//! -//! - If a leaf is present in the tree *before* `instructions` are applied, its index -//! can be obtained from the node ancestors loaded on the first step of the process. -//! - Otherwise, a leaf may have been added by a previous instruction for the same key. -//! Since we already need [`SortedKeys`] to efficiently load ancestors, it's easy -//! to determine such pairs of instructions. -//! - Otherwise, we have a first write, and the leaf index is defined as the current leaf -//! count. -//! -//! In summary, we can determine leaf indices for all write `instructions` in linear time -//! and without synchronization required during the parallel steps of the process. -//! //! ## Merging Merkle proofs //! //! The proofs produced by different groups only intersect at levels 0..4. This can be dealt with @@ -68,7 +48,7 @@ use crate::{ BlockOutputWithProofs, InternalNode, Key, Nibbles, Node, TreeInstruction, TreeLogEntry, TreeLogEntryWithProof, ValueHash, }, - utils::{increment_counter, merge_by_index}, + utils::merge_by_index, }; /// Number of subtrees used for parallel computations. @@ -93,16 +73,13 @@ impl TreeUpdater { for instruction in instructions { let InstructionWithPrecomputes { index, - key, instruction, parent_nibbles, - leaf_index, } = instruction; let log = match instruction { - TreeInstruction::Write(value_hash) => { - let (log, leaf_data) = - self.insert(key, value_hash, &parent_nibbles, || leaf_index); + TreeInstruction::Write(entry) => { + let (log, leaf_data) = self.insert(entry, &parent_nibbles); let (new_root_hash, merkle_path) = self.update_node_hashes(hasher, &leaf_data); root_hash = new_root_hash; TreeLogEntryWithProof { @@ -111,7 +88,7 @@ impl TreeUpdater { root_hash, } } - TreeInstruction::Read => { + TreeInstruction::Read(key) => { let (log, merkle_path) = self.prove(hasher, key, &parent_nibbles); TreeLogEntryWithProof { base: log, @@ -183,7 +160,7 @@ impl TreeUpdater { self.patch_set .create_proof(hasher, key, parent_nibbles, SUBTREE_ROOT_LEVEL / 4); let operation = leaf.map_or(TreeLogEntry::ReadMissingKey, |leaf| { - TreeLogEntry::read(leaf.value_hash, leaf.leaf_index) + TreeLogEntry::read(leaf.leaf_index, leaf.value_hash) }); if matches!(operation, TreeLogEntry::ReadMissingKey) { @@ -259,16 +236,14 @@ impl TreeUpdater { impl<'a, DB: Database + ?Sized> Storage<'a, DB> { pub fn extend_with_proofs( mut self, - instructions: Vec<(Key, TreeInstruction)>, + instructions: Vec, ) -> (BlockOutputWithProofs, PatchSet) { let load_nodes_latency = BLOCK_TIMINGS.load_nodes.start(); - let sorted_keys = SortedKeys::new(instructions.iter().map(|(key, _)| *key)); + let sorted_keys = SortedKeys::new(instructions.iter().map(TreeInstruction::key)); let parent_nibbles = self.updater.load_ancestors(&sorted_keys, self.db); load_nodes_latency.observe(); - let leaf_indices = self.compute_leaf_indices(&instructions, sorted_keys, &parent_nibbles); - let instruction_parts = - InstructionWithPrecomputes::split(instructions, parent_nibbles, leaf_indices); + let instruction_parts = InstructionWithPrecomputes::split(instructions, parent_nibbles); let initial_root = self.updater.patch_set.ensure_internal_root_node(); let initial_metrics = self.updater.metrics; let storage_parts = self.updater.split(); @@ -310,44 +285,13 @@ impl<'a, DB: Database + ?Sized> Storage<'a, DB> { output_with_proofs } - /// Computes leaf indices for all writes in `instructions`. Leaf indices are not used for reads; - /// thus, the corresponding entries are always 0. - fn compute_leaf_indices( - &mut self, - instructions: &[(Key, TreeInstruction)], - mut sorted_keys: SortedKeys, - parent_nibbles: &[Nibbles], - ) -> Vec { - sorted_keys.remove_read_instructions(instructions); - let key_mentions = sorted_keys.key_mentions(instructions.len()); - let patch_set = &self.updater.patch_set; - - let mut leaf_indices = Vec::with_capacity(instructions.len()); - let it = instructions.iter().zip(parent_nibbles).enumerate(); - for (idx, ((key, instruction), nibbles)) in it { - let leaf_index = match (instruction, key_mentions[idx]) { - (TreeInstruction::Read, _) => 0, - // ^ Leaf indices are not used for read instructions. - (TreeInstruction::Write(_), KeyMention::First) => { - let leaf_index = match patch_set.get(nibbles) { - Some(Node::Leaf(leaf)) if leaf.full_key == *key => Some(leaf.leaf_index), - _ => None, - }; - leaf_index.unwrap_or_else(|| increment_counter(&mut self.leaf_count)) - } - (TreeInstruction::Write(_), KeyMention::SameAs(prev_idx)) => leaf_indices[prev_idx], - }; - leaf_indices.push(leaf_index); - } - leaf_indices - } - fn finalize_with_proofs( mut self, hasher: &mut HasherWithStats<'_>, root: InternalNode, logs: Vec<(usize, TreeLogEntryWithProof)>, ) -> (BlockOutputWithProofs, PatchSet) { + self.leaf_count += self.updater.metrics.new_leaves; tracing::debug!( "Finished updating tree; total leaf count: {}, stats: {:?}", self.leaf_count, @@ -370,95 +314,35 @@ impl<'a, DB: Database + ?Sized> Storage<'a, DB> { } } -/// Mention of a key in a block: either the first mention, or the same mention as the specified -/// 0-based index in the block. -#[derive(Debug, Clone, Copy)] -enum KeyMention { - First, - SameAs(usize), -} - -impl SortedKeys { - fn remove_read_instructions(&mut self, instructions: &[(Key, TreeInstruction)]) { - debug_assert_eq!(instructions.len(), self.0.len()); - - self.0.retain(|(idx, key)| { - let (key_for_instruction, instruction) = &instructions[*idx]; - debug_assert_eq!(key_for_instruction, key); - matches!(instruction, TreeInstruction::Write(_)) - }); - } - - /// Determines for the original sequence of `Key`s whether a particular key mention - /// is the first one, or it follows after another mention. - fn key_mentions(&self, original_len: usize) -> Vec { - debug_assert!(original_len >= self.0.len()); - - let mut flags = vec![KeyMention::First; original_len]; - let [(mut first_key_mention, mut prev_key), tail @ ..] = self.0.as_slice() else { - return flags; - }; - - // Note that `SameAs(_)` doesn't necessarily reference the first mention of a key, - // just one with a lesser index. This is OK for our purposes. - for &(idx, key) in tail { - if prev_key == key { - if idx > first_key_mention { - flags[idx] = KeyMention::SameAs(first_key_mention); - } else { - debug_assert!(idx < first_key_mention); // all indices should be unique - flags[first_key_mention] = KeyMention::SameAs(idx); - first_key_mention = idx; - } - } else { - prev_key = key; - first_key_mention = idx; - } - } - flags - } -} - /// [`TreeInstruction`] together with precomputed data necessary to efficiently parallelize /// Merkle tree traversal. #[derive(Debug)] struct InstructionWithPrecomputes { /// 0-based index of the instruction. index: usize, - /// Key read / written by the instruction. - key: Key, instruction: TreeInstruction, /// Nibbles for the parent node computed by [`Storage::load_ancestors()`]. parent_nibbles: Nibbles, - /// Leaf index for the operation computed by [`Storage::compute_leaf_indices()`]. - /// Always 0 for reads. - leaf_index: u64, } impl InstructionWithPrecomputes { /// Creates groups of instructions to be used during parallelized tree traversal. fn split( - instructions: Vec<(Key, TreeInstruction)>, + instructions: Vec, parent_nibbles: Vec, - leaf_indices: Vec, ) -> [Vec; SUBTREE_COUNT] { const EMPTY_VEC: Vec = Vec::new(); // ^ Need to extract this to a constant to be usable as an array initializer. let mut parts = [EMPTY_VEC; SUBTREE_COUNT]; - let it = instructions - .into_iter() - .zip(parent_nibbles) - .zip(leaf_indices); - for (index, (((key, instruction), parent_nibbles), leaf_index)) in it.enumerate() { - let first_nibble = Nibbles::nibble(&key, 0); + let it = instructions.into_iter().zip(parent_nibbles); + for (index, (instruction, parent_nibbles)) in it.enumerate() { + let first_nibble = Nibbles::nibble(&instruction.key(), 0); let part = &mut parts[first_nibble as usize]; part.push(Self { index, - key, instruction, parent_nibbles, - leaf_index, }); } parts @@ -472,8 +356,6 @@ mod tests { use super::*; use crate::types::Root; - const HASH: ValueHash = ValueHash::zero(); - fn byte_key(byte: u8) -> Key { Key::from_little_endian(&[byte; 32]) } @@ -485,88 +367,14 @@ mod tests { assert_eq!(sorted_keys.0, [1, 3, 4, 0, 2].map(|i| (i, keys[i]))); } - #[test] - fn computing_key_mentions() { - let keys = [4, 1, 3, 4, 3, 3].map(byte_key); - let sorted_keys = SortedKeys::new(keys.into_iter()); - let mentions = sorted_keys.key_mentions(6); - - assert_matches!( - mentions.as_slice(), - [ - KeyMention::First, KeyMention::First, KeyMention::First, - KeyMention::SameAs(0), KeyMention::SameAs(2), KeyMention::SameAs(i) - ] if *i == 2 || *i == 4 - ); - } - - #[test] - fn computing_leaf_indices() { - let db = prepare_db(); - let (instructions, expected_indices) = get_instructions_and_leaf_indices(); - let mut storage = Storage::new(&db, &(), 1, true); - let sorted_keys = SortedKeys::new(instructions.iter().map(|(key, _)| *key)); - let parent_nibbles = storage.updater.load_ancestors(&sorted_keys, &db); - - let leaf_indices = - storage.compute_leaf_indices(&instructions, sorted_keys, &parent_nibbles); - assert_eq!(leaf_indices, expected_indices); - } - - fn prepare_db() -> PatchSet { - let mut db = PatchSet::default(); - let (_, patch) = - Storage::new(&db, &(), 0, true).extend(vec![(byte_key(2), HASH), (byte_key(1), HASH)]); - db.apply_patch(patch); - db - } - - fn get_instructions_and_leaf_indices() -> (Vec<(Key, TreeInstruction)>, Vec) { - let instructions_and_indices = vec![ - (byte_key(3), TreeInstruction::Read, 0), - (byte_key(1), TreeInstruction::Write(HASH), 2), - (byte_key(2), TreeInstruction::Read, 0), - (byte_key(3), TreeInstruction::Write(HASH), 3), - (byte_key(1), TreeInstruction::Read, 0), - (byte_key(3), TreeInstruction::Write(HASH), 3), - (byte_key(2), TreeInstruction::Write(HASH), 1), - (byte_key(0xc0), TreeInstruction::Write(HASH), 4), - (byte_key(2), TreeInstruction::Write(HASH), 1), - ]; - instructions_and_indices - .into_iter() - .map(|(key, instr, idx)| ((key, instr), idx)) - .unzip() - } - - #[test] - fn extending_storage_with_proofs() { - let db = prepare_db(); - let (instructions, expected_indices) = get_instructions_and_leaf_indices(); - let storage = Storage::new(&db, &(), 1, true); - let (block_output, _) = storage.extend_with_proofs(instructions); - assert_eq!(block_output.leaf_count, 4); - - assert_eq!(block_output.logs.len(), expected_indices.len()); - for (expected_idx, log) in expected_indices.into_iter().zip(&block_output.logs) { - match log.base { - TreeLogEntry::Inserted { leaf_index } - | TreeLogEntry::Updated { leaf_index, .. } => { - assert_eq!(leaf_index, expected_idx); - } - _ => {} - } - } - } - #[test] fn proofs_for_empty_storage() { let db = PatchSet::default(); let storage = Storage::new(&db, &(), 0, true); let instructions = vec![ - (byte_key(1), TreeInstruction::Read), - (byte_key(2), TreeInstruction::Read), - (byte_key(0xff), TreeInstruction::Read), + TreeInstruction::Read(byte_key(1)), + TreeInstruction::Read(byte_key(2)), + TreeInstruction::Read(byte_key(0xff)), ]; let (block_output, patch) = storage.extend_with_proofs(instructions); assert_eq!(block_output.leaf_count, 0); diff --git a/core/lib/merkle_tree/src/storage/serialization.rs b/core/lib/merkle_tree/src/storage/serialization.rs index 15d67604cc04..6a9216fa104a 100644 --- a/core/lib/merkle_tree/src/storage/serialization.rs +++ b/core/lib/merkle_tree/src/storage/serialization.rs @@ -26,7 +26,11 @@ impl LeafNode { let leaf_index = leb128::read::unsigned(&mut bytes).map_err(|err| { DeserializeErrorKind::Leb128(err).with_context(ErrorContext::LeafIndex) })?; - Ok(Self::new(full_key, value_hash, leaf_index)) + Ok(Self { + full_key, + value_hash, + leaf_index, + }) } pub(super) fn serialize(&self, buffer: &mut Vec) { @@ -297,6 +301,7 @@ impl Manifest { #[cfg(test)] mod tests { use super::*; + use crate::types::TreeEntry; use zksync_types::H256; #[test] @@ -369,7 +374,7 @@ mod tests { #[test] fn serializing_leaf_node() { - let leaf = LeafNode::new(513.into(), H256([4; 32]), 42); + let leaf = LeafNode::new(TreeEntry::new(513.into(), 42, H256([4; 32]))); let mut buffer = vec![]; leaf.serialize(&mut buffer); assert_eq!(buffer[..30], [0; 30]); // padding for the key @@ -426,7 +431,7 @@ mod tests { #[test] fn serializing_root_with_leaf() { - let leaf = LeafNode::new(513.into(), H256([4; 32]), 42); + let leaf = LeafNode::new(TreeEntry::new(513.into(), 42, H256([4; 32]))); let root = Root::new(1, leaf.into()); let mut buffer = vec![]; root.serialize(&mut buffer); diff --git a/core/lib/merkle_tree/src/storage/tests.rs b/core/lib/merkle_tree/src/storage/tests.rs index 958c906289ea..e70cb057280e 100644 --- a/core/lib/merkle_tree/src/storage/tests.rs +++ b/core/lib/merkle_tree/src/storage/tests.rs @@ -25,7 +25,7 @@ pub(super) fn generate_nodes(version: u64, nibble_counts: &[usize]) -> HashMap) -> V fn reading_keys_does_not_change_child_version() { let mut db = PatchSet::default(); let storage = Storage::new(&db, &(), 0, true); - let kvs = vec![(FIRST_KEY, H256([0; 32])), (SECOND_KEY, H256([1; 32]))]; + let kvs = vec![ + TreeEntry::new(FIRST_KEY, 1, H256([0; 32])), + TreeEntry::new(SECOND_KEY, 2, H256([1; 32])), + ]; let (_, patch) = storage.extend(kvs); db.apply_patch(patch); let storage = Storage::new(&db, &(), 1, true); let instructions = vec![ - (FIRST_KEY, TreeInstruction::Read), - (E_KEY, TreeInstruction::Write(H256([2; 32]))), + TreeInstruction::Read(FIRST_KEY), + TreeInstruction::Write(TreeEntry::new(E_KEY, 3, H256([2; 32]))), ]; let (_, patch) = storage.extend_with_proofs(instructions); @@ -327,12 +339,15 @@ fn reading_keys_does_not_change_child_version() { fn read_ops_are_not_reflected_in_patch() { let mut db = PatchSet::default(); let storage = Storage::new(&db, &(), 0, true); - let kvs = vec![(FIRST_KEY, H256([0; 32])), (SECOND_KEY, H256([1; 32]))]; + let kvs = vec![ + TreeEntry::new(FIRST_KEY, 1, H256([0; 32])), + TreeEntry::new(SECOND_KEY, 2, H256([1; 32])), + ]; let (_, patch) = storage.extend(kvs); db.apply_patch(patch); let storage = Storage::new(&db, &(), 1, true); - let instructions = vec![(FIRST_KEY, TreeInstruction::Read)]; + let instructions = vec![TreeInstruction::Read(FIRST_KEY)]; let (_, patch) = storage.extend_with_proofs(instructions); assert!(patch.patches_by_version[&1].nodes.is_empty()); } @@ -351,7 +366,7 @@ fn read_instructions_do_not_lead_to_copied_nodes(writes_per_block: u64) { let mut database = PatchSet::default(); let storage = Storage::new(&database, &(), 0, true); let kvs = (0..key_count) - .map(|i| (big_endian_key(i), H256::zero())) + .map(|i| TreeEntry::new(big_endian_key(i), i + 1, H256::zero())) .collect(); let (_, patch) = storage.extend(kvs); database.apply_patch(patch); @@ -361,10 +376,11 @@ fn read_instructions_do_not_lead_to_copied_nodes(writes_per_block: u64) { // Select some existing keys to read. Keys may be repeated, this is fine for our purpose. let reads = (0..writes_per_block).map(|_| { let key = big_endian_key(rng.gen_range(0..key_count)); - (key, TreeInstruction::Read) + TreeInstruction::Read(key) + }); + let writes = (key_count..key_count + writes_per_block).map(|i| { + TreeInstruction::Write(TreeEntry::new(big_endian_key(i), i + 1, H256::zero())) }); - let writes = (key_count..key_count + writes_per_block) - .map(|i| (big_endian_key(i), TreeInstruction::Write(H256::zero()))); let mut instructions: Vec<_> = reads.chain(writes).collect(); instructions.shuffle(&mut rng); @@ -400,7 +416,7 @@ fn replaced_keys_are_correctly_tracked(writes_per_block: usize, with_proofs: boo let mut database = PatchSet::default(); let storage = Storage::new(&database, &(), 0, true); let kvs = (0..100) - .map(|i| (big_endian_key(i), H256::zero())) + .map(|i| TreeEntry::new(big_endian_key(i), i + 1, H256::zero())) .collect(); let (_, patch) = storage.extend(kvs); @@ -412,11 +428,11 @@ fn replaced_keys_are_correctly_tracked(writes_per_block: usize, with_proofs: boo let updates = (0..100) .choose_multiple(&mut rng, writes_per_block) .into_iter() - .map(|i| (big_endian_key(i), H256::zero())); + .map(|i| TreeEntry::new(big_endian_key(i), i + 1, H256::zero())); let storage = Storage::new(&database, &(), new_version, true); let patch = if with_proofs { - let instructions = updates.map(|(key, value)| (key, TreeInstruction::Write(value))); + let instructions = updates.map(TreeInstruction::Write); storage.extend_with_proofs(instructions.collect()).1 } else { storage.extend(updates.collect()).1 @@ -454,14 +470,18 @@ fn assert_replaced_keys(db: &PatchSet, patch: &PatchSet) { #[test] fn tree_handles_keys_at_terminal_level() { let mut db = PatchSet::default(); - let kvs = (0_u32..100) - .map(|i| (Key::from(i), ValueHash::zero())) + let kvs = (0_u64..100) + .map(|i| TreeEntry::new(Key::from(i), i + 1, ValueHash::zero())) .collect(); let (_, patch) = Storage::new(&db, &(), 0, true).extend(kvs); db.apply_patch(patch); // Overwrite a key and check that we don't panic. - let new_kvs = vec![(Key::from(0), ValueHash::from_low_u64_be(1))]; + let new_kvs = vec![TreeEntry::new( + Key::from(0), + 1, + ValueHash::from_low_u64_be(1), + )]; let (_, patch) = Storage::new(&db, &(), 1, true).extend(new_kvs); assert_eq!( @@ -483,7 +503,7 @@ fn tree_handles_keys_at_terminal_level() { #[test] fn recovery_flattens_node_versions() { let recovery_version = 100; - let recovery_entries = (0_u64..10).map(|i| RecoveryEntry { + let recovery_entries = (0_u64..10).map(|i| TreeEntry { key: Key::from(i) << 252, // the first key nibbles are distinct value: ValueHash::zero(), leaf_index: i + 1, @@ -516,7 +536,7 @@ fn recovery_flattens_node_versions() { #[test_casing(7, [256, 4, 5, 20, 69, 127, 128])] fn recovery_with_node_hierarchy(chunk_size: usize) { let recovery_version = 100; - let recovery_entries = (0_u64..256).map(|i| RecoveryEntry { + let recovery_entries = (0_u64..256).map(|i| TreeEntry { key: Key::from(i) << 248, // the first two key nibbles are distinct value: ValueHash::zero(), leaf_index: i + 1, @@ -567,7 +587,7 @@ fn recovery_with_node_hierarchy(chunk_size: usize) { #[test_casing(7, [256, 5, 7, 20, 59, 127, 128])] fn recovery_with_deep_node_hierarchy(chunk_size: usize) { let recovery_version = 1_000; - let recovery_entries = (0_u64..256).map(|i| RecoveryEntry { + let recovery_entries = (0_u64..256).map(|i| TreeEntry { key: Key::from(i), // the last two key nibbles are distinct value: ValueHash::zero(), leaf_index: i + 1, @@ -630,7 +650,7 @@ fn recovery_with_deep_node_hierarchy(chunk_size: usize) { fn recovery_workflow_with_multiple_stages() { let mut db = PatchSet::default(); let recovery_version = 100; - let recovery_entries = (0_u64..100).map(|i| RecoveryEntry { + let recovery_entries = (0_u64..100).map(|i| TreeEntry { key: Key::from(i), value: ValueHash::zero(), leaf_index: i, @@ -640,7 +660,7 @@ fn recovery_workflow_with_multiple_stages() { assert_eq!(patch.root(recovery_version).unwrap().leaf_count(), 100); db.apply_patch(patch); - let more_recovery_entries = (100_u64..200).map(|i| RecoveryEntry { + let more_recovery_entries = (100_u64..200).map(|i| TreeEntry { key: Key::from(i), value: ValueHash::zero(), leaf_index: i, @@ -653,7 +673,7 @@ fn recovery_workflow_with_multiple_stages() { // Check that all entries can be accessed let storage = Storage::new(&db, &(), recovery_version + 1, true); - let instructions = (0_u32..200).map(|i| (Key::from(i), TreeInstruction::Read)); + let instructions = (0_u32..200).map(|i| TreeInstruction::Read(Key::from(i))); let (output, _) = storage.extend_with_proofs(instructions.collect()); assert_eq!(output.leaf_count, 200); assert_eq!(output.logs.len(), 200); @@ -687,17 +707,15 @@ fn test_recovery_pruning_equivalence( ); let mut rng = StdRng::seed_from_u64(RNG_SEED); - let kvs = (0..100).map(|i| { - ( - U256([rng.gen(), rng.gen(), rng.gen(), rng.gen()]), - ValueHash::repeat_byte(i), - ) + let entries = (0..100).map(|i| { + let key = U256([rng.gen(), rng.gen(), rng.gen(), rng.gen()]); + TreeEntry::new(key, u64::from(i) + 1, ValueHash::repeat_byte(i)) }); - let kvs: Vec<_> = kvs.collect(); + let entries: Vec<_> = entries.collect(); // Add `kvs` into the tree in several commits. let mut db = PatchSet::default(); - for (version, chunk) in kvs.chunks(chunk_size).enumerate() { + for (version, chunk) in entries.chunks(chunk_size).enumerate() { let (_, patch) = Storage::new(&db, hasher, version as u64, true).extend(chunk.to_vec()); db.apply_patch(patch); } @@ -716,11 +734,7 @@ fn test_recovery_pruning_equivalence( // Generate recovery entries. let recovery_entries = all_nodes.values().filter_map(|node| { if let Node::Leaf(leaf) = node { - return Some(RecoveryEntry { - key: leaf.full_key, - value: leaf.value_hash, - leaf_index: leaf.leaf_index, - }); + return Some(TreeEntry::from(*leaf)); } None }); diff --git a/core/lib/merkle_tree/src/types/internal.rs b/core/lib/merkle_tree/src/types/internal.rs index 5e875f6e28ac..cb35b0281c2b 100644 --- a/core/lib/merkle_tree/src/types/internal.rs +++ b/core/lib/merkle_tree/src/types/internal.rs @@ -4,10 +4,9 @@ use std::{fmt, num::NonZeroU64}; -use zksync_types::{H256, U256}; - use crate::{ hasher::{HashTree, InternalNodeCache}, + types::{Key, TreeEntry, ValueHash}, utils::SmallMap, }; @@ -323,11 +322,6 @@ impl fmt::Display for NodeKey { } } -/// Key stored in the tree. -pub type Key = U256; -/// Hashed value stored in the tree. -pub type ValueHash = H256; - /// Leaf node of the tree. #[derive(Debug, Clone, Copy)] #[cfg_attr(test, derive(PartialEq, Eq))] @@ -338,13 +332,18 @@ pub struct LeafNode { } impl LeafNode { - pub(crate) fn new(full_key: Key, value_hash: ValueHash, leaf_index: u64) -> Self { + pub(crate) fn new(entry: TreeEntry) -> Self { Self { - full_key, - value_hash, - leaf_index, + full_key: entry.key, + value_hash: entry.value, + leaf_index: entry.leaf_index, } } + + pub(crate) fn update_from(&mut self, entry: TreeEntry) { + self.value_hash = entry.value; + self.leaf_index = entry.leaf_index; + } } /// Reference to a child in an [`InternalNode`]. @@ -556,6 +555,7 @@ impl StaleNodeKey { #[cfg(test)] mod tests { use super::*; + use zksync_types::U256; // `U256` uses little-endian `u64` ordering; i.e., this is // 0x_dead_beef_0000_0000_.._0000. diff --git a/core/lib/merkle_tree/src/types/mod.rs b/core/lib/merkle_tree/src/types/mod.rs index de35d9024b7b..15ab72b6911d 100644 --- a/core/lib/merkle_tree/src/types/mod.rs +++ b/core/lib/merkle_tree/src/types/mod.rs @@ -5,22 +5,53 @@ mod internal; pub(crate) use self::internal::{ ChildRef, Nibbles, NibblesBytes, StaleNodeKey, TreeTags, HASH_SIZE, KEY_SIZE, TREE_DEPTH, }; -pub use self::internal::{InternalNode, Key, LeafNode, Manifest, Node, NodeKey, Root, ValueHash}; +pub use self::internal::{InternalNode, LeafNode, Manifest, Node, NodeKey, Root}; + +use zksync_types::{H256, U256}; + +/// Key stored in the tree. +pub type Key = U256; +/// Hash type of values and intermediate nodes in the tree. +pub type ValueHash = H256; /// Instruction to read or write a tree value at a certain key. #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum TreeInstruction { - /// Read the current tree value. - Read, - /// Write the specified value. - Write(ValueHash), +pub enum TreeInstruction { + /// Read the current tree value at the specified key. + Read(K), + /// Write the specified entry. + Write(TreeEntry), +} + +impl TreeInstruction { + /// Creates a write instruction. + pub fn write(key: K, leaf_index: u64, value: ValueHash) -> Self { + Self::Write(TreeEntry::new(key, leaf_index, value)) + } + + /// Returns the tree key this instruction is related to. + pub fn key(&self) -> K { + match self { + Self::Read(key) => *key, + Self::Write(entry) => entry.key, + } + } + + pub(crate) fn map_key(&self, map_fn: impl FnOnce(&K) -> U) -> TreeInstruction { + match self { + Self::Read(key) => TreeInstruction::Read(map_fn(key)), + Self::Write(entry) => TreeInstruction::Write(entry.map_key(map_fn)), + } + } } /// Entry in a Merkle tree associated with a key. -#[derive(Debug, Clone, Copy)] -pub struct TreeEntry { +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct TreeEntry { + /// Tree key. + pub key: K, /// Value associated with the key. - pub value_hash: ValueHash, + pub value: ValueHash, /// Enumeration index of the key. pub leaf_index: u64, } @@ -28,23 +59,40 @@ pub struct TreeEntry { impl From for TreeEntry { fn from(leaf: LeafNode) -> Self { Self { - value_hash: leaf.value_hash, + key: leaf.full_key, + value: leaf.value_hash, leaf_index: leaf.leaf_index, } } } +impl TreeEntry { + /// Creates a new entry with the specified fields. + pub fn new(key: K, leaf_index: u64, value: ValueHash) -> Self { + Self { + key, + value, + leaf_index, + } + } + + pub(crate) fn map_key(&self, map_fn: impl FnOnce(&K) -> U) -> TreeEntry { + TreeEntry::new(map_fn(&self.key), self.leaf_index, self.value) + } +} + impl TreeEntry { - pub(crate) fn empty() -> Self { + pub(crate) fn empty(key: Key) -> Self { Self { - value_hash: ValueHash::zero(), + key, + value: ValueHash::zero(), leaf_index: 0, } } /// Returns `true` if and only if this entry encodes lack of a value. pub fn is_empty(&self) -> bool { - self.leaf_index == 0 && self.value_hash.is_zero() + self.leaf_index == 0 && self.value.is_zero() } pub(crate) fn with_merkle_path(self, merkle_path: Vec) -> TreeEntryWithProof { @@ -53,6 +101,12 @@ impl TreeEntry { merkle_path, } } + + /// Replaces the value in this entry and returns the modified entry. + #[must_use] + pub fn with_value(self, value: H256) -> Self { + Self { value, ..self } + } } /// Entry in a Merkle tree together with a proof of authenticity. @@ -86,10 +140,7 @@ pub struct BlockOutput { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum TreeLogEntry { /// A node was inserted into the tree. - Inserted { - /// Index of the inserted node. - leaf_index: u64, - }, + Inserted, /// A node with the specified index was updated. Updated { /// Index of the updated node. @@ -109,18 +160,14 @@ pub enum TreeLogEntry { } impl TreeLogEntry { - pub(crate) fn insert(leaf_index: u64) -> Self { - Self::Inserted { leaf_index } - } - - pub(crate) fn update(previous_value: ValueHash, leaf_index: u64) -> Self { + pub(crate) fn update(leaf_index: u64, previous_value: ValueHash) -> Self { Self::Updated { leaf_index, previous_value, } } - pub(crate) fn read(value: ValueHash, leaf_index: u64) -> Self { + pub(crate) fn read(leaf_index: u64, value: ValueHash) -> Self { Self::Read { leaf_index, value } } diff --git a/core/lib/merkle_tree/src/utils.rs b/core/lib/merkle_tree/src/utils.rs index 9542b24bbd3c..4771a940f2c8 100644 --- a/core/lib/merkle_tree/src/utils.rs +++ b/core/lib/merkle_tree/src/utils.rs @@ -114,11 +114,6 @@ impl SmallMap { } } -pub(crate) fn increment_counter(counter: &mut u64) -> u64 { - *counter += 1; - *counter -} - pub(crate) fn find_diverging_bit(lhs: Key, rhs: Key) -> usize { let diff = lhs ^ rhs; diff.leading_zeros() as usize diff --git a/core/lib/merkle_tree/tests/integration/common.rs b/core/lib/merkle_tree/tests/integration/common.rs index fd9e00855c20..096a54ce7111 100644 --- a/core/lib/merkle_tree/tests/integration/common.rs +++ b/core/lib/merkle_tree/tests/integration/common.rs @@ -5,23 +5,22 @@ use once_cell::sync::Lazy; use std::collections::HashMap; use zksync_crypto::hasher::{blake2::Blake2Hasher, Hasher}; -use zksync_merkle_tree::{HashTree, TreeInstruction}; +use zksync_merkle_tree::{HashTree, TreeEntry, TreeInstruction}; use zksync_types::{AccountTreeId, Address, StorageKey, H256, U256}; -pub fn generate_key_value_pairs(indexes: impl Iterator) -> Vec<(U256, H256)> { +pub fn generate_key_value_pairs(indexes: impl Iterator) -> Vec { let address: Address = "4b3af74f66ab1f0da3f2e4ec7a3cb99baf1af7b2".parse().unwrap(); let kvs = indexes.map(|idx| { let key = H256::from_low_u64_be(idx); let key = StorageKey::new(AccountTreeId::new(address), key); - (key.hashed_key_u256(), H256::from_low_u64_be(idx + 1)) + let value = H256::from_low_u64_be(idx + 1); + TreeEntry::new(key.hashed_key_u256(), idx + 1, value) }); kvs.collect() } -pub fn compute_tree_hash(kvs: impl Iterator) -> H256 { - let kvs_with_indices = kvs - .enumerate() - .map(|(i, (key, value))| (key, value, i as u64 + 1)); +pub fn compute_tree_hash(kvs: impl Iterator) -> H256 { + let kvs_with_indices = kvs.map(|entry| (entry.key, entry.value, entry.leaf_index)); compute_tree_hash_with_indices(kvs_with_indices) } @@ -70,17 +69,18 @@ fn compute_tree_hash_with_indices(kvs: impl Iterator) } // Computing the expected hash takes some time in the debug mode, so we memoize it. -pub static KVS_AND_HASH: Lazy<(Vec<(U256, H256)>, H256)> = Lazy::new(|| { - let kvs = generate_key_value_pairs(0..100); - let expected_hash = compute_tree_hash(kvs.iter().copied()); - (kvs, expected_hash) +pub static ENTRIES_AND_HASH: Lazy<(Vec, H256)> = Lazy::new(|| { + let entries = generate_key_value_pairs(0..100); + let expected_hash = compute_tree_hash(entries.iter().copied()); + (entries, expected_hash) }); -pub fn convert_to_writes(kvs: &[(U256, H256)]) -> Vec<(U256, TreeInstruction)> { - let kvs = kvs +pub fn convert_to_writes(entries: &[TreeEntry]) -> Vec { + entries .iter() - .map(|&(key, hash)| (key, TreeInstruction::Write(hash))); - kvs.collect() + .copied() + .map(TreeInstruction::Write) + .collect() } /// Emulates leaf index assignment in a real Merkle tree. @@ -88,22 +88,22 @@ pub fn convert_to_writes(kvs: &[(U256, H256)]) -> Vec<(U256, TreeInstruction)> { pub struct TreeMap(HashMap); impl TreeMap { - pub fn new(initial_entries: &[(U256, H256)]) -> Self { + pub fn new(initial_entries: &[TreeEntry]) -> Self { let map = initial_entries .iter() - .enumerate() - .map(|(i, (key, value))| (*key, (*value, i as u64 + 1))) + .map(|entry| (entry.key, (entry.value, entry.leaf_index))) .collect(); Self(map) } - pub fn extend(&mut self, kvs: &[(U256, H256)]) { - for &(key, new_value) in kvs { - if let Some((value, _)) = self.0.get_mut(&key) { - *value = new_value; + pub fn extend(&mut self, kvs: &[TreeEntry]) { + for &new_entry in kvs { + if let Some((value, leaf_index)) = self.0.get_mut(&new_entry.key) { + assert_eq!(*leaf_index, new_entry.leaf_index); // sanity check + *value = new_entry.value; } else { - let leaf_index = self.0.len() as u64 + 1; - self.0.insert(key, (new_value, leaf_index)); + self.0 + .insert(new_entry.key, (new_entry.value, new_entry.leaf_index)); } } } @@ -112,7 +112,7 @@ impl TreeMap { let entries = self .0 .iter() - .map(|(key, (value, idx))| (*key, *value, *idx)); + .map(|(key, (value, leaf_index))| (*key, *value, *leaf_index)); compute_tree_hash_with_indices(entries) } } diff --git a/core/lib/merkle_tree/tests/integration/consistency.rs b/core/lib/merkle_tree/tests/integration/consistency.rs index 7c1d69657bff..da3312d2002d 100644 --- a/core/lib/merkle_tree/tests/integration/consistency.rs +++ b/core/lib/merkle_tree/tests/integration/consistency.rs @@ -26,7 +26,7 @@ fn five_thousand_angry_monkeys_vs_merkle_tree() { let kvs = generate_key_value_pairs(0..100); tree.extend(kvs); - tree.verify_consistency(0).unwrap(); + tree.verify_consistency(0, true).unwrap(); let mut raw_db = db.into_inner(); let cf = MerkleTreeColumnFamily::Tree; @@ -53,7 +53,9 @@ fn five_thousand_angry_monkeys_vs_merkle_tree() { raw_db.write(batch).unwrap(); let mut db = RocksDBWrapper::from(raw_db); - let err = MerkleTree::new(&mut db).verify_consistency(0).unwrap_err(); + let err = MerkleTree::new(&mut db) + .verify_consistency(0, true) + .unwrap_err(); println!("{err}"); // Restore the value back so that it doesn't influence the following cases. diff --git a/core/lib/merkle_tree/tests/integration/domain.rs b/core/lib/merkle_tree/tests/integration/domain.rs index d3b666c88492..f3febda5f06a 100644 --- a/core/lib/merkle_tree/tests/integration/domain.rs +++ b/core/lib/merkle_tree/tests/integration/domain.rs @@ -7,14 +7,14 @@ use tempfile::TempDir; use std::slice; use zksync_crypto::hasher::blake2::Blake2Hasher; -use zksync_merkle_tree::{domain::ZkSyncTree, HashTree}; +use zksync_merkle_tree::{domain::ZkSyncTree, HashTree, TreeEntry, TreeInstruction}; use zksync_storage::RocksDB; use zksync_system_constants::ACCOUNT_CODE_STORAGE_ADDRESS; use zksync_types::{ - proofs::StorageLogMetadata, AccountTreeId, Address, L1BatchNumber, StorageKey, StorageLog, H256, + proofs::StorageLogMetadata, AccountTreeId, Address, L1BatchNumber, StorageKey, H256, }; -fn gen_storage_logs() -> Vec { +fn gen_storage_logs() -> Vec> { let addrs = vec![ "4b3af74f66ab1f0da3f2e4ec7a3cb99baf1af7b2", "ef4bb7b21c5fe7432a7d63876cc59ecc23b46636", @@ -32,7 +32,11 @@ fn gen_storage_logs() -> Vec { proof_keys .zip(proof_values) - .map(|(proof_key, proof_value)| StorageLog::new_write_log(proof_key, proof_value)) + .enumerate() + .map(|(i, (proof_key, proof_value))| { + let entry = TreeEntry::new(proof_key, i as u64 + 1, proof_value); + TreeInstruction::Write(entry) + }) .collect() } @@ -54,7 +58,11 @@ fn basic_workflow() { assert_eq!(metadata.rollup_last_leaf_index, 101); assert_eq!(metadata.initial_writes.len(), logs.len()); for (write, log) in metadata.initial_writes.iter().zip(&logs) { - assert_eq!(write.value, log.value); + let expected_value = match log { + TreeInstruction::Write(entry) => entry.value, + TreeInstruction::Read(_) => unreachable!(), + }; + assert_eq!(write.value, expected_value); } assert!(metadata.repeated_writes.is_empty()); @@ -124,7 +132,10 @@ fn filtering_out_no_op_writes() { // Add some actual repeated writes. let mut expected_writes_count = 0; for log in logs.iter_mut().step_by(3) { - log.value = H256::repeat_byte(0xff); + let TreeInstruction::Write(entry) = log else { + unreachable!("Unexpected instruction: {log:?}"); + }; + entry.value = H256::repeat_byte(0xff); expected_writes_count += 1; } let new_metadata = tree.process_l1_batch(&logs); @@ -155,14 +166,16 @@ fn revert_blocks() { // Add couple of blocks of distinct keys/values let mut logs: Vec<_> = proof_keys .zip(proof_values) - .map(|(proof_key, proof_value)| StorageLog::new_write_log(proof_key, proof_value)) + .map(|(proof_key, proof_value)| { + let entry = TreeEntry::new(proof_key, proof_value.to_low_u64_be() + 1, proof_value); + TreeInstruction::Write(entry) + }) .collect(); // Add a block with repeated keys let extra_logs = (0..block_size).map(move |i| { - StorageLog::new_write_log( - StorageKey::new(AccountTreeId::new(address), H256::from_low_u64_be(i as u64)), - H256::from_low_u64_be((i + 1) as u64), - ) + let key = StorageKey::new(AccountTreeId::new(address), H256::from_low_u64_be(i as u64)); + let entry = TreeEntry::new(key, i as u64 + 1, H256::from_low_u64_be(i as u64 + 1)); + TreeInstruction::Write(entry) }); logs.extend(extra_logs); @@ -277,7 +290,7 @@ fn read_logs() { let mut tree = ZkSyncTree::new_lightweight(db); let read_logs: Vec<_> = logs .into_iter() - .map(|log| StorageLog::new_read_log(log.key, log.value)) + .map(|instr| TreeInstruction::Read(instr.key())) .collect(); let read_metadata = tree.process_l1_batch(&read_logs); @@ -285,14 +298,13 @@ fn read_logs() { } fn create_write_log( + leaf_index: u64, address: Address, address_storage_key: [u8; 32], value: [u8; 32], -) -> StorageLog { - StorageLog::new_write_log( - StorageKey::new(AccountTreeId::new(address), H256(address_storage_key)), - H256(value), - ) +) -> TreeInstruction { + let key = StorageKey::new(AccountTreeId::new(address), H256(address_storage_key)); + TreeInstruction::Write(TreeEntry::new(key, leaf_index, H256(value))) } fn subtract_from_max_value(diff: u8) -> [u8; 32] { @@ -315,28 +327,33 @@ fn root_hash_compatibility() { ); let storage_logs = vec![ - create_write_log(ACCOUNT_CODE_STORAGE_ADDRESS, [0; 32], [1; 32]), + create_write_log(1, ACCOUNT_CODE_STORAGE_ADDRESS, [0; 32], [1; 32]), create_write_log( + 2, Address::from_low_u64_be(9223372036854775808), [254; 32], subtract_from_max_value(1), ), create_write_log( + 3, Address::from_low_u64_be(9223372036854775809), [253; 32], subtract_from_max_value(2), ), create_write_log( + 4, Address::from_low_u64_be(9223372036854775810), [252; 32], subtract_from_max_value(3), ), create_write_log( + 5, Address::from_low_u64_be(9223372036854775811), [251; 32], subtract_from_max_value(4), ), create_write_log( + 6, Address::from_low_u64_be(9223372036854775812), [250; 32], subtract_from_max_value(5), diff --git a/core/lib/merkle_tree/tests/integration/merkle_tree.rs b/core/lib/merkle_tree/tests/integration/merkle_tree.rs index 9f3eb970cd38..e4f052bb03c4 100644 --- a/core/lib/merkle_tree/tests/integration/merkle_tree.rs +++ b/core/lib/merkle_tree/tests/integration/merkle_tree.rs @@ -7,12 +7,14 @@ use std::{cmp, mem}; use zksync_crypto::hasher::blake2::Blake2Hasher; use zksync_merkle_tree::{ - Database, HashTree, MerkleTree, PatchSet, Patched, TreeInstruction, TreeLogEntry, + Database, HashTree, MerkleTree, PatchSet, Patched, TreeEntry, TreeInstruction, TreeLogEntry, TreeRangeDigest, }; use zksync_types::{AccountTreeId, Address, StorageKey, H256, U256}; -use crate::common::{compute_tree_hash, convert_to_writes, generate_key_value_pairs, KVS_AND_HASH}; +use crate::common::{ + compute_tree_hash, convert_to_writes, generate_key_value_pairs, ENTRIES_AND_HASH, +}; #[test] fn compute_tree_hash_works_correctly() { @@ -25,7 +27,7 @@ fn compute_tree_hash_works_correctly() { let address: Address = "4b3af74f66ab1f0da3f2e4ec7a3cb99baf1af7b2".parse().unwrap(); let key = StorageKey::new(AccountTreeId::new(address), H256::zero()); let key = key.hashed_key_u256(); - let hash = compute_tree_hash([(key, H256([1; 32]))].into_iter()); + let hash = compute_tree_hash([TreeEntry::new(key, 1, H256([1; 32]))].into_iter()); assert_eq!(hash, EXPECTED_HASH); } @@ -59,7 +61,7 @@ fn output_proofs_are_computed_correctly_on_empty_tree(kv_count: u64) { let reads = instructions .iter() - .map(|(key, _)| (*key, TreeInstruction::Read)); + .map(|instr| TreeInstruction::Read(instr.key())); let mut reads: Vec<_> = reads.collect(); reads.shuffle(&mut rng); let output = tree.extend_with_proofs(reads.clone()); @@ -77,25 +79,26 @@ fn entry_proofs_are_computed_correctly_on_empty_tree(kv_count: u64) { let expected_hash = compute_tree_hash(kvs.iter().copied()); tree.extend(kvs.clone()); - let existing_keys: Vec<_> = kvs.iter().map(|(key, _)| *key).collect(); + let existing_keys: Vec<_> = kvs.iter().map(|entry| entry.key).collect(); let entries = tree.entries_with_proofs(0, &existing_keys).unwrap(); assert_eq!(entries.len(), existing_keys.len()); - for ((key, value), entry) in kvs.iter().zip(entries) { - entry.verify(&Blake2Hasher, *key, expected_hash); - assert_eq!(entry.base.value_hash, *value); + for (input_entry, entry) in kvs.iter().zip(entries) { + entry.verify(&Blake2Hasher, expected_hash); + assert_eq!(entry.base, *input_entry); } // Test some keys adjacent to existing ones. - let adjacent_keys = kvs.iter().flat_map(|(key, _)| { + let adjacent_keys = kvs.iter().flat_map(|entry| { + let key = entry.key; [ - *key ^ (U256::one() << rng.gen_range(0..256)), - *key ^ (U256::one() << rng.gen_range(0..256)), - *key ^ (U256::one() << rng.gen_range(0..256)), + key ^ (U256::one() << rng.gen_range(0..256)), + key ^ (U256::one() << rng.gen_range(0..256)), + key ^ (U256::one() << rng.gen_range(0..256)), ] }); let random_keys = generate_key_value_pairs(kv_count..(kv_count * 2)) .into_iter() - .map(|(key, _)| key); + .map(|entry| entry.key); let mut missing_keys: Vec<_> = adjacent_keys.chain(random_keys).collect(); missing_keys.shuffle(&mut rng); @@ -103,7 +106,8 @@ fn entry_proofs_are_computed_correctly_on_empty_tree(kv_count: u64) { assert_eq!(entries.len(), missing_keys.len()); for (key, entry) in missing_keys.iter().zip(entries) { assert!(entry.base.is_empty()); - entry.verify(&Blake2Hasher, *key, expected_hash); + assert_eq!(entry.base.key, *key); + entry.verify(&Blake2Hasher, expected_hash); } } @@ -117,10 +121,13 @@ fn proofs_are_computed_correctly_for_mixed_instructions() { let output = tree.extend(kvs.clone()); let old_root_hash = output.root_hash; - let reads = kvs.iter().map(|(key, _)| (*key, TreeInstruction::Read)); + let reads = kvs.iter().map(|entry| TreeInstruction::Read(entry.key)); let mut instructions: Vec<_> = reads.collect(); // Overwrite all keys in the tree. - let writes: Vec<_> = kvs.iter().map(|(key, _)| (*key, H256::zero())).collect(); + let writes: Vec<_> = kvs + .iter() + .map(|entry| entry.with_value(H256::zero())) + .collect(); let expected_hash = compute_tree_hash(writes.iter().copied()); instructions.extend(convert_to_writes(&writes)); instructions.shuffle(&mut rng); @@ -145,7 +152,7 @@ fn proofs_are_computed_correctly_for_missing_keys() { let mut instructions = convert_to_writes(&kvs); let missing_reads = generate_key_value_pairs(20..50) .into_iter() - .map(|(key, _)| (key, TreeInstruction::Read)); + .map(|entry| TreeInstruction::Read(entry.key)); instructions.extend(missing_reads); instructions.shuffle(&mut rng); @@ -161,7 +168,7 @@ fn proofs_are_computed_correctly_for_missing_keys() { } fn test_intermediate_commits(db: &mut impl Database, chunk_size: usize) { - let (kvs, expected_hash) = &*KVS_AND_HASH; + let (kvs, expected_hash) = &*ENTRIES_AND_HASH; let mut final_hash = H256::zero(); let mut tree = MerkleTree::new(db); for chunk in kvs.chunks(chunk_size) { @@ -172,7 +179,7 @@ fn test_intermediate_commits(db: &mut impl Database, chunk_size: usize) { let latest_version = tree.latest_version().unwrap(); for version in 0..=latest_version { - tree.verify_consistency(version).unwrap(); + tree.verify_consistency(version, true).unwrap(); } } @@ -183,7 +190,7 @@ fn root_hash_is_computed_correctly_with_intermediate_commits(chunk_size: usize) #[test_casing(6, [3, 5, 10, 17, 28, 42])] fn output_proofs_are_computed_correctly_with_intermediate_commits(chunk_size: usize) { - let (kvs, expected_hash) = &*KVS_AND_HASH; + let (kvs, expected_hash) = &*ENTRIES_AND_HASH; let mut tree = MerkleTree::new(PatchSet::default()); let mut root_hash = Blake2Hasher.empty_subtree_hash(256); @@ -198,8 +205,8 @@ fn output_proofs_are_computed_correctly_with_intermediate_commits(chunk_size: us #[test_casing(4, [10, 17, 28, 42])] fn entry_proofs_are_computed_correctly_with_intermediate_commits(chunk_size: usize) { - let (kvs, _) = &*KVS_AND_HASH; - let all_keys: Vec<_> = kvs.iter().map(|(key, _)| *key).collect(); + let (kvs, _) = &*ENTRIES_AND_HASH; + let all_keys: Vec<_> = kvs.iter().map(|entry| entry.key).collect(); let mut tree = MerkleTree::new(PatchSet::default()); let mut root_hashes = vec![]; for chunk in kvs.chunks(chunk_size) { @@ -210,8 +217,9 @@ fn entry_proofs_are_computed_correctly_with_intermediate_commits(chunk_size: usi let entries = tree.entries_with_proofs(version as u64, &all_keys).unwrap(); assert_eq!(entries.len(), all_keys.len()); for (i, (key, entry)) in all_keys.iter().zip(entries).enumerate() { + assert_eq!(entry.base.key, *key); assert_eq!(entry.base.is_empty(), i >= (version + 1) * chunk_size); - entry.verify(&Blake2Hasher, *key, output.root_hash); + entry.verify(&Blake2Hasher, output.root_hash); } } @@ -220,14 +228,15 @@ fn entry_proofs_are_computed_correctly_with_intermediate_commits(chunk_size: usi let entries = tree.entries_with_proofs(version as u64, &all_keys).unwrap(); assert_eq!(entries.len(), all_keys.len()); for (i, (key, entry)) in all_keys.iter().zip(entries).enumerate() { + assert_eq!(entry.base.key, *key); assert_eq!(entry.base.is_empty(), i >= (version + 1) * chunk_size); - entry.verify(&Blake2Hasher, *key, root_hash); + entry.verify(&Blake2Hasher, root_hash); } } } fn test_accumulated_commits(db: DB, chunk_size: usize) -> DB { - let (kvs, expected_hash) = &*KVS_AND_HASH; + let (kvs, expected_hash) = &*ENTRIES_AND_HASH; let mut db = Patched::new(db); let mut final_hash = H256::zero(); for chunk in kvs.chunks(chunk_size) { @@ -242,7 +251,7 @@ fn test_accumulated_commits(db: DB, chunk_size: usize) -> DB { let tree = MerkleTree::new(&mut db); let latest_version = tree.latest_version().unwrap(); for version in 0..=latest_version { - tree.verify_consistency(version).unwrap(); + tree.verify_consistency(version, true).unwrap(); } db } @@ -253,9 +262,12 @@ fn accumulating_commits(chunk_size: usize) { } fn test_root_hash_computing_with_reverts(db: &mut impl Database) { - let (kvs, expected_hash) = &*KVS_AND_HASH; + let (kvs, expected_hash) = &*ENTRIES_AND_HASH; let (initial_update, final_update) = kvs.split_at(75); - let key_updates: Vec<_> = kvs.iter().map(|(key, _)| (*key, H256([255; 32]))).collect(); + let key_updates: Vec<_> = kvs + .iter() + .map(|entry| entry.with_value(H256([255; 32]))) + .collect(); let key_inserts = generate_key_value_pairs(100..200); let mut tree = MerkleTree::new(db); @@ -300,7 +312,7 @@ fn test_root_hash_computing_with_key_updates(db: impl Database) { // Overwrite some `kvs` entries and add some new ones. let changed_kvs = kvs.iter_mut().enumerate().filter_map(|(i, kv)| { if i % 3 == 1 { - kv.1 = H256::from_low_u64_be((i + 100) as u64); + *kv = kv.with_value(H256::from_low_u64_be((i + 100) as u64)); return Some(*kv); } None @@ -361,12 +373,12 @@ fn root_hash_is_computed_correctly_with_key_updates() { fn proofs_are_computed_correctly_with_key_updates(updated_keys: usize) { const RNG_SEED: u64 = 1_234; - let (kvs, expected_hash) = &*KVS_AND_HASH; + let (kvs, expected_hash) = &*ENTRIES_AND_HASH; let mut rng = StdRng::seed_from_u64(RNG_SEED); let old_instructions: Vec<_> = kvs[..updated_keys] .iter() - .map(|(key, _)| (*key, TreeInstruction::Write(H256([255; 32])))) + .map(|entry| TreeInstruction::Write(entry.with_value(H256([255; 32])))) .collect(); // Move the updated keys to the random places in the `kvs` vector. let mut writes = convert_to_writes(kvs); @@ -386,11 +398,11 @@ fn proofs_are_computed_correctly_with_key_updates(updated_keys: usize) { assert_eq!(output.root_hash(), Some(*expected_hash)); output.verify_proofs(&Blake2Hasher, root_hash, &instructions); - let keys: Vec<_> = kvs.iter().map(|(key, _)| *key).collect(); + let keys: Vec<_> = kvs.iter().map(|entry| entry.key).collect(); let proofs = tree.entries_with_proofs(1, &keys).unwrap(); - for ((key, value), proof) in kvs.iter().zip(proofs) { - assert_eq!(proof.base.value_hash, *value); - proof.verify(&Blake2Hasher, *key, *expected_hash); + for (entry, proof) in kvs.iter().zip(proofs) { + assert_eq!(proof.base, *entry); + proof.verify(&Blake2Hasher, *expected_hash); } } @@ -417,7 +429,11 @@ fn test_root_hash_equals_to_previous_implementation(db: &mut impl Database) { }) }); let values = (0..100).map(H256::from_low_u64_be); - let kvs: Vec<_> = keys.zip(values).collect(); + let kvs: Vec<_> = keys + .zip(values) + .enumerate() + .map(|(idx, (key, value))| TreeEntry::new(key, idx as u64 + 1, value)) + .collect(); let expected_hash = compute_tree_hash(kvs.iter().copied()); assert_eq!(expected_hash, PREV_IMPL_HASH); @@ -437,13 +453,13 @@ fn root_hash_equals_to_previous_implementation() { #[test_casing(7, [2, 3, 5, 10, 17, 28, 42])] fn range_proofs_with_multiple_existing_items(range_size: usize) { - let (kvs, expected_hash) = &*KVS_AND_HASH; + let (kvs, expected_hash) = &*ENTRIES_AND_HASH; assert!(range_size >= 2 && range_size <= kvs.len()); let mut tree = MerkleTree::new(PatchSet::default()); tree.extend(kvs.clone()); - let mut sorted_keys: Vec<_> = kvs.iter().map(|(key, _)| *key).collect(); + let mut sorted_keys: Vec<_> = kvs.iter().map(|entry| entry.key).collect(); sorted_keys.sort_unstable(); for start_idx in 0..(sorted_keys.len() - range_size) { @@ -460,10 +476,10 @@ fn range_proofs_with_multiple_existing_items(range_size: usize) { let other_entries = tree.entries(0, other_keys).unwrap(); let mut range = TreeRangeDigest::new(&Blake2Hasher, *first_key, &first_entry); - for (key, entry) in other_keys.iter().zip(other_entries) { - range.update(*key, entry); + for entry in other_entries { + range.update(entry); } - let range_hash = range.finalize(*last_key, &last_entry); + let range_hash = range.finalize(&last_entry); assert_eq!(range_hash, *expected_hash); } } @@ -479,7 +495,7 @@ fn range_proofs_with_random_ranges() { const RNG_SEED: u64 = 321; let mut rng = StdRng::seed_from_u64(RNG_SEED); - let (kvs, expected_hash) = &*KVS_AND_HASH; + let (kvs, expected_hash) = &*ENTRIES_AND_HASH; let mut tree = MerkleTree::new(PatchSet::default()); tree.extend(kvs.clone()); @@ -493,9 +509,9 @@ fn range_proofs_with_random_ranges() { } // Find out keys falling into the range. - let keys_in_range = kvs - .iter() - .filter_map(|&(key, _)| (key > start_key && key < end_key).then_some(key)); + let keys_in_range = kvs.iter().filter_map(|entry| { + (entry.key > start_key && entry.key < end_key).then_some(entry.key) + }); let mut keys_in_range: Vec<_> = keys_in_range.collect(); keys_in_range.sort_unstable(); println!("Proving range with {} keys", keys_in_range.len()); @@ -506,10 +522,10 @@ fn range_proofs_with_random_ranges() { let other_entries = tree.entries(0, &keys_in_range).unwrap(); let mut range = TreeRangeDigest::new(&Blake2Hasher, start_key, &first_entry); - for (key, entry) in keys_in_range.iter().zip(other_entries) { - range.update(*key, entry); + for entry in other_entries { + range.update(entry); } - let range_hash = range.finalize(end_key, &last_entry); + let range_hash = range.finalize(&last_entry); assert_eq!(range_hash, *expected_hash); } } @@ -633,7 +649,7 @@ mod rocksdb { fn tree_tags_mismatch() { let Harness { mut db, dir: _dir } = Harness::new(); let mut tree = MerkleTree::new(&mut db); - tree.extend(vec![(U256::zero(), H256::zero())]); + tree.extend(vec![TreeEntry::new(U256::zero(), 1, H256::zero())]); MerkleTree::with_hasher(&mut db, ()); } @@ -643,7 +659,7 @@ mod rocksdb { fn tree_tags_mismatch_with_cold_restart() { let Harness { db, dir } = Harness::new(); let mut tree = MerkleTree::new(db); - tree.extend(vec![(U256::zero(), H256::zero())]); + tree.extend(vec![TreeEntry::new(U256::zero(), 1, H256::zero())]); drop(tree); let db = RocksDBWrapper::new(dir.path()); diff --git a/core/lib/merkle_tree/tests/integration/recovery.rs b/core/lib/merkle_tree/tests/integration/recovery.rs index fda57f788514..6739e4ffe023 100644 --- a/core/lib/merkle_tree/tests/integration/recovery.rs +++ b/core/lib/merkle_tree/tests/integration/recovery.rs @@ -5,11 +5,10 @@ use test_casing::test_casing; use zksync_crypto::hasher::blake2::Blake2Hasher; use zksync_merkle_tree::{ - recovery::{MerkleTreeRecovery, RecoveryEntry}, - Database, MerkleTree, PatchSet, PruneDatabase, ValueHash, + recovery::MerkleTreeRecovery, Database, MerkleTree, PatchSet, PruneDatabase, ValueHash, }; -use crate::common::{convert_to_writes, generate_key_value_pairs, TreeMap, KVS_AND_HASH}; +use crate::common::{convert_to_writes, generate_key_value_pairs, TreeMap, ENTRIES_AND_HASH}; #[derive(Debug, Clone, Copy)] enum RecoveryKind { @@ -23,16 +22,8 @@ impl RecoveryKind { #[test] fn recovery_basics() { - let (kvs, expected_hash) = &*KVS_AND_HASH; - let recovery_entries = kvs - .iter() - .enumerate() - .map(|(i, &(key, value))| RecoveryEntry { - key, - value, - leaf_index: i as u64 + 1, - }); - let mut recovery_entries: Vec<_> = recovery_entries.collect(); + let (kvs, expected_hash) = &*ENTRIES_AND_HASH; + let mut recovery_entries: Vec<_> = kvs.clone(); recovery_entries.sort_unstable_by_key(|entry| entry.key); let greatest_key = recovery_entries[99].key; @@ -44,20 +35,12 @@ fn recovery_basics() { assert_eq!(recovery.root_hash(), *expected_hash); let tree = recovery.finalize(); - tree.verify_consistency(recovered_version).unwrap(); + tree.verify_consistency(recovered_version, true).unwrap(); } fn test_recovery_in_chunks(mut db: impl PruneDatabase, kind: RecoveryKind, chunk_size: usize) { - let (kvs, expected_hash) = &*KVS_AND_HASH; - let recovery_entries = kvs - .iter() - .enumerate() - .map(|(i, &(key, value))| RecoveryEntry { - key, - value, - leaf_index: i as u64 + 1, - }); - let mut recovery_entries: Vec<_> = recovery_entries.collect(); + let (kvs, expected_hash) = &*ENTRIES_AND_HASH; + let mut recovery_entries = kvs.clone(); if matches!(kind, RecoveryKind::Linear) { recovery_entries.sort_unstable_by_key(|entry| entry.key); } @@ -84,7 +67,7 @@ fn test_recovery_in_chunks(mut db: impl PruneDatabase, kind: RecoveryKind, chunk assert_eq!(recovery.root_hash(), *expected_hash); let mut tree = recovery.finalize(); - tree.verify_consistency(recovered_version).unwrap(); + tree.verify_consistency(recovered_version, true).unwrap(); // Check that new tree versions can be built and function as expected. test_tree_after_recovery(&mut tree, recovered_version, *expected_hash); } @@ -107,13 +90,13 @@ fn test_tree_after_recovery( let mut rng = StdRng::seed_from_u64(RNG_SEED); let mut kvs = generate_key_value_pairs(100..=150); let mut modified_kvs = generate_key_value_pairs(50..=100); - for (_, value) in &mut modified_kvs { - *value = ValueHash::repeat_byte(1); + for entry in &mut modified_kvs { + entry.value = ValueHash::repeat_byte(1); } + modified_kvs.shuffle(&mut rng); kvs.extend(modified_kvs); - kvs.shuffle(&mut rng); - let mut tree_map = TreeMap::new(&KVS_AND_HASH.0); + let mut tree_map = TreeMap::new(&ENTRIES_AND_HASH.0); let mut prev_root_hash = root_hash; for (i, chunk) in kvs.chunks(CHUNK_SIZE).enumerate() { tree_map.extend(chunk); @@ -129,7 +112,7 @@ fn test_tree_after_recovery( }; assert_eq!(new_root_hash, tree_map.root_hash()); - tree.verify_consistency(recovered_version + i as u64) + tree.verify_consistency(recovered_version + i as u64, true) .unwrap(); prev_root_hash = new_root_hash; } diff --git a/core/lib/zksync_core/src/api_server/tree/mod.rs b/core/lib/zksync_core/src/api_server/tree/mod.rs index 74dd3e5b70c1..7b4c9086ac68 100644 --- a/core/lib/zksync_core/src/api_server/tree/mod.rs +++ b/core/lib/zksync_core/src/api_server/tree/mod.rs @@ -54,7 +54,7 @@ impl TreeEntryWithProof { let mut merkle_path = src.merkle_path; merkle_path.reverse(); // Use root-to-leaf enumeration direction as in Ethereum Self { - value: src.base.value_hash, + value: src.base.value, index: src.base.leaf_index, merkle_path, } diff --git a/core/lib/zksync_core/src/metadata_calculator/helpers.rs b/core/lib/zksync_core/src/metadata_calculator/helpers.rs index 32f39276a1e5..9ae936febfe6 100644 --- a/core/lib/zksync_core/src/metadata_calculator/helpers.rs +++ b/core/lib/zksync_core/src/metadata_calculator/helpers.rs @@ -16,10 +16,10 @@ use zksync_dal::StorageProcessor; use zksync_health_check::{Health, HealthStatus}; use zksync_merkle_tree::{ domain::{TreeMetadata, ZkSyncTree, ZkSyncTreeReader}, - Key, MerkleTreeColumnFamily, NoVersionError, TreeEntryWithProof, + Key, MerkleTreeColumnFamily, NoVersionError, TreeEntryWithProof, TreeInstruction, }; use zksync_storage::{RocksDB, RocksDBOptions, StalledWritesRetries}; -use zksync_types::{block::L1BatchHeader, L1BatchNumber, StorageLog, H256}; +use zksync_types::{block::L1BatchHeader, L1BatchNumber, StorageKey, H256}; use super::metrics::{LoadChangesStage, TreeUpdateStage, METRICS}; @@ -147,7 +147,10 @@ impl AsyncTree { self.as_ref().root_hash() } - pub async fn process_l1_batch(&mut self, storage_logs: Vec) -> TreeMetadata { + pub async fn process_l1_batch( + &mut self, + storage_logs: Vec>, + ) -> TreeMetadata { let mut tree = self.inner.take().expect(Self::INCONSISTENT_MSG); let (tree, metadata) = tokio::task::spawn_blocking(move || { let metadata = tree.process_l1_batch(&storage_logs); @@ -242,7 +245,7 @@ impl Delayer { #[cfg_attr(test, derive(PartialEq))] pub(crate) struct L1BatchWithLogs { pub header: L1BatchHeader, - pub storage_logs: Vec, + pub storage_logs: Vec>, } impl L1BatchWithLogs { @@ -276,15 +279,22 @@ impl L1BatchWithLogs { .await; touched_slots_latency.observe_with_count(touched_slots.len()); + let leaf_indices_latency = METRICS.start_load_stage(LoadChangesStage::LoadLeafIndices); + let hashed_keys_for_writes: Vec<_> = + touched_slots.keys().map(StorageKey::hashed_key).collect(); + let l1_batches_for_initial_writes = storage + .storage_logs_dal() + .get_l1_batches_and_indices_for_initial_writes(&hashed_keys_for_writes) + .await; + leaf_indices_latency.observe_with_count(hashed_keys_for_writes.len()); + let mut storage_logs = BTreeMap::new(); for storage_key in protective_reads { touched_slots.remove(&storage_key); // ^ As per deduplication rules, all keys in `protective_reads` haven't *really* changed // in the considered L1 batch. Thus, we can remove them from `touched_slots` in order to simplify // their further processing. - - let log = StorageLog::new_read_log(storage_key, H256::zero()); - // ^ The tree doesn't use the read value, so we set it to zero. + let log = TreeInstruction::Read(storage_key); storage_logs.insert(storage_key, log); } tracing::debug!( @@ -292,45 +302,17 @@ impl L1BatchWithLogs { touched_slots.len() ); - // We don't want to update the tree with zero values which were never written to per storage log - // deduplication rules. If we write such values to the tree, it'd result in bogus tree hashes because - // new (bogus) leaf indices would be allocated for them. To filter out those values, it's sufficient - // to check when a `storage_key` was first written per `initial_writes` table. If this never occurred - // or occurred after the considered `l1_batch_number`, this means that the write must be ignored. - // - // Note that this approach doesn't filter out no-op writes of the same value, but this is fine; - // since no new leaf indices are allocated in the tree for them, such writes are no-op on the tree side as well. - let hashed_keys_for_zero_values: Vec<_> = touched_slots - .iter() - .filter(|(_, value)| { - // Only zero values are worth checking for initial writes; non-zero values are always - // written per deduplication rules. - value.is_zero() - }) - .map(|(key, _)| key.hashed_key()) - .collect(); - METRICS - .load_changes_zero_values - .observe(hashed_keys_for_zero_values.len()); - - let latency = METRICS.start_load_stage(LoadChangesStage::LoadInitialWritesForZeroValues); - let l1_batches_for_initial_writes = storage - .storage_logs_dal() - .get_l1_batches_and_indices_for_initial_writes(&hashed_keys_for_zero_values) - .await; - latency.observe_with_count(hashed_keys_for_zero_values.len()); - for (storage_key, value) in touched_slots { - let write_matters = if value.is_zero() { - let initial_write_batch_for_key = - l1_batches_for_initial_writes.get(&storage_key.hashed_key()); - initial_write_batch_for_key.map_or(false, |&(number, _)| number <= l1_batch_number) - } else { - true - }; - - if write_matters { - storage_logs.insert(storage_key, StorageLog::new_write_log(storage_key, value)); + if let Some(&(initial_write_batch_for_key, leaf_index)) = + l1_batches_for_initial_writes.get(&storage_key.hashed_key()) + { + // Filter out logs that correspond to deduplicated writes. + if initial_write_batch_for_key <= l1_batch_number { + storage_logs.insert( + storage_key, + TreeInstruction::write(storage_key, leaf_index, value), + ); + } } } @@ -347,7 +329,7 @@ mod tests { use tempfile::TempDir; use zksync_dal::ConnectionPool; - use zksync_types::{proofs::PrepareBasicCircuitsJob, L2ChainId, StorageKey, StorageLogKind}; + use zksync_types::{proofs::PrepareBasicCircuitsJob, L2ChainId, StorageKey, StorageLog}; use super::*; use crate::{ @@ -386,6 +368,10 @@ mod tests { .storage_logs_dal() .get_previous_storage_values(&hashed_keys, l1_batch_number) .await; + let l1_batches_for_initial_writes = storage + .storage_logs_dal() + .get_l1_batches_and_indices_for_initial_writes(&hashed_keys) + .await; for storage_key in protective_reads { let previous_value = previous_values[&storage_key.hashed_key()].unwrap_or_default(); @@ -397,16 +383,17 @@ mod tests { ); } - storage_logs.insert( - storage_key, - StorageLog::new_read_log(storage_key, previous_value), - ); + storage_logs.insert(storage_key, TreeInstruction::Read(storage_key)); } for (storage_key, value) in touched_slots { let previous_value = previous_values[&storage_key.hashed_key()].unwrap_or_default(); if previous_value != value { - storage_logs.insert(storage_key, StorageLog::new_write_log(storage_key, value)); + let (_, leaf_index) = l1_batches_for_initial_writes[&storage_key.hashed_key()]; + storage_logs.insert( + storage_key, + TreeInstruction::write(storage_key, leaf_index, value), + ); } } @@ -608,7 +595,7 @@ mod tests { let read_logs_count = l1_batch_with_logs .storage_logs .iter() - .filter(|log| log.kind == StorageLogKind::Read) + .filter(|log| matches!(log, TreeInstruction::Read(_))) .count(); assert_eq!(read_logs_count, 7); diff --git a/core/lib/zksync_core/src/metadata_calculator/metrics.rs b/core/lib/zksync_core/src/metadata_calculator/metrics.rs index f2bedf47229d..f8ef8f85b641 100644 --- a/core/lib/zksync_core/src/metadata_calculator/metrics.rs +++ b/core/lib/zksync_core/src/metadata_calculator/metrics.rs @@ -35,7 +35,7 @@ pub(super) enum LoadChangesStage { LoadL1BatchHeader, LoadProtectiveReads, LoadTouchedSlots, - LoadInitialWritesForZeroValues, + LoadLeafIndices, } /// Latency metric for a certain stage of the tree update. From 15d7eaf872e222338810243865cec9dff7f6e799 Mon Sep 17 00:00:00 2001 From: Alex Ostrovski Date: Thu, 30 Nov 2023 17:17:05 +0200 Subject: [PATCH 022/268] feat(en): Support arbitrary genesis block for external nodes (#537) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Support non-zero genesis block specified in executor configuration. Check whether this block exists on initialization; validate its correspondence if it does, and persist consensus fields if it doesn't. ## Why ❔ This is necessary to support gossip-based syncing in practice; we likely won't back-sign all blocks in all envs. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --- core/lib/types/src/block.rs | 10 +- core/lib/zksync_core/src/consensus/payload.rs | 2 +- .../zksync_core/src/sync_layer/external_io.rs | 31 ++- .../lib/zksync_core/src/sync_layer/fetcher.rs | 16 +- .../src/sync_layer/gossip/buffered/tests.rs | 2 + .../src/sync_layer/gossip/conversions.rs | 1 - .../zksync_core/src/sync_layer/gossip/mod.rs | 4 +- .../src/sync_layer/gossip/storage/mod.rs | 165 ++++++++++-- .../src/sync_layer/gossip/storage/tests.rs | 224 +++++++++++++++- .../src/sync_layer/gossip/tests.rs | 249 +++++++++++++++--- .../zksync_core/src/sync_layer/sync_action.rs | 4 +- core/lib/zksync_core/src/sync_layer/tests.rs | 13 +- 12 files changed, 611 insertions(+), 110 deletions(-) diff --git a/core/lib/types/src/block.rs b/core/lib/types/src/block.rs index 80a4d131e21b..b40264688684 100644 --- a/core/lib/types/src/block.rs +++ b/core/lib/types/src/block.rs @@ -89,7 +89,7 @@ pub struct MiniblockHeader { } /// Consensus-related L2 block (= miniblock) fields. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct ConsensusBlockFields { /// Hash of the previous consensus block. pub parent: validator::BlockHeaderHash, @@ -99,12 +99,14 @@ pub struct ConsensusBlockFields { impl ProtoFmt for ConsensusBlockFields { type Proto = crate::proto::ConsensusBlockFields; - fn read(r: &Self::Proto) -> anyhow::Result { + + fn read(proto: &Self::Proto) -> anyhow::Result { Ok(Self { - parent: read_required(&r.parent).context("parent")?, - justification: read_required(&r.justification).context("justification")?, + parent: read_required(&proto.parent).context("parent")?, + justification: read_required(&proto.justification).context("justification")?, }) } + fn build(&self) -> Self::Proto { Self::Proto { parent: Some(self.parent.build()), diff --git a/core/lib/zksync_core/src/consensus/payload.rs b/core/lib/zksync_core/src/consensus/payload.rs index 8d53fdf21f31..dbe276196b0f 100644 --- a/core/lib/zksync_core/src/consensus/payload.rs +++ b/core/lib/zksync_core/src/consensus/payload.rs @@ -6,7 +6,7 @@ use zksync_types::api::en::SyncBlock; use zksync_types::{Address, L1BatchNumber, Transaction, H256}; /// L2 block (= miniblock) payload. -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub(crate) struct Payload { pub hash: H256, pub l1_batch_number: L1BatchNumber, diff --git a/core/lib/zksync_core/src/sync_layer/external_io.rs b/core/lib/zksync_core/src/sync_layer/external_io.rs index dcc38334a998..8e3ca863072a 100644 --- a/core/lib/zksync_core/src/sync_layer/external_io.rs +++ b/core/lib/zksync_core/src/sync_layer/external_io.rs @@ -1,4 +1,5 @@ use async_trait::async_trait; +use futures::future; use std::{ collections::HashMap, @@ -108,7 +109,6 @@ impl ExternalIO { async fn load_previous_l1_batch_hash(&self) -> U256 { let mut storage = self.pool.access_storage_tagged("sync_layer").await.unwrap(); - let wait_latency = KEEPER_METRICS.wait_for_prev_hash_time.start(); let (hash, _) = extractors::wait_for_prev_l1_batch_params(&mut storage, self.current_l1_batch_number) @@ -117,6 +117,18 @@ impl ExternalIO { hash } + async fn load_previous_miniblock_hash(&self) -> H256 { + let prev_miniblock_number = self.current_miniblock_number - 1; + let mut storage = self.pool.access_storage_tagged("sync_layer").await.unwrap(); + let header = storage + .blocks_dal() + .get_miniblock_header(prev_miniblock_number) + .await + .unwrap() + .unwrap_or_else(|| panic!("Miniblock #{prev_miniblock_number} is missing")); + header.hash + } + async fn load_base_system_contracts_by_version_id( &self, id: ProtocolVersionId, @@ -307,15 +319,20 @@ impl StateKeeperIO for ExternalIO { operator_address, protocol_version, first_miniblock_info: (miniblock_number, virtual_blocks), - prev_miniblock_hash, }) => { assert_eq!( number, self.current_l1_batch_number, "Batch number mismatch" ); - tracing::info!("Getting previous L1 batch hash"); - let previous_l1_batch_hash = self.load_previous_l1_batch_hash().await; - tracing::info!("Previous L1 batch hash: {previous_l1_batch_hash}"); + tracing::info!("Getting previous L1 batch hash and miniblock hash"); + let (previous_l1_batch_hash, previous_miniblock_hash) = future::join( + self.load_previous_l1_batch_hash(), + self.load_previous_miniblock_hash(), + ) + .await; + tracing::info!( + "Previous L1 batch hash: {previous_l1_batch_hash}, previous miniblock hash: {previous_miniblock_hash}" + ); let base_system_contracts = self .load_base_system_contracts_by_version_id(protocol_version) @@ -328,7 +345,7 @@ impl StateKeeperIO for ExternalIO { l1_gas_price, l2_fair_gas_price, miniblock_number, - prev_miniblock_hash, + previous_miniblock_hash, base_system_contracts, self.validation_computational_gas_limit, protocol_version, @@ -539,6 +556,8 @@ impl StateKeeperIO for ExternalIO { // Mimic the metric emitted by the main node to reuse existing Grafana charts. APP_METRICS.block_number[&BlockStage::Sealed].set(self.current_l1_batch_number.0.into()); + self.sync_state + .set_local_block(self.current_miniblock_number); self.current_miniblock_number += 1; // Due to fictive miniblock being sealed. self.current_l1_batch_number += 1; Ok(()) diff --git a/core/lib/zksync_core/src/sync_layer/fetcher.rs b/core/lib/zksync_core/src/sync_layer/fetcher.rs index 4aabd163f21e..9cdd7e64fd1e 100644 --- a/core/lib/zksync_core/src/sync_layer/fetcher.rs +++ b/core/lib/zksync_core/src/sync_layer/fetcher.rs @@ -6,7 +6,7 @@ use std::time::Duration; use zksync_dal::StorageProcessor; use zksync_types::{ api::en::SyncBlock, block::ConsensusBlockFields, Address, L1BatchNumber, MiniblockNumber, - ProtocolVersionId, H256, + ProtocolVersionId, }; use zksync_web3_decl::jsonrpsee::core::Error as RpcError; @@ -29,7 +29,6 @@ pub(super) struct FetchedBlock { pub last_in_batch: bool, pub protocol_version: ProtocolVersionId, pub timestamp: u64, - pub hash: H256, pub l1_gas_price: u64, pub l2_fair_gas_price: u64, pub virtual_blocks: u32, @@ -38,15 +37,14 @@ pub(super) struct FetchedBlock { pub consensus: Option, } -impl FetchedBlock { - fn from_sync_block(block: SyncBlock) -> Self { +impl From for FetchedBlock { + fn from(block: SyncBlock) -> Self { Self { number: block.number, l1_batch_number: block.l1_batch_number, last_in_batch: block.last_in_batch, protocol_version: block.protocol_version, timestamp: block.timestamp, - hash: block.hash.unwrap_or_default(), l1_gas_price: block.l1_gas_price, l2_fair_gas_price: block.l2_fair_gas_price, virtual_blocks: block.virtual_blocks.unwrap_or(0), @@ -64,7 +62,6 @@ impl FetchedBlock { pub struct FetcherCursor { // Fields are public for testing purposes. pub(super) next_miniblock: MiniblockNumber, - pub(super) prev_miniblock_hash: H256, pub(super) l1_batch: L1BatchNumber, } @@ -93,7 +90,6 @@ impl FetcherCursor { // Miniblocks are always fully processed. let next_miniblock = last_miniblock_header.number + 1; - let prev_miniblock_hash = last_miniblock_header.hash; // Decide whether the next batch should be explicitly opened or not. let l1_batch = if was_new_batch_open { // No `OpenBatch` action needed. @@ -106,7 +102,6 @@ impl FetcherCursor { Ok(Self { next_miniblock, l1_batch, - prev_miniblock_hash, }) } @@ -136,7 +131,6 @@ impl FetcherCursor { protocol_version: block.protocol_version, // `block.virtual_blocks` can be `None` only for old VM versions where it's not used, so it's fine to provide any number. first_miniblock_info: (block.number, block.virtual_blocks), - prev_miniblock_hash: self.prev_miniblock_hash, }); FETCHER_METRICS.l1_batch[&L1BatchStage::Open].set(block.l1_batch_number.0.into()); self.l1_batch += 1; @@ -168,7 +162,6 @@ impl FetcherCursor { new_actions.push(SyncAction::SealMiniblock(block.consensus)); } self.next_miniblock += 1; - self.prev_miniblock_hash = block.hash; new_actions } @@ -280,8 +273,7 @@ impl MainNodeFetcher { request_latency.observe(); let block_number = block.number; - let fetched_block = FetchedBlock::from_sync_block(block); - let new_actions = self.cursor.advance(fetched_block); + let new_actions = self.cursor.advance(block.into()); tracing::info!( "New miniblock: {block_number} / {}", diff --git a/core/lib/zksync_core/src/sync_layer/gossip/buffered/tests.rs b/core/lib/zksync_core/src/sync_layer/gossip/buffered/tests.rs index de5ef8a88cb0..62c81bca7ca6 100644 --- a/core/lib/zksync_core/src/sync_layer/gossip/buffered/tests.rs +++ b/core/lib/zksync_core/src/sync_layer/gossip/buffered/tests.rs @@ -11,6 +11,7 @@ use zksync_concurrency::{ ctx::{self, channel}, scope, sync::{self, watch}, + testonly::abort_on_panic, time, }; use zksync_consensus_roles::validator::{BlockHeader, BlockNumber, FinalBlock, Payload}; @@ -131,6 +132,7 @@ async fn test_buffered_storage( block_interval: time::Duration, shuffle_blocks: impl FnOnce(&mut StdRng, &mut [FinalBlock]), ) { + abort_on_panic(); let ctx = &ctx::test_root(&ctx::RealClock); let rng = &mut ctx.rng(); diff --git a/core/lib/zksync_core/src/sync_layer/gossip/conversions.rs b/core/lib/zksync_core/src/sync_layer/gossip/conversions.rs index 8face4e69426..410c2bfe2046 100644 --- a/core/lib/zksync_core/src/sync_layer/gossip/conversions.rs +++ b/core/lib/zksync_core/src/sync_layer/gossip/conversions.rs @@ -42,7 +42,6 @@ impl FetchedBlock { last_in_batch, protocol_version: ProtocolVersionId::latest(), // FIXME timestamp: payload.timestamp, - hash: payload.hash, l1_gas_price: payload.l1_gas_price, l2_fair_gas_price: payload.l2_fair_gas_price, virtual_blocks: payload.virtual_blocks, diff --git a/core/lib/zksync_core/src/sync_layer/gossip/mod.rs b/core/lib/zksync_core/src/sync_layer/gossip/mod.rs index 630ded953453..2fd9f46aabb5 100644 --- a/core/lib/zksync_core/src/sync_layer/gossip/mod.rs +++ b/core/lib/zksync_core/src/sync_layer/gossip/mod.rs @@ -67,7 +67,9 @@ async fn run_gossip_fetcher_inner( let cursor = FetcherCursor::new(&mut storage).await?; drop(storage); - let store = PostgresBlockStorage::new(pool, actions, cursor); + let store = + PostgresBlockStorage::new(ctx, pool, actions, cursor, &executor_config.genesis_block) + .await?; let buffered = Arc::new(Buffered::new(store)); let store = buffered.inner(); let executor = Executor::new(executor_config, node_key, buffered.clone()) diff --git a/core/lib/zksync_core/src/sync_layer/gossip/storage/mod.rs b/core/lib/zksync_core/src/sync_layer/gossip/storage/mod.rs index d4e95c9e2d46..a490147512e4 100644 --- a/core/lib/zksync_core/src/sync_layer/gossip/storage/mod.rs +++ b/core/lib/zksync_core/src/sync_layer/gossip/storage/mod.rs @@ -13,15 +13,18 @@ use zksync_concurrency::{ use zksync_consensus_roles::validator::{BlockNumber, FinalBlock}; use zksync_consensus_storage::{BlockStore, StorageError, StorageResult}; use zksync_dal::{ConnectionPool, StorageProcessor}; -use zksync_types::{Address, MiniblockNumber}; +use zksync_types::{api::en::SyncBlock, block::ConsensusBlockFields, Address, MiniblockNumber}; #[cfg(test)] mod tests; use super::{buffered::ContiguousBlockStore, conversions::sync_block_to_consensus_block}; -use crate::sync_layer::{ - fetcher::{FetchedBlock, FetcherCursor}, - sync_action::{ActionQueueSender, SyncAction}, +use crate::{ + consensus, + sync_layer::{ + fetcher::{FetchedBlock, FetcherCursor}, + sync_action::{ActionQueueSender, SyncAction}, + }, }; #[derive(Debug)] @@ -67,6 +70,7 @@ impl CursorWithCachedBlock { #[derive(Debug)] pub(super) struct PostgresBlockStorage { pool: ConnectionPool, + first_block_number: MiniblockNumber, actions: ActionQueueSender, block_sender: watch::Sender, cursor: Mutex, @@ -74,16 +78,109 @@ pub(super) struct PostgresBlockStorage { impl PostgresBlockStorage { /// Creates a new storage handle. `pool` should have multiple connections to work efficiently. - pub fn new(pool: ConnectionPool, actions: ActionQueueSender, cursor: FetcherCursor) -> Self { + pub async fn new( + ctx: &ctx::Ctx, + pool: ConnectionPool, + actions: ActionQueueSender, + cursor: FetcherCursor, + genesis_block: &FinalBlock, + ) -> StorageResult { + let mut storage = ctx + .wait(pool.access_storage_tagged("sync_layer")) + .await? + .map_err(StorageError::Database)?; + Self::ensure_genesis_block(ctx, &mut storage, genesis_block).await?; + drop(storage); + + let first_block_number = u32::try_from(genesis_block.header.number.0) + .context("Block number overflow for genesis block") + .map_err(StorageError::Database)?; + let first_block_number = MiniblockNumber(first_block_number); + + Ok(Self::new_unchecked( + pool, + first_block_number, + actions, + cursor, + )) + } + + fn new_unchecked( + pool: ConnectionPool, + first_block_number: MiniblockNumber, + actions: ActionQueueSender, + cursor: FetcherCursor, + ) -> Self { let current_block_number = cursor.next_miniblock.0.saturating_sub(1).into(); Self { pool, + first_block_number, actions, block_sender: watch::channel(BlockNumber(current_block_number)).0, cursor: Mutex::new(cursor.into()), } } + async fn ensure_genesis_block( + ctx: &ctx::Ctx, + storage: &mut StorageProcessor<'_>, + genesis_block: &FinalBlock, + ) -> StorageResult<()> { + let block_number = u32::try_from(genesis_block.header.number.0) + .context("Block number overflow for genesis block") + .map_err(StorageError::Database)?; + let block = Self::sync_block(ctx, storage, MiniblockNumber(block_number)).await?; + let block = block + .with_context(|| { + format!("Genesis block #{block_number} (first block with consensus data) is not present in Postgres") + }) + .map_err(StorageError::Database)?; + let actual_consensus_fields = block.consensus.clone(); + + // Some of the following checks are duplicated in `Executor` initialization, but it's necessary + // to run them if the genesis consensus block is not present locally. + let expected_payload = consensus::Payload::decode(&genesis_block.payload) + .context("Cannot decode genesis block payload") + .map_err(StorageError::Database)?; + let actual_payload: consensus::Payload = + block.try_into().map_err(StorageError::Database)?; + if actual_payload != expected_payload { + let err = anyhow::anyhow!( + "Genesis block payload from Postgres {actual_payload:?} does not match the configured one \ + {expected_payload:?}" + ); + return Err(StorageError::Database(err)); + } + + let expected_consensus_fields = ConsensusBlockFields { + parent: genesis_block.header.parent, + justification: genesis_block.justification.clone(), + }; + if let Some(actual_consensus_fields) = &actual_consensus_fields { + // While justifications may differ among nodes for an arbitrary block, we assume that + // the genesis block has a hardcoded justification. + if *actual_consensus_fields != expected_consensus_fields { + let err = anyhow::anyhow!( + "Genesis block consensus fields in Postgres {actual_consensus_fields:?} do not match \ + the configured ones {expected_consensus_fields:?}" + ); + return Err(StorageError::Database(err)); + } + } else { + tracing::info!( + "Postgres doesn't have consensus fields for genesis block; saving {expected_consensus_fields:?}" + ); + ctx.wait(storage.blocks_dal().set_miniblock_consensus_fields( + MiniblockNumber(block_number), + &expected_consensus_fields, + )) + .await? + .context("Failed saving consensus fields for genesis block") + .map_err(StorageError::Database)?; + } + Ok(()) + } + /// Runs background tasks for this store. This method **must** be spawned as a background task /// which should be running as long at the [`PostgresBlockStorage`] is in use; otherwise, /// it will function incorrectly. @@ -116,22 +213,28 @@ impl PostgresBlockStorage { .map_err(StorageError::Database) } + async fn sync_block( + ctx: &ctx::Ctx, + storage: &mut StorageProcessor<'_>, + number: MiniblockNumber, + ) -> StorageResult> { + let operator_address = Address::default(); // FIXME: where to get this address from? + ctx.wait( + storage + .sync_dal() + .sync_block(number, operator_address, true), + ) + .await? + .with_context(|| format!("Failed getting miniblock #{number} from Postgres")) + .map_err(StorageError::Database) + } + async fn block( ctx: &ctx::Ctx, storage: &mut StorageProcessor<'_>, number: MiniblockNumber, ) -> StorageResult> { - let operator_address = Address::default(); // FIXME: where to get this address from? - let Some(block) = ctx - .wait( - storage - .sync_dal() - .sync_block(number, operator_address, true), - ) - .await? - .with_context(|| format!("Failed getting miniblock #{number} from Postgres")) - .map_err(StorageError::Database)? - else { + let Some(block) = Self::sync_block(ctx, storage, number).await? else { return Ok(None); }; let block = sync_block_to_consensus_block(block).map_err(StorageError::Database)?; @@ -167,7 +270,7 @@ impl BlockStore for PostgresBlockStorage { async fn first_block(&self, ctx: &ctx::Ctx) -> StorageResult { let mut storage = self.storage(ctx).await?; - Self::block(ctx, &mut storage, MiniblockNumber(0)) + Self::block(ctx, &mut storage, self.first_block_number) .await? .context("Genesis miniblock not present in Postgres") .map_err(StorageError::Database) @@ -182,19 +285,33 @@ impl BlockStore for PostgresBlockStorage { ctx: &ctx::Ctx, number: BlockNumber, ) -> StorageResult> { - let number = u32::try_from(number.0) - .context("block number is too large") - .map_err(StorageError::Database)?; + let Ok(number) = u32::try_from(number.0) else { + return Ok(None); + }; + let number = MiniblockNumber(number); + if number < self.first_block_number { + return Ok(None); + } let mut storage = self.storage(ctx).await?; - Self::block(ctx, &mut storage, MiniblockNumber(number)).await + Self::block(ctx, &mut storage, number).await } async fn missing_block_numbers( &self, - _ctx: &ctx::Ctx, - _range: ops::Range, + ctx: &ctx::Ctx, + range: ops::Range, ) -> StorageResult> { - Ok(vec![]) // The storage never has missing blocks by construction + let mut output = vec![]; + let first_block_number = u64::from(self.first_block_number.0); + let numbers_before_first_block = (range.start.0..first_block_number).map(BlockNumber); + output.extend(numbers_before_first_block); + + let last_block_number = self.sealed_miniblock_number(ctx).await?; + let numbers_after_last_block = (last_block_number.next().0..range.end.0).map(BlockNumber); + output.extend(numbers_after_last_block); + + // By design, no blocks are missing in the `first_block_number..=last_block_number` range. + Ok(output) } fn subscribe_to_block_writes(&self) -> watch::Receiver { diff --git a/core/lib/zksync_core/src/sync_layer/gossip/storage/tests.rs b/core/lib/zksync_core/src/sync_layer/gossip/storage/tests.rs index 437c51883308..cfd14f784112 100644 --- a/core/lib/zksync_core/src/sync_layer/gossip/storage/tests.rs +++ b/core/lib/zksync_core/src/sync_layer/gossip/storage/tests.rs @@ -2,7 +2,8 @@ use rand::{thread_rng, Rng}; -use zksync_concurrency::scope; +use zksync_concurrency::{scope, testonly::abort_on_panic}; +use zksync_consensus_roles::validator; use zksync_types::L2ChainId; use super::*; @@ -11,7 +12,7 @@ use crate::{ sync_layer::{ gossip::tests::{ add_consensus_fields, assert_first_block_actions, assert_second_block_actions, - load_final_block, + block_payload, create_genesis_block, load_final_block, }, tests::run_state_keeper_with_multiple_miniblocks, ActionQueue, @@ -22,15 +23,21 @@ const TEST_TIMEOUT: time::Duration = time::Duration::seconds(10); #[tokio::test] async fn block_store_basics_for_postgres() { + abort_on_panic(); let pool = ConnectionPool::test_pool().await; run_state_keeper_with_multiple_miniblocks(pool.clone()).await; let mut storage = pool.access_storage().await.unwrap(); - add_consensus_fields(&mut storage, &thread_rng().gen(), 3).await; + add_consensus_fields(&mut storage, &thread_rng().gen(), 0..3).await; let cursor = FetcherCursor::new(&mut storage).await.unwrap(); drop(storage); let (actions_sender, _) = ActionQueue::new(); - let storage = PostgresBlockStorage::new(pool.clone(), actions_sender, cursor); + let storage = PostgresBlockStorage::new_unchecked( + pool.clone(), + MiniblockNumber(0), + actions_sender, + cursor, + ); let ctx = &ctx::test_root(&ctx::RealClock); let genesis_block = BlockStore::first_block(&storage, ctx).await.unwrap(); @@ -52,6 +59,7 @@ async fn block_store_basics_for_postgres() { #[tokio::test] async fn subscribing_to_block_updates_for_postgres() { + abort_on_panic(); let pool = ConnectionPool::test_pool().await; let mut storage = pool.access_storage().await.unwrap(); if storage.blocks_dal().is_genesis_needed().await.unwrap() { @@ -64,7 +72,12 @@ async fn subscribing_to_block_updates_for_postgres() { // `ContiguousBlockStore`), but for testing subscriptions this is fine. drop(storage); let (actions_sender, _) = ActionQueue::new(); - let storage = PostgresBlockStorage::new(pool.clone(), actions_sender, cursor); + let storage = PostgresBlockStorage::new_unchecked( + pool.clone(), + MiniblockNumber(0), + actions_sender, + cursor, + ); let mut subscriber = storage.subscribe_to_block_writes(); let ctx = &ctx::test_root(&ctx::RealClock); @@ -90,11 +103,12 @@ async fn subscribing_to_block_updates_for_postgres() { #[tokio::test] async fn processing_new_blocks() { + abort_on_panic(); let pool = ConnectionPool::test_pool().await; run_state_keeper_with_multiple_miniblocks(pool.clone()).await; let mut storage = pool.access_storage().await.unwrap(); - add_consensus_fields(&mut storage, &thread_rng().gen(), 3).await; + add_consensus_fields(&mut storage, &thread_rng().gen(), 0..3).await; let first_block = load_final_block(&mut storage, 1).await; let second_block = load_final_block(&mut storage, 2).await; storage @@ -110,7 +124,12 @@ async fn processing_new_blocks() { drop(storage); let (actions_sender, mut actions) = ActionQueue::new(); - let storage = PostgresBlockStorage::new(pool.clone(), actions_sender, cursor); + let storage = PostgresBlockStorage::new_unchecked( + pool.clone(), + MiniblockNumber(0), + actions_sender, + cursor, + ); let ctx = &ctx::test_root(&ctx::RealClock); let ctx = &ctx.with_timeout(TEST_TIMEOUT); storage @@ -125,3 +144,194 @@ async fn processing_new_blocks() { .unwrap(); assert_second_block_actions(&mut actions).await; } + +#[tokio::test] +async fn ensuring_consensus_fields_for_genesis_block() { + abort_on_panic(); + let ctx = &ctx::test_root(&ctx::RealClock); + let pool = ConnectionPool::test_pool().await; + let mut storage = pool.access_storage().await.unwrap(); + if storage.blocks_dal().is_genesis_needed().await.unwrap() { + ensure_genesis_state(&mut storage, L2ChainId::default(), &GenesisParams::mock()) + .await + .unwrap(); + } + let cursor = FetcherCursor::new(&mut storage).await.unwrap(); + let block_payload = block_payload(&mut storage, 0).await.encode(); + drop(storage); + + let validator_key = validator::SecretKey::generate(&mut ctx.rng()); + let genesis_block = create_genesis_block(&validator_key, 0, block_payload.clone()); + + let (actions_sender, _) = ActionQueue::new(); + PostgresBlockStorage::new(ctx, pool.clone(), actions_sender, cursor, &genesis_block) + .await + .unwrap(); + + // Check that the consensus fields are persisted for the genesis block. + let mut storage = pool.access_storage().await.unwrap(); + let sync_block = storage + .sync_dal() + .sync_block(MiniblockNumber(0), Address::default(), false) + .await + .unwrap() + .expect("No genesis block"); + assert!(sync_block.consensus.is_some()); + let cursor = FetcherCursor::new(&mut storage).await.unwrap(); + let other_cursor = FetcherCursor::new(&mut storage).await.unwrap(); + drop(storage); + + // Check that the storage can be initialized again. + let (actions_sender, _) = ActionQueue::new(); + PostgresBlockStorage::new(ctx, pool.clone(), actions_sender, cursor, &genesis_block) + .await + .unwrap(); + + // Create a genesis block with another validator. + let validator_key = validator::SecretKey::generate(&mut ctx.rng()); + let other_genesis_block = create_genesis_block(&validator_key, 0, block_payload); + + // Storage should not be able to initialize with other genesis block. + let (actions_sender, _) = ActionQueue::new(); + PostgresBlockStorage::new( + ctx, + pool, + actions_sender, + other_cursor, + &other_genesis_block, + ) + .await + .unwrap_err(); +} + +#[tokio::test] +async fn genesis_block_payload_mismatch() { + abort_on_panic(); + let ctx = &ctx::test_root(&ctx::RealClock); + let pool = ConnectionPool::test_pool().await; + let mut storage = pool.access_storage().await.unwrap(); + if storage.blocks_dal().is_genesis_needed().await.unwrap() { + ensure_genesis_state(&mut storage, L2ChainId::default(), &GenesisParams::mock()) + .await + .unwrap(); + } + let cursor = FetcherCursor::new(&mut storage).await.unwrap(); + let other_cursor = FetcherCursor::new(&mut storage).await.unwrap(); + + let bogus_block_payload = validator::Payload(vec![]); + let validator_key = validator::SecretKey::generate(&mut ctx.rng()); + let genesis_block = create_genesis_block(&validator_key, 0, bogus_block_payload); + + let (actions_sender, _) = ActionQueue::new(); + PostgresBlockStorage::new(ctx, pool.clone(), actions_sender, cursor, &genesis_block) + .await + .unwrap_err(); + + let mut bogus_block_payload = block_payload(&mut storage, 0).await; + bogus_block_payload.timestamp += 1; + let genesis_block = create_genesis_block(&validator_key, 0, bogus_block_payload.encode()); + + let (actions_sender, _) = ActionQueue::new(); + PostgresBlockStorage::new( + ctx, + pool.clone(), + actions_sender, + other_cursor, + &genesis_block, + ) + .await + .unwrap_err(); +} + +#[tokio::test] +async fn missing_genesis_block() { + abort_on_panic(); + let ctx = &ctx::test_root(&ctx::RealClock); + let pool = ConnectionPool::test_pool().await; + let mut storage = pool.access_storage().await.unwrap(); + if storage.blocks_dal().is_genesis_needed().await.unwrap() { + ensure_genesis_state(&mut storage, L2ChainId::default(), &GenesisParams::mock()) + .await + .unwrap(); + } + let cursor = FetcherCursor::new(&mut storage).await.unwrap(); + let block_payload = block_payload(&mut storage, 0).await.encode(); + drop(storage); + + // Create a genesis block for the (non-existing) block #2. + let validator_key = validator::SecretKey::generate(&mut ctx.rng()); + let genesis_block = create_genesis_block(&validator_key, 2, block_payload.clone()); + + let (actions_sender, _) = ActionQueue::new(); + PostgresBlockStorage::new(ctx, pool, actions_sender, cursor, &genesis_block) + .await + .unwrap_err(); +} + +#[tokio::test] +async fn using_non_zero_genesis_block() { + abort_on_panic(); + let ctx = &ctx::test_root(&ctx::RealClock); + let pool = ConnectionPool::test_pool().await; + run_state_keeper_with_multiple_miniblocks(pool.clone()).await; + + let mut storage = pool.access_storage().await.unwrap(); + let cursor = FetcherCursor::new(&mut storage).await.unwrap(); + let block_payload = block_payload(&mut storage, 2).await.encode(); + drop(storage); + + let validator_key = validator::SecretKey::generate(&mut ctx.rng()); + let genesis_block = create_genesis_block(&validator_key, 2, block_payload.clone()); + + let (actions_sender, _) = ActionQueue::new(); + let store = PostgresBlockStorage::new(ctx, pool, actions_sender, cursor, &genesis_block) + .await + .unwrap(); + + let head_block = store.head_block(ctx).await.unwrap(); + assert_eq!(head_block.header.number, BlockNumber(2)); + assert_eq!( + head_block.header.parent, + validator::BlockHeaderHash::from_bytes([0; 32]) + ); + let first_block = store.first_block(ctx).await.unwrap(); + assert_eq!(first_block.header.number, BlockNumber(2)); + let last_contiguous_block_number = store.last_contiguous_block_number(ctx).await.unwrap(); + assert_eq!(last_contiguous_block_number, BlockNumber(2)); + + let block = store.block(ctx, BlockNumber(2)).await.unwrap(); + assert_eq!(block, Some(head_block)); + for number in [0, 1, 3] { + let missing_block = store.block(ctx, BlockNumber(number)).await.unwrap(); + assert!(missing_block.is_none()); + } + + let missing_blocks = store + .missing_block_numbers(ctx, BlockNumber(0)..BlockNumber(5)) + .await + .unwrap(); + assert_eq!( + missing_blocks, + [ + BlockNumber(0), + BlockNumber(1), + BlockNumber(3), + BlockNumber(4) + ] + ); + let missing_blocks = store + .missing_block_numbers(ctx, BlockNumber(0)..BlockNumber(2)) + .await + .unwrap(); + assert_eq!(missing_blocks, [BlockNumber(0), BlockNumber(1)]); + let missing_blocks = store + .missing_block_numbers(ctx, BlockNumber(2)..BlockNumber(5)) + .await + .unwrap(); + assert_eq!(missing_blocks, [BlockNumber(3), BlockNumber(4)]); + let missing_blocks = store + .missing_block_numbers(ctx, BlockNumber(2)..BlockNumber(3)) + .await + .unwrap(); + assert_eq!(missing_blocks, []); +} diff --git a/core/lib/zksync_core/src/sync_layer/gossip/tests.rs b/core/lib/zksync_core/src/sync_layer/gossip/tests.rs index ca3ce29f4d37..30597189f0b3 100644 --- a/core/lib/zksync_core/src/sync_layer/gossip/tests.rs +++ b/core/lib/zksync_core/src/sync_layer/gossip/tests.rs @@ -3,12 +3,16 @@ use assert_matches::assert_matches; use test_casing::{test_casing, Product}; -use zksync_concurrency::{ctx, scope, time}; +use std::ops; + +use zksync_concurrency::{ctx, scope, testonly::abort_on_panic, time}; use zksync_consensus_executor::testonly::FullValidatorConfig; use zksync_consensus_roles::validator::{self, FinalBlock}; use zksync_consensus_storage::{InMemoryStorage, WriteBlockStore}; use zksync_dal::{ConnectionPool, StorageProcessor}; -use zksync_types::{block::ConsensusBlockFields, Address, L1BatchNumber, MiniblockNumber}; +use zksync_types::{ + api::en::SyncBlock, block::ConsensusBlockFields, Address, L1BatchNumber, MiniblockNumber, H256, +}; use super::*; use crate::{ @@ -26,40 +30,49 @@ use crate::{ const CLOCK_SPEEDUP: i64 = 20; const POLL_INTERVAL: time::Duration = time::Duration::milliseconds(50 * CLOCK_SPEEDUP); +async fn load_sync_block(storage: &mut StorageProcessor<'_>, number: u32) -> SyncBlock { + storage + .sync_dal() + .sync_block(MiniblockNumber(number), Address::default(), true) + .await + .unwrap() + .unwrap_or_else(|| panic!("no sync block #{number}")) +} + /// Loads a block from the storage and converts it to a `FinalBlock`. pub(super) async fn load_final_block( storage: &mut StorageProcessor<'_>, number: u32, ) -> FinalBlock { - let sync_block = storage - .sync_dal() - .sync_block(MiniblockNumber(number), Address::repeat_byte(1), true) - .await - .unwrap() - .unwrap_or_else(|| panic!("no sync block #{number}")); + let sync_block = load_sync_block(storage, number).await; conversions::sync_block_to_consensus_block(sync_block).unwrap() } -pub async fn block_payload(storage: &mut StorageProcessor<'_>, number: u32) -> validator::Payload { - let sync_block = storage - .sync_dal() - .sync_block(MiniblockNumber(number), Address::repeat_byte(1), true) - .await - .unwrap() - .unwrap_or_else(|| panic!("no sync block #{number}")); - consensus::Payload::try_from(sync_block).unwrap().encode() +fn convert_sync_blocks(sync_blocks: Vec) -> Vec { + sync_blocks + .into_iter() + .map(|sync_block| conversions::sync_block_to_consensus_block(sync_block).unwrap()) + .collect() +} + +pub(super) async fn block_payload( + storage: &mut StorageProcessor<'_>, + number: u32, +) -> consensus::Payload { + let sync_block = load_sync_block(storage, number).await; + consensus::Payload::try_from(sync_block).unwrap() } /// Adds consensus information for the specified `count` of miniblocks, starting from the genesis. pub(super) async fn add_consensus_fields( storage: &mut StorageProcessor<'_>, validator_key: &validator::SecretKey, - count: u32, + block_numbers: ops::Range, ) { let mut prev_block_hash = validator::BlockHeaderHash::from_bytes([0; 32]); let validator_set = validator::ValidatorSet::new([validator_key.public()]).unwrap(); - for number in 0..count { - let payload = block_payload(storage, number).await; + for number in block_numbers { + let payload = block_payload(storage, number).await.encode(); let block_header = validator::BlockHeader { parent: prev_block_hash, number: validator::BlockNumber(number.into()), @@ -87,6 +100,33 @@ pub(super) async fn add_consensus_fields( } } +/// Creates a genesis block for the consensus with the specified number / payload authored by a single validator. +pub(super) fn create_genesis_block( + validator_key: &validator::SecretKey, + number: u64, + payload: validator::Payload, +) -> FinalBlock { + let block_header = validator::BlockHeader { + parent: validator::BlockHeaderHash::from_bytes([0; 32]), + number: validator::BlockNumber(number), + payload: payload.hash(), + }; + let validator_set = validator::ValidatorSet::new([validator_key.public()]).unwrap(); + let replica_commit = validator::ReplicaCommit { + protocol_version: validator::CURRENT_VERSION, + view: validator::ViewNumber(number), + proposal: block_header, + }; + let replica_commit = validator_key.sign_msg(replica_commit); + let justification = + validator::CommitQC::from(&[replica_commit], &validator_set).expect("Failed creating QC"); + FinalBlock { + header: block_header, + payload, + justification, + } +} + pub(super) async fn assert_first_block_actions(actions: &mut ActionQueue) -> Vec { let mut received_actions = vec![]; while !matches!(received_actions.last(), Some(SyncAction::SealMiniblock(_))) { @@ -137,20 +177,21 @@ pub(super) async fn assert_second_block_actions(actions: &mut ActionQueue) -> Ve #[test_casing(4, Product(([false, true], [false, true])))] #[tokio::test] async fn syncing_via_gossip_fetcher(delay_first_block: bool, delay_second_block: bool) { - zksync_concurrency::testonly::abort_on_panic(); + abort_on_panic(); let pool = ConnectionPool::test_pool().await; let tx_hashes = run_state_keeper_with_multiple_miniblocks(pool.clone()).await; let mut storage = pool.access_storage().await.unwrap(); - let genesis_block_payload = block_payload(&mut storage, 0).await; + let genesis_block_payload = block_payload(&mut storage, 0).await.encode(); let ctx = &ctx::test_root(&ctx::AffineClock::new(CLOCK_SPEEDUP as f64)); let rng = &mut ctx.rng(); let mut validator = FullValidatorConfig::for_single_validator(rng, genesis_block_payload); let validator_set = validator.node_config.validators.clone(); let external_node = validator.connect_full_node(rng); - let (genesis_block, blocks) = - get_blocks_and_reset_storage(storage, &validator.validator_key).await; + let genesis_block = validator.node_config.genesis_block.clone(); + add_consensus_fields(&mut storage, &validator.validator_key, 0..3).await; + let blocks = convert_sync_blocks(reset_storage(storage).await); let [first_block, second_block] = blocks.as_slice() else { unreachable!("Unexpected blocks in storage: {blocks:?}"); }; @@ -228,21 +269,16 @@ async fn syncing_via_gossip_fetcher(delay_first_block: bool, delay_second_block: } } -async fn get_blocks_and_reset_storage( - mut storage: StorageProcessor<'_>, - validator_key: &validator::SecretKey, -) -> (FinalBlock, Vec) { +/// Returns the removed blocks. +async fn reset_storage(mut storage: StorageProcessor<'_>) -> Vec { let sealed_miniblock_number = storage .blocks_dal() .get_sealed_miniblock_number() .await .unwrap(); - add_consensus_fields(&mut storage, validator_key, sealed_miniblock_number.0 + 1).await; - let genesis_block = load_final_block(&mut storage, 0).await; - - let mut blocks = Vec::with_capacity(sealed_miniblock_number.0 as usize); + let mut blocks = vec![]; for number in 1..=sealed_miniblock_number.0 { - blocks.push(load_final_block(&mut storage, number).await); + blocks.push(load_sync_block(&mut storage, number).await); } storage @@ -259,29 +295,30 @@ async fn get_blocks_and_reset_storage( .delete_l1_batches(L1BatchNumber(0)) .await .unwrap(); - (genesis_block, blocks) + blocks } #[test_casing(4, [3, 2, 1, 0])] #[tokio::test] async fn syncing_via_gossip_fetcher_with_multiple_l1_batches(initial_block_count: usize) { assert!(initial_block_count <= 3); - zksync_concurrency::testonly::abort_on_panic(); + abort_on_panic(); let pool = ConnectionPool::test_pool().await; let tx_hashes = run_state_keeper_with_multiple_l1_batches(pool.clone()).await; let tx_hashes: Vec<_> = tx_hashes.iter().map(Vec::as_slice).collect(); let mut storage = pool.access_storage().await.unwrap(); - let genesis_block_payload = block_payload(&mut storage, 0).await; + let genesis_block_payload = block_payload(&mut storage, 0).await.encode(); let ctx = &ctx::test_root(&ctx::AffineClock::new(CLOCK_SPEEDUP as f64)); let rng = &mut ctx.rng(); let mut validator = FullValidatorConfig::for_single_validator(rng, genesis_block_payload); let validator_set = validator.node_config.validators.clone(); let external_node = validator.connect_full_node(rng); - let (genesis_block, blocks) = - get_blocks_and_reset_storage(storage, &validator.validator_key).await; + let genesis_block = validator.node_config.genesis_block.clone(); + add_consensus_fields(&mut storage, &validator.validator_key, 0..4).await; + let blocks = convert_sync_blocks(reset_storage(storage).await); assert_eq!(blocks.len(), 3); // 2 real + 1 fictive blocks tracing::trace!("Node storage reset"); let (initial_blocks, delayed_blocks) = blocks.split_at(initial_block_count); @@ -309,9 +346,8 @@ async fn syncing_via_gossip_fetcher_with_multiple_l1_batches(initial_block_count Ok(()) }); - let cloned_pool = pool.clone(); s.spawn_bg(async { - mock_l1_batch_hash_computation(cloned_pool, 1).await; + mock_l1_batch_hash_computation(pool.clone(), 1).await; Ok(()) }); s.spawn_bg(run_gossip_fetcher_inner( @@ -337,3 +373,138 @@ async fn syncing_via_gossip_fetcher_with_multiple_l1_batches(initial_block_count block.justification.verify(&validator_set, 1).unwrap(); } } + +#[test_casing(2, [1, 2])] +#[tokio::test] +async fn syncing_from_non_zero_block(first_block_number: u32) { + abort_on_panic(); + let pool = ConnectionPool::test_pool().await; + let tx_hashes = run_state_keeper_with_multiple_l1_batches(pool.clone()).await; + let tx_hashes: Vec<_> = tx_hashes.iter().map(Vec::as_slice).collect(); + + let mut storage = pool.access_storage().await.unwrap(); + let genesis_block_payload = block_payload(&mut storage, first_block_number) + .await + .encode(); + let ctx = &ctx::test_root(&ctx::AffineClock::new(CLOCK_SPEEDUP as f64)); + let rng = &mut ctx.rng(); + let mut validator = + FullValidatorConfig::for_single_validator(rng, genesis_block_payload.clone()); + // Override the genesis block since it has an incorrect block number. + let genesis_block = create_genesis_block( + &validator.validator_key, + first_block_number.into(), + genesis_block_payload, + ); + validator.node_config.genesis_block = genesis_block.clone(); + let validator_set = validator.node_config.validators.clone(); + let external_node = validator.connect_full_node(rng); + + add_consensus_fields( + &mut storage, + &validator.validator_key, + first_block_number..4, + ) + .await; + let mut initial_blocks = reset_storage(storage).await; + let delayed_blocks = initial_blocks.split_off(first_block_number as usize); + assert!(!initial_blocks.is_empty()); + assert!(!delayed_blocks.is_empty()); + let delayed_blocks = convert_sync_blocks(delayed_blocks); + + // Re-insert initial blocks to the storage. This allows to more precisely emulate node syncing + // (e.g., missing L1 batch relation for the latest blocks). + insert_sync_blocks(pool.clone(), initial_blocks, &tx_hashes).await; + tracing::trace!("Re-inserted blocks to node storage"); + + let validator_storage = Arc::new(InMemoryStorage::new(genesis_block)); + let validator = Executor::new( + validator.node_config, + validator.node_key, + validator_storage.clone(), + ) + .unwrap(); + + let tx_hashes = if first_block_number >= 2 { + &tx_hashes[1..] // Skip transactions in L1 batch #1, since they won't be executed + } else { + &tx_hashes + }; + let (actions_sender, actions) = ActionQueue::new(); + let state_keeper = StateKeeperHandles::new(pool.clone(), actions, tx_hashes).await; + scope::run!(ctx, |ctx, s| async { + s.spawn_bg(validator.run(ctx)); + s.spawn_bg(async { + for block in &delayed_blocks { + ctx.sleep(POLL_INTERVAL).await?; + validator_storage.put_block(ctx, block).await?; + } + Ok(()) + }); + + if first_block_number < 2 { + // L1 batch #1 will be sealed during the state keeper operation; we need to emulate + // computing metadata for it. + s.spawn_bg(async { + mock_l1_batch_hash_computation(pool.clone(), 1).await; + Ok(()) + }); + } + + s.spawn_bg(run_gossip_fetcher_inner( + ctx, + pool.clone(), + actions_sender, + external_node.node_config, + external_node.node_key, + )); + + state_keeper + .wait(|state| state.get_local_block() == MiniblockNumber(3)) + .await; + Ok(()) + }) + .await + .unwrap(); + + // Check that received blocks have consensus fields persisted. + let mut storage = pool.access_storage().await.unwrap(); + for number in first_block_number..4 { + let block = load_final_block(&mut storage, number).await; + block.justification.verify(&validator_set, 1).unwrap(); + } +} + +async fn insert_sync_blocks(pool: ConnectionPool, blocks: Vec, tx_hashes: &[&[H256]]) { + let expected_block_number = blocks.last().expect("blocks cannot be empty").number; + let sealed_l1_batches = blocks + .iter() + .filter_map(|block| block.last_in_batch.then_some(block.l1_batch_number)); + let sealed_l1_batches: Vec<_> = sealed_l1_batches.collect(); + + let mut fetcher = FetcherCursor::new(&mut pool.access_storage().await.unwrap()) + .await + .unwrap(); + let (actions_sender, actions) = ActionQueue::new(); + let state_keeper = StateKeeperHandles::new(pool.clone(), actions, tx_hashes).await; + for block in blocks { + let block_actions = fetcher.advance(block.into()); + actions_sender.push_actions(block_actions).await; + } + + let hash_tasks: Vec<_> = sealed_l1_batches + .into_iter() + .map(|l1_batch_number| { + tokio::spawn(mock_l1_batch_hash_computation( + pool.clone(), + l1_batch_number.0, + )) + }) + .collect(); + state_keeper + .wait(|state| state.get_local_block() == expected_block_number) + .await; + for hash_task in hash_tasks { + hash_task.await.unwrap(); + } +} diff --git a/core/lib/zksync_core/src/sync_layer/sync_action.rs b/core/lib/zksync_core/src/sync_layer/sync_action.rs index b4f56999d4fd..b278cb1c98e6 100644 --- a/core/lib/zksync_core/src/sync_layer/sync_action.rs +++ b/core/lib/zksync_core/src/sync_layer/sync_action.rs @@ -2,7 +2,7 @@ use tokio::sync::mpsc; use zksync_types::{ block::ConsensusBlockFields, Address, L1BatchNumber, MiniblockNumber, ProtocolVersionId, - Transaction, H256, + Transaction, }; use super::metrics::QUEUE_METRICS; @@ -137,7 +137,6 @@ pub(crate) enum SyncAction { protocol_version: ProtocolVersionId, // Miniblock number and virtual blocks count. first_miniblock_info: (MiniblockNumber, u32), - prev_miniblock_hash: H256, }, Miniblock { number: MiniblockNumber, @@ -180,7 +179,6 @@ mod tests { operator_address: Default::default(), protocol_version: ProtocolVersionId::latest(), first_miniblock_info: (1.into(), 1), - prev_miniblock_hash: H256::default(), } } diff --git a/core/lib/zksync_core/src/sync_layer/tests.rs b/core/lib/zksync_core/src/sync_layer/tests.rs index 4a337bbf5dc3..20bafc51cf67 100644 --- a/core/lib/zksync_core/src/sync_layer/tests.rs +++ b/core/lib/zksync_core/src/sync_layer/tests.rs @@ -137,7 +137,6 @@ fn open_l1_batch(number: u32, timestamp: u64, first_miniblock_number: u32) -> Sy operator_address: Default::default(), protocol_version: ProtocolVersionId::latest(), first_miniblock_info: (MiniblockNumber(first_miniblock_number), 1), - prev_miniblock_hash: H256::default(), } } @@ -404,7 +403,7 @@ pub(super) async fn mock_l1_batch_hash_computation(pool: ConnectionPool, number: let metadata = create_l1_batch_metadata(number); storage .blocks_dal() - .save_l1_batch_metadata(L1BatchNumber(1), &metadata, H256::zero(), false) + .save_l1_batch_metadata(L1BatchNumber(number), &metadata, H256::zero(), false) .await .unwrap(); break; @@ -574,15 +573,6 @@ async fn fetcher_with_real_server() { // Fill in transactions grouped in multiple miniblocks in the storage. let tx_hashes = run_state_keeper_with_multiple_miniblocks(pool.clone()).await; let mut tx_hashes = VecDeque::from(tx_hashes); - let mut connection = pool.access_storage().await.unwrap(); - let genesis_miniblock_hash = connection - .blocks_dal() - .get_miniblock_header(MiniblockNumber(0)) - .await - .unwrap() - .expect("No genesis miniblock") - .hash; - drop(connection); // Start the API server. let network_config = NetworkConfig::for_tests(); @@ -598,7 +588,6 @@ async fn fetcher_with_real_server() { let client = ::json_rpc(&format!("http://{server_addr}/")).unwrap(); let fetcher_cursor = FetcherCursor { next_miniblock: MiniblockNumber(1), - prev_miniblock_hash: genesis_miniblock_hash, l1_batch: L1BatchNumber(0), }; let fetcher = fetcher_cursor.into_fetcher( From e05d955036c76a29f9b6e900872c69e20278e045 Mon Sep 17 00:00:00 2001 From: EmilLuta Date: Fri, 1 Dec 2023 11:10:25 +0100 Subject: [PATCH 023/268] fix(witness_generator): Disable BWIP dependency (#573) This revert is done to facilitate boojum upgrade on mainnet2. Without this, old provers would be halt and boojum upgrade could take longer than anticipated. `waiting_for_artifacts` forced witness to wait on BWIP run. `queued` makes them run instantly. - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [ ] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --------- Signed-off-by: Danil Co-authored-by: Danil --- core/lib/dal/sqlx-data.json | 30 +++++++++++------------ core/lib/dal/src/witness_generator_dal.rs | 2 +- spellcheck/era.dic | 1 + 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/core/lib/dal/sqlx-data.json b/core/lib/dal/sqlx-data.json index 3776b4f84b34..2c958ce0394c 100644 --- a/core/lib/dal/sqlx-data.json +++ b/core/lib/dal/sqlx-data.json @@ -7470,21 +7470,6 @@ }, "query": "SELECT number, timestamp, is_finished, l1_tx_count, l2_tx_count, fee_account_address, bloom, priority_ops_onchain_data, hash, parent_hash, commitment, compressed_write_logs, compressed_contracts, eth_prove_tx_id, eth_commit_tx_id, eth_execute_tx_id, merkle_root_hash, l2_to_l1_logs, l2_to_l1_messages, used_contract_hashes, compressed_initial_writes, compressed_repeated_writes, l2_l1_compressed_messages, l2_l1_merkle_root, l1_gas_price, l2_fair_gas_price, rollup_last_leaf_index, zkporter_is_available, bootloader_code_hash, default_aa_code_hash, base_fee_per_gas, aux_data_hash, pass_through_data_hash, meta_parameters_hash, protocol_version, compressed_state_diffs, system_logs, events_queue_commitment, bootloader_initial_content_commitment FROM l1_batches LEFT JOIN commitments ON commitments.l1_batch_number = l1_batches.number WHERE eth_prove_tx_id IS NOT NULL AND eth_execute_tx_id IS NULL ORDER BY number LIMIT $1" }, - "8ff9d76b4791af1177231661847b6c8879ad625fd11c15de51a16c81d8712129": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Int8", - "Bytea", - "Text", - "Int4" - ] - } - }, - "query": "INSERT INTO witness_inputs(l1_batch_number, merkle_tree_paths, merkel_tree_paths_blob_url, status, protocol_version, created_at, updated_at) VALUES ($1, $2, $3, 'waiting_for_artifacts', $4, now(), now()) ON CONFLICT (l1_batch_number) DO NOTHING" - }, "9051cc1a715e152afdd0c19739c76666b1a9b134e17601ef9fdf3dec5d2fc561": { "describe": { "columns": [ @@ -11182,6 +11167,21 @@ }, "query": "UPDATE l1_batches SET predicted_commit_gas_cost = $2, updated_at = now() WHERE number = $1" }, + "ec35fc5128cf59d19e6d65ed6d84fcc50fedce921405c4ce700dd2e08c990642": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Int8", + "Bytea", + "Text", + "Int4" + ] + } + }, + "query": "INSERT INTO witness_inputs(l1_batch_number, merkle_tree_paths, merkel_tree_paths_blob_url, status, protocol_version, created_at, updated_at) VALUES ($1, $2, $3, 'queued', $4, now(), now()) ON CONFLICT (l1_batch_number) DO NOTHING" + }, "ed50c609371b4588964e29f8757c41973706710090a80eb025ec263ce3d019b4": { "describe": { "columns": [], diff --git a/core/lib/dal/src/witness_generator_dal.rs b/core/lib/dal/src/witness_generator_dal.rs index 983112ab35cc..a8079a9dccee 100644 --- a/core/lib/dal/src/witness_generator_dal.rs +++ b/core/lib/dal/src/witness_generator_dal.rs @@ -728,7 +728,7 @@ impl WitnessGeneratorDal<'_, '_> { { sqlx::query!( "INSERT INTO witness_inputs(l1_batch_number, merkle_tree_paths, merkel_tree_paths_blob_url, status, protocol_version, created_at, updated_at) \ - VALUES ($1, $2, $3, 'waiting_for_artifacts', $4, now(), now()) \ + VALUES ($1, $2, $3, 'queued', $4, now(), now()) \ ON CONFLICT (l1_batch_number) DO NOTHING", block_number.0 as i64, // TODO(SMA-1476): remove the below column once blob is migrated to GCS. diff --git a/spellcheck/era.dic b/spellcheck/era.dic index 214efbcd595f..666ee047fd26 100644 --- a/spellcheck/era.dic +++ b/spellcheck/era.dic @@ -262,6 +262,7 @@ sidechain sidechains tokenomics validator's +validator CHAINID PREVRANDAO ECDSA From 5994aaef62f43910629c5ec916799d6cd6ff0967 Mon Sep 17 00:00:00 2001 From: zksync-era-bot <147085853+zksync-era-bot@users.noreply.github.com> Date: Fri, 1 Dec 2023 12:12:27 +0100 Subject: [PATCH 024/268] chore(main): release core 18.4.0 (#560) :robot: I have created a release *beep* *boop* --- ## [18.4.0](https://github.com/matter-labs/zksync-era/compare/core-v18.3.1...core-v18.4.0) (2023-12-01) ### Features * adds spellchecker workflow, and corrects misspelled words ([#559](https://github.com/matter-labs/zksync-era/issues/559)) ([beac0a8](https://github.com/matter-labs/zksync-era/commit/beac0a85bb1535b05c395057171f197cd976bf82)) * **en:** Support arbitrary genesis block for external nodes ([#537](https://github.com/matter-labs/zksync-era/issues/537)) ([15d7eaf](https://github.com/matter-labs/zksync-era/commit/15d7eaf872e222338810243865cec9dff7f6e799)) * **merkle tree:** Remove enumeration index assignment from Merkle tree ([#551](https://github.com/matter-labs/zksync-era/issues/551)) ([e2c1b20](https://github.com/matter-labs/zksync-era/commit/e2c1b20e361e6ee2f5ac69cefe75d9c5575eb2f7)) * Restore commitment test in Boojum integration ([#539](https://github.com/matter-labs/zksync-era/issues/539)) ([06f510d](https://github.com/matter-labs/zksync-era/commit/06f510d00f855ddafaebb504f7ea799700221072)) ### Bug Fixes * Change no pending batches 404 error into a success response ([#279](https://github.com/matter-labs/zksync-era/issues/279)) ([e8fd805](https://github.com/matter-labs/zksync-era/commit/e8fd805c8be7980de7676bca87cfc2d445aab9e1)) * **vm:** Expose additional types and traits ([#563](https://github.com/matter-labs/zksync-era/issues/563)) ([bd268ac](https://github.com/matter-labs/zksync-era/commit/bd268ac02bc3530c1d3247cb9496c3e13c2e52d9)) * **witness_generator:** Disable BWIP dependency ([#573](https://github.com/matter-labs/zksync-era/issues/573)) ([e05d955](https://github.com/matter-labs/zksync-era/commit/e05d955036c76a29f9b6e900872c69e20278e045)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --- .github/release-please/manifest.json | 2 +- core/CHANGELOG.md | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/release-please/manifest.json b/.github/release-please/manifest.json index f5774af59440..9c7f15805a9b 100644 --- a/.github/release-please/manifest.json +++ b/.github/release-please/manifest.json @@ -1,5 +1,5 @@ { "sdk/zksync-rs": "0.4.0", - "core": "18.3.1", + "core": "18.4.0", "prover": "9.0.0" } diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index d34f9d4faf58..04f3f13e716e 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## [18.4.0](https://github.com/matter-labs/zksync-era/compare/core-v18.3.1...core-v18.4.0) (2023-12-01) + + +### Features + +* adds spellchecker workflow, and corrects misspelled words ([#559](https://github.com/matter-labs/zksync-era/issues/559)) ([beac0a8](https://github.com/matter-labs/zksync-era/commit/beac0a85bb1535b05c395057171f197cd976bf82)) +* **en:** Support arbitrary genesis block for external nodes ([#537](https://github.com/matter-labs/zksync-era/issues/537)) ([15d7eaf](https://github.com/matter-labs/zksync-era/commit/15d7eaf872e222338810243865cec9dff7f6e799)) +* **merkle tree:** Remove enumeration index assignment from Merkle tree ([#551](https://github.com/matter-labs/zksync-era/issues/551)) ([e2c1b20](https://github.com/matter-labs/zksync-era/commit/e2c1b20e361e6ee2f5ac69cefe75d9c5575eb2f7)) +* Restore commitment test in Boojum integration ([#539](https://github.com/matter-labs/zksync-era/issues/539)) ([06f510d](https://github.com/matter-labs/zksync-era/commit/06f510d00f855ddafaebb504f7ea799700221072)) + + +### Bug Fixes + +* Change no pending batches 404 error into a success response ([#279](https://github.com/matter-labs/zksync-era/issues/279)) ([e8fd805](https://github.com/matter-labs/zksync-era/commit/e8fd805c8be7980de7676bca87cfc2d445aab9e1)) +* **vm:** Expose additional types and traits ([#563](https://github.com/matter-labs/zksync-era/issues/563)) ([bd268ac](https://github.com/matter-labs/zksync-era/commit/bd268ac02bc3530c1d3247cb9496c3e13c2e52d9)) +* **witness_generator:** Disable BWIP dependency ([#573](https://github.com/matter-labs/zksync-era/issues/573)) ([e05d955](https://github.com/matter-labs/zksync-era/commit/e05d955036c76a29f9b6e900872c69e20278e045)) + ## [18.3.1](https://github.com/matter-labs/zksync-era/compare/core-v18.3.0...core-v18.3.1) (2023-11-28) From 0cd2c6b5ada568ffe01bce6d2dd8951457369141 Mon Sep 17 00:00:00 2001 From: Yury Akudovich Date: Fri, 1 Dec 2023 12:42:44 +0100 Subject: [PATCH 025/268] ci: Runs spellcheck in merge queue. (#574) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Runs spellcheck in merge queue. ## Why ❔ ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [ ] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --- .github/workflows/check-spelling.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check-spelling.yml b/.github/workflows/check-spelling.yml index 76fd6352c8e7..9073401eab9e 100644 --- a/.github/workflows/check-spelling.yml +++ b/.github/workflows/check-spelling.yml @@ -5,8 +5,9 @@ on: branches: - main pull_request: + merge_group: -env: +env: CARGO_TERM_COLOR: always jobs: @@ -17,7 +18,7 @@ jobs: uses: taiki-e/install-action@v2 with: tool: cargo-spellcheck - + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4 - name: Run cargo-spellcheck From 94a331952de64c02aeed6b0416b71ca7215ddece Mon Sep 17 00:00:00 2001 From: Karma <148863819+0xKarm@users.noreply.github.com> Date: Sat, 2 Dec 2023 05:11:48 +0800 Subject: [PATCH 026/268] chore: fix typo (#575) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ - fix typo ## Why ❔ - fix typo ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --------- Co-authored-by: Igor Aleksanov --- .../lib/multivm/src/versions/vm_virtual_blocks/tracers/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/utils.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/utils.rs index 0b6c7ebcfa8a..0ab697f626fa 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/utils.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/utils.rs @@ -91,7 +91,7 @@ pub(crate) fn get_debug_log( let msg = String::from_utf8(msg).expect("Invalid debug message"); let data = U256::from_big_endian(&data); - // For long data, it is better to use hex-encoding for greater readibility + // For long data, it is better to use hex-encoding for greater readability let data_str = if data > U256::from(u64::max_value()) { let mut bytes = [0u8; 32]; data.to_big_endian(&mut bytes); From 6e6e118c496c4a693f657da8e841a0bdf55b35db Mon Sep 17 00:00:00 2001 From: Todd <148772493+toddfil@users.noreply.github.com> Date: Sat, 2 Dec 2023 05:40:08 +0800 Subject: [PATCH 027/268] chore: fix typos in document (#577) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ - fixed typo ## Why ❔ fix typos in document ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --------- Co-authored-by: Igor Aleksanov --- .../multivm/src/versions/vm_virtual_blocks/utils/overhead.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/utils/overhead.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/utils/overhead.rs index 59b54888ee18..c17d619b464d 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/utils/overhead.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/utils/overhead.rs @@ -23,7 +23,7 @@ pub fn derive_overhead( let gas_limit = U256::from(gas_limit); let encoded_len = U256::from(encoded_len); - // The MAX_TX_ERGS_LIMIT is formed in a way that may fullfills a single-instance circuits + // The MAX_TX_ERGS_LIMIT is formed in a way that may fulfills a single-instance circuits // if used in full. That is, within MAX_TX_ERGS_LIMIT it is possible to fully saturate all the single-instance // circuits. let overhead_for_single_instance_circuits = From c594323054f5243ca08d6db3f59e007f14e30f4f Mon Sep 17 00:00:00 2001 From: Santala <31094102+tranhoaison@users.noreply.github.com> Date: Sat, 2 Dec 2023 06:07:21 +0700 Subject: [PATCH 028/268] chore: Fix typos (#567) Hi, I have just resolve conflict #432 Co-authored-by: Igor Aleksanov --- core/lib/multivm/src/tracers/validator/mod.rs | 2 +- .../lib/multivm/src/versions/vm_1_3_2/oracles/tracer/utils.rs | 2 +- .../src/versions/vm_1_3_2/oracles/tracer/validation.rs | 2 +- core/lib/multivm/src/versions/vm_1_3_2/tests/bootloader.rs | 2 +- core/lib/multivm/src/versions/vm_1_3_2/transaction_data.rs | 2 +- core/lib/multivm/src/versions/vm_1_3_2/vm_instance.rs | 2 +- core/lib/multivm/src/versions/vm_1_3_2/vm_with_bootloader.rs | 2 +- .../multivm/src/versions/vm_latest/bootloader_state/utils.rs | 2 +- .../multivm/src/versions/vm_latest/tests/require_eip712.rs | 2 +- core/lib/multivm/src/versions/vm_latest/tracers/utils.rs | 2 +- core/lib/multivm/src/versions/vm_latest/utils/overhead.rs | 2 +- core/lib/multivm/src/versions/vm_m5/oracles/tracer.rs | 4 ++-- core/lib/multivm/src/versions/vm_m5/transaction_data.rs | 2 +- core/lib/multivm/src/versions/vm_m5/vm_instance.rs | 2 +- core/lib/multivm/src/versions/vm_m6/oracles/tracer/utils.rs | 2 +- .../multivm/src/versions/vm_m6/oracles/tracer/validation.rs | 2 +- core/lib/multivm/src/versions/vm_m6/transaction_data.rs | 2 +- core/lib/multivm/src/versions/vm_m6/vm_instance.rs | 2 +- core/lib/multivm/src/versions/vm_m6/vm_with_bootloader.rs | 2 +- .../versions/vm_refunds_enhancement/bootloader_state/utils.rs | 2 +- .../versions/vm_refunds_enhancement/tests/require_eip712.rs | 2 +- .../src/versions/vm_refunds_enhancement/tracers/utils.rs | 2 +- .../src/versions/vm_refunds_enhancement/utils/overhead.rs | 2 +- .../src/versions/vm_virtual_blocks/bootloader_state/utils.rs | 2 +- .../src/versions/vm_virtual_blocks/tests/require_eip712.rs | 2 +- .../contracts/custom-account/custom-paymaster.sol | 2 +- etc/env/base/rust.toml | 2 +- etc/env/ext-node-docker.toml | 2 +- etc/env/ext-node.toml | 2 +- 29 files changed, 30 insertions(+), 30 deletions(-) diff --git a/core/lib/multivm/src/tracers/validator/mod.rs b/core/lib/multivm/src/tracers/validator/mod.rs index 26d3b0ad926c..718edf1a9641 100644 --- a/core/lib/multivm/src/tracers/validator/mod.rs +++ b/core/lib/multivm/src/tracers/validator/mod.rs @@ -104,7 +104,7 @@ impl ValidationTracer { return true; } - // The pair of MSG_VALUE_SIMULATOR_ADDRESS & L2_ETH_TOKEN_ADDRESS simulates the behavior of transfering ETH + // The pair of MSG_VALUE_SIMULATOR_ADDRESS & L2_ETH_TOKEN_ADDRESS simulates the behavior of transferring ETH // that is safe for the DDoS protection rules. if valid_eth_token_call(address, msg_sender) { return true; diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/utils.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/utils.rs index 9c9e87c065d0..3b3b99991ed2 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/utils.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/utils.rs @@ -84,7 +84,7 @@ pub(crate) fn get_debug_log( let msg = String::from_utf8(msg).expect("Invalid debug message"); let data = U256::from_big_endian(&data); - // For long data, it is better to use hex-encoding for greater readibility + // For long data, it is better to use hex-encoding for greater readability let data_str = if data > U256::from(u64::max_value()) { let mut bytes = [0u8; 32]; data.to_big_endian(&mut bytes); diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/validation.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/validation.rs index ee1587df3b06..c9ee54f35ba4 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/validation.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/validation.rs @@ -223,7 +223,7 @@ impl ValidationTracer { return true; } - // The pair of MSG_VALUE_SIMULATOR_ADDRESS & L2_ETH_TOKEN_ADDRESS simulates the behavior of transfering ETH + // The pair of MSG_VALUE_SIMULATOR_ADDRESS & L2_ETH_TOKEN_ADDRESS simulates the behavior of transferring ETH // that is safe for the DDoS protection rules. if valid_eth_token_call(address, msg_sender) { return true; diff --git a/core/lib/multivm/src/versions/vm_1_3_2/tests/bootloader.rs b/core/lib/multivm/src/versions/vm_1_3_2/tests/bootloader.rs index da9087afedd1..b42c17363b0a 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/tests/bootloader.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/tests/bootloader.rs @@ -2087,7 +2087,7 @@ // vm_test_env.get_eth_balance(&beneficiary), // U256::from(888000088) // ); -// // Make sure that the tokens were transfered from the AA account. +// // Make sure that the tokens were transferred from the AA account. // assert_eq!( // private_account_balance, // vm_test_env.get_eth_balance(&private_address) diff --git a/core/lib/multivm/src/versions/vm_1_3_2/transaction_data.rs b/core/lib/multivm/src/versions/vm_1_3_2/transaction_data.rs index 2d9dd1cb7aa1..d3a96dc06a7c 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/transaction_data.rs @@ -242,7 +242,7 @@ pub fn derive_overhead( let gas_limit = U256::from(gas_limit); let encoded_len = U256::from(encoded_len); - // The MAX_TX_ERGS_LIMIT is formed in a way that may fullfills a single-instance circuits + // The MAX_TX_ERGS_LIMIT is formed in a way that may fulfills a single-instance circuits // if used in full. That is, within MAX_TX_ERGS_LIMIT it is possible to fully saturate all the single-instance // circuits. let overhead_for_single_instance_circuits = diff --git a/core/lib/multivm/src/versions/vm_1_3_2/vm_instance.rs b/core/lib/multivm/src/versions/vm_1_3_2/vm_instance.rs index 3e157e74c02f..8b7c416522e6 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/vm_instance.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/vm_instance.rs @@ -85,7 +85,7 @@ pub struct VmExecutionResult { pub l2_to_l1_logs: Vec, pub return_data: Vec, - /// Value denoting the amount of gas spent withing VM invocation. + /// Value denoting the amount of gas spent within VM invocation. /// Note that return value represents the difference between the amount of gas /// available to VM before and after execution. /// diff --git a/core/lib/multivm/src/versions/vm_1_3_2/vm_with_bootloader.rs b/core/lib/multivm/src/versions/vm_1_3_2/vm_with_bootloader.rs index d5d3cba4a237..c2ff035c6698 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/vm_with_bootloader.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/vm_with_bootloader.rs @@ -589,7 +589,7 @@ pub(crate) fn get_bootloader_memory_for_encoded_tx( let encoding_length = encoded_tx.len(); memory.extend((tx_description_offset..tx_description_offset + encoding_length).zip(encoded_tx)); - // Note, +1 is moving for poitner + // Note, +1 is moving for pointer let compressed_bytecodes_offset = COMPRESSED_BYTECODES_OFFSET + 1 + previous_compressed_bytecode_size; diff --git a/core/lib/multivm/src/versions/vm_latest/bootloader_state/utils.rs b/core/lib/multivm/src/versions/vm_latest/bootloader_state/utils.rs index 78b98f0a4046..7e76f3faeffb 100644 --- a/core/lib/multivm/src/versions/vm_latest/bootloader_state/utils.rs +++ b/core/lib/multivm/src/versions/vm_latest/bootloader_state/utils.rs @@ -71,7 +71,7 @@ pub(super) fn apply_tx_to_memory( }; apply_l2_block(memory, &bootloader_l2_block, tx_index); - // Note, +1 is moving for poitner + // Note, +1 is moving for pointer let compressed_bytecodes_offset = COMPRESSED_BYTECODES_OFFSET + 1 + compressed_bytecodes_size; let encoded_compressed_bytecodes = diff --git a/core/lib/multivm/src/versions/vm_latest/tests/require_eip712.rs b/core/lib/multivm/src/versions/vm_latest/tests/require_eip712.rs index ad1d405a0753..1ad6f3512068 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/require_eip712.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/require_eip712.rs @@ -109,7 +109,7 @@ async fn test_require_eip712() { vm.get_eth_balance(beneficiary.address), U256::from(888000088) ); - // Make sure that the tokens were transfered from the AA account. + // Make sure that the tokens were transferred from the AA account. assert_eq!( private_account_balance, vm.get_eth_balance(private_account.address) diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/utils.rs b/core/lib/multivm/src/versions/vm_latest/tracers/utils.rs index c91d2f3ce0c1..52ff84f8c3c5 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/utils.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/utils.rs @@ -94,7 +94,7 @@ pub(crate) fn get_debug_log( let msg = String::from_utf8(msg).expect("Invalid debug message"); let data = U256::from_big_endian(&data); - // For long data, it is better to use hex-encoding for greater readibility + // For long data, it is better to use hex-encoding for greater readability let data_str = if data > U256::from(u64::max_value()) { let mut bytes = [0u8; 32]; data.to_big_endian(&mut bytes); diff --git a/core/lib/multivm/src/versions/vm_latest/utils/overhead.rs b/core/lib/multivm/src/versions/vm_latest/utils/overhead.rs index a4012e540ed1..c977267db8fe 100644 --- a/core/lib/multivm/src/versions/vm_latest/utils/overhead.rs +++ b/core/lib/multivm/src/versions/vm_latest/utils/overhead.rs @@ -23,7 +23,7 @@ pub fn derive_overhead( let gas_limit = U256::from(gas_limit); let encoded_len = U256::from(encoded_len); - // The MAX_TX_ERGS_LIMIT is formed in a way that may fullfills a single-instance circuits + // The MAX_TX_ERGS_LIMIT is formed in a way that may fulfills a single-instance circuits // if used in full. That is, within MAX_TX_ERGS_LIMIT it is possible to fully saturate all the single-instance // circuits. let overhead_for_single_instance_circuits = diff --git a/core/lib/multivm/src/versions/vm_m5/oracles/tracer.rs b/core/lib/multivm/src/versions/vm_m5/oracles/tracer.rs index 96ba04e85aa5..a9e3c32786ab 100644 --- a/core/lib/multivm/src/versions/vm_m5/oracles/tracer.rs +++ b/core/lib/multivm/src/versions/vm_m5/oracles/tracer.rs @@ -306,7 +306,7 @@ impl ValidationTracer { return true; } - // The pair of MSG_VALUE_SIMULATOR_ADDRESS & L2_ETH_TOKEN_ADDRESS simulates the behavior of transfering ETH + // The pair of MSG_VALUE_SIMULATOR_ADDRESS & L2_ETH_TOKEN_ADDRESS simulates the behavior of transferring ETH // that is safe for the DDoS protection rules. if valid_eth_token_call(address, msg_sender) { return true; @@ -801,7 +801,7 @@ fn get_debug_log(state: &VmLocalStateData<'_>, memory: &SimpleMemory) -> String let msg = String::from_utf8(msg).expect("Invalid debug message"); let data = U256::from_big_endian(&data); - // For long data, it is better to use hex-encoding for greater readibility + // For long data, it is better to use hex-encoding for greater readability let data_str = if data > U256::from(u64::max_value()) { let mut bytes = [0u8; 32]; data.to_big_endian(&mut bytes); diff --git a/core/lib/multivm/src/versions/vm_m5/transaction_data.rs b/core/lib/multivm/src/versions/vm_m5/transaction_data.rs index b749ff092750..819f22a53249 100644 --- a/core/lib/multivm/src/versions/vm_m5/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_m5/transaction_data.rs @@ -225,7 +225,7 @@ pub fn derive_overhead(gas_limit: u32, gas_price_per_pubdata: u32, encoded_len: let gas_price_per_pubdata = U256::from(gas_price_per_pubdata); let encoded_len = U256::from(encoded_len); - // The MAX_TX_ERGS_LIMIT is formed in a way that may fullfills a single-instance circuits + // The MAX_TX_ERGS_LIMIT is formed in a way that may fulfills a single-instance circuits // if used in full. That is, within MAX_TX_ERGS_LIMIT it is possible to fully saturate all the single-instance // circuits. let overhead_for_single_instance_circuits = diff --git a/core/lib/multivm/src/versions/vm_m5/vm_instance.rs b/core/lib/multivm/src/versions/vm_m5/vm_instance.rs index e92305003c72..5638ed1c023c 100644 --- a/core/lib/multivm/src/versions/vm_m5/vm_instance.rs +++ b/core/lib/multivm/src/versions/vm_m5/vm_instance.rs @@ -103,7 +103,7 @@ pub struct VmExecutionResult { pub l2_to_l1_logs: Vec, pub return_data: Vec, - /// Value denoting the amount of gas spent withing VM invocation. + /// Value denoting the amount of gas spent within VM invocation. /// Note that return value represents the difference between the amount of gas /// available to VM before and after execution. /// diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/utils.rs b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/utils.rs index 87aa81d69db5..b256575726ae 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/utils.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/utils.rs @@ -84,7 +84,7 @@ pub(crate) fn get_debug_log( let msg = String::from_utf8(msg).expect("Invalid debug message"); let data = U256::from_big_endian(&data); - // For long data, it is better to use hex-encoding for greater readibility + // For long data, it is better to use hex-encoding for greater readability let data_str = if data > U256::from(u64::max_value()) { let mut bytes = [0u8; 32]; data.to_big_endian(&mut bytes); diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/validation.rs b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/validation.rs index 4e55ad4db004..13a0badd442b 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/validation.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/validation.rs @@ -238,7 +238,7 @@ impl ValidationTracer { return true; } - // The pair of MSG_VALUE_SIMULATOR_ADDRESS & L2_ETH_TOKEN_ADDRESS simulates the behavior of transfering ETH + // The pair of MSG_VALUE_SIMULATOR_ADDRESS & L2_ETH_TOKEN_ADDRESS simulates the behavior of transferring ETH // that is safe for the DDoS protection rules. if valid_eth_token_call(address, msg_sender) { return true; diff --git a/core/lib/multivm/src/versions/vm_m6/transaction_data.rs b/core/lib/multivm/src/versions/vm_m6/transaction_data.rs index bdecb9bf4547..6779ce95fc3e 100644 --- a/core/lib/multivm/src/versions/vm_m6/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_m6/transaction_data.rs @@ -243,7 +243,7 @@ pub fn derive_overhead( let gas_limit = U256::from(gas_limit); let encoded_len = U256::from(encoded_len); - // The MAX_TX_ERGS_LIMIT is formed in a way that may fullfills a single-instance circuits + // The MAX_TX_ERGS_LIMIT is formed in a way that may fulfills a single-instance circuits // if used in full. That is, within MAX_TX_ERGS_LIMIT it is possible to fully saturate all the single-instance // circuits. let overhead_for_single_instance_circuits = diff --git a/core/lib/multivm/src/versions/vm_m6/vm_instance.rs b/core/lib/multivm/src/versions/vm_m6/vm_instance.rs index 468dd3fc72da..f15adde2584b 100644 --- a/core/lib/multivm/src/versions/vm_m6/vm_instance.rs +++ b/core/lib/multivm/src/versions/vm_m6/vm_instance.rs @@ -103,7 +103,7 @@ pub struct VmExecutionResult { pub l2_to_l1_logs: Vec, pub return_data: Vec, - /// Value denoting the amount of gas spent withing VM invocation. + /// Value denoting the amount of gas spent within VM invocation. /// Note that return value represents the difference between the amount of gas /// available to VM before and after execution. /// diff --git a/core/lib/multivm/src/versions/vm_m6/vm_with_bootloader.rs b/core/lib/multivm/src/versions/vm_m6/vm_with_bootloader.rs index 306c0ffc6dec..998f41275b41 100644 --- a/core/lib/multivm/src/versions/vm_m6/vm_with_bootloader.rs +++ b/core/lib/multivm/src/versions/vm_m6/vm_with_bootloader.rs @@ -766,7 +766,7 @@ pub(crate) fn get_bootloader_memory_for_encoded_tx( let encoding_length = encoded_tx.len(); memory.extend((tx_description_offset..tx_description_offset + encoding_length).zip(encoded_tx)); - // Note, +1 is moving for poitner + // Note, +1 is moving for pointer let compressed_bytecodes_offset = COMPRESSED_BYTECODES_OFFSET + 1 + previous_compressed_bytecode_size; diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/utils.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/utils.rs index dbb3fa0dff24..fed5108d7f3d 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/utils.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/utils.rs @@ -69,7 +69,7 @@ pub(super) fn apply_tx_to_memory( }; apply_l2_block(memory, &bootloader_l2_block, tx_index); - // Note, +1 is moving for poitner + // Note, +1 is moving for pointer let compressed_bytecodes_offset = COMPRESSED_BYTECODES_OFFSET + 1 + compressed_bytecodes_size; let encoded_compressed_bytecodes = diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tests/require_eip712.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/tests/require_eip712.rs index 253a3463c532..03a704841b04 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/tests/require_eip712.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/tests/require_eip712.rs @@ -109,7 +109,7 @@ async fn test_require_eip712() { vm.get_eth_balance(beneficiary.address), U256::from(888000088) ); - // Make sure that the tokens were transfered from the AA account. + // Make sure that the tokens were transferred from the AA account. assert_eq!( private_account_balance, vm.get_eth_balance(private_account.address) diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/utils.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/utils.rs index a9170c5a442a..3026afea0078 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/utils.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/utils.rs @@ -94,7 +94,7 @@ pub(crate) fn get_debug_log( let msg = String::from_utf8(msg).expect("Invalid debug message"); let data = U256::from_big_endian(&data); - // For long data, it is better to use hex-encoding for greater readibility + // For long data, it is better to use hex-encoding for greater readability let data_str = if data > U256::from(u64::max_value()) { let mut bytes = [0u8; 32]; data.to_big_endian(&mut bytes); diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/overhead.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/overhead.rs index cce2f2914e33..6c56515cfd7b 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/overhead.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/overhead.rs @@ -23,7 +23,7 @@ pub fn derive_overhead( let gas_limit = U256::from(gas_limit); let encoded_len = U256::from(encoded_len); - // The MAX_TX_ERGS_LIMIT is formed in a way that may fullfills a single-instance circuits + // The MAX_TX_ERGS_LIMIT is formed in a way that may fulfills a single-instance circuits // if used in full. That is, within MAX_TX_ERGS_LIMIT it is possible to fully saturate all the single-instance // circuits. let overhead_for_single_instance_circuits = diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/utils.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/utils.rs index ffe0be2f03bc..6e836ad201db 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/utils.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/utils.rs @@ -69,7 +69,7 @@ pub(super) fn apply_tx_to_memory( }; apply_l2_block(memory, &bootloader_l2_block, tx_index); - // Note, +1 is moving for poitner + // Note, +1 is moving for pointer let compressed_bytecodes_offset = COMPRESSED_BYTECODES_OFFSET + 1 + compressed_bytecodes_size; let encoded_compressed_bytecodes = diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/require_eip712.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/require_eip712.rs index 82c1a0527923..988841e90ce6 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tests/require_eip712.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tests/require_eip712.rs @@ -107,7 +107,7 @@ async fn test_require_eip712() { vm.get_eth_balance(beneficiary.address), U256::from(888000088) ); - // Make sure that the tokens were transfered from the AA account. + // Make sure that the tokens were transferred from the AA account. assert_eq!( private_account_balance, vm.get_eth_balance(private_account.address) diff --git a/core/tests/ts-integration/contracts/custom-account/custom-paymaster.sol b/core/tests/ts-integration/contracts/custom-account/custom-paymaster.sol index 164aee98518c..e55f093cb788 100644 --- a/core/tests/ts-integration/contracts/custom-account/custom-paymaster.sol +++ b/core/tests/ts-integration/contracts/custom-account/custom-paymaster.sol @@ -63,7 +63,7 @@ contract CustomPaymaster is IPaymaster { bool success = _transaction.payToTheBootloader(); require(success, "Failed to transfer funds to the bootloader"); - // For now, refunds are not supported, so we just test the fact that the transfered context is correct + // For now, refunds are not supported, so we just test the fact that the transferred context is correct txCounter += 1; context = abi.encode(txCounter); } else { diff --git a/etc/env/base/rust.toml b/etc/env/base/rust.toml index 2842d3bbcafb..4c0ebb6ed052 100644 --- a/etc/env/base/rust.toml +++ b/etc/env/base/rust.toml @@ -2,7 +2,7 @@ # We don't provide the group name like `[rust]` here, because we don't want # these variables to be prefixed during the compiling. -# `RUST_LOG` environmnet variable for `env_logger` +# `RUST_LOG` environment variable for `env_logger` # Here we use TOML multiline strings: newlines will be trimmed. RUST_LOG="""\ zksync_core=debug,\ diff --git a/etc/env/ext-node-docker.toml b/etc/env/ext-node-docker.toml index 4db60a3c19d6..b14a35ffef19 100644 --- a/etc/env/ext-node-docker.toml +++ b/etc/env/ext-node-docker.toml @@ -33,7 +33,7 @@ bootloader_hash="0x0100038581be3d0e201b3cc45d151ef5cc59eb3a0f146ad44f0f72abf00b5 default_aa_hash="0x0100038dc66b69be75ec31653c64cb931678299b9b659472772b2550b703f41c" [rust] -# `RUST_LOG` environmnet variable for `env_logger` +# `RUST_LOG` environment variable for `env_logger` # Here we use TOML multiline strings: newlines will be trimmed. log="""\ warn,\ diff --git a/etc/env/ext-node.toml b/etc/env/ext-node.toml index 697580d19386..61f74a87ce00 100644 --- a/etc/env/ext-node.toml +++ b/etc/env/ext-node.toml @@ -33,7 +33,7 @@ bootloader_hash="0x0100038581be3d0e201b3cc45d151ef5cc59eb3a0f146ad44f0f72abf00b5 default_aa_hash="0x0100038dc66b69be75ec31653c64cb931678299b9b659472772b2550b703f41c" [rust] -# `RUST_LOG` environmnet variable for `env_logger` +# `RUST_LOG` environment variable for `env_logger` # Here we use TOML multiline strings: newlines will be trimmed. log="""\ warn,\ From 19c84ce624d53735133fa3b12c7f980e8c14260d Mon Sep 17 00:00:00 2001 From: Lech <88630083+Artemka374@users.noreply.github.com> Date: Mon, 4 Dec 2023 11:51:17 +0200 Subject: [PATCH 029/268] feat: Add metric to CallTracer for calculating maximum depth of the calls (#535) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Add metric to CallTracer for calculating maximum depth of the calls ## Why ❔ We need to know what our limits are. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --- .../src/tracers/call_tracer/metrics.rs | 15 ++++++ .../multivm/src/tracers/call_tracer/mod.rs | 46 +++++++++++++++++++ .../src/tracers/call_tracer/vm_latest/mod.rs | 15 ++---- .../call_tracer/vm_refunds_enhancement/mod.rs | 16 +++---- .../call_tracer/vm_virtual_blocks/mod.rs | 15 ++---- 5 files changed, 77 insertions(+), 30 deletions(-) create mode 100644 core/lib/multivm/src/tracers/call_tracer/metrics.rs diff --git a/core/lib/multivm/src/tracers/call_tracer/metrics.rs b/core/lib/multivm/src/tracers/call_tracer/metrics.rs new file mode 100644 index 000000000000..b3d94464f50f --- /dev/null +++ b/core/lib/multivm/src/tracers/call_tracer/metrics.rs @@ -0,0 +1,15 @@ +use vise::{Buckets, Histogram, Metrics}; + +#[derive(Debug, Metrics)] +#[metrics(prefix = "vm_call_tracer")] +pub struct CallMetrics { + /// Maximum call stack depth during the execution of the transaction. + #[metrics(buckets = Buckets::exponential(1.0..=64.0, 2.0))] + pub call_stack_depth: Histogram, + /// Maximum number of near calls during the execution of the transaction. + #[metrics(buckets = Buckets::exponential(1.0..=64.0, 2.0))] + pub max_near_calls: Histogram, +} + +#[vise::register] +pub static CALL_METRICS: vise::Global = vise::Global::new(); diff --git a/core/lib/multivm/src/tracers/call_tracer/mod.rs b/core/lib/multivm/src/tracers/call_tracer/mod.rs index 90343a53bf66..90f15fb68d40 100644 --- a/core/lib/multivm/src/tracers/call_tracer/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/mod.rs @@ -1,7 +1,9 @@ +use crate::tracers::call_tracer::metrics::CALL_METRICS; use once_cell::sync::OnceCell; use std::sync::Arc; use zksync_types::vm_trace::Call; +mod metrics; pub mod vm_latest; pub mod vm_refunds_enhancement; pub mod vm_virtual_blocks; @@ -10,12 +12,23 @@ pub mod vm_virtual_blocks; pub struct CallTracer { stack: Vec, result: Arc>>, + + max_stack_depth: usize, + max_near_calls: usize, } #[derive(Debug, Clone)] struct FarcallAndNearCallCount { farcall: Call, near_calls_after: usize, + stack_depth_on_prefix: usize, +} + +impl Drop for CallTracer { + fn drop(&mut self) { + CALL_METRICS.call_stack_depth.observe(self.max_stack_depth); + CALL_METRICS.max_near_calls.observe(self.max_near_calls); + } } impl CallTracer { @@ -23,6 +36,8 @@ impl CallTracer { Self { stack: vec![], result, + max_stack_depth: 0, + max_near_calls: 0, } } @@ -38,4 +53,35 @@ impl CallTracer { let cell = self.result.as_ref(); cell.set(result).unwrap(); } + + fn push_call_and_update_stats(&mut self, farcall: Call, near_calls_after: usize) { + let stack_depth = self + .stack + .last() + .map(|x| x.stack_depth_on_prefix) + .unwrap_or(0); + + let depth_on_prefix = stack_depth + 1 + near_calls_after; + + let call = FarcallAndNearCallCount { + farcall, + near_calls_after, + stack_depth_on_prefix: depth_on_prefix, + }; + + self.stack.push(call); + + self.max_stack_depth = self.max_stack_depth.max(depth_on_prefix); + self.max_near_calls = self.max_near_calls.max(near_calls_after); + } + + fn increase_near_call_count(&mut self) { + if let Some(last) = self.stack.last_mut() { + last.near_calls_after += 1; + last.stack_depth_on_prefix += 1; + + self.max_near_calls = self.max_near_calls.max(last.near_calls_after); + self.max_stack_depth = self.max_stack_depth.max(last.stack_depth_on_prefix); + } + } } diff --git a/core/lib/multivm/src/tracers/call_tracer/vm_latest/mod.rs b/core/lib/multivm/src/tracers/call_tracer/vm_latest/mod.rs index 2b6fc144bd4d..f5f5c1077d3d 100644 --- a/core/lib/multivm/src/tracers/call_tracer/vm_latest/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/vm_latest/mod.rs @@ -15,7 +15,7 @@ use crate::interface::{ tracer::VmExecutionStopReason, traits::tracers::dyn_tracers::vm_1_4_0::DynTracer, VmRevertReason, }; -use crate::tracers::call_tracer::{CallTracer, FarcallAndNearCallCount}; +use crate::tracers::call_tracer::CallTracer; use crate::vm_latest::{BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState}; impl DynTracer> for CallTracer { @@ -28,9 +28,7 @@ impl DynTracer> for CallTracer { ) { match data.opcode.variant.opcode { Opcode::NearCall(_) => { - if let Some(last) = self.stack.last_mut() { - last.near_calls_after += 1; - } + self.increase_near_call_count(); } Opcode::FarCall(far_call) => { // We use parent gas for properly calculating gas used in the trace. @@ -51,10 +49,7 @@ impl DynTracer> for CallTracer { }; self.handle_far_call_op_code_latest(state, memory, &mut current_call); - self.stack.push(FarcallAndNearCallCount { - farcall: current_call, - near_calls_after: 0, - }); + self.push_call_and_update_stats(current_call, 0); } Opcode::Ret(ret_code) => { self.handle_ret_op_code_latest(state, memory, ret_code); @@ -187,7 +182,7 @@ impl CallTracer { if current_call.near_calls_after > 0 { current_call.near_calls_after -= 1; - self.stack.push(current_call); + self.push_call_and_update_stats(current_call.farcall, current_call.near_calls_after); return; } @@ -203,7 +198,7 @@ impl CallTracer { if let Some(parent_call) = self.stack.last_mut() { parent_call.farcall.calls.push(current_call.farcall); } else { - self.stack.push(current_call); + self.push_call_and_update_stats(current_call.farcall, current_call.near_calls_after); } } } diff --git a/core/lib/multivm/src/tracers/call_tracer/vm_refunds_enhancement/mod.rs b/core/lib/multivm/src/tracers/call_tracer/vm_refunds_enhancement/mod.rs index 43dd363dcea6..fab4ee0ff0fb 100644 --- a/core/lib/multivm/src/tracers/call_tracer/vm_refunds_enhancement/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/vm_refunds_enhancement/mod.rs @@ -15,7 +15,7 @@ use crate::interface::{ tracer::VmExecutionStopReason, traits::tracers::dyn_tracers::vm_1_3_3::DynTracer, VmRevertReason, }; -use crate::tracers::call_tracer::{CallTracer, FarcallAndNearCallCount}; +use crate::tracers::call_tracer::CallTracer; use crate::vm_refunds_enhancement::{ BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState, }; @@ -30,9 +30,7 @@ impl DynTracer> for CallTracer { ) { match data.opcode.variant.opcode { Opcode::NearCall(_) => { - if let Some(last) = self.stack.last_mut() { - last.near_calls_after += 1; - } + self.increase_near_call_count(); } Opcode::FarCall(far_call) => { // We use parent gas for properly calculating gas used in the trace. @@ -53,10 +51,8 @@ impl DynTracer> for CallTracer { }; self.handle_far_call_op_code_refunds_enhancement(state, memory, &mut current_call); - self.stack.push(FarcallAndNearCallCount { - farcall: current_call, - near_calls_after: 0, - }); + + self.push_call_and_update_stats(current_call, 0); } Opcode::Ret(ret_code) => { self.handle_ret_op_code_refunds_enhancement(state, memory, ret_code); @@ -189,7 +185,7 @@ impl CallTracer { if current_call.near_calls_after > 0 { current_call.near_calls_after -= 1; - self.stack.push(current_call); + self.push_call_and_update_stats(current_call.farcall, current_call.near_calls_after); return; } @@ -205,7 +201,7 @@ impl CallTracer { if let Some(parent_call) = self.stack.last_mut() { parent_call.farcall.calls.push(current_call.farcall); } else { - self.stack.push(current_call); + self.push_call_and_update_stats(current_call.farcall, current_call.near_calls_after); } } } diff --git a/core/lib/multivm/src/tracers/call_tracer/vm_virtual_blocks/mod.rs b/core/lib/multivm/src/tracers/call_tracer/vm_virtual_blocks/mod.rs index c78593b40e7d..631d4d2081ca 100644 --- a/core/lib/multivm/src/tracers/call_tracer/vm_virtual_blocks/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/vm_virtual_blocks/mod.rs @@ -12,7 +12,7 @@ use zksync_types::U256; use crate::interface::{ dyn_tracers::vm_1_3_3::DynTracer, VmExecutionResultAndLogs, VmRevertReason, }; -use crate::tracers::call_tracer::{CallTracer, FarcallAndNearCallCount}; +use crate::tracers::call_tracer::CallTracer; use crate::vm_virtual_blocks::{ ExecutionEndTracer, ExecutionProcessing, HistoryMode, SimpleMemory, VmTracer, }; @@ -27,9 +27,7 @@ impl DynTracer> for CallTracer { ) { match data.opcode.variant.opcode { Opcode::NearCall(_) => { - if let Some(last) = self.stack.last_mut() { - last.near_calls_after += 1; - } + self.increase_near_call_count(); } Opcode::FarCall(far_call) => { // We use parent gas for properly calculating gas used in the trace. @@ -50,10 +48,7 @@ impl DynTracer> for CallTracer { }; self.handle_far_call_op_code_virtual_blocks(state, data, memory, &mut current_call); - self.stack.push(FarcallAndNearCallCount { - farcall: current_call, - near_calls_after: 0, - }); + self.push_call_and_update_stats(current_call, 0); } Opcode::Ret(ret_code) => { self.handle_ret_op_code_virtual_blocks(state, data, memory, ret_code); @@ -187,7 +182,7 @@ impl CallTracer { if current_call.near_calls_after > 0 { current_call.near_calls_after -= 1; - self.stack.push(current_call); + self.push_call_and_update_stats(current_call.farcall, current_call.near_calls_after); return; } @@ -203,7 +198,7 @@ impl CallTracer { if let Some(parent_call) = self.stack.last_mut() { parent_call.farcall.calls.push(current_call.farcall); } else { - self.stack.push(current_call); + self.push_call_and_update_stats(current_call.farcall, current_call.near_calls_after); } } } From 58a4e6c4c22bd7f002ede1c6def0dc260706185e Mon Sep 17 00:00:00 2001 From: Lech <88630083+Artemka374@users.noreply.github.com> Date: Mon, 4 Dec 2023 12:06:40 +0200 Subject: [PATCH 030/268] feat: Add various metrics to the Prover subsystems (#541) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ 1. Add various metrics to the Prover subsystems, especially: * oldest block, that wasn't sent to prover(`fri_prover.oldest_unprocessed_block`) * oldest block, that didn't go through basic/leaf/node aggregation levels (`fri_prover.oldest_unprocessed_block_by_round`) * how much time is spent on waiting for available prover to send data to (`prover_fri_witness_vector_generator.prover_waiting_time) * count for attempts to send data to prover (`prover_fri_witness_vector_generator.prover_attempts_count`) 2. Refactor metrics in prover to use vise. ## Why ❔ We have some metric coverage on the prover subsystem, but it's incomplete. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --- core/lib/dal/sqlx-data.json | 74 +++++++++++++++++++ core/lib/dal/src/fri_proof_compressor_dal.rs | 16 ++++ core/lib/dal/src/fri_prover_dal.rs | 22 ++++++ core/lib/dal/src/proof_generation_dal.rs | 32 ++++++++ core/lib/types/src/proofs.rs | 11 +++ .../fri_proof_compressor_queue_monitor.rs | 20 +++++ .../house_keeper/fri_prover_queue_monitor.rs | 39 ++++++++++ .../zksync_core/src/witness_generator/mod.rs | 7 +- prover/Cargo.lock | 16 ++-- prover/circuit_synthesizer/Cargo.toml | 3 +- .../src/circuit_synthesizer.rs | 14 +--- prover/circuit_synthesizer/src/main.rs | 1 + prover/circuit_synthesizer/src/metrics.rs | 14 ++++ prover/proof_fri_compressor/Cargo.toml | 3 +- prover/proof_fri_compressor/src/compressor.rs | 24 +++--- prover/proof_fri_compressor/src/main.rs | 1 + prover/proof_fri_compressor/src/metrics.rs | 16 ++++ prover/prover/Cargo.toml | 3 +- prover/prover/src/main.rs | 1 + prover/prover/src/metrics.rs | 40 ++++++++++ prover/prover/src/prover.rs | 57 ++++---------- prover/prover/src/run.rs | 4 +- .../src/synthesized_circuit_provider.rs | 9 +-- prover/prover_fri/Cargo.toml | 3 +- .../src/gpu_prover_job_processor.rs | 24 +++--- prover/prover_fri/src/lib.rs | 1 + prover/prover_fri/src/main.rs | 2 + prover/prover_fri/src/metrics.rs | 43 +++++++++++ prover/prover_fri/src/prover_job_processor.rs | 41 +++++----- prover/prover_fri/src/socket_listener.rs | 18 ++--- prover/prover_fri/src/utils.rs | 16 ++-- prover/prover_fri_gateway/Cargo.toml | 3 +- .../src/api_data_fetcher.rs | 3 +- prover/prover_fri_gateway/src/main.rs | 1 + prover/prover_fri_gateway/src/metrics.rs | 11 +++ prover/prover_fri_utils/Cargo.toml | 3 +- prover/prover_fri_utils/src/lib.rs | 15 ++-- prover/prover_fri_utils/src/metrics.rs | 36 +++++++++ prover/witness_generator/Cargo.toml | 3 +- .../witness_generator/src/basic_circuits.rs | 31 ++++---- .../witness_generator/src/leaf_aggregation.rs | 35 ++++----- prover/witness_generator/src/lib.rs | 2 + prover/witness_generator/src/main.rs | 10 +-- prover/witness_generator/src/metrics.rs | 33 +++++++++ .../witness_generator/src/node_aggregation.rs | 36 ++++----- prover/witness_generator/src/scheduler.rs | 31 +++----- prover/witness_vector_generator/Cargo.toml | 3 +- .../witness_vector_generator/src/generator.rs | 20 ++--- prover/witness_vector_generator/src/lib.rs | 2 + prover/witness_vector_generator/src/main.rs | 1 + .../witness_vector_generator/src/metrics.rs | 18 +++++ 51 files changed, 623 insertions(+), 249 deletions(-) create mode 100644 prover/circuit_synthesizer/src/metrics.rs create mode 100644 prover/proof_fri_compressor/src/metrics.rs create mode 100644 prover/prover/src/metrics.rs create mode 100644 prover/prover_fri/src/metrics.rs create mode 100644 prover/prover_fri_gateway/src/metrics.rs create mode 100644 prover/prover_fri_utils/src/metrics.rs create mode 100644 prover/witness_generator/src/metrics.rs create mode 100644 prover/witness_vector_generator/src/metrics.rs diff --git a/core/lib/dal/sqlx-data.json b/core/lib/dal/sqlx-data.json index 2c958ce0394c..9084adb61cda 100644 --- a/core/lib/dal/sqlx-data.json +++ b/core/lib/dal/sqlx-data.json @@ -2744,6 +2744,24 @@ }, "query": "UPDATE transactions\n SET in_mempool = TRUE\n FROM (\n SELECT hash FROM (\n SELECT hash\n FROM transactions\n WHERE miniblock_number IS NULL AND in_mempool = FALSE AND error IS NULL\n AND (is_priority = TRUE OR (max_fee_per_gas >= $2 and gas_per_pubdata_limit >= $3))\n AND tx_format != $4\n ORDER BY is_priority DESC, priority_op_id, received_at\n LIMIT $1\n ) as subquery1\n ORDER BY hash\n ) as subquery2\n WHERE transactions.hash = subquery2.hash\n RETURNING transactions.*" }, + "2cc57497090a97bcb453036f7b5e2139b590699aa1a2df4d6fd2b19e27e06251": { + "describe": { + "columns": [ + { + "name": "l1_batch_number", + "ordinal": 0, + "type_info": "Int8" + } + ], + "nullable": [ + false + ], + "parameters": { + "Left": [] + } + }, + "query": "SELECT l1_batch_number FROM proof_generation_details WHERE status <> 'generated' ORDER BY l1_batch_number ASC LIMIT 1" + }, "2e3f116ca05ae70b7c83ac550302194c91f57b69902ff8e42140fde732ae5e6a": { "describe": { "columns": [], @@ -3971,6 +3989,24 @@ }, "query": "VACUUM storage_logs" }, + "4860c1118485da8673963a260ded76eb8e13989936f9ab17e23687a1103132cb": { + "describe": { + "columns": [ + { + "name": "l1_batch_number", + "ordinal": 0, + "type_info": "Int8" + } + ], + "nullable": [ + false + ], + "parameters": { + "Left": [] + } + }, + "query": "SELECT l1_batch_number FROM proof_generation_details WHERE status = 'ready_to_be_proven' ORDER BY l1_batch_number ASC LIMIT 1" + }, "4ab8a25620b5400d836e1b847320d4e176629a27e1a6cb0666ab02bb55371769": { "describe": { "columns": [ @@ -8814,6 +8850,26 @@ }, "query": "SELECT MAX(operation_number) as \"max?\" FROM storage_logs WHERE miniblock_number = $1" }, + "a7c7e8f036404d24dc6bfa184a84b92d8f73ca034970481af34b6163e66dc59a": { + "describe": { + "columns": [ + { + "name": "l1_batch_number", + "ordinal": 0, + "type_info": "Int8" + } + ], + "nullable": [ + false + ], + "parameters": { + "Left": [ + "Int2" + ] + } + }, + "query": "\n SELECT l1_batch_number \n FROM prover_jobs_fri \n WHERE status <> 'skipped'\n AND status <> 'successful'\n AND aggregation_round = $1 \n ORDER BY l1_batch_number ASC \n LIMIT 1\n " + }, "a8b32073a67ad77caab11e73a5cac5aa5b5382648ff95d6787a309eb3f64d434": { "describe": { "columns": [], @@ -11541,6 +11597,24 @@ }, "query": "\n UPDATE node_aggregation_witness_jobs\n SET status='queued'\n WHERE l1_batch_number IN\n (SELECT prover_jobs.l1_batch_number\n FROM prover_jobs\n JOIN node_aggregation_witness_jobs nawj ON prover_jobs.l1_batch_number = nawj.l1_batch_number\n WHERE nawj.status = 'waiting_for_proofs'\n AND prover_jobs.status = 'successful'\n AND prover_jobs.aggregation_round = 1\n GROUP BY prover_jobs.l1_batch_number, nawj.number_of_leaf_circuits\n HAVING COUNT(*) = nawj.number_of_leaf_circuits)\n RETURNING l1_batch_number;\n " }, + "f15f0848cfd830ec5d5b479fdcdd36c6a4439495b7680614ac1b0e4d73fb992f": { + "describe": { + "columns": [ + { + "name": "l1_batch_number", + "ordinal": 0, + "type_info": "Int8" + } + ], + "nullable": [ + false + ], + "parameters": { + "Left": [] + } + }, + "query": "SELECT l1_batch_number FROM proof_compression_jobs_fri WHERE status <> 'successful' ORDER BY l1_batch_number ASC LIMIT 1" + }, "f1defa140e20b9c250d3212602dc259c0a35598c2e69d1c42746a8fab6dd8d3e": { "describe": { "columns": [], diff --git a/core/lib/dal/src/fri_proof_compressor_dal.rs b/core/lib/dal/src/fri_proof_compressor_dal.rs index 97caf76ebce0..b7f1d1921e91 100644 --- a/core/lib/dal/src/fri_proof_compressor_dal.rs +++ b/core/lib/dal/src/fri_proof_compressor_dal.rs @@ -206,6 +206,22 @@ impl FriProofCompressorDal<'_, '_> { } } + pub async fn get_oldest_not_compressed_batch(&mut self) -> Option { + let result: Option = sqlx::query!( + "SELECT l1_batch_number \ + FROM proof_compression_jobs_fri \ + WHERE status <> 'successful' \ + ORDER BY l1_batch_number ASC \ + LIMIT 1", + ) + .fetch_optional(self.storage.conn()) + .await + .unwrap() + .map(|row| L1BatchNumber(row.l1_batch_number as u32)); + + result + } + pub async fn requeue_stuck_jobs( &mut self, processing_timeout: Duration, diff --git a/core/lib/dal/src/fri_prover_dal.rs b/core/lib/dal/src/fri_prover_dal.rs index 026cb783dd32..7878cbfa5421 100644 --- a/core/lib/dal/src/fri_prover_dal.rs +++ b/core/lib/dal/src/fri_prover_dal.rs @@ -341,6 +341,28 @@ impl FriProverDal<'_, '_> { } } + pub async fn min_unproved_l1_batch_number_for_aggregation_round( + &mut self, + aggregation_round: AggregationRound, + ) -> Option { + sqlx::query!( + r#" + SELECT l1_batch_number + FROM prover_jobs_fri + WHERE status <> 'skipped' + AND status <> 'successful' + AND aggregation_round = $1 + ORDER BY l1_batch_number ASC + LIMIT 1 + "#, + aggregation_round as i16 + ) + .fetch_optional(self.storage.conn()) + .await + .unwrap() + .map(|row| L1BatchNumber(row.l1_batch_number as u32)) + } + pub async fn update_status(&mut self, id: u32, status: &str) { sqlx::query!( "UPDATE prover_jobs_fri \ diff --git a/core/lib/dal/src/proof_generation_dal.rs b/core/lib/dal/src/proof_generation_dal.rs index d5fd3079dc18..22db44634698 100644 --- a/core/lib/dal/src/proof_generation_dal.rs +++ b/core/lib/dal/src/proof_generation_dal.rs @@ -109,4 +109,36 @@ impl ProofGenerationDal<'_, '_> { .then_some(()) .ok_or(sqlx::Error::RowNotFound) } + + pub async fn get_oldest_unprocessed_batch(&mut self) -> Option { + let result: Option = sqlx::query!( + "SELECT l1_batch_number \ + FROM proof_generation_details \ + WHERE status = 'ready_to_be_proven' \ + ORDER BY l1_batch_number ASC \ + LIMIT 1", + ) + .fetch_optional(self.storage.conn()) + .await + .unwrap() + .map(|row| L1BatchNumber(row.l1_batch_number as u32)); + + result + } + + pub async fn get_oldest_not_generated_batch(&mut self) -> Option { + let result: Option = sqlx::query!( + "SELECT l1_batch_number \ + FROM proof_generation_details \ + WHERE status <> 'generated' \ + ORDER BY l1_batch_number ASC \ + LIMIT 1", + ) + .fetch_optional(self.storage.conn()) + .await + .unwrap() + .map(|row| L1BatchNumber(row.l1_batch_number as u32)); + + result + } } diff --git a/core/lib/types/src/proofs.rs b/core/lib/types/src/proofs.rs index 28d25900231d..b28b81b79fbf 100644 --- a/core/lib/types/src/proofs.rs +++ b/core/lib/types/src/proofs.rs @@ -98,6 +98,17 @@ impl AggregationRound { } } +impl std::fmt::Display for AggregationRound { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str(match self { + Self::BasicCircuits => "basic_circuits", + Self::LeafAggregation => "leaf_aggregation", + Self::NodeAggregation => "node_aggregation", + Self::Scheduler => "scheduler", + }) + } +} + impl FromStr for AggregationRound { type Err = String; diff --git a/core/lib/zksync_core/src/house_keeper/fri_proof_compressor_queue_monitor.rs b/core/lib/zksync_core/src/house_keeper/fri_proof_compressor_queue_monitor.rs index 7a86dcf905f0..769792b6a584 100644 --- a/core/lib/zksync_core/src/house_keeper/fri_proof_compressor_queue_monitor.rs +++ b/core/lib/zksync_core/src/house_keeper/fri_proof_compressor_queue_monitor.rs @@ -58,6 +58,26 @@ impl PeriodicJob for FriProofCompressorStatsReporter { stats.in_progress as f64, "type" => "in_progress" ); + + let oldest_not_compressed_batch = self + .pool + .access_storage() + .await + .unwrap() + .fri_proof_compressor_dal() + .get_oldest_not_compressed_batch() + .await; + + if let Some(l1_batch_number) = oldest_not_compressed_batch { + metrics::gauge!( + format!( + "prover_fri.{}.oldest_not_compressed_batch", + PROOF_COMPRESSOR_SERVICE_NAME + ), + l1_batch_number.0 as f64 + ); + } + Ok(()) } diff --git a/core/lib/zksync_core/src/house_keeper/fri_prover_queue_monitor.rs b/core/lib/zksync_core/src/house_keeper/fri_prover_queue_monitor.rs index 9d3264b679ec..129f9befbd57 100644 --- a/core/lib/zksync_core/src/house_keeper/fri_prover_queue_monitor.rs +++ b/core/lib/zksync_core/src/house_keeper/fri_prover_queue_monitor.rs @@ -82,6 +82,45 @@ impl PeriodicJob for FriProverStatsReporter { "circuit_id" => circuit_id.to_string(), "aggregation_round" => aggregation_round.to_string()); } + + // FIXME: refactor metrics here + + if let Some(l1_batch_number) = conn + .proof_generation_dal() + .get_oldest_unprocessed_batch() + .await + { + metrics::gauge!( + "fri_prover.oldest_unprocessed_batch", + l1_batch_number.0 as f64 + ) + } + + if let Some(l1_batch_number) = conn + .proof_generation_dal() + .get_oldest_not_generated_batch() + .await + { + metrics::gauge!( + "fri_prover.oldest_not_generated_batch", + l1_batch_number.0 as f64 + ) + } + + for aggregation_round in 0..2 { + if let Some(l1_batch_number) = conn + .fri_prover_jobs_dal() + .min_unproved_l1_batch_number_for_aggregation_round(aggregation_round.into()) + .await + { + metrics::gauge!( + "fri_prover.oldest_unprocessed_block_by_round", + l1_batch_number.0 as f64, + "aggregation_round" => aggregation_round.to_string() + ) + } + } + Ok(()) } diff --git a/core/lib/zksync_core/src/witness_generator/mod.rs b/core/lib/zksync_core/src/witness_generator/mod.rs index 5dcebadf6a81..18b238660568 100644 --- a/core/lib/zksync_core/src/witness_generator/mod.rs +++ b/core/lib/zksync_core/src/witness_generator/mod.rs @@ -66,12 +66,7 @@ impl From for StageLabel { impl fmt::Display for StageLabel { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - formatter.write_str(match self.0 { - AggregationRound::BasicCircuits => "basic_circuits", - AggregationRound::LeafAggregation => "leaf_aggregation", - AggregationRound::NodeAggregation => "node_aggregation", - AggregationRound::Scheduler => "scheduler", - }) + self.0.fmt(formatter) } } diff --git a/prover/Cargo.lock b/prover/Cargo.lock index d27b787084f1..823b426d4cc0 100644 --- a/prover/Cargo.lock +++ b/prover/Cargo.lock @@ -7035,13 +7035,13 @@ dependencies = [ "ctrlc", "futures 0.3.29", "local-ip-address", - "metrics", "prometheus_exporter", "prover-service", "structopt", "thiserror", "tokio", "tracing", + "vise", "vlog", "zkevm_test_harness 1.3.3", "zksync_config", @@ -7279,11 +7279,11 @@ dependencies = [ "bincode", "ctrlc", "futures 0.3.29", - "metrics", "prometheus_exporter", "structopt", "tokio", "tracing", + "vise", "vk_setup_data_generator_server_fri", "vlog", "zkevm_test_harness 1.4.0", @@ -7345,7 +7345,6 @@ dependencies = [ "futures 0.3.29", "hex", "local-ip-address", - "metrics", "prometheus_exporter", "prover-service", "queues", @@ -7356,6 +7355,7 @@ dependencies = [ "thiserror", "tokio", "tracing", + "vise", "vlog", "zkevm_test_harness 1.3.3", "zksync_circuit_breaker", @@ -7380,12 +7380,12 @@ dependencies = [ "ctrlc", "futures 0.3.29", "local-ip-address", - "metrics", "prometheus_exporter", "serde", "shivini", "tokio", "tracing", + "vise", "vk_setup_data_generator_server_fri", "vlog", "zkevm_test_harness 1.4.0", @@ -7410,12 +7410,12 @@ dependencies = [ "ctrlc", "futures 0.3.29", "log", - "metrics", "prometheus_exporter", "reqwest", "serde", "tokio", "tracing", + "vise", "vlog", "zksync_config", "zksync_dal", @@ -7439,9 +7439,9 @@ dependencies = [ name = "zksync_prover_fri_utils" version = "0.1.0" dependencies = [ - "metrics", "serde", "tracing", + "vise", "zksync_config", "zksync_dal", "zksync_object_store", @@ -7610,7 +7610,6 @@ dependencies = [ "ctrlc", "futures 0.3.29", "hex", - "metrics", "multivm", "prometheus_exporter", "rand 0.8.5", @@ -7619,6 +7618,7 @@ dependencies = [ "structopt", "tokio", "tracing", + "vise", "vk_setup_data_generator_server_fri", "vlog", "zk_evm 1.4.0", @@ -7646,13 +7646,13 @@ dependencies = [ "bincode", "ctrlc", "futures 0.3.29", - "metrics", "prometheus_exporter", "queues", "serde", "structopt", "tokio", "tracing", + "vise", "vk_setup_data_generator_server_fri", "vlog", "zksync_config", diff --git a/prover/circuit_synthesizer/Cargo.toml b/prover/circuit_synthesizer/Cargo.toml index d8de49fb7655..c6f70bd5c3b8 100644 --- a/prover/circuit_synthesizer/Cargo.toml +++ b/prover/circuit_synthesizer/Cargo.toml @@ -8,6 +8,8 @@ name = "zksync_circuit_synthesizer" path = "src/main.rs" [dependencies] +vise = { git = "https://github.com/matter-labs/vise.git", version = "0.1.0", rev = "dd05139b76ab0843443ab3ff730174942c825dae" } + zksync_dal = { path = "../../core/lib/dal" } zksync_types = { path = "../../core/lib/types" } zksync_queued_job_processor = { path = "../../core/lib/queued_job_processor" } @@ -34,5 +36,4 @@ tokio = { version = "1.23.0", features = ["full"] } futures = "0.3" ctrlc = { version = "3.1", features = ["termination"] } local-ip-address = "0.5.0" -metrics = "0.21" tracing = "0.1" diff --git a/prover/circuit_synthesizer/src/circuit_synthesizer.rs b/prover/circuit_synthesizer/src/circuit_synthesizer.rs index 1d68138cc608..55da03949a7e 100644 --- a/prover/circuit_synthesizer/src/circuit_synthesizer.rs +++ b/prover/circuit_synthesizer/src/circuit_synthesizer.rs @@ -13,6 +13,7 @@ use zkevm_test_harness::bellman::plonk::better_better_cs::cs::Circuit; use zkevm_test_harness::pairing::bn256::Bn256; use zkevm_test_harness::witness::oracle::VmWitnessOracle; +use crate::metrics::METRICS; use zksync_config::configs::prover_group::ProverGroupConfig; use zksync_config::configs::CircuitSynthesizerConfig; use zksync_config::ProverConfigs; @@ -116,11 +117,7 @@ impl CircuitSynthesizer { "Finished circuit synthesis for circuit: {circuit_type} took {:?}", start_instant.elapsed() ); - metrics::histogram!( - "server.circuit_synthesizer.synthesize", - start_instant.elapsed(), - "circuit_type" => circuit_type, - ); + METRICS.synthesize[&circuit_type].observe(start_instant.elapsed()); // we don't perform assembly finalization here since it increases the assembly size significantly due to padding. Ok((assembly, circuit.numeric_circuit_type())) @@ -302,11 +299,8 @@ async fn handle_send_result( "Sent assembly of size: {blob_size_in_gb}GB successfully, took: {elapsed:?} \ for job: {job_id} by: {local_ip:?} to: {address:?}" ); - metrics::histogram!( - "server.circuit_synthesizer.blob_sending_time", - *elapsed, - "blob_size_in_gb" => blob_size_in_gb.to_string(), - ); + + METRICS.blob_sending_time[&blob_size_in_gb].observe(*elapsed); // endregion diff --git a/prover/circuit_synthesizer/src/main.rs b/prover/circuit_synthesizer/src/main.rs index 5789854e97a8..5592885dcdd9 100644 --- a/prover/circuit_synthesizer/src/main.rs +++ b/prover/circuit_synthesizer/src/main.rs @@ -16,6 +16,7 @@ use zksync_verification_key_server::get_cached_commitments; use crate::circuit_synthesizer::CircuitSynthesizer; mod circuit_synthesizer; +mod metrics; #[derive(Debug, StructOpt)] #[structopt(name = "TODO", about = "TODO")] diff --git a/prover/circuit_synthesizer/src/metrics.rs b/prover/circuit_synthesizer/src/metrics.rs new file mode 100644 index 000000000000..b9ee5b10c159 --- /dev/null +++ b/prover/circuit_synthesizer/src/metrics.rs @@ -0,0 +1,14 @@ +use std::time::Duration; +use vise::{Buckets, Histogram, LabeledFamily, Metrics}; + +#[derive(Debug, Metrics)] +#[metrics(prefix = "prover_circuit_synthesizer")] +pub(crate) struct CircuitSynthesizerMetrics { + #[metrics(buckets = Buckets::LATENCIES, labels = ["blob_size_in_gb"])] + pub blob_sending_time: LabeledFamily>, + #[metrics(buckets = Buckets::LATENCIES, labels = ["circuit_type"])] + pub synthesize: LabeledFamily<&'static str, Histogram>, +} + +#[vise::register] +pub(crate) static METRICS: vise::Global = vise::Global::new(); diff --git a/prover/proof_fri_compressor/Cargo.toml b/prover/proof_fri_compressor/Cargo.toml index 45ffe756e196..80ee37898068 100644 --- a/prover/proof_fri_compressor/Cargo.toml +++ b/prover/proof_fri_compressor/Cargo.toml @@ -6,6 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +vise = { git = "https://github.com/matter-labs/vise.git", version = "0.1.0", rev = "dd05139b76ab0843443ab3ff730174942c825dae" } + zksync_types = { path = "../../core/lib/types" } zksync_dal = { path = "../../core/lib/dal" } zksync_config = { path = "../../core/lib/config" } @@ -27,6 +29,5 @@ structopt = "0.3.26" tokio = { version = "1", features = ["time"] } futures = { version = "0.3", features = ["compat"] } ctrlc = { version = "3.1", features = ["termination"] } -metrics = "0.21" async-trait = "0.1" bincode = "1.0" diff --git a/prover/proof_fri_compressor/src/compressor.rs b/prover/proof_fri_compressor/src/compressor.rs index 9da71c83eac4..f0f8efc6102c 100644 --- a/prover/proof_fri_compressor/src/compressor.rs +++ b/prover/proof_fri_compressor/src/compressor.rs @@ -3,6 +3,7 @@ use async_trait::async_trait; use std::time::Instant; use tokio::task::JoinHandle; +use crate::metrics::METRICS; use zkevm_test_harness::proof_wrapper_utils::{wrap_proof, WrapperConfig}; use zksync_dal::ConnectionPool; use zksync_object_store::ObjectStore; @@ -124,13 +125,13 @@ impl JobProcessor for ProofCompressor { "Started proof compression for L1 batch: {:?}", l1_batch_number ); - let started_at = Instant::now(); + let observer = METRICS.blob_fetch_time.start(); + let fri_proof: FriProofWrapper = self.blob_store.get(fri_proof_id) .await.with_context(|| format!("Failed to get fri proof from blob store for {l1_batch_number} with id {fri_proof_id}"))?; - metrics::histogram!( - "prover_fri.proof_fri_compressor.blob_fetch_time", - started_at.elapsed(), - ); + + observer.observe(); + let scheduler_proof = match fri_proof { FriProofWrapper::Base(_) => anyhow::bail!("Must be a scheduler proof not base layer"), FriProofWrapper::Recursive(proof) => proof, @@ -166,10 +167,7 @@ impl JobProcessor for ProofCompressor { started_at: Instant, artifacts: Proof>>, ) -> anyhow::Result<()> { - metrics::histogram!( - "prover_fri.proof_fri_compressor.compression_time", - started_at.elapsed(), - ); + METRICS.compression_time.observe(started_at.elapsed()); tracing::info!( "Finished fri proof compression for job: {job_id} took: {:?}", started_at.elapsed() @@ -192,10 +190,10 @@ impl JobProcessor for ProofCompressor { .put(job_id, &l1_batch_proof) .await .context("Failed to save converted l1_batch_proof")?; - metrics::histogram!( - "prover_fri.proof_fri_compressor.blob_save_time", - blob_save_started_at.elapsed(), - ); + METRICS + .blob_save_time + .observe(blob_save_started_at.elapsed()); + self.pool .access_storage() .await diff --git a/prover/proof_fri_compressor/src/main.rs b/prover/proof_fri_compressor/src/main.rs index b5b5ba672c5b..c8396803339f 100644 --- a/prover/proof_fri_compressor/src/main.rs +++ b/prover/proof_fri_compressor/src/main.rs @@ -16,6 +16,7 @@ use zksync_utils::wait_for_tasks::wait_for_tasks; use crate::compressor::ProofCompressor; mod compressor; +mod metrics; #[derive(Debug, StructOpt)] #[structopt( diff --git a/prover/proof_fri_compressor/src/metrics.rs b/prover/proof_fri_compressor/src/metrics.rs new file mode 100644 index 000000000000..5891da2f4166 --- /dev/null +++ b/prover/proof_fri_compressor/src/metrics.rs @@ -0,0 +1,16 @@ +use std::time::Duration; +use vise::{Buckets, Histogram, Metrics}; + +#[derive(Debug, Metrics)] +#[metrics(prefix = "prover_fri_proof_fri_compressor")] +pub(crate) struct ProofFriCompressorMetrics { + #[metrics(buckets = Buckets::LATENCIES)] + pub blob_fetch_time: Histogram, + #[metrics(buckets = Buckets::LATENCIES)] + pub compression_time: Histogram, + #[metrics(buckets = Buckets::LATENCIES)] + pub blob_save_time: Histogram, +} + +#[vise::register] +pub(crate) static METRICS: vise::Global = vise::Global::new(); diff --git a/prover/prover/Cargo.toml b/prover/prover/Cargo.toml index d0b66f3e3f7f..03adce72daf0 100644 --- a/prover/prover/Cargo.toml +++ b/prover/prover/Cargo.toml @@ -11,6 +11,8 @@ categories = ["cryptography"] publish = false # We don't want to publish our binaries. [dependencies] +vise = { git = "https://github.com/matter-labs/vise.git", version = "0.1.0", rev = "dd05139b76ab0843443ab3ff730174942c825dae" } + zksync_dal = { path = "../../core/lib/dal" } zksync_config = { path = "../../core/lib/config" } zksync_env_config = { path = "../../core/lib/env_config" } @@ -43,7 +45,6 @@ thiserror = "1.0" chrono = "0.4" serde_json = "1.0" ethabi = "18.0.0" -metrics = "0.21" hex = "0.4" serde = { version = "1.0", features = ["derive"] } bincode = "1.3.2" diff --git a/prover/prover/src/main.rs b/prover/prover/src/main.rs index 2c03c2f070d2..56ac77336c24 100644 --- a/prover/prover/src/main.rs +++ b/prover/prover/src/main.rs @@ -2,6 +2,7 @@ #[cfg(feature = "gpu")] mod artifact_provider; +mod metrics; #[cfg(feature = "gpu")] mod prover; #[cfg(feature = "gpu")] diff --git a/prover/prover/src/metrics.rs b/prover/prover/src/metrics.rs new file mode 100644 index 000000000000..4544ae9bfa7c --- /dev/null +++ b/prover/prover/src/metrics.rs @@ -0,0 +1,40 @@ +use std::time::Duration; +use vise::{Buckets, Counter, Histogram, LabeledFamily, Metrics}; + +const PROVER_LATENCY_BUCKETS: Buckets = Buckets::values(&[ + 1.0, 10.0, 20.0, 40.0, 60.0, 120.0, 240.0, 360.0, 600.0, 1800.0, 3600.0, +]); + +#[derive(Debug, Metrics)] +#[metrics(prefix = "prover")] +pub(crate) struct ProverMetrics { + #[metrics(buckets = PROVER_LATENCY_BUCKETS, labels = ["circuit_type"])] + pub proof_generation_time: LabeledFamily>, + #[metrics(buckets = PROVER_LATENCY_BUCKETS, labels = ["circuit_type"])] + pub circuit_synthesis_time: LabeledFamily>, + #[metrics(buckets = PROVER_LATENCY_BUCKETS, labels = ["circuit_type"])] + pub assembly_finalize_time: LabeledFamily>, + #[metrics(buckets = PROVER_LATENCY_BUCKETS, labels = ["circuit_type"])] + pub assembly_encoding_time: LabeledFamily>, + #[metrics(buckets = PROVER_LATENCY_BUCKETS, labels = ["circuit_type"])] + pub assembly_decoding_time: LabeledFamily>, + #[metrics(buckets = PROVER_LATENCY_BUCKETS, labels = ["circuit_type"])] + pub assembly_transferring_time: LabeledFamily>, + #[metrics(buckets = PROVER_LATENCY_BUCKETS, labels = ["circuit_type"])] + pub setup_load_time: LabeledFamily>, + #[metrics(labels = ["circuit_type"])] + pub setup_loading_cache_miss: LabeledFamily, + #[metrics(buckets = PROVER_LATENCY_BUCKETS)] + pub prover_wait_idle_time: Histogram, + #[metrics(buckets = PROVER_LATENCY_BUCKETS)] + pub setup_load_wait_idle_time: Histogram, + #[metrics(buckets = PROVER_LATENCY_BUCKETS)] + pub scheduler_wait_idle_time: Histogram, + #[metrics(buckets = PROVER_LATENCY_BUCKETS)] + pub download_time: Histogram, + #[metrics(buckets = PROVER_LATENCY_BUCKETS, labels = ["queue_capacity"])] + pub queue_free_slots: LabeledFamily>, +} + +#[vise::register] +pub(crate) static METRICS: vise::Global = vise::Global::new(); diff --git a/prover/prover/src/prover.rs b/prover/prover/src/prover.rs index 25e46938a1d9..1885d8153329 100644 --- a/prover/prover/src/prover.rs +++ b/prover/prover/src/prover.rs @@ -9,6 +9,7 @@ use tokio::runtime::Handle; use zkevm_test_harness::abstract_zksync_circuit::concrete_circuits::ZkSyncProof; use zkevm_test_harness::pairing::bn256::Bn256; +use crate::metrics::METRICS; use zksync_config::{PostgresConfig, ProverConfig}; use zksync_dal::ConnectionPool; use zksync_dal::StorageProcessor; @@ -64,11 +65,9 @@ impl ProverReporter { serialized.len() >> 10, duration, ); - metrics::histogram!( - "server.prover.proof_generation_time", - duration, - "circuit_type" => circuit_type, - ); + + METRICS.proof_generation_time[&circuit_type].observe(duration); + let job_id = job_id as u32; self.rt_handle.block_on(async { let mut connection = self.pool.access_storage().await.unwrap(); @@ -187,11 +186,7 @@ impl JobReporter for ProverReporter { circuit_type, duration, ); - metrics::histogram!( - "server.prover.circuit_synthesis_time", - duration, - "circuit_type" => circuit_type, - ); + METRICS.circuit_synthesis_time[&circuit_type].observe(duration); } JobResult::AssemblyFinalized(job_id, duration) => { @@ -202,11 +197,7 @@ impl JobReporter for ProverReporter { circuit_type, duration, ); - metrics::histogram!( - "server.prover.assembly_finalize_time", - duration, - "circuit_type" => circuit_type, - ); + METRICS.assembly_finalize_time[&circuit_type].observe(duration); } JobResult::SetupLoaded(job_id, duration, cache_miss) => { @@ -219,16 +210,8 @@ impl JobReporter for ProverReporter { duration, cache_miss ); - metrics::histogram!( - "server.prover.setup_load_time", - duration, - "circuit_type" => circuit_type.clone() - ); - metrics::counter!( - "server.prover.setup_loading_cache_miss", - 1, - "circuit_type" => circuit_type - ); + METRICS.setup_load_time[&circuit_type].observe(duration); + METRICS.setup_loading_cache_miss[&circuit_type].inc(); } JobResult::AssemblyEncoded(job_id, duration) => { @@ -239,11 +222,7 @@ impl JobReporter for ProverReporter { circuit_type, duration, ); - metrics::histogram!( - "server.prover.assembly_encoding_time", - duration, - "circuit_type" => circuit_type, - ); + METRICS.assembly_encoding_time[&circuit_type].observe(duration); } JobResult::AssemblyDecoded(job_id, duration) => { @@ -254,11 +233,7 @@ impl JobReporter for ProverReporter { circuit_type, duration, ); - metrics::histogram!( - "server.prover.assembly_decoding_time", - duration, - "circuit_type" => circuit_type, - ); + METRICS.assembly_decoding_time[&circuit_type].observe(duration); } JobResult::FailureWithDebugging(job_id, circuit_id, assembly, error) => { @@ -285,11 +260,7 @@ impl JobReporter for ProverReporter { circuit_type, duration, ); - metrics::histogram!( - "server.prover.assembly_transferring_time", - duration, - "circuit_type" => circuit_type, - ); + METRICS.assembly_transferring_time[&circuit_type].observe(duration); } JobResult::ProverWaitedIdle(prover_id, duration) => { @@ -298,17 +269,17 @@ impl JobReporter for ProverReporter { duration, prover_id ); - metrics::histogram!("server.prover.prover_wait_idle_time", duration,); + METRICS.prover_wait_idle_time.observe(duration); } JobResult::SetupLoaderWaitedIdle(duration) => { tracing::trace!("Setup load wait idle time: {:?}", duration); - metrics::histogram!("server.prover.setup_load_wait_wait_idle_time", duration,); + METRICS.setup_load_wait_idle_time.observe(duration); } JobResult::SchedulerWaitedIdle(duration) => { tracing::trace!("Scheduler wait idle time: {:?}", duration); - metrics::histogram!("server.prover.scheduler_wait_idle_time", duration,); + METRICS.scheduler_wait_idle_time.observe(duration); } } } diff --git a/prover/prover/src/run.rs b/prover/prover/src/run.rs index d54e8a873a78..9342cd554e62 100644 --- a/prover/prover/src/run.rs +++ b/prover/prover/src/run.rs @@ -20,6 +20,7 @@ use zksync_types::proofs::{GpuProverInstanceStatus, SocketAddress}; use zksync_utils::wait_for_tasks::wait_for_tasks; use crate::artifact_provider::ProverArtifactProvider; +use crate::metrics::METRICS; use crate::prover::ProverReporter; use crate::prover_params::ProverParams; use crate::socket_listener::incoming_socket_listener; @@ -148,8 +149,7 @@ pub async fn run() -> anyhow::Result<()> { &prover_config.initial_setup_key_path, &prover_config.key_download_url, ); - metrics::histogram!("server.prover.download_time", started_at.elapsed()); - + METRICS.download_time.observe(started_at.elapsed()); env::set_var("CRS_FILE", prover_config.initial_setup_key_path.clone()); // We don't have a graceful shutdown process for the prover, so `_stop_sender` is unused. // Though we still need to create a channel because circuit breaker expects `stop_receiver`. diff --git a/prover/prover/src/synthesized_circuit_provider.rs b/prover/prover/src/synthesized_circuit_provider.rs index c5016810a0c1..3c6939dc6aa3 100644 --- a/prover/prover/src/synthesized_circuit_provider.rs +++ b/prover/prover/src/synthesized_circuit_provider.rs @@ -6,6 +6,7 @@ use tokio::sync::Mutex; use prover_service::RemoteSynthesizer; use queues::{Buffer, IsQueue}; +use crate::metrics::METRICS; use tokio::runtime::Handle; use zksync_dal::ConnectionPool; use zksync_types::proofs::SocketAddress; @@ -69,11 +70,9 @@ impl RemoteSynthesizer for SynthesizedCircuitProvider { queue_free_slots, assembly_queue.capacity() ); - metrics::histogram!( - "server.prover.queue_free_slots", - queue_free_slots as f64, - "queue_capacity" => assembly_queue.capacity().to_string() - ); + METRICS.queue_free_slots[&assembly_queue.capacity().to_string()] + .observe(queue_free_slots); + Some(Box::new(Cursor::new(blob))) } Err(_) => None, diff --git a/prover/prover_fri/Cargo.toml b/prover/prover_fri/Cargo.toml index 314f034a9a63..1d641311c225 100644 --- a/prover/prover_fri/Cargo.toml +++ b/prover/prover_fri/Cargo.toml @@ -6,6 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +vise = { git = "https://github.com/matter-labs/vise.git", version = "0.1.0", rev = "dd05139b76ab0843443ab3ff730174942c825dae" } + zksync_types = { path = "../../core/lib/types" } zksync_dal = { path = "../../core/lib/dal" } zksync_config = { path = "../../core/lib/config" } @@ -34,7 +36,6 @@ tracing = "0.1" tokio = { version = "1", features = ["time"] } futures = { version = "0.3", features = ["compat"] } ctrlc = { version = "3.1", features = ["termination"] } -metrics = "0.21" serde = { version = "1.0", features = ["derive"] } async-trait = "0.1" local-ip-address = "0.5.0" diff --git a/prover/prover_fri/src/gpu_prover_job_processor.rs b/prover/prover_fri/src/gpu_prover_job_processor.rs index ec022d419d49..9d7eda1202a7 100644 --- a/prover/prover_fri/src/gpu_prover_job_processor.rs +++ b/prover/prover_fri/src/gpu_prover_job_processor.rs @@ -16,6 +16,7 @@ pub mod gpu_prover { use zksync_prover_fri_types::circuit_definitions::circuit_definitions::recursion_layer::ZkSyncRecursionLayerProof; use zksync_prover_fri_types::WitnessVectorArtifacts; + use crate::metrics::METRICS; use zksync_config::configs::fri_prover_group::FriProverGroupConfig; use zksync_config::configs::FriProverConfig; use zksync_dal::ConnectionPool; @@ -102,11 +103,10 @@ pub mod gpu_prover { let artifact: GoldilocksGpuProverSetupData = get_setup_data_for_circuit_type(key.clone()) .context("get_setup_data_for_circuit_type()")?; - metrics::histogram!( - "prover_fri.prover.gpu_setup_data_load_time", - started_at.elapsed(), - "circuit_type" => key.circuit_id.to_string(), - ); + + METRICS.gpu_setup_data_load_time[&key.circuit_id.to_string()] + .observe(started_at.elapsed()); + Arc::new(artifact) } }) @@ -161,11 +161,9 @@ pub mod gpu_prover { prover_job.job_id, started_at.elapsed() ); - metrics::histogram!( - "prover_fri.prover.gpu_proof_generation_time", - started_at.elapsed(), - "circuit_type" => circuit_id.to_string() - ); + METRICS.gpu_proof_generation_time[&circuit_id.to_string()] + .observe(started_at.elapsed()); + let proof = proof.into(); verify_proof( &prover_job.circuit_wrapper, @@ -258,10 +256,8 @@ pub mod gpu_prover { started_at: Instant, artifacts: Self::JobArtifacts, ) -> anyhow::Result<()> { - metrics::histogram!( - "prover_fri.prover.gpu_total_proving_time", - started_at.elapsed(), - ); + METRICS.gpu_total_proving_time.observe(started_at.elapsed()); + let mut storage_processor = self.prover_connection_pool.access_storage().await.unwrap(); save_proof( job_id, diff --git a/prover/prover_fri/src/lib.rs b/prover/prover_fri/src/lib.rs index 5fdb260d40d6..8d57083ebd36 100644 --- a/prover/prover_fri/src/lib.rs +++ b/prover/prover_fri/src/lib.rs @@ -1,3 +1,4 @@ #![feature(generic_const_exprs)] +mod metrics; pub mod prover_job_processor; pub mod utils; diff --git a/prover/prover_fri/src/main.rs b/prover/prover_fri/src/main.rs index 14b54350946c..ab0994a36489 100644 --- a/prover/prover_fri/src/main.rs +++ b/prover/prover_fri/src/main.rs @@ -29,6 +29,8 @@ mod prover_job_processor; mod socket_listener; mod utils; +mod metrics; + async fn graceful_shutdown(port: u16) -> anyhow::Result> { let postgres_config = PostgresConfig::from_env().context("PostgresConfig::from_env()")?; let pool = ConnectionPool::singleton(postgres_config.prover_url()?) diff --git a/prover/prover_fri/src/metrics.rs b/prover/prover_fri/src/metrics.rs new file mode 100644 index 000000000000..27ddce54d6c1 --- /dev/null +++ b/prover/prover_fri/src/metrics.rs @@ -0,0 +1,43 @@ +use std::time::Duration; +use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, LabeledFamily, Metrics}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelSet)] +pub(crate) struct CircuitLabels { + pub circuit_type: u8, + pub layer: Layer, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelValue)] +#[metrics(rename_all = "snake_case")] +pub(crate) enum Layer { + Recursive, + Base, +} + +#[derive(Debug, Metrics)] +#[metrics(prefix = "prover_fri_prover")] +pub(crate) struct ProverFriMetrics { + #[metrics(buckets = Buckets::LATENCIES, labels = ["circuit_type"])] + pub gpu_setup_data_load_time: LabeledFamily>, + #[metrics(buckets = Buckets::LATENCIES, labels = ["circuit_type"])] + pub gpu_proof_generation_time: LabeledFamily>, + #[metrics(buckets = Buckets::LATENCIES)] + pub gpu_total_proving_time: Histogram, + #[metrics(buckets = Buckets::LATENCIES, labels = ["circuit_type"])] + pub setup_data_load_time: LabeledFamily>, + #[metrics(buckets = Buckets::LATENCIES)] + pub proof_generation_time: Family>, + #[metrics(buckets = Buckets::LATENCIES, labels = ["circuit_type"])] + pub proof_verification_time: LabeledFamily>, + #[metrics(buckets = Buckets::LATENCIES)] + pub cpu_total_proving_time: Histogram, + #[metrics(buckets = Buckets::LATENCIES, labels = ["blob_size_in_gb"])] + pub witness_vector_blob_time: LabeledFamily>, + #[metrics(buckets = Buckets::LATENCIES, labels = ["circuit_type"])] + pub gpu_assembly_generation_time: LabeledFamily>, + #[metrics(buckets = Buckets::LATENCIES, labels = ["circuit_type"])] + pub blob_save_time: LabeledFamily>, +} + +#[vise::register] +pub(crate) static METRICS: vise::Global = vise::Global::new(); diff --git a/prover/prover_fri/src/prover_job_processor.rs b/prover/prover_fri/src/prover_job_processor.rs index d540771fd14f..30beae5bc82d 100644 --- a/prover/prover_fri/src/prover_job_processor.rs +++ b/prover/prover_fri/src/prover_job_processor.rs @@ -19,6 +19,7 @@ use zksync_prover_fri_types::circuit_definitions::{ use zkevm_test_harness::prover_utils::{prove_base_layer_circuit, prove_recursion_layer_circuit}; +use crate::metrics::{CircuitLabels, Layer, METRICS}; use zksync_config::configs::fri_prover_group::FriProverGroupConfig; use zksync_config::configs::FriProverConfig; use zksync_dal::ConnectionPool; @@ -90,11 +91,9 @@ impl Prover { let artifact: GoldilocksProverSetupData = get_cpu_setup_data_for_circuit_type(key.clone()) .context("get_cpu_setup_data_for_circuit_type()")?; - metrics::histogram!( - "prover_fri.prover.setup_data_load_time", - started_at.elapsed(), - "circuit_type" => key.circuit_id.to_string(), - ); + METRICS.gpu_setup_data_load_time[&key.circuit_id.to_string()] + .observe(started_at.elapsed()); + Arc::new(artifact) } }) @@ -137,12 +136,13 @@ impl Prover { &artifact.wits_hint, &artifact.finalization_hint, ); - metrics::histogram!( - "prover_fri.prover.proof_generation_time", - started_at.elapsed(), - "circuit_type" => circuit_id.to_string(), - "layer" => "recursive", - ); + + let label = CircuitLabels { + circuit_type: circuit_id, + layer: Layer::Recursive, + }; + METRICS.proof_generation_time[&label].observe(started_at.elapsed()); + verify_proof( &CircuitWrapper::Recursive(circuit), &proof, @@ -177,12 +177,13 @@ impl Prover { &artifact.wits_hint, &artifact.finalization_hint, ); - metrics::histogram!( - "prover_fri.prover.proof_generation_time", - started_at.elapsed(), - "circuit_type" => circuit_id.to_string(), - "layer" => "base", - ); + + let label = CircuitLabels { + circuit_type: circuit_id, + layer: Layer::Base, + }; + METRICS.proof_generation_time[&label].observe(started_at.elapsed()); + verify_proof(&CircuitWrapper::Base(circuit), &proof, &artifact.vk, job_id); FriProofWrapper::Base(ZkSyncBaseLayerProof::from_inner(circuit_id, proof)) } @@ -242,10 +243,8 @@ impl JobProcessor for Prover { started_at: Instant, artifacts: Self::JobArtifacts, ) -> anyhow::Result<()> { - metrics::histogram!( - "prover_fri.prover.cpu_total_proving_time", - started_at.elapsed(), - ); + METRICS.cpu_total_proving_time.observe(started_at.elapsed()); + let mut storage_processor = self.prover_connection_pool.access_storage().await.unwrap(); save_proof( job_id, diff --git a/prover/prover_fri/src/socket_listener.rs b/prover/prover_fri/src/socket_listener.rs index 45c5518a1c64..e9ecbd1e60b0 100644 --- a/prover/prover_fri/src/socket_listener.rs +++ b/prover/prover_fri/src/socket_listener.rs @@ -12,6 +12,7 @@ pub mod gpu_socket_listener { get_finalization_hints, get_round_for_recursive_circuit_type, }; + use crate::metrics::METRICS; use crate::utils::{GpuProverJob, ProvingAssembly, SharedWitnessVectorQueue}; use anyhow::Context as _; use tokio::sync::watch; @@ -113,11 +114,10 @@ pub mod gpu_socket_listener { file_size_in_gb, started_at.elapsed().as_secs() ); - metrics::histogram!( - "prover_fri.prover_fri.witness_vector_blob_time", - started_at.elapsed(), - "blob_size_in_gb" => file_size_in_gb.to_string(), - ); + + METRICS.witness_vector_blob_time[&(file_size_in_gb as u64)] + .observe(started_at.elapsed()); + let witness_vector = bincode::deserialize::(&assembly) .context("Failed deserializing witness vector")?; let assembly = generate_assembly_for_repeated_proving( @@ -185,11 +185,9 @@ pub mod gpu_socket_listener { job_id, started_at.elapsed() ); - metrics::histogram!( - "prover_fri.prover.gpu_assembly_generation_time", - started_at.elapsed(), - "circuit_type" => circuit_id.to_string() - ); + + METRICS.gpu_assembly_generation_time[&circuit_id.to_string()].observe(started_at.elapsed()); + Ok(cs) } } diff --git a/prover/prover_fri/src/utils.rs b/prover/prover_fri/src/utils.rs index 5b0f8be04a47..c67ee9149f11 100644 --- a/prover/prover_fri/src/utils.rs +++ b/prover/prover_fri/src/utils.rs @@ -26,6 +26,7 @@ use zksync_prover_fri_types::{ }; use zksync_prover_fri_utils::get_base_layer_circuit_id_for_recursive_layer; +use crate::metrics::METRICS; use zksync_types::{basic_fri_types::CircuitIdRoundTuple, proofs::AggregationRound, L1BatchNumber}; pub type F = GoldilocksField; @@ -94,11 +95,8 @@ pub async fn save_proof( let blob_save_started_at = Instant::now(); let blob_url = blob_store.put(job_id, &proof).await.unwrap(); - metrics::histogram!( - "prover_fri.prover.blob_save_time", - blob_save_started_at.elapsed(), - "circuit_type" => circuit_type.to_string(), - ); + + METRICS.blob_save_time[&circuit_type.to_string()].observe(blob_save_started_at.elapsed()); let mut transaction = storage_processor.start_transaction().await.unwrap(); let job_metadata = transaction @@ -141,11 +139,9 @@ pub fn verify_proof( recursive_circuit.numeric_circuit_type(), ), }; - metrics::histogram!( - "prover_fri.prover.proof_verification_time", - started_at.elapsed(), - "circuit_type" => circuit_id.to_string(), - ); + + METRICS.proof_verification_time[&circuit_id.to_string()].observe(started_at.elapsed()); + if !is_valid { let msg = format!( "Failed to verify base layer proof for job-id: {job_id} circuit_type {circuit_id}" diff --git a/prover/prover_fri_gateway/Cargo.toml b/prover/prover_fri_gateway/Cargo.toml index 24e0a97bae73..198d32e55504 100644 --- a/prover/prover_fri_gateway/Cargo.toml +++ b/prover/prover_fri_gateway/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] +vise = { git = "https://github.com/matter-labs/vise.git", version = "0.1.0", rev = "dd05139b76ab0843443ab3ff730174942c825dae" } + zksync_types = { path = "../../core/lib/types" } zksync_dal = { path = "../../core/lib/dal" } zksync_config = { path = "../../core/lib/config" } @@ -21,5 +23,4 @@ ctrlc = { version = "3.1", features = ["termination"] } async-trait = "0.1" futures = { version = "0.3", features = ["compat"] } serde = { version = "1.0", features = ["derive"] } -metrics = "0.21" log = "0.4.20" diff --git a/prover/prover_fri_gateway/src/api_data_fetcher.rs b/prover/prover_fri_gateway/src/api_data_fetcher.rs index a009f1783f21..339a7bec9e6a 100644 --- a/prover/prover_fri_gateway/src/api_data_fetcher.rs +++ b/prover/prover_fri_gateway/src/api_data_fetcher.rs @@ -1,5 +1,6 @@ use std::time::Duration; +use crate::metrics::METRICS; use async_trait::async_trait; use reqwest::Client; use serde::{de::DeserializeOwned, Serialize}; @@ -70,7 +71,7 @@ impl PeriodicApiStruct { self.handle_response(job_id, response).await; } Err(err) => { - metrics::counter!("prover_fri.prover_fri_gateway.http_error", 1, "service_name" => Self::SERVICE_NAME); + METRICS.http_error[&Self::SERVICE_NAME].inc(); tracing::error!("HTTP request failed due to error: {}", err); } } diff --git a/prover/prover_fri_gateway/src/main.rs b/prover/prover_fri_gateway/src/main.rs index dd1570989c19..3a3f8b42ae0c 100644 --- a/prover/prover_fri_gateway/src/main.rs +++ b/prover/prover_fri_gateway/src/main.rs @@ -12,6 +12,7 @@ use zksync_types::prover_server_api::{ProofGenerationDataRequest, SubmitProofReq use zksync_utils::wait_for_tasks::wait_for_tasks; mod api_data_fetcher; +mod metrics; mod proof_gen_data_fetcher; mod proof_submitter; diff --git a/prover/prover_fri_gateway/src/metrics.rs b/prover/prover_fri_gateway/src/metrics.rs new file mode 100644 index 000000000000..34d10ef9a799 --- /dev/null +++ b/prover/prover_fri_gateway/src/metrics.rs @@ -0,0 +1,11 @@ +use vise::{Counter, LabeledFamily, Metrics}; + +#[derive(Debug, Metrics)] +#[metrics(prefix = "prover_fri_prover_fri_gateway")] +pub(crate) struct ProverFriGatewayMetrics { + #[metrics(labels = ["service_name"])] + pub http_error: LabeledFamily<&'static str, Counter>, +} + +#[vise::register] +pub(crate) static METRICS: vise::Global = vise::Global::new(); diff --git a/prover/prover_fri_utils/Cargo.toml b/prover/prover_fri_utils/Cargo.toml index fb2d729800ce..b216f6307a7b 100644 --- a/prover/prover_fri_utils/Cargo.toml +++ b/prover/prover_fri_utils/Cargo.toml @@ -6,6 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +vise = { git = "https://github.com/matter-labs/vise.git", version = "0.1.0", rev = "dd05139b76ab0843443ab3ff730174942c825dae" } + zksync_object_store = { path = "../../core/lib/object_store" } zksync_config = { path = "../../core/lib/config" } zksync_types = { path = "../../core/lib/types" } @@ -14,4 +16,3 @@ zksync_dal = { path = "../../core/lib/dal" } tracing = "0.1" serde = { version = "1.0", features = ["derive"] } -metrics = "0.21" diff --git a/prover/prover_fri_utils/src/lib.rs b/prover/prover_fri_utils/src/lib.rs index cbe6570d657c..f0edcd07902d 100644 --- a/prover/prover_fri_utils/src/lib.rs +++ b/prover/prover_fri_utils/src/lib.rs @@ -11,9 +11,11 @@ use zksync_prover_fri_types::{ get_current_pod_name, CircuitWrapper, ProverJob, ProverServiceDataKey, }; +use crate::metrics::{CircuitLabels, PROVER_FRI_UTILS_METRICS}; use zksync_types::proofs::AggregationRound; use zksync_types::protocol_version::L1VerifierConfig; +pub mod metrics; pub mod socket_utils; pub async fn fetch_next_circuit( @@ -61,12 +63,13 @@ pub async fn fetch_next_circuit( .get(circuit_key) .await .unwrap_or_else(|err| panic!("{err:?}")); - metrics::histogram!( - "prover_fri.prover.blob_fetch_time", - started_at.elapsed(), - "circuit_type" => prover_job.circuit_id.to_string(), - "aggregation_round" => format!("{:?}", prover_job.aggregation_round), - ); + + let label = CircuitLabels { + circuit_type: prover_job.circuit_id, + aggregation_round: prover_job.aggregation_round.into(), + }; + PROVER_FRI_UTILS_METRICS.blob_fetch_time[&label].observe(started_at.elapsed()); + let setup_data_key = ProverServiceDataKey { circuit_id: prover_job.circuit_id, round: prover_job.aggregation_round, diff --git a/prover/prover_fri_utils/src/metrics.rs b/prover/prover_fri_utils/src/metrics.rs new file mode 100644 index 000000000000..767e2c25fc17 --- /dev/null +++ b/prover/prover_fri_utils/src/metrics.rs @@ -0,0 +1,36 @@ +use std::time::Duration; +use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics}; +use zksync_types::proofs::AggregationRound; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelSet)] +pub struct CircuitLabels { + pub circuit_type: u8, + pub aggregation_round: StageLabel, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelValue, EncodeLabelSet)] +#[metrics(label = "stage", format = "wit_gen_{}")] +pub struct StageLabel(AggregationRound); + +impl From for StageLabel { + fn from(round: AggregationRound) -> Self { + Self(round) + } +} + +impl std::fmt::Display for StageLabel { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(formatter) + } +} + +#[derive(Debug, Metrics)] +#[metrics(prefix = "prover_fri_prover")] +pub(crate) struct ProverFriUtilsMetrics { + #[metrics(buckets = Buckets::LATENCIES)] + pub blob_fetch_time: Family>, +} + +#[vise::register] +pub(crate) static PROVER_FRI_UTILS_METRICS: vise::Global = + vise::Global::new(); diff --git a/prover/witness_generator/Cargo.toml b/prover/witness_generator/Cargo.toml index a8dce0547b23..0d28734a70f3 100644 --- a/prover/witness_generator/Cargo.toml +++ b/prover/witness_generator/Cargo.toml @@ -11,6 +11,8 @@ categories = ["cryptography"] publish = false # We don't want to publish our binaries. [dependencies] +vise = { git = "https://github.com/matter-labs/vise.git", version = "0.1.0", rev = "dd05139b76ab0843443ab3ff730174942c825dae" } + zksync_dal = { path = "../../core/lib/dal" } zksync_config = { path = "../../core/lib/config" } zksync_env_config = { path = "../../core/lib/env_config" } @@ -38,7 +40,6 @@ anyhow = "1.0" tracing = "0.1" tokio = { version = "1", features = ["time"] } futures = { version = "0.3", features = ["compat"] } -metrics = "0.21" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" async-trait = "0.1" diff --git a/prover/witness_generator/src/basic_circuits.rs b/prover/witness_generator/src/basic_circuits.rs index 645e36722745..963ec034b261 100644 --- a/prover/witness_generator/src/basic_circuits.rs +++ b/prover/witness_generator/src/basic_circuits.rs @@ -23,6 +23,7 @@ use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::scheduler::blo use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::scheduler::input::SchedulerCircuitInstanceWitness; use zksync_prover_fri_types::{AuxOutputWitnessWrapper, get_current_pod_name}; +use crate::metrics::WITNESS_GENERATOR_METRICS; use crate::storage_oracle::StorageOracle; use multivm::vm_latest::{ constants::MAX_CYCLES_FOR_TX, HistoryDisabled, StorageOracle as VmStorageOracle, @@ -124,7 +125,7 @@ impl BasicWitnessGenerator { // We get value higher than `blocks_proving_percentage` with prob = `1 - blocks_proving_percentage`. // In this case job should be skipped. if threshold > blocks_proving_percentage && !shall_force_process_block { - metrics::counter!("server.witness_generator_fri.skipped_blocks", 1); + WITNESS_GENERATOR_METRICS.skipped_blocks.inc(); tracing::info!( "Skipping witness generation for block {}, blocks_proving_percentage: {}", block_number.0, @@ -146,7 +147,7 @@ impl BasicWitnessGenerator { } } - metrics::counter!("server.witness_generator_fri.sampled_blocks", 1); + WITNESS_GENERATOR_METRICS.sampled_blocks.inc(); tracing::info!( "Starting witness generation of type {:?} for block {}", AggregationRound::BasicCircuits, @@ -196,11 +197,10 @@ impl JobProcessor for BasicWitnessGenerator { ); let started_at = Instant::now(); let job = get_artifacts(block_number, &*self.object_store).await; - metrics::histogram!( - "prover_fri.witness_generation.blob_fetch_time", - started_at.elapsed(), - "aggregation_round" => format!("{:?}", AggregationRound::BasicCircuits), - ); + + WITNESS_GENERATOR_METRICS.blob_fetch_time[&AggregationRound::BasicCircuits.into()] + .observe(started_at.elapsed()); + Ok(Some((block_number, job))) } None => Ok(None), @@ -258,11 +258,10 @@ impl JobProcessor for BasicWitnessGenerator { self.config.shall_save_to_public_bucket, ) .await; - metrics::histogram!( - "prover_fri.witness_generation.blob_save_time", - blob_started_at.elapsed(), - "aggregation_round" => format!("{:?}", AggregationRound::BasicCircuits), - ); + + WITNESS_GENERATOR_METRICS.blob_save_time[&AggregationRound::BasicCircuits.into()] + .observe(blob_started_at.elapsed()); + update_database(&self.prover_connection_pool, started_at, job_id, blob_urls).await; Ok(()) } @@ -305,11 +304,9 @@ async fn process_basic_circuits_job( scheduler_witness, aux_output_witness, ) = generate_witness(object_store, config, connection_pool, witness_gen_input).await; - metrics::histogram!( - "prover_fri.witness_generation.witness_generation_time", - started_at.elapsed(), - "aggregation_round" => format!("{:?}", AggregationRound::BasicCircuits), - ); + WITNESS_GENERATOR_METRICS.witness_generation_time[&AggregationRound::BasicCircuits.into()] + .observe(started_at.elapsed()); + tracing::info!( "Witness generation for block {} is complete in {:?}", block_number.0, diff --git a/prover/witness_generator/src/leaf_aggregation.rs b/prover/witness_generator/src/leaf_aggregation.rs index e31a44c42aa9..d90520a19e6f 100644 --- a/prover/witness_generator/src/leaf_aggregation.rs +++ b/prover/witness_generator/src/leaf_aggregation.rs @@ -18,6 +18,7 @@ use zksync_vk_setup_data_server_fri::{ get_base_layer_vk_for_circuit_type, get_recursive_layer_vk_for_circuit_type, }; +use crate::metrics::WITNESS_GENERATOR_METRICS; use crate::utils::{ load_proofs_for_job_ids, save_node_aggregations_artifacts, save_recursive_layer_prover_input_artifacts, ClosedFormInputWrapper, @@ -199,11 +200,10 @@ pub async fn prepare_leaf_aggregation_job( let started_at = Instant::now(); let closed_form_input = get_artifacts(&metadata, object_store).await; let proofs = load_proofs_for_job_ids(&metadata.prover_job_ids_for_proofs, object_store).await; - metrics::histogram!( - "prover_fri.witness_generation.blob_fetch_time", - started_at.elapsed(), - "aggregation_round" => format!("{:?}", AggregationRound::LeafAggregation), - ); + + WITNESS_GENERATOR_METRICS.blob_fetch_time[&AggregationRound::LeafAggregation.into()] + .observe(started_at.elapsed()); + let started_at = Instant::now(); let base_vk = get_base_layer_vk_for_circuit_type(metadata.circuit_id) .context("get_base_layer_vk_for_circuit_type()")?; @@ -221,11 +221,10 @@ pub async fn prepare_leaf_aggregation_job( } } let leaf_params = compute_leaf_params(metadata.circuit_id, base_vk.clone(), leaf_vk); - metrics::histogram!( - "prover_fri.witness_generation.prepare_job_time", - started_at.elapsed(), - "aggregation_round" => format!("{:?}", AggregationRound::LeafAggregation), - ); + + WITNESS_GENERATOR_METRICS.prepare_job_time[&AggregationRound::LeafAggregation.into()] + .observe(started_at.elapsed()); + Ok(LeafAggregationWitnessGeneratorJob { circuit_id: metadata.circuit_id, block_number: metadata.block_number, @@ -249,11 +248,9 @@ pub fn process_leaf_aggregation_job( let leaf_params = (circuit_id, job.leaf_params); let (aggregations, closed_form_inputs) = create_leaf_witnesses(subsets, job.proofs, job.base_vk, leaf_params); - metrics::histogram!( - "prover_fri.witness_generation.witness_generation_time", - started_at.elapsed(), - "aggregation_round" => format!("{:?}", AggregationRound::LeafAggregation), - ); + WITNESS_GENERATOR_METRICS.witness_generation_time[&AggregationRound::LeafAggregation.into()] + .observe(started_at.elapsed()); + tracing::info!( "Leaf witness generation for block {} with circuit id {}: is complete in {:?}.", job.block_number.0, @@ -379,11 +376,9 @@ async fn save_artifacts( None, ) .await; - metrics::histogram!( - "prover_fri.witness_generation.blob_save_time", - started_at.elapsed(), - "aggregation_round" => format!("{:?}", AggregationRound::LeafAggregation), - ); + WITNESS_GENERATOR_METRICS.blob_save_time[&AggregationRound::LeafAggregation.into()] + .observe(started_at.elapsed()); + BlobUrls { circuit_ids_and_urls, aggregations_urls, diff --git a/prover/witness_generator/src/lib.rs b/prover/witness_generator/src/lib.rs index f7f0fcb2642d..567769c07b9b 100644 --- a/prover/witness_generator/src/lib.rs +++ b/prover/witness_generator/src/lib.rs @@ -8,5 +8,7 @@ pub mod scheduler; mod storage_oracle; pub mod utils; +pub mod metrics; + #[cfg(test)] mod tests; diff --git a/prover/witness_generator/src/main.rs b/prover/witness_generator/src/main.rs index e6226c401314..a8d0bda48f2e 100644 --- a/prover/witness_generator/src/main.rs +++ b/prover/witness_generator/src/main.rs @@ -19,6 +19,7 @@ use zksync_vk_setup_data_server_fri::commitment_utils::get_cached_commitments; use crate::basic_circuits::BasicWitnessGenerator; use crate::leaf_aggregation::LeafAggregationWitnessGenerator; +use crate::metrics::SERVER_METRICS; use crate::node_aggregation::NodeAggregationWitnessGenerator; use crate::scheduler::SchedulerWitnessGenerator; @@ -28,6 +29,9 @@ mod node_aggregation; mod precalculated_merkle_paths_provider; mod scheduler; mod storage_oracle; + +mod metrics; + mod utils; #[derive(Debug, StructOpt)] @@ -225,11 +229,7 @@ async fn main() -> anyhow::Result<()> { round, started_at.elapsed() ); - metrics::gauge!( - "server.init.latency", - started_at.elapsed(), - "stage" => format!("fri_witness_generator_{:?}", round) - ); + SERVER_METRICS.init_latency[&(*round).into()].set(started_at.elapsed()); } let mut stop_signal_receiver = get_stop_signal_receiver(); diff --git a/prover/witness_generator/src/metrics.rs b/prover/witness_generator/src/metrics.rs new file mode 100644 index 000000000000..3bddefc00c4b --- /dev/null +++ b/prover/witness_generator/src/metrics.rs @@ -0,0 +1,33 @@ +use std::time::Duration; +use vise::{Buckets, Counter, Family, Gauge, Histogram, LabeledFamily, Metrics}; +use zksync_prover_fri_utils::metrics::StageLabel; + +#[derive(Debug, Metrics)] +#[metrics(prefix = "prover_fri_witness_generator")] +pub(crate) struct WitnessGeneratorMetrics { + #[metrics(buckets = Buckets::LATENCIES)] + pub blob_fetch_time: Family>, + #[metrics(buckets = Buckets::LATENCIES)] + pub prepare_job_time: Family>, + #[metrics(buckets = Buckets::LATENCIES)] + pub witness_generation_time: Family>, + #[metrics(buckets = Buckets::LATENCIES)] + pub blob_save_time: Family>, + + pub sampled_blocks: Counter, + pub skipped_blocks: Counter, +} + +#[vise::register] +pub(crate) static WITNESS_GENERATOR_METRICS: vise::Global = + vise::Global::new(); + +#[derive(Debug, Metrics)] +#[metrics(prefix = "prover")] +pub(crate) struct ServerMetrics { + #[metrics(labels = ["stage"])] + pub init_latency: LabeledFamily>, +} + +#[vise::register] +pub(crate) static SERVER_METRICS: vise::Global = vise::Global::new(); diff --git a/prover/witness_generator/src/node_aggregation.rs b/prover/witness_generator/src/node_aggregation.rs index 8349b1e18e9d..1ae5a2551978 100644 --- a/prover/witness_generator/src/node_aggregation.rs +++ b/prover/witness_generator/src/node_aggregation.rs @@ -16,6 +16,7 @@ use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::recursion::lea use zksync_vk_setup_data_server_fri::get_recursive_layer_vk_for_circuit_type; use zksync_vk_setup_data_server_fri::utils::get_leaf_vk_params; +use crate::metrics::WITNESS_GENERATOR_METRICS; use crate::utils::{ load_proofs_for_job_ids, save_node_aggregations_artifacts, save_recursive_layer_prover_input_artifacts, AggregationWrapper, @@ -108,11 +109,10 @@ impl NodeAggregationWitnessGenerator { node_vk_commitment, &job.all_leafs_layer_params, ); - metrics::histogram!( - "prover_fri.witness_generation.witness_generation_time", - started_at.elapsed(), - "aggregation_round" => format!("{:?}", AggregationRound::NodeAggregation), - ); + WITNESS_GENERATOR_METRICS.witness_generation_time + [&AggregationRound::NodeAggregation.into()] + .observe(started_at.elapsed()); + tracing::info!( "Node witness generation for block {} with circuit id {} at depth {} with {} next_aggregations jobs completed in {:?}.", job.block_number.0, @@ -228,11 +228,10 @@ pub async fn prepare_job( let started_at = Instant::now(); let artifacts = get_artifacts(&metadata, object_store).await; let proofs = load_proofs_for_job_ids(&metadata.prover_job_ids_for_proofs, object_store).await; - metrics::histogram!( - "prover_fri.witness_generation.blob_fetch_time", - started_at.elapsed(), - "aggregation_round" => format!("{:?}", AggregationRound::NodeAggregation), - ); + + WITNESS_GENERATOR_METRICS.blob_fetch_time[&AggregationRound::NodeAggregation.into()] + .observe(started_at.elapsed()); + let started_at = Instant::now(); let leaf_vk = get_recursive_layer_vk_for_circuit_type(metadata.circuit_id) .context("get_recursive_layer_vk_for_circuit_type")?; @@ -254,11 +253,9 @@ pub async fn prepare_job( } } - metrics::histogram!( - "prover_fri.witness_generation.job_preparation_time", - started_at.elapsed(), - "aggregation_round" => format!("{:?}", AggregationRound::NodeAggregation), - ); + WITNESS_GENERATOR_METRICS.prepare_job_time[&AggregationRound::NodeAggregation.into()] + .observe(started_at.elapsed()); + Ok(NodeAggregationWitnessGeneratorJob { circuit_id: metadata.circuit_id, block_number: metadata.block_number, @@ -376,11 +373,10 @@ async fn save_artifacts( Some(artifacts.circuit_id), ) .await; - metrics::histogram!( - "prover_fri.witness_generation.blob_save_time", - started_at.elapsed(), - "aggregation_round" => format!("{:?}", AggregationRound::NodeAggregation), - ); + + WITNESS_GENERATOR_METRICS.blob_save_time[&AggregationRound::NodeAggregation.into()] + .observe(started_at.elapsed()); + BlobUrls { node_aggregations_url: aggregations_urls, circuit_ids_and_urls, diff --git a/prover/witness_generator/src/scheduler.rs b/prover/witness_generator/src/scheduler.rs index 50950c8e986e..5036aa188ecf 100644 --- a/prover/witness_generator/src/scheduler.rs +++ b/prover/witness_generator/src/scheduler.rs @@ -16,6 +16,7 @@ use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::scheduler::Sch use zksync_vk_setup_data_server_fri::get_recursive_layer_vk_for_circuit_type; use zksync_vk_setup_data_server_fri::utils::get_leaf_vk_params; +use crate::metrics::WITNESS_GENERATOR_METRICS; use crate::utils::{load_proofs_for_job_ids, SchedulerPartialInputWrapper}; use zksync_config::configs::FriWitnessGeneratorConfig; use zksync_dal::ConnectionPool; @@ -86,11 +87,8 @@ impl SchedulerWitnessGenerator { transcript_params: (), _marker: std::marker::PhantomData, }; - metrics::histogram!( - "prover_fri.witness_generation.witness_generation_time", - started_at.elapsed(), - "aggregation_round" => format!("{:?}", AggregationRound::Scheduler), - ); + WITNESS_GENERATOR_METRICS.witness_generation_time[&AggregationRound::Scheduler.into()] + .observe(started_at.elapsed()); tracing::info!( "Scheduler generation for block {} is complete in {:?}", @@ -173,11 +171,8 @@ impl JobProcessor for SchedulerWitnessGenerator { .put(key, &CircuitWrapper::Recursive(artifacts.scheduler_circuit)) .await .unwrap(); - metrics::histogram!( - "prover_fri.witness_generation.blob_save_time", - blob_save_started_at.elapsed(), - "aggregation_round" => format!("{:?}", AggregationRound::Scheduler), - ); + WITNESS_GENERATOR_METRICS.blob_save_time[&AggregationRound::Scheduler.into()] + .observe(blob_save_started_at.elapsed()); let mut prover_connection = self.prover_connection_pool.access_storage().await.unwrap(); let mut transaction = prover_connection.start_transaction().await.unwrap(); @@ -234,11 +229,9 @@ pub async fn prepare_job( ) -> anyhow::Result { let started_at = Instant::now(); let proofs = load_proofs_for_job_ids(&proof_job_ids, object_store).await; - metrics::histogram!( - "prover_fri.witness_generation.blob_fetch_time", - started_at.elapsed(), - "aggregation_round" => format!("{:?}", AggregationRound::Scheduler), - ); + WITNESS_GENERATOR_METRICS.blob_fetch_time[&AggregationRound::Scheduler.into()] + .observe(started_at.elapsed()); + let mut recursive_proofs = vec![]; for wrapper in proofs { match wrapper { @@ -270,11 +263,9 @@ pub async fn prepare_job( .try_into() .unwrap(); scheduler_witness.leaf_layer_parameters = leaf_layer_params; - metrics::histogram!( - "prover_fri.witness_generation.prepare_job_time", - started_at.elapsed(), - "aggregation_round" => format!("{:?}", AggregationRound::Scheduler), - ); + + WITNESS_GENERATOR_METRICS.prepare_job_time[&AggregationRound::Scheduler.into()] + .observe(started_at.elapsed()); Ok(SchedulerWitnessGeneratorJob { block_number: l1_batch_number, diff --git a/prover/witness_vector_generator/Cargo.toml b/prover/witness_vector_generator/Cargo.toml index e8f94849e30e..be5869fb1a44 100644 --- a/prover/witness_vector_generator/Cargo.toml +++ b/prover/witness_vector_generator/Cargo.toml @@ -6,6 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +vise = { git = "https://github.com/matter-labs/vise.git", version = "0.1.0", rev = "dd05139b76ab0843443ab3ff730174942c825dae" } + zksync_types = { path = "../../core/lib/types" } zksync_dal = { path = "../../core/lib/dal" } zksync_config = { path = "../../core/lib/config" } @@ -26,7 +28,6 @@ structopt = "0.3.26" tokio = { version = "1", features = ["time"] } futures = { version = "0.3", features = ["compat"] } ctrlc = { version = "3.1", features = ["termination"] } -metrics = "0.21" serde = { version = "1.0", features = ["derive"] } async-trait = "0.1" queues = "1.1.0" diff --git a/prover/witness_vector_generator/src/generator.rs b/prover/witness_vector_generator/src/generator.rs index 27e71b6cdec4..ee17e0edaa7f 100644 --- a/prover/witness_vector_generator/src/generator.rs +++ b/prover/witness_vector_generator/src/generator.rs @@ -4,6 +4,7 @@ use anyhow::Context as _; use async_trait::async_trait; use tokio::task::JoinHandle; +use crate::metrics::METRICS; use tokio::time::sleep; use zksync_config::configs::FriWitnessVectorGeneratorConfig; use zksync_dal::ConnectionPool; @@ -114,11 +115,11 @@ impl JobProcessor for WitnessVectorGenerator { started_at: Instant, artifacts: WitnessVectorArtifacts, ) -> anyhow::Result<()> { - metrics::histogram!( - "prover_fri.witness_vector_generator.gpu_witness_vector_generation_time", - started_at.elapsed(), - "circuit_type" => get_numeric_circuit_id(&artifacts.prover_job.circuit_wrapper).to_string(), - ); + let circuit_type = + get_numeric_circuit_id(&artifacts.prover_job.circuit_wrapper).to_string(); + + METRICS.gpu_witness_vector_generation_time[&circuit_type].observe(started_at.elapsed()); + tracing::info!( "Finished witness vector generation for job: {job_id} in zone: {:?} took: {:?}", self.zone, @@ -150,6 +151,8 @@ impl JobProcessor for WitnessVectorGenerator { handle_send_result(&result, job_id, &address, &self.pool, self.zone.clone()).await; if result.is_ok() { + METRICS.prover_waiting_time[&circuit_type].observe(now.elapsed()); + METRICS.prover_attempts_count[&circuit_type].observe(attempts as usize); return Ok(()); } @@ -204,11 +207,8 @@ async fn handle_send_result( "Sent assembly of size: {blob_size_in_mb}MB successfully, took: {elapsed:?} \ for job: {job_id} to: {address:?}" ); - metrics::histogram!( - "prover_fri.witness_vector_generator.blob_sending_time", - *elapsed, - "blob_size_in_mb" => blob_size_in_mb.to_string(), - ); + + METRICS.blob_sending_time[&blob_size_in_mb.to_string()].observe(*elapsed); pool.access_storage() .await diff --git a/prover/witness_vector_generator/src/lib.rs b/prover/witness_vector_generator/src/lib.rs index 365bdf1cc0d3..d9d47d54897c 100644 --- a/prover/witness_vector_generator/src/lib.rs +++ b/prover/witness_vector_generator/src/lib.rs @@ -1,3 +1,5 @@ #![feature(generic_const_exprs)] pub mod generator; + +pub mod metrics; diff --git a/prover/witness_vector_generator/src/main.rs b/prover/witness_vector_generator/src/main.rs index bcf95f603517..7c1aa8e0b89a 100644 --- a/prover/witness_vector_generator/src/main.rs +++ b/prover/witness_vector_generator/src/main.rs @@ -20,6 +20,7 @@ use zksync_utils::wait_for_tasks::wait_for_tasks; use zksync_vk_setup_data_server_fri::commitment_utils::get_cached_commitments; mod generator; +mod metrics; #[derive(Debug, StructOpt)] #[structopt( diff --git a/prover/witness_vector_generator/src/metrics.rs b/prover/witness_vector_generator/src/metrics.rs new file mode 100644 index 000000000000..4bc11ff401b0 --- /dev/null +++ b/prover/witness_vector_generator/src/metrics.rs @@ -0,0 +1,18 @@ +use std::time::Duration; +use vise::{Buckets, Histogram, LabeledFamily, Metrics}; + +#[derive(Debug, Metrics)] +#[metrics(prefix = "prover_fri_witness_vector_generator")] +pub(crate) struct WitnessVectorGeneratorMetrics { + #[metrics(buckets = Buckets::LATENCIES, labels = ["circuit_type"])] + pub gpu_witness_vector_generation_time: LabeledFamily>, + #[metrics(buckets = Buckets::LATENCIES, labels = ["circuit_type"])] + pub blob_sending_time: LabeledFamily>, + #[metrics(buckets = Buckets::LATENCIES, labels = ["circuit_type"])] + pub prover_waiting_time: LabeledFamily>, + #[metrics(buckets = Buckets::exponential(1.0..=64.0, 2.0), labels = ["circuit_type"])] + pub prover_attempts_count: LabeledFamily>, +} + +#[vise::register] +pub(crate) static METRICS: vise::Global = vise::Global::new(); From 90a085e524bce2422cf1681406e6b2546a524d70 Mon Sep 17 00:00:00 2001 From: gorden <148852660+gordera@users.noreply.github.com> Date: Mon, 4 Dec 2023 18:43:32 +0800 Subject: [PATCH 031/268] chore: fix wrong line (#592) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ fix wrong line ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --- docs/advanced/prover_keys.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/prover_keys.md b/docs/advanced/prover_keys.md index 6e127f431fc9..8cf59067cf89 100644 --- a/docs/advanced/prover_keys.md +++ b/docs/advanced/prover_keys.md @@ -114,7 +114,7 @@ For SNARK circuits (like snark_wrapper), we use keccak as hash function. For STA friendly hash function (currently Poseidon2). [basic_circuit_list]: - https://github.com/matter-labs/era-zkevm_test_harness/blob/3cd647aa57fc2e1180bab53f7a3b61ec47502a46/circuit_definitions/src/circuit_definitions/base_layer/mod.rs#L80 + https://github.com/matter-labs/era-zkevm_test_harness/blob/3cd647aa57fc2e1180bab53f7a3b61ec47502a46/circuit_definitions/src/circuit_definitions/base_layer/mod.rs#L77 [recursive_circuit_list]: https://github.com/matter-labs/era-zkevm_test_harness/blob/3cd647aa57fc2e1180bab53f7a3b61ec47502a46/circuit_definitions/src/circuit_definitions/recursion_layer/mod.rs#L29 [verification_key_list]: From a08ba322eb1e0c9e66dd343c522eea8899d1a5b7 Mon Sep 17 00:00:00 2001 From: yilirong1992 <42017468+yilirong1992@users.noreply.github.com> Date: Mon, 4 Dec 2023 20:52:03 +0800 Subject: [PATCH 032/268] chore(docs): fixed docs typo (#588) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ - Hello, fixed typo ## Why ❔ - fixed typo ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --------- Co-authored-by: Roman Brodetski --- prover/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prover/README.md b/prover/README.md index 310913bf06bf..c97cdaff2b8b 100644 --- a/prover/README.md +++ b/prover/README.md @@ -29,7 +29,7 @@ feature flag). Only used in GPU proving mode. Prepares all the witness data using CPU, and then streams it to the prover_fri. -This is mosty used for resource efficiency (as machines with GPUs are more expensive, it allows us to run many +This is mostly used for resource efficiency (as machines with GPUs are more expensive, it allows us to run many witness_vector_generators, that can 'share' as single gpu based prover_fri). ### proof_fri_compressor From 8c634357fecfe97b6149af27c5627ebd450415c9 Mon Sep 17 00:00:00 2001 From: Salad <148864073+Saladerl@users.noreply.github.com> Date: Mon, 4 Dec 2023 20:52:26 +0800 Subject: [PATCH 033/268] chore(docs): fix typos in document (#589) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Hello, I corrected the typo. ## Why ❔ - fixed typo ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --------- Co-authored-by: Igor Aleksanov --- docs/advanced/prover.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/prover.md b/docs/advanced/prover.md index 6211f00dea78..71cb668bcdd1 100644 --- a/docs/advanced/prover.md +++ b/docs/advanced/prover.md @@ -184,7 +184,7 @@ pub struct UInt32 { pub(crate) variable: Variable, } impl CSAllocatable for UInt32 { - // So the 'witness' type (concrete value) for U32 is u32 - no surprsise ;-) + // So the 'witness' type (concrete value) for U32 is u32 - no surprises ;-) type Witness = u32; ... } From 659d0e4718b8eba324aeb50718d36ecd5bdeab02 Mon Sep 17 00:00:00 2001 From: min <52465594+yilimin999@users.noreply.github.com> Date: Mon, 4 Dec 2023 20:52:27 +0800 Subject: [PATCH 034/268] chore: fix typo (#587) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ fixed typos ## Why ❔ fixed typos ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --------- Co-authored-by: Igor Aleksanov --- docs/advanced/how_l2_messaging_works.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/how_l2_messaging_works.md b/docs/advanced/how_l2_messaging_works.md index c62fe4afc5b8..dbf0abbff8df 100644 --- a/docs/advanced/how_l2_messaging_works.md +++ b/docs/advanced/how_l2_messaging_works.md @@ -177,7 +177,7 @@ return actualRootHash == calculatedRootHash; ## Summary -In this article, we've travelled through a vast array of topics: from a user contract dispatching a message to L1 by +In this article, we've traveled through a vast array of topics: from a user contract dispatching a message to L1 by invoking a system contract, to this message's hash making its way all the way to the VM via special opcodes. We've also explored how it's ultimately included in the execution results (as part of QueryLogs), gathered by the State Keeper, and transmitted to L1 for final verification. From ad4f9ab29c2a48982585f93350c9ef38cbec985a Mon Sep 17 00:00:00 2001 From: Jean <148654781+oxJean@users.noreply.github.com> Date: Mon, 4 Dec 2023 20:56:22 +0800 Subject: [PATCH 035/268] chore(docs): fix broken link (#590) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ fixed broken link ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --------- Co-authored-by: Igor Aleksanov --- docs/advanced/how_l2_messaging_works.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/how_l2_messaging_works.md b/docs/advanced/how_l2_messaging_works.md index dbf0abbff8df..147f55dee065 100644 --- a/docs/advanced/how_l2_messaging_works.md +++ b/docs/advanced/how_l2_messaging_works.md @@ -194,7 +194,7 @@ transmitted to L1 for final verification. [vm_execution_result]: https://github.com/matter-labs/zksync-era/blob/43d7bd587a84b1b4489f4c6a4169ccb90e0df467/core/lib/vm/src/vm.rs#L81 [log_queries]: - https://github.com/matter-labs/zk_evm_abstractions/blob/839721a4ae2093c5c0aa8ffd49758f32ecd172ed/src/queries.rs#L30C2-L30C2 + https://github.com/matter-labs/era-zk_evm_abstractions/blob/15a2af404902d5f10352e3d1fac693cc395fcff9/src/queries.rs#L30C2-L30C2 [aux_bytes]: https://github.com/matter-labs/zkevm_opcode_defs/blob/780ce4129a95ab9a68abf0d60c156ee8df6008c2/src/system_params.rs#L37C39-L37C39 [event_sink]: From ccd13ce88ff52c3135d794c6f92bec3b16f2210f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Grze=C5=9Bkiewicz?= Date: Mon, 4 Dec 2023 14:44:35 +0100 Subject: [PATCH 036/268] feat: faster and less noisy zk fmt (#513) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ I've added caching to prettier and changed so that noisy output about changed files is redirected to /dev/null. `zk fmt` is 3 times faster after those changes ## Why ❔ `zk fmt` output was too verbose and we didn't use cache ## Checklist - [X] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [X] Code has been formatted via `zk fmt` and `zk lint`. --- infrastructure/zk/src/fmt.ts | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/infrastructure/zk/src/fmt.ts b/infrastructure/zk/src/fmt.ts index 4bb46dba7608..fa8b5e79691b 100644 --- a/infrastructure/zk/src/fmt.ts +++ b/infrastructure/zk/src/fmt.ts @@ -4,6 +4,10 @@ import * as utils from './utils'; const EXTENSIONS = ['ts', 'md', 'sol', 'js']; const CONFIG_PATH = 'etc/prettier-config'; +function prettierFlags(phaseName: string) { + phaseName = phaseName.replace('/', '-').replace('.', ''); + return ` --cache --cache-location node_modules/.cache/prettier/.prettier-cache-${phaseName}`; +} export async function prettier(extension: string, check: boolean = false) { if (!EXTENSIONS.includes(extension)) { throw new Error('Unsupported extension'); @@ -17,19 +21,31 @@ export async function prettier(extension: string, check: boolean = false) { return; } - await utils.spawn(`yarn --silent prettier --config ${CONFIG_PATH}/${extension}.js --${command} ${files}`); + await utils.spawn( + `yarn --silent prettier --config ${CONFIG_PATH}/${extension}.js --${command} ${files} ${prettierFlags( + extension + )} ${check ? '' : '> /dev/null'}` + ); +} + +async function prettierContracts(check: boolean, directory: string) { + await utils.spawn( + `yarn --silent --cwd ${directory} prettier:${check ? 'check' : 'fix'} ${prettierFlags(directory)} ${ + check ? '' : '> /dev/null' + }` + ); } async function prettierL1Contracts(check: boolean = false) { - await utils.spawn(`yarn --silent --cwd contracts/ethereum prettier:${check ? 'check' : 'fix'}`); + await prettierContracts(check, 'contracts/ethereum'); } async function prettierL2Contracts(check: boolean = false) { - await utils.spawn(`yarn --silent --cwd contracts/zksync prettier:${check ? 'check' : 'fix'}`); + await prettierContracts(check, 'contracts/zksync'); } async function prettierSystemContracts(check: boolean = false) { - await utils.spawn(`yarn --silent --cwd etc/system-contracts prettier:${check ? 'check' : 'fix'}`); + await prettierContracts(check, 'etc/system-contracts'); } export async function rustfmt(check: boolean = false) { From aebb70d09ed3225ac9fba996e90d36a743813566 Mon Sep 17 00:00:00 2001 From: penghuarong <42017444+penghuarong@users.noreply.github.com> Date: Tue, 5 Dec 2023 01:06:22 +0800 Subject: [PATCH 037/268] chore: the errors in the document have been correct (#583) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ the errors in the document have been correct ## Why ❔ the errors in the document have been correct ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --------- Co-authored-by: Igor Aleksanov --- docs/setup-dev.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/setup-dev.md b/docs/setup-dev.md index cb42a2b1c7c8..b33a38487695 100644 --- a/docs/setup-dev.md +++ b/docs/setup-dev.md @@ -20,7 +20,7 @@ If you are a NixOS user or would like to have a reproducible environment, skip t Install `docker`. It is recommended to follow the instructions from the [official site](https://docs.docker.com/install/). -Note: currently official site proposes using Docker Desktop for linux, which is a GUI tool with plenty of quirks. If you +Note: currently official site proposes using Docker Desktop for Linux, which is a GUI tool with plenty of quirks. If you want to only have CLI tool, you need the `docker-ce` package and you can follow [this guide](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-20-04) for Ubuntu. From 445a39ba51131edbff260a9d737079ffcf4d7f48 Mon Sep 17 00:00:00 2001 From: umi <66466781+yiliminiqihang@users.noreply.github.com> Date: Tue, 5 Dec 2023 17:09:54 +0800 Subject: [PATCH 038/268] chore(docs): the errors in the document have been corrected. (#461) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ - the errors in the document have been corrected. ## Why ❔ - the errors in the document have been corrected. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --------- Co-authored-by: perekopskiy <53865202+perekopskiy@users.noreply.github.com> From c8295221fc1443727f449714a5ec06240d668473 Mon Sep 17 00:00:00 2001 From: Doll <148654386+Dollyerls@users.noreply.github.com> Date: Tue, 5 Dec 2023 17:28:41 +0800 Subject: [PATCH 039/268] chore: update document (#601) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ - update document ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --------- Co-authored-by: Igor Aleksanov Co-authored-by: perekopskiy <53865202+perekopskiy@users.noreply.github.com> --- docs/advanced/bytecode_compression.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/advanced/bytecode_compression.md b/docs/advanced/bytecode_compression.md index 2c9e42acd5d6..0742da6db80c 100644 --- a/docs/advanced/bytecode_compression.md +++ b/docs/advanced/bytecode_compression.md @@ -2,7 +2,7 @@ ## Overview -As we are a rollup - all the bytecodes that contracts use in our chain must be copied into L1 (so that the chain can be +As we are a rollup - all the bytecodes that contracts used in our chain must be copied into L1 (so that the chain can be reconstructed from L1 if needed). Given the want/need to cutdown on space used, bytecode is compressed prior to being posted to L1. At a high level @@ -31,7 +31,7 @@ Dictionary would be: 3 -> 0xC (count: 1) ``` -Note that '1' maps to '0xD', as it occurs twice, and first occurrence is earlier than first occurrence of 0xB, that also +Note that '1' maps to '0xD', as it occurs twice, and first occurrence is earlier than first occurence of 0xB, that also occurs twice. Compressed bytecode: @@ -99,10 +99,10 @@ with no change to the underlying algorithm. ### Verification & Publication The function `publishCompressBytecode` takes in both the original `_bytecode` and the `_rawCompressedData` , the latter -of which comes from the output of the server’s compression algorithm. Looping over the encoded data, derived from -`_rawCompressedData` , the corresponding chunks are pulled from the dictionary and compared to the original byte code, -reverting if there is a mismatch. After the encoded data has been verified, it is published to L1 and marked accordingly -within the `KnownCodesStorage` contract. +of which comes from the server’s compression algorithm output. Looping over the encoded data, derived from +`_rawCompressedData` , the corresponding chunks are retrieved from the dictionary and compared to the original byte +code, reverting if there is a mismatch. After the encoded data has been verified, it is published to L1 and marked +accordingly within the `KnownCodesStorage` contract. Pseudo-code implementation: From 714a8905d407de36a906a4b6d464ec2cab6eb3e8 Mon Sep 17 00:00:00 2001 From: gorden <148852660+gordera@users.noreply.github.com> Date: Tue, 5 Dec 2023 17:29:18 +0800 Subject: [PATCH 040/268] chore: fixed typos in documentation (#603) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ - fixed typos in documentation ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --------- Co-authored-by: perekopskiy <53865202+perekopskiy@users.noreply.github.com> --- docs/advanced/pubdata.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/pubdata.md b/docs/advanced/pubdata.md index d34033d0a5ef..2f5cdb251328 100644 --- a/docs/advanced/pubdata.md +++ b/docs/advanced/pubdata.md @@ -194,4 +194,4 @@ the writes will be repeated ones. Given the structure above, there is a tool, created by the [Equilibrium Team](https://equilibrium.co/) that solely uses L1 pubdata for reconstructing the state and verifying that the state root on L1 can be created using pubdata. A link to the repo can be found [here](https://github.com/eqlabs/zksync-state-reconstruct). The way the tool works is by parsing -out all the L1 pubdata for an executed batch, compaing the state roots after each batch is processed. +out all the L1 pubdata for an executed batch, comparing the state roots after each batch is processed. From 8f92aaea33f8dd9818075c3b92d6cfd3127a97fa Mon Sep 17 00:00:00 2001 From: Jean <148654781+oxJean@users.noreply.github.com> Date: Tue, 5 Dec 2023 18:09:53 +0800 Subject: [PATCH 041/268] chore: remove incorrect branch prompts (#594) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ remove incorrect branch prompts ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --------- Co-authored-by: Igor Aleksanov --- docs/advanced/gas_and_fees.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/gas_and_fees.md b/docs/advanced/gas_and_fees.md index b8f0e531e98f..0f10f0ef1d65 100644 --- a/docs/advanced/gas_and_fees.md +++ b/docs/advanced/gas_and_fees.md @@ -127,5 +127,5 @@ There are a few reasons why refunds might be 'larger' on zkSync (i.e., why we mi https://github.com/matter-labs/zksync-era/blob/main/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/mod.rs#L30 'gas_adjuster' [get_txs_fee_in_wei]: - https://github.com/matter-labs/zksync-era/blob/d590b3f0965a23eb0011779aab829d86d4fdc1d1/core/bin/zksync_core/src/api_server/tx_sender/mod.rs#L450 + https://github.com/matter-labs/zksync-era/blob/714a8905d407de36a906a4b6d464ec2cab6eb3e8/core/lib/zksync_core/src/api_server/tx_sender/mod.rs#L656 'get_txs_fee_in_wei' From 56776f929f547b1a91c5b70f89e87ef7dc25c65a Mon Sep 17 00:00:00 2001 From: Alex Ostrovski Date: Tue, 5 Dec 2023 12:47:12 +0200 Subject: [PATCH 042/268] fix: Sync protocol version between consensus and server blocks (#568) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Aligns the protocol version for consensus blocks with that of `SyncBlock`s. ## Why ❔ Required for gossip-based block syncing to work correctly. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --- Cargo.lock | 23 +++--- core/lib/types/Cargo.toml | 6 +- core/lib/zksync_core/Cargo.toml | 12 +-- .../src/sync_layer/gossip/conversions.rs | 16 +++- .../zksync_core/src/sync_layer/gossip/mod.rs | 5 +- .../src/sync_layer/gossip/tests.rs | 77 ++++++++++++------- prover/Cargo.lock | 13 ++-- spellcheck/era.dic | 1 + 8 files changed, 96 insertions(+), 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 073f25b71bb7..5b7b6cb3424d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8514,7 +8514,7 @@ dependencies = [ [[package]] name = "zksync_concurrency" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=da015d4c94b19962bc11622b6cc256e214256555#da015d4c94b19962bc11622b6cc256e214256555" dependencies = [ "anyhow", "once_cell", @@ -8541,7 +8541,7 @@ dependencies = [ [[package]] name = "zksync_consensus_bft" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=da015d4c94b19962bc11622b6cc256e214256555#da015d4c94b19962bc11622b6cc256e214256555" dependencies = [ "anyhow", "once_cell", @@ -8561,7 +8561,7 @@ dependencies = [ [[package]] name = "zksync_consensus_crypto" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=da015d4c94b19962bc11622b6cc256e214256555#da015d4c94b19962bc11622b6cc256e214256555" dependencies = [ "anyhow", "blst", @@ -8579,7 +8579,7 @@ dependencies = [ [[package]] name = "zksync_consensus_executor" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=da015d4c94b19962bc11622b6cc256e214256555#da015d4c94b19962bc11622b6cc256e214256555" dependencies = [ "anyhow", "prost", @@ -8601,7 +8601,7 @@ dependencies = [ [[package]] name = "zksync_consensus_network" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=da015d4c94b19962bc11622b6cc256e214256555#da015d4c94b19962bc11622b6cc256e214256555" dependencies = [ "anyhow", "async-trait", @@ -8625,7 +8625,7 @@ dependencies = [ [[package]] name = "zksync_consensus_roles" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=da015d4c94b19962bc11622b6cc256e214256555#da015d4c94b19962bc11622b6cc256e214256555" dependencies = [ "anyhow", "bit-vec", @@ -8633,6 +8633,7 @@ dependencies = [ "prost", "rand 0.8.5", "serde", + "thiserror", "tracing", "zksync_concurrency", "zksync_consensus_crypto", @@ -8644,7 +8645,7 @@ dependencies = [ [[package]] name = "zksync_consensus_storage" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=da015d4c94b19962bc11622b6cc256e214256555#da015d4c94b19962bc11622b6cc256e214256555" dependencies = [ "anyhow", "async-trait", @@ -8661,7 +8662,7 @@ dependencies = [ [[package]] name = "zksync_consensus_sync_blocks" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=da015d4c94b19962bc11622b6cc256e214256555#da015d4c94b19962bc11622b6cc256e214256555" dependencies = [ "anyhow", "thiserror", @@ -8676,7 +8677,7 @@ dependencies = [ [[package]] name = "zksync_consensus_utils" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=da015d4c94b19962bc11622b6cc256e214256555#da015d4c94b19962bc11622b6cc256e214256555" dependencies = [ "thiserror", "zksync_concurrency", @@ -9002,7 +9003,7 @@ dependencies = [ [[package]] name = "zksync_protobuf" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=da015d4c94b19962bc11622b6cc256e214256555#da015d4c94b19962bc11622b6cc256e214256555" dependencies = [ "anyhow", "bit-vec", @@ -9020,7 +9021,7 @@ dependencies = [ [[package]] name = "zksync_protobuf_build" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=da015d4c94b19962bc11622b6cc256e214256555#da015d4c94b19962bc11622b6cc256e214256555" dependencies = [ "anyhow", "heck 0.4.1", diff --git a/core/lib/types/Cargo.toml b/core/lib/types/Cargo.toml index 6bf130bc70c0..3e81701a04a8 100644 --- a/core/lib/types/Cargo.toml +++ b/core/lib/types/Cargo.toml @@ -23,8 +23,8 @@ codegen = { git = "https://github.com/matter-labs/solidity_plonk_verifier.git", zkevm_test_harness = { git = "https://github.com/matter-labs/era-zkevm_test_harness.git", branch = "v1.3.3" } zk_evm_1_4_0 = { git = "https://github.com/matter-labs/era-zk_evm.git", branch = "v1.4.0", package = "zk_evm" } zk_evm = { git = "https://github.com/matter-labs/era-zk_evm.git", tag = "v1.3.3-rc2" } -zksync_consensus_roles = { version = "0.1.0", git = "https://github.com/matter-labs/era-consensus.git", rev = "ed71b2e817c980a2daffef6a01885219e1dc6fa0" } -zksync_protobuf = { version = "0.1.0", git = "https://github.com/matter-labs/era-consensus.git", rev = "ed71b2e817c980a2daffef6a01885219e1dc6fa0" } +zksync_consensus_roles = { version = "0.1.0", git = "https://github.com/matter-labs/era-consensus.git", rev = "da015d4c94b19962bc11622b6cc256e214256555" } +zksync_protobuf = { version = "0.1.0", git = "https://github.com/matter-labs/era-consensus.git", rev = "da015d4c94b19962bc11622b6cc256e214256555" } anyhow = "1.0.75" chrono = { version = "0.4", features = ["serde"] } @@ -55,4 +55,4 @@ tokio = { version = "1", features = ["rt", "macros"] } serde_with = { version = "1", features = ["hex"] } [build-dependencies] -zksync_protobuf_build = { version = "0.1.0", git = "https://github.com/matter-labs/era-consensus.git", rev = "ed71b2e817c980a2daffef6a01885219e1dc6fa0" } +zksync_protobuf_build = { version = "0.1.0", git = "https://github.com/matter-labs/era-consensus.git", rev = "da015d4c94b19962bc11622b6cc256e214256555" } diff --git a/core/lib/zksync_core/Cargo.toml b/core/lib/zksync_core/Cargo.toml index 2bccff98ae9e..2d313a213675 100644 --- a/core/lib/zksync_core/Cargo.toml +++ b/core/lib/zksync_core/Cargo.toml @@ -40,11 +40,11 @@ vlog = { path = "../vlog" } multivm = { path = "../multivm" } # Consensus dependenices -zksync_concurrency = { version = "0.1.0", git = "https://github.com/matter-labs/era-consensus.git", rev = "ed71b2e817c980a2daffef6a01885219e1dc6fa0" } -zksync_consensus_roles = { version = "0.1.0", git = "https://github.com/matter-labs/era-consensus.git", rev = "ed71b2e817c980a2daffef6a01885219e1dc6fa0" } -zksync_consensus_storage = { version = "0.1.0", git = "https://github.com/matter-labs/era-consensus.git", rev = "ed71b2e817c980a2daffef6a01885219e1dc6fa0" } -zksync_consensus_executor = { version = "0.1.0", git = "https://github.com/matter-labs/era-consensus.git", rev = "ed71b2e817c980a2daffef6a01885219e1dc6fa0" } -zksync_protobuf = { version = "0.1.0", git = "https://github.com/matter-labs/era-consensus.git", rev = "ed71b2e817c980a2daffef6a01885219e1dc6fa0" } +zksync_concurrency = { version = "0.1.0", git = "https://github.com/matter-labs/era-consensus.git", rev = "da015d4c94b19962bc11622b6cc256e214256555" } +zksync_consensus_roles = { version = "0.1.0", git = "https://github.com/matter-labs/era-consensus.git", rev = "da015d4c94b19962bc11622b6cc256e214256555" } +zksync_consensus_storage = { version = "0.1.0", git = "https://github.com/matter-labs/era-consensus.git", rev = "da015d4c94b19962bc11622b6cc256e214256555" } +zksync_consensus_executor = { version = "0.1.0", git = "https://github.com/matter-labs/era-consensus.git", rev = "da015d4c94b19962bc11622b6cc256e214256555" } +zksync_protobuf = { version = "0.1.0", git = "https://github.com/matter-labs/era-consensus.git", rev = "da015d4c94b19962bc11622b6cc256e214256555" } prost = "0.12.1" serde = { version = "1.0", features = ["derive"] } @@ -98,4 +98,4 @@ tempfile = "3.0.2" test-casing = "0.1.2" [build-dependencies] -zksync_protobuf_build = { version = "0.1.0", git = "https://github.com/matter-labs/era-consensus.git", rev = "ed71b2e817c980a2daffef6a01885219e1dc6fa0" } +zksync_protobuf_build = { version = "0.1.0", git = "https://github.com/matter-labs/era-consensus.git", rev = "da015d4c94b19962bc11622b6cc256e214256555" } diff --git a/core/lib/zksync_core/src/sync_layer/gossip/conversions.rs b/core/lib/zksync_core/src/sync_layer/gossip/conversions.rs index 410c2bfe2046..ee2286917c15 100644 --- a/core/lib/zksync_core/src/sync_layer/gossip/conversions.rs +++ b/core/lib/zksync_core/src/sync_layer/gossip/conversions.rs @@ -12,6 +12,14 @@ use crate::{consensus, sync_layer::fetcher::FetchedBlock}; pub(super) fn sync_block_to_consensus_block(mut block: SyncBlock) -> anyhow::Result { let number = BlockNumber(block.number.0.into()); let consensus = block.consensus.take().context("Missing consensus fields")?; + let consensus_protocol_version = consensus.justification.message.protocol_version.as_u32(); + let block_protocol_version = block.protocol_version as u32; + anyhow::ensure!( + consensus_protocol_version == block_protocol_version, + "Protocol version for justification ({consensus_protocol_version}) differs from \ + SyncBlock.protocol_version={block_protocol_version}" + ); + let payload: consensus::Payload = block.try_into().context("Missing `SyncBlock` data")?; let payload = payload.encode(); let header = BlockHeader { @@ -36,11 +44,17 @@ impl FetchedBlock { let payload = consensus::Payload::decode(&block.payload) .context("Failed deserializing block payload")?; + let protocol_version = block.justification.message.protocol_version; + let protocol_version = + u16::try_from(protocol_version.as_u32()).context("Invalid protocol version")?; + let protocol_version = ProtocolVersionId::try_from(protocol_version) + .with_context(|| format!("Unsupported protocol version: {protocol_version}"))?; + Ok(Self { number: MiniblockNumber(number), l1_batch_number: payload.l1_batch_number, last_in_batch, - protocol_version: ProtocolVersionId::latest(), // FIXME + protocol_version, timestamp: payload.timestamp, l1_gas_price: payload.l1_gas_price, l2_fair_gas_price: payload.l2_fair_gas_price, diff --git a/core/lib/zksync_core/src/sync_layer/gossip/mod.rs b/core/lib/zksync_core/src/sync_layer/gossip/mod.rs index 2fd9f46aabb5..2ec9ca5b60ea 100644 --- a/core/lib/zksync_core/src/sync_layer/gossip/mod.rs +++ b/core/lib/zksync_core/src/sync_layer/gossip/mod.rs @@ -72,10 +72,11 @@ async fn run_gossip_fetcher_inner( .await?; let buffered = Arc::new(Buffered::new(store)); let store = buffered.inner(); - let executor = Executor::new(executor_config, node_key, buffered.clone()) - .context("Node executor misconfiguration")?; scope::run!(ctx, |ctx, s| async { + let executor = Executor::new(ctx, executor_config, node_key, buffered.clone()) + .await + .context("Node executor misconfiguration")?; s.spawn_bg(async { store .run_background_tasks(ctx) diff --git a/core/lib/zksync_core/src/sync_layer/gossip/tests.rs b/core/lib/zksync_core/src/sync_layer/gossip/tests.rs index 30597189f0b3..d8775d3637c8 100644 --- a/core/lib/zksync_core/src/sync_layer/gossip/tests.rs +++ b/core/lib/zksync_core/src/sync_layer/gossip/tests.rs @@ -11,7 +11,8 @@ use zksync_consensus_roles::validator::{self, FinalBlock}; use zksync_consensus_storage::{InMemoryStorage, WriteBlockStore}; use zksync_dal::{ConnectionPool, StorageProcessor}; use zksync_types::{ - api::en::SyncBlock, block::ConsensusBlockFields, Address, L1BatchNumber, MiniblockNumber, H256, + api::en::SyncBlock, block::ConsensusBlockFields, Address, L1BatchNumber, MiniblockNumber, + ProtocolVersionId, H256, }; use super::*; @@ -63,12 +64,19 @@ pub(super) async fn block_payload( consensus::Payload::try_from(sync_block).unwrap() } +fn latest_protocol_version() -> validator::ProtocolVersion { + (ProtocolVersionId::latest() as u32) + .try_into() + .expect("latest protocol version is invalid") +} + /// Adds consensus information for the specified `count` of miniblocks, starting from the genesis. pub(super) async fn add_consensus_fields( storage: &mut StorageProcessor<'_>, validator_key: &validator::SecretKey, block_numbers: ops::Range, ) { + let protocol_version = latest_protocol_version(); let mut prev_block_hash = validator::BlockHeaderHash::from_bytes([0; 32]); let validator_set = validator::ValidatorSet::new([validator_key.public()]).unwrap(); for number in block_numbers { @@ -79,7 +87,7 @@ pub(super) async fn add_consensus_fields( payload: payload.hash(), }; let replica_commit = validator::ReplicaCommit { - protocol_version: validator::CURRENT_VERSION, + protocol_version, view: validator::ViewNumber(number.into()), proposal: block_header, }; @@ -113,7 +121,7 @@ pub(super) fn create_genesis_block( }; let validator_set = validator::ValidatorSet::new([validator_key.public()]).unwrap(); let replica_commit = validator::ReplicaCommit { - protocol_version: validator::CURRENT_VERSION, + protocol_version: latest_protocol_version(), view: validator::ViewNumber(number), proposal: block_header, }; @@ -182,10 +190,12 @@ async fn syncing_via_gossip_fetcher(delay_first_block: bool, delay_second_block: let tx_hashes = run_state_keeper_with_multiple_miniblocks(pool.clone()).await; let mut storage = pool.access_storage().await.unwrap(); + let protocol_version = latest_protocol_version(); let genesis_block_payload = block_payload(&mut storage, 0).await.encode(); let ctx = &ctx::test_root(&ctx::AffineClock::new(CLOCK_SPEEDUP as f64)); let rng = &mut ctx.rng(); - let mut validator = FullValidatorConfig::for_single_validator(rng, genesis_block_payload); + let mut validator = + FullValidatorConfig::for_single_validator(rng, protocol_version, genesis_block_payload); let validator_set = validator.node_config.validators.clone(); let external_node = validator.connect_full_node(rng); @@ -207,19 +217,21 @@ async fn syncing_via_gossip_fetcher(delay_first_block: bool, delay_second_block: .unwrap(); } } - let validator = Executor::new( - validator.node_config, - validator.node_key, - validator_storage.clone(), - ) - .unwrap(); - // ^ We intentionally do not run consensus on the validator node, since it'll produce blocks - // with payloads that cannot be parsed by the external node. let (actions_sender, mut actions) = ActionQueue::new(); let (keeper_actions_sender, keeper_actions) = ActionQueue::new(); let state_keeper = StateKeeperHandles::new(pool.clone(), keeper_actions, &[&tx_hashes]).await; scope::run!(ctx, |ctx, s| async { + let validator = Executor::new( + ctx, + validator.node_config, + validator.node_key, + validator_storage.clone(), + ) + .await?; + // ^ We intentionally do not run consensus on the validator node, since it'll produce blocks + // with payloads that cannot be parsed by the external node. + s.spawn_bg(validator.run(ctx)); s.spawn_bg(run_gossip_fetcher_inner( ctx, @@ -309,10 +321,12 @@ async fn syncing_via_gossip_fetcher_with_multiple_l1_batches(initial_block_count let tx_hashes: Vec<_> = tx_hashes.iter().map(Vec::as_slice).collect(); let mut storage = pool.access_storage().await.unwrap(); + let protocol_version = latest_protocol_version(); let genesis_block_payload = block_payload(&mut storage, 0).await.encode(); let ctx = &ctx::test_root(&ctx::AffineClock::new(CLOCK_SPEEDUP as f64)); let rng = &mut ctx.rng(); - let mut validator = FullValidatorConfig::for_single_validator(rng, genesis_block_payload); + let mut validator = + FullValidatorConfig::for_single_validator(rng, protocol_version, genesis_block_payload); let validator_set = validator.node_config.validators.clone(); let external_node = validator.connect_full_node(rng); @@ -327,16 +341,18 @@ async fn syncing_via_gossip_fetcher_with_multiple_l1_batches(initial_block_count for block in initial_blocks { validator_storage.put_block(ctx, block).await.unwrap(); } - let validator = Executor::new( - validator.node_config, - validator.node_key, - validator_storage.clone(), - ) - .unwrap(); let (actions_sender, actions) = ActionQueue::new(); let state_keeper = StateKeeperHandles::new(pool.clone(), actions, &tx_hashes).await; scope::run!(ctx, |ctx, s| async { + let validator = Executor::new( + ctx, + validator.node_config, + validator.node_key, + validator_storage.clone(), + ) + .await?; + s.spawn_bg(validator.run(ctx)); s.spawn_bg(async { for block in delayed_blocks { @@ -388,8 +404,12 @@ async fn syncing_from_non_zero_block(first_block_number: u32) { .encode(); let ctx = &ctx::test_root(&ctx::AffineClock::new(CLOCK_SPEEDUP as f64)); let rng = &mut ctx.rng(); - let mut validator = - FullValidatorConfig::for_single_validator(rng, genesis_block_payload.clone()); + let protocol_version = latest_protocol_version(); + let mut validator = FullValidatorConfig::for_single_validator( + rng, + protocol_version, + genesis_block_payload.clone(), + ); // Override the genesis block since it has an incorrect block number. let genesis_block = create_genesis_block( &validator.validator_key, @@ -418,13 +438,6 @@ async fn syncing_from_non_zero_block(first_block_number: u32) { tracing::trace!("Re-inserted blocks to node storage"); let validator_storage = Arc::new(InMemoryStorage::new(genesis_block)); - let validator = Executor::new( - validator.node_config, - validator.node_key, - validator_storage.clone(), - ) - .unwrap(); - let tx_hashes = if first_block_number >= 2 { &tx_hashes[1..] // Skip transactions in L1 batch #1, since they won't be executed } else { @@ -433,6 +446,14 @@ async fn syncing_from_non_zero_block(first_block_number: u32) { let (actions_sender, actions) = ActionQueue::new(); let state_keeper = StateKeeperHandles::new(pool.clone(), actions, tx_hashes).await; scope::run!(ctx, |ctx, s| async { + let validator = Executor::new( + ctx, + validator.node_config, + validator.node_key, + validator_storage.clone(), + ) + .await?; + s.spawn_bg(validator.run(ctx)); s.spawn_bg(async { for block in &delayed_blocks { diff --git a/prover/Cargo.lock b/prover/Cargo.lock index 823b426d4cc0..95ff42d7052b 100644 --- a/prover/Cargo.lock +++ b/prover/Cargo.lock @@ -7059,7 +7059,7 @@ dependencies = [ [[package]] name = "zksync_concurrency" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=da015d4c94b19962bc11622b6cc256e214256555#da015d4c94b19962bc11622b6cc256e214256555" dependencies = [ "anyhow", "once_cell", @@ -7086,7 +7086,7 @@ dependencies = [ [[package]] name = "zksync_consensus_crypto" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=da015d4c94b19962bc11622b6cc256e214256555#da015d4c94b19962bc11622b6cc256e214256555" dependencies = [ "anyhow", "blst", @@ -7104,7 +7104,7 @@ dependencies = [ [[package]] name = "zksync_consensus_roles" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=da015d4c94b19962bc11622b6cc256e214256555#da015d4c94b19962bc11622b6cc256e214256555" dependencies = [ "anyhow", "bit-vec", @@ -7112,6 +7112,7 @@ dependencies = [ "prost", "rand 0.8.5", "serde", + "thiserror", "tracing", "zksync_concurrency", "zksync_consensus_crypto", @@ -7123,7 +7124,7 @@ dependencies = [ [[package]] name = "zksync_consensus_utils" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=da015d4c94b19962bc11622b6cc256e214256555#da015d4c94b19962bc11622b6cc256e214256555" dependencies = [ "thiserror", "zksync_concurrency", @@ -7301,7 +7302,7 @@ dependencies = [ [[package]] name = "zksync_protobuf" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=da015d4c94b19962bc11622b6cc256e214256555#da015d4c94b19962bc11622b6cc256e214256555" dependencies = [ "anyhow", "bit-vec", @@ -7319,7 +7320,7 @@ dependencies = [ [[package]] name = "zksync_protobuf_build" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=da015d4c94b19962bc11622b6cc256e214256555#da015d4c94b19962bc11622b6cc256e214256555" dependencies = [ "anyhow", "heck 0.4.1", diff --git a/spellcheck/era.dic b/spellcheck/era.dic index 666ee047fd26..13dd303a3dcd 100644 --- a/spellcheck/era.dic +++ b/spellcheck/era.dic @@ -261,6 +261,7 @@ blockchains sidechain sidechains tokenomics +validator validator's validator CHAINID From c6786190a0b98f308b84f40f84ffff442ae1f63b Mon Sep 17 00:00:00 2001 From: zksync-era-bot <147085853+zksync-era-bot@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:59:45 +0100 Subject: [PATCH 043/268] chore(main): release core 18.5.0 (#593) :robot: I have created a release *beep* *boop* --- ## [18.5.0](https://github.com/matter-labs/zksync-era/compare/core-v18.4.0...core-v18.5.0) (2023-12-05) ### Features * Add metric to CallTracer for calculating maximum depth of the calls ([#535](https://github.com/matter-labs/zksync-era/issues/535)) ([19c84ce](https://github.com/matter-labs/zksync-era/commit/19c84ce624d53735133fa3b12c7f980e8c14260d)) * Add various metrics to the Prover subsystems ([#541](https://github.com/matter-labs/zksync-era/issues/541)) ([58a4e6c](https://github.com/matter-labs/zksync-era/commit/58a4e6c4c22bd7f002ede1c6def0dc260706185e)) ### Bug Fixes * Sync protocol version between consensus and server blocks ([#568](https://github.com/matter-labs/zksync-era/issues/568)) ([56776f9](https://github.com/matter-labs/zksync-era/commit/56776f929f547b1a91c5b70f89e87ef7dc25c65a)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --- .github/release-please/manifest.json | 2 +- core/CHANGELOG.md | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/release-please/manifest.json b/.github/release-please/manifest.json index 9c7f15805a9b..315ef45f25dd 100644 --- a/.github/release-please/manifest.json +++ b/.github/release-please/manifest.json @@ -1,5 +1,5 @@ { "sdk/zksync-rs": "0.4.0", - "core": "18.4.0", + "core": "18.5.0", "prover": "9.0.0" } diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 04f3f13e716e..cc48a8b8c8db 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## [18.5.0](https://github.com/matter-labs/zksync-era/compare/core-v18.4.0...core-v18.5.0) (2023-12-05) + + +### Features + +* Add metric to CallTracer for calculating maximum depth of the calls ([#535](https://github.com/matter-labs/zksync-era/issues/535)) ([19c84ce](https://github.com/matter-labs/zksync-era/commit/19c84ce624d53735133fa3b12c7f980e8c14260d)) +* Add various metrics to the Prover subsystems ([#541](https://github.com/matter-labs/zksync-era/issues/541)) ([58a4e6c](https://github.com/matter-labs/zksync-era/commit/58a4e6c4c22bd7f002ede1c6def0dc260706185e)) + + +### Bug Fixes + +* Sync protocol version between consensus and server blocks ([#568](https://github.com/matter-labs/zksync-era/issues/568)) ([56776f9](https://github.com/matter-labs/zksync-era/commit/56776f929f547b1a91c5b70f89e87ef7dc25c65a)) + ## [18.4.0](https://github.com/matter-labs/zksync-era/compare/core-v18.3.1...core-v18.4.0) (2023-12-01) From 8ace0bea93bdc7eac0babb165b2e186b4d98aabd Mon Sep 17 00:00:00 2001 From: Jack <87960263+ylmin@users.noreply.github.com> Date: Tue, 5 Dec 2023 20:05:06 +0800 Subject: [PATCH 044/268] chore: fix link (#576) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ fix link ## Why ❔ - fix link ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --------- Co-authored-by: Igor Aleksanov Co-authored-by: perekopskiy <53865202+perekopskiy@users.noreply.github.com> --- docs/advanced/how_call_works.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/how_call_works.md b/docs/advanced/how_call_works.md index cf9adc12f68e..4ba4859c4109 100644 --- a/docs/advanced/how_call_works.md +++ b/docs/advanced/how_call_works.md @@ -115,7 +115,7 @@ In this article, we covered the 'life of a call' from the RPC to the inner worki https://github.com/matter-labs/zksync-era/blob/main/core/lib/zksync_core/src/api_server/execution_sandbox/execute.rs 'execution sandbox' [vm_code]: - https://github.com/matter-labs/zksync-2-dev/blob/dc3b3d6b055c558b0e1a76ef5de3184291489d9f/core/lib/vm/src/vm.rs#L544 + https://github.com/matter-labs/zksync-era/blob/ccd13ce88ff52c3135d794c6f92bec3b16f2210f/core/lib/multivm/src/versions/vm_latest/implementation/execution.rs#L108 'vm code' [bootloader_code]: https://github.com/matter-labs/era-system-contracts/blob/93a375ef6ccfe0181a248cb712c88a1babe1f119/bootloader/bootloader.yul From e71ee349009ba42289e08ec352fc92e9e78c18bb Mon Sep 17 00:00:00 2001 From: zksync-era-bot <147085853+zksync-era-bot@users.noreply.github.com> Date: Tue, 5 Dec 2023 13:24:26 +0100 Subject: [PATCH 045/268] chore(main): release prover 9.1.0 (#460) :robot: I have created a release *beep* *boop* --- ## [9.1.0](https://github.com/matter-labs/zksync-era/compare/prover-v9.0.0...prover-v9.1.0) (2023-12-05) ### Features * Add various metrics to the Prover subsystems ([#541](https://github.com/matter-labs/zksync-era/issues/541)) ([58a4e6c](https://github.com/matter-labs/zksync-era/commit/58a4e6c4c22bd7f002ede1c6def0dc260706185e)) * adds spellchecker workflow, and corrects misspelled words ([#559](https://github.com/matter-labs/zksync-era/issues/559)) ([beac0a8](https://github.com/matter-labs/zksync-era/commit/beac0a85bb1535b05c395057171f197cd976bf82)) * **dal:** Do not load config from env in DAL crate ([#444](https://github.com/matter-labs/zksync-era/issues/444)) ([3fe1bb2](https://github.com/matter-labs/zksync-era/commit/3fe1bb21f8d33557353f447811ca86c60f1fe51a)) * **en:** Implement gossip fetcher ([#371](https://github.com/matter-labs/zksync-era/issues/371)) ([a49b61d](https://github.com/matter-labs/zksync-era/commit/a49b61d7769f9dd7b4cbc4905f8f8a23abfb541c)) * **hyperchain:** Adding prover related commands to zk stack ([#440](https://github.com/matter-labs/zksync-era/issues/440)) ([580cada](https://github.com/matter-labs/zksync-era/commit/580cada003bdfe2fff686a1fc3ce001b4959aa4d)) * **job-processor:** report attempts metrics ([#448](https://github.com/matter-labs/zksync-era/issues/448)) ([ab31f03](https://github.com/matter-labs/zksync-era/commit/ab31f031dfcaa7ddf296786ddccb78e8edd2d3c5)) * **merkle tree:** Allow random-order tree recovery ([#485](https://github.com/matter-labs/zksync-era/issues/485)) ([146e4cf](https://github.com/matter-labs/zksync-era/commit/146e4cf2f8d890ff0a8d33229e224442e14be437)) * **witness-generator:** add logs to leaf aggregation job ([#542](https://github.com/matter-labs/zksync-era/issues/542)) ([7e95a3a](https://github.com/matter-labs/zksync-era/commit/7e95a3a66ea48be7b6059d34630e22c503399bdf)) ### Bug Fixes * Change no pending batches 404 error into a success response ([#279](https://github.com/matter-labs/zksync-era/issues/279)) ([e8fd805](https://github.com/matter-labs/zksync-era/commit/e8fd805c8be7980de7676bca87cfc2d445aab9e1)) * **ci:** Use the same nightly rust ([#530](https://github.com/matter-labs/zksync-era/issues/530)) ([67ef133](https://github.com/matter-labs/zksync-era/commit/67ef1339d42786efbeb83c22fac99f3bf5dd4380)) * **crypto:** update shivini to switch to era-cuda ([#469](https://github.com/matter-labs/zksync-era/issues/469)) ([38bb482](https://github.com/matter-labs/zksync-era/commit/38bb4823c7b5e0e651d9f531feede66c24afd19f)) * Sync protocol version between consensus and server blocks ([#568](https://github.com/matter-labs/zksync-era/issues/568)) ([56776f9](https://github.com/matter-labs/zksync-era/commit/56776f929f547b1a91c5b70f89e87ef7dc25c65a)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: Artem Makhortov <13339874+artmakh@users.noreply.github.com> --- .github/release-please/manifest.json | 2 +- prover/CHANGELOG.md | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/.github/release-please/manifest.json b/.github/release-please/manifest.json index 315ef45f25dd..e52e2f0a537d 100644 --- a/.github/release-please/manifest.json +++ b/.github/release-please/manifest.json @@ -1,5 +1,5 @@ { "sdk/zksync-rs": "0.4.0", "core": "18.5.0", - "prover": "9.0.0" + "prover": "9.1.0" } diff --git a/prover/CHANGELOG.md b/prover/CHANGELOG.md index ea18d81dbccf..7c3b072b147c 100644 --- a/prover/CHANGELOG.md +++ b/prover/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## [9.1.0](https://github.com/matter-labs/zksync-era/compare/prover-v9.0.0...prover-v9.1.0) (2023-12-05) + + +### Features + +* Add various metrics to the Prover subsystems ([#541](https://github.com/matter-labs/zksync-era/issues/541)) ([58a4e6c](https://github.com/matter-labs/zksync-era/commit/58a4e6c4c22bd7f002ede1c6def0dc260706185e)) +* adds spellchecker workflow, and corrects misspelled words ([#559](https://github.com/matter-labs/zksync-era/issues/559)) ([beac0a8](https://github.com/matter-labs/zksync-era/commit/beac0a85bb1535b05c395057171f197cd976bf82)) +* **dal:** Do not load config from env in DAL crate ([#444](https://github.com/matter-labs/zksync-era/issues/444)) ([3fe1bb2](https://github.com/matter-labs/zksync-era/commit/3fe1bb21f8d33557353f447811ca86c60f1fe51a)) +* **en:** Implement gossip fetcher ([#371](https://github.com/matter-labs/zksync-era/issues/371)) ([a49b61d](https://github.com/matter-labs/zksync-era/commit/a49b61d7769f9dd7b4cbc4905f8f8a23abfb541c)) +* **hyperchain:** Adding prover related commands to zk stack ([#440](https://github.com/matter-labs/zksync-era/issues/440)) ([580cada](https://github.com/matter-labs/zksync-era/commit/580cada003bdfe2fff686a1fc3ce001b4959aa4d)) +* **job-processor:** report attempts metrics ([#448](https://github.com/matter-labs/zksync-era/issues/448)) ([ab31f03](https://github.com/matter-labs/zksync-era/commit/ab31f031dfcaa7ddf296786ddccb78e8edd2d3c5)) +* **merkle tree:** Allow random-order tree recovery ([#485](https://github.com/matter-labs/zksync-era/issues/485)) ([146e4cf](https://github.com/matter-labs/zksync-era/commit/146e4cf2f8d890ff0a8d33229e224442e14be437)) +* **witness-generator:** add logs to leaf aggregation job ([#542](https://github.com/matter-labs/zksync-era/issues/542)) ([7e95a3a](https://github.com/matter-labs/zksync-era/commit/7e95a3a66ea48be7b6059d34630e22c503399bdf)) + + +### Bug Fixes + +* Change no pending batches 404 error into a success response ([#279](https://github.com/matter-labs/zksync-era/issues/279)) ([e8fd805](https://github.com/matter-labs/zksync-era/commit/e8fd805c8be7980de7676bca87cfc2d445aab9e1)) +* **ci:** Use the same nightly rust ([#530](https://github.com/matter-labs/zksync-era/issues/530)) ([67ef133](https://github.com/matter-labs/zksync-era/commit/67ef1339d42786efbeb83c22fac99f3bf5dd4380)) +* **crypto:** update shivini to switch to era-cuda ([#469](https://github.com/matter-labs/zksync-era/issues/469)) ([38bb482](https://github.com/matter-labs/zksync-era/commit/38bb4823c7b5e0e651d9f531feede66c24afd19f)) +* Sync protocol version between consensus and server blocks ([#568](https://github.com/matter-labs/zksync-era/issues/568)) ([56776f9](https://github.com/matter-labs/zksync-era/commit/56776f929f547b1a91c5b70f89e87ef7dc25c65a)) + ## [9.0.0](https://github.com/matter-labs/zksync-era/compare/prover-v8.1.0...prover-v9.0.0) (2023-11-09) From e2e94ff59deffda11f45d19d8075ed98a0590780 Mon Sep 17 00:00:00 2001 From: zksync-era-bot <147085853+zksync-era-bot@users.noreply.github.com> Date: Tue, 5 Dec 2023 14:27:32 +0100 Subject: [PATCH 046/268] chore(main): release prover 10.0.0 (#608) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit :robot: I have created a release *beep* *boop* --- ## [10.0.0](https://github.com/matter-labs/zksync-era/compare/prover-v9.1.0...prover-v10.0.0) (2023-12-05) ### ⚠ BREAKING CHANGES * boojum integration ([#112](https://github.com/matter-labs/zksync-era/issues/112)) * Update to protocol version 17 ([#384](https://github.com/matter-labs/zksync-era/issues/384)) ### Features * Add various metrics to the Prover subsystems ([#541](https://github.com/matter-labs/zksync-era/issues/541)) ([58a4e6c](https://github.com/matter-labs/zksync-era/commit/58a4e6c4c22bd7f002ede1c6def0dc260706185e)) * adds spellchecker workflow, and corrects misspelled words ([#559](https://github.com/matter-labs/zksync-era/issues/559)) ([beac0a8](https://github.com/matter-labs/zksync-era/commit/beac0a85bb1535b05c395057171f197cd976bf82)) * boojum integration ([#112](https://github.com/matter-labs/zksync-era/issues/112)) ([e76d346](https://github.com/matter-labs/zksync-era/commit/e76d346d02ded771dea380aa8240da32119d7198)) * **boojum:** Adding README to prover directory ([#189](https://github.com/matter-labs/zksync-era/issues/189)) ([c175033](https://github.com/matter-labs/zksync-era/commit/c175033b48a8da4969d88b6850dd0247c4004794)) * **config:** Extract everything not related to the env config from zksync_config crate ([#245](https://github.com/matter-labs/zksync-era/issues/245)) ([42c64e9](https://github.com/matter-labs/zksync-era/commit/42c64e91e13b6b37619f1459f927fa046ef01097)) * **core:** Split config definitions and deserialization ([#414](https://github.com/matter-labs/zksync-era/issues/414)) ([c7c6b32](https://github.com/matter-labs/zksync-era/commit/c7c6b321a63dbcc7f1af045aa7416e697beab08f)) * **dal:** Do not load config from env in DAL crate ([#444](https://github.com/matter-labs/zksync-era/issues/444)) ([3fe1bb2](https://github.com/matter-labs/zksync-era/commit/3fe1bb21f8d33557353f447811ca86c60f1fe51a)) * **en:** Implement gossip fetcher ([#371](https://github.com/matter-labs/zksync-era/issues/371)) ([a49b61d](https://github.com/matter-labs/zksync-era/commit/a49b61d7769f9dd7b4cbc4905f8f8a23abfb541c)) * **fri-prover:** In witness - panic if protocol version is not available ([#192](https://github.com/matter-labs/zksync-era/issues/192)) ([0403749](https://github.com/matter-labs/zksync-era/commit/040374900656c854a7b9de32e5dbaf47c1c47889)) * **hyperchain:** Adding prover related commands to zk stack ([#440](https://github.com/matter-labs/zksync-era/issues/440)) ([580cada](https://github.com/matter-labs/zksync-era/commit/580cada003bdfe2fff686a1fc3ce001b4959aa4d)) * **job-processor:** report attempts metrics ([#448](https://github.com/matter-labs/zksync-era/issues/448)) ([ab31f03](https://github.com/matter-labs/zksync-era/commit/ab31f031dfcaa7ddf296786ddccb78e8edd2d3c5)) * **merkle tree:** Allow random-order tree recovery ([#485](https://github.com/matter-labs/zksync-era/issues/485)) ([146e4cf](https://github.com/matter-labs/zksync-era/commit/146e4cf2f8d890ff0a8d33229e224442e14be437)) * **merkle tree:** Snapshot recovery for Merkle tree ([#163](https://github.com/matter-labs/zksync-era/issues/163)) ([9e20703](https://github.com/matter-labs/zksync-era/commit/9e2070380e6720d84563a14a2246fc18fdb1f8f9)) * Rewrite server binary to use `vise` metrics ([#120](https://github.com/matter-labs/zksync-era/issues/120)) ([26ee1fb](https://github.com/matter-labs/zksync-era/commit/26ee1fbb16cbd7c4fad334cbc6804e7d779029b6)) * Update to protocol version 17 ([#384](https://github.com/matter-labs/zksync-era/issues/384)) ([ba271a5](https://github.com/matter-labs/zksync-era/commit/ba271a5f34d64d04c0135b8811685b80f26a8c32)) * **vm:** Move all vm versions to the one crate ([#249](https://github.com/matter-labs/zksync-era/issues/249)) ([e3fb489](https://github.com/matter-labs/zksync-era/commit/e3fb4894d08aa98a84e64eaa95b51001055cf911)) * **witness-generator:** add logs to leaf aggregation job ([#542](https://github.com/matter-labs/zksync-era/issues/542)) ([7e95a3a](https://github.com/matter-labs/zksync-era/commit/7e95a3a66ea48be7b6059d34630e22c503399bdf)) ### Bug Fixes * Change no pending batches 404 error into a success response ([#279](https://github.com/matter-labs/zksync-era/issues/279)) ([e8fd805](https://github.com/matter-labs/zksync-era/commit/e8fd805c8be7980de7676bca87cfc2d445aab9e1)) * **ci:** Use the same nightly rust ([#530](https://github.com/matter-labs/zksync-era/issues/530)) ([67ef133](https://github.com/matter-labs/zksync-era/commit/67ef1339d42786efbeb83c22fac99f3bf5dd4380)) * **crypto:** update deps to include circuit fixes ([#402](https://github.com/matter-labs/zksync-era/issues/402)) ([4c82015](https://github.com/matter-labs/zksync-era/commit/4c820150714dfb01c304c43e27f217f17deba449)) * **crypto:** update shivini to switch to era-cuda ([#469](https://github.com/matter-labs/zksync-era/issues/469)) ([38bb482](https://github.com/matter-labs/zksync-era/commit/38bb4823c7b5e0e651d9f531feede66c24afd19f)) * **crypto:** update snark-vk to be used in server and update args for proof wrapping ([#240](https://github.com/matter-labs/zksync-era/issues/240)) ([4a5c54c](https://github.com/matter-labs/zksync-era/commit/4a5c54c48bbc100c29fa719c4b1dc3535743003d)) * **docs:** Add links to setup-data keys ([#360](https://github.com/matter-labs/zksync-era/issues/360)) ([1d4fe69](https://github.com/matter-labs/zksync-era/commit/1d4fe697e4e98a8e64642cde4fe202338ce5ec61)) * **path:** update gpu prover setup data path to remove extra gpu suffix ([#454](https://github.com/matter-labs/zksync-era/issues/454)) ([2e145c1](https://github.com/matter-labs/zksync-era/commit/2e145c192b348b2756acf61fac5bfe0ca5a6575f)) * **prover-fri:** Update setup loading for node agg circuit ([#323](https://github.com/matter-labs/zksync-era/issues/323)) ([d1034b0](https://github.com/matter-labs/zksync-era/commit/d1034b05754219b603508ef79c114d908c94c1e9)) * **prover-logging:** tasks_allowed_to_finish set to true for 1 off jobs ([#227](https://github.com/matter-labs/zksync-era/issues/227)) ([0fac66f](https://github.com/matter-labs/zksync-era/commit/0fac66f5ff86cc801ea0bb6f9272cb397cd03a95)) * Sync protocol version between consensus and server blocks ([#568](https://github.com/matter-labs/zksync-era/issues/568)) ([56776f9](https://github.com/matter-labs/zksync-era/commit/56776f929f547b1a91c5b70f89e87ef7dc25c65a)) * Update prover to use the correct storage oracle ([#446](https://github.com/matter-labs/zksync-era/issues/446)) ([835dd82](https://github.com/matter-labs/zksync-era/commit/835dd828ef5610a446ec8c456e4df1def0e213ab)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --- .github/release-please/manifest.json | 2 +- prover/CHANGELOG.md | 43 ++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/.github/release-please/manifest.json b/.github/release-please/manifest.json index e52e2f0a537d..4d3b72a795fd 100644 --- a/.github/release-please/manifest.json +++ b/.github/release-please/manifest.json @@ -1,5 +1,5 @@ { "sdk/zksync-rs": "0.4.0", "core": "18.5.0", - "prover": "9.1.0" + "prover": "10.0.0" } diff --git a/prover/CHANGELOG.md b/prover/CHANGELOG.md index 7c3b072b147c..3038c52081ae 100644 --- a/prover/CHANGELOG.md +++ b/prover/CHANGELOG.md @@ -1,5 +1,48 @@ # Changelog +## [10.0.0](https://github.com/matter-labs/zksync-era/compare/prover-v9.1.0...prover-v10.0.0) (2023-12-05) + + +### ⚠ BREAKING CHANGES + +* boojum integration ([#112](https://github.com/matter-labs/zksync-era/issues/112)) +* Update to protocol version 17 ([#384](https://github.com/matter-labs/zksync-era/issues/384)) + +### Features + +* Add various metrics to the Prover subsystems ([#541](https://github.com/matter-labs/zksync-era/issues/541)) ([58a4e6c](https://github.com/matter-labs/zksync-era/commit/58a4e6c4c22bd7f002ede1c6def0dc260706185e)) +* adds spellchecker workflow, and corrects misspelled words ([#559](https://github.com/matter-labs/zksync-era/issues/559)) ([beac0a8](https://github.com/matter-labs/zksync-era/commit/beac0a85bb1535b05c395057171f197cd976bf82)) +* boojum integration ([#112](https://github.com/matter-labs/zksync-era/issues/112)) ([e76d346](https://github.com/matter-labs/zksync-era/commit/e76d346d02ded771dea380aa8240da32119d7198)) +* **boojum:** Adding README to prover directory ([#189](https://github.com/matter-labs/zksync-era/issues/189)) ([c175033](https://github.com/matter-labs/zksync-era/commit/c175033b48a8da4969d88b6850dd0247c4004794)) +* **config:** Extract everything not related to the env config from zksync_config crate ([#245](https://github.com/matter-labs/zksync-era/issues/245)) ([42c64e9](https://github.com/matter-labs/zksync-era/commit/42c64e91e13b6b37619f1459f927fa046ef01097)) +* **core:** Split config definitions and deserialization ([#414](https://github.com/matter-labs/zksync-era/issues/414)) ([c7c6b32](https://github.com/matter-labs/zksync-era/commit/c7c6b321a63dbcc7f1af045aa7416e697beab08f)) +* **dal:** Do not load config from env in DAL crate ([#444](https://github.com/matter-labs/zksync-era/issues/444)) ([3fe1bb2](https://github.com/matter-labs/zksync-era/commit/3fe1bb21f8d33557353f447811ca86c60f1fe51a)) +* **en:** Implement gossip fetcher ([#371](https://github.com/matter-labs/zksync-era/issues/371)) ([a49b61d](https://github.com/matter-labs/zksync-era/commit/a49b61d7769f9dd7b4cbc4905f8f8a23abfb541c)) +* **fri-prover:** In witness - panic if protocol version is not available ([#192](https://github.com/matter-labs/zksync-era/issues/192)) ([0403749](https://github.com/matter-labs/zksync-era/commit/040374900656c854a7b9de32e5dbaf47c1c47889)) +* **hyperchain:** Adding prover related commands to zk stack ([#440](https://github.com/matter-labs/zksync-era/issues/440)) ([580cada](https://github.com/matter-labs/zksync-era/commit/580cada003bdfe2fff686a1fc3ce001b4959aa4d)) +* **job-processor:** report attempts metrics ([#448](https://github.com/matter-labs/zksync-era/issues/448)) ([ab31f03](https://github.com/matter-labs/zksync-era/commit/ab31f031dfcaa7ddf296786ddccb78e8edd2d3c5)) +* **merkle tree:** Allow random-order tree recovery ([#485](https://github.com/matter-labs/zksync-era/issues/485)) ([146e4cf](https://github.com/matter-labs/zksync-era/commit/146e4cf2f8d890ff0a8d33229e224442e14be437)) +* **merkle tree:** Snapshot recovery for Merkle tree ([#163](https://github.com/matter-labs/zksync-era/issues/163)) ([9e20703](https://github.com/matter-labs/zksync-era/commit/9e2070380e6720d84563a14a2246fc18fdb1f8f9)) +* Rewrite server binary to use `vise` metrics ([#120](https://github.com/matter-labs/zksync-era/issues/120)) ([26ee1fb](https://github.com/matter-labs/zksync-era/commit/26ee1fbb16cbd7c4fad334cbc6804e7d779029b6)) +* Update to protocol version 17 ([#384](https://github.com/matter-labs/zksync-era/issues/384)) ([ba271a5](https://github.com/matter-labs/zksync-era/commit/ba271a5f34d64d04c0135b8811685b80f26a8c32)) +* **vm:** Move all vm versions to the one crate ([#249](https://github.com/matter-labs/zksync-era/issues/249)) ([e3fb489](https://github.com/matter-labs/zksync-era/commit/e3fb4894d08aa98a84e64eaa95b51001055cf911)) +* **witness-generator:** add logs to leaf aggregation job ([#542](https://github.com/matter-labs/zksync-era/issues/542)) ([7e95a3a](https://github.com/matter-labs/zksync-era/commit/7e95a3a66ea48be7b6059d34630e22c503399bdf)) + + +### Bug Fixes + +* Change no pending batches 404 error into a success response ([#279](https://github.com/matter-labs/zksync-era/issues/279)) ([e8fd805](https://github.com/matter-labs/zksync-era/commit/e8fd805c8be7980de7676bca87cfc2d445aab9e1)) +* **ci:** Use the same nightly rust ([#530](https://github.com/matter-labs/zksync-era/issues/530)) ([67ef133](https://github.com/matter-labs/zksync-era/commit/67ef1339d42786efbeb83c22fac99f3bf5dd4380)) +* **crypto:** update deps to include circuit fixes ([#402](https://github.com/matter-labs/zksync-era/issues/402)) ([4c82015](https://github.com/matter-labs/zksync-era/commit/4c820150714dfb01c304c43e27f217f17deba449)) +* **crypto:** update shivini to switch to era-cuda ([#469](https://github.com/matter-labs/zksync-era/issues/469)) ([38bb482](https://github.com/matter-labs/zksync-era/commit/38bb4823c7b5e0e651d9f531feede66c24afd19f)) +* **crypto:** update snark-vk to be used in server and update args for proof wrapping ([#240](https://github.com/matter-labs/zksync-era/issues/240)) ([4a5c54c](https://github.com/matter-labs/zksync-era/commit/4a5c54c48bbc100c29fa719c4b1dc3535743003d)) +* **docs:** Add links to setup-data keys ([#360](https://github.com/matter-labs/zksync-era/issues/360)) ([1d4fe69](https://github.com/matter-labs/zksync-era/commit/1d4fe697e4e98a8e64642cde4fe202338ce5ec61)) +* **path:** update gpu prover setup data path to remove extra gpu suffix ([#454](https://github.com/matter-labs/zksync-era/issues/454)) ([2e145c1](https://github.com/matter-labs/zksync-era/commit/2e145c192b348b2756acf61fac5bfe0ca5a6575f)) +* **prover-fri:** Update setup loading for node agg circuit ([#323](https://github.com/matter-labs/zksync-era/issues/323)) ([d1034b0](https://github.com/matter-labs/zksync-era/commit/d1034b05754219b603508ef79c114d908c94c1e9)) +* **prover-logging:** tasks_allowed_to_finish set to true for 1 off jobs ([#227](https://github.com/matter-labs/zksync-era/issues/227)) ([0fac66f](https://github.com/matter-labs/zksync-era/commit/0fac66f5ff86cc801ea0bb6f9272cb397cd03a95)) +* Sync protocol version between consensus and server blocks ([#568](https://github.com/matter-labs/zksync-era/issues/568)) ([56776f9](https://github.com/matter-labs/zksync-era/commit/56776f929f547b1a91c5b70f89e87ef7dc25c65a)) +* Update prover to use the correct storage oracle ([#446](https://github.com/matter-labs/zksync-era/issues/446)) ([835dd82](https://github.com/matter-labs/zksync-era/commit/835dd828ef5610a446ec8c456e4df1def0e213ab)) + ## [9.1.0](https://github.com/matter-labs/zksync-era/compare/prover-v9.0.0...prover-v9.1.0) (2023-12-05) From 83d5a2a869177d4f512fe709b54f09c2269db477 Mon Sep 17 00:00:00 2001 From: pompon0 Date: Tue, 5 Dec 2023 18:06:51 +0300 Subject: [PATCH 047/268] refactor: Removed protobuf encoding from zksync_types (#562) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Removed protobuf encoding from zksync_types. ## Why ❔ To make zksync_types have less dependencies. --- Cargo.lock | 5 +- core/lib/dal/Cargo.toml | 10 +- core/lib/{types => dal}/build.rs | 4 +- core/lib/dal/src/blocks_dal.rs | 5 +- core/lib/dal/src/models/mod.rs | 1 + .../src => dal/src/models}/proto/mod.proto | 2 +- core/lib/dal/src/models/proto/mod.rs | 2 + core/lib/dal/src/models/storage_sync.rs | 146 ++++++++++++++---- core/lib/dal/src/sync_dal.rs | 6 +- core/lib/types/Cargo.toml | 3 - core/lib/types/src/api/en.rs | 8 +- core/lib/types/src/block.rs | 42 ----- core/lib/types/src/lib.rs | 2 - core/lib/types/src/proto/mod.rs | 2 - .../lib/zksync_core/src/sync_layer/fetcher.rs | 25 +-- .../src/sync_layer/gossip/conversions.rs | 15 +- .../src/sync_layer/gossip/storage/mod.rs | 8 +- .../src/sync_layer/gossip/tests.rs | 6 +- .../zksync_core/src/sync_layer/sync_action.rs | 7 +- 19 files changed, 181 insertions(+), 118 deletions(-) rename core/lib/{types => dal}/build.rs (77%) rename core/lib/{types/src => dal/src/models}/proto/mod.proto (90%) create mode 100644 core/lib/dal/src/models/proto/mod.rs delete mode 100644 core/lib/types/src/proto/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 5b7b6cb3424d..3fbf3deb35e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8828,6 +8828,7 @@ dependencies = [ "itertools", "num 0.3.1", "once_cell", + "prost", "rand 0.8.5", "serde", "serde_json", @@ -8838,8 +8839,11 @@ dependencies = [ "tracing", "url", "vise", + "zksync_consensus_roles", "zksync_contracts", "zksync_health_check", + "zksync_protobuf", + "zksync_protobuf_build", "zksync_system_constants", "zksync_types", "zksync_utils", @@ -9158,7 +9162,6 @@ dependencies = [ "num_enum", "once_cell", "parity-crypto", - "prost", "rlp", "secp256k1 0.27.0", "serde", diff --git a/core/lib/dal/Cargo.toml b/core/lib/dal/Cargo.toml index 616e8a32a9ef..d73837cdc29c 100644 --- a/core/lib/dal/Cargo.toml +++ b/core/lib/dal/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "zksync_dal" version = "0.1.0" -edition = "2018" +edition = "2021" authors = ["The Matter Labs Team "] homepage = "https://zksync.io/" repository = "https://github.com/matter-labs/zksync-era" @@ -9,6 +9,8 @@ license = "MIT OR Apache-2.0" keywords = ["blockchain", "zksync"] categories = ["cryptography"] +links = "zksync_dal_proto" + [dependencies] vise = { git = "https://github.com/matter-labs/vise.git", version = "0.1.0", rev = "dd05139b76ab0843443ab3ff730174942c825dae" } zksync_utils = { path = "../utils" } @@ -16,11 +18,14 @@ zksync_system_constants = { path = "../constants" } zksync_contracts = { path = "../contracts" } zksync_types = { path = "../types" } zksync_health_check = { path = "../health_check" } +zksync_consensus_roles = { version = "0.1.0", git = "https://github.com/matter-labs/era-consensus.git", rev = "da015d4c94b19962bc11622b6cc256e214256555" } +zksync_protobuf = { version = "0.1.0", git = "https://github.com/matter-labs/era-consensus.git", rev = "da015d4c94b19962bc11622b6cc256e214256555" } itertools = "0.10.1" thiserror = "1.0" anyhow = "1.0" url = "2" +prost = "0.12.1" rand = "0.8" tokio = { version = "1", features = ["full"] } sqlx = { version = "0.5.13", default-features = false, features = [ @@ -46,3 +51,6 @@ tracing = "0.1" [dev-dependencies] assert_matches = "1.5.0" + +[build-dependencies] +zksync_protobuf_build = { version = "0.1.0", git = "https://github.com/matter-labs/era-consensus.git", rev = "da015d4c94b19962bc11622b6cc256e214256555" } diff --git a/core/lib/types/build.rs b/core/lib/dal/build.rs similarity index 77% rename from core/lib/types/build.rs rename to core/lib/dal/build.rs index 464a905e47aa..66896b3e02ff 100644 --- a/core/lib/types/build.rs +++ b/core/lib/dal/build.rs @@ -1,8 +1,8 @@ //! Generates rust code from protobufs. fn main() { zksync_protobuf_build::Config { - input_root: "src/proto".into(), - proto_root: "zksync/types".into(), + input_root: "src/models/proto".into(), + proto_root: "zksync/dal".into(), dependencies: vec!["::zksync_consensus_roles::proto".parse().unwrap()], protobuf_crate: "::zksync_protobuf".parse().unwrap(), is_public: true, diff --git a/core/lib/dal/src/blocks_dal.rs b/core/lib/dal/src/blocks_dal.rs index ca5018ae51e6..c60d52e197b0 100644 --- a/core/lib/dal/src/blocks_dal.rs +++ b/core/lib/dal/src/blocks_dal.rs @@ -10,12 +10,13 @@ use sqlx::Row; use zksync_types::{ aggregated_operations::AggregatedActionType, - block::{BlockGasCount, ConsensusBlockFields, L1BatchHeader, MiniblockHeader}, + block::{BlockGasCount, L1BatchHeader, MiniblockHeader}, commitment::{L1BatchMetadata, L1BatchWithMetadata}, Address, L1BatchNumber, LogQuery, MiniblockNumber, ProtocolVersionId, H256, MAX_GAS_PER_PUBDATA_BYTE, U256, }; +pub use crate::models::storage_sync::ConsensusBlockFields; use crate::{ instrument::InstrumentExt, models::storage_block::{StorageL1Batch, StorageL1BatchHeader, StorageMiniblockHeader}, @@ -405,7 +406,7 @@ impl BlocksDal<'_, '_> { let result = sqlx::query!( "UPDATE miniblocks SET consensus = $2 WHERE number = $1", miniblock_number.0 as i64, - serde_json::to_value(consensus).unwrap(), + zksync_protobuf::serde::serialize(consensus, serde_json::value::Serializer).unwrap(), ) .execute(self.storage.conn()) .await?; diff --git a/core/lib/dal/src/models/mod.rs b/core/lib/dal/src/models/mod.rs index f6ebb6fc7810..4e3e08539911 100644 --- a/core/lib/dal/src/models/mod.rs +++ b/core/lib/dal/src/models/mod.rs @@ -1,3 +1,4 @@ +mod proto; pub mod storage_block; pub mod storage_eth_tx; pub mod storage_event; diff --git a/core/lib/types/src/proto/mod.proto b/core/lib/dal/src/models/proto/mod.proto similarity index 90% rename from core/lib/types/src/proto/mod.proto rename to core/lib/dal/src/models/proto/mod.proto index 2fc03e285d35..b817d35e0323 100644 --- a/core/lib/types/src/proto/mod.proto +++ b/core/lib/dal/src/models/proto/mod.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package zksync.types; +package zksync.dal; import "zksync/roles/validator.proto"; diff --git a/core/lib/dal/src/models/proto/mod.rs b/core/lib/dal/src/models/proto/mod.rs new file mode 100644 index 000000000000..29f7c04d5d64 --- /dev/null +++ b/core/lib/dal/src/models/proto/mod.rs @@ -0,0 +1,2 @@ +#![allow(warnings)] +include!(concat!(env!("OUT_DIR"), "/src/models/proto/gen.rs")); diff --git a/core/lib/dal/src/models/storage_sync.rs b/core/lib/dal/src/models/storage_sync.rs index b49cfd98acc5..3415cb9b264e 100644 --- a/core/lib/dal/src/models/storage_sync.rs +++ b/core/lib/dal/src/models/storage_sync.rs @@ -1,8 +1,9 @@ -use std::convert::TryInto; - +use anyhow::Context as _; +use zksync_consensus_roles::validator; use zksync_contracts::BaseSystemContractsHashes; -use zksync_types::api::en::SyncBlock; -use zksync_types::{Address, L1BatchNumber, MiniblockNumber, Transaction, H256}; +use zksync_protobuf::{read_required, ProtoFmt}; +use zksync_types::api::en; +use zksync_types::{Address, L1BatchNumber, MiniblockNumber, Transaction, H160, H256}; #[derive(Debug, Clone, sqlx::FromRow)] pub struct StorageSyncBlock { @@ -24,45 +25,126 @@ pub struct StorageSyncBlock { pub consensus: Option, } +fn parse_h256(bytes: &[u8]) -> anyhow::Result { + Ok(<[u8; 32]>::try_from(bytes).context("invalid size")?.into()) +} + +fn parse_h160(bytes: &[u8]) -> anyhow::Result { + Ok(<[u8; 20]>::try_from(bytes).context("invalid size")?.into()) +} + impl StorageSyncBlock { pub(crate) fn into_sync_block( self, current_operator_address: Address, transactions: Option>, - ) -> SyncBlock { - let number = self.number; - - SyncBlock { - number: MiniblockNumber(self.number as u32), - l1_batch_number: L1BatchNumber(self.l1_batch_number as u32), + ) -> anyhow::Result { + Ok(en::SyncBlock { + number: MiniblockNumber(self.number.try_into().context("number")?), + l1_batch_number: L1BatchNumber( + self.l1_batch_number.try_into().context("l1_batch_number")?, + ), last_in_batch: self .last_batch_miniblock - .map(|n| n == number) + .map(|n| n == self.number) .unwrap_or(false), - timestamp: self.timestamp as u64, - root_hash: self.root_hash.as_deref().map(H256::from_slice), - l1_gas_price: self.l1_gas_price as u64, - l2_fair_gas_price: self.l2_fair_gas_price as u64, - // TODO (SMA-1635): Make these filed non optional in database + timestamp: self.timestamp.try_into().context("timestamp")?, + root_hash: self + .root_hash + .map(|h| parse_h256(&h)) + .transpose() + .context("root_hash")?, + l1_gas_price: self.l1_gas_price.try_into().context("l1_gas_price")?, + l2_fair_gas_price: self + .l2_fair_gas_price + .try_into() + .context("l2_fair_gas_price")?, + // TODO (SMA-1635): Make these fields non optional in database base_system_contracts_hashes: BaseSystemContractsHashes { - bootloader: self - .bootloader_code_hash - .map(|bootloader_code_hash| H256::from_slice(&bootloader_code_hash)) - .expect("Should not be none"), - default_aa: self - .default_aa_code_hash - .map(|default_aa_code_hash| H256::from_slice(&default_aa_code_hash)) - .expect("Should not be none"), + bootloader: parse_h256( + &self + .bootloader_code_hash + .context("bootloader_code_hash should not be none")?, + ) + .context("bootloader_code_hash")?, + default_aa: parse_h256( + &self + .default_aa_code_hash + .context("default_aa_code_hash should not be none")?, + ) + .context("default_aa_code_hash")?, + }, + operator_address: match self.fee_account_address { + Some(addr) => parse_h160(&addr).context("fee_account_address")?, + None => current_operator_address, }, - operator_address: self - .fee_account_address - .map(|fee_account_address| Address::from_slice(&fee_account_address)) - .unwrap_or(current_operator_address), transactions, - virtual_blocks: Some(self.virtual_blocks as u32), - hash: Some(H256::from_slice(&self.hash)), - protocol_version: (self.protocol_version as u16).try_into().unwrap(), - consensus: self.consensus.map(|v| serde_json::from_value(v).unwrap()), + virtual_blocks: Some(self.virtual_blocks.try_into().context("virtual_blocks")?), + hash: Some(parse_h256(&self.hash).context("hash")?), + protocol_version: u16::try_from(self.protocol_version) + .context("protocol_version")? + .try_into() + .context("protocol_version")?, + consensus: match self.consensus { + None => None, + Some(v) => { + let v: ConsensusBlockFields = + zksync_protobuf::serde::deserialize(v).context("consensus")?; + Some(v.encode()) + } + }, + }) + } +} + +/// Consensus-related L2 block (= miniblock) fields. +#[derive(Debug, Clone, PartialEq)] +pub struct ConsensusBlockFields { + /// Hash of the previous consensus block. + pub parent: validator::BlockHeaderHash, + /// Quorum certificate for the block. + pub justification: validator::CommitQC, +} + +impl ConsensusBlockFields { + pub fn encode(&self) -> en::ConsensusBlockFields { + en::ConsensusBlockFields(zksync_protobuf::encode(self).into()) + } + pub fn decode(x: &en::ConsensusBlockFields) -> anyhow::Result { + zksync_protobuf::decode(&x.0 .0) + } +} + +impl ProtoFmt for ConsensusBlockFields { + type Proto = crate::models::proto::ConsensusBlockFields; + fn read(r: &Self::Proto) -> anyhow::Result { + Ok(Self { + parent: read_required(&r.parent).context("parent")?, + justification: read_required(&r.justification).context("justification")?, + }) + } + fn build(&self) -> Self::Proto { + Self::Proto { + parent: Some(self.parent.build()), + justification: Some(self.justification.build()), } } } + +#[cfg(test)] +mod tests { + use super::ConsensusBlockFields; + use rand::Rng; + use zksync_consensus_roles::validator; + + #[tokio::test] + async fn encode_decode() { + let rng = &mut rand::thread_rng(); + let block = rng.gen::(); + let want = ConsensusBlockFields { + parent: block.header.parent, + justification: block.justification, + }; + assert_eq!(want, ConsensusBlockFields::decode(&want.encode()).unwrap()); + } +} diff --git a/core/lib/dal/src/sync_dal.rs b/core/lib/dal/src/sync_dal.rs index 7b7c13594140..991469db4692 100644 --- a/core/lib/dal/src/sync_dal.rs +++ b/core/lib/dal/src/sync_dal.rs @@ -4,7 +4,7 @@ use crate::{ instrument::InstrumentExt, metrics::MethodLatency, models::{storage_sync::StorageSyncBlock, storage_transaction::StorageTransaction}, - SqlxError, StorageProcessor, + StorageProcessor, }; /// DAL subset dedicated to the EN synchronization. @@ -19,7 +19,7 @@ impl SyncDal<'_, '_> { block_number: MiniblockNumber, current_operator_address: Address, include_transactions: bool, - ) -> Result, SqlxError> { + ) -> anyhow::Result> { let latency = MethodLatency::new("sync_dal_sync_block"); let storage_block_details = sqlx::query_as!( StorageSyncBlock, @@ -65,7 +65,7 @@ impl SyncDal<'_, '_> { } else { None }; - Some(storage_block_details.into_sync_block(current_operator_address, transactions)) + Some(storage_block_details.into_sync_block(current_operator_address, transactions)?) } else { None }; diff --git a/core/lib/types/Cargo.toml b/core/lib/types/Cargo.toml index 3e81701a04a8..aef8ce52d3a9 100644 --- a/core/lib/types/Cargo.toml +++ b/core/lib/types/Cargo.toml @@ -10,8 +10,6 @@ keywords = ["blockchain", "zksync"] categories = ["cryptography"] readme = "README.md" -links = "zksync_types_proto" - [dependencies] zksync_system_constants = { path = "../constants" } zksync_utils = { path = "../utils" } @@ -30,7 +28,6 @@ anyhow = "1.0.75" chrono = { version = "0.4", features = ["serde"] } num = { version = "0.3.1", features = ["serde"] } once_cell = "1.7" -prost = "0.12.1" rlp = "0.5" serde = "1.0.90" serde_json = "1.0.0" diff --git a/core/lib/types/src/api/en.rs b/core/lib/types/src/api/en.rs index aa3d2955e2e0..c1899c2bf3c7 100644 --- a/core/lib/types/src/api/en.rs +++ b/core/lib/types/src/api/en.rs @@ -5,7 +5,13 @@ use zk_evm::ethereum_types::Address; use zksync_basic_types::{L1BatchNumber, MiniblockNumber, H256}; use zksync_contracts::BaseSystemContractsHashes; -use crate::{block::ConsensusBlockFields, ProtocolVersionId}; +use crate::ProtocolVersionId; + +/// Protobuf-encoded consensus-related L2 block (= miniblock) fields. +/// See `zksync_dal::models::storage_sync::ConsensusBlockFields`. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(transparent)] +pub struct ConsensusBlockFields(pub zksync_basic_types::Bytes); /// Representation of the L2 block, as needed for the EN synchronization. /// This structure has several fields that describe *L1 batch* rather than diff --git a/core/lib/types/src/block.rs b/core/lib/types/src/block.rs index b40264688684..e61a56d2c916 100644 --- a/core/lib/types/src/block.rs +++ b/core/lib/types/src/block.rs @@ -1,13 +1,10 @@ -use anyhow::Context as _; use serde::{Deserialize, Serialize}; use zksync_system_constants::SYSTEM_BLOCK_INFO_BLOCK_NUMBER_MULTIPLIER; use std::{fmt, ops}; use zksync_basic_types::{H2048, H256, U256}; -use zksync_consensus_roles::validator; use zksync_contracts::BaseSystemContractsHashes; -use zksync_protobuf::{read_required, ProtoFmt}; use crate::{ l2_to_l1_log::{SystemL2ToL1Log, UserL2ToL1Log}, @@ -88,45 +85,6 @@ pub struct MiniblockHeader { pub virtual_blocks: u32, } -/// Consensus-related L2 block (= miniblock) fields. -#[derive(Debug, Clone, PartialEq)] -pub struct ConsensusBlockFields { - /// Hash of the previous consensus block. - pub parent: validator::BlockHeaderHash, - /// Quorum certificate for the block. - pub justification: validator::CommitQC, -} - -impl ProtoFmt for ConsensusBlockFields { - type Proto = crate::proto::ConsensusBlockFields; - - fn read(proto: &Self::Proto) -> anyhow::Result { - Ok(Self { - parent: read_required(&proto.parent).context("parent")?, - justification: read_required(&proto.justification).context("justification")?, - }) - } - - fn build(&self) -> Self::Proto { - Self::Proto { - parent: Some(self.parent.build()), - justification: Some(self.justification.build()), - } - } -} - -impl Serialize for ConsensusBlockFields { - fn serialize(&self, s: S) -> Result { - zksync_protobuf::serde::serialize(self, s) - } -} - -impl<'de> Deserialize<'de> for ConsensusBlockFields { - fn deserialize>(d: D) -> Result { - zksync_protobuf::serde::deserialize(d) - } -} - /// Data needed to execute a miniblock in the VM. #[derive(Debug)] pub struct MiniblockExecutionData { diff --git a/core/lib/types/src/lib.rs b/core/lib/types/src/lib.rs index 22904eb71b8c..4715a2f86dac 100644 --- a/core/lib/types/src/lib.rs +++ b/core/lib/types/src/lib.rs @@ -62,8 +62,6 @@ pub mod utils; pub mod vk_transform; pub mod vm_version; -mod proto; - /// Denotes the first byte of the special zkSync's EIP-712-signed transaction. pub const EIP_712_TX_TYPE: u8 = 0x71; diff --git a/core/lib/types/src/proto/mod.rs b/core/lib/types/src/proto/mod.rs deleted file mode 100644 index 660bf4c5b4cc..000000000000 --- a/core/lib/types/src/proto/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -#![allow(warnings)] -include!(concat!(env!("OUT_DIR"), "/src/proto/gen.rs")); diff --git a/core/lib/zksync_core/src/sync_layer/fetcher.rs b/core/lib/zksync_core/src/sync_layer/fetcher.rs index 9cdd7e64fd1e..3adbc8920bf4 100644 --- a/core/lib/zksync_core/src/sync_layer/fetcher.rs +++ b/core/lib/zksync_core/src/sync_layer/fetcher.rs @@ -3,10 +3,9 @@ use tokio::sync::watch; use std::time::Duration; -use zksync_dal::StorageProcessor; +use zksync_dal::{blocks_dal::ConsensusBlockFields, StorageProcessor}; use zksync_types::{ - api::en::SyncBlock, block::ConsensusBlockFields, Address, L1BatchNumber, MiniblockNumber, - ProtocolVersionId, + api::en::SyncBlock, Address, L1BatchNumber, MiniblockNumber, ProtocolVersionId, }; use zksync_web3_decl::jsonrpsee::core::Error as RpcError; @@ -37,9 +36,10 @@ pub(super) struct FetchedBlock { pub consensus: Option, } -impl From for FetchedBlock { - fn from(block: SyncBlock) -> Self { - Self { +impl TryFrom for FetchedBlock { + type Error = anyhow::Error; + fn try_from(block: SyncBlock) -> anyhow::Result { + Ok(Self { number: block.number, l1_batch_number: block.l1_batch_number, last_in_batch: block.last_in_batch, @@ -51,9 +51,14 @@ impl From for FetchedBlock { operator_address: block.operator_address, transactions: block .transactions - .expect("Transactions are always requested"), - consensus: block.consensus, - } + .context("Transactions are always requested")?, + consensus: block + .consensus + .as_ref() + .map(ConsensusBlockFields::decode) + .transpose() + .context("ConsensusBlockFields::decode()")?, + }) } } @@ -273,7 +278,7 @@ impl MainNodeFetcher { request_latency.observe(); let block_number = block.number; - let new_actions = self.cursor.advance(block.into()); + let new_actions = self.cursor.advance(block.try_into()?); tracing::info!( "New miniblock: {block_number} / {}", diff --git a/core/lib/zksync_core/src/sync_layer/gossip/conversions.rs b/core/lib/zksync_core/src/sync_layer/gossip/conversions.rs index ee2286917c15..00c6c6514524 100644 --- a/core/lib/zksync_core/src/sync_layer/gossip/conversions.rs +++ b/core/lib/zksync_core/src/sync_layer/gossip/conversions.rs @@ -1,17 +1,20 @@ //! Conversion logic between server and consensus types. - use anyhow::Context as _; use zksync_consensus_roles::validator::{BlockHeader, BlockNumber, FinalBlock}; -use zksync_types::{ - api::en::SyncBlock, block::ConsensusBlockFields, MiniblockNumber, ProtocolVersionId, -}; +use zksync_dal::blocks_dal::ConsensusBlockFields; +use zksync_types::{api::en, MiniblockNumber, ProtocolVersionId}; use crate::{consensus, sync_layer::fetcher::FetchedBlock}; -pub(super) fn sync_block_to_consensus_block(mut block: SyncBlock) -> anyhow::Result { +pub(super) fn sync_block_to_consensus_block(block: en::SyncBlock) -> anyhow::Result { let number = BlockNumber(block.number.0.into()); - let consensus = block.consensus.take().context("Missing consensus fields")?; + let consensus = block + .consensus + .as_ref() + .context("Missing consensus fields")?; + let consensus = + ConsensusBlockFields::decode(consensus).context("ConsensusBlockFields::decode()")?; let consensus_protocol_version = consensus.justification.message.protocol_version.as_u32(); let block_protocol_version = block.protocol_version as u32; anyhow::ensure!( diff --git a/core/lib/zksync_core/src/sync_layer/gossip/storage/mod.rs b/core/lib/zksync_core/src/sync_layer/gossip/storage/mod.rs index a490147512e4..db36f71d35c7 100644 --- a/core/lib/zksync_core/src/sync_layer/gossip/storage/mod.rs +++ b/core/lib/zksync_core/src/sync_layer/gossip/storage/mod.rs @@ -12,8 +12,9 @@ use zksync_concurrency::{ }; use zksync_consensus_roles::validator::{BlockNumber, FinalBlock}; use zksync_consensus_storage::{BlockStore, StorageError, StorageResult}; +use zksync_dal::blocks_dal::ConsensusBlockFields; use zksync_dal::{ConnectionPool, StorageProcessor}; -use zksync_types::{api::en::SyncBlock, block::ConsensusBlockFields, Address, MiniblockNumber}; +use zksync_types::{api::en::SyncBlock, Address, MiniblockNumber}; #[cfg(test)] mod tests; @@ -157,9 +158,12 @@ impl PostgresBlockStorage { justification: genesis_block.justification.clone(), }; if let Some(actual_consensus_fields) = &actual_consensus_fields { + let actual_consensus_fields = ConsensusBlockFields::decode(actual_consensus_fields) + .context("ConsensusBlockFields::decode()") + .map_err(StorageError::Database)?; // While justifications may differ among nodes for an arbitrary block, we assume that // the genesis block has a hardcoded justification. - if *actual_consensus_fields != expected_consensus_fields { + if actual_consensus_fields != expected_consensus_fields { let err = anyhow::anyhow!( "Genesis block consensus fields in Postgres {actual_consensus_fields:?} do not match \ the configured ones {expected_consensus_fields:?}" diff --git a/core/lib/zksync_core/src/sync_layer/gossip/tests.rs b/core/lib/zksync_core/src/sync_layer/gossip/tests.rs index d8775d3637c8..ddb974849687 100644 --- a/core/lib/zksync_core/src/sync_layer/gossip/tests.rs +++ b/core/lib/zksync_core/src/sync_layer/gossip/tests.rs @@ -9,10 +9,10 @@ use zksync_concurrency::{ctx, scope, testonly::abort_on_panic, time}; use zksync_consensus_executor::testonly::FullValidatorConfig; use zksync_consensus_roles::validator::{self, FinalBlock}; use zksync_consensus_storage::{InMemoryStorage, WriteBlockStore}; +use zksync_dal::blocks_dal::ConsensusBlockFields; use zksync_dal::{ConnectionPool, StorageProcessor}; use zksync_types::{ - api::en::SyncBlock, block::ConsensusBlockFields, Address, L1BatchNumber, MiniblockNumber, - ProtocolVersionId, H256, + api::en::SyncBlock, Address, L1BatchNumber, MiniblockNumber, ProtocolVersionId, H256, }; use super::*; @@ -509,7 +509,7 @@ async fn insert_sync_blocks(pool: ConnectionPool, blocks: Vec, tx_has let (actions_sender, actions) = ActionQueue::new(); let state_keeper = StateKeeperHandles::new(pool.clone(), actions, tx_hashes).await; for block in blocks { - let block_actions = fetcher.advance(block.into()); + let block_actions = fetcher.advance(block.try_into().unwrap()); actions_sender.push_actions(block_actions).await; } diff --git a/core/lib/zksync_core/src/sync_layer/sync_action.rs b/core/lib/zksync_core/src/sync_layer/sync_action.rs index b278cb1c98e6..994676def491 100644 --- a/core/lib/zksync_core/src/sync_layer/sync_action.rs +++ b/core/lib/zksync_core/src/sync_layer/sync_action.rs @@ -1,9 +1,6 @@ use tokio::sync::mpsc; - -use zksync_types::{ - block::ConsensusBlockFields, Address, L1BatchNumber, MiniblockNumber, ProtocolVersionId, - Transaction, -}; +use zksync_dal::blocks_dal::ConsensusBlockFields; +use zksync_types::{Address, L1BatchNumber, MiniblockNumber, ProtocolVersionId, Transaction}; use super::metrics::QUEUE_METRICS; From f6a69e025979a1283964ff69508543ed7606e391 Mon Sep 17 00:00:00 2001 From: Zijing Zhang <50045289+pluveto@users.noreply.github.com> Date: Tue, 5 Dec 2023 23:49:57 +0800 Subject: [PATCH 048/268] fix: use powers array in plonkSetup function (#508) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ This PR modifies the `plonkSetup` function in `run.ts` to use the `powers` array when downloading key files. ## Why ❔ Previously, the function forget to use the argument values `powers`. Now, it will download keys for any powers specified in the `powers` array. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [NA] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --------- Co-authored-by: Igor Aleksanov --- infrastructure/zk/src/run/run.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/infrastructure/zk/src/run/run.ts b/infrastructure/zk/src/run/run.ts index 3e13bd5a2224..3f17fb3e54ce 100644 --- a/infrastructure/zk/src/run/run.ts +++ b/infrastructure/zk/src/run/run.ts @@ -70,7 +70,8 @@ export async function plonkSetup(powers?: number[]) { const URL = 'https://storage.googleapis.com/universal-setup'; fs.mkdirSync('keys/setup', { recursive: true }); process.chdir('keys/setup'); - for (let power = 20; power <= 26; power++) { + for (let i = 0; i < powers.length; i++) { + const power = powers[i]; if (!fs.existsSync(`setup_2^${power}.key`)) { await utils.spawn(`curl -LO ${URL}/setup_2^${power}.key`); await utils.sleep(1); From aeaaecb54b6bd3f173727531418dc242357b2aee Mon Sep 17 00:00:00 2001 From: Lech <88630083+Artemka374@users.noreply.github.com> Date: Tue, 5 Dec 2023 18:46:48 +0200 Subject: [PATCH 049/268] fix: Fix database connections in house keeper (#610) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Use correct connections for databases in house keeper. ## Why ❔ Databases are divided in 2 on mainnet and testnet ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --- .../src/house_keeper/fri_prover_queue_monitor.rs | 9 +++++++-- core/lib/zksync_core/src/lib.rs | 14 +++++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/core/lib/zksync_core/src/house_keeper/fri_prover_queue_monitor.rs b/core/lib/zksync_core/src/house_keeper/fri_prover_queue_monitor.rs index 129f9befbd57..635d630fe1a7 100644 --- a/core/lib/zksync_core/src/house_keeper/fri_prover_queue_monitor.rs +++ b/core/lib/zksync_core/src/house_keeper/fri_prover_queue_monitor.rs @@ -7,6 +7,7 @@ use zksync_prover_utils::periodic_job::PeriodicJob; pub struct FriProverStatsReporter { reporting_interval_ms: u64, prover_connection_pool: ConnectionPool, + db_connection_pool: ConnectionPool, config: FriProverGroupConfig, } @@ -14,11 +15,13 @@ impl FriProverStatsReporter { pub fn new( reporting_interval_ms: u64, prover_connection_pool: ConnectionPool, + db_connection_pool: ConnectionPool, config: FriProverGroupConfig, ) -> Self { Self { reporting_interval_ms, prover_connection_pool, + db_connection_pool, config, } } @@ -85,7 +88,9 @@ impl PeriodicJob for FriProverStatsReporter { // FIXME: refactor metrics here - if let Some(l1_batch_number) = conn + let mut db_conn = self.db_connection_pool.access_storage().await.unwrap(); + + if let Some(l1_batch_number) = db_conn .proof_generation_dal() .get_oldest_unprocessed_batch() .await @@ -96,7 +101,7 @@ impl PeriodicJob for FriProverStatsReporter { ) } - if let Some(l1_batch_number) = conn + if let Some(l1_batch_number) = db_conn .proof_generation_dal() .get_oldest_not_generated_batch() .await diff --git a/core/lib/zksync_core/src/lib.rs b/core/lib/zksync_core/src/lib.rs index 3b00d7cffda1..2389d5761738 100644 --- a/core/lib/zksync_core/src/lib.rs +++ b/core/lib/zksync_core/src/lib.rs @@ -1088,13 +1088,16 @@ async fn add_house_keeper_to_task_futures( .clone() .context("house_keeper_config")?; let postgres_config = configs.postgres_config.clone().context("postgres_config")?; - let connection_pool = ConnectionPool::singleton(postgres_config.replica_url()?) - .build() - .await - .context("failed to build a connection pool")?; + let connection_pool = ConnectionPool::builder( + postgres_config.replica_url()?, + postgres_config.max_connections()?, + ) + .build() + .await + .context("failed to build a connection pool")?; let l1_batch_metrics_reporter = L1BatchMetricsReporter::new( house_keeper_config.l1_batch_metrics_reporting_interval_ms, - connection_pool, + connection_pool.clone(), ); let prover_connection_pool = ConnectionPool::builder( @@ -1192,6 +1195,7 @@ async fn add_house_keeper_to_task_futures( let fri_prover_stats_reporter = FriProverStatsReporter::new( house_keeper_config.fri_prover_stats_reporting_interval_ms, prover_connection_pool.clone(), + connection_pool.clone(), fri_prover_group_config, ); task_futures.push(tokio::spawn(fri_prover_stats_reporter.run())); From b65fedd6894497a4c9fbf38d558ccfaca535d1d2 Mon Sep 17 00:00:00 2001 From: perekopskiy <53865202+perekopskiy@users.noreply.github.com> Date: Tue, 5 Dec 2023 19:06:24 +0200 Subject: [PATCH 050/268] feat(contract-verifier): Support verification for zksolc v1.3.17 (#606) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Adds support for zksolc v1.3.17 to contract-verifier. ## Why ❔ Contract-verifier should support latest version ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --- .github/workflows/ci-core-reusable.yml | 48 +++++++++---------- core/tests/ts-integration/hardhat.config.ts | 6 +-- .../ts-integration/scripts/compile-yul.ts | 2 +- .../tests/api/contract-verification.test.ts | 6 +-- docker/contract-verifier/Dockerfile | 6 ++- 5 files changed, 36 insertions(+), 32 deletions(-) diff --git a/.github/workflows/ci-core-reusable.yml b/.github/workflows/ci-core-reusable.yml index 7ad0e54074cb..341a37a2c8c4 100644 --- a/.github/workflows/ci-core-reusable.yml +++ b/.github/workflows/ci-core-reusable.yml @@ -113,20 +113,20 @@ jobs: run: | sudo apt update && sudo apt install wget -y - mkdir -p $(pwd)/etc/solc-bin/0.8.21 - wget https://github.com/ethereum/solc-bin/raw/gh-pages/linux-amd64/solc-linux-amd64-v0.8.21%2Bcommit.d9974bed - mv solc-linux-amd64-v0.8.21+commit.d9974bed $(pwd)/etc/solc-bin/0.8.21/solc - chmod +x $(pwd)/etc/solc-bin/0.8.21/solc + mkdir -p $(pwd)/etc/solc-bin/0.8.23 + wget https://github.com/ethereum/solc-bin/raw/gh-pages/linux-amd64/solc-linux-amd64-v0.8.23%2Bcommit.f704f362 + mv solc-linux-amd64-v0.8.23+commit.f704f362 $(pwd)/etc/solc-bin/0.8.23/solc + chmod +x $(pwd)/etc/solc-bin/0.8.23/solc - mkdir -p $(pwd)/etc/zksolc-bin/v1.3.16 - wget https://github.com/matter-labs/zksolc-bin/raw/main/linux-amd64/zksolc-linux-amd64-musl-v1.3.16 - mv zksolc-linux-amd64-musl-v1.3.16 $(pwd)/etc/zksolc-bin/v1.3.16/zksolc - chmod +x $(pwd)/etc/zksolc-bin/v1.3.16/zksolc + mkdir -p $(pwd)/etc/zksolc-bin/v1.3.17 + wget https://github.com/matter-labs/zksolc-bin/raw/main/linux-amd64/zksolc-linux-amd64-musl-v1.3.17 + mv zksolc-linux-amd64-musl-v1.3.17 $(pwd)/etc/zksolc-bin/v1.3.17/zksolc + chmod +x $(pwd)/etc/zksolc-bin/v1.3.17/zksolc - mkdir -p $(pwd)/etc/vyper-bin/0.3.3 - wget -O vyper0.3.3 https://github.com/vyperlang/vyper/releases/download/v0.3.3/vyper.0.3.3%2Bcommit.48e326f0.linux - mv vyper0.3.3 $(pwd)/etc/vyper-bin/0.3.3/vyper - chmod +x $(pwd)/etc/vyper-bin/0.3.3/vyper + mkdir -p $(pwd)/etc/vyper-bin/0.3.10 + wget -O vyper0.3.10 https://github.com/vyperlang/vyper/releases/download/v0.3.10/vyper.0.3.10%2Bcommit.91361694.linux + mv vyper0.3.10 $(pwd)/etc/vyper-bin/0.3.10/vyper + chmod +x $(pwd)/etc/vyper-bin/0.3.10/vyper mkdir -p $(pwd)/etc/zkvyper-bin/v1.3.13 wget https://github.com/matter-labs/zkvyper-bin/raw/main/linux-amd64/zkvyper-linux-amd64-musl-v1.3.13 @@ -211,20 +211,20 @@ jobs: run: | sudo apt update && sudo apt install wget -y - mkdir -p $(pwd)/etc/solc-bin/0.8.21 - wget https://github.com/ethereum/solc-bin/raw/gh-pages/linux-amd64/solc-linux-amd64-v0.8.21%2Bcommit.d9974bed - mv solc-linux-amd64-v0.8.21+commit.d9974bed $(pwd)/etc/solc-bin/0.8.21/solc - chmod +x $(pwd)/etc/solc-bin/0.8.21/solc + mkdir -p $(pwd)/etc/solc-bin/0.8.23 + wget https://github.com/ethereum/solc-bin/raw/gh-pages/linux-amd64/solc-linux-amd64-v0.8.23%2Bcommit.f704f362 + mv solc-linux-amd64-v0.8.23+commit.f704f362 $(pwd)/etc/solc-bin/0.8.23/solc + chmod +x $(pwd)/etc/solc-bin/0.8.23/solc - mkdir -p $(pwd)/etc/zksolc-bin/v1.3.16 - wget https://github.com/matter-labs/zksolc-bin/raw/main/linux-amd64/zksolc-linux-amd64-musl-v1.3.16 - mv zksolc-linux-amd64-musl-v1.3.16 $(pwd)/etc/zksolc-bin/v1.3.16/zksolc - chmod +x $(pwd)/etc/zksolc-bin/v1.3.16/zksolc + mkdir -p $(pwd)/etc/zksolc-bin/v1.3.17 + wget https://github.com/matter-labs/zksolc-bin/raw/main/linux-amd64/zksolc-linux-amd64-musl-v1.3.17 + mv zksolc-linux-amd64-musl-v1.3.17 $(pwd)/etc/zksolc-bin/v1.3.17/zksolc + chmod +x $(pwd)/etc/zksolc-bin/v1.3.17/zksolc - mkdir -p $(pwd)/etc/vyper-bin/0.3.3 - wget -O vyper0.3.3 https://github.com/vyperlang/vyper/releases/download/v0.3.3/vyper.0.3.3%2Bcommit.48e326f0.linux - mv vyper0.3.3 $(pwd)/etc/vyper-bin/0.3.3/vyper - chmod +x $(pwd)/etc/vyper-bin/0.3.3/vyper + mkdir -p $(pwd)/etc/vyper-bin/0.3.10 + wget -O vyper0.3.10 https://github.com/vyperlang/vyper/releases/download/v0.3.10/vyper.0.3.10%2Bcommit.91361694.linux + mv vyper0.3.10 $(pwd)/etc/vyper-bin/0.3.10/vyper + chmod +x $(pwd)/etc/vyper-bin/0.3.10/vyper mkdir -p $(pwd)/etc/zkvyper-bin/v1.3.11 wget https://github.com/matter-labs/zkvyper-bin/raw/main/linux-amd64/zkvyper-linux-amd64-musl-v1.3.11 diff --git a/core/tests/ts-integration/hardhat.config.ts b/core/tests/ts-integration/hardhat.config.ts index 166feea91d9a..4840e46e82b0 100644 --- a/core/tests/ts-integration/hardhat.config.ts +++ b/core/tests/ts-integration/hardhat.config.ts @@ -4,7 +4,7 @@ import '@matterlabs/hardhat-zksync-vyper'; export default { zksolc: { - version: '1.3.16', + version: '1.3.17', compilerSource: 'binary', settings: { isSystem: true @@ -20,9 +20,9 @@ export default { } }, solidity: { - version: '0.8.21' + version: '0.8.23' }, vyper: { - version: '0.3.3' + version: '0.3.10' } }; diff --git a/core/tests/ts-integration/scripts/compile-yul.ts b/core/tests/ts-integration/scripts/compile-yul.ts index 26f779878ae2..f92c259723c5 100644 --- a/core/tests/ts-integration/scripts/compile-yul.ts +++ b/core/tests/ts-integration/scripts/compile-yul.ts @@ -7,7 +7,7 @@ import { getZksolcUrl, saltFromUrl } from '@matterlabs/hardhat-zksync-solc'; import { getCompilersDir } from 'hardhat/internal/util/global-dir'; import path from 'path'; -const COMPILER_VERSION = '1.3.16'; +const COMPILER_VERSION = '1.3.17'; const IS_COMPILER_PRE_RELEASE = false; async function compilerLocation(): Promise { diff --git a/core/tests/ts-integration/tests/api/contract-verification.test.ts b/core/tests/ts-integration/tests/api/contract-verification.test.ts index cfda8a810740..984361fb7b31 100644 --- a/core/tests/ts-integration/tests/api/contract-verification.test.ts +++ b/core/tests/ts-integration/tests/api/contract-verification.test.ts @@ -9,11 +9,11 @@ import { sleep } from 'zksync-web3/build/src/utils'; // Regular expression to match ISO dates. const DATE_REGEX = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{6})?/; -const ZKSOLC_VERSION = 'v1.3.16'; -const SOLC_VERSION = '0.8.21'; +const ZKSOLC_VERSION = 'v1.3.17'; +const SOLC_VERSION = '0.8.23'; const ZKVYPER_VERSION = 'v1.3.13'; -const VYPER_VERSION = '0.3.3'; +const VYPER_VERSION = '0.3.10'; type HttpMethod = 'POST' | 'GET'; diff --git a/docker/contract-verifier/Dockerfile b/docker/contract-verifier/Dockerfile index 1f244b389066..21ce6946489b 100644 --- a/docker/contract-verifier/Dockerfile +++ b/docker/contract-verifier/Dockerfile @@ -24,7 +24,7 @@ RUN apt-get update && apt-get install -y curl libpq5 ca-certificates wget python # install zksolc 1.3.x RUN skip_versions="v1.3.12 v1.3.15" && \ - for VERSION in $(seq -f "v1.3.%g" 0 16); do \ + for VERSION in $(seq -f "v1.3.%g" 0 17); do \ if echo " $skip_versions " | grep -q -w " $VERSION "; then \ continue; \ fi; \ @@ -53,6 +53,10 @@ RUN mkdir -p /etc/vyper-bin/0.3.9 \ && wget -O vyper0.3.9 https://github.com/vyperlang/vyper/releases/download/v0.3.9/vyper.0.3.9%2Bcommit.66b96705.linux \ && mv vyper0.3.9 /etc/vyper-bin/0.3.9/vyper \ && chmod +x /etc/vyper-bin/0.3.9/vyper +RUN mkdir -p /etc/vyper-bin/0.3.10 \ + && wget -O vyper0.3.10 https://github.com/vyperlang/vyper/releases/download/v0.3.10/vyper.0.3.10%2Bcommit.91361694.linux \ + && mv vyper0.3.10 /etc/vyper-bin/0.3.10/vyper \ + && chmod +x /etc/vyper-bin/0.3.10/vyper COPY --from=builder /usr/src/zksync/target/release/zksync_contract_verifier /usr/bin/ COPY etc/system-contracts/bootloader/build/artifacts/ /etc/system-contracts/bootloader/build/artifacts/ From b7007a3ab5499634ab17b03efcbfa096d55408f8 Mon Sep 17 00:00:00 2001 From: zksync-era-bot <147085853+zksync-era-bot@users.noreply.github.com> Date: Tue, 5 Dec 2023 18:45:01 +0100 Subject: [PATCH 051/268] chore(main): release core 18.6.0 (#613) :robot: I have created a release *beep* *boop* --- ## [18.6.0](https://github.com/matter-labs/zksync-era/compare/core-v18.5.0...core-v18.6.0) (2023-12-05) ### Features * **contract-verifier:** Support verification for zksolc v1.3.17 ([#606](https://github.com/matter-labs/zksync-era/issues/606)) ([b65fedd](https://github.com/matter-labs/zksync-era/commit/b65fedd6894497a4c9fbf38d558ccfaca535d1d2)) ### Bug Fixes * Fix database connections in house keeper ([#610](https://github.com/matter-labs/zksync-era/issues/610)) ([aeaaecb](https://github.com/matter-labs/zksync-era/commit/aeaaecb54b6bd3f173727531418dc242357b2aee)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --- .github/release-please/manifest.json | 2 +- core/CHANGELOG.md | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/release-please/manifest.json b/.github/release-please/manifest.json index 4d3b72a795fd..bce645236442 100644 --- a/.github/release-please/manifest.json +++ b/.github/release-please/manifest.json @@ -1,5 +1,5 @@ { "sdk/zksync-rs": "0.4.0", - "core": "18.5.0", + "core": "18.6.0", "prover": "10.0.0" } diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index cc48a8b8c8db..41439ca46514 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [18.6.0](https://github.com/matter-labs/zksync-era/compare/core-v18.5.0...core-v18.6.0) (2023-12-05) + + +### Features + +* **contract-verifier:** Support verification for zksolc v1.3.17 ([#606](https://github.com/matter-labs/zksync-era/issues/606)) ([b65fedd](https://github.com/matter-labs/zksync-era/commit/b65fedd6894497a4c9fbf38d558ccfaca535d1d2)) + + +### Bug Fixes + +* Fix database connections in house keeper ([#610](https://github.com/matter-labs/zksync-era/issues/610)) ([aeaaecb](https://github.com/matter-labs/zksync-era/commit/aeaaecb54b6bd3f173727531418dc242357b2aee)) + ## [18.5.0](https://github.com/matter-labs/zksync-era/compare/core-v18.4.0...core-v18.5.0) (2023-12-05) From 684e933fdd177eb323ad75df0accef8821dd0fe1 Mon Sep 17 00:00:00 2001 From: Stanislav Bezkorovainyi Date: Wed, 6 Dec 2023 09:26:54 +0100 Subject: [PATCH 052/268] chore: Mainnet upgrade calldata (#564) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Includes mainnet upgrade preparation as well as some minor fixes for the upgrade tool ## Why ❔ ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zk fmt` and `zk lint`. --------- Co-authored-by: koloz --- contracts | 2 +- .../1699353977-boojum/mainnet2/crypto.json | 11 + .../1699353977-boojum/mainnet2/facetCuts.json | 177 ++++++++++ .../1699353977-boojum/mainnet2/facets.json | 18 + .../1699353977-boojum/mainnet2/l2Upgrade.json | 323 ++++++++++++++++++ .../mainnet2/transactions.json | 235 +++++++++++++ infrastructure/protocol-upgrade/README.md | 2 +- .../protocol-upgrade/src/crypto/crypto.ts | 2 +- 8 files changed, 767 insertions(+), 3 deletions(-) create mode 100644 etc/upgrades/1699353977-boojum/mainnet2/crypto.json create mode 100644 etc/upgrades/1699353977-boojum/mainnet2/facetCuts.json create mode 100644 etc/upgrades/1699353977-boojum/mainnet2/facets.json create mode 100644 etc/upgrades/1699353977-boojum/mainnet2/l2Upgrade.json create mode 100644 etc/upgrades/1699353977-boojum/mainnet2/transactions.json diff --git a/contracts b/contracts index 3e2bee96e412..a8429e8ec10c 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit 3e2bee96e412bac7c0a58c4b919837b59e9af36e +Subproject commit a8429e8ec10cb43edef1b1e8bb9b4b480d09222d diff --git a/etc/upgrades/1699353977-boojum/mainnet2/crypto.json b/etc/upgrades/1699353977-boojum/mainnet2/crypto.json new file mode 100644 index 000000000000..e9d650053950 --- /dev/null +++ b/etc/upgrades/1699353977-boojum/mainnet2/crypto.json @@ -0,0 +1,11 @@ +{ + "verifier": { + "address": "0xB465882F67d236DcC0D090F78ebb0d838e9719D8", + "txHash": "0xf623007b5e569800c688b84d2549cba86e0780c1814a8b586ed93deb131337e0" + }, + "keys": { + "recursionNodeLevelVkHash": "0x5a3ef282b21e12fe1f4438e5bb158fc5060b160559c5158c6389d62d9fe3d080", + "recursionLeafLevelVkHash": "0x14628525c227822148e718ca1138acfc6d25e759e19452455d89f7f610c3dcb8", + "recursionCircuitsSetVksHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } +} \ No newline at end of file diff --git a/etc/upgrades/1699353977-boojum/mainnet2/facetCuts.json b/etc/upgrades/1699353977-boojum/mainnet2/facetCuts.json new file mode 100644 index 000000000000..90dfba4d42e6 --- /dev/null +++ b/etc/upgrades/1699353977-boojum/mainnet2/facetCuts.json @@ -0,0 +1,177 @@ +[ + { + "facet": "0x0000000000000000000000000000000000000000", + "selectors": [ + "0x73fb9297", + "0x36d4eb84", + "0x27ae4c16", + "0x0551448c", + "0x8043760a", + "0xbeda4b12", + "0x17338945", + "0x587809c7" + ], + "action": 2, + "isFreezable": false + }, + { + "facet": "0x0000000000000000000000000000000000000000", + "selectors": [ + "0xcdffacc6", + "0x52ef6b2c", + "0xadfca15e", + "0x7a0ed627", + "0xa7cd63b7", + "0xfe10226d", + "0x79823c9a", + "0x4fc07d75", + "0xd86970d8", + "0xfd791f3c", + "0x9d1b5a81", + "0x7b30c8da", + "0x8665b150", + "0x631f4bac", + "0x0ec6b0b7", + "0x1b60e626", + "0xe39d3bff", + "0x33ce93fe", + "0x0ef240a0", + "0xfe26699e", + "0x39607382", + "0xaf6a2dcd", + "0xa1954fc5", + "0xa39980a0", + "0x46657fe9", + "0x18e3a941", + "0x3db920ce", + "0x29b98c67", + "0xbd7c5412", + "0xc3bbd2d7", + "0xe81e0ba1", + "0xfacd743b", + "0x9cd939e4", + "0x56142d7a", + "0x74f4d30d" + ], + "action": 2, + "isFreezable": false + }, + { + "facet": "0x0000000000000000000000000000000000000000", + "selectors": [ + "0x6c0960f9", + "0xb473318e", + "0x042901c7", + "0x263b7f8e", + "0xe4948f43", + "0xeb672419" + ], + "action": 2, + "isFreezable": false + }, + { + "facet": "0x0000000000000000000000000000000000000000", + "selectors": [ + "0x0c4dd810", + "0xce9dcf16", + "0x7739cbe7", + "0xa9a2d18a" + ], + "action": 2, + "isFreezable": false + }, + { + "facet": "0x0000000000000000000000000000000000000000", + "selectors": [ + "0xe58bb639", + "0xf235757f", + "0x1cc5d103", + "0xbe6f11cf", + "0x4623c91d" + ], + "action": 2, + "isFreezable": false + }, + { + "facet": "0x409560DE546e057ce5bD5dB487EdF2bB5E785baB", + "selectors": [ + "0x0e18b681", + "0xe58bb639", + "0xa9f6d941", + "0x27ae4c16", + "0x4dd18bf5", + "0xf235757f", + "0x1cc5d103", + "0xbe6f11cf", + "0x4623c91d", + "0x17338945" + ], + "action": 0, + "isFreezable": false + }, + { + "facet": "0xF3ACF6a03ea4a914B78Ec788624B25ceC37c14A4", + "selectors": [ + "0xcdffacc6", + "0x52ef6b2c", + "0xadfca15e", + "0x7a0ed627", + "0xa7cd63b7", + "0x79823c9a", + "0x4fc07d75", + "0xd86970d8", + "0xfd791f3c", + "0xe5355c75", + "0x9d1b5a81", + "0x7b30c8da", + "0x8665b150", + "0x631f4bac", + "0x0ec6b0b7", + "0x33ce93fe", + "0xdb1f0bf9", + "0xb8c2f66f", + "0xef3f0bae", + "0xfe26699e", + "0x39607382", + "0xaf6a2dcd", + "0xa1954fc5", + "0x46657fe9", + "0x18e3a941", + "0x29b98c67", + "0xbd7c5412", + "0xc3bbd2d7", + "0xe81e0ba1", + "0xfacd743b", + "0x9cd939e4", + "0x56142d7a", + "0xb22dd78e", + "0x74f4d30d" + ], + "action": 0, + "isFreezable": false + }, + { + "facet": "0x63b5EC36B09384fFA7106A80Ec7cfdFCa521fD08", + "selectors": [ + "0x6c0960f9", + "0xb473318e", + "0x042901c7", + "0x263b7f8e", + "0xe4948f43", + "0xeb672419" + ], + "action": 0, + "isFreezable": true + }, + { + "facet": "0x9e3Fa34a10619fEDd7aE40A3fb86FA515fcfd269", + "selectors": [ + "0x701f58c5", + "0xc3d93e7c", + "0x7f61885c", + "0x97c09d34" + ], + "action": 0, + "isFreezable": true + } +] \ No newline at end of file diff --git a/etc/upgrades/1699353977-boojum/mainnet2/facets.json b/etc/upgrades/1699353977-boojum/mainnet2/facets.json new file mode 100644 index 000000000000..12f1457c6032 --- /dev/null +++ b/etc/upgrades/1699353977-boojum/mainnet2/facets.json @@ -0,0 +1,18 @@ +{ + "ExecutorFacet": { + "address": "0x9e3Fa34a10619fEDd7aE40A3fb86FA515fcfd269", + "txHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + "AdminFacet": { + "address": "0x409560DE546e057ce5bD5dB487EdF2bB5E785baB", + "txHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + "GettersFacet": { + "address": "0xF3ACF6a03ea4a914B78Ec788624B25ceC37c14A4", + "txHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + "MailboxFacet": { + "address": "0x63b5EC36B09384fFA7106A80Ec7cfdFCa521fD08", + "txHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } +} \ No newline at end of file diff --git a/etc/upgrades/1699353977-boojum/mainnet2/l2Upgrade.json b/etc/upgrades/1699353977-boojum/mainnet2/l2Upgrade.json new file mode 100644 index 000000000000..19977b5cc2a7 --- /dev/null +++ b/etc/upgrades/1699353977-boojum/mainnet2/l2Upgrade.json @@ -0,0 +1,323 @@ +{ + "systemContracts": [ + { + "name": "EmptyContract", + "bytecodeHashes": [ + "0x01000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c7" + ], + "address": "0x0000000000000000000000000000000000000000" + }, + { + "name": "Ecrecover", + "bytecodeHashes": [ + "0x010000114daca2ff44f27d543b8ef67d885bfed09a74ba9cb25f5912dd3d739c" + ], + "address": "0x0000000000000000000000000000000000000001" + }, + { + "name": "SHA256", + "bytecodeHashes": [ + "0x010000178d93b2d7d6448866009892223caf018a8e8dbcf090c2b9053a285f8d" + ], + "address": "0x0000000000000000000000000000000000000002" + }, + { + "name": "EcAdd", + "bytecodeHashes": [ + "0x010000c5a85a372f441ac693210a18e683b530bed875fdcab2f7e101b057d433" + ], + "address": "0x0000000000000000000000000000000000000006" + }, + { + "name": "EcMul", + "bytecodeHashes": [ + "0x0100013759b40792c2c3d033990e992e5508263c15252eb2d9bfbba571350675" + ], + "address": "0x0000000000000000000000000000000000000007" + }, + { + "name": "EmptyContract", + "bytecodeHashes": [ + "0x01000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c7" + ], + "address": "0x0000000000000000000000000000000000008001" + }, + { + "name": "AccountCodeStorage", + "bytecodeHashes": [ + "0x0100009bc0511159b5ec703d0c56f87615964017739def4ab1ee606b8ec6458c" + ], + "address": "0x0000000000000000000000000000000000008002" + }, + { + "name": "NonceHolder", + "bytecodeHashes": [ + "0x0100012fa73fa922dd9fabb40d3275ce80396eff6ccf1b452c928c17d98bd470" + ], + "address": "0x0000000000000000000000000000000000008003" + }, + { + "name": "KnownCodesStorage", + "bytecodeHashes": [ + "0x0100008b0ca6c6f277035366e99407fbb4b01e743e80b7d24dea5a3d647b423e" + ], + "address": "0x0000000000000000000000000000000000008004" + }, + { + "name": "ImmutableSimulator", + "bytecodeHashes": [ + "0x01000047a3c40e3f4eb98f14967f141452ae602d8723a10975dc33960911d8c5" + ], + "address": "0x0000000000000000000000000000000000008005" + }, + { + "name": "ContractDeployer", + "bytecodeHashes": [ + "0x010006091341955c8f76409de00549fb00b275166b5a0d0d7b82cbd629bb4212" + ], + "address": "0x0000000000000000000000000000000000008006" + }, + { + "name": "L1Messenger", + "bytecodeHashes": [ + "0x01000301c943edb65f5a0b8cdd806218b8ecf25c022720fe3afe6951f202f3fa" + ], + "address": "0x0000000000000000000000000000000000008008" + }, + { + "name": "MsgValueSimulator", + "bytecodeHashes": [ + "0x0100006fa1591d93fcc4a25e9340ad11d0e825904cd1842b8f7255701e1aacbb" + ], + "address": "0x0000000000000000000000000000000000008009" + }, + { + "name": "L2EthToken", + "bytecodeHashes": [ + "0x01000139b506af2b02225838c5a33e30ace701b44b210a422eedab7dd31c28a3" + ], + "address": "0x000000000000000000000000000000000000800a" + }, + { + "name": "SystemContext", + "bytecodeHashes": [ + "0x0100023ba65021e4689dd1755f82108214a1f25150d439fe58c55cdb1f376436" + ], + "address": "0x000000000000000000000000000000000000800b" + }, + { + "name": "BootloaderUtilities", + "bytecodeHashes": [ + "0x010009759cab4fa9e6ca0784746e1df600ff523f0f90c1e94191755cab4b2ed0" + ], + "address": "0x000000000000000000000000000000000000800c" + }, + { + "name": "EventWriter", + "bytecodeHashes": [ + "0x01000019642d87621fdd82cf65aa9146486c9256d5f8849af9a37c78ef519339" + ], + "address": "0x000000000000000000000000000000000000800d" + }, + { + "name": "Compressor", + "bytecodeHashes": [ + "0x010001b72874590239af612f65d50a35975299f88de022493fe7f0a190e79496" + ], + "address": "0x000000000000000000000000000000000000800e" + }, + { + "name": "ComplexUpgrader", + "bytecodeHashes": [ + "0x0100005bfc0443349233459892b51e9f67e27ac828d44d9c7cba8c8285fd66bc" + ], + "address": "0x000000000000000000000000000000000000800f" + }, + { + "name": "Keccak256", + "bytecodeHashes": [ + "0x0100001fb52ca33668d01c230a1c3b13ede90fe2e37d77222410e9f183cb7a89" + ], + "address": "0x0000000000000000000000000000000000008010" + } + ], + "defaultAA": { + "name": "DefaultAccount", + "bytecodeHashes": [ + "0x01000651c5ae96f2aab07d720439e42491bb44c6384015e3a08e32620a4d582d" + ] + }, + "bootloader": { + "name": "Bootloader", + "bytecodeHashes": [ + "0x01000983d4ac4f797cf5c077e022f72284969b13248c2a8e9846f574bdeb5b88" + ] + }, + "forcedDeployments": [ + { + "bytecodeHash": "0x01000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c7", + "newAddress": "0x0000000000000000000000000000000000000000", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x010000114daca2ff44f27d543b8ef67d885bfed09a74ba9cb25f5912dd3d739c", + "newAddress": "0x0000000000000000000000000000000000000001", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x010000178d93b2d7d6448866009892223caf018a8e8dbcf090c2b9053a285f8d", + "newAddress": "0x0000000000000000000000000000000000000002", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x010000c5a85a372f441ac693210a18e683b530bed875fdcab2f7e101b057d433", + "newAddress": "0x0000000000000000000000000000000000000006", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x0100013759b40792c2c3d033990e992e5508263c15252eb2d9bfbba571350675", + "newAddress": "0x0000000000000000000000000000000000000007", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x01000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c7", + "newAddress": "0x0000000000000000000000000000000000008001", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x0100009bc0511159b5ec703d0c56f87615964017739def4ab1ee606b8ec6458c", + "newAddress": "0x0000000000000000000000000000000000008002", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x0100012fa73fa922dd9fabb40d3275ce80396eff6ccf1b452c928c17d98bd470", + "newAddress": "0x0000000000000000000000000000000000008003", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x0100008b0ca6c6f277035366e99407fbb4b01e743e80b7d24dea5a3d647b423e", + "newAddress": "0x0000000000000000000000000000000000008004", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x01000047a3c40e3f4eb98f14967f141452ae602d8723a10975dc33960911d8c5", + "newAddress": "0x0000000000000000000000000000000000008005", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x010006091341955c8f76409de00549fb00b275166b5a0d0d7b82cbd629bb4212", + "newAddress": "0x0000000000000000000000000000000000008006", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x01000301c943edb65f5a0b8cdd806218b8ecf25c022720fe3afe6951f202f3fa", + "newAddress": "0x0000000000000000000000000000000000008008", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x0100006fa1591d93fcc4a25e9340ad11d0e825904cd1842b8f7255701e1aacbb", + "newAddress": "0x0000000000000000000000000000000000008009", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x01000139b506af2b02225838c5a33e30ace701b44b210a422eedab7dd31c28a3", + "newAddress": "0x000000000000000000000000000000000000800a", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x0100023ba65021e4689dd1755f82108214a1f25150d439fe58c55cdb1f376436", + "newAddress": "0x000000000000000000000000000000000000800b", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x010009759cab4fa9e6ca0784746e1df600ff523f0f90c1e94191755cab4b2ed0", + "newAddress": "0x000000000000000000000000000000000000800c", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x01000019642d87621fdd82cf65aa9146486c9256d5f8849af9a37c78ef519339", + "newAddress": "0x000000000000000000000000000000000000800d", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x010001b72874590239af612f65d50a35975299f88de022493fe7f0a190e79496", + "newAddress": "0x000000000000000000000000000000000000800e", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x0100005bfc0443349233459892b51e9f67e27ac828d44d9c7cba8c8285fd66bc", + "newAddress": "0x000000000000000000000000000000000000800f", + "value": 0, + "input": "0x", + "callConstructor": false + }, + { + "bytecodeHash": "0x0100001fb52ca33668d01c230a1c3b13ede90fe2e37d77222410e9f183cb7a89", + "newAddress": "0x0000000000000000000000000000000000008010", + "value": 0, + "input": "0x", + "callConstructor": false + } + ], + "forcedDeploymentCalldata": "0xe9f18c170000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000005800000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000007c0000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000ac00000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000dc00000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000f40000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010c001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000114daca2ff44f27d543b8ef67d885bfed09a74ba9cb25f5912dd3d739c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000178d93b2d7d6448866009892223caf018a8e8dbcf090c2b9053a285f8d00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000c5a85a372f441ac693210a18e683b530bed875fdcab2f7e101b057d43300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100013759b40792c2c3d033990e992e5508263c15252eb2d9bfbba57135067500000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000080010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100009bc0511159b5ec703d0c56f87615964017739def4ab1ee606b8ec6458c00000000000000000000000000000000000000000000000000000000000080020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100012fa73fa922dd9fabb40d3275ce80396eff6ccf1b452c928c17d98bd47000000000000000000000000000000000000000000000000000000000000080030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100008b0ca6c6f277035366e99407fbb4b01e743e80b7d24dea5a3d647b423e00000000000000000000000000000000000000000000000000000000000080040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000047a3c40e3f4eb98f14967f141452ae602d8723a10975dc33960911d8c500000000000000000000000000000000000000000000000000000000000080050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010006091341955c8f76409de00549fb00b275166b5a0d0d7b82cbd629bb421200000000000000000000000000000000000000000000000000000000000080060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000301c943edb65f5a0b8cdd806218b8ecf25c022720fe3afe6951f202f3fa00000000000000000000000000000000000000000000000000000000000080080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100006fa1591d93fcc4a25e9340ad11d0e825904cd1842b8f7255701e1aacbb00000000000000000000000000000000000000000000000000000000000080090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000139b506af2b02225838c5a33e30ace701b44b210a422eedab7dd31c28a3000000000000000000000000000000000000000000000000000000000000800a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100023ba65021e4689dd1755f82108214a1f25150d439fe58c55cdb1f376436000000000000000000000000000000000000000000000000000000000000800b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010009759cab4fa9e6ca0784746e1df600ff523f0f90c1e94191755cab4b2ed0000000000000000000000000000000000000000000000000000000000000800c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000019642d87621fdd82cf65aa9146486c9256d5f8849af9a37c78ef519339000000000000000000000000000000000000000000000000000000000000800d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010001b72874590239af612f65d50a35975299f88de022493fe7f0a190e79496000000000000000000000000000000000000000000000000000000000000800e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100005bfc0443349233459892b51e9f67e27ac828d44d9c7cba8c8285fd66bc000000000000000000000000000000000000000000000000000000000000800f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100001fb52ca33668d01c230a1c3b13ede90fe2e37d77222410e9f183cb7a8900000000000000000000000000000000000000000000000000000000000080100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000", + "calldata": "0xe9f18c170000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000005800000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000007c0000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000ac00000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000dc00000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000f40000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010c001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000114daca2ff44f27d543b8ef67d885bfed09a74ba9cb25f5912dd3d739c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000178d93b2d7d6448866009892223caf018a8e8dbcf090c2b9053a285f8d00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000c5a85a372f441ac693210a18e683b530bed875fdcab2f7e101b057d43300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100013759b40792c2c3d033990e992e5508263c15252eb2d9bfbba57135067500000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000080010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100009bc0511159b5ec703d0c56f87615964017739def4ab1ee606b8ec6458c00000000000000000000000000000000000000000000000000000000000080020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100012fa73fa922dd9fabb40d3275ce80396eff6ccf1b452c928c17d98bd47000000000000000000000000000000000000000000000000000000000000080030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100008b0ca6c6f277035366e99407fbb4b01e743e80b7d24dea5a3d647b423e00000000000000000000000000000000000000000000000000000000000080040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000047a3c40e3f4eb98f14967f141452ae602d8723a10975dc33960911d8c500000000000000000000000000000000000000000000000000000000000080050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010006091341955c8f76409de00549fb00b275166b5a0d0d7b82cbd629bb421200000000000000000000000000000000000000000000000000000000000080060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000301c943edb65f5a0b8cdd806218b8ecf25c022720fe3afe6951f202f3fa00000000000000000000000000000000000000000000000000000000000080080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100006fa1591d93fcc4a25e9340ad11d0e825904cd1842b8f7255701e1aacbb00000000000000000000000000000000000000000000000000000000000080090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000139b506af2b02225838c5a33e30ace701b44b210a422eedab7dd31c28a3000000000000000000000000000000000000000000000000000000000000800a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100023ba65021e4689dd1755f82108214a1f25150d439fe58c55cdb1f376436000000000000000000000000000000000000000000000000000000000000800b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010009759cab4fa9e6ca0784746e1df600ff523f0f90c1e94191755cab4b2ed0000000000000000000000000000000000000000000000000000000000000800c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000019642d87621fdd82cf65aa9146486c9256d5f8849af9a37c78ef519339000000000000000000000000000000000000000000000000000000000000800d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010001b72874590239af612f65d50a35975299f88de022493fe7f0a190e79496000000000000000000000000000000000000000000000000000000000000800e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100005bfc0443349233459892b51e9f67e27ac828d44d9c7cba8c8285fd66bc000000000000000000000000000000000000000000000000000000000000800f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100001fb52ca33668d01c230a1c3b13ede90fe2e37d77222410e9f183cb7a8900000000000000000000000000000000000000000000000000000000000080100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000", + "tx": { + "txType": 254, + "from": "0x0000000000000000000000000000000000008007", + "to": "0x0000000000000000000000000000000000008006", + "gasLimit": 72000000, + "gasPerPubdataByteLimit": 800, + "maxFeePerGas": 0, + "maxPriorityFeePerGas": 0, + "paymaster": 0, + "nonce": "18", + "value": 0, + "reserved": [ + 0, + 0, + 0, + 0 + ], + "data": "0xe9f18c170000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000005800000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000007c0000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000ac00000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000dc00000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000f40000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010c001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000114daca2ff44f27d543b8ef67d885bfed09a74ba9cb25f5912dd3d739c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000178d93b2d7d6448866009892223caf018a8e8dbcf090c2b9053a285f8d00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000c5a85a372f441ac693210a18e683b530bed875fdcab2f7e101b057d43300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100013759b40792c2c3d033990e992e5508263c15252eb2d9bfbba57135067500000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000080010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100009bc0511159b5ec703d0c56f87615964017739def4ab1ee606b8ec6458c00000000000000000000000000000000000000000000000000000000000080020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100012fa73fa922dd9fabb40d3275ce80396eff6ccf1b452c928c17d98bd47000000000000000000000000000000000000000000000000000000000000080030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100008b0ca6c6f277035366e99407fbb4b01e743e80b7d24dea5a3d647b423e00000000000000000000000000000000000000000000000000000000000080040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000047a3c40e3f4eb98f14967f141452ae602d8723a10975dc33960911d8c500000000000000000000000000000000000000000000000000000000000080050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010006091341955c8f76409de00549fb00b275166b5a0d0d7b82cbd629bb421200000000000000000000000000000000000000000000000000000000000080060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000301c943edb65f5a0b8cdd806218b8ecf25c022720fe3afe6951f202f3fa00000000000000000000000000000000000000000000000000000000000080080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100006fa1591d93fcc4a25e9340ad11d0e825904cd1842b8f7255701e1aacbb00000000000000000000000000000000000000000000000000000000000080090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000139b506af2b02225838c5a33e30ace701b44b210a422eedab7dd31c28a3000000000000000000000000000000000000000000000000000000000000800a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100023ba65021e4689dd1755f82108214a1f25150d439fe58c55cdb1f376436000000000000000000000000000000000000000000000000000000000000800b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010009759cab4fa9e6ca0784746e1df600ff523f0f90c1e94191755cab4b2ed0000000000000000000000000000000000000000000000000000000000000800c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000019642d87621fdd82cf65aa9146486c9256d5f8849af9a37c78ef519339000000000000000000000000000000000000000000000000000000000000800d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010001b72874590239af612f65d50a35975299f88de022493fe7f0a190e79496000000000000000000000000000000000000000000000000000000000000800e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100005bfc0443349233459892b51e9f67e27ac828d44d9c7cba8c8285fd66bc000000000000000000000000000000000000000000000000000000000000800f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100001fb52ca33668d01c230a1c3b13ede90fe2e37d77222410e9f183cb7a8900000000000000000000000000000000000000000000000000000000000080100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000", + "signature": "0x", + "factoryDeps": [], + "paymasterInput": "0x", + "reservedDynamic": "0x" + } +} \ No newline at end of file diff --git a/etc/upgrades/1699353977-boojum/mainnet2/transactions.json b/etc/upgrades/1699353977-boojum/mainnet2/transactions.json new file mode 100644 index 000000000000..cc1554ffeea5 --- /dev/null +++ b/etc/upgrades/1699353977-boojum/mainnet2/transactions.json @@ -0,0 +1,235 @@ +{ + "proposeUpgradeTx": { + "l2ProtocolUpgradeTx": { + "txType": 254, + "from": "0x0000000000000000000000000000000000008007", + "to": "0x0000000000000000000000000000000000008006", + "gasLimit": 72000000, + "gasPerPubdataByteLimit": 800, + "maxFeePerGas": 0, + "maxPriorityFeePerGas": 0, + "paymaster": 0, + "nonce": "18", + "value": 0, + "reserved": [ + 0, + 0, + 0, + 0 + ], + "data": "0xe9f18c170000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000005800000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000007c0000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000ac00000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000dc00000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000f40000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010c001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000114daca2ff44f27d543b8ef67d885bfed09a74ba9cb25f5912dd3d739c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000178d93b2d7d6448866009892223caf018a8e8dbcf090c2b9053a285f8d00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000c5a85a372f441ac693210a18e683b530bed875fdcab2f7e101b057d43300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100013759b40792c2c3d033990e992e5508263c15252eb2d9bfbba57135067500000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000080010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100009bc0511159b5ec703d0c56f87615964017739def4ab1ee606b8ec6458c00000000000000000000000000000000000000000000000000000000000080020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100012fa73fa922dd9fabb40d3275ce80396eff6ccf1b452c928c17d98bd47000000000000000000000000000000000000000000000000000000000000080030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100008b0ca6c6f277035366e99407fbb4b01e743e80b7d24dea5a3d647b423e00000000000000000000000000000000000000000000000000000000000080040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000047a3c40e3f4eb98f14967f141452ae602d8723a10975dc33960911d8c500000000000000000000000000000000000000000000000000000000000080050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010006091341955c8f76409de00549fb00b275166b5a0d0d7b82cbd629bb421200000000000000000000000000000000000000000000000000000000000080060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000301c943edb65f5a0b8cdd806218b8ecf25c022720fe3afe6951f202f3fa00000000000000000000000000000000000000000000000000000000000080080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100006fa1591d93fcc4a25e9340ad11d0e825904cd1842b8f7255701e1aacbb00000000000000000000000000000000000000000000000000000000000080090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000139b506af2b02225838c5a33e30ace701b44b210a422eedab7dd31c28a3000000000000000000000000000000000000000000000000000000000000800a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100023ba65021e4689dd1755f82108214a1f25150d439fe58c55cdb1f376436000000000000000000000000000000000000000000000000000000000000800b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010009759cab4fa9e6ca0784746e1df600ff523f0f90c1e94191755cab4b2ed0000000000000000000000000000000000000000000000000000000000000800c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000019642d87621fdd82cf65aa9146486c9256d5f8849af9a37c78ef519339000000000000000000000000000000000000000000000000000000000000800d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010001b72874590239af612f65d50a35975299f88de022493fe7f0a190e79496000000000000000000000000000000000000000000000000000000000000800e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100005bfc0443349233459892b51e9f67e27ac828d44d9c7cba8c8285fd66bc000000000000000000000000000000000000000000000000000000000000800f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100001fb52ca33668d01c230a1c3b13ede90fe2e37d77222410e9f183cb7a8900000000000000000000000000000000000000000000000000000000000080100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000", + "signature": "0x", + "factoryDeps": [], + "paymasterInput": "0x", + "reservedDynamic": "0x" + }, + "bootloaderHash": "0x01000983d4ac4f797cf5c077e022f72284969b13248c2a8e9846f574bdeb5b88", + "defaultAccountHash": "0x01000651c5ae96f2aab07d720439e42491bb44c6384015e3a08e32620a4d582d", + "verifier": "0xB465882F67d236DcC0D090F78ebb0d838e9719D8", + "verifierParams": { + "recursionNodeLevelVkHash": "0x5a3ef282b21e12fe1f4438e5bb158fc5060b160559c5158c6389d62d9fe3d080", + "recursionLeafLevelVkHash": "0x14628525c227822148e718ca1138acfc6d25e759e19452455d89f7f610c3dcb8", + "recursionCircuitsSetVksHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + "l1ContractsUpgradeCalldata": "0x", + "postUpgradeCalldata": "0x", + "upgradeTimestamp": { + "type": "BigNumber", + "hex": "0x656d9c18" + }, + "factoryDeps": [], + "newProtocolVersion": "18", + "newAllowList": "0x0C0dC1171258694635AA50cec5845aC1031cA6d7" + }, + "l1upgradeCalldata": "0x1ed824a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000168001000983d4ac4f797cf5c077e022f72284969b13248c2a8e9846f574bdeb5b8801000651c5ae96f2aab07d720439e42491bb44c6384015e3a08e32620a4d582d000000000000000000000000b465882f67d236dcc0d090f78ebb0d838e9719d85a3ef282b21e12fe1f4438e5bb158fc5060b160559c5158c6389d62d9fe3d08014628525c227822148e718ca1138acfc6d25e759e19452455d89f7f610c3dcb8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016a000000000000000000000000000000000000000000000000000000000000016c000000000000000000000000000000000000000000000000000000000656d9c1800000000000000000000000000000000000000000000000000000000000000120000000000000000000000000c0dc1171258694635aa50cec5845ac1031ca6d700000000000000000000000000000000000000000000000000000000000000fe0000000000000000000000000000000000000000000000000000000000008007000000000000000000000000000000000000000000000000000000000000800600000000000000000000000000000000000000000000000000000000044aa200000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000001460000000000000000000000000000000000000000000000000000000000000148000000000000000000000000000000000000000000000000000000000000014a000000000000000000000000000000000000000000000000000000000000014c000000000000000000000000000000000000000000000000000000000000011c4e9f18c170000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000005800000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000007c0000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000ac00000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000dc00000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000f40000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010c001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000114daca2ff44f27d543b8ef67d885bfed09a74ba9cb25f5912dd3d739c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000178d93b2d7d6448866009892223caf018a8e8dbcf090c2b9053a285f8d00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000c5a85a372f441ac693210a18e683b530bed875fdcab2f7e101b057d43300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100013759b40792c2c3d033990e992e5508263c15252eb2d9bfbba57135067500000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000080010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100009bc0511159b5ec703d0c56f87615964017739def4ab1ee606b8ec6458c00000000000000000000000000000000000000000000000000000000000080020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100012fa73fa922dd9fabb40d3275ce80396eff6ccf1b452c928c17d98bd47000000000000000000000000000000000000000000000000000000000000080030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100008b0ca6c6f277035366e99407fbb4b01e743e80b7d24dea5a3d647b423e00000000000000000000000000000000000000000000000000000000000080040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000047a3c40e3f4eb98f14967f141452ae602d8723a10975dc33960911d8c500000000000000000000000000000000000000000000000000000000000080050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010006091341955c8f76409de00549fb00b275166b5a0d0d7b82cbd629bb421200000000000000000000000000000000000000000000000000000000000080060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000301c943edb65f5a0b8cdd806218b8ecf25c022720fe3afe6951f202f3fa00000000000000000000000000000000000000000000000000000000000080080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100006fa1591d93fcc4a25e9340ad11d0e825904cd1842b8f7255701e1aacbb00000000000000000000000000000000000000000000000000000000000080090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000139b506af2b02225838c5a33e30ace701b44b210a422eedab7dd31c28a3000000000000000000000000000000000000000000000000000000000000800a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100023ba65021e4689dd1755f82108214a1f25150d439fe58c55cdb1f376436000000000000000000000000000000000000000000000000000000000000800b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010009759cab4fa9e6ca0784746e1df600ff523f0f90c1e94191755cab4b2ed0000000000000000000000000000000000000000000000000000000000000800c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000019642d87621fdd82cf65aa9146486c9256d5f8849af9a37c78ef519339000000000000000000000000000000000000000000000000000000000000800d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010001b72874590239af612f65d50a35975299f88de022493fe7f0a190e79496000000000000000000000000000000000000000000000000000000000000800e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100005bfc0443349233459892b51e9f67e27ac828d44d9c7cba8c8285fd66bc000000000000000000000000000000000000000000000000000000000000800f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100001fb52ca33668d01c230a1c3b13ede90fe2e37d77222410e9f183cb7a8900000000000000000000000000000000000000000000000000000000000080100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "upgradeAddress": "0x567e1B57A80a7F048A7402191F96C62730e30dB2", + "protocolVersion": "18", + "diamondUpgradeProposalId": { + "type": "BigNumber", + "hex": "0x0b" + }, + "upgradeTimestamp": "1701682200", + "transparentUpgrade": { + "facetCuts": [ + { + "facet": "0x0000000000000000000000000000000000000000", + "selectors": [ + "0x73fb9297", + "0x36d4eb84", + "0x27ae4c16", + "0x0551448c", + "0x8043760a", + "0xbeda4b12", + "0x17338945", + "0x587809c7" + ], + "action": 2, + "isFreezable": false + }, + { + "facet": "0x0000000000000000000000000000000000000000", + "selectors": [ + "0xcdffacc6", + "0x52ef6b2c", + "0xadfca15e", + "0x7a0ed627", + "0xa7cd63b7", + "0xfe10226d", + "0x79823c9a", + "0x4fc07d75", + "0xd86970d8", + "0xfd791f3c", + "0x9d1b5a81", + "0x7b30c8da", + "0x8665b150", + "0x631f4bac", + "0x0ec6b0b7", + "0x1b60e626", + "0xe39d3bff", + "0x33ce93fe", + "0x0ef240a0", + "0xfe26699e", + "0x39607382", + "0xaf6a2dcd", + "0xa1954fc5", + "0xa39980a0", + "0x46657fe9", + "0x18e3a941", + "0x3db920ce", + "0x29b98c67", + "0xbd7c5412", + "0xc3bbd2d7", + "0xe81e0ba1", + "0xfacd743b", + "0x9cd939e4", + "0x56142d7a", + "0x74f4d30d" + ], + "action": 2, + "isFreezable": false + }, + { + "facet": "0x0000000000000000000000000000000000000000", + "selectors": [ + "0x6c0960f9", + "0xb473318e", + "0x042901c7", + "0x263b7f8e", + "0xe4948f43", + "0xeb672419" + ], + "action": 2, + "isFreezable": false + }, + { + "facet": "0x0000000000000000000000000000000000000000", + "selectors": [ + "0x0c4dd810", + "0xce9dcf16", + "0x7739cbe7", + "0xa9a2d18a" + ], + "action": 2, + "isFreezable": false + }, + { + "facet": "0x0000000000000000000000000000000000000000", + "selectors": [ + "0xe58bb639", + "0xf235757f", + "0x1cc5d103", + "0xbe6f11cf", + "0x4623c91d" + ], + "action": 2, + "isFreezable": false + }, + { + "facet": "0x409560DE546e057ce5bD5dB487EdF2bB5E785baB", + "selectors": [ + "0x0e18b681", + "0xe58bb639", + "0xa9f6d941", + "0x27ae4c16", + "0x4dd18bf5", + "0xf235757f", + "0x1cc5d103", + "0xbe6f11cf", + "0x4623c91d", + "0x17338945" + ], + "action": 0, + "isFreezable": false + }, + { + "facet": "0xF3ACF6a03ea4a914B78Ec788624B25ceC37c14A4", + "selectors": [ + "0xcdffacc6", + "0x52ef6b2c", + "0xadfca15e", + "0x7a0ed627", + "0xa7cd63b7", + "0x79823c9a", + "0x4fc07d75", + "0xd86970d8", + "0xfd791f3c", + "0xe5355c75", + "0x9d1b5a81", + "0x7b30c8da", + "0x8665b150", + "0x631f4bac", + "0x0ec6b0b7", + "0x33ce93fe", + "0xdb1f0bf9", + "0xb8c2f66f", + "0xef3f0bae", + "0xfe26699e", + "0x39607382", + "0xaf6a2dcd", + "0xa1954fc5", + "0x46657fe9", + "0x18e3a941", + "0x29b98c67", + "0xbd7c5412", + "0xc3bbd2d7", + "0xe81e0ba1", + "0xfacd743b", + "0x9cd939e4", + "0x56142d7a", + "0xb22dd78e", + "0x74f4d30d" + ], + "action": 0, + "isFreezable": false + }, + { + "facet": "0x63b5EC36B09384fFA7106A80Ec7cfdFCa521fD08", + "selectors": [ + "0x6c0960f9", + "0xb473318e", + "0x042901c7", + "0x263b7f8e", + "0xe4948f43", + "0xeb672419" + ], + "action": 0, + "isFreezable": true + }, + { + "facet": "0x9e3Fa34a10619fEDd7aE40A3fb86FA515fcfd269", + "selectors": [ + "0x701f58c5", + "0xc3d93e7c", + "0x7f61885c", + "0x97c09d34" + ], + "action": 0, + "isFreezable": true + } + ], + "initAddress": "0x567e1B57A80a7F048A7402191F96C62730e30dB2", + "initCalldata": "0x1ed824a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000168001000983d4ac4f797cf5c077e022f72284969b13248c2a8e9846f574bdeb5b8801000651c5ae96f2aab07d720439e42491bb44c6384015e3a08e32620a4d582d000000000000000000000000b465882f67d236dcc0d090f78ebb0d838e9719d85a3ef282b21e12fe1f4438e5bb158fc5060b160559c5158c6389d62d9fe3d08014628525c227822148e718ca1138acfc6d25e759e19452455d89f7f610c3dcb8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016a000000000000000000000000000000000000000000000000000000000000016c000000000000000000000000000000000000000000000000000000000656d9c1800000000000000000000000000000000000000000000000000000000000000120000000000000000000000000c0dc1171258694635aa50cec5845ac1031ca6d700000000000000000000000000000000000000000000000000000000000000fe0000000000000000000000000000000000000000000000000000000000008007000000000000000000000000000000000000000000000000000000000000800600000000000000000000000000000000000000000000000000000000044aa200000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000001460000000000000000000000000000000000000000000000000000000000000148000000000000000000000000000000000000000000000000000000000000014a000000000000000000000000000000000000000000000000000000000000014c000000000000000000000000000000000000000000000000000000000000011c4e9f18c170000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000005800000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000007c0000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000ac00000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000dc00000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000f40000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010c001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000114daca2ff44f27d543b8ef67d885bfed09a74ba9cb25f5912dd3d739c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000178d93b2d7d6448866009892223caf018a8e8dbcf090c2b9053a285f8d00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000c5a85a372f441ac693210a18e683b530bed875fdcab2f7e101b057d43300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100013759b40792c2c3d033990e992e5508263c15252eb2d9bfbba57135067500000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000080010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100009bc0511159b5ec703d0c56f87615964017739def4ab1ee606b8ec6458c00000000000000000000000000000000000000000000000000000000000080020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100012fa73fa922dd9fabb40d3275ce80396eff6ccf1b452c928c17d98bd47000000000000000000000000000000000000000000000000000000000000080030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100008b0ca6c6f277035366e99407fbb4b01e743e80b7d24dea5a3d647b423e00000000000000000000000000000000000000000000000000000000000080040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000047a3c40e3f4eb98f14967f141452ae602d8723a10975dc33960911d8c500000000000000000000000000000000000000000000000000000000000080050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010006091341955c8f76409de00549fb00b275166b5a0d0d7b82cbd629bb421200000000000000000000000000000000000000000000000000000000000080060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000301c943edb65f5a0b8cdd806218b8ecf25c022720fe3afe6951f202f3fa00000000000000000000000000000000000000000000000000000000000080080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100006fa1591d93fcc4a25e9340ad11d0e825904cd1842b8f7255701e1aacbb00000000000000000000000000000000000000000000000000000000000080090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000139b506af2b02225838c5a33e30ace701b44b210a422eedab7dd31c28a3000000000000000000000000000000000000000000000000000000000000800a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100023ba65021e4689dd1755f82108214a1f25150d439fe58c55cdb1f376436000000000000000000000000000000000000000000000000000000000000800b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010009759cab4fa9e6ca0784746e1df600ff523f0f90c1e94191755cab4b2ed0000000000000000000000000000000000000000000000000000000000000800c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000019642d87621fdd82cf65aa9146486c9256d5f8849af9a37c78ef519339000000000000000000000000000000000000000000000000000000000000800d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010001b72874590239af612f65d50a35975299f88de022493fe7f0a190e79496000000000000000000000000000000000000000000000000000000000000800e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100005bfc0443349233459892b51e9f67e27ac828d44d9c7cba8c8285fd66bc000000000000000000000000000000000000000000000000000000000000800f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100001fb52ca33668d01c230a1c3b13ede90fe2e37d77222410e9f183cb7a8900000000000000000000000000000000000000000000000000000000000080100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + "proposeTransparentUpgradeCalldata": "0x8043760a0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000b0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000567e1b57a80a7f048a7402191f96c62730e30db200000000000000000000000000000000000000000000000000000000000015400000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000007c000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000d60000000000000000000000000000000000000000000000000000000000000124000000000000000000000000000000000000000000000000000000000000013a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000873fb92970000000000000000000000000000000000000000000000000000000036d4eb840000000000000000000000000000000000000000000000000000000027ae4c16000000000000000000000000000000000000000000000000000000000551448c000000000000000000000000000000000000000000000000000000008043760a00000000000000000000000000000000000000000000000000000000beda4b12000000000000000000000000000000000000000000000000000000001733894500000000000000000000000000000000000000000000000000000000587809c70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000023cdffacc60000000000000000000000000000000000000000000000000000000052ef6b2c00000000000000000000000000000000000000000000000000000000adfca15e000000000000000000000000000000000000000000000000000000007a0ed62700000000000000000000000000000000000000000000000000000000a7cd63b700000000000000000000000000000000000000000000000000000000fe10226d0000000000000000000000000000000000000000000000000000000079823c9a000000000000000000000000000000000000000000000000000000004fc07d7500000000000000000000000000000000000000000000000000000000d86970d800000000000000000000000000000000000000000000000000000000fd791f3c000000000000000000000000000000000000000000000000000000009d1b5a81000000000000000000000000000000000000000000000000000000007b30c8da000000000000000000000000000000000000000000000000000000008665b15000000000000000000000000000000000000000000000000000000000631f4bac000000000000000000000000000000000000000000000000000000000ec6b0b7000000000000000000000000000000000000000000000000000000001b60e62600000000000000000000000000000000000000000000000000000000e39d3bff0000000000000000000000000000000000000000000000000000000033ce93fe000000000000000000000000000000000000000000000000000000000ef240a000000000000000000000000000000000000000000000000000000000fe26699e000000000000000000000000000000000000000000000000000000003960738200000000000000000000000000000000000000000000000000000000af6a2dcd00000000000000000000000000000000000000000000000000000000a1954fc500000000000000000000000000000000000000000000000000000000a39980a00000000000000000000000000000000000000000000000000000000046657fe90000000000000000000000000000000000000000000000000000000018e3a941000000000000000000000000000000000000000000000000000000003db920ce0000000000000000000000000000000000000000000000000000000029b98c6700000000000000000000000000000000000000000000000000000000bd7c541200000000000000000000000000000000000000000000000000000000c3bbd2d700000000000000000000000000000000000000000000000000000000e81e0ba100000000000000000000000000000000000000000000000000000000facd743b000000000000000000000000000000000000000000000000000000009cd939e40000000000000000000000000000000000000000000000000000000056142d7a0000000000000000000000000000000000000000000000000000000074f4d30d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000066c0960f900000000000000000000000000000000000000000000000000000000b473318e00000000000000000000000000000000000000000000000000000000042901c700000000000000000000000000000000000000000000000000000000263b7f8e00000000000000000000000000000000000000000000000000000000e4948f4300000000000000000000000000000000000000000000000000000000eb67241900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000040c4dd81000000000000000000000000000000000000000000000000000000000ce9dcf16000000000000000000000000000000000000000000000000000000007739cbe700000000000000000000000000000000000000000000000000000000a9a2d18a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000005e58bb63900000000000000000000000000000000000000000000000000000000f235757f000000000000000000000000000000000000000000000000000000001cc5d10300000000000000000000000000000000000000000000000000000000be6f11cf000000000000000000000000000000000000000000000000000000004623c91d00000000000000000000000000000000000000000000000000000000000000000000000000000000409560de546e057ce5bd5db487edf2bb5e785bab000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000a0e18b68100000000000000000000000000000000000000000000000000000000e58bb63900000000000000000000000000000000000000000000000000000000a9f6d9410000000000000000000000000000000000000000000000000000000027ae4c16000000000000000000000000000000000000000000000000000000004dd18bf500000000000000000000000000000000000000000000000000000000f235757f000000000000000000000000000000000000000000000000000000001cc5d10300000000000000000000000000000000000000000000000000000000be6f11cf000000000000000000000000000000000000000000000000000000004623c91d000000000000000000000000000000000000000000000000000000001733894500000000000000000000000000000000000000000000000000000000000000000000000000000000f3acf6a03ea4a914b78ec788624b25cec37c14a40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000022cdffacc60000000000000000000000000000000000000000000000000000000052ef6b2c00000000000000000000000000000000000000000000000000000000adfca15e000000000000000000000000000000000000000000000000000000007a0ed62700000000000000000000000000000000000000000000000000000000a7cd63b70000000000000000000000000000000000000000000000000000000079823c9a000000000000000000000000000000000000000000000000000000004fc07d7500000000000000000000000000000000000000000000000000000000d86970d800000000000000000000000000000000000000000000000000000000fd791f3c00000000000000000000000000000000000000000000000000000000e5355c75000000000000000000000000000000000000000000000000000000009d1b5a81000000000000000000000000000000000000000000000000000000007b30c8da000000000000000000000000000000000000000000000000000000008665b15000000000000000000000000000000000000000000000000000000000631f4bac000000000000000000000000000000000000000000000000000000000ec6b0b70000000000000000000000000000000000000000000000000000000033ce93fe00000000000000000000000000000000000000000000000000000000db1f0bf900000000000000000000000000000000000000000000000000000000b8c2f66f00000000000000000000000000000000000000000000000000000000ef3f0bae00000000000000000000000000000000000000000000000000000000fe26699e000000000000000000000000000000000000000000000000000000003960738200000000000000000000000000000000000000000000000000000000af6a2dcd00000000000000000000000000000000000000000000000000000000a1954fc50000000000000000000000000000000000000000000000000000000046657fe90000000000000000000000000000000000000000000000000000000018e3a9410000000000000000000000000000000000000000000000000000000029b98c6700000000000000000000000000000000000000000000000000000000bd7c541200000000000000000000000000000000000000000000000000000000c3bbd2d700000000000000000000000000000000000000000000000000000000e81e0ba100000000000000000000000000000000000000000000000000000000facd743b000000000000000000000000000000000000000000000000000000009cd939e40000000000000000000000000000000000000000000000000000000056142d7a00000000000000000000000000000000000000000000000000000000b22dd78e0000000000000000000000000000000000000000000000000000000074f4d30d0000000000000000000000000000000000000000000000000000000000000000000000000000000063b5ec36b09384ffa7106a80ec7cfdfca521fd0800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000066c0960f900000000000000000000000000000000000000000000000000000000b473318e00000000000000000000000000000000000000000000000000000000042901c700000000000000000000000000000000000000000000000000000000263b7f8e00000000000000000000000000000000000000000000000000000000e4948f4300000000000000000000000000000000000000000000000000000000eb672419000000000000000000000000000000000000000000000000000000000000000000000000000000009e3fa34a10619fedd7ae40a3fb86fa515fcfd2690000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004701f58c500000000000000000000000000000000000000000000000000000000c3d93e7c000000000000000000000000000000000000000000000000000000007f61885c0000000000000000000000000000000000000000000000000000000097c09d340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017041ed824a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000168001000983d4ac4f797cf5c077e022f72284969b13248c2a8e9846f574bdeb5b8801000651c5ae96f2aab07d720439e42491bb44c6384015e3a08e32620a4d582d000000000000000000000000b465882f67d236dcc0d090f78ebb0d838e9719d85a3ef282b21e12fe1f4438e5bb158fc5060b160559c5158c6389d62d9fe3d08014628525c227822148e718ca1138acfc6d25e759e19452455d89f7f610c3dcb8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016a000000000000000000000000000000000000000000000000000000000000016c000000000000000000000000000000000000000000000000000000000656d9c1800000000000000000000000000000000000000000000000000000000000000120000000000000000000000000c0dc1171258694635aa50cec5845ac1031ca6d700000000000000000000000000000000000000000000000000000000000000fe0000000000000000000000000000000000000000000000000000000000008007000000000000000000000000000000000000000000000000000000000000800600000000000000000000000000000000000000000000000000000000044aa200000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000001460000000000000000000000000000000000000000000000000000000000000148000000000000000000000000000000000000000000000000000000000000014a000000000000000000000000000000000000000000000000000000000000014c000000000000000000000000000000000000000000000000000000000000011c4e9f18c170000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000005800000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000007c0000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000ac00000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000dc00000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000f40000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010c001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000114daca2ff44f27d543b8ef67d885bfed09a74ba9cb25f5912dd3d739c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000178d93b2d7d6448866009892223caf018a8e8dbcf090c2b9053a285f8d00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000c5a85a372f441ac693210a18e683b530bed875fdcab2f7e101b057d43300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100013759b40792c2c3d033990e992e5508263c15252eb2d9bfbba57135067500000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000080010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100009bc0511159b5ec703d0c56f87615964017739def4ab1ee606b8ec6458c00000000000000000000000000000000000000000000000000000000000080020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100012fa73fa922dd9fabb40d3275ce80396eff6ccf1b452c928c17d98bd47000000000000000000000000000000000000000000000000000000000000080030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100008b0ca6c6f277035366e99407fbb4b01e743e80b7d24dea5a3d647b423e00000000000000000000000000000000000000000000000000000000000080040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000047a3c40e3f4eb98f14967f141452ae602d8723a10975dc33960911d8c500000000000000000000000000000000000000000000000000000000000080050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010006091341955c8f76409de00549fb00b275166b5a0d0d7b82cbd629bb421200000000000000000000000000000000000000000000000000000000000080060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000301c943edb65f5a0b8cdd806218b8ecf25c022720fe3afe6951f202f3fa00000000000000000000000000000000000000000000000000000000000080080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100006fa1591d93fcc4a25e9340ad11d0e825904cd1842b8f7255701e1aacbb00000000000000000000000000000000000000000000000000000000000080090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000139b506af2b02225838c5a33e30ace701b44b210a422eedab7dd31c28a3000000000000000000000000000000000000000000000000000000000000800a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100023ba65021e4689dd1755f82108214a1f25150d439fe58c55cdb1f376436000000000000000000000000000000000000000000000000000000000000800b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010009759cab4fa9e6ca0784746e1df600ff523f0f90c1e94191755cab4b2ed0000000000000000000000000000000000000000000000000000000000000800c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000019642d87621fdd82cf65aa9146486c9256d5f8849af9a37c78ef519339000000000000000000000000000000000000000000000000000000000000800d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010001b72874590239af612f65d50a35975299f88de022493fe7f0a190e79496000000000000000000000000000000000000000000000000000000000000800e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100005bfc0443349233459892b51e9f67e27ac828d44d9c7cba8c8285fd66bc000000000000000000000000000000000000000000000000000000000000800f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100001fb52ca33668d01c230a1c3b13ede90fe2e37d77222410e9f183cb7a8900000000000000000000000000000000000000000000000000000000000080100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "executeUpgradeCalldata": "0x36d4eb84000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000567e1b57a80a7f048a7402191f96c62730e30db200000000000000000000000000000000000000000000000000000000000015400000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000007c000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000d60000000000000000000000000000000000000000000000000000000000000124000000000000000000000000000000000000000000000000000000000000013a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000873fb92970000000000000000000000000000000000000000000000000000000036d4eb840000000000000000000000000000000000000000000000000000000027ae4c16000000000000000000000000000000000000000000000000000000000551448c000000000000000000000000000000000000000000000000000000008043760a00000000000000000000000000000000000000000000000000000000beda4b12000000000000000000000000000000000000000000000000000000001733894500000000000000000000000000000000000000000000000000000000587809c70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000023cdffacc60000000000000000000000000000000000000000000000000000000052ef6b2c00000000000000000000000000000000000000000000000000000000adfca15e000000000000000000000000000000000000000000000000000000007a0ed62700000000000000000000000000000000000000000000000000000000a7cd63b700000000000000000000000000000000000000000000000000000000fe10226d0000000000000000000000000000000000000000000000000000000079823c9a000000000000000000000000000000000000000000000000000000004fc07d7500000000000000000000000000000000000000000000000000000000d86970d800000000000000000000000000000000000000000000000000000000fd791f3c000000000000000000000000000000000000000000000000000000009d1b5a81000000000000000000000000000000000000000000000000000000007b30c8da000000000000000000000000000000000000000000000000000000008665b15000000000000000000000000000000000000000000000000000000000631f4bac000000000000000000000000000000000000000000000000000000000ec6b0b7000000000000000000000000000000000000000000000000000000001b60e62600000000000000000000000000000000000000000000000000000000e39d3bff0000000000000000000000000000000000000000000000000000000033ce93fe000000000000000000000000000000000000000000000000000000000ef240a000000000000000000000000000000000000000000000000000000000fe26699e000000000000000000000000000000000000000000000000000000003960738200000000000000000000000000000000000000000000000000000000af6a2dcd00000000000000000000000000000000000000000000000000000000a1954fc500000000000000000000000000000000000000000000000000000000a39980a00000000000000000000000000000000000000000000000000000000046657fe90000000000000000000000000000000000000000000000000000000018e3a941000000000000000000000000000000000000000000000000000000003db920ce0000000000000000000000000000000000000000000000000000000029b98c6700000000000000000000000000000000000000000000000000000000bd7c541200000000000000000000000000000000000000000000000000000000c3bbd2d700000000000000000000000000000000000000000000000000000000e81e0ba100000000000000000000000000000000000000000000000000000000facd743b000000000000000000000000000000000000000000000000000000009cd939e40000000000000000000000000000000000000000000000000000000056142d7a0000000000000000000000000000000000000000000000000000000074f4d30d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000066c0960f900000000000000000000000000000000000000000000000000000000b473318e00000000000000000000000000000000000000000000000000000000042901c700000000000000000000000000000000000000000000000000000000263b7f8e00000000000000000000000000000000000000000000000000000000e4948f4300000000000000000000000000000000000000000000000000000000eb67241900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000040c4dd81000000000000000000000000000000000000000000000000000000000ce9dcf16000000000000000000000000000000000000000000000000000000007739cbe700000000000000000000000000000000000000000000000000000000a9a2d18a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000005e58bb63900000000000000000000000000000000000000000000000000000000f235757f000000000000000000000000000000000000000000000000000000001cc5d10300000000000000000000000000000000000000000000000000000000be6f11cf000000000000000000000000000000000000000000000000000000004623c91d00000000000000000000000000000000000000000000000000000000000000000000000000000000409560de546e057ce5bd5db487edf2bb5e785bab000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000a0e18b68100000000000000000000000000000000000000000000000000000000e58bb63900000000000000000000000000000000000000000000000000000000a9f6d9410000000000000000000000000000000000000000000000000000000027ae4c16000000000000000000000000000000000000000000000000000000004dd18bf500000000000000000000000000000000000000000000000000000000f235757f000000000000000000000000000000000000000000000000000000001cc5d10300000000000000000000000000000000000000000000000000000000be6f11cf000000000000000000000000000000000000000000000000000000004623c91d000000000000000000000000000000000000000000000000000000001733894500000000000000000000000000000000000000000000000000000000000000000000000000000000f3acf6a03ea4a914b78ec788624b25cec37c14a40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000022cdffacc60000000000000000000000000000000000000000000000000000000052ef6b2c00000000000000000000000000000000000000000000000000000000adfca15e000000000000000000000000000000000000000000000000000000007a0ed62700000000000000000000000000000000000000000000000000000000a7cd63b70000000000000000000000000000000000000000000000000000000079823c9a000000000000000000000000000000000000000000000000000000004fc07d7500000000000000000000000000000000000000000000000000000000d86970d800000000000000000000000000000000000000000000000000000000fd791f3c00000000000000000000000000000000000000000000000000000000e5355c75000000000000000000000000000000000000000000000000000000009d1b5a81000000000000000000000000000000000000000000000000000000007b30c8da000000000000000000000000000000000000000000000000000000008665b15000000000000000000000000000000000000000000000000000000000631f4bac000000000000000000000000000000000000000000000000000000000ec6b0b70000000000000000000000000000000000000000000000000000000033ce93fe00000000000000000000000000000000000000000000000000000000db1f0bf900000000000000000000000000000000000000000000000000000000b8c2f66f00000000000000000000000000000000000000000000000000000000ef3f0bae00000000000000000000000000000000000000000000000000000000fe26699e000000000000000000000000000000000000000000000000000000003960738200000000000000000000000000000000000000000000000000000000af6a2dcd00000000000000000000000000000000000000000000000000000000a1954fc50000000000000000000000000000000000000000000000000000000046657fe90000000000000000000000000000000000000000000000000000000018e3a9410000000000000000000000000000000000000000000000000000000029b98c6700000000000000000000000000000000000000000000000000000000bd7c541200000000000000000000000000000000000000000000000000000000c3bbd2d700000000000000000000000000000000000000000000000000000000e81e0ba100000000000000000000000000000000000000000000000000000000facd743b000000000000000000000000000000000000000000000000000000009cd939e40000000000000000000000000000000000000000000000000000000056142d7a00000000000000000000000000000000000000000000000000000000b22dd78e0000000000000000000000000000000000000000000000000000000074f4d30d0000000000000000000000000000000000000000000000000000000000000000000000000000000063b5ec36b09384ffa7106a80ec7cfdfca521fd0800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000066c0960f900000000000000000000000000000000000000000000000000000000b473318e00000000000000000000000000000000000000000000000000000000042901c700000000000000000000000000000000000000000000000000000000263b7f8e00000000000000000000000000000000000000000000000000000000e4948f4300000000000000000000000000000000000000000000000000000000eb672419000000000000000000000000000000000000000000000000000000000000000000000000000000009e3fa34a10619fedd7ae40a3fb86fa515fcfd2690000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004701f58c500000000000000000000000000000000000000000000000000000000c3d93e7c000000000000000000000000000000000000000000000000000000007f61885c0000000000000000000000000000000000000000000000000000000097c09d340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017041ed824a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000168001000983d4ac4f797cf5c077e022f72284969b13248c2a8e9846f574bdeb5b8801000651c5ae96f2aab07d720439e42491bb44c6384015e3a08e32620a4d582d000000000000000000000000b465882f67d236dcc0d090f78ebb0d838e9719d85a3ef282b21e12fe1f4438e5bb158fc5060b160559c5158c6389d62d9fe3d08014628525c227822148e718ca1138acfc6d25e759e19452455d89f7f610c3dcb8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016a000000000000000000000000000000000000000000000000000000000000016c000000000000000000000000000000000000000000000000000000000656d9c1800000000000000000000000000000000000000000000000000000000000000120000000000000000000000000c0dc1171258694635aa50cec5845ac1031ca6d700000000000000000000000000000000000000000000000000000000000000fe0000000000000000000000000000000000000000000000000000000000008007000000000000000000000000000000000000000000000000000000000000800600000000000000000000000000000000000000000000000000000000044aa200000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000001460000000000000000000000000000000000000000000000000000000000000148000000000000000000000000000000000000000000000000000000000000014a000000000000000000000000000000000000000000000000000000000000014c000000000000000000000000000000000000000000000000000000000000011c4e9f18c170000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000005800000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000007c0000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000ac00000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000dc00000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000f40000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010c001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000114daca2ff44f27d543b8ef67d885bfed09a74ba9cb25f5912dd3d739c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000178d93b2d7d6448866009892223caf018a8e8dbcf090c2b9053a285f8d00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010000c5a85a372f441ac693210a18e683b530bed875fdcab2f7e101b057d43300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100013759b40792c2c3d033990e992e5508263c15252eb2d9bfbba57135067500000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000007271e9710c356751295d83a25ffec94be2b4ada01ec1fa04c7cd6f2c700000000000000000000000000000000000000000000000000000000000080010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100009bc0511159b5ec703d0c56f87615964017739def4ab1ee606b8ec6458c00000000000000000000000000000000000000000000000000000000000080020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100012fa73fa922dd9fabb40d3275ce80396eff6ccf1b452c928c17d98bd47000000000000000000000000000000000000000000000000000000000000080030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100008b0ca6c6f277035366e99407fbb4b01e743e80b7d24dea5a3d647b423e00000000000000000000000000000000000000000000000000000000000080040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000047a3c40e3f4eb98f14967f141452ae602d8723a10975dc33960911d8c500000000000000000000000000000000000000000000000000000000000080050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010006091341955c8f76409de00549fb00b275166b5a0d0d7b82cbd629bb421200000000000000000000000000000000000000000000000000000000000080060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000301c943edb65f5a0b8cdd806218b8ecf25c022720fe3afe6951f202f3fa00000000000000000000000000000000000000000000000000000000000080080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100006fa1591d93fcc4a25e9340ad11d0e825904cd1842b8f7255701e1aacbb00000000000000000000000000000000000000000000000000000000000080090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000139b506af2b02225838c5a33e30ace701b44b210a422eedab7dd31c28a3000000000000000000000000000000000000000000000000000000000000800a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100023ba65021e4689dd1755f82108214a1f25150d439fe58c55cdb1f376436000000000000000000000000000000000000000000000000000000000000800b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010009759cab4fa9e6ca0784746e1df600ff523f0f90c1e94191755cab4b2ed0000000000000000000000000000000000000000000000000000000000000800c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001000019642d87621fdd82cf65aa9146486c9256d5f8849af9a37c78ef519339000000000000000000000000000000000000000000000000000000000000800d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000010001b72874590239af612f65d50a35975299f88de022493fe7f0a190e79496000000000000000000000000000000000000000000000000000000000000800e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100005bfc0443349233459892b51e9f67e27ac828d44d9c7cba8c8285fd66bc000000000000000000000000000000000000000000000000000000000000800f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000100001fb52ca33668d01c230a1c3b13ede90fe2e37d77222410e9f183cb7a8900000000000000000000000000000000000000000000000000000000000080100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/infrastructure/protocol-upgrade/README.md b/infrastructure/protocol-upgrade/README.md index 6636dff8b0ff..caa14236f8b3 100644 --- a/infrastructure/protocol-upgrade/README.md +++ b/infrastructure/protocol-upgrade/README.md @@ -167,7 +167,7 @@ $ zk f yarn start l2-transaction force-deployment-calldata \ To deploy a new verifier, use the following command: ```bash -$ zk f yarn start crypto deploy-verifier +$ zk f yarn start crypto deploy-verifier \ --private-key \ --l1rpc \ --gas-price \ diff --git a/infrastructure/protocol-upgrade/src/crypto/crypto.ts b/infrastructure/protocol-upgrade/src/crypto/crypto.ts index 8c7d666473f0..df7aa6bd44b5 100644 --- a/infrastructure/protocol-upgrade/src/crypto/crypto.ts +++ b/infrastructure/protocol-upgrade/src/crypto/crypto.ts @@ -58,7 +58,7 @@ command command .command('deploy-verifier') - .option('--l1Rpc ') + .option('--l1rpc ') .option('--private-key ') .option('--create2-address ') .option('--nonce ') From 2de48256ebe028c6f95eb32e75339d8b05472c0f Mon Sep 17 00:00:00 2001 From: zksync-admin-bot2 <91326834+zksync-admin-bot2@users.noreply.github.com> Date: Wed, 6 Dec 2023 11:38:12 +0200 Subject: [PATCH 053/268] chore: Update generated Prover FRI CPU setup-data keys from branch main (#609) "Update generated Prover FRI CPU setup-data keys from branch main" --- prover/setup-data-cpu-keys.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prover/setup-data-cpu-keys.json b/prover/setup-data-cpu-keys.json index 98a15d880384..1a0427e53463 100644 --- a/prover/setup-data-cpu-keys.json +++ b/prover/setup-data-cpu-keys.json @@ -1,5 +1,5 @@ { - "us": "gs://matterlabs-setup-data-us/fb5e9fd/", - "europe": "gs://matterlabs-setup-data-europe/fb5e9fd/", - "asia": "gs://matterlabs-setup-data-asia/fb5e9fd/" + "us": "gs://matterlabs-setup-data-us/e2e94ff/", + "europe": "gs://matterlabs-setup-data-europe/e2e94ff/", + "asia": "gs://matterlabs-setup-data-asia/e2e94ff/" } From 5cf7210dc77bb615944352f23ed39fad324b914f Mon Sep 17 00:00:00 2001 From: perekopskiy <53865202+perekopskiy@users.noreply.github.com> Date: Wed, 6 Dec 2023 11:51:40 +0200 Subject: [PATCH 054/268] perf(external-node): Use async miniblock sealing in external IO (#611) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ External IO uses async miniblock sealing. ## Why ❔ Execution of transactions and miniblock sealing (writing data to postgres) happen in parallel so the perfomance is better. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --- core/bin/external_node/src/config/mod.rs | 9 +++ core/bin/external_node/src/main.rs | 17 +++- .../src/state_keeper/io/mempool.rs | 3 + .../zksync_core/src/state_keeper/io/mod.rs | 11 +-- .../src/state_keeper/io/seal_logic.rs | 55 ++++++++++++- .../src/state_keeper/io/tests/mod.rs | 12 +++ .../zksync_core/src/state_keeper/metrics.rs | 7 +- core/lib/zksync_core/src/state_keeper/mod.rs | 6 +- .../src/state_keeper/updates/mod.rs | 10 +++ .../zksync_core/src/sync_layer/external_io.rs | 78 ++++--------------- core/lib/zksync_core/src/sync_layer/tests.rs | 6 +- spellcheck/era.dic | 1 + 12 files changed, 132 insertions(+), 83 deletions(-) diff --git a/core/bin/external_node/src/config/mod.rs b/core/bin/external_node/src/config/mod.rs index 3f26a334ea3c..c116201b91db 100644 --- a/core/bin/external_node/src/config/mod.rs +++ b/core/bin/external_node/src/config/mod.rs @@ -190,6 +190,11 @@ pub struct OptionalENConfig { /// Number of keys that is processed by enum_index migration in State Keeper each L1 batch. #[serde(default = "OptionalENConfig::default_enum_index_migration_chunk_size")] pub enum_index_migration_chunk_size: usize, + /// Capacity of the queue for asynchronous miniblock sealing. Once this many miniblocks are queued, + /// sealing will block until some of the miniblocks from the queue are processed. + /// 0 means that sealing is synchronous; this is mostly useful for performance comparison, testing etc. + #[serde(default = "OptionalENConfig::default_miniblock_seal_queue_capacity")] + pub miniblock_seal_queue_capacity: usize, } impl OptionalENConfig { @@ -288,6 +293,10 @@ impl OptionalENConfig { 5000 } + const fn default_miniblock_seal_queue_capacity() -> usize { + 10 + } + pub fn polling_interval(&self) -> Duration { Duration::from_millis(self.polling_interval) } diff --git a/core/bin/external_node/src/main.rs b/core/bin/external_node/src/main.rs index 52f3353dc072..6324b0599a6a 100644 --- a/core/bin/external_node/src/main.rs +++ b/core/bin/external_node/src/main.rs @@ -22,7 +22,10 @@ use zksync_core::{ }, reorg_detector::ReorgDetector, setup_sigint_handler, - state_keeper::{L1BatchExecutorBuilder, MainBatchExecutorBuilder, ZkSyncStateKeeper}, + state_keeper::{ + L1BatchExecutorBuilder, MainBatchExecutorBuilder, MiniblockSealer, MiniblockSealerHandle, + ZkSyncStateKeeper, + }, sync_layer::{ batch_status_updater::BatchStatusUpdater, external_io::ExternalIO, fetcher::FetcherCursor, genesis::perform_genesis_if_needed, ActionQueue, MainNodeClient, SyncState, @@ -47,6 +50,7 @@ async fn build_state_keeper( connection_pool: ConnectionPool, sync_state: SyncState, l2_erc20_bridge_addr: Address, + miniblock_sealer_handle: MiniblockSealerHandle, stop_receiver: watch::Receiver, chain_id: L2ChainId, ) -> ZkSyncStateKeeper { @@ -73,6 +77,7 @@ async fn build_state_keeper( let main_node_client = ::json_rpc(&main_node_url) .expect("Failed creating JSON-RPC client for main node"); let io = ExternalIO::new( + miniblock_sealer_handle, connection_pool, action_queue, sync_state, @@ -106,6 +111,14 @@ async fn init_tasks( let sync_state = SyncState::new(); let (action_queue_sender, action_queue) = ActionQueue::new(); + + let mut task_handles = vec![]; + let (miniblock_sealer, miniblock_sealer_handle) = MiniblockSealer::new( + connection_pool.clone(), + config.optional.miniblock_seal_queue_capacity, + ); + task_handles.push(tokio::spawn(miniblock_sealer.run())); + let state_keeper = build_state_keeper( action_queue, config.required.state_cache_path.clone(), @@ -113,6 +126,7 @@ async fn init_tasks( connection_pool.clone(), sync_state.clone(), config.remote.l2_erc20_bridge_addr, + miniblock_sealer_handle, stop_receiver.clone(), config.remote.l2_chain_id, ) @@ -271,7 +285,6 @@ async fn init_tasks( healthchecks, ); - let mut task_handles = vec![]; if let Some(port) = config.optional.prometheus_port { let prometheus_task = PrometheusExporterConfig::pull(port).run(stop_receiver.clone()); task_handles.push(tokio::spawn(prometheus_task)); diff --git a/core/lib/zksync_core/src/state_keeper/io/mempool.rs b/core/lib/zksync_core/src/state_keeper/io/mempool.rs index f10ad87580c0..1d3ad506df6b 100644 --- a/core/lib/zksync_core/src/state_keeper/io/mempool.rs +++ b/core/lib/zksync_core/src/state_keeper/io/mempool.rs @@ -274,6 +274,8 @@ where self.current_l1_batch_number, self.current_miniblock_number, self.l2_erc20_bridge_addr, + None, + false, ); self.miniblock_sealer_handle.submit(command).await; self.current_miniblock_number += 1; @@ -323,6 +325,7 @@ where l1_batch_env, finished_batch, self.l2_erc20_bridge_addr, + None, ) .await; self.current_miniblock_number += 1; // Due to fictive miniblock being sealed. diff --git a/core/lib/zksync_core/src/state_keeper/io/mod.rs b/core/lib/zksync_core/src/state_keeper/io/mod.rs index 858c46b2e704..313c363418d9 100644 --- a/core/lib/zksync_core/src/state_keeper/io/mod.rs +++ b/core/lib/zksync_core/src/state_keeper/io/mod.rs @@ -130,7 +130,7 @@ struct Completable { /// Handle for [`MiniblockSealer`] allowing to submit [`MiniblockSealCommand`]s. #[derive(Debug)] -pub(crate) struct MiniblockSealerHandle { +pub struct MiniblockSealerHandle { commands_sender: mpsc::Sender>, latest_completion_receiver: Option>, // If true, `submit()` will wait for the operation to complete. @@ -144,7 +144,7 @@ impl MiniblockSealerHandle { /// /// If there are currently too many unprocessed commands, this method will wait until /// enough of them are processed (i.e., there is back pressure). - pub async fn submit(&mut self, command: MiniblockSealCommand) { + pub(crate) async fn submit(&mut self, command: MiniblockSealCommand) { let miniblock_number = command.miniblock_number; tracing::debug!( "Enqueuing sealing command for miniblock #{miniblock_number} with #{} txs (L1 batch #{})", @@ -209,7 +209,7 @@ impl MiniblockSealerHandle { /// Component responsible for sealing miniblocks (i.e., storing their data to Postgres). #[derive(Debug)] -pub(crate) struct MiniblockSealer { +pub struct MiniblockSealer { pool: ConnectionPool, is_sync: bool, // Weak sender handle to get queue capacity stats. @@ -220,10 +220,7 @@ pub(crate) struct MiniblockSealer { impl MiniblockSealer { /// Creates a sealer that will use the provided Postgres connection and will have the specified /// `command_capacity` for unprocessed sealing commands. - pub(crate) fn new( - pool: ConnectionPool, - mut command_capacity: usize, - ) -> (Self, MiniblockSealerHandle) { + pub fn new(pool: ConnectionPool, mut command_capacity: usize) -> (Self, MiniblockSealerHandle) { let is_sync = command_capacity == 0; command_capacity = command_capacity.max(1); diff --git a/core/lib/zksync_core/src/state_keeper/io/seal_logic.rs b/core/lib/zksync_core/src/state_keeper/io/seal_logic.rs index ca2dc6419098..4501be62f785 100644 --- a/core/lib/zksync_core/src/state_keeper/io/seal_logic.rs +++ b/core/lib/zksync_core/src/state_keeper/io/seal_logic.rs @@ -8,6 +8,7 @@ use std::{ }; use multivm::interface::{FinishedL1Batch, L1BatchEnv}; +use zksync_dal::blocks_dal::ConsensusBlockFields; use zksync_dal::StorageProcessor; use zksync_system_constants::ACCOUNT_CODE_STORAGE_ADDRESS; use zksync_types::{ @@ -18,14 +19,18 @@ use zksync_types::{ use zksync_types::{ block::{L1BatchHeader, MiniblockHeader}, event::{extract_added_tokens, extract_long_l2_to_l1_messages}, + l1::L1Tx, + l2::L2Tx, + protocol_version::ProtocolUpgradeTx, storage_writes_deduplicator::{ModifiedSlot, StorageWritesDeduplicator}, tx::{ tx_execution_info::DeduplicatedWritesMetrics, IncludedTxLocation, TransactionExecutionResult, }, zkevm_test_harness::witness::sort_storage_access::sort_storage_access_queries, - AccountTreeId, Address, ExecuteTransactionCommon, L1BatchNumber, LogQuery, MiniblockNumber, - StorageKey, StorageLog, StorageLogQuery, StorageValue, Transaction, VmEvent, H256, + AccountTreeId, Address, ExecuteTransactionCommon, L1BatchNumber, L1BlockNumber, LogQuery, + MiniblockNumber, StorageKey, StorageLog, StorageLogQuery, StorageValue, Transaction, VmEvent, + H256, }; // TODO (SMA-1206): use seconds instead of milliseconds. use zksync_utils::{h256_to_u256, time::millis_since_epoch, u256_to_h256}; @@ -50,6 +55,7 @@ impl UpdatesManager { l1_batch_env: &L1BatchEnv, finished_batch: FinishedL1Batch, l2_erc20_bridge_addr: Address, + consensus: Option, ) { let started_at = Instant::now(); let progress = L1_BATCH_METRICS.start(L1BatchSealStage::VmFinalization); @@ -63,6 +69,8 @@ impl UpdatesManager { l1_batch_env.number, current_miniblock_number, l2_erc20_bridge_addr, + consensus, + false, // fictive miniblocks don't have txs, so it's fine to pass `false` here. ); miniblock_command.seal_inner(&mut transaction, true).await; progress.observe(None); @@ -274,6 +282,36 @@ impl MiniblockSealCommand { async fn seal_inner(&self, storage: &mut StorageProcessor<'_>, is_fictive: bool) { self.assert_valid_miniblock(is_fictive); + let mut transaction = storage.start_transaction().await.unwrap(); + if self.pre_insert_txs { + let progress = MINIBLOCK_METRICS.start(MiniblockSealStage::PreInsertTxs, is_fictive); + for tx in &self.miniblock.executed_transactions { + if let Ok(l1_tx) = L1Tx::try_from(tx.transaction.clone()) { + let l1_block_number = L1BlockNumber(l1_tx.common_data.eth_block as u32); + transaction + .transactions_dal() + .insert_transaction_l1(l1_tx, l1_block_number) + .await; + } else if let Ok(l2_tx) = L2Tx::try_from(tx.transaction.clone()) { + // Using `Default` for execution metrics should be OK here, since this data is not used on the EN. + transaction + .transactions_dal() + .insert_transaction_l2(l2_tx, Default::default()) + .await; + } else if let Ok(protocol_system_upgrade_tx) = + ProtocolUpgradeTx::try_from(tx.transaction.clone()) + { + transaction + .transactions_dal() + .insert_system_transaction(protocol_system_upgrade_tx) + .await; + } else { + unreachable!("Transaction {:?} is neither L1 nor L2", tx.transaction); + } + } + progress.observe(Some(self.miniblock.executed_transactions.len())); + } + let l1_batch_number = self.l1_batch_number; let miniblock_number = self.miniblock_number; let started_at = Instant::now(); @@ -291,7 +329,6 @@ impl MiniblockSealCommand { event_count = self.miniblock.events.len() ); - let mut transaction = storage.start_transaction().await.unwrap(); let miniblock_header = MiniblockHeader { number: miniblock_number, timestamp: self.miniblock.timestamp, @@ -404,6 +441,18 @@ impl MiniblockSealCommand { .await; progress.observe(user_l2_to_l1_log_count); + let progress = MINIBLOCK_METRICS.start(MiniblockSealStage::InsertConsensus, is_fictive); + // We want to add miniblock consensus fields atomically with the miniblock data so that we + // don't need to deal with corner cases (e.g., a miniblock w/o consensus fields). + if let Some(consensus) = &self.consensus { + transaction + .blocks_dal() + .set_miniblock_consensus_fields(self.miniblock_number, consensus) + .await + .unwrap(); + } + progress.observe(None); + let progress = MINIBLOCK_METRICS.start(MiniblockSealStage::CommitMiniblock, is_fictive); let current_l2_virtual_block_info = transaction .storage_dal() diff --git a/core/lib/zksync_core/src/state_keeper/io/tests/mod.rs b/core/lib/zksync_core/src/state_keeper/io/tests/mod.rs index 0c13a7a614b4..2b924554f27d 100644 --- a/core/lib/zksync_core/src/state_keeper/io/tests/mod.rs +++ b/core/lib/zksync_core/src/state_keeper/io/tests/mod.rs @@ -245,6 +245,8 @@ async fn processing_storage_logs_when_sealing_miniblock() { base_system_contracts_hashes: BaseSystemContractsHashes::default(), protocol_version: Some(ProtocolVersionId::latest()), l2_erc20_bridge_addr: Address::default(), + consensus: None, + pre_insert_txs: false, }; let mut conn = connection_pool .access_storage_tagged("state_keeper") @@ -321,6 +323,8 @@ async fn processing_events_when_sealing_miniblock() { base_system_contracts_hashes: BaseSystemContractsHashes::default(), protocol_version: Some(ProtocolVersionId::latest()), l2_erc20_bridge_addr: Address::default(), + consensus: None, + pre_insert_txs: false, }; let mut conn = pool.access_storage_tagged("state_keeper").await.unwrap(); conn.protocol_versions_dal() @@ -434,6 +438,8 @@ async fn miniblock_sealer_handle_blocking() { L1BatchNumber(1), MiniblockNumber(1), Address::default(), + None, + false, ); sealer_handle.submit(seal_command).await; @@ -442,6 +448,8 @@ async fn miniblock_sealer_handle_blocking() { L1BatchNumber(1), MiniblockNumber(2), Address::default(), + None, + false, ); { let submit_future = sealer_handle.submit(seal_command); @@ -470,6 +478,8 @@ async fn miniblock_sealer_handle_blocking() { L1BatchNumber(2), MiniblockNumber(3), Address::default(), + None, + false, ); sealer_handle.submit(seal_command).await; let command = sealer.commands_receiver.recv().await.unwrap(); @@ -489,6 +499,8 @@ async fn miniblock_sealer_handle_parallel_processing() { L1BatchNumber(1), MiniblockNumber(i), Address::default(), + None, + false, ); sealer_handle.submit(seal_command).await; } diff --git a/core/lib/zksync_core/src/state_keeper/metrics.rs b/core/lib/zksync_core/src/state_keeper/metrics.rs index f3f433243209..72b89c4a2b81 100644 --- a/core/lib/zksync_core/src/state_keeper/metrics.rs +++ b/core/lib/zksync_core/src/state_keeper/metrics.rs @@ -168,7 +168,6 @@ pub(super) enum L1BatchSealStage { FilterWrittenSlots, InsertInitialWrites, CommitL1Batch, - ExternalNodeStoreTransactions, } /// Buckets for positive integer, not-so-large values (e.g., initial writes count). @@ -221,10 +220,6 @@ impl L1BatchMetrics { latency_per_unit: &self.sealed_entity_per_unit[&stage], } } - - pub(crate) fn start_storing_on_en(&self) -> LatencyObserver<'_> { - self.sealed_time_stage[&L1BatchSealStage::ExternalNodeStoreTransactions].start() - } } #[vise::register] @@ -241,6 +236,7 @@ pub(super) enum MiniblockQueueStage { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelValue)] #[metrics(rename_all = "snake_case")] pub(super) enum MiniblockSealStage { + PreInsertTxs, InsertMiniblockHeader, MarkTransactionsInMiniblock, InsertStorageLogs, @@ -253,6 +249,7 @@ pub(super) enum MiniblockSealStage { InsertEvents, ExtractL2ToL1Logs, InsertL2ToL1Logs, + InsertConsensus, CommitMiniblock, } diff --git a/core/lib/zksync_core/src/state_keeper/mod.rs b/core/lib/zksync_core/src/state_keeper/mod.rs index bdc1f90e2066..5ec395267dfc 100644 --- a/core/lib/zksync_core/src/state_keeper/mod.rs +++ b/core/lib/zksync_core/src/state_keeper/mod.rs @@ -24,14 +24,14 @@ pub(crate) mod updates; pub use self::{ batch_executor::{L1BatchExecutorBuilder, MainBatchExecutorBuilder}, + io::{MiniblockSealer, MiniblockSealerHandle}, keeper::ZkSyncStateKeeper, }; pub(crate) use self::{ - io::MiniblockSealer, mempool_actor::MempoolFetcher, seal_criteria::ConditionalSealer, - types::MempoolGuard, + mempool_actor::MempoolFetcher, seal_criteria::ConditionalSealer, types::MempoolGuard, }; -use self::io::{MempoolIO, MiniblockSealerHandle}; +use self::io::MempoolIO; use crate::l1_gas_price::L1GasPriceProvider; #[allow(clippy::too_many_arguments)] diff --git a/core/lib/zksync_core/src/state_keeper/updates/mod.rs b/core/lib/zksync_core/src/state_keeper/updates/mod.rs index dc72893e7034..3f09f7be30bb 100644 --- a/core/lib/zksync_core/src/state_keeper/updates/mod.rs +++ b/core/lib/zksync_core/src/state_keeper/updates/mod.rs @@ -1,6 +1,7 @@ use multivm::interface::{L1BatchEnv, VmExecutionResultAndLogs}; use zksync_contracts::BaseSystemContractsHashes; +use zksync_dal::blocks_dal::ConsensusBlockFields; use zksync_types::vm_trace::Call; use zksync_types::{ block::BlockGasCount, storage_writes_deduplicator::StorageWritesDeduplicator, @@ -81,6 +82,8 @@ impl UpdatesManager { l1_batch_number: L1BatchNumber, miniblock_number: MiniblockNumber, l2_erc20_bridge_addr: Address, + consensus: Option, + pre_insert_txs: bool, ) -> MiniblockSealCommand { MiniblockSealCommand { l1_batch_number, @@ -93,6 +96,8 @@ impl UpdatesManager { base_system_contracts_hashes: self.base_system_contract_hashes, protocol_version: Some(self.protocol_version), l2_erc20_bridge_addr, + consensus, + pre_insert_txs, } } @@ -172,6 +177,11 @@ pub(crate) struct MiniblockSealCommand { pub base_system_contracts_hashes: BaseSystemContractsHashes, pub protocol_version: Option, pub l2_erc20_bridge_addr: Address, + pub consensus: Option, + /// Whether transactions should be pre-inserted to DB. + /// Should be set to `true` for EN's IO as EN doesn't store transactions in DB + /// before they are included into miniblocks. + pub pre_insert_txs: bool, } #[cfg(test)] diff --git a/core/lib/zksync_core/src/sync_layer/external_io.rs b/core/lib/zksync_core/src/sync_layer/external_io.rs index 8e3ca863072a..4e870b956747 100644 --- a/core/lib/zksync_core/src/sync_layer/external_io.rs +++ b/core/lib/zksync_core/src/sync_layer/external_io.rs @@ -1,20 +1,14 @@ use async_trait::async_trait; use futures::future; -use std::{ - collections::HashMap, - convert::{TryFrom, TryInto}, - iter::FromIterator, - time::Duration, -}; +use std::{collections::HashMap, convert::TryInto, iter::FromIterator, time::Duration}; use multivm::interface::{FinishedL1Batch, L1BatchEnv, SystemEnv}; use zksync_contracts::{BaseSystemContracts, SystemContractCode}; use zksync_dal::ConnectionPool; use zksync_types::{ - ethabi::Address, l1::L1Tx, l2::L2Tx, protocol_version::ProtocolUpgradeTx, - witness_block_state::WitnessBlockState, L1BatchNumber, L1BlockNumber, L2ChainId, - MiniblockNumber, ProtocolVersionId, Transaction, H256, U256, + ethabi::Address, protocol_version::ProtocolUpgradeTx, witness_block_state::WitnessBlockState, + L1BatchNumber, L2ChainId, MiniblockNumber, ProtocolVersionId, Transaction, H256, U256, }; use zksync_utils::{be_words_to_bytes, bytes_to_be_words}; @@ -29,9 +23,9 @@ use crate::{ extractors, io::{ common::{l1_batch_params, load_pending_batch, poll_iters}, - MiniblockParams, PendingBatchData, StateKeeperIO, + MiniblockParams, MiniblockSealerHandle, PendingBatchData, StateKeeperIO, }, - metrics::{KEEPER_METRICS, L1_BATCH_METRICS}, + metrics::KEEPER_METRICS, seal_criteria::IoSealCriteria, updates::UpdatesManager, }, @@ -48,6 +42,7 @@ const POLL_INTERVAL: Duration = Duration::from_millis(100); /// to the one in the mempool IO (which is used in the main node). #[derive(Debug)] pub struct ExternalIO { + miniblock_sealer_handle: MiniblockSealerHandle, pool: ConnectionPool, current_l1_batch_number: L1BatchNumber, @@ -64,7 +59,9 @@ pub struct ExternalIO { } impl ExternalIO { + #[allow(clippy::too_many_arguments)] pub async fn new( + miniblock_sealer_handle: MiniblockSealerHandle, pool: ConnectionPool, actions: ActionQueue, sync_state: SyncState, @@ -95,6 +92,7 @@ impl ExternalIO { sync_state.set_local_block(last_miniblock_number); Self { + miniblock_sealer_handle, pool, current_l1_batch_number: last_sealed_l1_batch_header.number + 1, current_miniblock_number: last_miniblock_number + 1, @@ -459,56 +457,15 @@ impl StateKeeperIO for ExternalIO { panic!("State keeper requested to seal miniblock, but the next action is {action:?}"); }; - let mut storage = self.pool.access_storage_tagged("sync_layer").await.unwrap(); - let mut transaction = storage.start_transaction().await.unwrap(); - - let store_latency = L1_BATCH_METRICS.start_storing_on_en(); - // We don't store the transactions in the database until they're executed to not overcomplicate the state - // recovery on restart. So we have to store them here. - for tx in &updates_manager.miniblock.executed_transactions { - if let Ok(l1_tx) = L1Tx::try_from(tx.transaction.clone()) { - let l1_block_number = L1BlockNumber(l1_tx.common_data.eth_block as u32); - transaction - .transactions_dal() - .insert_transaction_l1(l1_tx, l1_block_number) - .await; - } else if let Ok(l2_tx) = L2Tx::try_from(tx.transaction.clone()) { - // Using `Default` for execution metrics should be OK here, since this data is not used on the EN. - transaction - .transactions_dal() - .insert_transaction_l2(l2_tx, Default::default()) - .await; - } else if let Ok(protocol_system_upgrade_tx) = - ProtocolUpgradeTx::try_from(tx.transaction.clone()) - { - transaction - .transactions_dal() - .insert_system_transaction(protocol_system_upgrade_tx) - .await; - } else { - unreachable!("Transaction {:?} is neither L1 nor L2", tx.transaction); - } - } - store_latency.observe(); - // Now transactions are stored, and we may mark them as executed. let command = updates_manager.seal_miniblock_command( self.current_l1_batch_number, self.current_miniblock_number, self.l2_erc20_bridge_addr, + consensus, + true, ); - command.seal(&mut transaction).await; - - // We want to add miniblock consensus fields atomically with the miniblock data so that we - // don't need to deal with corner cases (e.g., a miniblock w/o consensus fields). - if let Some(consensus) = &consensus { - transaction - .blocks_dal() - .set_miniblock_consensus_fields(self.current_miniblock_number, consensus) - .await - .unwrap(); - } - transaction.commit().await.unwrap(); + self.miniblock_sealer_handle.submit(command).await; self.sync_state .set_local_block(self.current_miniblock_number); @@ -531,6 +488,9 @@ impl StateKeeperIO for ExternalIO { ); }; + // We cannot start sealing an L1 batch until we've sealed all miniblocks included in it. + self.miniblock_sealer_handle.wait_for_all_commands().await; + let mut storage = self.pool.access_storage_tagged("sync_layer").await.unwrap(); let mut transaction = storage.start_transaction().await.unwrap(); updates_manager @@ -540,15 +500,9 @@ impl StateKeeperIO for ExternalIO { l1_batch_env, finished_batch, self.l2_erc20_bridge_addr, + consensus, ) .await; - if let Some(consensus) = &consensus { - transaction - .blocks_dal() - .set_miniblock_consensus_fields(self.current_miniblock_number, consensus) - .await - .unwrap(); - } transaction.commit().await.unwrap(); tracing::info!("Batch {} is sealed", self.current_l1_batch_number); diff --git a/core/lib/zksync_core/src/sync_layer/tests.rs b/core/lib/zksync_core/src/sync_layer/tests.rs index 20bafc51cf67..10582c7d9f9a 100644 --- a/core/lib/zksync_core/src/sync_layer/tests.rs +++ b/core/lib/zksync_core/src/sync_layer/tests.rs @@ -22,7 +22,7 @@ use crate::{ genesis::{ensure_genesis_state, GenesisParams}, state_keeper::{ tests::{create_l1_batch_metadata, create_l2_transaction, TestBatchExecutorBuilder}, - ZkSyncStateKeeper, + MiniblockSealer, ZkSyncStateKeeper, }, }; @@ -156,7 +156,11 @@ impl StateKeeperHandles { ensure_genesis(&mut pool.access_storage().await.unwrap()).await; let sync_state = SyncState::new(); + let (miniblock_sealer, miniblock_sealer_handle) = MiniblockSealer::new(pool.clone(), 5); + tokio::spawn(miniblock_sealer.run()); + let io = ExternalIO::new( + miniblock_sealer_handle, pool, actions, sync_state.clone(), diff --git a/spellcheck/era.dic b/spellcheck/era.dic index 13dd303a3dcd..e56162fcf02b 100644 --- a/spellcheck/era.dic +++ b/spellcheck/era.dic @@ -276,6 +276,7 @@ versa blake2 AR16MT Preimages +EN's // Names Vyper From 66e76b5f6baa7a7e7bea434376aeaa9f42701898 Mon Sep 17 00:00:00 2001 From: Jean <148654781+oxJean@users.noreply.github.com> Date: Wed, 6 Dec 2023 17:56:20 +0800 Subject: [PATCH 055/268] chore: fix document path (#615) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ fix document path ## Why ❔ ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --------- Co-authored-by: Igor Aleksanov --- docs/advanced/how_call_works.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/advanced/how_call_works.md b/docs/advanced/how_call_works.md index 4ba4859c4109..178a95b32398 100644 --- a/docs/advanced/how_call_works.md +++ b/docs/advanced/how_call_works.md @@ -69,7 +69,8 @@ opcodes similar to EVM, but operates on registers rather than a stack. We have t 'pure rust' without circuits (in the zk_evm repository), and the other has circuits (in the sync_vm repository). In this example, the api server uses the 'zk_evm' implementation without circuits. -Most of the code that the server uses to interact with the VM is in [core/lib/vm/src/vm.rs][vm_code]. +Most of the code that the server uses to interact with the VM is in +[core/lib/multivm/src/versions/vm_latest/implementation/execution.rs][vm_code]. In this line, we're calling self.state.cycle(), which executes a single VM instruction. You can see that we do a lot of things around this, such as executing multiple tracers after each instruction. This allows us to debug and provide From 66af65029428d38a80ccfca29a0b5eecb1938e13 Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Wed, 6 Dec 2023 15:31:06 +0400 Subject: [PATCH 056/268] chore: Remove era-reviewers from codeowners (#618) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Removes era-reviewers group from codeowners. ## Why ❔ - Too noisy. - We have internal processes for that anyways. ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zk fmt` and `zk lint`. - [ ] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --- CODEOWNERS | 1 - 1 file changed, 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 8cde1cc1ade7..eea7f1fa1373 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,4 +1,3 @@ -* @matter-labs/era-reviewers .github/release-please/** @RomanBrodetski @perekopskiy @Deniallugo @popzxc **/CHANGELOG.md @RomanBrodetski @perekopskiy @Deniallugo @popzxc CODEOWNERS @RomanBrodetski @perekopskiy @Deniallugo @popzxc From c4dc1e1d5b0e88cf22679aea46287b5725fcc9bc Mon Sep 17 00:00:00 2001 From: zksync-era-bot <147085853+zksync-era-bot@users.noreply.github.com> Date: Wed, 6 Dec 2023 13:33:28 +0100 Subject: [PATCH 057/268] chore(main): release core 18.6.1 (#616) :robot: I have created a release *beep* *boop* --- ## [18.6.1](https://github.com/matter-labs/zksync-era/compare/core-v18.6.0...core-v18.6.1) (2023-12-06) ### Performance Improvements * **external-node:** Use async miniblock sealing in external IO ([#611](https://github.com/matter-labs/zksync-era/issues/611)) ([5cf7210](https://github.com/matter-labs/zksync-era/commit/5cf7210dc77bb615944352f23ed39fad324b914f)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --- .github/release-please/manifest.json | 2 +- core/CHANGELOG.md | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/release-please/manifest.json b/.github/release-please/manifest.json index bce645236442..5fa24197fc78 100644 --- a/.github/release-please/manifest.json +++ b/.github/release-please/manifest.json @@ -1,5 +1,5 @@ { "sdk/zksync-rs": "0.4.0", - "core": "18.6.0", + "core": "18.6.1", "prover": "10.0.0" } diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 41439ca46514..77773c81ba7d 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [18.6.1](https://github.com/matter-labs/zksync-era/compare/core-v18.6.0...core-v18.6.1) (2023-12-06) + + +### Performance Improvements + +* **external-node:** Use async miniblock sealing in external IO ([#611](https://github.com/matter-labs/zksync-era/issues/611)) ([5cf7210](https://github.com/matter-labs/zksync-era/commit/5cf7210dc77bb615944352f23ed39fad324b914f)) + ## [18.6.0](https://github.com/matter-labs/zksync-era/compare/core-v18.5.0...core-v18.6.0) (2023-12-05) From c7d4315fcc2711b7a6435c1414f61896d85ede8f Mon Sep 17 00:00:00 2001 From: Artem Makhortov <13339874+artmakh@users.noreply.github.com> Date: Wed, 6 Dec 2023 19:45:54 +0700 Subject: [PATCH 058/268] feat(hyperchain-wizard): zkStack CLI GPU support (#612) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Support for creating zk hyperchain via zk cli with GPU-based provers ## Why ❔ ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [X] Code has been formatted via `zk fmt` and `zk lint`. - [ ] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --------- Co-authored-by: Igor Borodin --- docker-compose-zkstack-common.yml | 35 ++++++ ...=> docker-compose-hyperchain-template.hbs} | 81 +++++++++++--- infrastructure/zk/src/hyperchain_wizard.ts | 104 +++++++++++++----- infrastructure/zk/src/prover_setup.ts | 72 ++++++++---- infrastructure/zk/src/up.ts | 8 +- 5 files changed, 230 insertions(+), 70 deletions(-) create mode 100644 docker-compose-zkstack-common.yml rename etc/hyperchains/{docker-compose-hyperchain-template => docker-compose-hyperchain-template.hbs} (70%) diff --git a/docker-compose-zkstack-common.yml b/docker-compose-zkstack-common.yml new file mode 100644 index 000000000000..5d92de5d31d2 --- /dev/null +++ b/docker-compose-zkstack-common.yml @@ -0,0 +1,35 @@ +version: '3.2' +networks: + zkstack: + driver: bridge +services: + geth: + image: "matterlabs/geth:latest" + ports: + - "127.0.0.1:8545:8545" + - "127.0.0.1:8546:8546" + volumes: + - type: bind + source: ./volumes/geth + target: /var/lib/geth/data + networks: + - zkstack + container_name: geth + postgres: + image: "postgres:14" + container_name: postgres + ports: + - "127.0.0.1:5432:5432" + volumes: + - type: bind + source: ./volumes/postgres + target: /var/lib/postgresql/data + environment: + # We bind only to 127.0.0.1, so setting insecure password is acceptable here + - POSTGRES_PASSWORD=notsecurepassword + command: + - "postgres" + - "-c" + - "max_connections=1000" + networks: + - zkstack diff --git a/etc/hyperchains/docker-compose-hyperchain-template b/etc/hyperchains/docker-compose-hyperchain-template.hbs similarity index 70% rename from etc/hyperchains/docker-compose-hyperchain-template rename to etc/hyperchains/docker-compose-hyperchain-template.hbs index 00cb0ebc2a72..a1b4f92e3ab9 100644 --- a/etc/hyperchains/docker-compose-hyperchain-template +++ b/etc/hyperchains/docker-compose-hyperchain-template.hbs @@ -1,13 +1,14 @@ version: '3.2' networks: - zkstack: - driver: bridge + zksync-era_zkstack: + external: true volumes: artifacts: services: zkstack-core: + container_name: zkstack-core networks: - - zkstack + - zksync-era_zkstack image: {{orgName}}/server-v2:latest command: ["--components", "tree_new,eth,data_fetcher,state_keeper,housekeeper,proof_data_handler"] healthcheck: @@ -24,20 +25,23 @@ services: - "3320:3320" # proof_data_handler api volumes: - artifacts:{{artifactsPath}} + zkstack-apis: networks: - - zkstack + - zksync-era_zkstack image: {{orgName}}/server-v2:latest command: ["--components", "http_api,ws_api"] env_file: - {{envFilePath}} environment: ZKSYNC_HOME: / + FRI_PROVER_GATEWAY_API_URL: http://zkstack-core:3320 ports: # assumes default ports in .env - "3071:3071" # health - "3312:3312" # prometheus metrics # we need a separate metrics port for each component - "3050:3050" # http_api - "3051:3051" # ws_api + {{#if hasProver}} # System requirements for CPU proving: # ~16+ CPU cores @@ -46,7 +50,7 @@ services: # - (PRO-47): Figure out how to properly set metrics ports for each service in env zkstack-prover-fri-gateway: networks: - - zkstack + - zksync-era_zkstack image: matterlabs/prover-fri-gateway:latest depends_on: zkstack-core: @@ -62,7 +66,7 @@ services: - {{proverSetupArtifacts}}:{{proverSetupArtifacts}} zkstack-witness-generator-basic-circuits: networks: - - zkstack + - zksync-era_zkstack image: matterlabs/witness-generator:latest command: ["--round", "basic_circuits"] env_file: @@ -72,9 +76,10 @@ services: volumes: - artifacts:{{artifactsPath}} - {{proverSetupArtifacts}}:{{proverSetupArtifacts}} + zkstack-witness-generator-leaf-aggregation: networks: - - zkstack + - zksync-era_zkstack image: matterlabs/witness-generator:latest command: ["--round", "leaf_aggregation"] env_file: @@ -84,9 +89,10 @@ services: volumes: - artifacts:{{artifactsPath}} - {{proverSetupArtifacts}}:{{proverSetupArtifacts}} + zkstack-witness-generator-node-aggregation: networks: - - zkstack + - zksync-era_zkstack image: matterlabs/witness-generator:latest command: ["--round", "node_aggregation"] env_file: @@ -96,9 +102,10 @@ services: volumes: - artifacts:{{artifactsPath}} - {{proverSetupArtifacts}}:{{proverSetupArtifacts}} + zkstack-witness-generator-scheduler: networks: - - zkstack + - zksync-era_zkstack image: matterlabs/witness-generator:latest command: ["--round", "scheduler"] env_file: @@ -108,10 +115,11 @@ services: volumes: - artifacts:{{artifactsPath}} - {{proverSetupArtifacts}}:{{proverSetupArtifacts}} - zkstack-prover-fri: + + zkstack-proof-fri-compressor: networks: - - zkstack - image: matterlabs/prover-fri:latest + - zksync-era_zkstack + image: matterlabs/proof-fri-compressor:latest env_file: - {{envFilePath}} # ports: # assumes default ports in .env @@ -119,16 +127,57 @@ services: volumes: - artifacts:{{artifactsPath}} - {{proverSetupArtifacts}}:{{proverSetupArtifacts}} - zkstack-proof-fri-compressor: + witness-vector-generator: networks: - - zkstack - image: matterlabs/proof-fri-compressor:latest + - zksync-era_zkstack + image: matterlabs/witness-vector-generator:latest + restart: always env_file: - {{envFilePath}} + deploy: + mode: replicated + replicas: {{witnessVectorGensCount}} # ports: # assumes default ports in .env # - "3312:3312" # prometheus metrics volumes: - artifacts:{{artifactsPath}} - {{proverSetupArtifacts}}:{{proverSetupArtifacts}} {{/if}} - \ No newline at end of file + {{#ifAnd hasProver hasCPUProver}} + zkstack-prover-cpu-fri: + networks: + - zksync-era_zkstack + image: matterlabs/prover-fri:latest + env_file: + - {{envFilePath}} + # - "3312:3312" # prometheus metrics + volumes: + - artifacts:{{artifactsPath}} + - {{proverSetupArtifacts}}:{{proverSetupArtifacts}} + {{/ifAnd}} + {{#ifAnd hasProver hasGPUProver}} + zkstack-prover-gpu-fri: + networks: + - zksync-era_zkstack + {{#if needBuildProver}} + build: # Needed for anything that is not NVIDIA CUDA_ARCH 89 + dockerfile: ./docker/prover-gpu-fri/Dockerfile + args: + CUDA_ARCH: {{cudaArch}} + {{else}} + image: matterlabs/prover-gpu-fri:latest # Only works for NVIDIA CUDA_ARCH 89 + {{/if}} + env_file: + - {{envFilePath}} + # - "3312:3312" # prometheus metrics + volumes: + - artifacts:{{artifactsPath}} + - {{proverSetupArtifacts}}:{{proverSetupArtifacts}} + security_opt: # HACK: Might work on vanilla Ubuntu distros without this + - seccomp:unconfined + deploy: + resources: + reservations: + devices: + - capabilities: [gpu] + {{/ifAnd}} diff --git a/infrastructure/zk/src/hyperchain_wizard.ts b/infrastructure/zk/src/hyperchain_wizard.ts index 1fdc191ec92c..05997127ca8a 100644 --- a/infrastructure/zk/src/hyperchain_wizard.ts +++ b/infrastructure/zk/src/hyperchain_wizard.ts @@ -30,7 +30,7 @@ enum BaseNetwork { enum ProverTypeOption { NONE = 'No (this hyperchain is for testing purposes only)', CPU = 'Yes - With a CPU implementation', - GPU = 'Yes - With a GPU implementation (Coming soon)' + GPU = 'Yes - With a GPU implementation' } export interface BasePromptOptions { @@ -43,6 +43,9 @@ export interface BasePromptOptions { skip?: ((state: object) => boolean | Promise) | boolean; } +// PLA:681 +let isLocalhost = false; + // An init command that allows configuring and spinning up a new hyperchain network. async function initHyperchain() { await announced('Initializing hyperchain creation', setupConfiguration()); @@ -70,6 +73,15 @@ async function initHyperchain() { await init(initArgs); + // if we used matterlabs/geth network, we need custom ENV file for hyperchain compose parts + // This breaks `zk status prover` command, but neccessary for working in isolated docker-network + // TODO: Think about better implementation + // PLA:681 + if (isLocalhost) { + wrapEnvModify('ETH_CLIENT_WEB3_URL', 'http://geth:8545'); + wrapEnvModify('DATABASE_URL', 'postgres://postgres:notsecurepassword@postgres:5432/zksync_local'); + } + env.mergeInitToEnv(); console.log(announce(`\nYour hyperchain configuration is available at ${process.env.ENV_FILE}\n`)); @@ -251,9 +263,12 @@ async function setHyperchainMetadata() { feeReceiverAddress = keyResults.feeReceiver; } } else { + // PLA:681 + isLocalhost = true; l1Rpc = 'http://localhost:8545'; l1Id = 9; - databaseUrl = 'postgres://postgres@localhost/zksync_local'; + databaseUrl = 'postgres://postgres:notsecurepassword@localhost:5432/zksync_local'; + wrapEnvModify('DATABASE_URL', databaseUrl); const richWalletsRaw = await fetch( 'https://raw.githubusercontent.com/matter-labs/local-setup/main/rich-wallets.json' @@ -267,7 +282,7 @@ async function setHyperchainMetadata() { feeReceiver = undefined; feeReceiverAddress = richWallets[3].address; - await up(); + await up('docker-compose-zkstack-common.yml'); await announced('Ensuring databases are up', db.wait()); } @@ -318,7 +333,8 @@ async function setHyperchainMetadata() { await compileConfig(environment); env.set(environment); - + // TODO: Generate url for data-compressor with selected region or fix env variable for keys location + // PLA-595 wrapEnvModify('DATABASE_URL', databaseUrl); wrapEnvModify('ETH_CLIENT_CHAIN_ID', l1Id.toString()); wrapEnvModify('ETH_CLIENT_WEB3_URL', l1Rpc); @@ -362,23 +378,6 @@ async function setupHyperchainProver() { proverType = proverResults.prover; - if (proverType === ProverTypeOption.GPU) { - const gpuQuestions: BasePromptOptions[] = [ - { - message: 'GPU prover is not yet available. Do you want to use the CPU implementation?', - name: 'prover', - type: 'confirm', - required: true - } - ]; - - const gpuResults: any = await enquirer.prompt(gpuQuestions); - - if (gpuResults.prover) { - proverType = ProverTypeOption.CPU; - } - } - switch (proverType) { case ProverTypeOption.NONE: wrapEnvModify('ETH_SENDER_SENDER_PROOF_SENDING_MODE', 'SkipEveryProof'); @@ -396,9 +395,12 @@ function printAddressInfo(name: string, address: string) { } async function initializeTestERC20s() { + // TODO: For now selecting NO breaks server-core deployment, should be always YES or create empty-mock file for v2-core + // PLA-595 const questions: BasePromptOptions[] = [ { - message: 'Do you want to deploy some test ERC20s to your hyperchain (only use on testing scenarios)?', + message: + 'Do you want to deploy some test ERC20s to your hyperchain? NB: Temporary broken, always select YES', name: 'deployERC20s', type: 'confirm' } @@ -410,7 +412,7 @@ async function initializeTestERC20s() { wrapEnvModify('DEPLOY_TEST_TOKENS', 'true'); console.log( warning( - `The addresses for the tokens will be available at the /etc/tokens/${getEnv( + `The addresses for the generated test ECR20 tokens will be available at the /etc/tokens/${getEnv( process.env.CHAIN_ETH_NETWORK! )}.json file.` ) @@ -474,7 +476,7 @@ async function initializeWethTokenForHyperchain() { async function startServer() { const YES_DEFAULT = 'Yes (default components)'; const YES_CUSTOM = 'Yes (custom components)'; - const NO = 'Not right now'; + const NO = 'Not right now (you can now configure prover, generate docker files, or just run the server later)'; const questions: BasePromptOptions[] = [ { @@ -651,10 +653,8 @@ async function generateDockerImages(cmd: Command) { async function _generateDockerImages(_orgName?: string) { console.log(warning(`\nThis process will build the docker images and it can take a while. Please be patient.\n`)); - const envName = await selectHyperchainConfiguration(); env.set(envName); - const orgName = _orgName || envName; await docker.customBuildForHyperchain('server-v2', orgName); @@ -662,7 +662,12 @@ async function _generateDockerImages(_orgName?: string) { console.log(warning(`\nDocker image for server created: Server image: ${orgName}/server-v2:latest\n`)); let hasProver = false; + let hasGPUProver = false; + let hasCPUProver = false; + let needBuildProver = false; let artifactsPath, proverSetupArtifacts; + let witnessVectorGensCount = 0; + let cudaArch = ''; if (process.env.ETH_SENDER_SENDER_PROOF_SENDING_MODE !== 'SkipEveryProof') { hasProver = true; @@ -672,9 +677,27 @@ async function _generateDockerImages(_orgName?: string) { } if (process.env.PROVER_TYPE === ProverType.GPU) { - throw new Error('GPU prover configuration not available yet'); + hasGPUProver = true; + const cudaArchPrompt: BasePromptOptions[] = [ + { + message: + 'What is your GPU Compute Capability version? You can find it in table here - https://en.wikipedia.org/wiki/CUDA#GPUs_supported. Input only 2 numbers withous dot, e.g. if you have RTX 3070 -> Compute Capability 8.6 -> Answer is 86', + name: 'cudaArch', + type: 'input', + required: true + } + ]; + const cudaRes: any = await enquirer.prompt(cudaArchPrompt); + cudaArch = cudaRes.cudaArch; + } else { + hasCPUProver = true; } + // TODO: Make this param configurable + // We need to generate at least 4 witnes-vector-generators per prover, but it can be less, and can be more + // PLA-683 + witnessVectorGensCount = 4; + // For Now use only the public images. Too soon to allow prover to be customized // await docker.customBuildForHyperchain('witness-generator', orgName); // await docker.customBuildForHyperchain('witness-vector-generator', orgName); @@ -689,15 +712,38 @@ async function _generateDockerImages(_orgName?: string) { // } } + // TODO: Autodetect version via nvidia-smi + // We have precompiled GPU prover image only for CUDA arch 89 aka ADA, all others need to be re-build + // PLA-682 + if (process.env.PROVER_TYPE === ProverType.GPU && cudaArch != '89') { + needBuildProver = true; + } + const composeArgs = { envFilePath: `./etc/env/${envName}.env`, orgName, hasProver, artifactsPath, - proverSetupArtifacts + proverSetupArtifacts, + hasGPUProver, + hasCPUProver, + cudaArch, + needBuildProver, + witnessVectorGensCount }; - const templateFileName = './etc/hyperchains/docker-compose-hyperchain-template'; + // Creating simple handlebars helper "if (foo AND bar)" to reduce copypaste in compose template + Handlebars.registerHelper( + 'ifAnd', + function (this: boolean, a: boolean, b: boolean, options: Handlebars.HelperOptions) { + if (a && b) { + return options.fn(this); + } + return options.inverse(this); + } + ); + + const templateFileName = './etc/hyperchains/docker-compose-hyperchain-template.hbs'; const templateString = fs.existsSync(templateFileName) && fs.readFileSync(templateFileName).toString().trim(); const template = Handlebars.compile(templateString); const result = template(composeArgs); diff --git a/infrastructure/zk/src/prover_setup.ts b/infrastructure/zk/src/prover_setup.ts index b438eea055df..d1de98166d0b 100644 --- a/infrastructure/zk/src/prover_setup.ts +++ b/infrastructure/zk/src/prover_setup.ts @@ -12,6 +12,12 @@ export enum ProverType { GPU = 'gpu' } +enum KeysRegionOption { + US = 'us', + EU = 'europe', + ASIA = 'asia' +} + export async function setupProver(proverType: ProverType) { // avoid doing work if receives the wrong param from the CLI if (proverType == ProverType.GPU || proverType == ProverType.CPU) { @@ -37,14 +43,16 @@ export async function setupProver(proverType: ProverType) { } } -async function downloadCSR(proverType: ProverType) { +async function downloadCSR(proverType: ProverType, region: string) { const currentEnv = env.get(); fs.mkdirSync(`${process.env.ZKSYNC_HOME}/etc/hyperchains/prover-keys/${currentEnv}/${proverType}/`, { recursive: true }); process.chdir(`${process.env.ZKSYNC_HOME}/etc/hyperchains/prover-keys/${currentEnv}/${proverType}/`); console.log(chalk.yellow('Downloading ceremony (CSR) file')); - await utils.spawn('wget -c https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_2^24.key'); + await utils.spawn( + `wget -q --show-progress -c https://storage.googleapis.com/matterlabs-setup-keys-${region}/setup-keys/setup_2^24.key` + ); await utils.sleep(1); process.chdir(process.env.ZKSYNC_HOME as string); wrapEnvModify('CRS_FILE', `${process.env.ZKSYNC_HOME}/etc/hyperchains/prover-keys/${currentEnv}/${proverType}/`); @@ -53,6 +61,8 @@ async function downloadCSR(proverType: ProverType) { async function setupProverKeys(proverType: ProverType) { const DOWNLOAD = 'Download default keys'; const GENERATE = 'Generate locally'; + let keysRegion = ''; + const questions: BasePromptOptions[] = [ { message: @@ -65,9 +75,20 @@ async function setupProverKeys(proverType: ProverType) { const results: any = await enquirer.prompt(questions); - await downloadCSR(proverType); + const proverKeysQuestions: BasePromptOptions[] = [ + { + message: 'From which s3 region download ceremony (CSR) file and/or Prover Keys?', + name: 'proverKeys', + type: 'select', + required: true, + choices: [KeysRegionOption.US, KeysRegionOption.EU, KeysRegionOption.ASIA] + } + ]; + const proverKeysResults: any = await enquirer.prompt(proverKeysQuestions); + keysRegion = proverKeysResults.proverKeys; + await downloadCSR(proverType, keysRegion); if (results.proverKeys == DOWNLOAD) { - await downloadDefaultSetupKeys(proverType); + await downloadDefaultSetupKeys(proverType, keysRegion); } else { await generateAllSetupData(proverType); } @@ -165,17 +186,20 @@ async function generateSetupDataForRecursiveLayers(proverType: ProverType) { async function generateSetupData(isBaseLayer: boolean, proverType: ProverType) { const currentEnv = env.get(); - fs.mkdirSync(`${process.env.ZKSYNC_HOME}/etc/hyperchains/prover-keys/${currentEnv}/${proverType}/`, { - recursive: true - }); - process.chdir(`${process.env.ZKSYNC_HOME}/prover`); - await utils.spawn( - `for i in {1..${isBaseLayer ? '13' : '15'}}; do zk f cargo run ${ - proverType == ProverType.GPU ? '--features "gpu"' : '' - } --release --bin zksync_setup_data_generator_fri -- --numeric-circuit $i ${ + + const proverKeysDir = `${process.env.ZKSYNC_HOME}/etc/hyperchains/prover-keys/${currentEnv}/${proverType}/`; + fs.mkdirSync(proverKeysDir, { recursive: true }); + const proverDir = `${process.env.ZKSYNC_HOME}/prover`; + process.chdir(proverDir); + const range = isBaseLayer ? 13 : 15; + const gpuFeatureFlag = proverType == ProverType.GPU ? '--features "gpu"' : ''; + for (let i = 1; i <= range; i++) { + const spawnCommand = `zk f cargo run ${gpuFeatureFlag} --release --bin zksync_setup_data_generator_fri -- --numeric-circuit ${i} ${ isBaseLayer ? '--is_base_layer' : '' - }; done` - ); + }`; + await utils.spawn(spawnCommand); + } + process.chdir(process.env.ZKSYNC_HOME as string); } @@ -184,7 +208,7 @@ async function generateAllSetupData(proverType: ProverType) { await generateSetupDataForRecursiveLayers(proverType); } -async function downloadDefaultSetupKeys(proverType: ProverType, region: 'us' | 'asia' | 'europe' = 'us') { +async function downloadDefaultSetupKeys(proverType: ProverType, region: string) { const proverKeysUrls = require(`${process.env.ZKSYNC_HOME}/prover/setup-data-${proverType}-keys.json`); const currentEnv = env.get(); await downloadFilesFromGCP( @@ -216,14 +240,16 @@ async function downloadFilesFromGCP(gcpUri: string, destination: string): Promis fs.mkdirSync(destination, { recursive: true }); process.chdir(destination); - const length = files.length; - for (const index in files) { - console.log(chalk.yellow(`Downloading file ${Number(index) + 1} of ${length}`)); - const file = files[index]; - await utils.spawn(`wget -c ${file}`); - await utils.sleep(1); - console.log(``); - } + // Download all files in parallel + await Promise.all( + files.map((file, index) => { + return (async () => { + console.log(chalk.yellow(`Downloading file ${index + 1} of ${files.length}`)); + await utils.spawn(`wget -q --show-progress -c "${file}"`); + await utils.sleep(1); + })(); + }) + ); process.chdir(process.env.ZKSYNC_HOME as string); } diff --git a/infrastructure/zk/src/up.ts b/infrastructure/zk/src/up.ts index 5057f4ca9d3c..94be874dc388 100644 --- a/infrastructure/zk/src/up.ts +++ b/infrastructure/zk/src/up.ts @@ -8,9 +8,13 @@ function createVolumes() { fs.mkdirSync(`${process.env.ZKSYNC_HOME}/volumes/postgres`, { recursive: true }); } -export async function up() { +export async function up(composeFile?: string) { createVolumes(); - await utils.spawn('docker-compose up -d geth postgres'); + if (composeFile) { + await utils.spawn(`docker compose -f ${composeFile} up -d geth postgres`); + } else { + await utils.spawn('docker compose up -d geth postgres'); + } } export const command = new Command('up').description('start development containers').action(up); From ec5907b70ff7d868a05b685a1641d96dc4fa9d69 Mon Sep 17 00:00:00 2001 From: Lech <88630083+Artemka374@users.noreply.github.com> Date: Wed, 6 Dec 2023 16:57:06 +0200 Subject: [PATCH 059/268] fix: Cursor not moving correctly after poll in `get_filter_changes` (#546) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ When polling filter changes, add 1 to actual from_block value ## Why ❔ Otherwise, last block that was included in poll will be included to the next one. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --------- Co-authored-by: Fedor Sakharov --- core/lib/dal/sqlx-data.json | 54 +++++++++---------- core/lib/dal/src/blocks_web3_dal.rs | 6 +-- core/lib/dal/src/events_web3_dal.rs | 13 ++--- core/lib/types/src/api/mod.rs | 2 +- .../src/api_server/web3/namespaces/eth.rs | 45 ++++++++++------ .../src/api_server/web3/namespaces/zks.rs | 2 +- .../zksync_core/src/api_server/web3/state.rs | 3 +- .../src/api_server/web3/tests/mod.rs | 24 ++++----- 8 files changed, 76 insertions(+), 73 deletions(-) diff --git a/core/lib/dal/sqlx-data.json b/core/lib/dal/sqlx-data.json index 9084adb61cda..2d1773482ea1 100644 --- a/core/lib/dal/sqlx-data.json +++ b/core/lib/dal/sqlx-data.json @@ -1928,6 +1928,33 @@ }, "query": "SELECT * from prover_jobs where id=$1" }, + "2044947d6d29f29cda508b2160c39f74a8bfd524afa2ffc20a98ae039bc86ed7": { + "describe": { + "columns": [ + { + "name": "number", + "ordinal": 0, + "type_info": "Int8" + }, + { + "name": "hash", + "ordinal": 1, + "type_info": "Bytea" + } + ], + "nullable": [ + false, + false + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + } + }, + "query": "SELECT number, hash FROM miniblocks WHERE number >= $1 ORDER BY number ASC LIMIT $2" + }, "20b22fd457417e9a72f5941887448f9a11b97b449db4759da0b9d368ce93996b": { "describe": { "columns": [ @@ -9492,33 +9519,6 @@ }, "query": "\n SELECT id, circuit_input_blob_url FROM prover_jobs\n WHERE status='successful'\n AND circuit_input_blob_url is NOT NULL\n AND updated_at < NOW() - INTERVAL '30 days'\n LIMIT $1;\n " }, - "b479b7d3334f8d4566c294a44e2adb282fbc66a87be5c248c65211c2a8a07db0": { - "describe": { - "columns": [ - { - "name": "number", - "ordinal": 0, - "type_info": "Int8" - }, - { - "name": "hash", - "ordinal": 1, - "type_info": "Bytea" - } - ], - "nullable": [ - false, - false - ], - "parameters": { - "Left": [ - "Int8", - "Int8" - ] - } - }, - "query": "SELECT number, hash FROM miniblocks WHERE number > $1 ORDER BY number ASC LIMIT $2" - }, "b4a3c902646725188f7c79ebac992cdce5896fc6fcc9f485c0cba9d90c4c982c": { "describe": { "columns": [ diff --git a/core/lib/dal/src/blocks_web3_dal.rs b/core/lib/dal/src/blocks_web3_dal.rs index e42a645966ff..0c2a8b4e1885 100644 --- a/core/lib/dal/src/blocks_web3_dal.rs +++ b/core/lib/dal/src/blocks_web3_dal.rs @@ -161,15 +161,15 @@ impl BlocksWeb3Dal<'_, '_> { })) } - /// Returns hashes of blocks with numbers greater than `from_block` and the number of the last block. - pub async fn get_block_hashes_after( + /// Returns hashes of blocks with numbers starting from `from_block` and the number of the last block. + pub async fn get_block_hashes_since( &mut self, from_block: MiniblockNumber, limit: usize, ) -> sqlx::Result<(Vec, Option)> { let rows = sqlx::query!( "SELECT number, hash FROM miniblocks \ - WHERE number > $1 \ + WHERE number >= $1 \ ORDER BY number ASC \ LIMIT $2", from_block.0 as i64, diff --git a/core/lib/dal/src/events_web3_dal.rs b/core/lib/dal/src/events_web3_dal.rs index 82a65c18444b..7cdf2dba6467 100644 --- a/core/lib/dal/src/events_web3_dal.rs +++ b/core/lib/dal/src/events_web3_dal.rs @@ -6,9 +6,7 @@ use zksync_types::{ }; use crate::{ - instrument::InstrumentExt, - models::{storage_block::web3_block_number_to_sql, storage_event::StorageWeb3Log}, - SqlxError, StorageProcessor, + instrument::InstrumentExt, models::storage_event::StorageWeb3Log, SqlxError, StorageProcessor, }; #[derive(Debug)] @@ -119,10 +117,8 @@ impl EventsWeb3Dal<'_, '_> { let mut where_sql = format!("(miniblock_number >= {})", filter.from_block.0 as i64); - if let Some(to_block) = filter.to_block { - let block_sql = web3_block_number_to_sql(to_block); - where_sql += &format!(" AND (miniblock_number <= {})", block_sql); - } + where_sql += &format!(" AND (miniblock_number <= {})", filter.to_block.0 as i64); + if !filter.addresses.is_empty() { where_sql += &format!(" AND (address = ANY(${}))", arg_index); arg_index += 1; @@ -172,7 +168,6 @@ impl EventsWeb3Dal<'_, '_> { #[cfg(test)] mod tests { - use zksync_types::api::BlockNumber; use zksync_types::{Address, H256}; use super::*; @@ -185,7 +180,7 @@ mod tests { let events_web3_dal = EventsWeb3Dal { storage }; let filter = GetLogsFilter { from_block: MiniblockNumber(100), - to_block: Some(BlockNumber::Number(200.into())), + to_block: MiniblockNumber(200), addresses: vec![Address::from_low_u64_be(123)], topics: vec![(0, vec![H256::from_low_u64_be(456)])], }; diff --git a/core/lib/types/src/api/mod.rs b/core/lib/types/src/api/mod.rs index 24ac74ab335e..1ad54ce6d1ab 100644 --- a/core/lib/types/src/api/mod.rs +++ b/core/lib/types/src/api/mod.rs @@ -548,7 +548,7 @@ pub struct TransactionDetails { #[derive(Debug, Clone)] pub struct GetLogsFilter { pub from_block: MiniblockNumber, - pub to_block: Option, + pub to_block: MiniblockNumber, pub addresses: Vec
, pub topics: Vec<(u32, Vec)>, } diff --git a/core/lib/zksync_core/src/api_server/web3/namespaces/eth.rs b/core/lib/zksync_core/src/api_server/web3/namespaces/eth.rs index 4cabb8e15da3..0aa9255c3dbf 100644 --- a/core/lib/zksync_core/src/api_server/web3/namespaces/eth.rs +++ b/core/lib/zksync_core/src/api_server/web3/namespaces/eth.rs @@ -564,7 +564,7 @@ impl EthNamespace { .installed_filters .lock() .await - .add(TypedFilter::Blocks(last_block_number)); + .add(TypedFilter::Blocks(last_block_number + 1)); method_latency.observe(); Ok(idx) } @@ -773,14 +773,19 @@ impl EthNamespace { .map_err(|err| internal_error(METHOD_NAME, err))?; let (block_hashes, last_block_number) = conn .blocks_web3_dal() - .get_block_hashes_after(*from_block, self.state.api_config.req_entities_limit) + .get_block_hashes_since(*from_block, self.state.api_config.req_entities_limit) .await .map_err(|err| internal_error(METHOD_NAME, err))?; - *from_block = last_block_number.unwrap_or(*from_block); + + *from_block = match last_block_number { + Some(last_block_number) => last_block_number + 1, + None => *from_block, + }; + FilterChanges::Hashes(block_hashes) } - TypedFilter::PendingTransactions(from_timestamp) => { + TypedFilter::PendingTransactions(from_timestamp_excluded) => { let mut conn = self .state .connection_pool @@ -790,12 +795,14 @@ impl EthNamespace { let (tx_hashes, last_timestamp) = conn .transactions_web3_dal() .get_pending_txs_hashes_after( - *from_timestamp, + *from_timestamp_excluded, Some(self.state.api_config.req_entities_limit), ) .await .map_err(|err| internal_error(METHOD_NAME, err))?; - *from_timestamp = last_timestamp.unwrap_or(*from_timestamp); + + *from_timestamp_excluded = last_timestamp.unwrap_or(*from_timestamp_excluded); + FilterChanges::Hashes(tx_hashes) } @@ -816,16 +823,26 @@ impl EthNamespace { } else { vec![] }; + + let mut to_block = self + .state + .resolve_filter_block_number(filter.to_block) + .await?; + + if matches!(filter.to_block, Some(BlockNumber::Number(_))) { + to_block = to_block.min( + self.state + .resolve_filter_block_number(Some(BlockNumber::Latest)) + .await?, + ); + } + let get_logs_filter = GetLogsFilter { from_block: *from_block, - to_block: filter.to_block, + to_block, addresses, topics, }; - let to_block = self - .state - .resolve_filter_block_number(filter.to_block) - .await?; let mut storage = self .state @@ -859,11 +876,7 @@ impl EthNamespace { .get_logs(get_logs_filter, i32::MAX as usize) .await .map_err(|err| internal_error(METHOD_NAME, err))?; - *from_block = logs - .last() - .map(|log| MiniblockNumber(log.block_number.unwrap().as_u32())) - .unwrap_or(*from_block); - // FIXME: why is `from_block` not updated? + *from_block = to_block + 1; FilterChanges::Logs(logs) } }; diff --git a/core/lib/zksync_core/src/api_server/web3/namespaces/zks.rs b/core/lib/zksync_core/src/api_server/web3/namespaces/zks.rs index 7f38c6afc525..9e3a90dde043 100644 --- a/core/lib/zksync_core/src/api_server/web3/namespaces/zks.rs +++ b/core/lib/zksync_core/src/api_server/web3/namespaces/zks.rs @@ -283,7 +283,7 @@ impl ZksNamespace { .get_logs( GetLogsFilter { from_block: first_miniblock_of_l1_batch, - to_block: Some(block_number.0.into()), + to_block: block_number, addresses: vec![L1_MESSENGER_ADDRESS], topics: vec![(2, vec![address_to_h256(&sender)]), (3, vec![msg])], }, diff --git a/core/lib/zksync_core/src/api_server/web3/state.rs b/core/lib/zksync_core/src/api_server/web3/state.rs index 0463d4823209..ea52b2ae61cc 100644 --- a/core/lib/zksync_core/src/api_server/web3/state.rs +++ b/core/lib/zksync_core/src/api_server/web3/state.rs @@ -505,9 +505,10 @@ impl RpcState { .enumerate() .filter_map(|(idx, topics)| topics.map(|topics| (idx as u32 + 1, topics.0))) .collect(); + let get_logs_filter = GetLogsFilter { from_block, - to_block: filter.to_block, + to_block, addresses, topics, }; diff --git a/core/lib/zksync_core/src/api_server/web3/tests/mod.rs b/core/lib/zksync_core/src/api_server/web3/tests/mod.rs index 12bb6481213d..1bb14df52fa2 100644 --- a/core/lib/zksync_core/src/api_server/web3/tests/mod.rs +++ b/core/lib/zksync_core/src/api_server/web3/tests/mod.rs @@ -386,10 +386,10 @@ impl HttpTest for LogFilterChanges { assert_logs_match(&topics_logs, &[events[1], events[3]]); let new_all_logs = client.get_filter_changes(all_logs_filter_id).await?; - let FilterChanges::Logs(new_all_logs) = new_all_logs else { + let FilterChanges::Hashes(new_all_logs) = new_all_logs else { panic!("Unexpected getFilterChanges output: {:?}", new_all_logs); }; - assert_eq!(new_all_logs, all_logs); // FIXME(#546): update test after behavior is fixed + assert!(new_all_logs.is_empty()); Ok(()) } } @@ -458,11 +458,10 @@ impl HttpTest for LogFilterChangesWithBlockBoundaries { }; assert_logs_match(&lower_bound_logs, &new_events); - // FIXME(#546): update test after behavior is fixed let new_upper_bound_logs = client.get_filter_changes(upper_bound_filter_id).await?; - assert_eq!(new_upper_bound_logs, FilterChanges::Logs(upper_bound_logs)); + assert_matches!(new_upper_bound_logs, FilterChanges::Hashes(hashes) if hashes.is_empty()); let new_bounded_logs = client.get_filter_changes(bounded_filter_id).await?; - assert_eq!(new_bounded_logs, FilterChanges::Logs(bounded_logs)); + assert_matches!(new_bounded_logs, FilterChanges::Hashes(hashes) if hashes.is_empty()); // Add miniblock #3. It should not be picked up by the bounded and upper bound filters, // and should be picked up by the lower bound filter. @@ -472,27 +471,22 @@ impl HttpTest for LogFilterChangesWithBlockBoundaries { let new_events: Vec<_> = new_events.iter().collect(); let bounded_logs = client.get_filter_changes(bounded_filter_id).await?; - let FilterChanges::Logs(bounded_logs) = bounded_logs else { + let FilterChanges::Hashes(bounded_logs) = bounded_logs else { panic!("Unexpected getFilterChanges output: {:?}", bounded_logs); }; - assert!(bounded_logs - .iter() - .all(|log| log.block_number.unwrap() < 3.into())); + assert!(bounded_logs.is_empty()); let upper_bound_logs = client.get_filter_changes(upper_bound_filter_id).await?; - let FilterChanges::Logs(upper_bound_logs) = upper_bound_logs else { + let FilterChanges::Hashes(upper_bound_logs) = upper_bound_logs else { panic!("Unexpected getFilterChanges output: {:?}", upper_bound_logs); }; - assert!(upper_bound_logs - .iter() - .all(|log| log.block_number.unwrap() < 3.into())); + assert!(upper_bound_logs.is_empty()); let lower_bound_logs = client.get_filter_changes(lower_bound_filter_id).await?; let FilterChanges::Logs(lower_bound_logs) = lower_bound_logs else { panic!("Unexpected getFilterChanges output: {:?}", lower_bound_logs); }; - let start_idx = lower_bound_logs.len() - 4; - assert_logs_match(&lower_bound_logs[start_idx..], &new_events); + assert_logs_match(&lower_bound_logs, &new_events); Ok(()) } } From 8a8cad6ce62f2d34bb34adcd956f6920c08f94b8 Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Wed, 6 Dec 2023 12:35:47 -0600 Subject: [PATCH 060/268] fix: update google cloud dependencies that do not depend on rsa (#622) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ This PR updates the dependencies of `google-cloud-storage` and `google-cloud-auth`. The changes are as follows: - From google-cloud-storage = "0.12.0" to google-cloud-storage = "0.15.0" - From google-cloud-auth = "0.11.0" to google-cloud-auth = "0.13.0" Relevant google-cloud changes: https://github.com/yoshidan/google-cloud-rust/pull/217 ## Why ❔ The primary reason for these updates is to address a security vulnerability associated with the `rsa` crate, as indicated by a recent `cargo-deny` check. The vulnerability (Marvin Attack, RUSTSEC-2023-0071) was detected in rsa v0.6.1, which is a dependency of `google-cloud-storage v0.12.0`. By updating to `google-cloud-storage v0.15.0`, we eliminate the use of the `rsa` crate, as the newer version of `google-cloud-storage` does not depend on it. Similarly, `google-cloud-auth` is updated for compatibility. Cargo deny output: ``` error[vulnerability]: Marvin Attack: potential key recovery through timing sidechannels ┌─ /Users/dustinbrickwood/Documents/dev/dut/forks/foundry-zksync/Cargo.lock:759:1 │ 759 │ rsa 0.6.1 registry+https://github.com/rust-lang/crates.io-index │ --------------------------------------------------------------- security vulnerability detected │ = ID: RUSTSEC-2023-0071 = Advisory: https://rustsec.org/advisories/RUSTSEC-2023-0071 = ### Impact Due to a non-constant-time implementation, information about the private key is leaked through timing information which is observable over the network. An attacker may be able to use that information to recover the key. ### Patches No patch is yet available, however work is underway to migrate to a fully constant-time implementation. ### Workarounds The only currently available workaround is to avoid using the `rsa` crate in settings where attackers are able to observe timing information, e.g. local use on a non-compromised computer is fine. ### References This vulnerability was discovered as part of the "[Marvin Attack]", which revealed several implementations of RSA including OpenSSL had not properly mitigated timing sidechannel attacks. [Marvin Attack]: https://people.redhat.com/~hkario/marvin/ = Announcement: https://github.com/RustCrypto/RSA/issues/19#issuecomment-1822995643 = Solution: No safe upgrade is available! = rsa v0.6.1 └── google-cloud-storage v0.12.0 └── zksync_object_store v0.1.0 ├── zksync_core v0.1.0 │ └── era_test_node v0.1.0-alpha.12 │ └── era_revm v0.0.1-alpha │ ├── foundry-common v0.2.0 │ │ ├── anvil v0.2.0 │ │ │ ├── (dev) forge v0.2.0 │ │ │ └── (dev) zkforge v0.2.0 │ │ ├── cast v0.2.0 │ │ ├── chisel v0.2.0 │ │ ├── forge v0.2.0 (*) │ │ ├── foundry-cli v0.2.0 │ │ │ ├── cast v0.2.0 (*) │ │ │ ├── chisel v0.2.0 (*) │ │ │ ├── forge v0.2.0 (*) │ │ │ ├── zkcast v0.2.0 │ │ │ │ └── zkforge v0.2.0 (*) │ │ │ └── zkforge v0.2.0 (*) │ │ ├── foundry-debugger v0.2.0 │ │ │ ├── forge v0.2.0 (*) │ │ │ ├── foundry-cli v0.2.0 (*) │ │ │ └── zkforge v0.2.0 (*) │ │ ├── foundry-evm v0.2.0 │ │ │ ├── anvil v0.2.0 (*) │ │ │ ├── anvil-core v0.2.0 │ │ │ │ └── anvil v0.2.0 (*) │ │ │ ├── cast v0.2.0 (*) │ │ │ ├── chisel v0.2.0 (*) │ │ │ ├── forge v0.2.0 (*) │ │ │ ├── foundry-cli v0.2.0 (*) │ │ │ ├── foundry-debugger v0.2.0 (*) │ │ │ ├── zkcast v0.2.0 (*) │ │ │ └── zkforge v0.2.0 (*) │ │ ├── foundry-test-utils v0.2.0 │ │ │ ├── (dev) cast v0.2.0 (*) │ │ │ ├── (dev) forge v0.2.0 (*) │ │ │ ├── (dev) zkcast v0.2.0 (*) │ │ │ └── (dev) zkforge v0.2.0 (*) │ │ ├── (dev) foundry-utils v0.2.0 │ │ │ ├── anvil v0.2.0 (*) │ │ │ ├── anvil-core v0.2.0 (*) │ │ │ ├── cast v0.2.0 (*) │ │ │ ├── chisel v0.2.0 (*) │ │ │ ├── forge v0.2.0 (*) │ │ │ ├── forge-doc v0.2.0 │ │ │ │ ├── forge v0.2.0 (*) │ │ │ │ └── zkforge v0.2.0 (*) │ │ │ ├── foundry-cli v0.2.0 (*) │ │ │ ├── foundry-debugger v0.2.0 (*) │ │ │ ├── (dev) foundry-evm v0.2.0 (*) │ │ │ ├── foundry-test-utils v0.2.0 (*) │ │ │ ├── zkcast v0.2.0 (*) │ │ │ └── zkforge v0.2.0 (*) │ │ ├── zkcast v0.2.0 (*) │ │ └── zkforge v0.2.0 (*) │ └── foundry-evm v0.2.0 (*) └── zksync_prover_utils v0.1.0 ├── zksync_core v0.1.0 (*) └── zksync_verification_key_generator_and_server v0.1.0 └── zksync_core v0.1.0 (*) ``` ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --- Cargo.lock | 161 +++++++++---------------------- core/lib/object_store/Cargo.toml | 4 +- deny.toml | 1 - 3 files changed, 47 insertions(+), 119 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3fbf3deb35e1..ec650188c8a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1436,12 +1436,6 @@ dependencies = [ "windows-sys 0.45.0", ] -[[package]] -name = "const-oid" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" - [[package]] name = "const-oid" version = "0.9.5" @@ -1748,16 +1742,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -[[package]] -name = "crypto-bigint" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" -dependencies = [ - "generic-array 0.14.7", - "subtle", -] - [[package]] name = "crypto-bigint" version = "0.4.9" @@ -1948,24 +1932,13 @@ dependencies = [ "uuid", ] -[[package]] -name = "der" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" -dependencies = [ - "const-oid 0.7.1", - "crypto-bigint 0.3.2", - "pem-rfc7468", -] - [[package]] name = "der" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" dependencies = [ - "const-oid 0.9.5", + "const-oid", "zeroize", ] @@ -1975,7 +1948,8 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ - "const-oid 0.9.5", + "const-oid", + "pem-rfc7468", "zeroize", ] @@ -2797,9 +2771,9 @@ dependencies = [ [[package]] name = "google-cloud-auth" -version = "0.11.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644f40175857d0b8d7b6cad6cd9594284da5041387fa2ddff30ab6d8faef65eb" +checksum = "af1087f1fbd2dd3f58c17c7574ddd99cd61cbbbc2c4dc81114b8687209b196cb" dependencies = [ "async-trait", "base64 0.21.5", @@ -2819,9 +2793,9 @@ dependencies = [ [[package]] name = "google-cloud-metadata" -version = "0.3.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96e4ad0802d3f416f62e7ce01ac1460898ee0efc98f8b45cd4aab7611607012f" +checksum = "cc279bfb50487d7bcd900e8688406475fc750fe474a835b2ab9ade9eb1fc90e2" dependencies = [ "reqwest", "thiserror", @@ -2830,11 +2804,12 @@ dependencies = [ [[package]] name = "google-cloud-storage" -version = "0.12.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "215abab97e07d144428425509c1dad07e57ea72b84b21bcdb6a8a5f12a5c4932" +checksum = "ac04b29849ebdeb9fb008988cc1c4d1f0c9d121b4c7f1ddeb8061df124580e93" dependencies = [ "async-stream", + "async-trait", "base64 0.21.5", "bytes 1.5.0", "futures-util", @@ -2844,10 +2819,10 @@ dependencies = [ "hex", "once_cell", "percent-encoding", + "pkcs8 0.10.2", "regex", "reqwest", - "ring", - "rsa", + "ring 0.17.7", "serde", "serde_json", "sha2 0.10.8", @@ -3722,7 +3697,7 @@ checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ "base64 0.21.5", "pem", - "ring", + "ring 0.16.20", "serde", "serde_json", "simple_asn1", @@ -3770,9 +3745,6 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -dependencies = [ - "spin", -] [[package]] name = "lazycell" @@ -4398,23 +4370,6 @@ dependencies = [ "serde", ] -[[package]] -name = "num-bigint-dig" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.5", - "smallvec", - "zeroize", -] - [[package]] name = "num-complex" version = "0.3.1" @@ -4906,9 +4861,9 @@ dependencies = [ [[package]] name = "pem-rfc7468" -version = "0.3.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01de5d978f34aa4b2296576379fcc416034702fd94117c56ffd8a1a767cefb30" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" dependencies = [ "base64ct", ] @@ -5006,28 +4961,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkcs1" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78f66c04ccc83dd4486fd46c33896f4e17b24a7a3a6400dedc48ed0ddd72320" -dependencies = [ - "der 0.5.1", - "pkcs8 0.8.0", - "zeroize", -] - -[[package]] -name = "pkcs8" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" -dependencies = [ - "der 0.5.1", - "spki 0.5.4", - "zeroize", -] - [[package]] name = "pkcs8" version = "0.9.0" @@ -5882,12 +5815,26 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", - "untrusted", + "spin 0.5.2", + "untrusted 0.7.1", "web-sys", "winapi 0.3.9", ] +[[package]] +name = "ring" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +dependencies = [ + "cc", + "getrandom 0.2.10", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.48.0", +] + [[package]] name = "ripemd160" version = "0.9.1" @@ -5931,26 +5878,6 @@ dependencies = [ "zksync_storage", ] -[[package]] -name = "rsa" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cf22754c49613d2b3b119f0e5d46e34a2c628a937e3024b8762de4e7d8c710b" -dependencies = [ - "byteorder", - "digest 0.10.7", - "num-bigint-dig", - "num-integer", - "num-iter", - "num-traits", - "pkcs1", - "pkcs8 0.8.0", - "rand_core 0.6.4", - "smallvec", - "subtle", - "zeroize", -] - [[package]] name = "rustc-demangle" version = "0.1.23" @@ -5998,7 +5925,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" dependencies = [ "log", - "ring", + "ring 0.16.20", "rustls-webpki", "sct", ] @@ -6030,8 +5957,8 @@ version = "0.101.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", ] [[package]] @@ -6101,8 +6028,8 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", ] [[package]] @@ -6680,14 +6607,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] -name = "spki" -version = "0.5.4" +name = "spin" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" -dependencies = [ - "base64ct", - "der 0.5.1", -] +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "spki" @@ -7638,6 +7561,12 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "ureq" version = "2.8.0" diff --git a/core/lib/object_store/Cargo.toml b/core/lib/object_store/Cargo.toml index 941674d6e502..20f52a995a8c 100644 --- a/core/lib/object_store/Cargo.toml +++ b/core/lib/object_store/Cargo.toml @@ -17,8 +17,8 @@ zksync_types = { path = "../types" } anyhow = "1.0" async-trait = "0.1" bincode = "1" -google-cloud-storage = "0.12.0" -google-cloud-auth = "0.11.0" +google-cloud-storage = "0.15.0" +google-cloud-auth = "0.13.0" http = "0.2.9" tokio = { version = "1.21.2", features = ["full"] } tracing = "0.1" diff --git a/deny.toml b/deny.toml index 7fa3c835088a..b50b165b72f5 100644 --- a/deny.toml +++ b/deny.toml @@ -8,7 +8,6 @@ yanked = "warn" notice = "warn" ignore = [ "RUSTSEC-2023-0018", - "RUSTSEC-2023-0071" ] [licenses] From 43c09644254e574ad06e49040f3ca6c2a811f866 Mon Sep 17 00:00:00 2001 From: Thomas Knauth Date: Thu, 7 Dec 2023 08:26:47 +0100 Subject: [PATCH 061/268] docs: Include command to create rich L2 wallets. (#569) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Improve documentation by including the command to create rich L2 wallets. ## Why ❔ Save other people time figuring out the exact invocation. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [ ] Code has been formatted via `zk fmt` and `zk lint`. - [ ] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --- infrastructure/local-setup-preparation/README.md | 6 ++++++ infrastructure/local-setup-preparation/src/index.ts | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/infrastructure/local-setup-preparation/README.md b/infrastructure/local-setup-preparation/README.md index 6fbbb7b3f37c..fe59f930bf4c 100644 --- a/infrastructure/local-setup-preparation/README.md +++ b/infrastructure/local-setup-preparation/README.md @@ -2,3 +2,9 @@ This project contains scripts that should be executed when preparing the zkSync local setup used by outside developers, e.g. deposit ETH to some of the test accounts. + +With the server running (`zk server`), execute the following from `$ZKSYNC_HOME` to fund the L2 wallets + +``` +zk f bash -c 'cd infrastructure/local-setup-preparation ; yarn start' +``` diff --git a/infrastructure/local-setup-preparation/src/index.ts b/infrastructure/local-setup-preparation/src/index.ts index 585df599f82a..435cc26aa382 100644 --- a/infrastructure/local-setup-preparation/src/index.ts +++ b/infrastructure/local-setup-preparation/src/index.ts @@ -35,7 +35,7 @@ async function depositWithRichAccounts() { }; const balance = await wallet.getBalance(); - console.log(`Wallet balance is ${ethers.utils.formatEther(balance)} ETH`); + console.log(`Wallet ${wallet.address} balance is ${ethers.utils.formatEther(balance)} ETH`); // TODO: Currently we're providing zero as an operator fee, which works right now, // but will be changed in the future. From 53a6bcf8d4f51aa3363665fc93bdbd4dceb6041a Mon Sep 17 00:00:00 2001 From: Alex Ostrovski Date: Thu, 7 Dec 2023 10:50:04 +0200 Subject: [PATCH 062/268] chore: Enforce uniform import structure (#617) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ ...using `zk fmt` command by suppling relevant command-line args to rustfmt. These args work on stable Rust (at least for now) despite being unstable. ## Why ❔ More structured imports are easier to read. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --- core/bin/block_reverter/src/main.rs | 8 +- core/bin/contract-verifier/src/main.rs | 5 +- core/bin/contract-verifier/src/verifier.rs | 19 +-- .../bin/contract-verifier/src/zksolc_utils.rs | 6 +- .../contract-verifier/src/zkvyper_utils.rs | 6 +- core/bin/external_node/src/config/mod.rs | 8 +- core/bin/external_node/src/main.rs | 7 +- .../src/main.rs | 5 +- core/bin/rocksdb_util/src/main.rs | 4 +- .../src/consistency.rs | 1 - .../storage_logs_dedup_migration/src/main.rs | 1 - .../src/intrinsic_costs.rs | 8 +- .../system-constants-generator/src/main.rs | 21 +-- .../system-constants-generator/src/utils.rs | 25 ++-- .../src/json_to_binary_vk_converter.rs | 4 +- .../src/lib.rs | 44 +++--- .../src/main.rs | 15 +- .../src/tests.rs | 14 +- core/bin/verified_sources_fetcher/src/main.rs | 1 + core/bin/zksync_server/src/main.rs | 11 +- core/lib/basic_types/src/lib.rs | 31 +++-- core/lib/circuit_breaker/src/l1_txs.rs | 3 +- core/lib/circuit_breaker/src/lib.rs | 1 - core/lib/config/src/configs/api.rs | 6 +- core/lib/config/src/configs/chain.rs | 10 +- .../config/src/configs/contract_verifier.rs | 3 +- core/lib/config/src/configs/database.rs | 4 +- core/lib/config/src/configs/eth_sender.rs | 4 +- core/lib/config/src/configs/eth_watch.rs | 3 +- core/lib/config/src/configs/fetcher.rs | 3 +- .../src/configs/fri_proof_compressor.rs | 3 +- core/lib/config/src/configs/fri_prover.rs | 3 +- .../config/src/configs/fri_prover_gateway.rs | 3 +- .../config/src/configs/fri_prover_group.rs | 2 +- core/lib/config/src/configs/mod.rs | 31 +++-- .../config/src/configs/proof_data_handler.rs | 3 +- core/lib/config/src/configs/utils.rs | 4 +- core/lib/contracts/src/lib.rs | 11 +- core/lib/contracts/src/test_contracts.rs | 6 +- core/lib/crypto/src/hasher/blake2.rs | 2 +- core/lib/crypto/src/hasher/keccak.rs | 3 +- core/lib/crypto/src/hasher/sha256.rs | 2 +- .../src/basic_witness_input_producer_dal.rs | 12 +- core/lib/dal/src/blocks_dal.rs | 1 - core/lib/dal/src/blocks_web3_dal.rs | 16 ++- core/lib/dal/src/connection/holder.rs | 5 +- core/lib/dal/src/connection/mod.rs | 10 +- core/lib/dal/src/contract_verification_dal.rs | 14 +- core/lib/dal/src/eth_sender_dal.rs | 23 ++-- core/lib/dal/src/events_dal.rs | 9 +- core/lib/dal/src/events_web3_dal.rs | 1 - core/lib/dal/src/fri_gpu_prover_queue_dal.rs | 4 +- core/lib/dal/src/fri_proof_compressor_dal.rs | 20 +-- core/lib/dal/src/fri_protocol_versions_dal.rs | 3 +- .../fri_scheduler_dependency_tracker_dal.rs | 3 +- core/lib/dal/src/fri_witness_generator_dal.rs | 8 +- core/lib/dal/src/gpu_prover_queue_dal.rs | 7 +- core/lib/dal/src/healthcheck.rs | 1 - core/lib/dal/src/instrument.rs | 4 +- core/lib/dal/src/lib.rs | 60 +++----- core/lib/dal/src/metrics.rs | 4 +- core/lib/dal/src/models/storage_block.rs | 1 - core/lib/dal/src/models/storage_eth_tx.rs | 11 +- .../src/models/storage_protocol_version.rs | 4 +- .../dal/src/models/storage_prover_job_info.rs | 15 +- core/lib/dal/src/models/storage_sync.rs | 6 +- core/lib/dal/src/models/storage_token.rs | 1 - .../lib/dal/src/models/storage_transaction.rs | 31 +++-- .../models/storage_verification_request.rs | 10 +- .../src/models/storage_witness_job_info.rs | 14 +- core/lib/dal/src/proof_generation_dal.rs | 5 +- core/lib/dal/src/protocol_versions_dal.rs | 7 +- .../lib/dal/src/protocol_versions_web3_dal.rs | 3 +- core/lib/dal/src/prover_dal.rs | 3 +- core/lib/dal/src/storage_dal.rs | 6 +- core/lib/dal/src/storage_logs_dal.rs | 12 +- core/lib/dal/src/storage_logs_dedup_dal.rs | 6 +- core/lib/dal/src/tests/mod.rs | 20 +-- core/lib/dal/src/time_utils.rs | 4 +- core/lib/dal/src/tokens_dal.rs | 3 +- core/lib/dal/src/tokens_web3_dal.rs | 5 +- core/lib/dal/src/transactions_dal.rs | 7 +- core/lib/dal/src/transactions_web3_dal.rs | 18 +-- core/lib/dal/src/witness_generator_dal.rs | 21 ++- core/lib/env_config/src/alerts.rs | 3 +- core/lib/env_config/src/api.rs | 4 +- core/lib/env_config/src/chain.rs | 3 +- core/lib/env_config/src/contracts.rs | 3 +- core/lib/env_config/src/database.rs | 3 +- .../env_config/src/fri_proof_compressor.rs | 3 +- core/lib/env_config/src/test_utils.rs | 3 +- core/lib/env_config/src/utils.rs | 3 +- core/lib/eth_client/src/clients/http/mod.rs | 10 +- core/lib/eth_client/src/clients/http/query.rs | 14 +- .../eth_client/src/clients/http/signing.rs | 29 ++-- core/lib/eth_client/src/clients/mock.rs | 13 +- core/lib/eth_client/src/lib.rs | 9 +- core/lib/eth_signer/src/json_rpc_signer.rs | 25 ++-- core/lib/eth_signer/src/lib.rs | 9 +- core/lib/eth_signer/src/pk_signer.rs | 14 +- core/lib/eth_signer/src/raw_ethereum_tx.rs | 12 +- core/lib/health_check/src/lib.rs | 9 +- core/lib/mempool/src/mempool_store.rs | 3 +- core/lib/mempool/src/tests.rs | 22 +-- core/lib/mempool/src/types.rs | 8 +- .../lib/merkle_tree/examples/loadtest/main.rs | 13 +- core/lib/merkle_tree/examples/recovery.rs | 5 +- core/lib/merkle_tree/src/consistency.rs | 10 +- core/lib/merkle_tree/src/domain.rs | 14 +- core/lib/merkle_tree/src/errors.rs | 3 +- core/lib/merkle_tree/src/hasher/mod.rs | 13 +- core/lib/merkle_tree/src/hasher/nodes.rs | 3 +- core/lib/merkle_tree/src/lib.rs | 34 ++--- core/lib/merkle_tree/src/metrics.rs | 3 +- core/lib/merkle_tree/src/recovery.rs | 3 +- core/lib/merkle_tree/src/storage/mod.rs | 17 ++- core/lib/merkle_tree/src/storage/patch.rs | 4 +- core/lib/merkle_tree/src/storage/rocksdb.rs | 10 +- .../merkle_tree/src/storage/serialization.rs | 3 +- core/lib/merkle_tree/src/storage/tests.rs | 8 +- core/lib/merkle_tree/src/types/internal.rs | 3 +- core/lib/merkle_tree/src/types/mod.rs | 4 +- .../merkle_tree/tests/integration/common.rs | 3 +- .../tests/integration/consistency.rs | 2 +- .../merkle_tree/tests/integration/domain.rs | 5 +- .../tests/integration/merkle_tree.rs | 13 +- .../merkle_tree/tests/integration/recovery.rs | 3 +- core/lib/mini_merkle_tree/benches/tree.rs | 1 - core/lib/mini_merkle_tree/src/lib.rs | 4 +- core/lib/multivm/src/glue/tracers/mod.rs | 5 +- .../src/glue/types/vm/block_context_mode.rs | 3 +- .../src/glue/types/vm/vm_block_result.rs | 10 +- .../glue/types/vm/vm_tx_execution_result.rs | 7 +- .../traits/tracers/dyn_tracers/vm_1_3_3.rs | 6 +- .../traits/tracers/dyn_tracers/vm_1_4_0.rs | 6 +- core/lib/multivm/src/interface/traits/vm.rs | 24 ++-- .../src/interface/types/errors/halt.rs | 3 +- .../types/errors/tx_revert_reason.rs | 4 +- .../interface/types/inputs/l1_batch_env.rs | 2 +- .../types/outputs/execution_result.rs | 13 +- .../types/outputs/execution_state.rs | 6 +- .../src/interface/types/outputs/mod.rs | 15 +- core/lib/multivm/src/lib.rs | 23 ++-- .../multivm/src/tracers/call_tracer/mod.rs | 6 +- .../src/tracers/call_tracer/vm_latest/mod.rs | 19 +-- .../call_tracer/vm_refunds_enhancement/mod.rs | 21 +-- .../call_tracer/vm_virtual_blocks/mod.rs | 29 ++-- .../multivm/src/tracers/multivm_dispatcher.rs | 3 +- .../storage_invocation/vm_latest/mod.rs | 17 ++- .../vm_refunds_enhancement/mod.rs | 15 +- .../vm_virtual_blocks/mod.rs | 15 +- core/lib/multivm/src/tracers/validator/mod.rs | 15 +- .../multivm/src/tracers/validator/types.rs | 8 +- .../src/tracers/validator/vm_latest/mod.rs | 34 ++--- .../validator/vm_refunds_enhancement/mod.rs | 33 +++-- .../validator/vm_virtual_blocks/mod.rs | 30 ++-- .../vm_1_3_2/errors/vm_revert_reason.rs | 6 +- .../src/versions/vm_1_3_2/event_sink.rs | 10 +- .../src/versions/vm_1_3_2/history_recorder.rs | 6 +- .../multivm/src/versions/vm_1_3_2/memory.rs | 22 +-- core/lib/multivm/src/versions/vm_1_3_2/mod.rs | 31 +++-- .../src/versions/vm_1_3_2/oracle_tools.rs | 17 ++- .../versions/vm_1_3_2/oracles/decommitter.rs | 18 ++- .../src/versions/vm_1_3_2/oracles/mod.rs | 7 +- .../versions/vm_1_3_2/oracles/precompile.rs | 7 +- .../src/versions/vm_1_3_2/oracles/storage.rs | 19 ++- .../vm_1_3_2/oracles/tracer/bootloader.rs | 16 ++- .../versions/vm_1_3_2/oracles/tracer/call.rs | 36 ++--- .../versions/vm_1_3_2/oracles/tracer/mod.rs | 26 ++-- .../vm_1_3_2/oracles/tracer/one_tx.rs | 20 +-- .../oracles/tracer/transaction_result.rs | 18 +-- .../versions/vm_1_3_2/oracles/tracer/utils.rs | 21 +-- .../vm_1_3_2/oracles/tracer/validation.rs | 24 ++-- .../src/versions/vm_1_3_2/pubdata_utils.rs | 16 ++- .../multivm/src/versions/vm_1_3_2/refunds.rs | 11 +- .../src/versions/vm_1_3_2/test_utils.rs | 6 +- .../src/versions/vm_1_3_2/transaction_data.rs | 14 +- .../multivm/src/versions/vm_1_3_2/utils.rs | 13 +- core/lib/multivm/src/versions/vm_1_3_2/vm.rs | 33 +++-- .../src/versions/vm_1_3_2/vm_instance.rs | 81 ++++++----- .../versions/vm_1_3_2/vm_with_bootloader.rs | 6 +- .../vm_latest/bootloader_state/l2_block.rs | 12 +- .../vm_latest/bootloader_state/state.rs | 26 ++-- .../versions/vm_latest/bootloader_state/tx.rs | 3 +- .../vm_latest/bootloader_state/utils.rs | 27 ++-- .../src/versions/vm_latest/constants.rs | 8 +- .../vm_latest/implementation/bytecode.rs | 11 +- .../vm_latest/implementation/execution.rs | 22 +-- .../versions/vm_latest/implementation/gas.rs | 7 +- .../versions/vm_latest/implementation/logs.rs | 20 +-- .../vm_latest/implementation/snapshots.rs | 3 +- .../vm_latest/implementation/statistics.rs | 10 +- .../versions/vm_latest/implementation/tx.rs | 17 ++- .../lib/multivm/src/versions/vm_latest/mod.rs | 41 +++--- .../versions/vm_latest/old_vm/event_sink.rs | 12 +- .../vm_latest/old_vm/history_recorder.rs | 8 +- .../src/versions/vm_latest/old_vm/memory.rs | 24 ++-- .../vm_latest/old_vm/oracles/decommitter.rs | 22 ++- .../vm_latest/old_vm/oracles/precompile.rs | 7 +- .../src/versions/vm_latest/old_vm/utils.rs | 19 ++- .../src/versions/vm_latest/oracles/storage.rs | 28 ++-- .../versions/vm_latest/tests/bootloader.rs | 18 +-- .../vm_latest/tests/bytecode_publishing.rs | 14 +- .../versions/vm_latest/tests/call_tracer.rs | 22 ++- .../versions/vm_latest/tests/default_aa.rs | 22 ++- .../src/versions/vm_latest/tests/gas_limit.rs | 16 +-- .../vm_latest/tests/get_used_contracts.rs | 22 +-- .../vm_latest/tests/is_write_initial.rs | 14 +- .../vm_latest/tests/l1_tx_execution.rs | 25 ++-- .../src/versions/vm_latest/tests/l2_blocks.rs | 28 ++-- .../versions/vm_latest/tests/nonce_holder.rs | 21 ++- .../src/versions/vm_latest/tests/refunds.rs | 17 ++- .../vm_latest/tests/require_eip712.rs | 26 ++-- .../src/versions/vm_latest/tests/rollbacks.rs | 29 ++-- .../vm_latest/tests/simple_execution.rs | 10 +- .../vm_latest/tests/tester/inner_state.rs | 18 ++- .../tests/tester/transaction_test_info.rs | 12 +- .../vm_latest/tests/tester/vm_tester.rs | 41 +++--- .../tests/tracing_execution_error.rs | 15 +- .../src/versions/vm_latest/tests/upgrade.rs | 36 ++--- .../src/versions/vm_latest/tests/utils.rs | 15 +- .../vm_latest/tracers/default_tracers.rs | 47 ++++--- .../versions/vm_latest/tracers/dispatcher.rs | 15 +- .../vm_latest/tracers/pubdata_tracer.rs | 34 ++--- .../src/versions/vm_latest/tracers/refunds.rs | 44 +++--- .../vm_latest/tracers/result_tracer.rs | 30 ++-- .../src/versions/vm_latest/tracers/traits.rs | 17 ++- .../src/versions/vm_latest/tracers/utils.rs | 22 +-- .../vm_latest/types/internals/pubdata.rs | 2 +- .../types/internals/transaction_data.rs | 21 +-- .../vm_latest/types/internals/vm_state.rs | 42 +++--- .../src/versions/vm_latest/types/l1_batch.rs | 3 +- .../src/versions/vm_latest/utils/l2_blocks.rs | 10 +- .../src/versions/vm_latest/utils/logs.rs | 7 +- .../src/versions/vm_latest/utils/overhead.rs | 10 +- .../vm_latest/utils/transaction_encoding.rs | 3 +- core/lib/multivm/src/versions/vm_latest/vm.rs | 32 +++-- .../versions/vm_m5/errors/vm_revert_reason.rs | 6 +- .../multivm/src/versions/vm_m5/event_sink.rs | 7 +- .../src/versions/vm_m5/history_recorder.rs | 5 +- core/lib/multivm/src/versions/vm_m5/memory.rs | 18 ++- core/lib/multivm/src/versions/vm_m5/mod.rs | 27 ++-- .../src/versions/vm_m5/oracle_tools.rs | 19 +-- .../src/versions/vm_m5/oracles/decommitter.rs | 19 ++- .../multivm/src/versions/vm_m5/oracles/mod.rs | 7 +- .../src/versions/vm_m5/oracles/precompile.rs | 7 +- .../src/versions/vm_m5/oracles/storage.rs | 29 ++-- .../src/versions/vm_m5/oracles/tracer.rs | 23 ++-- .../src/versions/vm_m5/pubdata_utils.rs | 23 ++-- .../lib/multivm/src/versions/vm_m5/refunds.rs | 12 +- .../lib/multivm/src/versions/vm_m5/storage.rs | 5 +- .../multivm/src/versions/vm_m5/test_utils.rs | 11 +- .../src/versions/vm_m5/transaction_data.rs | 15 +- core/lib/multivm/src/versions/vm_m5/utils.rs | 10 +- core/lib/multivm/src/versions/vm_m5/vm.rs | 36 ++--- .../multivm/src/versions/vm_m5/vm_instance.rs | 83 ++++++----- .../src/versions/vm_m5/vm_with_bootloader.rs | 6 +- .../versions/vm_m6/errors/vm_revert_reason.rs | 6 +- .../multivm/src/versions/vm_m6/event_sink.rs | 12 +- .../src/versions/vm_m6/history_recorder.rs | 5 +- core/lib/multivm/src/versions/vm_m6/memory.rs | 22 +-- .../src/versions/vm_m6/oracle_tools.rs | 20 +-- .../src/versions/vm_m6/oracles/decommitter.rs | 18 +-- .../multivm/src/versions/vm_m6/oracles/mod.rs | 7 +- .../src/versions/vm_m6/oracles/precompile.rs | 7 +- .../src/versions/vm_m6/oracles/storage.rs | 28 ++-- .../vm_m6/oracles/tracer/bootloader.rs | 16 ++- .../src/versions/vm_m6/oracles/tracer/call.rs | 42 +++--- .../src/versions/vm_m6/oracles/tracer/mod.rs | 26 ++-- .../versions/vm_m6/oracles/tracer/one_tx.rs | 20 +-- .../oracles/tracer/transaction_result.rs | 18 +-- .../versions/vm_m6/oracles/tracer/utils.rs | 21 +-- .../vm_m6/oracles/tracer/validation.rs | 31 ++--- .../src/versions/vm_m6/pubdata_utils.rs | 23 ++-- .../lib/multivm/src/versions/vm_m6/refunds.rs | 13 +- .../lib/multivm/src/versions/vm_m6/storage.rs | 5 +- .../multivm/src/versions/vm_m6/test_utils.rs | 9 +- .../src/versions/vm_m6/transaction_data.rs | 17 ++- core/lib/multivm/src/versions/vm_m6/utils.rs | 18 +-- core/lib/multivm/src/versions/vm_m6/vm.rs | 33 ++--- .../multivm/src/versions/vm_m6/vm_instance.rs | 87 ++++++------ .../src/versions/vm_m6/vm_with_bootloader.rs | 3 +- .../bootloader_state/l2_block.rs | 12 +- .../bootloader_state/state.rs | 23 ++-- .../bootloader_state/tx.rs | 3 +- .../bootloader_state/utils.rs | 23 ++-- .../vm_refunds_enhancement/constants.rs | 8 +- .../implementation/bytecode.rs | 11 +- .../implementation/execution.rs | 21 +-- .../implementation/gas.rs | 7 +- .../implementation/logs.rs | 20 +-- .../implementation/snapshots.rs | 11 +- .../implementation/statistics.rs | 10 +- .../implementation/tx.rs | 19 +-- .../versions/vm_refunds_enhancement/mod.rs | 37 +++-- .../old_vm/event_sink.rs | 10 +- .../old_vm/history_recorder.rs | 8 +- .../vm_refunds_enhancement/old_vm/memory.rs | 24 ++-- .../old_vm/oracles/decommitter.rs | 22 ++- .../old_vm/oracles/precompile.rs | 7 +- .../vm_refunds_enhancement/old_vm/utils.rs | 19 ++- .../vm_refunds_enhancement/oracles/storage.rs | 25 ++-- .../tracers/default_tracers.rs | 38 ++--- .../tracers/dispatcher.rs | 15 +- .../vm_refunds_enhancement/tracers/refunds.rs | 37 +++-- .../tracers/result_tracer.rs | 32 +++-- .../vm_refunds_enhancement/tracers/traits.rs | 17 ++- .../vm_refunds_enhancement/tracers/utils.rs | 17 +-- .../types/internals/transaction_data.rs | 21 +-- .../types/internals/vm_state.rs | 42 +++--- .../vm_refunds_enhancement/types/l1_batch.rs | 3 +- .../vm_refunds_enhancement/utils/l2_blocks.rs | 10 +- .../vm_refunds_enhancement/utils/overhead.rs | 10 +- .../utils/transaction_encoding.rs | 3 +- .../src/versions/vm_refunds_enhancement/vm.rs | 28 ++-- .../bootloader_state/l2_block.rs | 12 +- .../bootloader_state/state.rs | 25 ++-- .../vm_virtual_blocks/bootloader_state/tx.rs | 3 +- .../bootloader_state/utils.rs | 23 ++-- .../versions/vm_virtual_blocks/constants.rs | 8 +- .../implementation/bytecode.rs | 11 +- .../implementation/execution.rs | 24 ++-- .../vm_virtual_blocks/implementation/gas.rs | 7 +- .../vm_virtual_blocks/implementation/logs.rs | 20 +-- .../implementation/snapshots.rs | 9 +- .../implementation/statistics.rs | 10 +- .../vm_virtual_blocks/implementation/tx.rs | 19 +-- .../src/versions/vm_virtual_blocks/mod.rs | 38 +++-- .../vm_virtual_blocks/old_vm/event_sink.rs | 10 +- .../old_vm/history_recorder.rs | 8 +- .../vm_virtual_blocks/old_vm/memory.rs | 24 ++-- .../old_vm/oracles/decommitter.rs | 22 ++- .../old_vm/oracles/precompile.rs | 7 +- .../old_vm/oracles/storage.rs | 20 ++- .../vm_virtual_blocks/old_vm/utils.rs | 19 ++- .../tracers/default_tracers.rs | 42 +++--- .../vm_virtual_blocks/tracers/dispatcher.rs | 19 +-- .../vm_virtual_blocks/tracers/refunds.rs | 40 +++--- .../tracers/result_tracer.rs | 38 ++--- .../vm_virtual_blocks/tracers/traits.rs | 17 ++- .../vm_virtual_blocks/tracers/utils.rs | 22 +-- .../types/internals/transaction_data.rs | 21 +-- .../types/internals/vm_state.rs | 42 +++--- .../vm_virtual_blocks/types/l1_batch_env.rs | 3 +- .../vm_virtual_blocks/utils/l2_blocks.rs | 10 +- .../vm_virtual_blocks/utils/overhead.rs | 10 +- .../utils/transaction_encoding.rs | 3 +- .../src/versions/vm_virtual_blocks/vm.rs | 31 +++-- core/lib/multivm/src/vm_instance.rs | 17 +-- core/lib/object_store/src/file.rs | 4 +- core/lib/object_store/src/gcs.rs | 18 +-- core/lib/object_store/src/metrics.rs | 4 +- core/lib/object_store/src/mock.rs | 4 +- core/lib/object_store/src/objects.rs | 8 +- core/lib/object_store/src/raw.rs | 6 +- core/lib/object_store/tests/integration.rs | 1 - core/lib/prometheus_exporter/src/lib.rs | 4 +- .../lib/prover_utils/src/gcs_proof_fetcher.rs | 3 +- core/lib/prover_utils/src/region_fetcher.rs | 7 +- .../prover_utils/src/vk_commitment_helper.rs | 3 +- core/lib/queued_job_processor/src/lib.rs | 14 +- core/lib/state/src/cache/metrics.rs | 4 +- core/lib/state/src/in_memory.rs | 3 +- core/lib/state/src/postgres/metrics.rs | 4 +- core/lib/state/src/postgres/mod.rs | 11 +- core/lib/state/src/postgres/tests.rs | 7 +- core/lib/state/src/rocksdb/metrics.rs | 4 +- core/lib/state/src/rocksdb/mod.rs | 10 +- core/lib/state/src/shadow_storage.rs | 2 +- core/lib/state/src/storage_view.rs | 10 +- core/lib/state/src/test_utils.rs | 4 +- core/lib/state/src/witness.rs | 3 +- core/lib/storage/src/db.rs | 10 +- core/lib/storage/src/metrics.rs | 6 +- core/lib/test_account/src/lib.rs | 18 +-- core/lib/types/src/aggregated_operations.rs | 12 +- core/lib/types/src/api/mod.rs | 11 +- core/lib/types/src/block.rs | 5 +- core/lib/types/src/circuit.rs | 3 +- core/lib/types/src/commitment.rs | 18 +-- .../types/src/contract_verification_api.rs | 3 +- core/lib/types/src/eth_sender.rs | 3 +- core/lib/types/src/event.rs | 15 +- core/lib/types/src/l1/mod.rs | 5 +- core/lib/types/src/l2/mod.rs | 28 ++-- core/lib/types/src/l2_to_l1_log.rs | 7 +- core/lib/types/src/lib.rs | 11 +- .../lib/types/src/priority_op_onchain_data.rs | 4 +- core/lib/types/src/proofs.rs | 28 ++-- core/lib/types/src/protocol_version.rs | 12 +- core/lib/types/src/prover_server_api/mod.rs | 9 +- core/lib/types/src/storage/log.rs | 3 +- .../types/src/storage/witness_block_state.rs | 6 +- .../types/src/storage/writes/compression.rs | 3 +- core/lib/types/src/storage/writes/mod.rs | 11 +- .../types/src/storage_writes_deduplicator.rs | 11 +- core/lib/types/src/system_contracts.rs | 2 +- core/lib/types/src/transaction_request.rs | 13 +- core/lib/types/src/tx/execute.rs | 3 +- core/lib/types/src/tx/mod.rs | 11 +- .../eip712_signature/member_types.rs | 9 +- .../eip712_signature/struct_builder.rs | 3 +- .../tx/primitives/eip712_signature/tests.rs | 21 ++- .../eip712_signature/typed_structure.rs | 8 +- .../tx/primitives/eip712_signature/utils.rs | 3 +- .../src/tx/primitives/packed_eth_signature.rs | 7 +- core/lib/types/src/tx/tx_execution_info.rs | 7 +- core/lib/types/src/utils.rs | 13 +- core/lib/types/src/vk_transform.rs | 4 +- core/lib/types/src/vm_trace.rs | 12 +- core/lib/utils/src/bytecode.rs | 10 +- core/lib/utils/src/convert.rs | 9 +- core/lib/utils/src/http_with_retries.rs | 3 +- core/lib/utils/src/misc.rs | 3 +- core/lib/vlog/src/lib.rs | 12 +- core/lib/web3_decl/src/namespaces/debug.rs | 8 +- core/lib/web3_decl/src/namespaces/eth.rs | 16 +-- core/lib/web3_decl/src/namespaces/mod.rs | 13 +- core/lib/web3_decl/src/namespaces/zks.rs | 1 - core/lib/web3_decl/src/types.rs | 10 +- .../contract_verification/api_decl.rs | 1 - .../contract_verification/api_impl.rs | 1 - .../contract_verification/metrics.rs | 4 +- .../api_server/contract_verification/mod.rs | 10 +- .../src/api_server/execution_sandbox/apply.rs | 10 +- .../src/api_server/execution_sandbox/error.rs | 3 +- .../api_server/execution_sandbox/execute.rs | 12 +- .../src/api_server/execution_sandbox/mod.rs | 23 ++-- .../api_server/execution_sandbox/tracers.rs | 8 +- .../api_server/execution_sandbox/validate.rs | 14 +- .../execution_sandbox/vm_metrics.rs | 11 +- .../zksync_core/src/api_server/healthcheck.rs | 5 +- .../src/api_server/tree/metrics.rs | 4 +- .../zksync_core/src/api_server/tree/mod.rs | 11 +- .../zksync_core/src/api_server/tree/tests.rs | 3 +- .../src/api_server/tx_sender/mod.rs | 48 +++---- .../src/api_server/tx_sender/proxy.rs | 2 +- .../src/api_server/tx_sender/result.rs | 11 +- .../batch_limiter_middleware.rs | 4 +- .../api_server/web3/backend_jsonrpc/error.rs | 4 +- .../web3/backend_jsonrpc/namespaces/debug.rs | 8 +- .../web3/backend_jsonrpc/namespaces/en.rs | 6 - .../web3/backend_jsonrpc/namespaces/eth.rs | 12 +- .../web3/backend_jsonrpc/namespaces/net.rs | 6 - .../web3/backend_jsonrpc/namespaces/zks.rs | 11 +- .../web3/backend_jsonrpc/pub_sub.rs | 4 +- .../api_server/web3/backend_jsonrpsee/mod.rs | 7 +- .../web3/backend_jsonrpsee/namespaces/zks.rs | 3 +- .../src/api_server/web3/metrics.rs | 12 +- .../zksync_core/src/api_server/web3/mod.rs | 46 ++++--- .../src/api_server/web3/namespaces/debug.rs | 30 ++-- .../src/api_server/web3/namespaces/en.rs | 2 +- .../src/api_server/web3/namespaces/zks.rs | 11 +- .../zksync_core/src/api_server/web3/pubsub.rs | 5 +- .../zksync_core/src/api_server/web3/state.rs | 5 +- .../src/api_server/web3/tests/mod.rs | 9 +- .../src/api_server/web3/tests/ws.rs | 1 - .../src/basic_witness_input_producer/mod.rs | 22 ++- .../vm_interactions.rs | 13 +- .../lib/zksync_core/src/block_reverter/mod.rs | 27 ++-- core/lib/zksync_core/src/consensus/payload.rs | 4 +- core/lib/zksync_core/src/data_fetchers/mod.rs | 3 +- .../src/data_fetchers/token_list/mock.rs | 6 +- .../src/data_fetchers/token_list/mod.rs | 4 +- .../src/data_fetchers/token_list/one_inch.rs | 4 +- .../data_fetchers/token_price/coingecko.rs | 4 +- .../src/data_fetchers/token_price/mock.rs | 3 +- .../src/data_fetchers/token_price/mod.rs | 7 +- .../src/eth_sender/eth_tx_aggregator.rs | 15 +- .../src/eth_sender/eth_tx_manager.rs | 9 +- .../lib/zksync_core/src/eth_sender/metrics.rs | 3 +- .../src/eth_sender/publish_criterion.rs | 5 +- core/lib/zksync_core/src/eth_sender/tests.rs | 11 +- .../event_processors/governance_upgrades.rs | 13 +- .../src/eth_watch/event_processors/mod.rs | 3 +- .../eth_watch/event_processors/upgrades.rs | 1 + core/lib/zksync_core/src/eth_watch/metrics.rs | 4 +- core/lib/zksync_core/src/eth_watch/mod.rs | 15 +- core/lib/zksync_core/src/eth_watch/tests.rs | 10 +- core/lib/zksync_core/src/genesis.rs | 10 +- .../src/house_keeper/blocks_state_reporter.rs | 1 - .../fri_proof_compressor_queue_monitor.rs | 3 +- .../fri_scheduler_circuit_queuer.rs | 1 - .../fri_witness_generator_queue_monitor.rs | 3 +- .../house_keeper/gpu_prover_queue_monitor.rs | 1 - .../house_keeper/prover_job_retry_manager.rs | 1 - .../src/house_keeper/prover_queue_monitor.rs | 4 +- ...waiting_to_queued_fri_witness_job_mover.rs | 1 - .../waiting_to_queued_witness_job_mover.rs | 1 - .../witness_generator_queue_monitor.rs | 3 +- .../src/l1_gas_price/gas_adjuster/mod.rs | 9 +- .../src/l1_gas_price/gas_adjuster/tests.rs | 7 +- .../src/l1_gas_price/main_node_fetcher.rs | 1 - core/lib/zksync_core/src/l1_gas_price/mod.rs | 3 +- .../zksync_core/src/l1_gas_price/singleton.rs | 12 +- core/lib/zksync_core/src/lib.rs | 89 ++++++------ .../src/metadata_calculator/helpers.rs | 8 +- .../src/metadata_calculator/metrics.rs | 5 +- .../src/metadata_calculator/mod.rs | 15 +- .../src/metadata_calculator/tests.rs | 5 +- .../src/metadata_calculator/updater.rs | 5 +- core/lib/zksync_core/src/metrics.rs | 3 +- .../zksync_core/src/proof_data_handler/mod.rs | 9 +- .../proof_data_handler/request_processor.rs | 23 ++-- .../lib/zksync_core/src/reorg_detector/mod.rs | 6 +- .../src/state_keeper/batch_executor/mod.rs | 22 ++- .../state_keeper/batch_executor/tests/mod.rs | 6 +- .../batch_executor/tests/tester.rs | 18 +-- .../src/state_keeper/extractors.rs | 3 +- .../zksync_core/src/state_keeper/io/common.rs | 6 +- .../src/state_keeper/io/mempool.rs | 11 +- .../zksync_core/src/state_keeper/io/mod.rs | 13 +- .../src/state_keeper/io/seal_logic.rs | 15 +- .../src/state_keeper/io/tests/mod.rs | 12 +- .../src/state_keeper/io/tests/tester.rs | 7 +- .../zksync_core/src/state_keeper/keeper.rs | 11 +- .../src/state_keeper/mempool_actor.rs | 3 +- .../zksync_core/src/state_keeper/metrics.rs | 9 +- core/lib/zksync_core/src/state_keeper/mod.rs | 28 ++-- .../criteria/geometry_seal_criteria.rs | 3 +- .../zksync_core/src/state_keeper/tests/mod.rs | 35 ++--- .../src/state_keeper/tests/tester.rs | 15 +- .../state_keeper/updates/l1_batch_updates.rs | 13 +- .../state_keeper/updates/miniblock_updates.rs | 20 +-- .../src/state_keeper/updates/mod.rs | 13 +- .../src/sync_layer/batch_status_updater.rs | 5 +- core/lib/zksync_core/src/sync_layer/client.rs | 5 +- .../zksync_core/src/sync_layer/external_io.rs | 5 +- .../lib/zksync_core/src/sync_layer/fetcher.rs | 5 +- .../lib/zksync_core/src/sync_layer/genesis.rs | 1 - .../src/sync_layer/gossip/buffered/mod.rs | 9 +- .../src/sync_layer/gossip/buffered/tests.rs | 5 +- .../src/sync_layer/gossip/conversions.rs | 2 +- .../src/sync_layer/gossip/metrics.rs | 4 +- .../zksync_core/src/sync_layer/gossip/mod.rs | 11 +- .../src/sync_layer/gossip/storage/mod.rs | 14 +- .../src/sync_layer/gossip/storage/tests.rs | 1 - .../src/sync_layer/gossip/tests.rs | 8 +- .../lib/zksync_core/src/sync_layer/metrics.rs | 4 +- core/lib/zksync_core/src/sync_layer/tests.rs | 5 +- .../src/witness_generator/basic_circuits.rs | 15 +- .../src/witness_generator/leaf_aggregation.rs | 13 +- .../zksync_core/src/witness_generator/mod.rs | 3 +- .../src/witness_generator/node_aggregation.rs | 6 +- .../precalculated_merkle_paths_provider.rs | 14 +- .../src/witness_generator/scheduler.rs | 3 +- .../src/witness_generator/storage_oracle.rs | 6 +- .../src/witness_generator/tests.rs | 10 +- .../src/witness_generator/utils.rs | 12 +- .../src/checker.rs | 9 +- .../src/config.rs | 3 +- .../src/divergence.rs | 1 + .../src/helpers.rs | 7 +- .../cross_external_nodes_checker/src/main.rs | 13 +- .../src/pubsub_checker.rs | 14 +- .../src/account/api_request_executor.rs | 1 - core/tests/loadnext/src/account/mod.rs | 9 +- .../loadnext/src/account/pubsub_executor.rs | 6 +- .../src/account/tx_command_executor.rs | 10 +- core/tests/loadnext/src/account_pool.rs | 1 - core/tests/loadnext/src/command/api.rs | 1 - core/tests/loadnext/src/command/tx_command.rs | 1 - core/tests/loadnext/src/config.rs | 9 +- core/tests/loadnext/src/corrupted_tx.rs | 20 +-- core/tests/loadnext/src/executor.rs | 21 +-- core/tests/loadnext/src/fs_utils.rs | 11 +- core/tests/loadnext/src/main.rs | 8 +- core/tests/loadnext/src/report.rs | 2 +- .../loadnext/src/report_collector/mod.rs | 4 +- .../operation_results_collector.rs | 4 +- core/tests/loadnext/src/rng.rs | 1 - core/tests/loadnext/src/utils.rs | 1 + .../vm-benchmark/benches/diy_benchmark.rs | 3 +- core/tests/vm-benchmark/harness/src/lib.rs | 14 +- .../vm-benchmark/src/compare_iai_results.rs | 5 +- core/tests/vm-benchmark/src/find_slowest.rs | 1 + .../src/iai_results_to_prometheus.rs | 1 + .../tests/vm-benchmark/src/with_prometheus.rs | 3 +- infrastructure/zk/src/fmt.ts | 7 +- prover/Cargo.lock | 130 +++--------------- .../src/circuit_synthesizer.rs | 42 +++--- prover/circuit_synthesizer/src/main.rs | 3 +- prover/circuit_synthesizer/src/metrics.rs | 1 + prover/proof_fri_compressor/src/compressor.rs | 39 +++--- prover/proof_fri_compressor/src/main.rs | 10 +- prover/proof_fri_compressor/src/metrics.rs | 1 + prover/prover/src/artifact_provider.rs | 8 +- prover/prover/src/metrics.rs | 1 + prover/prover/src/prover.rs | 14 +- prover/prover/src/prover_params.rs | 1 - prover/prover/src/run.rs | 18 ++- prover/prover/src/socket_listener.rs | 15 +- .../src/synthesized_circuit_provider.rs | 14 +- .../src/gpu_prover_job_processor.rs | 51 +++---- prover/prover_fri/src/main.rs | 33 +++-- prover/prover_fri/src/metrics.rs | 1 + prover/prover_fri/src/prover_job_processor.rs | 46 +++---- prover/prover_fri/src/socket_listener.rs | 27 ++-- prover/prover_fri/src/utils.rs | 36 ++--- prover/prover_fri/tests/basic_test.rs | 11 +- .../src/api_data_fetcher.rs | 6 +- prover/prover_fri_gateway/src/main.rs | 8 +- .../src/proof_gen_data_fetcher.rs | 1 - .../prover_fri_gateway/src/proof_submitter.rs | 7 +- prover/prover_fri_types/src/lib.rs | 32 ++--- prover/prover_fri_utils/src/lib.rs | 17 ++- prover/prover_fri_utils/src/metrics.rs | 1 + prover/prover_fri_utils/src/socket_utils.rs | 12 +- .../setup_key_generator_and_server/src/lib.rs | 26 ++-- .../src/main.rs | 11 +- .../src/commitment_utils.rs | 19 ++- .../src/lib.rs | 93 +++++++------ .../src/main.rs | 20 +-- .../src/setup_data_generator.rs | 40 +++--- .../src/tests.rs | 12 +- .../src/utils.rs | 113 ++++++++------- .../src/vk_generator.rs | 27 ++-- .../witness_generator/src/basic_circuits.rs | 65 +++++---- .../witness_generator/src/leaf_aggregation.rs | 56 ++++---- prover/witness_generator/src/main.rs | 26 ++-- prover/witness_generator/src/metrics.rs | 1 + .../witness_generator/src/node_aggregation.rs | 48 ++++--- .../precalculated_merkle_paths_provider.rs | 11 +- prover/witness_generator/src/scheduler.rs | 48 ++++--- .../witness_generator/src/storage_oracle.rs | 6 +- prover/witness_generator/src/tests.rs | 12 +- prover/witness_generator/src/utils.rs | 36 ++--- prover/witness_generator/tests/basic_test.rs | 22 +-- .../witness_vector_generator/src/generator.rs | 27 ++-- prover/witness_vector_generator/src/main.rs | 10 +- .../witness_vector_generator/src/metrics.rs | 1 + .../tests/basic_test.rs | 4 +- sdk/zksync-rs/src/ethereum/mod.rs | 24 ++-- sdk/zksync-rs/src/lib.rs | 21 ++- sdk/zksync-rs/src/operations/mod.rs | 4 +- sdk/zksync-rs/src/operations/transfer.rs | 15 +- sdk/zksync-rs/src/operations/withdraw.rs | 6 +- sdk/zksync-rs/src/signer.rs | 11 +- sdk/zksync-rs/src/utils.rs | 1 - sdk/zksync-rs/src/wallet.rs | 5 +- 640 files changed, 4414 insertions(+), 4176 deletions(-) diff --git a/core/bin/block_reverter/src/main.rs b/core/bin/block_reverter/src/main.rs index bc49b731d149..c1b02a1a1201 100644 --- a/core/bin/block_reverter/src/main.rs +++ b/core/bin/block_reverter/src/main.rs @@ -1,15 +1,13 @@ use anyhow::Context as _; use clap::{Parser, Subcommand}; use tokio::io::{self, AsyncReadExt}; - use zksync_config::{ContractsConfig, DBConfig, ETHClientConfig, ETHSenderConfig, PostgresConfig}; -use zksync_dal::ConnectionPool; -use zksync_env_config::FromEnv; -use zksync_types::{L1BatchNumber, U256}; - use zksync_core::block_reverter::{ BlockReverter, BlockReverterEthConfig, BlockReverterFlags, L1ExecutedBatchesRevert, }; +use zksync_dal::ConnectionPool; +use zksync_env_config::FromEnv; +use zksync_types::{L1BatchNumber, U256}; #[derive(Debug, Parser)] #[command(author = "Matter Labs", version, about = "Block revert utility", long_about = None)] diff --git a/core/bin/contract-verifier/src/main.rs b/core/bin/contract-verifier/src/main.rs index 05ee51139dd3..33090697c510 100644 --- a/core/bin/contract-verifier/src/main.rs +++ b/core/bin/contract-verifier/src/main.rs @@ -1,16 +1,15 @@ use std::cell::RefCell; use anyhow::Context as _; +use futures::{channel::mpsc, executor::block_on, SinkExt, StreamExt}; use prometheus_exporter::PrometheusExporterConfig; +use tokio::sync::watch; use zksync_config::{configs::PrometheusConfig, ApiConfig, ContractVerifierConfig, PostgresConfig}; use zksync_dal::ConnectionPool; use zksync_env_config::FromEnv; use zksync_queued_job_processor::JobProcessor; use zksync_utils::wait_for_tasks::wait_for_tasks; -use futures::{channel::mpsc, executor::block_on, SinkExt, StreamExt}; -use tokio::sync::watch; - use crate::verifier::ContractVerifier; pub mod error; diff --git a/core/bin/contract-verifier/src/verifier.rs b/core/bin/contract-verifier/src/verifier.rs index e34b4784c1cb..63c46ed90f73 100644 --- a/core/bin/contract-verifier/src/verifier.rs +++ b/core/bin/contract-verifier/src/verifier.rs @@ -1,7 +1,9 @@ -use std::collections::HashMap; -use std::env; -use std::path::Path; -use std::time::{Duration, Instant}; +use std::{ + collections::HashMap, + env, + path::Path, + time::{Duration, Instant}, +}; use anyhow::Context as _; use chrono::Utc; @@ -9,7 +11,6 @@ use ethabi::{Contract, Token}; use lazy_static::lazy_static; use regex::Regex; use tokio::time; - use zksync_config::ContractVerifierConfig; use zksync_dal::{ConnectionPool, StorageProcessor}; use zksync_env_config::FromEnv; @@ -22,11 +23,11 @@ use zksync_types::{ Address, }; -use crate::error::ContractVerifierError; -use crate::zksolc_utils::{ - Optimizer, Settings, Source, StandardJson, ZkSolc, ZkSolcInput, ZkSolcOutput, +use crate::{ + error::ContractVerifierError, + zksolc_utils::{Optimizer, Settings, Source, StandardJson, ZkSolc, ZkSolcInput, ZkSolcOutput}, + zkvyper_utils::{ZkVyper, ZkVyperInput}, }; -use crate::zkvyper_utils::{ZkVyper, ZkVyperInput}; lazy_static! { static ref DEPLOYER_CONTRACT: Contract = zksync_contracts::deployer_contract(); diff --git a/core/bin/contract-verifier/src/zksolc_utils.rs b/core/bin/contract-verifier/src/zksolc_utils.rs index 4fba999453c4..560bacb809f9 100644 --- a/core/bin/contract-verifier/src/zksolc_utils.rs +++ b/core/bin/contract-verifier/src/zksolc_utils.rs @@ -1,8 +1,6 @@ +use std::{collections::HashMap, io::Write, path::PathBuf, process::Stdio}; + use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::io::Write; -use std::path::PathBuf; -use std::process::Stdio; use crate::error::ContractVerifierError; diff --git a/core/bin/contract-verifier/src/zkvyper_utils.rs b/core/bin/contract-verifier/src/zkvyper_utils.rs index 33a99f256f90..c597f78d4588 100644 --- a/core/bin/contract-verifier/src/zkvyper_utils.rs +++ b/core/bin/contract-verifier/src/zkvyper_utils.rs @@ -1,8 +1,4 @@ -use std::collections::HashMap; -use std::fs::File; -use std::io::Write; -use std::path::PathBuf; -use std::process::Stdio; +use std::{collections::HashMap, fs::File, io::Write, path::PathBuf, process::Stdio}; use crate::error::ContractVerifierError; diff --git a/core/bin/external_node/src/config/mod.rs b/core/bin/external_node/src/config/mod.rs index c116201b91db..aea48bc0aeb7 100644 --- a/core/bin/external_node/src/config/mod.rs +++ b/core/bin/external_node/src/config/mod.rs @@ -1,14 +1,14 @@ +use std::{env, time::Duration}; + use anyhow::Context; use serde::Deserialize; -use std::{env, time::Duration}; use url::Url; - use zksync_basic_types::{Address, L1ChainId, L2ChainId, MiniblockNumber}; use zksync_core::api_server::{ - tx_sender::TxSenderConfig, web3::state::InternalApiConfig, web3::Namespace, + tx_sender::TxSenderConfig, + web3::{state::InternalApiConfig, Namespace}, }; use zksync_types::api::BridgeAddresses; - use zksync_web3_decl::{ jsonrpsee::http_client::{HttpClient, HttpClientBuilder}, namespaces::{EnNamespaceClient, EthNamespaceClient, ZksNamespaceClient}, diff --git a/core/bin/external_node/src/main.rs b/core/bin/external_node/src/main.rs index 6324b0599a6a..da28329c18f4 100644 --- a/core/bin/external_node/src/main.rs +++ b/core/bin/external_node/src/main.rs @@ -1,11 +1,10 @@ -use anyhow::Context; -use clap::Parser; -use tokio::{sync::watch, task, time::sleep}; - use std::{sync::Arc, time::Duration}; +use anyhow::Context; +use clap::Parser; use futures::{future::FusedFuture, FutureExt}; use prometheus_exporter::PrometheusExporterConfig; +use tokio::{sync::watch, task, time::sleep}; use zksync_basic_types::{Address, L2ChainId}; use zksync_core::{ api_server::{ diff --git a/core/bin/merkle_tree_consistency_checker/src/main.rs b/core/bin/merkle_tree_consistency_checker/src/main.rs index b132bda87fa0..8cac3d997243 100644 --- a/core/bin/merkle_tree_consistency_checker/src/main.rs +++ b/core/bin/merkle_tree_consistency_checker/src/main.rs @@ -1,8 +1,7 @@ -use anyhow::Context as _; -use clap::Parser; - use std::{path::Path, time::Instant}; +use anyhow::Context as _; +use clap::Parser; use zksync_config::DBConfig; use zksync_env_config::FromEnv; use zksync_merkle_tree::domain::ZkSyncTree; diff --git a/core/bin/rocksdb_util/src/main.rs b/core/bin/rocksdb_util/src/main.rs index 30d3d42e771c..1fd60ca67c72 100644 --- a/core/bin/rocksdb_util/src/main.rs +++ b/core/bin/rocksdb_util/src/main.rs @@ -1,6 +1,5 @@ use anyhow::Context as _; use clap::{Parser, Subcommand}; - use zksync_config::DBConfig; use zksync_env_config::FromEnv; use zksync_storage::rocksdb::{ @@ -57,9 +56,10 @@ fn main() -> anyhow::Result<()> { #[cfg(test)] mod tests { - use super::*; use tempfile::TempDir; + use super::*; + #[test] fn backup_restore_workflow() { let backup_dir = TempDir::new().expect("failed to get temporary directory for RocksDB"); diff --git a/core/bin/storage_logs_dedup_migration/src/consistency.rs b/core/bin/storage_logs_dedup_migration/src/consistency.rs index 3c63c8c81a72..dc0b3da389c2 100644 --- a/core/bin/storage_logs_dedup_migration/src/consistency.rs +++ b/core/bin/storage_logs_dedup_migration/src/consistency.rs @@ -1,5 +1,4 @@ use clap::Parser; - use zksync_config::PostgresConfig; use zksync_dal::ConnectionPool; use zksync_env_config::FromEnv; diff --git a/core/bin/storage_logs_dedup_migration/src/main.rs b/core/bin/storage_logs_dedup_migration/src/main.rs index 7277c231e43f..733976b44e1e 100644 --- a/core/bin/storage_logs_dedup_migration/src/main.rs +++ b/core/bin/storage_logs_dedup_migration/src/main.rs @@ -1,7 +1,6 @@ use std::collections::hash_map::{Entry, HashMap}; use clap::Parser; - use zksync_config::PostgresConfig; use zksync_dal::ConnectionPool; use zksync_env_config::FromEnv; diff --git a/core/bin/system-constants-generator/src/intrinsic_costs.rs b/core/bin/system-constants-generator/src/intrinsic_costs.rs index e15abf7d1345..94cec591e007 100644 --- a/core/bin/system-constants-generator/src/intrinsic_costs.rs +++ b/core/bin/system-constants-generator/src/intrinsic_costs.rs @@ -4,13 +4,13 @@ //! as well as contracts/SystemConfig.json //! +use multivm::vm_latest::constants::BOOTLOADER_TX_ENCODING_SPACE; +use zksync_types::{ethabi::Address, IntrinsicSystemGasConstants, U256}; + use crate::utils::{ execute_internal_transfer_test, execute_user_txs_in_test_gas_vm, get_l1_tx, get_l1_txs, - get_l2_txs, + get_l2_txs, metrics_from_txs, TransactionGenerator, }; -use crate::utils::{metrics_from_txs, TransactionGenerator}; -use multivm::vm_latest::constants::BOOTLOADER_TX_ENCODING_SPACE; -use zksync_types::{ethabi::Address, IntrinsicSystemGasConstants, U256}; #[derive(Debug, Clone, Copy, PartialEq)] pub(crate) struct VmSpentResourcesResult { diff --git a/core/bin/system-constants-generator/src/main.rs b/core/bin/system-constants-generator/src/main.rs index ed906e1c9bb5..44659d217811 100644 --- a/core/bin/system-constants-generator/src/main.rs +++ b/core/bin/system-constants-generator/src/main.rs @@ -1,7 +1,18 @@ use std::fs; +use codegen::{Block, Scope}; +use multivm::vm_latest::constants::{ + BLOCK_OVERHEAD_GAS, BLOCK_OVERHEAD_L1_GAS, BOOTLOADER_TX_ENCODING_SPACE, MAX_PUBDATA_PER_BLOCK, +}; use serde::{Deserialize, Serialize}; use zksync_types::{ + zkevm_test_harness::zk_evm::zkevm_opcode_defs::{ + circuit_prices::{ + ECRECOVER_CIRCUIT_COST_IN_ERGS, KECCAK256_CIRCUIT_COST_IN_ERGS, + SHA256_CIRCUIT_COST_IN_ERGS, + }, + system_params::MAX_TX_ERGS_LIMIT, + }, IntrinsicSystemGasConstants, GUARANTEED_PUBDATA_IN_TX, L1_GAS_PER_PUBDATA_BYTE, MAX_GAS_PER_PUBDATA_BYTE, MAX_NEW_FACTORY_DEPS, MAX_TXS_IN_BLOCK, }; @@ -9,16 +20,6 @@ use zksync_types::{ mod intrinsic_costs; mod utils; -use codegen::Block; -use codegen::Scope; -use multivm::vm_latest::constants::{ - BLOCK_OVERHEAD_GAS, BLOCK_OVERHEAD_L1_GAS, BOOTLOADER_TX_ENCODING_SPACE, MAX_PUBDATA_PER_BLOCK, -}; -use zksync_types::zkevm_test_harness::zk_evm::zkevm_opcode_defs::circuit_prices::{ - ECRECOVER_CIRCUIT_COST_IN_ERGS, KECCAK256_CIRCUIT_COST_IN_ERGS, SHA256_CIRCUIT_COST_IN_ERGS, -}; -use zksync_types::zkevm_test_harness::zk_evm::zkevm_opcode_defs::system_params::MAX_TX_ERGS_LIMIT; - // Params needed for L1 contracts #[derive(Copy, Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] diff --git a/core/bin/system-constants-generator/src/utils.rs b/core/bin/system-constants-generator/src/utils.rs index fc576ff44eec..b138c5261a83 100644 --- a/core/bin/system-constants-generator/src/utils.rs +++ b/core/bin/system-constants-generator/src/utils.rs @@ -1,16 +1,17 @@ -use once_cell::sync::Lazy; -use std::cell::RefCell; -use std::rc::Rc; - -use multivm::interface::{ - dyn_tracers::vm_1_4_0::DynTracer, tracer::VmExecutionStopReason, L1BatchEnv, L2BlockEnv, - SystemEnv, TxExecutionMode, VmExecutionMode, VmInterface, -}; -use multivm::vm_latest::{ - constants::{BLOCK_GAS_LIMIT, BOOTLOADER_HEAP_PAGE}, - BootloaderState, HistoryEnabled, HistoryMode, SimpleMemory, ToTracerPointer, Vm, VmTracer, - ZkSyncVmState, +use std::{cell::RefCell, rc::Rc}; + +use multivm::{ + interface::{ + dyn_tracers::vm_1_4_0::DynTracer, tracer::VmExecutionStopReason, L1BatchEnv, L2BlockEnv, + SystemEnv, TxExecutionMode, VmExecutionMode, VmInterface, + }, + vm_latest::{ + constants::{BLOCK_GAS_LIMIT, BOOTLOADER_HEAP_PAGE}, + BootloaderState, HistoryEnabled, HistoryMode, SimpleMemory, ToTracerPointer, Vm, VmTracer, + ZkSyncVmState, + }, }; +use once_cell::sync::Lazy; use zksync_contracts::{ load_sys_contract, read_bootloader_code, read_sys_contract_bytecode, read_zbin_bytecode, BaseSystemContracts, ContractLanguage, SystemContractCode, diff --git a/core/bin/verification_key_generator_and_server/src/json_to_binary_vk_converter.rs b/core/bin/verification_key_generator_and_server/src/json_to_binary_vk_converter.rs index 65a2e3361bf4..c04a67128334 100644 --- a/core/bin/verification_key_generator_and_server/src/json_to_binary_vk_converter.rs +++ b/core/bin/verification_key_generator_and_server/src/json_to_binary_vk_converter.rs @@ -1,6 +1,6 @@ +use std::{fs::File, io::BufWriter}; + use bincode::serialize_into; -use std::fs::File; -use std::io::BufWriter; use structopt::StructOpt; use zksync_verification_key_server::get_vk_for_circuit_type; diff --git a/core/bin/verification_key_generator_and_server/src/lib.rs b/core/bin/verification_key_generator_and_server/src/lib.rs index 2b05363595b6..20260a30b20b 100644 --- a/core/bin/verification_key_generator_and_server/src/lib.rs +++ b/core/bin/verification_key_generator_and_server/src/lib.rs @@ -1,29 +1,29 @@ -use ff::to_hex; -use once_cell::sync::Lazy; -use std::collections::HashMap; -use std::path::Path; -use std::str::FromStr; -use zksync_types::zkevm_test_harness::abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit; -use zksync_types::zkevm_test_harness::bellman::bn256::Bn256; -use zksync_types::zkevm_test_harness::bellman::plonk::better_better_cs::setup::VerificationKey; -use zksync_types::zkevm_test_harness::witness::oracle::VmWitnessOracle; +use std::{collections::HashMap, path::Path, str::FromStr}; +use ff::to_hex; use itertools::Itertools; +use once_cell::sync::Lazy; use structopt::lazy_static::lazy_static; -use zksync_types::circuit::SCHEDULER_CIRCUIT_INDEX; -use zksync_types::circuit::{ - GEOMETRY_CONFIG, LEAF_CIRCUIT_INDEX, LEAF_SPLITTING_FACTOR, NODE_CIRCUIT_INDEX, - NODE_SPLITTING_FACTOR, SCHEDULER_UPPER_BOUND, -}; -use zksync_types::protocol_version::{L1VerifierConfig, VerifierParams}; -use zksync_types::vk_transform::generate_vk_commitment; -use zksync_types::zkevm_test_harness::witness; -use zksync_types::zkevm_test_harness::witness::full_block_artifact::BlockBasicCircuits; -use zksync_types::zkevm_test_harness::witness::recursive_aggregation::{ - erase_vk_type, padding_aggregations, +use zksync_types::{ + circuit::{ + GEOMETRY_CONFIG, LEAF_CIRCUIT_INDEX, LEAF_SPLITTING_FACTOR, NODE_CIRCUIT_INDEX, + NODE_SPLITTING_FACTOR, SCHEDULER_CIRCUIT_INDEX, SCHEDULER_UPPER_BOUND, + }, + protocol_version::{L1VerifierConfig, VerifierParams}, + vk_transform::generate_vk_commitment, + zkevm_test_harness::{ + abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit, + bellman::{bn256::Bn256, plonk::better_better_cs::setup::VerificationKey}, + witness, + witness::{ + full_block_artifact::BlockBasicCircuits, + oracle::VmWitnessOracle, + recursive_aggregation::{erase_vk_type, padding_aggregations}, + vk_set_generator::circuits_for_vk_generation, + }, + }, + H256, }; -use zksync_types::zkevm_test_harness::witness::vk_set_generator::circuits_for_vk_generation; -use zksync_types::H256; #[cfg(test)] mod tests; diff --git a/core/bin/verification_key_generator_and_server/src/main.rs b/core/bin/verification_key_generator_and_server/src/main.rs index 30ffb0574d4d..b64e5757fceb 100644 --- a/core/bin/verification_key_generator_and_server/src/main.rs +++ b/core/bin/verification_key_generator_and_server/src/main.rs @@ -1,9 +1,12 @@ -use std::collections::HashSet; -use std::env; -use zksync_types::zkevm_test_harness::abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit; -use zksync_types::zkevm_test_harness::bellman::bn256::Bn256; -use zksync_types::zkevm_test_harness::bellman::plonk::better_better_cs::cs::PlonkCsWidth4WithNextStepAndCustomGatesParams; -use zksync_types::zkevm_test_harness::witness::oracle::VmWitnessOracle; +use std::{collections::HashSet, env}; + +use zksync_types::zkevm_test_harness::{ + abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit, + bellman::{ + bn256::Bn256, plonk::better_better_cs::cs::PlonkCsWidth4WithNextStepAndCustomGatesParams, + }, + witness::oracle::VmWitnessOracle, +}; use zksync_verification_key_server::{get_circuits_for_vk, save_vk_for_circuit_type}; /// Creates verification keys for the given circuit. diff --git a/core/bin/verification_key_generator_and_server/src/tests.rs b/core/bin/verification_key_generator_and_server/src/tests.rs index 8f013bad2000..f0fea866de6d 100644 --- a/core/bin/verification_key_generator_and_server/src/tests.rs +++ b/core/bin/verification_key_generator_and_server/src/tests.rs @@ -1,12 +1,14 @@ -use crate::{get_vk_for_circuit_type, get_vks_for_basic_circuits, get_vks_for_commitment}; +use std::collections::HashMap; + use itertools::Itertools; use serde_json::Value; -use std::collections::HashMap; -use zksync_types::zkevm_test_harness::abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit; -use zksync_types::zkevm_test_harness::bellman::bn256::Bn256; -use zksync_types::zkevm_test_harness::bellman::plonk::better_better_cs::setup::VerificationKey; +use zksync_types::zkevm_test_harness::{ + abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit, + bellman::{bn256::Bn256, plonk::better_better_cs::setup::VerificationKey}, + witness::oracle::VmWitnessOracle, +}; -use zksync_types::zkevm_test_harness::witness::oracle::VmWitnessOracle; +use crate::{get_vk_for_circuit_type, get_vks_for_basic_circuits, get_vks_for_commitment}; #[test] fn test_get_vk_for_circuit_type() { diff --git a/core/bin/verified_sources_fetcher/src/main.rs b/core/bin/verified_sources_fetcher/src/main.rs index 6bb6ee66cee1..cc53229329fc 100644 --- a/core/bin/verified_sources_fetcher/src/main.rs +++ b/core/bin/verified_sources_fetcher/src/main.rs @@ -1,4 +1,5 @@ use std::io::Write; + use zksync_config::PostgresConfig; use zksync_dal::ConnectionPool; use zksync_env_config::FromEnv; diff --git a/core/bin/zksync_server/src/main.rs b/core/bin/zksync_server/src/main.rs index f2aed9c75c24..9a5ccf8be603 100644 --- a/core/bin/zksync_server/src/main.rs +++ b/core/bin/zksync_server/src/main.rs @@ -1,8 +1,7 @@ -use anyhow::Context as _; -use clap::Parser; - use std::{str::FromStr, time::Duration}; +use anyhow::Context as _; +use clap::Parser; use zksync_config::{ configs::{ api::{HealthCheckConfig, MerkleTreeApiConfig, Web3JsonRpcConfig}, @@ -18,11 +17,9 @@ use zksync_config::{ ApiConfig, ContractsConfig, DBConfig, ETHClientConfig, ETHSenderConfig, ETHWatchConfig, FetcherConfig, GasAdjusterConfig, ObjectStoreConfig, PostgresConfig, ProverConfigs, }; - -use zksync_core::temp_config_store::TempConfigStore; use zksync_core::{ - genesis_init, initialize_components, is_genesis_needed, setup_sigint_handler, Component, - Components, + genesis_init, initialize_components, is_genesis_needed, setup_sigint_handler, + temp_config_store::TempConfigStore, Component, Components, }; use zksync_env_config::FromEnv; use zksync_storage::RocksDB; diff --git a/core/lib/basic_types/src/lib.rs b/core/lib/basic_types/src/lib.rs index 6c6223fbb177..aa9bf615c915 100644 --- a/core/lib/basic_types/src/lib.rs +++ b/core/lib/basic_types/src/lib.rs @@ -2,25 +2,25 @@ //! //! Most of them are just re-exported from the `web3` crate. +use std::{ + convert::{Infallible, TryFrom, TryInto}, + fmt, + num::ParseIntError, + ops::{Add, Deref, DerefMut, Sub}, + str::FromStr, +}; + +use serde::{de, Deserialize, Deserializer, Serialize}; +pub use web3::{ + self, ethabi, + types::{Address, Bytes, Log, TransactionRequest, H128, H160, H2048, H256, U128, U256, U64}, +}; + #[macro_use] mod macros; - pub mod basic_fri_types; pub mod network; -use serde::{de, Deserialize, Deserializer, Serialize}; -use std::convert::{Infallible, TryFrom, TryInto}; -use std::fmt; -use std::num::ParseIntError; -use std::ops::{Add, Deref, DerefMut, Sub}; -use std::str::FromStr; - -pub use web3; -pub use web3::ethabi; -pub use web3::types::{ - Address, Bytes, Log, TransactionRequest, H128, H160, H2048, H256, U128, U256, U64, -}; - /// Account place in the global state tree is uniquely identified by its address. /// Binary this type is represented by 160 bit big-endian representation of account address. #[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Hash, Ord, PartialOrd)] @@ -222,9 +222,10 @@ impl Default for PriorityOpId { #[cfg(test)] mod tests { - use super::*; use serde_json::from_str; + use super::*; + #[test] fn test_from_str_valid_decimal() { let input = "42"; diff --git a/core/lib/circuit_breaker/src/l1_txs.rs b/core/lib/circuit_breaker/src/l1_txs.rs index 5279106637e7..5d3c4dc9ccf3 100644 --- a/core/lib/circuit_breaker/src/l1_txs.rs +++ b/core/lib/circuit_breaker/src/l1_txs.rs @@ -1,6 +1,7 @@ -use crate::{CircuitBreaker, CircuitBreakerError}; use zksync_dal::ConnectionPool; +use crate::{CircuitBreaker, CircuitBreakerError}; + #[derive(Debug)] pub struct FailedL1TransactionChecker { pub pool: ConnectionPool, diff --git a/core/lib/circuit_breaker/src/lib.rs b/core/lib/circuit_breaker/src/lib.rs index 878114f0d042..4c84f857a295 100644 --- a/core/lib/circuit_breaker/src/lib.rs +++ b/core/lib/circuit_breaker/src/lib.rs @@ -4,7 +4,6 @@ use anyhow::Context as _; use futures::channel::oneshot; use thiserror::Error; use tokio::sync::watch; - use zksync_config::configs::chain::CircuitBreakerConfig; pub mod l1_txs; diff --git a/core/lib/config/src/configs/api.rs b/core/lib/config/src/configs/api.rs index 14b3d81520c1..348c1c95e2d7 100644 --- a/core/lib/config/src/configs/api.rs +++ b/core/lib/config/src/configs/api.rs @@ -1,10 +1,10 @@ -use serde::Deserialize; - use std::{net::SocketAddr, time::Duration}; -pub use crate::configs::PrometheusConfig; +use serde::Deserialize; use zksync_basic_types::H256; +pub use crate::configs::PrometheusConfig; + /// API configuration. #[derive(Debug, Deserialize, Clone, PartialEq)] pub struct ApiConfig { diff --git a/core/lib/config/src/configs/chain.rs b/core/lib/config/src/configs/chain.rs index f09b5bb292c5..eb77467183fe 100644 --- a/core/lib/config/src/configs/chain.rs +++ b/core/lib/config/src/configs/chain.rs @@ -1,11 +1,7 @@ -/// External uses +use std::{str::FromStr, time::Duration}; + use serde::Deserialize; -use std::str::FromStr; -/// Built-in uses -use std::time::Duration; -// Local uses -use zksync_basic_types::network::Network; -use zksync_basic_types::{Address, L2ChainId}; +use zksync_basic_types::{network::Network, Address, L2ChainId}; #[derive(Debug, Deserialize, Clone, PartialEq)] pub struct ChainConfig { diff --git a/core/lib/config/src/configs/contract_verifier.rs b/core/lib/config/src/configs/contract_verifier.rs index 5c2a1608c8f5..db3c8fa1b526 100644 --- a/core/lib/config/src/configs/contract_verifier.rs +++ b/core/lib/config/src/configs/contract_verifier.rs @@ -1,6 +1,5 @@ -// Built-in uses use std::time::Duration; -// External uses + use serde::Deserialize; #[derive(Debug, Deserialize, Clone, PartialEq)] diff --git a/core/lib/config/src/configs/database.rs b/core/lib/config/src/configs/database.rs index d257e661eb37..dcff7d486a83 100644 --- a/core/lib/config/src/configs/database.rs +++ b/core/lib/config/src/configs/database.rs @@ -1,8 +1,8 @@ +use std::time::Duration; + use anyhow::Context as _; use serde::{Deserialize, Serialize}; -use std::time::Duration; - /// Mode of operation for the Merkle tree. /// /// The mode does not influence how tree data is stored; i.e., a mode can be switched on the fly. diff --git a/core/lib/config/src/configs/eth_sender.rs b/core/lib/config/src/configs/eth_sender.rs index 3d036483347f..cd44daed17f8 100644 --- a/core/lib/config/src/configs/eth_sender.rs +++ b/core/lib/config/src/configs/eth_sender.rs @@ -1,8 +1,6 @@ -// Built-in uses use std::time::Duration; -// External uses + use serde::Deserialize; -// Workspace uses use zksync_basic_types::H256; /// Configuration for the Ethereum sender crate. diff --git a/core/lib/config/src/configs/eth_watch.rs b/core/lib/config/src/configs/eth_watch.rs index 93d73ddf6bfd..05afebf81c3b 100644 --- a/core/lib/config/src/configs/eth_watch.rs +++ b/core/lib/config/src/configs/eth_watch.rs @@ -1,6 +1,5 @@ -// Built-in uses use std::time::Duration; -// External uses + use serde::Deserialize; /// Configuration for the Ethereum sender crate. diff --git a/core/lib/config/src/configs/fetcher.rs b/core/lib/config/src/configs/fetcher.rs index b1a5fca4b24a..a1e63742e224 100644 --- a/core/lib/config/src/configs/fetcher.rs +++ b/core/lib/config/src/configs/fetcher.rs @@ -1,6 +1,7 @@ -use serde::Deserialize; use std::time::Duration; +use serde::Deserialize; + #[derive(Debug, Deserialize, Clone, Copy, PartialEq)] pub enum TokenListSource { OneInch, diff --git a/core/lib/config/src/configs/fri_proof_compressor.rs b/core/lib/config/src/configs/fri_proof_compressor.rs index bbf58f2d1c69..4b4e062dee28 100644 --- a/core/lib/config/src/configs/fri_proof_compressor.rs +++ b/core/lib/config/src/configs/fri_proof_compressor.rs @@ -1,6 +1,7 @@ -use serde::Deserialize; use std::time::Duration; +use serde::Deserialize; + /// Configuration for the fri proof compressor #[derive(Debug, Deserialize, Clone, PartialEq)] pub struct FriProofCompressorConfig { diff --git a/core/lib/config/src/configs/fri_prover.rs b/core/lib/config/src/configs/fri_prover.rs index aab358a4adaf..44521ee36576 100644 --- a/core/lib/config/src/configs/fri_prover.rs +++ b/core/lib/config/src/configs/fri_prover.rs @@ -1,6 +1,7 @@ -use serde::Deserialize; use std::time::Duration; +use serde::Deserialize; + #[derive(Debug, Deserialize, Clone, PartialEq)] pub enum SetupLoadMode { FromDisk, diff --git a/core/lib/config/src/configs/fri_prover_gateway.rs b/core/lib/config/src/configs/fri_prover_gateway.rs index 652c7d1bc0f5..86723ff30433 100644 --- a/core/lib/config/src/configs/fri_prover_gateway.rs +++ b/core/lib/config/src/configs/fri_prover_gateway.rs @@ -1,6 +1,7 @@ -use serde::Deserialize; use std::time::Duration; +use serde::Deserialize; + #[derive(Debug, Deserialize, Clone, PartialEq)] pub struct FriProverGatewayConfig { pub api_url: String, diff --git a/core/lib/config/src/configs/fri_prover_group.rs b/core/lib/config/src/configs/fri_prover_group.rs index 71ed5d1f7d9b..856ff59809f7 100644 --- a/core/lib/config/src/configs/fri_prover_group.rs +++ b/core/lib/config/src/configs/fri_prover_group.rs @@ -1,6 +1,6 @@ -use serde::Deserialize; use std::collections::HashSet; +use serde::Deserialize; use zksync_basic_types::basic_fri_types::CircuitIdRoundTuple; /// Configuration for the grouping of specialized provers. diff --git a/core/lib/config/src/configs/mod.rs b/core/lib/config/src/configs/mod.rs index 0c2ecc46103b..710c128c951f 100644 --- a/core/lib/config/src/configs/mod.rs +++ b/core/lib/config/src/configs/mod.rs @@ -1,15 +1,26 @@ // Public re-exports pub use self::{ - alerts::AlertsConfig, api::ApiConfig, chain::ChainConfig, - circuit_synthesizer::CircuitSynthesizerConfig, contract_verifier::ContractVerifierConfig, - contracts::ContractsConfig, database::DBConfig, database::PostgresConfig, - eth_client::ETHClientConfig, eth_sender::ETHSenderConfig, eth_sender::GasAdjusterConfig, - eth_watch::ETHWatchConfig, fetcher::FetcherConfig, - fri_proof_compressor::FriProofCompressorConfig, fri_prover::FriProverConfig, - fri_prover_gateway::FriProverGatewayConfig, fri_witness_generator::FriWitnessGeneratorConfig, - fri_witness_vector_generator::FriWitnessVectorGeneratorConfig, object_store::ObjectStoreConfig, - proof_data_handler::ProofDataHandlerConfig, prover::ProverConfig, prover::ProverConfigs, - prover_group::ProverGroupConfig, utils::PrometheusConfig, + alerts::AlertsConfig, + api::ApiConfig, + chain::ChainConfig, + circuit_synthesizer::CircuitSynthesizerConfig, + contract_verifier::ContractVerifierConfig, + contracts::ContractsConfig, + database::{DBConfig, PostgresConfig}, + eth_client::ETHClientConfig, + eth_sender::{ETHSenderConfig, GasAdjusterConfig}, + eth_watch::ETHWatchConfig, + fetcher::FetcherConfig, + fri_proof_compressor::FriProofCompressorConfig, + fri_prover::FriProverConfig, + fri_prover_gateway::FriProverGatewayConfig, + fri_witness_generator::FriWitnessGeneratorConfig, + fri_witness_vector_generator::FriWitnessVectorGeneratorConfig, + object_store::ObjectStoreConfig, + proof_data_handler::ProofDataHandlerConfig, + prover::{ProverConfig, ProverConfigs}, + prover_group::ProverGroupConfig, + utils::PrometheusConfig, witness_generator::WitnessGeneratorConfig, }; diff --git a/core/lib/config/src/configs/proof_data_handler.rs b/core/lib/config/src/configs/proof_data_handler.rs index e3efd6b7a4d7..b773efbd7df2 100644 --- a/core/lib/config/src/configs/proof_data_handler.rs +++ b/core/lib/config/src/configs/proof_data_handler.rs @@ -1,6 +1,7 @@ -use serde::Deserialize; use std::time::Duration; +use serde::Deserialize; + #[derive(Debug, Deserialize, Clone, Copy, PartialEq)] pub enum ProtocolVersionLoadingMode { FromDb, diff --git a/core/lib/config/src/configs/utils.rs b/core/lib/config/src/configs/utils.rs index bfa9e7e7f3e6..977a48e82d20 100644 --- a/core/lib/config/src/configs/utils.rs +++ b/core/lib/config/src/configs/utils.rs @@ -1,7 +1,7 @@ -use serde::Deserialize; - use std::{env, time::Duration}; +use serde::Deserialize; + #[derive(Debug, Deserialize, Clone, PartialEq)] pub struct PrometheusConfig { /// Port to which the Prometheus exporter server is listening. diff --git a/core/lib/contracts/src/lib.rs b/core/lib/contracts/src/lib.rs index 766d2464d343..917bf7a6ffef 100644 --- a/core/lib/contracts/src/lib.rs +++ b/core/lib/contracts/src/lib.rs @@ -3,17 +3,18 @@ //! Careful: some of the methods are reading the contracts based on the ZKSYNC_HOME environment variable. #![allow(clippy::derive_partial_eq_without_eq)] + +use std::{ + fs::{self, File}, + path::{Path, PathBuf}, +}; + use ethabi::{ ethereum_types::{H256, U256}, Contract, Function, }; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; -use std::{ - fs::{self, File}, - path::{Path, PathBuf}, -}; - use zksync_utils::{bytecode::hash_bytecode, bytes_to_be_words}; pub mod test_contracts; diff --git a/core/lib/contracts/src/test_contracts.rs b/core/lib/contracts/src/test_contracts.rs index 9db4051cfdbd..eab1587f8335 100644 --- a/core/lib/contracts/src/test_contracts.rs +++ b/core/lib/contracts/src/test_contracts.rs @@ -1,8 +1,8 @@ -use crate::get_loadnext_contract; -use ethabi::ethereum_types::U256; -use ethabi::{Bytes, Token}; +use ethabi::{ethereum_types::U256, Bytes, Token}; use serde::Deserialize; +use crate::get_loadnext_contract; + #[derive(Debug, Clone, Deserialize)] pub struct LoadnextContractExecutionParams { pub reads: usize, diff --git a/core/lib/crypto/src/hasher/blake2.rs b/core/lib/crypto/src/hasher/blake2.rs index 70d8c9797e8e..97d3fbb8a1ea 100644 --- a/core/lib/crypto/src/hasher/blake2.rs +++ b/core/lib/crypto/src/hasher/blake2.rs @@ -1,7 +1,7 @@ use blake2::{Blake2s256, Digest}; +use zksync_basic_types::H256; use crate::hasher::Hasher; -use zksync_basic_types::H256; #[derive(Default, Clone, Debug)] pub struct Blake2Hasher; diff --git a/core/lib/crypto/src/hasher/keccak.rs b/core/lib/crypto/src/hasher/keccak.rs index e4c441328de9..d3baab873f9d 100644 --- a/core/lib/crypto/src/hasher/keccak.rs +++ b/core/lib/crypto/src/hasher/keccak.rs @@ -1,6 +1,7 @@ -use crate::hasher::Hasher; use zksync_basic_types::{web3::signing::keccak256, H256}; +use crate::hasher::Hasher; + #[derive(Default, Clone, Debug)] pub struct KeccakHasher; diff --git a/core/lib/crypto/src/hasher/sha256.rs b/core/lib/crypto/src/hasher/sha256.rs index 73e593ead72e..b976c79d2108 100644 --- a/core/lib/crypto/src/hasher/sha256.rs +++ b/core/lib/crypto/src/hasher/sha256.rs @@ -1,7 +1,7 @@ use sha2::{Digest, Sha256}; +use zksync_basic_types::H256; use crate::hasher::Hasher; -use zksync_basic_types::H256; #[derive(Debug, Default, Clone, Copy)] pub struct Sha256Hasher; diff --git a/core/lib/dal/src/basic_witness_input_producer_dal.rs b/core/lib/dal/src/basic_witness_input_producer_dal.rs index ac0627a96a05..cae640e94b83 100644 --- a/core/lib/dal/src/basic_witness_input_producer_dal.rs +++ b/core/lib/dal/src/basic_witness_input_producer_dal.rs @@ -1,10 +1,14 @@ -use crate::instrument::InstrumentExt; -use crate::time_utils::{duration_to_naive_time, pg_interval_from_duration}; -use crate::StorageProcessor; -use sqlx::postgres::types::PgInterval; use std::time::{Duration, Instant}; + +use sqlx::postgres::types::PgInterval; use zksync_types::L1BatchNumber; +use crate::{ + instrument::InstrumentExt, + time_utils::{duration_to_naive_time, pg_interval_from_duration}, + StorageProcessor, +}; + #[derive(Debug)] pub struct BasicWitnessInputProducerDal<'a, 'c> { pub(crate) storage: &'a mut StorageProcessor<'c>, diff --git a/core/lib/dal/src/blocks_dal.rs b/core/lib/dal/src/blocks_dal.rs index c60d52e197b0..16e926393fbc 100644 --- a/core/lib/dal/src/blocks_dal.rs +++ b/core/lib/dal/src/blocks_dal.rs @@ -7,7 +7,6 @@ use std::{ use anyhow::Context as _; use bigdecimal::{BigDecimal, FromPrimitive, ToPrimitive}; use sqlx::Row; - use zksync_types::{ aggregated_operations::AggregatedActionType, block::{BlockGasCount, L1BatchHeader, MiniblockHeader}, diff --git a/core/lib/dal/src/blocks_web3_dal.rs b/core/lib/dal/src/blocks_web3_dal.rs index 0c2a8b4e1885..87f6fca1eb26 100644 --- a/core/lib/dal/src/blocks_web3_dal.rs +++ b/core/lib/dal/src/blocks_web3_dal.rs @@ -1,6 +1,5 @@ use bigdecimal::BigDecimal; use sqlx::Row; - use zksync_system_constants::EMPTY_UNCLES_HASH; use zksync_types::{ api, @@ -13,14 +12,17 @@ use zksync_types::{ }; use zksync_utils::bigdecimal_to_u256; -use crate::models::{ - storage_block::{ - bind_block_where_sql_params, web3_block_number_to_sql, web3_block_where_sql, - StorageBlockDetails, StorageL1BatchDetails, +use crate::{ + instrument::InstrumentExt, + models::{ + storage_block::{ + bind_block_where_sql_params, web3_block_number_to_sql, web3_block_where_sql, + StorageBlockDetails, StorageL1BatchDetails, + }, + storage_transaction::{extract_web3_transaction, web3_transaction_select_sql, CallTrace}, }, - storage_transaction::{extract_web3_transaction, web3_transaction_select_sql, CallTrace}, + StorageProcessor, }; -use crate::{instrument::InstrumentExt, StorageProcessor}; const BLOCK_GAS_LIMIT: u32 = system_params::VM_INITIAL_FRAME_ERGS; diff --git a/core/lib/dal/src/connection/holder.rs b/core/lib/dal/src/connection/holder.rs index 265b892c089a..1174f834ae8b 100644 --- a/core/lib/dal/src/connection/holder.rs +++ b/core/lib/dal/src/connection/holder.rs @@ -1,8 +1,7 @@ -// Built-in deps -use sqlx::pool::PoolConnection; -use sqlx::{postgres::Postgres, Transaction}; use std::fmt; +use sqlx::{pool::PoolConnection, postgres::Postgres, Transaction}; + /// Connection holder unifies the type of underlying connection, which /// can be either pooled or direct. pub(crate) enum ConnectionHolder<'a> { diff --git a/core/lib/dal/src/connection/mod.rs b/core/lib/dal/src/connection/mod.rs index 845dbc64dc4c..b7f82d619ff8 100644 --- a/core/lib/dal/src/connection/mod.rs +++ b/core/lib/dal/src/connection/mod.rs @@ -1,17 +1,15 @@ +use std::{env, fmt, time::Duration}; + +use anyhow::Context as _; use sqlx::{ pool::PoolConnection, postgres::{PgConnectOptions, PgPool, PgPoolOptions, Postgres}, }; -use anyhow::Context as _; -use std::env; -use std::fmt; -use std::time::Duration; +use crate::{metrics::CONNECTION_METRICS, StorageProcessor}; pub mod holder; -use crate::{metrics::CONNECTION_METRICS, StorageProcessor}; - /// Obtains the test database URL from the environment variable. fn get_test_database_url() -> anyhow::Result { env::var("TEST_DATABASE_URL").context( diff --git a/core/lib/dal/src/contract_verification_dal.rs b/core/lib/dal/src/contract_verification_dal.rs index a6c549f482bc..5466b0c11b23 100644 --- a/core/lib/dal/src/contract_verification_dal.rs +++ b/core/lib/dal/src/contract_verification_dal.rs @@ -1,7 +1,10 @@ -use anyhow::Context as _; -use std::fmt::{Display, Formatter}; -use std::time::Duration; +use std::{ + fmt::{Display, Formatter}, + time::Duration, +}; +use anyhow::Context as _; +use sqlx::postgres::types::PgInterval; use zksync_types::{ contract_verification_api::{ DeployContractCalldata, VerificationIncomingRequest, VerificationInfo, VerificationRequest, @@ -10,10 +13,7 @@ use zksync_types::{ get_code_key, Address, CONTRACT_DEPLOYER_ADDRESS, FAILED_CONTRACT_DEPLOYMENT_BYTECODE_HASH, }; -use sqlx::postgres::types::PgInterval; - -use crate::models::storage_verification_request::StorageVerificationRequest; -use crate::StorageProcessor; +use crate::{models::storage_verification_request::StorageVerificationRequest, StorageProcessor}; #[derive(Debug)] pub struct ContractVerificationDal<'a, 'c> { diff --git a/core/lib/dal/src/eth_sender_dal.rs b/core/lib/dal/src/eth_sender_dal.rs index 0d9d1da0dab9..94d7adfe2849 100644 --- a/core/lib/dal/src/eth_sender_dal.rs +++ b/core/lib/dal/src/eth_sender_dal.rs @@ -1,17 +1,22 @@ -use crate::models::storage_eth_tx::{ - L1BatchEthSenderStats, StorageEthTx, StorageTxHistory, StorageTxHistoryToSend, -}; -use crate::StorageProcessor; +use std::{convert::TryFrom, str::FromStr}; + use anyhow::Context as _; use sqlx::{ types::chrono::{DateTime, Utc}, Row, }; -use std::convert::TryFrom; -use std::str::FromStr; -use zksync_types::aggregated_operations::AggregatedActionType; -use zksync_types::eth_sender::{EthTx, TxHistory, TxHistoryToSend}; -use zksync_types::{Address, L1BatchNumber, H256, U256}; +use zksync_types::{ + aggregated_operations::AggregatedActionType, + eth_sender::{EthTx, TxHistory, TxHistoryToSend}, + Address, L1BatchNumber, H256, U256, +}; + +use crate::{ + models::storage_eth_tx::{ + L1BatchEthSenderStats, StorageEthTx, StorageTxHistory, StorageTxHistoryToSend, + }, + StorageProcessor, +}; #[derive(Debug)] pub struct EthSenderDal<'a, 'c> { diff --git a/core/lib/dal/src/events_dal.rs b/core/lib/dal/src/events_dal.rs index 6355deaf29ab..22967982b3b0 100644 --- a/core/lib/dal/src/events_dal.rs +++ b/core/lib/dal/src/events_dal.rs @@ -1,14 +1,14 @@ -use sqlx::types::chrono::Utc; - use std::fmt; -use crate::{models::storage_event::StorageL2ToL1Log, SqlxError, StorageProcessor}; +use sqlx::types::chrono::Utc; use zksync_types::{ l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}, tx::IncludedTxLocation, MiniblockNumber, VmEvent, H256, }; +use crate::{models::storage_event::StorageL2ToL1Log, SqlxError, StorageProcessor}; + /// Wrapper around an optional event topic allowing to hex-format it for `COPY` instructions. #[derive(Debug)] struct EventTopic<'a>(Option<&'a H256>); @@ -196,9 +196,10 @@ impl EventsDal<'_, '_> { #[cfg(test)] mod tests { + use zksync_types::{Address, L1BatchNumber, ProtocolVersion}; + use super::*; use crate::{tests::create_miniblock_header, ConnectionPool}; - use zksync_types::{Address, L1BatchNumber, ProtocolVersion}; fn create_vm_event(index: u8, topic_count: u8) -> VmEvent { assert!(topic_count <= 4); diff --git a/core/lib/dal/src/events_web3_dal.rs b/core/lib/dal/src/events_web3_dal.rs index 7cdf2dba6467..e8b1c802446e 100644 --- a/core/lib/dal/src/events_web3_dal.rs +++ b/core/lib/dal/src/events_web3_dal.rs @@ -1,5 +1,4 @@ use sqlx::Row; - use zksync_types::{ api::{GetLogsFilter, Log}, Address, MiniblockNumber, H256, diff --git a/core/lib/dal/src/fri_gpu_prover_queue_dal.rs b/core/lib/dal/src/fri_gpu_prover_queue_dal.rs index 46c46a15b73d..141b2e0378e2 100644 --- a/core/lib/dal/src/fri_gpu_prover_queue_dal.rs +++ b/core/lib/dal/src/fri_gpu_prover_queue_dal.rs @@ -1,8 +1,8 @@ use std::time::Duration; + use zksync_types::proofs::{GpuProverInstanceStatus, SocketAddress}; -use crate::time_utils::pg_interval_from_duration; -use crate::StorageProcessor; +use crate::{time_utils::pg_interval_from_duration, StorageProcessor}; #[derive(Debug)] pub struct FriGpuProverQueueDal<'a, 'c> { diff --git a/core/lib/dal/src/fri_proof_compressor_dal.rs b/core/lib/dal/src/fri_proof_compressor_dal.rs index b7f1d1921e91..6e6db0bb6d86 100644 --- a/core/lib/dal/src/fri_proof_compressor_dal.rs +++ b/core/lib/dal/src/fri_proof_compressor_dal.rs @@ -1,14 +1,16 @@ +use std::{collections::HashMap, str::FromStr, time::Duration}; + use sqlx::Row; -use std::collections::HashMap; -use std::str::FromStr; -use std::time::Duration; use strum::{Display, EnumString}; - -use zksync_types::proofs::{JobCountStatistics, StuckJobs}; -use zksync_types::L1BatchNumber; - -use crate::time_utils::{duration_to_naive_time, pg_interval_from_duration}; -use crate::StorageProcessor; +use zksync_types::{ + proofs::{JobCountStatistics, StuckJobs}, + L1BatchNumber, +}; + +use crate::{ + time_utils::{duration_to_naive_time, pg_interval_from_duration}, + StorageProcessor, +}; #[derive(Debug)] pub struct FriProofCompressorDal<'a, 'c> { diff --git a/core/lib/dal/src/fri_protocol_versions_dal.rs b/core/lib/dal/src/fri_protocol_versions_dal.rs index 8fbcf922d8bb..7eac1190bb9f 100644 --- a/core/lib/dal/src/fri_protocol_versions_dal.rs +++ b/core/lib/dal/src/fri_protocol_versions_dal.rs @@ -1,7 +1,6 @@ use std::convert::TryFrom; -use zksync_types::protocol_version::FriProtocolVersionId; -use zksync_types::protocol_version::L1VerifierConfig; +use zksync_types::protocol_version::{FriProtocolVersionId, L1VerifierConfig}; use crate::StorageProcessor; diff --git a/core/lib/dal/src/fri_scheduler_dependency_tracker_dal.rs b/core/lib/dal/src/fri_scheduler_dependency_tracker_dal.rs index 3844f5777cec..a9639dfe9518 100644 --- a/core/lib/dal/src/fri_scheduler_dependency_tracker_dal.rs +++ b/core/lib/dal/src/fri_scheduler_dependency_tracker_dal.rs @@ -1,6 +1,7 @@ -use crate::StorageProcessor; use zksync_types::L1BatchNumber; +use crate::StorageProcessor; + #[derive(Debug)] pub struct FriSchedulerDependencyTrackerDal<'a, 'c> { pub storage: &'a mut StorageProcessor<'c>, diff --git a/core/lib/dal/src/fri_witness_generator_dal.rs b/core/lib/dal/src/fri_witness_generator_dal.rs index c05dd3b3d1a8..c11f20adec56 100644 --- a/core/lib/dal/src/fri_witness_generator_dal.rs +++ b/core/lib/dal/src/fri_witness_generator_dal.rs @@ -1,14 +1,12 @@ -use sqlx::Row; - -use std::convert::TryFrom; -use std::{collections::HashMap, time::Duration}; +use std::{collections::HashMap, convert::TryFrom, time::Duration}; -use zksync_types::protocol_version::FriProtocolVersionId; +use sqlx::Row; use zksync_types::{ proofs::{ AggregationRound, JobCountStatistics, LeafAggregationJobMetadata, NodeAggregationJobMetadata, StuckJobs, }, + protocol_version::FriProtocolVersionId, L1BatchNumber, }; diff --git a/core/lib/dal/src/gpu_prover_queue_dal.rs b/core/lib/dal/src/gpu_prover_queue_dal.rs index cc769ff30087..b4c348ab3e77 100644 --- a/core/lib/dal/src/gpu_prover_queue_dal.rs +++ b/core/lib/dal/src/gpu_prover_queue_dal.rs @@ -1,10 +1,9 @@ -use std::time::Duration; +use std::{collections::HashMap, time::Duration}; -use crate::time_utils::pg_interval_from_duration; -use crate::StorageProcessor; -use std::collections::HashMap; use zksync_types::proofs::{GpuProverInstanceStatus, SocketAddress}; +use crate::{time_utils::pg_interval_from_duration, StorageProcessor}; + #[derive(Debug)] pub struct GpuProverQueueDal<'a, 'c> { pub(crate) storage: &'a mut StorageProcessor<'c>, diff --git a/core/lib/dal/src/healthcheck.rs b/core/lib/dal/src/healthcheck.rs index 902a235ce540..ec56f8ea9318 100644 --- a/core/lib/dal/src/healthcheck.rs +++ b/core/lib/dal/src/healthcheck.rs @@ -1,6 +1,5 @@ use serde::Serialize; use sqlx::PgPool; - use zksync_health_check::{async_trait, CheckHealth, Health, HealthStatus}; use crate::ConnectionPool; diff --git a/core/lib/dal/src/instrument.rs b/core/lib/dal/src/instrument.rs index cd761fb35004..5d99b0729de6 100644 --- a/core/lib/dal/src/instrument.rs +++ b/core/lib/dal/src/instrument.rs @@ -1,5 +1,7 @@ //! DAL query instrumentation. +use std::{fmt, future::Future, panic::Location}; + use sqlx::{ postgres::{PgConnection, PgQueryResult, PgRow}, query::{Map, Query, QueryAs}, @@ -7,8 +9,6 @@ use sqlx::{ }; use tokio::time::{Duration, Instant}; -use std::{fmt, future::Future, panic::Location}; - use crate::metrics::REQUEST_METRICS; type ThreadSafeDebug<'a> = dyn fmt::Debug + Send + Sync + 'a; diff --git a/core/lib/dal/src/lib.rs b/core/lib/dal/src/lib.rs index 9dfc9458202d..68d19fe84b95 100644 --- a/core/lib/dal/src/lib.rs +++ b/core/lib/dal/src/lib.rs @@ -1,45 +1,27 @@ #![allow(clippy::derive_partial_eq_without_eq, clippy::format_push_string)] -// Built-in deps -pub use sqlx::Error as SqlxError; -use sqlx::{postgres::Postgres, Connection, PgConnection, Transaction}; -// External imports -use sqlx::pool::PoolConnection; -pub use sqlx::types::BigDecimal; - -// Local imports -use crate::accounts_dal::AccountsDal; -use crate::basic_witness_input_producer_dal::BasicWitnessInputProducerDal; -use crate::blocks_dal::BlocksDal; -use crate::blocks_web3_dal::BlocksWeb3Dal; -use crate::connection::holder::ConnectionHolder; +use sqlx::{pool::PoolConnection, postgres::Postgres, Connection, PgConnection, Transaction}; +pub use sqlx::{types::BigDecimal, Error as SqlxError}; + pub use crate::connection::ConnectionPool; -use crate::contract_verification_dal::ContractVerificationDal; -use crate::eth_sender_dal::EthSenderDal; -use crate::events_dal::EventsDal; -use crate::events_web3_dal::EventsWeb3Dal; -use crate::fri_gpu_prover_queue_dal::FriGpuProverQueueDal; -use crate::fri_proof_compressor_dal::FriProofCompressorDal; -use crate::fri_protocol_versions_dal::FriProtocolVersionsDal; -use crate::fri_prover_dal::FriProverDal; -use crate::fri_scheduler_dependency_tracker_dal::FriSchedulerDependencyTrackerDal; -use crate::fri_witness_generator_dal::FriWitnessGeneratorDal; -use crate::gpu_prover_queue_dal::GpuProverQueueDal; -use crate::proof_generation_dal::ProofGenerationDal; -use crate::protocol_versions_dal::ProtocolVersionsDal; -use crate::protocol_versions_web3_dal::ProtocolVersionsWeb3Dal; -use crate::prover_dal::ProverDal; -use crate::storage_dal::StorageDal; -use crate::storage_logs_dal::StorageLogsDal; -use crate::storage_logs_dedup_dal::StorageLogsDedupDal; -use crate::storage_web3_dal::StorageWeb3Dal; -use crate::sync_dal::SyncDal; -use crate::system_dal::SystemDal; -use crate::tokens_dal::TokensDal; -use crate::tokens_web3_dal::TokensWeb3Dal; -use crate::transactions_dal::TransactionsDal; -use crate::transactions_web3_dal::TransactionsWeb3Dal; -use crate::witness_generator_dal::WitnessGeneratorDal; +use crate::{ + accounts_dal::AccountsDal, basic_witness_input_producer_dal::BasicWitnessInputProducerDal, + blocks_dal::BlocksDal, blocks_web3_dal::BlocksWeb3Dal, connection::holder::ConnectionHolder, + contract_verification_dal::ContractVerificationDal, eth_sender_dal::EthSenderDal, + events_dal::EventsDal, events_web3_dal::EventsWeb3Dal, + fri_gpu_prover_queue_dal::FriGpuProverQueueDal, + fri_proof_compressor_dal::FriProofCompressorDal, + fri_protocol_versions_dal::FriProtocolVersionsDal, fri_prover_dal::FriProverDal, + fri_scheduler_dependency_tracker_dal::FriSchedulerDependencyTrackerDal, + fri_witness_generator_dal::FriWitnessGeneratorDal, gpu_prover_queue_dal::GpuProverQueueDal, + proof_generation_dal::ProofGenerationDal, protocol_versions_dal::ProtocolVersionsDal, + protocol_versions_web3_dal::ProtocolVersionsWeb3Dal, prover_dal::ProverDal, + storage_dal::StorageDal, storage_logs_dal::StorageLogsDal, + storage_logs_dedup_dal::StorageLogsDedupDal, storage_web3_dal::StorageWeb3Dal, + sync_dal::SyncDal, system_dal::SystemDal, tokens_dal::TokensDal, + tokens_web3_dal::TokensWeb3Dal, transactions_dal::TransactionsDal, + transactions_web3_dal::TransactionsWeb3Dal, witness_generator_dal::WitnessGeneratorDal, +}; #[macro_use] mod macro_utils; diff --git a/core/lib/dal/src/metrics.rs b/core/lib/dal/src/metrics.rs index 58e733acc90d..4840d073f577 100644 --- a/core/lib/dal/src/metrics.rs +++ b/core/lib/dal/src/metrics.rs @@ -1,12 +1,12 @@ //! Metrics for the data access layer. +use std::{thread, time::Duration}; + use vise::{ Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Histogram, LabeledFamily, LatencyObserver, Metrics, }; -use std::{thread, time::Duration}; - /// Request-related DB metrics. #[derive(Debug, Metrics)] #[metrics(prefix = "sql")] diff --git a/core/lib/dal/src/models/storage_block.rs b/core/lib/dal/src/models/storage_block.rs index 390bd3b2fd87..5d3eeba2a681 100644 --- a/core/lib/dal/src/models/storage_block.rs +++ b/core/lib/dal/src/models/storage_block.rs @@ -7,7 +7,6 @@ use sqlx::{ types::chrono::{DateTime, NaiveDateTime, Utc}, }; use thiserror::Error; - use zksync_contracts::BaseSystemContractsHashes; use zksync_types::{ api, diff --git a/core/lib/dal/src/models/storage_eth_tx.rs b/core/lib/dal/src/models/storage_eth_tx.rs index ed5a732ff792..9026be8326d0 100644 --- a/core/lib/dal/src/models/storage_eth_tx.rs +++ b/core/lib/dal/src/models/storage_eth_tx.rs @@ -1,8 +1,11 @@ -use sqlx::types::chrono::NaiveDateTime; use std::str::FromStr; -use zksync_types::aggregated_operations::AggregatedActionType; -use zksync_types::eth_sender::{EthTx, TxHistory, TxHistoryToSend}; -use zksync_types::{Address, L1BatchNumber, Nonce, H256}; + +use sqlx::types::chrono::NaiveDateTime; +use zksync_types::{ + aggregated_operations::AggregatedActionType, + eth_sender::{EthTx, TxHistory, TxHistoryToSend}, + Address, L1BatchNumber, Nonce, H256, +}; #[derive(Debug, Clone)] pub struct StorageEthTx { diff --git a/core/lib/dal/src/models/storage_protocol_version.rs b/core/lib/dal/src/models/storage_protocol_version.rs index 93010f1b8147..6eb6e94b003e 100644 --- a/core/lib/dal/src/models/storage_protocol_version.rs +++ b/core/lib/dal/src/models/storage_protocol_version.rs @@ -1,4 +1,6 @@ use std::convert::TryInto; + +use sqlx::types::chrono::NaiveDateTime; use zksync_contracts::BaseSystemContractsHashes; use zksync_types::{ api, @@ -6,8 +8,6 @@ use zksync_types::{ Address, H256, }; -use sqlx::types::chrono::NaiveDateTime; - #[derive(sqlx::FromRow)] pub struct StorageProtocolVersion { pub id: i32, diff --git a/core/lib/dal/src/models/storage_prover_job_info.rs b/core/lib/dal/src/models/storage_prover_job_info.rs index facec83e0c2d..3242953b39dd 100644 --- a/core/lib/dal/src/models/storage_prover_job_info.rs +++ b/core/lib/dal/src/models/storage_prover_job_info.rs @@ -1,14 +1,11 @@ -use core::panic; -use sqlx::types::chrono::{DateTime, NaiveDateTime, NaiveTime, Utc}; -use std::convert::TryFrom; -use std::str::FromStr; +use std::{convert::TryFrom, panic, str::FromStr}; -use zksync_types::proofs::{ - JobPosition, ProverJobStatus, ProverJobStatusFailed, ProverJobStatusInProgress, - ProverJobStatusSuccessful, -}; +use sqlx::types::chrono::{DateTime, NaiveDateTime, NaiveTime, Utc}; use zksync_types::{ - proofs::{AggregationRound, ProverJobInfo}, + proofs::{ + AggregationRound, JobPosition, ProverJobInfo, ProverJobStatus, ProverJobStatusFailed, + ProverJobStatusInProgress, ProverJobStatusSuccessful, + }, L1BatchNumber, }; diff --git a/core/lib/dal/src/models/storage_sync.rs b/core/lib/dal/src/models/storage_sync.rs index 3415cb9b264e..dc15250671bd 100644 --- a/core/lib/dal/src/models/storage_sync.rs +++ b/core/lib/dal/src/models/storage_sync.rs @@ -2,8 +2,7 @@ use anyhow::Context as _; use zksync_consensus_roles::validator; use zksync_contracts::BaseSystemContractsHashes; use zksync_protobuf::{read_required, ProtoFmt}; -use zksync_types::api::en; -use zksync_types::{Address, L1BatchNumber, MiniblockNumber, Transaction, H160, H256}; +use zksync_types::{api::en, Address, L1BatchNumber, MiniblockNumber, Transaction, H160, H256}; #[derive(Debug, Clone, sqlx::FromRow)] pub struct StorageSyncBlock { @@ -133,10 +132,11 @@ impl ProtoFmt for ConsensusBlockFields { #[cfg(test)] mod tests { - use super::ConsensusBlockFields; use rand::Rng; use zksync_consensus_roles::validator; + use super::ConsensusBlockFields; + #[tokio::test] async fn encode_decode() { let rng = &mut rand::thread_rng(); diff --git a/core/lib/dal/src/models/storage_token.rs b/core/lib/dal/src/models/storage_token.rs index 1cc42405fe2f..3acd7e03bc97 100644 --- a/core/lib/dal/src/models/storage_token.rs +++ b/core/lib/dal/src/models/storage_token.rs @@ -2,7 +2,6 @@ use sqlx::types::{ chrono::{DateTime, NaiveDateTime, Utc}, BigDecimal, }; - use zksync_types::tokens::TokenPrice; use zksync_utils::big_decimal_to_ratio; diff --git a/core/lib/dal/src/models/storage_transaction.rs b/core/lib/dal/src/models/storage_transaction.rs index 40fd5aa692c4..8e03590dcc59 100644 --- a/core/lib/dal/src/models/storage_transaction.rs +++ b/core/lib/dal/src/models/storage_transaction.rs @@ -1,29 +1,30 @@ use std::{convert::TryInto, str::FromStr}; -use crate::BigDecimal; use bigdecimal::Zero; - use serde::{Deserialize, Serialize}; -use sqlx::postgres::PgRow; -use sqlx::types::chrono::{DateTime, NaiveDateTime, Utc}; -use sqlx::{Error, FromRow, Row}; - -use zksync_types::l2::TransactionType; -use zksync_types::protocol_version::ProtocolUpgradeTxCommonData; -use zksync_types::transaction_request::PaymasterParams; -use zksync_types::vm_trace::Call; -use zksync_types::web3::types::U64; -use zksync_types::{api, Bytes, ExecuteTransactionCommon}; +use sqlx::{ + postgres::PgRow, + types::chrono::{DateTime, NaiveDateTime, Utc}, + Error, FromRow, Row, +}; use zksync_types::{ + api, api::{TransactionDetails, TransactionStatus}, fee::Fee, l1::{OpProcessingType, PriorityQueueType}, - Address, Execute, L1TxCommonData, L2ChainId, L2TxCommonData, Nonce, PackedEthSignature, - PriorityOpId, Transaction, EIP_1559_TX_TYPE, EIP_2930_TX_TYPE, EIP_712_TX_TYPE, H160, H256, - PRIORITY_OPERATION_L2_TX_TYPE, PROTOCOL_UPGRADE_TX_TYPE, U256, + l2::TransactionType, + protocol_version::ProtocolUpgradeTxCommonData, + transaction_request::PaymasterParams, + vm_trace::Call, + web3::types::U64, + Address, Bytes, Execute, ExecuteTransactionCommon, L1TxCommonData, L2ChainId, L2TxCommonData, + Nonce, PackedEthSignature, PriorityOpId, Transaction, EIP_1559_TX_TYPE, EIP_2930_TX_TYPE, + EIP_712_TX_TYPE, H160, H256, PRIORITY_OPERATION_L2_TX_TYPE, PROTOCOL_UPGRADE_TX_TYPE, U256, }; use zksync_utils::bigdecimal_to_u256; +use crate::BigDecimal; + #[derive(Debug, Clone, sqlx::FromRow)] pub struct StorageTransaction { pub priority_op_id: Option, diff --git a/core/lib/dal/src/models/storage_verification_request.rs b/core/lib/dal/src/models/storage_verification_request.rs index 47e9abd11db3..e6c68ca16fd9 100644 --- a/core/lib/dal/src/models/storage_verification_request.rs +++ b/core/lib/dal/src/models/storage_verification_request.rs @@ -1,8 +1,10 @@ -use zksync_types::contract_verification_api::{ - CompilerType, CompilerVersions, SourceCodeData, VerificationIncomingRequest, - VerificationRequest, +use zksync_types::{ + contract_verification_api::{ + CompilerType, CompilerVersions, SourceCodeData, VerificationIncomingRequest, + VerificationRequest, + }, + Address, }; -use zksync_types::Address; #[derive(Debug, Clone, sqlx::FromRow)] pub struct StorageVerificationRequest { diff --git a/core/lib/dal/src/models/storage_witness_job_info.rs b/core/lib/dal/src/models/storage_witness_job_info.rs index 1aa41032cfa0..486b9f89681b 100644 --- a/core/lib/dal/src/models/storage_witness_job_info.rs +++ b/core/lib/dal/src/models/storage_witness_job_info.rs @@ -1,11 +1,13 @@ +use std::{convert::TryFrom, str::FromStr}; + use sqlx::types::chrono::{DateTime, NaiveDateTime, NaiveTime, Utc}; -use std::convert::TryFrom; -use std::str::FromStr; -use zksync_types::proofs::{ - AggregationRound, JobPosition, WitnessJobInfo, WitnessJobStatus, WitnessJobStatusFailed, - WitnessJobStatusSuccessful, +use zksync_types::{ + proofs::{ + AggregationRound, JobPosition, WitnessJobInfo, WitnessJobStatus, WitnessJobStatusFailed, + WitnessJobStatusSuccessful, + }, + L1BatchNumber, }; -use zksync_types::L1BatchNumber; #[derive(sqlx::FromRow)] pub struct StorageWitnessJobInfo { diff --git a/core/lib/dal/src/proof_generation_dal.rs b/core/lib/dal/src/proof_generation_dal.rs index 22db44634698..684a3b08acb3 100644 --- a/core/lib/dal/src/proof_generation_dal.rs +++ b/core/lib/dal/src/proof_generation_dal.rs @@ -1,10 +1,9 @@ use std::time::Duration; +use strum::{Display, EnumString}; use zksync_types::L1BatchNumber; -use crate::time_utils::pg_interval_from_duration; -use crate::{SqlxError, StorageProcessor}; -use strum::{Display, EnumString}; +use crate::{time_utils::pg_interval_from_duration, SqlxError, StorageProcessor}; #[derive(Debug)] pub struct ProofGenerationDal<'a, 'c> { diff --git a/core/lib/dal/src/protocol_versions_dal.rs b/core/lib/dal/src/protocol_versions_dal.rs index dde7574d3905..6f62f3fb01b8 100644 --- a/core/lib/dal/src/protocol_versions_dal.rs +++ b/core/lib/dal/src/protocol_versions_dal.rs @@ -1,14 +1,15 @@ use std::convert::{TryFrom, TryInto}; + use zksync_contracts::{BaseSystemContracts, BaseSystemContractsHashes}; use zksync_types::{ protocol_version::{L1VerifierConfig, ProtocolUpgradeTx, ProtocolVersion, VerifierParams}, Address, ProtocolVersionId, H256, }; -use crate::models::storage_protocol_version::{ - protocol_version_from_storage, StorageProtocolVersion, +use crate::{ + models::storage_protocol_version::{protocol_version_from_storage, StorageProtocolVersion}, + StorageProcessor, }; -use crate::StorageProcessor; #[derive(Debug)] pub struct ProtocolVersionsDal<'a, 'c> { diff --git a/core/lib/dal/src/protocol_versions_web3_dal.rs b/core/lib/dal/src/protocol_versions_web3_dal.rs index dc43dadbd22a..2819d94f8d81 100644 --- a/core/lib/dal/src/protocol_versions_web3_dal.rs +++ b/core/lib/dal/src/protocol_versions_web3_dal.rs @@ -1,7 +1,6 @@ use zksync_types::api::ProtocolVersion; -use crate::models::storage_protocol_version::StorageProtocolVersion; -use crate::StorageProcessor; +use crate::{models::storage_protocol_version::StorageProtocolVersion, StorageProcessor}; #[derive(Debug)] pub struct ProtocolVersionsWeb3Dal<'a, 'c> { diff --git a/core/lib/dal/src/prover_dal.rs b/core/lib/dal/src/prover_dal.rs index d84d0628372b..ea6eba5eda0f 100644 --- a/core/lib/dal/src/prover_dal.rs +++ b/core/lib/dal/src/prover_dal.rs @@ -1,5 +1,3 @@ -use sqlx::Error; - use std::{ collections::HashMap, convert::{TryFrom, TryInto}, @@ -7,6 +5,7 @@ use std::{ time::Duration, }; +use sqlx::Error; use zksync_types::{ aggregated_operations::L1BatchProofForL1, proofs::{ diff --git a/core/lib/dal/src/storage_dal.rs b/core/lib/dal/src/storage_dal.rs index 8ec6d9164938..4512d028488f 100644 --- a/core/lib/dal/src/storage_dal.rs +++ b/core/lib/dal/src/storage_dal.rs @@ -1,7 +1,6 @@ -use itertools::Itertools; - use std::collections::{HashMap, HashSet}; +use itertools::Itertools; use zksync_contracts::{BaseSystemContracts, SystemContractCode}; use zksync_types::{MiniblockNumber, StorageKey, StorageLog, StorageValue, H256, U256}; use zksync_utils::{bytes_to_be_words, bytes_to_chunks}; @@ -210,9 +209,10 @@ impl StorageDal<'_, '_> { #[cfg(test)] mod tests { + use zksync_types::{AccountTreeId, Address}; + use super::*; use crate::ConnectionPool; - use zksync_types::{AccountTreeId, Address}; #[tokio::test] async fn applying_storage_logs() { diff --git a/core/lib/dal/src/storage_logs_dal.rs b/core/lib/dal/src/storage_logs_dal.rs index c368e5adc8da..dc23d29af5c8 100644 --- a/core/lib/dal/src/storage_logs_dal.rs +++ b/core/lib/dal/src/storage_logs_dal.rs @@ -1,14 +1,13 @@ -use sqlx::types::chrono::Utc; -use sqlx::Row; - use std::{collections::HashMap, time::Instant}; -use crate::{instrument::InstrumentExt, StorageProcessor}; +use sqlx::{types::chrono::Utc, Row}; use zksync_types::{ get_code_key, AccountTreeId, Address, L1BatchNumber, MiniblockNumber, StorageKey, StorageLog, FAILED_CONTRACT_DEPLOYMENT_BYTECODE_HASH, H256, }; +use crate::{instrument::InstrumentExt, StorageProcessor}; + #[derive(Debug)] pub struct StorageLogsDal<'a, 'c> { pub(crate) storage: &'a mut StorageProcessor<'c>, @@ -529,14 +528,15 @@ impl StorageLogsDal<'_, '_> { #[cfg(test)] mod tests { - use super::*; - use crate::{tests::create_miniblock_header, ConnectionPool}; use zksync_contracts::BaseSystemContractsHashes; use zksync_types::{ block::{BlockGasCount, L1BatchHeader}, ProtocolVersion, ProtocolVersionId, }; + use super::*; + use crate::{tests::create_miniblock_header, ConnectionPool}; + async fn insert_miniblock(conn: &mut StorageProcessor<'_>, number: u32, logs: Vec) { let mut header = L1BatchHeader::new( L1BatchNumber(number), diff --git a/core/lib/dal/src/storage_logs_dedup_dal.rs b/core/lib/dal/src/storage_logs_dedup_dal.rs index 8a70ceb50fe6..25e0a8f6eefd 100644 --- a/core/lib/dal/src/storage_logs_dedup_dal.rs +++ b/core/lib/dal/src/storage_logs_dedup_dal.rs @@ -1,9 +1,11 @@ -use crate::StorageProcessor; -use sqlx::types::chrono::Utc; use std::collections::HashSet; + +use sqlx::types::chrono::Utc; use zksync_types::{AccountTreeId, Address, L1BatchNumber, LogQuery, StorageKey, H256}; use zksync_utils::u256_to_h256; +use crate::StorageProcessor; + #[derive(Debug)] pub struct StorageLogsDedupDal<'a, 'c> { pub(crate) storage: &'a mut StorageProcessor<'c>, diff --git a/core/lib/dal/src/tests/mod.rs b/core/lib/dal/src/tests/mod.rs index c383ea7f9441..94fb6e9ebf64 100644 --- a/core/lib/dal/src/tests/mod.rs +++ b/core/lib/dal/src/tests/mod.rs @@ -1,5 +1,4 @@ -use std::fs; -use std::time::Duration; +use std::{fs, time::Duration}; use zksync_contracts::BaseSystemContractsHashes; use zksync_types::{ @@ -14,14 +13,15 @@ use zksync_types::{ PriorityOpId, ProtocolVersion, ProtocolVersionId, H160, H256, MAX_GAS_PER_PUBDATA_BYTE, U256, }; -use crate::blocks_dal::BlocksDal; -use crate::connection::ConnectionPool; -use crate::protocol_versions_dal::ProtocolVersionsDal; -use crate::prover_dal::{GetProverJobsParams, ProverDal}; -use crate::transactions_dal::L2TxSubmissionResult; -use crate::transactions_dal::TransactionsDal; -use crate::transactions_web3_dal::TransactionsWeb3Dal; -use crate::witness_generator_dal::WitnessGeneratorDal; +use crate::{ + blocks_dal::BlocksDal, + connection::ConnectionPool, + protocol_versions_dal::ProtocolVersionsDal, + prover_dal::{GetProverJobsParams, ProverDal}, + transactions_dal::{L2TxSubmissionResult, TransactionsDal}, + transactions_web3_dal::TransactionsWeb3Dal, + witness_generator_dal::WitnessGeneratorDal, +}; const DEFAULT_GAS_PER_PUBDATA: u32 = 100; diff --git a/core/lib/dal/src/time_utils.rs b/core/lib/dal/src/time_utils.rs index 45ff661a319f..0ede5e6fc576 100644 --- a/core/lib/dal/src/time_utils.rs +++ b/core/lib/dal/src/time_utils.rs @@ -1,7 +1,7 @@ -use sqlx::postgres::types::PgInterval; -use sqlx::types::chrono::NaiveTime; use std::time::Duration; +use sqlx::{postgres::types::PgInterval, types::chrono::NaiveTime}; + pub fn duration_to_naive_time(duration: Duration) -> NaiveTime { let total_seconds = duration.as_secs() as u32; NaiveTime::from_hms_opt( diff --git a/core/lib/dal/src/tokens_dal.rs b/core/lib/dal/src/tokens_dal.rs index f7b64aed69ea..5c0f306cc05d 100644 --- a/core/lib/dal/src/tokens_dal.rs +++ b/core/lib/dal/src/tokens_dal.rs @@ -1,4 +1,3 @@ -use crate::StorageProcessor; use num::{rational::Ratio, BigUint}; use sqlx::types::chrono::Utc; use zksync_types::{ @@ -8,6 +7,8 @@ use zksync_types::{ }; use zksync_utils::ratio_to_big_decimal; +use crate::StorageProcessor; + // Precision of the USD price per token pub(crate) const STORED_USD_PRICE_PRECISION: usize = 6; diff --git a/core/lib/dal/src/tokens_web3_dal.rs b/core/lib/dal/src/tokens_web3_dal.rs index aa3674b6c3d8..753f57c85c63 100644 --- a/core/lib/dal/src/tokens_web3_dal.rs +++ b/core/lib/dal/src/tokens_web3_dal.rs @@ -1,11 +1,10 @@ -use crate::models::storage_token::StorageTokenPrice; -use crate::SqlxError; -use crate::StorageProcessor; use zksync_types::{ tokens::{TokenInfo, TokenMetadata, TokenPrice}, Address, }; +use crate::{models::storage_token::StorageTokenPrice, SqlxError, StorageProcessor}; + #[derive(Debug)] pub struct TokensWeb3Dal<'a, 'c> { pub(crate) storage: &'a mut StorageProcessor<'c>, diff --git a/core/lib/dal/src/transactions_dal.rs b/core/lib/dal/src/transactions_dal.rs index cbca986b16c0..78da3e0fc045 100644 --- a/core/lib/dal/src/transactions_dal.rs +++ b/core/lib/dal/src/transactions_dal.rs @@ -1,10 +1,9 @@ +use std::{collections::HashMap, fmt, time::Duration}; + +use anyhow::Context; use bigdecimal::BigDecimal; use itertools::Itertools; use sqlx::{error, types::chrono::NaiveDateTime}; - -use anyhow::Context; -use std::{collections::HashMap, fmt, time::Duration}; - use zksync_types::{ block::MiniblockExecutionData, fee::TransactionExecutionMetrics, diff --git a/core/lib/dal/src/transactions_web3_dal.rs b/core/lib/dal/src/transactions_web3_dal.rs index 5e2342d05b75..d87ddc9a5174 100644 --- a/core/lib/dal/src/transactions_web3_dal.rs +++ b/core/lib/dal/src/transactions_web3_dal.rs @@ -1,20 +1,22 @@ use sqlx::types::chrono::NaiveDateTime; - use zksync_types::{ api, Address, L2ChainId, MiniblockNumber, Transaction, ACCOUNT_CODE_STORAGE_ADDRESS, FAILED_CONTRACT_DEPLOYMENT_BYTECODE_HASH, H160, H256, U256, U64, }; use zksync_utils::{bigdecimal_to_u256, h256_to_account_address}; -use crate::models::{ - storage_block::{bind_block_where_sql_params, web3_block_where_sql}, - storage_event::StorageWeb3Log, - storage_transaction::{ - extract_web3_transaction, web3_transaction_select_sql, StorageTransaction, - StorageTransactionDetails, +use crate::{ + instrument::InstrumentExt, + models::{ + storage_block::{bind_block_where_sql_params, web3_block_where_sql}, + storage_event::StorageWeb3Log, + storage_transaction::{ + extract_web3_transaction, web3_transaction_select_sql, StorageTransaction, + StorageTransactionDetails, + }, }, + SqlxError, StorageProcessor, }; -use crate::{instrument::InstrumentExt, SqlxError, StorageProcessor}; #[derive(Debug)] pub struct TransactionsWeb3Dal<'a, 'c> { diff --git a/core/lib/dal/src/witness_generator_dal.rs b/core/lib/dal/src/witness_generator_dal.rs index a8079a9dccee..b437c2ad34f5 100644 --- a/core/lib/dal/src/witness_generator_dal.rs +++ b/core/lib/dal/src/witness_generator_dal.rs @@ -1,17 +1,16 @@ -use itertools::Itertools; -use sqlx::Row; - use std::{collections::HashMap, ops::Range, time::Duration}; -use zksync_types::proofs::{ - AggregationRound, JobCountStatistics, WitnessGeneratorJobMetadata, WitnessJobInfo, +use itertools::Itertools; +use sqlx::Row; +use zksync_types::{ + proofs::{AggregationRound, JobCountStatistics, WitnessGeneratorJobMetadata, WitnessJobInfo}, + zkevm_test_harness::{ + abstract_zksync_circuit::concrete_circuits::{ZkSyncCircuit, ZkSyncProof}, + bellman::{bn256::Bn256, plonk::better_better_cs::proof::Proof}, + witness::oracle::VmWitnessOracle, + }, + L1BatchNumber, ProtocolVersionId, }; -use zksync_types::zkevm_test_harness::abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit; -use zksync_types::zkevm_test_harness::abstract_zksync_circuit::concrete_circuits::ZkSyncProof; -use zksync_types::zkevm_test_harness::bellman::bn256::Bn256; -use zksync_types::zkevm_test_harness::bellman::plonk::better_better_cs::proof::Proof; -use zksync_types::zkevm_test_harness::witness::oracle::VmWitnessOracle; -use zksync_types::{L1BatchNumber, ProtocolVersionId}; use crate::{ instrument::InstrumentExt, diff --git a/core/lib/env_config/src/alerts.rs b/core/lib/env_config/src/alerts.rs index c72b23bbd9fa..63cbde48bdf9 100644 --- a/core/lib/env_config/src/alerts.rs +++ b/core/lib/env_config/src/alerts.rs @@ -1,6 +1,7 @@ -use crate::{envy_load, FromEnv}; use zksync_config::configs::AlertsConfig; +use crate::{envy_load, FromEnv}; + impl FromEnv for AlertsConfig { fn from_env() -> anyhow::Result { envy_load("sporadic_crypto_errors_substrs", "ALERTS_") diff --git a/core/lib/env_config/src/api.rs b/core/lib/env_config/src/api.rs index 20ecfe41e21b..d256e27ceca6 100644 --- a/core/lib/env_config/src/api.rs +++ b/core/lib/env_config/src/api.rs @@ -1,6 +1,4 @@ use anyhow::Context as _; - -use crate::{envy_load, FromEnv}; use zksync_config::configs::{ api::{ ContractVerificationApiConfig, HealthCheckConfig, MerkleTreeApiConfig, Web3JsonRpcConfig, @@ -8,6 +6,8 @@ use zksync_config::configs::{ ApiConfig, PrometheusConfig, }; +use crate::{envy_load, FromEnv}; + impl FromEnv for ApiConfig { fn from_env() -> anyhow::Result { Ok(Self { diff --git a/core/lib/env_config/src/chain.rs b/core/lib/env_config/src/chain.rs index e64ba3c36b89..7c2aa7e59419 100644 --- a/core/lib/env_config/src/chain.rs +++ b/core/lib/env_config/src/chain.rs @@ -1,10 +1,11 @@ -use crate::{envy_load, FromEnv}; use anyhow::Context as _; use zksync_config::configs::chain::{ ChainConfig, CircuitBreakerConfig, MempoolConfig, NetworkConfig, OperationsManagerConfig, StateKeeperConfig, }; +use crate::{envy_load, FromEnv}; + impl FromEnv for ChainConfig { fn from_env() -> anyhow::Result { Ok(Self { diff --git a/core/lib/env_config/src/contracts.rs b/core/lib/env_config/src/contracts.rs index 8c58483db064..537b68414c63 100644 --- a/core/lib/env_config/src/contracts.rs +++ b/core/lib/env_config/src/contracts.rs @@ -10,9 +10,10 @@ impl FromEnv for ContractsConfig { #[cfg(test)] mod tests { + use zksync_config::configs::contracts::ProverAtGenesis; + use super::*; use crate::test_utils::{addr, hash, EnvMutex}; - use zksync_config::configs::contracts::ProverAtGenesis; static MUTEX: EnvMutex = EnvMutex::new(); diff --git a/core/lib/env_config/src/database.rs b/core/lib/env_config/src/database.rs index 939725d6773e..e350c487662a 100644 --- a/core/lib/env_config/src/database.rs +++ b/core/lib/env_config/src/database.rs @@ -1,5 +1,6 @@ -use anyhow::Context as _; use std::env; + +use anyhow::Context as _; use zksync_config::{DBConfig, PostgresConfig}; use crate::{envy_load, FromEnv}; diff --git a/core/lib/env_config/src/fri_proof_compressor.rs b/core/lib/env_config/src/fri_proof_compressor.rs index 2594433025e4..777bdb03c58c 100644 --- a/core/lib/env_config/src/fri_proof_compressor.rs +++ b/core/lib/env_config/src/fri_proof_compressor.rs @@ -10,9 +10,8 @@ impl FromEnv for FriProofCompressorConfig { #[cfg(test)] mod tests { - use crate::test_utils::EnvMutex; - use super::*; + use crate::test_utils::EnvMutex; static MUTEX: EnvMutex = EnvMutex::new(); diff --git a/core/lib/env_config/src/test_utils.rs b/core/lib/env_config/src/test_utils.rs index 013d12493ae4..2909071df394 100644 --- a/core/lib/env_config/src/test_utils.rs +++ b/core/lib/env_config/src/test_utils.rs @@ -1,4 +1,3 @@ -// Built-in uses. use std::{ collections::HashMap, env, @@ -6,7 +5,7 @@ use std::{ mem, sync::{Mutex, MutexGuard, PoisonError}, }; -// Workspace uses + use zksync_basic_types::{Address, H256}; /// Mutex that allows to modify certain env variables and roll them back to initial values when diff --git a/core/lib/env_config/src/utils.rs b/core/lib/env_config/src/utils.rs index 655d3b2e6d56..211e73ae2b17 100644 --- a/core/lib/env_config/src/utils.rs +++ b/core/lib/env_config/src/utils.rs @@ -1,6 +1,7 @@ -use crate::{envy_load, FromEnv}; use zksync_config::configs::PrometheusConfig; +use crate::{envy_load, FromEnv}; + impl FromEnv for PrometheusConfig { fn from_env() -> anyhow::Result { envy_load("prometheus", "API_PROMETHEUS_") diff --git a/core/lib/eth_client/src/clients/http/mod.rs b/core/lib/eth_client/src/clients/http/mod.rs index 5d94a383171c..e3295ee4b76d 100644 --- a/core/lib/eth_client/src/clients/http/mod.rs +++ b/core/lib/eth_client/src/clients/http/mod.rs @@ -1,17 +1,17 @@ +use std::time::Duration; + use vise::{ Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Histogram, LabeledFamily, Metrics, }; -use std::time::Duration; - -mod query; -mod signing; - pub use self::{ query::QueryClient, signing::{PKSigningClient, SigningClient}, }; +mod query; +mod signing; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelValue, EncodeLabelSet)] #[metrics(label = "method", rename_all = "snake_case")] enum Method { diff --git a/core/lib/eth_client/src/clients/http/query.rs b/core/lib/eth_client/src/clients/http/query.rs index 0094c76f88a6..3e88944ca0e4 100644 --- a/core/lib/eth_client/src/clients/http/query.rs +++ b/core/lib/eth_client/src/clients/http/query.rs @@ -1,12 +1,6 @@ -use async_trait::async_trait; - use std::sync::Arc; -use crate::{ - clients::http::{Method, COUNTERS, LATENCIES}, - types::{Error, ExecutedTxStatus, FailureInfo}, - EthInterface, -}; +use async_trait::async_trait; use zksync_types::web3::{ self, contract::{ @@ -22,6 +16,12 @@ use zksync_types::web3::{ Web3, }; +use crate::{ + clients::http::{Method, COUNTERS, LATENCIES}, + types::{Error, ExecutedTxStatus, FailureInfo}, + EthInterface, +}; + /// An "anonymous" Ethereum client that can invoke read-only methods that aren't /// tied to a particular account. #[derive(Debug, Clone)] diff --git a/core/lib/eth_client/src/clients/http/signing.rs b/core/lib/eth_client/src/clients/http/signing.rs index a0a6647db5fd..8b56dc1cfbdf 100644 --- a/core/lib/eth_client/src/clients/http/signing.rs +++ b/core/lib/eth_client/src/clients/http/signing.rs @@ -1,24 +1,25 @@ -use async_trait::async_trait; - use std::{fmt, sync::Arc}; +use async_trait::async_trait; use zksync_config::{ContractsConfig, ETHClientConfig, ETHSenderConfig}; use zksync_contracts::zksync_contract; use zksync_eth_signer::{raw_ethereum_tx::TransactionParameters, EthereumSigner, PrivateKeySigner}; -use zksync_types::web3::{ - self, - contract::{ - tokens::{Detokenize, Tokenize}, - Options, - }, - ethabi, - transports::Http, - types::{ - Address, Block, BlockId, BlockNumber, Filter, Log, Transaction, TransactionReceipt, H160, - H256, U256, U64, +use zksync_types::{ + web3::{ + self, + contract::{ + tokens::{Detokenize, Tokenize}, + Options, + }, + ethabi, + transports::Http, + types::{ + Address, Block, BlockId, BlockNumber, Filter, Log, Transaction, TransactionReceipt, + H160, H256, U256, U64, + }, }, + L1ChainId, PackedEthSignature, EIP_1559_TX_TYPE, }; -use zksync_types::{L1ChainId, PackedEthSignature, EIP_1559_TX_TYPE}; use super::{query::QueryClient, Method, LATENCIES}; use crate::{ diff --git a/core/lib/eth_client/src/clients/mock.rs b/core/lib/eth_client/src/clients/mock.rs index 576fbac21a70..a8eceac75aff 100644 --- a/core/lib/eth_client/src/clients/mock.rs +++ b/core/lib/eth_client/src/clients/mock.rs @@ -1,9 +1,13 @@ -use std::sync::atomic::{AtomicU64, Ordering}; +use std::{ + collections::{BTreeMap, HashMap}, + sync::{ + atomic::{AtomicU64, Ordering}, + RwLock, + }, +}; use async_trait::async_trait; use jsonrpc_core::types::error::Error as RpcError; -use std::collections::{BTreeMap, HashMap}; -use std::sync::RwLock; use zksync_types::{ web3::{ contract::{ @@ -92,8 +96,7 @@ impl MockEthereum { /// A fake `sha256` hasher, which calculates an `std::hash` instead. /// This is done for simplicity and it's also much faster. pub fn fake_sha256(data: &[u8]) -> H256 { - use std::collections::hash_map::DefaultHasher; - use std::hash::Hasher; + use std::{collections::hash_map::DefaultHasher, hash::Hasher}; let mut hasher = DefaultHasher::new(); hasher.write(data); diff --git a/core/lib/eth_client/src/lib.rs b/core/lib/eth_client/src/lib.rs index f61814893bb4..5bb40f60a083 100644 --- a/core/lib/eth_client/src/lib.rs +++ b/core/lib/eth_client/src/lib.rs @@ -1,9 +1,5 @@ #![allow(clippy::upper_case_acronyms, clippy::derive_partial_eq_without_eq)] -pub mod clients; -pub mod types; - -use crate::types::{Error, ExecutedTxStatus, FailureInfo, SignedCallResult}; use async_trait::async_trait; use zksync_types::{ web3::{ @@ -20,6 +16,11 @@ use zksync_types::{ L1ChainId, }; +use crate::types::{Error, ExecutedTxStatus, FailureInfo, SignedCallResult}; + +pub mod clients; +pub mod types; + /// Common Web3 interface, as seen by the core applications. /// Encapsulates the raw Web3 interaction, providing a high-level interface. /// diff --git a/core/lib/eth_signer/src/json_rpc_signer.rs b/core/lib/eth_signer/src/json_rpc_signer.rs index b6619f5e831f..66a7b33e9894 100644 --- a/core/lib/eth_signer/src/json_rpc_signer.rs +++ b/core/lib/eth_signer/src/json_rpc_signer.rs @@ -1,13 +1,15 @@ -use crate::error::{RpcSignerError, SignerError}; -use crate::json_rpc_signer::messages::JsonRpcRequest; -use crate::raw_ethereum_tx::TransactionParameters; -use crate::EthereumSigner; - use jsonrpc_core::types::response::Output; -use zksync_types::tx::primitives::PackedEthSignature; -use zksync_types::{Address, EIP712TypedStructure, Eip712Domain, H256}; - use serde_json::Value; +use zksync_types::{ + tx::primitives::PackedEthSignature, Address, EIP712TypedStructure, Eip712Domain, H256, +}; + +use crate::{ + error::{RpcSignerError, SignerError}, + json_rpc_signer::messages::JsonRpcRequest, + raw_ethereum_tx::TransactionParameters, + EthereumSigner, +}; pub fn is_signature_from_address( signature: &PackedEthSignature, @@ -325,13 +327,14 @@ impl JsonRpcSigner { } mod messages { - use crate::raw_ethereum_tx::TransactionParameters; use hex::encode; use serde::{Deserialize, Serialize}; use zksync_types::{ eip712_signature::utils::get_eip712_json, Address, EIP712TypedStructure, Eip712Domain, }; + use crate::raw_ethereum_tx::TransactionParameters; + #[derive(Debug, Serialize, Deserialize)] pub struct JsonRpcRequest { pub id: String, @@ -429,7 +432,6 @@ mod messages { #[cfg(test)] mod tests { - use crate::raw_ethereum_tx::TransactionParameters; use actix_web::{ post, web::{self, Data}, @@ -439,11 +441,10 @@ mod tests { use jsonrpc_core::{Failure, Id, Output, Success, Version}; use parity_crypto::publickey::{Generator, KeyPair, Random}; use serde_json::json; - use zksync_types::{tx::primitives::PackedEthSignature, Address}; use super::{is_signature_from_address, messages::JsonRpcRequest}; - use crate::{EthereumSigner, JsonRpcSigner}; + use crate::{raw_ethereum_tx::TransactionParameters, EthereumSigner, JsonRpcSigner}; #[post("/")] async fn index(req: web::Json, state: web::Data) -> impl Responder { diff --git a/core/lib/eth_signer/src/lib.rs b/core/lib/eth_signer/src/lib.rs index ce4540c151b7..164a124dbc93 100644 --- a/core/lib/eth_signer/src/lib.rs +++ b/core/lib/eth_signer/src/lib.rs @@ -1,11 +1,12 @@ use async_trait::async_trait; use error::SignerError; -use zksync_types::tx::primitives::PackedEthSignature; -use zksync_types::{Address, EIP712TypedStructure, Eip712Domain}; - -pub use crate::raw_ethereum_tx::TransactionParameters; pub use json_rpc_signer::JsonRpcSigner; pub use pk_signer::PrivateKeySigner; +use zksync_types::{ + tx::primitives::PackedEthSignature, Address, EIP712TypedStructure, Eip712Domain, +}; + +pub use crate::raw_ethereum_tx::TransactionParameters; pub mod error; pub mod json_rpc_signer; diff --git a/core/lib/eth_signer/src/pk_signer.rs b/core/lib/eth_signer/src/pk_signer.rs index 680d87d62d0e..4f9795dca865 100644 --- a/core/lib/eth_signer/src/pk_signer.rs +++ b/core/lib/eth_signer/src/pk_signer.rs @@ -1,11 +1,11 @@ use secp256k1::SecretKey; - -use zksync_types::tx::primitives::PackedEthSignature; -use zksync_types::{Address, EIP712TypedStructure, Eip712Domain, H256}; +use zksync_types::{ + tx::primitives::PackedEthSignature, Address, EIP712TypedStructure, Eip712Domain, H256, +}; use crate::{ raw_ethereum_tx::{Transaction, TransactionParameters}, - {EthereumSigner, SignerError}, + EthereumSigner, SignerError, }; #[derive(Clone)] @@ -86,11 +86,11 @@ impl EthereumSigner for PrivateKeySigner { #[cfg(test)] mod test { - use super::PrivateKeySigner; - use crate::raw_ethereum_tx::TransactionParameters; - use crate::EthereumSigner; use zksync_types::{H160, H256, U256, U64}; + use super::PrivateKeySigner; + use crate::{raw_ethereum_tx::TransactionParameters, EthereumSigner}; + #[tokio::test] async fn test_generating_signed_raw_transaction() { let private_key = H256::from([5; 32]); diff --git a/core/lib/eth_signer/src/raw_ethereum_tx.rs b/core/lib/eth_signer/src/raw_ethereum_tx.rs index fcee13494456..124c09965de4 100644 --- a/core/lib/eth_signer/src/raw_ethereum_tx.rs +++ b/core/lib/eth_signer/src/raw_ethereum_tx.rs @@ -8,12 +8,16 @@ //! We can refactor this code and adapt it for our needs better, but I prefer to reuse as much code as we can. //! In the case where it will be possible to use only the web3 library without copy-paste, the changes will be small and simple //! Link to @Deniallugo's PR to web3: https://github.com/tomusdrw/rust-web3/pull/630 + use rlp::RlpStream; -use zksync_types::web3::{ - signing::{self, Signature}, - types::{AccessList, SignedTransaction}, +use zksync_types::{ + ethabi::Address, + web3::{ + signing::{self, Signature}, + types::{AccessList, SignedTransaction}, + }, + U256, U64, }; -use zksync_types::{ethabi::Address, U256, U64}; const LEGACY_TX_ID: u64 = 0; const ACCESSLISTS_TX_ID: u64 = 1; diff --git a/core/lib/health_check/src/lib.rs b/core/lib/health_check/src/lib.rs index fac8ec46dbbb..12bb292bc850 100644 --- a/core/lib/health_check/src/lib.rs +++ b/core/lib/health_check/src/lib.rs @@ -1,11 +1,10 @@ -use futures::{future, FutureExt}; -use serde::Serialize; -use tokio::sync::watch; - use std::{collections::HashMap, thread}; -/// Public re-export for other crates to be able to implement the interface. +// Public re-export for other crates to be able to implement the interface. pub use async_trait::async_trait; +use futures::{future, FutureExt}; +use serde::Serialize; +use tokio::sync::watch; /// Health status returned as a part of `Health`. #[derive(Debug, Clone, Copy, PartialEq, Serialize)] diff --git a/core/lib/mempool/src/mempool_store.rs b/core/lib/mempool/src/mempool_store.rs index a8b02bee0cb8..51a8d708a740 100644 --- a/core/lib/mempool/src/mempool_store.rs +++ b/core/lib/mempool/src/mempool_store.rs @@ -1,10 +1,11 @@ use std::collections::{hash_map, BTreeSet, HashMap, HashSet}; -use crate::types::{AccountTransactions, L2TxFilter, MempoolScore}; use zksync_types::{ l1::L1Tx, l2::L2Tx, Address, ExecuteTransactionCommon, Nonce, PriorityOpId, Transaction, }; +use crate::types::{AccountTransactions, L2TxFilter, MempoolScore}; + #[derive(Debug)] pub struct MempoolInfo { pub stashed_accounts: Vec
, diff --git a/core/lib/mempool/src/tests.rs b/core/lib/mempool/src/tests.rs index cb149752e2dc..cd595509ec56 100644 --- a/core/lib/mempool/src/tests.rs +++ b/core/lib/mempool/src/tests.rs @@ -1,12 +1,18 @@ +use std::{ + collections::{HashMap, HashSet}, + iter::FromIterator, +}; + +use zksync_types::{ + fee::Fee, + helpers::unix_timestamp_ms, + l1::{OpProcessingType, PriorityQueueType}, + l2::L2Tx, + Address, Execute, ExecuteTransactionCommon, L1TxCommonData, Nonce, PriorityOpId, Transaction, + H256, U256, +}; + use crate::{mempool_store::MempoolStore, types::L2TxFilter}; -use std::collections::{HashMap, HashSet}; -use std::iter::FromIterator; -use zksync_types::fee::Fee; -use zksync_types::helpers::unix_timestamp_ms; -use zksync_types::l1::{OpProcessingType, PriorityQueueType}; -use zksync_types::l2::L2Tx; -use zksync_types::{Address, ExecuteTransactionCommon, L1TxCommonData, PriorityOpId, H256, U256}; -use zksync_types::{Execute, Nonce, Transaction}; #[test] fn basic_flow() { diff --git a/core/lib/mempool/src/types.rs b/core/lib/mempool/src/types.rs index 130f8ad0016a..9bc58a4e2cea 100644 --- a/core/lib/mempool/src/types.rs +++ b/core/lib/mempool/src/types.rs @@ -1,8 +1,6 @@ -use std::cmp::Ordering; -use std::collections::HashMap; -use zksync_types::fee::Fee; -use zksync_types::l2::L2Tx; -use zksync_types::{Address, Nonce, Transaction, U256}; +use std::{cmp::Ordering, collections::HashMap}; + +use zksync_types::{fee::Fee, l2::L2Tx, Address, Nonce, Transaction, U256}; /// Pending mempool transactions of account #[derive(Debug)] diff --git a/core/lib/merkle_tree/examples/loadtest/main.rs b/core/lib/merkle_tree/examples/loadtest/main.rs index 527daa87b37a..185ae0543f9d 100644 --- a/core/lib/merkle_tree/examples/loadtest/main.rs +++ b/core/lib/merkle_tree/examples/loadtest/main.rs @@ -3,16 +3,15 @@ //! Should be compiled with the release profile, otherwise hashing and other ops would be //! prohibitively slow. -use clap::Parser; -use rand::{rngs::StdRng, seq::IteratorRandom, SeedableRng}; -use tempfile::TempDir; -use tracing_subscriber::EnvFilter; - use std::{ thread, time::{Duration, Instant}, }; +use clap::Parser; +use rand::{rngs::StdRng, seq::IteratorRandom, SeedableRng}; +use tempfile::TempDir; +use tracing_subscriber::EnvFilter; use zksync_crypto::hasher::blake2::Blake2Hasher; use zksync_merkle_tree::{ Database, HashTree, MerkleTree, MerkleTreePruner, PatchSet, RocksDBWrapper, TreeEntry, @@ -21,10 +20,10 @@ use zksync_merkle_tree::{ use zksync_storage::{RocksDB, RocksDBOptions}; use zksync_types::{AccountTreeId, Address, StorageKey, H256, U256}; -mod batch; - use crate::batch::WithBatching; +mod batch; + /// CLI for load-testing for the Merkle tree implementation. #[derive(Debug, Parser)] #[command(author, version, about, long_about = None)] diff --git a/core/lib/merkle_tree/examples/recovery.rs b/core/lib/merkle_tree/examples/recovery.rs index 1a2aae236ea5..603eb2ec4d7e 100644 --- a/core/lib/merkle_tree/examples/recovery.rs +++ b/core/lib/merkle_tree/examples/recovery.rs @@ -1,12 +1,11 @@ //! Tree recovery load test. +use std::time::Instant; + use clap::Parser; use rand::{rngs::StdRng, Rng, SeedableRng}; use tempfile::TempDir; use tracing_subscriber::EnvFilter; - -use std::time::Instant; - use zksync_crypto::hasher::blake2::Blake2Hasher; use zksync_merkle_tree::{ recovery::MerkleTreeRecovery, HashTree, Key, PatchSet, PruneDatabase, RocksDBWrapper, diff --git a/core/lib/merkle_tree/src/consistency.rs b/core/lib/merkle_tree/src/consistency.rs index 2cc8996e64e9..659befbe048e 100644 --- a/core/lib/merkle_tree/src/consistency.rs +++ b/core/lib/merkle_tree/src/consistency.rs @@ -1,9 +1,9 @@ //! Consistency verification for the Merkle tree. -use rayon::prelude::*; - use std::sync::atomic::{AtomicU64, Ordering}; +use rayon::prelude::*; + use crate::{ errors::DeserializeError, hasher::{HashTree, HasherWithStats}, @@ -267,17 +267,17 @@ impl AtomicBitSet { #[cfg(test)] mod tests { + use std::num::NonZeroU64; + use assert_matches::assert_matches; use rayon::ThreadPoolBuilder; - - use std::num::NonZeroU64; + use zksync_types::{H256, U256}; use super::*; use crate::{ types::{InternalNode, TreeEntry}, PatchSet, }; - use zksync_types::{H256, U256}; const FIRST_KEY: Key = U256([0, 0, 0, 0x_dead_beef_0000_0000]); const SECOND_KEY: Key = U256([0, 0, 0, 0x_dead_beef_0100_0000]); diff --git a/core/lib/merkle_tree/src/domain.rs b/core/lib/merkle_tree/src/domain.rs index 0cd9a56a4866..f21a5e09133a 100644 --- a/core/lib/merkle_tree/src/domain.rs +++ b/core/lib/merkle_tree/src/domain.rs @@ -1,6 +1,13 @@ //! Tying the Merkle tree implementation to the problem domain. use rayon::{ThreadPool, ThreadPoolBuilder}; +use zksync_crypto::hasher::blake2::Blake2Hasher; +use zksync_storage::RocksDB; +use zksync_types::{ + proofs::{PrepareBasicCircuitsJob, StorageLogMetadata}, + writes::{InitialStorageWrite, RepeatedStorageWrite, StateDiffRecord}, + L1BatchNumber, StorageKey, U256, +}; use zksync_utils::h256_to_u256; use crate::{ @@ -11,13 +18,6 @@ use crate::{ }, BlockOutput, HashTree, MerkleTree, NoVersionError, }; -use zksync_crypto::hasher::blake2::Blake2Hasher; -use zksync_storage::RocksDB; -use zksync_types::{ - proofs::{PrepareBasicCircuitsJob, StorageLogMetadata}, - writes::{InitialStorageWrite, RepeatedStorageWrite, StateDiffRecord}, - L1BatchNumber, StorageKey, U256, -}; /// Metadata for the current tree state. #[derive(Debug, Clone)] diff --git a/core/lib/merkle_tree/src/errors.rs b/core/lib/merkle_tree/src/errors.rs index a30b0b98f5be..4afe8a2367c8 100644 --- a/core/lib/merkle_tree/src/errors.rs +++ b/core/lib/merkle_tree/src/errors.rs @@ -166,9 +166,10 @@ impl error::Error for NoVersionError {} #[cfg(test)] mod tests { + use zksync_types::U256; + use super::*; use crate::{types::Nibbles, Key}; - use zksync_types::U256; const TEST_KEY: Key = U256([0, 0, 0, 0x_dead_beef_0000_0000]); diff --git a/core/lib/merkle_tree/src/hasher/mod.rs b/core/lib/merkle_tree/src/hasher/mod.rs index 9425a5836f02..fa700a68244f 100644 --- a/core/lib/merkle_tree/src/hasher/mod.rs +++ b/core/lib/merkle_tree/src/hasher/mod.rs @@ -1,11 +1,9 @@ //! Hashing operations on the Merkle tree. -use once_cell::sync::Lazy; - use std::{fmt, iter}; -mod nodes; -mod proofs; +use once_cell::sync::Lazy; +use zksync_crypto::hasher::{blake2::Blake2Hasher, Hasher}; pub(crate) use self::nodes::{InternalNodeCache, MerklePath}; pub use self::proofs::TreeRangeDigest; @@ -13,7 +11,9 @@ use crate::{ metrics::HashingStats, types::{TreeEntry, ValueHash, TREE_DEPTH}, }; -use zksync_crypto::hasher::{blake2::Blake2Hasher, Hasher}; + +mod nodes; +mod proofs; /// Tree hashing functionality. pub trait HashTree: Send + Sync { @@ -222,9 +222,10 @@ impl HasherWithStats<'_> { #[cfg(test)] mod tests { + use zksync_types::{AccountTreeId, Address, StorageKey, H256}; + use super::*; use crate::types::LeafNode; - use zksync_types::{AccountTreeId, Address, StorageKey, H256}; #[test] fn empty_tree_hash_is_as_expected() { diff --git a/core/lib/merkle_tree/src/hasher/nodes.rs b/core/lib/merkle_tree/src/hasher/nodes.rs index d36c58c0ae13..6e1c007bc423 100644 --- a/core/lib/merkle_tree/src/hasher/nodes.rs +++ b/core/lib/merkle_tree/src/hasher/nodes.rs @@ -258,10 +258,11 @@ impl Node { #[cfg(test)] mod tests { - use super::*; use zksync_crypto::hasher::{blake2::Blake2Hasher, Hasher}; use zksync_types::H256; + use super::*; + fn test_internal_node_hashing(child_indexes: &[u8]) { println!("Testing indices: {child_indexes:?}"); diff --git a/core/lib/merkle_tree/src/lib.rs b/core/lib/merkle_tree/src/lib.rs index 85ace50aada5..687e957f8ef4 100644 --- a/core/lib/merkle_tree/src/lib.rs +++ b/core/lib/merkle_tree/src/lib.rs @@ -46,6 +46,23 @@ clippy::doc_markdown // frequent false positive: RocksDB )] +use zksync_crypto::hasher::blake2::Blake2Hasher; + +pub use crate::{ + errors::NoVersionError, + hasher::{HashTree, TreeRangeDigest}, + pruning::{MerkleTreePruner, MerkleTreePrunerHandle}, + storage::{ + Database, MerkleTreeColumnFamily, PatchSet, Patched, PruneDatabase, PrunePatchSet, + RocksDBWrapper, + }, + types::{ + BlockOutput, BlockOutputWithProofs, Key, TreeEntry, TreeEntryWithProof, TreeInstruction, + TreeLogEntry, TreeLogEntryWithProof, ValueHash, + }, +}; +use crate::{hasher::HasherWithStats, storage::Storage, types::Root}; + mod consistency; pub mod domain; mod errors; @@ -69,23 +86,6 @@ pub mod unstable { }; } -pub use crate::{ - errors::NoVersionError, - hasher::{HashTree, TreeRangeDigest}, - pruning::{MerkleTreePruner, MerkleTreePrunerHandle}, - storage::{ - Database, MerkleTreeColumnFamily, PatchSet, Patched, PruneDatabase, PrunePatchSet, - RocksDBWrapper, - }, - types::{ - BlockOutput, BlockOutputWithProofs, Key, TreeEntry, TreeEntryWithProof, TreeInstruction, - TreeLogEntry, TreeLogEntryWithProof, ValueHash, - }, -}; - -use crate::{hasher::HasherWithStats, storage::Storage, types::Root}; -use zksync_crypto::hasher::blake2::Blake2Hasher; - /// Binary Merkle tree implemented using AR16MT from Diem [Jellyfish Merkle tree] white paper. /// /// A tree is persistent and is backed by a key-value store (the `DB` type param). It is versioned, diff --git a/core/lib/merkle_tree/src/metrics.rs b/core/lib/merkle_tree/src/metrics.rs index 29bd58e599eb..ef1e94f9b050 100644 --- a/core/lib/merkle_tree/src/metrics.rs +++ b/core/lib/merkle_tree/src/metrics.rs @@ -6,11 +6,12 @@ use std::{ time::Duration, }; -use crate::types::Nibbles; use vise::{ Buckets, EncodeLabelSet, EncodeLabelValue, Family, Gauge, Global, Histogram, Metrics, Unit, }; +use crate::types::Nibbles; + #[derive(Debug, Metrics)] #[metrics(prefix = "merkle_tree")] pub(crate) struct GeneralMetrics { diff --git a/core/lib/merkle_tree/src/recovery.rs b/core/lib/merkle_tree/src/recovery.rs index d1f2618a5cdd..fd1790d2b7cf 100644 --- a/core/lib/merkle_tree/src/recovery.rs +++ b/core/lib/merkle_tree/src/recovery.rs @@ -37,13 +37,14 @@ use std::time::Instant; +use zksync_crypto::hasher::blake2::Blake2Hasher; + use crate::{ hasher::{HashTree, HasherWithStats}, storage::{PatchSet, PruneDatabase, PrunePatchSet, Storage}, types::{Key, Manifest, Root, TreeEntry, TreeTags, ValueHash}, MerkleTree, }; -use zksync_crypto::hasher::blake2::Blake2Hasher; /// Handle to a Merkle tree during its recovery. #[derive(Debug)] diff --git a/core/lib/merkle_tree/src/storage/mod.rs b/core/lib/merkle_tree/src/storage/mod.rs index ae273d22f323..d2b89da48cdb 100644 --- a/core/lib/merkle_tree/src/storage/mod.rs +++ b/core/lib/merkle_tree/src/storage/mod.rs @@ -1,20 +1,11 @@ //! Storage-related logic. -mod database; -mod patch; -mod proofs; -mod rocksdb; -mod serialization; -#[cfg(test)] -mod tests; - pub(crate) use self::patch::{LoadAncestorsResult, WorkingPatchSet}; pub use self::{ database::{Database, NodeKeys, Patched, PruneDatabase, PrunePatchSet}, patch::PatchSet, rocksdb::{MerkleTreeColumnFamily, RocksDBWrapper}, }; - use crate::{ hasher::HashTree, metrics::{TreeUpdaterStats, BLOCK_TIMINGS, GENERAL_METRICS}, @@ -24,6 +15,14 @@ use crate::{ }, }; +mod database; +mod patch; +mod proofs; +mod rocksdb; +mod serialization; +#[cfg(test)] +mod tests; + /// Tree operation: either inserting a new version or updating an existing one (the latter is only /// used during tree recovery). #[derive(Debug, Clone, Copy)] diff --git a/core/lib/merkle_tree/src/storage/patch.rs b/core/lib/merkle_tree/src/storage/patch.rs index ff41fb2f6bf3..f0b06c83bf2a 100644 --- a/core/lib/merkle_tree/src/storage/patch.rs +++ b/core/lib/merkle_tree/src/storage/patch.rs @@ -1,13 +1,13 @@ //! Types related to DB patches: `PatchSet` and `WorkingPatchSet`. -use rayon::prelude::*; - use std::{ collections::{hash_map::Entry, HashMap}, iter, time::Instant, }; +use rayon::prelude::*; + use crate::{ hasher::{HashTree, HasherWithStats, MerklePath}, metrics::HashingStats, diff --git a/core/lib/merkle_tree/src/storage/rocksdb.rs b/core/lib/merkle_tree/src/storage/rocksdb.rs index 6c6a3a18105e..7dd4d6083d79 100644 --- a/core/lib/merkle_tree/src/storage/rocksdb.rs +++ b/core/lib/merkle_tree/src/storage/rocksdb.rs @@ -1,9 +1,10 @@ //! RocksDB implementation of [`Database`]. -use rayon::prelude::*; - use std::path::Path; +use rayon::prelude::*; +use zksync_storage::{db::NamedColumnFamily, rocksdb::DBPinnableSlice, RocksDB}; + use crate::{ errors::{DeserializeError, ErrorContext}, metrics::ApplyPatchStats, @@ -13,7 +14,6 @@ use crate::{ }, types::{InternalNode, LeafNode, Manifest, Nibbles, Node, NodeKey, Root, StaleNodeKey}, }; -use zksync_storage::{db::NamedColumnFamily, rocksdb::DBPinnableSlice, RocksDB}; /// RocksDB column families used by the tree. #[derive(Debug, Clone, Copy)] @@ -285,10 +285,10 @@ impl PruneDatabase for RocksDBWrapper { #[cfg(test)] mod tests { - use tempfile::TempDir; - use std::collections::{HashMap, HashSet}; + use tempfile::TempDir; + use super::*; use crate::storage::tests::{create_patch, generate_nodes}; diff --git a/core/lib/merkle_tree/src/storage/serialization.rs b/core/lib/merkle_tree/src/storage/serialization.rs index 6a9216fa104a..09a06a3630cd 100644 --- a/core/lib/merkle_tree/src/storage/serialization.rs +++ b/core/lib/merkle_tree/src/storage/serialization.rs @@ -300,9 +300,10 @@ impl Manifest { #[cfg(test)] mod tests { + use zksync_types::H256; + use super::*; use crate::types::TreeEntry; - use zksync_types::H256; #[test] fn serializing_manifest() { diff --git a/core/lib/merkle_tree/src/storage/tests.rs b/core/lib/merkle_tree/src/storage/tests.rs index e70cb057280e..a0c1ae4c9494 100644 --- a/core/lib/merkle_tree/src/storage/tests.rs +++ b/core/lib/merkle_tree/src/storage/tests.rs @@ -1,3 +1,5 @@ +use std::collections::{HashMap, HashSet}; + use assert_matches::assert_matches; use rand::{ rngs::StdRng, @@ -5,16 +7,14 @@ use rand::{ Rng, SeedableRng, }; use test_casing::test_casing; - -use std::collections::{HashMap, HashSet}; +use zksync_crypto::hasher::blake2::Blake2Hasher; +use zksync_types::{H256, U256}; use super::*; use crate::{ hasher::{HasherWithStats, MerklePath}, types::{NodeKey, TreeInstruction, KEY_SIZE}, }; -use zksync_crypto::hasher::blake2::Blake2Hasher; -use zksync_types::{H256, U256}; pub(super) const FIRST_KEY: Key = U256([0, 0, 0, 0x_dead_beef_0000_0000]); const SECOND_KEY: Key = U256([0, 0, 0, 0x_dead_beef_0100_0000]); diff --git a/core/lib/merkle_tree/src/types/internal.rs b/core/lib/merkle_tree/src/types/internal.rs index cb35b0281c2b..e983928c554b 100644 --- a/core/lib/merkle_tree/src/types/internal.rs +++ b/core/lib/merkle_tree/src/types/internal.rs @@ -554,9 +554,10 @@ impl StaleNodeKey { #[cfg(test)] mod tests { - use super::*; use zksync_types::U256; + use super::*; + // `U256` uses little-endian `u64` ordering; i.e., this is // 0x_dead_beef_0000_0000_.._0000. const TEST_KEY: Key = U256([0, 0, 0, 0x_dead_beef_0000_0000]); diff --git a/core/lib/merkle_tree/src/types/mod.rs b/core/lib/merkle_tree/src/types/mod.rs index 15ab72b6911d..43a3922da861 100644 --- a/core/lib/merkle_tree/src/types/mod.rs +++ b/core/lib/merkle_tree/src/types/mod.rs @@ -1,13 +1,13 @@ //! Basic storage types. -mod internal; +use zksync_types::{H256, U256}; pub(crate) use self::internal::{ ChildRef, Nibbles, NibblesBytes, StaleNodeKey, TreeTags, HASH_SIZE, KEY_SIZE, TREE_DEPTH, }; pub use self::internal::{InternalNode, LeafNode, Manifest, Node, NodeKey, Root}; -use zksync_types::{H256, U256}; +mod internal; /// Key stored in the tree. pub type Key = U256; diff --git a/core/lib/merkle_tree/tests/integration/common.rs b/core/lib/merkle_tree/tests/integration/common.rs index 096a54ce7111..28c3827827a9 100644 --- a/core/lib/merkle_tree/tests/integration/common.rs +++ b/core/lib/merkle_tree/tests/integration/common.rs @@ -1,9 +1,8 @@ //! Shared functionality. -use once_cell::sync::Lazy; - use std::collections::HashMap; +use once_cell::sync::Lazy; use zksync_crypto::hasher::{blake2::Blake2Hasher, Hasher}; use zksync_merkle_tree::{HashTree, TreeEntry, TreeInstruction}; use zksync_types::{AccountTreeId, Address, StorageKey, H256, U256}; diff --git a/core/lib/merkle_tree/tests/integration/consistency.rs b/core/lib/merkle_tree/tests/integration/consistency.rs index da3312d2002d..b6b424e431ad 100644 --- a/core/lib/merkle_tree/tests/integration/consistency.rs +++ b/core/lib/merkle_tree/tests/integration/consistency.rs @@ -3,9 +3,9 @@ use rand::{rngs::StdRng, seq::SliceRandom, Rng, SeedableRng}; use tempfile::TempDir; +use zksync_merkle_tree::{MerkleTree, MerkleTreeColumnFamily, RocksDBWrapper}; use crate::common::generate_key_value_pairs; -use zksync_merkle_tree::{MerkleTree, MerkleTreeColumnFamily, RocksDBWrapper}; // Something (maybe RocksDB) makes the test below work very slowly in the debug mode; // thus, the number of test cases is conditionally reduced. diff --git a/core/lib/merkle_tree/tests/integration/domain.rs b/core/lib/merkle_tree/tests/integration/domain.rs index f3febda5f06a..451650672212 100644 --- a/core/lib/merkle_tree/tests/integration/domain.rs +++ b/core/lib/merkle_tree/tests/integration/domain.rs @@ -1,11 +1,10 @@ //! Domain-specific tests. Taken almost verbatim from the previous tree implementation. +use std::slice; + use serde::{Deserialize, Serialize}; use serde_with::{hex::Hex, serde_as}; use tempfile::TempDir; - -use std::slice; - use zksync_crypto::hasher::blake2::Blake2Hasher; use zksync_merkle_tree::{domain::ZkSyncTree, HashTree, TreeEntry, TreeInstruction}; use zksync_storage::RocksDB; diff --git a/core/lib/merkle_tree/tests/integration/merkle_tree.rs b/core/lib/merkle_tree/tests/integration/merkle_tree.rs index e4f052bb03c4..117ea0db4d99 100644 --- a/core/lib/merkle_tree/tests/integration/merkle_tree.rs +++ b/core/lib/merkle_tree/tests/integration/merkle_tree.rs @@ -1,10 +1,9 @@ //! Tests not tied to the zksync domain. -use rand::{rngs::StdRng, seq::SliceRandom, Rng, SeedableRng}; -use test_casing::test_casing; - use std::{cmp, mem}; +use rand::{rngs::StdRng, seq::SliceRandom, Rng, SeedableRng}; +use test_casing::test_casing; use zksync_crypto::hasher::blake2::Blake2Hasher; use zksync_merkle_tree::{ Database, HashTree, MerkleTree, PatchSet, Patched, TreeEntry, TreeInstruction, TreeLogEntry, @@ -532,16 +531,16 @@ fn range_proofs_with_random_ranges() { /// RocksDB-specific tests. mod rocksdb { + use std::collections::BTreeMap; + use serde::{Deserialize, Serialize}; use serde_with::{hex::Hex, serde_as}; use tempfile::TempDir; - - use std::collections::BTreeMap; - - use super::*; use zksync_merkle_tree::{MerkleTreeColumnFamily, MerkleTreePruner, RocksDBWrapper}; use zksync_storage::RocksDB; + use super::*; + #[derive(Debug)] struct Harness { db: RocksDBWrapper, diff --git a/core/lib/merkle_tree/tests/integration/recovery.rs b/core/lib/merkle_tree/tests/integration/recovery.rs index 6739e4ffe023..399b214e3fec 100644 --- a/core/lib/merkle_tree/tests/integration/recovery.rs +++ b/core/lib/merkle_tree/tests/integration/recovery.rs @@ -2,7 +2,6 @@ use rand::{rngs::StdRng, seq::SliceRandom, SeedableRng}; use test_casing::test_casing; - use zksync_crypto::hasher::blake2::Blake2Hasher; use zksync_merkle_tree::{ recovery::MerkleTreeRecovery, Database, MerkleTree, PatchSet, PruneDatabase, ValueHash, @@ -125,9 +124,9 @@ fn recovery_in_chunks(kind: RecoveryKind, chunk_size: usize) { mod rocksdb { use tempfile::TempDir; + use zksync_merkle_tree::RocksDBWrapper; use super::*; - use zksync_merkle_tree::RocksDBWrapper; #[test_casing(8, test_casing::Product((RecoveryKind::ALL, [6, 10, 17, 42])))] fn recovery_in_chunks(kind: RecoveryKind, chunk_size: usize) { diff --git a/core/lib/mini_merkle_tree/benches/tree.rs b/core/lib/mini_merkle_tree/benches/tree.rs index a964456bfb45..8ea4128ac34d 100644 --- a/core/lib/mini_merkle_tree/benches/tree.rs +++ b/core/lib/mini_merkle_tree/benches/tree.rs @@ -3,7 +3,6 @@ use criterion::{ criterion_group, criterion_main, BatchSize, Bencher, BenchmarkId, Criterion, Throughput, }; - use zksync_mini_merkle_tree::MiniMerkleTree; const TREE_SIZES: &[usize] = &[32, 64, 128, 256, 512, 1_024]; diff --git a/core/lib/mini_merkle_tree/src/lib.rs b/core/lib/mini_merkle_tree/src/lib.rs index a6cbf37213c8..168e5d8dd095 100644 --- a/core/lib/mini_merkle_tree/src/lib.rs +++ b/core/lib/mini_merkle_tree/src/lib.rs @@ -5,10 +5,10 @@ #![warn(clippy::all, clippy::pedantic)] #![allow(clippy::must_use_candidate, clippy::similar_names)] -use once_cell::sync::Lazy; - use std::{iter, str::FromStr}; +use once_cell::sync::Lazy; + #[cfg(test)] mod tests; diff --git a/core/lib/multivm/src/glue/tracers/mod.rs b/core/lib/multivm/src/glue/tracers/mod.rs index a504d5d2c8ff..c58e717a646a 100644 --- a/core/lib/multivm/src/glue/tracers/mod.rs +++ b/core/lib/multivm/src/glue/tracers/mod.rs @@ -29,10 +29,11 @@ //! - Add this trait as a trait bound to the `MultiVMTracer`. //! - Add this trait as a trait bound for `T` in `MultiVMTracer` implementation. //! - Implement the trait for `T` with a bound to `VmTracer` for a specific version. -//! -use crate::HistoryMode; + use zksync_state::WriteStorage; +use crate::HistoryMode; + pub type MultiVmTracerPointer = Box>; pub trait MultiVMTracer: diff --git a/core/lib/multivm/src/glue/types/vm/block_context_mode.rs b/core/lib/multivm/src/glue/types/vm/block_context_mode.rs index eba3c503e06f..0cbbcbf33e3a 100644 --- a/core/lib/multivm/src/glue/types/vm/block_context_mode.rs +++ b/core/lib/multivm/src/glue/types/vm/block_context_mode.rs @@ -1,6 +1,7 @@ -use crate::glue::GlueFrom; use zksync_utils::h256_to_u256; +use crate::glue::GlueFrom; + impl GlueFrom for crate::vm_m5::vm_with_bootloader::BlockContextMode { fn glue_from(value: crate::interface::L1BatchEnv) -> Self { let derived = crate::vm_m5::vm_with_bootloader::DerivedBlockContext { diff --git a/core/lib/multivm/src/glue/types/vm/vm_block_result.rs b/core/lib/multivm/src/glue/types/vm/vm_block_result.rs index 827ac7fe82a2..e63ab376bad4 100644 --- a/core/lib/multivm/src/glue/types/vm/vm_block_result.rs +++ b/core/lib/multivm/src/glue/types/vm/vm_block_result.rs @@ -1,9 +1,11 @@ use zksync_types::l2_to_l1_log::UserL2ToL1Log; -use crate::glue::{GlueFrom, GlueInto}; -use crate::interface::{ - types::outputs::VmExecutionLogs, CurrentExecutionState, ExecutionResult, Refunds, - VmExecutionResultAndLogs, VmExecutionStatistics, +use crate::{ + glue::{GlueFrom, GlueInto}, + interface::{ + types::outputs::VmExecutionLogs, CurrentExecutionState, ExecutionResult, Refunds, + VmExecutionResultAndLogs, VmExecutionStatistics, + }, }; // Note: In version after vm VmVirtualBlocks the bootloader memory knowledge is encapsulated into the VM. diff --git a/core/lib/multivm/src/glue/types/vm/vm_tx_execution_result.rs b/core/lib/multivm/src/glue/types/vm/vm_tx_execution_result.rs index 10e422edbcac..0c888cdda232 100644 --- a/core/lib/multivm/src/glue/types/vm/vm_tx_execution_result.rs +++ b/core/lib/multivm/src/glue/types/vm/vm_tx_execution_result.rs @@ -1,7 +1,10 @@ -use crate::glue::{GlueFrom, GlueInto}; -use crate::interface::{ExecutionResult, Refunds, TxRevertReason, VmExecutionResultAndLogs}; use zksync_types::tx::tx_execution_info::TxExecutionStatus; +use crate::{ + glue::{GlueFrom, GlueInto}, + interface::{ExecutionResult, Refunds, TxRevertReason, VmExecutionResultAndLogs}, +}; + impl GlueFrom for VmExecutionResultAndLogs { fn glue_from(value: crate::vm_m5::vm_instance::VmTxExecutionResult) -> Self { let mut result: VmExecutionResultAndLogs = value.result.glue_into(); diff --git a/core/lib/multivm/src/interface/traits/tracers/dyn_tracers/vm_1_3_3.rs b/core/lib/multivm/src/interface/traits/tracers/dyn_tracers/vm_1_3_3.rs index 2138dd086c0e..c088889aa038 100644 --- a/core/lib/multivm/src/interface/traits/tracers/dyn_tracers/vm_1_3_3.rs +++ b/core/lib/multivm/src/interface/traits/tracers/dyn_tracers/vm_1_3_3.rs @@ -1,6 +1,6 @@ -use zk_evm_1_3_3::abstractions::Memory; -use zk_evm_1_3_3::tracing::{ - AfterDecodingData, AfterExecutionData, BeforeExecutionData, VmLocalStateData, +use zk_evm_1_3_3::{ + abstractions::Memory, + tracing::{AfterDecodingData, AfterExecutionData, BeforeExecutionData, VmLocalStateData}, }; use zksync_state::StoragePtr; diff --git a/core/lib/multivm/src/interface/traits/tracers/dyn_tracers/vm_1_4_0.rs b/core/lib/multivm/src/interface/traits/tracers/dyn_tracers/vm_1_4_0.rs index 61d7831393d2..3ce69d02942b 100644 --- a/core/lib/multivm/src/interface/traits/tracers/dyn_tracers/vm_1_4_0.rs +++ b/core/lib/multivm/src/interface/traits/tracers/dyn_tracers/vm_1_4_0.rs @@ -1,6 +1,6 @@ -use zk_evm_1_4_0::abstractions::Memory; -use zk_evm_1_4_0::tracing::{ - AfterDecodingData, AfterExecutionData, BeforeExecutionData, VmLocalStateData, +use zk_evm_1_4_0::{ + abstractions::Memory, + tracing::{AfterDecodingData, AfterExecutionData, BeforeExecutionData, VmLocalStateData}, }; use zksync_state::StoragePtr; diff --git a/core/lib/multivm/src/interface/traits/vm.rs b/core/lib/multivm/src/interface/traits/vm.rs index b4a9320bbc66..0dbacc9d1d9e 100644 --- a/core/lib/multivm/src/interface/traits/vm.rs +++ b/core/lib/multivm/src/interface/traits/vm.rs @@ -47,20 +47,24 @@ //! let result = vm.execute(multivm::interface::VmExecutionMode::Batch); //! ``` -use crate::interface::types::errors::BytecodeCompressionError; -use crate::interface::types::inputs::{L1BatchEnv, L2BlockEnv, SystemEnv, VmExecutionMode}; -use crate::interface::types::outputs::{ - BootloaderMemory, CurrentExecutionState, VmExecutionResultAndLogs, -}; - -use crate::interface::{FinishedL1Batch, VmMemoryMetrics}; -use crate::tracers::TracerDispatcher; -use crate::vm_latest::HistoryEnabled; -use crate::HistoryMode; use zksync_state::StoragePtr; use zksync_types::Transaction; use zksync_utils::bytecode::CompressedBytecodeInfo; +use crate::{ + interface::{ + types::{ + errors::BytecodeCompressionError, + inputs::{L1BatchEnv, L2BlockEnv, SystemEnv, VmExecutionMode}, + outputs::{BootloaderMemory, CurrentExecutionState, VmExecutionResultAndLogs}, + }, + FinishedL1Batch, VmMemoryMetrics, + }, + tracers::TracerDispatcher, + vm_latest::HistoryEnabled, + HistoryMode, +}; + pub trait VmInterface { type TracerDispatcher: Default + From>; diff --git a/core/lib/multivm/src/interface/types/errors/halt.rs b/core/lib/multivm/src/interface/types/errors/halt.rs index 23bab7ee55e0..3323a128c682 100644 --- a/core/lib/multivm/src/interface/types/errors/halt.rs +++ b/core/lib/multivm/src/interface/types/errors/halt.rs @@ -1,6 +1,7 @@ -use super::VmRevertReason; use std::fmt::{Display, Formatter}; +use super::VmRevertReason; + /// Structure for non-contract errors from the Virtual Machine (EVM). /// Differentiates VM-specific issues from contract-related errors. diff --git a/core/lib/multivm/src/interface/types/errors/tx_revert_reason.rs b/core/lib/multivm/src/interface/types/errors/tx_revert_reason.rs index f92a913fb8b9..9578a06ea0a5 100644 --- a/core/lib/multivm/src/interface/types/errors/tx_revert_reason.rs +++ b/core/lib/multivm/src/interface/types/errors/tx_revert_reason.rs @@ -1,8 +1,6 @@ -use super::halt::Halt; - use std::fmt::Display; -use super::{BootloaderErrorCode, VmRevertReason}; +use super::{halt::Halt, BootloaderErrorCode, VmRevertReason}; #[derive(Debug, Clone, PartialEq)] pub enum TxRevertReason { diff --git a/core/lib/multivm/src/interface/types/inputs/l1_batch_env.rs b/core/lib/multivm/src/interface/types/inputs/l1_batch_env.rs index ff239ec42662..b5cb0cbe5e8f 100644 --- a/core/lib/multivm/src/interface/types/inputs/l1_batch_env.rs +++ b/core/lib/multivm/src/interface/types/inputs/l1_batch_env.rs @@ -1,6 +1,6 @@ -use super::L2BlockEnv; use zksync_types::{Address, L1BatchNumber, H256}; +use super::L2BlockEnv; use crate::vm_latest::utils::fee::derive_base_fee_and_gas_per_pubdata; /// Unique params for each block diff --git a/core/lib/multivm/src/interface/types/outputs/execution_result.rs b/core/lib/multivm/src/interface/types/outputs/execution_result.rs index 3181a94a9da6..e177b6300120 100644 --- a/core/lib/multivm/src/interface/types/outputs/execution_result.rs +++ b/core/lib/multivm/src/interface/types/outputs/execution_result.rs @@ -1,11 +1,14 @@ -use crate::interface::{Halt, VmExecutionStatistics, VmRevertReason}; use zksync_system_constants::PUBLISH_BYTECODE_OVERHEAD; -use zksync_types::event::{extract_long_l2_to_l1_messages, extract_published_bytecodes}; -use zksync_types::l2_to_l1_log::{SystemL2ToL1Log, UserL2ToL1Log}; -use zksync_types::tx::ExecutionMetrics; -use zksync_types::{StorageLogQuery, Transaction, VmEvent}; +use zksync_types::{ + event::{extract_long_l2_to_l1_messages, extract_published_bytecodes}, + l2_to_l1_log::{SystemL2ToL1Log, UserL2ToL1Log}, + tx::ExecutionMetrics, + StorageLogQuery, Transaction, VmEvent, +}; use zksync_utils::bytecode::bytecode_len_in_bytes; +use crate::interface::{Halt, VmExecutionStatistics, VmRevertReason}; + /// Refunds produced for the user. #[derive(Debug, Clone, Default)] pub struct Refunds { diff --git a/core/lib/multivm/src/interface/types/outputs/execution_state.rs b/core/lib/multivm/src/interface/types/outputs/execution_state.rs index 066de92ffbe6..24034a962218 100644 --- a/core/lib/multivm/src/interface/types/outputs/execution_state.rs +++ b/core/lib/multivm/src/interface/types/outputs/execution_state.rs @@ -1,5 +1,7 @@ -use zksync_types::l2_to_l1_log::{SystemL2ToL1Log, UserL2ToL1Log}; -use zksync_types::{LogQuery, StorageLogQuery, VmEvent, U256}; +use zksync_types::{ + l2_to_l1_log::{SystemL2ToL1Log, UserL2ToL1Log}, + LogQuery, StorageLogQuery, VmEvent, U256, +}; /// State of the VM since the start of the batch execution. #[derive(Debug, Clone, PartialEq)] diff --git a/core/lib/multivm/src/interface/types/outputs/mod.rs b/core/lib/multivm/src/interface/types/outputs/mod.rs index 39fed3ad9cb8..eec19826e0b2 100644 --- a/core/lib/multivm/src/interface/types/outputs/mod.rs +++ b/core/lib/multivm/src/interface/types/outputs/mod.rs @@ -1,12 +1,13 @@ +pub use self::{ + execution_result::{ExecutionResult, Refunds, VmExecutionLogs, VmExecutionResultAndLogs}, + execution_state::{BootloaderMemory, CurrentExecutionState}, + finished_l1batch::FinishedL1Batch, + l2_block::L2Block, + statistic::{VmExecutionStatistics, VmMemoryMetrics}, +}; + mod execution_result; mod execution_state; mod finished_l1batch; mod l2_block; mod statistic; - -pub use execution_result::VmExecutionLogs; -pub use execution_result::{ExecutionResult, Refunds, VmExecutionResultAndLogs}; -pub use execution_state::{BootloaderMemory, CurrentExecutionState}; -pub use finished_l1batch::FinishedL1Batch; -pub use l2_block::L2Block; -pub use statistic::{VmExecutionStatistics, VmMemoryMetrics}; diff --git a/core/lib/multivm/src/lib.rs b/core/lib/multivm/src/lib.rs index 06d7a4291300..23ea80a68602 100644 --- a/core/lib/multivm/src/lib.rs +++ b/core/lib/multivm/src/lib.rs @@ -3,6 +3,14 @@ #![warn(unused_extern_crates)] #![warn(unused_imports)] +pub use zk_evm_1_3_1; +pub use zk_evm_1_3_3; +pub use zk_evm_1_4_0; +pub use zksync_types::vm_version::VmVersion; + +pub use self::versions::{ + vm_1_3_2, vm_latest, vm_m5, vm_m6, vm_refunds_enhancement, vm_virtual_blocks, +}; pub use crate::{ glue::{ history_mode::HistoryMode, @@ -10,22 +18,9 @@ pub use crate::{ }, vm_instance::VmInstance, }; -pub use zksync_types::vm_version::VmVersion; mod glue; - -mod vm_instance; - pub mod interface; pub mod tracers; pub mod versions; - -pub use versions::vm_1_3_2; -pub use versions::vm_latest; -pub use versions::vm_m5; -pub use versions::vm_m6; -pub use versions::vm_refunds_enhancement; -pub use versions::vm_virtual_blocks; -pub use zk_evm_1_3_1; -pub use zk_evm_1_3_3; -pub use zk_evm_1_4_0; +mod vm_instance; diff --git a/core/lib/multivm/src/tracers/call_tracer/mod.rs b/core/lib/multivm/src/tracers/call_tracer/mod.rs index 90f15fb68d40..0c7e4d3c2800 100644 --- a/core/lib/multivm/src/tracers/call_tracer/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/mod.rs @@ -1,8 +1,10 @@ -use crate::tracers::call_tracer::metrics::CALL_METRICS; -use once_cell::sync::OnceCell; use std::sync::Arc; + +use once_cell::sync::OnceCell; use zksync_types::vm_trace::Call; +use crate::tracers::call_tracer::metrics::CALL_METRICS; + mod metrics; pub mod vm_latest; pub mod vm_refunds_enhancement; diff --git a/core/lib/multivm/src/tracers/call_tracer/vm_latest/mod.rs b/core/lib/multivm/src/tracers/call_tracer/vm_latest/mod.rs index f5f5c1077d3d..09b5b828fc03 100644 --- a/core/lib/multivm/src/tracers/call_tracer/vm_latest/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/vm_latest/mod.rs @@ -7,16 +7,19 @@ use zk_evm_1_4_0::{ }; use zksync_state::{StoragePtr, WriteStorage}; use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS; -use zksync_types::vm_trace::{Call, CallType}; -use zksync_types::FarCallOpcode; -use zksync_types::U256; +use zksync_types::{ + vm_trace::{Call, CallType}, + FarCallOpcode, U256, +}; -use crate::interface::{ - tracer::VmExecutionStopReason, traits::tracers::dyn_tracers::vm_1_4_0::DynTracer, - VmRevertReason, +use crate::{ + interface::{ + tracer::VmExecutionStopReason, traits::tracers::dyn_tracers::vm_1_4_0::DynTracer, + VmRevertReason, + }, + tracers::call_tracer::CallTracer, + vm_latest::{BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState}, }; -use crate::tracers::call_tracer::CallTracer; -use crate::vm_latest::{BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState}; impl DynTracer> for CallTracer { fn after_execution( diff --git a/core/lib/multivm/src/tracers/call_tracer/vm_refunds_enhancement/mod.rs b/core/lib/multivm/src/tracers/call_tracer/vm_refunds_enhancement/mod.rs index fab4ee0ff0fb..3bc4426e8cc2 100644 --- a/core/lib/multivm/src/tracers/call_tracer/vm_refunds_enhancement/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/vm_refunds_enhancement/mod.rs @@ -7,17 +7,18 @@ use zk_evm_1_3_3::{ }; use zksync_state::{StoragePtr, WriteStorage}; use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS; -use zksync_types::vm_trace::{Call, CallType}; -use zksync_types::FarCallOpcode; -use zksync_types::U256; - -use crate::interface::{ - tracer::VmExecutionStopReason, traits::tracers::dyn_tracers::vm_1_3_3::DynTracer, - VmRevertReason, +use zksync_types::{ + vm_trace::{Call, CallType}, + FarCallOpcode, U256, }; -use crate::tracers::call_tracer::CallTracer; -use crate::vm_refunds_enhancement::{ - BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState, + +use crate::{ + interface::{ + tracer::VmExecutionStopReason, traits::tracers::dyn_tracers::vm_1_3_3::DynTracer, + VmRevertReason, + }, + tracers::call_tracer::CallTracer, + vm_refunds_enhancement::{BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState}, }; impl DynTracer> for CallTracer { diff --git a/core/lib/multivm/src/tracers/call_tracer/vm_virtual_blocks/mod.rs b/core/lib/multivm/src/tracers/call_tracer/vm_virtual_blocks/mod.rs index 631d4d2081ca..f96b2cb0f58f 100644 --- a/core/lib/multivm/src/tracers/call_tracer/vm_virtual_blocks/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/vm_virtual_blocks/mod.rs @@ -1,20 +1,23 @@ -use zk_evm_1_3_3::tracing::{AfterExecutionData, VmLocalStateData}; -use zk_evm_1_3_3::zkevm_opcode_defs::{ - FarCallABI, FatPointer, Opcode, RetOpcode, CALL_IMPLICIT_CALLDATA_FAT_PTR_REGISTER, - RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER, +use zk_evm_1_3_3::{ + tracing::{AfterExecutionData, VmLocalStateData}, + zkevm_opcode_defs::{ + FarCallABI, FatPointer, Opcode, RetOpcode, CALL_IMPLICIT_CALLDATA_FAT_PTR_REGISTER, + RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER, + }, }; use zksync_state::{StoragePtr, WriteStorage}; use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS; -use zksync_types::vm_trace::{Call, CallType}; -use zksync_types::FarCallOpcode; -use zksync_types::U256; - -use crate::interface::{ - dyn_tracers::vm_1_3_3::DynTracer, VmExecutionResultAndLogs, VmRevertReason, +use zksync_types::{ + vm_trace::{Call, CallType}, + FarCallOpcode, U256, }; -use crate::tracers::call_tracer::CallTracer; -use crate::vm_virtual_blocks::{ - ExecutionEndTracer, ExecutionProcessing, HistoryMode, SimpleMemory, VmTracer, + +use crate::{ + interface::{dyn_tracers::vm_1_3_3::DynTracer, VmExecutionResultAndLogs, VmRevertReason}, + tracers::call_tracer::CallTracer, + vm_virtual_blocks::{ + ExecutionEndTracer, ExecutionProcessing, HistoryMode, SimpleMemory, VmTracer, + }, }; impl DynTracer> for CallTracer { diff --git a/core/lib/multivm/src/tracers/multivm_dispatcher.rs b/core/lib/multivm/src/tracers/multivm_dispatcher.rs index d4c7337ce65a..8ee858a61108 100644 --- a/core/lib/multivm/src/tracers/multivm_dispatcher.rs +++ b/core/lib/multivm/src/tracers/multivm_dispatcher.rs @@ -1,6 +1,7 @@ -use crate::{HistoryMode, MultiVmTracerPointer}; use zksync_state::WriteStorage; +use crate::{HistoryMode, MultiVmTracerPointer}; + /// Tracer dispatcher is a tracer that can dispatch calls to multiple tracers. pub struct TracerDispatcher { tracers: Vec>, diff --git a/core/lib/multivm/src/tracers/storage_invocation/vm_latest/mod.rs b/core/lib/multivm/src/tracers/storage_invocation/vm_latest/mod.rs index 0490ec34107c..46213ff54fc1 100644 --- a/core/lib/multivm/src/tracers/storage_invocation/vm_latest/mod.rs +++ b/core/lib/multivm/src/tracers/storage_invocation/vm_latest/mod.rs @@ -1,12 +1,15 @@ -use crate::interface::{ - tracer::{TracerExecutionStatus, TracerExecutionStopReason}, - traits::tracers::dyn_tracers::vm_1_4_0::DynTracer, - Halt, -}; -use crate::tracers::storage_invocation::StorageInvocations; -use crate::vm_latest::{BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState}; use zksync_state::WriteStorage; +use crate::{ + interface::{ + tracer::{TracerExecutionStatus, TracerExecutionStopReason}, + traits::tracers::dyn_tracers::vm_1_4_0::DynTracer, + Halt, + }, + tracers::storage_invocation::StorageInvocations, + vm_latest::{BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState}, +}; + impl DynTracer> for StorageInvocations {} impl VmTracer for StorageInvocations { diff --git a/core/lib/multivm/src/tracers/storage_invocation/vm_refunds_enhancement/mod.rs b/core/lib/multivm/src/tracers/storage_invocation/vm_refunds_enhancement/mod.rs index fe4fc33418db..1e562374afd5 100644 --- a/core/lib/multivm/src/tracers/storage_invocation/vm_refunds_enhancement/mod.rs +++ b/core/lib/multivm/src/tracers/storage_invocation/vm_refunds_enhancement/mod.rs @@ -1,10 +1,15 @@ -use crate::interface::tracer::{TracerExecutionStatus, TracerExecutionStopReason}; -use crate::interface::{traits::tracers::dyn_tracers::vm_1_3_3::DynTracer, Halt}; -use crate::tracers::storage_invocation::StorageInvocations; -use crate::vm_refunds_enhancement::VmTracer; -use crate::vm_refunds_enhancement::{BootloaderState, HistoryMode, SimpleMemory, ZkSyncVmState}; use zksync_state::WriteStorage; +use crate::{ + interface::{ + tracer::{TracerExecutionStatus, TracerExecutionStopReason}, + traits::tracers::dyn_tracers::vm_1_3_3::DynTracer, + Halt, + }, + tracers::storage_invocation::StorageInvocations, + vm_refunds_enhancement::{BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState}, +}; + impl DynTracer> for StorageInvocations {} impl VmTracer for StorageInvocations { diff --git a/core/lib/multivm/src/tracers/storage_invocation/vm_virtual_blocks/mod.rs b/core/lib/multivm/src/tracers/storage_invocation/vm_virtual_blocks/mod.rs index 023b6f376cd3..cd0ab9f4bb54 100644 --- a/core/lib/multivm/src/tracers/storage_invocation/vm_virtual_blocks/mod.rs +++ b/core/lib/multivm/src/tracers/storage_invocation/vm_virtual_blocks/mod.rs @@ -1,11 +1,14 @@ -use crate::interface::dyn_tracers::vm_1_3_3::DynTracer; -use crate::tracers::storage_invocation::StorageInvocations; -use crate::vm_virtual_blocks::{ - BootloaderState, ExecutionEndTracer, ExecutionProcessing, HistoryMode, SimpleMemory, VmTracer, - ZkSyncVmState, -}; use zksync_state::WriteStorage; +use crate::{ + interface::dyn_tracers::vm_1_3_3::DynTracer, + tracers::storage_invocation::StorageInvocations, + vm_virtual_blocks::{ + BootloaderState, ExecutionEndTracer, ExecutionProcessing, HistoryMode, SimpleMemory, + VmTracer, ZkSyncVmState, + }, +}; + impl ExecutionEndTracer for StorageInvocations { fn should_stop_execution(&self) -> bool { self.current >= self.limit diff --git a/core/lib/multivm/src/tracers/validator/mod.rs b/core/lib/multivm/src/tracers/validator/mod.rs index 718edf1a9641..0f43f235ade4 100644 --- a/core/lib/multivm/src/tracers/validator/mod.rs +++ b/core/lib/multivm/src/tracers/validator/mod.rs @@ -1,19 +1,11 @@ -mod types; -mod vm_latest; -mod vm_refunds_enhancement; -mod vm_virtual_blocks; - -use std::sync::Arc; -use std::{collections::HashSet, marker::PhantomData}; +use std::{collections::HashSet, marker::PhantomData, sync::Arc}; use once_cell::sync::OnceCell; - use zksync_state::{StoragePtr, WriteStorage}; use zksync_system_constants::{ ACCOUNT_CODE_STORAGE_ADDRESS, BOOTLOADER_ADDRESS, CONTRACT_DEPLOYER_ADDRESS, L2_ETH_TOKEN_ADDRESS, MSG_VALUE_SIMULATOR_ADDRESS, SYSTEM_CONTEXT_ADDRESS, }; - use zksync_types::{ vm_trace::ViolatedValidationRule, web3::signing::keccak256, AccountTreeId, Address, StorageKey, H256, U256, @@ -23,6 +15,11 @@ use zksync_utils::{be_bytes_to_safe_address, u256_to_account_address, u256_to_h2 use crate::tracers::validator::types::{NewTrustedValidationItems, ValidationTracerMode}; pub use crate::tracers::validator::types::{ValidationError, ValidationTracerParams}; +mod types; +mod vm_latest; +mod vm_refunds_enhancement; +mod vm_virtual_blocks; + /// Tracer that is used to ensure that the validation adheres to all the rules /// to prevent DDoS attacks on the server. #[derive(Debug, Clone)] diff --git a/core/lib/multivm/src/tracers/validator/types.rs b/core/lib/multivm/src/tracers/validator/types.rs index eb80e6f1650d..de6217c29889 100644 --- a/core/lib/multivm/src/tracers/validator/types.rs +++ b/core/lib/multivm/src/tracers/validator/types.rs @@ -1,8 +1,8 @@ +use std::{collections::HashSet, fmt::Display}; + +use zksync_types::{vm_trace::ViolatedValidationRule, Address, H256, U256}; + use crate::interface::Halt; -use std::collections::HashSet; -use std::fmt::Display; -use zksync_types::vm_trace::ViolatedValidationRule; -use zksync_types::{Address, H256, U256}; #[derive(Debug, Clone, Eq, PartialEq, Copy)] #[allow(clippy::enum_variant_names)] diff --git a/core/lib/multivm/src/tracers/validator/vm_latest/mod.rs b/core/lib/multivm/src/tracers/validator/vm_latest/mod.rs index 4d5ff43ec470..dbdc7253f75d 100644 --- a/core/lib/multivm/src/tracers/validator/vm_latest/mod.rs +++ b/core/lib/multivm/src/tracers/validator/vm_latest/mod.rs @@ -2,32 +2,32 @@ use zk_evm_1_4_0::{ tracing::{BeforeExecutionData, VmLocalStateData}, zkevm_opcode_defs::{ContextOpcode, FarCallABI, LogOpcode, Opcode}, }; - use zksync_state::{StoragePtr, WriteStorage}; use zksync_system_constants::KECCAK256_PRECOMPILE_ADDRESS; - -use crate::HistoryMode; use zksync_types::{ get_code_key, vm_trace::ViolatedValidationRule, AccountTreeId, StorageKey, H256, }; use zksync_utils::{h256_to_account_address, u256_to_account_address, u256_to_h256}; -use crate::vm_latest::tracers::utils::{ - computational_gas_price, get_calldata_page_via_abi, print_debug_if_needed, VmHook, -}; - -use crate::interface::{ - traits::tracers::dyn_tracers::vm_1_4_0::DynTracer, - types::tracer::{TracerExecutionStatus, TracerExecutionStopReason}, - Halt, -}; -use crate::tracers::validator::{ - types::{NewTrustedValidationItems, ValidationTracerMode}, - {ValidationRoundResult, ValidationTracer}, +use crate::{ + interface::{ + traits::tracers::dyn_tracers::vm_1_4_0::DynTracer, + types::tracer::{TracerExecutionStatus, TracerExecutionStopReason}, + Halt, + }, + tracers::validator::{ + types::{NewTrustedValidationItems, ValidationTracerMode}, + ValidationRoundResult, ValidationTracer, + }, + vm_latest::{ + tracers::utils::{ + computational_gas_price, get_calldata_page_via_abi, print_debug_if_needed, VmHook, + }, + BootloaderState, SimpleMemory, VmTracer, ZkSyncVmState, + }, + HistoryMode, }; -use crate::vm_latest::{BootloaderState, SimpleMemory, VmTracer, ZkSyncVmState}; - impl ValidationTracer { fn check_user_restrictions_vm_latest( &mut self, diff --git a/core/lib/multivm/src/tracers/validator/vm_refunds_enhancement/mod.rs b/core/lib/multivm/src/tracers/validator/vm_refunds_enhancement/mod.rs index ec4e95e56301..ab3a16c4b901 100644 --- a/core/lib/multivm/src/tracers/validator/vm_refunds_enhancement/mod.rs +++ b/core/lib/multivm/src/tracers/validator/vm_refunds_enhancement/mod.rs @@ -2,31 +2,30 @@ use zk_evm_1_3_3::{ tracing::{BeforeExecutionData, VmLocalStateData}, zkevm_opcode_defs::{ContextOpcode, FarCallABI, LogOpcode, Opcode}, }; - use zksync_state::{StoragePtr, WriteStorage}; use zksync_system_constants::KECCAK256_PRECOMPILE_ADDRESS; - -use crate::HistoryMode; use zksync_types::{ get_code_key, vm_trace::ViolatedValidationRule, AccountTreeId, StorageKey, H256, }; use zksync_utils::{h256_to_account_address, u256_to_account_address, u256_to_h256}; -use crate::interface::{ - traits::tracers::dyn_tracers::vm_1_3_3::DynTracer, - types::tracer::{TracerExecutionStatus, TracerExecutionStopReason}, - Halt, -}; -use crate::tracers::validator::{ - types::{NewTrustedValidationItems, ValidationTracerMode}, - {ValidationRoundResult, ValidationTracer}, -}; - -use crate::vm_refunds_enhancement::{ - tracers::utils::{ - computational_gas_price, get_calldata_page_via_abi, print_debug_if_needed, VmHook, +use crate::{ + interface::{ + traits::tracers::dyn_tracers::vm_1_3_3::DynTracer, + types::tracer::{TracerExecutionStatus, TracerExecutionStopReason}, + Halt, + }, + tracers::validator::{ + types::{NewTrustedValidationItems, ValidationTracerMode}, + ValidationRoundResult, ValidationTracer, + }, + vm_refunds_enhancement::{ + tracers::utils::{ + computational_gas_price, get_calldata_page_via_abi, print_debug_if_needed, VmHook, + }, + BootloaderState, SimpleMemory, VmTracer, ZkSyncVmState, }, - BootloaderState, SimpleMemory, VmTracer, ZkSyncVmState, + HistoryMode, }; impl ValidationTracer { diff --git a/core/lib/multivm/src/tracers/validator/vm_virtual_blocks/mod.rs b/core/lib/multivm/src/tracers/validator/vm_virtual_blocks/mod.rs index d2155f4ecf87..6fd2955f60b6 100644 --- a/core/lib/multivm/src/tracers/validator/vm_virtual_blocks/mod.rs +++ b/core/lib/multivm/src/tracers/validator/vm_virtual_blocks/mod.rs @@ -2,25 +2,27 @@ use zk_evm_1_3_3::{ tracing::{BeforeExecutionData, VmLocalStateData}, zkevm_opcode_defs::{ContextOpcode, FarCallABI, LogOpcode, Opcode}, }; - use zksync_state::{StoragePtr, WriteStorage}; use zksync_system_constants::KECCAK256_PRECOMPILE_ADDRESS; - -use crate::HistoryMode; -use zksync_types::vm_trace::ViolatedValidationRule; -use zksync_types::{get_code_key, AccountTreeId, StorageKey, H256}; +use zksync_types::{ + get_code_key, vm_trace::ViolatedValidationRule, AccountTreeId, StorageKey, H256, +}; use zksync_utils::{h256_to_account_address, u256_to_account_address, u256_to_h256}; -use crate::vm_virtual_blocks::tracers::utils::{ - computational_gas_price, get_calldata_page_via_abi, print_debug_if_needed, VmHook, +use crate::{ + interface::{dyn_tracers::vm_1_3_3::DynTracer, VmExecutionResultAndLogs}, + tracers::validator::{ + types::{NewTrustedValidationItems, ValidationTracerMode}, + ValidationRoundResult, ValidationTracer, + }, + vm_virtual_blocks::{ + tracers::utils::{ + computational_gas_price, get_calldata_page_via_abi, print_debug_if_needed, VmHook, + }, + ExecutionEndTracer, ExecutionProcessing, SimpleMemory, VmTracer, + }, + HistoryMode, }; -use crate::vm_virtual_blocks::SimpleMemory; -use crate::vm_virtual_blocks::{ExecutionEndTracer, ExecutionProcessing, VmTracer}; - -use crate::interface::dyn_tracers::vm_1_3_3::DynTracer; -use crate::interface::VmExecutionResultAndLogs; -use crate::tracers::validator::types::{NewTrustedValidationItems, ValidationTracerMode}; -use crate::tracers::validator::{ValidationRoundResult, ValidationTracer}; impl ValidationTracer { fn check_user_restrictions_vm_virtual_blocks( diff --git a/core/lib/multivm/src/versions/vm_1_3_2/errors/vm_revert_reason.rs b/core/lib/multivm/src/versions/vm_1_3_2/errors/vm_revert_reason.rs index c127a9e6f2df..70c954425f46 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/errors/vm_revert_reason.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/errors/vm_revert_reason.rs @@ -1,5 +1,7 @@ -use std::convert::TryFrom; -use std::fmt::{Debug, Display}; +use std::{ + convert::TryFrom, + fmt::{Debug, Display}, +}; use zksync_types::U256; diff --git a/core/lib/multivm/src/versions/vm_1_3_2/event_sink.rs b/core/lib/multivm/src/versions/vm_1_3_2/event_sink.rs index db6c5d11aee4..cbf7c183d3a7 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/event_sink.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/event_sink.rs @@ -1,8 +1,5 @@ -use crate::vm_1_3_2::{ - history_recorder::{AppDataFrameManagerWithHistory, HistoryEnabled, HistoryMode}, - oracles::OracleWithHistory, -}; use std::collections::HashMap; + use zk_evm_1_3_3::{ abstractions::EventSink, aux_structures::{LogQuery, Timestamp}, @@ -12,6 +9,11 @@ use zk_evm_1_3_3::{ }, }; +use crate::vm_1_3_2::{ + history_recorder::{AppDataFrameManagerWithHistory, HistoryEnabled, HistoryMode}, + oracles::OracleWithHistory, +}; + #[derive(Debug, Clone, PartialEq, Default)] pub struct InMemoryEventSink { frames_stack: AppDataFrameManagerWithHistory, H>, diff --git a/core/lib/multivm/src/versions/vm_1_3_2/history_recorder.rs b/core/lib/multivm/src/versions/vm_1_3_2/history_recorder.rs index 3c83b68e0a38..263c1f023dd2 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/history_recorder.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/history_recorder.rs @@ -5,7 +5,6 @@ use zk_evm_1_3_3::{ vm_state::PrimitiveValue, zkevm_opcode_defs::{self}, }; - use zksync_state::{StoragePtr, WriteStorage}; use zksync_types::{StorageKey, U256}; use zksync_utils::{h256_to_u256, u256_to_h256}; @@ -765,12 +764,13 @@ impl HistoryRecorder, H> { #[cfg(test)] mod tests { + use zk_evm_1_3_3::{aux_structures::Timestamp, vm_state::PrimitiveValue}; + use zksync_types::U256; + use crate::vm_1_3_2::{ history_recorder::{HistoryRecorder, MemoryWrapper}, HistoryDisabled, }; - use zk_evm_1_3_3::{aux_structures::Timestamp, vm_state::PrimitiveValue}; - use zksync_types::U256; #[test] fn memory_equality() { diff --git a/core/lib/multivm/src/versions/vm_1_3_2/memory.rs b/core/lib/multivm/src/versions/vm_1_3_2/memory.rs index b269ba89b3c7..91fdbe223d8b 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/memory.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/memory.rs @@ -1,15 +1,19 @@ -use zk_evm_1_3_3::abstractions::{Memory, MemoryType}; -use zk_evm_1_3_3::aux_structures::{MemoryPage, MemoryQuery, Timestamp}; -use zk_evm_1_3_3::vm_state::PrimitiveValue; -use zk_evm_1_3_3::zkevm_opcode_defs::FatPointer; +use zk_evm_1_3_3::{ + abstractions::{Memory, MemoryType}, + aux_structures::{MemoryPage, MemoryQuery, Timestamp}, + vm_state::PrimitiveValue, + zkevm_opcode_defs::FatPointer, +}; use zksync_types::U256; -use crate::vm_1_3_2::history_recorder::{ - FramedStack, HistoryEnabled, HistoryMode, IntFrameManagerWithHistory, MemoryWithHistory, - MemoryWrapper, WithHistory, +use crate::vm_1_3_2::{ + history_recorder::{ + FramedStack, HistoryEnabled, HistoryMode, IntFrameManagerWithHistory, MemoryWithHistory, + MemoryWrapper, WithHistory, + }, + oracles::OracleWithHistory, + utils::{aux_heap_page_from_base, heap_page_from_base, stack_page_from_base}, }; -use crate::vm_1_3_2::oracles::OracleWithHistory; -use crate::vm_1_3_2::utils::{aux_heap_page_from_base, heap_page_from_base, stack_page_from_base}; #[derive(Debug, Clone, PartialEq)] pub struct SimpleMemory { diff --git a/core/lib/multivm/src/versions/vm_1_3_2/mod.rs b/core/lib/multivm/src/versions/vm_1_3_2/mod.rs index 24e433d9123d..37c5f34ffd0e 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/mod.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/mod.rs @@ -1,5 +1,18 @@ #![allow(clippy::derive_partial_eq_without_eq)] +pub use zk_evm_1_3_3::{self, block_properties::BlockProperties}; +pub use zksync_types::vm_trace::VmExecutionTrace; + +pub(crate) use self::vm_instance::VmInstance; +pub use self::{ + errors::TxRevertReason, + history_recorder::{HistoryDisabled, HistoryEnabled, HistoryMode}, + oracle_tools::OracleTools, + oracles::storage::StorageOracle, + vm::Vm, + vm_instance::{VmBlockResult, VmExecutionResult}, +}; + mod bootloader_state; pub mod errors; pub mod event_sink; @@ -11,25 +24,13 @@ pub mod oracles; mod pubdata_utils; mod refunds; pub mod test_utils; -pub mod transaction_data; -pub mod utils; -pub mod vm_with_bootloader; - #[cfg(test)] mod tests; +pub mod transaction_data; +pub mod utils; mod vm; pub mod vm_instance; - -pub use errors::TxRevertReason; -pub use history_recorder::{HistoryDisabled, HistoryEnabled, HistoryMode}; -pub use oracle_tools::OracleTools; -pub use oracles::storage::StorageOracle; -pub use vm::Vm; -pub(crate) use vm_instance::VmInstance; -pub use vm_instance::{VmBlockResult, VmExecutionResult}; -pub use zk_evm_1_3_3; -pub use zk_evm_1_3_3::block_properties::BlockProperties; -pub use zksync_types::vm_trace::VmExecutionTrace; +pub mod vm_with_bootloader; pub type Word = zksync_types::U256; diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracle_tools.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracle_tools.rs index 9f0f2600c5bb..f271d86474cb 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracle_tools.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracle_tools.rs @@ -1,15 +1,18 @@ use std::fmt::Debug; -use crate::vm_1_3_2::event_sink::InMemoryEventSink; -use crate::vm_1_3_2::history_recorder::HistoryMode; -use crate::vm_1_3_2::memory::SimpleMemory; -use crate::vm_1_3_2::oracles::{ - decommitter::DecommitterOracle, precompile::PrecompilesProcessorWithHistory, - storage::StorageOracle, -}; use zk_evm_1_3_3::witness_trace::DummyTracer; use zksync_state::{StoragePtr, WriteStorage}; +use crate::vm_1_3_2::{ + event_sink::InMemoryEventSink, + history_recorder::HistoryMode, + memory::SimpleMemory, + oracles::{ + decommitter::DecommitterOracle, precompile::PrecompilesProcessorWithHistory, + storage::StorageOracle, + }, +}; + /// zkEVM requires a bunch of objects implementing given traits to work. /// For example: Storage, Memory, PrecompilerProcessor etc /// (you can find all these traits in zk_evm crate -> src/abstractions/mod.rs) diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/decommitter.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/decommitter.rs index 17583b70dc9f..d58b501b244d 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/decommitter.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/decommitter.rs @@ -1,21 +1,19 @@ use std::collections::HashMap; -use crate::vm_1_3_2::history_recorder::{ - HistoryEnabled, HistoryMode, HistoryRecorder, WithHistory, -}; - -use zk_evm_1_3_3::abstractions::MemoryType; -use zk_evm_1_3_3::aux_structures::Timestamp; use zk_evm_1_3_3::{ - abstractions::{DecommittmentProcessor, Memory}, - aux_structures::{DecommittmentQuery, MemoryIndex, MemoryLocation, MemoryPage, MemoryQuery}, + abstractions::{DecommittmentProcessor, Memory, MemoryType}, + aux_structures::{ + DecommittmentQuery, MemoryIndex, MemoryLocation, MemoryPage, MemoryQuery, Timestamp, + }, }; use zksync_state::{StoragePtr, WriteStorage}; use zksync_types::U256; -use zksync_utils::bytecode::bytecode_len_in_words; -use zksync_utils::{bytes_to_be_words, u256_to_h256}; +use zksync_utils::{bytecode::bytecode_len_in_words, bytes_to_be_words, u256_to_h256}; use super::OracleWithHistory; +use crate::vm_1_3_2::history_recorder::{ + HistoryEnabled, HistoryMode, HistoryRecorder, WithHistory, +}; /// The main job of the DecommiterOracle is to implement the DecommittmentProcessor trait - that is /// used by the VM to 'load' bytecodes into memory. diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/mod.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/mod.rs index 342fadb554a2..08eb1c6e1747 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/mod.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/mod.rs @@ -1,11 +1,10 @@ use zk_evm_1_3_3::aux_structures::Timestamp; -// We will discard RAM as soon as the execution of a tx ends, so -// it is ok for now to use SimpleMemory -pub use zk_evm_1_3_3::reference_impls::memory::SimpleMemory as RamOracle; // All the changes to the events in the DB will be applied after the tx is executed, // so fow now it is fine. pub use zk_evm_1_3_3::reference_impls::event_sink::InMemoryEventSink as EventSinkOracle; - +// We will discard RAM as soon as the execution of a tx ends, so +// it is ok for now to use SimpleMemory +pub use zk_evm_1_3_3::reference_impls::memory::SimpleMemory as RamOracle; pub use zk_evm_1_3_3::testing::simple_tracer::NoopTracer; pub mod decommitter; diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/precompile.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/precompile.rs index 0693fac6d60e..8089527183f7 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/precompile.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/precompile.rs @@ -1,14 +1,11 @@ use zk_evm_1_3_3::{ - abstractions::Memory, - abstractions::PrecompileCyclesWitness, - abstractions::PrecompilesProcessor, + abstractions::{Memory, PrecompileCyclesWitness, PrecompilesProcessor}, aux_structures::{LogQuery, MemoryQuery, Timestamp}, precompiles::DefaultPrecompilesProcessor, }; -use crate::vm_1_3_2::history_recorder::{HistoryEnabled, HistoryMode, HistoryRecorder}; - use super::OracleWithHistory; +use crate::vm_1_3_2::history_recorder::{HistoryEnabled, HistoryMode, HistoryRecorder}; /// Wrap of DefaultPrecompilesProcessor that store queue /// of timestamp when precompiles are called to be executed. diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/storage.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/storage.rs index 9a4873fe59a6..ea2ecf83a3d6 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/storage.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/storage.rs @@ -1,25 +1,22 @@ use std::collections::HashMap; -use crate::vm_1_3_2::history_recorder::{ - AppDataFrameManagerWithHistory, HashMapHistoryEvent, HistoryEnabled, HistoryMode, - HistoryRecorder, StorageWrapper, WithHistory, -}; - -use zk_evm_1_3_3::abstractions::RefundedAmounts; -use zk_evm_1_3_3::zkevm_opcode_defs::system_params::INITIAL_STORAGE_WRITE_PUBDATA_BYTES; use zk_evm_1_3_3::{ - abstractions::{RefundType, Storage as VmStorageOracle}, + abstractions::{RefundType, RefundedAmounts, Storage as VmStorageOracle}, aux_structures::{LogQuery, Timestamp}, + zkevm_opcode_defs::system_params::INITIAL_STORAGE_WRITE_PUBDATA_BYTES, }; use zksync_state::{StoragePtr, WriteStorage}; -use zksync_types::utils::storage_key_for_eth_balance; use zksync_types::{ - AccountTreeId, Address, StorageKey, StorageLogQuery, StorageLogQueryType, BOOTLOADER_ADDRESS, - U256, + utils::storage_key_for_eth_balance, AccountTreeId, Address, StorageKey, StorageLogQuery, + StorageLogQueryType, BOOTLOADER_ADDRESS, U256, }; use zksync_utils::u256_to_h256; use super::OracleWithHistory; +use crate::vm_1_3_2::history_recorder::{ + AppDataFrameManagerWithHistory, HashMapHistoryEvent, HistoryEnabled, HistoryMode, + HistoryRecorder, StorageWrapper, WithHistory, +}; // While the storage does not support different shards, it was decided to write the // code of the StorageOracle with the shard parameters in mind. diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/bootloader.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/bootloader.rs index 16b1efdff54b..3bc80f311194 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/bootloader.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/bootloader.rs @@ -1,12 +1,5 @@ use std::marker::PhantomData; -use crate::vm_1_3_2::history_recorder::HistoryMode; -use crate::vm_1_3_2::memory::SimpleMemory; -use crate::vm_1_3_2::oracles::tracer::{ - utils::gas_spent_on_bytecodes_and_long_messages_this_opcode, ExecutionEndTracer, - PendingRefundTracer, PubdataSpentTracer, StorageInvocationTracer, -}; - use zk_evm_1_3_3::{ tracing::{ AfterDecodingData, AfterExecutionData, BeforeExecutionData, Tracer, VmLocalStateData, @@ -16,6 +9,15 @@ use zk_evm_1_3_3::{ zkevm_opcode_defs::{Opcode, RetOpcode}, }; +use crate::vm_1_3_2::{ + history_recorder::HistoryMode, + memory::SimpleMemory, + oracles::tracer::{ + utils::gas_spent_on_bytecodes_and_long_messages_this_opcode, ExecutionEndTracer, + PendingRefundTracer, PubdataSpentTracer, StorageInvocationTracer, + }, +}; + /// Tells the VM to end the execution before `ret` from the bootloader if there is no panic or revert. /// Also, saves the information if this `ret` was caused by "out of gas" panic. #[derive(Debug, Clone, Default)] diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/call.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/call.rs index 72701f6e0f29..88b21818fc34 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/call.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/call.rs @@ -1,20 +1,23 @@ -use crate::vm_1_3_2::errors::VmRevertReason; -use crate::vm_1_3_2::history_recorder::HistoryMode; -use crate::vm_1_3_2::memory::SimpleMemory; -use std::convert::TryFrom; -use std::marker::PhantomData; -use std::mem; -use zk_evm_1_3_3::tracing::{ - AfterDecodingData, AfterExecutionData, BeforeExecutionData, Tracer, VmLocalStateData, -}; -use zk_evm_1_3_3::zkevm_opcode_defs::FatPointer; -use zk_evm_1_3_3::zkevm_opcode_defs::{ - FarCallABI, FarCallOpcode, Opcode, RetOpcode, CALL_IMPLICIT_CALLDATA_FAT_PTR_REGISTER, - RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER, +use std::{convert::TryFrom, marker::PhantomData, mem}; + +use zk_evm_1_3_3::{ + tracing::{ + AfterDecodingData, AfterExecutionData, BeforeExecutionData, Tracer, VmLocalStateData, + }, + zkevm_opcode_defs::{ + FarCallABI, FarCallOpcode, FatPointer, Opcode, RetOpcode, + CALL_IMPLICIT_CALLDATA_FAT_PTR_REGISTER, RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER, + }, }; use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS; -use zksync_types::vm_trace::{Call, CallType}; -use zksync_types::U256; +use zksync_types::{ + vm_trace::{Call, CallType}, + U256, +}; + +use crate::vm_1_3_2::{ + errors::VmRevertReason, history_recorder::HistoryMode, memory::SimpleMemory, +}; /// NOTE Auto implementing clone for this tracer can cause stack overflow. /// This is because of the stack field which is a Vec with nested vecs inside. @@ -282,9 +285,10 @@ fn filter_near_call(mut call: Call) -> Vec { #[cfg(test)] mod tests { - use crate::vm_1_3_2::oracles::tracer::call::{filter_near_call, Call, CallType}; use zk_evm_1_3_3::zkevm_opcode_defs::FarCallOpcode; + use crate::vm_1_3_2::oracles::tracer::call::{filter_near_call, Call, CallType}; + #[test] fn test_filter_near_calls() { let mut call = Call::default(); diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/mod.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/mod.rs index 29121f35c5fe..5395a0a9d7ba 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/mod.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/mod.rs @@ -1,5 +1,15 @@ -use zk_evm_1_3_3::tracing::Tracer; -use zk_evm_1_3_3::vm_state::VmLocalState; +use zk_evm_1_3_3::{tracing::Tracer, vm_state::VmLocalState}; + +pub(crate) use self::transaction_result::TransactionResultTracer; +pub use self::{ + bootloader::BootloaderTracer, + call::CallTracer, + one_tx::OneTxTracer, + validation::{ + ValidationError, ValidationTracer, ValidationTracerParams, ViolatedValidationRule, + }, +}; +use crate::vm_1_3_2::{history_recorder::HistoryMode, memory::SimpleMemory}; mod bootloader; mod call; @@ -8,18 +18,6 @@ mod transaction_result; mod utils; mod validation; -pub use bootloader::BootloaderTracer; -pub use call::CallTracer; -pub use one_tx::OneTxTracer; -pub use validation::{ - ValidationError, ValidationTracer, ValidationTracerParams, ViolatedValidationRule, -}; - -pub(crate) use transaction_result::TransactionResultTracer; - -use crate::vm_1_3_2::history_recorder::HistoryMode; -use crate::vm_1_3_2::memory::SimpleMemory; - pub trait ExecutionEndTracer: Tracer> { // Returns whether the vm execution should stop. fn should_stop_execution(&self) -> bool; diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/one_tx.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/one_tx.rs index a9349ea20356..896befb8abc5 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/one_tx.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/one_tx.rs @@ -1,23 +1,23 @@ +use zk_evm_1_3_3::{ + tracing::{ + AfterDecodingData, AfterExecutionData, BeforeExecutionData, Tracer, VmLocalStateData, + }, + vm_state::VmLocalState, +}; +use zksync_types::vm_trace::Call; + use super::utils::{computational_gas_price, print_debug_if_needed}; use crate::vm_1_3_2::{ history_recorder::HistoryMode, memory::SimpleMemory, oracles::tracer::{ utils::{gas_spent_on_bytecodes_and_long_messages_this_opcode, VmHook}, - BootloaderTracer, ExecutionEndTracer, PendingRefundTracer, PubdataSpentTracer, + BootloaderTracer, CallTracer, ExecutionEndTracer, PendingRefundTracer, PubdataSpentTracer, + StorageInvocationTracer, }, vm_instance::get_vm_hook_params, }; -use crate::vm_1_3_2::oracles::tracer::{CallTracer, StorageInvocationTracer}; -use zk_evm_1_3_3::{ - tracing::{ - AfterDecodingData, AfterExecutionData, BeforeExecutionData, Tracer, VmLocalStateData, - }, - vm_state::VmLocalState, -}; -use zksync_types::vm_trace::Call; - /// Allows any opcodes, but tells the VM to end the execution once the tx is over. // Internally depeds on Bootloader's VMHooks to get the notification once the transaction is finished. #[derive(Debug)] diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/transaction_result.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/transaction_result.rs index 215c66bfa745..c74e9bb862d9 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/transaction_result.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/transaction_result.rs @@ -7,18 +7,18 @@ use zk_evm_1_3_3::{ }; use zksync_types::{vm_trace, U256}; -use crate::vm_1_3_2::memory::SimpleMemory; -use crate::vm_1_3_2::oracles::tracer::{ - CallTracer, ExecutionEndTracer, PendingRefundTracer, PubdataSpentTracer, - StorageInvocationTracer, -}; -use crate::vm_1_3_2::vm_instance::get_vm_hook_params; use crate::vm_1_3_2::{ history_recorder::HistoryMode, - oracles::tracer::utils::{ - gas_spent_on_bytecodes_and_long_messages_this_opcode, print_debug_if_needed, read_pointer, - VmHook, + memory::SimpleMemory, + oracles::tracer::{ + utils::{ + gas_spent_on_bytecodes_and_long_messages_this_opcode, print_debug_if_needed, + read_pointer, VmHook, + }, + CallTracer, ExecutionEndTracer, PendingRefundTracer, PubdataSpentTracer, + StorageInvocationTracer, }, + vm_instance::get_vm_hook_params, }; #[derive(Debug)] diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/utils.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/utils.rs index 3b3b99991ed2..76890b042de1 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/utils.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/utils.rs @@ -1,14 +1,9 @@ -use crate::vm_1_3_2::history_recorder::HistoryMode; -use crate::vm_1_3_2::memory::SimpleMemory; -use crate::vm_1_3_2::utils::{aux_heap_page_from_base, heap_page_from_base}; -use crate::vm_1_3_2::vm_instance::{get_vm_hook_params, VM_HOOK_POSITION}; -use crate::vm_1_3_2::vm_with_bootloader::BOOTLOADER_HEAP_PAGE; - -use zk_evm_1_3_3::aux_structures::MemoryPage; -use zk_evm_1_3_3::zkevm_opcode_defs::{FarCallABI, FarCallForwardPageType}; use zk_evm_1_3_3::{ + aux_structures::MemoryPage, tracing::{BeforeExecutionData, VmLocalStateData}, - zkevm_opcode_defs::{FatPointer, LogOpcode, Opcode, UMAOpcode}, + zkevm_opcode_defs::{ + FarCallABI, FarCallForwardPageType, FatPointer, LogOpcode, Opcode, UMAOpcode, + }, }; use zksync_system_constants::{ ECRECOVER_PRECOMPILE_ADDRESS, KECCAK256_PRECOMPILE_ADDRESS, KNOWN_CODES_STORAGE_ADDRESS, @@ -17,6 +12,14 @@ use zksync_system_constants::{ use zksync_types::U256; use zksync_utils::u256_to_h256; +use crate::vm_1_3_2::{ + history_recorder::HistoryMode, + memory::SimpleMemory, + utils::{aux_heap_page_from_base, heap_page_from_base}, + vm_instance::{get_vm_hook_params, VM_HOOK_POSITION}, + vm_with_bootloader::BOOTLOADER_HEAP_PAGE, +}; + #[derive(Clone, Debug, Copy)] pub(crate) enum VmHook { AccountValidationEntered, diff --git a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/validation.rs b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/validation.rs index c9ee54f35ba4..d3308c7ea2dc 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/validation.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/oracles/tracer/validation.rs @@ -1,15 +1,5 @@ use std::{collections::HashSet, fmt::Display, marker::PhantomData}; -use crate::vm_1_3_2::{ - errors::VmRevertReasonParsingResult, - history_recorder::HistoryMode, - memory::SimpleMemory, - oracles::tracer::{ - utils::{computational_gas_price, print_debug_if_needed, VmHook}, - ExecutionEndTracer, PendingRefundTracer, PubdataSpentTracer, - }, -}; - use zk_evm_1_3_3::{ tracing::{ AfterDecodingData, AfterExecutionData, BeforeExecutionData, Tracer, VmLocalStateData, @@ -17,8 +7,6 @@ use zk_evm_1_3_3::{ zkevm_opcode_defs::{ContextOpcode, FarCallABI, LogOpcode, Opcode}, }; use zksync_state::{StoragePtr, WriteStorage}; - -use crate::vm_1_3_2::oracles::tracer::{utils::get_calldata_page_via_abi, StorageInvocationTracer}; use zksync_system_constants::{ ACCOUNT_CODE_STORAGE_ADDRESS, BOOTLOADER_ADDRESS, CONTRACT_DEPLOYER_ADDRESS, KECCAK256_PRECOMPILE_ADDRESS, L2_ETH_TOKEN_ADDRESS, MSG_VALUE_SIMULATOR_ADDRESS, @@ -31,6 +19,18 @@ use zksync_utils::{ be_bytes_to_safe_address, h256_to_account_address, u256_to_account_address, u256_to_h256, }; +use crate::vm_1_3_2::{ + errors::VmRevertReasonParsingResult, + history_recorder::HistoryMode, + memory::SimpleMemory, + oracles::tracer::{ + utils::{ + computational_gas_price, get_calldata_page_via_abi, print_debug_if_needed, VmHook, + }, + ExecutionEndTracer, PendingRefundTracer, PubdataSpentTracer, StorageInvocationTracer, + }, +}; + #[derive(Debug, Clone, Eq, PartialEq, Copy)] #[allow(clippy::enum_variant_names)] pub enum ValidationTracerMode { diff --git a/core/lib/multivm/src/versions/vm_1_3_2/pubdata_utils.rs b/core/lib/multivm/src/versions/vm_1_3_2/pubdata_utils.rs index 936b85bfc097..23d42fc2b5a3 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/pubdata_utils.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/pubdata_utils.rs @@ -1,14 +1,18 @@ -use crate::vm_1_3_2::history_recorder::HistoryMode; -use crate::vm_1_3_2::oracles::storage::storage_key_of_log; -use crate::vm_1_3_2::VmInstance; use std::collections::HashMap; + use zk_evm_1_3_3::aux_structures::Timestamp; use zksync_state::WriteStorage; -use zksync_types::event::{extract_long_l2_to_l1_messages, extract_published_bytecodes}; -use zksync_types::zkevm_test_harness::witness::sort_storage_access::sort_storage_access_queries; -use zksync_types::{StorageKey, PUBLISH_BYTECODE_OVERHEAD, SYSTEM_CONTEXT_ADDRESS}; +use zksync_types::{ + event::{extract_long_l2_to_l1_messages, extract_published_bytecodes}, + zkevm_test_harness::witness::sort_storage_access::sort_storage_access_queries, + StorageKey, PUBLISH_BYTECODE_OVERHEAD, SYSTEM_CONTEXT_ADDRESS, +}; use zksync_utils::bytecode::bytecode_len_in_bytes; +use crate::vm_1_3_2::{ + history_recorder::HistoryMode, oracles::storage::storage_key_of_log, VmInstance, +}; + impl VmInstance { pub fn pubdata_published(&self, from_timestamp: Timestamp) -> u32 { let storage_writes_pubdata_published = self.pubdata_published_for_writes(from_timestamp); diff --git a/core/lib/multivm/src/versions/vm_1_3_2/refunds.rs b/core/lib/multivm/src/versions/vm_1_3_2/refunds.rs index 0277379143ba..9de2ee9676bf 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/refunds.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/refunds.rs @@ -1,13 +1,14 @@ -use crate::vm_1_3_2::history_recorder::HistoryMode; -use crate::vm_1_3_2::vm_with_bootloader::{ - eth_price_per_pubdata_byte, BOOTLOADER_HEAP_PAGE, TX_GAS_LIMIT_OFFSET, -}; -use crate::vm_1_3_2::VmInstance; use zk_evm_1_3_3::aux_structures::Timestamp; use zksync_state::WriteStorage; use zksync_types::U256; use zksync_utils::ceil_div_u256; +use crate::vm_1_3_2::{ + history_recorder::HistoryMode, + vm_with_bootloader::{eth_price_per_pubdata_byte, BOOTLOADER_HEAP_PAGE, TX_GAS_LIMIT_OFFSET}, + VmInstance, +}; + impl VmInstance { pub(crate) fn tx_body_refund( &self, diff --git a/core/lib/multivm/src/versions/vm_1_3_2/test_utils.rs b/core/lib/multivm/src/versions/vm_1_3_2/test_utils.rs index e697e3b310d8..c3aa161543aa 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/test_utils.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/test_utils.rs @@ -10,8 +10,10 @@ use std::collections::HashMap; use itertools::Itertools; use zk_evm_1_3_3::{aux_structures::Timestamp, vm_state::VmLocalState}; -use zksync_contracts::test_contracts::LoadnextContractExecutionParams; -use zksync_contracts::{deployer_contract, get_loadnext_contract, load_contract}; +use zksync_contracts::{ + deployer_contract, get_loadnext_contract, load_contract, + test_contracts::LoadnextContractExecutionParams, +}; use zksync_state::WriteStorage; use zksync_types::{ ethabi::{Address, Token}, diff --git a/core/lib/multivm/src/versions/vm_1_3_2/transaction_data.rs b/core/lib/multivm/src/versions/vm_1_3_2/transaction_data.rs index d3a96dc06a7c..f2c8f278f56d 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/transaction_data.rs @@ -1,12 +1,14 @@ use zk_evm_1_3_3::zkevm_opcode_defs::system_params::MAX_TX_ERGS_LIMIT; -use zksync_types::ethabi::{encode, Address, Token}; -use zksync_types::fee::encoding_len; use zksync_types::{ - l1::is_l1_tx_type, l2::TransactionType, ExecuteTransactionCommon, Transaction, U256, + ethabi::{encode, Address, Token}, + fee::encoding_len, + l1::is_l1_tx_type, + l2::TransactionType, + ExecuteTransactionCommon, Transaction, MAX_L2_TX_GAS_LIMIT, MAX_TXS_IN_BLOCK, U256, +}; +use zksync_utils::{ + address_to_h256, bytecode::hash_bytecode, bytes_to_be_words, ceil_div_u256, h256_to_u256, }; -use zksync_types::{MAX_L2_TX_GAS_LIMIT, MAX_TXS_IN_BLOCK}; -use zksync_utils::{address_to_h256, ceil_div_u256}; -use zksync_utils::{bytecode::hash_bytecode, bytes_to_be_words, h256_to_u256}; use crate::vm_1_3_2::vm_with_bootloader::{ BLOCK_OVERHEAD_GAS, BLOCK_OVERHEAD_PUBDATA, BOOTLOADER_TX_ENCODING_SPACE, diff --git a/core/lib/multivm/src/versions/vm_1_3_2/utils.rs b/core/lib/multivm/src/versions/vm_1_3_2/utils.rs index 44be1b9c8b90..d0a51cde7fa7 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/utils.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/utils.rs @@ -1,13 +1,7 @@ -use crate::vm_1_3_2::history_recorder::HistoryMode; -use crate::vm_1_3_2::{ - memory::SimpleMemory, oracles::tracer::PubdataSpentTracer, vm_with_bootloader::BlockContext, - VmInstance, -}; use once_cell::sync::Lazy; - -use zk_evm_1_3_3::block_properties::BlockProperties; use zk_evm_1_3_3::{ aux_structures::{MemoryPage, Timestamp}, + block_properties::BlockProperties, vm_state::PrimitiveValue, zkevm_opcode_defs::FatPointer, }; @@ -17,6 +11,11 @@ use zksync_system_constants::ZKPORTER_IS_AVAILABLE; use zksync_types::{Address, H160, MAX_L2_TX_GAS_LIMIT, U256}; use zksync_utils::h256_to_u256; +use crate::vm_1_3_2::{ + history_recorder::HistoryMode, memory::SimpleMemory, oracles::tracer::PubdataSpentTracer, + vm_with_bootloader::BlockContext, VmInstance, +}; + pub const INITIAL_TIMESTAMP: u32 = 1024; pub const INITIAL_MEMORY_COUNTER: u32 = 2048; pub const INITIAL_CALLDATA_PAGE: u32 = 7; diff --git a/core/lib/multivm/src/versions/vm_1_3_2/vm.rs b/core/lib/multivm/src/versions/vm_1_3_2/vm.rs index 84b84d3e31a4..f0cf5d9c1aa2 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/vm.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/vm.rs @@ -1,21 +1,24 @@ -use crate::interface::{ - BootloaderMemory, BytecodeCompressionError, CurrentExecutionState, FinishedL1Batch, L1BatchEnv, - L2BlockEnv, SystemEnv, TxExecutionMode, VmExecutionMode, VmExecutionResultAndLogs, VmInterface, - VmInterfaceHistoryEnabled, VmMemoryMetrics, -}; - use std::collections::HashSet; use zksync_state::{StoragePtr, WriteStorage}; -use zksync_types::l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}; -use zksync_types::Transaction; -use zksync_utils::bytecode::{hash_bytecode, CompressedBytecodeInfo}; -use zksync_utils::{h256_to_u256, u256_to_h256}; - -use crate::glue::history_mode::HistoryMode; -use crate::glue::GlueInto; -use crate::vm_1_3_2::events::merge_events; -use crate::vm_1_3_2::VmInstance; +use zksync_types::{ + l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}, + Transaction, +}; +use zksync_utils::{ + bytecode::{hash_bytecode, CompressedBytecodeInfo}, + h256_to_u256, u256_to_h256, +}; + +use crate::{ + glue::{history_mode::HistoryMode, GlueInto}, + interface::{ + BootloaderMemory, BytecodeCompressionError, CurrentExecutionState, FinishedL1Batch, + L1BatchEnv, L2BlockEnv, SystemEnv, TxExecutionMode, VmExecutionMode, + VmExecutionResultAndLogs, VmInterface, VmInterfaceHistoryEnabled, VmMemoryMetrics, + }, + vm_1_3_2::{events::merge_events, VmInstance}, +}; #[derive(Debug)] pub struct Vm { diff --git a/core/lib/multivm/src/versions/vm_1_3_2/vm_instance.rs b/core/lib/multivm/src/versions/vm_1_3_2/vm_instance.rs index 8b7c416522e6..2217b4f50d66 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/vm_instance.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/vm_instance.rs @@ -1,43 +1,52 @@ -use std::convert::TryFrom; -use std::fmt::Debug; - -use zk_evm_1_3_3::aux_structures::Timestamp; -use zk_evm_1_3_3::vm_state::{PrimitiveValue, VmLocalState, VmState}; -use zk_evm_1_3_3::witness_trace::DummyTracer; -use zk_evm_1_3_3::zkevm_opcode_defs::decoding::{ - AllowedPcOrImm, EncodingModeProduction, VmEncodingMode, +use std::{convert::TryFrom, fmt::Debug}; + +use zk_evm_1_3_3::{ + aux_structures::Timestamp, + vm_state::{PrimitiveValue, VmLocalState, VmState}, + witness_trace::DummyTracer, + zkevm_opcode_defs::{ + decoding::{AllowedPcOrImm, EncodingModeProduction, VmEncodingMode}, + definitions::RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER, + }, }; -use zk_evm_1_3_3::zkevm_opcode_defs::definitions::RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER; use zksync_state::WriteStorage; use zksync_system_constants::MAX_TXS_IN_BLOCK; -use zksync_types::l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}; -use zksync_types::tx::tx_execution_info::TxExecutionStatus; -use zksync_types::vm_trace::{Call, VmExecutionTrace, VmTrace}; -use zksync_types::{L1BatchNumber, StorageLogQuery, VmEvent, H256, U256}; - -use crate::interface::types::outputs::VmExecutionLogs; -use crate::vm_1_3_2::bootloader_state::BootloaderState; -use crate::vm_1_3_2::errors::{TxRevertReason, VmRevertReason, VmRevertReasonParsingResult}; -use crate::vm_1_3_2::event_sink::InMemoryEventSink; -use crate::vm_1_3_2::events::merge_events; -use crate::vm_1_3_2::history_recorder::{HistoryEnabled, HistoryMode}; -use crate::vm_1_3_2::memory::SimpleMemory; -use crate::vm_1_3_2::oracles::decommitter::DecommitterOracle; -use crate::vm_1_3_2::oracles::precompile::PrecompilesProcessorWithHistory; -use crate::vm_1_3_2::oracles::storage::StorageOracle; -use crate::vm_1_3_2::oracles::tracer::{ - BootloaderTracer, ExecutionEndTracer, OneTxTracer, PendingRefundTracer, PubdataSpentTracer, - StorageInvocationTracer, TransactionResultTracer, ValidationError, ValidationTracer, - ValidationTracerParams, +use zksync_types::{ + l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}, + tx::tx_execution_info::TxExecutionStatus, + vm_trace::{Call, VmExecutionTrace, VmTrace}, + L1BatchNumber, StorageLogQuery, VmEvent, H256, U256, }; -use crate::vm_1_3_2::oracles::OracleWithHistory; -use crate::vm_1_3_2::utils::{ - calculate_computational_gas_used, dump_memory_page_using_primitive_value, - precompile_calls_count_after_timestamp, -}; -use crate::vm_1_3_2::vm_with_bootloader::{ - BootloaderJobType, DerivedBlockContext, TxExecutionMode, BOOTLOADER_HEAP_PAGE, - OPERATOR_REFUNDS_OFFSET, + +use crate::{ + interface::types::outputs::VmExecutionLogs, + vm_1_3_2::{ + bootloader_state::BootloaderState, + errors::{TxRevertReason, VmRevertReason, VmRevertReasonParsingResult}, + event_sink::InMemoryEventSink, + events::merge_events, + history_recorder::{HistoryEnabled, HistoryMode}, + memory::SimpleMemory, + oracles::{ + decommitter::DecommitterOracle, + precompile::PrecompilesProcessorWithHistory, + storage::StorageOracle, + tracer::{ + BootloaderTracer, ExecutionEndTracer, OneTxTracer, PendingRefundTracer, + PubdataSpentTracer, StorageInvocationTracer, TransactionResultTracer, + ValidationError, ValidationTracer, ValidationTracerParams, + }, + OracleWithHistory, + }, + utils::{ + calculate_computational_gas_used, dump_memory_page_using_primitive_value, + precompile_calls_count_after_timestamp, + }, + vm_with_bootloader::{ + BootloaderJobType, DerivedBlockContext, TxExecutionMode, BOOTLOADER_HEAP_PAGE, + OPERATOR_REFUNDS_OFFSET, + }, + }, }; pub type ZkSyncVmState = VmState< diff --git a/core/lib/multivm/src/versions/vm_1_3_2/vm_with_bootloader.rs b/core/lib/multivm/src/versions/vm_1_3_2/vm_with_bootloader.rs index c2ff035c6698..71c108cae326 100644 --- a/core/lib/multivm/src/versions/vm_1_3_2/vm_with_bootloader.rs +++ b/core/lib/multivm/src/versions/vm_1_3_2/vm_with_bootloader.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; +use itertools::Itertools; use zk_evm_1_3_3::{ aux_structures::{MemoryPage, Timestamp}, block_properties::BlockProperties, @@ -11,8 +12,8 @@ use zk_evm_1_3_3::{ }, }; use zksync_contracts::BaseSystemContracts; +use zksync_state::WriteStorage; use zksync_system_constants::MAX_TXS_IN_BLOCK; - use zksync_types::{ l1::is_l1_tx_type, zkevm_test_harness::INITIAL_MONOTONIC_CYCLE_COUNTER, Address, Transaction, BOOTLOADER_ADDRESS, L1_GAS_PER_PUBDATA_BYTE, MAX_GAS_PER_PUBDATA_BYTE, MAX_NEW_FACTORY_DEPS, @@ -25,9 +26,6 @@ use zksync_utils::{ misc::ceil_div, }; -use itertools::Itertools; -use zksync_state::WriteStorage; - use crate::vm_1_3_2::{ bootloader_state::BootloaderState, history_recorder::HistoryMode, diff --git a/core/lib/multivm/src/versions/vm_latest/bootloader_state/l2_block.rs b/core/lib/multivm/src/versions/vm_latest/bootloader_state/l2_block.rs index 6da9b64673e1..146e8713c69e 100644 --- a/core/lib/multivm/src/versions/vm_latest/bootloader_state/l2_block.rs +++ b/core/lib/multivm/src/versions/vm_latest/bootloader_state/l2_block.rs @@ -1,11 +1,15 @@ use std::cmp::Ordering; + use zksync_types::{MiniblockNumber, H256}; use zksync_utils::concat_and_hash; -use crate::interface::{L2Block, L2BlockEnv}; -use crate::vm_latest::bootloader_state::snapshot::L2BlockSnapshot; -use crate::vm_latest::bootloader_state::tx::BootloaderTx; -use crate::vm_latest::utils::l2_blocks::l2_block_hash; +use crate::{ + interface::{L2Block, L2BlockEnv}, + vm_latest::{ + bootloader_state::{snapshot::L2BlockSnapshot, tx::BootloaderTx}, + utils::l2_blocks::l2_block_hash, + }, +}; const EMPTY_TXS_ROLLING_HASH: H256 = H256::zero(); diff --git a/core/lib/multivm/src/versions/vm_latest/bootloader_state/state.rs b/core/lib/multivm/src/versions/vm_latest/bootloader_state/state.rs index b4641d9bc649..14e693a1d2e3 100644 --- a/core/lib/multivm/src/versions/vm_latest/bootloader_state/state.rs +++ b/core/lib/multivm/src/versions/vm_latest/bootloader_state/state.rs @@ -1,20 +1,24 @@ -use crate::vm_latest::bootloader_state::l2_block::BootloaderL2Block; -use crate::vm_latest::bootloader_state::snapshot::BootloaderStateSnapshot; -use crate::vm_latest::bootloader_state::utils::{apply_l2_block, apply_tx_to_memory}; -use once_cell::sync::OnceCell; use std::cmp::Ordering; + +use once_cell::sync::OnceCell; use zksync_types::{L2ChainId, U256}; use zksync_utils::bytecode::CompressedBytecodeInfo; -use crate::interface::{BootloaderMemory, L2BlockEnv, TxExecutionMode}; -use crate::vm_latest::types::internals::pubdata::PubdataInput; -use crate::vm_latest::{ - constants::TX_DESCRIPTION_OFFSET, types::internals::TransactionData, - utils::l2_blocks::assert_next_block, +use super::{tx::BootloaderTx, utils::apply_pubdata_to_memory}; +use crate::{ + interface::{BootloaderMemory, L2BlockEnv, TxExecutionMode}, + vm_latest::{ + bootloader_state::{ + l2_block::BootloaderL2Block, + snapshot::BootloaderStateSnapshot, + utils::{apply_l2_block, apply_tx_to_memory}, + }, + constants::TX_DESCRIPTION_OFFSET, + types::internals::{pubdata::PubdataInput, TransactionData}, + utils::l2_blocks::assert_next_block, + }, }; -use super::tx::BootloaderTx; -use super::utils::apply_pubdata_to_memory; /// Intermediate bootloader-related VM state. /// /// Required to process transactions one by one (since we intercept the VM execution to execute diff --git a/core/lib/multivm/src/versions/vm_latest/bootloader_state/tx.rs b/core/lib/multivm/src/versions/vm_latest/bootloader_state/tx.rs index dce0ecce3fbc..21aee75b38b5 100644 --- a/core/lib/multivm/src/versions/vm_latest/bootloader_state/tx.rs +++ b/core/lib/multivm/src/versions/vm_latest/bootloader_state/tx.rs @@ -1,7 +1,8 @@ -use crate::vm_latest::types::internals::TransactionData; use zksync_types::{L2ChainId, H256, U256}; use zksync_utils::bytecode::CompressedBytecodeInfo; +use crate::vm_latest::types::internals::TransactionData; + /// Information about tx necessary for execution in bootloader. #[derive(Debug, Clone)] pub(super) struct BootloaderTx { diff --git a/core/lib/multivm/src/versions/vm_latest/bootloader_state/utils.rs b/core/lib/multivm/src/versions/vm_latest/bootloader_state/utils.rs index 7e76f3faeffb..16776be444e0 100644 --- a/core/lib/multivm/src/versions/vm_latest/bootloader_state/utils.rs +++ b/core/lib/multivm/src/versions/vm_latest/bootloader_state/utils.rs @@ -1,18 +1,21 @@ use zksync_types::U256; -use zksync_utils::bytecode::CompressedBytecodeInfo; -use zksync_utils::{bytes_to_be_words, h256_to_u256}; - -use crate::interface::{BootloaderMemory, TxExecutionMode}; -use crate::vm_latest::bootloader_state::l2_block::BootloaderL2Block; -use crate::vm_latest::constants::{ - BOOTLOADER_TX_DESCRIPTION_OFFSET, BOOTLOADER_TX_DESCRIPTION_SIZE, COMPRESSED_BYTECODES_OFFSET, - OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_OFFSET, OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_SLOTS, - OPERATOR_REFUNDS_OFFSET, TX_DESCRIPTION_OFFSET, TX_OPERATOR_L2_BLOCK_INFO_OFFSET, - TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO, TX_OVERHEAD_OFFSET, TX_TRUSTED_GAS_LIMIT_OFFSET, -}; -use crate::vm_latest::types::internals::pubdata::PubdataInput; +use zksync_utils::{bytecode::CompressedBytecodeInfo, bytes_to_be_words, h256_to_u256}; use super::tx::BootloaderTx; +use crate::{ + interface::{BootloaderMemory, TxExecutionMode}, + vm_latest::{ + bootloader_state::l2_block::BootloaderL2Block, + constants::{ + BOOTLOADER_TX_DESCRIPTION_OFFSET, BOOTLOADER_TX_DESCRIPTION_SIZE, + COMPRESSED_BYTECODES_OFFSET, OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_OFFSET, + OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_SLOTS, OPERATOR_REFUNDS_OFFSET, + TX_DESCRIPTION_OFFSET, TX_OPERATOR_L2_BLOCK_INFO_OFFSET, + TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO, TX_OVERHEAD_OFFSET, TX_TRUSTED_GAS_LIMIT_OFFSET, + }, + types::internals::pubdata::PubdataInput, + }, +}; pub(super) fn get_memory_for_compressed_bytecodes( compressed_bytecodes: &[CompressedBytecodeInfo], diff --git a/core/lib/multivm/src/versions/vm_latest/constants.rs b/core/lib/multivm/src/versions/vm_latest/constants.rs index 4d1c77054237..44266344be61 100644 --- a/core/lib/multivm/src/versions/vm_latest/constants.rs +++ b/core/lib/multivm/src/versions/vm_latest/constants.rs @@ -1,14 +1,12 @@ use zk_evm_1_4_0::aux_structures::MemoryPage; - +pub use zk_evm_1_4_0::zkevm_opcode_defs::system_params::{ + ERGS_PER_CIRCUIT, INITIAL_STORAGE_WRITE_PUBDATA_BYTES, MAX_PUBDATA_PER_BLOCK, +}; use zksync_system_constants::{ L1_GAS_PER_PUBDATA_BYTE, MAX_L2_TX_GAS_LIMIT, MAX_NEW_FACTORY_DEPS, MAX_TXS_IN_BLOCK, USED_BOOTLOADER_MEMORY_WORDS, }; -pub use zk_evm_1_4_0::zkevm_opcode_defs::system_params::{ - ERGS_PER_CIRCUIT, INITIAL_STORAGE_WRITE_PUBDATA_BYTES, MAX_PUBDATA_PER_BLOCK, -}; - use crate::vm_latest::old_vm::utils::heap_page_from_base; /// Max cycles for a single transaction. diff --git a/core/lib/multivm/src/versions/vm_latest/implementation/bytecode.rs b/core/lib/multivm/src/versions/vm_latest/implementation/bytecode.rs index 83a7be748978..bda1803067fb 100644 --- a/core/lib/multivm/src/versions/vm_latest/implementation/bytecode.rs +++ b/core/lib/multivm/src/versions/vm_latest/implementation/bytecode.rs @@ -1,13 +1,12 @@ use itertools::Itertools; - -use crate::interface::VmInterface; -use crate::HistoryMode; use zksync_state::{StoragePtr, WriteStorage}; use zksync_types::U256; -use zksync_utils::bytecode::{compress_bytecode, hash_bytecode, CompressedBytecodeInfo}; -use zksync_utils::bytes_to_be_words; +use zksync_utils::{ + bytecode::{compress_bytecode, hash_bytecode, CompressedBytecodeInfo}, + bytes_to_be_words, +}; -use crate::vm_latest::Vm; +use crate::{interface::VmInterface, vm_latest::Vm, HistoryMode}; impl Vm { /// Checks the last transaction has successfully published compressed bytecodes and returns `true` if there is at least one is still unknown. diff --git a/core/lib/multivm/src/versions/vm_latest/implementation/execution.rs b/core/lib/multivm/src/versions/vm_latest/implementation/execution.rs index 1b3197f57b92..a913ea3ed463 100644 --- a/core/lib/multivm/src/versions/vm_latest/implementation/execution.rs +++ b/core/lib/multivm/src/versions/vm_latest/implementation/execution.rs @@ -1,15 +1,19 @@ -use crate::HistoryMode; use zk_evm_1_4_0::aux_structures::Timestamp; use zksync_state::WriteStorage; -use crate::interface::{ - types::tracer::{TracerExecutionStatus, VmExecutionStopReason}, - VmExecutionMode, VmExecutionResultAndLogs, -}; -use crate::vm_latest::{ - old_vm::utils::{vm_may_have_ended_inner, VmExecutionResult}, - tracers::{dispatcher::TracerDispatcher, DefaultExecutionTracer, PubdataTracer, RefundsTracer}, - vm::Vm, +use crate::{ + interface::{ + types::tracer::{TracerExecutionStatus, VmExecutionStopReason}, + VmExecutionMode, VmExecutionResultAndLogs, + }, + vm_latest::{ + old_vm::utils::{vm_may_have_ended_inner, VmExecutionResult}, + tracers::{ + dispatcher::TracerDispatcher, DefaultExecutionTracer, PubdataTracer, RefundsTracer, + }, + vm::Vm, + }, + HistoryMode, }; impl Vm { diff --git a/core/lib/multivm/src/versions/vm_latest/implementation/gas.rs b/core/lib/multivm/src/versions/vm_latest/implementation/gas.rs index c970cd4e5d24..526eab76f07f 100644 --- a/core/lib/multivm/src/versions/vm_latest/implementation/gas.rs +++ b/core/lib/multivm/src/versions/vm_latest/implementation/gas.rs @@ -1,8 +1,9 @@ -use crate::HistoryMode; use zksync_state::WriteStorage; -use crate::vm_latest::tracers::DefaultExecutionTracer; -use crate::vm_latest::vm::Vm; +use crate::{ + vm_latest::{tracers::DefaultExecutionTracer, vm::Vm}, + HistoryMode, +}; impl Vm { /// Returns the amount of gas remaining to the VM. diff --git a/core/lib/multivm/src/versions/vm_latest/implementation/logs.rs b/core/lib/multivm/src/versions/vm_latest/implementation/logs.rs index c468cf87817c..9e0817aa9394 100644 --- a/core/lib/multivm/src/versions/vm_latest/implementation/logs.rs +++ b/core/lib/multivm/src/versions/vm_latest/implementation/logs.rs @@ -1,16 +1,16 @@ use zk_evm_1_4_0::aux_structures::Timestamp; use zksync_state::WriteStorage; -use zksync_types::event::extract_l2tol1logs_from_l1_messenger; +use zksync_types::{ + event::extract_l2tol1logs_from_l1_messenger, + l2_to_l1_log::{L2ToL1Log, SystemL2ToL1Log, UserL2ToL1Log}, + VmEvent, +}; -use crate::HistoryMode; -use zksync_types::l2_to_l1_log::{L2ToL1Log, SystemL2ToL1Log, UserL2ToL1Log}; -use zksync_types::VmEvent; - -use crate::interface::types::outputs::VmExecutionLogs; - -use crate::vm_latest::old_vm::utils::precompile_calls_count_after_timestamp; -use crate::vm_latest::utils::logs; -use crate::vm_latest::vm::Vm; +use crate::{ + interface::types::outputs::VmExecutionLogs, + vm_latest::{old_vm::utils::precompile_calls_count_after_timestamp, utils::logs, vm::Vm}, + HistoryMode, +}; impl Vm { pub(crate) fn collect_execution_logs_after_timestamp( diff --git a/core/lib/multivm/src/versions/vm_latest/implementation/snapshots.rs b/core/lib/multivm/src/versions/vm_latest/implementation/snapshots.rs index 99d41a2aec6f..b6b452834388 100644 --- a/core/lib/multivm/src/versions/vm_latest/implementation/snapshots.rs +++ b/core/lib/multivm/src/versions/vm_latest/implementation/snapshots.rs @@ -1,7 +1,6 @@ -use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics}; - use std::time::Duration; +use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics}; use zk_evm_1_4_0::aux_structures::Timestamp; use zksync_state::WriteStorage; diff --git a/core/lib/multivm/src/versions/vm_latest/implementation/statistics.rs b/core/lib/multivm/src/versions/vm_latest/implementation/statistics.rs index 92604479a88e..6af9ad041feb 100644 --- a/core/lib/multivm/src/versions/vm_latest/implementation/statistics.rs +++ b/core/lib/multivm/src/versions/vm_latest/implementation/statistics.rs @@ -1,12 +1,12 @@ use zk_evm_1_4_0::aux_structures::Timestamp; use zksync_state::WriteStorage; - -use crate::HistoryMode; use zksync_types::U256; -use crate::interface::{VmExecutionStatistics, VmMemoryMetrics}; -use crate::vm_latest::tracers::DefaultExecutionTracer; -use crate::vm_latest::vm::Vm; +use crate::{ + interface::{VmExecutionStatistics, VmMemoryMetrics}, + vm_latest::{tracers::DefaultExecutionTracer, vm::Vm}, + HistoryMode, +}; /// Module responsible for observing the VM behavior, i.e. calculating the statistics of the VM runs /// or reporting the VM memory usage. diff --git a/core/lib/multivm/src/versions/vm_latest/implementation/tx.rs b/core/lib/multivm/src/versions/vm_latest/implementation/tx.rs index 6def1da0f5d7..326be41c5ee7 100644 --- a/core/lib/multivm/src/versions/vm_latest/implementation/tx.rs +++ b/core/lib/multivm/src/versions/vm_latest/implementation/tx.rs @@ -1,13 +1,16 @@ -use crate::vm_latest::constants::BOOTLOADER_HEAP_PAGE; -use crate::vm_latest::implementation::bytecode::{bytecode_to_factory_dep, compress_bytecodes}; -use crate::HistoryMode; use zk_evm_1_4_0::aux_structures::Timestamp; use zksync_state::WriteStorage; -use zksync_types::l1::is_l1_tx_type; -use zksync_types::Transaction; +use zksync_types::{l1::is_l1_tx_type, Transaction}; -use crate::vm_latest::types::internals::TransactionData; -use crate::vm_latest::vm::Vm; +use crate::{ + vm_latest::{ + constants::BOOTLOADER_HEAP_PAGE, + implementation::bytecode::{bytecode_to_factory_dep, compress_bytecodes}, + types::internals::TransactionData, + vm::Vm, + }, + HistoryMode, +}; impl Vm { pub(crate) fn push_raw_transaction( diff --git a/core/lib/multivm/src/versions/vm_latest/mod.rs b/core/lib/multivm/src/versions/vm_latest/mod.rs index 49cd7111f6f3..0b4919f83d7b 100644 --- a/core/lib/multivm/src/versions/vm_latest/mod.rs +++ b/core/lib/multivm/src/versions/vm_latest/mod.rs @@ -1,15 +1,18 @@ -pub use old_vm::{ - history_recorder::{HistoryDisabled, HistoryEnabled, HistoryMode}, - memory::SimpleMemory, -}; - -pub use oracles::storage::StorageOracle; - -pub use tracers::{ - dispatcher::TracerDispatcher, - traits::{ToTracerPointer, TracerPointer, VmTracer}, +pub use self::{ + bootloader_state::BootloaderState, + old_vm::{ + history_recorder::{HistoryDisabled, HistoryEnabled, HistoryMode}, + memory::SimpleMemory, + }, + oracles::storage::StorageOracle, + tracers::{ + dispatcher::TracerDispatcher, + traits::{ToTracerPointer, TracerPointer, VmTracer}, + }, + types::internals::ZkSyncVmState, + utils::transaction_encoding::TransactionVmExt, + vm::Vm, }; - pub use crate::interface::types::{ inputs::{L1BatchEnv, L2BlockEnv, SystemEnv, TxExecutionMode, VmExecutionMode}, outputs::{ @@ -17,23 +20,15 @@ pub use crate::interface::types::{ Refunds, VmExecutionLogs, VmExecutionResultAndLogs, VmExecutionStatistics, VmMemoryMetrics, }, }; -pub use types::internals::ZkSyncVmState; -pub use utils::transaction_encoding::TransactionVmExt; - -pub use bootloader_state::BootloaderState; - -pub use vm::Vm; mod bootloader_state; +pub mod constants; mod implementation; mod old_vm; mod oracles; +#[cfg(test)] +mod tests; pub(crate) mod tracers; mod types; -mod vm; - -pub mod constants; pub mod utils; - -#[cfg(test)] -mod tests; +mod vm; diff --git a/core/lib/multivm/src/versions/vm_latest/old_vm/event_sink.rs b/core/lib/multivm/src/versions/vm_latest/old_vm/event_sink.rs index 4174d9f4f170..8e7f4d447b4d 100644 --- a/core/lib/multivm/src/versions/vm_latest/old_vm/event_sink.rs +++ b/core/lib/multivm/src/versions/vm_latest/old_vm/event_sink.rs @@ -1,9 +1,6 @@ -use crate::vm_latest::old_vm::{ - history_recorder::{AppDataFrameManagerWithHistory, HistoryEnabled, HistoryMode}, - oracles::OracleWithHistory, -}; -use itertools::Itertools; use std::collections::HashMap; + +use itertools::Itertools; use zk_evm_1_4_0::{ abstractions::EventSink, aux_structures::{LogQuery, Timestamp}, @@ -14,6 +11,11 @@ use zk_evm_1_4_0::{ }; use zksync_types::U256; +use crate::vm_latest::old_vm::{ + history_recorder::{AppDataFrameManagerWithHistory, HistoryEnabled, HistoryMode}, + oracles::OracleWithHistory, +}; + #[derive(Debug, Clone, PartialEq, Default)] pub struct InMemoryEventSink { frames_stack: AppDataFrameManagerWithHistory, H>, diff --git a/core/lib/multivm/src/versions/vm_latest/old_vm/history_recorder.rs b/core/lib/multivm/src/versions/vm_latest/old_vm/history_recorder.rs index 7c0490044d6c..2253831b7454 100644 --- a/core/lib/multivm/src/versions/vm_latest/old_vm/history_recorder.rs +++ b/core/lib/multivm/src/versions/vm_latest/old_vm/history_recorder.rs @@ -5,7 +5,6 @@ use zk_evm_1_4_0::{ vm_state::PrimitiveValue, zkevm_opcode_defs::{self}, }; - use zksync_state::{StoragePtr, WriteStorage}; use zksync_types::{StorageKey, U256}; use zksync_utils::{h256_to_u256, u256_to_h256}; @@ -771,11 +770,14 @@ impl HistoryRecorder, H> { #[cfg(test)] mod tests { - use crate::vm_latest::old_vm::history_recorder::{HistoryRecorder, MemoryWrapper}; - use crate::vm_latest::HistoryDisabled; use zk_evm_1_4_0::{aux_structures::Timestamp, vm_state::PrimitiveValue}; use zksync_types::U256; + use crate::vm_latest::{ + old_vm::history_recorder::{HistoryRecorder, MemoryWrapper}, + HistoryDisabled, + }; + #[test] fn memory_equality() { let mut a: HistoryRecorder = Default::default(); diff --git a/core/lib/multivm/src/versions/vm_latest/old_vm/memory.rs b/core/lib/multivm/src/versions/vm_latest/old_vm/memory.rs index 5694a725d932..5a7592ce9654 100644 --- a/core/lib/multivm/src/versions/vm_latest/old_vm/memory.rs +++ b/core/lib/multivm/src/versions/vm_latest/old_vm/memory.rs @@ -1,16 +1,18 @@ -use zk_evm_1_4_0::abstractions::{Memory, MemoryType}; -use zk_evm_1_4_0::aux_structures::{MemoryPage, MemoryQuery, Timestamp}; -use zk_evm_1_4_0::vm_state::PrimitiveValue; -use zk_evm_1_4_0::zkevm_opcode_defs::FatPointer; +use zk_evm_1_4_0::{ + abstractions::{Memory, MemoryType}, + aux_structures::{MemoryPage, MemoryQuery, Timestamp}, + vm_state::PrimitiveValue, + zkevm_opcode_defs::FatPointer, +}; use zksync_types::U256; -use crate::vm_latest::old_vm::history_recorder::{ - FramedStack, HistoryEnabled, HistoryMode, IntFrameManagerWithHistory, MemoryWithHistory, - MemoryWrapper, WithHistory, -}; -use crate::vm_latest::old_vm::oracles::OracleWithHistory; -use crate::vm_latest::old_vm::utils::{ - aux_heap_page_from_base, heap_page_from_base, stack_page_from_base, +use crate::vm_latest::old_vm::{ + history_recorder::{ + FramedStack, HistoryEnabled, HistoryMode, IntFrameManagerWithHistory, MemoryWithHistory, + MemoryWrapper, WithHistory, + }, + oracles::OracleWithHistory, + utils::{aux_heap_page_from_base, heap_page_from_base, stack_page_from_base}, }; #[derive(Debug, Clone, PartialEq)] diff --git a/core/lib/multivm/src/versions/vm_latest/old_vm/oracles/decommitter.rs b/core/lib/multivm/src/versions/vm_latest/old_vm/oracles/decommitter.rs index c679532fa763..4a718917a21d 100644 --- a/core/lib/multivm/src/versions/vm_latest/old_vm/oracles/decommitter.rs +++ b/core/lib/multivm/src/versions/vm_latest/old_vm/oracles/decommitter.rs @@ -1,23 +1,19 @@ -use std::collections::HashMap; -use std::fmt::Debug; +use std::{collections::HashMap, fmt::Debug}; -use crate::vm_latest::old_vm::history_recorder::{ - HistoryEnabled, HistoryMode, HistoryRecorder, WithHistory, -}; - -use zk_evm_1_4_0::abstractions::MemoryType; -use zk_evm_1_4_0::aux_structures::Timestamp; use zk_evm_1_4_0::{ - abstractions::{DecommittmentProcessor, Memory}, - aux_structures::{DecommittmentQuery, MemoryIndex, MemoryLocation, MemoryPage, MemoryQuery}, + abstractions::{DecommittmentProcessor, Memory, MemoryType}, + aux_structures::{ + DecommittmentQuery, MemoryIndex, MemoryLocation, MemoryPage, MemoryQuery, Timestamp, + }, }; - use zksync_state::{ReadStorage, StoragePtr}; use zksync_types::U256; -use zksync_utils::bytecode::bytecode_len_in_words; -use zksync_utils::{bytes_to_be_words, u256_to_h256}; +use zksync_utils::{bytecode::bytecode_len_in_words, bytes_to_be_words, u256_to_h256}; use super::OracleWithHistory; +use crate::vm_latest::old_vm::history_recorder::{ + HistoryEnabled, HistoryMode, HistoryRecorder, WithHistory, +}; /// The main job of the DecommiterOracle is to implement the DecommittmentProcessor trait - that is /// used by the VM to 'load' bytecodes into memory. diff --git a/core/lib/multivm/src/versions/vm_latest/old_vm/oracles/precompile.rs b/core/lib/multivm/src/versions/vm_latest/old_vm/oracles/precompile.rs index ed3621fc4975..92b88e40fc95 100644 --- a/core/lib/multivm/src/versions/vm_latest/old_vm/oracles/precompile.rs +++ b/core/lib/multivm/src/versions/vm_latest/old_vm/oracles/precompile.rs @@ -1,14 +1,11 @@ use zk_evm_1_4_0::{ - abstractions::Memory, - abstractions::PrecompileCyclesWitness, - abstractions::PrecompilesProcessor, + abstractions::{Memory, PrecompileCyclesWitness, PrecompilesProcessor}, aux_structures::{LogQuery, MemoryQuery, Timestamp}, zk_evm_abstractions::precompiles::DefaultPrecompilesProcessor, }; -use crate::vm_latest::old_vm::history_recorder::{HistoryEnabled, HistoryMode, HistoryRecorder}; - use super::OracleWithHistory; +use crate::vm_latest::old_vm::history_recorder::{HistoryEnabled, HistoryMode, HistoryRecorder}; /// Wrap of DefaultPrecompilesProcessor that store queue /// of timestamp when precompiles are called to be executed. diff --git a/core/lib/multivm/src/versions/vm_latest/old_vm/utils.rs b/core/lib/multivm/src/versions/vm_latest/old_vm/utils.rs index afaa19cac870..1dbe82a81d4f 100644 --- a/core/lib/multivm/src/versions/vm_latest/old_vm/utils.rs +++ b/core/lib/multivm/src/versions/vm_latest/old_vm/utils.rs @@ -1,22 +1,19 @@ -use crate::vm_latest::old_vm::memory::SimpleMemory; - -use crate::vm_latest::types::internals::ZkSyncVmState; -use crate::vm_latest::HistoryMode; - -use zk_evm_1_4_0::zkevm_opcode_defs::decoding::{ - AllowedPcOrImm, EncodingModeProduction, VmEncodingMode, -}; -use zk_evm_1_4_0::zkevm_opcode_defs::RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER; use zk_evm_1_4_0::{ aux_structures::{MemoryPage, Timestamp}, vm_state::PrimitiveValue, - zkevm_opcode_defs::FatPointer, + zkevm_opcode_defs::{ + decoding::{AllowedPcOrImm, EncodingModeProduction, VmEncodingMode}, + FatPointer, RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER, + }, }; use zksync_state::WriteStorage; use zksync_system_constants::L1_GAS_PER_PUBDATA_BYTE; - use zksync_types::{Address, U256}; +use crate::vm_latest::{ + old_vm::memory::SimpleMemory, types::internals::ZkSyncVmState, HistoryMode, +}; + #[derive(Debug, Clone)] pub(crate) enum VmExecutionResult { Ok(Vec), diff --git a/core/lib/multivm/src/versions/vm_latest/oracles/storage.rs b/core/lib/multivm/src/versions/vm_latest/oracles/storage.rs index beec2fa086fc..60516083d29a 100644 --- a/core/lib/multivm/src/versions/vm_latest/oracles/storage.rs +++ b/core/lib/multivm/src/versions/vm_latest/oracles/storage.rs @@ -1,28 +1,30 @@ use std::collections::HashMap; -use crate::vm_latest::old_vm::history_recorder::{ - AppDataFrameManagerWithHistory, HashMapHistoryEvent, HistoryEnabled, HistoryMode, - HistoryRecorder, StorageWrapper, VectorHistoryEvent, WithHistory, -}; -use crate::vm_latest::old_vm::oracles::OracleWithHistory; - -use zk_evm_1_4_0::abstractions::RefundedAmounts; -use zk_evm_1_4_0::zkevm_opcode_defs::system_params::INITIAL_STORAGE_WRITE_PUBDATA_BYTES; use zk_evm_1_4_0::{ - abstractions::{RefundType, Storage as VmStorageOracle}, + abstractions::{RefundType, RefundedAmounts, Storage as VmStorageOracle}, aux_structures::{LogQuery, Timestamp}, + zkevm_opcode_defs::system_params::INITIAL_STORAGE_WRITE_PUBDATA_BYTES, }; - use zksync_state::{StoragePtr, WriteStorage}; -use zksync_types::utils::storage_key_for_eth_balance; -use zksync_types::writes::compression::compress_with_best_strategy; -use zksync_types::writes::{BYTES_PER_DERIVED_KEY, BYTES_PER_ENUMERATION_INDEX}; use zksync_types::{ + utils::storage_key_for_eth_balance, + writes::{ + compression::compress_with_best_strategy, BYTES_PER_DERIVED_KEY, + BYTES_PER_ENUMERATION_INDEX, + }, AccountTreeId, Address, StorageKey, StorageLogQuery, StorageLogQueryType, BOOTLOADER_ADDRESS, U256, }; use zksync_utils::u256_to_h256; +use crate::vm_latest::old_vm::{ + history_recorder::{ + AppDataFrameManagerWithHistory, HashMapHistoryEvent, HistoryEnabled, HistoryMode, + HistoryRecorder, StorageWrapper, VectorHistoryEvent, WithHistory, + }, + oracles::OracleWithHistory, +}; + // While the storage does not support different shards, it was decided to write the // code of the StorageOracle with the shard parameters in mind. pub(crate) fn triplet_to_storage_key(_shard_id: u8, address: Address, key: U256) -> StorageKey { diff --git a/core/lib/multivm/src/versions/vm_latest/tests/bootloader.rs b/core/lib/multivm/src/versions/vm_latest/tests/bootloader.rs index b2763f358bee..78fb964f7221 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/bootloader.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/bootloader.rs @@ -1,15 +1,17 @@ use zksync_types::U256; -use crate::interface::{Halt, TxExecutionMode, VmExecutionMode, VmInterface}; -use crate::vm_latest::constants::BOOTLOADER_HEAP_PAGE; -use crate::vm_latest::tests::tester::VmTesterBuilder; -use crate::vm_latest::tests::utils::{ - get_bootloader, verify_required_memory, BASE_SYSTEM_CONTRACTS, +use crate::{ + interface::{ExecutionResult, Halt, TxExecutionMode, VmExecutionMode, VmInterface}, + vm_latest::{ + constants::BOOTLOADER_HEAP_PAGE, + tests::{ + tester::VmTesterBuilder, + utils::{get_bootloader, verify_required_memory, BASE_SYSTEM_CONTRACTS}, + }, + HistoryEnabled, + }, }; -use crate::interface::ExecutionResult; -use crate::vm_latest::HistoryEnabled; - #[test] fn test_dummy_bootloader() { let mut base_system_contracts = BASE_SYSTEM_CONTRACTS.clone(); diff --git a/core/lib/multivm/src/versions/vm_latest/tests/bytecode_publishing.rs b/core/lib/multivm/src/versions/vm_latest/tests/bytecode_publishing.rs index e574a881d911..a0c10addff93 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/bytecode_publishing.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/bytecode_publishing.rs @@ -1,10 +1,16 @@ use zksync_types::event::extract_long_l2_to_l1_messages; use zksync_utils::bytecode::compress_bytecode; -use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; -use crate::vm_latest::tests::tester::{DeployContractsTx, TxType, VmTesterBuilder}; -use crate::vm_latest::tests::utils::read_test_contract; -use crate::vm_latest::HistoryEnabled; +use crate::{ + interface::{TxExecutionMode, VmExecutionMode, VmInterface}, + vm_latest::{ + tests::{ + tester::{DeployContractsTx, TxType, VmTesterBuilder}, + utils::read_test_contract, + }, + HistoryEnabled, + }, +}; #[test] fn test_bytecode_publishing() { diff --git a/core/lib/multivm/src/versions/vm_latest/tests/call_tracer.rs b/core/lib/multivm/src/versions/vm_latest/tests/call_tracer.rs index e5b1ce15fcd3..2f8f37e081bd 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/call_tracer.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/call_tracer.rs @@ -1,13 +1,21 @@ -use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; -use crate::tracers::CallTracer; -use crate::vm_latest::constants::BLOCK_GAS_LIMIT; -use crate::vm_latest::tests::tester::VmTesterBuilder; -use crate::vm_latest::tests::utils::{read_max_depth_contract, read_test_contract}; -use crate::vm_latest::{HistoryEnabled, ToTracerPointer}; -use once_cell::sync::OnceCell; use std::sync::Arc; + +use once_cell::sync::OnceCell; use zksync_types::{Address, Execute}; +use crate::{ + interface::{TxExecutionMode, VmExecutionMode, VmInterface}, + tracers::CallTracer, + vm_latest::{ + constants::BLOCK_GAS_LIMIT, + tests::{ + tester::VmTesterBuilder, + utils::{read_max_depth_contract, read_test_contract}, + }, + HistoryEnabled, ToTracerPointer, + }, +}; + // This test is ultra slow, so it's ignored by default. #[test] #[ignore] diff --git a/core/lib/multivm/src/versions/vm_latest/tests/default_aa.rs b/core/lib/multivm/src/versions/vm_latest/tests/default_aa.rs index b31e32270d9a..7c951e313210 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/default_aa.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/default_aa.rs @@ -1,13 +1,21 @@ use zksync_system_constants::L2_ETH_TOKEN_ADDRESS; -use zksync_types::system_contracts::{DEPLOYMENT_NONCE_INCREMENT, TX_NONCE_INCREMENT}; - -use zksync_types::{get_code_key, get_known_code_key, get_nonce_key, AccountTreeId, U256}; +use zksync_types::{ + get_code_key, get_known_code_key, get_nonce_key, + system_contracts::{DEPLOYMENT_NONCE_INCREMENT, TX_NONCE_INCREMENT}, + AccountTreeId, U256, +}; use zksync_utils::u256_to_h256; -use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; -use crate::vm_latest::tests::tester::{DeployContractsTx, TxType, VmTesterBuilder}; -use crate::vm_latest::tests::utils::{get_balance, read_test_contract, verify_required_storage}; -use crate::vm_latest::HistoryEnabled; +use crate::{ + interface::{TxExecutionMode, VmExecutionMode, VmInterface}, + vm_latest::{ + tests::{ + tester::{DeployContractsTx, TxType, VmTesterBuilder}, + utils::{get_balance, read_test_contract, verify_required_storage}, + }, + HistoryEnabled, + }, +}; #[test] fn test_default_aa_interaction() { diff --git a/core/lib/multivm/src/versions/vm_latest/tests/gas_limit.rs b/core/lib/multivm/src/versions/vm_latest/tests/gas_limit.rs index 6bebffeaceeb..533d9ec660eb 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/gas_limit.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/gas_limit.rs @@ -1,13 +1,13 @@ -use zksync_types::fee::Fee; -use zksync_types::Execute; +use zksync_types::{fee::Fee, Execute}; -use crate::vm_latest::constants::{ - BOOTLOADER_HEAP_PAGE, TX_DESCRIPTION_OFFSET, TX_GAS_LIMIT_OFFSET, +use crate::{ + interface::{TxExecutionMode, VmInterface}, + vm_latest::{ + constants::{BOOTLOADER_HEAP_PAGE, TX_DESCRIPTION_OFFSET, TX_GAS_LIMIT_OFFSET}, + tests::tester::VmTesterBuilder, + HistoryDisabled, + }, }; -use crate::vm_latest::tests::tester::VmTesterBuilder; - -use crate::interface::{TxExecutionMode, VmInterface}; -use crate::vm_latest::HistoryDisabled; /// Checks that `TX_GAS_LIMIT_OFFSET` constant is correct. #[test] diff --git a/core/lib/multivm/src/versions/vm_latest/tests/get_used_contracts.rs b/core/lib/multivm/src/versions/vm_latest/tests/get_used_contracts.rs index 688711d5a9c7..b82057bef8b7 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/get_used_contracts.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/get_used_contracts.rs @@ -1,19 +1,23 @@ use std::collections::{HashMap, HashSet}; use itertools::Itertools; - -use crate::HistoryMode; use zksync_state::WriteStorage; use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS; use zksync_test_account::Account; use zksync_types::{Execute, U256}; -use zksync_utils::bytecode::hash_bytecode; -use zksync_utils::h256_to_u256; - -use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; -use crate::vm_latest::tests::tester::{TxType, VmTesterBuilder}; -use crate::vm_latest::tests::utils::{read_test_contract, BASE_SYSTEM_CONTRACTS}; -use crate::vm_latest::{HistoryDisabled, Vm}; +use zksync_utils::{bytecode::hash_bytecode, h256_to_u256}; + +use crate::{ + interface::{TxExecutionMode, VmExecutionMode, VmInterface}, + vm_latest::{ + tests::{ + tester::{TxType, VmTesterBuilder}, + utils::{read_test_contract, BASE_SYSTEM_CONTRACTS}, + }, + HistoryDisabled, Vm, + }, + HistoryMode, +}; #[test] fn test_get_used_contracts() { diff --git a/core/lib/multivm/src/versions/vm_latest/tests/is_write_initial.rs b/core/lib/multivm/src/versions/vm_latest/tests/is_write_initial.rs index d40f9109dcb7..d5a6679502b5 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/is_write_initial.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/is_write_initial.rs @@ -1,10 +1,16 @@ use zksync_state::ReadStorage; use zksync_types::get_nonce_key; -use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; -use crate::vm_latest::tests::tester::{Account, TxType, VmTesterBuilder}; -use crate::vm_latest::tests::utils::read_test_contract; -use crate::vm_latest::HistoryDisabled; +use crate::{ + interface::{TxExecutionMode, VmExecutionMode, VmInterface}, + vm_latest::{ + tests::{ + tester::{Account, TxType, VmTesterBuilder}, + utils::read_test_contract, + }, + HistoryDisabled, + }, +}; #[test] fn test_is_write_initial_behaviour() { diff --git a/core/lib/multivm/src/versions/vm_latest/tests/l1_tx_execution.rs b/core/lib/multivm/src/versions/vm_latest/tests/l1_tx_execution.rs index 5c1bdbad58af..4f61dd90fad7 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/l1_tx_execution.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/l1_tx_execution.rs @@ -1,16 +1,23 @@ use zksync_system_constants::BOOTLOADER_ADDRESS; -use zksync_types::l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}; -use zksync_types::storage_writes_deduplicator::StorageWritesDeduplicator; -use zksync_types::{get_code_key, get_known_code_key, U256}; +use zksync_types::{ + get_code_key, get_known_code_key, + l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}, + storage_writes_deduplicator::StorageWritesDeduplicator, + U256, +}; use zksync_utils::u256_to_h256; -use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; -use crate::vm_latest::tests::tester::{TxType, VmTesterBuilder}; -use crate::vm_latest::tests::utils::{ - read_test_contract, verify_required_storage, BASE_SYSTEM_CONTRACTS, +use crate::{ + interface::{TxExecutionMode, VmExecutionMode, VmInterface}, + vm_latest::{ + tests::{ + tester::{TxType, VmTesterBuilder}, + utils::{read_test_contract, verify_required_storage, BASE_SYSTEM_CONTRACTS}, + }, + types::internals::TransactionData, + HistoryEnabled, + }, }; -use crate::vm_latest::types::internals::TransactionData; -use crate::vm_latest::HistoryEnabled; #[test] fn test_l1_tx_execution() { diff --git a/core/lib/multivm/src/versions/vm_latest/tests/l2_blocks.rs b/core/lib/multivm/src/versions/vm_latest/tests/l2_blocks.rs index 4fd4e0207d4d..81939d402ff5 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/l2_blocks.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/l2_blocks.rs @@ -3,23 +3,11 @@ //! The description for each of the tests can be found in the corresponding `.yul` file. //! -use crate::interface::{ - ExecutionResult, Halt, L2BlockEnv, TxExecutionMode, VmExecutionMode, VmInterface, -}; -use crate::vm_latest::constants::{ - BOOTLOADER_HEAP_PAGE, TX_OPERATOR_L2_BLOCK_INFO_OFFSET, TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO, -}; -use crate::vm_latest::tests::tester::default_l1_batch; -use crate::vm_latest::tests::tester::VmTesterBuilder; -use crate::vm_latest::utils::l2_blocks::get_l2_block_hash_key; -use crate::vm_latest::{HistoryEnabled, Vm}; -use crate::HistoryMode; use zk_evm_1_4_0::aux_structures::Timestamp; use zksync_state::WriteStorage; use zksync_system_constants::REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_BYTE; -use zksync_types::block::pack_block_info; use zksync_types::{ - block::{legacy_miniblock_hash, miniblock_hash}, + block::{legacy_miniblock_hash, miniblock_hash, pack_block_info}, AccountTreeId, Execute, ExecuteTransactionCommon, L1BatchNumber, L1TxCommonData, MiniblockNumber, StorageKey, Transaction, H160, H256, SYSTEM_CONTEXT_ADDRESS, SYSTEM_CONTEXT_BLOCK_INFO_POSITION, SYSTEM_CONTEXT_CURRENT_L2_BLOCK_INFO_POSITION, @@ -27,6 +15,20 @@ use zksync_types::{ }; use zksync_utils::{h256_to_u256, u256_to_h256}; +use crate::{ + interface::{ExecutionResult, Halt, L2BlockEnv, TxExecutionMode, VmExecutionMode, VmInterface}, + vm_latest::{ + constants::{ + BOOTLOADER_HEAP_PAGE, TX_OPERATOR_L2_BLOCK_INFO_OFFSET, + TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO, + }, + tests::tester::{default_l1_batch, VmTesterBuilder}, + utils::l2_blocks::get_l2_block_hash_key, + HistoryEnabled, Vm, + }, + HistoryMode, +}; + fn get_l1_noop() -> Transaction { Transaction { common_data: ExecuteTransactionCommon::L1(L1TxCommonData { diff --git a/core/lib/multivm/src/versions/vm_latest/tests/nonce_holder.rs b/core/lib/multivm/src/versions/vm_latest/tests/nonce_holder.rs index dedaae5c933e..2de5e23bdd23 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/nonce_holder.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/nonce_holder.rs @@ -1,12 +1,19 @@ use zksync_types::{Execute, Nonce}; -use crate::interface::VmRevertReason; -use crate::interface::{ExecutionResult, Halt, TxRevertReason, VmExecutionMode}; -use crate::interface::{TxExecutionMode, VmInterface}; -use crate::vm_latest::tests::tester::{Account, VmTesterBuilder}; -use crate::vm_latest::tests::utils::read_nonce_holder_tester; -use crate::vm_latest::types::internals::TransactionData; -use crate::vm_latest::HistoryEnabled; +use crate::{ + interface::{ + ExecutionResult, Halt, TxExecutionMode, TxRevertReason, VmExecutionMode, VmInterface, + VmRevertReason, + }, + vm_latest::{ + tests::{ + tester::{Account, VmTesterBuilder}, + utils::read_nonce_holder_tester, + }, + types::internals::TransactionData, + HistoryEnabled, + }, +}; pub enum NonceHolderTestMode { SetValueUnderNonce, diff --git a/core/lib/multivm/src/versions/vm_latest/tests/refunds.rs b/core/lib/multivm/src/versions/vm_latest/tests/refunds.rs index 9d4afcdb3178..dc1f4fe55bca 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/refunds.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/refunds.rs @@ -1,9 +1,14 @@ -use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; -use crate::vm_latest::tests::tester::{DeployContractsTx, TxType, VmTesterBuilder}; -use crate::vm_latest::tests::utils::read_test_contract; - -use crate::vm_latest::types::internals::TransactionData; -use crate::vm_latest::HistoryEnabled; +use crate::{ + interface::{TxExecutionMode, VmExecutionMode, VmInterface}, + vm_latest::{ + tests::{ + tester::{DeployContractsTx, TxType, VmTesterBuilder}, + utils::read_test_contract, + }, + types::internals::TransactionData, + HistoryEnabled, + }, +}; #[test] fn test_predetermined_refunded_gas() { diff --git a/core/lib/multivm/src/versions/vm_latest/tests/require_eip712.rs b/core/lib/multivm/src/versions/vm_latest/tests/require_eip712.rs index 1ad6f3512068..c03e5fe64212 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/require_eip712.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/require_eip712.rs @@ -1,22 +1,24 @@ use std::convert::TryInto; use ethabi::Token; - -use zksync_eth_signer::raw_ethereum_tx::TransactionParameters; -use zksync_eth_signer::EthereumSigner; +use zksync_eth_signer::{raw_ethereum_tx::TransactionParameters, EthereumSigner}; use zksync_system_constants::L2_ETH_TOKEN_ADDRESS; -use zksync_types::fee::Fee; -use zksync_types::l2::L2Tx; -use zksync_types::transaction_request::TransactionRequest; -use zksync_types::utils::storage_key_for_standard_token_balance; use zksync_types::{ - AccountTreeId, Address, Eip712Domain, Execute, L2ChainId, Nonce, Transaction, U256, + fee::Fee, l2::L2Tx, transaction_request::TransactionRequest, + utils::storage_key_for_standard_token_balance, AccountTreeId, Address, Eip712Domain, Execute, + L2ChainId, Nonce, Transaction, U256, }; -use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface}; -use crate::vm_latest::tests::tester::{Account, VmTester, VmTesterBuilder}; -use crate::vm_latest::tests::utils::read_many_owners_custom_account_contract; -use crate::vm_latest::HistoryDisabled; +use crate::{ + interface::{TxExecutionMode, VmExecutionMode, VmInterface}, + vm_latest::{ + tests::{ + tester::{Account, VmTester, VmTesterBuilder}, + utils::read_many_owners_custom_account_contract, + }, + HistoryDisabled, + }, +}; impl VmTester { pub(crate) fn get_eth_balance(&mut self, address: Address) -> U256 { diff --git a/core/lib/multivm/src/versions/vm_latest/tests/rollbacks.rs b/core/lib/multivm/src/versions/vm_latest/tests/rollbacks.rs index 343d30dcd95a..23c1ab49ad98 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/rollbacks.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/rollbacks.rs @@ -1,21 +1,22 @@ use ethabi::Token; - -use zksync_contracts::get_loadnext_contract; -use zksync_contracts::test_contracts::LoadnextContractExecutionParams; - +use zksync_contracts::{get_loadnext_contract, test_contracts::LoadnextContractExecutionParams}; use zksync_state::WriteStorage; use zksync_types::{get_nonce_key, Execute, U256}; -use crate::interface::dyn_tracers::vm_1_4_0::DynTracer; -use crate::interface::tracer::{TracerExecutionStatus, TracerExecutionStopReason}; -use crate::interface::{TxExecutionMode, VmExecutionMode, VmInterface, VmInterfaceHistoryEnabled}; -use crate::vm_latest::tests::tester::{ - DeployContractsTx, TransactionTestInfo, TxModifier, TxType, VmTesterBuilder, -}; -use crate::vm_latest::tests::utils::read_test_contract; -use crate::vm_latest::types::internals::ZkSyncVmState; -use crate::vm_latest::{ - BootloaderState, HistoryEnabled, HistoryMode, SimpleMemory, ToTracerPointer, VmTracer, +use crate::{ + interface::{ + dyn_tracers::vm_1_4_0::DynTracer, + tracer::{TracerExecutionStatus, TracerExecutionStopReason}, + TxExecutionMode, VmExecutionMode, VmInterface, VmInterfaceHistoryEnabled, + }, + vm_latest::{ + tests::{ + tester::{DeployContractsTx, TransactionTestInfo, TxModifier, TxType, VmTesterBuilder}, + utils::read_test_contract, + }, + types::internals::ZkSyncVmState, + BootloaderState, HistoryEnabled, HistoryMode, SimpleMemory, ToTracerPointer, VmTracer, + }, }; #[test] diff --git a/core/lib/multivm/src/versions/vm_latest/tests/simple_execution.rs b/core/lib/multivm/src/versions/vm_latest/tests/simple_execution.rs index 9f0c855b459c..a864538524a2 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/simple_execution.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/simple_execution.rs @@ -1,6 +1,10 @@ -use crate::interface::{ExecutionResult, VmExecutionMode, VmInterface}; -use crate::vm_latest::tests::tester::{TxType, VmTesterBuilder}; -use crate::vm_latest::HistoryDisabled; +use crate::{ + interface::{ExecutionResult, VmExecutionMode, VmInterface}, + vm_latest::{ + tests::tester::{TxType, VmTesterBuilder}, + HistoryDisabled, + }, +}; #[test] fn estimate_fee() { diff --git a/core/lib/multivm/src/versions/vm_latest/tests/tester/inner_state.rs b/core/lib/multivm/src/versions/vm_latest/tests/tester/inner_state.rs index 4767f9344795..b82e995c2db3 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/tester/inner_state.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/tester/inner_state.rs @@ -1,15 +1,19 @@ use std::collections::HashMap; -use zk_evm_1_4_0::aux_structures::Timestamp; -use zk_evm_1_4_0::vm_state::VmLocalState; +use zk_evm_1_4_0::{aux_structures::Timestamp, vm_state::VmLocalState}; use zksync_state::WriteStorage; - use zksync_types::{StorageKey, StorageLogQuery, StorageValue, U256}; -use crate::vm_latest::old_vm::event_sink::InMemoryEventSink; -use crate::vm_latest::old_vm::history_recorder::{AppDataFrameManagerWithHistory, HistoryRecorder}; -use crate::vm_latest::{HistoryEnabled, HistoryMode, SimpleMemory, Vm}; -use crate::HistoryMode as CommonHistoryMode; +use crate::{ + vm_latest::{ + old_vm::{ + event_sink::InMemoryEventSink, + history_recorder::{AppDataFrameManagerWithHistory, HistoryRecorder}, + }, + HistoryEnabled, HistoryMode, SimpleMemory, Vm, + }, + HistoryMode as CommonHistoryMode, +}; #[derive(Clone, Debug)] pub(crate) struct ModifiedKeysMap(HashMap); diff --git a/core/lib/multivm/src/versions/vm_latest/tests/tester/transaction_test_info.rs b/core/lib/multivm/src/versions/vm_latest/tests/tester/transaction_test_info.rs index 6fdfa7955e05..114f80d1a217 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/tester/transaction_test_info.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/tester/transaction_test_info.rs @@ -1,12 +1,12 @@ use zksync_types::{ExecuteTransactionCommon, Transaction}; -use crate::interface::{ - CurrentExecutionState, ExecutionResult, Halt, TxRevertReason, VmExecutionMode, - VmExecutionResultAndLogs, +use crate::{ + interface::{ + CurrentExecutionState, ExecutionResult, Halt, TxRevertReason, VmExecutionMode, + VmExecutionResultAndLogs, VmInterface, VmInterfaceHistoryEnabled, VmRevertReason, + }, + vm_latest::{tests::tester::vm_tester::VmTester, HistoryEnabled}, }; -use crate::interface::{VmInterface, VmInterfaceHistoryEnabled, VmRevertReason}; -use crate::vm_latest::tests::tester::vm_tester::VmTester; -use crate::vm_latest::HistoryEnabled; #[derive(Debug, Clone)] pub(crate) enum TxModifier { diff --git a/core/lib/multivm/src/versions/vm_latest/tests/tester/vm_tester.rs b/core/lib/multivm/src/versions/vm_latest/tests/tester/vm_tester.rs index cbf009d5b020..6218a3918243 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/tester/vm_tester.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/tester/vm_tester.rs @@ -1,28 +1,31 @@ use std::marker::PhantomData; + use zksync_contracts::BaseSystemContracts; use zksync_state::{InMemoryStorage, StoragePtr, StorageView, WriteStorage}; - -use zksync_types::block::legacy_miniblock_hash; -use zksync_types::helpers::unix_timestamp_ms; -use zksync_types::utils::{deployed_address_create, storage_key_for_eth_balance}; use zksync_types::{ - get_code_key, get_is_account_key, Address, L1BatchNumber, L2ChainId, MiniblockNumber, Nonce, - ProtocolVersionId, U256, + block::legacy_miniblock_hash, + get_code_key, get_is_account_key, + helpers::unix_timestamp_ms, + utils::{deployed_address_create, storage_key_for_eth_balance}, + Address, L1BatchNumber, L2ChainId, MiniblockNumber, Nonce, ProtocolVersionId, U256, }; -use zksync_utils::bytecode::hash_bytecode; -use zksync_utils::u256_to_h256; - -use crate::vm_latest::constants::BLOCK_GAS_LIMIT; - -use crate::interface::{ - L1BatchEnv, L2Block, L2BlockEnv, SystemEnv, TxExecutionMode, VmExecutionMode, VmInterface, +use zksync_utils::{bytecode::hash_bytecode, u256_to_h256}; + +use crate::{ + interface::{ + L1BatchEnv, L2Block, L2BlockEnv, SystemEnv, TxExecutionMode, VmExecutionMode, VmInterface, + }, + vm_latest::{ + constants::BLOCK_GAS_LIMIT, + tests::{ + tester::{Account, TxType}, + utils::read_test_contract, + }, + utils::l2_blocks::load_last_l2_block, + Vm, + }, + HistoryMode, }; -use crate::vm_latest::tests::tester::Account; -use crate::vm_latest::tests::tester::TxType; -use crate::vm_latest::tests::utils::read_test_contract; -use crate::vm_latest::utils::l2_blocks::load_last_l2_block; -use crate::vm_latest::Vm; -use crate::HistoryMode; pub(crate) type InMemoryStorageView = StorageView; diff --git a/core/lib/multivm/src/versions/vm_latest/tests/tracing_execution_error.rs b/core/lib/multivm/src/versions/vm_latest/tests/tracing_execution_error.rs index f55eadecde67..f02de899b03e 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/tracing_execution_error.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/tracing_execution_error.rs @@ -1,12 +1,15 @@ use zksync_types::{Execute, H160}; -use crate::interface::TxExecutionMode; -use crate::interface::{TxRevertReason, VmRevertReason}; -use crate::vm_latest::tests::tester::{ExpectedError, TransactionTestInfo, VmTesterBuilder}; -use crate::vm_latest::tests::utils::{ - get_execute_error_calldata, read_error_contract, BASE_SYSTEM_CONTRACTS, +use crate::{ + interface::{TxExecutionMode, TxRevertReason, VmRevertReason}, + vm_latest::{ + tests::{ + tester::{ExpectedError, TransactionTestInfo, VmTesterBuilder}, + utils::{get_execute_error_calldata, read_error_contract, BASE_SYSTEM_CONTRACTS}, + }, + HistoryEnabled, + }, }; -use crate::vm_latest::HistoryEnabled; #[test] fn test_tracing_of_execution_errors() { diff --git a/core/lib/multivm/src/versions/vm_latest/tests/upgrade.rs b/core/lib/multivm/src/versions/vm_latest/tests/upgrade.rs index 65780114e9a0..b5c493ca7075 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/upgrade.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/upgrade.rs @@ -1,28 +1,28 @@ use zk_evm_1_4_0::aux_structures::Timestamp; - -use zksync_types::{ - ethabi::Contract, - Execute, COMPLEX_UPGRADER_ADDRESS, CONTRACT_DEPLOYER_ADDRESS, CONTRACT_FORCE_DEPLOYER_ADDRESS, - REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_BYTE, - {ethabi::Token, Address, ExecuteTransactionCommon, Transaction, H256, U256}, - {get_code_key, get_known_code_key, H160}, -}; - -use zksync_utils::{bytecode::hash_bytecode, bytes_to_be_words, h256_to_u256, u256_to_h256}; - use zksync_contracts::{deployer_contract, load_contract, load_sys_contract, read_bytecode}; use zksync_state::WriteStorage; use zksync_test_account::TxType; - -use crate::interface::{ - ExecutionResult, Halt, TxExecutionMode, VmExecutionMode, VmInterface, VmInterfaceHistoryEnabled, +use zksync_types::{ + ethabi::{Contract, Token}, + get_code_key, get_known_code_key, + protocol_version::ProtocolUpgradeTxCommonData, + Address, Execute, ExecuteTransactionCommon, Transaction, COMPLEX_UPGRADER_ADDRESS, + CONTRACT_DEPLOYER_ADDRESS, CONTRACT_FORCE_DEPLOYER_ADDRESS, H160, H256, + REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_BYTE, U256, }; -use crate::vm_latest::tests::tester::VmTesterBuilder; -use crate::vm_latest::tests::utils::verify_required_storage; -use crate::vm_latest::HistoryEnabled; -use zksync_types::protocol_version::ProtocolUpgradeTxCommonData; +use zksync_utils::{bytecode::hash_bytecode, bytes_to_be_words, h256_to_u256, u256_to_h256}; use super::utils::read_test_contract; +use crate::{ + interface::{ + ExecutionResult, Halt, TxExecutionMode, VmExecutionMode, VmInterface, + VmInterfaceHistoryEnabled, + }, + vm_latest::{ + tests::{tester::VmTesterBuilder, utils::verify_required_storage}, + HistoryEnabled, + }, +}; /// In this test we ensure that the requirements for protocol upgrade transactions are enforced by the bootloader: /// - This transaction must be the only one in block diff --git a/core/lib/multivm/src/versions/vm_latest/tests/utils.rs b/core/lib/multivm/src/versions/vm_latest/tests/utils.rs index e30f0b9f39a4..90bc8c2aef27 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/utils.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/utils.rs @@ -1,18 +1,17 @@ use ethabi::Contract; use once_cell::sync::Lazy; - -use crate::vm_latest::tests::tester::InMemoryStorageView; use zksync_contracts::{ load_contract, read_bytecode, read_zbin_bytecode, BaseSystemContracts, SystemContractCode, }; use zksync_state::{StoragePtr, WriteStorage}; -use zksync_types::utils::storage_key_for_standard_token_balance; -use zksync_types::{AccountTreeId, Address, StorageKey, H256, U256}; -use zksync_utils::bytecode::hash_bytecode; -use zksync_utils::{bytes_to_be_words, h256_to_u256, u256_to_h256}; +use zksync_types::{ + utils::storage_key_for_standard_token_balance, AccountTreeId, Address, StorageKey, H256, U256, +}; +use zksync_utils::{bytecode::hash_bytecode, bytes_to_be_words, h256_to_u256, u256_to_h256}; -use crate::vm_latest::types::internals::ZkSyncVmState; -use crate::vm_latest::HistoryMode; +use crate::vm_latest::{ + tests::tester::InMemoryStorageView, types::internals::ZkSyncVmState, HistoryMode, +}; pub(crate) static BASE_SYSTEM_CONTRACTS: Lazy = Lazy::new(BaseSystemContracts::load_from_disk); diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/default_tracers.rs b/core/lib/multivm/src/versions/vm_latest/tracers/default_tracers.rs index 9582e6e10536..0e18d989af62 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/default_tracers.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/default_tracers.rs @@ -1,8 +1,8 @@ -use std::fmt::{Debug, Formatter}; -use std::marker::PhantomData; +use std::{ + fmt::{Debug, Formatter}, + marker::PhantomData, +}; -use crate::interface::tracer::{TracerExecutionStopReason, VmExecutionStopReason}; -use crate::interface::{Halt, VmExecutionMode}; use zk_evm_1_4_0::{ tracing::{ AfterDecodingData, AfterExecutionData, BeforeExecutionData, Tracer, VmLocalStateData, @@ -14,23 +14,30 @@ use zk_evm_1_4_0::{ use zksync_state::{StoragePtr, WriteStorage}; use zksync_types::Timestamp; -use crate::interface::traits::tracers::dyn_tracers::vm_1_4_0::DynTracer; -use crate::interface::types::tracer::TracerExecutionStatus; -use crate::vm_latest::bootloader_state::utils::apply_l2_block; -use crate::vm_latest::bootloader_state::BootloaderState; -use crate::vm_latest::constants::BOOTLOADER_HEAP_PAGE; -use crate::vm_latest::old_vm::history_recorder::HistoryMode; -use crate::vm_latest::old_vm::memory::SimpleMemory; -use crate::vm_latest::tracers::dispatcher::TracerDispatcher; -use crate::vm_latest::tracers::utils::{ - computational_gas_price, gas_spent_on_bytecodes_and_long_messages_this_opcode, - print_debug_if_needed, VmHook, -}; -use crate::vm_latest::tracers::{RefundsTracer, ResultTracer}; -use crate::vm_latest::types::internals::ZkSyncVmState; -use crate::vm_latest::VmTracer; - use super::PubdataTracer; +use crate::{ + interface::{ + tracer::{TracerExecutionStopReason, VmExecutionStopReason}, + traits::tracers::dyn_tracers::vm_1_4_0::DynTracer, + types::tracer::TracerExecutionStatus, + Halt, VmExecutionMode, + }, + vm_latest::{ + bootloader_state::{utils::apply_l2_block, BootloaderState}, + constants::BOOTLOADER_HEAP_PAGE, + old_vm::{history_recorder::HistoryMode, memory::SimpleMemory}, + tracers::{ + dispatcher::TracerDispatcher, + utils::{ + computational_gas_price, gas_spent_on_bytecodes_and_long_messages_this_opcode, + print_debug_if_needed, VmHook, + }, + RefundsTracer, ResultTracer, + }, + types::internals::ZkSyncVmState, + VmTracer, + }, +}; /// Default tracer for the VM. It manages the other tracers execution and stop the vm when needed. pub(crate) struct DefaultExecutionTracer { diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/dispatcher.rs b/core/lib/multivm/src/versions/vm_latest/tracers/dispatcher.rs index b75277670dca..5ee5c8ab0c19 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/dispatcher.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/dispatcher.rs @@ -1,13 +1,18 @@ -use crate::interface::dyn_tracers::vm_1_4_0::DynTracer; -use crate::interface::tracer::{TracerExecutionStatus, VmExecutionStopReason}; -use crate::vm_latest::{ - BootloaderState, HistoryMode, SimpleMemory, TracerPointer, VmTracer, ZkSyncVmState, -}; use zk_evm_1_4_0::tracing::{ AfterDecodingData, AfterExecutionData, BeforeExecutionData, VmLocalStateData, }; use zksync_state::{StoragePtr, WriteStorage}; +use crate::{ + interface::{ + dyn_tracers::vm_1_4_0::DynTracer, + tracer::{TracerExecutionStatus, VmExecutionStopReason}, + }, + vm_latest::{ + BootloaderState, HistoryMode, SimpleMemory, TracerPointer, VmTracer, ZkSyncVmState, + }, +}; + /// Tracer dispatcher is a tracer that can dispatch calls to multiple tracers. pub struct TracerDispatcher { tracers: Vec>, diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/pubdata_tracer.rs b/core/lib/multivm/src/versions/vm_latest/tracers/pubdata_tracer.rs index 59a9d8eb452c..388b5ef209e0 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/pubdata_tracer.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/pubdata_tracer.rs @@ -1,9 +1,9 @@ use std::marker::PhantomData; + use zk_evm_1_4_0::{ aux_structures::Timestamp, tracing::{BeforeExecutionData, VmLocalStateData}, }; - use zksync_state::{StoragePtr, WriteStorage}; use zksync_types::{ event::{ @@ -14,24 +14,24 @@ use zksync_types::{ zkevm_test_harness::witness::sort_storage_access::sort_storage_access_queries, AccountTreeId, StorageKey, L1_MESSENGER_ADDRESS, }; -use zksync_utils::u256_to_h256; -use zksync_utils::{h256_to_u256, u256_to_bytes_be}; +use zksync_utils::{h256_to_u256, u256_to_bytes_be, u256_to_h256}; -use crate::vm_latest::{ - old_vm::{history_recorder::HistoryMode, memory::SimpleMemory}, - types::internals::pubdata::PubdataInput, -}; -use crate::{vm_latest::constants::BOOTLOADER_HEAP_PAGE, vm_latest::StorageOracle}; - -use crate::interface::dyn_tracers::vm_1_4_0::DynTracer; -use crate::interface::tracer::{TracerExecutionStatus, TracerExecutionStopReason}; -use crate::interface::types::inputs::L1BatchEnv; -use crate::vm_latest::tracers::{traits::VmTracer, utils::VmHook}; -use crate::vm_latest::types::internals::ZkSyncVmState; -use crate::vm_latest::utils::logs::collect_events_and_l1_system_logs_after_timestamp; use crate::{ - interface::VmExecutionMode, - vm_latest::bootloader_state::{utils::apply_pubdata_to_memory, BootloaderState}, + interface::{ + dyn_tracers::vm_1_4_0::DynTracer, + tracer::{TracerExecutionStatus, TracerExecutionStopReason}, + types::inputs::L1BatchEnv, + VmExecutionMode, + }, + vm_latest::{ + bootloader_state::{utils::apply_pubdata_to_memory, BootloaderState}, + constants::BOOTLOADER_HEAP_PAGE, + old_vm::{history_recorder::HistoryMode, memory::SimpleMemory}, + tracers::{traits::VmTracer, utils::VmHook}, + types::internals::{pubdata::PubdataInput, ZkSyncVmState}, + utils::logs::collect_events_and_l1_system_logs_after_timestamp, + StorageOracle, + }, }; /// Tracer responsible for collecting information about refunds. diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/refunds.rs b/core/lib/multivm/src/versions/vm_latest/tracers/refunds.rs index f3e6c3366840..e852fba1dac8 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/refunds.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/refunds.rs @@ -1,9 +1,6 @@ use std::marker::PhantomData; -use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics}; -use crate::interface::traits::tracers::dyn_tracers::vm_1_4_0::DynTracer; -use crate::interface::types::tracer::TracerExecutionStatus; -use crate::interface::{L1BatchEnv, Refunds}; +use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics}; use zk_evm_1_4_0::{ aux_structures::Timestamp, tracing::{BeforeExecutionData, VmLocalStateData}, @@ -16,24 +13,29 @@ use zksync_types::{ l2_to_l1_log::L2ToL1Log, L1BatchNumber, U256, }; -use zksync_utils::bytecode::bytecode_len_in_bytes; -use zksync_utils::{ceil_div_u256, u256_to_h256}; - -use crate::vm_latest::constants::{ - BOOTLOADER_HEAP_PAGE, OPERATOR_REFUNDS_OFFSET, TX_GAS_LIMIT_OFFSET, -}; -use crate::vm_latest::old_vm::{ - events::merge_events, history_recorder::HistoryMode, memory::SimpleMemory, - utils::eth_price_per_pubdata_byte, -}; - -use crate::vm_latest::bootloader_state::BootloaderState; -use crate::vm_latest::tracers::utils::gas_spent_on_bytecodes_and_long_messages_this_opcode; -use crate::vm_latest::tracers::{ - traits::VmTracer, - utils::{get_vm_hook_params, VmHook}, +use zksync_utils::{bytecode::bytecode_len_in_bytes, ceil_div_u256, u256_to_h256}; + +use crate::{ + interface::{ + traits::tracers::dyn_tracers::vm_1_4_0::DynTracer, types::tracer::TracerExecutionStatus, + L1BatchEnv, Refunds, + }, + vm_latest::{ + bootloader_state::BootloaderState, + constants::{BOOTLOADER_HEAP_PAGE, OPERATOR_REFUNDS_OFFSET, TX_GAS_LIMIT_OFFSET}, + old_vm::{ + events::merge_events, history_recorder::HistoryMode, memory::SimpleMemory, + utils::eth_price_per_pubdata_byte, + }, + tracers::{ + traits::VmTracer, + utils::{ + gas_spent_on_bytecodes_and_long_messages_this_opcode, get_vm_hook_params, VmHook, + }, + }, + types::internals::ZkSyncVmState, + }, }; -use crate::vm_latest::types::internals::ZkSyncVmState; /// Tracer responsible for collecting information about refunds. #[derive(Debug, Clone)] diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/result_tracer.rs b/core/lib/multivm/src/versions/vm_latest/tracers/result_tracer.rs index 7e6e08a0a491..b3412587725c 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/result_tracer.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/result_tracer.rs @@ -1,27 +1,29 @@ use std::marker::PhantomData; + use zk_evm_1_4_0::{ tracing::{AfterDecodingData, BeforeExecutionData, VmLocalStateData}, vm_state::{ErrorFlags, VmLocalState}, zkevm_opcode_defs::FatPointer, }; use zksync_state::{StoragePtr, WriteStorage}; - -use crate::interface::{ - tracer::VmExecutionStopReason, traits::tracers::dyn_tracers::vm_1_4_0::DynTracer, - types::tracer::TracerExecutionStopReason, ExecutionResult, Halt, TxRevertReason, - VmExecutionMode, VmRevertReason, -}; use zksync_types::U256; -use crate::vm_latest::{ - constants::{BOOTLOADER_HEAP_PAGE, RESULT_SUCCESS_FIRST_SLOT}, - old_vm::utils::{vm_may_have_ended_inner, VmExecutionResult}, - tracers::{ - traits::VmTracer, - utils::{get_vm_hook_params, read_pointer, VmHook}, +use crate::{ + interface::{ + tracer::VmExecutionStopReason, traits::tracers::dyn_tracers::vm_1_4_0::DynTracer, + types::tracer::TracerExecutionStopReason, ExecutionResult, Halt, TxRevertReason, + VmExecutionMode, VmRevertReason, + }, + vm_latest::{ + constants::{BOOTLOADER_HEAP_PAGE, RESULT_SUCCESS_FIRST_SLOT}, + old_vm::utils::{vm_may_have_ended_inner, VmExecutionResult}, + tracers::{ + traits::VmTracer, + utils::{get_vm_hook_params, read_pointer, VmHook}, + }, + types::internals::ZkSyncVmState, + BootloaderState, HistoryMode, SimpleMemory, }, - types::internals::ZkSyncVmState, - BootloaderState, HistoryMode, SimpleMemory, }; #[derive(Debug, Clone)] diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/traits.rs b/core/lib/multivm/src/versions/vm_latest/tracers/traits.rs index a3970541bac2..68307b3f2867 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/traits.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/traits.rs @@ -1,11 +1,16 @@ -use crate::interface::dyn_tracers::vm_1_4_0::DynTracer; -use crate::interface::tracer::{TracerExecutionStatus, VmExecutionStopReason}; use zksync_state::WriteStorage; -use crate::vm_latest::bootloader_state::BootloaderState; -use crate::vm_latest::old_vm::history_recorder::HistoryMode; -use crate::vm_latest::old_vm::memory::SimpleMemory; -use crate::vm_latest::types::internals::ZkSyncVmState; +use crate::{ + interface::{ + dyn_tracers::vm_1_4_0::DynTracer, + tracer::{TracerExecutionStatus, VmExecutionStopReason}, + }, + vm_latest::{ + bootloader_state::BootloaderState, + old_vm::{history_recorder::HistoryMode, memory::SimpleMemory}, + types::internals::ZkSyncVmState, + }, +}; pub type TracerPointer = Box>; diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/utils.rs b/core/lib/multivm/src/versions/vm_latest/tracers/utils.rs index 52ff84f8c3c5..93710586fdac 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/utils.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/utils.rs @@ -1,10 +1,10 @@ -use zk_evm_1_4_0::aux_structures::MemoryPage; -use zk_evm_1_4_0::zkevm_opcode_defs::{FarCallABI, FarCallForwardPageType}; use zk_evm_1_4_0::{ + aux_structures::MemoryPage, tracing::{BeforeExecutionData, VmLocalStateData}, - zkevm_opcode_defs::{FatPointer, LogOpcode, Opcode, UMAOpcode}, + zkevm_opcode_defs::{ + FarCallABI, FarCallForwardPageType, FatPointer, LogOpcode, Opcode, UMAOpcode, + }, }; - use zksync_system_constants::{ ECRECOVER_PRECOMPILE_ADDRESS, KECCAK256_PRECOMPILE_ADDRESS, KNOWN_CODES_STORAGE_ADDRESS, L1_MESSENGER_ADDRESS, SHA256_PRECOMPILE_ADDRESS, @@ -12,12 +12,16 @@ use zksync_system_constants::{ use zksync_types::U256; use zksync_utils::u256_to_h256; -use crate::vm_latest::constants::{ - BOOTLOADER_HEAP_PAGE, VM_HOOK_PARAMS_COUNT, VM_HOOK_PARAMS_START_POSITION, VM_HOOK_POSITION, +use crate::vm_latest::{ + constants::{ + BOOTLOADER_HEAP_PAGE, VM_HOOK_PARAMS_COUNT, VM_HOOK_PARAMS_START_POSITION, VM_HOOK_POSITION, + }, + old_vm::{ + history_recorder::HistoryMode, + memory::SimpleMemory, + utils::{aux_heap_page_from_base, heap_page_from_base}, + }, }; -use crate::vm_latest::old_vm::history_recorder::HistoryMode; -use crate::vm_latest::old_vm::memory::SimpleMemory; -use crate::vm_latest::old_vm::utils::{aux_heap_page_from_base, heap_page_from_base}; #[derive(Clone, Debug, Copy)] pub(crate) enum VmHook { diff --git a/core/lib/multivm/src/versions/vm_latest/types/internals/pubdata.rs b/core/lib/multivm/src/versions/vm_latest/types/internals/pubdata.rs index e246bceeac54..fd00b333c725 100644 --- a/core/lib/multivm/src/versions/vm_latest/types/internals/pubdata.rs +++ b/core/lib/multivm/src/versions/vm_latest/types/internals/pubdata.rs @@ -1,5 +1,5 @@ -use zksync_types::ethabi; use zksync_types::{ + ethabi, event::L1MessengerL2ToL1Log, writes::{compress_state_diffs, StateDiffRecord}, }; diff --git a/core/lib/multivm/src/versions/vm_latest/types/internals/transaction_data.rs b/core/lib/multivm/src/versions/vm_latest/types/internals/transaction_data.rs index 3c7b9bcac035..f5b97ca9793e 100644 --- a/core/lib/multivm/src/versions/vm_latest/types/internals/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_latest/types/internals/transaction_data.rs @@ -1,15 +1,15 @@ use std::convert::TryInto; -use zksync_types::ethabi::{encode, Address, Token}; -use zksync_types::fee::{encoding_len, Fee}; -use zksync_types::l1::is_l1_tx_type; -use zksync_types::l2::L2Tx; -use zksync_types::transaction_request::{PaymasterParams, TransactionRequest}; + use zksync_types::{ - l2::TransactionType, Bytes, Execute, ExecuteTransactionCommon, L2ChainId, L2TxCommonData, - Nonce, Transaction, H256, U256, + ethabi::{encode, Address, Token}, + fee::{encoding_len, Fee}, + l1::is_l1_tx_type, + l2::{L2Tx, TransactionType}, + transaction_request::{PaymasterParams, TransactionRequest}, + Bytes, Execute, ExecuteTransactionCommon, L2ChainId, L2TxCommonData, Nonce, Transaction, H256, + U256, }; -use zksync_utils::address_to_h256; -use zksync_utils::{bytecode::hash_bytecode, bytes_to_be_words, h256_to_u256}; +use zksync_utils::{address_to_h256, bytecode::hash_bytecode, bytes_to_be_words, h256_to_u256}; use crate::vm_latest::utils::overhead::{get_amortized_overhead, OverheadCoefficients}; @@ -303,9 +303,10 @@ impl TryInto for TransactionData { #[cfg(test)] mod tests { - use super::*; use zksync_types::fee::encoding_len; + use super::*; + #[test] fn test_consistency_with_encoding_length() { let transaction = TransactionData { diff --git a/core/lib/multivm/src/versions/vm_latest/types/internals/vm_state.rs b/core/lib/multivm/src/versions/vm_latest/types/internals/vm_state.rs index 0c519a324c0a..f65785dcfe5b 100644 --- a/core/lib/multivm/src/versions/vm_latest/types/internals/vm_state.rs +++ b/core/lib/multivm/src/versions/vm_latest/types/internals/vm_state.rs @@ -1,34 +1,40 @@ use zk_evm_1_4_0::{ - aux_structures::MemoryPage, - aux_structures::Timestamp, + aux_structures::{MemoryPage, Timestamp}, block_properties::BlockProperties, vm_state::{CallStackEntry, PrimitiveValue, VmState}, witness_trace::DummyTracer, zkevm_opcode_defs::{ system_params::{BOOTLOADER_MAX_MEMORY, INITIAL_FRAME_FORMAL_EH_LOCATION}, - FatPointer, BOOTLOADER_CALLDATA_PAGE, + FatPointer, BOOTLOADER_BASE_PAGE, BOOTLOADER_CALLDATA_PAGE, BOOTLOADER_CODE_PAGE, + STARTING_BASE_PAGE, STARTING_TIMESTAMP, }, }; - -use crate::interface::{L1BatchEnv, L2Block, SystemEnv}; -use zk_evm_1_4_0::zkevm_opcode_defs::{ - BOOTLOADER_BASE_PAGE, BOOTLOADER_CODE_PAGE, STARTING_BASE_PAGE, STARTING_TIMESTAMP, -}; use zksync_state::{StoragePtr, WriteStorage}; use zksync_system_constants::BOOTLOADER_ADDRESS; -use zksync_types::block::legacy_miniblock_hash; -use zksync_types::{zkevm_test_harness::INITIAL_MONOTONIC_CYCLE_COUNTER, Address, MiniblockNumber}; +use zksync_types::{ + block::legacy_miniblock_hash, zkevm_test_harness::INITIAL_MONOTONIC_CYCLE_COUNTER, Address, + MiniblockNumber, +}; use zksync_utils::h256_to_u256; -use crate::vm_latest::bootloader_state::BootloaderState; -use crate::vm_latest::constants::BOOTLOADER_HEAP_PAGE; -use crate::vm_latest::old_vm::{ - event_sink::InMemoryEventSink, history_recorder::HistoryMode, memory::SimpleMemory, - oracles::decommitter::DecommitterOracle, oracles::precompile::PrecompilesProcessorWithHistory, +use crate::{ + interface::{L1BatchEnv, L2Block, SystemEnv}, + vm_latest::{ + bootloader_state::BootloaderState, + constants::BOOTLOADER_HEAP_PAGE, + old_vm::{ + event_sink::InMemoryEventSink, + history_recorder::HistoryMode, + memory::SimpleMemory, + oracles::{ + decommitter::DecommitterOracle, precompile::PrecompilesProcessorWithHistory, + }, + }, + oracles::storage::StorageOracle, + types::l1_batch::bootloader_initial_memory, + utils::l2_blocks::{assert_next_block, load_last_l2_block}, + }, }; -use crate::vm_latest::oracles::storage::StorageOracle; -use crate::vm_latest::types::l1_batch::bootloader_initial_memory; -use crate::vm_latest::utils::l2_blocks::{assert_next_block, load_last_l2_block}; pub type ZkSyncVmState = VmState< StorageOracle, diff --git a/core/lib/multivm/src/versions/vm_latest/types/l1_batch.rs b/core/lib/multivm/src/versions/vm_latest/types/l1_batch.rs index 631f1436cc3b..6f16e95f8d77 100644 --- a/core/lib/multivm/src/versions/vm_latest/types/l1_batch.rs +++ b/core/lib/multivm/src/versions/vm_latest/types/l1_batch.rs @@ -1,7 +1,8 @@ -use crate::interface::L1BatchEnv; use zksync_types::U256; use zksync_utils::{address_to_u256, h256_to_u256}; +use crate::interface::L1BatchEnv; + const OPERATOR_ADDRESS_SLOT: usize = 0; const PREV_BLOCK_HASH_SLOT: usize = 1; const NEW_BLOCK_TIMESTAMP_SLOT: usize = 2; diff --git a/core/lib/multivm/src/versions/vm_latest/utils/l2_blocks.rs b/core/lib/multivm/src/versions/vm_latest/utils/l2_blocks.rs index 3d5f58094e01..5dd26c4c0277 100644 --- a/core/lib/multivm/src/versions/vm_latest/utils/l2_blocks.rs +++ b/core/lib/multivm/src/versions/vm_latest/utils/l2_blocks.rs @@ -1,15 +1,17 @@ -use crate::interface::{L2Block, L2BlockEnv}; use zksync_state::{ReadStorage, StoragePtr}; use zksync_system_constants::{ SYSTEM_CONTEXT_ADDRESS, SYSTEM_CONTEXT_CURRENT_L2_BLOCK_HASHES_POSITION, SYSTEM_CONTEXT_CURRENT_L2_BLOCK_INFO_POSITION, SYSTEM_CONTEXT_CURRENT_TX_ROLLING_HASH_POSITION, SYSTEM_CONTEXT_STORED_L2_BLOCK_HASHES, }; -use zksync_types::block::unpack_block_info; -use zksync_types::web3::signing::keccak256; -use zksync_types::{AccountTreeId, MiniblockNumber, StorageKey, H256, U256}; +use zksync_types::{ + block::unpack_block_info, web3::signing::keccak256, AccountTreeId, MiniblockNumber, StorageKey, + H256, U256, +}; use zksync_utils::{h256_to_u256, u256_to_h256}; +use crate::interface::{L2Block, L2BlockEnv}; + pub(crate) fn get_l2_block_hash_key(block_number: u32) -> StorageKey { let position = h256_to_u256(SYSTEM_CONTEXT_CURRENT_L2_BLOCK_HASHES_POSITION) + U256::from(block_number % SYSTEM_CONTEXT_STORED_L2_BLOCK_HASHES); diff --git a/core/lib/multivm/src/versions/vm_latest/utils/logs.rs b/core/lib/multivm/src/versions/vm_latest/utils/logs.rs index a2b7f548684f..b7fa07956a96 100644 --- a/core/lib/multivm/src/versions/vm_latest/utils/logs.rs +++ b/core/lib/multivm/src/versions/vm_latest/utils/logs.rs @@ -2,8 +2,11 @@ use zksync_state::WriteStorage; use zksync_types::{l2_to_l1_log::L2ToL1Log, Timestamp, VmEvent}; use crate::{ - interface::L1BatchEnv, vm_latest::old_vm::events::merge_events, - vm_latest::old_vm::history_recorder::HistoryMode, vm_latest::types::internals::ZkSyncVmState, + interface::L1BatchEnv, + vm_latest::{ + old_vm::{events::merge_events, history_recorder::HistoryMode}, + types::internals::ZkSyncVmState, + }, }; pub(crate) fn collect_events_and_l1_system_logs_after_timestamp( diff --git a/core/lib/multivm/src/versions/vm_latest/utils/overhead.rs b/core/lib/multivm/src/versions/vm_latest/utils/overhead.rs index c977267db8fe..38aaede8d4bb 100644 --- a/core/lib/multivm/src/versions/vm_latest/utils/overhead.rs +++ b/core/lib/multivm/src/versions/vm_latest/utils/overhead.rs @@ -1,12 +1,12 @@ -use crate::vm_latest::constants::{ - BLOCK_OVERHEAD_GAS, BLOCK_OVERHEAD_PUBDATA, BOOTLOADER_TX_ENCODING_SPACE, -}; use zk_evm_1_4_0::zkevm_opcode_defs::system_params::MAX_TX_ERGS_LIMIT; use zksync_system_constants::{MAX_L2_TX_GAS_LIMIT, MAX_TXS_IN_BLOCK}; -use zksync_types::l1::is_l1_tx_type; -use zksync_types::U256; +use zksync_types::{l1::is_l1_tx_type, U256}; use zksync_utils::ceil_div_u256; +use crate::vm_latest::constants::{ + BLOCK_OVERHEAD_GAS, BLOCK_OVERHEAD_PUBDATA, BOOTLOADER_TX_ENCODING_SPACE, +}; + /// Derives the overhead for processing transactions in a block. pub fn derive_overhead( gas_limit: u32, diff --git a/core/lib/multivm/src/versions/vm_latest/utils/transaction_encoding.rs b/core/lib/multivm/src/versions/vm_latest/utils/transaction_encoding.rs index 9aecef6367ee..86c49a3eb15d 100644 --- a/core/lib/multivm/src/versions/vm_latest/utils/transaction_encoding.rs +++ b/core/lib/multivm/src/versions/vm_latest/utils/transaction_encoding.rs @@ -1,6 +1,7 @@ -use crate::vm_latest::types::internals::TransactionData; use zksync_types::Transaction; +use crate::vm_latest::types::internals::TransactionData; + /// Extension for transactions, specific for VM. Required for bypassing the orphan rule pub trait TransactionVmExt { /// Get the size of the transaction in tokens. diff --git a/core/lib/multivm/src/versions/vm_latest/vm.rs b/core/lib/multivm/src/versions/vm_latest/vm.rs index 20d74e39093f..159f50a89ee3 100644 --- a/core/lib/multivm/src/versions/vm_latest/vm.rs +++ b/core/lib/multivm/src/versions/vm_latest/vm.rs @@ -1,21 +1,25 @@ -use crate::HistoryMode; use zksync_state::{StoragePtr, WriteStorage}; -use zksync_types::l2_to_l1_log::{SystemL2ToL1Log, UserL2ToL1Log}; -use zksync_types::{event::extract_l2tol1logs_from_l1_messenger, Transaction}; +use zksync_types::{ + event::extract_l2tol1logs_from_l1_messenger, + l2_to_l1_log::{SystemL2ToL1Log, UserL2ToL1Log}, + Transaction, +}; use zksync_utils::bytecode::CompressedBytecodeInfo; -use crate::vm_latest::old_vm::events::merge_events; -use crate::vm_latest::old_vm::history_recorder::HistoryEnabled; - -use crate::interface::{ - BootloaderMemory, CurrentExecutionState, L1BatchEnv, L2BlockEnv, SystemEnv, VmExecutionMode, - VmExecutionResultAndLogs, VmInterfaceHistoryEnabled, VmMemoryMetrics, +use crate::{ + interface::{ + BootloaderMemory, BytecodeCompressionError, CurrentExecutionState, L1BatchEnv, L2BlockEnv, + SystemEnv, VmExecutionMode, VmExecutionResultAndLogs, VmInterface, + VmInterfaceHistoryEnabled, VmMemoryMetrics, + }, + vm_latest::{ + bootloader_state::BootloaderState, + old_vm::{events::merge_events, history_recorder::HistoryEnabled}, + tracers::dispatcher::TracerDispatcher, + types::internals::{new_vm_state, VmSnapshot, ZkSyncVmState}, + }, + HistoryMode, }; -use crate::interface::{BytecodeCompressionError, VmInterface}; -use crate::vm_latest::bootloader_state::BootloaderState; -use crate::vm_latest::tracers::dispatcher::TracerDispatcher; - -use crate::vm_latest::types::internals::{new_vm_state, VmSnapshot, ZkSyncVmState}; /// Main entry point for Virtual Machine integration. /// The instance should process only one l1 batch diff --git a/core/lib/multivm/src/versions/vm_m5/errors/vm_revert_reason.rs b/core/lib/multivm/src/versions/vm_m5/errors/vm_revert_reason.rs index 5d1a075f6a53..7cfa8708fc30 100644 --- a/core/lib/multivm/src/versions/vm_m5/errors/vm_revert_reason.rs +++ b/core/lib/multivm/src/versions/vm_m5/errors/vm_revert_reason.rs @@ -1,5 +1,7 @@ -use std::convert::TryFrom; -use std::fmt::{Debug, Display}; +use std::{ + convert::TryFrom, + fmt::{Debug, Display}, +}; use zksync_types::U256; diff --git a/core/lib/multivm/src/versions/vm_m5/event_sink.rs b/core/lib/multivm/src/versions/vm_m5/event_sink.rs index 80ceb8baeaa9..0bb1ee498f61 100644 --- a/core/lib/multivm/src/versions/vm_m5/event_sink.rs +++ b/core/lib/multivm/src/versions/vm_m5/event_sink.rs @@ -1,5 +1,5 @@ -use crate::vm_m5::{oracles::OracleWithHistory, utils::collect_log_queries_after_timestamp}; use std::collections::HashMap; + use zk_evm_1_3_1::{ abstractions::EventSink, aux_structures::{LogQuery, Timestamp}, @@ -9,7 +9,10 @@ use zk_evm_1_3_1::{ }, }; -use crate::vm_m5::history_recorder::AppDataFrameManagerWithHistory; +use crate::vm_m5::{ + history_recorder::AppDataFrameManagerWithHistory, oracles::OracleWithHistory, + utils::collect_log_queries_after_timestamp, +}; #[derive(Debug, Default, Clone, PartialEq)] pub struct InMemoryEventSink { diff --git a/core/lib/multivm/src/versions/vm_m5/history_recorder.rs b/core/lib/multivm/src/versions/vm_m5/history_recorder.rs index 896b2261e9c5..7a158b4dea79 100644 --- a/core/lib/multivm/src/versions/vm_m5/history_recorder.rs +++ b/core/lib/multivm/src/versions/vm_m5/history_recorder.rs @@ -3,18 +3,17 @@ use std::{ hash::{BuildHasherDefault, Hash, Hasher}, }; -use crate::vm_m5::storage::{Storage, StoragePtr}; - use zk_evm_1_3_1::{ aux_structures::Timestamp, reference_impls::event_sink::ApplicationData, vm_state::PrimitiveValue, zkevm_opcode_defs::{self}, }; - use zksync_types::{StorageKey, U256}; use zksync_utils::{h256_to_u256, u256_to_h256}; +use crate::vm_m5::storage::{Storage, StoragePtr}; + pub type AppDataFrameManagerWithHistory = FrameManagerWithHistory>; pub type MemoryWithHistory = HistoryRecorder; pub type FrameManagerWithHistory = HistoryRecorder>; diff --git a/core/lib/multivm/src/versions/vm_m5/memory.rs b/core/lib/multivm/src/versions/vm_m5/memory.rs index 2c0b317a798a..dc58450263e4 100644 --- a/core/lib/multivm/src/versions/vm_m5/memory.rs +++ b/core/lib/multivm/src/versions/vm_m5/memory.rs @@ -1,12 +1,16 @@ -use zk_evm_1_3_1::abstractions::{Memory, MemoryType, MEMORY_CELLS_OTHER_PAGES}; -use zk_evm_1_3_1::aux_structures::{MemoryPage, MemoryQuery, Timestamp}; -use zk_evm_1_3_1::vm_state::PrimitiveValue; -use zk_evm_1_3_1::zkevm_opcode_defs::FatPointer; +use zk_evm_1_3_1::{ + abstractions::{Memory, MemoryType, MEMORY_CELLS_OTHER_PAGES}, + aux_structures::{MemoryPage, MemoryQuery, Timestamp}, + vm_state::PrimitiveValue, + zkevm_opcode_defs::FatPointer, +}; use zksync_types::U256; -use crate::vm_m5::history_recorder::{IntFrameManagerWithHistory, MemoryWithHistory}; -use crate::vm_m5::oracles::OracleWithHistory; -use crate::vm_m5::utils::{aux_heap_page_from_base, heap_page_from_base, stack_page_from_base}; +use crate::vm_m5::{ + history_recorder::{IntFrameManagerWithHistory, MemoryWithHistory}, + oracles::OracleWithHistory, + utils::{aux_heap_page_from_base, heap_page_from_base, stack_page_from_base}, +}; #[derive(Debug, Default, Clone, PartialEq)] pub struct SimpleMemory { diff --git a/core/lib/multivm/src/versions/vm_m5/mod.rs b/core/lib/multivm/src/versions/vm_m5/mod.rs index d8231ea502d2..fc549761e033 100644 --- a/core/lib/multivm/src/versions/vm_m5/mod.rs +++ b/core/lib/multivm/src/versions/vm_m5/mod.rs @@ -1,5 +1,16 @@ #![allow(clippy::derive_partial_eq_without_eq)] +pub use zk_evm_1_3_1; +pub use zksync_types::vm_trace::VmExecutionTrace; + +pub use self::{ + errors::TxRevertReason, + oracle_tools::OracleTools, + oracles::storage::StorageOracle, + vm::Vm, + vm_instance::{VmBlockResult, VmExecutionResult}, +}; + mod bootloader_state; pub mod errors; pub mod event_sink; @@ -12,24 +23,14 @@ mod pubdata_utils; mod refunds; pub mod storage; pub mod test_utils; +#[cfg(test)] +mod tests; pub mod transaction_data; pub mod utils; +mod vm; pub mod vm_instance; pub mod vm_with_bootloader; -#[cfg(test)] -mod tests; -mod vm; - -pub use errors::TxRevertReason; -pub use oracle_tools::OracleTools; -pub use oracles::storage::StorageOracle; -pub use vm::Vm; -pub use vm_instance::VmBlockResult; -pub use vm_instance::VmExecutionResult; -pub use zk_evm_1_3_1; -pub use zksync_types::vm_trace::VmExecutionTrace; - pub type Word = zksync_types::U256; pub const MEMORY_SIZE: usize = 1 << 16; diff --git a/core/lib/multivm/src/versions/vm_m5/oracle_tools.rs b/core/lib/multivm/src/versions/vm_m5/oracle_tools.rs index 4858a23adb6e..32930f31cd71 100644 --- a/core/lib/multivm/src/versions/vm_m5/oracle_tools.rs +++ b/core/lib/multivm/src/versions/vm_m5/oracle_tools.rs @@ -1,15 +1,18 @@ -use crate::vm_m5::memory::SimpleMemory; -use crate::vm_m5::vm_instance::MultiVMSubversion; - use std::fmt::Debug; -use crate::vm_m5::event_sink::InMemoryEventSink; -use crate::vm_m5::oracles::decommitter::DecommitterOracle; -use crate::vm_m5::oracles::precompile::PrecompilesProcessorWithHistory; -use crate::vm_m5::oracles::storage::StorageOracle; -use crate::vm_m5::storage::{Storage, StoragePtr}; use zk_evm_1_3_1::witness_trace::DummyTracer; +use crate::vm_m5::{ + event_sink::InMemoryEventSink, + memory::SimpleMemory, + oracles::{ + decommitter::DecommitterOracle, precompile::PrecompilesProcessorWithHistory, + storage::StorageOracle, + }, + storage::{Storage, StoragePtr}, + vm_instance::MultiVMSubversion, +}; + #[derive(Debug)] pub struct OracleTools { pub storage: StorageOracle, diff --git a/core/lib/multivm/src/versions/vm_m5/oracles/decommitter.rs b/core/lib/multivm/src/versions/vm_m5/oracles/decommitter.rs index 24a18f998dfe..bc43c72966ea 100644 --- a/core/lib/multivm/src/versions/vm_m5/oracles/decommitter.rs +++ b/core/lib/multivm/src/versions/vm_m5/oracles/decommitter.rs @@ -1,20 +1,19 @@ use std::collections::HashMap; -use crate::vm_m5::history_recorder::HistoryRecorder; -use crate::vm_m5::storage::{Storage, StoragePtr}; - -use zk_evm_1_3_1::abstractions::MemoryType; -use zk_evm_1_3_1::aux_structures::Timestamp; use zk_evm_1_3_1::{ - abstractions::{DecommittmentProcessor, Memory}, - aux_structures::{DecommittmentQuery, MemoryIndex, MemoryLocation, MemoryPage, MemoryQuery}, + abstractions::{DecommittmentProcessor, Memory, MemoryType}, + aux_structures::{ + DecommittmentQuery, MemoryIndex, MemoryLocation, MemoryPage, MemoryQuery, Timestamp, + }, }; - use zksync_types::U256; -use zksync_utils::bytecode::bytecode_len_in_words; -use zksync_utils::{bytes_to_be_words, u256_to_h256}; +use zksync_utils::{bytecode::bytecode_len_in_words, bytes_to_be_words, u256_to_h256}; use super::OracleWithHistory; +use crate::vm_m5::{ + history_recorder::HistoryRecorder, + storage::{Storage, StoragePtr}, +}; #[derive(Debug)] pub struct DecommitterOracle { diff --git a/core/lib/multivm/src/versions/vm_m5/oracles/mod.rs b/core/lib/multivm/src/versions/vm_m5/oracles/mod.rs index 31686fa70f6e..6b821c68e9d2 100644 --- a/core/lib/multivm/src/versions/vm_m5/oracles/mod.rs +++ b/core/lib/multivm/src/versions/vm_m5/oracles/mod.rs @@ -1,11 +1,10 @@ use zk_evm_1_3_1::aux_structures::Timestamp; -// We will discard RAM as soon as the execution of a tx ends, so -// it is ok for now to use SimpleMemory -pub use zk_evm_1_3_1::reference_impls::memory::SimpleMemory as RamOracle; // All the changes to the events in the DB will be applied after the tx is executed, // so fow now it is fine. pub use zk_evm_1_3_1::reference_impls::event_sink::InMemoryEventSink as EventSinkOracle; - +// We will discard RAM as soon as the execution of a tx ends, so +// it is ok for now to use SimpleMemory +pub use zk_evm_1_3_1::reference_impls::memory::SimpleMemory as RamOracle; pub use zk_evm_1_3_1::testing::simple_tracer::NoopTracer; pub mod decommitter; diff --git a/core/lib/multivm/src/versions/vm_m5/oracles/precompile.rs b/core/lib/multivm/src/versions/vm_m5/oracles/precompile.rs index 137a1046d48d..41a00b2e8a5b 100644 --- a/core/lib/multivm/src/versions/vm_m5/oracles/precompile.rs +++ b/core/lib/multivm/src/versions/vm_m5/oracles/precompile.rs @@ -1,14 +1,11 @@ use zk_evm_1_3_1::{ - abstractions::Memory, - abstractions::PrecompileCyclesWitness, - abstractions::PrecompilesProcessor, + abstractions::{Memory, PrecompileCyclesWitness, PrecompilesProcessor}, aux_structures::{LogQuery, MemoryQuery, Timestamp}, precompiles::DefaultPrecompilesProcessor, }; -use crate::vm_m5::history_recorder::HistoryRecorder; - use super::OracleWithHistory; +use crate::vm_m5::history_recorder::HistoryRecorder; /// Wrap of DefaultPrecompilesProcessor that store queue /// of timestamp when precompiles are called to be executed. diff --git a/core/lib/multivm/src/versions/vm_m5/oracles/storage.rs b/core/lib/multivm/src/versions/vm_m5/oracles/storage.rs index ca2c3ab7514e..c81b90f9c9cf 100644 --- a/core/lib/multivm/src/versions/vm_m5/oracles/storage.rs +++ b/core/lib/multivm/src/versions/vm_m5/oracles/storage.rs @@ -1,29 +1,28 @@ use std::collections::HashMap; -use crate::vm_m5::storage::{Storage, StoragePtr}; - -use crate::vm_m5::history_recorder::{ - AppDataFrameManagerWithHistory, HashMapHistoryEvent, HistoryRecorder, StorageWrapper, -}; -use crate::vm_m5::vm_instance::MultiVMSubversion; - -use zk_evm_1_3_1::abstractions::RefundedAmounts; -use zk_evm_1_3_1::zkevm_opcode_defs::system_params::INITIAL_STORAGE_WRITE_PUBDATA_BYTES; use zk_evm_1_3_1::{ - abstractions::{RefundType, Storage as VmStorageOracle}, + abstractions::{RefundType, RefundedAmounts, Storage as VmStorageOracle}, aux_structures::{LogQuery, Timestamp}, reference_impls::event_sink::ApplicationData, + zkevm_opcode_defs::system_params::INITIAL_STORAGE_WRITE_PUBDATA_BYTES, }; - -use crate::glue::GlueInto; -use zksync_types::utils::storage_key_for_eth_balance; use zksync_types::{ - AccountTreeId, Address, StorageKey, StorageLogQuery, StorageLogQueryType, BOOTLOADER_ADDRESS, - U256, + utils::storage_key_for_eth_balance, AccountTreeId, Address, StorageKey, StorageLogQuery, + StorageLogQueryType, BOOTLOADER_ADDRESS, U256, }; use zksync_utils::u256_to_h256; use super::OracleWithHistory; +use crate::{ + glue::GlueInto, + vm_m5::{ + history_recorder::{ + AppDataFrameManagerWithHistory, HashMapHistoryEvent, HistoryRecorder, StorageWrapper, + }, + storage::{Storage, StoragePtr}, + vm_instance::MultiVMSubversion, + }, +}; // While the storage does not support different shards, it was decided to write the // code of the StorageOracle with the shard parameters in mind. diff --git a/core/lib/multivm/src/versions/vm_m5/oracles/tracer.rs b/core/lib/multivm/src/versions/vm_m5/oracles/tracer.rs index a9e3c32786ab..ac370f832e4c 100644 --- a/core/lib/multivm/src/versions/vm_m5/oracles/tracer.rs +++ b/core/lib/multivm/src/versions/vm_m5/oracles/tracer.rs @@ -1,19 +1,8 @@ -use std::fmt::Debug; use std::{ collections::HashSet, - fmt::{self, Display}, + fmt::{self, Debug, Display}, }; -use crate::vm_m5::{ - errors::VmRevertReasonParsingResult, - memory::SimpleMemory, - storage::StoragePtr, - utils::{aux_heap_page_from_base, heap_page_from_base}, - vm_instance::{get_vm_hook_params, VM_HOOK_POSITION}, - vm_with_bootloader::BOOTLOADER_HEAP_PAGE, -}; -// use zk_evm_1_3_1::testing::memory::SimpleMemory; -use crate::vm_m5::storage::Storage; use zk_evm_1_3_1::{ abstractions::{ AfterDecodingData, AfterExecutionData, BeforeExecutionData, Tracer, VmLocalStateData, @@ -26,7 +15,6 @@ use zk_evm_1_3_1::{ LogOpcode, Opcode, RetOpcode, UMAOpcode, }, }; - use zksync_types::{ get_code_key, web3::signing::keccak256, AccountTreeId, Address, StorageKey, ACCOUNT_CODE_STORAGE_ADDRESS, BOOTLOADER_ADDRESS, CONTRACT_DEPLOYER_ADDRESS, H256, @@ -37,6 +25,15 @@ use zksync_utils::{ be_bytes_to_safe_address, h256_to_account_address, u256_to_account_address, u256_to_h256, }; +use crate::vm_m5::{ + errors::VmRevertReasonParsingResult, + memory::SimpleMemory, + storage::{Storage, StoragePtr}, + utils::{aux_heap_page_from_base, heap_page_from_base}, + vm_instance::{get_vm_hook_params, VM_HOOK_POSITION}, + vm_with_bootloader::BOOTLOADER_HEAP_PAGE, +}; + pub trait ExecutionEndTracer: Tracer { // Returns whether the vm execution should stop. fn should_stop_execution(&self) -> bool; diff --git a/core/lib/multivm/src/versions/vm_m5/pubdata_utils.rs b/core/lib/multivm/src/versions/vm_m5/pubdata_utils.rs index 80c1cd2c0e46..63e45edcbb85 100644 --- a/core/lib/multivm/src/versions/vm_m5/pubdata_utils.rs +++ b/core/lib/multivm/src/versions/vm_m5/pubdata_utils.rs @@ -1,16 +1,21 @@ -use crate::vm_m5::oracles::storage::storage_key_of_log; -use crate::vm_m5::storage::Storage; -use crate::vm_m5::utils::collect_storage_log_queries_after_timestamp; -use crate::vm_m5::vm_instance::VmInstance; use std::collections::HashMap; -use zk_evm_1_3_1::aux_structures::Timestamp; -use crate::glue::GlueInto; -use zksync_types::event::{extract_long_l2_to_l1_messages, extract_published_bytecodes}; -use zksync_types::zkevm_test_harness::witness::sort_storage_access::sort_storage_access_queries; -use zksync_types::{StorageKey, PUBLISH_BYTECODE_OVERHEAD, SYSTEM_CONTEXT_ADDRESS}; +use zk_evm_1_3_1::aux_structures::Timestamp; +use zksync_types::{ + event::{extract_long_l2_to_l1_messages, extract_published_bytecodes}, + zkevm_test_harness::witness::sort_storage_access::sort_storage_access_queries, + StorageKey, PUBLISH_BYTECODE_OVERHEAD, SYSTEM_CONTEXT_ADDRESS, +}; use zksync_utils::bytecode::bytecode_len_in_bytes; +use crate::{ + glue::GlueInto, + vm_m5::{ + oracles::storage::storage_key_of_log, storage::Storage, + utils::collect_storage_log_queries_after_timestamp, vm_instance::VmInstance, + }, +}; + impl VmInstance { pub fn pubdata_published(&self, from_timestamp: Timestamp) -> u32 { let storage_writes_pubdata_published = self.pubdata_published_for_writes(from_timestamp); diff --git a/core/lib/multivm/src/versions/vm_m5/refunds.rs b/core/lib/multivm/src/versions/vm_m5/refunds.rs index 8f1b2b44f4d2..8e084fd9ee32 100644 --- a/core/lib/multivm/src/versions/vm_m5/refunds.rs +++ b/core/lib/multivm/src/versions/vm_m5/refunds.rs @@ -1,13 +1,13 @@ -use crate::vm_m5::storage::Storage; -use crate::vm_m5::vm_instance::VmInstance; -use crate::vm_m5::vm_with_bootloader::{ - eth_price_per_pubdata_byte, BOOTLOADER_HEAP_PAGE, TX_GAS_LIMIT_OFFSET, -}; use zk_evm_1_3_1::aux_structures::Timestamp; - use zksync_types::U256; use zksync_utils::ceil_div_u256; +use crate::vm_m5::{ + storage::Storage, + vm_instance::VmInstance, + vm_with_bootloader::{eth_price_per_pubdata_byte, BOOTLOADER_HEAP_PAGE, TX_GAS_LIMIT_OFFSET}, +}; + impl VmInstance { pub(crate) fn tx_body_refund( &self, diff --git a/core/lib/multivm/src/versions/vm_m5/storage.rs b/core/lib/multivm/src/versions/vm_m5/storage.rs index d5f448812ca4..deb3501b4160 100644 --- a/core/lib/multivm/src/versions/vm_m5/storage.rs +++ b/core/lib/multivm/src/versions/vm_m5/storage.rs @@ -1,7 +1,4 @@ -use std::cell::RefCell; -use std::collections::HashMap; -use std::fmt::Debug; -use std::rc::Rc; +use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc}; use zksync_state::{ReadStorage, WriteStorage}; use zksync_types::{StorageKey, StorageValue, H256}; diff --git a/core/lib/multivm/src/versions/vm_m5/test_utils.rs b/core/lib/multivm/src/versions/vm_m5/test_utils.rs index 36c1d60dfdac..6920e77b8a8c 100644 --- a/core/lib/multivm/src/versions/vm_m5/test_utils.rs +++ b/core/lib/multivm/src/versions/vm_m5/test_utils.rs @@ -12,8 +12,10 @@ use itertools::Itertools; use zk_evm_1_3_1::{ aux_structures::Timestamp, reference_impls::event_sink::ApplicationData, vm_state::VmLocalState, }; -use zksync_contracts::test_contracts::LoadnextContractExecutionParams; -use zksync_contracts::{deployer_contract, get_loadnext_contract, load_contract}; +use zksync_contracts::{ + deployer_contract, get_loadnext_contract, load_contract, + test_contracts::LoadnextContractExecutionParams, +}; use zksync_types::{ ethabi::{Address, Token}, fee::Fee, @@ -26,13 +28,12 @@ use zksync_utils::{ address_to_h256, bytecode::hash_bytecode, h256_to_account_address, u256_to_h256, }; -use crate::vm_m5::storage::Storage; -use crate::vm_m5::vm_instance::VmInstance; -/// The tests here help us with the testing the VM use crate::vm_m5::{ event_sink::InMemoryEventSink, history_recorder::{FrameManager, HistoryRecorder}, memory::SimpleMemory, + storage::Storage, + vm_instance::VmInstance, }; #[derive(Clone, Debug)] diff --git a/core/lib/multivm/src/versions/vm_m5/transaction_data.rs b/core/lib/multivm/src/versions/vm_m5/transaction_data.rs index 819f22a53249..f150db2ebaa9 100644 --- a/core/lib/multivm/src/versions/vm_m5/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_m5/transaction_data.rs @@ -1,10 +1,13 @@ use zk_evm_1_3_1::zkevm_opcode_defs::system_params::{MAX_PUBDATA_PER_BLOCK, MAX_TX_ERGS_LIMIT}; -use zksync_types::ethabi::{encode, Address, Token}; -use zksync_types::fee::encoding_len; -use zksync_types::MAX_TXS_IN_BLOCK; -use zksync_types::{l2::TransactionType, ExecuteTransactionCommon, Transaction, U256}; -use zksync_utils::{address_to_h256, ceil_div_u256}; -use zksync_utils::{bytecode::hash_bytecode, bytes_to_be_words, h256_to_u256}; +use zksync_types::{ + ethabi::{encode, Address, Token}, + fee::encoding_len, + l2::TransactionType, + ExecuteTransactionCommon, Transaction, MAX_TXS_IN_BLOCK, U256, +}; +use zksync_utils::{ + address_to_h256, bytecode::hash_bytecode, bytes_to_be_words, ceil_div_u256, h256_to_u256, +}; use crate::vm_m5::vm_with_bootloader::{ BLOCK_OVERHEAD_GAS, BLOCK_OVERHEAD_PUBDATA, BOOTLOADER_TX_ENCODING_SPACE, diff --git a/core/lib/multivm/src/versions/vm_m5/utils.rs b/core/lib/multivm/src/versions/vm_m5/utils.rs index b8fef9944282..09170a8f502f 100644 --- a/core/lib/multivm/src/versions/vm_m5/utils.rs +++ b/core/lib/multivm/src/versions/vm_m5/utils.rs @@ -1,10 +1,7 @@ -use crate::vm_m5::{memory::SimpleMemory, vm_with_bootloader::BlockContext}; use once_cell::sync::Lazy; - -use crate::glue::GlueInto; -use zk_evm_1_3_1::block_properties::BlockProperties; use zk_evm_1_3_1::{ aux_structures::{LogQuery, MemoryPage, Timestamp}, + block_properties::BlockProperties, vm_state::PrimitiveValue, zkevm_opcode_defs::FatPointer, }; @@ -13,6 +10,11 @@ use zksync_system_constants::ZKPORTER_IS_AVAILABLE; use zksync_types::{Address, StorageLogQuery, H160, MAX_L2_TX_GAS_LIMIT, U256}; use zksync_utils::h256_to_u256; +use crate::{ + glue::GlueInto, + vm_m5::{memory::SimpleMemory, vm_with_bootloader::BlockContext}, +}; + pub const INITIAL_TIMESTAMP: u32 = 1024; pub const INITIAL_MEMORY_COUNTER: u32 = 2048; pub const INITIAL_CALLDATA_PAGE: u32 = 7; diff --git a/core/lib/multivm/src/versions/vm_m5/vm.rs b/core/lib/multivm/src/versions/vm_m5/vm.rs index 87186bb7f151..67c4f126309d 100644 --- a/core/lib/multivm/src/versions/vm_m5/vm.rs +++ b/core/lib/multivm/src/versions/vm_m5/vm.rs @@ -1,21 +1,23 @@ -use crate::interface::{ - BootloaderMemory, BytecodeCompressionError, CurrentExecutionState, FinishedL1Batch, L1BatchEnv, - L2BlockEnv, SystemEnv, TxExecutionMode, VmExecutionMode, VmExecutionResultAndLogs, VmInterface, - VmInterfaceHistoryEnabled, VmMemoryMetrics, -}; - use zksync_state::StoragePtr; -use zksync_types::l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}; -use zksync_types::{Transaction, VmVersion}; -use zksync_utils::bytecode::CompressedBytecodeInfo; -use zksync_utils::{h256_to_u256, u256_to_h256}; - -use crate::glue::history_mode::HistoryMode; -use crate::glue::GlueInto; -use crate::vm_m5::events::merge_events; -use crate::vm_m5::storage::Storage; -use crate::vm_m5::vm_instance::MultiVMSubversion; -use crate::vm_m5::vm_instance::VmInstance; +use zksync_types::{ + l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}, + Transaction, VmVersion, +}; +use zksync_utils::{bytecode::CompressedBytecodeInfo, h256_to_u256, u256_to_h256}; + +use crate::{ + glue::{history_mode::HistoryMode, GlueInto}, + interface::{ + BootloaderMemory, BytecodeCompressionError, CurrentExecutionState, FinishedL1Batch, + L1BatchEnv, L2BlockEnv, SystemEnv, TxExecutionMode, VmExecutionMode, + VmExecutionResultAndLogs, VmInterface, VmInterfaceHistoryEnabled, VmMemoryMetrics, + }, + vm_m5::{ + events::merge_events, + storage::Storage, + vm_instance::{MultiVMSubversion, VmInstance}, + }, +}; #[derive(Debug)] pub struct Vm { diff --git a/core/lib/multivm/src/versions/vm_m5/vm_instance.rs b/core/lib/multivm/src/versions/vm_m5/vm_instance.rs index 5638ed1c023c..99a96ded4d47 100644 --- a/core/lib/multivm/src/versions/vm_m5/vm_instance.rs +++ b/core/lib/multivm/src/versions/vm_m5/vm_instance.rs @@ -1,43 +1,52 @@ -use std::convert::TryFrom; -use std::fmt::Debug; - -use zk_evm_1_3_1::aux_structures::Timestamp; -use zk_evm_1_3_1::vm_state::{PrimitiveValue, VmLocalState, VmState}; -use zk_evm_1_3_1::witness_trace::DummyTracer; -use zk_evm_1_3_1::zkevm_opcode_defs::decoding::{ - AllowedPcOrImm, EncodingModeProduction, VmEncodingMode, +use std::{convert::TryFrom, fmt::Debug}; + +use zk_evm_1_3_1::{ + aux_structures::Timestamp, + vm_state::{PrimitiveValue, VmLocalState, VmState}, + witness_trace::DummyTracer, + zkevm_opcode_defs::{ + decoding::{AllowedPcOrImm, EncodingModeProduction, VmEncodingMode}, + definitions::RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER, + }, }; -use zk_evm_1_3_1::zkevm_opcode_defs::definitions::RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER; use zksync_system_constants::MAX_TXS_IN_BLOCK; - -use crate::glue::GlueInto; -use zksync_types::l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}; -use zksync_types::tx::tx_execution_info::TxExecutionStatus; -use zksync_types::vm_trace::VmExecutionTrace; -use zksync_types::{L1BatchNumber, StorageLogQuery, VmEvent, U256}; - -use crate::interface::types::outputs::VmExecutionLogs; -use crate::vm_m5::bootloader_state::BootloaderState; -use crate::vm_m5::errors::{TxRevertReason, VmRevertReason, VmRevertReasonParsingResult}; -use crate::vm_m5::event_sink::InMemoryEventSink; -use crate::vm_m5::events::merge_events; -use crate::vm_m5::memory::SimpleMemory; -use crate::vm_m5::oracles::decommitter::DecommitterOracle; -use crate::vm_m5::oracles::precompile::PrecompilesProcessorWithHistory; -use crate::vm_m5::oracles::storage::StorageOracle; -use crate::vm_m5::oracles::tracer::{ - BootloaderTracer, ExecutionEndTracer, OneTxTracer, PendingRefundTracer, PubdataSpentTracer, - TransactionResultTracer, ValidationError, ValidationTracer, ValidationTracerParams, -}; -use crate::vm_m5::oracles::OracleWithHistory; -use crate::vm_m5::storage::Storage; -use crate::vm_m5::utils::{ - collect_log_queries_after_timestamp, collect_storage_log_queries_after_timestamp, - dump_memory_page_using_primitive_value, precompile_calls_count_after_timestamp, +use zksync_types::{ + l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}, + tx::tx_execution_info::TxExecutionStatus, + vm_trace::VmExecutionTrace, + L1BatchNumber, StorageLogQuery, VmEvent, U256, }; -use crate::vm_m5::vm_with_bootloader::{ - BootloaderJobType, DerivedBlockContext, TxExecutionMode, BOOTLOADER_HEAP_PAGE, - OPERATOR_REFUNDS_OFFSET, + +use crate::{ + glue::GlueInto, + interface::types::outputs::VmExecutionLogs, + vm_m5::{ + bootloader_state::BootloaderState, + errors::{TxRevertReason, VmRevertReason, VmRevertReasonParsingResult}, + event_sink::InMemoryEventSink, + events::merge_events, + memory::SimpleMemory, + oracles::{ + decommitter::DecommitterOracle, + precompile::PrecompilesProcessorWithHistory, + storage::StorageOracle, + tracer::{ + BootloaderTracer, ExecutionEndTracer, OneTxTracer, PendingRefundTracer, + PubdataSpentTracer, TransactionResultTracer, ValidationError, ValidationTracer, + ValidationTracerParams, + }, + OracleWithHistory, + }, + storage::Storage, + utils::{ + collect_log_queries_after_timestamp, collect_storage_log_queries_after_timestamp, + dump_memory_page_using_primitive_value, precompile_calls_count_after_timestamp, + }, + vm_with_bootloader::{ + BootloaderJobType, DerivedBlockContext, TxExecutionMode, BOOTLOADER_HEAP_PAGE, + OPERATOR_REFUNDS_OFFSET, + }, + }, }; pub type ZkSyncVmState = VmState< diff --git a/core/lib/multivm/src/versions/vm_m5/vm_with_bootloader.rs b/core/lib/multivm/src/versions/vm_m5/vm_with_bootloader.rs index 0116660594e3..f9ba88fea143 100644 --- a/core/lib/multivm/src/versions/vm_m5/vm_with_bootloader.rs +++ b/core/lib/multivm/src/versions/vm_m5/vm_with_bootloader.rs @@ -12,7 +12,6 @@ use zk_evm_1_3_1::{ }; use zksync_contracts::BaseSystemContracts; use zksync_system_constants::MAX_TXS_IN_BLOCK; - use zksync_types::{ zkevm_test_harness::INITIAL_MONOTONIC_CYCLE_COUNTER, Address, Transaction, BOOTLOADER_ADDRESS, L1_GAS_PER_PUBDATA_BYTE, MAX_GAS_PER_PUBDATA_BYTE, MAX_NEW_FACTORY_DEPS, U256, @@ -21,16 +20,15 @@ use zksync_utils::{ address_to_u256, bytecode::hash_bytecode, bytes_to_be_words, h256_to_u256, misc::ceil_div, }; -use crate::vm_m5::storage::Storage; use crate::vm_m5::{ bootloader_state::BootloaderState, oracles::OracleWithHistory, + storage::Storage, transaction_data::TransactionData, utils::{ code_page_candidate_from_base, heap_page_from_base, BLOCK_GAS_LIMIT, INITIAL_BASE_PAGE, }, - vm_instance::VmInstance, - vm_instance::{MultiVMSubversion, ZkSyncVmState}, + vm_instance::{MultiVMSubversion, VmInstance, ZkSyncVmState}, OracleTools, }; diff --git a/core/lib/multivm/src/versions/vm_m6/errors/vm_revert_reason.rs b/core/lib/multivm/src/versions/vm_m6/errors/vm_revert_reason.rs index 9025ee9f3781..fb2341c0b2eb 100644 --- a/core/lib/multivm/src/versions/vm_m6/errors/vm_revert_reason.rs +++ b/core/lib/multivm/src/versions/vm_m6/errors/vm_revert_reason.rs @@ -1,5 +1,7 @@ -use std::convert::TryFrom; -use std::fmt::{Debug, Display}; +use std::{ + convert::TryFrom, + fmt::{Debug, Display}, +}; use zksync_types::U256; diff --git a/core/lib/multivm/src/versions/vm_m6/event_sink.rs b/core/lib/multivm/src/versions/vm_m6/event_sink.rs index 41fd22e9eedb..2fb5d934e969 100644 --- a/core/lib/multivm/src/versions/vm_m6/event_sink.rs +++ b/core/lib/multivm/src/versions/vm_m6/event_sink.rs @@ -1,9 +1,5 @@ -use crate::vm_m6::{ - history_recorder::{AppDataFrameManagerWithHistory, HistoryEnabled, HistoryMode}, - oracles::OracleWithHistory, - utils::collect_log_queries_after_timestamp, -}; use std::collections::HashMap; + use zk_evm_1_3_1::{ abstractions::EventSink, aux_structures::{LogQuery, Timestamp}, @@ -13,6 +9,12 @@ use zk_evm_1_3_1::{ }, }; +use crate::vm_m6::{ + history_recorder::{AppDataFrameManagerWithHistory, HistoryEnabled, HistoryMode}, + oracles::OracleWithHistory, + utils::collect_log_queries_after_timestamp, +}; + #[derive(Debug, Clone, PartialEq, Default)] pub struct InMemoryEventSink { pub frames_stack: AppDataFrameManagerWithHistory, diff --git a/core/lib/multivm/src/versions/vm_m6/history_recorder.rs b/core/lib/multivm/src/versions/vm_m6/history_recorder.rs index a85279e56c13..7ec8b2fde3bf 100644 --- a/core/lib/multivm/src/versions/vm_m6/history_recorder.rs +++ b/core/lib/multivm/src/versions/vm_m6/history_recorder.rs @@ -4,17 +4,16 @@ use std::{ hash::{BuildHasherDefault, Hash, Hasher}, }; -use crate::vm_m6::storage::{Storage, StoragePtr}; - use zk_evm_1_3_1::{ aux_structures::Timestamp, vm_state::PrimitiveValue, zkevm_opcode_defs::{self}, }; - use zksync_types::{StorageKey, U256}; use zksync_utils::{h256_to_u256, u256_to_h256}; +use crate::vm_m6::storage::{Storage, StoragePtr}; + pub type MemoryWithHistory = HistoryRecorder; pub type IntFrameManagerWithHistory = HistoryRecorder, H>; diff --git a/core/lib/multivm/src/versions/vm_m6/memory.rs b/core/lib/multivm/src/versions/vm_m6/memory.rs index 52a3d7f606f8..5a5042e5657f 100644 --- a/core/lib/multivm/src/versions/vm_m6/memory.rs +++ b/core/lib/multivm/src/versions/vm_m6/memory.rs @@ -1,15 +1,19 @@ -use zk_evm_1_3_1::abstractions::{Memory, MemoryType, MEMORY_CELLS_OTHER_PAGES}; -use zk_evm_1_3_1::aux_structures::{MemoryPage, MemoryQuery, Timestamp}; -use zk_evm_1_3_1::vm_state::PrimitiveValue; -use zk_evm_1_3_1::zkevm_opcode_defs::FatPointer; +use zk_evm_1_3_1::{ + abstractions::{Memory, MemoryType, MEMORY_CELLS_OTHER_PAGES}, + aux_structures::{MemoryPage, MemoryQuery, Timestamp}, + vm_state::PrimitiveValue, + zkevm_opcode_defs::FatPointer, +}; use zksync_types::U256; -use crate::vm_m6::history_recorder::{ - FramedStack, HistoryEnabled, HistoryMode, IntFrameManagerWithHistory, MemoryWithHistory, - MemoryWrapper, WithHistory, +use crate::vm_m6::{ + history_recorder::{ + FramedStack, HistoryEnabled, HistoryMode, IntFrameManagerWithHistory, MemoryWithHistory, + MemoryWrapper, WithHistory, + }, + oracles::OracleWithHistory, + utils::{aux_heap_page_from_base, heap_page_from_base, stack_page_from_base}, }; -use crate::vm_m6::oracles::OracleWithHistory; -use crate::vm_m6::utils::{aux_heap_page_from_base, heap_page_from_base, stack_page_from_base}; #[derive(Debug, Clone, PartialEq, Default)] pub struct SimpleMemory { diff --git a/core/lib/multivm/src/versions/vm_m6/oracle_tools.rs b/core/lib/multivm/src/versions/vm_m6/oracle_tools.rs index 4acc2fe68e57..7ae5e874806c 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracle_tools.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracle_tools.rs @@ -1,16 +1,18 @@ -use crate::vm_m6::memory::SimpleMemory; - use std::fmt::Debug; -use crate::vm_m6::event_sink::InMemoryEventSink; -use crate::vm_m6::history_recorder::HistoryMode; -use crate::vm_m6::oracles::{ - decommitter::DecommitterOracle, precompile::PrecompilesProcessorWithHistory, - storage::StorageOracle, -}; -use crate::vm_m6::storage::{Storage, StoragePtr}; use zk_evm_1_3_1::witness_trace::DummyTracer; +use crate::vm_m6::{ + event_sink::InMemoryEventSink, + history_recorder::HistoryMode, + memory::SimpleMemory, + oracles::{ + decommitter::DecommitterOracle, precompile::PrecompilesProcessorWithHistory, + storage::StorageOracle, + }, + storage::{Storage, StoragePtr}, +}; + /// zkEVM requires a bunch of objects implementing given traits to work. /// For example: Storage, Memory, PrecompilerProcessor etc /// (you can find all these traits in zk_evm crate -> src/abstractions/mod.rs) diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/decommitter.rs b/core/lib/multivm/src/versions/vm_m6/oracles/decommitter.rs index 48948827c3dd..a1c2a97edf9f 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/decommitter.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/decommitter.rs @@ -1,19 +1,19 @@ use std::collections::HashMap; -use crate::vm_m6::history_recorder::{HistoryEnabled, HistoryMode, HistoryRecorder, WithHistory}; -use crate::vm_m6::storage::{Storage, StoragePtr}; - -use zk_evm_1_3_1::abstractions::MemoryType; -use zk_evm_1_3_1::aux_structures::Timestamp; use zk_evm_1_3_1::{ - abstractions::{DecommittmentProcessor, Memory}, - aux_structures::{DecommittmentQuery, MemoryIndex, MemoryLocation, MemoryPage, MemoryQuery}, + abstractions::{DecommittmentProcessor, Memory, MemoryType}, + aux_structures::{ + DecommittmentQuery, MemoryIndex, MemoryLocation, MemoryPage, MemoryQuery, Timestamp, + }, }; use zksync_types::U256; -use zksync_utils::bytecode::bytecode_len_in_words; -use zksync_utils::{bytes_to_be_words, u256_to_h256}; +use zksync_utils::{bytecode::bytecode_len_in_words, bytes_to_be_words, u256_to_h256}; use super::OracleWithHistory; +use crate::vm_m6::{ + history_recorder::{HistoryEnabled, HistoryMode, HistoryRecorder, WithHistory}, + storage::{Storage, StoragePtr}, +}; /// The main job of the DecommiterOracle is to implement the DecommittmentProcessor trait - that is /// used by the VM to 'load' bytecodes into memory. diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/mod.rs b/core/lib/multivm/src/versions/vm_m6/oracles/mod.rs index d6b00c8500d7..2b7aa3a49f7d 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/mod.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/mod.rs @@ -1,11 +1,10 @@ use zk_evm_1_3_1::aux_structures::Timestamp; -// We will discard RAM as soon as the execution of a tx ends, so -// it is ok for now to use SimpleMemory -pub use zk_evm_1_3_1::reference_impls::memory::SimpleMemory as RamOracle; // All the changes to the events in the DB will be applied after the tx is executed, // so fow now it is fine. pub use zk_evm_1_3_1::reference_impls::event_sink::InMemoryEventSink as EventSinkOracle; - +// We will discard RAM as soon as the execution of a tx ends, so +// it is ok for now to use SimpleMemory +pub use zk_evm_1_3_1::reference_impls::memory::SimpleMemory as RamOracle; pub use zk_evm_1_3_1::testing::simple_tracer::NoopTracer; pub mod decommitter; diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/precompile.rs b/core/lib/multivm/src/versions/vm_m6/oracles/precompile.rs index aff382614af0..2e236b702679 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/precompile.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/precompile.rs @@ -1,14 +1,11 @@ use zk_evm_1_3_1::{ - abstractions::Memory, - abstractions::PrecompileCyclesWitness, - abstractions::PrecompilesProcessor, + abstractions::{Memory, PrecompileCyclesWitness, PrecompilesProcessor}, aux_structures::{LogQuery, MemoryQuery, Timestamp}, precompiles::DefaultPrecompilesProcessor, }; -use crate::vm_m6::history_recorder::{HistoryEnabled, HistoryMode, HistoryRecorder}; - use super::OracleWithHistory; +use crate::vm_m6::history_recorder::{HistoryEnabled, HistoryMode, HistoryRecorder}; /// Wrap of DefaultPrecompilesProcessor that store queue /// of timestamp when precompiles are called to be executed. diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/storage.rs b/core/lib/multivm/src/versions/vm_m6/oracles/storage.rs index 45c3bdf50f8e..7ceab94bd472 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/storage.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/storage.rs @@ -1,27 +1,27 @@ use std::collections::HashMap; -use crate::vm_m6::storage::{Storage, StoragePtr}; - -use crate::glue::GlueInto; -use crate::vm_m6::history_recorder::{ - AppDataFrameManagerWithHistory, HashMapHistoryEvent, HistoryEnabled, HistoryMode, - HistoryRecorder, StorageWrapper, WithHistory, -}; - -use zk_evm_1_3_1::abstractions::RefundedAmounts; -use zk_evm_1_3_1::zkevm_opcode_defs::system_params::INITIAL_STORAGE_WRITE_PUBDATA_BYTES; use zk_evm_1_3_1::{ - abstractions::{RefundType, Storage as VmStorageOracle}, + abstractions::{RefundType, RefundedAmounts, Storage as VmStorageOracle}, aux_structures::{LogQuery, Timestamp}, + zkevm_opcode_defs::system_params::INITIAL_STORAGE_WRITE_PUBDATA_BYTES, }; -use zksync_types::utils::storage_key_for_eth_balance; use zksync_types::{ - AccountTreeId, Address, StorageKey, StorageLogQuery, StorageLogQueryType, BOOTLOADER_ADDRESS, - U256, + utils::storage_key_for_eth_balance, AccountTreeId, Address, StorageKey, StorageLogQuery, + StorageLogQueryType, BOOTLOADER_ADDRESS, U256, }; use zksync_utils::u256_to_h256; use super::OracleWithHistory; +use crate::{ + glue::GlueInto, + vm_m6::{ + history_recorder::{ + AppDataFrameManagerWithHistory, HashMapHistoryEvent, HistoryEnabled, HistoryMode, + HistoryRecorder, StorageWrapper, WithHistory, + }, + storage::{Storage, StoragePtr}, + }, +}; // While the storage does not support different shards, it was decided to write the // code of the StorageOracle with the shard parameters in mind. diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/bootloader.rs b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/bootloader.rs index 81902f330a55..5509cef90832 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/bootloader.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/bootloader.rs @@ -1,12 +1,5 @@ use std::marker::PhantomData; -use crate::vm_m6::history_recorder::HistoryMode; -use crate::vm_m6::memory::SimpleMemory; -use crate::vm_m6::oracles::tracer::{ - utils::gas_spent_on_bytecodes_and_long_messages_this_opcode, ExecutionEndTracer, - PendingRefundTracer, PubdataSpentTracer, StorageInvocationTracer, -}; - use zk_evm_1_3_1::{ abstractions::{ AfterDecodingData, AfterExecutionData, BeforeExecutionData, Tracer, VmLocalStateData, @@ -16,6 +9,15 @@ use zk_evm_1_3_1::{ zkevm_opcode_defs::{Opcode, RetOpcode}, }; +use crate::vm_m6::{ + history_recorder::HistoryMode, + memory::SimpleMemory, + oracles::tracer::{ + utils::gas_spent_on_bytecodes_and_long_messages_this_opcode, ExecutionEndTracer, + PendingRefundTracer, PubdataSpentTracer, StorageInvocationTracer, + }, +}; + /// Tells the VM to end the execution before `ret` from the bootloader if there is no panic or revert. /// Also, saves the information if this `ret` was caused by "out of gas" panic. #[derive(Debug, Clone, Default)] diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/call.rs b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/call.rs index f2ddd2762adb..1166e7a8cdb6 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/call.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/call.rs @@ -1,21 +1,24 @@ -use crate::glue::GlueInto; -use crate::vm_m6::errors::VmRevertReason; -use crate::vm_m6::history_recorder::HistoryMode; -use crate::vm_m6::memory::SimpleMemory; -use std::convert::TryFrom; -use std::marker::PhantomData; -use std::mem; -use zk_evm_1_3_1::abstractions::{ - AfterDecodingData, AfterExecutionData, BeforeExecutionData, Tracer, VmLocalStateData, -}; -use zk_evm_1_3_1::zkevm_opcode_defs::FatPointer; -use zk_evm_1_3_1::zkevm_opcode_defs::{ - FarCallABI, FarCallOpcode, Opcode, RetOpcode, CALL_IMPLICIT_CALLDATA_FAT_PTR_REGISTER, - RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER, +use std::{convert::TryFrom, marker::PhantomData, mem}; + +use zk_evm_1_3_1::{ + abstractions::{ + AfterDecodingData, AfterExecutionData, BeforeExecutionData, Tracer, VmLocalStateData, + }, + zkevm_opcode_defs::{ + FarCallABI, FarCallOpcode, FatPointer, Opcode, RetOpcode, + CALL_IMPLICIT_CALLDATA_FAT_PTR_REGISTER, RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER, + }, }; use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS; -use zksync_types::vm_trace::{Call, CallType}; -use zksync_types::U256; +use zksync_types::{ + vm_trace::{Call, CallType}, + U256, +}; + +use crate::{ + glue::GlueInto, + vm_m6::{errors::VmRevertReason, history_recorder::HistoryMode, memory::SimpleMemory}, +}; /// NOTE Auto implementing clone for this tracer can cause stack overflow. /// This is because of the stack field which is a Vec with nested vecs inside. @@ -283,10 +286,13 @@ fn filter_near_call(mut call: Call) -> Vec { #[cfg(test)] mod tests { - use crate::glue::GlueInto; - use crate::vm_m6::oracles::tracer::call::{filter_near_call, Call, CallType}; use zk_evm_1_3_1::zkevm_opcode_defs::FarCallOpcode; + use crate::{ + glue::GlueInto, + vm_m6::oracles::tracer::call::{filter_near_call, Call, CallType}, + }; + #[test] fn test_filter_near_calls() { let mut call = Call::default(); diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/mod.rs b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/mod.rs index 93486f039fac..cdf83345d2fd 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/mod.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/mod.rs @@ -1,5 +1,15 @@ -use zk_evm_1_3_1::abstractions::Tracer; -use zk_evm_1_3_1::vm_state::VmLocalState; +use zk_evm_1_3_1::{abstractions::Tracer, vm_state::VmLocalState}; + +pub(crate) use self::transaction_result::TransactionResultTracer; +pub use self::{ + bootloader::BootloaderTracer, + call::CallTracer, + one_tx::OneTxTracer, + validation::{ + ValidationError, ValidationTracer, ValidationTracerParams, ViolatedValidationRule, + }, +}; +use crate::vm_m6::{history_recorder::HistoryMode, memory::SimpleMemory}; mod bootloader; mod call; @@ -8,18 +18,6 @@ mod transaction_result; mod utils; mod validation; -pub use bootloader::BootloaderTracer; -pub use call::CallTracer; -pub use one_tx::OneTxTracer; -pub use validation::{ - ValidationError, ValidationTracer, ValidationTracerParams, ViolatedValidationRule, -}; - -pub(crate) use transaction_result::TransactionResultTracer; - -use crate::vm_m6::history_recorder::HistoryMode; -use crate::vm_m6::memory::SimpleMemory; - pub trait ExecutionEndTracer: Tracer> { // Returns whether the vm execution should stop. fn should_stop_execution(&self) -> bool; diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/one_tx.rs b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/one_tx.rs index d5fbb78c9096..346daba21317 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/one_tx.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/one_tx.rs @@ -1,23 +1,23 @@ +use zk_evm_1_3_1::{ + abstractions::{ + AfterDecodingData, AfterExecutionData, BeforeExecutionData, Tracer, VmLocalStateData, + }, + vm_state::VmLocalState, +}; +use zksync_types::vm_trace::Call; + use super::utils::{computational_gas_price, print_debug_if_needed}; use crate::vm_m6::{ history_recorder::HistoryMode, memory::SimpleMemory, oracles::tracer::{ utils::{gas_spent_on_bytecodes_and_long_messages_this_opcode, VmHook}, - BootloaderTracer, ExecutionEndTracer, PendingRefundTracer, PubdataSpentTracer, + BootloaderTracer, CallTracer, ExecutionEndTracer, PendingRefundTracer, PubdataSpentTracer, + StorageInvocationTracer, }, vm_instance::get_vm_hook_params, }; -use crate::vm_m6::oracles::tracer::{CallTracer, StorageInvocationTracer}; -use zk_evm_1_3_1::{ - abstractions::{ - AfterDecodingData, AfterExecutionData, BeforeExecutionData, Tracer, VmLocalStateData, - }, - vm_state::VmLocalState, -}; -use zksync_types::vm_trace::Call; - /// Allows any opcodes, but tells the VM to end the execution once the tx is over. // Internally depeds on Bootloader's VMHooks to get the notification once the transaction is finished. #[derive(Debug)] diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/transaction_result.rs b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/transaction_result.rs index a3e4391af24b..2ecf484b60af 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/transaction_result.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/transaction_result.rs @@ -7,18 +7,18 @@ use zk_evm_1_3_1::{ }; use zksync_types::{vm_trace, U256}; -use crate::vm_m6::memory::SimpleMemory; -use crate::vm_m6::oracles::tracer::{ - CallTracer, ExecutionEndTracer, PendingRefundTracer, PubdataSpentTracer, - StorageInvocationTracer, -}; -use crate::vm_m6::vm_instance::get_vm_hook_params; use crate::vm_m6::{ history_recorder::HistoryMode, - oracles::tracer::utils::{ - gas_spent_on_bytecodes_and_long_messages_this_opcode, print_debug_if_needed, read_pointer, - VmHook, + memory::SimpleMemory, + oracles::tracer::{ + utils::{ + gas_spent_on_bytecodes_and_long_messages_this_opcode, print_debug_if_needed, + read_pointer, VmHook, + }, + CallTracer, ExecutionEndTracer, PendingRefundTracer, PubdataSpentTracer, + StorageInvocationTracer, }, + vm_instance::get_vm_hook_params, }; #[derive(Debug)] diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/utils.rs b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/utils.rs index b256575726ae..d29476ea4ccc 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/utils.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/utils.rs @@ -1,14 +1,9 @@ -use crate::vm_m6::history_recorder::HistoryMode; -use crate::vm_m6::memory::SimpleMemory; -use crate::vm_m6::utils::{aux_heap_page_from_base, heap_page_from_base}; -use crate::vm_m6::vm_instance::{get_vm_hook_params, VM_HOOK_POSITION}; -use crate::vm_m6::vm_with_bootloader::BOOTLOADER_HEAP_PAGE; - -use zk_evm_1_3_1::aux_structures::MemoryPage; -use zk_evm_1_3_1::zkevm_opcode_defs::{FarCallABI, FarCallForwardPageType}; use zk_evm_1_3_1::{ abstractions::{BeforeExecutionData, VmLocalStateData}, - zkevm_opcode_defs::{FatPointer, LogOpcode, Opcode, UMAOpcode}, + aux_structures::MemoryPage, + zkevm_opcode_defs::{ + FarCallABI, FarCallForwardPageType, FatPointer, LogOpcode, Opcode, UMAOpcode, + }, }; use zksync_system_constants::{ ECRECOVER_PRECOMPILE_ADDRESS, KECCAK256_PRECOMPILE_ADDRESS, KNOWN_CODES_STORAGE_ADDRESS, @@ -17,6 +12,14 @@ use zksync_system_constants::{ use zksync_types::U256; use zksync_utils::u256_to_h256; +use crate::vm_m6::{ + history_recorder::HistoryMode, + memory::SimpleMemory, + utils::{aux_heap_page_from_base, heap_page_from_base}, + vm_instance::{get_vm_hook_params, VM_HOOK_POSITION}, + vm_with_bootloader::BOOTLOADER_HEAP_PAGE, +}; + #[derive(Clone, Debug, Copy)] pub(crate) enum VmHook { AccountValidationEntered, diff --git a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/validation.rs b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/validation.rs index 13a0badd442b..e75a9f34a4ba 100644 --- a/core/lib/multivm/src/versions/vm_m6/oracles/tracer/validation.rs +++ b/core/lib/multivm/src/versions/vm_m6/oracles/tracer/validation.rs @@ -1,16 +1,4 @@ -use std::fmt; -use std::fmt::Display; -use std::{collections::HashSet, marker::PhantomData}; - -use crate::vm_m6::{ - errors::VmRevertReasonParsingResult, - history_recorder::HistoryMode, - memory::SimpleMemory, - oracles::tracer::{ - utils::{computational_gas_price, print_debug_if_needed, VmHook}, - ExecutionEndTracer, PendingRefundTracer, PubdataSpentTracer, - }, -}; +use std::{collections::HashSet, fmt, fmt::Display, marker::PhantomData}; use zk_evm_1_3_1::{ abstractions::{ @@ -18,15 +6,11 @@ use zk_evm_1_3_1::{ }, zkevm_opcode_defs::{ContextOpcode, FarCallABI, LogOpcode, Opcode}, }; - -use crate::vm_m6::oracles::tracer::{utils::get_calldata_page_via_abi, StorageInvocationTracer}; -use crate::vm_m6::storage::{Storage, StoragePtr}; use zksync_system_constants::{ ACCOUNT_CODE_STORAGE_ADDRESS, BOOTLOADER_ADDRESS, CONTRACT_DEPLOYER_ADDRESS, KECCAK256_PRECOMPILE_ADDRESS, L2_ETH_TOKEN_ADDRESS, MSG_VALUE_SIMULATOR_ADDRESS, SYSTEM_CONTEXT_ADDRESS, }; - use zksync_types::{ get_code_key, web3::signing::keccak256, AccountTreeId, Address, StorageKey, H256, U256, }; @@ -34,6 +18,19 @@ use zksync_utils::{ be_bytes_to_safe_address, h256_to_account_address, u256_to_account_address, u256_to_h256, }; +use crate::vm_m6::{ + errors::VmRevertReasonParsingResult, + history_recorder::HistoryMode, + memory::SimpleMemory, + oracles::tracer::{ + utils::{ + computational_gas_price, get_calldata_page_via_abi, print_debug_if_needed, VmHook, + }, + ExecutionEndTracer, PendingRefundTracer, PubdataSpentTracer, StorageInvocationTracer, + }, + storage::{Storage, StoragePtr}, +}; + #[derive(Debug, Clone, Eq, PartialEq, Copy)] #[allow(clippy::enum_variant_names)] pub enum ValidationTracerMode { diff --git a/core/lib/multivm/src/versions/vm_m6/pubdata_utils.rs b/core/lib/multivm/src/versions/vm_m6/pubdata_utils.rs index a823e5f5ae60..33307507f7ec 100644 --- a/core/lib/multivm/src/versions/vm_m6/pubdata_utils.rs +++ b/core/lib/multivm/src/versions/vm_m6/pubdata_utils.rs @@ -1,16 +1,21 @@ -use crate::glue::GlueInto; -use crate::vm_m6::history_recorder::HistoryMode; -use crate::vm_m6::oracles::storage::storage_key_of_log; -use crate::vm_m6::storage::Storage; -use crate::vm_m6::utils::collect_storage_log_queries_after_timestamp; -use crate::vm_m6::VmInstance; use std::collections::HashMap; + use zk_evm_1_3_1::aux_structures::Timestamp; -use zksync_types::event::{extract_long_l2_to_l1_messages, extract_published_bytecodes}; -use zksync_types::zkevm_test_harness::witness::sort_storage_access::sort_storage_access_queries; -use zksync_types::{StorageKey, PUBLISH_BYTECODE_OVERHEAD, SYSTEM_CONTEXT_ADDRESS}; +use zksync_types::{ + event::{extract_long_l2_to_l1_messages, extract_published_bytecodes}, + zkevm_test_harness::witness::sort_storage_access::sort_storage_access_queries, + StorageKey, PUBLISH_BYTECODE_OVERHEAD, SYSTEM_CONTEXT_ADDRESS, +}; use zksync_utils::bytecode::bytecode_len_in_bytes; +use crate::{ + glue::GlueInto, + vm_m6::{ + history_recorder::HistoryMode, oracles::storage::storage_key_of_log, storage::Storage, + utils::collect_storage_log_queries_after_timestamp, VmInstance, + }, +}; + impl VmInstance { pub fn pubdata_published(&self, from_timestamp: Timestamp) -> u32 { let storage_writes_pubdata_published = self.pubdata_published_for_writes(from_timestamp); diff --git a/core/lib/multivm/src/versions/vm_m6/refunds.rs b/core/lib/multivm/src/versions/vm_m6/refunds.rs index da16d6219114..4b4229b306b9 100644 --- a/core/lib/multivm/src/versions/vm_m6/refunds.rs +++ b/core/lib/multivm/src/versions/vm_m6/refunds.rs @@ -1,13 +1,14 @@ -use crate::vm_m6::history_recorder::HistoryMode; -use crate::vm_m6::storage::Storage; -use crate::vm_m6::vm_with_bootloader::{ - eth_price_per_pubdata_byte, BOOTLOADER_HEAP_PAGE, TX_GAS_LIMIT_OFFSET, -}; -use crate::vm_m6::VmInstance; use zk_evm_1_3_1::aux_structures::Timestamp; use zksync_types::U256; use zksync_utils::ceil_div_u256; +use crate::vm_m6::{ + history_recorder::HistoryMode, + storage::Storage, + vm_with_bootloader::{eth_price_per_pubdata_byte, BOOTLOADER_HEAP_PAGE, TX_GAS_LIMIT_OFFSET}, + VmInstance, +}; + impl VmInstance { pub(crate) fn tx_body_refund( &self, diff --git a/core/lib/multivm/src/versions/vm_m6/storage.rs b/core/lib/multivm/src/versions/vm_m6/storage.rs index 5441fc8a296c..80f7e0160108 100644 --- a/core/lib/multivm/src/versions/vm_m6/storage.rs +++ b/core/lib/multivm/src/versions/vm_m6/storage.rs @@ -1,7 +1,4 @@ -use std::cell::RefCell; -use std::collections::HashMap; -use std::fmt::Debug; -use std::rc::Rc; +use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc}; use zksync_state::{ReadStorage, WriteStorage}; use zksync_types::{get_known_code_key, StorageKey, StorageValue, H256}; diff --git a/core/lib/multivm/src/versions/vm_m6/test_utils.rs b/core/lib/multivm/src/versions/vm_m6/test_utils.rs index 6cce779362da..55e5add11648 100644 --- a/core/lib/multivm/src/versions/vm_m6/test_utils.rs +++ b/core/lib/multivm/src/versions/vm_m6/test_utils.rs @@ -10,8 +10,10 @@ use std::collections::HashMap; use itertools::Itertools; use zk_evm_1_3_1::{aux_structures::Timestamp, vm_state::VmLocalState}; -use zksync_contracts::test_contracts::LoadnextContractExecutionParams; -use zksync_contracts::{deployer_contract, get_loadnext_contract, load_contract}; +use zksync_contracts::{ + deployer_contract, get_loadnext_contract, load_contract, + test_contracts::LoadnextContractExecutionParams, +}; use zksync_types::{ ethabi::{Address, Token}, fee::Fee, @@ -24,14 +26,13 @@ use zksync_utils::{ address_to_h256, bytecode::hash_bytecode, h256_to_account_address, u256_to_h256, }; -use crate::vm_m6::storage::Storage; -/// The tests here help us with the testing the VM use crate::vm_m6::{ event_sink::InMemoryEventSink, history_recorder::{ AppDataFrameManagerWithHistory, HistoryEnabled, HistoryMode, HistoryRecorder, }, memory::SimpleMemory, + storage::Storage, VmInstance, }; diff --git a/core/lib/multivm/src/versions/vm_m6/transaction_data.rs b/core/lib/multivm/src/versions/vm_m6/transaction_data.rs index 6779ce95fc3e..136a6d7647ac 100644 --- a/core/lib/multivm/src/versions/vm_m6/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_m6/transaction_data.rs @@ -1,11 +1,14 @@ use zk_evm_1_3_1::zkevm_opcode_defs::system_params::MAX_TX_ERGS_LIMIT; -use zksync_types::ethabi::{encode, Address, Token}; -use zksync_types::fee::encoding_len; -use zksync_types::l1::is_l1_tx_type; -use zksync_types::{l2::TransactionType, ExecuteTransactionCommon, Transaction, U256}; -use zksync_types::{MAX_L2_TX_GAS_LIMIT, MAX_TXS_IN_BLOCK}; -use zksync_utils::{address_to_h256, ceil_div_u256}; -use zksync_utils::{bytecode::hash_bytecode, bytes_to_be_words, h256_to_u256}; +use zksync_types::{ + ethabi::{encode, Address, Token}, + fee::encoding_len, + l1::is_l1_tx_type, + l2::TransactionType, + ExecuteTransactionCommon, Transaction, MAX_L2_TX_GAS_LIMIT, MAX_TXS_IN_BLOCK, U256, +}; +use zksync_utils::{ + address_to_h256, bytecode::hash_bytecode, bytes_to_be_words, ceil_div_u256, h256_to_u256, +}; use crate::vm_m6::vm_with_bootloader::{ BLOCK_OVERHEAD_GAS, BLOCK_OVERHEAD_PUBDATA, BOOTLOADER_TX_ENCODING_SPACE, diff --git a/core/lib/multivm/src/versions/vm_m6/utils.rs b/core/lib/multivm/src/versions/vm_m6/utils.rs index a8ed8b02a527..070d51a6b252 100644 --- a/core/lib/multivm/src/versions/vm_m6/utils.rs +++ b/core/lib/multivm/src/versions/vm_m6/utils.rs @@ -1,15 +1,7 @@ -use crate::vm_m6::history_recorder::HistoryMode; -use crate::vm_m6::{ - memory::SimpleMemory, oracles::tracer::PubdataSpentTracer, vm_with_bootloader::BlockContext, - VmInstance, -}; use once_cell::sync::Lazy; - -use crate::glue::GlueInto; -use crate::vm_m6::storage::Storage; -use zk_evm_1_3_1::block_properties::BlockProperties; use zk_evm_1_3_1::{ aux_structures::{LogQuery, MemoryPage, Timestamp}, + block_properties::BlockProperties, vm_state::PrimitiveValue, zkevm_opcode_defs::FatPointer, }; @@ -18,6 +10,14 @@ use zksync_system_constants::ZKPORTER_IS_AVAILABLE; use zksync_types::{Address, StorageLogQuery, H160, MAX_L2_TX_GAS_LIMIT, U256}; use zksync_utils::h256_to_u256; +use crate::{ + glue::GlueInto, + vm_m6::{ + history_recorder::HistoryMode, memory::SimpleMemory, oracles::tracer::PubdataSpentTracer, + storage::Storage, vm_with_bootloader::BlockContext, VmInstance, + }, +}; + pub const INITIAL_TIMESTAMP: u32 = 1024; pub const INITIAL_MEMORY_COUNTER: u32 = 2048; pub const INITIAL_CALLDATA_PAGE: u32 = 7; diff --git a/core/lib/multivm/src/versions/vm_m6/vm.rs b/core/lib/multivm/src/versions/vm_m6/vm.rs index 2937b621a9a5..e82d51d2bf3b 100644 --- a/core/lib/multivm/src/versions/vm_m6/vm.rs +++ b/core/lib/multivm/src/versions/vm_m6/vm.rs @@ -1,23 +1,24 @@ -use crate::interface::{ - BootloaderMemory, BytecodeCompressionError, CurrentExecutionState, FinishedL1Batch, L1BatchEnv, - L2BlockEnv, SystemEnv, TxExecutionMode, VmExecutionMode, VmExecutionResultAndLogs, VmInterface, - VmInterfaceHistoryEnabled, VmMemoryMetrics, -}; - use std::collections::HashSet; use zksync_state::StoragePtr; -use zksync_types::l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}; -use zksync_types::{Transaction, VmVersion}; -use zksync_utils::bytecode::{hash_bytecode, CompressedBytecodeInfo}; -use zksync_utils::{h256_to_u256, u256_to_h256}; +use zksync_types::{ + l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}, + Transaction, VmVersion, +}; +use zksync_utils::{ + bytecode::{hash_bytecode, CompressedBytecodeInfo}, + h256_to_u256, u256_to_h256, +}; -use crate::glue::history_mode::HistoryMode; -use crate::glue::GlueInto; -use crate::vm_m6::events::merge_events; -use crate::vm_m6::storage::Storage; -use crate::vm_m6::vm_instance::MultiVMSubversion; -use crate::vm_m6::VmInstance; +use crate::{ + glue::{history_mode::HistoryMode, GlueInto}, + interface::{ + BootloaderMemory, BytecodeCompressionError, CurrentExecutionState, FinishedL1Batch, + L1BatchEnv, L2BlockEnv, SystemEnv, TxExecutionMode, VmExecutionMode, + VmExecutionResultAndLogs, VmInterface, VmInterfaceHistoryEnabled, VmMemoryMetrics, + }, + vm_m6::{events::merge_events, storage::Storage, vm_instance::MultiVMSubversion, VmInstance}, +}; #[derive(Debug)] pub struct Vm { diff --git a/core/lib/multivm/src/versions/vm_m6/vm_instance.rs b/core/lib/multivm/src/versions/vm_m6/vm_instance.rs index f15adde2584b..379476d7664c 100644 --- a/core/lib/multivm/src/versions/vm_m6/vm_instance.rs +++ b/core/lib/multivm/src/versions/vm_m6/vm_instance.rs @@ -1,45 +1,54 @@ -use std::convert::TryFrom; -use std::fmt::Debug; - -use crate::glue::GlueInto; -use zk_evm_1_3_1::aux_structures::Timestamp; -use zk_evm_1_3_1::vm_state::{PrimitiveValue, VmLocalState, VmState}; -use zk_evm_1_3_1::witness_trace::DummyTracer; -use zk_evm_1_3_1::zkevm_opcode_defs::decoding::{ - AllowedPcOrImm, EncodingModeProduction, VmEncodingMode, +use std::{convert::TryFrom, fmt::Debug}; + +use zk_evm_1_3_1::{ + aux_structures::Timestamp, + vm_state::{PrimitiveValue, VmLocalState, VmState}, + witness_trace::DummyTracer, + zkevm_opcode_defs::{ + decoding::{AllowedPcOrImm, EncodingModeProduction, VmEncodingMode}, + definitions::RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER, + }, }; -use zk_evm_1_3_1::zkevm_opcode_defs::definitions::RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER; use zksync_system_constants::MAX_TXS_IN_BLOCK; -use zksync_types::l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}; -use zksync_types::tx::tx_execution_info::TxExecutionStatus; -use zksync_types::vm_trace::{Call, VmExecutionTrace, VmTrace}; -use zksync_types::{L1BatchNumber, StorageLogQuery, VmEvent, H256, U256}; - -use crate::interface::types::outputs::VmExecutionLogs; -use crate::vm_m6::bootloader_state::BootloaderState; -use crate::vm_m6::errors::{TxRevertReason, VmRevertReason, VmRevertReasonParsingResult}; -use crate::vm_m6::event_sink::InMemoryEventSink; -use crate::vm_m6::events::merge_events; -use crate::vm_m6::history_recorder::{HistoryEnabled, HistoryMode}; -use crate::vm_m6::memory::SimpleMemory; -use crate::vm_m6::oracles::decommitter::DecommitterOracle; -use crate::vm_m6::oracles::precompile::PrecompilesProcessorWithHistory; -use crate::vm_m6::oracles::storage::StorageOracle; -use crate::vm_m6::oracles::tracer::{ - BootloaderTracer, ExecutionEndTracer, OneTxTracer, PendingRefundTracer, PubdataSpentTracer, - StorageInvocationTracer, TransactionResultTracer, ValidationError, ValidationTracer, - ValidationTracerParams, +use zksync_types::{ + l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}, + tx::tx_execution_info::TxExecutionStatus, + vm_trace::{Call, VmExecutionTrace, VmTrace}, + L1BatchNumber, StorageLogQuery, VmEvent, H256, U256, }; -use crate::vm_m6::oracles::OracleWithHistory; -use crate::vm_m6::storage::Storage; -use crate::vm_m6::utils::{ - calculate_computational_gas_used, collect_log_queries_after_timestamp, - collect_storage_log_queries_after_timestamp, dump_memory_page_using_primitive_value, - precompile_calls_count_after_timestamp, -}; -use crate::vm_m6::vm_with_bootloader::{ - BootloaderJobType, DerivedBlockContext, TxExecutionMode, BOOTLOADER_HEAP_PAGE, - OPERATOR_REFUNDS_OFFSET, + +use crate::{ + glue::GlueInto, + interface::types::outputs::VmExecutionLogs, + vm_m6::{ + bootloader_state::BootloaderState, + errors::{TxRevertReason, VmRevertReason, VmRevertReasonParsingResult}, + event_sink::InMemoryEventSink, + events::merge_events, + history_recorder::{HistoryEnabled, HistoryMode}, + memory::SimpleMemory, + oracles::{ + decommitter::DecommitterOracle, + precompile::PrecompilesProcessorWithHistory, + storage::StorageOracle, + tracer::{ + BootloaderTracer, ExecutionEndTracer, OneTxTracer, PendingRefundTracer, + PubdataSpentTracer, StorageInvocationTracer, TransactionResultTracer, + ValidationError, ValidationTracer, ValidationTracerParams, + }, + OracleWithHistory, + }, + storage::Storage, + utils::{ + calculate_computational_gas_used, collect_log_queries_after_timestamp, + collect_storage_log_queries_after_timestamp, dump_memory_page_using_primitive_value, + precompile_calls_count_after_timestamp, + }, + vm_with_bootloader::{ + BootloaderJobType, DerivedBlockContext, TxExecutionMode, BOOTLOADER_HEAP_PAGE, + OPERATOR_REFUNDS_OFFSET, + }, + }, }; pub type ZkSyncVmState = VmState< diff --git a/core/lib/multivm/src/versions/vm_m6/vm_with_bootloader.rs b/core/lib/multivm/src/versions/vm_m6/vm_with_bootloader.rs index 998f41275b41..c7d4ee3d45e8 100644 --- a/core/lib/multivm/src/versions/vm_m6/vm_with_bootloader.rs +++ b/core/lib/multivm/src/versions/vm_m6/vm_with_bootloader.rs @@ -12,7 +12,6 @@ use zk_evm_1_3_1::{ }; use zksync_contracts::BaseSystemContracts; use zksync_system_constants::MAX_TXS_IN_BLOCK; - use zksync_types::{ zkevm_test_harness::INITIAL_MONOTONIC_CYCLE_COUNTER, Address, Transaction, BOOTLOADER_ADDRESS, L1_GAS_PER_PUBDATA_BYTE, MAX_GAS_PER_PUBDATA_BYTE, MAX_NEW_FACTORY_DEPS, U256, @@ -24,10 +23,10 @@ use zksync_utils::{ misc::ceil_div, }; -use crate::vm_m6::storage::Storage; use crate::vm_m6::{ bootloader_state::BootloaderState, history_recorder::HistoryMode, + storage::Storage, transaction_data::{TransactionData, L1_TX_TYPE}, utils::{ code_page_candidate_from_base, heap_page_from_base, BLOCK_GAS_LIMIT, INITIAL_BASE_PAGE, diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/l2_block.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/l2_block.rs index 56b5b1b6b39e..6cd1096b3bd3 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/l2_block.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/l2_block.rs @@ -1,11 +1,15 @@ use std::cmp::Ordering; + use zksync_types::{MiniblockNumber, H256}; use zksync_utils::concat_and_hash; -use crate::interface::{L2Block, L2BlockEnv}; -use crate::vm_refunds_enhancement::bootloader_state::snapshot::L2BlockSnapshot; -use crate::vm_refunds_enhancement::bootloader_state::tx::BootloaderTx; -use crate::vm_refunds_enhancement::utils::l2_blocks::l2_block_hash; +use crate::{ + interface::{L2Block, L2BlockEnv}, + vm_refunds_enhancement::{ + bootloader_state::{snapshot::L2BlockSnapshot, tx::BootloaderTx}, + utils::l2_blocks::l2_block_hash, + }, +}; const EMPTY_TXS_ROLLING_HASH: H256 = H256::zero(); diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/state.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/state.rs index 4c8d48bc1a7f..d436a2adb0a1 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/state.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/state.rs @@ -1,17 +1,22 @@ -use crate::vm_refunds_enhancement::bootloader_state::l2_block::BootloaderL2Block; -use crate::vm_refunds_enhancement::bootloader_state::snapshot::BootloaderStateSnapshot; -use crate::vm_refunds_enhancement::bootloader_state::utils::{apply_l2_block, apply_tx_to_memory}; use std::cmp::Ordering; + use zksync_types::{L2ChainId, U256}; use zksync_utils::bytecode::CompressedBytecodeInfo; -use crate::interface::{BootloaderMemory, L2BlockEnv, TxExecutionMode}; -use crate::vm_refunds_enhancement::{ - constants::TX_DESCRIPTION_OFFSET, types::internals::TransactionData, - utils::l2_blocks::assert_next_block, -}; - use super::tx::BootloaderTx; +use crate::{ + interface::{BootloaderMemory, L2BlockEnv, TxExecutionMode}, + vm_refunds_enhancement::{ + bootloader_state::{ + l2_block::BootloaderL2Block, + snapshot::BootloaderStateSnapshot, + utils::{apply_l2_block, apply_tx_to_memory}, + }, + constants::TX_DESCRIPTION_OFFSET, + types::internals::TransactionData, + utils::l2_blocks::assert_next_block, + }, +}; /// Intermediate bootloader-related VM state. /// /// Required to process transactions one by one (since we intercept the VM execution to execute diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/tx.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/tx.rs index 3bd10e9374b0..e7f833e5badd 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/tx.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/tx.rs @@ -1,7 +1,8 @@ -use crate::vm_refunds_enhancement::types::internals::TransactionData; use zksync_types::{L2ChainId, H256, U256}; use zksync_utils::bytecode::CompressedBytecodeInfo; +use crate::vm_refunds_enhancement::types::internals::TransactionData; + /// Information about tx necessary for execution in bootloader. #[derive(Debug, Clone)] pub(super) struct BootloaderTx { diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/utils.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/utils.rs index fed5108d7f3d..8adeb3e0b428 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/utils.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/bootloader_state/utils.rs @@ -1,16 +1,19 @@ use zksync_types::U256; -use zksync_utils::bytecode::CompressedBytecodeInfo; -use zksync_utils::{bytes_to_be_words, h256_to_u256}; - -use crate::interface::{BootloaderMemory, TxExecutionMode}; -use crate::vm_refunds_enhancement::bootloader_state::l2_block::BootloaderL2Block; -use crate::vm_refunds_enhancement::constants::{ - BOOTLOADER_TX_DESCRIPTION_OFFSET, BOOTLOADER_TX_DESCRIPTION_SIZE, COMPRESSED_BYTECODES_OFFSET, - OPERATOR_REFUNDS_OFFSET, TX_DESCRIPTION_OFFSET, TX_OPERATOR_L2_BLOCK_INFO_OFFSET, - TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO, TX_OVERHEAD_OFFSET, TX_TRUSTED_GAS_LIMIT_OFFSET, -}; +use zksync_utils::{bytecode::CompressedBytecodeInfo, bytes_to_be_words, h256_to_u256}; use super::tx::BootloaderTx; +use crate::{ + interface::{BootloaderMemory, TxExecutionMode}, + vm_refunds_enhancement::{ + bootloader_state::l2_block::BootloaderL2Block, + constants::{ + BOOTLOADER_TX_DESCRIPTION_OFFSET, BOOTLOADER_TX_DESCRIPTION_SIZE, + COMPRESSED_BYTECODES_OFFSET, OPERATOR_REFUNDS_OFFSET, TX_DESCRIPTION_OFFSET, + TX_OPERATOR_L2_BLOCK_INFO_OFFSET, TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO, + TX_OVERHEAD_OFFSET, TX_TRUSTED_GAS_LIMIT_OFFSET, + }, + }, +}; pub(super) fn get_memory_for_compressed_bytecodes( compressed_bytecodes: &[CompressedBytecodeInfo], diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/constants.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/constants.rs index 0dca7a6ce265..82ab754e4036 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/constants.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/constants.rs @@ -1,14 +1,12 @@ use zk_evm_1_3_3::aux_structures::MemoryPage; - +pub use zk_evm_1_3_3::zkevm_opcode_defs::system_params::{ + ERGS_PER_CIRCUIT, INITIAL_STORAGE_WRITE_PUBDATA_BYTES, MAX_PUBDATA_PER_BLOCK, +}; use zksync_system_constants::{ L1_GAS_PER_PUBDATA_BYTE, MAX_L2_TX_GAS_LIMIT, MAX_NEW_FACTORY_DEPS, MAX_TXS_IN_BLOCK, USED_BOOTLOADER_MEMORY_WORDS, }; -pub use zk_evm_1_3_3::zkevm_opcode_defs::system_params::{ - ERGS_PER_CIRCUIT, INITIAL_STORAGE_WRITE_PUBDATA_BYTES, MAX_PUBDATA_PER_BLOCK, -}; - use crate::vm_refunds_enhancement::old_vm::utils::heap_page_from_base; /// Max cycles for a single transaction. diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/bytecode.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/bytecode.rs index 4b7e529fc5b0..69670f9682b0 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/bytecode.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/bytecode.rs @@ -1,13 +1,12 @@ use itertools::Itertools; - -use crate::interface::VmInterface; -use crate::HistoryMode; use zksync_state::{StoragePtr, WriteStorage}; use zksync_types::U256; -use zksync_utils::bytecode::{compress_bytecode, hash_bytecode, CompressedBytecodeInfo}; -use zksync_utils::bytes_to_be_words; +use zksync_utils::{ + bytecode::{compress_bytecode, hash_bytecode, CompressedBytecodeInfo}, + bytes_to_be_words, +}; -use crate::vm_refunds_enhancement::Vm; +use crate::{interface::VmInterface, vm_refunds_enhancement::Vm, HistoryMode}; impl Vm { /// Checks the last transaction has successfully published compressed bytecodes and returns `true` if there is at least one is still unknown. diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/execution.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/execution.rs index 9e55180d66f6..a1d81bdce5ef 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/execution.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/execution.rs @@ -1,15 +1,20 @@ -use crate::HistoryMode; use zk_evm_1_3_3::aux_structures::Timestamp; use zksync_state::WriteStorage; -use crate::interface::tracer::{TracerExecutionStatus, VmExecutionStopReason}; -use crate::interface::{VmExecutionMode, VmExecutionResultAndLogs}; -use crate::vm_refunds_enhancement::old_vm::utils::{vm_may_have_ended_inner, VmExecutionResult}; -use crate::vm_refunds_enhancement::tracers::dispatcher::TracerDispatcher; -use crate::vm_refunds_enhancement::tracers::{ - traits::VmTracer, DefaultExecutionTracer, RefundsTracer, +use crate::{ + interface::{ + tracer::{TracerExecutionStatus, VmExecutionStopReason}, + VmExecutionMode, VmExecutionResultAndLogs, + }, + vm_refunds_enhancement::{ + old_vm::utils::{vm_may_have_ended_inner, VmExecutionResult}, + tracers::{ + dispatcher::TracerDispatcher, traits::VmTracer, DefaultExecutionTracer, RefundsTracer, + }, + vm::Vm, + }, + HistoryMode, }; -use crate::vm_refunds_enhancement::vm::Vm; impl Vm { pub(crate) fn inspect_inner( diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/gas.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/gas.rs index cce9bfad6999..4083e27b0b3d 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/gas.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/gas.rs @@ -1,8 +1,9 @@ -use crate::HistoryMode; use zksync_state::WriteStorage; -use crate::vm_refunds_enhancement::tracers::DefaultExecutionTracer; -use crate::vm_refunds_enhancement::vm::Vm; +use crate::{ + vm_refunds_enhancement::{tracers::DefaultExecutionTracer, vm::Vm}, + HistoryMode, +}; impl Vm { /// Returns the amount of gas remaining to the VM. diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/logs.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/logs.rs index b8e8652f3012..bded1c19041f 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/logs.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/logs.rs @@ -1,14 +1,18 @@ use zk_evm_1_3_3::aux_structures::Timestamp; use zksync_state::WriteStorage; +use zksync_types::{ + l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}, + VmEvent, +}; -use crate::HistoryMode; -use zksync_types::l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}; -use zksync_types::VmEvent; - -use crate::interface::types::outputs::VmExecutionLogs; -use crate::vm_refunds_enhancement::old_vm::events::merge_events; -use crate::vm_refunds_enhancement::old_vm::utils::precompile_calls_count_after_timestamp; -use crate::vm_refunds_enhancement::vm::Vm; +use crate::{ + interface::types::outputs::VmExecutionLogs, + vm_refunds_enhancement::{ + old_vm::{events::merge_events, utils::precompile_calls_count_after_timestamp}, + vm::Vm, + }, + HistoryMode, +}; impl Vm { pub(crate) fn collect_execution_logs_after_timestamp( diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/snapshots.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/snapshots.rs index 972d50e5d76e..c34535726c04 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/snapshots.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/snapshots.rs @@ -1,13 +1,14 @@ -use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics}; - use std::time::Duration; -use crate::vm_latest::HistoryEnabled; +use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics}; use zk_evm_1_3_3::aux_structures::Timestamp; use zksync_state::WriteStorage; -use crate::vm_refunds_enhancement::{ - old_vm::oracles::OracleWithHistory, types::internals::VmSnapshot, vm::Vm, +use crate::{ + vm_latest::HistoryEnabled, + vm_refunds_enhancement::{ + old_vm::oracles::OracleWithHistory, types::internals::VmSnapshot, vm::Vm, + }, }; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelSet, EncodeLabelValue)] diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/statistics.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/statistics.rs index a49ce2a67464..3e9de5de4ec0 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/statistics.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/statistics.rs @@ -1,12 +1,12 @@ use zk_evm_1_3_3::aux_structures::Timestamp; use zksync_state::WriteStorage; - -use crate::HistoryMode; use zksync_types::U256; -use crate::interface::{VmExecutionStatistics, VmMemoryMetrics}; -use crate::vm_refunds_enhancement::tracers::DefaultExecutionTracer; -use crate::vm_refunds_enhancement::vm::Vm; +use crate::{ + interface::{VmExecutionStatistics, VmMemoryMetrics}, + vm_refunds_enhancement::{tracers::DefaultExecutionTracer, vm::Vm}, + HistoryMode, +}; /// Module responsible for observing the VM behavior, i.e. calculating the statistics of the VM runs /// or reporting the VM memory usage. diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/tx.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/tx.rs index d6fd4858870d..a786e9b0ad7e 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/tx.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/tx.rs @@ -1,15 +1,16 @@ -use crate::vm_refunds_enhancement::constants::BOOTLOADER_HEAP_PAGE; -use crate::vm_refunds_enhancement::implementation::bytecode::{ - bytecode_to_factory_dep, compress_bytecodes, -}; use zk_evm_1_3_3::aux_structures::Timestamp; use zksync_state::WriteStorage; -use zksync_types::l1::is_l1_tx_type; -use zksync_types::Transaction; +use zksync_types::{l1::is_l1_tx_type, Transaction}; -use crate::vm_refunds_enhancement::types::internals::TransactionData; -use crate::vm_refunds_enhancement::vm::Vm; -use crate::HistoryMode; +use crate::{ + vm_refunds_enhancement::{ + constants::BOOTLOADER_HEAP_PAGE, + implementation::bytecode::{bytecode_to_factory_dep, compress_bytecodes}, + types::internals::TransactionData, + vm::Vm, + }, + HistoryMode, +}; impl Vm { pub(crate) fn push_raw_transaction( diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/mod.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/mod.rs index 28a681e5e604..691d453c4b07 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/mod.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/mod.rs @@ -1,30 +1,25 @@ -pub use old_vm::{ - history_recorder::{HistoryDisabled, HistoryEnabled, HistoryMode}, - memory::SimpleMemory, +pub use self::{ + bootloader_state::BootloaderState, + old_vm::{ + history_recorder::{HistoryDisabled, HistoryEnabled, HistoryMode}, + memory::SimpleMemory, + }, + oracles::storage::StorageOracle, + tracers::{ + dispatcher::TracerDispatcher, + traits::{ToTracerPointer, TracerPointer, VmTracer}, + }, + types::internals::ZkSyncVmState, + utils::transaction_encoding::TransactionVmExt, + vm::Vm, }; -pub use oracles::storage::StorageOracle; - -pub use tracers::dispatcher::TracerDispatcher; -pub use tracers::traits::{ToTracerPointer, TracerPointer, VmTracer}; - -pub use utils::transaction_encoding::TransactionVmExt; - -pub use bootloader_state::BootloaderState; -pub use types::internals::ZkSyncVmState; - -pub use vm::Vm; - mod bootloader_state; +pub mod constants; mod implementation; mod old_vm; mod oracles; pub(crate) mod tracers; mod types; -mod vm; - -pub mod constants; pub mod utils; - -// #[cfg(test)] -// mod tests; +mod vm; diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/event_sink.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/event_sink.rs index adbee280a3db..43019cce1ce7 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/event_sink.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/event_sink.rs @@ -1,8 +1,5 @@ -use crate::vm_refunds_enhancement::old_vm::{ - history_recorder::{AppDataFrameManagerWithHistory, HistoryEnabled, HistoryMode}, - oracles::OracleWithHistory, -}; use std::collections::HashMap; + use zk_evm_1_3_3::{ abstractions::EventSink, aux_structures::{LogQuery, Timestamp}, @@ -12,6 +9,11 @@ use zk_evm_1_3_3::{ }, }; +use crate::vm_refunds_enhancement::old_vm::{ + history_recorder::{AppDataFrameManagerWithHistory, HistoryEnabled, HistoryMode}, + oracles::OracleWithHistory, +}; + #[derive(Debug, Clone, PartialEq, Default)] pub struct InMemoryEventSink { frames_stack: AppDataFrameManagerWithHistory, H>, diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/history_recorder.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/history_recorder.rs index 44d510b0075e..fdab00a199e9 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/history_recorder.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/history_recorder.rs @@ -5,7 +5,6 @@ use zk_evm_1_3_3::{ vm_state::PrimitiveValue, zkevm_opcode_defs::{self}, }; - use zksync_state::{StoragePtr, WriteStorage}; use zksync_types::{StorageKey, U256}; use zksync_utils::{h256_to_u256, u256_to_h256}; @@ -771,11 +770,14 @@ impl HistoryRecorder, H> { #[cfg(test)] mod tests { - use crate::vm_refunds_enhancement::old_vm::history_recorder::{HistoryRecorder, MemoryWrapper}; - use crate::vm_refunds_enhancement::HistoryDisabled; use zk_evm_1_3_3::{aux_structures::Timestamp, vm_state::PrimitiveValue}; use zksync_types::U256; + use crate::vm_refunds_enhancement::{ + old_vm::history_recorder::{HistoryRecorder, MemoryWrapper}, + HistoryDisabled, + }; + #[test] fn memory_equality() { let mut a: HistoryRecorder = Default::default(); diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/memory.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/memory.rs index 1ef04da58cbe..8568d6c72157 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/memory.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/memory.rs @@ -1,16 +1,18 @@ -use zk_evm_1_3_3::abstractions::{Memory, MemoryType}; -use zk_evm_1_3_3::aux_structures::{MemoryPage, MemoryQuery, Timestamp}; -use zk_evm_1_3_3::vm_state::PrimitiveValue; -use zk_evm_1_3_3::zkevm_opcode_defs::FatPointer; +use zk_evm_1_3_3::{ + abstractions::{Memory, MemoryType}, + aux_structures::{MemoryPage, MemoryQuery, Timestamp}, + vm_state::PrimitiveValue, + zkevm_opcode_defs::FatPointer, +}; use zksync_types::U256; -use crate::vm_refunds_enhancement::old_vm::history_recorder::{ - FramedStack, HistoryEnabled, HistoryMode, IntFrameManagerWithHistory, MemoryWithHistory, - MemoryWrapper, WithHistory, -}; -use crate::vm_refunds_enhancement::old_vm::oracles::OracleWithHistory; -use crate::vm_refunds_enhancement::old_vm::utils::{ - aux_heap_page_from_base, heap_page_from_base, stack_page_from_base, +use crate::vm_refunds_enhancement::old_vm::{ + history_recorder::{ + FramedStack, HistoryEnabled, HistoryMode, IntFrameManagerWithHistory, MemoryWithHistory, + MemoryWrapper, WithHistory, + }, + oracles::OracleWithHistory, + utils::{aux_heap_page_from_base, heap_page_from_base, stack_page_from_base}, }; #[derive(Debug, Clone, PartialEq)] diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/oracles/decommitter.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/oracles/decommitter.rs index a39be0ba93b9..6705831dbade 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/oracles/decommitter.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/oracles/decommitter.rs @@ -1,23 +1,19 @@ -use std::collections::HashMap; -use std::fmt::Debug; +use std::{collections::HashMap, fmt::Debug}; -use crate::vm_refunds_enhancement::old_vm::history_recorder::{ - HistoryEnabled, HistoryMode, HistoryRecorder, WithHistory, -}; - -use zk_evm_1_3_3::abstractions::MemoryType; -use zk_evm_1_3_3::aux_structures::Timestamp; use zk_evm_1_3_3::{ - abstractions::{DecommittmentProcessor, Memory}, - aux_structures::{DecommittmentQuery, MemoryIndex, MemoryLocation, MemoryPage, MemoryQuery}, + abstractions::{DecommittmentProcessor, Memory, MemoryType}, + aux_structures::{ + DecommittmentQuery, MemoryIndex, MemoryLocation, MemoryPage, MemoryQuery, Timestamp, + }, }; - use zksync_state::{ReadStorage, StoragePtr}; use zksync_types::U256; -use zksync_utils::bytecode::bytecode_len_in_words; -use zksync_utils::{bytes_to_be_words, u256_to_h256}; +use zksync_utils::{bytecode::bytecode_len_in_words, bytes_to_be_words, u256_to_h256}; use super::OracleWithHistory; +use crate::vm_refunds_enhancement::old_vm::history_recorder::{ + HistoryEnabled, HistoryMode, HistoryRecorder, WithHistory, +}; /// The main job of the DecommiterOracle is to implement the DecommittmentProcessor trait - that is /// used by the VM to 'load' bytecodes into memory. diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/oracles/precompile.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/oracles/precompile.rs index eb3f7b866b10..c59fb188e597 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/oracles/precompile.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/oracles/precompile.rs @@ -1,17 +1,14 @@ use zk_evm_1_3_3::{ - abstractions::Memory, - abstractions::PrecompileCyclesWitness, - abstractions::PrecompilesProcessor, + abstractions::{Memory, PrecompileCyclesWitness, PrecompilesProcessor}, aux_structures::{LogQuery, MemoryQuery, Timestamp}, precompiles::DefaultPrecompilesProcessor, }; +use super::OracleWithHistory; use crate::vm_refunds_enhancement::old_vm::history_recorder::{ HistoryEnabled, HistoryMode, HistoryRecorder, }; -use super::OracleWithHistory; - /// Wrap of DefaultPrecompilesProcessor that store queue /// of timestamp when precompiles are called to be executed. /// Number of precompiles per block is strictly limited, diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/utils.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/utils.rs index 9b4aae851d2e..bc4b2c3eff15 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/utils.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/utils.rs @@ -1,22 +1,19 @@ -use crate::vm_refunds_enhancement::old_vm::memory::SimpleMemory; - -use crate::vm_refunds_enhancement::types::internals::ZkSyncVmState; -use crate::vm_refunds_enhancement::HistoryMode; - -use zk_evm_1_3_3::zkevm_opcode_defs::decoding::{ - AllowedPcOrImm, EncodingModeProduction, VmEncodingMode, -}; -use zk_evm_1_3_3::zkevm_opcode_defs::RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER; use zk_evm_1_3_3::{ aux_structures::{MemoryPage, Timestamp}, vm_state::PrimitiveValue, - zkevm_opcode_defs::FatPointer, + zkevm_opcode_defs::{ + decoding::{AllowedPcOrImm, EncodingModeProduction, VmEncodingMode}, + FatPointer, RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER, + }, }; use zksync_state::WriteStorage; use zksync_system_constants::L1_GAS_PER_PUBDATA_BYTE; - use zksync_types::{Address, U256}; +use crate::vm_refunds_enhancement::{ + old_vm::memory::SimpleMemory, types::internals::ZkSyncVmState, HistoryMode, +}; + #[derive(Debug, Clone)] pub(crate) enum VmExecutionResult { Ok(Vec), diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/oracles/storage.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/oracles/storage.rs index e054cdbe2a6d..b970a8a95f73 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/oracles/storage.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/oracles/storage.rs @@ -1,26 +1,25 @@ use std::collections::HashMap; -use crate::vm_refunds_enhancement::old_vm::history_recorder::{ - AppDataFrameManagerWithHistory, HashMapHistoryEvent, HistoryEnabled, HistoryMode, - HistoryRecorder, StorageWrapper, VectorHistoryEvent, WithHistory, -}; -use crate::vm_refunds_enhancement::old_vm::oracles::OracleWithHistory; - -use zk_evm_1_3_3::abstractions::RefundedAmounts; -use zk_evm_1_3_3::zkevm_opcode_defs::system_params::INITIAL_STORAGE_WRITE_PUBDATA_BYTES; use zk_evm_1_3_3::{ - abstractions::{RefundType, Storage as VmStorageOracle}, + abstractions::{RefundType, RefundedAmounts, Storage as VmStorageOracle}, aux_structures::{LogQuery, Timestamp}, + zkevm_opcode_defs::system_params::INITIAL_STORAGE_WRITE_PUBDATA_BYTES, }; - use zksync_state::{StoragePtr, WriteStorage}; -use zksync_types::utils::storage_key_for_eth_balance; use zksync_types::{ - AccountTreeId, Address, StorageKey, StorageLogQuery, StorageLogQueryType, BOOTLOADER_ADDRESS, - U256, + utils::storage_key_for_eth_balance, AccountTreeId, Address, StorageKey, StorageLogQuery, + StorageLogQueryType, BOOTLOADER_ADDRESS, U256, }; use zksync_utils::u256_to_h256; +use crate::vm_refunds_enhancement::old_vm::{ + history_recorder::{ + AppDataFrameManagerWithHistory, HashMapHistoryEvent, HistoryEnabled, HistoryMode, + HistoryRecorder, StorageWrapper, VectorHistoryEvent, WithHistory, + }, + oracles::OracleWithHistory, +}; + // While the storage does not support different shards, it was decided to write the // code of the StorageOracle with the shard parameters in mind. pub(crate) fn triplet_to_storage_key(_shard_id: u8, address: Address, key: U256) -> StorageKey { diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/default_tracers.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/default_tracers.rs index 51fbf06d855d..8e9c0f11aba4 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/default_tracers.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/default_tracers.rs @@ -1,11 +1,5 @@ use std::fmt::{Debug, Formatter}; -use crate::interface::dyn_tracers::vm_1_3_3::DynTracer; -use crate::interface::tracer::{ - TracerExecutionStatus, TracerExecutionStopReason, VmExecutionStopReason, -}; -use crate::interface::{Halt, VmExecutionMode}; -use crate::vm_refunds_enhancement::VmTracer; use zk_evm_1_3_3::{ tracing::{ AfterDecodingData, AfterExecutionData, BeforeExecutionData, Tracer, VmLocalStateData, @@ -17,18 +11,28 @@ use zk_evm_1_3_3::{ use zksync_state::{StoragePtr, WriteStorage}; use zksync_types::Timestamp; -use crate::vm_refunds_enhancement::bootloader_state::utils::apply_l2_block; -use crate::vm_refunds_enhancement::bootloader_state::BootloaderState; -use crate::vm_refunds_enhancement::constants::BOOTLOADER_HEAP_PAGE; -use crate::vm_refunds_enhancement::old_vm::history_recorder::HistoryMode; -use crate::vm_refunds_enhancement::old_vm::memory::SimpleMemory; -use crate::vm_refunds_enhancement::tracers::dispatcher::TracerDispatcher; -use crate::vm_refunds_enhancement::tracers::utils::{ - computational_gas_price, gas_spent_on_bytecodes_and_long_messages_this_opcode, - print_debug_if_needed, VmHook, +use crate::{ + interface::{ + dyn_tracers::vm_1_3_3::DynTracer, + tracer::{TracerExecutionStatus, TracerExecutionStopReason, VmExecutionStopReason}, + Halt, VmExecutionMode, + }, + vm_refunds_enhancement::{ + bootloader_state::{utils::apply_l2_block, BootloaderState}, + constants::BOOTLOADER_HEAP_PAGE, + old_vm::{history_recorder::HistoryMode, memory::SimpleMemory}, + tracers::{ + dispatcher::TracerDispatcher, + utils::{ + computational_gas_price, gas_spent_on_bytecodes_and_long_messages_this_opcode, + print_debug_if_needed, VmHook, + }, + RefundsTracer, ResultTracer, + }, + types::internals::ZkSyncVmState, + VmTracer, + }, }; -use crate::vm_refunds_enhancement::tracers::{RefundsTracer, ResultTracer}; -use crate::vm_refunds_enhancement::types::internals::ZkSyncVmState; /// Default tracer for the VM. It manages the other tracers execution and stop the vm when needed. pub(crate) struct DefaultExecutionTracer { diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/dispatcher.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/dispatcher.rs index f2296d205a95..2392c3e51afa 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/dispatcher.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/dispatcher.rs @@ -1,13 +1,18 @@ -use crate::interface::dyn_tracers::vm_1_3_3::DynTracer; -use crate::interface::tracer::{TracerExecutionStatus, VmExecutionStopReason}; -use crate::vm_refunds_enhancement::{ - BootloaderState, HistoryMode, SimpleMemory, TracerPointer, VmTracer, ZkSyncVmState, -}; use zk_evm_1_3_3::tracing::{ AfterDecodingData, AfterExecutionData, BeforeExecutionData, VmLocalStateData, }; use zksync_state::{StoragePtr, WriteStorage}; +use crate::{ + interface::{ + dyn_tracers::vm_1_3_3::DynTracer, + tracer::{TracerExecutionStatus, VmExecutionStopReason}, + }, + vm_refunds_enhancement::{ + BootloaderState, HistoryMode, SimpleMemory, TracerPointer, VmTracer, ZkSyncVmState, + }, +}; + /// Tracer dispatcher is a tracer that can dispatch calls to multiple tracers. pub struct TracerDispatcher { tracers: Vec>, diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/refunds.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/refunds.rs index 5256561b5eb5..f906cef6230b 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/refunds.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/refunds.rs @@ -1,5 +1,4 @@ use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics}; - use zk_evm_1_3_3::{ aux_structures::Timestamp, tracing::{BeforeExecutionData, VmLocalStateData}, @@ -12,27 +11,27 @@ use zksync_types::{ l2_to_l1_log::L2ToL1Log, L1BatchNumber, U256, }; -use zksync_utils::bytecode::bytecode_len_in_bytes; -use zksync_utils::{ceil_div_u256, u256_to_h256}; - -use crate::interface::{ - dyn_tracers::vm_1_3_3::DynTracer, tracer::TracerExecutionStatus, L1BatchEnv, Refunds, -}; -use crate::vm_refunds_enhancement::constants::{ - BOOTLOADER_HEAP_PAGE, OPERATOR_REFUNDS_OFFSET, TX_GAS_LIMIT_OFFSET, -}; +use zksync_utils::{bytecode::bytecode_len_in_bytes, ceil_div_u256, u256_to_h256}; -use crate::vm_refunds_enhancement::{ - bootloader_state::BootloaderState, - old_vm::{ - events::merge_events, history_recorder::HistoryMode, memory::SimpleMemory, - utils::eth_price_per_pubdata_byte, +use crate::{ + interface::{ + dyn_tracers::vm_1_3_3::DynTracer, tracer::TracerExecutionStatus, L1BatchEnv, Refunds, }, - tracers::{ - traits::VmTracer, - utils::{gas_spent_on_bytecodes_and_long_messages_this_opcode, get_vm_hook_params, VmHook}, + vm_refunds_enhancement::{ + bootloader_state::BootloaderState, + constants::{BOOTLOADER_HEAP_PAGE, OPERATOR_REFUNDS_OFFSET, TX_GAS_LIMIT_OFFSET}, + old_vm::{ + events::merge_events, history_recorder::HistoryMode, memory::SimpleMemory, + utils::eth_price_per_pubdata_byte, + }, + tracers::{ + traits::VmTracer, + utils::{ + gas_spent_on_bytecodes_and_long_messages_this_opcode, get_vm_hook_params, VmHook, + }, + }, + types::internals::ZkSyncVmState, }, - types::internals::ZkSyncVmState, }; /// Tracer responsible for collecting information about refunds. diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/result_tracer.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/result_tracer.rs index c0a8e5d6cc0d..1281b416bb47 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/result_tracer.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/result_tracer.rs @@ -4,23 +4,27 @@ use zk_evm_1_3_3::{ zkevm_opcode_defs::FatPointer, }; use zksync_state::{StoragePtr, WriteStorage}; - -use crate::interface::dyn_tracers::vm_1_3_3::DynTracer; -use crate::interface::tracer::{TracerExecutionStopReason, VmExecutionStopReason}; -use crate::interface::{ExecutionResult, Halt, TxRevertReason, VmExecutionMode, VmRevertReason}; use zksync_types::U256; -use crate::vm_refunds_enhancement::bootloader_state::BootloaderState; -use crate::vm_refunds_enhancement::old_vm::{ - history_recorder::HistoryMode, - memory::SimpleMemory, - utils::{vm_may_have_ended_inner, VmExecutionResult}, +use crate::{ + interface::{ + dyn_tracers::vm_1_3_3::DynTracer, + tracer::{TracerExecutionStopReason, VmExecutionStopReason}, + ExecutionResult, Halt, TxRevertReason, VmExecutionMode, VmRevertReason, + }, + vm_refunds_enhancement::{ + bootloader_state::BootloaderState, + constants::{BOOTLOADER_HEAP_PAGE, RESULT_SUCCESS_FIRST_SLOT}, + old_vm::{ + history_recorder::HistoryMode, + memory::SimpleMemory, + utils::{vm_may_have_ended_inner, VmExecutionResult}, + }, + tracers::utils::{get_vm_hook_params, read_pointer, VmHook}, + types::internals::ZkSyncVmState, + VmTracer, + }, }; -use crate::vm_refunds_enhancement::tracers::utils::{get_vm_hook_params, read_pointer, VmHook}; - -use crate::vm_refunds_enhancement::constants::{BOOTLOADER_HEAP_PAGE, RESULT_SUCCESS_FIRST_SLOT}; -use crate::vm_refunds_enhancement::types::internals::ZkSyncVmState; -use crate::vm_refunds_enhancement::VmTracer; #[derive(Debug, Clone)] enum Result { diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/traits.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/traits.rs index 13b295b9fe9b..b54819148fad 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/traits.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/traits.rs @@ -1,11 +1,16 @@ -use crate::interface::dyn_tracers::vm_1_3_3::DynTracer; -use crate::interface::tracer::{TracerExecutionStatus, VmExecutionStopReason}; use zksync_state::WriteStorage; -use crate::vm_refunds_enhancement::bootloader_state::BootloaderState; -use crate::vm_refunds_enhancement::old_vm::history_recorder::HistoryMode; -use crate::vm_refunds_enhancement::old_vm::memory::SimpleMemory; -use crate::vm_refunds_enhancement::types::internals::ZkSyncVmState; +use crate::{ + interface::{ + dyn_tracers::vm_1_3_3::DynTracer, + tracer::{TracerExecutionStatus, VmExecutionStopReason}, + }, + vm_refunds_enhancement::{ + bootloader_state::BootloaderState, + old_vm::{history_recorder::HistoryMode, memory::SimpleMemory}, + types::internals::ZkSyncVmState, + }, +}; pub type TracerPointer = Box>; diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/utils.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/utils.rs index 3026afea0078..8de2ad181f47 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/utils.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/tracers/utils.rs @@ -5,7 +5,6 @@ use zk_evm_1_3_3::{ FarCallABI, FarCallForwardPageType, FatPointer, LogOpcode, Opcode, UMAOpcode, }, }; - use zksync_system_constants::{ ECRECOVER_PRECOMPILE_ADDRESS, KECCAK256_PRECOMPILE_ADDRESS, KNOWN_CODES_STORAGE_ADDRESS, L1_MESSENGER_ADDRESS, SHA256_PRECOMPILE_ADDRESS, @@ -13,13 +12,15 @@ use zksync_system_constants::{ use zksync_types::U256; use zksync_utils::u256_to_h256; -use crate::vm_refunds_enhancement::constants::{ - BOOTLOADER_HEAP_PAGE, VM_HOOK_PARAMS_COUNT, VM_HOOK_PARAMS_START_POSITION, VM_HOOK_POSITION, -}; -use crate::vm_refunds_enhancement::old_vm::{ - history_recorder::HistoryMode, - memory::SimpleMemory, - utils::{aux_heap_page_from_base, heap_page_from_base}, +use crate::vm_refunds_enhancement::{ + constants::{ + BOOTLOADER_HEAP_PAGE, VM_HOOK_PARAMS_COUNT, VM_HOOK_PARAMS_START_POSITION, VM_HOOK_POSITION, + }, + old_vm::{ + history_recorder::HistoryMode, + memory::SimpleMemory, + utils::{aux_heap_page_from_base, heap_page_from_base}, + }, }; #[derive(Clone, Debug, Copy)] diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/types/internals/transaction_data.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/types/internals/transaction_data.rs index 1ad2ce0f9774..4b70a79fdd4c 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/types/internals/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/types/internals/transaction_data.rs @@ -1,15 +1,15 @@ use std::convert::TryInto; -use zksync_types::ethabi::{encode, Address, Token}; -use zksync_types::fee::{encoding_len, Fee}; -use zksync_types::l1::is_l1_tx_type; -use zksync_types::l2::L2Tx; -use zksync_types::transaction_request::{PaymasterParams, TransactionRequest}; + use zksync_types::{ - l2::TransactionType, Bytes, Execute, ExecuteTransactionCommon, L2ChainId, L2TxCommonData, - Nonce, Transaction, H256, U256, + ethabi::{encode, Address, Token}, + fee::{encoding_len, Fee}, + l1::is_l1_tx_type, + l2::{L2Tx, TransactionType}, + transaction_request::{PaymasterParams, TransactionRequest}, + Bytes, Execute, ExecuteTransactionCommon, L2ChainId, L2TxCommonData, Nonce, Transaction, H256, + U256, }; -use zksync_utils::address_to_h256; -use zksync_utils::{bytecode::hash_bytecode, bytes_to_be_words, h256_to_u256}; +use zksync_utils::{address_to_h256, bytecode::hash_bytecode, bytes_to_be_words, h256_to_u256}; use crate::vm_refunds_enhancement::utils::overhead::{ get_amortized_overhead, OverheadCoefficients, @@ -305,9 +305,10 @@ impl TryInto for TransactionData { #[cfg(test)] mod tests { - use super::*; use zksync_types::fee::encoding_len; + use super::*; + #[test] fn test_consistency_with_encoding_length() { let transaction = TransactionData { diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/types/internals/vm_state.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/types/internals/vm_state.rs index b656cd09f9b5..adeef89466fa 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/types/internals/vm_state.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/types/internals/vm_state.rs @@ -1,34 +1,40 @@ use zk_evm_1_3_3::{ - aux_structures::MemoryPage, - aux_structures::Timestamp, + aux_structures::{MemoryPage, Timestamp}, block_properties::BlockProperties, vm_state::{CallStackEntry, PrimitiveValue, VmState}, witness_trace::DummyTracer, zkevm_opcode_defs::{ system_params::{BOOTLOADER_MAX_MEMORY, INITIAL_FRAME_FORMAL_EH_LOCATION}, - FatPointer, BOOTLOADER_CALLDATA_PAGE, + FatPointer, BOOTLOADER_BASE_PAGE, BOOTLOADER_CALLDATA_PAGE, BOOTLOADER_CODE_PAGE, + STARTING_BASE_PAGE, STARTING_TIMESTAMP, }, }; - -use crate::interface::{L1BatchEnv, L2Block, SystemEnv}; -use zk_evm_1_3_3::zkevm_opcode_defs::{ - BOOTLOADER_BASE_PAGE, BOOTLOADER_CODE_PAGE, STARTING_BASE_PAGE, STARTING_TIMESTAMP, -}; use zksync_state::{StoragePtr, WriteStorage}; use zksync_system_constants::BOOTLOADER_ADDRESS; -use zksync_types::block::legacy_miniblock_hash; -use zksync_types::{zkevm_test_harness::INITIAL_MONOTONIC_CYCLE_COUNTER, Address, MiniblockNumber}; +use zksync_types::{ + block::legacy_miniblock_hash, zkevm_test_harness::INITIAL_MONOTONIC_CYCLE_COUNTER, Address, + MiniblockNumber, +}; use zksync_utils::h256_to_u256; -use crate::vm_refunds_enhancement::bootloader_state::BootloaderState; -use crate::vm_refunds_enhancement::constants::BOOTLOADER_HEAP_PAGE; -use crate::vm_refunds_enhancement::old_vm::{ - event_sink::InMemoryEventSink, history_recorder::HistoryMode, memory::SimpleMemory, - oracles::decommitter::DecommitterOracle, oracles::precompile::PrecompilesProcessorWithHistory, +use crate::{ + interface::{L1BatchEnv, L2Block, SystemEnv}, + vm_refunds_enhancement::{ + bootloader_state::BootloaderState, + constants::BOOTLOADER_HEAP_PAGE, + old_vm::{ + event_sink::InMemoryEventSink, + history_recorder::HistoryMode, + memory::SimpleMemory, + oracles::{ + decommitter::DecommitterOracle, precompile::PrecompilesProcessorWithHistory, + }, + }, + oracles::storage::StorageOracle, + types::l1_batch::bootloader_initial_memory, + utils::l2_blocks::{assert_next_block, load_last_l2_block}, + }, }; -use crate::vm_refunds_enhancement::oracles::storage::StorageOracle; -use crate::vm_refunds_enhancement::types::l1_batch::bootloader_initial_memory; -use crate::vm_refunds_enhancement::utils::l2_blocks::{assert_next_block, load_last_l2_block}; pub type ZkSyncVmState = VmState< StorageOracle, diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/types/l1_batch.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/types/l1_batch.rs index 631f1436cc3b..6f16e95f8d77 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/types/l1_batch.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/types/l1_batch.rs @@ -1,7 +1,8 @@ -use crate::interface::L1BatchEnv; use zksync_types::U256; use zksync_utils::{address_to_u256, h256_to_u256}; +use crate::interface::L1BatchEnv; + const OPERATOR_ADDRESS_SLOT: usize = 0; const PREV_BLOCK_HASH_SLOT: usize = 1; const NEW_BLOCK_TIMESTAMP_SLOT: usize = 2; diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/l2_blocks.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/l2_blocks.rs index 3d5f58094e01..5dd26c4c0277 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/l2_blocks.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/l2_blocks.rs @@ -1,15 +1,17 @@ -use crate::interface::{L2Block, L2BlockEnv}; use zksync_state::{ReadStorage, StoragePtr}; use zksync_system_constants::{ SYSTEM_CONTEXT_ADDRESS, SYSTEM_CONTEXT_CURRENT_L2_BLOCK_HASHES_POSITION, SYSTEM_CONTEXT_CURRENT_L2_BLOCK_INFO_POSITION, SYSTEM_CONTEXT_CURRENT_TX_ROLLING_HASH_POSITION, SYSTEM_CONTEXT_STORED_L2_BLOCK_HASHES, }; -use zksync_types::block::unpack_block_info; -use zksync_types::web3::signing::keccak256; -use zksync_types::{AccountTreeId, MiniblockNumber, StorageKey, H256, U256}; +use zksync_types::{ + block::unpack_block_info, web3::signing::keccak256, AccountTreeId, MiniblockNumber, StorageKey, + H256, U256, +}; use zksync_utils::{h256_to_u256, u256_to_h256}; +use crate::interface::{L2Block, L2BlockEnv}; + pub(crate) fn get_l2_block_hash_key(block_number: u32) -> StorageKey { let position = h256_to_u256(SYSTEM_CONTEXT_CURRENT_L2_BLOCK_HASHES_POSITION) + U256::from(block_number % SYSTEM_CONTEXT_STORED_L2_BLOCK_HASHES); diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/overhead.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/overhead.rs index 6c56515cfd7b..ab5149a050fb 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/overhead.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/overhead.rs @@ -1,12 +1,12 @@ -use crate::vm_refunds_enhancement::constants::{ - BLOCK_OVERHEAD_GAS, BLOCK_OVERHEAD_PUBDATA, BOOTLOADER_TX_ENCODING_SPACE, -}; use zk_evm_1_3_3::zkevm_opcode_defs::system_params::MAX_TX_ERGS_LIMIT; use zksync_system_constants::{MAX_L2_TX_GAS_LIMIT, MAX_TXS_IN_BLOCK}; -use zksync_types::l1::is_l1_tx_type; -use zksync_types::U256; +use zksync_types::{l1::is_l1_tx_type, U256}; use zksync_utils::ceil_div_u256; +use crate::vm_refunds_enhancement::constants::{ + BLOCK_OVERHEAD_GAS, BLOCK_OVERHEAD_PUBDATA, BOOTLOADER_TX_ENCODING_SPACE, +}; + /// Derives the overhead for processing transactions in a block. pub fn derive_overhead( gas_limit: u32, diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/transaction_encoding.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/transaction_encoding.rs index ab1352c2c75f..56052eca8133 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/transaction_encoding.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/utils/transaction_encoding.rs @@ -1,6 +1,7 @@ -use crate::vm_refunds_enhancement::types::internals::TransactionData; use zksync_types::Transaction; +use crate::vm_refunds_enhancement::types::internals::TransactionData; + /// Extension for transactions, specific for VM. Required for bypassing the orphan rule pub trait TransactionVmExt { /// Get the size of the transaction in tokens. diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/vm.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/vm.rs index 11eea1206a81..678a467d4474 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/vm.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/vm.rs @@ -1,20 +1,22 @@ -use crate::HistoryMode; use zksync_state::{StoragePtr, WriteStorage}; -use zksync_types::l2_to_l1_log::UserL2ToL1Log; -use zksync_types::Transaction; +use zksync_types::{l2_to_l1_log::UserL2ToL1Log, Transaction}; use zksync_utils::bytecode::CompressedBytecodeInfo; -use crate::vm_refunds_enhancement::old_vm::events::merge_events; - -use crate::interface::{ - BootloaderMemory, CurrentExecutionState, L1BatchEnv, L2BlockEnv, SystemEnv, VmExecutionMode, - VmExecutionResultAndLogs, VmInterface, VmInterfaceHistoryEnabled, +use crate::{ + interface::{ + BootloaderMemory, BytecodeCompressionError, CurrentExecutionState, L1BatchEnv, L2BlockEnv, + SystemEnv, VmExecutionMode, VmExecutionResultAndLogs, VmInterface, + VmInterfaceHistoryEnabled, VmMemoryMetrics, + }, + vm_latest::HistoryEnabled, + vm_refunds_enhancement::{ + bootloader_state::BootloaderState, + old_vm::events::merge_events, + tracers::dispatcher::TracerDispatcher, + types::internals::{new_vm_state, VmSnapshot, ZkSyncVmState}, + }, + HistoryMode, }; -use crate::interface::{BytecodeCompressionError, VmMemoryMetrics}; -use crate::vm_latest::HistoryEnabled; -use crate::vm_refunds_enhancement::bootloader_state::BootloaderState; -use crate::vm_refunds_enhancement::tracers::dispatcher::TracerDispatcher; -use crate::vm_refunds_enhancement::types::internals::{new_vm_state, VmSnapshot, ZkSyncVmState}; /// Main entry point for Virtual Machine integration. /// The instance should process only one l1 batch diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/l2_block.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/l2_block.rs index fac7cb33d218..8ce851d6699c 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/l2_block.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/l2_block.rs @@ -1,11 +1,15 @@ use std::cmp::Ordering; + use zksync_types::{MiniblockNumber, H256}; use zksync_utils::concat_and_hash; -use crate::interface::{L2Block, L2BlockEnv}; -use crate::vm_virtual_blocks::bootloader_state::snapshot::L2BlockSnapshot; -use crate::vm_virtual_blocks::bootloader_state::tx::BootloaderTx; -use crate::vm_virtual_blocks::utils::l2_blocks::l2_block_hash; +use crate::{ + interface::{L2Block, L2BlockEnv}, + vm_virtual_blocks::{ + bootloader_state::{snapshot::L2BlockSnapshot, tx::BootloaderTx}, + utils::l2_blocks::l2_block_hash, + }, +}; const EMPTY_TXS_ROLLING_HASH: H256 = H256::zero(); diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/state.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/state.rs index 2d67121e89b2..685b1821fd5a 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/state.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/state.rs @@ -1,19 +1,22 @@ -use crate::vm_virtual_blocks::bootloader_state::{ - l2_block::BootloaderL2Block, - snapshot::BootloaderStateSnapshot, - utils::{apply_l2_block, apply_tx_to_memory}, -}; use std::cmp::Ordering; + use zksync_types::{L2ChainId, U256}; use zksync_utils::bytecode::CompressedBytecodeInfo; -use crate::interface::{BootloaderMemory, L2BlockEnv, TxExecutionMode}; -use crate::vm_virtual_blocks::{ - constants::TX_DESCRIPTION_OFFSET, types::internals::TransactionData, - utils::l2_blocks::assert_next_block, -}; - use super::tx::BootloaderTx; +use crate::{ + interface::{BootloaderMemory, L2BlockEnv, TxExecutionMode}, + vm_virtual_blocks::{ + bootloader_state::{ + l2_block::BootloaderL2Block, + snapshot::BootloaderStateSnapshot, + utils::{apply_l2_block, apply_tx_to_memory}, + }, + constants::TX_DESCRIPTION_OFFSET, + types::internals::TransactionData, + utils::l2_blocks::assert_next_block, + }, +}; /// Intermediate bootloader-related VM state. /// /// Required to process transactions one by one (since we intercept the VM execution to execute diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/tx.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/tx.rs index 3b53c918fda6..067d62a9fdd6 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/tx.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/tx.rs @@ -1,7 +1,8 @@ -use crate::vm_virtual_blocks::types::internals::TransactionData; use zksync_types::{L2ChainId, H256, U256}; use zksync_utils::bytecode::CompressedBytecodeInfo; +use crate::vm_virtual_blocks::types::internals::TransactionData; + /// Information about tx necessary for execution in bootloader. #[derive(Debug, Clone)] pub(super) struct BootloaderTx { diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/utils.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/utils.rs index 6e836ad201db..a3986d6fe463 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/utils.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/bootloader_state/utils.rs @@ -1,16 +1,19 @@ use zksync_types::U256; -use zksync_utils::bytecode::CompressedBytecodeInfo; -use zksync_utils::{bytes_to_be_words, h256_to_u256}; - -use crate::interface::{BootloaderMemory, TxExecutionMode}; -use crate::vm_virtual_blocks::bootloader_state::l2_block::BootloaderL2Block; -use crate::vm_virtual_blocks::constants::{ - BOOTLOADER_TX_DESCRIPTION_OFFSET, BOOTLOADER_TX_DESCRIPTION_SIZE, COMPRESSED_BYTECODES_OFFSET, - OPERATOR_REFUNDS_OFFSET, TX_DESCRIPTION_OFFSET, TX_OPERATOR_L2_BLOCK_INFO_OFFSET, - TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO, TX_OVERHEAD_OFFSET, TX_TRUSTED_GAS_LIMIT_OFFSET, -}; +use zksync_utils::{bytecode::CompressedBytecodeInfo, bytes_to_be_words, h256_to_u256}; use super::tx::BootloaderTx; +use crate::{ + interface::{BootloaderMemory, TxExecutionMode}, + vm_virtual_blocks::{ + bootloader_state::l2_block::BootloaderL2Block, + constants::{ + BOOTLOADER_TX_DESCRIPTION_OFFSET, BOOTLOADER_TX_DESCRIPTION_SIZE, + COMPRESSED_BYTECODES_OFFSET, OPERATOR_REFUNDS_OFFSET, TX_DESCRIPTION_OFFSET, + TX_OPERATOR_L2_BLOCK_INFO_OFFSET, TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO, + TX_OVERHEAD_OFFSET, TX_TRUSTED_GAS_LIMIT_OFFSET, + }, + }, +}; pub(super) fn get_memory_for_compressed_bytecodes( compressed_bytecodes: &[CompressedBytecodeInfo], diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/constants.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/constants.rs index 5535be903812..c03260f1b6de 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/constants.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/constants.rs @@ -1,14 +1,12 @@ use zk_evm_1_3_3::aux_structures::MemoryPage; - +pub use zk_evm_1_3_3::zkevm_opcode_defs::system_params::{ + ERGS_PER_CIRCUIT, INITIAL_STORAGE_WRITE_PUBDATA_BYTES, MAX_PUBDATA_PER_BLOCK, +}; use zksync_system_constants::{ L1_GAS_PER_PUBDATA_BYTE, MAX_L2_TX_GAS_LIMIT, MAX_NEW_FACTORY_DEPS, MAX_TXS_IN_BLOCK, USED_BOOTLOADER_MEMORY_WORDS, }; -pub use zk_evm_1_3_3::zkevm_opcode_defs::system_params::{ - ERGS_PER_CIRCUIT, INITIAL_STORAGE_WRITE_PUBDATA_BYTES, MAX_PUBDATA_PER_BLOCK, -}; - use crate::vm_virtual_blocks::old_vm::utils::heap_page_from_base; /// Max cycles for a single transaction. diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/bytecode.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/bytecode.rs index 2ae53a48ef3a..570581740ef6 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/bytecode.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/bytecode.rs @@ -1,13 +1,12 @@ use itertools::Itertools; - -use crate::interface::VmInterface; -use crate::HistoryMode; use zksync_state::{StoragePtr, WriteStorage}; use zksync_types::U256; -use zksync_utils::bytecode::{compress_bytecode, hash_bytecode, CompressedBytecodeInfo}; -use zksync_utils::bytes_to_be_words; +use zksync_utils::{ + bytecode::{compress_bytecode, hash_bytecode, CompressedBytecodeInfo}, + bytes_to_be_words, +}; -use crate::vm_virtual_blocks::Vm; +use crate::{interface::VmInterface, vm_virtual_blocks::Vm, HistoryMode}; impl Vm { /// Checks the last transaction has successfully published compressed bytecodes and returns `true` if there is at least one is still unknown. diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/execution.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/execution.rs index ac95312019d2..2938280d2665 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/execution.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/execution.rs @@ -1,16 +1,22 @@ -use crate::interface::tracer::{TracerExecutionStopReason, VmExecutionStopReason}; -use crate::interface::{VmExecutionMode, VmExecutionResultAndLogs}; -use crate::HistoryMode; use zk_evm_1_3_3::aux_structures::Timestamp; use zksync_state::WriteStorage; -use crate::vm_virtual_blocks::old_vm::utils::{vm_may_have_ended_inner, VmExecutionResult}; -use crate::vm_virtual_blocks::tracers::dispatcher::TracerDispatcher; -use crate::vm_virtual_blocks::tracers::{ - traits::{ExecutionEndTracer, VmTracer}, - DefaultExecutionTracer, RefundsTracer, +use crate::{ + interface::{ + tracer::{TracerExecutionStopReason, VmExecutionStopReason}, + VmExecutionMode, VmExecutionResultAndLogs, + }, + vm_virtual_blocks::{ + old_vm::utils::{vm_may_have_ended_inner, VmExecutionResult}, + tracers::{ + dispatcher::TracerDispatcher, + traits::{ExecutionEndTracer, VmTracer}, + DefaultExecutionTracer, RefundsTracer, + }, + vm::Vm, + }, + HistoryMode, }; -use crate::vm_virtual_blocks::vm::Vm; impl Vm { pub(crate) fn inspect_inner( diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/gas.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/gas.rs index 1f06ecb08274..0ca52d2b6870 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/gas.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/gas.rs @@ -1,8 +1,9 @@ -use crate::HistoryMode; use zksync_state::WriteStorage; -use crate::vm_virtual_blocks::tracers::DefaultExecutionTracer; -use crate::vm_virtual_blocks::vm::Vm; +use crate::{ + vm_virtual_blocks::{tracers::DefaultExecutionTracer, vm::Vm}, + HistoryMode, +}; impl Vm { /// Returns the amount of gas remaining to the VM. diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/logs.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/logs.rs index a32f3a165727..0d407efd0411 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/logs.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/logs.rs @@ -1,14 +1,18 @@ use zk_evm_1_3_3::aux_structures::Timestamp; use zksync_state::WriteStorage; +use zksync_types::{ + l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}, + VmEvent, +}; -use crate::interface::types::outputs::VmExecutionLogs; -use crate::HistoryMode; -use zksync_types::l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}; -use zksync_types::VmEvent; - -use crate::vm_virtual_blocks::old_vm::events::merge_events; -use crate::vm_virtual_blocks::old_vm::utils::precompile_calls_count_after_timestamp; -use crate::vm_virtual_blocks::vm::Vm; +use crate::{ + interface::types::outputs::VmExecutionLogs, + vm_virtual_blocks::{ + old_vm::{events::merge_events, utils::precompile_calls_count_after_timestamp}, + vm::Vm, + }, + HistoryMode, +}; impl Vm { pub(crate) fn collect_execution_logs_after_timestamp( diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/snapshots.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/snapshots.rs index 1a8ad6fefd2f..569e11150390 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/snapshots.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/snapshots.rs @@ -1,13 +1,12 @@ -use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics}; - use std::time::Duration; -use crate::vm_latest::HistoryEnabled; +use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics}; use zk_evm_1_3_3::aux_structures::Timestamp; use zksync_state::WriteStorage; -use crate::vm_virtual_blocks::{ - old_vm::oracles::OracleWithHistory, types::internals::VmSnapshot, vm::Vm, +use crate::{ + vm_latest::HistoryEnabled, + vm_virtual_blocks::{old_vm::oracles::OracleWithHistory, types::internals::VmSnapshot, vm::Vm}, }; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelSet, EncodeLabelValue)] diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/statistics.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/statistics.rs index dd4a5ad55b21..074e8dae56ed 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/statistics.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/statistics.rs @@ -1,12 +1,12 @@ use zk_evm_1_3_3::aux_structures::Timestamp; use zksync_state::WriteStorage; - -use crate::interface::{VmExecutionStatistics, VmMemoryMetrics}; -use crate::HistoryMode; use zksync_types::U256; -use crate::vm_virtual_blocks::tracers::DefaultExecutionTracer; -use crate::vm_virtual_blocks::vm::Vm; +use crate::{ + interface::{VmExecutionStatistics, VmMemoryMetrics}, + vm_virtual_blocks::{tracers::DefaultExecutionTracer, vm::Vm}, + HistoryMode, +}; /// Module responsible for observing the VM behavior, i.e. calculating the statistics of the VM runs /// or reporting the VM memory usage. diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/tx.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/tx.rs index bfeeb56e022e..72a7dbc65de0 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/tx.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/tx.rs @@ -1,15 +1,16 @@ -use crate::vm_virtual_blocks::constants::BOOTLOADER_HEAP_PAGE; -use crate::vm_virtual_blocks::implementation::bytecode::{ - bytecode_to_factory_dep, compress_bytecodes, -}; -use crate::HistoryMode; use zk_evm_1_3_3::aux_structures::Timestamp; use zksync_state::WriteStorage; -use zksync_types::l1::is_l1_tx_type; -use zksync_types::Transaction; +use zksync_types::{l1::is_l1_tx_type, Transaction}; -use crate::vm_virtual_blocks::types::internals::TransactionData; -use crate::vm_virtual_blocks::vm::Vm; +use crate::{ + vm_virtual_blocks::{ + constants::BOOTLOADER_HEAP_PAGE, + implementation::bytecode::{bytecode_to_factory_dep, compress_bytecodes}, + types::internals::TransactionData, + vm::Vm, + }, + HistoryMode, +}; impl Vm { pub(crate) fn push_raw_transaction( diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/mod.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/mod.rs index 3a7a96e729d7..1500e7027b72 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/mod.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/mod.rs @@ -1,30 +1,24 @@ -pub use old_vm::{ - history_recorder::{HistoryDisabled, HistoryEnabled, HistoryMode}, - memory::SimpleMemory, - oracles::storage::StorageOracle, +pub use self::{ + bootloader_state::BootloaderState, + old_vm::{ + history_recorder::{HistoryDisabled, HistoryEnabled, HistoryMode}, + memory::SimpleMemory, + oracles::storage::StorageOracle, + }, + tracers::{ + dispatcher::TracerDispatcher, + traits::{ExecutionEndTracer, ExecutionProcessing, TracerPointer, VmTracer}, + }, + types::internals::ZkSyncVmState, + utils::transaction_encoding::TransactionVmExt, + vm::Vm, }; -pub use tracers::{ - dispatcher::TracerDispatcher, - traits::{ExecutionEndTracer, ExecutionProcessing, TracerPointer, VmTracer}, -}; - -pub use types::internals::ZkSyncVmState; -pub use utils::transaction_encoding::TransactionVmExt; - -pub use bootloader_state::BootloaderState; - -pub use vm::Vm; - mod bootloader_state; +pub mod constants; mod implementation; mod old_vm; pub(crate) mod tracers; mod types; -mod vm; - -pub mod constants; pub mod utils; - -// #[cfg(test)] -// mod tests; +mod vm; diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/event_sink.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/event_sink.rs index 49ec162fd5e2..02938594b5cf 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/event_sink.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/event_sink.rs @@ -1,8 +1,5 @@ -use crate::vm_virtual_blocks::old_vm::{ - history_recorder::{AppDataFrameManagerWithHistory, HistoryEnabled, HistoryMode}, - oracles::OracleWithHistory, -}; use std::collections::HashMap; + use zk_evm_1_3_3::{ abstractions::EventSink, aux_structures::{LogQuery, Timestamp}, @@ -12,6 +9,11 @@ use zk_evm_1_3_3::{ }, }; +use crate::vm_virtual_blocks::old_vm::{ + history_recorder::{AppDataFrameManagerWithHistory, HistoryEnabled, HistoryMode}, + oracles::OracleWithHistory, +}; + #[derive(Debug, Clone, PartialEq, Default)] pub struct InMemoryEventSink { frames_stack: AppDataFrameManagerWithHistory, H>, diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/history_recorder.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/history_recorder.rs index a38ee1772459..ca02739032cc 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/history_recorder.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/history_recorder.rs @@ -5,7 +5,6 @@ use zk_evm_1_3_3::{ vm_state::PrimitiveValue, zkevm_opcode_defs::{self}, }; - use zksync_state::{StoragePtr, WriteStorage}; use zksync_types::{StorageKey, U256}; use zksync_utils::{h256_to_u256, u256_to_h256}; @@ -767,11 +766,14 @@ impl HistoryRecorder, H> { #[cfg(test)] mod tests { - use crate::vm_virtual_blocks::old_vm::history_recorder::{HistoryRecorder, MemoryWrapper}; - use crate::vm_virtual_blocks::HistoryDisabled; use zk_evm_1_3_3::{aux_structures::Timestamp, vm_state::PrimitiveValue}; use zksync_types::U256; + use crate::vm_virtual_blocks::{ + old_vm::history_recorder::{HistoryRecorder, MemoryWrapper}, + HistoryDisabled, + }; + #[test] fn memory_equality() { let mut a: HistoryRecorder = Default::default(); diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/memory.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/memory.rs index f1a424c36aed..c78f8a9e7798 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/memory.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/memory.rs @@ -1,16 +1,18 @@ -use zk_evm_1_3_3::abstractions::{Memory, MemoryType}; -use zk_evm_1_3_3::aux_structures::{MemoryPage, MemoryQuery, Timestamp}; -use zk_evm_1_3_3::vm_state::PrimitiveValue; -use zk_evm_1_3_3::zkevm_opcode_defs::FatPointer; +use zk_evm_1_3_3::{ + abstractions::{Memory, MemoryType}, + aux_structures::{MemoryPage, MemoryQuery, Timestamp}, + vm_state::PrimitiveValue, + zkevm_opcode_defs::FatPointer, +}; use zksync_types::U256; -use crate::vm_virtual_blocks::old_vm::history_recorder::{ - FramedStack, HistoryEnabled, HistoryMode, IntFrameManagerWithHistory, MemoryWithHistory, - MemoryWrapper, WithHistory, -}; -use crate::vm_virtual_blocks::old_vm::oracles::OracleWithHistory; -use crate::vm_virtual_blocks::old_vm::utils::{ - aux_heap_page_from_base, heap_page_from_base, stack_page_from_base, +use crate::vm_virtual_blocks::old_vm::{ + history_recorder::{ + FramedStack, HistoryEnabled, HistoryMode, IntFrameManagerWithHistory, MemoryWithHistory, + MemoryWrapper, WithHistory, + }, + oracles::OracleWithHistory, + utils::{aux_heap_page_from_base, heap_page_from_base, stack_page_from_base}, }; #[derive(Debug, Clone, PartialEq)] diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/oracles/decommitter.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/oracles/decommitter.rs index 12c3ffd403d0..061912f83c92 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/oracles/decommitter.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/oracles/decommitter.rs @@ -1,23 +1,19 @@ -use std::collections::HashMap; -use std::fmt::Debug; +use std::{collections::HashMap, fmt::Debug}; -use crate::vm_virtual_blocks::old_vm::history_recorder::{ - HistoryEnabled, HistoryMode, HistoryRecorder, WithHistory, -}; - -use zk_evm_1_3_3::abstractions::MemoryType; -use zk_evm_1_3_3::aux_structures::Timestamp; use zk_evm_1_3_3::{ - abstractions::{DecommittmentProcessor, Memory}, - aux_structures::{DecommittmentQuery, MemoryIndex, MemoryLocation, MemoryPage, MemoryQuery}, + abstractions::{DecommittmentProcessor, Memory, MemoryType}, + aux_structures::{ + DecommittmentQuery, MemoryIndex, MemoryLocation, MemoryPage, MemoryQuery, Timestamp, + }, }; - use zksync_state::{ReadStorage, StoragePtr}; use zksync_types::U256; -use zksync_utils::bytecode::bytecode_len_in_words; -use zksync_utils::{bytes_to_be_words, u256_to_h256}; +use zksync_utils::{bytecode::bytecode_len_in_words, bytes_to_be_words, u256_to_h256}; use super::OracleWithHistory; +use crate::vm_virtual_blocks::old_vm::history_recorder::{ + HistoryEnabled, HistoryMode, HistoryRecorder, WithHistory, +}; /// The main job of the DecommiterOracle is to implement the DecommittmentProcessor trait - that is /// used by the VM to 'load' bytecodes into memory. diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/oracles/precompile.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/oracles/precompile.rs index 11ddb26d03a1..8fd77ef7f874 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/oracles/precompile.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/oracles/precompile.rs @@ -1,17 +1,14 @@ use zk_evm_1_3_3::{ - abstractions::Memory, - abstractions::PrecompileCyclesWitness, - abstractions::PrecompilesProcessor, + abstractions::{Memory, PrecompileCyclesWitness, PrecompilesProcessor}, aux_structures::{LogQuery, MemoryQuery, Timestamp}, precompiles::DefaultPrecompilesProcessor, }; +use super::OracleWithHistory; use crate::vm_virtual_blocks::old_vm::history_recorder::{ HistoryEnabled, HistoryMode, HistoryRecorder, }; -use super::OracleWithHistory; - /// Wrap of DefaultPrecompilesProcessor that store queue /// of timestamp when precompiles are called to be executed. /// Number of precompiles per block is strictly limited, diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/oracles/storage.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/oracles/storage.rs index 70186b78b324..91c293f4ac80 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/oracles/storage.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/oracles/storage.rs @@ -1,26 +1,22 @@ use std::collections::HashMap; -use crate::vm_virtual_blocks::old_vm::history_recorder::{ - AppDataFrameManagerWithHistory, HashMapHistoryEvent, HistoryEnabled, HistoryMode, - HistoryRecorder, StorageWrapper, WithHistory, -}; - -use zk_evm_1_3_3::abstractions::RefundedAmounts; -use zk_evm_1_3_3::zkevm_opcode_defs::system_params::INITIAL_STORAGE_WRITE_PUBDATA_BYTES; use zk_evm_1_3_3::{ - abstractions::{RefundType, Storage as VmStorageOracle}, + abstractions::{RefundType, RefundedAmounts, Storage as VmStorageOracle}, aux_structures::{LogQuery, Timestamp}, + zkevm_opcode_defs::system_params::INITIAL_STORAGE_WRITE_PUBDATA_BYTES, }; - use zksync_state::{StoragePtr, WriteStorage}; -use zksync_types::utils::storage_key_for_eth_balance; use zksync_types::{ - AccountTreeId, Address, StorageKey, StorageLogQuery, StorageLogQueryType, BOOTLOADER_ADDRESS, - U256, + utils::storage_key_for_eth_balance, AccountTreeId, Address, StorageKey, StorageLogQuery, + StorageLogQueryType, BOOTLOADER_ADDRESS, U256, }; use zksync_utils::u256_to_h256; use super::OracleWithHistory; +use crate::vm_virtual_blocks::old_vm::history_recorder::{ + AppDataFrameManagerWithHistory, HashMapHistoryEvent, HistoryEnabled, HistoryMode, + HistoryRecorder, StorageWrapper, WithHistory, +}; // While the storage does not support different shards, it was decided to write the // code of the StorageOracle with the shard parameters in mind. diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/utils.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/utils.rs index 654977784957..7d38ba1058df 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/utils.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/utils.rs @@ -1,22 +1,19 @@ -use crate::vm_virtual_blocks::old_vm::memory::SimpleMemory; - -use crate::vm_virtual_blocks::types::internals::ZkSyncVmState; -use crate::vm_virtual_blocks::HistoryMode; - -use zk_evm_1_3_3::zkevm_opcode_defs::decoding::{ - AllowedPcOrImm, EncodingModeProduction, VmEncodingMode, -}; -use zk_evm_1_3_3::zkevm_opcode_defs::RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER; use zk_evm_1_3_3::{ aux_structures::{MemoryPage, Timestamp}, vm_state::PrimitiveValue, - zkevm_opcode_defs::FatPointer, + zkevm_opcode_defs::{ + decoding::{AllowedPcOrImm, EncodingModeProduction, VmEncodingMode}, + FatPointer, RET_IMPLICIT_RETURNDATA_PARAMS_REGISTER, + }, }; use zksync_state::WriteStorage; use zksync_system_constants::L1_GAS_PER_PUBDATA_BYTE; - use zksync_types::{Address, U256}; +use crate::vm_virtual_blocks::{ + old_vm::memory::SimpleMemory, types::internals::ZkSyncVmState, HistoryMode, +}; + #[derive(Debug, Clone)] pub(crate) enum VmExecutionResult { Ok(Vec), diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/default_tracers.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/default_tracers.rs index f394ab5f752d..463bdaa4f359 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/default_tracers.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/default_tracers.rs @@ -1,33 +1,37 @@ -use std::fmt::{Debug, Formatter}; -use std::marker::PhantomData; +use std::{ + fmt::{Debug, Formatter}, + marker::PhantomData, +}; -use crate::interface::dyn_tracers::vm_1_3_3::DynTracer; -use crate::interface::tracer::VmExecutionStopReason; -use crate::interface::VmExecutionMode; -use zk_evm_1_3_3::witness_trace::DummyTracer; -use zk_evm_1_3_3::zkevm_opcode_defs::{Opcode, RetOpcode}; use zk_evm_1_3_3::{ tracing::{ AfterDecodingData, AfterExecutionData, BeforeExecutionData, Tracer, VmLocalStateData, }, vm_state::VmLocalState, + witness_trace::DummyTracer, + zkevm_opcode_defs::{Opcode, RetOpcode}, }; use zksync_state::{StoragePtr, WriteStorage}; use zksync_types::Timestamp; -use crate::vm_virtual_blocks::bootloader_state::utils::apply_l2_block; -use crate::vm_virtual_blocks::bootloader_state::BootloaderState; -use crate::vm_virtual_blocks::constants::BOOTLOADER_HEAP_PAGE; -use crate::vm_virtual_blocks::old_vm::history_recorder::HistoryMode; -use crate::vm_virtual_blocks::old_vm::memory::SimpleMemory; -use crate::vm_virtual_blocks::tracers::dispatcher::TracerDispatcher; -use crate::vm_virtual_blocks::tracers::traits::{ExecutionEndTracer, ExecutionProcessing}; -use crate::vm_virtual_blocks::tracers::utils::{ - computational_gas_price, gas_spent_on_bytecodes_and_long_messages_this_opcode, - print_debug_if_needed, VmHook, +use crate::{ + interface::{dyn_tracers::vm_1_3_3::DynTracer, tracer::VmExecutionStopReason, VmExecutionMode}, + vm_virtual_blocks::{ + bootloader_state::{utils::apply_l2_block, BootloaderState}, + constants::BOOTLOADER_HEAP_PAGE, + old_vm::{history_recorder::HistoryMode, memory::SimpleMemory}, + tracers::{ + dispatcher::TracerDispatcher, + traits::{ExecutionEndTracer, ExecutionProcessing}, + utils::{ + computational_gas_price, gas_spent_on_bytecodes_and_long_messages_this_opcode, + print_debug_if_needed, VmHook, + }, + RefundsTracer, ResultTracer, + }, + types::internals::ZkSyncVmState, + }, }; -use crate::vm_virtual_blocks::tracers::{RefundsTracer, ResultTracer}; -use crate::vm_virtual_blocks::types::internals::ZkSyncVmState; /// Default tracer for the VM. It manages the other tracers execution and stop the vm when needed. pub(crate) struct DefaultExecutionTracer { diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/dispatcher.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/dispatcher.rs index 7eb89461eab3..b1b5ef418eeb 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/dispatcher.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/dispatcher.rs @@ -1,17 +1,18 @@ -use crate::interface::dyn_tracers::vm_1_3_3::DynTracer; -use crate::interface::tracer::VmExecutionStopReason; -use crate::interface::VmExecutionResultAndLogs; -use crate::vm_virtual_blocks::TracerPointer; -use crate::vm_virtual_blocks::{ - BootloaderState, ExecutionEndTracer, ExecutionProcessing, HistoryMode, SimpleMemory, VmTracer, - ZkSyncVmState, -}; - use zk_evm_1_3_3::tracing::{ AfterDecodingData, AfterExecutionData, BeforeExecutionData, VmLocalStateData, }; use zksync_state::{StoragePtr, WriteStorage}; +use crate::{ + interface::{ + dyn_tracers::vm_1_3_3::DynTracer, tracer::VmExecutionStopReason, VmExecutionResultAndLogs, + }, + vm_virtual_blocks::{ + BootloaderState, ExecutionEndTracer, ExecutionProcessing, HistoryMode, SimpleMemory, + TracerPointer, VmTracer, ZkSyncVmState, + }, +}; + impl From> for TracerDispatcher { fn from(value: TracerPointer) -> Self { Self { diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/refunds.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/refunds.rs index 6496e13172a2..6051cd7bb7d8 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/refunds.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/refunds.rs @@ -1,9 +1,6 @@ -use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics}; - use std::collections::HashMap; -use crate::interface::dyn_tracers::vm_1_3_3::DynTracer; -use crate::interface::{L1BatchEnv, Refunds, VmExecutionResultAndLogs}; +use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics}; use zk_evm_1_3_3::{ aux_structures::Timestamp, tracing::{BeforeExecutionData, VmLocalStateData}, @@ -17,23 +14,26 @@ use zksync_types::{ zkevm_test_harness::witness::sort_storage_access::sort_storage_access_queries, L1BatchNumber, StorageKey, U256, }; -use zksync_utils::bytecode::bytecode_len_in_bytes; -use zksync_utils::{ceil_div_u256, u256_to_h256}; - -use crate::vm_virtual_blocks::bootloader_state::BootloaderState; -use crate::vm_virtual_blocks::constants::{ - BOOTLOADER_HEAP_PAGE, OPERATOR_REFUNDS_OFFSET, TX_GAS_LIMIT_OFFSET, -}; -use crate::vm_virtual_blocks::old_vm::{ - events::merge_events, history_recorder::HistoryMode, memory::SimpleMemory, - oracles::storage::storage_key_of_log, utils::eth_price_per_pubdata_byte, -}; -use crate::vm_virtual_blocks::tracers::utils::gas_spent_on_bytecodes_and_long_messages_this_opcode; -use crate::vm_virtual_blocks::tracers::{ - traits::{ExecutionEndTracer, ExecutionProcessing, VmTracer}, - utils::{get_vm_hook_params, VmHook}, +use zksync_utils::{bytecode::bytecode_len_in_bytes, ceil_div_u256, u256_to_h256}; + +use crate::{ + interface::{dyn_tracers::vm_1_3_3::DynTracer, L1BatchEnv, Refunds, VmExecutionResultAndLogs}, + vm_virtual_blocks::{ + bootloader_state::BootloaderState, + constants::{BOOTLOADER_HEAP_PAGE, OPERATOR_REFUNDS_OFFSET, TX_GAS_LIMIT_OFFSET}, + old_vm::{ + events::merge_events, history_recorder::HistoryMode, memory::SimpleMemory, + oracles::storage::storage_key_of_log, utils::eth_price_per_pubdata_byte, + }, + tracers::{ + traits::{ExecutionEndTracer, ExecutionProcessing, VmTracer}, + utils::{ + gas_spent_on_bytecodes_and_long_messages_this_opcode, get_vm_hook_params, VmHook, + }, + }, + types::internals::ZkSyncVmState, + }, }; -use crate::vm_virtual_blocks::types::internals::ZkSyncVmState; /// Tracer responsible for collecting information about refunds. #[derive(Debug, Clone)] diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/result_tracer.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/result_tracer.rs index 1f566fea5677..8c6a5d1793f8 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/result_tracer.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/result_tracer.rs @@ -4,28 +4,28 @@ use zk_evm_1_3_3::{ zkevm_opcode_defs::FatPointer, }; use zksync_state::{StoragePtr, WriteStorage}; - -use crate::interface::dyn_tracers::vm_1_3_3::DynTracer; -use crate::interface::tracer::VmExecutionStopReason; -use crate::interface::{ - ExecutionResult, Halt, TxRevertReason, VmExecutionMode, VmExecutionResultAndLogs, - VmRevertReason, -}; use zksync_types::U256; -use crate::vm_virtual_blocks::bootloader_state::BootloaderState; -use crate::vm_virtual_blocks::old_vm::{ - history_recorder::HistoryMode, - memory::SimpleMemory, - utils::{vm_may_have_ended_inner, VmExecutionResult}, +use crate::{ + interface::{ + dyn_tracers::vm_1_3_3::DynTracer, tracer::VmExecutionStopReason, ExecutionResult, Halt, + TxRevertReason, VmExecutionMode, VmExecutionResultAndLogs, VmRevertReason, + }, + vm_virtual_blocks::{ + bootloader_state::BootloaderState, + constants::{BOOTLOADER_HEAP_PAGE, RESULT_SUCCESS_FIRST_SLOT}, + old_vm::{ + history_recorder::HistoryMode, + memory::SimpleMemory, + utils::{vm_may_have_ended_inner, VmExecutionResult}, + }, + tracers::{ + traits::{ExecutionEndTracer, ExecutionProcessing, VmTracer}, + utils::{get_vm_hook_params, read_pointer, VmHook}, + }, + types::internals::ZkSyncVmState, + }, }; -use crate::vm_virtual_blocks::tracers::{ - traits::{ExecutionEndTracer, ExecutionProcessing, VmTracer}, - utils::{get_vm_hook_params, read_pointer, VmHook}, -}; -use crate::vm_virtual_blocks::types::internals::ZkSyncVmState; - -use crate::vm_virtual_blocks::constants::{BOOTLOADER_HEAP_PAGE, RESULT_SUCCESS_FIRST_SLOT}; #[derive(Debug, Clone)] enum Result { diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/traits.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/traits.rs index 3045e6f83199..6d8fdab4e661 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/traits.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/traits.rs @@ -1,12 +1,15 @@ -use crate::interface::dyn_tracers::vm_1_3_3::DynTracer; -use crate::interface::tracer::VmExecutionStopReason; -use crate::interface::VmExecutionResultAndLogs; use zksync_state::WriteStorage; -use crate::vm_virtual_blocks::bootloader_state::BootloaderState; -use crate::vm_virtual_blocks::old_vm::history_recorder::HistoryMode; -use crate::vm_virtual_blocks::old_vm::memory::SimpleMemory; -use crate::vm_virtual_blocks::types::internals::ZkSyncVmState; +use crate::{ + interface::{ + dyn_tracers::vm_1_3_3::DynTracer, tracer::VmExecutionStopReason, VmExecutionResultAndLogs, + }, + vm_virtual_blocks::{ + bootloader_state::BootloaderState, + old_vm::{history_recorder::HistoryMode, memory::SimpleMemory}, + types::internals::ZkSyncVmState, + }, +}; pub type TracerPointer = Box>; /// Run tracer for collecting data during the vm execution cycles diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/utils.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/utils.rs index 0ab697f626fa..b2358602fe05 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/utils.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/tracers/utils.rs @@ -1,10 +1,10 @@ -use zk_evm_1_3_3::aux_structures::MemoryPage; -use zk_evm_1_3_3::zkevm_opcode_defs::{FarCallABI, FarCallForwardPageType}; use zk_evm_1_3_3::{ + aux_structures::MemoryPage, tracing::{BeforeExecutionData, VmLocalStateData}, - zkevm_opcode_defs::{FatPointer, LogOpcode, Opcode, UMAOpcode}, + zkevm_opcode_defs::{ + FarCallABI, FarCallForwardPageType, FatPointer, LogOpcode, Opcode, UMAOpcode, + }, }; - use zksync_system_constants::{ ECRECOVER_PRECOMPILE_ADDRESS, KECCAK256_PRECOMPILE_ADDRESS, KNOWN_CODES_STORAGE_ADDRESS, L1_MESSENGER_ADDRESS, SHA256_PRECOMPILE_ADDRESS, @@ -12,12 +12,16 @@ use zksync_system_constants::{ use zksync_types::U256; use zksync_utils::u256_to_h256; -use crate::vm_virtual_blocks::constants::{ - BOOTLOADER_HEAP_PAGE, VM_HOOK_PARAMS_COUNT, VM_HOOK_PARAMS_START_POSITION, VM_HOOK_POSITION, +use crate::vm_virtual_blocks::{ + constants::{ + BOOTLOADER_HEAP_PAGE, VM_HOOK_PARAMS_COUNT, VM_HOOK_PARAMS_START_POSITION, VM_HOOK_POSITION, + }, + old_vm::{ + history_recorder::HistoryMode, + memory::SimpleMemory, + utils::{aux_heap_page_from_base, heap_page_from_base}, + }, }; -use crate::vm_virtual_blocks::old_vm::history_recorder::HistoryMode; -use crate::vm_virtual_blocks::old_vm::memory::SimpleMemory; -use crate::vm_virtual_blocks::old_vm::utils::{aux_heap_page_from_base, heap_page_from_base}; #[derive(Clone, Debug, Copy)] pub(crate) enum VmHook { diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/types/internals/transaction_data.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/types/internals/transaction_data.rs index add3d829d800..6d2fe36868b0 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/types/internals/transaction_data.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/types/internals/transaction_data.rs @@ -1,15 +1,15 @@ use std::convert::TryInto; -use zksync_types::ethabi::{encode, Address, Token}; -use zksync_types::fee::{encoding_len, Fee}; -use zksync_types::l1::is_l1_tx_type; -use zksync_types::l2::L2Tx; -use zksync_types::transaction_request::{PaymasterParams, TransactionRequest}; + use zksync_types::{ - l2::TransactionType, Bytes, Execute, ExecuteTransactionCommon, L2ChainId, L2TxCommonData, - Nonce, Transaction, H256, U256, + ethabi::{encode, Address, Token}, + fee::{encoding_len, Fee}, + l1::is_l1_tx_type, + l2::{L2Tx, TransactionType}, + transaction_request::{PaymasterParams, TransactionRequest}, + Bytes, Execute, ExecuteTransactionCommon, L2ChainId, L2TxCommonData, Nonce, Transaction, H256, + U256, }; -use zksync_utils::address_to_h256; -use zksync_utils::{bytecode::hash_bytecode, bytes_to_be_words, h256_to_u256}; +use zksync_utils::{address_to_h256, bytecode::hash_bytecode, bytes_to_be_words, h256_to_u256}; use crate::vm_virtual_blocks::utils::overhead::{get_amortized_overhead, OverheadCoefficients}; @@ -303,9 +303,10 @@ impl TryInto for TransactionData { #[cfg(test)] mod tests { - use super::*; use zksync_types::fee::encoding_len; + use super::*; + #[test] fn test_consistency_with_encoding_length() { let transaction = TransactionData { diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/types/internals/vm_state.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/types/internals/vm_state.rs index 8784c754fadb..5d67982e7b43 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/types/internals/vm_state.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/types/internals/vm_state.rs @@ -1,34 +1,40 @@ use zk_evm_1_3_3::{ - aux_structures::MemoryPage, - aux_structures::Timestamp, + aux_structures::{MemoryPage, Timestamp}, block_properties::BlockProperties, vm_state::{CallStackEntry, PrimitiveValue, VmState}, witness_trace::DummyTracer, zkevm_opcode_defs::{ system_params::{BOOTLOADER_MAX_MEMORY, INITIAL_FRAME_FORMAL_EH_LOCATION}, - FatPointer, BOOTLOADER_CALLDATA_PAGE, + FatPointer, BOOTLOADER_BASE_PAGE, BOOTLOADER_CALLDATA_PAGE, BOOTLOADER_CODE_PAGE, + STARTING_BASE_PAGE, STARTING_TIMESTAMP, }, }; - -use crate::interface::{L1BatchEnv, L2Block, SystemEnv}; -use zk_evm_1_3_3::zkevm_opcode_defs::{ - BOOTLOADER_BASE_PAGE, BOOTLOADER_CODE_PAGE, STARTING_BASE_PAGE, STARTING_TIMESTAMP, -}; use zksync_state::{StoragePtr, WriteStorage}; use zksync_system_constants::BOOTLOADER_ADDRESS; -use zksync_types::block::legacy_miniblock_hash; -use zksync_types::{zkevm_test_harness::INITIAL_MONOTONIC_CYCLE_COUNTER, Address, MiniblockNumber}; +use zksync_types::{ + block::legacy_miniblock_hash, zkevm_test_harness::INITIAL_MONOTONIC_CYCLE_COUNTER, Address, + MiniblockNumber, +}; use zksync_utils::h256_to_u256; -use crate::vm_virtual_blocks::bootloader_state::BootloaderState; -use crate::vm_virtual_blocks::constants::BOOTLOADER_HEAP_PAGE; -use crate::vm_virtual_blocks::old_vm::{ - event_sink::InMemoryEventSink, history_recorder::HistoryMode, memory::SimpleMemory, - oracles::decommitter::DecommitterOracle, oracles::precompile::PrecompilesProcessorWithHistory, - oracles::storage::StorageOracle, +use crate::{ + interface::{L1BatchEnv, L2Block, SystemEnv}, + vm_virtual_blocks::{ + bootloader_state::BootloaderState, + constants::BOOTLOADER_HEAP_PAGE, + old_vm::{ + event_sink::InMemoryEventSink, + history_recorder::HistoryMode, + memory::SimpleMemory, + oracles::{ + decommitter::DecommitterOracle, precompile::PrecompilesProcessorWithHistory, + storage::StorageOracle, + }, + }, + types::l1_batch_env::bootloader_initial_memory, + utils::l2_blocks::{assert_next_block, load_last_l2_block}, + }, }; -use crate::vm_virtual_blocks::types::l1_batch_env::bootloader_initial_memory; -use crate::vm_virtual_blocks::utils::l2_blocks::{assert_next_block, load_last_l2_block}; pub type ZkSyncVmState = VmState< StorageOracle, diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/types/l1_batch_env.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/types/l1_batch_env.rs index 8af706954ed3..0e43863b1965 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/types/l1_batch_env.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/types/l1_batch_env.rs @@ -1,7 +1,8 @@ -use crate::interface::L1BatchEnv; use zksync_types::U256; use zksync_utils::{address_to_u256, h256_to_u256}; +use crate::interface::L1BatchEnv; + const OPERATOR_ADDRESS_SLOT: usize = 0; const PREV_BLOCK_HASH_SLOT: usize = 1; const NEW_BLOCK_TIMESTAMP_SLOT: usize = 2; diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/utils/l2_blocks.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/utils/l2_blocks.rs index 3d5f58094e01..5dd26c4c0277 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/utils/l2_blocks.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/utils/l2_blocks.rs @@ -1,15 +1,17 @@ -use crate::interface::{L2Block, L2BlockEnv}; use zksync_state::{ReadStorage, StoragePtr}; use zksync_system_constants::{ SYSTEM_CONTEXT_ADDRESS, SYSTEM_CONTEXT_CURRENT_L2_BLOCK_HASHES_POSITION, SYSTEM_CONTEXT_CURRENT_L2_BLOCK_INFO_POSITION, SYSTEM_CONTEXT_CURRENT_TX_ROLLING_HASH_POSITION, SYSTEM_CONTEXT_STORED_L2_BLOCK_HASHES, }; -use zksync_types::block::unpack_block_info; -use zksync_types::web3::signing::keccak256; -use zksync_types::{AccountTreeId, MiniblockNumber, StorageKey, H256, U256}; +use zksync_types::{ + block::unpack_block_info, web3::signing::keccak256, AccountTreeId, MiniblockNumber, StorageKey, + H256, U256, +}; use zksync_utils::{h256_to_u256, u256_to_h256}; +use crate::interface::{L2Block, L2BlockEnv}; + pub(crate) fn get_l2_block_hash_key(block_number: u32) -> StorageKey { let position = h256_to_u256(SYSTEM_CONTEXT_CURRENT_L2_BLOCK_HASHES_POSITION) + U256::from(block_number % SYSTEM_CONTEXT_STORED_L2_BLOCK_HASHES); diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/utils/overhead.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/utils/overhead.rs index c17d619b464d..79c52ac373b6 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/utils/overhead.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/utils/overhead.rs @@ -1,12 +1,12 @@ -use crate::vm_virtual_blocks::constants::{ - BLOCK_OVERHEAD_GAS, BLOCK_OVERHEAD_PUBDATA, BOOTLOADER_TX_ENCODING_SPACE, -}; use zk_evm_1_3_3::zkevm_opcode_defs::system_params::MAX_TX_ERGS_LIMIT; use zksync_system_constants::{MAX_L2_TX_GAS_LIMIT, MAX_TXS_IN_BLOCK}; -use zksync_types::l1::is_l1_tx_type; -use zksync_types::U256; +use zksync_types::{l1::is_l1_tx_type, U256}; use zksync_utils::ceil_div_u256; +use crate::vm_virtual_blocks::constants::{ + BLOCK_OVERHEAD_GAS, BLOCK_OVERHEAD_PUBDATA, BOOTLOADER_TX_ENCODING_SPACE, +}; + /// Derives the overhead for processing transactions in a block. pub fn derive_overhead( gas_limit: u32, diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/utils/transaction_encoding.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/utils/transaction_encoding.rs index b45ec4d14110..5f9c37cbb737 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/utils/transaction_encoding.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/utils/transaction_encoding.rs @@ -1,6 +1,7 @@ -use crate::vm_virtual_blocks::types::internals::TransactionData; use zksync_types::Transaction; +use crate::vm_virtual_blocks::types::internals::TransactionData; + /// Extension for transactions, specific for VM. Required for bypassing the orphan rule pub trait TransactionVmExt { /// Get the size of the transaction in tokens. diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/vm.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/vm.rs index 4110825a260e..ed05e9514753 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/vm.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/vm.rs @@ -1,21 +1,22 @@ -use crate::interface::{ - BootloaderMemory, BytecodeCompressionError, CurrentExecutionState, L1BatchEnv, L2BlockEnv, - SystemEnv, VmExecutionMode, VmExecutionResultAndLogs, VmInterface, VmInterfaceHistoryEnabled, - VmMemoryMetrics, -}; -use crate::vm_latest::HistoryEnabled; -use crate::HistoryMode; use zksync_state::{StoragePtr, WriteStorage}; -use zksync_types::l2_to_l1_log::UserL2ToL1Log; -use zksync_types::Transaction; +use zksync_types::{l2_to_l1_log::UserL2ToL1Log, Transaction}; use zksync_utils::bytecode::CompressedBytecodeInfo; -use crate::vm_virtual_blocks::old_vm::events::merge_events; - -use crate::vm_virtual_blocks::bootloader_state::BootloaderState; -use crate::vm_virtual_blocks::tracers::dispatcher::TracerDispatcher; - -use crate::vm_virtual_blocks::types::internals::{new_vm_state, VmSnapshot, ZkSyncVmState}; +use crate::{ + interface::{ + BootloaderMemory, BytecodeCompressionError, CurrentExecutionState, L1BatchEnv, L2BlockEnv, + SystemEnv, VmExecutionMode, VmExecutionResultAndLogs, VmInterface, + VmInterfaceHistoryEnabled, VmMemoryMetrics, + }, + vm_latest::HistoryEnabled, + vm_virtual_blocks::{ + bootloader_state::BootloaderState, + old_vm::events::merge_events, + tracers::dispatcher::TracerDispatcher, + types::internals::{new_vm_state, VmSnapshot, ZkSyncVmState}, + }, + HistoryMode, +}; /// Main entry point for Virtual Machine integration. /// The instance should process only one l1 batch diff --git a/core/lib/multivm/src/vm_instance.rs b/core/lib/multivm/src/vm_instance.rs index 6b90da4bd3ba..6716aeaf146f 100644 --- a/core/lib/multivm/src/vm_instance.rs +++ b/core/lib/multivm/src/vm_instance.rs @@ -1,15 +1,16 @@ -use crate::interface::{ - BootloaderMemory, CurrentExecutionState, FinishedL1Batch, L1BatchEnv, L2BlockEnv, SystemEnv, - VmExecutionMode, VmExecutionResultAndLogs, VmInterface, VmInterfaceHistoryEnabled, - VmMemoryMetrics, -}; - use zksync_state::{StoragePtr, WriteStorage}; use zksync_types::VmVersion; use zksync_utils::bytecode::CompressedBytecodeInfo; -use crate::glue::history_mode::HistoryMode; -use crate::tracers::TracerDispatcher; +use crate::{ + glue::history_mode::HistoryMode, + interface::{ + BootloaderMemory, CurrentExecutionState, FinishedL1Batch, L1BatchEnv, L2BlockEnv, + SystemEnv, VmExecutionMode, VmExecutionResultAndLogs, VmInterface, + VmInterfaceHistoryEnabled, VmMemoryMetrics, + }, + tracers::TracerDispatcher, +}; #[derive(Debug)] pub enum VmInstance { diff --git a/core/lib/object_store/src/file.rs b/core/lib/object_store/src/file.rs index c248fb76595c..6f589e83630d 100644 --- a/core/lib/object_store/src/file.rs +++ b/core/lib/object_store/src/file.rs @@ -1,8 +1,8 @@ +use std::fmt::Debug; + use async_trait::async_trait; use tokio::{fs, io}; -use std::fmt::Debug; - use crate::raw::{Bucket, ObjectStore, ObjectStoreError}; impl From for ObjectStoreError { diff --git a/core/lib/object_store/src/gcs.rs b/core/lib/object_store/src/gcs.rs index d01fb833b12a..1d88aa5237ae 100644 --- a/core/lib/object_store/src/gcs.rs +++ b/core/lib/object_store/src/gcs.rs @@ -1,21 +1,23 @@ //! GCS-based [`ObjectStore`] implementation. +use std::{fmt, future::Future, time::Duration}; + use async_trait::async_trait; use google_cloud_auth::{credentials::CredentialsFile, error::Error}; use google_cloud_storage::{ client::{Client, ClientConfig}, - http::objects::{ - delete::DeleteObjectRequest, - download::Range, - get::GetObjectRequest, - upload::{Media, UploadObjectRequest, UploadType}, + http::{ + objects::{ + delete::DeleteObjectRequest, + download::Range, + get::GetObjectRequest, + upload::{Media, UploadObjectRequest, UploadType}, + }, + Error as HttpError, }, - http::Error as HttpError, }; use http::StatusCode; -use std::{fmt, future::Future, time::Duration}; - use crate::{ metrics::GCS_METRICS, raw::{Bucket, ObjectStore, ObjectStoreError}, diff --git a/core/lib/object_store/src/metrics.rs b/core/lib/object_store/src/metrics.rs index 9cd51ba3ed73..f372b5bac1cc 100644 --- a/core/lib/object_store/src/metrics.rs +++ b/core/lib/object_store/src/metrics.rs @@ -1,9 +1,9 @@ //! Metrics for the object storage. -use vise::{Buckets, Histogram, LabeledFamily, LatencyObserver, Metrics}; - use std::time::Duration; +use vise::{Buckets, Histogram, LabeledFamily, LatencyObserver, Metrics}; + use crate::Bucket; #[derive(Debug, Metrics)] diff --git a/core/lib/object_store/src/mock.rs b/core/lib/object_store/src/mock.rs index 727ef1e8d53a..ac1a2fd7a444 100644 --- a/core/lib/object_store/src/mock.rs +++ b/core/lib/object_store/src/mock.rs @@ -1,10 +1,10 @@ //! Mock implementation of [`ObjectStore`]. +use std::collections::HashMap; + use async_trait::async_trait; use tokio::sync::Mutex; -use std::collections::HashMap; - use crate::raw::{Bucket, ObjectStore, ObjectStoreError}; type BucketMap = HashMap>; diff --git a/core/lib/object_store/src/objects.rs b/core/lib/object_store/src/objects.rs index e5ee186676eb..35808bb4686d 100644 --- a/core/lib/object_store/src/objects.rs +++ b/core/lib/object_store/src/objects.rs @@ -1,15 +1,17 @@ //! Stored objects. -use zksync_types::aggregated_operations::L1BatchProofForL1; use zksync_types::{ + aggregated_operations::L1BatchProofForL1, proofs::{AggregationRound, PrepareBasicCircuitsJob}, storage::witness_block_state::WitnessBlockState, zkevm_test_harness::{ abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit, bellman::bn256::Bn256, encodings::{recursion_request::RecursionRequest, QueueSimulator}, - witness::full_block_artifact::{BlockBasicCircuits, BlockBasicCircuitsPublicInputs}, - witness::oracle::VmWitnessOracle, + witness::{ + full_block_artifact::{BlockBasicCircuits, BlockBasicCircuitsPublicInputs}, + oracle::VmWitnessOracle, + }, LeafAggregationOutputDataWitness, NodeAggregationOutputDataWitness, SchedulerCircuitInstanceWitness, }, diff --git a/core/lib/object_store/src/raw.rs b/core/lib/object_store/src/raw.rs index bf318a61610f..c68b4cb978f6 100644 --- a/core/lib/object_store/src/raw.rs +++ b/core/lib/object_store/src/raw.rs @@ -1,10 +1,10 @@ -use async_trait::async_trait; - use std::{error, fmt, sync::Arc}; -use crate::{file::FileBackedObjectStore, gcs::GoogleCloudStorage, mock::MockStore}; +use async_trait::async_trait; use zksync_config::configs::object_store::{ObjectStoreConfig, ObjectStoreMode}; +use crate::{file::FileBackedObjectStore, gcs::GoogleCloudStorage, mock::MockStore}; + /// Bucket for [`ObjectStore`] in which objects can be placed. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] diff --git a/core/lib/object_store/tests/integration.rs b/core/lib/object_store/tests/integration.rs index dfa659dcf8b9..9db2061f17fd 100644 --- a/core/lib/object_store/tests/integration.rs +++ b/core/lib/object_store/tests/integration.rs @@ -1,7 +1,6 @@ //! Integration tests for object store. use tokio::fs; - use zksync_object_store::{Bucket, ObjectStoreFactory}; use zksync_types::{ proofs::{PrepareBasicCircuitsJob, StorageLogMetadata}, diff --git a/core/lib/prometheus_exporter/src/lib.rs b/core/lib/prometheus_exporter/src/lib.rs index 25f5915e2057..4eda0bebe0e6 100644 --- a/core/lib/prometheus_exporter/src/lib.rs +++ b/core/lib/prometheus_exporter/src/lib.rs @@ -1,11 +1,11 @@ +use std::{net::Ipv4Addr, time::Duration}; + use anyhow::Context as _; use metrics_exporter_prometheus::{Matcher, PrometheusBuilder}; use tokio::sync::watch; use vise::MetricsCollection; use vise_exporter::MetricsExporter; -use std::{net::Ipv4Addr, time::Duration}; - fn configure_legacy_exporter(builder: PrometheusBuilder) -> PrometheusBuilder { // in seconds let default_latency_buckets = [0.001, 0.005, 0.025, 0.1, 0.25, 1.0, 5.0, 30.0, 120.0]; diff --git a/core/lib/prover_utils/src/gcs_proof_fetcher.rs b/core/lib/prover_utils/src/gcs_proof_fetcher.rs index 8b59fe67a611..26872701a1fe 100644 --- a/core/lib/prover_utils/src/gcs_proof_fetcher.rs +++ b/core/lib/prover_utils/src/gcs_proof_fetcher.rs @@ -1,6 +1,5 @@ use zksync_object_store::{ObjectStore, ObjectStoreError}; -use zksync_types::aggregated_operations::L1BatchProofForL1; -use zksync_types::L1BatchNumber; +use zksync_types::{aggregated_operations::L1BatchProofForL1, L1BatchNumber}; pub async fn load_wrapped_fri_proofs_for_range( from: L1BatchNumber, diff --git a/core/lib/prover_utils/src/region_fetcher.rs b/core/lib/prover_utils/src/region_fetcher.rs index 22a0cedce494..d2c49dd068dc 100644 --- a/core/lib/prover_utils/src/region_fetcher.rs +++ b/core/lib/prover_utils/src/region_fetcher.rs @@ -1,8 +1,9 @@ use anyhow::Context as _; use regex::Regex; -use reqwest::header::{HeaderMap, HeaderValue}; -use reqwest::Method; - +use reqwest::{ + header::{HeaderMap, HeaderValue}, + Method, +}; use zksync_config::configs::ProverGroupConfig; use zksync_utils::http_with_retries::send_request_with_retries; diff --git a/core/lib/prover_utils/src/vk_commitment_helper.rs b/core/lib/prover_utils/src/vk_commitment_helper.rs index 05e35a4f7eec..9a6c074b1d23 100644 --- a/core/lib/prover_utils/src/vk_commitment_helper.rs +++ b/core/lib/prover_utils/src/vk_commitment_helper.rs @@ -1,5 +1,6 @@ -use anyhow::Context as _; use std::fs; + +use anyhow::Context as _; use toml_edit::{Document, Item, Value}; pub fn get_toml_formatted_value(string_value: String) -> Item { diff --git a/core/lib/queued_job_processor/src/lib.rs b/core/lib/queued_job_processor/src/lib.rs index d5ed185b2569..2966fba49cac 100644 --- a/core/lib/queued_job_processor/src/lib.rs +++ b/core/lib/queued_job_processor/src/lib.rs @@ -1,15 +1,13 @@ -use std::fmt::Debug; -use std::time::{Duration, Instant}; +use std::{ + fmt::Debug, + time::{Duration, Instant}, +}; use anyhow::Context as _; pub use async_trait::async_trait; -use tokio::sync::watch; -use tokio::task::JoinHandle; -use tokio::time::sleep; - -use zksync_utils::panic_extractor::try_extract_panic_message; - +use tokio::{sync::watch, task::JoinHandle, time::sleep}; use vise::{Buckets, Counter, Histogram, LabeledFamily, Metrics}; +use zksync_utils::panic_extractor::try_extract_panic_message; const ATTEMPT_BUCKETS: Buckets = Buckets::exponential(1.0..=64.0, 2.0); diff --git a/core/lib/state/src/cache/metrics.rs b/core/lib/state/src/cache/metrics.rs index 13bc8c94aa97..0e8c8cd86854 100644 --- a/core/lib/state/src/cache/metrics.rs +++ b/core/lib/state/src/cache/metrics.rs @@ -1,9 +1,9 @@ //! General-purpose cache metrics. -use vise::{Buckets, Counter, EncodeLabelValue, Gauge, Histogram, LabeledFamily, Metrics}; - use std::time::Duration; +use vise::{Buckets, Counter, EncodeLabelValue, Gauge, Histogram, LabeledFamily, Metrics}; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelValue)] #[metrics(rename_all = "snake_case")] pub(super) enum Method { diff --git a/core/lib/state/src/in_memory.rs b/core/lib/state/src/in_memory.rs index fcb69affea85..6f9fb8680459 100644 --- a/core/lib/state/src/in_memory.rs +++ b/core/lib/state/src/in_memory.rs @@ -1,6 +1,5 @@ use std::collections::{hash_map::Entry, BTreeMap, HashMap}; -use crate::ReadStorage; use zksync_types::{ block::DeployedContract, get_code_key, get_known_code_key, get_system_context_init_logs, system_contracts::get_system_smart_contracts, L2ChainId, StorageKey, StorageLog, @@ -8,6 +7,8 @@ use zksync_types::{ }; use zksync_utils::u256_to_h256; +use crate::ReadStorage; + /// Network ID we use by default for in memory storage. pub const IN_MEMORY_STORAGE_DEFAULT_NETWORK_ID: u32 = 270; diff --git a/core/lib/state/src/postgres/metrics.rs b/core/lib/state/src/postgres/metrics.rs index 33e5664bb2bc..18fb54cdfa37 100644 --- a/core/lib/state/src/postgres/metrics.rs +++ b/core/lib/state/src/postgres/metrics.rs @@ -1,9 +1,9 @@ //! Metrics for `PostgresStorage`. -use vise::{Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Gauge, Histogram, Metrics}; - use std::time::Duration; +use vise::{Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Gauge, Histogram, Metrics}; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelSet, EncodeLabelValue)] #[metrics(label = "stage", rename_all = "snake_case")] pub(super) enum ValuesUpdateStage { diff --git a/core/lib/state/src/postgres/mod.rs b/core/lib/state/src/postgres/mod.rs index 8cc69f7bbbd1..7208877abb3f 100644 --- a/core/lib/state/src/postgres/mod.rs +++ b/core/lib/state/src/postgres/mod.rs @@ -1,23 +1,22 @@ -use tokio::{runtime::Handle, sync::mpsc}; - use std::{ mem, sync::{Arc, RwLock}, }; +use tokio::{runtime::Handle, sync::mpsc}; use zksync_dal::{ConnectionPool, StorageProcessor}; use zksync_types::{L1BatchNumber, MiniblockNumber, StorageKey, StorageValue, H256}; -mod metrics; -#[cfg(test)] -mod tests; - use self::metrics::{Method, ValuesUpdateStage, CACHE_METRICS, STORAGE_METRICS}; use crate::{ cache::{Cache, CacheValue}, ReadStorage, }; +mod metrics; +#[cfg(test)] +mod tests; + /// Type alias for smart contract source code cache. type FactoryDepsCache = Cache>; diff --git a/core/lib/state/src/postgres/tests.rs b/core/lib/state/src/postgres/tests.rs index 213360bb73de..6514da136d56 100644 --- a/core/lib/state/src/postgres/tests.rs +++ b/core/lib/state/src/postgres/tests.rs @@ -1,13 +1,12 @@ //! Tests for `PostgresStorage`. +use std::{collections::HashMap, mem}; + use rand::{ + rngs::StdRng, seq::{IteratorRandom, SliceRandom}, Rng, SeedableRng, }; - -use rand::rngs::StdRng; -use std::{collections::HashMap, mem}; - use zksync_dal::ConnectionPool; use zksync_types::StorageLog; diff --git a/core/lib/state/src/rocksdb/metrics.rs b/core/lib/state/src/rocksdb/metrics.rs index 81b035811d5f..997f4b42ed37 100644 --- a/core/lib/state/src/rocksdb/metrics.rs +++ b/core/lib/state/src/rocksdb/metrics.rs @@ -1,9 +1,9 @@ //! Metrics for `RocksdbStorage`. -use vise::{Buckets, Gauge, Histogram, Metrics}; - use std::time::Duration; +use vise::{Buckets, Gauge, Histogram, Metrics}; + #[derive(Debug, Metrics)] #[metrics(prefix = "server_state_keeper_secondary_storage")] pub(super) struct RocksdbStorageMetrics { diff --git a/core/lib/state/src/rocksdb/mod.rs b/core/lib/state/src/rocksdb/mod.rs index e3748f3acfdf..96d227271442 100644 --- a/core/lib/state/src/rocksdb/mod.rs +++ b/core/lib/state/src/rocksdb/mod.rs @@ -19,19 +19,19 @@ //! | Contracts | address (20 bytes) | `Vec` | Contract contents | //! | Factory deps | hash (32 bytes) | `Vec` | Bytecodes for new contracts that a certain contract may deploy. | -use itertools::{Either, Itertools}; use std::{collections::HashMap, convert::TryInto, mem, path::Path, time::Instant}; +use itertools::{Either, Itertools}; use zksync_dal::StorageProcessor; use zksync_storage::{db::NamedColumnFamily, RocksDB}; use zksync_types::{L1BatchNumber, StorageKey, StorageValue, H256, U256}; use zksync_utils::{h256_to_u256, u256_to_h256}; -mod metrics; - use self::metrics::METRICS; use crate::{InMemoryStorage, ReadStorage}; +mod metrics; + fn serialize_block_number(block_number: u32) -> [u8; 4] { block_number.to_le_bytes() } @@ -506,13 +506,13 @@ impl ReadStorage for RocksdbStorage { #[cfg(test)] mod tests { use tempfile::TempDir; + use zksync_dal::ConnectionPool; + use zksync_types::{MiniblockNumber, StorageLog}; use super::*; use crate::test_utils::{ create_l1_batch, create_miniblock, gen_storage_logs, prepare_postgres, }; - use zksync_dal::ConnectionPool; - use zksync_types::{MiniblockNumber, StorageLog}; #[tokio::test] async fn rocksdb_storage_basics() { diff --git a/core/lib/state/src/shadow_storage.rs b/core/lib/state/src/shadow_storage.rs index dea713ba40cb..0a2bd0fa43ee 100644 --- a/core/lib/state/src/shadow_storage.rs +++ b/core/lib/state/src/shadow_storage.rs @@ -1,7 +1,7 @@ use vise::{Counter, Metrics}; +use zksync_types::{L1BatchNumber, StorageKey, StorageValue, H256}; use crate::ReadStorage; -use zksync_types::{L1BatchNumber, StorageKey, StorageValue, H256}; #[derive(Debug, Metrics)] #[metrics(prefix = "shadow_storage")] diff --git a/core/lib/state/src/storage_view.rs b/core/lib/state/src/storage_view.rs index 8476be78aa9c..543b41bc6576 100644 --- a/core/lib/state/src/storage_view.rs +++ b/core/lib/state/src/storage_view.rs @@ -1,14 +1,15 @@ -use std::cell::RefCell; -use std::rc::Rc; use std::{ + cell::RefCell, collections::HashMap, fmt, mem, + rc::Rc, time::{Duration, Instant}, }; -use crate::{ReadStorage, WriteStorage}; use zksync_types::{witness_block_state::WitnessBlockState, StorageKey, StorageValue, H256}; +use crate::{ReadStorage, WriteStorage}; + /// Metrics for [`StorageView`]. #[derive(Debug, Default, Clone, Copy)] pub struct StorageViewMetrics { @@ -204,9 +205,10 @@ impl WriteStorage for StorageView { #[cfg(test)] mod test { + use zksync_types::{AccountTreeId, Address, H256}; + use super::*; use crate::InMemoryStorage; - use zksync_types::{AccountTreeId, Address, H256}; #[test] fn test_storage_access() { diff --git a/core/lib/state/src/test_utils.rs b/core/lib/state/src/test_utils.rs index b9a9d81fc547..3a100c505698 100644 --- a/core/lib/state/src/test_utils.rs +++ b/core/lib/state/src/test_utils.rs @@ -1,5 +1,7 @@ //! Shared utils for unit tests. +use std::ops; + use zksync_dal::StorageProcessor; use zksync_types::{ block::{BlockGasCount, L1BatchHeader, MiniblockHeader}, @@ -7,8 +9,6 @@ use zksync_types::{ StorageLog, H256, }; -use std::ops; - pub(crate) async fn prepare_postgres(conn: &mut StorageProcessor<'_>) { if conn.blocks_dal().is_genesis_needed().await.unwrap() { conn.protocol_versions_dal() diff --git a/core/lib/state/src/witness.rs b/core/lib/state/src/witness.rs index 72aab8bbe6ee..50e2d9b54076 100644 --- a/core/lib/state/src/witness.rs +++ b/core/lib/state/src/witness.rs @@ -1,9 +1,8 @@ use vise::{Counter, Metrics}; +use zksync_types::{witness_block_state::WitnessBlockState, StorageKey, StorageValue, H256}; use crate::ReadStorage; -use zksync_types::{witness_block_state::WitnessBlockState, StorageKey, StorageValue, H256}; - #[derive(Debug, Metrics)] #[metrics(prefix = "witness_storage")] struct WitnessStorageMetrics { diff --git a/core/lib/storage/src/db.rs b/core/lib/storage/src/db.rs index 617d14d272d0..f6237d49950a 100644 --- a/core/lib/storage/src/db.rs +++ b/core/lib/storage/src/db.rs @@ -1,8 +1,3 @@ -use rocksdb::{ - properties, BlockBasedOptions, Cache, ColumnFamily, ColumnFamilyDescriptor, DBPinnableSlice, - Direction, IteratorMode, Options, PrefixRange, ReadOptions, WriteOptions, DB, -}; - use std::{ collections::{HashMap, HashSet}, ffi::CStr, @@ -15,6 +10,11 @@ use std::{ time::{Duration, Instant}, }; +use rocksdb::{ + properties, BlockBasedOptions, Cache, ColumnFamily, ColumnFamilyDescriptor, DBPinnableSlice, + Direction, IteratorMode, Options, PrefixRange, ReadOptions, WriteOptions, DB, +}; + use crate::metrics::{RocksdbLabels, RocksdbSizeMetrics, METRICS}; /// Number of active RocksDB instances used to determine if it's safe to exit current process. diff --git a/core/lib/storage/src/metrics.rs b/core/lib/storage/src/metrics.rs index 928e735a30ca..47b0a52ee986 100644 --- a/core/lib/storage/src/metrics.rs +++ b/core/lib/storage/src/metrics.rs @@ -1,14 +1,14 @@ //! General-purpose RocksDB metrics. All metrics code in the crate should be in this module. -use once_cell::sync::Lazy; -use vise::{Buckets, Collector, Counter, EncodeLabelSet, Family, Gauge, Histogram, Metrics, Unit}; - use std::{ collections::HashMap, sync::{Mutex, Weak}, time::Duration, }; +use once_cell::sync::Lazy; +use vise::{Buckets, Collector, Counter, EncodeLabelSet, Family, Gauge, Histogram, Metrics, Unit}; + use crate::db::RocksDBInner; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelSet)] diff --git a/core/lib/test_account/src/lib.rs b/core/lib/test_account/src/lib.rs index 00764df6dc4b..5a84c84f4f52 100644 --- a/core/lib/test_account/src/lib.rs +++ b/core/lib/test_account/src/lib.rs @@ -1,21 +1,21 @@ use ethabi::Token; -use zksync_contracts::test_contracts::LoadnextContractExecutionParams; -use zksync_contracts::{deployer_contract, load_contract}; +use zksync_contracts::{ + deployer_contract, load_contract, test_contracts::LoadnextContractExecutionParams, +}; +use zksync_eth_signer::{raw_ethereum_tx::TransactionParameters, EthereumSigner, PrivateKeySigner}; use zksync_system_constants::{ CONTRACT_DEPLOYER_ADDRESS, MAX_GAS_PER_PUBDATA_BYTE, REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_BYTE, }; -use zksync_types::fee::Fee; -use zksync_types::l2::L2Tx; -use zksync_types::utils::deployed_address_create; use zksync_types::{ + fee::Fee, + l1::{OpProcessingType, PriorityQueueType}, + l2::L2Tx, + utils::deployed_address_create, Address, Execute, ExecuteTransactionCommon, L1TxCommonData, L2ChainId, Nonce, PackedEthSignature, PriorityOpId, Transaction, H256, U256, }; - -use zksync_eth_signer::{raw_ethereum_tx::TransactionParameters, EthereumSigner, PrivateKeySigner}; -use zksync_types::l1::{OpProcessingType, PriorityQueueType}; - use zksync_utils::bytecode::hash_bytecode; + pub const L1_TEST_GAS_PER_PUBDATA_BYTE: u32 = 800; const BASE_FEE: u64 = 2_000_000_000; diff --git a/core/lib/types/src/aggregated_operations.rs b/core/lib/types/src/aggregated_operations.rs index 8819460f2696..006eca562e71 100644 --- a/core/lib/types/src/aggregated_operations.rs +++ b/core/lib/types/src/aggregated_operations.rs @@ -1,12 +1,12 @@ -use codegen::serialize_proof; - use std::{fmt, ops, str::FromStr}; +use codegen::serialize_proof; use serde::{Deserialize, Serialize}; -use zkevm_test_harness::abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit; -use zkevm_test_harness::bellman::bn256::Bn256; -use zkevm_test_harness::bellman::plonk::better_better_cs::proof::Proof; -use zkevm_test_harness::witness::oracle::VmWitnessOracle; +use zkevm_test_harness::{ + abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit, + bellman::{bn256::Bn256, plonk::better_better_cs::proof::Proof}, + witness::oracle::VmWitnessOracle, +}; use zksync_basic_types::{ethabi::Token, L1BatchNumber}; use crate::{commitment::L1BatchWithMetadata, ProtocolVersionId, U256}; diff --git a/core/lib/types/src/api/mod.rs b/core/lib/types/src/api/mod.rs index 1ad54ce6d1ab..73fdf199debe 100644 --- a/core/lib/types/src/api/mod.rs +++ b/core/lib/types/src/api/mod.rs @@ -1,20 +1,21 @@ use chrono::{DateTime, Utc}; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use strum::Display; - use zksync_basic_types::{ web3::types::{Bytes, H160, H256, H64, U256, U64}, L1BatchNumber, }; use zksync_contracts::BaseSystemContractsHashes; -use crate::protocol_version::L1VerifierConfig; pub use crate::transaction_request::{ Eip712Meta, SerializationTransactionError, TransactionRequest, }; -use crate::vm_trace::{Call, CallType}; -use crate::web3::types::{AccessList, Index, H2048}; -use crate::{Address, MiniblockNumber, ProtocolVersionId}; +use crate::{ + protocol_version::L1VerifierConfig, + vm_trace::{Call, CallType}, + web3::types::{AccessList, Index, H2048}, + Address, MiniblockNumber, ProtocolVersionId, +}; pub mod en; diff --git a/core/lib/types/src/block.rs b/core/lib/types/src/block.rs index e61a56d2c916..0aa9d06711c4 100644 --- a/core/lib/types/src/block.rs +++ b/core/lib/types/src/block.rs @@ -1,10 +1,9 @@ -use serde::{Deserialize, Serialize}; -use zksync_system_constants::SYSTEM_BLOCK_INFO_BLOCK_NUMBER_MULTIPLIER; - use std::{fmt, ops}; +use serde::{Deserialize, Serialize}; use zksync_basic_types::{H2048, H256, U256}; use zksync_contracts::BaseSystemContractsHashes; +use zksync_system_constants::SYSTEM_BLOCK_INFO_BLOCK_NUMBER_MULTIPLIER; use crate::{ l2_to_l1_log::{SystemL2ToL1Log, UserL2ToL1Log}, diff --git a/core/lib/types/src/circuit.rs b/core/lib/types/src/circuit.rs index 940b4ecf273b..05f269c451ec 100644 --- a/core/lib/types/src/circuit.rs +++ b/core/lib/types/src/circuit.rs @@ -1,5 +1,4 @@ -use zkevm_test_harness::geometry_config::get_geometry_config; -use zkevm_test_harness::toolset::GeometryConfig; +use zkevm_test_harness::{geometry_config::get_geometry_config, toolset::GeometryConfig}; pub const LEAF_SPLITTING_FACTOR: usize = 50; pub const NODE_SPLITTING_FACTOR: usize = 48; diff --git a/core/lib/types/src/commitment.rs b/core/lib/types/src/commitment.rs index 7925a37d92fb..a0e3039a59a8 100644 --- a/core/lib/types/src/commitment.rs +++ b/core/lib/types/src/commitment.rs @@ -6,15 +6,14 @@ //! required for the rollup to execute L1 batches, it's needed for the proof generation and the Ethereum //! transactions, thus the calculations are done separately and asynchronously. -use serde::{Deserialize, Serialize}; -use zksync_utils::u256_to_h256; - use std::{collections::HashMap, convert::TryFrom}; +use serde::{Deserialize, Serialize}; use zksync_mini_merkle_tree::MiniMerkleTree; use zksync_system_constants::{ L2_TO_L1_LOGS_TREE_ROOT_KEY, STATE_DIFF_HASH_KEY, ZKPORTER_IS_AVAILABLE, }; +use zksync_utils::u256_to_h256; use crate::{ block::L1BatchHeader, @@ -666,12 +665,15 @@ mod tests { use serde::{Deserialize, Serialize}; use serde_with::serde_as; - use crate::commitment::{ - L1BatchAuxiliaryOutput, L1BatchCommitment, L1BatchMetaParameters, L1BatchPassThroughData, + use crate::{ + commitment::{ + L1BatchAuxiliaryOutput, L1BatchCommitment, L1BatchMetaParameters, + L1BatchPassThroughData, + }, + l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}, + writes::{InitialStorageWrite, RepeatedStorageWrite}, + H256, U256, }; - use crate::l2_to_l1_log::{L2ToL1Log, UserL2ToL1Log}; - use crate::writes::{InitialStorageWrite, RepeatedStorageWrite}; - use crate::{H256, U256}; #[serde_as] #[derive(Debug, Serialize, Deserialize)] diff --git a/core/lib/types/src/contract_verification_api.rs b/core/lib/types/src/contract_verification_api.rs index a7feb5116f2f..02a5bef727dc 100644 --- a/core/lib/types/src/contract_verification_api.rs +++ b/core/lib/types/src/contract_verification_api.rs @@ -6,9 +6,8 @@ use serde::{ Deserialize, Serialize, }; -use crate::{Address, Bytes}; - pub use crate::Execute as ExecuteData; +use crate::{Address, Bytes}; #[derive(Debug, Clone, Serialize)] #[serde(tag = "codeFormat", content = "sourceCode")] diff --git a/core/lib/types/src/eth_sender.rs b/core/lib/types/src/eth_sender.rs index 847662eaeaa5..6c8d268888e2 100644 --- a/core/lib/types/src/eth_sender.rs +++ b/core/lib/types/src/eth_sender.rs @@ -1,5 +1,4 @@ -use crate::aggregated_operations::AggregatedActionType; -use crate::{Address, Nonce, H256}; +use crate::{aggregated_operations::AggregatedActionType, Address, Nonce, H256}; #[derive(Clone)] pub struct EthTx { diff --git a/core/lib/types/src/event.rs b/core/lib/types/src/event.rs index 285567c89119..01561912cceb 100644 --- a/core/lib/types/src/event.rs +++ b/core/lib/types/src/event.rs @@ -1,3 +1,10 @@ +use std::fmt::Debug; + +use once_cell::sync::Lazy; +use serde::{Deserialize, Serialize}; +use zksync_basic_types::ethabi::Token; +use zksync_utils::{h256_to_account_address, u256_to_bytes_be, u256_to_h256}; + use crate::{ ethabi, l2_to_l1_log::L2ToL1Log, @@ -5,11 +12,6 @@ use crate::{ Address, L1BatchNumber, CONTRACT_DEPLOYER_ADDRESS, H256, KNOWN_CODES_STORAGE_ADDRESS, L1_MESSENGER_ADDRESS, U256, }; -use once_cell::sync::Lazy; -use serde::{Deserialize, Serialize}; -use std::fmt::Debug; -use zksync_basic_types::ethabi::Token; -use zksync_utils::{h256_to_account_address, u256_to_bytes_be, u256_to_h256}; #[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] pub struct VmEvent { @@ -348,13 +350,12 @@ mod tests { }; use zksync_utils::u256_to_h256; - use crate::VmEvent; - use super::{ extract_bytecode_publication_requests_from_l1_messenger, extract_l2tol1logs_from_l1_messenger, L1MessengerBytecodePublicationRequest, L1MessengerL2ToL1Log, }; + use crate::VmEvent; fn create_l2_to_l1_log_sent_value( tx_number: U256, diff --git a/core/lib/types/src/l1/mod.rs b/core/lib/types/src/l1/mod.rs index 75d7f71a8833..16ce192bf8b3 100644 --- a/core/lib/types/src/l1/mod.rs +++ b/core/lib/types/src/l1/mod.rs @@ -1,14 +1,15 @@ //! Definition of zkSync network priority operations: operations initiated from the L1. -use serde::{Deserialize, Serialize}; use std::convert::TryFrom; +use serde::{Deserialize, Serialize}; use zksync_basic_types::{ ethabi::{decode, ParamType, Token}, Address, L1BlockNumber, Log, PriorityOpId, H160, H256, U256, }; use zksync_utils::u256_to_account_address; +use super::Transaction; use crate::{ helpers::unix_timestamp_ms, l1::error::L1TxParseError, @@ -18,8 +19,6 @@ use crate::{ ExecuteTransactionCommon, PRIORITY_OPERATION_L2_TX_TYPE, PROTOCOL_UPGRADE_TX_TYPE, }; -use super::Transaction; - pub mod error; #[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Copy)] diff --git a/core/lib/types/src/l2/mod.rs b/core/lib/types/src/l2/mod.rs index 4c0632c5553a..61a505909b2b 100644 --- a/core/lib/types/src/l2/mod.rs +++ b/core/lib/types/src/l2/mod.rs @@ -2,26 +2,25 @@ use std::convert::TryFrom; use num_enum::TryFromPrimitive; use rlp::{Rlp, RlpStream}; +use serde::{Deserialize, Serialize}; use self::error::SignError; -use crate::transaction_request::PaymasterParams; -use crate::LEGACY_TX_TYPE; - use crate::{ - api, tx::primitives::PackedEthSignature, tx::Execute, web3::types::U64, Address, Bytes, - EIP712TypedStructure, Eip712Domain, ExecuteTransactionCommon, InputData, L2ChainId, Nonce, - StructBuilder, Transaction, EIP_1559_TX_TYPE, EIP_2930_TX_TYPE, EIP_712_TX_TYPE, H256, - PRIORITY_OPERATION_L2_TX_TYPE, PROTOCOL_UPGRADE_TX_TYPE, U256, + api, + api::TransactionRequest, + fee::{encoding_len, Fee}, + helpers::unix_timestamp_ms, + transaction_request::PaymasterParams, + tx::{primitives::PackedEthSignature, Execute}, + web3::types::U64, + Address, Bytes, EIP712TypedStructure, Eip712Domain, ExecuteTransactionCommon, InputData, + L2ChainId, Nonce, StructBuilder, Transaction, EIP_1559_TX_TYPE, EIP_2930_TX_TYPE, + EIP_712_TX_TYPE, H256, LEGACY_TX_TYPE, PRIORITY_OPERATION_L2_TX_TYPE, PROTOCOL_UPGRADE_TX_TYPE, + U256, }; -use serde::{Deserialize, Serialize}; - pub mod error; -use crate::api::TransactionRequest; -use crate::fee::{encoding_len, Fee}; -use crate::helpers::unix_timestamp_ms; - #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive)] #[repr(u32)] pub enum TransactionType { @@ -463,13 +462,12 @@ impl EIP712TypedStructure for L2Tx { mod tests { use zksync_basic_types::{Nonce, U256}; + use super::{L2Tx, TransactionType}; use crate::{ api::TransactionRequest, fee::Fee, transaction_request::PaymasterParams, Execute, L2TxCommonData, }; - use super::{L2Tx, TransactionType}; - #[test] fn test_correct_l2_tx_transaction_request_conversion() { // It is a random valid signature diff --git a/core/lib/types/src/l2_to_l1_log.rs b/core/lib/types/src/l2_to_l1_log.rs index 670a2b22e81f..335e6e740be5 100644 --- a/core/lib/types/src/l2_to_l1_log.rs +++ b/core/lib/types/src/l2_to_l1_log.rs @@ -1,10 +1,10 @@ -use crate::commitment::SerializeCommitment; -use crate::{Address, H256}; use serde::{Deserialize, Serialize}; use zk_evm::reference_impls::event_sink::EventMessage; use zk_evm_1_4_0::reference_impls::event_sink::EventMessage as EventMessage_1_4_0; use zksync_utils::u256_to_h256; +use crate::{commitment::SerializeCommitment, Address, H256}; + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, Eq)] pub struct L2ToL1Log { pub shard_id: u8, @@ -94,11 +94,12 @@ impl From for L2ToL1Log { #[cfg(test)] mod tests { - use super::L2ToL1Log; use zksync_basic_types::U256; use zksync_system_constants::L1_MESSENGER_ADDRESS; use zksync_utils::u256_to_h256; + use super::L2ToL1Log; + #[test] fn l2_to_l1_log_to_bytes() { let expected_log_bytes = [ diff --git a/core/lib/types/src/lib.rs b/core/lib/types/src/lib.rs index 4715a2f86dac..4574824b37f8 100644 --- a/core/lib/types/src/lib.rs +++ b/core/lib/types/src/lib.rs @@ -5,33 +5,32 @@ #![allow(clippy::upper_case_acronyms, clippy::derive_partial_eq_without_eq)] +use std::{fmt, fmt::Debug}; + use fee::encoding_len; use serde::{Deserialize, Serialize}; -use std::{fmt, fmt::Debug}; pub use crate::{Nonce, H256, U256, U64}; pub type SerialId = u64; -use crate::l2::TransactionType; -use crate::protocol_version::ProtocolUpgradeTxCommonData; pub use event::{VmEvent, VmEventGroupKey}; pub use l1::L1TxCommonData; pub use l2::L2TxCommonData; pub use protocol_version::{ProtocolUpgrade, ProtocolVersion, ProtocolVersionId}; pub use storage::*; -pub use tx::primitives::*; -pub use tx::Execute; +pub use tx::{primitives::*, Execute}; pub use vm_version::VmVersion; pub use zk_evm::{ aux_structures::{LogQuery, Timestamp}, reference_impls::event_sink::EventMessage, zkevm_opcode_defs::FarCallOpcode, }; - pub use zkevm_test_harness; pub use zksync_basic_types::*; +use crate::{l2::TransactionType, protocol_version::ProtocolUpgradeTxCommonData}; + pub mod aggregated_operations; pub mod block; pub mod circuit; diff --git a/core/lib/types/src/priority_op_onchain_data.rs b/core/lib/types/src/priority_op_onchain_data.rs index a729aa27bf41..559bb9963881 100644 --- a/core/lib/types/src/priority_op_onchain_data.rs +++ b/core/lib/types/src/priority_op_onchain_data.rs @@ -1,7 +1,7 @@ -use serde::{Deserialize, Serialize}; - use std::cmp::Ordering; +use serde::{Deserialize, Serialize}; + use crate::{ l1::{OpProcessingType, PriorityQueueType}, H256, U256, diff --git a/core/lib/types/src/proofs.rs b/core/lib/types/src/proofs.rs index b28b81b79fbf..0067552c8298 100644 --- a/core/lib/types/src/proofs.rs +++ b/core/lib/types/src/proofs.rs @@ -1,25 +1,25 @@ -use std::convert::{TryFrom, TryInto}; -use std::fmt::Debug; -use std::net::IpAddr; -use std::ops::Add; -use std::str::FromStr; +use std::{ + convert::{TryFrom, TryInto}, + fmt::Debug, + net::IpAddr, + ops::Add, + str::FromStr, +}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, Bytes}; -use zkevm_test_harness::abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit; -use zkevm_test_harness::bellman::bn256::Bn256; -use zkevm_test_harness::bellman::plonk::better_better_cs::proof::Proof; -use zkevm_test_harness::encodings::{recursion_request::RecursionRequest, QueueSimulator}; -use zkevm_test_harness::witness::full_block_artifact::{ - BlockBasicCircuits, BlockBasicCircuitsPublicInputs, -}; -use zkevm_test_harness::witness::oracle::VmWitnessOracle; use zkevm_test_harness::{ + abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit, + bellman::{bn256::Bn256, plonk::better_better_cs::proof::Proof}, + encodings::{recursion_request::RecursionRequest, QueueSimulator}, + witness::{ + full_block_artifact::{BlockBasicCircuits, BlockBasicCircuitsPublicInputs}, + oracle::VmWitnessOracle, + }, LeafAggregationOutputDataWitness, NodeAggregationOutputDataWitness, SchedulerCircuitInstanceWitness, }; - use zksync_basic_types::{L1BatchNumber, H256, U256}; const HASH_LEN: usize = H256::len_bytes(); diff --git a/core/lib/types/src/protocol_version.rs b/core/lib/types/src/protocol_version.rs index 09a722c72cdb..b855cb92fdff 100644 --- a/core/lib/types/src/protocol_version.rs +++ b/core/lib/types/src/protocol_version.rs @@ -1,3 +1,10 @@ +use std::convert::{TryFrom, TryInto}; + +use num_enum::TryFromPrimitive; +use serde::{Deserialize, Serialize}; +use zksync_contracts::BaseSystemContractsHashes; +use zksync_utils::u256_to_account_address; + use crate::{ ethabi::{decode, encode, ParamType, Token}, helpers::unix_timestamp_ms, @@ -8,11 +15,6 @@ use crate::{ Address, Execute, ExecuteTransactionCommon, Log, Transaction, TransactionType, VmVersion, H256, PROTOCOL_UPGRADE_TX_TYPE, U256, }; -use num_enum::TryFromPrimitive; -use serde::{Deserialize, Serialize}; -use std::convert::{TryFrom, TryInto}; -use zksync_contracts::BaseSystemContractsHashes; -use zksync_utils::u256_to_account_address; #[repr(u16)] #[derive( diff --git a/core/lib/types/src/prover_server_api/mod.rs b/core/lib/types/src/prover_server_api/mod.rs index dc226f11d265..fdbbd57624f8 100644 --- a/core/lib/types/src/prover_server_api/mod.rs +++ b/core/lib/types/src/prover_server_api/mod.rs @@ -1,10 +1,11 @@ use serde::{Deserialize, Serialize}; - use zksync_basic_types::L1BatchNumber; -use crate::aggregated_operations::L1BatchProofForL1; -use crate::proofs::PrepareBasicCircuitsJob; -use crate::protocol_version::{FriProtocolVersionId, L1VerifierConfig}; +use crate::{ + aggregated_operations::L1BatchProofForL1, + proofs::PrepareBasicCircuitsJob, + protocol_version::{FriProtocolVersionId, L1VerifierConfig}, +}; #[derive(Debug, Serialize, Deserialize)] pub struct ProofGenerationData { diff --git a/core/lib/types/src/storage/log.rs b/core/lib/types/src/storage/log.rs index aa295a2badee..11756f7175ae 100644 --- a/core/lib/types/src/storage/log.rs +++ b/core/lib/types/src/storage/log.rs @@ -1,7 +1,6 @@ -use serde::{Deserialize, Serialize}; - use std::mem; +use serde::{Deserialize, Serialize}; use zk_evm::aux_structures::{LogQuery, Timestamp}; use zksync_basic_types::AccountTreeId; use zksync_utils::u256_to_h256; diff --git a/core/lib/types/src/storage/witness_block_state.rs b/core/lib/types/src/storage/witness_block_state.rs index 2ba57a9aea0a..63ee1ba1c566 100644 --- a/core/lib/types/src/storage/witness_block_state.rs +++ b/core/lib/types/src/storage/witness_block_state.rs @@ -1,7 +1,9 @@ -use crate::{StorageKey, StorageValue}; -use serde::{Deserialize, Serialize}; use std::collections::HashMap; +use serde::{Deserialize, Serialize}; + +use crate::{StorageKey, StorageValue}; + /// Storage data used during Witness Generation. #[derive(Debug, Default, Serialize, Deserialize)] pub struct WitnessBlockState { diff --git a/core/lib/types/src/storage/writes/compression.rs b/core/lib/types/src/storage/writes/compression.rs index a325801b8a83..cd0a174fa76f 100644 --- a/core/lib/types/src/storage/writes/compression.rs +++ b/core/lib/types/src/storage/writes/compression.rs @@ -210,9 +210,10 @@ pub fn compress_with_best_strategy(prev_value: U256, new_value: U256) -> Vec #[cfg(test)] mod tests { - use super::*; use std::ops::{Add, BitAnd, Shr, Sub}; + use super::*; + #[test] fn test_compress_addition() { let initial_val = U256::from(255438218); diff --git a/core/lib/types/src/storage/writes/mod.rs b/core/lib/types/src/storage/writes/mod.rs index 54393f417856..22400964bf46 100644 --- a/core/lib/types/src/storage/writes/mod.rs +++ b/core/lib/types/src/storage/writes/mod.rs @@ -1,10 +1,10 @@ use std::convert::TryInto; -use crate::H256; use serde::{Deserialize, Serialize}; use zksync_basic_types::{Address, U256}; pub(crate) use self::compression::{compress_with_best_strategy, COMPRESSION_VERSION_NUMBER}; +use crate::H256; pub mod compression; @@ -184,12 +184,13 @@ fn prepend_header(compressed_state_diffs: Vec) -> Vec { #[cfg(test)] mod tests { - use std::ops::{Add, Sub}; - use std::str::FromStr; + use std::{ + ops::{Add, Sub}, + str::FromStr, + }; use super::*; - use crate::commitment::serialize_commitments; - use crate::{H256, U256}; + use crate::{commitment::serialize_commitments, H256, U256}; #[test] fn calculate_hash_for_storage_writes() { diff --git a/core/lib/types/src/storage_writes_deduplicator.rs b/core/lib/types/src/storage_writes_deduplicator.rs index 42ce67e63757..14a5413ee6a0 100644 --- a/core/lib/types/src/storage_writes_deduplicator.rs +++ b/core/lib/types/src/storage_writes_deduplicator.rs @@ -2,9 +2,11 @@ use std::collections::HashMap; use zksync_utils::u256_to_h256; -use crate::tx::tx_execution_info::DeduplicatedWritesMetrics; -use crate::writes::compression::compress_with_best_strategy; -use crate::{AccountTreeId, StorageKey, StorageLogQuery, StorageLogQueryType, U256}; +use crate::{ + tx::tx_execution_info::DeduplicatedWritesMetrics, + writes::compression::compress_with_best_strategy, AccountTreeId, StorageKey, StorageLogQuery, + StorageLogQueryType, U256, +}; #[derive(Debug, Clone, Copy, PartialEq, Default)] pub struct ModifiedSlot { @@ -219,9 +221,8 @@ impl StorageWritesDeduplicator { mod tests { use zk_evm::aux_structures::{LogQuery, Timestamp}; - use crate::H160; - use super::*; + use crate::H160; fn storage_log_query( key: U256, diff --git a/core/lib/types/src/system_contracts.rs b/core/lib/types/src/system_contracts.rs index 430d8d4701dd..7e896b83bc22 100644 --- a/core/lib/types/src/system_contracts.rs +++ b/core/lib/types/src/system_contracts.rs @@ -1,5 +1,6 @@ use std::path::PathBuf; +use once_cell::sync::Lazy; use zksync_basic_types::{AccountTreeId, Address, U256}; use zksync_contracts::{read_sys_contract_bytecode, ContractLanguage, SystemContractsRepo}; use zksync_system_constants::{ @@ -13,7 +14,6 @@ use crate::{ L1_MESSENGER_ADDRESS, L2_ETH_TOKEN_ADDRESS, MSG_VALUE_SIMULATOR_ADDRESS, NONCE_HOLDER_ADDRESS, SHA256_PRECOMPILE_ADDRESS, SYSTEM_CONTEXT_ADDRESS, }; -use once_cell::sync::Lazy; // Note, that in the NONCE_HOLDER_ADDRESS's storage the nonces of accounts // are stored in the following form: diff --git a/core/lib/types/src/transaction_request.rs b/core/lib/types/src/transaction_request.rs index 85194902e323..e66c2495afe3 100644 --- a/core/lib/types/src/transaction_request.rs +++ b/core/lib/types/src/transaction_request.rs @@ -1,17 +1,15 @@ -// Built-in uses use std::convert::{TryFrom, TryInto}; -// External uses use rlp::{DecoderError, Rlp, RlpStream}; use serde::{Deserialize, Serialize}; use thiserror::Error; use zksync_basic_types::H256; - use zksync_system_constants::{MAX_GAS_PER_PUBDATA_BYTE, USED_BOOTLOADER_MEMORY_BYTES}; -use zksync_utils::bytecode::{hash_bytecode, validate_bytecode, InvalidBytecodeError}; -use zksync_utils::{concat_and_hash, u256_to_h256}; +use zksync_utils::{ + bytecode::{hash_bytecode, validate_bytecode, InvalidBytecodeError}, + concat_and_hash, u256_to_h256, +}; -// Local uses use super::{EIP_1559_TX_TYPE, EIP_2930_TX_TYPE, EIP_712_TX_TYPE}; use crate::{ fee::Fee, @@ -947,13 +945,14 @@ pub fn validate_factory_deps( #[cfg(test)] mod tests { + use secp256k1::SecretKey; + use super::*; use crate::web3::{ api::Namespace, transports::test::TestTransport, types::{TransactionParameters, H256, U256}, }; - use secp256k1::SecretKey; #[tokio::test] async fn decode_real_tx() { diff --git a/core/lib/types/src/tx/execute.rs b/core/lib/types/src/tx/execute.rs index e33dff694fe3..50340230cb9e 100644 --- a/core/lib/types/src/tx/execute.rs +++ b/core/lib/types/src/tx/execute.rs @@ -1,8 +1,9 @@ -use crate::{web3::ethabi, Address, EIP712TypedStructure, StructBuilder, H256, U256}; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; use zksync_utils::ZeroPrefixHexSerde; +use crate::{web3::ethabi, Address, EIP712TypedStructure, StructBuilder, H256, U256}; + /// `Execute` transaction executes a previously deployed smart contract in the L2 rollup. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] diff --git a/core/lib/types/src/tx/mod.rs b/core/lib/types/src/tx/mod.rs index 71f188f3217e..1371fa74ee76 100644 --- a/core/lib/types/src/tx/mod.rs +++ b/core/lib/types/src/tx/mod.rs @@ -5,19 +5,18 @@ //! with metadata (such as fees and/or signatures) for L1 and L2 separately. use std::fmt::Debug; + use zksync_basic_types::{Address, H256}; use zksync_utils::bytecode::CompressedBytecodeInfo; +use self::tx_execution_info::TxExecutionStatus; +pub use self::{execute::Execute, tx_execution_info::ExecutionMetrics}; +use crate::{vm_trace::Call, Transaction}; + pub mod execute; pub mod primitives; pub mod tx_execution_info; -pub use self::execute::Execute; -use crate::vm_trace::Call; -use crate::Transaction; -pub use tx_execution_info::ExecutionMetrics; -use tx_execution_info::TxExecutionStatus; - #[derive(Debug, Clone, PartialEq)] pub struct TransactionExecutionResult { pub transaction: Transaction, diff --git a/core/lib/types/src/tx/primitives/eip712_signature/member_types.rs b/core/lib/types/src/tx/primitives/eip712_signature/member_types.rs index cc4906ef7e8f..aecece572dda 100644 --- a/core/lib/types/src/tx/primitives/eip712_signature/member_types.rs +++ b/core/lib/types/src/tx/primitives/eip712_signature/member_types.rs @@ -1,9 +1,10 @@ -use crate::tx::primitives::eip712_signature::typed_structure::{ - EncodedStructureMember, StructMember, -}; -use crate::web3::signing::keccak256; use zksync_basic_types::{Address, H256, U256}; +use crate::{ + tx::primitives::eip712_signature::typed_structure::{EncodedStructureMember, StructMember}, + web3::signing::keccak256, +}; + impl StructMember for String { const MEMBER_TYPE: &'static str = "string"; const IS_REFERENCE_TYPE: bool = false; diff --git a/core/lib/types/src/tx/primitives/eip712_signature/struct_builder.rs b/core/lib/types/src/tx/primitives/eip712_signature/struct_builder.rs index f6189f504df3..2093042b9f7a 100644 --- a/core/lib/types/src/tx/primitives/eip712_signature/struct_builder.rs +++ b/core/lib/types/src/tx/primitives/eip712_signature/struct_builder.rs @@ -1,5 +1,6 @@ -use serde_json::Value; use std::collections::{BTreeMap, VecDeque}; + +use serde_json::Value; use zksync_basic_types::H256; use crate::tx::primitives::eip712_signature::typed_structure::{ diff --git a/core/lib/types/src/tx/primitives/eip712_signature/tests.rs b/core/lib/types/src/tx/primitives/eip712_signature/tests.rs index 70ae415531c9..8bfd14b45c49 100644 --- a/core/lib/types/src/tx/primitives/eip712_signature/tests.rs +++ b/core/lib/types/src/tx/primitives/eip712_signature/tests.rs @@ -1,13 +1,20 @@ -use crate::tx::primitives::eip712_signature::{ - struct_builder::StructBuilder, - typed_structure::{EIP712TypedStructure, Eip712Domain}, -}; -use crate::tx::primitives::{eip712_signature::utils::get_eip712_json, PackedEthSignature}; -use crate::web3::signing::keccak256; -use serde::Serialize; use std::str::FromStr; + +use serde::Serialize; use zksync_basic_types::{Address, H256, U256}; +use crate::{ + tx::primitives::{ + eip712_signature::{ + struct_builder::StructBuilder, + typed_structure::{EIP712TypedStructure, Eip712Domain}, + utils::get_eip712_json, + }, + PackedEthSignature, + }, + web3::signing::keccak256, +}; + #[derive(Clone, Serialize)] struct Person { name: String, diff --git a/core/lib/types/src/tx/primitives/eip712_signature/typed_structure.rs b/core/lib/types/src/tx/primitives/eip712_signature/typed_structure.rs index 28e9d27f0a68..daf1c9698ee1 100644 --- a/core/lib/types/src/tx/primitives/eip712_signature/typed_structure.rs +++ b/core/lib/types/src/tx/primitives/eip712_signature/typed_structure.rs @@ -1,11 +1,11 @@ -use crate::web3::signing::keccak256; use serde::{Deserialize, Serialize}; use serde_json::Value; -use crate::tx::primitives::eip712_signature::struct_builder::{ - EncodeBuilder, StructBuilder, TypeBuilder, +use crate::{ + tx::primitives::eip712_signature::struct_builder::{EncodeBuilder, StructBuilder, TypeBuilder}, + web3::signing::keccak256, + L2ChainId, H256, U256, }; -use crate::{L2ChainId, H256, U256}; #[derive(Debug, Clone)] pub struct EncodedStructureMember { diff --git a/core/lib/types/src/tx/primitives/eip712_signature/utils.rs b/core/lib/types/src/tx/primitives/eip712_signature/utils.rs index 57db78943212..f338c017e2b5 100644 --- a/core/lib/types/src/tx/primitives/eip712_signature/utils.rs +++ b/core/lib/types/src/tx/primitives/eip712_signature/utils.rs @@ -1,7 +1,8 @@ +use serde_json::{Map, Value}; + use crate::tx::primitives::eip712_signature::typed_structure::{ EIP712TypedStructure, Eip712Domain, }; -use serde_json::{Map, Value}; /// Formats the data that needs to be signed in json according to the standard eip-712. /// Compatible with `eth_signTypedData` RPC call. diff --git a/core/lib/types/src/tx/primitives/packed_eth_signature.rs b/core/lib/types/src/tx/primitives/packed_eth_signature.rs index 32564829ad88..c165f6a36b21 100644 --- a/core/lib/types/src/tx/primitives/packed_eth_signature.rs +++ b/core/lib/types/src/tx/primitives/packed_eth_signature.rs @@ -1,6 +1,3 @@ -use crate::tx::primitives::eip712_signature::typed_structure::{ - EIP712TypedStructure, Eip712Domain, -}; use ethereum_types_old::H256 as ParityCryptoH256; use parity_crypto::{ publickey::{ @@ -14,6 +11,10 @@ use thiserror::Error; use zksync_basic_types::{Address, H256}; use zksync_utils::ZeroPrefixHexSerde; +use crate::tx::primitives::eip712_signature::typed_structure::{ + EIP712TypedStructure, Eip712Domain, +}; + /// Struct used for working with Ethereum signatures created using eth_sign (using geth, ethers.js, etc) /// message is serialized as 65 bytes long `0x` prefixed string. /// diff --git a/core/lib/types/src/tx/tx_execution_info.rs b/core/lib/types/src/tx/tx_execution_info.rs index 0f72172f529c..d19757ee970b 100644 --- a/core/lib/types/src/tx/tx_execution_info.rs +++ b/core/lib/types/src/tx/tx_execution_info.rs @@ -1,14 +1,15 @@ -use crate::fee::TransactionExecutionMetrics; -use crate::l2_to_l1_log::L2ToL1Log; +use std::ops::{Add, AddAssign}; + use crate::{ commitment::SerializeCommitment, + fee::TransactionExecutionMetrics, + l2_to_l1_log::L2ToL1Log, writes::{ InitialStorageWrite, RepeatedStorageWrite, BYTES_PER_DERIVED_KEY, BYTES_PER_ENUMERATION_INDEX, }, ProtocolVersionId, }; -use std::ops::{Add, AddAssign}; #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum TxExecutionStatus { diff --git a/core/lib/types/src/utils.rs b/core/lib/types/src/utils.rs index 617179c4936b..7828f2d1262b 100644 --- a/core/lib/types/src/utils.rs +++ b/core/lib/types/src/utils.rs @@ -1,11 +1,11 @@ -use crate::system_contracts::DEPLOYMENT_NONCE_INCREMENT; -use crate::L2_ETH_TOKEN_ADDRESS; -use crate::{web3::signing::keccak256, AccountTreeId, StorageKey, U256}; - use zksync_basic_types::{Address, H256}; - use zksync_utils::{address_to_h256, u256_to_h256}; +use crate::{ + system_contracts::DEPLOYMENT_NONCE_INCREMENT, web3::signing::keccak256, AccountTreeId, + StorageKey, L2_ETH_TOKEN_ADDRESS, U256, +}; + /// Transforms the *full* account nonce into an *account* nonce. /// Full nonce is a composite one: it includes both account nonce (number of transactions /// initiated by the account) and deployer nonce (number of smart contracts deployed by the @@ -79,10 +79,11 @@ pub fn deployed_address_create(sender: Address, deploy_nonce: U256) -> Address { #[cfg(test)] mod tests { + use std::str::FromStr; + use crate::{ utils::storage_key_for_standard_token_balance, AccountTreeId, Address, StorageKey, H256, }; - use std::str::FromStr; #[test] fn test_storage_key_for_eth_token() { diff --git a/core/lib/types/src/vk_transform.rs b/core/lib/types/src/vk_transform.rs index dfa022fb7c1b..b19fdaef6927 100644 --- a/core/lib/types/src/vk_transform.rs +++ b/core/lib/types/src/vk_transform.rs @@ -1,5 +1,5 @@ -use crate::{ethabi::Token, H256}; use std::str::FromStr; + use zkevm_test_harness::{ abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit, bellman::{ @@ -14,6 +14,8 @@ use zkevm_test_harness::{ }, }; +use crate::{ethabi::Token, H256}; + /// Calculates commitment for vk from L1 verifier contract. pub fn l1_vk_commitment(token: Token) -> H256 { let vk = vk_from_token(token); diff --git a/core/lib/types/src/vm_trace.rs b/core/lib/types/src/vm_trace.rs index 6b37848dc5a7..d3a94d51fa5a 100644 --- a/core/lib/types/src/vm_trace.rs +++ b/core/lib/types/src/vm_trace.rs @@ -1,12 +1,16 @@ -use crate::{Address, U256}; +use std::{ + collections::{HashMap, HashSet}, + fmt, + fmt::Display, +}; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::collections::{HashMap, HashSet}; -use std::fmt; -use std::fmt::Display; use zk_evm::zkevm_opcode_defs::FarCallOpcode; use zksync_system_constants::BOOTLOADER_ADDRESS; use zksync_utils::u256_to_h256; +use crate::{Address, U256}; + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub enum VmTrace { ExecutionTrace(VmExecutionTrace), diff --git a/core/lib/utils/src/bytecode.rs b/core/lib/utils/src/bytecode.rs index 4a9d1cd2475b..c533642d240f 100644 --- a/core/lib/utils/src/bytecode.rs +++ b/core/lib/utils/src/bytecode.rs @@ -1,8 +1,10 @@ +use std::{collections::HashMap, convert::TryInto}; + use itertools::Itertools; -use std::collections::HashMap; -use std::convert::TryInto; -use zksync_basic_types::ethabi::{encode, Token}; -use zksync_basic_types::H256; +use zksync_basic_types::{ + ethabi::{encode, Token}, + H256, +}; use crate::bytes_to_chunks; diff --git a/core/lib/utils/src/convert.rs b/core/lib/utils/src/convert.rs index 973b28cc613a..cc4699448e67 100644 --- a/core/lib/utils/src/convert.rs +++ b/core/lib/utils/src/convert.rs @@ -1,3 +1,5 @@ +use std::convert::TryInto; + use bigdecimal::BigDecimal; use num::{ bigint::ToBigInt, @@ -5,7 +7,6 @@ use num::{ traits::{sign::Signed, Pow}, BigUint, }; -use std::convert::TryInto; use zksync_basic_types::{Address, H256, U256}; pub fn u256_to_big_decimal(value: U256) -> BigDecimal { @@ -170,10 +171,12 @@ pub fn u256_to_bytes_be(value: &U256) -> Vec { #[cfg(test)] mod test { - use super::*; - use num::BigInt; use std::str::FromStr; + use num::BigInt; + + use super::*; + #[test] fn test_ratio_to_big_decimal() { let ratio = Ratio::from_integer(BigUint::from(0u32)); diff --git a/core/lib/utils/src/http_with_retries.rs b/core/lib/utils/src/http_with_retries.rs index 61742769fd65..15973ee6b2a0 100644 --- a/core/lib/utils/src/http_with_retries.rs +++ b/core/lib/utils/src/http_with_retries.rs @@ -1,5 +1,4 @@ -use reqwest::header::HeaderMap; -use reqwest::{Client, Error, Method, Response}; +use reqwest::{header::HeaderMap, Client, Error, Method, Response}; use tokio::time::{sleep, Duration}; #[derive(Debug)] diff --git a/core/lib/utils/src/misc.rs b/core/lib/utils/src/misc.rs index 468e953f83bc..887413a6f45e 100644 --- a/core/lib/utils/src/misc.rs +++ b/core/lib/utils/src/misc.rs @@ -1,5 +1,4 @@ -use zksync_basic_types::web3::signing::keccak256; -use zksync_basic_types::{H256, U256}; +use zksync_basic_types::{web3::signing::keccak256, H256, U256}; pub const fn ceil_div(a: u64, b: u64) -> u64 { if a == 0 { diff --git a/core/lib/vlog/src/lib.rs b/core/lib/vlog/src/lib.rs index 2c6702ede960..1ea573148c4a 100644 --- a/core/lib/vlog/src/lib.rs +++ b/core/lib/vlog/src/lib.rs @@ -1,18 +1,14 @@ //! This module contains the observability subsystem. //! It is responsible for providing a centralized interface for consistent observability configuration. -use std::backtrace::Backtrace; -use std::borrow::Cow; -use std::panic::PanicInfo; +use std::{backtrace::Backtrace, borrow::Cow, panic::PanicInfo}; +// Temporary re-export of `sentry::capture_message` aiming to simplify the transition from `vlog` to using +// crates directly. +pub use sentry::{capture_message, Level as AlertLevel}; use sentry::{types::Dsn, ClientInitGuard}; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt}; -/// Temporary re-export of `sentry::capture_message` aiming to simplify the transition from `vlog` to using -/// crates directly. -pub use sentry::capture_message; -pub use sentry::Level as AlertLevel; - /// Specifies the format of the logs in stdout. #[derive(Debug, Clone, Copy, Default)] pub enum LogFormat { diff --git a/core/lib/web3_decl/src/namespaces/debug.rs b/core/lib/web3_decl/src/namespaces/debug.rs index 7db44f27527a..02e75e946b72 100644 --- a/core/lib/web3_decl/src/namespaces/debug.rs +++ b/core/lib/web3_decl/src/namespaces/debug.rs @@ -1,8 +1,10 @@ -use crate::types::H256; use jsonrpsee::{core::RpcResult, proc_macros::rpc}; +use zksync_types::{ + api::{BlockId, BlockNumber, DebugCall, ResultDebugCall, TracerConfig}, + transaction_request::CallRequest, +}; -use zksync_types::api::{BlockId, BlockNumber, DebugCall, ResultDebugCall, TracerConfig}; -use zksync_types::transaction_request::CallRequest; +use crate::types::H256; #[cfg_attr( all(feature = "client", feature = "server"), diff --git a/core/lib/web3_decl/src/namespaces/eth.rs b/core/lib/web3_decl/src/namespaces/eth.rs index f92f2a562392..861478bf4bff 100644 --- a/core/lib/web3_decl/src/namespaces/eth.rs +++ b/core/lib/web3_decl/src/namespaces/eth.rs @@ -1,20 +1,14 @@ -// External uses use jsonrpsee::{core::RpcResult, proc_macros::rpc}; - -// Workspace uses -use crate::types::{ - Block, Bytes, FeeHistory, Filter, FilterChanges, Index, Log, SyncState, TransactionReceipt, - U256, U64, -}; - use zksync_types::{ - api::Transaction, - api::{BlockIdVariant, BlockNumber, TransactionVariant}, + api::{BlockIdVariant, BlockNumber, Transaction, TransactionVariant}, transaction_request::CallRequest, Address, H256, }; -// Local uses +use crate::types::{ + Block, Bytes, FeeHistory, Filter, FilterChanges, Index, Log, SyncState, TransactionReceipt, + U256, U64, +}; #[cfg_attr( all(feature = "client", feature = "server"), diff --git a/core/lib/web3_decl/src/namespaces/mod.rs b/core/lib/web3_decl/src/namespaces/mod.rs index 996cb27267ce..26e610c16449 100644 --- a/core/lib/web3_decl/src/namespaces/mod.rs +++ b/core/lib/web3_decl/src/namespaces/mod.rs @@ -6,16 +6,13 @@ pub mod net; pub mod web3; pub mod zks; -// Server trait re-exports. -#[cfg(feature = "server")] -pub use self::{ - debug::DebugNamespaceServer, en::EnNamespaceServer, eth::EthNamespaceServer, - net::NetNamespaceServer, web3::Web3NamespaceServer, zks::ZksNamespaceServer, -}; - -// Client trait re-exports. #[cfg(feature = "client")] pub use self::{ debug::DebugNamespaceClient, en::EnNamespaceClient, eth::EthNamespaceClient, net::NetNamespaceClient, web3::Web3NamespaceClient, zks::ZksNamespaceClient, }; +#[cfg(feature = "server")] +pub use self::{ + debug::DebugNamespaceServer, en::EnNamespaceServer, eth::EthNamespaceServer, + net::NetNamespaceServer, web3::Web3NamespaceServer, zks::ZksNamespaceServer, +}; diff --git a/core/lib/web3_decl/src/namespaces/zks.rs b/core/lib/web3_decl/src/namespaces/zks.rs index d3bf43b9a973..e7ee1dffa9fa 100644 --- a/core/lib/web3_decl/src/namespaces/zks.rs +++ b/core/lib/web3_decl/src/namespaces/zks.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use bigdecimal::BigDecimal; use jsonrpsee::{core::RpcResult, proc_macros::rpc}; - use zksync_types::{ api::{ BlockDetails, BridgeAddresses, L1BatchDetails, L2ToL1LogProof, Proof, ProtocolVersion, diff --git a/core/lib/web3_decl/src/types.rs b/core/lib/web3_decl/src/types.rs index 7abe34637d67..61a3e10397c1 100644 --- a/core/lib/web3_decl/src/types.rs +++ b/core/lib/web3_decl/src/types.rs @@ -5,16 +5,15 @@ //! //! These "extensions" are required to provide more zkSync-specific information while remaining Web3-compilant. -use itertools::unfold; -use rlp::Rlp; -use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; - use core::{ convert::{TryFrom, TryInto}, fmt, marker::PhantomData, }; +use itertools::unfold; +use rlp::Rlp; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; pub use zksync_types::{ api::{Block, BlockNumber, Log, TransactionReceipt, TransactionRequest}, vm_trace::{ContractSourceDebugInfo, VmDebugTrace, VmExecutionStep}, @@ -350,9 +349,10 @@ pub enum PubSubResult { #[cfg(test)] mod tests { - use super::*; use zksync_types::api::{BlockId, BlockIdVariant}; + use super::*; + #[test] fn get_block_number_serde() { let test_vector = &[ diff --git a/core/lib/zksync_core/src/api_server/contract_verification/api_decl.rs b/core/lib/zksync_core/src/api_server/contract_verification/api_decl.rs index 5f9730d458e1..553f6f2ad45e 100644 --- a/core/lib/zksync_core/src/api_server/contract_verification/api_decl.rs +++ b/core/lib/zksync_core/src/api_server/contract_verification/api_decl.rs @@ -1,5 +1,4 @@ use actix_web::web; - use zksync_dal::connection::ConnectionPool; #[derive(Debug, Clone)] diff --git a/core/lib/zksync_core/src/api_server/contract_verification/api_impl.rs b/core/lib/zksync_core/src/api_server/contract_verification/api_impl.rs index d107483db01d..81c9f7e264c6 100644 --- a/core/lib/zksync_core/src/api_server/contract_verification/api_impl.rs +++ b/core/lib/zksync_core/src/api_server/contract_verification/api_impl.rs @@ -3,7 +3,6 @@ use actix_web::{ HttpResponse, Result as ActixResult, }; use serde::Serialize; - use zksync_types::{contract_verification_api::VerificationIncomingRequest, Address}; use super::{api_decl::RestApi, metrics::METRICS}; diff --git a/core/lib/zksync_core/src/api_server/contract_verification/metrics.rs b/core/lib/zksync_core/src/api_server/contract_verification/metrics.rs index 1e114f68ff6a..4947e48b0943 100644 --- a/core/lib/zksync_core/src/api_server/contract_verification/metrics.rs +++ b/core/lib/zksync_core/src/api_server/contract_verification/metrics.rs @@ -1,9 +1,9 @@ //! Metrics for contract verification. -use vise::{Buckets, Histogram, LabeledFamily, Metrics}; - use std::time::Duration; +use vise::{Buckets, Histogram, LabeledFamily, Metrics}; + #[derive(Debug, Metrics)] #[metrics(prefix = "api_contract_verification")] pub(super) struct ContractVerificationMetrics { diff --git a/core/lib/zksync_core/src/api_server/contract_verification/mod.rs b/core/lib/zksync_core/src/api_server/contract_verification/mod.rs index 5b59fafa9178..a1bf980a49a1 100644 --- a/core/lib/zksync_core/src/api_server/contract_verification/mod.rs +++ b/core/lib/zksync_core/src/api_server/contract_verification/mod.rs @@ -1,22 +1,18 @@ use std::{net::SocketAddr, time::Duration}; use actix_cors::Cors; -use actix_web::{ - dev::Server, - {web, App, HttpResponse, HttpServer}, -}; +use actix_web::{dev::Server, web, App, HttpResponse, HttpServer}; use tokio::{sync::watch, task::JoinHandle}; - use zksync_config::configs::api::ContractVerificationApiConfig; use zksync_dal::connection::ConnectionPool; use zksync_utils::panic_notify::{spawn_panic_handler, ThreadPanicNotify}; +use self::api_decl::RestApi; + mod api_decl; mod api_impl; mod metrics; -use self::api_decl::RestApi; - fn start_server(api: RestApi, bind_to: SocketAddr, threads: usize) -> Server { HttpServer::new(move || { let api = api.clone(); diff --git a/core/lib/zksync_core/src/api_server/execution_sandbox/apply.rs b/core/lib/zksync_core/src/api_server/execution_sandbox/apply.rs index 36ede77abdba..f0488f711906 100644 --- a/core/lib/zksync_core/src/api_server/execution_sandbox/apply.rs +++ b/core/lib/zksync_core/src/api_server/execution_sandbox/apply.rs @@ -8,11 +8,11 @@ use std::time::{Duration, Instant}; -use multivm::vm_latest::{constants::BLOCK_GAS_LIMIT, HistoryDisabled}; - -use multivm::interface::VmInterface; -use multivm::interface::{L1BatchEnv, L2BlockEnv, SystemEnv}; -use multivm::VmInstance; +use multivm::{ + interface::{L1BatchEnv, L2BlockEnv, SystemEnv, VmInterface}, + vm_latest::{constants::BLOCK_GAS_LIMIT, HistoryDisabled}, + VmInstance, +}; use zksync_dal::{ConnectionPool, SqlxError, StorageProcessor}; use zksync_state::{PostgresStorage, ReadStorage, StorageView, WriteStorage}; use zksync_system_constants::{ diff --git a/core/lib/zksync_core/src/api_server/execution_sandbox/error.rs b/core/lib/zksync_core/src/api_server/execution_sandbox/error.rs index 59e874ade902..c5928cfd8470 100644 --- a/core/lib/zksync_core/src/api_server/execution_sandbox/error.rs +++ b/core/lib/zksync_core/src/api_server/execution_sandbox/error.rs @@ -1,6 +1,5 @@ -use thiserror::Error; - use multivm::interface::{Halt, TxRevertReason}; +use thiserror::Error; #[derive(Debug, Error)] pub(crate) enum SandboxExecutionError { diff --git a/core/lib/zksync_core/src/api_server/execution_sandbox/execute.rs b/core/lib/zksync_core/src/api_server/execution_sandbox/execute.rs index 9621adae2d87..c900dd4e5a5a 100644 --- a/core/lib/zksync_core/src/api_server/execution_sandbox/execute.rs +++ b/core/lib/zksync_core/src/api_server/execution_sandbox/execute.rs @@ -1,13 +1,13 @@ //! Implementation of "executing" methods, e.g. `eth_call`. +use multivm::{ + interface::{TxExecutionMode, VmExecutionMode, VmExecutionResultAndLogs, VmInterface}, + tracers::StorageInvocations, + vm_latest::constants::ETH_CALL_GAS_LIMIT, + MultiVMTracer, +}; use tracing::{span, Level}; - -use multivm::interface::{TxExecutionMode, VmExecutionMode, VmExecutionResultAndLogs, VmInterface}; -use multivm::tracers::StorageInvocations; -use multivm::vm_latest::constants::ETH_CALL_GAS_LIMIT; -use multivm::MultiVMTracer; use zksync_dal::ConnectionPool; - use zksync_types::{ fee::TransactionExecutionMetrics, l2::L2Tx, ExecuteTransactionCommon, Nonce, PackedEthSignature, Transaction, U256, diff --git a/core/lib/zksync_core/src/api_server/execution_sandbox/mod.rs b/core/lib/zksync_core/src/api_server/execution_sandbox/mod.rs index 67feced9d5ec..461be71c089f 100644 --- a/core/lib/zksync_core/src/api_server/execution_sandbox/mod.rs +++ b/core/lib/zksync_core/src/api_server/execution_sandbox/mod.rs @@ -1,21 +1,13 @@ -use std::sync::Arc; -use std::time::Duration; -use tokio::runtime::Handle; +use std::{sync::Arc, time::Duration}; +use multivm::vm_latest::utils::fee::derive_base_fee_and_gas_per_pubdata; +use tokio::runtime::Handle; use zksync_dal::{ConnectionPool, SqlxError, StorageProcessor}; use zksync_state::{PostgresStorage, PostgresStorageCaches, ReadStorage, StorageView}; use zksync_system_constants::PUBLISH_BYTECODE_OVERHEAD; use zksync_types::{api, AccountTreeId, L2ChainId, MiniblockNumber, U256}; use zksync_utils::bytecode::{compress_bytecode, hash_bytecode}; -// Note: keep the modules private, and instead re-export functions that make public interface. -mod apply; -mod error; -mod execute; -mod tracers; -mod validate; -mod vm_metrics; - use self::vm_metrics::SandboxStage; pub(super) use self::{ error::SandboxExecutionError, @@ -24,7 +16,14 @@ pub(super) use self::{ vm_metrics::{SubmitTxStage, SANDBOX_METRICS}, }; use super::tx_sender::MultiVMBaseSystemContracts; -use multivm::vm_latest::utils::fee::derive_base_fee_and_gas_per_pubdata; + +// Note: keep the modules private, and instead re-export functions that make public interface. +mod apply; +mod error; +mod execute; +mod tracers; +mod validate; +mod vm_metrics; /// Permit to invoke VM code. /// diff --git a/core/lib/zksync_core/src/api_server/execution_sandbox/tracers.rs b/core/lib/zksync_core/src/api_server/execution_sandbox/tracers.rs index 9f987a150da8..719f6da0b4a9 100644 --- a/core/lib/zksync_core/src/api_server/execution_sandbox/tracers.rs +++ b/core/lib/zksync_core/src/api_server/execution_sandbox/tracers.rs @@ -1,9 +1,7 @@ -use multivm::tracers::CallTracer; -use multivm::vm_latest::HistoryMode; -use multivm::{MultiVMTracer, MultiVmTracerPointer}; -use once_cell::sync::OnceCell; - use std::sync::Arc; + +use multivm::{tracers::CallTracer, vm_latest::HistoryMode, MultiVMTracer, MultiVmTracerPointer}; +use once_cell::sync::OnceCell; use zksync_state::WriteStorage; use zksync_types::vm_trace::Call; diff --git a/core/lib/zksync_core/src/api_server/execution_sandbox/validate.rs b/core/lib/zksync_core/src/api_server/execution_sandbox/validate.rs index 4b9e13084efd..df70d02fe44b 100644 --- a/core/lib/zksync_core/src/api_server/execution_sandbox/validate.rs +++ b/core/lib/zksync_core/src/api_server/execution_sandbox/validate.rs @@ -1,12 +1,14 @@ -use multivm::interface::{ExecutionResult, VmExecutionMode, VmInterface}; -use multivm::MultiVMTracer; use std::collections::HashSet; -use multivm::tracers::{ - validator::{ValidationError, ValidationTracer, ValidationTracerParams}, - StorageInvocations, +use multivm::{ + interface::{ExecutionResult, VmExecutionMode, VmInterface}, + tracers::{ + validator::{ValidationError, ValidationTracer, ValidationTracerParams}, + StorageInvocations, + }, + vm_latest::HistoryDisabled, + MultiVMTracer, }; -use multivm::vm_latest::HistoryDisabled; use zksync_dal::{ConnectionPool, StorageProcessor}; use zksync_types::{l2::L2Tx, Transaction, TRUSTED_ADDRESS_SLOTS, TRUSTED_TOKEN_SLOTS, U256}; diff --git a/core/lib/zksync_core/src/api_server/execution_sandbox/vm_metrics.rs b/core/lib/zksync_core/src/api_server/execution_sandbox/vm_metrics.rs index 138d06a3a7c8..6842fe438f83 100644 --- a/core/lib/zksync_core/src/api_server/execution_sandbox/vm_metrics.rs +++ b/core/lib/zksync_core/src/api_server/execution_sandbox/vm_metrics.rs @@ -1,12 +1,13 @@ -use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Gauge, Histogram, Metrics}; - use std::time::Duration; use multivm::interface::{VmExecutionResultAndLogs, VmMemoryMetrics}; +use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Gauge, Histogram, Metrics}; use zksync_state::StorageViewMetrics; -use zksync_types::event::{extract_long_l2_to_l1_messages, extract_published_bytecodes}; -use zksync_types::fee::TransactionExecutionMetrics; -use zksync_types::storage_writes_deduplicator::StorageWritesDeduplicator; +use zksync_types::{ + event::{extract_long_l2_to_l1_messages, extract_published_bytecodes}, + fee::TransactionExecutionMetrics, + storage_writes_deduplicator::StorageWritesDeduplicator, +}; use zksync_utils::bytecode::bytecode_len_in_bytes; use crate::metrics::InteractionType; diff --git a/core/lib/zksync_core/src/api_server/healthcheck.rs b/core/lib/zksync_core/src/api_server/healthcheck.rs index 74495f3439cb..58444c30dc94 100644 --- a/core/lib/zksync_core/src/api_server/healthcheck.rs +++ b/core/lib/zksync_core/src/api_server/healthcheck.rs @@ -1,8 +1,7 @@ -use axum::{extract::State, http::StatusCode, routing::get, Json, Router}; -use tokio::sync::watch; - use std::{collections::HashSet, net::SocketAddr, sync::Arc, time::Duration}; +use axum::{extract::State, http::StatusCode, routing::get, Json, Router}; +use tokio::sync::watch; use zksync_health_check::{AppHealth, CheckHealth}; type SharedHealthchecks = Arc<[Box]>; diff --git a/core/lib/zksync_core/src/api_server/tree/metrics.rs b/core/lib/zksync_core/src/api_server/tree/metrics.rs index e6b552468d8a..d185861d07c6 100644 --- a/core/lib/zksync_core/src/api_server/tree/metrics.rs +++ b/core/lib/zksync_core/src/api_server/tree/metrics.rs @@ -1,9 +1,9 @@ //! Metrics for the Merkle tree API. -use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics, Unit}; - use std::time::Duration; +use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics, Unit}; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelValue, EncodeLabelSet)] #[metrics(label = "method", rename_all = "snake_case")] pub(super) enum MerkleTreeApiMethod { diff --git a/core/lib/zksync_core/src/api_server/tree/mod.rs b/core/lib/zksync_core/src/api_server/tree/mod.rs index 7b4c9086ac68..00a5fd285546 100644 --- a/core/lib/zksync_core/src/api_server/tree/mod.rs +++ b/core/lib/zksync_core/src/api_server/tree/mod.rs @@ -1,5 +1,7 @@ //! Primitive Merkle tree API used internally to fetch proofs. +use std::{fmt, future::Future, net::SocketAddr, pin::Pin}; + use anyhow::Context as _; use async_trait::async_trait; use axum::{ @@ -10,19 +12,16 @@ use axum::{ }; use serde::{Deserialize, Serialize}; use tokio::sync::watch; - -use std::{fmt, future::Future, net::SocketAddr, pin::Pin}; - use zksync_merkle_tree::NoVersionError; use zksync_types::{L1BatchNumber, H256, U256}; +use self::metrics::{MerkleTreeApiMethod, API_METRICS}; +use crate::metadata_calculator::{AsyncTreeReader, MerkleTreeInfo}; + mod metrics; #[cfg(test)] mod tests; -use self::metrics::{MerkleTreeApiMethod, API_METRICS}; -use crate::metadata_calculator::{AsyncTreeReader, MerkleTreeInfo}; - #[derive(Debug, Serialize, Deserialize)] struct TreeProofsRequest { l1_batch_number: L1BatchNumber, diff --git a/core/lib/zksync_core/src/api_server/tree/tests.rs b/core/lib/zksync_core/src/api_server/tree/tests.rs index 2f90b9fabdf0..11161805633e 100644 --- a/core/lib/zksync_core/src/api_server/tree/tests.rs +++ b/core/lib/zksync_core/src/api_server/tree/tests.rs @@ -1,9 +1,8 @@ //! Tests for the Merkle tree API. -use tempfile::TempDir; - use std::net::Ipv4Addr; +use tempfile::TempDir; use zksync_dal::ConnectionPool; use super::*; diff --git a/core/lib/zksync_core/src/api_server/tx_sender/mod.rs b/core/lib/zksync_core/src/api_server/tx_sender/mod.rs index 12c738004151..3766c8a4ee19 100644 --- a/core/lib/zksync_core/src/api_server/tx_sender/mod.rs +++ b/core/lib/zksync_core/src/api_server/tx_sender/mod.rs @@ -1,27 +1,23 @@ //! Helper module to submit transactions into the zkSync Network. -// External uses +use std::{cmp, num::NonZeroU32, sync::Arc, time::Instant}; + use governor::{ clock::MonotonicClock, middleware::NoOpMiddleware, state::{InMemoryState, NotKeyed}, Quota, RateLimiter, }; - -// Built-in uses -use std::{cmp, num::NonZeroU32, sync::Arc, time::Instant}; - -// Workspace uses - -use multivm::interface::VmExecutionResultAndLogs; -use multivm::vm_latest::{ - constants::{BLOCK_GAS_LIMIT, MAX_PUBDATA_PER_BLOCK}, - utils::{ - fee::derive_base_fee_and_gas_per_pubdata, - overhead::{derive_overhead, OverheadCoefficients}, +use multivm::{ + interface::VmExecutionResultAndLogs, + vm_latest::{ + constants::{BLOCK_GAS_LIMIT, MAX_PUBDATA_PER_BLOCK}, + utils::{ + fee::derive_base_fee_and_gas_per_pubdata, + overhead::{derive_overhead, OverheadCoefficients}, + }, }, }; - use zksync_config::configs::{api::Web3JsonRpcConfig, chain::StateKeeperConfig}; use zksync_contracts::BaseSystemContracts; use zksync_dal::{transactions_dal::L2TxSubmissionResult, ConnectionPool}; @@ -29,26 +25,24 @@ use zksync_state::PostgresStorageCaches; use zksync_types::{ fee::{Fee, TransactionExecutionMetrics}, get_code_key, get_intrinsic_constants, - l2::error::TxCheckError::TxDuplication, - l2::L2Tx, + l2::{error::TxCheckError::TxDuplication, L2Tx}, utils::storage_key_for_eth_balance, AccountTreeId, Address, ExecuteTransactionCommon, L2ChainId, Nonce, PackedEthSignature, ProtocolVersionId, Transaction, H160, H256, MAX_GAS_PER_PUBDATA_BYTE, MAX_L2_TX_GAS_LIMIT, MAX_NEW_FACTORY_DEPS, U256, }; - use zksync_utils::h256_to_u256; -// Local uses -use crate::api_server::{ - execution_sandbox::{ - adjust_l1_gas_price_for_tx, execute_tx_eth_call, execute_tx_with_pending_state, - get_pubdata_for_factory_deps, BlockArgs, SubmitTxStage, TxExecutionArgs, TxSharedArgs, - VmConcurrencyLimiter, VmPermit, SANDBOX_METRICS, - }, - tx_sender::result::ApiCallResult, -}; +pub(super) use self::{proxy::TxProxy, result::SubmitTxError}; use crate::{ + api_server::{ + execution_sandbox::{ + adjust_l1_gas_price_for_tx, execute_tx_eth_call, execute_tx_with_pending_state, + get_pubdata_for_factory_deps, BlockArgs, SubmitTxStage, TxExecutionArgs, TxSharedArgs, + VmConcurrencyLimiter, VmPermit, SANDBOX_METRICS, + }, + tx_sender::result::ApiCallResult, + }, l1_gas_price::L1GasPriceProvider, metrics::{TxStage, APP_METRICS}, state_keeper::seal_criteria::{ConditionalSealer, SealData}, @@ -57,8 +51,6 @@ use crate::{ mod proxy; mod result; -pub(super) use self::{proxy::TxProxy, result::SubmitTxError}; - /// Type alias for the rate limiter implementation. type TxSenderRateLimiter = RateLimiter>; diff --git a/core/lib/zksync_core/src/api_server/tx_sender/proxy.rs b/core/lib/zksync_core/src/api_server/tx_sender/proxy.rs index 4f70b1d5e503..7a4b928a8098 100644 --- a/core/lib/zksync_core/src/api_server/tx_sender/proxy.rs +++ b/core/lib/zksync_core/src/api_server/tx_sender/proxy.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use tokio::sync::RwLock; +use tokio::sync::RwLock; use zksync_types::{ api::{BlockId, Transaction, TransactionDetails, TransactionId, TransactionReceipt}, l2::L2Tx, diff --git a/core/lib/zksync_core/src/api_server/tx_sender/result.rs b/core/lib/zksync_core/src/api_server/tx_sender/result.rs index b02049f014e7..5b5af7b9cacc 100644 --- a/core/lib/zksync_core/src/api_server/tx_sender/result.rs +++ b/core/lib/zksync_core/src/api_server/tx_sender/result.rs @@ -1,10 +1,11 @@ -use crate::api_server::execution_sandbox::SandboxExecutionError; +use multivm::{ + interface::{ExecutionResult, VmExecutionResultAndLogs}, + tracers::validator::ValidationError, +}; use thiserror::Error; +use zksync_types::{l2::error::TxCheckError, U256}; -use multivm::interface::{ExecutionResult, VmExecutionResultAndLogs}; -use multivm::tracers::validator::ValidationError; -use zksync_types::l2::error::TxCheckError; -use zksync_types::U256; +use crate::api_server::execution_sandbox::SandboxExecutionError; #[derive(Debug, Error)] pub enum SubmitTxError { diff --git a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/batch_limiter_middleware.rs b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/batch_limiter_middleware.rs index f85325c03bcb..0192ffe3a5ce 100644 --- a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/batch_limiter_middleware.rs +++ b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/batch_limiter_middleware.rs @@ -1,3 +1,5 @@ +use std::{future::Future, num::NonZeroU32, sync::Arc}; + use futures::{future, FutureExt}; use governor::{ clock::DefaultClock, @@ -12,8 +14,6 @@ use jsonrpc_core::{ use jsonrpc_pubsub::Session; use vise::{Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics}; -use std::{future::Future, num::NonZeroU32, sync::Arc}; - /// Configures the rate limiting for the WebSocket API. /// Rate limiting is applied per active connection, e.g. a single connected user may not send more than X requests /// per minute. diff --git a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/error.rs b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/error.rs index 4a30961c4531..e750d05d9b51 100644 --- a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/error.rs +++ b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/error.rs @@ -1,8 +1,8 @@ +use std::fmt; + use jsonrpc_core::{Error, ErrorCode}; use zksync_web3_decl::error::Web3Error; -use std::fmt; - use crate::api_server::web3::metrics::API_METRICS; pub fn into_jsrpc_error(err: Web3Error) -> Error { diff --git a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/debug.rs b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/debug.rs index 3775da78e41a..57ae0bb7116c 100644 --- a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/debug.rs +++ b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/debug.rs @@ -1,15 +1,15 @@ -// External uses -use crate::api_server::web3::backend_jsonrpc::error::into_jsrpc_error; -use crate::api_server::web3::namespaces::DebugNamespace; use jsonrpc_core::{BoxFuture, Result}; use jsonrpc_derive::rpc; - use zksync_types::{ api::{BlockId, BlockNumber, DebugCall, ResultDebugCall, TracerConfig}, transaction_request::CallRequest, H256, }; +use crate::api_server::web3::{ + backend_jsonrpc::error::into_jsrpc_error, namespaces::DebugNamespace, +}; + #[rpc] pub trait DebugNamespaceT { #[rpc(name = "debug_traceBlockByNumber")] diff --git a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/en.rs b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/en.rs index e75d7caade29..2fc08eafcace 100644 --- a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/en.rs +++ b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/en.rs @@ -1,13 +1,7 @@ -// Built-in uses - -// External uses use jsonrpc_core::{BoxFuture, Result}; use jsonrpc_derive::rpc; - -// Workspace uses use zksync_types::{api::en::SyncBlock, MiniblockNumber}; -// Local uses use crate::{ api_server::web3::{backend_jsonrpc::error::into_jsrpc_error, EnNamespace}, l1_gas_price::L1GasPriceProvider, diff --git a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/eth.rs b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/eth.rs index 00ba9379ae5f..706701cfcf3e 100644 --- a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/eth.rs +++ b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/eth.rs @@ -1,10 +1,5 @@ -// Built-in uses - -// External uses use jsonrpc_core::{BoxFuture, Result}; use jsonrpc_derive::rpc; - -// Workspace uses use zksync_types::{ api::{ BlockId, BlockIdVariant, BlockNumber, Transaction, TransactionId, TransactionReceipt, @@ -16,9 +11,10 @@ use zksync_types::{ }; use zksync_web3_decl::types::{Block, Filter, FilterChanges, Log}; -// Local uses -use crate::web3::namespaces::EthNamespace; -use crate::{l1_gas_price::L1GasPriceProvider, web3::backend_jsonrpc::error::into_jsrpc_error}; +use crate::{ + l1_gas_price::L1GasPriceProvider, + web3::{backend_jsonrpc::error::into_jsrpc_error, namespaces::EthNamespace}, +}; #[rpc] pub trait EthNamespaceT { diff --git a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/net.rs b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/net.rs index 89abd3177c84..4dbd9fb7a753 100644 --- a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/net.rs +++ b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/net.rs @@ -1,13 +1,7 @@ -// Built-in uses - -// External uses use jsonrpc_core::Result; use jsonrpc_derive::rpc; - -// Workspace uses use zksync_types::U256; -// Local uses use crate::web3::namespaces::NetNamespace; #[rpc] diff --git a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/zks.rs b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/zks.rs index bf700a64156e..48f413fba058 100644 --- a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/zks.rs +++ b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/namespaces/zks.rs @@ -1,12 +1,8 @@ -// Built-in uses use std::collections::HashMap; -// External uses use bigdecimal::BigDecimal; use jsonrpc_core::{BoxFuture, Result}; use jsonrpc_derive::rpc; - -// Workspace uses use zksync_types::{ api::{ BlockDetails, BridgeAddresses, L1BatchDetails, L2ToL1LogProof, Proof, ProtocolVersion, @@ -18,9 +14,10 @@ use zksync_types::{ }; use zksync_web3_decl::types::{Filter, Log, Token}; -// Local uses -use crate::web3::namespaces::ZksNamespace; -use crate::{l1_gas_price::L1GasPriceProvider, web3::backend_jsonrpc::error::into_jsrpc_error}; +use crate::{ + l1_gas_price::L1GasPriceProvider, + web3::{backend_jsonrpc::error::into_jsrpc_error, namespaces::ZksNamespace}, +}; #[rpc] pub trait ZksNamespaceT { diff --git a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/pub_sub.rs b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/pub_sub.rs index 1b11919abde4..e54249f84c38 100644 --- a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/pub_sub.rs +++ b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpc/pub_sub.rs @@ -2,9 +2,7 @@ use std::sync::Arc; use jsonrpc_core::{BoxFuture, Result}; use jsonrpc_derive::rpc; -use jsonrpc_pubsub::typed; -use jsonrpc_pubsub::{Session, SubscriptionId}; - +use jsonrpc_pubsub::{typed, Session, SubscriptionId}; use zksync_web3_decl::types::PubSubResult; use super::{super::EthSubscribe, batch_limiter_middleware::RateLimitMetadata}; diff --git a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpsee/mod.rs b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpsee/mod.rs index 04f6102066f4..2c4f2a3ce91d 100644 --- a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpsee/mod.rs +++ b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpsee/mod.rs @@ -3,8 +3,11 @@ //! namespace structures defined in `zksync_core`. use std::error::Error; -use zksync_web3_decl::error::Web3Error; -use zksync_web3_decl::jsonrpsee::types::{error::ErrorCode, ErrorObjectOwned}; + +use zksync_web3_decl::{ + error::Web3Error, + jsonrpsee::types::{error::ErrorCode, ErrorObjectOwned}, +}; pub mod namespaces; diff --git a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpsee/namespaces/zks.rs b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpsee/namespaces/zks.rs index 6b6ed67c3c68..93a957906661 100644 --- a/core/lib/zksync_core/src/api_server/web3/backend_jsonrpsee/namespaces/zks.rs +++ b/core/lib/zksync_core/src/api_server/web3/backend_jsonrpsee/namespaces/zks.rs @@ -1,7 +1,6 @@ -use bigdecimal::BigDecimal; - use std::collections::HashMap; +use bigdecimal::BigDecimal; use zksync_types::{ api::{ BlockDetails, BridgeAddresses, L1BatchDetails, L2ToL1LogProof, Proof, ProtocolVersion, diff --git a/core/lib/zksync_core/src/api_server/web3/metrics.rs b/core/lib/zksync_core/src/api_server/web3/metrics.rs index 2df24f9dd603..60d41c6ea324 100644 --- a/core/lib/zksync_core/src/api_server/web3/metrics.rs +++ b/core/lib/zksync_core/src/api_server/web3/metrics.rs @@ -1,18 +1,18 @@ //! Metrics for the JSON-RPC server. -use vise::{ - Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Gauge, Histogram, LabeledFamily, - LatencyObserver, Metrics, Unit, -}; - use std::{ fmt, time::{Duration, Instant}, }; -use super::{ApiTransport, TypedFilter}; +use vise::{ + Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Gauge, Histogram, LabeledFamily, + LatencyObserver, Metrics, Unit, +}; use zksync_types::api; +use super::{ApiTransport, TypedFilter}; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelValue, EncodeLabelSet)] #[metrics(label = "scheme", rename_all = "UPPERCASE")] pub(super) enum ApiTransportLabel { diff --git a/core/lib/zksync_core/src/api_server/web3/mod.rs b/core/lib/zksync_core/src/api_server/web3/mod.rs index 2904d5af79dc..411c04112c90 100644 --- a/core/lib/zksync_core/src/api_server/web3/mod.rs +++ b/core/lib/zksync_core/src/api_server/web3/mod.rs @@ -1,16 +1,17 @@ +use std::{net::SocketAddr, sync::Arc, time::Duration}; + use anyhow::Context as _; +use chrono::NaiveDateTime; use futures::future; use jsonrpc_core::MetaIoHandler; use jsonrpc_http_server::hyper; use jsonrpc_pubsub::PubSubHandler; use serde::Deserialize; -use tokio::sync::{mpsc, oneshot, watch, Mutex}; +use tokio::{ + sync::{mpsc, oneshot, watch, Mutex}, + task::JoinHandle, +}; use tower_http::{cors::CorsLayer, metrics::InFlightRequestsLayer}; - -use chrono::NaiveDateTime; -use std::{net::SocketAddr, sync::Arc, time::Duration}; -use tokio::task::JoinHandle; - use zksync_dal::{ConnectionPool, StorageProcessor}; use zksync_health_check::{HealthStatus, HealthUpdater, ReactiveHealthCheck}; use zksync_types::{api, MiniblockNumber}; @@ -27,6 +28,23 @@ use zksync_web3_decl::{ types::Filter, }; +use self::{ + backend_jsonrpc::{ + batch_limiter_middleware::{LimitMiddleware, Transport}, + error::internal_error, + namespaces::{ + debug::DebugNamespaceT, en::EnNamespaceT, eth::EthNamespaceT, net::NetNamespaceT, + web3::Web3NamespaceT, zks::ZksNamespaceT, + }, + pub_sub::Web3PubSub, + }, + metrics::API_METRICS, + namespaces::{ + DebugNamespace, EnNamespace, EthNamespace, NetNamespace, Web3Namespace, ZksNamespace, + }, + pubsub::{EthSubscribe, PubSubEvent}, + state::{Filters, InternalApiConfig, RpcState, SealedMiniblockNumber}, +}; use crate::{ api_server::{ execution_sandbox::VmConcurrencyBarrier, tree::TreeApiHttpClient, tx_sender::TxSender, @@ -45,22 +63,6 @@ pub mod state; #[cfg(test)] pub(crate) mod tests; -use self::backend_jsonrpc::{ - batch_limiter_middleware::{LimitMiddleware, Transport}, - error::internal_error, - namespaces::{ - debug::DebugNamespaceT, en::EnNamespaceT, eth::EthNamespaceT, net::NetNamespaceT, - web3::Web3NamespaceT, zks::ZksNamespaceT, - }, - pub_sub::Web3PubSub, -}; -use self::metrics::API_METRICS; -use self::namespaces::{ - DebugNamespace, EnNamespace, EthNamespace, NetNamespace, Web3Namespace, ZksNamespace, -}; -use self::pubsub::{EthSubscribe, PubSubEvent}; -use self::state::{Filters, InternalApiConfig, RpcState, SealedMiniblockNumber}; - /// Timeout for graceful shutdown logic within API servers. const GRACEFUL_SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(5); diff --git a/core/lib/zksync_core/src/api_server/web3/namespaces/debug.rs b/core/lib/zksync_core/src/api_server/web3/namespaces/debug.rs index d59c25ddbe9e..76e4f01ee478 100644 --- a/core/lib/zksync_core/src/api_server/web3/namespaces/debug.rs +++ b/core/lib/zksync_core/src/api_server/web3/namespaces/debug.rs @@ -1,9 +1,7 @@ -use multivm::vm_latest::constants::BLOCK_GAS_LIMIT; -use once_cell::sync::OnceCell; use std::sync::Arc; -use multivm::interface::ExecutionResult; - +use multivm::{interface::ExecutionResult, vm_latest::constants::BLOCK_GAS_LIMIT}; +use once_cell::sync::OnceCell; use zksync_dal::ConnectionPool; use zksync_state::PostgresStorageCaches; use zksync_types::{ @@ -15,19 +13,21 @@ use zksync_types::{ }; use zksync_web3_decl::error::Web3Error; -use crate::api_server::{ - execution_sandbox::{ - execute_tx_eth_call, ApiTracer, BlockArgs, TxSharedArgs, VmConcurrencyLimiter, - }, - tx_sender::ApiContracts, - web3::{ - backend_jsonrpc::error::internal_error, - metrics::API_METRICS, - resolve_block, - state::{RpcState, SealedMiniblockNumber}, +use crate::{ + api_server::{ + execution_sandbox::{ + execute_tx_eth_call, ApiTracer, BlockArgs, TxSharedArgs, VmConcurrencyLimiter, + }, + tx_sender::ApiContracts, + web3::{ + backend_jsonrpc::error::internal_error, + metrics::API_METRICS, + resolve_block, + state::{RpcState, SealedMiniblockNumber}, + }, }, + l1_gas_price::L1GasPriceProvider, }; -use crate::l1_gas_price::L1GasPriceProvider; #[derive(Debug, Clone)] pub struct DebugNamespace { diff --git a/core/lib/zksync_core/src/api_server/web3/namespaces/en.rs b/core/lib/zksync_core/src/api_server/web3/namespaces/en.rs index b43f5523938b..97c6eb9e768c 100644 --- a/core/lib/zksync_core/src/api_server/web3/namespaces/en.rs +++ b/core/lib/zksync_core/src/api_server/web3/namespaces/en.rs @@ -2,7 +2,7 @@ use zksync_types::{api::en::SyncBlock, MiniblockNumber}; use zksync_web3_decl::error::Web3Error; use crate::{ - api_server::{web3::backend_jsonrpc::error::internal_error, web3::state::RpcState}, + api_server::web3::{backend_jsonrpc::error::internal_error, state::RpcState}, l1_gas_price::L1GasPriceProvider, }; diff --git a/core/lib/zksync_core/src/api_server/web3/namespaces/zks.rs b/core/lib/zksync_core/src/api_server/web3/namespaces/zks.rs index 9e3a90dde043..7f575c57414f 100644 --- a/core/lib/zksync_core/src/api_server/web3/namespaces/zks.rs +++ b/core/lib/zksync_core/src/api_server/web3/namespaces/zks.rs @@ -2,7 +2,6 @@ use std::{collections::HashMap, convert::TryInto}; use bigdecimal::{BigDecimal, Zero}; use zksync_dal::StorageProcessor; - use zksync_mini_merkle_tree::MiniMerkleTree; use zksync_types::{ api::{ @@ -25,11 +24,13 @@ use zksync_web3_decl::{ types::{Address, Filter, Log, Token, H256}, }; -use crate::api_server::{ - tree::TreeApiClient, - web3::{backend_jsonrpc::error::internal_error, metrics::API_METRICS, RpcState}, +use crate::{ + api_server::{ + tree::TreeApiClient, + web3::{backend_jsonrpc::error::internal_error, metrics::API_METRICS, RpcState}, + }, + l1_gas_price::L1GasPriceProvider, }; -use crate::l1_gas_price::L1GasPriceProvider; #[derive(Debug)] pub struct ZksNamespace { diff --git a/core/lib/zksync_core/src/api_server/web3/pubsub.rs b/core/lib/zksync_core/src/api_server/web3/pubsub.rs index 946c0744ba4d..57e54cad10f7 100644 --- a/core/lib/zksync_core/src/api_server/web3/pubsub.rs +++ b/core/lib/zksync_core/src/api_server/web3/pubsub.rs @@ -1,5 +1,7 @@ //! (Largely) backend-agnostic logic for dealing with Web3 subscriptions. +use std::{collections::HashMap, sync::Arc}; + use anyhow::Context as _; use jsonrpc_core::error::{Error, ErrorCode}; use jsonrpc_pubsub::{typed, SubscriptionId}; @@ -8,9 +10,6 @@ use tokio::{ task::JoinHandle, time::{interval, Duration}, }; - -use std::{collections::HashMap, sync::Arc}; - use zksync_dal::ConnectionPool; use zksync_types::{MiniblockNumber, H128, H256}; use zksync_web3_decl::types::{BlockHeader, Log, PubSubFilter, PubSubResult}; diff --git a/core/lib/zksync_core/src/api_server/web3/state.rs b/core/lib/zksync_core/src/api_server/web3/state.rs index ea52b2ae61cc..75b41d58b20d 100644 --- a/core/lib/zksync_core/src/api_server/web3/state.rs +++ b/core/lib/zksync_core/src/api_server/web3/state.rs @@ -1,5 +1,3 @@ -use zksync_utils::h256_to_u256; - use std::{ collections::HashMap, convert::TryFrom, @@ -10,9 +8,9 @@ use std::{ }, time::{Duration, Instant}, }; + use tokio::sync::Mutex; use vise::GaugeGuard; - use zksync_config::configs::{api::Web3JsonRpcConfig, chain::NetworkConfig, ContractsConfig}; use zksync_dal::ConnectionPool; use zksync_types::{ @@ -23,6 +21,7 @@ use zksync_types::{ AccountTreeId, Address, L1BatchNumber, L1ChainId, L2ChainId, MiniblockNumber, StorageKey, H256, SYSTEM_CONTEXT_ADDRESS, U256, U64, VIRTUIAL_BLOCK_UPGRADE_INFO_POSITION, }; +use zksync_utils::h256_to_u256; use zksync_web3_decl::{ error::Web3Error, types::{Filter, Log}, diff --git a/core/lib/zksync_core/src/api_server/web3/tests/mod.rs b/core/lib/zksync_core/src/api_server/web3/tests/mod.rs index 1bb14df52fa2..8743330710cb 100644 --- a/core/lib/zksync_core/src/api_server/web3/tests/mod.rs +++ b/core/lib/zksync_core/src/api_server/web3/tests/mod.rs @@ -1,9 +1,8 @@ +use std::{sync::Arc, time::Instant}; + use assert_matches::assert_matches; use async_trait::async_trait; use tokio::sync::watch; - -use std::{sync::Arc, time::Instant}; - use zksync_config::configs::{ api::Web3JsonRpcConfig, chain::{NetworkConfig, StateKeeperConfig}, @@ -23,8 +22,6 @@ use zksync_web3_decl::{ types::FilterChanges, }; -mod ws; - use super::{metrics::ApiTransportLabel, *}; use crate::{ api_server::tx_sender::TxSenderConfig, @@ -32,6 +29,8 @@ use crate::{ state_keeper::tests::create_l2_transaction, }; +mod ws; + const TEST_TIMEOUT: Duration = Duration::from_secs(10); const POLL_INTERVAL: Duration = Duration::from_millis(50); diff --git a/core/lib/zksync_core/src/api_server/web3/tests/ws.rs b/core/lib/zksync_core/src/api_server/web3/tests/ws.rs index 704dfef67005..58fcebeda0d6 100644 --- a/core/lib/zksync_core/src/api_server/web3/tests/ws.rs +++ b/core/lib/zksync_core/src/api_server/web3/tests/ws.rs @@ -2,7 +2,6 @@ use async_trait::async_trait; use tokio::sync::watch; - use zksync_config::configs::chain::NetworkConfig; use zksync_dal::ConnectionPool; use zksync_types::{api, Address, L1BatchNumber, H256, U64}; diff --git a/core/lib/zksync_core/src/basic_witness_input_producer/mod.rs b/core/lib/zksync_core/src/basic_witness_input_producer/mod.rs index e4d605d2545d..d9295b413fc8 100644 --- a/core/lib/zksync_core/src/basic_witness_input_producer/mod.rs +++ b/core/lib/zksync_core/src/basic_witness_input_producer/mod.rs @@ -1,24 +1,22 @@ -use anyhow::Context; -use std::sync::Arc; -use std::time::Instant; +use std::{sync::Arc, time::Instant}; +use anyhow::Context; +use async_trait::async_trait; +use multivm::interface::{L2BlockEnv, VmInterface}; +use tokio::{runtime::Handle, task::JoinHandle}; use zksync_dal::{basic_witness_input_producer_dal::JOB_MAX_ATTEMPT, ConnectionPool}; use zksync_object_store::{ObjectStore, ObjectStoreFactory}; use zksync_queued_job_processor::JobProcessor; -use zksync_types::witness_block_state::WitnessBlockState; -use zksync_types::{L1BatchNumber, L2ChainId}; +use zksync_types::{witness_block_state::WitnessBlockState, L1BatchNumber, L2ChainId}; -use async_trait::async_trait; -use multivm::interface::{L2BlockEnv, VmInterface}; -use tokio::runtime::Handle; -use tokio::task::JoinHandle; +use self::{ + metrics::METRICS, + vm_interactions::{create_vm, execute_tx}, +}; mod metrics; mod vm_interactions; -use self::metrics::METRICS; -use self::vm_interactions::{create_vm, execute_tx}; - /// Component that extracts all data (from DB) necessary to run a Basic Witness Generator. /// Does this by rerunning an entire L1Batch and extracting information from both the VM run and DB. /// This component will upload Witness Inputs to the object store. diff --git a/core/lib/zksync_core/src/basic_witness_input_producer/vm_interactions.rs b/core/lib/zksync_core/src/basic_witness_input_producer/vm_interactions.rs index 464ab1f92d0f..e655112fade2 100644 --- a/core/lib/zksync_core/src/basic_witness_input_producer/vm_interactions.rs +++ b/core/lib/zksync_core/src/basic_witness_input_producer/vm_interactions.rs @@ -1,15 +1,16 @@ use anyhow::{anyhow, Context}; - -use crate::state_keeper::io::common::load_l1_batch_params; - -use multivm::interface::{VmInterface, VmInterfaceHistoryEnabled}; -use multivm::vm_latest::HistoryEnabled; -use multivm::VmInstance; +use multivm::{ + interface::{VmInterface, VmInterfaceHistoryEnabled}, + vm_latest::HistoryEnabled, + VmInstance, +}; use tokio::runtime::Handle; use zksync_dal::StorageProcessor; use zksync_state::{PostgresStorage, StoragePtr, StorageView, WriteStorage}; use zksync_types::{L1BatchNumber, L2ChainId, Transaction}; +use crate::state_keeper::io::common::load_l1_batch_params; + pub(super) type VmAndStorage<'a> = ( VmInstance>, HistoryEnabled>, StoragePtr>>, diff --git a/core/lib/zksync_core/src/block_reverter/mod.rs b/core/lib/zksync_core/src/block_reverter/mod.rs index 1170af9d5bab..09358ec9abca 100644 --- a/core/lib/zksync_core/src/block_reverter/mod.rs +++ b/core/lib/zksync_core/src/block_reverter/mod.rs @@ -1,27 +1,26 @@ +use std::{path::Path, time::Duration}; + use bitflags::bitflags; use serde::Serialize; use tokio::time::sleep; - -use std::path::Path; -use std::time::Duration; - use zksync_config::{ContractsConfig, ETHSenderConfig}; use zksync_contracts::zksync_contract; use zksync_dal::ConnectionPool; +use zksync_eth_signer::{EthereumSigner, PrivateKeySigner, TransactionParameters}; use zksync_merkle_tree::domain::ZkSyncTree; use zksync_state::RocksdbStorage; use zksync_storage::RocksDB; -use zksync_types::aggregated_operations::AggregatedActionType; -use zksync_types::ethabi::Token; -use zksync_types::web3::{ - contract::{Contract, Options}, - transports::Http, - types::{BlockId, BlockNumber}, - Web3, +use zksync_types::{ + aggregated_operations::AggregatedActionType, + ethabi::Token, + web3::{ + contract::{Contract, Options}, + transports::Http, + types::{BlockId, BlockNumber}, + Web3, + }, + L1BatchNumber, PackedEthSignature, H160, H256, U256, }; -use zksync_types::{L1BatchNumber, PackedEthSignature, H160, H256, U256}; - -use zksync_eth_signer::{EthereumSigner, PrivateKeySigner, TransactionParameters}; bitflags! { pub struct BlockReverterFlags: u32 { diff --git a/core/lib/zksync_core/src/consensus/payload.rs b/core/lib/zksync_core/src/consensus/payload.rs index dbe276196b0f..8051d87ca585 100644 --- a/core/lib/zksync_core/src/consensus/payload.rs +++ b/core/lib/zksync_core/src/consensus/payload.rs @@ -1,9 +1,7 @@ use anyhow::Context as _; - use zksync_consensus_roles::validator; use zksync_protobuf::{required, ProtoFmt}; -use zksync_types::api::en::SyncBlock; -use zksync_types::{Address, L1BatchNumber, Transaction, H256}; +use zksync_types::{api::en::SyncBlock, Address, L1BatchNumber, Transaction, H256}; /// L2 block (= miniblock) payload. #[derive(Debug, PartialEq)] diff --git a/core/lib/zksync_core/src/data_fetchers/mod.rs b/core/lib/zksync_core/src/data_fetchers/mod.rs index f04a80c315e8..850f2814d5e2 100644 --- a/core/lib/zksync_core/src/data_fetchers/mod.rs +++ b/core/lib/zksync_core/src/data_fetchers/mod.rs @@ -9,8 +9,7 @@ //! Every data fetcher is represented by an autonomic routine, which spend most of the time sleeping; //! once in the configurable interval it fetches the data from an API and store it into the database. -use tokio::sync::watch; -use tokio::task::JoinHandle; +use tokio::{sync::watch, task::JoinHandle}; use zksync_config::FetcherConfig; use zksync_dal::ConnectionPool; diff --git a/core/lib/zksync_core/src/data_fetchers/token_list/mock.rs b/core/lib/zksync_core/src/data_fetchers/token_list/mock.rs index c813888cf522..4b2aaefafb87 100644 --- a/core/lib/zksync_core/src/data_fetchers/token_list/mock.rs +++ b/core/lib/zksync_core/src/data_fetchers/token_list/mock.rs @@ -2,16 +2,14 @@ use std::{collections::HashMap, fs::read_to_string, path::PathBuf, str::FromStr} use async_trait::async_trait; use serde::{Deserialize, Serialize}; - -use zksync_types::network::Network; use zksync_types::{ + network::Network, tokens::{TokenMetadata, ETHEREUM_ADDRESS}, Address, }; -use crate::data_fetchers::error::ApiFetchError; - use super::FetcherImpl; +use crate::data_fetchers::error::ApiFetchError; #[derive(Debug, Clone)] pub struct MockTokenListFetcher { diff --git a/core/lib/zksync_core/src/data_fetchers/token_list/mod.rs b/core/lib/zksync_core/src/data_fetchers/token_list/mod.rs index 3981ea8ea40b..e213cf89a066 100644 --- a/core/lib/zksync_core/src/data_fetchers/token_list/mod.rs +++ b/core/lib/zksync_core/src/data_fetchers/token_list/mod.rs @@ -15,11 +15,9 @@ use std::{ use async_trait::async_trait; use tokio::sync::watch; - use zksync_config::{configs::fetcher::TokenListSource, FetcherConfig}; use zksync_dal::{ConnectionPool, StorageProcessor}; -use zksync_types::network::Network; -use zksync_types::{tokens::TokenMetadata, Address}; +use zksync_types::{network::Network, tokens::TokenMetadata, Address}; use super::error::{ApiFetchError, ErrorAnalyzer}; diff --git a/core/lib/zksync_core/src/data_fetchers/token_list/one_inch.rs b/core/lib/zksync_core/src/data_fetchers/token_list/one_inch.rs index 1d022e4700d4..a21b942b4727 100644 --- a/core/lib/zksync_core/src/data_fetchers/token_list/one_inch.rs +++ b/core/lib/zksync_core/src/data_fetchers/token_list/one_inch.rs @@ -3,13 +3,11 @@ use std::{collections::HashMap, str::FromStr}; use async_trait::async_trait; use reqwest::{Client, Url}; use serde::{Deserialize, Serialize}; - use zksync_config::FetcherConfig; use zksync_types::{tokens::TokenMetadata, Address}; -use crate::data_fetchers::error::ApiFetchError; - use super::FetcherImpl; +use crate::data_fetchers::error::ApiFetchError; #[derive(Debug, Clone)] pub struct OneInchTokenListFetcher { diff --git a/core/lib/zksync_core/src/data_fetchers/token_price/coingecko.rs b/core/lib/zksync_core/src/data_fetchers/token_price/coingecko.rs index a046c23ea2d2..686410eed082 100644 --- a/core/lib/zksync_core/src/data_fetchers/token_price/coingecko.rs +++ b/core/lib/zksync_core/src/data_fetchers/token_price/coingecko.rs @@ -7,7 +7,6 @@ use itertools::Itertools; use num::{rational::Ratio, BigUint}; use reqwest::{Client, Url}; use serde::{Deserialize, Serialize}; - use zksync_config::FetcherConfig; use zksync_types::{ tokens::{TokenPrice, ETHEREUM_ADDRESS}, @@ -15,9 +14,8 @@ use zksync_types::{ }; use zksync_utils::UnsignedRatioSerializeAsDecimal; -use crate::data_fetchers::error::ApiFetchError; - use super::FetcherImpl; +use crate::data_fetchers::error::ApiFetchError; #[derive(Debug, Clone)] pub struct CoinGeckoFetcher { diff --git a/core/lib/zksync_core/src/data_fetchers/token_price/mock.rs b/core/lib/zksync_core/src/data_fetchers/token_price/mock.rs index 6e5f4893e530..3fde09f65f48 100644 --- a/core/lib/zksync_core/src/data_fetchers/token_price/mock.rs +++ b/core/lib/zksync_core/src/data_fetchers/token_price/mock.rs @@ -8,9 +8,8 @@ use zksync_types::{ Address, }; -use crate::data_fetchers::error::ApiFetchError; - use super::FetcherImpl; +use crate::data_fetchers::error::ApiFetchError; #[derive(Debug, Default, Clone)] pub struct MockPriceFetcher; diff --git a/core/lib/zksync_core/src/data_fetchers/token_price/mod.rs b/core/lib/zksync_core/src/data_fetchers/token_price/mod.rs index 8e7d5575f69a..074f8d81aa6d 100644 --- a/core/lib/zksync_core/src/data_fetchers/token_price/mod.rs +++ b/core/lib/zksync_core/src/data_fetchers/token_price/mod.rs @@ -3,15 +3,14 @@ use std::{collections::HashMap, time::Duration}; use async_trait::async_trait; - +use bigdecimal::FromPrimitive; +use num::{rational::Ratio, BigUint}; +use tokio::sync::watch; use zksync_config::{configs::fetcher::TokenPriceSource, FetcherConfig}; use zksync_dal::{ConnectionPool, StorageProcessor}; use zksync_types::{tokens::TokenPrice, Address}; use super::error::{ApiFetchError, ErrorAnalyzer}; -use bigdecimal::FromPrimitive; -use num::{rational::Ratio, BigUint}; -use tokio::sync::watch; pub mod coingecko; pub mod mock; diff --git a/core/lib/zksync_core/src/eth_sender/eth_tx_aggregator.rs b/core/lib/zksync_core/src/eth_sender/eth_tx_aggregator.rs index 4cb40c475f92..43b1e51da10d 100644 --- a/core/lib/zksync_core/src/eth_sender/eth_tx_aggregator.rs +++ b/core/lib/zksync_core/src/eth_sender/eth_tx_aggregator.rs @@ -1,7 +1,6 @@ use std::convert::TryInto; use tokio::sync::watch; - use zksync_config::configs::eth_sender::SenderConfig; use zksync_contracts::BaseSystemContractsHashes; use zksync_dal::{ConnectionPool, StorageProcessor}; @@ -17,13 +16,15 @@ use zksync_types::{ Address, ProtocolVersionId, H256, U256, }; -use crate::eth_sender::{ - metrics::{PubdataKind, METRICS}, - zksync_functions::ZkSyncFunctions, - Aggregator, ETHSenderError, +use crate::{ + eth_sender::{ + metrics::{PubdataKind, METRICS}, + zksync_functions::ZkSyncFunctions, + Aggregator, ETHSenderError, + }, + gas_tracker::agg_l1_batch_base_cost, + metrics::BlockL1Stage, }; -use crate::gas_tracker::agg_l1_batch_base_cost; -use crate::metrics::BlockL1Stage; /// Data queried from L1 using multicall contract. #[derive(Debug)] diff --git a/core/lib/zksync_core/src/eth_sender/eth_tx_manager.rs b/core/lib/zksync_core/src/eth_sender/eth_tx_manager.rs index 5aab4a2903c8..2ef9ea87a7c7 100644 --- a/core/lib/zksync_core/src/eth_sender/eth_tx_manager.rs +++ b/core/lib/zksync_core/src/eth_sender/eth_tx_manager.rs @@ -1,9 +1,7 @@ +use std::{sync::Arc, time::Duration}; + use anyhow::Context as _; use tokio::sync::watch; - -use std::sync::Arc; -use std::time::Duration; - use zksync_config::configs::eth_sender::SenderConfig; use zksync_dal::{ConnectionPool, StorageProcessor}; use zksync_eth_client::{ @@ -22,8 +20,7 @@ use zksync_types::{ use zksync_utils::time::seconds_since_epoch; use super::{metrics::METRICS, ETHSenderError}; -use crate::l1_gas_price::L1TxParamsProvider; -use crate::metrics::BlockL1Stage; +use crate::{l1_gas_price::L1TxParamsProvider, metrics::BlockL1Stage}; #[derive(Debug)] struct EthFee { diff --git a/core/lib/zksync_core/src/eth_sender/metrics.rs b/core/lib/zksync_core/src/eth_sender/metrics.rs index 950ff8bf6f79..4bce1bf1a1f4 100644 --- a/core/lib/zksync_core/src/eth_sender/metrics.rs +++ b/core/lib/zksync_core/src/eth_sender/metrics.rs @@ -1,9 +1,8 @@ //! Metrics for the Ethereum sender component. -use vise::{Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Gauge, Histogram, Metrics}; - use std::{fmt, time::Duration}; +use vise::{Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Gauge, Histogram, Metrics}; use zksync_dal::StorageProcessor; use zksync_types::{aggregated_operations::AggregatedActionType, eth_sender::EthTx}; use zksync_utils::time::seconds_since_epoch; diff --git a/core/lib/zksync_core/src/eth_sender/publish_criterion.rs b/core/lib/zksync_core/src/eth_sender/publish_criterion.rs index 33fd33ad5770..85f6a46c960b 100644 --- a/core/lib/zksync_core/src/eth_sender/publish_criterion.rs +++ b/core/lib/zksync_core/src/eth_sender/publish_criterion.rs @@ -1,8 +1,7 @@ -use async_trait::async_trait; -use chrono::Utc; - use std::fmt; +use async_trait::async_trait; +use chrono::Utc; use zksync_dal::StorageProcessor; use zksync_types::{ aggregated_operations::AggregatedActionType, commitment::L1BatchWithMetadata, L1BatchNumber, diff --git a/core/lib/zksync_core/src/eth_sender/tests.rs b/core/lib/zksync_core/src/eth_sender/tests.rs index 51166fc794a1..01781a424f5c 100644 --- a/core/lib/zksync_core/src/eth_sender/tests.rs +++ b/core/lib/zksync_core/src/eth_sender/tests.rs @@ -1,8 +1,7 @@ -use assert_matches::assert_matches; use std::sync::{atomic::Ordering, Arc}; +use assert_matches::assert_matches; use once_cell::sync::Lazy; - use zksync_config::{ configs::eth_sender::{ProofSendingMode, SenderConfig}, ContractsConfig, ETHSenderConfig, GasAdjusterConfig, @@ -23,10 +22,12 @@ use zksync_types::{ Address, L1BatchNumber, L1BlockNumber, ProtocolVersionId, H256, }; -use crate::eth_sender::{ - eth_tx_manager::L1BlockNumbers, Aggregator, ETHSenderError, EthTxAggregator, EthTxManager, +use crate::{ + eth_sender::{ + eth_tx_manager::L1BlockNumbers, Aggregator, ETHSenderError, EthTxAggregator, EthTxManager, + }, + l1_gas_price::GasAdjuster, }; -use crate::l1_gas_price::GasAdjuster; // Alias to conveniently call static methods of ETHSender. type MockEthTxManager = EthTxManager, GasAdjuster>>; diff --git a/core/lib/zksync_core/src/eth_watch/event_processors/governance_upgrades.rs b/core/lib/zksync_core/src/eth_watch/event_processors/governance_upgrades.rs index b43fc6fb0505..2f7e2e86b2c7 100644 --- a/core/lib/zksync_core/src/eth_watch/event_processors/governance_upgrades.rs +++ b/core/lib/zksync_core/src/eth_watch/event_processors/governance_upgrades.rs @@ -1,15 +1,16 @@ -use crate::eth_watch::{ - client::{Error, EthClient}, - event_processors::EventProcessor, -}; -use std::convert::TryFrom; -use std::time::Instant; +use std::{convert::TryFrom, time::Instant}; + use zksync_dal::StorageProcessor; use zksync_types::{ ethabi::Contract, protocol_version::GovernanceOperation, web3::types::Log, Address, ProtocolUpgrade, ProtocolVersionId, H256, }; +use crate::eth_watch::{ + client::{Error, EthClient}, + event_processors::EventProcessor, +}; + /// Listens to operation events coming from the governance contract and saves new protocol upgrade proposals to the database. #[derive(Debug)] pub struct GovernanceUpgradesEventProcessor { diff --git a/core/lib/zksync_core/src/eth_watch/event_processors/mod.rs b/core/lib/zksync_core/src/eth_watch/event_processors/mod.rs index 84ea1eeb04cf..202b7efb586a 100644 --- a/core/lib/zksync_core/src/eth_watch/event_processors/mod.rs +++ b/core/lib/zksync_core/src/eth_watch/event_processors/mod.rs @@ -1,7 +1,8 @@ -use crate::eth_watch::client::{Error, EthClient}; use zksync_dal::StorageProcessor; use zksync_types::{web3::types::Log, H256}; +use crate::eth_watch::client::{Error, EthClient}; + pub mod governance_upgrades; pub mod priority_ops; pub mod upgrades; diff --git a/core/lib/zksync_core/src/eth_watch/event_processors/upgrades.rs b/core/lib/zksync_core/src/eth_watch/event_processors/upgrades.rs index 210b540c48e4..497cb705ee1c 100644 --- a/core/lib/zksync_core/src/eth_watch/event_processors/upgrades.rs +++ b/core/lib/zksync_core/src/eth_watch/event_processors/upgrades.rs @@ -1,4 +1,5 @@ use std::convert::TryFrom; + use zksync_dal::StorageProcessor; use zksync_types::{web3::types::Log, ProtocolUpgrade, ProtocolVersionId, H256}; diff --git a/core/lib/zksync_core/src/eth_watch/metrics.rs b/core/lib/zksync_core/src/eth_watch/metrics.rs index e5166f137ca3..c96b8c084833 100644 --- a/core/lib/zksync_core/src/eth_watch/metrics.rs +++ b/core/lib/zksync_core/src/eth_watch/metrics.rs @@ -1,9 +1,9 @@ //! Metrics for Ethereum watcher. -use vise::{Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics}; - use std::time::Duration; +use vise::{Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics}; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelValue, EncodeLabelSet)] #[metrics(label = "stage", rename_all = "snake_case")] pub(super) enum PollStage { diff --git a/core/lib/zksync_core/src/eth_watch/mod.rs b/core/lib/zksync_core/src/eth_watch/mod.rs index fdb629bce28a..69f447e7fba9 100644 --- a/core/lib/zksync_core/src/eth_watch/mod.rs +++ b/core/lib/zksync_core/src/eth_watch/mod.rs @@ -4,10 +4,9 @@ //! Poll interval is configured using the `ETH_POLL_INTERVAL` constant. //! Number of confirmations is configured using the `CONFIRMATIONS_FOR_ETH_EVENT` environment variable. -use tokio::{sync::watch, task::JoinHandle}; - use std::time::Duration; +use tokio::{sync::watch, task::JoinHandle}; use zksync_config::ETHWatchConfig; use zksync_dal::{ConnectionPool, StorageProcessor}; use zksync_eth_client::EthInterface; @@ -17,12 +16,6 @@ use zksync_types::{ ProtocolVersionId, }; -mod client; -mod event_processors; -mod metrics; -#[cfg(test)] -mod tests; - use self::{ client::{Error, EthClient, EthHttpQueryClient, RETRY_LIMIT}, event_processors::{ @@ -32,6 +25,12 @@ use self::{ metrics::{PollStage, METRICS}, }; +mod client; +mod event_processors; +mod metrics; +#[cfg(test)] +mod tests; + #[derive(Debug)] struct EthWatchState { last_seen_version_id: ProtocolVersionId, diff --git a/core/lib/zksync_core/src/eth_watch/tests.rs b/core/lib/zksync_core/src/eth_watch/tests.rs index d7627a56c136..31c467419294 100644 --- a/core/lib/zksync_core/src/eth_watch/tests.rs +++ b/core/lib/zksync_core/src/eth_watch/tests.rs @@ -1,17 +1,13 @@ -use std::collections::HashMap; -use std::convert::TryInto; -use std::sync::Arc; +use std::{collections::HashMap, convert::TryInto, sync::Arc}; use tokio::sync::RwLock; - use zksync_contracts::{governance_contract, zksync_contract}; use zksync_dal::{ConnectionPool, StorageProcessor}; -use zksync_types::protocol_version::{ProtocolUpgradeTx, ProtocolUpgradeTxCommonData}; -use zksync_types::web3::types::{Address, BlockNumber}; use zksync_types::{ ethabi::{encode, Hash, Token}, l1::{L1Tx, OpProcessingType, PriorityQueueType}, - web3::types::Log, + protocol_version::{ProtocolUpgradeTx, ProtocolUpgradeTxCommonData}, + web3::types::{Address, BlockNumber, Log}, Execute, L1TxCommonData, PriorityOpId, ProtocolUpgrade, ProtocolVersion, ProtocolVersionId, Transaction, H256, U256, }; diff --git a/core/lib/zksync_core/src/genesis.rs b/core/lib/zksync_core/src/genesis.rs index 39a8645767d1..01d4628caac1 100644 --- a/core/lib/zksync_core/src/genesis.rs +++ b/core/lib/zksync_core/src/genesis.rs @@ -3,14 +3,13 @@ //! setups the required databases, and outputs the data required to initialize a smart contract. use anyhow::Context as _; - use zksync_contracts::BaseSystemContracts; use zksync_dal::StorageProcessor; use zksync_merkle_tree::domain::ZkSyncTree; - use zksync_types::{ - block::DeployedContract, - block::{legacy_miniblock_hash, BlockGasCount, L1BatchHeader, MiniblockHeader}, + block::{ + legacy_miniblock_hash, BlockGasCount, DeployedContract, L1BatchHeader, MiniblockHeader, + }, commitment::{L1BatchCommitment, L1BatchMetadata}, get_code_key, get_system_context_init_logs, protocol_version::{L1VerifierConfig, ProtocolVersion}, @@ -19,8 +18,7 @@ use zksync_types::{ AccountTreeId, Address, L1BatchNumber, L2ChainId, LogQuery, MiniblockNumber, ProtocolVersionId, StorageKey, StorageLog, StorageLogKind, Timestamp, H256, }; -use zksync_utils::{be_words_to_bytes, h256_to_u256}; -use zksync_utils::{bytecode::hash_bytecode, u256_to_h256}; +use zksync_utils::{be_words_to_bytes, bytecode::hash_bytecode, h256_to_u256, u256_to_h256}; use crate::metadata_calculator::L1BatchWithLogs; diff --git a/core/lib/zksync_core/src/house_keeper/blocks_state_reporter.rs b/core/lib/zksync_core/src/house_keeper/blocks_state_reporter.rs index 6ba94cbac6dd..190764ec57d8 100644 --- a/core/lib/zksync_core/src/house_keeper/blocks_state_reporter.rs +++ b/core/lib/zksync_core/src/house_keeper/blocks_state_reporter.rs @@ -1,5 +1,4 @@ use async_trait::async_trait; - use zksync_dal::ConnectionPool; use zksync_prover_utils::periodic_job::PeriodicJob; use zksync_utils::time::seconds_since_epoch; diff --git a/core/lib/zksync_core/src/house_keeper/fri_proof_compressor_queue_monitor.rs b/core/lib/zksync_core/src/house_keeper/fri_proof_compressor_queue_monitor.rs index 769792b6a584..73c752b69558 100644 --- a/core/lib/zksync_core/src/house_keeper/fri_proof_compressor_queue_monitor.rs +++ b/core/lib/zksync_core/src/house_keeper/fri_proof_compressor_queue_monitor.rs @@ -1,8 +1,7 @@ use async_trait::async_trait; use zksync_dal::ConnectionPool; -use zksync_types::proofs::JobCountStatistics; - use zksync_prover_utils::periodic_job::PeriodicJob; +use zksync_types::proofs::JobCountStatistics; const PROOF_COMPRESSOR_SERVICE_NAME: &str = "proof_compressor"; diff --git a/core/lib/zksync_core/src/house_keeper/fri_scheduler_circuit_queuer.rs b/core/lib/zksync_core/src/house_keeper/fri_scheduler_circuit_queuer.rs index ab9eba1fc66c..0adfdb470551 100644 --- a/core/lib/zksync_core/src/house_keeper/fri_scheduler_circuit_queuer.rs +++ b/core/lib/zksync_core/src/house_keeper/fri_scheduler_circuit_queuer.rs @@ -1,6 +1,5 @@ use async_trait::async_trait; use zksync_dal::ConnectionPool; - use zksync_prover_utils::periodic_job::PeriodicJob; #[derive(Debug)] diff --git a/core/lib/zksync_core/src/house_keeper/fri_witness_generator_queue_monitor.rs b/core/lib/zksync_core/src/house_keeper/fri_witness_generator_queue_monitor.rs index 15b56e165532..67f81295b44f 100644 --- a/core/lib/zksync_core/src/house_keeper/fri_witness_generator_queue_monitor.rs +++ b/core/lib/zksync_core/src/house_keeper/fri_witness_generator_queue_monitor.rs @@ -2,9 +2,8 @@ use std::collections::HashMap; use async_trait::async_trait; use zksync_dal::ConnectionPool; -use zksync_types::proofs::{AggregationRound, JobCountStatistics}; - use zksync_prover_utils::periodic_job::PeriodicJob; +use zksync_types::proofs::{AggregationRound, JobCountStatistics}; const FRI_WITNESS_GENERATOR_SERVICE_NAME: &str = "fri_witness_generator"; diff --git a/core/lib/zksync_core/src/house_keeper/gpu_prover_queue_monitor.rs b/core/lib/zksync_core/src/house_keeper/gpu_prover_queue_monitor.rs index 7ddb1bd75dd0..ab96b52bedc9 100644 --- a/core/lib/zksync_core/src/house_keeper/gpu_prover_queue_monitor.rs +++ b/core/lib/zksync_core/src/house_keeper/gpu_prover_queue_monitor.rs @@ -1,6 +1,5 @@ use async_trait::async_trait; use zksync_dal::ConnectionPool; - use zksync_prover_utils::periodic_job::PeriodicJob; #[derive(Debug)] diff --git a/core/lib/zksync_core/src/house_keeper/prover_job_retry_manager.rs b/core/lib/zksync_core/src/house_keeper/prover_job_retry_manager.rs index 4142f1d57664..f7b630475ea0 100644 --- a/core/lib/zksync_core/src/house_keeper/prover_job_retry_manager.rs +++ b/core/lib/zksync_core/src/house_keeper/prover_job_retry_manager.rs @@ -2,7 +2,6 @@ use std::time::Duration; use async_trait::async_trait; use zksync_dal::ConnectionPool; - use zksync_prover_utils::periodic_job::PeriodicJob; #[derive(Debug)] diff --git a/core/lib/zksync_core/src/house_keeper/prover_queue_monitor.rs b/core/lib/zksync_core/src/house_keeper/prover_queue_monitor.rs index 5b41ee74ac9a..e0f598d5a590 100644 --- a/core/lib/zksync_core/src/house_keeper/prover_queue_monitor.rs +++ b/core/lib/zksync_core/src/house_keeper/prover_queue_monitor.rs @@ -1,9 +1,7 @@ use async_trait::async_trait; use zksync_config::configs::ProverGroupConfig; use zksync_dal::ConnectionPool; -use zksync_prover_utils::circuit_name_to_numeric_index; - -use zksync_prover_utils::periodic_job::PeriodicJob; +use zksync_prover_utils::{circuit_name_to_numeric_index, periodic_job::PeriodicJob}; #[derive(Debug)] pub struct ProverStatsReporter { diff --git a/core/lib/zksync_core/src/house_keeper/waiting_to_queued_fri_witness_job_mover.rs b/core/lib/zksync_core/src/house_keeper/waiting_to_queued_fri_witness_job_mover.rs index 2fd00bcd6f6d..1292ee3f44fa 100644 --- a/core/lib/zksync_core/src/house_keeper/waiting_to_queued_fri_witness_job_mover.rs +++ b/core/lib/zksync_core/src/house_keeper/waiting_to_queued_fri_witness_job_mover.rs @@ -1,6 +1,5 @@ use async_trait::async_trait; use zksync_dal::ConnectionPool; - use zksync_prover_utils::periodic_job::PeriodicJob; #[derive(Debug)] diff --git a/core/lib/zksync_core/src/house_keeper/waiting_to_queued_witness_job_mover.rs b/core/lib/zksync_core/src/house_keeper/waiting_to_queued_witness_job_mover.rs index c99603676ec6..4521b4bfc47b 100644 --- a/core/lib/zksync_core/src/house_keeper/waiting_to_queued_witness_job_mover.rs +++ b/core/lib/zksync_core/src/house_keeper/waiting_to_queued_witness_job_mover.rs @@ -1,6 +1,5 @@ use async_trait::async_trait; use zksync_dal::ConnectionPool; - use zksync_prover_utils::periodic_job::PeriodicJob; #[derive(Debug)] diff --git a/core/lib/zksync_core/src/house_keeper/witness_generator_queue_monitor.rs b/core/lib/zksync_core/src/house_keeper/witness_generator_queue_monitor.rs index 40a8e2a66133..da816da3c668 100644 --- a/core/lib/zksync_core/src/house_keeper/witness_generator_queue_monitor.rs +++ b/core/lib/zksync_core/src/house_keeper/witness_generator_queue_monitor.rs @@ -2,9 +2,8 @@ use std::collections::HashMap; use async_trait::async_trait; use zksync_dal::ConnectionPool; -use zksync_types::proofs::{AggregationRound, JobCountStatistics}; - use zksync_prover_utils::periodic_job::PeriodicJob; +use zksync_types::proofs::{AggregationRound, JobCountStatistics}; const WITNESS_GENERATOR_SERVICE_NAME: &str = "witness_generator"; diff --git a/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/mod.rs b/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/mod.rs index 9a8825190eeb..dd9806f998c1 100644 --- a/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/mod.rs +++ b/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/mod.rs @@ -1,23 +1,22 @@ //! This module determines the fees to pay in txs containing blocks submitted to the L1. -use tokio::sync::watch; - use std::{ collections::VecDeque, sync::{Arc, RwLock}, }; +use tokio::sync::watch; use zksync_config::GasAdjusterConfig; use zksync_eth_client::{types::Error, EthInterface}; +use self::metrics::METRICS; +use super::{L1GasPriceProvider, L1TxParamsProvider}; + pub mod bounded_gas_adjuster; mod metrics; #[cfg(test)] mod tests; -use self::metrics::METRICS; -use super::{L1GasPriceProvider, L1TxParamsProvider}; - /// This component keeps track of the median base_fee from the last `max_base_fee_samples` blocks. /// It is used to adjust the base_fee of transactions sent to L1. #[derive(Debug)] diff --git a/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/tests.rs b/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/tests.rs index a0c6dac365c7..84ace37ecec4 100644 --- a/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/tests.rs +++ b/core/lib/zksync_core/src/l1_gas_price/gas_adjuster/tests.rs @@ -1,9 +1,10 @@ -use super::{GasAdjuster, GasStatisticsInner}; -use std::collections::VecDeque; -use std::sync::Arc; +use std::{collections::VecDeque, sync::Arc}; + use zksync_config::GasAdjusterConfig; use zksync_eth_client::clients::mock::MockEthereum; +use super::{GasAdjuster, GasStatisticsInner}; + /// Check that we compute the median correctly #[test] fn median() { diff --git a/core/lib/zksync_core/src/l1_gas_price/main_node_fetcher.rs b/core/lib/zksync_core/src/l1_gas_price/main_node_fetcher.rs index 2244607a47e8..a3f7b92f0e46 100644 --- a/core/lib/zksync_core/src/l1_gas_price/main_node_fetcher.rs +++ b/core/lib/zksync_core/src/l1_gas_price/main_node_fetcher.rs @@ -7,7 +7,6 @@ use std::{ }; use tokio::sync::watch::Receiver; - use zksync_web3_decl::{ jsonrpsee::http_client::{HttpClient, HttpClientBuilder}, namespaces::ZksNamespaceClient, diff --git a/core/lib/zksync_core/src/l1_gas_price/mod.rs b/core/lib/zksync_core/src/l1_gas_price/mod.rs index 45e228d79c54..bab30c035843 100644 --- a/core/lib/zksync_core/src/l1_gas_price/mod.rs +++ b/core/lib/zksync_core/src/l1_gas_price/mod.rs @@ -1,7 +1,6 @@ //! This module determines the fees to pay in txs containing blocks submitted to the L1. -pub use gas_adjuster::bounded_gas_adjuster::BoundedGasAdjuster; -pub use gas_adjuster::GasAdjuster; +pub use gas_adjuster::{bounded_gas_adjuster::BoundedGasAdjuster, GasAdjuster}; pub use main_node_fetcher::MainNodeGasPriceFetcher; pub use singleton::GasAdjusterSingleton; diff --git a/core/lib/zksync_core/src/l1_gas_price/singleton.rs b/core/lib/zksync_core/src/l1_gas_price/singleton.rs index 4808dee548ba..0c70ba2466ca 100644 --- a/core/lib/zksync_core/src/l1_gas_price/singleton.rs +++ b/core/lib/zksync_core/src/l1_gas_price/singleton.rs @@ -1,11 +1,15 @@ -use crate::l1_gas_price::{BoundedGasAdjuster, GasAdjuster}; -use anyhow::Context as _; use std::sync::Arc; -use tokio::sync::{watch, OnceCell}; -use tokio::task::JoinHandle; + +use anyhow::Context as _; +use tokio::{ + sync::{watch, OnceCell}, + task::JoinHandle, +}; use zksync_config::GasAdjusterConfig; use zksync_eth_client::clients::http::QueryClient; +use crate::l1_gas_price::{BoundedGasAdjuster, GasAdjuster}; + /// Special struct for creating a singleton of `GasAdjuster`. /// This is needed only for running the server. #[derive(Debug)] diff --git a/core/lib/zksync_core/src/lib.rs b/core/lib/zksync_core/src/lib.rs index 2389d5761738..5406f0bbd897 100644 --- a/core/lib/zksync_core/src/lib.rs +++ b/core/lib/zksync_core/src/lib.rs @@ -7,7 +7,6 @@ use futures::channel::oneshot; use prometheus_exporter::PrometheusExporterConfig; use temp_config_store::TempConfigStore; use tokio::{sync::watch, task::JoinHandle}; - use zksync_circuit_breaker::{ l1_txs::FailedL1TransactionChecker, replication_lag::ReplicationLagChecker, CircuitBreaker, CircuitBreakerChecker, CircuitBreakerError, @@ -26,9 +25,10 @@ use zksync_config::{ }; use zksync_contracts::{governance_contract, BaseSystemContracts}; use zksync_dal::{healthcheck::ConnectionPoolHealthCheck, ConnectionPool}; -use zksync_eth_client::clients::http::QueryClient; -use zksync_eth_client::EthInterface; -use zksync_eth_client::{clients::http::PKSigningClient, BoundEthInterface}; +use zksync_eth_client::{ + clients::http::{PKSigningClient, QueryClient}, + BoundEthInterface, EthInterface, +}; use zksync_health_check::{CheckHealth, HealthStatus, ReactiveHealthCheck}; use zksync_object_store::{ObjectStore, ObjectStoreFactory}; use zksync_prover_utils::periodic_job::PeriodicJob; @@ -42,6 +42,46 @@ use zksync_types::{ }; use zksync_verification_key_server::get_cached_commitments; +use crate::{ + api_server::{ + contract_verification, + execution_sandbox::{VmConcurrencyBarrier, VmConcurrencyLimiter}, + healthcheck::HealthCheckHandle, + tx_sender::{ApiContracts, TxSender, TxSenderBuilder, TxSenderConfig}, + web3, + web3::{state::InternalApiConfig, ApiServerHandles, Namespace}, + }, + basic_witness_input_producer::BasicWitnessInputProducer, + data_fetchers::run_data_fetchers, + eth_sender::{Aggregator, EthTxAggregator, EthTxManager}, + eth_watch::start_eth_watch, + house_keeper::{ + blocks_state_reporter::L1BatchMetricsReporter, + fri_proof_compressor_job_retry_manager::FriProofCompressorJobRetryManager, + fri_proof_compressor_queue_monitor::FriProofCompressorStatsReporter, + fri_prover_job_retry_manager::FriProverJobRetryManager, + fri_prover_queue_monitor::FriProverStatsReporter, + fri_scheduler_circuit_queuer::SchedulerCircuitQueuer, + fri_witness_generator_jobs_retry_manager::FriWitnessGeneratorJobRetryManager, + fri_witness_generator_queue_monitor::FriWitnessGeneratorStatsReporter, + gpu_prover_queue_monitor::GpuProverQueueMonitor, + prover_job_retry_manager::ProverJobRetryManager, prover_queue_monitor::ProverStatsReporter, + waiting_to_queued_fri_witness_job_mover::WaitingToQueuedFriWitnessJobMover, + waiting_to_queued_witness_job_mover::WaitingToQueuedWitnessJobMover, + witness_generator_queue_monitor::WitnessGeneratorStatsReporter, + }, + l1_gas_price::{GasAdjusterSingleton, L1GasPriceProvider}, + metadata_calculator::{ + MetadataCalculator, MetadataCalculatorConfig, MetadataCalculatorModeConfig, + }, + metrics::{InitStage, APP_METRICS}, + state_keeper::{create_state_keeper, MempoolFetcher, MempoolGuard, MiniblockSealer}, + witness_generator::{ + basic_circuits::BasicWitnessGenerator, leaf_aggregation::LeafAggregationWitnessGenerator, + node_aggregation::NodeAggregationWitnessGenerator, scheduler::SchedulerWitnessGenerator, + }, +}; + pub mod api_server; pub mod basic_witness_input_producer; pub mod block_reverter; @@ -63,47 +103,6 @@ pub mod sync_layer; pub mod temp_config_store; pub mod witness_generator; -use crate::api_server::healthcheck::HealthCheckHandle; -use crate::api_server::tx_sender::{TxSender, TxSenderBuilder, TxSenderConfig}; -use crate::api_server::web3::{state::InternalApiConfig, ApiServerHandles, Namespace}; -use crate::basic_witness_input_producer::BasicWitnessInputProducer; -use crate::eth_sender::{Aggregator, EthTxManager}; -use crate::house_keeper::fri_proof_compressor_job_retry_manager::FriProofCompressorJobRetryManager; -use crate::house_keeper::fri_proof_compressor_queue_monitor::FriProofCompressorStatsReporter; -use crate::house_keeper::fri_prover_job_retry_manager::FriProverJobRetryManager; -use crate::house_keeper::fri_prover_queue_monitor::FriProverStatsReporter; -use crate::house_keeper::fri_scheduler_circuit_queuer::SchedulerCircuitQueuer; -use crate::house_keeper::fri_witness_generator_jobs_retry_manager::FriWitnessGeneratorJobRetryManager; -use crate::house_keeper::fri_witness_generator_queue_monitor::FriWitnessGeneratorStatsReporter; -use crate::house_keeper::{ - blocks_state_reporter::L1BatchMetricsReporter, gpu_prover_queue_monitor::GpuProverQueueMonitor, - prover_job_retry_manager::ProverJobRetryManager, prover_queue_monitor::ProverStatsReporter, - waiting_to_queued_fri_witness_job_mover::WaitingToQueuedFriWitnessJobMover, - waiting_to_queued_witness_job_mover::WaitingToQueuedWitnessJobMover, - witness_generator_queue_monitor::WitnessGeneratorStatsReporter, -}; -use crate::l1_gas_price::{GasAdjusterSingleton, L1GasPriceProvider}; -use crate::metadata_calculator::{ - MetadataCalculator, MetadataCalculatorConfig, MetadataCalculatorModeConfig, -}; -use crate::state_keeper::{create_state_keeper, MempoolFetcher, MempoolGuard, MiniblockSealer}; -use crate::witness_generator::{ - basic_circuits::BasicWitnessGenerator, leaf_aggregation::LeafAggregationWitnessGenerator, - node_aggregation::NodeAggregationWitnessGenerator, scheduler::SchedulerWitnessGenerator, -}; -use crate::{ - api_server::{ - contract_verification, - execution_sandbox::{VmConcurrencyBarrier, VmConcurrencyLimiter}, - tx_sender::ApiContracts, - web3, - }, - data_fetchers::run_data_fetchers, - eth_sender::EthTxAggregator, - eth_watch::start_eth_watch, - metrics::{InitStage, APP_METRICS}, -}; - /// Inserts the initial information about zkSync tokens into the database. pub async fn genesis_init( postgres_config: &PostgresConfig, diff --git a/core/lib/zksync_core/src/metadata_calculator/helpers.rs b/core/lib/zksync_core/src/metadata_calculator/helpers.rs index 9ae936febfe6..790beca30706 100644 --- a/core/lib/zksync_core/src/metadata_calculator/helpers.rs +++ b/core/lib/zksync_core/src/metadata_calculator/helpers.rs @@ -1,9 +1,5 @@ //! Various helpers for the metadata calculator. -use serde::{Deserialize, Serialize}; -#[cfg(test)] -use tokio::sync::mpsc; - use std::{ collections::BTreeMap, future::Future, @@ -11,6 +7,9 @@ use std::{ time::Duration, }; +use serde::{Deserialize, Serialize}; +#[cfg(test)] +use tokio::sync::mpsc; use zksync_config::configs::database::MerkleTreeMode; use zksync_dal::StorageProcessor; use zksync_health_check::{Health, HealthStatus}; @@ -327,7 +326,6 @@ impl L1BatchWithLogs { #[cfg(test)] mod tests { use tempfile::TempDir; - use zksync_dal::ConnectionPool; use zksync_types::{proofs::PrepareBasicCircuitsJob, L2ChainId, StorageKey, StorageLog}; diff --git a/core/lib/zksync_core/src/metadata_calculator/metrics.rs b/core/lib/zksync_core/src/metadata_calculator/metrics.rs index f8ef8f85b641..da4995bdbf9c 100644 --- a/core/lib/zksync_core/src/metadata_calculator/metrics.rs +++ b/core/lib/zksync_core/src/metadata_calculator/metrics.rs @@ -1,11 +1,10 @@ //! Metrics for `MetadataCalculator`. +use std::time::{Duration, Instant}; + use vise::{ Buckets, EncodeLabelSet, EncodeLabelValue, Family, Gauge, Histogram, LatencyObserver, Metrics, }; - -use std::time::{Duration, Instant}; - use zksync_types::block::L1BatchHeader; use zksync_utils::time::seconds_since_epoch; diff --git a/core/lib/zksync_core/src/metadata_calculator/mod.rs b/core/lib/zksync_core/src/metadata_calculator/mod.rs index 7289347fec0d..31b39a909527 100644 --- a/core/lib/zksync_core/src/metadata_calculator/mod.rs +++ b/core/lib/zksync_core/src/metadata_calculator/mod.rs @@ -1,10 +1,9 @@ //! This module applies updates to the ZkSyncTree, calculates metadata for sealed blocks, and //! stores them in the DB. -use tokio::sync::watch; - use std::time::Duration; +use tokio::sync::watch; use zksync_config::configs::{ chain::OperationsManagerConfig, database::{MerkleTreeConfig, MerkleTreeMode}, @@ -19,12 +18,6 @@ use zksync_types::{ H256, }; -mod helpers; -mod metrics; -#[cfg(test)] -pub(crate) mod tests; -mod updater; - pub(crate) use self::helpers::{AsyncTreeReader, L1BatchWithLogs, MerkleTreeInfo}; use self::{ helpers::Delayer, @@ -33,6 +26,12 @@ use self::{ }; use crate::gas_tracker::commit_gas_count_for_l1_batch; +mod helpers; +mod metrics; +#[cfg(test)] +pub(crate) mod tests; +mod updater; + /// Part of [`MetadataCalculator`] related to the operation mode of the Merkle tree. #[derive(Debug, Clone, Copy)] pub enum MetadataCalculatorModeConfig<'a> { diff --git a/core/lib/zksync_core/src/metadata_calculator/tests.rs b/core/lib/zksync_core/src/metadata_calculator/tests.rs index 5e86db6087be..85d179fe3b05 100644 --- a/core/lib/zksync_core/src/metadata_calculator/tests.rs +++ b/core/lib/zksync_core/src/metadata_calculator/tests.rs @@ -1,10 +1,9 @@ +use std::{future::Future, ops, panic, path::Path, time::Duration}; + use assert_matches::assert_matches; use itertools::Itertools; use tempfile::TempDir; use tokio::sync::{mpsc, watch}; - -use std::{future::Future, ops, panic, path::Path, time::Duration}; - use zksync_config::configs::{chain::OperationsManagerConfig, database::MerkleTreeConfig}; use zksync_contracts::BaseSystemContracts; use zksync_dal::{ConnectionPool, StorageProcessor}; diff --git a/core/lib/zksync_core/src/metadata_calculator/updater.rs b/core/lib/zksync_core/src/metadata_calculator/updater.rs index ed38dae14eda..87127947fbd2 100644 --- a/core/lib/zksync_core/src/metadata_calculator/updater.rs +++ b/core/lib/zksync_core/src/metadata_calculator/updater.rs @@ -1,11 +1,10 @@ //! Tree updater trait and its implementations. +use std::{ops, time::Instant}; + use anyhow::Context as _; use futures::{future, FutureExt}; use tokio::sync::watch; - -use std::{ops, time::Instant}; - use zksync_commitment_utils::{bootloader_initial_content_commitment, events_queue_commitment}; use zksync_config::configs::database::MerkleTreeMode; use zksync_dal::{ConnectionPool, StorageProcessor}; diff --git a/core/lib/zksync_core/src/metrics.rs b/core/lib/zksync_core/src/metrics.rs index 539cbbbb2fbb..0206c2647591 100644 --- a/core/lib/zksync_core/src/metrics.rs +++ b/core/lib/zksync_core/src/metrics.rs @@ -1,9 +1,8 @@ //! Application-wide metrics. -use vise::{Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Gauge, Histogram, Metrics}; - use std::{fmt, time::Duration}; +use vise::{Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Gauge, Histogram, Metrics}; use zksync_dal::transactions_dal::L2TxSubmissionResult; use zksync_types::{aggregated_operations::AggregatedActionType, proofs::AggregationRound}; diff --git a/core/lib/zksync_core/src/proof_data_handler/mod.rs b/core/lib/zksync_core/src/proof_data_handler/mod.rs index 898ac4652ba2..f1227d8298c9 100644 --- a/core/lib/zksync_core/src/proof_data_handler/mod.rs +++ b/core/lib/zksync_core/src/proof_data_handler/mod.rs @@ -1,8 +1,7 @@ -use crate::proof_data_handler::request_processor::RequestProcessor; -use anyhow::Context as _; -use axum::extract::Path; -use axum::{routing::post, Json, Router}; use std::net::SocketAddr; + +use anyhow::Context as _; +use axum::{extract::Path, routing::post, Json, Router}; use tokio::sync::watch; use zksync_config::{ configs::{proof_data_handler::ProtocolVersionLoadingMode, ProofDataHandlerConfig}, @@ -16,6 +15,8 @@ use zksync_types::{ H256, }; +use crate::proof_data_handler::request_processor::RequestProcessor; + mod request_processor; fn fri_l1_verifier_config(contracts_config: &ContractsConfig) -> L1VerifierConfig { diff --git a/core/lib/zksync_core/src/proof_data_handler/request_processor.rs b/core/lib/zksync_core/src/proof_data_handler/request_processor.rs index 866990b31c92..5a3302ee926d 100644 --- a/core/lib/zksync_core/src/proof_data_handler/request_processor.rs +++ b/core/lib/zksync_core/src/proof_data_handler/request_processor.rs @@ -1,26 +1,27 @@ -use axum::extract::Path; -use axum::response::Response; -use axum::{http::StatusCode, response::IntoResponse, Json}; -use std::convert::TryFrom; -use std::sync::Arc; +use std::{convert::TryFrom, sync::Arc}; + +use axum::{ + extract::Path, + http::StatusCode, + response::{IntoResponse, Response}, + Json, +}; use zksync_config::configs::{ proof_data_handler::ProtocolVersionLoadingMode, ProofDataHandlerConfig, }; -use zksync_types::commitment::serialize_commitments; -use zksync_types::web3::signing::keccak256; -use zksync_utils::u256_to_h256; - use zksync_dal::{ConnectionPool, SqlxError}; use zksync_object_store::{ObjectStore, ObjectStoreError}; -use zksync_types::protocol_version::FriProtocolVersionId; use zksync_types::{ - protocol_version::L1VerifierConfig, + commitment::serialize_commitments, + protocol_version::{FriProtocolVersionId, L1VerifierConfig}, prover_server_api::{ ProofGenerationData, ProofGenerationDataRequest, ProofGenerationDataResponse, SubmitProofRequest, SubmitProofResponse, }, + web3::signing::keccak256, L1BatchNumber, H256, }; +use zksync_utils::u256_to_h256; #[derive(Clone)] pub(crate) struct RequestProcessor { diff --git a/core/lib/zksync_core/src/reorg_detector/mod.rs b/core/lib/zksync_core/src/reorg_detector/mod.rs index 16137d40af67..cd399716c201 100644 --- a/core/lib/zksync_core/src/reorg_detector/mod.rs +++ b/core/lib/zksync_core/src/reorg_detector/mod.rs @@ -4,8 +4,10 @@ use tokio::sync::watch; use zksync_dal::ConnectionPool; use zksync_types::{L1BatchNumber, MiniblockNumber}; use zksync_web3_decl::{ - jsonrpsee::core::Error as RpcError, - jsonrpsee::http_client::{HttpClient, HttpClientBuilder}, + jsonrpsee::{ + core::Error as RpcError, + http_client::{HttpClient, HttpClientBuilder}, + }, namespaces::{EthNamespaceClient, ZksNamespaceClient}, RpcResult, }; diff --git a/core/lib/zksync_core/src/state_keeper/batch_executor/mod.rs b/core/lib/zksync_core/src/state_keeper/batch_executor/mod.rs index 389677b04397..2267792297fd 100644 --- a/core/lib/zksync_core/src/state_keeper/batch_executor/mod.rs +++ b/core/lib/zksync_core/src/state_keeper/batch_executor/mod.rs @@ -1,13 +1,6 @@ -use async_trait::async_trait; -use once_cell::sync::OnceCell; -use tokio::{ - sync::{mpsc, oneshot}, - task::JoinHandle, -}; - -use multivm::MultiVMTracer; use std::{fmt, sync::Arc}; +use async_trait::async_trait; use multivm::{ interface::{ ExecutionResult, FinishedL1Batch, Halt, L1BatchEnv, L2BlockEnv, SystemEnv, VmExecutionMode, @@ -15,16 +8,18 @@ use multivm::{ }, tracers::CallTracer, vm_latest::HistoryEnabled, - VmInstance, + MultiVMTracer, VmInstance, +}; +use once_cell::sync::OnceCell; +use tokio::{ + sync::{mpsc, oneshot}, + task::JoinHandle, }; use zksync_dal::ConnectionPool; use zksync_state::{RocksdbStorage, StorageView, WriteStorage}; use zksync_types::{vm_trace::Call, witness_block_state::WitnessBlockState, Transaction, U256}; use zksync_utils::bytecode::CompressedBytecodeInfo; -#[cfg(test)] -mod tests; - use crate::{ gas_tracker::{gas_count_from_metrics, gas_count_from_tx_and_metrics}, metrics::{InteractionType, TxStage, APP_METRICS}, @@ -34,6 +29,9 @@ use crate::{ }, }; +#[cfg(test)] +mod tests; + /// Representation of a transaction executed in the virtual machine. #[derive(Debug, Clone)] pub(crate) enum TxExecutionResult { diff --git a/core/lib/zksync_core/src/state_keeper/batch_executor/tests/mod.rs b/core/lib/zksync_core/src/state_keeper/batch_executor/tests/mod.rs index 05a8220bb832..362afe20437d 100644 --- a/core/lib/zksync_core/src/state_keeper/batch_executor/tests/mod.rs +++ b/core/lib/zksync_core/src/state_keeper/batch_executor/tests/mod.rs @@ -1,15 +1,13 @@ use assert_matches::assert_matches; - use zksync_dal::ConnectionPool; +use zksync_test_account::Account; use zksync_types::PriorityOpId; -mod tester; - use self::tester::Tester; use super::TxExecutionResult; use crate::state_keeper::batch_executor::tests::tester::{AccountLoadNextExecutable, TestConfig}; -use zksync_test_account::Account; +mod tester; /// Ensures that the transaction was executed successfully. fn assert_executed(execution_result: &TxExecutionResult) { diff --git a/core/lib/zksync_core/src/state_keeper/batch_executor/tests/tester.rs b/core/lib/zksync_core/src/state_keeper/batch_executor/tests/tester.rs index cd72f3eeb074..6417c65a5f8f 100644 --- a/core/lib/zksync_core/src/state_keeper/batch_executor/tests/tester.rs +++ b/core/lib/zksync_core/src/state_keeper/batch_executor/tests/tester.rs @@ -1,11 +1,11 @@ //! Testing harness for the batch executor. //! Contains helper functionality to initialize test context and perform tests without too much boilerplate. +use multivm::{ + interface::{L1BatchEnv, SystemEnv}, + vm_latest::constants::INITIAL_STORAGE_WRITE_PUBDATA_BYTES, +}; use tempfile::TempDir; - -use multivm::interface::{L1BatchEnv, SystemEnv}; -use multivm::vm_latest::constants::INITIAL_STORAGE_WRITE_PUBDATA_BYTES; - use zksync_config::configs::chain::StateKeeperConfig; use zksync_contracts::{get_loadnext_contract, test_contracts::LoadnextContractExecutionParams}; use zksync_dal::ConnectionPool; @@ -19,10 +19,12 @@ use zksync_types::{ }; use zksync_utils::u256_to_h256; -use crate::genesis::create_genesis_l1_batch; -use crate::state_keeper::{ - batch_executor::BatchExecutorHandle, - tests::{default_l1_batch_env, default_system_env, BASE_SYSTEM_CONTRACTS}, +use crate::{ + genesis::create_genesis_l1_batch, + state_keeper::{ + batch_executor::BatchExecutorHandle, + tests::{default_l1_batch_env, default_system_env, BASE_SYSTEM_CONTRACTS}, + }, }; const DEFAULT_GAS_PER_PUBDATA: u32 = 100; diff --git a/core/lib/zksync_core/src/state_keeper/extractors.rs b/core/lib/zksync_core/src/state_keeper/extractors.rs index e542b5b0959a..e31020734f58 100644 --- a/core/lib/zksync_core/src/state_keeper/extractors.rs +++ b/core/lib/zksync_core/src/state_keeper/extractors.rs @@ -1,13 +1,12 @@ //! Pure functions that convert data as required by the state keeper. -use chrono::{DateTime, TimeZone, Utc}; - use std::{ convert::TryFrom, fmt, time::{Duration, Instant}, }; +use chrono::{DateTime, TimeZone, Utc}; use zksync_dal::StorageProcessor; use zksync_types::{L1BatchNumber, U256}; use zksync_utils::h256_to_u256; diff --git a/core/lib/zksync_core/src/state_keeper/io/common.rs b/core/lib/zksync_core/src/state_keeper/io/common.rs index c99508322efc..857c7618d11a 100644 --- a/core/lib/zksync_core/src/state_keeper/io/common.rs +++ b/core/lib/zksync_core/src/state_keeper/io/common.rs @@ -1,7 +1,9 @@ use std::time::Duration; -use multivm::interface::{L1BatchEnv, L2BlockEnv, SystemEnv, TxExecutionMode}; -use multivm::vm_latest::constants::BLOCK_GAS_LIMIT; +use multivm::{ + interface::{L1BatchEnv, L2BlockEnv, SystemEnv, TxExecutionMode}, + vm_latest::constants::BLOCK_GAS_LIMIT, +}; use zksync_contracts::BaseSystemContracts; use zksync_dal::StorageProcessor; use zksync_types::{ diff --git a/core/lib/zksync_core/src/state_keeper/io/mempool.rs b/core/lib/zksync_core/src/state_keeper/io/mempool.rs index 1d3ad506df6b..f5a4df1c333d 100644 --- a/core/lib/zksync_core/src/state_keeper/io/mempool.rs +++ b/core/lib/zksync_core/src/state_keeper/io/mempool.rs @@ -1,5 +1,3 @@ -use async_trait::async_trait; - use std::{ cmp, collections::HashMap, @@ -7,9 +5,11 @@ use std::{ time::{Duration, Instant}, }; -use multivm::interface::{FinishedL1Batch, L1BatchEnv, SystemEnv}; -use multivm::vm_latest::utils::fee::derive_base_fee_and_gas_per_pubdata; - +use async_trait::async_trait; +use multivm::{ + interface::{FinishedL1Batch, L1BatchEnv, SystemEnv}, + vm_latest::utils::fee::derive_base_fee_and_gas_per_pubdata, +}; use zksync_config::configs::chain::StateKeeperConfig; use zksync_dal::ConnectionPool; use zksync_mempool::L2TxFilter; @@ -526,7 +526,6 @@ impl MempoolIO { #[cfg(test)] mod tests { use tokio::time::timeout_at; - use zksync_utils::time::seconds_since_epoch; use super::*; diff --git a/core/lib/zksync_core/src/state_keeper/io/mod.rs b/core/lib/zksync_core/src/state_keeper/io/mod.rs index 313c363418d9..d1366858116c 100644 --- a/core/lib/zksync_core/src/state_keeper/io/mod.rs +++ b/core/lib/zksync_core/src/state_keeper/io/mod.rs @@ -1,13 +1,11 @@ -use async_trait::async_trait; -use tokio::sync::{mpsc, oneshot}; - use std::{ fmt, time::{Duration, Instant}, }; +use async_trait::async_trait; use multivm::interface::{FinishedL1Batch, L1BatchEnv, SystemEnv}; - +use tokio::sync::{mpsc, oneshot}; use zksync_dal::ConnectionPool; use zksync_types::{ block::MiniblockExecutionData, protocol_version::ProtocolUpgradeTx, @@ -15,10 +13,6 @@ use zksync_types::{ Transaction, }; -pub(crate) mod common; -pub(crate) mod mempool; -pub(crate) mod seal_logic; - pub(crate) use self::mempool::MempoolIO; use super::{ metrics::{MiniblockQueueStage, MINIBLOCK_METRICS}, @@ -26,6 +20,9 @@ use super::{ updates::{MiniblockSealCommand, UpdatesManager}, }; +pub(crate) mod common; +pub(crate) mod mempool; +pub(crate) mod seal_logic; #[cfg(test)] mod tests; diff --git a/core/lib/zksync_core/src/state_keeper/io/seal_logic.rs b/core/lib/zksync_core/src/state_keeper/io/seal_logic.rs index 4501be62f785..e152709cff5d 100644 --- a/core/lib/zksync_core/src/state_keeper/io/seal_logic.rs +++ b/core/lib/zksync_core/src/state_keeper/io/seal_logic.rs @@ -1,26 +1,21 @@ //! This module is a source-of-truth on what is expected to be done when sealing a block. //! It contains the logic of the block sealing, which is used by both the mempool-based and external node IO. -use itertools::Itertools; use std::{ collections::HashMap, time::{Duration, Instant}, }; +use itertools::Itertools; use multivm::interface::{FinishedL1Batch, L1BatchEnv}; -use zksync_dal::blocks_dal::ConsensusBlockFields; -use zksync_dal::StorageProcessor; +use zksync_dal::{blocks_dal::ConsensusBlockFields, StorageProcessor}; use zksync_system_constants::ACCOUNT_CODE_STORAGE_ADDRESS; use zksync_types::{ - block::unpack_block_info, - l2_to_l1_log::{SystemL2ToL1Log, UserL2ToL1Log}, - CURRENT_VIRTUAL_BLOCK_INFO_POSITION, SYSTEM_CONTEXT_ADDRESS, -}; -use zksync_types::{ - block::{L1BatchHeader, MiniblockHeader}, + block::{unpack_block_info, L1BatchHeader, MiniblockHeader}, event::{extract_added_tokens, extract_long_l2_to_l1_messages}, l1::L1Tx, l2::L2Tx, + l2_to_l1_log::{SystemL2ToL1Log, UserL2ToL1Log}, protocol_version::ProtocolUpgradeTx, storage_writes_deduplicator::{ModifiedSlot, StorageWritesDeduplicator}, tx::{ @@ -30,7 +25,7 @@ use zksync_types::{ zkevm_test_harness::witness::sort_storage_access::sort_storage_access_queries, AccountTreeId, Address, ExecuteTransactionCommon, L1BatchNumber, L1BlockNumber, LogQuery, MiniblockNumber, StorageKey, StorageLog, StorageLogQuery, StorageValue, Transaction, VmEvent, - H256, + CURRENT_VIRTUAL_BLOCK_INFO_POSITION, H256, SYSTEM_CONTEXT_ADDRESS, }; // TODO (SMA-1206): use seconds instead of milliseconds. use zksync_utils::{h256_to_u256, time::millis_since_epoch, u256_to_h256}; diff --git a/core/lib/zksync_core/src/state_keeper/io/tests/mod.rs b/core/lib/zksync_core/src/state_keeper/io/tests/mod.rs index 2b924554f27d..ef8c54248541 100644 --- a/core/lib/zksync_core/src/state_keeper/io/tests/mod.rs +++ b/core/lib/zksync_core/src/state_keeper/io/tests/mod.rs @@ -1,7 +1,6 @@ -use futures::FutureExt; - use std::time::Duration; +use futures::FutureExt; use multivm::vm_latest::utils::fee::derive_base_fee_and_gas_per_pubdata; use zksync_contracts::BaseSystemContractsHashes; use zksync_dal::ConnectionPool; @@ -12,22 +11,19 @@ use zksync_types::{ }; use zksync_utils::time::seconds_since_epoch; -use crate::state_keeper::tests::{create_l1_batch_metadata, default_l1_batch_env}; - +use self::tester::Tester; use crate::state_keeper::{ io::{MiniblockParams, MiniblockSealer, StateKeeperIO}, mempool_actor::l2_tx_filter, tests::{ - create_execution_result, create_transaction, create_updates_manager, - default_vm_block_result, Query, + create_execution_result, create_l1_batch_metadata, create_transaction, + create_updates_manager, default_l1_batch_env, default_vm_block_result, Query, }, updates::{MiniblockSealCommand, MiniblockUpdates, UpdatesManager}, }; mod tester; -use self::tester::Tester; - /// Ensure that MempoolIO.filter is correctly initialized right after mempool initialization. #[tokio::test] async fn test_filter_initialization() { diff --git a/core/lib/zksync_core/src/state_keeper/io/tests/tester.rs b/core/lib/zksync_core/src/state_keeper/io/tests/tester.rs index 875bf89e0481..5528ae9f206a 100644 --- a/core/lib/zksync_core/src/state_keeper/io/tests/tester.rs +++ b/core/lib/zksync_core/src/state_keeper/io/tests/tester.rs @@ -1,14 +1,13 @@ //! Testing harness for the IO. -use multivm::vm_latest::constants::BLOCK_GAS_LIMIT; use std::{sync::Arc, time::Duration}; -use zksync_object_store::ObjectStoreFactory; -use zksync_config::configs::chain::StateKeeperConfig; -use zksync_config::GasAdjusterConfig; +use multivm::vm_latest::constants::BLOCK_GAS_LIMIT; +use zksync_config::{configs::chain::StateKeeperConfig, GasAdjusterConfig}; use zksync_contracts::BaseSystemContracts; use zksync_dal::ConnectionPool; use zksync_eth_client::clients::mock::MockEthereum; +use zksync_object_store::ObjectStoreFactory; use zksync_types::{ block::{L1BatchHeader, MiniblockHeader}, protocol_version::L1VerifierConfig, diff --git a/core/lib/zksync_core/src/state_keeper/keeper.rs b/core/lib/zksync_core/src/state_keeper/keeper.rs index 761e186e7aec..3cc153120c26 100644 --- a/core/lib/zksync_core/src/state_keeper/keeper.rs +++ b/core/lib/zksync_core/src/state_keeper/keeper.rs @@ -1,10 +1,11 @@ -use anyhow::Context as _; -use tokio::sync::watch; - -use std::convert::Infallible; -use std::time::{Duration, Instant}; +use std::{ + convert::Infallible, + time::{Duration, Instant}, +}; +use anyhow::Context as _; use multivm::interface::{Halt, L1BatchEnv, SystemEnv}; +use tokio::sync::watch; use zksync_types::{ block::MiniblockExecutionData, l2::TransactionType, protocol_version::ProtocolUpgradeTx, storage_writes_deduplicator::StorageWritesDeduplicator, Transaction, diff --git a/core/lib/zksync_core/src/state_keeper/mempool_actor.rs b/core/lib/zksync_core/src/state_keeper/mempool_actor.rs index 2c369d35a0fb..4797ed0006cd 100644 --- a/core/lib/zksync_core/src/state_keeper/mempool_actor.rs +++ b/core/lib/zksync_core/src/state_keeper/mempool_actor.rs @@ -1,8 +1,7 @@ -use tokio::sync::watch; - use std::{sync::Arc, time::Duration}; use multivm::vm_latest::utils::fee::derive_base_fee_and_gas_per_pubdata; +use tokio::sync::watch; use zksync_config::configs::chain::MempoolConfig; use zksync_dal::ConnectionPool; use zksync_mempool::L2TxFilter; diff --git a/core/lib/zksync_core/src/state_keeper/metrics.rs b/core/lib/zksync_core/src/state_keeper/metrics.rs index 72b89c4a2b81..8daccb5a3aa8 100644 --- a/core/lib/zksync_core/src/state_keeper/metrics.rs +++ b/core/lib/zksync_core/src/state_keeper/metrics.rs @@ -1,15 +1,14 @@ //! General-purpose state keeper metrics. -use vise::{ - Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Gauge, Histogram, LatencyObserver, - Metrics, -}; - use std::{ sync::{Mutex, Weak}, time::Duration, }; +use vise::{ + Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Gauge, Histogram, LatencyObserver, + Metrics, +}; use zksync_mempool::MempoolStore; use super::seal_criteria::SealResolution; diff --git a/core/lib/zksync_core/src/state_keeper/mod.rs b/core/lib/zksync_core/src/state_keeper/mod.rs index 5ec395267dfc..ee71a93bcf4c 100644 --- a/core/lib/zksync_core/src/state_keeper/mod.rs +++ b/core/lib/zksync_core/src/state_keeper/mod.rs @@ -1,15 +1,25 @@ -use tokio::sync::watch; -use zksync_object_store::ObjectStore; - use std::sync::Arc; +use tokio::sync::watch; use zksync_config::{ configs::chain::{MempoolConfig, NetworkConfig, StateKeeperConfig}, ContractsConfig, DBConfig, }; use zksync_dal::ConnectionPool; +use zksync_object_store::ObjectStore; use zksync_system_constants::MAX_TXS_IN_BLOCK; +use self::io::MempoolIO; +pub use self::{ + batch_executor::{L1BatchExecutorBuilder, MainBatchExecutorBuilder}, + io::{MiniblockSealer, MiniblockSealerHandle}, + keeper::ZkSyncStateKeeper, +}; +pub(crate) use self::{ + mempool_actor::MempoolFetcher, seal_criteria::ConditionalSealer, types::MempoolGuard, +}; +use crate::l1_gas_price::L1GasPriceProvider; + mod batch_executor; pub(crate) mod extractors; pub(crate) mod io; @@ -22,18 +32,6 @@ pub(crate) mod tests; pub(crate) mod types; pub(crate) mod updates; -pub use self::{ - batch_executor::{L1BatchExecutorBuilder, MainBatchExecutorBuilder}, - io::{MiniblockSealer, MiniblockSealerHandle}, - keeper::ZkSyncStateKeeper, -}; -pub(crate) use self::{ - mempool_actor::MempoolFetcher, seal_criteria::ConditionalSealer, types::MempoolGuard, -}; - -use self::io::MempoolIO; -use crate::l1_gas_price::L1GasPriceProvider; - #[allow(clippy::too_many_arguments)] pub(crate) async fn create_state_keeper( contracts_config: &ContractsConfig, diff --git a/core/lib/zksync_core/src/state_keeper/seal_criteria/criteria/geometry_seal_criteria.rs b/core/lib/zksync_core/src/state_keeper/seal_criteria/criteria/geometry_seal_criteria.rs index 1ec0c66e4d72..9f99554e58a2 100644 --- a/core/lib/zksync_core/src/state_keeper/seal_criteria/criteria/geometry_seal_criteria.rs +++ b/core/lib/zksync_core/src/state_keeper/seal_criteria/criteria/geometry_seal_criteria.rs @@ -1,5 +1,6 @@ -use multivm::vm_latest::constants::{ERGS_PER_CIRCUIT, MAX_CYCLES_FOR_TX}; use std::fmt; + +use multivm::vm_latest::constants::{ERGS_PER_CIRCUIT, MAX_CYCLES_FOR_TX}; use zksync_config::configs::chain::StateKeeperConfig; use zksync_types::{ circuit::{GEOMETRY_CONFIG, SCHEDULER_UPPER_BOUND}, diff --git a/core/lib/zksync_core/src/state_keeper/tests/mod.rs b/core/lib/zksync_core/src/state_keeper/tests/mod.rs index c5841fd8b1b3..06411ecaa55d 100644 --- a/core/lib/zksync_core/src/state_keeper/tests/mod.rs +++ b/core/lib/zksync_core/src/state_keeper/tests/mod.rs @@ -1,5 +1,3 @@ -use once_cell::sync::Lazy; - use std::{ sync::{ atomic::{AtomicBool, AtomicU64, Ordering}, @@ -8,11 +6,14 @@ use std::{ time::Instant, }; -use multivm::interface::{ - CurrentExecutionState, ExecutionResult, FinishedL1Batch, L1BatchEnv, L2BlockEnv, Refunds, - SystemEnv, TxExecutionMode, VmExecutionResultAndLogs, VmExecutionStatistics, +use multivm::{ + interface::{ + CurrentExecutionState, ExecutionResult, FinishedL1Batch, L1BatchEnv, L2BlockEnv, Refunds, + SystemEnv, TxExecutionMode, VmExecutionResultAndLogs, VmExecutionStatistics, + }, + vm_latest::{constants::BLOCK_GAS_LIMIT, VmExecutionLogs}, }; -use multivm::vm_latest::{constants::BLOCK_GAS_LIMIT, VmExecutionLogs}; +use once_cell::sync::Lazy; use zksync_config::configs::chain::StateKeeperConfig; use zksync_contracts::{BaseSystemContracts, BaseSystemContractsHashes}; use zksync_system_constants::ZKPORTER_IS_AVAILABLE; @@ -28,24 +29,26 @@ use zksync_types::{ StorageLogQuery, StorageLogQueryType, Timestamp, Transaction, H256, U256, }; -mod tester; - pub(crate) use self::tester::TestBatchExecutorBuilder; use self::tester::{ bootloader_tip_out_of_gas, pending_batch_data, random_tx, rejected_exec, successful_exec, successful_exec_with_metrics, TestScenario, }; -use crate::gas_tracker::l1_batch_base_cost; -use crate::state_keeper::{ - keeper::POLL_WAIT_DURATION, - seal_criteria::{ - criteria::{GasCriterion, SlotsCriterion}, - ConditionalSealer, +use crate::{ + gas_tracker::l1_batch_base_cost, + state_keeper::{ + keeper::POLL_WAIT_DURATION, + seal_criteria::{ + criteria::{GasCriterion, SlotsCriterion}, + ConditionalSealer, + }, + types::ExecutionMetricsForCriteria, + updates::UpdatesManager, }, - types::ExecutionMetricsForCriteria, - updates::UpdatesManager, }; +mod tester; + pub(super) static BASE_SYSTEM_CONTRACTS: Lazy = Lazy::new(BaseSystemContracts::load_from_disk); diff --git a/core/lib/zksync_core/src/state_keeper/tests/tester.rs b/core/lib/zksync_core/src/state_keeper/tests/tester.rs index 8d0d1fb047e7..a2dc7f05c5e8 100644 --- a/core/lib/zksync_core/src/state_keeper/tests/tester.rs +++ b/core/lib/zksync_core/src/state_keeper/tests/tester.rs @@ -1,6 +1,3 @@ -use async_trait::async_trait; -use tokio::sync::{mpsc, watch}; - use std::{ collections::{HashMap, HashSet, VecDeque}, convert::TryInto, @@ -8,11 +5,15 @@ use std::{ time::{Duration, Instant}, }; -use multivm::interface::{ - ExecutionResult, FinishedL1Batch, L1BatchEnv, L2BlockEnv, SystemEnv, TxExecutionMode, - VmExecutionResultAndLogs, +use async_trait::async_trait; +use multivm::{ + interface::{ + ExecutionResult, FinishedL1Batch, L1BatchEnv, L2BlockEnv, SystemEnv, TxExecutionMode, + VmExecutionResultAndLogs, + }, + vm_latest::constants::BLOCK_GAS_LIMIT, }; -use multivm::vm_latest::constants::BLOCK_GAS_LIMIT; +use tokio::sync::{mpsc, watch}; use zksync_types::{ block::MiniblockExecutionData, protocol_version::ProtocolUpgradeTx, witness_block_state::WitnessBlockState, Address, L1BatchNumber, L2ChainId, MiniblockNumber, diff --git a/core/lib/zksync_core/src/state_keeper/updates/l1_batch_updates.rs b/core/lib/zksync_core/src/state_keeper/updates/l1_batch_updates.rs index fdaa0b036f9e..584a0c835e73 100644 --- a/core/lib/zksync_core/src/state_keeper/updates/l1_batch_updates.rs +++ b/core/lib/zksync_core/src/state_keeper/updates/l1_batch_updates.rs @@ -1,9 +1,12 @@ +use zksync_types::{ + block::BlockGasCount, + priority_op_onchain_data::PriorityOpOnchainData, + tx::{tx_execution_info::ExecutionMetrics, TransactionExecutionResult}, + ExecuteTransactionCommon, +}; + use super::miniblock_updates::MiniblockUpdates; use crate::gas_tracker::new_block_gas_count; -use zksync_types::block::BlockGasCount; -use zksync_types::priority_op_onchain_data::PriorityOpOnchainData; -use zksync_types::tx::tx_execution_info::ExecutionMetrics; -use zksync_types::{tx::TransactionExecutionResult, ExecuteTransactionCommon}; #[derive(Debug, Clone, PartialEq)] pub struct L1BatchUpdates { @@ -44,6 +47,7 @@ impl L1BatchUpdates { #[cfg(test)] mod tests { + use multivm::vm_latest::TransactionVmExt; use zksync_types::{ProtocolVersionId, H256}; use super::*; @@ -51,7 +55,6 @@ mod tests { gas_tracker::new_block_gas_count, state_keeper::tests::{create_execution_result, create_transaction}, }; - use multivm::vm_latest::TransactionVmExt; #[test] fn apply_miniblock_with_empty_tx() { diff --git a/core/lib/zksync_core/src/state_keeper/updates/miniblock_updates.rs b/core/lib/zksync_core/src/state_keeper/updates/miniblock_updates.rs index d0a4f035f514..0c8591a2898e 100644 --- a/core/lib/zksync_core/src/state_keeper/updates/miniblock_updates.rs +++ b/core/lib/zksync_core/src/state_keeper/updates/miniblock_updates.rs @@ -1,18 +1,21 @@ -use multivm::interface::{ExecutionResult, L2BlockEnv, VmExecutionResultAndLogs}; -use multivm::vm_latest::TransactionVmExt; use std::collections::HashMap; -use zksync_types::l2_to_l1_log::{SystemL2ToL1Log, UserL2ToL1Log}; +use multivm::{ + interface::{ExecutionResult, L2BlockEnv, VmExecutionResultAndLogs}, + vm_latest::TransactionVmExt, +}; use zksync_types::{ block::{legacy_miniblock_hash, miniblock_hash, BlockGasCount}, event::extract_bytecodes_marked_as_known, - tx::tx_execution_info::TxExecutionStatus, - tx::{ExecutionMetrics, TransactionExecutionResult}, + l2_to_l1_log::{SystemL2ToL1Log, UserL2ToL1Log}, + tx::{tx_execution_info::TxExecutionStatus, ExecutionMetrics, TransactionExecutionResult}, vm_trace::Call, MiniblockNumber, ProtocolVersionId, StorageLogQuery, Transaction, VmEvent, H256, }; -use zksync_utils::bytecode::{hash_bytecode, CompressedBytecodeInfo}; -use zksync_utils::concat_and_hash; +use zksync_utils::{ + bytecode::{hash_bytecode, CompressedBytecodeInfo}, + concat_and_hash, +}; #[derive(Debug, Clone, PartialEq)] pub struct MiniblockUpdates { @@ -168,9 +171,10 @@ impl MiniblockUpdates { #[cfg(test)] mod tests { + use multivm::vm_latest::TransactionVmExt; + use super::*; use crate::state_keeper::tests::{create_execution_result, create_transaction}; - use multivm::vm_latest::TransactionVmExt; #[test] fn apply_empty_l2_tx() { diff --git a/core/lib/zksync_core/src/state_keeper/updates/mod.rs b/core/lib/zksync_core/src/state_keeper/updates/mod.rs index 3f09f7be30bb..c34164557b5e 100644 --- a/core/lib/zksync_core/src/state_keeper/updates/mod.rs +++ b/core/lib/zksync_core/src/state_keeper/updates/mod.rs @@ -1,22 +1,19 @@ use multivm::interface::{L1BatchEnv, VmExecutionResultAndLogs}; - use zksync_contracts::BaseSystemContractsHashes; use zksync_dal::blocks_dal::ConsensusBlockFields; -use zksync_types::vm_trace::Call; use zksync_types::{ block::BlockGasCount, storage_writes_deduplicator::StorageWritesDeduplicator, - tx::tx_execution_info::ExecutionMetrics, Address, L1BatchNumber, MiniblockNumber, - ProtocolVersionId, Transaction, + tx::tx_execution_info::ExecutionMetrics, vm_trace::Call, Address, L1BatchNumber, + MiniblockNumber, ProtocolVersionId, Transaction, }; use zksync_utils::bytecode::CompressedBytecodeInfo; -pub mod l1_batch_updates; -pub mod miniblock_updates; - pub(crate) use self::{l1_batch_updates::L1BatchUpdates, miniblock_updates::MiniblockUpdates}; - use super::io::MiniblockParams; +pub mod l1_batch_updates; +pub mod miniblock_updates; + /// Most of the information needed to seal the l1 batch/mini-block is contained within the VM, /// things that are not captured there are accumulated externally. /// `MiniblockUpdates` keeps updates for the pending mini-block. diff --git a/core/lib/zksync_core/src/sync_layer/batch_status_updater.rs b/core/lib/zksync_core/src/sync_layer/batch_status_updater.rs index 8e7ebe7a9850..8924fa5c5db5 100644 --- a/core/lib/zksync_core/src/sync_layer/batch_status_updater.rs +++ b/core/lib/zksync_core/src/sync_layer/batch_status_updater.rs @@ -1,8 +1,7 @@ -use chrono::{DateTime, Utc}; -use tokio::sync::watch::Receiver; - use std::time::Duration; +use chrono::{DateTime, Utc}; +use tokio::sync::watch::Receiver; use zksync_dal::ConnectionPool; use zksync_types::{ aggregated_operations::AggregatedActionType, api::BlockDetails, L1BatchNumber, MiniblockNumber, diff --git a/core/lib/zksync_core/src/sync_layer/client.rs b/core/lib/zksync_core/src/sync_layer/client.rs index 5d4f61a4f2a3..a13fba2d65c4 100644 --- a/core/lib/zksync_core/src/sync_layer/client.rs +++ b/core/lib/zksync_core/src/sync_layer/client.rs @@ -1,10 +1,9 @@ //! Client abstractions for syncing between the external node and the main node. -use anyhow::Context as _; -use async_trait::async_trait; - use std::{collections::HashMap, convert::TryInto, fmt}; +use anyhow::Context as _; +use async_trait::async_trait; use zksync_contracts::{BaseSystemContracts, BaseSystemContractsHashes, SystemContractCode}; use zksync_system_constants::ACCOUNT_CODE_STORAGE_ADDRESS; use zksync_types::{ diff --git a/core/lib/zksync_core/src/sync_layer/external_io.rs b/core/lib/zksync_core/src/sync_layer/external_io.rs index 4e870b956747..d751cdc8d014 100644 --- a/core/lib/zksync_core/src/sync_layer/external_io.rs +++ b/core/lib/zksync_core/src/sync_layer/external_io.rs @@ -1,8 +1,7 @@ -use async_trait::async_trait; -use futures::future; - use std::{collections::HashMap, convert::TryInto, iter::FromIterator, time::Duration}; +use async_trait::async_trait; +use futures::future; use multivm::interface::{FinishedL1Batch, L1BatchEnv, SystemEnv}; use zksync_contracts::{BaseSystemContracts, SystemContractCode}; use zksync_dal::ConnectionPool; diff --git a/core/lib/zksync_core/src/sync_layer/fetcher.rs b/core/lib/zksync_core/src/sync_layer/fetcher.rs index 3adbc8920bf4..2989b6b70a31 100644 --- a/core/lib/zksync_core/src/sync_layer/fetcher.rs +++ b/core/lib/zksync_core/src/sync_layer/fetcher.rs @@ -1,8 +1,7 @@ -use anyhow::Context as _; -use tokio::sync::watch; - use std::time::Duration; +use anyhow::Context as _; +use tokio::sync::watch; use zksync_dal::{blocks_dal::ConsensusBlockFields, StorageProcessor}; use zksync_types::{ api::en::SyncBlock, Address, L1BatchNumber, MiniblockNumber, ProtocolVersionId, diff --git a/core/lib/zksync_core/src/sync_layer/genesis.rs b/core/lib/zksync_core/src/sync_layer/genesis.rs index 4f7501fb0c37..77678a3b412b 100644 --- a/core/lib/zksync_core/src/sync_layer/genesis.rs +++ b/core/lib/zksync_core/src/sync_layer/genesis.rs @@ -1,5 +1,4 @@ use anyhow::Context as _; - use zksync_dal::StorageProcessor; use zksync_types::{ block::DeployedContract, protocol_version::L1VerifierConfig, diff --git a/core/lib/zksync_core/src/sync_layer/gossip/buffered/mod.rs b/core/lib/zksync_core/src/sync_layer/gossip/buffered/mod.rs index 41ca50e1cf2f..5f2308930a3f 100644 --- a/core/lib/zksync_core/src/sync_layer/gossip/buffered/mod.rs +++ b/core/lib/zksync_core/src/sync_layer/gossip/buffered/mod.rs @@ -1,9 +1,8 @@ //! Buffered [`BlockStore`] implementation. -use async_trait::async_trait; - use std::{collections::BTreeMap, ops, time::Instant}; +use async_trait::async_trait; #[cfg(test)] use zksync_concurrency::ctx::channel; use zksync_concurrency::{ @@ -13,14 +12,14 @@ use zksync_concurrency::{ use zksync_consensus_roles::validator::{BlockNumber, FinalBlock}; use zksync_consensus_storage::{BlockStore, StorageError, StorageResult, WriteBlockStore}; -#[cfg(test)] -mod tests; - use super::{ metrics::{BlockResponseKind, METRICS}, utils::MissingBlockNumbers, }; +#[cfg(test)] +mod tests; + /// [`BlockStore`] variation that upholds additional invariants as to how blocks are processed. /// /// The invariants are as follows: diff --git a/core/lib/zksync_core/src/sync_layer/gossip/buffered/tests.rs b/core/lib/zksync_core/src/sync_layer/gossip/buffered/tests.rs index 62c81bca7ca6..c5fd860ab87d 100644 --- a/core/lib/zksync_core/src/sync_layer/gossip/buffered/tests.rs +++ b/core/lib/zksync_core/src/sync_layer/gossip/buffered/tests.rs @@ -1,12 +1,11 @@ //! Tests for buffered storage. +use std::{iter, ops}; + use assert_matches::assert_matches; use async_trait::async_trait; use rand::{rngs::StdRng, seq::SliceRandom, Rng}; use test_casing::test_casing; - -use std::{iter, ops}; - use zksync_concurrency::{ ctx::{self, channel}, scope, diff --git a/core/lib/zksync_core/src/sync_layer/gossip/conversions.rs b/core/lib/zksync_core/src/sync_layer/gossip/conversions.rs index 00c6c6514524..616a4283c73a 100644 --- a/core/lib/zksync_core/src/sync_layer/gossip/conversions.rs +++ b/core/lib/zksync_core/src/sync_layer/gossip/conversions.rs @@ -1,6 +1,6 @@ //! Conversion logic between server and consensus types. -use anyhow::Context as _; +use anyhow::Context as _; use zksync_consensus_roles::validator::{BlockHeader, BlockNumber, FinalBlock}; use zksync_dal::blocks_dal::ConsensusBlockFields; use zksync_types::{api::en, MiniblockNumber, ProtocolVersionId}; diff --git a/core/lib/zksync_core/src/sync_layer/gossip/metrics.rs b/core/lib/zksync_core/src/sync_layer/gossip/metrics.rs index f67c150b99c0..73caf510269b 100644 --- a/core/lib/zksync_core/src/sync_layer/gossip/metrics.rs +++ b/core/lib/zksync_core/src/sync_layer/gossip/metrics.rs @@ -1,9 +1,9 @@ //! Metrics for gossip-powered syncing. -use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Gauge, Histogram, Metrics, Unit}; - use std::time::Duration; +use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Gauge, Histogram, Metrics, Unit}; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelValue, EncodeLabelSet)] #[metrics(label = "kind", rename_all = "snake_case")] pub(super) enum BlockResponseKind { diff --git a/core/lib/zksync_core/src/sync_layer/gossip/mod.rs b/core/lib/zksync_core/src/sync_layer/gossip/mod.rs index 2ec9ca5b60ea..9d769ab65f30 100644 --- a/core/lib/zksync_core/src/sync_layer/gossip/mod.rs +++ b/core/lib/zksync_core/src/sync_layer/gossip/mod.rs @@ -1,15 +1,17 @@ //! Consensus adapter for EN synchronization logic. -use anyhow::Context as _; -use tokio::sync::watch; - use std::sync::Arc; +use anyhow::Context as _; +use tokio::sync::watch; use zksync_concurrency::{ctx, scope}; use zksync_consensus_executor::{Executor, ExecutorConfig}; use zksync_consensus_roles::node; use zksync_dal::ConnectionPool; +use self::{buffered::Buffered, storage::PostgresBlockStorage}; +use super::{fetcher::FetcherCursor, sync_action::ActionQueueSender}; + mod buffered; mod conversions; mod metrics; @@ -18,9 +20,6 @@ mod storage; mod tests; mod utils; -use self::{buffered::Buffered, storage::PostgresBlockStorage}; -use super::{fetcher::FetcherCursor, sync_action::ActionQueueSender}; - /// Starts fetching L2 blocks using peer-to-peer gossip network. pub async fn run_gossip_fetcher( pool: ConnectionPool, diff --git a/core/lib/zksync_core/src/sync_layer/gossip/storage/mod.rs b/core/lib/zksync_core/src/sync_layer/gossip/storage/mod.rs index db36f71d35c7..1e35d17daaf0 100644 --- a/core/lib/zksync_core/src/sync_layer/gossip/storage/mod.rs +++ b/core/lib/zksync_core/src/sync_layer/gossip/storage/mod.rs @@ -1,10 +1,9 @@ //! Storage implementation based on DAL. -use anyhow::Context as _; -use async_trait::async_trait; - use std::ops; +use anyhow::Context as _; +use async_trait::async_trait; use zksync_concurrency::{ ctx, sync::{self, watch, Mutex}, @@ -12,13 +11,9 @@ use zksync_concurrency::{ }; use zksync_consensus_roles::validator::{BlockNumber, FinalBlock}; use zksync_consensus_storage::{BlockStore, StorageError, StorageResult}; -use zksync_dal::blocks_dal::ConsensusBlockFields; -use zksync_dal::{ConnectionPool, StorageProcessor}; +use zksync_dal::{blocks_dal::ConsensusBlockFields, ConnectionPool, StorageProcessor}; use zksync_types::{api::en::SyncBlock, Address, MiniblockNumber}; -#[cfg(test)] -mod tests; - use super::{buffered::ContiguousBlockStore, conversions::sync_block_to_consensus_block}; use crate::{ consensus, @@ -28,6 +23,9 @@ use crate::{ }, }; +#[cfg(test)] +mod tests; + #[derive(Debug)] struct CursorWithCachedBlock { inner: FetcherCursor, diff --git a/core/lib/zksync_core/src/sync_layer/gossip/storage/tests.rs b/core/lib/zksync_core/src/sync_layer/gossip/storage/tests.rs index cfd14f784112..c7e53f6456e1 100644 --- a/core/lib/zksync_core/src/sync_layer/gossip/storage/tests.rs +++ b/core/lib/zksync_core/src/sync_layer/gossip/storage/tests.rs @@ -1,7 +1,6 @@ //! Tests for Postgres storage implementation. use rand::{thread_rng, Rng}; - use zksync_concurrency::{scope, testonly::abort_on_panic}; use zksync_consensus_roles::validator; use zksync_types::L2ChainId; diff --git a/core/lib/zksync_core/src/sync_layer/gossip/tests.rs b/core/lib/zksync_core/src/sync_layer/gossip/tests.rs index ddb974849687..338fc9016f47 100644 --- a/core/lib/zksync_core/src/sync_layer/gossip/tests.rs +++ b/core/lib/zksync_core/src/sync_layer/gossip/tests.rs @@ -1,16 +1,14 @@ //! Tests for consensus adapters for EN synchronization logic. -use assert_matches::assert_matches; -use test_casing::{test_casing, Product}; - use std::ops; +use assert_matches::assert_matches; +use test_casing::{test_casing, Product}; use zksync_concurrency::{ctx, scope, testonly::abort_on_panic, time}; use zksync_consensus_executor::testonly::FullValidatorConfig; use zksync_consensus_roles::validator::{self, FinalBlock}; use zksync_consensus_storage::{InMemoryStorage, WriteBlockStore}; -use zksync_dal::blocks_dal::ConsensusBlockFields; -use zksync_dal::{ConnectionPool, StorageProcessor}; +use zksync_dal::{blocks_dal::ConsensusBlockFields, ConnectionPool, StorageProcessor}; use zksync_types::{ api::en::SyncBlock, Address, L1BatchNumber, MiniblockNumber, ProtocolVersionId, H256, }; diff --git a/core/lib/zksync_core/src/sync_layer/metrics.rs b/core/lib/zksync_core/src/sync_layer/metrics.rs index c3082c51052d..3a431294b259 100644 --- a/core/lib/zksync_core/src/sync_layer/metrics.rs +++ b/core/lib/zksync_core/src/sync_layer/metrics.rs @@ -1,9 +1,9 @@ //! Metrics for the synchronization layer of external node. -use vise::{Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Gauge, Histogram, Metrics}; - use std::time::Duration; +use vise::{Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Gauge, Histogram, Metrics}; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelValue, EncodeLabelSet)] #[metrics(label = "stage", rename_all = "snake_case")] pub(super) enum FetchStage { diff --git a/core/lib/zksync_core/src/sync_layer/tests.rs b/core/lib/zksync_core/src/sync_layer/tests.rs index 10582c7d9f9a..3c76e05d93f2 100644 --- a/core/lib/zksync_core/src/sync_layer/tests.rs +++ b/core/lib/zksync_core/src/sync_layer/tests.rs @@ -1,14 +1,13 @@ //! High-level sync layer tests. -use async_trait::async_trait; -use tokio::{sync::watch, task::JoinHandle}; - use std::{ collections::{HashMap, VecDeque}, iter, time::{Duration, Instant}, }; +use async_trait::async_trait; +use tokio::{sync::watch, task::JoinHandle}; use zksync_config::configs::chain::NetworkConfig; use zksync_contracts::{BaseSystemContractsHashes, SystemContractCode}; use zksync_dal::{ConnectionPool, StorageProcessor}; diff --git a/core/lib/zksync_core/src/witness_generator/basic_circuits.rs b/core/lib/zksync_core/src/witness_generator/basic_circuits.rs index c700d59120a1..e4d8b01357db 100644 --- a/core/lib/zksync_core/src/witness_generator/basic_circuits.rs +++ b/core/lib/zksync_core/src/witness_generator/basic_circuits.rs @@ -1,7 +1,3 @@ -use async_trait::async_trait; -use rand::Rng; -use serde::{Deserialize, Serialize}; - use std::{ collections::{hash_map::DefaultHasher, HashMap, HashSet}, hash::{Hash, Hasher}, @@ -9,9 +5,12 @@ use std::{ time::Instant, }; +use async_trait::async_trait; use multivm::vm_latest::{ constants::MAX_CYCLES_FOR_TX, HistoryDisabled, SimpleMemory, StorageOracle as VmStorageOracle, }; +use rand::Rng; +use serde::{Deserialize, Serialize}; use zksync_config::configs::{ witness_generator::BasicWitnessGeneratorDataSource, WitnessGeneratorConfig, }; @@ -23,12 +22,14 @@ use zksync_system_constants::BOOTLOADER_ADDRESS; use zksync_types::{ circuit::GEOMETRY_CONFIG, proofs::{AggregationRound, BasicCircuitWitnessGeneratorInput, PrepareBasicCircuitsJob}, - zkevm_test_harness::toolset::GeometryConfig, zkevm_test_harness::{ abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit, bellman::bn256::Bn256, - witness::full_block_artifact::{BlockBasicCircuits, BlockBasicCircuitsPublicInputs}, - witness::oracle::VmWitnessOracle, + toolset::GeometryConfig, + witness::{ + full_block_artifact::{BlockBasicCircuits, BlockBasicCircuitsPublicInputs}, + oracle::VmWitnessOracle, + }, SchedulerCircuitInstanceWitness, }, Address, L1BatchNumber, ProtocolVersionId, H256, U256, USED_BOOTLOADER_MEMORY_BYTES, diff --git a/core/lib/zksync_core/src/witness_generator/leaf_aggregation.rs b/core/lib/zksync_core/src/witness_generator/leaf_aggregation.rs index 4c9201b65f68..94082c421583 100644 --- a/core/lib/zksync_core/src/witness_generator/leaf_aggregation.rs +++ b/core/lib/zksync_core/src/witness_generator/leaf_aggregation.rs @@ -1,7 +1,6 @@ -use async_trait::async_trait; - use std::{collections::HashMap, time::Instant}; +use async_trait::async_trait; use zksync_config::configs::WitnessGeneratorConfig; use zksync_dal::ConnectionPool; use zksync_object_store::{ObjectStore, ObjectStoreFactory}; @@ -10,10 +9,12 @@ use zksync_types::{ circuit::LEAF_SPLITTING_FACTOR, proofs::{AggregationRound, PrepareLeafAggregationCircuitsJob, WitnessGeneratorJobMetadata}, zkevm_test_harness::{ - abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit, bellman::bn256::Bn256, - bellman::plonk::better_better_cs::setup::VerificationKey, - encodings::recursion_request::RecursionRequest, encodings::QueueSimulator, witness, - witness::oracle::VmWitnessOracle, LeafAggregationOutputDataWitness, + abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit, + bellman::{bn256::Bn256, plonk::better_better_cs::setup::VerificationKey}, + encodings::{recursion_request::RecursionRequest, QueueSimulator}, + witness, + witness::oracle::VmWitnessOracle, + LeafAggregationOutputDataWitness, }, L1BatchNumber, ProtocolVersionId, }; diff --git a/core/lib/zksync_core/src/witness_generator/mod.rs b/core/lib/zksync_core/src/witness_generator/mod.rs index 18b238660568..2fa941f0bda1 100644 --- a/core/lib/zksync_core/src/witness_generator/mod.rs +++ b/core/lib/zksync_core/src/witness_generator/mod.rs @@ -38,10 +38,9 @@ //! Note that the very first input table (`basic_circuit_witness_jobs` (TODO SMA-1362: will be renamed from `witness_inputs`)) //! is populated by the tree (as the input artifact for the `WitnessGeneratorJobType::BasicCircuits` is the merkle proofs) -use vise::{Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics}; - use std::{fmt, time::Duration}; +use vise::{Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics}; use zksync_types::proofs::AggregationRound; pub mod basic_circuits; diff --git a/core/lib/zksync_core/src/witness_generator/node_aggregation.rs b/core/lib/zksync_core/src/witness_generator/node_aggregation.rs index 6d884563c9d4..8ca86be00a52 100644 --- a/core/lib/zksync_core/src/witness_generator/node_aggregation.rs +++ b/core/lib/zksync_core/src/witness_generator/node_aggregation.rs @@ -1,7 +1,6 @@ -use async_trait::async_trait; - use std::{collections::HashMap, env, time::Instant}; +use async_trait::async_trait; use zksync_config::configs::WitnessGeneratorConfig; use zksync_dal::ConnectionPool; use zksync_object_store::{ObjectStore, ObjectStoreFactory}; @@ -13,8 +12,7 @@ use zksync_types::{ proofs::{AggregationRound, PrepareNodeAggregationCircuitJob, WitnessGeneratorJobMetadata}, zkevm_test_harness::{ abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit, - bellman::bn256::Bn256, - bellman::plonk::better_better_cs::setup::VerificationKey, + bellman::{bn256::Bn256, plonk::better_better_cs::setup::VerificationKey}, ff::to_hex, witness::{ self, diff --git a/core/lib/zksync_core/src/witness_generator/precalculated_merkle_paths_provider.rs b/core/lib/zksync_core/src/witness_generator/precalculated_merkle_paths_provider.rs index 96705de7e918..73f714d73145 100644 --- a/core/lib/zksync_core/src/witness_generator/precalculated_merkle_paths_provider.rs +++ b/core/lib/zksync_core/src/witness_generator/precalculated_merkle_paths_provider.rs @@ -1,9 +1,13 @@ use serde::{Deserialize, Serialize}; -use zksync_types::proofs::{PrepareBasicCircuitsJob, StorageLogMetadata}; -use zksync_types::zkevm_test_harness::blake2::Blake2s256; -use zksync_types::zkevm_test_harness::witness::tree::BinaryHasher; -use zksync_types::zkevm_test_harness::witness::tree::{ - BinarySparseStorageTree, EnumeratedBinaryLeaf, LeafQuery, ZkSyncStorageLeaf, +use zksync_types::{ + proofs::{PrepareBasicCircuitsJob, StorageLogMetadata}, + zkevm_test_harness::{ + blake2::Blake2s256, + witness::tree::{ + BinaryHasher, BinarySparseStorageTree, EnumeratedBinaryLeaf, LeafQuery, + ZkSyncStorageLeaf, + }, + }, }; #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] diff --git a/core/lib/zksync_core/src/witness_generator/scheduler.rs b/core/lib/zksync_core/src/witness_generator/scheduler.rs index ae8c2daff732..a0f1b6b6d7ac 100644 --- a/core/lib/zksync_core/src/witness_generator/scheduler.rs +++ b/core/lib/zksync_core/src/witness_generator/scheduler.rs @@ -1,7 +1,6 @@ -use async_trait::async_trait; - use std::{collections::HashMap, slice, time::Instant}; +use async_trait::async_trait; use zksync_config::configs::WitnessGeneratorConfig; use zksync_dal::ConnectionPool; use zksync_object_store::{ObjectStore, ObjectStoreFactory}; diff --git a/core/lib/zksync_core/src/witness_generator/storage_oracle.rs b/core/lib/zksync_core/src/witness_generator/storage_oracle.rs index 112b4eb5988c..f0b3203686fb 100644 --- a/core/lib/zksync_core/src/witness_generator/storage_oracle.rs +++ b/core/lib/zksync_core/src/witness_generator/storage_oracle.rs @@ -1,7 +1,7 @@ -use zksync_types::zkevm_test_harness::zk_evm::abstractions::{ - RefundType, RefundedAmounts, Storage, +use zksync_types::{ + zkevm_test_harness::zk_evm::abstractions::{RefundType, RefundedAmounts, Storage}, + LogQuery, Timestamp, }; -use zksync_types::{LogQuery, Timestamp}; #[derive(Debug)] pub(super) struct StorageOracle { diff --git a/core/lib/zksync_core/src/witness_generator/tests.rs b/core/lib/zksync_core/src/witness_generator/tests.rs index 38a77331fa31..fb7b285b1199 100644 --- a/core/lib/zksync_core/src/witness_generator/tests.rs +++ b/core/lib/zksync_core/src/witness_generator/tests.rs @@ -1,7 +1,11 @@ -use crate::witness_generator::precalculated_merkle_paths_provider::PrecalculatedMerklePathsProvider; use std::convert::TryInto; -use zksync_types::proofs::StorageLogMetadata; -use zksync_types::zkevm_test_harness::witness::tree::{BinarySparseStorageTree, ZkSyncStorageLeaf}; + +use zksync_types::{ + proofs::StorageLogMetadata, + zkevm_test_harness::witness::tree::{BinarySparseStorageTree, ZkSyncStorageLeaf}, +}; + +use crate::witness_generator::precalculated_merkle_paths_provider::PrecalculatedMerklePathsProvider; #[test] fn test_filter_renumerate_all_first_writes() { diff --git a/core/lib/zksync_core/src/witness_generator/utils.rs b/core/lib/zksync_core/src/witness_generator/utils.rs index 2135eddb3ccb..35f5fd431ce0 100644 --- a/core/lib/zksync_core/src/witness_generator/utils.rs +++ b/core/lib/zksync_core/src/witness_generator/utils.rs @@ -1,8 +1,12 @@ use zksync_object_store::{CircuitKey, ObjectStore}; -use zksync_types::zkevm_test_harness::abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit; -use zksync_types::zkevm_test_harness::bellman::bn256::Bn256; -use zksync_types::zkevm_test_harness::witness::oracle::VmWitnessOracle; -use zksync_types::{proofs::AggregationRound, L1BatchNumber}; +use zksync_types::{ + proofs::AggregationRound, + zkevm_test_harness::{ + abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit, bellman::bn256::Bn256, + witness::oracle::VmWitnessOracle, + }, + L1BatchNumber, +}; pub async fn save_prover_input_artifacts( block_number: L1BatchNumber, diff --git a/core/tests/cross_external_nodes_checker/src/checker.rs b/core/tests/cross_external_nodes_checker/src/checker.rs index 61421816c60a..be1dbd6faf6a 100644 --- a/core/tests/cross_external_nodes_checker/src/checker.rs +++ b/core/tests/cross_external_nodes_checker/src/checker.rs @@ -7,7 +7,6 @@ use std::{ use serde_json::Value; use tokio::{sync::watch::Receiver, time::sleep}; - use zksync_types::{ api::{BlockDetails, BlockNumber, L1BatchDetails}, web3::types::U64, @@ -15,15 +14,17 @@ use zksync_types::{ }; use zksync_utils::wait_for_tasks::wait_for_tasks; use zksync_web3_decl::{ - jsonrpsee::core::Error, - jsonrpsee::http_client::{HttpClient, HttpClientBuilder}, + jsonrpsee::{ + core::Error, + http_client::{HttpClient, HttpClientBuilder}, + }, namespaces::{EnNamespaceClient, EthNamespaceClient, ZksNamespaceClient}, types::FilterBuilder, RpcResult, }; -use crate::config::{CheckerConfig, RpcMode}; use crate::{ + config::{CheckerConfig, RpcMode}, divergence::{Divergence, DivergenceDetails}, helpers::compare_json, }; diff --git a/core/tests/cross_external_nodes_checker/src/config.rs b/core/tests/cross_external_nodes_checker/src/config.rs index 6273b3405a0b..636a4fd9ae58 100644 --- a/core/tests/cross_external_nodes_checker/src/config.rs +++ b/core/tests/cross_external_nodes_checker/src/config.rs @@ -116,9 +116,10 @@ fn default_subscription_duration() -> Option { #[cfg(test)] mod tests { - use super::*; use std::env; + use super::*; + #[test] fn success() { let config = r#" diff --git a/core/tests/cross_external_nodes_checker/src/divergence.rs b/core/tests/cross_external_nodes_checker/src/divergence.rs index 7f18f5fa6059..18c910349f79 100644 --- a/core/tests/cross_external_nodes_checker/src/divergence.rs +++ b/core/tests/cross_external_nodes_checker/src/divergence.rs @@ -1,4 +1,5 @@ use std::fmt; + use zksync_types::{web3::types::U64, MiniblockNumber}; #[derive(Debug, Clone)] diff --git a/core/tests/cross_external_nodes_checker/src/helpers.rs b/core/tests/cross_external_nodes_checker/src/helpers.rs index 14843e558680..6247b5e8c8ad 100644 --- a/core/tests/cross_external_nodes_checker/src/helpers.rs +++ b/core/tests/cross_external_nodes_checker/src/helpers.rs @@ -1,7 +1,7 @@ +use std::{collections::HashMap, future::Future, time::Duration}; + use futures::channel::oneshot; use serde_json::{Map, Value}; -use std::future::Future; -use std::{collections::HashMap, time::Duration}; use tokio::time::sleep; /// Sets up an interrupt handler and returns a future that resolves once an interrupt signal is received. @@ -132,9 +132,10 @@ impl ExponentialBackoff { #[cfg(test)] mod tests { - use super::*; use serde_json::json; + use super::*; + #[test] fn test_same_json() { let json1 = json!({ diff --git a/core/tests/cross_external_nodes_checker/src/main.rs b/core/tests/cross_external_nodes_checker/src/main.rs index 45192fe20fa9..7199c1cbd32c 100644 --- a/core/tests/cross_external_nodes_checker/src/main.rs +++ b/core/tests/cross_external_nodes_checker/src/main.rs @@ -1,4 +1,8 @@ -extern crate core; +use tokio::sync::watch; +use zksync_utils::wait_for_tasks::wait_for_tasks; + +use self::{checker::Checker, pubsub_checker::PubSubChecker}; +use crate::{config::CheckerConfig, helpers::setup_sigint_handler}; mod checker; mod config; @@ -6,13 +10,6 @@ mod divergence; mod helpers; mod pubsub_checker; -use crate::config::CheckerConfig; -use crate::helpers::setup_sigint_handler; -use checker::Checker; -use pubsub_checker::PubSubChecker; -use tokio::sync::watch; -use zksync_utils::wait_for_tasks::wait_for_tasks; - #[tokio::main] async fn main() -> anyhow::Result<()> { #[allow(deprecated)] // TODO (QIT-21): Use centralized configuration approach. diff --git a/core/tests/cross_external_nodes_checker/src/pubsub_checker.rs b/core/tests/cross_external_nodes_checker/src/pubsub_checker.rs index 788602102975..8a3bc765acec 100644 --- a/core/tests/cross_external_nodes_checker/src/pubsub_checker.rs +++ b/core/tests/cross_external_nodes_checker/src/pubsub_checker.rs @@ -1,14 +1,10 @@ -use crate::{ - config::CheckerConfig, - divergence::{Divergence, DivergenceDetails}, - helpers::{compare_json, ExponentialBackoff}, -}; -use anyhow::Context as _; use std::{ collections::HashMap, sync::Arc, time::{Duration, Instant}, }; + +use anyhow::Context as _; use tokio::{ select, spawn, sync::{watch::Receiver, Mutex as TokioMutex}, @@ -28,6 +24,12 @@ use zksync_web3_decl::{ types::{BlockHeader, PubSubResult}, }; +use crate::{ + config::CheckerConfig, + divergence::{Divergence, DivergenceDetails}, + helpers::{compare_json, ExponentialBackoff}, +}; + const MAX_RETRIES: u32 = 6; const GRACE_PERIOD: Duration = Duration::from_secs(60); const SUBSCRIPTION_TIMEOUT: Duration = Duration::from_secs(120); diff --git a/core/tests/loadnext/src/account/api_request_executor.rs b/core/tests/loadnext/src/account/api_request_executor.rs index e1e09004d4e9..18d25a1da9c9 100644 --- a/core/tests/loadnext/src/account/api_request_executor.rs +++ b/core/tests/loadnext/src/account/api_request_executor.rs @@ -2,7 +2,6 @@ use std::time::Instant; use rand::seq::IteratorRandom; use regex::Regex; - use zksync::{ error::{ClientError, RpcError}, types::FilterBuilder, diff --git a/core/tests/loadnext/src/account/mod.rs b/core/tests/loadnext/src/account/mod.rs index 14aa23b50315..42afd28d87ea 100644 --- a/core/tests/loadnext/src/account/mod.rs +++ b/core/tests/loadnext/src/account/mod.rs @@ -1,18 +1,16 @@ -use futures::{channel::mpsc, SinkExt}; use std::{ collections::VecDeque, sync::Arc, time::{Duration, Instant}, }; -use tokio::sync::RwLock; +use futures::{channel::mpsc, SinkExt}; +use tokio::sync::RwLock; use zksync::{error::ClientError, operations::SyncTransactionHandle, HttpClient}; +use zksync_contracts::test_contracts::LoadnextContractExecutionParams; use zksync_types::{api::TransactionReceipt, Address, Nonce, H256, U256, U64}; use zksync_web3_decl::jsonrpsee::core::Error as CoreError; -use zksync_contracts::test_contracts::LoadnextContractExecutionParams; - -use crate::utils::format_gwei; use crate::{ account::tx_command_executor::SubmitResult, account_pool::{AddressPool, TestWallet}, @@ -20,6 +18,7 @@ use crate::{ config::{LoadtestConfig, RequestLimiters}, constants::{MAX_L1_TRANSACTIONS, POLLING_INTERVAL}, report::{Report, ReportBuilder, ReportLabel}, + utils::format_gwei, }; mod api_request_executor; diff --git a/core/tests/loadnext/src/account/pubsub_executor.rs b/core/tests/loadnext/src/account/pubsub_executor.rs index 2dec5dbd8c60..d3c9d7144f1f 100644 --- a/core/tests/loadnext/src/account/pubsub_executor.rs +++ b/core/tests/loadnext/src/account/pubsub_executor.rs @@ -1,9 +1,7 @@ -use futures::{stream, TryStreamExt}; - use std::time::{Duration, Instant}; -use zksync::error::ClientError; -use zksync::types::PubSubFilterBuilder; +use futures::{stream, TryStreamExt}; +use zksync::{error::ClientError, types::PubSubFilterBuilder}; use zksync_web3_decl::{ jsonrpsee::{ core::client::{Subscription, SubscriptionClientT}, diff --git a/core/tests/loadnext/src/account/tx_command_executor.rs b/core/tests/loadnext/src/account/tx_command_executor.rs index 9fb8631fdc1e..f1ace035547f 100644 --- a/core/tests/loadnext/src/account/tx_command_executor.rs +++ b/core/tests/loadnext/src/account/tx_command_executor.rs @@ -1,12 +1,13 @@ use std::time::Instant; -use zksync::web3::ethabi; -use zksync::EthNamespaceClient; + use zksync::{ error::ClientError, ethereum::PriorityOpHolder, utils::{ get_approval_based_paymaster_input, get_approval_based_paymaster_input_for_estimation, }, + web3::ethabi, + EthNamespaceClient, }; use zksync_eth_client::EthInterface; use zksync_system_constants::MAX_L1_TRANSACTION_GAS_LIMIT; @@ -16,14 +17,13 @@ use zksync_types::{ Address, H256, U256, }; -use crate::account::ExecutionType; -use crate::utils::format_gwei; use crate::{ - account::AccountLifespan, + account::{AccountLifespan, ExecutionType}, command::{IncorrectnessModifier, TxCommand, TxType}, constants::{ETH_CONFIRMATION_TIMEOUT, ETH_POLLING_INTERVAL}, corrupted_tx::Corrupted, report::ReportLabel, + utils::format_gwei, }; #[derive(Debug)] diff --git a/core/tests/loadnext/src/account_pool.rs b/core/tests/loadnext/src/account_pool.rs index e4ded62dcf14..730a6d07b48d 100644 --- a/core/tests/loadnext/src/account_pool.rs +++ b/core/tests/loadnext/src/account_pool.rs @@ -3,7 +3,6 @@ use std::{collections::VecDeque, convert::TryFrom, str::FromStr, sync::Arc, time use once_cell::sync::OnceCell; use rand::Rng; use tokio::time::timeout; - use zksync::{signer::Signer, HttpClient, HttpClientBuilder, Wallet, ZksNamespaceClient}; use zksync_eth_signer::PrivateKeySigner; use zksync_types::{tx::primitives::PackedEthSignature, Address, L2ChainId, H256}; diff --git a/core/tests/loadnext/src/command/api.rs b/core/tests/loadnext/src/command/api.rs index 1e520d7c195a..76ed5db57470 100644 --- a/core/tests/loadnext/src/command/api.rs +++ b/core/tests/loadnext/src/command/api.rs @@ -1,6 +1,5 @@ use num::Integer; use rand::RngCore; - use zksync::EthNamespaceClient; use zksync_types::api; diff --git a/core/tests/loadnext/src/command/tx_command.rs b/core/tests/loadnext/src/command/tx_command.rs index 945a7ca16bb5..84e07d1f0d28 100644 --- a/core/tests/loadnext/src/command/tx_command.rs +++ b/core/tests/loadnext/src/command/tx_command.rs @@ -1,7 +1,6 @@ use once_cell::sync::OnceCell; use rand::Rng; use static_assertions::const_assert; - use zksync_types::{Address, U256}; use crate::{ diff --git a/core/tests/loadnext/src/config.rs b/core/tests/loadnext/src/config.rs index d62f4cdb63e5..b31cb5d3d8a0 100644 --- a/core/tests/loadnext/src/config.rs +++ b/core/tests/loadnext/src/config.rs @@ -1,12 +1,9 @@ +use std::{path::PathBuf, time::Duration}; + use serde::Deserialize; use tokio::sync::Semaphore; - -use std::path::PathBuf; -use std::time::Duration; - use zksync_contracts::test_contracts::LoadnextContractExecutionParams; -use zksync_types::network::Network; -use zksync_types::{Address, L2ChainId, H160}; +use zksync_types::{network::Network, Address, L2ChainId, H160}; use crate::fs_utils::read_tokens; diff --git a/core/tests/loadnext/src/corrupted_tx.rs b/core/tests/loadnext/src/corrupted_tx.rs index c3ada60472e0..c51b0c88d02c 100644 --- a/core/tests/loadnext/src/corrupted_tx.rs +++ b/core/tests/loadnext/src/corrupted_tx.rs @@ -1,13 +1,13 @@ use async_trait::async_trait; - use zksync::signer::Signer; -use zksync_eth_signer::{error::SignerError, EthereumSigner}; -use zksync_types::{Address, EIP712TypedStructure, Eip712Domain, PackedEthSignature, H256}; +use zksync_eth_signer::{ + error::SignerError, raw_ethereum_tx::TransactionParameters, EthereumSigner, +}; +use zksync_types::{ + fee::Fee, l2::L2Tx, Address, EIP712TypedStructure, Eip712Domain, PackedEthSignature, H256, +}; use crate::command::IncorrectnessModifier; -use zksync_eth_signer::raw_ethereum_tx::TransactionParameters; -use zksync_types::fee::Fee; -use zksync_types::l2::L2Tx; /// Trait that exists solely to extend the signed zkSync transaction interface, providing the ability /// to modify transaction in a way that will make it invalid. @@ -94,14 +94,14 @@ impl EthereumSigner for CorruptedSigner { #[cfg(test)] mod tests { - use super::*; use zksync_eth_signer::PrivateKeySigner; - use zksync_types::fee::Fee; - use zksync_types::L2ChainId; use zksync_types::{ - tokens::ETHEREUM_ADDRESS, tx::primitives::PackedEthSignature, Address, Nonce, H256, + fee::Fee, tokens::ETHEREUM_ADDRESS, tx::primitives::PackedEthSignature, Address, L2ChainId, + Nonce, H256, }; + use super::*; + const AMOUNT: u64 = 100; const FEE: u64 = 100; const NONCE: Nonce = Nonce(1); diff --git a/core/tests/loadnext/src/executor.rs b/core/tests/loadnext/src/executor.rs index 08d1ce47d6a7..5c64f1b61bed 100644 --- a/core/tests/loadnext/src/executor.rs +++ b/core/tests/loadnext/src/executor.rs @@ -1,14 +1,15 @@ -use anyhow::anyhow; -use futures::{channel::mpsc, future, SinkExt}; - use std::sync::Arc; -use zksync::ethereum::{PriorityOpHolder, DEFAULT_PRIORITY_FEE}; -use zksync::utils::{ - get_approval_based_paymaster_input, get_approval_based_paymaster_input_for_estimation, +use anyhow::anyhow; +use futures::{channel::mpsc, future, SinkExt}; +use zksync::{ + ethereum::{PriorityOpHolder, DEFAULT_PRIORITY_FEE}, + utils::{ + get_approval_based_paymaster_input, get_approval_based_paymaster_input_for_estimation, + }, + web3::{contract::Options, types::TransactionReceipt}, + EthNamespaceClient, EthereumProvider, ZksNamespaceClient, }; -use zksync::web3::{contract::Options, types::TransactionReceipt}; -use zksync::{EthNamespaceClient, EthereumProvider, ZksNamespaceClient}; use zksync_eth_client::{BoundEthInterface, EthInterface}; use zksync_eth_signer::PrivateKeySigner; use zksync_system_constants::MAX_L1_TRANSACTION_GAS_LIMIT; @@ -17,14 +18,14 @@ use zksync_types::{ REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_BYTE, U256, U64, }; -use crate::report::ReportBuilder; -use crate::utils::format_eth; use crate::{ account::AccountLifespan, account_pool::AccountPool, config::{ExecutionConfig, LoadtestConfig, RequestLimiters}, constants::*, + report::ReportBuilder, report_collector::{LoadtestResult, ReportCollector}, + utils::format_eth, }; /// Executor is the entity capable of running the loadtest flow. diff --git a/core/tests/loadnext/src/fs_utils.rs b/core/tests/loadnext/src/fs_utils.rs index d5b92b3c7a91..9fee9916f916 100644 --- a/core/tests/loadnext/src/fs_utils.rs +++ b/core/tests/loadnext/src/fs_utils.rs @@ -1,14 +1,10 @@ //! Utilities used for reading tokens, contracts bytecode and ABI from the //! filesystem. -use std::fs::File; -use std::io::BufReader; -use std::path::Path; +use std::{fs::File, io::BufReader, path::Path}; use serde::Deserialize; - -use zksync_types::network::Network; -use zksync_types::{ethabi::Contract, Address}; +use zksync_types::{ethabi::Contract, network::Network, Address}; /// A token stored in `etc/tokens/{network}.json` files. #[derive(Debug, Deserialize)] @@ -93,9 +89,10 @@ pub fn loadnext_contract(path: &Path) -> anyhow::Result { #[cfg(test)] mod tests { - use super::*; use std::path::PathBuf; + use super::*; + #[test] fn check_read_test_contract() { let test_contracts_path = { diff --git a/core/tests/loadnext/src/main.rs b/core/tests/loadnext/src/main.rs index 5d35c4e7f799..595532706c74 100644 --- a/core/tests/loadnext/src/main.rs +++ b/core/tests/loadnext/src/main.rs @@ -4,19 +4,17 @@ //! Without required variables provided, test is launched in the localhost/development mode with some hard-coded //! values to check the local zkSync deployment. -use tokio::sync::watch; - use std::time::Duration; -use prometheus_exporter::PrometheusExporterConfig; -use zksync_config::configs::api::PrometheusConfig; - use loadnext::{ command::TxType, config::{ExecutionConfig, LoadtestConfig}, executor::Executor, report_collector::LoadtestResult, }; +use prometheus_exporter::PrometheusExporterConfig; +use tokio::sync::watch; +use zksync_config::configs::api::PrometheusConfig; #[tokio::main] async fn main() -> anyhow::Result<()> { diff --git a/core/tests/loadnext/src/report.rs b/core/tests/loadnext/src/report.rs index 0ea86a49de04..e6c6bfdb5514 100644 --- a/core/tests/loadnext/src/report.rs +++ b/core/tests/loadnext/src/report.rs @@ -2,8 +2,8 @@ use std::time::Duration; use zksync_types::Address; -use crate::account::ExecutionType; use crate::{ + account::ExecutionType, all::All, command::{ApiRequest, ApiRequestType, SubscriptionType, TxCommand, TxType}, }; diff --git a/core/tests/loadnext/src/report_collector/mod.rs b/core/tests/loadnext/src/report_collector/mod.rs index a7798e0bea7f..6a7a5de39ba2 100644 --- a/core/tests/loadnext/src/report_collector/mod.rs +++ b/core/tests/loadnext/src/report_collector/mod.rs @@ -1,8 +1,8 @@ +use std::time::{Duration, Instant}; + use futures::{channel::mpsc::Receiver, StreamExt}; use operation_results_collector::OperationResultsCollector; -use std::time::{Duration, Instant}; - use crate::{ report::{ActionType, Report, ReportLabel}, report_collector::metrics_collector::MetricsCollector, diff --git a/core/tests/loadnext/src/report_collector/operation_results_collector.rs b/core/tests/loadnext/src/report_collector/operation_results_collector.rs index 63f2bb7dbf9f..ab460af839b5 100644 --- a/core/tests/loadnext/src/report_collector/operation_results_collector.rs +++ b/core/tests/loadnext/src/report_collector/operation_results_collector.rs @@ -1,7 +1,7 @@ -use crate::report::{ActionType, ReportLabel}; - use std::{fmt, time::Duration}; +use crate::report::{ActionType, ReportLabel}; + /// Collector that analyzes the outcomes of the performed operations. /// Currently it's solely capable of deciding whether test was failed or not. /// API requests are counted separately. diff --git a/core/tests/loadnext/src/rng.rs b/core/tests/loadnext/src/rng.rs index 4d5ab84c714b..3612a7c2a847 100644 --- a/core/tests/loadnext/src/rng.rs +++ b/core/tests/loadnext/src/rng.rs @@ -1,7 +1,6 @@ use std::convert::TryInto; use rand::{rngs::SmallRng, seq::SliceRandom, thread_rng, RngCore, SeedableRng}; - use zksync::web3::signing::keccak256; use zksync_types::H256; diff --git a/core/tests/loadnext/src/utils.rs b/core/tests/loadnext/src/utils.rs index 3f528e97e347..95f61c8cee89 100644 --- a/core/tests/loadnext/src/utils.rs +++ b/core/tests/loadnext/src/utils.rs @@ -1,4 +1,5 @@ use std::ops::Div; + use zksync_types::U256; pub fn format_eth(value: U256) -> String { diff --git a/core/tests/vm-benchmark/benches/diy_benchmark.rs b/core/tests/vm-benchmark/benches/diy_benchmark.rs index 8f5b6cd685bb..b99837d8eab2 100644 --- a/core/tests/vm-benchmark/benches/diy_benchmark.rs +++ b/core/tests/vm-benchmark/benches/diy_benchmark.rs @@ -1,5 +1,6 @@ -use criterion::black_box; use std::time::{Duration, Instant}; + +use criterion::black_box; use vm_benchmark_harness::{cut_to_allowed_bytecode_size, get_deploy_tx, BenchmarkingVm}; fn main() { diff --git a/core/tests/vm-benchmark/harness/src/lib.rs b/core/tests/vm-benchmark/harness/src/lib.rs index b7da44aed928..00da5bcca9fd 100644 --- a/core/tests/vm-benchmark/harness/src/lib.rs +++ b/core/tests/vm-benchmark/harness/src/lib.rs @@ -1,9 +1,12 @@ -use multivm::interface::{ - L2BlockEnv, TxExecutionMode, VmExecutionMode, VmExecutionResultAndLogs, VmInterface, +use std::{cell::RefCell, rc::Rc}; + +use multivm::{ + interface::{ + L2BlockEnv, TxExecutionMode, VmExecutionMode, VmExecutionResultAndLogs, VmInterface, + }, + vm_latest::{constants::BLOCK_GAS_LIMIT, HistoryEnabled, Vm}, }; -use multivm::vm_latest::{constants::BLOCK_GAS_LIMIT, HistoryEnabled, Vm}; use once_cell::sync::Lazy; -use std::{cell::RefCell, rc::Rc}; use zksync_contracts::{deployer_contract, BaseSystemContracts}; use zksync_state::{InMemoryStorage, StorageView}; use zksync_system_constants::ethereum::MAX_GAS_PER_PUBDATA_BYTE; @@ -133,9 +136,10 @@ pub fn get_deploy_tx(code: &[u8]) -> Transaction { #[cfg(test)] mod tests { - use crate::*; use zksync_contracts::read_bytecode; + use crate::*; + #[test] fn can_deploy_contract() { let test_contract = read_bytecode( diff --git a/core/tests/vm-benchmark/src/compare_iai_results.rs b/core/tests/vm-benchmark/src/compare_iai_results.rs index d67d72386836..d903d727117c 100644 --- a/core/tests/vm-benchmark/src/compare_iai_results.rs +++ b/core/tests/vm-benchmark/src/compare_iai_results.rs @@ -1,6 +1,5 @@ -use std::collections::HashMap; -use std::fs::File; -use std::io::BufReader; +use std::{collections::HashMap, fs::File, io::BufReader}; + use vm_benchmark::parse_iai::parse_iai; fn main() { diff --git a/core/tests/vm-benchmark/src/find_slowest.rs b/core/tests/vm-benchmark/src/find_slowest.rs index 947f944541cf..2bc2a894d2d0 100644 --- a/core/tests/vm-benchmark/src/find_slowest.rs +++ b/core/tests/vm-benchmark/src/find_slowest.rs @@ -2,6 +2,7 @@ use std::{ io::Write, time::{Duration, Instant}, }; + use vm_benchmark_harness::*; fn main() { diff --git a/core/tests/vm-benchmark/src/iai_results_to_prometheus.rs b/core/tests/vm-benchmark/src/iai_results_to_prometheus.rs index dc3c8f6d98f7..396d59948a87 100644 --- a/core/tests/vm-benchmark/src/iai_results_to_prometheus.rs +++ b/core/tests/vm-benchmark/src/iai_results_to_prometheus.rs @@ -1,4 +1,5 @@ use std::io::BufReader; + use vm_benchmark::parse_iai::IaiResult; fn main() { diff --git a/core/tests/vm-benchmark/src/with_prometheus.rs b/core/tests/vm-benchmark/src/with_prometheus.rs index e9d4f2e57edc..1fcf5652c6dd 100644 --- a/core/tests/vm-benchmark/src/with_prometheus.rs +++ b/core/tests/vm-benchmark/src/with_prometheus.rs @@ -1,6 +1,7 @@ -use metrics_exporter_prometheus::PrometheusBuilder; use std::time::Duration; +use metrics_exporter_prometheus::PrometheusBuilder; + pub fn with_prometheus(f: F) { println!("Pushing results to Prometheus"); diff --git a/infrastructure/zk/src/fmt.ts b/infrastructure/zk/src/fmt.ts index fa8b5e79691b..896ef28dbbd8 100644 --- a/infrastructure/zk/src/fmt.ts +++ b/infrastructure/zk/src/fmt.ts @@ -50,7 +50,12 @@ async function prettierSystemContracts(check: boolean = false) { export async function rustfmt(check: boolean = false) { process.chdir(process.env.ZKSYNC_HOME as string); - const command = check ? 'cargo fmt -- --check' : 'cargo fmt'; + + // We rely on a supposedly undocumented bug/feature of `rustfmt` that allows us to use unstable features on stable Rust. + // Please note that this only works with CLI flags, and if you happened to visit this place after things suddenly stopped working, + // it is certainly possible that the feature was deemed a bug and was fixed. Then welp. + const config = '--config imports_granularity=Crate --config group_imports=StdExternalCrate'; + const command = check ? `cargo fmt -- --check ${config}` : `cargo fmt -- ${config}`; await utils.spawn(command); process.chdir('./prover'); await utils.spawn(command); diff --git a/prover/Cargo.lock b/prover/Cargo.lock index 95ff42d7052b..d6ea72d2c8b6 100644 --- a/prover/Cargo.lock +++ b/prover/Cargo.lock @@ -971,12 +971,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5241cd7938b1b415942e943ea96f615953d500b50347b505b0b507080bad5a6f" -[[package]] -name = "const-oid" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" - [[package]] name = "const-oid" version = "0.9.5" @@ -1245,16 +1239,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -[[package]] -name = "crypto-bigint" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" -dependencies = [ - "generic-array", - "subtle", -] - [[package]] name = "crypto-bigint" version = "0.4.9" @@ -1454,24 +1438,13 @@ dependencies = [ "uuid", ] -[[package]] -name = "der" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" -dependencies = [ - "const-oid 0.7.1", - "crypto-bigint 0.3.2", - "pem-rfc7468", -] - [[package]] name = "der" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" dependencies = [ - "const-oid 0.9.5", + "const-oid", "zeroize", ] @@ -1481,7 +1454,8 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ - "const-oid 0.9.5", + "const-oid", + "pem-rfc7468", "zeroize", ] @@ -2186,9 +2160,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "google-cloud-auth" -version = "0.11.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644f40175857d0b8d7b6cad6cd9594284da5041387fa2ddff30ab6d8faef65eb" +checksum = "af1087f1fbd2dd3f58c17c7574ddd99cd61cbbbc2c4dc81114b8687209b196cb" dependencies = [ "async-trait", "base64 0.21.5", @@ -2208,9 +2182,9 @@ dependencies = [ [[package]] name = "google-cloud-metadata" -version = "0.3.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96e4ad0802d3f416f62e7ce01ac1460898ee0efc98f8b45cd4aab7611607012f" +checksum = "cc279bfb50487d7bcd900e8688406475fc750fe474a835b2ab9ade9eb1fc90e2" dependencies = [ "reqwest", "thiserror", @@ -2219,11 +2193,12 @@ dependencies = [ [[package]] name = "google-cloud-storage" -version = "0.12.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "215abab97e07d144428425509c1dad07e57ea72b84b21bcdb6a8a5f12a5c4932" +checksum = "ac04b29849ebdeb9fb008988cc1c4d1f0c9d121b4c7f1ddeb8061df124580e93" dependencies = [ "async-stream", + "async-trait", "base64 0.21.5", "bytes", "futures-util", @@ -2233,10 +2208,10 @@ dependencies = [ "hex", "once_cell", "percent-encoding", + "pkcs8 0.10.2", "regex", "reqwest", - "ring 0.16.20", - "rsa", + "ring 0.17.5", "serde", "serde_json", "sha2 0.10.8", @@ -2811,9 +2786,6 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -dependencies = [ - "spin 0.5.2", -] [[package]] name = "lazycell" @@ -3323,23 +3295,6 @@ dependencies = [ "serde", ] -[[package]] -name = "num-bigint-dig" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.5", - "smallvec", - "zeroize", -] - [[package]] name = "num-complex" version = "0.3.1" @@ -3801,9 +3756,9 @@ dependencies = [ [[package]] name = "pem-rfc7468" -version = "0.3.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01de5d978f34aa4b2296576379fcc416034702fd94117c56ffd8a1a767cefb30" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" dependencies = [ "base64ct", ] @@ -3901,28 +3856,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkcs1" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78f66c04ccc83dd4486fd46c33896f4e17b24a7a3a6400dedc48ed0ddd72320" -dependencies = [ - "der 0.5.1", - "pkcs8 0.8.0", - "zeroize", -] - -[[package]] -name = "pkcs8" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" -dependencies = [ - "der 0.5.1", - "spki 0.5.4", - "zeroize", -] - [[package]] name = "pkcs8" version = "0.9.0" @@ -4808,26 +4741,6 @@ dependencies = [ "librocksdb-sys", ] -[[package]] -name = "rsa" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cf22754c49613d2b3b119f0e5d46e34a2c628a937e3024b8762de4e7d8c710b" -dependencies = [ - "byteorder", - "digest 0.10.7", - "num-bigint-dig", - "num-integer", - "num-iter", - "num-traits", - "pkcs1", - "pkcs8 0.8.0", - "rand_core 0.6.4", - "smallvec", - "subtle", - "zeroize", -] - [[package]] name = "rustc-demangle" version = "0.1.23" @@ -5515,16 +5428,6 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -[[package]] -name = "spki" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" -dependencies = [ - "base64ct", - "der 0.5.1", -] - [[package]] name = "spki" version = "0.6.0" @@ -7168,6 +7071,7 @@ dependencies = [ "itertools 0.10.5", "num 0.3.1", "once_cell", + "prost", "rand 0.8.5", "serde", "serde_json", @@ -7178,8 +7082,11 @@ dependencies = [ "tracing", "url", "vise", + "zksync_consensus_roles", "zksync_contracts", "zksync_health_check", + "zksync_protobuf", + "zksync_protobuf_build", "zksync_system_constants", "zksync_types", "zksync_utils", @@ -7539,7 +7446,6 @@ dependencies = [ "num_enum", "once_cell", "parity-crypto", - "prost", "rlp", "serde", "serde_json", diff --git a/prover/circuit_synthesizer/src/circuit_synthesizer.rs b/prover/circuit_synthesizer/src/circuit_synthesizer.rs index 55da03949a7e..96a164c69c15 100644 --- a/prover/circuit_synthesizer/src/circuit_synthesizer.rs +++ b/prover/circuit_synthesizer/src/circuit_synthesizer.rs @@ -1,34 +1,40 @@ -use std::option::Option; -use std::time::Duration; -use std::time::Instant; +use std::{ + option::Option, + time::{Duration, Instant}, +}; use anyhow::Context as _; use local_ip_address::local_ip; -use prover_service::prover::{Prover, ProvingAssembly}; -use prover_service::remote_synth::serialize_job; -use tokio::task::JoinHandle; -use tokio::time::sleep; -use zkevm_test_harness::abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit; -use zkevm_test_harness::bellman::plonk::better_better_cs::cs::Circuit; -use zkevm_test_harness::pairing::bn256::Bn256; -use zkevm_test_harness::witness::oracle::VmWitnessOracle; - -use crate::metrics::METRICS; -use zksync_config::configs::prover_group::ProverGroupConfig; -use zksync_config::configs::CircuitSynthesizerConfig; -use zksync_config::ProverConfigs; +use prover_service::{ + prover::{Prover, ProvingAssembly}, + remote_synth::serialize_job, +}; +use tokio::{task::JoinHandle, time::sleep}; +use zkevm_test_harness::{ + abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit, + bellman::plonk::better_better_cs::cs::Circuit, pairing::bn256::Bn256, + witness::oracle::VmWitnessOracle, +}; +use zksync_config::{ + configs::{prover_group::ProverGroupConfig, CircuitSynthesizerConfig}, + ProverConfigs, +}; use zksync_dal::ConnectionPool; use zksync_env_config::FromEnv; use zksync_object_store::{CircuitKey, ObjectStore, ObjectStoreError, ObjectStoreFactory}; use zksync_prover_fri_utils::socket_utils::send_assembly; -use zksync_prover_utils::numeric_index_to_circuit_name; -use zksync_prover_utils::region_fetcher::{get_region, get_zone}; +use zksync_prover_utils::{ + numeric_index_to_circuit_name, + region_fetcher::{get_region, get_zone}, +}; use zksync_queued_job_processor::{async_trait, JobProcessor}; use zksync_types::{ proofs::{GpuProverInstanceStatus, SocketAddress}, protocol_version::L1VerifierConfig, }; +use crate::metrics::METRICS; + #[derive(thiserror::Error, Debug)] pub enum CircuitSynthesizerError { #[error("InvalidaGroupCircuits: {0}")] diff --git a/prover/circuit_synthesizer/src/main.rs b/prover/circuit_synthesizer/src/main.rs index 5592885dcdd9..a4ac19e18d7b 100644 --- a/prover/circuit_synthesizer/src/main.rs +++ b/prover/circuit_synthesizer/src/main.rs @@ -1,8 +1,7 @@ use anyhow::Context as _; use prometheus_exporter::PrometheusExporterConfig; use structopt::StructOpt; -use tokio::{sync::oneshot, sync::watch}; - +use tokio::sync::{oneshot, watch}; use zksync_config::configs::{ AlertsConfig, CircuitSynthesizerConfig, ObjectStoreConfig, PostgresConfig, ProverGroupConfig, }; diff --git a/prover/circuit_synthesizer/src/metrics.rs b/prover/circuit_synthesizer/src/metrics.rs index b9ee5b10c159..78049d6cf789 100644 --- a/prover/circuit_synthesizer/src/metrics.rs +++ b/prover/circuit_synthesizer/src/metrics.rs @@ -1,4 +1,5 @@ use std::time::Duration; + use vise::{Buckets, Histogram, LabeledFamily, Metrics}; #[derive(Debug, Metrics)] diff --git a/prover/proof_fri_compressor/src/compressor.rs b/prover/proof_fri_compressor/src/compressor.rs index f0f8efc6102c..b4346305b9f9 100644 --- a/prover/proof_fri_compressor/src/compressor.rs +++ b/prover/proof_fri_compressor/src/compressor.rs @@ -1,30 +1,37 @@ +use std::time::Instant; + use anyhow::Context as _; use async_trait::async_trait; -use std::time::Instant; use tokio::task::JoinHandle; - -use crate::metrics::METRICS; use zkevm_test_harness::proof_wrapper_utils::{wrap_proof, WrapperConfig}; use zksync_dal::ConnectionPool; use zksync_object_store::ObjectStore; -use zksync_prover_fri_types::circuit_definitions::boojum::field::goldilocks::GoldilocksField; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::recursion_layer::{ - ZkSyncRecursionLayerProof, ZkSyncRecursionLayerStorageType, +use zksync_prover_fri_types::{ + circuit_definitions::{ + boojum::field::goldilocks::GoldilocksField, + circuit_definitions::recursion_layer::{ + ZkSyncRecursionLayerProof, ZkSyncRecursionLayerStorageType, + }, + zkevm_circuits::scheduler::block_header::BlockAuxilaryOutputWitness, + }, + get_current_pod_name, AuxOutputWitnessWrapper, FriProofWrapper, }; -use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::scheduler::block_header::BlockAuxilaryOutputWitness; -use zksync_prover_fri_types::{get_current_pod_name, AuxOutputWitnessWrapper, FriProofWrapper}; use zksync_queued_job_processor::JobProcessor; -use zksync_types::aggregated_operations::L1BatchProofForL1; -use zksync_types::zkevm_test_harness::abstract_zksync_circuit::concrete_circuits::ZkSyncVerificationKey; -use zksync_types::zkevm_test_harness::abstract_zksync_circuit::concrete_circuits::{ - ZkSyncCircuit, ZkSyncProof, +use zksync_types::{ + aggregated_operations::L1BatchProofForL1, + zkevm_test_harness::{ + abstract_zksync_circuit::concrete_circuits::{ + ZkSyncCircuit, ZkSyncProof, ZkSyncVerificationKey, + }, + bellman::{bn256::Bn256, plonk::better_better_cs::proof::Proof}, + witness::oracle::VmWitnessOracle, + }, + L1BatchNumber, }; -use zksync_types::zkevm_test_harness::bellman::bn256::Bn256; -use zksync_types::zkevm_test_harness::bellman::plonk::better_better_cs::proof::Proof; -use zksync_types::zkevm_test_harness::witness::oracle::VmWitnessOracle; -use zksync_types::L1BatchNumber; use zksync_vk_setup_data_server_fri::{get_recursive_layer_vk_for_circuit_type, get_snark_vk}; +use crate::metrics::METRICS; + pub struct ProofCompressor { blob_store: Box, pool: ConnectionPool, diff --git a/prover/proof_fri_compressor/src/main.rs b/prover/proof_fri_compressor/src/main.rs index c8396803339f..04f2935ead0d 100644 --- a/prover/proof_fri_compressor/src/main.rs +++ b/prover/proof_fri_compressor/src/main.rs @@ -1,11 +1,9 @@ -use anyhow::Context as _; -use std::env; -use structopt::StructOpt; -use tokio::{sync::oneshot, sync::watch}; - -use std::time::Duration; +use std::{env, time::Duration}; +use anyhow::Context as _; use prometheus_exporter::PrometheusExporterConfig; +use structopt::StructOpt; +use tokio::sync::{oneshot, watch}; use zksync_config::configs::{FriProofCompressorConfig, PostgresConfig}; use zksync_dal::ConnectionPool; use zksync_env_config::{object_store::ProverObjectStoreConfig, FromEnv}; diff --git a/prover/proof_fri_compressor/src/metrics.rs b/prover/proof_fri_compressor/src/metrics.rs index 5891da2f4166..724da6d73b8b 100644 --- a/prover/proof_fri_compressor/src/metrics.rs +++ b/prover/proof_fri_compressor/src/metrics.rs @@ -1,4 +1,5 @@ use std::time::Duration; + use vise::{Buckets, Histogram, Metrics}; #[derive(Debug, Metrics)] diff --git a/prover/prover/src/artifact_provider.rs b/prover/prover/src/artifact_provider.rs index 8ee065be5a58..9af365d95f40 100644 --- a/prover/prover/src/artifact_provider.rs +++ b/prover/prover/src/artifact_provider.rs @@ -1,8 +1,10 @@ +use std::io::Read; + use anyhow::Context as _; use prover_service::ArtifactProvider; -use std::io::Read; -use zkevm_test_harness::abstract_zksync_circuit::concrete_circuits::ZkSyncVerificationKey; -use zkevm_test_harness::pairing::bn256::Bn256; +use zkevm_test_harness::{ + abstract_zksync_circuit::concrete_circuits::ZkSyncVerificationKey, pairing::bn256::Bn256, +}; use zksync_setup_key_server::get_setup_for_circuit_type; use zksync_verification_key_server::get_vk_for_circuit_type; diff --git a/prover/prover/src/metrics.rs b/prover/prover/src/metrics.rs index 4544ae9bfa7c..ab18c59bcf7c 100644 --- a/prover/prover/src/metrics.rs +++ b/prover/prover/src/metrics.rs @@ -1,4 +1,5 @@ use std::time::Duration; + use vise::{Buckets, Counter, Histogram, LabeledFamily, Metrics}; const PROVER_LATENCY_BUCKETS: Buckets = Buckets::values(&[ diff --git a/prover/prover/src/prover.rs b/prover/prover/src/prover.rs index 1885d8153329..efb570050b57 100644 --- a/prover/prover/src/prover.rs +++ b/prover/prover/src/prover.rs @@ -1,21 +1,21 @@ -use anyhow::Context as _; use std::{env, time::Duration}; +use anyhow::Context as _; use prover_service::{ JobReporter, JobResult::{self, Failure, ProofGenerated}, }; use tokio::runtime::Handle; -use zkevm_test_harness::abstract_zksync_circuit::concrete_circuits::ZkSyncProof; -use zkevm_test_harness::pairing::bn256::Bn256; - -use crate::metrics::METRICS; +use zkevm_test_harness::{ + abstract_zksync_circuit::concrete_circuits::ZkSyncProof, pairing::bn256::Bn256, +}; use zksync_config::{PostgresConfig, ProverConfig}; -use zksync_dal::ConnectionPool; -use zksync_dal::StorageProcessor; +use zksync_dal::{ConnectionPool, StorageProcessor}; use zksync_object_store::{Bucket, ObjectStore, ObjectStoreFactory}; use zksync_types::proofs::ProverJobMetadata; +use crate::metrics::METRICS; + #[derive(Debug)] pub struct ProverReporter { rt_handle: Handle, diff --git a/prover/prover/src/prover_params.rs b/prover/prover/src/prover_params.rs index 558e9058ed61..fc59b88ddf77 100644 --- a/prover/prover/src/prover_params.rs +++ b/prover/prover/src/prover_params.rs @@ -1,7 +1,6 @@ use std::time::Duration; use prover_service::Params; - use zksync_config::ProverConfig; #[derive(Debug)] diff --git a/prover/prover/src/run.rs b/prover/prover/src/run.rs index 9342cd554e62..9784b2f1b666 100644 --- a/prover/prover/src/run.rs +++ b/prover/prover/src/run.rs @@ -1,11 +1,10 @@ -use anyhow::Context as _; use std::{env, future::Future, sync::Arc, time::Instant}; -use tokio::sync::{oneshot, Mutex}; +use anyhow::Context as _; use local_ip_address::local_ip; -use queues::Buffer; - use prometheus_exporter::PrometheusExporterConfig; +use queues::Buffer; +use tokio::sync::{oneshot, Mutex}; use zksync_config::{ configs::{ api::PrometheusConfig, prover_group::ProverGroupConfig, AlertsConfig, ObjectStoreConfig, @@ -19,12 +18,11 @@ use zksync_prover_utils::region_fetcher::{get_region, get_zone}; use zksync_types::proofs::{GpuProverInstanceStatus, SocketAddress}; use zksync_utils::wait_for_tasks::wait_for_tasks; -use crate::artifact_provider::ProverArtifactProvider; -use crate::metrics::METRICS; -use crate::prover::ProverReporter; -use crate::prover_params::ProverParams; -use crate::socket_listener::incoming_socket_listener; -use crate::synthesized_circuit_provider::SynthesizedCircuitProvider; +use crate::{ + artifact_provider::ProverArtifactProvider, metrics::METRICS, prover::ProverReporter, + prover_params::ProverParams, socket_listener::incoming_socket_listener, + synthesized_circuit_provider::SynthesizedCircuitProvider, +}; async fn graceful_shutdown() -> anyhow::Result> { let postgres_config = PostgresConfig::from_env().context("PostgresConfig::from_env()")?; diff --git a/prover/prover/src/socket_listener.rs b/prover/prover/src/socket_listener.rs index d8dbaff74dca..95a369e70786 100644 --- a/prover/prover/src/socket_listener.rs +++ b/prover/prover/src/socket_listener.rs @@ -1,15 +1,18 @@ -use crate::synthesized_circuit_provider::SharedAssemblyQueue; -use queues::IsQueue; -use std::net::{IpAddr, SocketAddr}; -use std::time::Instant; -use zksync_dal::ConnectionPool; -use zksync_types::proofs::{GpuProverInstanceStatus, SocketAddress}; +use std::{ + net::{IpAddr, SocketAddr}, + time::Instant, +}; use anyhow::Context as _; +use queues::IsQueue; use tokio::{ io::copy, net::{TcpListener, TcpStream}, }; +use zksync_dal::ConnectionPool; +use zksync_types::proofs::{GpuProverInstanceStatus, SocketAddress}; + +use crate::synthesized_circuit_provider::SharedAssemblyQueue; #[allow(clippy::too_many_arguments)] pub async fn incoming_socket_listener( diff --git a/prover/prover/src/synthesized_circuit_provider.rs b/prover/prover/src/synthesized_circuit_provider.rs index 3c6939dc6aa3..e1cec64162b0 100644 --- a/prover/prover/src/synthesized_circuit_provider.rs +++ b/prover/prover/src/synthesized_circuit_provider.rs @@ -1,16 +1,16 @@ -use std::io::Cursor; -use std::io::Read; -use std::sync::Arc; -use tokio::sync::Mutex; +use std::{ + io::{Cursor, Read}, + sync::Arc, +}; use prover_service::RemoteSynthesizer; use queues::{Buffer, IsQueue}; - -use crate::metrics::METRICS; -use tokio::runtime::Handle; +use tokio::{runtime::Handle, sync::Mutex}; use zksync_dal::ConnectionPool; use zksync_types::proofs::SocketAddress; +use crate::metrics::METRICS; + pub type SharedAssemblyQueue = Arc>>>; pub struct SynthesizedCircuitProvider { diff --git a/prover/prover_fri/src/gpu_prover_job_processor.rs b/prover/prover_fri/src/gpu_prover_job_processor.rs index 9d7eda1202a7..b56388bce05b 100644 --- a/prover/prover_fri/src/gpu_prover_job_processor.rs +++ b/prover/prover_fri/src/gpu_prover_job_processor.rs @@ -1,39 +1,42 @@ #[cfg(feature = "gpu")] pub mod gpu_prover { - use std::collections::HashMap; - use std::{sync::Arc, time::Instant}; + use std::{collections::HashMap, sync::Arc, time::Instant}; use anyhow::Context as _; + use shivini::{gpu_prove_from_external_witness_data, ProverContext}; use tokio::task::JoinHandle; - use zksync_prover_fri_types::circuit_definitions::base_layer_proof_config; - use zksync_prover_fri_types::circuit_definitions::boojum::algebraic_props::round_function::AbsorptionModeOverwrite; - use zksync_prover_fri_types::circuit_definitions::boojum::algebraic_props::sponge::GoldilocksPoseidon2Sponge; - use zksync_prover_fri_types::circuit_definitions::boojum::cs::implementations::pow::NoPow; - - use zksync_prover_fri_types::circuit_definitions::boojum::cs::implementations::transcript::GoldilocksPoisedon2Transcript; - use zksync_prover_fri_types::circuit_definitions::boojum::worker::Worker; - use zksync_prover_fri_types::circuit_definitions::circuit_definitions::base_layer::ZkSyncBaseLayerProof; - use zksync_prover_fri_types::circuit_definitions::circuit_definitions::recursion_layer::ZkSyncRecursionLayerProof; - use zksync_prover_fri_types::WitnessVectorArtifacts; - - use crate::metrics::METRICS; - use zksync_config::configs::fri_prover_group::FriProverGroupConfig; - use zksync_config::configs::FriProverConfig; + use zksync_config::configs::{fri_prover_group::FriProverGroupConfig, FriProverConfig}; use zksync_dal::ConnectionPool; use zksync_env_config::FromEnv; use zksync_object_store::ObjectStore; - use zksync_prover_fri_types::{CircuitWrapper, FriProofWrapper, ProverServiceDataKey}; + use zksync_prover_fri_types::{ + circuit_definitions::{ + base_layer_proof_config, + boojum::{ + algebraic_props::{ + round_function::AbsorptionModeOverwrite, sponge::GoldilocksPoseidon2Sponge, + }, + cs::implementations::{pow::NoPow, transcript::GoldilocksPoisedon2Transcript}, + worker::Worker, + }, + circuit_definitions::{ + base_layer::ZkSyncBaseLayerProof, recursion_layer::ZkSyncRecursionLayerProof, + }, + }, + CircuitWrapper, FriProofWrapper, ProverServiceDataKey, WitnessVectorArtifacts, + }; use zksync_queued_job_processor::{async_trait, JobProcessor}; use zksync_types::{basic_fri_types::CircuitIdRoundTuple, proofs::SocketAddress}; - use zksync_vk_setup_data_server_fri::get_setup_data_for_circuit_type; - use { - shivini::gpu_prove_from_external_witness_data, shivini::ProverContext, - zksync_vk_setup_data_server_fri::GoldilocksGpuProverSetupData, + use zksync_vk_setup_data_server_fri::{ + get_setup_data_for_circuit_type, GoldilocksGpuProverSetupData, }; - use crate::utils::{ - get_setup_data_key, save_proof, setup_metadata_to_setup_data_key, verify_proof, - GpuProverJob, ProverArtifacts, SharedWitnessVectorQueue, + use crate::{ + metrics::METRICS, + utils::{ + get_setup_data_key, save_proof, setup_metadata_to_setup_data_key, verify_proof, + GpuProverJob, ProverArtifacts, SharedWitnessVectorQueue, + }, }; type DefaultTranscript = GoldilocksPoisedon2Transcript; diff --git a/prover/prover_fri/src/main.rs b/prover/prover_fri/src/main.rs index ab0994a36489..ff59fdbabfcd 100644 --- a/prover/prover_fri/src/main.rs +++ b/prover/prover_fri/src/main.rs @@ -1,13 +1,16 @@ #![feature(generic_const_exprs)] -use anyhow::Context as _; use std::future::Future; -use tokio::sync::oneshot; -use tokio::sync::watch::Receiver; -use tokio::task::JoinHandle; +use anyhow::Context as _; +use local_ip_address::local_ip; use prometheus_exporter::PrometheusExporterConfig; -use zksync_config::configs::fri_prover_group::FriProverGroupConfig; -use zksync_config::configs::{FriProverConfig, PostgresConfig, ProverGroupConfig}; +use tokio::{ + sync::{oneshot, watch::Receiver}, + task::JoinHandle, +}; +use zksync_config::configs::{ + fri_prover_group::FriProverGroupConfig, FriProverConfig, PostgresConfig, ProverGroupConfig, +}; use zksync_dal::ConnectionPool; use zksync_env_config::{ object_store::{ProverObjectStoreConfig, PublicObjectStoreConfig}, @@ -15,22 +18,20 @@ use zksync_env_config::{ }; use zksync_object_store::{ObjectStore, ObjectStoreFactory}; use zksync_prover_fri_utils::get_all_circuit_id_round_tuples_for; - -use local_ip_address::local_ip; use zksync_prover_utils::region_fetcher::get_zone; use zksync_queued_job_processor::JobProcessor; -use zksync_types::basic_fri_types::CircuitIdRoundTuple; -use zksync_types::proofs::GpuProverInstanceStatus; -use zksync_types::proofs::SocketAddress; +use zksync_types::{ + basic_fri_types::CircuitIdRoundTuple, + proofs::{GpuProverInstanceStatus, SocketAddress}, +}; use zksync_utils::wait_for_tasks::wait_for_tasks; mod gpu_prover_job_processor; +mod metrics; mod prover_job_processor; mod socket_listener; mod utils; -mod metrics; - async fn graceful_shutdown(port: u16) -> anyhow::Result> { let postgres_config = PostgresConfig::from_env().context("PostgresConfig::from_env()")?; let pool = ConnectionPool::singleton(postgres_config.prover_url()?) @@ -170,9 +171,10 @@ async fn get_prover_tasks( pool: ConnectionPool, circuit_ids_for_round_to_be_proven: Vec, ) -> anyhow::Result>>> { - use crate::prover_job_processor::{load_setup_data_cache, Prover}; use zksync_vk_setup_data_server_fri::commitment_utils::get_cached_commitments; + use crate::prover_job_processor::{load_setup_data_cache, Prover}; + let vk_commitments = get_cached_commitments(); tracing::info!( @@ -203,9 +205,10 @@ async fn get_prover_tasks( pool: ConnectionPool, circuit_ids_for_round_to_be_proven: Vec, ) -> anyhow::Result>>> { + use std::sync::Arc; + use gpu_prover_job_processor::gpu_prover; use socket_listener::gpu_socket_listener; - use std::sync::Arc; use tokio::sync::Mutex; use zksync_prover_fri_types::queue::FixedSizeQueue; diff --git a/prover/prover_fri/src/metrics.rs b/prover/prover_fri/src/metrics.rs index 27ddce54d6c1..f6f7adb817d3 100644 --- a/prover/prover_fri/src/metrics.rs +++ b/prover/prover_fri/src/metrics.rs @@ -1,4 +1,5 @@ use std::time::Duration; + use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, LabeledFamily, Metrics}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelSet)] diff --git a/prover/prover_fri/src/prover_job_processor.rs b/prover/prover_fri/src/prover_job_processor.rs index 30beae5bc82d..dbe4bee0c86e 100644 --- a/prover/prover_fri/src/prover_job_processor.rs +++ b/prover/prover_fri/src/prover_job_processor.rs @@ -1,31 +1,27 @@ -use std::collections::HashMap; -use std::{sync::Arc, time::Instant}; +use std::{collections::HashMap, sync::Arc, time::Instant}; use anyhow::Context as _; use tokio::task::JoinHandle; -use zksync_prover_fri_types::circuit_definitions::aux_definitions::witness_oracle::VmWitnessOracle; -use zksync_prover_fri_types::circuit_definitions::boojum::cs::implementations::pow::NoPow; -use zksync_prover_fri_types::circuit_definitions::boojum::field::goldilocks::GoldilocksField; -use zksync_prover_fri_types::circuit_definitions::boojum::worker::Worker; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::base_layer::{ - ZkSyncBaseLayerCircuit, ZkSyncBaseLayerProof, -}; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::recursion_layer::{ - ZkSyncRecursionLayerProof, ZkSyncRecursiveLayerCircuit, -}; -use zksync_prover_fri_types::circuit_definitions::{ - base_layer_proof_config, recursion_layer_proof_config, ZkSyncDefaultRoundFunction, -}; - use zkevm_test_harness::prover_utils::{prove_base_layer_circuit, prove_recursion_layer_circuit}; - -use crate::metrics::{CircuitLabels, Layer, METRICS}; -use zksync_config::configs::fri_prover_group::FriProverGroupConfig; -use zksync_config::configs::FriProverConfig; +use zksync_config::configs::{fri_prover_group::FriProverGroupConfig, FriProverConfig}; use zksync_dal::ConnectionPool; use zksync_env_config::FromEnv; use zksync_object_store::ObjectStore; -use zksync_prover_fri_types::{CircuitWrapper, FriProofWrapper, ProverJob, ProverServiceDataKey}; +use zksync_prover_fri_types::{ + circuit_definitions::{ + aux_definitions::witness_oracle::VmWitnessOracle, + base_layer_proof_config, + boojum::{ + cs::implementations::pow::NoPow, field::goldilocks::GoldilocksField, worker::Worker, + }, + circuit_definitions::{ + base_layer::{ZkSyncBaseLayerCircuit, ZkSyncBaseLayerProof}, + recursion_layer::{ZkSyncRecursionLayerProof, ZkSyncRecursiveLayerCircuit}, + }, + recursion_layer_proof_config, ZkSyncDefaultRoundFunction, + }, + CircuitWrapper, FriProofWrapper, ProverJob, ProverServiceDataKey, +}; use zksync_prover_fri_utils::fetch_next_circuit; use zksync_queued_job_processor::{async_trait, JobProcessor}; use zksync_types::{basic_fri_types::CircuitIdRoundTuple, protocol_version::L1VerifierConfig}; @@ -33,8 +29,12 @@ use zksync_vk_setup_data_server_fri::{ get_cpu_setup_data_for_circuit_type, GoldilocksProverSetupData, }; -use crate::utils::{ - get_setup_data_key, save_proof, setup_metadata_to_setup_data_key, verify_proof, ProverArtifacts, +use crate::{ + metrics::{CircuitLabels, Layer, METRICS}, + utils::{ + get_setup_data_key, save_proof, setup_metadata_to_setup_data_key, verify_proof, + ProverArtifacts, + }, }; pub enum SetupLoadMode { diff --git a/prover/prover_fri/src/socket_listener.rs b/prover/prover_fri/src/socket_listener.rs index e9ecbd1e60b0..653fb4eb8dad 100644 --- a/prover/prover_fri/src/socket_listener.rs +++ b/prover/prover_fri/src/socket_listener.rs @@ -1,27 +1,28 @@ #[cfg(feature = "gpu")] pub mod gpu_socket_listener { + use std::{net::SocketAddr, time::Instant}; + + use anyhow::Context as _; use shivini::synthesis_utils::{ init_base_layer_cs_for_repeated_proving, init_recursive_layer_cs_for_repeated_proving, }; - use std::net::SocketAddr; - use std::time::Instant; - use zksync_dal::ConnectionPool; - use zksync_types::proofs::AggregationRound; - use zksync_types::proofs::{GpuProverInstanceStatus, SocketAddress}; - use zksync_vk_setup_data_server_fri::{ - get_finalization_hints, get_round_for_recursive_circuit_type, - }; - - use crate::metrics::METRICS; - use crate::utils::{GpuProverJob, ProvingAssembly, SharedWitnessVectorQueue}; - use anyhow::Context as _; - use tokio::sync::watch; use tokio::{ io::copy, net::{TcpListener, TcpStream}, + sync::watch, }; + use zksync_dal::ConnectionPool; use zksync_object_store::bincode; use zksync_prover_fri_types::{CircuitWrapper, ProverServiceDataKey, WitnessVectorArtifacts}; + use zksync_types::proofs::{AggregationRound, GpuProverInstanceStatus, SocketAddress}; + use zksync_vk_setup_data_server_fri::{ + get_finalization_hints, get_round_for_recursive_circuit_type, + }; + + use crate::{ + metrics::METRICS, + utils::{GpuProverJob, ProvingAssembly, SharedWitnessVectorQueue}, + }; pub(crate) struct SocketListener { address: SocketAddress, diff --git a/prover/prover_fri/src/utils.rs b/prover/prover_fri/src/utils.rs index c67ee9149f11..d86adbf4e899 100644 --- a/prover/prover_fri/src/utils.rs +++ b/prover/prover_fri/src/utils.rs @@ -1,33 +1,35 @@ #![cfg_attr(not(feature = "gpu"), allow(unused_imports))] -use std::sync::Arc; -use std::time::Instant; -use zksync_prover_fri_types::circuit_definitions::boojum::config::ProvingCSConfig; -use zksync_prover_fri_types::circuit_definitions::boojum::cs::implementations::reference_cs::CSReferenceAssembly; +use std::{sync::Arc, time::Instant}; use tokio::sync::Mutex; use zkevm_test_harness::prover_utils::{verify_base_layer_proof, verify_recursion_layer_proof}; use zksync_dal::StorageProcessor; use zksync_object_store::ObjectStore; -use zksync_prover_fri_types::circuit_definitions::boojum::algebraic_props::round_function::AbsorptionModeOverwrite; -use zksync_prover_fri_types::circuit_definitions::boojum::algebraic_props::sponge::GoldilocksPoseidon2Sponge; -use zksync_prover_fri_types::circuit_definitions::boojum::cs::implementations::pow::NoPow; -use zksync_prover_fri_types::circuit_definitions::boojum::cs::implementations::proof::Proof; -use zksync_prover_fri_types::circuit_definitions::boojum::cs::implementations::verifier::VerificationKey; -use zksync_prover_fri_types::circuit_definitions::boojum::field::goldilocks::{ - GoldilocksExt2, GoldilocksField, -}; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::recursion_layer::{ - ZkSyncRecursionLayerProof, ZkSyncRecursionLayerStorageType, -}; -use zksync_prover_fri_types::queue::FixedSizeQueue; use zksync_prover_fri_types::{ + circuit_definitions::{ + boojum::{ + algebraic_props::{ + round_function::AbsorptionModeOverwrite, sponge::GoldilocksPoseidon2Sponge, + }, + config::ProvingCSConfig, + cs::implementations::{ + pow::NoPow, proof::Proof, reference_cs::CSReferenceAssembly, + verifier::VerificationKey, + }, + field::goldilocks::{GoldilocksExt2, GoldilocksField}, + }, + circuit_definitions::recursion_layer::{ + ZkSyncRecursionLayerProof, ZkSyncRecursionLayerStorageType, + }, + }, + queue::FixedSizeQueue, CircuitWrapper, FriProofWrapper, ProverServiceDataKey, WitnessVectorArtifacts, }; use zksync_prover_fri_utils::get_base_layer_circuit_id_for_recursive_layer; +use zksync_types::{basic_fri_types::CircuitIdRoundTuple, proofs::AggregationRound, L1BatchNumber}; use crate::metrics::METRICS; -use zksync_types::{basic_fri_types::CircuitIdRoundTuple, proofs::AggregationRound, L1BatchNumber}; pub type F = GoldilocksField; pub type H = GoldilocksPoseidon2Sponge; diff --git a/prover/prover_fri/tests/basic_test.rs b/prover/prover_fri/tests/basic_test.rs index 133598d66db2..89089ac8249e 100644 --- a/prover/prover_fri/tests/basic_test.rs +++ b/prover/prover_fri/tests/basic_test.rs @@ -1,16 +1,13 @@ -use anyhow::Context as _; use std::sync::Arc; -use zksync_config::configs::FriProverConfig; -use zksync_config::ObjectStoreConfig; +use anyhow::Context as _; +use serde::Serialize; +use zksync_config::{configs::FriProverConfig, ObjectStoreConfig}; use zksync_env_config::FromEnv; use zksync_object_store::{bincode, FriCircuitKey, ObjectStoreFactory}; -use zksync_types::proofs::AggregationRound; -use zksync_types::L1BatchNumber; - -use serde::Serialize; use zksync_prover_fri::prover_job_processor::Prover; use zksync_prover_fri_types::{CircuitWrapper, ProverJob, ProverServiceDataKey}; +use zksync_types::{proofs::AggregationRound, L1BatchNumber}; use zksync_vk_setup_data_server_fri::generate_cpu_base_layer_setup_data; fn compare_serialized(expected: &T, actual: &T) { diff --git a/prover/prover_fri_gateway/src/api_data_fetcher.rs b/prover/prover_fri_gateway/src/api_data_fetcher.rs index 339a7bec9e6a..f56a9af4cc8f 100644 --- a/prover/prover_fri_gateway/src/api_data_fetcher.rs +++ b/prover/prover_fri_gateway/src/api_data_fetcher.rs @@ -1,14 +1,14 @@ use std::time::Duration; -use crate::metrics::METRICS; use async_trait::async_trait; use reqwest::Client; use serde::{de::DeserializeOwned, Serialize}; -use tokio::sync::watch; -use tokio::time::sleep; +use tokio::{sync::watch, time::sleep}; use zksync_dal::ConnectionPool; use zksync_object_store::ObjectStore; +use crate::metrics::METRICS; + /// The path to the API endpoint that returns the next proof generation data. pub(crate) const PROOF_GENERATION_DATA_PATH: &str = "/proof_generation_data"; diff --git a/prover/prover_fri_gateway/src/main.rs b/prover/prover_fri_gateway/src/main.rs index 3a3f8b42ae0c..15329ce955a8 100644 --- a/prover/prover_fri_gateway/src/main.rs +++ b/prover/prover_fri_gateway/src/main.rs @@ -1,9 +1,7 @@ use anyhow::Context as _; -use reqwest::Client; -use tokio::{sync::oneshot, sync::watch}; - -use crate::api_data_fetcher::{PeriodicApiStruct, PROOF_GENERATION_DATA_PATH, SUBMIT_PROOF_PATH}; use prometheus_exporter::PrometheusExporterConfig; +use reqwest::Client; +use tokio::sync::{oneshot, watch}; use zksync_config::configs::{FriProverGatewayConfig, PostgresConfig}; use zksync_dal::ConnectionPool; use zksync_env_config::{object_store::ProverObjectStoreConfig, FromEnv}; @@ -11,6 +9,8 @@ use zksync_object_store::ObjectStoreFactory; use zksync_types::prover_server_api::{ProofGenerationDataRequest, SubmitProofRequest}; use zksync_utils::wait_for_tasks::wait_for_tasks; +use crate::api_data_fetcher::{PeriodicApiStruct, PROOF_GENERATION_DATA_PATH, SUBMIT_PROOF_PATH}; + mod api_data_fetcher; mod metrics; mod proof_gen_data_fetcher; diff --git a/prover/prover_fri_gateway/src/proof_gen_data_fetcher.rs b/prover/prover_fri_gateway/src/proof_gen_data_fetcher.rs index 1f00c7f74299..a25d447ad221 100644 --- a/prover/prover_fri_gateway/src/proof_gen_data_fetcher.rs +++ b/prover/prover_fri_gateway/src/proof_gen_data_fetcher.rs @@ -1,5 +1,4 @@ use async_trait::async_trait; - use zksync_types::prover_server_api::{ ProofGenerationData, ProofGenerationDataRequest, ProofGenerationDataResponse, }; diff --git a/prover/prover_fri_gateway/src/proof_submitter.rs b/prover/prover_fri_gateway/src/proof_submitter.rs index 86b2e4004b33..78c7a6a6d8e7 100644 --- a/prover/prover_fri_gateway/src/proof_submitter.rs +++ b/prover/prover_fri_gateway/src/proof_submitter.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; use zksync_dal::fri_proof_compressor_dal::ProofCompressionJobStatus; - -use zksync_types::prover_server_api::{SubmitProofRequest, SubmitProofResponse}; -use zksync_types::L1BatchNumber; +use zksync_types::{ + prover_server_api::{SubmitProofRequest, SubmitProofResponse}, + L1BatchNumber, +}; use crate::api_data_fetcher::{PeriodicApi, PeriodicApiStruct}; diff --git a/prover/prover_fri_types/src/lib.rs b/prover/prover_fri_types/src/lib.rs index 9e84c02f0574..c244cb99f5a7 100644 --- a/prover/prover_fri_types/src/lib.rs +++ b/prover/prover_fri_types/src/lib.rs @@ -1,24 +1,20 @@ -pub mod queue; +use std::env; pub use circuit_definitions; -use std::env; +use circuit_definitions::{ + aux_definitions::witness_oracle::VmWitnessOracle, + boojum::{cs::implementations::witness::WitnessVec, field::goldilocks::GoldilocksField}, + circuit_definitions::{ + base_layer::{ZkSyncBaseLayerCircuit, ZkSyncBaseLayerProof}, + recursion_layer::{ZkSyncRecursionLayerProof, ZkSyncRecursiveLayerCircuit}, + }, + zkevm_circuits::scheduler::block_header::BlockAuxilaryOutputWitness, + ZkSyncDefaultRoundFunction, +}; +use zksync_object_store::{serialize_using_bincode, Bucket, FriCircuitKey, StoredObject}; +use zksync_types::{proofs::AggregationRound, L1BatchNumber}; -use circuit_definitions::aux_definitions::witness_oracle::VmWitnessOracle; -use circuit_definitions::boojum::cs::implementations::witness::WitnessVec; -use circuit_definitions::boojum::field::goldilocks::GoldilocksField; -use circuit_definitions::circuit_definitions::base_layer::ZkSyncBaseLayerCircuit; -use circuit_definitions::circuit_definitions::base_layer::ZkSyncBaseLayerProof; -use circuit_definitions::circuit_definitions::recursion_layer::ZkSyncRecursionLayerProof; -use circuit_definitions::circuit_definitions::recursion_layer::ZkSyncRecursiveLayerCircuit; -use circuit_definitions::zkevm_circuits::scheduler::block_header::BlockAuxilaryOutputWitness; -use circuit_definitions::ZkSyncDefaultRoundFunction; - -use zksync_object_store::serialize_using_bincode; -use zksync_object_store::Bucket; -use zksync_object_store::FriCircuitKey; -use zksync_object_store::StoredObject; -use zksync_types::proofs::AggregationRound; -use zksync_types::L1BatchNumber; +pub mod queue; #[derive(serde::Serialize, serde::Deserialize, Clone)] #[allow(clippy::large_enum_variant)] diff --git a/prover/prover_fri_utils/src/lib.rs b/prover/prover_fri_utils/src/lib.rs index f0edcd07902d..eee7293b591b 100644 --- a/prover/prover_fri_utils/src/lib.rs +++ b/prover/prover_fri_utils/src/lib.rs @@ -2,18 +2,21 @@ use std::time::Instant; use zksync_dal::StorageProcessor; use zksync_object_store::{FriCircuitKey, ObjectStore}; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::recursion_layer::base_circuit_type_into_recursive_leaf_circuit_type; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::recursion_layer::ZkSyncRecursionLayerStorageType; -use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::scheduler::aux::BaseLayerCircuitType; -use zksync_types::basic_fri_types::CircuitIdRoundTuple; - use zksync_prover_fri_types::{ + circuit_definitions::{ + circuit_definitions::recursion_layer::{ + base_circuit_type_into_recursive_leaf_circuit_type, ZkSyncRecursionLayerStorageType, + }, + zkevm_circuits::scheduler::aux::BaseLayerCircuitType, + }, get_current_pod_name, CircuitWrapper, ProverJob, ProverServiceDataKey, }; +use zksync_types::{ + basic_fri_types::CircuitIdRoundTuple, proofs::AggregationRound, + protocol_version::L1VerifierConfig, +}; use crate::metrics::{CircuitLabels, PROVER_FRI_UTILS_METRICS}; -use zksync_types::proofs::AggregationRound; -use zksync_types::protocol_version::L1VerifierConfig; pub mod metrics; pub mod socket_utils; diff --git a/prover/prover_fri_utils/src/metrics.rs b/prover/prover_fri_utils/src/metrics.rs index 767e2c25fc17..acb48bacb3e3 100644 --- a/prover/prover_fri_utils/src/metrics.rs +++ b/prover/prover_fri_utils/src/metrics.rs @@ -1,4 +1,5 @@ use std::time::Duration; + use vise::{Buckets, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics}; use zksync_types::proofs::AggregationRound; diff --git a/prover/prover_fri_utils/src/socket_utils.rs b/prover/prover_fri_utils/src/socket_utils.rs index 14e31e56f00a..c0c5ddcbcb9b 100644 --- a/prover/prover_fri_utils/src/socket_utils.rs +++ b/prover/prover_fri_utils/src/socket_utils.rs @@ -1,9 +1,9 @@ -use std::io::copy; -use std::io::ErrorKind; -use std::io::Read; -use std::net::SocketAddr; -use std::net::TcpStream; -use std::time::{Duration, Instant}; +use std::{ + io::{copy, ErrorKind, Read}, + net::{SocketAddr, TcpStream}, + time::{Duration, Instant}, +}; + use zksync_types::proofs::SocketAddress; pub fn send_assembly( diff --git a/prover/setup_key_generator_and_server/src/lib.rs b/prover/setup_key_generator_and_server/src/lib.rs index a2e4f0998f85..34b4896cefe1 100644 --- a/prover/setup_key_generator_and_server/src/lib.rs +++ b/prover/setup_key_generator_and_server/src/lib.rs @@ -1,18 +1,20 @@ -use anyhow::Context as _; -use std::fs::File; -use std::io::Read; -use std::path::Path; - -use zkevm_test_harness::abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit; -use zkevm_test_harness::bellman::bn256::Bn256; -use zkevm_test_harness::witness::oracle::VmWitnessOracle; -use zkevm_test_harness::witness::recursive_aggregation::padding_aggregations; -use zkevm_test_harness::witness::vk_set_generator::circuits_for_vk_generation; -use zksync_types::circuit::GEOMETRY_CONFIG; +use std::{fs::File, io::Read, path::Path}; +use anyhow::Context as _; +use zkevm_test_harness::{ + abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit, + bellman::bn256::Bn256, + witness::{ + oracle::VmWitnessOracle, recursive_aggregation::padding_aggregations, + vk_set_generator::circuits_for_vk_generation, + }, +}; use zksync_config::ProverConfigs; use zksync_env_config::FromEnv; -use zksync_types::circuit::{LEAF_SPLITTING_FACTOR, NODE_SPLITTING_FACTOR, SCHEDULER_UPPER_BOUND}; +use zksync_types::circuit::{ + GEOMETRY_CONFIG, LEAF_SPLITTING_FACTOR, NODE_SPLITTING_FACTOR, SCHEDULER_UPPER_BOUND, +}; + pub fn get_setup_for_circuit_type(circuit_type: u8) -> anyhow::Result> { let filepath = get_setup_key_file_path(circuit_type).context("get_setup_key_file_path()")?; tracing::info!("Fetching setup key from path: {}", filepath); diff --git a/prover/setup_key_generator_and_server/src/main.rs b/prover/setup_key_generator_and_server/src/main.rs index ea56a15aed7e..9eee0aa5c09e 100644 --- a/prover/setup_key_generator_and_server/src/main.rs +++ b/prover/setup_key_generator_and_server/src/main.rs @@ -1,12 +1,13 @@ #![cfg_attr(not(feature = "gpu"), allow(unused_imports))] +use std::{env, fs::File}; + use anyhow::Context as _; -use std::env; -use std::fs::File; use structopt::StructOpt; -use zkevm_test_harness::abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit; -use zkevm_test_harness::bellman::bn256::Bn256; -use zkevm_test_harness::witness::oracle::VmWitnessOracle; +use zkevm_test_harness::{ + abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit, bellman::bn256::Bn256, + witness::oracle::VmWitnessOracle, +}; use zksync_setup_key_server::{get_circuits_for_vk, get_setup_key_write_file_path}; #[cfg(feature = "gpu")] diff --git a/prover/vk_setup_data_generator_server_fri/src/commitment_utils.rs b/prover/vk_setup_data_generator_server_fri/src/commitment_utils.rs index d04943dd1d69..d6efbb29c71e 100644 --- a/prover/vk_setup_data_generator_server_fri/src/commitment_utils.rs +++ b/prover/vk_setup_data_generator_server_fri/src/commitment_utils.rs @@ -1,16 +1,21 @@ -use crate::get_recursive_layer_vk_for_circuit_type; -use crate::utils::get_leaf_vk_params; +use std::str::FromStr; + use anyhow::Context as _; use once_cell::sync::Lazy; -use std::str::FromStr; use structopt::lazy_static::lazy_static; use zkevm_test_harness::witness::recursive_aggregation::{ compute_leaf_vks_and_params_commitment, compute_node_vk_commitment, }; -use zksync_prover_fri_types::circuit_definitions::boojum::field::goldilocks::GoldilocksField; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::recursion_layer::ZkSyncRecursionLayerStorageType; -use zksync_types::protocol_version::{L1VerifierConfig, VerifierParams}; -use zksync_types::H256; +use zksync_prover_fri_types::circuit_definitions::{ + boojum::field::goldilocks::GoldilocksField, + circuit_definitions::recursion_layer::ZkSyncRecursionLayerStorageType, +}; +use zksync_types::{ + protocol_version::{L1VerifierConfig, VerifierParams}, + H256, +}; + +use crate::{get_recursive_layer_vk_for_circuit_type, utils::get_leaf_vk_params}; lazy_static! { // TODO: do not initialize a static const with data read in runtime. diff --git a/prover/vk_setup_data_generator_server_fri/src/lib.rs b/prover/vk_setup_data_generator_server_fri/src/lib.rs index 75125aa81508..19fab9af470b 100644 --- a/prover/vk_setup_data_generator_server_fri/src/lib.rs +++ b/prover/vk_setup_data_generator_server_fri/src/lib.rs @@ -1,62 +1,65 @@ #![feature(generic_const_exprs)] #![feature(allocator_api)] +use std::{fs, fs::File, io::Read}; + use anyhow::Context as _; use circuit_definitions::circuit_definitions::aux_layer::{ ZkSyncCompressionLayerStorageType, ZkSyncSnarkWrapperVK, }; -use std::fs; -use std::fs::File; -use std::io::Read; -use zksync_prover_fri_types::circuit_definitions::aux_definitions::witness_oracle::VmWitnessOracle; -use zksync_prover_fri_types::circuit_definitions::boojum::algebraic_props::round_function::AbsorptionModeOverwrite; -use zksync_prover_fri_types::circuit_definitions::boojum::algebraic_props::sponge::GenericAlgebraicSponge; - -use zksync_prover_fri_types::circuit_definitions::boojum::cs::implementations::hints::{ - DenseVariablesCopyHint, DenseWitnessCopyHint, -}; -use zksync_prover_fri_types::circuit_definitions::boojum::cs::implementations::polynomial_storage::{ - SetupBaseStorage, SetupStorage, -}; -use zksync_prover_fri_types::circuit_definitions::boojum::cs::implementations::setup::FinalizationHintsForProver; -use zksync_prover_fri_types::circuit_definitions::boojum::cs::implementations::verifier::VerificationKey; -use zksync_prover_fri_types::circuit_definitions::boojum::cs::oracle::merkle_tree::MerkleTreeWithCap; -use zksync_prover_fri_types::circuit_definitions::boojum::cs::oracle::TreeHasher; -use zksync_prover_fri_types::circuit_definitions::boojum::field::goldilocks::GoldilocksField; -use zksync_prover_fri_types::circuit_definitions::boojum::field::{PrimeField, SmallField}; - -use zksync_prover_fri_types::circuit_definitions::boojum::field::traits::field_like::PrimeFieldLikeVectorized; -use zksync_prover_fri_types::circuit_definitions::boojum::implementations::poseidon2::Poseidon2Goldilocks; -use zksync_prover_fri_types::circuit_definitions::boojum::worker::Worker; - -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::base_layer::{ - ZkSyncBaseLayerCircuit, ZkSyncBaseLayerVerificationKey, -}; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::recursion_layer::{ - ZkSyncRecursionLayerStorageType, ZkSyncRecursionLayerVerificationKey, -}; -use zksync_prover_fri_types::circuit_definitions::{ - ZkSyncDefaultRoundFunction, BASE_LAYER_CAP_SIZE, BASE_LAYER_FRI_LDE_FACTOR, -}; - -use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; use zkevm_test_harness::prover_utils::create_base_layer_setup_data; use zksync_config::configs::FriProverConfig; use zksync_env_config::FromEnv; -use zksync_types::proofs::AggregationRound; -use zksync_types::zkevm_test_harness::abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit; -use zksync_types::zkevm_test_harness::bellman::bn256::Bn256; -use zksync_types::zkevm_test_harness::bellman::plonk::better_better_cs::setup::VerificationKey as SnarkVerificationKey; -use zksync_types::zkevm_test_harness::witness::oracle::VmWitnessOracle as SnarkWitnessOracle; +use zksync_prover_fri_types::{ + circuit_definitions::{ + aux_definitions::witness_oracle::VmWitnessOracle, + boojum::{ + algebraic_props::{ + round_function::AbsorptionModeOverwrite, sponge::GenericAlgebraicSponge, + }, + cs::{ + implementations::{ + hints::{DenseVariablesCopyHint, DenseWitnessCopyHint}, + polynomial_storage::{SetupBaseStorage, SetupStorage}, + setup::FinalizationHintsForProver, + verifier::VerificationKey, + }, + oracle::{merkle_tree::MerkleTreeWithCap, TreeHasher}, + }, + field::{ + goldilocks::GoldilocksField, traits::field_like::PrimeFieldLikeVectorized, + PrimeField, SmallField, + }, + implementations::poseidon2::Poseidon2Goldilocks, + worker::Worker, + }, + circuit_definitions::{ + base_layer::{ZkSyncBaseLayerCircuit, ZkSyncBaseLayerVerificationKey}, + recursion_layer::{ + ZkSyncRecursionLayerStorageType, ZkSyncRecursionLayerVerificationKey, + }, + }, + ZkSyncDefaultRoundFunction, BASE_LAYER_CAP_SIZE, BASE_LAYER_FRI_LDE_FACTOR, + }, + ProverServiceDataKey, +}; +use zksync_types::{ + proofs::AggregationRound, + zkevm_test_harness::{ + abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit, + bellman::{ + bn256::Bn256, plonk::better_better_cs::setup::VerificationKey as SnarkVerificationKey, + }, + witness::oracle::VmWitnessOracle as SnarkWitnessOracle, + }, +}; +#[cfg(feature = "gpu")] +use {shivini::cs::GpuSetup, std::alloc::Global}; pub mod commitment_utils; pub mod utils; -use zksync_prover_fri_types::ProverServiceDataKey; -#[cfg(feature = "gpu")] -use {shivini::cs::GpuSetup, std::alloc::Global}; - #[derive(Debug, Serialize, Deserialize)] #[serde( bound = "F: serde::Serialize + serde::de::DeserializeOwned, P: serde::Serialize + serde::de::DeserializeOwned" diff --git a/prover/vk_setup_data_generator_server_fri/src/main.rs b/prover/vk_setup_data_generator_server_fri/src/main.rs index 6d2e1d0712a5..158a4390a967 100644 --- a/prover/vk_setup_data_generator_server_fri/src/main.rs +++ b/prover/vk_setup_data_generator_server_fri/src/main.rs @@ -1,17 +1,19 @@ #![feature(generic_const_exprs)] + use anyhow::Context as _; use circuit_definitions::circuit_definitions::recursion_layer::ZkSyncRecursionLayerVerificationKey; -use zkevm_test_harness::compute_setups::{ - generate_base_layer_vks_and_proofs, generate_recursive_layer_vks_and_proofs, +use zkevm_test_harness::{ + compute_setups::{generate_base_layer_vks_and_proofs, generate_recursive_layer_vks_and_proofs}, + data_source::{in_memory_data_source::InMemoryDataSource, SetupDataSource}, + proof_wrapper_utils::{get_wrapper_setup_and_vk_from_scheduler_vk, WrapperConfig}, }; -use zkevm_test_harness::data_source::in_memory_data_source::InMemoryDataSource; -use zkevm_test_harness::data_source::SetupDataSource; -use zkevm_test_harness::proof_wrapper_utils::{ - get_wrapper_setup_and_vk_from_scheduler_vk, WrapperConfig, +use zksync_prover_fri_types::{ + circuit_definitions::{ + circuit_definitions::recursion_layer::ZkSyncRecursionLayerStorageType, + zkevm_circuits::scheduler::aux::BaseLayerCircuitType, + }, + ProverServiceDataKey, }; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::recursion_layer::ZkSyncRecursionLayerStorageType; -use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::scheduler::aux::BaseLayerCircuitType; -use zksync_prover_fri_types::ProverServiceDataKey; use zksync_types::proofs::AggregationRound; use zksync_vk_setup_data_server_fri::{ get_round_for_recursive_circuit_type, save_base_layer_vk, save_finalization_hints, diff --git a/prover/vk_setup_data_generator_server_fri/src/setup_data_generator.rs b/prover/vk_setup_data_generator_server_fri/src/setup_data_generator.rs index 354594a556a0..5df4b75b3a65 100644 --- a/prover/vk_setup_data_generator_server_fri/src/setup_data_generator.rs +++ b/prover/vk_setup_data_generator_server_fri/src/setup_data_generator.rs @@ -1,28 +1,28 @@ use anyhow::Context as _; -use zksync_prover_fri_types::circuit_definitions::aux_definitions::witness_oracle::VmWitnessOracle; -use zksync_prover_fri_types::circuit_definitions::boojum::field::goldilocks::GoldilocksField; -use zksync_prover_fri_types::circuit_definitions::boojum::worker::Worker; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::base_layer::ZkSyncBaseLayerCircuit; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::recursion_layer::ZkSyncRecursiveLayerCircuit; -use zksync_prover_fri_types::circuit_definitions::{ - ZkSyncDefaultRoundFunction, BASE_LAYER_CAP_SIZE, BASE_LAYER_FRI_LDE_FACTOR, -}; - use structopt::StructOpt; -use zkevm_test_harness::geometry_config::get_geometry_config; -use zkevm_test_harness::prover_utils::create_recursive_layer_setup_data; -use zksync_types::proofs::AggregationRound; -use zksync_vk_setup_data_server_fri::generate_cpu_base_layer_setup_data; -use zksync_vk_setup_data_server_fri::utils::{ - get_basic_circuits, get_leaf_circuits, get_node_circuit, get_scheduler_circuit, CYCLE_LIMIT, +use zkevm_test_harness::{ + geometry_config::get_geometry_config, prover_utils::create_recursive_layer_setup_data, }; +use zksync_prover_fri_types::{ + circuit_definitions::{ + aux_definitions::witness_oracle::VmWitnessOracle, + boojum::{field::goldilocks::GoldilocksField, worker::Worker}, + circuit_definitions::{ + base_layer::ZkSyncBaseLayerCircuit, recursion_layer::ZkSyncRecursiveLayerCircuit, + }, + ZkSyncDefaultRoundFunction, BASE_LAYER_CAP_SIZE, BASE_LAYER_FRI_LDE_FACTOR, + }, + ProverServiceDataKey, +}; +use zksync_types::proofs::AggregationRound; use zksync_vk_setup_data_server_fri::{ - get_finalization_hints, get_recursive_layer_vk_for_circuit_type, - get_round_for_recursive_circuit_type, save_setup_data, GoldilocksProverSetupData, - ProverSetupData, + generate_cpu_base_layer_setup_data, get_finalization_hints, + get_recursive_layer_vk_for_circuit_type, get_round_for_recursive_circuit_type, save_setup_data, + utils::{ + get_basic_circuits, get_leaf_circuits, get_node_circuit, get_scheduler_circuit, CYCLE_LIMIT, + }, + GoldilocksProverSetupData, ProverSetupData, }; - -use zksync_prover_fri_types::ProverServiceDataKey; #[cfg(feature = "gpu")] use { shivini::cs::setup::GpuSetup, shivini::ProverContext, diff --git a/prover/vk_setup_data_generator_server_fri/src/tests.rs b/prover/vk_setup_data_generator_server_fri/src/tests.rs index 46cdc94562b0..3d6a203ed97c 100644 --- a/prover/vk_setup_data_generator_server_fri/src/tests.rs +++ b/prover/vk_setup_data_generator_server_fri/src/tests.rs @@ -1,9 +1,13 @@ use proptest::prelude::*; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::recursion_layer::{ - base_circuit_type_into_recursive_leaf_circuit_type, ZkSyncRecursionLayerStorageType, +use zksync_prover_fri_types::{ + circuit_definitions::{ + circuit_definitions::recursion_layer::{ + base_circuit_type_into_recursive_leaf_circuit_type, ZkSyncRecursionLayerStorageType, + }, + zkevm_circuits::scheduler::aux::BaseLayerCircuitType, + }, + ProverServiceDataKey, }; -use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::scheduler::aux::BaseLayerCircuitType; -use zksync_prover_fri_types::ProverServiceDataKey; use zksync_types::proofs::AggregationRound; use zksync_vk_setup_data_server_fri::{ get_base_layer_vk_for_circuit_type, get_base_path, get_file_path, get_finalization_hints, diff --git a/prover/vk_setup_data_generator_server_fri/src/utils.rs b/prover/vk_setup_data_generator_server_fri/src/utils.rs index 03c0fb4a2108..01b40de33943 100644 --- a/prover/vk_setup_data_generator_server_fri/src/utils.rs +++ b/prover/vk_setup_data_generator_server_fri/src/utils.rs @@ -1,57 +1,74 @@ -use crate::{ - get_base_layer_vk_for_circuit_type, get_base_path, get_recursive_layer_vk_for_circuit_type, -}; -use zksync_prover_fri_types::circuit_definitions::aux_definitions::witness_oracle::VmWitnessOracle; -use zksync_prover_fri_types::circuit_definitions::boojum::field::goldilocks::{GoldilocksExt2, GoldilocksField}; -use zksync_prover_fri_types::circuit_definitions::boojum::gadgets::queue::full_state_queue::FullStateCircuitQueueRawWitness; -use zksync_prover_fri_types::circuit_definitions::boojum::gadgets::recursion::recursive_tree_hasher::CircuitGoldilocksPoseidon2Sponge; -use zksync_prover_fri_types::circuit_definitions::boojum::gadgets::traits::allocatable::CSAllocatable; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::base_layer::ZkSyncBaseLayerCircuit; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::recursion_layer::leaf_layer::ZkSyncLeafLayerRecursiveCircuit; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::recursion_layer::node_layer::ZkSyncNodeLayerRecursiveCircuit; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::recursion_layer::scheduler::SchedulerCircuit; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::recursion_layer::{ - base_circuit_type_into_recursive_leaf_circuit_type, ZkSyncRecursionLayerStorageType, - ZkSyncRecursionProof, ZkSyncRecursiveLayerCircuit, RECURSION_ARITY, SCHEDULER_CAPACITY, -}; -use zksync_prover_fri_types::circuit_definitions::zk_evm::bytecode_to_code_hash; -use zksync_prover_fri_types::circuit_definitions::zk_evm::testing::storage::InMemoryStorage; -use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::recursion::leaf_layer::input::RecursionLeafParametersWitness; -use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::recursion::leaf_layer::input::{ - RecursionLeafInput, RecursionLeafInstanceWitness, -}; -use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::recursion::leaf_layer::LeafLayerRecursionConfig; -use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::recursion::node_layer::input::{ - RecursionNodeInput, RecursionNodeInstanceWitness, -}; -use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::recursion::node_layer::NodeLayerRecursionConfig; -use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::scheduler::aux::BaseLayerCircuitType; -use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::scheduler::input::SchedulerCircuitInstanceWitness; -use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::scheduler::SchedulerConfig; -use zksync_prover_fri_types::circuit_definitions::{ - base_layer_proof_config, recursion_layer_proof_config, zk_evm, ZkSyncDefaultRoundFunction, +use std::{ + collections::{HashMap, VecDeque}, + fs, }; + use anyhow::Context as _; use itertools::Itertools; -use std::collections::{HashMap, VecDeque}; -use std::fs; -use zkevm_test_harness::compute_setups::{ - generate_base_layer_vks_and_proofs, generate_recursive_layer_vks_and_proofs, +use zkevm_test_harness::{ + compute_setups::{generate_base_layer_vks_and_proofs, generate_recursive_layer_vks_and_proofs}, + data_source::{in_memory_data_source::InMemoryDataSource, BlockDataSource}, + ethereum_types::{Address, U256}, + external_calls::run, + helper::artifact_utils::{save_predeployed_contracts, TestArtifact}, + sha3::{Digest, Keccak256}, + toolset::GeometryConfig, + witness::{ + full_block_artifact::{ + BlockBasicCircuits, BlockBasicCircuitsPublicCompactFormsWitnesses, + BlockBasicCircuitsPublicInputs, + }, + recursive_aggregation::compute_leaf_params, + tree::{BinarySparseStorageTree, ZKSyncTestingTree}, + }, }; -use zkevm_test_harness::data_source::BlockDataSource; -use zkevm_test_harness::ethereum_types::{Address, U256}; -use zkevm_test_harness::external_calls::run; -use zkevm_test_harness::helper::artifact_utils::{save_predeployed_contracts, TestArtifact}; -use zkevm_test_harness::sha3::{Digest, Keccak256}; -use zkevm_test_harness::toolset::GeometryConfig; -use zkevm_test_harness::witness::full_block_artifact::{ - BlockBasicCircuits, BlockBasicCircuitsPublicCompactFormsWitnesses, - BlockBasicCircuitsPublicInputs, +use zksync_prover_fri_types::circuit_definitions::{ + aux_definitions::witness_oracle::VmWitnessOracle, + base_layer_proof_config, + boojum::{ + field::goldilocks::{GoldilocksExt2, GoldilocksField}, + gadgets::{ + queue::full_state_queue::FullStateCircuitQueueRawWitness, + recursion::recursive_tree_hasher::CircuitGoldilocksPoseidon2Sponge, + traits::allocatable::CSAllocatable, + }, + }, + circuit_definitions::{ + base_layer::ZkSyncBaseLayerCircuit, + recursion_layer::{ + base_circuit_type_into_recursive_leaf_circuit_type, + leaf_layer::ZkSyncLeafLayerRecursiveCircuit, + node_layer::ZkSyncNodeLayerRecursiveCircuit, scheduler::SchedulerCircuit, + ZkSyncRecursionLayerStorageType, ZkSyncRecursionProof, ZkSyncRecursiveLayerCircuit, + RECURSION_ARITY, SCHEDULER_CAPACITY, + }, + }, + recursion_layer_proof_config, zk_evm, + zk_evm::{bytecode_to_code_hash, testing::storage::InMemoryStorage}, + zkevm_circuits::{ + recursion::{ + leaf_layer::{ + input::{ + RecursionLeafInput, RecursionLeafInstanceWitness, + RecursionLeafParametersWitness, + }, + LeafLayerRecursionConfig, + }, + node_layer::{ + input::{RecursionNodeInput, RecursionNodeInstanceWitness}, + NodeLayerRecursionConfig, + }, + }, + scheduler::{ + aux::BaseLayerCircuitType, input::SchedulerCircuitInstanceWitness, SchedulerConfig, + }, + }, + ZkSyncDefaultRoundFunction, }; -use zkevm_test_harness::witness::recursive_aggregation::compute_leaf_params; -use zkevm_test_harness::witness::tree::{BinarySparseStorageTree, ZKSyncTestingTree}; -use zkevm_test_harness::data_source::in_memory_data_source::InMemoryDataSource; +use crate::{ + get_base_layer_vk_for_circuit_type, get_base_path, get_recursive_layer_vk_for_circuit_type, +}; pub const CYCLE_LIMIT: usize = 20000; diff --git a/prover/vk_setup_data_generator_server_fri/src/vk_generator.rs b/prover/vk_setup_data_generator_server_fri/src/vk_generator.rs index 2a94b53eb633..2b633bc6d08d 100644 --- a/prover/vk_setup_data_generator_server_fri/src/vk_generator.rs +++ b/prover/vk_setup_data_generator_server_fri/src/vk_generator.rs @@ -1,23 +1,26 @@ use anyhow::Context as _; -use zkevm_test_harness::geometry_config::get_geometry_config; -use zkevm_test_harness::prover_utils::{ - create_base_layer_setup_data, create_recursive_layer_setup_data, +use zkevm_test_harness::{ + geometry_config::get_geometry_config, + prover_utils::{create_base_layer_setup_data, create_recursive_layer_setup_data}, }; -use zksync_prover_fri_types::circuit_definitions::boojum::worker::Worker; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::base_layer::ZkSyncBaseLayerVerificationKey; -use zksync_prover_fri_types::circuit_definitions::{ - BASE_LAYER_CAP_SIZE, BASE_LAYER_FRI_LDE_FACTOR, +use zksync_prover_fri_types::{ + circuit_definitions::{ + boojum::worker::Worker, + circuit_definitions::{ + base_layer::ZkSyncBaseLayerVerificationKey, + recursion_layer::ZkSyncRecursionLayerVerificationKey, + }, + BASE_LAYER_CAP_SIZE, BASE_LAYER_FRI_LDE_FACTOR, + }, + ProverServiceDataKey, }; -use zksync_vk_setup_data_server_fri::utils::{get_basic_circuits, get_leaf_circuits, CYCLE_LIMIT}; +use zksync_types::proofs::AggregationRound; use zksync_vk_setup_data_server_fri::{ get_round_for_recursive_circuit_type, save_base_layer_vk, save_finalization_hints, save_recursive_layer_vk, + utils::{get_basic_circuits, get_leaf_circuits, CYCLE_LIMIT}, }; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::recursion_layer::ZkSyncRecursionLayerVerificationKey; -use zksync_prover_fri_types::ProverServiceDataKey; -use zksync_types::proofs::AggregationRound; - #[allow(dead_code)] fn main() -> anyhow::Result<()> { tracing::info!("starting vk generator"); diff --git a/prover/witness_generator/src/basic_circuits.rs b/prover/witness_generator/src/basic_circuits.rs index 963ec034b261..b883c4bb35aa 100644 --- a/prover/witness_generator/src/basic_circuits.rs +++ b/prover/witness_generator/src/basic_circuits.rs @@ -1,54 +1,61 @@ -use std::hash::Hash; -use std::sync::Arc; use std::{ collections::{hash_map::DefaultHasher, HashMap, HashSet}, - hash::Hasher, + hash::{Hash, Hasher}, + sync::Arc, time::Instant, }; use anyhow::Context as _; use async_trait::async_trait; -use zksync_prover_fri_types::circuit_definitions::ZkSyncDefaultRoundFunction; -use rand::Rng; -use serde::{Deserialize, Serialize}; -use zksync_prover_fri_types::circuit_definitions::boojum::field::goldilocks::{GoldilocksExt2, GoldilocksField}; -use zksync_prover_fri_types::circuit_definitions::boojum::gadgets::recursion::recursive_tree_hasher::CircuitGoldilocksPoseidon2Sponge; -use zkevm_test_harness::geometry_config::get_geometry_config; -use zkevm_test_harness::toolset::GeometryConfig; -use zkevm_test_harness::witness::full_block_artifact::{ - BlockBasicCircuits, BlockBasicCircuitsPublicCompactFormsWitnesses, - BlockBasicCircuitsPublicInputs, -}; -use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::scheduler::block_header::BlockAuxilaryOutputWitness; -use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::scheduler::input::SchedulerCircuitInstanceWitness; -use zksync_prover_fri_types::{AuxOutputWitnessWrapper, get_current_pod_name}; - -use crate::metrics::WITNESS_GENERATOR_METRICS; -use crate::storage_oracle::StorageOracle; use multivm::vm_latest::{ constants::MAX_CYCLES_FOR_TX, HistoryDisabled, StorageOracle as VmStorageOracle, }; +use rand::Rng; +use serde::{Deserialize, Serialize}; +use zkevm_test_harness::{ + geometry_config::get_geometry_config, + toolset::GeometryConfig, + witness::full_block_artifact::{ + BlockBasicCircuits, BlockBasicCircuitsPublicCompactFormsWitnesses, + BlockBasicCircuitsPublicInputs, + }, +}; use zksync_config::configs::FriWitnessGeneratorConfig; -use zksync_dal::fri_witness_generator_dal::FriWitnessJobStatus; -use zksync_dal::ConnectionPool; +use zksync_dal::{fri_witness_generator_dal::FriWitnessJobStatus, ConnectionPool}; use zksync_object_store::{ Bucket, ClosedFormInputKey, ObjectStore, ObjectStoreFactory, StoredObject, }; +use zksync_prover_fri_types::{ + circuit_definitions::{ + boojum::{ + field::goldilocks::{GoldilocksExt2, GoldilocksField}, + gadgets::recursion::recursive_tree_hasher::CircuitGoldilocksPoseidon2Sponge, + }, + zkevm_circuits::scheduler::{ + block_header::BlockAuxilaryOutputWitness, input::SchedulerCircuitInstanceWitness, + }, + ZkSyncDefaultRoundFunction, + }, + get_current_pod_name, AuxOutputWitnessWrapper, +}; use zksync_prover_fri_utils::get_recursive_layer_circuit_id_for_base_layer; use zksync_queued_job_processor::JobProcessor; use zksync_state::{PostgresStorage, StorageView}; -use zksync_types::proofs::AggregationRound; -use zksync_types::protocol_version::FriProtocolVersionId; use zksync_types::{ - proofs::{BasicCircuitWitnessGeneratorInput, PrepareBasicCircuitsJob}, + proofs::{AggregationRound, BasicCircuitWitnessGeneratorInput, PrepareBasicCircuitsJob}, + protocol_version::FriProtocolVersionId, Address, L1BatchNumber, BOOTLOADER_ADDRESS, H256, U256, }; use zksync_utils::{bytes_to_chunks, h256_to_u256, u256_to_h256}; -use crate::precalculated_merkle_paths_provider::PrecalculatedMerklePathsProvider; -use crate::utils::{ - expand_bootloader_contents, save_base_prover_input_artifacts, ClosedFormInputWrapper, - SchedulerPartialInputWrapper, +use crate::{ + metrics::WITNESS_GENERATOR_METRICS, + precalculated_merkle_paths_provider::PrecalculatedMerklePathsProvider, + storage_oracle::StorageOracle, + utils::{ + expand_bootloader_contents, save_base_prover_input_artifacts, ClosedFormInputWrapper, + SchedulerPartialInputWrapper, + }, }; pub struct BasicCircuitArtifacts { diff --git a/prover/witness_generator/src/leaf_aggregation.rs b/prover/witness_generator/src/leaf_aggregation.rs index d90520a19e6f..f190aeb21645 100644 --- a/prover/witness_generator/src/leaf_aggregation.rs +++ b/prover/witness_generator/src/leaf_aggregation.rs @@ -1,36 +1,46 @@ -use zkevm_test_harness::witness::recursive_aggregation::{ - compute_leaf_params, create_leaf_witnesses, -}; - -use anyhow::Context as _; use std::time::Instant; +use anyhow::Context as _; use async_trait::async_trait; -use zksync_prover_fri_types::circuit_definitions::boojum::field::goldilocks::GoldilocksField; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::base_layer::{ - ZkSyncBaseLayerClosedFormInput, ZkSyncBaseLayerProof, ZkSyncBaseLayerVerificationKey, +use zkevm_test_harness::witness::recursive_aggregation::{ + compute_leaf_params, create_leaf_witnesses, +}; +use zksync_config::configs::FriWitnessGeneratorConfig; +use zksync_dal::ConnectionPool; +use zksync_object_store::{ClosedFormInputKey, ObjectStore, ObjectStoreFactory}; +use zksync_prover_fri_types::{ + circuit_definitions::{ + boojum::field::goldilocks::GoldilocksField, + circuit_definitions::{ + base_layer::{ + ZkSyncBaseLayerClosedFormInput, ZkSyncBaseLayerProof, + ZkSyncBaseLayerVerificationKey, + }, + recursion_layer::ZkSyncRecursiveLayerCircuit, + }, + encodings::recursion_request::RecursionQueueSimulator, + zkevm_circuits::recursion::leaf_layer::input::RecursionLeafParametersWitness, + }, + get_current_pod_name, FriProofWrapper, }; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::recursion_layer::ZkSyncRecursiveLayerCircuit; -use zksync_prover_fri_types::circuit_definitions::encodings::recursion_request::RecursionQueueSimulator; -use zksync_prover_fri_types::{get_current_pod_name, FriProofWrapper}; use zksync_prover_fri_utils::get_recursive_layer_circuit_id_for_base_layer; +use zksync_queued_job_processor::JobProcessor; +use zksync_types::{ + proofs::{AggregationRound, LeafAggregationJobMetadata}, + protocol_version::FriProtocolVersionId, + L1BatchNumber, +}; use zksync_vk_setup_data_server_fri::{ get_base_layer_vk_for_circuit_type, get_recursive_layer_vk_for_circuit_type, }; -use crate::metrics::WITNESS_GENERATOR_METRICS; -use crate::utils::{ - load_proofs_for_job_ids, save_node_aggregations_artifacts, - save_recursive_layer_prover_input_artifacts, ClosedFormInputWrapper, +use crate::{ + metrics::WITNESS_GENERATOR_METRICS, + utils::{ + load_proofs_for_job_ids, save_node_aggregations_artifacts, + save_recursive_layer_prover_input_artifacts, ClosedFormInputWrapper, + }, }; -use zksync_config::configs::FriWitnessGeneratorConfig; -use zksync_dal::ConnectionPool; -use zksync_object_store::{ClosedFormInputKey, ObjectStore, ObjectStoreFactory}; -use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::recursion::leaf_layer::input::RecursionLeafParametersWitness; -use zksync_queued_job_processor::JobProcessor; -use zksync_types::proofs::{AggregationRound, LeafAggregationJobMetadata}; -use zksync_types::protocol_version::FriProtocolVersionId; -use zksync_types::L1BatchNumber; pub struct LeafAggregationArtifacts { circuit_id: u8, diff --git a/prover/witness_generator/src/main.rs b/prover/witness_generator/src/main.rs index a8d0bda48f2e..c977f216104b 100644 --- a/prover/witness_generator/src/main.rs +++ b/prover/witness_generator/src/main.rs @@ -1,37 +1,37 @@ #![feature(generic_const_exprs)] +use std::time::Instant; + use anyhow::{anyhow, Context as _}; use prometheus_exporter::PrometheusExporterConfig; -use std::time::Instant; use structopt::StructOpt; use tokio::sync::watch; -use zksync_config::configs::{FriWitnessGeneratorConfig, PostgresConfig, PrometheusConfig}; -use zksync_config::ObjectStoreConfig; +use zksync_config::{ + configs::{FriWitnessGeneratorConfig, PostgresConfig, PrometheusConfig}, + ObjectStoreConfig, +}; use zksync_dal::ConnectionPool; use zksync_env_config::{object_store::ProverObjectStoreConfig, FromEnv}; use zksync_object_store::ObjectStoreFactory; use zksync_prover_utils::get_stop_signal_receiver; use zksync_queued_job_processor::JobProcessor; -use zksync_types::proofs::AggregationRound; -use zksync_types::web3::futures::StreamExt; +use zksync_types::{proofs::AggregationRound, web3::futures::StreamExt}; use zksync_utils::wait_for_tasks::wait_for_tasks; use zksync_vk_setup_data_server_fri::commitment_utils::get_cached_commitments; -use crate::basic_circuits::BasicWitnessGenerator; -use crate::leaf_aggregation::LeafAggregationWitnessGenerator; -use crate::metrics::SERVER_METRICS; -use crate::node_aggregation::NodeAggregationWitnessGenerator; -use crate::scheduler::SchedulerWitnessGenerator; +use crate::{ + basic_circuits::BasicWitnessGenerator, leaf_aggregation::LeafAggregationWitnessGenerator, + metrics::SERVER_METRICS, node_aggregation::NodeAggregationWitnessGenerator, + scheduler::SchedulerWitnessGenerator, +}; mod basic_circuits; mod leaf_aggregation; +mod metrics; mod node_aggregation; mod precalculated_merkle_paths_provider; mod scheduler; mod storage_oracle; - -mod metrics; - mod utils; #[derive(Debug, StructOpt)] diff --git a/prover/witness_generator/src/metrics.rs b/prover/witness_generator/src/metrics.rs index 3bddefc00c4b..f0497dd23a13 100644 --- a/prover/witness_generator/src/metrics.rs +++ b/prover/witness_generator/src/metrics.rs @@ -1,4 +1,5 @@ use std::time::Duration; + use vise::{Buckets, Counter, Family, Gauge, Histogram, LabeledFamily, Metrics}; use zksync_prover_fri_utils::metrics::StageLabel; diff --git a/prover/witness_generator/src/node_aggregation.rs b/prover/witness_generator/src/node_aggregation.rs index 1ae5a2551978..be9e5d0d6225 100644 --- a/prover/witness_generator/src/node_aggregation.rs +++ b/prover/witness_generator/src/node_aggregation.rs @@ -2,33 +2,41 @@ use std::time::Instant; use anyhow::Context as _; use async_trait::async_trait; -use zksync_prover_fri_types::circuit_definitions::boojum::field::goldilocks::GoldilocksField; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::recursion_layer::{ - ZkSyncRecursionLayerProof, ZkSyncRecursionLayerStorageType, - ZkSyncRecursionLayerVerificationKey, ZkSyncRecursiveLayerCircuit, -}; -use zksync_prover_fri_types::circuit_definitions::encodings::recursion_request::RecursionQueueSimulator; - use zkevm_test_harness::witness::recursive_aggregation::{ compute_node_vk_commitment, create_node_witnesses, }; -use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::recursion::leaf_layer::input::RecursionLeafParametersWitness; -use zksync_vk_setup_data_server_fri::get_recursive_layer_vk_for_circuit_type; -use zksync_vk_setup_data_server_fri::utils::get_leaf_vk_params; - -use crate::metrics::WITNESS_GENERATOR_METRICS; -use crate::utils::{ - load_proofs_for_job_ids, save_node_aggregations_artifacts, - save_recursive_layer_prover_input_artifacts, AggregationWrapper, -}; use zksync_config::configs::FriWitnessGeneratorConfig; use zksync_dal::ConnectionPool; use zksync_object_store::{AggregationsKey, ObjectStore, ObjectStoreFactory}; -use zksync_prover_fri_types::{get_current_pod_name, FriProofWrapper}; +use zksync_prover_fri_types::{ + circuit_definitions::{ + boojum::field::goldilocks::GoldilocksField, + circuit_definitions::recursion_layer::{ + ZkSyncRecursionLayerProof, ZkSyncRecursionLayerStorageType, + ZkSyncRecursionLayerVerificationKey, ZkSyncRecursiveLayerCircuit, + }, + encodings::recursion_request::RecursionQueueSimulator, + zkevm_circuits::recursion::leaf_layer::input::RecursionLeafParametersWitness, + }, + get_current_pod_name, FriProofWrapper, +}; use zksync_queued_job_processor::JobProcessor; -use zksync_types::proofs::NodeAggregationJobMetadata; -use zksync_types::protocol_version::FriProtocolVersionId; -use zksync_types::{proofs::AggregationRound, L1BatchNumber}; +use zksync_types::{ + proofs::{AggregationRound, NodeAggregationJobMetadata}, + protocol_version::FriProtocolVersionId, + L1BatchNumber, +}; +use zksync_vk_setup_data_server_fri::{ + get_recursive_layer_vk_for_circuit_type, utils::get_leaf_vk_params, +}; + +use crate::{ + metrics::WITNESS_GENERATOR_METRICS, + utils::{ + load_proofs_for_job_ids, save_node_aggregations_artifacts, + save_recursive_layer_prover_input_artifacts, AggregationWrapper, + }, +}; pub struct NodeAggregationArtifacts { circuit_id: u8, diff --git a/prover/witness_generator/src/precalculated_merkle_paths_provider.rs b/prover/witness_generator/src/precalculated_merkle_paths_provider.rs index 01bfd78fb28d..89f5ca408aab 100644 --- a/prover/witness_generator/src/precalculated_merkle_paths_provider.rs +++ b/prover/witness_generator/src/precalculated_merkle_paths_provider.rs @@ -1,12 +1,9 @@ use serde::{Deserialize, Serialize}; - -use zkevm_test_harness::witness::tree::{BinaryHasher, EnumeratedBinaryLeaf, LeafQuery}; - -use zksync_types::proofs::{PrepareBasicCircuitsJob, StorageLogMetadata}; - use zk_evm::blake2::Blake2s256; -use zkevm_test_harness::witness::tree::BinarySparseStorageTree; -use zkevm_test_harness::witness::tree::ZkSyncStorageLeaf; +use zkevm_test_harness::witness::tree::{ + BinaryHasher, BinarySparseStorageTree, EnumeratedBinaryLeaf, LeafQuery, ZkSyncStorageLeaf, +}; +use zksync_types::proofs::{PrepareBasicCircuitsJob, StorageLogMetadata}; #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] pub struct PrecalculatedMerklePathsProvider { diff --git a/prover/witness_generator/src/scheduler.rs b/prover/witness_generator/src/scheduler.rs index 5036aa188ecf..921ba68f4024 100644 --- a/prover/witness_generator/src/scheduler.rs +++ b/prover/witness_generator/src/scheduler.rs @@ -1,31 +1,37 @@ -use std::convert::TryInto; -use std::time::Instant; +use std::{convert::TryInto, time::Instant}; use anyhow::Context as _; use async_trait::async_trait; -use zksync_prover_fri_types::circuit_definitions::boojum::field::goldilocks::{GoldilocksExt2, GoldilocksField}; -use zksync_prover_fri_types::circuit_definitions::boojum::gadgets::recursion::recursive_tree_hasher::CircuitGoldilocksPoseidon2Sponge; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::recursion_layer::scheduler::SchedulerCircuit; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::recursion_layer::{ - ZkSyncRecursionLayerStorageType, ZkSyncRecursionLayerVerificationKey, - ZkSyncRecursiveLayerCircuit, SCHEDULER_CAPACITY, -}; -use zksync_prover_fri_types::circuit_definitions::recursion_layer_proof_config; -use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::scheduler::input::SchedulerCircuitInstanceWitness; -use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::scheduler::SchedulerConfig; -use zksync_vk_setup_data_server_fri::get_recursive_layer_vk_for_circuit_type; -use zksync_vk_setup_data_server_fri::utils::get_leaf_vk_params; - -use crate::metrics::WITNESS_GENERATOR_METRICS; -use crate::utils::{load_proofs_for_job_ids, SchedulerPartialInputWrapper}; use zksync_config::configs::FriWitnessGeneratorConfig; use zksync_dal::ConnectionPool; use zksync_object_store::{FriCircuitKey, ObjectStore, ObjectStoreFactory}; -use zksync_prover_fri_types::{get_current_pod_name, CircuitWrapper, FriProofWrapper}; +use zksync_prover_fri_types::{ + circuit_definitions::{ + boojum::{ + field::goldilocks::{GoldilocksExt2, GoldilocksField}, + gadgets::recursion::recursive_tree_hasher::CircuitGoldilocksPoseidon2Sponge, + }, + circuit_definitions::recursion_layer::{ + scheduler::SchedulerCircuit, ZkSyncRecursionLayerStorageType, + ZkSyncRecursionLayerVerificationKey, ZkSyncRecursiveLayerCircuit, SCHEDULER_CAPACITY, + }, + recursion_layer_proof_config, + zkevm_circuits::scheduler::{input::SchedulerCircuitInstanceWitness, SchedulerConfig}, + }, + get_current_pod_name, CircuitWrapper, FriProofWrapper, +}; use zksync_queued_job_processor::JobProcessor; -use zksync_types::proofs::AggregationRound; -use zksync_types::protocol_version::FriProtocolVersionId; -use zksync_types::L1BatchNumber; +use zksync_types::{ + proofs::AggregationRound, protocol_version::FriProtocolVersionId, L1BatchNumber, +}; +use zksync_vk_setup_data_server_fri::{ + get_recursive_layer_vk_for_circuit_type, utils::get_leaf_vk_params, +}; + +use crate::{ + metrics::WITNESS_GENERATOR_METRICS, + utils::{load_proofs_for_job_ids, SchedulerPartialInputWrapper}, +}; pub struct SchedulerArtifacts { pub scheduler_circuit: ZkSyncRecursiveLayerCircuit, diff --git a/prover/witness_generator/src/storage_oracle.rs b/prover/witness_generator/src/storage_oracle.rs index a23a08aa6ee3..6771a0252132 100644 --- a/prover/witness_generator/src/storage_oracle.rs +++ b/prover/witness_generator/src/storage_oracle.rs @@ -1,7 +1,7 @@ -use zksync_types::zkevm_test_harness::zk_evm::abstractions::{ - RefundType, RefundedAmounts, Storage, +use zksync_types::{ + zkevm_test_harness::zk_evm::abstractions::{RefundType, RefundedAmounts, Storage}, + LogQuery, Timestamp, }; -use zksync_types::{LogQuery, Timestamp}; #[derive(Debug)] pub struct StorageOracle { diff --git a/prover/witness_generator/src/tests.rs b/prover/witness_generator/src/tests.rs index 27f5637c0b3a..7fd95a7c7d89 100644 --- a/prover/witness_generator/src/tests.rs +++ b/prover/witness_generator/src/tests.rs @@ -1,11 +1,13 @@ -use const_decoder::Decoder::Hex; - use std::iter; -use super::precalculated_merkle_paths_provider::PrecalculatedMerklePathsProvider; +use const_decoder::Decoder::Hex; use zkevm_test_harness::witness::tree::{BinarySparseStorageTree, ZkSyncStorageLeaf}; -use zksync_types::proofs::{PrepareBasicCircuitsJob, StorageLogMetadata}; -use zksync_types::U256; +use zksync_types::{ + proofs::{PrepareBasicCircuitsJob, StorageLogMetadata}, + U256, +}; + +use super::precalculated_merkle_paths_provider::PrecalculatedMerklePathsProvider; // Sample `StorageLogMetadata` entries. Since we cannot allocate in constants, we store // the only Merkle path hash separately. diff --git a/prover/witness_generator/src/utils.rs b/prover/witness_generator/src/utils.rs index 0a70858977f7..6efa333a8190 100644 --- a/prover/witness_generator/src/utils.rs +++ b/prover/witness_generator/src/utils.rs @@ -1,26 +1,28 @@ -use zksync_prover_fri_types::circuit_definitions::boojum::field::goldilocks::GoldilocksExt2; -use zksync_prover_fri_types::circuit_definitions::boojum::gadgets::recursion::recursive_tree_hasher::CircuitGoldilocksPoseidon2Sponge; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::base_layer::{ - ZkSyncBaseLayerClosedFormInput, +use zkevm_test_harness::{ + boojum::field::goldilocks::GoldilocksField, witness::full_block_artifact::BlockBasicCircuits, }; -use zksync_prover_fri_types::circuit_definitions::circuit_definitions::recursion_layer::{ - ZkSyncRecursiveLayerCircuit, -}; - -use zksync_prover_fri_types::circuit_definitions::encodings::recursion_request::RecursionQueueSimulator; - -use zkevm_test_harness::boojum::field::goldilocks::GoldilocksField; -use zkevm_test_harness::witness::full_block_artifact::BlockBasicCircuits; use zksync_object_store::{ serialize_using_bincode, AggregationsKey, Bucket, ClosedFormInputKey, FriCircuitKey, ObjectStore, StoredObject, }; -use zksync_prover_fri_types::circuit_definitions::zkevm_circuits::scheduler::input::SchedulerCircuitInstanceWitness; -use zksync_prover_fri_types::circuit_definitions::ZkSyncDefaultRoundFunction; -use zksync_prover_fri_types::{CircuitWrapper, FriProofWrapper}; +use zksync_prover_fri_types::{ + circuit_definitions::{ + boojum::{ + field::goldilocks::GoldilocksExt2, + gadgets::recursion::recursive_tree_hasher::CircuitGoldilocksPoseidon2Sponge, + }, + circuit_definitions::{ + base_layer::ZkSyncBaseLayerClosedFormInput, + recursion_layer::ZkSyncRecursiveLayerCircuit, + }, + encodings::recursion_request::RecursionQueueSimulator, + zkevm_circuits::scheduler::input::SchedulerCircuitInstanceWitness, + ZkSyncDefaultRoundFunction, + }, + CircuitWrapper, FriProofWrapper, +}; use zksync_system_constants::USED_BOOTLOADER_MEMORY_BYTES; -use zksync_types::proofs::AggregationRound; -use zksync_types::{L1BatchNumber, U256}; +use zksync_types::{proofs::AggregationRound, L1BatchNumber, U256}; pub fn expand_bootloader_contents(packed: &[(usize, U256)]) -> Vec { let mut result = vec![0u8; USED_BOOTLOADER_MEMORY_BYTES]; diff --git a/prover/witness_generator/tests/basic_test.rs b/prover/witness_generator/tests/basic_test.rs index 1c8d00ff35e7..16cce19929da 100644 --- a/prover/witness_generator/tests/basic_test.rs +++ b/prover/witness_generator/tests/basic_test.rs @@ -4,20 +4,20 @@ use serde::Serialize; use zksync_config::ObjectStoreConfig; use zksync_env_config::FromEnv; use zksync_object_store::{AggregationsKey, FriCircuitKey, ObjectStoreFactory}; -use zksync_types::proofs::{ - AggregationRound, LeafAggregationJobMetadata, NodeAggregationJobMetadata, -}; -use zksync_types::L1BatchNumber; - use zksync_prover_fri_types::CircuitWrapper; use zksync_prover_fri_utils::get_recursive_layer_circuit_id_for_base_layer; -use zksync_witness_generator::leaf_aggregation::{ - prepare_leaf_aggregation_job, LeafAggregationWitnessGenerator, +use zksync_types::{ + proofs::{AggregationRound, LeafAggregationJobMetadata, NodeAggregationJobMetadata}, + L1BatchNumber, +}; +use zksync_witness_generator::{ + leaf_aggregation::{prepare_leaf_aggregation_job, LeafAggregationWitnessGenerator}, + node_aggregation, + node_aggregation::NodeAggregationWitnessGenerator, + scheduler, + scheduler::SchedulerWitnessGenerator, + utils::AggregationWrapper, }; -use zksync_witness_generator::node_aggregation::NodeAggregationWitnessGenerator; -use zksync_witness_generator::scheduler::SchedulerWitnessGenerator; -use zksync_witness_generator::utils::AggregationWrapper; -use zksync_witness_generator::{node_aggregation, scheduler}; fn compare_serialized(expected: &T, actual: &T) { let serialized_expected = bincode::serialize(expected).unwrap(); diff --git a/prover/witness_vector_generator/src/generator.rs b/prover/witness_vector_generator/src/generator.rs index ee17e0edaa7f..74e25b389886 100644 --- a/prover/witness_vector_generator/src/generator.rs +++ b/prover/witness_vector_generator/src/generator.rs @@ -2,24 +2,27 @@ use std::time::{Duration, Instant}; use anyhow::Context as _; use async_trait::async_trait; -use tokio::task::JoinHandle; - -use crate::metrics::METRICS; -use tokio::time::sleep; +use tokio::{task::JoinHandle, time::sleep}; use zksync_config::configs::FriWitnessVectorGeneratorConfig; use zksync_dal::ConnectionPool; use zksync_object_store::ObjectStore; -use zksync_prover_fri_types::circuit_definitions::boojum::field::goldilocks::GoldilocksField; -use zksync_prover_fri_types::{CircuitWrapper, ProverJob, WitnessVectorArtifacts}; -use zksync_prover_fri_utils::fetch_next_circuit; -use zksync_prover_fri_utils::get_numeric_circuit_id; -use zksync_prover_fri_utils::socket_utils::send_assembly; +use zksync_prover_fri_types::{ + circuit_definitions::boojum::field::goldilocks::GoldilocksField, CircuitWrapper, ProverJob, + WitnessVectorArtifacts, +}; +use zksync_prover_fri_utils::{ + fetch_next_circuit, get_numeric_circuit_id, socket_utils::send_assembly, +}; use zksync_queued_job_processor::JobProcessor; -use zksync_types::basic_fri_types::CircuitIdRoundTuple; -use zksync_types::proofs::{GpuProverInstanceStatus, SocketAddress}; -use zksync_types::protocol_version::L1VerifierConfig; +use zksync_types::{ + basic_fri_types::CircuitIdRoundTuple, + proofs::{GpuProverInstanceStatus, SocketAddress}, + protocol_version::L1VerifierConfig, +}; use zksync_vk_setup_data_server_fri::get_finalization_hints; +use crate::metrics::METRICS; + pub struct WitnessVectorGenerator { blob_store: Box, pool: ConnectionPool, diff --git a/prover/witness_vector_generator/src/main.rs b/prover/witness_vector_generator/src/main.rs index 7c1aa8e0b89a..1358680d3a11 100644 --- a/prover/witness_vector_generator/src/main.rs +++ b/prover/witness_vector_generator/src/main.rs @@ -3,12 +3,10 @@ use anyhow::Context as _; use prometheus_exporter::PrometheusExporterConfig; use structopt::StructOpt; -use tokio::{sync::oneshot, sync::watch}; - -use crate::generator::WitnessVectorGenerator; -use zksync_config::configs::fri_prover_group::FriProverGroupConfig; +use tokio::sync::{oneshot, watch}; use zksync_config::configs::{ - FriProverConfig, FriWitnessVectorGeneratorConfig, PostgresConfig, ProverGroupConfig, + fri_prover_group::FriProverGroupConfig, FriProverConfig, FriWitnessVectorGeneratorConfig, + PostgresConfig, ProverGroupConfig, }; use zksync_dal::ConnectionPool; use zksync_env_config::{object_store::ProverObjectStoreConfig, FromEnv}; @@ -19,6 +17,8 @@ use zksync_queued_job_processor::JobProcessor; use zksync_utils::wait_for_tasks::wait_for_tasks; use zksync_vk_setup_data_server_fri::commitment_utils::get_cached_commitments; +use crate::generator::WitnessVectorGenerator; + mod generator; mod metrics; diff --git a/prover/witness_vector_generator/src/metrics.rs b/prover/witness_vector_generator/src/metrics.rs index 4bc11ff401b0..7c8284244b67 100644 --- a/prover/witness_vector_generator/src/metrics.rs +++ b/prover/witness_vector_generator/src/metrics.rs @@ -1,4 +1,5 @@ use std::time::Duration; + use vise::{Buckets, Histogram, LabeledFamily, Metrics}; #[derive(Debug, Metrics)] diff --git a/prover/witness_vector_generator/tests/basic_test.rs b/prover/witness_vector_generator/tests/basic_test.rs index 5ed0769d4162..648b1ee4d9e6 100644 --- a/prover/witness_vector_generator/tests/basic_test.rs +++ b/prover/witness_vector_generator/tests/basic_test.rs @@ -1,7 +1,7 @@ use std::fs; + use zksync_prover_fri_types::{CircuitWrapper, ProverJob, ProverServiceDataKey}; -use zksync_types::proofs::AggregationRound; -use zksync_types::L1BatchNumber; +use zksync_types::{proofs::AggregationRound, L1BatchNumber}; use zksync_witness_vector_generator::generator::WitnessVectorGenerator; #[test] diff --git a/sdk/zksync-rs/src/ethereum/mod.rs b/sdk/zksync-rs/src/ethereum/mod.rs index 3371c7201499..ac91e3589831 100644 --- a/sdk/zksync-rs/src/ethereum/mod.rs +++ b/sdk/zksync-rs/src/ethereum/mod.rs @@ -1,32 +1,34 @@ //! Utilities for the on-chain operations, such as `Deposit` and `FullExit`. -use core::{convert::TryFrom, time::Duration}; +use std::{ + convert::TryFrom, + time::{Duration, Instant}, +}; + use serde_json::{Map, Value}; -use std::time::Instant; +use zksync_eth_client::{ + clients::http::SigningClient, types::Error, BoundEthInterface, EthInterface, +}; +use zksync_eth_signer::EthereumSigner; use zksync_types::{ api::BridgeAddresses, + l1::L1Tx, + network::Network, web3::{ contract::{tokens::Tokenize, Options}, ethabi, transports::Http, types::{TransactionReceipt, H160, H256, U256}, }, - L1ChainId, REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_BYTE, + Address, L1ChainId, L1TxCommonData, REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_BYTE, }; use zksync_web3_decl::namespaces::{EthNamespaceClient, ZksNamespaceClient}; -use zksync_eth_client::{ - clients::http::SigningClient, types::Error, BoundEthInterface, EthInterface, -}; -use zksync_eth_signer::EthereumSigner; -use zksync_types::network::Network; -use zksync_types::{l1::L1Tx, Address, L1TxCommonData}; - -use crate::web3::ethabi::Bytes; use crate::{ error::ClientError, operations::SyncTransactionHandle, utils::{is_token_eth, load_contract}, + web3::ethabi::Bytes, }; const IERC20_INTERFACE: &str = include_str!("../abi/IERC20.json"); diff --git a/sdk/zksync-rs/src/lib.rs b/sdk/zksync-rs/src/lib.rs index 4ee03e8ff9da..aa4a158ef069 100644 --- a/sdk/zksync-rs/src/lib.rs +++ b/sdk/zksync-rs/src/lib.rs @@ -1,18 +1,15 @@ +pub use zksync_types::{self, network::Network, web3}; +pub use zksync_web3_decl::{ + jsonrpsee::http_client::*, + namespaces::{EthNamespaceClient, NetNamespaceClient, Web3NamespaceClient, ZksNamespaceClient}, + types, +}; + +pub use crate::{ethereum::EthereumProvider, wallet::Wallet}; + pub mod error; pub mod ethereum; pub mod operations; pub mod signer; pub mod utils; pub mod wallet; - -pub use crate::{ethereum::EthereumProvider, wallet::Wallet}; -pub use zksync_types::network::Network; - -pub use zksync_types; -pub use zksync_types::web3; - -pub use zksync_web3_decl::{ - jsonrpsee::http_client::*, - namespaces::{EthNamespaceClient, NetNamespaceClient, Web3NamespaceClient, ZksNamespaceClient}, - types, -}; diff --git a/sdk/zksync-rs/src/operations/mod.rs b/sdk/zksync-rs/src/operations/mod.rs index a59bd57a2dd9..36a0d2b29b89 100644 --- a/sdk/zksync-rs/src/operations/mod.rs +++ b/sdk/zksync-rs/src/operations/mod.rs @@ -2,10 +2,9 @@ use std::time::{Duration, Instant}; -use crate::{error::ClientError, EthNamespaceClient}; -use zksync_types::l2::L2Tx; use zksync_types::{ api::{BlockNumber, TransactionReceipt}, + l2::L2Tx, Bytes, L2ChainId, H256, }; @@ -15,6 +14,7 @@ pub use self::{ transfer::{create_transfer_calldata, TransferBuilder}, withdraw::WithdrawBuilder, }; +use crate::{error::ClientError, EthNamespaceClient}; mod deploy_contract; mod execute_contract; diff --git a/sdk/zksync-rs/src/operations/transfer.rs b/sdk/zksync-rs/src/operations/transfer.rs index 85d90c61fd23..f0f7525e8f6d 100644 --- a/sdk/zksync-rs/src/operations/transfer.rs +++ b/sdk/zksync-rs/src/operations/transfer.rs @@ -1,13 +1,14 @@ use zksync_eth_signer::EthereumSigner; -use zksync_types::L2_ETH_TOKEN_ADDRESS; -use zksync_types::{fee::Fee, l2::L2Tx, Address, Nonce, U256}; +use zksync_types::{fee::Fee, l2::L2Tx, Address, Nonce, L2_ETH_TOKEN_ADDRESS, U256}; -use crate::ethereum::ierc20_contract; -use crate::web3::contract::tokens::Tokenize; -use crate::zksync_types::{transaction_request::PaymasterParams, Execute, L2TxCommonData}; use crate::{ - error::ClientError, operations::SyncTransactionHandle, wallet::Wallet, EthNamespaceClient, - ZksNamespaceClient, + error::ClientError, + ethereum::ierc20_contract, + operations::SyncTransactionHandle, + wallet::Wallet, + web3::contract::tokens::Tokenize, + zksync_types::{transaction_request::PaymasterParams, Execute, L2TxCommonData}, + EthNamespaceClient, ZksNamespaceClient, }; pub struct TransferBuilder<'a, S: EthereumSigner, P> { diff --git a/sdk/zksync-rs/src/operations/withdraw.rs b/sdk/zksync-rs/src/operations/withdraw.rs index 0037deacd630..a580a0c35e00 100644 --- a/sdk/zksync-rs/src/operations/withdraw.rs +++ b/sdk/zksync-rs/src/operations/withdraw.rs @@ -1,9 +1,7 @@ use zksync_eth_signer::EthereumSigner; - -use zksync_types::l2::L2Tx; use zksync_types::{ - fee::Fee, tokens::ETHEREUM_ADDRESS, transaction_request::PaymasterParams, web3::ethabi, - Address, Nonce, L2_ETH_TOKEN_ADDRESS, U256, + fee::Fee, l2::L2Tx, tokens::ETHEREUM_ADDRESS, transaction_request::PaymasterParams, + web3::ethabi, Address, Nonce, L2_ETH_TOKEN_ADDRESS, U256, }; use crate::{ diff --git a/sdk/zksync-rs/src/signer.rs b/sdk/zksync-rs/src/signer.rs index 0c92a354ce5b..445c5172ff6e 100644 --- a/sdk/zksync-rs/src/signer.rs +++ b/sdk/zksync-rs/src/signer.rs @@ -1,15 +1,12 @@ -// Built-in imports use std::fmt::Debug; -// Workspace uses + use zksync_eth_signer::{error::SignerError, EthereumSigner}; -use zksync_types::L2_ETH_TOKEN_ADDRESS; use zksync_types::{ fee::Fee, l2::L2Tx, transaction_request::PaymasterParams, Address, Eip712Domain, L2ChainId, - Nonce, PackedEthSignature, U256, + Nonce, PackedEthSignature, L2_ETH_TOKEN_ADDRESS, U256, }; -// Local imports -use crate::operations::create_transfer_calldata; -use crate::types::TransactionRequest; + +use crate::{operations::create_transfer_calldata, types::TransactionRequest}; fn signing_failed_error(err: impl ToString) -> SignerError { SignerError::SigningFailed(err.to_string()) diff --git a/sdk/zksync-rs/src/utils.rs b/sdk/zksync-rs/src/utils.rs index c97fe42d47e1..5137dbca6db1 100644 --- a/sdk/zksync-rs/src/utils.rs +++ b/sdk/zksync-rs/src/utils.rs @@ -1,7 +1,6 @@ use std::str::FromStr; use num::BigUint; - use zksync_types::{transaction_request::PaymasterParams, Address, U256}; use crate::web3::ethabi::{Contract, Token}; diff --git a/sdk/zksync-rs/src/wallet.rs b/sdk/zksync-rs/src/wallet.rs index 7d665b4f42e7..dd2cdf14208e 100644 --- a/sdk/zksync-rs/src/wallet.rs +++ b/sdk/zksync-rs/src/wallet.rs @@ -1,23 +1,22 @@ use zksync_eth_signer::EthereumSigner; -use zksync_types::transaction_request::CallRequest; use zksync_types::{ api::{BlockIdVariant, BlockNumber, TransactionRequest}, l2::L2Tx, tokens::ETHEREUM_ADDRESS, + transaction_request::CallRequest, Address, Bytes, Eip712Domain, U256, }; - use zksync_web3_decl::{ jsonrpsee::http_client::{HttpClient, HttpClientBuilder}, namespaces::{EthNamespaceClient, NetNamespaceClient, Web3NamespaceClient, ZksNamespaceClient}, }; -use crate::web3::contract::tokens::Tokenizable; use crate::{ error::ClientError, ethereum::{ierc20_contract, EthereumProvider}, operations::*, signer::Signer, + web3::contract::tokens::Tokenizable, }; #[derive(Debug)] From dd9b308be9b0a6e37aad75f6f54b98e30a2ae14e Mon Sep 17 00:00:00 2001 From: perekopskiy <53865202+perekopskiy@users.noreply.github.com> Date: Thu, 7 Dec 2023 12:07:00 +0200 Subject: [PATCH 063/268] fix(job-processor): `max_attepts_reached` metric (#626) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ `max_attepts_reached` metric is now reported on job start rather failure. With this change metric will be reported not only if last attempt failed but also if it got stuck/stopped/etc. ## Why ❔ Reporting `max_attepts_reached` metric for all cases. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --- core/lib/queued_job_processor/src/lib.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/core/lib/queued_job_processor/src/lib.rs b/core/lib/queued_job_processor/src/lib.rs index 2966fba49cac..49ec8b348ee2 100644 --- a/core/lib/queued_job_processor/src/lib.rs +++ b/core/lib/queued_job_processor/src/lib.rs @@ -109,6 +109,16 @@ pub trait JobProcessor: Sync + Send { task: JoinHandle>, ) -> anyhow::Result<()> { let attempts = self.get_job_attempts(&job_id).await?; + let max_attempts = self.max_attempts(); + if attempts == max_attempts { + METRICS.max_attempts_reached[&(Self::SERVICE_NAME, format!("{job_id:?}"))].inc(); + tracing::error!( + "Max attempts ({max_attempts}) reached for {} job {:?}", + Self::SERVICE_NAME, + job_id, + ); + } + let result = loop { tracing::trace!( "Polling {} task with id {:?}. Is finished: {}", @@ -144,15 +154,6 @@ pub trait JobProcessor: Sync + Send { error_message ); - let max_attempts = self.max_attempts(); - if attempts == max_attempts { - METRICS.max_attempts_reached[&(Self::SERVICE_NAME, format!("{job_id:?}"))].inc(); - tracing::error!( - "Max attempts ({max_attempts}) reached for {} job {:?}", - Self::SERVICE_NAME, - job_id, - ); - } self.save_failure(job_id, started_at, error_message).await; Ok(()) } From d84dd6fde02b2fc3dad97f5ecb522d308b8e20ef Mon Sep 17 00:00:00 2001 From: Danil Date: Thu, 7 Dec 2023 16:20:36 +0100 Subject: [PATCH 064/268] chore(vm): Expose more pubs and make inmemory clonable (#632) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Expose AppFramestack public and make InMemoryStorage Clonable. ## Why ❔ For supporting era-test-node ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zk fmt` and `zk lint`. - [ ] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --------- Signed-off-by: Danil --- core/lib/multivm/src/versions/vm_latest/mod.rs | 4 +++- .../src/versions/vm_latest/old_vm/history_recorder.rs | 2 +- .../lib/multivm/src/versions/vm_refunds_enhancement/mod.rs | 4 +++- .../vm_refunds_enhancement/old_vm/history_recorder.rs | 2 +- .../versions/vm_virtual_blocks/old_vm/history_recorder.rs | 2 +- core/lib/state/src/in_memory.rs | 7 ++++++- 6 files changed, 15 insertions(+), 6 deletions(-) diff --git a/core/lib/multivm/src/versions/vm_latest/mod.rs b/core/lib/multivm/src/versions/vm_latest/mod.rs index 0b4919f83d7b..c3df28f6c31c 100644 --- a/core/lib/multivm/src/versions/vm_latest/mod.rs +++ b/core/lib/multivm/src/versions/vm_latest/mod.rs @@ -1,7 +1,9 @@ pub use self::{ bootloader_state::BootloaderState, old_vm::{ - history_recorder::{HistoryDisabled, HistoryEnabled, HistoryMode}, + history_recorder::{ + AppDataFrameManagerWithHistory, HistoryDisabled, HistoryEnabled, HistoryMode, + }, memory::SimpleMemory, }, oracles::storage::StorageOracle, diff --git a/core/lib/multivm/src/versions/vm_latest/old_vm/history_recorder.rs b/core/lib/multivm/src/versions/vm_latest/old_vm/history_recorder.rs index 2253831b7454..83a22f35b4a1 100644 --- a/core/lib/multivm/src/versions/vm_latest/old_vm/history_recorder.rs +++ b/core/lib/multivm/src/versions/vm_latest/old_vm/history_recorder.rs @@ -437,7 +437,7 @@ impl HistoryRecorder, H> { } #[derive(Debug, Clone, PartialEq)] -pub(crate) struct AppDataFrameManagerWithHistory { +pub struct AppDataFrameManagerWithHistory { forward: HistoryRecorder, H>, rollback: HistoryRecorder, H>, } diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/mod.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/mod.rs index 691d453c4b07..81267701b5cd 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/mod.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/mod.rs @@ -1,7 +1,9 @@ pub use self::{ bootloader_state::BootloaderState, old_vm::{ - history_recorder::{HistoryDisabled, HistoryEnabled, HistoryMode}, + history_recorder::{ + AppDataFrameManagerWithHistory, HistoryDisabled, HistoryEnabled, HistoryMode, + }, memory::SimpleMemory, }, oracles::storage::StorageOracle, diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/history_recorder.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/history_recorder.rs index fdab00a199e9..fc6179cb1481 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/history_recorder.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/old_vm/history_recorder.rs @@ -437,7 +437,7 @@ impl HistoryRecorder, H> { } #[derive(Debug, Clone, PartialEq)] -pub(crate) struct AppDataFrameManagerWithHistory { +pub struct AppDataFrameManagerWithHistory { forward: HistoryRecorder, H>, rollback: HistoryRecorder, H>, } diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/history_recorder.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/history_recorder.rs index ca02739032cc..6154dc8e24d6 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/history_recorder.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/old_vm/history_recorder.rs @@ -433,7 +433,7 @@ impl HistoryRecorder, H> { } #[derive(Debug, Clone, PartialEq)] -pub(crate) struct AppDataFrameManagerWithHistory { +pub struct AppDataFrameManagerWithHistory { forward: HistoryRecorder, H>, rollback: HistoryRecorder, H>, } diff --git a/core/lib/state/src/in_memory.rs b/core/lib/state/src/in_memory.rs index 6f9fb8680459..d6058649a459 100644 --- a/core/lib/state/src/in_memory.rs +++ b/core/lib/state/src/in_memory.rs @@ -13,7 +13,7 @@ use crate::ReadStorage; pub const IN_MEMORY_STORAGE_DEFAULT_NETWORK_ID: u32 = 270; /// In-memory storage. -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct InMemoryStorage { pub(crate) state: HashMap, pub(crate) factory_deps: HashMap>, @@ -101,6 +101,11 @@ impl InMemoryStorage { pub fn store_factory_dep(&mut self, hash: H256, bytecode: Vec) { self.factory_deps.insert(hash, bytecode); } + + /// Get internal state of the storage. + pub fn get_state(&self) -> &HashMap { + &self.state + } } impl ReadStorage for &InMemoryStorage { From 6fefe6242e1be691330408925d70b624b8fe68d5 Mon Sep 17 00:00:00 2001 From: AnastasiiaVashchuk <72273339+AnastasiiaVashchuk@users.noreply.github.com> Date: Thu, 7 Dec 2023 18:21:37 +0200 Subject: [PATCH 065/268] chore: remove old witness generator (#619) --- core/bin/external_node/src/main.rs | 9 +- core/bin/zksync_server/src/main.rs | 13 +- core/lib/dal/sqlx-data.json | 695 +------------ core/lib/dal/src/lib.rs | 7 +- core/lib/dal/src/tests/mod.rs | 277 +----- core/lib/dal/src/witness_generator_dal.rs | 929 ------------------ .../zksync_core/src/api_server/tree/tests.rs | 3 +- .../src/basic_witness_input_producer/mod.rs | 4 - core/lib/zksync_core/src/house_keeper/mod.rs | 2 - core/lib/zksync_core/src/lib.rs | 178 +--- .../src/metadata_calculator/mod.rs | 9 +- .../src/metadata_calculator/tests.rs | 33 +- .../src/metadata_calculator/updater.rs | 38 +- core/lib/zksync_core/src/metrics.rs | 4 +- .../src/witness_generator/basic_circuits.rs | 646 ------------ .../src/witness_generator/leaf_aggregation.rs | 341 ------- .../zksync_core/src/witness_generator/mod.rs | 82 -- .../src/witness_generator/node_aggregation.rs | 376 ------- .../precalculated_merkle_paths_provider.rs | 265 ----- .../src/witness_generator/scheduler.rs | 375 ------- .../src/witness_generator/storage_oracle.rs | 46 - .../src/witness_generator/tests.rs | 300 ------ .../src/witness_generator/utils.rs | 31 - 23 files changed, 28 insertions(+), 4635 deletions(-) delete mode 100644 core/lib/dal/src/witness_generator_dal.rs delete mode 100644 core/lib/zksync_core/src/witness_generator/basic_circuits.rs delete mode 100644 core/lib/zksync_core/src/witness_generator/leaf_aggregation.rs delete mode 100644 core/lib/zksync_core/src/witness_generator/mod.rs delete mode 100644 core/lib/zksync_core/src/witness_generator/node_aggregation.rs delete mode 100644 core/lib/zksync_core/src/witness_generator/precalculated_merkle_paths_provider.rs delete mode 100644 core/lib/zksync_core/src/witness_generator/scheduler.rs delete mode 100644 core/lib/zksync_core/src/witness_generator/storage_oracle.rs delete mode 100644 core/lib/zksync_core/src/witness_generator/tests.rs delete mode 100644 core/lib/zksync_core/src/witness_generator/utils.rs diff --git a/core/bin/external_node/src/main.rs b/core/bin/external_node/src/main.rs index da28329c18f4..cf85c223e7ff 100644 --- a/core/bin/external_node/src/main.rs +++ b/core/bin/external_node/src/main.rs @@ -193,14 +193,7 @@ async fn init_tasks( .build() .await .context("failed to build a tree_pool")?; - // todo: PLA-335 - // Note: This pool isn't actually used by the metadata calculator, but it has to be provided anyway. - let prover_tree_pool = ConnectionPool::singleton(&config.postgres.database_url) - .build() - .await - .context("failed to build a prover_tree_pool")?; - let tree_handle = - task::spawn(metadata_calculator.run(tree_pool, prover_tree_pool, tree_stop_receiver)); + let tree_handle = task::spawn(metadata_calculator.run(tree_pool, tree_stop_receiver)); let consistency_checker_handle = tokio::spawn(consistency_checker.run(stop_receiver.clone())); diff --git a/core/bin/zksync_server/src/main.rs b/core/bin/zksync_server/src/main.rs index 9a5ccf8be603..6da8bae1ac31 100644 --- a/core/bin/zksync_server/src/main.rs +++ b/core/bin/zksync_server/src/main.rs @@ -41,7 +41,7 @@ struct Cli { /// Comma-separated list of components to launch. #[arg( long, - default_value = "api,tree,eth,data_fetcher,state_keeper,witness_generator,housekeeper,basic_witness_input_producer" + default_value = "api,tree,eth,data_fetcher,state_keeper,housekeeper,basic_witness_input_producer" )] components: ComponentsToRun, } @@ -151,16 +151,9 @@ async fn main() -> anyhow::Result<()> { opt.components.0 }; - // OneShotWitnessGenerator is the only component that is not expected to run indefinitely - // if this value is `false`, we expect all components to run indefinitely: we panic if any component returns. - let is_only_oneshot_witness_generator_task = matches!( - components.as_slice(), - [Component::WitnessGenerator(Some(_), _)] - ); - // Run core actors. let (core_task_handles, stop_sender, cb_receiver, health_check_handle) = - initialize_components(&configs, components, is_only_oneshot_witness_generator_task) + initialize_components(&configs, components) .await .context("Unable to start Core actors")?; @@ -169,7 +162,7 @@ async fn main() -> anyhow::Result<()> { let particular_crypto_alerts = None::>; let graceful_shutdown = None::>; - let tasks_allowed_to_finish = is_only_oneshot_witness_generator_task; + let tasks_allowed_to_finish = false; tokio::select! { _ = wait_for_tasks(core_task_handles, particular_crypto_alerts, graceful_shutdown, tasks_allowed_to_finish) => {}, _ = sigint_receiver => { diff --git a/core/lib/dal/sqlx-data.json b/core/lib/dal/sqlx-data.json index 2d1773482ea1..d35fd3205080 100644 --- a/core/lib/dal/sqlx-data.json +++ b/core/lib/dal/sqlx-data.json @@ -364,19 +364,6 @@ }, "query": "UPDATE l1_batches SET hash = $1, merkle_root_hash = $2, compressed_repeated_writes = $3, compressed_initial_writes = $4, l2_l1_compressed_messages = $5, l2_l1_merkle_root = $6, zkporter_is_available = $7, parent_hash = $8, rollup_last_leaf_index = $9, pass_through_data_hash = $10, meta_parameters_hash = $11, compressed_state_diffs = $12, updated_at = now() WHERE number = $13 AND hash IS NULL" }, - "0c212f47b9a0e719f947a419be8284837b1b01aa23994ba6401b420790b802b8": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Int8", - "Int4" - ] - } - }, - "query": "\n INSERT INTO node_aggregation_witness_jobs\n (l1_batch_number, protocol_version, status, created_at, updated_at)\n VALUES ($1, $2, 'waiting_for_artifacts', now(), now())\n " - }, "0cbbcd30fde109c4c44162f94b6ed9bab4e9db9948d03e584c2cab543449d298": { "describe": { "columns": [ @@ -598,113 +585,6 @@ }, "query": "\n SELECT COUNT(*) as \"count!\", status as \"status!\"\n FROM prover_jobs\n GROUP BY status\n " }, - "100ede607d40d8d07000fcdc40705c806e8229323e0e6dfb7507691838963ccf": { - "describe": { - "columns": [ - { - "name": "l1_batch_number", - "ordinal": 0, - "type_info": "Int8" - }, - { - "name": "basic_circuits", - "ordinal": 1, - "type_info": "Bytea" - }, - { - "name": "basic_circuits_inputs", - "ordinal": 2, - "type_info": "Bytea" - }, - { - "name": "number_of_basic_circuits", - "ordinal": 3, - "type_info": "Int4" - }, - { - "name": "status", - "ordinal": 4, - "type_info": "Text" - }, - { - "name": "processing_started_at", - "ordinal": 5, - "type_info": "Timestamp" - }, - { - "name": "time_taken", - "ordinal": 6, - "type_info": "Time" - }, - { - "name": "error", - "ordinal": 7, - "type_info": "Text" - }, - { - "name": "created_at", - "ordinal": 8, - "type_info": "Timestamp" - }, - { - "name": "updated_at", - "ordinal": 9, - "type_info": "Timestamp" - }, - { - "name": "attempts", - "ordinal": 10, - "type_info": "Int4" - }, - { - "name": "basic_circuits_blob_url", - "ordinal": 11, - "type_info": "Text" - }, - { - "name": "basic_circuits_inputs_blob_url", - "ordinal": 12, - "type_info": "Text" - }, - { - "name": "is_blob_cleaned", - "ordinal": 13, - "type_info": "Bool" - }, - { - "name": "protocol_version", - "ordinal": 14, - "type_info": "Int4" - } - ], - "nullable": [ - false, - false, - false, - false, - false, - true, - true, - true, - false, - false, - false, - true, - true, - false, - true - ], - "parameters": { - "Left": [ - "Interval", - "Int4", - "Int8", - "Int4Array" - ] - } - }, - "query": "\n UPDATE leaf_aggregation_witness_jobs\n SET status = 'in_progress', attempts = attempts + 1,\n updated_at = now(), processing_started_at = now()\n WHERE l1_batch_number = (\n SELECT l1_batch_number\n FROM leaf_aggregation_witness_jobs\n WHERE l1_batch_number <= $3\n AND\n ( status = 'queued'\n OR (status = 'in_progress' AND processing_started_at < now() - $1::interval)\n OR (status = 'failed' AND attempts < $2)\n )\n AND protocol_version = ANY($4)\n ORDER BY l1_batch_number ASC\n LIMIT 1\n FOR UPDATE\n SKIP LOCKED\n )\n RETURNING leaf_aggregation_witness_jobs.*\n " - }, "13e5f6a2a73eaa979229611ffdbed86d6e5e1bad0c645d39b56fdc47f5c17971": { "describe": { "columns": [ @@ -3688,113 +3568,6 @@ }, "query": "DELETE FROM call_traces\n WHERE tx_hash = ANY($1)" }, - "3af5a385c6636afb16e0fa5eda5373d64a76cef695dfa0b3b156e236224d32c8": { - "describe": { - "columns": [ - { - "name": "l1_batch_number", - "ordinal": 0, - "type_info": "Int8" - }, - { - "name": "scheduler_witness", - "ordinal": 1, - "type_info": "Bytea" - }, - { - "name": "final_node_aggregations", - "ordinal": 2, - "type_info": "Bytea" - }, - { - "name": "status", - "ordinal": 3, - "type_info": "Text" - }, - { - "name": "processing_started_at", - "ordinal": 4, - "type_info": "Timestamp" - }, - { - "name": "time_taken", - "ordinal": 5, - "type_info": "Time" - }, - { - "name": "error", - "ordinal": 6, - "type_info": "Text" - }, - { - "name": "created_at", - "ordinal": 7, - "type_info": "Timestamp" - }, - { - "name": "updated_at", - "ordinal": 8, - "type_info": "Timestamp" - }, - { - "name": "attempts", - "ordinal": 9, - "type_info": "Int4" - }, - { - "name": "aggregation_result_coords", - "ordinal": 10, - "type_info": "Bytea" - }, - { - "name": "scheduler_witness_blob_url", - "ordinal": 11, - "type_info": "Text" - }, - { - "name": "final_node_aggregations_blob_url", - "ordinal": 12, - "type_info": "Text" - }, - { - "name": "is_blob_cleaned", - "ordinal": 13, - "type_info": "Bool" - }, - { - "name": "protocol_version", - "ordinal": 14, - "type_info": "Int4" - } - ], - "nullable": [ - false, - false, - true, - false, - true, - true, - true, - false, - false, - false, - true, - true, - true, - false, - true - ], - "parameters": { - "Left": [ - "Interval", - "Int4", - "Int8", - "Int4Array" - ] - } - }, - "query": "\n UPDATE scheduler_witness_jobs\n SET status = 'in_progress', attempts = attempts + 1,\n updated_at = now(), processing_started_at = now()\n WHERE l1_batch_number = (\n SELECT l1_batch_number\n FROM scheduler_witness_jobs\n WHERE l1_batch_number <= $3\n AND\n ( status = 'queued'\n OR (status = 'in_progress' AND processing_started_at < now() - $1::interval)\n OR (status = 'failed' AND attempts < $2)\n )\n AND protocol_version = ANY($4)\n ORDER BY l1_batch_number ASC\n LIMIT 1\n FOR UPDATE\n SKIP LOCKED\n )\n RETURNING scheduler_witness_jobs.*\n " - }, "3be0d3fd7a1ff997edb1eaff3fac59324a5b33663e7862cfddd4a5db8015f13c": { "describe": { "columns": [ @@ -3920,19 +3693,6 @@ }, "query": "INSERT INTO l1_batches (number, l1_tx_count, l2_tx_count, timestamp, is_finished, fee_account_address, l2_to_l1_logs, l2_to_l1_messages, bloom, priority_ops_onchain_data, predicted_commit_gas_cost, predicted_prove_gas_cost, predicted_execute_gas_cost, initial_bootloader_heap_content, used_contract_hashes, base_fee_per_gas, l1_gas_price, l2_fair_gas_price, bootloader_code_hash, default_aa_code_hash, protocol_version, system_logs, storage_refunds, created_at, updated_at ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, now(), now())" }, - "40a86f39a74ab22bdcd8b40446ea063c68bfb3e930e3150212474a657e82b38f": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Int8", - "Text" - ] - } - }, - "query": "\n UPDATE scheduler_witness_jobs\n SET final_node_aggregations_blob_url = $2,\n status = 'waiting_for_proofs',\n updated_at = now()\n WHERE l1_batch_number = $1 AND status != 'queued'\n " - }, "42762c079948860eb59ba807eb9ae5a53b94c93e6b5635471d0018dde1d4c9d9": { "describe": { "columns": [ @@ -3959,21 +3719,6 @@ }, "query": "SELECT l1_batch_number, merkel_tree_paths_blob_url FROM witness_inputs WHERE status = 'successful' AND merkel_tree_paths_blob_url is NOT NULL AND updated_at < NOW() - INTERVAL '30 days' LIMIT $1" }, - "433d5da4d72150cf2c1e1007ee3ff51edfa51924f4b662b8cf382f06e60fd228": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Int4", - "Int8", - "Text", - "Text" - ] - } - }, - "query": "\n UPDATE node_aggregation_witness_jobs\n SET number_of_leaf_circuits = $1,\n leaf_layer_subqueues_blob_url = $3,\n aggregation_outputs_blob_url = $4,\n status = 'waiting_for_proofs',\n updated_at = now()\n WHERE l1_batch_number = $2 AND status != 'queued'\n " - }, "43b5082ff7673ee3a8e8f3fafa64667fac4f7f5c8bd26a21ead6b4ba0f8fd17b": { "describe": { "columns": [ @@ -5016,113 +4761,6 @@ }, "query": "SELECT MAX(priority_op_id) as \"op_id\" from transactions where is_priority = true" }, - "5eb9f25dacfb02e70a9fcf0a41937d4c63bd786efb2fd0d1180f449a3ae0bbc0": { - "describe": { - "columns": [ - { - "name": "l1_batch_number", - "ordinal": 0, - "type_info": "Int8" - }, - { - "name": "leaf_layer_subqueues", - "ordinal": 1, - "type_info": "Bytea" - }, - { - "name": "aggregation_outputs", - "ordinal": 2, - "type_info": "Bytea" - }, - { - "name": "number_of_leaf_circuits", - "ordinal": 3, - "type_info": "Int4" - }, - { - "name": "status", - "ordinal": 4, - "type_info": "Text" - }, - { - "name": "processing_started_at", - "ordinal": 5, - "type_info": "Timestamp" - }, - { - "name": "time_taken", - "ordinal": 6, - "type_info": "Time" - }, - { - "name": "error", - "ordinal": 7, - "type_info": "Text" - }, - { - "name": "created_at", - "ordinal": 8, - "type_info": "Timestamp" - }, - { - "name": "updated_at", - "ordinal": 9, - "type_info": "Timestamp" - }, - { - "name": "attempts", - "ordinal": 10, - "type_info": "Int4" - }, - { - "name": "leaf_layer_subqueues_blob_url", - "ordinal": 11, - "type_info": "Text" - }, - { - "name": "aggregation_outputs_blob_url", - "ordinal": 12, - "type_info": "Text" - }, - { - "name": "is_blob_cleaned", - "ordinal": 13, - "type_info": "Bool" - }, - { - "name": "protocol_version", - "ordinal": 14, - "type_info": "Int4" - } - ], - "nullable": [ - false, - true, - true, - true, - false, - true, - true, - true, - false, - false, - false, - true, - true, - false, - true - ], - "parameters": { - "Left": [ - "Interval", - "Int4", - "Int8", - "Int4Array" - ] - } - }, - "query": "\n UPDATE node_aggregation_witness_jobs\n SET status = 'in_progress', attempts = attempts + 1,\n updated_at = now(), processing_started_at = now()\n WHERE l1_batch_number = (\n SELECT l1_batch_number\n FROM node_aggregation_witness_jobs\n WHERE l1_batch_number <= $3\n AND\n ( status = 'queued'\n OR (status = 'in_progress' AND processing_started_at < now() - $1::interval)\n OR (status = 'failed' AND attempts < $2)\n )\n AND protocol_version = ANY($4)\n ORDER BY l1_batch_number ASC\n LIMIT 1\n FOR UPDATE\n SKIP LOCKED\n )\n RETURNING node_aggregation_witness_jobs.*\n " - }, "5f037f6ae8489d5224772d4f9e3e6cfc2075560957fa491d97a95c0e79ff4830": { "describe": { "columns": [ @@ -5558,24 +5196,6 @@ }, "query": "SELECT l2_to_l1_logs FROM l1_batches WHERE number = $1" }, - "67efc7ea5bd3821d8325759ed8357190f6122dd2ae503a57faf15d8b749a4361": { - "describe": { - "columns": [ - { - "name": "l1_batch_number", - "ordinal": 0, - "type_info": "Int8" - } - ], - "nullable": [ - false - ], - "parameters": { - "Left": [] - } - }, - "query": "\n UPDATE leaf_aggregation_witness_jobs\n SET status='queued'\n WHERE l1_batch_number IN\n (SELECT prover_jobs.l1_batch_number\n FROM prover_jobs\n JOIN leaf_aggregation_witness_jobs lawj ON prover_jobs.l1_batch_number = lawj.l1_batch_number\n WHERE lawj.status = 'waiting_for_proofs'\n AND prover_jobs.status = 'successful'\n AND prover_jobs.aggregation_round = 0\n GROUP BY prover_jobs.l1_batch_number, lawj.number_of_basic_circuits\n HAVING COUNT(*) = lawj.number_of_basic_circuits)\n RETURNING l1_batch_number;\n " - }, "6939e766e122458b2ac618d19b2759c4a7298ef72b81e8c3957e0a5cf35c9552": { "describe": { "columns": [ @@ -5609,18 +5229,6 @@ }, "query": "\n UPDATE witness_inputs_fri\n SET status = 'queued', updated_at = now(), processing_started_at = now()\n WHERE (status = 'in_progress' AND processing_started_at <= now() - $1::interval AND attempts < $2)\n OR (status = 'in_gpu_proof' AND processing_started_at <= now() - $1::interval AND attempts < $2)\n OR (status = 'failed' AND attempts < $2)\n RETURNING l1_batch_number, status, attempts\n " }, - "694f1d154f3f38b123d8f845fef6e876d35dc3743f1c5b69dce6be694e5e726c": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Int8" - ] - } - }, - "query": "UPDATE witness_inputs SET status='queued' WHERE l1_batch_number=$1 AND status='waiting_for_artifacts'" - }, "697835cdd5be1b99a0f332c4c8f3245e317b0282b46e55f15e728a7642382b25": { "describe": { "columns": [ @@ -5771,32 +5379,12 @@ "nullable": [], "parameters": { "Left": [ - "TextArray", - "Text" - ] - } - }, - "query": "INSERT INTO compiler_versions (version, compiler, created_at, updated_at) SELECT u.version, $2, now(), now() FROM UNNEST($1::text[]) AS u(version) ON CONFLICT (version, compiler) DO NOTHING" - }, - "6ffd22b0590341c38ce3957dccdb5a4edf47fb558bc64e4df08897a0c72dbf23": { - "describe": { - "columns": [ - { - "name": "protocol_version", - "ordinal": 0, - "type_info": "Int4" - } - ], - "nullable": [ - true - ], - "parameters": { - "Left": [ - "Int8" + "TextArray", + "Text" ] } }, - "query": "\n SELECT protocol_version\n FROM witness_inputs\n WHERE l1_batch_number = $1\n " + "query": "INSERT INTO compiler_versions (version, compiler, created_at, updated_at) SELECT u.version, $2, now(), now() FROM UNNEST($1::text[]) AS u(version) ON CONFLICT (version, compiler) DO NOTHING" }, "715aba794d60ce2faf937eacd9498b203dbb8e620d6d8850b9071cd72902ffbf": { "describe": { @@ -5858,24 +5446,6 @@ }, "query": "SELECT l1_batch_number FROM miniblocks WHERE number = $1" }, - "769c021b51b9aaafdf27b4019834729047702b17b0684f7271eecd6ffdf96e7c": { - "describe": { - "columns": [ - { - "name": "l1_batch_number", - "ordinal": 0, - "type_info": "Int8" - } - ], - "nullable": [ - false - ], - "parameters": { - "Left": [] - } - }, - "query": "\n UPDATE scheduler_witness_jobs\n SET status='queued'\n WHERE l1_batch_number IN\n (SELECT prover_jobs.l1_batch_number\n FROM prover_jobs\n JOIN scheduler_witness_jobs swj ON prover_jobs.l1_batch_number = swj.l1_batch_number\n WHERE swj.status = 'waiting_for_proofs'\n AND prover_jobs.status = 'successful'\n AND prover_jobs.aggregation_round = 2\n GROUP BY prover_jobs.l1_batch_number\n HAVING COUNT(*) = 1)\n RETURNING l1_batch_number;\n " - }, "7717652bb4933f87cbeb7baa2e70e8e0b439663c6b15493bd2e406bed2486b42": { "describe": { "columns": [ @@ -6161,24 +5731,6 @@ }, "query": "SELECT attempts FROM scheduler_witness_jobs_fri WHERE l1_batch_number = $1" }, - "7a5aba2130fec60318266c8059d3757cd78eb6099d50486b4996fb4090c99622": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Int8", - "Bytea", - "Bytea", - "Text", - "Text", - "Int4", - "Int4" - ] - } - }, - "query": "\n INSERT INTO leaf_aggregation_witness_jobs\n (l1_batch_number, basic_circuits, basic_circuits_inputs, basic_circuits_blob_url, basic_circuits_inputs_blob_url, number_of_basic_circuits, protocol_version, status, created_at, updated_at)\n VALUES ($1, $2, $3, $4, $5, $6, $7, 'waiting_for_proofs', now(), now())\n " - }, "7b8043a59029a19a3ba2433a438e8a4fe560aba7eda57b7a63b580de2e19aacb": { "describe": { "columns": [], @@ -8408,21 +7960,6 @@ }, "query": "SELECT attempts FROM basic_witness_input_producer_jobs WHERE l1_batch_number = $1" }, - "9aaf98668f384f634860c4acf793ff47be08975e5d09061cc26fd53dea249c55": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Int8", - "Bytea", - "Text", - "Int4" - ] - } - }, - "query": "\n INSERT INTO scheduler_witness_jobs\n (l1_batch_number, scheduler_witness, scheduler_witness_blob_url, protocol_version, status, created_at, updated_at)\n VALUES ($1, $2, $3, $4, 'waiting_for_artifacts', now(), now())\n " - }, "9b70e9039cdc1a8c8baf9220a9d42a9b1b209ce73f74cccb9e313bcacdc3daf3": { "describe": { "columns": [], @@ -8621,38 +8158,6 @@ }, "query": "INSERT INTO basic_witness_input_producer_jobs (l1_batch_number, status, created_at, updated_at) VALUES ($1, $2, now(), now()) ON CONFLICT (l1_batch_number) DO NOTHING" }, - "a0b720f4e9a558cb073725ecb62765c27d1635f3099d1850e7269bce8bf0ab36": { - "describe": { - "columns": [ - { - "name": "l1_batch_number", - "ordinal": 0, - "type_info": "Int8" - }, - { - "name": "leaf_layer_subqueues_blob_url", - "ordinal": 1, - "type_info": "Text" - }, - { - "name": "aggregation_outputs_blob_url", - "ordinal": 2, - "type_info": "Text" - } - ], - "nullable": [ - false, - true, - true - ], - "parameters": { - "Left": [ - "Int8" - ] - } - }, - "query": "\n SELECT l1_batch_number, leaf_layer_subqueues_blob_url, aggregation_outputs_blob_url FROM node_aggregation_witness_jobs\n WHERE status='successful'\n AND leaf_layer_subqueues_blob_url is NOT NULL\n AND aggregation_outputs_blob_url is NOT NULL\n AND updated_at < NOW() - INTERVAL '30 days'\n LIMIT $1;\n " - }, "a19b7137403c5cdf1be5f5122ce4d297ed661fa8bdb3bc91f8a81fe9da47469e": { "describe": { "columns": [ @@ -9193,19 +8698,6 @@ }, "query": "\n UPDATE scheduler_witness_jobs_fri\n SET status = 'queued', updated_at = now(), processing_started_at = now()\n WHERE (status = 'in_progress' AND processing_started_at <= now() - $1::interval AND attempts < $2)\n OR (status = 'failed' AND attempts < $2)\n RETURNING l1_batch_number, status, attempts\n " }, - "ad4f74aa6f131df0243f4fa500ade1b98aa335bd71ed417b02361e2c697e60f8": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Bytea", - "Int8" - ] - } - }, - "query": "\n UPDATE scheduler_witness_jobs\n SET aggregation_result_coords = $1,\n updated_at = now()\n WHERE l1_batch_number = $2\n " - }, "ae072f51b65d0b5212264be9a34027922e5aedef7e4741517ad8104bf5aa79e9": { "describe": { "columns": [], @@ -10488,38 +9980,6 @@ }, "query": "UPDATE l1_batches SET skip_proof = TRUE WHERE number = $1" }, - "d91a80fdfe140ac71760755a0bb6c29cf4f613dc3fd88df6facd63d7338b8470": { - "describe": { - "columns": [ - { - "name": "l1_batch_number", - "ordinal": 0, - "type_info": "Int8" - }, - { - "name": "scheduler_witness_blob_url", - "ordinal": 1, - "type_info": "Text" - }, - { - "name": "final_node_aggregations_blob_url", - "ordinal": 2, - "type_info": "Text" - } - ], - "nullable": [ - false, - true, - true - ], - "parameters": { - "Left": [ - "Int8" - ] - } - }, - "query": "\n SELECT l1_batch_number, scheduler_witness_blob_url, final_node_aggregations_blob_url FROM scheduler_witness_jobs\n WHERE status='successful'\n AND updated_at < NOW() - INTERVAL '30 days'\n AND scheduler_witness_blob_url is NOT NULL\n AND final_node_aggregations_blob_url is NOT NULL\n LIMIT $1;\n " - }, "dba127c0f3023586217bfb214c5d3749e8e7ec3edc0c99cfd970332e31f81cb7": { "describe": { "columns": [ @@ -10609,33 +10069,6 @@ }, "query": "SELECT id FROM protocol_versions" }, - "ddb3b38be2b6038b63288961f46ba7d3bb7250caff1146e13c5ee77b6a994ffc": { - "describe": { - "columns": [ - { - "name": "circuit_type", - "ordinal": 0, - "type_info": "Text" - }, - { - "name": "result", - "ordinal": 1, - "type_info": "Bytea" - } - ], - "nullable": [ - false, - true - ], - "parameters": { - "Left": [ - "Int8", - "Int4" - ] - } - }, - "query": "\n SELECT circuit_type, result from prover_jobs\n WHERE l1_batch_number = $1 AND status = 'successful' AND aggregation_round = $2\n ORDER BY sequence_number ASC;\n " - }, "ddd8b105f5e5cf9db40b14ea47e4ba2b3875f89280019464be34f51605833f1b": { "describe": { "columns": [], @@ -10687,95 +10120,6 @@ }, "query": "INSERT INTO storage (hashed_key, address, key, value, tx_hash, created_at, updated_at) SELECT u.hashed_key, u.address, u.key, u.value, u.tx_hash, now(), now() FROM UNNEST ($1::bytea[], $2::bytea[], $3::bytea[], $4::bytea[], $5::bytea[]) AS u(hashed_key, address, key, value, tx_hash) ON CONFLICT (hashed_key) DO UPDATE SET tx_hash = excluded.tx_hash, value = excluded.value, updated_at = now()" }, - "df857ee85c600bd90687b2ed91517d91a5dc4de3cd6c15c34119ca52a3321828": { - "describe": { - "columns": [ - { - "name": "l1_batch_number", - "ordinal": 0, - "type_info": "Int8" - }, - { - "name": "merkle_tree_paths", - "ordinal": 1, - "type_info": "Bytea" - }, - { - "name": "created_at", - "ordinal": 2, - "type_info": "Timestamp" - }, - { - "name": "updated_at", - "ordinal": 3, - "type_info": "Timestamp" - }, - { - "name": "status", - "ordinal": 4, - "type_info": "Text" - }, - { - "name": "time_taken", - "ordinal": 5, - "type_info": "Time" - }, - { - "name": "processing_started_at", - "ordinal": 6, - "type_info": "Timestamp" - }, - { - "name": "error", - "ordinal": 7, - "type_info": "Varchar" - }, - { - "name": "attempts", - "ordinal": 8, - "type_info": "Int4" - }, - { - "name": "merkel_tree_paths_blob_url", - "ordinal": 9, - "type_info": "Text" - }, - { - "name": "is_blob_cleaned", - "ordinal": 10, - "type_info": "Bool" - }, - { - "name": "protocol_version", - "ordinal": 11, - "type_info": "Int4" - } - ], - "nullable": [ - false, - true, - false, - false, - false, - false, - true, - true, - false, - true, - false, - true - ], - "parameters": { - "Left": [ - "Interval", - "Int4", - "Int8", - "Int4Array" - ] - } - }, - "query": "\n UPDATE witness_inputs\n SET status = 'in_progress', attempts = attempts + 1,\n updated_at = now(), processing_started_at = now()\n WHERE l1_batch_number = (\n SELECT l1_batch_number\n FROM witness_inputs\n WHERE l1_batch_number <= $3\n AND\n ( status = 'queued'\n OR (status = 'in_progress' AND processing_started_at < now() - $1::interval)\n OR (status = 'failed' AND attempts < $2)\n )\n AND protocol_version = ANY($4)\n ORDER BY l1_batch_number ASC\n LIMIT 1\n FOR UPDATE\n SKIP LOCKED\n )\n RETURNING witness_inputs.*\n " - }, "e05a8c74653afc78c892ddfd08e60ab040d2b2f7c4b5ee110988eac2dd0dd90d": { "describe": { "columns": [ @@ -11223,21 +10567,6 @@ }, "query": "UPDATE l1_batches SET predicted_commit_gas_cost = $2, updated_at = now() WHERE number = $1" }, - "ec35fc5128cf59d19e6d65ed6d84fcc50fedce921405c4ce700dd2e08c990642": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Int8", - "Bytea", - "Text", - "Int4" - ] - } - }, - "query": "INSERT INTO witness_inputs(l1_batch_number, merkle_tree_paths, merkel_tree_paths_blob_url, status, protocol_version, created_at, updated_at) VALUES ($1, $2, $3, 'queued', $4, now(), now()) ON CONFLICT (l1_batch_number) DO NOTHING" - }, "ed50c609371b4588964e29f8757c41973706710090a80eb025ec263ce3d019b4": { "describe": { "columns": [], @@ -11579,24 +10908,6 @@ }, "query": "SELECT hash FROM l1_batches WHERE number = $1" }, - "f0c83c517fdf9696a0acf288f061bd00a993e0b2379b667738b6876e2f588043": { - "describe": { - "columns": [ - { - "name": "l1_batch_number", - "ordinal": 0, - "type_info": "Int8" - } - ], - "nullable": [ - false - ], - "parameters": { - "Left": [] - } - }, - "query": "\n UPDATE node_aggregation_witness_jobs\n SET status='queued'\n WHERE l1_batch_number IN\n (SELECT prover_jobs.l1_batch_number\n FROM prover_jobs\n JOIN node_aggregation_witness_jobs nawj ON prover_jobs.l1_batch_number = nawj.l1_batch_number\n WHERE nawj.status = 'waiting_for_proofs'\n AND prover_jobs.status = 'successful'\n AND prover_jobs.aggregation_round = 1\n GROUP BY prover_jobs.l1_batch_number, nawj.number_of_leaf_circuits\n HAVING COUNT(*) = nawj.number_of_leaf_circuits)\n RETURNING l1_batch_number;\n " - }, "f15f0848cfd830ec5d5b479fdcdd36c6a4439495b7680614ac1b0e4d73fb992f": { "describe": { "columns": [ diff --git a/core/lib/dal/src/lib.rs b/core/lib/dal/src/lib.rs index 68d19fe84b95..d934c2c06e63 100644 --- a/core/lib/dal/src/lib.rs +++ b/core/lib/dal/src/lib.rs @@ -20,7 +20,7 @@ use crate::{ storage_logs_dedup_dal::StorageLogsDedupDal, storage_web3_dal::StorageWeb3Dal, sync_dal::SyncDal, system_dal::SystemDal, tokens_dal::TokensDal, tokens_web3_dal::TokensWeb3Dal, transactions_dal::TransactionsDal, - transactions_web3_dal::TransactionsWeb3Dal, witness_generator_dal::WitnessGeneratorDal, + transactions_web3_dal::TransactionsWeb3Dal, }; #[macro_use] @@ -60,7 +60,6 @@ pub mod tokens_dal; pub mod tokens_web3_dal; pub mod transactions_dal; pub mod transactions_web3_dal; -pub mod witness_generator_dal; #[cfg(test)] mod tests; @@ -183,10 +182,6 @@ impl<'a> StorageProcessor<'a> { ProverDal { storage: self } } - pub fn witness_generator_dal(&mut self) -> WitnessGeneratorDal<'_, 'a> { - WitnessGeneratorDal { storage: self } - } - pub fn contract_verification_dal(&mut self) -> ContractVerificationDal<'_, 'a> { ContractVerificationDal { storage: self } } diff --git a/core/lib/dal/src/tests/mod.rs b/core/lib/dal/src/tests/mod.rs index 94fb6e9ebf64..ba26501bdcfb 100644 --- a/core/lib/dal/src/tests/mod.rs +++ b/core/lib/dal/src/tests/mod.rs @@ -1,4 +1,4 @@ -use std::{fs, time::Duration}; +use std::time::Duration; use zksync_contracts::BaseSystemContractsHashes; use zksync_types::{ @@ -20,7 +20,6 @@ use crate::{ prover_dal::{GetProverJobsParams, ProverDal}, transactions_dal::{L2TxSubmissionResult, TransactionsDal}, transactions_web3_dal::TransactionsWeb3Dal, - witness_generator_dal::WitnessGeneratorDal, }; const DEFAULT_GAS_PER_PUBDATA: u32 = 100; @@ -396,277 +395,3 @@ async fn test_requeue_prover_jobs() { assert!(job.is_some()); } } - -#[tokio::test] -async fn test_move_leaf_aggregation_jobs_from_waiting_to_queued() { - let connection_pool = ConnectionPool::test_pool().await; - let storage = &mut connection_pool.access_storage().await.unwrap(); - let protocol_version = ProtocolVersion::default(); - storage - .protocol_versions_dal() - .save_protocol_version_with_tx(protocol_version) - .await; - storage - .protocol_versions_dal() - .save_prover_protocol_version(Default::default()) - .await; - let block_number = 1; - let header = L1BatchHeader::new( - L1BatchNumber(block_number), - 0, - Default::default(), - Default::default(), - ProtocolVersionId::latest(), - ); - storage - .blocks_dal() - .insert_l1_batch(&header, &[], Default::default(), &[], &[]) - .await - .unwrap(); - - let mut prover_dal = ProverDal { storage }; - let circuits = create_circuits(); - let l1_batch_number = L1BatchNumber(block_number); - prover_dal - .insert_prover_jobs( - l1_batch_number, - circuits.clone(), - AggregationRound::BasicCircuits, - ProtocolVersionId::latest() as i32, - ) - .await; - let prover_jobs_params = get_default_prover_jobs_params(l1_batch_number); - let jobs = prover_dal.get_jobs(prover_jobs_params).await; - let job_ids: Vec = jobs.unwrap().into_iter().map(|job| job.id).collect(); - - let proof = get_sample_proof(); - - // mark all basic circuit proofs as successful. - for id in job_ids.iter() { - prover_dal - .save_proof(*id, Duration::from_secs(0), proof.clone(), "unit-test") - .await - .unwrap(); - } - let mut witness_generator_dal = WitnessGeneratorDal { storage }; - - witness_generator_dal - .create_aggregation_jobs( - l1_batch_number, - "basic_circuits_1.bin", - "basic_circuits_inputs_1.bin", - circuits.len(), - "scheduler_witness_1.bin", - ProtocolVersionId::latest() as i32, - ) - .await; - - // move the leaf aggregation job to be queued - witness_generator_dal - .move_leaf_aggregation_jobs_from_waiting_to_queued() - .await; - - // Ensure get-next job gives the leaf aggregation witness job - let job = witness_generator_dal - .get_next_leaf_aggregation_witness_job( - Duration::from_secs(0), - 10, - u32::MAX, - &[ProtocolVersionId::latest()], - ) - .await; - assert_eq!(l1_batch_number, job.unwrap().block_number); -} - -#[tokio::test] -async fn test_move_node_aggregation_jobs_from_waiting_to_queued() { - let connection_pool = ConnectionPool::test_pool().await; - let storage = &mut connection_pool.access_storage().await.unwrap(); - let protocol_version = ProtocolVersion::default(); - storage - .protocol_versions_dal() - .save_protocol_version_with_tx(protocol_version) - .await; - storage - .protocol_versions_dal() - .save_prover_protocol_version(Default::default()) - .await; - let block_number = 1; - let header = L1BatchHeader::new( - L1BatchNumber(block_number), - 0, - Default::default(), - Default::default(), - ProtocolVersionId::latest(), - ); - storage - .blocks_dal() - .insert_l1_batch(&header, &[], Default::default(), &[], &[]) - .await - .unwrap(); - - let mut prover_dal = ProverDal { storage }; - let circuits = create_circuits(); - let l1_batch_number = L1BatchNumber(block_number); - prover_dal - .insert_prover_jobs( - l1_batch_number, - circuits.clone(), - AggregationRound::LeafAggregation, - ProtocolVersionId::latest() as i32, - ) - .await; - let prover_jobs_params = get_default_prover_jobs_params(l1_batch_number); - let jobs = prover_dal.get_jobs(prover_jobs_params).await; - let job_ids: Vec = jobs.unwrap().into_iter().map(|job| job.id).collect(); - - let proof = get_sample_proof(); - // mark all leaf aggregation circuit proofs as successful. - for id in job_ids { - prover_dal - .save_proof(id, Duration::from_secs(0), proof.clone(), "unit-test") - .await - .unwrap(); - } - let mut witness_generator_dal = WitnessGeneratorDal { storage }; - - witness_generator_dal - .create_aggregation_jobs( - l1_batch_number, - "basic_circuits_1.bin", - "basic_circuits_inputs_1.bin", - circuits.len(), - "scheduler_witness_1.bin", - ProtocolVersionId::latest() as i32, - ) - .await; - witness_generator_dal - .save_leaf_aggregation_artifacts( - l1_batch_number, - circuits.len(), - "leaf_layer_subqueues_1.bin", - "aggregation_outputs_1.bin", - ) - .await; - - // move the leaf aggregation job to be queued - witness_generator_dal - .move_node_aggregation_jobs_from_waiting_to_queued() - .await; - - // Ensure get-next job gives the node aggregation witness job - let job = witness_generator_dal - .get_next_node_aggregation_witness_job( - Duration::from_secs(0), - 10, - u32::MAX, - &[ProtocolVersionId::latest()], - ) - .await; - assert_eq!(l1_batch_number, job.unwrap().block_number); -} - -#[tokio::test] -async fn test_move_scheduler_jobs_from_waiting_to_queued() { - let connection_pool = ConnectionPool::test_pool().await; - let storage = &mut connection_pool.access_storage().await.unwrap(); - let protocol_version = ProtocolVersion::default(); - storage - .protocol_versions_dal() - .save_protocol_version_with_tx(protocol_version) - .await; - storage - .protocol_versions_dal() - .save_prover_protocol_version(Default::default()) - .await; - let block_number = 1; - let header = L1BatchHeader::new( - L1BatchNumber(block_number), - 0, - Default::default(), - Default::default(), - ProtocolVersionId::latest(), - ); - storage - .blocks_dal() - .insert_l1_batch(&header, &[], Default::default(), &[], &[]) - .await - .unwrap(); - - let mut prover_dal = ProverDal { storage }; - let circuits = vec![( - "Node aggregation", - "1_0_Node aggregation_NodeAggregation.bin".to_owned(), - )]; - let l1_batch_number = L1BatchNumber(block_number); - prover_dal - .insert_prover_jobs( - l1_batch_number, - circuits.clone(), - AggregationRound::NodeAggregation, - ProtocolVersionId::latest() as i32, - ) - .await; - let prover_jobs_params = get_default_prover_jobs_params(l1_batch_number); - let jobs = prover_dal.get_jobs(prover_jobs_params).await; - let job_ids: Vec = jobs.unwrap().into_iter().map(|job| job.id).collect(); - - let proof = get_sample_proof(); - // mark node aggregation circuit proofs as successful. - for id in &job_ids { - prover_dal - .save_proof(*id, Duration::from_secs(0), proof.clone(), "unit-test") - .await - .unwrap(); - } - let mut witness_generator_dal = WitnessGeneratorDal { storage }; - - witness_generator_dal - .create_aggregation_jobs( - l1_batch_number, - "basic_circuits_1.bin", - "basic_circuits_inputs_1.bin", - circuits.len(), - "scheduler_witness_1.bin", - ProtocolVersionId::latest() as i32, - ) - .await; - witness_generator_dal - .save_node_aggregation_artifacts(l1_batch_number, "final_node_aggregations_1.bin") - .await; - - // move the leaf aggregation job to be queued - witness_generator_dal - .move_scheduler_jobs_from_waiting_to_queued() - .await; - - // Ensure get-next job gives the scheduler witness job - let job = witness_generator_dal - .get_next_scheduler_witness_job( - Duration::from_secs(0), - 10, - u32::MAX, - &[ProtocolVersionId::latest()], - ) - .await; - assert_eq!(l1_batch_number, job.unwrap().block_number); -} - -fn get_default_prover_jobs_params(l1_batch_number: L1BatchNumber) -> GetProverJobsParams { - GetProverJobsParams { - statuses: None, - blocks: Some(std::ops::Range { - start: l1_batch_number, - end: l1_batch_number + 1, - }), - limit: None, - desc: false, - round: None, - } -} - -fn get_sample_proof() -> Vec { - let zksync_home = std::env::var("ZKSYNC_HOME").unwrap_or_else(|_| ".".into()); - fs::read(format!("{}/etc/prover-test-data/proof.bin", zksync_home)) - .expect("Failed reading test proof file") -} diff --git a/core/lib/dal/src/witness_generator_dal.rs b/core/lib/dal/src/witness_generator_dal.rs deleted file mode 100644 index b437c2ad34f5..000000000000 --- a/core/lib/dal/src/witness_generator_dal.rs +++ /dev/null @@ -1,929 +0,0 @@ -use std::{collections::HashMap, ops::Range, time::Duration}; - -use itertools::Itertools; -use sqlx::Row; -use zksync_types::{ - proofs::{AggregationRound, JobCountStatistics, WitnessGeneratorJobMetadata, WitnessJobInfo}, - zkevm_test_harness::{ - abstract_zksync_circuit::concrete_circuits::{ZkSyncCircuit, ZkSyncProof}, - bellman::{bn256::Bn256, plonk::better_better_cs::proof::Proof}, - witness::oracle::VmWitnessOracle, - }, - L1BatchNumber, ProtocolVersionId, -}; - -use crate::{ - instrument::InstrumentExt, - metrics::MethodLatency, - models::storage_witness_job_info::StorageWitnessJobInfo, - time_utils::{duration_to_naive_time, pg_interval_from_duration}, - StorageProcessor, -}; - -#[derive(Debug)] -pub struct WitnessGeneratorDal<'a, 'c> { - pub(crate) storage: &'a mut StorageProcessor<'c>, -} - -impl WitnessGeneratorDal<'_, '_> { - pub async fn get_next_basic_circuit_witness_job( - &mut self, - processing_timeout: Duration, - max_attempts: u32, - last_l1_batch_to_process: u32, - protocol_versions: &[ProtocolVersionId], - ) -> Option { - let protocol_versions: Vec = protocol_versions.iter().map(|&id| id as i32).collect(); - let processing_timeout = pg_interval_from_duration(processing_timeout); - let result: Option = sqlx::query!( - " - UPDATE witness_inputs - SET status = 'in_progress', attempts = attempts + 1, - updated_at = now(), processing_started_at = now() - WHERE l1_batch_number = ( - SELECT l1_batch_number - FROM witness_inputs - WHERE l1_batch_number <= $3 - AND - ( status = 'queued' - OR (status = 'in_progress' AND processing_started_at < now() - $1::interval) - OR (status = 'failed' AND attempts < $2) - ) - AND protocol_version = ANY($4) - ORDER BY l1_batch_number ASC - LIMIT 1 - FOR UPDATE - SKIP LOCKED - ) - RETURNING witness_inputs.* - ", - &processing_timeout, - max_attempts as i32, - last_l1_batch_to_process as i64, - &protocol_versions[..], - ) - .fetch_optional(self.storage.conn()) - .await - .unwrap() - .map(|row| WitnessGeneratorJobMetadata { - block_number: L1BatchNumber(row.l1_batch_number as u32), - proofs: vec![], - }); - result - } - - pub async fn get_witness_generated_l1_batches( - &mut self, - ) -> Vec<(L1BatchNumber, AggregationRound)> { - let mut generated_batches = Vec::with_capacity(4); - for round in [ - "node_aggregation_witness_jobs", - "leaf_aggregation_witness_jobs", - "scheduler_witness_jobs", - "witness_inputs", - ] { - let record = sqlx::query(&format!( - "SELECT MAX(l1_batch_number) as l1_batch FROM {} WHERE status='successful'", - round - )) - .fetch_one(self.storage.conn()) - .await - .unwrap(); - let generated_batch = ( - L1BatchNumber( - record - .get::, &str>("l1_batch") - .unwrap_or_default() as u32, - ), - match round { - "node_aggregation_witness_jobs" => AggregationRound::NodeAggregation, - "leaf_aggregation_witness_jobs" => AggregationRound::LeafAggregation, - "scheduler_witness_jobs" => AggregationRound::Scheduler, - "witness_inputs" => AggregationRound::BasicCircuits, - _ => unreachable!(), - }, - ); - generated_batches.push(generated_batch); - } - generated_batches - } - - pub async fn get_next_leaf_aggregation_witness_job( - &mut self, - processing_timeout: Duration, - max_attempts: u32, - last_l1_batch_to_process: u32, - protocol_versions: &[ProtocolVersionId], - ) -> Option { - let protocol_versions: Vec = protocol_versions.iter().map(|&id| id as i32).collect(); - let processing_timeout = pg_interval_from_duration(processing_timeout); - let record = sqlx::query!( - " - UPDATE leaf_aggregation_witness_jobs - SET status = 'in_progress', attempts = attempts + 1, - updated_at = now(), processing_started_at = now() - WHERE l1_batch_number = ( - SELECT l1_batch_number - FROM leaf_aggregation_witness_jobs - WHERE l1_batch_number <= $3 - AND - ( status = 'queued' - OR (status = 'in_progress' AND processing_started_at < now() - $1::interval) - OR (status = 'failed' AND attempts < $2) - ) - AND protocol_version = ANY($4) - ORDER BY l1_batch_number ASC - LIMIT 1 - FOR UPDATE - SKIP LOCKED - ) - RETURNING leaf_aggregation_witness_jobs.* - ", - &processing_timeout, - max_attempts as i32, - last_l1_batch_to_process as i64, - &protocol_versions[..], - ) - .fetch_optional(self.storage.conn()) - .await - .unwrap(); - if let Some(row) = record { - let l1_batch_number = L1BatchNumber(row.l1_batch_number as u32); - let number_of_basic_circuits = row.number_of_basic_circuits; - - // Now that we have a job in `queued` status, we need to enrich it with the computed proofs. - // We select `aggregation_round = 0` to only get basic circuits. - // Note that at this point there cannot be any other circuits anyway, - // but we keep the check for explicitness - let basic_circuits_proofs: Vec< - Proof>>, - > = self - .load_proofs_for_block(l1_batch_number, AggregationRound::BasicCircuits) - .await; - - assert_eq!( - basic_circuits_proofs.len(), - number_of_basic_circuits as usize, - "leaf_aggregation_witness_job for l1 batch {} is in status `queued`, but there are only {} computed basic proofs, which is different from expected {}", - l1_batch_number, - basic_circuits_proofs.len(), - number_of_basic_circuits - ); - Some(WitnessGeneratorJobMetadata { - block_number: l1_batch_number, - proofs: basic_circuits_proofs, - }) - } else { - None - } - } - - pub async fn get_next_node_aggregation_witness_job( - &mut self, - processing_timeout: Duration, - max_attempts: u32, - last_l1_batch_to_process: u32, - protocol_versions: &[ProtocolVersionId], - ) -> Option { - let protocol_versions: Vec = protocol_versions.iter().map(|&id| id as i32).collect(); - { - let processing_timeout = pg_interval_from_duration(processing_timeout); - let record = sqlx::query!( - " - UPDATE node_aggregation_witness_jobs - SET status = 'in_progress', attempts = attempts + 1, - updated_at = now(), processing_started_at = now() - WHERE l1_batch_number = ( - SELECT l1_batch_number - FROM node_aggregation_witness_jobs - WHERE l1_batch_number <= $3 - AND - ( status = 'queued' - OR (status = 'in_progress' AND processing_started_at < now() - $1::interval) - OR (status = 'failed' AND attempts < $2) - ) - AND protocol_version = ANY($4) - ORDER BY l1_batch_number ASC - LIMIT 1 - FOR UPDATE - SKIP LOCKED - ) - RETURNING node_aggregation_witness_jobs.* - ", - &processing_timeout, - max_attempts as i32, - last_l1_batch_to_process as i64, - &protocol_versions[..], - ) - .fetch_optional(self.storage.conn()) - .await - .unwrap(); - if let Some(row) = record { - let l1_batch_number = L1BatchNumber(row.l1_batch_number as u32); - let number_of_leaf_circuits = row.number_of_leaf_circuits.expect("number_of_leaf_circuits is not found in a `queued` `node_aggregation_witness_jobs` job"); - - // Now that we have a job in `queued` status, we need to enrich it with the computed proofs. - // We select `aggregation_round = 1` to only get leaf aggregation circuits - let leaf_circuits_proofs: Vec< - Proof>>, - > = self - .load_proofs_for_block(l1_batch_number, AggregationRound::LeafAggregation) - .await; - - assert_eq!( - leaf_circuits_proofs.len(), - number_of_leaf_circuits as usize, - "node_aggregation_witness_job for l1 batch {} is in status `queued`, but there are only {} computed leaf proofs, which is different from expected {}", - l1_batch_number, - leaf_circuits_proofs.len(), - number_of_leaf_circuits - ); - Some(WitnessGeneratorJobMetadata { - block_number: l1_batch_number, - proofs: leaf_circuits_proofs, - }) - } else { - None - } - } - } - - pub async fn get_next_scheduler_witness_job( - &mut self, - processing_timeout: Duration, - max_attempts: u32, - last_l1_batch_to_process: u32, - protocol_versions: &[ProtocolVersionId], - ) -> Option { - let protocol_versions: Vec = protocol_versions.iter().map(|&id| id as i32).collect(); - { - let processing_timeout = pg_interval_from_duration(processing_timeout); - let record = sqlx::query!( - " - UPDATE scheduler_witness_jobs - SET status = 'in_progress', attempts = attempts + 1, - updated_at = now(), processing_started_at = now() - WHERE l1_batch_number = ( - SELECT l1_batch_number - FROM scheduler_witness_jobs - WHERE l1_batch_number <= $3 - AND - ( status = 'queued' - OR (status = 'in_progress' AND processing_started_at < now() - $1::interval) - OR (status = 'failed' AND attempts < $2) - ) - AND protocol_version = ANY($4) - ORDER BY l1_batch_number ASC - LIMIT 1 - FOR UPDATE - SKIP LOCKED - ) - RETURNING scheduler_witness_jobs.* - ", - &processing_timeout, - max_attempts as i32, - last_l1_batch_to_process as i64, - &protocol_versions[..], - ) - .fetch_optional(self.storage.conn()) - .await - .unwrap(); - if let Some(row) = record { - let l1_batch_number = L1BatchNumber(row.l1_batch_number as u32); - // Now that we have a job in `queued` status, we need to enrich it with the computed proof. - // We select `aggregation_round = 2` to only get node aggregation circuits - let leaf_circuits_proofs: Vec< - Proof>>, - > = self - .load_proofs_for_block(l1_batch_number, AggregationRound::NodeAggregation) - .await; - - assert_eq!( - leaf_circuits_proofs.len(), - 1usize, - "scheduler_job for l1 batch {} is in status `queued`, but there is {} computed node proofs. We expect exactly one node proof.", - l1_batch_number.0, - leaf_circuits_proofs.len() - ); - Some(WitnessGeneratorJobMetadata { - block_number: l1_batch_number, - proofs: leaf_circuits_proofs, - }) - } else { - None - } - } - } - - async fn load_proofs_for_block( - &mut self, - block_number: L1BatchNumber, - aggregation_round: AggregationRound, - ) -> Vec>>> { - { - sqlx::query!( - " - SELECT circuit_type, result from prover_jobs - WHERE l1_batch_number = $1 AND status = 'successful' AND aggregation_round = $2 - ORDER BY sequence_number ASC; - ", - block_number.0 as i64, - aggregation_round as i64 - ) - .fetch_all(self.storage.conn()) - .await - .unwrap() - .into_iter() - .map(|row| { - ZkSyncProof::into_proof(bincode::deserialize::>( - &row.result - .expect("prove_job with `successful` status has no result"), - ) - .expect("cannot deserialize proof")) - }) - .collect::>>>>() - } - } - - pub async fn mark_witness_job_as_successful( - &mut self, - block_number: L1BatchNumber, - aggregation_round: AggregationRound, - time_taken: Duration, - ) { - ({ - let table_name = Self::input_table_name_for(aggregation_round); - let sql = format!( - "UPDATE {} - SET status = 'successful', updated_at = now(), time_taken = $1 - WHERE l1_batch_number = $2", - table_name - ); - let mut query = sqlx::query(&sql); - query = query.bind(duration_to_naive_time(time_taken)); - query = query.bind(block_number.0 as i64); - - query.execute(self.storage.conn()).await.unwrap(); - }); - } - - /// Is invoked by the prover when all the required proofs are computed - pub async fn mark_witness_job_as_queued( - &mut self, - block_number: L1BatchNumber, - aggregation_round: AggregationRound, - ) { - ({ - let table_name = Self::input_table_name_for(aggregation_round); - let sql = format!( - "UPDATE {} - SET status = 'queued', updated_at = now() - WHERE l1_batch_number = $1", - table_name - ); - let mut query = sqlx::query(&sql); - query = query.bind(block_number.0 as i64); - - query.execute(self.storage.conn()).await.unwrap(); - }); - } - - pub async fn mark_witness_job_as_skipped( - &mut self, - block_number: L1BatchNumber, - aggregation_round: AggregationRound, - ) { - ({ - let table_name = Self::input_table_name_for(aggregation_round); - let sql = format!( - "UPDATE {} - SET status = 'skipped', updated_at = now() - WHERE l1_batch_number = $1", - table_name - ); - let mut query = sqlx::query(&sql); - query = query.bind(block_number.0 as i64); - - query.execute(self.storage.conn()).await.unwrap(); - }); - } - - /// Is invoked by the Witness Generator when the previous aggregation round is complete - pub async fn mark_witness_job_as_waiting_for_proofs( - &mut self, - block_number: L1BatchNumber, - aggregation_round: AggregationRound, - ) { - let table_name = Self::input_table_name_for(aggregation_round); - let sql = format!( - "UPDATE {} - SET status = 'waiting_for_proofs', updated_at = now() - WHERE l1_batch_number = $1", - table_name - ); - let mut query = sqlx::query(&sql); - query = query.bind(block_number.0 as i64); - - query.execute(self.storage.conn()).await.unwrap(); - } - - pub async fn mark_witness_job_as_failed( - &mut self, - aggregation_round: AggregationRound, - l1_batch_number: L1BatchNumber, - time_taken: Duration, - error: String, - ) -> u32 { - let table_name = Self::input_table_name_for(aggregation_round); - let sql = format!( - "UPDATE {} - SET status = 'failed', updated_at = now(), time_taken = $1, error = $2 - WHERE l1_batch_number = $3 - RETURNING attempts - ", - table_name - ); - let mut query = sqlx::query(&sql); - query = query.bind(duration_to_naive_time(time_taken)); - query = query.bind(error); - query = query.bind(l1_batch_number.0 as i64); - // returns the number of attempts of the job - query - .fetch_one(self.storage.conn()) - .await - .unwrap() - .get::("attempts") as u32 - } - - /// Creates a leaf_aggregation_job in `waiting_for_proofs` status, - /// and also a node_aggregation_job and scheduler_job in `waiting_for_artifacts` status. - /// The jobs will be advanced to `waiting_for_proofs` by the `Witness Generator` when the corresponding artifacts are computed, - /// and to `queued` by the `Prover` when all the dependency proofs are computed - pub async fn create_aggregation_jobs( - &mut self, - block_number: L1BatchNumber, - basic_circuits_blob_url: &str, - basic_circuits_inputs_blob_url: &str, - number_of_basic_circuits: usize, - scheduler_witness_blob_url: &str, - protocol_version: i32, - ) { - { - let latency = MethodLatency::new("create_aggregation_jobs"); - - sqlx::query!( - " - INSERT INTO leaf_aggregation_witness_jobs - (l1_batch_number, basic_circuits, basic_circuits_inputs, basic_circuits_blob_url, basic_circuits_inputs_blob_url, number_of_basic_circuits, protocol_version, status, created_at, updated_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, 'waiting_for_proofs', now(), now()) - ", - block_number.0 as i64, - // TODO(SMA-1476): remove the below columns once blob is migrated to GCS. - vec![], - vec![], - basic_circuits_blob_url, - basic_circuits_inputs_blob_url, - number_of_basic_circuits as i64, - protocol_version, - ) - .execute(self.storage.conn()) - .await - .unwrap(); - - sqlx::query!( - " - INSERT INTO node_aggregation_witness_jobs - (l1_batch_number, protocol_version, status, created_at, updated_at) - VALUES ($1, $2, 'waiting_for_artifacts', now(), now()) - ", - block_number.0 as i64, - protocol_version, - ) - .execute(self.storage.conn()) - .await - .unwrap(); - - sqlx::query!( - " - INSERT INTO scheduler_witness_jobs - (l1_batch_number, scheduler_witness, scheduler_witness_blob_url, protocol_version, status, created_at, updated_at) - VALUES ($1, $2, $3, $4, 'waiting_for_artifacts', now(), now()) - ", - block_number.0 as i64, - // TODO(SMA-1476): remove the below column once blob is migrated to GCS. - vec![], - scheduler_witness_blob_url, - protocol_version, - ) - .execute(self.storage.conn()) - .await - .unwrap(); - - drop(latency); - } - } - - /// Saves artifacts in node_aggregation_job - /// and advances it to `waiting_for_proofs` status - /// it will be advanced to `queued` by the prover when all the dependency proofs are computed. - /// If the node aggregation job was already `queued` in case of connector run of same leaf aggregation job - /// we keep the status as is to prevent data race. - pub async fn save_leaf_aggregation_artifacts( - &mut self, - l1_batch_number: L1BatchNumber, - number_of_leaf_circuits: usize, - leaf_layer_subqueues_blob_url: &str, - aggregation_outputs_blob_url: &str, - ) { - { - sqlx::query!( - " - UPDATE node_aggregation_witness_jobs - SET number_of_leaf_circuits = $1, - leaf_layer_subqueues_blob_url = $3, - aggregation_outputs_blob_url = $4, - status = 'waiting_for_proofs', - updated_at = now() - WHERE l1_batch_number = $2 AND status != 'queued' - ", - number_of_leaf_circuits as i64, - l1_batch_number.0 as i64, - leaf_layer_subqueues_blob_url, - aggregation_outputs_blob_url, - ) - .instrument("save_leaf_aggregation_artifacts") - .report_latency() - .with_arg("l1_batch_number", &l1_batch_number) - .execute(self.storage.conn()) - .await - .unwrap(); - } - } - - /// Saves artifacts in `scheduler_artifacts_jobs` and advances it to `waiting_for_proofs` status. - /// It will be advanced to `queued` by the prover when all the dependency proofs are computed. - /// If the scheduler witness job was already queued the in case of concurrent run - /// of same node aggregation job, we keep the status as is to prevent data race. - pub async fn save_node_aggregation_artifacts( - &mut self, - block_number: L1BatchNumber, - node_aggregations_blob_url: &str, - ) { - { - sqlx::query!( - " - UPDATE scheduler_witness_jobs - SET final_node_aggregations_blob_url = $2, - status = 'waiting_for_proofs', - updated_at = now() - WHERE l1_batch_number = $1 AND status != 'queued' - ", - block_number.0 as i64, - node_aggregations_blob_url, - ) - .instrument("save_node_aggregation_artifacts") - .report_latency() - .execute(self.storage.conn()) - .await - .unwrap(); - } - } - - pub async fn save_final_aggregation_result( - &mut self, - block_number: L1BatchNumber, - aggregation_result_coords: [[u8; 32]; 4], - ) { - { - let aggregation_result_coords_serialized = - bincode::serialize(&aggregation_result_coords) - .expect("cannot serialize aggregation_result_coords"); - sqlx::query!( - " - UPDATE scheduler_witness_jobs - SET aggregation_result_coords = $1, - updated_at = now() - WHERE l1_batch_number = $2 - ", - aggregation_result_coords_serialized, - block_number.0 as i64, - ) - .execute(self.storage.conn()) - .await - .unwrap(); - } - } - - pub async fn get_witness_jobs_stats( - &mut self, - aggregation_round: AggregationRound, - ) -> JobCountStatistics { - { - let table_name = Self::input_table_name_for(aggregation_round); - let sql = format!( - r#" - SELECT COUNT(*) as "count", status as "status" - FROM {} - GROUP BY status - "#, - table_name - ); - let mut results: HashMap = sqlx::query(&sql) - .fetch_all(self.storage.conn()) - .await - .unwrap() - .into_iter() - .map(|row| (row.get("status"), row.get::("count"))) - .collect::>(); - - JobCountStatistics { - queued: results.remove("queued").unwrap_or(0i64) as usize, - in_progress: results.remove("in_progress").unwrap_or(0i64) as usize, - failed: results.remove("failed").unwrap_or(0i64) as usize, - successful: results.remove("successful").unwrap_or(0i64) as usize, - } - } - } - - fn input_table_name_for(aggregation_round: AggregationRound) -> &'static str { - match aggregation_round { - AggregationRound::BasicCircuits => "witness_inputs", - AggregationRound::LeafAggregation => "leaf_aggregation_witness_jobs", - AggregationRound::NodeAggregation => "node_aggregation_witness_jobs", - AggregationRound::Scheduler => "scheduler_witness_jobs", - } - } - - pub async fn get_jobs( - &mut self, - opts: GetWitnessJobsParams, - ) -> Result, sqlx::Error> { - struct SqlSlice { - columns: String, - table_name: String, - } - - impl SqlSlice { - fn new(ar: u32, table_name: String) -> SqlSlice { - SqlSlice { - columns: format!( - "{} as aggregation_round, - l1_batch_number, - created_at, - updated_at, - status, - time_taken, - processing_started_at, - error, - attempts", - ar - ), - table_name, - } - } - - fn sql(&self, opts: &GetWitnessJobsParams) -> String { - let where_blocks = opts - .blocks - .as_ref() - .map(|b| format!("AND l1_batch_number BETWEEN {} AND {}", b.start, b.end)) - .unwrap_or_default(); - - format!( - "SELECT {} - FROM {} - WHERE 1 = 1 -- Where clause can't be empty - {where_blocks}", - self.columns, self.table_name - ) - } - } - - let slices = vec![ - SqlSlice::new(0, "witness_inputs".to_string()), - SqlSlice::new(1, "leaf_aggregation_witness_jobs".to_string()), - SqlSlice::new(2, "node_aggregation_witness_jobs".to_string()), - SqlSlice::new(3, "scheduler_witness_jobs".to_string()), - ]; - - let sql = slices.iter().map(move |x| x.sql(&opts)).join(" UNION "); - - let query = sqlx::query_as(&sql); - - Ok(query - .fetch_all(self.storage.conn()) - .await? - .into_iter() - .map(|x: StorageWitnessJobInfo| x.into()) - .collect()) - } - - pub async fn save_witness_inputs( - &mut self, - block_number: L1BatchNumber, - object_key: &str, - protocol_version: Option, - ) { - { - sqlx::query!( - "INSERT INTO witness_inputs(l1_batch_number, merkle_tree_paths, merkel_tree_paths_blob_url, status, protocol_version, created_at, updated_at) \ - VALUES ($1, $2, $3, 'queued', $4, now(), now()) \ - ON CONFLICT (l1_batch_number) DO NOTHING", - block_number.0 as i64, - // TODO(SMA-1476): remove the below column once blob is migrated to GCS. - vec![], - object_key, - protocol_version.map(|v| v as i32), - ) - .fetch_optional(self.storage.conn()) - .await - .unwrap(); - } - } - - pub async fn mark_witness_inputs_job_as_queued(&mut self, block_number: L1BatchNumber) { - sqlx::query!( - "UPDATE witness_inputs \ - SET status='queued' \ - WHERE l1_batch_number=$1 \ - AND status='waiting_for_artifacts'", - block_number.0 as i64, - ) - .execute(self.storage.conn()) - .await - .unwrap(); - } - - pub async fn get_leaf_layer_subqueues_and_aggregation_outputs_blob_urls_to_be_cleaned( - &mut self, - limit: u8, - ) -> Vec<(i64, (String, String))> { - { - let job_ids = sqlx::query!( - r#" - SELECT l1_batch_number, leaf_layer_subqueues_blob_url, aggregation_outputs_blob_url FROM node_aggregation_witness_jobs - WHERE status='successful' - AND leaf_layer_subqueues_blob_url is NOT NULL - AND aggregation_outputs_blob_url is NOT NULL - AND updated_at < NOW() - INTERVAL '30 days' - LIMIT $1; - "#, - limit as i32 - ) - .fetch_all(self.storage.conn()) - .await - .unwrap(); - job_ids - .into_iter() - .map(|row| { - ( - row.l1_batch_number, - ( - row.leaf_layer_subqueues_blob_url.unwrap(), - row.aggregation_outputs_blob_url.unwrap(), - ), - ) - }) - .collect() - } - } - - pub async fn get_scheduler_witness_and_node_aggregations_blob_urls_to_be_cleaned( - &mut self, - limit: u8, - ) -> Vec<(i64, (String, String))> { - { - let job_ids = sqlx::query!( - r#" - SELECT l1_batch_number, scheduler_witness_blob_url, final_node_aggregations_blob_url FROM scheduler_witness_jobs - WHERE status='successful' - AND updated_at < NOW() - INTERVAL '30 days' - AND scheduler_witness_blob_url is NOT NULL - AND final_node_aggregations_blob_url is NOT NULL - LIMIT $1; - "#, - limit as i32 - ) - .fetch_all(self.storage.conn()) - .await - .unwrap(); - job_ids - .into_iter() - .map(|row| { - ( - row.l1_batch_number, - ( - row.scheduler_witness_blob_url.unwrap(), - row.final_node_aggregations_blob_url.unwrap(), - ), - ) - }) - .collect() - } - } - - pub async fn move_leaf_aggregation_jobs_from_waiting_to_queued(&mut self) -> Vec { - { - sqlx::query!( - r#" - UPDATE leaf_aggregation_witness_jobs - SET status='queued' - WHERE l1_batch_number IN - (SELECT prover_jobs.l1_batch_number - FROM prover_jobs - JOIN leaf_aggregation_witness_jobs lawj ON prover_jobs.l1_batch_number = lawj.l1_batch_number - WHERE lawj.status = 'waiting_for_proofs' - AND prover_jobs.status = 'successful' - AND prover_jobs.aggregation_round = 0 - GROUP BY prover_jobs.l1_batch_number, lawj.number_of_basic_circuits - HAVING COUNT(*) = lawj.number_of_basic_circuits) - RETURNING l1_batch_number; - "#, - ) - .fetch_all(self.storage.conn()) - .await - .unwrap() - .into_iter() - .map(|row| row.l1_batch_number) - .collect() - } - } - - pub async fn move_node_aggregation_jobs_from_waiting_to_queued(&mut self) -> Vec { - { - sqlx::query!( - r#" - UPDATE node_aggregation_witness_jobs - SET status='queued' - WHERE l1_batch_number IN - (SELECT prover_jobs.l1_batch_number - FROM prover_jobs - JOIN node_aggregation_witness_jobs nawj ON prover_jobs.l1_batch_number = nawj.l1_batch_number - WHERE nawj.status = 'waiting_for_proofs' - AND prover_jobs.status = 'successful' - AND prover_jobs.aggregation_round = 1 - GROUP BY prover_jobs.l1_batch_number, nawj.number_of_leaf_circuits - HAVING COUNT(*) = nawj.number_of_leaf_circuits) - RETURNING l1_batch_number; - "#, - ) - .fetch_all(self.storage.conn()) - .await - .unwrap() - .into_iter() - .map(|row| row.l1_batch_number) - .collect() - } - } - - pub async fn move_scheduler_jobs_from_waiting_to_queued(&mut self) -> Vec { - { - // There is always just one final node circuit - // hence we do AND p.number_of_jobs = 1 - sqlx::query!( - r#" - UPDATE scheduler_witness_jobs - SET status='queued' - WHERE l1_batch_number IN - (SELECT prover_jobs.l1_batch_number - FROM prover_jobs - JOIN scheduler_witness_jobs swj ON prover_jobs.l1_batch_number = swj.l1_batch_number - WHERE swj.status = 'waiting_for_proofs' - AND prover_jobs.status = 'successful' - AND prover_jobs.aggregation_round = 2 - GROUP BY prover_jobs.l1_batch_number - HAVING COUNT(*) = 1) - RETURNING l1_batch_number; - "#, - ) - .fetch_all(self.storage.conn()) - .await - .unwrap() - .into_iter() - .map(|row| row.l1_batch_number) - .collect() - } - } - - pub async fn protocol_version_for_l1_batch( - &mut self, - l1_batch_number: L1BatchNumber, - ) -> Option { - sqlx::query!( - r#" - SELECT protocol_version - FROM witness_inputs - WHERE l1_batch_number = $1 - "#, - l1_batch_number.0 as i64, - ) - .fetch_one(self.storage.conn()) - .await - .unwrap() - .protocol_version - } -} - -pub struct GetWitnessJobsParams { - pub blocks: Option>, -} diff --git a/core/lib/zksync_core/src/api_server/tree/tests.rs b/core/lib/zksync_core/src/api_server/tree/tests.rs index 11161805633e..314333d8a4a1 100644 --- a/core/lib/zksync_core/src/api_server/tree/tests.rs +++ b/core/lib/zksync_core/src/api_server/tree/tests.rs @@ -13,7 +13,6 @@ use crate::metadata_calculator::tests::{ #[tokio::test] async fn merkle_tree_api() { let pool = ConnectionPool::test_pool().await; - let prover_pool = ConnectionPool::test_pool().await; let temp_dir = TempDir::new().expect("failed get temporary directory for RocksDB"); let (calculator, _) = setup_calculator(temp_dir.path(), &pool).await; let api_addr = (Ipv4Addr::LOCALHOST, 0).into(); @@ -28,7 +27,7 @@ async fn merkle_tree_api() { reset_db_state(&pool, 5).await; // Wait until the calculator processes initial L1 batches. - run_calculator(calculator, pool, prover_pool).await; + run_calculator(calculator, pool).await; // Query the API. let tree_info = api_client.get_info().await.unwrap(); diff --git a/core/lib/zksync_core/src/basic_witness_input_producer/mod.rs b/core/lib/zksync_core/src/basic_witness_input_producer/mod.rs index d9295b413fc8..0367830f9c6f 100644 --- a/core/lib/zksync_core/src/basic_witness_input_producer/mod.rs +++ b/core/lib/zksync_core/src/basic_witness_input_producer/mod.rs @@ -190,10 +190,6 @@ impl JobProcessor for BasicWitnessInputProducer { .mark_job_as_successful(job_id, started_at, &object_path) .await .context("failed to mark job as successful for BasicWitnessInputProducer")?; - transaction - .witness_generator_dal() - .mark_witness_inputs_job_as_queued(job_id) - .await; transaction .commit() .await diff --git a/core/lib/zksync_core/src/house_keeper/mod.rs b/core/lib/zksync_core/src/house_keeper/mod.rs index b14a089f9117..b4a5a3047321 100644 --- a/core/lib/zksync_core/src/house_keeper/mod.rs +++ b/core/lib/zksync_core/src/house_keeper/mod.rs @@ -10,5 +10,3 @@ pub mod gpu_prover_queue_monitor; pub mod prover_job_retry_manager; pub mod prover_queue_monitor; pub mod waiting_to_queued_fri_witness_job_mover; -pub mod waiting_to_queued_witness_job_mover; -pub mod witness_generator_queue_monitor; diff --git a/core/lib/zksync_core/src/lib.rs b/core/lib/zksync_core/src/lib.rs index 5406f0bbd897..f9192bf295cb 100644 --- a/core/lib/zksync_core/src/lib.rs +++ b/core/lib/zksync_core/src/lib.rs @@ -35,12 +35,10 @@ use zksync_prover_utils::periodic_job::PeriodicJob; use zksync_queued_job_processor::JobProcessor; use zksync_state::PostgresStorageCaches; use zksync_types::{ - proofs::AggregationRound, protocol_version::{L1VerifierConfig, VerifierParams}, system_contracts::get_system_smart_contracts, L2ChainId, PackedEthSignature, ProtocolVersionId, }; -use zksync_verification_key_server::get_cached_commitments; use crate::{ api_server::{ @@ -67,8 +65,6 @@ use crate::{ gpu_prover_queue_monitor::GpuProverQueueMonitor, prover_job_retry_manager::ProverJobRetryManager, prover_queue_monitor::ProverStatsReporter, waiting_to_queued_fri_witness_job_mover::WaitingToQueuedFriWitnessJobMover, - waiting_to_queued_witness_job_mover::WaitingToQueuedWitnessJobMover, - witness_generator_queue_monitor::WitnessGeneratorStatsReporter, }, l1_gas_price::{GasAdjusterSingleton, L1GasPriceProvider}, metadata_calculator::{ @@ -76,10 +72,6 @@ use crate::{ }, metrics::{InitStage, APP_METRICS}, state_keeper::{create_state_keeper, MempoolFetcher, MempoolGuard, MiniblockSealer}, - witness_generator::{ - basic_circuits::BasicWitnessGenerator, leaf_aggregation::LeafAggregationWitnessGenerator, - node_aggregation::NodeAggregationWitnessGenerator, scheduler::SchedulerWitnessGenerator, - }, }; pub mod api_server; @@ -101,7 +93,6 @@ pub mod reorg_detector; pub mod state_keeper; pub mod sync_layer; pub mod temp_config_store; -pub mod witness_generator; /// Inserts the initial information about zkSync tokens into the database. pub async fn genesis_init( @@ -245,9 +236,6 @@ pub enum Component { /// Produces input for basic witness generator and uploads it as bin encoded file (blob) to GCS. /// The blob is later used as input for Basic Witness Generators. BasicWitnessInputProducer, - /// Witness Generator. The first argument is a number of jobs to process. If None, runs indefinitely. - /// The second argument is the type of the witness-generation performed - WitnessGenerator(Option, AggregationRound), /// Component for housekeeping task such as cleaning blobs from GCS, reporting metrics etc. Housekeeper, /// Component for exposing APIs to prover for providing proof generation data and accepting proofs. @@ -283,38 +271,6 @@ impl FromStr for Components { "basic_witness_input_producer" => { Ok(Components(vec![Component::BasicWitnessInputProducer])) } - "witness_generator" => Ok(Components(vec![ - Component::WitnessGenerator(None, AggregationRound::BasicCircuits), - Component::WitnessGenerator(None, AggregationRound::LeafAggregation), - Component::WitnessGenerator(None, AggregationRound::NodeAggregation), - Component::WitnessGenerator(None, AggregationRound::Scheduler), - ])), - "one_shot_witness_generator" => Ok(Components(vec![ - Component::WitnessGenerator(Some(1), AggregationRound::BasicCircuits), - Component::WitnessGenerator(Some(1), AggregationRound::LeafAggregation), - Component::WitnessGenerator(Some(1), AggregationRound::NodeAggregation), - Component::WitnessGenerator(Some(1), AggregationRound::Scheduler), - ])), - "one_shot_basic_witness_generator" => { - Ok(Components(vec![Component::WitnessGenerator( - Some(1), - AggregationRound::BasicCircuits, - )])) - } - "one_shot_leaf_witness_generator" => Ok(Components(vec![Component::WitnessGenerator( - Some(1), - AggregationRound::LeafAggregation, - )])), - "one_shot_node_witness_generator" => Ok(Components(vec![Component::WitnessGenerator( - Some(1), - AggregationRound::NodeAggregation, - )])), - "one_shot_scheduler_witness_generator" => { - Ok(Components(vec![Component::WitnessGenerator( - Some(1), - AggregationRound::Scheduler, - )])) - } "eth" => Ok(Components(vec![ Component::EthWatcher, Component::EthTxAggregator, @@ -332,7 +288,6 @@ impl FromStr for Components { pub async fn initialize_components( configs: &TempConfigStore, components: Vec, - use_prometheus_push_gateway: bool, ) -> anyhow::Result<( Vec>>, watch::Sender, @@ -350,10 +305,6 @@ pub async fn initialize_components( .build() .await .context("failed to build connection_pool")?; - let prover_connection_pool = ConnectionPool::builder(postgres_config.prover_url()?, pool_size) - .build() - .await - .context("failed to build prover_connection_pool")?; let replica_connection_pool = ConnectionPool::builder(postgres_config.replica_url()?, pool_size) .set_statement_timeout(statement_timeout) @@ -398,11 +349,7 @@ pub async fn initialize_components( .prometheus_config .clone() .context("prometheus_config")?; - let prom_config = if use_prometheus_push_gateway { - PrometheusExporterConfig::push(prom_config.gateway_endpoint(), prom_config.push_interval()) - } else { - PrometheusExporterConfig::pull(prom_config.listener_port) - }; + let prom_config = PrometheusExporterConfig::pull(prom_config.listener_port); let (prometheus_health_check, prometheus_health_updater) = ReactiveHealthCheck::new("prometheus_exporter"); @@ -703,17 +650,6 @@ pub async fn initialize_components( ) .await .context("add_trees_to_task_futures()")?; - add_witness_generator_to_task_futures( - configs, - &mut task_futures, - &components, - &connection_pool, - &prover_connection_pool, - &store_factory, - &stop_receiver, - ) - .await - .context("add_witness_generator_to_task_futures()")?; if components.contains(&Component::BasicWitnessInputProducer) { let singleton_connection_pool = ConnectionPool::singleton(postgres_config.master_url()?) @@ -944,11 +880,7 @@ async fn run_tree( .build() .await .context("failed to build connection pool")?; - let prover_pool = ConnectionPool::singleton(postgres_config.prover_url()?) - .build() - .await - .context("failed to build prover_pool")?; - let tree_task = tokio::spawn(metadata_calculator.run(pool, prover_pool, stop_receiver)); + let tree_task = tokio::spawn(metadata_calculator.run(pool, stop_receiver)); task_futures.push(tree_task); let elapsed = started_at.elapsed(); @@ -983,101 +915,6 @@ async fn add_basic_witness_input_producer_to_task_futures( Ok(()) } -async fn add_witness_generator_to_task_futures( - configs: &TempConfigStore, - task_futures: &mut Vec>>, - components: &[Component], - connection_pool: &ConnectionPool, - prover_connection_pool: &ConnectionPool, - store_factory: &ObjectStoreFactory, - stop_receiver: &watch::Receiver, -) -> anyhow::Result<()> { - // We don't want witness generator to run on local nodes, as it's CPU heavy. - if std::env::var("ZKSYNC_LOCAL_SETUP") == Ok("true".to_owned()) { - return Ok(()); - } - - let generator_params = components.iter().filter_map(|component| { - if let Component::WitnessGenerator(batch_size, component_type) = component { - Some((*batch_size, *component_type)) - } else { - None - } - }); - - for (batch_size, component_type) in generator_params { - let started_at = Instant::now(); - tracing::info!( - "initializing the {component_type:?} witness generator, batch size: {batch_size:?}" - ); - - let vk_commitments = get_cached_commitments(); - let protocol_versions = prover_connection_pool - .access_storage() - .await - .unwrap() - .protocol_versions_dal() - .protocol_version_for(&vk_commitments) - .await; - let config = configs - .witness_generator_config - .clone() - .context("witness_generator_config")?; - let task = match component_type { - AggregationRound::BasicCircuits => { - let witness_generator = BasicWitnessGenerator::new( - config, - store_factory, - protocol_versions.clone(), - connection_pool.clone(), - prover_connection_pool.clone(), - ) - .await; - tokio::spawn(witness_generator.run(stop_receiver.clone(), batch_size)) - } - AggregationRound::LeafAggregation => { - let witness_generator = LeafAggregationWitnessGenerator::new( - config, - store_factory, - protocol_versions.clone(), - connection_pool.clone(), - prover_connection_pool.clone(), - ) - .await; - tokio::spawn(witness_generator.run(stop_receiver.clone(), batch_size)) - } - AggregationRound::NodeAggregation => { - let witness_generator = NodeAggregationWitnessGenerator::new( - config, - store_factory, - protocol_versions.clone(), - connection_pool.clone(), - prover_connection_pool.clone(), - ) - .await; - tokio::spawn(witness_generator.run(stop_receiver.clone(), batch_size)) - } - AggregationRound::Scheduler => { - let witness_generator = SchedulerWitnessGenerator::new( - config, - store_factory, - protocol_versions.clone(), - connection_pool.clone(), - prover_connection_pool.clone(), - ) - .await; - tokio::spawn(witness_generator.run(stop_receiver.clone(), batch_size)) - } - }; - task_futures.push(task); - - let elapsed = started_at.elapsed(); - APP_METRICS.init_latency[&InitStage::WitnessGenerator(component_type)].set(elapsed); - tracing::info!("initialized {component_type:?} witness generator in {elapsed:?}"); - } - Ok(()) -} - async fn add_house_keeper_to_task_futures( configs: &TempConfigStore, task_futures: &mut Vec>>, @@ -1128,20 +965,9 @@ async fn add_house_keeper_to_task_futures( prover_connection_pool.clone(), prover_group_config.clone(), ); - let waiting_to_queued_witness_job_mover = WaitingToQueuedWitnessJobMover::new( - house_keeper_config.witness_job_moving_interval_ms, - prover_connection_pool.clone(), - ); - let witness_generator_stats_reporter = WitnessGeneratorStatsReporter::new( - house_keeper_config.witness_generator_stats_reporting_interval_ms, - prover_connection_pool.clone(), - ); - - task_futures.push(tokio::spawn(witness_generator_stats_reporter.run())); task_futures.push(tokio::spawn(gpu_prover_queue.run())); task_futures.push(tokio::spawn(l1_batch_metrics_reporter.run())); task_futures.push(tokio::spawn(prover_stats_reporter.run())); - task_futures.push(tokio::spawn(waiting_to_queued_witness_job_mover.run())); task_futures.push(tokio::spawn(prover_job_retry_manager.run())); // All FRI Prover related components are configured below. diff --git a/core/lib/zksync_core/src/metadata_calculator/mod.rs b/core/lib/zksync_core/src/metadata_calculator/mod.rs index 31b39a909527..4d3777f69f9a 100644 --- a/core/lib/zksync_core/src/metadata_calculator/mod.rs +++ b/core/lib/zksync_core/src/metadata_calculator/mod.rs @@ -140,17 +140,10 @@ impl MetadataCalculator { pub async fn run( self, pool: ConnectionPool, - prover_pool: ConnectionPool, stop_receiver: watch::Receiver, ) -> anyhow::Result<()> { self.updater - .loop_updating_tree( - self.delayer, - &pool, - &prover_pool, - stop_receiver, - self.health_updater, - ) + .loop_updating_tree(self.delayer, &pool, stop_receiver, self.health_updater) .await } diff --git a/core/lib/zksync_core/src/metadata_calculator/tests.rs b/core/lib/zksync_core/src/metadata_calculator/tests.rs index 85d179fe3b05..bacf9f99a6d3 100644 --- a/core/lib/zksync_core/src/metadata_calculator/tests.rs +++ b/core/lib/zksync_core/src/metadata_calculator/tests.rs @@ -39,11 +39,10 @@ where #[tokio::test] async fn genesis_creation() { let pool = ConnectionPool::test_pool().await; - let prover_pool = ConnectionPool::test_pool().await; let temp_dir = TempDir::new().expect("failed get temporary directory for RocksDB"); let (calculator, _) = setup_calculator(temp_dir.path(), &pool).await; - run_calculator(calculator, pool.clone(), prover_pool).await; + run_calculator(calculator, pool.clone()).await; let (calculator, _) = setup_calculator(temp_dir.path(), &pool).await; assert_eq!( calculator.updater.tree().next_l1_batch_number(), @@ -56,13 +55,12 @@ async fn genesis_creation() { #[tokio::test] async fn basic_workflow() { let pool = ConnectionPool::test_pool().await; - let prover_pool = ConnectionPool::test_pool().await; let temp_dir = TempDir::new().expect("failed get temporary directory for RocksDB"); let (calculator, object_store) = setup_calculator(temp_dir.path(), &pool).await; reset_db_state(&pool, 1).await; - let merkle_tree_hash = run_calculator(calculator, pool.clone(), prover_pool).await; + let merkle_tree_hash = run_calculator(calculator, pool.clone()).await; // Check the hash against the reference. let expected_tree_hash = expected_tree_hash(&pool).await; @@ -102,7 +100,6 @@ async fn expected_tree_hash(pool: &ConnectionPool) -> H256 { #[tokio::test] async fn status_receiver_has_correct_states() { let pool = ConnectionPool::test_pool().await; - let prover_pool = ConnectionPool::test_pool().await; let temp_dir = TempDir::new().expect("failed get temporary directory for RocksDB"); let (mut calculator, _) = setup_calculator(temp_dir.path(), &pool).await; @@ -121,7 +118,7 @@ async fn status_receiver_has_correct_states() { let (delay_sx, mut delay_rx) = mpsc::unbounded_channel(); calculator.delayer.delay_notifier = delay_sx; - let calculator_handle = tokio::spawn(calculator.run(pool, prover_pool, stop_rx)); + let calculator_handle = tokio::spawn(calculator.run(pool, stop_rx)); delay_rx.recv().await.unwrap(); assert_eq!( tree_health_check.check_health().await.status(), @@ -151,19 +148,18 @@ async fn status_receiver_has_correct_states() { #[tokio::test] async fn multi_l1_batch_workflow() { let pool = ConnectionPool::test_pool().await; - let prover_pool = ConnectionPool::test_pool().await; // Collect all storage logs in a single L1 batch let temp_dir = TempDir::new().expect("failed get temporary directory for RocksDB"); let (calculator, _) = setup_calculator(temp_dir.path(), &pool).await; reset_db_state(&pool, 1).await; - let root_hash = run_calculator(calculator, pool.clone(), prover_pool.clone()).await; + let root_hash = run_calculator(calculator, pool.clone()).await; // Collect the same logs in multiple L1 batches let temp_dir = TempDir::new().expect("failed get temporary directory for RocksDB"); let (calculator, object_store) = setup_calculator(temp_dir.path(), &pool).await; reset_db_state(&pool, 10).await; - let multi_block_root_hash = run_calculator(calculator, pool, prover_pool).await; + let multi_block_root_hash = run_calculator(calculator, pool).await; assert_eq!(multi_block_root_hash, root_hash); let mut prev_index = None; @@ -188,20 +184,18 @@ async fn multi_l1_batch_workflow() { #[tokio::test] async fn running_metadata_calculator_with_additional_blocks() { let pool = ConnectionPool::test_pool().await; - let prover_pool = ConnectionPool::test_pool().await; let temp_dir = TempDir::new().expect("failed get temporary directory for RocksDB"); let calculator = setup_lightweight_calculator(temp_dir.path(), &pool).await; reset_db_state(&pool, 5).await; - run_calculator(calculator, pool.clone(), prover_pool.clone()).await; + run_calculator(calculator, pool.clone()).await; let mut calculator = setup_lightweight_calculator(temp_dir.path(), &pool).await; let (stop_sx, stop_rx) = watch::channel(false); let (delay_sx, mut delay_rx) = mpsc::unbounded_channel(); calculator.delayer.delay_notifier = delay_sx; - let calculator_handle = - tokio::spawn(calculator.run(pool.clone(), prover_pool.clone(), stop_rx)); + let calculator_handle = tokio::spawn(calculator.run(pool.clone(), stop_rx)); // Wait until the calculator has processed initial L1 batches. let (next_l1_batch, _) = tokio::time::timeout(RUN_TIMEOUT, delay_rx.recv()) .await @@ -233,14 +227,13 @@ async fn running_metadata_calculator_with_additional_blocks() { // Switch to the full tree. It should pick up from the same spot and result in the same tree root hash. let (calculator, _) = setup_calculator(temp_dir.path(), &pool).await; - let root_hash_for_full_tree = run_calculator(calculator, pool, prover_pool).await; + let root_hash_for_full_tree = run_calculator(calculator, pool).await; assert_eq!(root_hash_for_full_tree, updated_root_hash); } #[tokio::test] async fn shutting_down_calculator() { let pool = ConnectionPool::test_pool().await; - let prover_pool = ConnectionPool::test_pool().await; let temp_dir = TempDir::new().expect("failed get temporary directory for RocksDB"); let (merkle_tree_config, mut operation_config) = create_config(temp_dir.path()); operation_config.delay_interval = 30_000; // ms; chosen to be larger than `RUN_TIMEOUT` @@ -256,7 +249,7 @@ async fn shutting_down_calculator() { reset_db_state(&pool, 5).await; let (stop_sx, stop_rx) = watch::channel(false); - let calculator_task = tokio::spawn(calculator.run(pool, prover_pool, stop_rx)); + let calculator_task = tokio::spawn(calculator.run(pool, stop_rx)); tokio::time::sleep(Duration::from_millis(100)).await; stop_sx.send_replace(true); run_with_timeout(RUN_TIMEOUT, calculator_task) @@ -270,11 +263,10 @@ async fn test_postgres_backup_recovery( insert_batch_without_metadata: bool, ) { let pool = ConnectionPool::test_pool().await; - let prover_pool = ConnectionPool::test_pool().await; let temp_dir = TempDir::new().expect("failed get temporary directory for RocksDB"); let calculator = setup_lightweight_calculator(temp_dir.path(), &pool).await; reset_db_state(&pool, 5).await; - run_calculator(calculator, pool.clone(), prover_pool.clone()).await; + run_calculator(calculator, pool.clone()).await; // Simulate recovery from a DB snapshot in which some newer L1 batches are erased. let last_batch_after_recovery = L1BatchNumber(3); @@ -308,7 +300,7 @@ async fn test_postgres_backup_recovery( let (delay_sx, mut delay_rx) = mpsc::unbounded_channel(); calculator.delayer.delay_notifier = delay_sx; - let calculator_handle = tokio::spawn(calculator.run(pool.clone(), prover_pool, stop_rx)); + let calculator_handle = tokio::spawn(calculator.run(pool.clone(), stop_rx)); // Wait until the calculator has processed initial L1 batches. let (next_l1_batch, _) = tokio::time::timeout(RUN_TIMEOUT, delay_rx.recv()) .await @@ -423,7 +415,6 @@ fn path_to_string(path: &Path) -> String { pub(crate) async fn run_calculator( mut calculator: MetadataCalculator, pool: ConnectionPool, - prover_pool: ConnectionPool, ) -> H256 { let (stop_sx, stop_rx) = watch::channel(false); let (delay_sx, mut delay_rx) = mpsc::unbounded_channel(); @@ -439,7 +430,7 @@ pub(crate) async fn run_calculator( root_hash }); - run_with_timeout(RUN_TIMEOUT, calculator.run(pool, prover_pool, stop_rx)) + run_with_timeout(RUN_TIMEOUT, calculator.run(pool, stop_rx)) .await .unwrap(); delayer_handle.await.unwrap() diff --git a/core/lib/zksync_core/src/metadata_calculator/updater.rs b/core/lib/zksync_core/src/metadata_calculator/updater.rs index 87127947fbd2..1a75a80cd6f2 100644 --- a/core/lib/zksync_core/src/metadata_calculator/updater.rs +++ b/core/lib/zksync_core/src/metadata_calculator/updater.rs @@ -103,7 +103,6 @@ impl TreeUpdater { async fn process_multiple_batches( &mut self, storage: &mut StorageProcessor<'_>, - prover_storage: &mut StorageProcessor<'_>, l1_batch_numbers: ops::RangeInclusive, ) -> L1BatchNumber { let start = Instant::now(); @@ -184,32 +183,6 @@ impl TreeUpdater { // right away without having to implement dedicated code. if let Some(object_key) = &object_key { - let protocol_version_id = storage - .blocks_dal() - .get_batch_protocol_version_id(l1_batch_number) - .await - .unwrap(); - if let Some(id) = protocol_version_id { - if !prover_storage - .protocol_versions_dal() - .prover_protocol_version_exists(id) - .await - { - let protocol_version = storage - .protocol_versions_dal() - .get_protocol_version(id) - .await - .unwrap(); - prover_storage - .protocol_versions_dal() - .save_prover_protocol_version(protocol_version) - .await; - } - } - prover_storage - .witness_generator_dal() - .save_witness_inputs(l1_batch_number, object_key, protocol_version_id) - .await; storage .basic_witness_input_producer_dal() .create_basic_witness_input_producer_job(l1_batch_number) @@ -282,7 +255,6 @@ impl TreeUpdater { async fn step( &mut self, mut storage: StorageProcessor<'_>, - mut prover_storage: StorageProcessor<'_>, next_l1_batch_to_seal: &mut L1BatchNumber, ) { let last_sealed_l1_batch = storage @@ -301,7 +273,7 @@ impl TreeUpdater { } else { tracing::info!("Updating Merkle tree with L1 batches #{l1_batch_numbers:?}"); *next_l1_batch_to_seal = self - .process_multiple_batches(&mut storage, &mut prover_storage, l1_batch_numbers) + .process_multiple_batches(&mut storage, l1_batch_numbers) .await; } } @@ -311,7 +283,6 @@ impl TreeUpdater { mut self, delayer: Delayer, pool: &ConnectionPool, - prover_pool: &ConnectionPool, mut stop_receiver: watch::Receiver, health_updater: HealthUpdater, ) -> anyhow::Result<()> { @@ -386,14 +357,9 @@ impl TreeUpdater { .access_storage_tagged("metadata_calculator") .await .unwrap(); - let prover_storage = prover_pool - .access_storage_tagged("metadata_calculator") - .await - .unwrap(); let snapshot = *next_l1_batch_to_seal; - self.step(storage, prover_storage, &mut next_l1_batch_to_seal) - .await; + self.step(storage, &mut next_l1_batch_to_seal).await; let delay = if snapshot == *next_l1_batch_to_seal { tracing::trace!( "Metadata calculator (next L1 batch: #{next_l1_batch_to_seal}) \ diff --git a/core/lib/zksync_core/src/metrics.rs b/core/lib/zksync_core/src/metrics.rs index 0206c2647591..8a6fd30a8197 100644 --- a/core/lib/zksync_core/src/metrics.rs +++ b/core/lib/zksync_core/src/metrics.rs @@ -4,7 +4,7 @@ use std::{fmt, time::Duration}; use vise::{Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Gauge, Histogram, Metrics}; use zksync_dal::transactions_dal::L2TxSubmissionResult; -use zksync_types::{aggregated_operations::AggregatedActionType, proofs::AggregationRound}; +use zksync_types::aggregated_operations::AggregatedActionType; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelValue, EncodeLabelSet)] #[metrics(label = "stage")] @@ -18,7 +18,6 @@ pub(crate) enum InitStage { EthTxManager, DataFetcher, Tree, - WitnessGenerator(AggregationRound), BasicWitnessInputProducer, } @@ -34,7 +33,6 @@ impl fmt::Display for InitStage { Self::EthTxManager => formatter.write_str("eth_tx_manager"), Self::DataFetcher => formatter.write_str("data_fetchers"), Self::Tree => formatter.write_str("tree"), - Self::WitnessGenerator(round) => write!(formatter, "witness_generator_{round:?}"), Self::BasicWitnessInputProducer => formatter.write_str("basic_witness_input_producer"), } } diff --git a/core/lib/zksync_core/src/witness_generator/basic_circuits.rs b/core/lib/zksync_core/src/witness_generator/basic_circuits.rs deleted file mode 100644 index e4d8b01357db..000000000000 --- a/core/lib/zksync_core/src/witness_generator/basic_circuits.rs +++ /dev/null @@ -1,646 +0,0 @@ -use std::{ - collections::{hash_map::DefaultHasher, HashMap, HashSet}, - hash::{Hash, Hasher}, - sync::Arc, - time::Instant, -}; - -use async_trait::async_trait; -use multivm::vm_latest::{ - constants::MAX_CYCLES_FOR_TX, HistoryDisabled, SimpleMemory, StorageOracle as VmStorageOracle, -}; -use rand::Rng; -use serde::{Deserialize, Serialize}; -use zksync_config::configs::{ - witness_generator::BasicWitnessGeneratorDataSource, WitnessGeneratorConfig, -}; -use zksync_dal::ConnectionPool; -use zksync_object_store::{Bucket, ObjectStore, ObjectStoreFactory, StoredObject}; -use zksync_queued_job_processor::JobProcessor; -use zksync_state::{PostgresStorage, ReadStorage, ShadowStorage, StorageView, WitnessStorage}; -use zksync_system_constants::BOOTLOADER_ADDRESS; -use zksync_types::{ - circuit::GEOMETRY_CONFIG, - proofs::{AggregationRound, BasicCircuitWitnessGeneratorInput, PrepareBasicCircuitsJob}, - zkevm_test_harness::{ - abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit, - bellman::bn256::Bn256, - toolset::GeometryConfig, - witness::{ - full_block_artifact::{BlockBasicCircuits, BlockBasicCircuitsPublicInputs}, - oracle::VmWitnessOracle, - }, - SchedulerCircuitInstanceWitness, - }, - Address, L1BatchNumber, ProtocolVersionId, H256, U256, USED_BOOTLOADER_MEMORY_BYTES, -}; -use zksync_utils::{bytes_to_chunks, expand_memory_contents, h256_to_u256, u256_to_h256}; - -use super::{ - precalculated_merkle_paths_provider::PrecalculatedMerklePathsProvider, - storage_oracle::StorageOracle, utils::save_prover_input_artifacts, METRICS, -}; - -pub struct BasicCircuitArtifacts { - basic_circuits: BlockBasicCircuits, - basic_circuits_inputs: BlockBasicCircuitsPublicInputs, - scheduler_witness: SchedulerCircuitInstanceWitness, - circuits: Vec>>, -} - -#[derive(Debug)] -struct BlobUrls { - basic_circuits_url: String, - basic_circuits_inputs_url: String, - scheduler_witness_url: String, - circuit_types_and_urls: Vec<(&'static str, String)>, -} - -#[derive(Clone)] -pub struct BasicWitnessGeneratorJob { - block_number: L1BatchNumber, - job: PrepareBasicCircuitsJob, -} - -#[derive(Debug)] -pub struct BasicWitnessGenerator { - config: WitnessGeneratorConfig, - object_store: Arc, - protocol_versions: Vec, - connection_pool: ConnectionPool, - prover_connection_pool: ConnectionPool, -} - -impl BasicWitnessGenerator { - pub async fn new( - config: WitnessGeneratorConfig, - store_factory: &ObjectStoreFactory, - protocol_versions: Vec, - connection_pool: ConnectionPool, - prover_connection_pool: ConnectionPool, - ) -> Self { - Self { - config, - object_store: store_factory.create_store().await.into(), - protocol_versions, - connection_pool, - prover_connection_pool, - } - } - - async fn process_job_impl( - config: WitnessGeneratorConfig, - object_store: Arc, - connection_pool: ConnectionPool, - prover_connection_pool: ConnectionPool, - basic_job: BasicWitnessGeneratorJob, - started_at: Instant, - ) -> anyhow::Result> { - let BasicWitnessGeneratorJob { block_number, job } = basic_job; - - if let Some(blocks_proving_percentage) = config.blocks_proving_percentage { - // Generate random number in (0; 100). - let threshold = rand::thread_rng().gen_range(1..100); - // We get value higher than `blocks_proving_percentage` with prob = `1 - blocks_proving_percentage`. - // In this case job should be skipped. - if threshold > blocks_proving_percentage { - METRICS.skipped_blocks.inc(); - tracing::info!( - "Skipping witness generation for block {}, blocks_proving_percentage: {}", - block_number.0, - blocks_proving_percentage - ); - let mut storage = connection_pool.access_storage().await.unwrap(); - storage - .blocks_dal() - .set_skip_proof_for_l1_batch(block_number) - .await - .unwrap(); - let mut prover_storage = prover_connection_pool.access_storage().await.unwrap(); - prover_storage - .witness_generator_dal() - .mark_witness_job_as_skipped(block_number, AggregationRound::BasicCircuits) - .await; - return Ok(None); - } - } - - METRICS.sampled_blocks.inc(); - tracing::info!( - "Starting witness generation of type {:?} for block {}", - AggregationRound::BasicCircuits, - block_number.0 - ); - - Ok(Some( - process_basic_circuits_job( - object_store, - config, - connection_pool, - started_at, - block_number, - job, - ) - .await, - )) - } -} - -#[async_trait] -impl JobProcessor for BasicWitnessGenerator { - type Job = BasicWitnessGeneratorJob; - type JobId = L1BatchNumber; - // The artifact is optional to support skipping blocks when sampling is enabled. - type JobArtifacts = Option; - - const SERVICE_NAME: &'static str = "basic_circuit_witness_generator"; - - async fn get_next_job(&self) -> anyhow::Result> { - let mut prover_connection = self.prover_connection_pool.access_storage().await.unwrap(); - let last_l1_batch_to_process = self.config.last_l1_batch_to_process(); - - Ok( - match prover_connection - .witness_generator_dal() - .get_next_basic_circuit_witness_job( - self.config.witness_generation_timeout(), - self.config.max_attempts, - last_l1_batch_to_process, - &self.protocol_versions, - ) - .await - { - Some(metadata) => { - let job = get_artifacts(metadata.block_number, &self.object_store).await; - Some((job.block_number, job)) - } - None => None, - }, - ) - } - - async fn save_failure(&self, job_id: L1BatchNumber, started_at: Instant, error: String) -> () { - let attempts = self - .prover_connection_pool - .access_storage() - .await - .unwrap() - .witness_generator_dal() - .mark_witness_job_as_failed( - AggregationRound::BasicCircuits, - job_id, - started_at.elapsed(), - error, - ) - .await; - - if attempts >= self.config.max_attempts { - self.connection_pool - .access_storage() - .await - .unwrap() - .blocks_dal() - .set_skip_proof_for_l1_batch(job_id) - .await - .unwrap(); - } - } - - #[allow(clippy::async_yields_async)] - async fn process_job( - &self, - job: BasicWitnessGeneratorJob, - started_at: Instant, - ) -> tokio::task::JoinHandle>> { - let object_store = Arc::clone(&self.object_store); - let config = self.config.clone(); - tokio::spawn(Self::process_job_impl( - config, - object_store, - self.connection_pool.clone(), - self.prover_connection_pool.clone(), - job, - started_at, - )) - } - - async fn save_result( - &self, - job_id: L1BatchNumber, - started_at: Instant, - optional_artifacts: Option, - ) -> anyhow::Result<()> { - match optional_artifacts { - None => (), - Some(artifacts) => { - let blob_urls = save_artifacts(job_id, artifacts, &self.object_store).await; - update_database(&self.prover_connection_pool, started_at, job_id, blob_urls).await; - } - } - Ok(()) - } - - fn max_attempts(&self) -> u32 { - self.config.max_attempts - } - - async fn get_job_attempts(&self, _job_id: &Self::JobId) -> anyhow::Result { - // Witness generator will be removed soon in favor of FRI one, so returning blank value. - Ok(1) - } -} - -pub async fn process_basic_circuits_job( - object_store: Arc, - config: WitnessGeneratorConfig, - connection_pool: ConnectionPool, - started_at: Instant, - block_number: L1BatchNumber, - job: PrepareBasicCircuitsJob, -) -> BasicCircuitArtifacts { - let witness_gen_input = - build_basic_circuits_witness_generator_input(connection_pool.clone(), job, block_number) - .await; - let (basic_circuits, basic_circuits_inputs, scheduler_witness) = - generate_witness(object_store, config, connection_pool, witness_gen_input).await; - let circuits = basic_circuits.clone().into_flattened_set(); - - tracing::info!( - "Witness generation for block {} is complete in {:?}. Number of circuits: {}", - block_number.0, - started_at.elapsed(), - circuits.len() - ); - - BasicCircuitArtifacts { - basic_circuits, - basic_circuits_inputs, - scheduler_witness, - circuits, - } -} - -async fn update_database( - prover_connection_pool: &ConnectionPool, - started_at: Instant, - block_number: L1BatchNumber, - blob_urls: BlobUrls, -) { - let mut prover_connection = prover_connection_pool.access_storage().await.unwrap(); - let mut transaction = prover_connection.start_transaction().await.unwrap(); - let protocol_version = transaction - .witness_generator_dal() - .protocol_version_for_l1_batch(block_number) - .await - .unwrap_or_else(|| { - panic!( - "No system version exist for l1 batch {} for basic circuits", - block_number.0 - ) - }); - transaction - .witness_generator_dal() - .create_aggregation_jobs( - block_number, - &blob_urls.basic_circuits_url, - &blob_urls.basic_circuits_inputs_url, - blob_urls.circuit_types_and_urls.len(), - &blob_urls.scheduler_witness_url, - protocol_version, - ) - .await; - transaction - .prover_dal() - .insert_prover_jobs( - block_number, - blob_urls.circuit_types_and_urls, - AggregationRound::BasicCircuits, - protocol_version, - ) - .await; - transaction - .witness_generator_dal() - .mark_witness_job_as_successful( - block_number, - AggregationRound::BasicCircuits, - started_at.elapsed(), - ) - .await; - - transaction.commit().await.unwrap(); - METRICS.processing_time[&AggregationRound::BasicCircuits.into()].observe(started_at.elapsed()); -} - -async fn get_artifacts( - block_number: L1BatchNumber, - object_store: &dyn ObjectStore, -) -> BasicWitnessGeneratorJob { - let job = object_store.get(block_number).await.unwrap(); - BasicWitnessGeneratorJob { block_number, job } -} - -async fn save_artifacts( - block_number: L1BatchNumber, - artifacts: BasicCircuitArtifacts, - object_store: &dyn ObjectStore, -) -> BlobUrls { - let basic_circuits_url = object_store - .put(block_number, &artifacts.basic_circuits) - .await - .unwrap(); - let basic_circuits_inputs_url = object_store - .put(block_number, &artifacts.basic_circuits_inputs) - .await - .unwrap(); - let scheduler_witness_url = object_store - .put(block_number, &artifacts.scheduler_witness) - .await - .unwrap(); - let circuit_types_and_urls = save_prover_input_artifacts( - block_number, - &artifacts.circuits, - object_store, - AggregationRound::BasicCircuits, - ) - .await; - BlobUrls { - basic_circuits_url, - basic_circuits_inputs_url, - scheduler_witness_url, - circuit_types_and_urls, - } -} - -// If making changes to this method, consider moving this logic to the DAL layer and make -// `PrepareBasicCircuitsJob` have all fields of `BasicCircuitWitnessGeneratorInput`. -pub async fn build_basic_circuits_witness_generator_input( - connection_pool: ConnectionPool, - witness_merkle_input: PrepareBasicCircuitsJob, - l1_batch_number: L1BatchNumber, -) -> BasicCircuitWitnessGeneratorInput { - let mut connection = connection_pool.access_storage().await.unwrap(); - let block_header = connection - .blocks_dal() - .get_l1_batch_header(l1_batch_number) - .await - .unwrap() - .unwrap(); - let initial_heap_content = connection - .blocks_dal() - .get_initial_bootloader_heap(l1_batch_number) - .await - .unwrap() - .unwrap(); - let (previous_block_hash, previous_block_timestamp) = connection - .blocks_dal() - .get_l1_batch_state_root_and_timestamp(l1_batch_number - 1) - .await - .unwrap() - .expect("cannot generate witness before the root hash is computed"); - BasicCircuitWitnessGeneratorInput { - block_number: l1_batch_number, - previous_block_timestamp, - previous_block_hash, - block_timestamp: block_header.timestamp, - used_bytecodes_hashes: block_header.used_contract_hashes, - initial_heap_content, - merkle_paths_input: witness_merkle_input, - } -} - -pub async fn generate_witness( - object_store: Arc, - config: WitnessGeneratorConfig, - connection_pool: ConnectionPool, - input: BasicCircuitWitnessGeneratorInput, -) -> ( - BlockBasicCircuits, - BlockBasicCircuitsPublicInputs, - SchedulerCircuitInstanceWitness, -) { - let mut connection = connection_pool.access_storage().await.unwrap(); - let header = connection - .blocks_dal() - .get_l1_batch_header(input.block_number) - .await - .unwrap() - .unwrap(); - let bootloader_code_bytes = connection - .storage_dal() - .get_factory_dep(header.base_system_contracts_hashes.bootloader) - .await - .expect("Bootloader bytecode should exist"); - let bootloader_code = bytes_to_chunks(&bootloader_code_bytes); - let account_bytecode_bytes = connection - .storage_dal() - .get_factory_dep(header.base_system_contracts_hashes.default_aa) - .await - .expect("Default aa bytecode should exist"); - let account_bytecode = bytes_to_chunks(&account_bytecode_bytes); - let bootloader_contents = - expand_memory_contents(&input.initial_heap_content, USED_BOOTLOADER_MEMORY_BYTES); - let account_code_hash = h256_to_u256(header.base_system_contracts_hashes.default_aa); - - let hashes: HashSet = input - .used_bytecodes_hashes - .iter() - // SMA-1555: remove this hack once updated to the latest version of zkevm_test_harness - .filter(|&&hash| hash != h256_to_u256(header.base_system_contracts_hashes.bootloader)) - .map(|hash| u256_to_h256(*hash)) - .collect(); - - let mut used_bytecodes = connection.storage_dal().get_factory_deps(&hashes).await; - if input.used_bytecodes_hashes.contains(&account_code_hash) { - used_bytecodes.insert(account_code_hash, account_bytecode); - } - - assert_eq!( - hashes.len(), - used_bytecodes.len(), - "{} factory deps are not found in DB", - hashes.len() - used_bytecodes.len() - ); - - // `DbStorageProvider` was designed to be used in API, so it accepts miniblock numbers. - // Probably, we should make it work with L1 batch numbers too. - let (_, last_miniblock_number) = connection - .blocks_dal() - .get_miniblock_range_of_l1_batch(input.block_number - 1) - .await - .unwrap() - .expect("L1 batch should contain at least one miniblock"); - let storage_refunds = connection - .blocks_dal() - .get_storage_refunds(input.block_number) - .await - .unwrap() - .unwrap(); - - drop(connection); - let rt_handle = tokio::runtime::Handle::current(); - - // The following part is CPU-heavy, so we move it to a separate thread. - tokio::task::spawn_blocking(move || { - // NOTE: this `match` will be moved higher up, as we need to load EVERYTHING from Blob, not just storage - // Until we can derive Storage from Merkle Paths, we'll have this version as testing ground. - let storage: Box = match config.data_source { - BasicWitnessGeneratorDataSource::FromPostgres => { - let connection = rt_handle - .block_on(connection_pool.access_storage()) - .unwrap(); - Box::new(PostgresStorage::new( - rt_handle.clone(), - connection, - last_miniblock_number, - true, - )) - } - BasicWitnessGeneratorDataSource::FromPostgresShadowBlob => { - let connection = rt_handle - .block_on(connection_pool.access_storage()) - .unwrap(); - let block_state = rt_handle.block_on(object_store.get(header.number)).unwrap(); - let source_storage = Box::new(PostgresStorage::new( - rt_handle.clone(), - connection, - last_miniblock_number, - true, - )); - let checked_storage = Box::new(WitnessStorage::new(block_state)); - Box::new(ShadowStorage::new( - source_storage, - checked_storage, - input.block_number, - )) - } - BasicWitnessGeneratorDataSource::FromBlob => { - let block_state = rt_handle.block_on(object_store.get(header.number)).unwrap(); - Box::new(WitnessStorage::new(block_state)) - } - }; - let mut tree = PrecalculatedMerklePathsProvider::new( - input.merkle_paths_input, - input.previous_block_hash.0, - ); - - let storage_view = StorageView::new(storage); - let storage_view = storage_view.to_rc_ptr(); - let vm_storage_oracle: VmStorageOracle>, HistoryDisabled> = - VmStorageOracle::new(storage_view); - let storage_oracle = StorageOracle::new(vm_storage_oracle, storage_refunds); - let memory: SimpleMemory = SimpleMemory::default(); - let mut hasher = DefaultHasher::new(); - GEOMETRY_CONFIG.hash(&mut hasher); - tracing::info!( - "generating witness for block {} using geometry config hash: {}", - input.block_number.0, - hasher.finish() - ); - - if config - .dump_arguments_for_blocks - .contains(&input.block_number.0) - { - rt_handle.block_on(save_run_with_fixed_params_args_to_gcs( - object_store, - input.block_number.0, - last_miniblock_number.0, - Address::zero(), - BOOTLOADER_ADDRESS, - bootloader_code.clone(), - bootloader_contents.clone(), - false, - account_code_hash, - used_bytecodes.clone(), - Vec::default(), - MAX_CYCLES_FOR_TX as usize, - GEOMETRY_CONFIG, - tree.clone(), - )); - } - - zksync_types::zkevm_test_harness::external_calls::run_with_fixed_params( - Address::zero(), - BOOTLOADER_ADDRESS, - bootloader_code, - bootloader_contents, - false, - account_code_hash, - used_bytecodes, - Vec::default(), - MAX_CYCLES_FOR_TX as usize, - GEOMETRY_CONFIG, - storage_oracle, - memory, - &mut tree, - ) - }) - .await - .unwrap() -} - -#[allow(clippy::too_many_arguments)] -async fn save_run_with_fixed_params_args_to_gcs( - object_store: Arc, - l1_batch_number: u32, - last_miniblock_number: u32, - caller: Address, - entry_point_address: Address, - entry_point_code: Vec<[u8; 32]>, - initial_heap_content: Vec, - zk_porter_is_available: bool, - default_aa_code_hash: U256, - used_bytecodes: HashMap>, - ram_verification_queries: Vec<(u32, U256)>, - cycle_limit: usize, - geometry: GeometryConfig, - tree: PrecalculatedMerklePathsProvider, -) { - let run_with_fixed_params_input = RunWithFixedParamsInput { - l1_batch_number, - last_miniblock_number, - caller, - entry_point_address, - entry_point_code, - initial_heap_content, - zk_porter_is_available, - default_aa_code_hash, - used_bytecodes, - ram_verification_queries, - cycle_limit, - geometry, - tree, - }; - object_store - .put(L1BatchNumber(l1_batch_number), &run_with_fixed_params_input) - .await - .unwrap(); -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub struct RunWithFixedParamsInput { - pub l1_batch_number: u32, - pub last_miniblock_number: u32, - pub caller: Address, - pub entry_point_address: Address, - pub entry_point_code: Vec<[u8; 32]>, - pub initial_heap_content: Vec, - pub zk_porter_is_available: bool, - pub default_aa_code_hash: U256, - pub used_bytecodes: HashMap>, - pub ram_verification_queries: Vec<(u32, U256)>, - pub cycle_limit: usize, - pub geometry: GeometryConfig, - pub tree: PrecalculatedMerklePathsProvider, -} - -impl StoredObject for RunWithFixedParamsInput { - const BUCKET: Bucket = Bucket::WitnessInput; - type Key<'a> = L1BatchNumber; - - fn encode_key(key: Self::Key<'_>) -> String { - format!("run_with_fixed_params_input_{}.bin", key) - } - - zksync_object_store::serialize_using_bincode!(); -} diff --git a/core/lib/zksync_core/src/witness_generator/leaf_aggregation.rs b/core/lib/zksync_core/src/witness_generator/leaf_aggregation.rs deleted file mode 100644 index 94082c421583..000000000000 --- a/core/lib/zksync_core/src/witness_generator/leaf_aggregation.rs +++ /dev/null @@ -1,341 +0,0 @@ -use std::{collections::HashMap, time::Instant}; - -use async_trait::async_trait; -use zksync_config::configs::WitnessGeneratorConfig; -use zksync_dal::ConnectionPool; -use zksync_object_store::{ObjectStore, ObjectStoreFactory}; -use zksync_queued_job_processor::JobProcessor; -use zksync_types::{ - circuit::LEAF_SPLITTING_FACTOR, - proofs::{AggregationRound, PrepareLeafAggregationCircuitsJob, WitnessGeneratorJobMetadata}, - zkevm_test_harness::{ - abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit, - bellman::{bn256::Bn256, plonk::better_better_cs::setup::VerificationKey}, - encodings::{recursion_request::RecursionRequest, QueueSimulator}, - witness, - witness::oracle::VmWitnessOracle, - LeafAggregationOutputDataWitness, - }, - L1BatchNumber, ProtocolVersionId, -}; -use zksync_verification_key_server::{ - get_ordered_vks_for_basic_circuits, get_vks_for_basic_circuits, get_vks_for_commitment, -}; - -use super::{utils::save_prover_input_artifacts, METRICS}; - -pub struct LeafAggregationArtifacts { - leaf_layer_subqueues: Vec, 2, 2>>, - aggregation_outputs: Vec>, - leaf_circuits: Vec>>, -} - -#[derive(Debug)] -struct BlobUrls { - leaf_layer_subqueues_url: String, - aggregation_outputs_url: String, - circuit_types_and_urls: Vec<(&'static str, String)>, -} - -#[derive(Clone)] -pub struct LeafAggregationWitnessGeneratorJob { - block_number: L1BatchNumber, - job: PrepareLeafAggregationCircuitsJob, -} - -#[derive(Debug)] -pub struct LeafAggregationWitnessGenerator { - config: WitnessGeneratorConfig, - object_store: Box, - protocol_versions: Vec, - connection_pool: ConnectionPool, - prover_connection_pool: ConnectionPool, -} - -impl LeafAggregationWitnessGenerator { - pub async fn new( - config: WitnessGeneratorConfig, - store_factory: &ObjectStoreFactory, - protocol_versions: Vec, - connection_pool: ConnectionPool, - prover_connection_pool: ConnectionPool, - ) -> Self { - Self { - config, - object_store: store_factory.create_store().await, - protocol_versions, - connection_pool, - prover_connection_pool, - } - } - - fn process_job_sync( - leaf_job: LeafAggregationWitnessGeneratorJob, - started_at: Instant, - ) -> LeafAggregationArtifacts { - let LeafAggregationWitnessGeneratorJob { block_number, job } = leaf_job; - - tracing::info!( - "Starting witness generation of type {:?} for block {}", - AggregationRound::LeafAggregation, - block_number.0 - ); - process_leaf_aggregation_job(started_at, block_number, job) - } -} - -#[async_trait] -impl JobProcessor for LeafAggregationWitnessGenerator { - type Job = LeafAggregationWitnessGeneratorJob; - type JobId = L1BatchNumber; - type JobArtifacts = LeafAggregationArtifacts; - - const SERVICE_NAME: &'static str = "leaf_aggregation_witness_generator"; - - async fn get_next_job(&self) -> anyhow::Result> { - let mut prover_connection = self.prover_connection_pool.access_storage().await.unwrap(); - let last_l1_batch_to_process = self.config.last_l1_batch_to_process(); - - Ok( - match prover_connection - .witness_generator_dal() - .get_next_leaf_aggregation_witness_job( - self.config.witness_generation_timeout(), - self.config.max_attempts, - last_l1_batch_to_process, - &self.protocol_versions, - ) - .await - { - Some(metadata) => { - let job = get_artifacts(metadata, &*self.object_store).await; - Some((job.block_number, job)) - } - None => None, - }, - ) - } - - async fn save_failure(&self, job_id: L1BatchNumber, started_at: Instant, error: String) -> () { - let attempts = self - .prover_connection_pool - .access_storage() - .await - .unwrap() - .witness_generator_dal() - .mark_witness_job_as_failed( - AggregationRound::LeafAggregation, - job_id, - started_at.elapsed(), - error, - ) - .await; - - if attempts >= self.config.max_attempts { - self.connection_pool - .access_storage() - .await - .unwrap() - .blocks_dal() - .set_skip_proof_for_l1_batch(job_id) - .await - .unwrap(); - } - } - - #[allow(clippy::async_yields_async)] - async fn process_job( - &self, - job: LeafAggregationWitnessGeneratorJob, - started_at: Instant, - ) -> tokio::task::JoinHandle> { - tokio::task::spawn_blocking(move || Ok(Self::process_job_sync(job, started_at))) - } - - async fn save_result( - &self, - job_id: L1BatchNumber, - started_at: Instant, - artifacts: LeafAggregationArtifacts, - ) -> anyhow::Result<()> { - let leaf_circuits_len = artifacts.leaf_circuits.len(); - let blob_urls = save_artifacts(job_id, artifacts, &*self.object_store).await; - update_database( - &self.prover_connection_pool, - started_at, - job_id, - leaf_circuits_len, - blob_urls, - ) - .await; - Ok(()) - } - - fn max_attempts(&self) -> u32 { - self.config.max_attempts - } - - async fn get_job_attempts(&self, _job_id: &Self::JobId) -> anyhow::Result { - // Witness generator will be removed soon in favor of FRI one, so returning blank value. - Ok(1) - } -} - -pub fn process_leaf_aggregation_job( - started_at: Instant, - block_number: L1BatchNumber, - job: PrepareLeafAggregationCircuitsJob, -) -> LeafAggregationArtifacts { - let stage_started_at = Instant::now(); - - let verification_keys: HashMap< - u8, - VerificationKey>>, - > = get_vks_for_basic_circuits(); - - tracing::info!( - "Verification keys loaded in {:?}", - stage_started_at.elapsed() - ); - - // we need the list of vks that matches the list of job.basic_circuit_proofs - let vks_for_aggregation: Vec< - VerificationKey>>, - > = get_ordered_vks_for_basic_circuits(&job.basic_circuits, &verification_keys); - - let (all_vk_committments, set_committment, g2_points) = - witness::recursive_aggregation::form_base_circuits_committment(get_vks_for_commitment( - verification_keys, - )); - - tracing::info!("Commitments generated in {:?}", stage_started_at.elapsed()); - - let stage_started_at = Instant::now(); - - let (leaf_layer_subqueues, aggregation_outputs, leaf_circuits) = - witness::recursive_aggregation::prepare_leaf_aggregations( - job.basic_circuits, - job.basic_circuits_inputs, - job.basic_circuits_proofs, - vks_for_aggregation, - LEAF_SPLITTING_FACTOR, - all_vk_committments, - set_committment, - g2_points, - ); - - tracing::info!( - "prepare_leaf_aggregations took {:?}", - stage_started_at.elapsed() - ); - tracing::info!( - "Leaf witness generation for block {} is complete in {:?}. Number of circuits: {}", - block_number.0, - started_at.elapsed(), - leaf_circuits.len() - ); - - LeafAggregationArtifacts { - leaf_layer_subqueues, - aggregation_outputs, - leaf_circuits, - } -} - -async fn update_database( - prover_connection_pool: &ConnectionPool, - started_at: Instant, - block_number: L1BatchNumber, - leaf_circuits_len: usize, - blob_urls: BlobUrls, -) { - let mut prover_connection = prover_connection_pool.access_storage().await.unwrap(); - let mut transaction = prover_connection.start_transaction().await.unwrap(); - - // inserts artifacts into the node_aggregation_witness_jobs table - // and advances it to waiting_for_proofs status - transaction - .witness_generator_dal() - .save_leaf_aggregation_artifacts( - block_number, - leaf_circuits_len, - &blob_urls.leaf_layer_subqueues_url, - &blob_urls.aggregation_outputs_url, - ) - .await; - let system_version = transaction - .witness_generator_dal() - .protocol_version_for_l1_batch(block_number) - .await - .unwrap_or_else(|| { - panic!( - "No system version exist for l1 batch {} for leaf agg", - block_number.0 - ) - }); - transaction - .prover_dal() - .insert_prover_jobs( - block_number, - blob_urls.circuit_types_and_urls, - AggregationRound::LeafAggregation, - system_version, - ) - .await; - transaction - .witness_generator_dal() - .mark_witness_job_as_successful( - block_number, - AggregationRound::LeafAggregation, - started_at.elapsed(), - ) - .await; - - transaction.commit().await.unwrap(); - METRICS.processing_time[&AggregationRound::LeafAggregation.into()] - .observe(started_at.elapsed()); -} - -async fn get_artifacts( - metadata: WitnessGeneratorJobMetadata, - object_store: &dyn ObjectStore, -) -> LeafAggregationWitnessGeneratorJob { - let basic_circuits = object_store.get(metadata.block_number).await.unwrap(); - let basic_circuits_inputs = object_store.get(metadata.block_number).await.unwrap(); - - LeafAggregationWitnessGeneratorJob { - block_number: metadata.block_number, - job: PrepareLeafAggregationCircuitsJob { - basic_circuits_inputs, - basic_circuits_proofs: metadata.proofs, - basic_circuits, - }, - } -} - -async fn save_artifacts( - block_number: L1BatchNumber, - artifacts: LeafAggregationArtifacts, - object_store: &dyn ObjectStore, -) -> BlobUrls { - let leaf_layer_subqueues_url = object_store - .put(block_number, &artifacts.leaf_layer_subqueues) - .await - .unwrap(); - let aggregation_outputs_url = object_store - .put(block_number, &artifacts.aggregation_outputs) - .await - .unwrap(); - let circuit_types_and_urls = save_prover_input_artifacts( - block_number, - &artifacts.leaf_circuits, - object_store, - AggregationRound::LeafAggregation, - ) - .await; - BlobUrls { - leaf_layer_subqueues_url, - aggregation_outputs_url, - circuit_types_and_urls, - } -} diff --git a/core/lib/zksync_core/src/witness_generator/mod.rs b/core/lib/zksync_core/src/witness_generator/mod.rs deleted file mode 100644 index 2fa941f0bda1..000000000000 --- a/core/lib/zksync_core/src/witness_generator/mod.rs +++ /dev/null @@ -1,82 +0,0 @@ -//! `WitnessGenerator` component is responsible for generating prover jobs -//! and saving artifacts needed for the next round of proof aggregation. -//! -//! That is, every aggregation round needs two sets of input: -//! * computed proofs from the previous round -//! * some artifacts that the witness generator of previous round(s) returns. -//! -//! There are four rounds of proofs for every block, -//! each of them starts with an invocation of `WitnessGenerator` with a corresponding `WitnessGeneratorJobType`: -//! * `WitnessGeneratorJobType::BasicCircuits`: -//! generates basic circuits (circuits like `Main VM` - up to 50 * 48 = 2400 circuits): -//! input table: `basic_circuit_witness_jobs` (TODO SMA-1362: will be renamed from `witness_inputs`) -//! artifact/output table: `leaf_aggregation_jobs` (also creates job stubs in `node_aggregation_jobs` and `scheduler_aggregation_jobs`) -//! value in `aggregation_round` field of `prover_jobs` table: 0 -//! * `WitnessGeneratorJobType::LeafAggregation`: -//! generates leaf aggregation circuits (up to 48 circuits of type `LeafAggregation`) -//! input table: `leaf_aggregation_jobs` -//! artifact/output table: `node_aggregation_jobs` -//! value in `aggregation_round` field of `prover_jobs` table: 1 -//! * `WitnessGeneratorJobType::NodeAggregation` -//! generates one circuit of type `NodeAggregation` -//! input table: `leaf_aggregation_jobs` -//! value in `aggregation_round` field of `prover_jobs` table: 2 -//! * scheduler circuit -//! generates one circuit of type `Scheduler` -//! input table: `scheduler_witness_jobs` -//! value in `aggregation_round` field of `prover_jobs` table: 3 -//! -//! One round of prover generation consists of: -//! * `WitnessGenerator` picks up the next `queued` job in its input table and processes it -//! (invoking the corresponding helper function in `zkevm_test_harness` repository) -//! * it saves the generated circuits to `prover_jobs` table and the other artifacts to its output table -//! * the individual proofs are picked up by the provers, processed, and marked as complete. -//! * when the last proof for this round is computed, the prover updates the row in the output table -//! setting its status to `queued` -//! * `WitnessGenerator` picks up such job and proceeds to the next round -//! -//! Note that the very first input table (`basic_circuit_witness_jobs` (TODO SMA-1362: will be renamed from `witness_inputs`)) -//! is populated by the tree (as the input artifact for the `WitnessGeneratorJobType::BasicCircuits` is the merkle proofs) - -use std::{fmt, time::Duration}; - -use vise::{Buckets, Counter, EncodeLabelSet, EncodeLabelValue, Family, Histogram, Metrics}; -use zksync_types::proofs::AggregationRound; - -pub mod basic_circuits; -pub mod leaf_aggregation; -pub mod node_aggregation; -mod precalculated_merkle_paths_provider; -pub mod scheduler; -mod storage_oracle; -#[cfg(test)] -mod tests; -mod utils; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelSet, EncodeLabelValue)] -#[metrics(label = "stage", format = "wit_gen_{}")] -struct StageLabel(AggregationRound); - -impl From for StageLabel { - fn from(round: AggregationRound) -> Self { - Self(round) - } -} - -impl fmt::Display for StageLabel { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(formatter) - } -} - -#[derive(Debug, Metrics)] -#[metrics(prefix = "server_witness_generator")] -struct WitnessGeneratorMetrics { - #[metrics(buckets = Buckets::LATENCIES)] - processing_time: Family>, - skipped_blocks: Counter, - sampled_blocks: Counter, -} - -#[vise::register] -static METRICS: vise::Global = vise::Global::new(); diff --git a/core/lib/zksync_core/src/witness_generator/node_aggregation.rs b/core/lib/zksync_core/src/witness_generator/node_aggregation.rs deleted file mode 100644 index 8ca86be00a52..000000000000 --- a/core/lib/zksync_core/src/witness_generator/node_aggregation.rs +++ /dev/null @@ -1,376 +0,0 @@ -use std::{collections::HashMap, env, time::Instant}; - -use async_trait::async_trait; -use zksync_config::configs::WitnessGeneratorConfig; -use zksync_dal::ConnectionPool; -use zksync_object_store::{ObjectStore, ObjectStoreFactory}; -use zksync_queued_job_processor::JobProcessor; -use zksync_types::{ - circuit::{ - LEAF_CIRCUIT_INDEX, LEAF_SPLITTING_FACTOR, NODE_CIRCUIT_INDEX, NODE_SPLITTING_FACTOR, - }, - proofs::{AggregationRound, PrepareNodeAggregationCircuitJob, WitnessGeneratorJobMetadata}, - zkevm_test_harness::{ - abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit, - bellman::{bn256::Bn256, plonk::better_better_cs::setup::VerificationKey}, - ff::to_hex, - witness::{ - self, - oracle::VmWitnessOracle, - recursive_aggregation::{erase_vk_type, padding_aggregations}, - }, - NodeAggregationOutputDataWitness, - }, - L1BatchNumber, ProtocolVersionId, -}; -use zksync_verification_key_server::{ - get_vk_for_circuit_type, get_vks_for_basic_circuits, get_vks_for_commitment, -}; - -use super::{utils::save_prover_input_artifacts, METRICS}; - -pub struct NodeAggregationArtifacts { - final_node_aggregation: NodeAggregationOutputDataWitness, - node_circuits: Vec>>, -} - -#[derive(Debug)] -struct BlobUrls { - node_aggregations_url: String, - circuit_types_and_urls: Vec<(&'static str, String)>, -} - -#[derive(Clone)] -pub struct NodeAggregationWitnessGeneratorJob { - block_number: L1BatchNumber, - job: PrepareNodeAggregationCircuitJob, -} - -#[derive(Debug)] -pub struct NodeAggregationWitnessGenerator { - config: WitnessGeneratorConfig, - object_store: Box, - protocol_versions: Vec, - connection_pool: ConnectionPool, - prover_connection_pool: ConnectionPool, -} - -impl NodeAggregationWitnessGenerator { - pub async fn new( - config: WitnessGeneratorConfig, - store_factory: &ObjectStoreFactory, - protocol_versions: Vec, - connection_pool: ConnectionPool, - prover_connection_pool: ConnectionPool, - ) -> Self { - Self { - config, - object_store: store_factory.create_store().await, - protocol_versions, - connection_pool, - prover_connection_pool, - } - } - - fn process_job_sync( - config: WitnessGeneratorConfig, - node_job: NodeAggregationWitnessGeneratorJob, - started_at: Instant, - ) -> anyhow::Result { - let NodeAggregationWitnessGeneratorJob { block_number, job } = node_job; - - tracing::info!( - "Starting witness generation of type {:?} for block {}", - AggregationRound::NodeAggregation, - block_number.0 - ); - Ok(process_node_aggregation_job( - config, - started_at, - block_number, - job, - )) - } -} - -#[async_trait] -impl JobProcessor for NodeAggregationWitnessGenerator { - type Job = NodeAggregationWitnessGeneratorJob; - type JobId = L1BatchNumber; - type JobArtifacts = NodeAggregationArtifacts; - - const SERVICE_NAME: &'static str = "node_aggregation_witness_generator"; - - async fn get_next_job(&self) -> anyhow::Result> { - let mut prover_connection = self.prover_connection_pool.access_storage().await.unwrap(); - let last_l1_batch_to_process = self.config.last_l1_batch_to_process(); - - Ok( - match prover_connection - .witness_generator_dal() - .get_next_node_aggregation_witness_job( - self.config.witness_generation_timeout(), - self.config.max_attempts, - last_l1_batch_to_process, - &self.protocol_versions, - ) - .await - { - Some(metadata) => { - let job = get_artifacts(metadata, &*self.object_store).await; - Some((job.block_number, job)) - } - None => None, - }, - ) - } - - async fn save_failure(&self, job_id: L1BatchNumber, started_at: Instant, error: String) -> () { - let attempts = self - .prover_connection_pool - .access_storage() - .await - .unwrap() - .witness_generator_dal() - .mark_witness_job_as_failed( - AggregationRound::NodeAggregation, - job_id, - started_at.elapsed(), - error, - ) - .await; - - if attempts >= self.config.max_attempts { - self.connection_pool - .access_storage() - .await - .unwrap() - .blocks_dal() - .set_skip_proof_for_l1_batch(job_id) - .await - .unwrap(); - } - } - - #[allow(clippy::async_yields_async)] - async fn process_job( - &self, - job: NodeAggregationWitnessGeneratorJob, - started_at: Instant, - ) -> tokio::task::JoinHandle> { - let config = self.config.clone(); - tokio::task::spawn_blocking(move || Self::process_job_sync(config, job, started_at)) - } - - async fn save_result( - &self, - job_id: L1BatchNumber, - started_at: Instant, - artifacts: NodeAggregationArtifacts, - ) -> anyhow::Result<()> { - let blob_urls = save_artifacts(job_id, artifacts, &*self.object_store).await; - update_database(&self.prover_connection_pool, started_at, job_id, blob_urls).await; - Ok(()) - } - - fn max_attempts(&self) -> u32 { - self.config.max_attempts - } - - async fn get_job_attempts(&self, _job_id: &Self::JobId) -> anyhow::Result { - // Witness generator will be removed soon in favor of FRI one, so returning blank value. - Ok(1) - } -} - -pub fn process_node_aggregation_job( - config: WitnessGeneratorConfig, - started_at: Instant, - block_number: L1BatchNumber, - job: PrepareNodeAggregationCircuitJob, -) -> NodeAggregationArtifacts { - let stage_started_at = Instant::now(); - zksync_prover_utils::ensure_initial_setup_keys_present( - &config.initial_setup_key_path, - &config.key_download_url, - ); - env::set_var("CRS_FILE", config.initial_setup_key_path); - tracing::info!("Keys loaded in {:?}", stage_started_at.elapsed()); - metrics::histogram!("server.prover.download_time", started_at.elapsed()); - - let stage_started_at = Instant::now(); - - let verification_keys: HashMap< - u8, - VerificationKey>>, - > = get_vks_for_basic_circuits(); - - let padding_aggregations = padding_aggregations(NODE_SPLITTING_FACTOR); - - let (_, set_committment, g2_points) = - witness::recursive_aggregation::form_base_circuits_committment(get_vks_for_commitment( - verification_keys, - )); - - let node_aggregation_vk = get_vk_for_circuit_type(NODE_CIRCUIT_INDEX); - - let leaf_aggregation_vk = get_vk_for_circuit_type(LEAF_CIRCUIT_INDEX); - - let (_, leaf_aggregation_vk_committment) = - witness::recursive_aggregation::compute_vk_encoding_and_committment(erase_vk_type( - leaf_aggregation_vk.clone(), - )); - - let (_, node_aggregation_vk_committment) = - witness::recursive_aggregation::compute_vk_encoding_and_committment(erase_vk_type( - node_aggregation_vk, - )); - - tracing::info!( - "commitments: basic set: {:?}, leaf: {:?}, node: {:?}", - to_hex(&set_committment), - to_hex(&leaf_aggregation_vk_committment), - to_hex(&node_aggregation_vk_committment) - ); - tracing::info!("Commitments generated in {:?}", stage_started_at.elapsed()); - - let stage_started_at = Instant::now(); - let (_, final_node_aggregations, node_circuits) = - zksync_types::zkevm_test_harness::witness::recursive_aggregation::prepare_node_aggregations( - job.previous_level_proofs, - leaf_aggregation_vk, - true, - 0, - job.previous_level_leafs_aggregations, - Vec::default(), - job.previous_sequence, - LEAF_SPLITTING_FACTOR, - NODE_SPLITTING_FACTOR, - padding_aggregations, - set_committment, - node_aggregation_vk_committment, - leaf_aggregation_vk_committment, - g2_points, - ); - - tracing::info!( - "prepare_node_aggregations took {:?}", - stage_started_at.elapsed() - ); - - assert_eq!( - node_circuits.len(), - 1, - "prepare_node_aggregations returned more than one circuit" - ); - assert_eq!( - final_node_aggregations.len(), - 1, - "prepare_node_aggregations returned more than one node aggregation" - ); - - tracing::info!( - "Node witness generation for block {} is complete in {:?}. Number of circuits: {}", - block_number.0, - started_at.elapsed(), - node_circuits.len() - ); - - NodeAggregationArtifacts { - final_node_aggregation: final_node_aggregations.into_iter().next().unwrap(), - node_circuits, - } -} - -async fn update_database( - prover_connection_pool: &ConnectionPool, - started_at: Instant, - block_number: L1BatchNumber, - blob_urls: BlobUrls, -) { - let mut prover_connection = prover_connection_pool.access_storage().await.unwrap(); - let mut transaction = prover_connection.start_transaction().await.unwrap(); - - // inserts artifacts into the scheduler_witness_jobs table - // and advances it to waiting_for_proofs status - transaction - .witness_generator_dal() - .save_node_aggregation_artifacts(block_number, &blob_urls.node_aggregations_url) - .await; - let protocol_version = transaction - .witness_generator_dal() - .protocol_version_for_l1_batch(block_number) - .await - .unwrap_or_else(|| { - panic!( - "No system version exist for l1 batch {} for node agg", - block_number.0 - ) - }); - transaction - .prover_dal() - .insert_prover_jobs( - block_number, - blob_urls.circuit_types_and_urls, - AggregationRound::NodeAggregation, - protocol_version, - ) - .await; - transaction - .witness_generator_dal() - .mark_witness_job_as_successful( - block_number, - AggregationRound::NodeAggregation, - started_at.elapsed(), - ) - .await; - - transaction.commit().await.unwrap(); - METRICS.processing_time[&AggregationRound::NodeAggregation.into()] - .observe(started_at.elapsed()); -} - -async fn get_artifacts( - metadata: WitnessGeneratorJobMetadata, - object_store: &dyn ObjectStore, -) -> NodeAggregationWitnessGeneratorJob { - let leaf_layer_subqueues = object_store - .get(metadata.block_number) - .await - .expect("leaf_layer_subqueues not found in queued `node_aggregation_witness_jobs` job"); - let aggregation_outputs = object_store - .get(metadata.block_number) - .await - .expect("aggregation_outputs not found in queued `node_aggregation_witness_jobs` job"); - - NodeAggregationWitnessGeneratorJob { - block_number: metadata.block_number, - job: PrepareNodeAggregationCircuitJob { - previous_level_proofs: metadata.proofs, - previous_level_leafs_aggregations: aggregation_outputs, - previous_sequence: leaf_layer_subqueues, - }, - } -} - -async fn save_artifacts( - block_number: L1BatchNumber, - artifacts: NodeAggregationArtifacts, - object_store: &dyn ObjectStore, -) -> BlobUrls { - let node_aggregations_url = object_store - .put(block_number, &artifacts.final_node_aggregation) - .await - .unwrap(); - let circuit_types_and_urls = save_prover_input_artifacts( - block_number, - &artifacts.node_circuits, - object_store, - AggregationRound::NodeAggregation, - ) - .await; - BlobUrls { - node_aggregations_url, - circuit_types_and_urls, - } -} diff --git a/core/lib/zksync_core/src/witness_generator/precalculated_merkle_paths_provider.rs b/core/lib/zksync_core/src/witness_generator/precalculated_merkle_paths_provider.rs deleted file mode 100644 index 73f714d73145..000000000000 --- a/core/lib/zksync_core/src/witness_generator/precalculated_merkle_paths_provider.rs +++ /dev/null @@ -1,265 +0,0 @@ -use serde::{Deserialize, Serialize}; -use zksync_types::{ - proofs::{PrepareBasicCircuitsJob, StorageLogMetadata}, - zkevm_test_harness::{ - blake2::Blake2s256, - witness::tree::{ - BinaryHasher, BinarySparseStorageTree, EnumeratedBinaryLeaf, LeafQuery, - ZkSyncStorageLeaf, - }, - }, -}; - -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] -pub struct PrecalculatedMerklePathsProvider { - // We keep the root hash of the last processed leaf, as it is needed by the the witness generator. - pub root_hash: [u8; 32], - // The ordered list of expected leaves to be interacted with - pub pending_leaves: Vec, - // The index that would be assigned to the next new leaf - pub next_enumeration_index: u64, - // For every Storage Write Log we expect two invocations: `get_leaf` and `insert_leaf`. - // We set this flag to `true` after the initial `get_leaf` is invoked. - pub is_get_leaf_invoked: bool, -} - -impl PrecalculatedMerklePathsProvider { - pub fn new(input: PrepareBasicCircuitsJob, root_hash: [u8; 32]) -> Self { - let next_enumeration_index = input.next_enumeration_index(); - tracing::debug!("Initializing PrecalculatedMerklePathsProvider. Initial root_hash: {:?}, initial next_enumeration_index: {:?}", root_hash, next_enumeration_index); - Self { - root_hash, - pending_leaves: input.into_merkle_paths().collect(), - next_enumeration_index, - is_get_leaf_invoked: false, - } - } -} - -impl BinarySparseStorageTree<256, 32, 32, 8, 32, Blake2s256, ZkSyncStorageLeaf> - for PrecalculatedMerklePathsProvider -{ - fn empty() -> Self { - unreachable!("`empty` must not be invoked by the witness generator code"); - } - - fn next_enumeration_index(&self) -> u64 { - self.next_enumeration_index - } - - fn set_next_enumeration_index(&mut self, _value: u64) { - unreachable!( - "`set_next_enumeration_index` must not be invoked by the witness generator code" - ); - } - - fn root(&self) -> [u8; 32] { - self.root_hash - } - - fn get_leaf(&mut self, index: &[u8; 32]) -> LeafQuery<256, 32, 32, 32, ZkSyncStorageLeaf> { - tracing::trace!( - "Invoked get_leaf({:?}). pending leaves size: {:?}. current root: {:?}", - index, - self.pending_leaves.len(), - self.root() - ); - assert!( - !self.is_get_leaf_invoked, - "`get_leaf()` invoked more than once or get_leaf is invoked when insert_leaf was expected" - ); - let next = self.pending_leaves.first().unwrap_or_else(|| { - panic!( - "invoked `get_leaf({:?})` with empty `pending_leaves`", - index - ) - }); - self.root_hash = next.root_hash; - - assert_eq!( - &next.leaf_hashed_key_array(), - index, - "`get_leaf` hashed key mismatch" - ); - - let mut res = LeafQuery { - leaf: ZkSyncStorageLeaf { - index: next.leaf_enumeration_index, - value: next.value_read, - }, - first_write: next.first_write, - index: *index, - merkle_path: next.clone().into_merkle_paths_array(), - }; - - if next.is_write { - // If it is a write, the next invocation will be `insert_leaf` with the very same parameters - self.is_get_leaf_invoked = true; - if res.first_write { - res.leaf.index = 0; - } - } else { - // If it is a read, the next invocation will relate to the next `pending_leaf` - self.pending_leaves.remove(0); - }; - - res - } - - fn insert_leaf( - &mut self, - index: &[u8; 32], - leaf: ZkSyncStorageLeaf, - ) -> LeafQuery<256, 32, 32, 32, ZkSyncStorageLeaf> { - tracing::trace!( - "Invoked insert_leaf({:?}). pending leaves size: {:?}. current root: {:?}", - index, - self.pending_leaves.len(), - self.root() - ); - - assert!( - self.is_get_leaf_invoked, - "`get_leaf()` is expected to be invoked before `insert_leaf()`" - ); - let next = self.pending_leaves.remove(0); - self.root_hash = next.root_hash; - - assert!( - next.is_write, - "invoked `insert_leaf({:?})`, but get_leaf() expected", - index - ); - - assert_eq!( - &next.leaf_hashed_key_array(), - index, - "insert_leaf hashed key mismatch", - ); - - assert_eq!( - &next.value_written, &leaf.value, - "insert_leaf enumeration index mismatch", - ); - - // reset is_get_leaf_invoked for the next get/insert invocation - self.is_get_leaf_invoked = false; - - // if this insert was in fact the very first insert, it should bump the `next_enumeration_index` - self.next_enumeration_index = self - .next_enumeration_index - .max(next.leaf_enumeration_index + 1); - - LeafQuery { - leaf: ZkSyncStorageLeaf { - index: next.leaf_enumeration_index, - value: next.value_written, - }, - first_write: next.first_write, - index: *index, - merkle_path: next.into_merkle_paths_array(), - } - } - - // Method to segregate the given leafs into 2 types: - // * leafs that are updated for first time - // * leafs that are not updated for the first time. - // The consumer of method must ensure that the length of passed argument indexes and leafs are same, - // and the merkle paths specified during the initialization must contains same number of write - // leaf nodes as that of the leafs passed as argument. - fn filter_renumerate<'a>( - &self, - mut indexes: impl Iterator, - mut leafs: impl Iterator, - ) -> ( - u64, - Vec<([u8; 32], ZkSyncStorageLeaf)>, - Vec, - ) { - tracing::trace!( - "invoked filter_renumerate(), pending leaves size: {:?}", - self.pending_leaves.len() - ); - let mut first_writes = vec![]; - let mut updates = vec![]; - let write_pending_leaves = self - .pending_leaves - .iter() - .filter(|&l| l.is_write) - .collect::>(); - let write_pending_leaves_iter = write_pending_leaves.iter(); - let mut length = 0; - for (&pending_leaf, (idx, mut leaf)) in - write_pending_leaves_iter.zip((&mut indexes).zip(&mut leafs)) - { - leaf.set_index(pending_leaf.leaf_enumeration_index); - if pending_leaf.first_write { - first_writes.push((*idx, leaf)); - } else { - updates.push(leaf); - } - length += 1; - } - assert_eq!( - length, - write_pending_leaves.len(), - "pending leaves: len({}) must be of same length as leafs and indexes: len({})", - write_pending_leaves.len(), - length - ); - assert!( - indexes.next().is_none(), - "indexes must be of same length as leafs and pending leaves: len({})", - write_pending_leaves.len() - ); - assert!( - leafs.next().is_none(), - "leafs must be of same length as indexes and pending leaves: len({})", - write_pending_leaves.len() - ); - (self.next_enumeration_index, first_writes, updates) - } - - fn verify_inclusion( - root: &[u8; 32], - query: &LeafQuery<256, 32, 32, 32, ZkSyncStorageLeaf>, - ) -> bool { - //copied from zkevm_test_harness/src/witness/tree/mod.rs with minor changes - tracing::trace!( - "invoked verify_inclusion. Index: {:?}, root: {:?})", - query.index, - root - ); - - let mut leaf_bytes = vec![0u8; 8 + 32]; // can make a scratch space somewhere later on - leaf_bytes[8..].copy_from_slice(query.leaf.value()); - - let leaf_index_bytes = query.leaf.current_index().to_be_bytes(); - leaf_bytes[0..8].copy_from_slice(&leaf_index_bytes); - - let leaf_hash = Blake2s256::leaf_hash(&leaf_bytes); - - let mut current_hash = leaf_hash; - for level in 0..256 { - let (l, r) = if is_right_side_node(&query.index, level) { - (&query.merkle_path[level], ¤t_hash) - } else { - (¤t_hash, &query.merkle_path[level]) - }; - - let this_level_hash = Blake2s256::node_hash(level, l, r); - - current_hash = this_level_hash; - } - - root == ¤t_hash - } -} - -fn is_right_side_node(index: &[u8; N], depth: usize) -> bool { - debug_assert!(depth < N * 8); - let byte_idx = depth / 8; - let bit_idx = depth % 8; - - index[byte_idx] & (1u8 << bit_idx) != 0 -} diff --git a/core/lib/zksync_core/src/witness_generator/scheduler.rs b/core/lib/zksync_core/src/witness_generator/scheduler.rs deleted file mode 100644 index a0f1b6b6d7ac..000000000000 --- a/core/lib/zksync_core/src/witness_generator/scheduler.rs +++ /dev/null @@ -1,375 +0,0 @@ -use std::{collections::HashMap, slice, time::Instant}; - -use async_trait::async_trait; -use zksync_config::configs::WitnessGeneratorConfig; -use zksync_dal::ConnectionPool; -use zksync_object_store::{ObjectStore, ObjectStoreFactory}; -use zksync_queued_job_processor::JobProcessor; -use zksync_types::{ - circuit::{ - LEAF_CIRCUIT_INDEX, LEAF_SPLITTING_FACTOR, NODE_CIRCUIT_INDEX, NODE_SPLITTING_FACTOR, - }, - proofs::{AggregationRound, PrepareSchedulerCircuitJob, WitnessGeneratorJobMetadata}, - zkevm_test_harness::{ - abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit, - bellman::{bn256::Bn256, plonk::better_better_cs::setup::VerificationKey}, - sync_vm::scheduler::BlockApplicationWitness, - witness::{self, oracle::VmWitnessOracle, recursive_aggregation::erase_vk_type}, - }, - L1BatchNumber, ProtocolVersionId, -}; -use zksync_verification_key_server::{ - get_vk_for_circuit_type, get_vks_for_basic_circuits, get_vks_for_commitment, -}; - -use super::{utils::save_prover_input_artifacts, METRICS}; - -pub struct SchedulerArtifacts { - final_aggregation_result: BlockApplicationWitness, - scheduler_circuit: ZkSyncCircuit>, -} - -#[derive(Clone)] -pub struct SchedulerWitnessGeneratorJob { - block_number: L1BatchNumber, - job: PrepareSchedulerCircuitJob, -} - -#[derive(Debug)] -pub struct SchedulerWitnessGenerator { - config: WitnessGeneratorConfig, - object_store: Box, - protocol_versions: Vec, - connection_pool: ConnectionPool, - prover_connection_pool: ConnectionPool, -} - -impl SchedulerWitnessGenerator { - pub async fn new( - config: WitnessGeneratorConfig, - store_factory: &ObjectStoreFactory, - protocol_versions: Vec, - connection_pool: ConnectionPool, - prover_connection_pool: ConnectionPool, - ) -> Self { - Self { - config, - object_store: store_factory.create_store().await, - protocol_versions, - connection_pool, - prover_connection_pool, - } - } - - fn process_job_sync( - scheduler_job: SchedulerWitnessGeneratorJob, - started_at: Instant, - ) -> SchedulerArtifacts { - let SchedulerWitnessGeneratorJob { block_number, job } = scheduler_job; - - tracing::info!( - "Starting witness generation of type {:?} for block {}", - AggregationRound::Scheduler, - block_number.0 - ); - process_scheduler_job(started_at, block_number, job) - } -} - -#[async_trait] -impl JobProcessor for SchedulerWitnessGenerator { - type Job = SchedulerWitnessGeneratorJob; - type JobId = L1BatchNumber; - type JobArtifacts = SchedulerArtifacts; - - const SERVICE_NAME: &'static str = "scheduler_witness_generator"; - - async fn get_next_job(&self) -> anyhow::Result> { - let mut connection = self.connection_pool.access_storage().await.unwrap(); - let mut prover_connection = self.prover_connection_pool.access_storage().await.unwrap(); - let last_l1_batch_to_process = self.config.last_l1_batch_to_process(); - - match prover_connection - .witness_generator_dal() - .get_next_scheduler_witness_job( - self.config.witness_generation_timeout(), - self.config.max_attempts, - last_l1_batch_to_process, - &self.protocol_versions, - ) - .await - { - Some(metadata) => { - let prev_metadata = connection - .blocks_dal() - .get_l1_batch_metadata(metadata.block_number - 1) - .await - .unwrap(); - let previous_aux_hash = prev_metadata - .as_ref() - .map_or([0u8; 32], |e| e.metadata.aux_data_hash.0); - let previous_meta_hash = - prev_metadata.map_or([0u8; 32], |e| e.metadata.meta_parameters_hash.0); - let job = get_artifacts( - metadata, - previous_aux_hash, - previous_meta_hash, - &*self.object_store, - ) - .await; - Ok(Some((job.block_number, job))) - } - None => Ok(None), - } - } - - async fn save_failure(&self, job_id: L1BatchNumber, started_at: Instant, error: String) -> () { - let attempts = self - .prover_connection_pool - .access_storage() - .await - .unwrap() - .witness_generator_dal() - .mark_witness_job_as_failed( - AggregationRound::Scheduler, - job_id, - started_at.elapsed(), - error, - ) - .await; - - if attempts >= self.config.max_attempts { - self.connection_pool - .access_storage() - .await - .unwrap() - .blocks_dal() - .set_skip_proof_for_l1_batch(job_id) - .await - .unwrap(); - } - } - - #[allow(clippy::async_yields_async)] - async fn process_job( - &self, - job: SchedulerWitnessGeneratorJob, - started_at: Instant, - ) -> tokio::task::JoinHandle> { - tokio::task::spawn_blocking(move || Ok(Self::process_job_sync(job, started_at))) - } - - async fn save_result( - &self, - job_id: L1BatchNumber, - started_at: Instant, - artifacts: SchedulerArtifacts, - ) -> anyhow::Result<()> { - let circuit_types_and_urls = - save_artifacts(job_id, &artifacts.scheduler_circuit, &*self.object_store).await; - update_database( - &self.connection_pool, - &self.prover_connection_pool, - started_at, - job_id, - artifacts.final_aggregation_result, - circuit_types_and_urls, - ) - .await; - Ok(()) - } - - fn max_attempts(&self) -> u32 { - self.config.max_attempts - } - - async fn get_job_attempts(&self, _job_id: &Self::JobId) -> anyhow::Result { - // Witness generator will be removed soon in favor of FRI one, so returning blank value. - Ok(1) - } -} - -pub fn process_scheduler_job( - started_at: Instant, - block_number: L1BatchNumber, - job: PrepareSchedulerCircuitJob, -) -> SchedulerArtifacts { - let stage_started_at = Instant::now(); - - let verification_keys: HashMap< - u8, - VerificationKey>>, - > = get_vks_for_basic_circuits(); - - let (_, set_committment, g2_points) = - witness::recursive_aggregation::form_base_circuits_committment(get_vks_for_commitment( - verification_keys, - )); - - tracing::info!( - "Verification keys loaded in {:?}", - stage_started_at.elapsed() - ); - - let leaf_aggregation_vk = get_vk_for_circuit_type(LEAF_CIRCUIT_INDEX); - - let node_aggregation_vk = get_vk_for_circuit_type(NODE_CIRCUIT_INDEX); - - let (_, leaf_aggregation_vk_committment) = - witness::recursive_aggregation::compute_vk_encoding_and_committment(erase_vk_type( - leaf_aggregation_vk, - )); - - let (_, node_aggregation_vk_committment) = - witness::recursive_aggregation::compute_vk_encoding_and_committment(erase_vk_type( - node_aggregation_vk.clone(), - )); - - tracing::info!("Commitments generated in {:?}", stage_started_at.elapsed()); - let stage_started_at = Instant::now(); - - let (scheduler_circuit, final_aggregation_result) = - witness::recursive_aggregation::prepare_scheduler_circuit( - job.incomplete_scheduler_witness, - job.node_final_proof_level_proof, - node_aggregation_vk, - job.final_node_aggregations, - set_committment, - node_aggregation_vk_committment, - leaf_aggregation_vk_committment, - job.previous_aux_hash, - job.previous_meta_hash, - (LEAF_SPLITTING_FACTOR * NODE_SPLITTING_FACTOR) as u32, - g2_points, - ); - - tracing::info!( - "prepare_scheduler_circuit took {:?}", - stage_started_at.elapsed() - ); - - tracing::info!( - "Scheduler generation for block {} is complete in {:?}", - block_number.0, - started_at.elapsed() - ); - - SchedulerArtifacts { - final_aggregation_result, - scheduler_circuit, - } -} - -pub async fn update_database( - connection_pool: &ConnectionPool, - prover_connection_pool: &ConnectionPool, - started_at: Instant, - block_number: L1BatchNumber, - final_aggregation_result: BlockApplicationWitness, - circuit_types_and_urls: Vec<(&'static str, String)>, -) { - let mut connection = connection_pool.access_storage().await.unwrap(); - let block = connection - .blocks_dal() - .get_l1_batch_metadata(block_number) - .await - .unwrap() - .expect("L1 batch should exist"); - - assert_eq!( - block.metadata.aux_data_hash.0, final_aggregation_result.aux_data_hash, - "Commitment for aux data is wrong" - ); - - assert_eq!( - block.metadata.pass_through_data_hash.0, final_aggregation_result.passthrough_data_hash, - "Commitment for pass through data is wrong" - ); - - assert_eq!( - block.metadata.meta_parameters_hash.0, final_aggregation_result.meta_data_hash, - "Commitment for metadata is wrong" - ); - - assert_eq!( - block.metadata.commitment.0, final_aggregation_result.block_header_hash, - "Commitment is wrong" - ); - - let mut prover_connection = prover_connection_pool.access_storage().await.unwrap(); - let mut transaction = prover_connection.start_transaction().await.unwrap(); - let protocol_version = transaction - .witness_generator_dal() - .protocol_version_for_l1_batch(block_number) - .await - .unwrap_or_else(|| { - panic!( - "No system version exist for l1 batch {} for node agg", - block_number.0 - ) - }); - transaction - .prover_dal() - .insert_prover_jobs( - block_number, - circuit_types_and_urls, - AggregationRound::Scheduler, - protocol_version, - ) - .await; - - transaction - .witness_generator_dal() - .save_final_aggregation_result( - block_number, - final_aggregation_result.aggregation_result_coords, - ) - .await; - - transaction - .witness_generator_dal() - .mark_witness_job_as_successful( - block_number, - AggregationRound::Scheduler, - started_at.elapsed(), - ) - .await; - - transaction.commit().await.unwrap(); - METRICS.processing_time[&AggregationRound::Scheduler.into()].observe(started_at.elapsed()); -} - -async fn save_artifacts( - block_number: L1BatchNumber, - scheduler_circuit: &ZkSyncCircuit>, - object_store: &dyn ObjectStore, -) -> Vec<(&'static str, String)> { - save_prover_input_artifacts( - block_number, - slice::from_ref(scheduler_circuit), - object_store, - AggregationRound::Scheduler, - ) - .await -} - -async fn get_artifacts( - metadata: WitnessGeneratorJobMetadata, - previous_aux_hash: [u8; 32], - previous_meta_hash: [u8; 32], - object_store: &dyn ObjectStore, -) -> SchedulerWitnessGeneratorJob { - let scheduler_witness = object_store.get(metadata.block_number).await.unwrap(); - let final_node_aggregations = object_store.get(metadata.block_number).await.unwrap(); - - SchedulerWitnessGeneratorJob { - block_number: metadata.block_number, - job: PrepareSchedulerCircuitJob { - incomplete_scheduler_witness: scheduler_witness, - final_node_aggregations, - node_final_proof_level_proof: metadata.proofs.into_iter().next().unwrap(), - previous_aux_hash, - previous_meta_hash, - }, - } -} diff --git a/core/lib/zksync_core/src/witness_generator/storage_oracle.rs b/core/lib/zksync_core/src/witness_generator/storage_oracle.rs deleted file mode 100644 index f0b3203686fb..000000000000 --- a/core/lib/zksync_core/src/witness_generator/storage_oracle.rs +++ /dev/null @@ -1,46 +0,0 @@ -use zksync_types::{ - zkevm_test_harness::zk_evm::abstractions::{RefundType, RefundedAmounts, Storage}, - LogQuery, Timestamp, -}; - -#[derive(Debug)] -pub(super) struct StorageOracle { - inn: T, - storage_refunds: std::vec::IntoIter, -} - -impl StorageOracle { - pub fn new(inn: T, storage_refunds: Vec) -> Self { - Self { - inn, - storage_refunds: storage_refunds.into_iter(), - } - } -} - -impl Storage for StorageOracle { - fn estimate_refunds_for_write( - &mut self, - _monotonic_cycle_counter: u32, - _partial_query: &LogQuery, - ) -> RefundType { - let pubdata_bytes = self.storage_refunds.next().expect("Missing refund"); - RefundType::RepeatedWrite(RefundedAmounts { - pubdata_bytes, - ergs: 0, - }) - } - - fn execute_partial_query(&mut self, monotonic_cycle_counter: u32, query: LogQuery) -> LogQuery { - self.inn - .execute_partial_query(monotonic_cycle_counter, query) - } - - fn finish_frame(&mut self, timestamp: Timestamp, panicked: bool) { - self.inn.finish_frame(timestamp, panicked) - } - - fn start_frame(&mut self, timestamp: Timestamp) { - self.inn.start_frame(timestamp) - } -} diff --git a/core/lib/zksync_core/src/witness_generator/tests.rs b/core/lib/zksync_core/src/witness_generator/tests.rs deleted file mode 100644 index fb7b285b1199..000000000000 --- a/core/lib/zksync_core/src/witness_generator/tests.rs +++ /dev/null @@ -1,300 +0,0 @@ -use std::convert::TryInto; - -use zksync_types::{ - proofs::StorageLogMetadata, - zkevm_test_harness::witness::tree::{BinarySparseStorageTree, ZkSyncStorageLeaf}, -}; - -use crate::witness_generator::precalculated_merkle_paths_provider::PrecalculatedMerklePathsProvider; - -#[test] -fn test_filter_renumerate_all_first_writes() { - let logs = vec![ - generate_storage_log_metadata( - "DDC60818D8F7CFE42514F8EA3CC52806DDC60818D8F7CFE42514F8EA3CC52806", - "12E9FF974B0FAEE514AD4AC50E2BDC6E12E9FF974B0FAEE514AD4AC50E2BDC6E", - false, - false, - 1, - ), - generate_storage_log_metadata( - "BDA1617CC883E2251D3BE0FD9B3F3064BDA1617CC883E2251D3BE0FD9B3F3064", - "D14917FCB067922F92322025D1BA50B4D14917FCB067922F92322025D1BA50B4", - true, - true, - 2, - ), - generate_storage_log_metadata( - "77F035AD50811CFABD956F6F1B48E48277F035AD50811CFABD956F6F1B48E482", - "7CF33B959916CC9B56F21C427ED7CA187CF33B959916CC9B56F21C427ED7CA18", - true, - true, - 3, - ), - ]; - let precalculated_merkle_paths_provider = PrecalculatedMerklePathsProvider { - root_hash: string_to_array( - "4AF44B3D5D4F9C7B117A68351AAB65CF4AF44B3D5D4F9C7B117A68351AAB65CF", - ), - pending_leaves: logs, - next_enumeration_index: 4, - is_get_leaf_invoked: false, - }; - let (leafs, indices) = generate_leafs_indices(); - - let (_, first_writes, updates) = - precalculated_merkle_paths_provider.filter_renumerate(indices.iter(), leafs.into_iter()); - assert_eq!(2, first_writes.len()); - assert_eq!(0, updates.len()); -} - -#[test] -fn test_filter_renumerate_all_repeated_writes() { - let logs = vec![ - generate_storage_log_metadata( - "DDC60818D8F7CFE42514F8EA3CC52806DDC60818D8F7CFE42514F8EA3CC52806", - "12E9FF974B0FAEE514AD4AC50E2BDC6E12E9FF974B0FAEE514AD4AC50E2BDC6E", - false, - false, - 1, - ), - generate_storage_log_metadata( - "BDA1617CC883E2251D3BE0FD9B3F3064BDA1617CC883E2251D3BE0FD9B3F3064", - "D14917FCB067922F92322025D1BA50B4D14917FCB067922F92322025D1BA50B4", - true, - false, - 2, - ), - generate_storage_log_metadata( - "77F035AD50811CFABD956F6F1B48E48277F035AD50811CFABD956F6F1B48E482", - "7CF33B959916CC9B56F21C427ED7CA187CF33B959916CC9B56F21C427ED7CA18", - true, - false, - 3, - ), - ]; - let precalculated_merkle_paths_provider = PrecalculatedMerklePathsProvider { - root_hash: string_to_array( - "4AF44B3D5D4F9C7B117A68351AAB65CF4AF44B3D5D4F9C7B117A68351AAB65CF", - ), - pending_leaves: logs, - next_enumeration_index: 4, - is_get_leaf_invoked: false, - }; - let (leafs, indices) = generate_leafs_indices(); - - let (_, first_writes, updates) = - precalculated_merkle_paths_provider.filter_renumerate(indices.iter(), leafs.into_iter()); - assert_eq!(0, first_writes.len()); - assert_eq!(2, updates.len()); -} - -#[test] -fn test_filter_renumerate_repeated_writes_with_first_write() { - let logs = vec![ - generate_storage_log_metadata( - "DDC60818D8F7CFE42514F8EA3CC52806DDC60818D8F7CFE42514F8EA3CC52806", - "12E9FF974B0FAEE514AD4AC50E2BDC6E12E9FF974B0FAEE514AD4AC50E2BDC6E", - false, - false, - 1, - ), - generate_storage_log_metadata( - "BDA1617CC883E2251D3BE0FD9B3F3064BDA1617CC883E2251D3BE0FD9B3F3064", - "D14917FCB067922F92322025D1BA50B4D14917FCB067922F92322025D1BA50B4", - true, - false, - 2, - ), - generate_storage_log_metadata( - "77F035AD50811CFABD956F6F1B48E48277F035AD50811CFABD956F6F1B48E482", - "7CF33B959916CC9B56F21C427ED7CA187CF33B959916CC9B56F21C427ED7CA18", - true, - true, - 3, - ), - ]; - let precalculated_merkle_paths_provider = PrecalculatedMerklePathsProvider { - root_hash: string_to_array( - "4AF44B3D5D4F9C7B117A68351AAB65CF4AF44B3D5D4F9C7B117A68351AAB65CF", - ), - pending_leaves: logs, - next_enumeration_index: 4, - is_get_leaf_invoked: false, - }; - let (leafs, indices) = generate_leafs_indices(); - - let (_, first_writes, updates) = - precalculated_merkle_paths_provider.filter_renumerate(indices.iter(), leafs.into_iter()); - assert_eq!(1, first_writes.len()); - assert_eq!(1, updates.len()); - assert_eq!(3, first_writes[0].1.index); - assert_eq!(2, updates[0].index); -} - -#[test] -#[should_panic(expected = "leafs must be of same length as indexes")] -fn test_filter_renumerate_panic_when_leafs_and_indices_are_of_different_length() { - let logs = vec![ - generate_storage_log_metadata( - "DDC60818D8F7CFE42514F8EA3CC52806DDC60818D8F7CFE42514F8EA3CC52806", - "12E9FF974B0FAEE514AD4AC50E2BDC6E12E9FF974B0FAEE514AD4AC50E2BDC6E", - false, - false, - 1, - ), - generate_storage_log_metadata( - "BDA1617CC883E2251D3BE0FD9B3F3064BDA1617CC883E2251D3BE0FD9B3F3064", - "D14917FCB067922F92322025D1BA50B4D14917FCB067922F92322025D1BA50B4", - true, - false, - 2, - ), - generate_storage_log_metadata( - "77F035AD50811CFABD956F6F1B48E48277F035AD50811CFABD956F6F1B48E482", - "7CF33B959916CC9B56F21C427ED7CA187CF33B959916CC9B56F21C427ED7CA18", - true, - true, - 3, - ), - ]; - let precalculated_merkle_paths_provider = PrecalculatedMerklePathsProvider { - root_hash: string_to_array( - "4AF44B3D5D4F9C7B117A68351AAB65CF4AF44B3D5D4F9C7B117A68351AAB65CF", - ), - pending_leaves: logs, - next_enumeration_index: 4, - is_get_leaf_invoked: false, - }; - - let leafs = vec![ - generate_leaf( - 1, - "AD558076F725ED8B5E5B42920422E9BEAD558076F725ED8B5E5B42920422E9BE", - ), - generate_leaf( - 1, - "98A0EADBD6118391B744252DA348873C98A0EADBD6118391B744252DA348873C", - ), - generate_leaf( - 2, - "72868932BBB002043AF50363EEB65AE172868932BBB002043AF50363EEB65AE1", - ), - ]; - let indices = [ - string_to_array("5534D106E0B590953AC0FC7D65CA3B2E5534D106E0B590953AC0FC7D65CA3B2E"), - string_to_array("00309D72EF0AD9786DA9044109E1704B00309D72EF0AD9786DA9044109E1704B"), - ]; - - precalculated_merkle_paths_provider.filter_renumerate(indices.iter(), leafs.into_iter()); -} - -#[test] -#[should_panic(expected = "indexes must be of same length as leafs and pending leaves")] -fn test_filter_renumerate_panic_when_indices_and_pending_leaves_are_of_different_length() { - let logs = vec![ - generate_storage_log_metadata( - "DDC60818D8F7CFE42514F8EA3CC52806DDC60818D8F7CFE42514F8EA3CC52806", - "12E9FF974B0FAEE514AD4AC50E2BDC6E12E9FF974B0FAEE514AD4AC50E2BDC6E", - false, - false, - 1, - ), - generate_storage_log_metadata( - "BDA1617CC883E2251D3BE0FD9B3F3064BDA1617CC883E2251D3BE0FD9B3F3064", - "D14917FCB067922F92322025D1BA50B4D14917FCB067922F92322025D1BA50B4", - true, - false, - 2, - ), - generate_storage_log_metadata( - "77F035AD50811CFABD956F6F1B48E48277F035AD50811CFABD956F6F1B48E482", - "7CF33B959916CC9B56F21C427ED7CA187CF33B959916CC9B56F21C427ED7CA18", - true, - true, - 3, - ), - ]; - let precalculated_merkle_paths_provider = PrecalculatedMerklePathsProvider { - root_hash: string_to_array( - "4AF44B3D5D4F9C7B117A68351AAB65CF4AF44B3D5D4F9C7B117A68351AAB65CF", - ), - pending_leaves: logs, - next_enumeration_index: 4, - is_get_leaf_invoked: false, - }; - - let leafs = vec![ - generate_leaf( - 1, - "AD558076F725ED8B5E5B42920422E9BEAD558076F725ED8B5E5B42920422E9BE", - ), - generate_leaf( - 1, - "98A0EADBD6118391B744252DA348873C98A0EADBD6118391B744252DA348873C", - ), - generate_leaf( - 2, - "72868932BBB002043AF50363EEB65AE172868932BBB002043AF50363EEB65AE1", - ), - ]; - let indices = [ - string_to_array("5534D106E0B590953AC0FC7D65CA3B2E5534D106E0B590953AC0FC7D65CA3B2E"), - string_to_array("00309D72EF0AD9786DA9044109E1704B00309D72EF0AD9786DA9044109E1704B"), - string_to_array("930058748339A83E06F0D1D22937E92A930058748339A83E06F0D1D22937E92A"), - ]; - - precalculated_merkle_paths_provider.filter_renumerate(indices.iter(), leafs.into_iter()); -} - -fn generate_leafs_indices() -> (Vec, Vec<[u8; 32]>) { - let leafs = vec![ - generate_leaf( - 1, - "AD558076F725ED8B5E5B42920422E9BEAD558076F725ED8B5E5B42920422E9BE", - ), - generate_leaf( - 2, - "72868932BBB002043AF50363EEB65AE172868932BBB002043AF50363EEB65AE1", - ), - ]; - let indices = vec![ - string_to_array("5534D106E0B590953AC0FC7D65CA3B2E5534D106E0B590953AC0FC7D65CA3B2E"), - string_to_array("00309D72EF0AD9786DA9044109E1704B00309D72EF0AD9786DA9044109E1704B"), - ]; - (leafs, indices) -} - -fn generate_leaf(index: u64, value: &str) -> ZkSyncStorageLeaf { - ZkSyncStorageLeaf { - index, - value: string_to_array(value), - } -} - -fn string_to_array(value: &str) -> [u8; 32] { - let array_value: [u8; 32] = hex::decode(value) - .expect("Hex decoding failed") - .try_into() - .unwrap(); - array_value -} - -fn generate_storage_log_metadata( - root_hash: &str, - merkle_path: &str, - is_write: bool, - first_write: bool, - leaf_enumeration_index: u64, -) -> StorageLogMetadata { - StorageLogMetadata { - root_hash: string_to_array(root_hash), - is_write, - first_write, - merkle_paths: vec![string_to_array(merkle_path)], - leaf_hashed_key: Default::default(), - leaf_enumeration_index, - value_written: [0; 32], - value_read: [0; 32], - } -} diff --git a/core/lib/zksync_core/src/witness_generator/utils.rs b/core/lib/zksync_core/src/witness_generator/utils.rs deleted file mode 100644 index 35f5fd431ce0..000000000000 --- a/core/lib/zksync_core/src/witness_generator/utils.rs +++ /dev/null @@ -1,31 +0,0 @@ -use zksync_object_store::{CircuitKey, ObjectStore}; -use zksync_types::{ - proofs::AggregationRound, - zkevm_test_harness::{ - abstract_zksync_circuit::concrete_circuits::ZkSyncCircuit, bellman::bn256::Bn256, - witness::oracle::VmWitnessOracle, - }, - L1BatchNumber, -}; - -pub async fn save_prover_input_artifacts( - block_number: L1BatchNumber, - circuits: &[ZkSyncCircuit>], - object_store: &dyn ObjectStore, - aggregation_round: AggregationRound, -) -> Vec<(&'static str, String)> { - // We intentionally process circuits sequentially to not overwhelm the object store. - let mut types_and_urls = Vec::with_capacity(circuits.len()); - for (sequence_number, circuit) in circuits.iter().enumerate() { - let circuit_type = circuit.short_description(); - let circuit_key = CircuitKey { - block_number, - sequence_number, - circuit_type, - aggregation_round, - }; - let blob_url = object_store.put(circuit_key, circuit).await.unwrap(); - types_and_urls.push((circuit_type, blob_url)); - } - types_and_urls -} From 25355a3cf1d4cfe2e27dfe7e566683502c163287 Mon Sep 17 00:00:00 2001 From: Jack Hamer <47187316+JackHamer09@users.noreply.github.com> Date: Fri, 8 Dec 2023 12:03:26 +0200 Subject: [PATCH 066/268] fix: improve docs repositories (#570) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ * Add new/missing zkSync repositories * Add missing descriptions * Remove deprecated repositories ## Why ❔ To make the list up-to-date ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --- docs/repositories.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/repositories.md b/docs/repositories.md index 0902f38dcd80..efed07beec23 100644 --- a/docs/repositories.md +++ b/docs/repositories.md @@ -1,6 +1,6 @@ # Repositories -## zkSync Era +## zkSync ### Core components @@ -49,27 +49,27 @@ | Public repository | Description | | --------------------------------------------------------------- | ----------------------------------------------------------------------------- | | [local-setup](https://github.com/matter-labs/local-setup) | Docker-based zk server (together with L1), that can be used for local testing | -| [zksolc-bin](https://github.com/matter-labs/zksolc-bin) | repository with solc compiler binaries | -| [zkvyper-bin](https://github.com/matter-labs/zkvyper-bin) | repository with vyper compiler binaries | | [zksync-cli](https://github.com/matter-labs/zksync-cli) | Command line tool to interact with zksync | -| [hardhat-zksync](https://github.com/matter-labs/hardhat-zksync) | Plugins for hardhat | +| [block-explorer](https://github.com/matter-labs/block-explorer) | Online blockchain browser for viewing and analyzing zkSync chain | +| [dapp-portal](https://github.com/matter-labs/dapp-portal) | zkSync Wallet + Bridge DApp | +| [hardhat-zksync](https://github.com/matter-labs/hardhat-zksync) | zkSync Hardhat plugins | +| [zksolc-bin](https://github.com/matter-labs/zksolc-bin) | solc compiler binaries | +| [zkvyper-bin](https://github.com/matter-labs/zkvyper-bin) | vyper compiler binaries | ### Examples & documentation -| Public repository | Description | -| ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | -| [zksync-web-era-docs](https://github.com/matter-labs/zksync-web-era-docs) | Public documentation, API descriptions etc. Source code for [public docs](https://era.zksync.io/docs/) | -| [era-tutorial-examples](https://github.com/matter-labs/era-tutorial-examples) | List of tutorials | -| [custom-paymaster-tutorial](https://github.com/matter-labs/custom-paymaster-tutorial) | ?? | -| [daily-spendlimit-tutorial](https://github.com/matter-labs/daily-spendlimit-tutorial) | ?? | -| [custom-aa-tutorial](https://github.com/matter-labs/custom-aa-tutorial) | Tutorial for Account Abstraction | -| [era-hardhat-with-plugins](https://github.com/matter-labs/era-hardhat-with-plugins) | ?? | -| [zksync-hardhat-template](https://github.com/matter-labs/zksync-hardhat-template) | ?? | +| Public repository | Description | +| --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | +| [zksync-web-era-docs](https://github.com/matter-labs/zksync-web-era-docs) | [Public zkSync documentation](https://era.zksync.io/docs/), API descriptions etc. | +| [zksync-contract-templates](https://github.com/matter-labs/zksync-contract-templates) | Quick contract deployment and testing with tools like Hardhat on Solidity or Vyper | +| [zksync-frontend-templates](https://github.com/matter-labs/zksync-frontend-templates) | Rapid UI development with templates for Vue, React, Next.js, Nuxt, Vite, etc. | +| [zksync-scripting-templates](https://github.com/matter-labs/zksync-scripting-templates) | Automated interactions and advanced zkSync operations using Node.js | +| [tutorials](https://github.com/matter-labs/tutorials) | Tutorials for developing on zkSync | -## zkSync Lite (v1) +## zkSync Lite -| Public repository | Description | -| --------------------------------------------------------------------------- | ---------------------------------- | -| [zksync](https://github.com/matter-labs/zksync) | zksync Lite/v1 implementation | -| [zksync-docs](https://github.com/matter-labs/zksync-docs) | Public documentation for zkSync v1 | -| [zksync-dapp-checkout](https://github.com/matter-labs/zksync-dapp-checkout) | ?? | +| Public repository | Description | +| --------------------------------------------------------------------------- | -------------------------------- | +| [zksync](https://github.com/matter-labs/zksync) | zkSync Lite implementation | +| [zksync-docs](https://github.com/matter-labs/zksync-docs) | Public zkSync Lite documentation | +| [zksync-dapp-checkout](https://github.com/matter-labs/zksync-dapp-checkout) | Batch payments DApp | From 7c992323a31baafb882f6ebb448359e0635269b2 Mon Sep 17 00:00:00 2001 From: Marcin M <128217157+mm-zk@users.noreply.github.com> Date: Fri, 8 Dec 2023 11:25:35 +0100 Subject: [PATCH 067/268] docs(setup): Added TL;DR instructions for new zkstack setup (#621) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ * Added a TL;DR set of instructions needed to setup the system to run the zk stack from scratch. ## Why ❔ * To have a list of commands in one place. --- docs/setup-dev.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/docs/setup-dev.md b/docs/setup-dev.md index b33a38487695..37190606fa63 100644 --- a/docs/setup-dev.md +++ b/docs/setup-dev.md @@ -1,5 +1,34 @@ # Installing dependencies +## TL;DR + +If you run on 'clean' Debian on GCP: + +```bash +# Rust +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +# NVM +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash +# All necessary stuff +sudo apt-get install build-essential pkg-config cmake clang lldb lld libssl-dev postgresql docker-compose +# Docker +sudo usermod -aG docker YOUR_USER + +## You might need to re-connect (due to usermod change). + +# Node & yarn +nvm install node +npm install -g yarn +yarn set version 1.22.19 + +# SQL tools +cargo install sqlx-cli --version 0.5.13 +# Stop default postgres (as we'll use the docker one) +sudo systemctl stop postgresql +# Start docker. +sudo systemctl start docker +``` + ## Supported operating systems zkSync currently can be launched on any \*nix operating system (e.g. any linux distribution or MacOS). From 1e9e3bd7bc865fc087a6c8adecf70e17c6337553 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Fri, 8 Dec 2023 12:05:47 +0100 Subject: [PATCH 068/268] chore: upgrades local test network to cancun+deneb compatible one (#580) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Upgrades local testnet to `Cancun+Deneb` compatible one. So far: Cancun gets enabled: ``` 2023-12-01 21:57:49 INFO [12-01|20:57:49.152] Merge configured: 2023-12-01 21:57:49 INFO [12-01|20:57:49.152] - Hard-fork specification: https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/paris.md 2023-12-01 21:57:49 INFO [12-01|20:57:49.152] - Network known to be merged: true 2023-12-01 21:57:49 INFO [12-01|20:57:49.152] - Total terminal difficulty: 0 2023-12-01 21:57:49 INFO [12-01|20:57:49.152] 2023-12-01 21:57:49 INFO [12-01|20:57:49.152] Post-Merge hard forks (timestamp based): 2023-12-01 21:57:49 INFO [12-01|20:57:49.152] - Shanghai: @1701464272 (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/shanghai.md) 2023-12-01 21:57:49 INFO [12-01|20:57:49.152] - Cancun: @1701464272 ``` New network has been built into CI workflows. ## Why ❔ ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zk fmt` and `zk lint`. - [ ] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --- .github/workflows/build-core-template.yml | 2 +- .../workflows/build-external-node-docker.yml | 2 +- .github/workflows/build-local-node-docker.yml | 2 +- .github/workflows/ci-core-lint-reusable.yml | 3 +- .github/workflows/ci-core-reusable.yml | 13 +- .github/workflows/ci-docs-reusable.yml | 3 +- bin/ci_localnet_up | 12 ++ bin/ci_run | 2 +- core/tests/loadnext/src/config.rs | 4 +- core/tests/ts-integration/package.json | 2 +- .../tests/custom-erc20-bridge.test.ts | 4 +- .../tests/ts-integration/tests/system.test.ts | 20 +++ core/tests/upgrade-test/tests/upgrade.test.ts | 2 +- docker-compose.yml | 141 +++++++++++++++++- docker/geth/jwtsecret | 1 + docker/geth/standard-dev.json | 14 +- docker/prysm/config.yml | 30 ++++ infrastructure/zk/src/down.ts | 4 +- infrastructure/zk/src/up.ts | 34 ++++- 19 files changed, 257 insertions(+), 38 deletions(-) create mode 100755 bin/ci_localnet_up create mode 100644 docker/geth/jwtsecret create mode 100644 docker/prysm/config.yml diff --git a/.github/workflows/build-core-template.yml b/.github/workflows/build-core-template.yml index 95bb28947953..9be01b94c879 100644 --- a/.github/workflows/build-core-template.yml +++ b/.github/workflows/build-core-template.yml @@ -53,7 +53,7 @@ jobs: - name: start-services run: | echo "IMAGE_TAG_SUFFIX=${{ env.IMAGE_TAG_SUFFIX }}" >> .env - docker-compose -f docker-compose-runner.yml up -d zk geth postgres + ci_localnet_up ci_run sccache --start-server - name: init diff --git a/.github/workflows/build-external-node-docker.yml b/.github/workflows/build-external-node-docker.yml index cccbc84c6c9b..064ab8733cde 100644 --- a/.github/workflows/build-external-node-docker.yml +++ b/.github/workflows/build-external-node-docker.yml @@ -27,7 +27,7 @@ jobs: - name: start-services run: | - docker-compose -f docker-compose-runner.yml up -d zk geth postgres + ci_localnet_up - name: init run: | diff --git a/.github/workflows/build-local-node-docker.yml b/.github/workflows/build-local-node-docker.yml index 9880361206c1..6490798c73e5 100644 --- a/.github/workflows/build-local-node-docker.yml +++ b/.github/workflows/build-local-node-docker.yml @@ -27,7 +27,7 @@ jobs: - name: start-services run: | - docker-compose -f docker-compose-runner.yml up -d zk geth postgres + ci_localnet_up - name: init run: | diff --git a/.github/workflows/ci-core-lint-reusable.yml b/.github/workflows/ci-core-lint-reusable.yml index 1ca6746b2d80..046070dd9acd 100644 --- a/.github/workflows/ci-core-lint-reusable.yml +++ b/.github/workflows/ci-core-lint-reusable.yml @@ -20,8 +20,7 @@ jobs: - name: Start services run: | - docker-compose -f docker-compose-runner.yml pull - docker-compose -f docker-compose-runner.yml up --build -d zk + ci_localnet_up ci_run sccache --start-server - name: Setup db diff --git a/.github/workflows/ci-core-reusable.yml b/.github/workflows/ci-core-reusable.yml index 341a37a2c8c4..910909506818 100644 --- a/.github/workflows/ci-core-reusable.yml +++ b/.github/workflows/ci-core-reusable.yml @@ -22,8 +22,7 @@ jobs: - name: Start services run: | - docker-compose -f docker-compose-runner.yml pull - docker-compose -f docker-compose-runner.yml up --build -d geth zk postgres + ci_localnet_up ci_run sccache --start-server - name: Init @@ -63,8 +62,7 @@ jobs: - name: Start services run: | - docker-compose -f docker-compose-runner.yml pull - docker-compose -f docker-compose-runner.yml up --build -d geth zk postgres + ci_localnet_up ci_run sccache --start-server - name: Init @@ -92,7 +90,6 @@ jobs: ci_run cat server.log ci_run sccache --show-stats ci_run cat /tmp/sccache_log.txt - integration: runs-on: [matterlabs-ci-runner] @@ -135,8 +132,7 @@ jobs: - name: Start services run: | - docker-compose -f docker-compose-runner.yml pull - docker-compose -f docker-compose-runner.yml up --build -d geth zk postgres + ci_localnet_up ci_run sccache --start-server - name: Init @@ -233,8 +229,7 @@ jobs: - name: Start services run: | - docker-compose -f docker-compose-runner.yml pull - docker-compose -f docker-compose-runner.yml up --build -d geth zk postgres + ci_localnet_up ci_run sccache --start-server - name: Init diff --git a/.github/workflows/ci-docs-reusable.yml b/.github/workflows/ci-docs-reusable.yml index c0c9690542a1..297a90434e62 100644 --- a/.github/workflows/ci-docs-reusable.yml +++ b/.github/workflows/ci-docs-reusable.yml @@ -19,8 +19,7 @@ jobs: - name: Start services run: | - docker-compose -f docker-compose-runner.yml pull - docker-compose -f docker-compose-runner.yml up --build -d zk + ci_localnet_up - name: Lints run: | diff --git a/bin/ci_localnet_up b/bin/ci_localnet_up new file mode 100755 index 000000000000..1552540e94c9 --- /dev/null +++ b/bin/ci_localnet_up @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +cd $ZKSYNC_HOME + +mkdir -p ./volumes/postgres ./volumes/geth/keystore ./volumes/prysm/beacon ./volumes/prysm/validator +cp ./docker/prysm/config.yml ./volumes/prysm/config.yml +cp ./docker/geth/jwtsecret ./volumes/geth/jwtsecret +cp ./docker/geth/password.sec ./volumes/geth/password.sec +cp ./docker/geth/keystore/UTC--2019-04-06T21-13-27.692266000Z--8a91dc2d28b689474298d91899f0c1baf62cb85b ./volumes/geth/keystore/ +docker-compose -f docker-compose-local-genesis.yml up +docker-compose -f docker-compose-local-genesis.yml rm -f +docker-compose --profile runner up -d --wait diff --git a/bin/ci_run b/bin/ci_run index 18f11e33fec4..709e057bafaf 100755 --- a/bin/ci_run +++ b/bin/ci_run @@ -2,7 +2,7 @@ # Runs the command from within CI docker-compose environment. cd $ZKSYNC_HOME -compose_file="${RUNNER_COMPOSE_FILE:-docker-compose-runner.yml}" +compose_file="${RUNNER_COMPOSE_FILE:-docker-compose.yml}" # Pass environment variables explicitly if specified if [ ! -z "$PASSED_ENV_VARS" ]; then diff --git a/core/tests/loadnext/src/config.rs b/core/tests/loadnext/src/config.rs index b31cb5d3d8a0..6620e3aed579 100644 --- a/core/tests/loadnext/src/config.rs +++ b/core/tests/loadnext/src/config.rs @@ -312,9 +312,9 @@ impl TransactionWeights { impl Default for TransactionWeights { fn default() -> Self { Self { - deposit: 0.1, + deposit: 0.05, withdrawal: 0.5, - l1_transactions: 0.1, + l1_transactions: 0.05, l2_transactions: 1.0, } } diff --git a/core/tests/ts-integration/package.json b/core/tests/ts-integration/package.json index 63b51a6e1799..d296db7174f0 100644 --- a/core/tests/ts-integration/package.json +++ b/core/tests/ts-integration/package.json @@ -4,7 +4,7 @@ "license": "MIT", "private": true, "scripts": { - "test": "zk f jest --forceExit --testTimeout 60000", + "test": "zk f jest --forceExit --testTimeout 120000", "long-running-test": "zk f jest", "fee-test": "RUN_FEE_TEST=1 zk f jest -- fees.test.ts", "api-test": "zk f jest -- api/web3.test.ts api/debug.test.ts", diff --git a/core/tests/ts-integration/tests/custom-erc20-bridge.test.ts b/core/tests/ts-integration/tests/custom-erc20-bridge.test.ts index da8a78639757..ff153309cf2f 100644 --- a/core/tests/ts-integration/tests/custom-erc20-bridge.test.ts +++ b/core/tests/ts-integration/tests/custom-erc20-bridge.test.ts @@ -69,13 +69,13 @@ describe('Tests for the custom bridge behavior', () => { let l1bridge2 = new L1ERC20BridgeFactory(alice._signerL1()).attach(l1BridgeProxy.address); - const maxAttempts = 5; + const maxAttempts = 200; let ready = false; for (let i = 0; i < maxAttempts; ++i) { const l2Bridge = await l1bridge2.l2Bridge(); if (l2Bridge != ethers.constants.AddressZero) { const code = await alice._providerL2().getCode(l2Bridge); - if (code.length > 0) { + if (code.length > 2) { ready = true; break; } diff --git a/core/tests/ts-integration/tests/system.test.ts b/core/tests/ts-integration/tests/system.test.ts index 0eaf8c23b465..5353ad728d79 100644 --- a/core/tests/ts-integration/tests/system.test.ts +++ b/core/tests/ts-integration/tests/system.test.ts @@ -30,6 +30,26 @@ describe('System behavior checks', () => { alice = testMaster.mainAccount(); }); + test('Network should be supporting Cancun+Deneb', async () => { + const address_a = '0x000000000000000000000000000000000000000A'; + const address_b = '0x000000000000000000000000000000000000000b'; + + const transaction_a = { + to: address_a, + data: '0x' + }; + + await expect(alice.providerL1!.call(transaction_a)).rejects.toThrow(); + + const transaction_b = { + to: address_b, + data: '0x' + }; + + const result_b = await alice.providerL1!.call(transaction_b); + expect(result_b).toEqual('0x'); + }); + test('Should check that system contracts and SDK create same CREATE/CREATE2 addresses', async () => { const deployerContract = new zksync.Contract( zksync.utils.CONTRACT_DEPLOYER_ADDRESS, diff --git a/core/tests/upgrade-test/tests/upgrade.test.ts b/core/tests/upgrade-test/tests/upgrade.test.ts index 80e2c96c4b43..bf381a7b19e7 100644 --- a/core/tests/upgrade-test/tests/upgrade.test.ts +++ b/core/tests/upgrade-test/tests/upgrade.test.ts @@ -197,7 +197,7 @@ describe('Upgrade test', function () { ).wait(); // Wait for server to process L1 event. - await utils.sleep(10); + await utils.sleep(30); }); step('Check bootloader is updated on L2', async () => { diff --git a/docker-compose.yml b/docker-compose.yml index dc1f375645a0..7c2f72c94292 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,22 +1,147 @@ version: '3.2' services: + create-beacon-chain-genesis: + image: "gcr.io/prysmaticlabs/prysm/cmd/prysmctl:latest" + command: + - testnet + - generate-genesis + - --fork=deneb + - --num-validators=64 + - --genesis-time-delay=5 + - --output-ssz=/consensus/genesis.ssz + - --chain-config-file=/prysm/config.yml + - --geth-genesis-json-in=/geth/standard-dev.json + - --geth-genesis-json-out=/execution/genesis.json + volumes: + - ./docker/geth:/geth/:ro + - ./docker/prysm:/prysm/:ro + - ./volumes/geth:/execution + - ./volumes/prysm:/consensus + geth-genesis: + image: "ethereum/client-go:v1.13.5" + command: --datadir=/execution init /execution/genesis.json + volumes: + - ./volumes/geth:/execution + depends_on: + create-beacon-chain-genesis: + condition: service_completed_successfully geth: - image: "matterlabs/geth:latest" + image: "ethereum/client-go:v1.13.5" + ports: + - 8551:8551 + - 8545:8545 + - 8546:8546 + volumes: + - ./volumes/geth:/var/lib/geth/data + - ./docker/geth/:/geth/:ro + command: + - --networkid=9 + - --datadir=/var/lib/geth/data + - --http + - --http.api=engine,eth,web3,personal,net,debug + - --http.addr=0.0.0.0 + - --http.corsdomain=* + - --http.vhosts=* + - --ws + - --ws.addr=0.0.0.0 + - --ws.port=8546 + - --ws.origins=* + - --nodiscover + - --authrpc.addr=0.0.0.0 + - --authrpc.vhosts=* + - --authrpc.jwtsecret=/var/lib/geth/data/jwtsecret + - --allow-insecure-unlock + - --unlock=0x8a91dc2d28b689474298d91899f0c1baf62cb85b + - --password=/var/lib/geth/data/password.sec + - --syncmode=full + depends_on: + beacon: + condition: service_started + geth-genesis: + condition: service_completed_successfully + beacon: + image: "gcr.io/prylabs-dev/prysm/beacon-chain:v4.1.1" + command: + - --datadir=/consensus/beacon/ + - --min-sync-peers=0 + - --genesis-state=/consensus/genesis.ssz + - --bootstrap-node= + - --interop-eth1data-votes + - --chain-config-file=/consensus/config.yml + - --contract-deployment-block=0 + - --chain-id=9 + - --rpc-host=0.0.0.0 + - --grpc-gateway-host=0.0.0.0 + - --execution-endpoint=http://geth:8551 + - --accept-terms-of-use + - --jwt-secret=/execution/jwtsecret + - --suggested-fee-recipient=0x8a91dc2d28b689474298d91899f0c1baf62cb85b + - --minimum-peers-per-subnet=0 + - --enable-debug-rpc-endpoints ports: - - "8545:8545" - - "8546:8546" + - 4000:4000 + - 3500:3500 + - 8080:8080 + - 6060:6060 + - 9090:9090 volumes: - - type: bind - source: ./volumes/geth - target: /var/lib/geth/data + - ./volumes/prysm:/consensus + - ./volumes/geth:/execution + depends_on: + create-beacon-chain-genesis: + condition: service_completed_successfully + validator: + image: "gcr.io/prylabs-dev/prysm/validator:v4.1.1" + command: + - --beacon-rpc-provider=beacon:4000 + - --datadir=/consensus/validatordata + - --accept-terms-of-use + - --interop-num-validators=64 + - --interop-start-index=0 + - --chain-config-file=/consensus/config.yml + depends_on: + beacon: + condition: service_started + volumes: + - ./volumes/prysm:/consensus postgres: image: "postgres:14" ports: - - "5432:5432" + - 5432:5432 volumes: - type: bind source: ./volumes/postgres target: /var/lib/postgresql/data environment: - POSTGRES_HOST_AUTH_METHOD=trust - + + # This is specific to runner + zk: + image: "matterlabs/zk-environment:latest2.0-lightweight" + depends_on: + - geth + - postgres + - beacon + - validator + security_opt: + - seccomp:unconfined + command: tail -f /dev/null + volumes: + - .:/usr/src/zksync + - /usr/src/cache:/usr/src/cache + - /var/run/docker.sock:/var/run/docker.sock + environment: + - CACHE_DIR=/usr/src/cache + - SCCACHE_CACHE_SIZE=50g + - SCCACHE_GCS_BUCKET=matterlabs-infra-sccache-storage + - SCCACHE_GCS_SERVICE_ACCOUNT=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com + - SCCACHE_ERROR_LOG=/tmp/sccache_log.txt + - SCCACHE_GCS_RW_MODE=READ_WRITE + - CI=1 + - GITHUB_WORKSPACE=$GITHUB_WORKSPACE + env_file: + - ./.env + extra_hosts: + - "host:host-gateway" + profiles: + - runner diff --git a/docker/geth/jwtsecret b/docker/geth/jwtsecret new file mode 100644 index 000000000000..9fb480ef6a25 --- /dev/null +++ b/docker/geth/jwtsecret @@ -0,0 +1 @@ +0xfad2709d0bb03bf0e8ba3c99bea194575d3e98863133d1af638ed056d1d59345 diff --git a/docker/geth/standard-dev.json b/docker/geth/standard-dev.json index 556af1632a6d..9a5ea0717077 100644 --- a/docker/geth/standard-dev.json +++ b/docker/geth/standard-dev.json @@ -12,10 +12,12 @@ "istanbulBlock": 0, "berlinBlock": 0, "londonBlock": 0, - "clique": { - "period": 1, - "epoch": 30000 - } + "arrowGlacierBlock": 0, + "grayGlacierBlock": 0, + "shanghaiTime": 0, + "cancunTime": 0, + "terminalTotalDifficulty": 0, + "terminalTotalDifficultyPassed": true }, "nonce": "0x0", "timestamp": "0x5ca9158b", @@ -28,6 +30,10 @@ "0000000000000000000000000000000000000000": { "balance": "0x1" }, + "4242424242424242424242424242424242424242": { + "code": "0x60806040526004361061003f5760003560e01c806301ffc9a71461004457806322895118146100b6578063621fd130146101e3578063c5f2892f14610273575b600080fd5b34801561005057600080fd5b5061009c6004803603602081101561006757600080fd5b8101908080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919050505061029e565b604051808215151515815260200191505060405180910390f35b6101e1600480360360808110156100cc57600080fd5b81019080803590602001906401000000008111156100e957600080fd5b8201836020820111156100fb57600080fd5b8035906020019184600183028401116401000000008311171561011d57600080fd5b90919293919293908035906020019064010000000081111561013e57600080fd5b82018360208201111561015057600080fd5b8035906020019184600183028401116401000000008311171561017257600080fd5b90919293919293908035906020019064010000000081111561019357600080fd5b8201836020820111156101a557600080fd5b803590602001918460018302840111640100000000831117156101c757600080fd5b909192939192939080359060200190929190505050610370565b005b3480156101ef57600080fd5b506101f8610fd0565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561023857808201518184015260208101905061021d565b50505050905090810190601f1680156102655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561027f57600080fd5b50610288610fe2565b6040518082815260200191505060405180910390f35b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061036957507f85640907000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b603087879050146103cc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806116ec6026913960400191505060405180910390fd5b60208585905014610428576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260368152602001806116836036913960400191505060405180910390fd5b60608383905014610484576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602981526020018061175f6029913960400191505060405180910390fd5b670de0b6b3a76400003410156104e5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806117396026913960400191505060405180910390fd5b6000633b9aca0034816104f457fe5b061461054b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260338152602001806116b96033913960400191505060405180910390fd5b6000633b9aca00348161055a57fe5b04905067ffffffffffffffff80168111156105c0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260278152602001806117126027913960400191505060405180910390fd5b60606105cb82611314565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a610600602054611314565b60405180806020018060200180602001806020018060200186810386528e8e82818152602001925080828437600081840152601f19601f82011690508083019250505086810385528c8c82818152602001925080828437600081840152601f19601f82011690508083019250505086810384528a818151815260200191508051906020019080838360005b838110156106a657808201518184015260208101905061068b565b50505050905090810190601f1680156106d35780820380516001836020036101000a031916815260200191505b508681038352898982818152602001925080828437600081840152601f19601f820116905080830192505050868103825287818151815260200191508051906020019080838360005b8381101561073757808201518184015260208101905061071c565b50505050905090810190601f1680156107645780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b6040516020018084848082843780830192505050826fffffffffffffffffffffffffffffffff19166fffffffffffffffffffffffffffffffff1916815260100193505050506040516020818303038152906040526040518082805190602001908083835b6020831061080e57805182526020820191506020810190506020830392506107eb565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa158015610850573d6000803e3d6000fd5b5050506040513d602081101561086557600080fd5b8101908080519060200190929190505050905060006002808888600090604092610891939291906115da565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106108eb57805182526020820191506020810190506020830392506108c8565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa15801561092d573d6000803e3d6000fd5b5050506040513d602081101561094257600080fd5b8101908080519060200190929190505050600289896040908092610968939291906115da565b6000801b604051602001808484808284378083019250505082815260200193505050506040516020818303038152906040526040518082805190602001908083835b602083106109cd57805182526020820191506020810190506020830392506109aa565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa158015610a0f573d6000803e3d6000fd5b5050506040513d6020811015610a2457600080fd5b810190808051906020019092919050505060405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310610a8e5780518252602082019150602081019050602083039250610a6b565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa158015610ad0573d6000803e3d6000fd5b5050506040513d6020811015610ae557600080fd5b810190808051906020019092919050505090506000600280848c8c604051602001808481526020018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b60208310610b615780518252602082019150602081019050602083039250610b3e565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa158015610ba3573d6000803e3d6000fd5b5050506040513d6020811015610bb857600080fd5b8101908080519060200190929190505050600286600060401b866040516020018084805190602001908083835b60208310610c085780518252602082019150602081019050602083039250610be5565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610c935780518252602082019150602081019050602083039250610c70565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa158015610cd5573d6000803e3d6000fd5b5050506040513d6020811015610cea57600080fd5b810190808051906020019092919050505060405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310610d545780518252602082019150602081019050602083039250610d31565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa158015610d96573d6000803e3d6000fd5b5050506040513d6020811015610dab57600080fd5b81019080805190602001909291905050509050858114610e16576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252605481526020018061162f6054913960600191505060405180910390fd5b6001602060020a0360205410610e77576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061160e6021913960400191505060405180910390fd5b60016020600082825401925050819055506000602054905060008090505b6020811015610fb75760018083161415610ec8578260008260208110610eb757fe5b018190555050505050505050610fc7565b600260008260208110610ed757fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310610f335780518252602082019150602081019050602083039250610f10565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa158015610f75573d6000803e3d6000fd5b5050506040513d6020811015610f8a57600080fd5b8101908080519060200190929190505050925060028281610fa757fe5b0491508080600101915050610e95565b506000610fc057fe5b5050505050505b50505050505050565b6060610fdd602054611314565b905090565b6000806000602054905060008090505b60208110156111d057600180831614156110e05760026000826020811061101557fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310611071578051825260208201915060208101905060208303925061104e565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa1580156110b3573d6000803e3d6000fd5b5050506040513d60208110156110c857600080fd5b810190808051906020019092919050505092506111b6565b600283602183602081106110f057fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061114b5780518252602082019150602081019050602083039250611128565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa15801561118d573d6000803e3d6000fd5b5050506040513d60208110156111a257600080fd5b810190808051906020019092919050505092505b600282816111c057fe5b0491508080600101915050610ff2565b506002826111df602054611314565b600060401b6040516020018084815260200183805190602001908083835b6020831061122057805182526020820191506020810190506020830392506111fd565b6001836020036101000a0380198251168184511680821785525050505050509050018267ffffffffffffffff191667ffffffffffffffff1916815260180193505050506040516020818303038152906040526040518082805190602001908083835b602083106112a55780518252602082019150602081019050602083039250611282565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa1580156112e7573d6000803e3d6000fd5b5050506040513d60208110156112fc57600080fd5b81019080805190602001909291905050509250505090565b6060600867ffffffffffffffff8111801561132e57600080fd5b506040519080825280601f01601f1916602001820160405280156113615781602001600182028036833780820191505090505b50905060008260c01b90508060076008811061137957fe5b1a60f81b8260008151811061138a57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350806006600881106113c657fe5b1a60f81b826001815181106113d757fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060056008811061141357fe5b1a60f81b8260028151811061142457fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060046008811061146057fe5b1a60f81b8260038151811061147157fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350806003600881106114ad57fe5b1a60f81b826004815181106114be57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350806002600881106114fa57fe5b1a60f81b8260058151811061150b57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060016008811061154757fe5b1a60f81b8260068151811061155857fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060006008811061159457fe5b1a60f81b826007815181106115a557fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535050919050565b600080858511156115ea57600080fd5b838611156115f757600080fd5b600185028301915084860390509450949250505056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a2646970667358221220230afd4b6e3551329e50f1239e08fa3ab7907b77403c4f237d9adf679e8e43cf64736f6c634300060b0033", + "balance": "0x0" + }, "8a91dc2d28b689474298d91899f0c1baf62cb85b": { "balance": "0x4B3B4CA85A86C47A098A224000000000" }, diff --git a/docker/prysm/config.yml b/docker/prysm/config.yml new file mode 100644 index 000000000000..0b674138155a --- /dev/null +++ b/docker/prysm/config.yml @@ -0,0 +1,30 @@ +CONFIG_NAME: interop +PRESET_BASE: interop + +# Genesis +GENESIS_FORK_VERSION: 0x20000089 + +# Altair +ALTAIR_FORK_EPOCH: 0 +ALTAIR_FORK_VERSION: 0x20000090 + +# Merge +BELLATRIX_FORK_EPOCH: 0 +BELLATRIX_FORK_VERSION: 0x20000091 +TERMINAL_TOTAL_DIFFICULTY: 0 + +# Capella +CAPELLA_FORK_EPOCH: 0 +CAPELLA_FORK_VERSION: 0x20000092 +MAX_WITHDRAWALS_PER_PAYLOAD: 16 + +# Deneb +DENEB_FORK_VERSION: 0x20000093 +DENEB_FORK_EPOCH: 0 + +# Time parameters +SECONDS_PER_SLOT: 2 +SLOTS_PER_EPOCH: 6 + +# Deposit contract +DEPOSIT_CONTRACT_ADDRESS: 0x4242424242424242424242424242424242424242 diff --git a/infrastructure/zk/src/down.ts b/infrastructure/zk/src/down.ts index 85f07cca6dfb..066e7852071b 100644 --- a/infrastructure/zk/src/down.ts +++ b/infrastructure/zk/src/down.ts @@ -2,7 +2,9 @@ import { Command } from 'commander'; import * as utils from './utils'; export async function down() { - await utils.spawn('docker-compose stop geth postgres'); + await utils.spawn('docker compose down -v'); + await utils.spawn('docker compose rm -s -f -v'); + await utils.spawn('rm -rf ./volumes/'); } export const command = new Command('down').description('stop development containers').action(down); diff --git a/infrastructure/zk/src/up.ts b/infrastructure/zk/src/up.ts index 94be874dc388..de0ef41cf8c7 100644 --- a/infrastructure/zk/src/up.ts +++ b/infrastructure/zk/src/up.ts @@ -1,20 +1,50 @@ import { Command } from 'commander'; import * as utils from './utils'; +import { down } from './down'; import fs from 'fs'; // Make sure that the volumes exists before starting the containers. function createVolumes() { fs.mkdirSync(`${process.env.ZKSYNC_HOME}/volumes/geth`, { recursive: true }); + fs.mkdirSync(`${process.env.ZKSYNC_HOME}/volumes/prysm/beacon`, { recursive: true }); + fs.mkdirSync(`${process.env.ZKSYNC_HOME}/volumes/prysm/validator`, { recursive: true }); fs.mkdirSync(`${process.env.ZKSYNC_HOME}/volumes/postgres`, { recursive: true }); + + fs.copyFileSync( + `${process.env.ZKSYNC_HOME}/docker/prysm/config.yml`, + `${process.env.ZKSYNC_HOME}/volumes/prysm/config.yml` + ); + + fs.copyFileSync( + `${process.env.ZKSYNC_HOME}/docker/geth/jwtsecret`, + `${process.env.ZKSYNC_HOME}/volumes/geth/jwtsecret` + ); + fs.copyFileSync( + `${process.env.ZKSYNC_HOME}/docker/geth/password.sec`, + `${process.env.ZKSYNC_HOME}/volumes/geth/password.sec` + ); + fs.mkdirSync(`${process.env.ZKSYNC_HOME}/volumes/geth/keystore`, { recursive: true }); + fs.copyFileSync( + `${process.env.ZKSYNC_HOME}/docker/geth/keystore/UTC--2019-04-06T21-13-27.692266000Z--8a91dc2d28b689474298d91899f0c1baf62cb85b`, + `${process.env.ZKSYNC_HOME}/volumes/geth/keystore/UTC--2019-04-06T21-13-27.692266000Z--8a91dc2d28b689474298d91899f0c1baf62cb85b` + ); } export async function up(composeFile?: string) { + await down(); + // There is some race on the filesystem, so backoff here + await utils.sleep(1); createVolumes(); if (composeFile) { await utils.spawn(`docker compose -f ${composeFile} up -d geth postgres`); } else { - await utils.spawn('docker compose up -d geth postgres'); + await utils.spawn('docker compose up -d'); } } -export const command = new Command('up').description('start development containers').action(up); +export const command = new Command('up') + .description('start development containers') + .option('--docker-file', 'path to a custom docker file') + .action(async (cmd) => { + await up(cmd.dockerFile); + }); From d91b55bcca9002d0fb0b0285a090eb3abeab6348 Mon Sep 17 00:00:00 2001 From: Marcin M <128217157+mm-zk@users.noreply.github.com> Date: Fri, 8 Dec 2023 13:22:09 +0100 Subject: [PATCH 069/268] chore(zk init): Removed plonk setup (#638) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ * Removed plonk setup (where we downloaded the CRS keys) ## Why ❔ * It is needed only for prover setup (and then this is handled within the prover_setup.ts directly) * it will save a lot of network bandwidth and time during zk stack setup --- docs/launch.md | 3 --- infrastructure/zk/src/hyperchain_wizard.ts | 2 -- infrastructure/zk/src/init.ts | 14 ++--------- infrastructure/zk/src/run/run.ts | 28 ---------------------- 4 files changed, 2 insertions(+), 45 deletions(-) diff --git a/docs/launch.md b/docs/launch.md index 90ee266eeb55..b463655719c6 100644 --- a/docs/launch.md +++ b/docs/launch.md @@ -17,9 +17,6 @@ zk # installs and builds zk itself zk init ``` -During the first initialization you have to download around 8 GB of setup files, this should be done once. If you have a -problem on this step of the initialization, see help for the `zk run plonk-setup` command. - If you face any other problems with the `zk init` command, go to the [Troubleshooting](#Troubleshooting) section at the end of this file. There are solutions for some common error cases. diff --git a/infrastructure/zk/src/hyperchain_wizard.ts b/infrastructure/zk/src/hyperchain_wizard.ts index 05997127ca8a..0a7d4b273c03 100644 --- a/infrastructure/zk/src/hyperchain_wizard.ts +++ b/infrastructure/zk/src/hyperchain_wizard.ts @@ -58,7 +58,6 @@ async function initHyperchain() { const initArgs: InitArgs = { skipSubmodulesCheckout: false, skipEnvSetup: true, - skipPlonkStep: true, governorPrivateKeyArgs: ['--private-key', governorPrivateKey], deployerL2ContractInput: { args: ['--private-key', deployerPrivateKey], @@ -791,7 +790,6 @@ async function configDemoHyperchain(cmd: Command) { const initArgs: InitArgs = { skipSubmodulesCheckout: false, skipEnvSetup: cmd.skipEnvSetup, - skipPlonkStep: true, governorPrivateKeyArgs: ['--private-key', governorPrivateKey], deployerL2ContractInput: { args: ['--private-key', deployerPrivateKey], diff --git a/infrastructure/zk/src/init.ts b/infrastructure/zk/src/init.ts index 0fe3c1137637..1de4e1b6a06f 100644 --- a/infrastructure/zk/src/init.ts +++ b/infrastructure/zk/src/init.ts @@ -18,21 +18,14 @@ const success = chalk.green; const timestamp = chalk.grey; export async function init(initArgs: InitArgs = DEFAULT_ARGS) { - const { - skipSubmodulesCheckout, - skipEnvSetup, - skipPlonkStep, - testTokens, - governorPrivateKeyArgs, - deployerL2ContractInput - } = initArgs; + const { skipSubmodulesCheckout, skipEnvSetup, testTokens, governorPrivateKeyArgs, deployerL2ContractInput } = + initArgs; if (!process.env.CI && !skipEnvSetup) { await announced('Pulling images', docker.pull()); await announced('Checking environment', checkEnv()); await announced('Checking git hooks', env.gitHooks()); await announced('Setting up containers', up()); - !skipPlonkStep && (await announced('Checking PLONK setup', run.plonkSetup())); } if (!skipSubmodulesCheckout) { await announced('Checkout system-contracts submodule', submoduleUpdate()); @@ -149,7 +142,6 @@ async function checkEnv() { export interface InitArgs { skipSubmodulesCheckout: boolean; skipEnvSetup: boolean; - skipPlonkStep: boolean; governorPrivateKeyArgs: any[]; deployerL2ContractInput: { args: any[]; @@ -165,7 +157,6 @@ export interface InitArgs { const DEFAULT_ARGS: InitArgs = { skipSubmodulesCheckout: false, skipEnvSetup: false, - skipPlonkStep: false, governorPrivateKeyArgs: [], deployerL2ContractInput: { args: [], includePaymaster: true, includeL2WETH: true }, testTokens: { deploy: true, args: [] } @@ -179,7 +170,6 @@ export const initCommand = new Command('init') const initArgs: InitArgs = { skipSubmodulesCheckout: cmd.skipSubmodulesCheckout, skipEnvSetup: cmd.skipEnvSetup, - skipPlonkStep: false, governorPrivateKeyArgs: [], deployerL2ContractInput: { args: [], includePaymaster: true, includeL2WETH: true }, testTokens: { deploy: true, args: [] } diff --git a/infrastructure/zk/src/run/run.ts b/infrastructure/zk/src/run/run.ts index 3f17fb3e54ce..12568cd5851d 100644 --- a/infrastructure/zk/src/run/run.ts +++ b/infrastructure/zk/src/run/run.ts @@ -63,23 +63,6 @@ export async function deployTestkit(genesisRoot: string) { await utils.spawn(`yarn l1-contracts deploy-testkit --genesis-root ${genesisRoot}`); } -export async function plonkSetup(powers?: number[]) { - if (!powers) { - powers = [20, 21, 22, 23, 24, 25, 26]; - } - const URL = 'https://storage.googleapis.com/universal-setup'; - fs.mkdirSync('keys/setup', { recursive: true }); - process.chdir('keys/setup'); - for (let i = 0; i < powers.length; i++) { - const power = powers[i]; - if (!fs.existsSync(`setup_2^${power}.key`)) { - await utils.spawn(`curl -LO ${URL}/setup_2^${power}.key`); - await utils.sleep(1); - } - } - process.chdir(process.env.ZKSYNC_HOME as string); -} - export async function revertReason(txHash: string, web3url?: string) { await utils.spawn(`yarn l1-contracts ts-node scripts/revert-reason.ts ${txHash} ${web3url || ''}`); } @@ -159,17 +142,6 @@ command await tokenInfo(address); }); -command - .command('plonk-setup [powers]') - .description('download missing keys') - .action(async (powers?: string) => { - const powersArray = powers - ?.split(' ') - .map((x) => parseInt(x)) - .filter((x) => !Number.isNaN(x)); - await plonkSetup(powersArray); - }); - command .command('deploy-testkit') .description('deploy testkit contracts') From 21fc6ff27e4a28cbf8fa7d4d71a2326b84819886 Mon Sep 17 00:00:00 2001 From: Marcin M <128217157+mm-zk@users.noreply.github.com> Date: Fri, 8 Dec 2023 14:50:08 +0100 Subject: [PATCH 070/268] chore(CI): Speeding up docker builds in CI (#640) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ * Removed zk contract compilation that was run before prover docker builds * Removed docker builds for old prover and old circuit_synthesizer * Downloading CRS file only for the snark wrapper / compressor job ## Why ❔ * to speed up docker CI that runs on every PR --- .github/workflows/build-prover-template.yml | 14 +++++--------- docker/proof-fri-compressor/Dockerfile | 2 +- docker/prover-fri-gateway/Dockerfile | 2 +- docker/prover-fri/Dockerfile | 2 +- docker/prover-gpu-fri/Dockerfile | 2 +- docker/witness-generator/Dockerfile | 2 +- docker/witness-vector-generator/Dockerfile | 2 +- prover/circuit_synthesizer/README.md | 1 + prover/prover/README.md | 2 +- 9 files changed, 13 insertions(+), 16 deletions(-) create mode 100644 prover/circuit_synthesizer/README.md diff --git a/.github/workflows/build-prover-template.yml b/.github/workflows/build-prover-template.yml index 4151d03a697a..0d9009f1cfec 100644 --- a/.github/workflows/build-prover-template.yml +++ b/.github/workflows/build-prover-template.yml @@ -45,8 +45,6 @@ jobs: matrix: component: - witness-generator - - prover-v2 - - circuit-synthesizer - prover-fri - prover-gpu-fri - witness-vector-generator @@ -74,16 +72,14 @@ jobs: - name: init run: | ci_run git config --global --add safe.directory /usr/src/zksync - ci_run git config --global --add safe.directory /usr/src/zksync/sdk/binaryen ci_run git config --global --add safe.directory /usr/src/zksync/etc/system-contracts ci_run git config --global --add safe.directory /usr/src/zksync/contracts ci_run zk - ci_run zk clean --all - ci_run zk run yarn - ci_run cp etc/tokens/{test,localhost}.json - ci_run zk compiler all - ci_run zk contract build - ci_run zk f yarn run l2-contracts build + + # We need the CRS only for the fri compressor. + - name: download CRS + if: matrix.component == 'proof-fri-compressor' + run: | ci_run curl -LO https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_2\^26.key - name: login to Docker registries diff --git a/docker/proof-fri-compressor/Dockerfile b/docker/proof-fri-compressor/Dockerfile index a46547013114..e18c0c27f552 100644 --- a/docker/proof-fri-compressor/Dockerfile +++ b/docker/proof-fri-compressor/Dockerfile @@ -19,7 +19,7 @@ RUN curl https://sh.rustup.rs -sSf | bash -s -- -y && \ WORKDIR /usr/src/zksync COPY . . -RUN cargo build --release +RUN cargo build --release --bin zksync_proof_fri_compressor FROM debian:bookworm-slim diff --git a/docker/prover-fri-gateway/Dockerfile b/docker/prover-fri-gateway/Dockerfile index b0f119495517..256621e8df75 100644 --- a/docker/prover-fri-gateway/Dockerfile +++ b/docker/prover-fri-gateway/Dockerfile @@ -17,7 +17,7 @@ RUN curl https://sh.rustup.rs -sSf | bash -s -- -y && \ WORKDIR /usr/src/zksync COPY . . -RUN cargo build --release +RUN cargo build --release --bin zksync_prover_fri_gateway FROM debian:bookworm-slim RUN apt-get update && apt-get install -y curl libpq5 ca-certificates && rm -rf /var/lib/apt/lists/* diff --git a/docker/prover-fri/Dockerfile b/docker/prover-fri/Dockerfile index a4406b34ec76..8244aea06b28 100644 --- a/docker/prover-fri/Dockerfile +++ b/docker/prover-fri/Dockerfile @@ -17,7 +17,7 @@ RUN curl https://sh.rustup.rs -sSf | bash -s -- -y && \ WORKDIR /usr/src/zksync COPY . . -RUN cargo build --release +RUN cargo build --release --bin zksync_prover_fri FROM debian:bookworm-slim RUN apt-get update && apt-get install -y curl libpq5 ca-certificates && rm -rf /var/lib/apt/lists/* diff --git a/docker/prover-gpu-fri/Dockerfile b/docker/prover-gpu-fri/Dockerfile index b3edd5758ab5..6a01926051ef 100644 --- a/docker/prover-gpu-fri/Dockerfile +++ b/docker/prover-gpu-fri/Dockerfile @@ -24,7 +24,7 @@ RUN curl -Lo cmake-3.24.2-linux-x86_64.sh https://github.com/Kitware/CMake/relea WORKDIR /usr/src/zksync COPY . . -RUN cargo build --release --features "gpu" +RUN cargo build --release --features "gpu" --bin zksync_prover_fri FROM nvidia/cuda:12.2.0-runtime-ubuntu22.04 diff --git a/docker/witness-generator/Dockerfile b/docker/witness-generator/Dockerfile index f2fe7926a929..f431339d3e96 100644 --- a/docker/witness-generator/Dockerfile +++ b/docker/witness-generator/Dockerfile @@ -17,7 +17,7 @@ RUN curl https://sh.rustup.rs -sSf | bash -s -- -y && \ WORKDIR /usr/src/zksync COPY . . -RUN cargo build --release +RUN cargo build --release --bin zksync_witness_generator FROM debian:bookworm-slim diff --git a/docker/witness-vector-generator/Dockerfile b/docker/witness-vector-generator/Dockerfile index b366006009e8..5861f3e51622 100644 --- a/docker/witness-vector-generator/Dockerfile +++ b/docker/witness-vector-generator/Dockerfile @@ -17,7 +17,7 @@ RUN curl https://sh.rustup.rs -sSf | bash -s -- -y && \ WORKDIR /usr/src/zksync COPY . . -RUN cargo build --release +RUN cargo build --release --bin zksync_witness_vector_generator FROM debian:bookworm-slim diff --git a/prover/circuit_synthesizer/README.md b/prover/circuit_synthesizer/README.md new file mode 100644 index 000000000000..cc40029cc03d --- /dev/null +++ b/prover/circuit_synthesizer/README.md @@ -0,0 +1 @@ +# PART OF OLD PROVER - OBSOLETE diff --git a/prover/prover/README.md b/prover/prover/README.md index 2d70cf056d4b..6ccb690a33f3 100644 --- a/prover/prover/README.md +++ b/prover/prover/README.md @@ -1,4 +1,4 @@ -# Readme +# OLD PROVER - OBSOLETE For compiling locally (no cuda) set `features=["legacy"], default-features=false` for: From 4c382a053b7ab24e1d5fc32b79d952da11e22664 Mon Sep 17 00:00:00 2001 From: Marcin M <128217157+mm-zk@users.noreply.github.com> Date: Fri, 8 Dec 2023 22:42:55 +0100 Subject: [PATCH 071/268] chore(zk): finishing migration to docker compose (#646) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ * replaced docker-compose with docker compose ## Why ❔ * we moved to use docker compose (instead of docker-compose) - which is a newer version --- infrastructure/zk/src/docker.ts | 4 ++-- infrastructure/zk/src/init.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/infrastructure/zk/src/docker.ts b/infrastructure/zk/src/docker.ts index 9a4bf6e291a4..dbb598da3106 100644 --- a/infrastructure/zk/src/docker.ts +++ b/infrastructure/zk/src/docker.ts @@ -151,11 +151,11 @@ export async function push(image: string, cmd: Command) { } export async function restart(container: string) { - await utils.spawn(`docker-compose restart ${container}`); + await utils.spawn(`docker compose restart ${container}`); } export async function pull() { - await utils.spawn('docker-compose pull'); + await utils.spawn('docker compose pull'); } export const command = new Command('docker').description('docker management'); diff --git a/infrastructure/zk/src/init.ts b/infrastructure/zk/src/init.ts index 1de4e1b6a06f..a04111395a74 100644 --- a/infrastructure/zk/src/init.ts +++ b/infrastructure/zk/src/init.ts @@ -127,7 +127,7 @@ export async function submoduleUpdate() { } async function checkEnv() { - const tools = ['node', 'yarn', 'docker', 'docker-compose', 'cargo']; + const tools = ['node', 'yarn', 'docker', 'cargo']; for (const tool of tools) { await utils.exec(`which ${tool}`); } From 87c334ebde96e9deb6e835fcba4b8c477fe3c687 Mon Sep 17 00:00:00 2001 From: Artem Makhortov <13339874+artmakh@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:23:44 +0700 Subject: [PATCH 072/268] chore(ci): Pre-download compilers, as a workaround for old hardhat plugins (#645) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Add hack way to pre-download needed compileres ## Why ❔ Hardhat plugins, which we are using right now, are pretty often rate limited by github for downloading compilers, due to unoptimized download procedure. This issue was fixed in later versions of plugins, but we can't update _some_ of the hardhat plugins due to changes in deps for them. This pre-download will help us till the time we can update them. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zk fmt` and `zk lint`. - [ ] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --- .github/workflows/build-core-template.yml | 21 ++++++++++++++++ .../workflows/build-external-node-docker.yml | 21 ++++++++++++++++ .github/workflows/build-local-node-docker.yml | 21 ++++++++++++++++ .github/workflows/ci-core-reusable.yml | 24 ++++++++++++++++++- docker-compose.yml | 3 ++- 5 files changed, 88 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-core-template.yml b/.github/workflows/build-core-template.yml index 9be01b94c879..17e9b0e27811 100644 --- a/.github/workflows/build-core-template.yml +++ b/.github/workflows/build-core-template.yml @@ -22,6 +22,11 @@ on: type: string default: "push" required: false + compilers: + description: 'JSON of required compilers and their versions' + type: string + required: false + default: '[{ "zksolc": ["1.3.14", "1.3.16", "1.3.17"] } , { "zkvyper": ["1.3.13"] }]' jobs: build-images: @@ -50,6 +55,22 @@ jobs: echo CI=1 >> .env echo IN_DOCKER=1 >> .env + # TODO: Remove after when we can upgrade hardhat-plugins + - name: pre-download compiilers + run: | + # Download needed versions of vyper compiler + # Not sanitized due to unconventional path and tags + mkdir -p ./hardhat-nodejs/compilers-v2/vyper/linux + wget -O ./hardhat-nodejs/compilers-v2/vyper/linux/0.3.10 https://github.com/vyperlang/vyper/releases/download/v0.3.10/vyper.0.3.10+commit.91361694.linux + chmod +x ./hardhat-nodejs/compilers-v2/vyper/linux/0.3.10 + + COMPILERS_JSON='${{ inputs.compilers }}' + echo "$COMPILERS_JSON" | jq -r '.[] | to_entries[] | .key as $compiler | .value[] | "\(.),\($compiler)"' | while IFS=, read -r version compiler; do + mkdir -p "./hardhat-nodejs/compilers-v2/$compiler" + wget -O "./hardhat-nodejs/compilers-v2/$compiler/${compiler}-v${version}" "https://github.com/matter-labs/${compiler}-bin/releases/download/v${version}/${compiler}-linux-amd64-musl-v${version}" + chmod +x "./hardhat-nodejs/compilers-v2/$compiler/${compiler}-v${version}" + done + - name: start-services run: | echo "IMAGE_TAG_SUFFIX=${{ env.IMAGE_TAG_SUFFIX }}" >> .env diff --git a/.github/workflows/build-external-node-docker.yml b/.github/workflows/build-external-node-docker.yml index 064ab8733cde..f9926efb516c 100644 --- a/.github/workflows/build-external-node-docker.yml +++ b/.github/workflows/build-external-node-docker.yml @@ -7,6 +7,11 @@ on: type: string required: false default: "latest2.0" + compilers: + description: 'JSON of required compilers and their versions' + type: string + required: false + default: '[{ "zksolc": ["1.3.14", "1.3.16", "1.3.17"] } , { "zkvyper": ["1.3.13"] }]' jobs: build-images: @@ -25,6 +30,22 @@ jobs: echo CI=1 >> .env echo IN_DOCKER=1 >> .env + # TODO: Remove after when we can upgrade hardhat-plugins + - name: pre-download compiilers + run: | + # Download needed versions of vyper compiler + # Not sanitized due to unconventional path and tags + mkdir -p ./hardhat-nodejs/compilers-v2/vyper/linux + wget -O ./hardhat-nodejs/compilers-v2/vyper/linux/0.3.10 https://github.com/vyperlang/vyper/releases/download/v0.3.10/vyper.0.3.10+commit.91361694.linux + chmod +x ./hardhat-nodejs/compilers-v2/vyper/linux/0.3.10 + + COMPILERS_JSON='${{ inputs.compilers }}' + echo "$COMPILERS_JSON" | jq -r '.[] | to_entries[] | .key as $compiler | .value[] | "\(.),\($compiler)"' | while IFS=, read -r version compiler; do + mkdir -p "./hardhat-nodejs/compilers-v2/$compiler" + wget -O "./hardhat-nodejs/compilers-v2/$compiler/${compiler}-v${version}" "https://github.com/matter-labs/${compiler}-bin/releases/download/v${version}/${compiler}-linux-amd64-musl-v${version}" + chmod +x "./hardhat-nodejs/compilers-v2/$compiler/${compiler}-v${version}" + done + - name: start-services run: | ci_localnet_up diff --git a/.github/workflows/build-local-node-docker.yml b/.github/workflows/build-local-node-docker.yml index 6490798c73e5..d610d161c817 100644 --- a/.github/workflows/build-local-node-docker.yml +++ b/.github/workflows/build-local-node-docker.yml @@ -7,6 +7,11 @@ on: type: string required: false default: "latest2.0" + compilers: + description: 'JSON of required compilers and their versions' + type: string + required: false + default: '[{ "zksolc": ["1.3.14", "1.3.16", "1.3.17"] } , { "zkvyper": ["1.3.13"] }]' jobs: build-images: @@ -25,6 +30,22 @@ jobs: echo CI=1 >> .env echo IN_DOCKER=1 >> .env + # TODO: Remove after when we can upgrade hardhat-plugins + - name: pre-download compiilers + run: | + # Download needed versions of vyper compiler + # Not sanitized due to unconventional path and tags + mkdir -p ./hardhat-nodejs/compilers-v2/vyper/linux + wget -O ./hardhat-nodejs/compilers-v2/vyper/linux/0.3.10 https://github.com/vyperlang/vyper/releases/download/v0.3.10/vyper.0.3.10+commit.91361694.linux + chmod +x ./hardhat-nodejs/compilers-v2/vyper/linux/0.3.10 + + COMPILERS_JSON='${{ inputs.compilers }}' + echo "$COMPILERS_JSON" | jq -r '.[] | to_entries[] | .key as $compiler | .value[] | "\(.),\($compiler)"' | while IFS=, read -r version compiler; do + mkdir -p "./hardhat-nodejs/compilers-v2/$compiler" + wget -O "./hardhat-nodejs/compilers-v2/$compiler/${compiler}-v${version}" "https://github.com/matter-labs/${compiler}-bin/releases/download/v${version}/${compiler}-linux-amd64-musl-v${version}" + chmod +x "./hardhat-nodejs/compilers-v2/$compiler/${compiler}-v${version}" + done + - name: start-services run: | ci_localnet_up diff --git a/.github/workflows/ci-core-reusable.yml b/.github/workflows/ci-core-reusable.yml index 910909506818..11c2b990dad4 100644 --- a/.github/workflows/ci-core-reusable.yml +++ b/.github/workflows/ci-core-reusable.yml @@ -1,6 +1,12 @@ name: Workflow template for CI jobs for Core Components on: workflow_call: + inputs: + compilers: + description: 'JSON of required compilers and their versions' + type: string + required: false + default: '[{ "zksolc": ["1.3.14", "1.3.16", "1.3.17"] } , { "zkvyper": ["1.3.13"] }]' jobs: lint: @@ -20,6 +26,22 @@ jobs: echo $(pwd)/bin >> $GITHUB_PATH echo IN_DOCKER=1 >> .env + # TODO: Remove when we after upgrade of hardhat-plugins + - name: pre-download compiilers + run: | + # Download needed versions of vyper compiler + # Not sanitized due to unconventional path and tags + mkdir -p ./hardhat-nodejs/compilers-v2/vyper/linux + wget -O ./hardhat-nodejs/compilers-v2/vyper/linux/0.3.10 https://github.com/vyperlang/vyper/releases/download/v0.3.10/vyper.0.3.10+commit.91361694.linux + chmod +x ./hardhat-nodejs/compilers-v2/vyper/linux/0.3.10 + + COMPILERS_JSON='${{ inputs.compilers }}' + echo "$COMPILERS_JSON" | jq -r '.[] | to_entries[] | .key as $compiler | .value[] | "\(.),\($compiler)"' | while IFS=, read -r version compiler; do + mkdir -p "./hardhat-nodejs/compilers-v2/$compiler" + wget -O "./hardhat-nodejs/compilers-v2/$compiler/${compiler}-v${version}" "https://github.com/matter-labs/${compiler}-bin/releases/download/v${version}/${compiler}-linux-amd64-musl-v${version}" + chmod +x "./hardhat-nodejs/compilers-v2/$compiler/${compiler}-v${version}" + done + - name: Start services run: | ci_localnet_up @@ -132,7 +154,7 @@ jobs: - name: Start services run: | - ci_localnet_up + ci_localnet_up ci_run sccache --start-server - name: Init diff --git a/docker-compose.yml b/docker-compose.yml index 7c2f72c94292..0df03bcb5d0c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -114,7 +114,7 @@ services: target: /var/lib/postgresql/data environment: - POSTGRES_HOST_AUTH_METHOD=trust - + # This is specific to runner zk: image: "matterlabs/zk-environment:latest2.0-lightweight" @@ -130,6 +130,7 @@ services: - .:/usr/src/zksync - /usr/src/cache:/usr/src/cache - /var/run/docker.sock:/var/run/docker.sock + - ./hardhat-nodejs:/root/.cache/hardhat-nodejs environment: - CACHE_DIR=/usr/src/cache - SCCACHE_CACHE_SIZE=50g From d4cc6e564642b4c49ef4a546cd1c86821327683c Mon Sep 17 00:00:00 2001 From: Alex Ostrovski Date: Mon, 11 Dec 2023 10:48:07 +0200 Subject: [PATCH 073/268] feat(en): Remove `SyncBlock.root_hash` (#633) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Removes `root_hash` field from `SyncBlock`. ## Why ❔ It's not used anywhere, set to a dummy value (miniblock hash) and doesn't make sense in general (state hashes are computed on L1 batch level, not miniblock level). ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. - [x] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --- core/lib/dal/sqlx-data.json | 190 +++++++++---------- core/lib/dal/src/models/storage_sync.rs | 17 +- core/lib/dal/src/sync_dal.rs | 163 ++++++++++++++-- core/lib/types/src/api/en.rs | 2 - core/lib/zksync_core/src/sync_layer/tests.rs | 1 - 5 files changed, 241 insertions(+), 132 deletions(-) diff --git a/core/lib/dal/sqlx-data.json b/core/lib/dal/sqlx-data.json index d35fd3205080..36be2088171b 100644 --- a/core/lib/dal/sqlx-data.json +++ b/core/lib/dal/sqlx-data.json @@ -5328,6 +5328,98 @@ }, "query": "SELECT id FROM prover_fri_protocol_versions WHERE recursion_circuits_set_vks_hash = $1 AND recursion_leaf_level_vk_hash = $2 AND recursion_node_level_vk_hash = $3 AND recursion_scheduler_level_vk_hash = $4 " }, + "6ad9309a48f387da7d437ba6ed94aa7b6a42813e5ee566104b904f0bee0dfde7": { + "describe": { + "columns": [ + { + "name": "number", + "ordinal": 0, + "type_info": "Int8" + }, + { + "name": "l1_batch_number!", + "ordinal": 1, + "type_info": "Int8" + }, + { + "name": "last_batch_miniblock?", + "ordinal": 2, + "type_info": "Int8" + }, + { + "name": "timestamp", + "ordinal": 3, + "type_info": "Int8" + }, + { + "name": "l1_gas_price", + "ordinal": 4, + "type_info": "Int8" + }, + { + "name": "l2_fair_gas_price", + "ordinal": 5, + "type_info": "Int8" + }, + { + "name": "bootloader_code_hash", + "ordinal": 6, + "type_info": "Bytea" + }, + { + "name": "default_aa_code_hash", + "ordinal": 7, + "type_info": "Bytea" + }, + { + "name": "virtual_blocks", + "ordinal": 8, + "type_info": "Int8" + }, + { + "name": "hash", + "ordinal": 9, + "type_info": "Bytea" + }, + { + "name": "consensus", + "ordinal": 10, + "type_info": "Jsonb" + }, + { + "name": "protocol_version!", + "ordinal": 11, + "type_info": "Int4" + }, + { + "name": "fee_account_address?", + "ordinal": 12, + "type_info": "Bytea" + } + ], + "nullable": [ + false, + null, + null, + false, + false, + false, + true, + true, + false, + false, + true, + true, + false + ], + "parameters": { + "Left": [ + "Int8" + ] + } + }, + "query": "SELECT miniblocks.number, COALESCE(miniblocks.l1_batch_number, (SELECT (max(number) + 1) FROM l1_batches)) as \"l1_batch_number!\", (SELECT max(m2.number) FROM miniblocks m2 WHERE miniblocks.l1_batch_number = m2.l1_batch_number) as \"last_batch_miniblock?\", miniblocks.timestamp, miniblocks.l1_gas_price, miniblocks.l2_fair_gas_price, miniblocks.bootloader_code_hash, miniblocks.default_aa_code_hash, miniblocks.virtual_blocks, miniblocks.hash, miniblocks.consensus, miniblocks.protocol_version as \"protocol_version!\", l1_batches.fee_account_address as \"fee_account_address?\" FROM miniblocks LEFT JOIN l1_batches ON miniblocks.l1_batch_number = l1_batches.number WHERE miniblocks.number = $1" + }, "6b53e5cb619c9649d28ae33df6a43e6984e2d9320f894f3d04156a2d1235bb60": { "describe": { "columns": [ @@ -5613,104 +5705,6 @@ }, "query": "SELECT * FROM call_traces WHERE tx_hash IN (SELECT hash FROM transactions WHERE miniblock_number = $1)" }, - "7947dd8e7d6c138146f7ebe6b1e89fcd494b2679ac4e9fcff6aa2b2944aeed50": { - "describe": { - "columns": [ - { - "name": "number", - "ordinal": 0, - "type_info": "Int8" - }, - { - "name": "l1_batch_number!", - "ordinal": 1, - "type_info": "Int8" - }, - { - "name": "last_batch_miniblock?", - "ordinal": 2, - "type_info": "Int8" - }, - { - "name": "timestamp", - "ordinal": 3, - "type_info": "Int8" - }, - { - "name": "root_hash?", - "ordinal": 4, - "type_info": "Bytea" - }, - { - "name": "l1_gas_price", - "ordinal": 5, - "type_info": "Int8" - }, - { - "name": "l2_fair_gas_price", - "ordinal": 6, - "type_info": "Int8" - }, - { - "name": "bootloader_code_hash", - "ordinal": 7, - "type_info": "Bytea" - }, - { - "name": "default_aa_code_hash", - "ordinal": 8, - "type_info": "Bytea" - }, - { - "name": "virtual_blocks", - "ordinal": 9, - "type_info": "Int8" - }, - { - "name": "hash", - "ordinal": 10, - "type_info": "Bytea" - }, - { - "name": "consensus", - "ordinal": 11, - "type_info": "Jsonb" - }, - { - "name": "protocol_version!", - "ordinal": 12, - "type_info": "Int4" - }, - { - "name": "fee_account_address?", - "ordinal": 13, - "type_info": "Bytea" - } - ], - "nullable": [ - false, - null, - null, - false, - false, - false, - false, - true, - true, - false, - false, - true, - true, - false - ], - "parameters": { - "Left": [ - "Int8" - ] - } - }, - "query": "SELECT miniblocks.number, COALESCE(miniblocks.l1_batch_number, (SELECT (max(number) + 1) FROM l1_batches)) as \"l1_batch_number!\", (SELECT max(m2.number) FROM miniblocks m2 WHERE miniblocks.l1_batch_number = m2.l1_batch_number) as \"last_batch_miniblock?\", miniblocks.timestamp, miniblocks.hash as \"root_hash?\", miniblocks.l1_gas_price, miniblocks.l2_fair_gas_price, miniblocks.bootloader_code_hash, miniblocks.default_aa_code_hash, miniblocks.virtual_blocks, miniblocks.hash, miniblocks.consensus, miniblocks.protocol_version as \"protocol_version!\", l1_batches.fee_account_address as \"fee_account_address?\" FROM miniblocks LEFT JOIN l1_batches ON miniblocks.l1_batch_number = l1_batches.number WHERE miniblocks.number = $1" - }, "79cdb4cdd3c47b3654e6240178985fb4b4420e0634f9482a6ef8169e90200b84": { "describe": { "columns": [ diff --git a/core/lib/dal/src/models/storage_sync.rs b/core/lib/dal/src/models/storage_sync.rs index dc15250671bd..e816e5a72ab7 100644 --- a/core/lib/dal/src/models/storage_sync.rs +++ b/core/lib/dal/src/models/storage_sync.rs @@ -5,12 +5,11 @@ use zksync_protobuf::{read_required, ProtoFmt}; use zksync_types::{api::en, Address, L1BatchNumber, MiniblockNumber, Transaction, H160, H256}; #[derive(Debug, Clone, sqlx::FromRow)] -pub struct StorageSyncBlock { +pub(crate) struct StorageSyncBlock { pub number: i64, pub l1_batch_number: i64, pub last_batch_miniblock: Option, pub timestamp: i64, - pub root_hash: Option>, // L1 gas price assumed in the corresponding batch pub l1_gas_price: i64, // L2 gas price assumed in the corresponding batch @@ -48,11 +47,6 @@ impl StorageSyncBlock { .map(|n| n == self.number) .unwrap_or(false), timestamp: self.timestamp.try_into().context("timestamp")?, - root_hash: self - .root_hash - .map(|h| parse_h256(&h)) - .transpose() - .context("root_hash")?, l1_gas_price: self.l1_gas_price.try_into().context("l1_gas_price")?, l2_fair_gas_price: self .l2_fair_gas_price @@ -106,22 +100,25 @@ pub struct ConsensusBlockFields { } impl ConsensusBlockFields { + pub fn decode(src: &en::ConsensusBlockFields) -> anyhow::Result { + zksync_protobuf::decode(&src.0 .0) + } + pub fn encode(&self) -> en::ConsensusBlockFields { en::ConsensusBlockFields(zksync_protobuf::encode(self).into()) } - pub fn decode(x: &en::ConsensusBlockFields) -> anyhow::Result { - zksync_protobuf::decode(&x.0 .0) - } } impl ProtoFmt for ConsensusBlockFields { type Proto = crate::models::proto::ConsensusBlockFields; + fn read(r: &Self::Proto) -> anyhow::Result { Ok(Self { parent: read_required(&r.parent).context("parent")?, justification: read_required(&r.justification).context("justification")?, }) } + fn build(&self) -> Self::Proto { Self::Proto { parent: Some(self.parent.build()), diff --git a/core/lib/dal/src/sync_dal.rs b/core/lib/dal/src/sync_dal.rs index 991469db4692..bf6eeb88cdbb 100644 --- a/core/lib/dal/src/sync_dal.rs +++ b/core/lib/dal/src/sync_dal.rs @@ -27,7 +27,6 @@ impl SyncDal<'_, '_> { COALESCE(miniblocks.l1_batch_number, (SELECT (max(number) + 1) FROM l1_batches)) as \"l1_batch_number!\", \ (SELECT max(m2.number) FROM miniblocks m2 WHERE miniblocks.l1_batch_number = m2.l1_batch_number) as \"last_batch_miniblock?\", \ miniblocks.timestamp, \ - miniblocks.hash as \"root_hash?\", \ miniblocks.l1_gas_price, \ miniblocks.l2_fair_gas_price, \ miniblocks.bootloader_code_hash, \ @@ -47,30 +46,152 @@ impl SyncDal<'_, '_> { .fetch_optional(self.storage.conn()) .await?; - let res = if let Some(storage_block_details) = storage_block_details { - let transactions = if include_transactions { - let block_transactions = sqlx::query_as!( - StorageTransaction, - r#"SELECT * FROM transactions WHERE miniblock_number = $1 ORDER BY index_in_block"#, - block_number.0 as i64 - ) - .instrument("sync_dal_sync_block.transactions") - .with_arg("block_number", &block_number) - .fetch_all(self.storage.conn()) - .await? - .into_iter() - .map(Transaction::from) - .collect(); - Some(block_transactions) - } else { - None - }; - Some(storage_block_details.into_sync_block(current_operator_address, transactions)?) + let Some(storage_block_details) = storage_block_details else { + return Ok(None); + }; + let transactions = if include_transactions { + let transactions = sqlx::query_as!( + StorageTransaction, + r#"SELECT * FROM transactions WHERE miniblock_number = $1 ORDER BY index_in_block"#, + block_number.0 as i64 + ) + .instrument("sync_dal_sync_block.transactions") + .with_arg("block_number", &block_number) + .fetch_all(self.storage.conn()) + .await?; + + Some(transactions.into_iter().map(Transaction::from).collect()) } else { None }; + let block = + storage_block_details.into_sync_block(current_operator_address, transactions)?; drop(latency); - Ok(res) + Ok(Some(block)) + } +} + +#[cfg(test)] +mod tests { + use zksync_types::{ + block::{BlockGasCount, L1BatchHeader}, + fee::TransactionExecutionMetrics, + L1BatchNumber, ProtocolVersion, ProtocolVersionId, + }; + + use super::*; + use crate::{ + tests::{create_miniblock_header, mock_execution_result, mock_l2_transaction}, + ConnectionPool, + }; + + #[tokio::test] + async fn sync_block_basics() { + let pool = ConnectionPool::test_pool().await; + let mut conn = pool.access_storage().await.unwrap(); + + // Simulate genesis. + conn.protocol_versions_dal() + .save_protocol_version_with_tx(ProtocolVersion::default()) + .await; + conn.blocks_dal() + .insert_miniblock(&create_miniblock_header(0)) + .await + .unwrap(); + let mut l1_batch_header = L1BatchHeader::new( + L1BatchNumber(0), + 0, + Address::repeat_byte(0x42), + Default::default(), + ProtocolVersionId::latest(), + ); + conn.blocks_dal() + .insert_l1_batch(&l1_batch_header, &[], BlockGasCount::default(), &[], &[]) + .await + .unwrap(); + conn.blocks_dal() + .mark_miniblocks_as_executed_in_l1_batch(L1BatchNumber(0)) + .await + .unwrap(); + + let operator_address = Address::repeat_byte(1); + assert!(conn + .sync_dal() + .sync_block(MiniblockNumber(1), operator_address, false) + .await + .unwrap() + .is_none()); + + // Insert another block in the store. + let miniblock_header = create_miniblock_header(1); + let tx = mock_l2_transaction(); + conn.transactions_dal() + .insert_transaction_l2(tx.clone(), TransactionExecutionMetrics::default()) + .await; + conn.blocks_dal() + .insert_miniblock(&miniblock_header) + .await + .unwrap(); + conn.transactions_dal() + .mark_txs_as_executed_in_miniblock( + MiniblockNumber(1), + &[mock_execution_result(tx.clone())], + 1.into(), + ) + .await; + + let block = conn + .sync_dal() + .sync_block(MiniblockNumber(1), operator_address, false) + .await + .unwrap() + .expect("no sync block"); + assert_eq!(block.number, MiniblockNumber(1)); + assert_eq!(block.l1_batch_number, L1BatchNumber(1)); + assert!(!block.last_in_batch); + assert_eq!(block.timestamp, miniblock_header.timestamp); + assert_eq!( + block.protocol_version, + miniblock_header.protocol_version.unwrap() + ); + assert_eq!( + block.virtual_blocks.unwrap(), + miniblock_header.virtual_blocks + ); + assert_eq!(block.l1_gas_price, miniblock_header.l1_gas_price); + assert_eq!(block.l2_fair_gas_price, miniblock_header.l2_fair_gas_price); + assert_eq!(block.operator_address, operator_address); + assert!(block.transactions.is_none()); + + let block = conn + .sync_dal() + .sync_block(MiniblockNumber(1), operator_address, true) + .await + .unwrap() + .expect("no sync block"); + let transactions = block.transactions.unwrap(); + assert_eq!(transactions, [Transaction::from(tx)]); + + l1_batch_header.number = L1BatchNumber(1); + l1_batch_header.timestamp = 1; + conn.blocks_dal() + .insert_l1_batch(&l1_batch_header, &[], BlockGasCount::default(), &[], &[]) + .await + .unwrap(); + conn.blocks_dal() + .mark_miniblocks_as_executed_in_l1_batch(L1BatchNumber(1)) + .await + .unwrap(); + + let block = conn + .sync_dal() + .sync_block(MiniblockNumber(1), operator_address, true) + .await + .unwrap() + .expect("no sync block"); + assert_eq!(block.l1_batch_number, L1BatchNumber(1)); + assert!(block.last_in_batch); + assert_eq!(block.operator_address, l1_batch_header.fee_account_address); } } diff --git a/core/lib/types/src/api/en.rs b/core/lib/types/src/api/en.rs index c1899c2bf3c7..24c92a458e37 100644 --- a/core/lib/types/src/api/en.rs +++ b/core/lib/types/src/api/en.rs @@ -30,8 +30,6 @@ pub struct SyncBlock { pub last_in_batch: bool, /// L2 block timestamp. pub timestamp: u64, - /// Hash of the L2 block (not the Merkle root hash). - pub root_hash: Option, /// L1 gas price used as VM parameter for the L1 batch corresponding to this L2 block. pub l1_gas_price: u64, /// L2 gas price used as VM parameter for the L1 batch corresponding to this L2 block. diff --git a/core/lib/zksync_core/src/sync_layer/tests.rs b/core/lib/zksync_core/src/sync_layer/tests.rs index 3c76e05d93f2..b75eae63f760 100644 --- a/core/lib/zksync_core/src/sync_layer/tests.rs +++ b/core/lib/zksync_core/src/sync_layer/tests.rs @@ -59,7 +59,6 @@ impl MockMainNodeClient { l1_batch_number, last_in_batch: is_fictive, timestamp: number.into(), - root_hash: Some(H256::repeat_byte(1)), l1_gas_price: 2, l2_fair_gas_price: 3, base_system_contracts_hashes: BaseSystemContractsHashes::default(), From 9c1d5bfad429041cf5c10ec436ac59cdedd9ceab Mon Sep 17 00:00:00 2001 From: kelemeno <34402761+kelemeno@users.noreply.github.com> Date: Mon, 11 Dec 2023 10:00:05 +0000 Subject: [PATCH 074/268] docs: New protocol specification (#641) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ New docs, focusing on protocol specs. Note: don't merge yet, waiting for review. ## Why ❔ We want to have good docs so people can understand the zkVM and its benefits. ## Checklist - [ X] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ X] Tests for the changes have been added / updated. - [ X] Documentation comments have been added / updated. - [ X] Code has been formatted via `zk fmt` and `zk lint`. - [ ] Spellcheck has been run via `cargo spellcheck --cfg=./spellcheck/era.cfg --code 1`. --------- Co-authored-by: kelemeno Co-authored-by: MexicanAce Co-authored-by: Jack Hamer <47187316+JackHamer09@users.noreply.github.com> Co-authored-by: Marcin M <128217157+mm-zk@users.noreply.github.com> Co-authored-by: Fedor Sakharov --- README.md | 15 +- docs/advanced/README.md | 14 - docs/advanced/blocks_and_batches.md | 85 -- .../advanced/01_initialization.md | 0 docs/{ => guides}/advanced/02_deposits.md | 0 docs/{ => guides}/advanced/03_withdrawals.md | 0 .../guides/advanced/0_alternative_vm_intro.md | 306 +++++++ .../advanced/advanced_debugging.md | 0 .../advanced/compression.md} | 0 docs/{ => guides}/advanced/contracts.md | 0 .../advanced/deeper_overview.md} | 6 +- .../advanced/fee_model.md} | 2 +- docs/{ => guides}/advanced/how_call_works.md | 3 +- .../advanced/how_l2_messaging_works.md | 0 .../advanced/how_transaction_works.md | 0 docs/{ => guides}/advanced/prover_keys.md | 0 docs/{ => guides}/advanced/pubdata.md | 27 +- docs/{ => guides}/advanced/zk_intuition.md | 4 +- docs/{ => guides}/architecture.md | 0 docs/{ => guides}/development.md | 0 docs/{ => guides}/external-node/01_intro.md | 0 .../external-node/02_configuration.md | 0 docs/{ => guides}/external-node/03_running.md | 0 .../external-node/04_observability.md | 0 .../external-node/05_troubleshooting.md | 0 .../external-node/06_components.md | 0 .../prepared_configs/mainnet-config.env | 0 .../prepared_configs/testnet-config.env | 0 docs/{ => guides}/launch.md | 0 docs/{ => guides}/repositories.md | 0 docs/{ => guides}/setup-dev.md | 0 docs/specs/README.md | 36 + docs/specs/blocks_batches.md | 274 +++++++ docs/specs/data_availability/README.md | 5 + docs/specs/data_availability/compression.md | 125 +++ docs/specs/data_availability/overview.md | 19 + docs/specs/data_availability/pubdata.md | 446 +++++++++++ .../specs/data_availability/reconstruction.md | 7 + .../data_availability/validium_zk_porter.md | 7 + docs/specs/img/L2_Components.png | Bin 0 -> 75908 bytes docs/specs/img/diamondProxy.jpg | Bin 0 -> 127297 bytes docs/specs/img/governance.jpg | Bin 0 -> 161578 bytes docs/specs/img/zk-the-collective-action.jpeg | Bin 0 -> 260019 bytes docs/specs/introduction.md | 19 + docs/specs/l1_l2_communication/README.md | 5 + docs/specs/l1_l2_communication/l1_to_l2.md | 170 ++++ docs/specs/l1_l2_communication/l2_to_l1.md | 72 ++ .../overview_deposits_withdrawals.md | 13 + docs/specs/l1_smart_contracts.md | 289 +++++++ docs/specs/overview.md | 39 + docs/specs/prover/README.md | 9 + .../boojum_function_check_if_satisfied.md | 97 +++ docs/specs/prover/boojum_gadgets.md | 189 +++++ docs/specs/prover/circuit_testing.md | 59 ++ docs/specs/prover/circuits/README.md | 17 + .../specs/prover/circuits/code_decommitter.md | 208 +++++ docs/specs/prover/circuits/demux_log_queue.md | 226 ++++++ docs/specs/prover/circuits/ecrecover.md | 320 ++++++++ docs/specs/prover/circuits/img/diagram.png | Bin 0 -> 12668 bytes docs/specs/prover/circuits/img/flowchart.png | Bin 0 -> 187572 bytes docs/specs/prover/circuits/img/image.png | Bin 0 -> 227383 bytes .../prover/circuits/keccak_round_function.md | 203 +++++ .../prover/circuits/l1_messages_hasher.md | 149 ++++ docs/specs/prover/circuits/log_sorter.md | 351 ++++++++ docs/specs/prover/circuits/main_vm.md | 342 ++++++++ docs/specs/prover/circuits/overview.md | 123 +++ docs/specs/prover/circuits/ram_permutation.md | 195 +++++ .../prover/circuits/sha256_round_function.md | 369 +++++++++ .../prover/circuits/sort_decommitments.md | 232 ++++++ docs/specs/prover/circuits/sorting.md | 56 ++ .../circuits/sorting_and_deduplicating.md | 9 + .../prover/circuits/storage_application.md | 213 +++++ docs/specs/prover/circuits/storage_sorter.md | 282 +++++++ docs/specs/prover/getting_started.md | 21 + .../Check_if_satisfied(1).png | Bin 0 -> 96306 bytes .../Check_if_satisfied(11).png | Bin 0 -> 92473 bytes .../Check_if_satisfied(12).png | Bin 0 -> 109109 bytes .../Check_if_satisfied(13).png | Bin 0 -> 122534 bytes .../Check_if_satisfied(14).png | Bin 0 -> 67201 bytes .../Check_if_satisfied(16).png | Bin 0 -> 84431 bytes .../Check_if_satisfied(17).png | Bin 0 -> 114302 bytes .../Check_if_satisfied(2).png | Bin 0 -> 143563 bytes .../Check_if_satisfied(3).png | Bin 0 -> 261820 bytes .../Check_if_satisfied(4).png | Bin 0 -> 151081 bytes .../Check_if_satisfied(7).png | Bin 0 -> 189514 bytes .../Check_if_satisfied(8).png | Bin 0 -> 124859 bytes .../Check_if_satisfied(9).png | Bin 0 -> 90694 bytes .../Check_if_satisfied.png | Bin 0 -> 147955 bytes .../img/circuit_testing/Contest(10).png | Bin 0 -> 139480 bytes .../img/circuit_testing/Contest(11).png | Bin 0 -> 85347 bytes .../img/circuit_testing/Contest(12).png | Bin 0 -> 152645 bytes .../prover/img/circuit_testing/Contest(4).png | Bin 0 -> 91702 bytes .../prover/img/circuit_testing/Contest(5).png | Bin 0 -> 265470 bytes .../prover/img/circuit_testing/Contest(6).png | Bin 0 -> 87853 bytes .../prover/img/circuit_testing/Contest(7).png | Bin 0 -> 207684 bytes .../prover/img/circuit_testing/Contest(8).png | Bin 0 -> 92569 bytes .../prover/img/circuit_testing/Contest(9).png | Bin 0 -> 135392 bytes .../circuit.png" | Bin 0 -> 14473 bytes docs/specs/prover/overview.md | 63 ++ docs/specs/prover/zk_terminology.md | 103 +++ docs/specs/the_hyperchain/README.md | 5 + docs/specs/the_hyperchain/hyperbridges.md | 42 + .../the_hyperchain/img/contractsExternal.png | Bin 0 -> 262845 bytes docs/specs/the_hyperchain/img/deployWeth.png | Bin 0 -> 81010 bytes docs/specs/the_hyperchain/img/depositWeth.png | Bin 0 -> 86769 bytes .../specs/the_hyperchain/img/hyperbridges.png | Bin 0 -> 179481 bytes .../the_hyperchain/img/hyperbridging.png | Bin 0 -> 113383 bytes docs/specs/the_hyperchain/img/newChain.png | Bin 0 -> 96670 bytes docs/specs/the_hyperchain/overview.md | 6 + docs/specs/the_hyperchain/shared_bridge.md | 247 ++++++ docs/specs/zk_evm/README.md | 9 + docs/specs/zk_evm/account_abstraction.md | 40 + docs/specs/zk_evm/bootloader.md | 333 ++++++++ docs/specs/zk_evm/fee_model.md | 522 ++++++++++++ docs/specs/zk_evm/precompiles.md | 298 +++++++ docs/specs/zk_evm/system_contracts.md | 313 ++++++++ docs/specs/zk_evm/vm_overview.md | 28 + .../EraVM_formal_specification.pdf | Bin 0 -> 1385808 bytes docs/specs/zk_evm/vm_specification/README.md | 5 + .../vm_specification/compiler/README.md | 8 + .../compiler/code_separation.md | 76 ++ .../compiler/evmla_translator.md | 755 ++++++++++++++++++ .../compiler/exception_handling.md | 101 +++ .../compiler/instructions/README.md | 7 + .../compiler/instructions/evm/README.md | 15 + .../compiler/instructions/evm/arithmetic.md | 330 ++++++++ .../compiler/instructions/evm/bitwise.md | 194 +++++ .../compiler/instructions/evm/block.md | 159 ++++ .../compiler/instructions/evm/call.md | 27 + .../compiler/instructions/evm/create.md | 16 + .../compiler/instructions/evm/environment.md | 294 +++++++ .../compiler/instructions/evm/logging.md | 27 + .../compiler/instructions/evm/logical.md | 188 +++++ .../compiler/instructions/evm/memory.md | 61 ++ .../compiler/instructions/evm/overview.md | 23 + .../compiler/instructions/evm/return.md | 56 ++ .../compiler/instructions/evm/sha3.md | 49 ++ .../compiler/instructions/evm/stack.md | 47 ++ .../compiler/instructions/evmla.md | 85 ++ .../instructions/extensions/README.md | 5 + .../compiler/instructions/extensions/call.md | 70 ++ .../instructions/extensions/overview.md | 7 + .../instructions/extensions/verbatim.md | 73 ++ .../compiler/instructions/overview.md | 50 ++ .../compiler/instructions/yul.md | 92 +++ .../vm_specification/compiler/overview.md | 141 ++++ .../compiler/system_contracts.md | 125 +++ .../vm_specification/img/arch-overview.png | Bin 0 -> 57728 bytes .../img/arithmetic_opcode.png | Bin 0 -> 15320 bytes .../zkSync_era_virtual_machine_primer.md | 637 +++++++++++++++ 150 files changed, 11260 insertions(+), 130 deletions(-) delete mode 100644 docs/advanced/README.md delete mode 100644 docs/advanced/blocks_and_batches.md rename docs/{ => guides}/advanced/01_initialization.md (100%) rename docs/{ => guides}/advanced/02_deposits.md (100%) rename docs/{ => guides}/advanced/03_withdrawals.md (100%) create mode 100644 docs/guides/advanced/0_alternative_vm_intro.md rename docs/{ => guides}/advanced/advanced_debugging.md (100%) rename docs/{advanced/bytecode_compression.md => guides/advanced/compression.md} (100%) rename docs/{ => guides}/advanced/contracts.md (100%) rename docs/{advanced/prover.md => guides/advanced/deeper_overview.md} (98%) rename docs/{advanced/gas_and_fees.md => guides/advanced/fee_model.md} (99%) rename docs/{ => guides}/advanced/how_call_works.md (98%) rename docs/{ => guides}/advanced/how_l2_messaging_works.md (100%) rename docs/{ => guides}/advanced/how_transaction_works.md (100%) rename docs/{ => guides}/advanced/prover_keys.md (100%) rename docs/{ => guides}/advanced/pubdata.md (92%) rename docs/{ => guides}/advanced/zk_intuition.md (99%) rename docs/{ => guides}/architecture.md (100%) rename docs/{ => guides}/development.md (100%) rename docs/{ => guides}/external-node/01_intro.md (100%) rename docs/{ => guides}/external-node/02_configuration.md (100%) rename docs/{ => guides}/external-node/03_running.md (100%) rename docs/{ => guides}/external-node/04_observability.md (100%) rename docs/{ => guides}/external-node/05_troubleshooting.md (100%) rename docs/{ => guides}/external-node/06_components.md (100%) rename docs/{ => guides}/external-node/prepared_configs/mainnet-config.env (100%) rename docs/{ => guides}/external-node/prepared_configs/testnet-config.env (100%) rename docs/{ => guides}/launch.md (100%) rename docs/{ => guides}/repositories.md (100%) rename docs/{ => guides}/setup-dev.md (100%) create mode 100644 docs/specs/README.md create mode 100644 docs/specs/blocks_batches.md create mode 100644 docs/specs/data_availability/README.md create mode 100644 docs/specs/data_availability/compression.md create mode 100644 docs/specs/data_availability/overview.md create mode 100644 docs/specs/data_availability/pubdata.md create mode 100644 docs/specs/data_availability/reconstruction.md create mode 100644 docs/specs/data_availability/validium_zk_porter.md create mode 100644 docs/specs/img/L2_Components.png create mode 100644 docs/specs/img/diamondProxy.jpg create mode 100644 docs/specs/img/governance.jpg create mode 100644 docs/specs/img/zk-the-collective-action.jpeg create mode 100644 docs/specs/introduction.md create mode 100644 docs/specs/l1_l2_communication/README.md create mode 100644 docs/specs/l1_l2_communication/l1_to_l2.md create mode 100644 docs/specs/l1_l2_communication/l2_to_l1.md create mode 100644 docs/specs/l1_l2_communication/overview_deposits_withdrawals.md create mode 100644 docs/specs/l1_smart_contracts.md create mode 100644 docs/specs/overview.md create mode 100644 docs/specs/prover/README.md create mode 100644 docs/specs/prover/boojum_function_check_if_satisfied.md create mode 100644 docs/specs/prover/boojum_gadgets.md create mode 100644 docs/specs/prover/circuit_testing.md create mode 100644 docs/specs/prover/circuits/README.md create mode 100644 docs/specs/prover/circuits/code_decommitter.md create mode 100644 docs/specs/prover/circuits/demux_log_queue.md create mode 100644 docs/specs/prover/circuits/ecrecover.md create mode 100644 docs/specs/prover/circuits/img/diagram.png create mode 100644 docs/specs/prover/circuits/img/flowchart.png create mode 100644 docs/specs/prover/circuits/img/image.png create mode 100644 docs/specs/prover/circuits/keccak_round_function.md create mode 100644 docs/specs/prover/circuits/l1_messages_hasher.md create mode 100644 docs/specs/prover/circuits/log_sorter.md create mode 100644 docs/specs/prover/circuits/main_vm.md create mode 100644 docs/specs/prover/circuits/overview.md create mode 100644 docs/specs/prover/circuits/ram_permutation.md create mode 100644 docs/specs/prover/circuits/sha256_round_function.md create mode 100644 docs/specs/prover/circuits/sort_decommitments.md create mode 100644 docs/specs/prover/circuits/sorting.md create mode 100644 docs/specs/prover/circuits/sorting_and_deduplicating.md create mode 100644 docs/specs/prover/circuits/storage_application.md create mode 100644 docs/specs/prover/circuits/storage_sorter.md create mode 100644 docs/specs/prover/getting_started.md create mode 100644 docs/specs/prover/img/boojum_function_check_if_satisfied/Check_if_satisfied(1).png create mode 100644 docs/specs/prover/img/boojum_function_check_if_satisfied/Check_if_satisfied(11).png create mode 100644 docs/specs/prover/img/boojum_function_check_if_satisfied/Check_if_satisfied(12).png create mode 100644 docs/specs/prover/img/boojum_function_check_if_satisfied/Check_if_satisfied(13).png create mode 100644 docs/specs/prover/img/boojum_function_check_if_satisfied/Check_if_satisfied(14).png create mode 100644 docs/specs/prover/img/boojum_function_check_if_satisfied/Check_if_satisfied(16).png create mode 100644 docs/specs/prover/img/boojum_function_check_if_satisfied/Check_if_satisfied(17).png create mode 100644 docs/specs/prover/img/boojum_function_check_if_satisfied/Check_if_satisfied(2).png create mode 100644 docs/specs/prover/img/boojum_function_check_if_satisfied/Check_if_satisfied(3).png create mode 100644 docs/specs/prover/img/boojum_function_check_if_satisfied/Check_if_satisfied(4).png create mode 100644 docs/specs/prover/img/boojum_function_check_if_satisfied/Check_if_satisfied(7).png create mode 100644 docs/specs/prover/img/boojum_function_check_if_satisfied/Check_if_satisfied(8).png create mode 100644 docs/specs/prover/img/boojum_function_check_if_satisfied/Check_if_satisfied(9).png create mode 100644 docs/specs/prover/img/boojum_function_check_if_satisfied/Check_if_satisfied.png create mode 100644 docs/specs/prover/img/circuit_testing/Contest(10).png create mode 100644 docs/specs/prover/img/circuit_testing/Contest(11).png create mode 100644 docs/specs/prover/img/circuit_testing/Contest(12).png create mode 100644 docs/specs/prover/img/circuit_testing/Contest(4).png create mode 100644 docs/specs/prover/img/circuit_testing/Contest(5).png create mode 100644 docs/specs/prover/img/circuit_testing/Contest(6).png create mode 100644 docs/specs/prover/img/circuit_testing/Contest(7).png create mode 100644 docs/specs/prover/img/circuit_testing/Contest(8).png create mode 100644 docs/specs/prover/img/circuit_testing/Contest(9).png create mode 100644 "docs/specs/prover/img/intro_to_zkSync\342\200\231s_ZK/circuit.png" create mode 100644 docs/specs/prover/overview.md create mode 100644 docs/specs/prover/zk_terminology.md create mode 100644 docs/specs/the_hyperchain/README.md create mode 100644 docs/specs/the_hyperchain/hyperbridges.md create mode 100644 docs/specs/the_hyperchain/img/contractsExternal.png create mode 100644 docs/specs/the_hyperchain/img/deployWeth.png create mode 100644 docs/specs/the_hyperchain/img/depositWeth.png create mode 100644 docs/specs/the_hyperchain/img/hyperbridges.png create mode 100644 docs/specs/the_hyperchain/img/hyperbridging.png create mode 100644 docs/specs/the_hyperchain/img/newChain.png create mode 100644 docs/specs/the_hyperchain/overview.md create mode 100644 docs/specs/the_hyperchain/shared_bridge.md create mode 100644 docs/specs/zk_evm/README.md create mode 100644 docs/specs/zk_evm/account_abstraction.md create mode 100644 docs/specs/zk_evm/bootloader.md create mode 100644 docs/specs/zk_evm/fee_model.md create mode 100644 docs/specs/zk_evm/precompiles.md create mode 100644 docs/specs/zk_evm/system_contracts.md create mode 100644 docs/specs/zk_evm/vm_overview.md create mode 100644 docs/specs/zk_evm/vm_specification/EraVM_formal_specification.pdf create mode 100644 docs/specs/zk_evm/vm_specification/README.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/README.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/code_separation.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/evmla_translator.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/exception_handling.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/instructions/README.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/instructions/evm/README.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/instructions/evm/arithmetic.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/instructions/evm/bitwise.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/instructions/evm/block.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/instructions/evm/call.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/instructions/evm/create.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/instructions/evm/environment.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/instructions/evm/logging.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/instructions/evm/logical.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/instructions/evm/memory.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/instructions/evm/overview.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/instructions/evm/return.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/instructions/evm/sha3.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/instructions/evm/stack.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/instructions/evmla.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/instructions/extensions/README.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/instructions/extensions/call.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/instructions/extensions/overview.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/instructions/extensions/verbatim.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/instructions/overview.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/instructions/yul.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/overview.md create mode 100644 docs/specs/zk_evm/vm_specification/compiler/system_contracts.md create mode 100644 docs/specs/zk_evm/vm_specification/img/arch-overview.png create mode 100644 docs/specs/zk_evm/vm_specification/img/arithmetic_opcode.png create mode 100644 docs/specs/zk_evm/vm_specification/zkSync_era_virtual_machine_primer.md diff --git a/README.md b/README.md index 5d34006846a2..e124c8bc02b9 100644 --- a/README.md +++ b/README.md @@ -11,13 +11,14 @@ write smart contracts in C++, Rust and other popular languages. The following questions will be answered by the following resources: -| Question | Resource | -| ------------------------------------------------------- | --------------------------------------- | -| What do I need to develop the project locally? | [development.md](docs/development.md) | -| How can I set up my dev environment? | [setup-dev.md](docs/setup-dev.md) | -| How can I run the project? | [launch.md](docs/launch.md) | -| What is the logical project structure and architecture? | [architecture.md](docs/architecture.md) | -| Where can I find developer docs? | [docs](https://v2-docs.zksync.io/dev/) | +| Question | Resource | +| ------------------------------------------------------- | ---------------------------------------------- | +| What do I need to develop the project locally? | [development.md](docs/guides/development.md) | +| How can I set up my dev environment? | [setup-dev.md](docs/guides/setup-dev.md) | +| How can I run the project? | [launch.md](docs/guides/launch.md) | +| What is the logical project structure and architecture? | [architecture.md](docs/guides/architecture.md) | +| Where can I find protocol specs? | [specs](docs/specs/README.md) | +| Where can I find developer docs? | [docs](https://v2-docs.zksync.io/dev/) | ## Policies diff --git a/docs/advanced/README.md b/docs/advanced/README.md deleted file mode 100644 index 032153fb2dca..000000000000 --- a/docs/advanced/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Advanced documentation - -This documentation is aimed at advanced users who are interested in developing the zkSync Era itself (rather than just -the contracts on top) - and would like to understand how the system works internally. - -The documents in this directory are not meant to be a full specification, but give you the rough understanding of the -system internals. - -Suggested order of reading: - -- [Initialization](01_initialization.md) -- [Deposits](02_deposits.md) -- [Withdrawals](03_withdrawals.md) -- [Contracts](contracts.md) diff --git a/docs/advanced/blocks_and_batches.md b/docs/advanced/blocks_and_batches.md deleted file mode 100644 index 7c8f136e8873..000000000000 --- a/docs/advanced/blocks_and_batches.md +++ /dev/null @@ -1,85 +0,0 @@ -# Blocks & Batches - How we package transactions - -In this article, we will explore the processing of transactions, how we group them into blocks, what it means to "seal" -a block, and why it is important to have rollbacks in our virtual machine (VM). - -At the basic level, we have individual transactions. However, to execute them more efficiently, we group them together -into blocks & batches - -## L1 Batch vs L2 Block (a.k.a MiniBlock) vs Transaction - -To help visualize the concept, here are two images: - -![Block layout][block_layout] - -You can refer to the Block layout image to see how the blocks are organized. It provides a graphical representation of -how transactions are arranged within the blocks and the arrangement of L2 blocks within L1 "batches." - -![Explorer example][explorer_example] - -### L2 blocks (aka Miniblocks) - -Currently, the L2 blocks do not have a major role in the system, until we transition to a decentralized sequencer. We -introduced them mainly as a "compatibility feature" to accommodate various tools, such as Metamask, which expect a block -that changes frequently. This allows these tools to provide feedback to users, confirming that their transaction has -been added. - -As of now, an L2 block is created every 2 seconds (controlled by StateKeeper's config `miniblock_commit_deadline_ms`), -and it includes all the transactions received during that time period. This periodic creation of L2 blocks ensures that -transactions are processed and included in the blocks regularly. - -### L1 batches - -L1 batches play a crucial role because they serve as the fundamental unit for generating proofs. From the perspective of -the virtual machine (VM), each L1 batch represents the execution of a single program, specifically the Bootloader. The -Bootloader internally processes all the transactions belonging to that particular batch. Therefore, the L1 batch serves -as the container for executing the program and handling the transactions within it. - -#### So how large can L1 batch be - -Most blockchains use factors like time and gas usage to determine when a block should be closed or sealed. However, our -case is a bit more complex because we also need to consider prover capacity and limits related to publishing to L1. - -The decision of when to seal the block is handled by the code in the [conditional_sealer][conditional_sealer] module. It -maintains a list of `SealCriterion` and at the time of writing this article, [we have 9 reasons to seal the -block][reasons_for_sealing], which include: - -- Transaction slots limit (currently set to 750 transactions in `StateKeeper`'s config - `transaction_slots`). -- Gas limit (currently set to `MAX_L2_TX_GAS_LIMIT` = 80M). -- Published data limit (as each L1 batch must publish information about the changed slots to L1, so all the changes must - fit within the L1 transaction limit, currently set to `MAX_PUBDATA_PER_L1_BATCH`= 120k). -- zkEVM Geometry limits - For certain operations like merklelization, there is a maximum number of circuits that can be - included in a single L1 batch. If this limit is exceeded, we wouldn't be able to generate the proof. - -We also have a `TimeoutCriterion` - but it is not enabled. - -However, these sealing criteria pose a significant challenge because it is difficult to predict in advance whether -adding a given transaction to the current batch will exceed the limits or not. This unpredictability adds complexity to -the process of determining when to seal the block. - -#### What if a transaction doesn't fit - -To handle situations where a transaction exceeds the limits of the currently active L1 batch, we employ a "try and -rollback" approach. This means that we attempt to add the transaction to the active L1 batch, and if we receive a -`ExcludeAndSeal` response indicating that it doesn't fit, we roll back the virtual machine (VM) to the state before the -transaction was attempted. - -Implementing this approach introduces a significant amount of complexity in the `oracles` (also known as interfaces) of -the VM. These oracles need to support snapshotting and rolling back operations to ensure consistency when handling -transactions that don't fit. - -In a separate article, we will delve into more details about how these oracles and the VM work, providing a -comprehensive understanding of their functionality and interactions. - -[block_layout]: - https://user-images.githubusercontent.com/128217157/236494232-aeed380c-78f6-4fda-ab2a-8de26c1089ff.png - 'block layout' -[explorer_example]: - https://user-images.githubusercontent.com/128217157/236500717-165470ad-30b8-4ad6-97ed-fc29c8eb1fe0.png - 'explorer example' -[conditional_sealer]: - https://github.com/matter-labs/zksync-era/blob/main/core/lib/zksync_core/src/state_keeper/seal_criteria/conditional_sealer.rs#20 - 'Conditional Sealer' -[reasons_for_sealing]: - https://github.com/matter-labs/zksync-era/blob/main/core/lib/zksync_core/src/state_keeper/seal_criteria/mod.rs#L106 - 'Reasons for Sealing' diff --git a/docs/advanced/01_initialization.md b/docs/guides/advanced/01_initialization.md similarity index 100% rename from docs/advanced/01_initialization.md rename to docs/guides/advanced/01_initialization.md diff --git a/docs/advanced/02_deposits.md b/docs/guides/advanced/02_deposits.md similarity index 100% rename from docs/advanced/02_deposits.md rename to docs/guides/advanced/02_deposits.md diff --git a/docs/advanced/03_withdrawals.md b/docs/guides/advanced/03_withdrawals.md similarity index 100% rename from docs/advanced/03_withdrawals.md rename to docs/guides/advanced/03_withdrawals.md diff --git a/docs/guides/advanced/0_alternative_vm_intro.md b/docs/guides/advanced/0_alternative_vm_intro.md new file mode 100644 index 000000000000..bce8c48a60aa --- /dev/null +++ b/docs/guides/advanced/0_alternative_vm_intro.md @@ -0,0 +1,306 @@ +# zkEVM internals + +## zkEVM clarifier + +[Back to ToC](../../README.md) + +The zkSync zkEVM plays a fundamentally different role in the zkStack than the EVM does in Ethereum. The EVM is used to +execute code in Ethereum's state transition function. This STF needs a client to implement and run it. Ethereum has a +multi-client philosophy, there are multiple clients, and they are written in Go, Rust, and other traditional programming +languages, all running and verifying the same STF. + +We have a different set of requirements, we need to produce a proof that some client executed the STF correctly. The +first consequence is that the client needs to be hard-coded, we cannot have the same multi-client philosophy. This +client is the zkEVM, it can run the STF efficiently, including execution of smart contracts similarly to the EVM. The +zkEVM was also designed to be proven efficiently. + +For efficiency reasons it the zkEVM is similar to the EVM. This makes executing smart programs inside of it easy. It +also has special features that are not in the EVM but are needed for the rollup's STF, storage, gas metering, +precompiles and other things. Some of these features are implemented as system contracts while others are built into the +VM. System Contracts are contracts with special permissions, deployed at predefined addresses. Finally, we have the +bootloader, which is also a contract, although it is not deployed at any address. This is the STF that is ultimately +executed by the zkEVM, and executes the transaction against the state. + + + +Full specification of the zkEVM is beyond the scope of this document. However, this section will give you most of the +details needed for understanding the L2 system smart contracts & basic differences between EVM and zkEVM. Note also that +usually understanding the EVM is needed for efficient smart contract development. Understanding the zkEVM goes beyond +this, it is needed for developing the rollup itself. + +## Registers and memory management + +On EVM, during transaction execution, the following memory areas are available: + +- `memory` itself. +- `calldata` the immutable slice of parent memory. +- `returndata` the immutable slice returned by the latest call to another contract. +- `stack` where the local variables are stored. + +Unlike EVM, which is stack machine, zkEVM has 16 registers. Instead of receiving input from `calldata`, zkEVM starts by +receiving a _pointer_ in its first register *(*basically a packed struct with 4 elements: the memory page id, start and +length of the slice to which it points to*)* to the calldata page of the parent. Similarly, a transaction can receive +some other additional data within its registers at the start of the program: whether the transaction should invoke the +constructor ([more about deployments here](#contractdeployer--immutablesimulator)), whether the transaction has +`isSystem` flag, etc. The meaning of each of these flags will be expanded further in this section. + +_Pointers_ are separate type in the VM. It is only possible to: + +- Read some value within a pointer. +- Shrink the pointer by reducing the slice to which pointer points to. +- Receive the pointer to the returndata/as a calldata. +- Pointers can be stored only on stack/registers to make sure that the other contracts can not read memory/returndata of + contracts they are not supposed to. +- A pointer can be converted to the u256 integer representing it, but an integer can not be converted to a pointer to + prevent unallowed memory access. +- It is not possible to return a pointer that points to a memory page with id smaller than the one for the current page. + What this means is that it is only possible to `return` only pointer to the memory of the current frame or one of the + pointers returned by the subcalls of the current frame. + +### Memory areas in zkEVM + +For each frame, the following memory areas are allocated: + +- _Heap_ (plays the same role as `memory` on Ethereum). +- _AuxHeap_ (auxiliary heap). It has the same properties as Heap, but it is used for the compiler to encode + calldata/copy the returndata from the calls to system contracts to not interfere with the standard Solidity memory + alignment. +- _Stack_. Unlike Ethereum, stack is not the primary place to get arguments for opcodes. The biggest difference between + stack on zkEVM and EVM is that on zkSync stack can be accessed at any location (just like memory). While users do not + pay for the growth of stack, the stack can be fully cleared at the end of the frame, so the overhead is minimal. +- _Code_. The memory area from which the VM executes the code of the contract. The contract itself can not read the code + page, it is only done implicitly by the VM. + +Also, as mentioned in the previous section, the contract receives the pointer to the calldata. + +### Managing returndata & calldata + +Whenever a contract finishes its execution, the parent’s frame receives a _pointer_ as `returndata`. This pointer may +point to the child frame’s Heap/AuxHeap or it can even be the same `returndata` pointer that the child frame received +from some of its child frames. + +The same goes with the `calldata`. Whenever a contract starts its execution, it receives the pointer to the calldata. +The parent frame can provide any valid pointer as the calldata, which means it can either be a pointer to the slice of +parent’s frame memory (heap or auxHeap) or it can be some valid pointer that the parent frame has received before as +calldata/returndata. + +Contracts simply remember the calldata pointer at the start of the execution frame (it is by design of the compiler) and +remembers the latest received returndata pointer. + +Some important implications of this is that it is now possible to do the following calls without any memory copying: + +A → B → C + +where C receives a slice of the calldata received by B. + +The same goes for returning data: + +A ← B ← C + +There is no need to copy returned data if the B returns a slice of the returndata returned by C. + +Note, that you can _not_ use the pointer that you received via calldata as returndata (i.e. return it at the end of the +execution frame). Otherwise, it would be possible that returndata points to the memory slice of the active frame and +allow editing the `returndata`. It means that in the examples above, C could not return a slice of its calldata without +memory copying. + +Some of these memory optimizations can be seen utilized in the +[EfficientCall](https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/contracts/libraries/EfficientCall.sol) +library that allows to perform a call while reusing the slice of calldata that the frame already has, without memory +copying. + +### Returndata & precompiles + +Some of the operations which are opcodes on Ethereum, have become calls to some of the system contracts. The most +notable examples are `Keccak256`, `SystemContext`, etc. Note, that, if done naively, the following lines of code would +work differently on zkSync and Ethereum: + +```solidity +pop(call(...)) +keccak(...) +returndatacopy(...) +``` + +Since the call to keccak precompile would modify the `returndata`. To avoid this, our compiler does not override the +latest `returndata` pointer after calls to such opcode-like precompiles. + +## zkEVM specific opcodes + +While some Ethereum opcodes are not supported out of the box, some of the new opcodes were added to facilitate the +development of the system contracts. + +Note, that this lists does not aim to be specific about the internals, but rather explain methods in the +[SystemContractHelper.sol](https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/contracts/libraries/SystemContractHelper.sol) + +### **Only for kernel space** + +These opcodes are allowed only for contracts in kernel space (i.e. system contracts). If executed in other places they +result in `revert(0,0)`. + +- `mimic_call`. The same as a normal `call`, but it can alter the `msg.sender` field of the transaction. +- `to_l1`. Sends a system L2→L1 log to Ethereum. The structure of this log can be seen + [here](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/contracts/ethereum/contracts/zksync/Storage.sol#L47). +- `event`. Emits an L2 log to zkSync. Note, that L2 logs are not equivalent to Ethereum events. Each L2 log can emit 64 + bytes of data (the actual size is 88 bytes, because it includes the emitter address, etc). A single Ethereum event is + represented with multiple `event` logs constitute. This opcode is only used by `EventWriter` system contract. +- `precompile_call`. This is an opcode that accepts two parameters: the uint256 representing the packed parameters for + it as well as the ergs to burn. Besides the price for the precompile call itself, it burns the provided ergs and + executes the precompile. The action that it does depend on `this` during execution: + - If it is the address of the `ecrecover` system contract, it performs the ecrecover operation + - If it is the address of the `sha256`/`keccak256` system contracts, it performs the corresponding hashing operation. + - It does nothing (i.e. just burns ergs) otherwise. It can be used to burn ergs needed for L2→L1 communication or + publication of bytecodes onchain. +- `setValueForNextFarCall` sets `msg.value` for the next `call`/`mimic_call`. Note, that it does not mean that the value + will be really transferred. It just sets the corresponding `msg.value` context variable. The transferring of ETH + should be done via other means by the system contract that uses this parameter. Note, that this method has no effect + on `delegatecall` , since `delegatecall` inherits the `msg.value` of the previous frame. +- `increment_tx_counter` increments the counter of the transactions within the VM. The transaction counter used mostly + for the VM’s internal tracking of events. Used only in bootloader after the end of each transaction. + +Note, that currently we do not have access to the `tx_counter` within VM (i.e. for now it is possible to increment it +and it will be automatically used for logs such as `event`s as well as system logs produced by `to_l1`, but we can not +read it). We need to read it to publish the _user_ L2→L1 logs, so `increment_tx_counter` is always accompanied by the +corresponding call to the [SystemContext](#systemcontext) contract. + +More on the difference between system and user logs can be read +[here](https://github.com/code-423n4/2023-10-zksync/blob/main/docs/Smart%20contract%20Section/Handling%20pubdata%20in%20Boojum.md). - +`set_pubdata_price` sets the price (in gas) for publishing a single byte of pubdata. + +### **Generally accessible** + +Here are opcodes that can be generally accessed by any contract. Note that while the VM allows to access these methods, +it does not mean that this is easy: the compiler might not have convenient support for some use-cases yet. + +- `near_call`. It is basically a “framed” jump to some location of the code of your contract. The difference between the + `near_call` and ordinary jump are: + 1. It is possible to provide an ergsLimit for it. Note, that unlike “`far_call`”s (i.e. calls between contracts) the + 63/64 rule does not apply to them. + 2. If the near call frame panics, all state changes made by it are reversed. Please note, that the memory changes will + **not** be reverted. +- `getMeta`. Returns an u256 packed value of + [ZkSyncMeta](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/system-contracts/contracts/libraries/SystemContractHelper.sol#L42) + struct. Note that this is not tight packing. The struct is formed by the + [following rust code](https://github.com/matter-labs/era-zkevm_opcode_defs/blob/c7ab62f4c60b27dfc690c3ab3efb5fff1ded1a25/src/definitions/abi/meta.rs#L4). +- `getCodeAddress` — receives the address of the executed code. This is different from `this` , since in case of + delegatecalls `this` is preserved, but `codeAddress` is not. + +### Flags for calls + +Besides the calldata, it is also possible to provide additional information to the callee when doing `call` , +`mimic_call`, `delegate_call`. The called contract will receive the following information in its first 12 registers at +the start of execution: + +- _r1_ — the pointer to the calldata. +- _r2_ — the pointer with flags of the call. This is a mask, where each bit is set only if certain flags have been set + to the call. Currently, two flags are supported: 0-th bit: `isConstructor` flag. This flag can only be set by system + contracts and denotes whether the account should execute its constructor logic. Note, unlike Ethereum, there is no + separation on constructor & deployment bytecode. More on that can be read + [here](#contractdeployer--immutablesimulator). 1-st bit: `isSystem` flag. Whether the call intends a system contracts’ + function. While most of the system contracts’ functions are relatively harmless, accessing some with calldata only may + break the invariants of Ethereum, e.g. if the system contract uses `mimic_call`: no one expects that by calling a + contract some operations may be done out of the name of the caller. This flag can be only set if the callee is in + kernel space. +- The rest r3..r12 registers are non-empty only if the `isSystem` flag is set. There may be arbitrary values passed, + which we call `extraAbiParams`. + +The compiler implementation is that these flags are remembered by the contract and can be accessed later during +execution via special +[simulations](https://github.com/code-423n4/2023-10-zksync/blob/main/docs/VM%20Section/How%20compiler%20works/instructions/extensions/overview.md). + +If the caller provides inappropriate flags (i.e. tries to set `isSystem` flag when callee is not in the kernel space), +the flags are ignored. + +### `onlySystemCall` modifier + +Some of the system contracts can act on behalf of the user or have a very important impact on the behavior of the +account. That’s why we wanted to make it clear that users can not invoke potentially dangerous operations by doing a +simple EVM-like `call`. Whenever a user wants to invoke some of the operations which we considered dangerous, they must +provide “`isSystem`” flag with them. + +The `onlySystemCall` flag checks that the call was either done with the “isSystemCall” flag provided or the call is done +by another system contract (since Matter Labs is fully aware of system contracts). + +### Simulations via our compiler + +In the future, we plan to introduce our “extended” version of Solidity with more supported opcodes than the original +one. However, right now it was beyond the capacity of the team to do, so in order to represent accessing zkSync-specific +opcodes, we use `call` opcode with certain constant parameters that will be automatically replaced by the compiler with +zkEVM native opcode. + +Example: + +```solidity +function getCodeAddress() internal view returns (address addr) { + address callAddr = CODE_ADDRESS_CALL_ADDRESS; + assembly { + addr := staticcall(0, callAddr, 0, 0xFFFF, 0, 0) + } +} + +``` + +In the example above, the compiler will detect that the static call is done to the constant `CODE_ADDRESS_CALL_ADDRESS` +and so it will replace it with the opcode for getting the code address of the current execution. + +Full list of opcode simulations can be found +[here](https://github.com/code-423n4/2023-10-zksync/blob/main/docs/VM%20Section/How%20compiler%20works/instructions/extensions/call.md). + +We also use +[verbatim-like](https://github.com/code-423n4/2023-10-zksync/blob/main/docs/VM%20Section/How%20compiler%20works/instructions/extensions/verbatim.md) +statements to access zkSync-specific opcodes in the bootloader. + +All the usages of the simulations in our Solidity code are implemented in the +[SystemContractHelper](https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/contracts/libraries/SystemContractHelper.sol) +library and the +[SystemContractsCaller](https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/contracts/libraries/SystemContractsCaller.sol) +library. + +**Simulating** `near_call` **(in Yul only)** + +In order to use `near_call` i.e. to call a local function, while providing a limit of ergs (gas) that this function can +use, the following syntax is used: + +The function should contain `ZKSYNC_NEAR_CALL` string in its name and accept at least 1 input parameter. The first input +parameter is the packed ABI of the `near_call`. Currently, it is equal to the number of ergs to be passed with the +`near_call`. + +Whenever a `near_call` panics, the `ZKSYNC_CATCH_NEAR_CALL` function is called. + +_Important note:_ the compiler behaves in a way that if there is a `revert` in the bootloader, the +`ZKSYNC_CATCH_NEAR_CALL` is not called and the parent frame is reverted as well. The only way to revert only the +`near_call` frame is to trigger VM’s _panic_ (it can be triggered with either invalid opcode or out of gas error). + +_Important note 2:_ The 63/64 rule does not apply to `near_call`. Also, if 0 gas is provided to the near call, then +actually all of the available gas will go to it. + +### Notes on security + +To prevent unintended substitution, the compiler requires `--system-mode` flag to be passed during compilation for the +above substitutions to work. + +## Bytecode hashes + +On zkSync the bytecode hashes are stored in the following format: + +- The 0th byte denotes the version of the format. Currently the only version that is used is “1”. +- The 1st byte is `0` for deployed contracts’ code and `1` for the contract code + [that is being constructed](#constructing-vs-non-constructing-code-hash). +- The 2nd and 3rd bytes denote the length of the contract in 32-byte words as big-endian 2-byte number. +- The next 28 bytes are the last 28 bytes of the sha256 hash of the contract’s bytecode. + +The bytes are ordered in little-endian order (i.e. the same way as for `bytes32` ). + +### Bytecode validity + +A bytecode is valid if it: + +- Has its length in bytes divisible by 32 (i.e. consists of an integer number of 32-byte words). +- Has a length of less than 2^16 words (i.e. its length in words fits into 2 bytes). +- Has an odd length in words (i.e. the 3rd byte is an odd number). + +Note, that it does not have to consist of only correct opcodes. In case the VM encounters an invalid opcode, it will +simply revert (similar to how EVM would treat them). + +A call to a contract with invalid bytecode can not be proven. That is why it is **essential** that no contract with +invalid bytecode is ever deployed on zkSync. It is the job of the [KnownCodesStorage](#knowncodestorage) to ensure that +all allowed bytecodes in the system are valid. diff --git a/docs/advanced/advanced_debugging.md b/docs/guides/advanced/advanced_debugging.md similarity index 100% rename from docs/advanced/advanced_debugging.md rename to docs/guides/advanced/advanced_debugging.md diff --git a/docs/advanced/bytecode_compression.md b/docs/guides/advanced/compression.md similarity index 100% rename from docs/advanced/bytecode_compression.md rename to docs/guides/advanced/compression.md diff --git a/docs/advanced/contracts.md b/docs/guides/advanced/contracts.md similarity index 100% rename from docs/advanced/contracts.md rename to docs/guides/advanced/contracts.md diff --git a/docs/advanced/prover.md b/docs/guides/advanced/deeper_overview.md similarity index 98% rename from docs/advanced/prover.md rename to docs/guides/advanced/deeper_overview.md index 71cb668bcdd1..5f58407ff7b9 100644 --- a/docs/advanced/prover.md +++ b/docs/guides/advanced/deeper_overview.md @@ -1,6 +1,8 @@ -# Overview +# Deeper Overview -The purpose of this document is to explain our new proof system from an engineering standpoint. We will examine the code +[Back to ToC](../../../README.md) + +The purpose of this section is to explain our new proof system from an engineering standpoint. We will examine the code examples and how the libraries communicate. Let's begin by discussing our constraint system. In the previous prover, we utilized the Bellman repository. However, in diff --git a/docs/advanced/gas_and_fees.md b/docs/guides/advanced/fee_model.md similarity index 99% rename from docs/advanced/gas_and_fees.md rename to docs/guides/advanced/fee_model.md index 0f10f0ef1d65..af598c950535 100644 --- a/docs/advanced/gas_and_fees.md +++ b/docs/guides/advanced/fee_model.md @@ -28,7 +28,7 @@ to L1. The maximum gas for a transaction is 80 million (80M/4k = 20k). ### L2 Fair price -The L2 fair gas price is currently determined by the StateKeeper configuration and is set at 0.25 Gwei (see +The L2 fair gas price is currently determined by the StateKeeper/Sequencer configuration and is set at 0.25 Gwei (see `fair_l2_gas_price` in the config). This price is meant to cover the compute costs (CPU + GPU) for the sequencer and prover. It can be changed as needed, with a safety limit of 10k Gwei in the bootloader. Once the system is decentralized, more deterministic rules will be established for this price. diff --git a/docs/advanced/how_call_works.md b/docs/guides/advanced/how_call_works.md similarity index 98% rename from docs/advanced/how_call_works.md rename to docs/guides/advanced/how_call_works.md index 178a95b32398..4ba4859c4109 100644 --- a/docs/advanced/how_call_works.md +++ b/docs/guides/advanced/how_call_works.md @@ -69,8 +69,7 @@ opcodes similar to EVM, but operates on registers rather than a stack. We have t 'pure rust' without circuits (in the zk_evm repository), and the other has circuits (in the sync_vm repository). In this example, the api server uses the 'zk_evm' implementation without circuits. -Most of the code that the server uses to interact with the VM is in -[core/lib/multivm/src/versions/vm_latest/implementation/execution.rs][vm_code]. +Most of the code that the server uses to interact with the VM is in [core/lib/vm/src/vm.rs][vm_code]. In this line, we're calling self.state.cycle(), which executes a single VM instruction. You can see that we do a lot of things around this, such as executing multiple tracers after each instruction. This allows us to debug and provide diff --git a/docs/advanced/how_l2_messaging_works.md b/docs/guides/advanced/how_l2_messaging_works.md similarity index 100% rename from docs/advanced/how_l2_messaging_works.md rename to docs/guides/advanced/how_l2_messaging_works.md diff --git a/docs/advanced/how_transaction_works.md b/docs/guides/advanced/how_transaction_works.md similarity index 100% rename from docs/advanced/how_transaction_works.md rename to docs/guides/advanced/how_transaction_works.md diff --git a/docs/advanced/prover_keys.md b/docs/guides/advanced/prover_keys.md similarity index 100% rename from docs/advanced/prover_keys.md rename to docs/guides/advanced/prover_keys.md diff --git a/docs/advanced/pubdata.md b/docs/guides/advanced/pubdata.md similarity index 92% rename from docs/advanced/pubdata.md rename to docs/guides/advanced/pubdata.md index 2f5cdb251328..9b8fd4346c42 100644 --- a/docs/advanced/pubdata.md +++ b/docs/guides/advanced/pubdata.md @@ -8,9 +8,9 @@ Pubdata in zkSync can be divided up into 4 different categories: 4. Storage writes Using data corresponding to these 4 facets, across all executed batches, we’re able to reconstruct the full state of L2. -One thing to note is that the way that the data is represented changes in a pre-boojum and post-boojum zkSync Era. At a -high level, in a pre-boojum era these are represented as separate fields while in boojum they are packed into a single -bytes array. +One thing to note is that the way that the data is represented changes in a pre-boojum and post-boojum zkEVM. At a high +level, in a pre-boojum era these are represented as separate fields while in boojum they are packed into a single bytes +array. > Note: Once 4844 gets integrated this bytes array will move from being part of the calldata to blob data. @@ -36,7 +36,7 @@ function _storeCodeHash(address _address, bytes32 _hash) internal { ``` -## Pre-Boojum Era +### Pre-Boojum Era In pre-boojum era the superset of pubdata fields and input to the `commitBlocks` function follows the following format: @@ -87,7 +87,7 @@ For the ids on the repeated writes, they are generated as we process the first t `key1` it will be encoded as `<1, new_val>` and so on and so forth. There is a little shortcut here where the last new id generated as part of a batch will be in the `indexRepeatedStorageChanges` field. -## Post-Boojum Era +### Post-Boojum Era ```solidity /// @notice Data needed to commit new block @@ -124,9 +124,9 @@ The 2 main fields needed for state reconstruction are the bytecodes and the stat structure and reasoning in the old system (as explained above). The state diffs will follow the compression illustrated below. -## Compression of State Diffs in Post-Boojum Era +### Compression of State Diffs in Post-Boojum Era -### Keys +#### Keys Keys will be packed in the same way as they were before boojum. The only change is that we’ll avoid using the 8-byte enumeration index and will pack it to the minimal necessary number of bytes. This number will be part of the pubdata. @@ -137,7 +137,7 @@ bytes on nonce/balance key, but ultimately the complexity may not be worth it. There is some room for the keys that are being written for the first time, however, these are rather more complex and achieve only a one-time effect (when the key is published for the first time). -### Values +#### Values Values are much easier to compress, since they usually contain only zeroes. Also, we can leverage the nature of how those values are changed. For instance if nonce has been increased only by 1, we do not need to write the entire 32-byte @@ -158,7 +158,7 @@ that it has been zeroed out). For `NoCompression` the whole 32 byte value is use So the format of the pubdata will be the following: -#### Part 1. Header +##### Part 1. Header - `` — this will enable easier automated unpacking in the future. Currently, it will be only equal to `1`. @@ -166,7 +166,7 @@ So the format of the pubdata will be the following: - ``. At the beginning it will be equal to `4`, but then it will automatically switch to `5` when needed. -#### Part 2. Initial writes +##### Part 2. Initial writes - `` (since each initial write publishes at least 32 bytes for key, then `2^16 * 32 = 2097152` will be enough for a lot of time (right now with the limit of 120kb it will take more than 15 L1 @@ -188,10 +188,3 @@ the writes will be repeated ones. - packing type as a 1 byte value, which consists of 5 bits to denote the length of the packing and 3 bits to denote the type of the packing (either `Add`, `Sub`, `Transform` or `NoCompression`). - The packed value itself. - -## L2 State Recosntruction Tool - -Given the structure above, there is a tool, created by the [Equilibrium Team](https://equilibrium.co/) that solely uses -L1 pubdata for reconstructing the state and verifying that the state root on L1 can be created using pubdata. A link to -the repo can be found [here](https://github.com/eqlabs/zksync-state-reconstruct). The way the tool works is by parsing -out all the L1 pubdata for an executed batch, comparing the state roots after each batch is processed. diff --git a/docs/advanced/zk_intuition.md b/docs/guides/advanced/zk_intuition.md similarity index 99% rename from docs/advanced/zk_intuition.md rename to docs/guides/advanced/zk_intuition.md index 58777b264fc1..359406df3611 100644 --- a/docs/advanced/zk_intuition.md +++ b/docs/guides/advanced/zk_intuition.md @@ -1,4 +1,4 @@ -# Intuition guide to ZK in zkSync +# Intuition guide to ZK in zkEVM **WARNING**: This guide simplifies the complex details of how we use ZK in our systems, just to give you a better understanding. We're leaving out a lot of details to keep things brief. @@ -20,7 +20,7 @@ Let’s break down the basic steps involved when a transaction is made within ou - **Verify proof on L1:** This means checking that the fancy math was done right on the Ethereum network (referred to as L1). -## Generate Witness - What Does It Mean +## What It Means to Generate a Witness When our State Keeper processes a transaction, it carries out a bunch of operations and assumes certain conditions without openly stating them. However, when it comes to ZK, we need to show clear evidence that these conditions hold. diff --git a/docs/architecture.md b/docs/guides/architecture.md similarity index 100% rename from docs/architecture.md rename to docs/guides/architecture.md diff --git a/docs/development.md b/docs/guides/development.md similarity index 100% rename from docs/development.md rename to docs/guides/development.md diff --git a/docs/external-node/01_intro.md b/docs/guides/external-node/01_intro.md similarity index 100% rename from docs/external-node/01_intro.md rename to docs/guides/external-node/01_intro.md diff --git a/docs/external-node/02_configuration.md b/docs/guides/external-node/02_configuration.md similarity index 100% rename from docs/external-node/02_configuration.md rename to docs/guides/external-node/02_configuration.md diff --git a/docs/external-node/03_running.md b/docs/guides/external-node/03_running.md similarity index 100% rename from docs/external-node/03_running.md rename to docs/guides/external-node/03_running.md diff --git a/docs/external-node/04_observability.md b/docs/guides/external-node/04_observability.md similarity index 100% rename from docs/external-node/04_observability.md rename to docs/guides/external-node/04_observability.md diff --git a/docs/external-node/05_troubleshooting.md b/docs/guides/external-node/05_troubleshooting.md similarity index 100% rename from docs/external-node/05_troubleshooting.md rename to docs/guides/external-node/05_troubleshooting.md diff --git a/docs/external-node/06_components.md b/docs/guides/external-node/06_components.md similarity index 100% rename from docs/external-node/06_components.md rename to docs/guides/external-node/06_components.md diff --git a/docs/external-node/prepared_configs/mainnet-config.env b/docs/guides/external-node/prepared_configs/mainnet-config.env similarity index 100% rename from docs/external-node/prepared_configs/mainnet-config.env rename to docs/guides/external-node/prepared_configs/mainnet-config.env diff --git a/docs/external-node/prepared_configs/testnet-config.env b/docs/guides/external-node/prepared_configs/testnet-config.env similarity index 100% rename from docs/external-node/prepared_configs/testnet-config.env rename to docs/guides/external-node/prepared_configs/testnet-config.env diff --git a/docs/launch.md b/docs/guides/launch.md similarity index 100% rename from docs/launch.md rename to docs/guides/launch.md diff --git a/docs/repositories.md b/docs/guides/repositories.md similarity index 100% rename from docs/repositories.md rename to docs/guides/repositories.md diff --git a/docs/setup-dev.md b/docs/guides/setup-dev.md similarity index 100% rename from docs/setup-dev.md rename to docs/guides/setup-dev.md diff --git a/docs/specs/README.md b/docs/specs/README.md new file mode 100644 index 000000000000..724d7b923e84 --- /dev/null +++ b/docs/specs/README.md @@ -0,0 +1,36 @@ +# ZK Stack protocol specs + +1. [Introduction](./introduction.md) +1. [Overview](./overview.md) +1. [L1 Contracts](./l1_smart_contracts.md) +1. [zkEVM](./zk_evm/README.md) + - [VM Overview](./zk_evm/vm_overview.md) + - [VM Specificiation](./zk_evm/vm_specification/README.md) + - [Bootloader](./zk_evm/bootloader.md) + - [System Contracts](./zk_evm/system_contracts.md) + - [Precompiles](./zk_evm/precompiles.md) + - [Account Abstraction](./zk_evm/account_abstraction.md) + - [Fee Model](./zk_evm/fee_model.md) +1. [L1<->L2 communication](./l1_l2_communication/README.md) + - [Overview - Deposits and Withdrawals](./l1_l2_communication/overview_deposits_withdrawals.md) + - [L2->L1 messages](./l1_l2_communication/l2_to_l1.md) + - [L1->L2 messages](./l1_l2_communication/l1_to_l2.md) +1. [Blocks and batches](./blocks_batches.md) +1. [Data Availability](./data_availability/README.md) + - [Overview](./data_availability/overview.md) + - [Pubdata](./data_availability/pubdata.md) + - [Compression](./data_availability/compression.md) + - [Reconstruction](./data_availability/reconstruction.md) + - [Validium and zkPorter](./data_availability/validium_zk_porter.md) +1. [Prover](./prover/README.md) + - [Overview - Boojum](./prover/overview.md) + - [ZK Terminology](./prover/zk_terminology.md) + - [Getting Started](./prover/getting_started.md) + - [Circuits](./prover/circuits/) + - [Circuit testing](./prover/circuit_testing.md) + - [Boojum gadgets](./prover/boojum_gadgets.md) + - [Boojum function: check_if_satisfied](./prover/boojum_function_check_if_satisfied.md) +1. [The Hyperchain](./the_hyperchain/README.md) + - [Overview](./the_hyperchain/overview.md) + - [Shared Bridge](./the_hyperchain/shared_bridge.md) + - [Hyperbrdges](./the_hyperchain/hyperbridges.md) diff --git a/docs/specs/blocks_batches.md b/docs/specs/blocks_batches.md new file mode 100644 index 000000000000..796429f78cec --- /dev/null +++ b/docs/specs/blocks_batches.md @@ -0,0 +1,274 @@ +# Blocks & Batches - How we package transactions + +In this article, we will explore the processing of transactions, how we group them into blocks, what it means to "seal" +a block, and why it is important to have rollbacks in our virtual machine (VM). + +At the basic level, we have individual transactions. However, to execute them more efficiently, we group them together +into blocks & batches. + +## L1 Batch vs L2 Block (a.k.a MiniBlock) vs Transaction + +To help visualize the concept, here are two images: + +![Block layout][block_layout] + +You can refer to the Block layout image to see how the blocks are organized. It provides a graphical representation of +how transactions are arranged within the blocks and the arrangement of L2 blocks within L1 "batches." + +![Explorer example][explorer_example] + +### L2 blocks (aka Miniblocks) + +Currently, the L2 blocks do not have a major role in the system, until we transition to a decentralized sequencer. We +introduced them mainly as a "compatibility feature" to accommodate various tools, such as Metamask, which expect a block +that changes frequently. This allows these tools to provide feedback to users, confirming that their transaction has +been added. + +As of now, an L2 block is created every 2 seconds (controlled by StateKeeper's config `miniblock_commit_deadline_ms`), +and it includes all the transactions received during that time period. This periodic creation of L2 blocks ensures that +transactions are processed and included in the blocks regularly. + +### L1 batches + +L1 batches play a crucial role because they serve as the fundamental unit for generating proofs. From the perspective of +the virtual machine (VM), each L1 batch represents the execution of a single program, specifically the Bootloader. The +Bootloader internally processes all the transactions belonging to that particular batch. Therefore, the L1 batch serves +as the container for executing the program and handling the transactions within it. + +#### So how large can L1 batch be + +Most blockchains use factors like time and gas usage to determine when a block should be closed or sealed. However, our +case is a bit more complex because we also need to consider prover capacity and limits related to publishing to L1. + +The decision of when to seal the block is handled by the code in the [conditional_sealer][conditional_sealer] module. It +maintains a list of `SealCriterion` and at the time of writing this article, [we have 9 reasons to seal the +block][reasons_for_sealing], which include: + +- Transaction slots limit (currently set to 750 transactions in `StateKeeper`'s config - `transaction_slots`). +- Gas limit (currently set to `MAX_L2_TX_GAS_LIMIT` = 80M). +- Published data limit (as each L1 batch must publish information about the changed slots to L1, so all the changes must + fit within the L1 transaction limit, currently set to `MAX_PUBDATA_PER_L1_BATCH`= 120k). +- zkEVM Geometry limits - For certain operations like merklelization, there is a maximum number of circuits that can be + included in a single L1 batch. If this limit is exceeded, we wouldn't be able to generate the proof. + +We also have a `TimeoutCriterion` - but it is not enabled. + +However, these sealing criteria pose a significant challenge because it is difficult to predict in advance whether +adding a given transaction to the current batch will exceed the limits or not. This unpredictability adds complexity to +the process of determining when to seal the block. + +#### What if a transaction doesn't fit + +To handle situations where a transaction exceeds the limits of the currently active L1 batch, we employ a "try and +rollback" approach. This means that we attempt to add the transaction to the active L1 batch, and if we receive a +`ExcludeAndSeal` response indicating that it doesn't fit, we roll back the virtual machine (VM) to the state before the +transaction was attempted. + +Implementing this approach introduces a significant amount of complexity in the `oracles` (also known as interfaces) of +the VM. These oracles need to support snapshotting and rolling back operations to ensure consistency when handling +transactions that don't fit. + +In a separate article, we will delve into more details about how these oracles and the VM work, providing a +comprehensive understanding of their functionality and interactions. + +[block_layout]: + https://user-images.githubusercontent.com/128217157/236494232-aeed380c-78f6-4fda-ab2a-8de26c1089ff.png + 'block layout' +[explorer_example]: + https://user-images.githubusercontent.com/128217157/236500717-165470ad-30b8-4ad6-97ed-fc29c8eb1fe0.png + 'explorer example' +[conditional_sealer]: + https://github.com/matter-labs/zksync-era/blob/main/core/lib/zksync_core/src/state_keeper/seal_criteria/conditional_sealer.rs#20 + 'Conditional Sealer' +[reasons_for_sealing]: + https://github.com/matter-labs/zksync-era/blob/main/core/lib/zksync_core/src/state_keeper/seal_criteria/mod.rs#L106 + 'Reasons for Sealing' + +## Deeper dive + +### Glossary + +- Batch - a set of transactions that the bootloader processes (`commitBatches`, `proveBatches`, + and `executeBatches` work with it). A batch consists of multiple transactions. +- L2 block - a non-intersecting sub-set of consecutively executed transactions. This is the kind of block you see in the + API. This is the one that will _eventually_ be used for `block.number`/`block.timestamp`/etc. This will happen + _eventually_, since at the time of this writing the virtual block migration is being + [run](#migration--virtual-blocks-logic). +- Virtual block — blocks the data of which will be returned in the contract execution environment during the migration. + They are called “virtual”, since they have no trace in our API, i.e. it is not possible to query information about + them in any way. + +### Motivation + +Before the recent upgrade, `block.number`, `block.timestamp`, as well as `blockhash` in Solidity, returned information +about _batches_, i.e. large blocks that are proven on L1 and which consist of many small L2 blocks. At the same time, +API returns `block.number` and `block.timestamp` as for L2 blocks. + +L2 blocks were created for fast soft confirmation on wallets and block explorer. For example, MetaMask shows +transactions as confirmed only after the block in which transaction execution was mined. So if the user needs to wait +for the batch confirmation it would take at least minutes (for soft confirmation) and hours for full confirmation which +is very bad UX. But API could return soft confirmation much earlier through L2 blocks. + +There was a huge outcry in the community for us to return the information for L2 blocks in `block.number`, +`block.timestamp`, as well as `blockhash`, because of discrepancy of runtime execution and returned data by API. + +However, there were over 15mln L2 blocks, while less than 200k batches, meaning that if we simply “switched” from +returning L1 batches’ info to L2 block’s info, some contracts (especially those that use `block.number` for measuring +time intervals instead of `block.timestamp`) would break. For that, we decided to have an accelerated migration process, +i.e. the `block.number` will grow faster and faster, until it becomes roughly 8x times the L2 block production speed, +allowing it to gradually reach the L2 block number, after which the information on the L2 `block.number` will be +returned. The blocks the info of which will be returned during this process are called “virtual blocks”. Their +information will never be available in any of our APIs, which should not be a major breaking change, since our API +already mostly works with L2 blocks, while L1 batches’s information is returned in the runtime. + +### Adapting for Solidity + +In order to get the returned value for `block.number`, `block.timestamp`, `blockhash` our compiler used the following +functions: + +- `getBlockNumber` +- `getBlockTimestamp` +- `getBlockHashEVM` + +During the migration process, these will return the values of the virtual blocks. After the migration is complete, they +will return values for L2 blocks. + +### Migration status + +At the time of this writing, the migration has been complete on testnet, i.e. there we already have only the L2 block +information returned. However, the [migration](https://github.com/zkSync-Community-Hub/zkync-developers/discussions/87) +on mainnet is still ongoing and most likely will end on late October / early November. + +## Blocks’ processing and consistency checks + +Our `SystemContext` contract allows to get information about batches and L2 blocks. Some of the information is hard to +calculate onchain. For instace, time. The timing information (for both batches and L2 blocks) are provided by the +operator. In order to check that the operator provided some realistic values, certain checks are done on L1. Generally +though, we try to check as much as we can on L2. + +## Initializing L1 batch + +At the start of the batch, the operator +[provides](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/system-contracts/bootloader/bootloader.yul#L3636) +the timestamp of the batch, its number and the hash of the previous batch.. The root hash of the Merkle tree serves as +the root hash of the batch. + +The SystemContext can immediately check whether the provided number is the correct batch number. It also immediately +sends the previous batch hash to L1, where it will be checked during the commit operation. Also, some general +consistency checks are performed. This logic can be found +[here](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/system-contracts/contracts/SystemContext.sol#L416). + +## L2 blocks processing and consistency checks + +### `setL2Block` + +Before each transaction, we call `setL2Block` +[method](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/system-contracts/bootloader/bootloader.yul#L2605). +There we will provide some data about the L2 block that the transaction belongs to: + +- `_l2BlockNumber` The number of the new L2 block. +- `_l2BlockTimestamp` The timestamp of the new L2 block. +- `_expectedPrevL2BlockHash` The expected hash of the previous L2 block. +- `_isFirstInBatch` Whether this method is called for the first time in the batch. +- `_maxVirtualBlocksToCreate` The maximum number of virtual block to create with this L2 block. + +If two transactions belong to the same L2 block, only the first one may have non-zero `_maxVirtualBlocksToCreate`. The +rest of the data must be same. + +The `setL2Block` +[performs](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/system-contracts/contracts/SystemContext.sol#L312) +a lot of similar consistency checks to the ones for the L1 batch. + +### L2 blockhash calculation and storage + +Unlike L1 batch’s hash, the L2 blocks’ hashes can be checked on L2. + +The hash of an L2 block is +`keccak256(abi.encode(_blockNumber, _blockTimestamp, _prevL2BlockHash, _blockTxsRollingHash))`. Where +`_blockTxsRollingHash` is defined in the following way: + +`_blockTxsRollingHash = 0` for an empty block. + +`_blockTxsRollingHash = keccak(0, tx1_hash)` for a block with one tx. + +`_blockTxsRollingHash = keccak(keccak(0, tx1_hash), tx2_hash)` for a block with two txs, etc. + +To add a transaction hash to the current miniblock we use the `appendTransactionToCurrentL2Block` +[function](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/system-contracts/contracts/SystemContext.sol#L373). + +Since zkSync is a state-diff based rollup, there is no way to deduce the hashes of the L2 blocks based on the +transactions’ in the batch (because there is no access to the transaction’s hashes). At the same time, in order to +server `blockhash` method, the VM requires the knowledge of some of the previous L2 block hashes. In order to save up on +pubdata (by making sure that the same storage slots are reused, i.e. we only have repeated writes) we +[store](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/system-contracts/contracts/SystemContext.sol#L70) +only the last 257 block hashes. You can read more on what are the repeated writes and how the pubdata is processed +[here](https://github.com/code-423n4/2023-10-zksync/blob/main/docs/Smart%20contract%20Section/Handling%20L1%E2%86%92L2%20ops%20on%20zkSync.md). + +We store only the last 257 blocks, since the EVM requires only 256 previous ones and we use 257 as a safe margin. + +### Legacy blockhash + +When initializing L2 blocks that do not have their hashes stored on L2 (basically these are blocks before the migration +upgrade), we use the following formula for their hash: + +`keccak256(abi.encodePacked(uint32(_blockNumber)))` + +### Timing invariants + +While the timestamp of each L2 block is provided by the operator, there are some timing invariants that the system +preserves: + +- For each L2 block its timestamp should be > the timestamp of the previous L2 block +- For each L2 block its timestamp should be ≥ timestamp of the batch it belongs to +- Each batch must start with a new L2 block (i.e. an L2 block can not span across batches). +- The timestamp of a batch must be ≥ the timestamp of the latest L2 block which belonged to the previous batch. +- The timestamp of the last miniblock in batch can not go too far into the future. This is enforced by publishing an + L2→L1 log, with the timestamp which is then checked on L1. + +## Fictive L2 block & finalizing the batch + +At the end of the batch, the bootloader calls the `setL2Block` +[one more time](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/system-contracts/bootloader/bootloader.yul#L3812) +to allow the operator to create a new empty block. This is done purely for some of the technical reasons inside the +node, where each batch ends with an empty L2 block. + +We do not enforce that the last block is empty explicitly as it complicates the development process and testing, but in +practice, it is, and either way, it should be secure. + +Also, at the end of the batch we send the timestamps of the batch as well as the timestamp of the last miniblock in +order to check on L1 that both of these are realistic. Checking any other L2 block’s timestamp is not required since all +of them are enforced to be between those two. + +## Migration & virtual blocks’ logic + +As already explained above, for a smoother upgrade for the ecosystem, there is a migration being performed during which +instead of returning either batch information or L2 block information, we will return the virtual block information +until they catch up with the L2 block’s number. + +### Production of the virtual blocks + +- In each batch, there should be at least one virtual block created. +- Whenever a new L2 block is created, the operator can select how many virtual blocks it wants to create. This can be + any number, however, if the number of the virtual block exceeds the L2 block number, the migration is considered + complete and we switch to the mode where the L2 block information will be returned. + +## Additional note on blockhashes + +Note, that if we used some complex formula for virtual blocks’ hashes (like we do for L2 blocks), we would have to put +all of these into storage for the data availability. Even if we used the same storage trick that we used for the L2 +blocks, where we store only the last 257’s block’s hashes under the current load/migration plans it would be expected +that we have roughly ~250 virtual blocks per batch, practically meaning that we will publish all of these anyway. This +would be too expensive. That is why we have to use a simple formula of `keccak(uint256(number))` for now. Note, that +they do not collide with the legacy miniblock hash, since legacy miniblock hashes are calculated as +`keccak(uint32(number))`. + +Also, we need to keep the consistency of previous blockhashes, i.e. if `blockhash(X)` returns a non-zero value, it +should be consistent among the future blocks. For instance, let’s say that the hash of batch `1000` is `1`, +i.e. `blockhash(1000) = 1`. Then, when we migrate to virtual blocks, we need to ensure that `blockhash(1000)` will +return either 0 (if and only if the block is more than 256 blocks old) or `1`. Because of that for `blockhash` we will +have the following complex +[logic](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/system-contracts/contracts/SystemContext.sol#L103): + +- For blocks that were created before the virtual block upgrade, use the batch hashes +- For blocks that were created during the virtual block upgrade, use `keccak(uint256(number))`. +- For blocks that were created after the virtual blocks have caught up with the L2 blocks, use L2 block hashes. diff --git a/docs/specs/data_availability/README.md b/docs/specs/data_availability/README.md new file mode 100644 index 000000000000..590343485f45 --- /dev/null +++ b/docs/specs/data_availability/README.md @@ -0,0 +1,5 @@ +# Data availability + +- [Overview](./overview.md) +- [Pubdata](./pubdata.md) +- [Compression](./compression.md) diff --git a/docs/specs/data_availability/compression.md b/docs/specs/data_availability/compression.md new file mode 100644 index 000000000000..ef29ece4a8bb --- /dev/null +++ b/docs/specs/data_availability/compression.md @@ -0,0 +1,125 @@ +# State diff Compression + +The most basic strategy to publish state diffs is to publish those in either of the following two forms: + +- When a key is updated for the first time — ``, where key is 32-byte derived key and the value is new + 32-byte value of the slot. +- When a key is updated for the second time and more — ``, where the `enumeration_index` is an + 8-byte id of the slot and the value is the new 32-byte value of the slot. + +This compression strategy will utilize a similar idea for treating keys and values separately and it will be focused on +the efficient compression of keys and values separately. + +## Keys + +Keys will be packed in the same way as they were before. The only change is that we’ll avoid using the 8-byte +enumeration index and will pack it to the minimal necessary number of bytes. This number will be part of the pubdata. +Once a key has been used, it can already use the 4 or 5 byte enumeration index and it is very hard to have something +cheaper for keys that has been used already. The opportunity comes when remembering the ids for accounts to spare some +bytes on nonce/balance key, but ultimately the complexity may not be worth it. + +There is some room for optimization of the keys that are being written for the first time, however, optimizing those is +more complex and achieves only a one-time effect (when the key is published for the first time), so they may be in scope +of the future upgrades. + +## Values + +Values are much easier to compress since they usually contain only zeroes. Also, we can leverage the nature of how those +values are changed. For instance, if nonce has been increased only by 1, we do not need to write the entire 32-byte new +value, we can just tell that the slot has been _increased_ and then supply only the 1-byte value by which it was +increased. This way instead of 32 bytes we need to publish only 2 bytes: first byte to denote which operation has been +applied and the second by to denote the number by which the addition has been made. + +We have the following 4 types of changes: `Add`, `Sub,` `Transform`, `NoCompression` where: + +- `NoCompression` denotes that the whole 32 byte will be provided. +- `Add` denotes that the value has been increased. (modulo 2^256) +- `Sub` denotes that the value has been decreased. (modulo 2^256) +- `Transform` denotes the value just has been changed (i.e. we disregard any potential relation between the previous and + the new value, though the new value might be small enough to save up on the number of bytes). + +Where the byte size of the output can be anywhere from 0 to 31 (also 0 makes sense for `Transform`, since it denotes +that it has been zeroed out). For `NoCompression` the whole 32 byte value is used. + +So the format of the pubdata is the following: + +**Part 1. Header.** + +- `` — this will enable easier automated unpacking in the future. Currently, it will be only equal to + `1`. +- `` — we need only 3 bytes to describe the total length of the L2→L1 logs. +- ``. It should be equal to the minimal required bytes to represent + the enum indexes for repeated writes. + +**Part 2. Initial writes.** + +- `` - the number of initial writes. Since each initial write publishes at least 32 + bytes for key, then `2^16 * 32 = 2097152` will be enough for a lot of time (right now with the limit of 120kb it will + take more than 15 L1 txs to use up all the space there). +- Then for each `` pair for each initial write: + - print key as 32-byte derived key. + - packing type as a 1 byte value, which consists of 5 bits to denote the length of the packing and 3 bits to denote + the type of the packing (either `Add`, `Sub`, `Transform` or `NoCompression`). + - The packed value itself. + +**Part 3. Repeated writes.** + +Note, that there is no need to write the number of repeated writes, since we know that until the end of the pubdata, all +the writes will be repeated ones. + +- For each `` pair for each repeated write: + - print key as derived key by using the number of bytes provided in the header. + - packing type as a 1 byte value, which consists of 5 bits to denote the length of the packing and 3 bits to denote + the type of the packing (either `Add`, `Sub`, `Transform` or `NoCompression`). + - The packed value itself. + +## Impact + +This setup allows us to achieve nearly 75% packing for values, and 50% gains overall in terms of the storage logs based +on historical data. + +## Encoding of packing type + +Since we have `32 * 3 + 1` ways to pack a state diff, we need at least 7 bits to present the packing type. To make +parsing easier, we will use 8 bits, i.e. 1 byte. + +We will use the first 5 bits to represent the length of the bytes (from 0 to 31 inclusive) to be used. The other 3 bits +will be used to represent the type of the packing: `Add`, `Sub` , `Transform`, `NoCompression`. + +## Worst case scenario + +The worst case scenario for such packing is when we have to pack a completely random new value, i.e. it will take us 32 +bytes to pack + 1 byte to denote which type it is. However, for such a write the user will anyway pay at least for 32 +bytes. Adding an additional byte is roughly 3% increase, which will likely be barely felt by users, most of which use +storage slots for balances, etc, which will consume only 7-9 bytes for packed value. + +## Why do we need to repeat the same packing method id + +You might have noticed that for each pair `` to describe value we always first write the packing type and +then write the packed value. However, the reader might ask, it is more efficient to just supply the packing id once and +then list all the pairs `` which use such packing. + +I.e. instead of listing + +(key = 0, type = 1, value = 1), (key = 1, type = 1, value = 3), (key = 2, type = 1, value = 4), … + +Just write: + +type = 1, (key = 0, value = 1), (key = 1, value = 3), (key = 2, value = 4), … + +There are two reasons for it: + +- A minor reason: sometimes it is less efficient in case the packing is used for very few slots (since for correct + unpacking we need to provide the number of slots for each packing type). +- A fundamental reason: currently enum indeces are stored directly in the merkle tree & have very strict order of + incrementing enforced by the circuits and (they are given in order by pairs `(address, key)`), which are generally not + accessible from pubdata. + +All this means that we are not allowed to change the order of “first writes” above, so indexes for them are directly +recoverable from their order, and so we can not permute them. If we were to reorder keys without supplying the new +enumeration indeces for them, the state would be unrecoverable. Always supplying the new enum index may add additional 5 +bytes for each key, which might negate the compression benefits in a lot of cases. Even if the compression will still be +beneficial, the added complexity may not be worth it. + +That being said, we _could_ rearange those for _repeated_ writes, but for now we stick to the same value compression +format for simplicity. diff --git a/docs/specs/data_availability/overview.md b/docs/specs/data_availability/overview.md new file mode 100644 index 000000000000..d2a5f0778967 --- /dev/null +++ b/docs/specs/data_availability/overview.md @@ -0,0 +1,19 @@ +# Overview + +To support being a rollup, the ZK Stack needs to post the data of the chain on L1. Instead of submitting the data of +each transaction, we submit how the state of the blockchain changes, this change is called the state diff. This approach +allows the transactions that change the same storage slots to be very cheap, since these transactions don't incur +additional data costs. + +Besides the state diff we also [post additional](./pubdata.md) data to L1, such as the L2->L1 messages, the L2->L1 logs, +the bytecodes of the deployed smart contracts. + +We also [compress](./compression.md) all the data that we send to L1, to reduce the costs of posting it. + +By posting all the data to L1, we can [reconstruct](./reconstruction.md) the state of the chain from the data on L1. +This is a key security property of the rollup. + +The the chain chooses not to post this data, they become a validium. This makes transactions there much cheaper, but +less secure. Because we use state diffs to post data, we can combine the rollup and validium features, by separating +storage slots that need to post data from the ones that don't. This construction combines the benefits of rollups and +validiums, and it is called a [zkPorter](./validium_zk_porter.md). diff --git a/docs/specs/data_availability/pubdata.md b/docs/specs/data_availability/pubdata.md new file mode 100644 index 000000000000..3584a0430557 --- /dev/null +++ b/docs/specs/data_availability/pubdata.md @@ -0,0 +1,446 @@ +# Handling pubdata in Boojum + +Pubdata in zkSync can be divided up into 4 different categories: + +1. L2 to L1 Logs +2. L2 to L1 Messages +3. Smart Contract Bytecodes +4. Storage writes + +Using data corresponding to these 4 facets, across all executed batches, we’re able to reconstruct the full state of L2. +With the upgrade to our new proof system, Boojum, the way this data is represented will change. At a high level, in the +pre-Boojum system these are represented as separate fields while for boojum they will be packed into a single bytes +array. Once 4844 gets integrated this bytes array will move from being part of the calldata to blob data. + +While the structure of the pubdata changes, the way in which one can go about pulling the information will remain the +same. Basically, we just need to filter all of the transactions to the L1 zkSync contract for only the `commitBatches` +transactions where the proposed block has been referenced by a corresponding `executeBatches` call (the reason for this +is that a committed or even proven block can be reverted but an executed one cannot). Once we have all the committed +batches that have been executed, we then will pull the transaction input and the relevant fields, applying them in order +to reconstruct the current state of L2. + +## L2→L1 communication + +### L2→L1 communication before Boojum + +While there were quite some changes during Boojum upgrade, most of the scheme remains the same and so explaining how it +worked before gives some background on why certain decisions are made and kept for backward compatibility. + +[L2→L1 communication before Boojum](https://github.com/code-423n4/2023-10-zksync/blob/main/docs/Smart%20contract%20Section/Handling%20pubdata%20in%20Boojum/L2%E2%86%92L1%20communication%20before%20Boojum.md) + +The most important feature that we’ll need to maintain in Boojum for backward compatibility is to provide a similar +Merkle tree of L2→L1 logs with the long L2→L1 messages and priority operations’ status. + +Before Boojum, whenever we sent an L2→L1 long message, a _log_ was appended to the Merkle tree of L2→L1 messages on L1 +due to necessity. In Boojum we’ll have to maintain this fact. Having the priority operations’ statuses is important to +enable +[proving](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/contracts/ethereum/contracts/bridge/L1ERC20Bridge.sol#L255) +failed deposits for bridges. + +### Changes with Boojum + +#### Problems with the previous approach + +- There was a limit of 512 L2→L1 logs per batch, which is very limiting. It causes our block to be forcefully closed + based on the number of these messages instead of having the pubdata as the only limit. +- In the ideal world, we would like to have the tree adapt to the requirements of the batch, with any number of leaves + possible (in practice, a maximum of 2048 would likely be enough for the foreseeable future). +- Extending the tree in the circuits will be hard to do and hard to maintain. +- The hash of the contents of the L2→L1 messages needs to be rehashed to support the danksharding blobs, so we want to + keep only the essential logs as parts of calldata and the rest should be separated so that they could be moved the + EIP4844 blob in the future. + +#### Solution + +We will implement the calculation of the Merkle root of the L2→L1 messages via a system contract as part of the +`L1Messenger`. Basically, whenever a new log emitted by users that needs to be Merklized is created, the `L1Messenger` +contract will append it to its rolling hash and then at the end of the batch, during the formation of the blob it will +receive the original preimages from the operator, verify, and include the logs to the blob. + +We will now call the logs that are created by users and are Merklized _user_ logs and the logs that are emitted by +natively by VM _system_ logs. Here is a short comparison table for better understanding: + +| System logs | User logs | +| --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Emitted by VM via an opcode. | VM knows nothing about them. | +| Consistency and correctness is enforced by the verifier on L1 (i.e. their hash is part of the block commitment. | Consistency and correctness is enforced by the L1Messenger system contract. The correctness of the behavior of the L1Messenger is enforced implicitly by prover in a sense that it proves the correctness of the execution overall. | +| We don’t calculate their Merkle root. | We calculate their Merkle root on the L1Messenger system contract. | +| We have constant small number of those. | We can have as much as possible as long as the commitBatches function on L1 remains executable (it is the job of the operator to ensure that only such transactions are selected) | +| In EIP4844 they will remain part of the calldata. | In EIP4844 they will become part of the blobs. | + +#### Backwards-compatibility + +Note, that to maintain a unified interface with the previous version of the protocol, the leaves of the Merkle tree will +have to maintain the following structure: + +```solidity +struct L2Log { + uint8 l2ShardId; + bool isService; + uint16 txNumberInBlock; + address sender; + bytes32 key; + bytes32 value; +} + +``` + +While the leaf will look the following way: + +```solidity +bytes32 hashedLog = keccak256( + abi.encodePacked(_log.l2ShardId, _log.isService, _log.txNumberInBlock, _log.sender, _log.key, _log.value) +); +``` + +`keccak256` will continue being the function for the merkle tree. + +To put it shortly, the proofs for L2→L1 log inclusion will continue having exactly the same format as they did in the +pre-Boojum system, which avoids breaking changes for SDKs and bridges alike. + +#### Implementation of `L1Messenger` + +The L1Messenger contract will maintain a rolling hash of all the L2ToL1 logs `chainedLogsHash` as well as the rolling +hashes of messages `chainedMessagesHash`. Whenever a contract wants to send an L2→L1 log, the following operation will +be +[applied](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/system-contracts/contracts/L1Messenger.sol#L110): + +`chainedLogsHash = keccak256(chainedLogsHash, hashedLog)`. L2→L1 logs have the same 88-byte format as in the current +version of zkSync. + +Note, that the user is charged for necessary future the computation that will be needed to calculate the final merkle +root. It is roughly 4x higher than the cost to calculate the hash of the leaf, since the eventual tree might have be 4x +times the number nodes. In any case, this will likely be a relatively negligible part compared to the cost of the +pubdata. + +At the end of the execution, the bootloader will +[provide](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/system-contracts/bootloader/bootloader.yul#L2470) +a list of all the L2ToL1 logs as well as the messages in this block to the L1Messenger (this will be provided by the +operator in the memory of the bootloader). The L1Messenger checks that the rolling hash from the provided logs is the +same as in the `chainedLogsHash` and calculate the merkle tree of the provided messages. Right now, we always build the +Merkle tree of size `2048`, but we charge the user as if the tree was built dynamically based on the number of leaves in +there. The implementation of the dynamic tree has been postponed until the later upgrades. + +#### Long L2→L1 messages & bytecodes + +Before, the fact that the correct preimages for L2→L1 messages as bytecodes were provided was checked on the L1 side. +Now, it will be done on L2. + +If the user wants to send an L2→L1 message, its preimage is +[appended](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/system-contracts/contracts/L1Messenger.sol#L125) +to the message’s rolling hash too `chainedMessagesHash = keccak256(chainedMessagesHash, keccak256(message))`. + +A very similar approach for bytecodes is used, where their rolling hash is calculated and then the preimages are +provided at the end of the batch to form the full pubdata for the batch. + +Note, that in for backward compatibility, just like before any long message or bytecode is accompanied by the +corresponding user L2→L1 log. + +#### Using system L2→L1 logs vs the user logs + +The content of the L2→L1 logs by the L1Messenger will go to the blob of EIP4844. Meaning, that all the data that belongs +to the tree by L1Messenger’s L2→L1 logs should not be needed during block commitment. Also, note that in the future we +will remove the calculation of the Merkle root of the built-in L2→L1 messages. + +The only places where the built-in L2→L1 messaging should continue to be used: + +- Logs by SystemContext (they are needed on commit to check the previous block hash). +- Logs by L1Messenger for the merkle root of the L2→L1 tree as well as the hash of the `totalPubdata`. +- `chainedPriorityTxsHash` and `numberOfLayer1Txs` from the bootloader (read more about it below). + +#### Obtaining `txNumberInBlock` + +To have the same log format, the `txNumberInBlock` must be obtained. While it is internally counted in the VM, there is +currently no opcode to retrieve this number. We will have a public variable `txNumberInBlock` in the `SystemContext`, +which will be incremented with each new transaction and retrieve this variable from there. It is +[zeroed out](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/system-contracts/contracts/SystemContext.sol#L458) +at the end of the batch. + +### Bootloader implementation + +The bootloader has a memory segment dedicated to the ABI-encoded data of the L1ToL2Messenger to perform the +`publishPubdataAndClearState` call. + +At the end of the execution of the batch, the operator should provide the corresponding data into the bootloader memory, +i.e user L2→L1 logs, long messages, bytecodes, etc. After that, the +[call](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/system-contracts/bootloader/bootloader.yul#L2484) +is performed to the `L1Messenger` system contract, that should validate the adherence of the pubdata to the required +format + +## Bytecode Publishing + +Within pubdata, bytecodes are published in 1 of 2 ways: (1) uncompressed via `factoryDeps` (pre-boojum this is within +its own field, and post-boojum as part of the `totalPubdata`) and (2) compressed via long l2 → l1 messages. + +### Uncompressed Bytecode Publishing + +With Boojum, `factoryDeps` are included within the `totalPubdata` bytes and have the following format: +`number of bytecodes || forEachBytecode (length of bytecode(n) || bytecode(n))` . + +### Compressed Bytecode Publishing + +This part stays the same in a pre and post boojum zkSync. Unlike uncompressed bytecode which are published as part of +`factoryDeps`, compressed bytecodes are published as long l2 → l1 messages which can be seen +[here](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/system-contracts/contracts/Compressor.sol#L80). + +#### Bytecode Compression Algorithm — Server Side + +This is the part that is responsible for taking bytecode, that has already been chunked into 8 byte words, performing +validation, and compressing it. + +Each 8 byte word from the chunked bytecode is assigned a 2 byte index (constraint on size of dictionary of chunk → index +is 2^16 - 1 elements). The length of the dictionary, dictionary entries (index assumed through order), and indexes are +all concatenated together to yield the final compressed version. + +For bytecode to be considered valid it must satisfy the following: + +1. Bytecode length must be less than 2097120 ((2^16 - 1) \* 32) bytes. +2. Bytecode length must be a multiple of 32. +3. Number of 32-byte words cannot be even. + +The following is a simplified version of the algorithm: + +```python +statistic: Map[chunk, (count, first_pos)] +dictionary: Map[chunk, index] +encoded_data: List[index] + +for position, chunk in chunked_bytecode: + if chunk is in statistic: + statistic[chunk].count += 1 + else: + statistic[chunk] = (count=1, first_pos=pos) + +# We want the more frequently used bytes to have smaller ids to save on calldata (zero bytes cost less) +statistic.sort(primary=count, secondary=first_pos, order=desc) + +for index, chunk in enumerated(sorted_statistics): + dictionary[chunk] = index + +for chunk in chunked_bytecode: + encoded_data.append(dictionary[chunk]) + +return [len(dictionary), dictionary.keys(order=index asc), encoded_data] +``` + +#### Verification And Publishing — L2 Contract + +The function `publishCompressBytecode` takes in both the original `_bytecode` and the `_rawCompressedData` , the latter +of which comes from the output of the server’s compression algorithm. Looping over the encoded data, derived from +`_rawCompressedData` , the corresponding chunks are pulled from the dictionary and compared to the original byte code, +reverting if there is a mismatch. After the encoded data has been verified, it is published to L1 and marked accordingly +within the `KnownCodesStorage` contract. + +Pseudo-code implementation: + +```python +length_of_dict = _rawCompressedData[:2] +dictionary = _rawCompressedData[2:2 + length_of_dict * 8] # need to offset by bytes used to store length (2) and multiply by 8 for chunk size +encoded_data = _rawCompressedData[2 + length_of_dict * 8:] + +assert(len(dictionary) % 8 == 0) # each element should be 8 bytes +assert(num_entries(dictionary) <= 2^16) +assert(len(encoded_data) * 4 == len(_bytecode)) # given that each chunk is 8 bytes and each index is 2 bytes they should differ by a factor of 4 + +for (index, dict_index) in list(enumerate(encoded_data)): + encoded_chunk = dictionary[dict_index] + real_chunk = _bytecode.readUint64(index * 8) # need to pull from index * 8 to account for difference in element size + verify(encoded_chunk == real_chunk) + +# Sending the compressed bytecode to L1 for data availability +sendToL1(_rawCompressedBytecode) +markAsPublished(hash(_bytecode)) +``` + +## Storage diff publishing + +zkSync is a statediff-based rollup and so publishing the correct state diffs plays an integral role in ensuring data +availability. + +### How publishing of storage diffs worked before Boojum + +As always in order to understand the new system better, some information about the previous one is important. + +Before, the system contracts had no clue about storage diffs. It was the job of the operator to provide the +`initialStorageChanges` and `reapeatedStorageWrites` (more on the differences will be explained below). The information +to commit the block looked the following way: + +```solidity +struct CommitBlockInfo { + uint64 blockNumber; + uint64 timestamp; + uint64 indexRepeatedStorageChanges; + bytes32 newStateRoot; + uint256 numberOfLayer1Txs; + bytes32 l2LogsTreeRoot; + bytes32 priorityOperationsHash; + bytes initialStorageChanges; + bytes repeatedStorageChanges; + bytes l2Logs; + bytes[] l2ArbitraryLengthMessages; + bytes[] factoryDeps; +} + +``` + +These two fields would be then included into the block commitment and checked by the verifier. + +### Difference between initial and repeated writes + +zkSync publishes state changes that happened within the batch instead of transactions themselves. Meaning, that for +instance some storage slot `S` under account `A` has changed to value `V`, we could publish a triple of `A,S,V`. Users +by observing all the triples could restore the state of zkSync. However, note that our tree unlike Ethereum’s one is not +account based (i.e. there is no first layer of depth 160 of the merkle tree corresponding to accounts and second layer +of depth 256 of the merkle tree corresponding to users). Our tree is “flat”, i.e. a slot `S` under account `A` is just +stored in the leaf number `H(S,A)`. Our tree is of depth 256 + 8 (the 256 is for these hashed account/key pairs and 8 is +for potential shards in the future, we currently have only one shard and it is irrelevant for the rest of the document). + +We call this `H(S,A)` _derived key_, because it is derived from the address and the actual key in the storage of the +account. Since our tree is flat, whenever a change happens, we can publish a pair `DK, V`, where `DK=H(S,A)`. + +However, these is an optimization that could be done: + +- Whenever a change to a key is used for the first time, we publish a pair of `DK,V` and we assign some sequential id to + this derived key. This is called an _initial write_. It happens for the first time and that’s why we must publish the + full key. +- If this storage slot is published in some of the subsequent batches, instead of publishing the whole `DK`, we can use + the sequential id instead. This is called a _repeated write_. + +For instance, if the slots `A`,`B` (I’ll use latin letters instead of 32-byte hashes for readability) changed their +values to `12`,`13` accordingly, in the batch it happened they will be published in the following format: + +- `(A, 12), (B, 13)`. Let’s say that the last sequential id ever used is 6. Then, `A` will receive the id of `7` and B + will receive the id of `8`. + +Let’s say that in the next block, they changes their values to `13`,`14`. Then, their diff will be published in the +following format: + +- `(7, 13), (8,14)`. + +The id is permanently assigned to each storage key that was ever published. While in the description above it may not +seem like a huge boost, however, each `DK` is 32 bytes long and id is at most 8 bytes long. + +We call this id _enumeration_index_. + +Note, that the enumeration indexes are assigned in the order of sorted array of (address, key), i.e. they are internally +sorted. The enumeration indexes are part of the state merkle tree, it is **crucial** that the initial writes are +published in the correct order, so that anyone could restore the correct enum indexes for the storage slots. In +addition, an enumeration index of `0` indicates that the storage write is an initial write. + +### State diffs after Boojum upgrade + +Firstly, let’s define what we’ll call the `stateDiffs`. A _state diff_ is an element of the following structure. + +[https://github.com/matter-labs/era-zkevm_test_harness/blob/3cd647aa57fc2e1180bab53f7a3b61ec47502a46/circuit_definitions/src/encodings/state_diff_record.rs#L8](https://github.com/matter-labs/era-zkevm_test_harness/blob/3cd647aa57fc2e1180bab53f7a3b61ec47502a46/circuit_definitions/src/encodings/state_diff_record.rs#L8). + +Basically, it contains all the values which might interest us about the state diff: + +- `address` where the storage has been changed. +- `key` (the original key inside the address) +- `derived_key` — `H(key, address)` as described in the previous section. + - Note, the hashing algorithm currently used here is `Blake2s` +- `enumeration_index` — Enumeration index as explained above. It is equal to 0 if the write is initial and contains the + non-zero enumeration index if it is the repeated write (indexes are numerated starting from 1). +- `initial_value` — The value that was present in the key at the start of the batch +- `final_value` — The value that the key has changed to by the end of the batch. + +We will consider `stateDiffs` an array of such objects, sorted by (address, key). + +This is the internal structure that is used by the circuits to represent the state diffs. The most basic “compression” +algorithm is the one described above: + +- For initial writes, write the pair of (`derived_key`, `final_value`) +- For repeated writes write the pair of (`enumeration_index`, `final_value`). + +Note, that values like `initial_value`, `address` and `key` are not used in the "simplified" algorithm above, but they +will be helpful for the more advanced compression algorithms in the future. The +[algorithm](#state-diff-compression-format) for Boojum will already utilize the difference between the `initial_value` +and `final_value` for saving up on pubdata. + +### How the new pubdata verification would work + +#### L2 + +1. The operator provides both full `stateDiffs` (i.e. the array of the structs above) and the compressed state diffs + (i.e. the array which contains the state diffs, compressed by the algorithm explained + [below](#state-diff-compression-format)). +2. The L1Messenger must verify that the compressed version is consistent with the original stateDiffs. +3. Once verified, the L1Messenger will publish the _hash_ of the original state diff via a system log. It will also + include the compressed state diffs into the totalPubdata to be published onto L1. + +#### L1 + +1. During committing the block, the L1 + [verifies](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/contracts/ethereum/contracts/zksync/facets/Executor.sol#L139) + that the operator has provided the full preimage for the totalPubdata (which includes L2→L1 logs, L2→L1 messages, + bytecodes as well as the compressed state diffs). +2. The block commitment + [includes](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/contracts/ethereum/contracts/zksync/facets/Executor.sol#L462) + \*the hash of the `stateDiffs`. Thus, during ZKP verification will fail if the provided stateDiff hash is not + correct. + +It is a secure construction because the proof can be verified only if both the execution was correct and the hash of the +provided hash of the `stateDiffs` is correct. This means that the L1Messenger indeed received the array of correct +`stateDiffs` and, assuming the L1Messenger is working correctly, double-checked that the compression is of the correct +format, while L1 contracts on the commit stage double checked that the operator provided the preimage for the compressed +state diffs. + +### State diff compression format + +The following algorithm is used for the state diff compression: + +[State diff compression v1 spec](https://github.com/code-423n4/2023-10-zksync/blob/main/docs/Smart%20contract%20Section/Handling%20pubdata%20in%20Boojum/State%20diff%20compression%20v1%20spec.md) + +## General pubdata format + +At the end of the execution of the batch, the bootloader provides the `L1Messenger` with the preimages for the user +L2→L1 logs, L2→L1 long messages as well as uncompressed bytecodes. It also provides with compressed state diffs as well +as the original expanded state diff entries. + +It will check that the preimages are correct as well as the fact that the compression is correct. It will output the +following three values via system logs: + +- The root of the L2→L1 log Merkle tree. It will be stored and used for proving withdrawals. +- The hash of the `totalPubdata` (i.e. the pubdata that contains the preimages above as well as packed state diffs). +- The hash of the state diffs provided by the operator (it later on be included in the block commitment and its will be + enforced by the circuits). + +The `totalPubdata` has the following structure: + +1. First 4 bytes — the number of user L2→L1 logs in the batch +2. Then, the concatenation of packed L2→L1 user logs. +3. Next, 4 bytes — the number of long L2→L1 messages in the batch. +4. Then, the concatenation of L2→L1 messages, each in the format of `<4 byte length || actual_message>`. +5. Next, 4 bytes — the number of uncompressed bytecodes in the batch. +6. Then, the concatenation of uncompressed bytecodes, each in the format of `<4 byte length || actual_bytecode>`. +7. Next, 4 bytes — the length of the compressed state diffs. +8. Then, state diffs are compressed by the spec [above](#state-diff-compression-format). + +With Boojum, the interface for committing batches is the following one: + +```solidity +/// @notice Data needed to commit new batch +/// @param batchNumber Number of the committed batch +/// @param timestamp Unix timestamp denoting the start of the batch execution +/// @param indexRepeatedStorageChanges The serial number of the shortcut index that's used as a unique identifier for storage keys that were used twice or more +/// @param newStateRoot The state root of the full state tree +/// @param numberOfLayer1Txs Number of priority operations to be processed +/// @param priorityOperationsHash Hash of all priority operations from this batch +/// @param bootloaderHeapInitialContentsHash Hash of the initial contents of the bootloader heap. In practice it serves as the commitment to the transactions in the batch. +/// @param eventsQueueStateHash Hash of the events queue state. In practice it serves as the commitment to the events in the batch. +/// @param systemLogs concatenation of all L2 -> L1 system logs in the batch +/// @param totalL2ToL1Pubdata Total pubdata committed to as part of bootloader run. Contents are: l2Tol1Logs <> l2Tol1Messages <> publishedBytecodes <> stateDiffs +struct CommitBatchInfo { + uint64 batchNumber; + uint64 timestamp; + uint64 indexRepeatedStorageChanges; + bytes32 newStateRoot; + uint256 numberOfLayer1Txs; + bytes32 priorityOperationsHash; + bytes32 bootloaderHeapInitialContentsHash; + bytes32 eventsQueueStateHash; + bytes systemLogs; + bytes totalL2ToL1Pubdata; +} + +``` diff --git a/docs/specs/data_availability/reconstruction.md b/docs/specs/data_availability/reconstruction.md new file mode 100644 index 000000000000..4b86b4325a22 --- /dev/null +++ b/docs/specs/data_availability/reconstruction.md @@ -0,0 +1,7 @@ +# L2 State Recosntruction Tool + +Given that we post all data to L1, there is a tool, created by the [Equilibrium Team](https://equilibrium.co/) that +solely uses L1 pubdata for reconstructing the state and verifying that the state root on L1 can be created using +pubdata. A link to the repo can be found [here](https://github.com/eqlabs/zksync-state-reconstruct). The way the tool +works is by parsing out all the L1 pubdata for an executed batch, comparing the state roots after each batch is +processed. diff --git a/docs/specs/data_availability/validium_zk_porter.md b/docs/specs/data_availability/validium_zk_porter.md new file mode 100644 index 000000000000..e2b0b1408e68 --- /dev/null +++ b/docs/specs/data_availability/validium_zk_porter.md @@ -0,0 +1,7 @@ +# Validium and zkPorter + +The may choose not to post their data to L1, in which case they become a validium. This makes transactions there much +cheaper, but less secure. Because the ZK Stack uses state diffs to post data, it can combine the rollup and validium +features, by separating storage slots that need to post data from the ones that don't. This construction combines the +benefits of rollups and validiums, and it is called a +[zkPorter](https://blog.matter-labs.io/zkporter-composable-scalability-in-l2-beyond-zkrollup-2a30c4d69a75). diff --git a/docs/specs/img/L2_Components.png b/docs/specs/img/L2_Components.png new file mode 100644 index 0000000000000000000000000000000000000000..1f9dcf39392e8278d840ee70fb91dd7f63fc6983 GIT binary patch literal 75908 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 Tt6zK^kg6%&Q!JBz6!^aYwjtrc literal 0 HcmV?d00001 diff --git a/docs/specs/img/diamondProxy.jpg b/docs/specs/img/diamondProxy.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9e1bc58ce4232b56609148716a3896a5f4f4cdd4 GIT binary patch literal 127297 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%0A=g>&}Vv7{AA0OVA%~657kAt@<%B@ zQg3rB+#rme^KObgZHzoSMYx4A!n7kx@QuV++geq~pZ<r&XYZhQp7Z7xHsi#fWR`Tg*sn5d17|;T}fjLKQ~AjHuEqVl{>WK#9KvE z>Z6ZQbq%iY4bUhpm_dgM=)rc~VY}%3NM4p+`PI>r6=m z(@~aI=3f4^jKD*uPK@f9=x zZrNmiNbu|>cX@^iNvCNO=U^CumfQHF+}=WKRx9J`+40i>YCBtVnOP>6YQF(A0v6hh zXFW-8_UC*aU3cnbdO)E+VVT&c*nOkC2DbFUPh-TST4_v&j3Y5FED#%b)z?AYTg`i3 zF~dU)f1{JbY^rc)E3SP9jJh%%Y#L=^HkD)EvZQw^&2i3Fr>_CoIy4 zs~;l=y+mnPef4&&cmOBvI<;t;Nszx6gRHJ6N^lT^m_dAb=|tYxthIDIZK+Ot74GXL z_F`$s*ztPU!?AMZRbH{EwJXj`*Y(6~;KP|u(bKr8@F_w)aSO|sT|m49kJ00To>D4m z<p`5+shcC|6sv6~fe6`k^=0b$5vjb3 z>GPj>W@L1vb+5>N-B|Fu;_ml_-~ZoQ7%XQtk+UZ(;49H%fjdkCvKrH05Hh29t5`+K zMUlznI_VjSy_KJKe`pls`EIOqb;=J_+zZ`e20g)m8il9La2IVFX-%!dFRd}2372yu z{W<(DAgl^M0d}IqPZ%o_b&~UuxDhzl;iH`n^=jb<`6!|?YF`TvPL`LI#;s3GElXTUbgq zEcZ|5+%UE?$YQzrbH`0EykTbn}OMP*b<6fR7 z^)jVRQ&v$SaVgTJZacDaM2s$kkFVaJ4Hci{xj1`1X!UIrvtIvUW9*aQ2A~FP%i3nm zIcYg|2sCqj*&3eg5*Cn1??8SbKJ>kvsRB{2&_`QP!bEqR`e>7p92h09nT`7!q*Z{`r74FN3qH*#_3o%&=^zz6?H``;2i7R;s zjgI{^aq}NiSNJ!K-;0!bYh?Q$|9b?|Tlu%NnXsAt0;4+xxGEj>R_>IOFXIFKJ;enC zOudPowU6btzeC==(eYhw{|qnYY}i4_lIhF&wnKxZwjdIAwu*K>{*AuTQ(wVV@o6~~ z&H1>l-mwc~yw?kVRE-(jmW)99V;kzXk^(d#gJW(8DnoJ@BGuA90J7k|Cw!4u^&JZRV&dNB;(u| zIj?oa?tvLS-)ODS$6hgyy_yWfZ?s8e2f1F_v?@usKoDoYhz0vI2+H~8{0oxs7YA(k zz;>TI#S98F?lMw4OhzU-DOdHe4pHt%XF1G>ENDwyiMeexXLR9*lIx=nlC}!Ghd8&* z47fO0HtH$%5MGezhKEq7w>6XTVJaRIQ2&R)i7dv)_(vM53EE3mUM(Y+EYJ}h9Tlbq z4XA7)?c~DX9gc*1g9{Cm0Od~Z3Dr{kCi3P3$%`Mt?tSt9q_@*-53*4#_VdGMa2-IN zfBdTrNGm=C*-A7JwxQ4=fx*3z--;hJDs3rEYlmjbL&s(pPGV6*-=15e$e}5rsOw-amsLMS`)##62`s4@Ss5MGNJ!Kf z(dm>c&(!JmQLI9bSV(O8VX3m)W|>mE0!-Yjp*^TZtPHsIw`QU5kSD5w$?W6Ut%E~` zO;4is$ncd^1s!RCg5~h9WNz0Ur}2U60#crA^yRr~Wb~DDpicNHMP<7NwQJ0_=3<)4 zT@l$pOEa4fI=Yt)Hxsbnn=ZydEpRv}W_v*w{e@01gKw@t^(eywa>(g%X}D?A9MUn6 z!ZKW&HAZY>X|kUXNzXF)fJxeHran~V^%{Mn;Z*7S9`7?}Yf=l<~ zO5M3=g30gZiD|id=>k2%e|e0>TqU%2K+4D#oD6FlG)>C}inLX)FHW7GDj!UH>K;IA z2p21*9B5JStV7-0fZu1ZZ7>4q7&5lJ&dEw=al|0pbCw3X>L=`)zBOc@fy3F8JEFC& zeR?t7>Evklyv#Ra_XV}02hLp<7g`%sH`X6m=N%&ei}zOSyQZXyvEgG-dZXUPr(Tx{ z28-r{nFVs*Esr!7j;9OBZ(G`QOSiQN7Vgc)cH~Z*brA(?cZ4ajL9kvElHiE1#u|aW zree}7jXzl)6&fFKU+ht^iH9dW-|zN&+Z&Gb%)^3%wOgSTrWvLgCB00%i!UL>pqfU6 z>DcL1kNe?-*XOR9KbxsbRvW?@?oc-uv!TSHFl_4AYZE0 z-TTOJj)maXsNpSFA8q$lkf|v$mZ6$pjzBoZ33h(9{4-C$L06)hz53>1cUHt<*um2O z6;GV zzaEvZTze?vEV%~zu~-#>^iy70UZ8Tqy(q-N{(5|AOM*|>p=X@-N_N?u9F!`3RzgD| zXvz4)^Vr5Fr%U+)*jH8`2ueJ*%`#f-M65TS5A9_`pO2>ZpY1QDs!OWNG-<4ehNLN~ zcQz>`zYWsWKXw1?6G7ncSOQ8ZIuQ@;#YGf#v;(;K)N7SlETjY~SqfoLf*h6SJ2ox8 z(3rWcB5owKbW_xfK%|;r(krp^)~OH3#=3m3wbq810q9vr$ekH{0h+Dv>l~R`H%WS` z_~dk+o>s$=X_<4^?FH+~?xX;J-DSl3Si;RtR8r9BI|=Ia*sLNwH-V$;hz@O(IKJCa zPinR?Zs0LqEizkS>;3P?68MbGCvw?oNKZr}QJ7=U#@xf{(LwnVGf{G%PQAarl=C@8 z;r0PN`axR(oZ3TQ;g2uI_CAwY0LL{_i6aTvt@yQgkI{80+Y(IU6VAuKDc$yimRg|2b&d!_k^uKq4%$iZAe0DU@}Xgr*yut7ODi( zIV`xhRsi$)<`UYgZk=26yJuOqRz;e!gX!vNhbHE5!jJs@$4plmv_tmb=sILRY88;F zzZT|1TWxwwl=Rqf?Dg&(kAtfROk+Mv_zw;n#3~#sQ*hlov&jdh`V3KV(6(c=Sfa&S zhY=4UU$D%JBh@qzzWbUM-#)@+`@bIiJs5!NA3`vG8@TSl zvf}Xk$T@#7I`6#kuZE)7pMC#ZW`u%IZ!(swzLrXxS%GpcsI1#kP*&v1{VfkpC@Ulz z?-b1JUE>DaT7XmFsS!Ib0J=CP4v?F9E+odE!oCU8%v5LlQnDM0lY>|c6I=ow8!ZUXLL zE##M_wC<)@PHf(>V`pFjLk_!!1?iKBgFal`Q1nN%3n~psvqeEHNcTD#ZzoA@drKJH z+HxGmI>3&?s6mebXL_K3(>l6+bS^Epyj|>0t9AOuEK&%@9FAJNvcqtNd7i6p1k_SP4!yMxSmLqi)py#&eLjx3{Me%nsF7@+@|>NV+LJ@k_~o z%_2#!lCfT4SjdW8aLO-7%GF(M72D-|%s+7KZmj^0Z8Nr=ok>e8BkW(|Lx=Q<|?{#g~bxT5jw2 zpF_OEN*L`lDrV}Ti^;ZkDeh_3!j2vp0T;O*b@gVIA34eQ`A7oY7_kf3>=1tp5Wo@O z0;UsPp`L8@=`JE4@L(J*L)7CVGP&q{^~8i=ogF-#Gs&Yv-^0^9zOEHD3Enfmm))9F zAbwcoPL-s?B~E{s9%vf@PXYcejx1W8JYVAIEMw{6q#gTEzQlcxte}yrbF1_LW9b4g z8wdfjftSC^tWYz6!*L}H9S8N$5~zo)pimA2_Y?N4%@|RZ3W3UxXXNWO4WcI)(G9Nj zcqx8K7h1k9ZWE-{4hhd@Mth32P70bG@me(^iV#T|VVExL4nr6o$HaQV*rkLCFNR+) zm}E>7uWH0NDYKt0+gsT-1q+3L>4EGD}KvL{k1@ zbEhRKeCz!>9?5|EO@@bm!cG=gLrRD_GJS{yk70RhG+DzF;V1jmt~Dw+**S)!3{~gm zRCnEvykBw2sC1f!t+OUxxfoE|qd@F+ey@^pbWTyK`K_xdIhm>t^5*ASSTEvFq?x}Vg%0JLw$j8N175I=|J*;QAGa_1Qp3Vg0^Ph*e~Y{bm;I4?`aG5IKEM;Qcn1JZk`bZUBV`e}Ro4?6lIO1B7mbO6#Avtsjb(>mrElic%bm$s4~e=Hph{@=wfo!vNsPef}eKIoMZriS9ghb}r zcl!F3?2ofBvR3)rq5EaTYXIwvAHtF}>fyJ_h=Lr2@784Y8MBJ2lrNSRh4M9)kL=D} z`%qYaYg>^|g7wbfo zFU6f)N>hcAV3?LLZP40Qr96&|KN$xx>eBs&_IGRS$6R4AA!%<0s_%~cgRLBys?wUyBZ;&vqCO8an0$OXb& zS!+_x+kBTK(gNP)($1d4!Z8SsQ_r>iXVU%6hwr}qAif{U$F13J zr4x~~6%8&wmfcCCOo!A}2`ZV1oEb%Mc)xpKB#vstR3EyoWx2^%vI5&Bl@P6%? z-ugQc7SG@b92u`)&6sfc4>FQnz|gc^+-l<|%i{Qx{_2m?ijzbyfSI+G(+Ot_Qs9(I zE5?Y$2ky=|OqccZPjrDN3Avs@r|Fhl3a~_5X~YI! z?h9ek?B{b@YErhe1g~7PAhnDmU+WeXg13adYf(@ZGn(}gNRy>uZuJmJxkT8N{)2cS zNUr4^U2MJ}gg1*g(3Ddd(xvJ=JmGsFaR6rOf9J^#7<(hF%kw@IYKgOV@Ko`FRY(dk zX%QB!3Olt!cqaSGUNQb7Zwm>!AEei#i(a$919S+^-r&jJ+C}^!0g7V>&@%sOf(gdf zodLeL{hWp}#5zy!x_l0{RlS4r9wcMW;$XyoL2~#bDS{86^+3l@iOM_%2FNOK_C~#b z0|E{ZIo3lA&^IbPk60HB{AbWL*V+E^44=qXGkV??)m5cs85f*kDXKAdvz!krB*RFq zi~>*n$r>0&3ruU>v2};BQ=_1FRVM>xJ1r{9_6$$x7TucSThGLbvYcrZwCI>0mG5Ko zg-V;AUvRl3k5&Isa_71dfv+@lMHFUBYGH}u>*?q*MjPZodOg3R&k$?Tb&&Ds?15wD zb~)cojbB6@k80&f=wC3z$a03Ui=f58L+}1bo7Y`nm--n?<5+qE^{D03zC;>I;wKD2 z0LTm1(Qiu@><2&S$1+f{?sPayq5d=S>9w~Z$jeLg#H6+1$(@tio5EAgmY=~J;LvWy z9p`YSE$e)LVfID!fcL)H5%2C475aV%c39T8DKyi)thWtSu>t|~98dQcBL(W5;$dNj zn6}gc81|5iau4Q^6SmI8z&($(ty^tHW9K(domqF@G2^0#a%I}%x85f!{DhrafIRFb#GM~!4%+l+g=sg(v`ZC^z|3!XqG7a=p7L5Bk1%KJ2V&yIve+k_HX6#mkFCV#@+6QIG|y; zCbpi!)Z+{|@vvW3yHGTQ(A&(3B_&_gvfk?ik2tvb?*Hg~IT0_=e8XPp>y9;U4nLA+ z*20j{q?J?8&|YqyFGUCn1k_rhKNGH9bM=b%YL5_Ueev#7k21e`GX-`KOI8PgUz%xs z`yh(R%mHZhz>xHM)W9U;3z1!P5I!hR>9|8(9$s}`(SPhUjQnVB@~HUo+XjF6Q z*KKW0;cyy&-aKL=y+)+UtLV$Yob7$R$S$?Cfq9aq@Jq?dJnu5IZ$6iXzg|J!!ItCd z7jP&ES?D5d>3Giz+miQ{-bd&zw+)O%DY^~%Z@n9@lu;s#ilAF#9fD!|Xl{hD(e1ZQN`YHx< z749yS7u%q(l6fo-eGt(X-Z#gm1in-;c9vNqKE4lI+%=wN59Ab_H5fSp$*h0kg`rQGM=MV zLvx>trnQAx$z(PfJ%7DfQd&UF;-a`^K$)q$@|AQ|UD+*X!(&6~QuWxm@p-_u2uCwsgS?vbYzOXxwMZMi+w5xqVWW8; z);p>9nO7?ET726km&`NxgR;6x=a4ZNoMY5V2(xZT^MDt6Z85St%twm=Y)Pgq9(_8QON zx3HwgY3C1M`iSBhFZq26sDWZJTQ(2v&XHL}E~Fw7F;jtr2?WW#8NLnEfhe(!uRSny zHTWj+?p&#Ujf$i8Z2eKJpGu<+J{9#jLT}?_exuh-d8epjJK zi+a5o(~+z7qAa`JKsn80W0S*}zQ7ZASf(=f_`<+NTaX0Yei&xc9iV`?{64*L|Hm!f zXRS+H_HUos@bS4Z)ecKLW4)$}y@Q+wS4U2pV>D~q0Wx1Afn_xYA55VrY#SPe3fyit zeu)$s9dehj4ZftKFBlgu#TB)-p5z6pq1%rpIXe)fwr0#(?pemv6!#1g+AGAS;^zuN zd_5zxYI~OFVv;1MDYScu2V~o@U!1j^M3UdVxZ#njFjsShC0sd8bg^ zrVv^E>HVmY%f7W#`=r$M2Ik3M-GRsznpxwh(5@%;D9zN>vx<=vW3 zsEUZ5?KphyG4pC0Q*~MbS=>TJJe#p|xGB`w{KaqMbb+|rlFVjj#f#bM?Fu*KwSzAS zRhh$rTdX5NK5-g9C|_TOfkTNTD?KZtdb$QhyZoku$mZldatNIv>yPKYnCjg^6%dPY zQPmeo#9K1FG;C5qZA;-)hE?g)9;3^aR>urz^#U@nt{7HtGdFZU;Ad zvWp*5>ddG3)x>1$kM9)%2{*f0nMo*__)# zRibBq-3)=#o4&7l3DZR9xaXR$2$G7i`K=Un^eJ0nuKXLmh zSa7l@5ZZGxTy=zZ|9;njtnhvokRch?0HsMDpu%4J34?{1u^)q)Y*>ZS1O?`{zqz`4 zy21WH-=R!KW_RXSTY0~JJpH_;DD8mZ za+`mV+--_2zpIU@Xe3Oq|6}c+%y?#r0D67}y^WDU$Hi1@k*wr6UERiIairnV5Wh#& zEqfy4w8Jzc+pgh_y-27bi!ibe9u&dAW{n-XUP0N3=xo~sxzx;Wwa!)#8gv+H$P;377oaG2st4u(VUCL4`02sK%XXsGhz17O6uJ zb)>RpAaQ(2{hQF@n#EFUT4&L>h_H?nc9+pd$cuS{(=fWZS|FQcNOP+R^m=jiZT^1v z+^3rP8Bhq~@Ie%s;1KHFEL1&4xEyS`=XL9qe*n zqPOK}P?(?mMrDj#^sGTf4_++;sHXx8svq z>p5e91*b7L{sL_}cAj=OBdVrwd&j`?L`hmtNN%J^yX?2TC+^Pv38ZaJu&^4Y%HAoE z3Bcoa_}slSXDb3l9XcZPCYJGjLS7nn@vO3Rp7j`(m1FqLq04wFOmza`7iMbZYI+H>CALi6l5%T3lLY29${YKvOgZvgiW29} z0o3<$lqa|vd2Ji z-n*fAG9ex#3USd9bxUjOrtdR8ccvtE)?VYeuPZI1BbdNNez1gk2`B^;&qH2fJ|aa5 zRnL|rdqi#I=7fAdey7cFw{1n;_-6|mRZAz5!H3`04EQf4W!OR?^R zM5Ddk23tvLL6lj?-d$;~rD@F*+qOp?P|0hZlK(D>sQj$kY!&Wki~ubf+Ij4n&hJqz zOW1lOq}UNvtH@FZm~9mtV&sAq#Y(-~CN$t}l1XIY<%|3q4{SIx`Ib z)7Dy8?dRR6Igz2L%b1VgqG>qDQg59m*Qg*M!3SJ`XJ!rUUX8}^gkAhqE%&_34k3KM z$vO_UeFnCzfhD=Rxx*LYBt;;-02Vyo`*KlHqHKA&Ns?~okt{iw*f~>}4Gbo{QDqTK z50|i^h=$SVL5HhWFV=Iucj-8*?&D3ssHkwxtus;LoDoDZr+^MBa>wR$a)2fj-;mCI z_U&D+$LWcZish5p7E`3=w-1 zZo2jtaQ@_(&o?D)$>9&BZFKavppD*E4Mc5qn(4o1r^0?>XP^62u337-ZAo|d9*?{3 zp}J@AO<>vHjG&{2Nd5H%^0o)$mZpy+?$`~RJXz;{k*v`4oj$4=dR+;oo zn6kg8Z+$wUbY)uT)kcd-7D>>}0MUcJHM)_dMw1{H-lq-Kdpr(5binVr@kRB*7u;Vm zPqce zOQWB`w*V%TZ<_%ZU7E8gYzyr%w9q^)TmAsPzkpmJTXgGkOh}5}t#q#YXYSeWiWrk< zM$pb7whn;-t8Y};A#XXKt>%4`n@mH_S3PZce+}V`l0$p7^da9NKvT8FX*blCJ8bkg z|NG2Kl~P>$eYF)!TuB`|;s)u>+Kp>%k{E$KM;RK;1W1|KfZGDuO_IUrd|C*|_J(;; z&_mWmi*w^q%C7GnGu;MK74G-!%z7iHs%g%B4=xRMu)z~ty!$8^4G*nPVm$=(Reo{I z;1O!2u$SVMw52T{w%F;6MBJJA$T#Nb%My{RU$U%$+Hz*LJXD_>@7$Gkpv^+wYvqb= z$F6rmXj|$8N2`oo;a!T04oVqCM<09JU21=UI;Q#FTTi>v{ac0NDHrty=Cc)0G=FWv z>;%`t;nV=^5Q@a8%9i!E{n+{ATee`w#=KpzNqWf`n{b$P-VBN#3%Xx!?e_JZo+HXB zr@jV8CPZ5R|9A879KT7-flqR)7`2Bys8ogE>*X{g^MotChBMTc3Mo zT$a>OW$g(y*sE7k^kOao`yryxjKd!e^g3R@+Ud^OT^WR3C?i~AC#YQ_vXhNuE;`Dh zeFE-Cz2+!}?9}AQMIgb8Usf3%{0TGfuC@rfkEa$4&Ynu-_<;}8lF<*+IvhsbfAV~25;S`jreT8vZ9hBO9a{2sg4`@Y`VcCX!c z-~HqFGuP$1p1J1meSGJ6?&ovgpU-{Y9ro9rXeGwjS4Fq1qGkr<>_)^}2y%6SGV?_Y z0lndM2w={J#l)8^=srW{{LR<*gdJ6*7Q6W;TL#_E4wH0Dlz$Q5D$T+dumtAKeqt@5 zz-yoqH85FOIwL(@>^fVz4SC~xk^MjngA-qb%+aMU)FY#a+W>INXc*Xi1UGGY_`1EX z>&~i*YF=@!;kZiF3mFlmocew94=#*5Vw|Vv<~z=;r3db9o|ik-^}fr!P@zzMIGplk zw27|_;l8Xp_w^l4za@3Z=9(hEKvr;8K6^sVV74L-uL(&7CW9aoLy*YSF$sgyj`!lm ztNy6G26Fw6TEzd~3f1gTX$H7OOB}1c3TH^y3Uv_-*B)H#!@2fq`;LgH%_;dl>*H~^ z?z{RhLzCUx>+A_WFH6D9ND&LW5M?gSykKfl13D~_<@JF#gzbavLXAm1{K<}Zqwa$I zQA`wvOb4$^vybcpf#@KA?nQ18#uAu!p8lB%ZMDiwc^}Dyk5?yVAkgt+hQc?x;1}-5 zI3wo-YA1#PI5joOl`_CHL0U0R z`u6_5^0M3ZA%bo0virD|4K#KLF_7p^w!f^n|!BF(6^VG+8eTNKa{7GkXl7g;zDo#>AUsnB?h9AB4Wb!3HPYlqC5S1ZcHLeil%#W0ql3N#VS$f zA3SU-e8^@WdojVx%;6(!uFPxVglJC~R)_*+b&!S;SzYcw7oHBys!}2^X~J zMaU{3Q+EEb?|U>FOwq>wR&T0-iS6|_I=qq6&$XuQccsPm?r z5tt;J^BP{mKJUd4bQc5BIaj_nLfPmVO=-K6_hgrHPQS^uWmRB!8uI9XJLbAc5A5gf zf;U3)aE|wRyh{#63oBI~934?)6RXkn>+uCLJ}0a36xEp9LJ^1Z9C@w4gpM5zFI7)F%n?}-BG;`8+ z$gWa~8XuZ8n==uw336H1^u%uGySYXVY)P+tu?Jp+pOT$n8{Sx2xGX_a&Ln#GU{d|u z%_VvNVIsTB%Ew`d)+=8}wn|}E$o4bKp{z0G7wU;Df!tWl8Y8`WarRebs1ae*eK)Ri zgHUaS?tP$sbsniy&;7GXft(vC;&H&06$Eck!`yA1xCp9I%Fyux58EQa_;wY zuulm6QfuZ`L(u4L@l~)h|39}%Xy8TVO{)p+b z@kZYK>=3TOP!d7<>{$HTsu?Sw`6bIfF243+|%AG}8&A3c-1!>?> zbRMcoQ+2`1njvI_jp&=}h@KD9)jxI}dY|e%QEec3vlM>K$Z^BJV!pJ-QOq#k0QS)v z&Ty(JiG+-4NDbmSFq~fp2-Q_{;3@4|mR3F$-5%eThGp5oGL;%~vu>L5I7qTgTs(Rk z2RyeBIDh#862WuO18(`+*2VwFsAIPq!-QELbBhIuLQG)Of?jD*WG7RAahTc>4vGh) zVf4;)FY@VFD&&7v9OhAI)*@`QyS-%Oice^q_2CGtDy4&SoWvBPCMLM1yQklxru9DQaboea4wSJKJ{q1O%-#UX+tL~^ec!T2h)J#%uM6*? z444ViD5j6u!UJD}UA4_1;pZnfSECp?+nT?Spu0I)ck_57Bdi3;NEMnn4&*s|53h+_ z{dJp4fNe?%?8ejhH&on78(lRvf9|vKRrR?>lL+yHJ=0jrAsn}>dTr%nU)*;@LUG8@ z2f5JwkX?Do zusmV8(47{oG!>tln;)27AtjzpXJo0A#nLIr9 zf?;=-Aj`tUo#fScx5G@4Y}&P{ zbqIG}1D-AcWIe;GGxtn6l_*Fyj7puA+ju`m=MjMACKk?g*HkYfwK$w}YXlOYJX{bmBre)J8Q3;gHbEoDQq0#rC7due_$L;dSqCg_uFW@cUWm5sZQIkr27WLdV zrOxtKa)QnnkF{YxYCR@Qw$d!u?k~=mPN~`Q5;T&&8&T@Eb2Qf&b#Ro;UV8(ZJ8x&o zDiL$c!6M!CW{_-dG=viZ)a1kH+C7ZpuNh|O;Tpq+YO{e`9%{bonIuJn0HLG?_ov?} z7j_yezVj|5MM98SzPQ_3+p*&G*lRtt2&#E(k&Pnjj8LMf;ng5T$i1Ru#bZ-8k91vF z+pv47LW9|jeXn0*`3J4{>eOn7RaN{DrT|&oHKPn&ovgVO^Y(vB+%{LnL#q5LcRR@d zeyBt0LqLr9l&}0!E$%lmFTc3|H`{{EW9UyHu`HBnjG*O#@^k-HkCzB8`Ym+%*c`ct z*@8hXW}n+=^RU@B*%-^FW@Njr)Z(vaPLhgk-u5?9FQQ&XNq%!mHUTrD%Wf%Rz?->K zyMCw;9G|T>7y^SGEUrcA^u5~M3`BODoH2>)k#BybEPq>RGUfcaCK>kPc!*e-*CIef zzC-vMIBpHk+}1*RPraY?O;s5Oi})$i?t;Si7M+RbEf3sImN}#k8Qf*!x2P_YwnL?b z$j4xr-~U`wWr#C;aUH_nciL0bhgSS<*&XvP_1of8vq~3%lEWDvjDxBkArWAMntKgi zXS5mSxDT(MbS~`GSHK4sIb;+-V8KIZZj3`qibky%$nAD5boHE3wi?hDYdUm()FLzZ z-W^{~(LDp{K^p$`@Y1<@XvCf#hGZZcRqby2r8MP&H|3eG>T=PFdM%^8$G*~kHf7TY zEX@mgX}vhl{A^0f(U>wmWRoBE+AUJwx$?*b*!O1l^d-KxJQe1|Qv6_-x&TVuJRxh` z%CRsi-Lf%zf~J(IMkCGVn>GqMT#~$+#+{pH2Z69=*@8tg1Y*p<+}*y;BadpafpuM< znmNkUynJ}tt~zmmZ_`QhtNQ@&ij-k}0a_v%4lXtMC)Uglgp>vN%?H6<2qht?XqF(h zENp#nt5x3MKoF5#OT7Pa#$fEWakBgZuY7y?je}P>(~?~8;0Da5EHf%(Ak2hO<`KFS z)qhCyg$;eK-b$+mQwJ@xvDB7tfz)j~S8G={T;LZbk_MHcqU&r!@KE$OXQ_I@ULL!b zId+ec8w8)Sb{dvFL(b(+GHj_!b;z9_1=EZGN`Xq#4_3$Hg+C&Wp?68!hde8l2$Q^` zKUU2IJ4M6f7egk&N%jrF_d5#PM0 zdiLb`!G-Odp*D|nAXjRS)9tClLynnh@SOyeY)s5v_vQUe5+aoUau!dXvcS7SIe|Jn zYv2xsQZ?WuBbdBhWqV2NM$HA8u2)Py+8%+bi1#-86$rAl(S7P{IH&=$*-tQrjn&?_ zdP@c>6}U2VhTsu?Fj|I$Z@h`r4XM$~GYW#+x6j(D zPLiP!SR}<-L0+I?#j!9pTN|%HekoWPtkTveYJcAOt@%rKL6!~-zClZHz9OCsjb22_ z+|y%P5i_}M;(2k5nWLolu_2(<5s#u2$o6Sd$MEPZ*F?7x9+ZAO*M5`P%&S4U znnAE9IuJX0Y(u~Be0U1^aFkb+Cfej%;XYebtLJw62LhtGFXHZ!377k5rfVtkhm9Wr z6)1GO;dht2)4bSl-fAa97mCty0M3-Pcxy3{80Wg;LGVuefv>oE7uWC!glrB3nuYqr z7x=rPADw?C8|(l`kEgEET-K_XN8JW?7RN_HBmp}do!59{QxM{$>Ep=8BFog)g!o0L=%qA4k+QaBP=WSIvbTV z8%a2)*cDDpEKsq4?11G1v2?$VX+W!;nBxbB1q`qRlAW9rVSt$g^$#uAQjp_fLrBK9 zO5i@FAh#yFb!WGh!N0)Q4NRo86<0*ZJEAkAc+yyu%orejia}0*w*Q=b(C#^gnt^*Sb=Ed69BJ@ z`*3gT0SS;^D7Wa>&C~?J@2j|kK;FPUTqV4|gDf%lS_?uIdtAhkEuQCrv05Pur!PE; zF|)6dzB9Yr>VAEU=)2KvOk8C_Ml<#dr1&r7nE^!iiF!N`PDju(>F8BuCJ=U&@eMQ? z2AuTj`KX^!$?OO}{>;tkUhO(W*`k>FgF_L;Rv-5nI1EC5E%Qqmh0ib|K#ZTFL&%z9 z`iArW8A#Oo4J^yL*bupa(mzGC@K2#^8nl}mGzRz#&~8l@_zGKhc54~@J1heTu%n;q zYXiBMy+Fd*+5iRG*ST;k-yn~*6`yWvlebm$Z5&!qX<6x%Cl7FKBfKZ>1y>4lrUo&W zJ|i1H&B*=q`9_AWHn5zar5Ffi{&zo(a3VME(p)&M6h0%dsw>@u3efc&LJCw>Fgrqy z7t;*y52*a~K`IKpGTNe&Y64sA*)nlkhsTyZZacDNkN-h?45fco-YSrf`G#15Za|Zb z;Sn}4dVpJQz3*d8`g58Su`*JaK}(gfm&zvG+!-@fCoOc09x(EKNP^3VA0Kk<1^<9mNHTXiFob%Xo))AZnG zs_^d|L)W{0cUk_fgeU!X`0xweg#4mBlBBVeol%6cC1i=2RJJ5h5@ISlAz5S0NLiAlNQyKG*~y+IlRZhY zhZuvhXT~xnv;9xs`}@6r|KGhlzvu4xKF|H!_qS80ypD6Ob6wZ@e6G*-{(LUxN9F{; zbHc#b0AOJO03+}Zz$5_Y^@H4;0l?H0*be}}R)Cd-4}gF_v4DSoC^vxZm!APZ2K*lY zAWvET&u^cy{`On0b59|^{mcfo2Dbbf;N8mR%ykaJvIk)0Wr6UrFk1i^*e*NEFUR+r zz8@^C5H|KL99ucJae+5f@_-$HKv>xz?Cfmdt64(9-veyC?0mab^tSMuU*g!~v-9AM zdl_4$j+Qp=vgjd7t6shq&bduMP)Jy0uZ*nRzWr+Ihcq;`w2vLvH!w6ZK5^!(Yd zxOl;Fv9hs2*f_qAi-k4t`@nhG*mtXJ;nOqcxa7mX=irU4JCEMWC~f4FQneuNx_qr? zn}D<$VK3?X(0&=&e{NvmKQyvG5A6SpYZN#Ju>NvDSXm+L5D0{Q3p;ph*}CQXv31+l zUyg0R9@~F8c6>jee?FLCBP?JWY;0^C;9njtPA;B*_+XBK#8t%{0k%U}Kw^UM0tkTq z=2?s!@bmmR2mfX?0AYUkN!$Ns-}L7?^{2HO!tD7;+aPU2UNdx1fu$7H95)ekT9(;l zi7+L9VtCeOR|c2v+B{2ui)1t*mdEjK_)gw-pDzbynE=sT*Y1pY2sX-w^%NJ^K0%Ax z2Rj~GOPk$GE1t~pejal8RD?Q`B3GvL^qJ#_29dNolAoeoY6ehw(&oddIvI$u$p+d} zXgH3*#{|NoFdtfpQ%rzG-HepU;8mu`1=6=8cOZ!TXPJNqbub2?$p6)4#9uvH^2+sR zlRxM3eg?9SlUZu^-we;XAr+e{GJQRA&&(F1 z*N}KFo}49WNVL*K{QRJPs_=bnZJxJpn|I%j-zs%i|1#}L&)QPR#7w+esKuCW z!?wvi`&~{=L=0x-Bnrs{E`}as0>JDrM#K=8m{dxXV=K#v=`^i5)`RAWXga68F0F>GPqx?e~ zBO+d#MdC#pR8ejNj6EZE?)-CT$@syKt)aeGO54f)(w>pWIj|Q#aLn!6wIWD6(7pH- z=X;|6%v=GU36SOygeWwSP~%o0+jqW!2~1V6tS^1PPle-k`tot^NRcpZ1FDcYMMlR$Au z*p^U3;%YZuBeCIV`ao!iJ}QCK)-T?aHFiqG?$s;AReXuYIa<1FjwPCe$U22`p*<_S(!zO)6Hr-UeRe zg(06n5Yo_cC7&py$$RT2&P-tVxA(dz-|^Uh>jvEoy;zB$D#D2GnSgN0R}_i_Yz~jW z=8nMZdyu;^&2ZFJu-oJ+c&-1VOvX-Vd+7$kmHR@dUz*HseLIH!Pyk+3G$HjUn5~01 zA(tfJGfSMIlvx3e%?}(y6;^MM#4LGMcO{w&Ps4@C8-*w`HR17&AM$AQc?46_P8Q_teb5q}pf4lEFy=W+>y})^sV)F*G z4uP(%0VW;9mDh4+xUZ4JSqYw46vRSN+NhnH}hhg?x zgAMdD!JO7~-iF$3(CIZy6DQTVWQ>yeRAcCP`%3VyGt5&msvnFpCXUkmTwoobxqRyw zdB7gagHpoV$(Ykpf1{IIw&W4}CtAd$P`}O_wA2KOA;RZtRweN1hA+) zdOJ8Mvr%!q^c8rK@rGWLk`t}74R33D1S9op$DA2s9)0JDkWkO{-LyEE1HEqOc?OZx z@M1pm+po;)U-QFCq`Mel$FO#AA3DcwIO%sdxQ>VM=uLO&5;BVo@h=-X=*y`Z* zK8A!wujg5e%CGNmLyvh38G`RnQr}H$g^j|h53UoLzzy5Ez+C8;U&&?(5B8RVY*bup zcm?Zn1;?I19=C7ULG%q(7tNpz{(0;<;Mg0sfMZube1kTaKrvwgo|X$lYSYr%KsNZ~ zuhegf@!YjQ9BuYo8@zx~%VhB5M%B?9O-n2dspE*$-@e}q%$0}*V%l-<=k+(gf#gui z1g_{W5Ce_9bD}T9x_%>RYg`p8eK~x@BUH!O3-QV9x8b~Dzh1s+K+F5E>PiZcryXFsc-GSk;7+@4#(-~EQK-LogFfGsZwBLnN&gvP|0n zu1Ib@8jOM8xV}KWXM?ddh!(1rg`nCbP&~v>g6)MPc{%#@!3C}|_t)9>@Q47Xj2$kG z76A}cd+^4ma3;{0aNA>@I79i3^_uNRpg7{qN$pUSCY~0YK)E6=4K^2nHbZ9HjhEKTaGaA3-Ru z2f+8<(z%Sa+kzqeX%rYjMT1BQo;Y-ee<9<71rum?N6Eu5$4N&+{SIUQEJLc$JW$+o zN*ug)FW41+V0DE)BdQ{syr+5BK`hoPJAD! zZ=Hp<1b&8K`RM_A4aUiNZt4wARGvUvn!iN#Mt7R_wO4_nu4n=z-kmHd1pGIn;yfnc z*aoBPSdAxtOCfqp2Ht=xFvd5#Z#PJ|cNEUuLur~=rK%+!kTJEkOXAyKb+q=$!!J0~ zzaJSYq#v{Bt4Q+=%(v5b@v0Y=@sDHKaF(>Jaf}xlce8aj;8M{A#FCJ=klS>)i!qJ* zaJ3*@8h=L76PAt zY7K<=B??;e(1ne61vlu>pWqy!ThZ?rQY6{vdf~!(?FyT&!qdmxP;=DuSi2R{EkmWF zK-+=Skd9A2tJpgTFT{vyQ>y1oT%~tdO0d-}zq$InnO9F-$Mo(R6e2rVj;R0#3{KvK zNE~F@#OkA5rVt}DCwrNIQyOx_rAb?*zo_`#Rs|)e6>I4bY}vcm`I2|nwg3^$02CPN z493%TrnJuy>Xf755){OB<(q~{+=F@kYI$BmPDfJZJ{#GnTi(BKaM|RdlV$t|mQ8{_ zDrFKgLM04=iiwM-JUOZ+^7<9^{^O;6{kbn6knR-NenrTwjA&`#B=_?{#P$*Kx0pZ` zqJ25KL5Wyd39bj!PUtpD*`(aW09%U0$pgl#qBm7-zj@_{y`-kS{${4evT15LlPd4( z_%gl4rzLwhfyL6m7@>z`K{?Q?)Ui8I=dlx*Mi^^1XuR+IxKAT{@4oFy`QrD^sb8kD zw8UDoR(E#1*G7PfM`e0SJ6*Z$2^WbY>Nzng4yEFD&D(GI@tbWjv+4KCG7Ai41djW& zn<#NPPC(`nEVR><>!hQD^Hjo2^c9+(_vHn#Wf!?*hXCPiEq>Czc62l-DjH>&!1Phl z2L09&X3fNEK_A#@{&+-e(ks*eRS45jNK*x^dmThq!5s2G_6GE` ztS#*{Kf?a={HM+Vj5+XQ#tLXHI47h{8n$28McH&R0n})AXb5sQ`W;du9<6P>Fm)K@ zp@vKG-^Ieeehi;wD|e%CZtD10!`caS{(g5k)<=DdqRY0&(YZK+xj4+o`;`OBW($sB zqjs93tzlP1WjQF4qvuO)j^5o(apK$`v(ts@P|Oib-E80@F4MBCz!*%K1C;SZe)$9- z_bldbp5Z}zY9dXp-`Ba8XK?02!FJwnRs`5$H3?}-k7`h&FaZLhZC1&T%57kDJh1fj zO0LJD`}zm;MMp%Vr{6f8lZ+6eexy$o!`fin4HC4YF`B6BgmjG-d0-EsdPM)41NYFS zd*_QoOOL?zE?}wxZ`Owrn+H1Y( zLLVIuS~egpzh(kFK}nNVt+<^YcwS}apd}s6q)+N8iG{JfpS%sDTk>9!GbKfjKyD&? zFcEMulrC}a;}J}Fz3TJXLqk&hh7NArWgzL;vANgPG64Y(E(6M5qIYw3b!y~|2~X&$ z8eF`QmjvFlDlRL=X;k`(UM68}=%DEqaf#SZIT^%8F*5?S5ywNIGFI}!mVOh|;#32N ztFBw)S6#fvXNv4nUN>yTHsOfj-Gni1v+cLeM?M(5n+8)9(k>Z(`_1+k@Y?gWl&&j; zm^O)wSnqORaMlr{T12i`m^oy)kEGe(66VcrU7vQ7r*bs8473-aZ_~u-FF}C<>ML^+ zEJTTfP(PG$!QRLRY9ALrBg@{KU*7%M|NKdJ!?OXJvo(_q-M#fv1UruA<|b{4J5D*X zMu`Tsp_LVWsh&Sq|q z`0m!a+-%tMkfl}tEl62t7V*A;@H7)YkGeoYR}z;`4wa~Lje5G698`$oUcr*?!T+>j9A%D?m!PkRtZ%Ze7 z-Ev&fdhd&T4p*jxOejYm>anIQH>UO}q~2Yu(P3C2Dq(4j)Y&~L0m z>9WD!3do>0Jjt2@evpenm^7O-377`PP!hGISn?p|$!mN>y z!B&lEad6Fyna7e(USa}$t)X+jo9ZD3`+tOXHmxSHv?Lg4cYTDNW&*u|C8;?AsX&TlGY z9=3UEebs6aqzQXo?AGBixxtPCrbz>}kNg%m2Oc2$+;9PsFINVhAObz+^{fp4DW(Q9y#shsq{-gWtA*vA}{0m z*2`NoA+nJ>S*VTrXm^Sr0o(2!-2m-H`4BK8zBVuG*3BXFe^w6HPrC{4eMmQl`Z z0mbG2{uB7$OKu$nA;MU?2Mk90)Z7 zhIpw;L1yQPh{a{%MiWCaE~CG&A_Q4+e1ap!*y^3i0l-&cDe9BIuE%?n7FD`AWHMB~ z4I{2|y^vNKMLIj zrISNEN}0rG_HAwo$D_f}Ke0(}I7*MxBAUfbGl3^h)Q3iyfYdLsxIY#|l0$cL=^AEx zkkxsTcrg}UbST9|x7T`f-}SFHLY>i>=81N1MgVbB%waqad@H&~0zM!(#nIBWui|>W zf3eLRnF)BqPZs&2PrMF?JaULsKmD{eR!;Po)mjATZT*N@VWEJYFbB}s!>u{qXIWid zRLrLNQ#2-vJSMDKm;e{v)-}mcCetCL`h9u0)BdtOpJZlZ-fg{@QC0~5dSPmf@vrZl z{i9@FKzv$0sD(cm1QT4Q zUVx!22(gpV&IF*C*$(+%6zgUta2MLFOoh>OLDfw9-%PLm->mch9)0^!5G%+km%Y%? zc|J_H8Lx*Y=qNV*#amb^h+wk^rCYHFsJSO4en-gs!OUq8N4NxfIwWC{NHT&F!UPPA zf0>yo7%dDfi~&I}gk`$$-tTCOKbD05S5Elg_))Mb$S0S^(JUk1Wrw=wCEQAY%`XBa zQj4DMcN;PDJfonRVb=BUx? zol%r3K_u|oh}K^SqLIevl-QBP2sX~wV4N*>1z~%Cv@j#+zWfUhF@dqNmx%kG z_Uu&QV*GFv0R&g(2qGS@h-@Im4t29HEYru}vkY_fpp?acI)pPas+o~7O#AG61a{)z z2sHH5zWH~zWB%Uwel+p}Nub;s)qzY}F}^+u#>m;%-y!SUVau(WdzirKm)&39c?>kj zRCPx(GI0`Ql7|i_Z=0LCZUp$yVIg8{3A3D*4AliaKfVNr4444|3Fzn5U z@wx+MX~O942tpac3AeegR++(uSudEy{M9pm)$Gsy{9kkWbAEo(&rklcY532rKOYfS z5W@kJe$@$+0g)2N!tCeFF#=cQXhtO&YGY=mgN2dj>5(W;k>2_=zmjz<#| zA5!Gd*_V|)V!?!t#Rwc*r~Sp+ns_;kuZwvmzO^BI)7~vMXp&3^n^O0SiVd=CGf7Iccm41*JrS zRy*-)Ax2)y<1pC=yl0~s^x{~2xsN$~z6%J^{XJ>)*S5r5S}@;apU8sQX_m{3=-^fC z#}J08JRyo64(c4og4evGnZSyf4ycM;EWp!w)@KkeHfX6<%}^?vEF%&u3gP*C^$G=} z8e=+!(eE(ptCM*8Awy)4RU-%(PNAizEVeTNBpd`P^f~&mtbhK!UirP9pUnC%%#?r6 zzr*r_C;-SCjH}ErQE<{EILPBX;@e(D4RVMb7GQ{!V`=&x=49;|Bh4pYgoq)fmKr!_u!A~kSD zdQjce#<}`MX}q3b>TF&5_)$?u*R37f+7$q?L;(Ky8BQH7gdRbN)tgO~a0emV@Q`&y z(NiS5cUNt|dL>!SVs5hUoMeJnjdH{4F5O=e28htv!Hj&jTGN{P0WU(?jo=QQJ0egD zKaU*bXw0_-asnkMpQcV>pG>&TV0!|VZ#WUYPtozJY_-GQ7N0{$UyL7Z_MudOzT;W>biW#O=?DrF62_ zIPa#IPy8Con(i**asO&1{ z(a6{cel&X^w!?=lbD#7TaiQQy;Vof`J=V}7*HRl3j8cZzgAZcH`;ysQ4V|w{pk<0} z>}|j&#$xCG;)(Ouf|8nh=A{gi&Q#j291-~9$IlmxdNj6aIk#Ns`8;O)%cN%Z<4Lu^ zZSR9G>1pYPoMxYwKgex+qV`qVsmfQ89(CU9FS25K4YA8|TCnIm<#pY|C#p4qv-{6o z-Pf4ta{dEsJ9N<(Jdq zzCs`r8k>?64B+&gZQW~~)}sd$iU}{vE*d@2+BLb`m@SIKi-k?Xwa-6P*7|l%ulL1< z+dFzc7?tJ1Z{P7iR(S(T=-t)$AodJlMI5iORxf9A=WZ&YEuK0 zo`suU#5p52zmdEdX;L7*^!LC${?-Oq;_m;3QT&pSc?IzepdDlar6Bp?HxHip1Hyg( zh2hQ6A1u}aX~DS}3{!P!Mu>G~cZby~A6p!opi84SAiIG=WJBNfYJb7G4GvQe(+m;o zEI8O>1^GgXS{0paU-BRj|f?tZ@UIRSoL&awgz>7&l`3T01lOb%x0gf*N(c-!uuup(Qu#rcrSPEVsjipmZL!k17{$F- zE40jr#6vI*^CK`B6hFNftw~d&V4pF%k(KsfS&Q5b_Vfq2M+?6_s?U`$h<)g_$EE4f z=}3;fcX)iJNfZ^FCv+6k{)CH?R%qW$C}G36wtKjFO2!tR6I2=Ae_TvE=i$TLsu*1z{MM?ny%XY!uZgjqEbi6+0ed5WQnyNU5Eo%Dv>x^MaXoo}(0-h7yftDZz zE}Di?k1S{pKHX%bd56H$yuaj?g};A0e>@0PW#FCOQnFGO_ihKAyxh})aKVo#x8lnU z6W}7Kv!wKd=z7n|g@@VIFB6T=wGGORkJUf5V3$Mqbwv{(z9A2an7|gsd$=ZXdC}IN zs%4LK>z=f(pr871!BC;C#W=)YjgSLs6;fUkY;8)hgCA!soUqmF^)bND@u*S#3< z;y>H3oJ9C#@7*X!DQve1A)T*}upHsPd~2Ma)Rod=Vio>1 z6#2&c#R+jPRhx%~4_{t>F3XYpelbyHlQe<(_>7DkR`MPmo%G6auYH6|J6(nAJDJq# zsTJCz$(OaACSYZ;#WI>rjQ7ivU34sFA_LAr9LQ;gZKIieP@t(^Y|yW4>)kjo>he`e zH_2!#$7fN7{f*ZUK!TTgj#Af-6w`5{!coGcy`MJK?3H@eOqNdVi4=1eDQp|D3W{fO zMOCTf!S-F&>?qbCN7EnIt5daT*C~3>f>bSW^*!pL27`XpxJOQ|N>cSV_BwvdeQLDL z<@0;1JR!((_edRCkL2ApOB0_mW2HojYqU4;M9RSj{CW+=-`u{HCz+@o7;p4>`)t7D zWi2USDB1&8sKfNAQxE;pZi}Y*-uez7;iAV zXnE90;&NNbO?%0T9+Y`TE1SNu@TA>fS;dR4owvnsn#z^QLvDa?KTM^P9*uGBa+$Ka z0oO#Ckj9fHO}&i=6qeO5qCN^7_7I*5nk?rX&DYv5cGqIpcEbzhr?+eY>1UriSPO0e zRIIP^4ZVX2>-bzE`I75M$vLe{sx9_%#O?^Mk~ONL>=C=Z<5d=SE;b4gqypHdFLi2} ze~3k=j59jN5jFIeg*Z1qTpvfI;hQj`~CeN#=VQb=|Out#u^sXPUEklwlRb$ zxG7~8TQ?hWI`J!`canNC%DOsegmd@HxXasHw%#h)87n;;_F1nT0?UW-F+_q6PtM#@ zqH#?v@aN7}cky>_>y(zRP?A0>A!8-v#BsCxSStuY*qA682{}4gS%e|qCZ$J3voG=< zb8^a&dE*&V>g*dWY1bm==fD_p->eGLNjOE>O&mHwqDQq*x#e0XbyZDFMaHMP@8??^ z94P(fMCRiVjrqD!(hZ}?E7PK%fQim8lJSh&HgxxrNQ@_bII|s7Q4(3Nk{KjGH7!lk zuX{4-kY}QHqEq1#aBRCO-!(0;w8Jc>LXeiq5sO>2OaW`ZN1(kJE%>s>2r(UT0uzYA z^VFZoO;hG{H{j+!c_ZoVZABMB3-5#S1nEbk`)`HO#h^aKlpvndfk#F@YRCdX>GPFr?MfCxKB7sqdbPIhO?|!OY zKAFF*K;h!k{l~be`6V=dS-U~Mk{CQ6TI8sAk(#Kh{TD^VX?8C~=kSFN*7FqJu2S+_ z0Cq4eczZCsoGOCumDmbmMJ91~kmWP66`^~ef*nCCow-uFt+mK^N;0tqgMArXl3O&` z4gnkxt##P&8IVzj7Qc~hwl*9haYPr=^s^TR;m57EIho9PNk7oaP7HdaQ_0>ub*0#h zZCw+f5$W+57jAxXG6|Zn1{3qJNzRGV(bB%4m>s(y@@jp-ROIBb!rS-b1z>@)k}kp= zA3@&}fy8BsNkForqzY=s-3B#`v+Ce{+@}sb8PV#~+3o!CJm~OMeQfm+S?ay`Y6>i$ z+G$C-5i%8e>|}%4u1<%&xqaDBEW(G2O};hw9hkss`BVoUd+hVXYy!7&u-n`Wclb$t*zSPRQ2G{K^bD#mo`Tw=v&QCXwY*Nen-y(db@#bG>G5gP*sO( zM>bw>pNtxx&7JGLr>~ueD=gj-@}&+3V{~AuX4>Vsl^Zd_C|^Qd7u2MNzv$D|uXREe zX?NRQxJpM(S*An81f;_Z(=m<5X`Gib77#29VK+g(V-7hV58d-trL7d2>lW1>zE<~8 zj&0>&s2n7ko`>di!_liMD27Gz7Ra%#tE*`p$EfFSahiHPLSn zqW;hkbA&5C3@zPT(RJ>#!$GB7QM!avrNEUp4aev9=tw;dd)uWEK+ zVit}N*S}=u&WpUPMCiT~2)yKw;P%aU)!9OWm1De7(rOqK+877q9UK&_eqe`~s*a;w zx-=PCQ&AIo?us#YA9Cy{>0mQo>t~@e`*+>h{aQ2b2x}cK99OoHzz{JyTq|!D4 zGB@ZJ>bq0ahD7 z=JVBqo@2L4Kbd_#Wu0jksUpfD%9y@+Rnh_W94w*Py_oDv)$7OzYHkef4GL@<7nUJ}VHq1$u}6OX=NhGVX|Z2L3!}8@b=?Vm;wE5vuBixyYfklg%DGg(OU|#h zWgjiIH42iR{tTAHa}I+Gk_?;KDcxWYnwM1FY-(s(-&GlA;RXJsEYj)*h{^(PB z7Df9FPE8zX^SLYkl(K80ot}=Hc&3UHAbRBm%0{V+UZ1?@d{;1$qj!X%A~RsAESAsu z;fQqF&xsqDY%qfg6>HPj(o@v9k`slr`p&><8_CpnAA0)<43%X2Hhvc(=0xr zI~x!wS4XI z>K9e3wx}nj6&`$E7q{5D$3EVkN}HxsQzaQ6co{vLVD;c(d~2$6gnyCTDjHuGT30W> zz4=o0%lPwn=dT9GBsf?K*34ep1jhJb*G3P4T}Sz*MCoV z(NFm3|1_-Tf8u?ZAFM400r}nCc^K5w4G`;W^{~f|R)NIi0MvfsWlW%cU({z$@#`~7 z_M-@ACJ-IPc)9^u6)G{I*w?{Df{YP$S?8f|$K)V2zc+TL5W!1HYi{7_l^vp4=8d|6 z_?UMof)<4;$^(yYBz^SPEx1 zZVa5#l!R>DKs5ZPVrIKU54L50im()xCIY+Cay){SUO3rFovv;;AYQh0uWCLD%Z7IbXJ$^50#nAqS^LfNFiG zL*-eDSwl;qKvh+D+P-AeOVW_93xxE%yKqh*^_u&T)rVsadkPem_bZ$%JeZ}m+eqrJ z@0+LG!SaVs4fu^+9`N={d+F^tdP9LvA=<2b-=2D9hKLEFiRNER$380Nh*nsPSlm$9 zZ7Xy`|L*Z#wG7%=Y*B);kksQZVep7&4d#?@;O9(W2$2kLD5AHy2HRI>x^J+#_l=*7 zu}~x}eY6Rx$}k8RFOZeo?gw4pJ1|xhS2t#V@&U2!V7C;TrNlH*j&)!cHfKKAtK^EZ zU%|@~urtzNXN=aIq%GnM%~Vail%B0#6wNYSs1Y29yv8vYraaUQ6Gk{EM8U-B^$A#h z%JA}o)u!TZXGNFucOTr7hF_Fxt%>!MiXEzT8)--`JUS$zzbM!vze}0z&i+Voz5Pbr zmFQp(WHlms?IHZoC*|;q_s~4}MrTCdh0szjb%9bV?@x^7&l1jCmd%0zy2P{k4T4qd z*M>bbRU$4ySz+i@T8oWm@l(%%cXtFw9IRQRcAH#0FvI$4+h6ZzMSDxH)v+rI;+TqB z+?X>hr=fAKjC!{OCHuyLEvzaLNDcp9H3GqSN z6o$iIk(`u&TslUtQT&m3a6u+(Va{E>T|+}nPxhDhgT>sjFguhH@pHKuF`+eZ|G4Cf z2xCS|!~F=6TU)jDc1xa!(OjYdq0J5|Gnpp z(oDWFu#ULH(CouGC2aXr&x1Eejk;4J#X;vcI=8*5vEA4!ev5X5z&Yn>`F?nF?PL^TR5n@T?Qp0x2%_g2X5HYRQ>@~wU4NNoYj#}z$|d`5brnMW5FqrwA{K3Ih@dNJ&ah)b|tG;K-- z@yvz(b6@7I33c%+=kf2UnsE<#qQlw`*2!nGaKZpdwOm|^XQJ*YE; zJn58^wMl`AsHG>4*B05sycJK>hM$dRVY|8Vxn$9jYKXc-!bPqpjMTMPiG005xEbxK zP`2{^M)DKYs`25SrY|`69?p_v8Dayjb1ZoizQHBY;}Tnlub$`mKQo*()i5njoF9H? zwa+tPi1&Sdh-%Zjhguu~TcspF48p}m8EyD#XfqOuQs`es8j>#cJ*x?y)RU^vj_4aU z-I>P9xzm&Hxm6Ufe%c+31}72X0^5+)*qb`)1kdxcHeH`1)5xL|*$rL3VkvybWLP*@ z`d!31Gh>kXRL+vdBo)Rx9u#D7#o~39Yq}H8Iu@Ch- z5wdyGkg1Z9gmxd=NqXrzf)iy@KI9Ktwn3&{g(8O{Rj;95LR6orjBvdj%}NrDy(lLI zB%Gl~DA6px2lI}dCt@Si%y{Pz+&afReWVYKjMZg?yP7*11^~yU*>o=5|9n4oWeYr& zvPzYp9Up|N+cJ6(<+vD6l=_sI^2lY}xfSVqmk$W@-tvV$QIKluNk)WRm3f7nfZhN% z#L<;+3mliFKhyDizF_0=QMdB>qvw)FQ|X`MXfvbUz+OvXgFtLrF1S%t60FQ>aS5@c zQA`G>#k>@oY$XD)cDp=4CD)KctuRNOpJ=`W=kZGK}%wrlf@n zFFG&cv=UToE39acuCQ`npz9j4Yc(T^?OSJDU9H$u?6>81=rG5WR|fTj?2;%E+LC(i zQU0ewXZ-;PCi7j@v#v*iZ_=d=Z~5L8KA-+78u(^H-q-J?BS>|2hqDli;wuSQ?uB!K zCNlSmAiEY%R5g8((g}hD-+PD`UKuTU=i3>iN_L|7PbrULTJdb^)VLEE=v=42_6aY& z`o!b$vSZTkqI?vllVVe;ffL<}9;86f`M4yXLkl3`Vij~w4;@NT&hNUtyVir6YcF&D z^7;DAZB9o^9W^<4bAfX%t+-(ha##X6p&t%syk{u%smT|)8K#lxqf29i^Uk8&uUf9P zQ8lg|6e4PJACXX@h6GuiBqHJvE}+HQ;6e_#vF*I=^`ZtdnGp}8Bwyg*=vl`J#{t}k zQnw{N3*$wHk-bRXH2yhCdyA+v-L8n2E2nG|@lkZM98k_kLsc^7f!iLO9Lk`+f$LQNr`g{BbuM znHMed*vl!MiIcq}?>Sp!CG*ZDOUZ(670oxh);B<_=;P|-V}vL_`4&AJW$iKP&2KG7 z6%#n}@PVFioOpLKtK4nlQ8AyTPk{n&8W-wismei2;3&PKa(U$7^65TUb8w+W*10)_ zFBD;+qMIq1?%VP9!{EQ%MgrT`dT65M@d%T_sCqbf-D`hJ<(@i#fM(l(TWSK z)?H{SMmXVNoz!c#j6QkHb4-)Uc75*B2bk*VIa7xgv{^Fa#SGnhY^G$_MiFq4;@iAw zb&H0`rr7F5Ep9r;d9bG!+CHMaRQzBl^7Zn0!rASK#>=+-f1?)Pe;&Ap&VD|(`99r0 z%e`tY&?$t!S?6G`W1<$zIfFBeU>FL?OOZd_k8oU;7(vO9oEAQz6iFu+8-)9dg-Uyi zKRwX)Y>CS9927*{rnbW8ij^k?n?aWv&72sSIe`%#zB49&Cc!Z#$nnzK4p9tO=B4DQ zqjHdnXARmEDH2Buaz{Owbg{jg6tnbIJxz0%rcv0{v=HnbI-uaoNM?=Io_+w)neq+2 z^N&| zw3j8x+#q(6V_uT5E%I5te!3ye0jd6xdH&QoBPCCzuQD&I5*=2K_ZF7Ese2ps@rZQp zPL{9ZT%?%@_-;1~4+*{os-wWFZpA#QgN?k}9!D}|dz;uQ$;MZle^sw-tNPH;3@&W<()zY@Zq9{Qn8U|Q>nXIaR(`d>m^fCPLS%GK zj#UoKk&aNp1`Dojq--y6QNBIiW@65p)XAf!Fnr{tLZE+R@#U|kNDE5$BpV49tA^*I zv<lQO{m07&Fk`7CbhxU$|f*=Mo3a!+aQ7dA%*u`o5^l_V# zWFjZSc%zp z5SFmaJzsk&t5@8@WPw}8&f;)#jPQl#J;tIYSDt$~Jz`h}w7+L5I_#z+@0~}18q=Sm z98>W(rHFM^Y5B&1x>b)bc}1g<*JZ|^TFMoq9B7W*{{kr-vIb0{|a@Yki--LQs$h@g&>!v`>jtAsb-TKY0`jm5n zL*}n4`r+;3%uOnn0M)${A(Un6&+P6R#RKGrM}_skx7>P@0M zm#@p-LjB>eRiW#3J6C2Ty(G7-AtRexYOyVGq7wl*ik-u>-LvX69`ucmUwGIyvr<%)XnbBy%9nkl zMp#O{n&7?Dm2t4=5^2jV7Z0t@eEPGI5aVM8twL&6oMJJ@ShdWNhbbj3n;gxE5fz#f ziI1RG#C3rx- zqLkPfqxt4}+{ZnqH7t%j@5(>^Av=G}jDOGKh?35WuF`Zf3vl=LbbWLXMYeUOshI$7 zXKGUL$(pid2uad^lAb$%#t6hrIhl%r4u0X!kH4zCFgS02Cb3Ry+LHU=@+nGDv1=7> zL@;SqWTvIa(|7yosOTNJHRJ5#+hY@^J6?#fLUyl#1aSC5buU0+&0UA`^_?*1i{aPE=!mZtBp>pAZVEG3x zzXgv+ldH+~vr7$q`=dshuXr^d4k}J+wL(BAYlyAbCX-n&l!|f`>WbmVVw=g~&qkga zmpU~)vmReN($NZpmJpV_I$K7zJOb7As}%#D8Ff)XBa92P(x%O_9NQ17+U}Y`aNwpB zgJc=6gS{C^hW2s)_!|G;fav{uxyygl>#!fJB@g*xHoC70+^LHPO4rP#eTlWIr|1R* znH)F!`f(Kcc5ew-+1&HcFsPH!zzl*csnwcjs}BqjyC-73Nz|&l(6=f_ME!oxvO9xL z`byLLb30_ifUj-?E6^p`$+5uJ)T(<_Rg`A!fuK8W(Y@|NKG-yq4|gXg9cEXzw+k&N zYVI`!d~wizMjuSXkPpFe1CnoIhOpEqfJ|LiRBE%q9JpX7mNdB_I4?-L&4 zlZXj3t~vA$V%Lj`-n9_Fs_NK)<3b&}!wCxQiN>-?cH)2!mKUrB`XmU;Xtcs@?p}D0 zxwQOEtt1jg-fBkYUg&9%0(TSAAtC7DC788iLysBF_!T$gcElnNc2o2N$3Vs3nHdym zGGREA(0+%ZHj5Kq4`={`aVdI67Q+td=SF!1_ciF=!Z!G>?ig6p{Rhh@{$rsqj(D0E zMK_D)NQ@#YtX4fLa_d$6l8~?8vKhK|p?&{>#L_YY_rfU1Ld0t$5S~kNW7Q1;S z_bKb${6tRS(KlqZiqkBnj)raP+!fP~E^L80=0~_4tt{dp`{tp0>Sd?!En|TMbjk#^ zYs<*sXPVw*oV@;HJE_|2R|_u!6C9pP+hw|GL26SszzwngvWgjq<=vLIJ6c#AI`|+) zj&d~HpE0^Kt0KTH(Fv-}PZ{8F`UL&E3^*oym**n_|^w*vF6dw z;w;$W)ncTDj#~=ty}kEg((L`ytOpD73|a0I^`=FHE$2olEOZu9rI1^vC;$v@lqp*e>9V1;$aXXL0)6}r=27qPZ{1#$SLeyH*@ zr7n?1M4dMe?XJ~3Shc(-xM^DuW-j?IZMmp5)HJ8Klgp8@XoT3iljYj)h~0(iQVK}& zpeebv9#TL%LXyWkbMp~6fB(kbmadi7?bqj5w6^K0CcRdl#*`zQy?I@OoeN0F$;&Au zaO-wXfsNBUv`VWWbb%)L{EPOuX6h%gr7iVj9*o;E=)bsf)tTZ%OkFIrZq_RZirB&x z-?i(hPJlv4!Q;@zUseJ!WXnx3J!7Aytcc|Xz=!Ea9MMw$0)p213bAEY&0Fkm8k_ft z-4+wsxP3V$=n3ZsW3d>^&8kL5g zxuCJQNGRRR1Iq)AOBu{~n)rEOZLhxM?>@5l^vfHG%eR(G$k(OaN{8Kb?ApMm|xG?iZO%c^LEo z`l75AWvZefCzX8vwm|%k)Uy6|!~f^VmR5#8)vzYa6F(B<8W=mk+&cIz3^Ky=+_-i3 ziDCIV;m1GequTm-XX4dgc)m5v>fXIFSUF2On`)uRcFWl!KWYBSxSQ60iWpgZH0{ z00gRb7I}PSv-1az*C`jn@d_ z!H+#F(iD?GyuzIx5V#u7kMm@r+`vz!#cFVsXqSDT3-JtDTWk<#f6e*q!CU-byF&KK z6Txa3|Hyjs*JF?7iJ_#?GKzc+{xGbCKEDqOIM`opQ40z!y0xZiIjS?_tWS!*qC5laE)ogyG>|1CEO1^I1P6X_ zRwSW|7O&HdBGEa}c>ibA|J;fH<8y+q2UgPwtwa7;%tdv(OZSx%lDh)b{D1XE6Fbm zSozRePhpsqw|D+leUbY06Ab40*ZnR&&aIJ+W@`Ox=9`!LBETXNPt(8>lGSdqzU`%K*S|8DG{0loh@e z=L7O~$5DXdo<2odKKwUm|Dp?oMCS=)&E{UZAYfJg&kVGv-ulHW_{9#KX;N=w@sqE{Lg-+|MYkN=MS{(G9AB@ z`{I=!R~y$+@A4O@s{VV=*{pf*Ua#11jF*AH7l=)$%o)Zs5RjP@coCh|B;bZ$FH8o& zPoPzwqU*o|I@~K3|D?Pn`;pPf1W7Mos@e!z?SzV#!HNC_DO8k7frJj)@Oli z?%)2Yr~GGM!v7~fkB{)6H#8UalEy6j$mPSv)Z9~U_?7fIb!QV^C$)B==ntUxpex(Z zoI1R5Bw(99=R{3rLhbKqfgk;w-p>Q;EtYF{2609c4-GR7KQCR#P12D_>SwN2$Y%Z| z!5I);2ZTx&FzUm`#9M@e$}T9ktsr_}uHwMP|?fd3FfTiW0w>xDEv}+FhS8&o)fI+E2as!U}L4H|Yd)XI-Sh1NzY>gqPr`&)X6^%sgIw9E*TVL0IgAAoU za8!N&Cm0)Fr#r25gd&(?5K#yRFz+U~mcQNh+D{5spwyg(14i15Tm`1oPV9g0;~L_o zjOl$g$*Ybn+F-l!{f)T3ShyYor=B+BN3mdyhE5S#{7!<#x-F!|h(dh467!FpTR~kP6u9ZMAW1vzPb-Ydf0%ws(&sp1}Y4Tp&9RBDyh+e)3o?oAoAKj z6A2zckfahQ8B!sLt%^!OG2O6)Y|A2)3RUE(jAD(n6fSpKJP#h-Wx z=>M!<9iX#PgPcw12GH#h!?+8G-8|2~7;-e|Lu%sdz?6N|7(ei{perk#|4G+#G(=1! zm6noNx|B6}@Pe|-2^3EV(J{>94s$=n@CDUNxG8Hj0kl0L@PR{`kVbhj-N!((_ayH` zKv{o^HxuY%BM*chd9PSF}gKO`u#FwS^!mlR|%kZVd-I{JoxlVi;80t#ru9)j#&6n5E=vHe%|EfS@u%@l(2QUUoHP1e}GGLKxB|c^ARezNOjH zx(UbZT)FCcYFUIUhReEb22Oj;l&m0c{nEk81N21ZEfkZ6AD-N9rJ0Ut9^wCfm;uX} z8k2saFvOBC2|Y*Wpa(w`<%zGQ+x-oS0?OeDyv~p+^9XE-gX7HNiC~r?%>+AyqFIEFYT!7LJ^6znF=3yg>i1wbT={t zZWBYCBIyv^_s_tLJ87&D1{0n^lWydg$=cS)o>l)gH_WPbBUjq{pY5ukRRlT}hRLM)&ij$A9Z-NoP9# z?P89jhwh`GUL-vrDR!PzOgQY$hKwSr_jiO~kx&mm2=$OT9^e+&`TaO5v?L`%@jY)deAeRp6z2W$)eG|P-rOVn{ zmaY+W3=x5Bkc!mMM<>M8x2fQ*NKmYCrz}7NZ6343eXaX!4NHGz$)6(BZU*`^H#oSR z+|W5sW7a1$8{?(IHC5c8jP{|J)Z*tday}9$HkpQtKdLjGX}h^^eN4sp98H=h_VbG1 z45aZq7!NUnTBxZxGv_QKNkD}uSz9P(8lA^cJ*lFd_or7fo0$WL&Xm&qTJ&Wo?j)mi;ArZT`KlXEQlbRx$n zA^}mqRF2_@3IdZ<@cLau5rDkdP^4cn;8U_JnN>7DZzN>M>6DUpeC{$T&F0t#565<# z3<{l-k@Xw&W1c{d{e(|xEsE0=+EfnkD9CRe%^1!2x#2!#v7S(ZTzbg5`~J#>6D>Q$ zchu+>FaBOnHp)YiCpt?Vn;qtqZ{=c@5aGj8P~%HxL(S%;2rnu1rGC^q{mF-1RuokS zH2N1|5x{(Yf{a??z@s{3r7?ZcEvAV4Oe3K?6X}Q;S)}Xe92{pLUsTP}MR*r}k?iclq-{j=w;Ee}{pR{hs_Qzki+7&rN*YCIi)qrg8 z5xUw%H1HM4gf7h2m^^2@ixb@Ka+&4YFF($t`PlCD@m43iRdp|``UwiwK3!Sdau1Kh z+EtRdI`D2?+-y9(r5HAK!x_o@Ox~XE%*LMUdX}I>@t&5;(9ElSN=MfokvW6L1S7)Q zz_XTcp|KWOe>Bx($>`RBkF`Oa(L=@NY|Oha_BS8i?}+&_bm}-?u)3CJGJ#`Thzzx% zEo5IH1>%2$IukbyKe%x4ikxYjd>lQz4eu{VF!fgv^WT;Ecs+2%eCGN6jDsp1^w@V` z`@UDn*--*>6|yzIgMZc3IBm4pr7+*a@VtqdDJjPv*Y3nJE|DBYFvdW(A^0ug$Rx^z zBP58Wn(!5gbr+Gnj6vQFANuj7N=HaQV^&;4$mgN?Gts1u9-p-3nuhKKo}h)Gb0lQN z_aO7&799+eqNQi*+9E2@%k7MU-dS#cig1@#f~T-EeLs}OtWAg~cg-Ptx9QR7CX!12 za(V1r@^Xn?+?d3pLGKX5kD_}00wDrdhF^kvE9Wb1@cOZB0we^+DWorMJLtS~(x%mu zApiO%|B5aai~h4o=Dy1+UPpRAVUzUkPFR%i9PhxcsQtc%6~)NJGri zQJD{@B#F{1o{p@?#O+l_cAUA)BCBMn5tjJ%v-0ll zXxLG}ZetpeLd~X$1Kp{9uK|6bc-0DuCHmTZ*Hcs8HxpxCKWh%rh%IilQ=MUSIIiRU z{umvhFaigS2~qr8?wT8^7-A2JYpy!B*&n%9) zMdH)CIOfP$`RzD-Gcl4hfR0n|3b+$iWH+;*^9aGvJHS>xmv_6+(a*o|Gg`OhB3k*R zJyZr7)Qza!p4{nyaJYflUR3gJ1zl|Z+*Cs-EGp?7Idf}7Zr+(CjmHKp)|~6_(Ds#q5&rPe?uMd-ojVaV41U=fr=&B;)&krl`=D<{g@6 ze}mq}xFCtQfrxB;7!(-x?_166bO@DbVkiOGF@o8?rB^laW5v2v+9V<-PV<-C<80;A z8k{PuyVuxH9&DbcU-SHf^)m9^&)YbDr-12GP?l zZTtqEZjtzvJXfED6_zx0D0eY=Z~Q0_%pr84W9H_$(+TeowWmQGj(;M6|4VTF|BDRv z4)RjQ<(c}sUV z{7)nfgR_B++ZcSLXv_fUf!IO;TfDRf!~(cE06qZeTJjCNG1Ujag7-IkpsxRSR`ZWs z(*MS>f7Ys*9>`D=kh3%*-#XI1EKdt?3jhQ`S5|xuwn4oA!HV;b z9DlMk_cI9-cZ%F?MI8fH#HaFJ-Wm~1XYLg{+V9hEZ9vZjef=d_Nigo2Lv9=6UOK%x z_&l5zR#tG8%pO@31#FD04f=97X*?ZsBwkJVMH|{Tp9 zx9nE<1i@RDvm*TV$@6^kxl|&Nm`bp+y75VyxkO*^{QVpE!XDgf2R#FAXz~0ThWE#- z`2Y%Uj5-nvSf<9ciT(zS)j%!(_Lvsf`!D|lP4|MsNw1gdLL@%o8 zP--OGDbsK{QTt{GTZb9Yfb^Kq7Yc~ei?B^mSg_~(#wglO*TTL<)n5(AuzK({3+JPW zWyWD(7K$|9zFp0@3&PN+7$6s2UEVZuHdRY9eck=5*2svs{e1K;^dz9;#L?pJj~}RY zQ(qa^yx8?IOOA-)R>d}tUs<b2I#W+IJZbHn~zBbyf3VHC=;1r_v!gZ;@mu(U2;&9l=!J0~n%_`Nq&(#%GRW;B|N z=X5N^oBX}RXuCfg(iaWlED|}QDORLy+~P~3H6GH9JOlH)k1tDfvtw(`CO9;BmFOr& zDRtM6S{jK*F0e)dk;NIgg%uFT4=`u5N?VT9?{-Z)NL$eqtup^FIJmv<^QIC_fouS zuJ&Pm((D6$0jW19j6eaxe0u;p5E*NyNGR_i5x||5oTTexERwDDnRne(RJ1r5j;T8N z#rrafsnF*PFoFnn!qnn|M#4xqMF6)GAtH_!gMD?8-M1coAbZ=Kr$^}K<8;w%1KAY^ zXo~F};}3wiNL?=KEbSsJ1{#V!+&1!NFY-AvB?1}EY4Az!(&4dn5zT*cCO5I0^U_t# z!Zr+HdYc!})QmjQ3@{o^xnjd8rZeGXGUkl;qHfFY^MX&KmL0&eKnQtjwZFk#CZ~y$}(&B^k1j zxFA=Dk#=ZIUG1c%qF*s(s9Go+wtI+kA3MQBtW1Erk&UfLNNnYgLxO2%hGauvzMBbb zX>i1_=H#JT>3n%znSxBv7 zO7-w3wx_D_eC-kSi@fC&E|Bo6Q5bY}0CY@?asIyfV2}#6_^HNag1#9^9#eUIE9ep- zKFh@=P&hjyF>w=WT=iCNRbt5SMFzI7(y%UKp&&R$iGu;Af}yqIvOK%$ zeWx=hYPaU!j)hGGJ$+^%QT-tm{Xs@7!Am@66T~=GYfgvn!$QJ>EAAHs1qSNEzJU~ISkIZD zJCYZ>CBe#|%Wi`Q_4hN5zcugog`9yq5O_L+#NCkLB4T*oWSHvk(PiRaO2B^|g$Hg!8AuT{6WKzEb>zWDv-omu0)20$E9 z*#6u|7nJ9AQBQzvQ!4FiqxY*4Ydy6K8w&BQh~}DYcX6|ErER%!>rs!q)=#J?sSwyZ z#pgYu3uXDwv>C;M6`lV?;pM+eft=Nt8}RCFECE6~pkn?@W*UHT{oKp<~T}|J|$5S8bB|V1>}# zPnBH>b1Xy{=^7x~sDp{0)jEp81Z zW^Lq{8k>6WdexT>(J%?93_P@PM+;oCZ;k^eK-^Jtdzd+xC$=b>!d2kXMn|w`daC=$WBS0J0k}m`rxdt(Q1)m#|2m5B@mao`5{VI^E zv{RdIoW@Q4l}d3w+?-n8b`@5Nt1lu$XhYc|)e}C#pj+~lo}ZRmLY?1L9EruNVtsc9 z0(+Lj=;PYKMYZPZ4L_R6R;2kp5jBE*hkSySq98|H%at?JY_gwb8Ir71xJxUJKt8O( zmQ4D$lVTlOAO_S5h#O)~yhr4$=WIl$l{0=MVukr!+wA(SGV7r|5&vAR-7ugLoDv#t z-ysUlMG3U2!_`SlIWwdftkKGpuT|X0>?7xg{sU?D1|c7r=v}kjCKR*oj9ZVD)n#3a znOp7>?!FO{-*+*>M0@3#J|F3jjM#*5)gfl>G1)}~*UouEg8SDBJKEGYE>^jRNY`LH z<4niJndd4_XCAjobtCHf5~QYEn8??HzNonuqOhOKWN?!nKSx(7|3Cak3LB5hjK6{IQ4AE`+)% zxMZZZ<&NJD-BziQnPQIp)RVHbNJs*@XM1$vKpBY5kAuoJ5(OdM0n;G*6r}~ozP_de z03WF$NYpAw$3uRu?IpPTNG=ObN5L|9c6l}JT<$SXGPw?a*tCUP^-7B;r?3*xp0 z5=ja}AWMyU=KghYQ7))1wN$3gm>YISV4sA>|E$TGUv^blP58y0=i}z(FPzR88(A*7 znEm{&-v^FQA1mW2N`xYuU!0?%JO>Q$9Qhp4l>v?P2%7hLmWikx9@2L$iC(^XQ_nW? zq@RPeD6^dewRq=AOs5%*v!<%1nZO^uO)->qMY+B?k8L`7RHyC2)0p&LRY7_3ZetfW z&{v(O3opX##4U3LiNf-_+Q0zpDXTbB2W6jug%EA63{rl)KIGAs{S>cs!r;p3-GP}) z0-2=yzl^(=MM!yC1jeuyCbYGXVI?Q5yi(+<50iwUp_|*u$@Qdet^OakBR__E7(V$# zpU7dK+opp^Sms=>8&hOc>GI`_nv%DW`yPXOhOfU;%rKc}%cBmuEypUZ~r1{9^W@o#$NGp&tI(1WBFQ>ta1H?#ia#Hin)Ld5d3cekQso7W`a+pOFC+XsZjqhFbWFlTE9%q@|^V9o5@7JeKw68@k7X4s|1 zo`s+$l#o!Coru8K=U@SRe5ec- zYBF)Idc3^sz{qiTm&wz!!oDFYs?q4Hu;v1LUuT$pA4SopwBw>dKNfJ?1CRPFCq5g^n?0RpUIc6B3x^2t6AjwVN zz^AN7CiZ_}8Fp9bu2NPz%P0{KJc7~EYhk25oG0VvbSm-&ajJ9;o_3M52$5A z_N~Mr%c{ru>SIe54<0jf-hCe&=&lRL;hXK!bjN~Z-Nxr_H}eb#%2jG}b7!+R9YU7) zIp_P5n)G@nZpT*9w?2p9rvWbD=ou2gq9EHZQLWaYcRrjZ26krJ@{Q8Y&Q^J$7U-siyc zxUTHDu}J2}4x)8r&byd7pgqy99yUt=QZ-^WISsNHz^9%wj7dD{3ZI9bBz6*;bO~#{_rpu9i4oW7Eif zFMsBKuto5~nS|2_(uJZ(N0PW3QC4o2COcd#Bw0t+C>-x+xb)eg%R{C;^2W{#F+1yKb1wWI zwF=Gy7=9sQG*!O`XlD6gjAU%T?_DnyTfC!_wj`RBn0s8{wBC>(FWoPGUAPKfhZq4+ z1>|Rix)EoTzW<1btj)aU3Nt%xDI2qaZ|-SiNfp1S0`JnCT!pZqTmZpRBoE{XhalW; z?8TzOdXJUt!S?6#f^#e{S*&kXocnxojC;3NIJyFp9M67I6kH@4`fV*0#e+g;2_m

(>YwaaK%|%)sik+x(gObP`nHwJjmVOG{PL3!99!e@nmejCkLky zWv0?_J~6<_eSijc@C17Xyzo1hKX9ws5~A^K{{Y1w1~MVsJ$~F~1K@SFiBUN5QPXFP zc*kRt4E}gPL2W7mcz0G@sH|O>9llcYHzon;*%q~J67H2?-PZlQw_fl^sW8vU?fMXG z^T8Kx?UFPaF_l@2Yv1D_$;a)*dOI%gg{&!gRaBk~?oR0U*1^1^jd&hIu>FW>8U`D) zN?yEg!Dt&9p_)+DfI9J!ny+Q6-ew*|?+f5xX6{-xpyzVgt`6xin!0QAkONo_%j~}; zkt<;Hy`inOrjk1COcwjIqc)2(05dS>3o=GEIN9#er)tb9Ishd>+P-3rnz@o2$3eU| z3&SDj(GO$7sW;KXNy=_6Y6vb;5NN`%Ns5xw-%wcD8YXrC04>Mg065m1(-f@X&_19h zt(?vQnVZHva0AOVo_}sIqoXT{Qb|o$rzdTaG(mJ-n>DlY7#wJHuHM&KCi1PQN`hp` z0}I`^L^qJ{hJ!y|4V4s6$Tu$`OCa+-t2f#cl^#{DFF?^(1pqKG>_){zzhfcU^L5{E zFC>eC4xTqXY!|=Fqu9SEVl%4pNOR}&J)=@88~SM6h3*V6O%utiAW<2V<+29Vi{+6> z^?)sq{Kzx42`mFYN>p#0Sw!*sm!r0YDrm4bsNFu6xk_STyrkW169eE`6YP575hYUE zs!vJ*)1NPsy}TBdJ`5Fr`0V69#CiR+$#8m;r4z8jjksW+ry6ZBIH2bfEH~A~TO>O? zv$ud?eSk0vJva(YxZ^eTBBNYFOTK$ibdsUj%LZ;F#+=qp?D8!vcFkp)hnbuY%!gA5 zp*X5064z_2ZRO0t(*yD+XCa@L$;lGtRFmKaK`z?E_b9qM8G^uyz-O+@!YNoIP*dAe zZz`xCBJteOfuW3wAln-M0DLTkg3E3;RH1K8>f?am)yiy4tSKh0XUfRIpkFayyQXTG zX9e;Fm-qd+&Fa;>Ybj$t1QrQ{TP2Uq&!+%855Hh~kZUrZRn=C}0j(W^{q(I9MS`FJ z7ul@L4<|T!5l<(e>N%|?J*$vWXO891Feb9YM>(NpbMJMqvJVrwGICO`p8%wxO)+b8o}i-?2Y=Bu2X-q$;CM_K-O_6kgjc(0P@IK zUi%7r#8X~bnxz+h;(XyI%f@GQ<~ zpJQKcF|Qd+mSB{a-#IyZRgH978rcBPS=rA4GH=Kd0NR{B(-G?3ZDw*YdNd zC-e2CQuA2QZG-@8EoYWI`T`Ew_%YCvCJd&iRCBYJx&XmD(d(}}1A~tERQ%X%ztG6K zqgsb4=DL6i^AzpNSCf@jV)^@)>RDp1yta=20GS%b#%I#0Q9IwN2GY>M4Q^~R*L((T zI+9~V*6!)acJgh|1PnL5dB6@X_E)ocRd}n989Q=_0oWV4fN)>Q4P@xO7q7J^f^NpR ze%jUPBal@_ljYwwXHH7iGnFxRKg&@2O8%dg_g>Yl`?M83+zAuFXPD+w5=aX(e2GTeocaBtzBk)^EkW*IWwOedgrOI zI3ZiRb|hzCGbPv!07%0QD3LqdJN%^L$lUiVGuClb*!tIc59KEovKnNOOrCPbdK|3& z=~I8&5Jt@Y&P&u&x9mXWZTCu?{J-i&gy8H*4~!tAXI-2Wt+E0W!094thGn_1G|({K z6gva48T0dYXetY?^Yar7Q5IyIMO z_ZETm}uZW;C|gDeOnBe856UdB?!>UzZfd zO3G9p_>vyjN9dE8l(`ObsWi8Q+6_m-}Z16IrNut={(UJ_(p#!IC`m z{;Hb1y*=o_I}ufy0hUKZPl_*W!~p88YNzrE!_{PNpDc@pwr0I4MM*&Ou)P%PgjD+Ul=8P-_+`0|rC^O>nw>SFxc@!sU)Xf#4k zvvr#1>Llx*sAn0w(;2)6Ri?6*lye#{C2Y{tM`_fe{F8=9{`@2KJm%;JPr?mM%efz( z6b4PAQ(cLxFV6&f>1QAVob*K=D6LzoT|Sia!7cXy;;>q4G5Q9GhkW9_@~)k?G~GY%Ah{Jv}&pF$F zgA~SLNW?6(bH?sUG0J_b0FPS%F*d^fQA1z=7oN;}z zlh|C=hnC1DHIGb+qHB0L-cPAByv#M^(c6zLwrz~*Bm-LKXQpu>_QDI`$MeA* zc9HiQS^cor-;0LZY}pM@UP<3q6k5_2=mgOPoXZaU>nsKt1Cq|%673?J*(F-SnhFDI zjMg_*Jgups?!Yua&s~`gI-p=fn5c~Q`V|ce&VFF;@h2E4{M`QlBSJlUTfsrvyQO<|o0I-Kw3_IeGY*1iY%_FCaid8+=vLi_l73X4A_x~3ns z8P3hoOH;+v zlWxur>6&#vp%@n1_DZD^6324sskvyo^cFB!Glh$y7Iyr!dd@L_3M>9B%8fhzav%L` zIAL#+dWZ)eJaAY$2_*7VPSkxaSQ<5`Ko&fP0n{E$kv2~KaG|S(6I}Dmp4fN)09B24 zPKY^l^)&!2I>7@l{^Z~l{Hck^c7~uJ2>qCKUz2JWus2!0;dQRFthk>Q&&x z=}bItft%2<&w$oA&EL&Xc}mmyEcr{L*Uw~VY#V>~FuVMX*Z%-iah6Y{E6F4@;)eS0 zbOz`<5Nr?wss8jG&EHM1{Y!Y-bH2Lbw5e z*=D!^s;bw`s1&HV;%3<;_f?xX0K5+eg3f&LbxO`1{>Rk+0Ep(O_`$60b-)b!9k`_9 z9-sJ?1y*SO0ramz(NcNiO{7fFZBsuILRc+%gOc8{UWpOY#7B{QKaV zr2Xwn6YKzgzX&CnBb~+RlPvaBPMQFp2tz}y%C_xQ{r zrL>-E+HUr^pSo;{wisAvkOPkY05UZ>s4fpo&_A6yJNtkKlhd*@^l9m=$Qn)*nJg0r zsDjt6oI31)02~Kk98I}8fT=LR_954jr5;KF^6R<1baBHO$?Ml;oDh3Q@6%9|uA1(A zSuO?8a)<`7JrE^QN~bbIXoXKQ17v560MINjKHp$F9PmF`?Fo7Ts!PeRSQ&H8ji5i1gO^QK$$j=6mza5x zcPc%X$tnlFJVApAnhRdd!?3V0I~)PW8$A!xbc~c!=^i>i_vaP0p2sOF+vRQ(Hg7=h z2MGO0B&KWp)MRF>6a?C$5nE|g7Qq`gG zNeur0t}_1s>Vg8LNU8q-SS43eA?^$QVHi5uJ>cfE*XvUSAWg7dd2$Ae+%@kD0?Q6A z+do{{2VUmz4B^0Wq15y+SDSd8Q|rDqX?V031wb#(0305CRMopEsp!V4uaV@x@U8xb zz7XtYGI)Wk%-ApPc7e1IQZE$TvC~q^^aYVFTFu(lJH)}~&tycEHs2gKVupYNGd@6q zQ^N;X38r9chCm3M!!ZqruMM=*^0SnCQ`jQyvY@6t;J!@x{dg;ut<@^g+fYP6!ob%Q zEtoC|0t=vEs%MhJ(HCuz!3;1@L}jMeXYH@1T4>XRGx4oH4;#Gz05d!s;mCY?;_n$WQb~00Ma7xyiLB=hAE0bzEkR&DaJ&z{ z1;CQ~En;Ditd00Bb7%f+<}QDtt^JW?bw1Q7C(K&EX5j+{O9QX~&(~R>&yA{UE|*Tw zCX<$lIR!cRJCWr5x5!x0DN3D5rbb}+&hY#Rg_c-;d`FddNLG^82}#ZHJjehFxJLDZ z&*Ot9T8`k*zX>6?+=ATmkuE^DS=5DvH__s$a*G;XTRGokW%a;(0e?C^{1t^&F_|)M z`eiD3YOanxDoBDMpRoZ|ncm&G;h0{D9^h+lbtXFU8KoJ)T-bmgmwHYM%MGpzxJdz&`nVFa! zhK5X+@_k5sX=oe_4SYMq@uXYcqB9BG6mXM#n~+rU1`bO+{{Vh1QGU@BvGO(>0?W?( z0yFm0FO*8Aruf0bP&?7Vzy5QC4;{>^RGLj+ZfPKQqH7{x3m&l1q7Hfh8bccD`v>VG zhc*LH>g-m}BgT0$L|(0KuoTyvIs1`W<1O(7orU+9UT0??95Q6_M$gh{r(MC2HQsQb z;09}T1H}&m8S=$jSc1+B52>&$(GrnK1WU+XXQC@*u6onQ&S9?20oaRV3*Z4Ix-VCv z6-x~h#23(PGQQA@u41MiPeNU_PegYd$=IfNuik7@#SZmPm)wrYo#a*>qs;;*7%5n} zh$G~2MDNiOIKR~tsQFPtd|f-S{@!lPMn+nnlwUf>7fhJ+OxFPDFbgxm8ZxFm3Z&N# z{xez_$2c8g0EqynLMh&7lp?cp zGz*tW4h9SA35qrVM|Rv$yKx<}z%tXud~rfn=JZM#5rbShH3WVIirj?abV=r-i>|>@_KP1^!|>>9NrEHn^RI@J=_2x zM`@~(w|pNDM04prj6d6g8QW{yfrXdA>!kVu~9J#fXG<6qP)c>?&mMAF~2S#qU^CCSO&2?G3Q=vfgA z^9CkE13Tg7Z9JOmotQmIpColf6EOtT;LseAa0Urk&MChu`_*E|u8F0Hb|uDrxT-M5 z-SIE87&zdS{unnj*Rvts5nAOG?qqr|avs6Q8`~7wiUj!JhaaEI6+jd_F95T^yDPgNT48=j*+H}JIjQ-mga-E|BlxLskEf&W#D11;dOyx?Jx{3v^JgxZx-dDL zo`3)Y_WKc1@B>o4D6Z!Zl9tQWe5R1>Ulb_UlIpkDWcltPtb4P| z49{8FWuBy3V=4EemMWimA>NPDtF5Riqpd!mCvRJ^V>xHz(=%PL8)E}Lehm?hY#i*_ z6-Lq4M1lOrKOvZ326Ly48*P4)eED#~URw32V;)X>BQKIu)r}0oWh39ISM486mwMP7 zOsudECnqJG@cEii_BQ^cnCaWe-R)W}9bF;-PPVqoE^*kQn7 z&K-!|s|gK8=feRhYpJZY8R#}Z_vY-+b;&P213UeA(kiWwx~v=|g=+bQ>9#Z&IAUgr zneu16I3q>&o{<1M^sIGlUD%njysa&o(=_zbRa3~{Kf-G89XXdcJI%Lq@Yy?%O|?p@ zgW4)muMgAYzJ%ASCp zQSZTw@?5hsJsfDCaQNau*~xrNOLY+ESCtRBr>)9B9Sj)4RYV#RCC z8#z5a5O*r98Y_FFK4#Pg! zMAfFPCPbIU1OV&{4Z}o17gzSr>7&_J#@{kAR7blx6a)<&?ShhCk5#jyOV;aZE9Do)EypgXz+I3or ztfjLWahqwLHZh{n{rB0d3@ki{8M}m+LA)WYWH>=n&RVjaziA|-rBno|_Wllo#cif7 z+XG+#{x$yQ{{T)ev83}=SuvQ^PF^a1GnO$8*aFVLXUX4yNo{zV)#1%wQnOHK)>VMc zTy3ld#%I?baO{GFYw(#=ZYp&o)+;2H7yuiTQR)oB{yxNJ9gguARiJ6cYfP7$_V(EZ zdrg=%KpcP|i(HNtHIG1Xo_ILe47E>-?&eK?cGdncavngu4l}K@RP1X#qfF`sH-bx| zhC}Qi0UHN{#y`b<(KHdobmemk&dh5pzBNzK+qZ^m6u=AjGa4-cX{vK1l1+*S@}s?O zzy`4I#jLgBo2`bKo^t@RzXOjAcF)1~8KT}|b}RwG77dKeB&A4Medmqbz7-Q7<69N6 zOyW&G;aQu@02AZjJ$J`fD`dwovydV?^^k1e$^p2B@0iAT90!p20z3c;A*M?1Pt8iP zL$O6H4nT_5XYz-=XYrGZ1Weqx2CP8bs&K{>^wnn*JaoYv2j&JGdIBTdfOA)~YLg&6 zAVO8K-u|5YzuySBcx*$fT|hJR;0fgpAhB>(mU^TD&b&;Z+l3c->yNgf*vL^JM zfr2^(tc1uO_MllLws2r;%B4nNW_B6HkSR1FMRnFO$C<&3-jxX zSqo~8zYIxr@_8Zu03I;wmi$jAeipqY!!7Gs8JN+4Fa?=Z^pHzr$vbsWBfXi2Q-fx| z(19m170rX`;3tDGYm+}2@Bnt>cbrplIZ~-fAdI5R)@XFsOpe1*Jq_@2JDZ3fjT9f8q7$<>)FEi>uHN5lq1GyR< zVz?}X5daRLI6VCDe$A6c44ccCpa{2W0D>SK01zEUER@%2HB|9U z66+=C`v>cyew-A)={5=s~wACQd(|ka4H$H(Q zGX#PG{p-+-X0Hiw4QCO~Vu1alx&Y?8MW-2&2K;XoxX|rhRBE7I))o@jZC+teBeJvsq}#4q9l>w?ItH!R4Ae zap<#^RA3>7yzOqr?1*0*o0H%z2Nm-BEH_KaOOA(|Yi z@&F3tvds72qBn)9S>cF$u4|(FhKL3HkzS6>Uv5V{pU)hFv=PR^4!X+&Mf`BGHRKrb zsUvP48Y#63kg6toG*O!-l4o-u3`5R%KA1&=ZJx22R<0$pC(c$TveXv^Kt?Z@wvr)PjMX!$s!tUl zis5?CI+cI`HN|bVPq+@c;;u}_*lgJb@mM_oAG_**`&F;!!aeY8&199a4hZnp%@}Vx zOQpkI^MbkDmaz`sVJug@K~e+(6u_S@fX0-(=rKf>i_ z{kHepI1G=a!TlT;%qD!}N*)l%TyRnzp9Sk*0&}xwJ7wzc`x@8SS|1C*kN7d$NA_!9 zVazDY)AXknt&h>$LjE$9f27-gj1atKF8*BG{9uOu$8?wS7Qf>heEp3nFXX8I0E~ZL z5AIQ#POiwjXkn4kKAbT6I{{XSQF^XVv+?gyeGllUQT8aFHN9_Lq66C@=S#}Po zxIHki`hNUZ_>z8)%_;*sI2_fokEwOxRn>!E-???{wiRQZxtxdz}`|yl2SrtK9 z$j;8O%02XASWRWG5;fSn1+Y0Ivs@Y`TS;9@QB7q)@c;o0W_UxS0W7GT@O ztSn5x4$oZTsm@J&5HmiZ=8u*-U;v}7pC=r3vjr@d@^$*wNugXxHM zQ=}khx;Nj*g7EL?2#k69$gnh2X2B*p6+XRg#~?&dMmd^iy-n?aEiO2HKYkVuOi07& z`|z}9_%ub?s&PV7P`+rE9|k%4@g4YHf?t$`SUC&dxKI?l009v5HTv-h=ap`seEm<; ziHZ%+aw?GRUue{;C0fWUiNiMLiGiXHOwqE=L&*=8?P_=u5R=Vi($|f`NXkkVqhX@e z74d@3+y*V3xu?&PIXS~S;yM6>C92D68Umr3QvNY$w2>tz6SbyB0eAun8YUKZGw?^N zJPhTm-fWb`oY9)yJrjpnF3-UqpU8rUy=b0(KV1I+LOl_&YuVA@X{diZT>W@Sd5R7( zfs5M*;hhTv)F*{`9v~uG3V~ z6TSPsum1pSEdKykoq_ zc3#27-9barRX^RC6JtNa09d!C5S~rJhaUAsXrZ$*Gd_ch0+MYZ0A6sUx~4Y3cpoQs z^xq}0^}(E~ys;{MqVlX@KsuF@Tb&*-iW&C7jTdYQk~j;FV7phmmR?M+IQf8iP98W8*_(<6fzHpLGCn zp1|F2ztjMMq}#}G=)DQ7JbPA(@v@uZN%Mrn{R=+4L}nN*WQZuA(6#juS_e_uD(@Q0 z9j_|9-7|Y;3f%S?>+V4WwxOpdb4el%9|y@n05DiA$%1_F5wi0jt;`8m))E^Rm?#fY)N&aN5fw&jU#BQL}F~zST=8i-Sz>StZe4`}z688$fu(1yfHM z9bje$lg|!+G9y&(ep=rU!pqG7$Wutf|vgz`zl8e`6p;Jcm4umM8LH_9CWnfX--m2moI#T+YZ>nCF6G z^lditMHEuNjWz~-IYDPef8(58N$1kp!Z*vEg58ckLI&w9^vH4b&c8l!oVjz1s5=D? z@X)Jsv0(EL+&Cqcb23mjv>Fw?-+Wihq>`zxSnUfH;8iS#8C0uiwH&EYK}Eu90rX0R zi+;YG8ARY%)Kgn- z%H!}bU)V-8TIA4LkaoL8!DO#M?*qT-f-~7_z#b4vhD#1wdi#O(B5dY>nN8#TR9$6F zlm7s$Ov1$f0L*}YnT-}@h7DW-;lZBpfQdL+;TZQuc@GDf8f@tV)nKnd@&Rf45Gir_CU>Dz>4jyc0a3h@f;LO(EVLiY*3>$m# zu}&K=;tt+h{aB;+qw5;-D&N8668Ys5A04tV5-%&F#1M(-2V0xax#uG_bGg?X3B4@m^Q_D9q$bY97Zy(B@O&!nWM`I^QnWOy* z_vgt4O||M%qlk+yh@FI3w6|1#U>JyN00T4i&Lx?zkPu%OKc)8|aO{?YCD2{_o9sO2 z)Zl18*Pbty2WI`c>8=QaP&3b<@@R}yhV<*veU{xcWVKrU3C)&3!Chcz9)*KJ#VWyq zZYI0Z#;YfYAZZ!o05!t!_5DMRvni4YvIo=5W)8t)G;mKm52B3m%QdDr0fjNhiYKhk zoB|8C(@%QGc(BTT46+$iRmDQ&7(F@5LqysyW_~bS10nvphFbaNfrtLVDt!6L=dp`^i%-%9%$zvkI z2IX~8=1Ux%t{V8rP9Gqp?Z7fDw;*dck6TV38cpd}a__RB z@eZ^DBEi66$*dr*PfRpmV4nOAY-VU+hJXVLAXjA3hPU!sn>UC1vn@UfRFEtrlf#>KOAfclhxEDpdxeAL<^_kLYf9-Q_5hKsfOd28b|QMXXo(%x00dIW-X`UPq!_ygo9fq_ zfcg;EubwQ{Ji~T;;du5SH-RvM@w^$Du;@h#vGBteo;zZ6edvITgw?m;3&b*aB4i9| z@&zfHod~%2IAEEf*bpWiIqv>wylmxgYL{3U!U<$E1(m+dY8%ba{m6(qZy$pA>HuVW zu0C@M^MplicchYm#t|Ssc;6F^75&KIOXN9-W(%uicQgSD%)~g(Ahiq26Si>%s#$bW zZk{GX4EA%tWRiU9TK4>Ys1Gv)GMRnl+09%ByQaXDYFAKfol%WGBYj+(j^kC z()oyBhsms1?9O>1(;OGeXc})CwU)4HDrA!uiI^mgfHS|_iEyDs>g1tA;YTi9{tUki z3D#5AEn9B30?Qu{ELct%&)m%0n-{=~M04wm#;kI&&KJhA>+FFho&cNJ;e_cM{i+hq zjL7@(@i#^Vd_+XCv!1B!(b4ciF1y#zbV0Deb~pFp^_STYrcV&u6~j{VJenhkoq>tT z6|;>eXE;WJG;pfb zVc)xeYcTd8ar@2MTB_k)YfEpq7;=N;Z3hs`q-W_%{7(|5w1VN^Avsv+}qJHyzEP)9$PKQQ99R2EETz3}!T)D}YHPbNz*j;W%1$}?OM z#>!g&RHui)%Oux83H*?pDrwA7SdB2S5->Oxtj!aCth!#wvo@&YJQP3; zV}bAhMJ(vmSy^BR@vlEooGE$$uUE-v33W#&MVd|BzOlub@*mWU-lKl( z3STOhE@-%zt!5}|MF0!T{m(u*d|r{?ou)DKtg)H%2OHo5*iK9=4u0ek(-iV-5(%vFaB}Zh zC5?C^liP+pn|eCSF6^QSU7%BPBAwIH-K7IWXR@prQU{2LE%TL++z6XbY-46i%{Nn| zku*GC z9M97~uN^~UTP_d%ikABS0A2}#PM7kxpY10O@j^e!UVn^mMrZY;I072|hPGX8pAqF> z;KaFB&Zw$+(LRyWGS+S^vlS$B#m9+|{#Nt-q~V7oZhyw#f3_TZ)c&>D0`fgn!3Ulk z&E-yz#%^pghyMVJ&JT8%Kw!w7y_HWV2+Pp3d17x3WN{uD*Ap>$jYg9BtXZ#Qo>PHVr( z-l72f%@9P<(PHKS-`5s+$g^PHk3sTw4>ps`(pw+>lYe4adC>m=7lFO*y4QHd=KOQu(Gh3Vl;vt5pSzcw$`_kr8sEY+Ev8qF`aq9D*f3W}`Ff8aYKH4yP0> zh=HJEYTZD$ub;W;`f0pz^!%)RF^)D^xUTCQ?qqX4SIh+1aqhZ88j(g_q=5FAjKx;PA}^Xcw!eCb7iD6GM3X zHTQse@LRP+T~b>-lgwHe8@hRBri+X0JM7sdi)qjG7-Y?sraFbu*84pCRFB&px+ zHfy@nY}ie_NY&B1-LFKz4EQ|w$tJC*J3_3LkJ)JS9=Kzec&>*_a-L~FM zZ$DUQKTbW?2;argM-KCc%j&$9whA&;MAqjclP^&z!^9jMKs&<(omr1RR1#Eke!Vwh83CJqXw~P}xt|s!uI^)Y~MwH@0wm zXL*=IDQ_E=_R=eSU~cN*t68kINAiVJQe|RTIOYZrJ5A2lVD;Yifvn+h0N0|-k#O>4 z>U>@Mt})cs zvs6#ay>D#&NCKgs{B7`raX3>tNSmdDxQJkfxfM%QtF2bJTog68pfd*x1_)jU$r+_R zHJZq3gLImfJ3#PF8|=pHL-^4B62)g^+yS{`Q~H;?JZi`n4dHZC8~D!1#dc|3romc) zuHxn6wdPlz4VH2H_rT*f?Pm@AV%2l&`0vgK_2QuyE~$y}&U~{+Sr!g|)8mD$!rEfA zPt5_r(BQTAHHA#`A{(;+>~+T#OE%xj5^DL&!~T@=dJ)Nk_On)?>5Aa8M+|d2l`vNU zcgW)Y1P=4`0u@vahk|Y|Gf>WitPw>7Lk%VpT`pyKXYVjA8py-{d0x5Pab^k zat(u#qCWY=w7#|HavsHMvcQPmy5IrMXJk@5t*OQz=MXdgi#$9jihldupl=oDaIu_N zBEnqH=CbC<_YsL~q8-N{a+@}wCKKubgY{oAraIFzGcary-xnXUb6=p5zdteyZ!pjY z2+ehajQw~ruQvYxF-k?JV{vmI?m_3ZM`j&GD)Z1ksTEUQr%&9qaKtGkfEPs3yuoaj zhjI*`%%#jT&1S8eemP(d(}8)G(5Z%fVh+}M`8q!N*%z}M2>h8r$dp1JauC1lgmByDUY$TDh_wph#% zXoiH63jiP)_o&dB5RbUiJAa0sxI9kF+6x!icZ0%oyzP}$RZVx9gC()iXw%4n@Lp%G zJqn3l(L(-ch7!Z+97PJ=^iFP?Nwpv~ct38)$4Z|37L$G<(xsO5w$M6#S|6oM#IE0OpHRtluK4sf3zOU>S$a zyKufd&41C-p#K2T$I;r=)27vscS}A?L)gjvj+*{C4`AY5dHd~>cRllr{#&&gj#bjC zY+6f0A79C$LptVpR@4%t=Ah50TP6xN94{ztYZJtc?;6q4UMJgU@V{CA0B=6_&I2IN zZ8`S8S*ia3jk@;WGBf!3D!*=nr)90{TkQUa}SKa!R~i8 zVJ@j;?Oz%#9`3u!LG;#NWNHzeHgx0}{YzCC^NtEchLTQj%-yKI34@bB!prnUm+X3( zo_AmFV1}DTqAgvZg@yp|^ambDvV~7cJl3i-V5?i!bv2_m01mtiGoFZ~@lxh$;g|)B z8Ue37Xw^PMlI|b50wJZx&%M&*o_-=0SwuFP*&u>=F&g~(JCOH_e51w6WXXEn$l`8O@@N<= zvE&Oq_$LIo(md+-J-BU`pJ6yxv;na-?=aV%IT&1|oi^MlM3erQ&(}E4WwJ)Hsx`(QMNMP?1CR{E>zrz`6n8mGcA>!t zr@N>Lt)MsNtlzn@gS9ykGxZ=Uk_7?oBALJ$fM4G;`jLB6zXWBwihfn^P!>liQ*GHJ z{jdYDA$V*A_`^}vVP<$7t)IRDmN7wffEROlfF8R72=C7TQ@N^hiohBmr^)3A^~#4i zm}K@MZByQ;myS3sDwWz?{TJ>OUv_wtT9bXS!oyj73pK?Q%V<*7sp{YtO=Mlo=B|w! zGk|qAS$smLgLYxPKFN8TrMm`s{fN)zO99gE7gHjuyfXmr{{YBw7GpBupk?|E0etYqm@UM*b{2U6T~!7wntj50v1?C@!J?QgKx zzzaK(Ua}?%$r>o3!H}Q}B4i(pQfad_9svDRUW(eenmZ_wCx?CRSqJ|BDse=^;SH2h zUAmEfZYe5#r%q?ogua}Zv}S1_{R0C4u<`-&L{qQj%qdE8OuV_uDN5%DFg1Z|@qo`o zkiDp`i*z5_M!kluPxUWNQ8=F+>opYoGcU{>UCt|tVnXwAn7--74PXeF+267<`VVVV zTGcg2HJMC~T9nadrrUcCL@YfJQ1X?h>ZeX7v~_UI9k17z0fNA1+}03{=!6e?iL+7* zM{1C=$Q-1>4xHL z+ynPcuSE5(00@S@{14FB;k1`)sC`k*W*{Adz>NZ-J=$X3aN~Y{y2!_mI&C^2KZ2+O zFwga@$TmeeX6d_Ha()XWgZEMw(&y9Ldh_j~>)YP+ju?zd=VeiII z;Y&xMN6jfjH?wjJqUgX3EWxtP5LLedlhrT1o}6qJyIH)MHcK4PSr*{r4Y8_@L+#(^ z#Z@1+Fq5@$DqdBLkX~l6EuYXh*;ba%7)gOQNrd8Fx*>Q1InfI_IOENeh89ZZ4G=*d zpf3PaFXIS0DTJDs5Uljv;Zz-Hxi3P{b+s`ylS|m4IYi!rWYNJqhG%3+slJYWK!Y_m>Drb@$BST$o_l<_V}w zb=E)}N3?nV=MNC{IeYo@jNW7hinq*VTZi<`!x^vWXYa9 zjCdI@WQLSDpL86O+;$-!cSqCa9(zM@yxTRc%g1`bXd>ntroE) zSp)Eodt&$g=dyZ{t<7_=a39d0u^uX>_MG{hZsMbK`Cva5?x?%7x0C)zEuU?sVsh+6 z=Cg*$Xgpxa>4bCkw@lD*uWg7K8Yu94{rFFC{{WGJ`th#UhM-c9aL&WCU!NXg*}URn z4_{4X(_a++>?vFkCBi_p_#$>@GQ9O(AEEal^;l-Kwp0q`v21Fm(=q+SuEjJfsfrGx(H0yJ5 zUTEX-CA?Ac*#nfBE`fu^jTOPl*O`YPS>|VssJxydeI(U1UQ7@(18`@O&I{qSoi^^0 zZmnxd%uRwW5Ir}hKs>&{gFkWk-Xsuy~ay}#C)sn-NJ3H*iupR!K zcCiaUF++F^`TnFPtHwv>w-Bvy+77LXq@BqSe<5qq!}DaGK>9N>-XA8!2-*bPL|R|P!!dM@XIM<+Ru z$-Y**XN@ntYZ0)~Gvn>&I3q^%Y+Zbcf;k)Cd53?u6+F)Dl;OLxOsVFlioGc%qVnX4 zffVSXy@@E9ftuOO&MiRTQv_kD7ykf12j7cUc$f)y4k;HtkmvvnW)Wi9YZx~?NNa8n zOqb03V1J!3AH8*XSUZ-a3{Yr}Kmfibv#4Jz>RzJhft`!w0PaAP?>nQ5iEc+y{+`1A z#5?TT2D8O{&6}*(rnt!-!9{z{@&;?kRF_|C=Ag7rdT$4PZ zlAq9oT@OEc&fZyNw=-wlr-$vw*s0yBe~cjEH_SXwgPj~6y8irJLu8XQp+b&N9H7Ai zcIitJKrcAu;^ewz(2}Ma?8$!wVbh^-o5Kfs@6Q_B3eH6}coNpDtPT$)h893x&?O23 z(t4VAq_-q0xQ5M(Md7(3)Qk(z%PZk|Q6orVEm z9!>~k&2U+VE7Jy9;3!{d7-2|aAebU(7-$P2uj%ABJW72#-q;7OI_*#}XtxW7L&gl3 z-_I7lHI~W$00L%VW-3|ii;d&MHBO60;ZTrP3X@s0Eanc(4R8$@A$@^=Zu{bElTEQp z*r*1ItKWEEeU=7u_2TZBk~A}!yLM|Dskz9mgWx}YA_E4FYrsu4{Did37Cv)WKBRVF z;bN&aU5?(b;-vViSuUO60OahvHRL$#<;Vlp=x=Kn8J&iG zfQrYD<}VU&xTuI29!moI1D+|E$Pk7K>pjX#H9y{>FhtH{GeZ)+;jjKNC+^ACP0!+rv_V)G5LuOJqIvLk2} z8?@Tr^FBR#<6P-d-||L5vLsEk?Z!IhvF>@*J$mC*eEp2MslJ&zMYC|$s>fhJkF~uO z7E4lE)^Cc>_J@*SouC2JW@CV^hl9b>(bEzudip^Eoog?F?~F1q96uR0 z{{R{RIB#dcZSD71%tin+6~=1j`oc(`^=H21R)fH-Dv7Ltb=L8GofJBhmNY@i=Y98> zXUSk>iu#^*#NNx_F?@;cxZt&ipFN*aAkcHLb68XW37f-pWrhc3`OmT2f~jpiB6h!m zSt{_)a4a@?WncamEuzu%lL|rX)?H7t34#kC0OLLO8Rz5VgIXG%l~Zy=lam>kI&2HM zGds^7%>HJ06|I{ZJ3E* z{{TVGJplUfzh)^>i6|80uRkJ23iJqeA>wjpxe)pAQKWwC@ad0Cpa~ zxr;}mUzQ2Soob{10Pg<)l72=X{*PKc5j2Qh`L+BhotK%R$=heo#5WZW84UP9c4Kpe z@E=^(6v5FG*#oJnEGBA6-2gOpcq*ol8 z^Yg$_TnxiB+GBoQ0cQRUCyBCId8UEdZyw~cN6L;2D%wh2QaYACnKVV`ip>5_;Tyof z!o$fKCc}19N^J{bjF)_h&8EvN=@6N>Sir-t^bRPdR7}=`nn~mpqp7KYEVvuyljkUe z)DHVFOwPbq4$OzIL@;tVy!)C&mUjfx0tP5Q!j_L~O{&SfufAWK3dGo@J$tuUmqNK{3C5;GjW^`+OQYE2H(sF zl3ai4h90aqe;@jx7TS2qpP>kU8w zfyw1xE@Y9|sC33HGTL16{q{RBjOL`MUA&tMN6wZuh5#Djglk!!G^y*eG5o`FE88_^ zEuS2}#jb$WLm}D0oGkVr_2$^vqNsR|OtQ%VCgJrlu+T9qN9K-WP-TPB0n9r&=ENjGUrDXwsK>da(-GH1^MyvrrA z#O653pcApU3fKZ`DmM7(%~{#_;DMTxX>vIuUy!H|+;Sp$1J8=oc0~BWBT{4*7y;f0 zz>R-Op-q?|4$WgUZ!<8m$R0JmE3RXfN)zt0TVt*`sRvkXQDs_8Q|bYc3qp+AZ22UU zUBR$O&i??HKflxn)n!tCgi-*N%B)8~0!YCVSrpY_&c@F9!so9UpP=3;#9s}FhgnbL zv9=ooL(9)9H!F4;03-F_fu6}|b{2hx2Pget&1>UNZA-rC`O~CBxt=Pkig~=cjh)Q* z$0Xsa2TP-2*_;zQG(dwgYp1xAM(x(dwt%d6VW7p$TUyOnpa`Xm0Ehr+40W`60UGy7 zbe~QC00dC%s#{OAldI7#l{0LwO^K{%hnDKYU#ACxn?#7R1XXZ!opCgZ%WwxyfT~j! z6fcBPjz;;J$%Mq zH@i&p2#y;l^G>Cs~Day)RK^vCVTC?h&;qRN`;F16-& zoG*(^e+aLq8`h=(wz3Yvb;CoEcSZ;9nqS}dB1grxKYefUqT-d3F=swxx_H-t@vR=Y zEF2Zhz;r9-yh*li;cBq{+jZ^0d>Cz)_+7030JhzGa330&-y{|mSI#t{LWKpYXtlk9 zAKj1cJ^0sSAJ{lw<5fQq@5aFiuAPCtIzL_;-u`kT)@XG}M(TQ6z0omDIb+zc!{hXRc}laI<* zgz~?moR|joZGbu3Ssa@hYc(|!JHXaE3^W`V$Qx-~iH*Kn2~*0O0?d+KcR-79TEjE! zXM?uRWb=pW%u_(t$FK|z@Uru=GxlpAh)=NPf6SZ`(0SU2YeVD<(cHGx5H>XcOgX?k zi0Z;>(S(v}3#nnQ{d)tq4M!n)f$T*K@V&dV`1kXL*ktnCHBZdd@--r7DM_MkffbW5 z1Ie88*{`__t*PmE#V2mJz2@|90}cbe$PaR9wqW7YuOxt8a3~dos%n#jC7|VFK&@ERH+xLL2U{{SRpv$|CYYVvjx?YXqq!Zd>vZ0&8I;snWQS!Er;p7yX60t!1EpB8@vDFUUhv80XN%NLOmG8I^WQyy ziljkL*&1fQ@>aoN`GNJ~IA_g!4s)#VB}j|;!#C>9Y6@a-XQ35ApHkR40?3@&t&kgx zH*+Les(cPFMm1Vtumr5szzpVRlfH51F9|)NR+1XzF}9=K_5)1SQG+jAfGk1Aw5JS97-rwRV6%ZU>zf>i`F+amsOC zOq)3IGV-KbH)?n*G;VtP=MZKF99K!bgrABncz!#X05$oOE`v zykd4ByH_on@q#(xb2`pU{l@BQBF&N?hC45V&65DY5Mb~d+NO-J#@*0UfDLo+o{v-8 zh*MDC-?f);0pqRiL{E(Ja05{X!12MI_9A-DCopY-2D<72xLos?dk_vaZ8)QV_SI?! z5)oNIh(Qin$TmwH^aKl0>Y0i3qPY0^S4cw+Pds>tItvy``@wHc4V!J&WQtOGbjD1< z;<8H(6?27_A!olC2t3yL-b|SS$XLoE)m0SouA&DGmpQ8Swi5I-t88=~Pdk-9LPl~! zmp?a84IsQ013bdY4ZgBHfdmWJ3GHAD+X>1+>Q9XN(t?KAbT{<91u=g_#XwTKoNYUiFpG;I0WD zTF~jYjTlvJ-OQH&ZVwvXe!sr~UKaHXj;fu~PNZ`t&(a3YfXySm%;6AU)@^1=n-mjS zvAuHCGxAsg>BWD9QZ(Z_f|p}Trie5K24`lw&2B#4NGcpUw)F_oE6d)V3*KzOc+YQ0EVRJ%ElzpqiS~ZSF^pC(B;{0A93X+h3E$k$8jTg1wWYjLT<8aro8I z-(YCZ8bC1JolLOZ-T=+27np0kbAVc7I=er=@XnX@BS>4z%=rUy5BKmq^WKfMh}Dop zw6E`RUOV1BKlkSYhH3$Y>A@W<``7Qed-?N$^JeV$AUN1$NrT)4L~~~Dt{wU!wHJoj za9urdWbhY7C2tEdMJ@T@z*U01W`DS<&3@Z2Y#QO8?pYdc$egD# z*=0ES_H$jB0r{Cd$R)#g8*R+iax1as5wr`>yB~SGRj+Bs-zjM^X6Ot%VVomfdAb~+ z7{0vRHOB^SHJ(i2>F7Adwr+}lvi|^9rHKQVUA}nNXBK$Tz6Nx&pONj^@5i%4QoT`D z$F$oZN7PBpPD0(L+*~Vgu)MR5zo&0}S>-)|XE_ zD6$?hh{J6Qwc1LZZ0e30d=b?bDk6A_XgzTqoA$399s${tk0eEYaer0Gc)A!zYlMy8QXaw`uG@6{>h)oo#2w9y**N2ew0K?EqON>JtT$ zJq8=?hZor##m7^diB-l}>F!$``Q9!RiT4Bj+N00VG&@8k#-n(RV&!cx+CM^7B@ zX4oBW>mI(q904%eEmbF&f{wG^b*q#!pT- zeECI|nzcR4kIRR-#};&}1tc`%bk|S;TMc!N{xi;T=rf=hnzOhG4vS;$QRYamIDQOW zuZ-Skb~8i|KmqmF`UeIJOIJkIUR3XP#tyg@3&8uFkyA&@3@@HK1HEuP%nyMBJN+}) z1cTGl)6>(_0le?fuW6o@BQ*_zCe2(0-)|>n=k?;u4>!XcdQ#7R7~RkCM{CnZ$|+<~ zq;7CidP7meH!7JhKZl_nuKfAp*%PsYK=#5ZJY>+^B$?#Dk6n>)XVfxHLkr9=jT14A zi2K*Y?zc9smwv6D(NSj%(?w8U9L&!1vdFM(9elIT_9CWawfI<^k!+Wgmihj~ADQTi z$+Q9XgP1DG#Ynu8a%dpY6TJ}5XPzd@XKfxhRYP9K26vbrbM)YLvkT|n+=Mzp7tbvI zm_4|I(S+H}9SZF8GOEwFsy?YC>MbN_XaSft*8w4jb|9VL2hYzNg-9P_Tyt~AUo~uj z_4r|m)bp4^M&83>+0x(M`oOH{Hp!a+ePMyf~0K=Ri{BU?A9|aL*p97}o z0~^v({AF+QG1$NiI>f;H@gonT@5dKiN=CORMT^0wTJ%GYCRFU5dpZzB6@&0d=LoKM zKBwmh8x>aQwE4ow0iH}vM`p5$d>Ds(K;Jq?r!P;#3Z9j}1-Omj;h+ndZdX(37)#f` zQcGxo$CA_dK+#sjiz2_TBYjfyfg$J&SH{GAaM7p zb&TS?_!HJck17sgo*zd7qT>o}IRllZ>C z3-OH4CcKb%JQ*y*&`1ks6a}>$lpDM}Y;EK`w;s6A5v62nGH#-cG8!Oj1?Qh60&RfQ z3N~P8W&6lv0JEMnR&9{no8zu_Zo$XciAX$K92c=6hPmhZkmF_wFn=>*o1jKtyyVZ= z52+NmGJlWw8}M$PD!+IIgI+d>s@xdOYS*YcK^r&6UZtFVoJ5+imdk4_1}s})c&N(X*L+?8sE`0)H2tJYKJhZsM(Q+E9qp5{oJ$9_*hBEDrIYT1&j;q|@vusPZc z)drp}SkJIw9GuN}#Eq|uoPnPnOpquWt(rdJ9jOtGu~N2l{Rvdj+Uurk9_`eCahwm= zG_7%7FOM~BT^^lo| zMx`uk_8F>-x`~qm{`mf+HEoR6KSt_X{xSVX+EZ5a%o_CA-5qMjGkKIj4ICZ6(1HT* zF)}|6!K%UE?255qh9c{*0Q{$4sRNXI$&7}5H9-cqssVsze?wu%p`4m)IK#)iQ`Wa9 zU?VZvD`S6O)C;@Llh(%T%r{42!#LLmUXFUHJj+uMJ3XMThJW#1Uw8ggbX?v%_6DO}CKmP0xy6;x_gihY+4mrP9-Go7D{?bao;*CU?O( zte%|}ruY2`!%ffhXBCG%mwpj%_G9{x#ivYg6|);h@?-ju>h^%Vn@~JO&;1}WMG;&K za8nG2{{V-r9+aB2Tc@Kaaz~8b00)M9OgxDj!@TS;JNHwMThs2;aKP~tGqCIn&Lw#a zbpHT_6J`UUIAi#((x%-z+58+C4?I8o*zdT%zXR{;2TitvBBvUyU0~OZkp84z ztbb!3_ZRjK7q%Ew6MCL>HJA7;n&GjDe(Ion@$SC-XmwPbI=)xpY047UER6c(TrkE1 z^v*MX1+|kMjOfS)ADhite2{!oxVeNt<#=!9j*W#>X|~9|$d#O*UgKdp)FQ z=5+k}##1IP4)fP|dmZqPK8M=#BiOza50jbft-&{37DvWSbAiW?L)?x30Ee9?*v!|? zd`if(D+c}xmrk&UeJ^qrA5Y(h{WLi*g3b>m?p=riVX|6ltJcroI5@}<6|pn_0J2(N zkK_UEoEDsXg5$nP{{RyE9lP{UPcsOwoM)6f?nQtwe7BHlvG)QQGN-32O5LWL6TH#L z5z&KqsmMP>*NnF0V0BwB$UXCogJ(tuv$-OPr<@taNw>Lc=~ZOWtksImkHw&12g!No zjoW1sMAq50IJ%qW5wq*CfHG$|-`o!idu!!ZmJRExn5gsSz_hs3;{!w6i1Ztm+pNI` zvn|;#8CYoN>yE(Vf-2ft4CE)eg1~slUyI+0-G-us%i}8-XaR;W7+x9=JZH%LA~m{U zqG`$kA9xSEc=Q)_p<}%7I84KQRaEgbc0XQ7C97$LBsD5hq#qrQgBT9W&yvWANl0ch zS&M`dKDYw&yff=R(}Kb9(aj>1#Fr%CtHM+qa63DdH3vtJjhr%A7sj-;9_$Y~i24T< zRc#*mn=c+AVcFCeBpe*^7}G~QGFSbW_y+M=Q^~6G*7ysjdGH@RCe58JkSOWW-eK7h zC)k_#0*YUf{E%SUjC=`w(nin3!y`2?Wr)FJ^LRmH42xJ1jP3fE zV@ys`Yb`f6aBl~B@J67GA2C?))2Pi5>>_#nc*{25B=I6r#1xvF#a$Zr-WFM+E`#$j z$!D&39!3bmV>7h^k_M_SiGVpf0aJsA^&;lg!eGaPS?37Nb~4S`)0eefVEIYtI|G6= z3B-nK+FRpQ&^#r~5Cb!^33`mSd(u{0nBnwmf*(0+9<@9R!G53*lbqt5*fmAKF1?16 zpuav%89t;Dc#>qawPn;y!6UH@=jDvgk}fK(ohKwYBUK+7&t@lQWA)DgJtQbt(ALV| zZF#WHpn|FCBJO9v(FZ}gE_lXz00K1xWmEcwrj;PlM)Bmek>i@cjzd2A{>Rsd zlO2W2YF9}$NzlNROIfo>I2)n>9;85dI&REyA50rJ4%yiP(;cc%>@LTj zgOlWL265rM^WU_t2x18RX_HXnh=)11Kp93z{{U{1G)Lf-$=td8X@Vi|z{JaKbyJ_1 zwPMVBm!Gd35uZO|C|PViD|K3Q^!hrm0DzsPP5>ym>vwY`K~rG8f@fvDvbKfus!)Fyz=IJf&M(`3r8#jjGjej1EamBS^P8nFF;hHDNs@5N%ad29I_nNvy z*0nY*Y5v!z$NOwrGi~1+&ITXVRXochx`E}Kh@KCz6o|ZHwrKYEq5`_!QglB>`uV4o`UVfxYlfP1-R>>TiaQ^_d-deSuosld>RjMC~nh|9o`>@Nu7ZTl=R}aKY(BGH)t;{ zW{?@9@`bqk$jkf#48bby&PHGHquW#o{exHX#rWgrm#ee!WRL>i+F=8c0Lghxf;LJ( z4NeR5=hqP=?B746nlTF&Zo4o*z|PE;cOt+#=1F%ig+!Wv7NzIGyJUjsUPz*+O3;60 zW0kXMI|-MIrOZvUhDX z0_QGRz`z#_8#EYXjQ{{p2Q~BAQl1x%jH}sEsC4nKqW3GEo@Ji=8SLmjZl4A>*MOO5 z8Z^_4K(N7V{+cnXY?f5f%#VIu05x%s1mIb311uF$HptICExP`takM3Y%?#lHJoX{L zeBl-pG}nBPx0ddU2I|cKHIG5$;D|dyY??Gg%)-C`L@cmBrU>mPoJyoLl(J#B^+Ff{p(8i=JWG#zU6TIFkBvt;1SkN417$fyF8f7@IbqrqR{I-#|o-d zVSoqDLMFaUOpBO_*UV8TLSc(F2f77c)&jvx3g#513;c zi_-%Q@ZwvVwwYOTHnYiTrTJn%UMm7?E?AWX<}Az&$(R^C4IZ3!gSjTrW=7BoX1Q%U z0ksD^UR)M@n!~&_X9&eMdt9kWO58C*O};9-%zSKXI5cRDg1=d#{hv~oc#V!hd+!big8u-);RbAt@|7Ng z0J8_2?C0&oCp!v3vRM8ru9|j~TMT?i_l{W0aJRjHA_%R0>O9m_J=$TEOH84BJa;ah+i* zeJVkiBy0>13d=9N`62;7QQ`zcFfzwA=Y^?Rk>0yU%-q-)^($FtrxQ`6pICU{=KXiz z-X5G0%9sMq9oVIpw$JDX)Pn}n)~3}XT>t~HFEE15kW(*a87f|DMDR>S&*3x0!#S!5fOY1LoQ82q z4;X;;?M(D&TF8`hDKf{@NtOV=@YlzOij!wn{mAZHTOf_;sQ3&u^}umpb!@5PprNRV z*`N&a1OYjn(n|nfQ;*TJU5_un)QXj!F*SOG9Dr~I@F#8#CVy!gYhGtHmn`5Ebhua= zGvtY=ylZ~^qUfxA00)!4af)y0@F?&+@P~xqa1Wil4>CKoqTst4+|Koa_3|aJKC?JW zETA4woE}6#K7=C_Myi=3)u#F7ors#xX%m(T-+li8jYJ}z%@P;D_lnlcFj5I>9#r+j zhS+APFRdoI5%0!i&uPirYC34Kn)=Kl=^d!Z(Xl%mqI@4FzR2wZ!B6eBb6TA+&v~d? z2EEAlrH{A46#G<~gx6@m)egAX0t~$UXX~6(%C^}fk}@`w%(dhBn*RVy;Tr<}=Ckfc zDq>YNT4>&RI89_#1-GC`{&eK;zVqY`{8ZaJvNfK$&+boR^@T$_UP#p@Db6Na#rAbC z%>4avnm>4(nTT}g0E%^|4vkY?^7pt?g8cyz&CQ7$rPQ$OJHwDTJourw*i{2H^OP70 z)+m*eTA`!mw}t31bh(F_8b$cQFkL=5t5&|aN1V2GHJZz2D$f&K5m*BO6EM6xk)g@< zknGY|9MRnZWD^CP7;)dC6U>HnJk#hi0f5srx6-QuG`c2wFLZzJ&Nxq)FDIv-&QyGe zU?X@~exzrN_OuKsR_$7*%*?>gt?ik`O?kAiso6A@OKjc10M@gN`5*|;-PTNHOaNh9 zd~@Fb0oVWotbky5=j1qW9pwN>xlv+d#fp7u2cNcg-e(5@2uY{c8+7>L6 zDLw)B@_l&V;6|yL>llKs+XSftmem+&8b2Vj%X;ufutpD-*zGvF4)49O*?KpCXE^1y zWK9+oULl2>f#kEX1YQokPQ|Hk1jFXX`QwjYKSUB6d@y2DqP*F%w%FTHcYn}tUfgTd zfMCtqCY_nl;|&?YGaZb9p3vt9$78S0#-Ul*YO-9y{a}8CT>1Ko$o`vu;z!i3I7q

Q(K8WF-eziG3d5CY`YKRYz4I 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 literal 0 HcmV?d00001 diff --git a/docs/specs/introduction.md b/docs/specs/introduction.md new file mode 100644 index 000000000000..66f8f95adb88 --- /dev/null +++ b/docs/specs/introduction.md @@ -0,0 +1,19 @@ +# Introduction + +The goal of the ZK Stack is to power the internet of value. Value needs to be secured, and only blockchains are able +provide the level of security that the internet needs. The ZK Stack can be used to launch zero-knowledge rollups, which +are extra secure blockchains. + +ZK Rollups use advanced mathematics called zero-knowledge proofs to show that the execution of the rollup was done +correctly. They also send ("roll up") their data to another chain, in our case this is Ethereum. The ZK Stack uses the +zkEVM to execute transactions, making it Ethereum compatible. + +These two techniques allow the rollup to be verified externally. Unlike traditional blockchains, where you have to run a +node to verify all transactions, the state of the rollup can be easily checked by external participants by validating +the proof. + +These external validators of a rollup can be other rollups. This means we can connect rollups trustlessly, and create a +network of rollups. This network is called the hyperchain. + +These specs will provide a high level overview of the zkEVM and a full specification of its more technical components, +such as the prover, compiler, and the VM itself. We also specify the foundations of the hyperchain ecosystem. diff --git a/docs/specs/l1_l2_communication/README.md b/docs/specs/l1_l2_communication/README.md new file mode 100644 index 000000000000..6cccb85f4576 --- /dev/null +++ b/docs/specs/l1_l2_communication/README.md @@ -0,0 +1,5 @@ +# L1<->L2 Communication + +- [Overview - Deposits and Withdrawals](./overview_deposits_withdrawals.md) +- [L2->L1 messages](./l2_to_l1.md) +- [L1->L2 messages](./l1_to_l2.md) diff --git a/docs/specs/l1_l2_communication/l1_to_l2.md b/docs/specs/l1_l2_communication/l1_to_l2.md new file mode 100644 index 000000000000..46855cee50cc --- /dev/null +++ b/docs/specs/l1_l2_communication/l1_to_l2.md @@ -0,0 +1,170 @@ +# Handling L1→L2 ops + +The transactions on zkSync can be initiated not only on L2, but also on L1. There are two types of transactions that can +be initiated on L1: + +- Priority operations. These are the kind of operations that any user can create. +- Upgrade transactions. These can be created only during upgrades. + +### Prerequisites + +Please read the full +[article](https://github.com/code-423n4/2023-10-zksync/blob/main/docs/Smart%20contract%20Section/System%20contracts%20bootloader%20description.md) +on the general system contracts / bootloader structure as well as the pubdata structure with Boojum system to understand +[the difference](https://github.com/code-423n4/2023-10-zksync/blob/main/docs/Smart%20contract%20Section/Handling%20pubdata%20in%20Boojum.md) +between system and user logs. + +## Priority operations + +### Initiation + +A new priority operation can be appended by calling the +[requestL2Transaction](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/contracts/ethereum/contracts/zksync/facets/Mailbox.sol#L236) +method on L1. This method will perform several checks for the transaction, making sure that it is processable and +provides enough fee to compensate the operator for this transaction. Then, this transaction will be +[appended](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/contracts/ethereum/contracts/zksync/facets/Mailbox.sol#L369C1-L369C1) +to the priority queue. + +### Bootloader + +Whenever an operator sees a priority operation, it can include the transaction into the batch. While for normal L2 +transaction the account abstraction protocol will ensure that the `msg.sender` has indeed agreed to start a transaction +out of this name, for L1→L2 transactions there is no signature verification. In order to verify that the operator +includes only transactions that were indeed requested on L1, the bootloader +[maintains](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/system-contracts/bootloader/bootloader.yul#L970) +two variables: + +- `numberOfPriorityTransactions` (maintained at `PRIORITY_TXS_L1_DATA_BEGIN_BYTE` of bootloader memory) +- `priorityOperationsRollingHash` (maintained at `PRIORITY_TXS_L1_DATA_BEGIN_BYTE + 32` of the bootloader memory) + +Whenever a priority transaction is processed, the `numberOfPriorityTransactions` gets incremented by 1, while +`priorityOperationsRollingHash` is assigned to `keccak256(priorityOperationsRollingHash, processedPriorityOpHash)`, +where `processedPriorityOpHash` is the hash of the priority operations that has been just processed. + +Also, for each priority transaction, we +[emit](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/system-contracts/bootloader/bootloader.yul#L966) +a user L2→L1 log with its hash and result, which basically means that it will get Merklized and users will be able to +prove on L1 that a certain priority transaction has succeeded or failed (which can be helpful to reclaim your funds from +bridges if the L2 part of the deposit has failed). + +Then, at the end of the batch, we +[submit](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/system-contracts/bootloader/bootloader.yul#L3819) +and 2 L2→L1 log system log with these values. + +### Batch commit + +During block commit, the contract will remember those values, but not validate them in any way. + +### Batch execution + +During batch execution, we would pop `numberOfPriorityTransactions` from the top of priority queue and +[verify](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/contracts/ethereum/contracts/zksync/facets/Executor.sol#L282) +that their rolling hash does indeed equal to `priorityOperationsRollingHash`. + +## Upgrade transactions + +### Initiation + +Upgrade transactions can only be created during a system upgrade. It is done if the `DiamondProxy` delegatecalls to the +implementation that manually puts this transaction into the storage of the DiamondProxy. Note, that since it happens +during the upgrade, there is no “real” checks on the structure of this transaction. We do have +[some validation](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol#L175), +but it is purely on the side of the implementation which the `DiamondProxy` delegatecalls to and so may be lifted if the +implementation is changed. + +The hash of the currently required upgrade transaction is +[stored](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/contracts/ethereum/contracts/zksync/Storage.sol#L138) +under `l2SystemContractsUpgradeTxHash`. + +We will also track the batch where the upgrade has been committed in the `l2SystemContractsUpgradeBatchNumber` +[variable](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/contracts/ethereum/contracts/zksync/Storage.sol#L141). + +We can not support multiple upgrades in parallel, i.e. the next upgrade should start only after the previous one has +been complete. + +### Bootloader + +The upgrade transactions are processed just like with priority transactions, with only the following differences: + +- We can have only one upgrade transaction per batch & this transaction must be the first transaction in the batch. +- The system contracts upgrade transaction is not appended to `priorityOperationsRollingHash` and doesn’t increment + `numberOfPriorityTransactions`. Instead, its hash is calculated via a system L2→L1 log _before_ it gets executed. + Note, that it is an important property. More on it [below](#security-considerations). + +### Commit + +After an upgrade has been initiated, it will be required that the next commit batches operation already contains the +system upgrade transaction. It is +[checked](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/contracts/ethereum/contracts/zksync/facets/Executor.sol#L157) +by verifying the corresponding L2→L1 log. + +We also remember that the upgrade transaction has been processed in this batch (by amending the +`l2SystemContractsUpgradeBatchNumber` variable). + +### Revert + +In a very rare event when the team needs to revert the batch with the upgrade on zkSync, the +`l2SystemContractsUpgradeBatchNumber` is +[reset](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/contracts/ethereum/contracts/zksync/facets/Executor.sol#L412). + +Note, however, that we do not “remember” that certain batches had a version before the upgrade, i.e. if the reverted +batches will have to be reexecuted, the upgrade transaction must still be present there, even if some of the deleted +batches were committed before the upgrade and thus didn’t contain the transaction. + +### Execute + +Once batch with the upgrade transaction has been executed, we +[delete](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/contracts/ethereum/contracts/zksync/facets/Executor.sol#L304) +them from storage for efficiency to signify that the upgrade has been fully processed and that a new upgrade can be +initiated. + +## Security considerations + +Since the operator can put any data into the bootloader memory and for L1→L2 transactions the bootloader has to blindly +trust it and rely on L1 contracts to validate it, it may be a very powerful tool for a malicious operator. Note, that +while the governance mechanism is generally trusted, we try to limit our trust for the operator as much as possible, +since in the future anyone would be able to become an operator. + +Some time ago, we _used to_ have a system where the upgrades could be done via L1→L2 transactions, i.e. the +implementation of the `DiamondProxy` upgrade would +[include](https://github.com/matter-labs/era-contracts/blob/f06a58360a2b8e7129f64413998767ac169d1efd/ethereum/contracts/zksync/upgrade-initializers/DIamondUpgradeInit2.sol#L27) +a priority transaction (with `from` equal to for instance `FORCE_DEPLOYER`) with all the upgrade params. + +In the Boojum though having such logic would be dangerous and would allow for the following attack: + +- Let’s say that we have at least 1 priority operations in the priority queue. This can be any operation, initiated by + anyone. +- The operator puts a malicious priority operation with an upgrade into the bootloader memory. This operation was never + included in the priority operations queue / and it is not an upgrade transaction. However, as already mentioned above + the bootloader has no idea what priority / upgrade transactions are correct and so this transaction will be processed. + +The most important caveat of this malicious upgrade is that it may change implementation of the `Keccak256` precompile +to return any values that the operator needs. + +- When the`priorityOperationsRollingHash` will be updated, instead of the “correct” rolling hash of the priority + transactions, the one which would appear with the correct topmost priority operation is returned. The operator can’t + amend the behaviour of `numberOfPriorityTransactions`, but it won’t help much, since the + the`priorityOperationsRollingHash` will match on L1 on the execution step. + +That’s why the concept of the upgrade transaction is needed: this is the only transaction that can initiate transactions +out of the kernel space and thus change bytecodes of system contracts. That’s why it must be the first one and that’s +why +[emit](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/system-contracts/bootloader/bootloader.yul#L587) +its hash via a system L2→L1 log before actually processing it. + +### Why it doesn’t break on the previous version of the system + +This section is not required for Boojum understanding but for those willing to analyze the production system that is +deployed at the time of this writing. + +Note that the hash of the transaction is calculated before the transaction is executed: +[https://github.com/matter-labs/era-system-contracts/blob/3e954a629ad8e01616174bde2218241b360fda0a/bootloader/bootloader.yul#L1055](https://github.com/matter-labs/era-system-contracts/blob/3e954a629ad8e01616174bde2218241b360fda0a/bootloader/bootloader.yul#L1055) + +And then we publish its hash on L1 via a _system_ L2→L1 log: +[https://github.com/matter-labs/era-system-contracts/blob/3e954a629ad8e01616174bde2218241b360fda0a/bootloader/bootloader.yul#L1133](https://github.com/matter-labs/era-system-contracts/blob/3e954a629ad8e01616174bde2218241b360fda0a/bootloader/bootloader.yul#L1133) + +In the new upgrade system, the `priorityOperationsRollingHash` is calculated on L2 and so if something in the middle +changes the implementation of `Keccak256`, it may lead to the full `priorityOperationsRollingHash` be maliciously +crafted. In the pre-Boojum system, we publish all the hashes of the priority transactions via system L2→L1 and then the +rolling hash is calculated on L1. This means that if at least one of the hash is incorrect, then the entire rolling hash +will not match also. diff --git a/docs/specs/l1_l2_communication/l2_to_l1.md b/docs/specs/l1_l2_communication/l2_to_l1.md new file mode 100644 index 000000000000..c13194bdec98 --- /dev/null +++ b/docs/specs/l1_l2_communication/l2_to_l1.md @@ -0,0 +1,72 @@ +# L2→L1 communication + +The L2→L1 communication is more fundamental than the L1→L2 communication, as the second relies on the first. L2→L1 +communication happens by the L1 smart contract verifying messages alongside the proofs. The only “provable” part of the +communication from L2 to L1 are native L2→L1 logs emitted by VM. These can be emitted by the `to_l1` +[opcode](https://github.com/code-423n4/2023-10-zksync/blob/main/docs/Smart%20contract%20Section/System%20contracts%20bootloader%20description.md). +Each log consists of the following fields: + +```solidity +struct L2Log { + uint8 l2ShardId; + bool isService; + uint16 txNumberInBatch; + address sender; + bytes32 key; + bytes32 value; +} + +``` + +Where: + +- `l2ShardId` is the id of the shard the opcode was called (it is currently always 0). +- `isService` a boolean flag that is not used right now +- `txNumberInBatch` the number of the transaction in the batch where the log has happened. This number is taken from the + internal counter which is incremented each time the `increment_tx_counter` is + [called](https://github.com/code-423n4/2023-10-zksync/blob/main/docs/Smart%20contract%20Section/System%20contracts%20bootloader%20description.md). +- `sender` is the value of `this` in the frame where the L2→L1 log was emitted. +- `key` and `value` are just two 32-byte values that could be used to carry some data with the log. + +The hashed array of these opcodes is then included into the +[batch commitment](https://github.com/matter-labs/era-contracts/blob/f06a58360a2b8e7129f64413998767ac169d1efd/ethereum/contracts/zksync/facets/Executor.sol#L493). +Because of that we know that if the proof verifies, then the L2→L1 logs provided by the operator were correct, so we can +use that fact to produce more complex structures. Before Boojum such logs were also Merklized within the circuits and so +the Merkle tree’s root hash was included into the batch commitment also. + +## Important system values + +Two `key` and `value` fields are enough for a lot of system-related use-cases, such as sending timestamp of the batch, +previous batch hash, etc. They were and are used +[used](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/system-contracts/contracts/SystemContext.sol#L438) +to verify the correctness of the batch's timestamps and hashes. You can read more about block processing +[here](https://github.com/code-423n4/2023-10-zksync/blob/main/docs/Smart%20contract%20Section/Batches%20&%20L2%20blocks%20on%20zkSync.md). + +## Long L2→L1 messages & bytecodes + +However, sometimes users want to send long messages beyond 64 bytes which `key` and `value` allow us. But as already +said, these L2→L1 logs are the only ways that the L2 can communicate with the outside world. How do we provide long +messages? + +Let’s add an `sendToL1` method in L1Messenger, where the main idea is the following: + +- Let’s submit an L2→L1 log with `key = msg.sender` (the actual sender of the long message) and + `value = keccak256(message)`. +- Now, during batch commitment the operator will have to provide an array of such long L2→L1 messages and it will be + checked on L1 that indeed for each such log the correct preimage was provided. + +A very similar idea is used to publish uncompressed bytecodes on L1 (the compressed bytecodes were sent via the long +L1→L2 messages mechanism as explained above). + +Note, however, that whenever someone wants to prove that a certain message was present, they need to compose the L2→L1 +log and prove its presence. + +## Priority operations + +Also, for each priority operation, we would send its hash and it status via an L2→L1 log. On L1 we would then +reconstruct the rolling hash of the processed priority transactions, allowing to correctly verify during the +`executeBatches` method that indeed the batch contained the correct priority operations. + +Importantly, the fact that both hash and status were sent, it made it possible to +[prove](https://github.com/code-423n4/2023-10-zksync/blob/ef99273a8fdb19f5912ca38ba46d6bd02071363d/code/contracts/ethereum/contracts/bridge/L1ERC20Bridge.sol#L255) +that the L2 part of a deposit has failed and ask the bridge to release funds. diff --git a/docs/specs/l1_l2_communication/overview_deposits_withdrawals.md b/docs/specs/l1_l2_communication/overview_deposits_withdrawals.md new file mode 100644 index 000000000000..4137fe1f1b5f --- /dev/null +++ b/docs/specs/l1_l2_communication/overview_deposits_withdrawals.md @@ -0,0 +1,13 @@ +# Overview - Deposits and Withdrawals + +The zkEVM supports general message passing for L1<->L2 communication. Proofs are settled on L1, so core of this process +is the [L2->L1] message passing process. [L1->L2] messages are recorded on L1 inside a priority queue, the sequencer +picks it up from here and executes it in the zkEVM. The zkEVM sends an L2->L1 message of the L1 transactions that it +processed, and the rollup's proof is only valid if the processed transactions were exactly right. + +There is an asymmetry in the two directions however, in the L1->L2 direction we support starting message calls by having +a special transaction type called L1 transactions. In the L2->L1 direction we only support message passing. + +In particular, deposits and withdrawals of ether also use the above methods. For deposits the L1->L2 transaction is sent +with empty calldata, the recipients address and the deposited value. When withdrawing, an L2->L1 message is sent. This +is then processed by the smart contract holding the ether on L1, which releases the funds. diff --git a/docs/specs/l1_smart_contracts.md b/docs/specs/l1_smart_contracts.md new file mode 100644 index 000000000000..3bcd6a1ac4fe --- /dev/null +++ b/docs/specs/l1_smart_contracts.md @@ -0,0 +1,289 @@ +# L1 Smart contracts + +This document presumes familiarity with Rollups. For a better understanding, consider reading the overview +[here](./overview.md). + +Rollups inherit security and decentralization guarantees from Ethereum, on which they store information about changes in +their own state, providing validity proofs for state transition, implementing a communication mechanism, etc. In +practice, all this is achieved by Smart Contracts built on top of Ethereum. This document details the architecture of +the L2 contracts on Ethereum Layer 1. We also have contracts that support the hyperchain ecosystem, we cover those in +the [Shared Bridge](./the_hyperchain/shared_bridge.md) section. The Shared Bridge relies on these individual contracts. + +## Diamond + +Technically, this L1 smart contract acts as a connector between Ethereum (L1) and a single L2. It checks the validity +proof and data availability, handles L2 <-> L1 communication, finalizes L2 state transition, and more. + +![diamondProxy.png](./img/diamondProxy.jpg) + +### DiamondProxy + +The main contract uses [EIP-2535](https://eips.ethereum.org/EIPS/eip-2535) diamond proxy pattern. It is an in-house +implementation that is inspired by the [mudgen reference implementation](https://github.com/mudgen/Diamond). It has no +external functions, only the fallback that delegates a call to one of the facets (target/implementation contract). So +even an upgrade system is a separate facet that can be replaced. + +One of the differences from the reference implementation is access freezability. Each of the facets has an associated +parameter that indicates if it is possible to freeze access to the facet. Privileged actors can freeze the **diamond** +(not a specific facet!) and all facets with the marker `isFreezable` should be inaccessible until the governor or admin +unfreezes the diamond. Note that it is a very dangerous thing since the diamond proxy can freeze the upgrade system and +then the diamond will be frozen forever. + +The diamond proxy pattern is very flexible and extendable. For now, it allows splitting implementation contracts by +their logical meaning, removes the limit of bytecode size per contract and implements security features such as +freezing. In the future, it can also be viewed as [EIP-6900](https://eips.ethereum.org/EIPS/eip-6900) for +[zkStack](https://blog.matter-labs.io/introducing-the-zk-stack-c24240c2532a), where each hyperchain can implement a +sub-set of allowed implementation contracts. + +### GettersFacet + +Separate facet, whose only function is providing `view` and `pure` methods. It also implements +[diamond loupe](https://eips.ethereum.org/EIPS/eip-2535#diamond-loupe) which makes managing facets easier. This contract +must never be frozen. + +### AdminFacet + +Controls changing the privileged addresses such as governor and validators or one of the system parameters (L2 +bootloader bytecode hash, verifier address, verifier parameters, etc), and it also manages the freezing/unfreezing and +execution of upgrades in the diamond proxy. + +The admin facet is controlled by two entities: + +- Governance - Separate smart contract that can perform critical changes to the system as protocol upgrades. This + contract controlled by two multisigs, one managed by Matter Labs team and another will be multisig with well-respected + contributors in the crypto space. Only together they can perform an instant upgrade, the Matter Labs team can only + schedule an upgrade with delay. +- Admin - Multisig smart contract managed by Matter Labs that can perform non-critical changes to the system such as + granting validator permissions. Note, that the Admin is the same multisig as the owner of the governance. + +### MailboxFacet + +The facet that handles L2 <-> L1 communication, an overview for which can be found in +[docs](https://era.zksync.io/docs/dev/developer-guides/bridging/l1-l2-interop.html). + +The Mailbox performs three functions: + +- L1 <-> L2 communication. +- Bridging native Ether to the L2 (with the launch of the Shared Bridge this will be moved) +- Censorship resistance mechanism (in the research stage). + +L1 -> L2 communication is implemented as requesting an L2 transaction on L1 and executing it on L2. This means a user +can call the function on the L1 contract to save the data about the transaction in some queue. Later on, a validator can +process it on L2 and mark it as processed on the L1 priority queue. Currently, it is used for sending information from +L1 to L2 or implementing multi-layer protocols. + +_NOTE_: While user requests the transaction from L1, the initiated transaction on L2 will have such a `msg.sender`: + +```solidity + address sender = msg.sender; + if (sender != tx.origin) { + sender = AddressAliasHelper.applyL1ToL2Alias(msg.sender); + } +``` + +where + +```solidity +uint160 constant offset = uint160(0x1111000000000000000000000000000000001111); + +function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) { + unchecked { + l2Address = address(uint160(l1Address) + offset); + } +} + +``` + +For most of the rollups the address aliasing needs to prevent cross-chain exploits that would otherwise be possible if +we simply reused the same L1 addresses as the L2 sender. In zkEVM address derivation rule is different from the +Ethereum, so cross-chain exploits are already impossible. However, the zkEVM may add full EVM support in the future, so +applying address aliasing leaves room for future EVM compatibility. + +The L1 -> L2 communication is also used for bridging ether. The user should include a `msg.value` when initiating a +transaction request on the L1 contract. Before executing a transaction on L2, the specified address will be credited +with the funds. To withdraw funds user should call `withdraw` function on the `L2EtherToken` system contracts. This will +burn the funds on L2, allowing the user to reclaim them through the `finalizeEthWithdrawal` function on the +`MailboxFacet`. + +More about L1->L2 operations can be found +[here](https://github.com/code-423n4/2023-10-zksync/blob/main/docs/Smart%20contract%20Section/Handling%20L1→L2%20ops%20on%20zkSync.md). + +L2 -> L1 communication, in contrast to L1 -> L2 communication, is based only on transferring the information, and not on +the transaction execution on L1. The full description of the mechanism for sending information from L2 to L1 can be +found +[here](https://github.com/code-423n4/2023-10-zksync/blob/main/docs/Smart%20contract%20Section/Handling%20pubdata%20in%20Boojum.md). + +### ExecutorFacet + +A contract that accepts L2 batches, enforces data availability and checks the validity of zk-proofs. + +The state transition is divided into three stages: + +- `commitBatches` - check L2 batch timestamp, process the L2 logs, save data for a batch, and prepare data for zk-proof. +- `proveBatches` - validate zk-proof. +- `executeBatches` - finalize the state, marking L1 -> L2 communication processing, and saving Merkle tree with L2 logs. + +Each L2 -> L1 system log will have a key that is part of the following: + +```solidity +enum SystemLogKey { + L2_TO_L1_LOGS_TREE_ROOT_KEY, + TOTAL_L2_TO_L1_PUBDATA_KEY, + STATE_DIFF_HASH_KEY, + PACKED_BATCH_AND_L2_BLOCK_TIMESTAMP_KEY, + PREV_BATCH_HASH_KEY, + CHAINED_PRIORITY_TXN_HASH_KEY, + NUMBER_OF_LAYER_1_TXS_KEY, + EXPECTED_SYSTEM_CONTRACT_UPGRADE_TX_HASH_KEY +} + +``` + +When a batch is committed, we process L2 -> L1 system logs. Here are the invariants that are expected there: + +- In a given batch there will be either 7 or 8 system logs. The 8th log is only required for a protocol upgrade. +- There will be a single log for each key that is contained within `SystemLogKey` +- Three logs from the `L2_TO_L1_MESSENGER` with keys: +- `L2_TO_L1_LOGS_TREE_ROOT_KEY` +- `TOTAL_L2_TO_L1_PUBDATA_KEY` +- `STATE_DIFF_HASH_KEY` +- Two logs from `L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR` with keys: + - `PACKED_BATCH_AND_L2_BLOCK_TIMESTAMP_KEY` + - `PREV_BATCH_HASH_KEY` +- Two or three logs from `L2_BOOTLOADER_ADDRESS` with keys: + - `CHAINED_PRIORITY_TXN_HASH_KEY` + - `NUMBER_OF_LAYER_1_TXS_KEY` + - `EXPECTED_SYSTEM_CONTRACT_UPGRADE_TX_HASH_KEY` +- None logs from other addresses (may be changed in the future). + +### DiamondInit + +It is a one-function contract that implements the logic of initializing a diamond proxy. It is called only once on the +diamond constructor and is not saved in the diamond as a facet. + +Implementation detail - function returns a magic value just like it is designed in +[EIP-1271](https://eips.ethereum.org/EIPS/eip-1271), but the magic value is 32 bytes in size. + +## Bridges + +Bridges are completely separate contracts from the Diamond. They are a wrapper for L1 <-> L2 communication on contracts +on both L1 and L2. Upon locking assets on L1, a request is sent to mint these bridged assets on L2. Upon burning assets +on L2, a request is sent to unlock them on L2. + +Unlike the native Ether bridging, all other assets can be bridged by the custom implementation relying on the trustless +L1 <-> L2 communication. + +### L1ERC20Bridge + +The "standard" implementation of the ERC20 token bridge. Works only with regular ERC20 tokens, i.e. not with +fee-on-transfer tokens or other custom logic for handling user balances. + +- `deposit` - lock funds inside the contract and send a request to mint bridged assets on L2. +- `claimFailedDeposit` - unlock funds if the deposit was initiated but then failed on L2. +- `finalizeWithdrawal` - unlock funds for the valid withdrawal request from L2. + +The owner of the L1ERC20Bridge is the Governance contract. + +### L2ERC20Bridge + +The L2 counterpart of the L1 ERC20 bridge. + +- `withdraw` - initiate a withdrawal by burning funds on the contract and sending a corresponding message to L1. +- `finalizeDeposit` - finalize the deposit and mint funds on L2. The function is only callable by L1 bridge. + +The owner of the L2ERC20Bridge and the contracts related to it is the Governance contract. + +### L1WethBridge + +The custom bridge exclusively handles transfers of WETH tokens between the two domains. It is designed to streamline and +enhance the user experience for bridging WETH tokens by minimizing the number of transactions required and reducing +liquidity fragmentation thus improving efficiency and user experience. + +This contract accepts WETH deposits on L1, unwraps them to ETH, and sends the ETH to the L2 WETH bridge contract, where +it is wrapped back into WETH and delivered to the L2 recipient. + +Thus, the deposit is made in one transaction, and the user receives L2 WETH that can be unwrapped to ETH. + +For withdrawals, the contract receives ETH from the L2 WETH bridge contract, wraps it into WETH, and sends the WETH to +the L1 recipient. + +The owner of the L1WethBridge contract is the Governance contract. + +### L2WethBridge + +The L2 counterpart of the L1 WETH bridge. + +The owner of the L2WethBridge and L2Weth contracts is the Governance contract. + +## Governance + +This contract manages calls for all governed zkEVM contracts on L1 and L2. Mostly, it is used for upgradability an +changing critical system parameters. The contract has minimum delay settings for the call execution. + +Each upgrade consists of two steps: + +- Scheduling - The owner can schedule upgrades in two different manners: + - Fully transparent data. All the targets, calldata, and upgrade conditions are known to the community before upgrade + execution. + - Shadow upgrade. The owner only shows the commitment to the upgrade. This upgrade type is mostly useful for fixing + critical issues in the production environment. +- Upgrade execution - the Owner or Security council can perform the upgrade with previously scheduled parameters. + - Upgrade with delay. Scheduled operations should elapse the delay period. Both the owner and Security Council can + execute this type of upgrade. + - Instant upgrade. Scheduled operations can be executed at any moment. Only the Security Council can perform this type + of upgrade. + +Please note, that both the Owner and Security council can cancel the upgrade before its execution. + +The diagram below outlines the complete journey from the initiation of an operation to its execution. + +![governance.png](./img/governance.jpg) + +## ValidatorTimelock + +An intermediate smart contract between the validator EOA account and the zkSync smart contract. Its primary purpose is +to provide a trustless means of delaying batch execution without modifying the main zkSync contract. zkSync actively +monitors the chain activity and reacts to any suspicious activity by freezing the chain. This allows time for +investigation and mitigation before resuming normal operations. + +It is a temporary solution to prevent any significant impact of the validator hot key leakage, while the network is in +the Alpha stage. + +This contract consists of four main functions `commitBatches`, `proveBatches`, `executeBatches`, and `revertBatches`, +which can be called only by the validator. + +When the validator calls `commitBatches`, the same calldata will be propagated to the zkSync contract (`DiamondProxy` +through `call` where it invokes the `ExecutorFacet` through `delegatecall`), and also a timestamp is assigned to these +batches to track the time these batches are committed by the validator to enforce a delay between committing and +execution of batches. Then, the validator can prove the already committed batches regardless of the mentioned timestamp, +and again the same calldata (related to the `proveBatches` function) will be propagated to the zkSync contract. After +the `delay` is elapsed, the validator is allowed to call `executeBatches` to propagate the same calldata to zkSync +contract. + +The owner of the ValidatorTimelock contract is the same as the owner of the Governance contract - Matter Labs multisig. + +## Allowlist + +The auxiliary contract controls the permission access list. It is used in bridges and diamond proxies to control which +addresses can interact with them in the Alpha release. Currently, it is supposed to set all permissions to public. + +The owner of the Allowlist contract is the Governance contract. + +## Deposit Limitation + +The amount of deposit can be limited. This limitation is applied on an account level and is not time-based. In other +words, each account cannot deposit more than the cap defined. The tokens and the cap can be set through governance +transactions. Moreover, there is an allow listing mechanism as well (only some allow listed accounts can call some +specific functions). So, the combination of deposit limitation and allow listing leads to limiting the deposit of the +allow listed account to be less than the defined cap. + +```solidity +struct Deposit { + bool depositLimitation; + uint256 depositCap; +} + +``` + +Currently, the limit is used only for blocking deposits of the specific token (turning on the limitation and setting the +limit to zero). And on the near future, this functionality will be completely removed. diff --git a/docs/specs/overview.md b/docs/specs/overview.md new file mode 100644 index 000000000000..2ac887090915 --- /dev/null +++ b/docs/specs/overview.md @@ -0,0 +1,39 @@ +# Overview + +As stated in the introduction, the ZK Stack can be used to launch rollups. These rollups have some operators that are +needed to run it, these are the sequencer and the prover, they create blocks and proofs, and submit them to the L1 +contract. + +A user submits their transaction to the sequencer. The job of the sequencer is to collect transactions and execute them +using the zkEVM, and to provide a soft confirmation to the user that their transaction was executed. If the user chooses +they can force the sequencer to include their transaction by submitting it via L1. After the sequencer executes the +block, it sends it over to the prover, who creates a cryptographic proof of the block's execution. This proof is then +sent to the L1 contract alongside the necessary data. On the L1 a +[smart contract](./zkEVM/high_level/l1_smart_contracts.md) verifies that the proof is valid and all the data has been +submitted, and the rollup's state is also updated in the contract. + +![Components](./img/L2_Components.png) + +The core of this mechanism was the execution of transactions. The ZK Stack uses the [zkEVM](./zk_evm/README.md) for +this, which is similar to the EVM, but its role is different than the EVM's role in Ethereum. + +Transactions can also be submitted via L1. This happens via the same process that allows +[L1<>L2 communication](./l1_l2_communication/README.md). This method provides the rollup with censorship resistance, and +allows trustless bridges to the L1. + +The sequencer collects transactions into blocks [blocks](./blocks_batches.md), similarly to Ethereum. To provide the +best UX the protocol has small blocks with quick soft confirmations for the users. Unlike Ethereum, the zkEVM does not +just have blocks, but also batches, which are just a collection of blocks. A batch is the unit that the prover +processes. + +Before we submit a proof we send the [data](./data_availability/README.md) to L1. Instead of submitting the data of each +transaction, we submit how the state of the blockchain changes, this change is called the state diff. This approach +allows the transactions that change the same storage slots to be very cheap, since these transactions don't incur +additional data costs. + +Finally at the end of the process, we [create the proofs](./data_availability/README.md) and send them to L1. Our Boojum +proof system provides excellent performance, and can be run on just 16Gb of GPU RAM. This will enable the proof +generation to be truly decentralized. + +Up to this point we have only talked about a single chain. We will connect these chains into a single ecosystem, called +[the hyperchain](./the_hyperchain/README.md). diff --git a/docs/specs/prover/README.md b/docs/specs/prover/README.md new file mode 100644 index 000000000000..4dccfc9a8338 --- /dev/null +++ b/docs/specs/prover/README.md @@ -0,0 +1,9 @@ +# Prover + +- [Overview](./overview.md) +- [ZK terminology](./zk_terminology.md) +- [Getting Started](./getting_started.md) +- [Circuits](./circuits/README.md) +- [Circuit testing](./circuit_testing.md) +- [Boojum gadgets](./boojum_gadgets.md) +- [Boojum function: check_if_satisfied](./boojum_function_check_if_satisfied.md) diff --git a/docs/specs/prover/boojum_function_check_if_satisfied.md b/docs/specs/prover/boojum_function_check_if_satisfied.md new file mode 100644 index 000000000000..31b3a88a59d6 --- /dev/null +++ b/docs/specs/prover/boojum_function_check_if_satisfied.md @@ -0,0 +1,97 @@ +# Boojum function: check_if_satisfied + +Note: Please read our other documentation and tests first before reading this page. + +Our circuits (and tests) depend on a function from Boojum called +[check_if_satisfied](https://github.com/matter-labs/era-boojum/blob/main/src/cs/implementations/satisfiability_test.rs#L11). +You don’t need to understand it to run circuit tests, but it can be informative to learn more about Boojum and our proof +system. + +First we prepare the constants, variables, and witness. As a reminder, the constants are just constant numbers, the +variables circuit columns that are under PLONK copy-permutation constraints (so they are close in semantics to variables +in programming languages), and the witness ephemeral values that can be used to prove certain constraints, for example +by providing an inverse if the variable must be non-zero. + +![Check_if_satisfied.png](./img/boojum_function_check_if_satisfied/check_if_satisfied.png) + +Next we prepare a view. Instead of working with all of the columns at once, it can be helpful to work with only a +subset. + +![Check_if_satisfied(1).png](<./img/boojum_function_check_if_satisfied/Check_if_satisfied(1).png>) + +Next we create the paths_mappings. For each gate in the circuit, we create a vector of booleans in the correct shape. +Later, when we traverse the gates with actual inputs, we’ll be able to remember which gates should be satisfied at +particular rows by computing the corresponding selector using constant columns and the paths_mappings. + +![Check_if_satisfied(2).png](<./img/boojum_function_check_if_satisfied/Check_if_satisfied(2).png>) + +Now, we have to actually check everything. The checks for the rows depend on whether they are under general purpose +columns, or under special purpose columns. + +**General purpose rows:** + +For each row and gate, we need several things. + +- Evaluator for the gate, to compute the result of the gate +- Path for the gate from the paths_mappings, to locate the gate +- Constants_placement_offset, to find the constants +- Num_terms in the evaluator + - If this is zero, we can skip the row since there is nothing to do +- Gate_debug_name +- num_constants_used +- this_view +- placement (described below) +- evaluation function + +![Check_if_satisfied(3).png](<./img/boojum_function_check_if_satisfied/Check_if_satisfied(3).png>) + +Placement is either UniqueOnRow or MultipleOnRow. UniqueOnRow means there is only one gate on the row (typically because +the gate is larger / more complicated). MultipleOnRow means there are multiple gates within the same row (typically +because the gate is smaller). For example, if a gate only needs 30 columns, but we have 150 columns, we could include +five copies fo that gate in the same row. + +Next, if the placement is UniqueOnRow, we call evaluate_over_general_purpose_columns. All of the evaluations should be +equal to zero, or we panic. + +![Check_if_satisfied(4).png](<./img/boojum_function_check_if_satisfied/Check_if_satisfied(4).png>) + +If the placement is MultipleOnRow, we again call evaluate_over_general_purpose_columns. If any of the evaluations are +non-zero, we log some extra debug information, and then panic. + +![Check_if_satisfied(7).png](<./img/boojum_function_check_if_satisfied/Check_if_satisfied(7).png>) + +This concludes evaluating and checking the generalized rows. Now we will check the specialized rows. + +![Check_if_satisfied(8).png](<./img/boojum_function_check_if_satisfied/Check_if_satisfied(8).png>) + +We start by initializing vectors for specialized_placement_data, evaluation_functions, views, and evaluator_names. Then, +we iterate over each gate_type_id and evaluator. + +![Check_if_satisfied(9).png](<./img/boojum_function_check_if_satisfied/Check_if_satisfied(9).png>) + +If gate_type_id is a LookupFormalGate, we don’t need to do anything in this loop because it is handled by the lookup +table. For all other cases, we need to check the evaluator’s total_quotient_terms_over_all_repititions is non-zero. + +![Check_if_satisfied(11).png](<./img/boojum_function_check_if_satisfied/Check_if_satisfied(11).png>) + +Next, we get num_terms, num_repetitions, and share_constants, total_terms, initial_offset, per_repetition_offset, and +total_constants_available. All of these together form our placement data. + +![Check_if_satisfied(12).png](<./img/boojum_function_check_if_satisfied/Check_if_satisfied(12).png>) + +![Check_if_satisfied(13).png](<./img/boojum_function_check_if_satisfied/Check_if_satisfied(13).png>) + +Once we know the placement_data, we can keep it for later, as well as the evaluator for this gate. + +![Check_if_satisfied(14).png](<./img/boojum_function_check_if_satisfied/Check_if_satisfied(14).png>) + +We also will keep the view and evaluator name. This is all the data we need from our specialized columns. + +To complete the satisfiability test on the special columns, we just need to loop through and check that each of the +evaluations are zero. + +![Check_if_satisfied(16).png](<./img/boojum_function_check_if_satisfied/Check_if_satisfied(16).png>) + +![Check_if_satisfied(17).png](<./img/boojum_function_check_if_satisfied/Check_if_satisfied(17).png>) + +Now we have checked every value on every row, so the satisfiability test is passed, and we can return true. diff --git a/docs/specs/prover/boojum_gadgets.md b/docs/specs/prover/boojum_gadgets.md new file mode 100644 index 000000000000..666422e53f45 --- /dev/null +++ b/docs/specs/prover/boojum_gadgets.md @@ -0,0 +1,189 @@ +# Boojum gadgets + +Boojum gadgets are low-level implementations of tools for constraint systems. They consist of various types: curves, +hash functions, lookup tables, and different circuit types. These gadgets are mostly a reference from +[franklin-crypto](https://github.com/matter-labs/franklin-crypto), with additional hash functions added. These gadgets +have been changed to use the Goldilocks field (order 2^64 - 2^32 + 1), which is much smaller than bn256. This allows us +to reduce the proof system. + +## Circuits types + +We have next types with we use for circuits: + +**Num (Number):** + +```rust +pub struct Num { + pub(crate) variable: Variable, + pub(crate) _marker: std::marker::PhantomData, +} +``` + +**Boolean:** + +```rust +pub struct Boolean { + pub(crate) variable: Variable, + pub(crate) _marker: std::marker::PhantomData, +} +``` + +**U8:** + +```rust +pub struct UInt8 { + pub(crate) variable: Variable, + pub(crate) _marker: std::marker::PhantomData, +} +``` + +**U16:** + +```rust +pub struct UInt16 { + pub(crate) variable: Variable, + pub(crate) _marker: std::marker::PhantomData, +} +``` + +**U32:** + +```rust +pub struct UInt32 { + pub(crate) variable: Variable, + pub(crate) _marker: std::marker::PhantomData, +} +``` + +**U160:** + +```rust +pub struct UInt160 { + pub inner: [UInt32; 5], +} +``` + +**U256:** + +```rust +pub struct UInt256 { + pub inner: [UInt32; 8], +} +``` + +**U512:** + +```rust +pub struct UInt512 { + pub inner: [UInt32; 16], +} +``` + +Every type consists of a Variable (the number inside Variable is just the index): + +```rust +pub struct Variable(pub(crate) u64); +``` + +which is represented in the current Field. Variable is quite diverse, and to have "good" alignment and size we manually +do encoding management to be able to represent it as both copiable variable or witness. + +The implementation of this circuit type itself is similar. We can also divide them into classes as main and dependent: +Such type like U8-U512 decoding inside functions to Num for using them in logical operations. As mentioned above, the +property of these types is to perform logical operations and allocate witnesses. + +Let's demonstrate this in a Boolean example: + +```rust +impl CSAllocatable for Boolean { + type Witness = bool; + fn placeholder_witness() -> Self::Witness { + false + } + + #[inline(always)] + fn allocate_without_value>(cs: &mut CS) -> Self { + let var = cs.alloc_variable_without_value(); + + Self::from_variable_checked(cs, var) + } + + fn allocate>(cs: &mut CS, witness: Self::Witness) -> Self { + let var = cs.alloc_single_variable_from_witness(F::from_u64_unchecked(witness as u64)); + + Self::from_variable_checked(cs, var) + } +} +``` + +As you see, you can allocate both with and without witnesses. + +## Hash function + +In gadgets we have a lot of hast implementation: + +- blake2s +- keccak256 +- poseidon/poseidon2 +- sha256 + +Each of them perform different functions in our proof system. + +## Queues + +One of the most important gadgets in our system is queue. It helps us to send data between circuits. Here is the quick +explanation how it works: + +```rust +Struct CircuitQueue{ + head: HashState, + tail: HashState, + length: UInt32, + witness: VecDeque, +} +``` + +The structure consists of `head` and `tail` commitments that basically are rolling hashes. Also, it has a `length` of +the queue. These three fields are allocated inside the constraint system. Also, there is a `witness`, that keeps actual +values that are now stored in the queue. + +And here is the main functions: + +```rust +fn push(&mut self, value: Element) { + // increment lenght + // head - hash(head, value) + // witness.push_back(value.witness) +} + +fn pop(&mut self) -> Element { + // check length > 0 + // decrement length + // value = witness.pop_front() + // tail = hash(tail, value) + // return value +} + +fn final_check(&self) -> Element { + // check that length == 0 + // check that head == tail +} +``` + +So the key point, of how the queue proofs that popped elements are the same as pushed ones, is equality of rolling +hashes that stored in fields `head` and `tail`. + +Also, we check that we can’t pop an element before it was pushed. This is done by checking that `length >= 0`. + +Very important is making the `final_check` that basically checks the equality of two hashes. So if the queue is never +empty, and we haven’t checked the equality of `head` and `tail` in the end, we also haven’t proven that the elements we +popped are correct. + +For now, we use poseidon2 hash. Here is the link to queue implementations: + +- [CircuitQueue](https://github.com/matter-labs/era-boojum/blob/main/src/gadgets/queue/mod.rs#L29) +- [FullStateCircuitQueue](https://github.com/matter-labs/era-boojum/blob/main/src/gadgets/queue/full_state_queue.rs#L20C12-L20C33) + +The difference is that we actually compute and store a hash inside CircuitQueue during `push` and `pop` operations. But +in FullStateCircuitQueue our `head` and `tail` are just states of sponges. So instead of computing a full hash, we just +absorb a pushed (popped) element. diff --git a/docs/specs/prover/circuit_testing.md b/docs/specs/prover/circuit_testing.md new file mode 100644 index 000000000000..16f1e7b8209f --- /dev/null +++ b/docs/specs/prover/circuit_testing.md @@ -0,0 +1,59 @@ +# Circuit testing + +This page explains unit tests for circuits. Specifically, it goes through a unit test of +[ecrecover](https://github.com/matter-labs/era-zkevm_circuits/blob/main/src/ecrecover/mod.rs#L796). The tests for other +circuits are very similar. + +Many of the tests for different circuits are nearly identical, for example: + +- test_signature_for_address_verification (ecrecover) +- test_code_unpacker_inner +- test_demultiplex_storage_logs_inner +- and several others. + +If you understand one, you will quickly be able to understand them all. + +Let’s focus on ecrecover. Ecrecover is a precompile that, given your signature, can compute your address. If our circuit +works correctly, we should be able to recover the proper address, and be able to prove the computation was done +correctly. + +![Contest(4).png](<./img/circuit_testing/Contest(4).png>) + +The test begins by defining the geometry, max_variables, and max_trace_len. This data will be used to create the +constraint system. Next, we define a helper function: + +![Contest(5).png](<./img/circuit_testing/Contest(5).png>) + +To help run the test, we have a helper function called configure that returns a builder. The builder knows all of the +gates and gate placement strategy, which will be useful for setting up the constraint system. + +![Contest(6).png](<./img/img/circuit_testing/Contest(6).png>) + +The constaint system is almost ready! We still need to add the lookup tables for common boolean functions: + +![Contest(7).png](<./img/circuit_testing/Contest(7).png>) + +Now the constraint system is ready! We can start the main part of the test! + +![Contest(8).png](<./img/circuit_testing/Contest(8).png>) + +Here we have hard coded a secret key with its associated public key, and generate a signature. We will test our circuit +on these inputs! Next we “allocate” these inputs as witnessess: + +![Contest(9).png](<./img/circuit_testing/Contest(9).png>) + +We have to use special integer types because we are working in a finite field. + +![Contest(10).png](<./img/circuit_testing/Contest(10).png>) + +The constants here are specific to the curve used, and are described in detail by code comments in the +ecrecover_precompile_inner_routine. + +Finally we can call the ecrecover_precompile_innner_routine: + +![Contest(11).png](<./img/circuit_testing/Contest(11).png>) + +Lastly, we need to check to make sure that 1) we recovered the correct address, and 2) the constraint system can be +satisfied, meaning the proof works. + +![Contest(12).png](<./img/circuit_testing/Contest(12).png>) diff --git a/docs/specs/prover/circuits/README.md b/docs/specs/prover/circuits/README.md new file mode 100644 index 000000000000..fbec506d8f91 --- /dev/null +++ b/docs/specs/prover/circuits/README.md @@ -0,0 +1,17 @@ +# Circuits + +- [Overview](./overview.md) +- [Code decommiter](./code_decommitter.md) +- [Demux log queue](./demux_log_queue.md) +- [ECRecover](./ecrecover.md) +- [Keccak round function](./keccak_round_function.md) +- [L1 messages hasher](./l1_messages_hasher.md) +- [Log sorter](./log_sorter.md) +- [Main VM](./main_vm.md) +- [RAM permutation](./ram_permutation.md) +- [Sha256 round function](./sha256_round_function.md) +- [Sort decommitments](./sort_decommitments.md) +- [Sorting and deduplication](./sorting_and_deduplicating.md) +- [Sorting](./sorting.md) +- [Storage application](./storage_application.md) +- [Storage sorter](./storage_sorter.md) diff --git a/docs/specs/prover/circuits/code_decommitter.md b/docs/specs/prover/circuits/code_decommitter.md new file mode 100644 index 000000000000..91ff102afcb2 --- /dev/null +++ b/docs/specs/prover/circuits/code_decommitter.md @@ -0,0 +1,208 @@ +# CodeDecommitter + +## CodeDecommitter PI + +### [Input](https://github.com/matter-labs/era-zkevm_circuits/blob/main/src/code_unpacker_sha256/input.rs#L80) + +```rust +pub struct CodeDecommitterInputData { + pub memory_queue_initial_state: QueueState, + pub sorted_requests_queue_initial_state: QueueState, +} +``` + +### [Output](https://github.com/matter-labs/era-zkevm_circuits/blob/main/src/code_unpacker_sha256/input.rs#L100) + +```rust +pub struct CodeDecommitterOutputData { + pub memory_queue_final_state: QueueState, +} +``` + +### [FSM Input and FSM Output](https://github.com/matter-labs/era-zkevm_circuits/blob/main/src/code_unpacker_sha256/input.rs#L61) + +```rust +pub struct CodeDecommitterFSMInputOutput { + pub internal_fsm: CodeDecommittmentFSM, + pub decommittment_requests_queue_state: QueueState, + pub memory_queue_state: QueueState, +} + +pub struct CodeDecommittmentFSM { + pub sha256_inner_state: [UInt32; 8], // 8 uint32 words of internal sha256 state + pub hash_to_compare_against: UInt256, + pub current_index: UInt32, + pub current_page: UInt32, + pub timestamp: UInt32, + pub num_rounds_left: UInt16, + pub length_in_bits: UInt32, + pub state_get_from_queue: Boolean, + pub state_decommit: Boolean, + pub finished: Boolean, +} +``` + +## Main circuit logic + +This circuit takes a queue of decommit requests for DecommitSorter circuit. For each decommit request, it checks that +the linear hash of all opcodes will be equal to this hash that is stored in the decommit request. Also, it writes code +to the corresponding memory page. Briefly, it unpacks the queue from the opcode and updates the memory queue and check +correctness. + +### [First part](https://github.com/matter-labs/era-zkevm_circuits/blob/main/src/code_unpacker_sha256/mod.rs#L48) + +The circuit begins with allocating input part of the PI. + +```rust +let CodeDecommitterCircuitInstanceWitness { + closed_form_input, + sorted_requests_queue_witness, + code_words, +} = witness; + +let mut structured_input = + CodeDecommitterCycleInputOutput::alloc_ignoring_outputs(cs, closed_form_input.clone()); +``` + +We chose what `memory_queue` state and `deccomitments_queue` state to continue to work with. + +```rust +let requests_queue_state = QueueState::conditionally_select( + cs, + structured_input.start_flag, + &structured_input + .observable_input + .sorted_requests_queue_initial_state, + &structured_input + .hidden_fsm_input + .decommittment_requests_queue_state, +); + +let memory_queue_state = QueueState::conditionally_select( + cs, + structured_input.start_flag, + &structured_input.observable_input.memory_queue_initial_state, + &structured_input.hidden_fsm_input.memory_queue_state, +); +``` + +We do the same with inner FSM part. + +```rust +let initial_state = CodeDecommittmentFSM::conditionally_select( + cs, + structured_input.start_flag, + &starting_fsm_state, + &structured_input.hidden_fsm_input.internal_fsm, +); +``` + +### [Main part](https://github.com/matter-labs/era-zkevm_circuits/blob/main/src/code_unpacker_sha256/mod.rs#L168) + +Here’s the part, where all the main logic is implemented. Firstly, we take a new decommit request if the queue is not +empty yet. + +```rust +let (may_be_new_request, _) = + unpack_requests_queue.pop_front(cs, state.state_get_from_queue); +``` + +Then we update the state of the circuit. + +```rust +state.num_rounds_left = UInt16::conditionally_select( + cs, + state.state_get_from_queue, + &length_in_rounds, + &state.num_rounds_left, +); +... +``` + +Then we create two write memory queries and push them to memory queue. + +```rust +let mem_query_0 = MemoryQuery { + timestamp: state.timestamp, + memory_page: state.current_page, + index: state.current_index, + rw_flag: boolean_true, + value: code_word_0, + is_ptr: boolean_false, +}; + +let mem_query_1 = MemoryQuery { + timestamp: state.timestamp, + memory_page: state.current_page, + index: state.current_index, + rw_flag: boolean_true, + value: code_word_1, + is_ptr: boolean_false, +}; + +memory_queue.push(cs, mem_query_0, state.state_decommit); +memory_queue.push(cs, mem_query_1, process_second_word); +``` + +Now we create a new input for hash to be absorbed. + +```rust +let mut sha256_input = [zero_u32; 16]; +for (dst, src) in sha256_input.iter_mut().zip( + code_word_0_be_bytes + .array_chunks::<4>() + .chain(code_word_1_be_bytes.array_chunks::<4>()), +) { + *dst = UInt32::from_be_bytes(cs, *src); +} +``` + +And absorb it to current state. + +```rust +let mut new_internal_state = state.sha256_inner_state; +round_function_over_uint32(cs, &mut new_internal_state, &sha256_input); +``` + +Also, we update current state. + +```rust +state.sha256_inner_state = <[UInt32; 8]>::conditionally_select( + cs, + state.state_decommit, + &new_internal_state, + &state.sha256_inner_state, +); +``` + +Finally, we check the hash if necessary. + +```rust +for (part_of_first, part_of_second) in hash + .inner + .iter() + .zip(state.hash_to_compare_against.inner.iter()) +{ + Num::conditionally_enforce_equal( + cs, + finalize, + &part_of_first.into_num(), + &part_of_second.into_num(), + ); +} +``` + +### [Final part](https://github.com/matter-labs/era-zkevm_circuits/blob/main/src/code_unpacker_sha256/mod.rs#L111) + +Now we update PI output parts and compute a commitment. Then we allocate it as public variables. + +```rust +let compact_form = + ClosedFormInputCompactForm::from_full_form(cs, &structured_input, round_function); + +let input_commitment = commit_variable_length_encodable_item(cs, &compact_form, round_function); +for el in input_commitment.iter() { + let gate = PublicInputGate::new(el.get_variable()); + gate.add_to_cs(cs); +} +``` diff --git a/docs/specs/prover/circuits/demux_log_queue.md b/docs/specs/prover/circuits/demux_log_queue.md new file mode 100644 index 000000000000..27bc596bfcf2 --- /dev/null +++ b/docs/specs/prover/circuits/demux_log_queue.md @@ -0,0 +1,226 @@ +# DemuxLogQueue + +## DemuxLogQueue PI + +### [Input](https://github.com/matter-labs/era-zkevm_circuits/blob/main/src/demux_log_queue/input.rs#L49) + +```rust +pub struct LogDemuxerInputData { + pub initial_log_queue_state: QueueState, +} +``` + +### [Output](https://github.com/matter-labs/era-zkevm_circuits/blob/main/src/fsm_input_output/circuit_inputs/main_vm.rs#L33) + +```rust +pub struct LogDemuxerOutputData { + pub storage_access_queue_state: QueueState, + pub events_access_queue_state: QueueState, + pub l1messages_access_queue_state: QueueState, + pub keccak256_access_queue_state: QueueState, + pub sha256_access_queue_state: QueueState, + pub ecrecover_access_queue_state: QueueState, +} +``` + +### [FSM Input and FSM Output](https://github.com/matter-labs/era-zkevm_circuits/blob/main/src/demux_log_queue/input.rs#L22) + +```rust +pub struct LogDemuxerFSMInputOutput { + pub initial_log_queue_state: QueueState, + pub storage_access_queue_state: QueueState, + pub events_access_queue_state: QueueState, + pub l1messages_access_queue_state: QueueState, + pub keccak256_access_queue_state: QueueState, + pub sha256_access_queue_state: QueueState, + pub ecrecover_access_queue_state: QueueState, +} +``` + +## Main circuit logic + +The input of Log_Demuxer receives log_queue, consisting of a request to storage, events, L1messages request, and a +request to the precompiles ecrecover, sha256, and keccak256. It divides this queue into six new queues. See our diagram. + +### Start + +The function of circuits is `demultiplex_storage_logs_enty_point`. We start for allocation of queue witnesses: + +```rust +let mut structured_input = + LogDemuxerInputOutput::alloc_ignoring_outputs(cs, closed_form_input.clone()); +``` + +Then we must verify that no elements have already been retrieved from the queue: + +```rust +structured_input + .observable_input + .initial_log_queue_state + .enforce_trivial_head(cs); +``` + +So long as `tail` is some equivalent of the merkel tree root and `head` is an equivalent of the current node hash, we +provide some path witness when we pop elements and require that we properly end up in the root. So we must prove that +element of head is zero: + +```rust +pub fn enforce_trivial_head>(&self, cs: &mut CS) { + let zero_num = Num::zero(cs); + for el in self.head.iter() { + Num::enforce_equal(cs, el, &zero_num); + } +} +``` + +Depends on `start_flag` we select which queue `observable_input` or `fsm_input`(internal intermediate queue) we took: + +```rust +let state = QueueState::conditionally_select( + cs, + structured_input.start_flag, + &structured_input.observable_input.initial_log_queue_state, + &structured_input.hidden_fsm_input.initial_log_queue_state, +); +``` + +Wrap the state and witnesses in `StorageLogQueue`, thereby preparing the input data for `inner` part: + +```rust +let mut initial_queue = StorageLogQueue::::from_state(cs, state); +use std::sync::Arc; +let initial_queue_witness = CircuitQueueWitness::from_inner_witness(initial_queue_witness); +initial_queue.witness = Arc::new(initial_queue_witness); +``` + +For the rest, it selects between empty or from FSM: + +```rust +let queue_states_from_fsm = [ +&structured_input.hidden_fsm_input.storage_access_queue_state, +&structured_input.hidden_fsm_input.events_access_queue_state, +&structured_input + .hidden_fsm_input + .l1messages_access_queue_state, +&structured_input + .hidden_fsm_input + .keccak256_access_queue_state, +&structured_input.hidden_fsm_input.sha256_access_queue_state, +&structured_input + .hidden_fsm_input + .ecrecover_access_queue_state, +]; + +let empty_state = QueueState::empty(cs); +let [mut storage_access_queue, mut events_access_queue, mut l1messages_access_queue, mut keccak256_access_queue, mut sha256_access_queue, mut ecrecover_access_queue] = +queue_states_from_fsm.map(|el| { +let state = QueueState::conditionally_select( + cs, + structured_input.start_flag, + &empty_state, + &el, + ); + StorageLogQueue::::from_state(cs, state) +}); +``` + +Prepared all queues into `input_queues` and call `inner` part: + +```rust +demultiplex_storage_logs_inner(cs, &mut initial_queue, input_queues, limit); +``` + +The last step is to form the final state. The flag `completed` shows us if `initial_queue` is empty or not. If not, we +fill fsm_output. If it is empty, we select observable_output for the different queues. + +Finally, we compute a commitment to PublicInput and allocate it as witness variables. + +```rust +let compact_form = + ClosedFormInputCompactForm::from_full_form(cs, &structured_input, round_function); + +let input_commitment = commit_variable_length_encodable_item(cs, &compact_form, round_function); +for el in input_commitment.iter() { + let gate = PublicInputGate::new(el.get_variable()); + gate.add_to_cs(cs); +} +``` + +### Inner part + +This is the logic part of the circuit. It depends on the main queue `storage_log_queue`, which separates the other +queues. After we have dealt with the initial precompile, we need to allocate constant addresses for +`keccak_precompile_address`, `sha256_precompile_address`, `ecrecover_precompile_address` and allocate constants for +`STORAGE_AUX_BYTE`, `EVENT_AUX_BYTE`, `L1_MESSAGE_AUX_BYTE`, `PRECOMPILE_AUX_BYTE`. Execution happens when we pop all +elements from `storage_log_queue`. We have appropriate flags for this, which depend on each other: + +```rust +let queue_is_empty = storage_log_queue.is_empty(cs); +let execute = queue_is_empty.negated(cs); +``` + +Here, we choose flags depending on the popped element data: + +```rust +let is_storage_aux_byte = UInt8::equals(cs, &aux_byte_for_storage, &popped.0.aux_byte); +let is_event_aux_byte = UInt8::equals(cs, &aux_byte_for_event, &popped.0.aux_byte); +let is_l1_message_aux_byte = + UInt8::equals(cs, &aux_byte_for_l1_message, &popped.0.aux_byte); +let is_precompile_aux_byte = + UInt8::equals(cs, &aux_byte_for_precompile_call, &popped.0.aux_byte); + +let is_keccak_address = UInt160::equals(cs, &keccak_precompile_address, &popped.0.address); +let is_sha256_address = UInt160::equals(cs, &sha256_precompile_address, &popped.0.address); +let is_ecrecover_address = + UInt160::equals(cs, &ecrecover_precompile_address, &popped.0.address); +``` + +Put up the right flag for shards: + +```rust +let is_rollup_shard = popped.0.shard_id.is_zero(cs); +let is_porter_shard = is_rollup_shard.negated(cs); +``` + +Execute all and push them into output queues: + +```rust +let execute_rollup_storage = Boolean::multi_and(cs, &[is_storage_aux_byte, is_rollup_shard, execute]); +let execute_porter_storage = Boolean::multi_and(cs, &[is_storage_aux_byte, is_porter_shard, execute]); + +let execute_event = Boolean::multi_and(cs, &[is_event_aux_byte, execute]); +let execute_l1_message = Boolean::multi_and(cs, &[is_l1_message_aux_byte, execute]); +let execute_keccak_call = Boolean::multi_and(cs, &[is_precompile_aux_byte, is_keccak_address, execute]); +let execute_sha256_call = Boolean::multi_and(cs, &[is_precompile_aux_byte, is_sha256_address, execute]); +let execute_ecrecover_call = Boolean::multi_and(cs, &[is_precompile_aux_byte, is_ecrecover_address, execute]); + +let bitmask = [ + execute_rollup_storage, + execute_event, + execute_l1_message, + execute_keccak_call, + execute_sha256_call, + execute_ecrecover_call, +]; + +push_with_optimize( + cs, + [ + rollup_storage_queue, + events_queue, + l1_messages_queue, + keccak_calls_queue, + sha256_calls_queue, + ecdsa_calls_queue, + ], + bitmask, + popped.0, +); +``` + +Note: since we do not have a porter, the flag is automatically set to `false`: + +```rust +let boolean_false = Boolean::allocated_constant(cs, false); +Boolean::enforce_equal(cs, &execute_porter_storage, &boolean_false); +``` diff --git a/docs/specs/prover/circuits/ecrecover.md b/docs/specs/prover/circuits/ecrecover.md new file mode 100644 index 000000000000..e1bc2347caf0 --- /dev/null +++ b/docs/specs/prover/circuits/ecrecover.md @@ -0,0 +1,320 @@ +# Ecrecover + +## Ecrecover PI + +### [Input](https://github.com/matter-labs/era-zkevm_circuits/blob/main/src/fsm_input_output/circuit_inputs/main_vm.rs#L9) + +```rust +pub struct PrecompileFunctionInputData { + pub initial_log_queue_state: QueueState, + pub initial_memory_queue_state: QueueState, +} +``` + +### [Output](https://github.com/matter-labs/era-zkevm_circuits/blob/main/src/base_structures/precompile_input_outputs/mod.rs#L42) + +```rust +pub struct PrecompileFunctionOutputData { + pub final_memory_state: QueueState, +} +``` + +### [FSM Input and FSM Output](https://github.com/matter-labs/era-zkevm_circuits/blob/main/src/keccak256_round_function/input.rs#L59) + +```rust +pub struct EcrecoverCircuitFSMInputOutput { + pub log_queue_state: QueueState, + pub memory_queue_state: QueueState, +} +``` + +## Main circuit logic + +This circuit implements the ecrecover precompile described in the Ethereum yellow paper: + + +The purpose of ecrecover is to recover the signer’s public key from digital signature. + +A special note about this circuit is that there are hardcoded ‘valid’ field element values provided to the circuit. This +is to prevent the circuit from not satisfying in case the user-provided inputs are incorrect and, when the circuit +detects this, the bad values are swapped out for the hardcoded ones. In this event, exceptions are logged and pushed +into a vector which are returned to the caller, informing them that the provided inputs were incorrect and the result +should be discarded. + +Most of the relevant circuit logic resides in the `ecrecover_precompile_inner_routine` function. Let’s take the circuit +step by step. + +1. The circuit starts off by declaring a set of constants which are useful to have throughout the circuit. These include + the B parameter of the secp256k1 curve, the constant -1 in the curve’s base field, and the base field and scalar + field modulus. We also create the vector that should capture any exceptions. + +```rust +let curve_b = Secp256Affine::b_coeff(); + +let mut minus_one = Secp256Fq::one(); +minus_one.negate(); + +let mut curve_b_nn = + Secp256BaseNNField::::allocated_constant(cs, curve_b, &base_field_params); +let mut minus_one_nn = + Secp256BaseNNField::::allocated_constant(cs, minus_one, &base_field_params); + +let secp_n_u256 = U256([ + scalar_field_params.modulus_u1024.as_ref().as_words()[0], + scalar_field_params.modulus_u1024.as_ref().as_words()[1], + scalar_field_params.modulus_u1024.as_ref().as_words()[2], + scalar_field_params.modulus_u1024.as_ref().as_words()[3], +]); +let secp_n_u256 = UInt256::allocated_constant(cs, secp_n_u256); + +let secp_p_u256 = U256([ + base_field_params.modulus_u1024.as_ref().as_words()[0], + base_field_params.modulus_u1024.as_ref().as_words()[1], + base_field_params.modulus_u1024.as_ref().as_words()[2], + base_field_params.modulus_u1024.as_ref().as_words()[3], +]); +let secp_p_u256 = UInt256::allocated_constant(cs, secp_p_u256); + +let mut exception_flags = ArrayVec::<_, EXCEPTION_FLAGS_ARR_LEN>::new(); +``` + +1. Next, the circuit checks whether or not the given `x` input (which is the x-coordinate of the signature) falls within + the scalar field of the curve. Since, in ecrecover, `x = r + kn`, almost any `r` will encode a unique x-coordinate, + except for when `r > scalar_field_modulus`. If this is the case, `x = r + n`, otherwise, `x = r`. `x` is recovered + here from `r`. + +```rust +let [y_is_odd, x_overflow, ..] = + Num::::from_variable(recid.get_variable()).spread_into_bits::<_, 8>(cs); + +let (r_plus_n, of) = r.overflowing_add(cs, &secp_n_u256); +let mut x_as_u256 = UInt256::conditionally_select(cs, x_overflow, &r_plus_n, &r); +let error = Boolean::multi_and(cs, &[x_overflow, of]); +exception_flags.push(error); + +// we handle x separately as it is the only element of base field of a curve (not a scalar field element!) +// check that x < q - order of base point on Secp256 curve +// if it is not actually the case - mask x to be zero +let (_res, is_in_range) = x_as_u256.overflowing_sub(cs, &secp_p_u256); +x_as_u256 = x_as_u256.mask(cs, is_in_range); +let x_is_not_in_range = is_in_range.negated(cs); +exception_flags.push(x_is_not_in_range); +``` + +1. Then, all field elements are interpreted as such within the circuit. As they are passed in, they are simply byte + arrays which are interpreted initially as `UInt256` numbers. These get converted to field elements by using the + conversion functions defined near the top of the file. Additionally, checks are done to make sure none of the passed + in field elements are zero. + +```rust +let mut x_fe = convert_uint256_to_field_element(cs, &x_as_u256, &base_field_params); + +let (mut r_fe, r_is_zero) = + convert_uint256_to_field_element_masked(cs, &r, &scalar_field_params); +exception_flags.push(r_is_zero); +let (mut s_fe, s_is_zero) = + convert_uint256_to_field_element_masked(cs, &s, &scalar_field_params); +exception_flags.push(s_is_zero); + +// NB: although it is not strictly an exception we also assume that hash is never zero as field element +let (mut message_hash_fe, message_hash_is_zero) = + convert_uint256_to_field_element_masked(cs, &message_hash, &scalar_field_params); +exception_flags.push(message_hash_is_zero); +``` + +1. Now we are going to compute `t` and check whether or not it is quadratic residue in the base field. To start, we take + `x` which we calculated before, and calculate `t` by doing `x^3 + b`, where `b` is the B parameter of the secp256k1 + curve. We check to make sure that `t` is not zero. + +```rust +let mut t = x_fe.square(cs); // x^2 +t = t.mul(cs, &mut x_fe); // x^3 +t = t.add(cs, &mut curve_b_nn); // x^3 + b + +let t_is_zero = t.is_zero(cs); +exception_flags.push(t_is_zero); +``` + +1. The Legendre symbol for `t` is computed to do a quadratic residue check. We need to compute `t^b` which corresponds + to `t^{2^255} / ( t^{2^31} * t^{2^8} * t^{2^7} * t^{2^6} * t^{2^5} * t^{2^3} * t)`. First, an array of powers of `t` + is created (up to `t^255`). Then, we multiply together all the elements in the denominator of the equation, which are + `t^{2^31} * t^{2^8} * t^{2^7} * t^{2^6} * t^{2^5} * t^{2^3} * t`. Lastly, the division is performed and we end up + with `t^b`. + +```rust +let t_is_zero = t.is_zero(cs); // We first do a zero check +exception_flags.push(t_is_zero); + +// if t is zero then just mask +let t = Selectable::conditionally_select(cs, t_is_zero, &valid_t_in_external_field, &t); + +// array of powers of t of the form t^{2^i} starting from i = 0 to 255 +let mut t_powers = Vec::with_capacity(X_POWERS_ARR_LEN); +t_powers.push(t); + +for _ in 1..X_POWERS_ARR_LEN { + let prev = t_powers.last_mut().unwrap(); + let next = prev.square(cs); + t_powers.push(next); +} + +let mut acc = t_powers[0].clone(); +for idx in [3, 5, 6, 7, 8, 31].into_iter() { + let other = &mut t_powers[idx]; + acc = acc.mul(cs, other); +} +let mut legendre_symbol = t_powers[255].div_unchecked(cs, &mut acc); +``` + +1. Before we proceed to the quadratic residue check, we take advantage of the powers we just calculated to compute the + square root of `t`, in order to determine whether the y-coordinate of the signature we’ve passed is positive or + negative. + +```rust +let mut acc_2 = t_powers[2].clone(); +for idx in [4, 5, 6, 7, 30].into_iter() { + let other = &mut t_powers[idx]; + acc_2 = acc_2.mul(cs, other); +} + +let mut may_be_recovered_y = t_powers[254].div_unchecked(cs, &mut acc_2); +may_be_recovered_y.normalize(cs); +let mut may_be_recovered_y_negated = may_be_recovered_y.negated(cs); +may_be_recovered_y_negated.normalize(cs); + +let [lowest_bit, ..] = + Num::::from_variable(may_be_recovered_y.limbs[0]).spread_into_bits::<_, 16>(cs); + +// if lowest bit != parity bit, then we need conditionally select +let should_swap = lowest_bit.xor(cs, y_is_odd); +let may_be_recovered_y = Selectable::conditionally_select( + cs, + should_swap, + &may_be_recovered_y_negated, + &may_be_recovered_y, +); +``` + +1. Then, proceed with the quadratic residue check. In case `t` is nonresidue, we swap out our inputs for the hardcoded + ‘valid’ inputs. + +```rust +let t_is_nonresidue = + Secp256BaseNNField::::equals(cs, &mut legendre_symbol, &mut minus_one_nn); +exception_flags.push(t_is_nonresidue); +// unfortunately, if t is found to be a quadratic nonresidue, we can't simply let x to be zero, +// because then t_new = 7 is again a quadratic nonresidue. So, in this case we let x to be 9, then +// t = 16 is a quadratic residue +let x = + Selectable::conditionally_select(cs, t_is_nonresidue, &valid_x_in_external_field, &x_fe); +let y = Selectable::conditionally_select( + cs, + t_is_nonresidue, + &valid_y_in_external_field, + &may_be_recovered_y, +); +``` + +1. The next step is computing the public key. We compute the public key `Q` by calculating `Q = (s * X - hash * G) / r`. + We can simplify this in-circuit by calculating `s / r` and `hash / r` separately, and then doing an MSM to get the + combined output. First, we pre-compute these divided field elements, and then compute the point like so: + +```rust +let mut r_fe_inversed = r_fe.inverse_unchecked(cs); +let mut s_by_r_inv = s_fe.mul(cs, &mut r_fe_inversed); +let mut message_hash_by_r_inv = message_hash_fe.mul(cs, &mut r_fe_inversed); + +s_by_r_inv.normalize(cs); +message_hash_by_r_inv.normalize(cs); + +let mut gen_negated = Secp256Affine::one(); +gen_negated.negate(); +let (gen_negated_x, gen_negated_y) = gen_negated.into_xy_unchecked(); +let gen_negated_x = + Secp256BaseNNField::allocated_constant(cs, gen_negated_x, base_field_params); +let gen_negated_y = + Secp256BaseNNField::allocated_constant(cs, gen_negated_y, base_field_params); + +let s_by_r_inv_normalized_lsb_bits: Vec<_> = s_by_r_inv + .limbs + .iter() + .map(|el| Num::::from_variable(*el).spread_into_bits::<_, 16>(cs)) + .flatten() + .collect(); +let message_hash_by_r_inv_lsb_bits: Vec<_> = message_hash_by_r_inv + .limbs + .iter() + .map(|el| Num::::from_variable(*el).spread_into_bits::<_, 16>(cs)) + .flatten() + .collect(); + +let mut recovered_point = (x, y); +let mut generator_point = (gen_negated_x, gen_negated_y); +// now we do multiexponentiation +let mut q_acc = + SWProjectivePoint::>::zero(cs, base_field_params); + +// we should start from MSB, double the accumulator, then conditionally add +for (cycle, (x_bit, hash_bit)) in s_by_r_inv_normalized_lsb_bits + .into_iter() + .rev() + .zip(message_hash_by_r_inv_lsb_bits.into_iter().rev()) + .enumerate() +{ + if cycle != 0 { + q_acc = q_acc.double(cs); + } + let q_plus_x = q_acc.add_mixed(cs, &mut recovered_point); + let mut q_0: SWProjectivePoint> = + Selectable::conditionally_select(cs, x_bit, &q_plus_x, &q_acc); + + let q_plux_gen = q_0.add_mixed(cs, &mut generator_point); + let q_1 = Selectable::conditionally_select(cs, hash_bit, &q_plux_gen, &q_0); + + q_acc = q_1; +} + +let ((mut q_x, mut q_y), is_infinity) = + q_acc.convert_to_affine_or_default(cs, Secp256Affine::one()); +exception_flags.push(is_infinity); +let any_exception = Boolean::multi_or(cs, &exception_flags[..]); + +q_x.normalize(cs); +q_y.normalize(cs); +``` + +1. Now that we have our public key recovered, the last thing we will need to do is take the keccak hash of the public + key and then take the first 20 bytes to recover the address. + +```rust +let zero_u8 = UInt8::zero(cs); + +let mut bytes_to_hash = [zero_u8; 64]; +let it = q_x.limbs[..16] + .iter() + .rev() + .chain(q_y.limbs[..16].iter().rev()); + +for (dst, src) in bytes_to_hash.array_chunks_mut::<2>().zip(it) { + let limb = unsafe { UInt16::from_variable_unchecked(*src) }; + *dst = limb.to_be_bytes(cs); +} + +let mut digest_bytes = keccak256(cs, &bytes_to_hash); +// digest is 32 bytes, but we need only 20 to recover address +digest_bytes[0..12].copy_from_slice(&[zero_u8; 12]); // empty out top bytes +digest_bytes.reverse(); +``` + +1. At this point, we are basically done! What’s left now is to ensure we send a masked value in case of any exception, + and then we can output the resulting address and any exceptions which occurred for the caller to handle. This wraps + up the ecrecover circuit! + +```rust +let written_value_unmasked = UInt256::from_le_bytes(cs, digest_bytes); + +let written_value = written_value_unmasked.mask_negated(cs, any_exception); +let all_ok = any_exception.negated(cs); + +(all_ok, written_value) // Return any exceptions and the resulting address value +``` diff --git a/docs/specs/prover/circuits/img/diagram.png b/docs/specs/prover/circuits/img/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..75ebf7c3f8879565b29c8b3c2033c4bd6054b67f GIT binary patch literal 12668 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 z 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 XEfJo)CRMK?8YswKyMmK3x&MCvU0(On literal 0 HcmV?d00001 diff --git a/docs/specs/prover/circuits/img/image.png b/docs/specs/prover/circuits/img/image.png new file mode 100644 index 0000000000000000000000000000000000000000..cb6f0be17dec7662434e17c0c2cad0c0ef27f0f6 GIT binary patch literal 227383 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ÿ́CV5d83! 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 literal 0 HcmV?d00001 diff --git a/docs/specs/img/zk-the-collective-action.jpeg b/docs/specs/img/zk-the-collective-action.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..1d75fcb856fdc0b072147ad8267a53d9c2711689 GIT binary patch literal 260019 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